/* $Id: iokeywds.c,v 1.7 2004/12/29 19:27:25 moniot Exp $ Routines to handle I/O keywords as in OPEN statement, and related matters. */ #include #include #if HAVE_STRINGS_H #include /* we use strcasecmp */ #endif #include "ftnchek.h" #include "symtab.h" #include "tokdefs.h" #include "iokeywds.h" #include "utils.h" /* we use strcasecmp */ /* variables shared with fortran.y */ extern int io_internal_file, io_list_directed; extern int current_io_unit_id; extern int current_io_unit_no; extern IO_ACCESS_TYPE current_io_access; extern IO_FORM_TYPE current_io_form; PROTO(PRIVATE int find_io_keyword,( char *s )); /* use_io_keyword handles keyword=value fields in i/o control lists */ void #if HAVE_STDC use_io_keyword(Token *keyword, Token *value, int stmt_class) #else /* K&R style */ use_io_keyword(keyword,value,stmt_class) Token *keyword,*value; int stmt_class; #endif /* HAVE_STDC */ { unsigned i; int k, stmt_flag=0, type_flag, setit,useit; int hkey=keyword->value.integer; /* Convert statement_class (a token class) into a bit flag compatible with io_keywords table. */ for(i=0; iline_num,keyword->col_num, "not an i/o statement class:"); (void)fprintf(stderr,"%d",stmt_class); return; } /* Convert value datatype into a bit flag compatible with io_keywords table. Note that '*' is handled by using type_UNDECL */ if(value->tclass == '*') type_flag = STAR; else type_flag = (1<TOK_type)); /* Look up keyword in table*/ k = find_io_keyword(hashtab[hkey].name); /* Not found or nonstandard: issue warning. A warning is always given (except under -nocheck) when a keyword is not recognized, since this can mean something is not processed correctly. */ if( io_keywords[k].nonstandard || io_keywords[k].nonf90 #ifdef ALLOW_VMS_IO /* special VMS case: OPEN(...,NAME=str,...) */ || (io_keywords[k].vms_name && stmt_flag==OP) #endif /*ALLOW_VMS_IO*/ ) { /* If nonstandard and -f77 or -f90 flag given, issue warning */ if( (io_keywords[k].nonstandard && f77_io_keywords) || (io_keywords[k].nonf90 && f90_io_keywords) ) { nonstandard(keyword->line_num,keyword->col_num, (io_keywords[k].nonf90 && f90_io_keywords),0); } if(io_keywords[k].name == NULL) { if( f77_io_keywords || f90_io_keywords ) { /* abbrev warning if nonstd message given */ msg_tail(": unrecognized keyword"); } else { if(misc_warn) { warning(keyword->line_num,keyword->col_num, "Unrecognized keyword"); } } if(f77_io_keywords || f90_io_keywords || misc_warn) { msg_tail(hashtab[hkey].name); msg_tail("-- Ftnchek may process incorrectly"); } } } /* If label expected, switch integer const to label */ if( (LAB & io_keywords[k].allowed_types) && (type_flag == INT && is_true(LIT_CONST,value->TOK_flags))) { type_flag = LAB; } /* Now check it out */ /* Check if keyword is allowed with statement */ if(!(stmt_flag & io_keywords[k].allowed_stmts)) { syntax_error(keyword->line_num,keyword->col_num, "keyword illegal in this context"); return; } /* Check if the type is OK */ if( !(type_flag & io_keywords[k].allowed_types) ) { syntax_error(value->line_num,value->col_num, "control specifier is incorrect type"); return; } /* Now handle usage */ /* internal file?: WRITE(UNIT=str,...) */ if(stmt_flag == WR && type_flag == CHR && io_keywords[k].allowed_types == UID) { setit = TRUE; useit = FALSE; } /* INQUIRE: set it if inquire_set flag true */ else if(stmt_flag == INQ && io_keywords[k].inquire_set) { setit = TRUE; useit = FALSE; } /* otherwise use use/set flags in table */ else { useit = io_keywords[k].implies_use; setit = io_keywords[k].implies_set; } /* Keep note if format is '*' */ if(value->tclass == '*' && io_keywords[k].allowed_types == FID ) { io_list_directed = TRUE; } /* Handle NML=namelist */ if(type_flag == NML){ ref_namelist(value,stmt_class); current_io_form = IO_FORM_FORMATTED; /* namelist I/O is formatted */ } /* Handle UNIT==unit */ if(io_keywords[k].allowed_types == UID) { if( type_flag == STAR ) { current_io_unit_id = IO_UNIT_DEFAULT; /* special code to signify default unit */ } /* Be careful only to treat integer variables or literals as unit numbers. Leave internal files as undefined units so they won't create records in the I/O usage table. */ else if( type_flag == INT ) { if(is_true(ID_EXPR,value->TOK_flags)){ current_io_unit_id = value->value.integer; /* get hash code of identifier */ } else if( is_true(LIT_CONST,value->TOK_flags) && value->TOK_type == type_byte(class_VAR,type_INTEGER)) { current_io_unit_no = value->value.integer; /* get literal int value */ } } /* Catch internal unit that is a const or expr (F90 std 9.2.2.1) */ else if( type_flag == CHR && !is_true(ID_EXPR,value->TOK_flags) ) { syntax_error(value->line_num,value->col_num, "internal file must be a variable"); } } /* Handle FMT=format */ if(io_keywords[k].allowed_types == FID) { current_io_form = IO_FORM_FORMATTED; if( type_flag == LAB) { ref_label(value,LAB_IO); } } /* Handle END=label and ERR=label */ if(io_keywords[k].allowed_types == LAB && type_flag == LAB) { ref_label(value,LAB_GOTO); if( style_goto ) { /* warn if GOTO considered harmful */ warning(keyword->line_num,keyword->col_num, "Obsolescent feature: branch to labeled statement"); } } /* Handle ACCESS=mode and FORM=mode */ if( stmt_flag != INQ && (k == IOKW_ACCESS || k == IOKW_FORM) ) { if( type_flag == CHR ) { /* protect ourself: error was warned about already */ char *modespec=(char *)NULL; /* place to record the literal string of value */ if( is_true(LIT_CONST,value->TOK_flags) ) { modespec = value->value.string; } else { /* if specifier value is not literal, let's hope it's a parameter */ int h=value->value.integer; Lsymtab *symt; if((symt=hashtab[h].loc_symtab) != NULL) { if(symt->parameter) modespec = symt->info.param->value.string;/* whew! found it! */ } /* if not, current value will have to stay default */ } if( modespec != (char *)NULL ) { /* if modespec was found, record it */ /* Record which type of ACCESS specified */ if(k == IOKW_ACCESS) { if( strcasecmp(modespec,"SEQUENTIAL") == 0 ) current_io_access = IO_ACCESS_SEQUENTIAL; else if( strcasecmp(modespec,"DIRECT") == 0 ) current_io_access = IO_ACCESS_DIRECT; /* Some compilers accept ACCESS=APPEND */ else if( strcasecmp(modespec,"APPEND") == 0 ) { if( f77_io_keywords || f90_io_keywords ) { nonstandard(value->line_num,value->col_num, f90_io_keywords,0); if(f90_io_keywords) { msg_tail(": suggest use POSITION='APPEND' instead"); } } /* treat as ACCESS=EQUENTIAL */ current_io_access = IO_ACCESS_SEQUENTIAL; } else { syntax_error(value->line_num,value->col_num, "ACCESS specifier must be \"SEQUENTIAL\" or \"DIRECT\""); } } /* Record which type of FORM specified */ else { /* k == IOKW_FORM */ if( strcasecmp(modespec,"FORMATTED") == 0 ) current_io_form = IO_FORM_FORMATTED; else if( strcasecmp(modespec,"UNFORMATTED") == 0 ) current_io_form = IO_FORM_UNFORMATTED; else { syntax_error(value->line_num,value->col_num, "FORM specifier must be \"FORMATTED\" or \"UNFORMATTED\""); } } } } } /* handle REC=rec in READ or WRITE */ if( k == IOKW_REC ) { current_io_access = IO_ACCESS_DIRECT; } /* Update usage status if a variable. */ if( is_true(ID_EXPR,value->TOK_flags)) { if(useit) { use_variable(value); } if(setit) { use_lvalue(value); } /* Character variable as unit id = internal file */ if(type_flag == CHR && io_keywords[k].allowed_types == UID) { io_internal_file = TRUE; } } else if(setit) { /* if value is set, must be an lvalue */ syntax_error(value->line_num,value->col_num, "variable required"); return; } } void record_io_usage( Token *stmt ) { /* First, see if we have something to record. (We don't record I/O to internal files, for instance.) Either id or number should be defined. */ if( current_io_unit_id != IO_UNIT_UNKNOWN || current_io_unit_no != IO_UNIT_UNKNOWN ) { /* Allocate space for table if that was not done as yet */ if(io_unit_info == (IO_Unit_Info*)NULL) { max_io_unit_usages = IO_UNIT_INFO_SIZE; io_unit_info = (IO_Unit_Info*)malloc(max_io_unit_usages*sizeof(IO_Unit_Info)); } /* Reallocate space if table has filled up */ if(num_io_unit_usages >= max_io_unit_usages) { max_io_unit_usages *= 2; /* double the table size */ io_unit_info = (IO_Unit_Info*)realloc(io_unit_info, max_io_unit_usages*sizeof(IO_Unit_Info)); } /* Now insert the new entry. If the unit_id is a parameter, look it up so the numerical value will be known too. */ if( current_io_unit_id >= 0 ) { Lsymtab *symt; if((symt=hashtab[current_io_unit_id].loc_symtab) != NULL) { /* ultra bogus if not in symtab */ if( symt->type == type_byte(class_VAR,type_INTEGER) && symt->parameter) { /* Can we do business? */ current_io_unit_no = symt->info.param->value.integer; } } } io_unit_info[num_io_unit_usages].line_num = stmt->line_num; io_unit_info[num_io_unit_usages].unit_no = current_io_unit_no; io_unit_info[num_io_unit_usages].unit_id = current_io_unit_id; io_unit_info[num_io_unit_usages].io_access = current_io_access; io_unit_info[num_io_unit_usages].io_form = current_io_form; io_unit_info[num_io_unit_usages].io_operation = curr_stmt_class; ++num_io_unit_usages; } } /* Handle VMS OPEN keywords that have no =value */ void #if HAVE_STDC use_special_open_keywd(Token *id) #else /* K&R style */ use_special_open_keywd(id) Token *id; #endif /* HAVE_STDC */ { #ifdef ALLOW_VMS_IO unsigned i; char *id_name= hashtab[id->value.integer].name; for(i=0; iline_num,id->col_num,f90_io_keywords,0); return; } } #endif/*ALLOW_VMS_IO*/ /* not found or not VMS: report error */ syntax_error(id->line_num,id->col_num, "Illegal control-list item"); } /* find_io_keyword looks up an i/o keyword in io_keywords table and returns its index. Uses simple linear search since not worth hash overhead. If not found, returns index of last element of list, which is special. */ PRIVATE int #if HAVE_STDC find_io_keyword(char *s) /* given name */ #else /* K&R style */ find_io_keyword(s) char *s; /* given name */ #endif /* HAVE_STDC */ { int i; for(i=0; io_keywords[i].name != NULL; i++) { if(strcmp(io_keywords[i].name, s) == 0) { break; } } return i; }