Rev 2311 | Rev 2354 | Go to most recent revision | 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) Delete Empty Directories**** Example Usage**** JatsFileUtil c9 'copyFile' aaa/bbb/ccc/dddd/file build_test.pl +w** 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 'RmDir' 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 <tchar.h>#include <stdio.h>#include <windows.h>#define INVALID_FILE_ATTIBUTES ((DWORD) -1)VOID ErrorExit (char *lpszMessage, LPTSTR lpszMessage2);void createPaths ( _TCHAR *path );void DeleteDir( int argc, _TCHAR* argv[] );void DeleteDirTree( int argc, _TCHAR* argv[] );_TCHAR * makePath( _TCHAR *base, _TCHAR *path);int DeleteOneFile (_TCHAR * dst );void DeleteOneDirectoryTree( _TCHAR* baseDir );void copyOneFile( int argc, _TCHAR* argv[] );void RmDir( int argc, _TCHAR* argv[] );/*** Global*/char verbose = 1; /* Debugging aid *//*----------------------------------------------------------------------------** FUNCTION : main**** DESCRIPTION : Main entry points****** INPUTS : argc - Argument Count** argv - Argument Vector**** RETURNS : 0 - All is good**----------------------------------------------------------------------------*/int _tmain(int argc, _TCHAR* argv[]){/*** User must provide some thing*/if ( argc < 2 ){ErrorExit("Insufficient arguments",L"");}/*** 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';/*** If Verbose, then display arguments*/if ( verbose > 2 ){int ii;for ( ii = 0; ii < argc ; ii++ ){fprintf(stderr, "Arg%d: %ls:\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':copyOneFile(argc, argv);break;/*** UnCopy a file** argv[2] - Text** argv[3] - target path*/case 'd':{_TCHAR * dst;if ( argc != 4 )ErrorExit("Incorrect argument count for mode",L"");/*** Display user text*/fprintf(stderr, "---- %ls %ls\n", argv[2], argv[3]);fflush(stderr) ;dst = makePath(argv[3], NULL);DeleteOneFile(dst);free (dst);}break;/*** Remove named files** argv[2] - Text** argv[3]+ - target path*/case 'r':{int ii;if ( argc < 4 )ErrorExit("Incorrect argument count for mode",L"");/*** Display user text*/if ( argv[2][0] ){fprintf(stderr, "%s\n", argv[2]);fflush(stderr) ;}for ( ii = 3; ii < argc ; ii++ ){_TCHAR * dst = makePath(argv[ii], NULL);DeleteOneFile(dst);free (dst);}}break;/*** Delete files in directory - with wildcards** argv[2] - Text** argv[3] - Base directory** argv[4]+ - Files in dir to delete.*/case 'D':DeleteDir(argc - 2, argv + 2 );break;/*** Delete files recursively** argv[2] - Text** argv[3]+ Base directory*/case 'T':DeleteDirTree(argc - 2, argv + 2 );break;/*** Delete Empty Directories** argv[2] - Text** argv[3]+ Base directory*/case 'R':RmDir(argc - 2, argv + 2 );break;default :ErrorExit("Unknown mode: ",argv[1]);break;}return 0;}/*----------------------------------------------------------------------------** FUNCTION : createPaths**** DESCRIPTION : Create the path to the target****** INPUTS : path** Assumed to be a \\?\X:\ style**** RETURNS : Will not return in error**----------------------------------------------------------------------------*/void createPaths ( _TCHAR *path ){DWORD rv;_TCHAR *ptr = path + 7;while ( *ptr ){if ( *ptr == '/' || *ptr == '\\'){*ptr = 0;//fprintf(stderr, "createPaths: %ls\n", path);if ( ! CreateDirectoryW ( path, NULL )){rv = GetLastError();if ( rv != ERROR_ALREADY_EXISTS )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**** RETURNS :**----------------------------------------------------------------------------*/void copyOneFile( int argc, _TCHAR* argv[] ){DWORD rv;_TCHAR * src;_TCHAR * dst;if ( argc != 6 )ErrorExit("Incorrect argument count for file copy",L"");/*** Display user text*/fprintf(stderr, "---- %ls %ls\n", argv[2], argv[3]);fflush(stderr) ;dst = makePath(argv[3], NULL);src = makePath(argv[4], NULL);/*** Check that the source is a file*/if ( verbose )fprintf(stderr, "Validate Source File: %ls\n", src);rv = GetFileAttributesW( src );if ( rv == INVALID_FILE_ATTIBUTES ){/* Need to be a better message */fprintf(stderr, "Source: %ls\n", src);ErrorExit("Source File not found: ", argv[4]);}/*** Remove the ReadOnly attribute on the dest file*/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 );/*** Copy the file*/if ( ! CopyFile( src, dst, FALSE ) ){rv = GetLastError();fprintf(stderr, "CopyFile Last Error: %ld\n", rv);ErrorExit("Copy Error: ", argv[4]);}/*** Test for files existence*/if ( verbose )fprintf(stderr, "Test target was copied: %ls\n", dst);rv = GetFileAttributesW( dst );if ( rv == INVALID_FILE_ATTIBUTES ){/* Need to be a better message */ErrorExit("File not found after copy: ", argv[3]);}/*** Set the files attributes** Assume read-only*/if ( _tcsstr( argv[5], L"-w" ) ){if ( verbose )fprintf(stderr, "Set target read-only: %ls\n", dst);rv |= FILE_ATTRIBUTE_READONLY;rv &= ~FILE_ATTRIBUTE_NORMAL;rv = SetFileAttributesW( dst, rv );if ( rv == 0 ){ErrorExit("Setting ReadOnly: ", argv[3]);}}}/*----------------------------------------------------------------------------** 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** [0]: Text to display** [1]: Base directory** [2]+ Files in dir to delete**** RETURNS : Will not return on error**----------------------------------------------------------------------------*/void DeleteDir( int argc, _TCHAR* argv[] ){DWORD rv;_TCHAR* baseDir;WIN32_FIND_DATA FindFileData;HANDLE hFind;_TCHAR *target;_TCHAR *dirPath;if ( argc < 3 )ErrorExit("Insufficient arguments for DeleteDir",L"");/*** Display the user message** Suppress display if the message is empty*/if ( argv[0][0] ){fprintf(stderr, "%ls\n", argv[0]);fflush(stderr) ;}/*** Extract the base directory from the argument list** This must be a directory*/baseDir = argv[1];argc -= 2;argv+=2;/*** Ensure that the base directory exists*/dirPath = makePath(baseDir, NULL);rv = GetFileAttributesW( dirPath );if ( rv == INVALID_FILE_ATTIBUTES ){/*** Directory does not exists** Assume its already deleted*/if ( verbose > 1 )fprintf(stderr, "Base dir does not exist: %ls\n", baseDir);free(dirPath);return;}if ( !(rv & FILE_ATTRIBUTE_DIRECTORY) ){/*** Target is not a directory** Don't do anything*/if ( verbose > 1 )fprintf(stderr, "Base dir is not a directory: %ls\n", baseDir);free(dirPath);return;}/*** Process all the suffixes** They may contain a wild card*/for ( ; argc ; argc--, argv++ ){_TCHAR * thisDir = makePath( baseDir, *argv);hFind = FindFirstFile(thisDir, &FindFileData);free(thisDir);if (hFind == INVALID_HANDLE_VALUE){/*** No match*/if ( verbose > 1 )fprintf(stderr, "No Match: %ls, %ls\n", baseDir, *argv);continue;}/*** Iterate over all the files*/do {if ( _tcscmp( TEXT("."), FindFileData.cFileName ) == 0 )continue;if ( _tcscmp( TEXT(".."), FindFileData.cFileName ) == 0 )continue;if ( verbose )fprintf(stderr, "Match: %ls\n", FindFileData.cFileName);/*** Matching file found*/target = makePath( baseDir, FindFileData.cFileName);DeleteOneFile(target);free (target);} while (FindNextFile(hFind, &FindFileData) != 0);FindClose(hFind);}/*** Finally delete the directory** Unless its '.'*/if ( _tcscmp( TEXT("."),baseDir) != 0 ){if ( verbose > 1 )fprintf(stderr, "Delete Directory: %ls\n", baseDir);if ( ! RemoveDirectory( dirPath ) ){if ( verbose )fprintf(stderr, "Directory not deleted: %ls\n", baseDir);}}free(dirPath);}/*----------------------------------------------------------------------------** FUNCTION : DeleteDirTree**** DESCRIPTION : Delete an entire directory tree(s)**** INPUTS : argc - count of args** argv - list of files to delete** [0]: Text to display** [1]+ Base directory**** RETURNS : Will not return on error**----------------------------------------------------------------------------*/void DeleteDirTree( int argc, _TCHAR* argv[] ){int ii;if ( argc < 2 )ErrorExit("Insufficient arguments for DeleteDirTree",L"");/*** Display the user message** Suppress display if the message is empty*/if ( argv[0][0] ){fprintf(stderr, "%ls\n", argv[0]);fflush(stderr) ;}for ( ii = 1; ii < argc ; ii++){DeleteOneDirectoryTree( argv[ii] );}}/*----------------------------------------------------------------------------** FUNCTION : DeleteOneDirectoryTree**** DESCRIPTION : Delete an entire directory tree(s)**** INPUTS : path - Dir to delete**** RETURNS : Will not return on error**----------------------------------------------------------------------------*/void DeleteOneDirectoryTree( _TCHAR* baseDir ){DWORD rv;WIN32_FIND_DATA FindFileData;HANDLE hFind;_TCHAR *target;_TCHAR *dirPath;_TCHAR *thisDir;/*** A bit of a sanity test*/if ( _tcscmp( L".", baseDir) == 0 || _tcscmp( L"..", baseDir) == 0 ){fprintf(stderr, "DeleteOneDirectoryTree will not delete '.' or '..'\n");return;}/*** Ensure that the base directory exists*/dirPath = makePath(baseDir, NULL);rv = GetFileAttributesW( dirPath );if ( rv == INVALID_FILE_ATTIBUTES ){/*** Directory does not exists** Assume its already deleted*/if ( verbose > 1 )fprintf(stderr, "Base dir does not exist: %ls\n", baseDir);free(dirPath);return;}if ( !(rv & FILE_ATTRIBUTE_DIRECTORY) ){/*** Target is not a directory** Don't do anything*/if ( verbose > 1 )fprintf(stderr, "Base dir is not a directory: %ls\n", baseDir);free(dirPath);return;}/*** Read next directory entry*/thisDir = makePath( baseDir, TEXT("*"));hFind = FindFirstFile(thisDir, &FindFileData);free(thisDir);if (hFind != INVALID_HANDLE_VALUE){/*** Iterate over all the files*/do {if ( _tcscmp( TEXT("."), FindFileData.cFileName ) == 0 )continue;if ( _tcscmp( TEXT(".."), FindFileData.cFileName ) == 0 )continue;if ( verbose > 2 )fprintf(stderr, "Directory Entry:%ls,%ls\n", baseDir, FindFileData.cFileName );/*** Create a full path to the file*/target = makePath( baseDir, FindFileData.cFileName);if ( FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){DeleteOneDirectoryTree( target );}else{DeleteOneFile (target);}free (target);} while (FindNextFile(hFind, &FindFileData) != 0);FindClose(hFind);}/*** Finally delete the directory*/if ( verbose > 1 )fprintf(stderr, "Delete Directory: %ls\n", baseDir);if ( ! RemoveDirectory( dirPath ) ){if ( verbose )fprintf(stderr, "Directory not deleted: %ls\n", baseDir);}free(dirPath);}/*----------------------------------------------------------------------------** FUNCTION : DeleteOneFile**** DESCRIPTION : Delete a file** Force it writable before deletion**** INPUTS : path - path to the file**** RETURNS : 0 - OK (file deleted or not present)**----------------------------------------------------------------------------*/int DeleteOneFile (_TCHAR * dst ){DWORD rv;int result = 0;if ( verbose > 1)fprintf(stderr, "Delete File: %ls\n", dst);rv = GetFileAttributesW( dst );if ( rv != INVALID_FILE_ATTIBUTES ){if ( verbose )fprintf(stderr, "Delete file: %ls : Attr: 0x%lx\n", dst, rv);if ( rv & FILE_ATTRIBUTE_READONLY ){rv &= ~FILE_ATTRIBUTE_READONLY;rv = SetFileAttributesW( dst, rv );if ( rv == 0 ){fprintf(stderr, "Warning: Attempt to allow write access: %ls\n", dst);}}if (! DeleteFile( dst ) ){fprintf(stderr, "Warning: Did not remove file: %ls\n", dst);result = 1;}}return result;}/*----------------------------------------------------------------------------** FUNCTION : RmDir**** DESCRIPTION : Remove Empty directories**** INPUTS : argc - count of args** argv - list of files to delete** [0]: Text to display** [1]+ Base directory**** RETURNS : Will not return on error**----------------------------------------------------------------------------*/void RmDir( int argc, _TCHAR* argv[] ){int ii;_TCHAR *target;if ( argc < 2 )ErrorExit("Insufficient arguments for RmDir",L"");/*** Display the user message** Suppress display if the message is empty*/if ( argv[0][0] ){fprintf(stderr, "%ls\n", argv[0]);fflush(stderr) ;}for ( ii = 1; ii < argc ; ii++){target = makePath( argv[ii],NULL);if ( verbose > 2)fprintf(stderr, "RmDir: %ls\n", target);if ( ! RemoveDirectory (target) ){if ( verbose )fprintf(stderr, "Directory not deleted: %ls\n", argv[ii]);}free(target);}}/*----------------------------------------------------------------------------** FUNCTION : makePath**** DESCRIPTION : Creates a full file path for user** Join 2 components together** Convert relative path to ABS path with \\?\ from** Convert / into \ for windows**** Handle path elements** ./ - Remove** /bbb/../ - Remove** /aaa/bbb/../../ - Remove****** INPUTS : base - Part 1** May already contain \\?\** file - Part 2 or NULL**** RETURNS : fullPath - User must free memory**----------------------------------------------------------------------------*/_TCHAR * makePath ( _TCHAR * base, _TCHAR *file ){int lenp = 0;int lencwd = 0;int len1 = _tcslen(base);int len2 = 0;_TCHAR *data;_TCHAR *cdata;_TCHAR *udst;/*** If the base contains a \\?\ then we don't need to add it*/if ( !( len1 > 3 && base[2] == '?' ) ){lenp = _tcslen(TEXT("\\\\?\\"));}/*** Unless an absolute path we need to insert CWD too** Just determine the length of the entry for now*/if ( lenp && ( ( base[0] && (base[1] != ':') ) || base[0] == 0 ) ){lencwd = GetCurrentDirectory(0,0) - 1; // determine size needed}/*** 2nd file argument may be a NULL*/if ( file ){len2 = _tcslen(file);}data = (_TCHAR *)malloc((lenp + lencwd+ len1 + len2 + 10) * sizeof(_TCHAR));cdata = data;if ( data == NULL ){ErrorExit ("Malloc error:makePath",L"");}/*** Join all the strings together*/if ( lenp ){_tcscpy( cdata,TEXT("\\\\?\\"));cdata += lenp;}if ( lencwd ){GetCurrentDirectory(lencwd+1, cdata);cdata += lencwd;_tcscpy( cdata, TEXT("\\"));cdata += 1;}_tcscpy( cdata, base);cdata += len1;if ( len2 ){_tcscpy( cdata, TEXT("\\"));_tcscpy( cdata+1, file);cdata += len2 + 1;}/*** Convert / into \*/for ( udst = data; *udst ; udst++){if ( *udst == '/' )*udst = '\\';}/*** Now remove silly relative path bits*/udst = data;while ( *udst ){if ( udst[0] == '\\' && udst[1] == '.' && (udst[2] == '\\' || udst[2] == 0) ){TCHAR* ptr1 = udst;TCHAR* ptr2 = udst + 2;while ( *ptr1++ = *ptr2++ ) {}}else if ( udst[0] == '\\' && udst[1] == '.' && udst[2] == '.' && udst[3] == '\\' ){TCHAR* ptr = udst - 1;TCHAR* ptr2 = udst + 3;while ( *ptr-- != '\\' ){if ( ptr - data < 6){*ptr = 0;fprintf(stderr, "Path: %ls\n", data);ErrorExit("Bad relative path: ", data);}}ptr++;udst = ptr;while ( *ptr++ = *ptr2++ ) {}}else{*udst++;}}if ( verbose > 2)fprintf(stderr, "makePath: %ls\n", data);return data;}/*----------------------------------------------------------------------------** 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, LPTSTR lpszMessage2){fprintf(stderr, "JatsFileUtil:Error:%s%ls\n", lpszMessage,lpszMessage2);fflush(stderr) ;ExitProcess(-1);}