/contrib/groff/src/devices/grops/psrm.cpp

https://bitbucket.org/freebsd/freebsd-head/ · C++ · 1178 lines · 1076 code · 68 blank · 34 comment · 354 complexity · 51f21f63501a170c00afc123eecfb278 MD5 · raw file

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004
  3. Free Software Foundation, Inc.
  4. Written by James Clark (jjc@jclark.com)
  5. This file is part of groff.
  6. groff is free software; you can redistribute it and/or modify it under
  7. the terms of the GNU General Public License as published by the Free
  8. Software Foundation; either version 2, or (at your option) any later
  9. version.
  10. groff is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12. FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  13. for more details.
  14. You should have received a copy of the GNU General Public License along
  15. with groff; see the file COPYING. If not, write to the Free Software
  16. Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
  17. #include "driver.h"
  18. #include "stringclass.h"
  19. #include "cset.h"
  20. #include "ps.h"
  21. #ifdef NEED_DECLARATION_PUTENV
  22. extern "C" {
  23. int putenv(const char *);
  24. }
  25. #endif /* NEED_DECLARATION_PUTENV */
  26. #define GROPS_PROLOGUE "prologue"
  27. static void print_ps_string(const string &s, FILE *outfp);
  28. cset white_space("\n\r \t\f");
  29. string an_empty_string;
  30. char valid_input_table[256]= {
  31. #ifndef IS_EBCDIC_HOST
  32. 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,
  33. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
  34. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  35. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  36. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  37. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  38. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  39. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
  40. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  41. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  42. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  43. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  44. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  45. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  46. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  47. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  48. #else
  49. 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
  50. 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  51. 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  52. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  53. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  54. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  55. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  56. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  57. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  58. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  59. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  60. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  61. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  62. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  63. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  64. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
  65. #endif
  66. };
  67. const char *extension_table[] = {
  68. "DPS",
  69. "CMYK",
  70. "Composite",
  71. "FileSystem",
  72. };
  73. const int NEXTENSIONS = sizeof(extension_table)/sizeof(extension_table[0]);
  74. const char *resource_table[] = {
  75. "font",
  76. "procset",
  77. "file",
  78. "encoding",
  79. "form",
  80. "pattern",
  81. };
  82. const int NRESOURCES = sizeof(resource_table)/sizeof(resource_table[0]);
  83. static int read_uint_arg(const char **pp, unsigned *res)
  84. {
  85. while (white_space(**pp))
  86. *pp += 1;
  87. if (**pp == '\0') {
  88. error("missing argument");
  89. return 0;
  90. }
  91. const char *start = *pp;
  92. // XXX use strtoul
  93. long n = strtol(start, (char **)pp, 10);
  94. if (n == 0 && *pp == start) {
  95. error("not an integer");
  96. return 0;
  97. }
  98. if (n < 0) {
  99. error("argument must not be negative");
  100. return 0;
  101. }
  102. *res = unsigned(n);
  103. return 1;
  104. }
  105. struct resource {
  106. resource *next;
  107. resource_type type;
  108. string name;
  109. enum { NEEDED = 01, SUPPLIED = 02, FONT_NEEDED = 04, BUSY = 010 };
  110. unsigned flags;
  111. string version;
  112. unsigned revision;
  113. char *filename;
  114. int rank;
  115. resource(resource_type, string &, string & = an_empty_string, unsigned = 0);
  116. ~resource();
  117. void print_type_and_name(FILE *outfp);
  118. };
  119. resource::resource(resource_type t, string &n, string &v, unsigned r)
  120. : next(0), type(t), flags(0), revision(r), filename(0), rank(-1)
  121. {
  122. name.move(n);
  123. version.move(v);
  124. if (type == RESOURCE_FILE) {
  125. if (name.search('\0') >= 0)
  126. error("filename contains a character with code 0");
  127. filename = name.extract();
  128. }
  129. }
  130. resource::~resource()
  131. {
  132. a_delete filename;
  133. }
  134. void resource::print_type_and_name(FILE *outfp)
  135. {
  136. fputs(resource_table[type], outfp);
  137. putc(' ', outfp);
  138. print_ps_string(name, outfp);
  139. if (type == RESOURCE_PROCSET) {
  140. putc(' ', outfp);
  141. print_ps_string(version, outfp);
  142. fprintf(outfp, " %u", revision);
  143. }
  144. }
  145. resource_manager::resource_manager()
  146. : extensions(0), language_level(0), resource_list(0)
  147. {
  148. read_download_file();
  149. string procset_name("grops");
  150. extern const char *version_string;
  151. extern const char *revision_string;
  152. unsigned revision_uint;
  153. if (!read_uint_arg(&revision_string, &revision_uint))
  154. revision_uint = 0;
  155. string procset_version(version_string);
  156. procset_resource = lookup_resource(RESOURCE_PROCSET, procset_name,
  157. procset_version, revision_uint);
  158. procset_resource->flags |= resource::SUPPLIED;
  159. }
  160. resource_manager::~resource_manager()
  161. {
  162. while (resource_list) {
  163. resource *tem = resource_list;
  164. resource_list = resource_list->next;
  165. delete tem;
  166. }
  167. }
  168. resource *resource_manager::lookup_resource(resource_type type,
  169. string &name,
  170. string &version,
  171. unsigned revision)
  172. {
  173. resource *r;
  174. for (r = resource_list; r; r = r->next)
  175. if (r->type == type
  176. && r->name == name
  177. && r->version == version
  178. && r->revision == revision)
  179. return r;
  180. r = new resource(type, name, version, revision);
  181. r->next = resource_list;
  182. resource_list = r;
  183. return r;
  184. }
  185. // Just a specialized version of lookup_resource().
  186. resource *resource_manager::lookup_font(const char *name)
  187. {
  188. resource *r;
  189. for (r = resource_list; r; r = r->next)
  190. if (r->type == RESOURCE_FONT
  191. && strlen(name) == (size_t)r->name.length()
  192. && memcmp(name, r->name.contents(), r->name.length()) == 0)
  193. return r;
  194. string s(name);
  195. r = new resource(RESOURCE_FONT, s);
  196. r->next = resource_list;
  197. resource_list = r;
  198. return r;
  199. }
  200. void resource_manager::need_font(const char *name)
  201. {
  202. lookup_font(name)->flags |= resource::FONT_NEEDED;
  203. }
  204. typedef resource *Presource; // Work around g++ bug.
  205. void resource_manager::document_setup(ps_output &out)
  206. {
  207. int nranks = 0;
  208. resource *r;
  209. for (r = resource_list; r; r = r->next)
  210. if (r->rank >= nranks)
  211. nranks = r->rank + 1;
  212. if (nranks > 0) {
  213. // Sort resource_list in reverse order of rank.
  214. Presource *head = new Presource[nranks + 1];
  215. Presource **tail = new Presource *[nranks + 1];
  216. int i;
  217. for (i = 0; i < nranks + 1; i++) {
  218. head[i] = 0;
  219. tail[i] = &head[i];
  220. }
  221. for (r = resource_list; r; r = r->next) {
  222. i = r->rank < 0 ? 0 : r->rank + 1;
  223. *tail[i] = r;
  224. tail[i] = &(*tail[i])->next;
  225. }
  226. resource_list = 0;
  227. for (i = 0; i < nranks + 1; i++)
  228. if (head[i]) {
  229. *tail[i] = resource_list;
  230. resource_list = head[i];
  231. }
  232. a_delete head;
  233. a_delete tail;
  234. // check it
  235. for (r = resource_list; r; r = r->next)
  236. if (r->next)
  237. assert(r->rank >= r->next->rank);
  238. for (r = resource_list; r; r = r->next)
  239. if (r->type == RESOURCE_FONT && r->rank >= 0)
  240. supply_resource(r, -1, out.get_file());
  241. }
  242. }
  243. void resource_manager::print_resources_comment(unsigned flag, FILE *outfp)
  244. {
  245. int continued = 0;
  246. for (resource *r = resource_list; r; r = r->next)
  247. if (r->flags & flag) {
  248. if (continued)
  249. fputs("%%+ ", outfp);
  250. else {
  251. fputs(flag == resource::NEEDED
  252. ? "%%DocumentNeededResources: "
  253. : "%%DocumentSuppliedResources: ",
  254. outfp);
  255. continued = 1;
  256. }
  257. r->print_type_and_name(outfp);
  258. putc('\n', outfp);
  259. }
  260. }
  261. void resource_manager::print_header_comments(ps_output &out)
  262. {
  263. for (resource *r = resource_list; r; r = r->next)
  264. if (r->type == RESOURCE_FONT && (r->flags & resource::FONT_NEEDED))
  265. supply_resource(r, 0, 0);
  266. print_resources_comment(resource::NEEDED, out.get_file());
  267. print_resources_comment(resource::SUPPLIED, out.get_file());
  268. print_language_level_comment(out.get_file());
  269. print_extensions_comment(out.get_file());
  270. }
  271. void resource_manager::output_prolog(ps_output &out)
  272. {
  273. FILE *outfp = out.get_file();
  274. out.end_line();
  275. char *path;
  276. if (!getenv("GROPS_PROLOGUE")) {
  277. string e = "GROPS_PROLOGUE";
  278. e += '=';
  279. e += GROPS_PROLOGUE;
  280. e += '\0';
  281. if (putenv(strsave(e.contents())))
  282. fatal("putenv failed");
  283. }
  284. char *prologue = getenv("GROPS_PROLOGUE");
  285. FILE *fp = font::open_file(prologue, &path);
  286. if (!fp)
  287. fatal("can't find `%1'", prologue);
  288. fputs("%%BeginResource: ", outfp);
  289. procset_resource->print_type_and_name(outfp);
  290. putc('\n', outfp);
  291. process_file(-1, fp, path, outfp);
  292. fclose(fp);
  293. a_delete path;
  294. fputs("%%EndResource\n", outfp);
  295. }
  296. void resource_manager::import_file(const char *filename, ps_output &out)
  297. {
  298. out.end_line();
  299. string name(filename);
  300. resource *r = lookup_resource(RESOURCE_FILE, name);
  301. supply_resource(r, -1, out.get_file(), 1);
  302. }
  303. void resource_manager::supply_resource(resource *r, int rank, FILE *outfp,
  304. int is_document)
  305. {
  306. if (r->flags & resource::BUSY) {
  307. r->name += '\0';
  308. fatal("loop detected in dependency graph for %1 `%2'",
  309. resource_table[r->type],
  310. r->name.contents());
  311. }
  312. r->flags |= resource::BUSY;
  313. if (rank > r->rank)
  314. r->rank = rank;
  315. char *path = 0; // pacify compiler
  316. FILE *fp = 0;
  317. if (r->filename != 0) {
  318. if (r->type == RESOURCE_FONT) {
  319. fp = font::open_file(r->filename, &path);
  320. if (!fp) {
  321. error("can't find `%1'", r->filename);
  322. a_delete r->filename;
  323. r->filename = 0;
  324. }
  325. }
  326. else {
  327. errno = 0;
  328. fp = include_search_path.open_file_cautious(r->filename);
  329. if (!fp) {
  330. error("can't open `%1': %2", r->filename, strerror(errno));
  331. a_delete r->filename;
  332. r->filename = 0;
  333. }
  334. else
  335. path = r->filename;
  336. }
  337. }
  338. if (fp) {
  339. if (outfp) {
  340. if (r->type == RESOURCE_FILE && is_document) {
  341. fputs("%%BeginDocument: ", outfp);
  342. print_ps_string(r->name, outfp);
  343. putc('\n', outfp);
  344. }
  345. else {
  346. fputs("%%BeginResource: ", outfp);
  347. r->print_type_and_name(outfp);
  348. putc('\n', outfp);
  349. }
  350. }
  351. process_file(rank, fp, path, outfp);
  352. fclose(fp);
  353. if (r->type == RESOURCE_FONT)
  354. a_delete path;
  355. if (outfp) {
  356. if (r->type == RESOURCE_FILE && is_document)
  357. fputs("%%EndDocument\n", outfp);
  358. else
  359. fputs("%%EndResource\n", outfp);
  360. }
  361. r->flags |= resource::SUPPLIED;
  362. }
  363. else {
  364. if (outfp) {
  365. if (r->type == RESOURCE_FILE && is_document) {
  366. fputs("%%IncludeDocument: ", outfp);
  367. print_ps_string(r->name, outfp);
  368. putc('\n', outfp);
  369. }
  370. else {
  371. fputs("%%IncludeResource: ", outfp);
  372. r->print_type_and_name(outfp);
  373. putc('\n', outfp);
  374. }
  375. }
  376. r->flags |= resource::NEEDED;
  377. }
  378. r->flags &= ~resource::BUSY;
  379. }
  380. #define PS_MAGIC "%!PS-Adobe-"
  381. static int ps_get_line(string &buf, FILE *fp)
  382. {
  383. buf.clear();
  384. int c = getc(fp);
  385. if (c == EOF)
  386. return 0;
  387. current_lineno++;
  388. while (c != '\r' && c != '\n' && c != EOF) {
  389. if (!valid_input_table[c])
  390. error("invalid input character code %1", int(c));
  391. buf += c;
  392. c = getc(fp);
  393. }
  394. buf += '\n';
  395. buf += '\0';
  396. if (c == '\r') {
  397. c = getc(fp);
  398. if (c != EOF && c != '\n')
  399. ungetc(c, fp);
  400. }
  401. return 1;
  402. }
  403. static int read_text_arg(const char **pp, string &res)
  404. {
  405. res.clear();
  406. while (white_space(**pp))
  407. *pp += 1;
  408. if (**pp == '\0') {
  409. error("missing argument");
  410. return 0;
  411. }
  412. if (**pp != '(') {
  413. for (; **pp != '\0' && !white_space(**pp); *pp += 1)
  414. res += **pp;
  415. return 1;
  416. }
  417. *pp += 1;
  418. res.clear();
  419. int level = 0;
  420. for (;;) {
  421. if (**pp == '\0' || **pp == '\r' || **pp == '\n') {
  422. error("missing ')'");
  423. return 0;
  424. }
  425. if (**pp == ')') {
  426. if (level == 0) {
  427. *pp += 1;
  428. break;
  429. }
  430. res += **pp;
  431. level--;
  432. }
  433. else if (**pp == '(') {
  434. level++;
  435. res += **pp;
  436. }
  437. else if (**pp == '\\') {
  438. *pp += 1;
  439. switch (**pp) {
  440. case 'n':
  441. res += '\n';
  442. break;
  443. case 'r':
  444. res += '\n';
  445. break;
  446. case 't':
  447. res += '\t';
  448. break;
  449. case 'b':
  450. res += '\b';
  451. break;
  452. case 'f':
  453. res += '\f';
  454. break;
  455. case '0':
  456. case '1':
  457. case '2':
  458. case '3':
  459. case '4':
  460. case '5':
  461. case '6':
  462. case '7':
  463. {
  464. int val = **pp - '0';
  465. if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
  466. *pp += 1;
  467. val = val*8 + (**pp - '0');
  468. if ((*pp)[1] >= '0' && (*pp)[1] <= '7') {
  469. *pp += 1;
  470. val = val*8 + (**pp - '0');
  471. }
  472. }
  473. }
  474. break;
  475. default:
  476. res += **pp;
  477. break;
  478. }
  479. }
  480. else
  481. res += **pp;
  482. *pp += 1;
  483. }
  484. return 1;
  485. }
  486. resource *resource_manager::read_file_arg(const char **ptr)
  487. {
  488. string arg;
  489. if (!read_text_arg(ptr, arg))
  490. return 0;
  491. return lookup_resource(RESOURCE_FILE, arg);
  492. }
  493. resource *resource_manager::read_font_arg(const char **ptr)
  494. {
  495. string arg;
  496. if (!read_text_arg(ptr, arg))
  497. return 0;
  498. return lookup_resource(RESOURCE_FONT, arg);
  499. }
  500. resource *resource_manager::read_procset_arg(const char **ptr)
  501. {
  502. string arg;
  503. if (!read_text_arg(ptr, arg))
  504. return 0;
  505. string version;
  506. if (!read_text_arg(ptr, version))
  507. return 0;
  508. unsigned revision;
  509. if (!read_uint_arg(ptr, &revision))
  510. return 0;
  511. return lookup_resource(RESOURCE_PROCSET, arg, version, revision);
  512. }
  513. resource *resource_manager::read_resource_arg(const char **ptr)
  514. {
  515. while (white_space(**ptr))
  516. *ptr += 1;
  517. const char *name = *ptr;
  518. while (**ptr != '\0' && !white_space(**ptr))
  519. *ptr += 1;
  520. if (name == *ptr) {
  521. error("missing resource type");
  522. return 0;
  523. }
  524. int ri;
  525. for (ri = 0; ri < NRESOURCES; ri++)
  526. if (strlen(resource_table[ri]) == size_t(*ptr - name)
  527. && memcmp(resource_table[ri], name, *ptr - name) == 0)
  528. break;
  529. if (ri >= NRESOURCES) {
  530. error("unknown resource type");
  531. return 0;
  532. }
  533. if (ri == RESOURCE_PROCSET)
  534. return read_procset_arg(ptr);
  535. string arg;
  536. if (!read_text_arg(ptr, arg))
  537. return 0;
  538. return lookup_resource(resource_type(ri), arg);
  539. }
  540. static const char *matches_comment(string &buf, const char *comment)
  541. {
  542. if ((size_t)buf.length() < strlen(comment) + 3)
  543. return 0;
  544. if (buf[0] != '%' || buf[1] != '%')
  545. return 0;
  546. const char *bufp = buf.contents() + 2;
  547. for (; *comment; comment++, bufp++)
  548. if (*bufp != *comment)
  549. return 0;
  550. if (comment[-1] == ':')
  551. return bufp;
  552. if (*bufp == '\0' || white_space(*bufp))
  553. return bufp;
  554. return 0;
  555. }
  556. // Return 1 if the line should be copied out.
  557. int resource_manager::do_begin_resource(const char *ptr, int, FILE *,
  558. FILE *)
  559. {
  560. resource *r = read_resource_arg(&ptr);
  561. if (r)
  562. r->flags |= resource::SUPPLIED;
  563. return 1;
  564. }
  565. int resource_manager::do_include_resource(const char *ptr, int rank, FILE *,
  566. FILE *outfp)
  567. {
  568. resource *r = read_resource_arg(&ptr);
  569. if (r) {
  570. if (r->type == RESOURCE_FONT) {
  571. if (rank >= 0)
  572. supply_resource(r, rank + 1, outfp);
  573. else
  574. r->flags |= resource::FONT_NEEDED;
  575. }
  576. else
  577. supply_resource(r, rank, outfp);
  578. }
  579. return 0;
  580. }
  581. int resource_manager::do_begin_document(const char *ptr, int, FILE *,
  582. FILE *)
  583. {
  584. resource *r = read_file_arg(&ptr);
  585. if (r)
  586. r->flags |= resource::SUPPLIED;
  587. return 1;
  588. }
  589. int resource_manager::do_include_document(const char *ptr, int rank, FILE *,
  590. FILE *outfp)
  591. {
  592. resource *r = read_file_arg(&ptr);
  593. if (r)
  594. supply_resource(r, rank, outfp, 1);
  595. return 0;
  596. }
  597. int resource_manager::do_begin_procset(const char *ptr, int, FILE *,
  598. FILE *outfp)
  599. {
  600. resource *r = read_procset_arg(&ptr);
  601. if (r) {
  602. r->flags |= resource::SUPPLIED;
  603. if (outfp) {
  604. fputs("%%BeginResource: ", outfp);
  605. r->print_type_and_name(outfp);
  606. putc('\n', outfp);
  607. }
  608. }
  609. return 0;
  610. }
  611. int resource_manager::do_include_procset(const char *ptr, int rank, FILE *,
  612. FILE *outfp)
  613. {
  614. resource *r = read_procset_arg(&ptr);
  615. if (r)
  616. supply_resource(r, rank, outfp);
  617. return 0;
  618. }
  619. int resource_manager::do_begin_file(const char *ptr, int, FILE *,
  620. FILE *outfp)
  621. {
  622. resource *r = read_file_arg(&ptr);
  623. if (r) {
  624. r->flags |= resource::SUPPLIED;
  625. if (outfp) {
  626. fputs("%%BeginResource: ", outfp);
  627. r->print_type_and_name(outfp);
  628. putc('\n', outfp);
  629. }
  630. }
  631. return 0;
  632. }
  633. int resource_manager::do_include_file(const char *ptr, int rank, FILE *,
  634. FILE *outfp)
  635. {
  636. resource *r = read_file_arg(&ptr);
  637. if (r)
  638. supply_resource(r, rank, outfp);
  639. return 0;
  640. }
  641. int resource_manager::do_begin_font(const char *ptr, int, FILE *,
  642. FILE *outfp)
  643. {
  644. resource *r = read_font_arg(&ptr);
  645. if (r) {
  646. r->flags |= resource::SUPPLIED;
  647. if (outfp) {
  648. fputs("%%BeginResource: ", outfp);
  649. r->print_type_and_name(outfp);
  650. putc('\n', outfp);
  651. }
  652. }
  653. return 0;
  654. }
  655. int resource_manager::do_include_font(const char *ptr, int rank, FILE *,
  656. FILE *outfp)
  657. {
  658. resource *r = read_font_arg(&ptr);
  659. if (r) {
  660. if (rank >= 0)
  661. supply_resource(r, rank + 1, outfp);
  662. else
  663. r->flags |= resource::FONT_NEEDED;
  664. }
  665. return 0;
  666. }
  667. int resource_manager::change_to_end_resource(const char *, int, FILE *,
  668. FILE *outfp)
  669. {
  670. if (outfp)
  671. fputs("%%EndResource\n", outfp);
  672. return 0;
  673. }
  674. int resource_manager::do_begin_preview(const char *, int, FILE *fp, FILE *)
  675. {
  676. string buf;
  677. do {
  678. if (!ps_get_line(buf, fp)) {
  679. error("end of file in preview section");
  680. break;
  681. }
  682. } while (!matches_comment(buf, "EndPreview"));
  683. return 0;
  684. }
  685. int read_one_of(const char **ptr, const char **s, int n)
  686. {
  687. while (white_space(**ptr))
  688. *ptr += 1;
  689. if (**ptr == '\0')
  690. return -1;
  691. const char *start = *ptr;
  692. do {
  693. ++(*ptr);
  694. } while (**ptr != '\0' && !white_space(**ptr));
  695. for (int i = 0; i < n; i++)
  696. if (strlen(s[i]) == size_t(*ptr - start)
  697. && memcmp(s[i], start, *ptr - start) == 0)
  698. return i;
  699. return -1;
  700. }
  701. void skip_possible_newline(FILE *fp, FILE *outfp)
  702. {
  703. int c = getc(fp);
  704. if (c == '\r') {
  705. current_lineno++;
  706. if (outfp)
  707. putc(c, outfp);
  708. int cc = getc(fp);
  709. if (cc != '\n') {
  710. if (cc != EOF)
  711. ungetc(cc, fp);
  712. }
  713. else {
  714. if (outfp)
  715. putc(cc, outfp);
  716. }
  717. }
  718. else if (c == '\n') {
  719. current_lineno++;
  720. if (outfp)
  721. putc(c, outfp);
  722. }
  723. else if (c != EOF)
  724. ungetc(c, fp);
  725. }
  726. int resource_manager::do_begin_data(const char *ptr, int, FILE *fp,
  727. FILE *outfp)
  728. {
  729. while (white_space(*ptr))
  730. ptr++;
  731. const char *start = ptr;
  732. unsigned numberof;
  733. if (!read_uint_arg(&ptr, &numberof))
  734. return 0;
  735. static const char *types[] = { "Binary", "Hex", "ASCII" };
  736. const int Binary = 0;
  737. int type = 0;
  738. static const char *units[] = { "Bytes", "Lines" };
  739. const int Bytes = 0;
  740. int unit = Bytes;
  741. while (white_space(*ptr))
  742. ptr++;
  743. if (*ptr != '\0') {
  744. type = read_one_of(&ptr, types, 3);
  745. if (type < 0) {
  746. error("bad data type");
  747. return 0;
  748. }
  749. while (white_space(*ptr))
  750. ptr++;
  751. if (*ptr != '\0') {
  752. unit = read_one_of(&ptr, units, 2);
  753. if (unit < 0) {
  754. error("expected `Bytes' or `Lines'");
  755. return 0;
  756. }
  757. }
  758. }
  759. if (type != Binary)
  760. return 1;
  761. if (outfp) {
  762. fputs("%%BeginData: ", outfp);
  763. fputs(start, outfp);
  764. }
  765. if (numberof > 0) {
  766. unsigned bytecount = 0;
  767. unsigned linecount = 0;
  768. do {
  769. int c = getc(fp);
  770. if (c == EOF) {
  771. error("end of file within data section");
  772. return 0;
  773. }
  774. if (outfp)
  775. putc(c, outfp);
  776. bytecount++;
  777. if (c == '\r') {
  778. int cc = getc(fp);
  779. if (cc != '\n') {
  780. linecount++;
  781. current_lineno++;
  782. }
  783. if (cc != EOF)
  784. ungetc(c, fp);
  785. }
  786. else if (c == '\n') {
  787. linecount++;
  788. current_lineno++;
  789. }
  790. } while ((unit == Bytes ? bytecount : linecount) < numberof);
  791. }
  792. skip_possible_newline(fp, outfp);
  793. string buf;
  794. if (!ps_get_line(buf, fp)) {
  795. error("missing %%%%EndData line");
  796. return 0;
  797. }
  798. if (!matches_comment(buf, "EndData"))
  799. error("bad %%%%EndData line");
  800. if (outfp)
  801. fputs(buf.contents(), outfp);
  802. return 0;
  803. }
  804. int resource_manager::do_begin_binary(const char *ptr, int, FILE *fp,
  805. FILE *outfp)
  806. {
  807. if (!outfp)
  808. return 0;
  809. unsigned count;
  810. if (!read_uint_arg(&ptr, &count))
  811. return 0;
  812. if (outfp)
  813. fprintf(outfp, "%%%%BeginData: %u Binary Bytes\n", count);
  814. while (count != 0) {
  815. int c = getc(fp);
  816. if (c == EOF) {
  817. error("end of file within binary section");
  818. return 0;
  819. }
  820. if (outfp)
  821. putc(c, outfp);
  822. --count;
  823. if (c == '\r') {
  824. int cc = getc(fp);
  825. if (cc != '\n')
  826. current_lineno++;
  827. if (cc != EOF)
  828. ungetc(cc, fp);
  829. }
  830. else if (c == '\n')
  831. current_lineno++;
  832. }
  833. skip_possible_newline(fp, outfp);
  834. string buf;
  835. if (!ps_get_line(buf, fp)) {
  836. error("missing %%%%EndBinary line");
  837. return 0;
  838. }
  839. if (!matches_comment(buf, "EndBinary")) {
  840. error("bad %%%%EndBinary line");
  841. if (outfp)
  842. fputs(buf.contents(), outfp);
  843. }
  844. else if (outfp)
  845. fputs("%%EndData\n", outfp);
  846. return 0;
  847. }
  848. static unsigned parse_extensions(const char *ptr)
  849. {
  850. unsigned flags = 0;
  851. for (;;) {
  852. while (white_space(*ptr))
  853. ptr++;
  854. if (*ptr == '\0')
  855. break;
  856. const char *name = ptr;
  857. do {
  858. ++ptr;
  859. } while (*ptr != '\0' && !white_space(*ptr));
  860. int i;
  861. for (i = 0; i < NEXTENSIONS; i++)
  862. if (strlen(extension_table[i]) == size_t(ptr - name)
  863. && memcmp(extension_table[i], name, ptr - name) == 0) {
  864. flags |= (1 << i);
  865. break;
  866. }
  867. if (i >= NEXTENSIONS) {
  868. string s(name, ptr - name);
  869. s += '\0';
  870. error("unknown extension `%1'", s.contents());
  871. }
  872. }
  873. return flags;
  874. }
  875. // XXX if it has not been surrounded with {Begin,End}Document need to strip
  876. // out Page: Trailer {Begin,End}Prolog {Begin,End}Setup sections.
  877. // XXX Perhaps the decision whether to use BeginDocument or
  878. // BeginResource: file should be postponed till we have seen
  879. // the first line of the file.
  880. void resource_manager::process_file(int rank, FILE *fp, const char *filename,
  881. FILE *outfp)
  882. {
  883. // If none of these comments appear in the header section, and we are
  884. // just analyzing the file (ie outfp is 0), then we can return immediately.
  885. static const char *header_comment_table[] = {
  886. "DocumentNeededResources:",
  887. "DocumentSuppliedResources:",
  888. "DocumentNeededFonts:",
  889. "DocumentSuppliedFonts:",
  890. "DocumentNeededProcSets:",
  891. "DocumentSuppliedProcSets:",
  892. "DocumentNeededFiles:",
  893. "DocumentSuppliedFiles:",
  894. };
  895. const int NHEADER_COMMENTS = sizeof(header_comment_table)
  896. / sizeof(header_comment_table[0]);
  897. struct comment_info {
  898. const char *name;
  899. int (resource_manager::*proc)(const char *, int, FILE *, FILE *);
  900. };
  901. static comment_info comment_table[] = {
  902. { "BeginResource:", &resource_manager::do_begin_resource },
  903. { "IncludeResource:", &resource_manager::do_include_resource },
  904. { "BeginDocument:", &resource_manager::do_begin_document },
  905. { "IncludeDocument:", &resource_manager::do_include_document },
  906. { "BeginProcSet:", &resource_manager::do_begin_procset },
  907. { "IncludeProcSet:", &resource_manager::do_include_procset },
  908. { "BeginFont:", &resource_manager::do_begin_font },
  909. { "IncludeFont:", &resource_manager::do_include_font },
  910. { "BeginFile:", &resource_manager::do_begin_file },
  911. { "IncludeFile:", &resource_manager::do_include_file },
  912. { "EndProcSet", &resource_manager::change_to_end_resource },
  913. { "EndFont", &resource_manager::change_to_end_resource },
  914. { "EndFile", &resource_manager::change_to_end_resource },
  915. { "BeginPreview:", &resource_manager::do_begin_preview },
  916. { "BeginData:", &resource_manager::do_begin_data },
  917. { "BeginBinary:", &resource_manager::do_begin_binary },
  918. };
  919. const int NCOMMENTS = sizeof(comment_table)/sizeof(comment_table[0]);
  920. string buf;
  921. int saved_lineno = current_lineno;
  922. const char *saved_filename = current_filename;
  923. current_filename = filename;
  924. current_lineno = 0;
  925. if (!ps_get_line(buf, fp)) {
  926. current_filename = saved_filename;
  927. current_lineno = saved_lineno;
  928. return;
  929. }
  930. if ((size_t)buf.length() < sizeof(PS_MAGIC)
  931. || memcmp(buf.contents(), PS_MAGIC, sizeof(PS_MAGIC) - 1) != 0) {
  932. if (outfp) {
  933. do {
  934. if (!(broken_flags & STRIP_PERCENT_BANG)
  935. || buf[0] != '%' || buf[1] != '!')
  936. fputs(buf.contents(), outfp);
  937. } while (ps_get_line(buf, fp));
  938. }
  939. }
  940. else {
  941. if (!(broken_flags & STRIP_PERCENT_BANG) && outfp)
  942. fputs(buf.contents(), outfp);
  943. int in_header = 1;
  944. int interesting = 0;
  945. int had_extensions_comment = 0;
  946. int had_language_level_comment = 0;
  947. for (;;) {
  948. if (!ps_get_line(buf, fp))
  949. break;
  950. int copy_this_line = 1;
  951. if (buf[0] == '%') {
  952. if (buf[1] == '%') {
  953. const char *ptr;
  954. int i;
  955. for (i = 0; i < NCOMMENTS; i++)
  956. if ((ptr = matches_comment(buf, comment_table[i].name))) {
  957. copy_this_line
  958. = (this->*(comment_table[i].proc))(ptr, rank, fp, outfp);
  959. break;
  960. }
  961. if (i >= NCOMMENTS && in_header) {
  962. if ((ptr = matches_comment(buf, "EndComments")))
  963. in_header = 0;
  964. else if (!had_extensions_comment
  965. && (ptr = matches_comment(buf, "Extensions:"))) {
  966. extensions |= parse_extensions(ptr);
  967. // XXX handle possibility that next line is %%+
  968. had_extensions_comment = 1;
  969. }
  970. else if (!had_language_level_comment
  971. && (ptr = matches_comment(buf, "LanguageLevel:"))) {
  972. unsigned ll;
  973. if (read_uint_arg(&ptr, &ll) && ll > language_level)
  974. language_level = ll;
  975. had_language_level_comment = 1;
  976. }
  977. else {
  978. for (i = 0; i < NHEADER_COMMENTS; i++)
  979. if (matches_comment(buf, header_comment_table[i])) {
  980. interesting = 1;
  981. break;
  982. }
  983. }
  984. }
  985. if ((broken_flags & STRIP_STRUCTURE_COMMENTS)
  986. && (matches_comment(buf, "EndProlog")
  987. || matches_comment(buf, "Page:")
  988. || matches_comment(buf, "Trailer")))
  989. copy_this_line = 0;
  990. }
  991. else if (buf[1] == '!') {
  992. if (broken_flags & STRIP_PERCENT_BANG)
  993. copy_this_line = 0;
  994. }
  995. }
  996. else
  997. in_header = 0;
  998. if (!outfp && !in_header && !interesting)
  999. break;
  1000. if (copy_this_line && outfp)
  1001. fputs(buf.contents(), outfp);
  1002. }
  1003. }
  1004. current_filename = saved_filename;
  1005. current_lineno = saved_lineno;
  1006. }
  1007. void resource_manager::read_download_file()
  1008. {
  1009. char *path = 0;
  1010. FILE *fp = font::open_file("download", &path);
  1011. if (!fp)
  1012. fatal("can't find `download'");
  1013. char buf[512];
  1014. int lineno = 0;
  1015. while (fgets(buf, sizeof(buf), fp)) {
  1016. lineno++;
  1017. char *p = strtok(buf, " \t\r\n");
  1018. if (p == 0 || *p == '#')
  1019. continue;
  1020. char *q = strtok(0, " \t\r\n");
  1021. if (!q)
  1022. fatal_with_file_and_line(path, lineno, "missing filename");
  1023. lookup_font(p)->filename = strsave(q);
  1024. }
  1025. a_delete path;
  1026. fclose(fp);
  1027. }
  1028. // XXX Can we share some code with ps_output::put_string()?
  1029. static void print_ps_string(const string &s, FILE *outfp)
  1030. {
  1031. int len = s.length();
  1032. const char *str = s.contents();
  1033. int funny = 0;
  1034. if (str[0] == '(')
  1035. funny = 1;
  1036. else {
  1037. for (int i = 0; i < len; i++)
  1038. if (str[i] <= 040 || str[i] > 0176) {
  1039. funny = 1;
  1040. break;
  1041. }
  1042. }
  1043. if (!funny) {
  1044. put_string(s, outfp);
  1045. return;
  1046. }
  1047. int level = 0;
  1048. int i;
  1049. for (i = 0; i < len; i++)
  1050. if (str[i] == '(')
  1051. level++;
  1052. else if (str[i] == ')' && --level < 0)
  1053. break;
  1054. putc('(', outfp);
  1055. for (i = 0; i < len; i++)
  1056. switch (str[i]) {
  1057. case '(':
  1058. case ')':
  1059. if (level != 0)
  1060. putc('\\', outfp);
  1061. putc(str[i], outfp);
  1062. break;
  1063. case '\\':
  1064. fputs("\\\\", outfp);
  1065. break;
  1066. case '\n':
  1067. fputs("\\n", outfp);
  1068. break;
  1069. case '\r':
  1070. fputs("\\r", outfp);
  1071. break;
  1072. case '\t':
  1073. fputs("\\t", outfp);
  1074. break;
  1075. case '\b':
  1076. fputs("\\b", outfp);
  1077. break;
  1078. case '\f':
  1079. fputs("\\f", outfp);
  1080. break;
  1081. default:
  1082. if (str[i] < 040 || str[i] > 0176)
  1083. fprintf(outfp, "\\%03o", str[i] & 0377);
  1084. else
  1085. putc(str[i], outfp);
  1086. break;
  1087. }
  1088. putc(')', outfp);
  1089. }
  1090. void resource_manager::print_extensions_comment(FILE *outfp)
  1091. {
  1092. if (extensions) {
  1093. fputs("%%Extensions:", outfp);
  1094. for (int i = 0; i < NEXTENSIONS; i++)
  1095. if (extensions & (1 << i)) {
  1096. putc(' ', outfp);
  1097. fputs(extension_table[i], outfp);
  1098. }
  1099. putc('\n', outfp);
  1100. }
  1101. }
  1102. void resource_manager::print_language_level_comment(FILE *outfp)
  1103. {
  1104. if (language_level)
  1105. fprintf(outfp, "%%%%LanguageLevel: %u\n", language_level);
  1106. }