3 * COPY.C -- copy internal command.
8 * 01-Aug-98 (Rob Lake z63rrl@morgan.ucs.mun.ca)
11 * 13-Aug-1998 (John P. Price)
12 * fixed memory leak problem in copy function.
13 * fixed copy function so it would work with wildcards in the source
15 * 13-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
16 * Added COPY command to CMD.
18 * 26-Jan-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
19 * Replaced CRT io functions by Win32 io functions.
21 * 27-Oct-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
22 * Disabled prompting when used in batch mode.
27 #ifdef INCLUDE_CMD_COPY
40 #define VERIFY 1 /* VERIFY Switch */
41 #define BINARY 2 /* File is to be copied as BINARY */
42 #define ASCII 4 /* File is to be copied as ASCII */
43 #define PROMPT 8 /* Prompt before overwriting files */
44 #define NPROMPT 16 /* Do not prompt before overwriting files */
45 #define HELP 32 /* Help was asked for */
46 #define SOURCE 128 /* File is a source */
49 typedef struct tagFILES
51 struct tagFILES *next;
52 TCHAR szFile[MAX_PATH];
53 DWORD dwFlag; /* BINARY -xor- ASCII */
57 static BOOL DoSwitches (LPTSTR, LPDWORD);
58 static BOOL AddFile (LPFILES, TCHAR *, int *, int *, LPDWORD);
59 static BOOL AddFiles (LPFILES, TCHAR *, int *, int *, int *, LPDWORD);
60 static BOOL GetDestination (LPFILES, LPFILES);
61 static INT ParseCommand (LPFILES, int, TCHAR **, LPDWORD);
62 static VOID DeleteFileList (LPFILES);
63 static INT Overwrite (LPTSTR);
68 IsDirectory (LPTSTR fn)
70 if (!IsValidFileName (fn))
72 return (GetFileAttributes (fn) & FILE_ATTRIBUTE_DIRECTORY);
77 DoSwitches (LPTSTR arg, LPDWORD lpdwFlags)
79 if (!_tcsicmp (arg, _T("/-Y")))
82 *lpdwFlags &= ~NPROMPT;
85 else if (_tcslen (arg) > 2)
87 error_too_many_parameters (_T(""));
91 switch (_totupper (arg[1]))
99 *lpdwFlags &= ~BINARY;
103 *lpdwFlags |= BINARY;
104 *lpdwFlags &= ~ASCII;
108 *lpdwFlags &= ~PROMPT;
109 *lpdwFlags |= NPROMPT;
113 error_invalid_switch (arg[1]);
121 AddFile (LPFILES f, TCHAR *arg, int *source, int *dest, LPDWORD flags)
125 error_too_many_parameters (_T(""));
138 _tcscpy(f->szFile, arg);
139 f->dwFlag |= *flags & ASCII ? ASCII : BINARY;
140 if ((f->next = (LPFILES)malloc (sizeof (FILES))) == NULL)
142 error_out_of_memory ();
153 AddFiles (LPFILES f, TCHAR *arg, int *source, int *dest,
154 int *count, LPDWORD flags)
162 error_too_many_parameters (_T(""));
169 while (arg[j] == _T('+'))
172 while (arg[j] != _T('\0'))
175 if (t[k] == '+' || arg[j] == _T('\0'))
179 if (arg[j] == _T('\0') && t[k] != _T('+'))
183 _tcscpy (f->szFile, t);
186 f->dwFlag |= *flags | SOURCE | ASCII;
188 f->dwFlag |= *flags | BINARY | SOURCE;
190 if ((f->next = (LPFILES)malloc (sizeof (FILES))) == NULL)
192 error_out_of_memory ();
204 if (arg[--j] == _T('+'))
212 GetDestination (LPFILES f, LPFILES dest)
217 while (f->next != NULL)
225 if ((f->dwFlag & SOURCE) == 0)
229 _tcscpy (dest->szFile, f->szFile);
230 dest->dwFlag = f->dwFlag;
241 ParseCommand (LPFILES f, int argc, TCHAR **arg, LPDWORD lpdwFlags)
252 for (i = 0; i < argc; i++)
254 if (arg[i][0] == _T('/'))
256 if (!DoSwitches (arg[i], lpdwFlags))
261 if (!_tcscmp(arg[i], _T("+")))
263 else if (!_tcschr(arg[i], _T('+')) && source)
266 // Make sure we have a clean workable path
268 GetFullPathName( arg[i], 128, (LPTSTR) &temp, NULL);
269 // printf("A Input %s, Output %s\n", arg[i], temp);
271 if (!AddFile(f, (TCHAR *) &temp, &source, &dest, lpdwFlags))
279 GetFullPathName( arg[i], 128, (LPTSTR) &temp, NULL);
280 // printf("B Input %s, Output %s\n", arg[i], temp);
282 if (!AddFiles(f, (TCHAR *) &temp, &source, &dest, &count, lpdwFlags))
284 while (f->next != NULL)
291 DebugPrintf (_T("ParseCommand: flags has %s\n"),
292 *lpdwFlags & ASCII ? _T("ASCII") : _T("BINARY"));
299 DeleteFileList (LPFILES f)
313 Overwrite (LPTSTR fn)
318 ConOutPrintf (_T("Overwrite %s (Yes/No/All)? "), fn);
319 ConInString (inp, 10);
323 for (p = inp; _istspace (*p); p++)
326 if (*p != _T('Y') && *p != _T('A'))
335 #define BUFF_SIZE 16384 /* 16k = max buffer size */
338 int copy (LPTSTR source, LPTSTR dest, int append, LPDWORD lpdwFlags)
351 DebugPrintf (_T("checking mode\n"));
354 dwAttrib = GetFileAttributes (source);
356 hFileSrc = CreateFile (source, GENERIC_READ, FILE_SHARE_READ,
357 NULL, OPEN_EXISTING, 0, NULL);
358 if (hFileSrc == INVALID_HANDLE_VALUE)
360 ConErrPrintf (_T("Error: Cannot open source - %s!\n"), source);
365 DebugPrintf (_T("getting time\n"));
368 GetFileTime (hFileSrc, &srctime, NULL, NULL);
371 DebugPrintf (_T("copy: flags has %s\n"),
372 *lpdwFlags & ASCII ? "ASCII" : "BINARY");
375 if (!IsValidFileName (dest))
378 DebugPrintf (_T("opening/creating\n"));
381 CreateFile (dest, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
385 if (!_tcscmp (dest, source))
387 ConErrPrintf (_T("Error: Can't copy onto itself!\n"));
388 CloseHandle (hFileSrc);
393 DebugPrintf (_T("SetFileAttributes (%s, FILE_ATTRIBUTE_NORMAL);\n"), dest);
395 SetFileAttributes (dest, FILE_ATTRIBUTE_NORMAL);
398 DebugPrintf (_T("DeleteFile (%s);\n"), dest);
403 CreateFile (dest, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
407 LONG lFilePosHigh = 0;
409 if (!_tcscmp (dest, source))
411 CloseHandle (hFileSrc);
416 DebugPrintf (_T("opening/appending\n"));
419 CreateFile (dest, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
420 SetFilePointer (hFileDest, 0, &lFilePosHigh,FILE_END);
423 if (hFileDest == INVALID_HANDLE_VALUE)
425 CloseHandle (hFileSrc);
426 error_path_not_found ();
430 buffer = (LPBYTE)malloc (BUFF_SIZE);
433 CloseHandle (hFileDest);
434 CloseHandle (hFileSrc);
435 error_out_of_memory ();
441 ReadFile (hFileSrc, buffer, BUFF_SIZE, &dwRead, NULL);
442 if (*lpdwFlags & ASCII)
444 for (i = 0; i < dwRead; i++)
446 if (((LPTSTR)buffer)[i] == 0x1A)
458 WriteFile (hFileDest, buffer, dwRead, &dwWritten, NULL);
459 if (dwWritten != dwRead)
461 ConErrPrintf (_T("Error writing destination!\n"));
463 CloseHandle (hFileDest);
464 CloseHandle (hFileSrc);
468 while (dwRead && !bEof);
471 DebugPrintf (_T("setting time\n"));
473 SetFileTime (hFileDest, &srctime, NULL, NULL);
475 if (*lpdwFlags & ASCII)
477 ((LPTSTR)buffer)[0] = 0x1A;
478 ((LPTSTR)buffer)[1] = _T('\0');
480 DebugPrintf (_T("appending ^Z\n"));
482 WriteFile (hFileDest, buffer, sizeof(TCHAR), &dwWritten, NULL);
486 CloseHandle (hFileDest);
487 CloseHandle (hFileSrc);
490 DebugPrintf (_T("setting mode\n"));
492 SetFileAttributes (dest, dwAttrib);
499 SetupCopy (LPFILES sources, TCHAR **p, BOOL bMultiple,
500 TCHAR *drive_d, TCHAR *dir_d, TCHAR *file_d,
501 TCHAR *ext_d, int *append, LPDWORD lpdwFlags)
503 WIN32_FIND_DATA find;
504 TCHAR drive_s[_MAX_DRIVE];
505 TCHAR dir_s[_MAX_DIR];
506 TCHAR file_s[_MAX_FNAME];
507 TCHAR ext_s[_MAX_EXT];
508 TCHAR from_merge[_MAX_PATH];
520 DebugPrintf (_T("SetupCopy\n"));
523 real_source = (LPTSTR)malloc (MAX_PATH * sizeof(TCHAR));
524 real_dest = (LPTSTR)malloc (MAX_PATH * sizeof(TCHAR));
526 if (!real_source || !real_dest)
528 error_out_of_memory ();
529 DeleteFileList (sources);
536 while (sources->next != NULL)
539 /* Force a clean full path
541 GetFullPathName( sources->szFile, 128, (LPTSTR) &temp, NULL);
543 _tsplitpath (sources->szFile, drive_s, dir_s, file_s, ext_s);
545 hFind = FindFirstFile ((TCHAR*)&temp, &find);
546 if (hFind == INVALID_HANDLE_VALUE)
548 error_file_not_found();
557 if (find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
560 _tmakepath(from_merge, drive_d, dir_d, file_d, ext_d);
562 if (from_merge[_tcslen(from_merge) - 1] == _T('\\'))
563 from_merge[_tcslen(from_merge) - 1] = 0;
565 // printf("Merge %s, filename %s\n", from_merge, find.cFileName);
567 if (IsDirectory (from_merge))
570 // printf("Merge DIR\n");
573 _tcscat (from_merge, _T("\\"));
574 _tcscat (from_merge, find.cFileName);
579 _tcscpy (real_dest, from_merge);
580 _tmakepath (real_source, drive_s, dir_s, find.cFileName, NULL);
583 DebugPrintf(_T("copying %S -> %S (%Sappending%S)\n"),
584 real_source, real_dest,
585 *append ? _T("") : _T("not "),
586 sources->dwFlag & ASCII ? _T(", ASCII") : _T(", BINARY"));
589 if (IsValidFileName (real_dest) && !bAll)
591 /* Don't prompt in a batch file */
600 over = Overwrite (real_dest);
609 if (copy (real_source, real_dest, *append, lpdwFlags))
612 bDone = FindNextFile (hFind, &find);
620 sources = sources->next;
630 INT cmd_copy (LPTSTR first, LPTSTR rest)
633 TCHAR drive_d[_MAX_DRIVE];
634 TCHAR dir_d[_MAX_DIR];
635 TCHAR file_d[_MAX_FNAME];
636 TCHAR ext_d[_MAX_EXT];
643 LPFILES sources = NULL;
644 LPFILES start = NULL;
651 if (!_tcsncmp (rest, _T("/?"), 2))
653 ConOutPuts (_T("Copies one or more files to another location.\n"
655 "COPY [/V][/Y|/-Y][/A|/B] source [/A|/B]\n"
656 " [+ source [/A|/B] [+ ...]] [destination [/A|/B]]\n"
658 " source Specifies the file or files to be copied.\n"
659 " /A Indicates an ASCII text file.\n"
660 " /B Indicates a binary file.\n"
661 " destination Specifies the directory and/or filename for the new file(s).\n"
662 " /V Verifies that new files are written correctly.\n"
663 " /Y Suppresses prompting to confirm you want to overwrite an\n"
664 " existing destination file.\n"
665 " /-Y Causes prompting to confirm you want to overwrite an\n"
666 " existing destination file.\n"
668 "The switch /Y may be present in the COPYCMD environment variable.\n"
673 p = split (rest, &argc, FALSE);
677 error_req_param_missing ();
681 sources = (LPFILES)malloc (sizeof (FILES));
684 error_out_of_memory ();
687 sources->next = NULL;
690 if ((files = ParseCommand (sources, argc, p, &dwFlags)) == -1)
692 DeleteFileList (sources);
698 error_req_param_missing();
699 DeleteFileList (sources);
705 bDestFound = GetDestination (sources, &dest);
708 _tsplitpath (dest.szFile, drive_d, dir_d, file_d, ext_d);
709 if (IsDirectory (dest.szFile))
711 // printf("A szFile= %s, Dir = %s, File = %s, Ext = %s\n", dest.szFile, dir_d, file_d, ext_d);
712 _tcscat (dir_d, file_d);
713 _tcscat (dir_d, ext_d);
714 file_d[0] = _T('\0');
719 if (_tcschr (dest.szFile, _T('*')) || _tcschr (dest.szFile, _T('?')))
724 if (_tcschr(rest, _T('+')))
732 if (bDestFound && !bWildcards)
735 // _tcscpy(sources->szFile, dest.szFile);
737 copied = SetupCopy (sources, p, bMultiple, drive_d, dir_d, file_d, ext_d, &append, &dwFlags);
739 else if (bDestFound && bWildcards)
741 ConErrPrintf (_T("Error: Not implemented yet!\n"));
742 DeleteFileList (sources);
746 else if (!bDestFound && !bMultiple)
748 _tsplitpath (sources->szFile, drive_d, dir_d, file_d, ext_d);
749 if (IsDirectory (sources->szFile))
751 // printf("B File = %s, Ext = %s\n", file_d, ext_d);
753 _tcscat (dir_d, file_d);
754 _tcscat (dir_d, ext_d);
755 file_d[0] = _T('\0');
758 copied = SetupCopy (sources, p, FALSE, _T(""), _T(""), file_d, ext_d, &append, &dwFlags);
762 _tsplitpath(sources->szFile, drive_d, dir_d, file_d, ext_d);
763 if (IsDirectory (sources->szFile))
765 // printf("C File = %s, Ext = %s\n", file_d, ext_d);
767 _tcscat (dir_d, file_d);
768 _tcscat (dir_d, ext_d);
769 file_d[0] = _T('\0');
773 ConOutPuts (sources->szFile);
775 copied = SetupCopy (sources->next, p, bMultiple, drive_d, dir_d, file_d, ext_d, &append, &dwFlags) + 1;
778 DeleteFileList (sources);
780 ConOutPrintf (_T(" %d file(s) copied\n"), copied);
784 #endif /* INCLUDE_CMD_COPY */