The Old errno Value

The original method that the programmer used to gain access to the error code was to declare an external reference to the int value errno:

extern int errno;

When an attempt to open a file fails, a program can simply query the external variable errno to determine the reason for the failure. The following example shows how the make(1) command could be written using the old errno method:

#include <errno.h>                         /* Defines ENOENT */
extern int errno;                          /* Error code */
int fd;                                    /* File descriptor */

/* Attempt to open makefile */
if ( (fd = open("makefile",O_RDONLY)) == -1 ) {  /* Fail to open? */
    if ( errno == ENOENT )                 /* File does not exist? */
        fd = open("Makefile",O_RDONLY);    /* No, so try Makefile instead */
}

if ( fd == -1 ) {                          /* Did either open(2) fail? */
    /* Yes, report the open failure... */
    ...
}  else {
    /* makefile or Makefile is open on file unit fd */
}

The example shows that if makefile fails to open, with the error ENOENT, Makefile is opened. The example also illustrates that the reason for the error is never returned directly by the function, nor is it returned by an argument reference. Instead, using this older external variable methodology, the programmer queries this value when a function returns a failure indication.

Note

ENOENT means No Such File or Directory. This error code indicates that the requested file system object was not found (does not exist).


Referencing Error Codes by Name

Using the errno external variable convention for errors required that a series of error codes be agreed on in advance. Since numeric error codes might vary on different UNIX platforms, a set of C macros is defined to refer to these error codes (for example, error code ENOMSG is 83 for FreeBSD 3.4, 35 for HPUX, and 42 for Linux). The symbolic macro names can be used to refer to the same error codes on different UNIX platforms. These C macros are defined in the include file errno.h.

#include <errno.h>

Using symbolic macro references for error codes is important, since it allows your C programs to be portable to other UNIX platforms. Only a compile is required to reference the correct numeric value for these codes on a given platform.

UNIX errno codes are non-zero values and usually start at 1 and work up. Zero is sometimes used to indicate "no error" (this convention is used in rare cases with the functions strtol(3), strtoul(3), and strtod(3), for example).

Applying errno Correctly

There is a temptation for novice programmers to use the errno value to test for success. However, it is incorrect to do so because the purpose of the errno value is to be a central place to which to post error codes. As a general policy, never expect the errno value to be cleared to zero for success. Only errors (failures) are posted to this variable.

There are special situations that require you to clear the errno value to zero before making a function call (some examples are strtol(3), strtoul(3), strtod(3), and getpwent(3)). This is necessary because the function will not clear the errno value to zero when success is returned. Under these special circumstances, if the errno value remains as the value 0 (presuming it was cleared prior to the call), then this indicates a successful return. This technique must only be applied to specially indicated functions. This technique cannot be extended for use on other functions. The special cases will be carefully indicated within this book.

Warning

The errno value is updated by system and library functions only after an error indication is returned. This value is never cleared to zero for a successful operation. Always test the function's return value to determine if an error has been indicated. If so, then the value of errno has meaning.


Testing for Failure with Integer Return Values

Earlier it was shown how functions, which return integer results, use the value of -1 to indicate that a call has failed. The following open(2) example indicates when the value of errno is valid:

extern int errno;              /* Old way of gaining access to errno */
int fd;                        /* File descriptor */

if ( (fd = open("makefile",O_RDONLY)) == -1 ) {
    /* Failed: errno holds a valid error code */
    ...
}  else {
    /* Success: fd holds file descriptor, and errno is meaningless here */
    ...
}

If the open(2) call returns a failed indication by a return value of -1, then we know that the error code will have been posted to the integer errno.

Testing for Failure with Pointer Results

Other functions that report their failure by returning a null pointer can identify when to use errno as follows:

FILE *fp = fopen("makefile","r");          /* Attempt to open makefile */

if ( fp == NULL ) {                         /* Failed? */
    /* Open Failed: the value of errno holds an error code */
    ...
}  else {
    /* Open succeeded: the value of errno has no meaningful value */
    ...
}

Here the fopen(3) call indicates failure by returning a null pointer (which matches the C macro value NULL). Again, only when it is determined that the function has returned a failure indication is the value errno valid and does it contain an error code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.103.8