Esempio n. 1
0
/*=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.
     */
    while (IS_WHITESPACE_CHAR(*pzArg)) pzArg++;
    if (! IS_DEC_DIGIT_CHAR(*pzArg))
        return pzScan;

    pCurCtx->lineNo = strtol(pzArg, &pzArg, 0);

    /*
     *  Now extract the quoted file name string.
     *  We dup the string so it won't disappear on us.
     */
    while (IS_WHITESPACE_CHAR(*pzArg)) pzArg++;
    if (*(pzArg++) != '"')
        return pzScan;
    {
        char* pz = strchr(pzArg, '"');
        if (pz == NULL)
            return pzScan;
        *pz = NUL;
    }

    AGDUPSTR(pCurCtx->pzCtxFname, pzArg, "#line file name");

    return pzScan;
}
Esempio n. 2
0
/*=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;
    while (IS_VARIABLE_NAME_CHAR(*pzArg)) 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++ = '=';
        while (IS_WHITESPACE_CHAR(*pz)) pz++;

        for (;;) {
            if ((*pzArg++ = *pz++) == NUL)
                break;
            if (! IS_UNQUOTABLE_CHAR(*pz)) {
                *pzArg = NUL;
                break;
            }
        }
    }

    SET_OPT_DEFINE(pzName);
    return pzScan;
}
Esempio n. 3
0
/**
 *  Associate a name with a boolean value
 *
 * @param[in,out] pp        argument list to add to
 * @param[in]     name      the name of the "suboption"
 * @param[in]     nm_len    the length of the name
 * @param[in]     val       the boolean value for the suboption
 * @param[in]     d_len     the length of the value
 *
 * @returns the new value structure
 */
static tOptionValue *
add_bool(void ** pp, char const * name, size_t nm_len,
         char const * val, size_t d_len)
{
    size_t sz = nm_len + sizeof(tOptionValue) + 1;
    tOptionValue * new_val = AGALOC(sz, "bool val");

    /*
     * Scan over whitespace is constrained by "d_len"
     */
    while (IS_WHITESPACE_CHAR(*val) && (d_len > 0)) {
        d_len--; val++;
    }

    if (d_len == 0)
        new_val->v.boolVal = 0;

    else if (IS_DEC_DIGIT_CHAR(*val))
        new_val->v.boolVal = (unsigned)atoi(val);

    else new_val->v.boolVal = ! IS_FALSE_TYPE_CHAR(*val);

    new_val->valType = OPARG_TYPE_BOOLEAN;
    new_val->pzName = (char *)(new_val + 1);
    memcpy(new_val->pzName, name, nm_len);
    new_val->pzName[ nm_len ] = NUL;
    addArgListEntry(pp, new_val);
    return new_val;
}
Esempio n. 4
0
/*  addBoolValue
 *
 *  Associate a name with either a string or no value.
 */
static tOptionValue*
addBoolValue( void** pp, char const* pzName, size_t nameLen,
                char const* pzValue, size_t dataLen )
{
    tOptionValue* pNV;
    size_t sz = nameLen + sizeof(*pNV) + 1;

    pNV = AGALOC( sz, "option name/bool value pair" );
    if (pNV == NULL)
        return NULL;
    while (IS_WHITESPACE_CHAR(*pzValue) && (dataLen > 0)) {
        dataLen--; pzValue++;
    }
    if (dataLen == 0)
        pNV->v.boolVal = 0;

    else if (IS_DEC_DIGIT_CHAR(*pzValue))
        pNV->v.boolVal = atoi(pzValue);

    else pNV->v.boolVal = ! IS_FALSE_TYPE_CHAR(*pzValue);

    pNV->valType = OPARG_TYPE_BOOLEAN;
    pNV->pzName = (char*)(pNV + 1);
    memcpy( pNV->pzName, pzName, nameLen );
    pNV->pzName[ nameLen ] = NUL;
    addArgListEntry( pp, pNV );
    return pNV;
}
Esempio n. 5
0
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 *  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;
}
Esempio n. 6
0
/*  addNumberValue
 *
 *  Associate a name with either a string or no value.
 */
static tOptionValue*
addNumberValue( void** pp, char const* pzName, size_t nameLen,
                char const* pzValue, size_t dataLen )
{
    tOptionValue* pNV;
    size_t sz = nameLen + sizeof(*pNV) + 1;

    pNV = AGALOC( sz, "option name/bool value pair" );
    if (pNV == NULL)
        return NULL;
    while (IS_WHITESPACE_CHAR(*pzValue) && (dataLen > 0)) {
        dataLen--; pzValue++;
    }
    if (dataLen == 0)
        pNV->v.longVal = 0;
    else
        pNV->v.longVal = strtol(pzValue, 0, 0);

    pNV->valType = OPARG_TYPE_NUMERIC;
    pNV->pzName  = (char*)(pNV + 1);
    memcpy( pNV->pzName, pzName, nameLen );
    pNV->pzName[ nameLen ] = NUL;
    addArgListEntry( pp, pNV );
    return pNV;
}
Esempio n. 7
0
/**
 *  Associate a name with strtol() value, defaulting to zero.
 *
 * @param[in,out] pp        argument list to add to
 * @param[in]     name      the name of the "suboption"
 * @param[in]     nm_len    the length of the name
 * @param[in]     val       the numeric value for the suboption
 * @param[in]     d_len     the length of the value
 *
 * @returns the new value structure
 */
static tOptionValue *
add_number(void ** pp, char const * name, size_t nm_len,
           char const * val, size_t d_len)
{
    size_t sz = nm_len + sizeof(tOptionValue) + 1;
    tOptionValue * new_val = AGALOC(sz, "int val");

    /*
     * Scan over whitespace is constrained by "d_len"
     */
    while (IS_WHITESPACE_CHAR(*val) && (d_len > 0)) {
        d_len--; val++;
    }
    if (d_len == 0)
        new_val->v.longVal = 0;
    else
        new_val->v.longVal = strtol(val, 0, 0);

    new_val->valType = OPARG_TYPE_NUMERIC;
    new_val->pzName  = (char *)(new_val + 1);
    memcpy(new_val->pzName, name, nm_len);
    new_val->pzName[ nm_len ] = NUL;
    addArgListEntry(pp, new_val);
    return new_val;
}
Esempio n. 8
0
/**
 *  Scan off the xml element name, and the rest of the header, too.
 *  Set the value type to NONE if it ends with "/>".
 *
 * @param[in]  name    the first name character (alphabetic)
 * @param[out] nm_len  the length of the name
 * @param[out] val     set valType field to STRING or NONE.
 *
 * @returns the scan resumption point, or NULL on error
 */
static char const *
scan_xml_name(char const * name, size_t * nm_len, tOptionValue * val)
{
    char const * scan = SPN_VALUE_NAME_CHARS(name + 1);
    *nm_len = (size_t)(scan - name);
    if (*nm_len > 64)
        return NULL;
    val->valType = OPARG_TYPE_STRING;

    if (IS_WHITESPACE_CHAR(*scan)) {
        /*
         * There are attributes following the name.  Parse 'em.
         */
        scan = SPN_WHITESPACE_CHARS(scan);
        scan = parse_attrs(NULL, scan, &option_load_mode, val);
        if (scan == NULL)
            return NULL; /* oops */
    }

    if (! IS_END_XML_TOKEN_CHAR(*scan))
        return NULL; /* oops */

    if (*scan == '/') {
        /*
         * Single element XML entries get inserted as an empty string.
         */
        if (*++scan != '>')
            return NULL;
        val->valType = OPARG_TYPE_NONE;
    }
    return scan+1;
}
Esempio n. 9
0
static token_list_t *
alloc_token_list(char const * str)
{
    token_list_t * res;

    int max_token_ct = 2; /* allow for trailing NULL pointer & NUL on string */

    if (str == NULL) goto enoent_res;

    /*
     *  Trim leading white space.  Use "ENOENT" and a NULL return to indicate
     *  an empty string was passed.
     */
    while (IS_WHITESPACE_CHAR(*str))  str++;
    if (*str == NUL)  goto enoent_res;

    /*
     *  Take an approximate count of tokens.  If no quoted strings are used,
     *  it will be accurate.  If quoted strings are used, it will be a little
     *  high and we'll squander the space for a few extra pointers.
     */
    {
        cc_t* pz = (cc_t*)str;

        do {
            max_token_ct++;
            while (! IS_WHITESPACE_CHAR(*++pz))
                if (*pz == NUL) goto found_nul;
            while (IS_WHITESPACE_CHAR(*pz))  pz++;
        } while (*pz != NUL);

    found_nul:
        res = malloc(sizeof(*res) + (pz - (cc_t*)str)
                     + (max_token_ct * sizeof(ch_t*)));
    }

    if (res == NULL)
        errno = ENOMEM;
    else res->tkn_list[0] = (ch_t*)(res->tkn_list + (max_token_ct - 1));

    return res;

    enoent_res:

    errno = ENOENT;
    return NULL;
}
Esempio n. 10
0
/*=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));
}
Esempio n. 11
0
/**
 *  Print the usage information for a single vendor option.
 *
 * @param pOpts     the program option descriptor
 * @param pOD       the option descriptor
 * @param pAT       names of the option argument types
 */
static void
prt_one_vendor(tOptions * pOptions, tOptDesc * pOD,
               arg_types_t * pAT, char const * usefmt)
{
    prt_preamble(pOptions, pOD, pAT);

    {
        char z[ 80 ];
        char const *  pzArgType;

        /*
         *  Determine the argument type string first on its usage, then,
         *  when the option argument is required, base the type string on the
         *  argument type.
         */
        if (pOD->fOptState & OPTST_ARG_OPTIONAL) {
            pzArgType = pAT->pzOpt;

        } else switch (OPTST_GET_ARGTYPE(pOD->fOptState)) {
        case OPARG_TYPE_NONE:        pzArgType = pAT->pzNo;   break;
        case OPARG_TYPE_ENUMERATION: pzArgType = pAT->pzKey;  break;
        case OPARG_TYPE_FILE:        pzArgType = pAT->pzFile; break;
        case OPARG_TYPE_MEMBERSHIP:  pzArgType = pAT->pzKeyL; break;
        case OPARG_TYPE_BOOLEAN:     pzArgType = pAT->pzBool; break;
        case OPARG_TYPE_NUMERIC:     pzArgType = pAT->pzNum;  break;
        case OPARG_TYPE_HIERARCHY:   pzArgType = pAT->pzNest; break;
        case OPARG_TYPE_STRING:      pzArgType = pAT->pzStr;  break;
        case OPARG_TYPE_TIME:        pzArgType = pAT->pzTime; break;
        default:                     goto bogus_desc;
        }

        while (IS_WHITESPACE_CHAR(*pzArgType))  pzArgType++;
        if (*pzArgType == NUL)
            snprintf(z, sizeof(z), "%s", pOD->pz_Name);
        else
            snprintf(z, sizeof(z), "%s=%s", pOD->pz_Name, pzArgType);
        fprintf(option_usage_fp, usefmt, z, pOD->pzText);

        switch (OPTST_GET_ARGTYPE(pOD->fOptState)) {
        case OPARG_TYPE_ENUMERATION:
        case OPARG_TYPE_MEMBERSHIP:
            displayEnum = (pOD->pOptProc != NULL) ? AG_TRUE : displayEnum;
        }
    }

    return;

bogus_desc:
    fprintf(stderr, zInvalOptDesc, pOD->pz_Name);
    exit(EX_SOFTWARE);
}
Esempio n. 12
0
/*  handleDirective
 *
 *  "pzText" points to a "<?" sequence.
 *  For the moment, we only handle "<?program" directives.
 */
static char*
handleDirective(
    tOptions*     pOpts,
    char*         pzText )
{
    char   ztitle[32] = "<?";
    size_t title_len = strlen( zProg );
    size_t name_len;

    if (  (strncmp( pzText+2, zProg, title_len ) != 0)
       || (! IS_WHITESPACE_CHAR(pzText[title_len+2])) )  {
        pzText = strchr( pzText+2, '>' );
        if (pzText != NULL)
            pzText++;
        return pzText;
    }

    name_len = strlen( pOpts->pzProgName );
    strcpy( ztitle+2, zProg );
    title_len += 2;

    do  {
        pzText += title_len;

        if (IS_WHITESPACE_CHAR(*pzText)) {
            while (IS_WHITESPACE_CHAR(*++pzText))  ;
            if (  (strneqvcmp( pzText, pOpts->pzProgName, (int)name_len) == 0)
               && (pzText[name_len] == '>'))  {
                pzText += name_len + 1;
                break;
            }
        }

        pzText = strstr( pzText, ztitle );
    } while (pzText != NULL);

    return pzText;
}
Esempio n. 13
0
LOCAL void
mungeString(char* pzTxt, tOptionLoadMode mode)
{
    char* pzE;

    if (mode == OPTION_LOAD_KEEP)
        return;

    if (IS_WHITESPACE_CHAR(*pzTxt)) {
        char* pzS = pzTxt;
        char* pzD = pzTxt;
        while (IS_WHITESPACE_CHAR(*++pzS))  ;
        while ((*(pzD++) = *(pzS++)) != NUL)   ;
        pzE = pzD-1;
    } else
        pzE = pzTxt + strlen(pzTxt);

    while ((pzE > pzTxt) && IS_WHITESPACE_CHAR(pzE[-1]))  pzE--;
    *pzE = NUL;

    if (mode == OPTION_LOAD_UNCOOKED)
        return;

    switch (*pzTxt) {
    default: return;
    case '"':
    case '\'': break;
    }

    switch (pzE[-1]) {
    default: return;
    case '"':
    case '\'': break;
    }

    (void)ao_string_cook(pzTxt, NULL);
}
Esempio n. 14
0
static char*
assembleArgValue(char* pzTxt, tOptionLoadMode mode)
{
    static char const zBrk[] = " \t\n:=";
    char* pzEnd = strpbrk(pzTxt, zBrk);
    int   space_break;

    /*
     *  Not having an argument to a configurable name is okay.
     */
    if (pzEnd == NULL)
        return pzTxt + strlen(pzTxt);

    /*
     *  If we are keeping all whitespace, then the  modevalue starts with the
     *  character that follows the end of the configurable name, regardless
     *  of which character caused it.
     */
    if (mode == OPTION_LOAD_KEEP) {
        *(pzEnd++) = NUL;
        return pzEnd;
    }

    /*
     *  If the name ended on a white space character, remember that
     *  because we'll have to skip over an immediately following ':' or '='
     *  (and the white space following *that*).
     */
    space_break = IS_WHITESPACE_CHAR(*pzEnd);
    *(pzEnd++) = NUL;
    while (IS_WHITESPACE_CHAR(*pzEnd))  pzEnd++;
    if (space_break && ((*pzEnd == ':') || (*pzEnd == '=')))
        while (IS_WHITESPACE_CHAR(*++pzEnd))  ;

    return pzEnd;
}
Esempio n. 15
0
/*=gfunc version_compare
 *
 * what:   compare two version numbers
 * general_use:
 *
 * exparg: op, comparison operator
 * exparg: v1, first version
 * exparg: v2, compared-to version
 *
 * doc:  Converts v1 and v2 strings into 64 bit values and returns the
 *       result of running 'op' on those values.  It assumes that the version
 *       is a 1 to 4 part dot-separated series of numbers.  Suffixes like,
 *       "5pre4" or "5-pre4" will be interpreted as two numbers.  The first
 *       number ("5" in this case) will be decremented and the number after
 *       the "pre" will be added to 0xC000.  (Unless your platform is unable
 *       to support 64 bit integer arithmetic.  Then it will be added to 0xC0.)
 *       Consequently, these yield true:
 *       @example
 *       (version-compare > "5.8.5"       "5.8.5-pre4")
 *       (version-compare > "5.8.5-pre10" "5.8.5-pre4")
 *       @end example
=*/
static ver_type_t
str2int_ver(char* pz)
{
    char* pzStr = pz;
    ver_type_t  val = 0;
    int ix = 4;

    while (--ix >= 0) {
        unsigned int v;
        val <<= VER_UNIT_SHIFT;
        while (IS_WHITESPACE_CHAR(*pz))  pz++;

    next_number:
        if (! IS_DEC_DIGIT_CHAR(*pz)) break;
        v = (unsigned int)strtoul(pz, &pz, 0) & ((1 << VER_UNIT_SHIFT) - 1);
        if (pz == NULL)
            break;
        val += v;
        if (*pz == '-') pz++;

        switch (*pz) {
        case 'p':
            if ((pz[1] == 'r') && (pz[2] == 'e')) {
                pz += 3;
                val = (val << 2) - 1;
                val <<= (VER_UNIT_SHIFT - 2);
                if (--ix < 0) goto leave_str2int_ver;
                goto next_number;
            }
            /* FALLTHROUGH */

        default:
            goto leave_str2int_ver;

        case '.':
            if (! IS_DEC_DIGIT_CHAR(*(++pz)))
                goto leave_str2int_ver;
            break;
        }
    } leave_str2int_ver: ;

    while (--ix >= 0)  val <<= VER_UNIT_SHIFT;
    if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS)
        fprintf(pfTrace, "0x%016llX <<== '%s'\n", (long long)val, pzStr);
    return val;
}
Esempio n. 16
0
/*=macfunc INCLUDE
 *
 *  what:   Read in and emit a template block
 *  handler_proc:
 *  load_proc:
 *
 *  desc:
 *
 *  The entire contents of the named file is inserted at this point.
 *  The contents of the file are processed for macro expansion.  The
 *  arguments are eval-ed, so you may compute the name of the file to
 *  be included.  The included file must not contain any incomplete
 *  function blocks.  Function blocks are template text beginning with
 *  any of the macro functions @samp{CASE}, @samp{DEFINE}, @samp{FOR},
 *  @samp{IF} and @samp{WHILE}; extending through their respective
 *  terminating macro functions.
=*/
tMacro*
mFunc_Include(tTemplate* pT, tMacro* pMac)
{
    tTemplate *   pNewTpl;
    ag_bool       needFree;
    char const *  pzFile = evalExpression(&needFree);
    tMacro*       pM;

    if (*pzFile != NUL) {
        pNewTpl = loadTemplate(pzFile, pT->pzTplFile);

        /*
         *  Strip off trailing white space from included templates
         */
        pM = pNewTpl->aMacros + (pNewTpl->macroCt - 1);
        if (pM->funcCode == FTYP_TEXT) {
            char* pz  = pNewTpl->pzTemplText + pM->ozText;
            char* pzE = pz + strlen(pz);
            while ((pzE > pz) && IS_WHITESPACE_CHAR(pzE[-1]))  --pzE;

            /*
             *  IF there is no text left, remove the macro entirely
             */
            if (pz == pzE)
                 pNewTpl->macroCt--;
            else *pzE = NUL;
        }

        if (OPT_VALUE_TRACE > TRACE_DEBUG_MESSAGE) {
            fprintf(pfTrace, TRACE_FN_INC_TPL, pNewTpl->pzTplFile);
            if (OPT_VALUE_TRACE == TRACE_EVERYTHING)
                fprintf(pfTrace, TRACE_FN_INC_LINE, pCurTemplate->pzTplFile,
                        pMac->lineNo);
        }

        generateBlock(pNewTpl, pNewTpl->aMacros,
                      pNewTpl->aMacros + pNewTpl->macroCt);
        unloadTemplate(pNewTpl);
        pCurTemplate = pT;
    }

    if (needFree)
        AGFREE((void*)pzFile);

    return pMac + 1;
}
Esempio n. 17
0
static void
trim_whitespace(void)
{
    char* pz = pCurCtx->pzScan;
    if (*pz == NL)
        pCurCtx->lineNo++;
    *(pz++) = NUL;

    /*
     *  This ensures that any names found previously
     *  are NUL terminated.
     */
    while (IS_WHITESPACE_CHAR(*pz)) {
        if (*pz == NL)
            pCurCtx->lineNo++;
        pz++;
    }
    pCurCtx->pzScan = pz;
}
Esempio n. 18
0
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;
}
Esempio n. 19
0
static void
lex_escaped_char(void)
{
    char* pz = strchr(pCurCtx->pzScan, ';');

    for (;;) {
        if (pz == NULL) {
            pz = pCurCtx->pzScan + strlen(pCurCtx->pzScan);
            break;
        }
        if (IS_WHITESPACE_CHAR(pz[1])) {
            *pz = NUL;
            pz[1] = ';';
            break;
        }
        pz = strchr(pz+1, ';');
    }

    lastToken = DP_EV_STRING;
    pz_token = pz;
}
Esempio n. 20
0
/**
 *  handle AutoOpts mode flags
 */
static char *
aoflags_directive(tOptions * pOpts, char * pzText)
{
    char * pz = pzText;

    while (IS_WHITESPACE_CHAR(*++pz))  ;
    pzText = strchr(pz, '>');
    if (pzText != NULL) {

        size_t len  = pzText - pz;
        char * ftxt = AGALOC(len + 1, "aoflags");

        memcpy(ftxt, pz, len);
        ftxt[len] = NUL;
        set_usage_flags(pOpts, ftxt);
        AGFREE(ftxt);

        pzText++;
    }

    return pzText;
}
Esempio n. 21
0
LOCAL void
mungeString(char * txt, tOptionLoadMode mode)
{
    char * pzE;

    if (mode == OPTION_LOAD_KEEP)
        return;

    if (IS_WHITESPACE_CHAR(*txt)) {
        char * pzS = SPN_WHITESPACE_CHARS(txt+1);
        size_t l   = strlen(pzS) + 1;
        memmove(txt, pzS, l);
        pzE = txt + l - 1;

    } else
        pzE = txt + strlen(txt);

    pzE  = SPN_WHITESPACE_BACK(txt, pzE);
    *pzE = NUL;

    if (mode == OPTION_LOAD_UNCOOKED)
        return;

    switch (*txt) {
    default: return;
    case '"':
    case '\'': break;
    }

    switch (pzE[-1]) {
    default: return;
    case '"':
    case '\'': break;
    }

    (void)ao_string_cook(txt, NULL);
}
Esempio n. 22
0
/**
 * handle program segmentation of config file.
 */
static char *
program_directive(tOptions * pOpts, char * pzText)
{
    static char const ttlfmt[] = "<?";
    size_t ttl_len  = sizeof(ttlfmt) + strlen(zCfgProg);
    char * ttl      = AGALOC(ttl_len, "prog title");
    size_t name_len = strlen(pOpts->pzProgName);

    memcpy(ttl, ttlfmt, sizeof(ttlfmt) - 1);
    strcpy(ttl + sizeof(ttlfmt) - 1, zCfgProg);

    do  {
        while (IS_WHITESPACE_CHAR(*++pzText))  ;

        if (  (strneqvcmp(pzText, pOpts->pzProgName, (int)name_len) == 0)
           && (IS_END_XML_TOKEN_CHAR(pzText[name_len])) ) {
            pzText += name_len;
            break;
        }

        pzText = strstr(pzText, ttl);
    } while (pzText != NULL);

    AGFREE(ttl);
    if (pzText != NULL)
        for (;;) {
            if (*pzText == NUL) {
                pzText = NULL;
                break;
            }
            if (*(pzText++) == '>')
                break;
        }

    return pzText;
}
Esempio n. 23
0
static char *
assemble_arg_val(char * txt, tOptionLoadMode mode)
{
    char * end = strpbrk(txt, ARG_BREAK_STR);
    int    space_break;

    /*
     *  Not having an argument to a configurable name is okay.
     */
    if (end == NULL)
        return txt + strlen(txt);

    /*
     *  If we are keeping all whitespace, then the  modevalue starts with the
     *  character that follows the end of the configurable name, regardless
     *  of which character caused it.
     */
    if (mode == OPTION_LOAD_KEEP) {
        *(end++) = NUL;
        return end;
    }

    /*
     *  If the name ended on a white space character, remember that
     *  because we'll have to skip over an immediately following ':' or '='
     *  (and the white space following *that*).
     */
    space_break = IS_WHITESPACE_CHAR(*end);
    *(end++) = NUL;

    end = SPN_WHITESPACE_CHARS(end);
    if (space_break && ((*end == ':') || (*end == '=')))
        end = SPN_WHITESPACE_CHARS(end+1);

    return end;
}
Esempio n. 24
0
/**
 *  "txt" points to the start of some value name.
 *  The end of the entry is the end of the line that is not preceded by
 *  a backslash escape character.  The string value is always processed
 *  in "cooked" mode.
 */
static char *
handle_cfg(tOptions * opts, tOptState * ost, char * txt, int dir)
{
    char* pzName = txt++;
    char* pzEnd  = strchr(txt, NL);

    if (pzEnd == NULL)
        return txt + strlen(txt);

    txt = SPN_VALUE_NAME_CHARS(txt);
    txt = SPN_WHITESPACE_CHARS(txt);
    if (txt > pzEnd) {
    name_only:
        *pzEnd++ = NUL;
        loadOptionLine(opts, ost, pzName, dir, OPTION_LOAD_UNCOOKED);
        return pzEnd;
    }

    /*
     *  Either the first character after the name is a ':' or '=',
     *  or else we must have skipped over white space.  Anything else
     *  is an invalid format and we give up parsing the text.
     */
    if ((*txt == '=') || (*txt == ':')) {
        txt = SPN_WHITESPACE_CHARS(txt+1);
        if (txt > pzEnd)
            goto name_only;
    } else if (! IS_WHITESPACE_CHAR(txt[-1]))
        return NULL;

    /*
     *  IF the value is continued, remove the backslash escape and push "pzEnd"
     *  on to a newline *not* preceded by a backslash.
     */
    if (pzEnd[-1] == '\\') {
        char* pcD = pzEnd-1;
        char* pcS = pzEnd;

        for (;;) {
            char ch = *(pcS++);
            switch (ch) {
            case NUL:
                pcS = NULL;
                /* FALLTHROUGH */

            case NL:
                *pcD = NUL;
                pzEnd = pcS;
                goto copy_done;

            case '\\':
                if (*pcS == NL)
                    ch = *(pcS++);
                /* FALLTHROUGH */
            default:
                *(pcD++) = ch;
            }
        } copy_done:;

    } else {
        /*
         *  The newline was not preceded by a backslash.  NUL it out
         */
        *(pzEnd++) = NUL;
    }

    /*
     *  "pzName" points to what looks like text for one option/configurable.
     *  It is NUL terminated.  Process it.
     */
    loadOptionLine(opts, ost, pzName, dir, OPTION_LOAD_UNCOOKED);

    return pzEnd;
}
Esempio n. 25
0
/*=export_func ao_string_tokenize
 *
 * what: tokenize an input string
 *
 * arg:  + char const* + string + string to be tokenized +
 *
 * ret_type:  token_list_t*
 * ret_desc:  pointer to a structure that lists each token
 *
 * doc:
 *
 * This function will convert one input string into a list of strings.
 * The list of strings is derived by separating the input based on
 * white space separation.  However, if the input contains either single
 * or double quote characters, then the text after that character up to
 * a matching quote will become the string in the list.
 *
 *  The returned pointer should be deallocated with @code{free(3C)} when
 *  are done using the data.  The data are placed in a single block of
 *  allocated memory.  Do not deallocate individual token/strings.
 *
 *  The structure pointed to will contain at least these two fields:
 *  @table @samp
 *  @item tkn_ct
 *  The number of tokens found in the input string.
 *  @item tok_list
 *  An array of @code{tkn_ct + 1} pointers to substring tokens, with
 *  the last pointer set to NULL.
 *  @end table
 *
 * There are two types of quoted strings: single quoted (@code{'}) and
 * double quoted (@code{"}).  Singly quoted strings are fairly raw in that
 * escape characters (@code{\\}) are simply another character, except when
 * preceding the following characters:
 * @example
 * @code{\\}  double backslashes reduce to one
 * @code{'}   incorporates the single quote into the string
 * @code{\n}  suppresses both the backslash and newline character
 * @end example
 *
 * Double quote strings are formed according to the rules of string
 * constants in ANSI-C programs.
 *
 * example:
 * @example
 *    #include <stdlib.h>
 *    int ix;
 *    token_list_t* ptl = ao_string_tokenize(some_string)
 *    for (ix = 0; ix < ptl->tkn_ct; ix++)
 *       do_something_with_tkn(ptl->tkn_list[ix]);
 *    free(ptl);
 * @end example
 * Note that everything is freed with the one call to @code{free(3C)}.
 *
 * err:
 *  NULL is returned and @code{errno} will be set to indicate the problem:
 *  @itemize @bullet
 *  @item
 *  @code{EINVAL} - There was an unterminated quoted string.
 *  @item
 *  @code{ENOENT} - The input string was empty.
 *  @item
 *  @code{ENOMEM} - There is not enough memory.
 *  @end itemize
=*/
token_list_t*
ao_string_tokenize(char const* str)
{
    token_list_t* res = alloc_token_list(str);
    ch_t* pzDest;

    /*
     *  Now copy each token into the output buffer.
     */
    if (res == NULL)
        return res;

    pzDest = (ch_t*)(res->tkn_list[0]);
    res->tkn_ct  = 0;

    do  {
        res->tkn_list[ res->tkn_ct++ ] = pzDest;
        for (;;) {
            int ch = (ch_t)*str;
            if (IS_WHITESPACE_CHAR(ch)) {
            found_white_space:
                str = SPN_WHITESPACE_CHARS(str+1);
                break;
            }

            switch (ch) {
            case '"':
                copy_cooked(&pzDest, &str);
                if (str == NULL) {
                    free(res);
                    errno = EINVAL;
                    return NULL;
                }
                if (IS_WHITESPACE_CHAR(*str))
                    goto found_white_space;
                break;

            case '\'':
                copy_raw(&pzDest, &str);
                if (str == NULL) {
                    free(res);
                    errno = EINVAL;
                    return NULL;
                }
                if (IS_WHITESPACE_CHAR(*str))
                    goto found_white_space;
                break;

            case NUL:
                goto copy_done;

            default:
                str++;
                *(pzDest++) = (unsigned char)ch;
            }
        } copy_done:;

        /*
         * NUL terminate the last token and see if we have any more tokens.
         */
        *(pzDest++) = NUL;
    } while (*str != NUL);

    res->tkn_list[ res->tkn_ct ] = NULL;

    return res;
}
Esempio n. 26
0
/**
 *  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.
 */
char *
doDir_define(directive_enum_t id, char const * dir, char * scan_next)
{
    char * def_name = SPN_WHITESPACE_CHARS(dir);
    (void)id;

    /*
     *  Skip any #defines that do not look reasonable
     */
    if (! IS_VAR_FIRST_CHAR(*def_name))
        return scan_next;

    dir = SPN_VARIABLE_NAME_CHARS(def_name);

    /*
     *  IF this is a macro definition (rather than a value def),
     *  THEN we will ignore it.
     */
    if (*dir == '(')
        return scan_next;

    /*
     *  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(*dir)) {
        char * pzS = (char *)dir;
        char * pzD = (def_name - 6);

        *pzS = NUL; // whatever it used to be, it is a NUL now.
        pzS = def_name;
        while ((*(pzD++) = *(pzS++)) != NUL)   ;
        pzD[-1] = '=';
        pzD[ 0] = NUL;
        def_name -= 6;

    } 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 = (char *)(dir++);
        *pz++ = '=';
        dir = SPN_WHITESPACE_CHARS(dir);

        /*
         * Copy chars for so long as it is not NUL and does not require quoting
         */
        for (;;) {
            if ((*pz++ = *dir++) == NUL)
                break;
            if (! IS_UNQUOTABLE_CHAR(*dir)) {
                *pz = NUL;
                break;
            }
        }
    }

    SET_OPT_DEFINE(def_name);
    return scan_next;
}
Esempio n. 27
0
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 *  A quoted string has been found.
 *  Find the end of it and compress any escape sequences.
 */
static bool
contiguous_quote(char ** pps, char * pq, int * lnct_p)
{
    char * ps = *pps + 1;

    for (;;) {
        while (IS_WHITESPACE_CHAR(*ps))
            if (*(ps++) == NL)
                (*lnct_p)++;

        /*
         *  IF the next character is a quote character,
         *  THEN we will concatenate the strings.
         */
        switch (*ps) {
        case '"':
        case '\'':
            *pq  = *(ps++);  /* assign new quote character and return */
            *pps = ps;
            return true;

        case '/':
            /*
             *  Allow for a comment embedded in the concatenated string.
             */
            switch (ps[1]) {
            default:
                *pps = NULL;
                return false;

            case '/':
                /*
                 *  Skip to end of line
                 */
                ps = strchr(ps, NL);
                if (ps == NULL) {
                    *pps = NULL;
                    return false;
                }
                break;

            case '*':
            {
                char * p = strstr( ps+2, "*/" );
                /*
                 *  Skip to terminating star slash
                 */
                if (p == NULL) {
                    *pps = NULL;
                    return false;
                }

                while (ps < p) {
                    if (*(ps++) == NL)
                        (*lnct_p)++;
                }

                ps = p + 2;
            }
            }
            continue;

        default:
            /*
             *  The next non-whitespace character is not a quote.
             *  The series of quoted strings has come to an end.
             */
            *pps = ps;
            return false;
        }
    }
}
Esempio n. 28
0
/*=macfunc SELECT
 *
 *  what:    Selection block for CASE function
 *  in-context:
 *  alias:   | ~ | = | * | ! | + |
 *  unload-proc:
 *
 *  desc:
 *    This macro selects a block of text by matching an expression
 *    against the sample text expression evaluated in the @code{CASE}
 *    macro.  @xref{CASE}.
 *
 *    You do not specify a @code{SELECT} macro with the word ``select''.
 *    Instead, you must use one of the 19 match operators described in
 *    the @code{CASE} macro description.
=*/
static tMacro*
mLoad_Select(tTemplate * pT, tMacro* pMac, char const ** ppzScan)
{
    char const *  pzScan = *ppzScan;  /* text after macro */
    char*         pzCopy = pT->pNext; /* next text dest   */
    char const *  pzSrc  = (char*)pMac->ozText; /* macro text */
    long          srcLen = pMac->res;           /* macro len  */

    static char const zInvSel[] = "Invalid selection clause";
    int  typ       = (int)FTYP_SELECT_COMPARE_FULL;

    /*
     *  Set the global macro loading mode
     */
    papLoadProc = apCaseLoad;
    pMac->res   = 0;
    if (srcLen == 0)
        AG_ABEND_IN(pT, pMac, "Empty macro text");

    /*
     *  IF the first character is an asterisk,
     *  THEN the match can start anywhere in the string
     */
    if (*pzSrc == '*') {
        pzSrc++;
        if (IS_WHITESPACE_CHAR(*pzSrc) || (*pzSrc == NUL)) {
            typ    = (int)FTYP_SELECT_MATCH_ANYTHING;
            srcLen = 0;
            pMac->ozText = 0;
            goto selection_done;
        }

        typ |= (int)FTYP_SELECT_COMPARE_SKP_START;
    }

    /*
     *  The next character must indicate whether we are
     *  pattern matching ('~') or doing string compares ('=')
     */
    switch (*pzSrc++) {
    case '~':
        /*
         *  Or in the pattern matching bit
         */
        typ |= (int)FTYP_SELECT_MATCH_FULL;
        pMac->res = REG_EXTENDED;
        /* FALLTHROUGH */

    case '=':
        /*
         *  IF the '~' or '=' is doubled,
         *  THEN it is a case sensitive match.  Skip over the char.
         *  ELSE or in the case insensitive bit
         */
        if (pzSrc[0] == pzSrc[-1]) {
            pzSrc++;
        } else {
            typ |= (int)FTYP_SELECT_EQUIVALENT_FULL;
        }
        break;

    case '!':
    case '+':
        switch (*pzSrc) {
        case 'e':
        case 'E':
            break;
        default:
            goto bad_sel;
        }
        if ((pzSrc[1] != NUL) && (! IS_WHITESPACE_CHAR(pzSrc[1])))
            goto bad_sel;

        typ = (int)((pzSrc[-1] == '!')
            ? FTYP_SELECT_MATCH_NONEXISTENCE : FTYP_SELECT_MATCH_EXISTENCE);
        srcLen = 0;
        pMac->ozText = 0;
        goto selection_done;

    default:
    bad_sel:
        AG_ABEND_IN(pT, pMac, zInvSel);
    }

    /*
     *  IF the last character is an asterisk,
     *  THEN the match may end before the test string ends.
     *       OR in the "may end early" bit.
     */
    if (*pzSrc == '*') {
        pzSrc++;
        typ |= (int)FTYP_SELECT_COMPARE_SKP_END;
    }

    if (! IS_WHITESPACE_CHAR(*pzSrc))
        AG_ABEND_IN(pT, pMac, zInvSel);

    while (IS_WHITESPACE_CHAR(*pzSrc)) pzSrc++;
    srcLen -= pzSrc - (char const *)pMac->ozText;
    if (srcLen <= 0)
        AG_ABEND_IN(pT, pMac, zInvSel);

    /*
     *  See if we are doing case insensitive regular expressions
     */
    if (  (typ & (int)FTYP_SELECT_EQV_MATCH_FULL)
       == (int)FTYP_SELECT_EQV_MATCH_FULL) {
        int  bitSet;
        pMac->res = REG_EXTENDED | REG_ICASE;

        /*
         *  Turn off the case comparison mode for regular expressions.
         *  We don't have to worry about it.  It is done for us.
         */
        bitSet  = ~(int)FTYP_SELECT_EQUIVALENT_FULL;
        bitSet |= (int)FTYP_SELECT_COMPARE_FULL; /* dont turn this bit off! */
        typ    &= bitSet;
    }

    /*
     *  Copy the expression
     */
    pzScan = pzCopy;
    pMac->ozText = (pzCopy - pT->pzTemplText);
    if (typ == (int)FTYP_SELECT_EQUIVALENT) {
        do  {
            *(pzCopy++) = toupper(*(pzSrc++));
        } while (--srcLen > 0);
    } else {
        do  {
            *(pzCopy++) = *(pzSrc++);
        } while (--srcLen > 0);
    }
    *(pzCopy++) = NUL;
    *(pzCopy++) = NUL;
    pT->pNext = pzCopy;

    if ((*pzScan == '"') || (*pzScan == '\'')) {
        void * ptr = (void *)pzScan;
        spanQuote(ptr);
    }

 selection_done:
    pMac->funcCode = (teFuncType)typ;

    current_case.pSelect->sibIndex = (pMac - pT->aMacros);
    current_case.pSelect = (tMacro*)pMac;

    return pMac + 1;
}
Esempio n. 29
0
LOCAL void
set_usage_flags(tOptions * opts, char const * flg_txt)
{
    typedef struct {
        size_t          fnm_len;
        uint32_t        fnm_mask;
        char const *    fnm_name;
    } ao_flag_names_t;

#   define _aof_(_n, _f)   AOUF_ ## _n ## _ID,
    typedef enum { AOFLAG_TABLE AOUF_COUNT } ao_flag_id_t;
#   undef  _aof_

#   define _aof_(_n, _f)   AOUF_ ## _n = (1 << AOUF_ ## _n ## _ID),
    typedef enum { AOFLAG_TABLE } ao_flags_t;
#   undef  _aof_

#   define _aof_(_n, _f)   { sizeof(#_n)-1, _f, #_n },
    static ao_flag_names_t const fn_table[AOUF_COUNT] = {
        AOFLAG_TABLE
    };
#   undef  _aof_

    ao_flags_t flg = 0;

    if (flg_txt == NULL) {
        flg_txt = getenv("AUTOOPTS_USAGE");
        if (flg_txt == NULL) return;
    }

    while (IS_WHITESPACE_CHAR(*flg_txt))  flg_txt++;
    if (*flg_txt == NUL)
        return;

    for (;;) {
        int ix = 0;
        ao_flag_names_t const * fnt = fn_table;

        for (;;) {
            if (strneqvcmp(flg_txt, fnt->fnm_name, fnt->fnm_len) == 0)
                break;
            if (++ix >= AOUF_COUNT)
                return;
            fnt++;
        }

        /*
         *  Make sure we have a full match.  Look for whitespace,
         *  a comma, or a NUL byte.
         */
        if (! IS_END_LIST_ENTRY_CHAR(flg_txt[fnt->fnm_len]))
            return;

        flg |= 1 << ix;
        flg_txt  += fnt->fnm_len;
        while (IS_WHITESPACE_CHAR(*flg_txt))  flg_txt++;

        if (*flg_txt == NUL)
            break;

        if (*flg_txt == ',') {
            /*
             *  skip the comma and following white space
             */
            while (IS_WHITESPACE_CHAR(*++flg_txt))  ;
            if (*flg_txt == NUL)
                break;
        }
    }

    {
        ao_flag_names_t const * fnm = fn_table;

        while (flg != 0) {
            if ((flg & 1) != 0) {
                if ((fnm->fnm_mask & OPTPROC_LONGOPT) != 0)
                     opts->fOptSet &= fnm->fnm_mask;
                else opts->fOptSet |= fnm->fnm_mask;
            }
            flg >>= 1;
            fnm++;
        }
    }
}
Esempio n. 30
0
/*
 *  Load an option from a block of text.  The text must start with the
 *  configurable/option name and be followed by its associated value.
 *  That value may be processed in any of several ways.  See "tOptionLoadMode"
 *  in autoopts.h.
 */
LOCAL void
loadOptionLine(
    tOptions*   pOpts,
    tOptState*  pOS,
    char*       pzLine,
    tDirection  direction,
    tOptionLoadMode   load_mode )
{
    while (IS_WHITESPACE_CHAR(*pzLine))  pzLine++;

    {
        char* pzArg = assembleArgValue(pzLine, load_mode);

        if (! SUCCESSFUL(opt_find_long(pOpts, pzLine, pOS)))
            return;
        if (pOS->flags & OPTST_NO_INIT)
            return;
        pOS->pzOptArg = pzArg;
    }

    switch (pOS->flags & (OPTST_IMM|OPTST_DISABLE_IMM)) {
    case 0:
        /*
         *  The selected option has no immediate action.
         *  THEREFORE, if the direction is PRESETTING
         *  THEN we skip this option.
         */
        if (PRESETTING(direction))
            return;
        break;

    case OPTST_IMM:
        if (PRESETTING(direction)) {
            /*
             *  We are in the presetting direction with an option we handle
             *  immediately for enablement, but normally for disablement.
             *  Therefore, skip if disabled.
             */
            if ((pOS->flags & OPTST_DISABLED) == 0)
                return;
        } else {
            /*
             *  We are in the processing direction with an option we handle
             *  immediately for enablement, but normally for disablement.
             *  Therefore, skip if NOT disabled.
             */
            if ((pOS->flags & OPTST_DISABLED) != 0)
                return;
        }
        break;

    case OPTST_DISABLE_IMM:
        if (PRESETTING(direction)) {
            /*
             *  We are in the presetting direction with an option we handle
             *  immediately for disablement, but normally for disablement.
             *  Therefore, skip if NOT disabled.
             */
            if ((pOS->flags & OPTST_DISABLED) != 0)
                return;
        } else {
            /*
             *  We are in the processing direction with an option we handle
             *  immediately for disablement, but normally for disablement.
             *  Therefore, skip if disabled.
             */
            if ((pOS->flags & OPTST_DISABLED) == 0)
                return;
        }
        break;

    case OPTST_IMM|OPTST_DISABLE_IMM:
        /*
         *  The selected option is always for immediate action.
         *  THEREFORE, if the direction is PROCESSING
         *  THEN we skip this option.
         */
        if (PROCESSING(direction))
            return;
        break;
    }

    /*
     *  Fix up the args.
     */
    if (OPTST_GET_ARGTYPE(pOS->pOD->fOptState) == OPARG_TYPE_NONE) {
        if (*pOS->pzOptArg != NUL)
            return;
        pOS->pzOptArg = NULL;

    } else if (pOS->pOD->fOptState & OPTST_ARG_OPTIONAL) {
        if (*pOS->pzOptArg == NUL)
             pOS->pzOptArg = NULL;
        else {
            AGDUPSTR(pOS->pzOptArg, pOS->pzOptArg, "option argument");
            pOS->flags |= OPTST_ALLOC_ARG;
        }

    } else {
        if (*pOS->pzOptArg == NUL)
             pOS->pzOptArg = zNil;
        else {
            AGDUPSTR(pOS->pzOptArg, pOS->pzOptArg, "option argument");
            pOS->flags |= OPTST_ALLOC_ARG;
        }
    }

    {
        tOptionLoadMode sv = option_load_mode;
        option_load_mode = load_mode;
        handle_opt(pOpts, pOS);
        option_load_mode = sv;
    }
}