Kye's SD_FATEngine.spin (bug in WriteString???)
Cluso99
Posts: 18,069
in Propeller 1
I have spent weeks looking for a bugs in my conversion of Michael Park's Sphinx compiler.
Finally I have found the last obscure bug (hopefully )
In sxfile.spin the WriteString writes out the string character plus the terminating null. In sd_fatengine.spin the WriteString does not write out the null terminator.
Kye handles ReadString (terminates with <cr>, <lf> or <null>) the <cr>, <lf> or <null>is included in the string buffer as a terminator.
Therefore, I think that WriteString (which does not look for <cr> or <lf> BTW) should write out the null terminator.
What are your thoughts???
Should SD_FATEngine WriteString include the null terminator when writing to a file or not???
I can just as easily modify the compiler - I am just not sure which way to go.
Finally I have found the last obscure bug (hopefully )
In sxfile.spin the WriteString writes out the string character plus the terminating null. In sd_fatengine.spin the WriteString does not write out the null terminator.
Kye handles ReadString (terminates with <cr>, <lf> or <null>) the <cr>, <lf> or <null>is included in the string buffer as a terminator.
Therefore, I think that WriteString (which does not look for <cr> or <lf> BTW) should write out the null terminator.
PUB writeString(stringPointer) '' 36+ Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Writes a string to the file that is currently open and advances the file position by the string length. '' // '' // This method will do nothing if a file is not currently open for writing. '' // '' // If an error occurs this method will abort and return a pointer to a string describing that error. '' // '' // StringPointer - A pointer to a string to write to the file. Writes nothing when at the end of a maximum size file. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' writeData(stringPointer, strsize(stringPointer)) writeData(stringPointer, strsize(stringPointer) + 1) 'RR20160228 write out the null terminator
What are your thoughts???
Should SD_FATEngine WriteString include the null terminator when writing to a file or not???
I can just as easily modify the compiler - I am just not sure which way to go.
Comments
On the other hand: it is a question of convention. I only know, linux and dos handle line end differently, CR/LF and LF I thought it would be better to have CR/LF, as this allows to discriminate different cases: CR/LF means: the user shows, he wants to start a newline at the first column. CR means, start at column 0 in the same line, LF: start at current column in the next line. That allowed to print a line, a CR and in a second run to print all decorations like hyphens, underline etc. But times have changed, now with html formatting is done by the browser and no longer under user control.
The question remains: what is a string? Is CR part of the string or a control character. Is a string terminated if a CR is detected and is the CR to be transferred or not. My opinion: 0 is the terminator. If you read a string, you get all the characters and either a length information or the 0. Then it is your decision how you make use of the string, append a 0 or another separator or a string lenght information. With UTF8 times have changed again, confusion persists.
As far as I can tell, it was the case on old DOS/Windows systems, *nix systems never had that nonsense, if I want to copy a file why the OS should bother about the content ?
I don't agree, the string concept is specific to the programming language and we are talking about files here so any language-specific concept should be avoided when managing files that may be readed or written from other languages. For example, the null termination is a convention for C, the old Pascal on DOS used the first byte of a string in memory to hold the lenght (so had the 255 chars limit). If you need files formatted in a specific way then do it at the application level.
A function named 'WriteString' implies a text source so if the convention for that particular language is that a string has a null terminator then it should write up to that and excludes the terminator (it is a convention of the language, other languages may not understand the null termination). Any other character should be written verbatim without translation. If you want to write the null termination then use a function named 'WriteBinary' with an additional lenght argument. WriteString is effectively a shortcut for WriteBinary(s,len(s)).
Maybe add some methods that better clarify the functionality for text-based files, like 'WriteLine' which adds the line termination (either CR/LF or LF or CR alone), 'ReadLine' which reads up to the line termination. See fputs/fgets from the C library as an example. A 'ReadString' method is not needed as it means nothing.
And: is a CR/LF during read interpreted as end of string, means the string including CR/LF is read, but not terminated by 0 or is a 0 added to the string x, x, x, CR, LF.
IMO for consistency all strings should be 0 terminated with 0 written to the file.
All ascii characters from $00 to $FF should be treated as characters for file I/O.
I believe that is silly to have a function that writes text in the convention expected by the programming language (this would break any other program expecting to read a string in another language or simply with another driver) but if the convention is to have 0 as the string terminator, WriteString should write the terminator, and ReadString should read the string with the terminator as this is the convention.
CR/LF is not the string terminator so it should be treated as any other character. Think at what would happen if you WriteString("Hello\r\nWorld\r\n") then read it back. You expect a single string but returns two, or maybe 3 because after the last \r\n we have the null termination thus another empty string.
This would be more easy and clear if we have ReadLine/WriteLine functions and WriteString just writes the text without the terminator.
WriteString needs to write the content of the string (up to but not including the null byte). WriteLine should be a wrapper around WriteString that adds a user configurable line terminator (CR, LF, CR/LF) to the string content. A 4th choice could be a null byte. ReadLine should read the string up to but not including the line terminator and discard the line terminator. Since what's read is a C-string, the variable's value must be null terminated. A null, should it be present on disk, could be treated as a line terminator since it's invalid in a string value.
In ReadString as Cluso says, <cr/lf> or <0> terminate a string. In my opinion a string is an array of (ascii) characters. Our ancestors had defined the ascii character DLE as an exception. Using <DLE 0> to signal end of ascii string would have solved the problem to interpret a character as data or control. But other ancestors decided to see strings as a subset of ascii and to terminate the string by <0>. That is were is begins, not with the apple in paradies.
Whenever I will make use of the driver I will do it this way: Exclude <0> from the characters in a string and use it as terminator.
MyWriteString will write a 0-terminates string including the 0. MyReadString will read the string including the 0. All other characters will be seen as equal, but not the same.
To explain further, the output file is a binary file mixed with text (strings) and binary, so its a binary file.
I can simply avoid the problem and use writeString(@s) followed by write a null with writeData(null,1). I am using my modified version of SD_FATEngine anyway.
What I was more concerned with whether this was considered to be a bug and requiring fixing or not. I would like to see more discussion before any changes are made to Kye's original SD_FATEngine.
Kye's driver is a FAT filesystem driver. FAT filesystems have metadata for the file length. We don't need the null terminator in the file; end of story.
If you want to write data instead of a string, then use a different method such as WriteData which takes an array and a length instead of just an array.
I already have a cut down version of the spin interface to reduce the footprint, with the PASM section a separate object too.