diff options
Diffstat (limited to 'mainloop.c')
-rw-r--r-- | mainloop.c | 671 |
1 files changed, 671 insertions, 0 deletions
diff --git a/mainloop.c b/mainloop.c new file mode 100644 index 0000000..f5f8349 --- /dev/null +++ b/mainloop.c @@ -0,0 +1,671 @@ +/* Copyright 1997-2002,2005-2009 Alain Knaff. + * This file is part of mtools. + * + * Mtools is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Mtools is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Mtools. If not, see <http://www.gnu.org/licenses/>. + * + * mainloop.c + * Iterating over all the command line parameters, and matching patterns + * where needed + */ + +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "vfat.h" +#include "fs.h" +#include "mainloop.h" +#include "plain_io.h" +#include "file.h" +#include "file_name.h" + + +/* Fix the info in the MCWD file to be a proper directory name. + * Always has a leading separator. Never has a trailing separator + * (unless it is the path itself). */ + +static const char *fix_mcwd(char *ans) +{ + FILE *fp; + char *s; + char buf[MAX_PATH]; + + fp = open_mcwd("r"); + if(!fp || !fgets(buf, MAX_PATH, fp)) { + if(fp) + fclose(fp); + ans[0] = get_default_drive(); + strcpy(ans+1, ":/"); + return ans; + } + + buf[strlen(buf) -1] = '\0'; + fclose(fp); + /* drive letter present? */ + s = buf; + if (buf[0] && buf[1] == ':') { + strncpy(ans, buf, 2); + ans[2] = '\0'; + s = &buf[2]; + } else { + ans[0] = get_default_drive(); + strcpy(ans+1, ":"); + } + /* add a leading separator */ + if (*s != '/' && *s != '\\') { + strcat(ans, "/"); + strcat(ans, s); + } else + strcat(ans, s); + +#if 0 + /* translate to upper case */ + for (s = ans; *s; ++s) { + *s = toupper(*s); + if (*s == '\\') + *s = '/'; + } +#endif + /* if only drive, colon, & separator */ + if (strlen(ans) == 3) + return(ans); + /* zap the trailing separator */ + if (*--s == '/') + *s = '\0'; + return ans; +} + +int unix_dir_loop(Stream_t *Stream, MainParam_t *mp); +int unix_loop(UNUSED(Stream_t *Stream), MainParam_t *mp, char *arg, + int follow_dir_link); + +static int _unix_loop(Stream_t *Dir, MainParam_t *mp, + const char *filename UNUSEDP) +{ + return unix_dir_loop(Dir, mp); +} + +int unix_loop(UNUSED(Stream_t *Stream), MainParam_t *mp, + char *arg, int follow_dir_link) +{ + int ret; + int isdir; + int unixNameLength; + + mp->File = NULL; + mp->direntry = NULL; + unixNameLength = strlen(arg); + if(unixNameLength > 1 && arg[unixNameLength-1] == '/') { + /* names ending in slash, and having at least two characters */ + char *name = strdup(arg); + name[unixNameLength-1]='\0'; + mp->unixSourceName = name; + } else { + mp->unixSourceName = arg; + } + /* mp->dir.attr = ATTR_ARCHIVE;*/ + mp->loop = _unix_loop; + if((mp->lookupflags & DO_OPEN)){ + mp->File = SimpleFileOpen(0, 0, arg, O_RDONLY, 0, 0, 0, 0); + if(!mp->File){ + perror(arg); +#if 0 + tmp = _basename(arg); + strncpy(mp->filename, tmp, VBUFSIZE); + mp->filename[VBUFSIZE-1] = '\0'; +#endif + return ERROR_ONE; + } + GET_DATA(mp->File, 0, 0, &isdir, 0); + if(isdir) { +#if !defined(__EMX__) && !defined(OS_mingw32msvc) + struct MT_STAT buf; +#endif + + FREE(&mp->File); +#if !defined(__EMX__) && !defined(OS_mingw32msvc) + if(!follow_dir_link && + MT_LSTAT(arg, &buf) == 0 && + S_ISLNK(buf.st_mode)) { + /* skip links to directories in order to avoid + * infinite loops */ + fprintf(stderr, + "skipping directory symlink %s\n", + arg); + return 0; + } +#endif + if(! (mp->lookupflags & ACCEPT_DIR)) + return 0; + mp->File = OpenDir(arg); + } + } + + if(isdir) + ret = mp->dirCallback(0, mp); + else + ret = mp->unixcallback(mp); + FREE(&mp->File); + return ret; +} + + +int isSpecial(const char *name) +{ + if(name[0] == '\0') + return 1; + if(!strcmp(name,".")) + return 1; + if(!strcmp(name,"..")) + return 1; + return 0; +} + +#ifdef HAVE_WCHAR_H +int isSpecialW(const wchar_t *name) +{ + if(name[0] == '\0') + return 1; + if(!wcscmp(name,L".")) + return 1; + if(!wcscmp(name,L"..")) + return 1; + return 0; +} +#endif + +static int checkForDot(int lookupflags, const wchar_t *name) +{ + return (lookupflags & NO_DOTS) && isSpecialW(name); +} + + +typedef struct lookupState_t { + Stream_t *container; + int nbContainers; + Stream_t *Dir; + int nbDirs; + const char *filename; +} lookupState_t; + +static int isUniqueTarget(const char *name) +{ + return name && strcmp(name, "-"); +} + +static int handle_leaf(direntry_t *direntry, MainParam_t *mp, + lookupState_t *lookupState) +{ + Stream_t *MyFile=0; + int ret; + + if(got_signal) + return ERROR_ONE; + if(lookupState) { + /* we are looking for a "target" file */ + switch(lookupState->nbDirs) { + case 0: /* no directory yet, open it */ + lookupState->Dir = OpenFileByDirentry(direntry); + lookupState->nbDirs++; + /* dump the container, we have + * better now */ + FREE(&lookupState->container); + return 0; + case 1: /* we have already a directory */ + FREE(&lookupState->Dir); + fprintf(stderr,"Ambigous\n"); + return STOP_NOW | ERROR_ONE; + default: + return STOP_NOW | ERROR_ONE; + } + } + + mp->direntry = direntry; + if(IS_DIR(direntry)) { + if(mp->lookupflags & (DO_OPEN | DO_OPEN_DIRS)) + MyFile = mp->File = OpenFileByDirentry(direntry); + ret = mp->dirCallback(direntry, mp); + } else { + if(mp->lookupflags & DO_OPEN) + MyFile = mp->File = OpenFileByDirentry(direntry); + ret = mp->callback(direntry, mp); + } + FREE(&MyFile); + if(isUniqueTarget(mp->targetName)) + ret |= STOP_NOW; + return ret; +} + +static int _dos_loop(Stream_t *Dir, MainParam_t *mp, const char *filename) +{ + Stream_t *MyFile=0; + direntry_t entry; + int ret; + int r; + + ret = 0; + r=0; + initializeDirentry(&entry, Dir); + while(!got_signal && + (r=vfat_lookup(&entry, filename, -1, + mp->lookupflags, mp->shortname, + mp->longname)) == 0 ){ + mp->File = NULL; + if(!checkForDot(mp->lookupflags,entry.name)) { + MyFile = 0; + if((mp->lookupflags & DO_OPEN) || + (IS_DIR(&entry) && + (mp->lookupflags & DO_OPEN_DIRS))) { + MyFile = mp->File = OpenFileByDirentry(&entry); + } + if(got_signal) + break; + mp->direntry = &entry; + if(IS_DIR(&entry)) + ret |= mp->dirCallback(&entry,mp); + else + ret |= mp->callback(&entry, mp); + FREE(&MyFile); + } + if (fat_error(Dir)) + ret |= ERROR_ONE; + if(mp->fast_quit && (ret & ERROR_ONE)) + break; + } + if (r == -2) + return ERROR_ONE; + if(got_signal) + ret |= ERROR_ONE; + return ret; +} + +static int recurs_dos_loop(MainParam_t *mp, const char *filename0, + const char *filename1, + lookupState_t *lookupState) +{ + /* Dir is de-allocated by the same entity which allocated it */ + const char *ptr; + direntry_t entry; + int length; + int lookupflags; + int ret; + int have_one; + int doing_mcwd; + int r; + + while(1) { + /* strip dots and / */ + if(!strncmp(filename0,"./", 2)) { + filename0 += 2; + continue; + } + if(!strcmp(filename0,".") && filename1) { + filename0 ++; + continue; + } + if(filename0[0] == '/') { + filename0++; + continue; + } + if(!filename0[0]) { + if(!filename1) + break; + filename0 = filename1; + filename1 = 0; + continue; + } + break; + } + + if(!strncmp(filename0,"../", 3) || + (!strcmp(filename0, "..") && filename1)) { + /* up one level */ + mp->File = getDirentry(mp->File)->Dir; + return recurs_dos_loop(mp, filename0+2, filename1, lookupState); + } + + doing_mcwd = !!filename1; + + ptr = strchr(filename0, '/'); + if(!ptr) { + length = strlen(filename0); + ptr = filename1; + filename1 = 0; + } else { + length = ptr - filename0; + ptr++; + } + if(!ptr) { + if(mp->lookupflags & OPEN_PARENT) { + mp->targetName = filename0; + ret = handle_leaf(getDirentry(mp->File), mp, + lookupState); + mp->targetName = 0; + return ret; + } + + if(!strcmp(filename0, ".") || !filename0[0]) { + return handle_leaf(getDirentry(mp->File), + mp, lookupState); + } + + if(!strcmp(filename0, "..")) { + return handle_leaf(getParent(getDirentry(mp->File)), mp, + lookupState); + } + + lookupflags = mp->lookupflags; + + if(lookupState) { + lookupState->filename = filename0; + if(lookupState->nbContainers + lookupState->nbDirs > 0){ + /* we have already one target, don't bother + * with this one. */ + FREE(&lookupState->container); + } else { + /* no match yet. Remember this container for + * later use */ + lookupState->container = COPY(mp->File); + } + lookupState->nbContainers++; + } + } else + lookupflags = ACCEPT_DIR | DO_OPEN | NO_DOTS; + + ret = 0; + r = 0; + have_one = 0; + initializeDirentry(&entry, mp->File); + while(!(ret & STOP_NOW) && + !got_signal && + (r=vfat_lookup(&entry, filename0, length, + lookupflags | NO_MSG, + mp->shortname, mp->longname)) == 0 ){ + if(checkForDot(lookupflags, entry.name)) + /* while following the path, ignore the + * special entries if they were not + * explicitly given */ + continue; + have_one = 1; + if(ptr) { + Stream_t *SubDir; + SubDir = mp->File = OpenFileByDirentry(&entry); + ret |= recurs_dos_loop(mp, ptr, filename1, lookupState); + FREE(&SubDir); + } else { + ret |= handle_leaf(&entry, mp, lookupState); + if(isUniqueTarget(mp->targetName)) + return ret | STOP_NOW; + } + if(doing_mcwd) + break; + } + if (r == -2) + return ERROR_ONE; + if(got_signal) + return ret | ERROR_ONE; + if(doing_mcwd && !have_one) + return NO_CWD; + return ret; +} + +static int common_dos_loop(MainParam_t *mp, const char *pathname, + lookupState_t *lookupState, int open_mode) + +{ + Stream_t *RootDir; + const char *cwd; + char drive; + + int ret; + mp->loop = _dos_loop; + + drive='\0'; + cwd = ""; + if(*pathname && pathname[1] == ':') { + drive = toupper(*pathname); + pathname += 2; + if(mp->mcwd[0] == drive) + cwd = mp->mcwd+2; + } else if(mp->mcwd[0]) { + drive = mp->mcwd[0]; + cwd = mp->mcwd+2; + } else { + drive = get_default_drive(); + } + + if(*pathname=='/') /* absolute path name */ + cwd = ""; + + RootDir = mp->File = open_root_dir(drive, open_mode, NULL); + if(!mp->File) + return ERROR_ONE; + + ret = recurs_dos_loop(mp, cwd, pathname, lookupState); + if(ret & NO_CWD) { + /* no CWD */ + *mp->mcwd = '\0'; + unlink_mcwd(); + ret = recurs_dos_loop(mp, "", pathname, lookupState); + } + FREE(&RootDir); + return ret; +} + +static int dos_loop(MainParam_t *mp, const char *arg) +{ + return common_dos_loop(mp, arg, 0, mp->openflags); +} + + +static int dos_target_lookup(MainParam_t *mp, const char *arg) +{ + lookupState_t lookupState; + int ret; + int lookupflags; + + lookupState.nbDirs = 0; + lookupState.Dir = 0; + lookupState.nbContainers = 0; + lookupState.container = 0; + + lookupflags = mp->lookupflags; + mp->lookupflags = DO_OPEN | ACCEPT_DIR; + ret = common_dos_loop(mp, arg, &lookupState, O_RDWR); + mp->lookupflags = lookupflags; + if(ret & ERROR_ONE) + return ret; + + if(lookupState.nbDirs) { + mp->targetName = 0; + mp->targetDir = lookupState.Dir; + FREE(&lookupState.container); /* container no longer needed */ + return ret; + } + + switch(lookupState.nbContainers) { + case 0: + /* no match */ + fprintf(stderr,"%s: no match for target\n", arg); + return MISSED_ONE; + case 1: + mp->targetName = strdup(lookupState.filename); + mp->targetDir = lookupState.container; + return ret; + default: + /* too much */ + fprintf(stderr, "Ambigous %s\n", arg); + return ERROR_ONE; + } +} + +static int unix_target_lookup(MainParam_t *mp, const char *arg) +{ + char *ptr; + mp->unixTarget = strdup(arg); + /* try complete filename */ + if(access(mp->unixTarget, F_OK) == 0) + return GOT_ONE; + ptr = strrchr(mp->unixTarget, '/'); + if(!ptr) { + mp->targetName = mp->unixTarget; + mp->unixTarget = strdup("."); + return GOT_ONE; + } else { + *ptr = '\0'; + mp->targetName = ptr+1; + return GOT_ONE; + } +} + +int target_lookup(MainParam_t *mp, const char *arg) +{ + if((mp->lookupflags & NO_UNIX) || (arg[0] && arg[1] == ':' )) + return dos_target_lookup(mp, arg); + else + return unix_target_lookup(mp, arg); +} + +int main_loop(MainParam_t *mp, char **argv, int argc) +{ + int i; + int ret, Bret; + + Bret = 0; + + if(argc != 1 && mp->targetName) { + fprintf(stderr, + "Several file names given, but last argument (%s) not a directory\n", mp->targetName); + } + + for (i = 0; i < argc; i++) { + if ( got_signal ) + break; + mp->originalArg = argv[i]; + mp->basenameHasWildcard = strpbrk(_basename(mp->originalArg), + "*[?") != 0; + if (mp->unixcallback && (!argv[i][0] +#ifdef OS_mingw32msvc +/* On Windows, support only the command-line image drive. */ + || argv[i][0] != ':' +#endif + || argv[i][1] != ':' )) + ret = unix_loop(0, mp, argv[i], 1); + else + ret = dos_loop(mp, argv[i]); + + if (! (ret & (GOT_ONE | ERROR_ONE)) ) { + /* one argument was unmatched */ + fprintf(stderr, "%s: File \"%s\" not found\n", + progname, argv[i]); + ret |= ERROR_ONE; + } + Bret |= ret; + if(mp->fast_quit && (Bret & (MISSED_ONE | ERROR_ONE))) + break; + } + FREE(&mp->targetDir); + if(Bret & ERROR_ONE) + return 1; + if ((Bret & GOT_ONE) && ( Bret & MISSED_ONE)) + return 2; + if (Bret & MISSED_ONE) + return 1; + return 0; +} + +static int dispatchToFile(direntry_t *entry, MainParam_t *mp) +{ + if(entry) + return mp->callback(entry, mp); + else + return mp->unixcallback(mp); +} + + +void init_mp(MainParam_t *mp) +{ + fix_mcwd(mp->mcwd); + mp->openflags = O_RDONLY; + mp->targetName = 0; + mp->targetDir = 0; + mp->unixTarget = 0; + mp->dirCallback = dispatchToFile; + mp->unixcallback = NULL; + mp->shortname = mp->longname = 0; + mp->File = 0; + mp->fast_quit = 0; +} + +const char *mpGetBasename(MainParam_t *mp) +{ + if(mp->direntry) { + wchar_to_native(mp->direntry->name, mp->targetBuffer, + MAX_VNAMELEN+1); + return mp->targetBuffer; + } else + return _basename(mp->unixSourceName); +} + +void mpPrintFilename(FILE *fp, MainParam_t *mp) +{ + if(mp->direntry) + fprintPwd(fp, mp->direntry, 0); + else + fprintf(fp,"%s",mp->originalArg); +} + +const char *mpPickTargetName(MainParam_t *mp) +{ + /* picks the target name: either the one explicitly given by the + * user, or the same as the source */ + if(mp->targetName) + return mp->targetName; + else + return mpGetBasename(mp); +} + +char *mpBuildUnixFilename(MainParam_t *mp) +{ + const char *target; + char *ret; + char *tmp; + + target = mpPickTargetName(mp); + ret = malloc(strlen(mp->unixTarget) + 2 + strlen(target)); + if(!ret) + return 0; + strcpy(ret, mp->unixTarget); + if(*target) { +#if 1 /* fix for 'mcopy -n x:file existingfile' -- H. Lermen 980816 */ + if(!mp->targetName && !mp->targetDir) { + struct MT_STAT buf; + if (!MT_STAT(ret, &buf) && !S_ISDIR(buf.st_mode)) + return ret; + } +#endif + strcat(ret, "/"); + if(!strcmp(target, ".")) { + target="DOT"; + } else if(!strcmp(target, "..")) { + target="DOTDOT"; + } + while( (tmp=strchr(target, '/')) ) { + strncat(ret, target, tmp-target); + strcat(ret, "\\"); + target=tmp+1; + } + strcat(ret, target); + } + return ret; +} |