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 */