/* **++ ** FACILITY: RECOMP ** ** MODULE DESCRIPTION: ** ** This module is designed to allow minimum recompilation of program ** modules, given the symbols that were changed during the module. The ** scope of the recompilation is direct; i.e., all the modules which ** contain primary and referenced occurrences of the given symbols are ** the ones that need to be recompiled. ** ** AUTHORS: ** ** Rajesh Mishra ** ** CREATION DATE: 30-DEC-1991 ** ** DESIGN ISSUES: ** ** In order to find all the modules that had been modified, the SCA ** callable interface commands have been used. The callable interface ** is also helpful in allowing queries to be done. The callable interface ** uses the VAX/VMS string descriptor format for handling strings. The ** PSE string interface module had to be included for this purpose. ** The program follows the following process: ** ** Input ---> Find ---> Output ** Symbols Modules Modules ** ** The List Manager (LM) has provided an efficient means of handling ** storage and manipulation of the symbols and modules. A basic list ** is used for storing the symbols. A B-tree is used to store the modules ** because a quick search is needed for modules, before storing them. ** ** COMPILATION QUALIFIERS: ** ** /SHARE - Include the SCA callable interface shareable image library ** /INCLUDE - Include SCA directory ** ** VERSION: ** ** 0.1 ** ** PORTABILITY ISSUES: ** ** Since a shareable image is used, other means of building this module is ** required to include the SCA callable interface. The PSE string ** functions are also used for calling queries, as well as STR$ sub-string ** functions. ** ** ** MODIFICATION HISTORY: ** **-- */ /* ** ** INCLUDE FILES ** */ #include "recomp.h" /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Main function for minimum_recompile program. ** ** FORMAL PARAMETERS: ** ** None ** ** RETURN VALUE: ** ** 0 - Successful completion ** 1 - Error. ** ** SIDE EFFECTS: ** ** None ** ** DESIGN: ** ** Calls functions as per the design of the module (input symbols, find ** modules, output modules). ** ** ** CALLING SEQUENCE: ** ** RUN RECOMP ** ** ERROR CODES: ** ** SCA$_NORMAL - Successful completion. ** SCA$_ERROR - Exit upon error. ** ** **-- */ int main ( int argc, char *argv[] ) { $sca_command_context command_context; recomp_list symbols, modules; /* Initialize SCA command context */ sca$initialize ( &command_context ); recomp_input_symbols ( &symbols ); recomp_find_modules ( &command_context, &symbols, &modules ); recomp_output_modules ( &command_context, &modules ); /* Clean up memory used */ recomp_cleanup ( &command_context, &symbols, &modules ); /* Return success */ return SCA$_NORMAL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** RECOMP_INPUT_SYMBOLS is designed to recieve input from the user all the ** modified symbols. The input is stored in a basic list. ** ** FORMAL PARAMETERS: ** ** comm_context: ** SCA command context that allows SCA commands to be executed. ** ** symbol_list: ** Output parameter that will contain all the input symbol names. ** ** RETURN VALUE: ** ** None ** ** SIDE EFFECTS: ** ** None ** ** DESIGN: ** ** Prompt user for input until is entered by user. ** The user input are modified symbols which are stored in a basic list. ** ** ** **-- */ static void recomp_input_symbols ( recomp_list *symbol_list ) { static pse_string prompt_str = pse_str_constant ( "Modified Symbol ( to STOP): "); pse_string symbol_name; pse_str_length symbol_name_len; recomp_list_entry *new_node=NULL; recomp_list_handle list_entry_handle; static int list_type = list_k_type_basic; /* Create list */ list_create ( symbol_list, &list_type ); list_clear ( symbol_list ); /* Initialize string variables */ symbol_name = pse_g_str_dynamic; /* Get symbols until none entered */ while (TRUE) { /* Get symbol from user */ sca$get_input ( &symbol_name, &prompt_str, &symbol_name_len ); /* If no symbol entered, STOP */ if (symbol_name_len == 0) return; /* Insert symbol into list */ new_node = (recomp_list_entry *)malloc(sizeof (recomp_list_entry)); memset ((char *) new_node, 0, sizeof (recomp_list_entry) ); new_node->string = pse_g_str_dynamic; pse_str_copy ( &new_node->string, &symbol_name ); list_insert_entry ( symbol_list, &new_node, &list_entry_handle ); /* Clean up for next symbol */ pse_str_free ( &symbol_name ); new_node = NULL; free ( new_node ); } } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** RECOMP_FIND_MODULES' purpose is to obtain all the modules in which the ** modified symbols exist as the input occurrence. These modules are the ** ones that need to be recompiled. ** ** FORMAL PARAMETERS: ** ** command_contxt: ** Pointer to the SCA context in which queries can be made. ** symbols: ** List of modified symbol names entered by user. ** modules: ** Output parameter that will contain all the recompilation modules. ** ** RETURN VALUE: ** ** None ** ** SIDE EFFECTS: ** ** None ** ** DESIGN: ** ** Get the primary and reference occurrences of each symbol. Then, ** identify the modules which refer to them. Insert these modules, ** ignoring duplicates, into the list of modules. ** ** **-- */ static void recomp_find_modules ( $sca_command_context *command_contxt, recomp_list *symbols, recomp_list *modules ) { pse_string module, query_command; struct $sca_handle prim_occ_handle; $sca_query_context query_context; recomp_list_handle iter_context, find_iter, dummy; recomp_list_entry *symbol_node, *module_node, *found_module; void *compare_routine_address = recomp_list_compare_entries; static int attri_module_constant = sca$k_attri_module, list_type = list_k_type_btree; int i; /* Initialize variables */ query_command = pse_g_str_dynamic; module = pse_g_str_dynamic; iter_context = NULL; symbol_node = NULL; dummy = NULL; module_node = NULL; /* Create a list for containing modules -- a b-tree is used to allow */ /* for quicker search using the string descriptor as the key */ list_create ( modules, &list_type ); list_clear ( modules ); list_set_user_compare ( modules, &compare_routine_address ); /* For each modified symbol in list... */ for ( i=1; i <= list_count (symbols); i++) { /* Get next symbol name */ if ( i==1) /* First entry to be inserted */ list_get_first_entry ( symbols, &symbol_node, &iter_context ); else /* Other entries to be inserted */ list_get_next_entry ( &iter_context, &symbol_node, &iter_context ); /* Create query */ pse_str_copy_asciz ( &query_command, "occurrence=(primary,reference) AND name=" ); pse_str_append ( &query_command, &symbol_node->string ); query_context.sca$l_query_context = 0; sca$query_initialize ( command_contxt, &query_context ); /* Find primary and reference occurrences of this symbol */ sca$query_parse ( &query_context, &query_command ); sca$query_find ( &query_context ); /* If there are occurrences of this type, get their modules */ prim_occ_handle.sca$l_handle = 0; while (sca$query_get_occurrence ( &query_context, &prim_occ_handle ) != SCA$_NOMORE) { /* Retrieve module name which contains this reference */ sca$query_get_attri_value_t ( &prim_occ_handle, &module, &attri_module_constant ); /* Create new list node for module */ module_node = (recomp_list_entry*)malloc(sizeof(recomp_list_entry)); memset ((char *)module_node, 0, sizeof (recomp_list_entry)); module_node->string = pse_g_str_dynamic; pse_str_copy ( &module_node->string, &module ); /* If module not already in list, insert it */ if ( !list_find_entry ( modules, &module_node, &found_module, &find_iter)) list_insert_entry ( modules, &module_node, &dummy ); /* Clean up memory for next node */ pse_str_free ( &module ); module_node = NULL; free ( module_node ); } /* Clean up memory used in this iteration */ symbol_node = NULL; free ( symbol_node ); pse_str_free ( &query_command ); sca$query_cleanup ( &query_context ); } } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** RECOMP_OUTPUT_MODULES prints out to STDOUT all the modules that need to ** be recompiled, as well as writes the command necessary to recompile the ** modules in the .COM file. Comments -- the names of the modules to be ** recompiled are placed in the .COM file also. ** ** FORMAL PARAMETERS: ** ** comm_ctxt: ** The SCA command context to perform SCA_DO_COMMAND ** modules: ** List of modules that need to be output. ** ** RETURN VALUE: ** ** None. ** ** SIDE EFFECTS: ** ** .COM file has been modified. ** ** DESIGN: ** ** Go through list retrieving module names and printing them onto STDOUT, ** as well as calling the RECOMP_EXTRACT_COMPILE_COMMAND function to write ** the recompilation command for the module in the .COM file. ** ** ** **-- */ static void recomp_output_modules ( $sca_command_context *comm_ctxt, recomp_list *modules ) { pse_string show_module_command; recomp_list_entry *print_module; FILE *com_file; recomp_list_handle iter_ctxt; int i; /* If no modules, output appropriately */ if (list_is_empty (modules)) { fprintf (stdout, "No references made to the symbol.\n"); return; } /* Initialize variables */ show_module_command = pse_g_str_dynamic; print_module = NULL; com_file = NULL; iter_ctxt = NULL; /* Create new .COM file and include a comment in the beginning */ com_file = fopen ("recomp.com", "w"); fprintf (com_file, "! Modules to be recompiled...\n"); /* Print out header output to STDOUT */ fprintf (stdout, "\n*****************\n"); fprintf (stdout, "Modules to be recompiled...\n\n"); /* For each module... */ for ( i=1; i <= list_count (modules); i++) { /* Get next module name */ if ( i==1) list_get_first_entry ( modules, &print_module, &iter_ctxt ); else { list_get_next_entry ( &iter_ctxt, &print_module, &iter_ctxt ); } /* Print module to STDOUT and .COM file */ sca$put_output ( &print_module->string ); fprintf (com_file, "!\t%s\n", asciz (&print_module->string) ); /* Create SHOW MODULE/FULL command */ pse_str_copy_asciz ( &show_module_command, "show module " ); pse_str_append ( &show_module_command, &print_module->string ); pse_str_append_asciz ( &show_module_command, "/full"); /* Perform "SCA> SHOW MODULE /FULL" for compile command line */ sca$do_command ( comm_ctxt, &show_module_command, 0, 0, "SCA>", com_file, 0, 0, recomp_extract_compile_command ); /* Clean up memory used in this iteration */ pse_str_free ( &show_module_command ); free ( print_module ); } /* End output to STDOUT, as well as close .COM file */ fprintf (stdout, "*****************\n"); fclose (com_file); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** RECOMP_CLEANUP cleans up everything used by this tool. These items are ** parameters to this function. ** ** FORMAL PARAMETERS: ** ** command_context: ** SCA command context. ** ** symbol_list: ** List of symbols. ** ** module_list: ** List of modules. ** ** RETURN VALUE: ** ** None ** ** SIDE EFFECTS: ** ** None ** ** DESIGN: ** ** None ** ** **-- */ static void recomp_cleanup ( $sca_command_context *command_context, recomp_list *symbol_list, recomp_list *module_list ) { sca$cleanup ( command_context ); list_clear ( symbol_list ); list_delete ( symbol_list ); list_clear ( module_list ); list_delete ( module_list ); } /* Utility Routines */ /* **++ ** FUNCTIONAL DESCRIPTION: ** ** RECOMP_EXTRACT_COMPLILE_COMMAND gets one line of "show module/full" ** output and parses it to obtain the compilation command of the given ** module. ** ** FORMAL PARAMETERS: ** ** output_line: ** The output line from "show module/full" SCA do command. ** ** output_file: ** File pointer to .COM file where compile command is written. ** ** RETURN VALUE: ** ** SCA$_NORMAL : Everything's OK. ** ** SIDE EFFECTS: ** ** Write output command line in .COM file. ** ** DESIGN: ** ** Use sub-string STR$ facilities to extract the command ** line from the "command line : ..." output line. Write ** this compile command in the form of a DCL command into ** the output file. The file pointer has already been placed ** where the output should be sent. ** **-- */ static short recomp_extract_compile_command (pse_string *output_line, FILE *output_file) { long int index, sub_index, start_pos, end_pos; static pse_string command_line_constant = pse_str_constant ( "command line :" ); pse_string compile_command; /* Initializing variables */ compile_command = pse_g_str_dynamic; index = 0; sub_index = 0; start_pos = 0; end_pos = 0; /* Find line beginning with "command line :" */ str$find_first_substring ( output_line, &index, &sub_index, &command_line_constant ); /* If "command line :" is found in the beginning of the output line, */ /* extract compile command line. */ if (index && (sub_index == 1)) { /* Extract compile command */ start_pos = index + (long)pse_str_get_length ( &command_line_constant ) + 1; end_pos = (long)pse_str_get_length ( output_line ); str$pos_extr ( &compile_command, output_line, &start_pos, &end_pos); /* Put command in .COM file */ fprintf (output_file, "\$%s\n", asciz (&compile_command)); pse_str_free ( &compile_command ); } return SCA$_NORMAL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** RECOMP_LIST_COMPARE_ENTRIES compares two entries in the list of ** recompilation modules that have been found. This routine is used by the ** list_set_user_compare () function for ordering the b-tree that contains ** the list of modules. ** ** FORMAL PARAMETERS: ** ** entry1: ** Pointer to first list entry to be compared ** ** entry2: ** Pointer to second list entry to be compared ** ** RETURN VALUE: ** ** -1: Entry1 < Entry2 ** 0: Entry1 == Entry2 ** 1: Entry1 > Entry2 ** ** SIDE EFFECTS: ** ** None ** ** DESIGN: ** ** None ** ** ** **-- */ static int recomp_list_compare_entries ( recomp_list_entry **entry1_rec, recomp_list_entry **entry2_rec) { pse_string *entry1, *entry2; entry1 = &(*entry1_rec)->string; entry2 = &(*entry2_rec)->string; return pse_str_compare ( entry1, entry2 ); }