/* ======================================================= *
* Copyright 1998-2005 Stephen C. Grubb *
* http://ploticus.sourceforge.net *
* Covered by GPL; see the file ./Copyright for details. *
* ======================================================= */
/* VALUE_SUBST.C - perform variable evaluation for one line */
/* Variables may be:
Field numbers like @1, @2, @3, etc. (@1 is first field)
TDH_data item names like @age
Returns 1 if one or more substitutions were done, 0 if there were no
substitutions done, or an error code > 0.
There are also options for retrieving involved fields and translating
field names to field numbers (possible uses in data cleaning / logical checking).
7/12/00 scg - unrecognized @items are now left as is, with no error being
generated.
*/
#include "tdhkit.h"
#include <ctype.h>
extern int atoi();
static int involved[ MAXITEMS ];
static int ninvolved = 0;
static int translate_to_fn = 0;
static int allowinlinecodes = 0;
static int hideund = 0;
static int suppressdll = 0;
static char varsym = '@';
static int omitws = 0;
static int shieldquotedvars = 0;
static int showwithquotes = 0;
static char punctlist[10] = "_";
static int sqlmode = 0; /* if 1, TDH_dequote will be more strict about quote errors.. */
static int omit_shell_meta = 0;
static int globqs = 0; /* if 1, TDH_dequote will only reset the var serial# when told (by a call to settings()) */
static int curvar = 1; /* the var serial# */
/* =============================== */
int
TDH_valuesubst_initstatic()
{
ninvolved = 0;
translate_to_fn = 0;
allowinlinecodes = 0;
hideund = 0;
suppressdll = 0;
varsym = '@';
omitws = 0;
shieldquotedvars = 0;
showwithquotes = 0;
strcpy( punctlist, "_" );
sqlmode = 0;
omit_shell_meta = 0;
globqs = 0;
return( 0 );
}
/* =============================== */
int
TDH_value_subst( out, in, data, recordid, mode, erronbadvar )
char *out; /* result buffer */
char *in; /* input buffer */
char data[ MAXITEMS ][ DATAMAXLEN+1 ]; /* data array */
char *recordid; /* recordid or "" */
int mode; /* either FOR_CONDEX, indicating that the line will be passed to condex(),
(minor hooks related to this) or NORMAL */
int erronbadvar;
{
int i, j, k;
char itemname[ 512 ]; /* big because arbitrary tokens are being stored in it */
char value[ DATAMAXLEN+1 ];
int found, stat, infunction, inlen, inamelen, vlen;
int lineconvatt, lineconvdone, lastgoodoutpos;
int dectab;
char fillchar[10];
char varsymstring[4];
int instring;
char oldvarsym;
int esc;
found = 0;
ninvolved = 0;
infunction = 0;
lineconvatt = lineconvdone = 0;
lastgoodoutpos = 0;
dectab = 0;
strcpy( fillchar, " " );
sprintf( varsymstring, "%c", varsym );
instring = 0;
esc = 0;
strcpy( out, "" );
j = 0; /* j will track current length of out */
for( i = 0, inlen = strlen( in ); i < inlen; i++ ) {
/* if shielding quoted vars, handle unescaped quote (") */
if( instring && i > 0 && in[ i-1 ] == '\\' && !esc ) esc = 1;
else esc = 0;
if( shieldquotedvars && in[i] == '"' && !esc ) {
if( !instring ) {
instring = 1;
oldvarsym = varsym;
varsym = '\0';
}
else if( instring ) {
instring = 0;
varsym = oldvarsym;
}
}
/* handle @@ (escape of @) */
if( in[i] == varsym && in[i+1] == varsym ) {
strcpy( &out[j], varsymstring );
j += 1;
i++;
continue;
}
if( in[i] == varsym && !isspace( (int) in[i+1] ) ) { /* @fieldname or @fieldnum .. fill 'value'.. */
sscanf( &in[i+1], "%s", itemname );
/* truncate itemname at first char which is not alphanumeric or _ (or . as of 4/24/01) */
inamelen = strlen( itemname );
for( k = 0; k < inamelen; k++ ) {
if( !isalnum( (int) itemname[k] ) && !GL_member( itemname[k], punctlist )) {
itemname[k] = '\0';
break;
}
}
inamelen = strlen( itemname );
lineconvatt++;
if( strcmp( itemname, "_RECORDID" )==0 ) strcpy( value, recordid ); /* 02/19/01 */
/* @n, @itemname, @varname.. */
else {
stat = TDH_getvalue( value, itemname, data, recordid );
if( stat != 0 ) {
if( erronbadvar ) return( err( 1402, "unrecognized symbol", itemname ) );
else sprintf( value, "%c%s", varsym, itemname ); /* replace @token on stream.. */
}
#ifndef TDH_NOREC
else {
int ifld;
/* do the following to add item to list of involved fields */
ifld = TDH_fieldmap( recordid, itemname );
if( ifld >= 0 ) involved[ ninvolved++ ] = ifld;
if( translate_to_fn ) sprintf( value, "%c%d", varsym, ifld+1 ); /* supercedes
effect of the previous lines*/
found = 1;
if( value[0] != '\0' ) lineconvdone++;
}
#endif
}
/* special case of 0 length data item when in a condex expression but
not within a function arg list.. to prevent condex syntax errors */
if( strcmp( value, "" )==0 && mode == FOR_CONDEX && !infunction )
strcpy( value, "_null_" );
if( dectab ) { /* decimal tab / right-align tab */
int dpos;
dpos = GL_member( TDH_decpt, value );
if( dpos == 0 ) dpos = strlen( value );
else dpos--;
for( k = (j-lastgoodoutpos); k < ((lastgoodoutpos + dectab)-dpos); k++ ) {
strcpy( &out[j], fillchar );
j+=strlen( fillchar );
}
dectab = 0;
}
/* append value to outbuf.. */
vlen = strlen( value );
if( hideund ) for( k = 0; k < vlen; k++ ) if( value[k] == '_' ) value[k] = ' '; /* datadelim - ok */
if( mode == FOR_CONDEX ) for( k = 0; k < vlen; k++ ) if( value[k] == ' ' ) value[k] = '_'; /* datadelim - ok */
if( showwithquotes ) {
sprintf( &out[j], "\"%s\"", value );
j += (vlen+2);
}
else if( omit_shell_meta ) {
for( k = 0; k < vlen; k++ ) {
if( strncmp( &value[k], "../", 3 )==0 ) { k+=2; continue; } /* added scg 5/19/05 */
else if( GL_member( value[k], TDH_shellmetachars )) continue;
else out[j++] = value[k];
}
out[j] = '\0';
}
else {
strcpy( &out[j], value );
j += vlen;
}
i += inamelen; /* advance past @itemname */
}
else {
if( in[i] == '$' && isalpha( (int) in[i+1] ) ) infunction = 1;
if( isspace( (int) in[i] ) ) infunction = 0; /* ???? 3/22/01 */
if( allowinlinecodes && in[i] == ':' ) {
if( strncmp( &in[i], ":u+ ", 4 )==0 ) { hideund = 0; i += 3; continue; }
else if( strncmp( &in[i], ":u- ", 4 )==0 ) { hideund = 1; i += 3; continue; }
else if( strcmp( &in[i], ":c\n" ) == 0 ) break;
else if( strncmp( &in[i], ":col", 4 )== 0 && isdigit( (int) in[i+4] )) { /* col pos */
int colnum;
colnum = atoi( &in[ i+4 ] );
for( k = (j-lastgoodoutpos); k < ((lastgoodoutpos+colnum)-2); k++ ) out[j++] = ' ';
if(colnum < 10)i+=5; else if(colnum < 100)i+=6; else if(colnum < 1000)i+=7;
}
else if( strncmp( &in[i], ":dot", 4 )== 0 && isdigit( (int) in[i+4] )) { /* leader dots to pos */
int colnum;
colnum = atoi( &in[ i+4 ] );
for( k = (j-lastgoodoutpos); k < ((lastgoodoutpos+colnum)-2); k++ ) out[j++] = '.';
if(colnum < 10)i+=5; else if(colnum < 100)i+=6; else if(colnum < 1000)i+=7;
}
else if( strncmp( &in[i], ":dec", 4 )== 0 && isdigit( (int) in[i+4] )) { /* decimal tab at pos */
dectab = atoi( &in[ i+4 ] ); strcpy( fillchar, " " );
if(dectab < 10)i+=5; else if(dectab < 100)i+=6; else if(dectab < 1000)i+=7;
}
else if( strncmp( &in[i], ":dch", 4 )== 0 && isdigit( (int) in[i+4] )) { /* decimal tab at pos */
dectab = atoi( &in[ i+4 ] ); strcpy( fillchar, " " );
if(dectab < 10)i+=5; else if(dectab < 100)i+=6; else if(dectab < 1000)i+=7;
}
}
if( in[i] == '\n' || i == inlen-1 ) { /* 2nd clause added 3/22/01 */
if( suppressdll && lineconvatt > 0 && lineconvdone == 0 ) { /* suppress line */
j = lastgoodoutpos;
lineconvatt = lineconvdone = 0;
continue;
}
lineconvatt = lineconvdone = 0;
lastgoodoutpos = j+1;
}
if( omitws && isspace( (int) in[i] ) ) continue;
out[j] = in[i];
j++;
}
}
/* when suppressdll, for case of @varname immediately followed by eol.. */
if( suppressdll && lineconvatt > 0 && lineconvdone == 0 ) j = lastgoodoutpos;
out[j] = '\0';
return( found );
}
/* ========================= */
/* GET_INVOLVED_ITEMS - allow an application to access the field #s of all
data items involved in the most recent value_subst call. Tmp vars
are not included since they have no field # */
int
TDH_get_involved_items( n, list )
int *n;
int list[ MAXITEMS ];
{
int i;
*n = ninvolved;
for( i = 0; i < ninvolved; i++ ) list[i] = involved[i];
return( 0 );
}
/* ========================= */
int
TDH_valuesubst_settings( tag, value )
char *tag;
int value; /* 1 = on, 0 = off */
{
if( strcmp( tag, "hideund" )==0 ) hideund = value;
else if( strcmp( tag, "allowinlinecodes" )==0 ) allowinlinecodes = value;
else if( strcmp( tag, "suppressdll" )==0 ) suppressdll = value;
else if( strcmp( tag, "translate_to_fn" )==0 ) translate_to_fn = value;
else if( strcmp( tag, "varsym" )==0 ) varsym = (char)value;
else if( strcmp( tag, "omitws" )==0 ) omitws = value;
else if( strcmp( tag, "shieldquotedvars" )==0 ) shieldquotedvars = value;
else if( strcmp( tag, "showwithquotes" )==0 ) showwithquotes = value;
else if( strcmp( tag, "sqlmode" )==0 ) sqlmode = value;
else if( strcmp( tag, "omit_shell_meta" )==0 ) omit_shell_meta = value;
else if( strcmp( tag, "globqs" )==0 ) {
if( value == 2 ) { curvar = 1; return( 0 ); } /* reset the var serial # scg 10/28/04 */
else globqs = value;
}
else if( strcmp( tag, "dot_in_varnames" )==0 ) {
if( value ) strcpy( punctlist, "_." );
else strcpy( punctlist, "_" );
}
return( 0 );
}
/* ===================================== */
/* DEQUOTE - scan a line and convert quoted "strings" */
int
TDH_dequote( out, in, prefix )
char *out;
char *in;
char *prefix; /* keeps string constants from colliding: sql uses QS; sinterp uses SL */
{
int i, j, k, len, instring, esc;
char tok[DATAMAXLEN+1];
char vartag[20];
char quotecharused; /* added 3/29/01 */
int truncflag;
if( !globqs ) curvar = 1; /* normally the var serial# is reset for every call (every line)
but w/ globqs it isn't - globqs added scg 10/28/04 */
truncflag = 0;
len = strlen( in );
sprintf( vartag, "_%s%02d", prefix, curvar ); /* limitation (99 _QS vars) */
instring = 0;
for( i = 0, j = 0; i < len; i++ ) {
if( instring && i > 0 && in[ i-1 ] == '\\' && !esc ) {
esc = 1;
k--; /* rm the backslash from tok */
}
else esc = 0;
/* non-quoted comment symbol encountered.. stop */
if( !instring && in[i] == '/' && in[i+1] == '/' ) break;
/* unescaped quote encountered */
if( ( in[i] == '"' || in[i] == '\'' ) && !esc ) {
if( instring && in[i] == quotecharused ) {
if( sqlmode && in[i+1] != '\0' && !isspace( (int) in[i+1] ) && !GL_member( in[i+1], ",)" ) )
return( err( 2734, "quote error", "" ));
tok[k] = '\0';
/* out[j++] = '@'; */ /* changed scg 10/28/04 */
out[j++] = varsym;
strcpy( &out[j], vartag );
j+= strlen( vartag );
/* if( mode == FOR_CONDEX && strlen( tok ) == 0 ) strcpy( tok, "_null_" ); */
TDH_setvar( vartag, tok );
curvar++;
sprintf( vartag, "_%s%02d", prefix, curvar );
instring = 0;
}
else if( instring ) { /* other quote - treat as normal char.. */
if( k >= DATAMAXLEN-1 ) { tok[DATAMAXLEN-1] = '\0'; truncflag = 1; }
tok[k++] = in[i];
}
else {
quotecharused = in[i];
instring = 1;
k = 0;
}
continue;
}
if( instring ) {
if( k >= DATAMAXLEN-1 ) { tok[DATAMAXLEN-1] = '\0'; truncflag = 1; }
tok[k++] = in[i];
}
else out[j++] = in[i];
}
if( instring ) { /* no ending quote.. */
if( sqlmode ) return( err( 2735, "mismatched quotes", "" ));
tok[k] = '\0';
/* out[j++] = '@'; */ /* changed scg 10/28/04 */
out[j++] = varsym;
strcpy( &out[j], vartag );
j+= strlen( vartag );
TDH_setvar( vartag, tok );
}
/* back up j to last non-white-space */
j--;
while( j >= 0 && isspace( (int) out[j] ) ) j-- ;
out[j+1] = '\0';
if( truncflag ) err( 1409, "value is too long and has been truncated", out );
return( 0 );
}
/* ======================================================= *
* Copyright 1998-2005 Stephen C. Grubb *
* http://ploticus.sourceforge.net *
* Covered by GPL; see the file ./Copyright for details. *
* ======================================================= */
syntax highlighted by Code2HTML, v. 0.9.1