1 /* Copyright 1997-2002,2005-2009 Alain Knaff.
2 * This file is part of mtools.
4 * Mtools is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Mtools is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
18 * Iterating over all the command line parameters, and matching patterns
22 #include "sysincludes.h"
28 #include "file_name.h"
31 /* Fix the info in the MCWD file to be a proper directory name.
32 * Always has a leading separator. Never has a trailing separator
33 * (unless it is the path itself). */
35 static const char *fix_mcwd(char *ans)
42 if(!fp || !fgets(buf, MAX_PATH, fp)) {
45 ans[0] = get_default_drive();
50 buf[strlen(buf) -1] = '\0';
52 /* drive letter present? */
54 if (buf[0] && buf[1] == ':') {
59 ans[0] = get_default_drive();
62 /* add a leading separator */
63 if (*s != '/' && *s != '\\') {
70 /* translate to upper case */
71 for (s = ans; *s; ++s) {
77 /* if only drive, colon, & separator */
80 /* zap the trailing separator */
86 static int _unix_loop(Stream_t *Dir, MainParam_t *mp,
87 const char *filename UNUSEDP)
89 return unix_dir_loop(Dir, mp);
92 int unix_loop(Stream_t *Stream UNUSEDP, MainParam_t *mp,
93 char *arg, int follow_dir_link)
97 size_t unixNameLength;
101 unixNameLength = strlen(arg);
102 if(unixNameLength > 1 && arg[unixNameLength-1] == '/') {
103 /* names ending in slash, and having at least two characters */
104 char *name = strdup(arg);
105 name[unixNameLength-1]='\0';
106 mp->unixSourceName = name;
108 mp->unixSourceName = arg;
110 /* mp->dir.attr = ATTR_ARCHIVE;*/
111 mp->loop = _unix_loop;
112 if((mp->lookupflags & DO_OPEN)){
113 mp->File = SimpleFileOpen(0, 0, arg, O_RDONLY, 0, 0, 0, 0);
117 tmp = _basename(arg);
118 strncpy(mp->filename, tmp, VBUFSIZE);
119 mp->filename[VBUFSIZE-1] = '\0';
123 GET_DATA(mp->File, 0, 0, &isdir, 0);
125 #if !defined(__EMX__) && !defined(OS_mingw32msvc)
130 #if !defined(__EMX__) && !defined(OS_mingw32msvc)
131 if(!follow_dir_link &&
132 MT_LSTAT(arg, &buf) == 0 &&
133 S_ISLNK(buf.st_mode)) {
134 /* skip links to directories in order to avoid
137 "skipping directory symlink %s\n",
142 if(! (mp->lookupflags & ACCEPT_DIR))
144 mp->File = OpenDir(arg);
149 ret = mp->dirCallback(0, mp);
151 ret = mp->unixcallback(mp);
156 static int checkForDot(int lookupflags, const wchar_t *name)
158 return (lookupflags & NO_DOTS) && isSpecialW(name);
162 typedef struct lookupState_t {
167 const char *filename;
170 static int isUniqueTarget(const char *name)
172 return name && strcmp(name, "-");
175 static int handle_leaf(direntry_t *direntry, MainParam_t *mp,
176 lookupState_t *lookupState,
177 Stream_t **DeferredFileP)
185 /* we are looking for a "target" file */
186 switch(lookupState->nbDirs) {
187 case 0: /* no directory yet, open it */
188 lookupState->Dir = OpenFileByDirentry(direntry);
189 lookupState->nbDirs++;
190 /* dump the container, we have
192 FREE(&lookupState->container);
194 case 1: /* we have already a directory */
195 FREE(&lookupState->Dir);
196 fprintf(stderr,"Ambiguous\n");
197 return STOP_NOW | ERROR_ONE;
199 return STOP_NOW | ERROR_ONE;
203 mp->direntry = direntry;
204 if(IS_DIR(direntry)) {
205 if(mp->lookupflags & (DO_OPEN | DO_OPEN_DIRS))
206 MyFile = mp->File = OpenFileByDirentry(direntry);
207 ret = mp->dirCallback(direntry, mp);
209 if(mp->lookupflags & DO_OPEN) {
210 if(DeferredFileP && *DeferredFileP) {
211 /* Already a deferred file => close it and error */
214 "Attempt to copy multiple files to non-directory\n");
215 return STOP_NOW | ERROR_ONE;
218 MyFile = mp->File = OpenFileByDirentry(direntry);
220 *DeferredFileP = MyFile;
224 ret = mp->callback(direntry, mp);
230 static int _dos_loop(Stream_t *Dir, MainParam_t *mp, const char *filename)
239 initializeDirentry(&entry, Dir);
241 (r=vfat_lookup_zt(&entry, filename,
243 mp->shortname.data, mp->shortname.len,
244 mp->longname.data, mp->longname.len)) == 0 ){
246 if(!checkForDot(mp->lookupflags,entry.name)) {
248 if((mp->lookupflags & DO_OPEN) ||
250 (mp->lookupflags & DO_OPEN_DIRS))) {
251 MyFile = mp->File = OpenFileByDirentry(&entry);
255 mp->direntry = &entry;
257 ret |= mp->dirCallback(&entry,mp);
259 ret |= mp->callback(&entry, mp);
264 if(mp->fast_quit && (ret & ERROR_ONE))
274 static int recurs_dos_loop(MainParam_t *mp, const char *filename0,
275 const char *filename1,
276 lookupState_t *lookupState,
277 Stream_t **DeferredFileP)
279 /* Dir is de-allocated by the same entity which allocated it */
290 /* strip dots and / */
291 if(!strncmp(filename0,"./", 2)) {
295 if(!strcmp(filename0,".") && filename1) {
299 if(filename0[0] == '/') {
306 filename0 = filename1;
313 if(!strncmp(filename0,"../", 3) ||
314 (!strcmp(filename0, "..") && filename1)) {
316 mp->File = getDirentry(mp->File)->Dir;
317 return recurs_dos_loop(mp, filename0+2, filename1, lookupState,
321 doing_mcwd = !!filename1;
323 ptr = strchr(filename0, '/');
325 length = strlen(filename0);
329 length = ptrdiff(ptr, filename0);
333 if(mp->lookupflags & OPEN_PARENT) {
334 mp->targetName = filename0;
335 ret = handle_leaf(getDirentry(mp->File), mp,
341 if(!strcmp(filename0, ".") || !filename0[0]) {
342 return handle_leaf(getDirentry(mp->File),
343 mp, lookupState, NULL);
346 if(!strcmp(filename0, "..")) {
347 return handle_leaf(getParent(getDirentry(mp->File)), mp,
351 lookupflags = mp->lookupflags;
354 lookupState->filename = filename0;
355 if(lookupState->nbContainers + lookupState->nbDirs > 0){
356 /* we have already one target, don't bother
358 FREE(&lookupState->container);
360 /* no match yet. Remember this container for
362 lookupState->container = COPY(mp->File);
364 lookupState->nbContainers++;
367 lookupflags = ACCEPT_DIR | DO_OPEN | NO_DOTS;
372 initializeDirentry(&entry, mp->File);
373 while(!(ret & STOP_NOW) &&
375 (r=vfat_lookup(&entry, filename0, length,
376 lookupflags | NO_MSG,
377 mp->shortname.data, mp->shortname.len,
378 mp->longname.data, mp->longname.len)) == 0 ){
379 if(checkForDot(lookupflags, entry.name))
380 /* while following the path, ignore the
381 * special entries if they were not
382 * explicitly given */
387 SubDir = mp->File = OpenFileByDirentry(&entry);
388 ret |= recurs_dos_loop(mp, ptr, filename1, lookupState,
392 ret |= handle_leaf(&entry, mp, lookupState,
401 return ret | ERROR_ONE;
402 if(doing_mcwd && !have_one)
407 static int common_dos_loop(MainParam_t *mp, const char *pathname,
408 lookupState_t *lookupState, int open_mode)
414 Stream_t *DeferredFile=NULL;
415 Stream_t **DeferredFileP=NULL;
418 mp->loop = _dos_loop;
422 if(*pathname && pathname[1] == ':') {
423 drive = ch_toupper(*pathname);
425 if(mp->mcwd[0] == drive)
427 } else if(mp->mcwd[0]) {
431 drive = get_default_drive();
434 if(*pathname=='/') /* absolute path name */
437 RootDir = mp->File = open_root_dir(drive, open_mode, NULL);
441 if(mp->originalArg && strpbrk(mp->originalArg, "*[?") != 0 &&
442 (mp->lookupflags & DEFERABLE) &&
443 isUniqueTarget(mp->targetName))
444 DeferredFileP = &DeferredFile;
446 ret = recurs_dos_loop(mp, cwd, pathname, lookupState, DeferredFileP);
451 ret = recurs_dos_loop(mp, "", pathname, lookupState, DeferredFileP);
454 mp->File = DeferredFile;
455 ret = mp->callback(NULL, mp);
462 static int dos_loop(MainParam_t *mp, const char *arg)
464 return common_dos_loop(mp, arg, 0, mp->openflags);
468 int dos_target_lookup(MainParam_t *mp, const char *arg)
470 lookupState_t lookupState;
474 lookupState.nbDirs = 0;
476 lookupState.nbContainers = 0;
477 lookupState.container = 0;
479 lookupflags = mp->lookupflags;
480 mp->lookupflags = DO_OPEN | ACCEPT_DIR;
481 ret = common_dos_loop(mp, arg, &lookupState, O_RDWR);
482 mp->lookupflags = lookupflags;
486 if(lookupState.nbDirs) {
488 mp->targetDir = lookupState.Dir;
489 FREE(&lookupState.container); /* container no longer needed */
493 switch(lookupState.nbContainers) {
496 fprintf(stderr,"%s: no match for target\n", arg);
499 mp->targetName = strdup(lookupState.filename);
500 mp->targetDir = lookupState.container;
504 fprintf(stderr, "Ambiguous %s\n", arg);
509 int main_loop(MainParam_t *mp, char **argv, int argc)
516 if(argc != 1 && mp->targetName) {
518 "Several file names given, but last argument (%s) not a directory\n", mp->targetName);
519 FREE(&mp->targetDir);
523 for (i = 0; i < argc; i++) {
526 mp->originalArg = argv[i];
527 mp->basenameHasWildcard = strpbrk(_basename(mp->originalArg),
529 if (mp->unixcallback && (!argv[i][0]
530 #ifdef OS_mingw32msvc
531 /* On Windows, support only the command-line image drive. */
534 || argv[i][1] != ':' ))
535 ret = unix_loop(0, mp, argv[i], 1);
537 ret = dos_loop(mp, argv[i]);
539 if (! (ret & (GOT_ONE | ERROR_ONE)) ) {
540 /* one argument was unmatched */
541 fprintf(stderr, "%s: File \"%s\" not found\n",
546 if(mp->fast_quit && (Bret & (MISSED_ONE | ERROR_ONE)))
549 FREE(&mp->targetDir);
552 if ((Bret & GOT_ONE) && ( Bret & MISSED_ONE))
554 if (Bret & MISSED_ONE)
559 static int dispatchToFile(direntry_t *entry, MainParam_t *mp)
562 return mp->callback(entry, mp);
564 return mp->unixcallback(mp);
568 void init_mp(MainParam_t *mp)
571 mp->openflags = O_RDONLY;
575 mp->dirCallback = dispatchToFile;
576 mp->unixcallback = NULL;
577 mp->shortname.data = mp->longname.data = 0;
578 mp->shortname.len = mp->longname.len = 0;
584 const char *mpGetBasename(MainParam_t *mp)
587 wchar_to_native(mp->direntry->name, mp->targetBuffer,
588 MAX_VNAMELEN+1, sizeof(mp->targetBuffer));
589 return mp->targetBuffer;
591 return _basename(mp->unixSourceName);
594 void mpPrintFilename(FILE *fp, MainParam_t *mp)
597 fprintPwd(fp, mp->direntry, 0);
599 fprintf(fp,"%s",mp->originalArg);
602 const char *mpPickTargetName(MainParam_t *mp)
604 /* picks the target name: either the one explicitly given by the
605 * user, or the same as the source */
607 return mp->targetName;
609 return mpGetBasename(mp);