ReactOS  0.4.12-dev-75-g00dd17e
xcopy.c
Go to the documentation of this file.
1 /*
2  * XCOPY - Wine-compatible xcopy program
3  *
4  * Copyright (C) 2007 J. Edmeades
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 /*
22  * FIXME:
23  * This should now support all options listed in the xcopy help from
24  * windows XP except:
25  * /Z - Copy from network drives in restartable mode
26  * /X - Copy file audit settings (sets /O)
27  * /O - Copy file ownership + ACL info
28  * /G - Copy encrypted files to unencrypted destination
29  * /V - Verifies files
30  */
31 
32 /*
33  * Notes:
34  * Documented valid return codes are:
35  * 0 - OK
36  * 1 - No files found to copy (*1)
37  * 2 - CTRL+C during copy
38  * 4 - Initialization error, or invalid source specification
39  * 5 - Disk write error
40  *
41  * (*1) Testing shows return code 1 is never returned
42  */
43 
44 
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <windows.h>
48 #include <wine/debug.h>
49 #include <wine/unicode.h>
50 #include "xcopy.h"
51 
53 
54 
55 /* Typedefs */
56 typedef struct _EXCLUDELIST
57 {
58  struct _EXCLUDELIST *next;
60 } EXCLUDELIST;
61 
62 
63 /* Global variables */
64 static ULONG filesCopied = 0; /* Number of files copied */
65 static EXCLUDELIST *excludeList = NULL; /* Excluded strings list */
66 static FILETIME dateRange; /* Date range to copy after*/
67 static const WCHAR wchr_slash[] = {'\\', 0};
68 static const WCHAR wchr_star[] = {'*', 0};
69 static const WCHAR wchr_dot[] = {'.', 0};
70 static const WCHAR wchr_dotdot[] = {'.', '.', 0};
71 
72 
73 /* To minimize stack usage during recursion, some temporary variables
74  made global */
77 
78 
79 /* =========================================================================
80  * Load a string from the resource file, handling any error
81  * Returns string retrieved from resource file
82  * ========================================================================= */
84  static WCHAR msg[MAXSTRING];
85  const WCHAR failedMsg[] = {'F', 'a', 'i', 'l', 'e', 'd', '!', 0};
86 
87  if (!LoadStringW(GetModuleHandleW(NULL), id, msg, ARRAY_SIZE(msg))) {
88  WINE_FIXME("LoadString failed with %d\n", GetLastError());
89  lstrcpyW(msg, failedMsg);
90  }
91  return msg;
92 }
93 
94 /* =========================================================================
95  * Output a formatted unicode string. Ideally this will go to the console
96  * and hence required WriteConsoleW to output it, however if file i/o is
97  * redirected, it needs to be WriteFile'd using OEM (not ANSI) format
98  * ========================================================================= */
99 static int WINAPIV XCOPY_wprintf(const WCHAR *format, ...) {
100 
101  static WCHAR *output_bufW = NULL;
102  static char *output_bufA = NULL;
103  static BOOL toConsole = TRUE;
104  static BOOL traceOutput = FALSE;
105 #define MAX_WRITECONSOLE_SIZE 65535
106 
107  __ms_va_list parms;
108  DWORD nOut;
109  int len;
110  DWORD res = 0;
111 
112  /*
113  * Allocate buffer to use when writing to console
114  * Note: Not freed - memory will be allocated once and released when
115  * xcopy ends
116  */
117 
118  if (!output_bufW) output_bufW = HeapAlloc(GetProcessHeap(), 0,
119  MAX_WRITECONSOLE_SIZE*sizeof(WCHAR));
120  if (!output_bufW) {
121  WINE_FIXME("Out of memory - could not allocate 2 x 64K buffers\n");
122  return 0;
123  }
124 
125  __ms_va_start(parms, format);
127  len = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, format, 0, 0, output_bufW,
128  MAX_WRITECONSOLE_SIZE/sizeof(*output_bufW), &parms);
129  __ms_va_end(parms);
130  if (len == 0 && GetLastError() != NO_ERROR) {
131  WINE_FIXME("Could not format string: le=%u, fmt=%s\n", GetLastError(), wine_dbgstr_w(format));
132  return 0;
133  }
134 
135  /* Try to write as unicode whenever we think it's a console */
136  if (toConsole) {
138  output_bufW, len, &nOut, NULL);
139  }
140 
141  /* If writing to console has failed (ever) we assume it's file
142  i/o so convert to OEM codepage and output */
143  if (!res) {
144  BOOL usedDefaultChar = FALSE;
145  DWORD convertedChars;
146 
147  toConsole = FALSE;
148 
149  /*
150  * Allocate buffer to use when writing to file. Not freed, as above
151  */
152  if (!output_bufA) output_bufA = HeapAlloc(GetProcessHeap(), 0,
154  if (!output_bufA) {
155  WINE_FIXME("Out of memory - could not allocate 2 x 64K buffers\n");
156  return 0;
157  }
158 
159  /* Convert to OEM, then output */
160  convertedChars = WideCharToMultiByte(GetConsoleOutputCP(), 0, output_bufW,
161  len, output_bufA, MAX_WRITECONSOLE_SIZE,
162  "?", &usedDefaultChar);
163  WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), output_bufA, convertedChars,
164  &nOut, FALSE);
165  }
166 
167  /* Trace whether screen or console */
168  if (!traceOutput) {
169  WINE_TRACE("Writing to console? (%d)\n", toConsole);
170  traceOutput = TRUE;
171  }
172  return nOut;
173 }
174 
175 /* =========================================================================
176  * Load a string for a system error and writes it to the screen
177  * Returns string retrieved from resource file
178  * ========================================================================= */
180  LPWSTR lpMsgBuf;
181  int status;
182 
185  NULL, err, 0,
186  (LPWSTR) &lpMsgBuf, 0, NULL);
187  if (!status) {
188  WINE_FIXME("FIXME: Cannot display message for error %d, status %d\n",
189  err, GetLastError());
190  } else {
191  const WCHAR infostr[] = {'%', '1', '\n', 0};
192  XCOPY_wprintf(infostr, lpMsgBuf);
193  LocalFree ((HLOCAL)lpMsgBuf);
194  }
195 }
196 
197 
198 /* =========================================================================
199  * Routine copied from cmd.exe md command -
200  * This works recursively. so creating dir1\dir2\dir3 will create dir1 and
201  * dir2 if they do not already exist.
202  * ========================================================================= */
204 {
205  int len;
206  WCHAR *new_path;
207  BOOL ret = TRUE;
208 
209  new_path = HeapAlloc(GetProcessHeap(),0, sizeof(WCHAR) * (lstrlenW(path)+1));
210  lstrcpyW(new_path,path);
211 
212  while ((len = lstrlenW(new_path)) && new_path[len - 1] == '\\')
213  new_path[len - 1] = 0;
214 
215  while (!CreateDirectoryW(new_path,NULL))
216  {
217  WCHAR *slash;
218  DWORD last_error = GetLastError();
219  if (last_error == ERROR_ALREADY_EXISTS)
220  break;
221 
222  if (last_error != ERROR_PATH_NOT_FOUND)
223  {
224  ret = FALSE;
225  break;
226  }
227 
228  if (!(slash = wcsrchr(new_path,'\\')) && ! (slash = wcsrchr(new_path,'/')))
229  {
230  ret = FALSE;
231  break;
232  }
233 
234  len = slash - new_path;
235  new_path[len] = 0;
236  if (!XCOPY_CreateDirectory(new_path))
237  {
238  ret = FALSE;
239  break;
240  }
241  new_path[len] = '\\';
242  }
243  HeapFree(GetProcessHeap(),0,new_path);
244  return ret;
245 }
246 
247 /* =========================================================================
248  * Process a single file from the /EXCLUDE: file list, building up a list
249  * of substrings to avoid copying
250  * Returns TRUE on any failure
251  * ========================================================================= */
253 
254  WCHAR endChar = *endOfName;
256  FILE *inFile = NULL;
257  const WCHAR readTextMode[] = {'r', 't', 0};
258 
259  /* Null terminate the filename (temporarily updates the filename hence
260  parms not const) */
261  *endOfName = 0x00;
262 
263  /* Open the file */
264  inFile = _wfopen(filename, readTextMode);
265  if (inFile == NULL) {
267  *endOfName = endChar;
268  return TRUE;
269  }
270 
271  /* Process line by line */
272  while (fgetws(buffer, ARRAY_SIZE(buffer), inFile) != NULL) {
273  EXCLUDELIST *thisEntry;
274  int length = lstrlenW(buffer);
275 
276  /* If more than CRLF */
277  if (length > 1) {
278  buffer[length-1] = 0; /* strip CRLF */
279  thisEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(EXCLUDELIST));
280  thisEntry->next = excludeList;
281  excludeList = thisEntry;
282  thisEntry->name = HeapAlloc(GetProcessHeap(), 0,
283  (length * sizeof(WCHAR))+1);
284  lstrcpyW(thisEntry->name, buffer);
285  CharUpperBuffW(thisEntry->name, length);
286  WINE_TRACE("Read line : '%s'\n", wine_dbgstr_w(thisEntry->name));
287  }
288  }
289 
290  /* See if EOF or error occurred */
291  if (!feof(inFile)) {
293  *endOfName = endChar;
294  fclose(inFile);
295  return TRUE;
296  }
297 
298  /* Revert the input string to original form, and cleanup + return */
299  *endOfName = endChar;
300  fclose(inFile);
301  return FALSE;
302 }
303 
304 /* =========================================================================
305  * Process the /EXCLUDE: file list, building up a list of substrings to
306  * avoid copying
307  * Returns TRUE on any failure
308  * ========================================================================= */
310 
311  WCHAR *filenameStart = parms;
312 
313  WINE_TRACE("/EXCLUDE parms: '%s'\n", wine_dbgstr_w(parms));
314  excludeList = NULL;
315 
316  while (*parms && *parms != ' ' && *parms != '/') {
317 
318  /* If found '+' then process the file found so far */
319  if (*parms == '+') {
320  if (XCOPY_ProcessExcludeFile(filenameStart, parms)) {
321  return TRUE;
322  }
323  filenameStart = parms+1;
324  }
325  parms++;
326  }
327 
328  if (filenameStart != parms) {
329  if (XCOPY_ProcessExcludeFile(filenameStart, parms)) {
330  return TRUE;
331  }
332  }
333 
334  return FALSE;
335 }
336 
337 /* =========================================================================
338  XCOPY_DoCopy - Recursive function to copy files based on input parms
339  of a stem and a spec
340 
341  This works by using FindFirstFile supplying the source stem and spec.
342  If results are found, any non-directory ones are processed
343  Then, if /S or /E is supplied, another search is made just for
344  directories, and this function is called again for that directory
345 
346  ========================================================================= */
347 static int XCOPY_DoCopy(WCHAR *srcstem, WCHAR *srcspec,
348  WCHAR *deststem, WCHAR *destspec,
349  DWORD flags)
350 {
351  WIN32_FIND_DATAW *finddata;
352  HANDLE h;
353  BOOL findres = TRUE;
354  WCHAR *inputpath, *outputpath;
355  BOOL copiedFile = FALSE;
356  DWORD destAttribs, srcAttribs;
357  BOOL skipFile;
358  int ret = 0;
359 
360  /* Allocate some working memory on heap to minimize footprint */
361  finddata = HeapAlloc(GetProcessHeap(), 0, sizeof(WIN32_FIND_DATAW));
362  inputpath = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
363  outputpath = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
364 
365  /* Build the search info into a single parm */
366  lstrcpyW(inputpath, srcstem);
367  lstrcatW(inputpath, srcspec);
368 
369  /* Search 1 - Look for matching files */
370  h = FindFirstFileW(inputpath, finddata);
371  while (h != INVALID_HANDLE_VALUE && findres) {
372 
373  skipFile = FALSE;
374 
375  /* Ignore . and .. */
376  if (lstrcmpW(finddata->cFileName, wchr_dot)==0 ||
377  lstrcmpW(finddata->cFileName, wchr_dotdot)==0 ||
378  finddata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
379 
380  WINE_TRACE("Skipping directory, . or .. (%s)\n", wine_dbgstr_w(finddata->cFileName));
381  } else {
382 
383  /* Get the filename information */
384  lstrcpyW(copyFrom, srcstem);
385  if (flags & OPT_SHORTNAME) {
386  lstrcatW(copyFrom, finddata->cAlternateFileName);
387  } else {
388  lstrcatW(copyFrom, finddata->cFileName);
389  }
390 
391  lstrcpyW(copyTo, deststem);
392  if (*destspec == 0x00) {
393  if (flags & OPT_SHORTNAME) {
394  lstrcatW(copyTo, finddata->cAlternateFileName);
395  } else {
396  lstrcatW(copyTo, finddata->cFileName);
397  }
398  } else {
399  lstrcatW(copyTo, destspec);
400  }
401 
402  /* Do the copy */
403  WINE_TRACE("ACTION: Copy '%s' -> '%s'\n", wine_dbgstr_w(copyFrom),
405  if (!copiedFile && !(flags & OPT_SIMULATE)) XCOPY_CreateDirectory(deststem);
406 
407  /* See if allowed to copy it */
408  srcAttribs = GetFileAttributesW(copyFrom);
409  WINE_TRACE("Source attribs: %d\n", srcAttribs);
410 
411  if ((srcAttribs & FILE_ATTRIBUTE_HIDDEN) ||
412  (srcAttribs & FILE_ATTRIBUTE_SYSTEM)) {
413 
414  if (!(flags & OPT_COPYHIDSYS)) {
415  skipFile = TRUE;
416  }
417  }
418 
419  if (!(srcAttribs & FILE_ATTRIBUTE_ARCHIVE) &&
420  (flags & OPT_ARCHIVEONLY)) {
421  skipFile = TRUE;
422  }
423 
424  /* See if file exists */
425  destAttribs = GetFileAttributesW(copyTo);
426  WINE_TRACE("Dest attribs: %d\n", srcAttribs);
427 
428  /* Check date ranges if a destination file already exists */
429  if (!skipFile && (flags & OPT_DATERANGE) &&
430  (CompareFileTime(&finddata->ftLastWriteTime, &dateRange) < 0)) {
431  WINE_TRACE("Skipping file as modified date too old\n");
432  skipFile = TRUE;
433  }
434 
435  /* If just /D supplied, only overwrite if src newer than dest */
436  if (!skipFile && (flags & OPT_DATENEWER) &&
437  (destAttribs != INVALID_FILE_ATTRIBUTES)) {
440  NULL);
441  if (h != INVALID_HANDLE_VALUE) {
442  FILETIME writeTime;
443  GetFileTime(h, NULL, NULL, &writeTime);
444 
445  if (CompareFileTime(&finddata->ftLastWriteTime, &writeTime) <= 0) {
446  WINE_TRACE("Skipping file as dest newer or same date\n");
447  skipFile = TRUE;
448  }
449  CloseHandle(h);
450  }
451  }
452 
453  /* See if exclude list provided. Note since filenames are case
454  insensitive, need to uppercase the filename before doing
455  strstr */
456  if (!skipFile && (flags & OPT_EXCLUDELIST)) {
458  WCHAR copyFromUpper[MAX_PATH];
459 
460  /* Uppercase source filename */
461  lstrcpyW(copyFromUpper, copyFrom);
462  CharUpperBuffW(copyFromUpper, lstrlenW(copyFromUpper));
463 
464  /* Loop through testing each exclude line */
465  while (pos) {
466  if (wcsstr(copyFromUpper, pos->name) != NULL) {
467  WINE_TRACE("Skipping file as matches exclude '%s'\n",
468  wine_dbgstr_w(pos->name));
469  skipFile = TRUE;
470  pos = NULL;
471  } else {
472  pos = pos->next;
473  }
474  }
475  }
476 
477  /* Prompt each file if necessary */
478  if (!skipFile && (flags & OPT_SRCPROMPT)) {
479  DWORD count;
480  char answer[10];
481  BOOL answered = FALSE;
482  WCHAR yesChar[2];
483  WCHAR noChar[2];
484 
485  /* Read the Y and N characters from the resource file */
488 
489  while (!answered) {
491  ReadFile (GetStdHandle(STD_INPUT_HANDLE), answer, sizeof(answer),
492  &count, NULL);
493 
494  answered = TRUE;
495  if (toupper(answer[0]) == noChar[0])
496  skipFile = TRUE;
497  else if (toupper(answer[0]) != yesChar[0])
498  answered = FALSE;
499  }
500  }
501 
502  if (!skipFile &&
503  destAttribs != INVALID_FILE_ATTRIBUTES && !(flags & OPT_NOPROMPT)) {
504  DWORD count;
505  char answer[10];
506  BOOL answered = FALSE;
507  WCHAR yesChar[2];
508  WCHAR allChar[2];
509  WCHAR noChar[2];
510 
511  /* Read the A,Y and N characters from the resource file */
515 
516  while (!answered) {
518  ReadFile (GetStdHandle(STD_INPUT_HANDLE), answer, sizeof(answer),
519  &count, NULL);
520 
521  answered = TRUE;
522  if (toupper(answer[0]) == allChar[0])
523  flags |= OPT_NOPROMPT;
524  else if (toupper(answer[0]) == noChar[0])
525  skipFile = TRUE;
526  else if (toupper(answer[0]) != yesChar[0])
527  answered = FALSE;
528  }
529  }
530 
531  /* See if it has to exist! */
532  if (destAttribs == INVALID_FILE_ATTRIBUTES && (flags & OPT_MUSTEXIST)) {
533  skipFile = TRUE;
534  }
535 
536  /* Output a status message */
537  if (!skipFile) {
538  if (flags & OPT_QUIET) {
539  /* Skip message */
540  } else if (flags & OPT_FULL) {
541  const WCHAR infostr[] = {'%', '1', ' ', '-', '>', ' ',
542  '%', '2', '\n', 0};
543 
544  XCOPY_wprintf(infostr, copyFrom, copyTo);
545  } else {
546  const WCHAR infostr[] = {'%', '1', '\n', 0};
547  XCOPY_wprintf(infostr, copyFrom);
548  }
549 
550  /* If allowing overwriting of read only files, remove any
551  write protection */
552  if ((destAttribs & FILE_ATTRIBUTE_READONLY) &&
553  (flags & OPT_REPLACEREAD)) {
554  SetFileAttributesW(copyTo, destAttribs & ~FILE_ATTRIBUTE_READONLY);
555  }
556 
557  copiedFile = TRUE;
558  if (flags & OPT_SIMULATE || flags & OPT_NOCOPY) {
559  /* Skip copy */
560  } else if (CopyFileW(copyFrom, copyTo, FALSE) == 0) {
561 
564  copyFrom, copyTo, error);
565  XCOPY_FailMessage(error);
566 
567  if (flags & OPT_IGNOREERRORS) {
568  skipFile = TRUE;
569  } else {
570  ret = RC_WRITEERROR;
571  goto cleanup;
572  }
573  } else {
574 
575  if (!skipFile) {
576  /* If keeping attributes, update the destination attributes
577  otherwise remove the read only attribute */
578  if (flags & OPT_KEEPATTRS) {
579  SetFileAttributesW(copyTo, srcAttribs | FILE_ATTRIBUTE_ARCHIVE);
580  } else {
582  (GetFileAttributesW(copyTo) & ~FILE_ATTRIBUTE_READONLY));
583  }
584 
585  /* If /M supplied, remove the archive bit after successful copy */
586  if ((srcAttribs & FILE_ATTRIBUTE_ARCHIVE) &&
587  (flags & OPT_REMOVEARCH)) {
588  SetFileAttributesW(copyFrom, (srcAttribs & ~FILE_ATTRIBUTE_ARCHIVE));
589  }
590  filesCopied++;
591  }
592  }
593  }
594  }
595 
596  /* Find next file */
597  findres = FindNextFileW(h, finddata);
598  }
599  FindClose(h);
600 
601  /* Search 2 - do subdirs */
602  if (flags & OPT_RECURSIVE) {
603 
604  /* If /E is supplied, create the directory now */
605  if ((flags & OPT_EMPTYDIR) &&
606  !(flags & OPT_SIMULATE)) {
607  XCOPY_CreateDirectory(deststem);
608  }
609 
610  lstrcpyW(inputpath, srcstem);
611  lstrcatW(inputpath, wchr_star);
612  findres = TRUE;
613  WINE_TRACE("Processing subdirs with spec: %s\n", wine_dbgstr_w(inputpath));
614 
615  h = FindFirstFileW(inputpath, finddata);
616  while (h != INVALID_HANDLE_VALUE && findres) {
617 
618  /* Only looking for dirs */
619  if ((finddata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
620  (lstrcmpW(finddata->cFileName, wchr_dot) != 0) &&
621  (lstrcmpW(finddata->cFileName, wchr_dotdot) != 0)) {
622 
623  WINE_TRACE("Handling subdir: %s\n", wine_dbgstr_w(finddata->cFileName));
624 
625  /* Make up recursive information */
626  lstrcpyW(inputpath, srcstem);
627  lstrcatW(inputpath, finddata->cFileName);
628  lstrcatW(inputpath, wchr_slash);
629 
630  lstrcpyW(outputpath, deststem);
631  if (*destspec == 0x00) {
632  lstrcatW(outputpath, finddata->cFileName);
633  lstrcatW(outputpath, wchr_slash);
634  }
635 
636  XCOPY_DoCopy(inputpath, srcspec, outputpath, destspec, flags);
637  }
638 
639  /* Find next one */
640  findres = FindNextFileW(h, finddata);
641  }
642  FindClose(h);
643  }
644 
645 cleanup:
646 
647  /* free up memory */
648  HeapFree(GetProcessHeap(), 0, finddata);
649  HeapFree(GetProcessHeap(), 0, inputpath);
650  HeapFree(GetProcessHeap(), 0, outputpath);
651 
652  return ret;
653 }
654 
655 
656 /* =========================================================================
657  XCOPY_ParseCommandLine - Parses the command line
658  ========================================================================= */
659 static inline BOOL is_whitespace(WCHAR c)
660 {
661  return c == ' ' || c == '\t';
662 }
663 
665 {
666  for (; *p && is_whitespace(*p); p++);
667  return p;
668 }
669 
670 static inline BOOL is_digit(WCHAR c)
671 {
672  return c >= '0' && c <= '9';
673 }
674 
675 /* Windows XCOPY uses a simplified command line parsing algorithm
676  that lacks the escaped-quote logic of build_argv(), because
677  literal double quotes are illegal in any of its arguments.
678  Example: 'XCOPY "c:\DIR A" "c:DIR B\"' is OK. */
679 static int find_end_of_word(const WCHAR *word, WCHAR **end)
680 {
681  BOOL in_quotes = FALSE;
682  const WCHAR *ptr = word;
683  for (;;) {
684  for (; *ptr != '\0' && *ptr != '"' &&
685  (in_quotes || !is_whitespace(*ptr)); ptr++);
686  if (*ptr == '"') {
687  in_quotes = !in_quotes;
688  ptr++;
689  }
690  /* Odd number of double quotes is illegal for XCOPY */
691  if (in_quotes && *ptr == '\0')
692  return RC_INITERROR;
693  if (*ptr == '\0' || (!in_quotes && is_whitespace(*ptr)))
694  break;
695  }
696  *end = (WCHAR*)ptr;
697  return RC_OK;
698 }
699 
700 /* Remove all double quotes from a word */
701 static void strip_quotes(WCHAR *word, WCHAR **end)
702 {
703  WCHAR *rp, *wp;
704  for (rp = word, wp = word; *rp != '\0'; rp++) {
705  if (*rp == '"')
706  continue;
707  if (wp < rp)
708  *wp = *rp;
709  wp++;
710  }
711  *wp = '\0';
712  *end = wp;
713 }
714 
715 static int XCOPY_ParseCommandLine(WCHAR *suppliedsource,
716  WCHAR *supplieddestination, DWORD *pflags)
717 {
718  const WCHAR EXCLUDE[] = {'E', 'X', 'C', 'L', 'U', 'D', 'E', ':', 0};
719  DWORD flags = *pflags;
720  WCHAR *cmdline, *word, *end, *next;
721  int rc = RC_INITERROR;
722 
723  cmdline = _wcsdup(GetCommandLineW());
724  if (cmdline == NULL)
725  return rc;
726 
727  /* Skip first arg, which is the program name */
728  if ((rc = find_end_of_word(cmdline, &word)) != RC_OK)
729  goto out;
730  word = skip_whitespace(word);
731 
732  while (*word)
733  {
734  WCHAR first;
735  if ((rc = find_end_of_word(word, &end)) != RC_OK)
736  goto out;
737 
738  next = skip_whitespace(end);
739  first = word[0];
740  *end = '\0';
741  strip_quotes(word, &end);
742  WINE_TRACE("Processing Arg: '%s'\n", wine_dbgstr_w(word));
743 
744  /* First non-switch parameter is source, second is destination */
745  if (first != '/') {
746  if (suppliedsource[0] == 0x00) {
747  lstrcpyW(suppliedsource, word);
748  } else if (supplieddestination[0] == 0x00) {
749  lstrcpyW(supplieddestination, word);
750  } else {
752  goto out;
753  }
754  } else {
755  /* Process all the switch options
756  Note: Windows docs say /P prompts when dest is created
757  but tests show it is done for each src file
758  regardless of the destination */
759  int skip=0;
760  WCHAR *rest;
761 
762  while (word[0]) {
763  rest = NULL;
764 
765  switch (toupper(word[1])) {
766  case 'I': flags |= OPT_ASSUMEDIR; break;
767  case 'S': flags |= OPT_RECURSIVE; break;
768  case 'Q': flags |= OPT_QUIET; break;
769  case 'F': flags |= OPT_FULL; break;
770  case 'L': flags |= OPT_SIMULATE; break;
771  case 'W': flags |= OPT_PAUSE; break;
772  case 'T': flags |= OPT_NOCOPY | OPT_RECURSIVE; break;
773  case 'Y': flags |= OPT_NOPROMPT; break;
774  case 'N': flags |= OPT_SHORTNAME; break;
775  case 'U': flags |= OPT_MUSTEXIST; break;
776  case 'R': flags |= OPT_REPLACEREAD; break;
777  case 'K': flags |= OPT_KEEPATTRS; break;
778  case 'H': flags |= OPT_COPYHIDSYS; break;
779  case 'C': flags |= OPT_IGNOREERRORS; break;
780  case 'P': flags |= OPT_SRCPROMPT; break;
781  case 'A': flags |= OPT_ARCHIVEONLY; break;
782  case 'M': flags |= OPT_ARCHIVEONLY |
783  OPT_REMOVEARCH; break;
784 
785  /* E can be /E or /EXCLUDE */
786  case 'E': if (CompareStringW(LOCALE_USER_DEFAULT,
788  &word[1], 8,
789  EXCLUDE, -1) == CSTR_EQUAL) {
790  if (XCOPY_ProcessExcludeList(&word[9])) {
792  goto out;
793  } else {
794  flags |= OPT_EXCLUDELIST;
795 
796  /* Do not support concatenated switches onto exclude lists yet */
797  rest = end;
798  }
799  } else {
800  flags |= OPT_EMPTYDIR | OPT_RECURSIVE;
801  }
802  break;
803 
804  /* D can be /D or /D: */
805  case 'D': if (word[2]==':' && is_digit(word[3])) {
806  SYSTEMTIME st;
807  WCHAR *pos = &word[3];
808  BOOL isError = FALSE;
809  memset(&st, 0x00, sizeof(st));
810 
811  /* Microsoft xcopy's usage message implies that the date
812  * format depends on the locale, but that is false.
813  * It is hardcoded to month-day-year.
814  */
815  st.wMonth = _wtol(pos);
816  while (*pos && is_digit(*pos)) pos++;
817  if (*pos++ != '-') isError = TRUE;
818 
819  if (!isError) {
820  st.wDay = _wtol(pos);
821  while (*pos && is_digit(*pos)) pos++;
822  if (*pos++ != '-') isError = TRUE;
823  }
824 
825  if (!isError) {
826  st.wYear = _wtol(pos);
827  while (*pos && is_digit(*pos)) pos++;
828  if (st.wYear < 100) st.wYear+=2000;
829  }
830 
831  /* Handle switches straight after the supplied date */
832  rest = pos;
833 
834  if (!isError && SystemTimeToFileTime(&st, &dateRange)) {
835  SYSTEMTIME st;
836  WCHAR datestring[32], timestring[32];
837 
838  flags |= OPT_DATERANGE;
839 
840  /* Debug info: */
841  FileTimeToSystemTime (&dateRange, &st);
842  GetDateFormatW(0, DATE_SHORTDATE, &st, NULL, datestring,
843  ARRAY_SIZE(datestring));
845  NULL, timestring, ARRAY_SIZE(timestring));
846 
847  WINE_TRACE("Date being used is: %s %s\n",
848  wine_dbgstr_w(datestring), wine_dbgstr_w(timestring));
849  } else {
851  goto out;
852  }
853  } else {
854  flags |= OPT_DATENEWER;
855  }
856  break;
857 
858  case '-': if (toupper(word[2])=='Y') {
859  flags &= ~OPT_NOPROMPT;
860  rest = &word[3]; /* Skip over 3 characters */
861  }
862  break;
864  rc = RC_HELP;
865  goto out;
866  case 'V':
867  WINE_FIXME("ignoring /V\n");
868  break;
869  default:
870  WINE_TRACE("Unhandled parameter '%s'\n", wine_dbgstr_w(word));
872  goto out;
873  }
874 
875  /* Unless overridden above, skip over the '/' and the first character */
876  if (rest == NULL) rest = &word[2];
877 
878  /* By now, rest should point either to the null after the
879  switch, or the beginning of the next switch if there
880  was no whitespace between them */
881  if (!skip && *rest && *rest != '/') {
882  WINE_FIXME("Unexpected characters found and ignored '%s'\n", wine_dbgstr_w(rest));
883  skip=1;
884  } else {
885  word = rest;
886  }
887  }
888  }
889  word = next;
890  }
891 
892  /* Default the destination if not supplied */
893  if (supplieddestination[0] == 0x00)
894  lstrcpyW(supplieddestination, wchr_dot);
895 
896  *pflags = flags;
897  rc = RC_OK;
898 
899  out:
900  free(cmdline);
901  return rc;
902 }
903 
904 
905 /* =========================================================================
906  XCOPY_ProcessSourceParm - Takes the supplied source parameter, and
907  converts it into a stem and a filespec
908  ========================================================================= */
909 static int XCOPY_ProcessSourceParm(WCHAR *suppliedsource, WCHAR *stem,
910  WCHAR *spec, DWORD flags)
911 {
912  WCHAR actualsource[MAX_PATH];
913  WCHAR *starPos;
914  WCHAR *questPos;
915  DWORD attribs;
916 
917  /*
918  * Validate the source, expanding to full path ensuring it exists
919  */
920  if (GetFullPathNameW(suppliedsource, MAX_PATH, actualsource, NULL) == 0) {
921  WINE_FIXME("Unexpected failure expanding source path (%d)\n", GetLastError());
922  return RC_INITERROR;
923  }
924 
925  /* If full names required, convert to using the full path */
926  if (flags & OPT_FULL) {
927  lstrcpyW(suppliedsource, actualsource);
928  }
929 
930  /*
931  * Work out the stem of the source
932  */
933 
934  /* If a directory is supplied, use that as-is (either fully or
935  partially qualified)
936  If a filename is supplied + a directory or drive path, use that
937  as-is
938  Otherwise
939  If no directory or path specified, add eg. C:
940  stem is Drive/Directory is bit up to last \ (or first :)
941  spec is bit after that */
942 
943  starPos = wcschr(suppliedsource, '*');
944  questPos = wcschr(suppliedsource, '?');
945  if (starPos || questPos) {
946  attribs = 0x00; /* Ensures skips invalid or directory check below */
947  } else {
948  attribs = GetFileAttributesW(actualsource);
949  }
950 
951  if (attribs == INVALID_FILE_ATTRIBUTES) {
953  return RC_INITERROR;
954 
955  /* Directory:
956  stem should be exactly as supplied plus a '\', unless it was
957  eg. C: in which case no slash required */
958  } else if (attribs & FILE_ATTRIBUTE_DIRECTORY) {
959  WCHAR lastChar;
960 
961  WINE_TRACE("Directory supplied\n");
962  lstrcpyW(stem, suppliedsource);
963  lastChar = stem[lstrlenW(stem)-1];
964  if (lastChar != '\\' && lastChar != ':') {
965  lstrcatW(stem, wchr_slash);
966  }
967  lstrcpyW(spec, wchr_star);
968 
969  /* File or wildcard search:
970  stem should be:
971  Up to and including last slash if directory path supplied
972  If c:filename supplied, just the c:
973  Otherwise stem should be the current drive letter + ':' */
974  } else {
975  WCHAR *lastDir;
976 
977  WINE_TRACE("Filename supplied\n");
978  lastDir = wcsrchr(suppliedsource, '\\');
979 
980  if (lastDir) {
981  lstrcpyW(stem, suppliedsource);
982  stem[(lastDir-suppliedsource) + 1] = 0x00;
983  lstrcpyW(spec, (lastDir+1));
984  } else if (suppliedsource[1] == ':') {
985  lstrcpyW(stem, suppliedsource);
986  stem[2] = 0x00;
987  lstrcpyW(spec, suppliedsource+2);
988  } else {
989  WCHAR curdir[MAXSTRING];
990  GetCurrentDirectoryW(ARRAY_SIZE(curdir), curdir);
991  stem[0] = curdir[0];
992  stem[1] = curdir[1];
993  stem[2] = 0x00;
994  lstrcpyW(spec, suppliedsource);
995  }
996  }
997 
998  return RC_OK;
999 }
1000 
1001 /* =========================================================================
1002  XCOPY_ProcessDestParm - Takes the supplied destination parameter, and
1003  converts it into a stem
1004  ========================================================================= */
1005 static int XCOPY_ProcessDestParm(WCHAR *supplieddestination, WCHAR *stem, WCHAR *spec,
1006  WCHAR *srcspec, DWORD flags)
1007 {
1008  WCHAR actualdestination[MAX_PATH];
1009  DWORD attribs;
1010  BOOL isDir = FALSE;
1011 
1012  /*
1013  * Validate the source, expanding to full path ensuring it exists
1014  */
1015  if (GetFullPathNameW(supplieddestination, MAX_PATH, actualdestination, NULL) == 0) {
1016  WINE_FIXME("Unexpected failure expanding source path (%d)\n", GetLastError());
1017  return RC_INITERROR;
1018  }
1019 
1020  /* Destination is either a directory or a file */
1021  attribs = GetFileAttributesW(actualdestination);
1022 
1023  if (attribs == INVALID_FILE_ATTRIBUTES) {
1024 
1025  /* If /I supplied and wildcard copy, assume directory */
1026  /* Also if destination ends with backslash */
1027  if ((flags & OPT_ASSUMEDIR &&
1028  (wcschr(srcspec, '?') || wcschr(srcspec, '*'))) ||
1029  (supplieddestination[lstrlenW(supplieddestination)-1] == '\\')) {
1030 
1031  isDir = TRUE;
1032 
1033  } else {
1034  DWORD count;
1035  char answer[10] = "";
1036  WCHAR fileChar[2];
1037  WCHAR dirChar[2];
1038 
1039  /* Read the F and D characters from the resource file */
1042 
1043  while (answer[0] != fileChar[0] && answer[0] != dirChar[0]) {
1044  XCOPY_wprintf(XCOPY_LoadMessage(STRING_QISDIR), supplieddestination);
1045 
1046  ReadFile(GetStdHandle(STD_INPUT_HANDLE), answer, sizeof(answer), &count, NULL);
1047  WINE_TRACE("User answer %c\n", answer[0]);
1048 
1049  answer[0] = toupper(answer[0]);
1050  }
1051 
1052  if (answer[0] == dirChar[0]) {
1053  isDir = TRUE;
1054  } else {
1055  isDir = FALSE;
1056  }
1057  }
1058  } else {
1059  isDir = (attribs & FILE_ATTRIBUTE_DIRECTORY);
1060  }
1061 
1062  if (isDir) {
1063  lstrcpyW(stem, actualdestination);
1064  *spec = 0x00;
1065 
1066  /* Ensure ends with a '\' */
1067  if (stem[lstrlenW(stem)-1] != '\\') {
1068  lstrcatW(stem, wchr_slash);
1069  }
1070 
1071  } else {
1072  WCHAR drive[MAX_PATH];
1073  WCHAR dir[MAX_PATH];
1074  WCHAR fname[MAX_PATH];
1075  WCHAR ext[MAX_PATH];
1076  _wsplitpath(actualdestination, drive, dir, fname, ext);
1077  lstrcpyW(stem, drive);
1078  lstrcatW(stem, dir);
1079  lstrcpyW(spec, fname);
1080  lstrcatW(spec, ext);
1081  }
1082  return RC_OK;
1083 }
1084 
1085 
1086 /* =========================================================================
1087  main - Main entrypoint for the xcopy command
1088 
1089  Processes the args, and drives the actual copying
1090  ========================================================================= */
1091 int wmain (int argc, WCHAR *argvW[])
1092 {
1093  int rc = 0;
1094  WCHAR suppliedsource[MAX_PATH] = {0}; /* As supplied on the cmd line */
1095  WCHAR supplieddestination[MAX_PATH] = {0};
1096  WCHAR sourcestem[MAX_PATH] = {0}; /* Stem of source */
1097  WCHAR sourcespec[MAX_PATH] = {0}; /* Filespec of source */
1098  WCHAR destinationstem[MAX_PATH] = {0}; /* Stem of destination */
1099  WCHAR destinationspec[MAX_PATH] = {0}; /* Filespec of destination */
1100  WCHAR copyCmd[MAXSTRING]; /* COPYCMD env var */
1101  DWORD flags = 0; /* Option flags */
1102  const WCHAR PROMPTSTR1[] = {'/', 'Y', 0};
1103  const WCHAR PROMPTSTR2[] = {'/', 'y', 0};
1104  const WCHAR COPYCMD[] = {'C', 'O', 'P', 'Y', 'C', 'M', 'D', 0};
1105 
1106  /* Preinitialize flags based on COPYCMD */
1107  if (GetEnvironmentVariableW(COPYCMD, copyCmd, MAXSTRING)) {
1108  if (wcsstr(copyCmd, PROMPTSTR1) != NULL ||
1109  wcsstr(copyCmd, PROMPTSTR2) != NULL) {
1110  flags |= OPT_NOPROMPT;
1111  }
1112  }
1113 
1114  /* FIXME: On UNIX, files starting with a '.' are treated as hidden under
1115  wine, but on windows these can be normal files. At least one installer
1116  uses files such as .packlist and (validly) expects them to be copied.
1117  Under wine, if we do not copy hidden files by default then they get
1118  lose */
1119  flags |= OPT_COPYHIDSYS;
1120 
1121  /*
1122  * Parse the command line
1123  */
1124  if ((rc = XCOPY_ParseCommandLine(suppliedsource, supplieddestination,
1125  &flags)) != RC_OK) {
1126  if (rc == RC_HELP)
1127  return RC_OK;
1128  else
1129  return rc;
1130  }
1131 
1132  /* Trace out the supplied information */
1133  WINE_TRACE("Supplied parameters:\n");
1134  WINE_TRACE("Source : '%s'\n", wine_dbgstr_w(suppliedsource));
1135  WINE_TRACE("Destination : '%s'\n", wine_dbgstr_w(supplieddestination));
1136 
1137  /* Extract required information from source specification */
1138  rc = XCOPY_ProcessSourceParm(suppliedsource, sourcestem, sourcespec, flags);
1139  if (rc != RC_OK) return rc;
1140 
1141  /* Extract required information from destination specification */
1142  rc = XCOPY_ProcessDestParm(supplieddestination, destinationstem,
1143  destinationspec, sourcespec, flags);
1144  if (rc != RC_OK) return rc;
1145 
1146  /* Trace out the resulting information */
1147  WINE_TRACE("Resolved parameters:\n");
1148  WINE_TRACE("Source Stem : '%s'\n", wine_dbgstr_w(sourcestem));
1149  WINE_TRACE("Source Spec : '%s'\n", wine_dbgstr_w(sourcespec));
1150  WINE_TRACE("Dest Stem : '%s'\n", wine_dbgstr_w(destinationstem));
1151  WINE_TRACE("Dest Spec : '%s'\n", wine_dbgstr_w(destinationspec));
1152 
1153  /* Pause if necessary */
1154  if (flags & OPT_PAUSE) {
1155  DWORD count;
1156  char pausestr[10];
1157 
1159  ReadFile (GetStdHandle(STD_INPUT_HANDLE), pausestr, sizeof(pausestr),
1160  &count, NULL);
1161  }
1162 
1163  /* Now do the hard work... */
1164  rc = XCOPY_DoCopy(sourcestem, sourcespec,
1165  destinationstem, destinationspec,
1166  flags);
1167 
1168  /* Clear up exclude list allocated memory */
1169  while (excludeList) {
1171  excludeList = excludeList -> next;
1172  HeapFree(GetProcessHeap(), 0, pos->name);
1173  HeapFree(GetProcessHeap(), 0, pos);
1174  }
1175 
1176  /* Finished - print trailer and exit */
1177  if (flags & OPT_SIMULATE) {
1179  } else if (!(flags & OPT_NOCOPY)) {
1181  }
1182  return rc;
1183 
1184 }
BOOL WINAPI SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes)
Definition: fileinfo.c:944
void _wsplitpath(const WCHAR *path, WCHAR *drv, WCHAR *dir, WCHAR *name, WCHAR *ext)
Definition: splitpath.c:22
static BOOL XCOPY_ProcessExcludeFile(WCHAR *filename, WCHAR *endOfName)
Definition: xcopy.c:252
#define ERROR_INVALID_PARAMETER
Definition: compat.h:91
BOOL WINAPI CreateDirectoryW(IN LPCWSTR lpPathName, IN LPSECURITY_ATTRIBUTES lpSecurityAttributes)
Definition: dir.c:90
BOOL WINAPI FindNextFileW(IN HANDLE hFindFile, OUT LPWIN32_FIND_DATAW lpFindFileData)
Definition: find.c:382
static int argc
Definition: ServiceArgs.c:12
static const char __ms_va_list
Definition: printf.c:70
struct _EXCLUDELIST EXCLUDELIST
#define TRUE
Definition: types.h:120
#define CloseHandle
Definition: compat.h:398
#define OPT_KEEPATTRS
Definition: xcopy.h:53
#define OPT_MUSTEXIST
Definition: xcopy.h:43
#define WideCharToMultiByte
Definition: compat.h:101
#define error(str)
Definition: mkdosfs.c:1605
static const WCHAR wchr_star[]
Definition: xcopy.c:68
#define OPT_QUIET
Definition: xcopy.h:36
WINE_DEFAULT_DEBUG_CHANNEL(xcopy)
WORD wMonth
Definition: winbase.h:871
#define RC_HELP
Definition: xcopy.h:31
_Check_return_ _CRTIMP FILE *__cdecl _wfopen(_In_z_ const wchar_t *_Filename, _In_z_ const wchar_t *_Mode)
#define MAX_WRITECONSOLE_SIZE
__wchar_t WCHAR
Definition: xmlstorage.h:180
#define FILE_ATTRIBUTE_SYSTEM
Definition: nt_native.h:704
_CONST_RETURN wchar_t *__cdecl wcsstr(_In_z_ const wchar_t *_Str, _In_z_ const wchar_t *_SubStr)
#define OPT_DATENEWER
Definition: xcopy.h:52
const GLint * attribs
Definition: glext.h:10538
#define LOCALE_USER_DEFAULT
int WINAPI lstrcmpW(LPCWSTR lpString1, LPCWSTR lpString2)
Definition: lstring.c:170
BOOL WINAPI DECLSPEC_HOTPATCH WriteConsoleW(IN HANDLE hConsoleOutput, IN CONST VOID *lpBuffer, IN DWORD nNumberOfCharsToWrite, OUT LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved)
Definition: readwrite.c:1449
GLuint GLuint GLsizei count
Definition: gl.h:1545
#define DATE_SHORTDATE
Definition: winnls.h:193
#define FORMAT_MESSAGE_FROM_STRING
Definition: winbase.h:402
#define OPT_NOCOPY
Definition: xcopy.h:40
#define free
Definition: debug_ros.c:5
const GLint * first
Definition: glext.h:5794
static WCHAR copyTo[MAX_PATH]
Definition: xcopy.c:76
#define SORT_STRINGSORT
Definition: winnls.h:180
#define OPT_SHORTNAME
Definition: xcopy.h:42
#define OPT_NOPROMPT
Definition: xcopy.h:41
#define GetCurrentDirectoryW(x, y)
Definition: compat.h:413
struct _EXCLUDELIST * next
Definition: xcopy.c:58
#define STRING_SRCPROMPT
Definition: xcopy.h:64
#define INVALID_HANDLE_VALUE
Definition: compat.h:391
#define WINE_TRACE
Definition: debug.h:353
DWORD WINAPI GetLastError(VOID)
Definition: except.c:1059
DWORD WINAPI GetFullPathNameW(IN LPCWSTR lpFileName, IN DWORD nBufferLength, OUT LPWSTR lpBuffer, OUT LPWSTR *lpFilePart)
Definition: path.c:1105
HANDLE WINAPI GetStdHandle(IN DWORD nStdHandle)
Definition: console.c:152
GLuint buffer
Definition: glext.h:5915
#define OPT_REPLACEREAD
Definition: xcopy.h:44
GLuint GLuint end
Definition: gl.h:1545
#define RC_INITERROR
Definition: xcopy.h:29
#define NORM_IGNORECASE
Definition: winnls.h:173
TCHAR * cmdline
Definition: stretchblt.cpp:32
#define STRING_SIMCOPY
Definition: xcopy.h:61
DWORD WINAPI FormatMessageW(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPWSTR lpBuffer, DWORD nSize, __ms_va_list *args)
Definition: format_msg.c:583
const char * filename
Definition: ioapi.h:135
#define NO_ERROR
Definition: dderror.h:5
#define lstrlenW
Definition: compat.h:407
int WINAPI LoadStringW(_In_opt_ HINSTANCE hInstance, _In_ UINT uID, _Out_writes_to_(cchBufferMax, return+1) LPWSTR lpBuffer, _In_ int cchBufferMax)
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
Definition: gl.h:1546
#define FILE_SHARE_READ
Definition: compat.h:125
GLfloat GLfloat GLfloat GLfloat h
Definition: glext.h:7723
#define OPT_PAUSE
Definition: xcopy.h:39
static BOOL XCOPY_ProcessExcludeList(WCHAR *parms)
Definition: xcopy.c:309
static const WCHAR wchr_slash[]
Definition: xcopy.c:67
#define OPT_IGNOREERRORS
Definition: xcopy.h:46
WORD wYear
Definition: winbase.h:870
#define OPT_COPYHIDSYS
Definition: xcopy.h:45
#define RC_OK
Definition: xcopy.h:26
#define OPT_EXCLUDELIST
Definition: xcopy.h:50
#define OPT_REMOVEARCH
Definition: xcopy.h:49
static int find_end_of_word(const WCHAR *word, WCHAR **end)
Definition: xcopy.c:679
DWORD WINAPI GetFileAttributesW(LPCWSTR lpFileName)
Definition: fileinfo.c:802
LPWSTR WINAPI GetCommandLineW(VOID)
Definition: proc.c:2043
static BOOL is_whitespace(WCHAR c)
Definition: xcopy.c:659
static PVOID ptr
Definition: dispmode.c:27
#define FORMAT_MESSAGE_ALLOCATE_BUFFER
Definition: winbase.h:400
char * wine_dbgstr_w(const wchar_t *wstr)
Definition: CString.cpp:62
UINT WINAPI DECLSPEC_HOTPATCH GetConsoleOutputCP(VOID)
Definition: console.c:2453
static BOOL is_digit(WCHAR c)
Definition: xcopy.c:670
UINT msg
Definition: msvc.h:92
smooth NULL
Definition: ftsmooth.c:416
char ext[3]
Definition: mkdosfs.c:358
static int WINAPIV XCOPY_wprintf(const WCHAR *format,...)
Definition: xcopy.c:99
_Check_return_ _CRTIMP _CONST_RETURN wchar_t *__cdecl wcsrchr(_In_z_ const wchar_t *_Str, _In_ wchar_t _Ch)
static int XCOPY_ProcessDestParm(WCHAR *supplieddestination, WCHAR *stem, WCHAR *spec, WCHAR *srcspec, DWORD flags)
Definition: xcopy.c:1005
unsigned int dir
Definition: maze.c:112
#define STD_INPUT_HANDLE
Definition: winbase.h:264
#define FORMAT_MESSAGE_FROM_SYSTEM
Definition: winbase.h:404
#define OPEN_EXISTING
Definition: compat.h:426
#define FILE_ATTRIBUTE_DIRECTORY
Definition: nt_native.h:705
BOOL WINAPI WriteFile(IN HANDLE hFile, IN LPCVOID lpBuffer, IN DWORD nNumberOfBytesToWrite OPTIONAL, OUT LPDWORD lpNumberOfBytesWritten OPTIONAL, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: rw.c:24
INT WINAPI CompareStringW(LCID lcid, DWORD flags, LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
Definition: lang.c:1899
static FILETIME dateRange
Definition: xcopy.c:66
int wmain(int argc, WCHAR *argvW[])
Definition: xcopy.c:1091
_Check_return_ _CRTIMP int __cdecl feof(_In_ FILE *_File)
int toupper(int c)
Definition: utclib.c:881
BOOL WINAPI FileTimeToSystemTime(IN CONST FILETIME *lpFileTime, OUT LPSYSTEMTIME lpSystemTime)
Definition: time.c:178
unsigned int BOOL
Definition: ntddk_ex.h:94
LONG WINAPI CompareFileTime(IN CONST FILETIME *lpFileTime1, IN CONST FILETIME *lpFileTime2)
Definition: time.c:106
#define GetProcessHeap()
Definition: compat.h:395
#define STRING_FILE_CHAR
Definition: xcopy.h:72
PVOID WINAPI HeapAlloc(HANDLE, DWORD, SIZE_T)
_CONST_RETURN wchar_t *__cdecl wcschr(_In_z_ const wchar_t *_Str, wchar_t _Ch)
BOOL WINAPI CopyFileW(IN LPCWSTR lpExistingFileName, IN LPCWSTR lpNewFileName, IN BOOL bFailIfExists)
Definition: copy.c:439
GLenum GLuint GLenum GLsizei length
Definition: glext.h:5579
static int XCOPY_DoCopy(WCHAR *srcstem, WCHAR *srcspec, WCHAR *deststem, WCHAR *destspec, DWORD flags)
Definition: xcopy.c:347
#define STRING_COPY
Definition: xcopy.h:62
#define OPT_ASSUMEDIR
Definition: xcopy.h:33
static const WCHAR wchr_dot[]
Definition: xcopy.c:69
#define MAX_PATH
Definition: compat.h:26
const GLubyte * c
Definition: glext.h:8905
unsigned int UINT
Definition: ndis.h:50
#define OPT_SRCPROMPT
Definition: xcopy.h:47
#define FILE_ATTRIBUTE_READONLY
Definition: nt_native.h:702
static FILE * out
Definition: regtests2xml.c:44
DWORD WINAPI CharUpperBuffW(_Inout_updates_(cchLength) LPWSTR lpsz, _In_ DWORD cchLength)
unsigned long DWORD
Definition: ntddk_ex.h:95
int nOut
Definition: unzcrash.c:44
#define EXCLUDE
Definition: match.h:22
#define RC_WRITEERROR
Definition: xcopy.h:30
#define SetLastError(x)
Definition: compat.h:409
#define STRING_OVERWRITE
Definition: xcopy.h:65
#define STRING_READFAIL
Definition: xcopy.h:68
#define INVALID_FILE_ATTRIBUTES
Definition: vfdcmd.c:23
#define STRING_DIR_CHAR
Definition: xcopy.h:73
GLbitfield flags
Definition: glext.h:7161
static WCHAR * skip_whitespace(WCHAR *p)
Definition: xcopy.c:664
#define FILE_ATTRIBUTE_ARCHIVE
Definition: nt_native.h:706
int ret
#define OPT_DATERANGE
Definition: xcopy.h:51
#define FILE_ATTRIBUTE_NORMAL
Definition: compat.h:126
_CRTIMP wchar_t *__cdecl wcscpy(_Out_writes_z_(_String_length_(_Source)+1) wchar_t *_Dest, _In_z_ const wchar_t *_Source)
static WCHAR copyFrom[MAX_PATH]
Definition: xcopy.c:75
GLenum GLsizei len
Definition: glext.h:6722
#define OPT_FULL
Definition: xcopy.h:37
#define CSTR_EQUAL
Definition: winnls.h:444
#define GENERIC_READ
Definition: compat.h:124
_Check_return_opt_ _CRTIMP int __cdecl fclose(_Inout_ FILE *_File)
#define STD_OUTPUT_HANDLE
Definition: winbase.h:265
#define err(...)
static BOOL XCOPY_CreateDirectory(const WCHAR *path)
Definition: xcopy.c:203
#define WINAPIV
Definition: sdbpapi.h:64
INT WINAPI GetDateFormatW(LCID lcid, DWORD dwFlags, const SYSTEMTIME *lpTime, LPCWSTR lpFormat, LPWSTR lpDateStr, INT cchOut)
Definition: lcformat.c:916
#define STRING_INVPARMS
Definition: xcopy.h:58
WORD wDay
Definition: winbase.h:873
#define STRING_QISDIR
Definition: xcopy.h:63
#define MAXSTRING
Definition: xcopy.h:55
BOOL WINAPI SystemTimeToFileTime(IN CONST SYSTEMTIME *lpSystemTime, OUT LPFILETIME lpFileTime)
Definition: time.c:148
static const WCHAR wchr_dotdot[]
Definition: xcopy.c:70
#define lstrcpyW
Definition: compat.h:406
#define ARRAY_SIZE(a)
Definition: main.h:24
HLOCAL NTAPI LocalFree(HLOCAL hMem)
Definition: heapmem.c:1577
#define STRING_NO_CHAR
Definition: xcopy.h:70
static EXCLUDELIST * excludeList
Definition: xcopy.c:65
#define skip(...)
Definition: CString.cpp:57
Definition: services.c:325
#define OPT_SIMULATE
Definition: xcopy.h:38
#define FILE_ATTRIBUTE_HIDDEN
Definition: nt_native.h:703
#define __ms_va_end(list)
Definition: windef.h:448
_Check_return_ _CRTIMP long __cdecl _wtol(_In_z_ const wchar_t *_Str)
#define STRING_HELP
Definition: xcopy.h:74
const WCHAR * word
Definition: lex.c:70
static ULONG filesCopied
Definition: xcopy.c:64
#define CreateFileW
Definition: compat.h:400
static int XCOPY_ParseCommandLine(WCHAR *suppliedsource, WCHAR *supplieddestination, DWORD *pflags)
Definition: xcopy.c:715
static void XCOPY_FailMessage(DWORD err)
Definition: xcopy.c:179
#define msg(x)
Definition: auth_time.c:54
#define __ms_va_start(list, arg)
Definition: windef.h:447
static WCHAR * XCOPY_LoadMessage(UINT id)
Definition: xcopy.c:83
GLuint res
Definition: glext.h:9613
wchar_t * fgetws(wchar_t *buf, int bufsize, FILE *file)
Definition: wmain.c:22
unsigned int ULONG
Definition: retypes.h:1
HMODULE WINAPI GetModuleHandleW(LPCWSTR lpModuleName)
Definition: loader.c:845
char * cleanup(char *str)
Definition: wpickclick.c:99
static void strip_quotes(WCHAR *word, WCHAR **end)
Definition: xcopy.c:701
#define TIME_NOSECONDS
Definition: winnls.h:266
#define ERROR_PATH_NOT_FOUND
Definition: winerror.h:106
#define STRING_ALL_CHAR
Definition: xcopy.h:71
LPWSTR WINAPI lstrcatW(LPWSTR lpString1, LPCWSTR lpString2)
Definition: lstring.c:274
#define ERROR_ALREADY_EXISTS
Definition: disk.h:80
#define STRING_YES_CHAR
Definition: xcopy.h:69
GLfloat GLfloat p
Definition: glext.h:8902
#define OPT_RECURSIVE
Definition: xcopy.h:34
WCHAR * LPWSTR
Definition: xmlstorage.h:184
#define STRING_PAUSE
Definition: xcopy.h:60
_Check_return_ _CRTIMP wchar_t *__cdecl _wcsdup(_In_z_ const wchar_t *_Str)
#define GetEnvironmentVariableW(x, y, z)
Definition: compat.h:412
#define OPT_ARCHIVEONLY
Definition: xcopy.h:48
INT WINAPI GetTimeFormatW(LCID lcid, DWORD dwFlags, const SYSTEMTIME *lpTime, LPCWSTR lpFormat, LPWSTR lpTimeStr, INT cchOut)
Definition: lcformat.c:1016
BOOL WINAPI ReadFile(IN HANDLE hFile, IN LPVOID lpBuffer, IN DWORD nNumberOfBytesToRead, OUT LPDWORD lpNumberOfBytesRead OPTIONAL, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: rw.c:123
#define STRING_COPYFAIL
Definition: xcopy.h:66
#define memset(x, y, z)
Definition: compat.h:39
static SERVICE_STATUS status
Definition: service.c:31
BOOL WINAPI GetFileTime(IN HANDLE hFile, OUT LPFILETIME lpCreationTime OPTIONAL, OUT LPFILETIME lpLastAccessTime OPTIONAL, OUT LPFILETIME lpLastWriteTime OPTIONAL)
Definition: fileinfo.c:1046
#define STRING_OPENFAIL
Definition: xcopy.h:67
#define HeapFree(x, y, z)
Definition: compat.h:394
WCHAR * name
Definition: xcopy.c:59
static int XCOPY_ProcessSourceParm(WCHAR *suppliedsource, WCHAR *stem, WCHAR *spec, DWORD flags)
Definition: xcopy.c:909
HANDLE WINAPI FindFirstFileW(IN LPCWSTR lpFileName, OUT LPWIN32_FIND_DATAW lpFindFileData)
Definition: find.c:320
#define OPT_EMPTYDIR
Definition: xcopy.h:35
#define STRING_INVPARM
Definition: xcopy.h:59
#define WINE_FIXME
Definition: debug.h:365
BOOL WINAPI FindClose(HANDLE hFindFile)
Definition: find.c:502