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