JRT Pascal User's Guide version 3.0 NOT FOR SALE -73- 7. Input/output JRT Pascal includes a powerful input/output subsystem which can be used to meet virtually any processing requirement. Three modes of input/output - console, sequential disk, random disk - are provided. Disk files can be processed in either TEXT mode or in BINARY mode. TEXT mode is most commonly used by BASIC languages. Data is stored in ASCII text readable format. BINARY mode is found on larger mini and mainframe computers. The data is input/output in the binary format used internally by the language. Not only is the data more compact in some cases but it is also of fixed length. For example, an integer in text format could occupy from two bytes to six bytes, depending on its value. But in binary format an integer is always exactly two bytes. TEXT mode is sometimes called "stream I/O". BINARY mode is sometimes called "record I/O". Another advantage of binary format is that the user can process data files or COM files containing special control characters. All files in JRT Pascal are "untyped". That is, the user can read and write data of any format to any file. The user can write records of entirely different formats and sizes on the same file. JRT Pascal also supports direct access to the hardware input/output ports without having to write an assembly language subroutine. The builtin function PORTIN and builtin procedure PORTOUT are described in the sections on builtin functions and builtin procedures. JRT Pascal version 3 now supports Pascal file variables. Files may now be passed as parameters to procedures, allocated locally in procedures, be used in records or arrays, be used in assignment statements. The Pascal builtin procedures GET and PUT are now supported. Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -74- 7.1 Console input/output Console input/output is the usual means for a program to interact with the user. Data values can be displayed at a video terminal or teletype and data can be keyed in. Console input/output always occurs in text rather than binary format. Integers, real numbers, strings, characters and Booleans will be displayed in text format. Set variables have no meaningful text format and cannot be written to the console. IMPORTANT - Since the console is regarded as a text device, data items are delimited by commas, spaces, tabs and semicolons. To read one character at a time, use this function: FUNCTION GET_CHAR : CHAR; VAR R : RECORD FLAG,A,C,D,E,D,L,H : CHAR; END; BEGIN R.C := CHR(1); CALL( 5,R,R ); GET_CHAR := R.A END; Using the HEX$ builtin function, any variable can be converted to hex format for direct display. On console input for integers, data may be keyed in using standard decimal format or in hex format. An 'H' character suffix indicates hex format. On input to the console, data items may be separated by spaces, tabs, commas or semicolons. Character or structured variable inputs which contain special characters may be entered in single quotes. The quote character itself may be entered by doubling it. Sample input lines: 3.14159,77 03ch,'JRT Systems' 'don''t say you can''t' 6.70234e-25,0.0000003 Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -75- Reading from the console into a dynamic string variable is treated differently. An entire line of text is obtained from the console and moved directly into the string variable. Separator characters and single quotes are ignored. The system will not allow more characters to be keyed in than can fit into the variable in the READ's parameter list. Console output can also be routed to the printer or list device. The SYSTEM procedure is fully described in the section on builtin procedures. Some of its options are: SYSTEM( LIST ); route output to printer SYSTEM( NOLIST ); do not route to printer SYSTEM( CONS ); route to console device SYSTEM( NOCONS ); do not route to console The builtin procedures/functions used in console input/output are: READ, READLN read data into storage WRITE, WRITELN write data to console/printer EOLN end of line function Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -76- 7.2 Sequential file processing Disk files are not inherently sequential or random. Those terms apply to the means of access which may be applied to any disk file. Sequential file processing is generally faster than random access because input/output can be buffered and because the disk positioning mechanism only needs to move short distances. JRT Pascal lets the user obtain maximum processing speed by defining the buffer size for sequential files. The buffer is the holding area where disk data is loaded and written. This area is filled or emptied in one burst - one disk access with one head load operation. A very small buffer may cause disk "chattering" during processing because of frequent accesses. A large buffer will result in less frequent but longer disk accesses. The buffer size is specified as an integer expression in the RESET or REWRITE procedure. It will be rounded up to a multiple of 128. If storage is plentiful, buffers of 4096 or 8192 bytes will improve processing. The builtin procedures/functions used in sequential disk file processing are RESET open file for input REWRITE open file for output CLOSE terminate file processing READ, READLN read data into storage WRITE, WRITELN write data to disk EOF end of file function EOLN end of line function ERASE delete a file RENAME rename a file Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -77- This sample program reads in a file and dumps it in hex format to the console: PROGRAM DUMP; TYPE BLOCK = ARRAY [1..16] OF CHAR; NAME = ARRAY [1..14] OF CHAR; VAR B : BLOCK; DUMP_FILE : FILE OF BLOCK; FILENAME : NAME; BEGIN WHILE TRUE DO (* INFINITE LOOP *) BEGIN WRITE('enter filename : '); READLN( FILENAME ); RESET( DUMP_FILE, FILENAME, BINARY, 4096); WHILE NOT EOF( DUMP_FILE ) DO BEGIN READ( DUMP_FILE; B); WRITELN( HEX$(B) ); END; CLOSE( DUMP_FILE ); WRITELN; END; END. Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -78- 7.3 Random file processing CP/M version 2.2 or higher is required to use JRT Pascal random file processing. For many types of processing it is not known in advance in which sequence the records of a file will be needed. A spelling dictionary or online inquiry customer database obviously must use random access files. In JRT Pascal, random access is fully supported. Data can be read and updated by providing the relative record number (RRN) within the file for fixed length records. The first record is at RRN=0. For variable length records, the data can be read or updated by providing the relative byte address (RBA). The RBA is the location of the data item within the file. The first byte is at RBA=0. The RBA mode of processing gives much greater flexibility than RRN. If all records had to be the same size, then all would have to be the size of the largest, resulting in much wasted space and slower access. Beginning with JRT Pascal version 2.1, random files up to the CP/M maximum of 8 megabytes are supported. The RBA or RRN value may be an integer or a real expression. Programs written under earlier versions are source code compatible but must be recompiled using the version 2.1 or later compiler. The procedures used in random file processing are: OPEN open or create random file CLOSE terminate file processing READ read data into storage WRITE transfer data to disk ERASE delete a file RENAME rename a file A sample program shows random access to a file containing sales information for the various departments of a retail store. The records are located by department number. Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -79- Sample program: PROGRAM INQUIRY; LABEL 10; TYPE DEPT_RECORD = RECORD INVENTORY : REAL; MTD_SALES : REAL; YTD_SALES : REAL; DISCOUNT : REAL; END; VAR INPUT_AREA : DEPT_RECORD; DEPT_FILE : FILE OF DEPT_RECORD; DEPT : INTEGER; BEGIN (* INQUIRY *) OPEN( DEPT_FILE, 'C:DEPTDATA.RND', BINARY ); REPEAT WRITE('Enter dept number : '); READLN( DEPT ); IF DEPT = 999 THEN GOTO 10; (* EXIT *) READ( DEPT_FILE, RRN, DEPT; INPUT_AREA ); WRITELN; WRITELN('dept ',DEPT, ' inv ',INPUT_AREA.INVENTORY:9:2, ' disc ',INPUT_AREA.DISCOUNT:9:2); WRITELN(' MTD sales',MTD_SALES:9:2, ' YTD sales',YTD_SALES:9:2); WRITELN; 10: (* EXIT LABEL *) UNTIL DEPT = 999; CLOSE( DEPT_FILE ); END (* INQUIRY *). Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -80- 7.4 Indexed file processing CP/M version 2.2 or higher is required to use JRT Pascal indexed file processing. Beginning with version 3.0, JRT Pascal provides full support for indexed files. The index file system is implemented as 2 external procedures so that it occupies no main storage when it is not being used. Indexed files consist of two separate disk files: the main data file with a filetype of DAT and an index file with the filetype of IX0. The indexed file system has 3 components. INDEX0 external procedure performs most of the functions. INDEX1 external procedure compresses the data files and rebalances the indexes. The INDEX2 program is executed by itself and reorganizes the files for more efficient access. The external procedure INDEX0 performs these operations: A add a new record B read first record (beginning) C close file D delete a record F flush buffers, close and reopen files N new file allocation O open file Q query whether indexes should be balanced R read a record S read next record in sequence U update a record W issue a warning Z turn off warning message INDEX1 performs these operations: J rebalance the indexes K compress data file and balance indexes Records must all be the same size - from 16 to 2048 bytes. They need not be a multiple of 128 bytes. The maximum number of records depends on the key size: Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -81- (1024 DIV (KEY_SIZE +3)) * 256 key size max records ________ ___________ 4 32767 <--- Not more than 32767 6 28928 records ever allowed 8 23808 15 14336 The maximum number of records should be set to somewhat less than the maximum theoretical number of records, to prevent the loss of a record when adding to an unbalanced file. Note also that the file of indexes will be 257k when the maximum number of records are entered, so a reasonable (high) estimate should be used for the maximum number of records. IMPORTANT - No key should contain all zeroes, since a zero key is used to indicate deleted keys and records. The key must be the first field in each record. The key size may be from 2 to 32 bytes. A utility program INDEX2 is provided to reorganize the data file and generate new index files. 7.4.1 Index file format The index file is divided into one primary index and up to 256 secondary indexes. Each index block is 1024 bytes. The primary index contains 256 4-byte fields. Each of these is the first 4 bytes of the lowest key in a secondary index. The secondary indexes contain actual key values and 3-byte record loacator fields. The number of keys per secondary index is: 1024 DIV (KEY_SIZE +3) Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -82- 7.4.2 Data file format The data file consists of a 1024 byte control record followed by the data records. The control record contains the filename, maximum record count, current record count, key size, record size, delete count, and deleted record list. Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -83- Index file format !-------------------------------! ! ! 1 K blocks ! primary index ! !-------------------------------! ! ! ! ! !----- up to 256 --------! ! ! ! secondary indexes ! !----- --------! ! ! ! ! !-------------------------------! Data file format !-------------------------------! ! ! ! control record 1 K ! !-------------------------------! ! ! ! ! ! ! ! ! ! data records ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !-------------------------------! Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -84- 7.4.3 Using INDEX0 The indexed file system is implemented in an external procedure named INDEX0. To access it, these declarations are required in your main program: TYPE KEY_TYPE = -------------- { your key type declarations } RECORD_TYPE = ----------- { your record type declarations } INDEX_RECORD = RECORD DISK : CHAR; FILENAME : ARRAY [1..8] OF CHAR; RETURN_CODE : INTEGER; RESERVED : ARRAY [1..200] OF CHAR; END; PROCEDURE INDEX0 ( COMMAND : CHAR; VAR KEY : KEY_TYPE; VAR DATA : RECORD_TYPE; VAR IR : INDEX_RECORD ); EXTERN; To use INDEX0 the index_record must be initialized with the filename and disk on which the file is located. The return code is set by INDEX0 and indicates if each operation was successfully completed. Warning messages may optionally be issued, see command 'W'. An indexed file must be allocated before it can be opened or used in any way. Each time INDEX0 is called, a valid command code must be passed. The key, data, and ir parameters are also required, although key and data will not be used by every command. It is allowed to have multiple indexed files open at the same time. Each one is indentified by a different index_record. The index record (IR) should be set to blanks before individual fields are initialized. For a given index file, the first call to INDEX0 in a program should be to open ('O') or create ('N') the index and data files. (INDEX0 can be called with the 'W' first, so that error messages will be printed.) Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -85- 7.4.4 INDEX commands Commands J and K are processed by INDEX1. All others are processed by INDEX0. A add a new record - insert a new key into index, if duplicate key exists, abort operation - write new data record to data file B read first record (begin) - read the first record (in sorted order) - returns key and record C close indexed files - this MUST be done on completion of processing or newly written data may be lost D delete a record - nullify key entry for record - add record locator to delete list F flush buffers, close and reopen files - flush buffers that have changed - close files to preserve changes J rebalance indexes (INDEX1) - uses temporary file - deletes old index file - renames new index file K rebalance indexes and compact data file (INDEX1) - uses temporary files - deletes old index and data files - renames new index and data files - reopen files for further processing N new file allocation - program will inquire at the console the parameters of the new indexed file 1. record size in bytes 2. key size in bytes 3. maximum number of records to be allowed; the index file will be allocated based on this number Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -86- - index files are left open for further processing - files must be closed (or flushed) to preserve the new contents O open indexed files - open the index and data files - load the primary index into dynamic storage Q query data base status - return 'Y' in key[1] if the data base should be reorganized ('J') - else return 'N' in key[1] R read a record - search the indexes for the key - read the data record into the user's record variable S read next record in sequence - will read next record after a previous 'B', 'R', 'S', or 'U' U update a record - the update operation MUST ALWAYS be preceded by a read operation with the same key - write modified record to data file W warning messages - turn on the warning message feature - caused non-zero return codes to print verbal error messages Z turn off warning messages Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -87- 7.4.5 INDEX return codes 0 successful completion 1 duplicate key 2 maximum number of records exceeded 3 key not found 4 update key does not match read key or previous read was not successful 5 key value does not match key in record 6 second open or new without closing previous file 7 invalid command (eg. 'M' or an 'S' without a preceeding 'B', 'R', 'S', or 'U') 8 file not open 9 serious error ( no specific message ) 7.4.6 Balanced indexes Searching for records is usually very efficient, both in random and sequential modes. Adding to a data base is usually efficient until one or more of the secondary indexes gets full. (If records are added in sorted order, then the addition process will be very efficient.) INDEX0 will not automatically "balance" keys in the index files, so that additions fill up the secondary indexes. Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -88- Your program can "Query" the status of an indexed file by using 'Q' in a call to the index. The first letter of the key will be set to 'Y' if the indexes should be balanced, and 'N' if that is not necessary yet. (INDEX0 decides that the indexes should be balanced when an add ('A') must move a secondary index from one block to another). Reorganizing indexes To reorganize an indexed file so that adding new records will be efficient, set the record argument to all blanks and call INDEX1 with command 'J' (for adJust or Justify). INDEX1 will create a new balanced index file on the same disk as the current index file. There must be space for the new index file, which will be called name.$$I. INDEX1 will then delete the old .IX0 file and rename the new file to name.IX0. Reorganization takes 2500 to 3200 bytes of space in main memory as well as space on the disk, so it is never done automatically. INDEX1 must be declared as an external procedure (just as INDEX0 was declared) if your program is going to balance indexes "on the fly". PROCEDURE INDEX1 ( COMMAND : CHAR; VAR KEY : KEY_TYPE; VAR DATA : RECORD_TYPE; VAR IR : INDEX_RECORD ); EXTERN; INDEX1 supports the J and K operations which are described in section 7.4.4. In general, the record variable should be set to all blanks before INDEX1 is called. 7.4.7 INDEX2 utility Type EXEC INDEX2 to rebalance the indexes in the file and to compact the data after many deletions. INDEX2 will ask for the name of the disk drive containing the indexed files (A to P), the name of index files (which you would enter without any '.' or '.DAT' or '.IX0'), and the name of the compacted files. You can have the new files put on the same or another disk drive as the original files. Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -89- INDEX2 will also ask for a new number of maximum records. If you enter 0, the previous maximum will be used. Compressing data from within a program INDEX2 uses INDEX0 and INDEX1 to perform the actual indexed file accesses. Highly sophisticated programs can also use INDEX1 to compact the data file as well as balance the indexes. Call INDEX1 with the command 'K' (kompress) to do a complete reorganization. If the record argument is set to all blanks, then the same disk drive and same maximum record count will be used in creating the new data base copies. If the record argument is given the following structure, then alternate disk drives or a different maximum number of records can be set. VAR new_param : RECORD new_disk_flag : CHAR; new_disk : CHAR; max_nr_flag : CHAR; max_nr_rec : INTEGER; old_leave : CHAR; END; Set new_param.new_disk_flag to 'Y' if new_param.new_disk contains another disk drive letter (such as 'C'). Set new_param.max_nr_flag to 'Y' if new_param.max_nr_rec contains a new maximum number of records, such as 2000. The new_disk_flag only works with the 'K' option. The old_leave flag only works with the 'K' option when a new_disk is specified. When the 'K' option is used, the record passed must be big enough to hold records read from the disk. You might want to assign rec to contain new_param, and then call INDEX1, for example Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -90- rec := new_param; INDEX1 ('K',key, rec, ir); Most programs will not need to use the 'K' option, since the equivalent can be done as needed by having the user issue the CP/M command EXEC INDEX2, preferably after the data bases have been copied to backup disks. 7.4.8 Efficiency notes Reading records from the data base is only slow when very many keys have the same first four characters. If the indexes in more than one secondary index block have the same first four characters, INDEX0 may have to search more than one secondary index block to find a given record. Generally, this will not occur. Random output in general under CP/M is inefficient due to buffering requirements. Random output will be most efficient with double density disks with 1K blocks or with single density disks with 128 blocks. Maximum number of records The maximum number of records should be set to somewhat (50 to 200) less than the theoretical maximum. If, for example, 8-byte keys are declared with up to 23808 records, 256 records are entered, the indexes are balanced (with 'J'). There will now be 256 secondary indexes blocks with one key each. Then, if 92 records are added with key greater than the 256th record, the last secondary index will be full. Since one secondary index block can hold 93 8-byte keys, adding a 93rd key larger than the 256th will "overflow" the top secondary index block. A serious error. Currently, the maximum number of records is 32767 for index files with 2-, 3-, and 4-byte keys. Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -91- 7.4.9 Sample indexed file program The following simple program will let you create, add to, query, close, and search any data base. It assumes that the record and the key are alphanumeric (printable) information. You can enter individual commands to the program, which will call INDEX0 (or INDEX1) to perform the equivalent command. The runtime example that follows the listing of TSTINDEX shows the creation of a simple address file, with 16 character search keys and (one line) addresses up to 80 characters long. The resulting records are then 96 bytes long. Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -92- PROGRAM tstindex; TYPE key_t = ARRAY[1..256] of CHAR; rec_t = ARRAY[1..2048] of CHAR; ctrl_rec = RECORD c_1 : ARRAY[1..4] of INTEGER; rec_size : INTEGER; c_2 : INTEGER; key_size : INTEGER; end; index_record = RECORD disk : CHAR; filename : ARRAY[1..8] of CHAR; return_code : INTEGER; res_1 : INTEGER; ctrl : ^ctrl_rec; reserved : ARRAY[1..196] of CHAR; END; VAR key : key_t; rec : rec_t; cmd : CHAR; ir : index_record; tem_d : ARRAY[1..2048] of CHAR; PROCEDURE INDEX0 ( command : CHAR; var key : key_t; var rec : rec_t; var ir : index_record ); extern; PROCEDURE INDEX1 ( command : CHAR; var key : key_t; var rec : rec_t; var ir : index_record ); extern; Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -93- BEGIN (* tstindex *) ir := ' '; write('Disk: '); readln(ir.disk); write('File: '); readln(ir.filename); REPEAT write('cmd: '); readln(cmd); cmd := upcase(cmd); key := ' '; rec := ' '; IF (cmd in ['A', 'D', 'R', 'U']) THEN BEGIN write('key: '); readln(key); IF (cmd in ['A', 'U'] THEN BEGIN write('data: '); readln(tem_d); rec := copy(key, 1, ir.ctl^.key_size) + copy(tem_d, 1, ir.ctl^.rec_size - ir.ctl^.key_size); END; END; (* justify or kompress must call INDEX1 *) IF (cmd in ['J', 'K'] THEN BEGIN rec := ' '; INDEX1(cmd, key, rec, ir); END ELSE INDEX0(cmd, key, rec, ir); IF (ir.return_code <> 0) THEN BEGIN writeln('Error:', ir.return_code); END; IF (cmd = 'Q') THEN writeln('query result: ',key[1]); IF (cmd in ['B', 'R', 'S']) THEN BEGIN writeln('key: ', copy(rec, 1, ir.ctl^.key_size)); writeln('data: ', copy(rec, ir.ctl^.key_size + 1, ir.ctl^.rec_size - ir.ctl^.key_size)); END; UNTIL (cmd = '?'); END. (* tstindex *) Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -94- Execution of TSTINDEX is shown for a simple data base with 16 character names and up to 96 characters of information (which happens to be addresses). Note that the key length and record length are entered from the terminal in the N command. A>EXEC B:TSTINDEX Exec ver 3.0 Disk: B File: ADDRESS cmd: W cmd: N Record size in bytes: 96 Key size in bytes: 16 Maximum number of records: 500 cmd: A key: JRT data: 'JRT Systems/45 Camino Alto/Mill Valley, CA 94941' cmd: A key: OLD data: 'Old JRT Office/550 Irving St/SF, CA 94122' cmd: B key: JRT data: JRT Systems/45 camino Alto/Mill Valley, CA 94941 cmd: S key: OLD data: Old JRT Office/550 Irving St/SF, CA 94122 cmd: S %INDEX error: Key not found Error: 3 cmd: a key: LITTLE data: 'Little Italy/4109 24th St/SF, CA 94114' cmd: a key: SZECHWAN data: 'Szechwan Court/1668 Haight St/SF, CA 94117' cmd: f cmd: r key: JRT key: JRT data: JRT Systems/45 Camino Alto/Mill Valley, CA 94941 cmd: r key: OTHER %INDEX error: Key not found return code 3 cmd: z Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -95- cmd: ? Error: 7 Program termination Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -96- 7.5 CLOSE Format CLOSE ( file_variable ); The CLOSE builtin procedure terminates processing against a sequential or random disk file. If a sequential output file is not properly closed, the data written out will be lost because CLOSE updates the disk directory. This procedure also releases storage reserved for input/output buffers of sequential files. Examples: CLOSE ( F1 ); CLOSE ( DATA_FILE ); CLOSE ( MASTER_CUSTOMER_REPORT ); Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -97- 7.5.1 EOF Format EOF ( file_variable ); The end of file function indicates when the end of a file is reached during input processing. It returns a Boolean value of true immediately after end of file detection, otherwise it returns false. The EOF function has no meaning in console or random disk processing. When processing a file in text mode, end of file is detected when all data up to the first CTRL-Z (1AH) has been read. This is the standard character to indicate the end of data. When processing a file in binary mode, end of file is detected when all the data in the last allocated sector of the file has been read. Examples: 1. (* COMPUTE THE AVERAGE OF A FILE OF NUMBERS *) RESET( F1, 'DAILY.SAL', TEXT, 4096); TOTAL := 0; COUNT := 0; WHILE NOT EOF(F1) DO BEGIN READ(F1; DAILY_SALES); TOTAL := TOTAL + DAILY_SALES; COUNT := COUNT + 1; END; AVERAGE := TOTAL / COUNT; CLOSE( F1 ); 2. (* WRITE A FILE TO THE PRINTER *) SYSTEM( LIST ); RESET( F1, 'TEST.PAS', BINARY, 2048 ); READ(F1; CH); (* INSTEAD OF USING EOF, WE DIRECTLY TEST FOR A CHARACTER 1AH, SINCE THIS IS BINARY FILE *) WHILE CH <> CHR(1AH) DO BEGIN WRITE( CH ); READ(F1; CH); END; CLOSE( F1 ); Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -98- 7.6 EOLN Format 1 EOLN ( file_variable ); Format 2 EOLN; The end of line function returns a Boolean value true if the end of line is reached, otherwise it returns false. This function applies only to console and text files, not to binary files. Format 1 is used to sense end of line while reading disk files. Format 2 is used to sense end of line in console input. This function is used primarily to read in an unknown number of data items from a line of text. Executing a READLN, with or without any parameters, always resets EOLN to false and positions the file at the start of the next line of text. Examples: 1. (* READ NUMBERS FROM CONSOLE, COMPUTE AVG *) TOTAL := 0; COUNT := 0; WHILE NOT EOLN DO BEGIN READ( NUMBER ); TOTAL := TOTAL + NUMBER; COUNT := COUNT + 1; END; READLN; AVERAGE := TOTAL DIV COUNT; 2. (* READ DATA FROM FILE, COUNT LINES OF TEXT *) LINE_COUNT := 0; WHILE NOT EOF(F1) DO BEGIN READ(F1; DATA_ITEM); PROCESS_DATA( DATA_ITEM ); IF EOLN(F1) THEN BEGIN LINE_COUNT := LINE_COUNT + 1; READLN(F1) END; END; Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -99- 7.7 ERASE Format ERASE ( filename ); The ERASE procedure deletes files from the disk. It can be used to delete files from any available disk by including the disk identifier in the filename. ERASE is implemented as an external procedure. Any program referencing it must include it declaration: PROCEDURE ERASE ( NAME : STRING[20] ); EXTERN; Examples: ERASE( 'TESTPGM.PAS' ); ERASE( CONCAT( 'B:', FILENAME, FILETYPE ) ); ERASE( 'A:' + NAME + '.HEX' ); ERASE( BACKUP_FILE ); Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -100- 7.8 GET Format GET ( file_variable ); This standard Pascal procedure moves the next data item from the sequential file into the file's buffer variable. If there is not another data item in the file then the EOF function becomes true. The READ procedure allows reading directly from a file into any variable. READ ( F; X ); is equivalent to: X := F^; GET ( F ); Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -101- 7.9 OPEN Format 1 OPEN ( file_variable, filename, BINARY ); Format 2 OPEN ( file_variable, filename, TEXT ); The OPEN builtin procedure is used to open files for random access. Format 1 is used to open files in binary mode. Format 2 is used to open files in text mode. The file_variable refers to a file variable declared in the VAR declaration section. The filename is a string or structured expression which may include disk identifier letter. The file specified by the filename is opened for use if present. If not present, a new file is created. Both formats may be used with both RRN and RBA accessing. Examples: OPEN ( INVENTORY, 'INVENTRY.DAT', BINARY ); OPEN ( F1, RANGE + '.DAT', TEXT ); OPEN ( CASE_HISTORY, 'D:TORTS.LIB', BINARY ); OPEN ( DICTIONARY, 'B:SPELLING.LIB', BINARY ); Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -102- 7.10 PICTURE The external function PICTURE allows you to format (real) numbers in powerful ways. Check printing is easy, as are commas within a number and exponential notation. Floating (or fixed) dollar signa are easy to specify. Credit and debit indications can be included. Literal characters such as currency signs can also be put in the formatted string. COBOL and PL/I programmers will find familiar features such as with trailing signs. PICTURE takes a format string and a real number as arguments. It returns a formated string, which can be printed on the console, the line printer, written to a file, concatenated with other strings, or saved for further processing. For example, RES$ := PICTURE("*$##,###.##", 1456.20); WRITELN ("Sum: ", PICTURE("###,###.### ###", 6583.1234567)); will set RES$ (which should be declared as a string or array of characters) to the eleven characters **$1,456.20 and next write a line consisting of the twenty characters Sum: 6,583.123 456. PICTURE is supplied as a compiled function (the file PICTURE.INT). PICTURE must be declared in any program that uses it as FUNCTION PICTURE (FMT : STRING; R : REAL) : STRING; EXTERN; The format string is not hard to create. PICTURE generally puts one character in the result string for every character in the format string, the exceptions marked with a *. The format characters are summarized below. Note that you will usually need only pound signs, commas, and periods in your formats. Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -103- Format Replaced with 0 Literal zero (used only with exponential notation 9 A decimal digit (always) B Space (or fill character) CR CR if the number is positive, else spaces DB DB if the number is negative, else spaces E Exponent (consisting of E, sign, and two digits) (*) E+## Exponent (sign and digit indications are ignored) (*) L Literal L (as a currency sign) S Minus or plus sign V Implied decimal point (*) Z Digit or fill character - Minus sign if negative, else space + Plus sign if positive, else minus sign # Digit or fill character % Digit or fill character * Asterisk fill ** Asterisk fill and one digit *$ Asterisk fill and floating dollar sign **$ Asterisk fill, floating dollar sign, and one digit , Comma if digit has already been formated, else space / Literal / (or fill character) : Literal : (or fill character) space Literal space (or fill character) ^ Exponent (E, sign, and two digits) (*) ^^^^ Exponent (*) _ Next character is included literally (*) _* or * A single asterisk (*) _$ or $ A single dollar sign (*) Examples (our favorite formats) -#.### ###^^^^ Large and small numbers $##.## Price of JRT Pascal ###,### Number of happy customers *$###,###.## Checks (especially pay checks) -##,###,###,###,###.## Change in the national debt Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -104- In general, PICTURE can use any format with legal characters. It is possible to create ridiculous formats, such as "-+". An appropriate matching string will be returned (either space, plus, or minus in this case). If the format contains and invalid format character, PICTURE will complain and will return a two character string ?? Upper case and lower case letters are equivalent in the format, so E or e can be used for the exponent. Simple number formating Pound signs (#) are usually used to indicate where digits should be placed. A decimal point indicates where the decimal point should go. PICTURE does NO rounding, but just truncates insignificant digits. (The vertical bar just indicates the start of the result in the following examples, and will not be included in the actual result). Format Number Result Length | ##### 15000 15000 5 | -2.6 -2 5 | -17.98 -17 5 | ###.## 29.95 29.95 6 | -10.756 -10.75 6 Punctuation Commas can be inserted in the formated number. A comma in the format will cause a comma AT THE CORRESPONDING POSITION if a digit has already been put into the result in a position to the comma position. If no significant digit has been seen, then a space or asterisk is substituted. Note that PICTURE DOES NOT automatically put commas every third position. You can place commas in any meaningful (or meaningless) position in your number. Format Number Result Length | ###,### 2470 2,470 7 | #,### -999 -999 5 | #,###### 2743562 2,743562 8 COUNT YOUR COMMAS AND DIGITS. Commas can be used after the decimal point if desired. Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -105- A space (or B) works exactly the same as commas for those of you who want to punctuate numbers with spaces instead of commas. Note that this is different from the PRINT USING statement in Basics, which treat blanks as delimiters. Exponential Notation Exponential notation is indicated either with an uparrow (^) or the letter E. Following uparrows, signs, and digit indicators are ignored, so you can use ^^^^ or E+##. The formated exponent ALWAYS takes four characters: the letter E, the sign of the exponent, and two digits. If you want PICTURE to create numbers in exponential notation with a leading 0 before the decimal point, you can use the digit 0 in a format before the decimal. Format Number Result Length | #.###^ 15000 1.500E+04 9 | -2.5 -.250E+01 9 | ###.####^ 15000 150.0000E+01 12 | -2.5 -25.0000E-01 12 | ###.####E+## -2.5 -25.0000E-01 12 | 0.### ###^^^^ 15000 0.150 000E+05 13 Signs Normally, PICTURE will put a minus sign before the first significant digit in a number if that number is negative. This is called a floating sign, and will take up one digit position. You can have PICTURE handle the sign in many other ways. To put the minus sign (or blank) in a fixed position, use a - in the format. The minus sign can be before the first significant digit or at the end of the number. To put a negative or positive sign in a fixed position, use a plus sign (+) or an S instead of the minus sign. Format Number Result Length | -#### -12 - 12 5 | 134 134 5 | ####+ -12 12- 5 | 134 134+ 5 Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -106- With exponential notation, you will generally want to specify the location of the sign, since a floating sign will cause one less digit before the decimal to be printed WITH NEGATIVE NUMBERS than with POSITIVE NUMBERS. Format Number Result Length | -0.### ###^^^^ 15000 0.150 000E+05 14 | -15000 -0.150 000E+05 14 | -#.######^^^^ 15000 1.500000E+04 13 | -.###^ 15001 .150E+04 9 | +.###^ 15001 +.150E+04 9 | -2.506 -.250E+01 9 | .###-^ 15001 .150 E+04 9 | -2.506 .250-E+01 9 Note that you can put the sign in a number of inappropriate places and can even have the sign appear more that once. Dollar signs and check printing Floating dollar signs and asterisks fill work in a straightforward manner, and will produce the sort of results you would want for printing dollar sign amounts or checks. To enter a $ or * at a fixed position, use one of the "literal next" characters, the underline (_) or backslash () before the $ or *. Format Number Result Length | _$##,###.## 2745.23 $ 2,745.23 10 | $##,###.## 2645.23 $2,745.23 10 Note that the **, $$, and **$ formats are optional in JRT Pascal's PICTURE function. They are equivalent to *#, $#, and *$#, respectively The only exceptions to the "one format character, one result character" rule are 1) the two "literal next" characters (_ and ) which do not appear in the result 2) the V, which is not printed 3) the two exponent characters (^ and E) which always take four characters (and which cause following ^, +, -, #, and 9 specifications to be ignored in the format). Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -107- Overflow Overflow occurs when the number to be formated cannot fit in the format provided, as when 1000 is to be formated in a three digit field (###). When that happens, PICTURE puts a % in place of ALL digits. In exponential notation, the only cause of overflow is with negative numbers when no sign is indicated and no digits are allowed before the decimal point. Format Number Result Length | -## 200005 %% 3 | ###### -40000102 -%%%%% 6 | *$#,### 400102 *$%,%%% 7 | .###^ -207 .%%%E+03 8 Testing formats for PICTURE Here is a routine you can use to test your own PICTURE specifications. (We use an extension of this program that allows file input and output to test ours.) The program reads the number of real digits to be formated and the numbers to be formated. It then reads one format specification at a time an prints each of the numbers in that format. PROGRAM TESTPICT CONST MAX_REAL = 100; VAR I : INTEGER; NR_REALS : INTEGER; PIC : STRING; REAL_ARR : ARRAY[1..MAX_REAL] OF REAL; EXTERN PICTURE ( FMT : STRING; R : REAL) : STRING; EXTERN; BEGIN REPEAT WRITE('Number of real numbers to format: '); READLN(NR_REALS); UNTIL (NR_REALS < MAX_REAL); FOR I := 1 TO NR_REALS DO READ(REAL_ARR[I]); READLN; Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -108- REPEAT WRITE('Format: '); READLN(PIC); IF(PIC <> '*') THEN FOR I := 1 TO NR_REALS DO BEGIN WRITELN(I:3, ' ', | REAL$(REAL_ARR[I], ' ', PICTURE(PIC, REAL_ARR[I]), | ' '); END; UNTIL (PIC = '*'); END. Note that currently, JRT Pascal requires that real numbers entered in exponential form must have a exponent sign and two exponent decimal digits. This restriction will be relaxed in the future. Formats for ex-COBOL and PL/I programmers The format character V can be used to set an implied decimal point without printing one. (V. and .V can also be used. The . will always be included in the result. Z can be used in place of #, and 9 can be used to force printing of a digit. The "literal" / and : can be used. They will be replaced by the fill character (space or *) if appropriate. Multiple + and - signs can be used in place of # to cause floating signs. Subtle differences between JRT Pascal's PICTURE and other languages will be found. Use the TESTPICT routine to experiment as needed. Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -109- 7.11 PUT Format PUT ( file_variable ); This standard Pascal procedure appends the current value of the buffer variable to the sequential file. The WRITE procedure allows writing directly to a file from any variable. WRITE ( F; X ); is equivalent to: F^ := X; PUT ( F ); Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -110- 7.12 READ, READLN Format 1 (console) READ/LN (variable1, variable2,... ); Format 2 (sequential disk) READ/LN ( file_variable ; variable1, variable2,... ); Format 3 (random disk) READ/LN ( file_variable, RRN, integer_or_real_expr ; variable1, variable2,... ); Format 4 (random disk) READ/LN ( file_variable, RBA, integer_or_real_expr ; variable1, variable2,... ); The READ standard procedure is used to bring data from console or disk into main storage. Format 1 is used for reading data from the console keyboard. When it is executed it will obtain data from the console buffer, convert it to the proper format, and store the data in the specified variables. If sufficient data is not available, the system will wait for more data to be keyed in. If data is keyed in with unacceptable format, a warning message is issued. Dynamic string variables may only be used in READ format 1 - in console input - and not in disk file input. To read character data from disk files, arrays of characters or records may be used. Reading from the console into a dynamic string variable is treated differently. An entire line of text is obtained from the console and moved directly into the string variable. Separator characters and single quotes are ignored. The system will not allow more characters to be keyed in than can fit into the variable. The string variable must be the only variable in the READ's parameter list. When all data on a given input line has been read in, the EOLN function becomes true. The READLN procedure has the additional purpose of resetting EOLN to false. READLN always clears out the current input line. For example, if 5 numbers were keyed in on one line and a READLN were issued with 3 variables in its parameter list, the last 2 numbers on that line would be lost. Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -111- Format 2 is used to read in data from a sequential disk file. Whether the file is processed as text or binary data is specified when the file is opened (RESET). The file_variable must refer to a file which has been successfully opened or a run-time error will occur. Note that JRT Pascal uses a semicolon after the file_variable rather than a comma. Format 3 is used to read in data from a random file by giving the relative record number (RRN) of the record required. The first record number is at RRN=0. The file must have been successfully opened with the OPEN procedure. Sequential and random file accesses cannot be mixed unless the file is first closed and then re-opened in the other mode. The size of records on the file for RRN processing is determined when the file is declared. For example, a FILE OF REAL has a record size of 8 bytes. Format 4 is used to read data from a random file by giving the relative byte address (RBA) of the data item required. The first byte of the file is at RBA=0. The file must have been successfully opened with the OPEN procedure. Random processing cannot be mixed with sequential processing but RRN and RBA processing can be mixed without re-opening the file. Examples: READLN( A, B ); READ( DATA_FILE; X_DATA, Y_DATA ); READ( HISTORY_FILE, RRN, YEAR; MAJOR_EVENT ); READ( INQUIRY_FILE, RBA, 0; INDEX ); READLN; (* RESET EOLN *) Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -112- 7.13 RENAME Format RENAME ( old_name, new_name ); The RENAME procedure is used to rename disk files on any disk. The old_name and new_name are string expressions. RENAME is implemented as an external procedure. Any program referencing it must include it declarations: PROCEDURE RENAME ( OLD, NEW1 : STRING[20] ); EXTERN; Examples: RENAME( 'C:TEST.PAS', 'TEST2.PAS' ); RENAME( OLD_FILE_NAME, NEW_FILE_NAME ); RENAME( DISK + OLD_NAME, NEW_NAME ); RENAME( 'SORT.BAK', 'SORT.PAS' ); Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -113- 7.14 RESET Format 1 RESET ( file_variable, filename, BINARY, bufr_size ); Format 2 RESET ( file_variable, filename, TEXT, bufr_size ); The RESET standard procedure is used to open already existing files for sequential input. IMPORTANT CHANGE from version 2.2 to version 3.0 of JRT Pascal: RESET now sets the EOF function to true and issues a warning message if the file does not exist on disk. It used to cause the old program to terminate with an error. NOTE: All programs should now test EOF immediately after RESET. Format 1 is used to open files in binary mode. Format 2 opens files in text mode. The file_variable refers to a file variable declared in the VAR declaration section. The filename is a string or structured expression which may include disk identifier letter. The bufr_size is an integer expression which indicates the size of the input buffer to be allocated in dynamic storage. When storarage is available, larger buffers are preferred because they result in fewer disk accesses and thus faster processing. The buffer size is rounded up to a multiple of 128. Values like 1024, 2048 and 4096 are recommended for bufr_size. Examples: RESET( INPUT_FILE, 'SOURCE.PAS', BINARY, 1024 ); RESET( LOG, 'B:LOG.DAT', TEXT, 2048 ); RESET( DAILY_SALES, 'C:DAILY.DAT', TEXT, 256 ); RESET( STATISTICS, 'STAT.DAT', BINARY, 1024 ); Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -114- 7.15 REWRITE Format 1 REWRITE( file_variable, filename, BINARY, bufr_size ); Format 2 REWRITE( file_variable, filename, TEXT, bufr_size ); The REWRITE standard procedure is used to open all files for sequential disk output. A new file with the given filename is allocated. If a file with that name already exists, it is deleted to free the space allocated to it. Format 1 is used to open files in binary mode. Format 2 opens files in text mode. The file_variable refers to a file variable declared in the VAR declaration section. The filename is a string or structured expression which may include disk identifier letter. The bufr_size is an integer expression which indicates the size of the input buffer to be allocated in dynamic storage. When storage is available, larger buffers are preferred because they result in fewer disk accesses and thus faster processing. The buffer size is rounded up to a multiple of 128. Values like 1024, 2048 and 4096 are recommended for bufr_size. Examples: REWRITE( LOG_FILE, 'F:LOG.DAT', TEXT, 512 ); REWRITE( REPORT, MONTH + '.RPT', TEXT, 1024 ); REWRITE( SYMBOL, PGM + '.SYM', BINARY, 256 ); REWRITE( STATISTICS, 'B:STATS.DAT', TEXT, 768 ); Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -115- 7.16 WRITE, WRITELN Format 1 (console) WRITE/LN ( variable1, variable2,... ); Format 2 (sequential disk) WRITE/LN ( file_variable ; variable1, variable2,... ); Format 3 (random disk) WRITE/LN ( file_variable, RRN, integer_or_real_expr ; variable1, variable2,... ); Format 4 (random disk) WRITE/LN ( file_variable, RBA, integer_or_real_expr ; variable1, variable2,... ); The WRITE standard procedure is used to transfer data from main storage to the console for display or to disk for storage. Format 1 is used to write data to the console or printer. The console is always considered to be a text device, i.e., data is always converted to readable text format before output. Standard ASCII control characters are supported: decimal hex purpose ------- --- -------- 9 09h horizontal tab 10 0ah line feed 12 0ch form feed, clear screen 13 0dh carriage return, end line For example, executing the Pascal statement WRITE( CHR(12) ); will clear the screen of most types of CRT terminals. The WRITELN statement is identical to the WRITE except that it also writes a carriage return character after the data, i.e., it ends the current output line. A WRITELN may be used by itself, without any variables, to write a blank line to the output device. Format 2 is used to write data to squential disk files. The file must have been successfully opened with a REWRITE procedure. This format may be used in either binary or text mode processing. Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -116- Note that JRT Pascal uses a semicolon after the file_variable rather than a comma. Format 3 is used to write data to a random file by giving the relative record number (RRN) of the record being updated or created. The first record is at RRN=0. The file must have been successfully opened with the OPEN procedure. Sequential and random file processing cannot be mixed unless the file is first closed then re-opened in the other mode. The size of records on the file for RRN processing is determined when the file is declared. For example, a FILE OF REAL has a record size of 8 bytes, the size of real variables. Format 4 is used to write data to a random file by giving the relative byte address (RBA) at which the data is to be stored. The first byte of the file is at RBA=0. The data will be stored beginning at the specified RBA and continuing until it is all written out. The file must have been successfully opened with the OPEN procedure. Random processing cannot be mixed with sequential processing but RRN and RBA processing can be mixed without re-opening the file. When processing in text mode, a convenient formatting option is available. Any of the variables in the WRITE parameter list may be suffixed with a colon and an integer expression. This specifies the field width of the data value being written. IF the data item is shorter than this then spaces will be inserted on the left of the item. This option is used when columns of figures must be aligned. A second option is available for real numbers. After the field width integer expression, a second colon and integer expression may be used to indicate the number of digits right of the decimal place to be displayed. Examples: WRITELN( 'THE TIME IS ',GET_TIME ); WRITE( DATA_FILE; X[1], X[2], X[3] ); FOR I:=1 TO 100 DO WRITE( DATA_FILE; X[1] ); IF DATA < 0 THEN WRITE( NEGATIVE_DATA; DATA ) ELSE WRITE( POSITIVE_DATA; DATA ); Copy compliments of Merle Schnick SECTION 7: Input/output JRT Pascal User's Guide version 3.0 NOT FOR SALE -117- WRITELN( REPORT; TOTAL_SALES:12:2 ); WRITE( CUSTOMER_FILE, RRN, CUST_NUM; NEW_CUSTOMER_RECORD ); WRITE( INQUIRY, RBA, 0; INDEX ); WRITELN; (* WRITE BLANK LINE *) WRITE( CHR(0CH) ); (* CLEAR SCREEN *) Copy compliments of Merle Schnick SECTION 7: Input/output LANK LINE *) WRITE( CHR(0CH) ); (* CLEAR SCREEN *)