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