diff options
Diffstat (limited to 'agen5/tpLoad.c')
-rw-r--r-- | agen5/tpLoad.c | 560 |
1 files changed, 560 insertions, 0 deletions
diff --git a/agen5/tpLoad.c b/agen5/tpLoad.c new file mode 100644 index 0000000..9180662 --- /dev/null +++ b/agen5/tpLoad.c @@ -0,0 +1,560 @@ + +/** + * @file tpLoad.c + * + * Time-stamp: "2012-08-11 08:55:46 bkorb" + * + * This module will load a template and return a template structure. + * + * This file is part of AutoGen. + * Copyright (c) 1992-2012 Bruce Korb - all rights reserved + * + * AutoGen is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AutoGen is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* = = = START-STATIC-FORWARD = = = */ +static bool +read_okay(char const * pzFName); + +static char const * +expand_dir(char const ** dir_pp, char * name_buf); + +static size_t +cnt_macros(char const * pz); + +static void +load_macs(templ_t * pT, char const * pzF, char const * pzN, + char const * pzData); + +static templ_t * +digest_tpl(tmap_info_t * minfo, char * fname); +/* = = = END-STATIC-FORWARD = = = */ + +/** + * Return the template structure matching the name passed in. + */ +LOCAL templ_t * +find_tpl(char const * pzTemplName) +{ + templ_t * pT = named_tpls; + while (pT != NULL) { + if (streqvcmp(pzTemplName, pT->td_name) == 0) + break; + pT = (templ_t*)(void*)(pT->td_scan); + } + return pT; +} + +/** + * the name is a regular file with read access. + * @param[in] pzFName file name to check + * @returns \a true when the named file exists and is a regular file + * @returns \a false otherwise. + */ +static bool +read_okay(char const * pzFName) +{ + struct stat stbf; + if (stat(pzFName, &stbf) != 0) + return false; + if (! S_ISREG(stbf.st_mode)) + return false; + return (access(pzFName, R_OK) == 0) ? true : false; +} + +/** + * Expand a directory name that starts with '$'. + * + * @param[in,out] dir_pp pointer to pointer to directory name + * @returns the resulting pointer + */ +static char const * +expand_dir(char const ** dir_pp, char * name_buf) +{ + char * res = (void *)*dir_pp; + + if (res[1] == NUL) + AG_ABEND(aprf(LOAD_FILE_SHORT_NAME, res)); + + if (! optionMakePath(name_buf, (int)AG_PATH_MAX, res, + autogenOptions.pzProgPath)) { + /* + * The name expanded to "empty", so substitute curdir. + */ + strcpy(res, FIND_FILE_CURDIR); + + } else { + free(res); + AGDUPSTR(res, name_buf, "find dir name"); + *dir_pp = res; /* save computed name for later */ + } + + return res; +} + +/** + * Search for a file. + * + * Starting with the current directory, search the directory list trying to + * find the base template file name. If there is a referring template (a + * template with an "INCLUDE" macro), then try that, too, before giving up. + * + * @param[in] in_name the file name we are looking for. + * @param[out] res_name where we stash the file name we found. + * @param[in] sfx_list a list of suffixes to try, if \a in_name has none. + * @param[in] referring_tpl file name of the template with a INCLUDE macro. + * + * @returns \a SUCCESS when \a res_name is valid + * @returns \a FAILURE when the file is not found. + */ +LOCAL tSuccess +find_file(char const * in_name, + char * res_name, + char const * const * sfx_list, + char const * referring_tpl) +{ + bool no_suffix; + void * free_me = NULL; + tSuccess res = SUCCESS; + + size_t nm_len = strlen(in_name); + if (nm_len >= AG_PATH_MAX - MAX_SUFFIX_LEN) + return FAILURE; + + /* + * Expand leading environment variables. + * We will not mess with embedded ones. + */ + if (*in_name == '$') { + if (! optionMakePath(res_name, (int)AG_PATH_MAX, in_name, + autogenOptions.pzProgPath)) + return FAILURE; + + AGDUPSTR(in_name, res_name, "find file name"); + free_me = (void*)in_name; + + /* + * in_name now points to the name the file system can use. + * It must _not_ point to res_name because we will likely + * rewrite that value using this pointer! + */ + nm_len = strlen(in_name); + } + + /* + * Not a complete file name. If there is not already + * a suffix for the file name, then append ".tpl". + * Check for immediate access once again. + */ + { + char * bf = strrchr(in_name, '/'); + bf = (bf != NULL) ? strchr(bf, '.') : strchr(in_name, '.'); + no_suffix = (bf == NULL); + } + + /* + * The referrer is useful only if it includes a directory name. + * If not NULL, referring_tpl becomes an allocated directory name. + */ + if (referring_tpl != NULL) { + char * pz = strrchr(referring_tpl, '/'); + if (pz == NULL) + referring_tpl = NULL; + else { + AGDUPSTR(referring_tpl, referring_tpl, "referring_tpl"); + pz = strrchr(referring_tpl, '/'); + *pz = NUL; + } + } + + { + /* + * Search each directory in our directory search list for the file. + * We always force two copies of this option, so we know it exists. + * Later entries are more recently added and are searched first. + * We start the "dirlist" pointing to the real last entry. + */ + int ct = STACKCT_OPT(TEMPL_DIRS); + char const ** dirlist = STACKLST_OPT(TEMPL_DIRS) + ct - 1; + char const * c_dir = FIND_FILE_CURDIR; + + /* + * IF the file name starts with a directory separator, + * then we only search once, looking for the exact file name. + */ + if (*in_name == '/') + ct = -1; + + for (;;) { + char * pzEnd; + + /* + * c_dir is always FIND_FILE_CURDIR the first time through + * and is never that value after that. + */ + if (c_dir == FIND_FILE_CURDIR) { + + memcpy(res_name, in_name, nm_len); + pzEnd = res_name + nm_len; + *pzEnd = NUL; + + } else { + int fmt_len; + + /* + * IF one of our template paths starts with '$', then expand it + * and replace it now and forever (the rest of this run, anyway). + */ + if (*c_dir == '$') + c_dir = expand_dir(dirlist+1, res_name); + + fmt_len = snprintf(res_name, AG_PATH_MAX - MAX_SUFFIX_LEN, + FIND_FILE_DIR_FMT, c_dir, in_name); + if (fmt_len >= AG_PATH_MAX - MAX_SUFFIX_LEN) + break; // fail-return + pzEnd = res_name + fmt_len; + } + + if (read_okay(res_name)) + goto find_file_done; + + /* + * IF the file does not already have a suffix, + * THEN try the ones that are okay for this file. + */ + if (no_suffix && (sfx_list != NULL)) { + char const * const * sfxl = sfx_list; + *(pzEnd++) = '.'; + + do { + strcpy(pzEnd, *(sfxl++)); /* must fit */ + if (read_okay(res_name)) + goto find_file_done; + + } while (*sfxl != NULL); + } + + /* + * IF we've exhausted the search list, + * THEN see if we're done, else go through search dir list. + * + * We try one more thing if there is a referrer. + * If the searched-for file is a full path, "ct" will + * start at -1 and we will leave the loop here and now. + */ + if (--ct < 0) { + if ((referring_tpl == NULL) || (ct != -1)) + break; + c_dir = referring_tpl; + + } else { + c_dir = *(dirlist--); + } + } + } + + res = FAILURE; + + find_file_done: + AGFREE(free_me); + AGFREE(referring_tpl); + return res; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/** + * Count the macros in a template. + * We need to allocate the right number of pointers. + */ +static size_t +cnt_macros(char const * pz) +{ + size_t ct = 2; + for (;;) { + pz = strstr(pz, st_mac_mark); + if (pz == NULL) + break; + ct += 2; + if (strncmp(pz - end_mac_len, end_mac_mark, end_mac_len) == 0) + ct--; + pz += st_mac_len; + } + return ct; +} + + +/** + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * Load the macro array and file name. + */ +static void +load_macs(templ_t * pT, char const * pzF, char const * pzN, + char const * pzData) +{ + macro_t* pMac = pT->td_macros; + + { + char* pzText = (char*)(pMac + pT->td_mac_ct); + size_t len; + + AGDUPSTR(pT->td_file, pzF, "templ file"); + + len = strlen(pzN) + 1; + memcpy((void*)pzText, (void*)pzN, len); + pT->td_name = pzText; + pzText += len; + pT->td_text = pzText; + pT->td_scan = pzText + 1; + } + + current_tpl = pT; + + { + macro_t* pMacEnd = parse_tpl(pMac, &pzData); + int ct; + + /* + * Make sure all of the input string was scanned. + */ + if (pzData != NULL) + AG_ABEND(LOAD_MACS_BAD_PARSE); + + ct = pMacEnd - pMac; + + /* + * IF there are empty macro slots, + * THEN pack the text + */ + if (ct < pT->td_mac_ct) { + int delta = sizeof(macro_t) * (pT->td_mac_ct - ct); + void* data = + (pT->td_name == NULL) ? pT->td_text : pT->td_name; + size_t size = pT->td_scan - (char*)data; + memmove((void*)pMacEnd, data, size); + + pT->td_text -= delta; + pT->td_scan -= delta; + pT->td_name -= delta; + pT->td_mac_ct = ct; + } + } + + pT->td_size = pT->td_scan - (char*)pT; + pT->td_scan = NULL; + + /* + * We cannot reallocate a smaller array because + * the entries are all linked together and + * realloc-ing it may cause it to move. + */ +#if defined(DEBUG_ENABLED) + if (HAVE_OPT(SHOW_DEFS)) { + static char const zSum[] = + "loaded %d macros from %s\n" + "\tBinary template size: 0x%zX\n\n"; + fprintf(trace_fp, zSum, pT->td_mac_ct, pzF, pT->td_size); + } +#endif +} + +/** + * Load a template from mapped memory. Load up the pseudo macro, + * count the macros, allocate the data, and parse all the macros. + * + * @param[in] minfo information about the mapped memory. + * @param[in] fname the full path input file name. + * + * @returns the digested data + */ +static templ_t * +digest_tpl(tmap_info_t * minfo, char * fname) +{ + templ_t * res; + + /* + * Count the number of macros in the template. Compute + * the output data size as a function of the number of macros + * and the size of the template data. These may get reduced + * by comments. + */ + char const * dta = + loadPseudoMacro((char const *)minfo->txt_data, fname); + + size_t mac_ct = cnt_macros(dta); + size_t alloc_sz = (sizeof(*res) + (mac_ct * sizeof(macro_t)) + + minfo->txt_size + - (dta - (char const *)minfo->txt_data) + + strlen(fname) + 0x10) & ~0x0F; + + res = (templ_t*)AGALOC(alloc_sz, "main template"); + memset((void*)res, 0, alloc_sz); + + /* + * Initialize the values: + */ + res->td_magic = magic_marker; + res->td_size = alloc_sz; + res->td_mac_ct = mac_ct; + + strcpy(res->td_start_mac, st_mac_mark); /* must fit */ + strcpy(res->td_end_mac, end_mac_mark); /* must fit */ + load_macs(res, fname, PSEUDO_MAC_TPL_FILE, dta); + + res->td_name -= (long)res; + res->td_text -= (long)res; + res = (templ_t*)AGREALOC((void*)res, res->td_size, + "resize template"); + res->td_name += (long)res; + res->td_text += (long)res; + + return res; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/** + * Starting with the current directory, search the directory + * list trying to find the base template file name. + */ +LOCAL templ_t * +tpl_load(char const * fname, char const * referrer) +{ + static tmap_info_t map_info; + static char tpl_file[ AG_PATH_MAX ]; + + /* + * Find the template file somewhere + */ + { + static char const * const sfx_list[] = { + LOAD_TPL_SFX_TPL, LOAD_TPL_SFX_AGL, NULL }; + if (! SUCCESSFUL(find_file(fname, tpl_file, sfx_list, referrer))) { + errno = ENOENT; + AG_CANT(LOAD_TPL_CANNOT_MAP, fname); + } + } + + /* + * Make sure the specified file is a regular file. + * Make sure the output time stamp is at least as recent. + */ + { + struct stat stbf; + if (stat(tpl_file, &stbf) != 0) + AG_CANT(LOAD_TPL_CANNOT_STAT, fname); + + if (! S_ISREG(stbf.st_mode)) { + errno = EINVAL; + AG_CANT(LOAD_TPL_IRREGULAR, fname); + } + + if (outfile_time < stbf.st_mtime) + outfile_time = stbf.st_mtime; + } + + text_mmap(tpl_file, PROT_READ|PROT_WRITE, MAP_PRIVATE, &map_info); + if (TEXT_MMAP_FAILED_ADDR(map_info.txt_data)) + AG_ABEND(aprf(LOAD_TPL_CANNOT_OPEN, tpl_file)); + + if (dep_fp != NULL) + add_source_file(tpl_file); + + /* + * Process the leading pseudo-macro. The template proper + * starts immediately after it. + */ + { + macro_t * sv_mac = cur_macro; + templ_t * res; + cur_macro = NULL; + + res = digest_tpl(&map_info, tpl_file); + cur_macro = sv_mac; + text_munmap(&map_info); + + return res; + } +} + +/** + * Deallocate anything related to a template. + * This includes the pointer passed in and any macros that have an + * unload procedure associated with it. + * + * @param[in] tpl the template to unload + */ +LOCAL void +tpl_unload(templ_t * tpl) +{ + macro_t * mac = tpl->td_macros; + int ct = tpl->td_mac_ct; + + while (--ct >= 0) { + unload_proc_p_t proc; + unsigned int ix = mac->md_code; + + /* + * "select" functions get remapped, depending on the alias used for + * the selection. See the "mac_func_t" enumeration in functions.h. + */ + if (ix >= FUNC_CT) + ix = FTYP_SELECT; + + proc = unload_procs[ ix ]; + if (proc != NULL) + (*proc)(mac); + + mac++; + } + + AGFREE((void*)(tpl->td_file)); + AGFREE(tpl); +} + +/** + * This gets called when all is well at the end. + * The supplied template and all named templates are unloaded. + * + * @param[in] tpl the last template standing + */ +LOCAL void +cleanup(templ_t * tpl) +{ + if (HAVE_OPT(USED_DEFINES)) + print_used_defines(); + + if (dep_fp != NULL) + wrap_up_depends(); + + optionFree(&autogenOptions); + + for (;;) { + tpl_unload(tpl); + tpl = named_tpls; + if (tpl == NULL) + break; + named_tpls = (templ_t*)(void*)(tpl->td_scan); + } + + free_for_context(true); + unload_defs(); +} + +/* + * Local Variables: + * mode: C + * c-file-style: "stroustrup" + * indent-tabs-mode: nil + * End: + * end of agen5/tpLoad.c */ |