LOCAL void yyerror(char* s) { static char const yyerr_fmt[] = "%s: in %s on line %d\n" "\ttoken in error: %s\n" "\t[[...<error-text>]] %s\n\n" "Likely causes: a mismatched quote, a value that needs " "quoting,\n\t\tor a missing semi-colon\n"; static char const zErrTkn[] = "%s: ``%s''\n"; static char const zDf[] = "`%s'\n"; char* pz; if (strlen(pCurCtx->pzScan) > 64 ) pCurCtx->pzScan[64] = NUL; switch (lastToken) { case DP_EV_VAR_NAME: case DP_EV_OTHER_NAME: case DP_EV_STRING: case DP_EV_NUMBER: if (strlen(pz_token) > 64 ) pz_token[64] = NUL; pz = aprf(zErrTkn, DP_EVT_NAME(lastToken), pz_token); break; default: pz = aprf(zDf, DP_EVT_NAME(lastToken)); } AG_ABEND(aprf(yyerr_fmt, s, pCurCtx->pzCtxFname, pCurCtx->lineNo, pz, pCurCtx->pzScan)); }
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Support routines for the directives * * skipToEndif * * Skip through the text to a matching "#endif". We do this when we * have processed the allowable text (found an "#else" after * accepting the preceeding text) or when encountering a "#if*def" * while skipping a block of text due to a failed test. */ static char* skipToEndif(char* pzStart) { char* pzScan = pzStart; char* pzRet; for (;;) { /* * 'pzScan' is pointing to the first character on a line. * Check for a directive on the current line before scanning * later lines. */ if (*pzScan == '#') pzScan++; else { char* pz = strstr(pzScan, zCheckList); if (pz == NULL) AG_ABEND(aprf(zNoEndif, pCurCtx->pzCtxFname, pCurCtx->lineNo)); pzScan = pz + STRSIZE(zCheckList); } while (IS_WHITESPACE_CHAR(*pzScan)) pzScan++; switch (findDirective(pzScan)) { case DIR_ENDIF: { /* * We found the endif we are interested in */ char* pz = strchr(pzScan, NL); if (pz != NULL) pzRet = pz+1; else pzRet = pzScan + strlen(pzScan); goto leave_func; } case DIR_IFDEF: case DIR_IFNDEF: /* * We found a nested ifdef/ifndef */ pzScan = skipToEndif(pzScan); break; default: /* * We do not care what we found */ break; /* ignore it */ } /* switch (findDirective(pzScan)) */ } leave_func: while (pzStart < pzRet) { if (*(pzStart++) == NL) pCurCtx->lineNo++; } return pzRet; }
/*=directive error * * arg: [ <descriptive text> ] * * text: * This directive will cause AutoGen to stop processing * and exit with a status of EXIT_FAILURE. =*/ static char* doDir_error(char* pzArg, char* pzScan) { AG_ABEND(aprf("#error directive -- in %s on line %d\n\t%s\n", pCurCtx->pzCtxFname, pCurCtx->lineNo, pzArg)); /* NOTREACHED */ return NULL; }
static void check_assert_str(char const * pz, char const * arg) { pz = SPN_WHITESPACE_CHARS(pz); if (IS_FALSE_TYPE_CHAR(*pz)) AG_ABEND(aprf(DIRECT_ASSERT_FMT, pz, arg)); }
/** * Starting with the current directory, search the directory * list trying to find the base template file name. */ LOCAL tTemplate * loadTemplate(char const * pzFileName, char const * referrer) { static tmap_info_t mapInfo; static char zRealFile[ AG_PATH_MAX ]; /* * Find the template file somewhere */ { static char const * const apzSfx[] = { "tpl", "agl", NULL }; if (! SUCCESSFUL(findFile(pzFileName, zRealFile, apzSfx, referrer))) { errno = ENOENT; AG_CANT("map data file", pzFileName); } } /* * 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(zRealFile, &stbf) != 0) AG_CANT("stat file", pzFileName); if (! S_ISREG(stbf.st_mode)) { errno = EINVAL; AG_CANT("not regular file", pzFileName); } if (outTime <= stbf.st_mtime) outTime = stbf.st_mtime + 1; } text_mmap(zRealFile, PROT_READ|PROT_WRITE, MAP_PRIVATE, &mapInfo); if (TEXT_MMAP_FAILED_ADDR(mapInfo.txt_data)) AG_ABEND(aprf("Could not open template '%s'", zRealFile)); if (pfDepends != NULL) append_source_name(zRealFile); /* * Process the leading pseudo-macro. The template proper * starts immediately after it. */ { tMacro * pSaveMac = pCurMacro; tTemplate * pRes; pCurMacro = NULL; pRes = digest_pseudo_macro(&mapInfo, zRealFile); pCurMacro = pSaveMac; text_munmap(&mapInfo); return pRes; } }
/*=gfunc prefix * * what: prefix lines with a string * general_use: * * exparg: prefix, string to insert at start of each line * exparg: text, multi-line block of text * * doc: * Prefix every line in the second string with the first string. * * For example, if the first string is "# " and the second contains: * @example * two * lines * @end example * @noindent * The result string will contain: * @example * # two * # lines * @end example =*/ SCM ag_scm_prefix(SCM prefix, SCM text) { char* pzPfx; char* pzText; char* pzDta; size_t out_size, rem_size; size_t pfx_size; char* pzRes; pzPfx = ag_scm2zchars(prefix, "prefix"); pzDta = \ pzText = ag_scm2zchars(text, "text"); pfx_size = strlen(pzPfx); out_size = pfx_size; for (;;) { switch (*(pzText++)) { case NUL: goto exit_count; case NL: out_size += pfx_size; /* FALLTHROUGH */ default: out_size++; } } exit_count:; pzText = pzDta; pzRes = pzDta = ag_scribble(rem_size = out_size); strcpy(pzDta, pzPfx); pzDta += pfx_size; rem_size -= pfx_size; pfx_size++; for (;;) { char ch = *(pzText++); switch (ch) { case NUL: if (rem_size != 0) AG_ABEND(PREFIX_FAIL); return AG_SCM_STR2SCM(pzRes, out_size); case NL: *pzDta = ch; strcpy(pzDta+1, pzPfx); pzDta += pfx_size; rem_size -= pfx_size; break; default: rem_size--; *(pzDta++) = ch; break; } } }
/*=directive elif * * text: * This must follow an @code{#if} * otherwise it will generate an error. * It will be ignored. =*/ static char* doDir_elif(char* pzArg, char* pzScan) { static char const z[] = "`#elif' directive encountered out of context\n\tin %s on line %d\n"; AG_ABEND(aprf(z, pCurCtx->pzCtxFname, pCurCtx->lineNo)); /* NOTREACHED */ return NULL; }
/** * @code{#endshell}, @code{#elif} and @code{#endmac} directives, when found * via a non-nested scan, are always in error. They should only be found * during the handling of @code{#shell}, @code{#if} and @code{#macdef} * directives. @code{#else} and @code{#endif} are in error if not matched * with a previous @code{#ifdef} or @code{#ifndef} directive. */ static char * bad_dirv_ctx(directive_enum_t id, char const * arg, char * scan_next) { AG_ABEND(aprf(DIRECT_BAD_CTX_FMT, directive_name(id), cctx->scx_fname, cctx->scx_line)); (void)arg; (void)id; /* NOTREACHED */ return scan_next; }
/** * This directive will cause AutoGen to stop processing * and exit with a status of EXIT_FAILURE. */ char * doDir_error(directive_enum_t id, char const * arg, char * scan_next) { arg = SPN_WHITESPACE_CHARS(arg); AG_ABEND(aprf(DIRECT_ERROR_FMT, cctx->scx_fname, cctx->scx_line, arg)); /* NOTREACHED */ (void)scan_next; (void)id; return NULL; }
/*=directive assert * * arg: `shell-script` | (scheme-expr) | <anything else> * * text: * If the @code{shell-script} or @code{scheme-expr} do not yield @code{true} * valued results, autogen will be aborted. If @code{<anything else>} or * nothing at all is provided, then this directive is ignored. * * When writing the shell script, remember this is on a preprocessing * line. Multiple lines must be backslash continued and the result is a * single long line. Separate multiple commands with semi-colons. * * The result is @code{false} (and fails) if the result is empty, the * number zero, or a string that starts with the letters 'n' or 'f' ("no" * or "false"). =*/ static void check_assert_str(char const* pz, char const* pzArg) { static char const fmt[] = "#assert yielded \"%s\":\n\t`%s`"; while (IS_WHITESPACE_CHAR(*pz)) pz++; if (IS_FALSE_TYPE_CHAR(*pz)) AG_ABEND(aprf(fmt, pz, pzArg)); }
static void compile_re(regex_t* pRe, char const * pzPat, int flags) { void * const pat = (void *)pzPat; int rerr = regcomp(pRe, pat, flags); if (rerr != 0) { char zEr[ SCRIBBLE_SIZE ]; regerror(rerr, pRe, zEr, sizeof(zEr)); fprintf(stderr, zBadRe, rerr, zEr, pzPat); AG_ABEND("Bad regular expression"); } }
/** * Skip through the text to a matching "#endif". We do this when we * have processed the allowable text (found an "#else" after * accepting the preceding text) or when encountering a "#if*def" * while skipping a block of text due to a failed test. */ static char * skip_to_endif(char * start) { bool skipping_if = skip_if_block; char * scan = start; char * dirv_end; for (;;) { scan = next_directive(scan); switch (find_directive(scan)) { case DIR_ENDIF: { /* * We found the endif we are interested in */ char * pz = strchr(scan, NL); if (pz != NULL) dirv_end = pz+1; else dirv_end = scan + strlen(scan); goto leave_func; } case DIR_ELIF: if (! skip_if_block) AG_ABEND(ELIF_CONTEXT_MSG); break; case DIR_IF: skip_if_block = true; /* FALLTHROUGH */ case DIR_IFDEF: case DIR_IFNDEF: /* * We found a nested conditional, so skip to endif nested, too. */ scan = skip_to_endif(scan); skip_if_block = skipping_if; break; default: /* * We do not care what we found */ break; /* ignore it */ } /* switch (find_directive(scan)) */ } leave_func: cctx->scx_line += count_lines(start, dirv_end); return dirv_end; }
/** * Print out an invalid transition message and return EXIT_FAILURE */ static int cgi_invalid_transition( te_cgi_state st, te_cgi_event evt ) { /* START == INVALID TRANS MSG == DO NOT CHANGE THIS COMMENT */ char * pz = aprf( zCgiStrings + CgiFsmErr_off, st, CGI_STATE_NAME( st ), evt, CGI_EVT_NAME( evt )); AG_ABEND( aprf(CGI_PARSE_ERR_FMT, pz )); /* END == INVALID TRANS MSG == DO NOT CHANGE THIS COMMENT */ return EXIT_FAILURE; }
static void init_scm(void) { last_scm_cmd = SCHEME_INIT_TEXT; { SCM ini_res = ag_scm_c_eval_string_from_file_line( SCHEME_INIT_TEXT, AG_TEXT_STRTABLE_FILE, SCHEME_INIT_TEXT_LINENO); AGDUPSTR(libguile_ver, scm2display(ini_res), "ini res"); } { unsigned int maj, min, mic; switch (sscanf(libguile_ver, "%u.%u.%u", &maj, &min, &mic)) { case 2: case 3: break; default: AG_ABEND(aprf(GUILE_VERSION_BAD, libguile_ver)); /* NOT_REACHED */ } maj = min + (100 * maj); if ((GUILE_VERSION / 1000) != maj) AG_ABEND(aprf(GUILE_VERSION_WRONG, libguile_ver, MK_STR(GUILE_VERSION))); } { # if GUILE_VERSION >= 200000 # define SCHEME_INIT_DEBUG SCHEME_INIT_DEBUG_2_0 # else # define SCHEME_INIT_DEBUG SCHEME_INIT_DEBUG_1_6 # endif char * p = aprf(INIT_SCM_ERRS_FMT, SCHEME_INIT_DEBUG); # undef SCHEME_INIT_DEBUG last_scm_cmd = p; ag_scm_c_eval_string_from_file_line(p, __FILE__, __LINE__); AGFREE(p); } }
static void loadScheme(void) { char* pzText = pCurCtx->pzScan; char* pzEnd = (char*)skipScheme(pzText, pzText + strlen(pzText)); char endCh = *pzEnd; int schemeLen = (pzEnd - pzText); int next_ln; SCM res; /* * NUL terminate the Scheme expression, run it, then restore * the NUL-ed character. */ if (*pzEnd == NUL) AG_ABEND(aprf(zErrMsg, pzProg, "end of Guile/scheme expression not found", pCurCtx->pzCtxFname, pCurCtx->lineNo)); *pzEnd = NUL; next_ln = pCurCtx->lineNo + count_nl(pzText); procState = PROC_STATE_GUILE_PRELOAD; res = ag_scm_c_eval_string_from_file_line( pzText, pCurCtx->pzCtxFname, pCurCtx->lineNo ); procState = PROC_STATE_LOAD_DEFS; *pzEnd = endCh; pCurCtx->pzScan = pzEnd; pzEnd = (char*)resolveSCM(res); /* ignore const-ness */ pCurCtx->lineNo = next_ln; if (strlen(pzEnd) >= schemeLen) { AGDUPSTR(pzEnd, pzEnd, "SCM Result"); pz_token = pzEnd; manageAllocatedData(pz_token); } else { /* * We know the result is smaller than the source. Copy in place. */ strcpy(pzText, pzEnd); pz_token = pzText; } lastToken = DP_EV_STRING; }
static char * next_directive(char * scan) { if (*scan == '#') scan++; else { char * pz = strstr(scan, DIRECT_CK_LIST_MARK); if (pz == NULL) AG_ABEND(aprf(DIRECT_NOENDIF_FMT, cctx->scx_fname, cctx->scx_line)); scan = pz + 2; } return SPN_WHITESPACE_CHARS(scan); }
static char* skipToEndmac(char* pzStart) { char* pzScan = pzStart; char* pzRet; for (;;) { /* * 'pzScan' is pointing to the first character on a line. * Check for a directive on the current line before scanning * later lines. */ if (*pzScan == '#') pzScan++; else { char* pz = strstr(pzScan, zCheckList); if (pz == NULL) AG_ABEND(aprf(zNoEndif, pCurCtx->pzCtxFname, pCurCtx->lineNo)); pzScan = pz + STRSIZE(zCheckList); } while (IS_WHITESPACE_CHAR(*pzScan)) pzScan++; if (findDirective(pzScan) == DIR_ENDMAC) { /* * We found the endmac we are interested in */ char* pz = strchr(pzScan, NL); if (pz != NULL) pzRet = pz+1; else pzRet = pzScan + strlen(pzScan); break; } } while (pzStart < pzRet) { if (*(pzStart++) == NL) pCurCtx->lineNo++; } return pzRet; }
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * A quoted string has been found. * Find the end of it and compress any escape sequences. */ static char* assembleHereString(char* pzScan) { static char const endless[] = "Unterminated HereString"; ag_bool trimTabs = AG_FALSE; char zMark[ MAX_HEREMARK_LEN ]; size_t markLen = 0; char* pzDest; int here_string_line_no; /* * See if we are to strip leading tab chars */ if (*pzScan == '-') { trimTabs = AG_TRUE; pzScan++; } /* * Skip white space up to the marker or EOL */ while (IS_WHITESPACE_CHAR(*pzScan)) { if (*pzScan++ == NL) AG_ABEND(aprf(zErrMsg, pzProg, "HereString missing the mark", pCurCtx->pzCtxFname, pCurCtx->lineNo)); } /* * Copy the marker, noting its length */ { char* pz = zMark; while (IS_VARIABLE_NAME_CHAR(*pzScan)) { if (++markLen >= sizeof(zMark)) AG_ABEND(aprf(zErrMsg, pzProg, "HereString mark " STR(MAX_HEREMARK_LEN) " or more chars", pCurCtx->pzCtxFname, pCurCtx->lineNo)); *(pz++) = *(pzScan++); } if (markLen == 0) AG_ABEND(aprf(zErrMsg, pzProg, "HereString missing the mark", pCurCtx->pzCtxFname, pCurCtx->lineNo)); *pz = NUL; } pzDest = pzScan; pz_token = pzDest; /* * Skip forward to the EOL after the marker. */ pzScan = strchr(pzScan, NL); if (pzScan == NULL) AG_ABEND(aprf(zErrMsg, pzProg, endless, pCurCtx->pzCtxFname, pCurCtx->lineNo)); /* * And skip the first new line + conditionally skip tabs */ here_string_line_no = pCurCtx->lineNo++; pzScan++; if (trimTabs) while (*pzScan == TAB) ++pzScan; /* * FOR as long as the text does not match the mark * OR it matches but is a substring * DO copy characters */ while ( (strncmp(pzScan, zMark, markLen) != 0) || IS_VARIABLE_NAME_CHAR(pzScan[markLen]) ) { for (;;) { switch (*(pzDest++) = *(pzScan++)) { case NL: pCurCtx->lineNo++; goto lineDone; case NUL: AG_ABEND(aprf(zErrMsg, pzProg, endless, pCurCtx->pzCtxFname, here_string_line_no)); } } lineDone:; if (trimTabs) while (*pzScan == TAB) ++pzScan; } /* while strncmp ... */ /* * pzDest may still equal pz_token, if no data were copied */ if (pzDest > (char*)pz_token) pzDest[-1] = NUL; else pzDest[0] = NUL; return pzScan + markLen; }
/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Load the macro array and file name. */ static void load_macs(tTemplate * pT, char const * pzF, char const * pzN, char const * pzData) { tMacro* pMac = pT->aMacros; { char* pzText = (char*)(pMac + pT->macroCt); size_t len; AGDUPSTR(pT->pzTplFile, pzF, "templ file"); len = strlen(pzN) + 1; memcpy((void*)pzText, (void*)pzN, len); pT->pzTplName = pzText; pzText += len; pT->pzTemplText = pzText; pT->pNext = pzText + 1; } pCurTemplate = pT; { tMacro* pMacEnd = parseTemplate(pMac, &pzData); int ct; /* * Make sure all of the input string was scanned. */ if (pzData != NULL) AG_ABEND("Template parse ended unexpectedly"); ct = pMacEnd - pMac; /* * IF there are empty macro slots, * THEN pack the text */ if (ct < pT->macroCt) { int delta = sizeof(tMacro) * (pT->macroCt - ct); void* data = (pT->pzTplName == NULL) ? pT->pzTemplText : pT->pzTplName; size_t size = pT->pNext - (char*)data; memmove((void*)pMacEnd, data, size); pT->pzTemplText -= delta; pT->pNext -= delta; pT->pzTplName -= delta; pT->macroCt = ct; } } pT->descSize = pT->pNext - (char*)pT; pT->pNext = 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(pfTrace, zSum, pT->macroCt, pzF, pT->descSize); } #endif }
/** * Invokes @code{$SHELL} or @file{/bin/sh} on a script that should * generate AutoGen definitions. It does this using the same server * process that handles the back-quoted @code{`} text. * The block of text handed to the shell is terminated with * the #endshell directive. * * @strong{CAUTION}@: let not your @code{$SHELL} be @code{csh}. */ char * doDir_shell(directive_enum_t id, char const * arg, char * scan_next) { static size_t const endshell_len = sizeof("\n#endshell") - 1; scan_ctx_t * pCtx; char * pzText = scan_next; (void)arg; (void)id; /* * The output time will always be the current time. * The dynamic content is always current :) */ maxfile_time = outfile_time = time(NULL); /* * IF there are no data after the '#shell' directive, * THEN we won't write any data * ELSE we have to find the end of the data. */ if (strncmp(pzText, DIRECT_SHELL_END_SHELL+1, endshell_len-1) == 0) return scan_next; { char * pz = strstr(scan_next, DIRECT_SHELL_END_SHELL); if (pz == NULL) AG_ABEND(aprf(DIRECT_SHELL_NOEND, cctx->scx_fname, cctx->scx_line)); while (scan_next < pz) { if (*(scan_next++) == NL) cctx->scx_line++; } *scan_next = NUL; } /* * Advance the scan pointer to the next line after '#endshell' * IF there is no such line, * THEN the scan will resume on a zero-length string. */ scan_next = strchr(scan_next + endshell_len, NL); if (scan_next == NULL) scan_next = VOIDP(zNil); /* * Save the scan pointer into the current context */ cctx->scx_scan = scan_next; /* * Run the shell command. The output text becomes the * "file text" that is used for more definitions. */ pzText = shell_cmd(pzText); if (pzText == NULL) return scan_next; if (*pzText == NUL) { AGFREE(pzText); return scan_next; } /* * Get the space for the output data and for context overhead. * This is an extra allocation and copy, but easier than rewriting * 'loadData()' for this special context. */ pCtx = (scan_ctx_t *)AGALOC(sizeof(scan_ctx_t) + strlen(pzText) + 4, "shell output"); /* * Link the new scan data into the context stack */ pCtx->scx_next = cctx; cctx = pCtx; /* * Set up the rest of the context structure */ AGDUPSTR(pCtx->scx_fname, DIRECT_SHELL_COMP_DEFS, DIRECT_SHELL_COMP_DEFS); pCtx->scx_scan = pCtx->scx_data = (char *)(pCtx + 1); pCtx->scx_line = 0; strcpy(pCtx->scx_scan, pzText); AGFREE(pzText); return pCtx->scx_scan; }
/* * skipToElseEnd * * Skip through the text to a matching "#endif" or "#else" or * "#elif*def". We do this when we are skipping code due to a failed * "#if*def" test. */ static char* skipToElseEnd(char* pzStart) { char* pzScan = pzStart; char* pzRet; for (;;) { /* * 'pzScan' is pointing to the first character on a line. * Check for a directive on the current line before scanning * later lines. */ if (*pzScan == '#') pzScan++; else { char* pz = strstr(pzScan, zCheckList); if (pz == NULL) AG_ABEND(aprf(zNoEndif, pCurCtx->pzCtxFname, pCurCtx->lineNo)); pzScan = pz + STRSIZE(zCheckList); } while (IS_WHITESPACE_CHAR(*pzScan)) pzScan++; switch (findDirective(pzScan)) { case DIR_ELSE: /* * We found an "else" directive for an "ifdef"/"ifndef" * that we were skipping over. Start processing the text. */ ifdefLevel++; /* FALLTHROUGH */ case DIR_ENDIF: { /* * We reached the end of the "ifdef"/"ifndef" we were * skipping (or we dropped in from above). * Start processing the text. */ char* pz = strchr(pzScan, NL); if (pz != NULL) pzRet = pz+1; else pzRet = pzScan + strlen(pzScan); goto leave_func; } case DIR_IFDEF: case DIR_IFNDEF: /* * We have found a nested "ifdef"/"ifndef". * Call "skipToEndif()" to find *its* end, then * resume looking for our own "endif" or "else". */ pzScan = skipToEndif(pzScan); break; default: /* * We either don't know what it is or we do not care. */ break; } /* switch (findDirective(pzScan)) */ } leave_func: while (pzStart < pzRet) { if (*(pzStart++) == NL) pCurCtx->lineNo++; } return pzRet; }
/*=directive shell * * text: * Invokes @code{$SHELL} or @file{/bin/sh} on a script that should * generate AutoGen definitions. It does this using the same server * process that handles the back-quoted @code{`} text. * @strong{CAUTION}@: let not your @code{$SHELL} be @code{csh}. =*/ static char* doDir_shell(char* pzArg, char* pzScan) { static char const zShellText[] = "Computed Definitions"; static char const zEndShell[] = "\n#endshell"; tScanCtx* pCtx; char* pzText = pzScan; /* * The output time will always be the current time. * The dynamic content is always current :) */ outTime = time(NULL); /* * IF there are no data after the '#shell' directive, * THEN we won't write any data * ELSE we have to find the end of the data. */ if (strncmp(pzText, zEndShell+1, STRSIZE(zEndShell)-1) == 0) return pzScan; { static char const noend[] = "Missing #endshell after '#shell' in %s on line %d\n"; char* pz = strstr(pzScan, zEndShell); if (pz == NULL) AG_ABEND(aprf(noend, pCurCtx->pzCtxFname, pCurCtx->lineNo)); while (pzScan < pz) { if (*(pzScan++) == NL) pCurCtx->lineNo++; } *pzScan = NUL; } /* * Advance the scan pointer to the next line after '#endshell' * IF there is no such line, * THEN the scan will resume on a zero-length string. */ pzScan = strchr(pzScan + STRSIZE(zEndShell), NL); if (pzScan == NULL) pzScan = (void*)zNil; /* * Save the scan pointer into the current context */ pCurCtx->pzScan = pzScan; /* * Run the shell command. The output text becomes the * "file text" that is used for more definitions. */ pzText = runShell(pzText); if (pzText == NULL) return pzScan; if (*pzText == NUL) { AGFREE(pzText); return pzScan; } /* * Get the space for the output data and for context overhead. * This is an extra allocation and copy, but easier than rewriting * 'loadData()' for this special context. */ pCtx = (tScanCtx*)AGALOC(sizeof(tScanCtx) + strlen(pzText) + 4, "shell output"); /* * Link the new scan data into the context stack */ pCtx->pCtx = pCurCtx; pCurCtx = pCtx; /* * Set up the rest of the context structure */ AGDUPSTR(pCtx->pzCtxFname, zShellText, "shell text"); pCtx->pzScan = pCtx->pzData = (char*)(pCtx+1); pCtx->lineNo = 0; strcpy(pCtx->pzScan, pzText); AGFREE(pzText); return pCtx->pzScan; }
/** * Suck in the entire definitions file and parse it. */ LOCAL void readDefines(void) { char const * pzDefFile; char * pzData; size_t dataSize; size_t sizeLeft; FILE * fp; def_input_mode_t in_mode = ready_input(&pzDefFile, &dataSize); if (in_mode == INPUT_DONE) return; /* * Allocate the space we need for our definitions. */ sizeLeft = dataSize+4+sizeof(*pBaseCtx); pBaseCtx = (tScanCtx*)AGALOC(sizeLeft, "file buffer"); memset((void*)pBaseCtx, 0, sizeLeft); pBaseCtx->lineNo = 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 = pBaseCtx->pzScan = pBaseCtx->pzData = (char*)(pBaseCtx+1); pBaseCtx->pCtx = 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("open", pzDefFile); if (pfDepends != NULL) append_source_name(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", pzDefFile); } /* * Advance input pointer, decrease remaining count */ pzData += rdct; sizeLeft -= rdct; /* * See if there is any space left */ if (sizeLeft == 0) { tScanCtx* 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 - pBaseCtx->pzData; p = AGREALOC((void*)pBaseCtx, dataSize+4+sizeof(*pBaseCtx), "expanded file buffer"); /* * 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 != pBaseCtx) { p->pzScan = \ p->pzData = (char*)(p+1); pzData = p->pzData + dataOff; pBaseCtx = p; } } } if (pzData == pBaseCtx->pzData) AG_ABEND("No definition data were read"); *pzData = NUL; AGDUPSTR(pBaseCtx->pzCtxFname, pzDefFile, "def file name"); manageAllocatedData(pBaseCtx); manageAllocatedData((void*)pBaseCtx->pzCtxFname); /* * Close the input file, parse the data * and alphabetically sort the definition tree contents. */ if (in_mode != INPUT_STDIN) fclose(fp); pCurCtx = pBaseCtx; dp_run_fsm(); }
/** * Load a file into memory. Keep it in memory and try to reuse it * if we get called again. Likely, there will be several extractions * from a single file. */ static char const* load_extract_file(char const* pzNewFile) { static char const * pzFile = NULL; static char const * pzText = NULL; struct stat sbuf; char* pzIn; /* * Make sure that we: * * o got the file name from the SCM value * o return the old text if we are searching the same file * o have a regular file with some data * o can allocate the space we need... * * If we don't know about the current file, we leave the data * from any previous file we may have loaded. * * DO *NOT* include this file in dependency output. The output may vary * based on its contents, but since it is always optional input, it cannot * be made to be required by make. */ if (pzNewFile == NULL) return NULL; if ( (pzFile != NULL) && (strcmp(pzFile, pzNewFile) == 0)) return pzText; if ( (stat(pzNewFile, &sbuf) != 0) || (! S_ISREG(sbuf.st_mode)) || (sbuf.st_size < 10) ) return NULL; if (pzFile != NULL) { AGFREE((void*)pzFile); AGFREE((void*)pzText); pzFile = pzText = NULL; } AGDUPSTR(pzFile, pzNewFile, "extract file"); pzIn = (char*)AGALOC(sbuf.st_size + 1, "Extract Text"); if (! HAVE_OPT(WRITABLE)) SET_OPT_WRITABLE; pzText = (char const*)pzIn; /* * Suck up the file. We must read it all. */ { struct stat stbf; FILE* fp = fopen(pzNewFile, "r"); if (fp == NULL) goto bad_return; if (fstat(fileno(fp), &stbf) != 0) { fclose(fp); goto bad_return; } if (outTime <= stbf.st_mtime) outTime = stbf.st_mtime + 1; do { size_t sz = fread(pzIn, (size_t)1, (size_t)sbuf.st_size, fp); if (sz == 0) { fprintf(stderr, LD_EXTRACT_BAD_READ, errno, strerror(errno), (int)sbuf.st_size, pzFile); AG_ABEND(LD_EXTRACT_READ_FAIL); } pzIn += sz; sbuf.st_size -= sz; } while (sbuf.st_size > 0); *pzIn = NUL; fclose(fp); if (pfDepends != NULL) add_source_file(pzNewFile); } return pzText; bad_return: AGFREE((void*)pzFile); pzFile = NULL; AGFREE((void*)pzText); pzText = NULL; return pzText; }
static ssize_t safePrintf(char** ppzBuf, char const * pzFmt, void** argV) { #if ! defined(DEBUG_ENABLED) /* * In normal operation (or during AutoGen testing), seg faults during the * printf operation are caused by bad input data. During AutoGen * development, we do not supply bad printf arguments, so we want to * capture any segfaults when they happen with the correct stack trace. * Therefore, during AutoGen development, we do not protect against seg * faults. */ struct sigaction saSave1; struct sigaction saSave2; { struct sigaction sa; sa.sa_handler = printFault; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(SIGBUS, &sa, &saSave1); sigaction(SIGSEGV, &sa, &saSave2); } { static char const zBadArgs[] = "Bad args to sprintf"; static char const zBadFmt[] = "%s ERROR: %s processing printf format:\n\t%s\n"; /* * IF the sprintfv call below is going to address fault, * THEN ... */ if (sigsetjmp(printJumpEnv, 0) != 0) { #ifndef HAVE_STRSIGNAL extern char* strsignal(int signo); #endif /* * IF the fprintf command in the then clause has not failed yet, * THEN perform that fprintf */ if (sigsetjmp(printJumpEnv, 0) == 0) fprintf(pfTrace, zBadFmt, pzProg, strsignal(printJumpSignal), pzFmt); /* * The "sprintfv" command below faulted, so we exit */ AG_ABEND(zBadArgs); } } #endif /* ! defined(DEBUG_ENABLED) */ { int printSize = asprintfv(ppzBuf, pzFmt, (snv_constpointer*)argV); if ((printSize & ~0xFFFFFU) != 0) /* 1MB max */ AG_ABEND(aprf("asprintfv returned 0x%08X\n", printSize)); #if ! defined(DEBUG_ENABLED) sigaction(SIGBUS, &saSave1, NULL); sigaction(SIGSEGV, &saSave2, NULL); #endif return printSize; } }
/** * Figure out what to use as the base name of the output file. * If an argument is not provided, we use the base name of * the definitions file. */ static void open_output(tOutSpec * spec) { static char const write_mode[] = "w" FOPEN_BINARY_FLAG "+"; char const * out_file = NULL; if (strcmp(spec->zSuffix, OPEN_OUTPUT_NULL) == 0) { static int const flags = FPF_NOUNLINK | FPF_NOCHMOD | FPF_TEMPFILE; null_open: open_output_file(DEV_NULL, DEV_NULL_LEN, write_mode, flags); return; } /* * IF we are to skip the current suffix, * we will redirect the output to /dev/null and * perform all the work. There may be side effects. */ if (HAVE_OPT(SKIP_SUFFIX)) { int ct = STACKCT_OPT(SKIP_SUFFIX); const char ** ppz = STACKLST_OPT(SKIP_SUFFIX); while (--ct >= 0) { if (strcmp(spec->zSuffix, *ppz++) == 0) goto null_open; } } /* * Remove any suffixes in the last file name */ { char const * def_file = OPT_ARG(BASE_NAME); char z[AG_PATH_MAX]; const char * pst = strrchr(def_file, '/'); char * end; pst = (pst == NULL) ? def_file : (pst + 1); /* * We allow users to specify a suffix with '-' and '_', but when * stripping a suffix from the "base name", we do not recognize 'em. */ end = strchr(pst, '.'); if (end != NULL) { size_t len = (unsigned)(end - pst); if (len >= sizeof(z)) AG_ABEND("--base-name name is too long"); memcpy(z, pst, len); z[ end - pst ] = NUL; pst = z; } /* * Now formulate the output file name in the buffer * provided as the input argument. */ out_file = aprf(spec->pzFileFmt, pst, spec->zSuffix); if (out_file == NULL) AG_ABEND(aprf(OPEN_OUTPUT_BAD_FMT, spec->pzFileFmt, pst, spec->zSuffix)); } open_output_file(out_file, strlen(out_file), write_mode, 0); free((void *)out_file); }
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * LEXICAL SCANNER */ LOCAL te_dp_event yylex(void) { lastToken = DP_EV_INVALID; scanAgain: /* * Start the process of locating a token. * We branch here after skipping over a comment * or processing a directive (which may change our context). */ if (IS_WHITESPACE_CHAR(*pCurCtx->pzScan)) trim_whitespace(); switch (*pCurCtx->pzScan) { case NUL: /* * IF we are not inside an include context, * THEN go finish. */ if (pCurCtx->pCtx == NULL) goto lex_done; pop_context(); goto scanAgain; case '#': { extern char * processDirective(char*); char * pz = processDirective(pCurCtx->pzScan+1); /* * Ensure that the compiler doesn't try to save a copy of * "pCurCtx" in a register. It must be reloaded from memory. */ pCurCtx->pzScan = pz; goto scanAgain; } case '{': SET_LIT_TKN(O_BRACE); break; case '=': SET_LIT_TKN(EQ); break; case '}': SET_LIT_TKN(C_BRACE); break; case '[': SET_LIT_TKN(OPEN_BKT); break; case ']': SET_LIT_TKN(CLOSE_BKT); break; case ';': SET_LIT_TKN(SEMI); break; case ',': SET_LIT_TKN(COMMA); break; case '\'': case '"': { char* pz = ao_string_cook(pCurCtx->pzScan, &(pCurCtx->lineNo)); if (pz == NULL) goto NUL_error; pz_token = pCurCtx->pzScan; lastToken = DP_EV_STRING; pCurCtx->pzScan = pz; break; } case '<': switch (lex_here_string()) { case SUCCESS: break; case FAILURE: goto BrokenToken; case PROBLEM: return DP_EV_INVALID; } break; case '(': loadScheme(); break; case '\\': if (strncmp(pCurCtx->pzScan+1, "'(", (size_t)2) == 0) { alist_to_autogen_def(); goto scanAgain; } lex_escaped_char(); break; case '`': switch (lex_backquote()) { case FAILURE: goto NUL_error; case PROBLEM: goto scanAgain; case SUCCESS: break; } break; case '/': switch (lex_comment()) { case SUCCESS: goto scanAgain; default: break; } /* FALLTHROUGH */ /* to Invalid input char */ default: BrokenToken: pCurCtx->pzScan = assembleName(pCurCtx->pzScan, &lastToken); break; } /* switch (*pCurCtx->pzScan) */ return lastToken; NUL_error: AG_ABEND(aprf(zErrMsg, pzProg, "unterminated quote in definition", pCurCtx->pzCtxFname, pCurCtx->lineNo)); return DP_EV_INVALID; lex_done: /* * First time through, return the DP_EV_END token. * Second time through, we really finish. */ if (pCurCtx->pzScan == zNil) { pCurCtx->pCtx = pDoneCtx; pDoneCtx = pCurCtx; return DP_EV_INVALID; } pCurCtx->pzScan = (char*)zNil; return DP_EV_END; }
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * It may be a number, a name, a keyword or garbage. * Figure out which. */ static char* assembleName(char * pzScan, te_dp_event * pRetVal) { /* * Check for a number. * Scan it in and advance "pzScan". */ if ( IS_DEC_DIGIT_CHAR(*pzScan) || ( (*pzScan == '-') && IS_DEC_DIGIT_CHAR(pzScan[1]) ) ) { pz_token = pzScan; (void)strtol(pzScan, &pzScan, 0); *pRetVal = DP_EV_NUMBER; return pzScan; } if (! IS_UNQUOTABLE_CHAR(*pzScan)) AG_ABEND(aprf("%s Error: Invalid input char '%c' in %s on line %d\n", pzProg, *pzScan, pCurCtx->pzCtxFname, pCurCtx->lineNo)); { unsigned char* pz = (unsigned char*)pzScan; while (IS_VALUE_NAME_CHAR(*pz)) pz++; if (IS_UNQUOTABLE_CHAR(*pz)) { *pRetVal = DP_EV_OTHER_NAME; while (IS_UNQUOTABLE_CHAR(*++pz)) ; } else *pRetVal = DP_EV_VAR_NAME; /* * Return a NAME token, maybe. * If the name is actually a keyword, * we will return that token code instead. */ pz_token = pzScan; pzScan = (char*)pz; } /* * Now scan the keyword table. */ if (*pRetVal == DP_EV_VAR_NAME) { char sv_ch = *pzScan; /* preserve the following character */ int kw_ix = 0; *pzScan = NUL; /* NUL terminate the name */ do { if (streqvcmp(apzKeywords[ kw_ix ], (char*)pz_token) == 0) { /* * Return the keyword token code instead of DP_EV_NAME */ *pRetVal = aKeywordTkn[ kw_ix ]; break; } } while (++kw_ix < KEYWORD_CT); *pzScan = sv_ch; /* restore the following character */ } return pzScan; }
/* * process a single scheme expression, yielding text that gets processed * into AutoGen definitions. */ static void alist_to_autogen_def(void) { static char const zSchemeText[] = "Scheme Computed Definitions"; static char const zWrap[] = "(alist->autogen-def %s)"; char* pzText = ++(pCurCtx->pzScan); char* pzEnd = (char*)skipScheme(pzText, pzText + strlen(pzText)); SCM res; size_t res_len; tScanCtx* pCtx; /* * Wrap the scheme expression with the `alist->autogen-def' function */ { char endCh = *pzEnd; *pzEnd = NUL; pzText = aprf(zWrap, pzText); *pzEnd = endCh; } /* * Run the scheme expression. The result is autogen definition text. */ procState = PROC_STATE_GUILE_PRELOAD; res = ag_scm_c_eval_string_from_file_line( pzText, pCurCtx->pzCtxFname, pCurCtx->lineNo ); /* * The result *must* be a string, or we choke. */ if (! AG_SCM_STRING_P(res)) { static char const zEr[] = "Scheme definition expression does not yield string:\n"; AG_ABEND(zEr); } res_len = AG_SCM_STRLEN(res); procState = PROC_STATE_LOAD_DEFS; pCurCtx->pzScan = pzEnd; AGFREE(pzText); /* * Now, push the resulting string onto the input stack * and link the new scan data into the context stack */ pCtx = (tScanCtx*)AGALOC(sizeof(tScanCtx) + 4 + res_len, "lex scan ctx"); pCtx->pCtx = pCurCtx; pCurCtx = pCtx; /* * Set up the rest of the context structure */ AGDUPSTR(pCtx->pzCtxFname, zSchemeText, "scheme text"); pCtx->pzScan = \ pCtx->pzData = (char*)(pCtx+1); pCtx->lineNo = 0; memcpy((void*)(pCtx->pzScan), (void*)AG_SCM_CHARS(res), res_len); pCtx->pzScan[ res_len ] = NUL; /* * At this point, the next token will be obtained * from the newly allocated context structure. * When empty, input will resume from the '}' that we * left as the next input token in the old context. */ }