/* Copyright (C) 2003, 2004 Dave Bayer. Subject to the terms and conditions of the MIT License. */ #include "root.h" #include "file.h" #include "filter.h" /*, markAlloc */ static filterMark *markAlloc( void ) { filterMark *p; p = malloc( sizeof(*p)); p->mark = 0; p->index = 0; p->next = 0; return p; } /*. markFree */ void markFree( void *data ) { filterMark *p, *q; p = data; while ( p != 0 ) { q = p->next; free( p ); p = q; } } /*. initFilterTable */ void initFilterTable( filterTable *table, filterRules *rules, unsigned nstates, BOOL clear ) { int i, j; unsigned len; filterRules *p, **pp; if ( clear ) for ( i=0; ifromStr!=0; ++p ) { if ( p->toStr == 0 ) p->toStr = p->fromStr; p->fromLen = len = strlen( p->fromStr ); p->toLen = strlen( p->toStr ); p->next = 0; /* keep entries sorted longest first, to implement longest match */ pp = &table[p->fromState][(int) *p->fromStr]; while ( *pp != 0 && (*pp)->fromLen > len ) pp = &(*pp)->next; p->next = *pp; *pp = p; } } /*. stateFilter */ void stateFilter( line *buf1, line *buf2, void *data ) { char *s, *t; filterRules *p; filterMark *q, **qq; filterState *state; state = data; qq = (filterMark **) &buf2->data; for ( s=buf1->s, t=buf2->s; *s!='\0'; ) { /* match nonempty prefixes */ for ( p=state->table[state->state][(int) *s]; p!=0; p=p->next ) if ( strncmp( s, p->fromStr, p->fromLen ) == 0 ) break; /* match empty prefix */ if ( p == 0 ) p = state->table[state->state][0]; /* found a rule */ if ( p != 0 ) { strncpy( t, p->toStr, p->toLen ); state->state = p->toState; if ( p->mark != 0 ) { q = markAlloc(); q->mark = p->mark; q->index = t - buf2->s; *qq = q; qq = &q->next; } s += p->fromLen; t += p->toLen; } /* no rule */ else if ( state->filt != 0 ) state->state = state->filt( &s, &t, state->state ); else *t++ = *s++; } *t = '\0'; buf2->len = t - buf2->s; } /* test code */ #ifdef TESTCODE /* test rules */ typedef enum { testStart, testQuote, testPhrase, testNStates } testState; static filterRules *testTable[testNStates][NCHARS]; static filterRules testRules[] = { { "\"", "", testStart, testQuote, 0 }, { "\"", "", testQuote, testStart, 0 }, { "\"", "", testPhrase, testStart, testQuote }, { " ", 0, testQuote, testQuote, 0 }, { "\t", 0, testQuote, testQuote, 0 }, { "", 0, testQuote, testPhrase, testPhrase }, { "\\\"", "\"", testStart, testStart, 0 }, { "\\\"", "\"", testPhrase, testPhrase, 0 }, { 0 } }; /*. testFilt */ static BOOL testFilt( char **ps, char **pt, unsigned state ) { if ( **ps == 'a' ) { **pt = 'A'; (*ps)++; (*pt)++; } else *(*pt)++ = *(*ps)++; return state; } /*. filterTest */ void filterTest( void ) { line *l, *m; filterMark *p; FILE *fin, *fout; lineFilter filter; filterState state; const char name[] = "TempFilterTest"; const char text[] = "here is an escaped \\\" quote\n" "testing \" filter\" and the \" mark\" mechanism\n" "does an embedded \" \\\" escaped quote\" work?\n" ; fout = fileOpen( name, "w", YES ); fputs( text, fout ); fileClose( fout, YES ); initFilterTable( testTable, testRules, testNStates, YES ); filter.filt = stateFilter; filter.next = 0; filter.data = &state; state.table = testTable; state.state = testStart; state.filt = testFilt; fin = fileOpen( name, "r", YES ); l = fileRead( fin, &filter, 0 ); fileWrite( stdout, l, 0 ); putchar( '\n' ); for( m=l; m!=0; m=m->next ) { for ( p=m->data; p!=0; p=p->next ) if ( p->mark == testQuote ) m->s[p->index] = '\0'; for ( p=m->data; p!=0; p=p->next ) if ( p->mark == testPhrase ) printf( "%s\n", m->s + p->index ); } lineListFree( l, markFree ); fileClose( fin, YES ); } #endif