/**
* @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 .
*/
#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) |
*
* text:
* If the @code{shell-script} or @code{scheme-expr} do not yield @code{true}
* valued results, autogen will be aborted. If @code{} 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:
* 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: [ ]
*
* 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: [ ]
*
* 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 "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:
*
* 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 */