summaryrefslogtreecommitdiff
path: root/agen5/defDirect.c
diff options
context:
space:
mode:
Diffstat (limited to 'agen5/defDirect.c')
-rw-r--r--agen5/defDirect.c1048
1 files changed, 1048 insertions, 0 deletions
diff --git a/agen5/defDirect.c b/agen5/defDirect.c
new file mode 100644
index 0000000..5da38e1
--- /dev/null
+++ b/agen5/defDirect.c
@@ -0,0 +1,1048 @@
+/**
+ * @file defDirect.c
+ *
+ * Time-stamp: "2012-03-31 13:59:07 bkorb"
+ *
+ * This module processes definition file directives.
+ *
+ * blocksort spacing=2 \
+ * output=defDirect-sorted.c \
+ * input=defDirect.c \
+ * pat='^/\*=directive' \
+ * start='^doDir_IGNORE' \
+ * trail='\+\+\+ End of Directives'
+ *
+ * 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/>.
+ */
+#define NO_MATCH_ERR(_typ) \
+AG_ABEND(aprf(DIRECT_NOMATCH_FMT, cctx->scx_fname, cctx->scx_line, _typ))
+
+static int ifdefLevel = 0;
+
+static teDirectives
+findDirective(char* pzDirName);
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * processDirective
+ *
+ * THIS IS THE ONLY EXTERNAL ENTRY POINT
+ *
+ * A directive character has been found.
+ * Decide what to do and return a pointer to the character
+ * where scanning is to resume.
+ */
+LOCAL char*
+processDirective(char* pzScan)
+{
+ const tDirTable * pTbl = dirTable;
+ char * pzDir;
+ char * pzEnd;
+
+ /*
+ * Search for the end of the #-directive.
+ * Replace "\\\n" sequences with " ".
+ */
+ for (;;) {
+ pzEnd = strchr(pzScan, NL);
+
+ if (pzEnd == NULL) {
+ /*
+ * The end of the directive is the end of the string
+ */
+ pzEnd = pzScan + strlen(pzScan);
+ break;
+ }
+ cctx->scx_line++;
+
+ if (pzEnd[-1] != '\\') {
+ /*
+ * The end of the directive is the end of the line
+ * and the line has not been continued.
+ */
+ *(pzEnd++) = NUL;
+ break;
+ }
+
+ /*
+ * Replace the escape-newline pair with spaces and
+ * find the next end of line
+ */
+ pzEnd[-1] = pzEnd[0] = ' ';
+ }
+
+ /*
+ * Ignore ``#!'' as a comment, enabling a definition file to behave
+ * as a script that gets interpreted by autogen. :-)
+ */
+ if (*pzScan == '!')
+ return pzEnd;
+
+ /*
+ * Find the start of the directive name. Ensure it _is_ a name.
+ */
+ pzScan = SPN_WHITESPACE_CHARS(pzScan);
+ if (! IS_ALPHABETIC_CHAR(*pzScan))
+ return pzEnd;
+
+ /*
+ * Find the *END* of the directive name.
+ */
+ pzDir = pzScan;
+ pzScan = SPN_ALPHABETIC_CHARS(pzScan+1);
+
+ /*
+ * IF there is anything that follows the name, ...
+ */
+ if (*pzScan != NUL) {
+ char * pz;
+
+ /*
+ * IF something funny immediately follows the directive name,
+ * THEN we will ignore it completely.
+ */
+ if (! IS_WHITESPACE_CHAR(*pzScan))
+ return pzEnd;
+
+ /*
+ * Terminate the name being defined
+ * and find the start of anything else.
+ */
+ *pzScan = NUL;
+ pzScan = SPN_WHITESPACE_CHARS(pzScan+1);
+
+
+ /*
+ * Trim off trailing white space
+ */
+ pz = pzScan + strlen(pzScan);
+ while ( (pz > pzScan)
+ && IS_WHITESPACE_CHAR(pz[-1])) pz--;
+ *pz = NUL;
+ }
+
+ pTbl = dirTable + (int)findDirective(pzDir);
+ return (*(pTbl->pDirProc))(pzScan, pzEnd);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Figure out the index of a directive. We will return the directive
+ * count if it is a bogus name.
+ */
+
+static teDirectives
+findDirective(char* pzDirName)
+{
+ teDirectives res = (teDirectives)0;
+ const tDirTable * pTbl = dirTable;
+
+ do {
+ if (strneqvcmp(pzDirName, pTbl->pzDirName, pTbl->nameSize) != 0)
+ continue;
+
+ /*
+ * A directive name ends with either white space or a NUL
+ */
+ if (IS_END_TOKEN_CHAR(pzDirName[ pTbl->nameSize ]))
+ return res;
+
+ } while (pTbl++, ++res < DIRECTIVE_CT);
+
+ {
+ char ch;
+ if (strlen(pzDirName) > 32) {
+ ch = pzDirName[32];
+ pzDirName[32] = NUL;
+ } else {
+ ch = NUL;
+ }
+
+ fprintf(trace_fp, FIND_DIRECT_UNKNOWN, cctx->scx_fname,
+ cctx->scx_line, pzDirName);
+
+ if (ch != NUL)
+ pzDirName[32] = ch;
+ }
+ return res;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * 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, DIRECT_CK_LIST_MARK);
+ if (pz == NULL)
+ AG_ABEND(aprf(DIRECT_NOENDIF_FMT, cctx->scx_fname,
+ cctx->scx_line));
+
+ pzScan = pz + 2;
+ }
+
+ pzScan = SPN_WHITESPACE_CHARS(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)
+ cctx->scx_line++;
+ }
+ return pzRet;
+}
+
+
+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, DIRECT_CK_LIST_MARK);
+ if (pz == NULL)
+ AG_ABEND(aprf(DIRECT_NOENDIF_FMT, cctx->scx_fname,
+ cctx->scx_line));
+
+ pzScan = pz + 2;
+ }
+
+ pzScan = SPN_WHITESPACE_CHARS(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)
+ cctx->scx_line++;
+ }
+ return pzRet;
+}
+
+
+/*
+ * 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, DIRECT_CK_LIST_MARK);
+ if (pz == NULL)
+ AG_ABEND(aprf(DIRECT_NOENDIF_FMT, cctx->scx_fname,
+ cctx->scx_line));
+
+ pzScan = pz + 2;
+ }
+
+ pzScan = SPN_WHITESPACE_CHARS(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)
+ cctx->scx_line++;
+ }
+ return pzRet;
+}
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Special routines for each directive. These routines are *ONLY*
+ * called from the table when the input is being processed.
+ * After this routine are either declarations or definitions of
+ * directive handling routines. The documentation for these routines
+ * is extracted from this file. See 'makedef.sh' for how it works.
+ * Each declared directive should have either a 'dummy:' section
+ * (if the directive is to be ignored) or a 'text:' section
+ * (if there is some form of implementation). If the directive
+ * needs or may take arguments (e.g. '#define'), then there should
+ * also be an 'arg:' section describing the argument(s).
+ */
+static char *
+doDir_IGNORE(char * arg, char * scan)
+{
+ (void)arg;
+ return scan;
+}
+
+
+/*=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)
+{
+ pz = SPN_WHITESPACE_CHARS(pz);
+
+ if (IS_FALSE_TYPE_CHAR(*pz))
+ AG_ABEND(aprf(DIRECT_ASSERT_FMT, pz, pzArg));
+}
+
+static char*
+doDir_assert(char* pzArg, char* pzScan)
+{
+ switch (*pzArg) {
+ case '`':
+ {
+ char* pzS = pzArg+1;
+ char* pzR;
+
+ pzR = strrchr(pzS, '`');
+ if (pzR == NULL)
+ break; /* not a valid script */
+
+ *pzR = NUL;
+ pzS = shell_cmd((char const*)pzS);
+ check_assert_str(pzS, pzArg);
+ free(pzS);
+ break;
+ }
+
+ case '(':
+ {
+ SCM res = ag_scm_c_eval_string_from_file_line(
+ pzArg, cctx->scx_fname, cctx->scx_line );
+
+ check_assert_str(scm2display(res), pzArg);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return pzScan;
+}
+
+
+/*=directive define
+ *
+ * arg: name [ <text> ]
+ *
+ * text:
+ * Will add the name to the define list as if it were a DEFINE program
+ * argument. Its value will be the first non-whitespace token following
+ * the name. Quotes are @strong{not} processed.
+ *
+ * After the definitions file has been processed, any remaining entries
+ * in the define list will be added to the environment.
+=*/
+static char*
+doDir_define(char* pzArg, char* pzScan)
+{
+ char* pzName = pzArg;
+
+ /*
+ * Skip any #defines that do not look reasonable
+ */
+ if (! IS_VAR_FIRST_CHAR(*pzArg))
+ return pzScan;
+
+ pzArg = SPN_VARIABLE_NAME_CHARS(pzArg);
+
+ /*
+ * IF this is a macro definition (rather than a value def),
+ * THEN we will ignore it.
+ */
+ if (*pzArg == '(')
+ return pzScan;
+
+ /*
+ * We have found the end of the name.
+ * IF there is no more data on the line,
+ * THEN we do not have space for the '=' required by PUTENV.
+ * Therefore, move the name back over the "#define"
+ * directive itself, giving us the space needed.
+ */
+ if (! IS_WHITESPACE_CHAR(*pzArg)) {
+ char* pzS = pzName;
+ char* pzD = (pzName -= 6);
+
+ *pzArg = NUL;
+ while ((*(pzD++) = *(pzS++)) != NUL) ;
+ pzD[-1] = '=';
+ pzD[ 0] = NUL;
+
+ } else {
+ /*
+ * Otherwise, insert the '=' and move any data up against it.
+ * We only accept one name-type, space separated token.
+ * We are not ANSI-C. ;-)
+ */
+ char* pz = pzArg+1;
+ *pzArg++ = '=';
+ pz = SPN_WHITESPACE_CHARS(pz);
+
+ for (;;) {
+ if ((*pzArg++ = *pz++) == NUL)
+ break;
+ if (! IS_UNQUOTABLE_CHAR(*pz)) {
+ *pzArg = NUL;
+ break;
+ }
+ }
+ }
+
+ SET_OPT_DEFINE(pzName);
+ return pzScan;
+}
+
+
+/*=directive elif
+ *
+ * text:
+ * This must follow an @code{#if}
+ * otherwise it will generate an error.
+ * It will be ignored.
+=*/
+static char *
+doDir_elif(char * arg, char * scan)
+{
+ (void)arg;
+ (void)scan;
+ AG_ABEND(aprf(DIRECT_ELIF_BAD_FMT, cctx->scx_fname, cctx->scx_line));
+ /* NOTREACHED */
+ return NULL;
+}
+
+
+/*=directive else
+ *
+ * text:
+ * This must follow an @code{#if}, @code{#ifdef} or @code{#ifndef}.
+ * If it follows the @code{#if}, then it will be ignored. Otherwise,
+ * it will change the processing state to the reverse of what it was.
+=*/
+static char *
+doDir_else(char * arg, char * scan)
+{
+ (void)arg;
+ if (--ifdefLevel < 0)
+ NO_MATCH_ERR(DIRECT_ELSE_BAD);
+
+ return skipToEndif(scan);
+}
+
+
+/*=directive endif
+ *
+ * text:
+ * This must follow an @code{#if}, @code{#ifdef} or @code{#ifndef}.
+ * In all cases, this will resume normal processing of text.
+=*/
+static char *
+doDir_endif(char * arg, char * scan)
+{
+ (void)arg;
+ if (--ifdefLevel < 0)
+ NO_MATCH_ERR(DIRECT_ENDIF_BAD);
+
+ return scan;
+}
+
+
+/*=directive endmac
+ *
+ * text:
+ * This terminates a "macdef", but must not ever be encountered directly.
+=*/
+static char *
+doDir_endmac(char * arg, char * scan)
+{
+ NO_MATCH_ERR(DIRECT_ENDMAC_BAD);
+ (void)arg;
+ (void)scan;
+ /* NOTREACHED */
+ return NULL;
+}
+
+
+/*=directive endshell
+ *
+ * text:
+ * Ends the text processed by a command shell into autogen definitions.
+=*/
+static char *
+doDir_endshell(char * arg, char * scan)
+{
+ /*
+ * In actual practice, the '#endshell's must be consumed inside
+ * the 'doDir_shell()' procedure.
+ */
+ NO_MATCH_ERR(DIRECT_ENDSHELL_BAD);
+ (void)arg;
+ (void)scan;
+ /* NOTREACHED */
+ return NULL;
+}
+
+
+/*=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 * arg, char * scan)
+{
+ (void)scan;
+
+ AG_ABEND(aprf(DIRECT_ERROR_FMT, cctx->scx_fname, cctx->scx_line, arg));
+ /* NOTREACHED */
+ return NULL;
+}
+
+
+/*=directive ident
+ *
+ * dummy: Ident directives are ignored.
+=*/
+
+
+/*=directive if
+ *
+ * arg: [ <ignored conditional expression> ]
+ *
+ * text:
+ * @code{#if} expressions are not analyzed. @strong{Everything} from here
+ * to the matching @code{#endif} is skipped.
+=*/
+static char *
+doDir_if(char * arg, char * pzScan)
+{
+ (void)arg;
+
+ return skipToEndif(pzScan);
+}
+
+
+/*=directive ifdef
+ *
+ * arg: name-to-test
+ *
+ * text:
+ * The definitions that follow, up to the matching @code{#endif} will be
+ * processed only if there is a corresponding @code{-Dname} command line
+ * option or if a @code{#define} of that name has been previously encountered.
+=*/
+static char *
+doDir_ifdef(char * pzArg, char * pzScan)
+{
+ if (get_define_str(pzArg, false) == NULL)
+ return skipToElseEnd(pzScan);
+ ifdefLevel++;
+ return pzScan;
+}
+
+
+/*=directive ifndef
+ *
+ * arg: name-to-test
+ *
+ * text:
+ * The definitions that follow, up to the matching @code{#endif} will be
+ * processed only if there is @strong{not} a corresponding @code{-Dname}
+ * command line option or there was a canceling @code{-Uname} option.
+=*/
+static char *
+doDir_ifndef(char * pzArg, char * pzScan)
+{
+ if (get_define_str(pzArg, false) != NULL)
+ return skipToElseEnd(pzScan);
+ ifdefLevel++;
+ return pzScan;
+}
+
+
+/*=directive include
+ *
+ * arg: unadorned-file-name
+ *
+ * text:
+ * This directive will insert definitions from another file into
+ * the current collection. If the file name is adorned with
+ * double quotes or angle brackets (as in a C program), then the
+ * include is ignored.
+=*/
+static char*
+doDir_include(char* pzArg, char* pzScan)
+{
+ static char const * const apzSfx[] = { DIRECT_INC_DEF_SFX, NULL };
+ scan_ctx_t* pCtx;
+ size_t inclSize;
+ char zFullName[ AG_PATH_MAX + 1 ];
+
+ /*
+ * Ignore C-style includes. This allows "C" files to be processed
+ * for their "#define"s.
+ */
+ if ((*pzArg == '"') || (*pzArg == '<'))
+ return pzScan;
+ cctx->scx_scan = pzScan;
+
+ if (! SUCCESSFUL(
+ find_file(pzArg, zFullName, apzSfx, cctx->scx_fname))) {
+ fprintf(trace_fp, DIRECT_INC_CANNOT_FIND,
+ pzArg);
+ return pzScan;
+ }
+
+ /*
+ * Make sure the specified file is a regular file and we can get
+ * the correct size for it.
+ */
+ {
+ struct stat stbf;
+ if (stat(zFullName, &stbf) != 0) {
+ fprintf(trace_fp, DIRECT_INC_CANNOT_STAT, errno, strerror(errno),
+ zFullName);
+ return pzScan;
+ }
+ if (! S_ISREG(stbf.st_mode)) {
+ fprintf(trace_fp, DIRECT_INC_NOT_REG, zFullName);
+ return pzScan;
+ }
+ inclSize = stbf.st_size;
+ if (outfile_time < stbf.st_mtime)
+ outfile_time = stbf.st_mtime;
+ }
+
+ if (inclSize == 0)
+ 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.
+ */
+ {
+ size_t sz = sizeof(scan_ctx_t) + 4 + inclSize;
+ pCtx = (scan_ctx_t*)AGALOC(sz, "inc def head");
+
+ memset((void*)pCtx, 0, sz);
+ pCtx->scx_line = 1;
+ }
+
+ /*
+ * Link it into the context stack
+ */
+ pCtx->scx_next = cctx;
+ cctx = pCtx;
+ AGDUPSTR(pCtx->scx_fname, zFullName, "def file");
+
+ pCtx->scx_scan =
+ pCtx->scx_data =
+ pzScan = (char*)(pCtx + 1);
+
+ /*
+ * Read all the data. Usually in a single read, but loop
+ * in case multiple passes are required.
+ */
+ {
+ FILE* fp = fopen(zFullName, "r" FOPEN_TEXT_FLAG);
+ char* pz = pzScan;
+
+ if (fp == NULL)
+ AG_CANT(DIRECT_INC_CANNOT_OPEN, zFullName);
+
+ if (dep_fp != NULL)
+ add_source_file(zFullName);
+
+ do {
+ size_t rdct = fread((void*)pz, (size_t)1, inclSize, fp);
+
+ if (rdct == 0)
+ AG_CANT(DIRECT_INC_CANNOT_READ, zFullName);
+
+ pz += rdct;
+ inclSize -= rdct;
+ } while (inclSize > 0);
+
+ fclose(fp);
+ *pz = NUL;
+ }
+
+ return pzScan;
+}
+
+
+/*=directive let
+ *
+ * dummy: let directives are ignored.
+=*/
+
+
+/*=directive line
+ *
+ * text:
+ *
+ * Alters the current line number and/or file name. You may wish to
+ * use this directive if you extract definition source from other files.
+ * @command{getdefs} uses this mechanism so AutoGen will report the correct
+ * file and approximate line number of any errors found in extracted
+ * definitions.
+=*/
+static char*
+doDir_line(char* pzArg, char* pzScan)
+{
+ /*
+ * The sequence must be: #line <number> "file-name-string"
+ *
+ * Start by scanning up to and extracting the line number.
+ */
+ pzArg = SPN_WHITESPACE_CHARS(pzArg);
+ if (! IS_DEC_DIGIT_CHAR(*pzArg))
+ return pzScan;
+
+ cctx->scx_line = strtol(pzArg, &pzArg, 0);
+
+ /*
+ * Now extract the quoted file name string.
+ * We dup the string so it won't disappear on us.
+ */
+ pzArg = SPN_WHITESPACE_CHARS(pzArg);
+ if (*(pzArg++) != '"')
+ return pzScan;
+ {
+ char* pz = strchr(pzArg, '"');
+ if (pz == NULL)
+ return pzScan;
+ *pz = NUL;
+ }
+
+ AGDUPSTR(cctx->scx_fname, pzArg, "#line");
+
+ return pzScan;
+}
+
+
+/*=directive macdef
+ *
+ * text:
+ * This is a new AT&T research preprocessing directive. Basically, it is
+ * a multi-line #define that may include other preprocessing directives.
+=*/
+static char*
+doDir_macdef(char* arg, char* pzScan)
+{
+ (void)arg;
+
+ return skipToEndmac(pzScan);
+}
+
+
+/*=directive option
+ *
+ * arg: opt-name [ <text> ]
+ *
+ * text:
+ *
+ * This directive will pass the option name and associated text to the
+ * AutoOpts optionLoadLine routine (@pxref{libopts-optionLoadLine}). The
+ * option text may span multiple lines by continuing them with a backslash.
+ * The backslash/newline pair will be replaced with two space characters.
+ * This directive may be used to set a search path for locating template files
+ * For example, this:
+ *
+ * @example
+ * #option templ-dirs $ENVVAR/dirname
+ * @end example
+ * @noindent
+ * will direct autogen to use the @code{ENVVAR} environment variable to find
+ * a directory named @code{dirname} that (may) contain templates. Since these
+ * directories are searched in most recently supplied first order, search
+ * directories supplied in this way will be searched before any supplied on
+ * the command line.
+=*/
+static char*
+doDir_option(char* pzArg, char* pzScan)
+{
+ optionLoadLine(&autogenOptions, pzArg);
+ return pzScan;
+}
+
+
+/*=directive pragma
+ *
+ * dummy: pragma directives are ignored.
+=*/
+
+
+/*=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 * arg, char * pzScan)
+{
+ static size_t const endshell_len = sizeof("\n#endshell") - 1;
+
+ scan_ctx_t * pCtx;
+ char * pzText = pzScan;
+
+ (void)arg;
+
+ /*
+ * The output time will always be the current time.
+ * The dynamic content is always current :)
+ */
+ 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 pzScan;
+
+ {
+ char* pz = strstr(pzScan, DIRECT_SHELL_END_SHELL);
+ if (pz == NULL)
+ AG_ABEND(aprf(DIRECT_SHELL_NOEND, cctx->scx_fname,
+ cctx->scx_line));
+
+ while (pzScan < pz) {
+ if (*(pzScan++) == NL) cctx->scx_line++;
+ }
+
+ *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 + endshell_len, NL);
+ if (pzScan == NULL)
+ pzScan = (void*)zNil;
+
+ /*
+ * Save the scan pointer into the current context
+ */
+ cctx->scx_scan = pzScan;
+
+ /*
+ * 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 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 = (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;
+}
+
+
+/*=directive undef
+ *
+ * arg: name-to-undefine
+ *
+ * text:
+ * Will remove any entries from the define list
+ * that match the undef name pattern.
+=*/
+static char*
+doDir_undef(char* pzArg, char* pzScan)
+{
+ SET_OPT_UNDEFINE(pzArg);
+ return pzScan;
+}
+
+
+/*+++ End of Directives +++*/
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/defDirect.c */