diff options
Diffstat (limited to 'xpa/word.c')
-rw-r--r-- | xpa/word.c | 1097 |
1 files changed, 1097 insertions, 0 deletions
diff --git a/xpa/word.c b/xpa/word.c new file mode 100644 index 0000000..2d60eb0 --- /dev/null +++ b/xpa/word.c @@ -0,0 +1,1097 @@ +/* + * Copyright (c) 1999-2003 Smithsonian Astrophysical Observatory + */ + +/* + * word.c -- token parser, pattern matcher, macro expander, + * and other word-related routines + * + */ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <word.h> + +/* SAOMOD_CTYPE -- work around Slackware/RedHat incompatibility */ +#ifdef linux +#ifdef isalnum +#undef isalnum +#define isalnum(c) (isalpha(c)||isdigit(c)) +#endif +#endif + +/* ************************************************************************** + * + * + * PRIVATE ROUTINES AND DATA + * + * + * **************************************************************************/ + +/* word */ +#define MAXDELIM 256 +#define MAXDTABLES 1024 +#define BUFINC 5000 + +static char lastd; +static char dtable[MAXDELIM]; +static char *dtables[MAXDTABLES]; +static int ndtable=0; + +/* tmatch */ +#define ALL '*' +#define ANY '?' +#define RANGE '[' +#define ENDRANGE ']' +#define RANGEDELIM '-' +#define NOTRANGE '~' + +/* + *---------------------------------------------------------------------------- + * + * Routine: checkrange (from tmatch) + * + * Purpose: see if character is in specified range + * + * Returns: 1 if in range, otherwise 0 + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static int +checkrange (char *xtemplate, int *ptr, int c) +#else +static int checkrange(xtemplate, ptr, c) + char *xtemplate; + int *ptr; + int c; +#endif +{ + int inrange, notrange; + char lorange, hirange; + int tptr; + + tptr = *ptr; + /* make sure we have a close bracket */ + if( strchr(&xtemplate[tptr], ENDRANGE) == NULL ) + return(0); + /* check for negation - match if not in range */ + if( xtemplate [tptr+1] == NOTRANGE ){ + notrange = 1; tptr++; + } + else + notrange = 0; + /* start pessimistically */ + inrange = 0; + /* point past RANGE character */ + tptr++; + while( xtemplate[tptr] != ENDRANGE ){ + /* get lo range */ + lorange = xtemplate[tptr]; + /* and hi range */ + tptr++; + if( xtemplate[tptr] != RANGEDELIM ) + hirange = lorange; + else{ + tptr++;hirange = xtemplate[tptr];tptr++; + } + if( (c>=lorange) && (c<=hirange) ){ + inrange = 1; break; + } + } + /* only exclusive OR of inrange and notrange is ok */ + if( (inrange ^ notrange) ==0 ) + return(0); + else{ + *ptr = strchr(&xtemplate[tptr],']') - xtemplate + 1; + return(1); + } +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: addstring (from macro) + * + * Purpose: add a string to a buffer + * + * Returns: none + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static void +addstring (char **buf, int *blen, int *maxlen, char *str) +#else +static void addstring(buf, blen, maxlen, str) + char **buf; + int *blen; + int *maxlen; + char *str; +#endif +{ + int slen; + + slen = strlen(str) + 1; + while( (*blen + slen) >= *maxlen ){ + *maxlen += BUFINC; + *buf = (char *)xrealloc(*buf, *maxlen); + } + strcat(*buf, str); + *blen += slen; +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: addchar (from macro) + * + * Purpose: add a single char to a buffer + * + * Returns: none + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static void +addchar (char **buf, int *blen, int *maxlen, int c) +#else +static void addchar(buf, blen, maxlen, c) + char **buf; + int *blen; + int *maxlen; + int c; +#endif +{ + char tbuf[2]; + + tbuf[0] = c; + tbuf[1] = '\0'; + addstring(buf, blen, maxlen, tbuf); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: lookupkeywords (from macro) + * + * Purpose: lookup a name in a list of keywords + * + * Returns: return the associated value or NULL + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static char * +lookupkeywords (char *name, char **keyword, char **value, int nkey) +#else +static char *lookupkeywords(name, keyword, value, nkey) + char *name; + char **keyword; + char **value; + int nkey; +#endif +{ + int i; + for(i=0; i<nkey; i++){ + if( !strcmp(name, keyword[i]) ) + return(value[i]); + } + return(NULL); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: hexval (from strtoul16) + * + * Purpose: return the int value corresponding to a hex character + * + * Returns: hex value or -1 if the character is not legal hex + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +static int +hexval (int c) +#else +static int hexval(c) + int c; +#endif +{ + switch(c){ + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'A': return 10; + case 'a': return 10; + case 'B': return 11; + case 'b': return 11; + case 'C': return 12; + case 'c': return 12; + case 'D': return 13; + case 'd': return 13; + case 'E': return 14; + case 'e': return 14; + case 'F': return 15; + case 'f': return 15; + default: return -1; + } +} + +/* ************************************************************************** + * + * + * PUBLIC ROUTINES + * + * + * **************************************************************************/ + +/* + *---------------------------------------------------------------------------- + * + * Routine: word + * + * Purpose: a simple spaced parser + * + * Returns: 1 if word was found, else 0 + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +word (char *lbuf, char *tbuf, int *lptr) +#else +int word(lbuf, tbuf, lptr) + char *lbuf; + char *tbuf; + int *lptr; +#endif +{ + int ip; + int i; + char quotes; + + /* reset last delimiter */ + lastd='\0'; + + /* null out the output string */ + *tbuf = '\0'; + + /* if no string was specified, just return */ + if( lbuf == NULL ) + return(0); + + /* just a more convenient pointer ... */ + ip = *lptr; + + /* if we are at the end of string, just return */ + if( lbuf[ip] == '\0' ) + return(0); + + /* skip over white space */ + while( isspace((int)lbuf[ip]) || (dtable[(int)lbuf[ip]]>0) ){ + if( lbuf[ip] == '\0' ){ + *lptr = ip; + return(0); + } + else + ip++; + } + + /* check for an explicit quote */ + quotes = '\0'; + if( lbuf[ip] == '"' ){ + quotes = '"'; + lastd = '"'; + } + if( lbuf[ip] == '\'' ){ + quotes = '\''; + lastd = '\''; + } + + /* grab next token */ + if( quotes != '\0' ){ + /* bump past quotes */ + ip++; + /* grab up to next quotes -- but skip escaped quotes */ + for(i=0; lbuf[ip] != '\0'; i++, ip++){ + if( (lbuf[ip] == quotes) && (lbuf[ip-1] != '\\') ) + break; + else + tbuf[i] = lbuf[ip]; + } + } + else{ + /* grab up to next whitespace */ + for(i=0; + lbuf[ip] && !isspace((int)lbuf[ip]) && (dtable[(int)lbuf[ip]]==0); + i++, ip++) + tbuf[i] = lbuf[ip]; + /* save this delimiter */ + lastd = lbuf[ip]; + } + /* bump past delimiter (but not null terminator) */ + if( lbuf[ip] ) + ip++; + + /* null terminate */ + tbuf[i] = '\0'; + + /* got something */ + *lptr = ip; + return(1); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: newdtable + * + * Purpose: save the current delim table and init a new one + * + * Returns: 1 if another delim table can be allocated, 0 otherwise + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +newdtable (char *s) +#else +int newdtable(s) + char *s; +#endif +{ + int i; + char *cur; + + if( ndtable >= MAXDTABLES ){ + fprintf(stderr, "ERROR: no more delimiter tables available\n"); + return(0); + } + /* save another dtable */ + ndtable++; + /* allocate new space for this table */ + dtables[ndtable-1] = (char *)xmalloc(MAXDELIM); + cur = dtables[ndtable-1]; + /* copy and zero the old table */ + for(i=0; i<MAXDELIM; i++){ + cur[i] = dtable[i]; + dtable[i] = 0; + } + /* add delims to the new table */ + if( s != NULL ){ + for(; *s; s++) + dtable[(int)*s] = 1; + } + return(1); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: freedtable + * + * Purpose: restore last delim table as the current + * + * Returns: 1 if there is a table to restore, else 0 + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +freedtable (void) +#else +int freedtable() +#endif +{ + int i; + char *cur; + + if( ndtable <= 0 ){ + fprintf(stderr, "ERROR: no delimiter tables to restore\n"); + return(0); + } + cur = dtables[ndtable-1]; + /* copy the restored table into 'current' */ + for(i=0; i<MAXDELIM; i++){ + dtable[i] = cur[i]; + } + /* free up this dtable */ + xfree((void *)cur); + /* one less dtable to worry about */ + ndtable--; + return(1); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: newdelim + * + * Purpose: add a string delimiters to the parse table + * + * Returns: none + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +void +newdelim (char *s) +#else +void newdelim(s) + char *s; +#endif +{ + if( s != NULL ){ + for(; *s; s++) + dtable[(int)*s] = 1; + } +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: freedelim + * + * Purpose: remove delims from current delim table + * + * Returns: none + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +void +freedelim (char *s) +#else +void freedelim(s) + char *s; +#endif +{ + int i; + + if( s ){ + for(; *s; s++) + if( dtable[(int)*s] > 0 ) + dtable[(int)*s] -= 1; + } + else{ + for(i=0; i<MAXDELIM; i++) + if( dtable[i] > 0 ) + dtable[i] -= 1; + } +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: lastdelim + * + * Purpose: return the last delimiter + * + * Returns: delim character + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +lastdelim (void) +#else +int lastdelim() +#endif +{ + return((int)lastd); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: tmatch + * + * Purpose: match string to a template + * + * the legal meta characters in a template are just like the + * C-shell meta characters, i.e: + * ? match any character, but there must be one + * * match anything, or nothing + * [<c>...] match an inclusive set + * + * + * Returns: non-zero if match, zero otherwise + + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +tmatch (char *string, char *xtemplate) +#else +int tmatch(string, xtemplate) + char *string; + char *xtemplate; +#endif +{ + char *lastmeta=0; + char *nonabsorbed=0; + int sptr=0; + int tptr=0; + + /* loop through string and template */ + while( (xtemplate[tptr] != '\0') || (string[sptr] != '\0') ){ + /* if exact match, just bump both pointers */ + if( string[sptr] == xtemplate[tptr] ){ + sptr++; tptr++; continue; + } + /* if range character, check ranges */ + if( xtemplate[tptr] == RANGE ){ + if( checkrange(xtemplate, &tptr, string[sptr]) == 0 ){ + /* no match - was there a meta character before */ + if( lastmeta == 0 ) return(0); + /* if so, back up to it and try again */ + xtemplate = lastmeta; tptr=0; + /* begin checking at the non-absorbed point */ + string = nonabsorbed; sptr=0; + continue; + } + /* got a match, so bump past */ + else{ + sptr++; continue; + } + } + /* if ANY, any character if fine, but there must be one */ + if( xtemplate[tptr] == ANY ){ + if( string[sptr] == '\0' ) + return(0); + else{ + sptr++; tptr++; continue; + } + } + /* if ALL, we can match anything */ + if( xtemplate[tptr] == ALL ){ + /* remember where the * is */ + lastmeta = &xtemplate[tptr]; + tptr++; + /* no more template after this means a win */ + if( xtemplate[tptr] == '\0' ) return(1); + /* if the next template char is not a meta, + we skip up to its match in the string */ + if( xtemplate[tptr] == RANGE){ + while( checkrange(xtemplate, &tptr, string[sptr]) == 0 ){ + /* missing the next template char */ + if( string[sptr] == '\0' ) return(0); + sptr++; + } + /* remember the first non-absorbed character */ + nonabsorbed = &string[sptr];nonabsorbed++; + sptr++; + continue; + } + /* skip past characters, if next template char is not a meta */ + else if( xtemplate[tptr] != ANY && xtemplate[tptr] != ALL ){ + while(string[sptr] != xtemplate[tptr]){ + /* not finding the next template char + is bad */ + if( string[sptr] == '\0' ) return(0); + sptr++; + } + /* remember the first non-absorbed character */ + nonabsorbed = &string[sptr];nonabsorbed++; + continue; + } + else{ + /* remember the first non-absorbed character */ + nonabsorbed = &string[sptr];nonabsorbed++; + continue; + } + } + /* no match, no meta char - see if we once had a meta */ + else{ + if( lastmeta == 0 ) return(0); + /* if so, back up to it and try again */ + xtemplate = lastmeta; tptr=0; + /* begin checking at the non-absorbed point */ + string = nonabsorbed; sptr=0; + continue; + } + } + /* matched to the nulls - we win */ + return(1); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: keyword + * + * Purpose: look for a keyword=<value> string inside another string, + * remove and return the <value> in another buffer + * + * Returns: len if keyword was found, 0 otherwise + * + * NB: ibuf cannot be static, as it is modified in place + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +keyword (char *ibuf, char *key, char *obuf, int maxlen) +#else +int keyword(ibuf, key, obuf, maxlen) + char *ibuf; + char *key; + char *obuf; + int maxlen; +#endif +{ + int len; + int qlev; + char *s; + char *t; + char *u; + char *v; + char *iptr; + char quote; + + /* if we have no input string, we are done */ + if( (ibuf == NULL) || (*ibuf == '\0') ){ + return(0); + } + + /* start out pessimistically */ + *obuf = '\0'; + iptr = ibuf; + + /* maxlen generally is 1 more than we can handle */ + maxlen--; + + /* keep trying */ + while( *iptr ){ + /* look for key from current position */ + if( (s = (char *)strstr(iptr, key)) == NULL ) + return(0); + /* if we found a key, we need to make sure ... */ + /* it must be preceeded by beginning of string, beginning of bracket, + or by a "," from previous keyword */ + if( (s == ibuf) || (*(s-1) == ',') || (*(s-1) == '[') ){ + /* it can be followed by spaces ... */ + t = s + strlen(key); + while( isspace((int)*t) ) + t++; + /* but must be followed by an "=" */ + if( *t == '=' ){ + t++; + /* skip spaces again */ + while( isspace((int)*t) ) + t++; + /* this is where the actual value part of the string begins */ + u = t; + /* this will be where it ends */ + v = t; + /* gather up everything to the next "," or end of filter */ + if( (*t == '"') || (*t == '\'') || (*t == '(') || (*t == '[') ){ + switch(*t){ + case '"': + case '\'': + quote = *t; + break; + case '(': + quote = ')'; + break; + case '[': + quote = ']'; + break; + } + /* bump past opening quote char */ + t++; u++; v++; + while( *t && (*t != quote) ){ + t++; v++; + } + if( *t == quote ){ + t++; + } + } + else{ + qlev = 0; + while( *t && + ((qlev != 0) || (*t != ',')) && + ((qlev != 0) || (*t != ']')) ){ + if( *t == '[' ) + qlev++; + else if( *t == ']' ) + qlev--; + t++; v++; + } + } + len = MIN(maxlen, v - u); + strncpy(obuf, u, len); + obuf[len] = '\0'; + /* remove keyword=value string from the original buffer */ + /* first remove preceding comma, if necessary */ + if( (s > ibuf) && (*(s-1) == ',') ) + s--; + /* but leave 1 comma in place */ + else if( *t == ',' ) + t++; + /* now overwrite original from where the keyword started */ + memmove(s, t, strlen(t)+1); + return(len); + } + } + /* start next search just past this one */ + iptr = s+1; + } + return(0); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: macro + * + * Purpose: expand a macro using a client's callback + * + * Returns: expanded macro as an allocated string + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +char * +macro (char *icmd, char **keyword, char **value, int nkey, + MacroCB client_callback, void *client_data) +#else +char *macro(icmd, keyword, value, nkey, client_callback, client_data) + char *icmd; + char **keyword; + char **value; + int nkey; + MacroCB client_callback; + void *client_data; +#endif +{ + int i, j; + int maxlen; + char brace; + char *result; + char tbuf[1000]; + char tbuf1[1000]; + char *s; + char *ip; + char *mip; + + /* make a new string using the command as a base, but substituting + for "$" values as needed */ + result = (char *)xmalloc(BUFINC+1); + maxlen = BUFINC; + *result = '\0'; + for(i=0, ip=icmd; *ip; ip++){ + if( *ip != '$' ){ + addchar(&result, &i, &maxlen, *ip); + } + else{ + /* save beginning of macro */ + mip = ip; + /* skip past '$' */ + ip++; + /* check for brace mode */ + if( *ip == '{' ){ + brace = '{'; + ip++; + } + else if( *ip == '(' ){ + brace = '('; + ip++; + } + else + brace = '\0'; + /* get variable up to next non-alpha character or close brace */ + for(*tbuf='\0', j=0; *ip; ip++ ){ + /* if we are in brace mode, look for trailing brace */ + if( brace && *ip == (brace == '(' ? ')' : '}') ){ + ip++; + break; + } + /* else look for a non-alpha character */ + else if( !isalnum((int)*ip) && *ip != '_'){ + break; + } + else{ + tbuf[j++] = *ip; + tbuf[j] = '\0'; + } + } + /* back up so the outer loop adds this delimiting char to the output */ + ip--; + /* search for keyword from the list */ + if( (nkey > 0) && + (s=lookupkeywords(tbuf, keyword, value, nkey)) != NULL ){ + addstring(&result, &i, &maxlen, s); + } + /* execute the client routine to expand macros */ + else if( (client_callback != NULL) && + ((s=(*client_callback)(tbuf, client_data)) != NULL) ){ + addstring(&result, &i, &maxlen, s); + } + /* look for an environment variable */ + else if( (s = (char *)getenv(tbuf)) != NULL ){ + addstring(&result, &i, &maxlen, s); + } + /* if we don't recognize this macro, put it back onto the string */ + else{ + int len; + len = ip - mip + 1; + strncpy(tbuf1, mip, len); + tbuf1[len] = '\0'; + addstring(&result, &i, &maxlen, tbuf1); + } + } + } + /* null terminate and save the string */ + result[i] = '\0'; + result = (char *)xrealloc(result, i+1); + return(result); +} + +/* ************************************************************************** + * + * misc word routines + * + * **************************************************************************/ + +/* + *---------------------------------------------------------------------------- + * + * Routine: cluc + * + * Purpose: convert lower to upper case string in place + * + * Returns: none + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +void +cluc (char *s) +#else +void cluc(s) + char *s; +#endif +{ + while(*s){ + if( islower((int)*s) ) + *s = toupper(*s); + s++; + } +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: culc + * + * Purpose: convert upper to lower case string in place + * + * Returns: + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +void +culc (char *s) +#else +void culc(s) + char *s; +#endif +{ + while(*s){ + if( isupper((int)*s) ) + *s = tolower(*s); + s++; + } +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: nowhite + * + * Purpose: removes all beginning and ending white space from string + * + * Returns: returns the number of characters + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +nowhite ( + char *c, /* buffer to be cleaned */ + char *cr /* buffer for returned string */ +) +#else +int nowhite(c,cr) +char *c; /* buffer to be cleaned */ +char *cr; /* buffer for returned string */ +#endif +{ + char *cr0; /* initial value of cr */ + int n; /* the number of characters */ + + /* skip leading white space */ + while(*c && isspace((int)*c)) + c++; + /* copy up to the null */ + cr0 = cr; + while(*c) + *cr++ = *c++; + n = cr - cr0; /* the number of characters */ + *cr-- = '\0'; /* Null and point to the last character */ + /* remove trailing white space */ + while( n && isspace((int)*cr) ){ + *cr-- = '\0'; + n--; + } + return(n); +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: nocr + * + * Purpose: remove trailing <CR> from a string (in place) + * + * Returns: none + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +void +nocr (char *s) +#else +void nocr(s) + char *s; +#endif +{ + int len; + + if( (s==NULL) || (*s=='\0') ) + return; + len = strlen(s); + if( s[len-1] == '\n' ) + s[len-1] = '\0'; +} + +/* + *---------------------------------------------------------------------------- + * + * Routine: istrue + * + * Purpose: check if a string is "true" or "yes" or "on" + * + * Returns: 1 if true string, 0 otherwise + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +istrue (char *s) +#else +int istrue(s) + char *s; +#endif +{ + char *t; + int result; + + if( (s==NULL) || (*s=='\0') ) + return(0); + t = (char *)xmalloc(strlen(s)+1); + nowhite(s, t); + culc(t); + result = (!strcmp(t, "true") || !strcmp(t, "yes") || + !strcmp(t, "on") || !strcmp(t, "1") ); + xfree(t); + return(result); +} + + +/* + *---------------------------------------------------------------------------- + * + * Routine: isfalse + * + * Purpose: check if a string is "false" or "no" or "off" + * + * Returns: 1 if false string, 0 otherwise + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +int +isfalse (char *s) +#else +int isfalse(s) + char *s; +#endif +{ + char *t; + int result; + + if( (s==NULL) || (*s=='\0') ) + return(0); + t = (char *)xmalloc(strlen(s)+1); + nowhite(s, t); + culc(t); + result = (!strcmp(t, "false") || !strcmp(t, "no") || + !strcmp(t, "off") || !strcmp(t, "0") ); + xfree(t); + return(result); +} + + +/* + *---------------------------------------------------------------------------- + * + * Routine: strtoul16 + * + * Purpose: convert a string to an unsigned long hex value + * + * Returns: converted hex value (end of converted string is in t) + * + *---------------------------------------------------------------------------- + */ +#ifdef ANSI_FUNC +unsigned long strtoul16 (char *s, char **t) +#else +unsigned long strtoul16(s, t) + char *s; + char **t; +#endif +{ + unsigned long v=0; + int h; + + while ( *s != ' ' && + *s != '\n' && + *s != '\r' && + *s != '\0' ){ + v *= 16; + if( (h = hexval(*s)) >= 0 ){ + v += h; + s++; + } + else{ + break; + } + } + if( t != NULL ) + *t = s; + return(v); +} |