Previous Next Contents Index Doc Set Home


Input and Output

7


This chapter describes the Pascal input and output environments, with emphasis on interactive programming. It contains the following sections:

Input and Output Routines

page 203

eof and eoln Functions

page 204

More About eoln

page 208

External Files and Pascal File Variables

page 210

input, output, and errout Variables

page 211

Pascal I/O Library

page 213

Buffering of File Output

page 213

I/O Error Recovery

page 214


Input and Output Routines

Pascal supports all standard input and output routines, plus the extensions listed in Table 7-1. For a complete description of the routines, refer to Chapter 6, "Built-In Procedures and Functions."




Table  7-1 Extensions to Input/Output Routines

Routine
Description

append

Opens a file for writing at its end.

close

Closes a file.

filesize

Returns the current size of a file.

flush

Writes the output buffer for the specified Pascal file into the associated operating system file.

getfile

Returns a pointer to the C standard I/O descriptor associated with the specified Pascal file.

linelimit

Terminates program execution after a specified number of lines has been written into a text file.

message

Writes the specified information to stderr.

open

Associates an external file with a file variable.

read and readln

Read in boolean, integer and floating-point variables, fixed- and variable-length strings, enumerated types, and pointers.

remove

Removes the specified file.

reset and rewrite

Accepts an optional second argument.

seek

Resets the current position of a file for random access I/O.

tell

Returns the current position of a file.

write and writeln

Outputs boolean integer and floating-point variables, fixed- and variable-length strings, enumerated types, and pointers; output expressions in octal or hexadecimal; allows negative field widths.


eof and eoln Functions

A common problem encountered by new users of Pascal, especially in the interactive environment of the operating system, relates to eof and eoln. These functions are supposed to be defined at the beginning of execution of a Pascal program, indicating whether the input device is at the end of a line (eoln) or the end of a file (eof).

Setting eof or eoln actually corresponds to an implicit read in which the input is inspected, but not "used up." In fact, the system cannot detect whether the input is at the end of a file or the end of a line unless it attempts to read a line from it.

If the input is from a previously created file, then this reading can take place without runtime action by you. However, if the input is from a terminal, then the input is what you type. If the system does an initial read automatically at the beginning of program execution, and if the input is a terminal, you must type some input before execution can begin. This makes it impossible for the program to begin by prompting for input.

Pascal has been designed so that an initial read is not necessary. At any given time, Pascal may or may not know whether the end-of-file and end-of-line conditions are true.

Thus, internally, these functions can have three values: true, false, and,
"I don't know yet; if you ask me I'll have to find out." All files remain in this last, indeterminate state until the program requires a value for eof or eoln, either explicitly or implicitly; for example, in a call to read. If you force Pascal to determine whether the input is at the end of the file or the end of the line, it must attempt to read from the input.

Consider the following example:

The Pascal program, eof_example1.p, which shows the improper use of the eof function

program eof_example1;

var
    i: integer;

begin
    while not eof do begin
      write('Number, please?  ');
      read(i);
      writeln('That was a ', i: 2, '.');
      writeln
    end
end. { eof_example1 }

At first glance, this may appear to be a correct program for requesting, reading, and echoing numbers. However, the while loop asks whether eof is true before the request is printed. Thus, this system is forced to decide whether the input is at the end of the file. It gives no messages; it simply waits for the user to type a line, as follows:

The commands to compile and execute eof_example1.p

hostname% pc eof_example1.p
hostname% a.out 
23
Number, please? That was a 23.

Number, please? ^D
standard input: Tried to read past end of file
a.out terminated by signal 5: SIGTRAP
Traceback being written to a.out.trace
Abort (core dumped)

The following code avoids this problem by prompting before testing eof:

The Pascal program, eof_example2.p, which also shows the improper use of the eof function.

program eof_example2;

var
    i: integer;

begin
    write('Number, please?  ');
    while not eof do begin
      read(i);
      writeln('That was a ', i: 2, '.');
      writeln;
      write('Number, please?  ')
    end
end. { eof_example2 }

You must still type a line before the while test is completed, but the prompt asks for it. This example, however, is still not correct, because it is first necessary to know that there is an end-of-line character at the end of each line in a Pascal text file. Each time you test for the end of the file, eof finds the end-of-line character. Then, when read attempts to read a character, it skips past the end-of-line character, and finds the end of the file, which is illegal.

Thus, the modified code still results in the following error message at the end of a session:

The commands to compile and execute eof_example2.p

hostname%  pc eof_example2.p
hostname% a.out
Number, please? 23
That was a 23.

Number, please? ^D
standard input: Tried to read past end of file
Traceback being written to a.out.trace
Abort (core dumped)

The simplest way to correct the problem in this example is to use the procedure readln instead of read. readln also reads the end-of-line character, and eof finds the end of the file:

The Pascal program, eof_example3.p, which shows the proper use of the eof function.

program eof_example3;

var
    i: integer;

begin
    write('Number, please?  ');
    while not eof do begin
      readln(i);
      writeln('That was a ', i: 2, '.');
      writeln;
      write('Number, please? ')
    end
end. { eof_example3 }

The commands to compile and execute eof_example3.p

hostname% pc eof_example3.p
hostname% a.out
Number, please? 23
That was a 23.

Number, please? ^D

In general, unless you test the end-of-file condition both before and after calls to read or readln, there may be input that causes your program to attempt to read past the end-of-file.


More About eoln

To have a good understanding of when eoln is true, remember that in any file text, there is a special character indicating end-of-line. In effect, Pascal always reads one character ahead of the read command.

For instance, in response to read(ch), Pascal sets ch to the current input character and gets the next input character. If the current input character is the last character of the line, then the next input character from the file is the newline character, the normal operating system line separator.

When the read routine gets the newline character, it replaces that character by a blank (causing every line to end with a blank) and sets eoln to true. eoln is true as soon as you read the last character of the line and before you read the blank character corresponding to the end of line. Thus, it is almost always a mistake to write a program that deals with input in the following way:

This code shows the improper
use of the eoln function.

read(ch);
if eoln then
 	Done with line
 else
 	Normal processing

This program almost always has the effect of ignoring the last character in the line. The read(ch) belongs as part of the normal processing. In Pascal terms, read(ch) corresponds to ch := input^; get(input).

This code shows the proper
use of eoln.

read(ch); 
if eoln then
 	Done with line 
else begin 
	read(ch);
 	Normal processing 
	end

Given this framework, the function of a readln call is defined as follows:

while not eoln do
 	get(input);
get(input);

This code advances the file until the blank corresponding to the end of line is the current input symbol and then discards this blank. The next character available from read is the first character of the next line, if one exists.


External Files and Pascal File Variables

In Pascal, most input and output routines have an argument that is a file variable. This system associates these variables with either a permanent or temporary file at compile-time.

Permanent Files

Table 7-2 shows how to associate a Pascal file variable with a permanent file.




Table  7-2 Pascal File Variable with a Permanent File

Association
Description

With the open function

open associates a permanent file with a file variable for reading or writing. open can also determine if a file actually exists.

With the reset and rewrite functions

In Pascal, reset and rewrite take an optional second argument, a file name. If you specify the file name, the compiler opens the file and associates it with the given file variable. Any previous file associated with the file variable is lost.

With the program header

If you call reset or rewrite with a file variable f1, which is bound to a file variable declared f2 in the program header and do not specify the file name, Pascal opens a file with the same name as the variable f2. reset gives a runtime error if the file does not exist. rewrite creates the file if it does not exist.

Temporary Files

Table 7-3 shows how to associate a Pascal file variable with a temporary file.




Table  7-3 Pascal File Variable with a Temporary File

Association
Description

With the procedure:
rewrite(file_variable)

file_variable must not be declared in the program statement. This procedure creates a temporary file called #tmp.suffix, where suffix is unique to that temporary file. When the program exits or leaves the scope in which file_variable is declared, the file is deleted.

With the procedure:
rewrite(output)

The procedure creates the temporary file called #tmp.suffix, where suffix is unique to that temporary file. This file is not deleted after program execution.


input, output, and errout Variables

The input, output, and errout variables are special predefined file variables.

Properties of input, output, and errout Variables

The input, output, and errout variables are of the type text and have the following special properties:

Associating input with a File Other Than stdin-

To associate input with a file other than stdin, call reset(input, filename). Pascal opens filename and associates it with input. read and readln read from that file. For example, this line opens the file, some/existing/file, and associates it with input:

reset(input,'some/existing/file');
You must supply a file name for the association to work.

Associating output with a File Other Than stdout

To associate output with a file other than stdout, call rewrite(output, filename). Pascal opens filename and associates it with output. For example, this line associates /home/willow/test with output:

rewrite(output, '/home/willow/test');
Now, whenever you direct write or writeln to output, the output is sent to /home/willow/test. This includes the default case, when you write without giving a file variable.

If you call rewrite on output and you haven't associated output with an external file, the program creates a file with a name of the form #tmp.suffix, where suffix is unique to that file. Pascal does not delete this file after the program exits.

Associating errout with a File Other Than stderr

To associate errout with a file other than stderr, call:

rewrite (errout, '/some/new/file');
Subsequently, whenever you direct write or writeln to errout, the output is sent to /some/new/file. You obtain the same results when you write a string to errout implicitly, using the message function. See "message" on page 155 for details.


Pascal I/O Library

Each file variable in Pascal is associated with a data structure. The data structure defines the physical Solaris 2.x operating system file with which the variable is associated. It also contains flags that indicate whether the file variable is in an eoln or eof state.

The data structure also includes the buffer. The buffer normally contains a single component that is the same type as the type of the file. For example, a file of char has one character buffer, and a file of integer has one integer buffer.


Buffering of File Output

It is extremely inefficient for Pascal to send each character to a terminal as it generates it for output. It is even less efficient if the output is the input of another program, such as the line printer daemon, lpr(1).

To gain efficiency, Pascal buffers output characters; it saves the characters in memory until the buffer is full and then outputs the entire buffer in one system interaction.

For interactive prompting to work, Pascal must print the prompt before waiting for the response. For this reason, Pascal normally prints all the output that has been generated for output whenever one of the following conditions occurs:

In the following code sequence, the output integer does not print until the writeln occurs:

for i := 1 to 5 do begin
 	write(i);
 	Compute a lot with no output
end;
writeln;

Pascal performs line buffering by default. To change the default, you can compile your program with -b option. When you specify the -b option on the command-line, the compiler turns on block-buffering with a block size of 1,024. You can specify this option in a program comment using one of these formats:

{$b0}

No buffering.

{$b1}

Line buffering. This is the default.

{$b2}

Block buffering. The block size is 1,024. Any number greater than 2, for example, {$b5}, is treated as {$b2}.

This option only has an effect in the main program. The value of the option in effect at the end statement of the main program is used for the entire program.


I/O Error Recovery

When an I/O routine encounters an error, it normally does the following:

1. Generates an error message.

2. Flushes its buffers.

3. Terminates with a SIGTRAP.

Although you can set up a signal handler to trap this signal, you cannot determine which routine called the signal or the reason it was called.

With Pascal, you can set I/O trap handlers dynamically in your program. The handler is a user-defined Pascal function.

When an I/O error occurs, Pascal runtime library checks if there is a current active I/O handler. If one does not exist, Pascal prints an error message, invokes a SIGTRAP signal, and terminates.

If a handler is present, the handler is passed the values err_code and filep as in parameters. The parameter err_code is bound to the error value that caused the I/O routine to fail. The parameter filep is bound to the I/O descriptor that getfile returned for the file in which the error occurred. If filep equals nil, no file was associated with the file variable when the error occurred.

The handler returns a boolean value. If the value is false, the program terminates. If the value is true, program execution continues with the statement immediately following the I/O routine that called the trap. The results of the I/O call remain undefined.

You can set the handler to nil to return it to its default state.

The scope of the active handler is determined dynamically. Pascal has restrictions as to the lexical scoping when you declare the handler. The compiler assumes that the handler is a function declared at the outermost level. Providing a nested function as the handler may cause unexpected results. The compiler issues a warning if it attempts to take the address of a nested procedure.

To set an I/O trap handler, you must include the file ioerr.h in your Pascal source file. ioerr.h consists of an enumeration type of all possible I/O error values, a type declaration of an io_handler procedure pointer type, and an external declaration of the set_io_handler routine.

This file resides in the following directory:

/opt/SUNWspro/SC4.2/include/pascal

If the compiler is installed in a non-default location, change /opt/SUNWspro to the location where the compiler is installed.

The include file, ioerr.h

/* Copyright 1989 Sun Microsystems, Inc. */

type
      IOerror_codes = (
              IOerr_no_error,
              IOerr_eoln_undefined,
              IOerr_read_open_for_writing,
              IOerr_write_open_for_reading,
              IOerr_bad_data_enum_read,
              IOerr_bad_data_integer_read,
              IOerr_bad_data_real_read,
              IOerr_bad_data_string_read,
              IOerr_bad_data_varying_read,
              IOerr_close_file,
              IOerr_close_null_file,
              IOerr_open_null_file,
              IOerr_create_file,
              IOerr_open_file,
              IOerr_remove_file,
              IOerr_reset_file,
              IOerr_seek_file,
              IOerr_write_file,
              IOerr_file_name_too_long,
              IOerr_file_table_overflow,
              IOerr_line_limit_exceeded,
              IOerr_overflow_integer_read,
              IOerr_inactive_file,
              IOerr_read_past_eof,
              IOerr_non_positive_format
              );


io_handler = ^function( in err_code : IOerror_codes;
                      in fileptr  : univ_ptr) :
                      boolean;

procedure set_ioerr_handler(handler : io_handler); extern c;

The following program illustrates how to set an I/O trap routine.

The Pascal program ioerr.p, which defines the I/O trap routine, test_handler. This routine is called each time a runtime error occurs during an I/O operation. The #include statement includes ioerr.h in the program.

{$w-}
program ioerr_example(output);
{ This program sets and uses an I/O trap routine. }

#include "ioerr.h"
const
    NAME = 'rmc.dat';

var
    f: text;
    IO_ERROR: IOerror_codes;
    str: array [1..10] of char := 'Testing';

function test_handler(in code: IOerror_codes;
                      in fileptr: univ_ptr): boolean;

begin
    if code = IO_ERROR then begin
      writeln('ERROR HANDLER ', code);
      test_handler := true
    end else 
      test_handler := false
end; { test_handler }

begin { main program }
    set_ioerr_handler(addr(test_handler));
    { Write to an unopened file. }
    IO_ERROR := IOerr_inactive_file;
    write(f, 'This file is not open.');
    { Read a file open for writing. }
    rewrite(f, NAME);
    IO_ERROR := IOerr_read_open_for_writing;
    readln(f, str);
    remove(NAME);
    { Remove a nonexistent file. }
    IO_ERROR := IOerr_remove_file;
    remove('nonexistent.dat')
end. { ioerr_example }

The commands to compile and execute ioerr.p. When you use an I/O error recovery routine, you should compile your program with the -C option.

hostname% pc -C ioerr.p
hostname% a.out
ERROR HANDLER IOerr_inactive_file
ERROR HANDLER IOerr_read_open_for_writing
ERROR HANDLER IOerr_remove_file


Previous Next Contents Index Doc Set Home