                           C ERROR HANDLING


                            Matthew Probert

                           Servile  Software

When a system error occurs within a program, for example when an 
attempt to open a file fails, it is helpful to the program's user to 
display a message reporting the failure. Equally it is useful to the 
program's developer to know why the error occurred, or at least as 
much about it as possible. To this end the ANSI standard on C 
describes a function, perror(), which has the prototype; 

    void perror(const char *s); 

and is used to display an error message. The program's own prefixed 
error message is passed to perror() as the string parameter. This 
error message is displayed by perror() followed by the host's system 
error separated by a colon. The following example illustrates a use 
for perror(); 

#include <stdio.h> 

void main()
{ 
    FILE *fp; 
    char fname[] = "none.xyz";

    fp = fopen(fname,"r"); 

    if(!fp) 
        perror(fname); 
    return; 
} 

If the fopen() operation fails, a message similar to; 

    none.xyz: No such file or directory 

is displayed. 

You should note, perror() sends its output to the predefined stream 
'stderr', which is usually the host computer's display unit. 

perror() finds its message from the host computer via the global 
variable 'errno' that is set by most, but not all system functions. 

Unpleasant errors might justify the use of abort(). abort() is a 
function that terminates the running program with a message; 

    "Abnormal program termination" 

and returns an exit code of 3 to the parent process or operating system. 


Critical Error Handling With The IBM PC AND DOS

The IBM PC DOS operating system provides a user amendable critical 
error handling function. This function is usually discovered by 
attempting to write to a disk drive that does not have a disk in it, 
in which case the familiar; 

Not ready error writing drive A
Abort Retry Ignore?

Message is displayed on the screen. Fine when it occurs from within a 
DOS program, not so fine from within your own program! 

The following example program shows how to redirect the DOS critical 
error interrupt to your own function; 


/* DOS critical error handler test */

#include <stdio.h>
#include <dos.h>

void interrupt new_int();
void interrupt (*old_int)();

char status;

main()
{
    FILE *fp;

    old_int = getvect(0x24);

    /* Set critical error handler to my function */
    setvect(0x24,new_int);

    /* Generate an error by not having a disc in drive A */
    fp = fopen("a:\\data.txt","w+");

    /* Display error status returned */
    printf("\nStatus ==  %d",status);

}

void interrupt new_int()
{
    /* set global error code */
    status = _DI;

    /* ignore error and return */
    _AL = 0;
}

When the DOS critical error interrupt is called, a status message is 
passed in the low byte of the DI register. This message is one of; 

Code    Meaning

00      Write-protect error
01      Unknown unit
02      Drive not ready
03      Unknown command
04      Data error, bad CRC
05      Bad request structure length
06      Seek error
07      Unknown media type
08      Sector not found
09      Printer out of paper
0A      Write error
0B      Read error
0C      General failure

Your critical error interrupt handler can transfer this status message 
into a global variable, and then set the result message held in 
register AL to one of; 

Code    Action

00      Ignore error
01      Retry
02      Terminate program
03      Fail (DOS 3.3 and above)


If you choose to set AL to 02, terminate program, you should ensure 
ALL files are closed first since DOS will terminate the program 
abruptly, leaving files open and memory allocated, not a pretty state 
to be in! 

The example program shown returns an ignore status from the critical 
error interrupt, and leaves the checking of any errors to the program 
itself. So, in this example after the call to fopen() we could check 
the return value in fp, and if it reveals an error (NULL in this case) 
we could then check the global variable status and act accordingly, 
perhaps displaying a polite message to the user to put a disk in the 
floppy drive and ensure that the door is closed. 

The following is a practical function for checking whether a specified 
disc drive can be accessed. It should be used with the earlier 
critical error handler and global variable 'status'. 


int DISCOK(int drive)
{
    /* Checks for whether a disc can be read */
    /* Returns false (zero) on error */
    /* Thus if(!DISCOK(drive)) */
    /*          error();  */

    unsigned char buffer[25];
    
    /* Assume okay */
    status = 0;

    /* If already logged to disc, return okay */
    if ('A' + drive == diry[0])
        return(1);

    /* Attempt to read disc */
    memset(buffer,0,20);
    sprintf(buffer,"%c:$$$.$$$",'A'+drive);
    _open(buffer,O_RDONLY);

    /* Check critical error handler status */
    if (status == 0)
        return(1);

    /* Disc cannot be read */
    return(0);
}

