tr: change the behaviour of of .co request
[neatroff.git] / tr.c
blobf79da209e6fc2974224c8b3120ab1a8b9e469a2e
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);
208 static void tr_ig(char **args)
210 macrobody(NULL, args[1] ? args[1] : ".");
213 /* read into sbuf until stop; if stop is NULL, stop at whitespace */
214 static int read_until(struct sbuf *sbuf, char *stop,
215 int (*next)(void), void (*back)(int))
217 char cs[GNLEN], cs2[GNLEN];
218 int c;
219 while ((c = next()) >= 0) {
220 if (c == c_ni)
221 continue;
222 back(c);
223 if (c == '\n')
224 return 1;
225 if (!stop && (c == ' ' || c == '\t'))
226 return 0;
227 charnext(cs, next, back);
228 if (stop && !strcmp(stop, cs))
229 return 0;
230 charnext_str(cs2, cs);
231 sbuf_append(sbuf, cs2);
233 return 1;
236 /* evaluate .if strcmp (i.e. 'str'str') */
237 static int if_strcmp(int (*next)(void), void (*back)(int))
239 char delim[GNLEN];
240 struct sbuf s1, s2;
241 int ret;
242 charnext(delim, next, back);
243 sbuf_init(&s1);
244 sbuf_init(&s2);
245 read_until(&s1, delim, next, back);
246 read_until(&s2, delim, next, back);
247 cp_reqbeg();
248 ret = !strcmp(sbuf_buf(&s1), sbuf_buf(&s2));
249 sbuf_done(&s1);
250 sbuf_done(&s2);
251 return ret;
254 /* evaluate .if condition letters */
255 static int if_cond(int (*next)(void), void (*back)(int))
257 switch (cp_next()) {
258 case 'o':
259 return n_pg % 2;
260 case 'e':
261 return !(n_pg % 2);
262 case 't':
263 return 1;
264 case 'n':
265 return 0;
267 return 0;
270 /* evaluate .if condition */
271 static int if_eval(int (*next)(void), void (*back)(int))
273 struct sbuf sbuf;
274 int ret;
275 sbuf_init(&sbuf);
276 read_until(&sbuf, NULL, next, back);
277 ret = eval(sbuf_buf(&sbuf), '\0') > 0;
278 sbuf_done(&sbuf);
279 return ret;
282 static int eval_if(int (*next)(void), void (*back)(int))
284 int neg = 0;
285 int ret;
286 int c;
287 do {
288 c = next();
289 } while (c == ' ' || c == '\t');
290 if (c == '!') {
291 neg = 1;
292 c = next();
294 back(c);
295 if (strchr("oetn", c)) {
296 ret = if_cond(next, back);
297 } else if (!isdigit(c) && !strchr("-+*/%<=>&:.|()", c)) {
298 ret = if_strcmp(next, back);
299 } else {
300 ret = if_eval(next, back);
302 return ret != neg;
305 static int ie_cond[NIES]; /* .ie condition stack */
306 static int ie_depth;
308 static void tr_if(char **args)
310 int c = eval_if(cp_next, cp_back);
311 if (args[0][1] == 'i' && args[0][2] == 'e') /* .ie command */
312 if (ie_depth < NIES)
313 ie_cond[ie_depth++] = c;
314 cp_blk(!c);
317 static void tr_el(char **args)
319 cp_blk(ie_depth > 0 ? ie_cond[--ie_depth] : 1);
322 static void tr_na(char **args)
324 n_na = 1;
327 static int adjmode(int c, int def)
329 switch (c) {
330 case 'l':
331 return AD_L;
332 case 'r':
333 return AD_R;
334 case 'c':
335 return AD_C;
336 case 'b':
337 case 'n':
338 return AD_B;
339 case 'k':
340 return AD_B | AD_K;
342 return def;
345 static void tr_ad(char **args)
347 char *s = args[1];
348 n_na = 0;
349 if (!s)
350 return;
351 if (isdigit((unsigned char) s[0]))
352 n_j = atoi(s) & 15;
353 else
354 n_j = s[0] == 'p' ? AD_P | adjmode(s[1], AD_B) : adjmode(s[0], n_j);
357 static void tr_tm(char **args)
359 fprintf(stderr, "%s\n", args[1] ? args[1] : "");
362 static void tr_so(char **args)
364 if (args[1])
365 in_so(args[1]);
368 static void tr_nx(char **args)
370 in_nx(args[1]);
373 static void tr_shift(char **args)
375 int n = args[1] ? atoi(args[1]) : 1;
376 while (n-- >= 1)
377 in_shift();
380 static void tr_ex(char **args)
382 in_ex();
385 static void tr_sy(char **args)
387 system(args[1]);
390 static void tr_lt(char **args)
392 int lt = args[1] ? eval_re(args[1], n_lt, 'm') : n_t0;
393 n_t0 = n_t0;
394 n_lt = MAX(0, lt);
397 static void tr_pc(char **args)
399 char *s = args[1];
400 if (!s || charread(&s, c_pc) < 0)
401 strcpy(c_pc, "");
404 static void tr_tl(char **args)
406 int c;
407 do {
408 c = cp_next();
409 } while (c >= 0 && (c == ' ' || c == '\t'));
410 cp_back(c);
411 ren_tl(cp_next, cp_back);
412 do {
413 c = cp_next();
414 } while (c >= 0 && c != '\n');
417 static void tr_ec(char **args)
419 c_ec = args[1] ? args[1][0] : '\\';
422 static void tr_cc(char **args)
424 c_cc = args[1] ? args[1][0] : '.';
427 static void tr_c2(char **args)
429 c_c2 = args[1] ? args[1][0] : '\'';
432 static void tr_eo(char **args)
434 c_ec = -1;
437 static void tr_hc(char **args)
439 char *s = args[1];
440 if (!s || charread(&s, c_hc) < 0)
441 strcpy(c_hc, "\\%");
444 /* sentence ending and their transparent characters */
445 static char eos_sent[NCHARS][GNLEN] = { ".", "?", "!", };
446 static int eos_sentcnt = 3;
447 static char eos_tran[NCHARS][GNLEN] = { "'", "\"", ")", "]", "*", };
448 static int eos_trancnt = 5;
450 static void tr_eos(char **args)
452 eos_sentcnt = 0;
453 eos_trancnt = 0;
454 if (args[1]) {
455 char *s = args[1];
456 while (s && charread(&s, eos_sent[eos_sentcnt]) >= 0)
457 if (eos_sentcnt < NCHARS - 1)
458 eos_sentcnt++;
460 if (args[2]) {
461 char *s = args[2];
462 while (s && charread(&s, eos_tran[eos_trancnt]) >= 0)
463 if (eos_trancnt < NCHARS - 1)
464 eos_trancnt++;
468 int c_eossent(char *s)
470 int i;
471 for (i = 0; i < eos_sentcnt; i++)
472 if (!strcmp(eos_sent[i], s))
473 return 1;
474 return 0;
477 int c_eostran(char *s)
479 int i;
480 for (i = 0; i < eos_trancnt; i++)
481 if (!strcmp(eos_tran[i], s))
482 return 1;
483 return 0;
486 /* hyphenation dashes and hyphenation inhibiting character */
487 static char hy_dash[NCHARS][GNLEN] = { "\\:", "-", "em", "en", "\\-", "--", "hy", };
488 static int hy_dashcnt = 7;
489 static char hy_stop[NCHARS][GNLEN] = { "\\%", };
490 static int hy_stopcnt = 1;
492 static void tr_nh(char **args)
494 n_hy = 0;
497 static void tr_hy(char **args)
499 n_hy = args[1] ? eval_re(args[1], n_hy, '\0') : 1;
502 static void tr_hlm(char **args)
504 n_hlm = args[1] ? eval_re(args[1], n_hlm, '\0') : 0;
507 static void tr_hycost(char **args)
509 n_hycost = args[1] ? eval_re(args[1], n_hycost, '\0') : 0;
510 n_hycost2 = args[2] ? eval_re(args[2], n_hycost2, '\0') : 0;
511 n_hycost3 = args[3] ? eval_re(args[3], n_hycost3, '\0') : 0;
514 static void tr_hydash(char **args)
516 hy_dashcnt = 0;
517 if (args[1]) {
518 char *s = args[1];
519 while (s && charread(&s, hy_dash[hy_dashcnt]) >= 0)
520 if (hy_dashcnt < NCHARS - 1)
521 hy_dashcnt++;
525 static void tr_hystop(char **args)
527 hy_stopcnt = 0;
528 if (args[1]) {
529 char *s = args[1];
530 while (s && charread(&s, hy_stop[hy_stopcnt]) >= 0)
531 if (hy_stopcnt < NCHARS - 1)
532 hy_stopcnt++;
536 int c_hydash(char *s)
538 int i;
539 for (i = 0; i < hy_dashcnt; i++)
540 if (!strcmp(hy_dash[i], s))
541 return 1;
542 return 0;
545 int c_hystop(char *s)
547 int i;
548 for (i = 0; i < hy_stopcnt; i++)
549 if (!strcmp(hy_stop[i], s))
550 return 1;
551 return 0;
554 int c_hymark(char *s)
556 return !strcmp(c_bp, s) || !strcmp(c_hc, s);
559 static void tr_pmll(char **args)
561 n_pmll = args[1] ? eval_re(args[1], n_pmll, '\0') : 0;
562 n_pmllcost = args[2] ? eval_re(args[2], n_pmllcost, '\0') : 100;
565 static void tr_lg(char **args)
567 if (args[1])
568 n_lg = eval(args[1], '\0');
571 static void tr_kn(char **args)
573 if (args[1])
574 n_kn = eval(args[1], '\0');
577 static void tr_cp(char **args)
579 if (args[1])
580 n_cp = atoi(args[1]);
583 static void tr_ss(char **args)
585 if (args[1]) {
586 n_ss = eval_re(args[1], n_ss, 0);
587 n_sss = args[2] ? eval_re(args[2], n_sss, 0) : n_ss;
591 static void tr_ssh(char **args)
593 n_ssh = args[1] ? eval_re(args[1], n_ssh, 0) : 0;
596 static void tr_cs(char **args)
598 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
599 if (fn)
600 font_setcs(fn, args[2] ? eval(args[2], 0) : 0,
601 args[3] ? eval(args[3], 0) : 0);
604 static void tr_fzoom(char **args)
606 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
607 if (fn)
608 font_setzoom(fn, args[2] ? eval(args[2], 0) : 0);
611 static void tr_tkf(char **args)
613 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
614 if (fn && args[5])
615 font_track(fn, eval(args[2], 0), eval(args[3], 0),
616 eval(args[4], 0), eval(args[5], 0));
619 static void tr_ff(char **args)
621 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
622 int i;
623 for (i = 2; i < NARGS; i++)
624 if (fn && args[i] && args[i][0] && args[i][1])
625 font_feat(fn, args[i] + 1, args[i][0] == '+');
628 static void tr_ffsc(char **args)
630 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
631 if (fn)
632 font_scrp(fn, args[2]);
633 if (fn)
634 font_lang(fn, args[3]);
637 static void tr_nm(char **args)
639 if (!args[1]) {
640 n_nm = 0;
641 return;
643 n_nm = 1;
644 n_ln = eval_re(args[1], n_ln, 0);
645 n_ln = MAX(0, n_ln);
646 if (args[2] && isdigit((unsigned char) args[2][0]))
647 n_nM = MAX(1, eval(args[2], 0));
648 if (args[3] && isdigit((unsigned char) args[3][0]))
649 n_nS = MAX(0, eval(args[3], 0));
650 if (args[4] && isdigit((unsigned char) args[4][0]))
651 n_nI = MAX(0, eval(args[4], 0));
654 static void tr_nn(char **args)
656 n_nn = args[1] ? eval(args[1], 0) : 1;
659 static void tr_bd(char **args)
661 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
662 if (!args[1] || !strcmp("S", args[1]))
663 return;
664 if (fn)
665 font_setbd(fn, args[2] ? eval(args[2], 'u') : 0);
668 static void tr_it(char **args)
670 if (args[2]) {
671 n_it = map(args[2]);
672 n_itn = eval(args[1], 0);
673 } else {
674 n_it = 0;
678 static void tr_mc(char **args)
680 char *s = args[1];
681 if (s && charread(&s, c_mc) >= 0) {
682 n_mc = 1;
683 n_mcn = args[2] ? eval(args[2], 'm') : SC_EM;
684 } else {
685 n_mc = 0;
689 static void tr_tc(char **args)
691 char *s = args[1];
692 if (!s || charread(&s, c_tc) < 0)
693 strcpy(c_tc, "");
696 static void tr_lc(char **args)
698 char *s = args[1];
699 if (!s || charread(&s, c_lc) < 0)
700 strcpy(c_lc, "");
703 static void tr_lf(char **args)
705 if (args[1])
706 in_lf(args[2], eval(args[1], 0));
709 static void tr_chop(char **args)
711 struct sbuf sbuf;
712 int id;
713 id = map(args[1]);
714 if (str_get(id)) {
715 sbuf_init(&sbuf);
716 sbuf_append(&sbuf, str_get(id));
717 if (!sbuf_empty(&sbuf)) {
718 sbuf_cut(&sbuf, sbuf_len(&sbuf) - 1);
719 str_set(id, sbuf_buf(&sbuf));
721 sbuf_done(&sbuf);
725 /* character translation (.tr) */
726 static struct dict *cmap; /* character mapping */
727 static char cmap_src[NCMAPS][GNLEN]; /* source character */
728 static char cmap_dst[NCMAPS][GNLEN]; /* character mapping */
729 static int cmap_n; /* number of translated character */
731 void cmap_add(char *c1, char *c2)
733 int i = dict_get(cmap, c1);
734 if (i >= 0) {
735 strcpy(cmap_dst[i], c2);
736 } else if (cmap_n < NCMAPS) {
737 strcpy(cmap_src[cmap_n], c1);
738 strcpy(cmap_dst[cmap_n], c2);
739 dict_put(cmap, cmap_src[cmap_n], cmap_n);
740 cmap_n++;
744 char *cmap_map(char *c)
746 int i = dict_get(cmap, c);
747 return i >= 0 ? cmap_dst[i] : c;
750 static void tr_tr(char **args)
752 char *s = args[1];
753 char c1[GNLEN], c2[GNLEN];
754 while (s && charread(&s, c1) >= 0) {
755 if (charread(&s, c2) < 0)
756 strcpy(c2, " ");
757 cmap_add(c1, c2);
761 /* character definition (.char) */
762 static char cdef_src[NCDEFS][GNLEN]; /* source character */
763 static char *cdef_dst[NCDEFS]; /* character definition */
764 static int cdef_fn[NCDEFS]; /* owning font */
765 static int cdef_n; /* number of defined characters */
766 static int cdef_expanding; /* inside cdef_expand() call */
768 static int cdef_find(char *c, int fn)
770 int i;
771 for (i = 0; i < cdef_n; i++)
772 if ((!cdef_fn[i] || cdef_fn[i] == fn) && !strcmp(cdef_src[i], c))
773 return i;
774 return -1;
777 /* return the definition of the given character */
778 char *cdef_map(char *c, int fn)
780 int i = cdef_find(c, fn);
781 return !cdef_expanding && i >= 0 ? cdef_dst[i] : NULL;
784 int cdef_expand(struct wb *wb, char *s, int fn)
786 char *d = cdef_map(s, fn);
787 if (!d)
788 return 1;
789 cdef_expanding = 1;
790 ren_parse(wb, d);
791 cdef_expanding = 0;
792 return 0;
795 static void cdef_remove(char *fn, char *cs)
797 char c[GNLEN];
798 int i;
799 int fp = fn ? dev_pos(fn) : -1;
800 if (!cs || charread(&cs, c) < 0)
801 return;
802 for (i = 0; i < cdef_n; i++) {
803 if (!strcmp(cdef_src[i], c)) {
804 if (!fn || (fp > 0 && cdef_fn[i] == fp)) {
805 free(cdef_dst[i]);
806 cdef_dst[i] = NULL;
807 cdef_src[i][0] = '\0';
813 static void cdef_add(char *fn, char *cs, char *def)
815 char c[GNLEN];
816 int i;
817 if (!def || charread(&cs, c) < 0)
818 return;
819 i = cdef_find(c, fn ? dev_pos(fn) : -1);
820 if (i < 0) {
821 for (i = 0; i < cdef_n; i++)
822 if (!cdef_dst[i])
823 break;
824 if (i == cdef_n && cdef_n < NCDEFS)
825 cdef_n++;
827 if (i >= 0 && i < cdef_n) {
828 snprintf(cdef_src[i], sizeof(cdef_src[i]), "%s", c);
829 cdef_dst[i] = xmalloc(strlen(def) + 1);
830 strcpy(cdef_dst[i], def);
831 cdef_fn[i] = fn ? dev_pos(fn) : 0;
835 static void tr_rchar(char **args)
837 int i;
838 for (i = 1; i < NARGS; i++)
839 if (args[i])
840 cdef_remove(NULL, args[i]);
843 static void tr_char(char **args)
845 if (args[2])
846 cdef_add(NULL, args[1], args[2]);
847 else
848 cdef_remove(NULL, args[1]);
851 static void tr_ochar(char **args)
853 if (args[3])
854 cdef_add(args[1], args[2], args[3]);
855 else
856 cdef_remove(args[1], args[2]);
859 static void tr_fmap(char **args)
861 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
862 if (fn && args[2])
863 font_map(fn, args[2], args[3]);
866 static void tr_blm(char **args)
868 tr_bm = args[1] ? map(args[1]) : -1;
871 static void tr_lsm(char **args)
873 tr_sm = args[1] ? map(args[1]) : -1;
876 static void tr_co(char **args)
878 char *src = args[1];
879 char *dst = args[2];
880 if (src && dst && str_get(map(src)))
881 str_set(map(dst), str_get(map(src)));
884 static void tr_coa(char **args)
886 char *src = args[1];
887 char *dst = args[2];
888 if (src && dst && str_get(map(src))) {
889 struct sbuf sb;
890 sbuf_init(&sb);
891 if (str_get(map(dst)))
892 sbuf_append(&sb, str_get(map(dst)));
893 sbuf_append(&sb, str_get(map(src)));
894 str_set(map(dst), sbuf_buf(&sb));
895 sbuf_done(&sb);
899 static void tr_coo(char **args)
901 char *reg = args[1];
902 char *path = args[2];
903 FILE *fp;
904 if (!reg || !reg[0] || !path || !path[0])
905 return;
906 if ((fp = fopen(path, "w"))) {
907 if (str_get(map(reg)))
908 fputs(str_get(map(reg)), fp);
909 fclose(fp);
913 static void tr_coi(char **args)
915 char *reg = args[1];
916 char *path = args[2];
917 char buf[1024];
918 FILE *fp;
919 if (!reg || !reg[0] || !path || !path[0])
920 return;
921 if ((fp = fopen(path + 1, "r"))) {
922 struct sbuf sb;
923 sbuf_init(&sb);
924 while (fgets(buf, sizeof(buf), fp))
925 sbuf_append(&sb, buf);
926 str_set(map(reg), sbuf_buf(&sb));
927 sbuf_done(&sb);
928 fclose(fp);
932 /* read a macro argument */
933 static int tr_arg(struct sbuf *sbuf, int brk, int (*next)(void), void (*back)(int))
935 int quoted = 0;
936 int c;
937 c = next();
938 while (c == ' ')
939 c = next();
940 if (c == '\n' || c == brk)
941 back(c);
942 if (c < 0 || c == '\n' || c == brk)
943 return 1;
944 if (c == '"') {
945 quoted = 1;
946 c = next();
948 while (c >= 0 && c != '\n' && (quoted || c != brk)) {
949 if (!quoted && c == ' ')
950 break;
951 if (quoted && c == '"') {
952 c = next();
953 if (c != '"')
954 break;
956 if (c == c_ec) {
957 sbuf_add(sbuf, c);
958 c = next();
960 sbuf_add(sbuf, c);
961 c = next();
963 sbuf_add(sbuf, 0);
964 if (c >= 0)
965 back(c);
966 return 0;
969 /* read macro arguments; free the returned pointer when done */
970 char *tr_args(char **args, int brk, int (*next)(void), void (*back)(int))
972 struct sbuf sbuf;
973 char *s, *e;
974 int n = 0;
975 sbuf_init(&sbuf);
976 while (!tr_arg(&sbuf, brk, next, back))
978 s = sbuf_buf(&sbuf);
979 e = s + sbuf_len(&sbuf);
980 while (n < NARGS && s && s < e) {
981 args[n++] = s;
982 if ((s = memchr(s, '\0', e - s)))
983 s++;
985 return sbuf_out(&sbuf);
988 /* split the arguments in sbuf */
989 static int tr_argschop(struct sbuf *sbuf, char **args)
991 char *s = sbuf_buf(sbuf);
992 char *e = s + sbuf_len(sbuf);
993 int n = 0;
994 while (n < NARGS && s && s < e) {
995 args[n++] = s;
996 if ((s = memchr(s, '\0', e - s)))
997 s++;
999 return n;
1002 /* read request arguments; trims tabs too */
1003 static void mkargs_req(struct sbuf *sbuf)
1005 int n = 0;
1006 int c;
1007 c = cp_next();
1008 while (n < NARGS) {
1009 int ok = 0;
1010 while (c == ' ' || c == '\t')
1011 c = cp_next();
1012 while (c >= 0 && c != '\n' && c != ' ' && c != '\t') {
1013 if (c != c_ni)
1014 sbuf_add(sbuf, c);
1015 c = cp_next();
1016 ok = 1;
1018 if (ok) {
1019 n++;
1020 sbuf_add(sbuf, 0);
1022 if (c == '\n')
1023 cp_back(c);
1024 if (c < 0 || c == '\n')
1025 break;
1027 jmp_eol();
1030 /* read arguments for .ds and .char */
1031 static void mkargs_ds(struct sbuf *sbuf)
1033 char *s = read_name(n_cp);
1034 sbuf_append(sbuf, s);
1035 sbuf_add(sbuf, 0);
1036 free(s);
1037 s = read_string();
1038 if (s) {
1039 sbuf_append(sbuf, s);
1040 sbuf_add(sbuf, 0);
1041 free(s);
1043 jmp_eol();
1046 /* read arguments for .ochar */
1047 static void mkargs_ochar(struct sbuf *sbuf)
1049 char *s = read_name(0);
1050 sbuf_append(sbuf, s);
1051 sbuf_add(sbuf, 0);
1052 free(s);
1053 mkargs_ds(sbuf);
1056 /* read arguments for .nr */
1057 static void mkargs_reg1(struct sbuf *sbuf)
1059 char *s = read_name(n_cp);
1060 sbuf_append(sbuf, s);
1061 sbuf_add(sbuf, 0);
1062 free(s);
1063 mkargs_req(sbuf);
1066 /* do not read any arguments; for .if, .ie and .el */
1067 static void mkargs_null(struct sbuf *sbuf)
1071 /* read the whole line for .tm */
1072 static void mkargs_eol(struct sbuf *sbuf)
1074 int c;
1075 cp_copymode(1);
1076 c = cp_next();
1077 while (c == ' ')
1078 c = cp_next();
1079 while (c >= 0 && c != '\n') {
1080 if (c != c_ni)
1081 sbuf_add(sbuf, c);
1082 c = cp_next();
1084 cp_copymode(0);
1087 static struct cmd {
1088 char *id;
1089 void (*f)(char **args);
1090 void (*args)(struct sbuf *sbuf);
1091 } cmds[] = {
1092 {TR_DIVBEG, tr_divbeg},
1093 {TR_DIVEND, tr_divend},
1094 {TR_DIVVS, tr_divvs},
1095 {TR_POPREN, tr_popren},
1096 {">>", tr_l2r},
1097 {"<<", tr_r2l},
1098 {"ab", tr_ab, mkargs_eol},
1099 {"ad", tr_ad},
1100 {"af", tr_af},
1101 {"am", tr_de, mkargs_reg1},
1102 {"as", tr_as, mkargs_ds},
1103 {"bd", tr_bd},
1104 {"blm", tr_blm},
1105 {"bp", tr_bp},
1106 {"br", tr_br},
1107 {"c2", tr_c2},
1108 {"cc", tr_cc},
1109 {"ce", tr_ce},
1110 {"ch", tr_ch},
1111 {"char", tr_char, mkargs_ds},
1112 {"chop", tr_chop, mkargs_reg1},
1113 {"cl", tr_cl},
1114 {"co", tr_co},
1115 {"co+", tr_coa},
1116 {"co<", tr_coi, mkargs_ds},
1117 {"co>", tr_coo, mkargs_ds},
1118 {"cp", tr_cp},
1119 {"cs", tr_cs},
1120 {"da", tr_di},
1121 {"de", tr_de, mkargs_reg1},
1122 {"di", tr_di},
1123 {"ds", tr_ds, mkargs_ds},
1124 {"dt", tr_dt},
1125 {"ec", tr_ec},
1126 {"el", tr_el, mkargs_null},
1127 {"em", tr_em},
1128 {"eo", tr_eo},
1129 {"eos", tr_eos},
1130 {"ev", tr_ev},
1131 {"ex", tr_ex},
1132 {"fc", tr_fc},
1133 {"ff", tr_ff},
1134 {"fi", tr_fi},
1135 {"fl", tr_br},
1136 {"fmap", tr_fmap},
1137 {"fp", tr_fp},
1138 {"ffsc", tr_ffsc},
1139 {"fspecial", tr_fspecial},
1140 {"ft", tr_ft},
1141 {"fzoom", tr_fzoom},
1142 {"hc", tr_hc},
1143 {"hcode", tr_hcode},
1144 {"hlm", tr_hlm},
1145 {"hpf", tr_hpf},
1146 {"hpfa", tr_hpfa},
1147 {"hy", tr_hy},
1148 {"hycost", tr_hycost},
1149 {"hydash", tr_hydash},
1150 {"hystop", tr_hystop},
1151 {"hw", tr_hw},
1152 {"ie", tr_if, mkargs_null},
1153 {"if", tr_if, mkargs_null},
1154 {"ig", tr_ig},
1155 {"in", tr_in},
1156 {"in2", tr_in2},
1157 {"it", tr_it},
1158 {"kn", tr_kn},
1159 {"lc", tr_lc},
1160 {"lf", tr_lf},
1161 {"lg", tr_lg},
1162 {"ll", tr_ll},
1163 {"ls", tr_ls},
1164 {"lsm", tr_lsm},
1165 {"lt", tr_lt},
1166 {"mc", tr_mc},
1167 {"mk", tr_mk},
1168 {"na", tr_na},
1169 {"ne", tr_ne},
1170 {"nf", tr_nf},
1171 {"nh", tr_nh},
1172 {"nm", tr_nm},
1173 {"nn", tr_nn},
1174 {"nr", tr_nr, mkargs_reg1},
1175 {"ns", tr_ns},
1176 {"nx", tr_nx},
1177 {"ochar", tr_ochar, mkargs_ochar},
1178 {"os", tr_os},
1179 {"pc", tr_pc},
1180 {"pl", tr_pl},
1181 {"pmll", tr_pmll},
1182 {"pn", tr_pn},
1183 {"po", tr_po},
1184 {"ps", tr_ps},
1185 {"rchar", tr_rchar},
1186 {"rm", tr_rm},
1187 {"rn", tr_rn},
1188 {"rr", tr_rr},
1189 {"rs", tr_rs},
1190 {"rt", tr_rt},
1191 {"shift", tr_shift},
1192 {"so", tr_so},
1193 {"sp", tr_sp},
1194 {"ss", tr_ss},
1195 {"ssh", tr_ssh},
1196 {"sv", tr_sv},
1197 {"sy", tr_sy, mkargs_eol},
1198 {"ta", tr_ta},
1199 {"tc", tr_tc},
1200 {"ti", tr_ti},
1201 {"ti2", tr_ti2},
1202 {"tkf", tr_tkf},
1203 {"tl", tr_tl, mkargs_null},
1204 {"tm", tr_tm, mkargs_eol},
1205 {"tr", tr_tr, mkargs_eol},
1206 {"vs", tr_vs},
1207 {"wh", tr_wh},
1210 static char *dotted(char *name, int dot)
1212 char *out = xmalloc(strlen(name) + 2);
1213 out[0] = dot;
1214 strcpy(out + 1, name);
1215 return out;
1218 /* execute a built-in request */
1219 void tr_req(int reg, char **args)
1221 struct cmd *req = str_dget(reg);
1222 if (req)
1223 req->f(args);
1226 /* interpolate a macro for tr_nextreq() */
1227 static void tr_nextreq_exec(char *mac, char *arg0, int readargs)
1229 char *args[NARGS + 3] = {arg0};
1230 struct cmd *req = str_dget(map(mac));
1231 struct sbuf sbuf;
1232 if (req) {
1233 sbuf_init(&sbuf);
1234 if (readargs) {
1235 if (req->args)
1236 req->args(&sbuf);
1237 else
1238 mkargs_req(&sbuf);
1239 tr_argschop(&sbuf, args + 1);
1241 req->f(args);
1242 sbuf_done(&sbuf);
1243 } else {
1244 char *buf = NULL;
1245 if (readargs) {
1246 cp_copymode(1);
1247 buf = tr_args(args + 1, -1, cp_next, cp_back);
1248 jmp_eol();
1249 cp_copymode(0);
1251 if (str_get(map(mac)))
1252 in_push(str_get(map(mac)), args);
1253 free(buf);
1257 /* read the next troff request; return zero if a request was executed. */
1258 int tr_nextreq(void)
1260 char *mac;
1261 char *arg0 = NULL;
1262 int c;
1263 if (!tr_nl)
1264 return 1;
1265 c = cp_next();
1266 /* transparent line indicator */
1267 if (c == c_ec) {
1268 int c2 = cp_next();
1269 if (c2 == '!') {
1270 char *args[NARGS + 3] = {"\\!"};
1271 struct sbuf sbuf;
1272 sbuf_init(&sbuf);
1273 cp_copymode(1);
1274 mkargs_eol(&sbuf);
1275 cp_copymode(0);
1276 tr_argschop(&sbuf, args + 1);
1277 tr_transparent(args);
1278 sbuf_done(&sbuf);
1279 return 0;
1281 cp_back(c2);
1283 /* not a request, a blank line, or a line with leading spaces */
1284 if (c < 0 || (c != c_cc && c != c_c2 &&
1285 (c != '\n' || tr_bm < 0) &&
1286 (c != ' ' || tr_sm < 0))) {
1287 cp_back(c);
1288 return 1;
1290 cp_reqbeg();
1291 if (c == '\n') { /* blank line macro */
1292 mac = malloc(strlen(map_name(tr_bm)) + 1);
1293 strcpy(mac, map_name(tr_bm));
1294 arg0 = dotted(mac, '.');
1295 tr_nextreq_exec(mac, arg0, 0);
1296 } else if (c == ' ') { /* leading space macro */
1297 int i;
1298 mac = malloc(strlen(map_name(tr_sm)) + 1);
1299 strcpy(mac, map_name(tr_sm));
1300 for (i = 0; c == ' '; i++)
1301 c = cp_next();
1302 cp_back(c);
1303 n_lsn = i;
1304 arg0 = dotted(mac, '.');
1305 tr_nextreq_exec(mac, arg0, 0);
1306 } else {
1307 mac = read_name(n_cp);
1308 arg0 = dotted(mac, c);
1309 tr_nextreq_exec(mac, arg0, 1);
1311 free(arg0);
1312 free(mac);
1313 return 0;
1316 int tr_next(void)
1318 int c;
1319 while (!tr_nextreq())
1321 c = cp_next();
1322 tr_nl = c == '\n' || c < 0;
1323 return c;
1326 void tr_init(void)
1328 int i;
1329 for (i = 0; i < LEN(cmds); i++)
1330 str_dset(map(cmds[i].id), &cmds[i]);
1331 cmap = dict_make(-1, 0, 2);
1334 void tr_done(void)
1336 int i;
1337 for (i = 0; i < cdef_n; i++)
1338 free(cdef_dst[i]);
1339 dict_free(cmap);