Wednesday, July 18, 2007

Two C++ snippets

These code fragments do not belong to AlderPump. I'm posting them here because they came handy few times in the past (about every 4 years), and every time I spent hours searching for them in the piles of files in my source code directories. Let the Internet store them now, hopefully they will be easier to find.

The first snippet parses SQL statement for bind variables. In Oracle, bind variables begin with colon and followed by variable name: INSERT INTO tbl(col1, col2, col3) VALUES(:val1, :val2, :val3). Here, val1, val2, and val3 are bind variables.

Function sqlNextBind() is called with an argument, pointing at the beginning of the statement. It returns pointer to the next bind variable or NULL at the end. To find next variable, call the function with the pointer returned by the previous call, plus one.

Literals (strings, enclosed in single quotes) are recognized and skipped, even for strings contain embedded quotes (by convention, such quotes are doubled).

SQL comments are recognized and skipped too: both /* multi-line */ and -- single line As with SQL, nested multi-line comments are not supported.

Function sqlCountBinds() demonstrates how to use sqlNextBind().
const char *sqlNextBind(const char *ptr) {
int lit = 0, cmt = 0;
for(; ptr && *ptr; ptr++) {
if( !cmt ) {
if( *ptr == '\'' ) lit = !lit;
if( !lit ) {
if( ptr[0] == ':' ) break;
if( ptr[0] == '-' && ptr[1] == '-' ) {
ptr = strchr(ptr+2, '\n');
if( !ptr ) break;
} else if( ptr[0] == '/' && ptr[1] == '*' ) {
ptr++;
cmt = 1;
}
}
} else if( ptr[0] == '*' && ptr[1] == '/' ) {
ptr++;
cmt = 0;
}
}

return ptr;
}

int sqlCountBinds(const char *stmt) {
int cnt;
const char *p = stmt-1;
for(cnt=0; (p = sqlNextBind(p+1)) && p && *p; cnt++) /* do nothing */;
return cnt;
}
Another snippet comes from unknown author. I found it 20 years ago in a program whose name is forgotten. Nevertheless the code had not rusted, it still works perfectly. The program matches string against a wildcard pattern, just like OS does when looking for files matching the mask. Recognized wildcards are '*' and '?', the comparison is case-insensitive. To make it case-sensitive, remove calls to tolower().

static bool matches(const char *pString, const char *pWild) {
register int i;
register bool star;

new_segment:
star = false;
while( *pWild == '*') { star = true; pWild++; }

test_match:
for (i = 0; pWild[i] && (pWild[i] != '*'); i++) {
if(tolower(pWild[i]) != tolower(pString[i])) {
if( !pString[i] ) return false;
if( pWild[i] == '?' ) continue;
if( !star) return false;
pString++;
goto test_match;
}
}

if( pWild[i] == '*' ) {
pString += i;
pWild += i;
goto new_segment;
}

if( !pString[i] ) return true;
if( i && pWild[i-1] == '*' ) return true;
if( !star ) return false;
pString++;
goto test_match;
}
PS: The indentation got partially lost during formatting. This blog editor is not well suited for code.

No comments: