tr: allow parsing macro arguments as a request
[neatroff.git] / cp.c
blob165175c1cd664d33c643b83da9fd634f002c8a3b
1 /* copy-mode character interpretation */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include "roff.h"
7 static int cp_blkdep; /* input block depth (text in \{ and \}) */
8 static int cp_cpmode; /* disable the interpretation of \w and \E */
9 static int cp_reqdep; /* the block depth of current request line */
11 /* just like cp_next(), but remove c_ni characters */
12 static int cp_noninext(void)
14 int c = cp_next();
15 while (c == c_ni)
16 c = cp_next();
17 return c;
20 /* return 1 if \*[] includes a space */
21 static int cparg(char *d, int len)
23 int c = cp_noninext();
24 int i = 0;
25 if (c == '(') {
26 i += utf8next(d + i, cp_noninext);
27 i += utf8next(d + i, cp_noninext);
28 } else if (!n_cp && c == '[') {
29 c = cp_noninext();
30 while (c >= 0 && c != ']' && c != ' ') {
31 if (i + 1 < len)
32 d[i++] = c;
33 c = cp_noninext();
35 d[i] = '\0';
36 return c == ' ';
37 } else {
38 cp_back(c);
39 utf8next(d, cp_noninext);
41 return 0;
44 static int regid(void)
46 char regname[NMLEN];
47 cparg(regname, sizeof(regname));
48 return map(regname);
51 /* interpolate \n(xy */
52 static void cp_num(void)
54 int id;
55 int c = cp_noninext();
56 if (c != '-' && c != '+')
57 cp_back(c);
58 id = regid();
59 if (c == '-' || c == '+')
60 num_inc(id, c == '+');
61 if (num_str(id))
62 in_push(num_str(id), NULL);
65 /* interpolate \*(xy */
66 static void cp_str(void)
68 char reg[NMLEN];
69 char *args[NARGS + 2] = {reg};
70 char *buf = NULL;
71 if (cparg(reg, sizeof(reg))) {
72 buf = tr_args(args + 1, ']', cp_noninext, cp_back);
73 cp_noninext();
75 if (str_get(map(reg)))
76 in_push(str_get(map(reg)), buf ? args : NULL);
77 else if (!n_cp)
78 tr_req(map(reg), args);
79 free(buf);
82 /* interpolate \g(xy */
83 static void cp_numfmt(void)
85 in_push(num_getfmt(regid()), NULL);
88 /* interpolate \$*, \$@, and \$^ */
89 static void cp_args(int quote, int escape)
91 struct sbuf sb;
92 char *s;
93 int i;
94 sbuf_init(&sb);
95 for (i = 1; i < in_nargs(); i++) {
96 sbuf_append(&sb, i > 1 ? " " : "");
97 sbuf_append(&sb, quote ? "\"" : "");
98 s = in_arg(i);
99 while (*s) {
100 sbuf_append(&sb, escape && *s == '"' ? "\"" : "");
101 sbuf_add(&sb, (unsigned char) *s++);
103 sbuf_append(&sb, quote ? "\"" : "");
105 in_push(sbuf_buf(&sb), NULL);
106 sbuf_done(&sb);
109 /* interpolate \$1 */
110 static void cp_arg(void)
112 char argname[NMLEN];
113 char *arg = NULL;
114 int argnum;
115 cparg(argname, sizeof(argname));
116 if (!strcmp("@", argname)) {
117 cp_args(1, 0);
118 return;
120 if (!strcmp("*", argname)) {
121 cp_args(0, 0);
122 return;
124 if (!strcmp("^", argname)) {
125 cp_args(1, 1);
126 return;
128 argnum = atoi(argname);
129 if (argnum >= 0 && argnum < NARGS)
130 arg = in_arg(argnum);
131 if (arg)
132 in_push(arg, NULL);
135 /* interpolate \w'xyz' */
136 static void cp_width(void)
138 char wid[16];
139 sprintf(wid, "%d", ren_wid(cp_next, cp_back));
140 in_push(wid, NULL);
143 /* define a register as \R'xyz expr' */
144 static void cp_numdef(void)
146 char *arg = quotednext(cp_noninext, cp_back);
147 char *s = arg;
148 while (*s && *s != ' ')
149 s++;
150 if (!*s) {
151 free(arg);
152 return;
154 *s++ = '\0';
155 num_set(map(arg), eval_re(s, num_get(map(arg)), 'u'));
156 free(arg);
159 /* conditional interpolation as \?'cond@expr1@expr2@' */
160 static void cp_cond(void)
162 char delim[GNLEN], cs[GNLEN];
163 char *r, *s;
164 char *s1, *s2;
165 int n;
166 char *arg = quotednext(cp_noninext, cp_back);
167 s = arg;
168 n = eval_up(&s, '\0');
169 if (charread(&s, delim) < 0) {
170 free(arg);
171 return;
173 if (!strcmp(delim, "\\&") && charread(&s, delim) < 0) {
174 free(arg);
175 return;
177 s1 = s;
178 r = s;
179 while (charread_delim(&s, cs, delim) >= 0)
180 r = s;
181 *r = '\0';
182 s2 = s;
183 r = s;
184 while (charread_delim(&s, cs, delim) >= 0)
185 r = s;
186 *r = '\0';
187 in_push(n > 0 ? s1 : s2, NULL);
188 free(arg);
191 static int cp_raw(void)
193 int c;
194 if (in_top() >= 0)
195 return in_next();
196 do {
197 c = in_next();
198 } while (c == c_ni);
199 if (c == c_ec) {
200 do {
201 c = in_next();
202 } while (c == c_ni);
203 if (c == '\n')
204 return cp_raw();
205 if (c == '.')
206 return '.';
207 if (c == '\\') {
208 in_back('\\');
209 return c_ni;
211 if (c == 't') {
212 in_back('\t');
213 return c_ni;
215 if (c == 'a') {
216 in_back('\x01');
217 return c_ni;
219 /* replace \{ and \} with a space if not in copy mode */
220 if (c == '}' && !cp_cpmode) {
221 cp_blkdep--;
222 return ' ';
224 if (c == '{' && !cp_cpmode) {
225 cp_blkdep++;
226 return ' ';
228 in_back(c);
229 return c_ec;
231 return c;
234 int cp_next(void)
236 int c;
237 if (in_top() >= 0)
238 return in_next();
239 c = cp_raw();
240 if (c == c_ec) {
241 c = cp_raw();
242 if (c == 'E' && !cp_cpmode)
243 c = cp_next();
244 if (c == '"') {
245 while (c >= 0 && c != '\n')
246 c = cp_raw();
247 } else if (c == 'w' && !cp_cpmode) {
248 cp_width();
249 c = cp_next();
250 } else if (c == 'n') {
251 cp_num();
252 c = cp_next();
253 } else if (c == '*') {
254 cp_str();
255 c = cp_next();
256 } else if (c == 'g') {
257 cp_numfmt();
258 c = cp_next();
259 } else if (c == '$') {
260 cp_arg();
261 c = cp_next();
262 } else if (c == '?') {
263 cp_cond();
264 c = cp_next();
265 } else if (c == 'R' && !cp_cpmode) {
266 cp_numdef();
267 c = cp_next();
268 } else {
269 cp_back(c);
270 c = c_ec;
273 return c;
276 void cp_blk(int skip)
278 if (skip) {
279 int c = cp_raw();
280 while (c >= 0 && (c != '\n' || cp_blkdep > cp_reqdep))
281 c = cp_raw();
282 } else {
283 int c = cp_next();
284 while (c == ' ')
285 c = cp_next();
286 /* push back if the space is not inserted due to \{ and \} */
287 if (c != ' ')
288 cp_back(c);
292 void cp_copymode(int mode)
294 cp_cpmode = mode;
297 /* beginning of a request; save current cp_blkdep */
298 void cp_reqbeg(void)
300 cp_reqdep = cp_blkdep;