Notes on the Pascal environment (GLOBDEFS.PAS, STDIO.PAS, STDUTIL.PAS) by Jon Dart These are "include files" that implement a programming environment similar to that used by Kernighan and Plauger in their book "Software Tools in Pascal" (Addison-Wesley, 1981). The following is a brief explanation of what these files do and why they do it in the way they do, with emphasis on the features in them that are different from K & P. For several reasons, I have chosen not to implement exactly the same environment used by K & P. One reason is that I consider I/O redirection to be unnecessary and even undesirable in some respects. For example, most of the programs written by K & P take their input from the command line. This is nice because it allows redirection (e.g. input can come from a file instead of the keyboard), but it also results in a condensed and rather cryptic command syntax. UNIX hackers may be able to remember all the one-letter switches required by their favorite programs, but ordinary mortals may have trouble with this. I prefer menu-driven programs. Another thing I have noticed is that K & P's programs tend to minimize all "unnecessary" screen output. This is related to the requirement for redirection, since it enables the program to be run as a background job (under UNIX and similar operating systems). But I like a program to let me know what it is doing while it is running. These prejudices aside, what I like about K & P's approach is the degree of machine independence it is able to achieve. Their basic idea is that you write a set of "primitives" that handle machine- dependent functions (like opening a file), and then write all your programs in terms of these primitives. This means that when the time comes to port your programs to another machine or another operating system, all you have to do (in theory) is to rewrite the primitives. For a program to be truly machine independent, it is also necessary to avoid the use of non-standard language features in the body of the program. Turbo Pascal has a lot of really nifty features like string variables, string functions, inline machine code, direct access to memory and ports, etc., but if you use any of these features you are no longer writing in standard Pascal and you are creating a big headache for yourself or for others who may want to run the same program using a different compiler. For this reason, I have avoided as much as possible the use of Turbo Pascal's non-standard extensions, except as necessary to implement the primitives in STDIO and STDUTIL. When string variables are needed, I have declared them to be of type "textline". This type is defined in GLOBDEFS.PAS as an array of type "character". The end of the string is marked with a special character (EOS). This is more convenient than Turbo Pascal's built-in strings, which store the length value at the start of the string and don't mark its end. A number of procedures are available in file STDUTIL.PAS to manipulate arrays of type "textline". One of these procedures, "setstring", allows assigning a quoted string constant to a "textline". E.g. instead of saying: s := 'This is a string'; { s is of type string } you have to say: setstring(s,'This is a string'); { s of type textline } I admit that the first form is more convenient, but it is non- standard and some compilers won't accept it. The second form is also non-standard (quoted strings aren't allowed as procedure parameters in standard Pascal), but at least it formally hides the details of how the assignment is made inside a procedure. As discussed in K & P, setstring can be implemented as a macro for compilers that won't allow quoted strings in a procedure call. For I/O operations, I assume that your computer has a keyboard, a screen, a printer, and one or more disk drives. The keyboard, screen and printer are assigned to logical devices as follows: TRMIN = the keyboard TRMOUT = the screen PRINTER = the printer. The procedure "getc" reads a single character from the keyboard, and "putc" writes a single character to the screen. Since I assume that the "standard input" is always the keyboard, and since redirection is not permitted, putc(c) is equivalent to write(chr(c)). Note that putc expects its argument to be of type "character" (defined as 0..127), while write expects its argument to be of type "char". The procedures "getcf" and "putcf" perform read and write operations to devices other than the screen and keyboard, e.g. "putcf(c,PRINTER)" writes a character to the printer. Getcf returns a special character ENDFILE when it reaches the end of a file, and it translates CR or LF into a character NEWLINE. "Getline" reads a variable of type textline from the keyboard or a file, and "Putstr" writes a variable of type textline to the screen, the printer, or a disk file. I use write and writeln only to send string constants to the screen (e.g. write('Hello')), which is allowed in standard Pascal. When a string variable (textline) has to be sent to the screen, I use putstr, because writeln doesn't recognize my type of string variable. All programs running under this environment must call a procedure "ioinit" before using the procedures in STDIO or STDUTIL. "ioinit" takes a single parameter that is the maximum number of disk files you expect to have open at one time. "ioinit" must be called even if you do not use any disk files, since it also initializes a table used by the routines "toupper", "tolower", "isupper", "islower", "isletter", "isdigit" and "isalphanum". To open a file for I/O operations, you call the procedure "open", which expects the name of the file (as a variable of type textline) and an "access mode", which can be "IOREAD", "IOWRITE" or "IOAPPEND". "IOAPPEND" allows tacking characters onto the end of an already existing file. A constant "BINARY" can also be added to the access mode. Currently the only effect of this is to prevent a ^Z from being added to the file when it is closed. "Open" returns an internal name for the file (of type filedesc), which can then be used by getcf and putcf to access the file. All disk I/O is buffered. When a read operation takes place on a file, a minimum of 1K bytes are read and stored in a buffer. When the buffer is empty, another 1K bytes are read, and so on until the end of the file. Similarly, when something is written to a file, it goes into a 2K buffer and the buffer is written to the disk only when it is full or when the file is closed. This buffering is a built-in option in the CP/M-86 and PCDOS versions of Turbo Pascal, but it is not available in the CP/M-80 version, whose built-in procedures always reads and write 128 bytes at a time. This causes the disk drive to do a lot of unnecessary seeks, especially if the program is reading input from one file and writing output to another file on the same disk. I got tired of listening to my disk drive thrash around like this, so I put in the buffered I/O feature. Because of the overhead this involves, it probably slows down program execution, but at least it is quieter and easier on the disk drives. The disk read procedure goes through some rather awkward code to test for the physical end of a file, because it was written to run under Turbo Pascal 2.0, whose "blockread" procedure doesn't return the number of blocks read. Version 3.0 does return this information, but I haven't modified the program to take advantage of this.