minix/commands/i386/mtools-3.9.7/mainloop.c
2005-04-21 14:53:53 +00:00

558 lines
12 KiB
C
Executable file

/*
* 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"
int unix_dir_loop(Stream_t *Stream, MainParam_t *mp);
int unix_loop(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)
{
unix_dir_loop(Dir, mp);
return GOT_ONE;
}
int unix_loop(Stream_t *Stream, MainParam_t *mp, char *arg, int follow_dir_link)
{
int ret;
int isdir;
mp->File = NULL;
mp->direntry = NULL;
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) {
struct stat buf;
FREE(&mp->File);
#ifdef S_ISLNK
if(!follow_dir_link &&
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(Stream, 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;
}
static int checkForDot(int lookupflags, const char *name)
{
return (lookupflags & NO_DOTS) && isSpecial(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;
char *cwd;
char *drive;
char *rest;
int ret;
mp->loop = _dos_loop;
drive='\0';
cwd = "";
if((rest = skip_drive(pathname)) > pathname) {
drive = get_drive(pathname, NULL);
if (strncmp(pathname, mp->mcwd, rest - pathname) == 0)
cwd = skip_drive(mp->mcwd);
pathname = rest;
} else {
drive = get_drive(mp->mcwd, NULL);
cwd = skip_drive(mp->mcwd);
}
if(*pathname=='/') /* absolute path name */
cwd = "";
RootDir = mp->File = open_root_dir(drive, open_mode);
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;
}
}
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) || skip_drive(arg) > arg)
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 && skip_drive(argv[i]) == argv[i])
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 = 0;
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)
return mp->direntry->name;
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;
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 stat buf;
if (!stat(ret, &buf) && !S_ISDIR(buf.st_mode))
return ret;
}
#endif
strcat(ret, "/");
strcat(ret, target);
}
return ret;
}