reg: fix 'a' format for number registers
[neatroff.git] / tr.c
blobbaa573b6c84c809861b1cc4c4da93cde93bf194b
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 if (args[3])
53 num_setinc(id, eval(args[3], 'u'));
56 static void tr_rr(char **args)
58 int i;
59 for (i = 1; i < NARGS; i++)
60 if (args[i])
61 num_del(map(args[i]));
64 static void tr_af(char **args)
66 if (args[2])
67 num_setfmt(map(args[1]), args[2]);
70 static void tr_ds(char **args)
72 str_set(map(args[1]), args[2] ? args[2] : "");
75 static void tr_as(char **args)
77 int reg;
78 char *s1, *s2, *s;
79 reg = map(args[1]);
80 s1 = str_get(reg) ? str_get(reg) : "";
81 s2 = args[2] ? args[2] : "";
82 s = xmalloc(strlen(s1) + strlen(s2) + 1);
83 strcpy(s, s1);
84 strcat(s, s2);
85 str_set(reg, s);
86 free(s);
89 static void tr_rm(char **args)
91 int i;
92 for (i = 1; i < NARGS; i++)
93 if (args[i])
94 str_rm(map(args[i]));
97 static void tr_rn(char **args)
99 if (!args[2])
100 return;
101 str_rn(map(args[1]), map(args[2]));
104 static void tr_po(char **args)
106 int po = args[1] ? eval_re(args[1], n_o, 'm') : n_o0;
107 n_o0 = n_o;
108 n_o = MAX(0, po);
111 /* read a string argument of a macro */
112 static char *read_string(void)
114 struct sbuf sbuf;
115 int c;
116 int empty;
117 sbuf_init(&sbuf);
118 cp_copymode(1);
119 while ((c = cp_next()) == ' ')
121 empty = c <= 0 || c == '\n';
122 if (c == '"')
123 c = cp_next();
124 while (c > 0 && c != '\n') {
125 if (c != c_ni)
126 sbuf_add(&sbuf, c);
127 c = cp_next();
129 if (c >= 0)
130 cp_back(c);
131 cp_copymode(0);
132 if (empty) {
133 sbuf_done(&sbuf);
134 return NULL;
136 return sbuf_out(&sbuf);
139 /* read a space separated macro argument; if two, read at most two characters */
140 static char *read_name(int two)
142 struct sbuf sbuf;
143 int c = cp_next();
144 int i = 0;
145 sbuf_init(&sbuf);
146 while (c == ' ' || c == '\t' || c == c_ni)
147 c = cp_next();
148 while (c > 0 && c != ' ' && c != '\t' && c != '\n' && (!two || i < 2)) {
149 if (c != c_ni) {
150 sbuf_add(&sbuf, c);
151 i++;
153 c = cp_next();
155 if (c >= 0)
156 cp_back(c);
157 return sbuf_out(&sbuf);
160 static void macrobody(struct sbuf *sbuf, char *end)
162 int first = 1;
163 int c;
164 char *req = NULL;
165 cp_back('\n');
166 cp_copymode(1);
167 while ((c = cp_next()) >= 0) {
168 if (sbuf && !first)
169 sbuf_add(sbuf, c);
170 first = 0;
171 if (c == '\n') {
172 if ((c = cp_next()) != c_cc) {
173 cp_back(c);
174 continue;
176 req = read_name(n_cp);
177 if (!strcmp(end, req)) {
178 in_push(end, NULL);
179 cp_back(c_cc);
180 break;
182 if (sbuf) {
183 sbuf_add(sbuf, c_cc);
184 sbuf_append(sbuf, req);
186 free(req);
187 req = NULL;
190 free(req);
191 cp_copymode(0);
194 static void tr_de(char **args)
196 struct sbuf sbuf;
197 int id;
198 if (!args[1])
199 return;
200 id = map(args[1]);
201 sbuf_init(&sbuf);
202 if (args[0][1] == 'a' && args[0][2] == 'm' && str_get(id))
203 sbuf_append(&sbuf, str_get(id));
204 macrobody(&sbuf, args[2] ? args[2] : ".");
205 str_set(id, sbuf_buf(&sbuf));
206 sbuf_done(&sbuf);
207 if (!n_cp && args[3]) /* parse the arguments as request argv[3] */
208 str_dset(id, str_dget(map(args[3])));
211 static void tr_ig(char **args)
213 macrobody(NULL, args[1] ? args[1] : ".");
216 /* read into sbuf until stop; if stop is NULL, stop at whitespace */
217 static int read_until(struct sbuf *sbuf, char *stop,
218 int (*next)(void), void (*back)(int))
220 char cs[GNLEN], cs2[GNLEN];
221 int c;
222 while ((c = next()) >= 0) {
223 if (c == c_ni)
224 continue;
225 back(c);
226 if (c == '\n')
227 return 1;
228 if (!stop && (c == ' ' || c == '\t'))
229 return 0;
230 charnext(cs, next, back);
231 if (stop && !strcmp(stop, cs))
232 return 0;
233 charnext_str(cs2, cs);
234 sbuf_append(sbuf, cs2);
236 return 1;
239 /* evaluate .if strcmp (i.e. 'str'str') */
240 static int if_strcmp(int (*next)(void), void (*back)(int))
242 char delim[GNLEN];
243 struct sbuf s1, s2;
244 int ret;
245 charnext(delim, next, back);
246 sbuf_init(&s1);
247 sbuf_init(&s2);
248 read_until(&s1, delim, next, back);
249 read_until(&s2, delim, next, back);
250 cp_reqbeg();
251 ret = !strcmp(sbuf_buf(&s1), sbuf_buf(&s2));
252 sbuf_done(&s1);
253 sbuf_done(&s2);
254 return ret;
257 /* evaluate .if condition letters */
258 static int if_cond(int (*next)(void), void (*back)(int))
260 switch (cp_next()) {
261 case 'o':
262 return n_pg % 2;
263 case 'e':
264 return !(n_pg % 2);
265 case 't':
266 return 1;
267 case 'n':
268 return 0;
270 return 0;
273 /* evaluate .if condition */
274 static int if_eval(int (*next)(void), void (*back)(int))
276 struct sbuf sbuf;
277 int ret;
278 sbuf_init(&sbuf);
279 read_until(&sbuf, NULL, next, back);
280 ret = eval(sbuf_buf(&sbuf), '\0') > 0;
281 sbuf_done(&sbuf);
282 return ret;
285 static int eval_if(int (*next)(void), void (*back)(int))
287 int neg = 0;
288 int ret;
289 int c;
290 do {
291 c = next();
292 } while (c == ' ' || c == '\t');
293 if (c == '!') {
294 neg = 1;
295 c = next();
297 back(c);
298 if (strchr("oetn", c)) {
299 ret = if_cond(next, back);
300 } else if (c == ' ') {
301 ret = 0;
302 } else if (!isdigit(c) && !strchr("-+*/%<=>&:.|()", c)) {
303 ret = if_strcmp(next, back);
304 } else {
305 ret = if_eval(next, back);
307 return ret != neg;
310 static int ie_cond[NIES]; /* .ie condition stack */
311 static int ie_depth;
313 static void tr_if(char **args)
315 int c = eval_if(cp_next, cp_back);
316 if (args[0][1] == 'i' && args[0][2] == 'e') /* .ie command */
317 if (ie_depth < NIES)
318 ie_cond[ie_depth++] = c;
319 cp_blk(!c);
322 static void tr_el(char **args)
324 cp_blk(ie_depth > 0 ? ie_cond[--ie_depth] : 1);
327 static void tr_na(char **args)
329 n_na = 1;
332 static int adjmode(int c, int def)
334 switch (c) {
335 case 'l':
336 return AD_L;
337 case 'r':
338 return AD_R;
339 case 'c':
340 return AD_C;
341 case 'b':
342 case 'n':
343 return AD_B;
344 case 'k':
345 return AD_B | AD_K;
347 return def;
350 static void tr_ad(char **args)
352 char *s = args[1];
353 n_na = 0;
354 if (!s)
355 return;
356 if (isdigit((unsigned char) s[0]))
357 n_j = atoi(s) & 15;
358 else
359 n_j = s[0] == 'p' ? AD_P | adjmode(s[1], AD_B) : adjmode(s[0], n_j);
362 static void tr_tm(char **args)
364 fprintf(stderr, "%s\n", args[1] ? args[1] : "");
367 static void tr_so(char **args)
369 if (args[1])
370 in_so(args[1]);
373 static void tr_nx(char **args)
375 in_nx(args[1]);
378 static void tr_shift(char **args)
380 int n = args[1] ? atoi(args[1]) : 1;
381 while (n-- >= 1)
382 in_shift();
385 static void tr_ex(char **args)
387 in_ex();
390 static void tr_sy(char **args)
392 system(args[1]);
395 static void tr_lt(char **args)
397 int lt = args[1] ? eval_re(args[1], n_lt, 'm') : n_t0;
398 n_t0 = n_t0;
399 n_lt = MAX(0, lt);
402 static void tr_pc(char **args)
404 char *s = args[1];
405 if (!s || charread(&s, c_pc) < 0)
406 strcpy(c_pc, "");
409 static void tr_tl(char **args)
411 int c;
412 do {
413 c = cp_next();
414 } while (c >= 0 && (c == ' ' || c == '\t'));
415 cp_back(c);
416 ren_tl(cp_next, cp_back);
417 do {
418 c = cp_next();
419 } while (c >= 0 && c != '\n');
422 static void tr_ec(char **args)
424 c_ec = args[1] ? args[1][0] : '\\';
427 static void tr_cc(char **args)
429 c_cc = args[1] ? args[1][0] : '.';
432 static void tr_c2(char **args)
434 c_c2 = args[1] ? args[1][0] : '\'';
437 static void tr_eo(char **args)
439 c_ec = -1;
442 static void tr_hc(char **args)
444 char *s = args[1];
445 if (!s || charread(&s, c_hc) < 0)
446 strcpy(c_hc, "\\%");
449 /* sentence ending and their transparent characters */
450 static char eos_sent[NCHARS][GNLEN] = { ".", "?", "!", };
451 static int eos_sentcnt = 3;
452 static char eos_tran[NCHARS][GNLEN] = { "'", "\"", ")", "]", "*", };
453 static int eos_trancnt = 5;
455 static void tr_eos(char **args)
457 eos_sentcnt = 0;
458 eos_trancnt = 0;
459 if (args[1]) {
460 char *s = args[1];
461 while (s && charread(&s, eos_sent[eos_sentcnt]) >= 0)
462 if (eos_sentcnt < NCHARS - 1)
463 eos_sentcnt++;
465 if (args[2]) {
466 char *s = args[2];
467 while (s && charread(&s, eos_tran[eos_trancnt]) >= 0)
468 if (eos_trancnt < NCHARS - 1)
469 eos_trancnt++;
473 int c_eossent(char *s)
475 int i;
476 for (i = 0; i < eos_sentcnt; i++)
477 if (!strcmp(eos_sent[i], s))
478 return 1;
479 return 0;
482 int c_eostran(char *s)
484 int i;
485 for (i = 0; i < eos_trancnt; i++)
486 if (!strcmp(eos_tran[i], s))
487 return 1;
488 return 0;
491 /* hyphenation dashes and hyphenation inhibiting character */
492 static char hy_dash[NCHARS][GNLEN] = { "\\:", "-", "em", "en", "\\-", "--", "hy", };
493 static int hy_dashcnt = 7;
494 static char hy_stop[NCHARS][GNLEN] = { "\\%", };
495 static int hy_stopcnt = 1;
497 static void tr_nh(char **args)
499 n_hy = 0;
502 static void tr_hy(char **args)
504 n_hy = args[1] ? eval_re(args[1], n_hy, '\0') : 1;
507 static void tr_hlm(char **args)
509 n_hlm = args[1] ? eval_re(args[1], n_hlm, '\0') : 0;
512 static void tr_hycost(char **args)
514 n_hycost = args[1] ? eval_re(args[1], n_hycost, '\0') : 0;
515 n_hycost2 = args[2] ? eval_re(args[2], n_hycost2, '\0') : 0;
516 n_hycost3 = args[3] ? eval_re(args[3], n_hycost3, '\0') : 0;
519 static void tr_hydash(char **args)
521 hy_dashcnt = 0;
522 if (args[1]) {
523 char *s = args[1];
524 while (s && charread(&s, hy_dash[hy_dashcnt]) >= 0)
525 if (hy_dashcnt < NCHARS - 1)
526 hy_dashcnt++;
530 static void tr_hystop(char **args)
532 hy_stopcnt = 0;
533 if (args[1]) {
534 char *s = args[1];
535 while (s && charread(&s, hy_stop[hy_stopcnt]) >= 0)
536 if (hy_stopcnt < NCHARS - 1)
537 hy_stopcnt++;
541 int c_hydash(char *s)
543 int i;
544 for (i = 0; i < hy_dashcnt; i++)
545 if (!strcmp(hy_dash[i], s))
546 return 1;
547 return 0;
550 int c_hystop(char *s)
552 int i;
553 for (i = 0; i < hy_stopcnt; i++)
554 if (!strcmp(hy_stop[i], s))
555 return 1;
556 return 0;
559 int c_hymark(char *s)
561 return !strcmp(c_bp, s) || !strcmp(c_hc, s);
564 static void tr_pmll(char **args)
566 n_pmll = args[1] ? eval_re(args[1], n_pmll, '\0') : 0;
567 n_pmllcost = args[2] ? eval_re(args[2], n_pmllcost, '\0') : 100;
570 static void tr_lg(char **args)
572 if (args[1])
573 n_lg = eval(args[1], '\0');
576 static void tr_kn(char **args)
578 if (args[1])
579 n_kn = eval(args[1], '\0');
582 static void tr_cp(char **args)
584 if (args[1])
585 n_cp = atoi(args[1]);
588 static void tr_ss(char **args)
590 if (args[1]) {
591 n_ss = eval_re(args[1], n_ss, 0);
592 n_sss = args[2] ? eval_re(args[2], n_sss, 0) : n_ss;
596 static void tr_ssh(char **args)
598 n_ssh = args[1] ? eval_re(args[1], n_ssh, 0) : 0;
601 static void tr_cs(char **args)
603 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
604 if (fn)
605 font_setcs(fn, args[2] ? eval(args[2], 0) : 0,
606 args[3] ? eval(args[3], 0) : 0);
609 static void tr_fzoom(char **args)
611 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
612 if (fn)
613 font_setzoom(fn, args[2] ? eval(args[2], 0) : 0);
616 static void tr_tkf(char **args)
618 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
619 if (fn && args[5])
620 font_track(fn, eval(args[2], 0), eval(args[3], 0),
621 eval(args[4], 0), eval(args[5], 0));
624 static void tr_ff(char **args)
626 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
627 int i;
628 for (i = 2; i < NARGS; i++)
629 if (fn && args[i] && args[i][0] && args[i][1])
630 font_feat(fn, args[i] + 1, args[i][0] == '+');
633 static void tr_ffsc(char **args)
635 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
636 if (fn)
637 font_scrp(fn, args[2]);
638 if (fn)
639 font_lang(fn, args[3]);
642 static void tr_nm(char **args)
644 if (!args[1]) {
645 n_nm = 0;
646 return;
648 n_nm = 1;
649 n_ln = eval_re(args[1], n_ln, 0);
650 n_ln = MAX(0, n_ln);
651 if (args[2] && isdigit((unsigned char) args[2][0]))
652 n_nM = MAX(1, eval(args[2], 0));
653 if (args[3] && isdigit((unsigned char) args[3][0]))
654 n_nS = MAX(0, eval(args[3], 0));
655 if (args[4] && isdigit((unsigned char) args[4][0]))
656 n_nI = MAX(0, eval(args[4], 0));
659 static void tr_nn(char **args)
661 n_nn = args[1] ? eval(args[1], 0) : 1;
664 static void tr_bd(char **args)
666 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
667 if (!args[1] || !strcmp("S", args[1]))
668 return;
669 if (fn)
670 font_setbd(fn, args[2] ? eval(args[2], 'u') : 0);
673 static void tr_it(char **args)
675 if (args[2]) {
676 n_it = map(args[2]);
677 n_itn = eval(args[1], 0);
678 } else {
679 n_it = 0;
683 static void tr_mc(char **args)
685 char *s = args[1];
686 if (s && charread(&s, c_mc) >= 0) {
687 n_mc = 1;
688 n_mcn = args[2] ? eval(args[2], 'm') : SC_EM;
689 } else {
690 n_mc = 0;
694 static void tr_tc(char **args)
696 char *s = args[1];
697 if (!s || charread(&s, c_tc) < 0)
698 strcpy(c_tc, "");
701 static void tr_lc(char **args)
703 char *s = args[1];
704 if (!s || charread(&s, c_lc) < 0)
705 strcpy(c_lc, "");
708 static void tr_lf(char **args)
710 if (args[1])
711 in_lf(args[2], eval(args[1], 0));
714 static void tr_chop(char **args)
716 struct sbuf sbuf;
717 int id;
718 id = map(args[1]);
719 if (str_get(id)) {
720 sbuf_init(&sbuf);
721 sbuf_append(&sbuf, str_get(id));
722 if (!sbuf_empty(&sbuf)) {
723 sbuf_cut(&sbuf, sbuf_len(&sbuf) - 1);
724 str_set(id, sbuf_buf(&sbuf));
726 sbuf_done(&sbuf);
730 /* character translation (.tr) */
731 static struct dict *cmap; /* character mapping */
732 static char cmap_src[NCMAPS][GNLEN]; /* source character */
733 static char cmap_dst[NCMAPS][GNLEN]; /* character mapping */
734 static int cmap_n; /* number of translated character */
736 void cmap_add(char *c1, char *c2)
738 int i = dict_get(cmap, c1);
739 if (i >= 0) {
740 strcpy(cmap_dst[i], c2);
741 } else if (cmap_n < NCMAPS) {
742 strcpy(cmap_src[cmap_n], c1);
743 strcpy(cmap_dst[cmap_n], c2);
744 dict_put(cmap, cmap_src[cmap_n], cmap_n);
745 cmap_n++;
749 char *cmap_map(char *c)
751 int i = dict_get(cmap, c);
752 return i >= 0 ? cmap_dst[i] : c;
755 static void tr_tr(char **args)
757 char *s = args[1];
758 char c1[GNLEN], c2[GNLEN];
759 while (s && charread(&s, c1) >= 0) {
760 if (charread(&s, c2) < 0)
761 strcpy(c2, " ");
762 cmap_add(c1, c2);
766 /* character definition (.char) */
767 static char cdef_src[NCDEFS][GNLEN]; /* source character */
768 static char *cdef_dst[NCDEFS]; /* character definition */
769 static int cdef_fn[NCDEFS]; /* owning font */
770 static int cdef_n; /* number of defined characters */
771 static int cdef_expanding; /* inside cdef_expand() call */
773 static int cdef_find(char *c, int fn)
775 int i;
776 for (i = 0; i < cdef_n; i++)
777 if ((!cdef_fn[i] || cdef_fn[i] == fn) && !strcmp(cdef_src[i], c))
778 return i;
779 return -1;
782 /* return the definition of the given character */
783 char *cdef_map(char *c, int fn)
785 int i = cdef_find(c, fn);
786 return !cdef_expanding && i >= 0 ? cdef_dst[i] : NULL;
789 int cdef_expand(struct wb *wb, char *s, int fn)
791 char *d = cdef_map(s, fn);
792 if (!d)
793 return 1;
794 cdef_expanding = 1;
795 ren_parse(wb, d);
796 cdef_expanding = 0;
797 return 0;
800 static void cdef_remove(char *fn, char *cs)
802 char c[GNLEN];
803 int i;
804 int fp = fn ? dev_pos(fn) : -1;
805 if (!cs || charread(&cs, c) < 0)
806 return;
807 for (i = 0; i < cdef_n; i++) {
808 if (!strcmp(cdef_src[i], c)) {
809 if (!fn || (fp > 0 && cdef_fn[i] == fp)) {
810 free(cdef_dst[i]);
811 cdef_dst[i] = NULL;
812 cdef_src[i][0] = '\0';
818 static void cdef_add(char *fn, char *cs, char *def)
820 char c[GNLEN];
821 int i;
822 if (!def || charread(&cs, c) < 0)
823 return;
824 i = cdef_find(c, fn ? dev_pos(fn) : -1);
825 if (i < 0) {
826 for (i = 0; i < cdef_n; i++)
827 if (!cdef_dst[i])
828 break;
829 if (i == cdef_n && cdef_n < NCDEFS)
830 cdef_n++;
832 if (i >= 0 && i < cdef_n) {
833 snprintf(cdef_src[i], sizeof(cdef_src[i]), "%s", c);
834 cdef_dst[i] = xmalloc(strlen(def) + 1);
835 strcpy(cdef_dst[i], def);
836 cdef_fn[i] = fn ? dev_pos(fn) : 0;
840 static void tr_rchar(char **args)
842 int i;
843 for (i = 1; i < NARGS; i++)
844 if (args[i])
845 cdef_remove(NULL, args[i]);
848 static void tr_char(char **args)
850 if (args[2])
851 cdef_add(NULL, args[1], args[2]);
852 else
853 cdef_remove(NULL, args[1]);
856 static void tr_ochar(char **args)
858 if (args[3])
859 cdef_add(args[1], args[2], args[3]);
860 else
861 cdef_remove(args[1], args[2]);
864 static void tr_fmap(char **args)
866 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
867 if (fn && args[2])
868 font_map(fn, args[2], args[3]);
871 static void tr_blm(char **args)
873 tr_bm = args[1] ? map(args[1]) : -1;
876 static void tr_lsm(char **args)
878 tr_sm = args[1] ? map(args[1]) : -1;
881 static void tr_co(char **args)
883 char *src = args[1];
884 char *dst = args[2];
885 if (src && dst && str_get(map(src)))
886 str_set(map(dst), str_get(map(src)));
889 static void tr_coa(char **args)
891 char *src = args[1];
892 char *dst = args[2];
893 if (src && dst && str_get(map(src))) {
894 struct sbuf sb;
895 sbuf_init(&sb);
896 if (str_get(map(dst)))
897 sbuf_append(&sb, str_get(map(dst)));
898 sbuf_append(&sb, str_get(map(src)));
899 str_set(map(dst), sbuf_buf(&sb));
900 sbuf_done(&sb);
904 static void tr_coo(char **args)
906 char *reg = args[1];
907 char *path = args[2];
908 FILE *fp;
909 if (!reg || !reg[0] || !path || !path[0])
910 return;
911 if ((fp = fopen(path, "w"))) {
912 if (str_get(map(reg)))
913 fputs(str_get(map(reg)), fp);
914 fclose(fp);
918 static void tr_coi(char **args)
920 char *reg = args[1];
921 char *path = args[2];
922 char buf[1024];
923 FILE *fp;
924 if (!reg || !reg[0] || !path || !path[0])
925 return;
926 if ((fp = fopen(path, "r"))) {
927 struct sbuf sb;
928 sbuf_init(&sb);
929 while (fgets(buf, sizeof(buf), fp))
930 sbuf_append(&sb, buf);
931 str_set(map(reg), sbuf_buf(&sb));
932 sbuf_done(&sb);
933 fclose(fp);
937 static void tr_dv(char **args)
939 if (args[1])
940 out_x(args[1]);
943 /* read a single macro argument */
944 static int macroarg(struct sbuf *sbuf, int brk, int (*next)(void), void (*back)(int))
946 int quoted = 0;
947 int c;
948 c = next();
949 while (c == ' ')
950 c = next();
951 if (c == '\n' || c == brk)
952 back(c);
953 if (c < 0 || c == '\n' || c == brk)
954 return 1;
955 if (c == '"') {
956 quoted = 1;
957 c = next();
959 while (c >= 0 && c != '\n' && (quoted || c != brk)) {
960 if (!quoted && c == ' ')
961 break;
962 if (quoted && c == '"') {
963 c = next();
964 if (c != '"')
965 break;
967 if (c == c_ec) {
968 sbuf_add(sbuf, c);
969 c = next();
971 sbuf_add(sbuf, c);
972 c = next();
974 sbuf_add(sbuf, 0);
975 if (c >= 0)
976 back(c);
977 return 0;
980 /* split the arguments in sbuf, after calling one of mkargs_*() */
981 static void chopargs(struct sbuf *sbuf, char **args)
983 char *s = sbuf_buf(sbuf);
984 char *e = s + sbuf_len(sbuf);
985 int n = 0;
986 while (n < NARGS && s && s < e) {
987 args[n++] = s;
988 if ((s = memchr(s, '\0', e - s)))
989 s++;
993 /* read macro arguments; free the returned pointer when done */
994 char *tr_args(char **args, int brk, int (*next)(void), void (*back)(int))
996 struct sbuf sbuf;
997 sbuf_init(&sbuf);
998 while (!macroarg(&sbuf, brk, next, back))
1000 chopargs(&sbuf, args);
1001 return sbuf_out(&sbuf);
1004 /* read regular macro arguments */
1005 static void mkargs_macro(struct sbuf *sbuf)
1007 cp_copymode(1);
1008 while (!macroarg(sbuf, -1, cp_next, cp_back))
1010 jmp_eol();
1011 cp_copymode(0);
1014 /* read request arguments; trims tabs too */
1015 static void mkargs_req(struct sbuf *sbuf)
1017 int n = 0;
1018 int c;
1019 c = cp_next();
1020 while (n < NARGS) {
1021 int ok = 0;
1022 while (c == ' ' || c == '\t')
1023 c = cp_next();
1024 while (c >= 0 && c != '\n' && c != ' ' && c != '\t') {
1025 if (c != c_ni)
1026 sbuf_add(sbuf, c);
1027 c = cp_next();
1028 ok = 1;
1030 if (ok) {
1031 n++;
1032 sbuf_add(sbuf, 0);
1034 if (c == '\n')
1035 cp_back(c);
1036 if (c < 0 || c == '\n')
1037 break;
1039 jmp_eol();
1042 /* read arguments for .ds and .char */
1043 static void mkargs_ds(struct sbuf *sbuf)
1045 char *s = read_name(n_cp);
1046 sbuf_append(sbuf, s);
1047 sbuf_add(sbuf, 0);
1048 free(s);
1049 s = read_string();
1050 if (s) {
1051 sbuf_append(sbuf, s);
1052 sbuf_add(sbuf, 0);
1053 free(s);
1055 jmp_eol();
1058 /* read arguments for .ochar */
1059 static void mkargs_ochar(struct sbuf *sbuf)
1061 char *s = read_name(0);
1062 sbuf_append(sbuf, s);
1063 sbuf_add(sbuf, 0);
1064 free(s);
1065 mkargs_ds(sbuf);
1068 /* read arguments for .nr */
1069 static void mkargs_reg1(struct sbuf *sbuf)
1071 char *s = read_name(n_cp);
1072 sbuf_append(sbuf, s);
1073 sbuf_add(sbuf, 0);
1074 free(s);
1075 mkargs_req(sbuf);
1078 /* do not read any arguments; for .if, .ie and .el */
1079 static void mkargs_null(struct sbuf *sbuf)
1083 /* read the whole line for .tm */
1084 static void mkargs_eol(struct sbuf *sbuf)
1086 int c;
1087 cp_copymode(1);
1088 c = cp_next();
1089 while (c == ' ')
1090 c = cp_next();
1091 while (c >= 0 && c != '\n') {
1092 if (c != c_ni)
1093 sbuf_add(sbuf, c);
1094 c = cp_next();
1096 cp_copymode(0);
1099 static struct cmd {
1100 char *id;
1101 void (*f)(char **args);
1102 void (*args)(struct sbuf *sbuf);
1103 } cmds[] = {
1104 {TR_DIVBEG, tr_divbeg},
1105 {TR_DIVEND, tr_divend},
1106 {TR_DIVVS, tr_divvs},
1107 {TR_POPREN, tr_popren},
1108 {">>", tr_l2r},
1109 {"<<", tr_r2l},
1110 {"ab", tr_ab, mkargs_eol},
1111 {"ad", tr_ad},
1112 {"af", tr_af},
1113 {"am", tr_de, mkargs_reg1},
1114 {"as", tr_as, mkargs_ds},
1115 {"bd", tr_bd},
1116 {"blm", tr_blm},
1117 {"bp", tr_bp},
1118 {"br", tr_br},
1119 {"c2", tr_c2},
1120 {"cc", tr_cc},
1121 {"ce", tr_ce},
1122 {"ch", tr_ch},
1123 {"char", tr_char, mkargs_ds},
1124 {"chop", tr_chop, mkargs_reg1},
1125 {"cl", tr_cl},
1126 {"co", tr_co},
1127 {"co+", tr_coa},
1128 {"co<", tr_coi, mkargs_ds},
1129 {"co>", tr_coo, mkargs_ds},
1130 {"cp", tr_cp},
1131 {"cs", tr_cs},
1132 {"da", tr_di},
1133 {"de", tr_de, mkargs_reg1},
1134 {"di", tr_di},
1135 {"ds", tr_ds, mkargs_ds},
1136 {"dt", tr_dt},
1137 {"dv", tr_dv, mkargs_eol},
1138 {"ec", tr_ec},
1139 {"el", tr_el, mkargs_null},
1140 {"em", tr_em},
1141 {"eo", tr_eo},
1142 {"eos", tr_eos},
1143 {"ev", tr_ev},
1144 {"ex", tr_ex},
1145 {"fc", tr_fc},
1146 {"ff", tr_ff},
1147 {"fi", tr_fi},
1148 {"fl", tr_br},
1149 {"fmap", tr_fmap},
1150 {"fp", tr_fp},
1151 {"ffsc", tr_ffsc},
1152 {"fspecial", tr_fspecial},
1153 {"ft", tr_ft},
1154 {"fzoom", tr_fzoom},
1155 {"hc", tr_hc},
1156 {"hcode", tr_hcode},
1157 {"hlm", tr_hlm},
1158 {"hpf", tr_hpf},
1159 {"hpfa", tr_hpfa},
1160 {"hy", tr_hy},
1161 {"hycost", tr_hycost},
1162 {"hydash", tr_hydash},
1163 {"hystop", tr_hystop},
1164 {"hw", tr_hw},
1165 {"ie", tr_if, mkargs_null},
1166 {"if", tr_if, mkargs_null},
1167 {"ig", tr_ig},
1168 {"in", tr_in},
1169 {"in2", tr_in2},
1170 {"it", tr_it},
1171 {"kn", tr_kn},
1172 {"lc", tr_lc},
1173 {"lf", tr_lf},
1174 {"lg", tr_lg},
1175 {"ll", tr_ll},
1176 {"ls", tr_ls},
1177 {"lsm", tr_lsm},
1178 {"lt", tr_lt},
1179 {"mc", tr_mc},
1180 {"mk", tr_mk},
1181 {"na", tr_na},
1182 {"ne", tr_ne},
1183 {"nf", tr_nf},
1184 {"nh", tr_nh},
1185 {"nm", tr_nm},
1186 {"nn", tr_nn},
1187 {"nr", tr_nr, mkargs_reg1},
1188 {"ns", tr_ns},
1189 {"nx", tr_nx},
1190 {"ochar", tr_ochar, mkargs_ochar},
1191 {"os", tr_os},
1192 {"pc", tr_pc},
1193 {"pl", tr_pl},
1194 {"pmll", tr_pmll},
1195 {"pn", tr_pn},
1196 {"po", tr_po},
1197 {"ps", tr_ps},
1198 {"rchar", tr_rchar},
1199 {"rm", tr_rm},
1200 {"rn", tr_rn},
1201 {"rr", tr_rr},
1202 {"rs", tr_rs},
1203 {"rt", tr_rt},
1204 {"shift", tr_shift},
1205 {"so", tr_so},
1206 {"sp", tr_sp},
1207 {"ss", tr_ss},
1208 {"ssh", tr_ssh},
1209 {"sv", tr_sv},
1210 {"sy", tr_sy, mkargs_eol},
1211 {"ta", tr_ta},
1212 {"tc", tr_tc},
1213 {"ti", tr_ti},
1214 {"ti2", tr_ti2},
1215 {"tkf", tr_tkf},
1216 {"tl", tr_tl, mkargs_null},
1217 {"tm", tr_tm, mkargs_eol},
1218 {"tr", tr_tr, mkargs_eol},
1219 {"vs", tr_vs},
1220 {"wh", tr_wh},
1223 static char *dotted(char *name, int dot)
1225 char *out = xmalloc(strlen(name) + 2);
1226 out[0] = dot;
1227 strcpy(out + 1, name);
1228 return out;
1231 /* execute a built-in request */
1232 void tr_req(int reg, char **args)
1234 struct cmd *req = str_dget(reg);
1235 if (req)
1236 req->f(args);
1239 /* interpolate a macro for tr_nextreq() */
1240 static void tr_nextreq_exec(char *mac, char *arg0, int readargs)
1242 char *args[NARGS + 3] = {arg0};
1243 struct cmd *req = str_dget(map(mac));
1244 char *str = str_get(map(mac));
1245 struct sbuf sbuf;
1246 sbuf_init(&sbuf);
1247 if (readargs) {
1248 if (req && req->args)
1249 req->args(&sbuf);
1250 if (req && !req->args)
1251 mkargs_req(&sbuf);
1252 if (!req)
1253 mkargs_macro(&sbuf);
1254 chopargs(&sbuf, args + 1);
1256 if (str)
1257 in_push(str, args);
1258 if (!str && req)
1259 req->f(args);
1260 sbuf_done(&sbuf);
1263 /* read the next troff request; return zero if a request was executed. */
1264 int tr_nextreq(void)
1266 char *mac;
1267 char *arg0 = NULL;
1268 int c;
1269 if (!tr_nl)
1270 return 1;
1271 c = cp_next();
1272 /* transparent line indicator */
1273 if (c == c_ec) {
1274 int c2 = cp_next();
1275 if (c2 == '!') {
1276 char *args[NARGS + 3] = {"\\!"};
1277 struct sbuf sbuf;
1278 sbuf_init(&sbuf);
1279 cp_copymode(1);
1280 mkargs_eol(&sbuf);
1281 cp_copymode(0);
1282 chopargs(&sbuf, args + 1);
1283 tr_transparent(args);
1284 sbuf_done(&sbuf);
1285 return 0;
1287 cp_back(c2);
1289 /* not a request, a blank line, or a line with leading spaces */
1290 if (c < 0 || (c != c_cc && c != c_c2 &&
1291 (c != '\n' || tr_bm < 0) &&
1292 (c != ' ' || tr_sm < 0))) {
1293 cp_back(c);
1294 return 1;
1296 cp_reqbeg();
1297 if (c == '\n') { /* blank line macro */
1298 mac = malloc(strlen(map_name(tr_bm)) + 1);
1299 strcpy(mac, map_name(tr_bm));
1300 arg0 = dotted(mac, '.');
1301 tr_nextreq_exec(mac, arg0, 0);
1302 } else if (c == ' ') { /* leading space macro */
1303 int i;
1304 mac = malloc(strlen(map_name(tr_sm)) + 1);
1305 strcpy(mac, map_name(tr_sm));
1306 for (i = 0; c == ' '; i++)
1307 c = cp_next();
1308 cp_back(c);
1309 n_lsn = i;
1310 arg0 = dotted(mac, '.');
1311 tr_nextreq_exec(mac, arg0, 0);
1312 } else {
1313 mac = read_name(n_cp);
1314 arg0 = dotted(mac, c);
1315 tr_nextreq_exec(mac, arg0, 1);
1317 free(arg0);
1318 free(mac);
1319 return 0;
1322 int tr_next(void)
1324 int c;
1325 while (!tr_nextreq())
1327 c = cp_next();
1328 tr_nl = c == '\n' || c < 0;
1329 return c;
1332 void tr_init(void)
1334 int i;
1335 for (i = 0; i < LEN(cmds); i++)
1336 str_dset(map(cmds[i].id), &cmds[i]);
1337 cmap = dict_make(-1, 0, 2);
1340 void tr_done(void)
1342 int i;
1343 for (i = 0; i < cdef_n; i++)
1344 free(cdef_dst[i]);
1345 dict_free(cmap);