I'm working on some legacy software written in Delphi 7 which runs on Windows. I've minified the problem to the following program:
var f: text;
begin
assign(f, 'a.txt');
rewrite(f);
writeln(f, 'before' + chr(14) + 'after');
close(f);
assign(f, 'a.txt');
append(f);
close(f);
end.
I expect it to create a.txt
file containing "before#14after#13#10"
and then append nothing to it. However, after I run this program on Windows, I see before
in a.txt
instead, like if Delphi's append
truncates the file. If I do not re-open the file, it shows before#14after#13#10
as expected.
If I write something (FooBar
) in the re-opened file, it's appended, but as if the file was already truncated: beforeFooBar
.
This effect does not occur with any other character between 0 and 32, even with 26 (which stands for EOF).
Is this a bug in Delphi or a well-defined behavior? What is so special about chr(14)
?
Nope. "open a file, or create it if it doesn't exist, and write to it" does not imply truncation; opening an existing file typically means to append to it. However, because it wasn't clear, I did not downvote your answer; if the question had been more clear about truncation/appending, I would have upvoted or downvoted accordingly.
If your Delphi version is new enough to offer it, you can use the TFile.Openwith the fmOpenOrCreateopen mode, which does exactly what you want; it returns a TFileStream. Otherwise, you can use the Windows API function CreateFileto open your file instead.
Unix can extend a file, so there is no error if the length increases. # Truncate a file named "myfile" to 1440 kilobytes. ls myfile >/dev/null && dd if= /dev/null of= myfile bs=1 seek= 1440k Some systems have a truncate (1) command ( FreeBSD truncate (1), GNU truncate (1) ).
# Open a file for writing, and truncate it to 1234 bytes. open FOO, ">>file" or die; truncate(FOO, 1234); close FOO; # Truncate a file to 567 bytes. truncate("file", 567); It will pad or truncate as needed. On the 64-bit version, we can call the native runtime library: Otherwise (on all versions), we call the external truncate command:
Thanks to some friends from a chat and Sertac Akyuz from comments: it looks like a bug in Delphi 7.
It's supposed to have special handling of the EOF symbol (ASCII 26), quoting from here:
Note: If a Ctrl+Z (ASCII 26) is present in the last 128-byte block of the file, the current file position is set so that the next character added to the file overwrites the first Ctrl+Z in the block. In this way, text can be appended to a file that terminates with a Ctrl+Z.
Kind of CP/M backward compatibility, I guess.
However, there is a bug in the implementation of TextOpen
for Windows (see Source/Rtl/Sys/System.pas
from your Delphi 7 installation around line 4282):
@@loop:
CMP EAX,EDX
JAE @@success
// if (f.Buffer[i] == eof)
CMP byte ptr [ESI].TTextRec.Buffer[EAX],eof
JE @@truncate
INC EAX
JMP @@loop
Here it says eof
instead of cEof
. Unfortunatley, that compiles for some reason and it even appeared on StackOverflow already. There is a label called @@eof
and that's pretty much it.
Consequence: instead of having special case for 26, we have special case for 14. The exact reason is yet to be found.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With