Ultrix-3.1/sys/distr/setup.c

Compare this file to the similar file:
Show the results in this format:

static char Sccsid[] = "@(#)setup.c	3.3	10/14/87";

/**********************************************************************
 *   Copyright (c) Digital Equipment Corporation 1984, 1985, 1986.    *
 *   All Rights Reserved. 					      *
 *   Reference "/usr/src/COPYRIGHT" for applicable restrictions.      *
 **********************************************************************/

/*
 * TODO:
 *
 * [n]	Need lp, mem, null, kmem, console, du & other oddballs!
 *
 * [n]	Convert all error messages to use perror()!!!!!!
 *
 * [n]	When to remove the BOOT floppy?
 *
 * [n]	Look at all fatal error exits (see if they could be warnings).
 *
 * [NO]	Chmod 400 /dev/??07 and /dev/r??07 (except RA) -- DOES NO GOOD!
 *
 * [n]	Test RP02/3 by configuring driver and faking drives present!
 *
 * [n]	Test interruptablity of each setup phase.
 *
 * [y]	Mount /usr/once, dismount on exit (need usr mnt flag for error).
 *
 * [y]	Write setup info back to setup.info if user enters it.
 *
 * [NO]	Print warning: do not remove setup.info, setup, etc. files.
 *	Handle in installation guide.
 *
 * [n]	Test hz, timezone, dstflag loading in /dev/mem and /unix.
 *	tested ok on 0431 kernel, not 0430.
 *
 * [n]	Must be in single-user mode!
 *
 * [y]	Need to replace ??unixes with a generic kernel.
 *
 * [y]	Need new disk partitions and standard root/swap/elog layouts.
 *
 * [y]	Handle case where target cpu not current cpu, split I/D space
 *	or not is the big issue.
 *
 * [y]	Save sid/* and nsid/* if target cpu not current cpu. -- TESTED
 *
 * [n]	Need TK50 supported as load device.
 *
 * [n]	Convert rxunit to rxflag (like setup53).
 *
 * [y]	Blast fstab.
 *
 * [n]	Make setup restartable to do phase 2 (setup.info).
 */
/*
 *
 *  File name:
 *
 *	setup.c
 *
 *  Source file description:
 *
 *	ULTRIX-11 initial setup program.
 *
 *	TBS, when I figger it out!
 *	need comment format of setup.info file
 *
 *		multiple versions, RX, MT, others????
 *		do more work for users
 *		even more on Micro/PDP-11s
 *
 *  Functions:
 *
 *	main()		The main is the main!
 *
 *	pmsg()		Prints a message from an array of character pointers.
 *
 *	yes()		Return YES, NO, HELP, depending on how the user
 *			answered a question.
 *
 *	intr()		Handles interrupt signal (<CTRL/C>).
 *
 *	retry()		Ask the user if a failed function should be retried.
 *
 *	iflop()		Instruct the user to insert the named floppy into
 *			one of the floppy disk drives.
 *
 *	rflop()		Instruct the user to remove the named floppy from
 *			one of the floppy disk drives.
 *
 *	prtc()		Handles "Press <RETURN> to continue: "
 *
 *	OTHERS?
 *
 *  Usage:
 *
 *	cd /.setup; setup
 *
 * 	The setup and setup.ifno files must not be removed.
 *	The above commands are executed by the root .profile when the
 *	system is first booted, after loading by sdload. The root .profile
 *	is replaced after setup has completed its work, and the setup
 *	program is removed. This is so that all subsequent boots be normal.
 *
 *  Compile:
 *
 *	cd /usr/sys/distr; make setup
 *
 *  Modification History:
 *
 *	18 January 1984
 *		File created -- Fred Canter
 *
 *	?? ??????? 198?
 *		Time passes -- Fred Canter
 *		Many changes, see sccs prt setup.c setup53.c
 *
 *	13 March 1985
 *		Version 2.1 -- Fred Canter
 *		Combined setup.c and setup53.c to form the new setup.c.
 *		Many changes for new kit layout and installation flow.
 *		Added setup_osl to handle loading optional software.
 *
 *	12 May 1985
 *		Version 2.2 -- Fred Canter
 *		Clean up setup and finalize installation flow.
 *		Take advantage of magtape in generic kernel.
 *		Handle install on other than target processor.
 *		Added #ifdef DEBUG.
 *	TODO: more?
 *
 */

#include <stdio.h>
#include <a.out.h>
#include <signal.h>
#include <setjmp.h>
#include <fstab.h>
#include <sys/param.h>
#include <sys/dir.h>
#include <sys/stat.h>
#include <sys/filsys.h>
#include <sys/devmaj.h>
#include <sys/conf.h>
#include <sys/ra_info.h>
#include <sys/hp_info.h>
#include <sys/tk_info.h>
#include <sys/time.h>

/*	#define	DEBUG	1	/* debug flag: turn on debug code */

jmp_buf savej;

struct	nlist	nl[] =
{
	{ "_cputype" },
#define			X_CPUTYPE	0
	{ "_hz" },
#define			X_HZ		1
	{ "_timezon" },
#define			X_TIMEZONE	2
	{ "_dstflag" },
#define			X_DSTFLAG	3
	{ "_nrk" },
#define			X_NRK		4
	{ "_rk_dt" },
#define			X_RK_DT		5
	{ "_nrp" },
#define			X_NRP		6
	{ "_rp_dt" },
#define			X_RP_DT		7
	{ "_nuda" },
#define			X_NUDA		8
	{ "_nra" },
#define			X_NRA		9
	{ "_ra_inde" },
#define			X_RA_INDEX	10
	{ "_ra_ctid" },
#define			X_RA_CTID	11
	{ "_ra_drv" },
#define			X_RA_DRV	12
	{ "_nrl" },
#define			X_NRL		13
	{ "_rl_dt" },
#define			X_RL_DT		14
	{ "_nhp" },
#define			X_NHP		15
	{ "_hp_inde" },
#define			X_HP_INDEX	16
	{ "_hp_dt" },
#define			X_HP_DT		17
	{ "_nhs" },
#define			X_NHS		18
	{ "_hs_dt" },
#define			X_HS_DT		19
	{ "_nhk" },
#define			X_NHK		20
	{ "_hk_dt" },
#define			X_HK_DT		21
	{ "_nkl11" },
#define			X_NKL11		22
	{ "_ndl11" },
#define			X_NDL11		23
	{ "_ndh11" },
#define			X_NDH11		24
	{ "_nuh11" },
#define			X_NUH11		25
	{ "_dz_cnt" },
#define			X_DZ_CNT	26
	{ "_ntty" },
#define			X_NTTY		27
	{ "_ra_mas" },
#define			X_RA_MAS	28
	{ "_ud_size" },
#define			X_UD_SIZES	29
	{ "_rq_size" },
#define			X_RQ_SIZES	30
	{ "_rc_size" },
#define			X_RC_SIZES	31
	{ "_hp_size" },
#define			X_HP_SIZES	32
	{ "_hk_size" },
#define			X_HK_SIZES	33
	{ "_rp_size" },
#define			X_RP_SIZES	34
	{ "_bdevsw" },
#define			X_BDEVSW	35
	{ "_nht" },
#define			X_NHT		36
	{ "_nts" },
#define			X_NTS		37
	{ "_ntm" },
#define			X_NTM		38
	{ "_ntk", },
#define			X_NTK		39
	{ "_tk_ctid" },
#define			X_TK_CTID	40
	{ "_npty" },
#define			X_NPTY		41
	{ "_nmausen" },
#define			X_NMAUSENT	42
	{ "" },
};

/* Structure for Daylight Savings Time information */
struct	dst_table {
	char	*dst_area;	/* Geographical area */
	int	dst_id;		/* Numeric id */
}  dst_table[] = {
	"USA",DST_USA,
	"Australia",DST_AUST,
	"Western Europe",DST_WET,
	"Central Europe",DST_MET,
	"Eastern Europe",DST_EET,
	0
};

#define	YES	1
#define	NO	0
#define	ERR	1
#define	NOERR	0
#define	HELP	2
#define	NOHELP	0
#define	MOUNT	1
#define	UMOUNT	0
#define	FATAL	1
#define	ABORT	2
#define	MSF	0
#define	MKFS	1
#define	BLOCK	0
#define	RAW	1
#define	FST_SCH	0
#define	FST_ENT	1
#define	FST_RMV	2
#define	CONSOLE	1
#define	OTHER	0


#define	LBSIZE	50
char	lbuf[LBSIZE];
#define	SCSIZE	512	/* CAUTION: 512 min size (doubles as disk read buf) */
char	syscmd[SCSIZE];
char	sphase[15];
char	sdname[15];
char	tproc[15];
char	loadev[20];
char	ldunit[10];
char	*info = "/.setup/setup.info";
char	*osload = "/bin/osload";
char	*rclock = "/.setup/rootcmd.lock";
char	*uclock = "/.setup/usrcmd.lock";
char	*homedir = "/.setup";
char	*lpfdir = "lost+found";
char	*confirm = "\nPLEASE CONFIRM: ";
char	*dsk_sfn = "/dev/setup.dsf";
char	*debug = "\nDEBUG: ";

int	firstfs = YES;	/* making very first file system flag */
int	sdtype;		/* Index into sd_info[] entry for system disk */
int	sdcntlr = 0;	/* System disk on controller number */
			/* TODO: 0 for now, how or if to set this? */
int	sdunit;		/* System disk unit number */
			/* TODO: RUX50 - unit could be 0 ---|	*/
int	rxflag;		/* Unit # if load device is floppy, 0 if not */
int	rxunit;
int	rd2 = NO;	/* YES if second RD disk present (changes RX unit #s) */
int	mtflag;		/* Load device is magtape (unit number) */
int	tkflag;		/* set if magtape is really a TK50 */
int	rlflag;		/* load device is RL02 */
int	rcflag;		/* load device is RC25 */

/*
 * System's disk info structure.
 *
 * Disk drive type ID definitions (must be unique):
 */
/* ra_info: RX33 RX50 RD31 RD32 RD51 RD52 RD53 RD54 RC25 RA60 RA80 RA81 */
/* hp_info: RP04 RP05 RP06 RM02 RM03 RM05 ML11 */
#define	RK05	5
#define	RX02	1
#define	RS03	8
#define	RS04	9
#define	RL01	10240
#define	RL02	20480
#define	RK06	0
#define	RK07	02000
#define	RP02	2
#define	RP03	3

#define	SD_SYSDSK	1	/* This is the system disk */
#define	SD_WINIE	2	/* Disk is a winchester (can be system disk) */
#define	SD_USRDSK	4	/* Disk has non-system partition(s) [/user] */
#define	SD_FLOPPY	010	/* Floppy disk (rx33 or rx50) */
#define	SD_SMALL	020	/* SMALL ROOT (rm -rf /sas to make room) */
#define	SD_ML11		040	/* ML11 (no setup user file systems) */

struct sd_info {
	char	*sd_lname;	/* disk type name in lowercase */
	char	*sd_uname;	/* disk type name in uppercase */
	char	*sd_swap;	/* /dev/swap link (0 if can't be system disk) */
	int	sd_type;	/* numeric disk type */
	char	sd_pmask;	/* BIT WISE -- disk partition usage mask */
	char	sd_smask;	/* BIT WISE -- partitions taken by system */
	char	sd_flags;	/* flags is flags */
} sd_info[] = {
	/* DUMMY ENTRY: must be here, see di_info.di_dt[] */
	"",	"",	0,	-1,	0,	0,	0,
	"rx02",	"RX02",	0,	RX02,	0,	0,	SD_FLOPPY,
	"rk05",	"RK05",	0,	RK05,	0,	0,	0,
	"ml11",	"ML11",	0,	ML11,	0,	0,	SD_ML11,
	"rx33",	"RX33",	0,	RX33,	0200,	0,	SD_FLOPPY,
	"rx50",	"RX50",	0,	RX50,	0200,	0,	SD_FLOPPY,
	"rd31",	"RD31",	"rd05",	RD31,	0341,	0141,	SD_WINIE,
	"rd32",	"RD32",	"rd02",	RD32,	0217,	0007,	(SD_WINIE|SD_USRDSK),
	"rd51",	"RD51",	"rd00",	RD51,	0221,	0021,	(SD_WINIE|SD_SMALL),
	"rd52",	"RD52",	"rd02",	RD52,	0217,	0007,	(SD_WINIE|SD_USRDSK),
	"rd53",	"RD53",	"rd02",	RD53,	0217,	0007,	(SD_WINIE|SD_USRDSK),
	"rd54",	"RD54",	"rd02",	RD54,	0217,	0007,	(SD_WINIE|SD_USRDSK),
	"rl01", "RL01", "rl00",	RL01,	0201,	0001,	SD_SMALL,
	"rl02", "RL02", "rl00",	RL02,	0203,	0003,	SD_SMALL,
	"rk06", "RK06", "hk01",	RK06,	0107,	0007,	SD_SMALL,
	"rk07", "RK07", "hk01",	RK07,	0213,	0013,	(SD_USRDSK|SD_SMALL),
	"rp02", "RP02", "rp01",	RP02,	0107,	0007,	SD_USRDSK,
	"rp03", "RP03", "rp01",	RP03,	0213,	0013,	SD_USRDSK,
	"rp04", "RP04", "hp02",	RP04,	0117,	0007,	SD_USRDSK,
	"rp05", "RP05", "hp02",	RP05,	0117,	0007,	SD_USRDSK,
	"rp06", "RP06", "hp02",	RP06,	0377,	0007,	SD_USRDSK,
	"rm02", "RM02", "hp02",	RM02,	0217,	0007,	SD_USRDSK,
	"rm03", "RM03", "hp02",	RM03,	0217,	0007,	SD_USRDSK,
	"rm05", "RM05", "hp02",	RM05,	0377,	0007,	SD_USRDSK,
	"ra60", "RA60", "ra02",	RA60,	0277,	0007,	SD_USRDSK,
	"ra80", "RA80", "ra02",	RA80,	0217,	0007,	SD_USRDSK,
	"ra81", "RA81", "ra02",	RA81,	0377,	0007,	SD_USRDSK,
	"rc25", "RC25", "rc11",	RC25,	0237,	0007,	SD_USRDSK,
	0
};

/*
 * File system table (/etc/fstab) information.
 * Used to blast the initial fstab.
 */

struct fs_info {
	char	*fs_lname;	/* lowercase disk name (same as sd_info) */
	char	*fs_root;	/* disk to mount on root (/) */
	char	*fs_usr;	/* disk to mount on /usr */
} fs_info[] = {
	"rd31",	"rd00",	"rd06",
	"rd32",	"rd00",	"rd01",
	"rd51",	"rd00",	"rd04",
	"rd52",	"rd00",	"rd01",
	"rd53",	"rd00",	"rd01",
	"rd54",	"rd00",	"rd01",
	"rl01",	"rl00",	"rl10",
	"rl02",	"rl00",	"rl01",
	"rk06",	"hk00",	"hk02",
	"rk07",	"hk00",	"hk03",
	"rp02",	"rp00",	"rp02",
	"rp03",	"rp00",	"rp03",
	"rp04",	"hp00",	"hp01",
	"rp05",	"hp00",	"hp01",
	"rp06",	"hp00",	"hp01",
	"rm02",	"hp00",	"hp01",
	"rm03",	"hp00",	"hp01",
	"rm05",	"hp00",	"hp01",
	"ra60",	"ra00",	"ra01",
	"ra80",	"ra00",	"ra01",
	"ra81",	"ra00",	"ra01",
	"rc25",	"rc10",	"rc12",
	0
};

/*
 * Disk controller and drive info table.
 * Loaded with controller type and type of each
 * configured disk. Used for making the fstab,
 * special files, and file systems.
 */

#define	DI_MSCP	01	/* MSCP controller flag */
#define	DI_MASS	02	/* MASSBUS controller flag */
#define	DI_NPD	04	/* Non-partitioned disk flag */
#define	DI_HX	010	/* RX02 special case flag */
#define	DI_RS	020	/* RS03/4 specical case flag */

int	rk_gdti(), rp_gdti(), ra_gdti(), rl_gdti();
int	hx_gdti(), hp_gdti(), hs_gdti(), hk_gdti();

struct	di_info {
	char	*di_typ;	/* Controller type (name string) */
	char	*di_name;	/* Two char ULTRIX-11 mnemonic */
				/* ("ra" changed on the fly to ra|rc|rd|rx) */
	char	di_cn;		/* Controller number (if massbus or mscp) */
	char	di_nunit;	/* Number units (0 if cntlr not configured) */
	char	di_bmaj;	/* Block major device number */
	char	di_rmaj;	/* Raw major device number */
	int	(*di_func)();	/* Call this function to get drive type info */
	int	di_flags;	/* Flags (massbus, mscp, NP disk, etc.) */
	int	di_dt[8];	/* Each unit's type (index into sd_info[]) */
				/* Zero indicates non existent drive */
} di_info[] = {
  { "RK11",	"rk", 0, 0, RK_BMAJ, RK_RMAJ, rk_gdti, DI_NPD },
  { "RP11",	"rp", 0, 0, RP_BMAJ, RP_RMAJ, rp_gdti, 0 },
  { "MSCP",	"ra", 0, 0, RA_BMAJ, RA_RMAJ, ra_gdti, DI_MSCP },
  { "MSCP",	"ra", 1, 0, RA_BMAJ, RA_RMAJ, ra_gdti, DI_MSCP },
  { "MSCP",	"ra", 2, 0, RA_BMAJ, RA_RMAJ, ra_gdti, DI_MSCP },
  { "MSCP",	"ra", 3, 0, RA_BMAJ, RA_RMAJ, ra_gdti, DI_MSCP },
  { "RL11",	"rl", 0, 0, RL_BMAJ, RL_RMAJ, rl_gdti, 0 },
  { "RX11",	"hx", 0, 0, HX_BMAJ, HX_RMAJ, hx_gdti, DI_HX },
  { "MASSBUS",	"hp", 0, 0, HP_BMAJ, HP_RMAJ, hp_gdti, DI_MASS },
  { "MASSBUS",	"hm", 1, 0, HM_BMAJ, HM_RMAJ, hp_gdti, DI_MASS },
  { "MASSBUS",	"hj", 2, 0, HJ_BMAJ, HJ_RMAJ, hp_gdti, DI_MASS },
  { "RK611",	"hk", 0, 0, HK_BMAJ, HK_RMAJ, hk_gdti, 0 },
  { 0 },
};

/*
 * Number of 512 byte blocks per cylinder
 * for each type of disk.
 * Not affected by 1K block file system.
 */

#define	RP23_BPC	200	/* RP02/3 */
#define	RP456_BPC	418	/* RP04/5/6 */
#define	RM23_BPC	160	/* RM02/3 */
#define	RM5_BPC		608	/* RM05 */
#define	RK67_BPC	66	/* RK06/7 */

/*
 * Disk partition info table.
 * Used to guide user thru disk partition selection.
 */

#define	PT_USER	1	/* partition has file system on it already */
#define	PT_SYS	2	/* partition used by the system */
#define	PT_NUP	4	/* non usable (disk doesn't use this partition) */
#define	PT_OP	010	/* not usable (overlapped by a used partition) */

struct	pt_info {
	daddr_t	pt_sb;		/* starting block number (1KB logical block) */
	daddr_t	pt_nb;		/* length in 1KB logical blocks */
	char	pt_op;		/* BIT WISE -- overlapping partitions */
	int	pt_flags;	/* special info flags */
} pt_info[8];

/*
 * Misc. stuff for setup of user file systems.
 */

int	ufsnum;		/* running count of # user file systems */
char	fsname[20];	/* superblock fsname (also mounted on directory name) */
char	volname[20];	/* superblock volname */
char	mntdir[20];	/* mounted on directory (/user#) */
char	fmntdir[20];	/* possible alternate mounted on directory */
char	bdisk[20];	/* block special file name (/dev/??##) */
char	rdisk[20];	/* raw special file name (/dev/r??##) */

/*
 * Kernel data structures needed to determine
 * disk controller and drive configuration.
 */

struct	dksize {
	daddr_t	nblocks;	/* partition length - 512 byte blocks (sectors) */
	int	cyloff;		/* partition starting cylinder number */
};

struct	bdevsw	bdevswb;

/*
 * RK05
 *	NED = -1
 *	RK05 = 1
 */
int	nrk;
char	rk_dt[8];
int	rk_size = 4872;

/*
 * RP02/3
 *	NED = -1
 *	RP02 = 0
 *	RP03 = 1
 * Note: must read each configured RP02/3 unit to
 *	 force update of rp_dt[].
 */
int	nrp;
char	rp_dt[8];
struct	dksize	rp_sizes[8];

/*
 * MSCP (UDA50 RC25 RQDX RUX50)
 *	NED = 0
 *	See /usr/include/sys/ra_info.h for drive types.
 *
 * Note: MSCP drive type info arrays are non-semetrical
 *	 ra_index must be used to access drive type info.
 */

int	nuda;		/* Number of configured MSCP controllers */
char	nra[MAXUDA];	/* Number of units configured on each controller */
char	ra_ctid[MAXUDA];/* Controller type ID and micro-code rev level */
char	ra_index[MAXUDA];/* Index into drive type info ra_drv[] */
struct	ra_drv ra_drv[MAXUDA*8];	/* RA drive info (see ra_info.h) */
char	rq_dt[8];	/* RD/RX disk drive types for iflop() & rflop() */
			/* Set up by ra_gdti() */

daddr_t	ra_mas[MAXUDA];		/* Size of maintenance area */
struct	rasize	ud_sizes[8];	/* UDA50 partition sizes */
struct	rasize	rc_sizes[8];	/* RC25 partition sizes */
struct	rasize	rq_sizes[8];	/* RQDX partition sizes */

/*
 * RL01/2
 *	NED = -1
 *	RL01 = 10240
 *	RL02 = 20480
 */
int	nrl;
int	rl_dt[4];
/* SIZE: drive type = size (RL01=10240, RL02=20480) */

/*
 * MASSBUS (HP HM HJ)
 *	NED = 0
 *	See /usr/include/sys/hp_info.h for drive types.
 *
 * Note: MASSBUS drive type info arrays are non-semetrical
 *	 hp_index must be used to access drive type info.
 */

char	hp_index[MAXRH];	/* Index into drive type info arrays */
char	nhp[MAXRH];		/* Number units per cntlr (0 = not config) */
char	hp_dt[MAXRH*8];		/* Drive type of each drive */
struct	dksize	hp_sizes[32];	/* disk partition sizes */

/*
 * RK06/7
 *	NED = -1
 *	RK06 = 0
 *	RK07 = 02000
 */
int	nhk;
int	hk_dt[8];
struct	dksize	hk_sizes[8];

/*
 * Kernel data symbols used to determine number
 * and type of magtape controllers configured.
 */

int	nht;		/* HT - TM02/3 */
int	nts;		/* TS - TS11/TU80/TSV05/TK25 (TK25 no longer supported) */
int	ntm;		/* TM - TM11 */
int	ntk;		/* TK - TK50/TU81 */
char	tk_ctid[MAXTK];	/* TK - TU81/TK50 */

/*
 * Kernel data symbols used to find comm. devices.
 */

int	ntty;		/* Total number of TTY structures */
int	nttys;		/* Number of TTY structures used so far */
int	nkl11;		/* Number of KL lines (including console) */
int	ndl11;		/* Number of DL lines */
int	ndh11;		/* Number of DH lines */
int	nuh11;		/* Number of DHU/DHV lines */
int	dz_cnt;		/* Number of DZ/DZV/DZQ lines */
char	*dl_name;	/* Set to DL11 or DLV11 */
char	*uh_name;	/* Set to DHU11 or DHV11 */
char	*dz_name;	/* Set to DZ11 or DZV11 (DZQ11 is out of luck) */
int	dz_lpu;		/* DZ/DZV lines per unit */
int	uh_lpu;		/* DHU/DHV lines per unit */
int	dh_lpu = 16;	/* DH lines per unit */

/*
 * Number of pseudo ttys and
 * maus segments.
 */

int	npty;
int	nmausent;

/*
 * Supported processor table.
 */
#define	NSID	0
#define	SID	1
#define	QBUS	0
#define	UBUS	1
int	cpi;			/* current processor index into cputyp[] */
int	tpi;			/* target processor index into cputyp[] */
int	ontarget;		/* on the target processor ? */
struct cputyp {
	int	p_type;		/* processor type */
	int	p_sid;		/* separate I & D space ? */
	int	p_bus;		/* Qbus or Unibus */
} cputyp[] = {
	23,	NSID,	QBUS,
	24,	NSID,	UBUS,
	34,	NSID,	UBUS,
	40,	NSID,	UBUS,
	44,	SID,	UBUS,
	45,	SID,	UBUS,
	53,	SID,	QBUS,
	55,	SID,	UBUS,
	60,	NSID,	UBUS,
	70,	SID,	UBUS,
	73,	SID,	QBUS,
	83,	SID,	QBUS,
	84,	SID,	UBUS,
	0
};
int	cputype;	/* current processor type */
int	hz;		/* line frequency */
int	tzone;		/* timezone (minutes behind GMT) */
int	dstflag;	/* daylight savings time flag */

/*
 * Variables used to overlay the new values of
 * hz, timezone, and dstflag onto the kernel.
 * Both memory (/dev/mem) and a.out (/??unix)
 * values are replaced.
 */

int	magic;		/* kernel magic number (430, 431, 450, 451) */
int	novseg;		/* number of overlay segments (8 or 32) */
unsigned int txtsiz;	/* size of root text segment */
unsigned int ovsz[16];	/* size of each overlay */
long	ovsize;		/* total size of all overlays */
long	tsoff;		/* offset to text (32 or 48) */
long	dsoff;		/* offset to data space in a.out file */
long	hzoff;		/* offset to hz */
long	tzoff;		/* offset to timezone */
long	dstoff;		/* offset to dstflag */

int	ab_flag;	/* abort flag, set by intr() */

/*
 * Some stuff for symbolic link of /usr/spool.
 * Remainder local vairables in main().
 */

#define	SL_MAXPL 50		/* MAX pathname length */

char	*sl_usd = "/usr/spool";

/*
 * Setup phase definitions:
 *
 * Phase 0 - cold start, first time setup runs after sdload.
 * Phase 1 - doing initial setup, read of setup.info successful.
 * Phase 2 - initial setup complete, setup restarted after sysgen.
 * Phase 3 - setup completed, customer wants to change one or more items.
 *
 * The setup phase is stored in the first byte of the setup.info
 * file. The sdload program initializes the phase to zero.
 */
#define	SUP0	0
#define	SUP1	1
#define	SUP2	2
#define	SUP3	3

int	phase;

main()
{
	int	intr();
	char	*asktt();
	register struct sd_info *sdp;
	struct	di_info *dip;
	register int i, j;
	int	k;
	int	mem, fd;
	FILE	*fi, *fo;
	int	fsp;
	daddr_t	fsz;
	int	crt;
	int	skipit;
	int	sifdirty;
	long	soff;
	int	*bip;
	int	nunits;
	/* /usr/spool symbolic link stuff */
	int	sl_islink;
	char	sl_src[SL_MAXPL+1];
	char	sl_dst[SL_MAXPL+1];
	char	sl_bdn[SL_MAXPL+1];
	char	sl_sbdn[DIRSIZ+1];
	char	sl_rlb[BUFSIZ];
	char	*p;
	struct	stat	statb;

	mem = -1;	/* supdone() needs to if mem open */
	crt = -1;	/* say we don't know console terminal type */
	ab_flag = 0;
	setjmp(savej);
	if(ab_flag) {
	    printf("\n\nSETUP PROGRAM interrupted via <CTRL/C>!\n");
	    printf("\nEXIT SETUP (y to exit, n to restart setup) <y or n> ? ");
	    if(yes(NOHELP) == YES)
		su_err(0);
	    ab_flag = 0;
	}
	signal(SIGINT,	intr);
/*
 * The /.profile (calls setup) does stty prterase, so
 * the terminal will behave like a printing terminal until
 * we ask the user if it is a CRT.
 *
 * Sneek a peek at the setup.info file, just to get the
 * phase number. If we can't get the phase number, set it
 * to zero (real read of setup.info will fail later).
 * Knowing the phase number in advance helps taylor setup's
 * operation and makes phase 3 less of a pain for the user,
 * and after all the user is paying the bills!
 */
	phase = SUP0;
	if((fi = fopen(info, "r")) != NULL) {
	    if(fgets(sphase, 10, fi) != NULL) {
		if(sphase[1] == '\n')
		    phase = sphase[0] - '0';
	    }
	    fclose(fi);
	}
	if((phase < 0) || (phase > 3))
	    phase = SUP0;
	if(phase < SUP3) {
	    phelp("sum1");
	    phelp("sum2");
	    prtc();
	} else {
	    phelp("sum1");
	    if(access("/usr/bin", 0) == 0) {
		phelp("mu_warn");
		printf("\n\nIs the system in multiuser mode <y or n> ? ");
		if(yes(NOHELP) == YES)
		    exit(1);
	    }
	    printf("\n\n\nPrint instructions <y or n> ? ");
	    if(yes(NOHELP) == YES) {
		phelp("sum2");
		prtc();
	    }
	}
/*
 * Unless in phase 3,
 * ask if the console is a CRT and do
 * stty accordingly.
 */
	if(phase < SUP3)
	    crt = gcrt();
/*
 * Get the setup phase, system disk, target CPU type,
 * and load device type
 * from /.setup/setup.info and validate info.
 * If they are not valid, ask the user for them.
 */
	printf("\n****** READING SETUP DATA FROM setup.info FILE ******\n");
	sifdirty = NO;
	while(1) {
	    if((fi = fopen(info, "r")) == NULL) {
		printf("\nCan't open %s file!\n", info);
		sifdirty = YES;
		break;
	    }
	    if(fgets(sphase, 10, fi) == NULL) {
		printf("\nCan't get setup phase from %s!\n", info);
		sifdirty = YES;
		break;
	    }
	    zapnl(&sphase);		/* change newline to null */
	    if(fgets(sdname, 10, fi) == NULL) {
		printf("\nCan't get system disk type from %s!\n", info);
		sifdirty = YES;
		break;
	    }
	    zapnl(&sdname);		/* change newline to null */
	    if(fgets(tproc, 10, fi) == NULL) {
		printf("\nCan't get target CPU type from %s!\n", info);
		sifdirty = YES;
		break;
	    }
	    zapnl(&tproc);		/* change newline to null */
	    if(fgets(loadev, 15, fi) == NULL) {
		printf("\nCan't get load device name from %s!\n", info);
		sifdirty = YES;
		break;
	    }
	    zapnl(&loadev);		/* change newline to null */
	    rcflag = -1;
	    rlflag = -1;
	    mtflag = -1;
	    tkflag = 0;
	    rxflag = 0;
	    if((strncmp(loadev, "rx", 2) == 0) &&
	       (loadev[3] > '0') && (loadev[3] <= '3')) {
			rxflag = 1;
			rxunit = loadev[3] - '0';
	    } else if((strncmp(loadev, "ht", 2) == 0) ||
		      (strncmp(loadev, "ts", 2) == 0) ||
		      (strncmp(loadev, "tm", 2) == 0))
			mtflag = (loadev[3] - '0') & 3;
	    else if(strncmp(loadev, "tk", 2) == 0) {
			mtflag = (loadev[3] - '0') & 3;
			tkflag = 1;
	    } else if(strncmp(loadev, "rl", 2) == 0)
			rlflag = loadev[3] - '0';
	    else if(strncmp(loadev, "rc", 2) == 0)
			rcflag = loadev[3] - '0';
	    else {
			printf("\n(%s) - bad load device!\n", loadev);
			sifdirty = YES;
			break;
	    }
	    fclose(fi);
	    for(i=0; sd_info[i].sd_lname; i++)
		if(strcmp(sdname, sd_info[i].sd_lname) == 0)
		    break;
	    if(sd_info[i].sd_lname == 0) {
		printf("\n(%s) - bad system disk type!\n", sdname);
		sifdirty = YES;
		break;
	    }
	    sd_info[i].sd_flags |= SD_SYSDSK;
	    sdunit = sd_info[i].sd_swap[2] - '0';
	    sdtype = i;
	    phase = sphase[0] - '0';
	    if((phase < 0) || (phase > 3)) {
		phase = 0;
		printf("\n(%s) - bad setup phase number!\n", sphase);
		sifdirty = YES;
		break;
	    }
	    if(rlflag >= 0)
		break;
	    if(rcflag >= 0)
		break;
	    for(i=0, j=atoi(tproc); cputyp[i].p_type; i++)
		if(cputyp[i].p_type == j)
		    break;
	    if(cputyp[i].p_type == 0) {
		printf("\n(%s) - bad target CPU type!\n", tproc);
		sifdirty = YES;
		break;
	    }
	    tpi = i;	/* save index to target processor type */
	    break;
	}
usr_info:
	if(sifdirty == YES) {
	    if(fi != NULL)
		fclose(fi);
	    phelp("usrinfo");
u_info:
	    printf("\nContinue the installation <y or n> ? ");
	    if(yes(NOHELP) != YES)
		su_err(ABORT);
	    sprintf(&loadev, "rx(0,0)boot");	/* for write to setup.info */
/*
 * Ask the user what phase to enter.
 * This is risky, but it should not be a
 * problem unless setup.info gets blown away!
 */
	    while(1) {
		phase = 0;
		do
		    printf("\nSetup phase number (? for help) < 1 2 3 > ? ");
		while(getline("h_pn") <= 0);
		phase = atoi(lbuf);
		if((phase <= 0) || (phase > 3)) {
		    printf("\n(%s) - bad setup phase number!\n", lbuf);
		    continue;
		}
		break;
	    }
	    while(1) {
		rxflag = 0;
		mtflag = -1;
		tkflag = 0;
		rlflag = -1;
		rcflag = -1;
		printf("\nWas system loaded from RX50 diskettes <y or n> ? ");
		if(yes(NOHELP) == YES) {
		    while(1) {
			do
			    printf("\nFloppy disk unit number < 1 2 3 > ? ");
			while(getline("h_rxldun") <= 0);
			if((strlen(lbuf) != 1) ||
			   (lbuf[0] < '1') ||
			   (lbuf[0] > '3')) {
			    printf("\n(%s) - Bad unit number!\n", lbuf);
			    continue;
			}
			rxflag = 1;
			rxunit = lbuf[0] - '0';
			loadev[3] = lbuf[0];
			break;
		    }
		break;
		}
		printf("\nWas system loaded from TK50 <y or n> ? ");
		if(yes(NOHELP) == YES) {
		    mtflag = 0;		/* assume unit zero */
		    tkflag = 1;
		    loadev[0] = 't';
		    loadev[1] = 'k';
		    break;
		}
		printf("\nWas system loaded from MAGTAPE <y or n> ? ");
		if(yes(NOHELP) == YES) {
		    mtflag = 0;
		    while(1) {
			do
			    printf("\nMagtape controller type < tm ht ts > ? ");
			while(getline("h_mtldct") <= 0);
			if((strlen(lbuf) != 2) ||
			   ((strcmp("ht", lbuf) != 0) &&
			    (strcmp("ts", lbuf) != 0) &&
			    (strcmp("tm", lbuf) != 0))) {
			    printf("\n(%s) - Invalid cntlr type!\n", lbuf);
			    continue;
			}
			loadev[0] = lbuf[0];
			loadev[1] = lbuf[1];
			break;
		    }
		    break;
		}
		printf("\nWas system loaded from RL02 disk pack <y or n> ? ");
		if(yes(NOHELP) == YES) {
		    rlflag = 0;
		    loadev[1] = 'l';
		    break;
		}
		printf("\nWas system loaded from RC25 disk pack <y or n> ? ");
		if(yes(NOHELP) == YES) {
		    rcflag = 0;
		    loadev[1] = 'c';
		    break;
		}
	    }
usr_sdt:
	    while(1) {
		printf("\nPlease enter the generic system disk name.\n");
		printf("\nThese disks are supported:\n");
		for(j=0, i=0; sd_info[i].sd_lname; i++) {
		    if(sd_info[i].sd_swap == 0)
			continue;	/* can't be system disk */
		    if((j & 7) == 0)
			printf("\n        ");
		    printf("%s ", sd_info[i].sd_lname);
		    j++;
		}
		printf("\n\nSystem Disk Type: ");
		    for(i=0; i<10; i++) {
			sdname[i] = getchar();
			if(sdname[i] == '\n') {
			    sdname[i] = '\0';
			    break;
			}
		    }
		for(i=0; sd_info[i].sd_lname; i++)
		    if(strcmp(sdname, sd_info[i].sd_lname) == 0)
			break;
		if(sd_info[i].sd_lname == 0) {
		    printf("\n(%s) - bad system disk!\n", sdname);
		    continue;
		}
		sd_info[i].sd_flags |= SD_SYSDSK;
		sdunit = sd_info[i].sd_swap[2] - '0';
		sdtype = i;
		break;
	    }
usr_cpt:
	    if((rlflag < 0) && (rcflag < 0)) {
		while(1) {
		    phelp("tcpu");
		    printf("\n\nTarget Processor Type: ");
		    for(i=0; i<10; i++) {
			tproc[i] = getchar();
			if(tproc[i] == '\n') {
			    tproc[i] = '\0';
			    break;
			}
		    }
		    for(i=0, j=atoi(tproc); cputyp[i].p_type; i++)
			if(cputyp[i].p_type == j)
			    break;
		    if(cputyp[i].p_type == 0) {
			printf("\n(%s) - bad target CPU type!\n", tproc);
			continue;
		    }
		    tpi = i;
		    break;
		}
	    }
	}
info_ok:
/*
 * Get current CPU type and lots of other
 * needed info from the kernel.
 */
	if(nlist("/unix", nl) < 0) {
		printf("\nCan't access namelist in /unix!\n");
		su_err(FATAL);
	}
	if((nl[X_CPUTYPE].n_value == 0) ||
	   (nl[X_HZ].n_value == 0) ||
	   (nl[X_TIMEZONE].n_value == 0) ||
	   (nl[X_DSTFLAG].n_value == 0)) {
		printf("\nCan't get needed symbol values from ");
		printf("/unix namelist!\n");
	        su_err(FATAL);
	}
	if((mem = open("/dev/mem", 2)) < 0) {
		printf("\nCan't open memory (/dev/mem)!\n");
		su_err(FATAL);
	}
	lseek(mem, (long)nl[X_CPUTYPE].n_value, 0);
	read(mem, (char *)&cputype, sizeof(cputype));
	for(cpi=0; cputyp[cpi].p_type; cpi++)
		if(cputyp[cpi].p_type == cputype)
			break;
	if(rxflag || (rlflag >= 0) || (rcflag >= 0)) {
		sprintf(tproc, "%d", cputype);
		tpi = cpi;
	}
/*
 * Phase 0 is not visable to the user, it is
 * only internal to setup.
 */
	printf("\nSETUP PHASE      = %d", (phase == 0) ? 1 : phase);
	switch(phase) {
	case SUP0:
	case SUP1:
		printf(" (Initial Setup)");
		break;
	case SUP2:
		printf(" (Final Setup)");
		break;
	case SUP3:
		printf(" (Change Setup)");
		break;
	/* NO DEFAULT: phase checked before we get here */
	}
	printf("\nLOAD DEVICE TYPE = ");
	if(rxflag)
		printf("FLOPPY DISK UNIT %d", rxunit);
	else if(rlflag >= 0)
		printf("RL02 UNIT %d", rlflag);
	else if(rcflag >= 0)
		printf("RC25 UNIT %d", rcflag);
	else if(tkflag)
		printf("TK50 UNIT %d", mtflag);
	else
		printf("MAGTAPE UNIT %d", mtflag);
	printf("\nSYSTEM DISK TYPE = %s", sd_info[sdtype].sd_uname);
	printf("\nCURRENT CPU TYPE = 11/%d", cputype);
	printf("\nTARGET  CPU TYPE = 11/%s", tproc);
	printf("\n\nIs the above information correct <y or n> ? ");
	if(yes(NOHELP) != YES) {
		phelp("info_err");
		goto u_info;
	}
/*
 * If we had to ask the user for the info that should
 * have come from setup.info, write the user entered
 * info back to the setup.info file. So, it will be
 * correct next time (most likely in setup phase 3).
 */
	if(sifdirty == YES) {
	    while(1) {
		if((fo = fopen(info, "w")) == NULL) {
		    printf("\nWARNING: can't create %s file!\n", info);
		    break;
		}
		fprintf(fo, "%d\n%s\n%d\n%s\n", phase,
		    sd_info[sdtype].sd_lname, cputyp[tpi].p_type, loadev);
		fclose(fo);
		break;
	    }
	}
/*
 * Finally know what phase we are really in!
 * Modify setup.info to current phase.
 */
	if((phase == SUP0) || ((phase == SUP1) && sifdirty)) {
/*
 * Arrange for a copy of the generic kernel to be
 * preserved in case of disaster.
 * Do this on first call of setup only!
 */
		if(access("/gunix", 0) != 0)
		    link("/unix", "/gunix");
		sync();
		phase = SUP1;
		wrtpn(phase);
	}
	printf("\n****** ENTERING SETUP PHASE %d ******\n", phase);
/*
 * If the system disk has a small root (SD_SMALL),
 * RD51, RL01, RL02, RK06, RK07,
 * remove the /sas directory (unload saprog).
 * The /sas files are always loaded from magtape or TK50,
 * this frees up some space in a tight root. User can re-load
 * saprog with osload if he/she really wants it.
 */
	if((phase == SUP1) && (sd_info[sdtype].sd_flags&SD_SMALL))
	    system("rm -rf /sas");
/*
 * If target CPU type equals the current CPU type,
 * ask the user if this is the target CPU.
 * If they are not equal, we know it is not the target CPU.
 * Hate to ask the user, but it is a critical peice of info
 * and there is no other way to get it!
 * NOTE: If not loading from a magtape kit, must be on target CPU!
 */
	if(phase == SUP1) {
	    if(tpi != cpi) {
		if(mtflag < 0) {
		    printf("\nNOT LOADING FROM MAGTAPE (or TK50): ");
		    printf("must install on target processor!\n");
		    su_err(FATAL);
		} else
		    ontarget = NO;
	    } else {
		while(1) {
		    if(mtflag < 0) {
			ontarget = YES;
			break;
		    }
		    printf("\nIs this the target processor ");
		    printf("(? for help) <y or n> ? ");
		    j = yes(HELP);
		    if(j == HELP) {	
			phelp("h_otp");
			continue;
		    }
		    ontarget = j;
		    printf("%s%sthe target processor <y or n> ? ",
			confirm, (j == YES) ? "on " : "not on ");
		    if(yes(NOHELP) != YES)
			continue;
		    break;
		}
	    }
	} else
	    ontarget = YES;	/* all other phase must be done on target CPU */
/*
 * Blast temporary /etc/ttys and /etc/ttytype files
 * containing entries for the console terminal only.
 * The real files will be blasted after the new kernel
 * is installed and we know what devices are configured.
 */
	if(phase < SUP2)
	    if(bttys(crt, 0, 0) < 0)
		su_err(FATAL);
/*
 * If phase 3, ask user if console terminal type
 * needs to be changed (CRT/PRT).
 */
	if(phase == SUP3) {
	    printf("\nChange console terminal type (CRT vs HARDCOPY) <y or n> ? ");
	    if(yes(NOHELP) == YES)
		crt = gcrt();
	}

/*
 * Ask for info on hz, timezone, & dstflag.
 */
	switch(phase) {
	case SUP0:
	case SUP1:
		skipit = NO;
		break;
	case SUP2:
		skipit = YES;
		break;
	case SUP3:
		printf("\nChange line frequency, timezone, or daylight ");
		printf("savings time <y or n> ? ");
		if(yes(NOHELP) == YES)
		    skipit = NO;
		else
		    skipit = YES;
		break;
	}
g_hz:
	while(1) {
	    if(skipit == YES)
		break;
	    do {
	    	printf("\nWhat is your AC power line frequency in hertz");
	    	printf(" < 50 or 60 > ? ");
	    } while(getline("h_hz") <= 0);
	    hz = atoi(lbuf);
	    if((hz != 50) && (hz != 60))
		printf("\nLine frequency should be 50 or 60 hertz!\n");
	    printf("%sAC line frequency is %d hertz <y or n> ? ", confirm, hz);
	    if(yes(NOHELP) != YES)
		continue;
	    break;
	}
g_tz:
	while(1) {
	    if(skipit == YES)
		break;
	    do {
	    	printf("\nWhat is your local time zone ");
	    	printf("< hours west/behind GMT > ? ");
	    } while(getline("h_tz") <= 0);
	    tzone = atoi(lbuf);
	    if((tzone < 0) || (tzone > 23)) {
	    	printf("\n%d - invalid time zone!\n", tzone);
		continue;
	    }
	    printf("%stime zone is %d hours west/behind GMT <y or n> ? ",
		confirm, tzone);
	    if(yes(NOHELP) != YES)
		continue;
	    tzone *= 60;
	    break;
	}
g_dst:
	while(1) {
	    if(skipit == YES)
		break;
	    printf("\nDoes your local area use daylight savings time <y or n> ? ");
	    j = yes(HELP);
	    if(j == HELP) {
	    	phelp("h_dst");
		continue;
	    }
	    if(j == YES) {
	    	dstflag = 1; 
	    }
	    else
	    	dstflag = 0;
	    printf("%sdaylight savings time%sin use <y or n> ? ",
	    	confirm, dstflag ? " " : " not ");
	    if(yes(NOHELP) != YES)
		continue;
	    break;
	}
	if(dstflag) 
	    while(1) {
		dstflag = get_dst();  /* Get geographic area */
		printf("%s Geographic area is %d <y or n> ?",confirm,dstflag);
		if(yes(NOHELP) != YES)
		    continue;
		break;
	    }
/*
 * Overlay new HZ, TIMEZONE, and DSTFLAG
 * onto kernel a.out file (/unix).
 */
	if(skipit == NO) {
	    if((fd = open("/unix", 2)) < 0) {
	    	printf("\nCan't open /unix for read/write!\n");
	    	su_err(FATAL);
	    }
	    read(fd, (char *)&magic, sizeof(magic));
	    switch(magic) {
	    case 0430:
	    case 0431:
		novseg = 8;
		tsoff = 32L;
		break;
	    case 0450:
	    case 0451:
		novseg = 16;
		tsoff = 48L;
		break;
	    default:
	    	printf("\nType %o kernels not supported!\n", magic);
	    	su_err(FATAL);
	    }
	    read(fd, (char *)&txtsiz, sizeof(txtsiz));
	    lseek(fd, 16L, 0);
	    read(fd, (char *)&ovsz, sizeof(ovsz));
	    ovsize = 0;
	    for(i=1; i<novseg; i++)
	    	ovsize += ovsz[i];
	    dsoff = (long)tsoff + (long)txtsiz + (long)ovsize;	/* data space offset */
	    hzoff = dsoff + (long)nl[X_HZ].n_value;
	    tzoff = dsoff + (long)nl[X_TIMEZONE].n_value;
	    dstoff = dsoff + (long)nl[X_DSTFLAG].n_value;
	    if((magic == 0430) || (magic == 0450)) {
		hzoff -= 060000L;
		tzoff -= 060000L;
		dstoff -= 060000L;
	    }
#ifdef	DEBUG
	    printf("%shzoff=%O tzoff=%O dstoff=%O\n", debug, hzoff, tzoff, dstoff);
#endif	DEBUG
	    lseek(fd, (long)hzoff, 0);
	    write(fd, (char *)&hz, sizeof(hz));
	    lseek(fd, (long)tzoff, 0);
	    write(fd, (char *)&tzone, sizeof(tzone));
	    lseek(fd, (long)dstoff, 0);
	    write(fd, (char *)&dstflag, sizeof(dstflag));
	    close(fd);
	    sync();
/*
 * Overlay new HZ, TIMEZONE, and DSTFLAG
 * onto the running kernel (/dev/mem).
 */
	    lseek(mem, (long)nl[X_HZ].n_value, 0);
	    write(mem, (char *)&hz, sizeof(hz));
	    lseek(mem, (long)nl[X_TIMEZONE].n_value, 0);
	    write(mem, (char *)&tzone, sizeof(tzone));
	    lseek(mem, (long)nl[X_DSTFLAG].n_value, 0);
	    write(mem, (char *)&dstflag, sizeof(dstflag));
	}
/*
 * Set the date and time.
 */
g_date:
	while(1) {
	    if((phase != SUP3) || ((phase == SUP3) && (skipit == NO))) {
		do {
		    printf("\nPlease enter the current date/time ");
		    printf("< yymmddhhmm.ss > ? ");
		} while(getline("h_date") <= 0);
		printf("\n");
		sprintf(syscmd, "date %s", lbuf);
		if(system(syscmd) != 0) {
		    printf("\nAttempt to set date/time failed!\n");
		    continue;
		}
	    } else {
		printf("\n");
		system("date");
	    }
	    printf("%sdate/time correct <y or n> ? ", confirm);
	    if(yes(NOHELP) != YES) {
		skipit = NO;
		continue;
	    }
	    sync();
	    break;
	}
/*
 * Select split or non-split I/D space commands in /bin.
 * The selection will be made twice if the current CPU has
 * split I/D space and the target CPU does not, or visa versa.
 */
sel_bin:
	while(1) {
	    if(phase != SUP1)
		break;
	    if(access(rclock, 0) == 0)
		break;		/* /bin commands already selected */
	    printf("\n****** SELECTING%sSPLIT I/D COMMANDS (/bin) ******\n",
		(cputyp[cpi].p_sid == SID) ? " " : " NON-");
	    if(chngdir("/bin"))
	    	su_err(FATAL);
#ifdef	DEBUG
	    printf("%s", debug);
	    fflush(stdout);
	    system("ls sid");	/* sid/nsid contain same files */
#endif	DEBUG
	    if(cputyp[cpi].p_sid == SID)
	    	sprintf(syscmd, "cp sid/* .");
	    else
	    	sprintf(syscmd, "cp nsid/* .");
	    if(system(syscmd) != 0) {
	    	printf("\nCan't `%s' in /bin!\n", syscmd);
	    	su_err(FATAL);
	    }
	    sync();
#ifdef	DEBUG
	    printf("%s/lib/c2\n", debug);
#endif	DEBUG
	    system("rm -f /lib/c2");
	    if(cputyp[cpi].p_sid == SID)
	    	sprintf(syscmd, "ln /lib/c2_id /lib/c2");
	    else
	    	sprintf(syscmd, "ln /lib/c2_ov /lib/c2");
	    if(system(syscmd) != 0) {
	    	printf("\nCan't `%s'!\n", syscmd);
	    	su_err(FATAL);
	    }
	    sync();
	    if(chngdir(homedir))
		su_err(FATAL);
	    if(cputyp[cpi].p_sid == cputyp[tpi].p_sid) { /* selection final */
		creat(rclock, 0444);			 /* create lock file */
		sync();
	    }
	    break;
	}
/*
 * Remove the alternate commands directories (/bin/sid & /bin/nsid).
 * ONLY if the lock file exists (final selection made).
 */
	while(1) {
	    if(phase != SUP1)
		break;
	    if(access(rclock, 0) != 0)
		break;		/* final selection not made yet */
	    if((access("/bin/sid", 0) != 0) && (access("/bin/nsid", 0) != 0))
		break;		/* /bin commands already removed */
#ifdef	DEBUG
	    printf("%sremove /bin/sid and /bin/nsid <y or n> ? ", debug);
	    if(yes(NOHELP) != YES)
		break;
#endif	DEBUG
	    system("rm -r /bin/sid /bin/nsid");
	    sync();
	    break;
	}
/*
 * Zero the error log file.
 */
	if(phase == SUP1) {
	    printf("\n****** ZEROING THE ERROR LOG FILE ******\n");
	    if(system("/etc/eli -f > /dev/null 2>&1") != 0) {
	    	printf("\nCan't zero the error log file\n");
	    	su_err(FATAL);
	    }
	}
/*
 * Make device special files.
 * If in phase 1, only make special files
 * for drives on the system disk controller and
 * magtapes (if any).
 * Also, take care of the work that used to be
 * done by /dev/makefile, i.e., system disk special files,
 * chmod 400 partition 7 (sometimes 6), /dev/swap file,
 * basic /etc/fstab, and boot block (redundant).
 *
 * If phase 2, make all special files.
 * If phase 3, ask user if we need to make special files.
 * NOTE: Even if the users says no, we go thru the motions of
 *	 making the special files, because the system config info
 *	 gathered is needed by some of the following steps.
 */
	switch(phase) {
	case SUP0:
	case SUP1:
	case SUP2:
		skipit = NO;
		break;
	case SUP3:
		while(1) {
		    printf("\nChange device special files <y or n> ? ");
		    j = yes(HELP);
		    if(j == YES)
			skipit = NO;
		    else if(j == NO)
			skipit = YES;
		    else if(j == HELP) {
			phelp("h_csf");
			continue;
		    }
		    break;
		}
		break;
	/* DEFAULT CASE NOT NEEDED */
	}
	if(chngdir("/dev"))
	    su_err(FATAL);
/*
 * Find out what disk controllers/drives are present
 * and save the necessary info in the di_info[] table.
 *
 * 1. Read bdevsw[] entry for disk controller.
 * 2. If d_tab entry is 0, controller not configured.
 * 3. Make a special file to access cntlr unit 0.
 * 4. Open unit 0 to force driver to load ??_dt[] with drive types.
 *    Note: RP require reading each unit.
 * 5. Call appropriate routine to get drive type info and
 *    load it into di_info[].
 *
 */
	while(1) {
	    if((phase > SUP1) && (skipit == NO)) {
		if(phase == SUP2) {
		    phelp("msf_rec");
		    prtc();
		}
		printf("\n****** DETERMINING SYSTEM'S DISK CONFIGURATION ******\n");
	    }
	    for(dip=di_info; dip->di_typ; dip++) {
/* 1 */		soff = (long)nl[X_BDEVSW].n_value +
		   (long)(sizeof(struct bdevsw) * dip->di_bmaj);
		lseek(mem, (long)soff, 0);
		read(mem, (char *)&bdevswb, sizeof(struct bdevsw));
/* 2 */		dip->di_nunit = 0;
		if(bdevswb.d_tab == 0)
		    continue;
/* 3 */		unlink(dsk_sfn);
		k = (dip->di_rmaj << 8);
		if(dip->di_flags&DI_MSCP)
		    k |= (dip->di_cn << 6);
		if(mknod(dsk_sfn, 020400, k) < 0) {
		    printf("\nmknod for %s controller %d failed!\n",
			dip->di_typ, dip->di_cn);
		    su_err(FATAL);
		}
/* 4 */		if((i = open(dsk_sfn, 0)) >= 0)
		    close(i);
		unlink(dsk_sfn);
/* 5 */		for(i=0; i<8; i++)	/* set all drive types to NED */
		    dip->di_dt[i] = 0;
		(*dip->di_func)(MSF, dip, mem);	/* get drive type info */
	    }
	    break;
	}
/*
 * If phase 1, don't print disk configuration and only
 * make special files for drives on the system disk controller.
 *
 * If phase 2, print the system's disk configuration
 * and make the disk special files.
 *
 * If phase 3, print the disk configuration then ask
 * if the user wants to remake the special files.
 *
 * DEPENDENCY:	TODO: fixed by sdunit -- check it!
 *
 *	This code assumes that the system disk is always unit 0.
 *	This is true execpt for the RC25 which is unit 1, but the
 *	code still works because, even though the system disk is
 *	unit 1, unit 0 will also be an RC25.
 */

	while(1) {
	    if(skipit == YES)
		break;
	    if(phase > SUP1)
		pdconf(MSF);
	    if(phase == SUP2) {
		prtc();
	    }
	    if(phase == SUP3) {
		phelp("cmp_info");
		while(1) {
		    printf("\nHas the disk configuration changed ");
		    printf("(? for help) <y or n> ? ");
		    j = yes(HELP);
		    if(j == HELP) {
			phelp("h_rmsf");
			continue;
		    } else
			break;
		}
		if(j == NO)
		    break;
	    }
	    printf("\n****** MAKING ");
	    if(phase == SUP1)
		printf("SYSTEM DISK(s) ");
	    else
		printf("DISK ");
	    printf("SPECIAL FILES ******\n");
	    for(dip=di_info; dip->di_typ; dip++) {
		if(dip->di_nunit == 0)
		    continue;
		/*
		 * Following due to HACK in KLESI bus adaptor design!
		 * Get unit status returns valid unit type (rc25) even if
		 * the drive is not present. We only need first two units
		 * during phase one. Things work ok in phase two, assuming
		 * user only configures units that really exist.
		 */
		if((phase == SUP1) && (strcmp("KLESI", dip->di_typ) == 0))
		    nunits = 2;
		else
		    nunits = dip->di_nunit;
		for(i=0; i<nunits; i++) {
		    if(dip->di_dt[i] == 0)
			continue;
		    if(dip->di_flags&(DI_MSCP|DI_MASS))
			sprintf(syscmd, "/etc/msf %s -c %d %d",
			    sd_info[dip->di_dt[i]].sd_lname, dip->di_cn, i);
		    else
			sprintf(syscmd, "/etc/msf %s %d",
			    sd_info[dip->di_dt[i]].sd_lname, i);
#ifdef	DEBUG
		    printf("%s%s\n", debug, syscmd);
#endif	DEBUG
		    if(system(syscmd) != 0) {
			printf("\n%s failed!\n", syscmd);
			su_err(FATAL);
		    }
/* TODO: following done in P1 & P2, I think that's ok! */
		    if(((sd_info[dip->di_dt[i]].sd_flags&SD_SYSDSK) == 0) ||
			(i != sdunit) || (dip->di_cn != sdcntlr))
			    continue; /* following only done for system disk */
		    /*
		     * Create /dev/swap so PS and other programs
		     * can access the swap area.
		     */
		    unlink("/dev/swap");
		    if(link(sd_info[sdtype].sd_swap, "/dev/swap") < 0) {
			printf("\nCan't link %s to /dev/swap!\n",
			    sd_info[sdtype].sd_swap);
			su_err(FATAL);
		    }
		    sync();
		    /*
		     * Blast the initial /etc/fstab (root & /usr entries only).
		     */
		    for(j=0; fs_info[j].fs_lname; j++)
			if(strcmp(sd_info[sdtype].sd_lname, fs_info[j].fs_lname) == 0)
			    break;
		    /* THIS CAN'T HAPPEN! */
		    if(fs_info[j].fs_lname == 0) {
			printf("\nPANIC: sd_info & fs_info names don't match!\n");
			su_err(FATAL);
		    }
		    unlink("/etc/fstab");
		    if((fo = fopen("/etc/fstab", "w")) == NULL) {
			printf("\nCreate of /etc/fstab file failed!\n");
			su_err(FATAL);
		    }
		    fprintf(fo, "/dev/%s:/:rw:0:1\n", fs_info[j].fs_root);
		    fprintf(fo, "/dev/%s:/usr:rw:0:1\n", fs_info[j].fs_usr);
		    fclose(fo);
		    sync();
		    /*
		     * DD the boot block from /mdec to block zero of the
		     * system disk, sdload does this, but we do it to be sure!
		     */
		    sprintf(syscmd,
			"dd if=/mdec/%suboot of=%s count=1 > /dev/null 2>&1",
			(dip->di_flags&DI_MSCP) ? "ra" : dip->di_name,
			dsfn(RAW, sdunit, 0, dip));
#ifdef	DEBUG
		    printf("%s%s\n", debug, syscmd);
#endif	DEBUG
		    system(syscmd);
		    sync();
		}
	    }
	    break;
	}
/*
 * Find out what magtape controllers are present
 * and how many drives are configured.
 *
 * If phase 2, print magtape config and make special files.
 *
 * If phase 3, print magtape config, ask user if special files need remaking.
 *
 * TODO: what about multiple tape cntlr type clashes?
 *	   what about crash dump device?
 */
	while(1) {
	    if(phase < SUP1)
		break;
	    if((skipit == NO) && (phase > SUP1))
		printf("\n****** DETERMINING SYSTEM'S TAPE CONFIGURATION ******\n");
	    nht = nts = ntm = ntk = 0;
	    for(i=0; i<MAXTK; i++)
		tk_ctid[i] = 0;
	    if(nl[X_NHT].n_value) {
		lseek(mem, (long)nl[X_NHT].n_value, 0);
		read(mem, (char *)&nht, sizeof(nht));
	    }
	    if(nl[X_NTS].n_value) {
		lseek(mem, (long)nl[X_NTS].n_value, 0);
		read(mem, (char *)&nts, sizeof(nts));
	    }
	    if(nl[X_NTM].n_value) {
		lseek(mem, (long)nl[X_NTM].n_value, 0);
		read(mem, (char *)&ntm, sizeof(ntm));
	    }
	    if(nl[X_NTK].n_value) {
		lseek(mem, (long)nl[X_NTK].n_value, 0);
		read(mem, (char *)&ntk, sizeof(ntk));
	    }
	    if(nl[X_TK_CTID].n_value) {
		lseek(mem, (long)nl[X_TK_CTID].n_value, 0);
		read(mem, (char *)&tk_ctid, sizeof(tk_ctid));
		for(i=0; i<MAXTK; i++)
		    tk_ctid[i] = (tk_ctid[i] >> 4) & 017;
	    }
	    if(skipit == YES)
		break;
	    if(phase > SUP1) {
		printf("\nMagtape Controller       # Units");
		printf("\n------------------       -------");
	    }
	    j = 0;
	    if(nht) {
		if(phase > SUP1)
		    printf("\nTM02/3 - TU16/TE16/TU77  %d", nht);
		j++;
	    }
	    if(nts) {
		if(phase > SUP1)
		    /* TK25 no longer supported */
		    printf("\nTS11/TU80/TS05           %d", nts);
		j++;
	    }
	    if(ntm) {
		if(phase > SUP1)
		    printf("\nTM11 - TU10/TE10/TS03    %d", ntm);
		j++;
	    }
	    if(ntk) {
		if(phase > SUP1)
		    printf("\nTK50/TU81                %d", ntk);
		j++;
	    }
/*
 * TODO: remove after debug
	    if(tk_ctid) {
		i = 0;
		switch(tk_ctid) {
		case TK50:
		    if(phase > SUP1)
			printf("\nTK50");
		    break;
		case TU81:
		    if(phase > SUP1)
			printf("\nTU81");
		    break;
		default:
		    if(phase > SUP1)
			printf("\nTK50/TU81 configured, but not present!\n");
		    i++;
		    break;
		}
		if(i == 0) {
		    j++;
		    if(phase > SUP1)
			printf("                     1");
		}
	    }
*/
	    if(j == 0) {
		if(phase > SUP1)
		    printf("\nNO MAGTAPES FOUND!\n");
		break;
	    } else
		if(phase > SUP1)
		    printf("\n");
	    if(phase == SUP2) {
		prtc();
	    }
	    if(phase == SUP3) {
		phelp("cmp_info");
		while(1) {
		    printf("\nHas the magtape configuration changed ");
		    printf("(? for help) <y or n> ? ");
		    j = yes(HELP);
		    if(j == HELP) {
			phelp("h_rmsf");
			continue;
		    } else
			break;
		}
		if(j == NO)
		    break;
	    }
	    printf("\n****** MAKING MAGTAPE SPECIAL FILES ******\n");
	    if(nht) {
		for(i=0; i<nht; i++) {
		    sprintf(syscmd, "/etc/msf tm03 %d", i);
#ifdef	DEBUG
		    printf("%s%s\n", debug, syscmd);
#endif	DEBUG
		    if(system(syscmd) != 0) {
			printf("\n%s failed!\n", syscmd);
			su_err(FATAL);
		    }
		}
	    }
	    if(nts) {
		for(i=0; i<nts; i++) {
		    sprintf(syscmd, "/etc/msf ts11 %d", i);
#ifdef	DEBUG
		    printf("%s%s\n", debug, syscmd);
#endif	DEBUG
		    if(system(syscmd) != 0) {
			printf("\n%s failed!\n", syscmd);
			su_err(FATAL);
		    }
		}
	    }
	    if(ntm) {
		for(i=0; i<ntm; i++) {
		    sprintf(syscmd, "/etc/msf tm11 %d", i);
#ifdef	DEBUG
		    printf("%s%s\n", debug, syscmd);
#endif	DEBUG
		    if(system(syscmd) != 0) {
			printf("\n%s failed!\n", syscmd);
			su_err(FATAL);
		    }
		}
	    }
	    if(ntk) {
		for(i=0; i<ntk; i++) {
		    if(tk_ctid[i] == TK50)
			sprintf(syscmd, "/etc/msf tk50 %d", i);
		    else if(tk_ctid[i] == TU81)
			sprintf(syscmd, "/etc/msf tu81 %d", i);
		    else {
			printf("\n\7\7\7TK50/TU81 unit %d: can't get ", i);
			printf("drive type (no special files created)!");
			printf("\nRerun setup or use msf(8) to create ");
			printf("TK50 or TU81 special files.\n");
		    }
#ifdef	DEBUG
		    printf("%s%s\n", debug, syscmd);
#endif	DEBUG
		    if(system(syscmd) != 0) {
			printf("\n%s failed!\n", syscmd);
			su_err(FATAL);
		    }
		}
	    }
	    sync();
	    break;
	}
/*
 * Find out what comm. devices area configured and
 * how many units/lines of each type are configured.
 *
 * If phase 2, print comm. config and make special files.
 *
 * If phase 3, print comm. config, ask user if special files need remaking.
 *
 * TODO: what about misc. devices (du ?) and console, mem, lp?
 */

	while(1) {
	    if(phase < SUP2)
		break;
	    if(skipit == NO) {
		printf("\n****** DETERMINING SYSTEM'S COMMUNICATIONS DEVICE ");
		printf("CONFIGURATION ******\n");
	    }
	    if(cputyp[cpi].p_bus == QBUS) {
		dz_lpu = 4;
		uh_lpu = 8;
		dl_name = "DLV11";
		dz_name = "DZV11";
		uh_name = "DHV11";
	    } else {
		dz_lpu = 8;
		uh_lpu = 16;
		dl_name = "DL11";
		dz_name = "DZ11";
		uh_name = "DHU11";
	    }
	    lseek(mem, (long)nl[X_NTTY].n_value, 0);
	    read(mem, (char *)&ntty, sizeof(ntty));
	    ntty--;	/* don't count console */
	    if(nl[X_NKL11].n_value) {
		lseek(mem, (long)nl[X_NKL11].n_value, 0);
		read(mem, (char *)&nkl11, sizeof(nkl11));
		nkl11--;	/* don't count console */
	    }
	    if(nl[X_NDL11].n_value) {
		lseek(mem, (long)nl[X_NDL11].n_value, 0);
		read(mem, (char *)&ndl11, sizeof(ndl11));
	    }
	    if(nl[X_NDH11].n_value) {
		lseek(mem, (long)nl[X_NDH11].n_value, 0);
		read(mem, (char *)&ndh11, sizeof(ndh11));
	    }
	    if(nl[X_NUH11].n_value) {
		lseek(mem, (long)nl[X_NUH11].n_value, 0);
		read(mem, (char *)&nuh11, sizeof(nuh11));
	    }
	    if(nl[X_DZ_CNT].n_value) {
		lseek(mem, (long)nl[X_DZ_CNT].n_value, 0);
		read(mem, (char *)&dz_cnt, sizeof(dz_cnt));
	    }
	    if(skipit == YES)
		break;
	    printf("\nDevice  # Units  Lines/Unit");
	    printf("\n------  -------  ----------");
	    if((i = nkl11 + ndl11) > 0)
		printf("\n%-6s  %-7d  1", dl_name, i);
	    if(ndh11)
		printf("\n%-6s  %-7d  %d", "DH11", (ndh11/dh_lpu), dh_lpu);
	    if(nuh11)
		printf("\n%-6s  %-7d  %d", uh_name, (nuh11/uh_lpu), uh_lpu);
	    if(dz_cnt)
		printf("\n%-6s  %-7d  %d", dz_name, (dz_cnt/dz_lpu), dz_lpu);
	    if((nkl11 + ndl11 + ndh11 + nuh11 + dz_cnt) == 0) {
		printf("\nNO COMMUNICATIONS DEVICES FOUND!\n");
		break;
	    } else
		printf("\n");
	    if(phase == SUP2) {
		prtc();
	    }
	    if(phase == SUP3) {
		phelp("cmp_info");
		while(1) {
		    printf("\nHas the communications configuration ");
		    printf("changed (? for help) <y or n> ? ");
		    j = yes(HELP);
		    if(j == HELP) {
			phelp("h_rmsf");
			continue;
		    } else
			break;
		}
		if(j == NO)
		    break;
	    }
	    printf("\n****** MAKING COMMUNICATIONS DEVICE SPECIAL FILES ******\n");
	    printf("\nCommunications port assignments:\n");
	    k = 0;
	    nttys = 0;
	    if((i = nkl11 + ndl11) > 0) {
		sprintf(syscmd, "/etc/msf dlv11 %d tty%02d", i, nttys);
#ifdef	DEBUG
		printf("%s%s\n", debug, syscmd);
#endif	DEBUG
		if(system(syscmd) != 0) {
		    printf("\n%s failed!\n", syscmd);
		    su_err(FATAL);
		}
		sync();
		for(j=0; j<i; j++) {
		    k = 1;
		    printf("\n%s\tUNIT %d\t\t/dev/tty%02d", dl_name, j, j);
		    if((++nttys % 16) == 0) {
			k = 0;
			printf("\n");
			prtc();
		    }
		}
	    }
	    if(ndh11) {
		for(i=0; i<ndh11; i++) {
		    if((i % dh_lpu) == 0) {
			sprintf(syscmd, "/etc/msf dh11 %d tty%02d",
				(i/dh_lpu), nttys);
#ifdef	DEBUG
			printf("%s%s\n", debug, syscmd);
#endif	DEBUG
			if(system(syscmd) != 0) {
			    printf("\n%s failed!\n", syscmd);
			    su_err(FATAL);
			}
		    }
		    sync();
		    k = 1;
		    printf("\nDH11\tUNIT %d\tLINE %d\t/dev/tty%02d",
			(i/dh_lpu), (i%dh_lpu), nttys);
		    if((++nttys % 16) == 0) {
			k = 0;
			printf("\n");
			prtc();
		    }
		}
	    }
	    if(nuh11) {
		for(i=0; i<nuh11; i++) {
		    if((i % uh_lpu) == 0) {
			if(cputyp[cpi].p_bus == QBUS)
			    sprintf(syscmd, "/etc/msf dhv11 %d tty%02d",
				(i/uh_lpu), nttys);
			else
			    sprintf(syscmd, "/etc/msf dhu11 %d tty%02d",
				(i/uh_lpu), nttys);
#ifdef	DEBUG
			printf("%s%s\n", debug, syscmd);
#endif	DEBUG
			if(system(syscmd) != 0) {
			    printf("\n%s failed!\n", syscmd);
			    su_err(FATAL);
			}
		    }
		    sync();
		    k = 1;
		    printf("\n%s\tUNIT %d\tLINE %d\t/dev/tty%02d",
			uh_name, (i/uh_lpu), (i%uh_lpu), nttys);
		    if((++nttys % 16) == 0) {
			k = 0;
			printf("\n");
			prtc();
		    }
		}
	    }
	    if(dz_cnt) {
		for(i=0; i<dz_cnt; i++) {
		    if((i % dz_lpu) == 0) {
			if(cputyp[cpi].p_bus == QBUS)
			    sprintf(syscmd, "/etc/msf dzv11 %d tty%02d",
				(i/dz_lpu), nttys);
			else
			    sprintf(syscmd, "/etc/msf dz11 %d tty%02d",
				(i/dz_lpu), nttys);
#ifdef	DEBUG
			printf("%s%s\n", debug, syscmd);
#endif	DEBUG
			if(system(syscmd) != 0) {
			    printf("\n%s failed!\n", syscmd);
			    su_err(FATAL);
			}
		    }
		    sync();
		    k = 1;
		    printf("\n%s\tUNIT %d\tLINE %d\t/dev/tty%02d",
			dz_name, (i/dz_lpu), (i%dz_lpu), nttys);
		    if((++nttys % 16) == 0) {
			k = 0;
			printf("\n");
			prtc();
		    }
		}
	    }
	    if(k)
		printf("\n");
	    break;
	}
/*
 * Make the pseudo TTYs.
 *
 * In phase 2, create them.
 *
 * In phase 3, ask if they changed, re-create if necessary.
 */
	while(1) {
	    if(phase < SUP2)
		break;
	    if(skipit == NO)
		printf("\n****** DETERMINING NUMBER OF PSEUDO TTYS ******\n");
	    lseek(mem, (long)nl[X_NPTY].n_value, 0);
	    read(mem, (char *)&npty, sizeof(npty));
	    if(skipit == YES)
		break;
	    if(npty)
		printf("\nNumber of PTTYs = %d\n", npty);
	    else {
		printf("\nNO PSEUDO TTYS FOUND!\n");
		break;
	    }
	    if(phase == SUP2) {
		prtc();
	    }
	    if(phase == SUP3) {
		phelp("cmp_info");
		printf("\nHas number of PTTYs changed <y or n> ? ");
		j = yes(NOHELP);
		if(j == NO)
		    break;
	    }
	    sprintf(syscmd, "/etc/msf ptty %d", npty);
#ifdef	DEBUG
	    printf("%s%s\n", debug, syscmd);
#endif	DEBUG
	    if(system(syscmd) != 0) {
		printf("\n%s failed!\n", syscmd);
		su_err(FATAL);
	    }
	    sync();
	    break;
	}
/*
 * Make maus special files.
 *
 * In phase 2, create them.
 *
 * In phase 3, ask if they changed, re-create if necessary.
 */
	while(1) {
	    if(phase < SUP2)
		break;
	    if(skipit == NO)
		printf("\n****** DETERMINING NUMBER OF MAUS SEGMENTS ******\n");
	    if(nl[X_NMAUSENT].n_value) {
		lseek(mem, (long)nl[X_NMAUSENT].n_value, 0);
		read(mem, (char *)&nmausent, sizeof(nmausent));
	    } else
		nmausent = 0;
	    if(skipit == YES)
		break;
	    if(nmausent)
		printf("\nNumber of MAUS segments = %d\n", nmausent);
	    else {
		printf("\nMAUS NOT CONFIGURED!\n");
		break;
	    }
	    printf("\n****** MAKING MAUS SPECIAL FILES ******\n");
	    sprintf(syscmd, "/etc/msf maus %d", nmausent);
#ifdef	DEBUG
	    printf("%s%s\n", debug, syscmd);
#endif	DEBUG
	    if(system(syscmd) != 0) {
		printf("\n%s failed!\n", syscmd);
		su_err(FATAL);
	    }
	    sync();
	    break;
	}
/*
 * Create the real /etc/ttys and /etc/ttytype files.
 *
 * If phase 2, just create them.
 *
 * If phase 3, ask user they should be created, save old ones if yes.
 */
	while(1) {
	    if(phase < SUP2)
		break;
	    if(phase != SUP3) {
		printf("\n****** CREATING /etc/ttys and /etc/ttytype ");
		printf("FILES ******\n");
	    }
	    if(phase == SUP3) {
		while(1) {
		    printf("\nCreate new ttys and ttytype files <y or n> ? ");
		    j = yes(HELP);
		    if(j == HELP) {
			phelp("h_ttys");
			continue;
		    } else
			break;
		}
		if(j == NO)
		    break;
		printf("\nSaving current files: /etc/ttys.old ");
		printf("and /etc/ttytype.old!\n");
		sprintf(syscmd, "cp /etc/ttys /etc/ttys.old");
		if(system(syscmd) != 0) {
		    printf("\n%s failed!\n", syscmd);
		    su_err(FATAL);
		}
		sprintf(syscmd, "cp /etc/ttytype /etc/ttytype.old");
		if(system(syscmd) != 0) {
		    printf("\n%s failed!\n", syscmd);
		    su_err(FATAL);
		}
		sync();
	    }
	    if(crt == -1)
		crt = gcrt();
	    if(bttys(crt, ntty, npty) < 0)
		su_err(FATAL);
	    break;
	}
	if(chngdir(homedir))
	    su_err(FATAL);
/*
 * OLDCODE
 * Mount the /usr file system so we can load
 * or otherwise mundge the sysgen files.
 * Leave /usr mounted for later operations.
 */
/*	if(usrmnt(MOUNT))	/* mount /usr file system */
/*	    su_err(FATAL);	*/
	system("/etc/mount /usr >/dev/null 2>&1");
	if((phase == SUP1) && rxflag) {
	    printf("\n****** LOADING SYSGEN FILES ******\n");
	    iflop("SYSGEN #1");
	    if(chngdir("/usr"))
	    	su_err(FATAL);
	    sprintf(syscmd, "tar xpbf 10 /dev/rrx%d ./sys/conf",
		rxunit);
	    if(cputyp[tpi].p_sid == SID)
		strcat(syscmd, " ./sys/sys");
#ifdef	DEBUG
	    printf("%s%s\n", debug, syscmd);
#endif	DEBUG
	    while(system(syscmd) != 0) {
	    	if(retry("SYSGEN FILES LOAD"))
	    		continue;
	    	else
	    		su_err(FATAL);
	    }
	    sync();
	    rflop("SYSGEN #1");
	    if(cputyp[tpi].p_sid == SID)
		printf("\nSYSGEN #2 not used with split I/D processors!\n");
	    if(cputyp[tpi].p_sid == NSID) {
		fmnt("SYSGEN #2");
		chngdir("/usr/sys/ovsys");
		system("ar x /mnt/LIB1_ov");
		system("chog sys *");
		system("chmod 444 *");
		chngdir("../ovnet");
		system("ar x /mnt/LIB3_ov");
		system("chog sys *");
		system("chmod 444 *");
		fmnt(0);
		sync();
	    	rflop("SYSGEN #2");
		printf("\nSYSGEN #3 not used with non split I/D processors!\n");
	    }
	    if(cputyp[tpi].p_sid == SID) {
		if(chngdir("/usr"))
		    su_err(FATAL);
		sprintf(syscmd, "tar xpbf 10 /dev/rrx%d ./sys", rxunit);
	    	iflop("SYSGEN #3");
#ifdef	DEBUG
		printf("%s%s\n", debug, syscmd);
#endif	DEBUG
		while(system(syscmd) != 0) {
		    if(retry("SYSGEN FILES LOAD"))
			continue;
		    else
			su_err(FATAL);
		}
		sync();
	    	rflop("SYSGEN #3");
	    }
	    fmnt("SYSGEN #4");
	    if(cputyp[tpi].p_sid == NSID) {
		chngdir("/usr/sys/ovdev");
		system("cp /mnt/asmfix? .");
		system("ar x /mnt/LIB2_ov");
	    } else {
		chngdir("/usr/sys/net");
		system("ar x /mnt/LIB3_id");
	    }
	    system("chog sys *");
	    system("chmod 444 *");
	    fmnt(0);
	    sync();
	    rflop("SYSGEN #4");
	    chngdir("/usr/sys");
	    if(cputyp[tpi].p_sid == SID)
		system("chmod 755 sys dev net");
	    else
		system("chmod 755 ovsys ovdev ovnet");
	    system("chog sys *");
	    if(chngdir(homedir))
	    	su_err(FATAL);
	    printf("\n");
	}
/*
 * Remove unused sysgen libraries depending on
 * whether or not CPU has split I/D space.
 */
	if((phase == SUP1) && (rxflag == 0)) {
	    if(cputyp[tpi].p_sid == SID) {
		system("rm -f /usr/sys/conf/mch_ov.o");
		system("rm -rf /usr/sys/ovsys /usr/sys/ovdev /usr/sys/ovnet");
	    } else {
		system("rm -f /usr/sys/conf/mch_id.o");
		system("rm -f /usr/sys/sys/LIB1_id /usr/sys/dev/LIB2_id");
		system("rm -f /usr/sys/sys/*.o /usr/sys/net/*.o");
	    }
	    sync();
	}
/*
 * Select the appropriate .profile for /usr/sys.
 * Save a copy in /usr/skel/sys_profile, so osload will get
 * the correct one if it has to reload sysgen (GROSS!).
 * If crt is -1, assume console TTY type didn't change.
 */
	if(crt >= 0) {
	    if(crt == YES)
		system("cp /usr/skel/sys_crt.profil /usr/sys/.profile");
	    else
		system("cp /usr/skel/sys_prt.profil /usr/sys/.profile");
	    system("cp /usr/sys/.profile /usr/skel/sys_profile");
	}
/*
 * Ask the user for the hostname.
 */

g_hostn:
	switch(phase) {
	case SUP0:
		skipit = YES;
		break;
	case SUP1:
		skipit = YES;
		break;
	case SUP2:
		skipit = NO;
		break;
	case SUP3:
		printf("\nChange the system's hostname <y or n> ? ");
		if(yes(NOHELP) == YES)
		    skipit = NO;
		else
		    skipit = YES;
		break;
	}
	if(skipit == NO)
	    printf("\n****** NAMING YOUR ULTRIX-11 SYSTEM ******\n");
	while(1) {
	    if(skipit == YES)
		break;
	    do
		printf("\nPlease enter your system's hostname <? for help> ? ");
	    while(getline("h_hostn") <= 0);
	    if(strlen(lbuf) > 16) {
	    	printf("\nHostname too long (16 char max)!\n");
		continue;
	    }
	    j = 0;
	    for(i=0; lbuf[i]; i++) {
	    	if((lbuf[i] >= 'a') && (lbuf[i] <= 'z'))
	    		continue;
	    	if((lbuf[i] >= '0') && (lbuf[i] <= '9'))
	    		continue;
	    	if(lbuf[i] == '-')
	    		continue;
		j++;
	    }
	    if(j) {
	    	printf("\nBad Hostname (lowercase alphanumeric only)!\n");
		continue;
	    }
	    printf("%shostname is `%s' <y or n> ? ", confirm, lbuf);
	    if(yes(NOHELP) != YES)
		continue;
	    if((fi = fopen("/etc/rc", "r")) == NULL) {
	    	printf("\nCan't open /etc/rc file for reading!\n");
	    	su_err(FATAL);
	    }
	    if((fo = fopen("/tmp/rc.setup", "w")) == NULL) {
	    	printf("\nCan't create /tmp/rc.setup file!\n");
	    	su_err(FATAL);
	    }
	    while(1) {
	        if(fgets(syscmd, SCSIZE, fi) == NULL) {
		    fclose(fi);
		    fclose(fo);
		    break;
	        }
	        if(strlen(syscmd) == (SCSIZE - 1)) {
		    printf("\n/etc/rc format error: line too long!\n");
		    su_err(FATAL);
	        }
	        if(strncmp(syscmd, "hostname", 8) == 0)
		    fprintf(fo, "hostname %s\n", lbuf);
	        else
		    fprintf(fo, "%s", syscmd);
	    }
	    sprintf(syscmd, "cp /tmp/rc.setup /etc/rc");
	    if(system(syscmd) != 0) {
	    	printf("`%s' failed!\n", syscmd);
	    	su_err(FATAL);
	    }
	    unlink("/tmp/rc.setup");
	    sync();
	    shostname(&lbuf, strlen(lbuf));
	    break;
	}
/*
 * Select alternate version of more commands by 
 * mounting the /usr file system and executing
 * makefiles in /usr/bin & /usr/lib & /usr/ucb.
 * Also, unpack help data base (if loaded from floppy kit).
 * ONLY select commands once, use lock file to make sure.
 */
	while(1) {
	    if(phase != SUP1)
		break;
	    if(access(uclock, 0) == 0)
		break;		/* command select already done */
	    if(chngdir("/usr/bin"))
		su_err(FATAL);
	    printf("\n****** SELECTING%sSPLIT I/D COMMANDS ",
		(cputyp[tpi].p_sid == SID) ? " " : " NON-");
	    printf("(/usr/bin, lib, ucb) ******\n");
	    system("rm -f e ex vi view edit");
#ifdef	DEBUG
	    printf("%sex lex pcc yacc awk s5make", debug);
#endif	DEBUG
	    if(cputyp[tpi].p_sid == SID) {
		system("cp ex70 ex");
		system("cp lex70 lex");
		system ("cp pcc70 pcc");
		system("cp yacc70 yacc");
		system("cp awk70 awk");
		system("cp s5make70 s5make");
	    } else {
		system("cp ex40 ex");
		system("cp lex40 lex");
		system("cp pcc40 pcc");
		system("cp yacc40 yacc");
		system("cp awk40 awk");
		system("cp s5make40 s5make");
	    }
	    sync();
	    link("ex", "edit");
	    link("ex", "e");
	    link("ex", "vi");
	    link("ex", "view");
	    system("chog bin ex");
	    chmod("ex", 01755);
	    sync();
	    if(chngdir("/usr/lib"))
		su_err(FATAL);
#ifdef	DEBUG
	    printf("%sccom lint\n", debug);
#endif	DEBUG
	    if(cputyp[tpi].p_sid == SID) {
		system("ln /usr/c/oc2_id /usr/c/oc2");
		system("cp sendmail70 sendmail");
		system("cp ccom70 ccom");
		system("chog bin ccom");
		chmod("ccom", 0755);
		system("cp lint170 lint1");
	    } else {
		system("ln /usr/c/oc2_ov /usr/c/oc2");
		system("cp sendmail40 sendmail");
		system("cp ccom140 ccom1");
		system("cp ccom240 ccom2");
		system("chog bin ccom1 ccom2");
		chmod("ccom1", 0755);
		chmod("ccom2", 0755);
		system("cp lint140 lint1");
	    }
	    sync();
	    if(rxflag) {
		if(chngdir("help"))
		    su_err(FATAL);
		system("unpack U11_help >/dev/null 2>&1");
		sync();
	    }
	    if(chngdir("/usr/ucb"))
		su_err(FATAL);
	    if(cputyp[tpi].p_sid == SID)
		system("cp Mail70 mail");
	    else
		system("cp Mail40 mail");
	    sync();
	    if(chngdir(homedir))
		su_err(FATAL);
	    creat(uclock, 0444);	/* say command select done */
	    sync();
	    break;
	}
/*
 * Remove alternate command files (/usr/bin & /usr/lib).
 * ONLY if lock file exists (selection done).
 * It is possible this could happen more than once, but
 * that is ok.
 */
	while(1) {
	    if(phase != SUP1)
		break;
	    if(access(uclock, 0) != 0)
		break;		/* select not done yet, CAN'T HAPPEN */
#ifdef	DEBUG
	    printf("%sremove /usr/bin & /usr/lib commands <y or n> ? ", debug);
	    if(yes(NOHELP) != YES)
		break;
#endif	DEBUG
	    if(chngdir("/usr/bin"))
		su_err(FATAL);
	    system("rm -f ex40 lex40 pcc40 yacc40 awk40 s5make40");
	    system("rm -f ex70 lex70 pcc70 yacc70 awk70 s5make70");
	    sync();
	    if(chngdir("/usr/lib"))
		su_err(FATAL);
	    system("rm -f ccom70 ccom140 ccom240 lint140 lint170");
	    system("rm -f sendmail40 sendmail70");
	    if(cputyp[tpi].p_sid == SID) {
		unlink("/usr/c/oc2_ov");
		unlink("ccom1");
		unlink("ccom2");
	    } else {
		unlink("/usr/c/oc2_id");
		unlink("ccom");
	    }
	    sync();
	    if(chngdir("/usr/ucb"))
		su_err(FATAL);
	    system("rm -f Mail40 Mail70");
	    sync();
	    if(chngdir(homedir))
		su_err(FATAL);
	    break;
	}
/*
 * Set up LPR printer ports, if any.
 */
	while(1) {
	    if(phase < SUP2)
		break;
	    while(1) {
		printf("\nSet up line printer spooler and printer ports <y or n> ? ");
		j = yes(HELP);
		if(j == HELP) {
		    phelp("h_lpr");
		    continue;
		} else
		    break;
	    }
	    if(j == NO)
		break;
	    system("/usr/etc/lprsetup");
	    break;
	}

/*
 * Find out if user wants to set up user file
 * systems at this time, do setup if so.
 */

	while(1) {
	    if((phase < SUP2) && (ontarget == YES))
		break;
	    while(1) {
		if(phase <= SUP2)
		    printf("\nSet up user file systems");
		else
		    printf("\nChange (or set up new) user file systems");
		printf(" <y or n> ? ");
		j = yes(HELP);
		if(j == HELP) {
		    phelp("h_cufs");
		    continue;
		} else
		    break;
	    }
	    if(j == NO)
		break;
	/*
	 * Following depends on drive type info obtained by
	 * make special files code. This is ok because that
	 * portion of the MSF code is always executed.
	 */
	    if(pdconf(MKFS) == 0) {
		phelp("no_disks");	/* Sorry, no disks available! */
		break;
	    }
	    prtc();
	    phelp("dsk_mntd");	/* tell user to make sure disks mounted */
	    prtc();
	/*
	 * Get drive type info again.
	 * This ensures MSCP disk sizes correct and
	 * on-line status is correct.
	 */
	    for(dip=di_info; dip->di_typ; dip++) {
		if(dip->di_nunit == 0)
		    continue;
		(*dip->di_func)(MKFS, dip, mem);
	    }
	    for(dip=di_info; dip->di_typ; dip++) {
		if(dip->di_nunit == 0)
		    continue;		/* controller not configured */
		for(i=0; i<dip->di_nunit; i++) {
		    if(dip->di_dt[i] == 0)
			continue;	/* non-existent drive */
		    if(sd_info[dip->di_dt[i]].sd_flags&SD_FLOPPY)
			continue;	/* we don't do floppy disks */
		    if(sd_info[dip->di_dt[i]].sd_flags&SD_ML11)
			continue;	/* we don't do ML11 disks */
/* TODO: system disk cntlr number HP HM HJ, RA, RC, RQ ????? */
		    if((sd_info[dip->di_dt[i]].sd_flags&SD_SYSDSK) &&
			(i == sdunit) && (dip->di_cn == sdcntlr) &&
			((sd_info[dip->di_dt[i]].sd_flags&SD_USRDSK) == 0))
			    continue;	/* system disk, no free file systems */
		    if(usedisk(i, dip) == NO)
			continue;	/* user says don't use this disk drive */
		    setdisk(i, dip);	/* setup user file system(s) on disk */
		}
	    }
	    break;
	}
/*
 * Load and/or unload optional software.
 * Call setup.osl to do the actual loading/unloading.
 * Retry if setup.osl fails.
 * If not on the target CPU, assume it has no load device
 * and load optional software during phase 1.
 */
	while(1) {
	    if((phase < SUP2) && (ontarget == YES))
		break;
	    if((phase == SUP1) && (ontarget == NO))
		phelp("ntp_warn");
	    while(1) {
		printf("\nLoad/unload optional software <y or n> ? ");
		j = yes(HELP);
		if(j == HELP) {
		    phelp("h_osl");
		    continue;
		} else
			break;
	    }
	    sprintf(syscmd, "setup_osl %d %.2s %d %c %d", cputyp[tpi].p_type,
		loadev, rxflag ? rq_dt[rxunit] : 0, loadev[3], rd2);
	    if((fo = fopen(osload, "w")) != NULL) {
		fprintf(fo, "/.setup/%s\n", syscmd);
		fclose(fo);
		chmod(osload, 0755);
		system("chog bin /bin/osload");
	    } else
		printf("\nCAUTION: can't create %s!\n", osload);
	    if(j == NO)
		break;
	    system(syscmd);
/*
 * TODO: setup_osl sometimes returns an error status,
 *	   has to do with using <CTRL/C> during setup_osl.
	    if(system(syscmd) != 0) {
		printf("\nOptional software load/unload failed!\n");
		printf("\nTry again <y or n> ? ");
		if(yes(NOHELP) == YES)
		    continue;
		else
		    break;
	    }
 * TODO: end
 */
	    sync();
	    break;
	}
/*
 * Ask the user if the /usr/spool directory
 * should be coverted to a symbolic link.
 *
 *	/usr/spool can already be a symbolic link.
 *	Target file system must have enough free space.
 *	Current and target /usr/spool can't be same directories.
 * TODO:	what if user types <CTRL/C>?
 */
	while(1) {
	    if(phase < SUP2)
		break;
	    while(1) {
		if(phase == SUP2)
		    printf("\nMake ");
		else
		    printf("\nMake (or change) ");
		printf("symbolic link for /usr/spool <y or n> ? ");
		j = yes(HELP);
		if(j == HELP) {
		    phelp("h_ussl");
		    continue;
		} else
		    break;
	    }
	    if(j == NO)
		break;
	    while(1) {
		do {
		    printf("\nSymbolic link base directory name ");
		    printf("(? for help): ");
		} while(getline("h_slbdn") <= 0);
		if(lbuf[0] == '/')
		    p = &lbuf[1];
		else
		    p = &lbuf[0];
		if(strlen(p) > DIRSIZ) {
		    printf("\nName too long (%d char max)!\n", DIRSIZ);
		    continue;
		}
		sprintf(&sl_bdn, "/%s", p);
		sprintf(&sl_dst, "%s/spool", sl_bdn);
		break;
	    }
	    /*
	     * Make sure destination base directory exists.
	     */
	    if(lstat(&sl_bdn, &statb) < 0) {
		printf("\n%s does not exist!\n", &sl_bdn);
		continue;
	    }
	    if((statb.st_mode&S_IFMT) != S_IFDIR) {
		printf("\n%s is not a directory!\n", &sl_bdn);
		continue;
	    }
	    /*
	     * Make sure /usr/spool exists and see if
	     * it is a symbolic link.
	     */
	    if(lstat(sl_usd, &statb) < 0) {
		printf("\n%s does not exist!\n", sl_usd);
		su_err(FATAL);
	    }
	    if((statb.st_mode&S_IFMT) != S_IFDIR) {
		if((statb.st_mode&S_IFMT) == S_IFLNK) {
		    sl_islink = YES;
		    i = readlink(sl_usd, &sl_rlb, BUFSIZ);
		    if(i <= 0) {
			printf("\nreadlink on %s failed!\n", sl_usd);
			continue;
		    } else
			sl_rlb[i] = '\0';
		    if(i > SL_MAXPL) {
			printf("\n%s symbolic link pathname too long!\n",
			    sl_usd);
			continue;
		    } else {
			sprintf(&sl_src, "%s", sl_rlb);
			/* save src directory base name for later use */
			for(p = &sl_rlb[1]; (*p != '/'); p++);
			*p = '\0';
			sprintf(&sl_sbdn, "%s", &sl_rlb);
		    }
		} else {
		    printf("\n%s is not a directory!\n", sl_usd);
		    su_err(FATAL);
		}
	    } else
		sprintf(&sl_src, "%s", sl_usd);	/* src dir is /usr/spool */
	    if(strcmp(&sl_src, &sl_dst) == 0) {
		printf("\nCan't move spool from %s to %s!\n", &sl_src, &sl_dst);
		continue;
	    }
	    /*
	     * Mount the dst file system.
	     * Mount the src file system, only if
	     * /usr/spool was already a symbolic link.
	     * Dismount first, incase already mounted.
	     */
	    sl_mnt(UMOUNT, NOERR, &sl_bdn);
	    if(sl_mnt(MOUNT, ERR, &sl_bdn))
		continue;
	    if(sl_islink) {
		sl_mnt(UMOUNT, NOERR, &sl_sbdn);
		if(sl_mnt(MOUNT, ERR, &sl_sbdn))
		    continue;
	    }
	    printf("\nSize of %s in Kbytes:\n\n", &sl_src);
	    sprintf(&syscmd, "du -s %s", &sl_src);
	    system(&syscmd);
	    printf("\nFile system free space (see %s):\n\n", &sl_bdn);
	    system("df");
	    printf("\nIs there room for %s in %s <y or n> ? ", sl_usd, &sl_bdn);
	    if(yes(NOHELP) != YES)
		continue;
	    /*
	     * Make sure the dst directory exists.
	     */
	    if(lstat(&sl_dst, &statb) < 0) {	/* dir does not exist */
		if(mkdir(&sl_dst, 0775) < 0) {	/* create it */
		    printf("\nCan't make %s directory!\n", &sl_dst);
		    continue;
		}
	    } else {				/* dir exists, check it */
		if((statb.st_mode&S_IFMT) != S_IFDIR) {
		    printf("\n%s exists, but is not a directory!\n", &sl_dst);
		    continue;
		}
	    }
	    /*
	     * Set protection modes and ownership just to be sure.
	     * Values are bogus, but used because of history.
	     */
	    sprintf(&syscmd, "chmod 775 %s", &sl_dst);
	    system(&syscmd);
	    sprintf(&syscmd, "chown root %s", &sl_dst);
	    system(&syscmd);
	    sprintf(&syscmd, "chgrp other %s", &sl_dst);
	    system(&syscmd);
	    printf("\nMoving files from %s to %s...\n", &sl_src, &sl_dst);
	    sprintf(&syscmd, "cd %s; tar cf - . | (cd %s; tar xpf -)",
		&sl_src, &sl_dst);
	    if(system(&syscmd) != 0) {
		printf("\nTAR command failed: cannot move files!\n");
		continue;
	    }
	    /*
	     * Remove the old /usr/spool files (src directory).
	     * If /usr/spool was already a symbolic link,
	     * unlink /usr/spool and rm -r src directory.
	     * Otherwise, just rm -r /usr/spool.
	     */
	    if(sl_islink) {	 	/* If /usr/spool already a symlink */
		unlink(sl_usd);		/* unlink it & blow away src dir */
		sprintf(&syscmd, "rm -rf %s", sl_src);
		system(&syscmd);
	    } else			/* else, blow away /usr/spool */
		system("rm -rf /usr/spool");
	    /*
	     * Make a symbolic link from dst dir to /usr/spool.
	     */
	    printf("\nMaking Symbolic link: %s -> %s\n", sl_usd, &sl_dst);
	    if(symlink(&sl_dst, sl_usd) < 0) {
		printf("\nCan't create symbolic link: %s -> %s\n",
		    sl_usd, &sl_dst);
		continue;
	    }
	    /*
	     * Dismount dst file system.
	     * Dismount src file system, only if
	     * /usr/spool was already a symbolic link.
	     */
	    sl_mnt(UMOUNT, NOERR, &sl_bdn);
	    if(sl_islink)
		sl_mnt(UMOUNT, NOERR, &sl_sbdn);
	    break;
	}
	supdone(mem);
}

/*
 * Mount/umount file system for symbolic links.
 *
 * opr	= mount or umount
 * err	= allow or ignore errors
 * dir	= directory name to mount
 *
 * Mount/umount by directory name requires an entry for
 * the file system in the /etc/fstab.
 */

sl_mnt(opr, err, dir)
int	opr;
int	err;
char	*dir;
{
	register int i;
	char	cmd[100];

	sprintf(&cmd, "/etc/%s %s%s", (opr==MOUNT) ? "mount" : "umount",
	    dir, (err==ERR) ? " " : " >/dev/null 2>&1");
	i = system(&cmd);
	if((err == ERR) && (i != 0)) {
	    printf("\n%s of %s failed!\n",
		(opr==MOUNT) ? "Mount" : "Dismount", dir);
	    printf("%s must exist and be in the /etc/fstab!\n", dir);
	}
	return(i);
}

/* pmsg(str)
char	**str;
{
	register int i;

	for(i=0; str[i]; i++)
		printf("\n%s", str[i]);
	fflush(stdout);
} */

/*
 * Print a help message.
 * Call setup_help and pass along the message name.
 */

phelp(str)
char	*str;
{
	register int i;

	i = fork();
	if(i == -1) {
	    printf("\nCan't call setup_help (fork failed)!\n");
	    return;
	}
	if(i == 0) {
	    execl("/.setup/setup_help", "setup_help", str, (char *)0);
	    exit();
	}
	while(wait(0) != -1) ;
}

char	line[10];

yes(hlp)
{
	register int i;

yorn:
	fflush(stdout);
	for(i=0; i<10; i++) {
		line[i] = getchar();
		if(line[i] == '\n') {
			line[i] = '\0';
			break;
		}
	}
	if(i > 4) {
ynerr:
		printf("\nPlease answer yes or no ? ");
		goto yorn;
	}
	if((strcmp(line, "yes") == 0) || (strcmp(line, "y") == 0))
		return(YES);
	else if((strcmp(line, "no") == 0) || (strcmp(line, "n") == 0))
		return(NO);
	else if((hlp==HELP) && ((line[0]=='?') || (strcmp(line, "help")==0)))
		return(HELP);
	else
		goto  ynerr;
}

intr()
{
	signal(SIGINT, intr);
	ab_flag = 1;
	longjmp(savej, 1);
}

retry(str)
char *str;
{

	printf("\n%s FAILED: try again <y or n> ? ", str);
	if(yes(NOHELP) == YES)
		return(1);
	else
		return(0);
}

iflop(fn)
char	*fn;
{
	printf("\n\7\7\7Insert (%s) diskette into RX%d unit %d",
		fn, rq_dt[rxunit], rxunit);
	if(rq_dt[rxunit] == RX50)
	    	printf(" %s", rxpos(rxunit));
	printf("\n");
	prtc();
}

rflop(fn)
char	*fn;
{
	printf("\n\7\7\7Remove (%s) diskette from RX%d unit %d",
		fn, rq_dt[rxunit], rxunit);
	if(rq_dt[rxunit] == RX50)
	    	printf(" %s", rxpos(rxunit));
	printf("\n");
	prtc();
}

char	*rxp_tl = "(top/left)";
char	*rxp_lr = "(lower/right)";

rxpos(unit)
{
	switch(unit) {
	case 1:
		return(rxp_tl);
	case 2:
		return((rd2 == YES) ? rxp_tl : rxp_lr);
	case 3:
		return(rxp_lr);
	default:
		return("(?)");
	}
}

/*
 * Get a line of text from the terminal,
 * replace the newline with a NULL.
 * Return the string length (not counting the NULL).
 * Return 0 if the user typed only <RETURN>.
 * Use lbuf[] as the buffer and LBSIZE as limit.
 * If ? or help is typed, print the help message, if
 * one is available, appologize if not. Return -1 after help.
 */

char	*badline = "\n\7\7\7Bad input line, please try again!\n";

getline(hlp)
char	*hlp;
{
	register int cc, ovflow;

	ovflow = 0;
	fflush(stdout);
	while(1) {
	    lbuf[0] = '\0';
	    if(fgets(&lbuf, LBSIZE, stdin) == NULL) {
		printf("%s", badline);
		return(-1);	/* reprint message */
	    }
	    for(cc=0; lbuf[cc]; cc++) ;
	    if((cc == 0) || (lbuf[0] == '\n'))
		return(0);	/* user typed only <RETURN> */
	    if(lbuf[cc-1] != '\n') {	/* line too long */
		ovflow++;
		continue;
	    }
	    if(ovflow) {
		printf("\nLine too long, please try again!\n");
		return(-1);
	    }
	    cc--;
	    lbuf[cc] = '\0';
	    if((lbuf[0] == '?') || (strcmp("help", lbuf) == 0)) {
		if(hlp)
			phelp(hlp);
		else
			printf("\nSorry no additional help available!\n");
		return(-1);
	    }
	    return(cc);
	}
}

prtc()
{
	printf("\nPress <RETURN> to continue: ");
	fflush(stdout);
	while(getchar() != '\n') ;
}
/*
 * Create /etc/ttys and /etc/ttytype.
 * First line depends on console CRT or hardcopy.
 * Remaining lines depend on _ntty from kernel.
 *
 *		ttys		ttytype
 * CRT		22console	vt100	console
 * Hardcopy	24console	dw3	console
 */

#define	TTMAX	80		/* MAX terminal type name length */
char	ttylast[TTMAX+1];	/* Last tty type name used (for default) */
char	*tt_vt100 = "vt100";
char	*tt_dw3 = "dw3";

bttys(crt, ntty, npty)
{
	register int i;
	FILE	*fi, *fo;
	char	p, n;
	int	ask;
	char	*tp;

	if((fo = fopen("/tmp/ttys.setup", "w")) == NULL) {
		printf("\nCan't create /tmp/ttys.setup file!\n");
		return(-1);
	}
	if(crt == YES)
		fprintf(fo, "22console\n");
	else
		fprintf(fo, "24console\n");
	if(ntty)
	    for(i=0; i<ntty; i++)
		fprintf(fo, "00tty%02d\n", i);
	if(npty > 0) {
	    for(i=0; i<npty; i++) {
		p = 'p' + (i / 16);
		if((i % 16) < 10)
		    n = '0' + (i % 16);
		else
		    n = 'a' + ((i % 16) - 10);
		fprintf(fo, "00tty%c%c\n", p, n);
	    }
	}
	fclose(fo);
	sprintf(syscmd, "cp /tmp/ttys.setup /etc/ttys");
	if(system(syscmd) != 0) {
		printf("\n`%s' failed!\n", syscmd);
		return(-1);
	}
	unlink("/tmp/ttys.setup");
	sync();
	if(phase == SUP3) {
	    printf("\nWas number of pseudo TTYs the only change <y or n> ? ");
	    if(yes(NOHELP) == YES) {
		printf("\nNo need to remake /etc/ttytype file!\n");
		return;
	    }
	}
	ttylast[0] = '\0';	/* init default tty type to no default */
	if((fo = fopen("/tmp/ttytype.setup", "w")) == NULL) {
		printf("\nCan't create /tmp/ttytype.setup file!\n");
		return(-1);
	}
	tp = asktt(CONSOLE, 0, crt);	/* ask for console terminal type */
	fprintf(fo, "%s\tconsole\n", tp);
	if(ntty) {
	    while(1) {
		printf("\nAssume (for now) all remaining terminals ");
		printf("are vt100 <y or n> ? ");
		ask = yes(HELP);
		if(ask == HELP) {
		    phelp("h_gta");
		    continue;
		}
		break;
	    }
	    for(i=0; i<ntty; i++) {
		if(ask == YES)
		    tp = tt_vt100;
		else
		    tp = asktt(OTHER, i, NO);
		fprintf(fo, "%s\ttty%02d\n", tp, i);
	    }
	}
	fclose(fo);
	sprintf(syscmd, "cp /tmp/ttytype.setup /etc/ttytype");
	if(system(syscmd) != 0) {
		printf("\n`%s' failed!\n", syscmd);
		return(-1);
	}
	unlink("/tmp/ttytype.setup");
	sync();
}

/*
 * Ask user for terminal type (for ttytype file entry).
 *
 *  tt	= terminal type (console/other).
 *  tn	= tty## (0 for console).
 * crt	= console is CRT or PRT.
 *
 * NOTE: some variables used by this routine are
 *	 defined just in front of bttys(), above.
 */


asktt(tt, tn, crt)
int	tt;
int	tn;
int	crt;
{
	register int cc;
	char	*p;

	while(1) {
	    do {
		printf("\nTerminal type for ");
		if(tt == CONSOLE)
		    printf("CONSOLE terminal ");
		else
		    printf("TTY%02d ", tn);
		if(ttylast[0])
		    p = &ttylast;
		else if(tt == CONSOLE)
		    p = (crt==YES) ? tt_vt100 : tt_dw3;
		else
		    p = tt_vt100;
		printf("< %s > ? ", p);
	    } while((cc = getline("h_gtt")) < 0);
	    if(cc == 0)		/* user typed <RETURN>, use default */
		return(p);
	    if(cc == 1)
		continue;	/* in case user type y or n */
	    if((strcmp("yes", &lbuf) == 0) || (strcmp("no", &lbuf) == 0))
		continue;	/* in case user types yes or no */
	    if(cc > TTMAX) {
		printf("\nName too long (%d char max)!\n", TTMAX);
		continue;
	    }
	    sprintf(&ttylast, "%s", &lbuf);
	    return(&ttylast);
	}
}

/*
 * Zap the newline at the end of a string
 * read by fgets(), i.e., replace nl with 0.
 */

zapnl(s)
char	*s;
{
	register int i;

	for(i=0; s[i]; i++)
		if(s[i] == '\n') {
			s[i] = '\0';
			break;
		}
}

/*
 * Write the current phase number to 
 * the setup.info file.
 * No error checking, because file may not
 * even exist.
 */

char	pnbuf;

wrtpn(pn)
{
	register int fd;

	if((fd = open(info, 1)) < 0)
		return;
	pnbuf = pn + '0';
	write(fd, (char *)&pnbuf, 1);
	close(fd);
	sync();
}

/*
 * Routines to fish the disk drive type
 * info out of the running kernel (/dev/mem).
 * BAD DEPENDENCY: routines need to know how each
 * disk driver defines NED and drive types.
 *
 * usage - identifies section of code calling ??_gdti(),
 *	   ONLY USED BY ra_gdti().
 *	   MSF, called by make special files code.
 *	   MKFS, called by setup user file systems code.
 * dip   - pointer to entry in di_info[].
 * mem   - open file descriptor for /dev/mem.
 *
 * Note:
 *	 The data structures used by these routines
 *	 are located before main(), so code in main can
 *	 access them.
 */

/*
 * RK05
 *	NED = -1
 *	RK05 = 1
 */

rk_gdti(usage, dip, mem)
register struct di_info *dip;
{
	register int i, j;

	lseek(mem, (long)nl[X_NRK].n_value, 0);
	read(mem, (char *)&nrk, sizeof(nrk));
	lseek(mem, (long)nl[X_RK_DT].n_value, 0);
	read(mem, (char *)&rk_dt, sizeof(rk_dt));
	dip->di_nunit = nrk;
	for(i=0; i<nrk; i++) {
	    if(rk_dt[i] != 1)
		dip->di_dt[i] = 0;	/* NED (configured but not found) */
	    else {
		for(j=0; sd_info[j].sd_lname; j++)
		    if(sd_info[j].sd_type == RK05)
			break;
		dip->di_dt[i] = j;	/* save index into drive info table */
	    }
	}
}

/*
 * RP02/3
 *	NED = -1
 *	RP02 = 0
 *	RP03 = 1
 * Note: must read each configured RP02/3 unit to
 *	 force update of rp_dt[].
 */

rp_gdti(usage, dip, mem)
register struct di_info *dip;
{
	register int i, j;

	for(i=0; i<nrp; i++) {
	    mknod(dsk_sfn, 020400, (dip->di_rmaj<<8) | (i<<3));
	    if((j = open(dsk_sfn, 0)) >= 0) {
		read(j, (char *)&syscmd, 512);
		close(j);
	    }
	    unlink(dsk_sfn);
	}
	lseek(mem, (long)nl[X_NRP].n_value, 0);
	read(mem, (char *)&nrp, sizeof(nrp));
	lseek(mem, (long)nl[X_RP_DT].n_value, 0);
	read(mem, (char *)&rp_dt, sizeof(rp_dt));
	lseek(mem, (long)nl[X_RP_SIZES].n_value, 0);
	read(mem, (char *)&rp_sizes, sizeof(rp_sizes));
	dip->di_nunit = nrp;
	for(i=0; i<nrp; i++) {
	    if(rp_dt[i] < 0)
		dip->di_dt[i] = 0;	/* NED (configured but not found) */
	    else {
		for(j=0; sd_info[j].sd_lname; j++)
		    /* setup uses (RP02=2, RP03=3, so we add 2) */
		    if(sd_info[j].sd_type == (rp_dt[i] + 2))
			break;
		dip->di_dt[i] = j;	/* save index into drive info table */
	    }
	}
}

/*
 * MSCP (UDA50 RC25 RQDX RUX50)
 *	NED = 0
 *	See /usr/include/sys/ra_info.h for drive types.
 *
 * Note: (usage argument)
 *
 *	If usage == MSF (called from make special files code),
 *	just get drive type info. If usage == MKFS (called from
 *	setup user file systems code), get drive type info and
 *	force all possilbe drives on-line by attempting open.
 *	This ensures the size info is correct.
 *
 * Note: MSCP drive type info arrays are non-semetrical
 *	 ra_index must be used to access drive type info.
 */

int	raload = NO;		/* RA info already loaded */

ra_gdti(usage, dip, mem)
register struct di_info *dip;
{
	register int i, j;
	int	cn, ind;
	int	raunits;

/*
 * Read in the controller and drive type info.
 */
/*
 * OLDCODE: for MKFS must get drive type info
 *	   each time.
	if(raload == NO) {
*/
	    raload = YES;
	    lseek(mem, (long)nl[X_NUDA].n_value, 0);
	    read(mem, (char *)&nuda, sizeof(nuda));
	    lseek(mem, (long)nl[X_NRA].n_value, 0);
	    read(mem, (char *)&nra, MAXUDA);
	    lseek(mem, (long)nl[X_UD_SIZES].n_value, 0);
	    read(mem, (char *)&ud_sizes, sizeof(ud_sizes));
	    lseek(mem, (long)nl[X_RQ_SIZES].n_value, 0);
	    read(mem, (char *)&rq_sizes, sizeof(rq_sizes));
	    lseek(mem, (long)nl[X_RC_SIZES].n_value, 0);
	    read(mem, (char *)&rc_sizes, sizeof(rc_sizes));
	    lseek(mem, (long)nl[X_RA_MAS].n_value, 0);
	    read(mem, (char *)&ra_mas, sizeof(daddr_t)*nuda);
	    raunits = 0;
	    for(i=0; i<MAXUDA; i++)
	        raunits += nra[i];
	    lseek(mem, (long)nl[X_RA_INDEX].n_value, 0);
	    read(mem, (char *)&ra_index, MAXUDA);
	    lseek(mem, (long)nl[X_RA_CTID].n_value, 0);
	    read(mem, (char *)&ra_ctid, MAXUDA);
	/*
	 * If called from make user file systems code,
	 * make sure size and on-line information in ra_drv[]
	 * is updated by attempting to open each configured drive.
	 */
	    if(usage == MKFS) {
		for(i=0; i<nuda; i++)
		    for(j=0; j<nra[i]; j++) {
			ind = (dip->di_rmaj << 8) | (i << 6) | (j << 3) | 7;
		/* TODO: some user may have /dev/setup.dsf! */
		/*		check for it??? */
			mknod(dsk_sfn, 020400, ind);
			if((ind = open(dsk_sfn, 0)) >= 0)
			    close(ind);
			unlink(dsk_sfn);
		    }
	    }
	    lseek(mem, (long)nl[X_RA_DRV].n_value, 0);
	    read(mem, (char *)&ra_drv, sizeof(struct ra_drv) * raunits);
/*	}	*/
	cn = dip->di_cn;
	ind = ra_index[cn];
	dip->di_nunit = nra[cn];
	for(i=0; i<nra[cn]; i++) {
	    if(ra_drv[ind+i].ra_dt == 0) {
		dip->di_dt[i] = 0;	/* NED (configured but not found) */
	    } else {
		for(j=0; sd_info[j].sd_lname; j++)
		    if(sd_info[j].sd_type == ra_drv[ind+i].ra_dt)
			break;
		dip->di_dt[i] = j;	/* save index into drive info table */
	    }
	}
/*
 * Change the controller name from MSCP to
 * the real name (UDA50, RQDX, etc.).
 */
	dip->di_typ = ra_gcti(ra_ctid[cn]);
/*
 * Change the two character ULTRIX-11 mnemonic to
 * ra, rc, rd, rx according to the controller type.
 * Also, save RD/RX drive types in rq_dt[] if controller
 * is RQDX1/2/3 (for iflop()).
 */
	switch((ra_ctid[cn] >> 4) & 017) {
	case UDA50:
	case UDA50A:
	case KDA50:
		dip->di_name[1] = 'a';		/* RA60/RA80/RA81 */
		break;
	case KLESI:
		dip->di_name[1] = 'c';		/* RC25 */
		break;
	case RQDX1:
	case RQDX3:
		dip->di_name[1] = 'd';	/* RD31/RD32/RD51/RD52/RD53/RD54 */
		for(i=0; i<nra[cn]; i++)
		    rq_dt[i] = ra_drv[ind+i].ra_dt;
		if(rxflag) {
		    if((rq_dt[1] != RX50) && (rq_dt[1] != RX33))
			rd2 = YES;
		}
		break;
	case RUX1:
		dip->di_name[1] = 'x';		/* RUX50 */
		break;
	default:
		dip->di_name[1] = 'a';		/* UNKNOWN (can't happen) */
		break;
	}
}

/*
 * Return a pointer to the controller name string
 * according to the controller type ID.
 */
/* TODO: some names will change */

ra_gcti(ctid)
{
	switch((ctid >> 4) & 017) {
	case UDA50:
		return("UDA50");
	case KDA50:
		return("KDA50");
	case KLESI:
		return("KLESI");
	case UDA50A:
		return("UDA50A");
	case RQDX1:
		return("RQDX1");
	case RQDX3:
		return("RQDX3");
	case RUX1:
		return("RUX1");
	default:
		return("UNKNOWN");
	}
}

/*
 * RX02
 *	RX02 driver does not have drive type info.
 *	Always 2 RX02 if HX configured.
 */

hx_gdti(usage, dip, mem)
register struct di_info *dip;
{
	register int i;

	dip->di_nunit = 2;
	for(i=0; sd_info[i].sd_lname; i++)
	    if(sd_info[i].sd_type == RX02)
		break;
	dip->di_dt[0] = i;	/* unit 0 = RX02 */
	dip->di_dt[1] = i;	/* unit 1 = RX02 */
}

/*
 * RL01/2
 *	NED = -1
 *	RL01 = 10240
 *	RL02 = 20480
 */

rl_gdti(usage, dip, mem)
register struct di_info *dip;
{
	register int i, j;

	lseek(mem, (long)nl[X_NRL].n_value, 0);
	read(mem, (char *)&nrl, sizeof(nrl));
	lseek(mem, (long)nl[X_RL_DT].n_value, 0);
	read(mem, (char *)&rl_dt, sizeof(rl_dt));
	dip->di_nunit = nrl;
	for(i=0; i<nrl; i++) {
	    if(rl_dt[i] < 0)
		dip->di_dt[i] = 0;	/* NED (configured but not found) */
	    else {
		for(j=0; sd_info[j].sd_lname; j++)
		    if(sd_info[j].sd_type == rl_dt[i])
			break;
		dip->di_dt[i] = j;	/* save index into drive info table */
	    }
	}
}

/*
 * MASSBUS (HP HM HJ)
 *	NED = 0
 *	See /usr/include/sys/hp_info.h for drive types.
 *
 * Note: MASSBUS drive type info arrays are non-semetrical
 *	 hp_index must be used to access drive type info.
 */

int	hpload = NO;		/* HP info already loaded */

hp_gdti(usage, dip, mem)
register struct di_info *dip;
{
	register int i, j;
	int	cn, ind;
	int	hpunits;

/*
 * Read in the controller and drive type info.
 */
	if(hpload == NO) {
	    hpload = YES;
	    lseek(mem, (long)nl[X_NHP].n_value, 0);
	    read(mem, (char *)&nhp, MAXRH);
	    lseek(mem, (long)nl[X_HP_SIZES].n_value, 0);
	    read(mem, (char *)&hp_sizes, sizeof(hp_sizes));
	    hpunits = 0;
	    for(i=0; i<MAXRH; i++)
	        hpunits += nhp[i];
	    lseek(mem, (long)nl[X_HP_INDEX].n_value, 0);
	    read(mem, (char *)&hp_index, MAXRH);
	}
	lseek(mem, (long)nl[X_HP_DT].n_value, 0);
	read(mem, (char *)&hp_dt, hpunits);
	cn = dip->di_cn;
	ind = hp_index[cn];
	dip->di_nunit = nhp[cn];
	for(i=0; i<nhp[cn]; i++) {
	    if(hp_dt[ind+i] == 0)
		dip->di_dt[i] = 0;	/* NED (configured but not found) */
	    else {
		for(j=0; sd_info[j].sd_lname; j++)
		    if(sd_info[j].sd_type == hp_dt[ind+i])
			break;
		dip->di_dt[i] = j;	/* save index into drive info table */
	    }
	}
/*
 * Change the controller name from MASBUS to
 * the real name (RH11 or RH70).
 */
	if(cputype == 70)
	    dip->di_typ = "RH70";
	else
	    dip->di_typ = "RH11";
}

/*
 * RK06/7
 *	NED = -1
 *	RK06 = 0
 *	RK07 = 02000
 */

hk_gdti(usage, dip, mem)
register struct di_info *dip;
{
	register int i, j;

	lseek(mem, (long)nl[X_NHK].n_value, 0);
	read(mem, (char *)&nhk, sizeof(nhk));
	lseek(mem, (long)nl[X_HK_DT].n_value, 0);
	read(mem, (char *)&hk_dt, sizeof(hk_dt));
	lseek(mem, (long)nl[X_HK_SIZES].n_value, 0);
	read(mem, (char *)&hk_sizes, sizeof(hk_sizes));
	dip->di_nunit = nhk;
	for(i=0; i<nhk; i++) {
	    if(hk_dt[i] < 0)
		dip->di_dt[i] = 0;	/* NED (configured but not found) */
	    else {
		for(j=0; sd_info[j].sd_lname; j++)
		    if(sd_info[j].sd_type == hk_dt[i])
			break;
		dip->di_dt[i] = j;	/* save index into drive info table */
	    }
	}
}

/*
 * Mount or unmount the /usr file system.
 *
 *  mnt = 0 for unmount, mnt = 1 for mount
 *
 * Don't mount if /usr already mounted, and
 * don't unmount if not mounted. Also,
 * don't unmount if setup didn't mount /usr.
 */

int	usrmntd = NO;
int	su_didit = NO;
char	*mntcmd = "/etc/mount /usr";
char	*umntcmd = "/etc/umount /usr";

/* TODO: routine not used */
usrmnt(mnt)
{
	switch(mnt) {
	case MOUNT:
		if(usrmntd == YES)
			break;
		if(access("/usr/bin", 0) == 0) {
			usrmntd = YES;
			su_didit = NO;
			break;
		}
		while(system(mntcmd) != 0) {
			if(retry("Mount of /usr file system"))
				continue;
			else
				return(1);
		}
		usrmntd = YES;
		su_didit = YES;
		break;
	case UMOUNT:
		if(usrmntd == NO)
			break;
		if(su_didit == NO)
			break;
		while(system(umntcmd) != 0) {
			if(retry("Dismount of /usr file system"))
				continue;
			else
				return(1);
		}
		usrmntd = NO;
		break;
	default:
		return(1);
	}
	return(0);
}

/*
 * Change directory,
 * print error message if chdir() fails.
 */

chngdir(dir)
char	*dir;
{
	if(chdir(dir) < 0) {
	    printf("\nCannot change directory to %s!\n", dir);
	    return(1);
	} else
	    return(0);
}

/*
 * Fatal error routine.
 * Depending on the error type (et),
 * print error messages, sync, and exit.
 */

su_err(et)
{
	signal(SIGINT, SIG_IGN);
	chdir("/");
	system("/etc/umount -a >/dev/null 2>&1");
	sync();
	if(et) {
	    if(et == FATAL)
		printf("\n****** FATAL ERROR DURING SETUP PHASE %d ******",
		    phase);
	    phelp("abort");
	}
	exit(1);
}

/*
 * Normal exit routine,
 * called at the end of each phase.
 * TODO: need messages about goto IG do sysgen.
 *	   may exec a copy of sysgen?????
 */

char	*cd_warn = "\nWARNING: alternate commands (*40 & *70) not removed from ";

supdone(mem)
{
	int	clean;

	signal(SIGINT, SIG_IGN);
	if(mem > 0)
	    close(mem);		/* I know, but I don't like loose ends! */
	chdir(homedir);
	if(ontarget == YES)
	    printf("\n\7\7\7****** ULTRIX-11 Setup Phase %d Completed ******\n",
		phase);
#ifdef	DEBUG
	if(ontarget == YES) {
	    printf("%sdo end of phase %d cleanup <y or n> ? ", debug, phase);
	    if(yes(NOHELP) == YES)
		clean = YES;
	    else
		clean = NO;
	}
#else	DEBUG
	clean = YES;
#endif	DEBUG
	if((clean == NO) || (phase == SUP3))
	    wrtpn(phase);
	else if((phase == SUP1) && (ontarget == NO))
	    wrtpn(SUP1);
	else
	    wrtpn(phase+1);
	if((phase == SUP1) && (ontarget == YES)) {
	    unlink(rclock);
	    unlink(uclock);
	}
	if((phase == SUP2) && (clean == YES)) {
	    sprintf(syscmd, "mv /profile /.profile");
	    if(system(syscmd) != 0)
		printf("\nWARNING: `%s' failed!\n", syscmd);
	    chmod("/.profile", 0644);
	    unlink("/profile");
	    sync();
	}
	chdir("/");
/*
 * OLDCODE
	if(usrmnt(UMOUNT))
	    printf("\nWARNING: dismount of /usr failed!\n");
*/
	system("/etc/umount -a >/dev/null 2>&1");
	sync();
	if(phase == SUP1) {
	    if(ontarget == YES)
		phelp("eop1_otp");
	    else
		phelp("eop1_ntp");
	} else if(phase == SUP2)
	    phelp("eop2");
	else
	    phelp("eop3");
	exit(0);
}

/*
 * Print disk configuration.
 * Printout altered slightly by usage argument:
 *
 *  usage = MSF (make special files)
 *  usage = MKFS (make user file systems)
 *
 * If usage == MKFS, return number of available disks.
 */

pdconf(usage)
{
	register struct di_info *dip;
	register int j, d;

	printf("\nULTRIX-11 System's Disk Configuration:\n");
	if(usage == MKFS) {
	    printf("\n* = SYSTEM DISK -- no partitions available for user ");
	    printf("file systems.");
	}
	printf("\nX = disk not configured, ");
	printf("NED = disk configured but not present.\n");
	printf("\nDisk    Cntlr  System  Unit  Unit  Unit  Unit  ");
	printf("Unit  Unit  Unit  Unit");
	printf("\nCntlr   #      Disk    0     1     2     3     ");
	printf("4     5     6     7");
	printf("\n-----   -----  ------  ----  ----  ----  ----  ");
	printf("----  ----  ----  ----");
	d = 0;
	for(dip=di_info; dip->di_typ; dip++) {
	    if(dip->di_nunit == 0)
		continue;
	    printf("\n%-6s  %-5d  ", dip->di_typ, dip->di_cn);
	    if((sd_info[dip->di_dt[sdunit]].sd_flags&SD_SYSDSK) == 0)
		printf("        ");
	    else
		printf("UNIT %c  ", sd_info[dip->di_dt[0]].sd_swap[2]);
	    for(j=0; j<8; j++) {
		if(j >= dip->di_nunit)
		    printf("X   ");
		else if(dip->di_dt[j] == 0)
		    printf("NED ");
		else {
		    printf("%-4s", sd_info[dip->di_dt[j]].sd_uname);
		    d++;
		}
		if((j == sdunit) && (usage == MKFS) &&
		   (sd_info[dip->di_dt[j]].sd_flags&SD_SYSDSK) &&
		   ((sd_info[dip->di_dt[j]].sd_flags&SD_USRDSK) == 0)) {
			printf("* ");
			d--;
		} else
			printf("  ");
	    }
	}
	printf("\n");
	return(d);
}

/*
 * Ask the user if this disk drive is to be
 * used for user file storage. Return YES or NO.
 *
 *	dip	drive info structure pointer
 *	unit	unit number
 */

usedisk(unit, dip)
register struct di_info *dip;
int	unit;
{
	register int j;

	while(1) {
	    pdoc(unit, dip);	/* print disk unit on cntlr message */
	    printf("file systems available.\n");
	    while(1) {
		printf("\nSet up user file system(s) on this disk ");
		printf("(? for help) <y or n> ? ");
		j = yes(HELP);
		if(j == HELP)
		    phelp("h_udisk");
		else
		    break;
	    }
	    return(j);
	}
}

/*
 * Set up user file system(s) on the specified disk drive.
 * Make the fstab entry and, possilby, make an empty
 * file system.
 */

setdisk(unit, dip)
register struct di_info *dip;
int	unit;
{
	register int i, j;
	int	fs, fd;
	struct	filsys	*fp;
	int	isfs, isv3fs;
	int	didmkfs, opt;
	int	fseflag;
	struct stat statb;

	spt(unit, dip);		/* Setup partition table for this disk */
	while(1) {
	    do {
		if(ppt(unit, dip)) {
		    printf("\nNo more partitions available on this disk!\n");
		    return;
		}
		if(phase == SUP3)
		    phelp("ufs_warn");
		printf("\nSelect a disk partition (? for help, ");
	        printf("`.' if done with this disk).\n");
		printf("\nDisk partition < ");
		for(i=0; i<8; i++)
		    if(pt_info[i].pt_flags == 0)
			printf("%d ", i);
		printf("> ? ");
	    } while(getline("h_sdp") <= 0);
	    if(strlen(lbuf) != 1) {
		printf("\nInvalid response!\n");
		continue;
	    }
	    if(lbuf[0] == '.') {
		printf("%sfinished with this disk <y or n> ? ", confirm);
		if(yes(NOHELP) == YES)
		    return;
		else
		    continue;
	    }
	    if((lbuf[0] < '0') || (lbuf[0] > '7')) {
		printf("\n%s - bad partition number!\n", lbuf);
		continue;
	    }
	    fs = lbuf[0] - '0';
	    if(pt_info[fs].pt_flags&PT_NUP) {
		printf("\nSorry, %s disk does not use partition %d!\n",
		    sd_info[dip->di_dt[unit]].sd_uname, fs);
		continue;
	    }
	    if(pt_info[fs].pt_flags&PT_SYS) {
		printf("\nSorry, partition %d used by the system!\n", fs);
		continue;
	    }
	    if(pt_info[fs].pt_flags&PT_USER) {
		printf("\nSorry, partition %d is already used!\n", fs);
		continue;
	    }
	    if(pt_info[fs].pt_flags&PT_OP) {
		printf("\nSorry, partition %d ", fs);
		printf("overlaps an existing file system!\n");
		continue;
	    }
	    printf("%sset up user file system on partition %d <y or n> ? ",
		confirm, fs);
	    if(yes(NOHELP) != YES)
		continue;
	    /*
	     * Set up superblock volname and fsname,
	     * mounted on directory name, and
	     * block/raw disk names.
	     */
	    sprintf(bdisk, "%s", dsfn(BLOCK, unit, fs, dip));
	    sprintf(rdisk, "%s", dsfn(RAW, unit, fs, dip));
	    if((sd_info[dip->di_dt[unit]].sd_flags&SD_SYSDSK) &&
	      (unit == sdunit) && (dip->di_cn == sdcntlr))
		sprintf(volname, "sd_%.3s", &bdisk[5]);
	    else
		sprintf(volname, "ud_%.3s", &bdisk[5]);
	    for(ufsnum=1; ufsnum<100; ufsnum++) {
		sprintf(fsname, "user%d", ufsnum);
		sprintf(mntdir, "/user%d", ufsnum);
		if(access(&mntdir, 0) == 0)
		    continue;	/* directory exists, this name already used */
		else
		    break;
/* TODO: may want to warn if bdisk or mntdir already in fstab */
	    }
	/* TODO: may ask user for 6 char name ????? */
	    if(ufsnum >= 100) {		/* PANIC: all 99 names used! */
		printf("\nSorry, all 99 possible file system names used!\n");
		su_err(FATAL);
	    }
	    /*
	     * Warn the user if a file system exists on the selected partition.
	     * If it's not a V3.0 1K file system, say can access via rawfs(8).
	     * Check s_fsize & s_isize to see if file system exists,
	     * check superblock magic numbers to see if its a V3.0 file system.
	     */
	    opt = 0;	/* opt tells how to deal with existing file systems */
	    fseflag = NO;
	    if((fd = open(rdisk, 0)) < 0) {
		printf("\n%s: open failed!\n", rdisk);
		su_err(FATAL);
	    }
	    j = sizeof(struct filsys);
	    lseek(fd, (long)(SUPERB*BSIZE), 0);
	    if(read(fd, (char *)&syscmd, j) != j) {
		printf("\n%s: superblock read failed!\n", rdisk);
		su_err(FATAL);
	    }
	    close(fd);
	    fp = (struct filsys *)&syscmd;
	    isfs = YES;
	    isv3fs = NO;
	    if((fp->s_isize <= (SUPERB+1)) ||
	       (fp->s_fsize <= 0L) ||
	       (fp->s_fsize > 16777216L) ||
	       (fp->s_isize >= fp->s_fsize))
		isfs = NO;
	    if(isfs == YES) {	/* see if it's a V3.0 file system */
		if((fp->s_magic[0] != S_0MAGIC) ||
		   (fp->s_magic[1] != S_1MAGIC) ||
		   (fp->s_magic[2] != S_2MAGIC) ||
		   (fp->s_magic[3] != S_3MAGIC))
			isv3fs = NO;
		else
			isv3fs = YES;
	    }
	    printf("\nSuperblock check indicates: ");
	    if(isfs == NO)
		printf("no ");
	    else
		printf("\n\n\t");
	    if(isfs == YES) {
		if(isv3fs == YES)
		    printf("An ");
		else
		    printf("A non ");
		printf("ULTRIX-11 V3.0 ");
	    }
	    printf("file system exists on partition %d!", fs);
	    if(isfs == YES) {
		printf("\n\tSuperblock fsname  = %.6s", fp->s_fname);
		printf("\n\tSuperblock volname = %.6s", fp->s_fpack);
	    }
	    printf("\n");
	    if((isfs == YES) && (isv3fs == NO)) {
		phelp("ufs_fe1");
		printf("\nOk to overwrite existing file system <y or n> ? ");
		if(yes(NOHELP) != YES)
		    opt = 3;	/* preserve filsys, don't use, mark part. used */
	    }
	    if((isfs == YES) && (isv3fs == YES)) {
		phelp("ufs_fe1");
		while(1) {
		    do {
			phelp("ufs_fe2");
			printf("\nOption < 1 2 3 > ? ");
		    } while(getline(NOHELP) <= 0);
		    if((strlen(lbuf) != 1) ||
		       (lbuf[0] < '1') ||
		       (lbuf[0] > '3')) {
			    printf("\nInvalid option!\n");
			    continue;
		    }
		    opt = lbuf[0] - '0';
		    break;
		}
	    }
	    while(1) {
		didmkfs = NO;
		if((isfs == YES) && (isv3fs == NO)) {
		    if(opt == 3)
			j = NO;	/* preserve filsys, don't use it */
		    else
			j = YES;
		    break;
		}
		if((isfs == YES) && (isv3fs == YES)) {
		    if(opt == 1)
			j = YES;	/* overwrite file system, use it */
		    else
			j = NO;		/* (opt==2), preserve filsys, use it */
					/* (opt==3), preserve filsys, don't use */
		    break;
		}
		printf("\nMake an empty file system on partition ");
		printf("%d (? for help) <y or n> ? ", fs);
		j = yes(HELP);
		if(j == HELP)
		    phelp("h_mkfs");
		else
		    break;
	    }
	    if(j == NO) {
		if(opt != 3) {
		    printf("%salready a file system on partition %d <y or n> ? ",
			confirm, fs);
		    if(yes(NOHELP) != YES)
			continue;
		}
	    } else {		/* answer must have been YES */
		printf("%smake a file system on partition %d <y or n> ? ",
		    confirm, fs);
		if(yes(NOHELP) != YES)
		    continue;
		if(mfsdisk(unit, fs, dip))
		    continue;	/* mkfs failed */
		didmkfs = YES;
	    }
	    /*
	     * Mark partition, and any overlapping ones, used.
	     */
	    pt_info[fs].pt_flags |= PT_USER;
	    for(j=0; j<8; j++) {
		if(j == fs)
		    continue;
		if(pt_info[fs].pt_op&(1 << j))
		    pt_info[j].pt_flags |= PT_OP;
	    }
	    if(opt == 3) {
		if(fstab(FST_RMV, bdisk, mntdir))
		    printf("\nPartition %d (%s): /etc/fstab entry removed!\n",
			fs, bdisk);
		continue;	/* preserve filsys, but don't use it */
				/* mark partition used (done above) */
	    }
	    /*
	     * If the device (bdisk) and the fsname match an entry in the
	     * fstab and the fsname matches an existing mount directory,
	     * then, we assume this is an existing user file system.
	     * Otherwise, we treate it as a new file system.
	     */
	    if((isfs == YES) && (isv3fs == YES) && (opt == 2)) {
		sprintf(fmntdir, "/%.6s", fp->s_fname);
		if(fstab(FST_SCH, &bdisk, &fmntdir) &&
		   (access(&fmntdir, 0) == 0)) {
		    sprintf(fsname, "%.6s", fp->s_fname);
		    sprintf(mntdir, "%s", fmntdir);
		    fseflag = YES;
		}
	    }
	    /*
	     * Print fsname, volname, mounted on directory.
	     * User may need this info later.
	     * First time only, tell user values can be changed.
	     */
	    printf("\nSuperblock file system name (fsname):\t%s", fsname);
	    printf("\nSuperblock volume label (volname):\t%s", volname);
	    printf("\nUser file system will be mounted on:\t%s\n", mntdir);
	    if(firstfs == YES) {
		firstfs = NO;
		printf("\nNote: you can change these values, refer to System ");
		printf("\n      Management Guide chapter 4 for instructions.\n");
	    }
	    if((isfs == YES) && (didmkfs == NO)) {
		printf("\nRe-labeling file system superblock...\n");
		sprintf(syscmd, "/etc/labelit %s %s %s -n",
		    rdisk, fsname, volname);
		while(system(syscmd) != 0) {
		    if(retry(syscmd))
			continue;
		    else
			su_err(FATAL);
		}
	    }
	    /*
	     * Make the mounted on directory.
	     * We know it does not already exist, check above.
	     * Don't make mount directory if this is an existing file system
	     * being reused, mount directory already exists.
	     */
	    if(fseflag == NO) {
		sprintf(syscmd, "mkdir %s", mntdir);
		if(system(syscmd) != 0)
		    printf("\nWARNING: %s failed!\n", syscmd);
		chmod(&mntdir, 0755);
	    }
	    /*
	     * Mount the file system and chmod 0755.
	     * Make the lost+found directory.
	     */
	    sprintf(syscmd, "/etc/mount %s %s", bdisk, mntdir);
	    if(system(syscmd) != 0)
		printf("\nWARNING: (%s) file system mount failed!\n", syscmd);
	    else {
		while(1) {
		    if(chngdir(mntdir))
			break;
		    if(lstat(lpfdir, &statb) >= 0) {
			if((statb.st_mode&S_IFMT) != S_IFDIR) {
			    printf("\n%s: lost+found exists, but ", mntdir);
			    printf("is not a directory (see fsck(8))!\n");
			}
			break;
		    }
		    mkdir(lpfdir, 0700);
		    if(chngdir(lpfdir))
			break;
		    for(i=0; i<25; i++) {
			sprintf(&syscmd, "junk%d", i);
			j = creat(&syscmd, 0666);
			if(j < 0)
			    continue;
			write(j, (char *)&syscmd, SCSIZE);
			close(j);
		    }
		    sync();
		    sync();
		    system("rm -f junk*");
		    break;
		}
		chngdir(homedir);
	    }
	    chmod(&mntdir, 0755);
	    sprintf(syscmd, "/etc/umount %s", bdisk);
	    if(system(syscmd) != 0)
		printf("\nWARNING: (%s) file system dismount failed!\n",syscmd);
	    if(fseflag == NO)
		fstab(FST_ENT, &bdisk, &mntdir);	/* add entry to /etc/fstab */
	    printf("\n%sile system table (/etc/fstab):\n\n",
		(fseflag == NO) ? "New f" : "F");
	    system("cat /etc/fstab");
	    prtc();
	}
}

/*
 * Set up the disk partition table.
 *
 *	unit	disk unit number
 *	dip	drive info table pointer
 */

spt(unit, dip)
register struct di_info *dip;
int	unit;
{
	register struct dksize *dsp;
	register struct rasize *rsp;
	int i, j, nbpc;
	long i_sb, i_eb, j_sb, j_eb;

	for(i=0; i<8; i++) {
	    pt_info[i].pt_op = 0;
	    pt_info[i].pt_nb = 0L;
	    pt_info[i].pt_sb = 0L;
	    pt_info[i].pt_flags = 0;
	    if(((dip->di_flags&DI_NPD) == 0) &&
	       ((sd_info[dip->di_dt[unit]].sd_pmask&(1 << i)) == 0))
		    pt_info[i].pt_flags |= PT_NUP;    /* nonusable partition */
	}
	switch(dip->di_bmaj) {
	case RK_BMAJ:
	    pt_info[0].pt_sb = 0L;
	    pt_info[0].pt_nb = (long)rk_size;
	    for(i=1; i<8; i++)
		pt_info[i].pt_flags |= PT_NUP;
	    return(0);
	case RP_BMAJ:
	    nbpc = RP23_BPC;
	    dsp = &rp_sizes;
	    break;
	case RA_BMAJ:
	    switch((ra_ctid[dip->di_cn] >> 4) & 017) {
	    case UDA50:
	    case UDA50A:
	    case KDA50:
		rsp = &ud_sizes;
		break;
	    case KLESI:
		rsp = &rc_sizes;
		break;
	    case RQDX1:
	    case RQDX3:
		rsp = &rq_sizes;
		break;
	    case RUX1:
		/* THIS CANNOT HAPPEN */
		printf("\n(spt) - can't use RUX1!\n");
		return(1);
	    default:
		/* THIS CAN'T HAPPEN */
		printf("\n(spt) - unknown controller type!\n");
		return(1);
	    }
	/*
	 * RA sizes tables use following magic numbers:
	 * (ud_sizes, rc_sizes, rq_sizes)
	 * -1, partition ends at end of disk.
	 * -2, partition ends at start of maintenance area.
	 * Note: for the purposes of mkfs -1 and -2 mean the same.
	 *	 This prevents a file system on partition 7 from
	 *	 overlapping the maintenance area.
	 */
	    for(i=0; i<8; i++) {
		if(rsp->nblocks) {
		    pt_info[i].pt_sb = rsp->blkoff;
		    pt_info[i].pt_nb = rsp->nblocks;
		} else
		    pt_info[i].pt_flags |= PT_NUP;
		/* See comment above (-1 & -2 same in this case) */
		if(rsp->nblocks < 0L) {
		    pt_info[i].pt_nb =
			ra_drv[ra_index[dip->di_cn]+unit].d_un.ra_dsize -
			rsp->blkoff;
		    pt_info[i].pt_nb -= ra_mas[dip->di_cn];
		}
		rsp++;
	    }
	    nbpc = 0;
	    break;
	case RL_BMAJ:
	    /* all partitions start at block 0 */
	    pt_info[7].pt_nb = (long)rl_dt[unit]; /* drive type = unit size */
	    pt_info[7].pt_op = 3;	/* operlaps 0 and 1 */
	    pt_info[0].pt_nb = 10240L;	/* RL01 & RL02 both use partition 0 */
	    i = 2;
	    if(rl_dt[unit] == RL02) {
		pt_info[1].pt_nb = 10240L;
	    } else
		i = 1;
	    for(; i<7; i++)
		pt_info[i].pt_flags |= PT_NUP;
/* TODO: should use sd_smask, but that's just too bad! -- Fred Canter */
	    if(sd_info[dip->di_dt[unit]].sd_flags&SD_SYSDSK) {
		if((rl_dt[unit] == RL02) && (unit == sdunit)) {
		    pt_info[0].pt_flags |= PT_SYS;
		    pt_info[1].pt_flags |= PT_SYS;
		    pt_info[7].pt_flags |= PT_OP;
		}
		if((rl_dt[unit] == RL01) && ((unit == 0) || (unit == 1))) {
		    pt_info[0].pt_flags |= PT_SYS;
		    pt_info[7].pt_flags |= PT_OP;
		}
	    }
	    return(0);
	case HX_BMAJ:
	    /* THIS CAN'T HAPPEN */
	    printf("\n(spt) - can't use RX02!\n");
	    return(1);
	case HP_BMAJ:
	case HM_BMAJ:
	case HJ_BMAJ:
	    switch(sd_info[dip->di_dt[unit]].sd_type) {
	    case RP04:
	    case RP05:
	    case RP06:
		nbpc = RP456_BPC;
		dsp = &hp_sizes;
		break;
	    case RM02:
	    case RM03:
		nbpc = RM23_BPC;
		dsp = &hp_sizes[8];
		break;
	    case RM05:
		nbpc = RM5_BPC;
		dsp = &hp_sizes[16];
		break;
	    case ML11:
		/* THIS CAN'T HAPPEN */
		printf("\n(spt) - can't use ML11!\n");
		return(1);
	    default:
		/* THIS CAN'T HAPPEN */
		printf("\n(spt) - unknown drive type!\n");
		return(1);
	    }
	    break;
	case HK_BMAJ:
	    nbpc = RK67_BPC;
	    dsp = &hk_sizes;
	    break;
	default:
	    /* THIS CAN'T HAPPEN */
	    printf("\n(spt) - unknown controller type!\n");
	    return(1);
	}
	if(nbpc)	/* get partition sizes, unless this is MSCP disk */
	    for(i=0; i<8; i++) {
		if(dsp->nblocks) {
		    pt_info[i].pt_sb = (long)dsp->cyloff * (long)nbpc;
		    pt_info[i].pt_nb = dsp->nblocks;
		} else
		    pt_info[i].pt_flags |= PT_NUP;
		dsp++;
	    }
	for(i=0; i<8; i++) {		/* find overlapping partitions */
	    if(pt_info[i].pt_flags&PT_NUP)
		continue;		/* non usable partition */
	    i_sb = pt_info[i].pt_sb;
	    i_eb = pt_info[i].pt_sb + pt_info[i].pt_nb - 1;
	    for(j=0; j<8; j++) {
		if(pt_info[j].pt_flags&PT_NUP)
		    continue;		/* non usable partition */
		if(j == i)
		    continue;		/* can't overlap itself */
		j_sb = pt_info[j].pt_sb;
		j_eb = pt_info[j].pt_sb + pt_info[j].pt_nb - 1;
		if(((i_sb >= j_sb) && (i_sb <= j_eb)) ||
		   ((i_eb >= j_sb) && (i_eb <= j_eb)) ||
		   ((j_sb >= i_sb) && (j_sb <= i_eb)) ||
		   ((j_eb >= i_sb) && (j_eb <= i_eb)) ||
		   ((j_sb <= i_eb) && (j_eb >= i_sb)))
			pt_info[i].pt_op |= (1 << j);
	    }
	}
	/*
	 * If this is the system disk,
	 * mark any partitions used by the system unusable.
	 * Also, mark any partitions which overlap them unusable.
	 */
	if((sd_info[dip->di_dt[unit]].sd_flags&SD_SYSDSK) &&
	   (unit == sdunit) && (dip->di_cn == sdcntlr)) {
		for(i=0; i<8; i++) {
		    if(sd_info[dip->di_dt[unit]].sd_smask&(1<<i)) {
			pt_info[i].pt_flags |= PT_SYS;
			for(j=0; j<8; j++)
			    if(pt_info[i].pt_op & (1<<j))
				pt_info[j].pt_flags |= PT_OP;
		    }
		}
	}
	return(0);
}

/*
 * Print the disk partition layout table (pt_info[]).
 * Show only the available partitions.
 * Return 1 if no available partitions remain.
 */

ppt(unit, dip)
register struct di_info *dip;
int	unit;
{
	register int i, j;
	int	npa;

	npa = 0;
	for(i=0; i<8; i++)
	    if(pt_info[i].pt_flags == 0)
		npa++;
	pdoc(unit, dip);	/* print disk unit # on cntlr message */
	printf("disk partition layout:");
	printf("\n(See also, System Management Guide Appendix D)\n");
	printf("\nPart-  Start    Size in  Overlapping      Partition");
	printf("\nition  Block #  K bytes  Partitions       Available");
	printf("\n-----  -------  -------  ---------------  ---------");
	for(i=0; i<8; i++) {
	    if(pt_info[i].pt_flags&PT_NUP)
		continue;
	    printf("\n%5d  %7D  %7D  ",i,pt_info[i].pt_sb/2,pt_info[i].pt_nb/2);
	    for(j=0; j<8; j++) {
		if((pt_info[i].pt_op&(1<<j)))
		    printf("%d ", j);
		else
		    printf("  ");
	    }
	    printf(" ");
	    if(pt_info[i].pt_flags&PT_SYS)
		printf("NO: used by the system!");
	    else if(pt_info[i].pt_flags&PT_USER)
		printf("NO: already used!");
	    else if(pt_info[i].pt_flags&PT_OP)
		printf("NO: overlaps used partition!");
	    else
		printf("YES: available for use!");
	}
	printf("\n");
	if(npa == 0)
	    return(1);
	else
	    return(0);
}

/*
 * Make a file system the specified disk.
 * TODO: directory naming scheme will break if there
 *	   are more than 10 mounted user file systems!
 */

char	mfscmd[100];

mfsdisk(unit, fs, dip)
register struct di_info *dip;
int	unit,fs;
{
	register char *p;

	p = dsfn(RAW, unit, fs, dip);	/* node name (/dev/r??##) */
	sprintf(mfscmd, "/etc/mkfs %s %D %s %d %s %s",
	    p, (long)pt_info[fs].pt_nb/2, sd_info[dip->di_dt[unit]].sd_lname,
	    cputyp[tpi].p_type, fsname, volname);
	printf("\n%s\n", mfscmd);
	while(system(mfscmd) != 0) {
	    if(retry(&mfscmd))
		continue;
	    else
		return(1);
	}
	return(0);
}

/*
 * Return a pointer to a special device name string.
 * RAW /dev/r??## or /dev/r??#
 * BLK /dev/??## or /dev/??#
 */

char	dsfname[15];

dsfn(mode, unit, fs, dip)
register struct di_info *dip;
int	mode, unit, fs;
{
	if(dip->di_flags&DI_NPD)
	    sprintf(dsfname, "/dev%s%s%d", (mode==RAW) ? "/r" : "/",
		dip->di_name, unit);
	else if(dip->di_flags&DI_MSCP)
	    sprintf(dsfname, "/dev%s%.2s%d%d", (mode==RAW) ? "/r" : "/",
		sd_info[dip->di_dt[unit]].sd_lname, unit, fs);
	else
	    sprintf(dsfname, "/dev%s%s%d%d", (mode==RAW) ? "/r" : "/",
		dip->di_name, unit, fs);
	return(&dsfname);
}

/*
 * Prints:
 *	DISK Unit # on ?????? Controller # --
 */

pdoc(unit, dip)
register struct di_info *dip;
int	unit;
{
	printf("\n%s Unit %d on %s Controller ",
	    sd_info[dip->di_dt[unit]].sd_uname, unit, dip->di_typ);
	if(dip->di_flags&DI_MASS)
	    printf("%d ", dip->di_cn);
	printf("-- ");
}

/*
 * Add an entry to the /etc/fstab for
 * the new user file system.
 *
 * flag	  - FST_SCH = return true if entry is in the fstab
 *          FST_ENT = make new fstab entry (discard any existing entry)
 * bdisk  - block special file (/dev/??##)
 * mntdir - mounted on directory (/user##)
 *
 * If making a new entry,
 * remove any fstab entry that matches bdisk or mntdir.
 */

fstab(flag, bdisk, mntdir)
int	flag;
char *bdisk;
char *mntdir;
{
	register FILE *fo;
	register struct fstab *fstp;
	int	pn, j, match;

	if(flag != FST_SCH) {
	    if((fo = fopen("/tmp/setup.fstab", "w")) == NULL) {
		printf("\nCreate of /tmp/setup.fstab file failed!\n");
		su_err(FATAL);
	    }
	}
	pn = 2;
	match = 0;
	while(1) {
	    if((fstp = getfsent()) == NULL)
		break;				/* end of fstab */
	    if(flag == FST_SCH) {
		if(fsequal(bdisk, fstp->fs_spec) &&
		   fsequal(mntdir, fstp->fs_file)) {
			match++;
			break;
		} else
		    continue;
	    }
	    if(fsequal(bdisk, fstp->fs_spec)) {
		match++;
		continue;			/* bdisk matches, discard */
	    }
	    if(flag == FST_ENT) {
		if(fsequal(mntdir, fstp->fs_file))
		    continue;			/* mntdir matches, discard */
	    }
	    if(fsequal("/", fstp->fs_file))
		j = 1;
	    else
		j = pn++;
	    fprintf(fo, "%s:%s:%s:%d:%d\n", fstp->fs_spec, fstp->fs_file,
		fstp->fs_type, fstp->fs_freq, j);
	}
	endfsent();
	if(flag == FST_SCH)
		return(match);
	if(flag == FST_ENT) {
	    if(strcmp("/", mntdir) == 0)
		j = 1;
	    else
		j = pn++;
	    fprintf(fo, "%s:%s:rw:1:%d\n", bdisk, mntdir, j);  /* add new entry */
	}
	fclose(fo);
	sprintf(syscmd, "cp /tmp/setup.fstab /etc/fstab");
	if(system(syscmd) != 0) {
	    printf("\n%s failed!\n", syscmd);
	    su_err(FATAL);
	}
	unlink("/tmp/setup.fstab");
	sync();
	return(match);
}

/*
 * Ask if console terminal is a CRT,
 * do stty and select correct .profile if so.
 */
/*
 * TODO: need to deal with following:
 *
 *	.cshrc and .login file for root
 *	change 24 -> 22 for console in /etc/ttys
 *	change dw3 to vt100 in /etc/ttytpye
 *	fix profiles & csh files for any possible console login account
 *	need to deal with speeds < 1200 BPS (no backspace/erase on CRT)
 */
gcrt()
{
	register int j, crt;

	while(1) {
	    crt = NO;
	    printf("\nIs the console terminal a CRT (video terminal) <y or n> ? ");
	    j = yes(HELP);
	    if(j == YES) {
	    	crt = YES;
	    	system("cp /crt.profile /profile");
	    	system("stty 9600");
	    	system("stty dec crt");
	    } else if(j == NO) {
		system("stty 300");
		system("stty dec prterase");
	    	system("cp /prt.profile /profile");
	    } else if(j == HELP) {
	    	phelp("h_crt");
		continue;
	    }
	    printf("%sconsole is a %s terminal <y or n> ? ",
		confirm, (crt == YES) ? "VIDEO" : "HARDCOPY");
	    if(yes(NOHELP) != YES)
		continue;
	    break;
	}
	return(crt);
}

fsequal(fs, fst)
char	*fs;	/* bdisk or mntdir */
char	*fst;	/* fstab: fs_spec or fs_file */
{
	register int i;
	register char *p;
	char	str[20];

	p = fst;
	for(i=0; i<20; i++) {
		str[i] = *p++;
		if(str[i] == ':') {
			str[i] = '\0';
			break;
		}
	}
	return(!strcmp(fs, &str));
}

fmnt(flop)
char	*flop;

{
	char	p[100];
	char	sysmnt[100];

	sprintf(sysmnt, "/dev/rx%d", rxunit);
	if(flop == 0) {
	    umount(sysmnt);
	    return(0);
	}
	iflop(flop);
	sprintf(p, "Mount of %s diskette on /mnt", flop);
	while(1) {
	    if(mount(sysmnt, "/mnt", 1) != 0) {
		if(retry(&p))
		     continue;
		else
		    su_err(FATAL);
	    }
	    break;
	}
}

get_dst()
{

	register int cc;
	register struct dst_table *dst_ptr;
again:
	printf("\nChoose the Geographic Area for the daylight savings time from  the table below\n");
	printf("\n\t\tGeographic Area\tSelection\n");
	printf("\t\t---------------\t---------\n");
	for(dst_ptr=dst_table;dst_ptr->dst_area;dst_ptr++) {
		printf("\t\t%s",dst_ptr->dst_area);
		if(strlen(dst_ptr->dst_area) > 7)
			printf("\t");
		else
			printf("\t\t");
		printf("%4d\n",dst_ptr->dst_id);
	}
	do 
		printf("\nEnter the selection number <%d> ",DST_USA);
	while((cc = getline("h_dstarea")) < 0);
	if(cc == 0)
		return(DST_USA);  /* USA (default) */
	cc = atoi(lbuf);
	if((cc < DST_USA) || (cc > DST_EET)) {
		printf("Enter a number between %d and %d\n",DST_USA,DST_EET);
		goto again;
	}
	return(cc);

}