Rev 5882 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*============================================================================** Copyright (C) 1998-2012 Vix Technology, All rights reserved**============================================================================**** Project/Product :** Filename : JatsFileUtil.c** Author(s) : DDP**** Description : Jats Build System File utility** Used by the generated makefiles to perform specific operations**** The program exists to solve problems:** Windows: Shell is very very slow to start up** Windows: Some commands have ~260 character path length issue** Windows/Solaris/Linux: Compatibility issues with the 'rm' command** All: Consistent use of '/' as a path separator**** Note: There are two flavors of this program that MUST be** kept in sync.**** The program will perform the following operations:** (c) CopyFile** (d) DeleteFile** (r) Remove Files (wildcard)** (D) DeleteDir after deleting specified files (wildcard)** (T) Delete Directory Trees** (R) Remove Files and Empty Directories** (P) Print Arguments**** Example Usage**** JatsFileUtil c9 'copyFile' aaa/bbb/ccc/dddd/file build_test.pl +w+x+l** JatsFileUtil d9 'unCopyFile' aaa/bbb/ccc/dddd/file** JatsFileUtil r9 'deleteFile' a1 b2 c3** JatsFileUtil D9 'DeleteFiles' src/WIN32P.OBJ *.err *.pch '*'** JatsFileUtil T9 'DeleteTree' interface** JatsFileUtil R9 'RmItem' build**** First two arguments are common to all** argv[1] - Mode specifier, Debug specifier** argv[2] - Display Text**** Information :** Compiler : ANSI C** Target : Windows 2000+, Linux, Solaris8+*****==========================================================================*/#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <stdlib.h>#include <dirent.h>#include <string.h>void ErrorExit (char * lpszMessage, char * lpszMessage2);void createPaths ( char *path );void DeleteDir( int argc, char* argv[] );char * makePath( char *base, char *path);void DeleteOneDirectoryTree( char* baseDir );void copyOneFile( int argc, char* argv[] );int CopyFile ( char *src, char *dst, mode_t st_mode );int CopyLink ( char *src, char *dst, int lsize );void RmItem( int argc, char* argv[] );int wildcmp(char *string, char *wild );int DeleteOneFile (char * dst );void DeleteOneDirectory (char *path );void stdCheck( char *name, int argBad, char *txt );void url_decode(char *str);/*** Global*/char verbose = 1; /* Debugging aid */char *helpText ="Usage: JatsFileUtil Mode Text Arguments\n""\n"" Where 'Mode' is two characters:\n"" 1 - Operation Specifier\n"" 2 - Debug Mode. 0..9\n"" Where 'Text' is a, possibly empty, display string\n""\n"" By Example:\n"" c9 copyFile dstPath srcPath modes(wxl)\n"" d9 unCopyFile dstPath\n"" r9 RmFile file+\n"" D9 DeleteFiles dstDir file+ - supports (?*)\n"" T9 DeleteTree dstDir+\n"" R9 RmItem (dir|file)+\n"" P9 PrintArgs ...\n";/*----------------------------------------------------------------------------** FUNCTION : main**** DESCRIPTION : Main entry points****** INPUTS : argc - Argument Count** argv - Argument Vector**** RETURNS : 0 - All is good**----------------------------------------------------------------------------*/int main(int argc, char* argv[]){int ii;/*** User must provide some thing*/if ( argc < 2 ){fprintf(stderr, helpText );return 1;}/*** Examine the first argument** Must be a character string of** [0] - Mode : One character** [1] - Verbose : 0 .. 9*/if ( argc > 1 && ( argv[1][1] >= '0' && argv[1][1] <= '9' ) ){verbose = argv[1][1] - '0';}/*** URL Decode most arguments** To get past the stupidities of shells and make the arguments** will have been URL(modified) encoded**** Decode all args in place*/for ( ii = 2; ii < argc ; ii++ ){url_decode(argv[ii]);/*** If Verbose, then display arguments*/if ( verbose > 2 )fprintf(stderr, "Arg%d: %s:\n", ii, argv[ii] );}fflush(stderr) ;/*** Switch to required operation*/switch( argv[1][0] ){/*** CopyFile** argv[2] - Text** argv[3] - target path** argv[4] - Source path** argv[5] - File attributes*/case 'c':stdCheck( "CopyFile", argc != 6, NULL );fprintf(stderr, "---- %s %s\n", argv[2], argv[3]);fflush(stderr) ;copyOneFile(argc, argv);break;/*** UnCopy a file** argv[2] - Text** argv[3] - target path*/case 'd':stdCheck( "UnCopy", argc != 4, NULL );fprintf(stderr, "---- %s %s\n", argv[2], argv[3]);fflush(stderr) ;DeleteOneFile(argv[3]);break;/*** Remove named files** argv[2] - Text** argv[3]+ - target path*/case 'r':stdCheck( "RmFile", argc <= 3, argv[2] );for ( ii = 3; ii < argc ; ii++ ){DeleteOneFile(argv[ii]);}break;/*** Delete files in directory - with wildcards** argv[2] - Text** argv[3] - Base directory** argv[4]+ - Files in dir to delete.*/case 'D':stdCheck( "DeleteDir", argc < 4, argv[2] );DeleteDir(argc, argv );break;/*** Delete files recursively** argv[2] - Text** argv[3]+ Base directory*/case 'T':stdCheck( "DeleteDirTree", argc < 3, argv[2] );for ( ii = 3; ii < argc ; ii++){DeleteOneDirectoryTree( argv[ii] );}break;/*** Delete Empty Directories** argv[2] - Text** argv[3]+ Base directory*/case 'R':stdCheck( "RmItem", argc < 3, argv[2] );RmItem(argc, argv );break;/*** Print arguments with a space betweenn them** All on the same line** argv[2]+ - Text*/case 'P':for ( ii = 2; ii < argc ; ii++ ){if ( ii > 2 )fprintf(stderr, " ");fprintf(stderr, "%s", argv[ii] );}fprintf(stderr, "\n");break;default :ErrorExit("Unknown mode: ",argv[1]);break;}return 0;}/*----------------------------------------------------------------------------** FUNCTION : stdCheck**** DESCRIPTION : Check arg count** Print standard header****** INPUTS : name - Name of the operation** argBad - Arg count Not Ok** text - Text to print**** RETURNS : Will not return on error**----------------------------------------------------------------------------*/void stdCheck( char *name, int argBad, char *txt ){if ( argBad ){fprintf(stderr, "JatsFileUtil:Incorrect argument count for %s\n", name);ErrorExit(NULL, NULL);}/*** Display user text** Suppress display if the message is empty*/if ( txt && *txt ){fprintf(stderr, "%s\n",txt);fflush(stderr) ;}}/*----------------------------------------------------------------------------** FUNCTION : createPaths**** DESCRIPTION : Create the path to the target****** INPUTS : path**** RETURNS : Will not return in error**----------------------------------------------------------------------------*/void createPaths ( char *path ){struct stat fstat;int rv;char *ptr = path;while ( *ptr ){if ( *ptr == '/' ){*ptr = 0;/* fprintf(stderr, "createPaths: %s\n", path); */rv = stat( path, &fstat );if ( rv ){if ( verbose > 1 ){fprintf(stderr, "createPaths: %s\n", path);fflush(stderr) ;}rv = mkdir( path, 0777 );if ( rv ){ErrorExit("Could not create directories:", path);}}*ptr = '/';}ptr++;}}/*----------------------------------------------------------------------------** FUNCTION : copyOneFile**** DESCRIPTION : Copy one file to a target** Used to package and install files****** INPUTS : argc - Argc count** argv - Argument list** argv[2] - Display text Prefix** argv[3] - Target path** argv[4] - Source Path** argv[5] - File attributes / Copy mode**** RETURNS :**----------------------------------------------------------------------------*/void copyOneFile( int argc, char* argv[] ){int rv;char * src;char * dst;struct stat fstat;struct stat lfstat;int symlink = 0;dst = argv[3];src = argv[4];/*** Check that the source is a file*/if ( verbose > 2)fprintf(stderr, "Validate Source File: %s\n", src);rv = stat( src, &fstat );if ( rv != 0 ){/* Need to be a better message */fprintf(stderr, "Source: %s\n", src);ErrorExit("Source File not found: ", argv[4]);}/*** Delete the destination file before the copy** Will force it to be writable*/DeleteOneFile(dst);/*** Create directories** Use the path to the target - not the provided directory** as the createPaths function will not create the last element*/createPaths( dst );/*** Sometimes symlinks are handled differently** If marked as a symlink** And it is a symlink*/if (strstr( argv[5], "+l" )){rv = lstat(src, &lfstat);if ( rv == 0 && S_ISLNK(lfstat.st_mode)){if ( verbose > 2)fprintf(stderr, "Copy Symlink: %s\n", src);symlink = 1;if ( ! CopyLink( src, dst, lfstat.st_size ) ){ErrorExit("Copy Symlink Error: ", argv[4]);}/*** Test for file (symlink) existence** Doesn't need the target to be present*/if ( verbose > 1 )fprintf(stderr, "Test symlink was created: %s\n", dst);rv = lstat( dst, &lfstat );if ( rv != 0 ){/* Need to be a better message */ErrorExit("Symlink not found after creation: ", argv[3]);}}}if (!symlink){/*** Copy the file*/if ( ! CopyFile( src, dst, fstat.st_mode ) ){ErrorExit("Copy Error: ", argv[4]);}/*** Test for files existence*/if ( verbose > 1 )fprintf(stderr, "Test target was copied: %s\n", dst);rv = stat( dst, &fstat );if ( rv != 0 ){/* Need to be a better message */ErrorExit("File not found after copy: ", argv[3]);}/*** Set the files attributes, unless copying a symlink** Assume read-only*/if (strstr(argv[5], "-w")){if ( verbose > 1 )fprintf(stderr, "Set target read-only: %s\n", dst);fstat.st_mode &= ~(S_IWRITE | S_IWOTH | S_IWGRP );}if ( strstr( argv[5], "+w" ) ){if ( verbose > 1 )fprintf(stderr, "Set target writable: %s\n", dst);fstat.st_mode |= (S_IWRITE | S_IWOTH | S_IWGRP );}if ( strstr( argv[5], "+x" ) ){if ( verbose > 1 )fprintf(stderr, "Set target executable: %s\n", dst);fstat.st_mode |= ( S_IXUSR | S_IXOTH | S_IXGRP );}if ( strstr( argv[5], "-x" ) ){if ( verbose > 1)fprintf(stderr, "Set target not executable: %s\n", dst);fstat.st_mode &= ~( S_IXUSR | S_IXOTH | S_IXGRP );}if ( verbose > 1 )fprintf(stderr, "Set target perms: %s, 0%o\n", dst, fstat.st_mode);rv = chmod( dst, fstat.st_mode );if ( rv != 0 ){ErrorExit("Setting ReadOnly: ", argv[3]);}}}/*----------------------------------------------------------------------------** FUNCTION : CopyFile**** DESCRIPTION : Just copy a file****** INPUTS : src - source path - already exists** dst - path. Dirs already exist** st_mode - Creation mode. Copied from source file**** RETURNS : false - Error**----------------------------------------------------------------------------*/#define COPYSIZE 1024int CopyFile ( char *src, char *dst, mode_t st_mode ){ssize_t rlen = 0 ;ssize_t wlen = 0 ;int in;int out;int ferror = 0;char buffer[COPYSIZE] = { '\0' } ;if ( verbose > 2 ){fprintf(stderr, "CopyFile: Output Mode: 0%o\n", st_mode);fflush(stderr) ;}in = open( src, O_RDONLY ) ;if ( in < 0 )ErrorExit("Could not open source:", src);out = open( dst, O_WRONLY | O_CREAT, st_mode | S_IWRITE );if ( out < 0 )ErrorExit("Could not open dst:", dst);while( (rlen = read( in, buffer, COPYSIZE )) > 0 ){wlen = write( out, buffer, rlen ) ;if ( wlen != rlen ){ferror = 1;break;}}close(in) ;close(out) ;/*** File error** Delete target*/if ( ferror || rlen < 0 ){unlink(dst) ;return 0;}return 1;}/*----------------------------------------------------------------------------** FUNCTION : CopyLink**** DESCRIPTION : Just copy a symlink****** INPUTS : src - source path to a symlink - already exists** dst - path. Dirs already exist** lsize - size of the link text**** RETURNS : false - Error**----------------------------------------------------------------------------*/int CopyLink ( char *src, char *dst, int lsize ){char *linkname = malloc(lsize + 1);ssize_t rlen = 0 ;int rv;if (linkname == NULL){ErrorExit("Malloc error: %s", src);}rlen = readlink(src, linkname, lsize + 1);if (rlen < 0){ErrorExit("Reading Link: %s", src);}rv = symlink( linkname, dst);if (rv){unlink(dst) ;ErrorExit("Reading Link: %s", src);}return 1;}/*----------------------------------------------------------------------------** FUNCTION : DeleteDir**** DESCRIPTION : Delete a list of files in a specified directory** Ensure files are writable** Wilcarding is supported**** Then delete the directory - if its empty****** INPUTS : argc - count of args** argv - list of files to delete** [3]: Base directory** [4]+ Files in dir to delete**** RETURNS : Will not return on error**----------------------------------------------------------------------------*/void DeleteDir( int argc, char* argv[] ){int rv;struct stat fstat;char* baseDir;DIR *dir;struct dirent *dirent;char *target;/*** Extract the base directory from the argument list** This must be a directory*/baseDir = argv[3];/*** Ensure that the base directory exists*/rv = stat( baseDir, &fstat );if ( rv != 0 ){/*** Directory does not exists** Assume its already deleted*/if ( verbose > 1 )fprintf(stderr, "Base dir does not exist: %s\n", baseDir);return;}if ( !(fstat.st_mode & S_IFDIR)){/*** Target is not a directory** Don't do anything*/if ( verbose > 1 )fprintf(stderr, "Base dir is not a directory: %s\n", baseDir);return;}/*** Process all the suffixes** They may contain a wild card*/dir = opendir( baseDir );if ( dir == NULL ){/*** Target is not a directory** Don't do anything*/if ( verbose > 1 )fprintf(stderr, "Base dir is not a directory: %s\n", baseDir);return;}/*** Read next directory entry*/while ( (dirent = readdir(dir)) != NULL ){int ii;if ( strcmp( ".", dirent->d_name ) == 0 )continue;if ( strcmp( "..", dirent->d_name ) == 0 )continue;if ( verbose > 2 )fprintf(stderr, "Directory Entry:%s,%s\n", baseDir, dirent->d_name );/*** Compare against each item in the user list*/for ( ii = 4; ii < argc ; ii++){if ( wildcmp(dirent->d_name, argv[ii] ) ){if ( verbose > 1 )fprintf(stderr, "Matching: %s, %s --- Found\n", dirent->d_name, argv[ii] );/*** Matching file found*/target = makePath( baseDir, dirent->d_name);DeleteOneFile(target);free(target);}}}closedir(dir);/*** Finally delete the diretory** Unless its '.'*/if ( strcmp( ".", baseDir) != 0 ){DeleteOneDirectory(baseDir);}}/*----------------------------------------------------------------------------** FUNCTION : DeleteOneDirectoryTree**** DESCRIPTION : Delete an entire directory tree(s)** Force it writable and searchable before deletion** Don't follow symbolic links - just delete them**** INPUTS : path - Dir to delete**** RETURNS : Will not return on error**----------------------------------------------------------------------------*/void DeleteOneDirectoryTree( char* baseDir ){struct stat fstat;DIR *dir;struct dirent *dirent;char *target;int rv;/*** A bit of a sanity test*/if ( strcmp( ".", baseDir) == 0 || strcmp( "..", baseDir) == 0 ){fprintf(stderr, "DeleteOneDirectoryTree will not delete '.' or '..'\n");return;}/*** Ensure that the directory exists** Force writable and searchable*/rv = stat( baseDir, &fstat );if ( rv == 0 ){if ( fstat.st_mode & S_IFDIR ){mode_t newMode = fstat.st_mode | S_IRUSR | S_IWUSR | S_IXUSR;if ( fstat.st_mode != newMode ){if ( verbose > 1 )fprintf(stderr, "Force u+rwx: %s\n", baseDir);rv = chmod( baseDir, newMode );if ( rv != 0 ){fprintf(stderr, "Warning: Attempt to allow u+rwx access: %s\n", baseDir);}}}}/*** Process all entries*/dir = opendir( baseDir );if ( dir == NULL ){/*** Target is not a directory** Don't do anything*/if ( verbose > 1 )fprintf(stderr, "Base dir is not a directory: %s\n", baseDir);return;}/*** Read next directory entry*/while ( (dirent = readdir(dir)) != NULL ){if ( strcmp( ".", dirent->d_name ) == 0 )continue;if ( strcmp( "..", dirent->d_name ) == 0 )continue;if ( verbose > 2 )fprintf(stderr, "Directory Entry:%s,%s\n", baseDir, dirent->d_name );target = makePath( baseDir, dirent->d_name);rv = lstat( target, &fstat );if ( rv == 0 ){if ( fstat.st_mode & S_IFDIR ){DeleteOneDirectoryTree( target );}else{DeleteOneFile (target);}}free(target);}closedir(dir);/*** Finally delete the directory** It should now be empty*/DeleteOneDirectory(baseDir);}/*----------------------------------------------------------------------------** FUNCTION : RmItem**** DESCRIPTION : Remove Empty directories and files**** INPUTS : argc - count of args** argv - list of files to delete** [3]+ Base directory**** RETURNS : Will not return on error**----------------------------------------------------------------------------*/void RmItem( int argc, char* argv[] ){int ii;struct stat fstat;for ( ii = 3; ii < argc ; ii++){int rv;if ( verbose > 2)fprintf(stderr, "RmItem: %s\n", argv[ii]);rv = lstat( argv[ii], &fstat );if ( rv == 0 ){if (fstat.st_mode & S_IFDIR){DeleteOneDirectory(argv[ii]);}else{DeleteOneFile(argv[ii]);}}}}/*----------------------------------------------------------------------------** FUNCTION : DeleteOneFile**** DESCRIPTION : Delete a file** Low level deletion operation to be used by other functions** Force it writable before deletion** Don't follow symbolic links - just delete them**** INPUTS : path - path to the file**** RETURNS : 0 - OK (file deleted or not present)**----------------------------------------------------------------------------*/int DeleteOneFile (char * dst ){int rv;struct stat fstat;int result = 0;if ( verbose > 1)fprintf(stderr, "Delete File: %s\n", dst);rv = lstat( dst, &fstat );if ( rv == 0 ){if ( verbose )fprintf(stderr, "Delete file: %s : Attr: 0%o\n", dst, fstat.st_mode);if ( !(fstat.st_mode & S_IWRITE) ){fstat.st_mode |= S_IWRITE;rv = chmod( dst, fstat.st_mode );if ( rv != 0 ){fprintf(stderr, "Warning: Attempt to allow write access: %s\n", dst);}}if ( unlink(dst) ){fprintf(stderr, "Warning: Did not remove file: %s\n", dst);result = 1;}}return result;}/*----------------------------------------------------------------------------** FUNCTION : DeleteOneDirectory**** DESCRIPTION : Low level function to delete a directory** Assumes that checks have been performed** It is a directory** It does exist****** INPUTS : path - Target Path**** RETURNS : Nothing**----------------------------------------------------------------------------*/void DeleteOneDirectory (char *path ){if ( verbose )fprintf(stderr, "Delete Directory: %s\n", path);if ( rmdir(path)){if ( verbose )fprintf(stderr, "Directory not deleted: %s\n", path);}}/*----------------------------------------------------------------------------** FUNCTION : makePath**** DESCRIPTION : Create a path from two elements** Join 2 components together** Allocate memory****** INPUTS : base - Part 1** file - Part 2 or NULL**** RETURNS :**----------------------------------------------------------------------------*/char * makePath( char *base, char *path){int len1 = strlen(base);int len2 = strlen(path);char *data;data = (char *)malloc(len1 + len2 + 10);if ( data == NULL ){ErrorExit ("Malloc error:makePath","");}strcpy( data,base);strcpy( data + len1, "/");strcpy( data + len1 + 1, path);return data;}/*----------------------------------------------------------------------------** FUNCTION : wildcmp**** DESCRIPTION : Wildcard comparision****** INPUTS : string - String** wild - Wildcard template**** RETURNS : TRUE - Match**----------------------------------------------------------------------------*/int wildcmp(char *string, char *wild ){char *cp, *mp;while ((*string) && (*wild != '*')){if ((*wild != *string) && (*wild != '?')){return 0;}wild++;string++;}while (*string){if (*wild == '*'){if (!*++wild){return 1;}mp = wild;cp = string+1;}else if ((*wild == *string) || (*wild == '?')){wild++;string++;}else{wild = mp;string = cp++;}}while (*wild == '*'){wild++;}return !*wild;}/*----------------------------------------------------------------------------** FUNCTION : from_hex**** DESCRIPTION : Converts a hex character to its integer value** Expects well formed HEX****** INPUTS : Character to convert**** RETURNS : Character**----------------------------------------------------------------------------*/char from_hex(char ch){if ( ch >= '0' && ch <= '9' ){return ch - '0';}if ( ch >= 'A' && ch <= 'F' ){return ch - 'A' + 10;}if ( ch >= 'a' && ch <= 'f' ){return ch - 'f' + 10;}return 0;}/*----------------------------------------------------------------------------** FUNCTION : url_decode**** DESCRIPTION : Perform (modified) URL decoding of a string** Only support %nn stuff** Its done inplace****** INPUTS : str - String to process**** RETURNS : Nothing**----------------------------------------------------------------------------*/void url_decode(char *str){char *pstr = str;char *pbuf = str;while (*pstr){if (*pstr == '%'){if (pstr[1] && pstr[2]){*pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);pstr += 2;}/* } *//* } else if (*pstr == '+') { *//* *pbuf++ = ' '; */}else{*pbuf++ = *pstr;}pstr++;}*pbuf = '\0';}/*----------------------------------------------------------------------------** FUNCTION : ErrorExit**** DESCRIPTION : Error processing** Report an error and terminate process****** INPUTS : lpszMessage - Message to display**** RETURNS : Does't return** Will exit with bad code**----------------------------------------------------------------------------*/void ErrorExit (char * lpszMessage, char * lpszMessage2){if ( lpszMessage ){fprintf(stderr, "JatsFileUtil:Error:%s%s\n", lpszMessage,lpszMessage2);}fflush(stderr) ;exit(-1);}