From: Bruno Haible Date: Thu, 14 Dec 2006 12:34:16 +0000 (+0000) Subject: tparm replacement for platforms that lack it (e.g. NetBSD 3.0). X-Git-Tag: v0.17~590 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7cfece14b5fa445ee79171b9a34ee57917040d9e;p=thirdparty%2Fgettext.git tparm replacement for platforms that lack it (e.g. NetBSD 3.0). --- diff --git a/gnulib-local/lib/tparm.c b/gnulib-local/lib/tparm.c new file mode 100644 index 000000000..b9497f371 --- /dev/null +++ b/gnulib-local/lib/tparm.c @@ -0,0 +1,898 @@ +/* Substitution of parameters in strings from terminal descriptions. + Copyright (C) 2006 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. */ + +/* Originally by Ross Ridge, Public Domain, 92/02/01 07:30:36 */ + +#include + +#include +#include +#include + +#include "c-ctype.h" + +#ifdef USE_SCCS_IDS +static const char SCCSid[] = "@(#) mytinfo tparm.c 3.2 92/02/01 public domain, By Ross Ridge"; +#endif + +#ifndef MAX_PUSHED +#define MAX_PUSHED 32 +#endif + +#define ARG 1 +#define NUM 2 + +#define INTEGER 1 +#define STRING 2 + +#define MAX_LINE 640 + +typedef struct stack_str +{ + int type; + int argnum; + int value; +} stack; + +static stack S[MAX_PUSHED]; +static stack vars['z'-'a'+1]; +static int pos = 0; + +static +struct arg_str +{ + int type; + int integer; + char *string; +} arg_list[10]; + +static int argcnt; + +static va_list tparm_args; + +static int +pusharg (int arg) +{ + if (pos == MAX_PUSHED) + return 1; + S[pos].type = ARG; + S[pos++].argnum = arg; + return 0; +} + +static int +pushnum (int num) +{ + if (pos == MAX_PUSHED) + return 1; + S[pos].type = NUM; + S[pos++].value = num; + return 0; +} + +static int +getarg (int argnum, int type, void *p) +{ + while (argcnt < argnum) + { + arg_list[argcnt].type = INTEGER; + arg_list[argcnt++].integer = (int) va_arg (tparm_args, int); + } + if (argcnt > argnum) + { + if (arg_list[argnum].type != type) + return 1; + else if (type == STRING) + *(char **)p = arg_list[argnum].string; + else + *(int *)p = arg_list[argnum].integer; + } + else + { + arg_list[argcnt].type = type; + if (type == STRING) + *(char **)p = arg_list[argcnt++].string = (char *) va_arg (tparm_args, char *); + else + *(int *)p = arg_list[argcnt++].integer = (int) va_arg (tparm_args, int); + } + return 0; +} + +static int +popstring (char **str) +{ + if (pos-- == 0) + return 1; + if (S[pos].type != ARG) + return 1; + return getarg (S[pos].argnum, STRING, str); +} + +static int +popnum (int *num) +{ + if (pos-- == 0) + return 1; + switch (S[pos].type) + { + case ARG: + return getarg (S[pos].argnum, INTEGER, num); + case NUM: + *num = S[pos].value; + return 0; + } + return 1; +} + +static int +cvtchar (const char *sp, char *c) +{ + switch (*sp) + { + case '\\': + switch (*++sp) + { + case '\'': + case '$': + case '\\': + case '%': + *c = *sp; + return 2; + case '\0': + *c = '\\'; + return 1; + case '0': + if (sp[1] == '0' && sp[2] == '0') + { + *c = '\0'; + return 4; + } + *c = '\200'; /* '\0' ???? */ + return 2; + default: + *c = *sp; + return 2; + } + default: + *c = *sp; + return 1; + } +} + +/* sigh... this has got to be the ugliest code I've ever written. + Trying to handle everything has its cost, I guess. + + It actually isn't to hard to figure out if a given % code is supposed + to be interpeted with its termcap or terminfo meaning since almost + all terminfo codes are invalid unless something has been pushed on + the stack and termcap strings will never push things on the stack + (%p isn't used by termcap). So where we have a choice we make the + decision by wether or not somthing has been pushed on the stack. + The static variable termcap keeps track of this; it starts out set + to 1 and is incremented as each argument processed by a termcap % code, + however if something is pushed on the stack it's set to 0 and the + rest of the % codes are interpeted as terminfo % codes. Another way + of putting it is that if termcap equals one we haven't decided either + way yet, if it equals zero we're looking for terminfo codes, and if + its greater than 1 we're looking for termcap codes. + + Terminfo % codes: + + %% output a '%' + %[[:][-+# ][width][.precision]][doxXs] + output pop according to the printf format + %c output pop as a char + %'c' push character constant c. + %{n} push decimal constant n. + %p[1-9] push paramter [1-9] + %g[a-z] push variable [a-z] + %P[a-z] put pop in variable [a-z] + %l push the length of pop (a string) + %+ add pop to pop and push the result + %- subtract pop from pop and push the result + %* multiply pop and pop and push the result + %& bitwise and pop and pop and push the result + %| bitwise or pop and pop and push the result + %^ bitwise xor pop and pop and push the result + %~ push the bitwise not of pop + %= compare if pop and pop are equal and push the result + %> compare if pop is less than pop and push the result + %< compare if pop is greater than pop and push the result + %A logical and pop and pop and push the result + %O logical or pop and pop and push the result + %! push the logical not of pop + %? condition %t if_true [%e if_false] %; + if condtion evaulates as true then evaluate if_true, + else evaluate if_false. elseif's can be done: + %? cond %t true [%e cond2 %t true2] ... [%e condN %t trueN] [%e false] %; + %i add one to parameters 1 and 2. (ANSI) + + Termcap Codes: + + %% output a % + %. output parameter as a character + %d output parameter as a decimal number + %2 output parameter in printf format %02d + %3 output parameter in printf format %03d + %+x add the character x to parameter and output it as a character +(UW) %-x subtract parameter FROM the character x and output it as a char +(UW) %ax add the character x to parameter +(GNU) %a[+*-/=][cp]x + GNU arithmetic. +(UW) %sx subtract parameter FROM the character x + %>xy if parameter > character x then add character y to parameter + %B convert to BCD (parameter = (parameter/10)*16 + parameter%16) + %D Delta Data encode (parameter = parameter - 2*(paramter%16)) + %i increment the first two parameters by one + %n xor the first two parameters by 0140 +(GNU) %m xor the first two parameters by 0177 + %r swap the first two parameters +(GNU) %b backup to previous parameter +(GNU) %f skip this parameter + + Note the two definitions of %a, the GNU defintion is used if the characters + after the 'a' are valid, otherwise the UW definition is used. + + (GNU) used by GNU Emacs termcap libraries + (UW) used by the University of Waterloo (MFCF) termcap libraries + +*/ + +char * +tparm (const char *str, ...) +{ + static int termcap; + static char OOPS[] = "OOPS"; + static char buf[MAX_LINE]; + const char *sp; + char *dp; + char *fmt; + char scan_for; + int scan_depth; + int if_depth; + char fmt_buf[MAX_LINE]; + char sbuf[MAX_LINE]; + + va_start (tparm_args, str); + + sp = str; + dp = buf; + scan_for = 0; + scan_depth = 0; + if_depth = 0; + argcnt = 0; + pos = 0; + termcap = 1; + while (*sp != '\0') + { + switch (*sp) + { + case '\\': + if (scan_for) + { + if (*++sp != '\0') + sp++; + break; + } + *dp++ = *sp++; + if (*sp != '\0') + *dp++ = *sp++; + break; + case '%': + sp++; + if (scan_for) + { + if (*sp == scan_for && if_depth == scan_depth) + { + if (scan_for == ';') + if_depth--; + scan_for = 0; + } + else if (*sp == '?') + if_depth++; + else if (*sp == ';') + { + if (if_depth == 0) + return OOPS; + else + if_depth--; + } + sp++; + break; + } + fmt = NULL; + switch (*sp) + { + case '%': + *dp++ = *sp++; + break; + case '+': + if (!termcap) + { + int i, j; + if (popnum (&j) || popnum (&i)) + return OOPS; + i += j; + if (pushnum (i)) + return OOPS; + sp++; + break; + } + /* FALLTHROUGH */ + case 'C': + if (*sp == 'C') + { + int i; + if (getarg (termcap - 1, INTEGER, &i)) + return OOPS; + if (i >= 96) + { + i /= 96; + if (i == '$') + *dp++ = '\\'; + *dp++ = i; + } + } + fmt = "%c"; + /* FALLTHROUGH */ + case 'a': + if (!termcap) + return OOPS; + { + int i; + if (getarg (termcap - 1, INTEGER, &i)) + return OOPS; + if (*++sp == '\0') + return OOPS; + if ((sp[1] == 'p' || sp[1] == 'c') + && sp[2] != '\0' && fmt == NULL) + { + /* GNU arithmetic parameter, what they really need is + terminfo. */ + int val; + int lc; + if (sp[1] == 'p' + && getarg (termcap - 1 + sp[2] - '@', INTEGER, &val)) + return OOPS; + if (sp[1] == 'c') + { + char c; + lc = cvtchar (sp + 2, &c) + 2; + /* Mask out 8th bit so \200 can be used for \0 as per + GNU docs. */ + val = c & 0177; + } + else + lc = 2; + switch (sp[0]) + { + case '=': + break; + case '+': + val = i + val; + break; + case '-': + val = i - val; + break; + case '*': + val = i * val; + break; + case '/': + val = i / val; + break; + default: + /* Not really GNU's %a after all... */ + { + char c; + lc = cvtchar (sp, &c); + val = c + i; + } + break; + } + arg_list[termcap - 1].integer = val; + sp += lc; + break; + } + { + char c; + sp += cvtchar (sp, &c); + arg_list[termcap - 1].integer = c + i; + } + } + if (fmt == NULL) + break; + sp--; + /* FALLTHROUGH */ + case '-': + if (!termcap) + { + int i, j; + if (popnum (&j) || popnum (&i)) + return OOPS; + i -= j; + if (pushnum (i)) + return OOPS; + sp++; + break; + } + fmt = "%c"; + /* FALLTHROUGH */ + case 's': + if (termcap && (fmt == NULL || *sp == '-')) + { + int i; + if (getarg (termcap - 1, INTEGER, &i)) + return OOPS; + if (*++sp == '\0') + return OOPS; + { + char c; + sp += cvtchar (sp, &c); + arg_list[termcap - 1].integer = c - i; + } + if (fmt == NULL) + break; + sp--; + } + if (!termcap) + return OOPS; + /* FALLTHROUGH */ + case '.': + if (termcap && fmt == NULL) + fmt = "%c"; + /* FALLTHROUGH */ + case 'd': + if (termcap && fmt == NULL) + fmt = "%d"; + /* FALLTHROUGH */ + case '2': + if (termcap && fmt == NULL) + fmt = "%02d"; + /* FALLTHROUGH */ + case '3': + if (termcap && fmt == NULL) + fmt = "%03d"; + /* FALLTHROUGH */ + case ':': case ' ': case '#': case 'u': + case 'x': case 'X': case 'o': case 'c': + case '0': case '1': case '4': case '5': + case '6': case '7': case '8': case '9': + if (fmt == NULL) + { + if (termcap) + return OOPS; + if (*sp == ':') + sp++; + fmt = fmt_buf; + *fmt++ = '%'; + while (*sp != 's' && *sp != 'x' && *sp != 'X' && *sp != 'd' + && *sp != 'o' && *sp != 'c' && *sp != 'u') + { + if (*sp == '\0') + return OOPS; + *fmt++ = *sp++; + } + *fmt++ = *sp; + *fmt = '\0'; + fmt = fmt_buf; + } + { + char conv_char = fmt[strlen (fmt) - 1]; + if (conv_char == 's') + { + char *s; + if (popstring (&s)) + return OOPS; + sprintf (sbuf, fmt, s); + } + else + { + int i; + if (termcap) + { + if (getarg (termcap++ - 1, INTEGER, &i)) + return OOPS; + } + else + if (popnum (&i)) + return OOPS; + if (i == 0 && conv_char == 'c') + strcpy (sbuf, "\000"); + else + sprintf (sbuf, fmt, i); + } + } + sp++; + fmt = sbuf; + while (*fmt != '\0') + { + if (*fmt == '$') + *dp++ = '\\'; + *dp++ = *fmt++; + } + break; + case 'r': + { + int i; + if (!termcap || getarg (1, INTEGER, &i)) + return OOPS; + arg_list[1].integer = arg_list[0].integer; + arg_list[0].integer = i; + } + sp++; + break; + case 'i': + { + int i; + if (getarg (1, INTEGER, &i) || arg_list[0].type != INTEGER) + return OOPS; + } + arg_list[1].integer++; + arg_list[0].integer++; + sp++; + break; + case 'n': + { + int i; + if (!termcap || getarg (1, INTEGER, &i)) + return OOPS; + } + arg_list[0].integer ^= 0140; + arg_list[1].integer ^= 0140; + sp++; + break; + case '>': + if (!termcap) + { + int i, j; + if (popnum (&j) || popnum (&i)) + return OOPS; + i = (i > j); + if (pushnum (i)) + return OOPS; + sp++; + break; + } + { + int i; + if (getarg (termcap-1, INTEGER, &i)) + return OOPS; + { + char c; + sp += cvtchar (sp, &c); + if (i > c) + { + sp += cvtchar (sp, &c); + arg_list[termcap-1].integer += c; + } + else + sp += cvtchar (sp, &c); + } + } + sp++; + break; + case 'B': + { + int i; + if (!termcap || getarg (termcap-1, INTEGER, &i)) + return OOPS; + arg_list[termcap-1].integer = 16 * (i / 10) + i % 10; + } + sp++; + break; + case 'D': + { + int i; + if (!termcap || getarg (termcap-1, INTEGER, &i)) + return OOPS; + arg_list[termcap-1].integer = i - 2 * (i % 16); + } + sp++; + break; + case 'p': + if (termcap > 1) + return OOPS; + if (*++sp == '\0') + return OOPS; + { + int i = (*sp == '0' ? 9 : *sp - '1'); + if (i < 0 || i > 9) + return OOPS; + if (pusharg (i)) + return OOPS; + } + termcap = 0; + sp++; + break; + case 'P': + if (termcap || *++sp == '\0') + return OOPS; + { + int i = *sp++ - 'a'; + if (i < 0 || i > 25) + return OOPS; + if (pos-- == 0) + return OOPS; + switch (vars[i].type = S[pos].type) + { + case ARG: + vars[i].argnum = S[pos].argnum; + break; + case NUM: + vars[i].value = S[pos].value; + break; + } + } + break; + case 'g': + if (termcap || *++sp == '\0') + return OOPS; + { + int i = *sp++ - 'a'; + if (i < 0 || i > 25) + return OOPS; + switch (vars[i].type) + { + case ARG: + if (pusharg (vars[i].argnum)) + return OOPS; + break; + case NUM: + if (pushnum (vars[i].value)) + return OOPS; + break; + } + } + break; + case '\'': + if (termcap > 1) + return OOPS; + if (*++sp == '\0') + return OOPS; + { + char c; + sp += cvtchar (sp, &c); + if (pushnum (c) || *sp++ != '\'') + return OOPS; + } + termcap = 0; + break; + case '{': + if (termcap > 1) + return OOPS; + { + int i; + i = 0; + sp++; + while (c_isdigit (*sp)) + i = 10 * i + *sp++ - '0'; + if (*sp++ != '}' || pushnum (i)) + return OOPS; + } + termcap = 0; + break; + case 'l': + { + int i; + char *s; + if (termcap || popstring (&s)) + return OOPS; + i = strlen (s); + if (pushnum (i)) + return OOPS; + } + sp++; + break; + case '*': + { + int i, j; + if (termcap || popnum (&j) || popnum (&i)) + return OOPS; + i *= j; + if (pushnum (i)) + return OOPS; + } + sp++; + break; + case '/': + { + int i, j; + if (termcap || popnum (&j) || popnum (&i)) + return OOPS; + i /= j; + if (pushnum (i)) + return OOPS; + } + sp++; + break; + case 'm': + if (termcap) + { + int i; + if (getarg (1, INTEGER, &i)) + return OOPS; + arg_list[0].integer ^= 0177; + arg_list[1].integer ^= 0177; + sp++; + break; + } + { + int i, j; + if (popnum (&j) || popnum (&i)) + return OOPS; + i %= j; + if (pushnum (i)) + return OOPS; + } + sp++; + break; + case '&': + { + int i, j; + if (popnum (&j) || popnum (&i)) + return OOPS; + i &= j; + if (pushnum (i)) + return OOPS; + } + sp++; + break; + case '|': + { + int i, j; + if (popnum (&j) || popnum (&i)) + return OOPS; + i |= j; + if (pushnum (i)) + return OOPS; + } + sp++; + break; + case '^': + { + int i, j; + if (popnum (&j) || popnum (&i)) + return OOPS; + i ^= j; + if (pushnum (i)) + return OOPS; + } + sp++; + break; + case '=': + { + int i, j; + if (popnum (&j) || popnum (&i)) + return OOPS; + i = (i == j); + if (pushnum (i)) + return OOPS; + } + sp++; + break; + case '<': + { + int i, j; + if (popnum (&j) || popnum (&i)) + return OOPS; + i = (i < j); + if (pushnum (i)) + return OOPS; + } + sp++; + break; + case 'A': + { + int i, j; + if (popnum (&j) || popnum (&i)) + return OOPS; + i = (i && j); + if (pushnum (i)) + return OOPS; + } + sp++; + break; + case 'O': + { + int i, j; + if (popnum (&j) || popnum (&i)) + return OOPS; + i = (i || j); + if (pushnum (i)) + return OOPS; + } + sp++; + break; + case '!': + { + int i; + if (popnum (&i)) + return OOPS; + i = !i; + if (pushnum (i)) + return OOPS; + } + sp++; + break; + case '~': + { + int i; + if (popnum (&i)) + return OOPS; + i = ~i; + if (pushnum (i)) + return OOPS; + } + sp++; + break; + case '?': + if (termcap > 1) + return OOPS; + termcap = 0; + if_depth++; + sp++; + break; + case 't': + { + int i; + if (popnum (&i) || if_depth == 0) + return OOPS; + if (!i) + { + scan_for = 'e'; + scan_depth = if_depth; + } + } + sp++; + break; + case 'e': + if (if_depth == 0) + return OOPS; + scan_for = ';'; + scan_depth = if_depth; + sp++; + break; + case ';': + if (if_depth-- == 0) + return OOPS; + sp++; + break; + case 'b': + if (--termcap < 1) + return OOPS; + sp++; + break; + case 'f': + if (!termcap++) + return OOPS; + sp++; + break; + } + break; + default: + if (scan_for) + sp++; + else + *dp++ = *sp++; + break; + } + } + va_end (tparm_args); + *dp = '\0'; + return buf; +}