Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

548 lines
18 KiB

  1. /* tc.c - general purpose tree copy program
  2. *
  3. * tc.c recursively walks the source tree and copies the entire structure
  4. * to the destination tree, creating directories as it goes along.
  5. *
  6. * 2/18/86 dan lipkie correct error msg v[0] -> v[1]
  7. * 2/18/86 dan lipkie allow for out of space on destination
  8. * 4/11/86 dan lipkie add /h switch
  9. * 4/13/86 dan lipkie allow all switches to use same switch char
  10. * 17-Jun-1986 dan lipkie add /n, allow ^C to cancel
  11. * 11-Jul-1986 dan lipkie add /s
  12. * 21-Jul-1986 dan lipkie add MAXDIRLEN
  13. * 06-Nov-1986 mz add /L
  14. * 13-May-1987 mz add /F
  15. * 15-May-1987 mz Make /F display dirs too
  16. * 11-Oct-1989 reubenb fix /L parsing (?)
  17. * add some void declarations
  18. * 19-Oct-1989 mz
  19. *
  20. */
  21. #include <direct.h>
  22. #include <sys\types.h>
  23. #include <sys\stat.h>
  24. #include <io.h>
  25. #include <conio.h>
  26. #include <errno.h>
  27. #include <string.h>
  28. #include <stdio.h>
  29. #include <process.h>
  30. #include <ctype.h>
  31. #include <windows.h>
  32. #include <tools.h>
  33. // Forward Function Declartions...
  34. void CopyNode( char *, struct findType *, void * );
  35. void MakeDir( char * );
  36. void __cdecl Usage( char *, ... );
  37. void errorexit( char *, unsigned, unsigned, unsigned );
  38. void ChkSpace( int, LONGLONG );
  39. int FormDest( char * );
  40. char const rgstrUsage[] = {
  41. "Usage: TC [/adhijnqrstAFLS] src-tree dst-tree\n"
  42. " /a only those files with the archive bit on are copied\n"
  43. " /b copies to inuse files are delayed to reboot\n"
  44. " /d deletes source files/directories as it copies them\n"
  45. " /h copy hidden directories, implied by /d\n"
  46. " /i ignore hidden, has nothing to do with hidden dir\n"
  47. " /j ignore system files, has nothing to do with hidden dir\n"
  48. " /n no subdirectories\n"
  49. " /q silent operation. Normal mode displays activity\n"
  50. " /r read-only files are overwritten\n"
  51. " /s structure only\n"
  52. " /t only those files with source time > dest time are copied\n"
  53. " /A allow errors from copy (won't delete if /d present)\n"
  54. " /F list files that would be copied\n"
  55. " /L large disk copy (no full disk checking)\n"
  56. " /S produce batch script to do copy"
  57. };
  58. flagType fReboot = FALSE; // TRUE => delay reboot
  59. flagType fDelete = FALSE; // TRUE => delete/rmdir source after
  60. flagType fQuiet = FALSE; // TRUE => no msg except for error
  61. flagType fArchive = FALSE; // TRUE => copy only ARCHIVEed files
  62. flagType fTime = FALSE; // TRUE => copy later dated files
  63. flagType fHidden = FALSE; // TRUE => copy hidden directories
  64. flagType fNoSub = FALSE; // TRUE => do not copy subdirect
  65. flagType fStructure = FALSE; // TRUE => copy only directory
  66. flagType fInCopyNode = FALSE; // TRUE => prevent recursion
  67. flagType fIgnoreHidden = FALSE; // TRUE => don't consider hidden
  68. flagType fIgnoreSystem; // TRUE => don't consider system
  69. flagType fOverwriteRO; // TRUE => ignore R/O bit
  70. flagType fLarge = FALSE; // TRUE => disables ChkSpace
  71. flagType fFiles = FALSE; // TRUE => output files
  72. flagType fScript = FALSE; // TRUE => output files as script
  73. flagType fAllowError = FALSE; // TRUE => fcopy errors ignored
  74. flagType fRebootNecessary = FALSE; // TRUE => reboot ultimately necessary
  75. char source[MAX_PATH];
  76. char dest[MAX_PATH];
  77. char tempdir[MAX_PATH];
  78. char tempfile[MAX_PATH];
  79. int drv;
  80. int srclen, dstlen;
  81. /* Usage takes a variable number of strings, terminated by zero,
  82. * e.g. Usage ("first ", "second ", 0);
  83. */
  84. void __cdecl Usage( char *p, ... )
  85. {
  86. char **rgstr;
  87. rgstr = &p;
  88. if (*rgstr) {
  89. fprintf (stderr, "TC: ");
  90. while (*rgstr)
  91. fprintf(stderr, "%s", *rgstr++);
  92. fprintf(stderr, "\n");
  93. }
  94. fputs(rgstrUsage, stderr);
  95. exit (1);
  96. }
  97. void errorexit (fmt, a1, a2, a3)
  98. char *fmt;
  99. unsigned a1, a2, a3;
  100. {
  101. fprintf (stderr, fmt, a1, a2, a3);
  102. fprintf (stderr, "\n");
  103. exit (1);
  104. }
  105. /* chkspace checks to see if there is enough space on drive d to hold a file
  106. * of size l. If not, requests a disk swap
  107. */
  108. void ChkSpace (d, l)
  109. int d;
  110. LONGLONG l;
  111. {
  112. char *pend;
  113. char pathStr[MAX_PATH];
  114. int i;
  115. if (!fLarge)
  116. while (freespac (d) < sizeround (l, d)) {
  117. _cprintf ("Please insert a new disk in drive %c: and strike any key",
  118. d + 'A'-1);
  119. if (_getch () == '\003') /* ^C */
  120. exit (1);
  121. _cprintf ("\n\r");
  122. pend = pathStr;
  123. drive(dest, pend);
  124. pend += strlen(pend);
  125. path(dest, pend);
  126. if (fPathChr(pathStr[(i = (strlen(pathStr) - 1))]) && i > 2)
  127. pathStr[i] = '\0';
  128. MakeDir(pathStr);
  129. }
  130. }
  131. __cdecl main (c, v)
  132. int c;
  133. char *v[];
  134. {
  135. struct findType fbuf;
  136. char *p;
  137. ConvertAppToOem( c, v );
  138. SHIFT(c,v);
  139. while (c && fSwitChr (*v[ 0 ])) {
  140. p = v[ 0 ];
  141. SHIFT(c,v);
  142. while (*++p)
  143. switch (*p) {
  144. case 'b':
  145. fReboot = TRUE;
  146. break;
  147. case 'd':
  148. fDelete = TRUE;
  149. /* fall through; d => h */
  150. case 'h':
  151. fHidden = TRUE;
  152. break;
  153. case 'S':
  154. fScript = TRUE;
  155. /* Fall through implies FILES and QUIET */
  156. case 'F':
  157. fFiles = TRUE;
  158. /* Fall through implies QUIET */
  159. case 'q':
  160. fQuiet = TRUE;
  161. break;
  162. case 'a':
  163. fArchive = TRUE;
  164. break;
  165. case 't':
  166. fTime = TRUE;
  167. break;
  168. case 'n':
  169. fNoSub = TRUE;
  170. break;
  171. case 's':
  172. fStructure = TRUE;
  173. break;
  174. case 'i':
  175. fIgnoreHidden = TRUE;
  176. break;
  177. case 'j':
  178. fIgnoreSystem = TRUE;
  179. break;
  180. case 'r':
  181. fOverwriteRO = TRUE;
  182. break;
  183. case 'L':
  184. fLarge = TRUE;
  185. break;
  186. case 'A':
  187. fAllowError = TRUE;
  188. break;
  189. default:
  190. Usage ( "Invalid switch - ", p, 0 );
  191. }
  192. }
  193. if (fStructure && fDelete)
  194. Usage ("Only one of /d and /s may be specified at a time", 0);
  195. if (c != 2)
  196. Usage (0);
  197. if (rootpath (v[0], source))
  198. Usage ("Invalid source", v[0], 0);
  199. if (rootpath (v[1], dest))
  200. Usage ("Invalid dest", v[1], 0); /* M000 */
  201. srclen = strlen (source);
  202. dstlen = strlen (dest);
  203. if (!strcmp(source, dest))
  204. Usage ("Source == dest == ", source, 0);
  205. fbuf.fbuf.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
  206. drv = toupper(*dest) - 'A' + 1;
  207. CopyNode (source, &fbuf, NULL);
  208. return( fRebootNecessary ? 2 : 0 );
  209. }
  210. /* copy node walks the source node and its children (recursively)
  211. * and creats the appropriate parts on the dst node
  212. */
  213. void
  214. CopyNode (
  215. char *p,
  216. struct findType *pfb,
  217. void *dummy
  218. )
  219. {
  220. char *pend;
  221. int attr;
  222. flagType fCopy;
  223. flagType fDestRO;
  224. DWORD Status;
  225. char *pszError;
  226. FormDest (p);
  227. if (TESTFLAG (pfb->fbuf.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY)) {
  228. /* If we're to exclude subdirectories, and we're in one then
  229. * skip it altogether
  230. */
  231. if (fNoSub && fInCopyNode)
  232. return;
  233. fInCopyNode = TRUE;
  234. /* Skip the . and .. entries; they're useless
  235. */
  236. if (!strcmp (pfb->fbuf.cFileName, ".") || !strcmp (pfb->fbuf.cFileName, ".."))
  237. return;
  238. /* if we're excluding hidden and this one is then
  239. * skip it altogether
  240. */
  241. if (!fHidden && TESTFLAG (pfb->fbuf.dwFileAttributes, FILE_ATTRIBUTE_HIDDEN))
  242. return;
  243. /* if we're not just outputting the list of files then
  244. * Make sure that the destination dir exists
  245. */
  246. if ( !fFiles ) {
  247. ChkSpace(drv, 256);
  248. }
  249. MakeDir (dest);
  250. pend = strend (p);
  251. if (!fPathChr (pend[-1]))
  252. strcat (p, "\\");
  253. strcat (p, "*.*");
  254. forfile (p, FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM, CopyNode, NULL);
  255. *pend = '\0';
  256. /* if we're not just outputting files then
  257. * if we're to delete this node then
  258. * ...
  259. */
  260. if (!fFiles)
  261. if (fDelete)
  262. if (_rmdir (p) == -1)
  263. Usage ("Unable to rmdir ", p, " - ", error (), 0);
  264. }
  265. else
  266. if (!fStructure) {
  267. if (_access(p, 04) == -1) /* If we can read the source */
  268. Usage ("Unable to peek status of ", p, " - ", error (), 0);
  269. /* do not copy the file if:
  270. * fIgnoreHidden && hidden
  271. * fIgnoreSystem && system
  272. * fArchive and archive bit not set
  273. * dest exists &&
  274. * fTime && src <= dest time ||
  275. * dest is readonly && !fOverwriteRO
  276. */
  277. fCopy = (flagType)TRUE;
  278. fDestRO = (flagType)FALSE;
  279. /* If destination exists, check the time of the destination to
  280. * see if we should copy the file
  281. */
  282. if (_access (dest, 00) != -1 ) {
  283. struct _stat srcbuf;
  284. struct _stat dstbuf;
  285. /* We have now determined that both the source and destination
  286. * exist, we now want to check to see if the destination is
  287. * read only, and if the /T switch was specified if the
  288. * destination is newer than the source.
  289. */
  290. if (_stat (p, &srcbuf) != -1) {/* if source is stat'able */
  291. if (_stat (dest, &dstbuf) != -1 ) { /* and destination too, */
  292. attr = GetFileAttributes( dest ); /* get dest's flag */
  293. fDestRO = (flagType)TESTFLAG ( attr, FILE_ATTRIBUTE_READONLY ); /* Flag dest R.O. */
  294. if ( fTime && srcbuf.st_mtime <= dstbuf.st_mtime)
  295. fCopy = FALSE;
  296. else
  297. if ( fDestRO && !fOverwriteRO ) {
  298. if (!fQuiet)
  299. printf ("%s => not copied, destination is read only\n", p);
  300. fCopy = FALSE;
  301. }
  302. }
  303. }
  304. }
  305. if (fCopy && fIgnoreHidden && TESTFLAG (pfb->fbuf.dwFileAttributes, FILE_ATTRIBUTE_HIDDEN))
  306. fCopy = FALSE;
  307. if (fCopy && fIgnoreSystem && TESTFLAG (pfb->fbuf.dwFileAttributes, FILE_ATTRIBUTE_SYSTEM))
  308. fCopy = FALSE;
  309. if (fCopy && fArchive && !TESTFLAG (pfb->fbuf.dwFileAttributes, FILE_ATTRIBUTE_ARCHIVE))
  310. fCopy = FALSE;
  311. if (fCopy) {
  312. if (!fFiles) {
  313. if (fDestRO) {
  314. RSETFLAG (attr, FILE_ATTRIBUTE_READONLY);
  315. SetFileAttributes( dest, attr );
  316. }
  317. _unlink(dest);
  318. ChkSpace(drv, FILESIZE(pfb->fbuf));
  319. }
  320. if (!fQuiet)
  321. printf ("%s => %s\t", p, dest);
  322. Status = NO_ERROR;
  323. pszError = "[OK]";
  324. if (fFiles) {
  325. if (fScript)
  326. printf ("copy %s %s\n", p, dest);
  327. else
  328. printf ("file %s\n", p, dest);
  329. }
  330. else
  331. if (!CopyFile (p, dest, FALSE)) {
  332. pszError = error ();
  333. Status = GetLastError ();
  334. // If we received a sharing violation, we try to perform
  335. // a boot-delayed copy.
  336. do {
  337. if (Status != ERROR_SHARING_VIOLATION)
  338. continue;
  339. if (!fReboot)
  340. continue;
  341. Status = NO_ERROR;
  342. pszError = "[reboot necessary]";
  343. // We attempt to delay this operation until reboot.
  344. // Since there is at least one DLL that we cannot
  345. // rename in this fashion, we perform delayed DELETE
  346. // of unused files.
  347. // get a temp name in the same directory
  348. upd (dest, ".", tempdir);
  349. if (GetTempFileName (tempdir, "tc", 0, tempfile) == 0) {
  350. pszError = error ();
  351. Status = GetLastError ();
  352. continue;
  353. }
  354. // rename dest file to temp name
  355. if (!MoveFileEx (dest, tempfile, MOVEFILE_REPLACE_EXISTING)) {
  356. pszError = error ();
  357. Status = GetLastError ();
  358. DeleteFile (tempfile);
  359. continue;
  360. }
  361. // copy again
  362. if (!CopyFile (p, dest, TRUE)) {
  363. pszError = error ();
  364. Status = GetLastError ();
  365. DeleteFile (dest);
  366. MoveFile (tempfile, dest);
  367. continue;
  368. }
  369. // mark temp for delete
  370. if (!MoveFileEx (tempfile, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)) {
  371. pszError = error ();
  372. Status = GetLastError ();
  373. DeleteFile (dest);
  374. MoveFile (tempfile, dest);
  375. continue;
  376. }
  377. fRebootNecessary = TRUE;
  378. } while (FALSE);
  379. }
  380. /* Display noise if we're not quiet
  381. */
  382. if (!fQuiet)
  383. printf ("%s\n", pszError);
  384. /* If we got an error and we're not supposed to ignore them
  385. * quit and report error
  386. */
  387. if (Status != NO_ERROR)
  388. if (!fAllowError)
  389. Usage ("Unable to copy ", p, " to ", dest, " - ", pszError, 0);
  390. else
  391. printf ("Unable to copy %s to %s - %s\n", p, dest, pszError);
  392. /* If we're not just producing a file list and no error on copy
  393. */
  394. if (!fFiles && Status == NO_ERROR) {
  395. /* If we're supposed to copy archived files and archive was
  396. * set, go reset the source
  397. */
  398. if (fArchive && TESTFLAG (pfb->fbuf.dwFileAttributes, FILE_ATTRIBUTE_ARCHIVE)) {
  399. RSETFLAG (pfb->fbuf.dwFileAttributes, FILE_ATTRIBUTE_ARCHIVE);
  400. if( SetFileAttributes( p, pfb->fbuf.dwFileAttributes ) == -1 )
  401. Usage ("Unable to set ", p, " attributes - ", error (), 0);
  402. }
  403. /* Copy attributes from source to destination
  404. */
  405. SetFileAttributes( dest, pfb->fbuf.dwFileAttributes );
  406. /* If we're supposed to delete the entry
  407. */
  408. if (fDelete) {
  409. /* If the source was read-only then
  410. * reset the source RO bit
  411. */
  412. if (TESTFLAG (pfb->fbuf.dwFileAttributes, FILE_ATTRIBUTE_READONLY))
  413. if( SetFileAttributes( p, 0 ) == -1 )
  414. Usage ("Unable to set attributes of ", " - ", error (), 0);
  415. /* Delete source and report error
  416. */
  417. if (_unlink (p) == -1)
  418. Usage ("Unable to del ", p, " - ", error (), 0);
  419. }
  420. }
  421. }
  422. }
  423. dummy;
  424. }
  425. /* given a source pointer, form the correct destination from it
  426. *
  427. * cases to consider:
  428. *
  429. * source dest p realdest
  430. * D:\path1 D:\path2 D:\path1\path3 D:\path2\path3
  431. * D:\ D:\path1 D:\path2\path3 D:\path1\path2\path3
  432. * D:\path1 D:\ D:\path1\path2 D:\path2
  433. * D:\ D:\ D:\ D:\
  434. */
  435. FormDest (p)
  436. char *p;
  437. {
  438. char *subsrc, *dstend;
  439. subsrc = p + srclen;
  440. if (fPathChr (*subsrc))
  441. subsrc++;
  442. dstend = dest + dstlen;
  443. if (fPathChr (dstend[-1]))
  444. dstend--;
  445. *dstend = '\0';
  446. if (*subsrc != '\0') {
  447. _strlwr(subsrc);
  448. strcat (dest, "\\");
  449. strcat (dest, subsrc);
  450. }
  451. return( 0 );
  452. }
  453. /* attempt to make the directory in pieces */
  454. void MakeDir (p)
  455. char *p;
  456. {
  457. struct _stat dbuf;
  458. char *pshort;
  459. int i;
  460. if (strlen (p) > 3) {
  461. if (_stat (p, &dbuf) != -1)
  462. if (!TESTFLAG (dbuf.st_mode, S_IFDIR))
  463. Usage (p, " is a file", 0);
  464. else
  465. return;
  466. pshort = strend (p);
  467. while (pshort > p)
  468. if (fPathChr (*pshort))
  469. break;
  470. else
  471. pshort--;
  472. /* pshort points to last path separator */
  473. *pshort = 0;
  474. MakeDir (p);
  475. *pshort = '\\';
  476. if (!fQuiet)
  477. printf ("Making %s\t", p);
  478. if (fFiles)
  479. if (fScript)
  480. printf ("mkdir %s\n", p);
  481. else
  482. printf ("dir %s\n", p);
  483. else {
  484. i = _mkdir (p);
  485. if (!fQuiet)
  486. printf ("%s\n", i != -1 ? "[OK]" : "");
  487. if (i == -1)
  488. Usage ("Unable to mkdir ", p, " - ", error (), 0);
  489. }
  490. }
  491. }