[net.sources] MS-DOS Critical Error Handler

kent@ncoast.UUCP (Kent Williams) (12/02/86)

s/*** REPLACE THIS LINE WITH YOUR MESSAGE ***/YOUR MESSAGE/g

The following is a MS-DOS critical error handler for C.  It is written
for Microsoft C, but should work with any compiler provided that the
assembly file crit.asm is edited to conform with local segment and public
naming conventions.

A MS-DOS critical error is when an I/O error occurs that puts you at a
prompt "abort, retry, ignore".  In the absence of a handler, if you select
abort, you get dumped back at the DOS prompt.  With the handler, control
returns to your program if an error has occured, where it can be dealt with
more gracefully.

----------------------------CUT HERE---------------------------------------
# shell archive (shar) file utility
# V1.00 (c) 1986 SIASOFT# Unpack using PC-SHELL or some reasonable facsimile
# by using:
#
#   sh < archivename
#
# FILE: crit.doc
echo unpacking crit.doc
sed s/^-// <<SHAR_EOF >crit.doc
-          CRIT (2)           Critical Error Handler           CRIT (2)
-
-
-     CRITICAL  
-
-          critical error handler routines for Microsoft C (et al) 
-
-     SYNOPSIS  
-
-               int setup_crit();
-
-               void restore_crit();
-
-
-     DESCRIPTION  
-
-          These  two  functions  implement  a  simple  critical  error
-          handler for Microsoft C. The setup_crit function works  much
-          like  the  standard  library's  setjmp function - when it is
-          called initially,  it  returns  0.  When  a  critical  error
-          occurs,  control is passed to the point where setup_crit was
-          called, and the function appears to return -1.  
-
-          If a critical error  occurs,  the  default  DOS  handler  is
-          called,  which  will  display  the  'ABORT,  RETRY,  IGNORE'
-          message and wait for a key to be struck.  If retry or ignore 
-          are selected, then DOS does whatever it is it supposed to do 
-          in that situation.  If, however,  the  user  selects  abort,
-          then  control is returned to your program at the point where
-          the call to setup_crit was made.  
-
-          The whole rationale for these functions is for your  program
-          to  be  able  to  catch  fatal  errors  and  deal  with them
-          relatively gracefully.   Unless  they  are  used,  DOS  will
-          unceremoniously   abort   your  program,  leaving  you  with
-          dangling files and interrupt vectors.  
-
-          The restore_crit function restores the default DOS  critical
-          error handler.    It  is  not necessary to call restore_crit
-          when your program terminates, as DOS restores  the  critical
-          error vector  out  of  your program segment prefix.  It will
-          allow you to turn off catching of critical errors.  
-
-     CAVEATS  
-
-          You  may  call   setup_crit   repeatedly   without   calling
-          restore_crit  -  an  interlock is used so that garbage isn't
-          put into the critical error interrupt vector.  However, each 
-          call to setup_crit overwrites the context saved by  previous
-          calls, so they do not nest.  
-
-     EXAMPLES  
-
-          The  following  example  calls  setup_crit to catch critical
-          errors.  It then tries to open a file on drive A:. If you've 
-          left the drive door open or have an unformatted diskette  in
-          A:,  the  critical  error  will  cause a return to the point
-          where setup_crit is called.  
-
-
-
-
-
-                                      -1-
-
-
-          CRIT (2)           Critical Error Handler           CRIT (2)
-
-
-          #include <stdio.h>
-          main(argc,argv)
-          int argc;
-          char **argv;
-          {
-    FILE *foo = NULL;
-    static char fnamebuf[65];
-    /* attempt to open the file specified by the first command line
-    ** argument
-    */
-    sprintf(fnamebuf,"A:%s",argv[1]);    /* build the file name */
-    for (;foo == NULL;)
-    {
-        if (-1 == setup_crit())    /* set up error handler */
-        {
-            /* if an error occurs, you will end up here */
-            fprintf(stderr,
-            "Check to make sure a valid disk is drive A:\n");
-            fprintf(stderr,"and that the drive door is closed\n");
-            fprintf(stderr,"Hit return to continue\n");
-            getchar();
-        }
-        foo = fopen(fnamebuf,"r");
-    }
-    restore_crit();    /* turn off critical error handling */
-          }
-
-     FILES  
-
-          testcrit.c, crit.asm 
-
-     RETURN VALUE  
-
-          setup_crit returns 0 when invoked to set up  critical  error
-          catching, and -1 when a critical error occurs.  
-
-     SEE ALSO  
-
-          setjmp,longjmp 
-
-     NOTES  
-
-          These functions were written by 
-          Kent Williams
-          722 Rundell
-          Iowa City IA 52240
-          (319)338-6053
-
-          They  may be used freely, provided proper credit is given to
-          their author.  They may be freely distributed,  so  long  as
-          all  components  of  this  package  (crit.asm testcrit.c and
-          crit.doc) are included, and  no  charge  be  made  beyond  a
-          nominal fee for duplication.  
-
-
-
-
-
-
-
-                                      -2-
-
SHAR_EOF
# FILE: crit.asm
echo unpacking crit.asm
sed s/^-// <<SHAR_EOF >crit.asm
-;
-; testcrit.asm - implements a 'setjmp' style critical error handler
-; for Microsoft C.  Implementation with other compilers should be trivial.
-; define LARGE_CODE to compile for large code models
-;
-_TEXT	segment public 'CODE'
-
-_DATA	segment public 'DATA'
-_DATA	ends
-
-assume cs:_TEXT,ds:_DATA
-
-old_handler	label dword
-old_off	dw ?
-old_seg	dw ?
-
-critical	equ 24H
-
-savess	dw	?
-savesp	dw	?
-savebp	dw	?
-savesi	dw	?
-savedi	dw	?
-returnaddress	dw	?
-ifdef LARGE_CODE
-returnsegment	dw	?
-endif
-datasegment	dw	?
-extrasegment	dw	?
-counter	dw	0
-
-; crit_handler is where the critical error vector is pointed.
-; it follows the suggestions found in the DOS technical reference handler,
-; i.e. it calls the old handler, and decides what to do on the basis of the
-; value returned in AL
-	public crit_handler
-crit_handler proc near
-	pushf								; push flags to simulate s/w interrupt
-	call	dword ptr cs:old_handler	; call the old handler
-	cmp		al,2						; is this an abort?
-	je do_abort							; if it is, handle abort
-	iret								; if not, just iret
-do_abort:
-	; otherwise, restore environment
-	cli									; disable interrupts
-	; restore all important registers to the values they had upon entry
-	; into setup_crit, set AX to -1 and return
-	mov	ss,cs:savess
-	mov	sp,cs:savesp
-	mov	bp,cs:savebp
-	mov	si,cs:savesi
-	mov	di,cs:savedi
-	mov	ds,cs:datasegment
-	mov	es,cs:extrasegment
-	mov	bx,cs:returnaddress				; pick up return address
-	mov	ax,-1							; return -1
-	sti									; enable interrupts
-ifdef LARGE_CODE
-	jmp	dword ptr cs:returnaddress
-else
-	jmp	bx
-endif
-crit_handler	endp
-
-;
-; setup_crit - sets up the critical error handler.
-; When this is called initially it will return 0.
-; If a critical error occurs, and the user responds with 'abort'
-; to the abort, retry, ignore message, then it will return -1.
-; This is very similar to the setjmp/longjmp non-local goto interface in
-; the standard C library
-;
-	public _setup_crit
-ifdef LARGE_CODE
-_setup_crit	proc	far
-else
-_setup_crit	proc	near
-endif
-	; the counter variable is an interlock - if you call setup_crit
-	; if another call to setup_crit is already pending, then restore_crit will
-	; be called in order to restore the old vector before installing the new
-	; one.
-	cmp	cs:counter,0					; have we been here before?
-	je continue							; if not, keep going
-ifdef LARGE_CODE
-	call far ptr _restore_crit
-else
-	call _restore_crit					; otherwise restore old handler 1st
-endif
-continue:
-	inc	cs:counter						; bump the counter
-	push	es							; save registers munged
-	push	ds							;
-	push	si							;
-	xor	ax,ax							; address interrupt vectors
-	mov	ds,ax
-	mov	bx,critical*4					; offset of critical error handler
-	les si,[bx]							; load into es:si
-	mov	cs:old_off,si					; save offset
-	mov	cs:old_seg,es					; save segment
-	mov	word ptr [bx],offset crit_handler	; install new handler
-	mov	2[bx],cs						; handler segment
-	pop	si								; restore munged variables
-	pop	ds								;
-	pop	es								;
-	; save the relevant calling environment, which consists of
-	; ss,sp,bp (stack environment) si,di (possible register vars)
-	; and ds,es (data segments)
-	mov	cs:savess,ss
-	mov	cs:savesp,sp
-	mov	cs:savebp,bp
-	mov	cs:savesi,si
-	mov	cs:savedi,di
-	mov	cs:datasegment,ds
-	mov	cs:extrasegment,es
-	xor	ax,ax							; return 0
-	pop	bx								; get return address
-	mov	cs:returnaddress,bx				; save it
-ifdef LARGE_CODE
-	pop	bx								; get return segment
-	mov	cs:returnsegment,bx
-	jmp dword ptr cs:returnaddress
-else
-	jmp	bx								; go to return address
-endif
-_setup_crit	endp
-
-	public _restore_crit
-ifdef LARGE_CODE
-_restore_crit	proc	far
-else
-_restore_crit	proc	near
-endif
-	; check the counter to make sure setup_crit has already been called
-	cmp	cs:counter,1					; has it been called?
-	je restore							; yes, restore environment
-	ret									; no, do nothing
-restore:
-	dec	cs:counter						; make counter 0
-	push	es							; save registers munged
-	push	ds							;
-	push	si							;
-	xor	ax,ax							; address interrupt vectors
-	mov	ds,ax							;
-	les	si,cs:old_handler				; pick up old handler into es:si
-	mov	bx,critical*4					; address critical vector
-	mov	[bx],si							; poke old handler back in
-	mov	2[bx],es						;
-	pop	si								; restore munged registers
-	pop	ds								;
-	pop	es								;
-	ret									; go away
-_restore_crit	endp
-
-_TEXT	ends
-	end
-	
SHAR_EOF
# FILE: testcrit.c
echo unpacking testcrit.c
sed s/^-// <<SHAR_EOF >testcrit.c
-#include <stdio.h>
-main(argc,argv)
-int argc;
-char **argv;
-{
-	FILE *foo = NULL;
-	static char fnamebuf[65];
-	/* attempt to open the file specified by the first command line
-    ** argument
-	*/
-	sprintf(fnamebuf,"A:%s",argv[1]);	/* build the file name */
-	for (;foo == NULL;)
-	{
-		if (-1 == setup_crit())	/* set up error handler */
-		{
-			/* if an error occurs, you will end up here */
-			fprintf(stderr,"Check to make sure a valid disk is drive A:\n");
-			fprintf(stderr,"and that the drive door is closed\n");
-			fprintf(stderr,"Hit return to continue\n");
-			getchar();
-		}
-		foo = fopen(fnamebuf,"r");
-	}
-	restore_crit();	/* turn off critical error handling */
-}
SHAR_EOF