text: /*
   * dasdview - Display DASD and VTOC information or dump the contents of a DASD
   *
   * Copyright IBM Corp. 2001, 2017
   *
   * s390-tools is free software; you can redistribute it and/or modify
   * it under the terms of the MIT license. See LICENSE for details.
   */
  
  #define _LARGEFILE64_SOURCE    /* needed for unistd.h */
  #define _FILE_OFFSET_BITS 64   /* needed for unistd.h */
  
  #include <ctype.h>
  #include <errno.h>
  #include <fcntl.h>
  #include <getopt.h>
  #include <linux/version.h>
  #include <malloc.h>
  #include <stdarg.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/ioctl.h>
  #include <sys/stat.h>
  #include <sys/utsname.h>
  #include <unistd.h>
  
  #include "lib/libzds.h"
  #include "lib/u2s.h"
  #include "lib/util_base.h"
  #include "lib/util_opt.h"
  #include "lib/util_prg.h"
  #include "lib/vtoc.h"
  #include "lib/zt_common.h"
  
  #include "dasdview.h"
  
  /* Characters per line */
  #define DASDVIEW_CPL 16
  
  static const struct util_prg prg = {
  	.desc = "Display DASD and VTOC information and dump the content of "
  		"a DASD to the console.\n"
  		"DEVICE is the node of the device (e.g. '/dev/dasda').",
  	.args = "DEVICE",
  	.copyright_vec = {
  		{
  			.owner = "IBM Corp.",
  			.pub_first = 2001,
  			.pub_last = 2006,
  		},
  		UTIL_PRG_COPYRIGHT_END
  	}
  };
  
  static struct util_opt opt_vec[] = {
  	UTIL_OPT_SECTION("DUMP OPTIONS"),
  	{
  		.option = { NULL, no_argument, NULL, '1' },
  		.desc = "Show DASD content in short Hex/EBCDIC/ASCII format",
  		.flags = UTIL_OPT_FLAG_NOLONG,
  	},
  	{
  		.option = { NULL, no_argument, NULL, '2' },
  		.desc = "Show DASD content in detailed Hex/EBCDIC/ASCII format",
  		.flags = UTIL_OPT_FLAG_NOLONG,
  	},
  	{
  		.option = { "begin", required_argument, NULL, 'b' },
  		.argument = "BEGIN",
  		.desc = "Specify start of dump in kilobytes (suffix k), "
  			"megabytes (m), blocks (b), tracks (t), or cylinders (c)",
  	},
  	{
  		.option = { "size", required_argument, NULL, 's' },
  		.argument = "SIZE",
  		.desc = "Specify size of dump in kilobytes (suffix k), "
  			"megabytes (m), blocks (b), tracks (t), or cylinders (c)",
  	},
  	UTIL_OPT_SECTION("MISC"),
  	{
  		.option = { "characteristic", no_argument, NULL, 'c' },
  		.desc = "Print the characteristics of a device",
  	},
  	{
  		.option = { "info", no_argument, NULL, 'i' },
  		.desc = "Print general DASD information and geometry",
  	},
  	{
  		.option = { "volser", no_argument, NULL, 'j' },
  		.desc = "Print the volume serial number",
  	},
  	{
  		.option = { "label", no_argument, NULL, 'l' },
  		.desc = "Print information about the volume label",
  	},
  	{
  		.option = { "vtoc", required_argument, NULL, 't' },
  		.argument = "SPEC",
  		.desc = "Print the table of content (VTOC)",
  	},
  	{
  		.option = { "extended", no_argument, NULL, 'x' },
  		.desc = "Print extended DASD information",
  	},
  	UTIL_OPT_HELP,
  	UTIL_OPT_VERSION,
  	UTIL_OPT_END
  };
  
  /*
   * Generate and print an error message based on the formatted
   * text string FMT and a variable amount of extra arguments.
   */
  static void zt_error_print(const char *fmt, ...)
  {
  	va_list args;
  
  	va_start (args, fmt);
  	vsnprintf(error_str, ERROR_STRING_SIZE, fmt, args);
  	va_end (args);
  
  	fprintf(stderr, "Error: %s\n", error_str);
  }
  
  /*
   * replace special characters with dots and question marks
   */
  static void
  dot (char label[]) {
  
          int i;
  	char c;
  
    	for (i = 0; i < 16; i++) {
  
  	        c = label[i];
  		if (c <= 0x20) label[i] = '?';
  		if (c == 0x00) label[i] = '.';
  		if (c == 0x60) label[i] = '?';
  		if (c >= 0x7f) label[i] = '?';
  	}
  }
  
  
  /*
   * Attempts to find the sysfs entry for the given busid and reads
   * the contents of a specified attribute to the buffer
   */
  static int dasdview_read_attribute(char *busid, char *attribute, char *buffer,
  				   size_t count)
  {
  	char path[100];
  	int rc, fd;
  	ssize_t rcount;
  
  	rc = 0;
  	snprintf(path, sizeof(path), "/sys/bus/ccw/devices/%s/%s",
  		 busid, attribute);
  	fd = open(path, O_RDONLY);
  	if (fd < 0)
  		return errno;
  	rcount = read(fd, buffer, count);
  	if (rcount < 0)
  		rc = errno;
  	close(fd);
  	return rc;
  }
  
  static void
  dasdview_get_info(dasdview_info_t *info)
  {
  	int fd;
  	struct dasd_eckd_characteristics *characteristics;
  	char buffer[10];
  	int rc;
  
  	fd = open(info->device, O_RDONLY);
  	if (fd == -1)
  	{
  		zt_error_print("dasdview: open error\n" \
  			"Could not open device '%s'.\n"
  			"Maybe you have specified an unknown device or \n"
  			"you are not authorized to do that.\n",
  			info->device);
  		exit(-1);
  	}
  
  	/* get disk geometry */
  	if (ioctl(fd, HDIO_GETGEO, &info->geo) != 0)
  	{
  	        close(fd);
  		zt_error_print("dasdview: ioctl error\n" \
  			"Could not retrieve disk geometry " \
  			"information.");
  		exit(-1);
  	}
  
  	if (ioctl(fd, BLKSSZGET, &info->blksize) != 0)
  	{
  	        close(fd);
  		zt_error_print("dasdview: ioctl error\n" \
  			"Could not retrieve blocksize information!\n");
  		exit(-1);
  	}
  
  	/* get disk information */
  	if (ioctl(fd, BIODASDINFO2, &info->dasd_info) == 0) {
  		info->dasd_info_version = 2;
  	} else {
  		/* INFO2 failed - try INFO using the same (larger) buffer */
  		if (ioctl(fd, BIODASDINFO, &info->dasd_info) != 0) {
  			close(fd);
  			zt_error_print("dasdview: ioctl error\n"	\
  				       "Could not retrieve disk information.");
  			exit(-1);
  		}
  	}
  
  	characteristics = (struct dasd_eckd_characteristics *)
  		&info->dasd_info.characteristics;
  	if (characteristics->no_cyl == LV_COMPAT_CYL &&
  	    characteristics->long_no_cyl)
  		info->hw_cylinders = characteristics->long_no_cyl;
  	else
  		info->hw_cylinders = characteristics->no_cyl;
  	close(fd);
  
  
  	if(u2s_getbusid(info->device, info->busid) == -1)
  		info->busid_valid = 0;
  	else
  		info->busid_valid = 1;
  
  	rc = dasdview_read_attribute(info->busid, "raw_track_access", buffer,
  				sizeof(buffer));
  	if (rc) {
  		zt_error_print("dasdview: Could not retrieve raw_track_access"
  			       " mode information.");
  		return;
  	}
  	if ('1' == buffer[0])
  		info->raw_track_access = 1;
  }
  
  
  static void
  dasdview_parse_input(unsigned long long *p, dasdview_info_t *info, char *s)
  {
  	unsigned long long l;
  	char *endp;
  	char suffix;
  
  	l = strtoull(s, &endp, 0);
  	if ((endp == s) || ((l+1) == 0))
  		goto error;
  
  	if (*endp) {
  		if (!strchr("kmtbcKMTBC", *endp) || *(endp+1))
  			goto error;
  		suffix = tolower(*endp);
  	} else
  		suffix = 0;
  	if (info->raw_track_access) {
  		switch (suffix) {
  		case 't':
  			l *= RAWTRACKSIZE;
  			break;
  		case 'c':
  			l *= (unsigned long long) info->geo.heads *
  				RAWTRACKSIZE;
  			break;
  		case 0:
  			if (l % RAWTRACKSIZE) {
  				zt_error_print("dasdview: only full tracks can"
  					     " be accessd on devices with "
  					     " raw_track_access enabled.\n", s);
  
  				goto error;
  			}
  			break;
  		default:
  			zt_error_print("dasdview: only types t and c are"
  				       " allowed for devices with"
  				       " raw_track_access enabled.\n", s);
  			goto error;
  		}
  	} else {
  		switch (suffix) {
  		case 'k':
  			l *= 1024LL;
  			break;
  		case 'm':
  			l *= 1024LL * 1024LL;
  			break;
  		case 't':
  			l *= (unsigned long long) info->blksize *
  				(unsigned long long) info->geo.sectors;
  			break;
  		case 'b':
  			l *= (unsigned long long) info->blksize;
  			break;
  		case 'c':
  			l *= (unsigned long long) info->blksize *
  				(unsigned long long) info->geo.sectors *
  				(unsigned long long) info->geo.heads;
  			break;
  		default:
  			break;
  		}
  	}
  	*p = l;
  
  	return;
  error:
  	zt_error_print("dasdview: usage error\n"
  		       "%s is not a valid begin/size value!", s);
  	exit(-1);
  }
  
  /*
   * Print general DASD information.
   */
  static void
  dasdview_print_general_info(dasdview_info_t *info)
  {
  	printf("\n--- general DASD information -----------------" \
  	       "---------------------------------\n");
  	printf("device node            : %s\n",info->device);
  #ifdef SYSFS
  	struct utsname buf;
  	unsigned char a,b,c;
  	char suffix[sizeof(buf.release)];
  	int rc;
  
  	rc = uname(&buf);
  	if(!rc)
  	{
  		sscanf(buf.release, "%c.%c.%c-%s", &a, &b, &c, suffix);
  		if(KERNEL_VERSION(2,5,0) <= KERNEL_VERSION(a, b, c)) {
  			if (info->busid_valid)
  				printf("busid                  : %s\n",
  				       info->busid);
  			else
  				printf("busid                  :"
  				       " <not found>\n");
  		} else {
  #endif
  			printf("device number          : hex %x  \tdec %d\n",
  			       info->dasd_info.devno,
  			       info->dasd_info.devno);
  #ifdef SYSFS
  		}
  	}
  #endif
  	printf("type                   : %4s\n", info->dasd_info.type);
  	printf("device type            : hex %x  \tdec %d\n",
  	       info->dasd_info.dev_type,
  	       info->dasd_info.dev_type);
  	printf("\n--- DASD geometry ----------------------------" \
  	       "---------------------------------\n");
  	printf("number of cylinders    : hex %x  \tdec %d\n",
  	       info->hw_cylinders,
  	       info->hw_cylinders);
  	printf("tracks per cylinder    : hex %x  \tdec %d\n",
  	       info->geo.heads,
  	       info->geo.heads);
  	printf("blocks per track       : hex %x  \tdec %d\n",
  	       info->geo.sectors,
  	       info->geo.sectors);
  	printf("blocksize              : hex %x  \tdec %d\n",
  	       info->blksize,
  	       info->blksize);
  }
  
  /*
   * Loop over the given character array and HEXdump the content.
   */
  static inline void
  dasdview_dump_array(char *name, int size, unsigned char *addr)
  {
  	int i;
  
  	for (i = 0; i < size; i++) {
  		if (i % DASDVIEW_CPL == 0) {
  			if (i == 0)
  				printf("%-23.23s: ", name);
  			else
  				printf("\n%25s", "");
  		} else {
  			if (i % 8 == 0) printf(" ");
  			if (i % 4 == 0) printf(" ");
  		}
  		printf("%02x", addr[i]);
  	}
  	printf("\n");
  }
  
  /*
   * Print extended DASD information.
   */
  static void
  dasdview_print_extended_info(dasdview_info_t *info)
  {
  	unsigned int i;
  	struct dasd_information2_t *dasd_info;
  	struct {
  		unsigned int mask;
  		char *name;
  	} flist[2] = {{DASD_FEATURE_READONLY, "ro"  },
  		      {DASD_FEATURE_USEDIAG,  "diag"}};
  
  	dasd_info = &info->dasd_info;
  	printf("\n--- extended DASD information ----------------" \
  	       "---------------------------------\n");
          printf("real device number     : hex %x  \tdec %d\n",
  	       dasd_info->real_devno, dasd_info->real_devno);
  	printf("subchannel identifier  : hex %x  \tdec %d\n",
  	       dasd_info->schid, dasd_info->schid);
  	printf("CU type  (SenseID)     : hex %x  \tdec %d\n",
  	       dasd_info->cu_type, dasd_info->cu_type);
  	printf("CU model (SenseID)     : hex %x  \tdec %d\n",
  	       dasd_info->cu_model, dasd_info->cu_model);
  	printf("device type  (SenseID) : hex %x  \tdec %d\n",
  	       dasd_info->dev_type, dasd_info->dev_type);
  	printf("device model (SenseID) : hex %x  \tdec %d\n",
  	       dasd_info->dev_model, dasd_info->dev_model);
  	printf("open count             : hex %x  \tdec %d\n",
  	       dasd_info->open_count, dasd_info->open_count);
          printf("req_queue_len          : hex %x  \tdec %d\n",
  	       dasd_info->req_queue_len, dasd_info->req_queue_len);
  	printf("chanq_len              : hex %x  \tdec %d\n",
  	       dasd_info->chanq_len, dasd_info->chanq_len);
  	printf("status                 : hex %x  \tdec %d\n",
  	       dasd_info->status, dasd_info->status);
  	printf("label_block            : hex %x  \tdec %d\n",
  	       dasd_info->label_block, dasd_info->label_block);
  	printf("FBA_layout             : hex %x  \tdec %d\n",
  	       dasd_info->FBA_layout, dasd_info->FBA_layout);
          printf("characteristics_size   : hex %x  \tdec %d\n",
  	       dasd_info->characteristics_size,
  	       dasd_info->characteristics_size);
          printf("confdata_size          : hex %x  \tdec %d\n",
  	       dasd_info->confdata_size, dasd_info->confdata_size);
  
  	if (info->dasd_info_version >= 2) {
  		printf("format                 : hex %x  \tdec %d      \t%s\n",
  		       dasd_info->format, dasd_info->format,
  		       dasd_info->format == DASD_FORMAT_NONE ?
  		       "NOT formatted" :
  		       dasd_info->format == DASD_FORMAT_LDL  ?
  		       "LDL formatted" :
  		       dasd_info->format == DASD_FORMAT_CDL  ?
  		       "CDL formatted" : "unknown format");
  
  		printf("features               : hex %x  \tdec %d      \t",
  		       dasd_info->features, dasd_info->features);
  		if (dasd_info->features == DASD_FEATURE_DEFAULT)
  			printf("default\n");
  		else {
  			for (i = 0; i < (sizeof(flist)/sizeof(flist[0])); i++)
  				if (dasd_info->features & flist[i].mask)
  					printf("%s ",flist[i].name);
  			printf("\n");
  		}
  	}
  	printf("\n");
  	dasdview_dump_array("characteristics",
  			    dasd_info->characteristics_size,
  			    dasd_info->characteristics);
  	printf("\n");
  	dasdview_dump_array("configuration_data",
  			    dasd_info->confdata_size,
  			    dasd_info->configuration_data);
  }
  
  
  static void
  dasdview_read_vlabel(dasdview_info_t *info, volume_label_t *vlabel)
  {
  	volume_label_t tmp;
  	unsigned long  pos;
  
  	pos = info->dasd_info.label_block * info->blksize;
  
  	bzero(vlabel, sizeof(volume_label_t));
  	if ((strncmp(info->dasd_info.type, "ECKD", 4) == 0) &&
  	    (!info->dasd_info.FBA_layout)) {
  		/* OS/390 and zOS compatible disk layout */
  		vtoc_read_volume_label(info->device, pos, vlabel);
  	}
  	else {
  		/* standard LINUX disk layout */
  		vtoc_read_volume_label(info->device, pos, &tmp);
  		memcpy(vlabel->vollbl, &tmp, sizeof(tmp)-4);
  	}
  }
  
  
  static void
  dasdview_print_vlabel(dasdview_info_t *info)
  {
  	volume_label_t vlabel;
  	volume_label_t *tmpvlabel;
  	int rc;
  	unsigned char s4[5], t4[5], s5[6], t5[6], s6[7], t6[7];
  	char s14[15], t14[15], s29[30], t29[30];
  	int i;
  
  	if (info->raw_track_access) {
  		rc = lzds_dasd_read_vlabel(info->dasd);
  		if (rc) {
  			zt_error_print("error when reading label from device:"
  				       " rc=%d\n", rc);
  			exit(-1);
  		}
  		lzds_dasd_get_vlabel(info->dasd, &tmpvlabel);
  		memcpy(&vlabel, tmpvlabel, sizeof(vlabel));
  	} else
  		dasdview_read_vlabel(info, &vlabel);
  
  	printf("\n--- volume label -----------------------------" \
  	       "---------------------------------\n");
  
  	bzero(s4, 5); bzero(t4, 5); strncpy((char *)s4, vlabel.volkey, 4);
  	printf("volume label key        : ascii  '%4s'\n", s4);
  	vtoc_ebcdic_dec((char *)s4, (char *)t4, 4);
  	printf("                        : ebcdic '%4s'\n", t4);
  	printf("                        : hex    ");
  	for (i=0; i<4; i++) printf("%02x", s4[i]);
  
  	bzero(s4, 5); bzero(s4, 5); strncpy((char *)s4, vlabel.vollbl, 4);
  	printf("\n\nvolume label identifier : ascii  '%4s'\n", s4);
  	vtoc_ebcdic_dec((char *)s4, (char *)t4, 4);
  	printf("                        : ebcdic '%4s'\n", t4);
  	printf("                        : hex    ");
  	for (i=0; i<4; i++) printf("%02x", s4[i]);
  
  	bzero(s6, 7); bzero(t6, 7); strncpy((char *)s6, vlabel.volid, 6);
  	printf("\n\nvolume identifier       : ascii  '%6s'\n", s6);
  	vtoc_ebcdic_dec((char *)s6, (char *)t6, 6);
  	printf("                        : ebcdic '%6s'\n", t6);
  	printf("                        : hex    ");
  	for (i=0; i<6; i++) printf("%02x", s6[i]);
  
  	printf("\n\nsecurity byte           : hex    %02x\n", vlabel.security);
  
  	printf("\n\nVTOC pointer            : hex    %04x%04x%02x ",
  	       vlabel.vtoc.cc, vlabel.vtoc.hh, vlabel.vtoc.b);
  	if ((vlabel.vtoc.cc == 0x4040) && (vlabel.vtoc.hh == 0x4040) &&
  	    (vlabel.vtoc.b == 0x40))
  		printf("\n");
  	else
  		printf("\n                                 " \
  		       "(cyl %d, trk %d, blk %d)\n\n",
  		       vtoc_get_cyl_from_cchhb(&vlabel.vtoc),
  		       vtoc_get_head_from_cchhb(&vlabel.vtoc), vlabel.vtoc.b);
  
  	bzero(s5, 6); bzero(t5, 6); strncpy((char *)s5, vlabel.res1, 5);
  	printf("reserved                : ascii  '%5s'\n", s5);
  	vtoc_ebcdic_dec((char *)s5, (char *)t5, 5);
  	printf("                        : ebcdic '%5s'\n", t5);
  	printf("                        : hex    ");
  	for (i=0; i<5; i++) printf("%02x", s5[i]);
  
  	bzero(s4, 5); bzero(t4, 5); strncpy((char *)s4, vlabel.cisize, 4);
  	printf("\n\nCI size for FBA         : ascii  '%4s'\n", s4);
  	vtoc_ebcdic_dec((char *)s4, (char *)t4, 4);
  	printf("                        : ebcdic '%4s'\n", t4);
  	printf("                        : hex    ");
  	for (i=0; i<4; i++) printf("%02x", s4[i]);
  
  	bzero(s4, 5); bzero(t4, 5); strncpy((char *)s4, vlabel.blkperci, 4);
  	printf("\n\nblocks per CI (FBA)     : ascii  '%4s'\n", s4);
  	vtoc_ebcdic_dec((char *)s4, (char *)t4, 4);
  	printf("                        : ebcdic '%4s'\n", t4);
  	printf("                        : hex    ");
  	for (i=0; i<4; i++) printf("%02x", s4[i]);
  
  	bzero(s4, 5); bzero(t4, 5); strncpy((char *)s4, vlabel.labperci, 4);
  	printf("\n\nlabels per CI (FBA)     : ascii  '%4s'\n", s4);
  	vtoc_ebcdic_dec((char *)s4, (char *)t4, 4);
  	printf("                        : ebcdic '%4s'\n", t4);
  	printf("                        : hex    ");
  	for (i=0; i<4; i++) printf("%02x", s4[i]);
  
  	bzero(s4, 5); bzero(t4, 5); strncpy((char *)s4, vlabel.res2, 4);
  	printf("\n\nreserved                : ascii  '%4s'\n", s4);
  	vtoc_ebcdic_dec((char *)s4, (char *)t4, 4);
  	printf("                        : ebcdic '%4s'\n", t4);
  	printf("                        : hex    ");
  	for (i=0; i<4; i++) printf("%02x", s4[i]);
  
  	bzero(s14, 15); bzero(t14, 15); strncpy(s14, vlabel.lvtoc, 14);
  	printf("\n\nowner code for VTOC     : ascii  '%14s'\n", s14);
  	vtoc_ebcdic_dec(s14, t14, 14);
  	printf("                          ebcdic '%14s'\n", t14);
  	printf("                          hex    ");
  	for (i=0; i<14; i++)
  	{
  		printf("%02x", s14[i]);
  		if ((i+1)%4 == 0) printf(" ");
  		if ((i+1)%8 == 0) printf(" ");
  	}
  
  	bzero(s29, 30); strncpy(s29, vlabel.res3, 28);
  	printf("\n\nreserved                : ascii  '%28s'\n", s29);
  	bzero(t29, 30);
  	vtoc_ebcdic_dec(s29, t29, 28);
  	printf("                          ebcdic '%28s'\n", t29);
  	printf("                          hex    ");
  	for (i=0; i<28; i++)
  	{
  		printf("%02x", s29[i]);
  		if ((i+1)%4 == 0) printf(" ");
  		if ((i+1)%8 == 0) printf(" ");
  		if ((i+1)%16 == 0) printf("\n                " \
  					  "                 ");
  	}
  
  	bzero(s4, 5); bzero(t4, 5); s4[0] = vlabel.ldl_version;
  	printf("\n\nldl_version             : ascii  '%1s'\n", s4);
  	vtoc_ebcdic_dec((char *)s4, (char *)t4, 1);
  	printf("                        : ebcdic '%1s'\n", t4);
  	printf("                        : hex    %02x", s4[0]);
  
  	printf("\n\nformatted_blocks        : dec %llu",
  	       vlabel.formatted_blocks);
  	printf("\n                        : hex %016llx",
  	       vlabel.formatted_blocks);
  
  	printf("\n");
  }
  
  
  static void
  dasdview_print_volser(dasdview_info_t *info)
  {
  	volume_label_t vlabel;
  	volume_label_t *tmpvlabel;
  	char           volser[7];
  	char           vollbl[5];
  	int rc;
  
  	if (info->raw_track_access) {
  		rc = lzds_dasd_read_vlabel(info->dasd);
  		if (rc) {
  			zt_error_print("error when reading label from device:"
  				       " rc=%d\n", rc);
  			exit(-1);
  		}
  		lzds_dasd_get_vlabel(info->dasd, &tmpvlabel);
  		memcpy(&vlabel, tmpvlabel, sizeof(vlabel));
  	} else
  		dasdview_read_vlabel(info, &vlabel);
  
  	bzero(vollbl, 5);
  	bzero(volser, 7);
  	strncpy(vollbl, vlabel.vollbl, 4);
  	vtoc_ebcdic_dec(vollbl, vollbl, 4);
  
  	if ((strncmp(vollbl, "VOL1", 4) == 0)||(strncmp(vollbl, "LNX1", 4) == 0)) {
  	        strncpy(volser, vlabel.volid, 6);
  		vtoc_ebcdic_dec(volser, volser, 6);
  	} else
  	        strncpy(volser, "      ", 6);
  
  	printf("%6.6s\n", volser);
  }
  
  
  static void
  dasdview_read_vtoc(dasdview_info_t *info)
  {
          volume_label_t vlabel;
  	format1_label_t tmp;
  	unsigned long maxblk, pos;
  	u_int64_t vtocblk;
  	int i;
  
  	pos = info->dasd_info.label_block * info->blksize;
  
  	bzero(&vlabel, sizeof(vlabel));
  	if ((strncmp(info->dasd_info.type, "ECKD", 4) == 0) &&
  	    (!info->dasd_info.FBA_layout))
  	{
  		/* OS/390 and zOS compatible disk layout */
  		vtoc_read_volume_label(info->device, pos, &vlabel);
  	}
  	else
  	{
  		zt_error_print("dasdview: disk layout error\n" \
  			"%s is not formatted with the z/OS " \
  			"compatible disk layout!\n", info->device);
  		exit(-1);
  	}
  
  	vtocblk = (u_int64_t) vtoc_get_cyl_from_cchhb(&vlabel.vtoc) *
  		info->geo.heads * info->geo.sectors +
  		vtoc_get_head_from_cchhb(&vlabel.vtoc) * info->geo.sectors +
  		vlabel.vtoc.b;
  
  	/*
  	 * geo.cylinders is the minimum of hw_cylinders and LV_COMPAT_CYL
  	 * Actually the vtoc should be located in in the first 65k-1 tracks
  	 * so this check could be even more restrictive, but it doesn't
  	 * hurt the way it is. Linux cdl format restricts the vtoc to
  	 * the first two tracks anyway.
  	 */
  	maxblk = info->geo.cylinders * info->geo.heads * info->geo.sectors;
  
  	if ((vtocblk <= 0) || (vtocblk > maxblk))
  	{
  		zt_error_print("dasdview: VTOC error\n" \
  			"Volume label VTOC pointer is not valid!\n");
  		exit(-1);
  	}
  
  	vtoc_read_label(info->device, (vtocblk - 1) * info->blksize,
  			NULL, &info->f4, NULL, NULL);
  
  	if ((info->f4.DS4KEYCD[0] != 0x04) ||
  	    (info->f4.DS4KEYCD[43] != 0x04) ||
  	    (info->f4.DS4IDFMT != 0xf4))
  	{
  		/* format4 DSCB is invalid */
  		zt_error_print("dasdview: VTOC error\n" \
  			"Format 4 DSCB is invalid!\n");
  		exit(-1);
  	}
  
  	info->f4c++;
  	pos = (vtocblk - 1) * info->blksize;
  
  	for (i=1; i<info->geo.sectors; i++)
  	{
  	        pos += info->blksize;
  	        vtoc_read_label(info->device, pos, &tmp, NULL, NULL, NULL);
  
  		switch (tmp.DS1FMTID) {
  		case 0xf1:
  			memcpy(&info->f1[info->f1c], &tmp,
  			       sizeof(format1_label_t));
  			info->f1c++;
  			break;
  		case 0xf4:
  			info->f4c++;
  			break;
  		case 0xf5:
  			memcpy(&info->f5, &tmp, sizeof(format1_label_t));
  			info->f5c++;
  			break;
  		case 0xf7:
  			memcpy(&info->f7, &tmp, sizeof(format1_label_t));
  			info->f7c++;
  			break;
  		case 0xf8:
  			memcpy(&info->f8[info->f8c], &tmp,
  			       sizeof(format1_label_t));
  			info->f8c++;
  			break;
  		case 0xf9:
  			memcpy(&info->f9[info->f9c], &tmp,
  			       sizeof(format1_label_t));
  			info->f9c++;
  			break;
  		case 0x00:
  			break;
  		default:
  			printf("Unknown label in VTOC detected (id=%x)\n",
  				 tmp.DS1FMTID);
  		}
  	}
  
  	if (info->f4c > 1)
          {
  		zt_error_print("dasdview: VTOC error\n" \
  			"More than one FMT4 DSCB!\n");
  		exit(-1);
  	}
  
  	if (info->f5c > 1)
          {
  		zt_error_print("dasdview: VTOC error\n" \
  			"More than one FMT5 DSCB!\n");
  		exit(-1);
  	}
  
  	if (info->f7c > 1)
          {
  		zt_error_print("dasdview: VTOC error\n" \
  			"More than one FMT7 DSCB!\n");
  		exit(-1);
  	}
  }
  
  static void dasdview_print_format1_8_short_info(format1_label_t *f1,
  						struct hd_geometry *geo)
  {
  	char s6[7], s13[14], s44[45];
  	unsigned long track_low , track_up;
  
  	bzero(s44, 45);
  	strncpy(s44, f1->DS1DSNAM, 44);
  	vtoc_ebcdic_dec(s44, s44, 44);
  	bzero(s6, 7);
  	strncpy(s6, (char *)f1->DS1DSSN, 6);
  	vtoc_ebcdic_dec(s6, s6, 6);
  	bzero(s13, 14);
  	strncpy(s13, (char *)f1->DS1SYSCD, 13);
  	vtoc_ebcdic_dec(s13, s13, 13);
  
  	track_low = cchh2trk(&f1->DS1EXT1.llimit, geo);
  	track_up = cchh2trk(&f1->DS1EXT1.ulimit, geo);
  
  	printf(" | %44s |          trk |          trk |\n",
  	       s44);
  	printf(" | data set serial number :"	     \
  	       " '%6s'            |"					\
  	       " %12ld | %12ld |\n", s6, track_low, track_up);
  	printf(" | system code            :"   \
  	       " '%13s'     |"						\
  	       "      cyl/trk |      cyl/trk |\n", s13);
  	printf(" | creation date          :"	       \
  	       "  year %4d, day %3d |"				\
  	       " %8d/%3d | %8d/%3d |\n",
  	       f1->DS1CREDT.year + 1900,
  	       f1->DS1CREDT.day,
  	       vtoc_get_cyl_from_cchh(&f1->DS1EXT1.llimit),
  	       vtoc_get_head_from_cchh(&f1->DS1EXT1.llimit),
  	       vtoc_get_cyl_from_cchh(&f1->DS1EXT1.ulimit),
  	       vtoc_get_head_from_cchh(&f1->DS1EXT1.ulimit));
  	printf(" +-----------------------------------------"		\
  	       "-----+--------------+--------------+\n");
  }
  
  static void dasdview_print_vtoc_info(dasdview_info_t *info)
  {
          int i;
  
  	printf("--- VTOC info --------------------------------" \
  	       "---------------------------------\n");
  	printf("The VTOC contains:\n");
  	printf("  %d format 1 label(s)\n", info->f1c);
  	printf("  %d format 4 label(s)\n", info->f4c);
  	printf("  %d format 5 label(s)\n", info->f5c);
  	printf("  %d format 7 label(s)\n", info->f7c);
  	printf("  %d format 8 label(s)\n", info->f8c);
  	printf("  %d format 9 label(s)\n", info->f9c);
  
  	if ((info->f1c < 1) && (info->f8c < 1))	{
  	        printf("There are no partitions defined.\n");
  	} else {
  	        printf("Other mainframe operating systems would see " \
  		       "the following data sets:\n");
  		printf(" +----------------------------------------------+" \
  		       "--------------+--------------+\n");
  		printf(" | data set                                     |" \
  		       " start        | end          |\n");
  		printf(" +----------------------------------------------+" \
  		       "--------------+--------------+\n");
  
  		for (i=0; i<info->f1c; i++)
  			dasdview_print_format1_8_short_info(&info->f1[i],
  							    &info->geo);
  		for (i=0; i<info->f8c; i++)
  			dasdview_print_format1_8_short_info(&info->f8[i],
  							    &info->geo);
  	}
  }
  
  static void dasdview_print_short_info_extent_raw(extent_t *ext)
  {
  	if (ext->typeind > 0x00)
  		printf("   %3d          (%5d,%5d)        (%5d,%5d)\n",
  		       ext->seqno,
  		       vtoc_get_cyl_from_cchh(&ext->llimit),
  		       vtoc_get_head_from_cchh(&ext->llimit),
  		       vtoc_get_cyl_from_cchh(&ext->ulimit),
  		       vtoc_get_head_from_cchh(&ext->ulimit));
  }
  
  static void dasdview_print_format1_8_short_info_raw(format1_label_t *f1,
  						    dasdview_info_t *info)
  {
  	char s6[7], s13[14], s44[45];
  	unsigned long long j;
  	format3_label_t *f3;
  	format9_label_t *f9;
  	struct dscb *dscb;
  	int rc;
  
  	bzero(s44, 45);
  	strncpy(s44, f1->DS1DSNAM, 44);
  	vtoc_ebcdic_dec(s44, s44, 44);
  	bzero(s6, 7);
  	strncpy(s6, (char *)f1->DS1DSSN, 6);
  	vtoc_ebcdic_dec(s6, s6, 6);
  	bzero(s13, 14);
  	strncpy(s13, (char *)f1->DS1SYSCD, 13);
  	vtoc_ebcdic_dec(s13, s13, 13);
  
  	printf("data set name          : '%44s'\n", s44);
  	printf("data set serial number : '%6s'\n", s6);
  	printf("system code            : '%13s'\n", s13);
  	printf("creation date          :  year %4d, day %3d\n",
  	       f1->DS1CREDT.year + 1900, f1->DS1CREDT.day);
  
  	printf("flags                  :  ");
  	if (f1->DS1FLAG1 & 0x80)
  		printf("DS1COMPR ");
  	if (f1->DS1FLAG1 & 0x40)
  		printf("DS1CPOIT ");
  	if (f1->DS1FLAG1 & 0x20)
  		printf("DS1EXPBY ");
  	if (f1->DS1FLAG1 & 0x10)
  		printf("DS1RECAL ");
  	if (f1->DS1FLAG1 & 0x08)
  		printf("DS1LARGE ");
  	if (f1->DS1FLAG1 & 0x04)
  		printf("unknown ");
  	if ((f1->DS1FLAG1 & 0x01) && (f1->DS1FLAG1 & 0x02))
  		printf("DS1EATTR=not used ");
  	if (!(f1->DS1FLAG1 & 0x01) && (f1->DS1FLAG1 & 0x02))
  		printf("DS1EATTR=optional ");
  	if ((f1->DS1FLAG1 & 0x01) && !(f1->DS1FLAG1 & 0x02))
  		printf("DS1EATTR=no ");
  	if (f1->DS1FLAG1 & 0x00)
  		printf("DS1EATTR=default ");
  	printf("\n");
  
  	printf("SMS flags              :  ");
  	if (f1->DS1SMSFG & 0x80)
  		printf("DS1SMSDS ");
  	if (f1->DS1SMSFG & 0x40)
  		printf("DS1SMSUC ");
  	if (f1->DS1SMSFG & 0x20)
  		printf("DS1REBLK ");
  	if (f1->DS1SMSFG & 0x10)
  		printf("DS1CRSDB ");
  	if (f1->DS1SMSFG & 0x08)
  		printf("DS1PDSE ");
  	if (f1->DS1SMSFG & 0x04)
  		printf("DS1STRP ");
  	if (f1->DS1SMSFG & 0x02)
  		printf("DS1PDSEX ");
  	if (f1->DS1SMSFG & 0x01)
  		printf("DS1DSAE ");
  	printf("\n");
  
  	printf("organisation           :  ");
  	if (f1->DS1DSRG1 & 0x80)
  		printf("DS1DSGIS ");
  	if (f1->DS1DSRG1 & 0x40)
  		printf("DS1DSGPS ");
  	if (f1->DS1DSRG1 & 0x20)
  		printf("DS1DSGDA ");
  	if (f1->DS1DSRG1 & 0x10)
  		printf("DS1DSGCX ");
  	if (f1->DS1DSRG1 & 0x08)
  		printf("reserved ");
  	if (f1->DS1DSRG1 & 0x04)
  		printf("reserved ");
  	if (f1->DS1DSRG1 & 0x02)
  		printf("DS1DSGPO ");
  	if (f1->DS1DSRG1 & 0x01)
  		printf("DS1DSGU ");
  	if (f1->DS1DSRG2 & 0x80)
  		printf("DS1DSGGS ");
  	if (f1->DS1DSRG2 & 0x40)
  		printf("DS1DSGTX ");
  	if (f1->DS1DSRG2 & 0x20)
  		printf("DS1DSGTQ ");
  	if (f1->DS1DSRG2 & 0x10)
  		printf("reserved ");
  	if (f1->DS1DSRG2 & 0x08)
  		printf("DS1ACBM ");
  	if (f1->DS1DSRG2 & 0x04)
  		printf("DS1DSGTR ");
  	if (f1->DS1DSRG2 & 0x02)
  		printf("reserved ");
  	if (f1->DS1DSRG2 & 0x01)
  		printf("reserved");
  	printf("\n");
  
  	printf("record format          :  ");
  	if ((f1->DS1RECFM & 0x80) && !(f1->DS1RECFM & 0x40))
  		printf("DS1RECFF ");
  	if (!(f1->DS1RECFM & 0x80) && (f1->DS1RECFM & 0x40))
  		printf("DS1RECFV ");
  	if ((f1->DS1RECFM & 0x80) && (f1->DS1RECFM & 0x40))
  		printf("DS1RECFU ");
  	if (f1->DS1RECFM & 0x20)
  		printf("DS1RECFT ");
  	if (f1->DS1RECFM & 0x10)
  		printf("DS1RECFB ");
  	if (f1->DS1RECFM & 0x08)
  		printf("DS1RECFS ");
  	if (f1->DS1RECFM & 0x04)
  		printf("DS1RECFA ");
  	if (f1->DS1RECFM & 0x02)
  		printf("DS1RECMC ");
  	if (f1->DS1RECFM & 0x01)
  		printf("reserved");
  	printf("\n");
  
  	printf("(max) block length     :  %u\n", f1->DS1BLKL);
  	printf("logical record length  :  %u\n", f1->DS1LRECL);
  
  	printf("extents belonging to this dataset:\n");
  	printf("  seqno        llimit (cyl, trk)    ulimit (cyl, trk)\n");
  
  	/* The format 1 label can point to a chain of f3 labels
  	 * The format 8 label points to a (chain of) f9 labels
  	 * The format 9 label may contain several format 3 labels, but as
  	 * far as I know, it is still OK to follow the 'next dscb' chain.
  	 * So for a format 9 label I have to follow this chain until I
  	 * find the first format 3 dscb and then I can follow the format 3
  	 * dscbs to the end of the chain.
  	 */
  	rc = lzds_raw_vtoc_get_dscb_from_cchhb(info->rawvtoc, &f1->DS1PTRDS,
  					       &dscb);
  	/* The first f9 label contains extra data that we may want
  	 * to print here */
  	while (!rc && dscb && dscb->fmtid == 0xf9) {
  		f9 = (format9_label_t *)dscb;
  		rc = lzds_raw_vtoc_get_dscb_from_cchhb(info->rawvtoc,
  						       &f9->DS9PTRDS, &dscb);
  	}
  	if (rc) {
  		zt_error_print("dasdview: Broken format 3 DSCB"
  			       " chain \n");
  		exit(-1);
  	}
  	f3 = (dscb && dscb->fmtid == 0xf3) ? (format3_label_t *)dscb : NULL;
  
  	/* first print the extents that are part of the f1/f8 label itself */
  	dasdview_print_short_info_extent_raw(&f1->DS1EXT1);
  	dasdview_print_short_info_extent_raw(&f1->DS1EXT2);
  	dasdview_print_short_info_extent_raw(&f1->DS1EXT3);
  
  	/* now follow the f3 chain into the rabbit hole */
  	while (f3) {
  		/* sanity check */
  		if (f3->DS3FMTID != 0xf3) {
  			zt_error_print("dasdview: Broken format 3 DSCB"
  				       " chain \n");
  			exit(-1);
  		}
  		for (j = 0; j < 4; ++j)
  			dasdview_print_short_info_extent_raw(&f3->DS3EXTNT[j]);
  		for (j = 0; j < 9; ++j)
  			dasdview_print_short_info_extent_raw(&f3->DS3ADEXT[j]);
  		rc = lzds_raw_vtoc_get_dscb_from_cchhb(info->rawvtoc,
  						       &f3->DS3PTRDS,
  						       (struct dscb **)&f3);
  		if (rc) {
  			zt_error_print("dasdview: Broken format 3 DSCB"
  				       " chain \n");
  			exit(-1);
  		}
  	}
  	printf("\n");
  }
  
  static void dasdview_print_vtoc_info_raw(dasdview_info_t *info)
  {
  	struct dscbiterator *it;
  	struct dscb *dscb;
  	int rc;
  	int f1c, f3c, f4c, f5c, f7c, f8c, f9c;
  
  	f1c = 0;
  	f3c = 0;
  	f4c = 0;
  	f5c = 0;
  	f7c = 0;
  	f8c = 0;
  	f9c = 0;
  	rc = lzds_raw_vtoc_alloc_dscbiterator(info->rawvtoc, &it);
  	if (rc) {
  		zt_error_print("dasdview: could not allocate DSCB iterator \n");
  		exit(-1);
  	}
  	while (!lzds_dscbiterator_get_next_dscb(it, &dscb)) {
  		if (dscb->fmtid == 0xf1)
  			++f1c;
  		else if (dscb->fmtid == 0xf3)
  			++f3c;
  		else if (dscb->fmtid == 0xf4)
  			++f4c;
  		else if (dscb->fmtid == 0xf5)
  			++f5c;
  		else if (dscb->fmtid == 0xf7)
  			++f7c;
  		else if (dscb->fmtid == 0xf8)
  			++f8c;
  		else if (dscb->fmtid == 0xf9)
  			++f9c;
  	}
  	lzds_dscbiterator_free(it);
  	printf("--- VTOC info --------------------------------" \
  	       "---------------------------------\n");
  	printf("The VTOC contains:\n");
  	printf("  %d format 1 label(s)\n", f1c);
  	printf("  %d format 3 label(s)\n", f3c);
  	printf("  %d format 4 label(s)\n", f4c);
  	printf("  %d format 5 label(s)\n", f5c);
  	printf("  %d format 7 label(s)\n", f7c);
  	printf("  %d format 8 label(s)\n", f8c);
  	printf("  %d format 9 label(s)\n", f9c);
  
  	rc = lzds_raw_vtoc_alloc_dscbiterator(info->rawvtoc, &it);
  	if (rc) {
  		zt_error_print("dasdview: could not allocate DSCB iterator \n");
  		exit(-1);
  	}
  	while (!lzds_dscbiterator_get_next_dscb(it, &dscb)) {
  		if (dscb->fmtid == 0xf1 || dscb->fmtid == 0xf8)
  			dasdview_print_format1_8_short_info_raw(
  				(format1_label_t *)dscb, info);
  	}
  	lzds_dscbiterator_free(it);
  }
  
  
  /*
   * Note: the explicit cylinder/head conversion for large volume
   * adresses should not be necessary for entries that point to
   * vtoc labels, as those must be located in the first 65K-1 tracks,
   * but we do it anyway to be on the safe side.
   */
  static void dasdview_print_format1_8_no_head(format1_label_t *f1)
  {
  	char s6[7], s13[14], s44[45];
  	int i;
  
  	bzero(s6, 7);
  	bzero(s13, 14);
  	bzero(s44, 45);
  
  	strncpy(s44, f1->DS1DSNAM, 44);
  	printf("DS1DSNAM    : ascii  '%44s'\n", s44);
  	vtoc_ebcdic_dec(s44, s44, 44);
  	printf("              ebcdic '%44s'\n", s44);
  	printf("DS1FMTID    : dec %d, hex %02x\n",
  	       f1->DS1FMTID, f1->DS1FMTID);
  	printf("DS1DSSN     : hex    ");
  	for (i=0; i<6; i++) printf("%02x", f1->DS1DSSN[i]);
  	strncpy(s6, (char *)f1->DS1DSSN, 6);
  	printf("\n              ascii  '%6s'\n", s6);
  	vtoc_ebcdic_dec(s6, s6, 6);
  	printf("              ebcdic '%6s'\n", s6);
  	printf("DS1VOLSQ    : dec %d, hex %04x\n",
  	       f1->DS1VOLSQ, f1->DS1VOLSQ);
  	printf("DS1CREDT    : hex %02x%04x "	\
  	       "(year %d, day %d)\n",
  	       f1->DS1CREDT.year, f1->DS1CREDT.day,
  	       f1->DS1CREDT.year + 1900,
  	       f1->DS1CREDT.day);
  	printf("DS1EXPDT    : hex %02x%04x "	\
  	       "(year %d, day %d)\n",
  	       f1->DS1EXPDT.year, f1->DS1EXPDT.day,
  	       f1->DS1EXPDT.year + 1900,
  	       f1->DS1EXPDT.day);
  	printf("DS1NOEPV    : dec %d, hex %02x\n",
  	       f1->DS1NOEPV, f1->DS1NOEPV);
  	printf("DS1NOBDB    : dec %d, hex %02x\n",
  	       f1->DS1NOBDB, f1->DS1NOBDB);
  	printf("DS1FLAG1    : dec %d, hex %02x\n",
  	       f1->DS1FLAG1, f1->DS1FLAG1);
  	printf("DS1SYSCD    : hex    ");
  	for (i=0; i<13; i++) printf("%02x", f1->DS1SYSCD[i]);
  	strncpy(s13, (char *)f1->DS1SYSCD, 13);
  	printf("\n              ascii  '%13s'\n", s13);
  	vtoc_ebcdic_dec(s13, s13, 13);
  	printf("              ebcdic '%13s'\n", s13);
  	printf("DS1REFD     : hex %02x%04x "	\
  	       "(year %d, day %d)\n",
  	       f1->DS1REFD.year, f1->DS1REFD.day,
  	       f1->DS1REFD.year + 1900,
  	       f1->DS1REFD.day);
  	printf("DS1SMSFG    : dec %d, hex %02x\n",
  	       f1->DS1SMSFG, f1->DS1SMSFG);
  	printf("DS1SCXTF    : dec %d, hex %02x\n",
  	       f1->DS1SCXTF, f1->DS1SCXTF);
  	printf("DS1SCXTV    : dec %d, hex %04x\n",
  	       f1->DS1SCXTV, f1->DS1SCXTV);
  	printf("DS1DSRG1    : dec %d, hex %02x\n",
  	       f1->DS1DSRG1, f1->DS1DSRG1);
  	printf("DS1DSRG2    : dec %d, hex %02x\n",
  	       f1->DS1DSRG2, f1->DS1DSRG2);
  	printf("DS1RECFM    : dec %d, hex %02x\n",
  	       f1->DS1RECFM, f1->DS1RECFM);
  	printf("DS1OPTCD    : dec %d, hex %02x\n",
  	       f1->DS1OPTCD, f1->DS1OPTCD);
  	printf("DS1BLKL     : dec %d, hex %04x\n",
  	       f1->DS1BLKL, f1->DS1BLKL);
  	printf("DS1LRECL    : dec %d, hex %04x\n",
  	       f1->DS1LRECL, f1->DS1LRECL);
  	printf("DS1KEYL     : dec %d, hex %02x\n",
  	       f1->DS1KEYL, f1->DS1KEYL);
  	printf("DS1RKP      : dec %d, hex %04x\n",
  	       f1->DS1RKP, f1->DS1RKP);
  	printf("DS1DSIND    : dec %d, hex %02x\n",
  	       f1->DS1DSIND, f1->DS1DSIND);
  	printf("DS1SCAL1    : dec %d, hex %02x\n",
  	       f1->DS1SCAL1, f1->DS1SCAL1);
  	printf("DS1SCAL3    : hex ");
  	for (i=0; i<3; i++) printf("%02x", f1->DS1SCAL3[i]);
  	printf("\nDS1LSTAR    : hex %04x%02x "	\
  	       "(trk %d, blk %d)\n",
  	       f1->DS1LSTAR.tt, f1->DS1LSTAR.r,
  	       f1->DS1LSTAR.tt, f1->DS1LSTAR.r);
  	printf("DS1TRBAL    : dec %d, hex %04x\n",
  	       f1->DS1TRBAL, f1->DS1TRBAL);
  	printf("reserved    : dec %d, hex %04x\n",
  	       f1->res1, f1->res1);
  	printf("DS1EXT1     : hex %02x%02x%04x%04x%04x%04x\n",
  	       f1->DS1EXT1.typeind,
  	       f1->DS1EXT1.seqno,
  	       f1->DS1EXT1.llimit.cc,
  	       f1->DS1EXT1.llimit.hh,
  	       f1->DS1EXT1.ulimit.cc,
  	       f1->DS1EXT1.ulimit.hh);
  	printf("              typeind    : dec %d, hex %02x\n",
  	       f1->DS1EXT1.typeind,
  	       f1->DS1EXT1.typeind);
  	printf("              seqno      : dec %d, hex %02x\n",
  	       f1->DS1EXT1.seqno, f1->DS1EXT1.seqno);
  	printf("              llimit     : hex %04x%04x "	\
  	       "(cyl %d, trk %d)\n",
  	       f1->DS1EXT1.llimit.cc,
  	       f1->DS1EXT1.llimit.hh,
  	       vtoc_get_cyl_from_cchh(&f1->DS1EXT1.llimit),
  	       vtoc_get_head_from_cchh(&f1->DS1EXT1.llimit));
  	printf("              ulimit     : hex %04x%04x "	\
  	       "(cyl %d, trk %d)\n",
  	       f1->DS1EXT1.ulimit.cc,
  	       f1->DS1EXT1.ulimit.hh,
  	       vtoc_get_cyl_from_cchh(&f1->DS1EXT1.ulimit),
  	       vtoc_get_head_from_cchh(&f1->DS1EXT1.ulimit));
  	printf("DS1EXT2     : hex %02x%02x%04x%04x%04x%04x\n",
  	       f1->DS1EXT2.typeind,
  	       f1->DS1EXT2.seqno,
  	       f1->DS1EXT2.llimit.cc,
  	       f1->DS1EXT2.llimit.hh,
  	       f1->DS1EXT2.ulimit.cc,
  	       f1->DS1EXT2.ulimit.hh);
  	printf("              typeind    : dec %d, hex %02x\n",
  	       f1->DS1EXT2.typeind,
  	       f1->DS1EXT2.typeind);
  	printf("              seqno      : dec %d, hex %02x\n",
  	       f1->DS1EXT2.seqno, f1->DS1EXT2.seqno);
  	printf("              llimit     : hex %04x%04x "	\
  	       "(cyl %d, trk %d)\n",
  	       f1->DS1EXT2.llimit.cc,
  	       f1->DS1EXT2.llimit.hh,
  	       vtoc_get_cyl_from_cchh(&f1->DS1EXT2.llimit),
  	       vtoc_get_head_from_cchh(&f1->DS1EXT2.llimit));
  	printf("              ulimit     : hex %04x%04x "	\
  	       "(cyl %d, trk %d)\n",
  	       f1->DS1EXT2.ulimit.cc,
  	       f1->DS1EXT2.ulimit.hh,
  	       vtoc_get_cyl_from_cchh(&f1->DS1EXT2.ulimit),
  	       vtoc_get_head_from_cchh(&f1->DS1EXT2.ulimit));
  	printf("DS1EXT3     : hex %02x%02x%04x%04x%04x%04x\n",
  	       f1->DS1EXT3.typeind,
  	       f1->DS1EXT3.seqno,
  	       f1->DS1EXT3.llimit.cc,
  	       f1->DS1EXT3.llimit.hh,
  	       f1->DS1EXT3.ulimit.cc,
  	       f1->DS1EXT3.ulimit.hh);
  	printf("              typeind    : dec %d, hex %02x\n",
  	       f1->DS1EXT3.typeind,
  	       f1->DS1EXT3.typeind);
  	printf("              seqno      : dec %d, hex %02x\n",
  	       f1->DS1EXT3.seqno, f1->DS1EXT3.seqno);
  	printf("              llimit     : hex %04x%04x "	\
  	       "(cyl %d, trk %d)\n",
  	       f1->DS1EXT3.llimit.cc,
  	       f1->DS1EXT3.llimit.hh,
  	       vtoc_get_cyl_from_cchh(&f1->DS1EXT3.llimit),
  	       vtoc_get_head_from_cchh(&f1->DS1EXT3.llimit));
  	printf("              ulimit     : hex %04x%04x "	\
  	       "(cyl %d, trk %d)\n",
  	       f1->DS1EXT3.ulimit.cc,
  	       f1->DS1EXT3.ulimit.hh,
  	       vtoc_get_cyl_from_cchh(&f1->DS1EXT3.ulimit),
  	       vtoc_get_head_from_cchh(&f1->DS1EXT3.ulimit));
  	printf("DS1PTRDS    : %04x%04x%02x "		\
  	       "(cyl %d, trk %d, blk %d)\n",
  	       f1->DS1PTRDS.cc, f1->DS1PTRDS.hh,
  	       f1->DS1PTRDS.b,
  	       vtoc_get_cyl_from_cchhb(&f1->DS1PTRDS),
  	       vtoc_get_head_from_cchhb(&f1->DS1PTRDS),
  	       f1->DS1PTRDS.b);
  }
  
  static void dasdview_print_vtoc_f1_raw(format1_label_t *f1)
  {
  	printf("\n--- VTOC format 1 label -----------------------"
  	       "---------------------------------\n");
  	dasdview_print_format1_8_no_head(f1);
  }
  
  /* Note: A format 8 label uses the same type as format 1 */
  static void dasdview_print_vtoc_f8_raw(format1_label_t *f8)
  {
  	printf("\n--- VTOC format 8 label -----------------------"
  	       "---------------------------------\n");
  	dasdview_print_format1_8_no_head(f8);
  }
  
  static void dasdview_print_extent(extent_t *ext, char *name, int index)
  {
  	printf("%s[%d] : hex %02x%02x%04x%04x%04x%04x\n",
  	       name,
  	       index,
  	       ext->typeind,
  	       ext->seqno,
  	       ext->llimit.cc,
  	       ext->llimit.hh,
  	       ext->ulimit.cc,
  	       ext->ulimit.hh);
  	printf("              typeind    : dec %d, hex %02x\n",
  	       ext->typeind,
  	       ext->typeind);
  	printf("              seqno      : dec %d, hex %02x\n",
  	       ext->seqno, ext->seqno);
  	printf("              llimit     : hex %04x%04x "	\
  	       "(cyl %d, trk %d)\n",
  	       ext->llimit.cc,
  	       ext->llimit.hh,
  	       vtoc_get_cyl_from_cchh(&ext->llimit),
  	       vtoc_get_head_from_cchh(&ext->llimit));
  	printf("              ulimit     : hex %04x%04x "	\
  	       "(cyl %d, trk %d)\n",
  	       ext->ulimit.cc,
  	       ext->ulimit.hh,
  	       vtoc_get_cyl_from_cchh(&ext->ulimit),
  	       vtoc_get_head_from_cchh(&ext->ulimit));
  }
  
  static void dasdview_print_vtoc_f3_raw(format3_label_t *f3)
  {
  	int i;
  
  	printf("\n--- VTOC format 3 label ----------------------"
  	       "---------------------------------\n");
  
  	printf("DS3KEYID    : ");
  	for (i=0; i<4; i++)
  		printf("%02x", f3->DS3KEYID[i]);
  	printf("\n");
  
  	for (i = 0; i < 4; ++i)
  		dasdview_print_extent(&f3->DS3EXTNT[i], "DS3EXTNT", i);
  
  	printf("DS3FMTID    : dec %d, hex %02x\n",
  	       f3->DS3FMTID, f3->DS3FMTID);
  
  	for (i = 0; i < 9; ++i)
  		dasdview_print_extent(&f3->DS3ADEXT[i], "DS3ADEXT", i);
  
  	printf("DS3PTRDS    : %04x%04x%02x "		\
  	       "(cyl %d, trk %d, blk %d)\n",
  	       f3->DS3PTRDS.cc, f3->DS3PTRDS.hh,
  	       f3->DS3PTRDS.b,
  	       vtoc_get_cyl_from_cchhb(&f3->DS3PTRDS),
  	       vtoc_get_head_from_cchhb(&f3->DS3PTRDS),
  	       f3->DS3PTRDS.b);
  
  }
  
  static void dasdview_print_vtoc_f4_raw(format4_label_t *f4)
  {
          int i;
  
  	printf("\n--- VTOC format 4 label ----------------------"
  	       "---------------------------------\n");
  
  	printf("DS4KEYCD    : ");
  	for (i=0; i<44; i++) printf("%02x", f4->DS4KEYCD[i]);
  	printf("\nDS4IDFMT    : dec %d, hex %02x\n",
  	       f4->DS4IDFMT, f4->DS4IDFMT);
  	printf("DS4HPCHR    : %04x%04x%02x " \
  	       "(cyl %d, trk %d, blk %d)\n",
  	       f4->DS4HPCHR.cc, f4->DS4HPCHR.hh,
  	       f4->DS4HPCHR.b,
  	       vtoc_get_cyl_from_cchhb(&f4->DS4HPCHR),
  	       vtoc_get_head_from_cchhb(&f4->DS4HPCHR),
  	       f4->DS4HPCHR.b);
  	printf("DS4DSREC    : dec %d, hex %04x\n",
  	       f4->DS4DSREC, f4->DS4DSREC);
  	printf("DS4HCCHH    : %04x%04x (cyl %d, trk %d)\n",
  	       f4->DS4HCCHH.cc, f4->DS4HCCHH.hh,
  	       vtoc_get_cyl_from_cchh(&f4->DS4HCCHH),
  	       vtoc_get_head_from_cchh(&f4->DS4HCCHH));
  	printf("DS4NOATK    : dec %d, hex %04x\n",
  	       f4->DS4NOATK, f4->DS4NOATK);
  	printf("DS4VTOCI    : dec %d, hex %02x\n",
  	       f4->DS4VTOCI, f4->DS4VTOCI);
  	printf("DS4NOEXT    : dec %d, hex %02x\n",
  	       f4->DS4NOEXT, f4->DS4NOEXT);
  	printf("DS4SMSFG    : dec %d, hex %02x\n",
  	       f4->DS4SMSFG, f4->DS4SMSFG);
  	printf("DS4DEVAC    : dec %d, hex %02x\n",
  	       f4->DS4DEVAC, f4->DS4DEVAC);
  	printf("DS4DSCYL    : dec %d, hex %04x\n",
  	       f4->DS4DEVCT.DS4DSCYL, f4->DS4DEVCT.DS4DSCYL);
  	printf("DS4DSTRK    : dec %d, hex %04x\n",
  	       f4->DS4DEVCT.DS4DSTRK, f4->DS4DEVCT.DS4DSTRK);
  	printf("DS4DEVTK    : dec %d, hex %04x\n",
  	       f4->DS4DEVCT.DS4DEVTK, f4->DS4DEVCT.DS4DEVTK);
  	printf("DS4DEVI     : dec %d, hex %02x\n",
  	       f4->DS4DEVCT.DS4DEVI, f4->DS4DEVCT.DS4DEVI);
  	printf("DS4DEVL     : dec %d, hex %02x\n",
  	       f4->DS4DEVCT.DS4DEVL, f4->DS4DEVCT.DS4DEVL);
  	printf("DS4DEVK     : dec %d, hex %02x\n",
  	       f4->DS4DEVCT.DS4DEVK, f4->DS4DEVCT.DS4DEVK);
  	printf("DS4DEVFG    : dec %d, hex %02x\n",
  	       f4->DS4DEVCT.DS4DEVFG, f4->DS4DEVCT.DS4DEVFG);
  	printf("DS4DEVTL    : dec %d, hex %04x\n",
  	       f4->DS4DEVCT.DS4DEVTL, f4->DS4DEVCT.DS4DEVTL);
  	printf("DS4DEVDT    : dec %d, hex %02x\n",
  	       f4->DS4DEVCT.DS4DEVDT, f4->DS4DEVCT.DS4DEVDT);
  	printf("DS4DEVDB    : dec %d, hex %02x\n",
  	       f4->DS4DEVCT.DS4DEVDB, f4->DS4DEVCT.DS4DEVDB);
  	printf("DS4AMTIM    : hex ");
  	for (i=0; i<8; i++) printf("%02x", f4->DS4AMTIM[i]);
  	printf("\nDS4AMCAT    : hex ");
  	for (i=0; i<3; i++) printf("%02x", f4->DS4AMCAT[i]);
  	printf("\nDS4R2TIM    : hex ");
  	for (i=0; i<8; i++) printf("%02x", f4->DS4R2TIM[i]);
  	printf("\nres1        : hex ");
  	for (i=0; i<5; i++) printf("%02x", f4->res1[i]);
  	printf("\nDS4F6PTR    : hex ");
  	for (i=0; i<5; i++) printf("%02x", f4->DS4F6PTR[i]);
  	printf("\nDS4VTOCE    : hex %02x%02x%04x%04x%04x%04x\n",
  	       f4->DS4VTOCE.typeind, f4->DS4VTOCE.seqno,
  	       f4->DS4VTOCE.llimit.cc, f4->DS4VTOCE.llimit.hh,
  	       f4->DS4VTOCE.ulimit.cc, f4->DS4VTOCE.ulimit.hh);
  	printf("              typeind    : dec %d, hex %02x\n",
  	       f4->DS4VTOCE.typeind, f4->DS4VTOCE.typeind);
  	printf("              seqno      : dec %d, hex %02x\n",
  	       f4->DS4VTOCE.seqno, f4->DS4VTOCE.seqno);
  	printf("              llimit     : hex %04x%04x (cyl %d, trk %d)\n",
  	       f4->DS4VTOCE.llimit.cc, f4->DS4VTOCE.llimit.hh,
  	       vtoc_get_cyl_from_cchh(&f4->DS4VTOCE.llimit),
  	       vtoc_get_head_from_cchh(&f4->DS4VTOCE.llimit));
  	printf("              ulimit     : hex %04x%04x (cyl %d, trk %d)\n",
  	       f4->DS4VTOCE.ulimit.cc, f4->DS4VTOCE.ulimit.hh,
  	       vtoc_get_cyl_from_cchh(&f4->DS4VTOCE.ulimit),
  	       vtoc_get_head_from_cchh(&f4->DS4VTOCE.ulimit));
  	printf("res2        : hex ");
  	for (i=0; i<10; i++) printf("%02x", f4->res2[i]);
  	printf("\nDS4EFLVL    : dec %d, hex %02x\n",
  	       f4->DS4EFLVL, f4->DS4EFLVL);
  	printf("DS4EFPTR    : hex %04x%04x%02x " \
  	       "(cyl %d, trk %d, blk %d)\n",
  	       f4->DS4EFPTR.cc, f4->DS4EFPTR.hh,
  	       f4->DS4EFPTR.b,
  	       vtoc_get_cyl_from_cchhb(&f4->DS4EFPTR),
  	       vtoc_get_head_from_cchhb(&f4->DS4EFPTR),
  	       f4->DS4EFPTR.b);
  	printf("res3        : hex %02x\n", f4->res3);
  	printf("DS4DCYL     : dec %d, hex %08x\n",
  	       f4->DS4DCYL, f4->DS4DCYL);
  	printf("res4        : hex ");
  	for (i=0; i<2; i++) printf("%02x", f4->res4[i]);
  	printf("\nDS4DEVF2    : dec %d, hex %02x\n",
  	       f4->DS4DEVF2, f4->DS4DEVF2);
  	printf("res5        : hex %02x\n", f4->res5);
  }
  
  static void dasdview_print_vtoc_f5_raw(format5_label_t *f5)
  {
          int i;
  
  	printf("\n--- VTOC format 5 label ----------------------" \
  	       "---------------------------------\n");
  
  	printf("key identifier\n        DS5KEYID    : ");
  	for (i=0; i<4; i++) printf("%02x", f5->DS5KEYID[i]);
  	printf("\nfirst extent description\n");
  	printf("        DS5AVEXT    : %04x%04x%02x " \
  	       "(start trk: %d, length: %d cyl, %d trk)\n",
  	       f5->DS5AVEXT.t,  f5->DS5AVEXT.fc,
  	       f5->DS5AVEXT.ft, f5->DS5AVEXT.t,
  	       f5->DS5AVEXT.fc, f5->DS5AVEXT.ft);
  	printf("next 7 extent descriptions\n");
  	for (i=0; i<7; i++)
          {
  	        printf("        DS5EXTAV[%d] : %04x%04x%02x " \
  		       "(start trk: %d, length: %d cyl, %d trk)\n", i+2,
  		       f5->DS5EXTAV[i].t,  f5->DS5EXTAV[i].fc,
  		       f5->DS5EXTAV[i].ft, f5->DS5EXTAV[i].t,
  		       f5->DS5EXTAV[i].fc, f5->DS5EXTAV[i].ft);
  	}
  	printf("format identifier\n" \
  	       "        DS5FMTID    : dec %d, hex %02x\n",
  	       f5->DS5FMTID, f5->DS5FMTID);
  	printf("next 18 extent descriptions\n");
  	for (i=0; i<18; i++)
          {
  	        printf("        DS5MAVET[%d] : %04x%04x%02x " \
  		       "(start trk: %d, length: %d cyl, %d trk)\n", i+9,
  		       f5->DS5MAVET[i].t,  f5->DS5MAVET[i].fc,
  		       f5->DS5MAVET[i].ft, f5->DS5MAVET[i].t,
  		       f5->DS5MAVET[i].fc, f5->DS5MAVET[i].ft);
  	}
  	printf("pointer to next format 5 label\n" \
  	       "        DS5PTRDS    : %04x%04x%02x " \
  	       "(cyl %d, trk %d, blk %d)\n",
  	       f5->DS5PTRDS.cc, f5->DS5PTRDS.hh,
  	       f5->DS5PTRDS.b,
  	       vtoc_get_cyl_from_cchhb(&f5->DS5PTRDS),
  	       vtoc_get_head_from_cchhb(&f5->DS5PTRDS),
  	       f5->DS5PTRDS.b);
  }
  
  static void dasdview_print_vtoc_f7_raw(format7_label_t *f7)
  {
          int i;
  
  	printf("\n--- VTOC format 7 label ----------------------" \
  	       "---------------------------------\n");
  
  	printf("key identifier\n        DS7KEYID    : ");
  	for (i=0; i<4; i++) printf("%02x", f7->DS7KEYID[i]);
  	printf("\nfirst 5 extent descriptions\n");
  	for (i=0; i<5; i++)
  	{
  	        printf("        DS7EXTNT[%d] : %08x %08x " \
  		       "(start trk %d, end trk %d)\n", i+1,
  		       f7->DS7EXTNT[i].a, f7->DS7EXTNT[i].b,
  		       f7->DS7EXTNT[i].a, f7->DS7EXTNT[i].b);
  	}
  	printf("format identifier\n" \
  	       "        DS7FMTID    : dec %d, hex %02x\n",
  	       f7->DS7FMTID, f7->DS7FMTID);
  	printf("next 11 extent descriptions\n");
  	for (i=0; i<11; i++)
  	{
  	        printf("        DS7ADEXT[%d] : %08x %08x " \
  		       "(start trk %d, end trk %d)\n", i+6,
  		       f7->DS7ADEXT[i].a, f7->DS7ADEXT[i].b,
  		       f7->DS7ADEXT[i].a, f7->DS7ADEXT[i].b);
  	}
  	printf("reserved field\n        res1        : ");
  	for (i=0; i<2; i++) printf("%02x", f7->res1[i]);
  	printf("\npointer to next format 7 label\n" \
  	       "        DS7PTRDS    : %04x%04x%02x " \
  	       "(cyl %d, trk %d, blk %d)\n",
  	       f7->DS7PTRDS.cc, f7->DS7PTRDS.hh,
  	       f7->DS7PTRDS.b,
  	       vtoc_get_cyl_from_cchhb(&f7->DS7PTRDS),
  	       vtoc_get_head_from_cchhb(&f7->DS7PTRDS),
  	       f7->DS7PTRDS.b);
  }
  
  static void dasdview_print_vtoc_f9_nohead(format9_label_t *f9)
  {
  	unsigned int i;
  
  	printf("DS9KEYID    : dec %d, hex %02x\n",
  	       f9->DS9KEYID, f9->DS9KEYID);
  	printf("DS9SUBTY    : dec %d, hex %02x\n",
  	       f9->DS9SUBTY, f9->DS9SUBTY);
  	printf("DS9NUMF9    : dec %d, hex %02x\n",
  	       f9->DS9NUMF9, f9->DS9NUMF9);
  
  	printf("res1        : hex ");
  	for (i=0; i < sizeof(f9->res1); i++) {
  		if ((i > 0) && (i % 16 == 0))
  			printf("\n                  ");
  		printf("%02x", f9->res1[i]);
  		if ((i+9) % 16 == 0)
  			printf(" ");
  	}
  	printf("\n");
  
  	printf("DS9FMTID    : dec %d, hex %02x\n",
  	       f9->DS9FMTID, f9->DS9FMTID);
  
  	printf("res2        : hex ");
  	for (i=0; i < sizeof(f9->res2); i++) {
  		if ((i > 0) && (i % 16 == 0))
  			printf("\n                  ");
  		printf("%02x", f9->res2[i]);
  		if ((i+9) % 16 == 0)
  			printf(" ");
  	}
  	printf("\n");
  	printf("pointer to next format 9 label\n"
  	       "        DS9PTRDS    : %04x%04x%02x "
  	       "(cyl %d, trk %d, blk %d)\n",
  	       f9->DS9PTRDS.cc, f9->DS9PTRDS.hh,
  	       f9->DS9PTRDS.b,
  	       vtoc_get_cyl_from_cchhb(&f9->DS9PTRDS),
  	       vtoc_get_head_from_cchhb(&f9->DS9PTRDS),
  	       f9->DS9PTRDS.b);
  }
  
  static void dasdview_print_vtoc_f9_raw(format9_label_t *f9)
  {
  	printf("\n--- VTOC format 9 label ----------------------" \
  	       "---------------------------------\n");
  	dasdview_print_vtoc_f9_nohead(f9);
  }
  
  static void dasdview_print_vtoc_dscb(dasdview_info_t *info, void *dscb)
  {
  	format1_label_t *tmp = dscb;
  
  	switch (tmp->DS1FMTID) {
          case 0x00:
  		break;
          case 0xf1:
  		if (info->vtoc_f1 || info->vtoc_all)
  			dasdview_print_vtoc_f1_raw(dscb);
  		break;
          case 0xf3:
  		if (info->vtoc_f3 || info->vtoc_all)
  			dasdview_print_vtoc_f3_raw(dscb);
  		break;
          case 0xf4:
  		if (info->vtoc_f4 || info->vtoc_all)
  			dasdview_print_vtoc_f4_raw(dscb);
  		break;
          case 0xf5:
  		if (info->vtoc_f5 || info->vtoc_all)
  			dasdview_print_vtoc_f5_raw(dscb);
  		break;
          case 0xf7:
  		if (info->vtoc_f7 || info->vtoc_all)
  			dasdview_print_vtoc_f7_raw(dscb);
  		break;
          case 0xf8:
  		if (info->vtoc_f8 || info->vtoc_all)
  			dasdview_print_vtoc_f8_raw(dscb);
  		break;
          case 0xf9:
  		if (info->vtoc_f9 || info->vtoc_all)
  			dasdview_print_vtoc_f9_raw(dscb);
  		break;
          default:
  		printf("unrecognized DSCB of type: %x \n\n", tmp->DS1FMTID);
                  break;
          }
  }
  
  static void dasdview_print_vtoc_f1(dasdview_info_t *info)
  {
  	int j;
  
  	printf("--- VTOC format 1 labels ----------------------"
  	       "---------------------------------\n");
  
  	if (info->f1c < 1)
  	{
  	        printf("This VTOC doesn't contain a format 1 label.\n");
  		return;
  	}
  
  	for (j=0; j<info->f1c; j++) {
  		printf("\n--- format 1 DSCB number %d ---\n", j+1);
  		dasdview_print_format1_8_no_head(&info->f1[j]);
  	}
  }
  
  static void dasdview_print_vtoc_f8(dasdview_info_t *info)
  {
  	int j;
  
  	printf("--- VTOC format 8 labels ----------------------"
  	       "---------------------------------\n");
  
  	if (info->f8c < 1)
  	{
  		printf("This VTOC doesn't contain a format 8 label.\n");
  		return;
  	}
  
  	for (j=0; j<info->f8c; j++) {
  		printf("\n--- format 8 DSCB number %d ---\n", j+1);
  		dasdview_print_format1_8_no_head(&info->f8[j]);
  	}
  }
  
  static void dasdview_print_vtoc_f4(dasdview_info_t *info)
  {
  	if (info->f4c < 1) {
  		printf("\n--- VTOC format 4 label ----------------------" \
  		       "---------------------------------\n");
  	        printf("This VTOC doesn't contain a format 4 label.\n");
  		return;
  	}
  	dasdview_print_vtoc_f4_raw(&info->f4);
  }
  
  static void dasdview_print_vtoc_f5(dasdview_info_t *info)
  {
  	if (info->f5c < 1)
  	{
  		printf("\n--- VTOC format 5 label ----------------------" \
  		       "---------------------------------\n");
  	        printf("This VTOC doesn't contain a format 5 label.\n");
  		return;
  	}
  	dasdview_print_vtoc_f5_raw(&info->f5);
  }
  
  static void dasdview_print_vtoc_f7(dasdview_info_t *info)
  {
  	if (info->f7c < 1)
  	{
  		printf("\n--- VTOC format 7 label ----------------------"
  		       "---------------------------------\n");
  	        printf("This VTOC doesn't contain a format 7 label.\n");
  		return;
  	}
  	dasdview_print_vtoc_f7_raw(&info->f7);
  }
  
  static void dasdview_print_vtoc_f9(dasdview_info_t *info)
  {
  	int j;
  
  	printf("\n--- VTOC format 9 label ----------------------"
  	       "---------------------------------\n");
  	if (info->f9c < 1) {
  	        printf("This VTOC doesn't contain a format 9 label.\n");
  		return;
  	}
  	for (j = 0; j < info->f9c; j++) {
  		printf("\n--- format 9 DSCB number %d ---\n", j+1);
  		dasdview_print_vtoc_f9_nohead(&info->f9[j]);
  	}
  }
  
  static void dasdview_print_vtoc_f3(void)
  {
  	/* dasdfmt formatted DASD devices have no format3 labels, but since the
  	 *  option exists for raw DASDs, we need to have some sensible message
  	 */
  	printf("\n--- VTOC format 3 label ----------------------"	\
  	       "---------------------------------\n");
  	printf("This VTOC doesn't contain a format 3 label.\n");
  	return;
  }
  
  static void dasdview_print_vtoc_standard(dasdview_info_t *info)
  {
  	dasdview_read_vtoc(info);
  
  	if (info->vtoc_info || info->vtoc_all)
  		dasdview_print_vtoc_info(info);
  
  	if (info->vtoc_f4 || info->vtoc_all)
  	        dasdview_print_vtoc_f4(info);
  
  	if (info->vtoc_f5 || info->vtoc_all)
  	        dasdview_print_vtoc_f5(info);
  
  	if (info->vtoc_f7 || info->vtoc_all)
  	        dasdview_print_vtoc_f7(info);
  
  	if (info->vtoc_f1 || info->vtoc_all)
  	        dasdview_print_vtoc_f1(info);
  
  	if (info->vtoc_f8 || info->vtoc_all)
  		dasdview_print_vtoc_f8(info);
  
  	if (info->vtoc_f9 || info->vtoc_all)
  		dasdview_print_vtoc_f9(info);
  
  	if (info->vtoc_f3 || info->vtoc_all)
  		dasdview_print_vtoc_f3();
  }
  
  /* a simple routine to print all records in the vtoc */
  static void dasdview_print_vtoc_raw(dasdview_info_t *info)
  {
  	struct dscbiterator *it;
  	struct dscb *record;
  	int rc;
  
  	rc = lzds_dasd_read_vlabel(info->dasd);
  	if (rc) {
  		zt_error_print("error when reading label from device:"
  			       " rc=%d\n", rc);
  		exit(-1);
  	}
  	rc = lzds_dasd_read_rawvtoc(info->dasd);
  	if (rc == EINVAL) {
  		zt_error_print("dasdview: Cannot read VTOC because disk does"
  			       " not contain valid VOL1 label.\n",
  			       info->device);
  		exit(-1);
  	} else if (rc) {
  		zt_error_print("error when reading vtoc from device:"
  			       " rc=%d\n", rc);
  		exit(-1);
  	}
  	rc = lzds_dasd_get_rawvtoc(info->dasd, &info->rawvtoc);
  	if (rc || !info->rawvtoc) {
  		zt_error_print("dasdview: libvtoc could not read vtoc\n");
  		exit(-1);
  	}
  
  	if (info->vtoc_info || info->vtoc_all)
  		dasdview_print_vtoc_info_raw(info);
  
  	rc = lzds_raw_vtoc_alloc_dscbiterator(info->rawvtoc, &it);
  	if (rc) {
  		zt_error_print("dasdview: could not allocate DSCB iterator\n");
  		exit(-1);
  	}
  	while (!lzds_dscbiterator_get_next_dscb(it, &record))
  		dasdview_print_vtoc_dscb(info, record);
  	lzds_dscbiterator_free(it);
  }
  
  static void dasdview_print_vtoc(dasdview_info_t *info)
  {
  	if (info->raw_track_access)
  		dasdview_print_vtoc_raw(info);
  	else
  		dasdview_print_vtoc_standard(info);
  }
  
  static int
  dasdview_print_format1(unsigned int size, unsigned char *dumpstr)
  {
  	unsigned int i;
  	char asc[17], ebc[17];
  
  	for (i = 0; i < size; i++)
  	{
  		if ((i / 16) * 16 == i) {
  			printf("\n|  ");
  			strncpy(asc, (char *)dumpstr + i, 16);
  			strncpy(ebc, (char *)dumpstr + i, 16);
  			asc[16] = '\0';
  			ebc[16] = '\0';
  		}
  		printf("%02X", dumpstr[i]);
  		if (((i + 1) / 4)  * 4  == i + 1) printf(" ");
  		if (((i + 1) / 8)  * 8  == i + 1) printf(" ");
  		if (((i + 1) / 16) * 16 == i + 1) {
  			vtoc_ebcdic_dec(asc, asc, 16);
  			dot(asc);
  			dot(ebc);
  			printf("| %16.16s | %16.16s |", asc, ebc);
  		}
  	}
  
  	return 0;
  }
  
  static int
  dasdview_print_format2(unsigned int size, unsigned char *dumpstr,
  		       unsigned long long begin)
  {
  	unsigned int i;
  	char asc[17], ebc[17];
  
  	for (i = 0; i < size; i++)
  	{
  		if ((i / 8) * 8 == i) {
  			printf("\n | %13llu | %13llX |  ",
  			       begin + (unsigned long long) i,
  			       begin + (unsigned long long) i);
  
  			strncpy(asc, (char *)dumpstr + i, 8);
  			strncpy(ebc, (char *)dumpstr + i, 8);
  		}
  		printf("%02X", dumpstr[i]);
  		if (((i + 1) / 4) * 4 == i + 1) printf("  ");
  		if (((i + 1) / 8) * 8 == i + 1) {
  			vtoc_ebcdic_dec(asc, asc, 8);
  			dot(asc);
  			dot(ebc);
  			printf("| %8.8s | %8.8s |", asc, ebc);
  		}
  	}
  
  	return 0;
  }
  
  static void dasdview_view_standard(dasdview_info_t *info)
  {
  	unsigned char  dumpstr[DUMP_STRING_SIZE];
  	unsigned long long i=0, j=0, k=0, count=0;
  	int   fd, rc;
  
  	unsigned long long a=0;
  	int b=0;
  
  	k = ((info->size) % 16LL);
  
  	if (k != 0)
  	{
  		info->size += (16LL - k);
  	}
  
  	fd = open(info->device, O_RDONLY);
  	if (fd == -1)
  	{
  		zt_error_print("dasdview: open error\n" \
  			"Unable to open device %s in read-only" \
  			"mode!\n", info->device);
  		exit(-1);
  	}
  
  	j = (info->begin / SEEK_STEP);
  	k = (info->begin % SEEK_STEP);
  
          /* seek in SEEK_STEP steps */
  	for (i=1; i <= j; i++)
  	{
  		rc = lseek64(fd, SEEK_STEP, SEEK_CUR);
  		if (rc == -1)
  		{
  			printf("*** rc: %d (%d) ***\n", rc, errno);
  			printf("*** j: %llu ***\n", j);
  			printf("*** k: %llu ***\n", k);
  			printf("*** a: %llu ***\n", a);
  			printf("*** b: %d ***\n", b);
  			close(fd);
  			zt_error_print("dasdview: seek error\n" \
  				"Unable to seek in device %s!\n",
  				info->device);
  			exit(-1);
  		}
  		b++;
  		a += SEEK_STEP;
  	}
  
  	if (k > 0)
  	{
  		rc = lseek(fd, k, SEEK_CUR);
  		if (rc == -1)
  		{
  			close(fd);
  			zt_error_print("dasdview: seek error\n" \
  				"Unable to seek in device %s!\n",
  				info->device);
  			exit(-1);
  		}
  	}
  
  	j = info->size / DUMP_STRING_SIZE;
  	k = info->size % DUMP_STRING_SIZE;
  
  
  	if (info->format1)
  	{
  		printf("+----------------------------------------+" \
  		       "------------------+------------------+\n");
  		printf("| HEXADECIMAL                            |" \
  		       " EBCDIC           | ASCII            |\n");
  		printf("|  01....04 05....08  09....12 13....16  |" \
  		       " 1.............16 | 1.............16 |\n");
  		printf("+----------------------------------------+" \
  		       "------------------+------------------+");
  	}
  	else if (info->format2)
  	{
  		printf(" +---------------+---------------+----------------" \
  		       "------+----------+----------+\n");
  		printf(" |     BYTE      |     BYTE      |     HEXADECIMAL" \
  		       "      |  EBCDIC  |  ASCII   |\n");
  		printf(" |    DECIMAL    |  HEXADECIMAL  |  1 2 3 4   5 6 " \
  		       "7 8   | 12345678 | 12345678 |\n");
  		printf(" +---------------+---------------+----------------" \
  		       "------+----------+----------+");
  	}
  
  	count = info->begin;
  	for (i=1; i <= j; i++)
  	{
  		bzero(dumpstr, DUMP_STRING_SIZE);
  		rc = read(fd, &dumpstr, DUMP_STRING_SIZE);
  		if (rc != DUMP_STRING_SIZE)
  		{
  			close(fd);
  			zt_error_print("dasdview: read error\n" \
  				"Unable to read from device %s!\n",
  				info->device);
  			exit(-1);
  		}
  
  		if (info->format1)
  			dasdview_print_format1(DUMP_STRING_SIZE, dumpstr);
  		else if (info->format2)
  			dasdview_print_format2(DUMP_STRING_SIZE, dumpstr,
  					       count);
  		count += DUMP_STRING_SIZE;
  	}
  
  	if (k > 0)
  	{
  		bzero(dumpstr, DUMP_STRING_SIZE);
  		rc = read(fd, &dumpstr, k);
  		if (rc != (int) k)
  		{
  			close(fd);
  			zt_error_print("dasdview: read error\n" \
  				"Unable to read from device %s!\n",
  				info->device);
  			exit(-1);
  		}
  
  		if (info->format1)
  			dasdview_print_format1((unsigned int) k, dumpstr);
  		else if (info->format2)
  			dasdview_print_format2((unsigned int) k, dumpstr,
  					       count);
  	}
  
  	close(fd);
  
  	if (info->format1)
  	{
  		printf("\n+----------------------------------------+" \
  		       "------------------+------------------+\n\n");
  	}
  	else if (info->format2)
  	{
  		printf("\n +---------------+---------------+----------------" \
  		       "------+----------+----------+\n\n");
  	}
  
  }
  
  static void dasdview_print_format_raw(unsigned int size, char *dumpstr)
  {
  	unsigned int i;
  	char asc[17], ebc[17];
  	unsigned int residual, count;
  	char *data;
  
  	data = dumpstr;
  	residual = size;
  	while (residual) {
  		/* we handle at most 16 bytes per line */
  		count = MIN(residual, 16u);
  		bzero(asc, 17);
  		bzero(ebc, 17);
  		printf("|");
  		memcpy(asc, data, count);
  		memcpy(ebc, data, count);
  
  		for (i = 0; i < 16; ++i) {
  			if ((i % 4) == 0)
  				printf(" ");
  			if ((i % 8) == 0)
  				printf(" ");
  			if (i < count)
  				printf("%02X", data[i]);
  			else
  				printf("  ");
  		}
  		vtoc_ebcdic_dec(asc, asc, count);
  		dot(asc);
  		dot(ebc);
  		printf("  | %16.16s | %16.16s |\n", asc, ebc);
  		data += count;
  		residual -= count;
  	}
  }
  
  /* gets the pointer to an eckd record structure in memory and
   * prints a hex/ascii/ebcdic dump for it
   */
  static void dasdview_print_raw_record(char *rec)
  {
  
  	struct eckd_count *ecount;
  	unsigned int cyl, head;
  
  	ecount = (struct eckd_count *)rec;
  	/* Note: the first 5 bytes of the count area are the
  	 * record ID and by convention these bytes are interpreted
  	 * as CCHHR (or ccccCCChR for large volumes)
  	 */
  	cyl = vtoc_get_cyl_from_cchhb(&ecount->recid);
  	head = vtoc_get_head_from_cchhb(&ecount->recid);
  	printf("+-----------------------------------------"
  	       "-------------------------------------+\n");
  	printf("| count area:                                    "
  	       "                              |\n");
  	printf("|          hex: %016llX                          "
  	       "                     |\n",
  	       *((unsigned long long *)ecount));
  	printf("|     cylinder:        %9d                 "
  	       "                              |\n", cyl);
  	printf("|         head:        %9d                 "
  	       "                              |\n", head);
  	printf("|       record:        %9d                 "
  	       "                              |\n", ecount->recid.b);
  	printf("|   key length:        %9d                 "
  	       "                              |\n", ecount->kl);
  	printf("|  data length:        %9d                 "
  	       "                              |\n", ecount->dl);
  	printf("+-----------------------------------------"
  	       "-------------------------------------+\n");
  	printf("| key area:                               "
  	       "                                     |\n");
  	printf("| HEXADECIMAL                            |"
  	       " EBCDIC           | ASCII            |\n");
  	printf("|  01....04 05....08  09....12 13....16  |"
  	       " 1.............16 | 1.............16 |\n");
  	printf("+----------------------------------------+"
  	       "------------------+------------------+\n");
  	dasdview_print_format_raw(ecount->kl, rec + sizeof(*ecount));
  	printf("+----------------------------------------+"
  	       "------------------+------------------+\n");
  	printf("| data area:                              "
  	       "                                     |\n");
  	printf("| HEXADECIMAL                            |"
  	       " EBCDIC           | ASCII            |\n");
  	printf("|  01....04 05....08  09....12 13....16  |"
  	       " 1.............16 | 1.............16 |\n");
  	printf("+----------------------------------------+"
  	       "------------------+------------------+\n");
  	dasdview_print_format_raw(ecount->dl,
  				  rec + sizeof(*ecount) + ecount->kl);
  	printf("+----------------------------------------+"
  	       "------------------+------------------+\n");
  }
  
  static void dasdview_print_raw_track(char *trackdata,
  				     unsigned int cyl,
  				     unsigned int head)
  {
  	struct eckd_count *ecount;
  	char *data;
  	u_int32_t record;
  
  	record = 0;
  	data = trackdata;
  
  	do {
  		printf("cylinder %u, head %u, record %u\n",
  		       cyl, head, record);
  		dasdview_print_raw_record(data);
  		printf("\n");
  
  		ecount = (struct eckd_count *)data;
  		data += sizeof(*ecount) + ecount->kl + ecount->dl;
  		++record;
  
  		if ((*(unsigned long long *)data) == ENDTOKEN) {
  			break;
  		}
  		if ((unsigned long)data >=
  		    (unsigned long)trackdata + RAWTRACKSIZE) {
  			break;
  		}
  	} while (1);
  }
  
  static void dasdview_view_raw(dasdview_info_t *info)
  {
  	u_int64_t residual, trckstart, trckend, track, trckbuffsize;
  	u_int64_t tracks_to_read, trckcount, i;
  	char *trackdata;
  	char *data;
  	int rc;
  	struct dasdhandle *dasdh;
  
  	trckstart = info->begin / RAWTRACKSIZE;
  	tracks_to_read = info->size / RAWTRACKSIZE;
  
  	/* TODO: how large should we make our buffer?
  	 * The DASD device driver cannot read more than 16 tracks at once
  	 * but we can read a larger blob and the block layer will split up
  	 * the requests for us.
  	 */
  	trckbuffsize = MIN(tracks_to_read, 16u);
  	/* track data must be page aligned for O_DIRECT */
  	trackdata = memalign(4096, trckbuffsize * RAWTRACKSIZE);
  	if (!trackdata) {
  		zt_error_print("failed to allocate memory\n");
  		exit(-1);
  	}
  	rc = lzds_dasd_alloc_dasdhandle(info->dasd, &dasdh);
  	if (rc) {
  		zt_error_print("failed to allocate memory\n");
  		exit(-1);
  	}
  	rc = lzds_dasdhandle_open(dasdh);
  	if (rc) {
  		lzds_dasdhandle_free(dasdh);
  		zt_error_print("failed to open device\n");
  		exit(-1);
  	}
  	/* residual is the number of tracks we still have to read */
  	residual = tracks_to_read;
  	track = trckstart;
  	while (residual) {
  		trckcount = MIN(trckbuffsize, residual);
  		trckend = track + trckcount - 1;
  		rc = lzds_dasdhandle_read_tracks_to_buffer(dasdh, track,
  							   trckend, trackdata);
  		if (rc) {
  			perror("Error on read");
  			exit(-1);
  		}
  		data = trackdata;
  		for (i = 0; i < trckcount; ++i) {
  			dasdview_print_raw_track(data, track / info->geo.heads,
  						track % info->geo.heads);
  			data += RAWTRACKSIZE;
  			++track;
  		}
  		residual -= trckcount;
  	}
  
  	free(trackdata);
  
  	rc = lzds_dasdhandle_close(dasdh);
  	lzds_dasdhandle_free(dasdh);
  	if (rc < 0) {
  		perror("Error on closing file");
  		exit(-1);
  	}
  }
  
  static void dasdview_view(dasdview_info_t *info)
  {
  	if (info->raw_track_access)
  		dasdview_view_raw(info);
  	else
  		dasdview_view_standard(info);
  }
  
  static void
  dasdview_print_characteristic(dasdview_info_t *info)
  {
  	dasd_information2_t dasd_info;
  	dasd_info = info->dasd_info;
  	printf("encrypted disk         : %s\n",
  	       (dasd_info.characteristics[46]&0x80)?"yes":"no");
  	printf("solid state device     : %s\n",
  	       (dasd_info.characteristics[46]&0x40)?"yes":"no");
  }
  
  int main(int argc, char * argv[]) {
  
  	dasdview_info_t info;
  	int oc;
  	unsigned long long max=0LL;
  	char *begin_param_str = NULL;
  	char *size_param_str  = NULL;
  	int rc;
  
  	util_prg_init(&prg);
  	util_opt_init(opt_vec, NULL);
  
  	bzero (&info, sizeof(info));
  	while (1)
  	{
  		oc = util_opt_getopt_long(argc, argv);
  
  		switch (oc)
  		{
  		case 'h':
  			util_prg_print_help();
  			util_opt_print_help();
  			exit(EXIT_SUCCESS);
  		case 'v':
  			util_prg_print_version();
  			exit(EXIT_SUCCESS);
  		case 'b':
  			begin_param_str = optarg;
  			info.action_specified = 1;
  			info.begin_specified = 1;
  			break;
  		case 's':
  			size_param_str = optarg;
  			info.action_specified = 1;
  			info.size_specified = 1;
  			break;
  		case '1':
  			info.format1 = 1;
  			info.format2 = 0;
  			break;
  		case '2':
  			info.format1 = 0;
  			info.format2 = 1;
  			break;
  		case 'i':  /* print general DASD information and geometry */
  			info.action_specified = 1;
  			info.general_info = 1;
  			break;
  		case 'x':  /* print extended DASD information */
  			info.action_specified = 1;
  			info.extended_info = 1;
  			break;
  		case 'j':
  			info.action_specified = 1;
  			info.volser = 1;
  			break;
  		case 't':
  		        if (strcmp(optarg, "info")==0)
  		                info.vtoc_info = 1;
  		        else if (strcmp(optarg, "f1")==0)
  		                info.vtoc_f1 = 1;
  		        else if (strcmp(optarg, "f3")==0)
  		                info.vtoc_f3 = 1;
  		        else if (strcmp(optarg, "f4")==0)
  		                info.vtoc_f4 = 1;
  		        else if (strcmp(optarg, "f5")==0)
  		                info.vtoc_f5 = 1;
  		        else if (strcmp(optarg, "f7")==0)
  		                info.vtoc_f7 = 1;
  		        else if (strcmp(optarg, "f8")==0)
  				info.vtoc_f8 = 1;
  			else if (strcmp(optarg, "f9")==0)
  				info.vtoc_f9 = 1;
  			else if (strcmp(optarg, "all")==0)
  		                info.vtoc_all = 1;
  		        else {
  				zt_error_print("dasdview: usage error\n"
  					       "%s is no valid argument for"
  					       " option -t/--vtoc\n", optarg);
  				exit(-1);
  			}
  		        info.vtoc = 1;
  			info.action_specified = 1;
  			break;
  		case 'l':
  			info.action_specified = 1;
  			info.vlabel_info = 1;
  			break;
  		case 'c':
  			info.action_specified = 1;
  			info.characteristic_specified = 1;
  			break;
  		case -1:
  			/* End of options string - start of devices list */
  			info.device_id = optind;
  			break;
  		default:
  			fprintf(stderr, "Try 'dasdview --help' for more"
  					" information.\n");
  			exit(1);
  		}
          	if (oc==-1) break;
  	}
  
  	/* do some tests */
  	if (info.device_id >= argc) {
  		zt_error_print("dasdview: usage error\n"	\
  			       "No device specified!");
  		exit(EXIT_FAILURE);
  	}
  
  	if (info.device_id + 1 < argc) {
  		zt_error_print("dasdview: usage error\n"	\
  			       "More than one device specified!");
  		exit(EXIT_FAILURE);
  	}
  
  	if (info.device_id < argc)
  		strcpy(info.device, argv[info.device_id]);
  
  	dasdview_get_info(&info);
  	if (info.raw_track_access) {
  		rc = lzds_zdsroot_alloc(&info.zdsroot);
  		if (rc) {
  			zt_error_print("Could not allocate index\n");
  			exit(-1);
  		}
  		rc = lzds_zdsroot_add_device(info.zdsroot, info.device,
  					     &info.dasd);
  		if (rc) {
  			zt_error_print("Could not add device to index\n");
  			exit(-1);
  		}
  	}
  
  	if (info.begin_specified)
  		dasdview_parse_input(&info.begin, &info, begin_param_str);
  	else
  		info.begin = DEFAULT_BEGIN;
  
  	if (info.raw_track_access) {
  		max = (unsigned long long) info.hw_cylinders *
  			(unsigned long long) info.geo.heads * RAWTRACKSIZE;
  	} else
  		max = (unsigned long long) info.hw_cylinders *
  			(unsigned long long) info.geo.heads *
  			(unsigned long long) info.geo.sectors *
  			(unsigned long long) info.blksize;
  
  	if (info.begin > max)
  	{
  		zt_error_print("dasdview: usage error\n" \
  			"'begin' value is not within disk range!");
  		exit(-1);
  	}
  
  	if (info.size_specified)
  		dasdview_parse_input(&info.size, &info, size_param_str);
  	else if (info.raw_track_access)
  		info.size = RAWTRACKSIZE;
  	else
  		info.size = DEFAULT_SIZE;
  
  	if ((info.begin_specified || info.size_specified) &&
  	    ((info.begin + info.size) > max             )   )
  	{
  		zt_error_print("dasdview: usage error\n" \
  			"'begin' + 'size' is not within " \
  			"disk range!");
  		exit(-1);
  	}
  
  	if ((info.begin_specified || info.size_specified) &&
  	    (!info.format1 && !info.format2))
  	{
  		info.format1 = 1;
  	}
  
  	if ((info.format1 || info.format2) &&
  	    (!info.size_specified && !info.begin_specified))
  	{
  		zt_error_print("dasdview: usage error\n" \
  			"Options -1 or -2 make only sense with " \
  			"options -b or -s!");
  		exit(-1);
  	}
  
  	/* do the output */
  
  	if (info.begin_specified || info.size_specified)
  		dasdview_view(&info);
  
  	if (info.general_info || info.extended_info)
  		dasdview_print_general_info(&info);
  
  	if (info.extended_info)
  		dasdview_print_extended_info(&info);
  
  	if (info.volser)
  		dasdview_print_volser(&info);
  
  	if (info.vlabel_info)
  		dasdview_print_vlabel(&info);
  
  	if (info.vtoc)
  		dasdview_print_vtoc(&info);
  
  
  	if (!info.action_specified)
  	{
  		printf("No action specified.\n");
  	}
  
  	if (info.characteristic_specified)
  		dasdview_print_characteristic(&info);
  
  	return 0;
  }
  