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"
30 #include "file_name.h"
33 /* Fix the info in the MCWD file to be a proper directory name.
34 * Always has a leading separator. Never has a trailing separator
35 * (unless it is the path itself). */
37 static const char *fix_mcwd(char *ans)
44 if(!fp || !fgets(buf, MAX_PATH, fp)) {
47 ans[0] = get_default_drive();
52 buf[strlen(buf) -1] = '\0';
54 /* drive letter present? */
56 if (buf[0] && buf[1] == ':') {
61 ans[0] = get_default_drive();
64 /* add a leading separator */
65 if (*s != '/' && *s != '\\') {
72 /* translate to upper case */
73 for (s = ans; *s; ++s) {
79 /* if only drive, colon, & separator */
82 /* zap the trailing separator */
88 int unix_dir_loop(Stream_t *Stream, MainParam_t *mp);
89 int unix_loop(UNUSED(Stream_t *Stream), MainParam_t *mp, char *arg,
92 static int _unix_loop(Stream_t *Dir, MainParam_t *mp,
93 const char *filename UNUSEDP)
95 return unix_dir_loop(Dir, mp);
98 int unix_loop(UNUSED(Stream_t *Stream), MainParam_t *mp,
99 char *arg, int follow_dir_link)
107 unixNameLength = strlen(arg);
108 if(unixNameLength > 1 && arg[unixNameLength-1] == '/') {
109 /* names ending in slash, and having at least two characters */
110 char *name = strdup(arg);
111 name[unixNameLength-1]='\0';
112 mp->unixSourceName = name;
114 mp->unixSourceName = arg;
116 /* mp->dir.attr = ATTR_ARCHIVE;*/
117 mp->loop = _unix_loop;
118 if((mp->lookupflags & DO_OPEN)){
119 mp->File = SimpleFileOpen(0, 0, arg, O_RDONLY, 0, 0, 0, 0);
123 tmp = _basename(arg);
124 strncpy(mp->filename, tmp, VBUFSIZE);
125 mp->filename[VBUFSIZE-1] = '\0';
129 GET_DATA(mp->File, 0, 0, &isdir, 0);
131 #if !defined(__EMX__) && !defined(OS_mingw32msvc)
136 #if !defined(__EMX__) && !defined(OS_mingw32msvc)
137 if(!follow_dir_link &&
138 MT_LSTAT(arg, &buf) == 0 &&
139 S_ISLNK(buf.st_mode)) {
140 /* skip links to directories in order to avoid
143 "skipping directory symlink %s\n",
148 if(! (mp->lookupflags & ACCEPT_DIR))
150 mp->File = OpenDir(arg);
155 ret = mp->dirCallback(0, mp);
157 ret = mp->unixcallback(mp);
163 int isSpecial(const char *name)
167 if(!strcmp(name,"."))
169 if(!strcmp(name,".."))
175 int isSpecialW(const wchar_t *name)
179 if(!wcscmp(name,L"."))
181 if(!wcscmp(name,L".."))
187 static int checkForDot(int lookupflags, const wchar_t *name)
189 return (lookupflags & NO_DOTS) && isSpecialW(name);
193 typedef struct lookupState_t {
198 const char *filename;
201 static int isUniqueTarget(const char *name)
203 return name && strcmp(name, "-");
206 static int handle_leaf(direntry_t *direntry, MainParam_t *mp,
207 lookupState_t *lookupState)
215 /* we are looking for a "target" file */
216 switch(lookupState->nbDirs) {
217 case 0: /* no directory yet, open it */
218 lookupState->Dir = OpenFileByDirentry(direntry);
219 lookupState->nbDirs++;
220 /* dump the container, we have
222 FREE(&lookupState->container);
224 case 1: /* we have already a directory */
225 FREE(&lookupState->Dir);
226 fprintf(stderr,"Ambigous\n");
227 return STOP_NOW | ERROR_ONE;
229 return STOP_NOW | ERROR_ONE;
233 mp->direntry = direntry;
234 if(IS_DIR(direntry)) {
235 if(mp->lookupflags & (DO_OPEN | DO_OPEN_DIRS))
236 MyFile = mp->File = OpenFileByDirentry(direntry);
237 ret = mp->dirCallback(direntry, mp);
239 if(mp->lookupflags & DO_OPEN)
240 MyFile = mp->File = OpenFileByDirentry(direntry);
241 ret = mp->callback(direntry, mp);
244 if(isUniqueTarget(mp->targetName))
249 static int _dos_loop(Stream_t *Dir, MainParam_t *mp, const char *filename)
258 initializeDirentry(&entry, Dir);
260 (r=vfat_lookup(&entry, filename, -1,
261 mp->lookupflags, mp->shortname,
262 mp->longname)) == 0 ){
264 if(!checkForDot(mp->lookupflags,entry.name)) {
266 if((mp->lookupflags & DO_OPEN) ||
268 (mp->lookupflags & DO_OPEN_DIRS))) {
269 MyFile = mp->File = OpenFileByDirentry(&entry);
273 mp->direntry = &entry;
275 ret |= mp->dirCallback(&entry,mp);
277 ret |= mp->callback(&entry, mp);
282 if(mp->fast_quit && (ret & ERROR_ONE))
292 static int recurs_dos_loop(MainParam_t *mp, const char *filename0,
293 const char *filename1,
294 lookupState_t *lookupState)
296 /* Dir is de-allocated by the same entity which allocated it */
307 /* strip dots and / */
308 if(!strncmp(filename0,"./", 2)) {
312 if(!strcmp(filename0,".") && filename1) {
316 if(filename0[0] == '/') {
323 filename0 = filename1;
330 if(!strncmp(filename0,"../", 3) ||
331 (!strcmp(filename0, "..") && filename1)) {
333 mp->File = getDirentry(mp->File)->Dir;
334 return recurs_dos_loop(mp, filename0+2, filename1, lookupState);
337 doing_mcwd = !!filename1;
339 ptr = strchr(filename0, '/');
341 length = strlen(filename0);
345 length = ptr - filename0;
349 if(mp->lookupflags & OPEN_PARENT) {
350 mp->targetName = filename0;
351 ret = handle_leaf(getDirentry(mp->File), mp,
357 if(!strcmp(filename0, ".") || !filename0[0]) {
358 return handle_leaf(getDirentry(mp->File),
362 if(!strcmp(filename0, "..")) {
363 return handle_leaf(getParent(getDirentry(mp->File)), mp,
367 lookupflags = mp->lookupflags;
370 lookupState->filename = filename0;
371 if(lookupState->nbContainers + lookupState->nbDirs > 0){
372 /* we have already one target, don't bother
374 FREE(&lookupState->container);
376 /* no match yet. Remember this container for
378 lookupState->container = COPY(mp->File);
380 lookupState->nbContainers++;
383 lookupflags = ACCEPT_DIR | DO_OPEN | NO_DOTS;
388 initializeDirentry(&entry, mp->File);
389 while(!(ret & STOP_NOW) &&
391 (r=vfat_lookup(&entry, filename0, length,
392 lookupflags | NO_MSG,
393 mp->shortname, mp->longname)) == 0 ){
394 if(checkForDot(lookupflags, entry.name))
395 /* while following the path, ignore the
396 * special entries if they were not
397 * explicitly given */
402 SubDir = mp->File = OpenFileByDirentry(&entry);
403 ret |= recurs_dos_loop(mp, ptr, filename1, lookupState);
406 ret |= handle_leaf(&entry, mp, lookupState);
407 if(isUniqueTarget(mp->targetName))
408 return ret | STOP_NOW;
416 return ret | ERROR_ONE;
417 if(doing_mcwd && !have_one)
422 static int common_dos_loop(MainParam_t *mp, const char *pathname,
423 lookupState_t *lookupState, int open_mode)
431 mp->loop = _dos_loop;
435 if(*pathname && pathname[1] == ':') {
436 drive = toupper(*pathname);
438 if(mp->mcwd[0] == drive)
440 } else if(mp->mcwd[0]) {
444 drive = get_default_drive();
447 if(*pathname=='/') /* absolute path name */
450 RootDir = mp->File = open_root_dir(drive, open_mode, NULL);
454 ret = recurs_dos_loop(mp, cwd, pathname, lookupState);
459 ret = recurs_dos_loop(mp, "", pathname, lookupState);
465 static int dos_loop(MainParam_t *mp, const char *arg)
467 return common_dos_loop(mp, arg, 0, mp->openflags);
471 static int dos_target_lookup(MainParam_t *mp, const char *arg)
473 lookupState_t lookupState;
477 lookupState.nbDirs = 0;
479 lookupState.nbContainers = 0;
480 lookupState.container = 0;
482 lookupflags = mp->lookupflags;
483 mp->lookupflags = DO_OPEN | ACCEPT_DIR;
484 ret = common_dos_loop(mp, arg, &lookupState, O_RDWR);
485 mp->lookupflags = lookupflags;
489 if(lookupState.nbDirs) {
491 mp->targetDir = lookupState.Dir;
492 FREE(&lookupState.container); /* container no longer needed */
496 switch(lookupState.nbContainers) {
499 fprintf(stderr,"%s: no match for target\n", arg);
502 mp->targetName = strdup(lookupState.filename);
503 mp->targetDir = lookupState.container;
507 fprintf(stderr, "Ambigous %s\n", arg);
512 static int unix_target_lookup(MainParam_t *mp, const char *arg)
515 mp->unixTarget = strdup(arg);
516 /* try complete filename */
517 if(access(mp->unixTarget, F_OK) == 0)
519 ptr = strrchr(mp->unixTarget, '/');
521 mp->targetName = mp->unixTarget;
522 mp->unixTarget = strdup(".");
526 mp->targetName = ptr+1;
531 int target_lookup(MainParam_t *mp, const char *arg)
533 if((mp->lookupflags & NO_UNIX) || (arg[0] && arg[1] == ':' ))
534 return dos_target_lookup(mp, arg);
536 return unix_target_lookup(mp, arg);
539 int main_loop(MainParam_t *mp, char **argv, int argc)
546 if(argc != 1 && mp->targetName) {
548 "Several file names given, but last argument (%s) not a directory\n", mp->targetName);
551 for (i = 0; i < argc; i++) {
554 mp->originalArg = argv[i];
555 mp->basenameHasWildcard = strpbrk(_basename(mp->originalArg),
557 if (mp->unixcallback && (!argv[i][0]
558 #ifdef OS_mingw32msvc
559 /* On Windows, support only the command-line image drive. */
562 || argv[i][1] != ':' ))
563 ret = unix_loop(0, mp, argv[i], 1);
565 ret = dos_loop(mp, argv[i]);
567 if (! (ret & (GOT_ONE | ERROR_ONE)) ) {
568 /* one argument was unmatched */
569 fprintf(stderr, "%s: File \"%s\" not found\n",
574 if(mp->fast_quit && (Bret & (MISSED_ONE | ERROR_ONE)))
577 FREE(&mp->targetDir);
580 if ((Bret & GOT_ONE) && ( Bret & MISSED_ONE))
582 if (Bret & MISSED_ONE)
587 static int dispatchToFile(direntry_t *entry, MainParam_t *mp)
590 return mp->callback(entry, mp);
592 return mp->unixcallback(mp);
596 void init_mp(MainParam_t *mp)
599 mp->openflags = O_RDONLY;
603 mp->dirCallback = dispatchToFile;
604 mp->unixcallback = NULL;
605 mp->shortname = mp->longname = 0;
610 const char *mpGetBasename(MainParam_t *mp)
613 wchar_to_native(mp->direntry->name, mp->targetBuffer,
615 return mp->targetBuffer;
617 return _basename(mp->unixSourceName);
620 void mpPrintFilename(FILE *fp, MainParam_t *mp)
623 fprintPwd(fp, mp->direntry, 0);
625 fprintf(fp,"%s",mp->originalArg);
628 const char *mpPickTargetName(MainParam_t *mp)
630 /* picks the target name: either the one explicitly given by the
631 * user, or the same as the source */
633 return mp->targetName;
635 return mpGetBasename(mp);
638 char *mpBuildUnixFilename(MainParam_t *mp)
644 target = mpPickTargetName(mp);
645 ret = malloc(strlen(mp->unixTarget) + 2 + strlen(target));
648 strcpy(ret, mp->unixTarget);
650 #if 1 /* fix for 'mcopy -n x:file existingfile' -- H. Lermen 980816 */
651 if(!mp->targetName && !mp->targetDir) {
653 if (!MT_STAT(ret, &buf) && !S_ISDIR(buf.st_mode))
658 if(!strcmp(target, ".")) {
660 } else if(!strcmp(target, "..")) {
663 while( (tmp=strchr(target, '/')) ) {
664 strncat(ret, target, tmp-target);