tparm.c revision 1.4 1 /* $NetBSD: tparm.c,v 1.4 2011/03/10 10:46:33 roy Exp $ */
2
3 /*
4 * Copyright (c) 2009 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.4 2011/03/10 10:46:33 roy Exp $");
32
33 #include <assert.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <stdarg.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <term_private.h>
41 #include <term.h>
42
43 static TERMINAL *dumbterm; /* For non thread safe functions */
44
45 typedef struct {
46 long nums[20];
47 char *strings[20];
48 size_t offset;
49 } TPSTACK;
50
51 typedef struct {
52 long num;
53 char *string;
54 } TPVAR;
55
56 static int
57 push(long num, char *string, TPSTACK *stack)
58 {
59 if (stack->offset > sizeof(stack->nums)) {
60 errno = E2BIG;
61 return -1;
62 }
63 stack->nums[stack->offset] = num;
64 stack->strings[stack->offset] = string;
65 stack->offset++;
66 return 0;
67 }
68
69 static int
70 pop(long *num, char **string, TPSTACK *stack)
71 {
72 if (stack->offset == 0) {
73 errno = E2BIG;
74 return -1;
75 }
76 stack->offset--;
77 if (num)
78 *num = stack->nums[stack->offset];
79 if (string)
80 *string = stack->strings[stack->offset];
81 return 0;
82 }
83
84 static char *
85 checkbuf(TERMINAL *term, size_t len)
86 {
87 char *buf;
88
89 if (term->_bufpos + len >= term->_buflen) {
90 len = term->_buflen + BUFSIZ;
91 buf = realloc(term->_buf, len);
92 if (buf == NULL)
93 return 0;
94 term->_buf = buf;
95 term->_buflen = len;
96 }
97 return term->_buf;
98 }
99
100 static size_t
101 ochar(TERMINAL *term, int c)
102 {
103 if (c == 0)
104 c = 0200;
105 /* Check we have space and a terminator */
106 if (checkbuf(term, 2) == NULL)
107 return 0;
108 term->_buf[term->_bufpos++] = (char)c;
109 return 1;
110 }
111
112 static size_t
113 onum(TERMINAL *term, const char *fmt, long num, int len)
114 {
115 size_t l;
116
117 /* Assume we never have natural number longer than 64 chars */
118 if (len < 64)
119 len = 64;
120 if (checkbuf(term, (size_t)len + 1) == NULL)
121 return 0;
122 l = sprintf(term->_buf + term->_bufpos, fmt, num);
123 term->_bufpos += l;
124 return l;
125 }
126
127 static char *
128 _ti_vtparm(TERMINAL *term, const char *str, va_list parms)
129 {
130 const char *sp;
131 char c, fmt[64], *fp, *ostr;
132 long val, val2;
133 long dnums[26]; /* dynamic variables a-z, not preserved */
134 size_t l, max;
135 TPSTACK stack;
136 TPVAR params[9];
137 int done, dot, minus, width, precision, olen;
138 int piss[9]; /* Parameter IS String - piss ;) */
139
140 if (str == NULL)
141 return NULL;
142
143 /*
144 If not passed a terminal, malloc a dummy one.
145 This means we can preserve buffers and variables per terminal and
146 still work with non thread safe functions (which sadly are still the
147 norm and standard).
148 */
149 if (term == NULL) {
150 if (dumbterm == NULL) {
151 dumbterm = malloc(sizeof(*dumbterm));
152 if (dumbterm == NULL)
153 return NULL;
154 dumbterm->_buflen = 0;
155 }
156 term = dumbterm;
157 }
158
159 term->_bufpos = 0;
160 /* Ensure we have an initial buffer */
161 if (term->_buflen == 0) {
162 term->_buf = malloc(BUFSIZ);
163 if (term->_buf == NULL)
164 return NULL;
165 term->_buflen = BUFSIZ;
166 }
167
168 /*
169 Make a first pass through the string so we can work out
170 which parameters are longs and which are char *.
171 Basically we only use char * if %p[1-9] is followed by %l or %s.
172 */
173 memset(&piss, 0, sizeof(piss));
174 max = 0;
175 sp = str;
176 while ((c = *sp++) != '\0') {
177 if (c != '%')
178 continue;
179 c = *sp++;
180 if (c == '\0')
181 break;
182 if (c != 'p')
183 continue;
184 c = *sp++;
185 if (c < '1' || c > '9') {
186 errno = EINVAL;
187 return NULL;
188 }
189 l = c - '0';
190 if (l > max)
191 max = l;
192 if (*sp != '%')
193 continue;
194 /* Skip formatting */
195 sp++;
196 while (*sp == '.' || *sp == '#' || *sp == ' ' || *sp == ':' ||
197 *sp == '-' || isdigit((unsigned char)*sp))
198 sp++;
199 if (*sp == 'l' || *sp == 's')
200 piss[l - 1] = 1;
201 }
202
203 /* Put our parameters into variables */
204 memset(¶ms, 0, sizeof(params));
205 for (l = 0; l < max; l++) {
206 if (piss[l] == 0)
207 params[l].num = va_arg(parms, long);
208 else
209 params[l].string = va_arg(parms, char *);
210 }
211
212 term->_bufpos = 0;
213 memset(&stack, 0, sizeof(stack));
214 while ((c = *str++) != '\0') {
215 if (c != '%' || (c = *str++) == '%') {
216 if (c == '\0')
217 break;
218 if (ochar(term, c) == 0)
219 return NULL;
220 continue;
221 }
222
223 /* Handle formatting. */
224 fp = fmt;
225 *fp++ = '%';
226 done = dot = minus = width = precision = 0;
227 val = 0;
228 while (done == 0 && (size_t)(fp - fmt) < sizeof(fmt)) {
229 switch (c) {
230 case 'c': /* FALLTHROUGH */
231 case 'd': /* FALLTHROUGH */
232 case 'o': /* FALLTHROUGH */
233 case 'x': /* FALLTHROUGH */
234 case 'X': /* FALLTHROUGH */
235 case 's':
236 *fp++ = c;
237 done = 1;
238 break;
239 case '#': /* FALLTHROUGH */
240 case ' ':
241 *fp++ = c;
242 break;
243 case '.':
244 *fp++ = c;
245 if (dot == 0) {
246 dot = 1;
247 width = val;
248 } else
249 done = 2;
250 val = 0;
251 break;
252 case ':':
253 minus = 1;
254 break;
255 case '-':
256 if (minus)
257 *fp++ = c;
258 else
259 done = 1;
260 break;
261 default:
262 if (isdigit((unsigned char)c)) {
263 val = (val * 10) + (c - '0');
264 if (val > 10000)
265 done = 2;
266 else
267 *fp++ = c;
268 } else
269 done = 1;
270 }
271 if (done == 0)
272 c = *str++;
273 }
274 if (done == 2) {
275 /* Found an error in the format */
276 fp = fmt + 1;
277 *fp = *str;
278 olen = 0;
279 } else {
280 if (dot == 0)
281 width = val;
282 else
283 precision = val;
284 olen = (width > precision) ? width : precision;
285 }
286 *fp++ = '\0';
287
288 /* Handle commands */
289 switch (c) {
290 case 'c':
291 if (pop(&val, NULL, &stack))
292 return NULL;
293 if (ochar(term, (unsigned char)val) == 0)
294 return NULL;
295 break;
296 case 's':
297 if (pop(NULL, &ostr, &stack))
298 return NULL;
299 if (ostr != NULL) {
300 l = strlen(ostr);
301 if (l < (size_t)olen)
302 l = olen;
303 if (checkbuf(term, (size_t)(l + 1)) == NULL)
304 return NULL;
305 l = sprintf(term->_buf + term->_bufpos,
306 fmt, ostr);
307 term->_bufpos += l;
308 }
309 break;
310 case 'l':
311 if (pop(NULL, &ostr, &stack))
312 return NULL;
313 if (ostr == NULL)
314 l = 0;
315 else
316 l = strlen(ostr);
317 if (onum(term, "%d", (long)l, 0) == 0)
318 return NULL;
319 break;
320 case 'd': /* FALLTHROUGH */
321 case 'o': /* FALLTHROUGH */
322 case 'x': /* FALLTHROUGH */
323 case 'X':
324 if (pop(&val, NULL, &stack))
325 return NULL;
326 if (onum(term, fmt, val, olen) == 0)
327 return NULL;
328 break;
329 case 'p':
330 if (*str < '1' || *str > '9') {
331 errno = EINVAL;
332 return NULL;
333 }
334 l = *str++ - '1';
335 if (push(params[l].num, params[l].string, &stack))
336 return NULL;
337 break;
338 case 'P':
339 if (pop(&val, NULL, &stack))
340 return NULL;
341 if (*str >= 'a' && *str <= 'z')
342 dnums[*str - 'a'] = val;
343 else if (*str >= 'A' && *str <= 'Z')
344 term->_snums[*str - 'A'] = val;
345 break;
346 case 'g':
347 if (*str >= 'a' && *str <= 'z') {
348 if (push(dnums[*str - 'a'], NULL, &stack))
349 return NULL;
350 } else if (*str >= 'A' && *str <= 'Z') {
351 if (push(term->_snums[*str - 'A'],
352 NULL, &stack))
353 return NULL;
354 }
355 break;
356 case 'i':
357 if (piss[0] == 0)
358 params[0].num++;
359 if (piss[1] == 0)
360 params[1].num++;
361 break;
362 case '\'':
363 if (push((long)(unsigned char)*str++, NULL, &stack))
364 return NULL;
365 while (*str != '\0' && *str != '\'')
366 str++;
367 if (*str == '\'')
368 str++;
369 break;
370 case '{':
371 val = 0;
372 for (; isdigit((unsigned char)*str); str++)
373 val = (val * 10) + (*str - '0');
374 if (push(val, NULL, &stack))
375 return NULL;
376 while (*str != '\0' && *str != '}')
377 str++;
378 if (*str == '}')
379 str++;
380 break;
381 case '+': /* FALLTHROUGH */
382 case '-': /* FALLTHROUGH */
383 case '*': /* FALLTHROUGH */
384 case '/': /* FALLTHROUGH */
385 case 'm': /* FALLTHROUGH */
386 case 'A': /* FALLTHROUGH */
387 case 'O': /* FALLTHROUGH */
388 case '&': /* FALLTHROUGH */
389 case '|': /* FALLTHROUGH */
390 case '^': /* FALLTHROUGH */
391 case '=': /* FALLTHROUGH */
392 case '<': /* FALLTHROUGH */
393 case '>':
394 if (pop(&val, NULL, &stack) ||
395 pop(&val2, NULL, &stack))
396 return NULL;
397 switch (c) {
398 case '+':
399 val = val + val2;
400 break;
401 case '-':
402 val = val2 - val;
403 break;
404 case '*':
405 val = val * val2;
406 break;
407 case '/':
408 val = val ? val2 / val : 0;
409 break;
410 case 'm':
411 val = val ? val2 % val : 0;
412 break;
413 case 'A':
414 val = val && val2;
415 break;
416 case 'O':
417 val = val || val2;
418 break;
419 case '&':
420 val = val & val2;
421 break;
422 case '|':
423 val = val | val2;
424 break;
425 case '^':
426 val = val ^ val2;
427 break;
428 case '=':
429 val = val == val2;
430 break;
431 case '<':
432 val = val2 < val;
433 break;
434 case '>':
435 val = val2 > val;
436 break;
437 }
438 if (push(val, NULL, &stack))
439 return NULL;
440 break;
441 case '!':
442 case '~':
443 if (pop(&val, NULL, &stack))
444 return NULL;
445 switch (*str) {
446 case '!':
447 val = !val;
448 break;
449 case '~':
450 val = ~val;
451 break;
452 }
453 if (push(val, NULL, &stack))
454 return NULL;
455 break;
456 case '?': /* if */
457 break;
458 case 't': /* then */
459 if (pop(&val, NULL, &stack))
460 return NULL;
461 if (val != 0) {
462 l = 0;
463 for (; *str != '\0'; str++) {
464 if (*str != '%')
465 continue;
466 str++;
467 if (*str == '?')
468 l++;
469 else if (*str == ';') {
470 if (l > 0)
471 l--;
472 else
473 break;
474 } else if (*str == 'e' && l == 0)
475 break;
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 break;
492 }
493 }
494 break;
495 case ';': /* fi */
496 break;
497 }
498 }
499 term->_buf[term->_bufpos] = '\0';
500 return term->_buf;
501 }
502
503 char *
504 t_vparm(TERMINAL *term, const char *str, ...)
505 {
506 va_list va;
507 char *ret;
508
509 _DIAGASSERT(term != NULL);
510 _DIAGASSERT(str != NULL);
511
512 va_start(va, str);
513 ret = _ti_vtparm(term, str, va);
514 va_end(va);
515 return ret;
516 }
517
518 char *
519 vtparm(const char *str, ...)
520 {
521 va_list va;
522 char *ret;
523
524 _DIAGASSERT(str != NULL);
525
526 va_start(va, str);
527 ret = _ti_vtparm(NULL, str, va);
528 va_end(va);
529 return ret;
530 }
531
532 char *
533 t_parm(TERMINAL *term, const char *str,
534 long p1, long p2, long p3, long p4, long p5,
535 long p6, long p7, long p8, long p9)
536 {
537
538 _DIAGASSERT(term != NULL);
539 _DIAGASSERT(str != NULL);
540 return t_vparm(term, str, p1, p2, p3, p4, p5, p6, p7, p8, p9);
541 }
542
543 char *
544 tparm(const char *str,
545 long p1, long p2, long p3, long p4, long p5,
546 long p6, long p7, long p8, long p9)
547 {
548
549 _DIAGASSERT(str != NULL);
550 return t_vparm(NULL, str, p1, p2, p3, p4, p5, p6, p7, p8, p9);
551 }
552