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