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.

412 lines
14 KiB

  1. /*++
  2. Copyright (c) 1988-1999 Microsoft Corporation
  3. Module Name:
  4. cpparse.c
  5. Abstract:
  6. Parsing for the copy command
  7. --*/
  8. #include "cmd.h"
  9. /* useful macro */
  10. #define Wild(spec) ((spec)->flags & (CI_NAMEWILD))
  11. /* Globals from cpwork.c */
  12. extern int copy_mode;
  13. extern unsigned DosErr ;
  14. /* Globals from command */
  15. extern TCHAR SwitChar;
  16. extern BOOLEAN VerifyCurrent; // cpwork.c
  17. /* parse_args
  18. *
  19. * This is the key function which decides how copy will react to any
  20. * given invocation. It parses the arguments, fills the source and
  21. * destination structures, and sets the copy_mode.
  22. *
  23. * ENTRY
  24. *
  25. * args - raw argument line entered by the user
  26. *
  27. * source - pointer to an initialized cpyinfo struct. This one isn't
  28. * used; it's just a header to a linked list containing the
  29. * actual source structures.
  30. *
  31. * dest - like source, but there will be at most one filled-in dest struct.
  32. *
  33. * EXIT
  34. *
  35. * source - the caller's source pointer has not, of course, been changed. It
  36. * still points to the empty header (which might not be completely
  37. * empty - see handle_switch for details). It is the head of a
  38. * linked list of structures, terminated by one with a NULL in its
  39. * next field. Each structure corresponds to a source spec.
  40. *
  41. * dest - if a destination was specified, the caller's dest pointer points
  42. * to an empty header which points to the actual destination struct.
  43. * If the destination is implicit, the fspec and next fields of the
  44. * struct are still NULL, but the flag field has been filled in with
  45. * the copy mode. If the user specified a copy mode (ascii or
  46. * binary) one or more times, the last switch applies to the
  47. * destination. If not, CI_NOTSET is used.
  48. *
  49. * copy_mode - set to type of copy being done - COPY, COMBINE, CONCAT, or
  50. * TOUCH.
  51. *
  52. */
  53. void
  54. parse_args(
  55. PTCHAR args,
  56. PCPYINFO source,
  57. PCPYINFO dest)
  58. {
  59. TCHAR *tas; /* tokenized argument string */
  60. TCHAR copydelims[4]; /* copy token delimters */
  61. int parse_state = SEEN_NO_FILES; /* state of the parser */
  62. int all_sources_wildcards = TRUE, /* flag to help decide copy mode */
  63. number_of_sources = 0, /* number of specs seen so far */
  64. current_copy_mode = CI_NOTSET, /* ascii, binary, or not set */
  65. tlen; /* offset to next token */
  66. BOOL ShortNameSwitch=FALSE;
  67. BOOL RestartableSwitch=FALSE;
  68. BOOL PromptOnOverwrite;
  69. copydelims[0] = PLUS; /* delimiters for token parser */
  70. copydelims[1] = COMMA;
  71. copydelims[2] = SwitChar;
  72. copydelims[3] = NULLC;
  73. //
  74. // Get default prompt okay flag from COPYCMD variable. Allow
  75. // user to override with /Y or /-Y switch. Always assume /Y
  76. // if command executed from inside batch script or via CMD.EXE
  77. // command line switch (/C or /K)
  78. //
  79. if (SingleBatchInvocation || SingleCommandInvocation || CurrentBatchFile != 0)
  80. PromptOnOverwrite = FALSE; // Assume /Y
  81. else
  82. PromptOnOverwrite = TRUE; // Assume /-Y
  83. GetPromptOkay(MyGetEnvVarPtr(TEXT("COPYCMD")), &PromptOnOverwrite);
  84. if (!*(tas = TokStr(args, copydelims, TS_SDTOKENS))) /* tokenize args */
  85. copy_error(MSG_BAD_SYNTAX,CE_NOPCOUNT); /* M003 */
  86. for ( ; *tas ; tas += tlen+1 ) /* cycle through tokens in args */
  87. {
  88. tlen = mystrlen(tas);
  89. switch(*tas) {
  90. case PLUS:
  91. if (parse_state != JUST_SEEN_SOURCE_FILE)
  92. /* M003 */ copy_error(MSG_BAD_SYNTAX,CE_NOPCOUNT);
  93. parse_state = SEEN_PLUS_EXPECTING_SOURCE_FILE;
  94. break;
  95. case COMMA:
  96. if (parse_state == SEEN_COMMA_EXPECTING_SECOND)
  97. parse_state = SEEN_TWO_COMMAS;
  98. else if ((parse_state == SEEN_PLUS_EXPECTING_SOURCE_FILE) &&
  99. (number_of_sources == 1))
  100. parse_state = SEEN_COMMA_EXPECTING_SECOND;
  101. else if (parse_state != JUST_SEEN_SOURCE_FILE)
  102. /* M003 */ copy_error(MSG_BAD_SYNTAX,CE_NOPCOUNT);
  103. break;
  104. default: /* file or switch */
  105. if (*tas == SwitChar) {
  106. handle_switch(tas,source,dest,parse_state,
  107. &current_copy_mode,
  108. &ShortNameSwitch,
  109. &RestartableSwitch,
  110. &PromptOnOverwrite
  111. );
  112. tlen = 2 + _tcslen(&tas[2]); /* offset past switch */
  113. }
  114. else
  115. /*509*/ { /* must be device or file */
  116. /*509*/ mystrcpy(tas, StripQuotes(tas));
  117. parse_state = found_file(tas,parse_state,&source,&dest,
  118. &number_of_sources,&all_sources_wildcards,current_copy_mode);
  119. /*509*/ }
  120. break;
  121. }
  122. }
  123. /* set copy mode appropriately */
  124. set_mode(number_of_sources,parse_state,all_sources_wildcards,dest);
  125. if (ShortNameSwitch)
  126. source->flags |= CI_SHORTNAME;
  127. if (PromptOnOverwrite)
  128. dest->flags |= CI_PROMPTUSER;
  129. if (RestartableSwitch)
  130. //
  131. // If running on a platform that does not support CopyFileEx
  132. // display an error message if they try to use /Z option
  133. //
  134. #ifndef WIN95_CMD
  135. if (lpCopyFileExW != NULL)
  136. source->flags |= CI_RESTARTABLE;
  137. else
  138. #endif
  139. copy_error(MSG_NO_COPYFILEEX,CE_NOPCOUNT);
  140. /* if no dest specified, put current copy mode in header */
  141. if (number_of_sources != 0) /*M005 if sources specd */
  142. { /*M005 then */
  143. if (parse_state != SEEN_DEST) /*M005 if seen a destspec*/
  144. { /*M005 then */
  145. dest->flags = current_copy_mode; /*M005 save cur mode */
  146. if (PromptOnOverwrite)
  147. dest->flags |= CI_PROMPTUSER;
  148. } /*M005 endif */
  149. } /*M005 */
  150. else /*M005 */
  151. { /*M005 else */
  152. copy_error(MSG_BAD_SYNTAX,CE_NOPCOUNT); /*M005 disp inv#parms */
  153. } /*M005 endif */
  154. }
  155. /* handle_switch
  156. *
  157. * There are four switches to handle: /A, /B, /F,
  158. * /V
  159. *
  160. * /B and /A set the copy mode to binary and ascii respectively.
  161. * This change applies to the previous filespec and all succeeding ones.
  162. * Figure out which was the last filespec read and set its flags; then set
  163. * the current copy mode.
  164. *
  165. * Note: If there was no previous filespec, the source pointer is pointing
  166. * at an unitialized header. In this case, we set the flags in this
  167. * struct to the current copy mode. This doesn't accomplish
  168. * anything, but the code is simpler if it doesn't bother to check.
  169. *
  170. * /F indicates that the copy should fail if we can't copy the EAs.
  171. *
  172. * /V enables the much slower verified copy mode. This is very
  173. * easy to handle; call a magic internal DOS routine. All writes are then
  174. * verified automagically without our interference.
  175. *
  176. *
  177. */
  178. void handle_switch(
  179. TCHAR *tas,
  180. PCPYINFO source,
  181. PCPYINFO dest,
  182. int parse_state,
  183. int *current_copy_mode,
  184. PBOOL ShortNameSwitch,
  185. PBOOL RestartableSwitch,
  186. PBOOL PromptOnOverwrite
  187. )
  188. {
  189. TCHAR ch = (TCHAR) _totupper(tas[2]);
  190. TCHAR szTmp[16];
  191. if (_tcslen(&tas[2]) < 14) {
  192. _tcscpy(szTmp,tas);
  193. _tcscat(szTmp,&tas[2]);
  194. if (GetPromptOkay(szTmp, PromptOnOverwrite))
  195. return;
  196. }
  197. if (ch == TEXT( 'A' ) || ch == TEXT( 'B' )) {
  198. *current_copy_mode = (ch == TEXT( 'A' ) ? CI_ASCII : CI_BINARY);
  199. if (parse_state == SEEN_DEST) { /* then prev spec was dest */
  200. dest->flags &= (~CI_ASCII) & (~CI_BINARY) & (~CI_NOTSET);
  201. dest->flags |= *current_copy_mode;
  202. }
  203. else { /* set last source spec */
  204. source->flags &= (~CI_ASCII) & (~CI_BINARY) & (~CI_NOTSET);
  205. source->flags |= *current_copy_mode;
  206. }
  207. }
  208. else if (ch == TEXT( 'V' )) {
  209. VerifyCurrent = 1;
  210. }
  211. else if (ch == TEXT( 'N' )) {
  212. *ShortNameSwitch = TRUE;
  213. }
  214. else if (ch == TEXT( 'Z' )) {
  215. *RestartableSwitch = TRUE;
  216. }
  217. else if (ch == TEXT( 'D' )) {
  218. *current_copy_mode |= CI_ALLOWDECRYPT;
  219. dest->flags |= CI_ALLOWDECRYPT;
  220. }
  221. else {
  222. copy_error(MSG_BAD_SYNTAX,CE_NOPCOUNT); /* M003 */
  223. }
  224. }
  225. /* found_file
  226. *
  227. * Token was a file or device. Put it in the appropriate structure and
  228. * run ScanFSpec on it. Figure out what the new parser state should be
  229. * and return it. Note: This function has one inelegant side-effect; if
  230. * it sees a destination file after a double-comma ("copy foo+,, bar"), it
  231. * sets the copy_mode to TOUCH. Otherwise the copier wouldn't remember to
  232. * use the current date and time with the copied file. Set_mode notices that
  233. * the mode is TOUCH and doesn't change it. This works because the copy modes
  234. * CONCAT, COMBINE, COPY, and TOUCH are mutually exclusive in all but this one
  235. * case where we both copy and touch at once.
  236. *
  237. */
  238. int
  239. found_file(
  240. PTCHAR token,
  241. int parse_state,
  242. PCPYINFO *source,
  243. PCPYINFO *dest,
  244. int *num_sources,
  245. int *all_sources_wild,
  246. int mode)
  247. {
  248. PCPYINFO add_filespec_to_struct();
  249. /* if it's a source, add to the list of source structures */
  250. if ((parse_state == SEEN_NO_FILES) ||
  251. (parse_state == SEEN_PLUS_EXPECTING_SOURCE_FILE)) {
  252. *source = add_filespec_to_struct(*source,token,mode);
  253. ScanFSpec(*source);
  254. //
  255. // Could have an abort from access to floppy etc. so get out
  256. // of copy. If it is just an invalid name then proceed since
  257. // it is a wild card. If is actually invalid we will catch
  258. // this later.
  259. if (DosErr != SUCCESS
  260. && DosErr != ERROR_INVALID_NAME
  261. #ifdef WIN95_CMD
  262. && (!Wild(*source) || DosErr != ERROR_FILE_NOT_FOUND)
  263. #endif
  264. ) {
  265. copy_error(DosErr, CE_NOPCOUNT);
  266. }
  267. parse_state = JUST_SEEN_SOURCE_FILE;
  268. (*num_sources)++;
  269. if (!Wild(*source))
  270. *all_sources_wild = FALSE;
  271. }
  272. /* if it's a dest, make it the dest structure */
  273. else if ((parse_state == SEEN_TWO_COMMAS) ||
  274. (parse_state == JUST_SEEN_SOURCE_FILE)) {
  275. if (parse_state == SEEN_TWO_COMMAS)
  276. copy_mode = TOUCH;
  277. *dest = add_filespec_to_struct(*dest,token,mode);
  278. ScanFSpec(*dest);
  279. //
  280. // Could have an abort from access to floppy etc. so get out
  281. // of copy
  282. //
  283. if ((DosErr) && (DosErr != ERROR_INVALID_NAME)) {
  284. copy_error(DosErr, CE_NOPCOUNT);
  285. }
  286. parse_state = SEEN_DEST;
  287. }
  288. /* if we have a dest or the syntax is messed up, complain */
  289. else
  290. copy_error(MSG_BAD_SYNTAX,CE_NOPCOUNT); /* M003 */
  291. return(parse_state);
  292. }
  293. /* set_mode
  294. *
  295. * Given all the current state information, determine which kind of copy
  296. * is being done and set the copy_mode. As explained in found_file, if
  297. * the mode has been set to TOUCH, set_mode doesn't do anything.
  298. */
  299. void set_mode(number_sources,parse_state,all_sources_wildcards,dest)
  300. int number_sources,
  301. parse_state,
  302. all_sources_wildcards;
  303. PCPYINFO dest;
  304. {
  305. if (copy_mode == TOUCH) /* tacky special case */
  306. return;
  307. /* If there was one source, we are doing a touch, a concatenate,
  308. * or a copy. It's a touch if there was one file and we saw a "+" or a
  309. * "+,,". If the source was a wildcard and the destination is a file,
  310. * it's a concatenate. Otherwise it's a copy.
  311. */
  312. if (number_sources == 1) {
  313. if ((parse_state == SEEN_TWO_COMMAS) ||
  314. (parse_state == SEEN_PLUS_EXPECTING_SOURCE_FILE))
  315. copy_mode = TOUCH;
  316. else if (all_sources_wildcards && dest->fspec && !Wild(dest) &&
  317. !(*lastc(dest->fspec) == COLON))
  318. copy_mode = CONCAT;
  319. }
  320. /* For more than one source, we are combining or concatenating. It's
  321. * a combine if all sources were wildcards and the destination is either
  322. * a wildcard, a directory, or implicit. Otherwise it's a concatenation.
  323. */
  324. else {
  325. if ((all_sources_wildcards) &&
  326. ((!dest->fspec) || Wild(dest) ||
  327. (*lastc(dest->fspec) == COLON)))
  328. copy_mode = COMBINE;
  329. else
  330. copy_mode = CONCAT;
  331. }
  332. DEBUG((FCGRP,COLVL,"Set flags: copy_mode = %d",copy_mode));
  333. }
  334. /* add_filespec_to_struct
  335. *
  336. * ENTRY
  337. *
  338. * spec - points to a filled structure with a NULL in its next field.
  339. *
  340. * file_spec - filename to put in new structure
  341. *
  342. * mode - copy mode
  343. *
  344. * EXIT
  345. *
  346. * Return a pointer to a new cpyinfo struct with its fields filled in
  347. * appropriately. The old spec struct's next field points to this new
  348. * structure.
  349. *
  350. */
  351. PCPYINFO
  352. add_filespec_to_struct(spec,file_spec,mode)
  353. PCPYINFO spec;
  354. TCHAR *file_spec;
  355. int mode;
  356. {
  357. spec->next = NewCpyInfo( );
  358. spec = spec->next;
  359. spec->fspec = file_spec;
  360. spec->flags |= mode;
  361. return(spec);
  362. }