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

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

/*
 * copy the unix boot programs to the harddisk
 */
#ifndef STANDALONE
#include <stdio.h>
#endif

#ifdef DOS
#include <fnctl.h>
#include <dos.h>
#else

/* UNIX & STANDALONE */
#define  SEEK_SET	0
#include "../machine/dkio.h"
#include "../h/file.h"

#ifdef STANDALONE
#include "saio.h"
#undef	DKIOCGPART
#define	DKIOCGPART	SAIOGETPART
#define usage		install_usage
#else 

/* UNIX */
#include "../h/types.h"
#include "../h/ioctl.h"
#endif
#endif /* DOS */


#include "installboot.h"

#ifdef DOS
#define	swapshort(x)	(x)
#define	swaplong(x)	(x)
#endif

/*
 * global variables.
 */
int	HD0;		/* disk descripter (dos device # or unix file descripter)  DEST*/
int	FD0;		/* disk descripter (dos device # or unix file descripter)  SOURCE */
int	notrash = 1;	/* flag not to trash exiting dos partition (default is to query) */
int	trash = 0;	/* flag to trash exiting dos partition (default is to query) */
int	use_zero = 0;	/* flag to use block zero rather than largest useable partition */
int	must_use_zero = 0;/* flag indicating that we MUST use 0 or exit with an error */
int	fake = 0;	/* "fake" a disk (used to test against a unix file) */
#ifdef STANDALONE
int	verify = 1;	/* verify the copy */ 
#else
int	verify = 0;	/* verify the copy */ 
#endif
int	nspc;		/* number sectors per cylinder on HD0, set by init_files() */
int	nspt;		/* number sectors per track on HD0, set by init_files() */
int	ntpc;	/* number heads on HD0, set by init_files() */
int	cluster_size;	/* number of sectors per cluster on HD0, set by init_files() */
int	dos_cluster_size; /* number of sectors per cluster on FD0, set by init_files() */
int	disk_cylinders; /* total number of cylinders on HD0, set by init_files() */
int	dos_dir_loc;	/* location of the first sector of the dos directory */
int	dos_dir_size;	/* size of dos directory */
char	fat_tab[FAT_SIZE]; /* fat for HD0 */
char	dos_fat[FAT_SIZE*MAX_FAT_COUNT]; /* fat for FD0 */
int	next_free_cluster;	/* next free cluster on HD0 */
int	next_directory; /* next free directory entry on HD0 */
int	fd_start;	/* starting block of the first cluster */
struct 	dos_entry dir[NUMBER_ENTRIES]; /* directory for HD0 */
/*
 * disk devices to open
 */
#ifdef DOS
/* dos */
char	*harddisk = "C:";
char	*floppy = "A:";
#else
#ifdef STANDALONE
/* standalone */
char	*harddisk = "hd(0,7)";
char	*floppy = "fd(0,0)";
#else
/* unix */
char	*harddisk = "/dev/rhd0h";
char	*floppy = "/dev/fd0";
#endif
#endif
struct filelist	filelist[] = {	/* list of files to copy to dos */
	{ "IBMBIO  COM",DIR_HIDDEN|DIR_SYSTEM,0 },
	{ "IBMDOS  COM",DIR_HIDDEN|DIR_SYSTEM,0 },
	{ "COMMAND COM",DIR_READONLY,0 },
	{ "AUTOEXECBAT",DIR_READONLY,0 },
	{ "UNIX    EXE",DIR_READONLY,0 },
	{ "BOOT       ",DIR_READONLY,0 },
	{ "NICPSHH EXE",DIR_READONLY,0 },
};
/* other globals (in another file.. )
 *
 * standard_fdisk: standard hd block 0 record.
 * dos_boot_rec: standard dos block 0 boot record.
 */
extern	struct fdisk_tab	standard_fdisk;
extern	struct dos_boot		*dos_boot_rec;

main(argc,argv)
	int	argc;
	char	**argv;
{
	struct  fdisk_tab	ftab;
	struct	dos_entry	*dosdir,*dep,*doslast;
	int	totalsize,total_sectors, start,end,entry;
	int	cylinders,bstart,bend,i;

	printf("\33K");		/* clear screen */
	parseargs(argc,argv);
	if (verify > 1) {
		error("Insert the boot diskette into %s\n",floppy);
		error("and hit <ENTER>\n");
		/* void */ getchar();
	}
	init_files();
	if (get_disk(HD0,BOOT_REC,&ftab,sizeof(ftab))) {
		error("Couldn't read hard disk block 0\n");
		exit(E_BAD_DISK);
	}
	/*
	 * if the partition exists, don't mess with anything
	 * an exit of '1' indicates the partition exists but
	 * the boot files have not been copied.
	 */
	start = 0;
	end = disk_cylinders-1;
	entry = 0;
	if (swapshort(ftab.magic) == FMAGIC) {
		if (verify > 1) {
			printf("partition has been found\n");
		}
		standard_fdisk = ftab;
		switch(getfreepart(&start,&end,&entry,ftab.entry)) {
		case DOS_EXISTS:
			error("Dos partition Exists\n");
			if (notrash) {
				exit(E_PARTITION_OK);
			}
			if (!(trash)) {
				error("Clobber partition? ");
#ifndef STANDALONE
				fflush(stderr);
#endif
				if (get_upper() != 'Y') {
					exit(E_PARTITION_OK);
				}
			}
			break;
		case NO_SPACE:
			error("No space for dos partition\n");
			exit(E_NO_SPACE);
		case OK:
			break;
		default:
			error("Illegal internal state\n");
			exit(E_PANIC);
		}
	} else if (verify > 1) {
		printf("no boot partition found magic = %x\n",ftab.magic);
	}

	dosdir = (struct dos_entry *) malloc(dos_dir_size);
	if (get_disk(FD0,dos_dir_loc,dosdir,dos_dir_size)) {
		error("Couldn't read diskette directory\n");
		exit(E_BAD_FLOPPY);
	}
	totalsize = (BOOT_SIZE/NBPS)+(DIR_SIZE/NBPS)+(FAT_SIZE/NBPS);
	if (verify > 1) {
		printf("total <BOOT/ DIR/ FAT> = %d\n",totalsize);
	}
	doslast = &dosdir[(dos_dir_size/sizeof(struct dos_entry))+1];
	for (i=0; i < sizeof(filelist)/sizeof(filelist[0]); i++) {
		for (dep = dosdir; (dep < doslast) && (dep->dend != DIR_END);
								       dep++) {
			if (strncmp(dep->name,filelist[i].name,DIR_NAME_SIZE)
								        == 0) {
				totalsize += (swaplong(dep->file_size)+NBPS-1)
								         /NBPS;
				if (verify > 1) {
					printf("total <%s> = %d <+%d>\n",
			  			filelist[i].name,totalsize,
						      swaplong(dep->file_size));
				}
				filelist[i].dep = dep;
				break;
			}
		}
		if (filelist[i].dep == NULL) {
			error("couldn't find file %s\n",filelist[i].name);
			exit(E_MISS_FILE);
		}
	}

	/* magic, ESDI disks look to dos as 2048 sectors per cyl. They have no
	 * bad blocks.
	 */
	if (nspc  <  2048) {
		/* one extra to handle bad blocks */
		cylinders = ((totalsize+nspc-1)/nspc)+1;
	} else {
		cylinders = (totalsize+nspc-1)/nspc;
	}

	if (verify > 1) {
		printf("allocating %d cyls between %d and %d\n",cylinders,
								    start,end);
	}
	if (start+cylinders > end ) {
		error("Not enough free partition space for a 4.3 boot\n");
		exit(E_NO_SPACE);
	}

	/* set up fdisk entry */
	total_sectors = cylinders*nspc - (start ? 0 : nspt);
	cluster_size = (total_sectors+NUMBER_FAT_ENTRIES-1)/(NUMBER_FAT_ENTRIES);
	for (i=1; i < cluster_size; i <<= 1);
	cluster_size = i;

	if (verify > 1) {	
		printf("cluster_size = %d, tsect=%d, FAT_ENT=%d\n",cluster_size,total_sectors,NUMBER_FAT_ENTRIES);
	}
	end = start + cylinders;
	bstart = (start ? start*nspc : nspt);
	bend = (end+1)*nspc;
	standard_fdisk.entry[entry].system_ind = DOSID;
	standard_fdisk.entry[entry].boot_ind = BOOT_ID;
	standard_fdisk.entry[entry].startcyl = LOWCYL(start);
	standard_fdisk.entry[entry].starthead = (start? 0 : 1);
	standard_fdisk.entry[entry].startsect = HIGHCYL(start) | 1;
	standard_fdisk.entry[entry].endcyl = LOWCYL(end);
	standard_fdisk.entry[entry].endhead = ntpc - 1;
	standard_fdisk.entry[entry].endsect = HIGHCYL(end) | nspt;
	standard_fdisk.entry[entry].rel_start[0] = swapshort(LOWORD(bstart));
	standard_fdisk.entry[entry].rel_start[1] = swapshort(HIWORD(bstart));
	standard_fdisk.entry[entry].part_len[0] = swapshort(LOWORD(bend-bstart));
	standard_fdisk.entry[entry].part_len[1] = swapshort(HIWORD(bend-bstart));
	standard_fdisk.magic = swapshort(FMAGIC);

	/* write it out */
	if (put_disk(HD0,BOOT_REC,&standard_fdisk,sizeof(standard_fdisk))) {
		return(E_BAD_DISK);
	}

	/* set up the boot record */
	dos_boot_rec->cluster_size = cluster_size;
	dos_boot_rec->device_size[0] = LOBYTE(total_sectors);
	dos_boot_rec->device_size[1] = HIBYTE(total_sectors);
	dos_boot_rec->tracks[0] = LOBYTE(ntpc);
	dos_boot_rec->tracks[1] = HIBYTE(ntpc);
	dos_boot_rec->sectors[0] = LOBYTE(nspt);
	dos_boot_rec->sectors[1] = HIBYTE(nspt);
	dos_boot_rec->hidden[0] = LOBYTE(bstart);
	dos_boot_rec->hidden[1] = HIBYTE(bstart);

	/* write it out */
	if (put_disk(HD0,bstart+DOS_BOOT_REC,dos_boot_rec,NBPS)) {
		return(E_BAD_DISK);
	}

	/* now copy the boot stuff to disk */
	init_fat(bstart,bend);
	init_dir(bstart);
	for (i=0; i < sizeof(filelist)/sizeof(filelist[0]); i++) {
		doscopy(&filelist[i],bstart);
	}
	fill_fat(bstart);
	exit(E_OK);
}

int
getfreepart(start,end,entry,fdisk)
	int	*start,*end,*entry;
	struct fdisk_entry *fdisk;
{
	int	starttab[MAX_ENTRIES];
	int	endtab[MAX_ENTRIES];
	int	i,j,k;
	int	tmp_start,tmp_end,max_size,dos_exists = 0;

	for (i=0; i < MAX_ENTRIES; i++) {
		starttab[i] = endtab[i] = -1;
	}
	*entry = 0; starttab[0] = 0; endtab[0] = disk_cylinders-1;

	/* parse out the current entries */
	for (i=0;i < MAX_ENTRIES; i++) {
		/* valid system id's are non-zero */
		if (fdisk[i].system_ind) {
			/* don't use this entry if someone else is using it */
			if (i == (*entry)) {
				(*entry)++;
			}
			/*
			 * dos parition exists
			 * don't count the space used by the dos parition.
			 */
			if ((fdisk[i].system_ind == DOSID) || (fdisk[i].system_ind == DOS4)) {
				if (verify > 1) {
					tmp_start=fdisk[i].startcyl+GETHIGH(fdisk[i].startsect);
					tmp_end=fdisk[i].endcyl+GETHIGH(fdisk[i].endsect);
					printf("dos partition @ %d - (%d,%d)\n",i,tmp_start,tmp_end);
				}
				(*entry) = i;
				dos_exists++;
				/* if dos exists, and we want to reuse dos's space */
				continue;
			}
			tmp_start = fdisk[i].startcyl + GETHIGH(fdisk[i].startsect);
			tmp_end = fdisk[i].endcyl + GETHIGH(fdisk[i].endsect);
			if (verify > 1) {
				printf("other parition @ %d - (%d,%d)\n",i,tmp_start,tmp_end);
			}
			for (j=0; j < MAX_ENTRIES; j++) {
				if (starttab[j] == -1)
					continue;
				/*
				 * check the fragment tables to delete
				 * the space used by this partition
				 */
				if ((starttab[j] <= tmp_start) && (tmp_start <= endtab[j])) {
					/* start of the fragment is overlaid ? */
					if (tmp_start == starttab[j]) {
						/* entire fragment is overlaid ? */
						if (tmp_end >= endtab[j]) {
							starttab[j] = -1;
						} else {
							starttab[j] = tmp_end+1;
						}
						continue;
					} 
					/* end of the fragment is overlaid ? */
					if (tmp_end >= endtab[j]) {
						endtab[j] = tmp_start-1;
					} else {
						/* middle of the fragment is overlaid,
						 * split it into two fragments.
						 */
						for (k=0; k < MAX_ENTRIES; k++)
							if(starttab[k] == -1)
								break;
						/* this can only happen if all
						 * four parition entries are used
						 */
						if (starttab[k] != -1) {
							if (verify > 1) {
							 printf("out of partition slots..\n");
							}
							return(NO_SPACE);
						}
						endtab[k] = endtab[j];
						starttab[k] = tmp_end+1;
						endtab[j] = tmp_start-1;
					}
				} /* if partion is in fragment */
			} /* end for j.. */
		} /* end if partition has a valid system id */
	} /* end for i... */

	/*
	 * now look for the biggest useable partition
	 * unless use zero is specified
	 */
	max_size = 0;
	for (i=0; i < MAX_ENTRIES; i++) {
		if (starttab[i] == -1)
			continue;
		/* don't select a partion beyond dos's max address range */
		if (starttab[i] > DOS_MAX) {
			if (verify > 1) {
				printf("free partition %d starts outside dos range\n");
			}
			continue;
		}
		tmp_end = (endtab[i] > DOS_MAX ? DOS_MAX : endtab[i]);
		if (max_size < (tmp_end -starttab[i])+1 ) {
			*start = starttab[i];
			*end = tmp_end;
			max_size = (tmp_end - starttab[i])+1;
		}
		if ((use_zero) && (starttab[i] == 0)) {
			*start = starttab[i];
			*end = tmp_end;
			max_size = (tmp_end - starttab[i])+1;
			break;
		}
	}
	if (verify > 1) {
		printf("max_size = %d, must_use_zero = %d, use_zero = %d, start=%d, end=%d, entry = %d\n",max_size, must_use_zero, use_zero, *start, *end, *entry);
	}
	if (max_size == 0) {
		return(NO_SPACE);
	}
	if ((must_use_zero) && (starttab[i] != 0)) {
		return(NO_SPACE);
	}
	if (dos_exists) {
		return(DOS_EXISTS);
	}
	return(OK);
}

/*
 * put initial magic number in the FATs
 * and mark the bad blocks from the Manufacturer's
 * bad block table.
 */
init_fat(start,end)
{
	int	i,j,cluster;
	char	buffer[NBPS];

	if (verify) {
		printf("initalizing FAT :\n");
	}

	bzero(fat_tab,FAT_SIZE);
	PUTFAT(fat_tab,0-FIRST_CLUST,FAT_SIG1);
	PUTFAT(fat_tab,1-FIRST_CLUST,FAT_SIG2);
	next_free_cluster = 0;

	/* find all the bad blocks */
	for (i=start+HD_START; i <= end; i++) {
		if (!put_disk(HD0,i,fat_tab,NBPS)) {
			if (!get_disk(HD0,i,buffer,NBPS)) {
				for (j=0;j < NBPS; j++) {
					if (fat_tab[j] != buffer[j])
						break;
				}
				if (j >= NBPS)
					continue;
			}
		}
		/* if we got here, the block is bad */
		cluster = (i-HD_START)/cluster_size;
		PUTFAT(fat_tab,cluster,BADBLOCK);
		if (cluster == next_free_cluster) {
			next_free_cluster++;
		}
	}
		
	if (verify > 1) {
		printf("\n");
	}
		
	if (put_disk(HD0,start+FAT,fat_tab,FAT_SIZE)) {
		return(E_BAD_DISK);
	}
}

/* 
 * clear out the root directory
 */
init_dir(start) 
	int	start;
{
	bzero(dir,sizeof(dir));
	if (put_disk(HD0,start+HD_DIRECTORY,dir,sizeof(dir))) {
		return(E_BAD_DISK);
	}
	next_directory = 0;
}

/*
 * copy a file with the approperiate attributes from dos diskette
 * to dos harddisk.
 */
doscopy(file,start)
	struct	filelist *file;
	int	start;
{
	char	*buffer=(char *)malloc(dos_cluster_size*NBPS);
	char	*bp;
	int	hdblock,i;
	int	dos_cluster;

	/* set up the directory */
	dir[next_directory] = *(file->dep);
	dir[next_directory].file_attribute = file->attribute;
	dir[next_directory++].file_start = swapshort(next_free_cluster+FIRST_CLUST);

	if (verify) {
		printf("Copying %s\n",file->name);
	}
	if (verify > 1) {
		printf("starting @ cluster %d :",next_free_cluster+FIRST_CLUST);
	}

	/* initialize the copy pointers */
	dos_cluster = swapshort(file->dep->file_start)-FIRST_CLUST;
	hdblock = next_free_cluster*cluster_size;

	/* do the copy */
	while (((dos_cluster+FIRST_CLUST)&DOS_EOF_R) != (DOS_EOF_R)) {
		if(get_disk(FD0,fd_start+dos_cluster*dos_cluster_size,buffer,dos_cluster_size*NBPS)) {
			error("Couldn't read floppy block %d (cluster=0x%x)\n",fd_start+dos_cluster*dos_cluster_size,dos_cluster+FIRST_CLUST);
			exit(E_BAD_FLOPPY);
		}
		bp= buffer;
		for (i=0; i < dos_cluster_size; i++) {
			if ((hdblock / cluster_size) != next_free_cluster) {
				while (GETFAT(fat_tab,hdblock/cluster_size) == BADBLOCK) {
					hdblock += cluster_size;
				}
				PUTFAT(fat_tab,next_free_cluster,(hdblock/cluster_size)+FIRST_CLUST);
				next_free_cluster = hdblock/cluster_size;
			}
			if (put_disk(HD0,start+HD_START+(hdblock++),bp,NBPS)) {
				error("Marginal disk block error\n");
				exit(E_MARGIN_DISK);
			}
			bp+=NBPS;
		}
		dos_cluster = GETFAT(dos_fat,dos_cluster)-FIRST_CLUST;
	}

	/* clean up the pointers */
	/* NOTE: the meaning of hd block has changed here! It is now a cluster */
	hdblock = next_free_cluster;
	while (GETFAT(fat_tab,++hdblock) == BADBLOCK);
	PUTFAT(fat_tab,next_free_cluster,DOS_EOF_W);
	if (verify > 1) {
		printf(" EOF\n");
	}
	next_free_cluster = hdblock;

	/* write the data out to disk */
	if (put_disk(HD0,start+FAT,fat_tab,FAT_SIZE)) {
		error("Can't write FAT\n");
		exit(E_BAD_DISK);
	}
	if (put_disk(HD0,start+HD_DIRECTORY,dir,sizeof(dir))) {
		error("Can't write DIR\n");
		exit(E_BAD_DISK);
	}
}

/*
 * change all unallocated blocks to bad blocks.
 */
fill_fat(start) 
	int	start;
{
	int i;

	if (verify > 1) {
		printf("Filling FAT...\n");
	}
	for (i=FIRST_CLUST;i < NUMBER_FAT_ENTRIES ; i++) {
		if (GETFAT(fat_tab,i-FIRST_CLUST) == UNALLOCATED) {
			PUTFAT(fat_tab,i-FIRST_CLUST,BADBLOCK);
		}
	}
	if (verify > 2) {
		printf("\n");
	}

	for (i=0; i < FAT_COUNT; i++) {
		/* void */ put_disk(HD0,start+FAT+(i*(FAT_SIZE/NBPS)),
				fat_tab,FAT_SIZE);
	}
}

/*
 * get a disk block
 *  block is NBPS bytes.
 *  size must be a multiple of NBPS (at least for dos side).
 */
get_disk(disk,block,buffer,size) 
	int	disk,block,size;
	char	*buffer;
{
#ifdef DOS
	union	REGS	regs;

	regs.h.al = disk;
	regs.w.bx = swapshort(buffer);
	regs.w.cx = swapshort(size/NBPS);
	regs.w.dx = swapshort(block);
	int86(ABSOLUTE_READ,&regs,&regs);
	return(regs.w.ah);
#else
	/* UNIX */
	lseek(disk,block*NBPS,SEEK_SET);
	return(read(disk,buffer,size) != size);
#endif
}

/*
 * put a disk block
 *  block is NBPS bytes.
 *  size must be a multiple of NBPS (at least for dos side).
 */
put_disk(disk,block,buffer,size) 
	int	disk,block,size;
	char	*buffer;
{
#ifdef DOS
	union	REGS	regs;

	regs.h.al = disk;
	regs.w.bx = swapshort(buffer);
	regs.w.cx = swapshort(size/NBPS);
	regs.w.dx = swapshort(block);
	int86(ABSOLUTE_WRITE,&regs,&regs);
	return(regs.w.ah);
#else
	/* UNIX */
	lseek(disk,block*NBPS,SEEK_SET);
	return(write(disk,buffer,size) != size);
#endif
}

char	cvdrive[] = { 0x00, 0x01, 0x80, 0x81 };
#define MAXDRIVE	sizeof(cvdrive)
/*
 * intialize the global data structures
 */
init_files()
{
	int	dos_fat_size;
	int	dos_fat_count;
	struct	dos_boot	*d_boot;
#ifdef DOS
	union	REGS	regs;
	/* convert the disk types */
	FD0 = UPPER(*floppy) - 'A';
	HD0 = UPPER(*harddisk) -'A';
	if (FD0 < 0 || FD0 > MAX_DRIVE) {
		error("%c: bad drive number\n",*floppy);
		exit(E_CANTOPEN);
	}
	if ( HD0 < 0 || HD0 > MAX_DRIVE) {
		error("%c: bad drive number\n",*harddisk);
		exit(E_CANTOPEN);
	}

	/* get harddisk drive info */
	if (!fake) {
		regs.h.ah = GETINFO;
		regs.h.dl = cvdrive[HD0];
		int86(BIO_DISK,&regs,&regs);
		if (regs.x.cflag) {
			error("Can't get info on %c:\n",*harddisk);
			exit(E_NOINFO);
		}
		nspt = GETSECTOR(regs.h.cl);
		disk_cylinders = GETHIGH(regs.h.cl) | regs.h.ch;
		ntpc = regs.h.dh;
	}
#else
	struct dkpart	dkinfo;

	/* UNIX */
	/* open the disk files */
	if ((HD0 = open(harddisk,O_RDWR)) < 0) {
		perror(harddisk);
		exit(E_CANTOPEN);
	}
	if ((FD0 = open(floppy,O_RDWR)) < 0) {
		perror(floppy);
		exit(E_CANTOPEN);
	}

	/* get harddisk drive info */
	if (!fake) {
		if ((ioctl(HD0,DKIOCGPART,&dkinfo)) < 0) {
			perror(harddisk);
			exit(E_NOINFO);
		}
		nspt = dkinfo.dk_nsector;
		disk_cylinders = dkinfo.dk_ncyl;
		ntpc =dkinfo.dk_ntrack;
	}
#endif
	nspc = nspt * ntpc;
	d_boot = (struct dos_boot *) &dos_fat;
	if (get_disk(FD0,DOS_BOOT_REC,(char *)d_boot,BOOT_SIZE)) {
		return(E_BAD_FLOPPY);
	}
	dos_cluster_size = d_boot->cluster_size;
	dos_fat_size = MKHIGH(d_boot->fat_size[1]) + MKLOW(d_boot->fat_size[0]);
	dos_fat_count = d_boot->fat_copies;
	dos_dir_loc = dos_fat_size * dos_fat_count+FAT; 
	dos_dir_size = (MKHIGH(d_boot->dir_entries[1]) + MKLOW(d_boot->dir_entries[0]))*sizeof(struct dos_entry);
	fd_start = dos_dir_size/NBPS + dos_dir_loc + MKHIGH(d_boot->hidden[1]) + MKHIGH(d_boot->hidden[0]);
	if (get_disk(FD0,FAT,(char *)&dos_fat,dos_fat_size * NBPS)) {
		return(E_BAD_FLOPPY);
	}
}


/*
 * put and get fat entries
 */
PUTFAT(fat,cluster,value)
	char	*fat;
	int	cluster,value;
{
	int	index =  (cluster+FIRST_CLUST)*FAT_ENTRY_SIZE/NBITPBYTE;

	if (verify > 2 ) {
		printf(" %03x>%03x",cluster+FIRST_CLUST,value);
	}

	if ((unsigned)index >= FAT_SIZE) {
		error("FAT overrun error!!\n");
		exit(E_PANIC);
	}

	if (cluster & 1) {
		fat[index] &=  0x0f;
		fat[index] |= (value << 4) & 0xf0;
		fat[index+1] = ((value >> 4) & 0xff);
	} else {
		fat[index] = value & 0xff;
		fat[index+1] &=  0xf0;
		fat[index+1] |= ((value >> 8) & 0x0f);
	}
}

/*
 * put and get fat entries
 */
GETFAT(fat,cluster)
	char	*fat;
	int	cluster;
{
	int	index =  (cluster+FIRST_CLUST)*FAT_ENTRY_SIZE/NBITPBYTE;

	if (cluster & 1) {
		return( (((int)fat[index] & 0xf0) >> 4) | ((int)fat[index+1] << 4) );
	} else {
		return( ((int)fat[index]) | (((int)fat[index+1] & 0x0f) << 8) );
	}
}

#ifndef DOS
/*
 * swap the word (dos word)  if need be (UNIX only)
 */
swapshort(x)
	int	x;
{
	return(((x & 0xff) << 8) | ((x >> 8) & 0xff));
}

/*
 * swap the long if need be (UNIX only)
 */
swaplong(x)
	long	x;
{
	return(swapshort(x & 0xffff) << 16 | swapshort((x >> 16) & 0xffff));
}
#endif

/*
 * get an uppercase letter from stdin
 */
get_upper()
{
	int	c;

	if ((c = getchar()) >= 'a' && ( c <= 'z')) {
		c = (c - 'a') + 'A';
	}
	return(c);
}

/*
 * parse the argument lists.
 */
parseargs(argc,argv)
	int	argc;
	char	**argv;
{
	char	*prog = *argv++;
	char	*cp;

	while (cp = *argv++) {

		/*
		 * parse the switches
		 */
		if ( (*cp == '-')
#ifdef DOS
 			|| (*cp == '/')
#endif DOS
					)  {
			while (*++cp) {
				switch (*cp) {
				/* don't trash dos if it exits */
				case 'n':
					notrash++;
					trash = 0;
					break;
				/* trash dos if it exits */
				case 't':
					trash++;
					notrash = 0;
					break;
				/* query (default) */
				case 'q':
					trash = 0;
					notrash = 0;
					break;
				/* perfer to use cyl 0 for dos */ 
				case 'z':
					use_zero++;
					break;
				/* must use cyl 0 for dos */
				case '0':
					use_zero++;
					must_use_zero++;
					break;
				case 'v':
					verify++;
					break;
				case 'h':
					if (!(harddisk = *argv++)) {
						usage(prog);
					}
					break;
				case 'f':
					if (!(floppy = *argv++)) {
						usage(prog);
					}
					break;
				case 'k':
					fake++;
					if (!(nspt = atoi(*argv++))) {
						usage(prog);
					}
					if (!(ntpc = atoi(*argv++))) {
						usage(prog);
					}
					if (!(disk_cylinders = atoi(*argv++))) {
						usage(prog);
					}
					break;
				/* 
				 * insert new switches here
				 * don't forget to update the 
				 * usage line
				 */
				default:
					usage(prog);
				}
			}
		} else {
			/*
			 * insert new non-switch parameters here
			 * as if statments (don't forget to update
			 *  the usage line).
			 */
			usage(prog);
		}
	}

}

usage(prog)
	char	*prog;
{
	error("usage:%s [-ntqz0v] [-h harddisk] [-f floppy] [-k sector track cyl]\n",prog);
	exit(E_BAD_PARAM);
}

#ifdef STANDALONE
#undef usage
#endif

#ifdef DOS
/*
 * utilities supplied by unix libc.a but not dos
 * these are not the most efficient routines, but
 * this program is a `use once' program and efficiency
 * is not a high prioritly.
 */
bzero(cp,count)
	char	*cp;
	int	count;
{
	/* "poor man's" bzero */
	while(count--)
		*cp++ = 0;
}

bcopy(cp1,cp2,count)
	char	*cp1,*cp2;
	int	count;
{
	/* "poor man's" bcopy */
	while (count--)
		*cp1 = *cp2;
}
#endif

error(format,x,y,z)
	char *format;
	int	x,y,z;
{
#ifdef STANDALONE
	printf(format,x,y,z);
#else
/* unix & dos */
	fprintf(stderr,format,x,y,z);
#endif 
}
