/*
 * File...........: s390-tools/dasdfmt/dasdfmt.c
 * Author(s)......: Utz Bacher,      <utz.bacher@de.ibm.com>
 *                  Fritz Elfert,    <felfert@to.com>
 *                  Carsten Otte,    <cotte@de.ibm.com>
 *                  Volker Sameske,  <sameske@de.ibm.com>
 *                  Holger Smolinski <smolinsk@de.ibm.com>
 * Bugreports.to..: <Linux390@de.ibm.com>
 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2002
 *
 * versions:
 *   1.01   2001-09-07    progressbar, hashmarks, new blksize test
 *   1.02   2001-10-01    main structure dasdfmt_info
 *                        signal handler for SIGTERM, SIGINT and SIGQUIT
 *                        cylinder-wise formatting in every case
 *   1.03   2001-10-31    some dasdfmt return codes changed
 *   1.04   2002-02-27    do some checks with volume serial
 *          2002-03-12    removed dependency to kernel headers
 *   1.05   2002-03-18    major code restructuring
 *                        removed dependency to proc-filesystem
 *                        removed unused range formatting stuff
 *   1.06   2002-05-10    changed API handling, set it back to 0
 */

#include "dasdfmt.h"
#include "vtoc.h" 

/*
 * print the usage text and exit
 */
static void exit_usage(int exitcode)
{
	printf("Usage: %s [-htvyLVF]\n"
	       "	       [-l <volser>      | --label=<volser>]\n"
	       "               [-b <blocksize>   | --blocksize=<blocksize>]\n"
	       "               [-d <disk layout> | --disk_layout=<disk layout>]\n"
	       "               <diskspec>\n\n",prog_name);

	printf("       -t or --test     means testmode\n"
	       "       -V or --version  means print version\n"
	       "       -L or --no_label means don't write disk label\n"
	       "       -p or --progressbar means show a progress bar\n"
	       "       -m x or --hashmarks=x means show a hashmark every x "
	       "cylinders\n"
	       "       -v means verbose mode\n"
	       "       -F means don't check if the device is in use\n\n"
	       "       <volser> is the volume identifier, which is converted\n"
	       "                to EBCDIC and written to disk. \n"
	       "                (6 characters, e.g. LNX001\n"
	       "       <blocksize> has to be power of 2 and at least 512\n"
	       "       <disk layout> is either \n"
	       "           'cdl' for compatible disk layout (default) or\n"
	       "           'ldl' for linux disk layout\n"
	       "       and <diskspec> is either\n"
	       "           -f /dev/dasdX or --device=/dev/dasdX\n"
	       "           if you do not use devfs\n"
	       "         or\n"
	       "           -f /dev/dasd/xxxx/device or "
	       "--device=/dev/dasd/xxxx/device\n"
	       "           and alternatively\n"
	       "           -n xxxx or --devno=xxxx\n"
	       "           in case you are using devfs.\n"
	       "           xxxx is your hexadecimal device number.\n");
	exit(exitcode);
}

/*
 * signal handler:
 * enables the disk again in case of SIGTERM, SIGINT and SIGQUIT
 */
static void program_interrupt_signal (int sig)
{
	int rc;

	if (program_interrupt_in_progress)
		raise (sig);
	program_interrupt_in_progress = 1;

	if (disk_disabled) {
		printf("Re-accessing the device... \n");
		rc = ioctl(filedes, BIODASDENABLE, format_params);
		if (rc)
			ERRMSG_EXIT(EXIT_FAILURE,
				    "%s: (signal handler) IOCTL BIODASDENABLE "
				    "failed (%s)\n",prog_name,strerror(errno));
	}

	printf("Rereading the partition table... \n");
	rc = ioctl(filedes, BLKRRPART, NULL);
	if (rc) 
	{
		ERRMSG("%s: (signal handler) Re-reading partition table "
		       "failed. (%s)\n", prog_name, strerror(errno));
	} else printf("Exiting...\n");

	rc = close(filedes);
	if (rc)
		ERRMSG("%s: (signal handler) Unable to close device (%s)\n", 
		       prog_name, strerror(errno));

	signal (sig, SIG_DFL);
	raise (sig);
}


/*
 * check given device name for blanks and some special characters
 * or create a devfs filename in case the devno was specified
 */
static void get_device_name(dasdfmt_info_t *info, char *name)
{
	if ((info->node_specified + info->devno_specified) > 1) 
		ERRMSG_EXIT(EXIT_MISUSE,"%s: Device can only specified once! "
			    "(%#04x or %s)\n", prog_name, info->devno, name);

	if ((info->node_specified + info->devno_specified) == 0) 
		ERRMSG_EXIT(EXIT_MISUSE,"%s: No device specified!\n", 
			    prog_name);

	if ((info->devno_specified) && 
	    ((info->devno < 0x0000) || (info->devno > 0xffff)))
		ERRMSG_EXIT(EXIT_MISUSE,"%s: Devno '%#04x' is not in range "
			    "0x0000 - 0xFFFF!\n", prog_name, info->devno);

	if (info->devno_specified)
		sprintf(info->devname, "/dev/dasd/%04x/device", info->devno);
	else {
		if ((strchr(name, ' ') != NULL)||(strchr(name, '#') != NULL)||
		    (strchr(name, '[') != NULL)||(strchr(name, ']') != NULL)||
		    (strchr(name, '!') != NULL)||(strchr(name, '>') != NULL)||
		    (strchr(name, '(') != NULL)||(strchr(name, '<') != NULL)||
		    (strchr(name, ')') != NULL)||(strchr(name, ':') != NULL)||
		    (strchr(name, '&') != NULL)||(strchr(name, ';') != NULL))
			ERRMSG_EXIT(EXIT_MISUSE,"%s: Your filename contains "
				    "blanks or special characters!\n", 
				    prog_name);

		strncpy(info->devname, name, LINE_LENGTH - 1);
		info->devname[LINE_LENGTH - 1] = '\0';
	}
}


/*
 * initialize the dasdfmt info structure
 */
static void init_info(dasdfmt_info_t *info)
{
	info->devno             = 0x0;
	info->usage_count       = 0;

	info->testmode          = 0;
	info->verbosity         = 0;
	info->withoutprompt     = 0;
	info->print_progressbar = 0;
	info->print_hashmarks   = 0;
	info->hashstep          = 0;
	info->force             = 0;
	info->writenolabel      = 0;
	info->labelspec         = 0;
        info->cdl_format        = 0;
	info->blksize_specified = 0;
	info->node_specified    = 0;
	info->devno_specified   = 0;
}


/*
 * check for disk type and set some variables (e.g. usage count)
 */
static void check_disk(dasdfmt_info_t *info)
{
	dasd_information_t dasd_info;

	if (ioctl(filedes, BIODASDINFO, &dasd_info) != 0) {
		close(filedes);
		ERRMSG_EXIT(EXIT_FAILURE,
			    "%s: the ioctl call to retrieve device information"
			    " failed (%s)\n", prog_name, strerror(errno));
	}

	if (!info->force)
		if (dasd_info.open_count > 1)
			ERRMSG_EXIT(EXIT_BUSY, "Disk in use!\n");

	info->usage_count = dasd_info.open_count;
	info->devno       = dasd_info.devno;
        if (strncmp(dasd_info.type, "ECKD",4) != 0) {
		if (info->devno_specified) {
			ERRMSG_EXIT(EXIT_FAILURE, 
				    "%s: Unsupported disk type\n%x is not an "
				    "ECKD disk!\n", prog_name, info->devno);
		}
		else {
			ERRMSG_EXIT(EXIT_FAILURE, 
				    "%s: Unsupported disk type\n%s is not an "
				    "ECKD disk!\n", prog_name, info->devname);
		}
	}                                       
}                                    


/*
 * check for the API version
 */
static void check_api_version (char *device)
{
        int api;
 
        /* get dasd driver API version */
	if (ioctl(filedes, DASDAPIVER, &api) != 0) {
                close(filedes);
                ERRMSG_EXIT(EXIT_FAILURE,
                            "%s: the ioctl call to get the API version "
			    "returned with the following error message:\n%s\n",
			    prog_name, strerror(errno));
        }
 
	/* check API versions */
	if (api < DASD_MIN_API_VERSION) {
                ERRMSG_EXIT(EXIT_FAILURE,"%s: API version mismatch\n"
			    "The current API version '%d' doesn't match "
			    "dasd driver API version '%d'!\n", 
			    prog_name, DASD_MIN_API_VERSION, api);          

        }

	if (api == 1) {
                ERRMSG_EXIT(EXIT_FAILURE,"%s: API version mismatch\n"
			    "The dasd driver API version '1' is not "
			    "supported!\n", prog_name);          

        }
}                      


/*
 * check the volume serial for special 
 * characters  and move blanks to the end
 */
static int check_volser(char *s, int devno)
{
	int i,j;

	for (i=0; i<6; i++) {
		if ((s[i] < 0x20) || (s[i] > 0x7a) ||
		    ((s[i] >= 0x21)&&(s[i] <= 0x22)) ||  /*  !"        */
		    ((s[i] >= 0x26)&&(s[i] <= 0x2f)) ||  /* &'()*+,-./ */
		    ((s[i] >= 0x3a)&&(s[i] <= 0x3f)) ||  /*  :;<=>?    */
		    ((s[i] >= 0x5b)&&(s[i] <= 0x60)))    /*  \]^_´     */
			s[i] = ' ';
		s[i] = toupper(s[i]);
	}
	s[6] = 0x00;		

	for (i=0; i<6; i++) {
		if (s[i] == ' ')
			for (j=i; j<6; j++)
				if (s[j] != ' ') {
					s[i] = s[j];
					s[j] = ' ';
					break;
				}
	}

	if (s[0] == ' ') {
		printf("Usage error, switching to default.\n");
		sprintf(s, "0X%04x", devno);
		for (i=0; i<6; i++)
			s[i] = toupper(s[i]);
		return -1;
	}

	return 0;
}


/*
 * do some blocksize checks
 */
static int check_param(char *s, format_data_t *data)
{
	int tmp = data->blksize;

	if ((tmp < 512) || (tmp > 4096)) {
		strcpy(s,"Blocksize must be one of the following positive "
		       "integers:\n512, 1024, 2048, 4096.");
		return -1;
	}

	while (tmp > 0) {
		if ((tmp % 2) && (tmp != 1)) {
			strcpy(s,"Blocksize must be a power of 2.");
			return -1;
		}
		tmp /= 2;
	}
	
	return 0;
}


/*
 * ask the user to specify a blocksize
 */
static format_data_t ask_user_for_blksize(format_data_t params)
{
	char c, str[ERR_LENGTH], buffer[20];
	int i, rc;

	i = params.blksize;

	do {
		params.blksize = i;

		printf("Please enter the blocksize of the formatting [%d]: ", i);
		fgets(buffer, sizeof(buffer), stdin);

		rc = sscanf(buffer,"%d%c", &params.blksize, &c);
		if ((rc == 2) && (c == '\n')) 
			rc = 1;
		if (rc == -1) 
			rc = 1; /* this happens, if enter is pressed */
		if (rc != 1) 
			printf(" -- wrong input, try again.\n");

		if (check_param(str, &params) < 0) {
			printf(" -- %s\n",str); 
			rc = 0; 
		}
	} while (rc != 1);

	return params;
}


/*
 * print all information needed to format the device
 */
static void dasdfmt_print_info(dasdfmt_info_t *info, volume_label_t *vlabel,
			       struct hd_geometry *drive_geo, format_data_t *p)
{
	char volser[6], vollbl[4];

	printf("Drive Geometry: %d Cylinders * %d Heads =  %d Tracks\n",
	       drive_geo->cylinders, drive_geo->heads,
	       (drive_geo->cylinders * drive_geo->heads));

	printf("\nI am going to format the device ");
	if (info->devno_specified)
		printf("%x in the following way:\n", info->devno);
	else
		printf("%s in the following way:\n", info->devname);

	printf("   Device number of device : 0x%x\n",info->devno);
	printf("   Labelling device        : %s\n",
	       (info->writenolabel)?"no":"yes");

	if (!info->writenolabel) {
		vtoc_volume_label_get_label(vlabel, vollbl);
		printf("   Disk label              : %.4s\n", vollbl);
		vtoc_volume_label_get_volser(vlabel, volser);
		printf("   Disk identifier         : %.6s\n", volser);
	}
	printf("   Extent start (trk no)   : %u\n", p->start_unit);
	printf("   Extent end (trk no)     : %u\n", p->stop_unit);
	printf("   Compatible Disk Layout  : %s\n",
	       (p->intensity & DASD_FMT_INT_COMPAT)?"yes":"no");
	printf("   Blocksize               : %d\n", p->blksize);

	if (info->testmode) 
		printf("Test mode active, omitting ioctl.\n");
}


/*
 * do all the labeling (volume label and initial VTOC)
 */
static void dasdfmt_write_labels(dasdfmt_info_t *info, volume_label_t *vlabel)
{
	unsigned int        label_position;
	dasd_information_t  dasd_info;
	struct hd_geometry  geo;
	format4_label_t     f4;
	format5_label_t     f5;
	format7_label_t     f7;
	int rc, blksize;

	if (info->verbosity > 0) printf("Retrieving dasd information... ");

	if (ioctl(filedes, BIODASDINFO, &dasd_info) != 0)
		ERRMSG_EXIT(EXIT_FAILURE, "%s: (write labels) IOCTL BIODASD"
			    "INFO failed (%s).\n",prog_name, strerror(errno));

	if (ioctl(filedes, BLKSSZGET, &blksize) != 0)
		ERRMSG_EXIT(EXIT_FAILURE, "%s: (write labels) IOCTL BLKSSZGET "
			    "failed (%s).\n", prog_name, strerror(errno));
	
	if (ioctl(filedes, HDIO_GETGEO, &geo) != 0)
		ERRMSG_EXIT(EXIT_FAILURE, "%s: (write labels) IOCTL HDIO_GET"
			    "GEO failed (%s).\n", prog_name, strerror(errno));

	if (info->verbosity > 0) printf("ok\n");

	/* write bootstrap */
	if (info->verbosity > 0) printf("Writing empty bootstrap...\n");

	if (lseek(filedes, 0, SEEK_SET) != 0)
		ERRMSG_EXIT(EXIT_FAILURE, "%s: lseek command 0 failed "
			    "(%s)\n", prog_name, strerror(errno));

	rc = write(filedes, &ipl1, sizeof(bootstrap1_t));
	if (rc != sizeof(bootstrap1_t))
		ERRMSG_EXIT(EXIT_FAILURE, "%s: Writing the bootstrap IPL1 "
			    "failed, only wrote %d bytes.\n", prog_name, rc);

	label_position = blksize;
	rc = lseek(filedes, label_position, SEEK_SET);
	if (rc != label_position)
		ERRMSG_EXIT(EXIT_FAILURE, "%s: lseek command to %i failed "
			    "(%s).\n", prog_name, label_position, 
			    strerror(errno));

	rc = write(filedes, &ipl2, sizeof(bootstrap2_t));
	if (rc != sizeof(bootstrap2_t))
		ERRMSG_EXIT(EXIT_FAILURE, "%s: Writing the bootstrap IPL2 "
			    "failed, only wrote %d bytes.\n", prog_name, rc);

	/* write VTOC */
	vtoc_init_format4_label(&f4, USABLE_PARTITIONS, geo.cylinders,
		geo.heads, geo.sectors, blksize, dasd_info.dev_type);

	vtoc_init_format5_label(&f5);
	vtoc_init_format7_label(&f7);
	vtoc_set_freespace(&f4, &f5, &f7, '+', 0, FIRST_USABLE_TRK,
			   (geo.cylinders * geo.heads - 1),
			   geo.cylinders, geo.heads);

	label_position = dasd_info.label_block * blksize;
			
	if (info->verbosity > 0) printf("Writing label...\n");

	rc = lseek(filedes, label_position, SEEK_SET);
	if (rc != label_position)
		ERRMSG_EXIT(EXIT_FAILURE, "%s: lseek command to %i failed "
			    "(%s).\n", prog_name, label_position, 
			    strerror(errno));

	if (info->cdl_format)
		rc = write(filedes, vlabel, sizeof(volume_label_t));
	else
		rc = write(filedes, vlabel->vollbl,(sizeof(volume_label_t)-4));

	if (((rc != sizeof(volume_label_t)) && info->cdl_format) || 
	    ((rc != (sizeof(volume_label_t)-4)) && (!info->cdl_format)))
		ERRMSG_EXIT(EXIT_FAILURE, "%s: Error writing volume label "
			    "(%d).\n", prog_name, rc);

	if (info->verbosity > 0) printf("Writing VTOC... ");

	label_position = (VTOC_START_CC * geo.heads + VTOC_START_HH) * 
		geo.sectors * blksize;

	rc = lseek(filedes, label_position, SEEK_SET);
	if (rc != label_position)
		ERRMSG_EXIT(EXIT_FAILURE, "%s: lseek command to %i failed "
			    "(%s).\n", prog_name, label_position, 
			    strerror(errno));
			
	/* write VTOC FMT4 DSCB */
	rc = write(filedes, &f4, sizeof(format4_label_t));
	if (rc != sizeof(format4_label_t))
		ERRMSG_EXIT(EXIT_FAILURE, "%s: Error writing FMT4 label "
			    "(%d).\n", prog_name, rc);

	label_position += blksize;

	rc = lseek(filedes, label_position, SEEK_SET);
	if (rc != label_position)
		ERRMSG_EXIT(EXIT_FAILURE, "%s: lseek to %i failed (%s).\n",
			    prog_name, label_position, strerror(errno));

	/* write VTOC FMT5 DSCB */
	rc = write(filedes, &f5, sizeof(format5_label_t));
	if (rc != sizeof(format5_label_t))
		ERRMSG_EXIT(EXIT_FAILURE, "%s: Error writing FMT5 label "
			    "(%d).\n", prog_name, rc);

	if ((geo.cylinders * geo.heads) > BIG_DISK_SIZE) {
		label_position += blksize;

		rc = lseek(filedes, label_position, SEEK_SET);
		if (rc != label_position)
			ERRMSG_EXIT(EXIT_FAILURE, "%s: lseek to %i failed "
				    "(%s).\n", prog_name, label_position,
				    strerror(errno));

		/* write VTOC FMT 7 DSCB (only on big disks) */
		rc = write(filedes, &f7, sizeof(format7_label_t));
		if (rc != sizeof(format7_label_t))
			ERRMSG_EXIT(EXIT_FAILURE, "%s: Error writing FMT7 "
				    "label (rc=%d).\n", prog_name, rc);
	}

	fsync(filedes);

	if (info->verbosity > 0) printf("ok\n");
}


/*
 * formats the disk cylinderwise
 */
static void dasdfmt_format(dasdfmt_info_t *info, struct hd_geometry *drive_geo,
			   format_data_t *format_params)
{
	format_data_t format_step;
	int j, k, cyl, tmp, p1, p2;

	if (info->print_hashmarks) {
		if ((info->hashstep < 1) || (info->hashstep > 1000)) {
			printf("Hashmark increment is not in range <1,1000>, "
			       "using the default.\n");
			info->hashstep = 10;
		}
		
		printf("Printing hashmark every %d cylinders.\n", 
		       info->hashstep);
	}

	format_step.blksize   = format_params->blksize;
	format_step.intensity = format_params->intensity;
		
	k = 0;
	cyl = 1;
	if (info->print_progressbar || info->print_hashmarks)
		printf("\n");

	while (1) {
		p1 = -1; 
		p2 =  0;

		format_step.start_unit = k;
		format_step.stop_unit  = k + drive_geo->heads - 1;

		if (cyl == 1)
			format_step.start_unit += 1;
			
		if (ioctl(filedes, BIODASDFMT, &format_step) != 0)
			ERRMSG_EXIT(EXIT_FAILURE,"%s: (format cylinder) IOCTL "
				    "BIODASDFMT failed. (%s)\n",
				    prog_name, strerror(errno));

		if (info->print_progressbar) {
			printf("cyl %5d of %5d |", cyl, drive_geo->cylinders);
			p2 = p1;
			p1 = cyl*100/drive_geo->cylinders;
			if (p1 != p2)
			{
				/* percent value has changed */
				tmp = cyl*50/drive_geo->cylinders;
				for (j=1; j<=tmp; j++)
					printf("#");
				for (j=tmp+1; j<=50; j++) 
					printf("-");
				printf("| %3d%%  ", p1);
			}
			printf("\r");
			fflush(stdout);
		}
		
		if (info->print_hashmarks)
			if (cyl % info->hashstep == 0) {
				printf("#");
				fflush(stdout);
			}

		if (k % drive_geo->heads == 0) {
			k += drive_geo->heads;
			cyl++;
		}
		else 
			k += format_params->stop_unit % drive_geo->heads;

		if (k > format_params->stop_unit) 
			break;
	}

	if (info->print_progressbar || info->print_hashmarks)
		printf("\n\n");	
}


/*
 *
 */
static void dasdfmt_prepare_and_format (dasdfmt_info_t *info,
					struct hd_geometry *drive_geo,
					format_data_t *p)
{
	format_data_t temp = {
		start_unit: 0,
		stop_unit:  0,
		blksize:    p->blksize,
		intensity: (p->intensity | DASD_FMT_INT_INVAL)
	};

	if (info->verbosity > 0) printf("Detaching the device...\n");

	if (ioctl(filedes, BIODASDDISABLE, p) != 0)
		ERRMSG_EXIT(EXIT_FAILURE, "%s: (prepare device) IOCTL "
			    "BIODASDDISABLE failed. (%s)\n", prog_name, 
			    strerror(errno));
	disk_disabled = 1;

	if (info->verbosity > 0) printf("Invalidate first track...\n");

	if (ioctl(filedes, BIODASDFMT, &temp) != 0)
		ERRMSG_EXIT(EXIT_FAILURE, "%s: (invalidate first track) IOCTL "
			    "BIODASDFMT failed. (%s)\n", prog_name, 
			    strerror(errno));

	/* except track 0 from standard formatting procss */
	p->start_unit = 1;

	dasdfmt_format(info, drive_geo, p);

	if (info->verbosity > 0) printf("formatting tracks complete...\n");

	temp.intensity = p->intensity;

	if (info->verbosity > 0) printf("Revalidate first track...\n");

	if (ioctl(filedes, BIODASDFMT, &temp) != 0)
		ERRMSG_EXIT(EXIT_FAILURE, "%s: (re-validate first track) IOCTL"
			    " BIODASDFMT failed (%s)\n", prog_name, 
			    strerror(errno));

	if (info->verbosity > 0) printf("Re-accessing the device...\n");

	if (ioctl(filedes, BIODASDENABLE, p) != 0)
		ERRMSG_EXIT(EXIT_FAILURE, "%s: (prepare device) IOCTL "
			    "BIODASDENABLE failed. (%s)\n", prog_name, 
			    strerror(errno));
	disk_disabled = 0;
}


/*
 *
 */
static void do_format_dasd(dasdfmt_info_t *info, format_data_t *p, 
			   volume_label_t *vlabel)
{
	struct hd_geometry drive_geo;
	char               inp_buffer[5];



	if (info->verbosity > 0) printf("Retrieving disk geometry...\n");

	if (ioctl(filedes, HDIO_GETGEO, &drive_geo) != 0)
		ERRMSG_EXIT(EXIT_FAILURE, "%s: the ioctl call to get geometry "
			    "returned with the following error message:\n%s\n",
			    prog_name, strerror(errno));

	p->start_unit = 0;
	p->stop_unit  = (drive_geo.cylinders * drive_geo.heads) - 1;
			
        if (!info->writenolabel) {
        	if (!info->labelspec) {
			char buf[7];

	               	sprintf(buf, "0X%04x", info->devno);
			check_volser(buf, info->devno);
			vtoc_volume_label_set_volser(vlabel, buf);
		}

                if (p->intensity == 0x08) {
			info->cdl_format = 1;
			vtoc_volume_label_set_label(vlabel, "VOL1");
			vtoc_volume_label_set_key(vlabel, "VOL1");
                        vtoc_set_cchhb(&vlabel->vtoc, 0x0000, 0x0001, 0x01);
                } 
		else 
			vtoc_volume_label_set_label(vlabel, "LNX1");
       	}

	if ((info->verbosity > 0) || (!info->withoutprompt)) 
		dasdfmt_print_info(info, vlabel, &drive_geo, p);

	if (!info->testmode) {
		if (!info->withoutprompt) {
			printf("\n--->> ATTENTION! <<---\n");
			printf("All data of that device will be lost.\nType "
			       "\"yes\" to continue, no will leave the disk "
			       "untouched: ");
			fgets(inp_buffer, sizeof(inp_buffer), stdin);
			if (strcasecmp(inp_buffer,"yes") &&
			    strcasecmp(inp_buffer,"yes\n")) {
				printf("Omitting ioctl call (disk will "
					"NOT be formatted).\n");
				return;
			}
		}

		if (!((info->withoutprompt)&&(info->verbosity<1)))
			printf("Formatting the device. This may take a "
			       "while (get yourself a coffee).\n");
		
		dasdfmt_prepare_and_format(info, &drive_geo, p);

		printf("Finished formatting the device.\n");

		if (!info->writenolabel) 
			dasdfmt_write_labels(info, vlabel);

		printf("Rereading the partition table... ");
		if (ioctl(filedes, BLKRRPART, NULL) != 0) {
			ERRMSG("%s: error during rereading the partition "
			       "table: %s.\n", prog_name, strerror(errno));
		}
		else 
			printf("ok\n");
	}
}


int main(int argc,char *argv[]) 
{
	dasdfmt_info_t info;
	volume_label_t vlabel;

	char dev_filename[LINE_LENGTH];
	char str[ERR_LENGTH];
	char buf[7];

	char *devno_param_str   = NULL;
	char *blksize_param_str = NULL;
	char *hashstep_str      = NULL;

	int rc, index;

	/* Establish a handler for interrupt signals. */
	signal (SIGTERM, program_interrupt_signal);
	signal (SIGINT,  program_interrupt_signal);
	signal (SIGQUIT, program_interrupt_signal);

	/******************* initialization ********************/
	prog_name = argv[0];

	/* set default values */
	init_info(&info);
	vtoc_volume_label_init(&vlabel);

	format_params.blksize   = DEFAULT_BLOCKSIZE;
	format_params.intensity = DASD_FMT_INT_COMPAT;

	/*************** parse parameters **********************/

	/* avoid error message generated by getopt */
	opterr=0;

	while (1)
	{
		rc=getopt_long(argc, argv, dasdfmt_getopt_string,
			       dasdfmt_getopt_long_options, &index);
        	if (rc==-1) break; // exit loop if finished

		switch (rc) 
		{
		case 'F':
 			info.force=1;
			break;

		case 'C':
                	format_params.intensity=0x08;
			break;

                case 'd' :
                        if (strncmp(optarg,"cdl",3)==0)
			{
				format_params.intensity=0x08;
				if (info.writenolabel)
				{
					printf("WARNING: using the cdl " \
					       "format without writing a " \
					       "label doesn't make much " \
					       "sense!\n");
					exit(1);
				}
			}
			else if (strncmp(optarg,"ldl",3)==0)
				format_params.intensity=0x00;
			else
			{
				printf("%s is not a valid option!\n", optarg);
				exit(1);
			}
                        break;

		case 'y':
			info.withoutprompt=1;
			break;

		case 't':
			info.testmode=1;
			break;

		case 'p':
			if (!info.print_hashmarks) info.print_progressbar=1;
			break;

		case 'm':
			if (!info.print_progressbar) 
			{
				hashstep_str=optarg;
				info.print_hashmarks=1;
			}
			break;

		case 'v':
			info.verbosity=1;
			break;

		case '?': /* fall-through */
		case 'h':
			exit_usage(0);

		case 'V':
			printf("%s version %s\n", prog_name, VERSION_NO);
			exit(0);

		case 'l':
	               	strncpy(buf, optarg, 6);
			if (check_volser(buf, 0) < 0)
				break;
			vtoc_volume_label_set_volser(&vlabel,buf);
			info.labelspec=1;
			break;

		case 'L':
			if (format_params.intensity == 0x08) 
			{
				printf("WARNING: using the cdl format " \
				       "without writing a label doesn't " \
				       "make much sense!\n");
				exit(1);
			}
			info.writenolabel=1;
			break;

		case 'b' :
			blksize_param_str=optarg;
			info.blksize_specified=1;
			break;
			
		case 'n' :
			devno_param_str=optarg;
			info.devno_specified=1;
			break;
		
		case 'f' :
			strncpy(dev_filename, optarg, LINE_LENGTH);
			info.node_specified=1;
			break;
		}
	}

	CHECK_SPEC_MAX_ONCE(info.blksize_specified, "blocksize");
	CHECK_SPEC_MAX_ONCE(info.labelspec, "label");
	CHECK_SPEC_MAX_ONCE(info.writenolabel, "omit-label-writing flag");

	if (info.devno_specified)
		PARSE_PARAM_INTO(info.devno, devno_param_str, 16,
				 "device number");
	if (info.blksize_specified)
		PARSE_PARAM_INTO(format_params.blksize,blksize_param_str,10,
				 "blocksize");
	if (info.print_hashmarks)
		PARSE_PARAM_INTO(info.hashstep, hashstep_str,10,"hashstep");

	get_device_name(&info, dev_filename);

        if (!info.blksize_specified)
                format_params = ask_user_for_blksize(format_params);

	if ((filedes = open(info.devname, O_RDWR)) == -1)
		ERRMSG_EXIT(EXIT_FAILURE,"%s: Unable to open device %s: %s\n", 
			    prog_name, info.devname, strerror(errno));

	check_api_version(info.devname);
	check_disk(&info);

	if (check_param(str, &format_params) < 0)
		ERRMSG_EXIT(EXIT_MISUSE, "%s: %s\n", prog_name, str);

	do_format_dasd(&info, &format_params, &vlabel);

	if (close(filedes) != 0)
		ERRMSG("%s: error during close: %s\ncontinuing...\n", 
		       prog_name, strerror(errno));

	return 0;
}
