#include <curses.priv.h>
+#include <ctype.h>
#include <term.h>
+#include <tic.h>
-MODULE_ID("$Id: lib_tparm.c,v 1.22 1998/02/11 12:13:54 tom Exp $")
+MODULE_ID("$Id: lib_tparm.c,v 1.39 1999/06/06 00:04:55 tom Exp $")
/*
* char *
* screen visible to the user, not to any unseen memory.) If
* the terminal has memory relative cursor addressing, that can
* be indicated by
- *
+ *
* The parameter mechanism uses a stack and special %
* codes to manipulate it. Typically a sequence will push one
* of the parameters onto the stack and then print it in some
* format. Often more complex operations are necessary.
- *
+ *
* The % encodings have the following meanings:
- *
+ *
* %% outputs `%'
- * %d print pop() like %d in printf()
- * %2d print pop() like %2d in printf()
- * %02d print pop() like %02d in printf()
- * %3d print pop() like %3d in printf()
- * %03d print pop() like %03d in printf()
- * %2x print pop() like %2x in printf()
- * %02x print pop() like %02x in printf()
- * %3x print pop() like %3x in printf()
- * %03x print pop() like %03x in printf()
* %c print pop() like %c in printf()
* %s print pop() like %s in printf()
- *
+ * %[[:]flags][width[.precision]][doxXs]
+ * as in printf, flags are [-+#] and space
+ *
* %p[1-9] push ith parm
- * %P[a-z] set variable [a-z] to pop()
- * %g[a-z] get variable [a-z] and push it
+ * %P[a-z] set dynamic variable [a-z] to pop()
+ * %g[a-z] get dynamic variable [a-z] and push it
+ * %P[A-Z] set static variable [A-Z] to pop()
+ * %g[A-Z] get static variable [A-Z] and push it
+ * %l push strlen(pop)
* %'c' push char constant c
* %{nn} push integer constant nn
- *
+ *
* %+ %- %* %/ %m
* arithmetic (%m is mod): push(pop() op pop())
* %& %| %^ bit operations: push(pop() op pop())
* %A %O logical and & or operations for conditionals
* %! %~ unary operations push(op pop())
* %i add 1 to first two parms (for ANSI terminals)
- *
+ *
* %? expr %t thenpart %e elsepart %;
* if-then-else, %e elsepart is optional.
* else-if's are possible ala Algol 68:
* %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %;
- *
+ *
* For those of the above operators which are binary and not commutative,
* the stack works in the usual way, with
* %gx %gy %m
* resulting in x mod y, not the reverse.
*/
-#define L_BRACE '{'
-#define R_BRACE '}'
-
#define STACKSIZE 20
typedef union {
}
#endif
-static void save_text(char *s)
+static void really_get_space(size_t need)
{
- size_t want = strlen(s);
- size_t need = want + out_used + 1;
-
- if (need > out_size) {
- out_size = need * 2;
- if (out_buff == 0)
- out_buff = malloc(out_size);
- else
- out_buff = realloc(out_buff, out_size);
- }
- (void)strcpy(out_buff + out_used, s);
- out_used += want;
+ 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)
+{
+ need += out_used;
+ if (need > out_size)
+ really_get_space(need);
+}
+
+static inline void save_text(const char *fmt, char *s, int len)
+{
+ size_t s_len = strlen(s);
+ if (len > (int)s_len)
+ s_len = len;
+
+ get_space(s_len + 1);
+
+ (void)sprintf(out_buff+out_used, fmt, s);
+ out_used += strlen(out_buff+out_used);
}
-static void save_number(const char *fmt, int number)
+static inline void save_number(const char *fmt, int number, int len)
{
- char temp[80];
- (void)sprintf(temp, fmt, number);
- save_text(temp);
+ if (len < 30)
+ len = 30; /* actually log10(MAX_INT)+1 */
+
+ get_space(len + 1);
+
+ (void)sprintf(out_buff+out_used, fmt, number);
+ out_used += strlen(out_buff+out_used);
}
static inline void save_char(int c)
{
- static char text[2];
if (c == 0)
c = 0200;
- text[0] = c;
- save_text(text);
+ get_space(1);
+ out_buff[out_used++] = c;
}
static inline void npush(int x)
{
if (stack_ptr < STACKSIZE) {
stack[stack_ptr].num = x;
- stack_ptr++;
- }
+ stack_ptr++;
+ }
}
static inline int npop(void)
static inline char *spop(void)
{
- return (stack_ptr > 0 ? stack[--stack_ptr].str : 0);
+ static char dummy[] = ""; /* avoid const-cast */
+ return (stack_ptr > 0 ? stack[--stack_ptr].str : dummy);
+}
+
+static inline const char *parse_format(const char *s, char *format, int *len)
+{
+ bool done = FALSE;
+ bool allowminus = FALSE;
+ bool dot = FALSE;
+ int prec = 0;
+ int width = 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++;
+ 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;
+ }
+ }
+ }
+ *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)
{
#define NUM_VARS 26
int param[9];
int popcount;
-int variable[NUM_VARS];
-char len;
int number;
+int len;
int level;
int x, y;
int i;
-int varused = -1;
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)
}
}
}
+ 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++) {
/*
* This is a termcap compatibility hack. If there are no explicit pop
- * operations in the string, load the stack in such a way that
+ * 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.
#ifdef TRACE
if (_nc_tracing & TRACE_CALLS) {
for (i = 0; i < popcount; i++)
- save_number(", %d", param[i]);
+ 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 != '%')
+ if (*string != '%') {
save_char(*string);
- else {
+ } else {
string++;
+ string = parse_format(string, format, &len);
switch (*string) {
default:
break;
save_char('%');
break;
- case 'd':
- save_number("%d", npop());
- break;
-
- case 'x':
- save_number("%x", npop());
- break;
-
- case '0':
- string++;
- len = *string;
- if (len == '2' || len == '3')
- {
- ++string;
- if (*string == 'd') {
- if (len == '2')
- save_number("%02d", npop());
- else
- save_number("%03d", npop());
- }
- else if (*string == 'x') {
- if (len == '2')
- save_number("%02x", npop());
- else
- save_number("%03x", npop());
- }
- }
- break;
-
- case '2':
- string++;
- if (*string == 'd') {
- save_number("%2d", npop());
- }
- else if (*string == 'x') {
- save_number("%2x", npop());
- }
- break;
-
- case '3':
- string++;
- if (*string == 'd') {
- save_number("%3d", npop());
- }
- else if (*string == 'x') {
- save_number("%3x", npop());
- }
+ case 'd': /* FALLTHRU */
+ case 'o': /* FALLTHRU */
+ case 'x': /* FALLTHRU */
+ case 'X': /* FALLTHRU */
+ case 'c':
+ save_number(format, npop(), len);
break;
- case 'c':
- save_char(npop());
+ case 'l':
+ save_number("%d", strlen(spop()), 0);
break;
case 's':
- save_text(spop());
+ save_text(format, spop(), len);
break;
case 'p':
case 'P':
string++;
- i = (*string - 'a');
- if (i >= 0 && i < NUM_VARS) {
- while (varused < i)
- variable[++varused] = 0;
- variable[i] = npop();
+ 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++;
- i = (*string - 'a');
- if (i >= 0 && i < NUM_VARS) {
- while (varused < i)
- variable[++varused] = 0;
- npush(variable[i]);
+ if (isUPPER(*string)) {
+ i = (*string - 'A');
+ npush(static_vars[i]);
+ } else if (isLOWER(*string)) {
+ i = (*string - 'a');
+ npush(dynamic_var[i]);
}
break;
- case '\'':
+ case S_QUOTE:
string++;
npush(*string);
string++;
case '/':
y = npop();
x = npop();
- npush(x / y);
+ npush(y ? (x / y) : 0);
break;
case 'm':
y = npop();
x = npop();
- npush(x % y);
+ npush(y ? (x % y) : 0);
break;
case 'A':
string++;
} /* endwhile (*string) */
- if (out_buff == 0)
- out_buff = calloc(1,1);
- if (out_used == 0)
- *out_buff = '\0';
+ if (out_buff == 0 && (out_buff = typeCalloc(char,1)) == NULL)
+ return(NULL);
+ out_buff[out_used] = '\0';
T((T_RETURN("%s"), _nc_visbuf(out_buff)));
return(out_buff);
}
-char *tparm(const char *string, ...)
+char *tparm(NCURSES_CONST char *string, ...)
{
va_list ap;
char *result;
va_end(ap);
return result;
}
-
-#ifdef __UNUSED__ /* we never documented this, and it confuses Emacs */
-char *tparam(const char *string, char *buffer, int bufsiz, ...)
-{
-va_list ap;
-char *result = 0;
-
- va_start(ap, bufsiz);
-#ifdef TRACE
- tname = "tparam";
-#endif /* TRACE */
- if (tparam_internal(string, ap) != 0
- && (int)out_used < bufsiz)
- result = strcpy(buffer, out_buff);
- va_end(ap);
- return result;
-}
-#endif /* __UNUSED__ */