eqn: pass the message as a string to fprintf in errdie
[neateqn.git] / eqn.c
blob8738bd438e247dc5e9430623764ac875d781d9bc
1 /*
2 * NEATEQN NEATROFF PREPROCESSOR
4 * Copyright (C) 2014-2017 Ali Gholami Rudi <ali at rudi dot ir>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <ctype.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include "eqn.h"
24 /* flags passed to eqn_box() */
25 #define EQN_TSMASK 0x00ffff /* style mask */
26 #define EQN_SUB 0x020000 /* this is a subscript */
27 #define EQN_FROM 0x040000 /* this is a from block */
29 static char gfont[FNLEN] = "2";
30 static char grfont[FNLEN] = "1";
31 static char gbfont[FNLEN] = "3";
32 static char gsize[FNLEN] = "\\n[" EQNSZ "]";
33 static char eqn_lineup[128]; /* the lineup horizontal request */
34 static int eqn_lineupreg; /* the number register holding lineup width */
35 static int eqn_mk; /* the value of MK */
37 static struct box *eqn_box(int flg, struct box *pre, int sz0, char *fn0);
39 /* read equations until delim is read */
40 static int eqn_boxuntil(struct box *box, int sz0, char *fn0, char *delim)
42 struct box *sub = NULL;
43 while (tok_get() && tok_jmp(delim)) {
44 if (!strcmp("}", tok_get()))
45 return 1;
46 sub = eqn_box(box->style, sub ? box : NULL, sz0, fn0);
47 box_merge(box, sub, 0);
48 box_free(sub);
50 return 0;
53 /* subscript size */
54 static void sizesub(int dst, int src, int style, int src_style)
56 if (TS_SZ(style) > TS_SZ(src_style)) {
57 printf(".nr %s %s*7/10\n", nregname(dst), nreg(src));
58 printf(".if %s<%d .nr %s %d\n",
59 nreg(dst), e_minimumsize,
60 nregname(dst), e_minimumsize);
61 } else {
62 printf(".nr %s %s\n", nregname(dst), nreg(src));
66 static char *tok_quotes(char *s)
68 if (s && s[0] == '"') {
69 s[strlen(s) - 1] = '\0';
70 return s + 1;
72 return s ? s : "";
75 static char *tok_improve(char *s)
77 if (s && s[0] == '-' && s[1] == '\0')
78 return "\\(mi";
79 if (s && s[0] == '+' && s[1] == '\0')
80 return "\\(pl";
81 if (s && s[0] == '\'' && s[1] == '\0')
82 return "\\(fm";
83 return tok_quotes(s);
86 static void eqn_bracketsizes(void)
88 char sign[BRLEN];
89 char bufs[NSIZES][BRLEN];
90 char *sizes[NSIZES] = {NULL};
91 int n, i;
92 snprintf(sign, sizeof(sign), "%s", tok_quotes(tok_poptext(1)));
93 n = atoi(tok_poptext(1));
94 for (i = 0; i < n; i++) {
95 char *size = tok_quotes(tok_poptext(1));
96 if (i < NSIZES) {
97 snprintf(bufs[i], sizeof(bufs[i]), "%s", size);
98 sizes[i] = bufs[i];
101 def_sizesput(sign, sizes);
104 static void eqn_bracketpieces(void)
106 char sign[BRLEN], top[BRLEN], mid[BRLEN], bot[BRLEN], cen[BRLEN];
107 snprintf(sign, sizeof(sign), "%s", tok_quotes(tok_poptext(1)));
108 snprintf(top, sizeof(top), "%s", tok_quotes(tok_poptext(1)));
109 snprintf(mid, sizeof(mid), "%s", tok_quotes(tok_poptext(1)));
110 snprintf(bot, sizeof(bot), "%s", tok_quotes(tok_poptext(1)));
111 snprintf(cen, sizeof(cen), "%s", tok_quotes(tok_poptext(1)));
112 def_piecesput(sign, top, mid, bot, cen);
115 static int typenum(char *s)
117 if (!strcmp("ord", s) || !strcmp("ordinary", s))
118 return T_ORD;
119 if (!strcmp("op", s) || !strcmp("operator", s))
120 return T_BIGOP;
121 if (!strcmp("bin", s) || !strcmp("binary", s))
122 return T_BINOP;
123 if (!strcmp("rel", s) || !strcmp("relation", s))
124 return T_RELOP;
125 if (!strcmp("open", s) || !strcmp("opening", s))
126 return T_LEFT;
127 if (!strcmp("close", s) || !strcmp("closing", s))
128 return T_RIGHT;
129 if (!strcmp("punct", s) || !strcmp("punctuation", s))
130 return T_PUNC;
131 if (!strcmp("inner", s))
132 return T_INNER;
133 return T_ORD;
136 /* read chartype command arguments and perform it */
137 static void eqn_chartype(void)
139 char gl[GNLEN], type[NMLEN];
140 snprintf(type, sizeof(type), "%s", tok_quotes(tok_poptext(1)));
141 snprintf(gl, sizeof(gl), "%s", tok_quotes(tok_poptext(1)));
142 if (typenum(type) >= 0)
143 def_typeput(gl, typenum(type));
146 /* read breakcost command arguments and perform it */
147 static void eqn_breakcost(void)
149 char tok[NMLEN];
150 int cost, type;
151 snprintf(tok, sizeof(tok), "%s", tok_quotes(tok_poptext(1)));
152 cost = atoi(tok_poptext(1));
153 type = !strcmp("any", tok) ? 0 : typenum(tok);
154 if (type >= 0)
155 def_brcostput(type, cost);
158 /* read general eqn commands */
159 static int eqn_commands(void)
161 char var[LNLEN];
162 char *sz;
163 if (!tok_jmp("delim")) {
164 tok_delim();
165 return 0;
167 if (!tok_jmp("define")) {
168 tok_macro();
169 return 0;
171 if (!tok_jmp("gfont")) {
172 strcpy(gfont, tok_quotes(tok_poptext(1)));
173 return 0;
175 if (!tok_jmp("grfont")) {
176 strcpy(grfont, tok_quotes(tok_poptext(1)));
177 return 0;
179 if (!tok_jmp("gbfont")) {
180 strcpy(gbfont, tok_quotes(tok_poptext(1)));
181 return 0;
183 if (!tok_jmp("gsize")) {
184 sz = tok_quotes(tok_poptext(1));
185 if (sz[0] == '-' || sz[0] == '+')
186 sprintf(gsize, "\\n%s%s", escarg(EQNSZ), sz);
187 else
188 strcpy(gsize, sz);
189 return 0;
191 if (!tok_jmp("set")) {
192 strcpy(var, tok_poptext(1));
193 def_set(var, atoi(tok_poptext(1)));
194 return 0;
196 if (!tok_jmp("bracketsizes")) {
197 eqn_bracketsizes();
198 return 0;
200 if (!tok_jmp("bracketpieces")) {
201 eqn_bracketpieces();
202 return 0;
204 if (!tok_jmp("chartype")) {
205 eqn_chartype();
206 return 0;
208 if (!tok_jmp("breakcost")) {
209 eqn_breakcost();
210 return 0;
212 return 1;
215 /* read user-specified spaces */
216 static int eqn_gaps(struct box *box, int szreg)
218 if (!tok_jmp("~")) {
219 box_puttext(box, T_GAP, "\\h'%du*%sp/100u'",
220 S_S3, nreg(szreg));
221 return 0;
223 if (!tok_jmp("^")) {
224 box_puttext(box, T_GAP, "\\h'%du*%sp/100u'",
225 S_S1, nreg(szreg));
226 return 0;
228 if (!tok_jmp("\t")) {
229 box_puttext(box, T_GAP, "\t");
230 return 0;
232 return 1;
235 /* return the font of the given token type */
236 static char *tok_font(int tok, char *fn)
238 if (fn && fn[0])
239 return fn;
240 if (tok == T_LETTER || tok == T_STRING)
241 return gfont;
242 return grfont;
245 /* check the next token */
246 static void tok_expect(char *s)
248 if (tok_jmp(s)) {
249 fprintf(stderr, "neateqn: expected %s bot got %s\n",
250 s, tok_get());
251 exit(1);
255 /* read pile command */
256 static void eqn_pile(struct box *box, int sz0, char *fn0, int adj)
258 struct box *pile[NPILES] = {NULL};
259 int i;
260 int n = 0;
261 int rowspace = 0;
262 if (tok_jmp("{")) {
263 rowspace = atoi(tok_poptext(1));
264 tok_expect("{");
266 do {
267 if (!tok_get())
268 errdie("neateqn: unterminated pile\n");
269 pile[n++] = box_alloc(sz0, 0, box->style);
270 } while (!eqn_boxuntil(pile[n - 1], sz0, fn0, "above"));
271 tok_expect("}");
272 box_pile(box, pile, adj, rowspace);
273 for (i = 0; i < n; i++)
274 box_free(pile[i]);
277 /* read matrix command */
278 static void eqn_matrix(struct box *box, int sz0, char *fn0)
280 struct box *cols[NPILES][NPILES] = {{NULL}};
281 int adj[NPILES];
282 int nrows;
283 int ncols = 0;
284 int colspace = 0;
285 int rowspace = 0;
286 int i, j;
287 if (tok_jmp("{")) {
288 colspace = atoi(tok_poptext(1));
289 tok_expect("{");
291 while (1) {
292 if (!tok_jmp("col") || !tok_jmp("ccol"))
293 adj[ncols] = 'c';
294 else if (!tok_jmp("lcol"))
295 adj[ncols] = 'l';
296 else if (!tok_jmp("rcol"))
297 adj[ncols] = 'r';
298 else
299 break;
300 nrows = 0;
301 if (tok_jmp("{")) {
302 i = atoi(tok_poptext(1));
303 if (i > rowspace)
304 rowspace = i;
305 tok_expect("{");
307 do {
308 if (!tok_get())
309 errdie("neateqn: unterminated matrix col\n");
310 cols[ncols][nrows++] = box_alloc(sz0, 0, box->style);
311 } while (!eqn_boxuntil(cols[ncols][nrows - 1],
312 sz0, fn0, "above"));
313 tok_expect("}");
314 ncols++;
316 tok_expect("}");
317 box_matrix(box, ncols, cols, adj, colspace, rowspace);
318 for (i = 0; i < ncols; i++)
319 for (j = 0; j < NPILES; j++)
320 if (cols[i][j])
321 box_free(cols[i][j]);
324 /* return nonzero if fn is italic */
325 static int italic(char *fn)
327 return (!strcmp("I", fn) || !strcmp("2", fn) ||
328 gfont == fn || !strcmp(gfont, fn)) ? T_ITALIC : 0;
331 /* read a box without fractions */
332 static struct box *eqn_left(int flg, struct box *pre, int sz0, char *fn0)
334 struct box *box = NULL;
335 struct box *sub_sub = NULL, *sub_sup = NULL;
336 struct box *sub_from = NULL, *sub_to = NULL;
337 struct box *sqrt, *inner;
338 char left[NMLEN] = "", right[NMLEN] = "";
339 char fn[FNLEN] = "";
340 int sz = sz0;
341 int subsz;
342 int dx = 0, dy = 0;
343 int style = EQN_TSMASK & flg;
344 if (fn0)
345 strcpy(fn, fn0);
346 while (!eqn_commands())
348 box = box_alloc(sz, pre ? pre->tcur : 0, style);
349 if (!eqn_gaps(box, sz)) {
350 while (!eqn_gaps(box, sz))
352 return box;
354 while (1) {
355 if (!tok_jmp("fat")) {
356 } else if (!tok_jmp("roman")) {
357 strcpy(fn, grfont);
358 } else if (!tok_jmp("italic")) {
359 strcpy(fn, gfont);
360 } else if (!tok_jmp("bold")) {
361 strcpy(fn, gbfont);
362 } else if (!tok_jmp("font")) {
363 strcpy(fn, tok_poptext(1));
364 } else if (!tok_jmp("size")) {
365 sz = box_size(box, tok_poptext(1));
366 } else if (!tok_jmp("fwd")) {
367 dx += atoi(tok_poptext(1));
368 } else if (!tok_jmp("back")) {
369 dx -= atoi(tok_poptext(1));
370 } else if (!tok_jmp("down")) {
371 dy += atoi(tok_poptext(1));
372 } else if (!tok_jmp("up")) {
373 dy -= atoi(tok_poptext(1));
374 } else {
375 break;
378 if (!tok_jmp("sqrt")) {
379 sqrt = eqn_left(TS_MK0(style), NULL, sz, fn);
380 printf(".ft %s\n", grfont);
381 box_sqrt(box, sqrt);
382 box_free(sqrt);
383 } else if (!tok_jmp("pile") || !tok_jmp("cpile")) {
384 eqn_pile(box, sz, fn, 'c');
385 } else if (!tok_jmp("lpile")) {
386 eqn_pile(box, sz, fn, 'l');
387 } else if (!tok_jmp("rpile")) {
388 eqn_pile(box, sz, fn, 'r');
389 } else if (!tok_jmp("matrix")) {
390 eqn_matrix(box, sz, fn);
391 } else if (!tok_jmp("vcenter")) {
392 inner = eqn_left(flg, pre, sz, fn);
393 box_vcenter(box, inner);
394 box_free(inner);
395 } else if (!tok_jmp("{")) {
396 eqn_boxuntil(box, sz, fn, "}");
397 } else if (!tok_jmp("left")) {
398 inner = box_alloc(sz, 0, style);
399 snprintf(left, sizeof(left), "%s", tok_quotes(tok_poptext(0)));
400 eqn_boxuntil(inner, sz, fn, "right");
401 snprintf(right, sizeof(right), "%s", tok_quotes(tok_poptext(0)));
402 printf(".ft %s\n", grfont);
403 box_wrap(box, inner, left[0] ? left : NULL,
404 right[0] ? right : NULL);
405 box_free(inner);
406 } else if (tok_get() && tok_type() != T_KEYWORD) {
407 if (dx || dy)
408 box_move(box, dy, dx);
409 box_putf(box, "\\s%s", escarg(nreg(sz)));
410 do {
411 char *cfn = tok_font(tok_type(), fn);
412 int chops;
413 box_puttext(box, tok_type() | italic(cfn), "\\f%s%s",
414 escarg(cfn), tok_improve(tok_get()));
415 chops = tok_chops(0);
416 tok_pop();
417 if (chops) /* what we read was a splitting */
418 break;
419 } while (!tok_chops(0)); /* the next token is splitting */
420 if (dx || dy)
421 box_move(box, -dy, -dx);
423 while (tok_get()) {
424 if (!tok_jmp("dyad")) {
425 printf(".ft %s\n", grfont);
426 box_accent(box, "\\(ab");
427 } else if (!tok_jmp("bar")) {
428 printf(".ft %s\n", grfont);
429 box_bar(box);
430 } else if (!tok_jmp("under")) {
431 printf(".ft %s\n", grfont);
432 box_under(box);
433 } else if (!tok_jmp("vec")) {
434 printf(".ft %s\n", grfont);
435 box_accent(box, "\\s[\\n(.s/2u]\\(->\\s0");
436 } else if (!tok_jmp("tilde")) {
437 printf(".ft %s\n", grfont);
438 box_accent(box, "\\s[\\n(.s*3u/4u]\\(ap\\s0");
439 } else if (!tok_jmp("hat")) {
440 printf(".ft %s\n", grfont);
441 box_accent(box, "ˆ");
442 } else if (!tok_jmp("dot")) {
443 printf(".ft %s\n", grfont);
444 box_accent(box, ".");
445 } else if (!tok_jmp("dotdot")) {
446 printf(".ft %s\n", grfont);
447 box_accent(box, "..");
448 } else {
449 break;
452 subsz = nregmk();
453 if (!tok_jmp("sub")) {
454 sizesub(subsz, sz0, ts_sup(style), style);
455 sub_sub = eqn_left(ts_sup(style) | EQN_SUB, NULL, subsz, fn0);
457 if ((sub_sub || !(flg & EQN_SUB)) && !tok_jmp("sup")) {
458 sizesub(subsz, sz0, ts_sub(style), style);
459 sub_sup = eqn_left(ts_sub(style), NULL, subsz, fn0);
461 if (sub_sub || sub_sup)
462 box_sub(box, sub_sub, sub_sup);
463 if (!tok_jmp("from")) {
464 sizesub(subsz, sz0, ts_sub(style), style);
465 sub_from = eqn_left(ts_sub(style) | EQN_FROM, NULL, subsz, fn0);
467 if ((sub_from || !(flg & EQN_FROM)) && !tok_jmp("to")) {
468 sizesub(subsz, sz0, ts_sup(style), style);
469 sub_to = eqn_left(ts_sup(style), NULL, subsz, fn0);
471 if (sub_from || sub_to) {
472 inner = box_alloc(sz0, 0, style);
473 box_from(inner, box, sub_from, sub_to);
474 box_free(box);
475 box = inner;
477 nregrm(subsz);
478 if (sub_sub)
479 box_free(sub_sub);
480 if (sub_sup)
481 box_free(sub_sup);
482 if (sub_from)
483 box_free(sub_from);
484 if (sub_to)
485 box_free(sub_to);
486 return box;
489 /* read a box */
490 static struct box *eqn_box(int flg, struct box *pre, int sz0, char *fn0)
492 struct box *box;
493 struct box *sub_num = NULL, *sub_den = NULL;
494 int style = flg & EQN_TSMASK;
495 box = eqn_left(flg, pre, sz0, fn0);
496 while (!tok_jmp("over")) {
497 sub_num = box;
498 sub_den = eqn_left(TS_MK0(style), NULL, sz0, fn0);
499 box = box_alloc(sz0, pre ? pre->tcur : 0, style);
500 printf(".ft %s\n", grfont);
501 box_over(box, sub_num, sub_den);
502 box_free(sub_num);
503 box_free(sub_den);
505 return box;
508 /* read an equation, either inline or block */
509 static struct box *eqn_read(int style)
511 struct box *box, *sub;
512 int szreg = nregmk();
513 printf(".nr %s %s\n", nregname(szreg), gsize);
514 box = box_alloc(szreg, 0, style);
515 while (tok_get()) {
516 if (!tok_jmp("mark")) {
517 eqn_mk = !eqn_mk ? 1 : eqn_mk;
518 box_markpos(box, EQNMK);
519 continue;
521 if (!tok_jmp("lineup")) {
522 eqn_mk = 2;
523 box_markpos(box, nregname(eqn_lineupreg));
524 sprintf(eqn_lineup, "\\h'\\n%su-%su'",
525 escarg(EQNMK), nreg(eqn_lineupreg));
526 continue;
528 sub = eqn_box(style, box, szreg, NULL);
529 box_merge(box, sub, 1);
530 box_free(sub);
532 box_vertspace(box);
533 nregrm(szreg);
534 return box;
537 void errdie(char *msg)
539 fprintf(stderr, "%s", msg);
540 exit(1);
543 int main(int argc, char **argv)
545 struct box *box;
546 char eqnblk[128];
547 int i;
548 for (i = 1; i < argc; i++) {
549 if (argv[i][0] != '-' || !argv[i][1])
550 break;
551 if (argv[i][1] == 'c') {
552 def_choppedset(argv[i][2] ? argv[i] + 2 : argv[++i]);
553 } else {
554 fprintf(stderr, "Usage: neateqn [options] <input >output\n\n"
555 "Options:\n"
556 " -c chars \tcharacters that chop equations\n");
557 return 1;
560 for (i = 0; def_macros[i][0]; i++)
561 src_define(def_macros[i][0], def_macros[i][1]);
562 while (!tok_eqn()) {
563 reg_reset();
564 eqn_mk = 0;
565 tok_pop();
566 printf(".nr %s \\n(.s\n", EQNSZ);
567 printf(".nr %s \\n(.f\n", EQNFN);
568 eqn_lineupreg = nregmk();
569 box = eqn_read(tok_inline() ? TS_T : TS_D);
570 printf(".nr MK %d\n", eqn_mk);
571 if (!box_empty(box)) {
572 sprintf(eqnblk, "%s%s", eqn_lineup, box_toreg(box));
573 tok_eqnout(eqnblk);
574 printf(".ps \\n%s\n", escarg(EQNSZ));
575 printf(".ft \\n%s\n", escarg(EQNFN));
577 printf(".lf %d\n", src_lineget());
578 eqn_lineup[0] = '\0';
579 nregrm(eqn_lineupreg);
580 box_free(box);
582 src_done();
583 return 0;