/*static char *SCCSID = "@(#)io.c	6.3 91/07/24";*/
/*static char *SCCSID = "@(#)io.c	6.3 91/07/24";*/
/***	IO.C - Routines to deal with the OS
 *
 *	Format
 *	Michael Walma
 *	Copyright March 1988, Microsoft Corp.
 *	March 28 1988
 *
 *	Modification History:
 *	G.A. Jones	09/29/88	Removed STRINGS.H.
 */

#define INCL_DOSDEVICES

#include "ldefs.h"
#include "ltypes.h"
#include "globals.h"
#include <pr_fmt.h>

/***	fmt_open_lock_disk - get a handle to the disk
 *
 *	This routine opens the disk for reading and writing, with no
 *	buffering, and locks out all other users.
 *
 *	Currently, physical disk IOCTLs are used.  This is TEMPORARY,
 *	and will be replaced when DASD Open is supported by Pinball.
 *
 *	fmt_open_lock_disk (void)
 *
 *	ENTRY		No parameters
 *
 *	EXIT		Returns TRUE if successful, FALSE if failure
 *
 *	CALLS		DosOpen
 *			DosDevIOCTL
 *
 *	WARNINGS	None
 *
 *	EFFECTS		Fills in the disk handle
 *			Locks the disk
 *			Initializes the disk packet
 */
USHORT fmt_open_lock_disk (void)
{
  USHORT ret_code;
  USHORT action_taken;
  ULONG zero_data = 0L;
  USHORT i;
  UCHAR c;

#ifdef	TRACE
  fprintf (stderr, "fmt_open_lock_disk ()\n");
  fflush (stderr);
#endif

#ifdef FILE_VER
  ret_code = DosOpen (disk_name, (PHFILE)&disk_handle,
		      (PUSHORT)&action_taken, fmt_volsize * (ULONG)SECSIZE,
		      0, 0x12, 0x42, 0L);
  if (ret_code) {
#ifdef TRACE
    fprintf (stderr, "DosOpen failed: %d.\n", ret_code);
    fflush (stderr);
#endif
    return (FALSE);
  }

  rec_param.dev_bpb.BytePerSector = SECSIZE;
  rec_param.dev_bpb.TotalSectors = 0;
  rec_param.dev_bpb.SectorsPerTrack = 1;
  rec_param.dev_bpb.Heads = 1;
  rec_param.dev_bpb.HiddenSectors = 0;
  rec_param.dev_bpb.Ext_totalSectors = fmt_volsize;

  partition_offset = rec_param.dev_bpb.HiddenSectors;
  prm.spt = rec_param.dev_bpb.SectorsPerTrack;
  prm.nh  = rec_param.dev_bpb.Heads;
  for (i=0; i<MAX_SECTORS_PER_TRACK; i++) {
    dp.map [i].sectno = i+1;
    dp.map [i].sectsize = SECSIZE;
  }
  dp.cmdinfo = 1;		/* track is consecutive sectors */
  return (TRUE);
#elif !defined(V11)
  ret_code = DosOpen (disk_name, &disk_handle,
		      &action_taken, 0L,
		      0, OPEN_FLAG, OPEN_MODE, 0L);
  if (ret_code) {
#ifdef TRACE
    fprintf (stderr, "DosOpen failed: %d.\n", ret_code);
    fflush (stderr);
#endif
    return (FALSE);
  }

  ret_code = DosDevIOCtl ((char far *)&zero_data, (char far *)&zero_data,
			  IOCTL_LOCK, IOCTL_CATEGORY, disk_handle);
  if (ret_code) {
#ifdef TRACE
    fprintf (stderr, "LOCK IOCTL failed: %d.\n", ret_code);
    fflush (stderr);
#endif
    return (FALSE);
  }

  c = GET_RECOMM;
  ret_code = DosDevIOCtl ((char far *)&rec_param, (char far *)&c,
			  IOCTL_GET_PARAMS, IOCTL_CATEGORY, disk_handle);
  if (ret_code) {
#ifdef TRACE
    fprintf (stderr, "GET PARAMS IOCTL failed: %d.\n", ret_code);
    fflush (stderr);
#endif
    return (FALSE);
  }

#ifndef FLOPPY_OK
  if (rec_param.device_type != HARD_DISK)
    fmt_exit_error (IS_FLOPPY_ERROR);
#endif

  if (rec_param.dev_bpb.TotalSectors) {
    rec_param.dev_bpb.Ext_totalSectors = (ULONG)rec_param.dev_bpb.TotalSectors;
    rec_param.dev_bpb.TotalSectors = 0;
  }
  rec_param.dev_bpb.NumberOfFATs = 0;	/* PTM 3174 */

  partition_offset = rec_param.dev_bpb.HiddenSectors;
  number_of_sectors = rec_param.dev_bpb.Ext_totalSectors;
  prm.spt = rec_param.dev_bpb.SectorsPerTrack;
  prm.nh  = rec_param.dev_bpb.Heads;
  for (i=0; i<MAX_SECTORS_PER_TRACK; i++) {
    dp.map [i].sectno = i+1;
    dp.map [i].sectsize = SECSIZE;
  }
  dp.cmdinfo = 1;		/* track is consecutive sectors */

  return (TRUE);
#else
/*
 * Get a handle to the disk.
 */

  ret_code = DOSPHYSICALDISK (2, (char far *)&disk_handle, 2,
			      (char far *)disk_name, 3);
  if (ret_code)
    return (FALSE);

/*
 * Initialize the sector layout.  This is done here so that
 * find_pinball_partition can use the packet to read the master
 * boot record.
 */

  for (i=0; i<MAX_SECTORS_PER_TRACK; i++) {
    dp.map [i].sectno = i+1;
    dp.map [i].sectsize = SECSIZE;
  }
  dp.cmdinfo = 1;		/* track is consecutive sectors */

/*
 * Read the disk parameters, including sectors per track, number of Heads,
 * and number of cylinders.
 */

  DosDevIOCtl ((char far *)&prm, (char far *)&zero_data, IOCTL_GET_PARAMS,
	       IOCTL_CATEGORY, disk_handle);

  return (find_pinball_partition ()); /* look for Pinball partition */
#endif
}

/***	fmt_format_track - format a single track
 *
 *	This function is called to erase a single track on the disk.
 *	A disk IOCTL is used for this purpose.	If the track is usable,
 *	a non-zero value is returned.  Otherwise, zero is returned so
 *	that the caller can mark the track as bad in filesystem structures.
 *
 *	Currently this function supports an extra command line
 *	parameter.  It is /NOF, and causes no formatting to take place --
 *	success is assumed, so that a file system may be initialized
 *	quickly.
 *
 *	fmt_format_track (void)
 *
 *	ENTRY		No parameters
 *
 *	EXIT		Returns TRUE if the track is OK
 *
 *	CALLS		DosDevIOCTL
 *
 *	WARNINGS	None
 *
 *	EFFECTS		Erases the track referenced by the format packet
 */
USHORT fmt_format_track (void)
{
  USHORT ret_code;
  ULONG zero_parameter = 0L;

#ifdef FILE_VER
  DOSWRITE (disk_handle, zeros, SECSIZE, (PUSHORT)&ret_code);
  return (TRUE);
#endif

#ifdef TEST_BAD_BLOCKS
  if ((dp.cyl == 10) && !dp.head)	/* TEST - cylinder 10 IS bad */
    return (FALSE);
#endif

/*
 * If the user specified /N, don't format at all.
 */

  if (!fmt_format_flag)		/* just init, don't actually format */
    return (TRUE);		/* return success immediately */

/*
 * Erase this track by writing to it.
 */

  ret_code = DosDevIOCtl ((char far *)blank_buf,
			  (char far *)&dp,
			  IOCTL_WRITE,
			  IOCTL_CATEGORY,
			  disk_handle);

  return (ret_code ? FALSE : TRUE);
}

/***	fmt_disk_write - write bytes to a sector run.
 *
 *	This function is called to write an arbitrary number of bytes of
 *	data to the disk, starting at a sector boundary.  If the write is
 *	not exactly a sector, it will be buffered.
 *
 *	fmt_disk_write (sector, buffer, size)
 *
 *	ENTRY		sector - sector number to write to
 *			buffer - pointer to data to write
 *			size   - number of bytes to write
 *
 *	EXIT		Returns TRUE if write is successful
 *
 *	CALLS		write_scratch
 *
 *	WARNINGS	None
 *
 *	EFFECTS		Writes data to the disk
 */
USHORT fmt_disk_write (sector, buffer, size)
  ULONG sector;
  UCHAR far *buffer;
  USHORT size;
{
  USHORT ret_code;
  UCHAR *scratchbuf;
  USHORT nsect;

#ifdef FILE_VER
  ULONG phys_sector;
  DOSCHGFILEPTR (disk_handle, sector * SECSIZE, 0, &phys_sector);
  return (!DOSWRITE (disk_handle, buffer, size, (PUSHORT)&ret_code));
#endif

/*
 * Calculate the number of sectors to write.
 */

  nsect = (size+SECSIZE-1) / SECSIZE;

#ifdef	TRACE
  fprintf (stderr, "fmt_disk_write (%ld, a buffer, %d)\n", sector, size);
  fflush (stderr);
#endif

/*
 * If the write is not an integral number of sectors, we have to buffer
 * the sectors so that data outside the operation won't be disturbed.
 */

  if (size % SECSIZE) {			/* must buffer a partial sector write */
    scratchbuf = malloc (((size+SECSIZE-1) / SECSIZE) * SECSIZE);
    if (scratchbuf == NULL) {
      com_dispmsg (rctomid (ERROR_NOT_ENOUGH_MEMORY), STDERR_CODE, NULL, 0);
      return (FALSE);
    }

/*
 * Read the entire run plus padding to the end of the last sector.
 */

    ret_code = read_scratch (sector, (char far *)scratchbuf, nsect);

/*
 * Copy our data in.
 */

    memcpy (scratchbuf, buffer, size);

/*
 * Now that we've buffered it, write the data out.
 */

    ret_code = write_scratch (sector, (char far *)scratchbuf, nsect);
    free (scratchbuf);			/* deallocate memory */
    return (ret_code ? FALSE : TRUE);	/* return success or failure */
  }

/*
 * It's an integral number of sectors.  Just write the data.
 */

  return (write_scratch (sector, buffer, nsect) ? FALSE : TRUE);
}

/***	fmt_query_volid - prompt for a volume label
 *
 *	This function is called to get a volume label from the user.
 *	It displays a prompt, reads a string from standard input, and
 *	checks the string for valid characters.  If any characters are
 *	invalid the prompt is re-displayed.
 *
 *	fmt_query_volid (void)
 *
 *	ENTRY		No parameters
 *
 *	EXIT		No return value
 *
 *	CALLS		computmsg
 *			strchr
 *
 *	WARNINGS	None
 *
 *	EFFECTS		Fills in global variable fmt_volume_label
 */
void fmt_query_volid (void)
{
  UCHAR c;
  USHORT i;
  USHORT good=FALSE;

  while (!good) 
  {
    memset (fmt_inbuf, '\0', VOL_LABEL_SIZE);
    memset (fmt_volume_label, '\0', VOL_LABEL_SIZE+2);

    FMT_COMPUTMSG( SYSTEM_MSG, NULL, 0, FMT_LABEL_PROMPT_STR, msgfilename, STDOUT_CODE,
	       STRING_RESPONSE, fmt_inbuf, VOL_LABEL_SIZE + 1);

    good = TRUE;			            /* assume correct label */
    com_strupr (fmt_inbuf);			/* convert to uppercase */

    for (i=0; i<VOL_LABEL_SIZE; i++) {		/* check each character */
      c = fmt_inbuf [i];		            /* get a byte */
      if (check_dbcs (c)) {			      /* is it a DBCS first byte? */

			if( i+1 >= VOL_LABEL_SIZE ) {								 //PCR CP20 J200307

			    /*	With this DBCS character, the label			 //PCR CP20 J200307
			     *	will be too long.  Leave off the lead		 //PCR CP20 J200307
			     *	DBCS byte.											 //PCR CP20 J200307
			     */															 //PCR CP20 J200307
			    fmt_volume_label[i+1] = '\0';						 //PCR CP20 J200307
			    break;														 //PCR CP20 J200307
			}																	 //PCR CP20 J200307

			/*																	 //PCR CP20 J200307
			 *  It's a DBCS character, and it fits.  Copy the	 //PCR CP20 J200307
			 *  lead byte, and then increment i and copy the	 //PCR CP20 J200307
			 *  trailing byte.											 //PCR CP20 J200307
			 */																 //PCR CP20 J200307
			fmt_volume_label [i+1] = c;								 //PCR CP20 J200307
			i++;																 //PCR CP20 J200307
			fmt_volume_label[i+1] = fmt_inbuf[i];					 //PCR CP20 J200307
      }
      else 
       if (c && (c != ' ') && strchr (iv_chars, c)) 
       { /* no, check validity */
       	scratchbuf [0] = c;			/* store character for display */
       	scratchbuf [1] = '\0';
         FMT_COMPUTMSG( SYSTEM_MSG, &scratchptr, 1, FMT_ILLEGAL_CHAR_STR, msgfilename, STDERR_CODE,
                        NO_RESPONSE, NULL, 0);
         good = FALSE;
         break;					/* have him try again */
       }
       else                                                  //PCR CP20 J200307
         fmt_volume_label [i+1] = c;		/* store upcased character */
    }
  }

  if (fmt_volume_label [VOL_LABEL_SIZE+1])	/* label too long */
    fmt_volume_label [0] = VOL_LABEL_SIZE;	/* truncate it */
  else						/* within limits */
    fmt_volume_label [0] = (BYTE) strlen (fmt_volume_label+1); /* store length byte */
}

/***	fmt_write_volid - sets the volume label
 *
 *	Copies the volume label into the specified location, usually
 *	in a BPB structure.  We could do this with the special Pinball
 *	IOCTL, but it's easier this way, because we're going to write
 *	the boot record anyway.
 *
 *	fmt_write_volid (boot_record_vol_id)
 *
 *	ENTRY		boot_record_vol_id - pointer to volume label field in
 *					     boot record.
 *
 *	EXIT		No return value
 *
 *	CALLS		memcpy
 *
 *	WARNINGS	None
 *
 *	EFFECTS		Sets volume label on disk
 */
void fmt_write_volid (boot_record_vol_id)
  UCHAR *boot_record_vol_id;
{
  memcpy (boot_record_vol_id, &fmt_volume_label [1], VOL_LABEL_SIZE);
}

/***	fmt_read_mbr - read the master boot record
 *
 *	This function reads the master boot record for a partition.
 *	It does so by reading cylinder 0, head 0, sector 0 of the
 *	partition.  The data is read into the global variable "ourmbr".
 *
 *	fmt_read_mbr (void)
 *
 *	ENTRY		No parameters
 *			disk_handle should be open on the partition
 *
 *	EXIT		Returns 0 if success, otherwise OS/2 error code
 *			"ourmbr" filled in with partition's master boot record
 *
 *	CALLS		DosDevIOCtl
 *
 *	WARNINGS	Open the disk first
 *
 *	EFFECTS		Reads disk
 */
USHORT fmt_read_mbr (void)
{
  dp.sect = 0;
  dp.head = 0;
  dp.cyl = 0;
  dp.nsect = 1;
  return (DosDevIOCtl ((UCHAR far *)&ourmbr, (UCHAR *)&dp, IOCTL_READ,
		       IOCTL_CATEGORY, disk_handle));
}

/***	fmt_write_mbr - write the master boot record
 *
 *	This function writes the master boot record for a partition.
 *	It does so by writing cylinder 0, head 0, sector 0 of the
 *	partition.  The data is written from the global variable "ourmbr".
 *
 *	fmt_write_mbr (void)
 *
 *	ENTRY		No parameters
 *			disk_handle should be open on the partition
 *
 *	EXIT		Returns 0 if success, otherwise OS/2 error code
 *			"ourmbr" written to disk
 *
 *	CALLS		DosDevIOCtl
 *
 *	WARNINGS	Open the disk first
 *
 *	EFFECTS		Writes disk
 */
USHORT fmt_write_mbr (void)
{
  dp.sect = 0;
  dp.head = 0;
  dp.cyl = 0;
  dp.nsect = 1;
  return (DosDevIOCtl ((UCHAR far *)&ourmbr, (UCHAR *)&dp, IOCTL_WRITE,
		       IOCTL_CATEGORY, disk_handle));
}
