Subversion Repositories DevTools

Rev

Rev 2313 | Rev 2429 | 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) Remove Files and 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 '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 );
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 );

/*
**  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 chmod\n"
         "      d9 unCopyFile   dstPath\n"
         "      r9 RmFile       file+\n"
         "      D9 DeleteFiles  dstDir file+ - supports (?*)\n"
         "      T9 DeleteTree   dstDir+\n"
         "      R9 RmItem       (dir|file)+\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';

        /*
        **  If Verbose, then display arguments
        */
        if ( verbose > 2 )
        {
            for ( ii = 0; ii < argc ; ii++ )
            {
                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;
            
            
        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
**
** RETURNS            :
**
----------------------------------------------------------------------------*/

void copyOneFile( int argc, char* argv[] )
{
        int    rv;
    char * src;
    char * dst;
    struct stat fstat;
    
    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 );

    /*
    **   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
    **      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 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  1024
int 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           : 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           : 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);
}