tr: ignore .lf with no arguments
[neatroff.git] / tr.c
blob374fb1643770930dd42c270e93a003163f213626
1 #include <ctype.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include "roff.h"
7 static int tr_nl = 1;
8 static int c_pc = '%'; /* page number character */
9 int c_ec = '\\';
10 int c_cc = '.';
11 int c_c2 = '\'';
13 /* skip everything until the end of line */
14 static void jmp_eol(void)
16 int c;
17 do {
18 c = cp_next();
19 } while (c >= 0 && c != '\n');
22 static void tr_vs(char **args)
24 int vs = args[1] ? eval_re(args[1], n_v, 'p') : n_v0;
25 n_v0 = n_v;
26 n_v = MAX(0, vs);
29 static void tr_ls(char **args)
31 int ls = args[1] ? eval_re(args[1], n_L, 0) : n_L0;
32 n_L0 = n_L;
33 n_L = MAX(1, ls);
36 static void tr_pl(char **args)
38 int n = eval_re(args[1] ? args[1] : "11i", n_p, 'v');
39 n_p = MAX(0, n);
42 static void tr_nr(char **args)
44 int id;
45 if (!args[2])
46 return;
47 id = map(args[1]);
48 num_set(id, eval_re(args[2], num_get(id, 0), 'u'));
49 num_inc(id, args[3] ? eval(args[3], 'u') : 0);
52 static void tr_rr(char **args)
54 int i;
55 for (i = 1; i <= NARGS; i++)
56 if (args[i])
57 num_del(map(args[i]));
60 static void tr_af(char **args)
62 if (args[2])
63 num_setfmt(map(args[1]), args[2]);
66 static void tr_ds(char **args)
68 if (args[2])
69 str_set(map(args[1]), args[2]);
72 static void tr_as(char **args)
74 int reg;
75 char *s1, *s2, *s;
76 if (!args[2])
77 return;
78 reg = map(args[1]);
79 s1 = str_get(reg) ? str_get(reg) : "";
80 s2 = args[2];
81 s = malloc(strlen(s1) + strlen(s2) + 1);
82 strcpy(s, s1);
83 strcat(s, s2);
84 str_set(reg, s);
85 free(s);
88 static void tr_rm(char **args)
90 int i;
91 for (i = 1; i <= NARGS; i++)
92 if (args[i])
93 str_rm(map(args[i]));
96 static void tr_rn(char **args)
98 if (!args[2])
99 return;
100 str_rn(map(args[1]), map(args[2]));
103 static void tr_po(char **args)
105 int po = args[1] ? eval_re(args[1], n_o, 'm') : n_o0;
106 n_o0 = n_o;
107 n_o = MAX(0, po);
110 static char *arg_regname(char *s, int len);
112 static void macrobody(struct sbuf *sbuf, char *end)
114 char buf[NMLEN];
115 int i, c;
116 int first = 1;
117 cp_back('\n');
118 cp_wid(0); /* copy-mode; disable \w handling */
119 while ((c = cp_next()) >= 0) {
120 if (sbuf && !first)
121 sbuf_add(sbuf, c);
122 first = 0;
123 if (c == '\n') {
124 c = cp_next();
125 if (c == '.') {
126 arg_regname(buf, sizeof(buf));
127 if ((n_cp && end[0] == buf[0] && end[1] == buf[1]) ||
128 !strcmp(end, buf)) {
129 jmp_eol();
130 break;
132 if (!sbuf)
133 continue;
134 sbuf_add(sbuf, '.');
135 for (i = 0; buf[i]; i++)
136 sbuf_add(sbuf, (unsigned char) buf[i]);
137 continue;
139 if (sbuf && c >= 0)
140 sbuf_add(sbuf, c);
143 cp_wid(1);
146 static void tr_de(char **args)
148 struct sbuf sbuf;
149 int id;
150 if (!args[1])
151 return;
152 id = map(args[1]);
153 sbuf_init(&sbuf);
154 if (args[0][1] == 'a' && args[0][2] == 'm' && str_get(id))
155 sbuf_append(&sbuf, str_get(id));
156 macrobody(&sbuf, args[2] ? args[2] : ".");
157 str_set(id, sbuf_buf(&sbuf));
158 sbuf_done(&sbuf);
161 static void tr_ig(char **args)
163 macrobody(NULL, args[1] ? args[1] : ".");
166 void schar_read(char *d, int (*next)(void))
168 d[0] = next();
169 d[1] = '\0';
170 if (d[0] == c_ni) {
171 d[1] = next();
172 d[2] = '\0';
174 if (d[0] == c_ec) {
175 d[1] = next();
176 d[2] = '\0';
177 if (d[1] == '(') {
178 d[2] = next();
179 d[3] = next();
180 d[4] = '\0';
185 int schar_jump(char *d, int (*next)(void), void (*back)(int))
187 int c, i;
188 for (i = 0; d[i]; i++)
189 if ((c = next()) != d[i])
190 break;
191 if (d[i]) {
192 back(c);
193 while (i > 0)
194 back(d[--i]);
195 return 1;
197 return 0;
200 /* read into sbuf until stop; if stop is NULL, stop at whitespace */
201 static int read_until(struct sbuf *sbuf, char *stop)
203 int c;
204 while ((c = cp_next()) >= 0) {
205 cp_back(c);
206 if (c == '\n')
207 return 1;
208 if (!stop && (c == ' ' || c == '\t'))
209 return 0;
210 if (stop && !schar_jump(stop, cp_next, cp_back))
211 return 0;
212 sbuf_add(sbuf, cp_next());
214 return 1;
217 /* evaluate .if strcmp (i.e. 'str'str') */
218 static int if_strcmp(void)
220 char delim[GNLEN];
221 struct sbuf s1, s2;
222 int ret;
223 schar_read(delim, cp_next);
224 sbuf_init(&s1);
225 sbuf_init(&s2);
226 read_until(&s1, delim);
227 read_until(&s2, delim);
228 ret = !strcmp(sbuf_buf(&s1), sbuf_buf(&s2));
229 sbuf_done(&s1);
230 sbuf_done(&s2);
231 return ret;
234 /* evaluate .if condition letters */
235 static int if_cond(void)
237 switch (cp_next()) {
238 case 'o':
239 return n_pg % 2;
240 case 'e':
241 return !(n_pg % 2);
242 case 't':
243 return 1;
244 case 'n':
245 return 0;
247 return 0;
250 /* evaluate .if condition */
251 static int if_eval(void)
253 struct sbuf sbuf;
254 int ret;
255 sbuf_init(&sbuf);
256 if (!read_until(&sbuf, NULL))
257 cp_back(' ');
258 ret = eval(sbuf_buf(&sbuf), '\0') > 0;
259 sbuf_done(&sbuf);
260 return ret;
263 static int ie_cond[NIES]; /* .ie condition stack */
264 static int ie_depth;
266 static void tr_if(char **args)
268 int neg = 0;
269 int ret;
270 int c;
271 do {
272 c = cp_next();
273 } while (c == ' ' || c == '\t');
274 if (c == '!') {
275 neg = 1;
276 c = cp_next();
278 cp_back(c);
279 if (strchr("oetn", c)) {
280 ret = if_cond();
281 } else if (!isdigit(c) && !strchr("-+*/%<=>&:.|()", c)) {
282 ret = if_strcmp();
283 } else {
284 ret = if_eval();
286 if (args[0][1] == 'i' && args[0][2] == 'e') /* .ie command */
287 if (ie_depth < NIES)
288 ie_cond[ie_depth++] = ret != neg;
289 cp_blk(ret == neg);
292 static void tr_el(char **args)
294 cp_blk(ie_depth > 0 ? ie_cond[--ie_depth] : 1);
297 static void tr_na(char **args)
299 n_na = 1;
302 static void tr_ad(char **args)
304 n_na = 0;
305 if (!args[1])
306 return;
307 switch (args[1][0]) {
308 case '0' + AD_L:
309 case 'l':
310 n_j = AD_L;
311 break;
312 case '0' + AD_R:
313 case 'r':
314 n_j = AD_R;
315 break;
316 case '0' + AD_C:
317 case 'c':
318 n_j = AD_C;
319 break;
320 case '0' + AD_B:
321 case 'b':
322 case 'n':
323 n_j = AD_B;
324 break;
328 static void tr_tm(char **args)
330 fprintf(stderr, "%s\n", args[1]);
333 static void tr_so(char **args)
335 if (args[1])
336 in_so(args[1]);
339 static void tr_nx(char **args)
341 in_nx(args[1]);
344 static void tr_ex(char **args)
346 in_ex();
349 static void tr_sy(char **args)
351 system(args[1]);
354 static void tr_lt(char **args)
356 int lt = args[1] ? eval_re(args[1], n_lt, 'm') : n_t0;
357 n_t0 = n_t0;
358 n_lt = MAX(0, lt);
361 static void tr_pc(char **args)
363 c_pc = args[1] ? args[1][0] : -1;
366 static int tl_next(void)
368 int c = cp_next();
369 if (c >= 0 && c == c_pc) {
370 in_push(num_str(REG('%', '\0')), NULL);
371 c = cp_next();
373 return c;
376 static void tr_tl(char **args)
378 int c;
379 do {
380 c = cp_next();
381 } while (c >= 0 && (c == ' ' || c == '\t'));
382 cp_back(c);
383 ren_tl(tl_next, cp_back);
384 do {
385 c = cp_next();
386 } while (c >= 0 && c != '\n');
389 static void tr_ec(char **args)
391 c_ec = args[1] ? args[1][0] : '\\';
394 static void tr_cc(char **args)
396 c_ec = args[1] ? args[1][0] : '.';
399 static void tr_c2(char **args)
401 c_ec = args[1] ? args[1][0] : '\'';
404 static void tr_eo(char **args)
406 c_ec = -1;
409 static void tr_hc(char **args)
411 strcpy(c_hc, args[1] ? args[1] : "\\%");
414 static void tr_nh(char **args)
416 n_hy = 0;
419 static void tr_hy(char **args)
421 n_hy = args[1] ? atoi(args[1]) : 1;
424 static void tr_lg(char **args)
426 if (args[1])
427 n_lg = atoi(args[1]);
430 static void tr_kn(char **args)
432 if (args[1])
433 n_kn = atoi(args[1]);
436 static void tr_cp(char **args)
438 if (args[1])
439 n_cp = atoi(args[1]);
442 static void tr_ss(char **args)
444 if (args[1])
445 n_ss = eval_re(args[1], n_ss, 0);
448 static void tr_cs(char **args)
450 if (!args[1])
451 return;
452 dev_setcs(dev_pos(args[1]), args[2] ? eval(args[2], 0) : 0);
455 static void tr_nm(char **args)
457 if (!args[1]) {
458 n_nm = 0;
459 return;
461 n_nm = 1;
462 n_ln = eval_re(args[1], n_ln, 0);
463 n_ln = MAX(0, n_ln);
464 if (args[2] && isdigit(args[2][0]))
465 n_nM = MAX(1, eval(args[2], 0));
466 if (args[3] && isdigit(args[3][0]))
467 n_nS = MAX(0, eval(args[3], 0));
468 if (args[4] && isdigit(args[4][0]))
469 n_nI = MAX(0, eval(args[4], 0));
472 static void tr_nn(char **args)
474 n_nn = args[1] ? eval(args[1], 0) : 1;
477 static void tr_bd(char **args)
479 if (!args[1] || !strcmp("S", args[1]))
480 return;
481 dev_setbd(dev_pos(args[1]), args[2] ? eval(args[2], 'u') : 0);
484 static void tr_it(char **args)
486 if (args[2]) {
487 n_it = map(args[2]);
488 n_itn = eval(args[1], 0);
489 } else {
490 n_it = 0;
494 static void tr_mc(char **args)
496 if (args[1]) {
497 n_mc = 1;
498 strcpy(c_mc, args[1]);
499 n_mcn = args[2] ? eval(args[2], 'm') : SC_EM;
500 } else {
501 n_mc = 0;
505 static void tr_lf(char **args)
507 if (args[1])
508 in_lf(args[2], eval(args[1], 0));
511 static char *arg_regname(char *s, int len)
513 char *e = n_cp ? s + 2 : s + len;
514 int c = cp_next();
515 while (c == ' ' || c == '\t')
516 c = cp_next();
517 while (s < e && c >= 0 && c != ' ' && c != '\t' && c != '\n') {
518 *s++ = c;
519 c = cp_next();
521 if (c >= 0)
522 cp_back(c);
523 *s++ = '\0';
524 return s;
527 static char *arg_normal(char *s, int len)
529 char *e = s + len - 1;
530 int quoted = 0;
531 int c;
532 c = cp_next();
533 while (c == ' ')
534 c = cp_next();
535 if (c == '"') {
536 quoted = 1;
537 c = cp_next();
539 while (s < e && c > 0 && c != '\n') {
540 if (!quoted && c == ' ')
541 break;
542 if (quoted && c == '"') {
543 c = cp_next();
544 if (c != '"')
545 break;
547 *s++ = c;
548 c = cp_next();
550 if (c >= 0)
551 cp_back(c);
552 *s++ = '\0';
553 return s;
556 static char *arg_string(char *s, int len)
558 char *e = s + len - 1;
559 int c;
560 while ((c = cp_next()) == ' ')
562 if (c == '"')
563 c = cp_next();
564 while (s < e && c > 0 && c != '\n') {
565 *s++ = c;
566 c = cp_next();
568 *s++ = '\0';
569 if (c >= 0)
570 cp_back(c);
571 return s;
574 /* read macro arguments; trims tabs if rmtabs is nonzero */
575 static int mkargs(char **args, char *buf, int len)
577 char *s = buf;
578 char *e = buf + len - 1;
579 int c;
580 int n = 0;
581 while (n < NARGS) {
582 char *r = s;
583 c = cp_next();
584 if (c < 0 || c == '\n')
585 return n;
586 cp_back(c);
587 s = arg_normal(s, e - s);
588 if (*r != '\0')
589 args[n++] = r;
591 jmp_eol();
592 return n;
595 /* read request arguments; trims tabs too */
596 static int mkargs_req(char **args, char *buf, int len)
598 char *r, *s = buf;
599 char *e = buf + len - 1;
600 int c;
601 int n = 0;
602 c = cp_next();
603 while (n < NARGS && s < e) {
604 r = s;
605 while (c == ' ' || c == '\t')
606 c = cp_next();
607 while (c >= 0 && c != '\n' && c != ' ' && c != '\t' && s < e) {
608 *s++ = c;
609 c = cp_next();
611 *s++ = '\0';
612 if (*r != '\0')
613 args[n++] = r;
614 if (c < 0 || c == '\n')
615 return n;
617 jmp_eol();
618 return n;
621 /* read arguments for .ds */
622 static int mkargs_ds(char **args, char *buf, int len)
624 char *s = buf;
625 char *e = buf + len - 1;
626 int c;
627 args[0] = s;
628 s = arg_regname(s, e - s);
629 args[1] = s;
630 cp_wid(0);
631 s = arg_string(s, e - s);
632 cp_wid(1);
633 c = cp_next();
634 if (c >= 0 && c != '\n')
635 jmp_eol();
636 return 2;
639 /* read arguments for commands .nr that expect a register name */
640 static int mkargs_reg1(char **args, char *buf, int len)
642 char *s = buf;
643 char *e = buf + len - 1;
644 args[0] = s;
645 s = arg_regname(s, e - s);
646 return mkargs_req(args + 1, s, e - s) + 1;
649 /* do not read arguments; for .if, .ie and .el */
650 static int mkargs_null(char **args, char *buf, int len)
652 return 0;
655 /* read the whole line for .tm */
656 static int mkargs_eol(char **args, char *buf, int len)
658 char *s = buf;
659 char *e = buf + len - 1;
660 int c;
661 args[0] = s;
662 c = cp_next();
663 while (c == ' ')
664 c = cp_next();
665 while (s < e && c >= 0 && c != '\n') {
666 *s++ = c;
667 c = cp_next();
669 *s = '\0';
670 return 1;
673 static struct cmd {
674 char *id;
675 void (*f)(char **args);
676 int (*args)(char **args, char *buf, int len);
677 } cmds[] = {
678 {TR_DIVBEG, tr_divbeg},
679 {TR_DIVEND, tr_divend},
680 {TR_EJECT, tr_eject},
681 {"ab", tr_ab, mkargs_eol},
682 {"ad", tr_ad},
683 {"af", tr_af},
684 {"am", tr_de, mkargs_reg1},
685 {"as", tr_as, mkargs_ds},
686 {"bd", tr_bd},
687 {"bp", tr_bp},
688 {"br", tr_br},
689 {"c2", tr_c2},
690 {"cc", tr_cc},
691 {"ce", tr_ce},
692 {"ch", tr_ch},
693 {"cp", tr_cp},
694 {"cs", tr_cs},
695 {"da", tr_di},
696 {"de", tr_de, mkargs_reg1},
697 {"di", tr_di},
698 {"ds", tr_ds, mkargs_ds},
699 {"dt", tr_dt},
700 {"ec", tr_ec},
701 {"el", tr_el, mkargs_null},
702 {"em", tr_em},
703 {"eo", tr_eo},
704 {"ev", tr_ev},
705 {"ex", tr_ex},
706 {"fc", tr_fc},
707 {"fi", tr_fi},
708 {"fp", tr_fp},
709 {"ft", tr_ft},
710 {"hc", tr_hc},
711 {"hy", tr_hy},
712 {"hw", tr_hw},
713 {"ie", tr_if, mkargs_null},
714 {"if", tr_if, mkargs_null},
715 {"ig", tr_ig},
716 {"in", tr_in},
717 {"it", tr_it},
718 {"kn", tr_kn},
719 {"lf", tr_lf},
720 {"lg", tr_lg},
721 {"ll", tr_ll},
722 {"ls", tr_ls},
723 {"lt", tr_lt},
724 {"mc", tr_mc},
725 {"mk", tr_mk},
726 {"na", tr_na},
727 {"ne", tr_ne},
728 {"nf", tr_nf},
729 {"nh", tr_nh},
730 {"nm", tr_nm},
731 {"nn", tr_nn},
732 {"nr", tr_nr, mkargs_reg1},
733 {"ns", tr_ns},
734 {"nx", tr_nx},
735 {"os", tr_os},
736 {"pc", tr_pc},
737 {"pl", tr_pl},
738 {"pn", tr_pn},
739 {"po", tr_po},
740 {"ps", tr_ps},
741 {"rm", tr_rm},
742 {"rn", tr_rn},
743 {"rr", tr_rr},
744 {"rs", tr_rs},
745 {"rt", tr_rt},
746 {"so", tr_so},
747 {"sp", tr_sp},
748 {"ss", tr_ss},
749 {"sv", tr_sv},
750 {"sy", tr_sy, mkargs_eol},
751 {"ta", tr_ta},
752 {"ti", tr_ti},
753 {"tl", tr_tl, mkargs_null},
754 {"tm", tr_tm, mkargs_eol},
755 {"vs", tr_vs},
756 {"wh", tr_wh},
759 int tr_next(void)
761 int c = cp_next();
762 int nl = c == '\n';
763 char *args[NARGS + 3] = {NULL};
764 char cmd[RNLEN];
765 char buf[LNLEN];
766 struct cmd *req;
767 while (tr_nl && c >= 0 && (c == c_cc || c == c_c2)) {
768 nl = 1;
769 memset(args, 0, sizeof(args));
770 args[0] = cmd;
771 cmd[0] = c;
772 req = NULL;
773 arg_regname(cmd + 1, sizeof(cmd) - 1);
774 req = str_dget(map(cmd + 1));
775 if (req) {
776 if (req->args)
777 req->args(args + 1, buf, sizeof(buf));
778 else
779 mkargs_req(args + 1, buf, sizeof(buf));
780 req->f(args);
781 } else {
782 cp_wid(0);
783 mkargs(args + 1, buf, sizeof(buf));
784 cp_wid(1);
785 if (str_get(map(cmd + 1)))
786 in_push(str_get(map(cmd + 1)), args + 1);
788 c = cp_next();
789 nl = c == '\n';
791 tr_nl = c < 0 || nl;
792 return c;
795 void tr_init(void)
797 int i;
798 for (i = 0; i < LEN(cmds); i++)
799 str_dset(map(cmds[i].id), &cmds[i]);
802 void tr_first(void)
804 cp_back(tr_next());
805 tr_nl = 1;