X-Git-Url: https://ncurses.scripts.mit.edu/?p=ncurses.git;a=blobdiff_plain;f=ncurses%2Ftinfo%2Flib_tparm.c;h=3fe42399855215f9ba1bc42408afdf21cc42658d;hp=6df1e2d085722d3a1bc7222aab7fffb89011d3e0;hb=81304798ee736c467839c779c9ca5dca48db7bea;hpb=46722468f47c2b77b3987729b4bcf2321cccfd01 diff --git a/ncurses/tinfo/lib_tparm.c b/ncurses/tinfo/lib_tparm.c index 6df1e2d0..3fe42399 100644 --- a/ncurses/tinfo/lib_tparm.c +++ b/ncurses/tinfo/lib_tparm.c @@ -1,5 +1,6 @@ /**************************************************************************** - * Copyright (c) 1998-2001,2002 Free Software Foundation, Inc. * + * Copyright 2018-2020,2021 Thomas E. Dickey * + * Copyright 1998-2016,2017 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 * @@ -37,13 +38,22 @@ * */ +#define entry _ncu_entry +#define ENTRY _ncu_ENTRY + #include +#undef entry +#undef ENTRY + +#if HAVE_TSEARCH +#include +#endif + #include -#include #include -MODULE_ID("$Id: lib_tparm.c,v 1.62 2002/10/05 19:33:24 Frank.Henigman Exp $") +MODULE_ID("$Id: lib_tparm.c,v 1.131 2021/04/03 22:05:59 tom Exp $") /* * char * @@ -54,7 +64,7 @@ MODULE_ID("$Id: lib_tparm.c,v 1.62 2002/10/05 19:33:24 Frank.Henigman Exp $") * * Cursor addressing and other strings requiring parame- * ters in the terminal are described by a parameterized string - * capability, with like escapes %x in it. For example, to + * capability, with escapes like %x in it. For example, to * address the cursor, the cup capability is given, using two * parameters: the row and column to address to. (Rows and * columns are numbered from zero and refer to the physical @@ -105,295 +115,325 @@ MODULE_ID("$Id: lib_tparm.c,v 1.62 2002/10/05 19:33:24 Frank.Henigman Exp $") * resulting in x mod y, not the reverse. */ -#define STACKSIZE 20 +NCURSES_EXPORT_VAR(int) _nc_tparm_err = 0; -typedef struct { - union { - int num; - char *str; - } data; - bool num_type; -} stack_frame; +#define TPS(var) _nc_prescreen.tparm_state.var +#define popcount _nc_popcount /* workaround for NetBSD 6.0 defect */ -NCURSES_EXPORT_VAR(int) _nc_tparm_err = 0; +#define isUPPER(c) ((c) >= 'A' && (c) <= 'Z') +#define isLOWER(c) ((c) >= 'a' && (c) <= 'z') +#define tc_BUMP() if (level < 0 && number < 2) number++ -static stack_frame stack[STACKSIZE]; -static int stack_ptr; -static const char *tparam_base = ""; +typedef struct { + const char *format; /* format-string can be used as cache-key */ + int tparm_type; /* bit-set for each string-parameter */ + int num_actual; + int num_parsed; + int num_popped; + TPARM_ARG param[NUM_PARM]; + char *p_is_s[NUM_PARM]; +} TPARM_DATA; + +#if HAVE_TSEARCH +#define MyCache _nc_globals.cached_tparm +#define MyCount _nc_globals.count_tparm +#if NO_LEAKS +static int which_tparm; +static TPARM_DATA **delete_tparm; +#endif +#endif /* HAVE_TSEARCH */ -#ifdef TRACE -static const char *tname; -#endif /* TRACE */ +static char dummy[] = ""; /* avoid const-cast */ -static char *out_buff; -static size_t out_size; -static size_t out_used; +#if HAVE_TSEARCH +static int +cmp_format(const void *p, const void *q) +{ + const char *a = *(char *const *) p; + const char *b = *(char *const *) q; + return strcmp(a, b); +} +#endif #if NO_LEAKS +#if HAVE_TSEARCH +static void +visit_nodes(const void *nodep, const VISIT which, const int depth) +{ + (void) depth; + if (which == preorder || which == leaf) { + delete_tparm[which_tparm] = *(TPARM_DATA **) nodep; + which_tparm++; + } +} +#endif + NCURSES_EXPORT(void) _nc_free_tparm(void) { - if (out_buff != 0) { - FreeAndNull(out_buff); - out_size = 0; - out_used = 0; +#if HAVE_TSEARCH + if (MyCount != 0) { + delete_tparm = typeCalloc(TPARM_DATA *, MyCount); + which_tparm = 0; + twalk(MyCache, visit_nodes); + for (which_tparm = 0; which_tparm < MyCount; ++which_tparm) { + TPARM_DATA *ptr = delete_tparm[which_tparm]; + if (ptr != NULL) { + tdelete(ptr, &MyCache, cmp_format); + free((char *) ptr->format); + free(ptr); + } + } + which_tparm = 0; + twalk(MyCache, visit_nodes); + FreeAndNull(delete_tparm); + MyCount = 0; + which_tparm = 0; } +#endif + FreeAndNull(TPS(out_buff)); + TPS(out_size) = 0; + TPS(out_used) = 0; + + FreeAndNull(TPS(fmt_buff)); + TPS(fmt_size) = 0; } #endif -static inline void +static NCURSES_INLINE void get_space(size_t need) { - need += out_used; - if (need > out_size) { - out_size = need * 2; - out_buff = typeRealloc(char, out_size, out_buff); - if (out_buff == 0) - _nc_err_abort(MSG_NO_MEMORY); + need += TPS(out_used); + if (need > TPS(out_size)) { + TPS(out_size) = need * 2; + TYPE_REALLOC(char, TPS(out_size), TPS(out_buff)); } } -static inline void +static NCURSES_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 = (size_t) len + strlen(s) + strlen(fmt); get_space(s_len + 1); - (void) sprintf(out_buff + out_used, fmt, s); - out_used += strlen(out_buff + out_used); + _nc_SPRINTF(TPS(out_buff) + TPS(out_used), + _nc_SLIMIT(TPS(out_size) - TPS(out_used)) + fmt, s); + TPS(out_used) += strlen(TPS(out_buff) + TPS(out_used)); } -static inline void +static NCURSES_INLINE void save_number(const char *fmt, int number, int len) { - if (len < 30) - len = 30; /* actually log10(MAX_INT)+1 */ - - get_space((unsigned) len + 1); + size_t s_len = (size_t) len + 30 + strlen(fmt); + get_space(s_len + 1); - (void) sprintf(out_buff + out_used, fmt, number); - out_used += strlen(out_buff + out_used); + _nc_SPRINTF(TPS(out_buff) + TPS(out_used), + _nc_SLIMIT(TPS(out_size) - TPS(out_used)) + fmt, number); + TPS(out_used) += strlen(TPS(out_buff) + TPS(out_used)); } -static inline void +static NCURSES_INLINE void save_char(int c) { if (c == 0) c = 0200; - get_space(1); - out_buff[out_used++] = c; + get_space((size_t) 1); + TPS(out_buff)[TPS(out_used)++] = (char) c; } -static inline void +static NCURSES_INLINE void npush(int x) { - if (stack_ptr < STACKSIZE) { - stack[stack_ptr].num_type = TRUE; - stack[stack_ptr].data.num = x; - stack_ptr++; + if (TPS(stack_ptr) < STACKSIZE) { + TPS(stack)[TPS(stack_ptr)].num_type = TRUE; + TPS(stack)[TPS(stack_ptr)].data.num = x; + TPS(stack_ptr)++; } else { - DEBUG(2, ("npush: stack overflow: %s", _nc_visbuf(tparam_base))); + DEBUG(2, ("npush: stack overflow: %s", _nc_visbuf(TPS(tparam_base)))); _nc_tparm_err++; } } -static inline int +static NCURSES_INLINE int npop(void) { int result = 0; - if (stack_ptr > 0) { - stack_ptr--; - if (stack[stack_ptr].num_type) - result = stack[stack_ptr].data.num; + if (TPS(stack_ptr) > 0) { + TPS(stack_ptr)--; + if (TPS(stack)[TPS(stack_ptr)].num_type) + result = TPS(stack)[TPS(stack_ptr)].data.num; } else { - DEBUG(2, ("npop: stack underflow: %s", _nc_visbuf(tparam_base))); + DEBUG(2, ("npop: stack underflow: %s", _nc_visbuf(TPS(tparam_base)))); _nc_tparm_err++; } return result; } -static inline void +static NCURSES_INLINE void spush(char *x) { - if (stack_ptr < STACKSIZE) { - stack[stack_ptr].num_type = FALSE; - stack[stack_ptr].data.str = x; - stack_ptr++; + if (TPS(stack_ptr) < STACKSIZE) { + TPS(stack)[TPS(stack_ptr)].num_type = FALSE; + TPS(stack)[TPS(stack_ptr)].data.str = x; + TPS(stack_ptr)++; } else { - DEBUG(2, ("spush: stack overflow: %s", _nc_visbuf(tparam_base))); + DEBUG(2, ("spush: stack overflow: %s", _nc_visbuf(TPS(tparam_base)))); _nc_tparm_err++; } } -static inline char * +static NCURSES_INLINE char * spop(void) { - 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; + if (TPS(stack_ptr) > 0) { + TPS(stack_ptr)--; + if (!TPS(stack)[TPS(stack_ptr)].num_type + && TPS(stack)[TPS(stack_ptr)].data.str != 0) + result = TPS(stack)[TPS(stack_ptr)].data.str; } else { - DEBUG(2, ("spop: stack underflow: %s", _nc_visbuf(tparam_base))); + DEBUG(2, ("spop: stack underflow: %s", _nc_visbuf(TPS(tparam_base)))); _nc_tparm_err++; } return result; } -static inline const char * +static NCURSES_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 my_width = 0; - int my_prec = 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 { /* value before '.' is the width */ - dot = TRUE; - my_width = value; - } - value = 0; - break; - case '#': - *format++ = *s++; - break; - case ' ': - *format++ = *s++; - break; - case ':': - s++; - allowminus = TRUE; - break; - case '-': - if (allowminus) { - *format++ = *s++; - } else { + if (format != 0) { + bool done = FALSE; + bool allowminus = FALSE; + bool dot = FALSE; + bool err = FALSE; + char *fmt = format; + int my_width = 0; + int my_prec = 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': +#ifdef EXP_XTERM_1005 + case 'u': +#endif + *format++ = *s; done = TRUE; - } - break; - default: - if (isdigit(UChar(*s))) { - value = (value * 10) + (*s - '0'); - if (value > 10000) + break; + case '.': + *format++ = *s++; + if (dot) { err = TRUE; + } else { /* value before '.' is the width */ + dot = TRUE; + my_width = value; + } + value = 0; + break; + case '#': *format++ = *s++; - } else { - done = TRUE; + break; + case ' ': + *format++ = *s++; + break; + case ':': + s++; + allowminus = TRUE; + break; + case '-': + if (allowminus) { + *format++ = *s++; + } else { + done = TRUE; + } + break; + default: + if (isdigit(UChar(*s))) { + value = (value * 10) + (*s - '0'); + if (value > 10000) + err = TRUE; + *format++ = *s++; + } else { + done = TRUE; + } } } - } - /* - * If we found an error, ignore (and remove) the flags. - */ - if (err) { - my_width = my_prec = value = 0; - format = fmt; - *format++ = '%'; - *format++ = *s; - } + /* + * If we found an error, ignore (and remove) the flags. + */ + if (err) { + my_width = my_prec = value = 0; + format = fmt; + *format++ = '%'; + *format++ = *s; + } - /* - * Any value after '.' is the precision. If we did not see '.', then - * the value is the width. - */ - if (dot) - my_prec = value; - else - my_width = value; - - *format = '\0'; - /* return maximum string length in print */ - *len = (my_width > my_prec) ? my_width : my_prec; + /* + * Any value after '.' is the precision. If we did not see '.', then + * the value is the width. + */ + if (dot) + my_prec = value; + else + my_width = value; + + *format = '\0'; + /* return maximum string length in print */ + *len = (my_width > my_prec) ? my_width : my_prec; + } 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) +/* + * 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. + */ +NCURSES_EXPORT(int) +_nc_tparm_analyze(const char *string, char **p_is_s, int *popcount) { -#define NUM_VARS 26 - char *p_is_s[9]; - long 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; + int i; + int lastpop = -1; + int len; + int number = 0; + int level = -1; + const char *cp = string; + + if (cp == 0) + return 0; + + if ((len2 = strlen(cp)) + 2 > TPS(fmt_size)) { + TPS(fmt_size) += len2 + 2; + TPS(fmt_buff) = typeRealloc(char, TPS(fmt_size), TPS(fmt_buff)); + if (TPS(fmt_buff) == 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;) { + memset(p_is_s, 0, sizeof(p_is_s[0]) * NUM_PARM); + *popcount = 0; + + while ((cp - string) < (int) len2) { if (*cp == '%') { cp++; - cp = parse_format(cp, format, &len); + cp = parse_format(cp, TPS(fmt_buff), &len); switch (*cp) { default: break; @@ -403,44 +443,55 @@ tparam_internal(const char *string, va_list ap) case 'x': /* FALLTHRU */ case 'X': /* FALLTHRU */ case 'c': /* FALLTHRU */ - number++; +#ifdef EXP_XTERM_1005 + case 'u': +#endif + if (lastpop <= 0) { + tc_BUMP(); + } + level -= 1; lastpop = -1; break; case 'l': case 's': - if (lastpop > 0) + if (lastpop > 0) { + level -= 1; p_is_s[lastpop - 1] = dummy; - ++number; + } + tc_BUMP(); break; case 'p': cp++; - i = (*cp - '0'); - if (i >= 0 && i <= 9) { + i = (UChar(*cp) - '0'); + if (i >= 0 && i <= NUM_PARM) { + ++level; lastpop = i; - if (lastpop > popcount) - popcount = lastpop; + if (lastpop > *popcount) + *popcount = lastpop; } break; case 'P': - ++number; ++cp; break; case 'g': + ++level; cp++; break; case S_QUOTE: + ++level; cp += 2; lastpop = -1; break; case L_BRACE: + ++level; cp++; - while (*cp >= '0' && *cp <= '9') { + while (isdigit(UChar(*cp))) { cp++; } break; @@ -458,20 +509,19 @@ tparam_internal(const char *string, va_list ap) case '=': case '<': case '>': + tc_BUMP(); + level -= 1; /* pop 2, operate, push 1 */ lastpop = -1; - number += 2; break; case '!': case '~': + tc_BUMP(); lastpop = -1; - ++number; break; case 'i': - lastpop = -1; - if (popcount < 2) - popcount = 2; + /* will add 1 to first (usually two) parameters */ break; } } @@ -479,56 +529,193 @@ tparam_internal(const char *string, va_list ap) cp++; } - if (number > 9) - number = 9; - for (i = 0; i < max(popcount, number); i++) { - /* - * 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. The - * normal prototype for this uses 9 long's, which is consistent with - * our va_arg() usage. - */ - if (p_is_s[i] != 0) { - p_is_s[i] = va_arg(ap, char *); + if (number > NUM_PARM) + number = NUM_PARM; + return number; +} + +/* + * Analyze the capability string, finding the number of parameters and their + * types. + * + * TODO: cache the result so that this is done once per capability per term. + */ +static int +tparm_setup(const char *string, TPARM_DATA * result) +{ + int rc = OK; + + TPS(out_used) = 0; + memset(result, 0, sizeof(*result)); + + if (string == NULL) { + TR(TRACE_CALLS, ("%s: format is null", TPS(tname))); + rc = ERR; + } else { +#if HAVE_TSEARCH + TPARM_DATA *fs; + void *ft; + + result->format = string; + if ((ft = tfind(result, &MyCache, cmp_format)) != 0) { + fs = *(TPARM_DATA **) ft; + *result = *fs; + } else +#endif + { + /* + * 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. + */ + result->num_parsed = _nc_tparm_analyze(string, + result->p_is_s, + &(result->num_popped)); + if (TPS(fmt_buff) == 0) { + TR(TRACE_CALLS, ("%s: error in analysis", TPS(tname))); + rc = ERR; + } else { + int n; + + if (result->num_parsed > NUM_PARM) + result->num_parsed = NUM_PARM; + if (result->num_popped > NUM_PARM) + result->num_popped = NUM_PARM; + result->num_actual = max(result->num_popped, result->num_parsed); + + for (n = 0; n < result->num_actual; ++n) { + if (result->p_is_s[n]) + result->tparm_type |= (1 << n); + } +#if HAVE_TSEARCH + if ((fs = typeCalloc(TPARM_DATA, 1)) != 0) { + *fs = *result; + if ((fs->format = strdup(string)) != 0) { + if (tsearch(fs, &MyCache, cmp_format) != 0) { + ++MyCount; + } else { + free(fs); + rc = ERR; + } + } else { + free(fs); + rc = ERR; + } + } else { + rc = ERR; + } +#endif + } + } + } + + return rc; +} + +/* + * 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. The normal prototype for tparm uses 9 + * long's, which is consistent with our va_arg() usage. + */ +static void +tparm_copy_valist(TPARM_DATA * data, int use_TPARM_ARG, va_list ap) +{ + int i; + + for (i = 0; i < data->num_actual; i++) { + if (data->p_is_s[i] != 0) { + char *value = va_arg(ap, char *); + if (value == 0) + value = dummy; + data->p_is_s[i] = value; + data->param[i] = 0; + } else if (use_TPARM_ARG) { + data->param[i] = va_arg(ap, TPARM_ARG); } else { - param[i] = va_arg(ap, long int); + data->param[i] = (TPARM_ARG) 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. + */ +static bool +tparm_tc_compat(TPARM_DATA * data) +{ + bool termcap_hack = FALSE; + + TPS(stack_ptr) = 0; - /* - * 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]); + if (data->num_popped == 0) { + int i; + + termcap_hack = TRUE; + for (i = data->num_parsed - 1; i >= 0; i--) { + if (data->p_is_s[i]) + spush(data->p_is_s[i]); + else + npush((int) data->param[i]); + } } + return termcap_hack; +} + #ifdef TRACE - 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); +static void +tparm_trace_call(const char *string, TPARM_DATA * data) +{ + if (USE_TRACEF(TRACE_CALLS)) { + int i; + for (i = 0; i < data->num_actual; i++) { + if (data->p_is_s[i] != 0) { + save_text(", %s", _nc_visbuf(data->p_is_s[i]), 0); + } else if ((long) data->param[i] > MAX_OF_TYPE(NCURSES_INT2) || + (long) data->param[i] < 0) { + _tracef("BUG: problem with tparm parameter #%d of %d", + i + 1, data->num_actual); + break; + } else { + save_number(", %d", (int) data->param[i], 0); + } } - _tracef(T_CALLED("%s(%s%s)"), tname, _nc_visbuf(string), out_buff); - out_used = 0; + _tracef(T_CALLED("%s(%s%s)"), TPS(tname), _nc_visbuf(string), TPS(out_buff)); + TPS(out_used) = 0; + _nc_unlock_global(tracef); } +} + +#else +#define tparm_trace_call(string, data) /* nothing */ #endif /* TRACE */ - while (*string) { - if (*string != '%') { - save_char(*string); +static NCURSES_INLINE char * +tparam_internal(const char *string, TPARM_DATA * data) +{ + int number; + int len; + int level; + int x, y; + int i; + const char *cp = string; + size_t len2 = strlen(cp); + bool incremented_two = FALSE; + bool termcap_hack = tparm_tc_compat(data); + + tparm_trace_call(string, data); + + while ((cp - string) < (int) len2) { + if (*cp != '%') { + save_char(UChar(*cp)); } else { - tparam_base = string++; - string = parse_format(string, format, &len); - switch (*string) { + TPS(tparam_base) = cp++; + cp = parse_format(cp, TPS(fmt_buff), &len); + switch (*cp) { default: break; case '%': @@ -539,66 +726,81 @@ tparam_internal(const char *string, va_list ap) case 'o': /* FALLTHRU */ case 'x': /* FALLTHRU */ case 'X': /* FALLTHRU */ - save_number(format, npop(), len); + save_number(TPS(fmt_buff), npop(), len); break; case 'c': /* FALLTHRU */ save_char(npop()); break; +#ifdef EXP_XTERM_1005 + case 'u': + { + unsigned char target[10]; + unsigned source = (unsigned) npop(); + int rc = _nc_conv_to_utf8(target, source, (unsigned) + sizeof(target)); + int n; + for (n = 0; n < rc; ++n) { + save_char(target[n]); + } + } + break; +#endif case 'l': - save_number("%d", (int) strlen(spop()), 0); + npush((int) strlen(spop())); break; case 's': - save_text(format, spop(), len); + save_text(TPS(fmt_buff), 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]); + cp++; + i = (UChar(*cp) - '1'); + if (i >= 0 && i < NUM_PARM) { + if (data->p_is_s[i]) { + spush(data->p_is_s[i]); + } else { + npush((int) data->param[i]); + } } 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(); + cp++; + if (isUPPER(*cp)) { + i = (UChar(*cp) - 'A'); + TPS(static_vars)[i] = npop(); + } else if (isLOWER(*cp)) { + i = (UChar(*cp) - 'a'); + TPS(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]); + cp++; + if (isUPPER(*cp)) { + i = (UChar(*cp) - 'A'); + npush(TPS(static_vars)[i]); + } else if (isLOWER(*cp)) { + i = (UChar(*cp) - 'a'); + npush(TPS(dynamic_var)[i]); } break; case S_QUOTE: - string++; - npush(*string); - string++; + cp++; + npush(UChar(*cp)); + cp++; break; case L_BRACE: number = 0; - string++; - while (*string >= '0' && *string <= '9') { - number = number * 10 + *string - '0'; - string++; + cp++; + while (isdigit(UChar(*cp))) { + number = (number * 10) + (UChar(*cp) - '0'); + cp++; } npush(number); break; @@ -630,11 +832,15 @@ tparam_internal(const char *string, va_list ap) break; case 'A': - npush(npop() && npop()); + y = npop(); + x = npop(); + npush(y && x); break; case 'O': - npush(npop() || npop()); + y = npop(); + x = npop(); + npush(y || x); break; case '&': @@ -676,10 +882,26 @@ tparam_internal(const char *string, va_list ap) break; case 'i': - if (p_is_s[0] == 0) - param[0]++; - if (p_is_s[1] == 0) - param[1]++; + /* + * Increment the first two parameters -- if they are numbers + * rather than strings. As a side effect, assign into the + * stack; if this is termcap, then the stack was populated + * using the termcap hack above rather than via the terminfo + * 'p' case. + */ + if (!incremented_two) { + incremented_two = TRUE; + if (data->p_is_s[0] == 0) { + data->param[0]++; + if (termcap_hack) + TPS(stack)[0].data.num = (int) data->param[0]; + } + if (data->p_is_s[1] == 0) { + data->param[1]++; + if (termcap_hack) + TPS(stack)[1].data.num = (int) data->param[1]; + } + } break; case '?': @@ -689,38 +911,38 @@ tparam_internal(const char *string, va_list ap) x = npop(); if (!x) { /* scan forward for %e or %; at level zero */ - string++; + cp++; level = 0; - while (*string) { - if (*string == '%') { - string++; - if (*string == '?') + while (*cp) { + if (*cp == '%') { + cp++; + if (*cp == '?') level++; - else if (*string == ';') { + else if (*cp == ';') { if (level > 0) level--; else break; - } else if (*string == 'e' && level == 0) + } else if (*cp == 'e' && level == 0) break; } - if (*string) - string++; + if (*cp) + cp++; } } break; case 'e': /* scan forward for a %; at level zero */ - string++; + cp++; level = 0; - while (*string) { - if (*string == '%') { - string++; - if (*string == '?') + while (*cp) { + if (*cp == '%') { + cp++; + if (*cp == '?') level++; - else if (*string == ';') { + else if (*cp == ';') { if (level > 0) level--; else @@ -728,42 +950,150 @@ tparam_internal(const char *string, va_list ap) } } - if (*string) - string++; + if (*cp) + cp++; } break; case ';': break; - } /* endswitch (*string) */ - } /* endelse (*string == '%') */ + } /* endswitch (*cp) */ + } /* endelse (*cp == '%') */ - if (*string == '\0') + if (*cp == '\0') break; - string++; - } /* endwhile (*string) */ + cp++; + } /* endwhile (*cp) */ + + get_space((size_t) 1); + TPS(out_buff)[TPS(out_used)] = '\0'; + + if (TPS(stack_ptr) && !_nc_tparm_err) { + DEBUG(2, ("tparm: stack has %d item%s on return", + TPS(stack_ptr), + TPS(stack_ptr) == 1 ? "" : "s")); + _nc_tparm_err++; + } + + T((T_RETURN("%s"), _nc_visbuf(TPS(out_buff)))); + return (TPS(out_buff)); +} + +#if NCURSES_TPARM_VARARGS + +NCURSES_EXPORT(char *) +tparm(const char *string, ...) +{ + TPARM_DATA myData; + char *result = NULL; + + _nc_tparm_err = 0; +#ifdef TRACE + TPS(tname) = "tparm"; +#endif /* TRACE */ - get_space(1); - out_buff[out_used] = '\0'; + if (tparm_setup(string, &myData) == OK) { + va_list ap; - T((T_RETURN("%s"), _nc_visbuf(out_buff))); - return (out_buff); + va_start(ap, string); + tparm_copy_valist(&myData, TRUE, ap); + va_end(ap); + + result = tparam_internal(string, &myData); + } + return result; } +#else /* !NCURSES_TPARM_VARARGS */ + NCURSES_EXPORT(char *) -tparm(NCURSES_CONST char *string,...) +tparm(const char *string, + TPARM_ARG a1, + TPARM_ARG a2, + TPARM_ARG a3, + TPARM_ARG a4, + TPARM_ARG a5, + TPARM_ARG a6, + TPARM_ARG a7, + TPARM_ARG a8, + TPARM_ARG a9) { - va_list ap; - char *result; + TPARM_DATA myData; + char *result = NULL; _nc_tparm_err = 0; - va_start(ap, string); #ifdef TRACE - tname = "tparm"; + TPS(tname) = "tparm"; #endif /* TRACE */ - result = tparam_internal(string, ap); - va_end(ap); + + if (tparm_setup(string, &myData) == OK) { + + myData.param[0] = a1; + myData.param[1] = a2; + myData.param[2] = a3; + myData.param[3] = a4; + myData.param[4] = a5; + myData.param[5] = a6; + myData.param[6] = a7; + myData.param[7] = a8; + myData.param[8] = a9; + + result = tparam_internal(string, &myData); + } + return result; +} + +#endif /* NCURSES_TPARM_VARARGS */ + +NCURSES_EXPORT(char *) +tiparm(const char *string, ...) +{ + TPARM_DATA myData; + char *result = NULL; + + _nc_tparm_err = 0; +#ifdef TRACE + TPS(tname) = "tiparm"; +#endif /* TRACE */ + + if (tparm_setup(string, &myData) == OK) { + va_list ap; + + va_start(ap, string); + tparm_copy_valist(&myData, FALSE, ap); + va_end(ap); + + result = tparam_internal(string, &myData); + } + return result; +} + +/* + * The internal-use flavor ensures that the parameters are numbers, not strings + */ +NCURSES_EXPORT(char *) +_nc_tiparm(int expected, const char *string, ...) +{ + TPARM_DATA myData; + char *result = NULL; + + _nc_tparm_err = 0; +#ifdef TRACE + TPS(tname) = "_nc_tiparm"; +#endif /* TRACE */ + + if (tparm_setup(string, &myData) == OK + && myData.num_actual <= expected + && myData.tparm_type == 0) { + va_list ap; + + va_start(ap, string); + tparm_copy_valist(&myData, FALSE, ap); + va_end(ap); + + result = tparam_internal(string, &myData); + } return result; }