font: specifying OpenType font language with .ffsc
[neatroff.git] / tr.c
blob2d1e365ad70f933c9c894af8565c92b69aa1df17
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 && args[2])
632 font_scrp(fn, args[2] ? args[2] : NULL);
633 if (fn && args[3])
634 font_lang(fn, args[3] ? args[3] : NULL);
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 *reg = args[1];
879 char *path = args[2];
880 char buf[1024];
881 if (!reg || !reg[0] || !path || !path[0])
882 return;
883 if (path[0] == '>') {
884 FILE *fp = fopen(path + 1, "w");
885 if (fp && reg && str_get(map(reg)))
886 fputs(str_get(map(reg)), fp);
887 if (fp)
888 fclose(fp);
890 if (path[0] == '<') {
891 FILE *fp = fopen(path + 1, "r");
892 struct sbuf sb;
893 sbuf_init(&sb);
894 while (fp && fgets(buf, sizeof(buf), fp))
895 sbuf_append(&sb, buf);
896 str_set(map(reg), sbuf_buf(&sb));
897 sbuf_done(&sb);
898 if (fp)
899 fclose(fp);
903 /* read a macro argument */
904 static int tr_arg(struct sbuf *sbuf, int brk, int (*next)(void), void (*back)(int))
906 int quoted = 0;
907 int c;
908 c = next();
909 while (c == ' ')
910 c = next();
911 if (c == '\n' || c == brk)
912 back(c);
913 if (c < 0 || c == '\n' || c == brk)
914 return 1;
915 if (c == '"') {
916 quoted = 1;
917 c = next();
919 while (c >= 0 && c != '\n' && (quoted || c != brk)) {
920 if (!quoted && c == ' ')
921 break;
922 if (quoted && c == '"') {
923 c = next();
924 if (c != '"')
925 break;
927 if (c == c_ec) {
928 sbuf_add(sbuf, c);
929 c = next();
931 sbuf_add(sbuf, c);
932 c = next();
934 sbuf_add(sbuf, 0);
935 if (c >= 0)
936 back(c);
937 return 0;
940 /* read macro arguments; free the returned pointer when done */
941 char *tr_args(char **args, int brk, int (*next)(void), void (*back)(int))
943 struct sbuf sbuf;
944 char *s, *e;
945 int n = 0;
946 sbuf_init(&sbuf);
947 while (!tr_arg(&sbuf, brk, next, back))
949 s = sbuf_buf(&sbuf);
950 e = s + sbuf_len(&sbuf);
951 while (n < NARGS && s && s < e) {
952 args[n++] = s;
953 if ((s = memchr(s, '\0', e - s)))
954 s++;
956 return sbuf_out(&sbuf);
959 /* split the arguments in sbuf */
960 static int tr_argschop(struct sbuf *sbuf, char **args)
962 char *s = sbuf_buf(sbuf);
963 char *e = s + sbuf_len(sbuf);
964 int n = 0;
965 while (n < NARGS && s && s < e) {
966 args[n++] = s;
967 if ((s = memchr(s, '\0', e - s)))
968 s++;
970 return n;
973 /* read request arguments; trims tabs too */
974 static void mkargs_req(struct sbuf *sbuf)
976 int n = 0;
977 int c;
978 c = cp_next();
979 while (n < NARGS) {
980 int ok = 0;
981 while (c == ' ' || c == '\t')
982 c = cp_next();
983 while (c >= 0 && c != '\n' && c != ' ' && c != '\t') {
984 if (c != c_ni)
985 sbuf_add(sbuf, c);
986 c = cp_next();
987 ok = 1;
989 if (ok) {
990 n++;
991 sbuf_add(sbuf, 0);
993 if (c == '\n')
994 cp_back(c);
995 if (c < 0 || c == '\n')
996 break;
998 jmp_eol();
1001 /* read arguments for .ds and .char */
1002 static void mkargs_ds(struct sbuf *sbuf)
1004 char *s = read_name(n_cp);
1005 sbuf_append(sbuf, s);
1006 sbuf_add(sbuf, 0);
1007 free(s);
1008 s = read_string();
1009 if (s) {
1010 sbuf_append(sbuf, s);
1011 sbuf_add(sbuf, 0);
1012 free(s);
1014 jmp_eol();
1017 /* read arguments for .ochar */
1018 static void mkargs_ochar(struct sbuf *sbuf)
1020 char *s = read_name(0);
1021 sbuf_append(sbuf, s);
1022 sbuf_add(sbuf, 0);
1023 free(s);
1024 mkargs_ds(sbuf);
1027 /* read arguments for .nr */
1028 static void mkargs_reg1(struct sbuf *sbuf)
1030 char *s = read_name(n_cp);
1031 sbuf_append(sbuf, s);
1032 sbuf_add(sbuf, 0);
1033 free(s);
1034 mkargs_req(sbuf);
1037 /* do not read any arguments; for .if, .ie and .el */
1038 static void mkargs_null(struct sbuf *sbuf)
1042 /* read the whole line for .tm */
1043 static void mkargs_eol(struct sbuf *sbuf)
1045 int c;
1046 cp_copymode(1);
1047 c = cp_next();
1048 while (c == ' ')
1049 c = cp_next();
1050 while (c >= 0 && c != '\n') {
1051 if (c != c_ni)
1052 sbuf_add(sbuf, c);
1053 c = cp_next();
1055 cp_copymode(0);
1058 static struct cmd {
1059 char *id;
1060 void (*f)(char **args);
1061 void (*args)(struct sbuf *sbuf);
1062 } cmds[] = {
1063 {TR_DIVBEG, tr_divbeg},
1064 {TR_DIVEND, tr_divend},
1065 {TR_DIVVS, tr_divvs},
1066 {TR_POPREN, tr_popren},
1067 {">>", tr_l2r},
1068 {"<<", tr_r2l},
1069 {"ab", tr_ab, mkargs_eol},
1070 {"ad", tr_ad},
1071 {"af", tr_af},
1072 {"am", tr_de, mkargs_reg1},
1073 {"as", tr_as, mkargs_ds},
1074 {"bd", tr_bd},
1075 {"blm", tr_blm},
1076 {"bp", tr_bp},
1077 {"br", tr_br},
1078 {"c2", tr_c2},
1079 {"cc", tr_cc},
1080 {"ce", tr_ce},
1081 {"ch", tr_ch},
1082 {"char", tr_char, mkargs_ds},
1083 {"chop", tr_chop, mkargs_reg1},
1084 {"cl", tr_cl},
1085 {"co", tr_co, mkargs_ds},
1086 {"cp", tr_cp},
1087 {"cs", tr_cs},
1088 {"da", tr_di},
1089 {"de", tr_de, mkargs_reg1},
1090 {"di", tr_di},
1091 {"ds", tr_ds, mkargs_ds},
1092 {"dt", tr_dt},
1093 {"ec", tr_ec},
1094 {"el", tr_el, mkargs_null},
1095 {"em", tr_em},
1096 {"eo", tr_eo},
1097 {"eos", tr_eos},
1098 {"ev", tr_ev},
1099 {"ex", tr_ex},
1100 {"fc", tr_fc},
1101 {"ff", tr_ff},
1102 {"fi", tr_fi},
1103 {"fl", tr_br},
1104 {"fmap", tr_fmap},
1105 {"fp", tr_fp},
1106 {"ffsc", tr_ffsc},
1107 {"fspecial", tr_fspecial},
1108 {"ft", tr_ft},
1109 {"fzoom", tr_fzoom},
1110 {"hc", tr_hc},
1111 {"hcode", tr_hcode},
1112 {"hlm", tr_hlm},
1113 {"hpf", tr_hpf},
1114 {"hpfa", tr_hpfa},
1115 {"hy", tr_hy},
1116 {"hycost", tr_hycost},
1117 {"hydash", tr_hydash},
1118 {"hystop", tr_hystop},
1119 {"hw", tr_hw},
1120 {"ie", tr_if, mkargs_null},
1121 {"if", tr_if, mkargs_null},
1122 {"ig", tr_ig},
1123 {"in", tr_in},
1124 {"in2", tr_in2},
1125 {"it", tr_it},
1126 {"kn", tr_kn},
1127 {"lc", tr_lc},
1128 {"lf", tr_lf},
1129 {"lg", tr_lg},
1130 {"ll", tr_ll},
1131 {"ls", tr_ls},
1132 {"lsm", tr_lsm},
1133 {"lt", tr_lt},
1134 {"mc", tr_mc},
1135 {"mk", tr_mk},
1136 {"na", tr_na},
1137 {"ne", tr_ne},
1138 {"nf", tr_nf},
1139 {"nh", tr_nh},
1140 {"nm", tr_nm},
1141 {"nn", tr_nn},
1142 {"nr", tr_nr, mkargs_reg1},
1143 {"ns", tr_ns},
1144 {"nx", tr_nx},
1145 {"ochar", tr_ochar, mkargs_ochar},
1146 {"os", tr_os},
1147 {"pc", tr_pc},
1148 {"pl", tr_pl},
1149 {"pmll", tr_pmll},
1150 {"pn", tr_pn},
1151 {"po", tr_po},
1152 {"ps", tr_ps},
1153 {"rchar", tr_rchar},
1154 {"rm", tr_rm},
1155 {"rn", tr_rn},
1156 {"rr", tr_rr},
1157 {"rs", tr_rs},
1158 {"rt", tr_rt},
1159 {"shift", tr_shift},
1160 {"so", tr_so},
1161 {"sp", tr_sp},
1162 {"ss", tr_ss},
1163 {"ssh", tr_ssh},
1164 {"sv", tr_sv},
1165 {"sy", tr_sy, mkargs_eol},
1166 {"ta", tr_ta},
1167 {"tc", tr_tc},
1168 {"ti", tr_ti},
1169 {"ti2", tr_ti2},
1170 {"tkf", tr_tkf},
1171 {"tl", tr_tl, mkargs_null},
1172 {"tm", tr_tm, mkargs_eol},
1173 {"tr", tr_tr, mkargs_eol},
1174 {"vs", tr_vs},
1175 {"wh", tr_wh},
1178 static char *dotted(char *name, int dot)
1180 char *out = xmalloc(strlen(name) + 2);
1181 out[0] = dot;
1182 strcpy(out + 1, name);
1183 return out;
1186 /* execute a built-in request */
1187 void tr_req(int reg, char **args)
1189 struct cmd *req = str_dget(reg);
1190 if (req)
1191 req->f(args);
1194 /* interpolate a macro for tr_nextreq() */
1195 static void tr_nextreq_exec(char *mac, char *arg0, int readargs)
1197 char *args[NARGS + 3] = {arg0};
1198 struct cmd *req = str_dget(map(mac));
1199 struct sbuf sbuf;
1200 if (req) {
1201 sbuf_init(&sbuf);
1202 if (readargs) {
1203 if (req->args)
1204 req->args(&sbuf);
1205 else
1206 mkargs_req(&sbuf);
1207 tr_argschop(&sbuf, args + 1);
1209 req->f(args);
1210 sbuf_done(&sbuf);
1211 } else {
1212 char *buf = NULL;
1213 if (readargs) {
1214 cp_copymode(1);
1215 buf = tr_args(args + 1, -1, cp_next, cp_back);
1216 jmp_eol();
1217 cp_copymode(0);
1219 if (str_get(map(mac)))
1220 in_push(str_get(map(mac)), args);
1221 free(buf);
1225 /* read the next troff request; return zero if a request was executed. */
1226 int tr_nextreq(void)
1228 char *mac;
1229 char *arg0 = NULL;
1230 int c;
1231 if (!tr_nl)
1232 return 1;
1233 c = cp_next();
1234 /* transparent line indicator */
1235 if (c == c_ec) {
1236 int c2 = cp_next();
1237 if (c2 == '!') {
1238 char *args[NARGS + 3] = {"\\!"};
1239 struct sbuf sbuf;
1240 sbuf_init(&sbuf);
1241 cp_copymode(1);
1242 mkargs_eol(&sbuf);
1243 cp_copymode(0);
1244 tr_argschop(&sbuf, args + 1);
1245 tr_transparent(args);
1246 sbuf_done(&sbuf);
1247 return 0;
1249 cp_back(c2);
1251 /* not a request, a blank line, or a line with leading spaces */
1252 if (c < 0 || (c != c_cc && c != c_c2 &&
1253 (c != '\n' || tr_bm < 0) &&
1254 (c != ' ' || tr_sm < 0))) {
1255 cp_back(c);
1256 return 1;
1258 cp_reqbeg();
1259 if (c == '\n') { /* blank line macro */
1260 mac = malloc(strlen(map_name(tr_bm)) + 1);
1261 strcpy(mac, map_name(tr_bm));
1262 arg0 = dotted(mac, '.');
1263 tr_nextreq_exec(mac, arg0, 0);
1264 } else if (c == ' ') { /* leading space macro */
1265 int i;
1266 mac = malloc(strlen(map_name(tr_sm)) + 1);
1267 strcpy(mac, map_name(tr_sm));
1268 for (i = 0; c == ' '; i++)
1269 c = cp_next();
1270 cp_back(c);
1271 n_lsn = i;
1272 arg0 = dotted(mac, '.');
1273 tr_nextreq_exec(mac, arg0, 0);
1274 } else {
1275 mac = read_name(n_cp);
1276 arg0 = dotted(mac, c);
1277 tr_nextreq_exec(mac, arg0, 1);
1279 free(arg0);
1280 free(mac);
1281 return 0;
1284 int tr_next(void)
1286 int c;
1287 while (!tr_nextreq())
1289 c = cp_next();
1290 tr_nl = c == '\n' || c < 0;
1291 return c;
1294 void tr_init(void)
1296 int i;
1297 for (i = 0; i < LEN(cmds); i++)
1298 str_dset(map(cmds[i].id), &cmds[i]);
1299 cmap = dict_make(-1, 0, 2);
1302 void tr_done(void)
1304 int i;
1305 for (i = 0; i < cdef_n; i++)
1306 free(cdef_dst[i]);
1307 dict_free(cmap);