/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: format.c 1.4 90/07/03 $

/*
 * format
 *	top level (main) of the combined scsi/zd online
 *	formatter.  This does some parsing of command
 *	line arguments and some checking based on tables,
 *	then branches to the appropriate subsytem.
 */

/* $Log:	format.c,v $
 */

#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/stat.h>
#include <sys/devsw.h>
#include <sys/vtoc.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include "format.h"

extern caddr_t malloc();

#define LINELENGTH	80
char openname[LINELENGTH];

main(argc, argv)
	int argc;
	char **argv;
{
	extern char	*optarg;
	extern int	optind;
	int c;
	struct stat statbuf;
	struct usage_check *use;
	char str[LINELENGTH+1], *p;
	int i, len;

	if (argc == 1)
		goto usage;
	argc--;		/* last arg must be device name */
	while ((c = getopt(argc, argv, "frumla:qyonwcvDt:b:s:e:p:d:h:")) != EOF) {
		switch (c) {

		case 'f':
			if (function)
				goto usage;
			function = FORMAT;
			break;
		case 'r':
			if (function)
				goto usage;
			function = REFORMAT;
			break;
		case 'm':
			if (function)
				goto usage;
			function = REFORMAT_MFG;
			break;
		case 'l':
			if (function)
				goto usage;
			function = DISPLAY;
			break;
		case 'u':
			if (function)
				goto usage;
			function = USAGE;
			break;
		case 'a':
			if (function)
				goto usage;
			function = ADDBAD;
			a_arg = malloc(ADDLENGTH);
			if (strlen(optarg) < ADDLENGTH)
				strcpy(a_arg, optarg);
			else {
				printf("args too long -- use file!\n");
				exit(1);
			}
			while (optind < argc && argv[optind][0] != '-') {
				char *ptr;

				if ((strlen(a_arg) 
				    + strlen(argv[optind] + 1))
				    >= ADDLENGTH) {
					printf("args too long -- use file!\n");
					exit(1);
				}
				ptr = a_arg + strlen(a_arg);
				strcpy(ptr++, " ");
				strcpy(ptr, argv[optind++]);
			}
			break;
		case 'q':
			if (function)
				goto usage;
			function = WRITEDIAG;
			break;
		case 'y':
			if (function)
				goto usage;
			function = VERIFY;
			break;
		case 'o':
			args |= B_OVERWRITE;
			break;
		case 'c':
			args |= B_CHECKDATA;
			break;
		case 'v':
			verbose++;
			break;
		case 'n':
			args |= B_NOVERIFY;
			break;
		case 'w':
			args |= B_NOWRITEDIAG;
			break;
		case 'D':
			debug++; 
                        setbuf(stdout, NULL);
                        setbuf(stderr, NULL);
			break;
		case 't':
			args |= B_TYPE;
			t_arg = optarg;
			break;
		case 'b':
			args |= B_BADFILE;
			b_arg = optarg;
			break;
		case 's':
			args |= B_START;
			s_arg = atoi(optarg);
			break;
		case 'e':
			args |= B_END;
			e_arg = atoi(optarg);
			break;
		case 'p':
			args |= B_FULLPASS;	
			p_arg = atoi(optarg);
			break;
		case 'd':
			args |= B_DEFECTPASS;
			d_arg = atoi(optarg);
			break;
		case 'h':
			args |= B_HDRPASS;
			h_arg = atoi(optarg);
			break;
		case '?':
			goto usage;
			
		default:
			break;
		}
	}
	diskname = argv[argc];
	if (debug) printf("diskname = %s\n", diskname);
	disk = -1;
	for (i = 0, len = 0; i < nformat_types; i++) {
		if (!strncmp(diskname, types[i].disk_name, 
			     strlen(types[i].disk_name))) 
			if (strlen(types[i].disk_name) > len) {
				len = strlen(types[i].disk_name);
				disk = i;
			}
	}
	if (disk < 0) {
		fprintf(stderr, "Error: %s disk type not supported\n", 
			diskname);
		fprintf(stderr, "The following disk types are supported\n");
		for (i = 0, len = 0, p = str; i < nformat_types; i++) {
			if ((len+strlen(types[i].disk_name)+1) > LINELENGTH) {
				fprintf(stderr, "%s\n", str);
				p = str;
			}
			sprintf(p, "%s ", types[i].disk_name);
			len = strlen(str);
			p += strlen(p);
		}
		if (p != str)
			fprintf(stderr, "%s\n", str);
		exit(1);
	}

	/*
	 * Do usage checking in relation to specific disk
	 * subsystem.
	 */
	for (i = 0, use = types[disk].use; i < *types[disk].numfuncs; 
	     i++, use++) {
		if (use->function == function) {
			usep = use;
			if (args & ~(use->opts_supported)
			    || (args & use->opts_required) 
				!= use->opts_required) {
				fprintf(stderr, "usage: %s\n",
					use->usage_string);
				exit(1);
			}
			break;
		}
	}
	if (i == *types[disk].numfuncs) {
		fprintf(stderr, "function not supported for specified ");
		fprintf(stderr, "subsystem\n");
		(*types[disk].usage)();
		general_usage();
		exit(1);
	}

	openname[0] = '\0';
	strcat(openname, DEVPATH);
	strcat(openname, diskname);
	if (stat(openname, &statbuf) < 0) {
		fprintf(stderr, "unable to stat %s: ", openname);
		perror("");
		exit(1);
	}
	if (VPART(statbuf.st_rdev) != V_RAW_MINOR) {
		fprintf(stderr, "must open raw disk\n");
		exit(1);
	}
	/*
	 * If we are going to write to the disk then
	 * open device for EXCLUSIVE access and make sure
	 * it's actually a device driver rather than a
	 * pseudo driver.
	 */
	if ((fd = open(openname, usep->open_flags)) < 0
	    || ioctl(fd, RIODRIVER, (char *)NULL) < 0) {
		if (fd < 0)
			fprintf(stderr, 
				"unable to open %s: ", openname);
		else
			fprintf(stderr, "RIODRIVER ioctl failed ");
		perror("");
		exit(2);
	}

	/*
	 * Do setup for specific device, and then device_main
	 * to perform the specified function.
	 */
	if ((*types[disk].setup)())
		exit(1);
	(*types[disk].dev_main)();
	exit(0);
usage:
	general_usage();
	exit(1);
}

/*
 * general_usage
 *	Give generic usage information
 */
general_usage()
{
	fprintf(stderr, "General usage of format command:\n");
	fprintf(stderr, 
	 "format [-f|r|m|y|l|u|a badlist] [-wovnc] [-b file] [-s n] [-e n] \\\n");
	fprintf(stderr,
	 "                 [-p n] [-d n] [-h n] [-t type] diskname\n");
	fprintf(stderr, 
	 "\n(Note that all options do not apply to all disk types.\n");
	fprintf(stderr,
	 "Do 'format -u diskname' to get more specific information\n");
}
