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.