ftp.nice.ch/pub/next/developer/languages/ada/Adaed.1.11.s.tar.gz#/Adaed-1.11.0a/predef4.c

This is predef4.c in view mode; [Download] [Up]

/*
 * Copyright (C) 1985-1992  New York University
 * 
 * This file is part of the Ada/Ed-C system.  See the Ada/Ed README file for
 * warranty (none) and distribution info and also the GNU General Public
 * License for more details.

 */
/*    +---------------------------------------------------+
      |                                                   |
      |          I N T E R P     P R E D E F S            |
      |         Part 4: Input/Output Procedures           |
      |                  (C Version)                      |
      |                                                   |
      |   Adapted From Low Level SETL version written by  |
      |                                                   |
      |                  Monte Zweben                     |
      |               Philippe Kruchten                   |
      |               Jean-Pierre Rosen                   |
      |                                                   |
      |    Original High Level SETL version written by    |
      |                                                   |
      |                   Clint Goss                      |
      |               Tracey M. Siesser                   |
      |               Bernard D. Banner                   |
      |               Stephen C. Bryant                   |
      |                  Gerry Fisher                     |
      |                                                   |
      |              C version written by                 |
      |                                                   |
      |               Robert B. K. Dewar                  |
      |                                                   |
      +---------------------------------------------------+
*/

/*  This module contains routines for the implementation of some of
 *  the predefined Ada packages and routines, namely SEQUENTIAL_IO,
 *  DIRECT_IO, TEXT_IO, and CALENDAR. Part 4 contains the initialization
 *  and termination routines for predef, and the basic I/O routines
*/

#include <stdlib.h>
#include <string.h>
#ifdef IBM_PC
#include <io.h>
#endif
#include "ipredef.h"
#include "miscprots.h"
#include "predefprots.h"

static void check_ifile_closed(int *);
static void check_xfile_closed(char *);
static void open_file();
#ifdef IBM_PC
static int stdin_is_console();
#undef putc
#define putc(A, B) fputc((A),(B));fflush(B)
#endif
static void clear_eof();
static int fgetceof(FILE *);
#ifdef DEBUG_PREDEF
static void gchar(char *, int);
static void pchar(char *, int);
#endif

/* AFCB for STANDARD_IN_FILE */

static struct afcb  in_afcb = {

	0,                          /* file descriptor for standard input */
	"",                         /* file name(null) */
	"",                         /* form string(null) */
	TIO_IN_FILE,                /* mode, TEXT_IO input */
#ifdef IO_EOF
	0,                          /* eof flag */
#endif
	0, 0, 0,                    /* unused DIRECT_IO fields */
	1,                          /* page number */
	1,                          /* line number */
	1,                          /* column number */
	0,                          /* unbounded line length */
	0,                          /* unbounded page length */
	0, "  "                    /* look ahead */
};

#ifdef IBM_PC
/* keep track of last character read from stdin so can detect whether we
 * need to flush what is left there before exiting
 */
static int last_char_input = EOF;
#endif

/* AFCB for STANDARD_OUT_FILE */

static struct afcb  out_afcb = {

	0,                          /* file descriptor for standard input */
	"",                         /* file name(null) */
	"",                         /* form string(null) */
	TIO_OUT_FILE,               /* mode, TEXT_IO output */
#ifdef IO_EOF
	0,				/* eof flag */
#endif
	0, 0, 0,                    /* unused DIRECT_IO fields */
	1,                          /* page number */
	1,                          /* line number */
	1,                          /* column number */
	0,                          /* unbounded line length */
	0,                          /* unbounded page length */
	0, "  "                    /* look ahead */
};

/* Procedure to initialize input/output data structures */

void initialize_predef()                     /*;initialize_predef*/
{
	/* Clear temporary file list, and clear AFCB vector */

	tfiles = 0;
	for (filenum = 1; filenum <= MAXFILES; filenum++)
		afcbs[filenum - 1] = 0;

	/* Setup references for current and standard files */

	current_in_file = 1;
	current_in_file_saved = 1;
	standard_in_file = 1;
	afcbs[0] = &in_afcb;
	filenum = 1;
	IOFDESC = stdin;
	CHARS = 0;

	current_out_file = 2;
	current_out_file_saved = 2;
	standard_out_file = 2;
	afcbs[1] = &out_afcb;
	filenum = 2;
	IOFDESC = stdout;

	/* Set standard exception signalled on bad data (changed temporarily
     * to CONSTRAINT_ERROR when TEXT_IO routines are called directly from
     * the main interpretor for the IMAGE attribute.
     */

	data_exception = DATA_ERROR;
}


/* CHECK_OPENED_OK */

/* Checks that an fopen succeeded, raise USE_ERROR if not */

void check_opened_ok()			                       /*;check_opened_ok*/
{
	if (IOFDESC == NULL)
		predef_raise(USE_ERROR, "Error opening or resetting file");
}

/* CHECK_IFILE_CLOSED */

/* Checks that the file object stored at file_ptr is closed */

static void check_ifile_closed(int *file_ptr)           /*;check_ifile_closed*/
{
	int     file_val;

	file_val = *file_ptr;
	if (file_val != 0)
		predef_raise(STATUS_ERROR, "File not closed");
}

/* CHECK_XFILE_CLOSED */

/* Checks that no external file with a matching name is currently open */

static void check_xfile_closed(char *fname)             /*;check_xfile_closed*/
{
	int     i;
	for (i = 1; i <= MAXFILES; i++) {
		if (afcbs[i - 1] == NULL) continue;
		if (strcmp(fname, afcbs[i - 1] -> io_fname) == 0 &&
		  afcbs[i - 1] -> io_fdesc != NULL) {
			predef_raise(USE_ERROR, "File already open");
		}
	}
}

/* CHECK_FILE_OPEN */

/* Check if the current file is open or not. If the file is not open,
  * then STATUS_ERROR is raised. Otherwise control returns normally.
*/

void check_file_open()				                       /*;check_file_open*/
{
	if (filenum == 0)
		predef_raise(STATUS_ERROR, "File not open");
}

/* If the current file is not open, then STATUS_ERROR is raised. If
 * the file is open, then the mode is checked against the argument which
 * is the desired mode for the operation. If it does not match, then
 * MODE_ERROR is raised, otherwise control returns normally.
 */

void check_status(int c_mode)			                    /*;check_status*/
{
	check_file_open();
	if (IOMODE != c_mode)
		predef_raise(MODE_ERROR, "Incorrect file status");
}

/*  Routine called by the OPEN and CREATE portions of the PREDEF procedure
 *  to perform common data structure operations for TEXT_IO operations.
 *  The operation in opn is 'C' for a create and 'O' for an open.
 */

void open_textio(char opn)                       /*;open_textio*/
{
	open_file();

#ifdef SYSTEM_V
	if (strlen(IOFNAME) > 14) predef_raise(NAME_ERROR,"Invalid file name");
#endif

	if (opn == 'C') {
		IOFDESC = fopen_txt(IOFNAME, "w");
		if (IOFDESC == NULL) predef_raise(NAME_ERROR,"Invalid file name");
		if (IOMODE == SIO_IN_FILE) {
			fclose(IOFDESC);
			IOFDESC = fopen_txt(IOFNAME, "r");
			check_opened_ok();
		}
	}
	else {                      /* opn == 'O' */
		/*
		* According to AI-00048: 
		* Opening a file with IN_FILE mode which is the default output file 
		* will raise MODE_ERROR. 
		* Opening a file with OUT_FILE mode which is the default input file 
		* will raise MODE_ERROR.
		* The values to be checked is in current_in_file_saved and
		* current_out_file_saved which are copies of the file numbers
		* associated with the default files. These copies must be used
		* because when the default files are closed their filenums saved
		* in current_XXX_file are set to zero and therefore lost for this
		* check.
		*/
		if (filenum == current_in_file_saved && IOMODE == TIO_OUT_FILE)
			predef_raise(MODE_ERROR,"File is default in file");
		if (filenum == current_out_file_saved && IOMODE == TIO_IN_FILE)
			predef_raise(MODE_ERROR,"File is default out file");
		IOFDESC = fopen_txt(IOFNAME, "r");
		if (IOFDESC == NULL) predef_raise(NAME_ERROR,"File not found");
		if (IOMODE == TIO_IN_FILE)
			CHARS = 0;
		else {
			fclose(IOFDESC);
			IOFDESC = fopen_txt(IOFNAME, "w");
			check_opened_ok();
		}
	}

	PAGE = 1;
	LINE = 1;
	COL = 1;
	LINE_LENGTH = 0;
	PAGE_LENGTH = 0;

	*get_argument_ptr(0) = filenum;
}

/* LOAD_LOOK_AHEAD */

/* This procedure loads the lookahead for a TEXT_IO input file, leaving
 * CHARS set to 3 (unless the file is less than 3 bytes long), and CHAR1
 * CHAR2 and CHAR3 containing the initial characters of the file. A special
 * exception occurs with the standard input file, where we never read ahead
 * more than one character, because of problems with interactive I/O.
*/

void load_look_ahead()				                       /*;load_look_ahead*/
{
	int c;

	/* Load first character of look ahead */

	if (CHARS == 0) {
		CHAR2 = CHAR3 = 0;
#ifdef IO_EOF
		c = fgetceof(IOFDESC);
#else
		c = fgetc(IOFDESC);
#endif

#ifdef IBM_PC
		if (IOFDESC == stdin)
			last_char_input = c;
#endif
		gchar("load_look 1", c);
		if (c == EOF) {
			CHAR1 = 0;
			return;
		}
		else {
			CHAR1 = c;
			CHARS = 1;
		}
	}

	/* Leave lookahead with one character loaded if standard input */

	if (IOFDESC != stdin) {

		/* Load second character of look ahead */

		if (CHARS == 1) {
			CHAR3 = 0;
#ifdef IO_EOF
			c = fgetceof(IOFDESC);
#else
			c = fgetc(IOFDESC);
#endif
			gchar("load_look 2",c);
			if (c == EOF) {
				CHAR2 = 0;
				return;
			}
			else {
				CHAR2 = c;
				CHARS = 2;
			}
		}

		/* Load third character of look ahead */

		if (CHARS == 2) {
#ifdef IO_EOF
			c = fgetceof(IOFDESC);
#else
			c = fgetc(IOFDESC);
#endif
			gchar("load_look 3",c);
			if (c == EOF) {
				CHAR3 = 0;
				return;
			}
			else {
				CHAR3 = c;
				CHARS = 3;
			}
		}
	}
}

/* CLOSE_TEXTIO */

/*  This routine is called by the CLOSE portion of the PREDEF procedure to
 *  perform common data structure operations for text_io. Perform various
 *  actions based on whether this is an input or output file.
 */

void close_textio()				                          /*;close_text_io*/
{
	if (IOMODE == TIO_OUT_FILE) {

		/* Simulate effect of NEW_PAGE unless current page is terminated */

		if (!PAGE_TERMINATED) {
			if (COL > 1 ||(COL == 1 && LINE == 1))
				put_line();
			put_page();
		}
	}

	/* Sever the association between the given file and its associated
	 * external file.   The given file is left closed.  Do not perform
	 * system closes on the standard input and output files.
	 */

	if (filenum != standard_in_file && filenum != standard_out_file)
		close_file();

	/* If the file being closed is one of the default files, set the default
	 * file indicator to zero to indicate that the file is closed.
	 */

	if (filenum == current_in_file)
		current_in_file = 0;
	else if (filenum == current_out_file)
		current_out_file = 0;
}

/*  TEXT_IO Line Management Procedures */

/* GET_CHAR */

/*  Procedure to get the next character from the current text input file.
 *  If no character is available, then END_ERROR is raised.
 */

char get_char()							                         /*;get_char*/
{
	char    c;                          /* character read */

	/* Load look ahead and check for no more characters */

	load_look_ahead();
	if (CHARS == 0)
		predef_raise(END_ERROR, "End of file on TEXT_IO input");
	c = CHAR1;

	/* Update lookahead */

	CHAR1 = CHAR2;
	CHAR2 = CHAR3;
	CHAR3 = 0;
	CHARS--;

	/* Update PAGE and LINE counters if page mark or line feed read */

	if (c == PAGE_MARK) {
		PAGE += 1;
		LINE = 1;
		COL = 1;
	}
	else if (c == LINE_FEED) {
		LINE += 1;
		COL = 1;
	}
	else
		COL += 1;
	if (c > 127)
		predef_raise(DATA_ERROR, "Character > 127 for TEXT_IO input");
	gchar("get_char",c);
	return c;
}

/* SKIP_LINE */

/* Procedure to perform SKIP_LINE operation for a spacing of 1 line. Skips
 * characters up to and including next line terminator. Also skips any
 * page marks following this line terminator (there should normally be at
 * most one in standard format files, but we just skip past several if
 * we find them present).
*/

void skip_line()					                             /*;skip_line*/
{
	for (;;) {
		load_look_ahead();
		if (get_char() == LINE_FEED)
			break;
	}

	/* ignore page marks for standard input. */

	if (IOFDESC == stdin) return;

	for (;;) {
		load_look_ahead();
		if (CHAR1 != PAGE_MARK) break;
		get_char();
	}
}

/* PUT_BLANKS */

/* Procedure to write n blanks to current text file. There is no check for
 * line overflow, it is assumed that the caller has checked line overflow.
 */

void put_blanks(int n)				                           /*;put_blanks*/
{
	while (n--) {
		pchar("put_blanks",' ');
		putc(' ', IOFDESC);
	}
}

/* PUT_CHAR */

/* Procedure to write 1 character to current text file. Functions exactly
 * as if writing a one character string(see PUT_STRING function)
 */

void put_char(char c)				                             /*;put_char*/
{
	if (BOUNDED_LINE_LENGTH && COL > LINE_LENGTH )
		put_line();
	pchar("put_char",c);
	putc(c, IOFDESC);
	COL++;
}

/* PUT_LINE */

/* This procedure outputs a line feed to the current text file */

void put_line()						                              /*;put_line*/
{
	pchar("put_line",LINE_FEED);
	putc(LINE_FEED, IOFDESC);
	COL = 1;
	if (BOUNDED_PAGE_LENGTH && LINE >= PAGE_LENGTH)
		put_page();
	else LINE += 1;
}

/* PUT_PAGE */

/* Procedure to write page mark to current text file */

void put_page()					                              /*;put_page*/
{
	pchar("put_page",PAGE_MARK);
	putc(PAGE_MARK, IOFDESC);
	PAGE += 1;
	LINE = 1;
	COL = 1;
}

/* Put a string into the output file, performing line breaks as needed */

void put_string(char *s)			                           /*;put_string*/
{
	int     left_in_string;             /* chars left in string */
	int     left_in_line;               /* chars left in current line */

	left_in_string = strlen(s);

	if (UNBOUNDED_LINE_LENGTH) {
		COL += left_in_string;
		while (left_in_string--) {
			pchar("put_string 1",*s);
			putc(*s++,IOFDESC);
		}
	}

	else {
		while(left_in_string != 0) {
			left_in_line = LINE_LENGTH - COL + 1;
			if (left_in_line <= 0) {
				put_line();
				left_in_line = LINE_LENGTH;
			}

			if (left_in_string <= left_in_line) {
				COL += left_in_string;
				while (left_in_string) {
					pchar("put_string 2",*s);
					putc(*s++,IOFDESC);
					/* left_in_string should never become negative */
					left_in_string--;
				}
			}
			else {
				left_in_string -= left_in_line;
				while (left_in_line--) {
					pchar("put_string 3",*s);
					putc(*s++,IOFDESC);
				}
				put_line();
			}
		}
	}
}


/* PUT_BUFFER */

/*  This routine writes an item(passed in as a string) with appropriate
 *  blank padding. The second parameter is the desired width, and the third
 *  parameter is 'L' for leading blank padding and 'T' for trailing padding.
 */

void put_buffer(char *buffer, int width, char padtype)	      /*;put_buffer*/
{
	int     slength, tlength;
	int n;

	slength = strlen(buffer);
	if (slength < width)
		tlength = width;
	else {
		tlength = slength;
		padtype = ' ';
	}

	/* Ensure the buffer size does not exceed the line length */

	if (BOUNDED_LINE_LENGTH) {
		if (tlength > LINE_LENGTH)
			predef_raise(LAYOUT_ERROR, "Line too big");

		/* New line if does not fit on current line */

		if (COL + tlength - 1 > LINE_LENGTH)
			put_line();
	}

	/* Output data with required padding */

	if (padtype == 'L')
		put_blanks(width - slength);
	n = slength;
	while (n--) {
		pchar("put_buffer",*buffer);
		putc(*buffer++,IOFDESC);
	}
	COL += tlength;
	if (padtype == 'T')
		put_blanks(width - slength);
}

/* OPEN_SEQ_IO */

/*  Create or open a SEQUENTIAL_IO file. If the file is to be created,
 *  an AFCB is created and initialized to empty. If the file is to be opened,
 *  the existing file is accessed, and the AFCB initialized for that file.
 *  The argument opn is 'C' to create a file and 'O' to open a file.
 */

void open_seq_io(int opn)				                        /*;open_seq*/
{
	DISCARD_GENERIC_PARAMETER;

	open_file();

#ifdef SYSTEM_V
	if (strlen(IOFNAME) > 14) predef_raise(NAME_ERROR,"Invalid file name");
#endif

	if (opn == 'C') {
		IOFDESC = fopen_bin(IOFNAME, "w");
		if (IOFDESC == NULL) predef_raise(NAME_ERROR,"Invalid file name");
		if (IOMODE == SIO_IN_FILE) {
			fclose(IOFDESC);
			IOFDESC = fopen_bin(IOFNAME, "r");
			check_opened_ok();
		}
	}
	else {                      /* opn == 'O' */
		IOFDESC = fopen_bin(IOFNAME, "r");
		if (IOFDESC == NULL) predef_raise(NAME_ERROR,"File not found");
		if (IOMODE == SIO_IN_FILE)
			;
		else {
			fclose(IOFDESC);
			IOFDESC = fopen_bin(IOFNAME, "w");
			check_opened_ok();
		}
	}
	*get_argument_ptr(0) = filenum;
}

/* OPEN_DIR_IO */

/*  Open or create a DIRECT_IO file. If the file is to be created, an
 *  AFCB is created and initialized to empty. If the file is to be opened,
 *  the existing file is accessed, and the AFCB initialized for that file.
 *  The argument opn is 'C' to create a file and 'O' to open a file.
 */

void open_dir_io(int opn)			                        /*;open_dir_io*/
{
	int     *item_tt_ptr;
	long    eof_pos;
	int     type;

	POP_PTR(item_tt_ptr);              /* pop generic type */

	type = TYPE(item_tt_ptr);
	if (type == TT_U_ARRAY || type == TT_U_RECORD) {
		predef_raise(USE_ERROR,
		  "Unconstrained types not permitted for direct IO");
	}
	open_file();
	DLENGTH = SIZE(item_tt_ptr) * sizeof(int);

#ifdef SYSTEM_V
	if (strlen(IOFNAME) > 14) predef_raise(NAME_ERROR,"Invalid file name");
#endif

	if (opn == 'C') {
		IOFDESC = fopen_bin(IOFNAME,"w+");
		if (IOFDESC == NULL) predef_raise(NAME_ERROR,"Invalid filename");
		DPOS = 1;
		DSIZE = 0;
	}
	else {
		IOFDESC = fopen_bin(IOFNAME, "r");
		if (IOFDESC == NULL) predef_raise(NAME_ERROR,"File not found");
		if (IOMODE == DIO_IN_FILE)
			;
		else if (IOMODE == DIO_OUT_FILE) {
			fclose(IOFDESC);
			IOFDESC = fopen_bin(IOFNAME, "w");
		}
		else {
			fclose(IOFDESC);
			IOFDESC = fopen_bin(IOFNAME, "r+");
		}
		check_opened_ok();

		DPOS = 1;
		fseek(IOFDESC, 0L, 2);
		eof_pos = ftell(IOFDESC);
		DSIZE = eof_pos / DLENGTH;
	}
	*get_argument_ptr(0) = filenum;
}


/* OPEN_FILE */

/*  This routine is used for all file types to perform the basic operations
 *  of acquiring the parameters for an open and constructing an AFCB. On
 *  return the AFCB is built and filenum references the afcbs entry set.
 */

static void open_file()				                             /*;open_file*/
{
	int     *file_ptr;

	file_ptr = get_argument_ptr(0);
	check_ifile_closed(file_ptr);

	filenum = 1;
	while(afcbs[filenum - 1] != 0 && filenum < 21)
		filenum++;
	if (afcbs[filenum - 1] != 0)
		predef_raise(USE_ERROR, "Too many files open");

	afcbs[filenum - 1] = (struct afcb  *)(predef_alloc(sizeof(struct afcb)));

	IOFDESC = NULL;

	IOMODE = get_argument_value(2);

	get_string_value(4);
	IOFNAME = make_string();

	get_string_value(8);
	IOFORM = make_string();

	/* Check for temporary file */

	if (*IOFNAME == '\0') {
		struct tfile   *tfilep;
#ifdef IBM_PC
		IOFNAME = predef_alloc(L_tmpnam);
		tmpnam(IOFNAME);
#else
		IOFNAME = predef_alloc(15);
		strcpy(IOFNAME, "ADATEMP_XXXXXX");
		IOFNAME = mktemp(IOFNAME);
#endif
		tfilep = (struct tfile *)(predef_alloc(sizeof(struct tfile)));
		tfilep -> tfile_next = tfiles;
		tfiles = tfilep;
		strcpy(tfiles -> tfile_name, IOFNAME);
	}

	/* If not temporary file, make sure it is not already open */

	else
		check_xfile_closed(IOFNAME);
}

/* CLOSE_FILE */

/* Close file and deallocate the AFCB */

void close_file()					                            /*;close_file*/
{
	fclose(IOFDESC);
	predef_free(IOFNAME);
	predef_free(IOFORM);
	predef_free((char *)(afcbs[filenum - 1]));
	afcbs[filenum - 1] = 0;
}

/* PREDEF_TERM : Termination routine for Input/Output packages. */

void predef_term()				                           /*;predef_term*/
{
	/* close all open files except stdin and stdout */

	for (filenum = 3; filenum <= MAXFILES; filenum++) {
		if (afcbs[filenum - 1] != NULL && IOFDESC != NULL) {
			if (IOMODE == SIO_OUT_FILE)
				close_file();
			else if (IOMODE == DIO_OUT_FILE || IOMODE == DIO_INOUT_FILE)
				close_file();
			else if (IOMODE == TIO_OUT_FILE)
				close_textio();
		}
	}

	/* Delete temporary files upon completion of the main program */

	while(tfiles != 0) {
#ifdef vms
		delete(strjoin((tfiles -> tfile_name),";"));
#else
		unlink(tfiles -> tfile_name);
#endif
		tfiles = tfiles -> tfile_next;
	}
#ifdef IBM_PC
	/* Flush input buffer if there is something there, since MS_DOS
     * will retain the unwanted information for next program execution.
     * Check that stdin is connected to the console and that the last
     * character read from stdin is not EOF (this will be the case also
     * if we have not read anything from stdin) and not LINE_FEED.
     */
	if ( isatty(fileno(stdin))  && 
	  last_char_input != EOF && last_char_input != LINE_FEED) {
		filenum = 1;	/* stdin */
		do {
#ifdef IO_EOF
			last_char_input = fgetceof(IOFDESC);
#else
			last_char_input = fgetc(IOFDESC);
#endif
		} while ( last_char_input != LINE_FEED && last_char_input != EOF);
	}
#endif
}

/* PREDEF_ALLOC */

/* Procedure to allocate storage for use by PREDEF. The argument is the
 * length of storage required in bytes. For now we allocate this storage
 * on the main C heap. Later on when we extend the Ada heap to multiple
 * segments, we may allocate on the Ada heap.
 */

char *predef_alloc(int s)					                 /*;predef_alloc*/
{
	return (char *) malloc(s);
}

/* Procedure to free storage previously acquired by a call to PREDEF_ALLOC */

void predef_free(char *p)			                          /*;predef_free*/
{
	free(p);
}

#ifdef IBM_PC
/* if need to distinguish binary and text files */

FILE *fopen_bin(char *fname, char *fmode)					/*;fopen_bin*/
{
	char mode[30];
	strcpy(mode,fmode);
	strcat(mode,"b");
#ifdef IO_EOF
	clear_eof();
#endif
	return fopen(fname, mode);
}

FILE *fopen_txt(char *fname, char *fmode)					/*;fopen_txt*/
{
	char mode[30];
	strcpy(mode,fmode);
	strcat(mode,"t");
#ifdef IO_EOF
	clear_eof();
#endif
	return fopen(fname, mode);
}
#endif

#ifdef IO_EOF
static void clear_eof()										/*;clear_eof*/
{
	/* reset eof flag for those systems that need it; that is, those systems
	 * that do not return EOF properly when called more than once at end of
	 * file
	 */
	afcbs[filenum-1]->io_eof = 0;
}

static int fgetceof(FILE *file)				/*;fgetceof*/
{
	int c;

	if (IOEOF) c = EOF;
	else {
		c = fgetc(file);
		if (c==EOF)
			IOEOF = 1; /* set flag to avoid reading past end */
	}
	return c;
}
#endif

#ifdef DEBUG_PREDEF
static int pchars=0;
static int gchars=0;

static void gchar(char *msg, int c)									/*;gchar*/
{
	gchars++;
	printf("\ngchar %03d %s %03d %02x ",gchars,msg,c,c);
	if (c>=0 && c<32) printf("^%c",c+64);
	else if (c>32 && c<127) printf(" %c",c);
	else printf(" ");
	printf("\n");
}

static void pchar(char *msg, int c)									/*;pchar*/
{
	pchars++;
	printf("\npchar %03d %s %03d %02x ",pchars,msg,c,c);
	if (c>=0 && c<32) printf("^%c",c+64);
	else if (c>32 && c<127) printf(" %c",c);
	else printf(" ");
	printf("\n");
}
#endif

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.