tr: .dv request
[neatroff.git] / tr.c
blobd9e0539684ee61b2bae83d4fa21e7fbd20ae1f66
1 /* built-in troff requests */
2 #include <ctype.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include "roff.h"
8 static int tr_nl = 1; /* just read a newline */
9 static int tr_bm = -1; /* blank line macro */
10 static int tr_sm = -1; /* leading space macro */
11 char c_pc[GNLEN] = "%"; /* page number character */
12 int c_ec = '\\'; /* escape character */
13 int c_cc = '.'; /* control character */
14 int c_c2 = '\''; /* no-break control character */
16 /* skip everything until the end of line */
17 static void jmp_eol(void)
19 int c;
20 do {
21 c = cp_next();
22 } while (c >= 0 && c != '\n');
25 static void tr_vs(char **args)
27 int vs = args[1] ? eval_re(args[1], n_v, 'p') : n_v0;
28 n_v0 = n_v;
29 n_v = MAX(0, vs);
32 static void tr_ls(char **args)
34 int ls = args[1] ? eval_re(args[1], n_L, 0) : n_L0;
35 n_L0 = n_L;
36 n_L = MAX(1, ls);
39 static void tr_pl(char **args)
41 int n = eval_re(args[1] ? args[1] : "11i", n_p, 'v');
42 n_p = MAX(0, n);
45 static void tr_nr(char **args)
47 int id;
48 if (!args[2])
49 return;
50 id = map(args[1]);
51 num_set(id, eval_re(args[2], num_get(id), 'u'));
52 num_setinc(id, args[3] ? eval(args[3], 'u') : 0);
55 static void tr_rr(char **args)
57 int i;
58 for (i = 1; i < NARGS; i++)
59 if (args[i])
60 num_del(map(args[i]));
63 static void tr_af(char **args)
65 if (args[2])
66 num_setfmt(map(args[1]), args[2]);
69 static void tr_ds(char **args)
71 str_set(map(args[1]), args[2] ? args[2] : "");
74 static void tr_as(char **args)
76 int reg;
77 char *s1, *s2, *s;
78 reg = map(args[1]);
79 s1 = str_get(reg) ? str_get(reg) : "";
80 s2 = args[2] ? args[2] : "";
81 s = xmalloc(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 /* read a string argument of a macro */
111 static char *read_string(void)
113 struct sbuf sbuf;
114 int c;
115 int empty;
116 sbuf_init(&sbuf);
117 cp_copymode(1);
118 while ((c = cp_next()) == ' ')
120 empty = c <= 0 || c == '\n';
121 if (c == '"')
122 c = cp_next();
123 while (c > 0 && c != '\n') {
124 if (c != c_ni)
125 sbuf_add(&sbuf, c);
126 c = cp_next();
128 if (c >= 0)
129 cp_back(c);
130 cp_copymode(0);
131 if (empty) {
132 sbuf_done(&sbuf);
133 return NULL;
135 return sbuf_out(&sbuf);
138 /* read a space separated macro argument; if two, read at most two characters */
139 static char *read_name(int two)
141 struct sbuf sbuf;
142 int c = cp_next();
143 int i = 0;
144 sbuf_init(&sbuf);
145 while (c == ' ' || c == '\t' || c == c_ni)
146 c = cp_next();
147 while (c > 0 && c != ' ' && c != '\t' && c != '\n' && (!two || i < 2)) {
148 if (c != c_ni) {
149 sbuf_add(&sbuf, c);
150 i++;
152 c = cp_next();
154 if (c >= 0)
155 cp_back(c);
156 return sbuf_out(&sbuf);
159 static void macrobody(struct sbuf *sbuf, char *end)
161 int first = 1;
162 int c;
163 char *req = NULL;
164 cp_back('\n');
165 cp_copymode(1);
166 while ((c = cp_next()) >= 0) {
167 if (sbuf && !first)
168 sbuf_add(sbuf, c);
169 first = 0;
170 if (c == '\n') {
171 if ((c = cp_next()) != c_cc) {
172 cp_back(c);
173 continue;
175 req = read_name(n_cp);
176 if (!strcmp(end, req)) {
177 in_push(end, NULL);
178 cp_back(c_cc);
179 break;
181 if (sbuf) {
182 sbuf_add(sbuf, c_cc);
183 sbuf_append(sbuf, req);
185 free(req);
186 req = NULL;
189 free(req);
190 cp_copymode(0);
193 static void tr_de(char **args)
195 struct sbuf sbuf;
196 int id;
197 if (!args[1])
198 return;
199 id = map(args[1]);
200 sbuf_init(&sbuf);
201 if (args[0][1] == 'a' && args[0][2] == 'm' && str_get(id))
202 sbuf_append(&sbuf, str_get(id));
203 macrobody(&sbuf, args[2] ? args[2] : ".");
204 str_set(id, sbuf_buf(&sbuf));
205 sbuf_done(&sbuf);
206 if (!n_cp && args[3]) /* parse the arguments as request argv[3] */
207 str_dset(id, str_dget(map(args[3])));
210 static void tr_ig(char **args)
212 macrobody(NULL, args[1] ? args[1] : ".");
215 /* read into sbuf until stop; if stop is NULL, stop at whitespace */
216 static int read_until(struct sbuf *sbuf, char *stop,
217 int (*next)(void), void (*back)(int))
219 char cs[GNLEN], cs2[GNLEN];
220 int c;
221 while ((c = next()) >= 0) {
222 if (c == c_ni)
223 continue;
224 back(c);
225 if (c == '\n')
226 return 1;
227 if (!stop && (c == ' ' || c == '\t'))
228 return 0;
229 charnext(cs, next, back);
230 if (stop && !strcmp(stop, cs))
231 return 0;
232 charnext_str(cs2, cs);
233 sbuf_append(sbuf, cs2);
235 return 1;
238 /* evaluate .if strcmp (i.e. 'str'str') */
239 static int if_strcmp(int (*next)(void), void (*back)(int))
241 char delim[GNLEN];
242 struct sbuf s1, s2;
243 int ret;
244 charnext(delim, next, back);
245 sbuf_init(&s1);
246 sbuf_init(&s2);
247 read_until(&s1, delim, next, back);
248 read_until(&s2, delim, next, back);
249 cp_reqbeg();
250 ret = !strcmp(sbuf_buf(&s1), sbuf_buf(&s2));
251 sbuf_done(&s1);
252 sbuf_done(&s2);
253 return ret;
256 /* evaluate .if condition letters */
257 static int if_cond(int (*next)(void), void (*back)(int))
259 switch (cp_next()) {
260 case 'o':
261 return n_pg % 2;
262 case 'e':
263 return !(n_pg % 2);
264 case 't':
265 return 1;
266 case 'n':
267 return 0;
269 return 0;
272 /* evaluate .if condition */
273 static int if_eval(int (*next)(void), void (*back)(int))
275 struct sbuf sbuf;
276 int ret;
277 sbuf_init(&sbuf);
278 read_until(&sbuf, NULL, next, back);
279 ret = eval(sbuf_buf(&sbuf), '\0') > 0;
280 sbuf_done(&sbuf);
281 return ret;
284 static int eval_if(int (*next)(void), void (*back)(int))
286 int neg = 0;
287 int ret;
288 int c;
289 do {
290 c = next();
291 } while (c == ' ' || c == '\t');
292 if (c == '!') {
293 neg = 1;
294 c = next();
296 back(c);
297 if (strchr("oetn", c)) {
298 ret = if_cond(next, back);
299 } else if (!isdigit(c) && !strchr("-+*/%<=>&:.|()", c)) {
300 ret = if_strcmp(next, back);
301 } else {
302 ret = if_eval(next, back);
304 return ret != neg;
307 static int ie_cond[NIES]; /* .ie condition stack */
308 static int ie_depth;
310 static void tr_if(char **args)
312 int c = eval_if(cp_next, cp_back);
313 if (args[0][1] == 'i' && args[0][2] == 'e') /* .ie command */
314 if (ie_depth < NIES)
315 ie_cond[ie_depth++] = c;
316 cp_blk(!c);
319 static void tr_el(char **args)
321 cp_blk(ie_depth > 0 ? ie_cond[--ie_depth] : 1);
324 static void tr_na(char **args)
326 n_na = 1;
329 static int adjmode(int c, int def)
331 switch (c) {
332 case 'l':
333 return AD_L;
334 case 'r':
335 return AD_R;
336 case 'c':
337 return AD_C;
338 case 'b':
339 case 'n':
340 return AD_B;
341 case 'k':
342 return AD_B | AD_K;
344 return def;
347 static void tr_ad(char **args)
349 char *s = args[1];
350 n_na = 0;
351 if (!s)
352 return;
353 if (isdigit((unsigned char) s[0]))
354 n_j = atoi(s) & 15;
355 else
356 n_j = s[0] == 'p' ? AD_P | adjmode(s[1], AD_B) : adjmode(s[0], n_j);
359 static void tr_tm(char **args)
361 fprintf(stderr, "%s\n", args[1] ? args[1] : "");
364 static void tr_so(char **args)
366 if (args[1])
367 in_so(args[1]);
370 static void tr_nx(char **args)
372 in_nx(args[1]);
375 static void tr_shift(char **args)
377 int n = args[1] ? atoi(args[1]) : 1;
378 while (n-- >= 1)
379 in_shift();
382 static void tr_ex(char **args)
384 in_ex();
387 static void tr_sy(char **args)
389 system(args[1]);
392 static void tr_lt(char **args)
394 int lt = args[1] ? eval_re(args[1], n_lt, 'm') : n_t0;
395 n_t0 = n_t0;
396 n_lt = MAX(0, lt);
399 static void tr_pc(char **args)
401 char *s = args[1];
402 if (!s || charread(&s, c_pc) < 0)
403 strcpy(c_pc, "");
406 static void tr_tl(char **args)
408 int c;
409 do {
410 c = cp_next();
411 } while (c >= 0 && (c == ' ' || c == '\t'));
412 cp_back(c);
413 ren_tl(cp_next, cp_back);
414 do {
415 c = cp_next();
416 } while (c >= 0 && c != '\n');
419 static void tr_ec(char **args)
421 c_ec = args[1] ? args[1][0] : '\\';
424 static void tr_cc(char **args)
426 c_cc = args[1] ? args[1][0] : '.';
429 static void tr_c2(char **args)
431 c_c2 = args[1] ? args[1][0] : '\'';
434 static void tr_eo(char **args)
436 c_ec = -1;
439 static void tr_hc(char **args)
441 char *s = args[1];
442 if (!s || charread(&s, c_hc) < 0)
443 strcpy(c_hc, "\\%");
446 /* sentence ending and their transparent characters */
447 static char eos_sent[NCHARS][GNLEN] = { ".", "?", "!", };
448 static int eos_sentcnt = 3;
449 static char eos_tran[NCHARS][GNLEN] = { "'", "\"", ")", "]", "*", };
450 static int eos_trancnt = 5;
452 static void tr_eos(char **args)
454 eos_sentcnt = 0;
455 eos_trancnt = 0;
456 if (args[1]) {
457 char *s = args[1];
458 while (s && charread(&s, eos_sent[eos_sentcnt]) >= 0)
459 if (eos_sentcnt < NCHARS - 1)
460 eos_sentcnt++;
462 if (args[2]) {
463 char *s = args[2];
464 while (s && charread(&s, eos_tran[eos_trancnt]) >= 0)
465 if (eos_trancnt < NCHARS - 1)
466 eos_trancnt++;
470 int c_eossent(char *s)
472 int i;
473 for (i = 0; i < eos_sentcnt; i++)
474 if (!strcmp(eos_sent[i], s))
475 return 1;
476 return 0;
479 int c_eostran(char *s)
481 int i;
482 for (i = 0; i < eos_trancnt; i++)
483 if (!strcmp(eos_tran[i], s))
484 return 1;
485 return 0;
488 /* hyphenation dashes and hyphenation inhibiting character */
489 static char hy_dash[NCHARS][GNLEN] = { "\\:", "-", "em", "en", "\\-", "--", "hy", };
490 static int hy_dashcnt = 7;
491 static char hy_stop[NCHARS][GNLEN] = { "\\%", };
492 static int hy_stopcnt = 1;
494 static void tr_nh(char **args)
496 n_hy = 0;
499 static void tr_hy(char **args)
501 n_hy = args[1] ? eval_re(args[1], n_hy, '\0') : 1;
504 static void tr_hlm(char **args)
506 n_hlm = args[1] ? eval_re(args[1], n_hlm, '\0') : 0;
509 static void tr_hycost(char **args)
511 n_hycost = args[1] ? eval_re(args[1], n_hycost, '\0') : 0;
512 n_hycost2 = args[2] ? eval_re(args[2], n_hycost2, '\0') : 0;
513 n_hycost3 = args[3] ? eval_re(args[3], n_hycost3, '\0') : 0;
516 static void tr_hydash(char **args)
518 hy_dashcnt = 0;
519 if (args[1]) {
520 char *s = args[1];
521 while (s && charread(&s, hy_dash[hy_dashcnt]) >= 0)
522 if (hy_dashcnt < NCHARS - 1)
523 hy_dashcnt++;
527 static void tr_hystop(char **args)
529 hy_stopcnt = 0;
530 if (args[1]) {
531 char *s = args[1];
532 while (s && charread(&s, hy_stop[hy_stopcnt]) >= 0)
533 if (hy_stopcnt < NCHARS - 1)
534 hy_stopcnt++;
538 int c_hydash(char *s)
540 int i;
541 for (i = 0; i < hy_dashcnt; i++)
542 if (!strcmp(hy_dash[i], s))
543 return 1;
544 return 0;
547 int c_hystop(char *s)
549 int i;
550 for (i = 0; i < hy_stopcnt; i++)
551 if (!strcmp(hy_stop[i], s))
552 return 1;
553 return 0;
556 int c_hymark(char *s)
558 return !strcmp(c_bp, s) || !strcmp(c_hc, s);
561 static void tr_pmll(char **args)
563 n_pmll = args[1] ? eval_re(args[1], n_pmll, '\0') : 0;
564 n_pmllcost = args[2] ? eval_re(args[2], n_pmllcost, '\0') : 100;
567 static void tr_lg(char **args)
569 if (args[1])
570 n_lg = eval(args[1], '\0');
573 static void tr_kn(char **args)
575 if (args[1])
576 n_kn = eval(args[1], '\0');
579 static void tr_cp(char **args)
581 if (args[1])
582 n_cp = atoi(args[1]);
585 static void tr_ss(char **args)
587 if (args[1]) {
588 n_ss = eval_re(args[1], n_ss, 0);
589 n_sss = args[2] ? eval_re(args[2], n_sss, 0) : n_ss;
593 static void tr_ssh(char **args)
595 n_ssh = args[1] ? eval_re(args[1], n_ssh, 0) : 0;
598 static void tr_cs(char **args)
600 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
601 if (fn)
602 font_setcs(fn, args[2] ? eval(args[2], 0) : 0,
603 args[3] ? eval(args[3], 0) : 0);
606 static void tr_fzoom(char **args)
608 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
609 if (fn)
610 font_setzoom(fn, args[2] ? eval(args[2], 0) : 0);
613 static void tr_tkf(char **args)
615 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
616 if (fn && args[5])
617 font_track(fn, eval(args[2], 0), eval(args[3], 0),
618 eval(args[4], 0), eval(args[5], 0));
621 static void tr_ff(char **args)
623 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
624 int i;
625 for (i = 2; i < NARGS; i++)
626 if (fn && args[i] && args[i][0] && args[i][1])
627 font_feat(fn, args[i] + 1, args[i][0] == '+');
630 static void tr_ffsc(char **args)
632 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
633 if (fn)
634 font_scrp(fn, args[2]);
635 if (fn)
636 font_lang(fn, args[3]);
639 static void tr_nm(char **args)
641 if (!args[1]) {
642 n_nm = 0;
643 return;
645 n_nm = 1;
646 n_ln = eval_re(args[1], n_ln, 0);
647 n_ln = MAX(0, n_ln);
648 if (args[2] && isdigit((unsigned char) args[2][0]))
649 n_nM = MAX(1, eval(args[2], 0));
650 if (args[3] && isdigit((unsigned char) args[3][0]))
651 n_nS = MAX(0, eval(args[3], 0));
652 if (args[4] && isdigit((unsigned char) args[4][0]))
653 n_nI = MAX(0, eval(args[4], 0));
656 static void tr_nn(char **args)
658 n_nn = args[1] ? eval(args[1], 0) : 1;
661 static void tr_bd(char **args)
663 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
664 if (!args[1] || !strcmp("S", args[1]))
665 return;
666 if (fn)
667 font_setbd(fn, args[2] ? eval(args[2], 'u') : 0);
670 static void tr_it(char **args)
672 if (args[2]) {
673 n_it = map(args[2]);
674 n_itn = eval(args[1], 0);
675 } else {
676 n_it = 0;
680 static void tr_mc(char **args)
682 char *s = args[1];
683 if (s && charread(&s, c_mc) >= 0) {
684 n_mc = 1;
685 n_mcn = args[2] ? eval(args[2], 'm') : SC_EM;
686 } else {
687 n_mc = 0;
691 static void tr_tc(char **args)
693 char *s = args[1];
694 if (!s || charread(&s, c_tc) < 0)
695 strcpy(c_tc, "");
698 static void tr_lc(char **args)
700 char *s = args[1];
701 if (!s || charread(&s, c_lc) < 0)
702 strcpy(c_lc, "");
705 static void tr_lf(char **args)
707 if (args[1])
708 in_lf(args[2], eval(args[1], 0));
711 static void tr_chop(char **args)
713 struct sbuf sbuf;
714 int id;
715 id = map(args[1]);
716 if (str_get(id)) {
717 sbuf_init(&sbuf);
718 sbuf_append(&sbuf, str_get(id));
719 if (!sbuf_empty(&sbuf)) {
720 sbuf_cut(&sbuf, sbuf_len(&sbuf) - 1);
721 str_set(id, sbuf_buf(&sbuf));
723 sbuf_done(&sbuf);
727 /* character translation (.tr) */
728 static struct dict *cmap; /* character mapping */
729 static char cmap_src[NCMAPS][GNLEN]; /* source character */
730 static char cmap_dst[NCMAPS][GNLEN]; /* character mapping */
731 static int cmap_n; /* number of translated character */
733 void cmap_add(char *c1, char *c2)
735 int i = dict_get(cmap, c1);
736 if (i >= 0) {
737 strcpy(cmap_dst[i], c2);
738 } else if (cmap_n < NCMAPS) {
739 strcpy(cmap_src[cmap_n], c1);
740 strcpy(cmap_dst[cmap_n], c2);
741 dict_put(cmap, cmap_src[cmap_n], cmap_n);
742 cmap_n++;
746 char *cmap_map(char *c)
748 int i = dict_get(cmap, c);
749 return i >= 0 ? cmap_dst[i] : c;
752 static void tr_tr(char **args)
754 char *s = args[1];
755 char c1[GNLEN], c2[GNLEN];
756 while (s && charread(&s, c1) >= 0) {
757 if (charread(&s, c2) < 0)
758 strcpy(c2, " ");
759 cmap_add(c1, c2);
763 /* character definition (.char) */
764 static char cdef_src[NCDEFS][GNLEN]; /* source character */
765 static char *cdef_dst[NCDEFS]; /* character definition */
766 static int cdef_fn[NCDEFS]; /* owning font */
767 static int cdef_n; /* number of defined characters */
768 static int cdef_expanding; /* inside cdef_expand() call */
770 static int cdef_find(char *c, int fn)
772 int i;
773 for (i = 0; i < cdef_n; i++)
774 if ((!cdef_fn[i] || cdef_fn[i] == fn) && !strcmp(cdef_src[i], c))
775 return i;
776 return -1;
779 /* return the definition of the given character */
780 char *cdef_map(char *c, int fn)
782 int i = cdef_find(c, fn);
783 return !cdef_expanding && i >= 0 ? cdef_dst[i] : NULL;
786 int cdef_expand(struct wb *wb, char *s, int fn)
788 char *d = cdef_map(s, fn);
789 if (!d)
790 return 1;
791 cdef_expanding = 1;
792 ren_parse(wb, d);
793 cdef_expanding = 0;
794 return 0;
797 static void cdef_remove(char *fn, char *cs)
799 char c[GNLEN];
800 int i;
801 int fp = fn ? dev_pos(fn) : -1;
802 if (!cs || charread(&cs, c) < 0)
803 return;
804 for (i = 0; i < cdef_n; i++) {
805 if (!strcmp(cdef_src[i], c)) {
806 if (!fn || (fp > 0 && cdef_fn[i] == fp)) {
807 free(cdef_dst[i]);
808 cdef_dst[i] = NULL;
809 cdef_src[i][0] = '\0';
815 static void cdef_add(char *fn, char *cs, char *def)
817 char c[GNLEN];
818 int i;
819 if (!def || charread(&cs, c) < 0)
820 return;
821 i = cdef_find(c, fn ? dev_pos(fn) : -1);
822 if (i < 0) {
823 for (i = 0; i < cdef_n; i++)
824 if (!cdef_dst[i])
825 break;
826 if (i == cdef_n && cdef_n < NCDEFS)
827 cdef_n++;
829 if (i >= 0 && i < cdef_n) {
830 snprintf(cdef_src[i], sizeof(cdef_src[i]), "%s", c);
831 cdef_dst[i] = xmalloc(strlen(def) + 1);
832 strcpy(cdef_dst[i], def);
833 cdef_fn[i] = fn ? dev_pos(fn) : 0;
837 static void tr_rchar(char **args)
839 int i;
840 for (i = 1; i < NARGS; i++)
841 if (args[i])
842 cdef_remove(NULL, args[i]);
845 static void tr_char(char **args)
847 if (args[2])
848 cdef_add(NULL, args[1], args[2]);
849 else
850 cdef_remove(NULL, args[1]);
853 static void tr_ochar(char **args)
855 if (args[3])
856 cdef_add(args[1], args[2], args[3]);
857 else
858 cdef_remove(args[1], args[2]);
861 static void tr_fmap(char **args)
863 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
864 if (fn && args[2])
865 font_map(fn, args[2], args[3]);
868 static void tr_blm(char **args)
870 tr_bm = args[1] ? map(args[1]) : -1;
873 static void tr_lsm(char **args)
875 tr_sm = args[1] ? map(args[1]) : -1;
878 static void tr_co(char **args)
880 char *src = args[1];
881 char *dst = args[2];
882 if (src && dst && str_get(map(src)))
883 str_set(map(dst), str_get(map(src)));
886 static void tr_coa(char **args)
888 char *src = args[1];
889 char *dst = args[2];
890 if (src && dst && str_get(map(src))) {
891 struct sbuf sb;
892 sbuf_init(&sb);
893 if (str_get(map(dst)))
894 sbuf_append(&sb, str_get(map(dst)));
895 sbuf_append(&sb, str_get(map(src)));
896 str_set(map(dst), sbuf_buf(&sb));
897 sbuf_done(&sb);
901 static void tr_coo(char **args)
903 char *reg = args[1];
904 char *path = args[2];
905 FILE *fp;
906 if (!reg || !reg[0] || !path || !path[0])
907 return;
908 if ((fp = fopen(path, "w"))) {
909 if (str_get(map(reg)))
910 fputs(str_get(map(reg)), fp);
911 fclose(fp);
915 static void tr_coi(char **args)
917 char *reg = args[1];
918 char *path = args[2];
919 char buf[1024];
920 FILE *fp;
921 if (!reg || !reg[0] || !path || !path[0])
922 return;
923 if ((fp = fopen(path + 1, "r"))) {
924 struct sbuf sb;
925 sbuf_init(&sb);
926 while (fgets(buf, sizeof(buf), fp))
927 sbuf_append(&sb, buf);
928 str_set(map(reg), sbuf_buf(&sb));
929 sbuf_done(&sb);
930 fclose(fp);
934 static void tr_dv(char **args)
936 if (args[1])
937 out_x(args[1]);
940 /* read a single macro argument */
941 static int macroarg(struct sbuf *sbuf, int brk, int (*next)(void), void (*back)(int))
943 int quoted = 0;
944 int c;
945 c = next();
946 while (c == ' ')
947 c = next();
948 if (c == '\n' || c == brk)
949 back(c);
950 if (c < 0 || c == '\n' || c == brk)
951 return 1;
952 if (c == '"') {
953 quoted = 1;
954 c = next();
956 while (c >= 0 && c != '\n' && (quoted || c != brk)) {
957 if (!quoted && c == ' ')
958 break;
959 if (quoted && c == '"') {
960 c = next();
961 if (c != '"')
962 break;
964 if (c == c_ec) {
965 sbuf_add(sbuf, c);
966 c = next();
968 sbuf_add(sbuf, c);
969 c = next();
971 sbuf_add(sbuf, 0);
972 if (c >= 0)
973 back(c);
974 return 0;
977 /* split the arguments in sbuf, after calling one of mkargs_*() */
978 static void chopargs(struct sbuf *sbuf, char **args)
980 char *s = sbuf_buf(sbuf);
981 char *e = s + sbuf_len(sbuf);
982 int n = 0;
983 while (n < NARGS && s && s < e) {
984 args[n++] = s;
985 if ((s = memchr(s, '\0', e - s)))
986 s++;
990 /* read macro arguments; free the returned pointer when done */
991 char *tr_args(char **args, int brk, int (*next)(void), void (*back)(int))
993 struct sbuf sbuf;
994 sbuf_init(&sbuf);
995 while (!macroarg(&sbuf, brk, next, back))
997 chopargs(&sbuf, args);
998 return sbuf_out(&sbuf);
1001 /* read regular macro arguments */
1002 static void mkargs_macro(struct sbuf *sbuf)
1004 cp_copymode(1);
1005 while (!macroarg(sbuf, -1, cp_next, cp_back))
1007 jmp_eol();
1008 cp_copymode(0);
1011 /* read request arguments; trims tabs too */
1012 static void mkargs_req(struct sbuf *sbuf)
1014 int n = 0;
1015 int c;
1016 c = cp_next();
1017 while (n < NARGS) {
1018 int ok = 0;
1019 while (c == ' ' || c == '\t')
1020 c = cp_next();
1021 while (c >= 0 && c != '\n' && c != ' ' && c != '\t') {
1022 if (c != c_ni)
1023 sbuf_add(sbuf, c);
1024 c = cp_next();
1025 ok = 1;
1027 if (ok) {
1028 n++;
1029 sbuf_add(sbuf, 0);
1031 if (c == '\n')
1032 cp_back(c);
1033 if (c < 0 || c == '\n')
1034 break;
1036 jmp_eol();
1039 /* read arguments for .ds and .char */
1040 static void mkargs_ds(struct sbuf *sbuf)
1042 char *s = read_name(n_cp);
1043 sbuf_append(sbuf, s);
1044 sbuf_add(sbuf, 0);
1045 free(s);
1046 s = read_string();
1047 if (s) {
1048 sbuf_append(sbuf, s);
1049 sbuf_add(sbuf, 0);
1050 free(s);
1052 jmp_eol();
1055 /* read arguments for .ochar */
1056 static void mkargs_ochar(struct sbuf *sbuf)
1058 char *s = read_name(0);
1059 sbuf_append(sbuf, s);
1060 sbuf_add(sbuf, 0);
1061 free(s);
1062 mkargs_ds(sbuf);
1065 /* read arguments for .nr */
1066 static void mkargs_reg1(struct sbuf *sbuf)
1068 char *s = read_name(n_cp);
1069 sbuf_append(sbuf, s);
1070 sbuf_add(sbuf, 0);
1071 free(s);
1072 mkargs_req(sbuf);
1075 /* do not read any arguments; for .if, .ie and .el */
1076 static void mkargs_null(struct sbuf *sbuf)
1080 /* read the whole line for .tm */
1081 static void mkargs_eol(struct sbuf *sbuf)
1083 int c;
1084 cp_copymode(1);
1085 c = cp_next();
1086 while (c == ' ')
1087 c = cp_next();
1088 while (c >= 0 && c != '\n') {
1089 if (c != c_ni)
1090 sbuf_add(sbuf, c);
1091 c = cp_next();
1093 cp_copymode(0);
1096 static struct cmd {
1097 char *id;
1098 void (*f)(char **args);
1099 void (*args)(struct sbuf *sbuf);
1100 } cmds[] = {
1101 {TR_DIVBEG, tr_divbeg},
1102 {TR_DIVEND, tr_divend},
1103 {TR_DIVVS, tr_divvs},
1104 {TR_POPREN, tr_popren},
1105 {">>", tr_l2r},
1106 {"<<", tr_r2l},
1107 {"ab", tr_ab, mkargs_eol},
1108 {"ad", tr_ad},
1109 {"af", tr_af},
1110 {"am", tr_de, mkargs_reg1},
1111 {"as", tr_as, mkargs_ds},
1112 {"bd", tr_bd},
1113 {"blm", tr_blm},
1114 {"bp", tr_bp},
1115 {"br", tr_br},
1116 {"c2", tr_c2},
1117 {"cc", tr_cc},
1118 {"ce", tr_ce},
1119 {"ch", tr_ch},
1120 {"char", tr_char, mkargs_ds},
1121 {"chop", tr_chop, mkargs_reg1},
1122 {"cl", tr_cl},
1123 {"co", tr_co},
1124 {"co+", tr_coa},
1125 {"co<", tr_coi, mkargs_ds},
1126 {"co>", tr_coo, mkargs_ds},
1127 {"cp", tr_cp},
1128 {"cs", tr_cs},
1129 {"da", tr_di},
1130 {"de", tr_de, mkargs_reg1},
1131 {"di", tr_di},
1132 {"ds", tr_ds, mkargs_ds},
1133 {"dt", tr_dt},
1134 {"dv", tr_dv, mkargs_eol},
1135 {"ec", tr_ec},
1136 {"el", tr_el, mkargs_null},
1137 {"em", tr_em},
1138 {"eo", tr_eo},
1139 {"eos", tr_eos},
1140 {"ev", tr_ev},
1141 {"ex", tr_ex},
1142 {"fc", tr_fc},
1143 {"ff", tr_ff},
1144 {"fi", tr_fi},
1145 {"fl", tr_br},
1146 {"fmap", tr_fmap},
1147 {"fp", tr_fp},
1148 {"ffsc", tr_ffsc},
1149 {"fspecial", tr_fspecial},
1150 {"ft", tr_ft},
1151 {"fzoom", tr_fzoom},
1152 {"hc", tr_hc},
1153 {"hcode", tr_hcode},
1154 {"hlm", tr_hlm},
1155 {"hpf", tr_hpf},
1156 {"hpfa", tr_hpfa},
1157 {"hy", tr_hy},
1158 {"hycost", tr_hycost},
1159 {"hydash", tr_hydash},
1160 {"hystop", tr_hystop},
1161 {"hw", tr_hw},
1162 {"ie", tr_if, mkargs_null},
1163 {"if", tr_if, mkargs_null},
1164 {"ig", tr_ig},
1165 {"in", tr_in},
1166 {"in2", tr_in2},
1167 {"it", tr_it},
1168 {"kn", tr_kn},
1169 {"lc", tr_lc},
1170 {"lf", tr_lf},
1171 {"lg", tr_lg},
1172 {"ll", tr_ll},
1173 {"ls", tr_ls},
1174 {"lsm", tr_lsm},
1175 {"lt", tr_lt},
1176 {"mc", tr_mc},
1177 {"mk", tr_mk},
1178 {"na", tr_na},
1179 {"ne", tr_ne},
1180 {"nf", tr_nf},
1181 {"nh", tr_nh},
1182 {"nm", tr_nm},
1183 {"nn", tr_nn},
1184 {"nr", tr_nr, mkargs_reg1},
1185 {"ns", tr_ns},
1186 {"nx", tr_nx},
1187 {"ochar", tr_ochar, mkargs_ochar},
1188 {"os", tr_os},
1189 {"pc", tr_pc},
1190 {"pl", tr_pl},
1191 {"pmll", tr_pmll},
1192 {"pn", tr_pn},
1193 {"po", tr_po},
1194 {"ps", tr_ps},
1195 {"rchar", tr_rchar},
1196 {"rm", tr_rm},
1197 {"rn", tr_rn},
1198 {"rr", tr_rr},
1199 {"rs", tr_rs},
1200 {"rt", tr_rt},
1201 {"shift", tr_shift},
1202 {"so", tr_so},
1203 {"sp", tr_sp},
1204 {"ss", tr_ss},
1205 {"ssh", tr_ssh},
1206 {"sv", tr_sv},
1207 {"sy", tr_sy, mkargs_eol},
1208 {"ta", tr_ta},
1209 {"tc", tr_tc},
1210 {"ti", tr_ti},
1211 {"ti2", tr_ti2},
1212 {"tkf", tr_tkf},
1213 {"tl", tr_tl, mkargs_null},
1214 {"tm", tr_tm, mkargs_eol},
1215 {"tr", tr_tr, mkargs_eol},
1216 {"vs", tr_vs},
1217 {"wh", tr_wh},
1220 static char *dotted(char *name, int dot)
1222 char *out = xmalloc(strlen(name) + 2);
1223 out[0] = dot;
1224 strcpy(out + 1, name);
1225 return out;
1228 /* execute a built-in request */
1229 void tr_req(int reg, char **args)
1231 struct cmd *req = str_dget(reg);
1232 if (req)
1233 req->f(args);
1236 /* interpolate a macro for tr_nextreq() */
1237 static void tr_nextreq_exec(char *mac, char *arg0, int readargs)
1239 char *args[NARGS + 3] = {arg0};
1240 struct cmd *req = str_dget(map(mac));
1241 char *str = str_get(map(mac));
1242 struct sbuf sbuf;
1243 sbuf_init(&sbuf);
1244 if (readargs) {
1245 if (req && req->args)
1246 req->args(&sbuf);
1247 if (req && !req->args)
1248 mkargs_req(&sbuf);
1249 if (!req)
1250 mkargs_macro(&sbuf);
1251 chopargs(&sbuf, args + 1);
1253 if (str)
1254 in_push(str, args);
1255 if (!str && req)
1256 req->f(args);
1257 sbuf_done(&sbuf);
1260 /* read the next troff request; return zero if a request was executed. */
1261 int tr_nextreq(void)
1263 char *mac;
1264 char *arg0 = NULL;
1265 int c;
1266 if (!tr_nl)
1267 return 1;
1268 c = cp_next();
1269 /* transparent line indicator */
1270 if (c == c_ec) {
1271 int c2 = cp_next();
1272 if (c2 == '!') {
1273 char *args[NARGS + 3] = {"\\!"};
1274 struct sbuf sbuf;
1275 sbuf_init(&sbuf);
1276 cp_copymode(1);
1277 mkargs_eol(&sbuf);
1278 cp_copymode(0);
1279 chopargs(&sbuf, args + 1);
1280 tr_transparent(args);
1281 sbuf_done(&sbuf);
1282 return 0;
1284 cp_back(c2);
1286 /* not a request, a blank line, or a line with leading spaces */
1287 if (c < 0 || (c != c_cc && c != c_c2 &&
1288 (c != '\n' || tr_bm < 0) &&
1289 (c != ' ' || tr_sm < 0))) {
1290 cp_back(c);
1291 return 1;
1293 cp_reqbeg();
1294 if (c == '\n') { /* blank line macro */
1295 mac = malloc(strlen(map_name(tr_bm)) + 1);
1296 strcpy(mac, map_name(tr_bm));
1297 arg0 = dotted(mac, '.');
1298 tr_nextreq_exec(mac, arg0, 0);
1299 } else if (c == ' ') { /* leading space macro */
1300 int i;
1301 mac = malloc(strlen(map_name(tr_sm)) + 1);
1302 strcpy(mac, map_name(tr_sm));
1303 for (i = 0; c == ' '; i++)
1304 c = cp_next();
1305 cp_back(c);
1306 n_lsn = i;
1307 arg0 = dotted(mac, '.');
1308 tr_nextreq_exec(mac, arg0, 0);
1309 } else {
1310 mac = read_name(n_cp);
1311 arg0 = dotted(mac, c);
1312 tr_nextreq_exec(mac, arg0, 1);
1314 free(arg0);
1315 free(mac);
1316 return 0;
1319 int tr_next(void)
1321 int c;
1322 while (!tr_nextreq())
1324 c = cp_next();
1325 tr_nl = c == '\n' || c < 0;
1326 return c;
1329 void tr_init(void)
1331 int i;
1332 for (i = 0; i < LEN(cmds); i++)
1333 str_dset(map(cmds[i].id), &cmds[i]);
1334 cmap = dict_make(-1, 0, 2);
1337 void tr_done(void)
1339 int i;
1340 for (i = 0; i < cdef_n; i++)
1341 free(cdef_dst[i]);
1342 dict_free(cmap);