Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!swrinde!zaphod.mps.ohio-state.edu!think.com!mintaka!bloom-picayune.mit.edu!news From: scs@adam.mit.edu (Steve Summit) Newsgroups: comp.lang.c Subject: Re: perror/strerror after fopen (was FILE *fp[];) Keywords: errno perror stdio Message-ID: <1990Dec6.050817.9741@athena.mit.edu> Date: 6 Dec 90 05:08:17 GMT References: <1990Nov28.152146.19560@ssd.kodak.com> <14603@smoke.brl.mil> <28078@mimsy.umd.edu> <1990Nov29.164552.452@Neon.Stanford.EDU> <4573@oasys.dt.navy.mil> <14617@smoke.brl.mil> Sender: news@athena.mit.edu (News system) Reply-To: scs@adam.mit.edu (Steve Summit) Organization: Thermal Technologies, Inc. Lines: 254 In article <14603@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes: >In article <1990Nov28.152146.19560@ssd.kodak.com> weimer@ssd.kodak.com (Gary Weimer) writes: >> if ((fp[i]=fopen(file_name[i],"r+")) <= 0) >> perror("Error opening file"); > >Please don't do this; on UNIX, perror() will report the reason for the last >SYSTEM CALL failure, not the reason for failure of a library routine such as >fopen(). Sometimes there is a close relation between these but at other >times there is not, leading to such absurd diagnostics as "Error opening >file: not a tty". I have to disagree with Doug here. The "close relation" is much more likely than the "absurd diagnostic." I find it more absurd, and downright frustrating to boot, when a program prints Error opening file or Error opening file /usr/lib/blort/piffle with no indication whatsoever of what the problem was. A generally agreed upon set of useful information which should be included in any error message is: 1. name of program reporting error 2. pathname (or other pertinent argument) to failing system call (or library routine) 3. perror text, if appropriate 4. file name and line number of source or other input file being read, if applicable Even in the presence of good standards, programming is still a pragmatic activity, and it is much more important that error messages include pertinent information (such as the above) most of the time than that they are strictly conforming or never print an occasional oddity such as "not a typewriter." There are other ways to reduce or eliminate occasional incorrect perror messages; read on. In article peter@ficc.ferranti.com (Peter da Silva) writes: >In every case where I've got the source to a program and have figured out >where something like that came from it's been one of the two following >cases: > if(!(fp = fopen(...))) { > fprintf(stderr, "%s: ", argv[0]); > perror(...); > } >(where the fprintf stomped on errno when deciding what buffering to do) or: [other case deleted] This is a highly unfortunate case. I believe that stderr should never be buffered; I'll discuss the tradeoffs in another article. I've written code like this, thinking that the only possible error in the fprintf call was unwritability of stderr, in which case the perror wasn't likely to do much good anyway. Unfortunately, ANSI X3.159 section 4.9.3 says that "the standard error stream is not fully buffered;" which means that it can be line buffered, which means that Peter's objection is legitimate. (Technically, of course, X3.159 allows fprintf to step on errno for any reason, buffering decisions or otherwise.) In any case, a much better alternative to fprintf/perror, which I have used for some time in serious programs, appears later in this article. In article <28078@mimsy.umd.edu> chris@mimsy.umd.edu (Chris Torek) writes: >Some of us regard this as a bug, rather than a feature. >To make the best of it, the 4.4BSD C library >guarantees that errno *is* meaningful after an error. As Posix requires; see below. >It is, of course, true that errno has no portable meaning after an >fopen failure. Still, you could print something like: > (void) fprintf(stderr, > "%s: error opening file %s for read-update\n" > "\tlast system error was %s\n", > progname, file_name[i], strerror(errno)); >This gets all the facts across, and is helpful when trying to figure >out *why* the open failed. Which I claim is a requirement (of good code). The "last system error" dodge is useful if you're seriously worried about fopen _not_ setting errno appropriately on failure. In article <4573@oasys.dt.navy.mil> stuart@oasys.dt.navy.mil (Stuart Pearlman) writes: >Is perror() specified in the ansi C standard? I avoid using perror() >after library routines such as fopen() because they can fail for other >reasons besides the system calls they make failing. Are any of the >library functions specified in the standard guaranteed to set errno? >(Is errno even guaranteed to exist?) What about posix 1003.1? Does >it specify what functions you can call perror() after? In my experience, the only fopen failure not accurately reflected by errno is insufficient FILE structure availability. Once upon a time, there were typically 20 low-level, int fd's available, and 20 FILEs, so this essentially never happened. Lately, some systems have increased the availability of int fd's without allocating more FILEs; this is a bug, or at least a quality-of- implementation issue. Modern stdio implementations avoid fixed FILE tables, replacing (or supplanting) them with malloc'ed FILEs; this makes running out of FILEs quite difficult (unless, of course, malloc runs out of memory). There is no particular reason why fopen couldn't set errno to EMFILE ("too many open files"). fopen implementations often do not, partly because of a general reluctance to play with errno at all (preferring always leaving it to contain the value from the last failed syscall, which is usually appropriate), and partly because EMFILE is theoretically supposed to mean that it is low-level, int fd's that have been used up (by various system calls), and not FILE structures (by stdio). However, the "implementation defined" clauses with which every description of errno is sprinkled mean that having fopen set EMFILE should not strictly be nonconformant. In article <14617@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes: >perror() is required for ANSI C conformance. errno is required, but >only a few of the standard functions (mainly math functions) are >required to set errno (upon certain conditions). IEEE Std 1003.1 >specifies that errno is to be set for error returns from a large >number of functions (mainly those normally implemented on UNIX as >genuine system calls). It's unfortunate that X3.159 can't say more about errno and stdio, but being a C language standard it is generally extremely reluctant to address operating system specifics. I hope that this situation (as exemplified in Doug's earlier recommendation) doesn't discourage people from using errno and/or perror with stdio, since it can be made to work (and proven so; read on). It would be extremely unfortunate if people concluded that they could only reliably use errno and/or perror after the low-level, Unixy (and Posixy) calls such as open, read, and write, since it is highly recommended for portability's sake (not to mention performance) that fopen and the rest of stdio be used instead. All we have to do is guarantee that fopen and the rest of the stdio calls leave errno set appropriately after errors. As Dave Eisen has already pointed out, this is not hard for a stdio implementor to do. (I can confirm this, having implemented stdio a number of times myself.) We can ask that implementors do this as a part of good quality-of-implementation, but Posix is on our side: section 8.2.3.11 (Error Reporting) says that If any of the functions above return an error indication caused by a condition that would be detected by the corresponding underlying functions listed above, the value returned in errno shall be the one provided for ["by"?] the underlying function. (To be sure, this is comp.lang.c, not comp.std.unix; so invoking Posix is not strictly playing fair, and people worrying about strict conformance may not be able to make use of this. On the other hand, I don't think calling perror or the like after fopen breaks strict conformance, it just isn't guaranteed to print anything meaningful.) Assuming that "the functions above" refers to those listed in sections 8.2.3.1 through 8.2.3.10, they include all of the stdio functions we might be concerned about; i.e. the answer to Stuart Pearlman's question "Does [posix 1003.1] specify what functions you can call perror() after?" is "yes; most of them." (What section 8.2 is all about is "importing" the stdio routines, "as defined by the C Standard," with amendments and additions to support their "interactions with other functions defined by this [Posix] Standard." The "corresponding underlying functions listed above" are the low-level, int-fd-using calls (open, read, write, etc.) upon which the stdio routines are based (although Posix does not specifically "require that there be any relation between the implementations of the stream function and its underlying functions"). Again, sections 8.2.3.1 through 8.2.3.10 spell out the details. To sum up, I'm satisfied that, for a Posix-compliant implementation (or, for that matter, any high-quality C implementation whether or not it's Posix), errno will be meaningful after stdio errors. (To repeat, even if there are systems for which this is not true, and to which code I write might be ported, I'll take that chance. I'd rather generate useful error messages on most systems than avoid potentially misleading messages on a few systems by dumbing- down the messages on all systems.) The remaining question is, how to generate useful messages (incorporating all four pieces of information listed above) conveniently without introducing a potentially errno-destructive fprintf call before the call to perror. A simple perror replacement, which I usually call errorp, suffices. Its usage is exemplified by extern void errorp(char *, ...); if((fp = fopen(filename, "r")) == NULL) { errorp("%s: can't open %s", progname, filename); return ERROR; } Like perror, errorp prints its arguments followed by a colon and a system-dependent error message derived from errno. The advantage is that errorp's arguments are those of printf: a format string and any number of additional arguments for filling in the %'s. (Of course, unlike printf, errorp's output goes to stderr.) A public domain, portable implementation of errorp is included following my signature. (It could obviously be trivially rewritten to do something like Chris's "last system error was" suggestion.) Steve Summit scs@adam.mit.edu ----------------------------errorp.c----------------------------- /* * perror replacement. * Prints its first argument a la printf (but to stderr), * interpolating additional arguments via % format specifiers, * and followed by ": ", a system-specific error message * derived from errno, and a newline. * * Placed in the Public Domain; use it in good health and * without restriction. * * scs 12/5/90 */ #include #include #include /* or extern int errno; */ #include /* or extern char *strerror(); */ /* VARARGS1 */ void errorp(char *fmt, ...) { int saverrno = errno; va_list argp; if(fmt != NULL && *fmt != '\0') { va_start(argp, fmt); vfprintf(stderr, fmt, argp); va_end(argp); fprintf(stderr, ": "); } fprintf(stderr, "%s\n", strerror(saverrno)); } -------------------------end of errorp.c------------------------- If you don't have , vfprintf, or strerror, I can supply versions that will probably work on your system. The FAQ list includes notes on retrofitting stdarg-using code to use . Brought to you by Super Global Mega Corp .com