ANEW --Input-File-- \ Wil Baden 2002-02-24 \ ******************************************************************* \ * How to read a file. * \ ******************************************************************* \ To read a file, we need a file ID, a buffer where a record can be \ read, and the maximum number of characters to be read at one time. \ We use VALUE to give us names for these. 0 VALUE Input-File 0 VALUE Input-Line 0 VALUE Input-Length \ The "values" for these VALUE names should be assigned when the \ file is opened. 128 CONSTANT Normal-Input-Length CREATE Normal-Input-Line Normal-Input-Length 2 + chars ALLOT : Open-Input ( str len -- ) R/O OPEN-FILE ABORT" Can't Open File. " to Input-File Normal-Input-Line to Input-Line Normal-Input-Length to Input-Length ; \ _str len_ is the file name, and is given in the form \ S" filename" \ or \ CREATE ," filename" \ ... \ COUNT \ or is extracted from input or existing data. \ Most contemporary applications process one file at a time, so \ `OPEN-INPUT` can be used successively. When a larger buffer is \ needed, an alternative version of `OPEN-INPUT` can be made, e.g. \ `OPEN-HTML-INPUT` or `OPEN-VERSION-INPUT`. \ `READ-INPUT` gets a record implicitly using `INPUT-FILE`, \ `INPUT-LINE` and `INPUT-LENGTH`. `READ-INPUT` returns _str len \ more_, where _str_ is the address of the record, _len_ is how much \ was read, and _more_ is true when there is more of the file to be \ read. The line terminator character is not included in the record. : Read-Input ( -- str len more ) Input-Line dup Input-Length Input-File READ-LINE ABORT" Can't READ-LINE " ; \ We want a function to reposition a file at its beginning, and one \ to close a file when we are done with it. \ : Rewind-File ( file-id -- ior ) 0 0 ROT REPOSITION-FILE ; : Rewind-Input ( -- ) Input-File Rewind-File ABORT" Can't REWIND-FILE " ; : Close-Input ( -- ) Input-File CLOSE-FILE ABORT" Can't CLOSE-FILE " ; \ From this we get a simple pattern for processing files. \ REWIND-INPUT ( ) \ BEGIN READ-INPUT WHILE ( str len) \ process a record of the file ( ) \ REPEAT 2DROP \ CLOSE-INPUT \ This pattern occurs frequently enough to have an even simpler \ form: \ FOR-EACH-READ ( str len) \ process a record of the file ( ) \ REPEAT \ CLOSE-INPUT \ `FOR-EACH-READ` is a macro and can be defined with `EVALUATE` \ : FOR-EACH-READ ( C: -- orig dest )( -- str len ) \ S" REWIND-INPUT BEGIN READ-INPUT ?dup AND ?dup AND WHILE " \ EVALUATE ; IMMEDIATE \ or `POSTPONE`. : FOR-EACH-READ ( C: -- orig dest )( -- str len ) postpone Rewind-File postpone BEGIN postpone Read-Input postpone ?dup postpone AND postpone ?dup postpone AND postpone WHILE ; IMMEDIATE \ Here are some convenient little tools. `Chars/Line` is number of \ characters per line. `EZOPEN` is in Power MacForth. \ 76 VALUE Chars/Line [DEFINED] POWERMACFORTH [IF] \ FILENAME ( -- str len ) \ Name of the file just used by `OPEN-FILE`, `CREATE-FILE`, or \ `DELETE-FILE`. : FILENAME FILENAME$ COUNT ; [THEN] : Show-Input ( -- ) FOR-EACH-READ TYPE CR REPEAT ; : LISTING ( str len -- ) Open-Input [READY] [SET] CR CR ." \ " #Chars/Line 0 DO [char] # EMIT LOOP CR CR ." \ " 8 SPACES FILENAME TYPE CR CR ." \ " #Chars/Line 0 DO [char] # EMIT LOOP CR CR FOR-EACH-READ TYPE CR REPEAT [GO] Close-Input ; \ System specific function to open a file from a menu. [DEFINED] EZOPEN [IF] : INPUT... ( -- ) EZOPEN ?dup IF to Input-File THEN Normal-Input-Line to Input-Line Normal-Input-Length to Input-Length ; [THEN] \ ******************************************************************* \ * Glossary * \ ******************************************************************* \ INPUT-FILE ( -- file-id ) \ _file-id_ of normal input. \ INPUT-LENGTH ( -- n ) \ The maximum number of characters to be read at one time in \ normal input. \ INPUT-LINE ( -- addr ) \ Address of normal input buffer. \ LISTING ( str len -- ) \ Display a heading for _str len_, open file _str len_ as \ `INPUT-FILE`, and display records. \ NORMAL-INPUT-LENGTH ( -- n ) \ The default for `INPUT-LENGTH`. \ NORMAL-INPUT-LINE ( -- addr ) \ The default for `INPUT-LINE`. \ OPEN-INPUT ( str len -- ) \ Open file _str len_ for `INPUT-FILE`, set `INPUT-LENGTH` and \ `INPUT-LINE` to defaults. \ READ-INPUT ( -- str len more ) \ Get a record implicitly using `INPUT-FILE`, `INPUT-LINE` and \ `INPUT-LENGTH`. `READ-INPUT` returns _str len more_, where \ _str_ is the address of the record, _len_ is how much was read, \ and _more_ is true when there is more of the file to be read. \ The line terminator character is not included in the record. \ REWIND-FILE ( file-id -- ior ) \ Reposition _file-id_ at its beginning. Return I/O error code. \ REWIND-INPUT ( -- ) \ Rewind `INPUT-FILE` and check for I/O error. \ SHOW-INPUT ( -- ) \ Display all records of `INPUT-FILE`. \ FOR-EACH-READ ( C: -- orig dest )( -- str len ) \ Rewind `INPUT-FILE` and begin loop. Each iteration reads a \ record and checks for more. When there is more, returns _str \ len_, where _str_ is address of the record, and _len_ is the \ length of what was read. When there is no more, the loop is \ terminated by `REPEAT` or ` UNTIL THEN`. \\ *********************** End of Input-File ***********************