[XCOPY] Sync with Wine Staging 3.17. CORE-15127
authorAmine Khaldi <amine.khaldi@reactos.org>
Sun, 30 Sep 2018 23:16:20 +0000 (00:16 +0100)
committerAmine Khaldi <amine.khaldi@reactos.org>
Sun, 30 Sep 2018 23:16:20 +0000 (00:16 +0100)
28 files changed:
base/applications/cmdutils/xcopy/CMakeLists.txt
base/applications/cmdutils/xcopy/lang/da-DK.rc
base/applications/cmdutils/xcopy/lang/de-DE.rc
base/applications/cmdutils/xcopy/lang/en-US.rc
base/applications/cmdutils/xcopy/lang/es-ES.rc
base/applications/cmdutils/xcopy/lang/fr-FR.rc
base/applications/cmdutils/xcopy/lang/it-IT.rc
base/applications/cmdutils/xcopy/lang/ja-JP.rc
base/applications/cmdutils/xcopy/lang/ko-KR.rc
base/applications/cmdutils/xcopy/lang/lt-LT.rc
base/applications/cmdutils/xcopy/lang/nl-NL.rc
base/applications/cmdutils/xcopy/lang/no-NO.rc
base/applications/cmdutils/xcopy/lang/pl-PL.rc
base/applications/cmdutils/xcopy/lang/pt-BR.rc
base/applications/cmdutils/xcopy/lang/pt-PT.rc
base/applications/cmdutils/xcopy/lang/ro-RO.rc
base/applications/cmdutils/xcopy/lang/ru-RU.rc
base/applications/cmdutils/xcopy/lang/sl-SI.rc
base/applications/cmdutils/xcopy/lang/sq-AL.rc
base/applications/cmdutils/xcopy/lang/sr-SP.rc
base/applications/cmdutils/xcopy/lang/sv-SE.rc
base/applications/cmdutils/xcopy/lang/tr-TR.rc
base/applications/cmdutils/xcopy/lang/uk-UA.rc
base/applications/cmdutils/xcopy/lang/zh-CN.rc
base/applications/cmdutils/xcopy/lang/zh-TW.rc
base/applications/cmdutils/xcopy/xcopy.c
base/applications/cmdutils/xcopy/xcopy.h
media/doc/README.WINE

index 5cc5e81..f2fa0cf 100644 (file)
@@ -1,4 +1,5 @@
 
+add_definitions(-D__WINESRC__)
 add_executable(xcopy xcopy.c xcopy.rc)
 target_link_libraries(xcopy wine)
 set_module_type(xcopy win32cui UNICODE)
index 8918785..6fda70a 100644 (file)
@@ -49,6 +49,7 @@ hvor:\n\
 [/C]  Fortsæt selv om det opstår fejl under kopieringen\n\
 [/A]  Kopier ikke filer som er markeret som arkiv\n\
 [/M]  Kopier kun filer som er markeret som akriv; fjerner denne markering\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-å] Kopier kun nye filer eller dem som er ændret efter\n\
 \t\tden opgivne dato.\n\
 \t\tHvis ingen dato opgives kopieres kun de filer hvor\n\
index b149d8b..4e4e711 100644 (file)
@@ -49,6 +49,7 @@ Mit:\n\
 [/C]  Nach Fehlern den Kopiervorgang fortsetzen\n\
 [/A]  Nur Dateien mit Archivbit kopieren\n\
 [/M]  Nur Dateien mit Archivbit kopieren, danach Archivbit löschen\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:M-T-J] Kopiere neue Dateien und Dateien, die neuer als das\n\
 \t\tangegebene Datum sind. Wird kein Datum angegegebn, werden nur\n\
 \t\tQuelldateien kopiert, die neuer sind als die Zieldatei\n\n"
index 615a244..a63c838 100644 (file)
@@ -50,6 +50,7 @@ Where:\n\
 [/A]  Only copy files with archive attribute set.\n\
 [/M]  Only copy files with archive attribute set, removes the\n\
 \tarchive attribute.\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-y] Copy new files or those modified after the supplied date.\n\
 \t\tIf no date is supplied, only copy if destination is older\n\
 \t\tthan source.\n\n"
index e0997c2..1a1358e 100644 (file)
@@ -50,6 +50,7 @@ En la cual:\n\
 [/A]  Copia solamente archivos con el attributo de archivo definido\n\
 [/M]  Copia solamente archivos con el attributo de archivo definido, supprime\n\
 \tl'attributo de archivo\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-y] Copia los nuevos archivos or los que fueront modificado despues la fecha especificada.\n\
 \t\tSi la fecha no esta especificada, copia unicamente cuando el archivo de destino est mas viejo\n\
 \t\tque el archivo fuente\n\n"
index eccb0a6..de5ee1f 100644 (file)
@@ -50,6 +50,7 @@ où :\n\
 [/A]  Copie uniquement les fichiers qui ont l'attribut archive défini\n\
 [/M]  Copie uniquement les fichiers qui ont l'attribut archive défini ; supprime\n\
 \tensuite l'attribut\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-y] Copie uniquement les nouveaux fichiers, ou ceux modifiés après la date spécifiée.\n\
 \t\tSi aucune date n'est spécifiée, copie uniquement lorsque le fichier de destination est plus ancien\n\
 \t\tque le fichier source\n\n"
index 3b518ba..4ffdf54 100644 (file)
@@ -50,6 +50,7 @@ Dove:\n\
 [/A]  Copia solo i file che abbiano l'attributo Archivio\n\
 [/M]  Copia solo i file che abbiano l'attributo Archivio, rimuovendo\n\
 \tl'attributo\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-g-a] Copia i nuovi file o quelli modificati dopo la data fornita.\n\
 \t\tSe nessuna data è stata fornita, copia solo se la destinazione è più vecchia\n\
 \t\tdella sorgente\n\n"
index 7ead13b..2eec679 100644 (file)
@@ -50,6 +50,7 @@ XCOPY 送り元 [送り先] [/I] [/S] [/Q] [/F] [/L] [/W] [/T] [/N] [/U]\n\
 [/A]  アーカイブ属性のファイルだけコピーします。\n\
 [/M]  アーカイブ属性のファイルだけコピーし、アーカイブ属性を\n\
 \t取り除きます。\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:月-日-年] 新しいファイルや指定された日付以降に変更された\n\
 \t\tファイルをコピーします。日付が指定されない場合は、\n\
 \t\tコピー先がコピー元より古いファイルだけコピーします。\n\n"
index f18c603..7b7f506 100644 (file)
@@ -50,6 +50,7 @@ Where:\n\
 [/A]  오직 압축 속성이 설정되어있는 파일만 복사\n\
 [/M]  오직 압축 속성을 제거하면서  압축 속성이 설정되어있는\n\
 \t파일만 복사\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-y] 지정된 날짜 후에 수정되거나 새로운 파일 복사.\n\
 \t\tI만약 어떠한 날짜도 지정되지 않으면,오직 원본보다\n\
 \t\t대상이 오래된 것만 복사\n\n"
index 96c9a38..0be2b44 100644 (file)
@@ -50,6 +50,7 @@ Kur:\n\
 [/A]  Kopijuoti tik failus su nustatytu archyvavimo požymiu\n\
 [/M]  Kopijuoti tik failus su nustatytu archyvavimo požymiu, pašalinti\n\
 \tarchyvavimo požymį\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-y] Kopijuoti naujus failus arba modifikuotos po nurodytos datos.\n\
 \t\tJei data nenurodyta, kopijuoti tik jei paskirties failas yra\n\
 \t\tsenesnis už šaltinio failą\n\n"
index 4c1620e..db714d1 100644 (file)
@@ -50,6 +50,7 @@ Parameters:\n\
 [/A]  Kopieer alleen bestanden met archiveringsattribuut aan\n\
 [/M]  Kopieer alleen bestanden met archiveringsattribuut aan, verwijdert\n\
 \tdit archiveringsattribuut\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-y] Kopieer bestanden die nieuw zijn of gewijzigd zijn na de opgegeven\n\
 \t\tdatum. Als geen datum wordt gegeven, kopieer alleen als bron nieuwer is.\n\n"
 
index e85c45b..e051212 100644 (file)
@@ -49,6 +49,7 @@ Where:\n\
 [/C]  Fortsett selv om det oppstår feil under kopieringen\n\
 [/A]  Ikke kopier filer som er markert som arkiv\n\
 [/M]  Bare kopier filer som er markert som akriv; fjerner denne merkingen\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-å] Kopier nye filer eller de som er endret etter\n\
 \t\tden oppgitte datoen.\n\
 \t\tHvis ingen dato oppgis kopieres bare de filene som er\n\
index aec9729..f6f3059 100644 (file)
@@ -49,6 +49,7 @@ Gdzie:\n\
 [/C]  Kontynuuje nawet jeżeli podczas kopiowania wystąpiły błędy\n\
 [/A]  Kopiuje tylko pliki z atrybutem archiwalny\n\
 [/M]  Kopiuje tylko pliki z atrybutem archiwalny i usuwa ten atrybut\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-y] Kopiuje tylko nowe pliki lub te zmodifikowane po podanej dacie.\n\
 \t\tJeżeli nie podano żadnej daty, to kopiowane są pliki, które są\n\
 \t\tnowsze niż w katalogu docelowym\n\n"
index e02e0f3..3ed93b0 100644 (file)
@@ -50,6 +50,7 @@ Onde:\n\
 [/A]  Copiar apenas arquivos com atributo de arquivo\n\
 [/M]  Copiar apenas arquivos com atributo de arquivo, removendo\n\
 \to atributo de arquivo\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-y] Copiar novos arquivos ou os alterados após a data fornecida\n\
 \t\tSe nenhuma data for fornecida, apenas copiar se o destino\n\
 \t\tfor mais antigo que a fonte\n\n"
index 4a47ee2..db3fd7e 100644 (file)
@@ -50,6 +50,7 @@ Onde:\n\
 [/A]  Copiar apenas ficheiros com atributo de arquivo\n\
 [/M]  Copiar apenas ficheiros com atributo de arquivo, removendo\n\
 \to atributo de arquivo\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-y] Copiar novos ficheiros ou os alterados após a data fornecida\n\
 \t\tSe nenhuma data for fornecida, apenas copiar se o destino\n\
 \t\tfor mais antigo que a fonte\n\n"
index 5dfe61d..777e09f 100644 (file)
@@ -49,6 +49,7 @@ XCOPY sursă [destinație] [/I] [/S] [/Q] [/F] [/L] [/W] [/T] [/N] [/U]\n\
 [/A]  Copiază numai fișierele cu atributul de arhivă activat.\n\
 [/M]  Copiază numai fișierele cu atributul de arhivă activat, dezactivează\n\
 \tapoi atributul.\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-y] Copiază fișierele noi sau pe cele modificate după data\n\
 \t\tspecificată. Dacă nu este specificată nici o dată, copiază\n\
 \t\tnumai dacă fișierul destinație este mai vechi decât fișierul\n\
index 9e18b8a..72e5965 100644 (file)
@@ -52,6 +52,7 @@ XCOPY источник [целевой_объект] [/I] [/S] [/Q] [/F] [/L] [/
 /A  Копирует только те файлы, для которых установлен атрибут ""архивный"".\n\
 /M  Копирует только те файлы, для которых установлен атрибут ""архивный"",\n\
       при этом атрибут удаляется.\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 /D | /D:m-d-y Копирование файлов, измененных не ранее указанной даты.\n\
               Если дата не указана, заменяются только конечные файлы,\n\
               более старые, чем исходные.\n"
index 4673100..4759f1d 100644 (file)
@@ -50,6 +50,7 @@ Where:\n\
 [/A]  Kopiraj samo datoteke, ki imajo atribut arhiva\n\
 [/M]  Kopiraj samo datoteke, ki imajo atribut arhiva ter odstrani atribut\n\
 \tarhiva\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-y] Kopiraj datoteke, ustvarjene ali spremenjene po navedenem datumu.\n\
 \t\tČe datum ni podan, gre za kopiranje datotek, katerih obstoječi cilj je\n\
 \t\tod izvora\n\n"
index 57bef2b..ce020c1 100644 (file)
@@ -54,6 +54,7 @@ Where:\n\
 [/A]  Kopjo vetëm dokumenta me arkiva me atribjute te përcaktuar\n\
 [/M]  Kopjo vetëm dokumente me arkiva dhe atribjute te caktuar, hiqnt\n\
 \tarkivat te atribuara\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-y] Kopjo dokumentët e ri ose modifiko ato pas datës së furnizuar.\n\
 \t\tNëse asnjë datë është paraqitur, vetëm kopjoni nëse destinacioni është i vjetër\n\
 \t\tse burimi\n\n"
index e8116bf..0ba72ee 100644 (file)
@@ -50,6 +50,7 @@ XCOPY извор [dest] [/I] [/S] [/Q] [/F] [/L] [/W] [/T] [/N] [/U]\n\
 [/A]  Умножи само архивиране датотеке\n\
 [/M]  Умножи само архивиране датотеке и уклони\n\
 \tособине архиве\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-y] Умножи нове или измењене датотеке након одређеног датума.\n\
 \t\tАко датум није унесен, умножи само ако је одредиште старије\n\
 \t\tод извора\n\n"
@@ -108,6 +109,7 @@ Gde:\n\
 [/A]  Umnoži samo arhivirane datoteke\n\
 [/M]  Umnoži samo arhivirane datoteke i ukloni\n\
 \tosobine arhive\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-y] Umnoži nove ili izmenjene datoteke nakon određenog datuma.\n\
 \t\tAko datum nije unesen, umnoži samo ako je odredište starije\n\
 \t\tod izvora\n\n"
index ee929d5..015a6a0 100644 (file)
@@ -48,6 +48,7 @@ Där:\n\
 [/A]  Kopiera enbart filer markerade som arkiv\n\
 [/M]  Kopiera enbart filer markerade som arkiv, ta bort\n\
       markeringen\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-y] Kopiera nya filer eller de ändrade efter angivet datum.\n\
 \t\tOm inget datum angivits utförs endast kopiering om målet är\n\
 \t\täldre än källan\n\n"
index 132e9d8..2f5be37 100644 (file)
@@ -52,6 +52,7 @@ Nereye:\n\
 [/A]  Yalnızca belgelik öz niteliği ayarlı kütükleri çoğalt.\n\
 [/M]  Yalnızca belgelik öz niteliği ayarlı kütükleri çoğalt,\n\
 \tbelgelik öz niteliğini siler.\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-y] Yeni kütükleri veyâ onların verilen târihten sonra değiştirilmişlerini çoğalt.\n\
 \t\tBir târih verilmemişse yalnızca varış kaynaktan eskiyse çoğalt.\n\n"
 
index 22403e4..fed4922 100644 (file)
@@ -49,6 +49,7 @@ XCOPY source [dest] [/I] [/S] [/Q] [/F] [/L] [/W] [/T] [/N] [/U]\n\
 [/A]  Копіює лише файли з властивістю АРХІВНИЙ\n\
 [/M]  Копіює лише файли з властивістю АРХІВНИЙ, видаляє\n\
 \tвластивість АРХІВНИЙ\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-y] Копіює лише нові файли або ті, які були змінені після вказаної\n\
 дати. Якщо дата не вказана, копіює лише ті файли, які новіші\n\
 в початковій папці\n\n"
index a3c4d70..e779337 100644 (file)
@@ -52,6 +52,7 @@ XCOPY 源 [目标] [/I] [/S] [/Q] [/F] [/L] [/W] [/T] [/N] [/U]\n\
 [/A]  仅复制具有存档属性设置的文件。\n\
 [/M]  仅复制具有存档属性设置的文件,并删除\n\
 \t存档属性。\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-y] 复制指定日期后的新文件或新修改的文件。\n\
 \t\t如果指定没有日期,则只复制目标中比源中旧的文件。\n\n"
 
index f86f669..c7f4493 100644 (file)
@@ -52,6 +52,7 @@ XCOPY 源 [目標] [/I] [/S] [/Q] [/F] [/L] [/W] [/T] [/N] [/U]\n\
 [/A]  僅複製具有存檔屬性設定的檔案。\n\
 [/M]  僅複製檔案的存檔屬性設定,刪除\n\
 \t存檔屬性。\n\
+[/K]  Copy file attributes, without this attributes are not preserved.\n\
 [/D | /D:m-d-y] 複製新檔案或那些修改後所提供的日期。\n\
 \t\t如果提供沒有日期,則只複製如果目的地\n\
 \t\t比源。\n\n"
index a173cc1..eb8c3f0 100644 (file)
 
 /*
  * Notes:
- * Apparently, valid return codes are:
+ * Documented valid return codes are:
  *   0 - OK
- *   1 - No files found to copy
+ *   1 - No files found to copy  (*1)
  *   2 - CTRL+C during copy
  *   4 - Initialization error, or invalid source specification
  *   5 - Disk write error
+ *
+ * (*1) Testing shows return code 1 is never returned
  */
 
 
@@ -82,7 +84,7 @@ static WCHAR *XCOPY_LoadMessage(UINT id) {
     static WCHAR msg[MAXSTRING];
     const WCHAR failedMsg[]  = {'F', 'a', 'i', 'l', 'e', 'd', '!', 0};
 
-    if (!LoadStringW(GetModuleHandleW(NULL), id, msg, sizeof(msg)/sizeof(WCHAR))) {
+    if (!LoadStringW(GetModuleHandleW(NULL), id, msg, ARRAY_SIZE(msg))) {
        WINE_FIXME("LoadString failed with %d\n", GetLastError());
        lstrcpyW(msg, failedMsg);
     }
@@ -267,7 +269,7 @@ static BOOL XCOPY_ProcessExcludeFile(WCHAR* filename, WCHAR* endOfName) {
     }
 
     /* Process line by line */
-    while (fgetws(buffer, sizeof(buffer)/sizeof(WCHAR), inFile) != NULL) {
+    while (fgetws(buffer, ARRAY_SIZE(buffer), inFile) != NULL) {
         EXCLUDELIST *thisEntry;
         int length = lstrlenW(buffer);
 
@@ -568,15 +570,25 @@ static int XCOPY_DoCopy(WCHAR *srcstem, WCHAR *srcspec,
                         ret = RC_WRITEERROR;
                         goto cleanup;
                     }
-                }
+                } else {
 
-                /* If /M supplied, remove the archive bit after successful copy */
-                if (!skipFile) {
-                    if ((srcAttribs & FILE_ATTRIBUTE_ARCHIVE) &&
-                        (flags & OPT_REMOVEARCH)) {
-                        SetFileAttributesW(copyFrom, (srcAttribs & ~FILE_ATTRIBUTE_ARCHIVE));
+                    if (!skipFile) {
+                        /* If keeping attributes, update the destination attributes
+                           otherwise remove the read only attribute                 */
+                        if (flags & OPT_KEEPATTRS) {
+                            SetFileAttributesW(copyTo, srcAttribs | FILE_ATTRIBUTE_ARCHIVE);
+                        } else {
+                            SetFileAttributesW(copyTo,
+                                     (GetFileAttributesW(copyTo) & ~FILE_ATTRIBUTE_READONLY));
+                        }
+
+                        /* If /M supplied, remove the archive bit after successful copy */
+                        if ((srcAttribs & FILE_ATTRIBUTE_ARCHIVE) &&
+                            (flags & OPT_REMOVEARCH)) {
+                            SetFileAttributesW(copyFrom, (srcAttribs & ~FILE_ATTRIBUTE_ARCHIVE));
+                        }
+                        filesCopied++;
                     }
-                    filesCopied++;
                 }
             }
         }
@@ -588,6 +600,13 @@ static int XCOPY_DoCopy(WCHAR *srcstem, WCHAR *srcspec,
 
     /* Search 2 - do subdirs */
     if (flags & OPT_RECURSIVE) {
+
+        /* If /E is supplied, create the directory now */
+        if ((flags & OPT_EMPTYDIR) &&
+           !(flags & OPT_SIMULATE)) {
+            XCOPY_CreateDirectory(deststem);
+        }
+
         lstrcpyW(inputpath, srcstem);
         lstrcatW(inputpath, wchr_star);
         findres = TRUE;
@@ -611,12 +630,6 @@ static int XCOPY_DoCopy(WCHAR *srcstem, WCHAR *srcspec,
                 lstrcpyW(outputpath, deststem);
                 if (*destspec == 0x00) {
                     lstrcatW(outputpath, finddata->cFileName);
-
-                    /* If /E is supplied, create the directory now */
-                    if ((flags & OPT_EMPTYDIR) &&
-                        !(flags & OPT_SIMULATE))
-                        XCOPY_CreateDirectory(outputpath);
-
                     lstrcatW(outputpath, wchr_slash);
                 }
 
@@ -743,101 +756,134 @@ static int XCOPY_ParseCommandLine(WCHAR *suppliedsource,
                  Note: Windows docs say /P prompts when dest is created
                        but tests show it is done for each src file
                        regardless of the destination                   */
-            switch (toupper(word[1])) {
-            case 'I': flags |= OPT_ASSUMEDIR;     break;
-            case 'S': flags |= OPT_RECURSIVE;     break;
-            case 'Q': flags |= OPT_QUIET;         break;
-            case 'F': flags |= OPT_FULL;          break;
-            case 'L': flags |= OPT_SIMULATE;      break;
-            case 'W': flags |= OPT_PAUSE;         break;
-            case 'T': flags |= OPT_NOCOPY | OPT_RECURSIVE; break;
-            case 'Y': flags |= OPT_NOPROMPT;      break;
-            case 'N': flags |= OPT_SHORTNAME;     break;
-            case 'U': flags |= OPT_MUSTEXIST;     break;
-            case 'R': flags |= OPT_REPLACEREAD;   break;
-            case 'H': flags |= OPT_COPYHIDSYS;    break;
-            case 'C': flags |= OPT_IGNOREERRORS;  break;
-            case 'P': flags |= OPT_SRCPROMPT;     break;
-            case 'A': flags |= OPT_ARCHIVEONLY;   break;
-            case 'M': flags |= OPT_ARCHIVEONLY |
-                               OPT_REMOVEARCH;    break;
-
-            /* E can be /E or /EXCLUDE */
-            case 'E': if (CompareStringW(LOCALE_USER_DEFAULT,
-                                         NORM_IGNORECASE | SORT_STRINGSORT,
-                                         &word[1], 8,
-                                         EXCLUDE, -1) == CSTR_EQUAL) {
-                        if (XCOPY_ProcessExcludeList(&word[9])) {
-                          XCOPY_FailMessage(ERROR_INVALID_PARAMETER);
-                          goto out;
-                        } else flags |= OPT_EXCLUDELIST;
-                      } else flags |= OPT_EMPTYDIR | OPT_RECURSIVE;
-                      break;
-
-            /* D can be /D or /D: */
-            case 'D': if (word[2]==':' && is_digit(word[3])) {
-                          SYSTEMTIME st;
-                          WCHAR     *pos = &word[3];
-                          BOOL       isError = FALSE;
-                          memset(&st, 0x00, sizeof(st));
-
-                          /* Microsoft xcopy's usage message implies that the date
-                           * format depends on the locale, but that is false.
-                           * It is hardcoded to month-day-year.
-                           */
-                          st.wMonth = _wtol(pos);
-                          while (*pos && is_digit(*pos)) pos++;
-                          if (*pos++ != '-') isError = TRUE;
-
-                          if (!isError) {
-                              st.wDay = _wtol(pos);
-                              while (*pos && is_digit(*pos)) pos++;
-                              if (*pos++ != '-') isError = TRUE;
-                          }
+            int skip=0;
+            WCHAR *rest;
+
+            while (word[0]) {
+                rest = NULL;
+
+                switch (toupper(word[1])) {
+                case 'I': flags |= OPT_ASSUMEDIR;     break;
+                case 'S': flags |= OPT_RECURSIVE;     break;
+                case 'Q': flags |= OPT_QUIET;         break;
+                case 'F': flags |= OPT_FULL;          break;
+                case 'L': flags |= OPT_SIMULATE;      break;
+                case 'W': flags |= OPT_PAUSE;         break;
+                case 'T': flags |= OPT_NOCOPY | OPT_RECURSIVE; break;
+                case 'Y': flags |= OPT_NOPROMPT;      break;
+                case 'N': flags |= OPT_SHORTNAME;     break;
+                case 'U': flags |= OPT_MUSTEXIST;     break;
+                case 'R': flags |= OPT_REPLACEREAD;   break;
+                case 'K': flags |= OPT_KEEPATTRS;     break;
+                case 'H': flags |= OPT_COPYHIDSYS;    break;
+                case 'C': flags |= OPT_IGNOREERRORS;  break;
+                case 'P': flags |= OPT_SRCPROMPT;     break;
+                case 'A': flags |= OPT_ARCHIVEONLY;   break;
+                case 'M': flags |= OPT_ARCHIVEONLY |
+                                   OPT_REMOVEARCH;    break;
+
+                /* E can be /E or /EXCLUDE */
+                case 'E': if (CompareStringW(LOCALE_USER_DEFAULT,
+                                             NORM_IGNORECASE | SORT_STRINGSORT,
+                                             &word[1], 8,
+                                             EXCLUDE, -1) == CSTR_EQUAL) {
+                            if (XCOPY_ProcessExcludeList(&word[9])) {
+                              XCOPY_FailMessage(ERROR_INVALID_PARAMETER);
+                              goto out;
+                            } else {
+                              flags |= OPT_EXCLUDELIST;
 
-                          if (!isError) {
-                              st.wYear = _wtol(pos);
-                              while (*pos && is_digit(*pos)) pos++;
-                              if (st.wYear < 100) st.wYear+=2000;
+                              /* Do not support concatenated switches onto exclude lists yet */
+                              rest = end;
+                            }
+                          } else {
+                              flags |= OPT_EMPTYDIR | OPT_RECURSIVE;
                           }
+                          break;
 
-                          if (!isError && SystemTimeToFileTime(&st, &dateRange)) {
+                /* D can be /D or /D: */
+                case 'D': if (word[2]==':' && is_digit(word[3])) {
                               SYSTEMTIME st;
-                              WCHAR datestring[32], timestring[32];
-
-                              flags |= OPT_DATERANGE;
-
-                              /* Debug info: */
-                              FileTimeToSystemTime (&dateRange, &st);
-                              GetDateFormatW(0, DATE_SHORTDATE, &st, NULL, datestring,
-                                             sizeof(datestring)/sizeof(WCHAR));
-                              GetTimeFormatW(0, TIME_NOSECONDS, &st,
-                                             NULL, timestring, sizeof(timestring)/sizeof(WCHAR));
+                              WCHAR     *pos = &word[3];
+                              BOOL       isError = FALSE;
+                              memset(&st, 0x00, sizeof(st));
+
+                              /* Microsoft xcopy's usage message implies that the date
+                               * format depends on the locale, but that is false.
+                               * It is hardcoded to month-day-year.
+                               */
+                              st.wMonth = _wtol(pos);
+                              while (*pos && is_digit(*pos)) pos++;
+                              if (*pos++ != '-') isError = TRUE;
 
-                              WINE_TRACE("Date being used is: %s %s\n",
-                                         wine_dbgstr_w(datestring), wine_dbgstr_w(timestring));
+                              if (!isError) {
+                                  st.wDay = _wtol(pos);
+                                  while (*pos && is_digit(*pos)) pos++;
+                                  if (*pos++ != '-') isError = TRUE;
+                              }
+
+                              if (!isError) {
+                                  st.wYear = _wtol(pos);
+                                  while (*pos && is_digit(*pos)) pos++;
+                                  if (st.wYear < 100) st.wYear+=2000;
+                              }
+
+                              /* Handle switches straight after the supplied date */
+                              rest = pos;
+
+                              if (!isError && SystemTimeToFileTime(&st, &dateRange)) {
+                                  SYSTEMTIME st;
+                                  WCHAR datestring[32], timestring[32];
+
+                                  flags |= OPT_DATERANGE;
+
+                                  /* Debug info: */
+                                  FileTimeToSystemTime (&dateRange, &st);
+                                  GetDateFormatW(0, DATE_SHORTDATE, &st, NULL, datestring,
+                                                 ARRAY_SIZE(datestring));
+                                  GetTimeFormatW(0, TIME_NOSECONDS, &st,
+                                                 NULL, timestring, ARRAY_SIZE(timestring));
+
+                                  WINE_TRACE("Date being used is: %s %s\n",
+                                             wine_dbgstr_w(datestring), wine_dbgstr_w(timestring));
+                              } else {
+                                  XCOPY_FailMessage(ERROR_INVALID_PARAMETER);
+                                  goto out;
+                              }
                           } else {
-                              XCOPY_FailMessage(ERROR_INVALID_PARAMETER);
-                              goto out;
+                              flags |= OPT_DATENEWER;
                           }
-                      } else {
-                          flags |= OPT_DATENEWER;
-                      }
-                      break;
-
-            case '-': if (toupper(word[2])=='Y')
-                          flags &= ~OPT_NOPROMPT;
-                      break;
-            case '?': XCOPY_wprintf(XCOPY_LoadMessage(STRING_HELP));
-                      rc = RC_HELP;
-                      goto out;
-            case 'V':
-                WINE_FIXME("ignoring /V\n");
-                break;
-            default:
-                WINE_TRACE("Unhandled parameter '%s'\n", wine_dbgstr_w(word));
-                XCOPY_wprintf(XCOPY_LoadMessage(STRING_INVPARM), word);
-                goto out;
+                          break;
+
+                case '-': if (toupper(word[2])=='Y') {
+                              flags &= ~OPT_NOPROMPT;
+                              rest = &word[3];  /* Skip over 3 characters */
+                          }
+                          break;
+                case '?': XCOPY_wprintf(XCOPY_LoadMessage(STRING_HELP));
+                          rc = RC_HELP;
+                          goto out;
+                case 'V':
+                    WINE_FIXME("ignoring /V\n");
+                    break;
+                default:
+                    WINE_TRACE("Unhandled parameter '%s'\n", wine_dbgstr_w(word));
+                    XCOPY_wprintf(XCOPY_LoadMessage(STRING_INVPARM), word);
+                    goto out;
+                }
+
+                /* Unless overridden above, skip over the '/' and the first character */
+                if (rest == NULL) rest = &word[2];
+
+                /* By now, rest should point either to the null after the
+                   switch, or the beginning of the next switch if there
+                   was no whitespace between them                          */
+                if (!skip && *rest && *rest != '/') {
+                    WINE_FIXME("Unexpected characters found and ignored '%s'\n", wine_dbgstr_w(rest));
+                    skip=1;
+                } else {
+                    word = rest;
+                }
             }
         }
         word = next;
@@ -941,7 +987,7 @@ static int XCOPY_ProcessSourceParm(WCHAR *suppliedsource, WCHAR *stem,
             lstrcpyW(spec, suppliedsource+2);
         } else {
             WCHAR curdir[MAXSTRING];
-            GetCurrentDirectoryW(sizeof(curdir)/sizeof(WCHAR), curdir);
+            GetCurrentDirectoryW(ARRAY_SIZE(curdir), curdir);
             stem[0] = curdir[0];
             stem[1] = curdir[1];
             stem[2] = 0x00;
@@ -1133,7 +1179,6 @@ int wmain (int argc, WCHAR *argvW[])
     } else if (!(flags & OPT_NOCOPY)) {
         XCOPY_wprintf(XCOPY_LoadMessage(STRING_COPY), filesCopied);
     }
-    if (rc == RC_OK && filesCopied == 0) rc = RC_NOFILES;
     return rc;
 
 }
index d18f52c..8e41bb7 100644 (file)
@@ -50,6 +50,7 @@
 #define OPT_EXCLUDELIST  0x00020000
 #define OPT_DATERANGE    0x00040000
 #define OPT_DATENEWER    0x00080000
+#define OPT_KEEPATTRS    0x00100000
 
 #define MAXSTRING 8192
 
index 487a218..3cc9cff 100644 (file)
@@ -232,7 +232,7 @@ reactos/base/applications/cmdutils/schtasks # Synced to WineStaging-3.3
 reactos/base/applications/cmdutils/taskkill # Synced to WineStaging-3.17
 reactos/base/applications/cmdutils/wmic     # Synced to WineStaging-3.17
 reactos/base/applications/cmdutils/wscript  # Synced to WineStaging-3.17
-reactos/base/applications/cmdutils/xcopy    # Synced to WineStaging-3.3
+reactos/base/applications/cmdutils/xcopy    # Synced to WineStaging-3.17
 reactos/base/applications/games/winmine     # Synced to WineStaging-2.16 with our own resources.
 reactos/base/applications/extrac32          # Synced to WineStaging-3.3
 reactos/base/applications/iexplore          # Synced to WineStaging-3.3