/* touch.c v1.22 - 2014-03-24
Update:
  http://myc01.free.fr/touch

License:
  Public Domain Dedication (CC0 1.0)
  http://creativecommons.org/publicdomain/zero/1.0/

Usage:
touch.exe [-hxamcDsvq] [-r ref_file|-t time|-d date_time] -- <file_1> [file_N]

  -h : Show this help
  -x : Modify creation time
  -a : Modify access time
  -m : Modify modification time
  -c : Do not create file if not found
  -r : Use a file or directory as a reference time source
  -D : If target doesn't exist, create a directory
  -s : Browse sub-directories
  -v : Output the result of every file processed
  -q : Silent mode, no error is displayed (disable '-v')
  -t : Use time [[CC]YY]MMDDhhmm[.SS] instead of today
  -d : Use date_time [[YYYY-]MM-DD][Thh:mm[:ss]] instead of today
  -- : Finish options, followed by the file list
The options '-r', '-d' and '-t' can not be used simultaneously.
By default, all times will be modified (creation, access and modification).

Examples:
  touch.exe -sv -d 2011-04-23          -- ".\folder\*.txt"
  touch.exe -cv -d           T10:55:00 -- *.dat
  touch.exe -xv -d 2011-04-23T10:55    -- test2.txt test3.txt

********************************************************************************
Changelog:
1.22 - 2014-03-24
  * fix '-c', '-D' switch, '-c' is always on and '-D' is ignored in 1.21
  * make '--' optional

1.21 - 2011-04-28
  * fix decode date/time
  * add touch folder with *.*
  * Combination of the options '-s' '-v' : '-sv'

1.2 - 2011-04-26
  * argument from 'GetCommandLine' / 'CommandLineToArgv'
  * change option '-d' to '-D'
  * add option '-d date_time'

1.1 - 2011-04-24
  * add options '-v' verbose, '-s' scan-subfolder, '-q' silent mode

1.0 - 2011-04-20
  * initial version with options '-hxamcrdt -- file_n ...'
*/


#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#ifndef INVALID_FILE_ATTRIBUTES
#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
#endif

#define PRG_NAME  "Touch"
#define PRG_VERS  "1.22"
#define PRG_DATE  "2014-03-24"
#define PRG_LIC   "Public Domain Dedication (CC0 1.0)"

/* Options in command line */
#define OPT_MODIFY_TIME_C     1  /* -x */
#define OPT_MODIFY_TIME_A     2  /* -a */
#define OPT_MODIFY_TIME_M     4  /* -m */
#define OPT_NO_CREATE         8  /* -c */
#define OPT_USE_REF_FILE     16  /* -r */
#define OPT_USER_TIME        32  /* -t */
#define OPT_USER_DATE        64  /* -d */
#define OPT_CREATE_DIR      128  /* -D */
#define OPT_VERBOSE         256  /* -v */
#define OPT_RECURSIVE       512  /* -s */
#define OPT_QUIET          1024  /* -q */

#define CommandLineToArgv  _CommandLineToArgvA
#define LEN_PATH           1024     /* _MAX_PATH = 260 */

LPFILETIME lpTimeC, lpTimeA, lpTimeM;
DWORD      dwOptions;
HANDLE     hStdOut;

char **ARGV;
int    ARGC;


char **_CommandLineToArgvA (const char *CmdLine, int *pnArgc)
{
    char           a;
    char         **argv, *pcArgv;
    unsigned long  len, i, j, argc = 0;
    int            in_QM = FALSE, in_TEXT = FALSE, in_SPACE = TRUE;

    len = strlen (CmdLine);
    i = ( (len + 2) / 2) * sizeof (void*) + sizeof (void*);

    argv   = (char**) calloc (i + (len + 2) * sizeof (char), 1);
    pcArgv = (char*) ( ( (unsigned char*) argv) + i);

    argv[argc] = pcArgv;
    i = j = 0;

    while ( (a = CmdLine[i]) )
    {
        if (in_QM)
        {
            if (a == '\"')
                in_QM = FALSE;
            else
                pcArgv[j++] = a;
        }
        else
        {
            switch (a)
            {
            case '\"':
                in_QM = in_TEXT = TRUE;
                if (in_SPACE)
                    argv[argc++] = pcArgv + j;
                in_SPACE = FALSE;
                break;
            case ' ' :
            case '\t':
            case '\n':
            case '\r':
                if (in_TEXT)
                    pcArgv[j++] = '\0';
                in_TEXT  = FALSE;
                in_SPACE = TRUE;
                break;
            default:
                in_TEXT = TRUE;
                if (in_SPACE)
                    argv[argc++] = pcArgv + j;
                pcArgv[j++] = a;
                in_SPACE = FALSE;
                break;
            }
        }
        i++;
    }
    pcArgv[j]  = '\0';
    argv[argc] = NULL;

    (*pnArgc) = argc;
    return argv;
}  /* _CommandLineToArgvA */


BOOL FolderExists (LPCTSTR lpPath)
{
    DWORD Attr = GetFileAttributes (lpPath);
    if (Attr != INVALID_FILE_ATTRIBUTES)
        if (Attr & FILE_ATTRIBUTE_DIRECTORY)
            return TRUE;

    return FALSE;
}  /* FolderExists */


BOOL Exists (LPCTSTR lpFileName)
{
    if (GetFileAttributes (lpFileName) != INVALID_FILE_ATTRIBUTES)
        return TRUE;
    else if (GetLastError () == ERROR_SHARING_VIOLATION)
        return TRUE;
    return FALSE;
} /* Exists */


int printFileTime (LPFILETIME lpFileTime)
{
    FILETIME   fTime;
    SYSTEMTIME sTime;

    if (!lpFileTime) return 0;

    FileTimeToLocalFileTime (lpFileTime, &fTime);
    FileTimeToSystemTime    (&fTime    , &sTime);
    return printf ("%04hu"      "-%02hu"      "-%02hu"    "T%02hu"     ":%02hu"       ":%02hu",
                   sTime.wYear, sTime.wMonth, sTime.wDay, sTime.wHour, sTime.wMinute, sTime.wSecond);
}  /* printFileTime */


int printfOem (LPCTSTR lpFmt, ...)
{
    PCHAR lpBuf;
    INT nLen;
    if (dwOptions & OPT_QUIET) return 0;
    if (! lpFmt) return 0;
    if (!*lpFmt) return 0;

    lpBuf = calloc (LEN_PATH, 1);
    nLen = _vsnprintf (lpBuf, LEN_PATH, lpFmt, (va_list)(&lpFmt + 1));
    if (nLen < 0)
        nLen = strlen (lpBuf);
    CharToOemBuff (lpBuf, lpBuf, nLen);
    WriteConsole (hStdOut, lpBuf, nLen, (PDWORD)&nLen, NULL);
    free (lpBuf);
    return nLen;
}  /* printfOem */


DWORD Touch (LPCTSTR lpFileName)
{
    DWORD dwFileAttributes, dwResult;
    HANDLE hFile;
    BOOL bCreateDir;
    SetLastError (ERROR_SUCCESS);
    dwFileAttributes = GetFileAttributes (lpFileName);
    dwResult = GetLastError ();
    if ((dwFileAttributes == INVALID_FILE_ATTRIBUTES) && (dwResult != ERROR_FILE_NOT_FOUND))
        return dwResult;

    bCreateDir = ((OPT_CREATE_DIR   & dwOptions)                && (dwFileAttributes == INVALID_FILE_ATTRIBUTES)) ||
                      ((dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (dwFileAttributes != INVALID_FILE_ATTRIBUTES));

    /* If target doesn't exist, create a directory. */
    if (bCreateDir && (dwFileAttributes == INVALID_FILE_ATTRIBUTES))
    {
        SetLastError (ERROR_SUCCESS);
        if (!CreateDirectory (lpFileName, NULL))
        {
            if ((dwResult = GetLastError ()) != ERROR_ALREADY_EXISTS)
                return dwResult;
        }
        else
            return ERROR_SUCCESS;  /* Done, the directory is created. */
    }

    SetLastError (ERROR_SUCCESS);
    hFile = CreateFile (lpFileName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                               ( (dwOptions & OPT_NO_CREATE) || bCreateDir) ? OPEN_EXISTING : OPEN_ALWAYS,
                               FILE_ATTRIBUTE_NORMAL | (bCreateDir ? FILE_FLAG_BACKUP_SEMANTICS : 0), 0);
    dwResult = GetLastError ();

    /* Check for CreateFile() special cases */
    if (hFile == INVALID_HANDLE_VALUE)
    {
        if ( (dwOptions & OPT_NO_CREATE) && dwResult == ERROR_FILE_NOT_FOUND)
            return ERROR_SUCCESS;  /* not an error */
        else if (dwResult == ERROR_ALREADY_EXISTS)
            dwResult = ERROR_SUCCESS;  /* not an error according to MSDN docs */
        return dwResult;
    }

    if (lpTimeC || lpTimeA || lpTimeM)
    {
        if (SetFileTime (hFile, lpTimeC, lpTimeA, lpTimeM))
        {
            if ( (dwOptions & OPT_VERBOSE) && !(dwOptions & OPT_QUIET) )
                printfOem     ("%s\n", lpFileName);  /*
                printf        ("%s\n", lpFileName);  */
            dwResult = ERROR_SUCCESS;
        }
        else
            dwResult = GetLastError ();
    }
    CloseHandle (hFile);

    return dwResult;
}  /* Touch */


BOOL PrintHelp (LPCTSTR lpMessage)
{
    if (lpMessage)
        printf ("%s\n\n", lpMessage);

    puts (PRG_NAME" v"PRG_VERS" ("PRG_DATE") - License: "PRG_LIC);  /*
    puts ("********************************************************************************"); */
    puts ("Usage:\n"
          "touch.exe [-hxamcDsvq] [-r ref_file|-t time|-d date_time] [--] <file_1> [file_N]");
    puts ("  -h : Show this help\n"
          "  -x : Modify creation time\n"
          "  -a : Modify access time\n"
          "  -m : Modify modification time\n"
          "  -c : Do not create file if not found\n"
          "  -r : Use a file or directory as a reference time source\n"
          "  -D : If target doesn't exist, create a directory\n"
          "  -s : Browse sub-directories\n"
          "  -v : Output the result of every file processed\n"
          "  -q : Silent mode, no error is displayed (disable '-v')\n"
          "  -t : Use time [[CC]YY]MMDDhhmm[.SS] instead of today\n"
          "  -d : Use date_time [[YYYY-]MM-DD][Thh:mm[:ss]] instead of today\n"
          "  -- : Finish options, followed by the file list\n"
          "       Without this switch, only last entry will be processed\n"
          "The options '-r', '-d' and '-t' can not be used simultaneously.\n"
          "By default, all times will be modified (creation, access and modification).");
    puts ("Examples:\n"
          "  touch.exe -sv -d 2011-04-23          -- \".\\folder\\*.txt\"\n"
          "  touch.exe -cv -d           T10:55:00 -- *.dat\n"
          "  touch.exe -xv -d 2011-04-23T10:55    -- test2.txt test3.txt");

    return FALSE;
}  /* PrintHelp */


/* Prints a message translated from a Windows Error code, then prints the usage */
PCHAR PrintError (LPCTSTR lpInfo, DWORD dwError)
{
    LPTSTR lpText = NULL;
    if (dwOptions & OPT_QUIET) return NULL;

    FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                   NULL, dwError, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpText, 0, NULL);
    printfOem ("\nError: %s = %s", lpInfo, lpText);
    LocalFree (lpText);

    return NULL;
}  /* PrintError */


BOOL GetArgs (LPDWORD dwList, LPTSTR *lpRefFile, LPTSTR *lpDateTime, LPDWORD dwOptions)
{
    int i, find_double_dash = 0;
    if (ARGC < 2)
        return PrintHelp (NULL);
    else if (ARGC == 2)
    {
        if (ARGV[1][0] == '-' && ARGV[1][0] == 'h')
            return PrintHelp (NULL);
        else if (ARGV[1][0] == '-')
            return PrintHelp ("Error 1: Not enough arguments.");
        *dwList = 1;
    }
    else
    {
        for(i = 1; i < (find_double_dash ? *dwList : (DWORD)ARGC); i++) {
            if(ARGV[i][0]=='-') {
                switch(ARGV[i][1]) {
                    case 'x':
                        *dwOptions |= OPT_MODIFY_TIME_C;
                        break;
                    case 'a':
                        *dwOptions |= OPT_MODIFY_TIME_A;
                        break;
                    case 'm':
                        *dwOptions |= OPT_MODIFY_TIME_M;
                        break;
                    case 'c':
                        *dwOptions |= OPT_NO_CREATE;
                        break;
                    case 'D':
                        *dwOptions |= OPT_CREATE_DIR;
                        break;
                    case 'v':
                        *dwOptions |= OPT_VERBOSE;
                        break;
                    case 's':
                        *dwOptions |= OPT_RECURSIVE;
                        break;
                    case 'q':
                        *dwOptions |= OPT_QUIET;
                        break;
                    case 'h':
                        return PrintHelp (NULL);
                    case '-':
                        find_double_dash = 1;
                        *dwList = i;
                        break;
                    case 'r':
                        if(i+1 < ARGC) {
                            *lpRefFile = ARGV[++i];
                            *dwOptions |= OPT_USE_REF_FILE;
                        }
                        break;
                    case 'd':
                        if(i+1 < ARGC) {
                            *lpDateTime = ARGV[++i];
                            *dwOptions |= OPT_USER_DATE;
                        }
                        break;
                    case 't':
                        if(i+1 < ARGC) {
                            *lpDateTime = ARGV[++i];
                            *dwOptions |= OPT_USER_TIME;
                        }
                        break;
                }
            } else {
                break;
            }
        }

        if(!find_double_dash && i < ARGC) {
            *dwList = i;
        }

        if ((find_double_dash && ++(*dwList) >= (DWORD)ARGC) ||
            (!find_double_dash && (*dwList) >= (DWORD)ARGC))
            return PrintHelp ("Error 2: No file(s) specified!");
    }

    if ( (*dwOptions & OPT_USE_REF_FILE) &&  (*dwOptions & OPT_USER_TIME) )
        return PrintHelp ("Error 3: You may not specify both '-r' and '-t'.");

    if ( (*dwOptions & OPT_USE_REF_FILE) &&  (*dwOptions & OPT_USER_DATE) )
        return PrintHelp ("Error 4: You may not specify both '-r' and '-d'.");

    if ( (*dwOptions & OPT_USER_TIME   ) &&  (*dwOptions & OPT_USER_DATE) )
        return PrintHelp ("Error 5: You may not specify both '-t' and '-d'.");

    if ( (*dwOptions & OPT_USE_REF_FILE) && !(*lpRefFile                ) )
        return PrintHelp ("Error 6: You must specify a 'ref_file' together with '-r'.");

    if ( (*dwOptions & OPT_USER_TIME   ) && !(*lpDateTime               ) )
        return PrintHelp ("Error 7: You must specify a timestamp together with '-t'.");

    /* If -x, -a and -m wasn't specified, then all of them are implied */
    if (! (*dwOptions &  (OPT_MODIFY_TIME_C | OPT_MODIFY_TIME_A | OPT_MODIFY_TIME_M) ) )
           *dwOptions |= (OPT_MODIFY_TIME_C | OPT_MODIFY_TIME_A | OPT_MODIFY_TIME_M);

    return TRUE;
}  /* GetArgs */


/* Gets FILETIME from a file */
BOOL GetFileTimes (LPCTSTR lpFileName, LPFILETIME fTimeC, LPFILETIME fTimeA, LPFILETIME fTimeM)
{
    BOOL bResult = TRUE;
    HANDLE hFile = CreateFile (lpFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
                               NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 0);

    if (INVALID_HANDLE_VALUE == hFile)
        return PrintHelp (PrintError (lpFileName, GetLastError ()));

    if (!GetFileTime (hFile, fTimeC, fTimeA, fTimeM))
        bResult = PrintHelp (PrintError (lpFileName, GetLastError ()));

    CloseHandle (hFile);
    return bResult;
}  /* GetFileTimes */


/* Format: ISO 8601:2004(E)
    http://en.wikipedia.org/wiki/ISO_8601
    http://dotat.at/tmp/ISO_8601-2004_E.pdf
    Extended format: YYYY-MM-DDThh:mm:ss Example: 1985-04-12T10:15:30

    [[[YYYY-]MM-DD][Thh:mm[:ss]]]
    123456789ABCDEF1234
    YYYY-MM-DDThh:mm:ss
    YYYY-MM-DD
    MM-DD
    Thh:mm:ss
    Thh:mm
*/
BOOL DecodeDateTime (LPTSTR lpDateTime, LPSYSTEMTIME st)
{
    char *pTime;
    int nLen = (lpDateTime) ? strlen (lpDateTime) : 0;

    if ((nLen < 5) || (nLen > 19))
        return PrintHelp ("Error 8: Invalid time format.");

    if (nLen && (pTime = strchr (lpDateTime, 'T')))
    {
        st->wSecond = st->wMilliseconds = 0;
        nLen = strlen (pTime);
             if (nLen == 9)  /* Thh:mm:ss */
            sscanf (pTime, "T%02hu:%02hu:%02hu", &st->wHour, &st->wMinute, &st->wSecond);
        else if (nLen == 6)  /* Thh:mm    */
            sscanf (pTime, "T%02hu:%02hu"      , &st->wHour, &st->wMinute              );
        else if (nLen >  0)
            return PrintHelp ("Error 9: Invalid time format.");
        *pTime = '\0';
        nLen = strlen (lpDateTime);
    }

    if (nLen > 0)
    {
             if (nLen == 10)  /* YYYY-MM-DD */
            sscanf (lpDateTime, "%4hu-%02hu-%02hu", &st->wYear, &st->wMonth, &st->wDay);
        else if (nLen ==  5)  /*      MM-DD */
            sscanf (lpDateTime,      "%02hu-%02hu",             &st->wMonth, &st->wDay);
        else
            return PrintHelp ("Error 10: Invalid date format.");
    }
    return TRUE;
}  /* DecodeDateTime */


/* Format:
  [[CC]YY]MMDDhhmm[.SS]
  123456789ABCDEF
  CCYYMMDDhhmm.SS
  CCYYMMDDhhmm
  MMDDhhmm
 */
BOOL DecodeTime (LPTSTR lpDateTime, LPSYSTEMTIME st)
{
    PCHAR pSec;
    INT   nLen = (lpDateTime) ? strlen (lpDateTime) : 0;
    WORD  CC = st->wYear / 100;
    WORD  YY = st->wYear % 100;
    st->wSecond = st->wMilliseconds = 0;

    if ((nLen < 8) || (nLen > 15))
        return PrintHelp ("Error 11: Invalid time format.");

    if ((pSec = strchr (lpDateTime, '.')))
    {
        sscanf (pSec, ".%02hu", &st->wSecond);
        *pSec = '\0';
        nLen = strlen (lpDateTime);
    }

    if (nLen > 0)
    {
             if (nLen == 12)  /* CC  YY   MM   DD  hh  mm */
            sscanf (lpDateTime, "%2hu%02hu%02hu%2hu%2hu%2hu", &CC, &YY, &st->wMonth, &st->wDay, &st->wHour, &st->wMinute);
        else if (nLen == 10)  /*     YY   MM   DD  hh  mm */
            sscanf (lpDateTime,     "%02hu%02hu%2hu%2hu%2hu",      &YY, &st->wMonth, &st->wDay, &st->wHour, &st->wMinute);
        else if (nLen ==  8)  /*          MM   DD  hh  mm */
            sscanf (lpDateTime,          "%02hu%2hu%2hu%2hu",           &st->wMonth, &st->wDay, &st->wHour, &st->wMinute);
        else
            return PrintHelp ("Error 12: Invalid date format.");
        if (nLen == 10)
            CC = (YY < 69) ? 20 : 19;
        st->wYear = CC * 100 + YY;
    }
    return TRUE;
}  /* DecodeTime */


BOOL StrToFileTime (LPTSTR lpDateTime, LPFILETIME ts)
{
    FILETIME ft;
    SYSTEMTIME st;
    INT nLen = (lpDateTime) ? strlen (lpDateTime) : 0;
    GetLocalTime (&st);

    if (nLen)
    {
        if (strchr (lpDateTime, ':') || strchr (lpDateTime, '-'))
        {
            if (!DecodeDateTime (lpDateTime, &st))  /* [[[YYYY-]MM-DD][Thh:mm[:ss]]] */
                return FALSE;
        }
        else
        {
            if (!DecodeTime (lpDateTime, &st))  /* [[CC]YY]MMDDhhmm[.SS] */
                return FALSE;
        }
    }

    if (!SystemTimeToFileTime (&st, &ft))
        return PrintHelp (PrintError ("13 SystemTimeToFileTime()"   , GetLastError ()));

    if (!LocalFileTimeToFileTime (&ft, ts))
        return PrintHelp (PrintError ("14 LocalFileTimeToFileTime()", GetLastError ()));

    return TRUE;
}  /* StrToFileTime */


DWORD FindFileRecursive (LPCTSTR lpDir, LPCTSTR lpFindName)
{
    DWORD           dwResult = ERROR_SUCCESS;
    HANDLE          hFile;
    CHAR            szPath[LEN_PATH];
    WIN32_FIND_DATA fData;

    strcpy (szPath, lpDir);
    strcat (szPath, "\\");
    strcat (szPath, (*lpFindName) ? lpFindName : "*.*");

    if ((hFile = FindFirstFile (szPath, &fData)) == INVALID_HANDLE_VALUE)
        return GetLastError ();

    do
    {
        if (!strcmp (fData.cFileName, ".") || !strcmp (fData.cFileName, ".."))
            continue;

        strcpy (szPath, lpDir);
        strcat (szPath, "\\");
        strcat (szPath, fData.cFileName);

        if (fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
            if (fData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
                continue;

            if ((dwResult = Touch (szPath)) != ERROR_SUCCESS)
                PrintError (szPath, dwResult);

            if (dwOptions & OPT_RECURSIVE)
            {
                if ((dwResult = FindFileRecursive (szPath, lpFindName)) != ERROR_SUCCESS)
                {
                    FindClose (hFile);
                    return dwResult;
                }
            }
        }
        else
            if ((dwResult = Touch (szPath)) != ERROR_SUCCESS)
                PrintError (szPath, dwResult);
    }
    while (FindNextFile (hFile, &fData));

    FindClose (hFile);
    return dwResult;
}  /* FindFileRecursive */


int ExitCode (int status)
{
    free (ARGV);
    exit (status);
}  /* ExitCode */


int main (void)  /* (int argc, char *argv[]) */
{
    DWORD     dwFilesList, dwResult;
    CHAR      szPath [LEN_PATH], szFullPath [LEN_PATH], szFindName[_MAX_FNAME];
    CHAR      szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szFileName[_MAX_FNAME], szExt[_MAX_EXT];
    PCHAR     lpRefFile = NULL, lpDateTime = NULL;
    FILETIME  fTimeC, fTimeA, fTimeM;
    HANDLE    fh;

    ARGV = CommandLineToArgv (GetCommandLine (), &ARGC);
    hStdOut = GetStdHandle (STD_OUTPUT_HANDLE);

    if (!GetArgs (&dwFilesList, &lpRefFile, &lpDateTime, &dwOptions))
        ExitCode (-1);

    if (dwOptions & OPT_USE_REF_FILE)
    {
        if (!GetFileTimes (lpRefFile, &fTimeC, &fTimeA, &fTimeM))
            ExitCode (-1);
    }
    else
    {
        if (!StrToFileTime (lpDateTime, &fTimeM))
            ExitCode (-1);
        fTimeC = fTimeA = fTimeM;
    }

    lpTimeC = (dwOptions & OPT_MODIFY_TIME_C) ? &fTimeC : NULL;
    lpTimeA = (dwOptions & OPT_MODIFY_TIME_A) ? &fTimeA : NULL;
    lpTimeM = (dwOptions & OPT_MODIFY_TIME_M) ? &fTimeM : NULL;

    if ( (dwOptions & OPT_VERBOSE) && !(dwOptions & OPT_QUIET) )
    {
        printf (    "Create       = ");
        printf ("%s\nAccess       = ", printFileTime (lpTimeC) ? "" : "unchanged");
        printf ("%s\nModification = ", printFileTime (lpTimeA) ? "" : "unchanged");
        printf ("%s\n\n"             , printFileTime (lpTimeM) ? "" : "unchanged");
    }

    while (dwFilesList < (DWORD)ARGC)
    {
        /* puts */ (strcpy (szPath, ARGV[dwFilesList++]));

        if (szPath[strlen (szPath) - 1] == '\"')
            szPath[strlen (szPath) - 1]  = '\\';


        if (!(dwOptions & OPT_RECURSIVE) )  /* file or directory */
        {
            _fullpath (szFullPath, szPath, LEN_PATH);
            if ((dwResult = Touch (szFullPath)) != ERROR_SUCCESS)
                PrintError (szPath, dwResult);
        }
        else if ( strchr (szPath, '*') || (dwOptions & OPT_RECURSIVE) )
        {
            if (FolderExists (szPath))
                strcat (szPath, "\\");

            _splitpath (szPath, szDrive, szDir, szFileName, szExt);
            strcpy (szPath, szDrive);
            strcat (szPath, szDir);
            strcpy (szFindName, szFileName);
            strcat (szFindName, szExt);
            _fullpath (szFullPath, (szPath[0]) ? szPath : ".\\", LEN_PATH);
            szFullPath[strlen (szFullPath) - 1] = '\0';  /* Delete the last '\' */

            FindFileRecursive (szFullPath, szFindName);
        }
    }

    return ExitCode (ERROR_SUCCESS);
}  /* main */
