Files

RFO-BASIC! programs can use files that are in internal storage or on a removable device such as an SD card. (Android phones often have a microSD card inside. It is an alternative "removable" storage space, though it may be difficult to actually remove it.)  With an adapter attached to the USB port, a removable device may also be a thumbdrive. Internal storage and removable devices are both called storage drives. By default, files reside on the base drive. Originally, this is the drive where you installed RFO-BASIC!. You can change the base drive using Menu → Preferences when running RFO-BASIC!.

Files are sequences of bytes. Traditional (8-bit) characters can be stored as bytes. UNICODE characters can be stored as multiple bytes. However, some RFO-BASIC! programs transform these characters.



File systems
A file system is a set of one or more directories. (Directories are also known as folders.) Each directory is a list of files by name. Within the list there can also be directories, which also have names. Such a directory is a subdirectory of the directory that lists it. Files and directories are known collectively as objects. The name of each object must be unique within its directory.

The base drive (defined at the start of this page) contains a directory named. This base directory exists alongside other directories on the drive such as  and. The base directory contains several subdirectories:
 * RFO-BASIC! directories

The  character means that the text to the right of it is an object residing in the directory named to the left of it. For example, in the table above,  describes an object (it is a directory) named   located inside the directory. (MS-DOS and Windows use  instead.)
 * Navigating directories

No special navigation is needed to reach a file that is in the usual directory shown in the table above. For example, if there is a text file named  in the usual directory for those files — if it is   — then an RFO-BASIC! program can refer to it simply by name:. An SQLite database in the usual place,, could be specified as simply.

You may create subdirectories using MKDIR. For example, within the  directory, you might create a subdirectory exclusively for the text files of a single RFO-BASIC! program, such as at. If this subdirectory contained a text file named, you would gain access to it as. If you created another directory for that program's databases, it would be at. Then the program could create databases in that subdirectory, such as.

You may create subdirectories inside a subdirectory; for example, a single program might have several files with different types of information, and use a separate subdirectory for files of each type. Each use of  travels from a directory to a subdirectory it lists.

An exception is that  navigates to the outer directory. For example, a text file statement in an RFO-BASIC! program could specify  to go out of the   directory (back to the   directory) and into the   directory, if a text file named   were stored there for some reason.

The RFO-BASIC! program always has permission to gain access to the three directories listed in the table above, as well as the  base directory. It can use MKDIR to create directories in addition to the three built-in directories, such as. It can step outside  to navigate to a sibling directory, such as   or.
 * File protection

However, the program might not have permission to step further out than the user directory containing. It might not have permission to use an absolute pathname, such as the pathname that FILE.ROOT returns, even if the absolute pathname specified a directory to which the program had permission.

Emerging versions of Android introduce the concept of Scoped Storage, in which a program specifies a file not by using a pathname to a directory but according to the type of information or relation to the program. The concept that this article relies on is called Legacy Storage.
 * Scoped storage
 * Android 10 will incorporate Scoped Storage but will make it a program option. However, a flag must be set in the Manifest in the .APK file to exercise this option.  hBasic v2.92 indeed specifies Legacy Storage.  There will have to be an RFO-BASIC! version after v1.92 that does so.
 * Android 11 removes the option of Legacy Storage. RFO-BASIC!'s file statements would need redesign, and some operations, such as exchanging information between RFO-BASIC! programs, and between them and programs outside RFO-BASIC!, will become difficult or impossible.

Alphabetic case
Objects (files and directories) have names, which the program must specify to open the file in order to gain access to its contents.
 * Files stored on SD cards and thumbdrives use the Microsoft FAT file system, in which a capital letter matches the corresponding lowercase letter. A file named   can be used by specifying   or   or other combinations of uppercase and lowercase.  But when a file is created, its name is stored in the directory with capital or lowercase letters as the program specifies.
 * Files stored in the phone's internal storage use the Linux file system, in which the case of letters is important. Each of the variations given above would refer to a different file.

Programmers need to pay attention to this because it can lead to unusual errors. For example, if an RFO-BASIC! program capitalized all filenames, in order to be tolerant of different styles of typing, then a file with lowercase in its name, which was accessible on a removable drive, would suddenly become inaccessible if moved to the phone's internal storage. To prevent errors, RFO-BASIC! programs should always assume a filename could contain lowercase letters, and should match case exactly in file paths and names.

Text files and byte files
RFO-BASIC! programs can open files as lines of text, using the statements in the section Text files, or as sequences of 8-bit bytes, using the separate statements in the section Byte files.

Text files and byte files each have a file position, the location in the file where the next read or write will occur. The initial position depends on the form of the TEXT.OPEN or BYTE.OPEN statement, and on whether the file exists.

End-of-file
End-of-file means that the file position is at the end of the entire contents of the file. When writing or appending to a file, the position is always at end-of-file. When reading a file, end-of-file means there is nothing more to read. Typically, the program has a loop that reads units of the file, and reaching end-of-file breaks out of the loop.

When RFO-BASIC! reads information from a file, it does not check whether doing so reached end-of-file. If you know where end-of-file is, such computation would be unnecessary. Instead, end-of-file is declared only if the program continues to read and fails. The TEXT.EOF and BYTE.EOF statements test this condition.

FILE.MKDIR
Create a subdirectory

FILE.MKDIR 
 * Synopsis

The FILE.MKDIR statement (you can simply write MKDIR) creates a directory. The  argument specifies the new directory. If  does not contain, then it is simply the name of the new directory, which will be a subdirectory of. If  does contain, then everything after the last   is the name of the new directory and everything before is the path of the directory in which to create it.
 * Description

MKDIR "Homes" This creates a directory  inside the assumed directory.
 * Examples

MKDIR "../libraries" This path goes up to the  directory and creates a subdirectory of it, named , alongside its existing subdirectories,  ,  , and.

FILE.DELETE
Remove an object (file or subdirectory) from a directory

FILE.DELETE , 
 * Synopsis

The FILE.DELETE statement deletes a file or a directory specified by . If  does not contain, then it is simply the name of the object to be deleted from. If  does contain, then everything after the last   is the name of the object to delete and everything before is the directory in which to look for it.
 * Description

The statement sets  to a nonzero value if the specified object existed and was deleted. It sets  to zero if the object did not exist.

FILE.DELETE success, "MyTempFile"
 * Example

FILE.RENAME
Rename an object

FILE.RENAME , 
 * Synopsis

The FILE.RENAME statement (you can simply write RENAME) looks for an object whose name is the string at  and renames it to . If the object was a directory, it remains a directory; if it was a file, it remains a file.
 * Description

The parameters can use the  character to specify not only the name of the object but its location. If a parameter omits, it is assumed to be in the   directory.

If the specified or implied directory in <oldpath_sexp> is not the same as that in <newpath_sexp>, then the object is moved from the first directory to the second directory.

It is an error if an object specified by <oldpath_sexp> does not exist in the specified or implied directory. The program can query the directory, using the statements described in the next section, to ensure that this error will not occur. If an object already exists with the desired new name, it is replaced.

RENAME "MyTempFile", "Preferences" This changes the name of a file in the  directory.
 * Examples

RENAME "../../Bluetooth/newdata" "1" Assuming a file named  has been imported from another phone using Bluetooth, this statement moves it from the   directory alongside the   directory into   and names it.

Querying directories
The statements in this section retrieve information about the contents of a directory. In all cases, the pathname starts in the directory. To query the RFO-BASIC! directory for databases, the program could use a string such as.

FILE.EXISTS
Determine if a specified object exists

FILE.EXISTS <result_lvar>, <path_sexp>
 * Synopsis

The FILE.EXISTS statement checks to see if an object exists with the name, and in the directory, specified by <path_sexp>. It sets <result_lvar> to a nonzero value if the object exists, and to zero if it does not.
 * Description

FILE.EXISTS success, Filename$ IF success THEN FILE.DELETE Filename$ A program should check for the existence of a file before using FILE.DELETE to delete it, to avoid the possibility of a run-time error.
 * Example

FILE.TYPE
Determine the existence and type of an object

FILE.TYPE <type_svar>, <path_sexp>
 * Synopsis

The FILE.TYPE statement checks to see if an object exists with the name, and in the directory, specified by <path_sexp>, as FILE.EXISTS does. However, FILE.TYPE returns, as a one-character string, an indicator describing the type of the object.
 * Description

The following example is another way to pre-test before trying to delete a file. This code also ensures that the program does not delete an entire directory.
 * Example

FILE.TYPE ind$, Filename$ IF ind$="f" THEN FILE.DELETE Filename$

FILE.SIZE
Obtain the size of a file

FILE.SIZE <size_nvar>, <path_sexp>
 * Synopsis

The FILE.SIZE statement determines the size, in bytes, of the file specified by <path_sexp>, returning the result in <size_nvar>. It is an error if such a file does not exist.
 * Description

FILE.TYPE ind$, Filename$ IF ind$="f" THEN FILE.SIZE Result, Filename$ ELSE Result = -1 This example ensures that  specifies an existing file (not a directory). If so, it places in  the length of the file. Files may have a length of 0. If there is no such file, it sets  to -1 to indicate this fact.
 * Example

FILE.DIR
Obtain a directory listing in an array

FILE.DIR <path_sexp>, Array$[]{, <dirmark_sexp>}
 * Synopsis

The FILE.DIR statement (you can simply write DIR) creates a listing of the specified directory and places it in the specified string array. If the array exists, it is overwritten; otherwise, it is created. The result is a one-dimensional array in which each element is the name of one object in the directory. The descriptions are sorted so that directories are grouped before files; within each group, entries are sorted alphabetically. The <dirmark_sexp> parameter is a string appended to an entry if the object is a directory. If <dirmark_sexp> is omitted, FILE.DIR appends. The program can specify  as <dirmark_sexp> to avoid any mark for directories.
 * Description

DIR "../databases", dbnames$[], "/" ARRAY.LENGTH nObjects, dbnames$ FOR i=1 TO nObjects PRINT dbnames$[i]  % Or take some other desired action on each object NEXT i This example produces, in, a list of the contents of RFO-BASIC!'s database directory. If it contains subdirectories, they are listed first, each ending with. The example then uses ARRAY.LENGTH to obtain the number of entries, which may be zero.
 * Example

FILE.ROOT
Get the absolute pathname of the RFO-BASIC! data directory

FILE.ROOT <path_svar>
 * Synopsis

The RFO-BASIC! directories are never in the phone's root directory, but instead are several directories removed from the root. The FILE.ROOT statement obtains an absolute pathname of the base directory. (Absolute pathnames start with, denoting the phone's root directory.
 * Description

RFO-BASIC! programs never use absolute pathnames; they use pathnames relative to the RFO-BASIC! base directory. However, a program might use FILE.ROOT to generate an absolute pathname of its files to pass to some other program.

FILE.ROOT S$
 * Example

An inexpensive Samsung phone set  to. This is because, when RFO-BASIC! is installed in the phone's internal storage, the phone places it in "emulated drive 0." This result will be different on different devices, and different still if the base directory is on a removable device.

Text files
Text files are files that contain lines of text. The text file statements of RFO-BASIC! read or write a single line at a time. A line ends with the character  (carriage return) or   (line feed) or both.

The characters in a line can be single-byte characters, such as USASCII codes, or can be UNICODE characters.

Every open file has a position within it where the next text will be read or written. See the table at TEXT.OPEN.


 * Buffering

Using TEXT.CLOSE and then using RUN to run a different program, or restart the same program, may take control away from the program before it can complete the writing. </DIV> As you write text to a file, the text goes into an intermediate buffer of 8192 characters. It is only when this buffer becomes full that it is written to the permanent file. If your program ends without closing the file using TEXT.CLOSE, then text in the intermediate buffer might be discarded and might not appear in the file the next time you open it.

TEXT.OPEN
Open a text file

TEXT.OPEN {R|W|A}, <handle_nvar>, <path_sexp>
 * Synopsis

The TEXT.OPEN statement opens a text file. The program must open a file before it can deal with the file's contents or write new contents to it. The program specifies this file using the <handle_nvar> that TEXT.OPEN returns. The <path_sexp> parameter gives the name of the file. It is assumed to reside in the  directory. See the start of this article for specifying some other directory, and for the rules on capitalization.
 * Description

One of three letters must follow TEXT.OPEN. This is typed literally and is not a string expression. This is the mode in which to open the file:

A  statement could set <handle_nvar> to -1 instead of to a file handle. This would indicate that the program does not have permission to write to the device (for example, the device might be a microSD card). There is no way for an RFO-BASIC! program to override lack of permission, nor to modify the user's list of permissions. (It might also mean that a microSD card is totally full.)
 * Testing access to a device

A  statement also sets <handle_nvar> to -1 if the program lacks access permission, but coding   and testing <handle_nvar> is a less ambiguous test for this condition.

See the examples below.
 * Example

TEXT.CLOSE
Close a text file

TEXT.CLOSE <handle_nexp>
 * Synopsis

The TEXT.CLOSE statement closes the file with the specified handle that was opened by TEXT.OPEN.
 * Description

TEXT.OPEN R, hdl, "MyFile" IF hdl = -1 THEN PRINT "The file doesn't exist!" : END TEXT.CLOSE hdl
 * Example

This example simply opens and closes a specified file. It is good for nothing except to see if the file exists.

TEXT.READLN
Read a line of text from a file

TEXT.READLN <handle_nexp>,{<line_svar>}...
 * Synopsis

The TEXT.READLN statement reads one or more lines from the file with the specified handle. That file must have been successfully opened by TEXT.OPEN. As many lines are read as the program provides string variables. After reading, the file is positioned beyond the last line that was read.
 * Description

If the file's position is at end-of-file, then the string "EOF" is returned and the position remains at end-of-file. (See also the TEXT.EOF statement.) The file is not automatically closed; the program should use TEXT.CLOSE.

This example reads each line of the specified file and prints it on the Console. The example is refined later in this article. TEXT.OPEN R, hdl, "MyFile" % Also test hdl > -1, as done above DO TEXT.READ hdl, str PRINT str                 % Or do whatever you want with the line UNTIL str = EOF            % EOF has been PRINTed to the Console too TEXT.CLOSE hdl
 * Example

TEXT.EOF
See if a text file's position is at the end

TEXT.EOF <handle_nexp>, <atend_lvar>
 * Synopsis

The TEXT.EOF statement returns in <atend_lvar> the value 0 if the file with the specified handle is not at end-of-file, and a nonzero value if it is at end-of-file.
 * Description

Using TEXT.EOF is vital to distinguish between TEXT.READ returning "EOF" to indicate end-of-file, and returning "EOF" because it actually read a line of text that was "EOF".

When reading a file, the position is not end-of-file until the program has read all the text from the file and then attemped one more read (which returns "EOF"). When writing or appending, the file's initial position is end-of-file.

This example refines the previous example so that an "EOF" that simply denotes end-of-file is not PRINTed. TEXT.OPEN R, hdl, "MyFile" % Also test hdl > -1, as done above DO TEXT.READ hdl, str IF str="EOF" THEN TEXT.EOF hdl, IsEnd IF IsEnd <> 0 THEN D_U.BREAK  % Exit the loop ENDIF PRINT str                 % Or do whatever you want with the line UNTIL 0 TEXT.CLOSE hdl
 * Example

TEXT.WRITELN
Write a line to a text file

TEXT.WRITELN <handle_nexp>{, }
 * Synopsis

The TEXT.WRITELN statement is like the PRINT statement but writes information to the file with the specified handle, rather than to the Console.

The parameters can be numeric or string expressions (including string literals). For numeric expressions, TEXT.WRITELN prints their string representation. The program can use functions such as USING$ to get precise control over the representation of numbers.

RFO-BASIC! has only one holder for an output line — not one per text file handle. You should only use multiple TEXT.WRITELN statements to assemble a single output line if you are writing only a single text file at a time. Otherwise, assemble the output line in a string variable instead. </DIV> The parameters are separated by comma or semicolon. If a parameter is followed by a comma, the string is assembled on the output line, followed by blanks. If it is followed by semicolon, the string is assembled on the output line without separation from the next string.

If the final parameter is not followed by a comma or semicolon, the line is complete and is written to the file. If the final parameter is followed by a comma or semicolon, the statement ends without actually writing the output line. Subsequent TEXT.WRITELN statements can add text to the output line. A TEXT.WRITELN statement with nothing following the <handle_nexp> parameter simply ends the output line, writing the line to the text file; if previous TEXT.WRITELN statements did not provide anything to write, this statement writes an empty line.

Random access to a text file
If TEXT.OPEN opens a file in mode  (Read), RFO-BASIC! lets your program move the position of a text file to a specified line number. The first line number is 1. As the program reads lines from a text file, RFO-BASIC! builds a table indicating the start of each line in the file. If you try to jump to a line number beyond the last one you have read or written, RFO-BASIC! reads through the file, further building the table. The ability to use line numbers lets you read lines from a text file in an order other than sequential order.

If TEXT.OPEN opens a file in mode  (Append), line 1 refers to the first new line you write. It is not possible to move the position to one of the preexisting lines preceding the appended text. However, after appending the desired text, you could close the file and reopen it in mode  to read its lines in arbitrary order.

RFO-BASIC! does not let programs write lines in other than sequential order, as it is problematic what happens when a line is replaced by a line of a different length.

Use of marks and limits affects the ability to gain random access to a text file.

TEXT.POSITION.GET
Get a text file's current line position

TEXT.POSITION.GET <handle_nexp>, <position_nvar>
 * Synopsis

The TEXT.POSITION.GET statement returns, in <position_nvar>, the line position of the text file whose handle is <handle_nexp>.
 * Description

TEXT.POSITION.SET
Change a text file's current line position

TEXT.POSITION.SET <handle_nexp>, <position_nexp>
 * Synopsis

The TEXT.POSITION.SET statement moves the position of a text file, whose handle is <handle_nexp>, to the line number given in <position_nexp>. If the specified line number is past the end of the file, the file is moved to end-of-file, and using TEXT.POSITION.GET will obtain the number of lines in the file, plus 1.
 * Description

Reading an entire file
Two statements place the entire contents of a file in a string variable.

GRABURL <text_svar>, <path_sexp>{, <timeout_nexp>} GRABFILE <text_svar>, <path_sexp>{, <unicode_lexp>} The GRABURL statement obtains an entire file across the Internet. It can also obtain an entire local file. When grabbing a local file, the specified time-out interval is meaningless.
 * Synopses
 * Description

The GRABFILE statement also obtains a local file. Its third parameter is a logical variable. If <unicode_lexp> is 0, then GRABFILE sets each character of <text_svar> to one character from the file. This is appropriate if the file contains 8-bit bytes (binary data) or exclusively 8-bit characters, such as ASCII. If <unicode_lexp> is TRUE (any nonzero value), then GRABFILE reads 16-bit UNICODE characters and sets each character of <text_svar> to one UNICODE character from the file. This makes the characters easier to process.

Both statements may set <text_svar> to the empty string. This may mean the file does not exist, or that there was an error reading it. The program can call GETERROR$ to get details on the error. It may also mean that the file exists and was readable but contained no information. In this case, GETERROR$ returns " ".

To edit a text file, the following two statements are often used together: GRABFILE text$, "MyJournal.txt" TEXT.INPUT EditedText$, text$, "Type your changes below"
 * Examples

The string returned by GRABURL or GRABFILE may be supplied to the SPLIT statement to produce a string array where each element is a single line from the file.

Byte files
The file types or extensions shown in parentheses are examples. It is not important that a filename end with that string to be the given type of file. It is important that the contents of the file be of a type that the RFO-BASIC! program is expecting. </DIV> Byte files can be any file, including
 * Files that contain lines of text, which could also be opened using the RFO-BASIC! statements for text files (.txt).
 * Music (.mp3 or .wav).
 * Pictures (.jpg or .png or .gif)
 * Formatted documents (.rtf or .pdf).

When an RFO-BASIC! program opens a file as a byte file, it prepares to read or write individual 8-bit bytes, codes from 0 through 255. If a text file contains multibyte characters, such as in UNICODE, they will appear as a sequence of bytes; a single byte read or written will not be an entire character.

BYTE.OPEN
Open a byte file

BYTE.OPEN {R|W|A}, <handle_nvar>, <path_sexp>
 * Synopsis


 * Description

This description is substantively the same as that for the TEXT.OPEN statement. </DIV> The BYTE.OPEN statement opens a byte file. The program must open a file before it can deal with the file's contents or write new contents to it. The program specifies this file using the <handle_nvar> that BYTE.OPEN returns. The <path_sexp> parameter gives the name of the file. It is assumed to reside in the  directory. See the start of this article for specifying some other directory, and for the rules on capitalization.

One of three letters must follow BYTE.OPEN. This is typed literally and is not a string expression. This is the mode in which to open the file:

See the examples below.
 * Example

BYTE.CLOSE
Close a byte file

BYTE.CLOSE <handle_nexp>
 * Synopsis

The BYTE.CLOSE statement closes the file with the specified handle that was opened by BYTE.OPEN.
 * Description

BYTE.OPEN R, hdl, "MyFile" IF hdl = -1 THEN PRINT "The file doesn't exist!" : END BYTE.CLOSE hdl
 * Example

This example simply opens and closes a specified file. It is good for nothing except to see if the file exists.

BYTE.READ.BYTE
Read one or more bytes from a byte file

BYTE.READ.BYTE <handle_nexp>{, <byte_nvar>}...
 * Synopsis

The BYTE.READ.BYTE statement reads one or more bytes from the file with the specified handle. That file must have been successfully opened by BYTE.OPEN. As many bytes are read as the program provides numeric variables. Each variable is set to a value from 0 through 255. After reading, the file is positioned beyond the last byte that was read.
 * Description

If the file's position was at end-of-file, then the position remains at end-of-file. (See also the BYTE.EOF statement.) The file is not automatically closed; the program should use BYTE.CLOSE.

Unlike TEXT.READLN, BYTE.READ.BYTE does not return values that indicate that it reached end-of-file. Unless you know the exact length of the byte file, the program should read one byte at a time and use BYTE.EOF to test for end-of-file.

The following example reads an entire file and prints it on the Console: BYTE.OPEN R, handle, "waypoints" IF handle = -1 THEN ! Report that the file could not be opened and do not continue ENDIF DO BYTE.READ.BYTE handle, i  BYTE.EOF handle, WasEOF IF WasEOF THEN D_U.BREAK % Exit loop on EOF; "i" is not file data PRINT CHR$(i) UNTIL 0 BYTE.CLOSE handle
 * Example

This example does not faithfully print multi-byte (UNICODE) characters, but tries to print separately each byte of the character. See the discussion of GRABFILE.

Instead of PRINTing each character, you could use BYTE.WRITE.BYTE to write it to a different file. However, to make an exact copy of a byte file, BYTE.COPY is thousands of times faster.

BYTE.READ.BUFFER
Read bytes from a byte file into a string variable

BYTE.READ.BUFFER <handle_nexp>, <count_nexp>, <buffer_svar>
 * Synopsis

The BYTE.READ.BUFFER statement reads bytes from the file with the specified handle. The <count_nexp> parameter specifies how many bytes to read. If <count_nexp> is 0, nothing happens.
 * Description

RFO-BASIC! strings contain 16-bit characters. But BYTE.READ.BUFFER loads into each character of <buffer_svar> one 8-bit character from the byte file. (The high byte of the character read into <buffer_svar> is 0.) If you are using BYTE.READ.BUFFER to read multibyte characters, such as UNICODE characters, a single UNICODE character will occupy multiple characters of <buffer_svar>. The ASCII or UCODE functions can be used to create a string that properly renders the UNICODE text.

The <buffer_svar> variable (or a substring of it, extracted using MID$) can also be passed to DECODE$ to convert it to an RFO-BASIC! string.

BYTE.WRITE.BYTE
Write to a byte file

BYTE.WRITE.BYTE <handle_nexp>,{ <byte_nexp>}...{, <string_sexp>}
 * Synopsis


 * Description

Any expression provided as <string_sexp> is evaluated twice. The results may be counterintuitive if <string_sexp> includes string functions that have side-effects. </DIV> The BYTE.WRITE.BYTE statement writes various things to the file with the specified handle. The handle may be followed by one or more numeric expressions (in which case, each expression is evaluated and causes a single byte to be written to the file), which may be followed by a string expression (in which case, each character of the string is written to the file as one byte). To simply write the characters of a string to a byte file, it is better to use BYTE.WRITE.BUFFER.

BYTE.WRITE.BUFFER
Write a string to a byte file

BYTE.WRITE.BUFFER <handle_nexp>, <buffer_sexp>
 * Synopsis

The BYTE.WRITE.BUFFER statement writes an RFO-BASIC! string to a byte file with the specified handle. The number of bytes written is the length of the string expression <buffer_sexp>.
 * Description

RFO-BASIC! strings contain 16-bit characters. But BYTE.WRITE.BUFFER writes only the low 8 bits of each character as one byte in the byte file. The high 8 bits have no effect.

The program can build the buffer in several ways:
 * The ENCODE$ function converts a string of UNICODE characters to 8-bit characters so that BYTE.WRITE.BUFFER will write correct UNICODE characters to the byte file.
 * The RFO-BASIC! program can create binary data in a string variable by using the CHR$ function to create 8-bit characters representing any binary value from 0 through 255.
 * If <buffer_sexp> is certain to contain only 8-bit characters (such as ASCII characters), then it can be output directly using BYTE.WRITE.BUFFER. The effect is the same as with TEXT.WRITELN except that BYTE.WRITE.BUFFER does not write a line-termination character unless the program appends one to the end of <buffer_sexp>.

BYTE.COPY
Copy an entire byte file

BYTE.COPY <handle_nexp>, <pathname_sexp>
 * Synopsis

The BYTE.COPY statement reads from a file that the RFO-BASIC! program has already successfully opened (that is, <handle_nexp> must be a valid byte file handle, and not -1). Beginning at this file's current position, it creates a new file with the given pathname and copies the remainder of the input file to this new file. BYTE.COPY then performs BYTE.CLOSE <handle_nexp> (as well as closing the new file).
 * Description

When using marks and limits, file position 0 might not be within the file's current window. To ensure correct operation, close the byte file and then reopen it. </DIV> The position of <handle_nexp> is at the start of the file when you use BYTE_OPEN R. However, if you have read from the file, then BYTE.COPY only copies the subsequent bytes from the file. If you want to make a complete copy of the file, set the file position back to the start of the file with a sequence such as: BYTE.POSITION.SET InputHandle,0 BYTE.COPY InputHandle, "Newfile"

Do not code a loop with BYTE.READ.BYTE and BYTE.WRITE.BYTE, unless your program sometimes makes conversions on the file's contents instead of copying them literally. To make an exact copy, BYTE.COPY is thousands of times faster.

BYTE.TRUNCATE
Truncate a byte file at the current position

BYTE.TRUNCATE <handle_nexp>, <length_nexp>
 * Synopsis

The BYTE.TRUNCATE statement operates on files successfully opened in mode  (Write) or   (Append). That is, <handle_nexp> must not be -1. The statement indicates the desired total size of the file in bytes. This must result in a shortening of the file; it is meaningless to specify a larger size than the number of bytes you wrote to the file, so <length_nexp> must be lower than the current size of the file (the current file position minus 1).

BYTE.TRUNCATE also closes the file.

The effect of BYTE.TRUNCATE is to declare that some of the bytes most recently written to the file should not be saved. Truncating a file to 0 bytes means the file will exist but will contain no bytes.

Reading and writing numeric data
Two statements read and write numbers to a byte file. In the file, each number takes up 8 bytes. In this way, an RFO-BASIC! program can store numeric values and read them back later, without potential problems, such as round-off error, that might occur when using a text file to store their text representation. This is the only recommended use for these statements; the numbers are written in their internal RFO-BASIC! format, which is not guaranteed to be the same as the format that any other program uses.

BYTE.READ.NUMBER <handle_nexp>{, <number_nvar>}... BYTE.WRITE.NUMBER <handle_nexp>{, <number_nexp>}...
 * Synopses

The BYTE.READ.NUMBER statement reads one or more numbers from the byte file with the specified handle, and places them in the numeric variables in the order in which it reads them. When reading numbers, the byte file position should be the exact position where BYTE.WRITE.NUMBER wrote them. If BYTE.READ.NUMBER reaches end-of-file, it sets the variable being read, and all subsequent variables in the parameter list, to -1. The program should not use -1 as an end-of-file indicator, because the number in the byte file might actually be -1. Instead, the program should know how many numbers the byte file contains, or should read one at a time and use BYTE.EOF to see if end-of-file was reached after each.
 * Description

The BYTE.WRITE.NUMBER statement writes one or more numbers to the byte file with the specified handle. The numbers to be written are given by the numeric expressions in the parameter list, and BYTE.WRITE.NUMBER writes them in the sequence in which they are listed.

Marks and limits
When TEXT.OPEN or BYTE.OPEN opens a file, the following things happen:
 * The mark is set to the start of the file.
 * The limit is set to the entire size of the file.
 * The mark and limit together constitute a window into the file.
 * The window is read into RFO-BASIC!'s internal memory.

The programmer need not bother with these concepts except when opening a file that is larger than RFO-BASIC!'s entire internal memory. An attempt to read or reposition the file would fail, and GETERROR$ would obtain the string "Out of memory". In this case, the programmer must specify a limit that fits in internal memory. The programmer may also specify a mark so that the portion of interest of the file is not the start of the file.


 * You can define a window in a text file with TEXT.POSITION.MARK
 * You can define a window in a byte file with BYTE.POSITION.MARK

BYTE.POSITION.MARK {{<handle_nexp>} {, <limit_nexp>}} TEXT.POSITION.MARK {{<handle_nexp>} {, <limit_nexp>}}
 * Synopses

This statement sets a window into the file whose handle is <handle_nexp>. It declares that the current position in the file is the mark, and the number of bytes in <limit_nexp> is the limit.
 * Description

Both parameters are optional. If <handle_nexp> is omitted, the default file is the file that was opened most recently. The specified or implied file must be a file opened in mode  (Read). TEXT.POSITION.MARK can only be used on text files, and BYTE.POSITION.MARK can only be used on byte files.

If you omit <limit_nexp>, then the statement does not change the limit.

The TEXT.POSITION.SET and BYTE.POSITION.SET statements, which enable random access to a file, cannot be used to jump to a position before the mark.

In addition, you cannot read nor jump past the limit. A statement that attempts this fails, and GETERROR$ returns "Invalid mark".