tparm.c revision 1.10 1 /* $NetBSD: tparm.c,v 1.10 2013/01/23 13:06:18 roy Exp $ */
2
3 /*
4 * Copyright (c) 2009, 2011 The NetBSD Foundation, Inc.
5 *
6 * This code is derived from software contributed to The NetBSD Foundation
7 * by Roy Marples.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <sys/cdefs.h>
31 __RCSID("$NetBSD: tparm.c,v 1.10 2013/01/23 13:06:18 roy Exp $");
32 #include <sys/param.h>
33
34 #include <assert.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <term_private.h>
42 #include <term.h>
43
44 static TERMINAL *dumbterm; /* For non thread safe functions */
45
46 typedef struct {
47 int nums[20];
48 char *strings[20];
49 size_t offset;
50 } TPSTACK;
51
52 typedef struct {
53 int num;
54 char *string;
55 } TPVAR;
56
57 static int
58 push(int num, char *string, TPSTACK *stack)
59 {
60 if (stack->offset >= sizeof(stack->nums)) {
61 errno = E2BIG;
62 return -1;
63 }
64 stack->nums[stack->offset] = num;
65 stack->strings[stack->offset] = string;
66 stack->offset++;
67 return 0;
68 }
69
70 static int
71 pop(int *num, char **string, TPSTACK *stack)
72 {
73 if (stack->offset == 0) {
74 if (num)
75 *num = 0;
76 if (string)
77 *string = NULL;
78 errno = E2BIG;
79 return -1;
80 }
81 stack->offset--;
82 if (num)
83 *num = stack->nums[stack->offset];
84 if (string)
85 *string = stack->strings[stack->offset];
86 return 0;
87 }
88
89 static char *
90 checkbuf(TERMINAL *term, size_t len)
91 {
92 char *buf;
93
94 if (term->_bufpos + len >= term->_buflen) {
95 len = term->_buflen + MAX(len, BUFSIZ);
96 buf = realloc(term->_buf, len);
97 if (buf == NULL)
98 return NULL;
99 term->_buf = buf;
100 term->_buflen = len;
101 }
102 return term->_buf;
103 }
104
105 static size_t
106 ochar(TERMINAL *term, int c)
107 {
108 if (c == 0)
109 c = 0200;
110 /* Check we have space and a terminator */
111 if (checkbuf(term, 2) == NULL)
112 return 0;
113 term->_buf[term->_bufpos++] = (char)c;
114 return 1;
115 }
116
117 static size_t
118 onum(TERMINAL *term, const char *fmt, int num, int len)
119 {
120 size_t l;
121
122 /* Assume we never have natural number longer than 64 chars */
123 if (len < 64)
124 len = 64;
125 if (checkbuf(term, (size_t)len + 1) == NULL)
126 return 0;
127 l = sprintf(term->_buf + term->_bufpos, fmt, num);
128 term->_bufpos += l;
129 return l;
130 }
131
132 static char *
133 _ti_tiparm(TERMINAL *term, const char *str, va_list parms)
134 {
135 const char *sp;
136 char c, fmt[64], *fp, *ostr;
137 int val, val2;
138 int dnums[26]; /* dynamic variables a-z, not preserved */
139 size_t l, max;
140 TPSTACK stack;
141 TPVAR params[9];
142 int done, dot, minus, width, precision, olen;
143 int piss[9]; /* Parameter IS String - piss ;) */
144
145 if (str == NULL)
146 return NULL;
147
148 /*
149 If not passed a terminal, malloc a dummy one.
150 This means we can preserve buffers and variables per terminal and
151 still work with non thread safe functions (which sadly are still the
152 norm and standard).
153 */
154
155 if (term == NULL) {
156 if (dumbterm == NULL) {
157 dumbterm = malloc(sizeof(*dumbterm));
158 if (dumbterm == NULL)
159 return NULL;
160 dumbterm->_buflen = 0;
161 }
162 term = dumbterm;
163 }
164
165 term->_bufpos = 0;
166 /* Ensure we have an initial buffer */
167 if (term->_buflen == 0) {
168 term->_buf = malloc(BUFSIZ);
169 if (term->_buf == NULL)
170 return NULL;
171 term->_buflen = BUFSIZ;
172 }
173
174 /*
175 Make a first pass through the string so we can work out
176 which parameters are ints and which are char *.
177 Basically we only use char * if %p[1-9] is followed by %l or %s.
178 */
179 memset(&piss, 0, sizeof(piss));
180 max = 0;
181 sp = str;
182 while ((c = *sp++) != '\0') {
183 if (c != '%')
184 continue;
185 c = *sp++;
186 if (c == '\0')
187 break;
188 if (c != 'p')
189 continue;
190 c = *sp++;
191 if (c < '1' || c > '9') {
192 errno = EINVAL;
193 continue;
194 }
195 l = c - '0';
196 if (l > max)
197 max = l;
198 if (*sp != '%')
199 continue;
200 /* Skip formatting */
201 sp++;
202 while (*sp == '.' || *sp == '#' || *sp == ' ' || *sp == ':' ||
203 *sp == '-' || isdigit((unsigned char)*sp))
204 sp++;
205 if (*sp == 'l' || *sp == 's')
206 piss[l - 1] = 1;
207 }
208
209 /* Put our parameters into variables */
210 memset(¶ms, 0, sizeof(params));
211 for (l = 0; l < max; l++) {
212 if (piss[l] == 0)
213 params[l].num = va_arg(parms, int);
214 else
215 params[l].string = va_arg(parms, char *);
216 }
217
218 term->_bufpos = 0;
219 memset(&stack, 0, sizeof(stack));
220 while ((c = *str++) != '\0') {
221 if (c != '%' || (c = *str++) == '%') {
222 if (c == '\0')
223 break;
224 if (ochar(term, c) == 0)
225 return NULL;
226 continue;
227 }
228
229 /* Handle formatting. */
230 fp = fmt;
231 *fp++ = '%';
232 done = dot = minus = width = precision = 0;
233 val = 0;
234 while (done == 0 && (size_t)(fp - fmt) < sizeof(fmt)) {
235 switch (c) {
236 case 'c': /* FALLTHROUGH */
237 case 'd': /* FALLTHROUGH */
238 case 'o': /* FALLTHROUGH */
239 case 'x': /* FALLTHROUGH */
240 case 'X': /* FALLTHROUGH */
241 case 's':
242 *fp++ = c;
243 done = 1;
244 break;
245 case '#': /* FALLTHROUGH */
246 case ' ':
247 *fp++ = c;
248 break;
249 case '.':
250 *fp++ = c;
251 if (dot == 0) {
252 dot = 1;
253 width = val;
254 } else
255 done = 2;
256 val = 0;
257 break;
258 case ':':
259 minus = 1;
260 break;
261 case '-':
262 if (minus)
263 *fp++ = c;
264 else
265 done = 1;
266 break;
267 default:
268 if (isdigit((unsigned char)c)) {
269 val = (val * 10) + (c - '0');
270 if (val > 10000)
271 done = 2;
272 else
273 *fp++ = c;
274 } else
275 done = 1;
276 }
277 if (done == 0)
278 c = *str++;
279 }
280 if (done == 2) {
281 /* Found an error in the format */
282 fp = fmt + 1;
283 *fp = *str;
284 olen = 0;
285 } else {
286 if (dot == 0)
287 width = val;
288 else
289 precision = val;
290 olen = (width > precision) ? width : precision;
291 }
292 *fp++ = '\0';
293
294 /* Handle commands */
295 switch (c) {
296 case 'c':
297 pop(&val, NULL, &stack);
298 if (ochar(term, (unsigned char)val) == 0)
299 return NULL;
300 break;
301 case 's':
302 pop(NULL, &ostr, &stack);
303 if (ostr != NULL) {
304 l = strlen(ostr);
305 if (l < (size_t)olen)
306 l = olen;
307 if (checkbuf(term, (size_t)(l + 1)) == NULL)
308 return NULL;
309 l = sprintf(term->_buf + term->_bufpos,
310 fmt, ostr);
311 term->_bufpos += l;
312 }
313 break;
314 case 'l':
315 pop(NULL, &ostr, &stack);
316 if (ostr == NULL)
317 l = 0;
318 else
319 l = strlen(ostr);
320 if (onum(term, "%d", (int)l, 0) == 0)
321 return NULL;
322 break;
323 case 'd': /* FALLTHROUGH */
324 case 'o': /* FALLTHROUGH */
325 case 'x': /* FALLTHROUGH */
326 case 'X':
327 pop(&val, NULL, &stack);
328 if (onum(term, fmt, val, olen) == 0)
329 return NULL;
330 break;
331 case 'p':
332 if (*str < '1' || *str > '9')
333 break;
334 l = *str++ - '1';
335 if (push(params[l].num, params[l].string, &stack))
336 return NULL;
337 break;
338 case 'P':
339 pop(&val, NULL, &stack);
340 if (*str >= 'a' && *str <= 'z')
341 dnums[*str - 'a'] = val;
342 else if (*str >= 'A' && *str <= 'Z')
343 term->_snums[*str - 'A'] = val;
344 break;
345 case 'g':
346 if (*str >= 'a' && *str <= 'z') {
347 if (push(dnums[*str - 'a'], NULL, &stack))
348 return NULL;
349 } else if (*str >= 'A' && *str <= 'Z') {
350 if (push(term->_snums[*str - 'A'],
351 NULL, &stack))
352 return NULL;
353 }
354 break;
355 case 'i':
356 if (piss[0] == 0)
357 params[0].num++;
358 if (piss[1] == 0)
359 params[1].num++;
360 break;
361 case '\'':
362 if (push((int)(unsigned char)*str++, NULL, &stack))
363 return NULL;
364 while (*str != '\0' && *str != '\'')
365 str++;
366 if (*str == '\'')
367 str++;
368 break;
369 case '{':
370 val = 0;
371 for (; isdigit((unsigned char)*str); str++)
372 val = (val * 10) + (*str - '0');
373 if (push(val, NULL, &stack))
374 return NULL;
375 while (*str != '\0' && *str != '}')
376 str++;
377 if (*str == '}')
378 str++;
379 break;
380 case '+': /* FALLTHROUGH */
381 case '-': /* FALLTHROUGH */
382 case '*': /* FALLTHROUGH */
383 case '/': /* FALLTHROUGH */
384 case 'm': /* FALLTHROUGH */
385 case 'A': /* FALLTHROUGH */
386 case 'O': /* FALLTHROUGH */
387 case '&': /* FALLTHROUGH */
388 case '|': /* FALLTHROUGH */
389 case '^': /* FALLTHROUGH */
390 case '=': /* FALLTHROUGH */
391 case '<': /* FALLTHROUGH */
392 case '>':
393 pop(&val, NULL, &stack);
394 pop(&val2, NULL, &stack);
395 switch (c) {
396 case '+':
397 val = val + val2;
398 break;
399 case '-':
400 val = val2 - val;
401 break;
402 case '*':
403 val = val * val2;
404 break;
405 case '/':
406 val = val ? val2 / val : 0;
407 break;
408 case 'm':
409 val = val ? val2 % val : 0;
410 break;
411 case 'A':
412 val = val && val2;
413 break;
414 case 'O':
415 val = val || val2;
416 break;
417 case '&':
418 val = val & val2;
419 break;
420 case '|':
421 val = val | val2;
422 break;
423 case '^':
424 val = val ^ val2;
425 break;
426 case '=':
427 val = val == val2;
428 break;
429 case '<':
430 val = val2 < val;
431 break;
432 case '>':
433 val = val2 > val;
434 break;
435 }
436 if (push(val, NULL, &stack))
437 return NULL;
438 break;
439 case '!':
440 case '~':
441 pop(&val, NULL, &stack);
442 switch (*str) {
443 case '!':
444 val = !val;
445 break;
446 case '~':
447 val = ~val;
448 break;
449 }
450 if (push(val, NULL, &stack))
451 return NULL;
452 break;
453 case '?': /* if */
454 break;
455 case 't': /* then */
456 pop(&val, NULL, &stack);
457 if (val == 0) {
458 l = 0;
459 for (; *str != '\0'; str++) {
460 if (*str != '%')
461 continue;
462 str++;
463 if (*str == '?')
464 l++;
465 else if (*str == ';') {
466 if (l > 0)
467 l--;
468 else {
469 str++;
470 break;
471 }
472 } else if (*str == 'e' && l == 0) {
473 str++;
474 break;
475 }
476 }
477 }
478 break;
479 case 'e': /* else */
480 l = 0;
481 for (; *str != '\0'; str++) {
482 if (*str != '%')
483 continue;
484 str++;
485 if (*str == '?')
486 l++;
487 else if (*str == ';') {
488 if (l > 0)
489 l--;
490 else {
491 str++;
492 break;
493 }
494 }
495 }
496 break;
497 case ';': /* fi */
498 break;
499 }
500 }
501 term->_buf[term->_bufpos] = '\0';
502 return term->_buf;
503 }
504
505 char *
506 ti_tiparm(TERMINAL *term, const char *str, ...)
507 {
508 va_list va;
509 char *ret;
510
511 _DIAGASSERT(term != NULL);
512 _DIAGASSERT(str != NULL);
513
514 va_start(va, str);
515 ret = _ti_tiparm(term, str, va);
516 va_end(va);
517 return ret;
518 }
519
520 char *
521 tiparm(const char *str, ...)
522 {
523 va_list va;
524 char *ret;
525
526 _DIAGASSERT(str != NULL);
527
528 va_start(va, str);
529 ret = _ti_tiparm(NULL, str, va);
530 va_end(va);
531 return ret;
532 }
533
534 char *
535 tparm(const char *str,
536 long lp1, long lp2, long lp3, long lp4, long lp5,
537 long lp6, long lp7, long lp8, long lp9)
538 {
539 int p1 = lp1, p2 = lp2, p3 = lp3, p4 = lp4, p5 = lp5;
540 int p6 = lp6, p7 = lp7, p8 = lp8, p9 = lp9;
541
542 return tiparm(str, p1, p2, p3, p4, p5, p6, p7, p8, p9);
543 }
544