/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */

/* $Header:minidisk.c 12.0$ */
/* $ACIS:minidisk.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/standca/RCS/minidisk.c,v $ */

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

#include "../h/types.h"
#include "../h/time.h"
#include <ctype.h>
#include <setjmp.h>
#include "../machineio/hdconfig.h"
#ifndef STANDALONE
#include "../h/ioctl.h"
#include "../machine/dkio.h"
#endif
#include "err.h"

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

/*
** These defines are for the different types of minidisk
** partitions that can be defined with the "standard"
** command.
*/

#define STANDARD 0	/* Use standard Berkeley rules. */
#define ROOT 1		/* Similar To Berkeley, with larger "a" partition. */
#define SOURCE 2	/* Two partitions, standard "a", remainder is "g". */
#define SINGLE 3	/* Entire disk is a single "g" partition. */
#define REDUCED 4	/* "a", "b", and "g" partition for reduced system. */
#define MINIMAL 5	/* "a" and "b" partitions for minimal system. */

int tracksize;		/* number of tracks */
int sectors;		/* number of sectors/trace */
int cylsize;		/* the number of sectors per cyl */
int disksize;		/* the size of this disk */
int unit;		/* the unit number from the name */
static int scsi;	/* a scsi device was selected */
static int ps2esdi;	/* a ps2 esdi device was selected */
static char type[3];	/* disk type */
static int how;		/* how the disk was opened */

#define TRUE 1
#define FALSE 0

char *mprompt();
char *getname();
struct minidisk *findmini();
struct minidisk *findiodn();
jmp_buf label;
int fd;
struct minidirectory *minidisk;
struct minidisk *findfree();
struct minidisk *splitfree();
static char *mpromptptr;
char *index();
int	iflg;		/* -i specified (initialize minidisk if not valid) */

#define IODN_BASE	32736	/* IODN range reserved for 4.2: 32736 ... 32751 */
#define IODN_MAX	32751

#ifdef ATR
#define RESERVED_NAME(x) ((x) == 'c' || (x) == 'h')
#else
#define RESERVED_NAME(x) ((x) == 'c')
#endif ATR

main(argc,argv) char **argv;
{
	register char *file;
	struct minidirectory minidirectory;
	struct minidirectory minidirectory2;
	int first = 0;
	register int differ = 0;

	minidisk = &minidirectory;
	iflg = 0;
	while (argc > 1 && argv[1][0] == '-') {
		switch(argv[1][1]) {
		case 'i':
			iflg++;
			break;
		default:
			err("minidisk: invalid option %s",argv[1]);
		}
		++argv; --argc;
	}
	if (argc > 1)
		file = argv[1];
	else
		{
#ifdef STANDALONE
		printf("Typical disk names are hd(unit,2) where unit=0, 1, 2\n");
#else
		printf("Typical disk names are /dev/rhd?c where ?=0, 1, 2\n");
		printf(" or /dev/rsc?c where ?=0-d\n");
#endif
		file = mprompt("disk ");
		}
	unit = getunit(file);
	if ((fd = open(file,how=2)) < 0 && (fd = open(file,how=0)) < 0)
		err("can't open %s",file);
loop:
	getdisksize();		/* read configuration block */

	printf("%s: %s\n",file,how == 0 ? "read-only" : "");

	lseek(fd, (long) MINIDISK_BLOCK * BLOCK_SIZE, 0);
	if (read(fd, (char *) minidisk, sizeof (struct minidirectory)) != sizeof (struct minidirectory))
		err("can't read minidisk directory");
	
	if (ps2esdi && minidisk->header.bad_size == 0)
		lseek(fd, (long) MINIDISK_BLOCK2(sectors) * BLOCK_SIZE, 0);
	else
		lseek(fd, (long) MINIDISK_BLOCK2(sectors) * BLOCK_SIZE, 0);
	if (read(fd, (char *) &minidirectory2, sizeof (struct minidirectory)) != sizeof (struct minidirectory))
		err("can't read minidisk directory2");
	if ((differ = bcmp(&minidirectory,&minidirectory2, sizeof (struct minidirectory))) != 0)
		if (iflg) {
			initialize();
			differ = 0;
		} else
			printf("Warning: primary and secondary directory differ\n	(write command will re-sync; swap will swap primary and secondary)\n");
	if (!differ && iszero((char *) &minidirectory, sizeof (struct minidirectory)))
		{
		printf("primary and secondary directories both zero - initializing\n");
		if (how == 0)
			printf("Can't initialize - %s open read-only\n",file);
		else
			initialize();
		}


	for (;;)
		{
		char *cmd;
		if (_setjmp(label))
			;
		if (first++ == 0)
			prmini(minidisk);
		mpromptptr = 0;		/* clear any pending stuff */
		cmd = mprompt("> ");
		if (cmd == 0)
			break;
		if (cmdcmp(cmd,"quit"))
			break;
		else if (cmdcmp(cmd,"create"))
			create(minidisk);
		else if (cmdcmp(cmd,"change"))
			change(minidisk);
		else if (cmdcmp(cmd,"disk"))
			{
			close(fd);
			file = mprompt("disk ");
			unit = getunit(file);
			if ((fd = open(file,how=2)) < 0 && (fd = open(file,how=0)) < 0)
				err("can't open %s",file);
			first = 0;		/* print directory */
			goto loop;
			}
		else if (cmdcmp(cmd,"delete"))
			delete(minidisk);
		else if (cmdcmp(cmd,"dir") ||
				cmdcmp(cmd,"list"))
			prmini(minidisk);
		else if (cmdcmp(cmd,"standard"))
			{
			if (minidisk->header.number != 1)
				warn("you must initialize first");
			standard();
			}
		else if (cmdcmp(cmd,"initialize"))
			{
			if (yes("confirm (all data will be lost) [y/n] ", 0))
				initialize();
			else
				printf("cancelled\n");
			}
		else if (cmdcmp(cmd,"write"))
			writemini();
		else if (cmdcmp(cmd,"help"))
			helpcmd();
		else if (cmdcmp(cmd,"swap"))
			{
			if (differ == 0)
				warn("secondary directory was identical to primary\n");
			else
				{
				struct minidirectory minidirectory3;
				bcopy(&minidirectory,&minidirectory3, sizeof (struct minidirectory));
				bcopy(&minidirectory2,&minidirectory, sizeof (struct minidirectory));
				bcopy(&minidirectory3,&minidirectory2, sizeof (struct minidirectory));
				first = 0;
				printf("another swap command will get back to the original state\n");
				}
			}
		else
			printf("invalid command (help will list commands)\n");
		}
}

helpcmd()
{
	printf("Commands available are:\n\n");
	printf("change		change minidisk parameters\n");
	printf("create		create new minidisk\n");
	printf("delete		delete minidisk directory\n");
	printf("dir		list minidisk directory\n");
	printf("disk		look at different disk\n");
	printf("help		display short help messages about each command\n");
	printf("initialize	initialize (erase) entire minidisk directory\n");
	printf("list		list minidisk directory\n");
	printf("quit		leave minidisk program\n");
	printf("standard	create standard partitions\n");
	printf("		  where type = \"root\", \"source\", \"single\", \"reduced\", or \"minimal\".\n");
	printf("swap		use secondary minidisk (if primary was bad)\n");
	printf("write		force minidisk directory to disk\n");
	printf("\n");
}

/*
 * create the standard 4.2 minidisk directories 
 */
standard()
{
	char buff[128];
	register int iodn = IODN_BASE;	/* start higher than VRM uses */
	register int size;
	register int total = cylsize;		/* reserve first cylinder */
	register int stdtype = STANDARD;
	register char *p;

	while (*mpromptptr && isspace(*mpromptptr))
		mpromptptr++;
	
	if (*mpromptptr) {
		p=mprompt(0);
		if (cmdcmp(p,"root")) {
			stdtype=ROOT;
		} else if (cmdcmp(p,"source")) {
			stdtype=SOURCE;
		} else if (cmdcmp(p,"single")) {
			stdtype=SINGLE;
		} else if (cmdcmp(p,"reduced")) {
			stdtype=REDUCED;
		} else if (cmdcmp(p,"minimal")) {
			stdtype=MINIMAL;
		} else {
			warn("Unknown standard type: %s\n",p);
		}
	}

	if (unit == 1 || unit == 2)
		iodn += unit * 5;		/* attempt to make unique */
	size = cylsize - START_BLOCK(sectors);
	if (size <= 0) {
		size += cylsize; /* make sure we have enough room */
		total += cylsize;
	}
	if (scsi) {
		/* can't boot from scsi, but reserve cylinder for minidisk */
		sprintf(buff,"create mini %d %d 0",iodn++, size);
		printf("%s\n",buff); mpromptptr = buff+7; create(minidisk);
	} else {
		sprintf(buff,"create boot %d %d ipl",iodn++, size);
		printf("%s\n",buff); mpromptptr = buff+7; create(minidisk);
	}
	switch (stdtype) {
	    case STANDARD:
	    case ROOT:
	    case SOURCE:
	    case REDUCED:
		/* Create "a" partition */
		size = ROUND((stdtype==ROOT) ? 32768 :
			     (stdtype==REDUCED) ? 30720 : 15884, cylsize);
		total += size;
		sprintf(buff,"create %s%xa %d %d 0",type,unit,iodn++,size);
		printf("%s\n",buff); mpromptptr = buff+7; create(minidisk);

		/* Create "b" partition */
		if (stdtype != SOURCE)
		{
			switch (stdtype) {
				case REDUCED:
				case ROOT:
					size=33440;
					break;
				default:
                                	size = (disksize > 355 * 1024 * 2 ) ? 66880 : 
					(disksize > 60 * 1024 * 2) ? 33440 : 10032;
			}

			size = ROUND(size, cylsize);
			total += size;
			sprintf(buff,"create %s%xb %d %d swap",type,unit,iodn++, size);
			printf("%s\n",buff); mpromptptr = buff+7; create(minidisk);
		}

		/* Create "h" partition */
		if (disksize > 205 * 1024 * 2) {
			size = ROUND(291346, cylsize);
			total += size;
			sprintf(buff,"create %s%xh %d %d 0",type,unit,iodn++,size);
			printf("%s\n",buff);mpromptptr=buff+7;create(minidisk);
		}
	    case SINGLE:
		/* amount available (in unit cylinders) */
		/* Create "g" partition */
		size = (minidisk->header.bad_block - total);
		sprintf(buff,"create %s%xg %d %d 0",type,unit,iodn++, size);
		printf("%s\n",buff); mpromptptr = buff+7; create(minidisk);
		break;
	    case MINIMAL:
		/* Create "a" partition */
		if (disksize < 20 * 1024 * 2) {

			/* If less than 20 MEG disk, use 10 MEG */
			size = ROUND(10 * 1024 * 2, cylsize);
		} else {
			/* Otherwise, use half */
			size = ROUND(disksize / 2,cylsize);
		}
		total += size;
		if (disksize-total > 15 * 1024 * 2 ) {
			/* If More than 15 MEG left, then boost the size a little more */
			total -= size;
			size = ROUND(disksize - (15 * 1024 * 2),cylsize);
			total += size;
		}
		sprintf(buff,"create %s%xa %d %d 0",type,unit,iodn++,size);
		printf("%s\n",buff);mpromptptr=buff+7;create(minidisk);

		/* Create "b" partition */
		size = minidisk->header.bad_block - total;
		total += size;
		sprintf(buff,"create %s%xb %d %d 0",type,unit,iodn++,size);
		printf("%s\n",buff);mpromptptr=buff+7;create(minidisk);
	}
}


writemini()
{
	lseek(fd, (long) MINIDISK_BLOCK * BLOCK_SIZE, 0);
	if (write(fd, (char *) minidisk, sizeof (struct minidirectory)) != sizeof (struct minidirectory))
		err("can't write minidisk directory");
	if (ps2esdi && minidisk->header.bad_size == 0)
		lseek(fd, (long) MINIDISK_BLOCK2_PS2ESDI(sectors) * BLOCK_SIZE, 0);
	else
		lseek(fd, (long) MINIDISK_BLOCK2(sectors) * BLOCK_SIZE, 0);
	if (write(fd, (char *) minidisk, sizeof (struct minidirectory)) != sizeof (struct minidirectory))
		err("can't write minidisk directory");
}

#ifdef __STDC__
void warn(char *fmt, ...)
#else
void warn(fmt,d1,d2,d3)
char *fmt;
#endif __STDC__
{
	printf(fmt,((int *) &fmt)[1],((int *) &fmt)[2],((int *) &fmt)[3]);
	printf("\n");
	_longjmp(label);
}

#ifndef SAUTIL
#include "err.c"
#include "utils.c"
#endif

#define PR(x,y,fmt) printf("x=fmt ",minidir->y)
#define PRH(x,fmt) PR(x,header.x,fmt)
prmini(minidir)
struct minidirectory *minidir;
{
	register struct minidisk * disk;
	register int next;
	register int cnt;
	register int badfree = 0;
	char used[MAXDISKS];

	bzero(used,MAXDISKS);	/* clear the flags */
	PRH(number,%d);
	PRH(level,%d);
	PRH(unused,%d);
	PRH(first,%d);
	PRH(last,%d);
	PRH(bad_block,%d);
	PRH(bad_size,%d);
	printf("\n");
	printf("index iodn name           date              start   size type\n\n");
	for (cnt=minidir->header.number, next=minidir->header.first; next >= 0; next=disk->next)
		{
		if (--cnt < 0 || next > MAXDISKS)
			bad_mini();
		disk = &minidir->minidisk[next];
		prdisk(next,disk);
		if (++ used[next] != 1)
			bad_mini();		/* oops - we have a loop */
		if (minidir->header.unused == next)
			++badfree;
		}
	if (next != ENDLIST)
		bad_mini();		/* paranoia */
	if (badfree)
		fixfree(minidir,used);	/* fix up the free list */
}

#ifdef STANDALONE
#define ctime(time) asctime(gmtime(time))
char *asctime();		/* sys/time.h != /usr/sys/time.h, sigh */
#endif

#define _PRD(x,y,fmt) printf("fmt ",disk->y)
#define PRD(x,fmt) _PRD(x,x,fmt)
prdisk(index,disk)
struct minidisk * disk;
{
	printf("%5d ",index);
/*	PRD(previous,%d);	/* */
/*	PRD(next,%d);	/* */
	PRD(iodn,%5d);
	PRD(name,%-5.4s);
	printf("%-24.24s ",disk->date ? ctime(&disk->date) : "");
	PRD(start,%6d);
	PRD(size,%6d);
	PRD(type,%02x);
	prtype(disk->type);
	printf("\n");
}

delete(minidirectory)
struct minidirectory *minidirectory;
{
	struct minidisk *disk;
	char *name;
	name = getname("name ");
	if ((disk = findmini(name,minidirectory)) == 0)
		warn("%s doesn't exist",name);
	if (!yes("confirm minidisk deletion (all data will be lost) [y/n] ", 0))
		{
		printf("cancelled");
		return;
		}
	strncpy(disk->name,"FREE",4);
	disk->iodn = 0;
	disk->type = 0;
	disk->date = 0;
	mergefree(disk,minidirectory);
	if (how != 0) {
		writemini();
	} else {
		printf("READONLY, change not written to disk\n");
	}
}

create(minidirectory)
struct minidirectory *minidirectory;
{
	char *name;
	int iodn, size;
	int type;
	int fix_size;
	struct minidisk *disk;
	name = getname("name ");
	if (findmini(name,minidirectory))
		warn("%s already exists",name);
	iodn = getiodn(minidirectory);
	size = cvtsize(mprompt("size "),sectors,tracksize);
	if (size < 0)
		warn("size (%d) must be positive",size);
	type = gettype(mprompt("type "));
	if ((disk = findfree(minidirectory,size)) == 0)
		warn("can't find %d blocks",size);
	if (size == 0)
		size = disk->size;	/* 0 size means first free partition */
	/*
	 * if a 4.3 partition, and if there's space we adjust the size
	 * to fill up to a cylinder boundary
	 */
	if (IODN_BASE <= iodn && iodn <= IODN_MAX &&
		(fix_size = (disk->start+size)%cylsize) > 0 && 
		(size+cylsize-fix_size) <= disk->size) {
		fix_size = cylsize-fix_size;
		printf("4.3 partition size (%d block%s) increased by %d blocks to fill cylinder\n",
			size, size == 1 ? "" : "s", fix_size);
		size += fix_size;
	}
	if (IODN_BASE <= iodn && iodn <= IODN_MAX &&
		((name[0] == 'h' && name[1] == 'd') ||
		(name[0] == 's' && name[1] == 'c'))) {
		if (!isdigit(name[2]))
			printf("Note: name %s should have a digit after %c%c\n",name,name[0],name[1]);
		if (RESERVED_NAME(name[3]))
			printf("Note: partition %s is reserved; you cannot override it.\n",name);
	}
	disk = splitfree(disk,size);
	time(&disk->date);
	strncpy(disk->name,name,4);
	disk->iodn = iodn;
	disk->type = type;
	if (how != 0) {
		writemini();
	} else {
		printf("READONLY, change not written to disk\n");
	}
}

change(minidirectory)
struct minidirectory *minidirectory;
{
	char *str;
	char *name;
	int iodn;
	int type;
	struct minidisk *disk;
	name = getname("name ");
	if ((disk = findmini(name,minidirectory)) == 0)
		warn("%s doesn't exist",name);
	prdisk(disk-minidisk->minidisk,disk);
	iodn = disk->iodn;
	type = disk->type;
	for (;(str = mprompt("Field [name,iodn,type] ")) && *str;)
		{
		if (cmdcmp(str,"iodn"))
			{
			iodn = getiodn(minidirectory);
			}
		else if (cmdcmp(str,"name"))
			{
			name = getname("name ");
			if (findmini(name,minidirectory))
				warn("%s already exists",name);
			}
		else if (cmdcmp(str,"type"))
			{
			type = gettype(mprompt("type "));
			}
		else if (cmdcmp(str,"quit"))
			break;
		else
			printf("Field not name, iodn, or type\n");
		}
	strncpy(disk->name,name,4);
	disk->iodn = iodn;
	disk->type = type;
	if (how != 0) {
		writemini();
	} else {
		printf("READONLY, change not written to disk\n");
	}
}

/*
 * Print mprompt and wait for reply
 * if the rest of the line already has the reply then just return
 * it.
 */

char *
mprompt(msg)
	char *msg;
{
	static char reply[132];
	register char *p = mpromptptr;
	register char *s;

	if (p)
		{
		while (*p && isspace(*p))
			++p;
		if (*p)
			{
			for (s=p; *s && !isspace(*s); ++s)
				;
			mpromptptr = s;
			return(p);
			}
		}
	printf("%s     \b\b\b\b\b", msg);
	if (gets(reply) == 0)
		return(0);
	for (s=reply; *s && !isspace(*s); ++s)
		;
	if (isspace(*s))
		*s++= 0;		/* delimit the word */
	mpromptptr = s;
	return (reply);
}


struct minidisk *findmini(name,minidir)
char *name;
struct minidirectory *minidir;
{
	register struct minidisk * disk;
	register int next;
	register int cnt;
	for (cnt=minidir->header.number, next=minidir->header.first; next >= 0; next=disk->next)
		{
		if (--cnt < 0 || next > MAXDISKS)
			bad_mini();
		disk = &minidir->minidisk[next];
		if (strncmp(name,disk->name,sizeof disk->name) == 0)	
			return(disk);
		}
	return(0);		/* didn't find it */
}


/*
 * find a minidisk with the given iodn 
 */
struct minidisk *findiodn(iodn,minidir)
struct minidirectory *minidir;
{
	register struct minidisk * disk;
	register int next;
	register int cnt;
	for (cnt=minidir->header.number, next=minidir->header.first; next >= 0; next=disk->next)
		{
		if (--cnt < 0 || next > MAXDISKS)
			bad_mini();
		disk = &minidir->minidisk[next];
		if (disk->iodn == iodn)
			return(disk);
		}
	return(0);		/* didn't find it */
}

/*
 * find free minidisk with at least "size" blocks free.
 * look first for exact fit, then first fit.
 */
struct minidisk *findfree(minidir,size)
struct minidirectory *minidir;
{
	register struct minidisk * disk;
	register int next;
	register int cnt;
	for (cnt=minidir->header.number, next=minidir->header.first; next >= 0; next=disk->next)
		{
		if (--cnt < 0 || next > MAXDISKS)
			bad_mini();
		disk = &minidir->minidisk[next];
		if (ISFREE(disk) && disk->size == size)
			return(disk);
		}
	for (cnt=minidir->header.number, next=minidir->header.first; next >= 0; next=disk->next)
		{
		if (--cnt < 0 || next > MAXDISKS)
			bad_mini();
		disk = &minidir->minidisk[next];
		if (ISFREE(disk) && disk->size >= size)
			return(disk);
		}
	return(0);		/* didn't find it */
}

/*
 * split existing disk into two (if not exactly the right size)
 * and create two minidisks dividing up the space. 
 * return pointer to the disk of the right size.
 * new free disk is inserted after the existing free disk.
 */
struct minidisk *splitfree(disk,size)
	register struct minidisk * disk;
{
	register struct minidisk * newdisk;
	register int next;
	int index_disk, index_newdisk;
	if (disk->size < size)
		err("can't splitfree %d into %d",disk->size,size);
	if (disk->size == size)
		return(disk);		/* that was easy! */
	next = minidisk->header.unused;
	if (next < 0 || next >= MAXDISKS)
		err("no more slots available");
	minidisk->header.number++;		/* bump count */
	newdisk = &minidisk->minidisk[next];
	minidisk->header.unused = newdisk->nextfree;	/* get next free */
	*newdisk = *disk;				/* copy everything */
	disk->size = size;
	newdisk->size -= size;
	newdisk->start += size;
	index_disk = disk-minidisk->minidisk;
	index_newdisk = newdisk-minidisk->minidisk;
	/* link into the list */
	newdisk->previous = index_disk;
	disk->next = index_newdisk;
	if (minidisk->header.last == index_disk)
		minidisk->header.last = index_newdisk;
	return(disk);
}

char *
getname(str) char *str;
{
	char *name = mprompt(str);
	register char *s, *q;
	static char namestr[5];		/* place to put the name */

	for (s=name, q=namestr; *s && s < name+4; ++s)
		*q++ = *s;
	for (;q < namestr+4; ++q)
		*q = ' ';
	*q = 0;
	return(namestr);
}

char *types[8] = { "ipl", "vrm", "pc", "aix", "file", "swap", "nobad", "writeverify" };

prtype(type)
register int type;
{
	register int i;
	register int cnt=0;

	for (i=0; i<8; ++i)
		if (type&(1<<i))
			printf("%s%s",(cnt++ == 0) ? "" : ",", types[i]);
}


#define inside(a,b,c) ((a)<=(b) && (b)<(c))

/*
 * look for adjacent free space and colapse it together
 */
mergefree(freedisk,minidir)
register struct minidisk * freedisk;
struct minidirectory *minidir;
{
	register struct minidisk * disk;
	register int next;
	register int cnt;
retry:
	for (cnt=minidir->header.number, next=minidir->header.first; next >= 0; next=disk->next)
		{
		if (--cnt < 0 || next > MAXDISKS)
			bad_mini();
		disk = &minidir->minidisk[next];
		if (disk == freedisk || !ISFREE(disk))
			continue;
		if (freedisk->start+freedisk->size == disk->start)
			{
			freedisk->size += disk->size;
			unlink_disk(disk,minidir);
			goto retry;
			}
		if (disk->start+disk->size == freedisk->start)
			{
			freedisk->size += disk->size;
			freedisk->start = disk->start;
			unlink_disk(disk,minidir);
			goto retry;
			}
		if (inside(disk->start,freedisk->start,disk->start+disk->size) ||
			inside(disk->start,freedisk->start,disk->start+disk->size)) 
			err("disks overlap");
		}
	return(0);		/* didn't find it */
}


/*
 * unlink minidisk "disk" by making the next (or first) pointer point
 * to our next.
 * make previous disk point to our previous.
 */
unlink_disk(disk,minidir)
register struct minidisk * disk;
struct minidirectory *minidir;
{
	register int index_disk;

	if (disk->previous < 0)
		minidir->header.first = disk->next;
	else
		minidir->minidisk[disk->previous].next = disk->next;
	if (disk->next < 0)
		minidir->header.last = disk->previous;
	else
		minidir->minidisk[disk->next].previous = disk->previous;
	index_disk = disk-minidir->minidisk;
	bzero(disk, sizeof (struct minidisk));
	disk->nextfree = minidir->header.unused;	/* link into free list */
	minidir->header.unused = index_disk;		/* now first on list */
	--minidir->header.number;		/* reduce count of disks */
}

/*
 * set the minidisk directory to one free disk that includes
 * the entire disk.
 */
initialize()
{
	register struct minidisk * disk;
	register int size = disksize;		/* size of the disk */
	register int i;

	bzero(minidisk, sizeof (struct minidirectory));
	minidisk->header.number = 1;
	minidisk->header.first = 0;
	minidisk->header.last = 0;
	minidisk->header.level = 1;		/* its not clear if it makes any difference */
	if (ps2esdi)
		minidisk->header.bad_size = 0;	/* don't need it */
	else
		minidisk->header.bad_size = MAXBADBLKS;
	for (i=0, disk = minidisk->minidisk; i<MAXDISKS-1; ++i, ++disk)
		disk->nextfree = i+1;		/* linked list? */
	disk->nextfree = ENDLIST;			/* linked list? */
	disk = minidisk->minidisk;
	disk->iodn = 0;				/* mark as free */
	if (ps2esdi)
		disk->start = START_BLOCK_PS2ESDI(sectors);
	else
		disk->start = START_BLOCK(sectors);
	disk->size = size - disk->start - minidisk->header.bad_size;
	disk->next = ENDLIST;
	disk->previous = ENDLIST;
	minidisk->header.bad_block = size - minidisk->header.bad_size;
	bcopy("FREE",disk->name,4);		/* make it named FREE */
	minidisk->header.unused = 1;		/* free after first */
	if (how != 0) {
		writemini();
	} else {
		printf("READONLY, change not written to disk\n");
	}
}

/*
 * get disk and cylinder sizes
 */

#ifdef STANDALONE
getdisksize()
{
	struct hdconfig hdconfig;

	lseek(fd, CONFIG_BLOCK * BLOCK_SIZE, 0);
	if (read(fd, (char *) &hdconfig, sizeof hdconfig) != sizeof hdconfig)
		err("could not read configuration record");
	sectors = hdconfig.conf_lastsect;
	tracksize = (hdconfig.conf_lasttrack+1);
	cylsize = tracksize *  sectors;
	disksize = hdconfig.conf_sectorcount;
	scsi = FALSE;
	ps2esdi = strcmp(hdconfig.conf_name,"hdpses") == 0;
	type[0]='h'; type[1]='d'; type[2] = 0;
}
# else
/*
 * SCSI's don't have configuration records, but in usr mode we can ask the 
 * driver about the disk.
 */
getdisksize()
{
	struct	dkpart dk;

	if (ioctl(fd, DKIOCGPART, &dk) < 0) {
		err("could not read configuration info (DKIOCPART failed)");
	}
	sectors = dk.dk_nsector;
	tracksize = dk.dk_ntrack;
	cylsize = tracksize *  sectors;
	if (strncmp(dk.dk_name,"scsi",4) == 0 ) {
		scsi = TRUE;
		ps2esdi = FALSE;
	} else {
		scsi = FALSE;
		ps2esdi = strcmp(dk.dk_name,"hdpses") == 0;
		if (!ps2esdi)
			dk.dk_ncyl -= 1;	/* avoid using CE cylinder */
	}
	disksize = dk.dk_ncyl * cylsize; /* hd report smaller size (?!) */
	type[0]=dk.dk_name[0];
	type[1]=dk.dk_name[1];
	type[2] = 0;
}
#endif

bad_mini()
{
	printf("minidisk directory corrupted\n");
	if (iflg || yes("re-initialize minidisk directory [y/n] ", 0))
		initialize();
	warn("");		/* cancel current command */
}

gettype(str)
register char *str;
{
	register int type;
	register int i;

	if (*str == 0)
		return(0);
	
	for  (type=0; *str; )
		{
		for (i=0; i<8; ++i)
			if (strcmp(str,types[i]) == 0)
				break;
		if (i < 8)
			{
			type |= 1<<i;
			str += strlen(types[i]);
			}
		else
			{
			if (isxdigit(*str))
				{
				type |= atox(str);
				while (isxdigit(*str))
					++str;
				if (*str && *str != ',')
					warn("invalid hex number: %s",str);
				}
			else
				warn("%s: invalid type",str);
			}
		if (*str == ',')
			++str;
		}
	return(type);
}

#ifndef STANDALONE

/*
 * 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);
}
#endif STANDALONE

getiodn(minidirectory)
struct minidirectory *minidirectory;
{	
	register int iodn;

	for (;;)
		{
		iodn = atoi(mprompt("iodn "));
		if (iodn < IODN_BASE  || iodn > IODN_MAX)
			{
			printf("warning: 4.3 iodns must be in range %d ... %d\n",IODN_BASE,IODN_MAX);
			if (yes("Change the iodn? [y/n] ", 0))
				{
				mpromptptr = 0;
				continue;
				}
			}

		if (findiodn(iodn,minidirectory))
			{
			printf("iodn %d already exists\n",iodn);
			continue;
			}
		return(iodn);
		}
}


/*
 * determine if the entire buffer is zero
 */
iszero(buff,length)
register char *buff;
register int length;
{
	while (--length >= 0)
		if (*buff++)
			return(0);
	return(1);
}

/*
 * we've noticed that the free list is corrupted - fix it up to be
 * compatible with previous level which used different free list
 * format due to inadequate documentation.
 */
fixfree(minidir,used)
struct minidirectory *minidir;
char *used;
{
	register struct minidisk * disk;
	register int unused = ENDLIST;
	register int cnt = 0;
	register int i;

	for (i=MAXDISKS; --i>=0; )
		if (used[i] == 0)
			{
			disk = &minidir->minidisk[i];
			disk->nextfree = unused;
			unused = i;
			++cnt;
			}
	minidir->header.unused = unused;
	printf("(free list has been reorganized; %d slots free)\n",cnt);
}

cvtsize(str,ntpc,nspt)
char *str;
{
	int n, factor;
	int cyl = ntpc * nspt;	/* cylinder size */
	int c;

	if (str == 0)
		return(0);
	n = atoi(str);
	if (*str == '+' || *str == '-')
		++str;			/* skip sign */
	while (isdigit(*str))
		++str;			/* skip over digits */
	c = *str;
	if (isupper(c))
		c = tolower(c);
	switch(c)
		{
	case 'c':
		factor = cyl;	/* conversion factor is cylinders */
		break;
	case 't':
		factor = nspt;
		break;
	case 'k':
		factor = 2;	/* 2 blocks/k */
		break;
	default:
		printf("warning: unknown conversion factor %s (not m,k,b,t or c) - assuming blocks\n", str);
	case 0:
	case ' ':
	case 'b':
		factor = 1;	/* in blocks */
		break;
	case 'm':
		factor = 1024 * 2;	/* in megs */
		break;
		}
	n *= factor;
	return(n);
}
