// @ZBS { // *MODULE_NAME File Specificaiton Parser // *MASTER_FILE 1 // +DESCRIPTION { // A portable filename & path parser // } // *PORTABILITY win32 unix // *REQUIRED_FILES zfilespec.cpp zfilespec.h // *VERSION 1.0 // +HISTORY { // } // +TODO { // } // *SELF_TEST yes // *PUBLISH yes // } // OPERATING SYSTEM specific includes: // SDK includes: // STDLIB includes: #include "string.h" #include "stdarg.h" #include "sys/stat.h" #include "assert.h" // MODULE includes: #include "zfilespec.h" // ZBSLIB includes: #ifdef WIN32 static char dirChar = '\\'; static char *dirString = "\\"; static char *dirNormal = ".\\"; #else static char dirChar = '/'; static char *dirString = "/"; static char *dirNormal = "./"; #endif static char *flipSlashes( char *_s ) { char *s = _s; while( *s ) { if( *s == '\\' ) { *s = '/'; } else if( *s == '/' ) { *s = '\\'; } s++; } return _s; } static void convertSlashes( char *s ) { while( *s ) { if( *s == '\\' || *s == '/' ) { *s = dirChar; } s++; } } static char *stripLeadingSlashes( char *s ) { while( *s && *s == dirChar ) { s++; } return s; } static char *stripTrailingSlashes( char *s ) { int l = strlen( s ); char *t = &s[l-1]; while( t > s && *t == dirChar ) { *t++ = 0; } return s; } static char *stripLeadingDots( char *s ) { while( *s && *s == '.' ) { s++; } return s; } static char *stripTrailingDots( char *s ) { int l = strlen( s ); char *t = &s[l-1]; while( t > s && *t == '.' ) { *t-- = 0; } return s; } static char *stripLastExt( char *s ) { int l = strlen( s ); char *t = &s[l-1]; while( t > s ) { if( *t == '.' ) { *t = 0; return s; } if( *t == dirChar || *t == ':' ) { return s; } t--; } return s; } static char *stripAllExt( char *s ) { int l = strlen( s ); char *t = &s[l-1]; while( t > s ) { if( *t == '.' ) { *t = 0; } if( *t == dirChar || *t == ':' ) { return s; } t--; } return s; } char *zFileSpecMake( int firstToken, ... ) { va_list argptr; va_start( argptr, firstToken ); char __s[256]; char *s; char *d = ZFileSpec::getNextWorkBuffer(); int firstDir = 1; int lastToken = 0; int token = firstToken; while( token ) { char *_s = va_arg( argptr, char * ); strcpy( __s, _s ); s = __s; // I added this extra copy due to recent default // changes in GNU which apprently make it illegal // to write into a static, or perhapse it is // merging strings and then making it illegal. // Anyways, this helps convertSlashes(s); int l = strlen(s); switch( token ) { case FS_DRIVE: if( lastToken != 0 ) { return NULL; } s = stripTrailingSlashes(s); l = strlen(s ); strcat( d, s ); if( l>0 && s[0] != dirChar ) { // If its not a UNC dir then ensure we have a colon if( s[l-1] != ':' ) { strcat( d, ":" ); } } break; case FS_DIR: if( lastToken == FS_FILE || lastToken == FS_EXT ) { return NULL; } if( !firstDir ) { s = stripLeadingSlashes(s ); } firstDir=0; s = stripTrailingSlashes(s ); l = strlen(s ); if( lastToken == 0 ) { // if first token, add a directory normalization if // there is no other slash in the string // Added a check for ':' as well; this was failing if this dir was // already fully qualified. -TNB if( !strchr(s, dirChar) && !strchr( s, ':') ) { strcat( d, dirNormal ); } } else if( strlen(d)>0 && d[strlen(d)-1]!=dirChar && *s!=dirChar ) { strcat( d, dirString ); } strcat( d, s ); if( d[strlen(d)-1]!=dirChar ) { strcat( d, dirString ); } break; case FS_FILE: if( lastToken == FS_FILE || lastToken == FS_EXT ) { return NULL; } s = stripLeadingSlashes(s ); s = stripTrailingSlashes(s ); l = strlen(s ); if( lastToken == 0 ) { // if first token, add a directory normalization if // there is no other slash in the string if( !strchr(s, dirChar) ) { strcat( d, dirNormal ); } } else if( d[strlen(d)-1]!=dirChar ) { strcat( d, dirString ); } strcat( d, s ); break; case FS_EXT: if( lastToken == FS_DRIVE || lastToken == FS_DIR || lastToken == 0 ) { return NULL; } s = stripLeadingDots(s ); s = stripTrailingDots(s ); l = strlen(s ); if( d[strlen(d)-1]!='.' ) { strcat( d, "." ); } strcat( d, s ); break; default: return NULL; } lastToken = token; token = va_arg( argptr, int ); } va_end( argptr ); return d; } int ZFileSpec::workBuffer = 0; char ZFileSpec::workBuffers[ZFILESPEC_WORK_BUFFER_COUNT][ZFILESPEC_PATH]; ZFileSpec::ZFileSpec( char *string ) { found = 0; findHandle = NULL; set( string ); } void ZFileSpec::set(char *string ) { assert( string ); path[0] = 0; if( !strchr( string, dirChar) ) { #ifdef WIN32 if( !strchr(string, ':') ) #endif strcpy( path, dirNormal ); } strcat( path, string ); convertSlashes(path ); } char *ZFileSpec::getNextWorkBuffer() { workBuffer = (++workBuffer)%ZFILESPEC_WORK_BUFFER_COUNT; char *p = workBuffers[workBuffer]; memset( p, 0, ZFILESPEC_PATH ); return p; } char *ZFileSpec::get(int normal ) { char *_d = getNextWorkBuffer(); if( normal ) { strcpy( _d, path ); } else { strcpy( _d, path+2 ); } return _d; } char *ZFileSpec::getDrive() { // This will deal correctly with UNC names. For example: // "c:\biteme\oink.cpp" -> getDrive() -> "c:" // "\\horton\c\biteme\oink.cpp" -> getDrive() -> "\\horton\" // "\c\biteme\oink.cpp" -> getDrive() -> "" // NOTE: The treatment of UNC machine names as a "drive" instead of using the // proper \ combination is intentional. This distinction should // not cause any problems. -TNB char *d = getNextWorkBuffer(); strcpy( d, path ); if( d[0] == dirChar && d[1] == dirChar ) { char *p = strchr( &d[2], dirChar ); if( p ) { *p = 0; } return d; } else { char *colon = strchr( d, ':' ); if( colon ) { *(colon+1) = 0; return d; } } *d = 0; return d; } char *ZFileSpec::getDir(int n ) { // If you pass in -1 then it will return the whole dir including the // last '\'. If you pass in >=0 then it returns that dir. // "\\horton\c\biteme\oink.cpp" -> getDir(-1) -> "\c\biteme\" // "\\horton\c\biteme\oink.cpp" -> getDir(0) -> "\c\" // "\\horton\c\biteme\oink.cpp" -> getDir(1) -> "\biteme\" // "\\horton\c\biteme\oink.cpp" -> getDir(2) -> "" // "c:\biteme\oink.cpp" -> getDir(0) -> "\biteme\" // "c:\biteme\oink.cpp" -> getDir(1) -> "" // "c:\oink.cpp" -> getDir(0) -> "\" // ".\oink.cpp" -> getDir(-1) -> ".\" // ".\oink.cpp" -> getDir(0) -> ".\" // ".\oink.cpp" -> getDir(1) -> "" // "\dir\oink.cpp" -> getDir(1) -> "\dir\" // "oink.cpp" -> getDir(-1) -> "" char *d = getNextWorkBuffer(); *d = 0; char *start = path; if( path[0] == dirChar && path[1] == dirChar ) { start = &path[2]; } for( int i=0; i<=n || n==-1; i++ ) { char *s = strchr( start, dirChar ); if( s > start && *(s-1) == '.' ) { // Deal with the normalized case s--; } if( n == -1 ) { strcpy( d, s ); int l = strlen(s ); char *_d = &d[l-1]; while( _d > d ) { if( *_d == dirChar ) { *(_d+1) = 0; break; } _d--; } return d; } char *next = strchr( s+1, dirChar ); if( next ) { strcpy( d, s ); *strchr( d+1, dirChar) = 0; start = s+1; } else if( i==0 && n<=0 ) { // Special case for root *d = dirChar; *(d+1) = 0; return d; } else { *d = 0; return d; } } if( d[strlen(d)-1] != dirChar ) { strcat( d, "\\" ); } return d; } char *ZFileSpec::getFile(int withExtension ) { // Returns the file with or without the extension( see below). // "\\horton\c\biteme\oink.cpp" -> getFile(0) -> "oink" // "\\horton\c\biteme\oink.cpp" -> getFile(1) -> "oink.cpp" // "\\horton\c\biteme\oink.cpp.lock" -> getFile(1) -> "oink.cpp.lock" // "\\horton\c\biteme\oink.cpp.lock\" -> getFile(1) -> "" // "oink.cpp.lock" -> getFile(0) -> "oink.cpp" // "oink.cpp.lock" -> getFile(1) -> "oink.cpp.lock" char *d = getNextWorkBuffer(); strcpy( d, path ); int l = strlen(d ); char *_d = &d[l-1]; while( _d >= d ) { if( *_d == dirChar || *_d == ':' ) { _d++; if( withExtension ) { return _d; } else { stripLastExt( _d ); return _d; } } _d--; } *d = 0; return d; } char *ZFileSpec::getExt() { // Returns the LAST extension. // "oink.cpp.lock.ignore" -> getExt() -> ".ignore" // "c:\oink" -> getExt() -> "" // "c:\\dir.ext\\oink" -> getExt() -> "" char *d = getNextWorkBuffer(); strcpy( d, path ); int l = strlen(d ); char *_d = &d[l-1]; while( _d >= d ) { if( *_d == '.' ) { return _d; } else if( *_d == dirChar || *_d == ':' ) { break; } _d--; } *d = 0; return d; } int ZFileSpec::getTime() { struct stat s; int a = stat( get(), &s ); return a==-1?0:s.st_mtime; } void ZFileSpec::stripExt( int stripAll ) { if( stripAll ) { stripAllExt( path ); } else { stripLastExt( path ); } } #ifdef SELF_TEST #include "assert.h" void main() { char *s; s = zFileSpecMake( FS_DRIVE, "c:", FS_DIR, "\\oink\\", FS_DIR, "\\boink", FS_DIR, "spoink\\", FS_DIR, "kerploink", FS_FILE, "file.blowme", FS_EXT, "hard", FS_END ); assert( !stricmp(s, "c:\\oink\\boink\\spoink\\kerploink\\file.blowme.hard" ) ); s = zFileSpecMake( FS_DRIVE, "\\\\horton\\", FS_DIR, "c\\oink\\", FS_DIR, "\\boink", FS_DIR, "spoink\\", FS_DIR, "kerploink", FS_FILE, "file.blowme", FS_EXT, "hard", FS_END ); assert( !stricmp(s, "\\\\horton\\c\\oink\\boink\\spoink\\kerploink\\file.blowme.hard" ) ); s = zFileSpecMake( FS_DIR, "boink", FS_FILE, "filename", FS_EXT, "ext", FS_END ); assert( !stricmp(s, ".\\boink\\filename.ext" ) ); s = zFileSpecMake( FS_FILE, "filename", FS_END ); assert( !stricmp(s, ".\\filename" ) ); s = zFileSpecMake( FS_FILE, "c:\\dir\\filename", FS_EXT, "ext", FS_END ); assert( !stricmp(s, "c:\\dir\\filename.ext" ) ); s = zFileSpecMake( FS_DIR, "c:\\dir\\dir\\", FS_FILE, "\\filename", FS_END ); assert( !stricmp(s, "c:\\dir\\dir\\filename" ) ); // getDrive s = ZFileSpec( "c:\\biteme\\oink.cpp").getDrive(); assert( !stricmp("c:",s) ); s = ZFileSpec("\\\\horton\\c\\biteme\\oink.cpp").getDrive(); assert( !stricmp("\\\\horton",s) ); s = ZFileSpec("\\c\\biteme\\oink.cpp" ).getDrive(); assert( !stricmp("",s) ); // getDir s = ZFileSpec("\\\\horton\\c\\biteme\\oink.cpp").getDir(-1 ); assert( !stricmp("\\c\\biteme\\",s) ); s = ZFileSpec("\\\\horton\\c\\biteme\\oink.cpp").getDir(0 ); assert( !stricmp("\\c\\",s) ); s = ZFileSpec("\\\\horton\\c\\biteme\\oink.cpp").getDir(1 ); assert( !stricmp("\\biteme\\",s) ); s = ZFileSpec("\\\\horton\\c\\biteme\\oink.cpp").getDir(2 ); assert( !stricmp("",s) ); s = ZFileSpec("c:\\biteme\\oink.cpp" ).getDir(0 ); assert( !stricmp("\\biteme\\",s) ); s = ZFileSpec("c:\\biteme\\oink.cpp" ).getDir(1 ); assert( !stricmp("",s) ); s = ZFileSpec("c:\\oink.cpp" ).getDir(0 ); assert( !stricmp("\\",s) ); s = ZFileSpec(".\\oink.cpp" ).getDir(-1 ); assert( !stricmp(".\\",s) ); s = ZFileSpec(".\\oink.cpp" ).getDir(0 ); assert( !stricmp(".\\",s) ); s = ZFileSpec(".\\oink.cpp" ).getDir(1 ); assert( !stricmp("",s) ); s = ZFileSpec("\\dir\\oink.cpp" ).getDir(0 ); assert( !stricmp("\\dir\\",s) ); s = ZFileSpec("\\dir\\oink.cpp" ).getDir(-1 ); assert( !stricmp("\\dir\\",s) ); s = ZFileSpec("oink.cpp" ).getDir(-1 ); assert( !stricmp(".\\",s) ); // getFile s = ZFileSpec( "\\\\horton\\c\\biteme\\oink.cpp" ).getFile(0 ); assert( !stricmp("oink",s) ); s = ZFileSpec( "\\\\horton\\c\\biteme\\oink.cpp" ).getFile(1 ); assert( !stricmp("oink.cpp",s) ); s = ZFileSpec( "\\\\horton\\c\\biteme\\oink.cpp.lock" ).getFile(1 ); assert( !stricmp("oink.cpp.lock",s) ); s = ZFileSpec( "\\\\horton\\c\\biteme\\oink.cpp.lock\\" ).getFile(1 ); assert( !stricmp("",s) ); s = ZFileSpec( "oink.cpp.lock").getFile(0 ); assert( !stricmp("oink.cpp",s) ); s = ZFileSpec( "oink.cpp.lock").getFile(1 ); assert( !stricmp("oink.cpp.lock",s) ); // GetExt s = ZFileSpec( "oink.cpp.lock.ignore" ).getExt(); assert( !stricmp(".ignore",s) ); s = ZFileSpec( "c:\\oink" ).getExt(); assert( !stricmp("",s) ); s = ZFileSpec( "c:\\\\dir.ext\\\\oink" ).getExt(); assert( !stricmp("",s) ); } #endif