/* $Id: project.c,v 1.12 2002/08/24 15:54:17 moniot Rel $ project.c: Project-file I/O routines. Copyright (c) 2001 by Robert K. Moniot. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Acknowledgement: the above permission notice is what is known as the "MIT License." Routines included: Shared routines: void proj_file_out() writes data from symbol table to project file. void proj_file_in() reads data from project file to symbol table. Private routines: int has_defn() TRUE if external has defn in current file int has_call() TRUE if external has call in current file int count_com_defns() Counts multiple common defns. void proj_alist_out() Outputs argument lists void proj_clist_out() Outputs common lists void proj_arg_info_in() Inputs argument lists void proj_com_info_in() Inputs common lists */ #include #include #include "ftnchek.h" #define PROJECT #include "symtab.h" #include /* Two options, proj_trim_calls and proj_trim_common, control whether Ftnchek creates project files with partial or complete global symbol table information. If these options are TRUE (the usual case), the action is: in library mode, keep only subprogram definitions, those external references not defined in the current file, and only one instance of each common block. In non-library mode, keep, besides the above, one call of a given routine from each module, and all common block declarations. Setting proj_trim_calls to FALSE causes all definitions and calls to be kept. Setting proj_trim_common to FALSE causes all common block instances to be kept. (In this case the action is the same whether or not in library mode.) These options formerly were controlled by compile-time define PROJ_KEEPALL. They are useful mainly for debugging ftnchek and for using the project files for purposes other than ftnchek. */ #define PROJFILE_COOKIE "FTNCHEK_" /* first part of magic cookie */ PROTO(PRIVATE int count_com_defns,( ComListHeader *clist )); PROTO(PRIVATE char *getstrn,(char s[], int n, FILE *fd)); PROTO(PRIVATE int has_call,( ArgListHeader *alist )); PROTO(PRIVATE int has_defn,( ArgListHeader *alist )); PROTO(PRIVATE int nil,( void )); PROTO(PRIVATE void proj_alist_out,( Gsymtab *gsymt, FILE *fd, int do_defns, int locally_defined )); PROTO(PRIVATE void proj_arg_info_in,( FILE *fd, char *filename, int is_defn )); PROTO(PRIVATE void proj_clist_out,( Gsymtab *gsymt, FILE *fd )); PROTO(PRIVATE void proj_com_info_in,( FILE *fd, char *filename )); PRIVATE int #if HAVE_STDC has_defn(ArgListHeader *alist) /* Returns TRUE if list has defns */ #else /* K&R style */ has_defn(alist) /* Returns TRUE if list has defns */ ArgListHeader *alist; #endif /* HAVE_STDC */ { while( alist != NULL && alist->topfile == top_filename ) { if(alist->is_defn) return TRUE; alist = alist->next; } return FALSE; } PRIVATE int #if HAVE_STDC has_call(ArgListHeader *alist) /* Returns TRUE if list has calls or defns */ #else /* K&R style */ has_call(alist) /* Returns TRUE if list has calls or defns */ ArgListHeader *alist; #endif /* HAVE_STDC */ { while( alist != NULL && alist->topfile == top_filename) { if( alist->is_call || alist->actual_arg ) return TRUE; alist = alist->next; } return FALSE; } PRIVATE int #if HAVE_STDC count_com_defns(ComListHeader *clist) /* Returns number of common decls in list */ #else /* K&R style */ count_com_defns(clist) /* Returns number of common decls in list */ ComListHeader *clist; #endif /* HAVE_STDC */ { int count=0; while( clist != NULL && clist->topfile == top_filename ) { ++count; clist = clist->next; } return count; } /* proj_file_out: writes data from symbol table to project file. */ #define WRITE_STR(LEADER,S) (void)(fprintf(fd,LEADER), fprintf(fd," %s",S)) #define WRITE_ARG(LEADER,S) (void)(fprintf(fd,LEADER), fprintf(fd," %s",S)) #define WRITE_NUM(LEADER,NUM) (void)(fprintf(fd,LEADER), fprintf(fd," %ld",NUM)) #define NEXTLINE (void)fprintf(fd,"\n") void #if HAVE_STDC proj_file_out(FILE *fd) #else /* K&R style */ proj_file_out(fd) FILE *fd; #endif /* HAVE_STDC */ { Gsymtab *sym_list[GLOBSYMTABSZ]; /* temp. list of symtab entries to print */ char sym_has_defn[GLOBSYMTABSZ]; char sym_has_call[GLOBSYMTABSZ]; if(fd == NULL) return; WRITE_STR(PROJFILE_COOKIE,PROJECT_VERSION); /* magic cookie */ NEXTLINE; WRITE_STR("file",top_filename); NEXTLINE; { /* Make list of subprograms defined or referenced in this file */ int i,numexts,numdefns,numcalls,do_defns,pass; ArgListHeader *alist; for(i=0,numexts=numdefns=numcalls=0;iname); else WRITE_STR(" external",sym_list[i]->name); WRITE_NUM(" class",(long)storage_class_of(sym_list[i]->type)); WRITE_NUM(" type",(long)datatype_of(sym_list[i]->type)); WRITE_NUM(" size",(long)sym_list[i]->size); /* Flag values stored are cumulative only for current file so they will not depend on what files were previously read in current run. When project file is read, flags will be ORed into Gsymtab as is done in process_lists. */ (void)fprintf(fd," flags %d %d %d %d %d %d %d %d", sym_list[i]->used_this_file, sym_list[i]->set_this_file, sym_list[i]->invoked_as_func_this_file, sym_list[i]->declared_external_this_file, /* N.B. library_module included here but is not restored */ sym_list[i]->library_module, 0, /* Flags for possible future use */ 0, 0); NEXTLINE; proj_alist_out(sym_list[i],fd,do_defns,(int)sym_has_defn[i]); } }/* end for i */ NEXTLINE; }/*end for pass */ } { int i,numblocks,numdefns; ComListHeader *clist; for(i=0,numblocks=numdefns=0;itopfile == top_filename ) { /* No keepall: save only one com decl if -lib mode */ if( proj_trim_common && library_mode) numdefns++; else /* keepall or -nolib mode: keep all com decls */ numdefns += count_com_defns(clist); sym_list[numblocks++] = &glob_symtab[i]; } } WRITE_NUM(" comblocks",(long)numdefns); NEXTLINE; for(i=0; iinfo.arglist; ArgListElement *arg; int i,n; unsigned long diminfo; Gsymtab *last_calling_module; /* This loop runs thru only those arglists that were created in the current top file. */ last_calling_module = NULL; while( a != NULL && a->topfile == top_filename) { /* do_defns mode: output only definitions */ if( (do_defns && a->is_defn) || (!do_defns && !a->is_defn) ) /* keep only externals not satisfied in this file in -lib mode, otherwise keep one actual call from each module. */ if( ! proj_trim_calls || (a->is_defn || !locally_defined || (!library_mode && (a->is_call || a->actual_arg) && a->module != last_calling_module)) ) { last_calling_module = a->module; if(a->is_defn) (void)fprintf(fd," defn\n"); else (void)fprintf(fd," call\n"); WRITE_STR(" module",a->module->name); WRITE_STR(" file",a->filename); WRITE_NUM(" line",(long)a->line_num); WRITE_NUM(" top",(long)a->top_line_num); WRITE_NUM(" class",(long)storage_class_of(a->type)); WRITE_NUM(" type",(long)datatype_of(a->type)); WRITE_NUM(" size",(long)a->size); (void)fprintf(fd," flags %d %d %d %d", a->is_defn, a->is_call, a->external_decl, a->actual_arg); NEXTLINE; n=a->numargs; if(a->is_defn || a->is_call) { WRITE_NUM(" args",(long)n); NEXTLINE; } /* Next lines, 2 per argument. 1st line: position number & name or source text of expr 2nd line: type, array dims, array size, flags */ arg = a->arg_array; for(i=0; iname; WRITE_STR(" cblk",cblk); } WRITE_NUM(" cndx",(long)arg[i].common_index); WRITE_NUM(" same",(long)arg[i].same_as); (void)fprintf(fd," flags %d %d %d %d %d %d %d %d", arg[i].is_lvalue, arg[i].set_flag, arg[i].assigned_flag, arg[i].used_before_set, arg[i].array_var, arg[i].array_element, arg[i].declared_external, arg[i].active_do_var); NEXTLINE; } }/* end if(do_defn...)*/ a = a->next; }/* end while(a!=NULL)*/ (void)fprintf(fd," end\n"); }/*proj_alist_out*/ /* proj_clist_out writes common var list data from symbol table to project file. */ PRIVATE void #if HAVE_STDC proj_clist_out(Gsymtab *gsymt, FILE *fd) #else /* K&R style */ proj_clist_out(gsymt,fd) Gsymtab *gsymt; FILE *fd; #endif /* HAVE_STDC */ { ComListHeader *c=gsymt->info.comlist; ComListElement *cvar; int i,n; while( c != NULL && c->topfile == top_filename ) { WRITE_STR(" block",gsymt->name); WRITE_NUM(" class",(long)storage_class_of(gsymt->type)); WRITE_NUM(" type",(long)datatype_of(gsymt->type)); NEXTLINE; WRITE_STR(" module",c->module->name); WRITE_STR(" file",c->filename); WRITE_NUM(" line",(long)c->line_num); WRITE_NUM(" top",(long)c->top_line_num); (void)fprintf(fd," flags %d %d %d %d", c->any_used, c->any_set, c->saved, 0); /* Flag for possible future use */ NEXTLINE; WRITE_NUM(" vars",(long)(n=c->numargs)); NEXTLINE; /* Next lines, 2 per variable. 1st line: position number, name. 2nd line: class, type, array dims, array size */ cvar = c->com_list_array; for(i=0; inext; }/* end while c != NULL */ } #undef WRITE_STR #undef WRITE_NUM #undef NEXTLINE /* proj_file_in: Reads a project file, storing info in global symbol table. See proj_file_out and its subroutines for the current project file format. */ #define MAXNAME 127 /* Max string that will be read in: see READ_STR below */ /* Macros for error-flagging input */ PRIVATE int nil(VOID)/* to make lint happy */ { return 0; } #define READ_ERROR (oops_message(OOPS_FATAL,proj_line_num,NO_COL_NUM,\ "error reading project file"),nil()) #define READ_OK nil() #define READ_FIRST_STR(LEADER,STR) (fscanf(fd,LEADER), \ fscanf(fd,"%127s",STR)) #define READ_STR(LEADER,STR) ((void)((fscanf(fd,LEADER)==0 &&\ fscanf(fd,"%127s",STR)==1)? READ_OK:READ_ERROR)) #define READ_ARG(LEADER,STR) ((void)((fscanf(fd,LEADER)==0 && fgetc(fd)==' ' &&\ (getstrn(STR,MAXNAME+1,fd)!=NULL)==1)? READ_OK:READ_ERROR)) #define READ_NUM(LEADER,NUM) ((void)((fscanf(fd,LEADER)==0 &&\ fscanf(fd,"%d",&NUM)==1)? READ_OK:READ_ERROR)) #define READ_LONG(LEADER,NUM) ((void)((fscanf(fd,LEADER)==0 &&\ fscanf(fd,"%ld",&NUM)==1)? READ_OK:READ_ERROR)) #define NEXTLINE {int c;while( (c=fgetc(fd)) != EOF && c != '\n') continue;\ if(c == EOF) READ_ERROR; else ++proj_line_num;} PRIVATE unsigned proj_line_num; /* Line number in proj file for diagnostic output */ void #if HAVE_STDC proj_file_in(FILE *fd) #else /* K&R style */ proj_file_in(fd) FILE *fd; #endif /* HAVE_STDC */ { char buf[MAXNAME+1],*topfilename=NULL; int retval; unsigned numentries,ientry, numexts,iext, numblocks,iblock; proj_line_num = 1; /* Allow project file to contain (manually) concatenated project files. These will be processed as if the separate project files were sequentially provided as arguments. */ do { while( (retval=READ_FIRST_STR(PROJFILE_COOKIE,buf)) == 1) { if( strcmp(buf,PROJECT_VERSION) != 0 ) { (void)fprintf(stderr, "\nProject file is not correct version -- must be re-created\n"); exit(1); } NEXTLINE; /* Save filename in permanent storage */ READ_STR("file",buf); topfilename = new_global_string(buf); NEXTLINE; #ifdef DEBUG_PROJECT printf("\nread file %s\n",topfilename); #endif READ_NUM(" entries",numentries); /* Get no. of entry points */ NEXTLINE; #ifdef DEBUG_PROJECT printf("read entries %d\n",numentries); #endif /* Read defn arglists */ for(ientry=0; ientrysize = id_size; } else if(is_defn) gsymt->size = id_size; /* Set library_module flag if project file was created with -lib mode in effect, or is now taken in -lib mode */ if(is_defn && (library_mode || id_library_module)) { gsymt->library_module = TRUE; } if(is_defn) gsymt->defined = TRUE; if(id_used_flag) gsymt->used_flag = TRUE; if(id_set_flag) gsymt->set_flag = TRUE; if(id_invoked) gsymt->invoked_as_func = TRUE; if(id_declared) gsymt->declared_external = TRUE; while( fscanf(fd,"%5s",sentinel), #ifdef DEBUG_PROJECT printf("sentinel=[%s]\n",sentinel), #endif strcmp(sentinel,(is_defn?"defn":"call")) == 0) { ArgListHeader *ahead; ArgListElement *alist; #ifdef KEEP_ARG_NAMES ArgListHeader *prev_ahead; ArgListElement *prev_alist; unsigned prev_n; #endif NEXTLINE; READ_STR(" module",module_name); READ_STR(" file",file_name); READ_NUM(" line",alist_line); /* line number */ READ_NUM(" top",alist_topline); /* topfile line number */ READ_NUM(" class",alist_class); /* class as in ArgListHeader */ READ_NUM(" type",alist_type); /* type as in ArgListHeader */ READ_LONG(" size",alist_size); /* size as in ArgListHeader */ if(fscanf(fd," flags %d %d %d %d", &alist_is_defn, &alist_is_call, &alist_external_decl, &alist_actual_arg) != 4) READ_ERROR; NEXTLINE; #ifdef DEBUG_PROJECT printf("read alist class %d type %d line %d\n", alist_class,alist_type,alist_line); #endif /* Find current module in symtab. If not there, make a global symtab entry for it. It will be filled in eventually when processing corresponding entry. */ h = hash_lookup(module_name); if( (module = hashtab[h].glob_symtab) == NULL) { module = install_global((int)h,type_UNDECL,class_SUBPROGRAM); } if(module->internal_entry) { warning(NO_LINE_NUM,NO_COL_NUM, "entry point redefined as module"); msg_tail(module->name); msg_tail(": redefinition ignored"); } else { if(is_defn) { if(module != gsymt) { #ifdef DEBUG_PROJECT printf("\nLinking entry %s to module %s", gsymt->name,module->name); #endif gsymt->internal_entry = TRUE; gsymt->link.module=module; /* interior entry: link it to module */ } } else { /* call: add to child list */ /* Avoid duplication on child list. It will have just been placed there on previous project-file entry, so it will be the first child on the list. */ #ifdef DEBUG_PROJECT printf("\nChild %s of module %s", gsymt->name,module->name); #endif if(module->link.child_list == NULL || module->link.child_list->child != gsymt) { ChildList *node= (ChildList *)calloc(1,sizeof(ChildList)); #ifdef DEBUG_PROJECT printf(" linked in"); #endif node->child = gsymt; node->next = module->link.child_list; module->link.child_list = node; } #ifdef DEBUG_PROJECT else { printf(" (duplicate)"); } #endif } } if(alist_is_defn || alist_is_call) { READ_NUM(" args",numargs); NEXTLINE; } else numargs = 0; #ifdef DEBUG_PROJECT printf("read numargs %d\n",numargs); #endif /* ** if(!is_defn) { ** gsymt->used_flag = TRUE; ** } */ /* Create arglist structure */ if(((ahead=(ArgListHeader *) calloc(1, sizeof(ArgListHeader))) == (ArgListHeader *) NULL) || (numargs != 0 && ((alist=(ArgListElement *) calloc(numargs,sizeof(ArgListElement))) == (ArgListElement *) NULL))){ oops_message(OOPS_FATAL,proj_line_num,NO_COL_NUM, "out of malloc space for argument list"); } /* Initialize arglist and link it to symtab */ ahead->type = type_byte(alist_class,alist_type); ahead->size = alist_size; ahead->numargs = (short)numargs; ahead->arg_array = (numargs==0? NULL: alist); ahead->module = module; ahead->topfile = filename; /* try to avoid reallocating space for same name */ ahead->filename = (strcmp(file_name,filename)==0? filename: (strcmp(file_name,prev_file_name)==0? prev_file_name: (prev_file_name=new_global_string(file_name)))); ahead->line_num = alist_line; ahead->top_line_num = alist_topline; ahead->is_defn = alist_is_defn; ahead->is_call = alist_is_call; ahead->external_decl = alist_external_decl; ahead->actual_arg = alist_actual_arg; ahead->next = prev_ahead = gsymt->info.arglist; gsymt->info.arglist = ahead; if(prev_ahead != NULL) { prev_n = prev_ahead->numargs; prev_alist = prev_ahead->arg_array; } /* Fill arglist array from project file */ for(iarg=0; iargnumargs = (short)numvars; chead->line_num = clist_line; chead->top_line_num = clist_topline; chead->com_list_array = (numvars==0? NULL: clist); chead->module = module; chead->topfile = filename; chead->any_used = clist_any_used; chead->any_set = clist_any_set; chead->saved = clist_saved; /* try to avoid reallocating space for same name */ chead->filename = (strcmp(file_name,filename)==0? filename: (strcmp(file_name,prev_file_name)==0? prev_file_name: (prev_file_name=new_global_string(file_name)))); chead->next = prev_chead = gsymt->info.comlist; gsymt->info.comlist = chead; if(prev_chead != NULL) { prev_n = prev_chead->numargs; prev_clist = prev_chead->com_list_array; } /* Fill comlist array from project file */ for(ivar=0; ivar