La gestione degli errori 

Le librerie della maggior parte dei compilatori implementano una modalità standard, derivata da Unix, di gestione degli errori restituiti dai servizi DOS utilizzati dalle funzioni di libreria. Concentriamo la nostra attenzione su alcune variabili globali, dichiarate nel file STDLIB.H
extern char *sys_errlist[];
extern int errno;
extern int _doserrno;
L'array sys_errlist contiene i puntatori alle stringhe che descrivono i diversi errori[1]. Vi è una funzione dedicata alla gestione dei messaggi d'errore, perror(), che richiede una stringa quale parametro, la scrive sullo standard error seguita da ":", dalla stringa che costituisce l'elemento di sys_errlist corrispondente all'errore verificatosi e dal carattere "\n". L'utilizzo di perror() non presenta difficoltà: 
#include <stdio.h>      // prototipi di fopen() e perror()

#define   PRG_NAME     "MYPROG"
....
    if(!(stream = fopen(filename,"r"))) {
        perror(PRG_NAME);
        return(0);
    }
Nell'ipotesi che filename contenga un pathname errato, fopen() non riesce ad aprire il file e restituisce NULL; la perror() produce il seguente output: 
MYPROG: path not found
Come riesce perror() a individuare il messaggio? Semplice: si basa sul valore assunto dalla variabile errno, che può essere validamente utilizzata come indice. In altre parole, sys_errlist[errno] è la stringa che descrive l'errore. Dietro le quinte, tutte le funzioni di libreria che per svolgere il proprio compito utilizzano servizi DOS[2], se ricevono da questo un codice di errore lo passano ad una funzione (generalmente non documentata), la __IOerror() [3], che lo assegna a _doserrno, la quale contiene perciò il codice di errore DOS, e, tramite un'apposita tabella di conversione, ricava il valore appropriato da assegnare ad errno

Può essere utile chiamare direttamente la __IOerror() nei propri sorgenti, soprattutto quando si scrivano funzioni destinate ad essere inserite in una libreria: risulta è immediato implementare la gestione degli errori in modo del tutto coerente con le librerie standard del compilatore utilizzato. E' sufficiente dichiarare nel sorgente il prototipo di __IOerror()
int pascal __IOerror(int dosErr);
La parola riservata pascal ha lo scopo di modificare lo stile di chiamata della funzione: a tutte le funzioni dichiarate pascal, secondo lo standard di questo linguaggio, i parametri attuali sono passati in ordine diretto, cioè dal primo a sinistra all'ultimo a destra, e non viceversa, secondo quanto previsto invece, per default, dalle regole del C. Lo scopo è ottenere una chiamata più efficiente; la perdita della possibilità di gestire un numero variabile di parametri, in questo caso, non ha alcuna importanza, in quanto __IOerror() ne riceve uno solo, dosErr, che rappresenta il codice di errore restituito dalla routine DOS (solitamente nel registro AX). La __IOerror() restituisce sempre ­1

Vediamo un esempio. Vogliamo scrivere una funzione in grado di dirci, in un ambiente di rete, se un drive è locale o remoto (per ulteriori notizie sulla gestione dei files in ambiente condiviso vedere il capitolo dedicato allo I/O): allo scopo si può utilizzare l'int 21h, servizio 44h, subfunzione 09h: 

INT 21H, SERV. 44H, SUBF. 09H: DETERMINA SE IL DRIVE E' REMOTO 
Input AX 

BL 
4409h 

Drive (00h = default, 01h = A:, 02h = B:, etc.) 
Output DX Se CarryFlag = 0, il bit 12 di DX a 1 indica che il drive è remoto; Se CarryFlag = 1 allora AX contiene il codice di errore. 
Ed ecco il listato: 
/********************

    BARNINGA_Z! - 1991

    ISREMOTE.C - isDriveRemote()

    int cdecl isDriveRemote(int driveNum);
    int driveNum;   drive da testare (0 = default, 1 = A:, ...)
    Restituisce: 0 = drive locale
                 1 = drive remoto
                -1 = errore (errno e _doserrno gestite come standard)

    COMPILABILE CON BORLAND C++ 3.0

        bcc -O -d -c -mx isremote.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/
#pragma  inline

#include <dos.h>        // per geninterrupt()

int pascal __IOerror(int dosErr);

int cdecl isDriveRemote(int driveNum)
{
    _BL = (char)driveNum;
    _AX = 0x4409;
    geninterrupt(0x21);
    asm jc JOB_ERROR;      // Se CarryFlag = 1 c'e' stato un errore
    asm and dx,01000000000000b;
    asm mov ax,dx;
    return(_AX);
JOB_ERROR:
    return(__IOerror(_AX));
}
La funzione isDriveRemote() restituisce ­1 in caso di errore, 0 se il drive è locale, 1 se è remoto. Il parametro è un intero che esprime il drive su cui effettuare il test (0 indica il drive di default, 1 indica il drive A:, 2 il drive B:, etc.). Quando la funzione restituisce ­1 è possibile chiamare perror() per scrivere su stderr la descrizione dell'errore verificatosi: 
    if(isDriveRemote(0) == -1)
        perror("What a pity");
E' immediato constatare che il comportamento di isDriveRemote() è conforme a quello delle altre funzioni di libreria che interagiscono con il DOS. Per altri esempi di utilizzo della __IOerror() in funzioni di libreria vedere le funzioni per la gestione della command line

OK, andiamo avanti a leggere il libro... 

Non ci ho capito niente! Ricominciamo...