woods@weird.com (Greg A. Woods) replied to my explanation of the 'f' prefixes in C file handling functions.
Well there's also the fact that the two uses of the 'f' prefix are really in fundamentally different levels of the API -- or depending on your point of view maybe even completely different APIs.
Fundamentally different? Some of them are in the kernel, and some of them are in the C stdio library, but from the point of view of your average UNIX programmer this is a distinction without a difference. open() and fopen() are *both* in POSIX. A UNIX look-alike could quite legitimately place fopen() in the kernel and open() in a library, just like it is on my Mac at home. (Yep, Think C layered the UNIX functions on top of the stdio functions, and they were layered on top of the MacOS ones.)
The stdio library functions that have an 'f' prefix operate on FILE structures. The system calls that have an 'f' prefix operate on kernel file descriptors. Yes, I know. That's pretty much what I said. That's the PROBLEM.
> It would make sense to have an fftruncate() function (truncate + > f prefix to get file descriptor + f prefix to get FILE*), but there is none. Actually, no, it doesn't -- but the reasons why depend on one having an intuitive understanding of the difference in API levels between the stdio library and raw system calls. The functions like fclose(), fread(), et al require a FILE* because they operate on the internal elements of a FILE structure. Non sequitur. The getchar(), getc(), putchar, putc(), printf(), and scanf() functions, amongst others, operate on the internal elements of a FILE structure, but don't have a FILE * argument. And the clearerr() and fileno() functions, amongst others, have a FILE * argument but don't have an "f" prefix. (fileno() begins with "f", but it's part of the word "file", not a prefix.)
Some stdio functions with a FILE* prefix have an f prefix. Some don't. A uniform rule appears to have been applied to *both* the (2) and (3s) functions: if you want a file handling function, and there is already one with the name you want, stick an 'f' in front. Anyone looking for a deeper level of meaning is looking for consistency that just isn't there.
By the way, I once wrote my own implementation of stdio, complete except for *scanf(), which I had no particular use for.
If you want to use a system call on a file opened by stdio you simply access the file descriptor from the (opaque) FILE structure using the fileno() function. For many things it's not that simple, but everyone who has an intuitive understanding of the difference in API levels &c &c already knows that.
I.e. there is no need to confuse stdio and system call naming conventions with ugly constructions like "fftruncate()" because it is already more properly done with ftruncate(fileno(FILE*)). I'm sorry, but this is precisely why it WOULD make sense to have fftruncate(), because ftruncate(fileno(fp)) just plain doesn't work. I *think* the following code will work, but of course it isn't portable to systems that don't have f{un,}lockfile():
int fftruncate(FILE *fp) { int r, e;
flockfile(fp); fflush(fp); r = ftruncate(fileno(fp)); e = errno; funlockfile(fp); errno = e; return r; }
Even this doesn't work if the last transput operation on the stream was an input operation; fflush() only makes the file position and the stream position agree when the last operation was not an input. In order to handle the input case, we'd have to seek back in the file to where the stream is (because of the buffered input), which is doable, *AND* we would have to mark the buffer as empty, which we can't do in a portable way. I could do it easily enough on any of the 32-bit UNIX systems I have access to, but it would not be portable, and it certainly wouldn't work with LFS 64-bit filesystems, because the vendors have taken care to hide the 64-bit filesystem FILE* record completely.
A little learning is a dangerous thing...
[ On Thursday, November 29, 2001 at 13:36:25 (+1300), Richard A. O'Keefe wrote: ]
Subject: Re: [BUG] file Truncate is it really busted?
woods@weird.com (Greg A. Woods) replied to my explanation of the 'f' prefixes in C file handling functions.
Well there's also the fact that the two uses of the 'f' prefix are really in fundamentally different levels of the API -- or depending on your point of view maybe even completely different APIs.
Fundamentally different?
Note I said "completely different", not "fundamentaly different" ;-)
Some of them are in the kernel, and some of them are in the C stdio library,
Well on most unix and unix-like systems both the kernel system call functions and the stdio functions are in libc. If the system call interfaces had ever been supplied separately it might have been in a library called libsys. Libc has become a mess of a whole slew of other very much unrelated APIs.
but from the point of view of your average UNIX programmer this is a distinction without a difference.
Perhaps that's true, though I don't really believe anyone with even a small amount of C programming experience could confuse the set of kernel interface functions that deal with file descriptors and the set of "higher" stdio functions that deal with the buffered file I/O using pointers to structures in which the library maintains internal state.
open() and fopen() are *both* in POSIX.
Yeah, sure, but that's really got nothing whatsoever to do with their differences..... POSIX defines the API for many unrelated sets of functions.
A UNIX look-alike could quite legitimately place fopen() in the kernel and open() in a library, just like it is on my Mac at home. (Yep, Think C layered the UNIX functions on top of the stdio functions, and they were layered on top of the MacOS ones.)
Hmmm... that's not really an important distinction in a single user system where the hardware doesn't properly isolate the data and code of the system from the data and code of user programs. The implementation of a difference between lower-level "kernel" file access functions and a layer of buffered I/O functions in a single address space, especially in C or something even lower-level, kind of makes all these distinctions artificial.
The stdio library functions that have an 'f' prefix operate on FILE structures. The system calls that have an 'f' prefix operate on kernel file descriptors.
Yes, I know. That's pretty much what I said. That's the PROBLEM.
I don't see how there can be a problem so long as you keep a separate view in your mind of the kernel API and the stdio API, and never attempt to mix the two without first gaining a deep understanding of the potential interactions of the particular mix you contemplate.
Since we're discussing this in a forum and in a context that relates to interfaces in an object-oriented system how about we simply declare that the lower level file-descriptor functions are in one class, and that the stdio functions are in another class (perhaps a richer class derived from the former one). The distiction is somewhat arbitrary from a C compiler's point of view, and it's only if you use the Unix manual chapters as a way of separating one class definition from another that you'll see a literal distinction.
The getchar(), getc(), putchar, putc(), printf(), and scanf() functions, amongst others, operate on the internal elements of a FILE structure, but don't have a FILE * argument.
Ah, now you're taking the analogy backwards and far too far.
It's irrelevant whether there are also stdio functions which expect a FILE* parameter but which do not have an 'f' prefixing their name.
You're perhaps confusing naming conventions between two unrelated APIs. There's no direct connection between the 'f' prefix and the fact that the function is a stdio function. If you read again very carefully exactly what I wrote above, and don't go on and read more into it than I've written, you'll also see that what I said was _not_ an attempt to imply that all stdio functions taking a FILE* parameter have an 'f' prefix either (or correspondingly that not all system calls expecting a file descriptor parameter will have an 'f' prefix either)! ;-)
Some stdio functions with a FILE* prefix have an f prefix. Some don't.
Exactly! ;-)
A uniform rule appears to have been applied to *both* the (2) and (3s) functions: if you want a file handling function, and there is already one with the name you want, stick an 'f' in front.
Yup, that's proabably a good enough analysis of how the names might have been created.
Anyone looking for a deeper level of meaning is looking for consistency that just isn't there.
Indeed! ;-)
I'm sorry, but this is precisely why it WOULD make sense to have fftruncate(), because ftruncate(fileno(fp)) just plain doesn't work. I *think* the following code will work, but of course it isn't portable to systems that don't have f{un,}lockfile():
Ah, I see -- you have some rather extravagant expectations! :-)
'ftruncate(fileno(fp))' does in fact work, perfectly even -- it just doesn't leave the FILE* pointed to by 'fp' in any kind of useful state, and expecting it to do so is perhaps unrealistic since you're mixing too many operations between different levels.
Any programmer wanting to use your fftruncate() would I think be creating something more complex than he apparently understands.
Generally speaking any experienced Unix programmer will endeavour to never mix stdio operations on a given file with low-level file descriptor I/O operations on the same file. Perhaps this strange example of ftruncate(fileno(fp) is a strange exception that might have some use in a program that may have been handed an already open file descriptor and thus might not know its pathname and so might use this construct to truncate the file. The result of course will generally make the FILE* unusable unless the programmer has taken care to implement something like the mechanism you suggested.
squeak-dev@lists.squeakfoundation.org