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