
/*+------------------------------------------------------------------------------+*/
/*                                                                                */
/* PROGRAM NAME: TCshadow.c                                                       */
/* -------------                                                                  */
/*                                                                                */
/*  A TeamConnection Shadow Program                                               */
/*                                                                                */
/*                 IBM TeamConnection                                             */
/*                 Version 3 Release 0                                            */
/*                    5622-717                                                    */
/*                                                                                */
/* COPYRIGHT:                                                                     */
/* ----------                                                                     */
/*        (C) Copyright, IBM Corp., 1998. All Rights Reserved.                    */
/*                  Licensed Materials - Property of IBM                          */
/*                 US Government Users Restricted Rights                          */
/*           - Use, duplication or disclosure restricted by                       */
/*              GSA ADP Schedule Contract with IBM Corp.                          */
/*                                                                                */
/*                  IBM is a registered trademark of                              */
/*             International Business Machines Corporation                        */
/*                                                                                */
/* DISCLAIMER OF WARRANTIES:                                                      */
/* -------------------------                                                      */
/* The following [enclosed] code is sample code created by IBM                    */
/* Corporation.  This sample code is not part of any standard IBM product         */
/* and is provided to you solely for the purpose of assisting you in the          */
/* development of your applications.  The code is provided "AS IS",               */
/* without warranty of any kind.  IBM shall not be liable for any damages         */
/* arising out of your use of the sample code, even if they have been             */
/* advised of the possibility of such damages.                                    */
/*                                                                                */
/*--------------------------------------------------------------------------------*/
/*                                                                                */
/* DESCRIPTION:                                                                   */
/*                                                                                */
/*  This sample program is intended to demonstrate what a basic                   */
/*  TeamConnection shadowing executable could look like.  Note that this          */
/*  program will be called by the TeamConnection server and will run in           */
/*  the family server.                                                            */
/*                                                                                */
/*                                                                                */
/* BUILD INSTRUCTIONS:                                                            */
/*                                                                                */
/*  Directives --                                                                 */
/*                                                                                */
/*    Set the __NT__ var for NT builds.                                           */
/*    Set the __UNIX__ var for AIX, Sun and HP builds.                            */
/*    Set the -Ae compiler option whne compiling on HP in order to use ANSI.      */
/*                                                                                */
/*                                                                                */
/* SYNTAX:                                                                        */
/*                                                                                */
/*  -chmod  -family Name -release Name -shadow Name -path Name -location location */
/*          -fmode Mode -parameters parameters                                    */
/*                                                                                */
/*  -copy   -family Name -release Name -shadow Name -path Name -location location */
/*          -type text|binary -fmode Mode -sourcefile filename                    */
/*          -parameters parameters                                                */
/*                                                                                */
/*  -delete -family Name -release Name -shadow Name -path Name -location location */
/*          -parameters parameters                                                */
/*                                                                                */
/*  -verify -family Name -release Name -shadow Name -path Name -location location */
/*          -timestamp timestamp -parameters parameters                           */
/*                                                                                */
/*  -validate Name -type Name -release Name -shadow Name -location location       */
/*                 -contents [drivers] [workareas] [release]                      */
/*                 -mode synchronous | manual -crlf yes|no -keys yes|no           */
/*                 -timestamp preserve | current -priority Number                 */
/*                 -parameters Parameters                                         */
/*                                                                                */
/*  FLAGS:                                                                        */
/*                                                                                */
/*     -family Name                                                               */
/*         The name of the TC family.                                             */
/*                                                                                */
/*     -release  Name                                                             */
/*         The name of the TeamConnection release where the specified part        */
/*         path exists.                                                           */
/*                                                                                */
/*     -contents [drivers] [workareas] [release]                                  */
/*         Indicates the contents of a shadow.  You may specify one or more.      */
/*         A value of "drivers" indicates that shadowing will occur for           */
/*         drivers in the release.  A value of "release" indicates that           */
/*         shadowing will occur for the current release.  A value of "workareas"  */
/*         indicates that shadowing will occur for workareas in the release.      */
/*                                                                                */
/*     -crlf  yes | no                                                            */
/*         Indicates whether a carriage return/line feed conversion should be     */
/*         performed when files are shadowed.                                     */
/*                                                                                */
/*     -fmode  Mode                                                               */
/*         The access mode to assign to directories and files created in the      */
/*         shadow.                                                                */
/*                                                                                */
/*     -keys  yes | no                                                            */
/*         Indicates whether keywords embedded in parts will be expanded          */
/*         when files are shadowed.                                               */
/*                                                                                */
/*     -location  Name                                                            */
/*         The full path to a file in the shadow.  This path will be created      */
/*         if it does not exist.                                                  */
/*                                                                                */
/*         THIS SAMPLE ASSUMES THAT -location IDENTIFIES THE ACTUAL FILE.         */
/*         (I.E. IT ENDS WITH $R/$N/$P).                                          */
/*                                                                                */
/*     -mode  synchronous | manual                                                */
/*         Indicates the way that the shadow will be updated.  A value of         */
/*         "synchronous" indicates that the shadow will be updated                */
/*         real-time.  A value of "manual" indicates that the shadow will be      */
/*         updated only when someone runs the shadow -synchronize command.        */
/*                                                                                */
/*     -parameters  Parameters                                                    */
/*         The value of parameters will contain a dynamic list of options         */
/*         specific to shadow instance as recorded in the ExtraParameters         */
/*         field in the Shadows table in the database.  Since the parameters      */
/*         value is dynamic, the -parameters flag will always be listed           */
/*         last among input parameters.  This example does not demonstrate        */
/*         the use of this value, but if you choose to define ExtraParameters     */
/*         for your shadow instance, you will have to customize your              */
/*         TeamConnection shadow executable to utilize it.                        */
/*                                                                                */
/*     -path  Name                                                                */
/*         The relative TeamConnection path to a part.                            */
/*                                                                                */
/*     -priority  Number                                                          */
/*         Indicates when files will be shadowed to this shadow instance.         */
/*         A priority of "1" is the lowest.  This attribute is valid only for     */
/*         shadows whose mode is defined as synchronous.                          */
/*                                                                                */
/*     -shadow Name                                                               */
/*         The name of the shadow.                                                */
/*                                                                                */
/*     -sourcefile  Filename                                                      */
/*         The full path to a the source of the file that must be shadowed.       */
/*                                                                                */
/*     -timestamp  Timestamp                                                      */
/*         Used to verify if the timestamp specified matches the                  */
/*         timestamp of the shadowed file (from -location).                       */
/*                                                                                */
/*     -timestamp  preserve | current                                             */
/*         Indicates the attribute used when storing files to the shadow.         */
/*         A value of "preserve" will cause shadowed files to maintain the same   */
/*         timestamp as the file in the database.  A value of "current" will      */
/*         cause the timestamp on the file to change to the time at which the     */
/*         file was shadowed.                                                     */
/*                                                                                */
/*     -type  text | binary                                                       */
/*         Indicate whether the file being shadowed is of type TEXT or BINARY.    */
/*                                                                                */
/*     -type  Name                                                                */
/*         Lists the name of the shadow type.                                     */
/*                                                                                */
/*                                                                                */
/*                                                                                */
/*  Notes:                                                                        */
/*       1. All values are completely resolved; no wildcards are included.        */
/*       2. There are no defaults.  All values will be specified.                 */
/*       3. There is no abbreviation of input flags.                              */
/*       4. The -release flag is provided but not used in this example.           */
/*       5. The -path value represents the TeamConnection part path.              */
/*          This value is used for messages only in this sample.                  */
/*       6. The copy is done from the sourcefile to the location.                 */
/*       7. The delete is done on the location value.                             */
/*       8. The validate action is run when a shadow is created or modified.      */
/*       9. It is recommended that your validateShadow routine validate the       */
/*          location and parameters values at a minimum.  The $x style macros     */
/*          will be validated when the shadow is created or modified.             */
/*              It is also recommended that you enforce the use of the            */
/*              $N (TC object), $R (release), and $P (path) macro variables.      */
/*      10. The validate routine should return zero if successful.                */
/*      11. The chmod action is only useful on AIX systems.                       */
/*          This sample places restrictions on the mode changes that are valid.   */
/*                                                                                */
/*                                                                                */
/* RETURNS:                                                                       */
/*                                                                                */
/*  0  - Successful                                                               */
/*  !0 - Unsuccessful (See E_* vars below for more info)                          */
/*--------------------------------------------------------------------------------*/

/* *****************************
 * Declare System Includes
 * ***************************** */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>


#define ISDIRECTORY(x) (x & S_IFDIR)

#if defined(__UNIX__)
#include <utime.h>
#define WRITABLE(x) ((x) | S_IWUSR | S_IRUSR)
#define READ_MODE S_IRUSR
#else
#include <direct.h>
#include <io.h>
#include <sys/utime.h>
#define WRITABLE(x) S_IWRITE
#define READ_MODE S_IREAD
#endif

#include <errno.h>


/* *****************************
 * Set program constants.
 * To include error constants.
* ***************************** */

#define MAX_FLAG_LEN            19

#define BUFFER_SIZE             4096

#define E_STAT      1
#define E_OPEN      2
#define E_MKDIR     3
#define E_COPIES    4
#define E_CLOSE     5
#define E_FMODE     6
#define E_TIMESTAMP 7
#define E_RENAME    8
#define E_REMOVE    9
#define E_VERIFY    10
#define E_VALIDATE  11


const char * PARM_NAMES[] = {
   "-family",
   "-release",
   "-shadow",
   "-path",
   "-location",
   "-type",
   "-fmode",
   "-sourcefile",
   "-parameters",
   "-timestamp",
   "-crlf",
   "-keys",
   "-mode",
   "-contents",
   "-priority"
};


enum PARM_INDEX {FAMILY=0, RELEASE, SHADOW, PATH, LOCATION, TYPE,
                 FMODE, SOURCEFILE, PARAMETERS, TIMESTAMP, CRLF, KEYS, MODE, CONTENTS,
                 PRIORITY, PARM_INDEX_COUNT};

const char * ME;
char * COMMAND_ACTION;
char * PARM_VALUES[PARM_INDEX_COUNT];


#define CHMOD_FLAG      "-chmod"
#define COPY_FLAG       "-copy"
#define DELETE_FLAG     "-delete"
#define VERIFY_FLAG     "-verify"
#define VALIDATE_FLAG   "-validate"

#define FAMILY_FLAG     "-family"
#define RELEASE_FLAG    "-release"
#define SHADOW_FLAG     "-shadow"
#define PATH_FLAG       "-path"
#define LOCATION_FLAG   "-location"
#define TYPE_FLAG       "-type"
#define FMODE_FLAG      "-fmode"
#define SOURCEFILE_FLAG "-sourcefile"
#define PARMS_FLAG      "-parameters"
#define TIMESTAMP_FLAG  "-timestamp"
#define CRLF_FLAG       "-crlf"
#define KEYS_FLAG       "-keys"
#define MODE_FLAG       "-mode"
#define CONTENTS_FLAG   "-contents"
#define PRIORITY_FLAG   "-priority"


#define SHD_TRIGGER "$"
/* NOTE!!! The MAC_NAMES array and MAC_INDEX enum must be kept in sync
           and also added to switch statement.
*/
const char * MAC_NAMES[] = {"B",
                            "C",
                            "F",
                            "N",
                            "P",
                            "R",
                            "S",
                            "V",
                            "$"};

enum MAC_INDEX { SHD_MAC_BASENAME = 0,
                 SHD_MAC_COMPONENT,
                 SHD_MAC_FAMILY,
                 SHD_MAC_OBJNAME,
                 SHD_MAC_PATH,
                 SHD_MAC_RELEASE,
                 SHD_MAC_SHADOW,
                 SHD_MAC_VERSION,
                 SHD_MAC_TRIGGER,
                 SHD_MAC_COUNT };
/* NOTE!!! The MAC_NAMES array and MAC_INDEX enum must be kept in sync */

#define cZero '\0'

#ifdef __UNIX__
#define SLASH   '/'
#else
#define SLASH   '\\'
#endif

int checkMacros(const char *checkFlag, const char *source, int required);
int mkdir_p(char * path);

void parseInput( const int argc, char ** argv);
int processDeleteAction(void);
int processChmodAction(void);
int processCopyAction(void);
int verifyFile(void);
int validateShadow(void);
int processAction(void);



/* *** Routine: checkMacros                         *** */
/* ***                                              *** */
/* *** Validate the macro variables (from the       *** */
/* ***   location and parameters values)            *** */
/* *** Returns:                                     *** */
/* ***    0  - Successful                           *** */
/* ***    1  - Failed.                              *** */
int checkMacros(const char *checkFlag, const char *source, int required)
{
        char tmp[BUFFER_SIZE];
        char *pSrc, *pTrg;
        char ch;
        int i;
        int result = 0;
        int relfound, objfound, pathfound;
        relfound = objfound = pathfound = 0;

   if (source == NULL)
        {
                if (required)
                {
                        printf("[%s] Error:  You must define the %s attribute.\n",ME,checkFlag);
                        result = 1;
                }
                return result;
        }

        strcpy(tmp,source);
        pSrc = tmp;

        for (;;) {  /* do forever */

                pTrg = strstr(pSrc, SHD_TRIGGER);  /* find the trigger */
                if (pTrg == NULL) break;

                pSrc = pTrg;
                pSrc++;

                for (i=0; i<SHD_MAC_COUNT; i++) {

                        if (!strncmp(pSrc, MAC_NAMES[i], pSrc-pTrg))
                                break;
                }

                /* $ followed by unknown character.  Bummer. */
                if ( i == SHD_MAC_COUNT ) {
                        result = 1;
                        break;
                } else {
                        switch(i) {
                        case SHD_MAC_BASENAME :
                                break;
                        case SHD_MAC_COMPONENT :
                                break;
                        case SHD_MAC_FAMILY :
                                break;
                        case SHD_MAC_OBJNAME :
                                objfound = 1;
                                break;
                        case SHD_MAC_PATH :
                                pathfound = 1;
                                break;
                        case SHD_MAC_RELEASE :
                                relfound = 1;
                                break;
                        case SHD_MAC_SHADOW :
                                break;
                        case SHD_MAC_VERSION :
                                break;
                        case SHD_MAC_TRIGGER :
                                break;
                        default :
                                /* Can't happen. */
                                break;
                        }
                        pSrc++;

                }
        }


        /* ** NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE ** */
        /* These are recommended values to validate for -location       */
        if ( strcmp(checkFlag,PARM_NAMES[LOCATION]) == 0)
        {
                if (!objfound) {
                        printf("[%s] Error:  The object macro [%s%s] is required for the %s definition.\n",ME,SHD_TRIGGER,MAC_NAMES[SHD_MAC_OBJNAME],checkFlag);
                        result = 1;
                }

                if (!relfound) {
                        printf("[%s] Error:  The release macro [%s%s] is required for the %s definition.\n",ME,SHD_TRIGGER,MAC_NAMES[SHD_MAC_RELEASE],checkFlag);
                        result = 1;
                }

                if (!pathfound) {
                        printf("[%s] Error:  The path macro [%s%s] is required for the %s definition.\n",ME,SHD_TRIGGER,MAC_NAMES[SHD_MAC_PATH],checkFlag);
                        result = 1;
                }

        }

   return result;
}

/* ** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ** */
/* ** Change '/' or '\' to the appropriate slash for this OS.  ** */
/* ** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ** */
#define UNIX_DIR_SEP_CHR '/'
#define DOS_DIR_SEP_CHR '\\'
int fixupPath(char* oldPath)
{

   char *temp;

   if (UNIX_DIR_SEP_CHR == SLASH)
      {
      /* look for DOS style seperators and convert them to UNIX style */
      while(temp = strchr(oldPath,DOS_DIR_SEP_CHR))
         {
         *temp = UNIX_DIR_SEP_CHR;
         }
      }
   else
      {
      /* look for UNIX style seperators and convert them to DOS style */
      while(temp = strchr(oldPath,UNIX_DIR_SEP_CHR))
         {
         *temp = DOS_DIR_SEP_CHR;
      }
   }
   return( 0 );
}

/* ** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ** */
/* ** Create the entire path specified if it does not exist.   ** */
/* ** The last node is assumed to be the basename.             ** */
/* ** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ** */

int mkdir_p(char * path) {

        static struct stat stat_info;
        char * p;
        int rc;

        p = strrchr(path, SLASH);
        // might be just a basename OR might be just a '/'
        if (p != NULL && p != path) {
                *p = '\0';
                if (0 == stat(path, &stat_info))   /* path exists, so just return  */
                {
                        *p = SLASH;
                        return 0;
                } else {                            /* path doesn't exist, so mkdir */
                        rc = mkdir_p(path);
                        if (rc != 0) return rc;
#ifdef __UNIX__
                        rc = mkdir(path,0750);
#else
                        rc = mkdir(path); /* Uses direct.h */
#endif
                        if (rc != 0 && errno != EEXIST) {
                                printf("[%s] Error:  Unable to create the directory [%s].\n", ME, path);
                                return E_MKDIR;
                        }
                        *p = SLASH;
                }
        }

        return 0;
}




/* ** +++++++++++++++++++++++++++++++++++++++++++++++++++ ** */
/* ** Iterate through the input parameters and record     ** */
/* ** each corresponding value.                           ** */
/* **                                                     ** */
/* ** NOTE:  This program will only be called by TC,      ** */
/* **        therefore the parameters are not validated   ** */
/* **        across shadow actions; they are assumed to   ** */
/* **        be accurate.  However, a message will be     ** */
/* **        returned for invalid flags, but the command  ** */
/* **        will not fail.  Also, if a flag that is      ** */
/* **        valid for one action is passed for another   ** */
/* **        action, that value will be ignored/not used. ** */
/* ** +++++++++++++++++++++++++++++++++++++++++++++++++++ ** */
void parseInput( const int argc, char ** argv) {

        int i,j;

        for (j=0; j<PARM_INDEX_COUNT; j++) {
                PARM_VALUES[j] = cZero;
        }

        COMMAND_ACTION = argv[1];
        ME = argv[0];

        for (i=2; i<argc; i+=2) {

                for (j=0; j<PARM_INDEX_COUNT; j++) {
                        if (strcmp(PARM_NAMES[j], argv[i]) == 0)
                                break;
                }
                if (j == PARM_INDEX_COUNT)
                        printf("[%s] Error:  Unknown parm, %s, found in input.  Ignoring\n",ME,argv[i]);
                else {
                        /* Avoid dump */
                        if ( (i+1) >= argc)
                                printf("[%s] Error:  Value missing for the %s flag.\n",ME,argv[i]);
                        else
                                PARM_VALUES[j] = argv[i+1];
                }
        }

}





/* ** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ** */
/* ** Handle the chmod action                                  ** */
/* ** This will change the file mode of the specified file     ** */
/* ** See the syntax diagram for a list of the other           ** */
/* ** PARM_VALUES that are available to this action.           ** */
/* ** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ** */
int processChmodAction() {

        int result = 0;

#if defined(__UNIX__)

        static struct stat stat_info;

        mode_t good_mode;

        if (0 != stat(PARM_VALUES[LOCATION], &stat_info)) {
                printf("[%s] Error:  Unable to get file information for [%s].\n", ME, PARM_VALUES[LOCATION]);
                return E_STAT;
        }

        if (ISDIRECTORY(stat_info.st_mode)) {
                printf("[%s] Error:  The specified file [%s] is a directory\n", ME, PARM_VALUES[LOCATION]);
                return E_STAT;
        }


        good_mode = (strtol(PARM_VALUES[FMODE],NULL,8) | 0660) & 0775;

        /* Setting group and other mode bits to zero to prevent other messes */
        if (chmod(PARM_VALUES[LOCATION], good_mode) != 0)
        {
                if (errno == ENOENT)
                        printf("[%s] Error:  File %s not found.  The chmod action failed.\n",ME,PARM_VALUES[LOCATION]);
                else
                        printf("[%s] Error:  The chmod action failed.\n",ME);
                result = 1; /* Failure */
        }
#endif

        return result;
}




/* ** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ** */
/* ** Handle the delete action.                                ** */
/* ** This will delete the file from location                  ** */
/* ** See the syntax diagram for a list of the other           ** */
/* ** PARM_VALUES that are available to this action.           ** */
/* ** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ** */
int processDeleteAction() {

        static struct stat stat_info;

        if (stat(PARM_VALUES[LOCATION], &stat_info) != 0) {
#if defined(__UNIX__) || defined(__NT__)
                if ((errno == ENOENT) || (errno == ENOTDIR)) {
                        /* the file is already gone */
                        return 0;
#else
                if (errno == ENOENT) {
                        /* the file is already gone */
                        return 0;
#endif
                } else {
                        printf("[%s] Error:  Unable to access file [%s]\n", ME, PARM_VALUES[LOCATION]);
                        return E_REMOVE;
                }
        }

        if (ISDIRECTORY(stat_info.st_mode)) {
                printf("[%s] Error:  The specified file [%s] is a directory\n", ME, PARM_VALUES[LOCATION]);
                return E_REMOVE;
        }

        chmod(PARM_VALUES[LOCATION], WRITABLE(stat_info.st_mode));

        if ( remove( PARM_VALUES[LOCATION])) {
                printf("[%s] Error:  Unable to remove the file [%s].\n", ME, PARM_VALUES[LOCATION]);
                return E_REMOVE;
        }

        return 0;
}



/* ** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ** */
/* ** Handle the copy action.                                  ** */
/* ** This will copy the sourcefile to the location.           ** */
/* ** Note that the file will be left in a read-only state.    ** */
/* ** See the syntax diagram for a list of the other           ** */
/* ** PARM_VALUES that are available to this action.           ** */
/* ** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ** */
int processCopyAction() {

        FILE *fp_in;
        FILE *fp_out;
        char line[BUFFER_SIZE];
        int readbytes, linebytes;
        struct stat stat_info;
        struct stat nustat_info;
        struct utimbuf filetimes;

   if (0 != stat(PARM_VALUES[SOURCEFILE], &stat_info))
        {
                printf("[%s] Error:  Unable to get file information for [%s].\n", ME, PARM_VALUES[SOURCEFILE]);
                return E_STAT;
        }

        filetimes.actime = time(NULL);
        filetimes.modtime = stat_info.st_mtime;

        if (0 == stat(PARM_VALUES[LOCATION], &nustat_info)) {
                if (ISDIRECTORY(nustat_info.st_mode)) {
                        printf("[%s] Error:  The specified file [%s] is a directory\n", ME, PARM_VALUES[LOCATION]);
                        return E_COPIES;
                } else {
                        chmod(PARM_VALUES[LOCATION], WRITABLE(nustat_info.st_mode));
                }
        }

        /* ** Make sure that the destination dir exists ** */
        if ( mkdir_p(PARM_VALUES[LOCATION]))
                return E_MKDIR;

        if ( (fp_in = fopen( PARM_VALUES[SOURCEFILE], "rb")) == NULL) {
                printf("[%s] Error:  The input file [%s] was not found.\n", ME, PARM_VALUES[SOURCEFILE]);
                return E_OPEN;
        }

        if ( (fp_out = fopen( PARM_VALUES[LOCATION], "wb")) == NULL) {
                printf("[%s] Error:  Unable to open output file [%s].\n", ME, PARM_VALUES[LOCATION]);
                return E_OPEN;
        }

        readbytes = BUFFER_SIZE;
        for(;;) {

                linebytes = fread(line, 1, readbytes, fp_in);
                if (linebytes < 0) {
                        printf("[%s] Error:  Error reading from source file.\n", ME);
                        return E_COPIES;
                }

                if (0 == linebytes) break;

                linebytes = fwrite(line, 1, linebytes, fp_out);
                if (linebytes < 0) {
                        printf("[%s] Error:  Error writing to output file.\n", ME);
                        return E_COPIES;
                }
        }

        fclose(fp_in);
        fclose(fp_out);

        /* Set the timestamp and set to readonly */

        if (utime(PARM_VALUES[LOCATION], &filetimes)) {
                return 1;
        }


#if defined(__UNIX__)

        if (processChmodAction()) {
                return 1;
        }

#else

        if (chmod(PARM_VALUES[LOCATION], READ_MODE)) {
                return 1;
        }

#endif


        return 0;
}




/* ** +++++++++++++++++++++++++++++++++++++++++++++++++ ** */
/* ** See if a specified timestamp matches the mtime    ** */
/* ** value of a path.                                  ** */
/* **                                                   ** */
/* **   Possible Return Codes:                          ** */
/* **     0 - File has correct timestamp                ** */
/* **     1 - File does not have the correct timestamp  ** */
/* **         or it does not exist.  Indicates to TC    ** */
/* **         that this file should be re-synchronized. ** */
/* **     2 - Other system problem.  This will cause    ** */
/* **         the verify action to stop processing.     ** */
/* **         For example, maybe a file server is down. ** */
/* ** See the syntax diagram for a list of the other    ** */
/* ** PARM_VALUES that are available to this action.    ** */
/* ** +++++++++++++++++++++++++++++++++++++++++++++++++ ** */
int verifyFile() {

        struct stat stat_info;
        struct tm       *lt;
        char  timeAsString[20];


        if (0 != stat(PARM_VALUES[LOCATION], &stat_info))
        {
        /* Assume simple case of file not found.  May need to process differently
                for other kinds of errors */
                printf("[%s] Error:  File [%s] does not exist.\n", ME, PARM_VALUES[LOCATION]);
                return 1;
        }


        lt = localtime(&stat_info.st_mtime);

        (void) sprintf(timeAsString, "%04d/%02d/%02d %02d:%02d:%02d",
                lt->tm_year+1900, (lt->tm_mon)+1, lt->tm_mday,
                lt->tm_hour, lt->tm_min, lt->tm_sec);

        if ( strcmp(PARM_VALUES[TIMESTAMP], timeAsString) != 0)
                return 1;

        return 0;

}


/* ** ++++++++++++++++++++++++++++++++++++++++++++++++++** */
/* ** Validate the attributes of a shadow on create and ** */
/* ** modify.                                           ** */
/* ** For this program, we will enforce that the value  ** */
/* ** of the location parameter is a path that includes ** */
/* ** the release macro ($R) the object macro ($N) and  ** */
/* ** the path macro ($P).                              ** */
/* ** See the syntax diagram for a list of the other    ** */
/* ** PARM_VALUES that are available to this action.    ** */
/* ** ++++++++++++++++++++++++++++++++++++++++++++++++++** */
int validateShadow() {

        return checkMacros(PARM_NAMES[LOCATION],PARM_VALUES[LOCATION],1);
}



/* ** +++++++++++++++++++++++++++++++++++++++++++++++++++ ** */
/* ** Perform the appropriate action based in input.      ** */
/* ** +++++++++++++++++++++++++++++++++++++++++++++++++++ ** */
int processAction() {

        int result = 0;

        if ( strcmp(COMMAND_ACTION,COPY_FLAG) == 0) {
                return processCopyAction();
        } else if ( strcmp(COMMAND_ACTION,CHMOD_FLAG) == 0) {
                return processChmodAction();
        } else if ( strcmp(COMMAND_ACTION,DELETE_FLAG) == 0) {
                return processDeleteAction();
        } else if ( strcmp(COMMAND_ACTION,VERIFY_FLAG) == 0) {
                return verifyFile();
        } else if ( strcmp(COMMAND_ACTION, VALIDATE_FLAG) == 0 ) {
                return ( validateShadow());
        }

        return result;

} /* END processAction */



int main (int argc, char *argv[]) {

        int result;

        /* =================================================== */
        /* =================================================== */
        /* ** Parse the input stream and assign variables.  ** */
        /* =================================================== */
        /* =================================================== */

        parseInput(argc,argv);

        if (PARM_VALUES[LOCATION])
                fixupPath(PARM_VALUES[LOCATION]);

        /* ==================================================== */
        /* ==================================================== */
        /* ** Perform the appropriate action based on input. ** */
        /* ==================================================== */
        /* ==================================================== */

        /* DEBUG printf("[%s][%s]\n", COMMAND_ACTION,   PARM_VALUES[LOCATION]); */

        result = 0;

        result = processAction();

        return result;
}
