
/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:dosread.c 12.1$ */
/* $ACIS:dosread.c 12.1$ */
/* $Source: /ibm/acis/usr/src/ibm/RCS/dosread.c,v $ */

#ifndef lint
static char    *rcsid = "$Header:dosread.c 12.1$";
#endif

#include <stdio.h>
#include <ctype.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>

#define CR	'\r'
#define LF	'\n'
#define NL	'\n'
#define NUL	0

#define ctl(x) ('x'&037)
#define CTL_Z	ctl(z)		/* control - Z */

#define MAX_FILE_NAME	512	/* max name length */
#define MAX_FILE_LENGTH	8
#define MAX_FILE_EXT	3
#define MIN_NAME_LENGTH 13

#if defined(vax) || defined(ibmpc)
#define swaph(n) n
#define swapw(n) n
#else
#define swaph(n) ((((n)>>8)&0xff) | (((n)&0xff) << 8))
#define swapw(n) (swaph((n)>>16) | (swaph((n)) << 16))
#endif

#define LSEEK(fd, pos, how) lseek(fd, dos_offset + (pos), how)
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))

long            dos_offset;	/* offset to DOS partition */
int             partition;	/* partition to use */
int             pflg;		/* using partitioning */

struct dir
{
    u_char          file_name[MAX_FILE_LENGTH];
#	define FILE_NOT_USED	0	/* never been used */
#	define FILE_ERASED	0xe5
#	define FILE_DIR	0x2e	/* file is a directory */
    u_char          file_ext[MAX_FILE_EXT];	/* extention */
    u_char          file_attr;	/* file attributes */
#	define ATTR_READ_ONLY	0x01	/* file is read only */
#	define ATTR_HIDDEN	0x02	/* file is hidden */
#	define ATTR_SYSTEM	0x04	/* file is a system file */
#	define ATTR_VOL_LABEL	0x08	/* volume label */
#	define ATTR_SUBDIR	0x10	/* sub-directory */
#	define ATTR_ARCHIVE	0x20	/* file changed since archive */
    u_char          file_fill[10];	/* reserved */
    unsigned short  file_time;	/* time last changed */
    unsigned short  file_date;	/* date last changed */
    unsigned short  file_start;	/* starting clustor */
    long            file_size;	/* file size in bytes */
};

#define BSIZE	512		/* DOS block size */
#define FAT_SECTOR	1	/* location of FAT */
#define FAT_POS	(FAT_SECTOR * BSIZE)	/* offset to the DOS FAT */
#define MAX_FAT_SECTORS 7	/* increased from 2 for DOS 3.0 */
#define FIRST_FAT	2	/* first available FAT number */
u_char          dir_buff[BSIZE];
u_char         *fat;		/* dynamically allocated FAT table */
u_char         *fat_end;	/* pointer to the end of the FAT */
int             fat_free;	/* pointer to next (possibly) free cluster */
#define FREE_CLUSTER	0x000	/* a free cluster */
#define EOF_CLUSTER 0xff8	/* EOF flag */
#define EOF_16BIT	0xfff8	/* EOF flag for 16 bits */
int		eof_cluster = EOF_CLUSTER;	/* default for 12bit FAT */
int		fat_16 = 0;	/* use 12 bit fat by default */
int             fd;
int             sectors;	/* number of sectors */
int             max_sectors;	/* number of actual sectors */
int             heads;		/* number of heads */
int             cluster_size;	/* number of sectors/cluster */
int             tracks;		/* number of tracks */
int             dir_sectors;	/* number of directory sectors */
int             dir_sector;	/* directory offset */
int             fat_size;	/* number of sectors for fat */
int             fat_length;	/* number of bytes in fat */
int             fat_count;	/* number of entries in fat */
int             fat_copies;	/* number of copies of fat */
int             data_sector;	/* start of data area (sector) */
int             cur_dir;	/* current directory sector */
char           *matcharg;	/* flags for arg checking */
char          **xargv;
int             xargc;
int             argstart;
int             tflg;
int             vflg;
int             xflg;
int             dflg;		/* delete file */
int             wflg;		/* write file */
int             aflg;		/* ascii mode file extract/write */
int             cflg;
int             iflg;
int             bflg;		/* write boot block */
int             Iflg;		/* ignore boot info */
int             hflg;		/* write/read hidden files */
int             attr;		/* default file attributes */
int		debug;		/* print debugging information */
int		Pflg;		/* set modification/access time */
/*
 * Warning: the -R flag is a new experimental feature - it may not 
 * continue to exist in later versions of dosread.
 */
int             Rflg;		/* recursive operation on directories */
int		oflg;		/* use old dosdir output format */
int             isize;		/* size (in blocks) of diskette (or disk) */
int		first_sector;	/* sector number to start allocation at */

char           *dostime();
char           *dosdate();
char           *file = "/dev/rfd0";
char           *basename();
char           *calloc();
struct dir     *getdir();
char           *
malloc(), *strsave();
char           *strcpy();

#define PCBOOT(x) ((x) == 0xE9 || (x) == 0xEB)

#define WORD(x) (((x[1]) << 8) + (x[0]))
#define BYTE(x)	(x)

/*
 * structure of a DOS boot record as per DOS Technical Reference 3.00
 * page 3-22.
 */
struct dos_boot_info
{
    u_char          jump[3];	/* 0-2: near or far jump */
    u_char          oem[8];	/* 3-10: OEM name */
    u_char          block_size[2];	/* 11-12: bytes/sector */
    u_char          cluster_size;	/* 13: number of sectors/cluster */
    u_char          reserved[2];/* 14-15: reserved sectors */
    u_char          fat_copies;	/* 16: number of fat's */
    u_char          dir_entries[2];	/* 17-18: number of root directory
					 * entires */
    u_char          device_size[2];	/* 19-20: number of sectors total */
    u_char          media_descriptor;	/* 21: who knows */
    u_char          fat_size[2];/* 22-23: number of sectors per fat */
    u_char          sectors[2];	/* 24-25: sector size */
    u_char          tracks[2];	/* 26-27: number of heads */
    u_char          hidden[2];	/* 28-29: number of hidden sectors */
};

struct disk_info
{
    int             fat_type;	/* identifier byte */
    int             fat_size;	/* number of sectors of FAT */
    int             sectors;	/* number of sectors */
    int             heads;	/* number of heads */
    int             dir_sectors;/* number of directory sectors */
    int             cluster_size;	/* number of sectors/cluster */
    int             tracks;	/* number of tracks */
    int             size;	/* total size (in blocks) */
    int             fat_copies;	/* number of copies of FAT */
}               disk_types[] = {

#define F	0x100 +		/* flag to eliminate in sequence search */
#define K *2
/*
 * NOTE: this table is ordered by "size" field  - be sure to insert new
 *	 entries at the correct place.
fat_type fat_size sectors heads	dir_sectors cluster tracks size fat_copies */

{ F 0xf9,    3,	    9,	    2,	   7,		2,	80, 720 K,   2 },	/* ps/2 720kb */
{ 0xfe,	    1,	     8,	    1,	    4,		1,	40, 160 K,   2 },	/* single sided, 8 sector */
{ 0xfc,	    2,	     9,	    1,	    4,		1,	40, 180 K,   2 },	/* single sided, 9 sector */
{ 0xff,	    1,	     8,	    2,	    7,		2,	40, 320 K,   2 },	/* dual sided, 8 sector */
{ 0xfd,	    2,	     9,	    2,	    7,		2,	40, 360 K,   2 },	/* dual sided, 9 sector */
{ F 0xf9,   3,	     9,	    2,	   7,		2,	80, 720 K,   2 },	/* ps/2 720kb */
{ 0xf9,	    7,	    15,	    2,	   14,		1,	80, 1200 K,   2 },	/* dual sided, 15 sector */
{ 0xf0,	    9,	    18,	    2,	   14,		1,	80, 1440 K,   2 },	/* ps/2 1.44mb */
{ 0xf8,	    7,	    17,	    7,	   5,		1,	80, 12000 K,   2 }	/* hard disk  (must be last) */
};

#ifdef __STDC__
void 
err(char *,...);
#endif

int            *info = (int *) disk_types;	/* for -T to override type
						 * info */

#define TYPES (sizeof disk_types / sizeof disk_types[0])

/* usage messages */
static char *dosdir_usage =
    "dosdir [-xdtw] [-v] [-R] [-P] [-a] [-f floppy] [-p{part}] [pattern] ...";
static char *dosread_usage =
    "dosread [-xdtw] [-v] [-R] [-P] [-a] [-f floppy] [-p{part}] pattern ...";
static char *dosdel_usage =
    "dosdel [-xdtw] [-v] [-R] [-a] [-f floppy] [-p{part}] pattern ...";
static char *doswrite_usage =
    "doswrite [-xdtw] [-v] [-R] [-P] [-a] [-f floppy] [-p{part}] file ...";

char *progname;

main(argc, argv)
    char ** argv;
{
    register char  *argp;
    register int    i;
    register int    j;
    register int    how;

    xargc = argc;
    progname = argv[0];
    matcharg = calloc(argc, 1);	/* one byte per arg in arg list */
    for (i = 1; i < argc; ++i)
    {
	argp = argv[i];
	if (*argp == '-')
	    while (*++argp)
	    {
		switch (argp[0])
		{
		case 'A':
		    attr = getattr(argp + 1);
		    argp += strlen(argp + 1);	/* rest of argument is attr */
		    break;
		case 'a':	/* ascii mode */
		    ++aflg;
		    break;
		case 'D':
		    ++debug;
		    break;
		case 'd':	/* delete file */
		    ++dflg;
		    break;
		case 'w':	/* write file */
		case 'r':	/* replace file */
		    ++wflg;
		    break;
		case 's':	/* sector number */
		    argp += cvt_number(argp+1, &first_sector);
		    break;
		case 'v':
		    ++vflg;
		    break;
		case 't':
		    ++tflg;
		    break;
		case 'x':
		    ++xflg;
		    break;
		case 'f':
		    file = argv[++i];
		    break;
		case 'b':
		    ++bflg;	/* add boot block */
		    break;
		case 'I':
		    ++Iflg;	/* ignore info in boot sector */
		    break;
		case 'i':
		    argp += cvt_number(argp+1, &isize);
		    ++iflg;
		    break;
		case 'P':	/* set modification/access time */
		    ++Pflg;
		    break;
		case 'p':
		    ++pflg;
		    partition = atoi(argp + 1);
		    while (isdigit(argp[1]))
			++argp;
		    break;
		case 'R':
		    ++Rflg;
		    break;
		case 'T':
		    do
			*info++ = aton(argp + 1);
		    while (argp = index(argp + 1, ','));
		    argp = "\0";/* force exit from loop */
		    break;
		case 'h':
		    ++hflg;
		    attr |= ATTR_HIDDEN;
		    break;
		case 'o':
		    ++oflg;
		    break;
		case 'H':
		case '?':
		    usage();
		    exit(1);	/* since we ignore any other options */
		    break;

		default:
		    err("unknown switch %s", argp);
		}
	    }
	else
	    break;
    }
/*
 * if no explicit function specified then use the last component of 
 * the program name.
 */

    if (tflg == 0 && xflg == 0 && dflg == 0 && wflg == 0)
    {
	argp = basename(argv[0]);
	++cflg;
	if (strcmp(argp, "dosread") == 0)
	    ++xflg;
	else if (strcmp(argp, "doswrite") == 0)
	    ++wflg;
	else if (strncmp(argp, "dosdel", 6) == 0)
	    ++dflg;
	else ++tflg;		/* default to -t */
    }
    if (Rflg && (wflg || dflg))
	err("sorry, -R not implemented for delete or write functions");

    if (i >= argc)
    {

	/* no file pattern for read, write or del, print a usage message. */

	if (xflg)
	    err("No pattern found.\nUsage: %s", dosread_usage);
	else if (dflg)
	    err("No pattern found.\nUsage: %s", dosdel_usage);
	else if (wflg && !(iflg || bflg))
	    err("No file found.\nUsage: %s", doswrite_usage);
    }
    if ((fd = open(file, how = (dflg || wflg || iflg || bflg) ? 2 : 0)) < 0)
	err("can't open %s for %s",
	    file, how == 2 ? "update" : "input");
    argstart = i;		/* first unprocessed argument */
    fdinit();			/* read FAT etc. */
    xargv = (char **) malloc((argc + 1) * sizeof(char *));
    if (xargv == (char **) 0)
	err("nospace");
    for (j = argstart; j < argc; ++j)
    {
	xargv[j] = strsave(argv[j]);
	if (xargv[j] == (char *) 0)
	    err("nospace");
    }
    xargv[argc] = (char *) 0;
    for (j = argstart; xargv[j] != NULL; j++)
	catname(xargv[j]);
    dosdir(fd, 0, "");
    if (tflg && vflg)
    {
	register int    nfree = get_free() * cluster_size;
	printf("\nfree sectors: %d, bytes: %d\n", nfree, nfree * BSIZE);
    }
    for (i = argstart; i < argc; ++i)
	if (matcharg[i] == 0)
	{
	    if (wflg)
		writefile((struct dir *) 0, argv[i]);
	    else
		err("%s: %s not found\n", argv[0], argv[i]);
	} else if (wflg)
	    writefile((struct dir *) 0, argv[i]);
    if (fd != 0)
	close(fd);
    exit(0);
}

usage()
{
    printf("Commands to access PC DOS format disks and diskettes:\n");
    printf("%s\n", dosdir_usage);
    printf("%s\n", dosread_usage);
    printf("%s\n", doswrite_usage);
    printf("%s\n", dosdel_usage);
    printf("-t		forces dosdir action\n");
    printf("-x		forces dosread action\n");
    printf("-w		forces doswrite action\n");
    printf("-d		forces dosdel action\n");
    printf("-f floppy	specifies which file or device to use instead of /dev/rfd0\n");
    printf("-p{part}	specifies (for hard disk) which partition\n");
    printf("		(defaults to active partition if part not specified)\n");
    printf("-P		preserves dates and times on files (dosread/doswrite)\n");
    printf("-v		produces more verbose output (more -v's produce more output\n");
    printf("-a		converts ASCII files to and from DOS format\n");
    printf("-b		write appropriate boot block\n");
    printf("-i{size}	specifies device size (doswrite)\n");
    printf("-R		recurses down subdirectores (dosdir/dosread)\n");
    printf("-h		accesses hidden files (dosread/dosdir) or writes them (doswrite)\n");
    printf("-s{sector}	starts writing at specified sector (doswrite)\n");
    printf("-D		prints debugging messages\n");
    printf("-I		ignore boot block information\n");
    printf("-o		use old dosdir output format\n");
    printf("-T{info}	specify new diskette type information\n");
    printf("-A{attr}	specify file attributes\n");
    printf("-H or -?	print usage and option information\n");
    printf("\n");
    printf("pattern means that arguments are matched against diskette files\n");
    printf("	and should be quoted if they contain *, ?, or [...]\n");
    printf("file means that arguments are UNIX file names and should NOT be quoted.\n");
    printf("Switches can generaly be combined (e.g. -vaf /dev/fd1) except for -A.\n");
    printf("Dosread and dosdel require that pattern be specified but will\n");
    printf("accept \"*\" to mean all files in the root directory.\n");
}


/*
 * convert string pointed to by argp and return result in
 * *result. return the number of bytes consumed.
 * the size can be followed by 'b' for blocks or 'k' for k
 * or 'm' for megabytes.
 */
int
cvt_number(argp, result)
    char *argp;
    int *result;
{
    char *oldargp = argp;
    int isize = atoi(argp);

    while (isdigit(argp[0]))
	++argp;
    if (argp[0] == 'b')
	++argp;
    else if (argp[0] == 'm')
    {
	++argp;
	isize <<= (10+1);	/* result is in meg */
    }
    else
    {
	if (argp[0] == 'k')
	    ++argp;
	isize <<= 1;	/* result is in k */
    }
    *result = isize;
    return(argp-oldargp);
}

fdinit()
{
    register u_char *tfat = dir_buff;	/* temporary buffer */
    register struct disk_info *diskinfo;
    register int    i;
    int             got_info = 0;

    if (pflg)
	get_partition(pflg);
    if (iflg)
	tfat[0] = getdensity();
    else
    {
	if (!Iflg)
	    got_info = read_boot_info();
	LSEEK(fd, FAT_POS, 0);	/* point to FAT on disk */
	if (read(fd, tfat, BSIZE) != BSIZE)
	    err("could not read FAT");
    }
    i = gettype(tfat[0]);
    if (i >= TYPES)
    {
	if (tfat[0] == 0x29 && !pflg && get_partition(0))
	{
	    LSEEK(fd, FAT_POS, 0);	/* point to FAT on disk */
	    if (read(fd, tfat, BSIZE) != BSIZE)
		err("could not read FAT");
	    i = gettype(tfat[0]);
	}
	if (i >= TYPES)
	    err("unknown diskette type %x\n", tfat[0]);
    }
    diskinfo = &disk_types[i];

/* for fixed disk we now get the particular information off the disk */
    if (i == TYPES - 1)
	read_fixed_info(diskinfo);

    if (!got_info)
    {
	/*
	 * copy information from structure 
	 */
	fat_size = diskinfo->fat_size;
	fat_copies = diskinfo->fat_copies;
	sectors = diskinfo->sectors;
	heads = diskinfo->heads;
	dir_sectors = diskinfo->dir_sectors;
	cluster_size = diskinfo->cluster_size;
	tracks = diskinfo->tracks;
	max_sectors = diskinfo->size;
    }
/*
 * calculate derived information
 */
    dir_sector = FAT_SECTOR + (fat_size * fat_copies);
    data_sector = dir_sector + dir_sectors;
    fat_count = (max_sectors - data_sector) / cluster_size + FIRST_FAT;	/* number of FAT entries */
    fat_length = fat_size * BSIZE;	/* number of bytes in fat */
    fat_free = FIRST_FAT;	/* first location in fat to search */
    if ((fat = (u_char *) calloc(fat_size, BSIZE)) == NULL)
	err("could not allocate FAT");
/*
 * copy the temp fat into permanent position 
 */
    bcopy(tfat, fat, BSIZE);
    if (iflg)
	init_disk();
    else
    {
	/*
	 * read the rest into place 
	 */
	if (fat_size > 1)
	{
	    if (vflg > 1)
		printf("reading next %d bytes of fat\n", fat_length - BSIZE);
	    LSEEK(fd, FAT_POS + BSIZE, 0);	/* point to next FAT sector
						 * on disk */
	    if (read(fd, fat + BSIZE, fat_length - BSIZE) != fat_length - BSIZE)
		err("could not read all of fat");
	}
    }
    if (iflg || bflg)
	init_boot();
    if (vflg > 1)
    {
	printf("%d sectors, %d heads, %d tracks, %d directory sectors, %d sectors/cluster\n",
	       sectors, heads, tracks, dir_sectors, cluster_size);
	printf("directory at block %d, data at block %d, %d blocks of FAT, %d FAT copies\n",
	       dir_sector, data_sector, fat_size, fat_copies);
	printf("fat_length=%d, fat_count=%d, max_sectors=%d\n", fat_length, fat_count, max_sectors);
    }
}

/*
 * look up the fat indicator type int the
 * master type table.
 */
gettype(byte)
u_char byte;
{
    int i;

    for (i = 0; i < TYPES; ++i)
	if (disk_types[i].fat_type == byte)
	    break;
    return(i);
}

/*
 * scan a DOS directory and do something to each file found.
 */
dosdir(fd, cluster, dirname)
    register int    fd;
    int             cluster;	/* the directory cluster */
    char	   *dirname;	/* the enclosing directory name */
{
    register struct dir *dirp;
    register int    i;
    char	    name[MAX_FILE_NAME];	/* space for the name */
    char           *filename;	/* filename.ext */
    struct dir      curdir;	/* a local copy of directory entry */
    int		    gotdir = 0;	/* if we've already checked for directory */
    int		    namelen;

/*
 * prepare place to build full pathname of each file in the directory 
 */
    if (debug)
	printf("dosdir(%s)\n",dirname);
    strcpy(name, dirname);
    filename = name + strlen(name);
    if (filename != name)
    {
	*filename++ = '/';
	*filename = 0;
	if (strlen(name) + MIN_NAME_LENGTH > MAX_FILE_NAME)
	    err("%s - too many levels of directories",dirname);
    }
    namelen = strlen(name) + MIN_NAME_LENGTH;

    for (i = 0;; ++i)
    {
	if (cluster)
	{
	    if ((i % cluster_size) == 0)
	    {
		if (cluster >= eof_cluster)
		    break;	/* EOF */
		cur_dir = cvt_cluster(cluster);
		cluster = next_cluster(cluster);
	    } else
		++cur_dir;
	} else
	{
	    if (i >= dir_sectors)
		break;		/* end of directory */
	    cur_dir = dir_sector + i;	/* current directory sector */
	}
	LSEEK(fd, cur_dir * BSIZE, 0);	/* seek to directory */
	if (read(fd, dir_buff, BSIZE) != BSIZE)
	    err("directory read error");
	for (dirp = (struct dir *) dir_buff; dirp < (struct dir *) (dir_buff + BSIZE); ++dirp)
	{
	    int selected = 0;
	    switch (dirp->file_name[0])
	    {
	    case FILE_NOT_USED:
		goto done;	/* end if dir scan */
	    case FILE_ERASED:
	    case FILE_DIR:
		continue;
	    default:
		sprintf(filename, "%.8s.%.3s", dirp->file_name, dirp->file_ext);
		fixname(filename);
		if ((dirp->file_attr & ATTR_VOL_LABEL) && !hflg)
		    continue;	/* ignore volumn label */
		if ((dirp->file_attr & ATTR_HIDDEN) && !hflg)
		    continue;
		if (!select(name)) {
		    if (((dirp->file_attr & ATTR_SUBDIR) && (Rflg || seldir(name))))
			++selected;
		    else
			continue;	/* not wanted and not recursive directory */
		}
		copydir(&curdir, dirp);	/* get a copy of dirp */
		if (tflg)
			printtoc(&curdir, name, namelen);
		if ((dirp->file_attr & ATTR_SUBDIR) && (Rflg || selected))
		{
		    char save_buff[BSIZE];
		    bcopy(dir_buff, save_buff, BSIZE);
		    copydir(&curdir, dirp);	/* get a copy of dirp */
		    dosdir(fd, curdir.file_start, name);
		    bcopy(save_buff, dir_buff, BSIZE);
		    continue;
		}
		if ((dirp->file_attr & ATTR_VOL_LABEL))
		    continue;
		if (xflg)
		{
		    struct stat statbuff;
		    if (!gotdir && dirname[0] &&
			    stat(dirname, &statbuff) < 0 && mkdir(dirname,0777) < 0
			    && mkpath(dirname) < 0)
			err("can't mkdir %s",dirname);
		    else
			++gotdir;
		    extract(&curdir, name);
		}
		else if (dflg)
		    delete(&curdir, dirp, filename);
		else if (wflg)
		{
		    if (vflg)
		    {
			--vflg;
			delete(&curdir, dirp, filename);
			++vflg;
		    } else
			delete(&curdir, dirp, filename);
		}
	    }
	}
    }
done:
    ;

}


printtoc(curdir, name, namelen)
struct dir      *curdir;
char *name;
{
    if (vflg)
    {
	if (oflg)
	{
	    printf("%-8.8s.%-3.3s %4d %s %s %ld",
	       curdir->file_name, curdir->file_ext,
	       curdir->file_start,
	       dostime((curdir->file_time)),
	       dosdate((curdir->file_date)),
	       (curdir->file_size));
	    if (vflg>1)
		printf(" %5d", cvt_cluster(curdir->file_start));
	}
	else
	{
	    printf("%-*s ", namelen, name);
	    if (curdir->file_attr & ATTR_SUBDIR)
		printf("  <DIR> ");
	    else
		printf("%7d ", curdir->file_size);
	    printf("%s %s ",
	       dostime(curdir->file_time),
	       dosdate(curdir->file_date));
	    if (vflg>1)
		printf(" %5d", cvt_cluster(curdir->file_start));
	    prattr(curdir->file_attr);
	}

    } else
	printf("%s", name);
    printf("\n");
}

char           *
    dostime(time) register int time;
{
    static char     ctime[10];

#define DOSHR(time) ((time) >> 11)
#define DOSMIN(time) ((time >> 5) & 0x3f)
#define DOSSEC(time) ((time << 1) & 0x3f)
    sprintf(ctime, "%02d:%02d:%02d", DOSHR(time), DOSMIN(time), DOSSEC(time));
    return (ctime);
}

/*
 * dos format for date is:  year-1980 (6 bits) : month (4 bits) : day (5 bits)
 */
char           *
    dosdate(date) register int date;
{
    static char     cdate[10];

#define DOSDAY(date) ((date) & 0x01f)
#define DOSMON(date) (((date) >> 5) & 0x0f)
#define DOSYEAR(date) (80 + ((date) >> 9))
    sprintf(cdate, "%02d-%02d-%02d", DOSMON(date), DOSDAY(date), DOSYEAR(date));
    return (cdate);
}

#ifdef __STDC__
void 
err(char *fmt,...)
#else
err(fmt, d1, d2, d3)
    char           *fmt;
#endif __STDC__
{
    fprintf(stderr, "%s: ",progname);
    fprintf(stderr, fmt, ((int *) &fmt)[1], ((int *) &fmt)[2], ((int *) &fmt)[3]);
    fprintf(stderr, "\n");
    fprintf(stderr, "NOTE: '%s -H' describes options and other information\n",progname);
    exit(1);
}

/* 
 * scan thru the argument list and see if any of the names match
 * the given string (str).
 */
select(str) register char *
    str;
{
    register int    i;

    if (argstart >= xargc)
	return (1);
    for (i = argstart; i < xargc; ++i)
	if (match(str, xargv[i]))
	    return (++matcharg[i]);	/* remember we matched */
    return (0);
}

/*
 * match a directory component explicity 
 * e.g. if user specified foo/x and we are passed
 * "foo" we will return a match. we don't count this
 * as a match on foo/x as we haven't yet gotten the 'x'.
 */
seldir(str)
char *str;
{
    register int len = strlen(str);
    int i;

    if (debug)
	printf("seldir(%s) ");
    if (argstart >= xargc) {
	if (debug)
	    printf("yes\n");
	return (1);
    }
    for (i = argstart; i < xargc; ++i)
	if (strncmp(str, xargv[i], len) == 0 && xargv[i][len] == '/') {
	    if (debug)
		printf("yes\n");
	    return (1);		/* we matched a directory component */
	}
    if (debug)
	printf("no\n");
    return (0);
}

extract(dirp, name) register struct dir *
    dirp;
    register char  *name;
{
    register FILE  *ofd;
    register int    length = dirp->file_size;
    register int    cluster = dirp->file_start;
    register int    sector;
    register int    i;
    char            buff[BSIZE];
    char            lastc = 0;

    if (vflg)
	printf("x %s\n", name);
    if ((ofd = fopen(name, "w")) == 0)
	err("can't open %s for output", name);
    if (vflg > 1)
	printf("length %d\n", length);
    for (; length > 0; cluster = next_cluster(cluster))
    {
	if (cluster >= eof_cluster)
	    break;		/* EOF */
	sector = cvt_cluster(cluster);
	LSEEK(fd, sector * BSIZE, 0);
	for (i = 0; i < cluster_size; ++i)
	{
	    if (read(fd, buff, BSIZE) != BSIZE)
		err("floppy read error");
	    if (awrite(ofd, buff, length > BSIZE ? BSIZE : length, &lastc) < 0)
		err("write error on %s", name);
	    if ((length -= BSIZE) <= 0)
		break;
	}
    }
    awrite(ofd, buff, 0);	/* flush the buffer */
    fclose(ofd);
    if (Pflg)
	{
	time_t timep[2];

	if (timep[0] = timep[1] = gtime(dirp->file_date,dirp->file_time))
		(void) utime(name, timep);
	}
}

int 
    cvt_cluster(n) register int n;
{
    register int    result = (n - FIRST_FAT) * cluster_size + data_sector;
    if (vflg > 2)
	printf("cluster %d ==> sector %d\n", n, result);
    return (result);
}

int 
    cvt_sector(n) register int n;
{
    register int    result = (n - data_sector) / cluster_size + FIRST_FAT;
    if (vflg > 2)
	printf("sector %d ==> cluster %d\n", n, result);
    return (result);
}

int 
    next_cluster(n) register int n;
{
    register int    x;
    register u_char *f;			/* point to proper fat entry */
    register int    result;

    if (fat_16)
	{
	if (n >= fat_length)
	    err("cluster %d not in FAT", n);
	x = n + n;
	f = fat+x;
	result = f[0] | (f[1] << 8);
	}
    else 
	{
    	x = n + n + n;	/* n * 3 */
    	f = fat + (x >> 1);	/* point to proper fat entry */
	if ((x >> 1) >= fat_length)
	    err("cluster %d not in FAT", n);
	if ((x & 1) == 0)
	    result = f[0] | (f[1] << 8);	/* 8 plus low 4 from next */
	else
	    result = (f[0] >> 4) | (f[1] << 4);	/* high 4 plus 8 from next */
	result &= 0xfff;
	}
    if (vflg > 2)
	printf("next(%d)=%d (0x%x) fat+%d=%02x, %02x\n",
	       n, result, result, x >> 1, f[0], f[1]);
    return (result);
}

/*
 * convert dos name to unix format:
 * 1. lower case letters
 * 2. no blanks
 * 3. remove trailing .
 */
fixname(filename) register char *
    filename;
{
    register char  *p = filename;

    for (; *p = *filename++;)
    {
	if (isupper(*p))
	    *p = tolower(*p);
	if (*p != ' ')
	    ++p;
    }
    if (p[-1] == '.')
	*--p = 0;
}

char           *
    basename(name) register char *name;
{
    register char  *p = name;
    while (*name)
	if (*name++ == '/')
	    p = name;
    return (p);
}

delete(dirp, realdirp, filename)
    register struct dir *dirp, *realdirp;
    register char  *filename;
{

    if (vflg)
	printf("d %s\n", filename);
    delete_file(dirp, filename);/* delete the file contents */
    dirp->file_name[0] = FILE_ERASED;	/* delete it */
    copydir(realdirp, dirp);	/* put back the entry */
    put_dir();
    put_fat();
}


/*
 * delete the contents of the file whose directory entry is "dirp".
 */
delete_file(dirp, filename) register char *
    filename;
    register struct dir *dirp;
{
    register int    length = dirp->file_size;
    register int    cluster = dirp->file_start;
    register int    next;

    if (dirp->file_attr & ATTR_READ_ONLY)
	err("%s is read-only\n", filename);
    if (vflg > 1)
	printf("length %d\n", length);
    for (; cluster < eof_cluster; cluster = next)
    {
	next = next_cluster(cluster);
	free_cluster(cluster);
    }
    dirp->file_start = 0;	/* remove start pointer */
}

/*
 * write a file named "filename" and put the directory entry 
 * into "dirp" (if dirp == 0 then allocate a directory entry).
 */
writefile(dirp, filename)
    register struct dir *dirp;
    register char  *filename;
{
    register int    io = open(filename, 0);
    struct dir      curdir;
    long            filetime;

    if (io < 0)
	err("can't open %s for input\n", filename);
    if (vflg)
	printf("w %s\n", filename);
    if (dirp == 0)
    {
	if ((dirp = getdir(fd)) == 0)
	    err("no directory slots available");
    } else
	delete_file(dirp, filename);	/* get rid of the blocks */
    bzero(&curdir, sizeof(struct dir));
    curdir.file_attr = attr;
    cvtname(&curdir, filename);
    if (Pflg)
	{
	struct stat statbuff;

	if (fstat(io, &statbuff) < 0)
		time(&filetime);
	else
		filetime = statbuff.st_mtime;
	}
    else
	time(&filetime);
    make_dos_time(&curdir, filetime);
    writedata(io, &curdir, filename);
    copydir(dirp, &curdir);
    put_dir();
    put_fat();
}

/*
 * write out the current directory entry back onto the floppy.
 * the byte swapping has already been done when the directory entry
 * was put into place (via copydir).
 */
put_dir()
{
    if (vflg > 1)
	printf("write directory block %d at %ld\n", cur_dir - dir_sector, cur_dir * BSIZE);
    LSEEK(fd, cur_dir * BSIZE, 0);
    if (write(fd, dir_buff, BSIZE) != BSIZE)
	err("putdir");
}

/*
 * write out (two copies) the fat and reset the free_fat pointer 
 * so that it will start again at the start of the fat for the next 
 * time. This really only needs to be done for 'delete' but it is 
 * slightly safer if done here rather than in the delete routine.
 */
put_fat()
{
    register int    i;
    if (vflg > 1)
	printf("write fat at block %d, %d copies\n", FAT_SECTOR, fat_copies);
    LSEEK(fd, FAT_POS, 0);	/* point to FAT on disk */
    for (i = 0; i < fat_copies; ++i)
	if (write(fd, fat, fat_length) != fat_length)
	    err("could not write FAT");	/* write FAT copy */
    fat_free = FIRST_FAT;	/* reset the free fat pointer */
}

free_cluster(cluster, dirp) register int 
    cluster;
    register struct dir *dirp;
{
    if (vflg > 1)
	printf("free_cluster(%d)\n", cluster);
    set_cluster(FREE_CLUSTER, cluster, dirp);
}

/*
 * get the next free cluster - do it by looking for a cluster whose 'next'
 * value is 0.
 */
get_cluster()
{
    for (; fat_free < fat_count; ++fat_free)
	if (next_cluster(fat_free) == FREE_CLUSTER)
	    return (fat_free++);/* increment just in case */
    return (0);			/* oops, no more left */
}

writedata(io, dirp, filename)
    register int    io;
    register struct dir *dirp;
    char           *filename;
{
    register int    l;
    register int    prev_cluster = 0;
    register int    c, s;
    register int    bsize = cluster_size * BSIZE;
    char           *buff = malloc(bsize);
    register int    length = 0;
    FILE           *file = fdopen(io, "r");

    if (buff == 0)
	err("can't allocate %d bytes for buffer", bsize);
    if (file == 0)
	err("can't fdopen file %s", filename);
    if (first_sector)
	if ((fat_free = cvt_sector(first_sector)) < FIRST_FAT)
		err("sector %d is too small (minimum is %d)",first_sector,data_sector);
    for (; (l = aread(file, buff, bsize)) > 0; length += l)
    {
	if ((c = get_cluster()) == 0)
	    err("out of space");
	s = cvt_cluster(c);
	if (first_sector)
	{
	    if(s != first_sector)
		err("cannot allocate requested block (%d)",first_sector);
	    first_sector += cluster_size;	/* bump to next cluster */
	}
	LSEEK(fd, s * BSIZE, 0);
	if (l != bsize)
	    bzero(buff + l, bsize - l);	/* clear rest to zero */
	if (write(fd, buff, bsize) != bsize)
	    err("write error");
	set_cluster(c, prev_cluster, dirp);
	prev_cluster = c;
    }
    if (l < 0)
	err("read error");
    dirp->file_size = length;
    set_cluster(eof_cluster, prev_cluster, dirp);
    free(buff);
    fclose(file);

}

/*
 * put the current cluster number (n) into 
 * the previous cluster (create a forward link)
 * if "prev" is null then put it into the directory (first time).
 */
set_cluster(n, prev, dirp) register int 
    n;
    register struct dir *dirp;
{
    register int    x;
    register u_char *f;	/* point to proper fat entry */

    if (prev == 0)
    {
	if (dirp)
	    dirp->file_start = n;
	if (vflg > 1)
	    printf("set(first)=%d\n", n);
	return;
    } 
    if (fat_16)
    {
	x = prev + prev;
	f = fat + x;
	f[0] = n;
	f[1] = n >> 8;
    }
    else
    {
    	x = prev + prev + prev;	/* prev * 3 */
    	f = fat + (x >> 1);	/* point to proper fat entry */
	if ((x & 1) == 0)
	{
	    f[0] = n;
	    f[1] = (f[1] & 0xf0) | (n >> 8);
	} else
	{
	    f[0] = (f[0] & 0x0f) | (n << 4);
	    f[1] = n >> 4;
	}
    }
    if (vflg > 1)
	printf("set(%d)=%d fat+%d=%02x, %02x\n", prev, n, x >> 1, f[0], f[1]);
}

/*
 * make a copy of *dirp into *curdir and do the swap at the same time
 */
copydir(curdir, dirp) register struct dir *
    curdir, *dirp;
{
    *curdir = *dirp;		/* make a copy of dir entry */
    curdir->file_start = swaph(curdir->file_start);
    curdir->file_time = swaph(curdir->file_time);
    curdir->file_date = swaph(curdir->file_date);
    curdir->file_size = swapw(curdir->file_size);

}

get_free()
{
    register int    free_cnt = 0;
    register int    free_ptr = FIRST_FAT;

    for (free_ptr = FIRST_FAT; free_ptr < fat_count; ++free_ptr)
	if (next_cluster(free_ptr) == FREE_CLUSTER)
	    ++free_cnt;
    return (free_cnt);
}

/*
 * return a pointer to a free directory slot.
 */
struct dir     *
    getdir(fd) register int fd;
{
    register struct dir *dirp;
    register int    i;
    register int    l;


    for (i = 0; i < dir_sectors; ++i)
    {
	cur_dir = dir_sector + i;	/* current directory sector */
	LSEEK(fd, cur_dir * BSIZE, 0);	/* seek to directory */
	if ((l = read(fd, dir_buff, BSIZE)) != BSIZE)
	    err("read returned %d instead of %d", l, BSIZE);
	for (dirp = (struct dir *) dir_buff; dirp < (struct dir *) (dir_buff + BSIZE); ++dirp)
	{
	    switch (dirp->file_name[0])
	    {
	    case FILE_NOT_USED:
	    case FILE_ERASED:
		return (dirp);	/* return it */
	    case FILE_DIR:
		continue;
	    default:
		continue;
	    }
	}
    }
    return ((struct dir *) 0);	/* no space available */
}

/* 
 * convert the unix path name "name" into a DOS file.ext 
 * and store it. translate name to upper case and add trailing
 * blanks.
 */
cvtname(dirp, name) register struct dir *
    dirp;
    register char  *name;
{
    register u_char *p = (u_char *) basename(name);
    register u_char *s;
    register u_char c;

    for (s = dirp->file_name; (c = *p++) && c != '.';)
    {
	if (islower(c))
	    c = toupper(c);
	if (s < dirp->file_name + MAX_FILE_LENGTH)
	    *s++ = c;
    }

    while (s < dirp->file_name + MAX_FILE_LENGTH)
	*s++ = ' ';
    if (c == 0)
	--p;			/* point back at terminator byte */
    for (s = dirp->file_ext; (c = *p++) && c != '.';)
    {
	if (islower(c))
	    c = toupper(c);
	if (s < dirp->file_ext + MAX_FILE_EXT)
	    *s++ = c;
    }
    while (s < dirp->file_ext + MAX_FILE_EXT)
	*s++ = ' ';
}

/*
 * take a unix time_t and store the corresponding dos time and date
 * fields into the directory structure.
 */
#include <sys/time.h>
struct tm      *localtime();

make_dos_time(dirp, time) register struct dir *
    dirp;
    long            time;
{
    register struct tm *tm = localtime(&time);

    dirp->file_time = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec >> 1);
    dirp->file_date = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday;
}

/*
 * if not in ascii mode then just write the data
 * otherwise we will change CR/LF into LF and LF/CR into LF.
 * we will remove NUL characters and stop on control-Z.
 */

#define Putc(file, ch) putc(ch, file)

awrite(fd, buff, length, lastc)
    register FILE  *fd;
    register int    length;
    register char  *buff;
    char           *lastc;
{
    register char  *end = buff + length;
    register int    c, lastchar = *lastc;

    if (!aflg)
    {
	fwrite(buff, 1, length, fd);
	return (ferror(fd) ? -1 : 0);
    }
    for (; buff < end;)
    {
	c = *buff++;
	if ((lastchar == CR && c == NL) || lastchar == NL && c == CR)
	{
	    Putc(fd, '\n');
	    lastchar = 0;	/* only collapse one at a time */
	    continue;
	}
	if (lastchar == CTL_Z)
	    break;
	if (lastchar)
	    Putc(fd, lastchar);
	lastchar = c;
    }
    if (length == 0 && lastchar && lastchar != CTL_Z)
	Putc(fd, lastchar);
    *lastc = lastchar;
    return (ferror(fd) ? -1 : 0);
}

/* return buff bytes from a unix file
 * if aflg then do special ASCII translations otherwise 
 * just return the binary data 
 */
aread(file, buff, length)
    register FILE  *file;
    register char  *buff;
    register int    length;
{
    static          next = 0;
    register char  *end;
    register char  *p = buff;
    register int    c;

    if (!aflg)
	return (read(fileno(file), buff, length));
    for (end = buff + length; p < end;)
    {
	if (c = next)
	{
	    *p++ = c;
	    next = 0;
	    continue;
	}			/* handle worst case */
	c = getc(file);
	if (c == EOF)
	{
	    if (p != buff)
		*p++ = CTL_Z;	/* not quite correct but close */
	    break;
	}
	if (c == NL)
	{
	    next = c;
	    c = CR;
	}
	*p++ = c;
    }
    return (p - buff);
}

/*
 * scan thru the name and convert it to one that is DOS acceptable.
 * we are in one of two states: FILE_STATE where we are scanning
 * the 8 bytes of the file name, or EXT_STATE where we are scanning
 * the 3 bytes of extension. If we encounter pattern matching characters
 * we count them appropriately (0 bytes for *, one byte for ? or [...]).
 */
#define FILE_STATE 0
#define EXT_STATE 1
catname(fn)
    char            fn[];
{
    int	c;
    char *s;
    char *limit;
    int state;

    for (s=fn, state = FILE_STATE, limit=fn+8; (c=*fn++); )
    {
	switch(c)
	    {
	case '\\':
	case '/':
		*s++ = '/';
		state = FILE_STATE;
		limit = s + 8;
		break;
	case '.':
		if (state == FILE_STATE)
		    {
		    *s++ = c;
		    limit = s + 3;
		    state++;
		    }
		else
			limit = s;	/* ignore after second . */
		break;
	case '*':
		*s++ = c;
		limit++;	/* we don't count *'s in limit */
		break;
	case '[':
		if (s >= limit)
			err("pattern can't match");
		do {
		    *s++ = c;
		    ++limit;
		}
		while ((c = *fn++) && c != ']');
		*s++ = c;
		break;
	default:
		if (s < limit)
			*s++ = c;
		}
		
    }
    *s = 0;
}

char           *
strsave(s)
    register char  *s;
{
    register int    len = strlen(s) + 1;	/* actual length required */
    register char  *new = malloc(len);
    if (new == (char *) 0)
	return (new);
    return (strcpy(new, s));
}


/*
 * determine FAT type from density
 * (either specified or from the floppy limits)
 */
getdensity()
{
    char            buff[512];
    register struct disk_info *diskinfo;

    if (isize == 0)
    {
	for (diskinfo = disk_types + TYPES; --diskinfo >= disk_types;)
	{
	    if (vflg > 1)
		printf("trying size=%d blocks fat=0x%x\n", diskinfo->size,
		       diskinfo->fat_type);
	    LSEEK(fd, (long) ((diskinfo->size - 1) * BSIZE), 0);
	    if (read(fd, buff, 512) == 512)
	    {
		isize = diskinfo->size;
		return (diskinfo->fat_type);
	    }
	}
    }
    for (diskinfo = disk_types; diskinfo < disk_types + TYPES; ++diskinfo)
    {
	if (isize == diskinfo->size)
	    return (diskinfo->fat_type);
    }
    err("invalid diskette size (%d blocks)", isize);
}

/*
 * build FAT and DOS directory structure 
 */

init_disk()
{
    register int    i;

    if (vflg)
	printf("initializing diskette (size=%d blocks %dk)\n", isize, isize / 2);
    fat[1] = 0xff;
    bzero(fat + FIRST_FAT, fat_length - FIRST_FAT);
    put_fat();
    LSEEK(fd, dir_sector * BSIZE, 0);
    bzero(dir_buff, sizeof dir_buff);
    for (i = 0; i < dir_sectors; ++i)
    {
	if (write(fd, dir_buff, sizeof dir_buff) != sizeof dir_buff)
	    err("writing directory");
    }
}


/*
 * write the appropriate boot block onto the disk
 * currently it only boots the first file on the disk but
 * this may change so that a particular file may be booted
 * we try to write a pc boot (without a warning message) unless
 * -b requests an RT boot block.
 */
init_boot()
{

    register int    f;
    register int    l;
    char            bootname[32];

    if (isize == 0)
	getdensity();
    sprintf(bootname, "/usr/mdec/fd%d%sdosboot", isize / 2, bflg ? "" : "pc");
    if ((f = open(bootname, 0)) < 0)
    {
	if (bflg)
	    err("cannot open %s", bootname);
	else if (vflg)
	{
	    printf("couldn't open bootstrap file %s\n", bootname);
	    return;
	}
    } else if (vflg)
	printf("installing bootstrap %s\n", bootname);
    if ((l = read(f, dir_buff, 512)) != 512)
	err("boot block short read (%l)", l);
    LSEEK(fd, (long) 0, 0);
    if ((l = write(fd, dir_buff, 512)) != 512)
	err("boot block - short write (%d)", l);
    close(f);
}

#define MAX_PART	4	/* maximum number of partitions */
#define BOOT_IND	0x80	/* boot indicator */
struct boot_block
{
#define PREAMBLE_SIZE 0x1be	/* Sigh */
    char            preamble[PREAMBLE_SIZE];	/* who knows what goes here */
    struct partition
    {
	char            boot_ind, boot_head, boot_sector, boot_cyl;
	char            sys_ind, sys_head, sys_sector, sys_cyl;
	unsigned short  rel_low, rel_high;
	unsigned short  size_low, size_high;
    }               part[MAX_PART];
    char            signature[2];
#define SIGNATURE_0 0x55
#define SIGNATURE_1 0xaa

};

/*
 * look for a hard disk partition table 
 * and set the offset variable appropriately
 * if the user specified -p without a partition number
 * then we will look for the active partition; otherwise
 * we will just use the one that was specified.
 * flag indicates if it is an error to not find a 
 * partition table or not.
 */
get_partition(flag)
{
    struct boot_block block;
    register struct partition *part = 0;
    register long   rel, size;
    register int    i;

    if (sizeof block != BSIZE)
	err("boot block size incorrect (%d)", sizeof block);
    lseek(fd, (long) 0, 0);
    if (read(fd, (char *) &block, sizeof block) != sizeof block)
	err("reading partition table (at block 0)");
    if (block.signature[0] != SIGNATURE_0 ||
	block.signature[1] != SIGNATURE_1)
	if (flag)
	    err("block 0 does not contain valid partition table (%02x,%02x)", block.signature[0], block.signature[1]);
	else
	    return(0);	/* no partition record */
    if (partition)
    {
	--partition;		/* make it zero based */
	if ((unsigned) partition >= MAX_PART)
	    err("invalid partition number (%d)", partition + 1);
	part = &block.part[partition];
    } else
    {
	for (i = 0; i < MAX_PART; ++i)
	{
	    if (block.part[i].boot_ind == BOOT_IND)
	    {
		if (part)
		    err("more than 1 active partition. Use -p#");
		partition = i;
		part = &block.part[partition];
	    }
	}
	if (part == 0)
	    if (flag)
		err("no active partition found");
	    else
		return(0);
    }
    part->rel_high = swaph(part->rel_high);
    part->rel_low = swaph(part->rel_low);
    rel = (part->rel_high << 16) + part->rel_low;
    size = (part->size_high << 16) + part->size_low;
    if (vflg > 1)
	printf("partition %d: start=%ld size=%ld boot=%s fat=%s\n",
	       partition + 1, rel, size,
	       part->boot_ind == BOOT_IND ? "yes" : "no",
	       part->sys_ind == 0x01 ? "12bit" : part->sys_ind == 0x04 ? "16bit" : "unknown");
    if (size <= 0)
	err("partition %d not allocated\n", partition);
    dos_offset = rel * BSIZE;
    disk_types[TYPES - 1].size = size;	/* store the size */
    if (part->sys_ind == 0x4)
	{
	++fat_16;		/* using 16 bit fat */
	eof_cluster = EOF_16BIT;
	}
    return(1);
}

/*
 * read the information about a hard disk partition off the disk
 */
read_fixed_info(type)
    register struct disk_info *type;
{
    struct dos_boot_info info;
    register int    cylsize;

    LSEEK(fd, (long) 0, 0);
    if (read(fd, (char *) &info, sizeof info) != sizeof info)
	err("couldn't read fixed disk information");
    if (vflg > 1)
	printf("oem=%.8s\n", info.oem);
    type->fat_size = WORD(info.fat_size);
    type->fat_copies = info.fat_copies;
    type->cluster_size = info.cluster_size;
    type->dir_sectors = WORD(info.dir_entries) /
	(BSIZE / (sizeof(struct dir)));
    type->heads = WORD(info.tracks);
    type->sectors = WORD(info.sectors);
    type->size = WORD(info.device_size);
    cylsize = (type->sectors * type->heads);
    if (cylsize)
	type->tracks = type->size / cylsize;
}

aton(ptr)
    register char  *ptr;
{
    if (*ptr == '+' || *ptr == '-')
	return (atoi(ptr));
    if (*ptr == '0' && (ptr[1] == 'x' || ptr[1] == 'X'))
	return (atox(ptr + 2));
    if (*ptr == '0' && (ptr[1] == 't' || ptr[1] == 't'))
	return (atoi(ptr + 2));
    return (atoi(ptr));
}

/*
 * convert string in hex to binary
 */
int 
atox(ptr)
    register char  *ptr;
{
    register int    n = 0;
    register int    c;

    for (; c = *ptr++;)
    {
	if (isdigit(c))
	    c -= '0';
	else if ('a' <= c && c <= 'f')
	    c -= 'a' - 10;
	else if ('A' <= c && c <= 'F')
	    c -= 'A' - 10;
	else
	    break;
	n = (n << 4) + c;
    }
    return (n);
}

/*
 * read the boot sector to determine the magic numbers for the diskette
 */
read_boot_info()
{
    char            buff[512];
    struct dos_boot_info *b = (struct dos_boot_info *) buff;
    int             cylsize;

    LSEEK(fd, 0, 0);

    if (read(fd, buff, sizeof buff) != sizeof buff)
	return (0);

    if (!PCBOOT(b->jump[0]))
	return (0);

    if (vflg > 1)
	printf("using information from boot record (%.8s)\n", b->oem);
    fat_size = WORD(b->fat_size);
    fat_copies = BYTE(b->fat_copies);
    sectors = WORD(b->sectors);
    heads = WORD(b->tracks);
    dir_sectors = WORD(b->dir_entries) /
	(BSIZE / (sizeof(struct dir)));
    cluster_size = BYTE(b->cluster_size);
    max_sectors = WORD(b->device_size);
    cylsize = (sectors * heads);
    if (cylsize)
	tracks = max_sectors / cylsize;
    return (1);
}

#define S	1		/* if user can set */
#define P	2		/* if normally printed */

char            attrs[] =	/* attribute byte bits */
{ATTR_READ_ONLY, ATTR_HIDDEN, ATTR_SYSTEM, ATTR_SUBDIR, ATTR_VOL_LABEL, ATTR_ARCHIVE};
char           *attrnames[] =	/* names to print for attribute byte */
{"RO",		"HIDDEN",	"SYSTEM",	"DIR",	"VOL",		"ARCHIVE", 0};
char            attrflags[] =				/* flags */
{	S+P,	S+P,		S+P,	    P,	    	P,		S, 0};

prattr(attr)
{
    int             i;
    if (vflg > 1)
	printf(" %02x", attr);
    for (i = 0; attrnames[i]; ++i)
	if ((attr & attrs[i]) && (vflg>1 || (attrflags[i]&P)))
	    printf(" %s", attrnames[i]);
}

getattr(string)
    char           *string;
{
    int             attr = 0;
    int             i, k=1;
    for (; *string;)
    {
	for (i = 0; attrnames[i]; ++i)
	    if ((k = strncmp(attrnames[i], string, strlen(attrnames[i]))) == 0)
		break;
	if (k != 0)
	    err("invalid attribute: %s", string);
	if ((attrflags[i]&S) == 0)
	    err("sorry, can't set %s attribute", attrnames[i]);
	attr |= attrs[i];
	string += strlen(attrnames[i]);
	if (*string == ',')
	    ++string;
    }
    return (attr);
}

mkpath(dirname)
char *dirname;
{
	char *s = dirname-1;
        struct stat statbuff;

	while (s = index(s+1,'/')) {
	    *s = 0;
	    if (stat(dirname,&statbuff) < 0 && mkdir(dirname,0777) < 0) {
		*s = '/';
		return(-1);	/* couldn't make it */
	    }
	    *s = '/';
	}
	return(mkdir(dirname,0777));
}

/*
 * date conversion routine - relies on the use of internal
 * routines from ctime
 */
static int
gtime(date,time)
	int date, time;
{
	int year, month;
	int day, hour, mins, secs;
	struct timeval tv;
	int result;
	int i;
	struct  timezone tz;
	static  int     dmsize[12] =
	    { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };


	day = DOSDAY(date);
	year = DOSYEAR(date);
	month = DOSMON(date);
	hour = DOSHR(time);
	mins = DOSMIN(time);
	secs = DOSSEC(time);

	if (debug)
		printf("%02d-%02d-%02d %02d:%02d:%02d\n",month,day,year,hour,mins,secs);
	gettimeofday(&tv, &tz);
	if (month < 1 || month > 12 ||
	    day < 1 || day > 31 ||
	    mins < 0 || mins > 59 ||
	    secs < 0 || secs > 59)
		return (0);
	if (hour == 24) {
		hour = 0;
		day++;
	}
	if (hour < 0 || hour > 23)
		return (0);
	result = 0;
	year += 1900;
	for (i = 1970; i < year; i++)
		result += dysize(i);
	/* Leap year */
	if (dysize(year) == 366 && month >= 3)
		result++;
	while (--month)
		result += dmsize[month-1];
	result += day-1;
	result = 24*result + hour;
	result = 60*result + mins;
	result = 60*result + secs;
	result += (long)tz.tz_minuteswest*60;
	/* now fix up local daylight time */
	if (localtime(&result)->tm_isdst)
		result -= 60*60;

	return (result);
}
