/contrib/groff/src/preproc/eqn/main.cpp

https://bitbucket.org/freebsd/freebsd-head/ · C++ · 395 lines · 357 code · 13 blank · 25 comment · 129 complexity · 28a967761f45ca68a7b5e970270130da MD5 · raw file

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002
  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 "eqn.h"
  18. #include "stringclass.h"
  19. #include "device.h"
  20. #include "searchpath.h"
  21. #include "macropath.h"
  22. #include "htmlhint.h"
  23. #include "pbox.h"
  24. #include "ctype.h"
  25. #define STARTUP_FILE "eqnrc"
  26. extern int yyparse();
  27. extern "C" const char *Version_string;
  28. static char *delim_search (char *, int);
  29. static int inline_equation (FILE *, string &, string &);
  30. char start_delim = '\0';
  31. char end_delim = '\0';
  32. int non_empty_flag;
  33. int inline_flag;
  34. int draw_flag = 0;
  35. int one_size_reduction_flag = 0;
  36. int compatible_flag = 0;
  37. int no_newline_in_delim_flag = 0;
  38. int html = 0;
  39. int read_line(FILE *fp, string *p)
  40. {
  41. p->clear();
  42. int c = -1;
  43. while ((c = getc(fp)) != EOF) {
  44. if (!invalid_input_char(c))
  45. *p += char(c);
  46. else
  47. error("invalid input character code `%1'", c);
  48. if (c == '\n')
  49. break;
  50. }
  51. current_lineno++;
  52. return p->length() > 0;
  53. }
  54. void do_file(FILE *fp, const char *filename)
  55. {
  56. string linebuf;
  57. string str;
  58. printf(".lf 1 %s\n", filename);
  59. current_filename = filename;
  60. current_lineno = 0;
  61. while (read_line(fp, &linebuf)) {
  62. if (linebuf.length() >= 4
  63. && linebuf[0] == '.' && linebuf[1] == 'l' && linebuf[2] == 'f'
  64. && (linebuf[3] == ' ' || linebuf[3] == '\n' || compatible_flag)) {
  65. put_string(linebuf, stdout);
  66. linebuf += '\0';
  67. if (interpret_lf_args(linebuf.contents() + 3))
  68. current_lineno--;
  69. }
  70. else if (linebuf.length() >= 4
  71. && linebuf[0] == '.'
  72. && linebuf[1] == 'E'
  73. && linebuf[2] == 'Q'
  74. && (linebuf[3] == ' ' || linebuf[3] == '\n'
  75. || compatible_flag)) {
  76. put_string(linebuf, stdout);
  77. int start_lineno = current_lineno + 1;
  78. str.clear();
  79. for (;;) {
  80. if (!read_line(fp, &linebuf))
  81. fatal("end of file before .EN");
  82. if (linebuf.length() >= 3 && linebuf[0] == '.' && linebuf[1] == 'E') {
  83. if (linebuf[2] == 'N'
  84. && (linebuf.length() == 3 || linebuf[3] == ' '
  85. || linebuf[3] == '\n' || compatible_flag))
  86. break;
  87. else if (linebuf[2] == 'Q' && linebuf.length() > 3
  88. && (linebuf[3] == ' ' || linebuf[3] == '\n'
  89. || compatible_flag))
  90. fatal("nested .EQ");
  91. }
  92. str += linebuf;
  93. }
  94. str += '\0';
  95. start_string();
  96. init_lex(str.contents(), current_filename, start_lineno);
  97. non_empty_flag = 0;
  98. inline_flag = 0;
  99. yyparse();
  100. restore_compatibility();
  101. if (non_empty_flag) {
  102. printf(".lf %d\n", current_lineno - 1);
  103. output_string();
  104. }
  105. printf(".lf %d\n", current_lineno);
  106. put_string(linebuf, stdout);
  107. }
  108. else if (start_delim != '\0' && linebuf.search(start_delim) >= 0
  109. && inline_equation(fp, linebuf, str))
  110. ;
  111. else
  112. put_string(linebuf, stdout);
  113. }
  114. current_filename = 0;
  115. current_lineno = 0;
  116. }
  117. // Handle an inline equation. Return 1 if it was an inline equation,
  118. // otherwise.
  119. static int inline_equation(FILE *fp, string &linebuf, string &str)
  120. {
  121. linebuf += '\0';
  122. char *ptr = &linebuf[0];
  123. char *start = delim_search(ptr, start_delim);
  124. if (!start) {
  125. // It wasn't a delimiter after all.
  126. linebuf.set_length(linebuf.length() - 1); // strip the '\0'
  127. return 0;
  128. }
  129. start_string();
  130. inline_flag = 1;
  131. for (;;) {
  132. if (no_newline_in_delim_flag && strchr(start + 1, end_delim) == 0) {
  133. error("missing `%1'", end_delim);
  134. char *nl = strchr(start + 1, '\n');
  135. if (nl != 0)
  136. *nl = '\0';
  137. do_text(ptr);
  138. break;
  139. }
  140. int start_lineno = current_lineno;
  141. *start = '\0';
  142. do_text(ptr);
  143. ptr = start + 1;
  144. str.clear();
  145. for (;;) {
  146. char *end = strchr(ptr, end_delim);
  147. if (end != 0) {
  148. *end = '\0';
  149. str += ptr;
  150. ptr = end + 1;
  151. break;
  152. }
  153. str += ptr;
  154. if (!read_line(fp, &linebuf))
  155. fatal("unterminated `%1' at line %2, looking for `%3'",
  156. start_delim, start_lineno, end_delim);
  157. linebuf += '\0';
  158. ptr = &linebuf[0];
  159. }
  160. str += '\0';
  161. if (html) {
  162. printf(".as1 %s ", LINE_STRING);
  163. html_begin_suppress();
  164. printf("\n");
  165. }
  166. init_lex(str.contents(), current_filename, start_lineno);
  167. yyparse();
  168. if (html) {
  169. printf(".as1 %s ", LINE_STRING);
  170. html_end_suppress();
  171. printf("\n");
  172. }
  173. start = delim_search(ptr, start_delim);
  174. if (start == 0) {
  175. char *nl = strchr(ptr, '\n');
  176. if (nl != 0)
  177. *nl = '\0';
  178. do_text(ptr);
  179. break;
  180. }
  181. }
  182. restore_compatibility();
  183. printf(".lf %d\n", current_lineno);
  184. output_string();
  185. printf(".lf %d\n", current_lineno + 1);
  186. return 1;
  187. }
  188. /* Search for delim. Skip over number register and string names etc. */
  189. static char *delim_search(char *ptr, int delim)
  190. {
  191. while (*ptr) {
  192. if (*ptr == delim)
  193. return ptr;
  194. if (*ptr++ == '\\') {
  195. switch (*ptr) {
  196. case 'n':
  197. case '*':
  198. case 'f':
  199. case 'g':
  200. case 'k':
  201. switch (*++ptr) {
  202. case '\0':
  203. case '\\':
  204. break;
  205. case '(':
  206. if (*++ptr != '\\' && *ptr != '\0'
  207. && *++ptr != '\\' && *ptr != '\0')
  208. ptr++;
  209. break;
  210. case '[':
  211. while (*++ptr != '\0')
  212. if (*ptr == ']') {
  213. ptr++;
  214. break;
  215. }
  216. break;
  217. default:
  218. ptr++;
  219. break;
  220. }
  221. break;
  222. case '\\':
  223. case '\0':
  224. break;
  225. default:
  226. ptr++;
  227. break;
  228. }
  229. }
  230. }
  231. return 0;
  232. }
  233. void usage(FILE *stream)
  234. {
  235. fprintf(stream,
  236. "usage: %s [ -rvDCNR ] -dxx -fn -sn -pn -mn -Mdir -Ts [ files ... ]\n",
  237. program_name);
  238. }
  239. int main(int argc, char **argv)
  240. {
  241. program_name = argv[0];
  242. static char stderr_buf[BUFSIZ];
  243. setbuf(stderr, stderr_buf);
  244. int opt;
  245. int load_startup_file = 1;
  246. static const struct option long_options[] = {
  247. { "help", no_argument, 0, CHAR_MAX + 1 },
  248. { "version", no_argument, 0, 'v' },
  249. { NULL, 0, 0, 0 }
  250. };
  251. while ((opt = getopt_long(argc, argv, "DCRvd:f:p:s:m:T:M:rN", long_options,
  252. NULL))
  253. != EOF)
  254. switch (opt) {
  255. case 'C':
  256. compatible_flag = 1;
  257. break;
  258. case 'R': // don't load eqnrc
  259. load_startup_file = 0;
  260. break;
  261. case 'M':
  262. config_macro_path.command_line_dir(optarg);
  263. break;
  264. case 'v':
  265. {
  266. printf("GNU eqn (groff) version %s\n", Version_string);
  267. exit(0);
  268. break;
  269. }
  270. case 'd':
  271. if (optarg[0] == '\0' || optarg[1] == '\0')
  272. error("-d requires two character argument");
  273. else if (invalid_input_char(optarg[0]))
  274. error("bad delimiter `%1'", optarg[0]);
  275. else if (invalid_input_char(optarg[1]))
  276. error("bad delimiter `%1'", optarg[1]);
  277. else {
  278. start_delim = optarg[0];
  279. end_delim = optarg[1];
  280. }
  281. break;
  282. case 'f':
  283. set_gfont(optarg);
  284. break;
  285. case 'T':
  286. device = optarg;
  287. if (strcmp(device, "ps:html") == 0) {
  288. device = "ps";
  289. html = 1;
  290. }
  291. break;
  292. case 's':
  293. if (!set_gsize(optarg))
  294. error("invalid size `%1'", optarg);
  295. break;
  296. case 'p':
  297. {
  298. int n;
  299. if (sscanf(optarg, "%d", &n) == 1)
  300. set_script_reduction(n);
  301. else
  302. error("bad size `%1'", optarg);
  303. }
  304. break;
  305. case 'm':
  306. {
  307. int n;
  308. if (sscanf(optarg, "%d", &n) == 1)
  309. set_minimum_size(n);
  310. else
  311. error("bad size `%1'", optarg);
  312. }
  313. break;
  314. case 'r':
  315. one_size_reduction_flag = 1;
  316. break;
  317. case 'D':
  318. warning("-D option is obsolete: use `set draw_lines 1' instead");
  319. draw_flag = 1;
  320. break;
  321. case 'N':
  322. no_newline_in_delim_flag = 1;
  323. break;
  324. case CHAR_MAX + 1: // --help
  325. usage(stdout);
  326. exit(0);
  327. break;
  328. case '?':
  329. usage(stderr);
  330. exit(1);
  331. break;
  332. default:
  333. assert(0);
  334. }
  335. init_table(device);
  336. init_char_table();
  337. printf(".if !'\\*(.T'%s' "
  338. ".if !'\\*(.T'html' " // the html device uses `-Tps' to render
  339. // equations as images
  340. ".tm warning: %s should have been given a `-T\\*(.T' option\n",
  341. device, program_name);
  342. printf(".if '\\*(.T'html' "
  343. ".if !'%s'ps' "
  344. ".tm warning: %s should have been given a `-Tps' option\n",
  345. device, program_name);
  346. printf(".if '\\*(.T'html' "
  347. ".if !'%s'ps' "
  348. ".tm warning: (it is advisable to invoke groff via: groff -Thtml -e)\n",
  349. device);
  350. if (load_startup_file) {
  351. char *path;
  352. FILE *fp = config_macro_path.open_file(STARTUP_FILE, &path);
  353. if (fp) {
  354. do_file(fp, path);
  355. fclose(fp);
  356. a_delete path;
  357. }
  358. }
  359. if (optind >= argc)
  360. do_file(stdin, "-");
  361. else
  362. for (int i = optind; i < argc; i++)
  363. if (strcmp(argv[i], "-") == 0)
  364. do_file(stdin, "-");
  365. else {
  366. errno = 0;
  367. FILE *fp = fopen(argv[i], "r");
  368. if (!fp)
  369. fatal("can't open `%1': %2", argv[i], strerror(errno));
  370. else {
  371. do_file(fp, argv[i]);
  372. fclose(fp);
  373. }
  374. }
  375. if (ferror(stdout) || fflush(stdout) < 0)
  376. fatal("output error");
  377. return 0;
  378. }