/* fEopen.c -- Copyright 1988,1992,1995 Liam R. E. Quin. * All Rights Reserved. * This code is NOT in the public domain. * See the file COPYRIGHT for full details. */ /* $Id: fEopen.c,v 1.10 2001/05/31 03:50:13 liam Exp $ * Error checking versions of fopen() and fclose(), taken from Liam Quin's * unreleased error handling library and stripped down somewhat. */ #include "globals.h" #include "error.h" #include #include #ifdef HAVE_STRING_H # include #else # include #endif #ifdef HAVE_SYSV_FCNTL_H # include /* for fcntl.h on some systems */ # include /* needed for fcntl.h on some systems */ #endif #ifdef HAVE_FCNTL_H # include /* for O_RDONLY etc */ #endif #ifdef HAVE_STDLIB_H # include #else # include #endif #include "emalloc.h" #include "lqutil.h" /** error-checking fopen and fclose... ** ** Contents: ** ** ** FILE *LQU_fEopen(int Severity; char *Name; char *What; char *Mode); ** ** void fEclose(int severity; FILE *fp; char *Name; char *What); **/ PRIVATE char *FindMissingPart( # ifdef HAVE_PROTO CONST char *BadPath # endif /*HAVE_PROTO*/ ); PRIVATE void Diagnose( # ifdef HAVE_PROTO int Severity, CONST char *Name, CONST char *What, int Errno, int Flags # endif /*HAVE_PROTO*/ ); /* * LQU_Eopen * Utilities/Files * * Opens the named file with the given Flags and Modes, as per open(2). * If the open fails, an error is generated with the given severity, * and including both the file name (Name) and description (What). * A diagnosis of the problem is also generated, using errno and * examining the filename to determine if (for example) a component * of the given path was not a directory. This generally produces * much more specific, and hence, clearer, error messages than using * perror(3) would give. * * a valid file descriptor on success, or -1 if the file couldn't * be opened. * If E_FATAL was given, LQU_Eopen does not return after an error. * * LQU_Eopen(E_FATAL, "foo.c", "input C source", O_RDONLY, 0) * * */ API int LQU_Eopen(Severity, Name, What, Flags, Modes) int Severity; CONST char *Name; CONST char *What; int Flags; int Modes; { int Result; if (!Name) { char *Format = "couldn't open \"%s\" (%s)"; if (What && *What) { Error(Severity|E_INTERNAL|E_BUG, Format, "[NULL name]", What ); } else { Error(Severity|E_INTERNAL|E_BUG, "LQU_Eopen(%d, 0, 0, %d, %d) illegal", Severity, Flags, Modes ); } return -1; } /* On some Unix systems, any attempt to open a directory for reading * produce a message on the console... * so we check for it first. * This means you couldn't build readdir() on top of this routine. */ if (LQU_IsDir(Name)) { Error(Severity, "file \"%s\" (%s) is a directory (not opened)", Name, What ); return -1; } errno = 0; Result = open(Name, Flags, Modes); if (Result >= 0) { return Result; } else { Diagnose( Severity, Name, What, errno, Flags ); return -1; } } PRIVATE int StdioModeToFlags(theMode) char *theMode; { int Flags = O_RDONLY; register char *p; for (p = theMode; *p; p++) { switch (*p) { case 'w': case 'W': Flags |= O_WRONLY|O_CREAT|O_TRUNC; break; case 'a': case 'A': Flags |= O_APPEND; Flags &= ~O_TRUNC; break; case '+': Flags |= O_RDWR|O_CREAT; Flags &= ~O_TRUNC; break; } } return Flags; } /* * LQU_fEopen * Utilities/Files * * This is the stdio equivalent of LQU_Eopen. * * a freshly opened file pointer (FILE *) on success, or * NULL if the file couldn't be opened. * If E_FATAL was given, LQU_fEopen does not return after an error. * * Warns if the file can't be opened. * */ API FILE * LQU_fEopen(Severity, Name, What, Mode) int Severity; CONST char *Name; CONST char *What; CONST char *Mode; { FILE *Result; char *Format = "couldn't open \"%s\" (%s)"; if (!Name) { if (What && *What) { Error(Severity|E_INTERNAL, Format, "[NULL name]", What); } else { Error(Severity|E_INTERNAL, "LQU_fEopen(%d, 0, 0, %s) illegal", Severity, (Mode ? ((*Mode)? Mode : "\"\"" ) : "0")); } return (FILE *) 0; } if (!Mode || !*Mode) { if (!What || !*What) { Error(Severity|E_INTERNAL, "LQU_fEopen: couldn't open \"%s\" (%s), as no Mode was given", Name, What ); } else { Error(Severity|E_INTERNAL, "LQU_fEopen: couldn't open \"%s\", since no Mode was given", Name ); } return (FILE *) 0; } if (LQU_IsDir(Name)) { Error(Severity, "file \"%s\" (%s) is a directory (not opened)", Name, What ); return (FILE *) 0; } errno = 0; if ((Result = fopen(Name, Mode)) != (FILE *) 0) { return Result; } else { Diagnose( Severity, Name, What, errno, StdioModeToFlags(Mode) ); } return (FILE *) 0; } PRIVATE char * OpenFlagsToString(Flags) int Flags; { if (Flags == O_RDONLY) { return "O_RDONLY"; } else if (Flags & O_RDWR) { return "O_RDWR"; } else if (Flags == (O_CREAT|O_RDWR)) { return "O_CREAT | O_CREAT"; } { static char tmp[30]; (void) sprintf(tmp, "0%d", Flags); return tmp; } } PRIVATE void Diagnose(Severity, Name, What, Errno, Flags) int Severity; CONST char *Name; CONST char *What; int Errno; int Flags; { char *Problem; switch (Errno) { case EFAULT: Error(Severity|E_BUG, "open called with invalid file-name (EFAULT)" ); /* NOTE: don't print "what" out in this case lest it's broken too */ break; case E2BIG: #ifdef ENAMETOOLONG case ENAMETOOLONG: #endif Error(Severity|E_SYS, "Can't open file \"%1024.1024s...\" (%s)", Name, What ); break; case EPERM: case ENOENT: case EACCES: case ENOTDIR: if ((Problem = FindMissingPart(Name)) != (char *) NULL) { if (STREQ(Problem, Name)) { Error(Severity, "\"%s\" (%s) not opened", Name, What ); } else { Error(Severity|E_MULTILINE, "\"%s\" (%s) not opened", Name, What ); Severity &= (~E_SYS); if (LQU_IsFile(Problem)) { Error(Severity|E_LASTLINE, "\"%s\" is a file, not a directory", Problem ); } else { Error(Severity|E_LASTLINE, "\"%s\" is not a directory", Problem ); } } (void) free(Problem); /* NOTE: not efree() */ break; } else { /* nothing was missing, the file simply isn't there */ if (Flags == O_RDONLY) { /* usual case */ Error(Severity, "%s: %s not found", Name, What ); } else { Error(Severity, "file \"%s\" (%s) not found (open mode %s)", Name, What, OpenFlagsToString(Flags) ); } } } /* if we get here, there was another problem opening the file */ Error(Severity|E_SYS, "can't open file \"%s\" (%s) Flags %s", Name, What, OpenFlagsToString(Flags) ); } PRIVATE char * NextSlash(String) register char *String; { for (; *String; String++) { if (*String == '/') { break; } } return String; } PRIVATE char * FindMissingPart(BadPath) CONST char *BadPath; { int e = errno; char *Path; register CONST char *End; /* We must look to find out which component of the path is missing. * Note that the parameter BadPath might be read-only... */ if (!BadPath || !*BadPath) { return (char *) NULL; } /* don't use emalloc, because we are called in the middle of * gnerating an error message, and we want the _real_ message * to be produced, not * "out of memory trying to produce a clearer error message" ! */ Path = malloc(strlen(BadPath) + 1); if (Path == (char *) NULL) { errno = e; return (char *) NULL; } /* Loop invariants: * * Precondition: * * Invariant: * *End == '/' * Postcondition: * *End == '\0' */ End = BadPath; while (*End) { int n; End = NextSlash(&End[1]); if (!*End) { break; } n = End - BadPath; (void) strncpy(Path, BadPath, n); Path[n] = '\0'; if (!LQU_IsDir(Path)) { return Path; } } (void) strcpy(Path, BadPath); errno = e; return Path; } /* * LQU_fEclose * Utilities/Files * * Closes the given file descriptor, printing error messages if * necessary. * * There is no return value. * If E_FATAL was given, LQU_fEclose does not return after an error. * */ API void LQU_fEclose(Severity, fp, Name, What) int Severity; FILE *fp; CONST char *Name; CONST char *What; { int Val; if (!Name) { if (fp == stdin) Name = "standard input"; else if (fp == stdout) Name = "standard output"; else if (fp == stderr) Name = "standard error"; } if (!fp) { Error(Severity|E_INTERNAL|E_FATAL, "LQU_fEclose %s [%s] with NULL fp forbidden", (What ? What : "stream"), (Name ? Name : " (unnamed) ") ); } else if (!Name) { Error(Severity|E_INTERNAL|E_FATAL, "LQU_fEclose %s with NULL Name forbidden", What ? What : "[anonymous stdio FILE]" ); } if ((Val = fclose(fp)) != 0) { Error(Severity|E_SYS, "%s: error closing %s, close --> %d", Name, What ? What : "file", Val ); } }