fmt: new hyphenation support with penalties
[neatroff.git] / tr.c
blob51139a0f9df3be1bdfaab372369e207e86ccfc21
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 c_pc = '%'; /* page number character */
10 int c_ec = '\\';
11 int c_cc = '.';
12 int c_c2 = '\'';
14 /* skip everything until the end of line */
15 static void jmp_eol(void)
17 int c;
18 do {
19 c = cp_next();
20 } while (c >= 0 && c != '\n');
23 static void tr_vs(char **args)
25 int vs = args[1] ? eval_re(args[1], n_v, 'p') : n_v0;
26 n_v0 = n_v;
27 n_v = MAX(0, vs);
30 static void tr_ls(char **args)
32 int ls = args[1] ? eval_re(args[1], n_L, 0) : n_L0;
33 n_L0 = n_L;
34 n_L = MAX(1, ls);
37 static void tr_pl(char **args)
39 int n = eval_re(args[1] ? args[1] : "11i", n_p, 'v');
40 n_p = MAX(0, n);
43 static void tr_nr(char **args)
45 int id;
46 if (!args[2])
47 return;
48 id = map(args[1]);
49 num_set(id, eval_re(args[2], num_get(id, 0), 'u'));
50 num_inc(id, args[3] ? eval(args[3], 'u') : 0);
53 static void tr_rr(char **args)
55 int i;
56 for (i = 1; i <= NARGS; i++)
57 if (args[i])
58 num_del(map(args[i]));
61 static void tr_af(char **args)
63 if (args[2])
64 num_setfmt(map(args[1]), args[2]);
67 static void tr_ds(char **args)
69 if (args[2])
70 str_set(map(args[1]), args[2]);
73 static void tr_as(char **args)
75 int reg;
76 char *s1, *s2, *s;
77 if (!args[2])
78 return;
79 reg = map(args[1]);
80 s1 = str_get(reg) ? str_get(reg) : "";
81 s2 = args[2];
82 s = malloc(strlen(s1) + strlen(s2) + 1);
83 strcpy(s, s1);
84 strcat(s, s2);
85 str_set(reg, s);
86 free(s);
89 static void tr_rm(char **args)
91 int i;
92 for (i = 1; i <= NARGS; i++)
93 if (args[i])
94 str_rm(map(args[i]));
97 static void tr_rn(char **args)
99 if (!args[2])
100 return;
101 str_rn(map(args[1]), map(args[2]));
104 static void tr_po(char **args)
106 int po = args[1] ? eval_re(args[1], n_o, 'm') : n_o0;
107 n_o0 = n_o;
108 n_o = MAX(0, po);
111 static char *arg_regname(char *s, int len);
113 static void macrobody(struct sbuf *sbuf, char *end)
115 char buf[NMLEN];
116 int i, c;
117 int first = 1;
118 cp_back('\n');
119 cp_wid(0); /* copy-mode; disable \w handling */
120 while ((c = cp_next()) >= 0) {
121 if (sbuf && !first)
122 sbuf_add(sbuf, c);
123 first = 0;
124 if (c == '\n') {
125 c = cp_next();
126 if (c == '.') {
127 arg_regname(buf, sizeof(buf));
128 if ((n_cp && end[0] == buf[0] && end[1] == buf[1]) ||
129 !strcmp(end, buf)) {
130 jmp_eol();
131 break;
133 if (!sbuf)
134 continue;
135 sbuf_add(sbuf, '.');
136 for (i = 0; buf[i]; i++)
137 sbuf_add(sbuf, (unsigned char) buf[i]);
138 continue;
140 if (sbuf && c >= 0)
141 sbuf_add(sbuf, c);
144 cp_wid(1);
147 static void tr_de(char **args)
149 struct sbuf sbuf;
150 int id;
151 if (!args[1])
152 return;
153 id = map(args[1]);
154 sbuf_init(&sbuf);
155 if (args[0][1] == 'a' && args[0][2] == 'm' && str_get(id))
156 sbuf_append(&sbuf, str_get(id));
157 macrobody(&sbuf, args[2] ? args[2] : ".");
158 str_set(id, sbuf_buf(&sbuf));
159 sbuf_done(&sbuf);
162 static void tr_ig(char **args)
164 macrobody(NULL, args[1] ? args[1] : ".");
167 /* read into sbuf until stop; if stop is NULL, stop at whitespace */
168 static int read_until(struct sbuf *sbuf, char *stop)
170 char cs[GNLEN], cs2[GNLEN];
171 int c;
172 while ((c = cp_next()) >= 0) {
173 cp_back(c);
174 if (c == '\n')
175 return 1;
176 if (!stop && (c == ' ' || c == '\t'))
177 return 0;
178 charnext(cs, cp_next, cp_back);
179 if (stop && !strcmp(stop, cs))
180 return 0;
181 charnext_str(cs2, cs);
182 sbuf_append(sbuf, cs2);
184 return 1;
187 /* evaluate .if strcmp (i.e. 'str'str') */
188 static int if_strcmp(void)
190 char delim[GNLEN];
191 struct sbuf s1, s2;
192 int ret;
193 charnext(delim, cp_next, cp_back);
194 sbuf_init(&s1);
195 sbuf_init(&s2);
196 read_until(&s1, delim);
197 read_until(&s2, delim);
198 ret = !strcmp(sbuf_buf(&s1), sbuf_buf(&s2));
199 sbuf_done(&s1);
200 sbuf_done(&s2);
201 return ret;
204 /* evaluate .if condition letters */
205 static int if_cond(void)
207 switch (cp_next()) {
208 case 'o':
209 return n_pg % 2;
210 case 'e':
211 return !(n_pg % 2);
212 case 't':
213 return 1;
214 case 'n':
215 return 0;
217 return 0;
220 /* evaluate .if condition */
221 static int if_eval(void)
223 struct sbuf sbuf;
224 int ret;
225 sbuf_init(&sbuf);
226 if (!read_until(&sbuf, NULL))
227 cp_back(' ');
228 ret = eval(sbuf_buf(&sbuf), '\0') > 0;
229 sbuf_done(&sbuf);
230 return ret;
233 static int ie_cond[NIES]; /* .ie condition stack */
234 static int ie_depth;
236 static void tr_if(char **args)
238 int neg = 0;
239 int ret;
240 int c;
241 do {
242 c = cp_next();
243 } while (c == ' ' || c == '\t');
244 if (c == '!') {
245 neg = 1;
246 c = cp_next();
248 cp_back(c);
249 if (strchr("oetn", c)) {
250 ret = if_cond();
251 } else if (!isdigit(c) && !strchr("-+*/%<=>&:.|()", c)) {
252 ret = if_strcmp();
253 } else {
254 ret = if_eval();
256 if (args[0][1] == 'i' && args[0][2] == 'e') /* .ie command */
257 if (ie_depth < NIES)
258 ie_cond[ie_depth++] = ret != neg;
259 cp_blk(ret == neg);
262 static void tr_el(char **args)
264 cp_blk(ie_depth > 0 ? ie_cond[--ie_depth] : 1);
267 static void tr_na(char **args)
269 n_na = 1;
272 static int adjmode(int c, int def)
274 switch (c) {
275 case 'l':
276 return AD_L;
277 case 'r':
278 return AD_R;
279 case 'c':
280 return AD_C;
281 case 'b':
282 case 'n':
283 return AD_B;
285 return def;
288 static void tr_ad(char **args)
290 char *s = args[1];
291 n_na = 0;
292 if (!s)
293 return;
294 if (isdigit(s[0]))
295 n_j = atoi(s) & 15;
296 else
297 n_j = s[0] == 'p' ? AD_P | adjmode(s[1], AD_B) : adjmode(s[0], n_j);
300 static void tr_tm(char **args)
302 fprintf(stderr, "%s\n", args[1]);
305 static void tr_so(char **args)
307 if (args[1])
308 in_so(args[1]);
311 static void tr_nx(char **args)
313 in_nx(args[1]);
316 static void tr_ex(char **args)
318 in_ex();
321 static void tr_sy(char **args)
323 system(args[1]);
326 static void tr_lt(char **args)
328 int lt = args[1] ? eval_re(args[1], n_lt, 'm') : n_t0;
329 n_t0 = n_t0;
330 n_lt = MAX(0, lt);
333 static void tr_pc(char **args)
335 c_pc = args[1] ? args[1][0] : -1;
338 static int tl_next(void)
340 int c = cp_next();
341 if (c >= 0 && c == c_pc) {
342 in_push(num_str(map("%")), NULL);
343 c = cp_next();
345 return c;
348 static void tr_tl(char **args)
350 int c;
351 do {
352 c = cp_next();
353 } while (c >= 0 && (c == ' ' || c == '\t'));
354 cp_back(c);
355 ren_tl(tl_next, cp_back);
356 do {
357 c = cp_next();
358 } while (c >= 0 && c != '\n');
361 static void tr_ec(char **args)
363 c_ec = args[1] ? args[1][0] : '\\';
366 static void tr_cc(char **args)
368 c_ec = args[1] ? args[1][0] : '.';
371 static void tr_c2(char **args)
373 c_ec = args[1] ? args[1][0] : '\'';
376 static void tr_eo(char **args)
378 c_ec = -1;
381 static void tr_hc(char **args)
383 char *s = args[1];
384 if (!s || charread(&s, c_hc) < 0)
385 strcpy(c_hc, "\\%");
388 static void tr_nh(char **args)
390 n_hy = 0;
393 static void tr_hy(char **args)
395 n_hy = args[1] ? atoi(args[1]) : 1;
398 static void tr_hyp(char **args)
400 n_hyp = args[1] ? atoi(args[1]) : 1;
403 static void tr_lg(char **args)
405 if (args[1])
406 n_lg = atoi(args[1]);
409 static void tr_kn(char **args)
411 if (args[1])
412 n_kn = atoi(args[1]);
415 static void tr_cp(char **args)
417 if (args[1])
418 n_cp = atoi(args[1]);
421 static void tr_ss(char **args)
423 if (args[1])
424 n_ss = eval_re(args[1], n_ss, 0);
427 static void tr_cs(char **args)
429 if (!args[1])
430 return;
431 dev_setcs(dev_pos(args[1]), args[2] ? eval(args[2], 0) : 0);
434 static void tr_nm(char **args)
436 if (!args[1]) {
437 n_nm = 0;
438 return;
440 n_nm = 1;
441 n_ln = eval_re(args[1], n_ln, 0);
442 n_ln = MAX(0, n_ln);
443 if (args[2] && isdigit(args[2][0]))
444 n_nM = MAX(1, eval(args[2], 0));
445 if (args[3] && isdigit(args[3][0]))
446 n_nS = MAX(0, eval(args[3], 0));
447 if (args[4] && isdigit(args[4][0]))
448 n_nI = MAX(0, eval(args[4], 0));
451 static void tr_nn(char **args)
453 n_nn = args[1] ? eval(args[1], 0) : 1;
456 static void tr_bd(char **args)
458 if (!args[1] || !strcmp("S", args[1]))
459 return;
460 dev_setbd(dev_pos(args[1]), args[2] ? eval(args[2], 'u') : 0);
463 static void tr_it(char **args)
465 if (args[2]) {
466 n_it = map(args[2]);
467 n_itn = eval(args[1], 0);
468 } else {
469 n_it = 0;
473 static void tr_mc(char **args)
475 char *s = args[1];
476 if (s && charread(&s, c_mc) >= 0) {
477 n_mc = 1;
478 n_mcn = args[2] ? eval(args[2], 'm') : SC_EM;
479 } else {
480 n_mc = 0;
484 static void tr_tc(char **args)
486 char *s = args[1];
487 if (!s || charread(&s, c_tc) < 0)
488 strcpy(c_tc, "");
491 static void tr_lc(char **args)
493 char *s = args[1];
494 if (!s || charread(&s, c_lc) < 0)
495 strcpy(c_lc, "");
498 static void tr_lf(char **args)
500 if (args[1])
501 in_lf(args[2], eval(args[1], 0));
504 static void tr_chop(char **args)
506 struct sbuf sbuf;
507 int id;
508 if (args[1])
509 in_lf(args[2], eval(args[1], 0));
510 id = map(args[1]);
511 if (str_get(id)) {
512 sbuf_init(&sbuf);
513 sbuf_append(&sbuf, str_get(id));
514 if (!sbuf_empty(&sbuf)) {
515 sbuf_cut(&sbuf, sbuf_len(&sbuf) - 1);
516 str_set(id, sbuf_buf(&sbuf));
518 sbuf_done(&sbuf);
522 /* character translation (.tr) */
523 static char cmap_src[NCMAPS][GNLEN]; /* source character */
524 static char cmap_dst[NCMAPS][GNLEN]; /* character mapping */
525 static int cmap_n; /* number of translated character */
527 static int tr_find(char *c)
529 int i;
530 for (i = 0; i < cmap_n; i++)
531 if (!strcmp(c, cmap_src[i]))
532 return i;
533 return -1;
536 void cmap_add(char *c1, char *c2)
538 int i = tr_find(c1);
539 if (i < 0 && cmap_n < NCMAPS)
540 i = cmap_n++;
541 if (i >= 0) {
542 strcpy(cmap_src[i], c1);
543 strcpy(cmap_dst[i], c2);
547 char *cmap_map(char *c)
549 int i = tr_find(c);
550 return i >= 0 ? cmap_dst[i] : c;
553 static void tr_tr(char **args)
555 char *s = args[1];
556 char c1[GNLEN], c2[GNLEN];
557 while (s && charread(&s, c1) >= 0) {
558 if (charread(&s, c2) < 0)
559 strcpy(c2, " ");
560 cmap_add(c1, c2);
564 /* character definition (.char) */
565 static char cdef_src[NCDEFS][GNLEN]; /* source character */
566 static char *cdef_dst[NCDEFS]; /* character definition */
567 static int cdef_fn[NCDEFS]; /* owning font */
568 static int cdef_n; /* number of defined characters */
569 static int cdef_expanding; /* inside cdef_expand() call */
571 static int cdef_find(char *c, int fn)
573 int i;
574 for (i = 0; i < cdef_n; i++)
575 if (!strcmp(cdef_src[i], c) && (!cdef_fn[i] || cdef_fn[i] == fn))
576 return i;
577 return -1;
580 /* return the definition of the given character */
581 char *cdef_map(char *c, int fn)
583 int i = cdef_find(c, fn);
584 return !cdef_expanding && i >= 0 ? cdef_dst[i] : NULL;
587 int cdef_expand(struct wb *wb, char *s, int fn)
589 char *d = cdef_map(s, fn);
590 if (!d)
591 return 1;
592 cdef_expanding = 1;
593 ren_parse(wb, d);
594 cdef_expanding = 0;
595 return 0;
598 static void cdef_add(char *fn, char *cs, char *def)
600 char c[GNLEN];
601 int i;
602 if (!def || charread(&cs, c) < 0)
603 return;
604 i = cdef_find(c, -1);
605 if (i < 0 && cdef_n < NCDEFS)
606 i = cdef_n++;
607 if (i >= 0) {
608 strncpy(cdef_src[i], c, sizeof(cdef_src[i]) - 1);
609 cdef_dst[i] = malloc(strlen(def) + 1);
610 strcpy(cdef_dst[i], def);
611 cdef_fn[i] = fn ? dev_pos(fn) : 0;
615 static void cdef_remove(char *cs)
617 char c[GNLEN];
618 int i;
619 if (!cs || charread(&cs, c) < 0)
620 return;
621 for (i = 0; i < cdef_n; i++) {
622 if (!strcmp(cdef_src[i], c)) {
623 free(cdef_dst[i]);
624 cdef_dst[i] = NULL;
625 cdef_src[i][0] = '\0';
630 static void tr_char(char **args)
632 cdef_add(NULL, args[1], args[2]);
635 static void tr_rchar(char **args)
637 int i;
638 for (i = 1; i <= NARGS; i++)
639 if (args[i])
640 cdef_remove(args[i]);
643 static void tr_ochar(char **args)
645 cdef_add(args[1], args[2], args[3]);
648 static void tr_fmap(char **args)
650 struct font *fn;
651 if (!args[2])
652 return;
653 fn = dev_font(dev_pos(args[1]));
654 if (fn)
655 font_map(fn, args[2], args[3] ? font_glyph(fn, args[3]) : NULL);
658 static char *arg_regname(char *s, int len)
660 char *e = n_cp ? s + 2 : s + len;
661 int c = cp_next();
662 while (c == ' ' || c == '\t')
663 c = cp_next();
664 while (s < e && c >= 0 && c != ' ' && c != '\t' && c != '\n') {
665 *s++ = c;
666 c = cp_next();
668 if (c >= 0)
669 cp_back(c);
670 *s++ = '\0';
671 return s;
674 static char *arg_normal(char *s, int len)
676 char *e = s + len - 1;
677 int quoted = 0;
678 int c;
679 c = cp_next();
680 while (c == ' ')
681 c = cp_next();
682 if (c == '"') {
683 quoted = 1;
684 c = cp_next();
686 while (s < e && c > 0 && c != '\n') {
687 if (!quoted && c == ' ')
688 break;
689 if (quoted && c == '"') {
690 c = cp_next();
691 if (c != '"')
692 break;
694 *s++ = c;
695 c = cp_next();
697 if (c >= 0)
698 cp_back(c);
699 *s++ = '\0';
700 return s;
703 static char *arg_string(char *s, int len)
705 char *e = s + len - 1;
706 int c;
707 while ((c = cp_next()) == ' ')
709 if (c == '"')
710 c = cp_next();
711 while (s < e && c > 0 && c != '\n') {
712 *s++ = c;
713 c = cp_next();
715 *s++ = '\0';
716 if (c >= 0)
717 cp_back(c);
718 return s;
721 /* read macro arguments; trims tabs if rmtabs is nonzero */
722 static int mkargs(char **args, char *buf, int len)
724 char *s = buf;
725 char *e = buf + len - 1;
726 int c;
727 int n = 0;
728 while (n < NARGS) {
729 char *r = s;
730 c = cp_next();
731 if (c < 0 || c == '\n')
732 return n;
733 cp_back(c);
734 s = arg_normal(s, e - s);
735 if (*r != '\0')
736 args[n++] = r;
738 jmp_eol();
739 return n;
742 /* read request arguments; trims tabs too */
743 static int mkargs_req(char **args, char *buf, int len)
745 char *r, *s = buf;
746 char *e = buf + len - 1;
747 int c;
748 int n = 0;
749 c = cp_next();
750 while (n < NARGS && s < e) {
751 r = s;
752 while (c == ' ' || c == '\t')
753 c = cp_next();
754 while (c >= 0 && c != '\n' && c != ' ' && c != '\t' && s < e) {
755 *s++ = c;
756 c = cp_next();
758 *s++ = '\0';
759 if (*r != '\0')
760 args[n++] = r;
761 if (c < 0 || c == '\n')
762 return n;
764 jmp_eol();
765 return n;
768 /* read arguments for .ds */
769 static int mkargs_ds(char **args, char *buf, int len)
771 char *s = buf;
772 char *e = buf + len - 1;
773 int c;
774 args[0] = s;
775 s = arg_regname(s, e - s);
776 args[1] = s;
777 cp_wid(0);
778 s = arg_string(s, e - s);
779 cp_wid(1);
780 c = cp_next();
781 if (c >= 0 && c != '\n')
782 jmp_eol();
783 return 2;
786 /* read arguments for commands .nr that expect a register name */
787 static int mkargs_reg1(char **args, char *buf, int len)
789 char *s = buf;
790 char *e = buf + len - 1;
791 args[0] = s;
792 s = arg_regname(s, e - s);
793 return mkargs_req(args + 1, s, e - s) + 1;
796 /* do not read arguments; for .if, .ie and .el */
797 static int mkargs_null(char **args, char *buf, int len)
799 return 0;
802 /* read the whole line for .tm */
803 static int mkargs_eol(char **args, char *buf, int len)
805 char *s = buf;
806 char *e = buf + len - 1;
807 int c;
808 args[0] = s;
809 c = cp_next();
810 while (c == ' ')
811 c = cp_next();
812 while (s < e && c >= 0 && c != '\n') {
813 *s++ = c;
814 c = cp_next();
816 *s = '\0';
817 return 1;
820 static struct cmd {
821 char *id;
822 void (*f)(char **args);
823 int (*args)(char **args, char *buf, int len);
824 } cmds[] = {
825 {TR_DIVBEG, tr_divbeg},
826 {TR_DIVEND, tr_divend},
827 {TR_POPREN, tr_popren},
828 {"ab", tr_ab, mkargs_eol},
829 {"ad", tr_ad},
830 {"af", tr_af},
831 {"am", tr_de, mkargs_reg1},
832 {"as", tr_as, mkargs_ds},
833 {"bd", tr_bd},
834 {"bp", tr_bp},
835 {"br", tr_br},
836 {"c2", tr_c2},
837 {"cc", tr_cc},
838 {"ochar", tr_ochar},
839 {"ce", tr_ce},
840 {"ch", tr_ch},
841 {"char", tr_char, mkargs_ds},
842 {"chop", tr_chop, mkargs_reg1},
843 {"cl", tr_cl},
844 {"cp", tr_cp},
845 {"cs", tr_cs},
846 {"da", tr_di},
847 {"de", tr_de, mkargs_reg1},
848 {"di", tr_di},
849 {"ds", tr_ds, mkargs_ds},
850 {"dt", tr_dt},
851 {"ec", tr_ec},
852 {"el", tr_el, mkargs_null},
853 {"em", tr_em},
854 {"eo", tr_eo},
855 {"ev", tr_ev},
856 {"ex", tr_ex},
857 {"fc", tr_fc},
858 {"fi", tr_fi},
859 {"fmap", tr_fmap},
860 {"fp", tr_fp},
861 {"fspecial", tr_fspecial},
862 {"ft", tr_ft},
863 {"hc", tr_hc},
864 {"hy", tr_hy},
865 {"hyp", tr_hyp},
866 {"hw", tr_hw},
867 {"ie", tr_if, mkargs_null},
868 {"if", tr_if, mkargs_null},
869 {"ig", tr_ig},
870 {"in", tr_in},
871 {"it", tr_it},
872 {"kn", tr_kn},
873 {"lc", tr_lc},
874 {"lf", tr_lf},
875 {"lg", tr_lg},
876 {"ll", tr_ll},
877 {"ls", tr_ls},
878 {"lt", tr_lt},
879 {"mc", tr_mc},
880 {"mk", tr_mk},
881 {"na", tr_na},
882 {"ne", tr_ne},
883 {"nf", tr_nf},
884 {"nh", tr_nh},
885 {"nm", tr_nm},
886 {"nn", tr_nn},
887 {"nr", tr_nr, mkargs_reg1},
888 {"ns", tr_ns},
889 {"nx", tr_nx},
890 {"os", tr_os},
891 {"pc", tr_pc},
892 {"pl", tr_pl},
893 {"pn", tr_pn},
894 {"po", tr_po},
895 {"ps", tr_ps},
896 {"rchar", tr_rchar, mkargs_ds},
897 {"rm", tr_rm},
898 {"rn", tr_rn},
899 {"rr", tr_rr},
900 {"rs", tr_rs},
901 {"rt", tr_rt},
902 {"so", tr_so},
903 {"sp", tr_sp},
904 {"ss", tr_ss},
905 {"sv", tr_sv},
906 {"sy", tr_sy, mkargs_eol},
907 {"ta", tr_ta},
908 {"tc", tr_tc},
909 {"ti", tr_ti},
910 {"tl", tr_tl, mkargs_null},
911 {"tm", tr_tm, mkargs_eol},
912 {"tr", tr_tr, mkargs_eol},
913 {"vs", tr_vs},
914 {"wh", tr_wh},
917 /* read the next troff request; return zero if a request was executed. */
918 int tr_nextreq(void)
920 char *args[NARGS + 3] = {NULL};
921 char cmd[RNLEN];
922 char buf[LNLEN];
923 struct cmd *req;
924 int c;
925 if (!tr_nl)
926 return 1;
927 c = cp_next();
928 if (c < 0 || (c != c_cc && c != c_c2)) {
929 cp_back(c);
930 return 1;
932 memset(args, 0, sizeof(args));
933 args[0] = cmd;
934 cmd[0] = c;
935 req = NULL;
936 arg_regname(cmd + 1, sizeof(cmd) - 1);
937 req = str_dget(map(cmd + 1));
938 if (req) {
939 if (req->args)
940 req->args(args + 1, buf, sizeof(buf));
941 else
942 mkargs_req(args + 1, buf, sizeof(buf));
943 req->f(args);
944 } else {
945 cp_wid(0);
946 mkargs(args + 1, buf, sizeof(buf));
947 cp_wid(1);
948 if (str_get(map(cmd + 1)))
949 in_push(str_get(map(cmd + 1)), args + 1);
951 return 0;
954 int tr_next(void)
956 int c;
957 while (!tr_nextreq())
959 c = cp_next();
960 tr_nl = c == '\n' || c < 0;
961 return c;
964 void tr_init(void)
966 int i;
967 for (i = 0; i < LEN(cmds); i++)
968 str_dset(map(cmds[i].id), &cmds[i]);