# ls -altr
total 540
-rw-r--r-- 1 root root 22606 Apr 12 1990 NOTES
-rw-r--r-- 1 root root 172645 Apr 12 1990 LIB
-rw-r--r-- 1 root root 102349 Apr 12 1990 APP
-rw-r--r-- 1 root root 224037 Apr 12 1990 C
Upload it to the Internet Archive! :D
What a mic drop. That must have been a fun ride from then until now! Would love to hear some of your battle stories.
Those early Linux distros borrowed a lot from SunOS (Solaris 1), so it was easy to adapt between work/home.
Without an indicator in `struct FILE` of whether the last operation was a read or a write, the stdio implementation has no way to detect the problem and correct the situation by automatically flushing and resetting the buffer, say. An alternative would be to have two buffers, naturally. But you can see how a pre-update version could be trivially made to support update modes without adding a second buffer or automatic buffer flushing. And that's almost certainly what happened when update mode was added. My guess is someone got bitten by that and then the maintainer decided to just document the problem rather than fix it, probably because by then fixing the problem was hard.
To avoid the extra check, you don't actually need two buffers, just separate buffer pointers for reading and writing. (This is probably how most libcs implement this today.) I suppose memory was really scarce back then.
I don't think an implementation with two active, non-empty buffers is all that useful because you can't tell which buffer's progress should be used for the file pointer adjustment in ftell.
> I don't think an implementation with two active, non-empty buffers is all that useful because you can't tell which buffer's progress should be used for the file pointer adjustment in ftell.
Oh interesting. The other problem is that two buffers reduces memory utilization.
https://pubs.opengroup.org/onlinepubs/9699919799/functions/w...
After a write() to a regular file has successfully returned:
Any successful read() from each byte position in the file that was modified by that write shall return the data specified by the write() for that position until such byte positions are again modified.
Though, as I offer that thought, the divergence between C and the system calls is definitely curious.
The article lists three libcs (Open Watcom, Microsoft Visual C++ 6.0, IBM C/C++ 3.6 for Windows) from the good old times. Does the emulator link to Open Watcom, i.e., does it emulate DOS on machines about as old as DOS itself? What's the point here?
The bug can pop up in any C program using stdio that assumes it's fine to do `fread` followed immediately by `fwrite`. The spec forbids this. To make matters more confusing, this behavior does _not_ seem to be in modern libc implementations. Or at least, it works on my machine. I bet modern implementations are able to be more sane about managing different buffers for reading and writing.
The original COMMAND.COM from MS-DOS probably did not have this problem, since at least in some versions it was written in assembly[2]. Even for a shell written in C, the fix is pretty easy: seek the file before switching between reading/writing.
The title of this post is confusing, since it clearly _is_ a bug somewhere. But I think the author was excited about possibly finding a bug in libc:
> Sitting down with a debugger, I could just see how the C run-time library (Open Watcom) could be fixed to avoid this problem.
[1] Here's DOSBox, for example: https://github.com/dosbox-staging/dosbox-staging/blob/main/s...
[2] MS-DOS 4.0: https://github.com/microsoft/MS-DOS/tree/main/v4.0/src/CMD/C...
C:\> echo AB> foo.txt
C:\> echo CD>> foo.txt
C:\> type foo.txt
AB
CD
(Note that echo adds a newline, same as on real DOS, or even UNIX without "-n". This other shell doesn't for some reason.)The "real" COMMAND.COM, and all other essential parts of MS-/PC-/DR-DOS, have always been written in asm, where none of this libc nonsense matters.
Also it annoys me greatly when people talk about "the C Library" as if it exists in some Platonic realm, and is essential to all software ever written.