tr: escaped spaces inside macro arguments
[neatroff.git] / cp.c
blob425fec8065aa9c24b498a2aa7b484b107d34febb
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 \w and \E */
9 static int cp_reqln; /* a request line; replace \{ with an space */
10 static int cp_reqdep; /* the block depth of current request line */
12 static void cparg(char *d, int len)
14 int c = cp_next();
15 int i = 0;
16 if (c == '(') {
17 d[i++] = cp_next();
18 d[i++] = cp_next();
19 } else if (!n_cp && c == '[') {
20 c = cp_next();
21 while (i < NMLEN - 1 && c >= 0 && c != ']') {
22 d[i++] = c;
23 c = cp_next();
25 } else {
26 d[i++] = c;
28 d[i] = '\0';
31 static int regid(void)
33 char regname[NMLEN];
34 cparg(regname, sizeof(regname));
35 return map(regname);
38 /* interpolate \n(xy */
39 static void cp_num(void)
41 int id;
42 int c = cp_next();
43 if (c != '-' && c != '+')
44 cp_back(c);
45 id = regid();
46 if (c == '-' || c == '+')
47 num_inc(id, c == '+');
48 if (num_str(id))
49 in_push(num_str(id), NULL);
52 /* interpolate \*(xy */
53 static void cp_str(void)
55 char arg[ILNLEN];
56 struct sbuf sbuf;
57 char *args[NARGS] = {NULL};
58 cparg(arg, sizeof(arg));
59 if (strchr(arg, ' ')) {
60 sbuf_init(&sbuf);
61 sstr_push(strchr(arg, ' ') + 1);
62 tr_readargs(args, &sbuf, sstr_next, sstr_back);
63 sstr_pop();
64 *strchr(arg, ' ') = '\0';
65 if (str_get(map(arg)))
66 in_push(str_get(map(arg)), args);
67 sbuf_done(&sbuf);
68 } else {
69 if (str_get(map(arg)))
70 in_push(str_get(map(arg)), NULL);
74 /* interpolate \g(xy */
75 static void cp_numfmt(void)
77 in_push(num_getfmt(regid()), NULL);
80 /* interpolate \$1 */
81 static void cp_arg(void)
83 char argname[NMLEN];
84 char *arg = NULL;
85 int argnum;
86 cparg(argname, sizeof(argname));
87 argnum = atoi(argname);
88 if (argnum > 0 && argnum < NARGS + 1)
89 arg = in_arg(argnum);
90 if (arg)
91 in_push(arg, NULL);
94 /* interpolate \w'xyz' */
95 static void cp_width(void)
97 char wid[16];
98 sprintf(wid, "%d", ren_wid(cp_next, cp_back));
99 in_push(wid, NULL);
102 /* define a register as \R'xyz expr' */
103 static void cp_numdef(void)
105 char arg[ILNLEN];
106 char *s;
107 quotednext(arg, cp_next, cp_back);
108 s = arg;
109 while (*s && *s != ' ')
110 s++;
111 if (!*s)
112 return;
113 *s++ = '\0';
114 num_set(map(arg), eval_re(s, num_get(map(arg)), 'u'));
117 /* conditional interpolation as \?'cond@expr1@expr2@' */
118 static void cp_cond(void)
120 char arg[ILNLEN];
121 char delim[GNLEN], cs[GNLEN];
122 char *r, *s = arg;
123 char *s1, *s2;
124 int n;
125 quotednext(arg, cp_next, cp_back);
126 n = eval_up(&s, '\0');
127 if (charread(&s, delim) < 0)
128 return;
129 if (!strcmp(delim, "\\&") && charread(&s, delim) < 0)
130 return;
131 s1 = s;
132 r = s;
133 while (charread_delim(&s, cs, delim) >= 0)
134 r = s;
135 *r = '\0';
136 s2 = s;
137 r = s;
138 while (charread_delim(&s, cs, delim) >= 0)
139 r = s;
140 *r = '\0';
141 in_push(n > 0 ? s1 : s2, NULL);
144 static int cp_raw(void)
146 int c;
147 if (in_top() >= 0)
148 return in_next();
149 do {
150 c = in_next();
151 } while (c == c_ni);
152 if (c == c_ec) {
153 do {
154 c = in_next();
155 } while (c == c_ni);
156 if (c == '\n')
157 return cp_raw();
158 if (c == '.')
159 return '.';
160 if (c == '\\') {
161 in_back('\\');
162 return c_ni;
164 if (c == 't') {
165 in_back('\t');
166 return c_ni;
168 if (c == 'a') {
169 in_back('\x01');
170 return c_ni;
172 if (c == '}' && !cp_cpmode) {
173 cp_blkdep--;
174 return cp_raw();
176 if (c == '{' && !cp_cpmode) {
177 cp_blkdep++;
178 return cp_reqln ? ' ' : cp_raw();
180 in_back(c);
181 return c_ec;
183 if (c == '\n')
184 cp_reqln = 0;
185 return c;
188 int cp_next(void)
190 int c;
191 if (in_top() >= 0)
192 return in_next();
193 c = cp_raw();
194 if (c == c_ec) {
195 c = cp_raw();
196 if (c == 'E' && !cp_cpmode)
197 c = cp_next();
198 if (c == '"') {
199 while (c >= 0 && c != '\n')
200 c = cp_raw();
201 } else if (c == 'w' && !cp_cpmode) {
202 cp_width();
203 c = cp_next();
204 } else if (c == 'n') {
205 cp_num();
206 c = cp_next();
207 } else if (c == '*') {
208 cp_str();
209 c = cp_next();
210 } else if (c == 'g') {
211 cp_numfmt();
212 c = cp_next();
213 } else if (c == '$') {
214 cp_arg();
215 c = cp_next();
216 } else if (c == 'R' && !cp_cpmode) {
217 cp_numdef();
218 c = cp_next();
219 } else if (c == '?' && !cp_cpmode) {
220 cp_cond();
221 c = cp_next();
222 } else {
223 cp_back(c);
224 c = c_ec;
227 return c;
230 void cp_blk(int skip)
232 if (skip) {
233 int c = cp_raw();
234 while (c >= 0 && (c != '\n' || cp_blkdep > cp_reqdep))
235 c = cp_raw();
236 } else {
237 int c = cp_next();
238 while ((c == ' ' || c == '\t') && cp_blkdep <= cp_reqdep)
239 c = cp_next();
240 /* push back if the space is not inserted because of cp_reqln */
241 if (c != ' ' && c != '\t')
242 cp_back(c);
246 void cp_copymode(int mode)
248 cp_cpmode = mode;
251 /* beginning of a request line; replace \{ with a space until an EOL */
252 void cp_reqline(void)
254 cp_reqln = 1;
255 cp_reqdep = cp_blkdep;