Changeset View
Changeset View
Standalone View
Standalone View
contrib/mg/tparm.c
- This file was added.
/* $NetBSD: tparm.c,v 1.17 2017/05/04 09:42:23 roy Exp $ */ | |||||
/* | |||||
* Copyright (c) 2009, 2011, 2013 The NetBSD Foundation, Inc. | |||||
* | |||||
* This code is derived from software contributed to The NetBSD Foundation | |||||
* by Roy Marples. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
*/ | |||||
#include <sys/cdefs.h> | |||||
#include <sys/param.h> | |||||
#include <ctype.h> | |||||
#include <errno.h> | |||||
#include <stdarg.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include "term_private.h" | |||||
#include "terminfo_term.h" | |||||
#define LONG_STR_MAX ((CHAR_BIT * sizeof(long)) / 3) | |||||
#define BUFINC 128 /* Size to increament the terminal buffer by */ | |||||
#define VA_LONG_LONG 1 | |||||
#define VA_CHAR_INT 2 | |||||
//#define VA_CHAR_LONG 3 /* No need for this yet */ | |||||
static TERMINAL *dumbterm; /* For non thread safe functions */ | |||||
typedef struct { | |||||
long nums[20]; | |||||
char *strings[20]; | |||||
size_t offset; | |||||
} TPSTACK; | |||||
typedef struct { | |||||
long num; | |||||
char *string; | |||||
} TPVAR; | |||||
static int | |||||
push(long num, char *string, TPSTACK *stack) | |||||
{ | |||||
if (stack->offset >= sizeof(stack->nums)) { | |||||
errno = E2BIG; | |||||
return -1; | |||||
} | |||||
stack->nums[stack->offset] = num; | |||||
stack->strings[stack->offset] = string; | |||||
stack->offset++; | |||||
return 0; | |||||
} | |||||
static int | |||||
pop(long *num, char **string, TPSTACK *stack) | |||||
{ | |||||
if (stack->offset == 0) { | |||||
if (num) | |||||
*num = 0; | |||||
if (string) | |||||
*string = NULL; | |||||
errno = E2BIG; | |||||
return -1; | |||||
} | |||||
stack->offset--; | |||||
if (num) | |||||
*num = stack->nums[stack->offset]; | |||||
if (string) | |||||
*string = stack->strings[stack->offset]; | |||||
return 0; | |||||
} | |||||
static char * | |||||
checkbuf(TERMINAL *term, size_t len) | |||||
{ | |||||
char *buf; | |||||
if (term->_bufpos + len >= term->_buflen) { | |||||
len = term->_buflen + MAX(len, BUFINC); | |||||
buf = realloc(term->_buf, len); | |||||
if (buf == NULL) | |||||
return NULL; | |||||
term->_buf = buf; | |||||
term->_buflen = len; | |||||
} | |||||
return term->_buf; | |||||
} | |||||
static size_t | |||||
ochar(TERMINAL *term, int c) | |||||
{ | |||||
if (c == 0) | |||||
c = 0200; | |||||
/* Check we have space and a terminator */ | |||||
if (checkbuf(term, 2) == NULL) | |||||
return 0; | |||||
term->_buf[term->_bufpos++] = (char)c; | |||||
return 1; | |||||
} | |||||
static size_t | |||||
onum(TERMINAL *term, const char *fmt, int num, size_t len) | |||||
{ | |||||
int l; | |||||
size_t r; | |||||
if (len < LONG_STR_MAX) | |||||
len = LONG_STR_MAX; | |||||
if (checkbuf(term, len + 2) == NULL) | |||||
return 0; | |||||
l = snprintf(term->_buf + term->_bufpos, len + 2, fmt, num); | |||||
if (l == -1) | |||||
return 0; | |||||
r = (size_t)l; | |||||
term->_bufpos += r; | |||||
return r; | |||||
} | |||||
/* | |||||
Make a pass through the string so we can work out | |||||
which parameters are ints and which are char *. | |||||
Basically we only use char * if %p[1-9] is followed by %l or %s. | |||||
*/ | |||||
int | |||||
_ti_parm_analyse(const char *str, int *piss, int piss_len) | |||||
{ | |||||
int nparm, lpop; | |||||
char c; | |||||
nparm = 0; | |||||
lpop = -1; | |||||
while ((c = *str++) != '\0') { | |||||
if (c != '%') | |||||
continue; | |||||
c = *str++; | |||||
switch (c) { | |||||
case 'l': /* FALLTHROUGH */ | |||||
case 's': | |||||
if (lpop > 0) { | |||||
if (lpop <= piss_len) | |||||
piss[lpop - 1] = 1; | |||||
else if (piss) | |||||
errno = E2BIG; | |||||
} | |||||
break; | |||||
case 'p': | |||||
c = *str++; | |||||
if (c < '1' || c > '9') { | |||||
errno = EINVAL; | |||||
continue; | |||||
} else { | |||||
lpop = c - '0'; | |||||
if (lpop > nparm) | |||||
nparm = lpop; | |||||
} | |||||
break; | |||||
default: | |||||
lpop = -1; | |||||
} | |||||
} | |||||
return nparm; | |||||
} | |||||
static char * | |||||
_ti_tiparm(TERMINAL *term, const char *str, int va_type, va_list parms) | |||||
{ | |||||
char c, fmt[64], *fp, *ostr; | |||||
long val, val2; | |||||
long dnums[26]; /* dynamic variables a-z, not preserved */ | |||||
size_t l, max, width, precision, olen; | |||||
TPSTACK stack; | |||||
TPVAR params[TPARM_MAX]; | |||||
unsigned int done, dot, minus; | |||||
int piss[TPARM_MAX]; /* Parameter IS String - piss ;) */ | |||||
if (str == NULL) | |||||
return NULL; | |||||
/* | |||||
If not passed a terminal, malloc a dummy one. | |||||
This means we can preserve buffers and variables per terminal and | |||||
still work with non thread safe functions (which sadly are still the | |||||
norm and standard). | |||||
*/ | |||||
if (term == NULL) { | |||||
if (dumbterm == NULL) { | |||||
dumbterm = malloc(sizeof(*dumbterm)); | |||||
if (dumbterm == NULL) | |||||
return NULL; | |||||
dumbterm->_buflen = 0; | |||||
} | |||||
term = dumbterm; | |||||
} | |||||
term->_bufpos = 0; | |||||
/* Ensure we have an initial buffer */ | |||||
if (term->_buflen == 0) { | |||||
term->_buf = malloc(BUFINC); | |||||
if (term->_buf == NULL) | |||||
return NULL; | |||||
term->_buflen = BUFINC; | |||||
} | |||||
memset(&piss, 0, sizeof(piss)); | |||||
max = (size_t)_ti_parm_analyse(str, piss, TPARM_MAX); | |||||
/* Put our parameters into variables */ | |||||
memset(¶ms, 0, sizeof(params)); | |||||
for (l = 0; l < max; l++) { | |||||
if (piss[l]) { | |||||
if (va_type == VA_LONG_LONG) { | |||||
/* This only works if char * fits into a long | |||||
* on this platform. */ | |||||
if (sizeof(char *) <= sizeof(long)/*CONSTCOND*/) | |||||
params[l].string = | |||||
(char *)va_arg(parms, long); | |||||
else { | |||||
errno = ENOTSUP; | |||||
return NULL; | |||||
} | |||||
} else | |||||
params[l].string = va_arg(parms, char *); | |||||
} else { | |||||
if (va_type == VA_CHAR_INT) | |||||
params[l].num = (long)va_arg(parms, int); | |||||
else | |||||
params[l].num = va_arg(parms, long); | |||||
} | |||||
} | |||||
memset(&stack, 0, sizeof(stack)); | |||||
while ((c = *str++) != '\0') { | |||||
if (c != '%' || (c = *str++) == '%') { | |||||
if (c == '\0') | |||||
break; | |||||
if (ochar(term, c) == 0) | |||||
return NULL; | |||||
continue; | |||||
} | |||||
/* Handle formatting. */ | |||||
fp = fmt; | |||||
*fp++ = '%'; | |||||
done = dot = minus = width = precision = 0; | |||||
val = 0; | |||||
while (done == 0 && (size_t)(fp - fmt) < sizeof(fmt)) { | |||||
switch (c) { | |||||
case 'c': /* FALLTHROUGH */ | |||||
case 's': | |||||
*fp++ = c; | |||||
done = 1; | |||||
break; | |||||
case 'd': /* FALLTHROUGH */ | |||||
case 'o': /* FALLTHROUGH */ | |||||
case 'x': /* FALLTHROUGH */ | |||||
case 'X': /* FALLTHROUGH */ | |||||
*fp++ = 'l'; | |||||
*fp++ = c; | |||||
done = 1; | |||||
break; | |||||
case '#': /* FALLTHROUGH */ | |||||
case ' ': | |||||
*fp++ = c; | |||||
break; | |||||
case '.': | |||||
*fp++ = c; | |||||
if (dot == 0) { | |||||
dot = 1; | |||||
width = (size_t)val; | |||||
} else | |||||
done = 2; | |||||
val = 0; | |||||
break; | |||||
case ':': | |||||
minus = 1; | |||||
break; | |||||
case '-': | |||||
if (minus) | |||||
*fp++ = c; | |||||
else | |||||
done = 1; | |||||
break; | |||||
default: | |||||
if (isdigit((unsigned char)c)) { | |||||
val = (val * 10) + (c - '0'); | |||||
if (val > 10000) | |||||
done = 2; | |||||
else | |||||
*fp++ = c; | |||||
} else | |||||
done = 1; | |||||
} | |||||
if (done == 0) | |||||
c = *str++; | |||||
} | |||||
if (done == 2) { | |||||
/* Found an error in the format */ | |||||
fp = fmt + 1; | |||||
*fp = *str; | |||||
olen = 0; | |||||
} else { | |||||
if (dot == 0) | |||||
width = (size_t)val; | |||||
else | |||||
precision = (size_t)val; | |||||
olen = MAX(width, precision); | |||||
} | |||||
*fp++ = '\0'; | |||||
/* Handle commands */ | |||||
switch (c) { | |||||
case 'c': | |||||
pop(&val, NULL, &stack); | |||||
if (ochar(term, (unsigned char)val) == 0) | |||||
return NULL; | |||||
break; | |||||
case 's': | |||||
pop(NULL, &ostr, &stack); | |||||
if (ostr != NULL) { | |||||
int r; | |||||
l = strlen(ostr); | |||||
if (l < (size_t)olen) | |||||
l = olen; | |||||
if (checkbuf(term, (size_t)(l + 1)) == NULL) | |||||
return NULL; | |||||
r = snprintf(term->_buf + term->_bufpos, l + 1, | |||||
fmt, ostr); | |||||
if (r != -1) | |||||
term->_bufpos += (size_t)r; | |||||
} | |||||
break; | |||||
case 'l': | |||||
pop(NULL, &ostr, &stack); | |||||
if (ostr == NULL) | |||||
l = 0; | |||||
else | |||||
l = strlen(ostr); | |||||
#ifdef NCURSES_COMPAT_57 | |||||
if (onum(term, "%ld", (long)l, 0) == 0) | |||||
return NULL; | |||||
#else | |||||
push((long)l, NULL, &stack); | |||||
#endif | |||||
break; | |||||
case 'd': /* FALLTHROUGH */ | |||||
case 'o': /* FALLTHROUGH */ | |||||
case 'x': /* FALLTHROUGH */ | |||||
case 'X': | |||||
pop(&val, NULL, &stack); | |||||
if (onum(term, fmt, (int)val, olen) == 0) | |||||
return NULL; | |||||
break; | |||||
case 'p': | |||||
if (*str < '1' || *str > '9') | |||||
break; | |||||
l = (size_t)(*str++ - '1'); | |||||
if (push(params[l].num, params[l].string, &stack)) | |||||
return NULL; | |||||
break; | |||||
case 'P': | |||||
pop(&val, NULL, &stack); | |||||
if (*str >= 'a' && *str <= 'z') | |||||
dnums[*str - 'a'] = val; | |||||
else if (*str >= 'A' && *str <= 'Z') | |||||
term->_snums[*str - 'A'] = val; | |||||
break; | |||||
case 'g': | |||||
if (*str >= 'a' && *str <= 'z') { | |||||
if (push(dnums[*str - 'a'], NULL, &stack)) | |||||
return NULL; | |||||
} else if (*str >= 'A' && *str <= 'Z') { | |||||
if (push(term->_snums[*str - 'A'], | |||||
NULL, &stack)) | |||||
return NULL; | |||||
} | |||||
break; | |||||
case 'i': | |||||
if (piss[0] == 0) | |||||
params[0].num++; | |||||
if (piss[1] == 0) | |||||
params[1].num++; | |||||
break; | |||||
case '\'': | |||||
if (push((long)(unsigned char)*str++, NULL, &stack)) | |||||
return NULL; | |||||
while (*str != '\0' && *str != '\'') | |||||
str++; | |||||
if (*str == '\'') | |||||
str++; | |||||
break; | |||||
case '{': | |||||
val = 0; | |||||
for (; isdigit((unsigned char)*str); str++) | |||||
val = (val * 10) + (*str - '0'); | |||||
if (push(val, NULL, &stack)) | |||||
return NULL; | |||||
while (*str != '\0' && *str != '}') | |||||
str++; | |||||
if (*str == '}') | |||||
str++; | |||||
break; | |||||
case '+': /* FALLTHROUGH */ | |||||
case '-': /* FALLTHROUGH */ | |||||
case '*': /* FALLTHROUGH */ | |||||
case '/': /* FALLTHROUGH */ | |||||
case 'm': /* FALLTHROUGH */ | |||||
case 'A': /* FALLTHROUGH */ | |||||
case 'O': /* FALLTHROUGH */ | |||||
case '&': /* FALLTHROUGH */ | |||||
case '|': /* FALLTHROUGH */ | |||||
case '^': /* FALLTHROUGH */ | |||||
case '=': /* FALLTHROUGH */ | |||||
case '<': /* FALLTHROUGH */ | |||||
case '>': | |||||
pop(&val, NULL, &stack); | |||||
pop(&val2, NULL, &stack); | |||||
switch (c) { | |||||
case '+': | |||||
val = val + val2; | |||||
break; | |||||
case '-': | |||||
val = val2 - val; | |||||
break; | |||||
case '*': | |||||
val = val * val2; | |||||
break; | |||||
case '/': | |||||
val = val ? val2 / val : 0; | |||||
break; | |||||
case 'm': | |||||
val = val ? val2 % val : 0; | |||||
break; | |||||
case 'A': | |||||
val = val && val2; | |||||
break; | |||||
case 'O': | |||||
val = val || val2; | |||||
break; | |||||
case '&': | |||||
val = val & val2; | |||||
break; | |||||
case '|': | |||||
val = val | val2; | |||||
break; | |||||
case '^': | |||||
val = val ^ val2; | |||||
break; | |||||
case '=': | |||||
val = val == val2; | |||||
break; | |||||
case '<': | |||||
val = val2 < val; | |||||
break; | |||||
case '>': | |||||
val = val2 > val; | |||||
break; | |||||
} | |||||
if (push(val, NULL, &stack)) | |||||
return NULL; | |||||
break; | |||||
case '!': | |||||
case '~': | |||||
pop(&val, NULL, &stack); | |||||
switch (c) { | |||||
case '!': | |||||
val = !val; | |||||
break; | |||||
case '~': | |||||
val = ~val; | |||||
break; | |||||
} | |||||
if (push(val, NULL, &stack)) | |||||
return NULL; | |||||
break; | |||||
case '?': /* if */ | |||||
break; | |||||
case 't': /* then */ | |||||
pop(&val, NULL, &stack); | |||||
if (val == 0) { | |||||
l = 0; | |||||
for (; *str != '\0'; str++) { | |||||
if (*str != '%') | |||||
continue; | |||||
str++; | |||||
if (*str == '?') | |||||
l++; | |||||
else if (*str == ';') { | |||||
if (l > 0) | |||||
l--; | |||||
else { | |||||
str++; | |||||
break; | |||||
} | |||||
} else if (*str == 'e' && l == 0) { | |||||
str++; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
break; | |||||
case 'e': /* else */ | |||||
l = 0; | |||||
for (; *str != '\0'; str++) { | |||||
if (*str != '%') | |||||
continue; | |||||
str++; | |||||
if (*str == '?') | |||||
l++; | |||||
else if (*str == ';') { | |||||
if (l > 0) | |||||
l--; | |||||
else { | |||||
str++; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
break; | |||||
case ';': /* fi */ | |||||
break; | |||||
} | |||||
} | |||||
term->_buf[term->_bufpos] = '\0'; | |||||
return term->_buf; | |||||
} | |||||
char * | |||||
ti_tiparm(TERMINAL *term, const char *str, ...) | |||||
{ | |||||
va_list va; | |||||
char *ret; | |||||
va_start(va, str); | |||||
ret = _ti_tiparm(term, str, VA_CHAR_INT, va); | |||||
va_end(va); | |||||
return ret; | |||||
} | |||||
char * | |||||
tiparm(const char *str, ...) | |||||
{ | |||||
va_list va; | |||||
char *ret; | |||||
va_start(va, str); | |||||
ret = _ti_tiparm(NULL, str, VA_CHAR_INT, va); | |||||
va_end(va); | |||||
return ret; | |||||
} | |||||
#ifdef VA_CHAR_LONG | |||||
char * | |||||
ti_tlparm(TERMINAL *term, const char *str, ...) | |||||
{ | |||||
va_list va; | |||||
char *ret; | |||||
va_start(va, str); | |||||
ret = _ti_tiparm(term, str, VA_CHAR_LONG, va); | |||||
va_end(va); | |||||
return ret; | |||||
} | |||||
char * | |||||
tlparm(const char *str, ...) | |||||
{ | |||||
va_list va; | |||||
char *ret; | |||||
va_start(va, str); | |||||
ret = _ti_tiparm(NULL, str, VA_CHAR_LONG, va); | |||||
va_end(va); | |||||
return ret; | |||||
} | |||||
#endif | |||||
static char * | |||||
_tparm(const char *str, ...) | |||||
{ | |||||
va_list va; | |||||
char *ret; | |||||
va_start(va, str); | |||||
ret = _ti_tiparm(NULL, str, VA_LONG_LONG, va); | |||||
va_end(va); | |||||
return ret; | |||||
} | |||||
char * | |||||
tparm(const char *str, | |||||
long p1, long p2, long p3, long p4, long p5, | |||||
long p6, long p7, long p8, long p9) | |||||
{ | |||||
return _tparm(str, p1, p2, p3, p4, p5, p6, p7, p8, p9); | |||||
} |