home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / CLIPPER / MISC / HARDERR.ZIP / HARDERR.C
Encoding:
Text File  |  1988-07-21  |  14.9 KB  |  441 lines

  1. /*-------------------------------------------------------------------
  2.  *
  3.  *  HARDERR.C   Hard (Critical) error handler
  4.  *
  5.  *  Copyright (C) 1988 by Gary M. Gibson.  All rights reserved.
  6.  *
  7.  *  This module may be freely used for personal or non-profit use
  8.  *  ONLY. No commercial use of this routine is allowed without
  9.  *  prior licensing by the author.
  10.  *
  11.  *  This module provides a critical error handler for use on
  12.  *  MS-DOS systems. It provides more information and options
  13.  *  than the regular MS-DOS error handler and, in addition,
  14.  *  senses a paper-out condition, or a not ready condition
  15.  *  on stdout directed to a character device, and silently
  16.  *  retries the write until the printer has been readied.
  17.  *
  18.  *  Each error not automatically retried displays a two line
  19.  *  message on the display console. The first line identifies
  20.  *  the error and has two formats, one for character devices,
  21.  *  and the other for disk devices. Line one for a character
  22.  *  device appears as:
  23.  *
  24.  *      tttt error on dddd
  25.  *
  26.  *  where 'tttt' is a brief description of the error, and 'dddd'
  27.  *  is the name of the device involved. Line one for a disk
  28.  *  error is more complex, appearing as:
  29.  *
  30.  *      tttt error rrrring aaaa on drive d
  31.  *
  32.  *  where 'tttt' is as above, 'rrrr' is 'read' or 'writ' depending
  33.  *  on whether the error occurred during a read or a write of the
  34.  *  disk.  'aaaa' is a description of the area of the disk being
  35.  *  read or written. This is one of:
  36.  *
  37.  *      DOS area                The disk partition or boot record.
  38.  *      file allocation table   The allocation bitmap(s).
  39.  *      directory               The file directory entries.
  40.  *      file data area          A named file.
  41.  *
  42.  *  Finally, 'd' is the alphabetic drive code (A, B, etc.) for the
  43.  *  disk having the error.
  44.  *
  45.  *  The second line solicits a user action selection, listing the
  46.  *  options available. If all options are available, the line
  47.  *  appears as:
  48.  *
  49.  *      Action? ([R]etry, (I)gnore, (A)bort, (F)ail):
  50.  *
  51.  *  Depending on the error, not all options may be displayed. Only those
  52.  *  that are may be chosen. The response must be the first letter of
  53.  *  the selection (R, I, A, or F) in either case, or a Return, which
  54.  *  will select Retry if available. A correct response is displayed,
  55.  *  incorrect responses are not displayed but cause a "beep". The
  56.  *  handler will wait until an acceptable response code has been given.
  57.  *
  58.  *  The actions have the following effect:
  59.  *
  60.  *  Retry   Causes the operation to be retried. If it fails again,
  61.  *          another message will appear. This is the safest action and
  62.  *          is the default whenever possible.
  63.  *
  64.  *  Ignore  This is the MOST DANGEROUS selection possible as it instructs
  65.  *          MS-DOS to *PRETEND* to the user that the operation completed
  66.  *          successfully. The program may malfunction as a result.
  67.  *
  68.  *  Abort   This causes the program to be abruptly terminated and a return
  69.  *          made to its caller (a shell like COMMAND.COM or an EXECing
  70.  *          program). The program does NOT receive control before termination
  71.  *          so cleanup actions may not be performed.
  72.  *
  73.  *  Fail    This instructs DOS to pass a "failed" status back to the requesting
  74.  *          program. What the program does with this information is up to it.
  75.  *          The actual status returned depends on the function being performed.
  76.  *
  77.  *  This has been tested with Microsoft C 5.0 and Turbo C 1.5.
  78.  *  Library differences are handled automatically.
  79.  *
  80.  *  To use in a program, simply:
  81.  *
  82.  *  1. Define the necessary function prototype: #include <dos.h>
  83.  *  2. Define the hard error function:
  84.  *     Microsoft C 5.0+     extern int far hard_error();
  85.  *     Turbo C 1.5          extern int hard_error();
  86.  *  3. Establish the hard error handler:
  87.  *     Microsoft C 5.0+     _harderr( hard_error );
  88.  *     Turbo C 1.5          harderr( hard_error );
  89.  *
  90.  *  That's it!
  91.  *
  92.  *  If MAIN is defined, a simple test program is generated at the end
  93.  *  of this module.
  94.  *
  95.  *-------------------------------------------------------------------*/
  96.  
  97. #include <conio.h>
  98. #include <dos.h>
  99. #include <ctype.h>
  100.  
  101. /* Compiler sensitive definitions: Microsoft C or Turbo C */
  102.  
  103. #ifdef __TURBOC__               /* Kindly provided by Borland */
  104.  
  105. #define _enable  enable
  106. #define _harderr harderr
  107.  
  108. #define IGNORE_ACTION   0
  109. #define RETRY_ACTION    1
  110. #define ABORT_ACTION    2
  111. #define FAIL_ACTION     3
  112.  
  113. #else   /* Microsoft C */
  114.  
  115. #define IGNORE_ACTION   _HARDERR_IGNORE
  116. #define RETRY_ACTION    _HARDERR_RETRY
  117. #define ABORT_ACTION    _HARDERR_ABORT
  118. #define FAIL_ACTION     _HARDERR_FAIL
  119.  
  120. #endif
  121.  
  122.  
  123. /* Define the format of the information passed to the
  124.  * critical error handler in register AX and from there
  125.  * via the 'flags' parameter to the hard_error function.
  126.  */
  127.  
  128. typedef struct
  129. {
  130.     unsigned drive_code : 8;        /* Drive code (0=A, 1=B, etc.)          */
  131.     unsigned is_write   : 1;        /* 1 = write operation, 0 = read        */
  132.     unsigned disk_area  : 2;        /* Affected disk area code              */
  133.     unsigned fail_ok    : 1;        /* 1 = FAIL allowed                     */
  134.     unsigned retry_ok   : 1;        /* 1 = RETRY allowed                    */
  135.     unsigned ignore_ok  : 1;        /* 1 = IGNORE allowed                   */
  136.     unsigned            : 1;        /* Reserved                             */
  137.     unsigned nondisk    : 1;        /* 1 = not a disk error                 */
  138. }
  139. ERROR_FLAGS;
  140.  
  141.  
  142. /* Define the format of the attribute word in the
  143.  * device driver's header area.
  144.  */
  145.  
  146. typedef struct
  147. {
  148.     unsigned std_in     : 1;        /* 1 = device is the stdin device       */
  149.     unsigned std_out    : 1;        /* 1 = device is the stdout device      */
  150.     unsigned nul        : 1;        /* 1 = device is the NUL device         */
  151.     unsigned clock      : 1;        /* 1 = device is the CLOCK$ device      */
  152.     unsigned specl      : 1;        /* 1 = device accessible via INT 29h    */
  153.     unsigned            : 6;        /* Reserved                             */
  154.     unsigned ocrm       : 1;        /* 1 = device supports OPEN/CLOSE/MEDIA */
  155.     unsigned network    : 1;        /* 1 = driver for a network device      */
  156.     unsigned nonibm     : 1;        /* 1 = driver for non-ibm type disk     */
  157.     unsigned ioctl      : 1;        /* 1 = device supports IOCTL functions  */
  158.     unsigned chr        : 1;        /* 1 = device is a character device     */
  159. }
  160. ATTRIBUTES;
  161.  
  162.  
  163. /* Define the format of the device driver header area which
  164.  * is addressed by BP:SI on entry to the critical error
  165.  * handler and via the 'header' parameter to the hard_error
  166.  * function.
  167.  */
  168.  
  169. typedef struct
  170. {
  171.     void far    *next_device;       /* Pointer to next device in chain      */
  172.     ATTRIBUTES  attributes;         /* Drive attribute flags                */
  173.     unsigned    driver_s;           /* Offset to driver strategy rtn.       */
  174.     unsigned    driver_i;           /* Offset to driver interrupt rtn.      */
  175.     char        device_name[8];     /* Device name (character devices)      */
  176. }
  177. DEVICE_HEADER;
  178.  
  179.  
  180. /* Define the error codes passed by DOS to the critical error (INT 24)
  181.  * handler and thence to the hard_error function. These codes are used
  182.  * as an index into the errmsg[] array.
  183.  */
  184.  
  185. #define WRITE_PROTECT   0x0
  186. #define UNKNOWN_UNIT    0x1
  187. #define NOT_READY       0x2
  188. #define UNKNOWN_CMD     0x3
  189. #define CRC_ERROR       0x4
  190. #define BAD_REQUEST     0x5
  191. #define SEEK_ERROR      0x6
  192. #define UNKNOWN_MEDIA   0x7
  193. #define SECTOR_NOT_FND  0x8
  194. #define OUT_OF_PAPER    0x9
  195. #define WRITE_FAULT     0xa
  196. #define READ_FAULT      0xb
  197. #define GENERAL_FAILURE 0xc
  198.  
  199. static char *errmsg[] = { "Write protect", "Unknown unit", "Not ready",
  200.                           "Unknown command", "Data (crc)",
  201.                           "Bad request length", "Seek",
  202.                           "Unknown media type", "Sector not found",
  203.                           "Out of paper", "Write fault", "Read fault",
  204.                           "General failure" };
  205.  
  206.  
  207. /* Define the descriptions of the effected disk area. The disk_area
  208.  * field of ERROR_FLAGS is used as an index into the where[] array.
  209.  */
  210.  
  211. static char *where[] = { "DOS area", "file allocation table",
  212.                          "directory", "file data area" };
  213.  
  214.  
  215. /* Define the internal action codes and associated messages.
  216.  * The order of the codes MUST match the order of the messages.
  217.  */
  218.  
  219. #define RETRY   1       /* Retry the operation                      */
  220. #define IGNORE  2       /* Ignore - pretend the operation succeeded */
  221. #define ABORT   4       /* Abort the program                        */
  222. #define FAIL    8       /* Return "failed" status to the program    */
  223.  
  224. static char *actions[] = { "[R]etry", "(I)gnore", "(A)bort", "(F)ail" };
  225.  
  226.  
  227. /*-------------------------------------------------------------------
  228.  * The hard_error function itself. Note there are two different
  229.  * definitions, one for Microsoft C 5.0 (on), and one for Turbo
  230.  * C 1.5. The differences are in the order of the formal
  231.  * parameters (same information, different order), the fact the
  232.  * the Microsoft C version is always a far function, and an
  233.  * error in Turbo C involving the DEVICE_HEADER pointer. All
  234.  * these differences are compensated for in the code.
  235.  */
  236.  
  237. #ifdef __TURBOC__
  238.  
  239. int hard_error( unsigned code,
  240.                 ERROR_FLAGS flags,
  241.                 unsigned header_seg,    /* Turbo C 1.5 PUSHes the segment & */
  242.                 unsigned header_ofs )   /* offset in the wrong order. This  */
  243.                                         /* This is corrected below.         */
  244.  
  245. #else
  246.  
  247. int far hard_error( ERROR_FLAGS flags,
  248.                     unsigned code,
  249.                     DEVICE_HEADER far *header )
  250.  
  251. #endif
  252.  
  253. {
  254.     /* NOTE: Do NOT do anything which will cause any DOS INT 21 function
  255.      * to be used other than those for keyboard and display I/O.
  256.      * Only the I/O functions in CONIO.H are safe. File I/O or other
  257.      * function usage will be hazardous to the system's health.
  258.      */
  259.  
  260.     int i;
  261.     unsigned char action;
  262.  
  263. #ifdef __TURBOC__
  264.  
  265.     /* This code is necessary because the Turbo C 1.5 library PUSHes
  266.      * the segment and offset portions of the device header pointer
  267.      * in the wrong order!  It can be removed and the function definition
  268.      * fixed when Turbo C is fixed.
  269.      */
  270.  
  271.     DEVICE_HEADER far *header;
  272.  
  273.     *((unsigned *)&header)   = header_ofs;
  274.     *((unsigned *)&header+1) = header_seg;
  275.  
  276. #endif
  277.  
  278.     _enable();              /* Ensure interrupts are enabled    */
  279.  
  280.     code &= 0xff;           /* High order byte is undefined     */
  281.  
  282.  
  283.     /* If this is an out-of-paper error, force a retry so the program
  284.      * will wait silently until the printer is ready. It is assumed
  285.      * the the printer will alert the user that it needs paper via
  286.      * a light, a beep, or something (in any event, it will stop
  287.      * printing).
  288.      */
  289.  
  290.     if( code == OUT_OF_PAPER )  return RETRY_ACTION;
  291.  
  292.     /* Check for not-ready error on standard output which is also
  293.      * a character device. If so, always retry the I/O.
  294.      */
  295.  
  296.     if( flags.nondisk && header->attributes.std_out && code == NOT_READY )
  297.     {
  298.         return RETRY_ACTION;
  299.     }
  300.  
  301.     /* We cannot just retry the error, describe the error to the user. */
  302.  
  303.     if( flags.nondisk )
  304.     {
  305.         cprintf( "%s error on ", errmsg[code] );
  306.         for ( i = 0; i < 8; i++ )
  307.         {
  308.             putch( header->device_name[i] );    /* We cannot use cprintf
  309.                                                  * here as we have a far
  310.                                                  * pointer and we may be
  311.                                                  * in a program using a small
  312.                                                  * or compact memory model
  313.                                                  * which would have a "printf"
  314.                                                  * that can't handle such
  315.                                                  * things!
  316.                                                  */
  317.         }
  318.         cputs( "\r\n" );
  319.  
  320.     }
  321.     else
  322.     {
  323.         cprintf( "%s error %sing %s on drive %c\r\n",
  324.                  errmsg[code], flags.is_write ? "writ" : "read",
  325.                  where[flags.disk_area], flags.drive_code + 'A' );
  326.     }
  327.  
  328.     /* Determine and display the set of allowable actions */
  329.  
  330.     action = ABORT;
  331.     if( flags.ignore_ok )   action |= IGNORE;
  332.     if( flags.retry_ok )    action |= RETRY;
  333.     if( flags.fail_ok )     action |= FAIL;
  334.  
  335.     cputs( "Action? (" );
  336.     i = 0;
  337.  
  338.     while( action )
  339.     {
  340.         if( action & 1 )
  341.         {
  342.             cputs( actions[i] );
  343.             if( action & 0xE )
  344.             {
  345.                 cputs( ", " );
  346.             }
  347.         }
  348.         i++;
  349.         action >>= 1;
  350.     }
  351.  
  352.     cputs( "): ");
  353.  
  354.     /* Get a valid action selection and return it to DOS */
  355.  
  356.     action = 0x10;
  357.  
  358.     while( action > 0xf )
  359.     {
  360.         i = getch();
  361.         if( i == '\r' ) i = 'R';
  362.  
  363.         i = toupper(i);
  364.         if     ( i == 'A' )                     action = ABORT_ACTION;
  365.         else if( i == 'R' && flags.retry_ok )   action = RETRY_ACTION;
  366.         else if( i == 'I' && flags.ignore_ok )  action = IGNORE_ACTION;
  367.         else if( i == 'F' && flags.fail_ok )    action = FAIL_ACTION;
  368.  
  369.         if( action < 0x10 )
  370.         {
  371.             cprintf( "%c\r\n", i );
  372.         }
  373.         else
  374.             putch( '\a' );
  375.     }
  376.  
  377.     return action;
  378.  
  379. }
  380.  
  381. #ifdef MAIN
  382.  
  383. /*-------------------------------------------------------------------
  384.  * Test program in two parts. Part one outputs to stdprn, which is
  385.  * expected to be a printer. Paper-out should cause a silent wait;
  386.  * not-ready should produce an error. Part two reads a file TEST.FIL
  387.  * from the default directory. If this is a floppy disk, this can
  388.  * be made not ready during the read to test disk errors. Alternatively,
  389.  * an unformatted disk could be installed to check that FAT & directory
  390.  * read errors are correctly noted. In both tests, a "F"ail response
  391.  * will terminate the test.
  392.  */
  393.  
  394. #include <stdio.h>
  395. #include <stdlib.h>
  396.  
  397. void main( void )
  398. {
  399.     int i;
  400.     FILE *f;
  401.  
  402.     _harderr( hard_error );
  403.  
  404.     /* Printer test */
  405.  
  406.     for( i=1; i < 200; i++ )
  407.     {
  408.         fprintf( stdprn, "%d -- %d -- %d -- %d\r\n", i, i, i, i );
  409.         if( ferror(stdprn) )
  410.         {
  411.             puts( "Print test terminated by FAIL response\r\n" );
  412.             clearerr( stdprn );
  413.             break;
  414.         }
  415.     }
  416.  
  417.     /* File test */
  418.  
  419.     if( (f = fopen("TEST.FIL","rb")) == NULL )
  420.     {
  421.         puts( "Cannot open TEST.FIL\r\n" );
  422.         exit( 1 );
  423.     }
  424.  
  425.     do
  426.     {
  427.         i = fgetc(f);
  428.         if( ferror(f) )
  429.         {
  430.             puts( "File test terminated by FAIL response\r\n" );
  431.             clearerr( f );
  432.             break;
  433.         }
  434.     } while( !feof(f) );
  435.  
  436.     fclose( f );
  437.  
  438. }
  439.  
  440. #endif
  441.