Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Delphi 7 truncate file after ASCII code 14 when opening in Append mode?

Tags:

ascii

delphi

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)?

like image 967
yeputons Avatar asked May 02 '20 18:05

yeputons


People also ask

Does opening a file mean truncation/appending?

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.

How do I open a t file in Delphi?

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.

How do I truncate a file in Unix?

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) ).

How to pad or truncate a file to 567 bytes?

# 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:


1 Answers

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.

like image 196
yeputons Avatar answered Oct 17 '22 22:10

yeputons