cp: the zero-th argument
[neatroff.git] / tr.c
blobc2e2223178f24cd044f3a1cf2307257798a0951e
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 char c_pc[GNLEN] = "%"; /* page number character */
11 int c_ec = '\\'; /* escape character */
12 int c_cc = '.'; /* control character */
13 int c_c2 = '\''; /* no-break control character */
15 /* skip everything until the end of line */
16 static void jmp_eol(void)
18 int c;
19 do {
20 c = cp_next();
21 } while (c >= 0 && c != '\n');
24 static void tr_vs(char **args)
26 int vs = args[1] ? eval_re(args[1], n_v, 'p') : n_v0;
27 n_v0 = n_v;
28 n_v = MAX(0, vs);
31 static void tr_ls(char **args)
33 int ls = args[1] ? eval_re(args[1], n_L, 0) : n_L0;
34 n_L0 = n_L;
35 n_L = MAX(1, ls);
38 static void tr_pl(char **args)
40 int n = eval_re(args[1] ? args[1] : "11i", n_p, 'v');
41 n_p = MAX(0, n);
44 static void tr_nr(char **args)
46 int id;
47 if (!args[2])
48 return;
49 id = map(args[1]);
50 num_set(id, eval_re(args[2], num_get(id), 'u'));
51 num_setinc(id, args[3] ? eval(args[3], 'u') : 0);
54 static void tr_rr(char **args)
56 int i;
57 for (i = 1; i < NARGS; i++)
58 if (args[i])
59 num_del(map(args[i]));
62 static void tr_af(char **args)
64 if (args[2])
65 num_setfmt(map(args[1]), args[2]);
68 static void tr_ds(char **args)
70 str_set(map(args[1]), args[2] ? args[2] : "");
73 static void tr_as(char **args)
75 int reg;
76 char *s1, *s2, *s;
77 reg = map(args[1]);
78 s1 = str_get(reg) ? str_get(reg) : "";
79 s2 = args[2] ? args[2] : "";
80 s = xmalloc(strlen(s1) + strlen(s2) + 1);
81 strcpy(s, s1);
82 strcat(s, s2);
83 str_set(reg, s);
84 free(s);
87 static void tr_rm(char **args)
89 int i;
90 for (i = 1; i < NARGS; i++)
91 if (args[i])
92 str_rm(map(args[i]));
95 static void tr_rn(char **args)
97 if (!args[2])
98 return;
99 str_rn(map(args[1]), map(args[2]));
102 static void tr_po(char **args)
104 int po = args[1] ? eval_re(args[1], n_o, 'm') : n_o0;
105 n_o0 = n_o;
106 n_o = MAX(0, po);
109 /* read a string argument of a macro */
110 static char *read_string(void)
112 struct sbuf sbuf;
113 int c;
114 int empty;
115 sbuf_init(&sbuf);
116 cp_copymode(1);
117 while ((c = cp_next()) == ' ')
119 empty = c <= 0 || c == '\n';
120 if (c == '"')
121 c = cp_next();
122 while (c > 0 && c != '\n') {
123 if (c != c_ni)
124 sbuf_add(&sbuf, c);
125 c = cp_next();
127 if (c >= 0)
128 cp_back(c);
129 cp_copymode(0);
130 if (empty) {
131 sbuf_done(&sbuf);
132 return NULL;
134 return sbuf_out(&sbuf);
137 /* read a space separated macro argument; if two, read at most two characters */
138 static char *read_name(int two)
140 struct sbuf sbuf;
141 int c = cp_next();
142 int i = 0;
143 sbuf_init(&sbuf);
144 while (c == ' ' || c == '\t' || c == c_ni)
145 c = cp_next();
146 while (c > 0 && c != ' ' && c != '\t' && c != '\n' && (!two || i < 2)) {
147 if (c != c_ni) {
148 sbuf_add(&sbuf, c);
149 i++;
151 c = cp_next();
153 if (c >= 0)
154 cp_back(c);
155 return sbuf_out(&sbuf);
158 static void macrobody(struct sbuf *sbuf, char *end)
160 int first = 1;
161 int c;
162 char *req = NULL;
163 cp_back('\n');
164 cp_copymode(1);
165 while ((c = cp_next()) >= 0) {
166 if (sbuf && !first)
167 sbuf_add(sbuf, c);
168 first = 0;
169 if (c == '\n') {
170 if ((c = cp_next()) != c_cc) {
171 cp_back(c);
172 continue;
174 req = read_name(n_cp);
175 if (!strcmp(end, req)) {
176 in_push(end, NULL);
177 cp_back(c_cc);
178 break;
180 if (sbuf) {
181 sbuf_add(sbuf, c_cc);
182 sbuf_append(sbuf, req);
184 free(req);
185 req = NULL;
188 free(req);
189 cp_copymode(0);
192 static void tr_de(char **args)
194 struct sbuf sbuf;
195 int id;
196 if (!args[1])
197 return;
198 id = map(args[1]);
199 sbuf_init(&sbuf);
200 if (args[0][1] == 'a' && args[0][2] == 'm' && str_get(id))
201 sbuf_append(&sbuf, str_get(id));
202 macrobody(&sbuf, args[2] ? args[2] : ".");
203 str_set(id, sbuf_buf(&sbuf));
204 sbuf_done(&sbuf);
207 static void tr_ig(char **args)
209 macrobody(NULL, args[1] ? args[1] : ".");
212 /* read into sbuf until stop; if stop is NULL, stop at whitespace */
213 static int read_until(struct sbuf *sbuf, char *stop,
214 int (*next)(void), void (*back)(int))
216 char cs[GNLEN], cs2[GNLEN];
217 int c;
218 while ((c = next()) >= 0) {
219 if (c == c_ni)
220 continue;
221 back(c);
222 if (c == '\n')
223 return 1;
224 if (!stop && (c == ' ' || c == '\t'))
225 return 0;
226 charnext(cs, next, back);
227 if (stop && !strcmp(stop, cs))
228 return 0;
229 charnext_str(cs2, cs);
230 sbuf_append(sbuf, cs2);
232 return 1;
235 /* evaluate .if strcmp (i.e. 'str'str') */
236 static int if_strcmp(int (*next)(void), void (*back)(int))
238 char delim[GNLEN];
239 struct sbuf s1, s2;
240 int ret;
241 charnext(delim, next, back);
242 sbuf_init(&s1);
243 sbuf_init(&s2);
244 read_until(&s1, delim, next, back);
245 read_until(&s2, delim, next, back);
246 cp_reqbeg();
247 ret = !strcmp(sbuf_buf(&s1), sbuf_buf(&s2));
248 sbuf_done(&s1);
249 sbuf_done(&s2);
250 return ret;
253 /* evaluate .if condition letters */
254 static int if_cond(int (*next)(void), void (*back)(int))
256 switch (cp_next()) {
257 case 'o':
258 return n_pg % 2;
259 case 'e':
260 return !(n_pg % 2);
261 case 't':
262 return 1;
263 case 'n':
264 return 0;
266 return 0;
269 /* evaluate .if condition */
270 static int if_eval(int (*next)(void), void (*back)(int))
272 struct sbuf sbuf;
273 int ret;
274 sbuf_init(&sbuf);
275 read_until(&sbuf, NULL, next, back);
276 ret = eval(sbuf_buf(&sbuf), '\0') > 0;
277 sbuf_done(&sbuf);
278 return ret;
281 static int eval_if(int (*next)(void), void (*back)(int))
283 int neg = 0;
284 int ret;
285 int c;
286 do {
287 c = next();
288 } while (c == ' ' || c == '\t');
289 if (c == '!') {
290 neg = 1;
291 c = next();
293 back(c);
294 if (strchr("oetn", c)) {
295 ret = if_cond(next, back);
296 } else if (!isdigit(c) && !strchr("-+*/%<=>&:.|()", c)) {
297 ret = if_strcmp(next, back);
298 } else {
299 ret = if_eval(next, back);
301 return ret != neg;
304 static int ie_cond[NIES]; /* .ie condition stack */
305 static int ie_depth;
307 static void tr_if(char **args)
309 int c = eval_if(cp_next, cp_back);
310 if (args[0][1] == 'i' && args[0][2] == 'e') /* .ie command */
311 if (ie_depth < NIES)
312 ie_cond[ie_depth++] = c;
313 cp_blk(!c);
316 static void tr_el(char **args)
318 cp_blk(ie_depth > 0 ? ie_cond[--ie_depth] : 1);
321 static void tr_na(char **args)
323 n_na = 1;
326 static int adjmode(int c, int def)
328 switch (c) {
329 case 'l':
330 return AD_L;
331 case 'r':
332 return AD_R;
333 case 'c':
334 return AD_C;
335 case 'b':
336 case 'n':
337 return AD_B;
338 case 'k':
339 return AD_B | AD_K;
341 return def;
344 static void tr_ad(char **args)
346 char *s = args[1];
347 n_na = 0;
348 if (!s)
349 return;
350 if (isdigit((unsigned char) s[0]))
351 n_j = atoi(s) & 15;
352 else
353 n_j = s[0] == 'p' ? AD_P | adjmode(s[1], AD_B) : adjmode(s[0], n_j);
356 static void tr_tm(char **args)
358 fprintf(stderr, "%s\n", args[1] ? args[1] : "");
361 static void tr_so(char **args)
363 if (args[1])
364 in_so(args[1]);
367 static void tr_nx(char **args)
369 in_nx(args[1]);
372 static void tr_ex(char **args)
374 in_ex();
377 static void tr_sy(char **args)
379 system(args[1]);
382 static void tr_lt(char **args)
384 int lt = args[1] ? eval_re(args[1], n_lt, 'm') : n_t0;
385 n_t0 = n_t0;
386 n_lt = MAX(0, lt);
389 static void tr_pc(char **args)
391 char *s = args[1];
392 if (!s || charread(&s, c_pc) < 0)
393 strcpy(c_pc, "");
396 static void tr_tl(char **args)
398 int c;
399 do {
400 c = cp_next();
401 } while (c >= 0 && (c == ' ' || c == '\t'));
402 cp_back(c);
403 ren_tl(cp_next, cp_back);
404 do {
405 c = cp_next();
406 } while (c >= 0 && c != '\n');
409 static void tr_ec(char **args)
411 c_ec = args[1] ? args[1][0] : '\\';
414 static void tr_cc(char **args)
416 c_cc = args[1] ? args[1][0] : '.';
419 static void tr_c2(char **args)
421 c_c2 = args[1] ? args[1][0] : '\'';
424 static void tr_eo(char **args)
426 c_ec = -1;
429 static void tr_hc(char **args)
431 char *s = args[1];
432 if (!s || charread(&s, c_hc) < 0)
433 strcpy(c_hc, "\\%");
436 /* sentence ending and their transparent characters */
437 static char eos_sent[NCHARS][GNLEN] = { ".", "?", "!", };
438 static int eos_sentcnt = 3;
439 static char eos_tran[NCHARS][GNLEN] = { "'", "\"", ")", "]", "*", };
440 static int eos_trancnt = 5;
442 static void tr_eos(char **args)
444 eos_sentcnt = 0;
445 eos_trancnt = 0;
446 if (args[1]) {
447 char *s = args[1];
448 while (s && charread(&s, eos_sent[eos_sentcnt]) >= 0)
449 if (eos_sentcnt < NCHARS - 1)
450 eos_sentcnt++;
452 if (args[2]) {
453 char *s = args[2];
454 while (s && charread(&s, eos_tran[eos_trancnt]) >= 0)
455 if (eos_trancnt < NCHARS - 1)
456 eos_trancnt++;
460 int c_eossent(char *s)
462 int i;
463 for (i = 0; i < eos_sentcnt; i++)
464 if (!strcmp(eos_sent[i], s))
465 return 1;
466 return 0;
469 int c_eostran(char *s)
471 int i;
472 for (i = 0; i < eos_trancnt; i++)
473 if (!strcmp(eos_tran[i], s))
474 return 1;
475 return 0;
478 /* hyphenation dashes and hyphenation inhibiting character */
479 static char hy_dash[NCHARS][GNLEN] = { "\\:", "-", "em", "en", "\\-", "--", "hy", };
480 static int hy_dashcnt = 7;
481 static char hy_stop[NCHARS][GNLEN] = { "\\%", };
482 static int hy_stopcnt = 1;
484 static void tr_nh(char **args)
486 n_hy = 0;
489 static void tr_hy(char **args)
491 n_hy = args[1] ? eval_re(args[1], n_hy, '\0') : 1;
494 static void tr_hlm(char **args)
496 n_hlm = args[1] ? eval_re(args[1], n_hlm, '\0') : 0;
499 static void tr_hycost(char **args)
501 n_hycost = args[1] ? eval_re(args[1], n_hycost, '\0') : 0;
502 n_hycost2 = args[2] ? eval_re(args[2], n_hycost2, '\0') : 0;
503 n_hycost3 = args[3] ? eval_re(args[3], n_hycost3, '\0') : 0;
506 static void tr_hydash(char **args)
508 hy_dashcnt = 0;
509 if (args[1]) {
510 char *s = args[1];
511 while (s && charread(&s, hy_dash[hy_dashcnt]) >= 0)
512 if (hy_dashcnt < NCHARS - 1)
513 hy_dashcnt++;
517 static void tr_hystop(char **args)
519 hy_stopcnt = 0;
520 if (args[1]) {
521 char *s = args[1];
522 while (s && charread(&s, hy_stop[hy_stopcnt]) >= 0)
523 if (hy_stopcnt < NCHARS - 1)
524 hy_stopcnt++;
528 int c_hydash(char *s)
530 int i;
531 for (i = 0; i < hy_dashcnt; i++)
532 if (!strcmp(hy_dash[i], s))
533 return 1;
534 return 0;
537 int c_hystop(char *s)
539 int i;
540 for (i = 0; i < hy_stopcnt; i++)
541 if (!strcmp(hy_stop[i], s))
542 return 1;
543 return 0;
546 int c_hymark(char *s)
548 return !strcmp(c_bp, s) || !strcmp(c_hc, s);
551 static void tr_pmll(char **args)
553 n_pmll = args[1] ? eval_re(args[1], n_pmll, '\0') : 0;
554 n_pmllcost = args[2] ? eval_re(args[2], n_pmllcost, '\0') : 100;
557 static void tr_lg(char **args)
559 if (args[1])
560 n_lg = eval(args[1], '\0');
563 static void tr_kn(char **args)
565 if (args[1])
566 n_kn = eval(args[1], '\0');
569 static void tr_cp(char **args)
571 if (args[1])
572 n_cp = atoi(args[1]);
575 static void tr_ss(char **args)
577 if (args[1]) {
578 n_ss = eval_re(args[1], n_ss, 0);
579 n_sss = args[2] ? eval_re(args[2], n_sss, 0) : n_ss;
583 static void tr_ssh(char **args)
585 n_ssh = args[1] ? eval_re(args[1], n_ssh, 0) : 0;
588 static void tr_cs(char **args)
590 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
591 if (fn)
592 font_setcs(fn, args[2] ? eval(args[2], 0) : 0,
593 args[3] ? eval(args[3], 0) : 0);
596 static void tr_fzoom(char **args)
598 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
599 if (fn)
600 font_setzoom(fn, args[2] ? eval(args[2], 0) : 0);
603 static void tr_tkf(char **args)
605 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
606 if (fn && args[5])
607 font_track(fn, eval(args[2], 0), eval(args[3], 0),
608 eval(args[4], 0), eval(args[5], 0));
611 static void tr_ff(char **args)
613 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
614 int i;
615 for (i = 2; i < NARGS; i++)
616 if (fn && args[i] && args[i][0] && args[i][1])
617 font_feat(fn, args[i] + 1, args[i][0] == '+');
620 static void tr_ffsc(char **args)
622 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
623 if (fn && args[2])
624 font_scrp(fn, args[2] ? args[2] : NULL);
627 static void tr_nm(char **args)
629 if (!args[1]) {
630 n_nm = 0;
631 return;
633 n_nm = 1;
634 n_ln = eval_re(args[1], n_ln, 0);
635 n_ln = MAX(0, n_ln);
636 if (args[2] && isdigit((unsigned char) args[2][0]))
637 n_nM = MAX(1, eval(args[2], 0));
638 if (args[3] && isdigit((unsigned char) args[3][0]))
639 n_nS = MAX(0, eval(args[3], 0));
640 if (args[4] && isdigit((unsigned char) args[4][0]))
641 n_nI = MAX(0, eval(args[4], 0));
644 static void tr_nn(char **args)
646 n_nn = args[1] ? eval(args[1], 0) : 1;
649 static void tr_bd(char **args)
651 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
652 if (!args[1] || !strcmp("S", args[1]))
653 return;
654 if (fn)
655 font_setbd(fn, args[2] ? eval(args[2], 'u') : 0);
658 static void tr_it(char **args)
660 if (args[2]) {
661 n_it = map(args[2]);
662 n_itn = eval(args[1], 0);
663 } else {
664 n_it = 0;
668 static void tr_mc(char **args)
670 char *s = args[1];
671 if (s && charread(&s, c_mc) >= 0) {
672 n_mc = 1;
673 n_mcn = args[2] ? eval(args[2], 'm') : SC_EM;
674 } else {
675 n_mc = 0;
679 static void tr_tc(char **args)
681 char *s = args[1];
682 if (!s || charread(&s, c_tc) < 0)
683 strcpy(c_tc, "");
686 static void tr_lc(char **args)
688 char *s = args[1];
689 if (!s || charread(&s, c_lc) < 0)
690 strcpy(c_lc, "");
693 static void tr_lf(char **args)
695 if (args[1])
696 in_lf(args[2], eval(args[1], 0));
699 static void tr_chop(char **args)
701 struct sbuf sbuf;
702 int id;
703 id = map(args[1]);
704 if (str_get(id)) {
705 sbuf_init(&sbuf);
706 sbuf_append(&sbuf, str_get(id));
707 if (!sbuf_empty(&sbuf)) {
708 sbuf_cut(&sbuf, sbuf_len(&sbuf) - 1);
709 str_set(id, sbuf_buf(&sbuf));
711 sbuf_done(&sbuf);
715 /* character translation (.tr) */
716 static struct dict *cmap; /* character mapping */
717 static char cmap_src[NCMAPS][GNLEN]; /* source character */
718 static char cmap_dst[NCMAPS][GNLEN]; /* character mapping */
719 static int cmap_n; /* number of translated character */
721 void cmap_add(char *c1, char *c2)
723 int i = dict_get(cmap, c1);
724 if (i >= 0) {
725 strcpy(cmap_dst[i], c2);
726 } else if (cmap_n < NCMAPS) {
727 strcpy(cmap_src[cmap_n], c1);
728 strcpy(cmap_dst[cmap_n], c2);
729 dict_put(cmap, cmap_src[cmap_n], cmap_n);
730 cmap_n++;
734 char *cmap_map(char *c)
736 int i = dict_get(cmap, c);
737 return i >= 0 ? cmap_dst[i] : c;
740 static void tr_tr(char **args)
742 char *s = args[1];
743 char c1[GNLEN], c2[GNLEN];
744 while (s && charread(&s, c1) >= 0) {
745 if (charread(&s, c2) < 0)
746 strcpy(c2, " ");
747 cmap_add(c1, c2);
751 /* character definition (.char) */
752 static char cdef_src[NCDEFS][GNLEN]; /* source character */
753 static char *cdef_dst[NCDEFS]; /* character definition */
754 static int cdef_fn[NCDEFS]; /* owning font */
755 static int cdef_n; /* number of defined characters */
756 static int cdef_expanding; /* inside cdef_expand() call */
758 static int cdef_find(char *c, int fn)
760 int i;
761 for (i = 0; i < cdef_n; i++)
762 if ((!cdef_fn[i] || cdef_fn[i] == fn) && !strcmp(cdef_src[i], c))
763 return i;
764 return -1;
767 /* return the definition of the given character */
768 char *cdef_map(char *c, int fn)
770 int i = cdef_find(c, fn);
771 return !cdef_expanding && i >= 0 ? cdef_dst[i] : NULL;
774 int cdef_expand(struct wb *wb, char *s, int fn)
776 char *d = cdef_map(s, fn);
777 if (!d)
778 return 1;
779 cdef_expanding = 1;
780 ren_parse(wb, d);
781 cdef_expanding = 0;
782 return 0;
785 static void cdef_remove(char *fn, char *cs)
787 char c[GNLEN];
788 int i;
789 int fp = fn ? dev_pos(fn) : -1;
790 if (!cs || charread(&cs, c) < 0)
791 return;
792 for (i = 0; i < cdef_n; i++) {
793 if (!strcmp(cdef_src[i], c)) {
794 if (!fn || (fp > 0 && cdef_fn[i] == fp)) {
795 free(cdef_dst[i]);
796 cdef_dst[i] = NULL;
797 cdef_src[i][0] = '\0';
803 static void cdef_add(char *fn, char *cs, char *def)
805 char c[GNLEN];
806 int i;
807 if (!def || charread(&cs, c) < 0)
808 return;
809 i = cdef_find(c, fn ? dev_pos(fn) : -1);
810 if (i < 0) {
811 for (i = 0; i < cdef_n; i++)
812 if (!cdef_dst[i])
813 break;
814 if (i == cdef_n && cdef_n < NCDEFS)
815 cdef_n++;
817 if (i >= 0 && i < cdef_n) {
818 snprintf(cdef_src[i], sizeof(cdef_src[i]), "%s", c);
819 cdef_dst[i] = xmalloc(strlen(def) + 1);
820 strcpy(cdef_dst[i], def);
821 cdef_fn[i] = fn ? dev_pos(fn) : 0;
825 static void tr_rchar(char **args)
827 int i;
828 for (i = 1; i < NARGS; i++)
829 if (args[i])
830 cdef_remove(NULL, args[i]);
833 static void tr_char(char **args)
835 if (args[2])
836 cdef_add(NULL, args[1], args[2]);
837 else
838 cdef_remove(NULL, args[1]);
841 static void tr_ochar(char **args)
843 if (args[3])
844 cdef_add(args[1], args[2], args[3]);
845 else
846 cdef_remove(args[1], args[2]);
849 static void tr_fmap(char **args)
851 struct font *fn = args[1] ? dev_font(dev_pos(args[1])) : NULL;
852 if (fn && args[2])
853 font_map(fn, args[2], args[3]);
856 static void tr_blm(char **args)
858 tr_bm = args[1] ? map(args[1]) : -1;
861 static void tr_co(char **args)
863 char *reg = args[1];
864 char *path = args[2];
865 char buf[1024];
866 if (!reg || !reg[0] || !path || !path[0])
867 return;
868 if (path[0] == '>') {
869 FILE *fp = fopen(path + 1, "w");
870 if (fp && reg && str_get(map(reg)))
871 fputs(str_get(map(reg)), fp);
872 if (fp)
873 fclose(fp);
875 if (path[0] == '<') {
876 FILE *fp = fopen(path + 1, "r");
877 struct sbuf sb;
878 sbuf_init(&sb);
879 while (fp && fgets(buf, sizeof(buf), fp))
880 sbuf_append(&sb, buf);
881 str_set(map(reg), sbuf_buf(&sb));
882 sbuf_done(&sb);
883 if (fp)
884 fclose(fp);
888 /* read a macro argument */
889 static int tr_arg(struct sbuf *sbuf, int brk, int (*next)(void), void (*back)(int))
891 int quoted = 0;
892 int c;
893 c = next();
894 while (c == ' ')
895 c = next();
896 if (c == '\n' || c == brk)
897 back(c);
898 if (c < 0 || c == '\n' || c == brk)
899 return 1;
900 if (c == '"') {
901 quoted = 1;
902 c = next();
904 while (c >= 0 && c != '\n' && (quoted || c != brk)) {
905 if (!quoted && c == ' ')
906 break;
907 if (quoted && c == '"') {
908 c = next();
909 if (c != '"')
910 break;
912 if (c == c_ec) {
913 sbuf_add(sbuf, c);
914 c = next();
916 sbuf_add(sbuf, c);
917 c = next();
919 sbuf_add(sbuf, 0);
920 if (c >= 0)
921 back(c);
922 return 0;
925 /* read macro arguments; free the returned pointer when done */
926 char *tr_args(char **args, int brk, int (*next)(void), void (*back)(int))
928 struct sbuf sbuf;
929 char *s, *e;
930 int n = 0;
931 sbuf_init(&sbuf);
932 while (!tr_arg(&sbuf, brk, next, back))
934 s = sbuf_buf(&sbuf);
935 e = s + sbuf_len(&sbuf);
936 while (n < NARGS && s && s < e) {
937 args[n++] = s;
938 if ((s = memchr(s, '\0', e - s)))
939 s++;
941 return sbuf_out(&sbuf);
944 /* split the arguments in sbuf */
945 static int tr_argschop(struct sbuf *sbuf, char **args)
947 char *s = sbuf_buf(sbuf);
948 char *e = s + sbuf_len(sbuf);
949 int n = 0;
950 while (n < NARGS && s && s < e) {
951 args[n++] = s;
952 if ((s = memchr(s, '\0', e - s)))
953 s++;
955 return n;
958 /* read request arguments; trims tabs too */
959 static void mkargs_req(struct sbuf *sbuf)
961 int n = 0;
962 int c;
963 c = cp_next();
964 while (n < NARGS) {
965 int ok = 0;
966 while (c == ' ' || c == '\t')
967 c = cp_next();
968 while (c >= 0 && c != '\n' && c != ' ' && c != '\t') {
969 if (c != c_ni)
970 sbuf_add(sbuf, c);
971 c = cp_next();
972 ok = 1;
974 if (ok) {
975 n++;
976 sbuf_add(sbuf, 0);
978 if (c == '\n')
979 cp_back(c);
980 if (c < 0 || c == '\n')
981 break;
983 jmp_eol();
986 /* read arguments for .ds and .char */
987 static void mkargs_ds(struct sbuf *sbuf)
989 char *s = read_name(n_cp);
990 sbuf_append(sbuf, s);
991 sbuf_add(sbuf, 0);
992 free(s);
993 s = read_string();
994 if (s) {
995 sbuf_append(sbuf, s);
996 sbuf_add(sbuf, 0);
997 free(s);
999 jmp_eol();
1002 /* read arguments for .ochar */
1003 static void mkargs_ochar(struct sbuf *sbuf)
1005 char *s = read_name(0);
1006 sbuf_append(sbuf, s);
1007 sbuf_add(sbuf, 0);
1008 free(s);
1009 mkargs_ds(sbuf);
1012 /* read arguments for .nr */
1013 static void mkargs_reg1(struct sbuf *sbuf)
1015 char *s = read_name(n_cp);
1016 sbuf_append(sbuf, s);
1017 sbuf_add(sbuf, 0);
1018 free(s);
1019 mkargs_req(sbuf);
1022 /* do not read any arguments; for .if, .ie and .el */
1023 static void mkargs_null(struct sbuf *sbuf)
1027 /* read the whole line for .tm */
1028 static void mkargs_eol(struct sbuf *sbuf)
1030 int c;
1031 cp_copymode(1);
1032 c = cp_next();
1033 while (c == ' ')
1034 c = cp_next();
1035 while (c >= 0 && c != '\n') {
1036 if (c != c_ni)
1037 sbuf_add(sbuf, c);
1038 c = cp_next();
1040 cp_copymode(0);
1043 static struct cmd {
1044 char *id;
1045 void (*f)(char **args);
1046 void (*args)(struct sbuf *sbuf);
1047 } cmds[] = {
1048 {TR_DIVBEG, tr_divbeg},
1049 {TR_DIVEND, tr_divend},
1050 {TR_DIVVS, tr_divvs},
1051 {TR_POPREN, tr_popren},
1052 {">>", tr_l2r},
1053 {"<<", tr_r2l},
1054 {"ab", tr_ab, mkargs_eol},
1055 {"ad", tr_ad},
1056 {"af", tr_af},
1057 {"am", tr_de, mkargs_reg1},
1058 {"as", tr_as, mkargs_ds},
1059 {"bd", tr_bd},
1060 {"blm", tr_blm},
1061 {"bp", tr_bp},
1062 {"br", tr_br},
1063 {"c2", tr_c2},
1064 {"cc", tr_cc},
1065 {"ce", tr_ce},
1066 {"ch", tr_ch},
1067 {"char", tr_char, mkargs_ds},
1068 {"chop", tr_chop, mkargs_reg1},
1069 {"cl", tr_cl},
1070 {"co", tr_co, mkargs_ds},
1071 {"cp", tr_cp},
1072 {"cs", tr_cs},
1073 {"da", tr_di},
1074 {"de", tr_de, mkargs_reg1},
1075 {"di", tr_di},
1076 {"ds", tr_ds, mkargs_ds},
1077 {"dt", tr_dt},
1078 {"ec", tr_ec},
1079 {"el", tr_el, mkargs_null},
1080 {"em", tr_em},
1081 {"eo", tr_eo},
1082 {"eos", tr_eos},
1083 {"ev", tr_ev},
1084 {"ex", tr_ex},
1085 {"fc", tr_fc},
1086 {"ff", tr_ff},
1087 {"fi", tr_fi},
1088 {"fl", tr_br},
1089 {"fmap", tr_fmap},
1090 {"fp", tr_fp},
1091 {"ffsc", tr_ffsc},
1092 {"fspecial", tr_fspecial},
1093 {"ft", tr_ft},
1094 {"fzoom", tr_fzoom},
1095 {"hc", tr_hc},
1096 {"hcode", tr_hcode},
1097 {"hlm", tr_hlm},
1098 {"hpf", tr_hpf},
1099 {"hpfa", tr_hpfa},
1100 {"hy", tr_hy},
1101 {"hycost", tr_hycost},
1102 {"hydash", tr_hydash},
1103 {"hystop", tr_hystop},
1104 {"hw", tr_hw},
1105 {"ie", tr_if, mkargs_null},
1106 {"if", tr_if, mkargs_null},
1107 {"ig", tr_ig},
1108 {"in", tr_in},
1109 {"in2", tr_in2},
1110 {"it", tr_it},
1111 {"kn", tr_kn},
1112 {"lc", tr_lc},
1113 {"lf", tr_lf},
1114 {"lg", tr_lg},
1115 {"ll", tr_ll},
1116 {"ls", tr_ls},
1117 {"lt", tr_lt},
1118 {"mc", tr_mc},
1119 {"mk", tr_mk},
1120 {"na", tr_na},
1121 {"ne", tr_ne},
1122 {"nf", tr_nf},
1123 {"nh", tr_nh},
1124 {"nm", tr_nm},
1125 {"nn", tr_nn},
1126 {"nr", tr_nr, mkargs_reg1},
1127 {"ns", tr_ns},
1128 {"nx", tr_nx},
1129 {"ochar", tr_ochar, mkargs_ochar},
1130 {"os", tr_os},
1131 {"pc", tr_pc},
1132 {"pl", tr_pl},
1133 {"pmll", tr_pmll},
1134 {"pn", tr_pn},
1135 {"po", tr_po},
1136 {"ps", tr_ps},
1137 {"rchar", tr_rchar},
1138 {"rm", tr_rm},
1139 {"rn", tr_rn},
1140 {"rr", tr_rr},
1141 {"rs", tr_rs},
1142 {"rt", tr_rt},
1143 {"so", tr_so},
1144 {"sp", tr_sp},
1145 {"ss", tr_ss},
1146 {"ssh", tr_ssh},
1147 {"sv", tr_sv},
1148 {"sy", tr_sy, mkargs_eol},
1149 {"ta", tr_ta},
1150 {"tc", tr_tc},
1151 {"ti", tr_ti},
1152 {"ti2", tr_ti2},
1153 {"tkf", tr_tkf},
1154 {"tl", tr_tl, mkargs_null},
1155 {"tm", tr_tm, mkargs_eol},
1156 {"tr", tr_tr, mkargs_eol},
1157 {"vs", tr_vs},
1158 {"wh", tr_wh},
1161 static char *dotted(char *name, int dot)
1163 char *out = xmalloc(strlen(name) + 2);
1164 out[0] = dot;
1165 strcpy(out + 1, name);
1166 return out;
1169 /* execute a built-in request */
1170 void tr_req(int reg, char **args)
1172 struct cmd *req = str_dget(reg);
1173 if (req)
1174 req->f(args);
1177 /* read the next troff request; return zero if a request was executed. */
1178 int tr_nextreq(void)
1180 char *args[NARGS + 3] = {NULL};
1181 char *cmd;
1182 struct cmd *req;
1183 struct sbuf sbuf;
1184 int c;
1185 if (!tr_nl)
1186 return 1;
1187 c = cp_next();
1188 if (c == c_ec) {
1189 int c2 = cp_next();
1190 if (c2 == '!') {
1191 args[0] = "\\!";
1192 sbuf_init(&sbuf);
1193 cp_copymode(1);
1194 mkargs_eol(&sbuf);
1195 cp_copymode(0);
1196 tr_argschop(&sbuf, args + 1);
1197 tr_transparent(args);
1198 sbuf_done(&sbuf);
1199 return 0;
1201 cp_back(c2);
1203 if (c < 0 || (c != c_cc && c != c_c2 && (c != '\n' || tr_bm < 0))) {
1204 cp_back(c);
1205 return 1;
1207 cp_reqbeg();
1208 if (c != '\n') {
1209 cmd = read_name(n_cp);
1210 } else { /* blank line macro */
1211 cmd = malloc(strlen(map_name(tr_bm)) + 1);
1212 strcpy(cmd, map_name(tr_bm));
1213 cp_back(c);
1215 args[0] = dotted(cmd, c);
1216 req = str_dget(map(cmd));
1217 if (req) {
1218 sbuf_init(&sbuf);
1219 if (req->args)
1220 req->args(&sbuf);
1221 else
1222 mkargs_req(&sbuf);
1223 tr_argschop(&sbuf, args + 1);
1224 req->f(args);
1225 sbuf_done(&sbuf);
1226 } else {
1227 char *buf;
1228 cp_copymode(1);
1229 buf = tr_args(args + 1, -1, cp_next, cp_back);
1230 jmp_eol();
1231 cp_copymode(0);
1232 if (str_get(map(cmd)))
1233 in_push(str_get(map(cmd)), args);
1234 free(buf);
1236 free(args[0]);
1237 free(cmd);
1238 return 0;
1241 int tr_next(void)
1243 int c;
1244 while (!tr_nextreq())
1246 c = cp_next();
1247 tr_nl = c == '\n' || c < 0;
1248 return c;
1251 void tr_init(void)
1253 int i;
1254 for (i = 0; i < LEN(cmds); i++)
1255 str_dset(map(cmds[i].id), &cmds[i]);
1256 cmap = dict_make(-1, 0, 2);
1259 void tr_done(void)
1261 int i;
1262 for (i = 0; i < cdef_n; i++)
1263 free(cdef_dst[i]);
1264 dict_free(cmap);