Wine imported xcopy command line tool
authorMarc Piulachs <marc.piulachs@live.com>
Mon, 22 Oct 2007 19:55:26 +0000 (19:55 +0000)
committerMarc Piulachs <marc.piulachs@live.com>
Mon, 22 Oct 2007 19:55:26 +0000 (19:55 +0000)
svn path=/trunk/; revision=29794

reactos/base/applications/cmdutils/cmdutils.rbuild
reactos/base/applications/cmdutils/xcopy/En.rc [new file with mode: 0644]
reactos/base/applications/cmdutils/xcopy/Ko.rc [new file with mode: 0644]
reactos/base/applications/cmdutils/xcopy/Pl.rc [new file with mode: 0644]
reactos/base/applications/cmdutils/xcopy/Ru.rc [new file with mode: 0644]
reactos/base/applications/cmdutils/xcopy/rsrc.rc [new file with mode: 0644]
reactos/base/applications/cmdutils/xcopy/xcopy.c [new file with mode: 0644]
reactos/base/applications/cmdutils/xcopy/xcopy.h [new file with mode: 0644]
reactos/base/applications/cmdutils/xcopy/xcopy.rbuild [new file with mode: 0644]
reactos/base/applications/cmdutils/xcopy/xcopy.rc [new file with mode: 0644]

index 8eaafc5..9a53d97 100644 (file)
@@ -10,4 +10,7 @@
        <directory name="more">
                <xi:include href="more/more.rbuild" />
        </directory>
+       <directory name="xcopy">
+               <xi:include href="xcopy/xcopy.rbuild" />
+       </directory>
 </group>
diff --git a/reactos/base/applications/cmdutils/xcopy/En.rc b/reactos/base/applications/cmdutils/xcopy/En.rc
new file mode 100644 (file)
index 0000000..d7d4344
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * XCOPY - Wine-compatible xcopy program
+ * English language support
+ *
+ * Copyright (C) 2007 J. Edmeades
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+
+LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
+
+STRINGTABLE
+{
+    STRING_INVPARMS, "Invalid number of parameters - Use xcopy /? for help\n"
+    STRING_INVPARM,  "Invalid parameter '%s' -  Use xcopy /? for help\n"
+    STRING_PAUSE,    "Press <enter> to begin copying\n"
+    STRING_SIMCOPY,  "%d file(s) would be copied\n"
+    STRING_COPY,     "%d file(s) copied\n"
+    STRING_QISDIR,   "Is '%s' a filename or directory\n" \
+                     "on the target?\n" \
+                     "(F - File, D - Directory)\n"
+    STRING_SRCPROMPT,"%s? (Yes|No)\n"
+    STRING_OVERWRITE,"Overwrite %s? (Yes|No|All)\n"
+    STRING_COPYFAIL, "Copying of '%s' to '%s' failed with r/c %d\n"
+    STRING_OPENFAIL, "Failed to open '%s'\n"
+    STRING_READFAIL, "Failed during reading of '%s'\n"
+    STRING_YES_CHAR, "Y"
+    STRING_NO_CHAR,  "N"
+    STRING_ALL_CHAR, "A"
+    STRING_FILE_CHAR,"F"
+    STRING_DIR_CHAR, "D"
+
+    STRING_HELP,
+"XCOPY - Copies source files or directory trees to a destination\n\
+\n\
+Syntax:\n\
+XCOPY source [dest] [/I] [/S] [/Q] [/F] [/L] [/W] [/T] [/N] [/U] \n\
+\t     [/R] [/H] [/C] [/P] [/A] [/M] [/E] [/D] [/Y] [/-Y]\n\
+\n\
+Where:\n\
+\n\
+[/I]  Assume directory if destination does not exist and copying 2 or \n\
+\tmore files\n\
+[/S]  Copy directories and subdirectories\n\
+[/E]  Copy directories and subdirectories, including any empty ones\n\
+[/Q]  Do not list names during copy, ie quiet.\n\
+[/F]  Show full source and destination names during copy\n\
+[/L]  Simulate operation, showing names which would be copied\n\
+[/W]  Prompts before beginning the copy operation\n\
+[/T]  Creates empty directory structure but does not copy files\n\
+[/Y]  Suppress prompting when overwriting files\n\
+[/-Y] Enable prompting when overwriting files\n\
+[/P]  Prompts on each source file before copying\n\
+[/N]  Copy using short names\n\
+[/U]  Copy only files which already exist in destination\n\
+[/R]  Overwrite any read only files\n\
+[/H]  Include hidden and system files in the copy\n\
+[/C]  Continue even if an error occurs during the copy\n\
+[/A]  Only copy files with archive attribute set\n\
+[/M]  Only copy files with archive attribute set, removes \n\
+\tarchive attribute\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"
+
+}
diff --git a/reactos/base/applications/cmdutils/xcopy/Ko.rc b/reactos/base/applications/cmdutils/xcopy/Ko.rc
new file mode 100644 (file)
index 0000000..843be88
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * XCOPY - Wine-compatible xcopy program
+ * Korean language support
+ *
+ * Copyright (C) 2007 J. Edmeades
+ * Copyright (C) 2007 YunSong Hwang(hys545@dreamwiz.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+
+LANGUAGE LANG_KOREAN, SUBLANG_DEFAULT
+
+STRINGTABLE
+{
+    STRING_INVPARMS, "¿Ã¹Ù¸£Áö ¾ÊÀº ¸Å°³º¯¼öÀÇ °¹¼ö - xcopy /?·Î µµ¿ò¸»À» º¸½Ã¿À\n"
+    STRING_INVPARM,  "¿Ã¹Ù¸£Áö ¾ÊÀº ¸Å°³º¯¼ö '%s' -  xcopy /?·Î µµ¿ò¸»À» º¸½Ã¿À\n"
+    STRING_PAUSE,    "<enter> ¸¦ ´©¸£¸é º¹»ç°¡ ½ÃÀ۵ɠ°ÍÀÔ´Ï´Ù\n"
+    STRING_SIMCOPY,  "%d ÆÄÀÏÀÌ º¹»çµÉ °ÍÀÔ´Ï´Ù\n"
+    STRING_COPY,     "%d ÆÄÀÏÀÌ º¹»çµÇ¾ú½À´Ï´Ù\n"
+    STRING_QISDIR,   "'%s'ÀÌ º¹»çÇÒ ÆÄÀÏÀ̳ª µð·ºÅ丮?\n" \
+                     "ÀԴϱî?\n" \
+                     "(F - ÆÄÀÏ, D - µð·ºÅ丮)\n"
+    STRING_SRCPROMPT,"%s? (¿¹|¾Æ´Ï¿À)\n"
+    STRING_OVERWRITE,"%s¸¦ µ¤¾î¾²°Ú½À´Ï±î? (¿¹|¾Æ´Ï¿À|¸ðµÎ)\n"
+    STRING_COPYFAIL, "Copying of '%s' to '%s' failed with r/c %d\n"
+    STRING_OPENFAIL, "'%s' ¿­±â ½ÇÆÐ\n"
+    STRING_READFAIL, "'%s¸¦ ÀÐÁö ¸øÇß½À´Ï´Ù'\n"
+    STRING_YES_CHAR, "Y"
+    STRING_NO_CHAR,  "N"
+    STRING_ALL_CHAR, "A"
+    STRING_FILE_CHAR,"F"
+    STRING_DIR_CHAR, "D"
+
+    STRING_HELP,
+"XCOPY - ¿øº» ÆÄÀÏÀ̳ª µð·ºÅ丮 ±¸Á¶¸¦ ¸ñÀûÁö·Î º¹»ç\n\
+\n\
+¹®¹ý:\n\
+XCOPY ¿øº» [´ë»ó] [/I] [/S] [/Q] [/F] [/L] [/W] [/T] [/N] [/U] \n\
+\t     [/R] [/H] [/C] [/P] [/A] [/M] [/E] [/D] [/Y] [/-Y]\n\
+\n\
+Where:\n\
+\n\
+[/I]  Assume directory if destination does not exist and copying 2 or \n\
+\tmore files\n\
+[/S]  µð·ºÅ丮ÇÏ°í ÇÏÀ§ µð·ºÅ丮 º¹»ç\n\
+[/E]  ºó µð·ºÅ丮¸¦ Æ÷ÇÔÇؼ­ µð·ºÅ丮¿Í ÇÏÀ§ µð·ºÅ丮 º¹»ç\n\
+[/Q]  Á¶¿ëÇÏ°Ô º¹»çµÇ´Â ÆÄÀÏÀ̳ª µð·ºÅ丮¸¦ Ç¥½ÃÇÏÁö ¾Ê°í º¹»ç.\n\
+[/F]  º¹»çÇϴ µ¿¾È ¿ÏÀüÇÑ ¿øº»°ú ´ë»ó º¸¿©ÁÖ±â\n\
+[/L]  º¹»çµÉ °ÍÀ» º¸¿©Áָ鼭 °¡»óÀ¸·Î ÀÛ¾÷\n\
+[/W]  º¹»ç½ÃÀÛ Çϱâ Àü¿¡ È®ÀÎÇϱâ\n\
+[/T]  ÆÄÀÏÀº º¹»çÇÏÁö ¾Ê°í ºó µð·ºÅ丮 ±¸Á¶¸¸ º¹»ç\n\
+[/Y]  ÆÄÀÏ µ¤¾î ¾µ ¶§ È®ÀÎÇÏÁö ¾Ê±â\n\
+[/-Y] ÆÄÀÏÀ» µ¤¾î ¾µ ¶§ È®ÀÎÇϱâ\n\
+[/P]  º¹»çÇϴ µ¿¾È¿¡ °¢°¡ÀÇ ¿øº» ÆÄÀϸ¶´Ù È®ÀÎ\n\
+[/N]  ÂªÀº À̸§À» »ç¿ëÇؼ­ º¹»ç\n\
+[/U]  À̹̠´ë»ó µð·ºÅ丮¿¡ Á¸ÀçÇϴ ÆÄÀϸ¸ º¹»ç\n\
+[/R]  Àбâ Àü¿ë ÆÄÀϵµ µ¤¾î ¾²±â\n\
+[/H]  ¼ûÀº ÆÄÀÏÀ̳ª ½Ã½ºÅÛ ÆÄÀϵµ Æ÷ÇÔÇؼ­ º¹»ç\n\
+[/C]  º¹»çÇϴ µ¿¾È¿¡ ¿¡·¯°¡ ¹ß»ýÇصµ °è¼Ó ÁøÇà\n\
+[/A]  ¿ÀÁ÷ ¾ÐÃà ¼Ó¼ºÀÌ ¼³Á¤µÇ¾îÀִ ÆÄÀϸ¸ º¹»ç\n\
+[/M]  ¿ÀÁ÷ ¾ÐÃà ¼Ó¼ºÀ» Á¦°ÅÇϸ鼭  ¾ÐÃà ¼Ó¼ºÀÌ ¼³Á¤µÇ¾îÀִ  \n\
+\ÆÄÀϸ¸ º¹»ç \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"
+
+}
diff --git a/reactos/base/applications/cmdutils/xcopy/Pl.rc b/reactos/base/applications/cmdutils/xcopy/Pl.rc
new file mode 100644 (file)
index 0000000..10176aa
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * XCOPY - Wine-compatible xcopy program
+ * Polish language support
+ *
+ * Copyright (C) 2007 J. Edmeades
+ * Copyright (C) 2007 Mikolaj Zalewski
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+
+LANGUAGE LANG_POLISH, SUBLANG_DEFAULT
+
+STRINGTABLE
+{
+    STRING_INVPARMS, "Niew³a\9cciwa liczba parametrów - uruchom xcopy /? aby wy\9cwietliæ pomoc\n"
+    STRING_INVPARM,  "Nieznany parameter '%s' -  uruchom xcopy /? aby wy\9cwietliæ pomoc\n"
+    STRING_PAUSE,    "Naci\9cnij <enter> aby rozpocz¹æ kopiowanie\n"
+    STRING_SIMCOPY,  "%d plik(ów) zosta³oby skopiowanych\n"
+    STRING_COPY,     "%d plik(ów) skopiowanych\n"
+    STRING_QISDIR,   "Czy '%s' jest nazw¹ pliku czy katalogu\n" \
+                     "docelowego?\n" \
+                     "(P - plik, K - katalog)\n"
+    STRING_SRCPROMPT,"%s? (Tak|Nie)\n"
+    STRING_OVERWRITE,"Zast¹piæ %s? (Tak|Nie|Wszystkie)\n"
+    STRING_COPYFAIL, "Kopiowanie '%s' do '%s' nie powiod³o siê - kod b³êdu %d\n"
+    STRING_OPENFAIL, "Nie uda³o siê otworzyæ '%s'\n"
+    STRING_READFAIL, "B³¹d podczas czytania '%s'\n"
+    STRING_YES_CHAR, "T"
+    STRING_NO_CHAR,  "N"
+    STRING_ALL_CHAR, "W"
+    STRING_FILE_CHAR,"P"
+    STRING_DIR_CHAR, "K"
+
+    STRING_HELP,
+"XCOPY - kopiuje pliki lub drzewa katalogów\n\
+\n\
+Sk³adnia:\n\
+XCOPY \9fród³o [cel] [/I] [/S] [/Q] [/F] [/L] [/W] [/T] [/N] [/U] \n\
+\t     [/R] [/H] [/C] [/P] [/A] [/M] [/E] [/D] [/Y] [/-Y]\n\
+\n\
+Gdzie:\n\
+\n\
+[/I]  Je¿eli \"cel\" nie istnieje i kopiowane s¹ co najmniej dwa pliki,\n\
+\tzak³ada, ¿e \"cel\" powien byæ katalogiem\n\
+[/S]  Kopiuje katalogi i podkatalogi\n\
+[/E]  Kopiuje katalogi i podkatalogi, ³¹cznie z pustymi\n\
+[/Q]  Nie wypisuje nazw plików podczas kopiowania (tryb cichy)\n\
+[/F]  Wypisuje pe³ne \9ccie¿ki \9fród³owe i docelowe podczas kopiowania\n\
+[/L]  Jedynie symuluje operacjê, pokazuj¹c pliki, które by³yby kopiowane\n\
+[/W]  Prosi o potwierdzenie przed rozpoczêciem kopiowania\n\
+[/T]  Tworzy puste katalogi, ale nie kopiuje plików\n\
+[/Y]  Zastêpuje pliki bez pro\9cby o potwierdzenie\n\
+[/-Y] Zawsze prosi o potwierdzenie przed zast¹pieniem pliku\n\
+[/P]  Prosi o potwierdzenie przed skopiowaniem ka¿dego pliku\n\
+[/N]  Kopiuje u¿ywaj¹c krótkich nazw plików\n\
+[/U]  Kopiuje tylko pliki, które ju¿ istniej¹ w miejscu docelowym\n\
+[/R]  Zastêpuje pliki tylko do odczytu\n\
+[/H]  Kopiuje równie¿ pliki ukryte i systemowe\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\
+[/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"
+
+}
diff --git a/reactos/base/applications/cmdutils/xcopy/Ru.rc b/reactos/base/applications/cmdutils/xcopy/Ru.rc
new file mode 100644 (file)
index 0000000..9c4d4e2
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * XCOPY - Wine-compatible xcopy program
+ * Russian language support
+ *
+ * Copyright (C) 2007 J. Edmeades
+ * Copyright (C) 2007 Kirill K. Smirnov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+
+LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
+
+STRINGTABLE
+{
+    STRING_INVPARMS,
+"Íåäîïóñòèìîå ÷èñëî ïàðàìåòðîâ - ïîïðîáóéòå 'xcopy /?' äëÿ ïîëó÷åíèÿ\n\
+ïîäðîáíîãî îïèñàíèÿ.\n"
+    STRING_INVPARM,
+"Íåäîïóñòèìûé ïàðàìåòð '%s' - ïîïðîáóéòå 'xcopy /?' äëÿ ïîëó÷åíèÿ ïîäðîáíîãî\n\
+îïèñàíèÿ.\n"
+    STRING_PAUSE,    "Íàæìèòå êëàâèøó <enter>, ÷òîáû íà÷àòü êîïèðîâàíèå.\n"
+    STRING_SIMCOPY,  "%d ôàéë(îâ) áûëî áû ñêîïèðîâàíî.\n"
+    STRING_COPY,     "%d ôàéë(îâ) ñêîïèðîâàíî.\n"
+    STRING_QISDIR,   "'%s' ÿâëÿåòñÿ ôàéëîì èëè ïàïêîé?\n" \
+                     "(F - Ôàéë, D - Ïàïêà)\n"
+    STRING_SRCPROMPT,"%s? (Yes|No)\n"
+    STRING_OVERWRITE,"Ïåðåïèñàòü %s? (Yes|No|All)\n"
+    STRING_COPYFAIL, "Ïðè êîïèðîâàíèè '%s' â '%s' ïðîèçîøëà îøèáêà: %d\n"
+    STRING_OPENFAIL, "Íåâîçìîæíî îòêðûòü '%s'\n"
+    STRING_READFAIL, "Ïðè ÷òåíèè '%s' ïðîèçîøëà îøèáêà\n"
+    STRING_YES_CHAR, "Y"
+    STRING_NO_CHAR,  "N"
+    STRING_ALL_CHAR, "A"
+    STRING_FILE_CHAR,"F"
+    STRING_DIR_CHAR, "D"
+
+    STRING_HELP,
+"XCOPY - Êîïèðóåò ôàéëû è äåðåâüÿ ïàïîê\n\
+\n\
+Ñèíòàêñèñ:\n\
+XCOPY source [dest] [/I] [/S] [/Q] [/F] [/L] [/W] [/T] [/N] [/U] \n\
+\t     [/R] [/H] [/C] [/P] [/A] [/M] [/E] [/D] [/Y] [/-Y]\n\
+\n\
+Ãäå:\n\
+\n\
+[/I]  Åñëè êîíå÷íàÿ ïàïêà îòñóòñòâóåò è êîïèðóåòñÿ áîëåå îäíîãî ôàéëà, \n\
+      òî ïðåäïîëàãàåòñÿ ïàïêà â êà÷åñòâå ìåñòà íàçíà÷åíèÿ.\n\
+[/S]  Êîïèðóåò ïàïêè è ïîäïàïêè.\n\
+[/E]  Êîïèðóåò ïàïêè è ïîäïàïêè, âêëþ÷àÿ ïóñòûå.\n\
+[/Q]  Íå îòîáðàæàåò èìåíà êîïèðóåìûõ ôàéëîâ.\n\
+[/F]  Îòîáðàæàåò ïîëíûå èìåíà èñõîäíûõ è êîíå÷íûõ ôàéëîâ.\n\
+[/L]  Âûâîäèò ñïèñîê ôàéëîâ, êîòîðûå áóäóò ñêîïèðîâàíû.\n\
+[/W]  Çàïðàøèâàåò ïîäòâåðæäåíèå ïåðåä íà÷àëîì êîïèðîâàíèÿ.\n\
+[/T]  Ñîçäàåò ñòðóêòóðó ïàïîê, íî íå êîïèðóåò ôàéëû.\n\
+[/Y]  Ïîäàâëÿåò çàïðîñ íà ïîäòâåðæäåíèå ïåðåçàïèñè ôàéëîâ.\n\
+[/-Y] Çàïðàøèâàåò ïîäòâåðæäåíèå íà ïåðåçàïèñü ôàéëîâ.\n\
+[/P]  Çàïðàøèâàåò ïîäòâåðæäåíèå äëÿ êàæäîãî êîïèðóåìîãî ôàéëà.\n\
+[/N]  Èñïîëüçóåò êîðîòêèå èìåíà ôàéëîâ ïðè êîïèðîâàíèè.\n\
+[/U]  Êîïèðóåò òîëüêî òå ôàéëû, êîòîðûå óæå ñóùåñòâóþò â êîíå÷íîé ïàïêå.\n\
+[/R]  Ïåðåçàïèñûâàåò ôàéëû, äîñòóïíûå òîëüêî äëÿ ÷òåíèÿ.\n\
+[/H]  Êîïèðóåò ñêðûòûå è ñèñòåìíûå ôàéëû.\n\
+[/C]  Ïðîäîëæàåò ðàáîòó, äàæå åñëè ïðîèçîøëà îøèáêà.\n\
+[/A]  Êîïèðóåò òîëüêî òå ôàéëû, äëÿ êîòîðûõ óñòàíîâëåí àòðèáóò \"àðõèâíûé\". \n\
+[/M]  Êîïèðóåò òîëüêî òå ôàéëû, äëÿ êîòîðûõ óñòàíîâëåí àòðèáóò \"àðõèâíûé\",\n\
+      ïðè ýòîì àòðèáóò óäàëÿåòñÿ.\n\
+[/D | /D:m-d-y] Êîïèðóåò òîëüêî íîâûå ôàéëû èëè òå, êîòîðûå áûëè èçìåíåíû\n\
+                ïîñëå óêàçàííîé äàòû. Åñëè äàòà íå óêàçàíà, êîïèðóåò òîëüêî\n\
+                òå ôàéëû, êîòîðûå íîâåå â èñõîäíîé ïàïêå.\n"
+}
diff --git a/reactos/base/applications/cmdutils/xcopy/rsrc.rc b/reactos/base/applications/cmdutils/xcopy/rsrc.rc
new file mode 100644 (file)
index 0000000..234a4cb
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2007 Jason Edmeades
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <windef.h>
+
+#include "xcopy.h"
+
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+
+#include "En.rc"
+#include "Ko.rc"
+#include "Pl.rc"
+#include "Ru.rc"
diff --git a/reactos/base/applications/cmdutils/xcopy/xcopy.c b/reactos/base/applications/cmdutils/xcopy/xcopy.c
new file mode 100644 (file)
index 0000000..a88a0ad
--- /dev/null
@@ -0,0 +1,1041 @@
+/*
+ * XCOPY - Wine-compatible xcopy program
+ *
+ * Copyright (C) 2007 J. Edmeades
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/*
+ * FIXME:
+ * This should now support all options listed in the xcopy help from
+ * windows XP except:
+ *  /Z - Copy from network drives in restartable mode
+ *  /X - Copy file audit settings (sets /O)
+ *  /O - Copy file ownership + ACL info
+ *  /G - Copy encrypted files to unencrypted destination
+ *  /V - Verifies files
+ */
+
+/*
+ * Notes:
+ * Apparently, valid return codes are:
+ *   0 - OK
+ *   1 - No files found to copy
+ *   2 - CTRL+C during copy
+ *   4 - Initialization error, or invalid source specification
+ *   5 - Disk write error
+ */
+
+
+#include <stdio.h>
+#include <windows.h>
+#include <wine/debug.h>
+#include "xcopy.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(xcopy);
+
+/* Prototypes */
+static int XCOPY_ProcessSourceParm(WCHAR *suppliedsource, WCHAR *stem,
+                                   WCHAR *spec, DWORD flags);
+static int XCOPY_ProcessDestParm(WCHAR *supplieddestination, WCHAR *stem,
+                                 WCHAR *spec, WCHAR *srcspec, DWORD flags);
+static int XCOPY_DoCopy(WCHAR *srcstem, WCHAR *srcspec,
+                        WCHAR *deststem, WCHAR *destspec,
+                        DWORD flags);
+static BOOL XCOPY_CreateDirectory(const WCHAR* path);
+static BOOL XCOPY_ProcessExcludeList(WCHAR* parms);
+static BOOL XCOPY_ProcessExcludeFile(WCHAR* filename, WCHAR* endOfName);
+static WCHAR *XCOPY_LoadMessage(UINT id);
+static void XCOPY_FailMessage(DWORD err);
+static int XCOPY_wprintf(const WCHAR *format, ...);
+
+/* Typedefs */
+typedef struct _EXCLUDELIST
+{
+  struct _EXCLUDELIST *next;
+  WCHAR               *name;
+} EXCLUDELIST;
+
+
+/* Global variables */
+static ULONG filesCopied           = 0;              /* Number of files copied  */
+static EXCLUDELIST *excludeList    = NULL;           /* Excluded strings list   */
+static FILETIME dateRange;                           /* Date range to copy after*/
+static const WCHAR wchr_slash[]   = {'\\', 0};
+static const WCHAR wchr_star[]    = {'*', 0};
+static const WCHAR wchr_dot[]     = {'.', 0};
+static const WCHAR wchr_dotdot[]  = {'.', '.', 0};
+
+/* Constants (Mostly for widechars) */
+
+
+/* To minimize stack usage during recursion, some temporary variables
+   made global                                                        */
+static WCHAR copyFrom[MAX_PATH];
+static WCHAR copyTo[MAX_PATH];
+
+
+/* =========================================================================
+   main - Main entrypoint for the xcopy command
+
+     Processes the args, and drives the actual copying
+   ========================================================================= */
+int wmain (int argc, WCHAR *argvW[])
+{
+    int     rc = 0;
+    WCHAR   suppliedsource[MAX_PATH] = {0};   /* As supplied on the cmd line */
+    WCHAR   supplieddestination[MAX_PATH] = {0};
+    WCHAR   sourcestem[MAX_PATH] = {0};       /* Stem of source          */
+    WCHAR   sourcespec[MAX_PATH] = {0};       /* Filespec of source      */
+    WCHAR   destinationstem[MAX_PATH] = {0};  /* Stem of destination     */
+    WCHAR   destinationspec[MAX_PATH] = {0};  /* Filespec of destination */
+    WCHAR   copyCmd[MAXSTRING];               /* COPYCMD env var         */
+    DWORD   flags = 0;                        /* Option flags            */
+    const WCHAR PROMPTSTR1[]  = {'/', 'Y', 0};
+    const WCHAR PROMPTSTR2[]  = {'/', 'y', 0};
+    const WCHAR COPYCMD[]  = {'C', 'O', 'P', 'Y', 'C', 'M', 'D', 0};
+    const WCHAR EXCLUDE[]  = {'E', 'X', 'C', 'L', 'U', 'D', 'E', ':', 0};
+
+    /*
+     * Parse the command line
+     */
+
+    /* Confirm at least one parameter */
+    if (argc < 2) {
+        XCOPY_wprintf(XCOPY_LoadMessage(STRING_INVPARMS));
+        return RC_INITERROR;
+    }
+
+    /* Preinitialize flags based on COPYCMD */
+    if (GetEnvironmentVariable(COPYCMD, copyCmd, MAXSTRING)) {
+        if (wcsstr(copyCmd, PROMPTSTR1) != NULL ||
+            wcsstr(copyCmd, PROMPTSTR2) != NULL) {
+            flags |= OPT_NOPROMPT;
+        }
+    }
+
+    /* FIXME: On UNIX, files starting with a '.' are treated as hidden under
+       wine, but on windows these can be normal files. At least one installer
+       uses files such as .packlist and (validly) expects them to be copied.
+       Under wine, if we do not copy hidden files by default then they get
+       lose                                                                   */
+    flags |= OPT_COPYHIDSYS;
+
+    /* Skip first arg, which is the program name */
+    argvW++;
+
+    while (argc > 1)
+    {
+        argc--;
+        WINE_TRACE("Processing Arg: '%s'\n", wine_dbgstr_w(*argvW));
+
+        /* First non-switch parameter is source, second is destination */
+        if (*argvW[0] != '/') {
+            if (suppliedsource[0] == 0x00) {
+                lstrcpyW(suppliedsource, *argvW);
+            } else if (supplieddestination[0] == 0x00) {
+                lstrcpyW(supplieddestination, *argvW);
+            } else {
+                XCOPY_wprintf(XCOPY_LoadMessage(STRING_INVPARMS));
+                return RC_INITERROR;
+            }
+        } else {
+            /* Process all the switch options
+                 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(argvW[0][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 (CompareString (LOCALE_USER_DEFAULT,
+                                         NORM_IGNORECASE | SORT_STRINGSORT,
+                                         &argvW[0][1], 8,
+                                         EXCLUDE, -1) == 2) {
+                        if (XCOPY_ProcessExcludeList(&argvW[0][9])) {
+                          XCOPY_FailMessage(ERROR_INVALID_PARAMETER);
+                          return RC_INITERROR;
+                        } else flags |= OPT_EXCLUDELIST;
+                      } else flags |= OPT_EMPTYDIR | OPT_RECURSIVE;
+                      break;
+
+            /* D can be /D or /D: */
+            case 'D': if ((argvW[0][2])==':' && isdigit(argvW[0][3])) {
+                          SYSTEMTIME st;
+                          WCHAR     *pos = &argvW[0][3];
+                          BOOL       isError = FALSE;
+                          memset(&st, 0x00, sizeof(st));
+
+                          /* Parse the arg : Month */
+                          st.wMonth = _wtol(pos);
+                          while (*pos && isdigit(*pos)) pos++;
+                          if (*pos++ != '-') isError = TRUE;
+
+                          /* Parse the arg : Day */
+                          if (!isError) {
+                              st.wDay = _wtol(pos);
+                              while (*pos && isdigit(*pos)) pos++;
+                              if (*pos++ != '-') isError = TRUE;
+                          }
+
+                          /* Parse the arg : Day */
+                          if (!isError) {
+                              st.wYear = _wtol(pos);
+                              if (st.wYear < 100) st.wYear+=2000;
+                          }
+
+                          if (!isError && SystemTimeToFileTime(&st, &dateRange)) {
+                              SYSTEMTIME st;
+                              WCHAR datestring[32], timestring[32];
+
+                              flags |= OPT_DATERANGE;
+
+                              /* Debug info: */
+                              FileTimeToSystemTime (&dateRange, &st);
+                              GetDateFormat (0, DATE_SHORTDATE, &st, NULL, datestring,
+                                          sizeof(datestring));
+                              GetTimeFormat (0, TIME_NOSECONDS, &st,
+                                          NULL, timestring, sizeof(timestring));
+
+                              WINE_TRACE("Date being used is: %s %s\n",
+                                         wine_dbgstr_w(datestring), wine_dbgstr_w(timestring));
+                          } else {
+                              XCOPY_FailMessage(ERROR_INVALID_PARAMETER);
+                              return RC_INITERROR;
+                          }
+                      } else {
+                          flags |= OPT_DATENEWER;
+                      }
+                      break;
+
+            case '-': if (toupper(argvW[0][2])=='Y')
+                          flags &= ~OPT_NOPROMPT; break;
+            case '?': XCOPY_wprintf(XCOPY_LoadMessage(STRING_HELP));
+                      return RC_OK;
+            default:
+                WINE_TRACE("Unhandled parameter '%s'\n", wine_dbgstr_w(*argvW));
+                XCOPY_wprintf(XCOPY_LoadMessage(STRING_INVPARM), *argvW);
+                return RC_INITERROR;
+            }
+        }
+        argvW++;
+    }
+
+    /* Default the destination if not supplied */
+    if (supplieddestination[0] == 0x00)
+        lstrcpyW(supplieddestination, wchr_dot);
+
+    /* Trace out the supplied information */
+    WINE_TRACE("Supplied parameters:\n");
+    WINE_TRACE("Source      : '%s'\n", wine_dbgstr_w(suppliedsource));
+    WINE_TRACE("Destination : '%s'\n", wine_dbgstr_w(supplieddestination));
+
+    /* Extract required information from source specification */
+    rc = XCOPY_ProcessSourceParm(suppliedsource, sourcestem, sourcespec, flags);
+
+    /* Extract required information from destination specification */
+    rc = XCOPY_ProcessDestParm(supplieddestination, destinationstem,
+                               destinationspec, sourcespec, flags);
+
+    /* Trace out the resulting information */
+    WINE_TRACE("Resolved parameters:\n");
+    WINE_TRACE("Source Stem : '%s'\n", wine_dbgstr_w(sourcestem));
+    WINE_TRACE("Source Spec : '%s'\n", wine_dbgstr_w(sourcespec));
+    WINE_TRACE("Dest   Stem : '%s'\n", wine_dbgstr_w(destinationstem));
+    WINE_TRACE("Dest   Spec : '%s'\n", wine_dbgstr_w(destinationspec));
+
+    /* Pause if necessary */
+    if (flags & OPT_PAUSE) {
+        DWORD count;
+        char pausestr[10];
+
+        XCOPY_wprintf(XCOPY_LoadMessage(STRING_PAUSE));
+        ReadFile (GetStdHandle(STD_INPUT_HANDLE), pausestr, sizeof(pausestr),
+                  &count, NULL);
+    }
+
+    /* Now do the hard work... */
+    rc = XCOPY_DoCopy(sourcestem, sourcespec,
+                destinationstem, destinationspec,
+                flags);
+
+    /* Clear up exclude list allocated memory */
+    while (excludeList) {
+        EXCLUDELIST *pos = excludeList;
+        excludeList = excludeList -> next;
+        HeapFree(GetProcessHeap(), 0, pos->name);
+        HeapFree(GetProcessHeap(), 0, pos);
+    }
+
+    /* Finished - print trailer and exit */
+    if (flags & OPT_SIMULATE) {
+        XCOPY_wprintf(XCOPY_LoadMessage(STRING_SIMCOPY), filesCopied);
+    } else if (!(flags & OPT_NOCOPY)) {
+        XCOPY_wprintf(XCOPY_LoadMessage(STRING_COPY), filesCopied);
+    }
+    if (rc == RC_OK && filesCopied == 0) rc = RC_NOFILES;
+    return rc;
+
+}
+
+
+/* =========================================================================
+   XCOPY_ProcessSourceParm - Takes the supplied source parameter, and
+     converts it into a stem and a filespec
+   ========================================================================= */
+static int XCOPY_ProcessSourceParm(WCHAR *suppliedsource, WCHAR *stem,
+                                   WCHAR *spec, DWORD flags)
+{
+    WCHAR             actualsource[MAX_PATH];
+    WCHAR            *starPos;
+    WCHAR            *questPos;
+    DWORD             attribs;
+
+    /*
+     * Validate the source, expanding to full path ensuring it exists
+     */
+    if (GetFullPathName(suppliedsource, MAX_PATH, actualsource, NULL) == 0) {
+        WINE_FIXME("Unexpected failure expanding source path (%d)\n", GetLastError());
+        return RC_INITERROR;
+    }
+
+    /* If full names required, convert to using the full path */
+    if (flags & OPT_FULL) {
+        lstrcpyW(suppliedsource, actualsource);
+    }
+
+    /*
+     * Work out the stem of the source
+     */
+
+    /* If a directory is supplied, use that as-is (either fully or
+          partially qualified)
+       If a filename is supplied + a directory or drive path, use that
+          as-is
+       Otherwise
+          If no directory or path specified, add eg. C:
+          stem is Drive/Directory is bit up to last \ (or first :)
+          spec is bit after that                                         */
+
+    starPos = wcschr(suppliedsource, '*');
+    questPos = wcschr(suppliedsource, '?');
+    if (starPos || questPos) {
+        attribs = 0x00;  /* Ensures skips invalid or directory check below */
+    } else {
+        attribs = GetFileAttributes(actualsource);
+    }
+
+    if (attribs == INVALID_FILE_ATTRIBUTES) {
+        XCOPY_FailMessage(GetLastError());
+        return RC_INITERROR;
+
+    /* Directory:
+         stem should be exactly as supplied plus a '\', unless it was
+          eg. C: in which case no slash required */
+    } else if (attribs & FILE_ATTRIBUTE_DIRECTORY) {
+        WCHAR lastChar;
+
+        WINE_TRACE("Directory supplied\n");
+        lstrcpyW(stem, suppliedsource);
+        lastChar = stem[lstrlenW(stem)-1];
+        if (lastChar != '\\' && lastChar != ':') {
+            lstrcatW(stem, wchr_slash);
+        }
+        lstrcpyW(spec, wchr_star);
+
+    /* File or wildcard search:
+         stem should be:
+           Up to and including last slash if directory path supplied
+           If c:filename supplied, just the c:
+           Otherwise stem should be the current drive letter + ':' */
+    } else {
+        WCHAR *lastDir;
+
+        WINE_TRACE("Filename supplied\n");
+        lastDir   = wcsrchr(suppliedsource, '\\');
+
+        if (lastDir) {
+            lstrcpyW(stem, suppliedsource);
+            stem[(lastDir-suppliedsource) + 1] = 0x00;
+            lstrcpyW(spec, (lastDir+1));
+        } else if (suppliedsource[1] == ':') {
+            lstrcpyW(stem, suppliedsource);
+            stem[2] = 0x00;
+            lstrcpyW(spec, suppliedsource+2);
+        } else {
+            WCHAR curdir[MAXSTRING];
+            GetCurrentDirectory (sizeof(curdir), curdir);
+            stem[0] = curdir[0];
+            stem[1] = curdir[1];
+            stem[2] = 0x00;
+            lstrcpyW(spec, suppliedsource);
+        }
+    }
+
+    return RC_OK;
+}
+
+/* =========================================================================
+   XCOPY_ProcessDestParm - Takes the supplied destination parameter, and
+     converts it into a stem
+   ========================================================================= */
+static int XCOPY_ProcessDestParm(WCHAR *supplieddestination, WCHAR *stem, WCHAR *spec,
+                                 WCHAR *srcspec, DWORD flags)
+{
+    WCHAR  actualdestination[MAX_PATH];
+    DWORD attribs;
+    BOOL isDir = FALSE;
+
+    /*
+     * Validate the source, expanding to full path ensuring it exists
+     */
+    if (GetFullPathName(supplieddestination, MAX_PATH, actualdestination, NULL) == 0) {
+        WINE_FIXME("Unexpected failure expanding source path (%d)\n", GetLastError());
+        return RC_INITERROR;
+    }
+
+    /* Destination is either a directory or a file */
+    attribs = GetFileAttributes(actualdestination);
+
+    if (attribs == INVALID_FILE_ATTRIBUTES) {
+
+        /* If /I supplied and wildcard copy, assume directory */
+        if (flags & OPT_ASSUMEDIR &&
+            (wcschr(srcspec, '?') || wcschr(srcspec, '*'))) {
+
+            isDir = TRUE;
+
+        } else {
+            DWORD count;
+            char  answer[10] = "";
+            WCHAR fileChar[2];
+            WCHAR dirChar[2];
+
+            /* Read the F and D characters from the resource file */
+            wcscpy(fileChar, XCOPY_LoadMessage(STRING_FILE_CHAR));
+            wcscpy(dirChar, XCOPY_LoadMessage(STRING_DIR_CHAR));
+
+            while (answer[0] != fileChar[0] && answer[0] != dirChar[0]) {
+                XCOPY_wprintf(XCOPY_LoadMessage(STRING_QISDIR), supplieddestination);
+
+                ReadFile(GetStdHandle(STD_INPUT_HANDLE), answer, sizeof(answer), &count, NULL);
+                WINE_TRACE("User answer %c\n", answer[0]);
+
+                answer[0] = toupper(answer[0]);
+            }
+
+            if (answer[0] == dirChar[0]) {
+                isDir = TRUE;
+            } else {
+                isDir = FALSE;
+            }
+        }
+    } else {
+        isDir = (attribs & FILE_ATTRIBUTE_DIRECTORY);
+    }
+
+    if (isDir) {
+        lstrcpyW(stem, actualdestination);
+        *spec = 0x00;
+
+        /* Ensure ends with a '\' */
+        if (stem[lstrlenW(stem)-1] != '\\') {
+            lstrcatW(stem, wchr_slash);
+        }
+
+    } else {
+        WCHAR drive[MAX_PATH];
+        WCHAR dir[MAX_PATH];
+        WCHAR fname[MAX_PATH];
+        WCHAR ext[MAX_PATH];
+        _wsplitpath(actualdestination, drive, dir, fname, ext);
+        lstrcpyW(stem, drive);
+        lstrcatW(stem, dir);
+        lstrcpyW(spec, fname);
+        lstrcatW(spec, ext);
+    }
+    return RC_OK;
+}
+
+/* =========================================================================
+   XCOPY_DoCopy - Recursive function to copy files based on input parms
+     of a stem and a spec
+
+      This works by using FindFirstFile supplying the source stem and spec.
+      If results are found, any non-directory ones are processed
+      Then, if /S or /E is supplied, another search is made just for
+      directories, and this function is called again for that directory
+
+   ========================================================================= */
+static int XCOPY_DoCopy(WCHAR *srcstem, WCHAR *srcspec,
+                        WCHAR *deststem, WCHAR *destspec,
+                        DWORD flags)
+{
+    WIN32_FIND_DATA *finddata;
+    HANDLE          h;
+    BOOL            findres = TRUE;
+    WCHAR           *inputpath, *outputpath;
+    BOOL            copiedFile = FALSE;
+    DWORD           destAttribs, srcAttribs;
+    BOOL            skipFile;
+
+    /* Allocate some working memory on heap to minimize footprint */
+    finddata = HeapAlloc(GetProcessHeap(), 0, sizeof(WIN32_FIND_DATA));
+    inputpath = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
+    outputpath = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
+
+    /* Build the search info into a single parm */
+    lstrcpyW(inputpath, srcstem);
+    lstrcatW(inputpath, srcspec);
+
+    /* Search 1 - Look for matching files */
+    h = FindFirstFile(inputpath, finddata);
+    while (h != INVALID_HANDLE_VALUE && findres) {
+
+        skipFile = FALSE;
+
+        /* Ignore . and .. */
+        if (lstrcmpW(finddata->cFileName, wchr_dot)==0 ||
+            lstrcmpW(finddata->cFileName, wchr_dotdot)==0 ||
+            finddata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+
+            WINE_TRACE("Skipping directory, . or .. (%s)\n", wine_dbgstr_w(finddata->cFileName));
+        } else {
+
+            /* Get the filename information */
+            lstrcpyW(copyFrom, srcstem);
+            if (flags & OPT_SHORTNAME) {
+              lstrcatW(copyFrom, finddata->cAlternateFileName);
+            } else {
+              lstrcatW(copyFrom, finddata->cFileName);
+            }
+
+            lstrcpyW(copyTo, deststem);
+            if (*destspec == 0x00) {
+                if (flags & OPT_SHORTNAME) {
+                    lstrcatW(copyTo, finddata->cAlternateFileName);
+                } else {
+                    lstrcatW(copyTo, finddata->cFileName);
+                }
+            } else {
+                lstrcatW(copyTo, destspec);
+            }
+
+            /* Do the copy */
+            WINE_TRACE("ACTION: Copy '%s' -> '%s'\n", wine_dbgstr_w(copyFrom),
+                                                      wine_dbgstr_w(copyTo));
+            if (!copiedFile && !(flags & OPT_SIMULATE)) XCOPY_CreateDirectory(deststem);
+
+            /* See if allowed to copy it */
+            srcAttribs = GetFileAttributesW(copyFrom);
+            WINE_TRACE("Source attribs: %d\n", srcAttribs);
+
+            if ((srcAttribs & FILE_ATTRIBUTE_HIDDEN) ||
+                (srcAttribs & FILE_ATTRIBUTE_SYSTEM)) {
+
+                if (!(flags & OPT_COPYHIDSYS)) {
+                    skipFile = TRUE;
+                }
+            }
+
+            if (!(srcAttribs & FILE_ATTRIBUTE_ARCHIVE) &&
+                (flags & OPT_ARCHIVEONLY)) {
+                skipFile = TRUE;
+            }
+
+            /* See if file exists */
+            destAttribs = GetFileAttributesW(copyTo);
+            WINE_TRACE("Dest attribs: %d\n", srcAttribs);
+
+            /* Check date ranges if a destination file already exists */
+            if (!skipFile && (flags & OPT_DATERANGE) &&
+                (CompareFileTime(&finddata->ftLastWriteTime, &dateRange) < 0)) {
+                WINE_TRACE("Skipping file as modified date too old\n");
+                skipFile = TRUE;
+            }
+
+            /* If just /D supplied, only overwrite if src newer than dest */
+            if (!skipFile && (flags & OPT_DATENEWER) &&
+               (destAttribs != INVALID_FILE_ATTRIBUTES)) {
+                HANDLE h = CreateFile(copyTo, GENERIC_READ, FILE_SHARE_READ,
+                                      NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
+                                      NULL);
+                if (h != INVALID_HANDLE_VALUE) {
+                    FILETIME writeTime;
+                    GetFileTime(h, NULL, NULL, &writeTime);
+
+                    if (CompareFileTime(&finddata->ftLastWriteTime, &writeTime) <= 0) {
+                        WINE_TRACE("Skipping file as dest newer or same date\n");
+                        skipFile = TRUE;
+                    }
+                    CloseHandle(h);
+                }
+            }
+
+            /* See if exclude list provided. Note since filenames are case
+               insensitive, need to uppercase the filename before doing
+               strstr                                                     */
+            if (!skipFile && (flags & OPT_EXCLUDELIST)) {
+                EXCLUDELIST *pos = excludeList;
+                WCHAR copyFromUpper[MAX_PATH];
+
+                /* Uppercase source filename */
+                lstrcpyW(copyFromUpper, copyFrom);
+                CharUpperBuff(copyFromUpper, lstrlenW(copyFromUpper));
+
+                /* Loop through testing each exclude line */
+                while (pos) {
+                    if (wcsstr(copyFromUpper, pos->name) != NULL) {
+                        WINE_TRACE("Skipping file as matches exclude '%s'\n",
+                                   wine_dbgstr_w(pos->name));
+                        skipFile = TRUE;
+                        pos = NULL;
+                    } else {
+                        pos = pos->next;
+                    }
+                }
+            }
+
+            /* Prompt each file if necessary */
+            if (!skipFile && (flags & OPT_SRCPROMPT)) {
+                DWORD count;
+                char  answer[10];
+                BOOL  answered = FALSE;
+                WCHAR yesChar[2];
+                WCHAR noChar[2];
+
+                /* Read the Y and N characters from the resource file */
+                wcscpy(yesChar, XCOPY_LoadMessage(STRING_YES_CHAR));
+                wcscpy(noChar, XCOPY_LoadMessage(STRING_NO_CHAR));
+
+                while (!answered) {
+                    XCOPY_wprintf(XCOPY_LoadMessage(STRING_SRCPROMPT), copyFrom);
+                    ReadFile (GetStdHandle(STD_INPUT_HANDLE), answer, sizeof(answer),
+                              &count, NULL);
+
+                    answered = TRUE;
+                    if (toupper(answer[0]) == noChar[0])
+                        skipFile = TRUE;
+                    else if (toupper(answer[0]) != yesChar[0])
+                        answered = FALSE;
+                }
+            }
+
+            if (!skipFile &&
+                destAttribs != INVALID_FILE_ATTRIBUTES && !(flags & OPT_NOPROMPT)) {
+                DWORD count;
+                char  answer[10];
+                BOOL  answered = FALSE;
+                WCHAR yesChar[2];
+                WCHAR allChar[2];
+                WCHAR noChar[2];
+
+                /* Read the A,Y and N characters from the resource file */
+                wcscpy(yesChar, XCOPY_LoadMessage(STRING_YES_CHAR));
+                wcscpy(allChar, XCOPY_LoadMessage(STRING_ALL_CHAR));
+                wcscpy(noChar, XCOPY_LoadMessage(STRING_NO_CHAR));
+
+                while (!answered) {
+                    XCOPY_wprintf(XCOPY_LoadMessage(STRING_OVERWRITE), copyTo);
+                    ReadFile (GetStdHandle(STD_INPUT_HANDLE), answer, sizeof(answer),
+                              &count, NULL);
+
+                    answered = TRUE;
+                    if (toupper(answer[0]) == allChar[0])
+                        flags |= OPT_NOPROMPT;
+                    else if (toupper(answer[0]) == noChar[0])
+                        skipFile = TRUE;
+                    else if (toupper(answer[0]) != yesChar[0])
+                        answered = FALSE;
+                }
+            }
+
+            /* See if it has to exist! */
+            if (destAttribs == INVALID_FILE_ATTRIBUTES && (flags & OPT_MUSTEXIST)) {
+                skipFile = TRUE;
+            }
+
+            /* Output a status message */
+            if (!skipFile) {
+                if (flags & OPT_QUIET) {
+                    /* Skip message */
+                } else if (flags & OPT_FULL) {
+                    const WCHAR infostr[]   = {'%', 's', ' ', '-', '>', ' ',
+                                               '%', 's', '\n', 0};
+
+                    XCOPY_wprintf(infostr, copyFrom, copyTo);
+                } else {
+                    const WCHAR infostr[] = {'%', 's', '\n', 0};
+                    XCOPY_wprintf(infostr, copyFrom);
+                }
+
+                /* If allowing overwriting of read only files, remove any
+                   write protection                                       */
+                if ((destAttribs & FILE_ATTRIBUTE_READONLY) &&
+                    (flags & OPT_REPLACEREAD)) {
+                    SetFileAttributes(copyTo, destAttribs & ~FILE_ATTRIBUTE_READONLY);
+                }
+
+                copiedFile = TRUE;
+                if (flags & OPT_SIMULATE || flags & OPT_NOCOPY) {
+                    /* Skip copy */
+                } else if (CopyFile(copyFrom, copyTo, FALSE) == 0) {
+
+                    DWORD error = GetLastError();
+                    XCOPY_wprintf(XCOPY_LoadMessage(STRING_COPYFAIL),
+                           copyFrom, copyTo, error);
+                    XCOPY_FailMessage(error);
+
+                    if (flags & OPT_IGNOREERRORS) {
+                        skipFile = TRUE;
+                    } else {
+                        return RC_WRITEERROR;
+                    }
+                }
+
+                /* If /M supplied, remove the archive bit after successful copy */
+                if (!skipFile) {
+                    if ((srcAttribs & FILE_ATTRIBUTE_ARCHIVE) &&
+                        (flags & OPT_REMOVEARCH)) {
+                        SetFileAttributes(copyFrom, (srcAttribs & ~FILE_ATTRIBUTE_ARCHIVE));
+                    }
+                    filesCopied++;
+                }
+            }
+        }
+
+        /* Find next file */
+        findres = FindNextFile(h, finddata);
+    }
+    FindClose(h);
+
+    /* Search 2 - do subdirs */
+    if (flags & OPT_RECURSIVE) {
+        lstrcpyW(inputpath, srcstem);
+        lstrcatW(inputpath, wchr_star);
+        findres = TRUE;
+        WINE_TRACE("Processing subdirs with spec: %s\n", wine_dbgstr_w(inputpath));
+
+        h = FindFirstFile(inputpath, finddata);
+        while (h != INVALID_HANDLE_VALUE && findres) {
+
+            /* Only looking for dirs */
+            if ((finddata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+                (lstrcmpW(finddata->cFileName, wchr_dot) != 0) &&
+                (lstrcmpW(finddata->cFileName, wchr_dotdot) != 0)) {
+
+                WINE_TRACE("Handling subdir: %s\n", wine_dbgstr_w(finddata->cFileName));
+
+                /* Make up recursive information */
+                lstrcpyW(inputpath, srcstem);
+                lstrcatW(inputpath, finddata->cFileName);
+                lstrcatW(inputpath, wchr_slash);
+
+                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);
+                }
+
+                XCOPY_DoCopy(inputpath, srcspec, outputpath, destspec, flags);
+            }
+
+            /* Find next one */
+            findres = FindNextFile(h, finddata);
+        }
+    }
+
+    /* free up memory */
+    HeapFree(GetProcessHeap(), 0, finddata);
+    HeapFree(GetProcessHeap(), 0, inputpath);
+    HeapFree(GetProcessHeap(), 0, outputpath);
+
+    return 0;
+}
+
+/* =========================================================================
+ * Routine copied from cmd.exe md command -
+ * This works recursivly. so creating dir1\dir2\dir3 will create dir1 and
+ * dir2 if they do not already exist.
+ * ========================================================================= */
+static BOOL XCOPY_CreateDirectory(const WCHAR* path)
+{
+    int len;
+    WCHAR *new_path;
+    BOOL ret = TRUE;
+
+    new_path = HeapAlloc(GetProcessHeap(),0, sizeof(WCHAR) * (lstrlenW(path)+1));
+    lstrcpyW(new_path,path);
+
+    while ((len = lstrlenW(new_path)) && new_path[len - 1] == '\\')
+        new_path[len - 1] = 0;
+
+    while (!CreateDirectory(new_path,NULL))
+    {
+        WCHAR *slash;
+        DWORD last_error = GetLastError();
+        if (last_error == ERROR_ALREADY_EXISTS)
+            break;
+
+        if (last_error != ERROR_PATH_NOT_FOUND)
+        {
+            ret = FALSE;
+            break;
+        }
+
+        if (!(slash = wcsrchr(new_path,'\\')) && ! (slash = wcsrchr(new_path,'/')))
+        {
+            ret = FALSE;
+            break;
+        }
+
+        len = slash - new_path;
+        new_path[len] = 0;
+        if (!XCOPY_CreateDirectory(new_path))
+        {
+            ret = FALSE;
+            break;
+        }
+        new_path[len] = '\\';
+    }
+    HeapFree(GetProcessHeap(),0,new_path);
+    return ret;
+}
+
+/* =========================================================================
+ * Process the /EXCLUDE: file list, building up a list of substrings to
+ * avoid copying
+ * Returns TRUE on any failure
+ * ========================================================================= */
+static BOOL XCOPY_ProcessExcludeList(WCHAR* parms) {
+
+    WCHAR *filenameStart = parms;
+
+    WINE_TRACE("/EXCLUDE parms: '%s'\n", wine_dbgstr_w(parms));
+    excludeList = NULL;
+
+    while (*parms && *parms != ' ' && *parms != '/') {
+
+        /* If found '+' then process the file found so far */
+        if (*parms == '+') {
+            if (XCOPY_ProcessExcludeFile(filenameStart, parms)) {
+                return TRUE;
+            }
+            filenameStart = parms+1;
+        }
+        parms++;
+    }
+
+    if (filenameStart != parms) {
+        if (XCOPY_ProcessExcludeFile(filenameStart, parms)) {
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+/* =========================================================================
+ * Process a single file from the /EXCLUDE: file list, building up a list
+ * of substrings to avoid copying
+ * Returns TRUE on any failure
+ * ========================================================================= */
+static BOOL XCOPY_ProcessExcludeFile(WCHAR* filename, WCHAR* endOfName) {
+
+    WCHAR   endChar = *endOfName;
+    WCHAR   buffer[MAXSTRING];
+    FILE   *inFile  = NULL;
+    const WCHAR readTextMode[]  = {'r', 't', 0};
+
+    /* Null terminate the filename (temporarily updates the filename hence
+         parms not const)                                                 */
+    *endOfName = 0x00;
+
+    /* Open the file */
+    inFile = _wfopen(filename, readTextMode);
+    if (inFile == NULL) {
+        XCOPY_wprintf(XCOPY_LoadMessage(STRING_OPENFAIL), filename);
+        *endOfName = endChar;
+        return TRUE;
+    }
+
+    /* Process line by line */
+    while (fgetws(buffer, sizeof(buffer), inFile) != NULL) {
+        EXCLUDELIST *thisEntry;
+        int length = lstrlenW(buffer);
+
+        /* Strip CRLF */
+        buffer[length-1] = 0x00;
+
+        /* If more than CRLF */
+        if (length > 1) {
+          thisEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(EXCLUDELIST));
+          thisEntry->next = excludeList;
+          excludeList = thisEntry;
+          thisEntry->name = HeapAlloc(GetProcessHeap(), 0,
+                                      (length * sizeof(WCHAR))+1);
+          lstrcpyW(thisEntry->name, buffer);
+          CharUpperBuff(thisEntry->name, length);
+          WINE_TRACE("Read line : '%s'\n", wine_dbgstr_w(thisEntry->name));
+        }
+    }
+
+    /* See if EOF or error occurred */
+    if (!feof(inFile)) {
+        XCOPY_wprintf(XCOPY_LoadMessage(STRING_READFAIL), filename);
+        *endOfName = endChar;
+        return TRUE;
+    }
+
+    /* Revert the input string to original form, and cleanup + return */
+    *endOfName = endChar;
+    fclose(inFile);
+    return FALSE;
+}
+
+/* =========================================================================
+ * Load a string from the resource file, handling any error
+ * Returns string retrieved from resource file
+ * ========================================================================= */
+static WCHAR *XCOPY_LoadMessage(UINT id) {
+    static WCHAR msg[MAXSTRING];
+    const WCHAR failedMsg[]  = {'F', 'a', 'i', 'l', 'e', 'd', '!', 0};
+
+    if (!LoadString(GetModuleHandle(NULL), id, msg, sizeof(msg))) {
+       WINE_FIXME("LoadString failed with %d\n", GetLastError());
+       lstrcpyW(msg, failedMsg);
+    }
+    return msg;
+}
+
+/* =========================================================================
+ * Load a string for a system error and writes it to the screen
+ * Returns string retrieved from resource file
+ * ========================================================================= */
+static void XCOPY_FailMessage(DWORD err) {
+    LPWSTR lpMsgBuf;
+    int status;
+
+    status = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                            FORMAT_MESSAGE_FROM_SYSTEM,
+                            NULL, err, 0,
+                            (LPTSTR) &lpMsgBuf, 0, NULL);
+    if (!status) {
+      WINE_FIXME("FIXME: Cannot display message for error %d, status %d\n",
+                 err, GetLastError());
+    } else {
+      const WCHAR infostr[] = {'%', 's', '\n', 0};
+      XCOPY_wprintf(infostr, lpMsgBuf);
+      LocalFree ((HLOCAL)lpMsgBuf);
+    }
+}
+
+/* =========================================================================
+ * Output a formatted unicode string. Ideally this will go to the console
+ *  and hence required WriteConsoleW to output it, however if file i/o is
+ *  redirected, it needs to be WriteFile'd using OEM (not ANSI) format
+ * ========================================================================= */
+int XCOPY_wprintf(const WCHAR *format, ...) {
+
+    static WCHAR *output_bufW = NULL;
+    static char  *output_bufA = NULL;
+    static BOOL  toConsole    = TRUE;
+    static BOOL  traceOutput  = FALSE;
+#define MAX_WRITECONSOLE_SIZE 65535
+
+    va_list parms;
+    DWORD   len, nOut;
+    DWORD   res = 0;
+
+    /*
+     * Allocate buffer to use when writing to console
+     * Note: Not freed - memory will be allocated once and released when
+     *         xcopy ends
+     */
+
+    if (!output_bufW) output_bufW = HeapAlloc(GetProcessHeap(), 0,
+                                              MAX_WRITECONSOLE_SIZE);
+    if (!output_bufW) {
+      WINE_FIXME("Out of memory - could not allocate 2 x 64K buffers\n");
+      return 0;
+    }
+
+    /* Use wvsprintf to store output into unicode buffer */
+    va_start(parms, format);
+    len = vswprintf(output_bufW, format, parms);
+    va_end(parms);
+
+    /* Try to write as unicode all the time we think its a console */
+    if (toConsole) {
+      res = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
+                          output_bufW, len, &nOut, NULL);
+    }
+
+    /* If writing to console has failed (ever) we assume its file
+       i/o so convert to OEM codepage and output                  */
+    if (!res) {
+      BOOL usedDefaultChar = FALSE;
+      DWORD convertedChars;
+
+      toConsole = FALSE;
+
+      /*
+       * Allocate buffer to use when writing to file. Not freed, as above
+       */
+      if (!output_bufA) output_bufA = HeapAlloc(GetProcessHeap(), 0,
+                                                MAX_WRITECONSOLE_SIZE);
+      if (!output_bufA) {
+        WINE_FIXME("Out of memory - could not allocate 2 x 64K buffers\n");
+        return 0;
+      }
+
+      /* Convert to OEM, then output */
+      convertedChars = WideCharToMultiByte(GetConsoleOutputCP(), 0, output_bufW,
+                          len, output_bufA, MAX_WRITECONSOLE_SIZE,
+                          "?", &usedDefaultChar);
+      WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), output_bufA, convertedChars,
+                &nOut, FALSE);
+    }
+
+    /* Trace whether screen or console */
+    if (!traceOutput) {
+      WINE_TRACE("Writing to console? (%d)\n", toConsole);
+      traceOutput = TRUE;
+    }
+    return nOut;
+}
diff --git a/reactos/base/applications/cmdutils/xcopy/xcopy.h b/reactos/base/applications/cmdutils/xcopy/xcopy.h
new file mode 100644 (file)
index 0000000..ee4bd91
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * XCOPY - Wine-compatible xcopy program
+ *
+ * Copyright (C) 2007 J. Edmeades
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/* Local #defines */
+#define RC_OK         0
+#define RC_NOFILES    1
+#define RC_CTRLC      2
+#define RC_INITERROR  4
+#define RC_WRITEERROR 5
+
+#define OPT_ASSUMEDIR    0x00000001
+#define OPT_RECURSIVE    0x00000002
+#define OPT_EMPTYDIR     0x00000004
+#define OPT_QUIET        0x00000008
+#define OPT_FULL         0x00000010
+#define OPT_SIMULATE     0x00000020
+#define OPT_PAUSE        0x00000040
+#define OPT_NOCOPY       0x00000080
+#define OPT_NOPROMPT     0x00000100
+#define OPT_SHORTNAME    0x00000200
+#define OPT_MUSTEXIST    0x00000400
+#define OPT_REPLACEREAD  0x00000800
+#define OPT_COPYHIDSYS   0x00001000
+#define OPT_IGNOREERRORS 0x00002000
+#define OPT_SRCPROMPT    0x00004000
+#define OPT_ARCHIVEONLY  0x00008000
+#define OPT_REMOVEARCH   0x00010000
+#define OPT_EXCLUDELIST  0x00020000
+#define OPT_DATERANGE    0x00040000
+#define OPT_DATENEWER    0x00080000
+
+#define MAXSTRING 8192
+
+/* Translation ids */
+#define STRING_INVPARMS         101
+#define STRING_INVPARM          102
+#define STRING_PAUSE            103
+#define STRING_SIMCOPY          104
+#define STRING_COPY             105
+#define STRING_QISDIR           106
+#define STRING_SRCPROMPT        107
+#define STRING_OVERWRITE        108
+#define STRING_COPYFAIL         109
+#define STRING_OPENFAIL         110
+#define STRING_READFAIL         111
+#define STRING_YES_CHAR         112
+#define STRING_NO_CHAR          113
+#define STRING_ALL_CHAR         114
+#define STRING_FILE_CHAR        115
+#define STRING_DIR_CHAR         116
+#define STRING_HELP             117
diff --git a/reactos/base/applications/cmdutils/xcopy/xcopy.rbuild b/reactos/base/applications/cmdutils/xcopy/xcopy.rbuild
new file mode 100644 (file)
index 0000000..5a44d73
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<module name="xcopy" type="win32cui" installbase="system32" installname="xcopy.exe" allowwarnings="true" unicode="true">
+       <include base="xcopy">.</include>
+       <library>kernel32</library>
+       <library>advapi32</library>
+       <library>user32</library>
+       <file>xcopy.c</file>
+       <file>xcopy.rc</file>
+       <metadata description="xcopy command-line tool" />
+</module>
diff --git a/reactos/base/applications/cmdutils/xcopy/xcopy.rc b/reactos/base/applications/cmdutils/xcopy/xcopy.rc
new file mode 100644 (file)
index 0000000..cb4db04
--- /dev/null
@@ -0,0 +1,17 @@
+#include <windows.h>
+#include <commctrl.h>
+
+#include "resource.h"
+
+
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION   "xcopy command\0"
+#define REACTOS_STR_INTERNAL_NAME      "xcopy\0"
+#define REACTOS_STR_ORIGINAL_FILENAME  "xcopy.exe\0"
+#include <reactos/version.rc>
+
+#include "rsrc.rc"
+
+