#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "machdr.h"
#include "rdfile.h"
#include "rdfileopt.h"
#include <dirent.h>
#define dirstruct dirent
#include "../util/curtime.h"
#include "../util/masks.h"
#include "../util/util.h"

#ifdef AUFSPLUS
#define AUFS
#endif /* AUFSPLUS */
#ifdef AUFS
#define APPLESHARE
#endif /* AUFS */
#ifdef APPLEDOUBLE
#define APPLESHARE
#endif /* APPLEDOUBLE */

#define NOTFOUND	0
#define ISFILE		1
#define	INFOFILE	2
#define INFOEXT		3
#define	SKIPFILE	4
#define	MACBINARY	5
#define	DIRECTORY	6
#ifdef APPLESHARE
#define	SHAREFILE	7
#endif /* APPLESHARE */

#define DATA_FORMAT	1
#define RSRC_FORMAT	2
#define UNIX_FORMAT	3

extern char *malloc();
extern char *realloc();
extern char *strcpy();
extern char *strncpy();
extern char *strcat();

static void check_files();
static void read_file();
static void enter_dir();
static void exit_dir();
static int get_stdin_file();

char file_info[INFOBYTES];
char *data_fork, *rsrc_fork;
int data_size, rsrc_size;
static int max_data_size, max_rsrc_size;

typedef struct filelist {
    int nfiles;
    char **files;
    int *kind;
    struct filelist *previous;
    int current;
#ifdef APPLESHARE
    int shared_dir;
#endif /* APPLESHARE */
} filelist;

static int data_only;
static int no_recurse;
static int read_stdin;
static filelist global_files;
static filelist *current_files;
static char f_auth[5];
static char f_type[5];
static char f_name[] = ".foldername";
#ifdef APPLESHARE
#ifdef AUFS
#include "aufs.h"
static char infodir[] = ".finderinfo";
static char rsrcdir[] = ".resource";
static void read_aufs_info();
#endif /* AUFS */
#ifdef APPLEDOUBLE
#include "appledouble.h"
static char infodir[] = ".AppleDouble";
static void read_appledouble_info();
#endif /* APPLEDOUBLE */
#endif /* APPLESHARE */
static char filename[255];
static int filekind;

void setup(argc, argv)
int argc;
char **argv;
{
    if(argc == 0) {
	read_stdin = 1;
    } else {
	read_stdin = 0;
	global_files.previous = NULL;
	global_files.nfiles = argc;
	global_files.files = argv;
	global_files.current = 0;
	current_files = &global_files;
	check_files(1);
    }
}

static void check_files(initial)
int initial;
{
    struct stat stbuf;
    int i, j, n;
    char filename[255], filename1[255];

    /* Check the method to read the file */
    current_files->current = 0;
    /* Set initially to NOTFOUND or DIRECTORY */
    n = current_files->nfiles;
    current_files->kind = (int *)malloc((unsigned)n * sizeof(int));
    if(current_files->kind == NULL) {
	(void)fprintf(stderr, "Insufficient memory\n");
	exit(1);
    }
    for(i = 0; i < n; i++) {
	current_files->kind[i] = NOTFOUND;
	if(stat(current_files->files[i], &stbuf) >= 0) {
	    if((stbuf.st_mode & S_IFMT) == S_IFDIR) {
		/* We found a directory */
		current_files->kind[i] = DIRECTORY;
		continue;
	    }
	    current_files->kind[i] = ISFILE;
	}
    }
    /* That is enough for data_only mode */
    if(data_only) {
	return;
    }
#ifdef APPLESHARE
    /* First check whether we are in a folder on a shared volume */
    i = 1;
#ifdef AUFS
    if(stat(rsrcdir,&stbuf) < 0) {
	i = 0;
    } else {
	if((stbuf.st_mode & S_IFMT) != S_IFDIR) {
	    i = 0;
	}
    }
    if(stat(infodir,&stbuf) < 0) {
	i = 0;
    } else {
	if((stbuf.st_mode & S_IFMT) != S_IFDIR) {
	    i = 0;
	}
    }
#endif /* AUFS */
#ifdef APPLEDOUBLE
    if(stat(infodir,&stbuf) < 0) {
	i = 0;
    } else {
	if((stbuf.st_mode & S_IFMT) != S_IFDIR) {
	    i = 0;
	}
    }
#endif /* APPLEDOUBLE */
    current_files->shared_dir = i;
#endif /* APPLESHARE */
    for(i = 0; i < n; i++) {
	if(current_files->kind[i] == NOTFOUND) {
	    j = 0;
	} else if(current_files->kind[i] == ISFILE) {
	    /* Check whether the file is special */
#ifdef APPLESHARE
	    if(!current_files->shared_dir &&
	       !strcmp(current_files->files[i], f_name)) {
		current_files->kind[i] = SKIPFILE;
		continue;
	    }
#else /* APPLESHARE */
	    if(!strcmp(current_files->files[i], f_name)) {
		current_files->kind[i] = SKIPFILE;
		continue;
	    }
#endif /* APPLESHARE */
	    j = 1;
	} else if(current_files->kind[i] == SKIPFILE) {
	    continue;
	} else if(!initial) { /* DIRECTORY */
	    /* Check whether the directory is special */
	    if(!strcmp(current_files->files[i], ".") ||
	       !strcmp(current_files->files[i], "..")) {
		current_files->kind[i] = SKIPFILE;
	    }
#ifdef APPLESHARE
#ifdef AUFS
	    if(current_files->shared_dir &&
	       (!strcmp(current_files->files[i], infodir) ||
		!strcmp(current_files->files[i], rsrcdir))) {
		current_files->kind[i] = SKIPFILE;
	    }
#endif /* AUFS */
#ifdef APPLEDOUBLE
	    if(current_files->shared_dir &&
	       !strcmp(current_files->files[i], infodir)) {
		current_files->kind[i] = SKIPFILE;
	    }
#endif /* APPLEDOUBLE */
#endif /* APPLESHARE */
	    continue;
	} else { /* Take all directories from command line! */
	    continue;
	}
#ifdef APPLESHARE
	/* Check whether file is in shared format */
	if(j & current_files->shared_dir) {
	    j = 0;
	    filename[0] = 0;
	    (void)strcat(filename, infodir);
	    (void)strcat(filename, "/");
	    (void)strcat(filename, current_files->files[i]);
	    /* There ought to be an associated file in the info direcory */
	    if(stat(filename, &stbuf) >= 0) {
		current_files->kind[i] = SHAREFILE;
		continue;
	    }
	}
#endif /* APPLESHARE */
	/* If file not found check for the same with .info extension */
	if(!j) {
	    filename[0] = 0;
	    (void)strcat(filename, current_files->files[i]);
	    (void)strcat(filename, ".info");
	    /* Found a .info file, else no such file found */
	    if(stat(filename, &stbuf) >= 0) {
		current_files->kind[i] = INFOEXT;
	    }
	    continue;
	}
	/* Now we found the file.  Check first whether the name ends with
	   .info */
	j = strlen(current_files->files[i]) - 5;
	if(!strncmp(current_files->files[i] + j, ".info", 5)) {
	    /* This is a .info file.  Set as INFOFILE */
	    current_files->kind[i] = INFOFILE;
	    /* Now remove from list of files the same with .data or .rsrc
	       extension */
	    filename[0] = 0;
	    (void)strcat(filename, current_files->files[i]);
	    filename[j] = 0;
	    (void)strcpy(filename1, filename);
	    (void)strcat(filename, ".data");
	    (void)strcat(filename1, ".rsrc");
	    for(j = i + 1; j < n; j++) {
		if(!strcmp(filename, current_files->files[j])) {
		    /* Associated .data file */
		    current_files->kind[j] = SKIPFILE;
		    continue;
		}
		if(!strcmp(filename1, current_files->files[j])) {
		    /* Associated .rsrc file */
		    current_files->kind[j] = SKIPFILE;
		}
	    }
	    continue;
	}
	if(!strncmp(current_files->files[i] + j, ".data", 5) ||
	   !strncmp(current_files->files[i] + j, ".rsrc", 5)) {
	    /* .data or .rsrc file found.  Check whether there is an
	       associated .info file in the filelist */
	    filename[0] = 0;
	    (void)strcat(filename, current_files->files[i]);
	    filename[j] = 0;
	    (void)strcat(filename, ".info");
	    for(j = i + 1; j < n; j++) {
		if(!strcmp(filename, current_files->files[j])) {
		    /* Found an associated .info file! */
		    current_files->kind[i] = SKIPFILE;
		    break;
		}
	    }
	    if(j < n) {
		continue;
	    }
	}
	/* Finally nothing special */
	current_files->kind[i] = MACBINARY;
    }
}

int nextfile()
{
    int i;

    if(read_stdin) {
	return get_stdin_file();
    }
    i = current_files->current;
again:
    if(i == current_files->nfiles) {
	if(current_files->previous == NULL) {
	    return ISATEND;
	} else {
	    exit_dir();
	    current_files->current++;
	    return ENDDIR;
	}
    }
    filename[0] = 0;
    (void)strcat(filename, current_files->files[i]);
    filekind = current_files->kind[i];
    switch(filekind) {
    case DIRECTORY:
	if(no_recurse) {
	    (void)fprintf(stderr, "Directory %s skipped.\n", filename);
	    i++;
	    current_files->current = i;
	    goto again;
	}
	enter_dir();
	return ISDIR;
    case SKIPFILE:
	i++;
	current_files->current = i;
	goto again;
    case NOTFOUND:
	(void)fprintf(stderr, "File %s not found.\n", filename);
	exit(1);
    default:
	read_file();
	current_files->current = i + 1;
	return ISFILE;
    }
}

static void read_file()
{
    FILE *fd;
    int c, j, lname, skip;
    struct stat stbuf;
#ifdef APPLESHARE
    char filename1[255];
#endif /* APPLESHARE */

    switch(filekind) {
    case ISFILE:
	if(stat(filename, &stbuf) < 0) {
	    (void)fprintf(stderr, "Cannot stat file %s\n", filename);
	    exit(1);
	}
	for(j = 0; j < INFOBYTES; j++) {
	    file_info[j] = 0;
	}
	(void)strcpy(file_info + I_NAMEOFF + 1, filename);
	file_info[I_NAMEOFF] = strlen(filename);
	put4(file_info + I_CTIMOFF, (unsigned long)stbuf.st_ctime + TIMEDIFF);
	put4(file_info + I_MTIMOFF, (unsigned long)stbuf.st_mtime + TIMEDIFF);
	if(data_only == RSRC_FORMAT) {
	    rsrc_size = stbuf.st_size;
	    data_size = 0;
	    if(rsrc_size > max_rsrc_size) {
		if(rsrc_fork == NULL) {
		    rsrc_fork = malloc((unsigned)rsrc_size);
		} else {
		    rsrc_fork = realloc(rsrc_fork, (unsigned)rsrc_size);
		}
		max_rsrc_size = rsrc_size;
	    }
	    if(f_type[0] == 0) {
		(void)strncpy(file_info + I_TYPEOFF, "RSRC", 4);
	    } else {
		(void)strncpy(file_info + I_TYPEOFF, f_type, 4);
	    }
	    if(f_auth[0] == 0) {
		(void)strncpy(file_info + I_AUTHOFF, "RSED", 4);
	    } else {
		(void)strncpy(file_info + I_AUTHOFF, f_auth, 4);
	    }
	    put4(file_info + I_RLENOFF, (unsigned long)rsrc_size);
	    if((fd = fopen(filename, "r")) == NULL) {
		(void)fprintf(stderr, "Cannot open file %s\n", filename);
		exit(1);
	    }
	    if(fread(rsrc_fork, 1, rsrc_size, fd) != rsrc_size) {
		(void)fprintf(stderr, "Short file %s\n", filename);
		exit(1);
	    }
	    (void)fclose(fd);
	} else {
	    data_size = stbuf.st_size;
	    rsrc_size = 0;
	    if(data_size > max_data_size) {
		if(data_fork == NULL) {
		    data_fork = malloc((unsigned)data_size);
		} else {
		    data_fork = realloc(data_fork, (unsigned)data_size);
		}
		max_data_size = data_size;
	    }
	    if(f_type[0] == 0) {
		(void)strncpy(file_info + I_TYPEOFF, "TEXT", 4);
	    } else {
		(void)strncpy(file_info + I_TYPEOFF, f_type, 4);
	    }
	    if(f_auth[0] == 0) {
		(void)strncpy(file_info + I_AUTHOFF, "MACA", 4);
	    } else {
		(void)strncpy(file_info + I_AUTHOFF, f_auth, 4);
	    }
	    put4(file_info + I_DLENOFF, (unsigned long)data_size);
	    if((fd = fopen(filename, "r")) == NULL) {
		(void)fprintf(stderr, "Cannot open file %s\n", filename);
		exit(1);
	    }
	    if(fread(data_fork, 1, data_size, fd) != data_size) {
		(void)fprintf(stderr, "Short file %s\n", filename);
		exit(1);
	    }
	    (void)fclose(fd);
	    if(data_only == UNIX_FORMAT) {
		for(j = 0; j < data_size; j++) {
		    c = data_fork[j];
		    if(c == '\012' || c == '\015') {
			data_fork[j] = '\027' -c;
		    }
		}
	    }
	}
	break;
    case INFOEXT:
	(void)strcat(filename, ".info");
    case INFOFILE:
	lname = strlen(filename) - 5;
	if((fd = fopen(filename, "r")) == NULL) {
	    (void)fprintf(stderr, "Cannot open file %s\n", filename);
	    exit(1);
	}
	if(fread(file_info, 1, INFOBYTES, fd) != INFOBYTES) {
	    (void)fprintf(stderr, "Cannot read info header %s\n", filename);
	}
	(void)fclose(fd);
	data_size = get4(file_info + I_DLENOFF);
	rsrc_size = get4(file_info + I_RLENOFF);
	if(data_size > max_data_size) {
	    if(data_fork == NULL) {
		data_fork = malloc((unsigned)data_size);
	    } else {
		data_fork = realloc(data_fork, (unsigned)data_size);
	    }
	    max_data_size = data_size;
	}
	if(rsrc_size > max_rsrc_size) {
	    if(rsrc_fork == NULL) {
		rsrc_fork = malloc((unsigned)rsrc_size);
	    } else {
		rsrc_fork = realloc(rsrc_fork, (unsigned)rsrc_size);
	    }
	    max_rsrc_size = rsrc_size;
	}
	if(data_size != 0) {
	    filename[lname] = 0;
	    (void)strcat(filename, ".data");
	    if((fd = fopen(filename, "r")) == NULL) {
		(void)fprintf(stderr, "Cannot open data fork %s\n", filename);
		exit(1);
	    }
	    if(fread(data_fork, 1, data_size, fd) != data_size) {
		(void)fprintf(stderr, "Premature EOF on %s\n", filename);
	    }
	    (void)fclose(fd);
	}
	if(rsrc_size != 0) {
	    filename[lname] = 0;
	    (void)strcat(filename, ".rsrc");
	    if((fd = fopen(filename, "r")) == NULL) {
		(void)fprintf(stderr, "Cannot open rsrc fork %s\n", filename);
		exit(1);
	    }
	    if(fread(rsrc_fork, 1, rsrc_size, fd) != rsrc_size) {
		(void)fprintf(stderr, "Premature EOF on %s\n", filename);
	    }
	    (void)fclose(fd);
	}
	break;
    case MACBINARY:
	if((fd = fopen(filename, "r")) == NULL) {
	    (void)fprintf(stderr, "Cannot open file %s\n", filename);
	    exit(1);
	}
	if(fread(file_info, 1, INFOBYTES, fd) != INFOBYTES) {
	    (void)fprintf(stderr, "Short file %s\n", filename);
	    exit(1);
	}
	if(file_info[0] != 0) {
	    (void)fprintf(stderr, "File is not MacBinary: %s\n", filename);
	    exit(1);
	}
	data_size = get4(file_info + I_DLENOFF);
	rsrc_size = get4(file_info + I_RLENOFF);
	if(file_info[I_LOCKOFF] & 1) {
	    file_info[I_FLAGOFF + 1] = PROTCT_MASK;
	    file_info[I_LOCKOFF] &= ~1;
	}
	if(data_size != 0) {
	    if(data_size > max_data_size) {
		if(data_fork == NULL) {
		    data_fork = malloc((unsigned)data_size);
		} else {
		    data_fork = realloc(data_fork, (unsigned)data_size);
		}
		max_data_size = data_size;
	    }
	    if(fread(data_fork, 1, data_size, fd) != data_size) {
		(void)fprintf(stderr, "Short file %s\n", filename);
		exit(1);
	    }
	    skip = (((data_size + 127) >> 7) << 7) - data_size;
	    for(j = 0; j < skip; j++) {
		(void)fgetc(fd);
	    }
	}
	if(rsrc_size != 0) {
	    if(rsrc_size > max_rsrc_size) {
		if(rsrc_fork == NULL) {
		    rsrc_fork = malloc((unsigned)rsrc_size);
		} else {
		    rsrc_fork = realloc(rsrc_fork, (unsigned)rsrc_size);
		}
		max_rsrc_size = rsrc_size;
	    }
	    if(fread(rsrc_fork, 1, rsrc_size, fd) != rsrc_size) {
		(void)fprintf(stderr, "Short file %s\n", filename);
		exit(1);
	    }
	}
	break;
#ifdef APPLESHARE
    case SHAREFILE:
#ifdef AUFS
	(void)strcpy(filename1, infodir);
	(void)strcat(filename1, "/");
	(void)strcat(filename1, filename);
	if((fd = fopen(filename1, "r")) == NULL) {
	    (void)fprintf(stderr, "Cannot open file %s\n", filename1);
	}
	read_aufs_info(fd);
	(void)fclose(fd);
	(void)strcpy(filename1, rsrcdir);
	(void)strcat(filename1, "/");
	(void)strcat(filename1, filename);
	if(stat(filename1, &stbuf) >= 0) {
	    rsrc_size = stbuf.st_size;
	    put4(file_info + I_RLENOFF, (unsigned long)rsrc_size);
	    if(rsrc_size > 0) {
		if(rsrc_size > max_rsrc_size) {
		    if(rsrc_fork == NULL) {
			rsrc_fork = malloc((unsigned)rsrc_size);
		    } else {
			rsrc_fork = realloc(rsrc_fork, (unsigned)rsrc_size);
		    }
		    max_rsrc_size = rsrc_size;
		}
		if((fd = fopen(filename1, "r")) == NULL) {
		    (void)fprintf(stderr, "Cannot open file %s\n", filename1);
		    exit(1);
		}
		if(fread(rsrc_fork, 1, rsrc_size, fd) != rsrc_size) {
		    (void)fprintf(stderr, "Short file %s\n", filename1);
		    exit(1);
		}
		(void)fclose(fd);
	    }
	}
	if(stat(filename, &stbuf) >= 0) {
	    data_size = stbuf.st_size;
	    put4(file_info + I_DLENOFF, (unsigned long)data_size);
	    if(data_size > 0) {
		if(data_size > max_data_size) {
		    if(data_fork == NULL) {
			data_fork = malloc((unsigned)data_size);
		    } else {
			data_fork = realloc(data_fork, (unsigned)data_size);
		    }
		    max_data_size = data_size;
		}
		if((fd = fopen(filename, "r")) == NULL) {
		    (void)fprintf(stderr, "Cannot open file %s\n", filename);
		    exit(1);
		}
		if(fread(data_fork, 1, data_size, fd) != data_size) {
		    (void)fprintf(stderr, "Short file %s\n", filename1);
		    exit(1);
		}
		(void)fclose(fd);
	    }
	}
#endif /* AUFS */
#ifdef APPLEDOUBLE
	(void)strcpy(filename1, infodir);
	(void)strcat(filename1, "/");
	(void)strcat(filename1, filename);
	if((fd = fopen(filename1, "r")) == NULL) {
	    (void)fprintf(stderr, "Cannot open file %s\n", filename1);
	}
	read_appledouble_info(fd);
	rsrc_size = get4(file_info + I_RLENOFF);
	if(rsrc_size > 0) {
	    if(rsrc_size > max_rsrc_size) {
		if(rsrc_fork == NULL) {
		    rsrc_fork = malloc((unsigned)rsrc_size);
		} else {
		    rsrc_fork = realloc(rsrc_fork, (unsigned)rsrc_size);
		}
		max_rsrc_size = rsrc_size;
	    }
	    if(fread(rsrc_fork, 1, rsrc_size, fd) != rsrc_size) {
		(void)fprintf(stderr, "Short file %s\n", filename1);
		exit(1);
	    }
	}
	(void)fclose(fd);
	if(stat(filename, &stbuf) >= 0) {
	    data_size = stbuf.st_size;
	    put4(file_info + I_DLENOFF, (unsigned long)data_size);
	    if(data_size > 0) {
		if(data_size > max_data_size) {
		    if(data_fork == NULL) {
			data_fork = malloc((unsigned)data_size);
		    } else {
			data_fork = realloc(data_fork, (unsigned)data_size);
		    }
		    max_data_size = data_size;
		}
		if((fd = fopen(filename, "r")) == NULL) {
		    (void)fprintf(stderr, "Cannot open file %s\n", filename);
		    exit(1);
		}
		if(fread(data_fork, 1, data_size, fd) != data_size) {
		    (void)fprintf(stderr, "Short file %s\n", filename1);
		    exit(1);
		}
		(void)fclose(fd);
	    }
	}
#endif /* APPLEDOUBLE */
	break;
#endif /* APPLESHARE */
    }
}

static void enter_dir()
{
    DIR *directory;
    struct dirstruct *curentry;
    FILE *fd;
    int n, j, namlen;
    int listsize, cursize;
    char *filetable;
    filelist *new_files;
#ifdef APPLESHARE
    char filename1[255];
#endif /* APPLESHARE */

    for(j = 0; j < INFOBYTES; j++) {
	file_info[j] = 0;
    }
    (void)strcpy(file_info + I_NAMEOFF + 1, filename);
    file_info[I_NAMEOFF] = strlen(filename);
    directory = opendir(filename);
    if(directory == NULL) {
	(void)fprintf(stderr, "Cannot read directory %s\n", filename);
	exit(1);
    }
    listsize = 1024;
    filetable = malloc((unsigned)listsize);
    cursize = 0;
    n = 0;
    while((curentry = readdir(directory)) != NULL) {
	namlen = strlen(curentry->d_name);
	if(namlen + 1 > listsize - cursize) {
	    listsize += 1024;
	    filetable = realloc(filetable, (unsigned)listsize);
	}
	(void)strcpy(filetable + cursize, curentry->d_name);
	cursize += (namlen + 1);
	n++;
    }
    filetable = realloc(filetable, (unsigned)cursize);
    (void)closedir(directory);
    new_files = (filelist *)malloc(sizeof(filelist));
    new_files->nfiles = n;
    new_files->files = (char **)malloc((unsigned)n * sizeof(char **));
    new_files->kind = (int *)malloc((unsigned)n * sizeof(int));
    new_files->previous = current_files;
    new_files->current = 0;
    cursize = 0;
    for(j = 0; j < n; j++) {
	new_files->files[j] = filetable + cursize;
	cursize += (strlen(filetable + cursize) + 1);
    }
    (void)chdir(filename);
#ifdef APPLESHARE
    if((fd = fopen(f_name, "r")) != NULL) {
	if(fread(file_info, 1, INFOBYTES, fd) != INFOBYTES) {
	    (void)fprintf(stderr, "File error on %s\n", f_name);
	    exit(1);
	}
	file_info[I_NAMEOFF] |= 0x80;
	(void)fclose(fd);
    } else {
#ifdef AUFS
	(void)strcpy(filename1, "../");
	(void)strcat(filename1, infodir);
	(void)strcat(filename1, "/");
	(void)strcat(filename1, filename);
	if((fd = fopen(filename1, "r")) != NULL) {
	    read_aufs_info(fd);
	    (void)fclose(fd);
	}
#endif /* AUFS */
#ifdef APPLEDOUBLE
	(void)strcpy(filename1, infodir);
	(void)strcat(filename1, "/.Parent");
	if((fd = fopen(filename1, "r")) != NULL) {
	    read_appledouble_info(fd);
	    (void)fclose(fd);
	}
#endif /* APPLEDOUBLE */
	file_info[I_NAMEOFF] |= 0x80;
    }
#else /* APPLESHARE */
    if((fd = fopen(f_name, "r")) != NULL) {
	if(fread(file_info, 1, INFOBYTES, fd) != INFOBYTES) {
	    (void)fprintf(stderr, "File error on %s\n", f_name);
	    exit(1);
	}
	file_info[I_NAMEOFF] |= 0x80;
	(void)fclose(fd);
    }
#endif /* APPLESHARE */
    current_files = new_files;
    check_files(0);
}

static void exit_dir()
{
    filelist *old_files;
    int i;

    for(i = 0; i < INFOBYTES; i++) {
	file_info[i] = 0;
    }
    file_info[I_NAMEOFF] = 0x80;
    old_files = current_files;
    /* Do some garbage collection here! */
    current_files = current_files->previous;
    (void)free(old_files->files[0]);
    (void)free((char *)old_files->files);
    (void)free((char *)old_files->kind);
    (void)free((char *)old_files);
    (void)chdir("..");
}

#ifdef APPLESHARE
#ifdef AUFS
static void read_aufs_info(fd)
FILE *fd;
{
    FileInfo theinfo;
    int i, n;
    struct stat stbuf;

    for(i = 0; i < INFOBYTES; i++) {
	file_info[i] = 0;
    }
    bzero((char *) &theinfo, sizeof(theinfo));
    if(fread((char *)&theinfo, 1, sizeof(theinfo), fd) != sizeof(theinfo)) {
	(void)fprintf(stderr, "Short AUFS info header for %s\n", filename);
	exit(1);
    }
    if(theinfo.fi_magic1 & BYTEMASK != FI_MAGIC1 ||
       theinfo.fi_version & BYTEMASK != FI_VERSION ||
       theinfo.fi_magic & BYTEMASK != FI_MAGIC) {
	(void)fprintf(stderr, "Magic number mismatch on %s\n", filename);
	exit(1);
    }
    bcopy(theinfo.fi_fndr, file_info + I_TYPEOFF, 4);
    bcopy(theinfo.fi_fndr + 4, file_info + I_AUTHOFF, 4);
    bcopy(theinfo.fi_fndr + 8, file_info + I_FLAGOFF, 2);
    if(theinfo.fi_bitmap & FI_BM_MACINTOSHFILENAME) {
	n = strlen(theinfo.fi_macfilename);
	(void)strncpy(file_info + I_NAMEOFF + 1, (char *)theinfo.fi_macfilename,
		n);
    } else if(theinfo.fi_bitmap & FI_BM_SHORTFILENAME) {
	n = strlen(theinfo.fi_shortfilename);
	(void)strncpy(file_info + I_NAMEOFF + 1,
		(char *)theinfo.fi_shortfilename, n);
    } else {
	n = strlen(filename);
	(void)strncpy(file_info + I_NAMEOFF + 1, filename, n);
    }
    file_info[I_NAMEOFF] = n;
#ifdef AUFSPLUS
    if(theinfo.fi_datemagic == FI_MAGIC &&
       (theinfo.fi_datevalid & (FI_CDATE | FI_MDATE)) ==
		(FI_CDATE | FI_MDATE)) {
	put4(file_info + I_CTIMOFF, get4(theinfo.fi_ctime) + TIMEDIFF);
	put4(file_info + I_MTIMOFF, get4(theinfo.fi_mtime) + TIMEDIFF);
    } else {
	if(fstat(fileno(fd), &stbuf) >= 0) {
	    put4(file_info + I_CTIMOFF,
		(unsigned long)stbuf.st_ctime + TIMEDIFF);
	    put4(file_info + I_MTIMOFF,
		(unsigned long)stbuf.st_mtime + TIMEDIFF);
	}
    }
#else /* AUFSPLUS */
    if(fstat(fileno(fd), &stbuf) >= 0) {
	put4(file_info + I_CTIMOFF, (unsigned long)stbuf.st_ctime + TIMEDIFF);
	put4(file_info + I_MTIMOFF, (unsigned long)stbuf.st_mtime + TIMEDIFF);
    }
#endif /* AUFSPLUS */
}
#endif /* AUFS */

#ifdef APPLEDOUBLE
/* This version assumes that the AppleDouble info header is always the same
   size and format.  I have not yet seen something that will lead me to
   believe different.
*/
static void read_appledouble_info(fd)
FILE *fd;
{
    FileInfo theinfo;
    int i, n;

    for(i = 0; i < INFOBYTES; i++) {
	file_info[i] = 0;
    }
    bzero((char *) &theinfo, sizeof(theinfo));
    if(fread((char *)&theinfo, 1, sizeof(theinfo), fd) != sizeof(theinfo)) {
	(void)fprintf(stderr, "Short AppleDouble info header for %s\n",
		filename);
	exit(1);
    }
    if(get4(theinfo.fi_magic) != FI_MAGIC ||
       get2(theinfo.fi_version) != FI_VERSION) {
	(void)fprintf(stderr, "Magic number mismatch on %s\n", filename);
	exit(1);
    }
    bcopy(theinfo.fi_type, file_info + I_TYPEOFF, 4);
    bcopy(theinfo.fi_auth, file_info + I_AUTHOFF, 4);
    bcopy(theinfo.fi_finfo, file_info + I_FLAGOFF, 2);
    n = get4(theinfo.fi_namlen);
    (void)strncpy(file_info + I_NAMEOFF + 1, theinfo.fi_name, n);
    file_info[I_NAMEOFF] = n;
    put4(file_info + I_CTIMOFF, get4(theinfo.fi_ctime) + TIMEDIFF);
    put4(file_info + I_MTIMOFF, get4(theinfo.fi_mtime) + TIMEDIFF);
    rsrc_size = get4(theinfo.fi_rsrc);
    put4(file_info + I_RLENOFF, (unsigned long)rsrc_size);
}
#endif /* APPLEDOUBLE */
#endif /* APPLESHARE */

static int get_stdin_file()
{
    int i, skip;

    i = fgetc(stdin);
    if(i == EOF) {
	return ISATEND;
    }
    (void)ungetc(i, stdin);
    if(fread(file_info, 1, INFOBYTES, stdin) != INFOBYTES) {
	(void)fprintf(stderr, "Short input\n");
	exit(1);
    }
    if(file_info[0] != 0) {
	(void)fprintf(stderr, "File is not MacBinary: %s\n", filename);
	exit(1);
    }
    data_size = get4(file_info + I_DLENOFF);
    rsrc_size = get4(file_info + I_RLENOFF);
    if(file_info[I_LOCKOFF] & 1) {
	file_info[I_FLAGOFF + 1] = PROTCT_MASK;
	file_info[I_LOCKOFF] &= ~1;
    }
    if(data_size != 0) {
	if(data_size > max_data_size) {
	    if(data_fork == NULL) {
		data_fork = malloc((unsigned)data_size);
	    } else {
		data_fork = realloc(data_fork, (unsigned)data_size);
	    }
	    max_data_size = data_size;
	}
	if(fread(data_fork, 1, data_size, stdin) != data_size) {
	    (void)fprintf(stderr, "Short input\n");
	    exit(1);
	}
	skip = (((data_size + 127) >> 7) << 7) - data_size;
	for(i = 0; i < skip; i++) {
	    (void)fgetc(stdin);
	}
    }
    if(rsrc_size != 0) {
	if(rsrc_size > max_rsrc_size) {
	    if(rsrc_fork == NULL) {
		rsrc_fork = malloc((unsigned)rsrc_size);
	    } else {
		rsrc_fork = realloc(rsrc_fork, (unsigned)rsrc_size);
	    }
	    max_rsrc_size = rsrc_size;
	}
	if(fread(rsrc_fork, 1, rsrc_size, stdin) != rsrc_size) {
	    (void)fprintf(stderr, "Short input\n");
	    exit(1);
	}
	skip = (((rsrc_size + 127) >> 7) << 7) - rsrc_size;
	for(i = 0; i < skip; i++) {
	    (void)fgetc(stdin);
	}
    }
    if(file_info[I_NAMEOFF] & 0x80) {
	if((file_info[I_NAMEOFF] & 0xff) == 0x80) {
	    return ENDDIR;
	}
	return ISDIR;
    }
    return ISFILE;
}

int rdfileopt(c)
char c;
{
extern char *optarg;
char name[32];

    switch(c) {
    case 'd':
	data_only = DATA_FORMAT;
	break;
    case 'u':
    case 'U':
	data_only = UNIX_FORMAT;
	break;
    case 'r':
	data_only = RSRC_FORMAT;
	break;
    case 'c':
	backtrans(name, optarg);
	(void)strncpy(f_auth, name, 4);
	break;
    case 't':
	backtrans(name, optarg);
	(void)strncpy(f_type, name, 4);
	break;
    default:
	return 0;
    }
    return 1;
}

void give_rdfileopt()
{
    (void)fprintf(stderr, "File input options:\n");
    (void)fprintf(stderr, "-r:\tread as resource files\n");
    (void)fprintf(stderr, "-d:\tread as data files\n");
    (void)fprintf(stderr,
	"-u:\tread as data files with Unix -> Mac text file translation\n");
    (void)fprintf(stderr, "-U:\ta synonym for -u\n");
    (void)fprintf(stderr,
	"-c cr:\tcreator if one of the above options is used\n");
    (void)fprintf(stderr,
	"-t ty:\tfiletype if one of the above options is used\n");
}

void set_norecurse()
{
    no_recurse = 1;
}

char *get_rdfileopt()
{
    static char options[] = "rduUc:t:";

    return options;
}

char *get_minb()
{
#ifdef APPLESHARE
#ifdef AUFS
    return ", AUFS supported";
#endif /* AUFS */
#ifdef APPLEDOUBLE
    return ", AppleDouble supported";
#endif /* APPLEDOUBLE */
#else /* APPLESHARE */
    return ", no Apple-Unix sharing supported";
#endif /* APPLESHARE */
}

