home *** CD-ROM | disk | FTP | other *** search
- /*-------------------------------------------------------------------
- *
- * HARDERR.C Hard (Critical) error handler
- *
- * Copyright (C) 1988 by Gary M. Gibson. All rights reserved.
- *
- * This module may be freely used for personal or non-profit use
- * ONLY. No commercial use of this routine is allowed without
- * prior licensing by the author.
- *
- * This module provides a critical error handler for use on
- * MS-DOS systems. It provides more information and options
- * than the regular MS-DOS error handler and, in addition,
- * senses a paper-out condition, or a not ready condition
- * on stdout directed to a character device, and silently
- * retries the write until the printer has been readied.
- *
- * Each error not automatically retried displays a two line
- * message on the display console. The first line identifies
- * the error and has two formats, one for character devices,
- * and the other for disk devices. Line one for a character
- * device appears as:
- *
- * tttt error on dddd
- *
- * where 'tttt' is a brief description of the error, and 'dddd'
- * is the name of the device involved. Line one for a disk
- * error is more complex, appearing as:
- *
- * tttt error rrrring aaaa on drive d
- *
- * where 'tttt' is as above, 'rrrr' is 'read' or 'writ' depending
- * on whether the error occurred during a read or a write of the
- * disk. 'aaaa' is a description of the area of the disk being
- * read or written. This is one of:
- *
- * DOS area The disk partition or boot record.
- * file allocation table The allocation bitmap(s).
- * directory The file directory entries.
- * file data area A named file.
- *
- * Finally, 'd' is the alphabetic drive code (A, B, etc.) for the
- * disk having the error.
- *
- * The second line solicits a user action selection, listing the
- * options available. If all options are available, the line
- * appears as:
- *
- * Action? ([R]etry, (I)gnore, (A)bort, (F)ail):
- *
- * Depending on the error, not all options may be displayed. Only those
- * that are may be chosen. The response must be the first letter of
- * the selection (R, I, A, or F) in either case, or a Return, which
- * will select Retry if available. A correct response is displayed,
- * incorrect responses are not displayed but cause a "beep". The
- * handler will wait until an acceptable response code has been given.
- *
- * The actions have the following effect:
- *
- * Retry Causes the operation to be retried. If it fails again,
- * another message will appear. This is the safest action and
- * is the default whenever possible.
- *
- * Ignore This is the MOST DANGEROUS selection possible as it instructs
- * MS-DOS to *PRETEND* to the user that the operation completed
- * successfully. The program may malfunction as a result.
- *
- * Abort This causes the program to be abruptly terminated and a return
- * made to its caller (a shell like COMMAND.COM or an EXECing
- * program). The program does NOT receive control before termination
- * so cleanup actions may not be performed.
- *
- * Fail This instructs DOS to pass a "failed" status back to the requesting
- * program. What the program does with this information is up to it.
- * The actual status returned depends on the function being performed.
- *
- * This has been tested with Microsoft C 5.0 and Turbo C 1.5.
- * Library differences are handled automatically.
- *
- * To use in a program, simply:
- *
- * 1. Define the necessary function prototype: #include <dos.h>
- * 2. Define the hard error function:
- * Microsoft C 5.0+ extern int far hard_error();
- * Turbo C 1.5 extern int hard_error();
- * 3. Establish the hard error handler:
- * Microsoft C 5.0+ _harderr( hard_error );
- * Turbo C 1.5 harderr( hard_error );
- *
- * That's it!
- *
- * If MAIN is defined, a simple test program is generated at the end
- * of this module.
- *
- *-------------------------------------------------------------------*/
-
- #include <conio.h>
- #include <dos.h>
- #include <ctype.h>
-
- /* Compiler sensitive definitions: Microsoft C or Turbo C */
-
- #ifdef __TURBOC__ /* Kindly provided by Borland */
-
- #define _enable enable
- #define _harderr harderr
-
- #define IGNORE_ACTION 0
- #define RETRY_ACTION 1
- #define ABORT_ACTION 2
- #define FAIL_ACTION 3
-
- #else /* Microsoft C */
-
- #define IGNORE_ACTION _HARDERR_IGNORE
- #define RETRY_ACTION _HARDERR_RETRY
- #define ABORT_ACTION _HARDERR_ABORT
- #define FAIL_ACTION _HARDERR_FAIL
-
- #endif
-
-
- /* Define the format of the information passed to the
- * critical error handler in register AX and from there
- * via the 'flags' parameter to the hard_error function.
- */
-
- typedef struct
- {
- unsigned drive_code : 8; /* Drive code (0=A, 1=B, etc.) */
- unsigned is_write : 1; /* 1 = write operation, 0 = read */
- unsigned disk_area : 2; /* Affected disk area code */
- unsigned fail_ok : 1; /* 1 = FAIL allowed */
- unsigned retry_ok : 1; /* 1 = RETRY allowed */
- unsigned ignore_ok : 1; /* 1 = IGNORE allowed */
- unsigned : 1; /* Reserved */
- unsigned nondisk : 1; /* 1 = not a disk error */
- }
- ERROR_FLAGS;
-
-
- /* Define the format of the attribute word in the
- * device driver's header area.
- */
-
- typedef struct
- {
- unsigned std_in : 1; /* 1 = device is the stdin device */
- unsigned std_out : 1; /* 1 = device is the stdout device */
- unsigned nul : 1; /* 1 = device is the NUL device */
- unsigned clock : 1; /* 1 = device is the CLOCK$ device */
- unsigned specl : 1; /* 1 = device accessible via INT 29h */
- unsigned : 6; /* Reserved */
- unsigned ocrm : 1; /* 1 = device supports OPEN/CLOSE/MEDIA */
- unsigned network : 1; /* 1 = driver for a network device */
- unsigned nonibm : 1; /* 1 = driver for non-ibm type disk */
- unsigned ioctl : 1; /* 1 = device supports IOCTL functions */
- unsigned chr : 1; /* 1 = device is a character device */
- }
- ATTRIBUTES;
-
-
- /* Define the format of the device driver header area which
- * is addressed by BP:SI on entry to the critical error
- * handler and via the 'header' parameter to the hard_error
- * function.
- */
-
- typedef struct
- {
- void far *next_device; /* Pointer to next device in chain */
- ATTRIBUTES attributes; /* Drive attribute flags */
- unsigned driver_s; /* Offset to driver strategy rtn. */
- unsigned driver_i; /* Offset to driver interrupt rtn. */
- char device_name[8]; /* Device name (character devices) */
- }
- DEVICE_HEADER;
-
-
- /* Define the error codes passed by DOS to the critical error (INT 24)
- * handler and thence to the hard_error function. These codes are used
- * as an index into the errmsg[] array.
- */
-
- #define WRITE_PROTECT 0x0
- #define UNKNOWN_UNIT 0x1
- #define NOT_READY 0x2
- #define UNKNOWN_CMD 0x3
- #define CRC_ERROR 0x4
- #define BAD_REQUEST 0x5
- #define SEEK_ERROR 0x6
- #define UNKNOWN_MEDIA 0x7
- #define SECTOR_NOT_FND 0x8
- #define OUT_OF_PAPER 0x9
- #define WRITE_FAULT 0xa
- #define READ_FAULT 0xb
- #define GENERAL_FAILURE 0xc
-
- static char *errmsg[] = { "Write protect", "Unknown unit", "Not ready",
- "Unknown command", "Data (crc)",
- "Bad request length", "Seek",
- "Unknown media type", "Sector not found",
- "Out of paper", "Write fault", "Read fault",
- "General failure" };
-
-
- /* Define the descriptions of the effected disk area. The disk_area
- * field of ERROR_FLAGS is used as an index into the where[] array.
- */
-
- static char *where[] = { "DOS area", "file allocation table",
- "directory", "file data area" };
-
-
- /* Define the internal action codes and associated messages.
- * The order of the codes MUST match the order of the messages.
- */
-
- #define RETRY 1 /* Retry the operation */
- #define IGNORE 2 /* Ignore - pretend the operation succeeded */
- #define ABORT 4 /* Abort the program */
- #define FAIL 8 /* Return "failed" status to the program */
-
- static char *actions[] = { "[R]etry", "(I)gnore", "(A)bort", "(F)ail" };
-
-
- /*-------------------------------------------------------------------
- * The hard_error function itself. Note there are two different
- * definitions, one for Microsoft C 5.0 (on), and one for Turbo
- * C 1.5. The differences are in the order of the formal
- * parameters (same information, different order), the fact the
- * the Microsoft C version is always a far function, and an
- * error in Turbo C involving the DEVICE_HEADER pointer. All
- * these differences are compensated for in the code.
- */
-
- #ifdef __TURBOC__
-
- int hard_error( unsigned code,
- ERROR_FLAGS flags,
- unsigned header_seg, /* Turbo C 1.5 PUSHes the segment & */
- unsigned header_ofs ) /* offset in the wrong order. This */
- /* This is corrected below. */
-
- #else
-
- int far hard_error( ERROR_FLAGS flags,
- unsigned code,
- DEVICE_HEADER far *header )
-
- #endif
-
- {
- /* NOTE: Do NOT do anything which will cause any DOS INT 21 function
- * to be used other than those for keyboard and display I/O.
- * Only the I/O functions in CONIO.H are safe. File I/O or other
- * function usage will be hazardous to the system's health.
- */
-
- int i;
- unsigned char action;
-
- #ifdef __TURBOC__
-
- /* This code is necessary because the Turbo C 1.5 library PUSHes
- * the segment and offset portions of the device header pointer
- * in the wrong order! It can be removed and the function definition
- * fixed when Turbo C is fixed.
- */
-
- DEVICE_HEADER far *header;
-
- *((unsigned *)&header) = header_ofs;
- *((unsigned *)&header+1) = header_seg;
-
- #endif
-
- _enable(); /* Ensure interrupts are enabled */
-
- code &= 0xff; /* High order byte is undefined */
-
-
- /* If this is an out-of-paper error, force a retry so the program
- * will wait silently until the printer is ready. It is assumed
- * the the printer will alert the user that it needs paper via
- * a light, a beep, or something (in any event, it will stop
- * printing).
- */
-
- if( code == OUT_OF_PAPER ) return RETRY_ACTION;
-
- /* Check for not-ready error on standard output which is also
- * a character device. If so, always retry the I/O.
- */
-
- if( flags.nondisk && header->attributes.std_out && code == NOT_READY )
- {
- return RETRY_ACTION;
- }
-
- /* We cannot just retry the error, describe the error to the user. */
-
- if( flags.nondisk )
- {
- cprintf( "%s error on ", errmsg[code] );
- for ( i = 0; i < 8; i++ )
- {
- putch( header->device_name[i] ); /* We cannot use cprintf
- * here as we have a far
- * pointer and we may be
- * in a program using a small
- * or compact memory model
- * which would have a "printf"
- * that can't handle such
- * things!
- */
- }
- cputs( "\r\n" );
-
- }
- else
- {
- cprintf( "%s error %sing %s on drive %c\r\n",
- errmsg[code], flags.is_write ? "writ" : "read",
- where[flags.disk_area], flags.drive_code + 'A' );
- }
-
- /* Determine and display the set of allowable actions */
-
- action = ABORT;
- if( flags.ignore_ok ) action |= IGNORE;
- if( flags.retry_ok ) action |= RETRY;
- if( flags.fail_ok ) action |= FAIL;
-
- cputs( "Action? (" );
- i = 0;
-
- while( action )
- {
- if( action & 1 )
- {
- cputs( actions[i] );
- if( action & 0xE )
- {
- cputs( ", " );
- }
- }
- i++;
- action >>= 1;
- }
-
- cputs( "): ");
-
- /* Get a valid action selection and return it to DOS */
-
- action = 0x10;
-
- while( action > 0xf )
- {
- i = getch();
- if( i == '\r' ) i = 'R';
-
- i = toupper(i);
- if ( i == 'A' ) action = ABORT_ACTION;
- else if( i == 'R' && flags.retry_ok ) action = RETRY_ACTION;
- else if( i == 'I' && flags.ignore_ok ) action = IGNORE_ACTION;
- else if( i == 'F' && flags.fail_ok ) action = FAIL_ACTION;
-
- if( action < 0x10 )
- {
- cprintf( "%c\r\n", i );
- }
- else
- putch( '\a' );
- }
-
- return action;
-
- }
-
- #ifdef MAIN
-
- /*-------------------------------------------------------------------
- * Test program in two parts. Part one outputs to stdprn, which is
- * expected to be a printer. Paper-out should cause a silent wait;
- * not-ready should produce an error. Part two reads a file TEST.FIL
- * from the default directory. If this is a floppy disk, this can
- * be made not ready during the read to test disk errors. Alternatively,
- * an unformatted disk could be installed to check that FAT & directory
- * read errors are correctly noted. In both tests, a "F"ail response
- * will terminate the test.
- */
-
- #include <stdio.h>
- #include <stdlib.h>
-
- void main( void )
- {
- int i;
- FILE *f;
-
- _harderr( hard_error );
-
- /* Printer test */
-
- for( i=1; i < 200; i++ )
- {
- fprintf( stdprn, "%d -- %d -- %d -- %d\r\n", i, i, i, i );
- if( ferror(stdprn) )
- {
- puts( "Print test terminated by FAIL response\r\n" );
- clearerr( stdprn );
- break;
- }
- }
-
- /* File test */
-
- if( (f = fopen("TEST.FIL","rb")) == NULL )
- {
- puts( "Cannot open TEST.FIL\r\n" );
- exit( 1 );
- }
-
- do
- {
- i = fgetc(f);
- if( ferror(f) )
- {
- puts( "File test terminated by FAIL response\r\n" );
- clearerr( f );
- break;
- }
- } while( !feof(f) );
-
- fclose( f );
-
- }
-
- #endif