/****************************************************************************
- * Copyright (c) 1998 Free Software Foundation, Inc. *
+ * Copyright (c) 1998,2000 Free Software Foundation, Inc. *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the *
* and: Eric S. Raymond <esr@snark.thyrsus.com> *
****************************************************************************/
-
/*
* tparm.c
*
#include <term.h>
#include <tic.h>
-MODULE_ID("$Id: lib_tparm.c,v 1.39 1999/06/06 00:04:55 tom Exp $")
+MODULE_ID("$Id: lib_tparm.c,v 1.48 2000/10/14 17:45:00 Sergei.Ivanov Exp $")
/*
* char *
* %s print pop() like %s in printf()
* %[[:]flags][width[.precision]][doxXs]
* as in printf, flags are [-+#] and space
+ * The ':' is used to avoid making %+ or %-
+ * patterns (see below).
*
* %p[1-9] push ith parm
* %P[a-z] set dynamic variable [a-z] to pop()
#define STACKSIZE 20
-typedef union {
- unsigned int num;
- char *str;
+typedef struct {
+ union {
+ unsigned int num;
+ char *str;
+ } data;
+ bool num_type;
} stack_frame;
-static stack_frame stack[STACKSIZE];
-static int stack_ptr;
+static stack_frame stack[STACKSIZE];
+static int stack_ptr;
+
#ifdef TRACE
static const char *tname;
#endif /* TRACE */
-static char *out_buff;
+static char *out_buff;
static size_t out_size;
static size_t out_used;
#if NO_LEAKS
-void _nc_free_tparm(void)
+void
+_nc_free_tparm(void)
{
- if (out_buff != 0) {
- FreeAndNull(out_buff);
- out_size = 0;
- out_used = 0;
- }
+ if (out_buff != 0) {
+ FreeAndNull(out_buff);
+ out_size = 0;
+ out_used = 0;
+ }
}
#endif
-static void really_get_space(size_t need)
+static void
+really_get_space(size_t need)
{
- out_size = need * 2;
- out_buff = typeRealloc(char, out_size, out_buff);
- if (out_buff == 0)
- _nc_err_abort("Out of memory");
+ out_size = need * 2;
+ out_buff = typeRealloc(char, out_size, out_buff);
+ if (out_buff == 0)
+ _nc_err_abort("Out of memory");
}
-static inline void get_space(size_t need)
+static inline void
+get_space(size_t need)
{
- need += out_used;
- if (need > out_size)
- really_get_space(need);
+ need += out_used;
+ if (need > out_size)
+ really_get_space(need);
}
-static inline void save_text(const char *fmt, char *s, int len)
+static inline void
+save_text(const char *fmt, const char *s, int len)
{
- size_t s_len = strlen(s);
- if (len > (int)s_len)
- s_len = len;
+ size_t s_len = strlen(s);
+ if (len > (int) s_len)
+ s_len = len;
- get_space(s_len + 1);
+ get_space(s_len + 1);
- (void)sprintf(out_buff+out_used, fmt, s);
- out_used += strlen(out_buff+out_used);
+ (void) sprintf(out_buff + out_used, fmt, s);
+ out_used += strlen(out_buff + out_used);
}
-static inline void save_number(const char *fmt, int number, int len)
+static inline void
+save_number(const char *fmt, int number, int len)
{
- if (len < 30)
- len = 30; /* actually log10(MAX_INT)+1 */
+ if (len < 30)
+ len = 30; /* actually log10(MAX_INT)+1 */
- get_space(len + 1);
+ get_space(len + 1);
- (void)sprintf(out_buff+out_used, fmt, number);
- out_used += strlen(out_buff+out_used);
+ (void) sprintf(out_buff + out_used, fmt, number);
+ out_used += strlen(out_buff + out_used);
}
-static inline void save_char(int c)
+static inline void
+save_char(int c)
{
- if (c == 0)
- c = 0200;
- get_space(1);
- out_buff[out_used++] = c;
+ if (c == 0)
+ c = 0200;
+ get_space(1);
+ out_buff[out_used++] = c;
}
-static inline void npush(int x)
+static inline void
+npush(int x)
{
- if (stack_ptr < STACKSIZE) {
- stack[stack_ptr].num = x;
- stack_ptr++;
- }
+ if (stack_ptr < STACKSIZE) {
+ stack[stack_ptr].num_type = TRUE;
+ stack[stack_ptr].data.num = x;
+ stack_ptr++;
+ }
}
-static inline int npop(void)
+static inline int
+npop(void)
{
- return (stack_ptr > 0 ? stack[--stack_ptr].num : 0);
+ int result = 0;
+ if (stack_ptr > 0) {
+ stack_ptr--;
+ if (stack[stack_ptr].num_type)
+ result = stack[stack_ptr].data.num;
+ }
+ return result;
}
-static inline char *spop(void)
+static inline void
+spush(char *x)
{
- static char dummy[] = ""; /* avoid const-cast */
- return (stack_ptr > 0 ? stack[--stack_ptr].str : dummy);
+ if (stack_ptr < STACKSIZE) {
+ stack[stack_ptr].num_type = FALSE;
+ stack[stack_ptr].data.str = x;
+ stack_ptr++;
+ }
}
-static inline const char *parse_format(const char *s, char *format, int *len)
+static inline char *
+spop(void)
{
- bool done = FALSE;
- bool allowminus = FALSE;
- bool dot = FALSE;
- int prec = 0;
- int width = 0;
+ static char dummy[] = ""; /* avoid const-cast */
+ char *result = dummy;
+ if (stack_ptr > 0) {
+ stack_ptr--;
+ if (!stack[stack_ptr].num_type && stack[stack_ptr].data.str != 0)
+ result = stack[stack_ptr].data.str;
+ }
+ return result;
+}
- *len = 0;
- *format++ = '%';
- while (*s != '\0' && !done) {
- switch (*s) {
- case 'c': /* FALLTHRU */
- case 'd': /* FALLTHRU */
- case 'o': /* FALLTHRU */
- case 'x': /* FALLTHRU */
- case 'X': /* FALLTHRU */
- case 's':
- *format++ = *s;
- done = TRUE;
- break;
- case '.':
- *format++ = *s++;
- dot = TRUE;
- break;
- case '#':
- *format++ = *s++;
- break;
- case ' ':
- *format++ = *s++;
- break;
- case ':':
- s++;
- allowminus = TRUE;
- break;
- case '-':
- if (allowminus) {
- *format++ = *s++;
- } else {
- done = TRUE;
- }
- break;
- default:
- if (isdigit(*s)) {
- if (dot)
- prec = (prec * 10) + (*s - '0');
- else
- width = (width * 10) + (*s - '0');
- *format++ = *s++;
- } else {
- done = TRUE;
- }
- }
+static inline const char *
+parse_format(const char *s, char *format, int *len)
+{
+ bool done = FALSE;
+ bool allowminus = FALSE;
+ bool dot = FALSE;
+ bool err = FALSE;
+ char *fmt = format;
+ int prec = 0;
+ int width = 0;
+ int value = 0;
+
+ *len = 0;
+ *format++ = '%';
+ while (*s != '\0' && !done) {
+ switch (*s) {
+ case 'c': /* FALLTHRU */
+ case 'd': /* FALLTHRU */
+ case 'o': /* FALLTHRU */
+ case 'x': /* FALLTHRU */
+ case 'X': /* FALLTHRU */
+ case 's':
+ *format++ = *s;
+ done = TRUE;
+ break;
+ case '.':
+ *format++ = *s++;
+ if (dot) {
+ err = TRUE;
+ } else {
+ dot = TRUE;
+ prec = value;
+ }
+ value = 0;
+ break;
+ case '#':
+ *format++ = *s++;
+ break;
+ case ' ':
+ *format++ = *s++;
+ break;
+ case ':':
+ s++;
+ allowminus = TRUE;
+ break;
+ case '-':
+ if (allowminus) {
+ *format++ = *s++;
+ } else {
+ done = TRUE;
+ }
+ break;
+ default:
+ if (isdigit(*s)) {
+ value = (value * 10) + (*s - '0');
+ if (value > 10000)
+ err = TRUE;
+ *format++ = *s++;
+ } else {
+ done = TRUE;
+ }
}
- *format = '\0';
- /* return maximum string length in print */
- *len = (prec > width) ? prec : width;
- return s;
+ }
+
+ /*
+ * If we found an error, ignore (and remove) the flags.
+ */
+ if (err) {
+ prec = width = value = 0;
+ format = fmt;
+ *format++ = '%';
+ *format++ = *s;
+ }
+
+ if (dot)
+ width = value;
+ else
+ prec = value;
+
+ *format = '\0';
+ /* return maximum string length in print */
+ *len = (prec > width) ? prec : width;
+ return s;
}
#define isUPPER(c) ((c) >= 'A' && (c) <= 'Z')
#define isLOWER(c) ((c) >= 'a' && (c) <= 'z')
-static inline char *tparam_internal(const char *string, va_list ap)
+static inline char *
+tparam_internal(const char *string, va_list ap)
{
#define NUM_VARS 26
-int param[9];
-int popcount;
-int number;
-int len;
-int level;
-int x, y;
-int i;
-register const char *cp;
-static size_t len_fmt;
-static char *format;
-static int dynamic_var[NUM_VARS];
-static int static_vars[NUM_VARS];
-
- out_used = 0;
- if (string == NULL)
- return NULL;
-
- /*
- * Find the highest parameter-number referred to in the format string.
- * Use this value to limit the number of arguments copied from the
- * variable-length argument list.
- */
- for (cp = string, popcount = number = 0; *cp != '\0'; cp++) {
- if (cp[0] == '%' && cp[1] != '\0') {
- switch (cp[1]) {
- case '%':
- cp++;
- break;
- case 'i':
- if (popcount < 2)
- popcount = 2;
- break;
- case 'p':
- cp++;
- if (cp[1] >= '1' && cp[1] <= '9') {
- int c = cp[1] - '0';
- if (c > popcount)
- popcount = c;
- }
- break;
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- case 'd': case 'c': case 's':
- ++number;
- break;
- }
+ char *p_is_s[9];
+ int param[9];
+ int lastpop;
+ int popcount;
+ int number;
+ int len;
+ int level;
+ int x, y;
+ int i;
+ size_t len2;
+ register const char *cp;
+ static size_t len_fmt;
+ static char dummy[] = "";
+ static char *format;
+ static int dynamic_var[NUM_VARS];
+ static int static_vars[NUM_VARS];
+
+ out_used = 0;
+ if (string == NULL)
+ return NULL;
+
+ if ((len2 = strlen(string)) > len_fmt) {
+ len_fmt = len2 + len_fmt + 2;
+ if ((format = typeRealloc(char, len_fmt, format)) == 0)
+ return 0;
+ }
+
+ /*
+ * Find the highest parameter-number referred to in the format string.
+ * Use this value to limit the number of arguments copied from the
+ * variable-length argument list.
+ */
+
+ number = 0;
+ lastpop = -1;
+ popcount = 0;
+ memset(p_is_s, 0, sizeof(p_is_s));
+
+ /*
+ * Analyze the string to see how many parameters we need from the varargs
+ * list, and what their types are. We will only accept string parameters
+ * if they appear as a %l or %s format following an explicit parameter
+ * reference (e.g., %p2%s). All other parameters are numbers.
+ *
+ * 'number' counts coarsely the number of pop's we see in the string, and
+ * 'popcount' shows the highest parameter number in the string. We would
+ * like to simply use the latter count, but if we are reading termcap
+ * strings, there may be cases that we cannot see the explicit parameter
+ * numbers.
+ */
+ for (cp = string; (cp - string) < (int) len2;) {
+ if (*cp == '%') {
+ cp++;
+ cp = parse_format(cp, format, &len);
+ switch (*cp) {
+ default:
+ break;
+
+ case 'd': /* FALLTHRU */
+ case 'o': /* FALLTHRU */
+ case 'x': /* FALLTHRU */
+ case 'X': /* FALLTHRU */
+ case 'c': /* FALLTHRU */
+ number++;
+ lastpop = -1;
+ break;
+
+ case 'l':
+ case 's':
+ if (lastpop > 0)
+ p_is_s[lastpop - 1] = dummy;
+ ++number;
+ break;
+
+ case 'p':
+ cp++;
+ i = (*cp - '0');
+ if (i >= 0 && i <= 9) {
+ lastpop = i;
+ if (lastpop > popcount)
+ popcount = lastpop;
}
+ break;
+
+ case 'P':
+ case 'g':
+ cp++;
+ break;
+
+ case S_QUOTE:
+ cp += 2;
+ lastpop = -1;
+ break;
+
+ case L_BRACE:
+ cp++;
+ while (*cp >= '0' && *cp <= '9') {
+ cp++;
+ }
+ break;
+
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case 'm':
+ case 'A':
+ case 'O':
+ case '&':
+ case '|':
+ case '^':
+ case '=':
+ case '<':
+ case '>':
+ case '!':
+ case '~':
+ lastpop = -1;
+ number += 2;
+ break;
+
+ case 'i':
+ lastpop = -1;
+ if (popcount < 2)
+ popcount = 2;
+ break;
+ }
}
- if ((size_t)(cp - string) > len_fmt) {
- len_fmt = (cp - string) + len_fmt + 2;
- if ((format = typeRealloc(char, len_fmt, format)) == 0)
- return 0;
- }
-
- if (number > 9) number = 9;
- for (i = 0; i < max(popcount, number); i++) {
- /*
- * FIXME: potential loss here if sizeof(int) != sizeof(char *).
- * A few caps (such as plab_norm) have string-valued parms.
- */
- param[i] = va_arg(ap, int);
- }
+ if (*cp != '\0')
+ cp++;
+ }
+ if (number > 9)
+ number = 9;
+ for (i = 0; i < max(popcount, number); i++) {
/*
- * This is a termcap compatibility hack. If there are no explicit pop
- * operations in the string, load the stack in such a way that
- * successive pops will grab successive parameters. That will make
- * the expansion of (for example) \E[%d;%dH work correctly in termcap
- * style, which means tparam() will expand termcap strings OK.
+ * A few caps (such as plab_norm) have string-valued parms.
+ * We'll have to assume that the caller knows the difference, since
+ * a char* and an int may not be the same size on the stack.
*/
- stack_ptr = 0;
- if (popcount == 0) {
- popcount = number;
- for (i = number - 1; i >= 0; i--)
- npush(param[i]);
+ if (p_is_s[i] != 0) {
+ p_is_s[i] = va_arg(ap, char *);
+ } else {
+ param[i] = va_arg(ap, int);
}
-
+ }
+
+ /*
+ * This is a termcap compatibility hack. If there are no explicit pop
+ * operations in the string, load the stack in such a way that
+ * successive pops will grab successive parameters. That will make
+ * the expansion of (for example) \E[%d;%dH work correctly in termcap
+ * style, which means tparam() will expand termcap strings OK.
+ */
+ stack_ptr = 0;
+ if (popcount == 0) {
+ popcount = number;
+ for (i = number - 1; i >= 0; i--)
+ npush(param[i]);
+ }
#ifdef TRACE
- if (_nc_tracing & TRACE_CALLS) {
- for (i = 0; i < popcount; i++)
- save_number(", %d", param[i], 0);
- _tracef(T_CALLED("%s(%s%s)"), tname, _nc_visbuf(string), out_buff);
- out_used = 0;
+ if (_nc_tracing & TRACE_CALLS) {
+ for (i = 0; i < popcount; i++) {
+ if (p_is_s[i] != 0)
+ save_text(", %s", _nc_visbuf(p_is_s[i]), 0);
+ else
+ save_number(", %d", param[i], 0);
}
+ _tracef(T_CALLED("%s(%s%s)"), tname, _nc_visbuf(string), out_buff);
+ out_used = 0;
+ }
#endif /* TRACE */
- while (*string) {
- if (*string != '%') {
- save_char(*string);
- } else {
- string++;
- string = parse_format(string, format, &len);
- switch (*string) {
- default:
- break;
- case '%':
- save_char('%');
- break;
-
- case 'd': /* FALLTHRU */
- case 'o': /* FALLTHRU */
- case 'x': /* FALLTHRU */
- case 'X': /* FALLTHRU */
- case 'c':
- save_number(format, npop(), len);
- break;
-
- case 'l':
- save_number("%d", strlen(spop()), 0);
- break;
-
- case 's':
- save_text(format, spop(), len);
- break;
-
- case 'p':
- string++;
- if (*string >= '1' && *string <= '9')
- npush(param[*string - '1']);
- break;
-
- case 'P':
- string++;
- if (isUPPER(*string)) {
- i = (*string - 'A');
- static_vars[i] = npop();
- } else if (isLOWER(*string)) {
- i = (*string - 'a');
- dynamic_var[i] = npop();
- }
- break;
-
- case 'g':
- string++;
- if (isUPPER(*string)) {
- i = (*string - 'A');
- npush(static_vars[i]);
- } else if (isLOWER(*string)) {
- i = (*string - 'a');
- npush(dynamic_var[i]);
- }
- break;
-
- case S_QUOTE:
- string++;
- npush(*string);
- string++;
- break;
-
- case L_BRACE:
- number = 0;
- string++;
- while (*string >= '0' && *string <= '9') {
- number = number * 10 + *string - '0';
- string++;
- }
- npush(number);
- break;
-
- case '+':
- npush(npop() + npop());
- break;
-
- case '-':
- y = npop();
- x = npop();
- npush(x - y);
- break;
-
- case '*':
- npush(npop() * npop());
- break;
-
- case '/':
- y = npop();
- x = npop();
- npush(y ? (x / y) : 0);
- break;
-
- case 'm':
- y = npop();
- x = npop();
- npush(y ? (x % y) : 0);
- break;
-
- case 'A':
- npush(npop() && npop());
- break;
-
- case 'O':
- npush(npop() || npop());
- break;
-
- case '&':
- npush(npop() & npop());
- break;
-
- case '|':
- npush(npop() | npop());
- break;
-
- case '^':
- npush(npop() ^ npop());
- break;
-
- case '=':
- y = npop();
- x = npop();
- npush(x == y);
- break;
-
- case '<':
- y = npop();
- x = npop();
- npush(x < y);
- break;
+ while (*string) {
+ if (*string != '%') {
+ save_char(*string);
+ } else {
+ string++;
+ string = parse_format(string, format, &len);
+ switch (*string) {
+ default:
+ break;
+ case '%':
+ save_char('%');
+ break;
+
+ case 'd': /* FALLTHRU */
+ case 'o': /* FALLTHRU */
+ case 'x': /* FALLTHRU */
+ case 'X': /* FALLTHRU */
+ case 'c': /* FALLTHRU */
+ save_number(format, npop(), len);
+ break;
+
+ case 'l':
+ save_number("%d", strlen(spop()), 0);
+ break;
+
+ case 's':
+ save_text(format, spop(), len);
+ break;
+
+ case 'p':
+ string++;
+ i = (*string - '1');
+ if (i >= 0 && i < 9) {
+ if (p_is_s[i])
+ spush(p_is_s[i]);
+ else
+ npush(param[i]);
+ }
+ break;
- case '>':
- y = npop();
- x = npop();
- npush(x > y);
- break;
+ case 'P':
+ string++;
+ if (isUPPER(*string)) {
+ i = (*string - 'A');
+ static_vars[i] = npop();
+ } else if (isLOWER(*string)) {
+ i = (*string - 'a');
+ dynamic_var[i] = npop();
+ }
+ break;
- case '!':
- npush(! npop());
- break;
+ case 'g':
+ string++;
+ if (isUPPER(*string)) {
+ i = (*string - 'A');
+ npush(static_vars[i]);
+ } else if (isLOWER(*string)) {
+ i = (*string - 'a');
+ npush(dynamic_var[i]);
+ }
+ break;
- case '~':
- npush(~ npop());
- break;
+ case S_QUOTE:
+ string++;
+ npush(*string);
+ string++;
+ break;
- case 'i':
- param[0]++;
- param[1]++;
+ case L_BRACE:
+ number = 0;
+ string++;
+ while (*string >= '0' && *string <= '9') {
+ number = number * 10 + *string - '0';
+ string++;
+ }
+ npush(number);
+ break;
+
+ case '+':
+ npush(npop() + npop());
+ break;
+
+ case '-':
+ y = npop();
+ x = npop();
+ npush(x - y);
+ break;
+
+ case '*':
+ npush(npop() * npop());
+ break;
+
+ case '/':
+ y = npop();
+ x = npop();
+ npush(y ? (x / y) : 0);
+ break;
+
+ case 'm':
+ y = npop();
+ x = npop();
+ npush(y ? (x % y) : 0);
+ break;
+
+ case 'A':
+ npush(npop() && npop());
+ break;
+
+ case 'O':
+ npush(npop() || npop());
+ break;
+
+ case '&':
+ npush(npop() & npop());
+ break;
+
+ case '|':
+ npush(npop() | npop());
+ break;
+
+ case '^':
+ npush(npop() ^ npop());
+ break;
+
+ case '=':
+ y = npop();
+ x = npop();
+ npush(x == y);
+ break;
+
+ case '<':
+ y = npop();
+ x = npop();
+ npush(x < y);
+ break;
+
+ case '>':
+ y = npop();
+ x = npop();
+ npush(x > y);
+ break;
+
+ case '!':
+ npush(!npop());
+ break;
+
+ case '~':
+ npush(~npop());
+ break;
+
+ case 'i':
+ if (p_is_s[0] == 0)
+ param[0]++;
+ if (p_is_s[1] == 0)
+ param[1]++;
+ break;
+
+ case '?':
+ break;
+
+ case 't':
+ x = npop();
+ if (!x) {
+ /* scan forward for %e or %; at level zero */
+ string++;
+ level = 0;
+ while (*string) {
+ if (*string == '%') {
+ string++;
+ if (*string == '?')
+ level++;
+ else if (*string == ';') {
+ if (level > 0)
+ level--;
+ else
+ break;
+ } else if (*string == 'e' && level == 0)
break;
+ }
- case '?':
- break;
+ if (*string)
+ string++;
+ }
+ }
+ break;
- case 't':
- x = npop();
- if (!x) {
- /* scan forward for %e or %; at level zero */
- string++;
- level = 0;
- while (*string) {
- if (*string == '%') {
- string++;
- if (*string == '?')
- level++;
- else if (*string == ';') {
- if (level > 0)
- level--;
- else
- break;
- }
- else if (*string == 'e' && level == 0)
- break;
- }
-
- if (*string)
- string++;
- }
- }
+ case 'e':
+ /* scan forward for a %; at level zero */
+ string++;
+ level = 0;
+ while (*string) {
+ if (*string == '%') {
+ string++;
+ if (*string == '?')
+ level++;
+ else if (*string == ';') {
+ if (level > 0)
+ level--;
+ else
break;
+ }
+ }
- case 'e':
- /* scan forward for a %; at level zero */
- string++;
- level = 0;
- while (*string) {
- if (*string == '%') {
- string++;
- if (*string == '?')
- level++;
- else if (*string == ';') {
- if (level > 0)
- level--;
- else
- break;
- }
- }
-
- if (*string)
- string++;
- }
- break;
+ if (*string)
+ string++;
+ }
+ break;
- case ';':
- break;
+ case ';':
+ break;
- } /* endswitch (*string) */
- } /* endelse (*string == '%') */
+ } /* endswitch (*string) */
+ } /* endelse (*string == '%') */
- if (*string == '\0')
- break;
+ if (*string == '\0')
+ break;
- string++;
- } /* endwhile (*string) */
+ string++;
+ } /* endwhile (*string) */
- if (out_buff == 0 && (out_buff = typeCalloc(char,1)) == NULL)
- return(NULL);
- out_buff[out_used] = '\0';
+ get_space(1);
+ out_buff[out_used] = '\0';
- T((T_RETURN("%s"), _nc_visbuf(out_buff)));
- return(out_buff);
+ T((T_RETURN("%s"), _nc_visbuf(out_buff)));
+ return (out_buff);
}
-char *tparm(NCURSES_CONST char *string, ...)
+char *
+tparm(NCURSES_CONST char *string,...)
{
-va_list ap;
-char *result;
+ va_list ap;
+ char *result;
- va_start(ap, string);
+ va_start(ap, string);
#ifdef TRACE
- tname = "tparm";
+ tname = "tparm";
#endif /* TRACE */
- result = tparam_internal(string, ap);
- va_end(ap);
- return result;
+ result = tparam_internal(string, ap);
+ va_end(ap);
+ return result;
}