diff options
Diffstat (limited to 'agen5/defLoad.c')
-rw-r--r-- | agen5/defLoad.c | 486 |
1 files changed, 486 insertions, 0 deletions
diff --git a/agen5/defLoad.c b/agen5/defLoad.c new file mode 100644 index 0000000..bc36317 --- /dev/null +++ b/agen5/defLoad.c @@ -0,0 +1,486 @@ + +/** + * @file defLoad.c + * + * Time-stamp: "2012-03-04 11:02:51 bkorb" + * + * This module loads the definitions, calls yyparse to decipher them, + * and then makes a fixup pass to point all children definitions to + * their parent definition. + * + * This file is part of AutoGen. + * AutoGen Copyright (c) 1992-2012 by 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/>. + */ + +typedef enum { + INPUT_DONE, + INPUT_STDIN, + INPUT_FILE +} def_input_mode_t; + +static def_ent_t* pFreeEntryList = NULL; +static void* pAllocList = NULL; + +#define ENTRY_SPACE (4096 - sizeof(void*)) +#define ENTRY_ALLOC_CT (ENTRY_SPACE / sizeof(def_ent_t)) +#define ENTRY_ALLOC_SIZE \ + ((ENTRY_ALLOC_CT * sizeof(def_ent_t)) + sizeof(void*)) + +/* = = = START-STATIC-FORWARD = = = */ +static def_ent_t* +insert_ent(def_ent_t* pDef); + +static def_input_mode_t +ready_def_input(char const ** ppzfile, size_t * psz); +/* = = = END-STATIC-FORWARD = = = */ + +LOCAL def_ent_t * +new_def_ent(void) +{ + def_ent_t * pRes = pFreeEntryList; + + if (pRes != NULL) { + pFreeEntryList = pRes->de_next; + + } else { + int ct = ENTRY_ALLOC_CT-1; + void* p = AGALOC(ENTRY_ALLOC_SIZE, "def headers"); + + *((void**)p) = pAllocList; + pAllocList = p; + pRes = pFreeEntryList = (def_ent_t*)((void**)p + 1); + + /* + * This is a post-loop test loop. It will cycle one fewer times + * than there are 'def_ent_t' structs in the memory we just alloced. + */ + do { + def_ent_t* pNxt = pRes+1; + pRes->de_next = pNxt; + + /* + * When the loop ends, "pRes" will point to the last allocated + * structure instance. That is the one we will return. + */ + pRes = pNxt; + } while (--ct > 0); + + /* + * Unlink the last entry from the chain. The next time this + * routine is called, the *FIRST* structure in this list will + * be returned. + */ + pRes[-1].de_next = NULL; + } + + memset((void*)pRes, 0, sizeof(*pRes)); + return pRes; +} + +/* + * Append a new entry at the end of a sibling (or twin) list. + */ +LOCAL void +print_ent(def_ent_t * pDef) +{ + int ix = 32 - (2 * ent_stack_depth); + char const * space = PRINT_DEF_SPACES + ((ix < 0) ? 0 : ix); + + char const * vtyp; + + switch (pDef->de_type) { + case VALTYP_UNKNOWN: vtyp = DEF_TYPE_UNKNOWN; break; + case VALTYP_TEXT: vtyp = DEF_TYPE_TEXT; break; + case VALTYP_BLOCK: vtyp = DEF_TYPE_BLOCK; break; + default: vtyp = DEF_TYPE_INVALID; break; + } + + fprintf(trace_fp, PRINT_DEF_SHOW_FMT, + space, + pDef->de_name, (unsigned int)pDef->de_index, + vtyp, + pDef->de_file, pDef->de_line); +} + +/** + * Append a new entry into a sibling (or twin) list. + * + * @param pDef new definition + * @returns usually, the input, but sometimes it is necessary to move + * the data, so returns the address of the incoming data regardless. + */ +static def_ent_t* +insert_ent(def_ent_t* pDef) +{ + def_ent_t* pList = ent_stack[ ent_stack_depth ]; + + /* + * If the current level is empty, then just insert this one and quit. + */ + if (pList->de_val.dvu_entry == NULL) { + if (pDef->de_index == NO_INDEX) + pDef->de_index = 0; + pList->de_val.dvu_entry = pDef; + + return pDef; + } + pList = pList->de_val.dvu_entry; + + /* + * Scan the list looking for a "twin" (same-named entry). + */ + while (strcmp(pDef->de_name, pList->de_name) != 0) { + /* + * IF we are at the end of the list, + * THEN put the new entry at the end of the list. + * This is a new name in the current context. + * The value type is forced to be the same type. + */ + if (pList->de_next == NULL) { + pList->de_next = pDef; + + if (pDef->de_index == NO_INDEX) + pDef->de_index = 0; + + return pDef; + } + + /* + * Check the next sibling for a twin value. + */ + pList = pList->de_next; + } + + /* * * * * * WE HAVE FOUND A TWIN + * + * Link in the new twin chain entry into the list. + */ + if (pDef->de_index == NO_INDEX) { + def_ent_t* pT = pList->de_etwin; + if (pT == NULL) + pT = pList; + + pDef->de_index = pT->de_index + 1; + pT->de_twin = pDef; + pDef->de_ptwin = pT; + pList->de_etwin = pDef; + + } else if (pList->de_index > pDef->de_index) { + + /* + * Insert the new entry before any other in the list. + * We actually do this by leaving the pList pointer alone and swapping + * the contents of the definition entry. + */ + def_ent_t def = *pDef; + + memcpy(&(pDef->de_name), &(pList->de_name), + sizeof(def) - ag_offsetof(def_ent_t, de_name)); + + memcpy(&(pList->de_name), &(def.de_name), + sizeof(def) - ag_offsetof(def_ent_t, de_name)); + + /* + * Contents are swapped. Link "pDef" after "pList" and return "pList". + */ + pDef->de_twin = pList->de_twin; + if (pDef->de_twin != NULL) + pDef->de_twin->de_ptwin = pDef; + + pDef->de_ptwin = pList; + pList->de_twin = pDef; + + /* + * IF this is the first twin, then the original list head is now + * the "end twin". + */ + if (pList->de_etwin == NULL) + pList->de_etwin = pDef; + + pDef = pList; /* Return the replacement structure address */ + + } else { + def_ent_t* pL = pList; + def_ent_t* pT = pL->de_twin; + + /* + * Insert someplace after the first entry. Scan the list until + * we either find a larger index or we get to the end. + */ + while ((pT != NULL) && (pT->de_index < pDef->de_index)) { + pL = pT; + pT = pT->de_twin; + } + + pDef->de_twin = pT; + + pL->de_twin = pDef; + pDef->de_ptwin = pL; + if (pT == NULL) + pList->de_etwin = pDef; + else + pT->de_ptwin = pDef; + } + + return pDef; /* sometimes will change */ +} + +/** + * Figure out where to insert an entry in a list of twins. + */ +LOCAL def_ent_t * +number_and_insert_ent(char * name, char const * idx_str) +{ + def_ent_t * ent = new_def_ent(); + + ent->de_name = name; + + if (idx_str == NULL) + ent->de_index = NO_INDEX; + + else if (IS_SIGNED_NUMBER_CHAR(*idx_str)) + ent->de_index = strtol(idx_str, NULL, 0); + + else { + idx_str = get_define_str(idx_str, true); + if (idx_str != NULL) + ent->de_index = strtol(idx_str, NULL, 0); + else ent->de_index = NO_INDEX; + } + + strtransform(ent->de_name, ent->de_name); + ent->de_type = VALTYP_UNKNOWN; + ent->de_file = (char*)cctx->scx_fname; + ent->de_line = cctx->scx_line; + return (curr_ent = insert_ent(ent)); +} + +/** + * figure out which file descriptor to use for reading definitions. + */ +static def_input_mode_t +ready_def_input(char const ** ppzfile, size_t * psz) +{ + struct stat stbf; + + if (! ENABLED_OPT(DEFINITIONS)) { + base_ctx = (scan_ctx_t*)AGALOC(sizeof(scan_ctx_t), "scan context"); + memset((void*)base_ctx, 0, sizeof(scan_ctx_t)); + base_ctx->scx_line = 1; + base_ctx->scx_fname = READY_INPUT_NODEF; + + if (! ENABLED_OPT(SOURCE_TIME)) + outfile_time = time(NULL); + return INPUT_DONE; + } + + *ppzfile = OPT_ARG(DEFINITIONS); + + if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS) + fprintf(trace_fp, TRACE_DEF_LOAD); + + /* + * Check for stdin as the input file. We use the current time + * as the modification time for stdin. We also note it so we + * do not try to open it and we try to allocate more memory if + * the stdin input exceeds our initial allocation of 16K. + */ + if (strcmp(*ppzfile, "-") == 0) { + *ppzfile = OPT_ARG(DEFINITIONS) = "stdin"; + if (getenv(REQUEST_METHOD) != NULL) { + loadCgi(); + cctx = base_ctx; + dp_run_fsm(); + return INPUT_DONE; + } + + accept_fifo: + outfile_time = time(NULL); + *psz = 0x4000 - (4+sizeof(*base_ctx)); + return INPUT_STDIN; + } + + /* + * This, then, must be a regular file. Make sure of that and + * find out how big it was and when it was last modified. + */ + if (stat(*ppzfile, &stbf) != 0) + AG_CANT(READY_INPUT_STAT, *ppzfile); + + if (! S_ISREG(stbf.st_mode)) { + if (S_ISFIFO(stbf.st_mode)) + goto accept_fifo; + + errno = EINVAL; + AG_CANT(READY_INPUT_NOT_REG, *ppzfile); + } + + /* + * IF the source-time option has been enabled, then + * our output file mod time will start as one second after + * the mod time on this file. If any of the template files + * are more recent, then it will be adjusted. + */ + *psz = stbf.st_size; + + outfile_time = ENABLED_OPT(SOURCE_TIME) ? stbf.st_mtime : time(NULL); + + return INPUT_FILE; +} + +/** + * Suck in the entire definitions file and parse it. + */ +LOCAL void +read_defs(void) +{ + char const * pzDefFile; + char * pzData; + size_t dataSize; + size_t sizeLeft; + FILE * fp; + def_input_mode_t in_mode = ready_def_input(&pzDefFile, &dataSize); + + if (in_mode == INPUT_DONE) + return; + + /* + * Allocate the space we need for our definitions. + */ + sizeLeft = dataSize+4+sizeof(*base_ctx); + base_ctx = (scan_ctx_t*)AGALOC(sizeLeft, "file buf"); + memset((void*)base_ctx, 0, sizeLeft); + base_ctx->scx_line = 1; + sizeLeft = dataSize; + + /* + * Our base context will have its currency pointer set to this + * input. It is also a scanning pointer, but since this buffer + * is never deallocated, we do not have to remember the initial + * value. (It may get reallocated here in this routine, tho...) + */ + pzData = + base_ctx->scx_scan = + base_ctx->scx_data = (char*)(base_ctx+1); + base_ctx->scx_next = NULL; + + /* + * Set the input file pointer, as needed + */ + if (in_mode == INPUT_STDIN) + fp = stdin; + + else { + fp = fopen(pzDefFile, "r" FOPEN_TEXT_FLAG); + if (fp == NULL) + AG_CANT(READ_DEF_OPEN, pzDefFile); + + if (dep_fp != NULL) + add_source_file(pzDefFile); + } + + /* + * Read until done... + */ + for (;;) { + size_t rdct = fread((void*)pzData, (size_t)1, sizeLeft, fp); + + /* + * IF we are done, + */ + if (rdct == 0) { + /* + * IF it is because we are at EOF, then break out + * ELSE abend. + */ + if (feof(fp) || (in_mode == INPUT_STDIN)) + break; + + AG_CANT(READ_DEF_READ, pzDefFile); + } + + /* + * Advance input pointer, decrease remaining count + */ + pzData += rdct; + sizeLeft -= rdct; + + /* + * See if there is any space left + */ + if (sizeLeft == 0) { + scan_ctx_t* p; + off_t dataOff; + + /* + * IF it is a regular file, then we are done + */ + if (in_mode != INPUT_STDIN) + break; + + /* + * We have more data and we are out of space. + * Try to reallocate our input buffer. + */ + dataSize += (sizeLeft = 0x1000); + dataOff = pzData - base_ctx->scx_data; + p = AGREALOC((void*)base_ctx, dataSize+4+sizeof(*base_ctx), + "expand f buf"); + + /* + * The buffer may have moved. Set the data pointer at an + * offset within the new buffer and make sure our base pointer + * has been corrected as well. + */ + if (p != base_ctx) { + p->scx_scan = \ + p->scx_data = (char*)(p+1); + pzData = p->scx_data + dataOff; + base_ctx = p; + } + } + } + + if (pzData == base_ctx->scx_data) + AG_ABEND(READ_DEF_NO_DEFS); + + *pzData = NUL; + AGDUPSTR(base_ctx->scx_fname, pzDefFile, "def file name"); + + /* + * Close the input file, parse the data + * and alphabetically sort the definition tree contents. + */ + if (in_mode != INPUT_STDIN) + fclose(fp); + + cctx = base_ctx; + dp_run_fsm(); +} + + +LOCAL void +unload_defs(void) +{ + return; +} +/* + * Local Variables: + * mode: C + * c-file-style: "stroustrup" + * indent-tabs-mode: nil + * End: + * end of agen5/defLoad.c */ |