reg: use snprintf for string values in num_str()
[neatroff.git] / cp.c
blob17a5ee53dfa790fa66a06c6e16095e95c94fd1f1
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 char *s;
57 if (c != '-' && c != '+')
58 cp_back(c);
59 id = regid();
60 if (c == '-' || c == '+')
61 num_inc(id, c == '+');
62 if ((s = num_str(id)) != NULL)
63 in_push(s, NULL);
66 /* interpolate \*(xy */
67 static void cp_str(void)
69 char reg[NMLEN];
70 char *args[NARGS + 2] = {reg};
71 char *buf = NULL;
72 if (cparg(reg, sizeof(reg))) {
73 buf = tr_args(args + 1, ']', cp_noninext, cp_back);
74 cp_noninext();
76 if (str_get(map(reg)))
77 in_push(str_get(map(reg)), buf ? args : NULL);
78 else if (!n_cp)
79 tr_req(map(reg), args);
80 free(buf);
83 /* interpolate \g(xy */
84 static void cp_numfmt(void)
86 in_push(num_getfmt(regid()), NULL);
89 /* interpolate \$*, \$@, and \$^ */
90 static void cp_args(int quote, int escape)
92 struct sbuf sb;
93 char *s;
94 int i;
95 sbuf_init(&sb);
96 for (i = 1; i < in_nargs(); i++) {
97 sbuf_append(&sb, i > 1 ? " " : "");
98 sbuf_append(&sb, quote ? "\"" : "");
99 s = in_arg(i);
100 while (*s) {
101 sbuf_append(&sb, escape && *s == '"' ? "\"" : "");
102 sbuf_add(&sb, (unsigned char) *s++);
104 sbuf_append(&sb, quote ? "\"" : "");
106 in_push(sbuf_buf(&sb), NULL);
107 sbuf_done(&sb);
110 /* interpolate \$1 */
111 static void cp_arg(void)
113 char argname[NMLEN];
114 char *arg = NULL;
115 int argnum;
116 cparg(argname, sizeof(argname));
117 if (!strcmp("@", argname)) {
118 cp_args(1, 0);
119 return;
121 if (!strcmp("*", argname)) {
122 cp_args(0, 0);
123 return;
125 if (!strcmp("^", argname)) {
126 cp_args(1, 1);
127 return;
129 argnum = atoi(argname);
130 if (argnum >= 0 && argnum < NARGS)
131 arg = in_arg(argnum);
132 if (arg)
133 in_push(arg, NULL);
136 /* interpolate \w'xyz' */
137 static void cp_width(void)
139 char wid[16];
140 sprintf(wid, "%d", ren_wid(cp_next, cp_back));
141 in_push(wid, NULL);
144 /* define a register as \R'xyz expr' */
145 static void cp_numdef(void)
147 char *arg = quotednext(cp_noninext, cp_back);
148 char *s = arg;
149 while (*s && *s != ' ')
150 s++;
151 if (!*s) {
152 free(arg);
153 return;
155 *s++ = '\0';
156 num_set(map(arg), eval_re(s, num_get(map(arg)), 'u'));
157 free(arg);
160 /* conditional interpolation as \?'cond@expr1@expr2@' */
161 static void cp_cond(void)
163 char delim[GNLEN], cs[GNLEN];
164 char *r, *s;
165 char *s1, *s2;
166 int n;
167 char *arg = quotednext(cp_noninext, cp_back);
168 s = arg;
169 n = eval_up(&s, '\0');
170 if (charread(&s, delim) < 0) {
171 free(arg);
172 return;
174 if (!strcmp(delim, "\\&") && charread(&s, delim) < 0) {
175 free(arg);
176 return;
178 s1 = s;
179 r = s;
180 while (charread_delim(&s, cs, delim) >= 0)
181 r = s;
182 *r = '\0';
183 s2 = s;
184 r = s;
185 while (charread_delim(&s, cs, delim) >= 0)
186 r = s;
187 *r = '\0';
188 in_push(n > 0 ? s1 : s2, NULL);
189 free(arg);
192 static int cp_raw(void)
194 int c;
195 if (in_top() >= 0)
196 return in_next();
197 do {
198 c = in_next();
199 } while (c == c_ni);
200 if (c == c_ec) {
201 do {
202 c = in_next();
203 } while (c == c_ni);
204 if (c == '\n')
205 return cp_raw();
206 if (c == '.')
207 return '.';
208 if (c == '\\') {
209 in_back('\\');
210 return c_ni;
212 if (c == 't') {
213 in_back('\t');
214 return c_ni;
216 if (c == 'a') {
217 in_back('\x01');
218 return c_ni;
220 /* replace \{ and \} with a space if not in copy mode */
221 if (c == '}' && !cp_cpmode) {
222 cp_blkdep--;
223 return ' ';
225 if (c == '{' && !cp_cpmode) {
226 cp_blkdep++;
227 return ' ';
229 in_back(c);
230 return c_ec;
232 return c;
235 int cp_next(void)
237 int c;
238 if (in_top() >= 0)
239 return in_next();
240 c = cp_raw();
241 if (c == c_ec) {
242 c = cp_raw();
243 if (c == 'E' && !cp_cpmode)
244 c = cp_next();
245 if (c == '"') {
246 while (c >= 0 && c != '\n')
247 c = cp_raw();
248 } else if (c == 'w' && !cp_cpmode) {
249 cp_width();
250 c = cp_next();
251 } else if (c == 'n') {
252 cp_num();
253 c = cp_next();
254 } else if (c == '*') {
255 cp_str();
256 c = cp_next();
257 } else if (c == 'g') {
258 cp_numfmt();
259 c = cp_next();
260 } else if (c == '$') {
261 cp_arg();
262 c = cp_next();
263 } else if (c == '?') {
264 cp_cond();
265 c = cp_next();
266 } else if (c == 'R' && !cp_cpmode) {
267 cp_numdef();
268 c = cp_next();
269 } else {
270 cp_back(c);
271 c = c_ec;
274 return c;
277 void cp_blk(int skip)
279 if (skip) {
280 int c = cp_raw();
281 while (c >= 0 && (c != '\n' || cp_blkdep > cp_reqdep))
282 c = cp_raw();
283 } else {
284 int c = cp_next();
285 while (c == ' ')
286 c = cp_next();
287 /* push back if the space is not inserted due to \{ and \} */
288 if (c != ' ')
289 cp_back(c);
293 void cp_copymode(int mode)
295 cp_cpmode = mode;
298 /* beginning of a request; save current cp_blkdep */
299 void cp_reqbeg(void)
301 cp_reqdep = cp_blkdep;