font: glyph cursive attachments for r2l scripts
[neatroff.git] / tr.c
blob95224a1a63f4cad6560cbd53c38f368b8ba6d662
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 /* read a macro argument */
862 static int tr_arg(struct sbuf *sbuf, int brk, int (*next)(void), void (*back)(int))
864 int quoted = 0;
865 int c;
866 c = next();
867 while (c == ' ')
868 c = next();
869 if (c == '\n' || c == brk)
870 back(c);
871 if (c < 0 || c == '\n' || c == brk)
872 return 1;
873 if (c == '"') {
874 quoted = 1;
875 c = next();
877 while (c >= 0 && c != '\n' && (quoted || c != brk)) {
878 if (!quoted && c == ' ')
879 break;
880 if (quoted && c == '"') {
881 c = next();
882 if (c != '"')
883 break;
885 if (c == c_ec) {
886 sbuf_add(sbuf, c);
887 c = next();
889 sbuf_add(sbuf, c);
890 c = next();
892 sbuf_add(sbuf, 0);
893 if (c >= 0)
894 back(c);
895 return 0;
898 /* read macro arguments; free the returned pointer when done */
899 char *tr_args(char **args, int brk, int (*next)(void), void (*back)(int))
901 struct sbuf sbuf;
902 char *s, *e;
903 int n = 0;
904 sbuf_init(&sbuf);
905 while (!tr_arg(&sbuf, brk, next, back))
907 s = sbuf_buf(&sbuf);
908 e = s + sbuf_len(&sbuf);
909 while (n < NARGS && s && s < e) {
910 args[n++] = s;
911 if ((s = memchr(s, '\0', e - s)))
912 s++;
914 return sbuf_out(&sbuf);
917 /* split the arguments in sbuf */
918 static int tr_argschop(struct sbuf *sbuf, char **args)
920 char *s = sbuf_buf(sbuf);
921 char *e = s + sbuf_len(sbuf);
922 int n = 0;
923 while (n < NARGS && s && s < e) {
924 args[n++] = s;
925 if ((s = memchr(s, '\0', e - s)))
926 s++;
928 return n;
931 /* read request arguments; trims tabs too */
932 static void mkargs_req(struct sbuf *sbuf)
934 int n = 0;
935 int c;
936 c = cp_next();
937 while (n < NARGS) {
938 int ok = 0;
939 while (c == ' ' || c == '\t')
940 c = cp_next();
941 while (c >= 0 && c != '\n' && c != ' ' && c != '\t') {
942 if (c != c_ni)
943 sbuf_add(sbuf, c);
944 c = cp_next();
945 ok = 1;
947 if (ok) {
948 n++;
949 sbuf_add(sbuf, 0);
951 if (c == '\n')
952 cp_back(c);
953 if (c < 0 || c == '\n')
954 break;
956 jmp_eol();
959 /* read arguments for .ds and .char */
960 static void mkargs_ds(struct sbuf *sbuf)
962 char *s = read_name(n_cp);
963 sbuf_append(sbuf, s);
964 sbuf_add(sbuf, 0);
965 free(s);
966 s = read_string();
967 if (s) {
968 sbuf_append(sbuf, s);
969 sbuf_add(sbuf, 0);
970 free(s);
972 jmp_eol();
975 /* read arguments for .ochar */
976 static void mkargs_ochar(struct sbuf *sbuf)
978 char *s = read_name(0);
979 sbuf_append(sbuf, s);
980 sbuf_add(sbuf, 0);
981 free(s);
982 mkargs_ds(sbuf);
985 /* read arguments for .nr */
986 static void mkargs_reg1(struct sbuf *sbuf)
988 char *s = read_name(n_cp);
989 sbuf_append(sbuf, s);
990 sbuf_add(sbuf, 0);
991 free(s);
992 mkargs_req(sbuf);
995 /* do not read any arguments; for .if, .ie and .el */
996 static void mkargs_null(struct sbuf *sbuf)
1000 /* read the whole line for .tm */
1001 static void mkargs_eol(struct sbuf *sbuf)
1003 int c;
1004 cp_copymode(1);
1005 c = cp_next();
1006 while (c == ' ')
1007 c = cp_next();
1008 while (c >= 0 && c != '\n') {
1009 if (c != c_ni)
1010 sbuf_add(sbuf, c);
1011 c = cp_next();
1013 cp_copymode(0);
1016 static struct cmd {
1017 char *id;
1018 void (*f)(char **args);
1019 void (*args)(struct sbuf *sbuf);
1020 } cmds[] = {
1021 {TR_DIVBEG, tr_divbeg},
1022 {TR_DIVEND, tr_divend},
1023 {TR_DIVVS, tr_divvs},
1024 {TR_POPREN, tr_popren},
1025 {">>", tr_l2r},
1026 {"<<", tr_r2l},
1027 {"ab", tr_ab, mkargs_eol},
1028 {"ad", tr_ad},
1029 {"af", tr_af},
1030 {"am", tr_de, mkargs_reg1},
1031 {"as", tr_as, mkargs_ds},
1032 {"bd", tr_bd},
1033 {"blm", tr_blm},
1034 {"bp", tr_bp},
1035 {"br", tr_br},
1036 {"c2", tr_c2},
1037 {"cc", tr_cc},
1038 {"ochar", tr_ochar, mkargs_ochar},
1039 {"ce", tr_ce},
1040 {"ch", tr_ch},
1041 {"char", tr_char, mkargs_ds},
1042 {"chop", tr_chop, mkargs_reg1},
1043 {"cl", tr_cl},
1044 {"cp", tr_cp},
1045 {"cs", tr_cs},
1046 {"da", tr_di},
1047 {"de", tr_de, mkargs_reg1},
1048 {"di", tr_di},
1049 {"ds", tr_ds, mkargs_ds},
1050 {"dt", tr_dt},
1051 {"ec", tr_ec},
1052 {"el", tr_el, mkargs_null},
1053 {"em", tr_em},
1054 {"eo", tr_eo},
1055 {"eos", tr_eos},
1056 {"ev", tr_ev},
1057 {"ex", tr_ex},
1058 {"fc", tr_fc},
1059 {"ff", tr_ff},
1060 {"fi", tr_fi},
1061 {"fl", tr_br},
1062 {"fmap", tr_fmap},
1063 {"fp", tr_fp},
1064 {"ffsc", tr_ffsc},
1065 {"fspecial", tr_fspecial},
1066 {"ft", tr_ft},
1067 {"fzoom", tr_fzoom},
1068 {"hc", tr_hc},
1069 {"hcode", tr_hcode},
1070 {"hlm", tr_hlm},
1071 {"hpf", tr_hpf},
1072 {"hpfa", tr_hpfa},
1073 {"hy", tr_hy},
1074 {"hycost", tr_hycost},
1075 {"hydash", tr_hydash},
1076 {"hystop", tr_hystop},
1077 {"hw", tr_hw},
1078 {"ie", tr_if, mkargs_null},
1079 {"if", tr_if, mkargs_null},
1080 {"ig", tr_ig},
1081 {"in", tr_in},
1082 {"in2", tr_in2},
1083 {"it", tr_it},
1084 {"kn", tr_kn},
1085 {"lc", tr_lc},
1086 {"lf", tr_lf},
1087 {"lg", tr_lg},
1088 {"ll", tr_ll},
1089 {"ls", tr_ls},
1090 {"lt", tr_lt},
1091 {"mc", tr_mc},
1092 {"mk", tr_mk},
1093 {"na", tr_na},
1094 {"ne", tr_ne},
1095 {"nf", tr_nf},
1096 {"nh", tr_nh},
1097 {"nm", tr_nm},
1098 {"nn", tr_nn},
1099 {"nr", tr_nr, mkargs_reg1},
1100 {"ns", tr_ns},
1101 {"nx", tr_nx},
1102 {"os", tr_os},
1103 {"pc", tr_pc},
1104 {"pl", tr_pl},
1105 {"pmll", tr_pmll},
1106 {"pn", tr_pn},
1107 {"po", tr_po},
1108 {"ps", tr_ps},
1109 {"rchar", tr_rchar},
1110 {"rm", tr_rm},
1111 {"rn", tr_rn},
1112 {"rr", tr_rr},
1113 {"rs", tr_rs},
1114 {"rt", tr_rt},
1115 {"so", tr_so},
1116 {"sp", tr_sp},
1117 {"ss", tr_ss},
1118 {"ssh", tr_ssh},
1119 {"sv", tr_sv},
1120 {"sy", tr_sy, mkargs_eol},
1121 {"ta", tr_ta},
1122 {"tc", tr_tc},
1123 {"ti", tr_ti},
1124 {"ti2", tr_ti2},
1125 {"tkf", tr_tkf},
1126 {"tl", tr_tl, mkargs_null},
1127 {"tm", tr_tm, mkargs_eol},
1128 {"tr", tr_tr, mkargs_eol},
1129 {"vs", tr_vs},
1130 {"wh", tr_wh},
1133 static char *dotted(char *name, int dot)
1135 char *out = xmalloc(strlen(name) + 2);
1136 out[0] = dot;
1137 strcpy(out + 1, name);
1138 return out;
1141 /* execute a built-in request */
1142 void tr_req(int reg, char **args)
1144 struct cmd *req = str_dget(reg);
1145 if (req)
1146 req->f(args);
1149 /* read the next troff request; return zero if a request was executed. */
1150 int tr_nextreq(void)
1152 char *args[NARGS + 3] = {NULL};
1153 char *cmd;
1154 struct cmd *req;
1155 struct sbuf sbuf;
1156 int c;
1157 if (!tr_nl)
1158 return 1;
1159 c = cp_next();
1160 if (c == c_ec) {
1161 int c2 = cp_next();
1162 if (c2 == '!') {
1163 args[0] = "\\!";
1164 sbuf_init(&sbuf);
1165 cp_copymode(1);
1166 mkargs_eol(&sbuf);
1167 cp_copymode(0);
1168 tr_argschop(&sbuf, args + 1);
1169 tr_transparent(args);
1170 sbuf_done(&sbuf);
1171 return 0;
1173 cp_back(c2);
1175 if (c < 0 || (c != c_cc && c != c_c2 && (c != '\n' || tr_bm < 0))) {
1176 cp_back(c);
1177 return 1;
1179 cp_reqbeg();
1180 if (c != '\n') {
1181 cmd = read_name(n_cp);
1182 } else { /* blank line macro */
1183 cmd = malloc(strlen(map_name(tr_bm)) + 1);
1184 strcpy(cmd, map_name(tr_bm));
1185 cp_back(c);
1187 args[0] = dotted(cmd, c);
1188 req = str_dget(map(cmd));
1189 if (req) {
1190 sbuf_init(&sbuf);
1191 if (req->args)
1192 req->args(&sbuf);
1193 else
1194 mkargs_req(&sbuf);
1195 tr_argschop(&sbuf, args + 1);
1196 req->f(args);
1197 sbuf_done(&sbuf);
1198 } else {
1199 char *buf;
1200 cp_copymode(1);
1201 buf = tr_args(args + 1, -1, cp_next, cp_back);
1202 jmp_eol();
1203 cp_copymode(0);
1204 if (str_get(map(cmd)))
1205 in_push(str_get(map(cmd)), args + 1);
1206 free(buf);
1208 free(args[0]);
1209 free(cmd);
1210 return 0;
1213 int tr_next(void)
1215 int c;
1216 while (!tr_nextreq())
1218 c = cp_next();
1219 tr_nl = c == '\n' || c < 0;
1220 return c;
1223 void tr_init(void)
1225 int i;
1226 for (i = 0; i < LEN(cmds); i++)
1227 str_dset(map(cmds[i].id), &cmds[i]);
1228 cmap = dict_make(-1, 0, 0);
1231 void tr_done(void)
1233 int i;
1234 for (i = 0; i < cdef_n; i++)
1235 free(cdef_dst[i]);
1236 dict_free(cmap);