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