1  /* touch.c v1.21 - 2011-04-28
  2  Update:
  3    http://myc01.free.fr/touch
  4  
  5  License:
  6    Public Domain Dedication (CC0 1.0)
  7    http://creativecommons.org/publicdomain/zero/1.0/
  8  
  9  Usage:
 10  touch.exe [-hxamcDsvq] [-r ref_file|-t time|-d date_time] -- <file_1> [file_N]
 11  
 12    -h : Show this help
 13    -x : Modify creation time
 14    -a : Modify access time
 15    -m : Modify modification time
 16    -c : Do not create file if not found
 17    -r : Use a file or directory as a reference time source
 18    -D : If target doesn't exist, create a directory
 19    -s : Browse sub-directories
 20    -v : Output the result of every file processed
 21    -q : Silent mode, no error is displayed (disable '-v')
 22    -t : Use time [[CC]YY]MMDDhhmm[.SS] instead of today
 23    -d : Use date_time [[YYYY-]MM-DD][Thh:mm[:ss]] instead of today
 24    -- : Finish options, followed by the file list
 25  The options '-r', '-d' and '-t' can not be used simultaneously.
 26  By default, all times will be modified (creation, access and modification).
 27  
 28  Examples:
 29    touch.exe -sv -d 2011-04-23          -- ".\folder\*.txt"
 30    touch.exe -cv -d           T10:55:00 -- *.dat
 31    touch.exe -xv -d 2011-04-23T10:55    -- test2.txt test3.txt
 32  
 33  ********************************************************************************
 34  Changelog:
 35  1.21 - 2011-04-28
 36    * fix decode date/time
 37    * add touch folder with *.*
 38    * Combination of the options '-s' '-v' : '-sv'
 39  
 40  1.2 - 2011-04-26
 41    * argument from 'GetCommandLine' / 'CommandLineToArgv'
 42    * change option '-d' to '-D'
 43    * add option '-d date_time'
 44  
 45  1.1 - 2011-04-24
 46    * add options '-v' verbose, '-s' scan-subfolder, '-q' silent mode
 47  
 48  1.0 - 2011-04-20
 49    * initial version with options '-hxamcrdt -- file_n ...'
 50  */
 51  
 52  
 53  #include <stdio.h>
 54  #include <stdlib.h>
 55  #include <windows.h>
 56  
 57  #define PRG_NAME  "Touch"
 58  #define PRG_VERS  "1.21"
 59  #define PRG_DATE  "2011-04-28"
 60  #define PRG_LIC   "Public Domain Dedication (CC0 1.0)"
 61  
 62  /* Options in command line */
 63  #define OPT_MODIFY_TIME_C     1  /* -x */
 64  #define OPT_MODIFY_TIME_A     2  /* -a */
 65  #define OPT_MODIFY_TIME_M     4  /* -m */
 66  #define OPT_NO_CREATE         8  /* -c */
 67  #define OPT_USE_REF_FILE     16  /* -r */
 68  #define OPT_USER_TIME        32  /* -t */
 69  #define OPT_USER_DATE        64  /* -d */
 70  #define OPT_CREATE_DIR      128  /* -D */
 71  #define OPT_VERBOSE         256  /* -v */
 72  #define OPT_RECURSIVE       512  /* -s */
 73  #define OPT_QUIET          1024  /* -q */
 74  
 75  #define CommandLineToArgv  _CommandLineToArgvA
 76  #define LEN_PATH           1024     /* _MAX_PATH = 260 */
 77  
 78  LPFILETIME lpTimeC, lpTimeA, lpTimeM;
 79  DWORD      dwOptions;
 80  HANDLE     hStdOut;
 81  
 82  char **ARGV;
 83  int    ARGC;
 84  
 85  
 86  int GetOptC (const char cOption, const int nDef)
 87  {
 88      if ( (ARGC < 2) || (!ARGV) )
 89          return nDef;
 90  
 91      int i;
 92      for (i = 1; i < ARGC; i++)
 93          if ( (ARGV[i][0] == '-') && (strchr (ARGV[i], cOption) != NULL) )
 94              return 1;
 95      return nDef;
 96  }  /* GetOptC */
 97  
 98  
 99  char *GetOptCPStr (char **szStr, const char cOption, char *nDef)
100  {
101      if ( (ARGC < 2) || (!ARGV) )
102          return (*szStr = nDef);
103  
104      int i;
105      for (i = 1; i < ARGC; i++)
106          if ( (ARGV[i][0] == '-') && (ARGV[i][1] == cOption) )
107          {
108              if (++i < ARGC)
109                  return (*szStr = ARGV[i]);
110              else
111                  return (*szStr = nDef);
112          }
113  
114      return (*szStr = nDef);
115  }  /* GetOptCPStr */
116  
117  
118  char **_CommandLineToArgvA (const char *CmdLine, int *pnArgc)
119  {
120      char           a;
121      char         **argv, *pcArgv;
122      unsigned long  len, i, j, argc = 0;
123      int            in_QM = FALSE, in_TEXT = FALSE, in_SPACE = TRUE;
124  
125      len = strlen (CmdLine);
126      i = ( (len + 2) / 2) * sizeof (void*) + sizeof (void*);
127  
128      argv   = (char**) calloc (i + (len + 2) * sizeof (char), 1);
129      pcArgv = (char*) ( ( (unsigned char*) argv) + i);
130  
131      argv[argc] = pcArgv;
132      i = j = 0;
133  
134      while ( (a = CmdLine[i]) )
135      {
136          if (in_QM)
137          {
138              if (a == '\"')
139                  in_QM = FALSE;
140              else
141                  pcArgv[j++] = a;
142          }
143          else
144          {
145              switch (a)
146              {
147              case '\"':
148                  in_QM = in_TEXT = TRUE;
149                  if (in_SPACE)
150                      argv[argc++] = pcArgv + j;
151                  in_SPACE = FALSE;
152                  break;
153              case ' ' :
154              case '\t':
155              case '\n':
156              case '\r':
157                  if (in_TEXT)
158                      pcArgv[j++] = '\0';
159                  in_TEXT  = FALSE;
160                  in_SPACE = TRUE;
161                  break;
162              default:
163                  in_TEXT = TRUE;
164                  if (in_SPACE)
165                      argv[argc++] = pcArgv + j;
166                  pcArgv[j++] = a;
167                  in_SPACE = FALSE;
168                  break;
169              }
170          }
171          i++;
172      }
173      pcArgv[j]  = '\0';
174      argv[argc] = NULL;
175  
176      (*pnArgc) = argc;
177      return argv;
178  }  /* _CommandLineToArgvA */
179  
180  
181  BOOL FolderExists (LPCTSTR lpPath)
182  {
183      DWORD Attr = GetFileAttributes (lpPath);
184      if (Attr != INVALID_FILE_ATTRIBUTES)
185          if (Attr & FILE_ATTRIBUTE_DIRECTORY)
186              return TRUE;
187  
188      return FALSE;
189  }  /* FolderExists */
190  
191  
192  BOOL Exists (LPCTSTR lpFileName)
193  {
194      if (GetFileAttributes (lpFileName) != INVALID_FILE_ATTRIBUTES)
195          return TRUE;
196      else if (GetLastError () == ERROR_SHARING_VIOLATION)
197          return TRUE;
198      return FALSE;
199  } /* Exists */
200  
201  
202  int printFileTime (LPFILETIME lpFileTime)
203  {
204      if (!lpFileTime) return 0;
205      FILETIME   fTime;
206      SYSTEMTIME sTime;
207  
208      FileTimeToLocalFileTime (lpFileTime, &fTime);
209      FileTimeToSystemTime    (&fTime    , &sTime);
210      return printf ("%04hu"      "-%02hu"      "-%02hu"    "T%02hu"     ":%02hu"       ":%02hu",
211                     sTime.wYear, sTime.wMonth, sTime.wDay, sTime.wHour, sTime.wMinute, sTime.wSecond);
212  }  /* printFileTime */
213  
214  
215  int printfOem (LPCTSTR lpFmt, ...)
216  {
217      if (dwOptions & OPT_QUIET) return 0;
218      if (! lpFmt) return 0;
219      if (!*lpFmt) return 0;
220  
221      PCHAR lpBuf = calloc (LEN_PATH, 1);
222      INT nLen = _vsnprintf (lpBuf, LEN_PATH, lpFmt, (va_list)(&lpFmt + 1));
223      if (nLen < 0)
224          nLen = strlen (lpBuf);
225      CharToOemBuff (lpBuf, lpBuf, nLen);
226      WriteConsole (hStdOut, lpBuf, nLen, (PDWORD)&nLen, NULL);
227      free (lpBuf);
228      return nLen;
229  }  /* printfOem */
230  
231  
232  DWORD Touch (LPCTSTR lpFileName)
233  {
234      SetLastError (ERROR_SUCCESS);
235      DWORD dwFileAttributes = GetFileAttributes (lpFileName);
236      DWORD dwResult = GetLastError ();
237      if ((dwFileAttributes == INVALID_FILE_ATTRIBUTES) && (dwResult != ERROR_FILE_NOT_FOUND))
238          return dwResult;
239  
240      BOOL bCreateDir = ((OPT_CREATE_DIR   & dwOptions)                && (dwFileAttributes == INVALID_FILE_ATTRIBUTES)) ||
241                        ((dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (dwFileAttributes != INVALID_FILE_ATTRIBUTES));
242  
243      /* If target doesn't exist, create a directory. */
244      if (bCreateDir && (dwFileAttributes == INVALID_FILE_ATTRIBUTES))
245      {
246          SetLastError (ERROR_SUCCESS);
247          if (!CreateDirectory (lpFileName, NULL))
248          {
249              if ((dwResult = GetLastError ()) != ERROR_ALREADY_EXISTS)
250                  return dwResult;
251          }
252          else
253              return ERROR_SUCCESS;  /* Done, the directory is created. */
254      }
255  
256      SetLastError (ERROR_SUCCESS);
257      HANDLE hFile = CreateFile (lpFileName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
258                                 ( (dwOptions & OPT_NO_CREATE) || bCreateDir) ? OPEN_EXISTING : OPEN_ALWAYS,
259                                 FILE_ATTRIBUTE_NORMAL | (bCreateDir ? FILE_FLAG_BACKUP_SEMANTICS : 0), 0);
260      dwResult = GetLastError ();
261  
262      /* Check for CreateFile() special cases */
263      if (hFile == INVALID_HANDLE_VALUE)
264      {
265          if ( (dwOptions & OPT_NO_CREATE) && dwResult == ERROR_FILE_NOT_FOUND)
266              return ERROR_SUCCESS;  /* not an error */
267          else if (dwResult == ERROR_ALREADY_EXISTS)
268              dwResult = ERROR_SUCCESS;  /* not an error according to MSDN docs */
269          return dwResult;
270      }
271  
272      if (lpTimeC || lpTimeA || lpTimeM)
273      {
274          if (SetFileTime (hFile, lpTimeC, lpTimeA, lpTimeM))
275          {
276              if ( (dwOptions & OPT_VERBOSE) && !(dwOptions & OPT_QUIET) )
277                  printfOem     ("%s\n", lpFileName);  /*
278                  printf        ("%s\n", lpFileName);  */
279              dwResult = ERROR_SUCCESS;
280          }
281          else
282              dwResult = GetLastError ();
283      }
284      CloseHandle (hFile);
285  
286      return dwResult;
287  }  /* Touch */
288  
289  
290  BOOL PrintHelp (LPCTSTR lpMessage)
291  {
292      if (lpMessage)
293          printf ("%s\n\n", lpMessage);
294  
295      puts (PRG_NAME" v"PRG_VERS" ("PRG_DATE") - License: "PRG_LIC);  /*
296      puts ("********************************************************************************"); */
297      puts ("Usage:\n"
298            "touch.exe [-hxamcDsvq] [-r ref_file|-t time|-d date_time] -- <file_1> [file_N]");
299      puts ("  -h : Show this help\n"
300            "  -x : Modify creation time\n"
301            "  -a : Modify access time\n"
302            "  -m : Modify modification time\n"
303            "  -c : Do not create file if not found\n"
304            "  -r : Use a file or directory as a reference time source\n"
305            "  -D : If target doesn't exist, create a directory\n"
306            "  -s : Browse sub-directories\n"
307            "  -v : Output the result of every file processed\n"
308            "  -q : Silent mode, no error is displayed (disable '-v')\n"
309            "  -t : Use time [[CC]YY]MMDDhhmm[.SS] instead of today\n"
310            "  -d : Use date_time [[YYYY-]MM-DD][Thh:mm[:ss]] instead of today\n"
311            "  -- : Finish options, followed by the file list\n"
312            "The options '-r', '-d' and '-t' can not be used simultaneously.\n"
313            "By default, all times will be modified (creation, access and modification).");
314      puts ("Examples:\n"
315            "  touch.exe -sv -d 2011-04-23          -- \".\\folder\\*.txt\"\n"
316            "  touch.exe -cv -d           T10:55:00 -- *.dat\n"
317            "  touch.exe -xv -d 2011-04-23T10:55    -- test2.txt test3.txt");
318  
319      return FALSE;
320  }  /* PrintHelp */
321  
322  
323  /* Prints a message translated from a Windows Error code, then prints the usage */
324  PCHAR PrintError (LPCTSTR lpInfo, DWORD dwError)
325  {
326      if (dwOptions & OPT_QUIET) return NULL;
327  
328      LPTSTR lpText = NULL;
329      FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
330                     NULL, dwError, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpText, 0, NULL);
331      printfOem ("\nError: %s = %s", lpInfo, lpText);
332      LocalFree (lpText);
333  
334      return NULL;
335  }  /* PrintError */
336  
337  
338  BOOL GetArgs (LPDWORD dwList, LPTSTR *lpRefFile, LPTSTR *lpDateTime, LPDWORD dwOptions)
339  {
340      if (ARGC < 2)
341          return PrintHelp (NULL);
342      else if (ARGC == 2)
343      {
344          if (GetOptC ('h', 0))
345              return PrintHelp (NULL);
346          else if (ARGV[1][0] == '-')
347              return PrintHelp ("Error 1: Not enough arguments.");
348          *dwList = 1;
349      }
350      else
351      {
352          for (*dwList = 1; *dwList < (DWORD)ARGC; (*dwList)++)
353              if (!strcmp ("--", ARGV[*dwList]))
354                  break;
355          if (++(*dwList) >= (DWORD)ARGC)
356              return PrintHelp ("Error 2: No file(s) specified!");
357  
358          *dwOptions |= GetOptC ('x', 0) * OPT_MODIFY_TIME_C;
359          *dwOptions |= GetOptC ('a', 0) * OPT_MODIFY_TIME_A;
360          *dwOptions |= GetOptC ('m', 0) * OPT_MODIFY_TIME_M;
361          *dwOptions |= GetOptC ('c', 0) * OPT_NO_CREATE;
362          *dwOptions |= GetOptC ('D', 0) * OPT_CREATE_DIR;
363          *dwOptions |= GetOptC ('v', 0) * OPT_VERBOSE;
364          *dwOptions |= GetOptC ('s', 0) * OPT_RECURSIVE;
365          *dwOptions |= GetOptC ('q', 0) * OPT_QUIET;
366          /*
367          if (GetOptPStr (lpRefFile , "-r", NULL)) *dwOptions |= OPT_USE_REF_FILE;
368          if (GetOptPStr (lpDateTime, "-d", NULL)) *dwOptions |= OPT_USER_DATE;
369          if (GetOptPStr (lpDateTime, "-t", NULL)) *dwOptions |= OPT_USER_TIME;
370          */
371          if (GetOptCPStr (lpRefFile , 'r', NULL)) *dwOptions |= OPT_USE_REF_FILE;
372          if (GetOptCPStr (lpDateTime, 'd', NULL)) *dwOptions |= OPT_USER_DATE;
373          if (GetOptCPStr (lpDateTime, 't', NULL)) *dwOptions |= OPT_USER_TIME;
374      }
375  
376      if ( (*dwOptions & OPT_USE_REF_FILE) &&  (*dwOptions & OPT_USER_TIME) )
377          return PrintHelp ("Error 3: You may not specify both '-r' and '-t'.");
378  
379      if ( (*dwOptions & OPT_USE_REF_FILE) &&  (*dwOptions & OPT_USER_DATE) )
380          return PrintHelp ("Error 4: You may not specify both '-r' and '-d'.");
381  
382      if ( (*dwOptions & OPT_USER_TIME   ) &&  (*dwOptions & OPT_USER_DATE) )
383          return PrintHelp ("Error 5: You may not specify both '-t' and '-d'.");
384  
385      if ( (*dwOptions & OPT_USE_REF_FILE) && !(*lpRefFile                ) )
386          return PrintHelp ("Error 6: You must specify a 'ref_file' together with '-r'.");
387  
388      if ( (*dwOptions & OPT_USER_TIME   ) && !(*lpDateTime               ) )
389          return PrintHelp ("Error 7: You must specify a timestamp together with '-t'.");
390  
391      /* If -x, -a and -m wasn't specified, then all of them are implied */
392      if (! (*dwOptions &  (OPT_MODIFY_TIME_C | OPT_MODIFY_TIME_A | OPT_MODIFY_TIME_M) ) )
393             *dwOptions |= (OPT_MODIFY_TIME_C | OPT_MODIFY_TIME_A | OPT_MODIFY_TIME_M);
394  
395      return TRUE;
396  }  /* GetArgs */
397  
398  
399  /* Gets FILETIME from a file */
400  BOOL GetFileTimes (LPCTSTR lpFileName, LPFILETIME fTimeC, LPFILETIME fTimeA, LPFILETIME fTimeM)
401  {
402      BOOL bResult = TRUE;
403      HANDLE hFile = CreateFile (lpFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
404                                 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 0);
405  
406      if (INVALID_HANDLE_VALUE == hFile)
407          return PrintHelp (PrintError (lpFileName, GetLastError ()));
408  
409      if (!GetFileTime (hFile, fTimeC, fTimeA, fTimeM))
410          bResult = PrintHelp (PrintError (lpFileName, GetLastError ()));
411  
412      CloseHandle (hFile);
413      return bResult;
414  }  /* GetFileTimes */
415  
416  
417  /* Format: ISO 8601:2004(E)
418      http://en.wikipedia.org/wiki/ISO_8601
419      http://dotat.at/tmp/ISO_8601-2004_E.pdf
420      Extended format: YYYY-MM-DDThh:mm:ss Example: 1985-04-12T10:15:30
421  
422      [[[YYYY-]MM-DD][Thh:mm[:ss]]]
423      123456789ABCDEF1234
424      YYYY-MM-DDThh:mm:ss
425      YYYY-MM-DD
426      MM-DD
427      Thh:mm:ss
428      Thh:mm
429  */
430  BOOL DecodeDateTime (LPTSTR lpDateTime, LPSYSTEMTIME st)
431  {
432      char *pTime;
433      int nLen = (lpDateTime) ? strlen (lpDateTime) : 0;
434  
435      if ((nLen < 5) || (nLen > 19))
436          return PrintHelp ("Error 8: Invalid time format.");
437  
438      if (nLen && (pTime = strchr (lpDateTime, 'T')))
439      {
440          st->wSecond = st->wMilliseconds = 0;
441          nLen = strlen (pTime);
442               if (nLen == 9)  /* Thh:mm:ss */
443              sscanf (pTime, "T%02hu:%02hu:%02hu", &st->wHour, &st->wMinute, &st->wSecond);
444          else if (nLen == 6)  /* Thh:mm    */
445              sscanf (pTime, "T%02hu:%02hu"      , &st->wHour, &st->wMinute              );
446          else if (nLen >  0)
447              return PrintHelp ("Error 9: Invalid time format.");
448          *pTime = '\0';
449          nLen = strlen (lpDateTime);
450      }
451  
452      if (nLen > 0)
453      {
454               if (nLen == 10)  /* YYYY-MM-DD */
455              sscanf (lpDateTime, "%4hu-%02hu-%02hu", &st->wYear, &st->wMonth, &st->wDay);
456          else if (nLen ==  5)  /*      MM-DD */
457              sscanf (lpDateTime,      "%02hu-%02hu",             &st->wMonth, &st->wDay);
458          else
459              return PrintHelp ("Error 10: Invalid date format.");
460      }
461      return TRUE;
462  }  /* DecodeDateTime */
463  
464  
465  /* Format:
466    [[CC]YY]MMDDhhmm[.SS]
467    123456789ABCDEF
468    CCYYMMDDhhmm.SS
469    CCYYMMDDhhmm
470    MMDDhhmm
471   */
472  BOOL DecodeTime (LPTSTR lpDateTime, LPSYSTEMTIME st)
473  {
474      PCHAR pSec;
475      INT   nLen = (lpDateTime) ? strlen (lpDateTime) : 0;
476      WORD  CC = st->wYear / 100;
477      WORD  YY = st->wYear % 100;
478      st->wSecond = st->wMilliseconds = 0;
479  
480      if ((nLen < 8) || (nLen > 15))
481          return PrintHelp ("Error 11: Invalid time format.");
482  
483      if ((pSec = strchr (lpDateTime, '.')))
484      {
485          sscanf (pSec, ".%02hu", &st->wSecond);
486          *pSec = '\0';
487          nLen = strlen (lpDateTime);
488      }
489  
490      if (nLen > 0)
491      {
492               if (nLen == 12)  /* CC  YY   MM   DD  hh  mm */
493              sscanf (lpDateTime, "%2hu%02hu%02hu%2hu%2hu%2hu", &CC, &YY, &st->wMonth, &st->wDay, &st->wHour, &st->wMinute);
494          else if (nLen == 10)  /*     YY   MM   DD  hh  mm */
495              sscanf (lpDateTime,     "%02hu%02hu%2hu%2hu%2hu",      &YY, &st->wMonth, &st->wDay, &st->wHour, &st->wMinute);
496          else if (nLen ==  8)  /*          MM   DD  hh  mm */
497              sscanf (lpDateTime,          "%02hu%2hu%2hu%2hu",           &st->wMonth, &st->wDay, &st->wHour, &st->wMinute);
498          else
499              return PrintHelp ("Error 12: Invalid date format.");
500          if (nLen == 10)
501              CC = (YY < 69) ? 20 : 19;
502          st->wYear = CC * 100 + YY;
503      }
504      return TRUE;
505  }  /* DecodeTime */
506  
507  
508  BOOL StrToFileTime (LPTSTR lpDateTime, LPFILETIME ts)
509  {
510      INT nLen = (lpDateTime) ? strlen (lpDateTime) : 0;
511      SYSTEMTIME st;
512      GetLocalTime (&st);
513  
514      if (nLen)
515      {
516          if (strchr (lpDateTime, ':') || strchr (lpDateTime, '-'))
517          {
518              if (!DecodeDateTime (lpDateTime, &st))  /* [[[YYYY-]MM-DD][Thh:mm[:ss]]] */
519                  return FALSE;
520          }
521          else
522          {
523              if (!DecodeTime (lpDateTime, &st))  /* [[CC]YY]MMDDhhmm[.SS] */
524                  return FALSE;
525          }
526      }
527  
528      FILETIME ft;
529      if (!SystemTimeToFileTime (&st, &ft))
530          return PrintHelp (PrintError ("13 SystemTimeToFileTime()"   , GetLastError ()));
531  
532      if (!LocalFileTimeToFileTime (&ft, ts))
533          return PrintHelp (PrintError ("14 LocalFileTimeToFileTime()", GetLastError ()));
534  
535      return TRUE;
536  }  /* StrToFileTime */
537  
538  
539  DWORD FindFileRecursive (LPCTSTR lpDir, LPCTSTR lpFindName)
540  {
541      DWORD           dwResult = ERROR_SUCCESS;
542      HANDLE          hFile;
543      CHAR            szPath[LEN_PATH];
544      WIN32_FIND_DATA fData;
545  
546      strcpy (szPath, lpDir);
547      strcat (szPath, "\\");
548      strcat (szPath, (*lpFindName) ? lpFindName : "*.*");
549  
550      if ((hFile = FindFirstFile (szPath, &fData)) == INVALID_HANDLE_VALUE)
551          return GetLastError ();
552  
553      do
554      {
555          if (!strcmp (fData.cFileName, ".") || !strcmp (fData.cFileName, ".."))
556              continue;
557  
558          strcpy (szPath, lpDir);
559          strcat (szPath, "\\");
560          strcat (szPath, fData.cFileName);
561  
562          if (fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
563          {
564              if (fData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
565                  continue;
566  
567              if ((dwResult = Touch (szPath)) != ERROR_SUCCESS)
568                  PrintError (szPath, dwResult);
569  
570              if (dwOptions & OPT_RECURSIVE)
571              {
572                  if ((dwResult = FindFileRecursive (szPath, lpFindName)) != ERROR_SUCCESS)
573                  {
574                      FindClose (hFile);
575                      return dwResult;
576                  }
577              }
578          }
579          else
580              if ((dwResult = Touch (szPath)) != ERROR_SUCCESS)
581                  PrintError (szPath, dwResult);
582      }
583      while (FindNextFile (hFile, &fData));
584  
585      FindClose (hFile);
586      return dwResult;
587  }  /* FindFileRecursive */
588  
589  
590  int ExitCode (int status)
591  {
592      free (ARGV);
593      exit (status);
594  }  /* ExitCode */
595  
596  
597  int main (void)  /* (int argc, char *argv[]) */
598  {
599      DWORD     dwFilesList, dwResult;
600      CHAR      szPath [LEN_PATH], szFullPath [LEN_PATH], szFindName[_MAX_FNAME];
601      CHAR      szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szFileName[_MAX_FNAME], szExt[_MAX_EXT];
602      PCHAR     lpRefFile = NULL, lpDateTime = NULL;
603      FILETIME  fTimeC, fTimeA, fTimeM;
604  
605      ARGV = CommandLineToArgv (GetCommandLine (), &ARGC);
606      hStdOut = GetStdHandle (STD_OUTPUT_HANDLE);
607  
608      if (!GetArgs (&dwFilesList, &lpRefFile, &lpDateTime, &dwOptions))
609          ExitCode (-1);
610  
611      if (dwOptions & OPT_USE_REF_FILE)
612      {
613          if (!GetFileTimes (lpRefFile, &fTimeC, &fTimeA, &fTimeM))
614              ExitCode (-1);
615      }
616      else
617      {
618          if (!StrToFileTime (lpDateTime, &fTimeM))
619              ExitCode (-1);
620          fTimeC = fTimeA = fTimeM;
621      }
622  
623      lpTimeC = (dwOptions & OPT_MODIFY_TIME_C) ? &fTimeC : NULL;
624      lpTimeA = (dwOptions & OPT_MODIFY_TIME_A) ? &fTimeA : NULL;
625      lpTimeM = (dwOptions & OPT_MODIFY_TIME_M) ? &fTimeM : NULL;
626  
627      if ( (dwOptions & OPT_VERBOSE) && !(dwOptions & OPT_QUIET) )
628      {
629          printf (    "Create       = ");
630          printf ("%s\nAccess       = ", printFileTime (lpTimeC) ? "" : "unchanged");
631          printf ("%s\nModification = ", printFileTime (lpTimeA) ? "" : "unchanged");
632          printf ("%s\n\n"             , printFileTime (lpTimeM) ? "" : "unchanged");
633      }
634  
635      while (dwFilesList < (DWORD)ARGC)
636      {
637          /* puts */ (strcpy (szPath, ARGV[dwFilesList++]));
638  
639          if (szPath[strlen (szPath) - 1] == '\"')
640              szPath[strlen (szPath) - 1]  = '\\';
641  
642          if (Exists (szPath) && !(dwOptions & OPT_RECURSIVE) )  /* file or directory */
643          {
644              _fullpath (szFullPath, szPath, LEN_PATH);
645              if ((dwResult = Touch (szFullPath)) != ERROR_SUCCESS)
646                  PrintError (szPath, dwResult);
647          }
648          else if ( strchr (szPath, '*') || (dwOptions & OPT_RECURSIVE) )
649          {
650              if (FolderExists (szPath))
651                  strcat (szPath, "\\");
652  
653              _splitpath (szPath, szDrive, szDir, szFileName, szExt);
654              strcpy (szPath, szDrive);
655              strcat (szPath, szDir);
656              strcpy (szFindName, szFileName);
657              strcat (szFindName, szExt);
658              _fullpath (szFullPath, (szPath[0]) ? szPath : ".\\", LEN_PATH);
659              szFullPath[strlen (szFullPath) - 1] = '\0';  /* Delete the last '\' */
660  
661              FindFileRecursive (szFullPath, szFindName);
662          }
663      }
664  
665      return ExitCode (ERROR_SUCCESS);
666  }  /* main */