From 1e7229d4e7a1d312d19bd11fb2b75335a0d27eab Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 15 Apr 2015 12:42:26 +0100 Subject: [PATCH] dosread: remove DOS formatting (and correct missing const on main argv[] via Tormod Volden) --- Applications/util/dosread.c | 2522 +++++++++++++++++------------------ 1 file changed, 1261 insertions(+), 1261 deletions(-) diff --git a/Applications/util/dosread.c b/Applications/util/dosread.c index 18d3d734..d2a754f5 100644 --- a/Applications/util/dosread.c +++ b/Applications/util/dosread.c @@ -1,1261 +1,1261 @@ -/* dos{dir|read|write|del} - {list|read|write|del} MS-DOS disks Author: M. Huisjes */ - -/* dosdir - list MS-DOS directories to stdout - * doswrite - write stdin/file to DOS-file - * dosread - read DOS-file to stdout/file - * dosdel - delete DOS-file - * - * Author: Michiel Huisjes. - * - * Usage: dos... [-lra] drive [ms-dos file/dir] [file] - * l: Give long listing. - * r: List recursively. - * a: Set ASCII bit. - */ - -/* no assertions - let's save memory! */ -#define NDEBUG -#undef CACHE_ROOT - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef MSX_UZIX_TARGET -/* these are true numbers, but you can increase if you have memory */ -#define MAX_CLUSTER_SIZE 8192 -#define MAX_ROOT_ENTRIES 256 -#else -#ifdef PC_UZIX_TARGET -/* due to limitation of memory... */ -#define MAX_CLUSTER_SIZE 2048 -#define MAX_ROOT_ENTRIES 256 -#else -#define MAX_CLUSTER_SIZE 4096 -#define MAX_ROOT_ENTRIES 512 -#endif -#endif -#define FAT_START 512L /* After bootsector */ -#define ROOTADDR (FAT_START + 2L * fat_size) -#define clus_add(cl_no) ((long) (((long) cl_no - 2L) \ - * (long) cluster_size \ - + data_start \ - )) -struct dir_entry { - unsigned char d_name[8]; - unsigned char d_ext[3]; - unsigned char d_attribute; - unsigned char d_reserved[10]; - unsigned short d_time; - unsigned short d_date; - unsigned short d_cluster; - unsigned long d_size; -}; - -typedef struct dir_entry DIRECTORY; - -#define NOT_USED 0x00 -#define ERASED 0xE5 -#define DIR 0x2E -#define DIR_SIZE (sizeof (struct dir_entry)) -#define SUB_DIR 0x10 -#define NIL_DIR ((DIRECTORY *) 0) - -#define LAST_CLUSTER12 0xFFF -#define LAST_CLUSTER 0xFFFF -#define FREE 0x000 -#define BAD 0xFF0 -#define BAD16 0xFFF0 - -#define FCLOSE if (fdo != stdout) fclose(fdo) -#define FABORT(x) FCLOSE; exit(x) - -typedef int BOOL; - -#define TRUE 1 -#define FALSE 0 -#define NIL_PTR ((char *) 0) - -#define DOS_TIME 315532800L /* 1970 - 1980 */ - -#define READ 0 -#define WRITE 1 - -#define FIND 3 -#define LABEL 4 -#define ENTRY 5 -#define find_entry(d, e, p) directory(d, e, FIND, p) -#define list_dir(d, e, f) (void) directory(d, e, f, NIL_PTR) -#define label() directory(root, root_entries, LABEL, NIL_PTR) -#define new_entry(d, e) directory(d, e, ENTRY, NIL_PTR) - -#define is_dir(d) ((d)->d_attribute & SUB_DIR) - -#define STD_OUT 1 - -char *cmnd; - -static int disk; /* File descriptor for disk I/O */ - -#ifdef CACHE_ROOT -static DIRECTORY root[MAX_ROOT_ENTRIES]; -#else -static DIRECTORY *root = NULL; -#endif -static DIRECTORY save_entry; -static char drive[] = "/dev/dosX"; -static char drive2[] = "/dev/fdX"; -#define DRIVE_NR (sizeof (drive) - 2) -static char buffer[MAX_CLUSTER_SIZE], *device = drive, path[128]; -static long data_start; -static long mark; /* offset of directory entry to be written */ -static unsigned short total_clusters, cluster_size, root_entries, sub_entries; -static unsigned long fat_size; -static char *unixfile; -FILE *fdo; - -static BOOL Rflag, Lflag, Aflag, dos_read, dos_write, dos_dir, dos_del, fat_16 = 0; -static BOOL big_endian; - -/* maximum size of a cooked 12bit FAT. Also Size of 16bit FAT cache - * if not enough memory for whole FAT - */ -#ifdef MSX_UZIX_TARGET -#define COOKED_SIZE 512 -#else -#define COOKED_SIZE 8192 -#endif -/* raw FAT. Only used for 12bit FAT to make conversion easier - */ -static unsigned char *raw_fat; -/* Cooked FAT. May be only part of the FAT for 16 bit FATs - */ -static unsigned short *cooked_fat; -/* lowest and highest entry in fat cache - */ -static unsigned short fat_low = USHRT_MAX, - fat_high = 0; -static BOOL fat_dirty = FALSE; -static unsigned int cache_size; -static unsigned long rawfat_size; - - -/* Prototypes. */ -void usage(const char *prog_name); -unsigned c2u2(const unsigned char *ucarray); -unsigned long c4u4(const unsigned char *ucarray); -void determine(void); -int main(int argc, char *argv []); -DIRECTORY *directory(DIRECTORY *dir, int entries, BOOL function, char *pathname); -void extract(DIRECTORY *entry); -void delete(DIRECTORY *entry); -void make_file(DIRECTORY *dir_ptr, int entries, char *name); -void fill_date(DIRECTORY *entry); -char *make_name(register DIRECTORY *dir_ptr, short dir_fl); -int fill(char *buff, size_t size); -void xmodes(int mode); -void show(DIRECTORY *dir_ptr, char *name); -void free_blocks(void); -DIRECTORY *read_cluster(unsigned int cluster); -unsigned short free_cluster(BOOL leave_fl); -void link_fat(unsigned int cl_1, unsigned int cl_2); -unsigned short next_cluster(unsigned int cl_no); -unsigned short clear_cluster(unsigned int cl_no); -char *slash(char *str); -void add_path(char *file, BOOL slash_fl); -void disk_io(BOOL op, unsigned long seek, void *address, unsigned bytes); -void flush_fat(void); -void read_fat(unsigned int cl_no); -BOOL free_range(unsigned short *first, unsigned short *last); -long lmin(long a, long b); - -void usage(const char *prog_name) -{ - fprintf (stderr, "usage: %s ", prog_name); - if (dos_dir) fprintf(stderr, "[-lr] drive [dir]\n"); - if (dos_read || dos_write) fprintf(stderr, "[-a] drive dosfile [file]\n"); - if (dos_del) fprintf(stderr, "drive dosfile\n"); - exit(1); -} - -unsigned c2u2(const unsigned char *ucarray) -{ - return ucarray[0] + (ucarray[1] << 8); /* parens vital */ -} - -unsigned long c4u4(const unsigned char *ucarray) -{ - return ucarray[0] + ((unsigned long) ucarray[1] << 8) + - ((unsigned long) ucarray[2] << 16) + - ((unsigned long) ucarray[3] << 24); -} - -void determine(void) -{ - struct dosboot { - unsigned char cjump[2]; /* unsigneds avoid bugs */ - unsigned char nop; - unsigned char name[8]; - unsigned char cbytepers[2]; /* don't use shorts, etc */ - unsigned char secpclus; /* to avoid struct member */ - unsigned char creservsec[2]; /* alignment and byte */ - unsigned char fats; /* order bugs */ - unsigned char cdirents[2]; - unsigned char ctotsec[2]; - unsigned char media; - unsigned char csecpfat[2]; - unsigned char csecptrack[2]; - unsigned char cheads[2]; - unsigned char chiddensec[2]; - unsigned char dos4hidd2[2]; - unsigned char dos4totsec[4]; - /* Char fill[476]; */ - } boot; - unsigned short boot_magic; /* last of boot block */ - unsigned bytepers, reservsec, dirents; - unsigned secpfat, secptrack, heads, hiddensec; - unsigned long totsec; - unsigned char fat_info, fat_check; - unsigned short endiantest = 1; - int errcount = 0; - - big_endian = !(*(unsigned char *)&endiantest); - - /* Read Bios-Parameterblock */ - disk_io(READ, 0L, &boot, sizeof boot); - disk_io(READ, 0x1FEL, &boot_magic, sizeof boot_magic); - - /* Convert some arrays */ - bytepers = c2u2(boot.cbytepers); - reservsec = c2u2(boot.creservsec); - dirents = c2u2(boot.cdirents); - totsec = c2u2(boot.ctotsec); - if (totsec == 0) totsec = c4u4(boot.dos4totsec); - secpfat = c2u2(boot.csecpfat); - secptrack = c2u2(boot.csecptrack); - heads = c2u2(boot.cheads); - - /* The `hidden sectors' are the sectors before the partition. - * The calculation here is probably wrong (I think the dos4hidd2 - * bytes are the msbs), but that doesn't matter, since the - * value isn't used anyway - */ - hiddensec = c2u2(boot.chiddensec); - if (hiddensec == 0) hiddensec = c2u2 (boot.dos4hidd2); - -#ifdef PC_UZIX_TARGET - /* Safety checking */ - if (boot_magic != 0xAA55) { - fprintf (stderr, "%s: magic != 0xAA55\n", cmnd); - ++errcount; - } -#endif - - /* Check sectors per track instead of inadequate media byte */ - if (secptrack < 15 && /* assume > 15 hard disk & wini OK */ -#ifdef SECT10 /* BIOS modified for 10 sec/track */ - secptrack != 10 && -#endif -#ifdef SECT8 /* BIOS modified for 8 sec/track */ - secptrack != 8 && -#endif - secptrack != 9) { - fprintf (stderr, "%s: %d sectors per track not supported\n", cmnd, secptrack); - ++errcount; - } - if (bytepers == 0) { - fprintf (stderr, "%s: bytes per sector == 0\n", cmnd); - ++errcount; - } - if (boot.secpclus == 0) { - fprintf (stderr, "%s: sectors per cluster == 0\n", cmnd); - ++errcount; - } - if (boot.fats != 2 && (dos_write || dos_del)) { - fprintf (stderr, "%s: fats != 2\n", cmnd); - ++errcount; - } - if (reservsec != 1) { - fprintf (stderr, "%s: reserved != 1\n", cmnd); - ++errcount; - } - if (errcount != 0) { - fprintf (stderr, "%s: Can't handle disk\n", cmnd); - exit(2); - } - - /* Calculate everything. */ - if (boot.secpclus == 0) boot.secpclus = 1; - total_clusters = - (totsec - boot.fats * secpfat - reservsec - - dirents * 32L / bytepers ) / boot.secpclus + 2; - /* first 2 entries in FAT aren't used */ - cluster_size = bytepers * boot.secpclus; - fat_size = (unsigned long) secpfat * (unsigned long) bytepers; - data_start = (long) bytepers + (long) boot.fats * fat_size - + (long) dirents *32L; - root_entries = dirents; - sub_entries = boot.secpclus * bytepers / 32; - if (total_clusters > 4096) fat_16 = 1; - - /* Further safety checking */ - if (cluster_size > MAX_CLUSTER_SIZE) { - fprintf (stderr, "%s: cluster size too big\n", cmnd); - ++errcount; - } -#ifndef CACHE_ROOT - if (dirents * DIR_SIZE > MAX_CLUSTER_SIZE) { - fprintf(stderr, "%s: root directory too big\n", cmnd); - ++errcount; - } -#endif - - disk_io(READ, FAT_START, &fat_info, 1); - disk_io(READ, FAT_START + fat_size, &fat_check, 1); - if (fat_check != fat_info) { - fprintf (stderr, "%s: Disk type in FAT copy differs from disk type in FAT original.\n", cmnd); - ++errcount; - } - if (errcount != 0) { - fprintf (stderr, "%s: Can't handle disk\n", cmnd); - exit(2); - } -} - -int main(int argc, const char *argv[]) -{ - register char *arg_ptr = slash(argv[0]); - DIRECTORY *entry; - short idx = 1; - char dev_nr = '0'; - - fdo = stdout; - cmnd = arg_ptr; /* needed for error messages */ - if (!strcmp(arg_ptr, "dosdir")) - dos_dir = TRUE; - else if (!strcmp(arg_ptr, "dosread")) - dos_read = TRUE; - else if (!strcmp(arg_ptr, "doswrite")) - dos_write = TRUE; - else if (!strcmp(arg_ptr, "dosdel")) - dos_del = TRUE; - else { - fprintf (stderr, "%s: Program should be named dosread, doswrite, dosdel or dosdir.\n", cmnd); - exit(1); - } - - if (argc == 1) usage(argv[0]); - - if (argv[1][0] == '-') { - for (arg_ptr = &argv[1][1]; *arg_ptr; arg_ptr++) { - if (*arg_ptr == 'l' && dos_dir) { - Lflag = TRUE; - } else if (*arg_ptr == 'r' && dos_dir) { - Rflag = TRUE; - } else if (*arg_ptr == 'a' && !dos_dir && !dos_del) { - assert ('\n' == 10); - assert ('\r' == 13); - Aflag = TRUE; - } else { - usage(argv[0]); - } - } - idx++; - } - if (idx == argc) usage(argv[0]); - - if (strlen(argv[idx]) > 1) { - if (*(argv[idx]+1) == ':') { - if ((dev_nr = toupper (*argv[idx])) < 'A' || dev_nr > 'Z') - usage(argv[0]); - dev_nr = dev_nr - 'A' + '0'; - device = drive2; - device[7] = dev_nr; - if (*(argv[idx]+2) == '\0') - idx++; - else - argv[idx] = (argv[idx] + 2); - } else { - device = argv[idx++]; - - /* If the device does not contain a / we assume that it - * is the name of a device in /dev. Instead of prepending - * /dev/ we try to chdir there. - */ - if (strchr(device, '/') == NULL && chdir("/dev") < 0) { - perror("/dev"); - exit(1); - } - dev_nr = device[strlen(device)-1]; - } - } else { - if ((dev_nr = toupper (*argv[idx++])) < 'A' || dev_nr > 'Z') - usage(argv[0]); - - device[DRIVE_NR] = dev_nr; - } - if ((disk = open(device, (dos_write || dos_del) ? O_RDWR : O_RDONLY)) < 0) { - fprintf (stderr, "%s: cannot open %s: %s\n", - cmnd, device, strerror (errno)); - exit(1); - } - determine(); - -#ifdef CACHE_ROOT - disk_io(READ, ROOTADDR, root, - root_entries > MAX_ROOT_ENTRIES ? - DIR_SIZE * MAX_ROOT_ENTRIES : - DIR_SIZE * root_entries); - if (root_entries > MAX_ROOT_ENTRIES) { - fprintf (stderr, "%s: root dir limited to %d files\n", cmnd, MAX_ROOT_ENTRIES); - } -#endif - - if (dos_read || dos_write) { - unixfile = argv[idx+1]; - if (unixfile == NIL_PTR) { - fdo = stdout; - if (dos_write) fdo = stdin; - } else { - if ((fdo = fopen(unixfile, dos_read ? "wb" : "rb")) == NULL) { - perror(argv[0]); - exit(1); - } - } - } - - if (dos_dir && Lflag) { - entry = label(); - fprintf (fdo, "Volume in drive %c ", dev_nr - '0' + 'A'); - if (entry == NIL_DIR) - fprintf(fdo, "has no label.\n\n"); - else - fprintf (fdo, "is %.11s\n\n", entry->d_name); - } - if (argv[idx] == NIL_PTR) { - if (!dos_dir) usage(argv[0]); - if (Lflag) fprintf (fdo, "Root directory:\n"); - list_dir(root, root_entries, FALSE); - if (Lflag) free_blocks(); - fflush (fdo); - FABORT(0); - } - for (arg_ptr = argv[idx]; *arg_ptr; arg_ptr++) - if (*arg_ptr == '\\') *arg_ptr = '/'; - else *arg_ptr = toupper (*arg_ptr); - if (*--arg_ptr == '/') *arg_ptr = '\0'; /* skip trailing '/' */ - - add_path(argv[idx], FALSE); - add_path("/", FALSE); - - if (dos_dir && Lflag) fprintf (fdo, "Directory %s:\n", path); - - entry = find_entry(root, root_entries, argv[idx]); - - if (dos_dir) { - list_dir(entry, sub_entries, FALSE); - if (Lflag) free_blocks(); - } else if (dos_read) - extract(entry); - else if (dos_del) - delete(entry); - else { - if (entry != NIL_DIR) { - fflush (fdo); - if (is_dir(entry)) - fprintf (stderr, "%s: %s is a directory.\n", cmnd, path); - else - fprintf (stderr, "%s: %s already exists.\n", cmnd, argv[idx]); - exit(1); - } - add_path(NIL_PTR, TRUE); - - if (*path) make_file(find_entry(root, root_entries, path), - sub_entries, slash(argv[idx])); - else - make_file(root, root_entries, argv[idx]); - } - - (void) close(disk); - fflush (fdo); - if (fdo != stdout) fclose(fdo); - return(0); -} - - -/* General directory search routine. - * - * dir: - * Points to one or more directory entries - * if dir == root, when ROOT_CACHE is defined, dir points to the - * entire root directory; if ROOT_CACHE is not defined, root dir - * will be read from disk when needed. If dir != root, it points - * to a single directory entry describing the directory to be - * searched. - * - * entries: - * number of entries - * - * function: - * FIND ... find pathname relative to directory dir. - * LABEL ... find first label entry in dir. - * ENTRY ... create a new empty entry. - * FALSE ... list directory - * - * pathname: - * name of the file to be found or directory to be listed. - * must be in upper case, pathname components must be - * separated by slashes, but can be longer than than - * 8+3 characters (The rest is ignored). - */ -DIRECTORY *directory(DIRECTORY *dir, int entries, int function, char *pathname) -{ - register DIRECTORY *dir_ptr = dir; - DIRECTORY *mem = NIL_DIR; - unsigned short cl_no = dir->d_cluster; - unsigned short type, last = 0; - char file_name[14]; - char dir_bkp[DIR_SIZE]; - char *name; - int i = 0; - - if (dir != NULL) { - memcpy((char *)dir_bkp, (char *)dir, DIR_SIZE); - dir = (void *)dir_bkp; - } - if (function == FIND) { - while (*pathname == '/') *pathname++; - while (*pathname != '/' && *pathname != '.' && *pathname && - i < 8) { - file_name[i++] = *pathname++; - } - if (*pathname == '.') { - int j = 0; - file_name[i++] = *pathname++; - while (*pathname != '/' && *pathname != '.' && *pathname && - j++ < 3) { - file_name[i++] = *pathname++; - } - } - while (*pathname != '/' && *pathname) pathname++; - file_name[i] = '\0'; - } - do { - if (dir != root) { - mem = dir_ptr = read_cluster(cl_no); - last = cl_no; - cl_no = next_cluster(cl_no); - } -#ifndef CACHE_ROOT - else { - disk_io(READ, ROOTADDR, buffer, root_entries * DIR_SIZE); - dir_ptr = (void *)buffer; - cl_no = dir_ptr->d_cluster; - } -#endif - for (i = 0; i < entries; i++, dir_ptr++) { - type = dir_ptr->d_name[0] & 0x0FF; - if (!mem) - mark = ROOTADDR + (long) i *(long) DIR_SIZE; - else - mark = clus_add(last) + (long) i *(long) DIR_SIZE; - if (function == ENTRY) { - if (type == NOT_USED || type == ERASED) return dir_ptr; - continue; - } - if (type == NOT_USED) break; - if (dir_ptr->d_attribute & 0x08) { - if (function == LABEL) return dir_ptr; - continue; - } - if (type == DIR || type == ERASED || function == LABEL) - continue; - type = is_dir(dir_ptr); - name = make_name(dir_ptr, - (function == FIND) ? FALSE : type); - if (function == FIND) { - if (strcmp(file_name, name) != 0) continue; - if (!type) { - if (dos_dir || *pathname) { - fflush (stdout); - fprintf (stderr, "%s: Not a directory: %s\n", cmnd, file_name); - exit(1); - } - } else if (*pathname == '\0' && dos_read) { - fflush (stdout); - fprintf (stderr, "%s: %s is a directory.\n", cmnd, path); - exit(1); - } - if (*pathname) { - dir_ptr = find_entry(dir_ptr, - sub_entries, pathname + 1); - } - if (mem) { - if (dir_ptr) { - memcpy((char *)&save_entry, (char *)dir_ptr, DIR_SIZE); - dir_ptr = &save_entry; - } - } - return dir_ptr; - } else { - if (function == FALSE) { - show(dir_ptr, name); - } else if (type) { /* Recursive */ - printf ( "Directory %s%s:\n", path, name); - add_path(name, FALSE); - list_dir(dir_ptr, sub_entries, FALSE); - add_path(NIL_PTR, FALSE); -#ifndef CACHE_ROOT - /* Re-read directory from disk */ - mem = dir_ptr = read_cluster(cl_no); -#endif - } - } - } - } while (cl_no != LAST_CLUSTER && mem); - - switch (function) { - case FIND: - if (dos_write && *pathname == '\0') return NIL_DIR; - fflush (stdout); - fprintf (stderr, "%s: Cannot find `%s'.\n", cmnd, file_name); - exit(1); - case LABEL: - return NIL_DIR; - case ENTRY: - if (!mem) { - fflush (stdout); - fprintf (stderr, "%s: No entries left in root directory.\n", cmnd); - exit(1); - } - cl_no = free_cluster(TRUE); - link_fat(last, cl_no); - link_fat(cl_no, LAST_CLUSTER); - memset(buffer, 0, cluster_size); - disk_io(WRITE, clus_add(cl_no), buffer, cluster_size); - - return new_entry(dir, entries); - case FALSE: - if (Rflag) { - printf ("\n"); - list_dir(dir, entries, TRUE); - } - } - return NULL; -} - -void delete(DIRECTORY *entry) -{ - register unsigned short cl_no = entry->d_cluster; - - entry->d_name[0] = 0xe5; - disk_io(WRITE, mark, entry, DIR_SIZE); - while (cl_no != LAST_CLUSTER) cl_no = clear_cluster(cl_no); - if (fat_dirty) flush_fat (); -} - -void extract(DIRECTORY *entry) -{ - register unsigned short cl_no = entry->d_cluster; - int rest, i; - long size = entry->d_size; - - if (size == 0) /* Empty file */ - return; - - do { - disk_io(READ, clus_add(cl_no), buffer, cluster_size); - rest = (size > (long) cluster_size) ? cluster_size : (short)size; - - if (Aflag) { - for (i = 0; i < rest; i ++) { - if (buffer [i] != '\r') fputc (buffer [i], fdo); - } - if (ferror (stdout)) { - fprintf (stderr, "%s: cannot write file: %s\n", - cmnd, strerror (errno)); - FABORT(1); - } - } else { - if (fwrite (buffer, 1, rest, fdo) != rest) { - fprintf (stderr, "%s: cannot write file: %s\n", - cmnd, strerror (errno)); - FABORT(1); - } - } - size -= (long) rest; - cl_no = next_cluster(cl_no); - if (cl_no == BAD16) { - fflush (stdout); - fprintf (stderr, "%s: reserved cluster value %x encountered.\n", - cmnd, cl_no); - FABORT(1); - } - } while (size && cl_no != LAST_CLUSTER); - - if (cl_no != LAST_CLUSTER) - fprintf (stderr, "%s: Too many clusters allocated for file.\n", cmnd); - else if (size != 0) - fprintf (stderr, "%s: Premature EOF: %ld bytes left.\n", cmnd, - entry->d_size); -} - - -/* Minimum of two long values - */ -long lmin (long a, long b) -{ - if (a < b) return a; - else return b; -} - - -void make_file(DIRECTORY *dir_ptr, int entries, char *name) -{ - register DIRECTORY *entry = new_entry(dir_ptr, entries); - register char *ptr; - unsigned short cl_no = 0; - int i, r; - long size = 0L; - unsigned short first_cluster, last_cluster; - long chunk; - char dir_bkp[DIR_SIZE]; - - memcpy((char *)dir_bkp, (char *)entry, DIR_SIZE); - entry = (DIRECTORY *)dir_bkp; - memset (&entry->d_name[0], ' ', 11); /* clear entry */ - for (i = 0, ptr = name; i < 8 && *ptr != '.' && *ptr; i++) - entry->d_name[i] = *ptr++; - while (*ptr != '.' && *ptr) ptr++; - if (*ptr == '.') ptr++; - for (i = 0; i < 3 && *ptr != '.' && *ptr; i++) entry->d_ext[i] = *ptr++; - - for (i = 0; i < 10; i++) entry->d_reserved[i] = '\0'; - entry->d_attribute = '\0'; - - entry->d_cluster = 0; - - while (free_range (&first_cluster, &last_cluster)) { - do { - unsigned short nr_clus; - - chunk = lmin ((long) (last_cluster - first_cluster + 1) * - cluster_size, - (long) MAX_CLUSTER_SIZE); - r = fill(buffer, chunk); - if (r == 0) goto done; - nr_clus = (r + cluster_size - 1) / cluster_size; - disk_io(WRITE, clus_add(first_cluster), buffer, r); - - for (i = 0; i < nr_clus; i ++) { - if (entry->d_cluster == 0) - cl_no = entry->d_cluster = first_cluster; - else { - link_fat(cl_no, first_cluster); - cl_no = first_cluster; - } - first_cluster ++; - } - - size += r; - } while (first_cluster <= last_cluster); - } - fprintf (stderr, "%s: disk full. File truncated\n", cmnd); -done: - if (entry->d_cluster != 0) link_fat(cl_no, LAST_CLUSTER); - entry->d_size = size; - fill_date(entry); - disk_io(WRITE, mark, entry, DIR_SIZE); - - if (fat_dirty) flush_fat (); -} - - -#define SEC_MIN 60L -#define SEC_HOUR (60L * SEC_MIN) -#define SEC_DAY (24L * SEC_HOUR) -#define SEC_YEAR (365L * SEC_DAY) -#define SEC_LYEAR (366L * SEC_DAY) - -unsigned short mon_len[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - -void fill_date(DIRECTORY *entry) -{ - time_t atime; - unsigned long cur_time; - unsigned short year = 0, month = 1, day, hour, minutes, seconds; - int i; - long tmp; - - time(&atime); - cur_time = atime - DOS_TIME; - for (;;) { - tmp = (year % 4 == 0) ? SEC_LYEAR : SEC_YEAR; - if (cur_time < tmp) break; - cur_time -= tmp; - year++; - } - day = (unsigned short) (cur_time / SEC_DAY); - cur_time -= (long) day *SEC_DAY; - - hour = (unsigned short) (cur_time / SEC_HOUR); - cur_time -= (long) hour *SEC_HOUR; - - minutes = (unsigned short) (cur_time / SEC_MIN); - cur_time -= (long) minutes *SEC_MIN; - - seconds = (unsigned short) cur_time; - - mon_len[1] = (year % 4 == 0) ? 29 : 28; - i = 0; - while (day >= mon_len[i]) { - month++; - day -= mon_len[i++]; - } - day++; - - entry->d_date = (year << 9) | (month << 5) | day; - entry->d_time = (hour << 11) | (minutes << 5) | seconds; -} - -char *make_name(DIRECTORY *dir_ptr, short dir_fl) -{ - static char name_buf[14]; - register char *ptr = name_buf; - short i; - - for (i = 0; i < 8; i++) *ptr++ = dir_ptr->d_name[i]; - - while (*--ptr == ' '); - assert (ptr >= name_buf); - - ptr++; - if (dir_ptr->d_ext[0] != ' ') { - *ptr++ = '.'; - for (i = 0; i < 3; i++) *ptr++ = dir_ptr->d_ext[i]; - while (*--ptr == ' '); - ptr++; - } - if (dir_fl) *ptr++ = '/'; - *ptr = '\0'; - - return name_buf; -} - - -int fill(char *buff, size_t size) -{ - static BOOL nl_mark = FALSE; - char *last = &buff[size]; - char *begin = buff; - register int c; - - while (buff < last) { - if (nl_mark) { - *buff ++ = '\n'; - nl_mark = FALSE; - } else { - c = fgetc(fdo); - if (c == EOF) break; - if (Aflag && c == '\n') { - *buff ++ = '\r'; - nl_mark = TRUE; - } else { - *buff++ = c; - } - } - } - - return (buff - begin); -} - -#define HOUR 0xF800 /* Upper 5 bits */ -#define MIN 0x07E0 /* Middle 6 bits */ -#define YEAR 0xFE00 /* Upper 7 bits */ -#define MONTH 0x01E0 /* Mid 4 bits */ -#define DAY 0x01F /* Lowest 5 bits */ - -char *month[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" -}; - -void xmodes(int mode) -{ - printf ( "\t%c%c%c%c%c", (mode & SUB_DIR) ? 'd' : '-', - (mode & 02) ? 'h' : '-', (mode & 04) ? 's' : '-', - (mode & 01) ? '-' : 'w', (mode & 0x20) ? 'a' : '-'); -} - -void show(DIRECTORY *dir_ptr, char *name) -{ - register unsigned short e_date = dir_ptr->d_date; - register unsigned short e_time = dir_ptr->d_time; - unsigned short next; - char bname[20]; - short i = 0; - - while (*name && *name != '/') bname[i++] = *name++; - bname[i] = '\0'; - if (!Lflag) { - fprintf (fdo, "%s\n", bname); - return; - } - xmodes( (int) dir_ptr->d_attribute); - fprintf (fdo, "\t%s%s", bname, strlen(bname) < 8 ? "\t\t" : "\t"); - i = 1; - if (is_dir(dir_ptr)) { - next = dir_ptr->d_cluster; - while ((next = next_cluster(next)) != LAST_CLUSTER) i++; - fprintf (fdo, "%8ld", (long) i * (long) cluster_size); - } else - fprintf (fdo, "%8ld", dir_ptr->d_size); - fprintf (fdo, " %02d:%02d %2d %s %d\n", ((e_time & HOUR) >> 11), - ((e_time & MIN) >> 5), (e_date & DAY), - month[((e_date & MONTH) >> 5) - 1], ((e_date & YEAR) >> 9) + 1980); -} - -void free_blocks(void) -{ - register unsigned short cl_no; - long nr_free = 0; - long nr_bad = 0; - - for (cl_no = 2; cl_no < total_clusters; cl_no++) { - switch (next_cluster(cl_no)) { - case FREE: nr_free++; break; - case BAD16: nr_bad++; break; - } - } - - fprintf (fdo, "Free space: %ld bytes.\n", nr_free * (long) cluster_size); - if (nr_bad != 0) - fprintf (fdo, "Bad sectors: %ld bytes.\n", nr_bad * (long) cluster_size); -} - - -DIRECTORY *read_cluster(unsigned int cluster) -{ - disk_io(READ, clus_add(cluster), buffer, cluster_size); - - return (DIRECTORY *)buffer; -} - -static unsigned short cl_index = 2; - -/* find a range of consecutive free clusters. Return TRUE if found - * and return the first and last cluster in the |*first| and |*last|. - * If no free clusters are left, return FALSE. - * - * Warning: Assumes that all of the range is used before the next call - * to free_range or free_cluster. - */ -BOOL free_range (unsigned short *first, unsigned short *last) -{ - while (cl_index < total_clusters && next_cluster(cl_index) != FREE) - cl_index++; - if (cl_index >= total_clusters) return FALSE; - *first = cl_index; - while (cl_index < total_clusters && next_cluster(cl_index) == FREE) - cl_index++; - *last = cl_index - 1; - return TRUE; -} - - -/* find a free cluster. - * Return the number of the free cluster or a number > |total_clusters| - * if none is found. - * If |leave_fl| is TRUE, the the program will be terminated if - * no free cluster can be found - * - * Warning: Assumes that the cluster is used before the next call - * to free_range or free_cluster. - */ -unsigned short free_cluster(BOOL leave_fl) -{ - while (cl_index < total_clusters && next_cluster(cl_index) != FREE) - cl_index++; - - if (leave_fl && cl_index >= total_clusters) { - fprintf (stderr, "%s: Diskette full. File not added.\n", cmnd); - exit(1); - } - return cl_index++; -} - -/* read a portion of the fat containing |cl_no| into the cache - */ -void read_fat (unsigned int cl_no) -{ - - if (!cooked_fat) { - /* Read the fat for the first time. We have to allocate all the - * buffers - */ - if (fat_16) { - /* FAT consists of little endian shorts. Easy to convert - */ - if ((cooked_fat = malloc (fat_size)) == NULL) { - /* Oops, FAT doesn't fit into memory, just read - * a chunk - */ - if ((cooked_fat = malloc (COOKED_SIZE)) == NULL) { - fprintf (stderr, "%s: not enough memory for FAT cache.\n", - cmnd); - exit (1); - } - cache_size = COOKED_SIZE / 2; - } else { - cache_size = fat_size / 2; - } - } else { - /* 12 bit FAT. Difficult encoding, but small. Keep - * both raw FAT and cooked version in memory if possible. - */ - cooked_fat = malloc (total_clusters * sizeof (short)); - raw_fat = malloc (fat_size); - if (cooked_fat == NULL || raw_fat == NULL) { - if (cooked_fat != NULL) free(cooked_fat); - if (raw_fat != NULL) free(raw_fat); - /* Oops, FAT doesn't fit into memory, just read - * a chunk - */ - cooked_fat = malloc (COOKED_SIZE); - raw_fat = malloc (COOKED_SIZE/sizeof (short)/2*3); - if (cooked_fat == NULL || raw_fat == NULL) { - fprintf (stderr, "%s: not enough memory for FAT cache.\n", - cmnd); - exit (1); - } - cache_size = COOKED_SIZE / sizeof(short); - rawfat_size = COOKED_SIZE / sizeof(short)/2*3; - } else { - cache_size = total_clusters; - rawfat_size = fat_size; - } - } - } - fat_low = cl_no; /* / cache_size * cache_size; ??? */ - - /* for FAT 12, round fat_low to a multiple of 2, so, when reading FAT from - * disk, we start at this cluster or previous one */ - if (!fat_16) if (fat_low & 1 != 0) fat_low--; - - fat_high = fat_low + cache_size - 1; - - if (!fat_16) { - unsigned short *cp; - unsigned char *rp; - unsigned short i; - - disk_io (READ, FAT_START + fat_low * 3 / 2, raw_fat, rawfat_size); - for (rp = raw_fat, cp = cooked_fat, i = 0; - i < cache_size; - rp += 3, i += 2) { - *cp = *rp + ((*(rp + 1) & 0x0f) << 8); - if (*cp == BAD) *cp = BAD16; - else if (*cp == LAST_CLUSTER12) *cp = LAST_CLUSTER; - cp ++; - *cp = ((*(rp + 1) & 0xf0) >> 4) + (*(rp + 2) << 4); - if (*cp == BAD) *cp = BAD16; - else if (*cp == LAST_CLUSTER12) *cp = LAST_CLUSTER; - cp ++; - } - } else { - assert (sizeof (short) == 2); - assert (CHAR_BIT == 8); /* just in case */ - - disk_io (READ, FAT_START + fat_low * 2, (void *)cooked_fat, cache_size * 2); - if (big_endian) { - unsigned short *cp; - unsigned char *rp; - unsigned short i; - - for (i = 0, rp = (unsigned char *)cooked_fat /* sic */, cp = cooked_fat; - i < cache_size; - rp += 2, cp ++, i ++) { - *cp = c2u2 (rp); - } - } - } -} - -/* flush the fat cache out to disk - */ -void flush_fat(void) -{ - if (fat_16) { - if (big_endian) { - unsigned short *cp; - unsigned char *rp; - unsigned short i; - - for (i = 0, rp = (unsigned char *)cooked_fat /* sic */, cp = cooked_fat; - i < cache_size; - rp += 2, cp ++, i ++) { - *rp = *cp; - *(rp + 1) = *cp >> 8; - } - } - disk_io (WRITE, FAT_START + fat_low * 2, (void *)cooked_fat, cache_size * 2); - disk_io (WRITE, FAT_START + fat_size + fat_low * 2, (void *)cooked_fat, cache_size * 2); - } else { - unsigned short *cp; - unsigned char *rp; - unsigned short i; - - for (rp = raw_fat, cp = cooked_fat, i = 0; - i < cache_size; - rp += 3, cp += 2, i += 2) { - *rp = *cp; - *(rp + 1) = ((*cp & 0xf00) >> 8) | - ((*(cp + 1) & 0x00f) << 4); - *(rp + 2) = ((*(cp + 1) & 0xff0) >> 4); - } - disk_io (WRITE, FAT_START + fat_low * 3 / 2, raw_fat, rawfat_size); - disk_io (WRITE, FAT_START + fat_size + fat_low * 3 / 2, raw_fat, rawfat_size); - } -} - -/* make cl_2 the successor of cl_1 - */ -void link_fat(unsigned int cl_1, unsigned int cl_2) -{ - if (cl_1 < fat_low || cl_1 > fat_high) { - if (fat_dirty) flush_fat (); - read_fat (cl_1); - } - cooked_fat [cl_1 - fat_low] = cl_2; - fat_dirty = TRUE; -} - - -unsigned short next_cluster(unsigned int cl_no) -{ - if (cl_no < fat_low || cl_no > fat_high) { - if (fat_dirty) flush_fat (); - read_fat (cl_no); - } - return cooked_fat [cl_no - fat_low]; -} - -/* free cluster cl_no in FAT and return its sucessor */ -unsigned short clear_cluster(unsigned int cl_no) -{ - unsigned short old; - - if (cl_no < fat_low || cl_no > fat_high) { - if (fat_dirty) flush_fat (); - read_fat (cl_no); - } - old = cooked_fat[cl_no - fat_low]; - cooked_fat[cl_no - fat_low] = 0; - fat_dirty = TRUE; - return old; -} - -char *slash(char *str) -{ - register char *result = str; - - while (*str) - if (*str++ == '/') result = str; - - return result; -} - -void add_path(char *file, BOOL slash_fl) -{ - register char *ptr = path; - - while (*ptr) ptr++; - - if (file == NIL_PTR) { - if (ptr != path) ptr--; - if (ptr != path) do { - ptr--; - } while (*ptr != '/' && ptr != path); - if (ptr != path && !slash_fl) *ptr++ = '/'; - *ptr = '\0'; - } else - strcpy (ptr, file); -} - - -void disk_io(BOOL op, unsigned long seek, void *address, unsigned bytes) -{ - unsigned int r; - - if (lseek(disk, seek, SEEK_SET) < 0L) { - fflush (stdout); - fprintf (stderr, "%s: Bad lseek: %s\n", cmnd, strerror (errno)); - exit(1); - } - if (op == READ) - r = read(disk, (char *) address, bytes); - else { - r = write(disk, (char *) address, bytes); - }; - - if (r != bytes) { - fprintf (stderr, "%s: %s error: %s\n", op == READ ? "read" : "write", cmnd, strerror (errno)); - exit (1); - } -} - -char dosread_c_rcs_id [] = - "$Id: dosread.c,v 2.0 2001/03/11 14:30:43 adrcunha Rel $"; - -/* $Log: dosread.c,v $ - * Revision 2.0 2001/03/11 14:30:43 adrcunha@dcc.unicamp.br - * Fixed some bugs, added FAT12 cache, decreased memory usage, changed - * drive number to letter on dir header, introduced some sanity check for - * memory, added compilation directive CACHE_ROOT (with this, root dir is - * loaded on memory at the beginning; without this, root dir is allways - * read from disk when needed - it's slow, but saves memory). - * - * Revision 1.9 1999/11/28 14:32:37 adrcunha@dcc.unicamp.br - * Dosread and doswrite can read/write to/from a file, not only stdin/stdout. - * Added one more personality to the schizophrenic dosread: dosdel - * Drives can be referenced as A:, B:, etc for /dev/fd0, /dev/fd1, etc - * Dosfiles can be referenced after drive if drive is 'X:' (e.g., A:FOO.BAR) - * - * Revision 1.8 1994/05/14 21:53:08 hjp - * filenames with more than 3 characters and extension work again. - * removed debugging stuff and b_copy. - * - * Revision 1.7 1994/04/09 03:09:01 hjp - * (posted to comp.os.minix) - * merged branch 1.5.387 with stem. - * changed treatment of drive parameter - * - * Revision 1.5.387.9 1994/04/09 02:07:51 hjp - * Disk full no longer produces lost clusters but a truncated file. - * Truncated file names to 8+3 before comparisons to avoid duplicate - * files and filenames containing dots in the extension. - * Replaced sbrk and brk by malloc and free (mixing brk and malloc causes - * heap corruption which sometimes lead to core dumps. It may also have - * been the cause of data corruption Kees reported). - * Made global variables static and removed some unused ones. - * Error messages now contain program name. - * - * Revision 1.5.387.8 1993/11/13 00:38:45 hjp - * Posted to comp.os.minix and included in Minix-386vm 1.6.25.1. - * Speed optimizations for 1.44 MB disks. - * Replaced lowlevel I/O by stdio. - * Simplified -a: Now only removes resp. adds CRs - * Cleaned up. - * - * Revision 1.5.387.1 1993/01/15 19:32:29 ast - * Released with 1.6.24b - */ +/* dos{dir|read|write|del} - {list|read|write|del} MS-DOS disks Author: M. Huisjes */ + +/* dosdir - list MS-DOS directories to stdout + * doswrite - write stdin/file to DOS-file + * dosread - read DOS-file to stdout/file + * dosdel - delete DOS-file + * + * Author: Michiel Huisjes. + * + * Usage: dos... [-lra] drive [ms-dos file/dir] [file] + * l: Give long listing. + * r: List recursively. + * a: Set ASCII bit. + */ + +/* no assertions - let's save memory! */ +#define NDEBUG +#undef CACHE_ROOT + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef MSX_UZIX_TARGET +/* these are true numbers, but you can increase if you have memory */ +#define MAX_CLUSTER_SIZE 8192 +#define MAX_ROOT_ENTRIES 256 +#else +#ifdef PC_UZIX_TARGET +/* due to limitation of memory... */ +#define MAX_CLUSTER_SIZE 2048 +#define MAX_ROOT_ENTRIES 256 +#else +#define MAX_CLUSTER_SIZE 4096 +#define MAX_ROOT_ENTRIES 512 +#endif +#endif +#define FAT_START 512L /* After bootsector */ +#define ROOTADDR (FAT_START + 2L * fat_size) +#define clus_add(cl_no) ((long) (((long) cl_no - 2L) \ + * (long) cluster_size \ + + data_start \ + )) +struct dir_entry { + unsigned char d_name[8]; + unsigned char d_ext[3]; + unsigned char d_attribute; + unsigned char d_reserved[10]; + unsigned short d_time; + unsigned short d_date; + unsigned short d_cluster; + unsigned long d_size; +}; + +typedef struct dir_entry DIRECTORY; + +#define NOT_USED 0x00 +#define ERASED 0xE5 +#define DIR 0x2E +#define DIR_SIZE (sizeof (struct dir_entry)) +#define SUB_DIR 0x10 +#define NIL_DIR ((DIRECTORY *) 0) + +#define LAST_CLUSTER12 0xFFF +#define LAST_CLUSTER 0xFFFF +#define FREE 0x000 +#define BAD 0xFF0 +#define BAD16 0xFFF0 + +#define FCLOSE if (fdo != stdout) fclose(fdo) +#define FABORT(x) FCLOSE; exit(x) + +typedef int BOOL; + +#define TRUE 1 +#define FALSE 0 +#define NIL_PTR ((char *) 0) + +#define DOS_TIME 315532800L /* 1970 - 1980 */ + +#define READ 0 +#define WRITE 1 + +#define FIND 3 +#define LABEL 4 +#define ENTRY 5 +#define find_entry(d, e, p) directory(d, e, FIND, p) +#define list_dir(d, e, f) (void) directory(d, e, f, NIL_PTR) +#define label() directory(root, root_entries, LABEL, NIL_PTR) +#define new_entry(d, e) directory(d, e, ENTRY, NIL_PTR) + +#define is_dir(d) ((d)->d_attribute & SUB_DIR) + +#define STD_OUT 1 + +char *cmnd; + +static int disk; /* File descriptor for disk I/O */ + +#ifdef CACHE_ROOT +static DIRECTORY root[MAX_ROOT_ENTRIES]; +#else +static DIRECTORY *root = NULL; +#endif +static DIRECTORY save_entry; +static char drive[] = "/dev/dosX"; +static char drive2[] = "/dev/fdX"; +#define DRIVE_NR (sizeof (drive) - 2) +static char buffer[MAX_CLUSTER_SIZE], *device = drive, path[128]; +static long data_start; +static long mark; /* offset of directory entry to be written */ +static unsigned short total_clusters, cluster_size, root_entries, sub_entries; +static unsigned long fat_size; +static char *unixfile; +FILE *fdo; + +static BOOL Rflag, Lflag, Aflag, dos_read, dos_write, dos_dir, dos_del, fat_16 = 0; +static BOOL big_endian; + +/* maximum size of a cooked 12bit FAT. Also Size of 16bit FAT cache + * if not enough memory for whole FAT + */ +#ifdef MSX_UZIX_TARGET +#define COOKED_SIZE 512 +#else +#define COOKED_SIZE 8192 +#endif +/* raw FAT. Only used for 12bit FAT to make conversion easier + */ +static unsigned char *raw_fat; +/* Cooked FAT. May be only part of the FAT for 16 bit FATs + */ +static unsigned short *cooked_fat; +/* lowest and highest entry in fat cache + */ +static unsigned short fat_low = USHRT_MAX, + fat_high = 0; +static BOOL fat_dirty = FALSE; +static unsigned int cache_size; +static unsigned long rawfat_size; + + +/* Prototypes. */ +void usage(const char *prog_name); +unsigned c2u2(const unsigned char *ucarray); +unsigned long c4u4(const unsigned char *ucarray); +void determine(void); +int main(int argc, char *argv []); +DIRECTORY *directory(DIRECTORY *dir, int entries, BOOL function, char *pathname); +void extract(DIRECTORY *entry); +void delete(DIRECTORY *entry); +void make_file(DIRECTORY *dir_ptr, int entries, char *name); +void fill_date(DIRECTORY *entry); +char *make_name(register DIRECTORY *dir_ptr, short dir_fl); +int fill(char *buff, size_t size); +void xmodes(int mode); +void show(DIRECTORY *dir_ptr, char *name); +void free_blocks(void); +DIRECTORY *read_cluster(unsigned int cluster); +unsigned short free_cluster(BOOL leave_fl); +void link_fat(unsigned int cl_1, unsigned int cl_2); +unsigned short next_cluster(unsigned int cl_no); +unsigned short clear_cluster(unsigned int cl_no); +char *slash(char *str); +void add_path(char *file, BOOL slash_fl); +void disk_io(BOOL op, unsigned long seek, void *address, unsigned bytes); +void flush_fat(void); +void read_fat(unsigned int cl_no); +BOOL free_range(unsigned short *first, unsigned short *last); +long lmin(long a, long b); + +void usage(const char *prog_name) +{ + fprintf (stderr, "usage: %s ", prog_name); + if (dos_dir) fprintf(stderr, "[-lr] drive [dir]\n"); + if (dos_read || dos_write) fprintf(stderr, "[-a] drive dosfile [file]\n"); + if (dos_del) fprintf(stderr, "drive dosfile\n"); + exit(1); +} + +unsigned c2u2(const unsigned char *ucarray) +{ + return ucarray[0] + (ucarray[1] << 8); /* parens vital */ +} + +unsigned long c4u4(const unsigned char *ucarray) +{ + return ucarray[0] + ((unsigned long) ucarray[1] << 8) + + ((unsigned long) ucarray[2] << 16) + + ((unsigned long) ucarray[3] << 24); +} + +void determine(void) +{ + struct dosboot { + unsigned char cjump[2]; /* unsigneds avoid bugs */ + unsigned char nop; + unsigned char name[8]; + unsigned char cbytepers[2]; /* don't use shorts, etc */ + unsigned char secpclus; /* to avoid struct member */ + unsigned char creservsec[2]; /* alignment and byte */ + unsigned char fats; /* order bugs */ + unsigned char cdirents[2]; + unsigned char ctotsec[2]; + unsigned char media; + unsigned char csecpfat[2]; + unsigned char csecptrack[2]; + unsigned char cheads[2]; + unsigned char chiddensec[2]; + unsigned char dos4hidd2[2]; + unsigned char dos4totsec[4]; + /* Char fill[476]; */ + } boot; + unsigned short boot_magic; /* last of boot block */ + unsigned bytepers, reservsec, dirents; + unsigned secpfat, secptrack, heads, hiddensec; + unsigned long totsec; + unsigned char fat_info, fat_check; + unsigned short endiantest = 1; + int errcount = 0; + + big_endian = !(*(unsigned char *)&endiantest); + + /* Read Bios-Parameterblock */ + disk_io(READ, 0L, &boot, sizeof boot); + disk_io(READ, 0x1FEL, &boot_magic, sizeof boot_magic); + + /* Convert some arrays */ + bytepers = c2u2(boot.cbytepers); + reservsec = c2u2(boot.creservsec); + dirents = c2u2(boot.cdirents); + totsec = c2u2(boot.ctotsec); + if (totsec == 0) totsec = c4u4(boot.dos4totsec); + secpfat = c2u2(boot.csecpfat); + secptrack = c2u2(boot.csecptrack); + heads = c2u2(boot.cheads); + + /* The `hidden sectors' are the sectors before the partition. + * The calculation here is probably wrong (I think the dos4hidd2 + * bytes are the msbs), but that doesn't matter, since the + * value isn't used anyway + */ + hiddensec = c2u2(boot.chiddensec); + if (hiddensec == 0) hiddensec = c2u2 (boot.dos4hidd2); + +#ifdef PC_UZIX_TARGET + /* Safety checking */ + if (boot_magic != 0xAA55) { + fprintf (stderr, "%s: magic != 0xAA55\n", cmnd); + ++errcount; + } +#endif + + /* Check sectors per track instead of inadequate media byte */ + if (secptrack < 15 && /* assume > 15 hard disk & wini OK */ +#ifdef SECT10 /* BIOS modified for 10 sec/track */ + secptrack != 10 && +#endif +#ifdef SECT8 /* BIOS modified for 8 sec/track */ + secptrack != 8 && +#endif + secptrack != 9) { + fprintf (stderr, "%s: %d sectors per track not supported\n", cmnd, secptrack); + ++errcount; + } + if (bytepers == 0) { + fprintf (stderr, "%s: bytes per sector == 0\n", cmnd); + ++errcount; + } + if (boot.secpclus == 0) { + fprintf (stderr, "%s: sectors per cluster == 0\n", cmnd); + ++errcount; + } + if (boot.fats != 2 && (dos_write || dos_del)) { + fprintf (stderr, "%s: fats != 2\n", cmnd); + ++errcount; + } + if (reservsec != 1) { + fprintf (stderr, "%s: reserved != 1\n", cmnd); + ++errcount; + } + if (errcount != 0) { + fprintf (stderr, "%s: Can't handle disk\n", cmnd); + exit(2); + } + + /* Calculate everything. */ + if (boot.secpclus == 0) boot.secpclus = 1; + total_clusters = + (totsec - boot.fats * secpfat - reservsec - + dirents * 32L / bytepers ) / boot.secpclus + 2; + /* first 2 entries in FAT aren't used */ + cluster_size = bytepers * boot.secpclus; + fat_size = (unsigned long) secpfat * (unsigned long) bytepers; + data_start = (long) bytepers + (long) boot.fats * fat_size + + (long) dirents *32L; + root_entries = dirents; + sub_entries = boot.secpclus * bytepers / 32; + if (total_clusters > 4096) fat_16 = 1; + + /* Further safety checking */ + if (cluster_size > MAX_CLUSTER_SIZE) { + fprintf (stderr, "%s: cluster size too big\n", cmnd); + ++errcount; + } +#ifndef CACHE_ROOT + if (dirents * DIR_SIZE > MAX_CLUSTER_SIZE) { + fprintf(stderr, "%s: root directory too big\n", cmnd); + ++errcount; + } +#endif + + disk_io(READ, FAT_START, &fat_info, 1); + disk_io(READ, FAT_START + fat_size, &fat_check, 1); + if (fat_check != fat_info) { + fprintf (stderr, "%s: Disk type in FAT copy differs from disk type in FAT original.\n", cmnd); + ++errcount; + } + if (errcount != 0) { + fprintf (stderr, "%s: Can't handle disk\n", cmnd); + exit(2); + } +} + +int main(int argc, const char *argv[]) +{ + register char *arg_ptr = slash(argv[0]); + DIRECTORY *entry; + short idx = 1; + char dev_nr = '0'; + + fdo = stdout; + cmnd = arg_ptr; /* needed for error messages */ + if (!strcmp(arg_ptr, "dosdir")) + dos_dir = TRUE; + else if (!strcmp(arg_ptr, "dosread")) + dos_read = TRUE; + else if (!strcmp(arg_ptr, "doswrite")) + dos_write = TRUE; + else if (!strcmp(arg_ptr, "dosdel")) + dos_del = TRUE; + else { + fprintf (stderr, "%s: Program should be named dosread, doswrite, dosdel or dosdir.\n", cmnd); + exit(1); + } + + if (argc == 1) usage(argv[0]); + + if (argv[1][0] == '-') { + for (arg_ptr = &argv[1][1]; *arg_ptr; arg_ptr++) { + if (*arg_ptr == 'l' && dos_dir) { + Lflag = TRUE; + } else if (*arg_ptr == 'r' && dos_dir) { + Rflag = TRUE; + } else if (*arg_ptr == 'a' && !dos_dir && !dos_del) { + assert ('\n' == 10); + assert ('\r' == 13); + Aflag = TRUE; + } else { + usage(argv[0]); + } + } + idx++; + } + if (idx == argc) usage(argv[0]); + + if (strlen(argv[idx]) > 1) { + if (*(argv[idx]+1) == ':') { + if ((dev_nr = toupper (*argv[idx])) < 'A' || dev_nr > 'Z') + usage(argv[0]); + dev_nr = dev_nr - 'A' + '0'; + device = drive2; + device[7] = dev_nr; + if (*(argv[idx]+2) == '\0') + idx++; + else + argv[idx] = (argv[idx] + 2); + } else { + device = argv[idx++]; + + /* If the device does not contain a / we assume that it + * is the name of a device in /dev. Instead of prepending + * /dev/ we try to chdir there. + */ + if (strchr(device, '/') == NULL && chdir("/dev") < 0) { + perror("/dev"); + exit(1); + } + dev_nr = device[strlen(device)-1]; + } + } else { + if ((dev_nr = toupper (*argv[idx++])) < 'A' || dev_nr > 'Z') + usage(argv[0]); + + device[DRIVE_NR] = dev_nr; + } + if ((disk = open(device, (dos_write || dos_del) ? O_RDWR : O_RDONLY)) < 0) { + fprintf (stderr, "%s: cannot open %s: %s\n", + cmnd, device, strerror (errno)); + exit(1); + } + determine(); + +#ifdef CACHE_ROOT + disk_io(READ, ROOTADDR, root, + root_entries > MAX_ROOT_ENTRIES ? + DIR_SIZE * MAX_ROOT_ENTRIES : + DIR_SIZE * root_entries); + if (root_entries > MAX_ROOT_ENTRIES) { + fprintf (stderr, "%s: root dir limited to %d files\n", cmnd, MAX_ROOT_ENTRIES); + } +#endif + + if (dos_read || dos_write) { + unixfile = argv[idx+1]; + if (unixfile == NIL_PTR) { + fdo = stdout; + if (dos_write) fdo = stdin; + } else { + if ((fdo = fopen(unixfile, dos_read ? "wb" : "rb")) == NULL) { + perror(argv[0]); + exit(1); + } + } + } + + if (dos_dir && Lflag) { + entry = label(); + fprintf (fdo, "Volume in drive %c ", dev_nr - '0' + 'A'); + if (entry == NIL_DIR) + fprintf(fdo, "has no label.\n\n"); + else + fprintf (fdo, "is %.11s\n\n", entry->d_name); + } + if (argv[idx] == NIL_PTR) { + if (!dos_dir) usage(argv[0]); + if (Lflag) fprintf (fdo, "Root directory:\n"); + list_dir(root, root_entries, FALSE); + if (Lflag) free_blocks(); + fflush (fdo); + FABORT(0); + } + for (arg_ptr = argv[idx]; *arg_ptr; arg_ptr++) + if (*arg_ptr == '\\') *arg_ptr = '/'; + else *arg_ptr = toupper (*arg_ptr); + if (*--arg_ptr == '/') *arg_ptr = '\0'; /* skip trailing '/' */ + + add_path(argv[idx], FALSE); + add_path("/", FALSE); + + if (dos_dir && Lflag) fprintf (fdo, "Directory %s:\n", path); + + entry = find_entry(root, root_entries, argv[idx]); + + if (dos_dir) { + list_dir(entry, sub_entries, FALSE); + if (Lflag) free_blocks(); + } else if (dos_read) + extract(entry); + else if (dos_del) + delete(entry); + else { + if (entry != NIL_DIR) { + fflush (fdo); + if (is_dir(entry)) + fprintf (stderr, "%s: %s is a directory.\n", cmnd, path); + else + fprintf (stderr, "%s: %s already exists.\n", cmnd, argv[idx]); + exit(1); + } + add_path(NIL_PTR, TRUE); + + if (*path) make_file(find_entry(root, root_entries, path), + sub_entries, slash(argv[idx])); + else + make_file(root, root_entries, argv[idx]); + } + + (void) close(disk); + fflush (fdo); + if (fdo != stdout) fclose(fdo); + return(0); +} + + +/* General directory search routine. + * + * dir: + * Points to one or more directory entries + * if dir == root, when ROOT_CACHE is defined, dir points to the + * entire root directory; if ROOT_CACHE is not defined, root dir + * will be read from disk when needed. If dir != root, it points + * to a single directory entry describing the directory to be + * searched. + * + * entries: + * number of entries + * + * function: + * FIND ... find pathname relative to directory dir. + * LABEL ... find first label entry in dir. + * ENTRY ... create a new empty entry. + * FALSE ... list directory + * + * pathname: + * name of the file to be found or directory to be listed. + * must be in upper case, pathname components must be + * separated by slashes, but can be longer than than + * 8+3 characters (The rest is ignored). + */ +DIRECTORY *directory(DIRECTORY *dir, int entries, int function, char *pathname) +{ + register DIRECTORY *dir_ptr = dir; + DIRECTORY *mem = NIL_DIR; + unsigned short cl_no = dir->d_cluster; + unsigned short type, last = 0; + char file_name[14]; + char dir_bkp[DIR_SIZE]; + char *name; + int i = 0; + + if (dir != NULL) { + memcpy((char *)dir_bkp, (char *)dir, DIR_SIZE); + dir = (void *)dir_bkp; + } + if (function == FIND) { + while (*pathname == '/') *pathname++; + while (*pathname != '/' && *pathname != '.' && *pathname && + i < 8) { + file_name[i++] = *pathname++; + } + if (*pathname == '.') { + int j = 0; + file_name[i++] = *pathname++; + while (*pathname != '/' && *pathname != '.' && *pathname && + j++ < 3) { + file_name[i++] = *pathname++; + } + } + while (*pathname != '/' && *pathname) pathname++; + file_name[i] = '\0'; + } + do { + if (dir != root) { + mem = dir_ptr = read_cluster(cl_no); + last = cl_no; + cl_no = next_cluster(cl_no); + } +#ifndef CACHE_ROOT + else { + disk_io(READ, ROOTADDR, buffer, root_entries * DIR_SIZE); + dir_ptr = (void *)buffer; + cl_no = dir_ptr->d_cluster; + } +#endif + for (i = 0; i < entries; i++, dir_ptr++) { + type = dir_ptr->d_name[0] & 0x0FF; + if (!mem) + mark = ROOTADDR + (long) i *(long) DIR_SIZE; + else + mark = clus_add(last) + (long) i *(long) DIR_SIZE; + if (function == ENTRY) { + if (type == NOT_USED || type == ERASED) return dir_ptr; + continue; + } + if (type == NOT_USED) break; + if (dir_ptr->d_attribute & 0x08) { + if (function == LABEL) return dir_ptr; + continue; + } + if (type == DIR || type == ERASED || function == LABEL) + continue; + type = is_dir(dir_ptr); + name = make_name(dir_ptr, + (function == FIND) ? FALSE : type); + if (function == FIND) { + if (strcmp(file_name, name) != 0) continue; + if (!type) { + if (dos_dir || *pathname) { + fflush (stdout); + fprintf (stderr, "%s: Not a directory: %s\n", cmnd, file_name); + exit(1); + } + } else if (*pathname == '\0' && dos_read) { + fflush (stdout); + fprintf (stderr, "%s: %s is a directory.\n", cmnd, path); + exit(1); + } + if (*pathname) { + dir_ptr = find_entry(dir_ptr, + sub_entries, pathname + 1); + } + if (mem) { + if (dir_ptr) { + memcpy((char *)&save_entry, (char *)dir_ptr, DIR_SIZE); + dir_ptr = &save_entry; + } + } + return dir_ptr; + } else { + if (function == FALSE) { + show(dir_ptr, name); + } else if (type) { /* Recursive */ + printf ( "Directory %s%s:\n", path, name); + add_path(name, FALSE); + list_dir(dir_ptr, sub_entries, FALSE); + add_path(NIL_PTR, FALSE); +#ifndef CACHE_ROOT + /* Re-read directory from disk */ + mem = dir_ptr = read_cluster(cl_no); +#endif + } + } + } + } while (cl_no != LAST_CLUSTER && mem); + + switch (function) { + case FIND: + if (dos_write && *pathname == '\0') return NIL_DIR; + fflush (stdout); + fprintf (stderr, "%s: Cannot find `%s'.\n", cmnd, file_name); + exit(1); + case LABEL: + return NIL_DIR; + case ENTRY: + if (!mem) { + fflush (stdout); + fprintf (stderr, "%s: No entries left in root directory.\n", cmnd); + exit(1); + } + cl_no = free_cluster(TRUE); + link_fat(last, cl_no); + link_fat(cl_no, LAST_CLUSTER); + memset(buffer, 0, cluster_size); + disk_io(WRITE, clus_add(cl_no), buffer, cluster_size); + + return new_entry(dir, entries); + case FALSE: + if (Rflag) { + printf ("\n"); + list_dir(dir, entries, TRUE); + } + } + return NULL; +} + +void delete(DIRECTORY *entry) +{ + register unsigned short cl_no = entry->d_cluster; + + entry->d_name[0] = 0xe5; + disk_io(WRITE, mark, entry, DIR_SIZE); + while (cl_no != LAST_CLUSTER) cl_no = clear_cluster(cl_no); + if (fat_dirty) flush_fat (); +} + +void extract(DIRECTORY *entry) +{ + register unsigned short cl_no = entry->d_cluster; + int rest, i; + long size = entry->d_size; + + if (size == 0) /* Empty file */ + return; + + do { + disk_io(READ, clus_add(cl_no), buffer, cluster_size); + rest = (size > (long) cluster_size) ? cluster_size : (short)size; + + if (Aflag) { + for (i = 0; i < rest; i ++) { + if (buffer [i] != '\r') fputc (buffer [i], fdo); + } + if (ferror (stdout)) { + fprintf (stderr, "%s: cannot write file: %s\n", + cmnd, strerror (errno)); + FABORT(1); + } + } else { + if (fwrite (buffer, 1, rest, fdo) != rest) { + fprintf (stderr, "%s: cannot write file: %s\n", + cmnd, strerror (errno)); + FABORT(1); + } + } + size -= (long) rest; + cl_no = next_cluster(cl_no); + if (cl_no == BAD16) { + fflush (stdout); + fprintf (stderr, "%s: reserved cluster value %x encountered.\n", + cmnd, cl_no); + FABORT(1); + } + } while (size && cl_no != LAST_CLUSTER); + + if (cl_no != LAST_CLUSTER) + fprintf (stderr, "%s: Too many clusters allocated for file.\n", cmnd); + else if (size != 0) + fprintf (stderr, "%s: Premature EOF: %ld bytes left.\n", cmnd, + entry->d_size); +} + + +/* Minimum of two long values + */ +long lmin (long a, long b) +{ + if (a < b) return a; + else return b; +} + + +void make_file(DIRECTORY *dir_ptr, int entries, char *name) +{ + register DIRECTORY *entry = new_entry(dir_ptr, entries); + register char *ptr; + unsigned short cl_no = 0; + int i, r; + long size = 0L; + unsigned short first_cluster, last_cluster; + long chunk; + char dir_bkp[DIR_SIZE]; + + memcpy((char *)dir_bkp, (char *)entry, DIR_SIZE); + entry = (DIRECTORY *)dir_bkp; + memset (&entry->d_name[0], ' ', 11); /* clear entry */ + for (i = 0, ptr = name; i < 8 && *ptr != '.' && *ptr; i++) + entry->d_name[i] = *ptr++; + while (*ptr != '.' && *ptr) ptr++; + if (*ptr == '.') ptr++; + for (i = 0; i < 3 && *ptr != '.' && *ptr; i++) entry->d_ext[i] = *ptr++; + + for (i = 0; i < 10; i++) entry->d_reserved[i] = '\0'; + entry->d_attribute = '\0'; + + entry->d_cluster = 0; + + while (free_range (&first_cluster, &last_cluster)) { + do { + unsigned short nr_clus; + + chunk = lmin ((long) (last_cluster - first_cluster + 1) * + cluster_size, + (long) MAX_CLUSTER_SIZE); + r = fill(buffer, chunk); + if (r == 0) goto done; + nr_clus = (r + cluster_size - 1) / cluster_size; + disk_io(WRITE, clus_add(first_cluster), buffer, r); + + for (i = 0; i < nr_clus; i ++) { + if (entry->d_cluster == 0) + cl_no = entry->d_cluster = first_cluster; + else { + link_fat(cl_no, first_cluster); + cl_no = first_cluster; + } + first_cluster ++; + } + + size += r; + } while (first_cluster <= last_cluster); + } + fprintf (stderr, "%s: disk full. File truncated\n", cmnd); +done: + if (entry->d_cluster != 0) link_fat(cl_no, LAST_CLUSTER); + entry->d_size = size; + fill_date(entry); + disk_io(WRITE, mark, entry, DIR_SIZE); + + if (fat_dirty) flush_fat (); +} + + +#define SEC_MIN 60L +#define SEC_HOUR (60L * SEC_MIN) +#define SEC_DAY (24L * SEC_HOUR) +#define SEC_YEAR (365L * SEC_DAY) +#define SEC_LYEAR (366L * SEC_DAY) + +unsigned short mon_len[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +void fill_date(DIRECTORY *entry) +{ + time_t atime; + unsigned long cur_time; + unsigned short year = 0, month = 1, day, hour, minutes, seconds; + int i; + long tmp; + + time(&atime); + cur_time = atime - DOS_TIME; + for (;;) { + tmp = (year % 4 == 0) ? SEC_LYEAR : SEC_YEAR; + if (cur_time < tmp) break; + cur_time -= tmp; + year++; + } + day = (unsigned short) (cur_time / SEC_DAY); + cur_time -= (long) day *SEC_DAY; + + hour = (unsigned short) (cur_time / SEC_HOUR); + cur_time -= (long) hour *SEC_HOUR; + + minutes = (unsigned short) (cur_time / SEC_MIN); + cur_time -= (long) minutes *SEC_MIN; + + seconds = (unsigned short) cur_time; + + mon_len[1] = (year % 4 == 0) ? 29 : 28; + i = 0; + while (day >= mon_len[i]) { + month++; + day -= mon_len[i++]; + } + day++; + + entry->d_date = (year << 9) | (month << 5) | day; + entry->d_time = (hour << 11) | (minutes << 5) | seconds; +} + +char *make_name(DIRECTORY *dir_ptr, short dir_fl) +{ + static char name_buf[14]; + register char *ptr = name_buf; + short i; + + for (i = 0; i < 8; i++) *ptr++ = dir_ptr->d_name[i]; + + while (*--ptr == ' '); + assert (ptr >= name_buf); + + ptr++; + if (dir_ptr->d_ext[0] != ' ') { + *ptr++ = '.'; + for (i = 0; i < 3; i++) *ptr++ = dir_ptr->d_ext[i]; + while (*--ptr == ' '); + ptr++; + } + if (dir_fl) *ptr++ = '/'; + *ptr = '\0'; + + return name_buf; +} + + +int fill(char *buff, size_t size) +{ + static BOOL nl_mark = FALSE; + char *last = &buff[size]; + char *begin = buff; + register int c; + + while (buff < last) { + if (nl_mark) { + *buff ++ = '\n'; + nl_mark = FALSE; + } else { + c = fgetc(fdo); + if (c == EOF) break; + if (Aflag && c == '\n') { + *buff ++ = '\r'; + nl_mark = TRUE; + } else { + *buff++ = c; + } + } + } + + return (buff - begin); +} + +#define HOUR 0xF800 /* Upper 5 bits */ +#define MIN 0x07E0 /* Middle 6 bits */ +#define YEAR 0xFE00 /* Upper 7 bits */ +#define MONTH 0x01E0 /* Mid 4 bits */ +#define DAY 0x01F /* Lowest 5 bits */ + +char *month[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +void xmodes(int mode) +{ + printf ( "\t%c%c%c%c%c", (mode & SUB_DIR) ? 'd' : '-', + (mode & 02) ? 'h' : '-', (mode & 04) ? 's' : '-', + (mode & 01) ? '-' : 'w', (mode & 0x20) ? 'a' : '-'); +} + +void show(DIRECTORY *dir_ptr, char *name) +{ + register unsigned short e_date = dir_ptr->d_date; + register unsigned short e_time = dir_ptr->d_time; + unsigned short next; + char bname[20]; + short i = 0; + + while (*name && *name != '/') bname[i++] = *name++; + bname[i] = '\0'; + if (!Lflag) { + fprintf (fdo, "%s\n", bname); + return; + } + xmodes( (int) dir_ptr->d_attribute); + fprintf (fdo, "\t%s%s", bname, strlen(bname) < 8 ? "\t\t" : "\t"); + i = 1; + if (is_dir(dir_ptr)) { + next = dir_ptr->d_cluster; + while ((next = next_cluster(next)) != LAST_CLUSTER) i++; + fprintf (fdo, "%8ld", (long) i * (long) cluster_size); + } else + fprintf (fdo, "%8ld", dir_ptr->d_size); + fprintf (fdo, " %02d:%02d %2d %s %d\n", ((e_time & HOUR) >> 11), + ((e_time & MIN) >> 5), (e_date & DAY), + month[((e_date & MONTH) >> 5) - 1], ((e_date & YEAR) >> 9) + 1980); +} + +void free_blocks(void) +{ + register unsigned short cl_no; + long nr_free = 0; + long nr_bad = 0; + + for (cl_no = 2; cl_no < total_clusters; cl_no++) { + switch (next_cluster(cl_no)) { + case FREE: nr_free++; break; + case BAD16: nr_bad++; break; + } + } + + fprintf (fdo, "Free space: %ld bytes.\n", nr_free * (long) cluster_size); + if (nr_bad != 0) + fprintf (fdo, "Bad sectors: %ld bytes.\n", nr_bad * (long) cluster_size); +} + + +DIRECTORY *read_cluster(unsigned int cluster) +{ + disk_io(READ, clus_add(cluster), buffer, cluster_size); + + return (DIRECTORY *)buffer; +} + +static unsigned short cl_index = 2; + +/* find a range of consecutive free clusters. Return TRUE if found + * and return the first and last cluster in the |*first| and |*last|. + * If no free clusters are left, return FALSE. + * + * Warning: Assumes that all of the range is used before the next call + * to free_range or free_cluster. + */ +BOOL free_range (unsigned short *first, unsigned short *last) +{ + while (cl_index < total_clusters && next_cluster(cl_index) != FREE) + cl_index++; + if (cl_index >= total_clusters) return FALSE; + *first = cl_index; + while (cl_index < total_clusters && next_cluster(cl_index) == FREE) + cl_index++; + *last = cl_index - 1; + return TRUE; +} + + +/* find a free cluster. + * Return the number of the free cluster or a number > |total_clusters| + * if none is found. + * If |leave_fl| is TRUE, the the program will be terminated if + * no free cluster can be found + * + * Warning: Assumes that the cluster is used before the next call + * to free_range or free_cluster. + */ +unsigned short free_cluster(BOOL leave_fl) +{ + while (cl_index < total_clusters && next_cluster(cl_index) != FREE) + cl_index++; + + if (leave_fl && cl_index >= total_clusters) { + fprintf (stderr, "%s: Diskette full. File not added.\n", cmnd); + exit(1); + } + return cl_index++; +} + +/* read a portion of the fat containing |cl_no| into the cache + */ +void read_fat (unsigned int cl_no) +{ + + if (!cooked_fat) { + /* Read the fat for the first time. We have to allocate all the + * buffers + */ + if (fat_16) { + /* FAT consists of little endian shorts. Easy to convert + */ + if ((cooked_fat = malloc (fat_size)) == NULL) { + /* Oops, FAT doesn't fit into memory, just read + * a chunk + */ + if ((cooked_fat = malloc (COOKED_SIZE)) == NULL) { + fprintf (stderr, "%s: not enough memory for FAT cache.\n", + cmnd); + exit (1); + } + cache_size = COOKED_SIZE / 2; + } else { + cache_size = fat_size / 2; + } + } else { + /* 12 bit FAT. Difficult encoding, but small. Keep + * both raw FAT and cooked version in memory if possible. + */ + cooked_fat = malloc (total_clusters * sizeof (short)); + raw_fat = malloc (fat_size); + if (cooked_fat == NULL || raw_fat == NULL) { + if (cooked_fat != NULL) free(cooked_fat); + if (raw_fat != NULL) free(raw_fat); + /* Oops, FAT doesn't fit into memory, just read + * a chunk + */ + cooked_fat = malloc (COOKED_SIZE); + raw_fat = malloc (COOKED_SIZE/sizeof (short)/2*3); + if (cooked_fat == NULL || raw_fat == NULL) { + fprintf (stderr, "%s: not enough memory for FAT cache.\n", + cmnd); + exit (1); + } + cache_size = COOKED_SIZE / sizeof(short); + rawfat_size = COOKED_SIZE / sizeof(short)/2*3; + } else { + cache_size = total_clusters; + rawfat_size = fat_size; + } + } + } + fat_low = cl_no; /* / cache_size * cache_size; ??? */ + + /* for FAT 12, round fat_low to a multiple of 2, so, when reading FAT from + * disk, we start at this cluster or previous one */ + if (!fat_16) if (fat_low & 1 != 0) fat_low--; + + fat_high = fat_low + cache_size - 1; + + if (!fat_16) { + unsigned short *cp; + unsigned char *rp; + unsigned short i; + + disk_io (READ, FAT_START + fat_low * 3 / 2, raw_fat, rawfat_size); + for (rp = raw_fat, cp = cooked_fat, i = 0; + i < cache_size; + rp += 3, i += 2) { + *cp = *rp + ((*(rp + 1) & 0x0f) << 8); + if (*cp == BAD) *cp = BAD16; + else if (*cp == LAST_CLUSTER12) *cp = LAST_CLUSTER; + cp ++; + *cp = ((*(rp + 1) & 0xf0) >> 4) + (*(rp + 2) << 4); + if (*cp == BAD) *cp = BAD16; + else if (*cp == LAST_CLUSTER12) *cp = LAST_CLUSTER; + cp ++; + } + } else { + assert (sizeof (short) == 2); + assert (CHAR_BIT == 8); /* just in case */ + + disk_io (READ, FAT_START + fat_low * 2, (void *)cooked_fat, cache_size * 2); + if (big_endian) { + unsigned short *cp; + unsigned char *rp; + unsigned short i; + + for (i = 0, rp = (unsigned char *)cooked_fat /* sic */, cp = cooked_fat; + i < cache_size; + rp += 2, cp ++, i ++) { + *cp = c2u2 (rp); + } + } + } +} + +/* flush the fat cache out to disk + */ +void flush_fat(void) +{ + if (fat_16) { + if (big_endian) { + unsigned short *cp; + unsigned char *rp; + unsigned short i; + + for (i = 0, rp = (unsigned char *)cooked_fat /* sic */, cp = cooked_fat; + i < cache_size; + rp += 2, cp ++, i ++) { + *rp = *cp; + *(rp + 1) = *cp >> 8; + } + } + disk_io (WRITE, FAT_START + fat_low * 2, (void *)cooked_fat, cache_size * 2); + disk_io (WRITE, FAT_START + fat_size + fat_low * 2, (void *)cooked_fat, cache_size * 2); + } else { + unsigned short *cp; + unsigned char *rp; + unsigned short i; + + for (rp = raw_fat, cp = cooked_fat, i = 0; + i < cache_size; + rp += 3, cp += 2, i += 2) { + *rp = *cp; + *(rp + 1) = ((*cp & 0xf00) >> 8) | + ((*(cp + 1) & 0x00f) << 4); + *(rp + 2) = ((*(cp + 1) & 0xff0) >> 4); + } + disk_io (WRITE, FAT_START + fat_low * 3 / 2, raw_fat, rawfat_size); + disk_io (WRITE, FAT_START + fat_size + fat_low * 3 / 2, raw_fat, rawfat_size); + } +} + +/* make cl_2 the successor of cl_1 + */ +void link_fat(unsigned int cl_1, unsigned int cl_2) +{ + if (cl_1 < fat_low || cl_1 > fat_high) { + if (fat_dirty) flush_fat (); + read_fat (cl_1); + } + cooked_fat [cl_1 - fat_low] = cl_2; + fat_dirty = TRUE; +} + + +unsigned short next_cluster(unsigned int cl_no) +{ + if (cl_no < fat_low || cl_no > fat_high) { + if (fat_dirty) flush_fat (); + read_fat (cl_no); + } + return cooked_fat [cl_no - fat_low]; +} + +/* free cluster cl_no in FAT and return its sucessor */ +unsigned short clear_cluster(unsigned int cl_no) +{ + unsigned short old; + + if (cl_no < fat_low || cl_no > fat_high) { + if (fat_dirty) flush_fat (); + read_fat (cl_no); + } + old = cooked_fat[cl_no - fat_low]; + cooked_fat[cl_no - fat_low] = 0; + fat_dirty = TRUE; + return old; +} + +char *slash(char *str) +{ + register char *result = str; + + while (*str) + if (*str++ == '/') result = str; + + return result; +} + +void add_path(char *file, BOOL slash_fl) +{ + register char *ptr = path; + + while (*ptr) ptr++; + + if (file == NIL_PTR) { + if (ptr != path) ptr--; + if (ptr != path) do { + ptr--; + } while (*ptr != '/' && ptr != path); + if (ptr != path && !slash_fl) *ptr++ = '/'; + *ptr = '\0'; + } else + strcpy (ptr, file); +} + + +void disk_io(BOOL op, unsigned long seek, void *address, unsigned bytes) +{ + unsigned int r; + + if (lseek(disk, seek, SEEK_SET) < 0L) { + fflush (stdout); + fprintf (stderr, "%s: Bad lseek: %s\n", cmnd, strerror (errno)); + exit(1); + } + if (op == READ) + r = read(disk, (char *) address, bytes); + else { + r = write(disk, (char *) address, bytes); + }; + + if (r != bytes) { + fprintf (stderr, "%s: %s error: %s\n", op == READ ? "read" : "write", cmnd, strerror (errno)); + exit (1); + } +} + +char dosread_c_rcs_id [] = + "$Id: dosread.c,v 2.0 2001/03/11 14:30:43 adrcunha Rel $"; + +/* $Log: dosread.c,v $ + * Revision 2.0 2001/03/11 14:30:43 adrcunha@dcc.unicamp.br + * Fixed some bugs, added FAT12 cache, decreased memory usage, changed + * drive number to letter on dir header, introduced some sanity check for + * memory, added compilation directive CACHE_ROOT (with this, root dir is + * loaded on memory at the beginning; without this, root dir is allways + * read from disk when needed - it's slow, but saves memory). + * + * Revision 1.9 1999/11/28 14:32:37 adrcunha@dcc.unicamp.br + * Dosread and doswrite can read/write to/from a file, not only stdin/stdout. + * Added one more personality to the schizophrenic dosread: dosdel + * Drives can be referenced as A:, B:, etc for /dev/fd0, /dev/fd1, etc + * Dosfiles can be referenced after drive if drive is 'X:' (e.g., A:FOO.BAR) + * + * Revision 1.8 1994/05/14 21:53:08 hjp + * filenames with more than 3 characters and extension work again. + * removed debugging stuff and b_copy. + * + * Revision 1.7 1994/04/09 03:09:01 hjp + * (posted to comp.os.minix) + * merged branch 1.5.387 with stem. + * changed treatment of drive parameter + * + * Revision 1.5.387.9 1994/04/09 02:07:51 hjp + * Disk full no longer produces lost clusters but a truncated file. + * Truncated file names to 8+3 before comparisons to avoid duplicate + * files and filenames containing dots in the extension. + * Replaced sbrk and brk by malloc and free (mixing brk and malloc causes + * heap corruption which sometimes lead to core dumps. It may also have + * been the cause of data corruption Kees reported). + * Made global variables static and removed some unused ones. + * Error messages now contain program name. + * + * Revision 1.5.387.8 1993/11/13 00:38:45 hjp + * Posted to comp.os.minix and included in Minix-386vm 1.6.25.1. + * Speed optimizations for 1.44 MB disks. + * Replaced lowlevel I/O by stdio. + * Simplified -a: Now only removes resp. adds CRs + * Cleaned up. + * + * Revision 1.5.387.1 1993/01/15 19:32:29 ast + * Released with 1.6.24b + */ -- 2.34.1