/************************************************************************ * * * Copyright (c) 1982, Fred Fish * * All Rights Reserved * * * * This software and/or documentation is released for public * * distribution for personal, non-commercial use only. * * Limited rights to use, modify, and redistribute are hereby * * granted for non-commercial purposes, provided that all * * copyright notices remain intact and all changes are clearly * * documented. The author makes no warranty of any kind with * * respect to this product and explicitly disclaims any implied * * warranties of merchantability or fitness for any particular * * purpose. * * * ************************************************************************ */ /* * FILE * * dex1.c dynamic reconfiguration functions * * KEY WORDS * * dex files * dex * * DESCRIPTION * * This file contains functions for doing dynamic * reconfiguration. DEX automatically searches * a directory for a reconfiguration file the first * time any file in that directory is processed. * The default reconfiguration file is ".dexrc" but * can be changed to any desired file via a command * line switch. * * FUNCTIONS * * make_entry make an entry in the name table * reconfig do dynamic reconfiguration * rc_name build reconfiguration name * do_reconfig process open reconfiguration file * do_flags process reconfiguration ".flags" line * file_out process reconfiguration ".output" line * * AUTHOR * * Fred Fish * */ #include #include "hashtbl.h" #include "dex.h" extern int debug; /* Global debug flag */ extern int vflag; /* Global verbose flag */ extern char *get_memory (); static struct flgwrd { /* Reconfiguration flags */ char *word; /* String form of flag */ int flag; /* Token form of flag */ }; static struct flgwrd flgwrds[] = { /* Flags recognized */ "PROCESS", PROCESS, "EMITTEXT", EMITTEXT, "EMITBOX", EMITBOX, "EMITFILL", EMITFILL, "EMITUL", EMITUL, "EMITBP", EMITBP, "REGION", REGION, NULL, NULL /* Marks end of flags list */ }; /* * FUNCTION * * make_entry make an entry in table with specified name * * KEY WORDS * * hash table insertion * * SYNOPSIS * * struct tbl_data *make_entry(name) * char *name; * * DESCRIPTION * * Make_entry allocates memory for a table entry, initializes * it's name field, and then adds it to the hash table. * Each entry name is actually the string which is recognized * as starting a new documentation section, such as * "FUNCTION" or "DESCRIPTION". * * Note that make_entry allocates fresh memory for the name * string, and copies the name there. This insures that the * characters comprising the entry's name do not vanish * when somebody's stack is popped. * */ /* * PSEUDO CODE * * Begin make_entry * Initialize entry pointer to NULL. * If name pointer is not invalid then * Get memory for the entry name. * If memory was allocated then * Copy the name to the fresh memory. * Get memory for the table data structure. * If memory was allocated then * Remember the name of the entry. * Add entry to table, getting pointer. * End if * End if * End if * Return table entry pointer. * End make_entry * */ static struct tbl_data *make_entry(name) char *name; { struct tbl_data *temp, *tbl_add(); char *name_save; temp = NULL; if (name != NULL) { name_save = (char *) get_memory(strlen(name)+1); if (name_save != NULL) { strcpy(name_save,name); temp = (struct tbl_data *) get_memory(sizeof(struct tbl_data)); if (temp != NULL) { temp->name = name_save; temp = tbl_add(temp); if (debug) {printf("make_entry: added \"%s\"\n",temp->name);} } } } return(temp); } /* * FUNCTION * * reconfig do dynamic reconfiguration if necessary * * KEY WORDS * * dynamic reconfiguration * reconfiguration routines * * SYNOPSIS * * reconfig(pfn,rcfn) * char *pfn; (processed file name) * char *rcfn; (rc file base name) * * DESCRIPTION * * Tests to see if a new reconfiguration file needs to * be processed and if so, opens it and calls routine * to actually do the reconfiguration. * */ /* * PSEUDO CODE * * Begin reconfig * Build reconfiguration file name. * If name is not same as last used name then * If the reconfiguration file can be opened * Do the reconfiguration. * Remember name of reconfig file. * Close the reconfiguration file. * End if * End if * End reconfig * */ static char last_rcfile[MAXNAMESIZE]; reconfig(pfn,rcfn) char *pfn; char *rcfn; { char buffer[MAXNAMESIZE]; FILE *rcfp, *fopen(); rc_name(buffer,pfn,rcfn); if (debug) {printf("reconfig: %s & %s => %s\n",pfn,rcfn,buffer);} if (strcmp(buffer,last_rcfile) != 0) { if ((rcfp = fopen(buffer,"r")) != NULL) { if (vflag) {printf("dex: reconfiguring using \"%s\"\n",buffer);} do_reconfig(rcfp); strcpy(last_rcfile,buffer); fclose(rcfp); } } } /* * FUNCTION * * rc_name build the reconfiguration file name * * KEY WORDS * * dynamic reconfiguration * reconfiguration file * * SYNOPSIS * * static rc_name(out,pfn,rcfn) * char *out; (output goes here) * char *pfn; (processed file name) * char *rcfn; (rc base file name) * * DESCRIPTION * * Builds the name used when an attempt is made to open * the reconfiguration file. Assumes that if the specified * base reconfiguration file name does not contain a slash character * "/" then any prefix in the processed file name is to be * extracted and prepended to the specified base reconfiguration * file name. * * For example, if the processed file name is "/usr/me/myfile" * and the base reconfiguration file name is * ".dexrc" then the name used to open the reconfiguration * file will be "/usr/me/.dexrc". * If however the base reconfiguration file name is * "/usr/me/newrc" then this name will simply be * returned unchanged * * This seems to be the best way of handling reconfiguration * file names since if the full pathname is given then * only that file will be used regardless of where the * processed file is. However a generic name may be * given (such as ".dexrc") in which case a search will * be made in the processed file's directory for a file * with that generic name. * */ /* * PSEUDO CODE * * Begin rc_name * If the specified base rc file name contains "/" * Copy that name to output. * Else * Copy processed file name to work buffer. * If processed file name has no last "/" in it then * Simply copy base rc file name to output. * Else * Append base rc file name after last "/" * Copy concatenated names to output. * End if * End if * End rc_name * */ static rc_name(out,pfn,rcfn) char *out; /* Pointer to output place */ char *pfn; /* Pointer to processed file name */ char *rcfn; /* Pointer to reconfig base file name */ { char buffer[MAXNAMESIZE]; /* Concatenation work buffer */ char *cp; /* Work pointer for name build */ char *rindex(); /* Pointer to last occurrence of char */ if (index(rcfn,'/') != NULL) { strcpy(out,rcfn); } else { strcpy(buffer,pfn); cp = rindex(buffer,'/'); if (cp == NULL) { strcpy(out,rcfn); } else { strcpy(++cp,rcfn); strcpy(out,buffer); } } } /* * FUNCTION * * do_reconfig process open reconfiguration file * * KEY WORDS * * dynamic reconfiguration * reconfiguration routines * * SYNOPSIS * * do_reconfig(rcfp) * FILE *rcfp; * * DESCRIPTION * * Reads each record from the currently open reconfiguration * file, creating table entries if necessary, and adding * the reconfiguration information to the specified table * entries. * */ /* * PSEUDO CODE * * Begin do_reconfig * While a record can be read from reconfiguration file * Zap the newline at the end. * Skip over any leading whitespace. * If the line has something useful then * Extract the option field. * Skip to start of next field. * If the option is to process flags then * Process all flags on the line. * Else if option is change output file * Change name of output file. * Else * Print warning message. * End if * End if * End while * End do_reconfig * */ do_reconfig(rcfp) FILE *rcfp; { char buffer[256]; char string[128]; char *cp, *skpbt(), *xfield(); while (fgets(buffer,sizeof(buffer),rcfp) != NULL) { buffer[strlen(buffer)-1] = NULL; cp = skpbt(buffer); if (*cp != '#' && *cp != NULL && *cp != '\014') { cp = xfield(string,cp); cp = skpbt(cp); if (strcmp(string,".flags") == 0) { do_flags(cp); } else if (strcmp(string,".output") == 0) { file_out(cp); } else { fprintf(stdout,"dex: \"%s\" reconfiguration action?\n",string); } } } } /* * FUNCTION * * do_flags process flags for specified section * * KEY WORDS * * dynamic reconfiguration * flags * * SYNOPSIS * * do_flags(flags_line) * char *flags_line; * * DESCRIPTION * * Processes all flags for the current section, setting * or resetting those specified. Flags not specified are * unchanged from the default (reset). * * Flags may begin with an explicit action character * "-" or "+" for reset or set respectively. The * default is to set the named flag. * */ /* * PSEUDO CODE * * Begin do_flags * If the passed pointer is not invalid then * If there is a line to process then * Extract the name of the section. * Skip to next field (first flag). * If the section is not in table * Create a new table entry. * End if * While there is a flag field left * Extract flag field. * Skip to next field. * Init flag field pointer. * If explicit state character * Skip over it. * End if * For each entry in flags list * If entry matches flag string * If reset requested * Reset flag. * Else * Set flag. * End if * Break for loop * End if * End for * End while * End if * End if * End do_flags * */ static do_flags(cp) char *cp; { char string[128]; char *xfield(), *skpbt(); struct tbl_data *rp, *make_entry(), *tbl_find(); struct flgwrd *flagp; char *stp; if (cp != NULL) { if (*cp != NULL) { cp = xfield(string,cp); cp = skpbt(cp); if (debug) {printf("do_flags: region \"%s\"\n",string);} if ((rp = tbl_find(string)) == NULL) { rp = make_entry(string); } while (*cp != NULL) { cp = xfield(string,cp); cp = skpbt(cp); if (debug) {printf("do_flags: got flag \"%s\"\n",string);} stp = string; if (*stp == '-' || *stp == '+') { stp++; } for (flagp = flgwrds; flagp->word != NULL; flagp++) { if (strcmp(flagp->word,stp) == 0) { if (string[0] == '-') { rp->flags &= ~(flagp->flag); } else { rp->flags |= flagp->flag; } break; } } } } } } /* * FUNCTION * * file_out reconfigure for new output file * * KEY WORDS * * dynamic reconfiguration * output file * * SYNOPSIS * * file_out(file_line) * char *file_line; * * DESCRIPTION * * Saves file name for output collection when specified * section is processed. Note that the name must * be stuffed safely away in static memory since * it will go away when one of this routines ancestors * exits. * */ /* * PSEUDO CODE * * Begin file_out * If passed pointer is not invalid then * If there is a line to use then * Extract name of section. * Skip to file name field. * If section is not in table * Create a new table entry. * End if * Extract name of file. * Allocate some static memory. * Save the name in static memory. * Remember where it is. * End if * End if * End file_out * */ file_out(cp) char *cp; { char string[128]; char *xfield(), *skpbt(); struct tbl_data *rp, *make_entry(), *tbl_find(); char *file_save; if (cp != NULL) { if (*cp != NULL) { cp = xfield(string,cp); cp = skpbt(cp); if ((rp = tbl_find(string)) == NULL) { rp = make_entry(string); } cp = xfield(string,cp); file_save = (char *) get_memory(strlen(string)+1); strcpy(file_save,string); rp->out = file_save; if (debug) {printf("file_out: out file \"%s\"\n",rp->out);} } } }