Ejemplo n.º 1
0
/**
 *  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.
 */
char *
doDir_line(directive_enum_t id, char const * dir, char * scan_next)
{
    (void)id;
    /*
     *  The sequence must be:  #line <number> "file-name-string"
     *
     *  Start by scanning up to and extracting the line number.
     */
    dir = SPN_WHITESPACE_CHARS(dir);
    if (! IS_DEC_DIGIT_CHAR(*dir))
        return scan_next;

    cctx->scx_line = (int)strtol(dir, (char **)&dir, 0);

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

    AGDUPSTR(cctx->scx_fname, dir, "#line");

    return scan_next;
}
Ejemplo n.º 2
0
/**
 * Configure a dependency option.
 * Handles any of these letters:  MFQTPGD as the first part of the option
 * argument.
 *
 * @param opts the autogen options data structure
 * @param pOptDesc the option descriptor for this option.
 */
LOCAL void
config_dep(tOptions * opts, tOptDesc * od)
{
    char const * opt_arg = od->optArg.argString;
    (void)opts;

    /*
     *  The option argument is optional.  Make sure we have one.
     */
    if (opt_arg == NULL)
        return;

    while (*opt_arg == 'M')  opt_arg++;
    opt_arg = SPN_WHITESPACE_CHARS(opt_arg);

    switch (*opt_arg) {
    case 'F':
        if (dep_file != NULL)
            dep_usage(CFGDEP_DUP_TARGET_MSG);

        opt_arg = SPN_WHITESPACE_CHARS(opt_arg + 1);
        AGDUPSTR(dep_file, opt_arg, "f name");
        break;

    case 'Q':
    case 'T':
    {
        bool quote_it = (*opt_arg == 'Q');

        if (dep_target != NULL)
            dep_usage(CFGDEP_DUP_TARGET_MSG);

        opt_arg = SPN_WHITESPACE_CHARS(opt_arg + 1);
        if (quote_it)
            dep_target = make_quote_str(opt_arg);
        else
            AGDUPSTR(dep_target, opt_arg, "t name");
        break;
    }

    case 'P':
        dep_phonies = true;
        break;

    case 'D':
    case 'G':
    case NUL:
        /*
         *  'D' and 'G' make sense to GCC, not us.  Ignore 'em.  If we
         *  found a NUL byte, then act like we found -MM on the command line.
         */
        break;

    default:
        dep_usage(CFGDEP_UNKNOWN_DEP_FMT, opt_arg);
    }
}
Ejemplo n.º 3
0
/**
 * Parse the option usage flags string.  Any parsing problems yield
 * a zero (no flags set) result.  This function is internal to
 * set_usage_flags().
 *
 * @param[in] fnt   Flag Name Table - maps a name to a mask
 * @param[in] txt   the text to process.  If NULL, then
 *                  getenv("AUTOOPTS_USAGE") is used.
 * @returns a bit mask indicating which \a fnt entries were found.
 */
static unsigned int
parse_usage_flags(ao_flag_names_t const * fnt, char const * txt)
{
    unsigned int res = 0;

    /*
     * The text may be passed in.  If not, use the environment variable.
     */
    if (txt == NULL) {
        txt = getenv("AUTOOPTS_USAGE");
        if (txt == NULL)
            return 0;
    }

    txt = SPN_WHITESPACE_CHARS(txt);
    if (*txt == NUL)
        return 0;

    /*
     * search the string for table entries.  We must understand everything
     * we see in the string, or we give up on it.
     */
    for (;;) {
        int ix = 0;

        for (;;) {
            if (strneqvcmp(txt, fnt[ix].fnm_name, (int)fnt[ix].fnm_len) == 0)
                break;
            if (++ix >= AOUF_COUNT)
                return 0;
        }

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

        res |= 1U << ix;
        txt = SPN_WHITESPACE_CHARS(txt + fnt[ix].fnm_len);

        switch (*txt) {
        case NUL:
            return res;

        case ',':
            txt = SPN_WHITESPACE_CHARS(txt + 1);
            /* Something must follow the comma */

        default:
            continue;
        }
    }
}
Ejemplo n.º 4
0
/**
 * We've found a closing '>' without a preceding '/', thus we must search
 * the text for '<name/>' where "name" is the name of the XML element.
 *
 * @param[in]  name     the start of the name in the element header
 * @param[in]  nm_len   the length of that name
 * @param[out] len      the length of the value (string between header and
 *                      the trailer/tail.
 * @returns the character after the trailer, or NULL if not found.
 */
static char const *
find_end_xml(char const * src, size_t nm_len, char const * val, size_t * len)
{
    char z[72] = "</";
    char * dst = z + 2;

    do  {
        *(dst++) = *(src++);
    } while (--nm_len > 0); /* nm_len is known to be 64 or less */
    *(dst++) = '>';
    *dst = NUL;

    {
        char const * res = strstr(val, z);

        if (res != NULL) {
            char const * end = (option_load_mode != OPTION_LOAD_KEEP)
                ? SPN_WHITESPACE_BACK(val, res)
                : res;
            *len = (size_t)(end - val); /* includes trailing white space */
            res =  SPN_WHITESPACE_CHARS(res + (dst - z));
        }
        return res;
    }
}
Ejemplo n.º 5
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;
}
Ejemplo n.º 6
0
static void
check_assert_str(char const * pz, char const * arg)
{
    pz = SPN_WHITESPACE_CHARS(pz);

    if (IS_FALSE_TYPE_CHAR(*pz))
        AG_ABEND(aprf(DIRECT_ASSERT_FMT, pz, arg));
}
Ejemplo n.º 7
0
/**
 * Check membership start conditions.  An equal character (@samp{=}) says to
 * clear the result and not carry over any residual value.  A carat
 * (@samp{^}), which may follow the equal character, says to invert the
 * result.  The scanning pointer is advanced past these characters and any
 * leading white space.  Invalid sequences are indicated by setting the
 * scanning pointer to NULL.
 *
 * @param od      the set membership option description
 * @param argp    a pointer to the string scanning pointer
 * @param invert  a pointer to the boolean inversion indicator
 *
 * @returns either zero or the original value for the optCookie.
 */
static uintptr_t
check_membership_start(tOptDesc * od, char const ** argp, bool * invert)
{
    uintptr_t    res = (uintptr_t)od->optCookie;
    char const * arg = SPN_WHITESPACE_CHARS(od->optArg.argString);
    if ((arg == NULL) || (*arg == NUL))
        goto member_start_fail;

    *invert = false;

    switch (*arg) {
    case '=':
        res = 0UL;
        arg = SPN_WHITESPACE_CHARS(arg + 1);
        switch (*arg) {
        case '=': case ',':
            goto member_start_fail;
        case '^':
            goto inversion;
        default:
            break;
        }
        break;

    case '^':
    inversion:
        *invert = true;
        arg = SPN_WHITESPACE_CHARS(arg + 1);
        if (*arg != ',')
            break;
        /* FALLTHROUGH */

    case ',':
        goto member_start_fail;

    default:
        break;
    }

    *argp = arg;
    return res;

member_start_fail:
    *argp = NULL;
    return 0UL;
}
Ejemplo n.º 8
0
/**
 *  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.
 */
char *
doDir_option(directive_enum_t id, char const * dir, char * scan_next)
{
    dir = SPN_WHITESPACE_CHARS(dir);
    optionLoadLine(&autogenOptions, dir);
    (void)id;
    return scan_next;
}
Ejemplo n.º 9
0
/**
 *  Will remove any entries from the define list
 *  that match the undef name pattern.
 */
char *
doDir_undef(directive_enum_t id, char const * dir, char * scan_next)
{
    dir = SPN_WHITESPACE_CHARS(dir);
    SET_OPT_UNDEFINE(dir);
    (void)id;
    return scan_next;
}
Ejemplo n.º 10
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.
     */
    str = SPN_WHITESPACE_CHARS(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.
     */
    {
        char const * pz = str;

        do {
            max_token_ct++;
            pz = BRK_WHITESPACE_CHARS(pz+1);
            pz = SPN_WHITESPACE_CHARS(pz);
        } while (*pz != NUL);

        res = malloc(sizeof(*res) + (size_t)(pz - str)
                     + ((size_t)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;
}
Ejemplo n.º 11
0
/**
 *  This directive will cause AutoGen to stop processing
 *  and exit with a status of EXIT_FAILURE.
 */
char *
doDir_error(directive_enum_t id, char const * arg, char * scan_next)
{
    arg = SPN_WHITESPACE_CHARS(arg);
    AG_ABEND(aprf(DIRECT_ERROR_FMT, cctx->scx_fname, cctx->scx_line, arg));
    /* NOTREACHED */
    (void)scan_next;
    (void)id;
    return NULL;
}
Ejemplo n.º 12
0
/**
 *  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.
 */
char *
doDir_ifdef(directive_enum_t id, char const * dir, char * scan_next)
{
    char const * defstr = get_define_str(SPN_WHITESPACE_CHARS(dir), false);
    bool ok = (id == DIR_IFDEF) ? (defstr != NULL) : (defstr == NULL);
    if (! ok)
        return skip_to_else_end(scan_next);

    ifdef_lvl++;
    return scan_next;
}
Ejemplo n.º 13
0
/**
 *  Print the usage information for a single vendor option.
 *
 * @param[in] opts    the program option descriptor
 * @param[in] od      the option descriptor
 * @param[in] argtp   names of the option argument types
 * @param[in] usefmt  format for primary usage line
 */
static void
prt_one_vendor(tOptions *    opts,  tOptDesc *   od,
               arg_types_t * argtp, char const * usefmt)
{
    prt_preamble(opts, od, argtp);

    {
        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 (od->fOptState & OPTST_ARG_OPTIONAL) {
            pzArgType = argtp->pzOpt;

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

        pzArgType = SPN_WHITESPACE_CHARS(pzArgType);
        if (*pzArgType == NUL)
            snprintf(z, sizeof(z), "%s", od->pz_Name);
        else
            snprintf(z, sizeof(z), "%s=%s", od->pz_Name, pzArgType);
        fprintf(option_usage_fp, usefmt, z, od->pzText);

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

    return;

 bogus_desc:
    fprintf(stderr, zbad_od, opts->pzProgName, od->pz_Name);
    ao_bug(zbad_arg_type_msg);
}
Ejemplo n.º 14
0
static char *
assemble_arg_val(char * txt, tOptionLoadMode mode)
{
    char* pzEnd = strpbrk(txt, ARG_BREAK_STR);
    int   space_break;

    /*
     *  Not having an argument to a configurable name is okay.
     */
    if (pzEnd == 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) {
        *(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;

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

    return pzEnd;
}
Ejemplo n.º 15
0
static char *
next_directive(char * scan)
{
    if (*scan == '#')
        scan++;
    else {
        char * pz = strstr(scan, DIRECT_CK_LIST_MARK);
        if (pz == NULL)
            AG_ABEND(aprf(DIRECT_NOENDIF_FMT, cctx->scx_fname,
                          cctx->scx_line));

        scan = pz + 2;
    }

    return SPN_WHITESPACE_CHARS(scan);
}
Ejemplo n.º 16
0
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 *  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 * scan)
{
    char * eodir = end_of_directive(scan);

    /*
     *  Ignore '#!' as a comment, enabling a definition file to behave
     *  as a script that gets interpreted by autogen.  :-)
     */
    if (*scan == '!')
        return eodir;

    scan = SPN_WHITESPACE_CHARS(scan);
    if (! IS_ALPHABETIC_CHAR(*scan))
        return doDir_invalid(DIR_INVALID, scan, eodir);

    return doDir_directive_disp(scan, eodir);
}
Ejemplo n.º 17
0
/**
 *  handle AutoOpts mode flags.
 *
 *  @param[in,out] opts  program option descriptor
 *  @param[in]     txt   scanning pointer
 *  @returns       the next character to look at
 */
static char *
aoflags_directive(tOptions * opts, char * txt)
{
    char * pz;

    pz = SPN_WHITESPACE_CHARS(txt+1);
    txt = strchr(pz, '>');
    if (txt != NULL) {

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

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

        txt++;
    }

    return txt;
}
Ejemplo n.º 18
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);
}
Ejemplo n.º 19
0
/**
 * handle program segmentation of config file.
 *
 *  @param[in,out] opts  program option descriptor
 *  @param[in]     txt   scanning pointer
 *  @returns       the next character to look at
 */
static char *
program_directive(tOptions * opts, char * txt)
{
    static char const ttlfmt[] = "<?";
    size_t ttl_len  = sizeof(ttlfmt) + strlen(zCfgProg);
    char * ttl      = AGALOC(ttl_len, "prog title");
    size_t name_len = strlen(opts->pzProgName);

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

    do  {
        txt = SPN_WHITESPACE_CHARS(txt+1);

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

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

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

    return txt;
}
Ejemplo n.º 20
0
/**
 *  This directive @i{is} processed, but only if the expression begins with
 *  either a back quote (@code{`}) or an open parenthesis (@code{(}).
 *  Text within the back quotes are handed off to the shell for processing
 *  and parenthesized text is handed off to Guile.  Multiple line expressions
 *  must be joined with backslashes.
 *
 *  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.
 *
 *  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").
 */
char *
doDir_assert(directive_enum_t id, char const * dir, char * scan_next)
{
    (void)id;
    dir = SPN_WHITESPACE_CHARS(dir);
    switch (*dir) {
    case '`':
    {
        char * pzS = (char *)dir+1;
        char * pzR = SPN_WHITESPACE_BACK(pzS, NULL);

        if (*(--pzR) != '`')
            break; /* not a valid script */

        *pzR = NUL;
        pzS = shell_cmd((char const *)pzS);
        check_assert_str(pzS, dir);
        AGFREE(pzS);
        break;
    }

    case '(':
    {
        SCM res = ag_scm_c_eval_string_from_file_line(
            dir, cctx->scx_fname, cctx->scx_line);

        check_assert_str(scm2display(res), dir);
        break;
    }

    default:
        break;
    }

    return scan_next;
}
Ejemplo n.º 21
0
/**
 *  We've found a '<' character.  We ignore this if it is a comment or a
 *  directive.  If it is something else, then whatever it is we are looking
 *  at is bogus.  Returning NULL stops processing.
 *
 * @param[in]     xml_name  the name of an xml bracket (usually)
 * @param[in,out] res_val   the option data derived from the XML element
 *
 * @returns the place to resume scanning input
 */
static char const *
scan_xml(char const * xml_name, tOptionValue * res_val)
{
    size_t          nm_len, v_len;
    char const *    scan;
    char const *    val_str;
    tOptionValue    valu;
    tOptionLoadMode save_mode = option_load_mode;

    if (! IS_VAR_FIRST_CHAR(*++xml_name))
        return unnamed_xml(xml_name);

    /*
     * "scan_xml_name()" may change "option_load_mode".
     */
    val_str = scan_xml_name(xml_name, &nm_len, &valu);
    if (val_str == NULL)
        goto bail_scan_xml;

    if (valu.valType == OPARG_TYPE_NONE)
        scan = val_str;
    else {
        if (option_load_mode != OPTION_LOAD_KEEP)
            val_str = SPN_WHITESPACE_CHARS(val_str);
        scan = find_end_xml(xml_name, nm_len, val_str, &v_len);
        if (scan == NULL)
            goto bail_scan_xml;
    }

    /*
     * "scan" now points to where the scan is to resume after returning.
     * It either points after "/>" at the end of the XML element header,
     * or it points after the "</name>" tail based on the name in the header.
     */

    switch (valu.valType) {
    case OPARG_TYPE_NONE:
        add_string(&(res_val->v.nestVal), xml_name, nm_len, NULL, 0);
        break;

    case OPARG_TYPE_STRING:
    {
        tOptionValue * new_val = add_string(
            &(res_val->v.nestVal), xml_name, nm_len, val_str, v_len);

        if (option_load_mode != OPTION_LOAD_KEEP)
            munge_str(new_val->v.strVal, option_load_mode);

        break;
    }

    case OPARG_TYPE_BOOLEAN:
        add_bool(&(res_val->v.nestVal), xml_name, nm_len, val_str, v_len);
        break;

    case OPARG_TYPE_NUMERIC:
        add_number(&(res_val->v.nestVal), xml_name, nm_len, val_str, v_len);
        break;

    case OPARG_TYPE_HIERARCHY:
    {
        char * pz = AGALOC(v_len+1, "h scan");
        memcpy(pz, val_str, v_len);
        pz[v_len] = NUL;
        add_nested(&(res_val->v.nestVal), xml_name, nm_len, pz, v_len);
        AGFREE(pz);
        break;
    }

    case OPARG_TYPE_ENUMERATION:
    case OPARG_TYPE_MEMBERSHIP:
    default:
        break;
    }

    option_load_mode = save_mode;
    return scan;

bail_scan_xml:
    option_load_mode = save_mode;
    return NULL;
}
Ejemplo n.º 22
0
/**
 *  "txt" points to a '<' character, followed by an alpha.
 *  The end of the entry is either the "/>" following the name, or else a
 *  "</name>" string.
 */
static char *
handle_struct(tOptions * opts, tOptState * ost, char * txt, int dir)
{
    tOptionLoadMode mode = option_load_mode;
    tOptionValue    valu;

    char* pzName = ++txt;
    char* pzData;
    char* pcNulPoint;

    txt = SPN_VALUE_NAME_CHARS(txt);
    pcNulPoint = txt;
    valu.valType = OPARG_TYPE_STRING;

    switch (*txt) {
    case ' ':
    case '\t':
        txt = (void *)parse_attrs(
            opts, SPN_WHITESPACE_CHARS(txt), &mode, &valu);
        if (txt == NULL)
            return txt;
        if (*txt == '>')
            break;
        if (*txt != '/')
            return NULL;
        /* FALLTHROUGH */

    case '/':
        if (txt[1] != '>')
            return NULL;
        *txt = NUL;
        txt += 2;
        loadOptionLine(opts, ost, pzName, dir, mode);
        return txt;

    case '>':
        break;

    default:
        txt = strchr(txt, '>');
        if (txt != NULL)
            txt++;
        return txt;
    }

    /*
     *  If we are here, we have a value.  "txt" points to a closing angle
     *  bracket.  Separate the name from the value for a moment.
     */
    *pcNulPoint = NUL;
    pzData = ++txt;
    txt = trim_xml_text(txt, pzName, mode);
    if (txt == NULL)
        return txt;

    /*
     *  Rejoin the name and value for parsing by "loadOptionLine()".
     *  Erase any attributes parsed by "parse_attrs()".
     */
    memset(pcNulPoint, ' ', (size_t)(pzData - pcNulPoint));

    /*
     *  If we are getting a "string" value that is to be cooked,
     *  then process the XML-ish &xx; XML-ish and %XX hex characters.
     */
    if (  (valu.valType == OPARG_TYPE_STRING)
       && (mode == OPTION_LOAD_COOKED))
        cook_xml_text(pzData);

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

    return txt;
}
Ejemplo n.º 23
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;
}
Ejemplo n.º 24
0
/**
 *  Load a file containing presetting information (a configuration file).
 */
static void
file_preset(tOptions * opts, char const * fname, int dir)
{
    tmap_info_t       cfgfile;
    tOptState         optst = OPTSTATE_INITIALIZER(PRESET);
    opt_state_mask_t  st_flags = optst.flags;
    char *            ftext =
        text_mmap(fname, PROT_READ|PROT_WRITE, MAP_PRIVATE, &cfgfile);

    if (TEXT_MMAP_FAILED_ADDR(ftext))
        return;

    if (dir == DIRECTION_CALLED) {
        st_flags = OPTST_DEFINED;
        dir   = DIRECTION_PROCESS;
    }

    /*
     *  IF this is called via "optionProcess", then we are presetting.
     *  This is the default and the PRESETTING bit will be set.
     *  If this is called via "optionFileLoad", then the bit is not set
     *  and we consider stuff set herein to be "set" by the client program.
     */
    if ((opts->fOptSet & OPTPROC_PRESETTING) == 0)
        st_flags = OPTST_SET;

    do  {
        optst.flags = st_flags;
        ftext = SPN_WHITESPACE_CHARS(ftext);

        if (IS_VAR_FIRST_CHAR(*ftext)) {
            ftext = handle_cfg(opts, &optst, ftext, dir);

        } else switch (*ftext) {
        case '<':
            if (IS_VAR_FIRST_CHAR(ftext[1]))
                ftext = handle_struct(opts, &optst, ftext, dir);

            else switch (ftext[1]) {
            case '?':
                ftext = handle_directive(opts, ftext);
                break;

            case '!':
                ftext = handle_comment(ftext);
                break;

            case '/':
                ftext = strchr(ftext + 2, '>');
                if (ftext++ != NULL)
                    break;

            default:
                ftext = NULL;
            }
            if (ftext == NULL)
                goto all_done;
            break;

        case '[':
            ftext = handle_section(opts, ftext);
            break;

        case '#':
            ftext = strchr(ftext + 1, NL);
            break;

        default:
            goto all_done; /* invalid format */
        }
    } while (ftext != NULL);

 all_done:
    text_munmap(&cfgfile);
}
Ejemplo n.º 25
0
/**
 *  Parse the various attributes of an XML-styled config file entry
 *
 * @returns NULL on failure, otherwise the scan point
 */
LOCAL char const *
parse_attrs(tOptions * opts, char const * txt, tOptionLoadMode * pMode,
            tOptionValue * pType)
{
    size_t len = 0;

    for (;;) {
        len = (size_t)(SPN_LOWER_CASE_CHARS(txt) - txt);

        /*
         * The enumeration used in this switch is derived from this switch
         * statement itself.  The "find_option_xat_attribute_cmd" function
         * will return XAT_CMD_MEMBERS for the "txt" string value
         * "members", etc.
         */
        switch (find_option_xat_attribute_cmd(txt, len)) {
        case XAT_CMD_TYPE:
            txt = parse_value(txt+len, pType);
            break;

        case XAT_CMD_WORDS:
            txt = parse_keyword(opts, txt+len, pType);
            break;

        case XAT_CMD_MEMBERS:
            txt = parse_set_mem(opts, txt+len, pType);
            break;

        case XAT_CMD_COOKED:
            txt += len;
            if (! IS_END_XML_TOKEN_CHAR(*txt))
                goto invalid_kwd;

            *pMode = OPTION_LOAD_COOKED;
            break;

        case XAT_CMD_UNCOOKED:
            txt += len;
            if (! IS_END_XML_TOKEN_CHAR(*txt))
                goto invalid_kwd;

            *pMode = OPTION_LOAD_UNCOOKED;
            break;

        case XAT_CMD_KEEP:
            txt += len;
            if (! IS_END_XML_TOKEN_CHAR(*txt))
                goto invalid_kwd;

            *pMode = OPTION_LOAD_KEEP;
            break;

        default:
        case XAT_INVALID_CMD:
        invalid_kwd:
            pType->valType = OPARG_TYPE_NONE;
            return skip_unkn(txt);
        }

        if (txt == NULL)
            return NULL;
        txt = SPN_WHITESPACE_CHARS(txt);
        switch (*txt) {
            case '/': pType->valType = OPARG_TYPE_NONE;
                      /* FALLTHROUGH */
            case '>': return txt;
        }
        if (! IS_LOWER_CASE_CHAR(*txt))
            return NULL;
    }
}
Ejemplo n.º 26
0
/*=
 * private:
 *
 * what:  parse a hierarchical option argument
 * arg:   + char const * + pzTxt  + the text to scan      +
 * arg:   + char const * + pzName + the name for the text +
 * arg:   + size_t       + nm_len + the length of "name"  +
 *
 * ret_type:  tOptionValue *
 * ret_desc:  An allocated, compound value structure
 *
 * doc:
 *  A block of text represents a series of values.  It may be an
 *  entire configuration file, or it may be an argument to an
 *  option that takes a hierarchical value.
 *
 *  If NULL is returned, errno will be set:
 *  @itemize @bullet
 *  @item
 *  @code{EINVAL} the input text was NULL.
 *  @item
 *  @code{ENOMEM} the storage structures could not be allocated
 *  @item
 *  @code{ENOMSG} no configuration values were found
 *  @end itemize
=*/
LOCAL tOptionValue *
optionLoadNested(char const * text, char const * name, size_t nm_len)
{
    tOptionValue * res_val;

    /*
     *  Make sure we have some data and we have space to put what we find.
     */
    if (text == NULL) {
        errno = EINVAL;
        return NULL;
    }
    text = SPN_WHITESPACE_CHARS(text);
    if (*text == NUL) {
        errno = ENOMSG;
        return NULL;
    }
    res_val = AGALOC(sizeof(*res_val) + nm_len + 1, "nest args");
    res_val->valType = OPARG_TYPE_HIERARCHY;
    res_val->pzName  = (char *)(res_val + 1);
    memcpy(res_val->pzName, name, nm_len);
    res_val->pzName[nm_len] = NUL;

    {
        tArgList * arg_list = AGALOC(sizeof(*arg_list), "nest arg l");

        res_val->v.nestVal = arg_list;
        arg_list->useCt   = 0;
        arg_list->allocCt = MIN_ARG_ALLOC_CT;
    }

    /*
     *  Scan until we hit a NUL.
     */
    do  {
        text = SPN_WHITESPACE_CHARS(text);
        if (IS_VAR_FIRST_CHAR(*text))
            text = scan_name(text, res_val);

        else switch (*text) {
        case NUL: goto scan_done;
        case '<': text = scan_xml(text, res_val);
                  if (text == NULL) goto woops;
                  if (*text == ',') text++; break;
        case '#': text = strchr(text, NL);  break;
        default:  goto woops;
        }
    } while (text != NULL); scan_done:;

    {
        tArgList * al = res_val->v.nestVal;
        if (al->useCt == 0) {
            errno = ENOMSG;
            goto woops;
        }
        if (al->useCt > 1)
            sort_list(al);
    }

    return res_val;

 woops:
    AGFREE(res_val->v.nestVal);
    AGFREE(res_val);
    return NULL;
}
Ejemplo n.º 27
0
/*=export_func  optionSetMembers
 * what:  Convert between bit flag values and strings
 * private:
 *
 * arg:   tOptions *,     opts,     the program options descriptor
 * arg:   tOptDesc *,     od,       the set membership option description
 * arg:   char const * const *,
 *                       nm_list,  list of enumeration names
 * arg:   unsigned int,  nm_ct,    number of names in list
 *
 * doc:   This converts the optArg.argString string from the option description
 *        into the index corresponding to an entry in the name list.
 *        This will match the generated enumeration value.
 *        Full matches are always accepted.  Partial matches are accepted
 *        if there is only one partial match.
=*/
void
optionSetMembers(tOptions * opts, tOptDesc * od,
                 char const * const * nm_list, unsigned int nm_ct)
{
    /*
     *  IF the program option descriptor pointer is invalid,
     *  then it is some sort of special request.
     */
    switch ((uintptr_t)opts) {
    case (uintptr_t)OPTPROC_EMIT_USAGE:
        enum_err(OPTPROC_EMIT_USAGE, od, nm_list, nm_ct);
        return;

    case (uintptr_t)OPTPROC_EMIT_SHELL:
        set_memb_shell(opts, od, nm_list, nm_ct);
        return;

    case (uintptr_t)OPTPROC_RETURN_VALNAME:
        set_memb_names(opts, od, nm_list, nm_ct);
        return;

    default:
        break;
    }

    if ((od->fOptState & OPTST_RESET) != 0)
        return;

    {
        char const * arg;
        bool         invert;
        uintptr_t    res = check_membership_start(od, &arg, &invert);
        if (arg == NULL)
            goto fail_return;

        while (*arg != NUL) {
            bool inv_val = false;
            int  len;

            switch (*arg) {
            case ',':
                arg = SPN_WHITESPACE_CHARS(arg+1);
                if ((*arg == ',') || (*arg == '|'))
                    goto fail_return;
                continue;

            case '-':
            case '!':
                inv_val = true;
                /* FALLTHROUGH */

            case '+':
            case '|':
                arg = SPN_WHITESPACE_CHARS(arg+1);
            }

            len = (int)(BRK_SET_SEPARATOR_CHARS(arg) - arg);
            if (len == 0)
                break;

            if ((len == 3) && (strncmp(arg, zAll, 3) == 0)) {
                if (inv_val)
                     res = 0;
                else res = ~0UL;
            }
            else if ((len == 4) && (strncmp(arg, zNone, 4) == 0)) {
                if (! inv_val)
                    res = 0;
            }
            else do {
                char *    pz;
                uintptr_t bit = strtoul(arg, &pz, 0);

                if (pz != arg + len) {
                    bit = find_member_bit(opts, od, pz, len, nm_list, nm_ct);
                    if (bit == 0UL)
                        goto fail_return;
                }
                if (inv_val)
                     res &= ~bit;
                else res |= bit;
            } while (false);

            arg = SPN_WHITESPACE_CHARS(arg + len);
        }

        if (invert)
            res ^= ~0UL;

        if (nm_ct < (8 * sizeof(uintptr_t)))
            res &= (1UL << nm_ct) - 1UL;

        od->optCookie = VOIDP(res);
    }
    return;

fail_return:
    od->optCookie = VOIDP(0);
}
Ejemplo n.º 28
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;
}
Ejemplo n.º 29
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;
}
Ejemplo n.º 30
0
/**
 *  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.
 */
char *
doDir_include(directive_enum_t id, char const * dir, char * scan_next)
{
    static char const * const apzSfx[] = { DIRECT_INC_DEF_SFX, NULL };
    scan_ctx_t * new_ctx;
    size_t     inc_sz;
    char       full_name[ AG_PATH_MAX + 1 ];
    (void)id;

    dir = SPN_WHITESPACE_CHARS(dir);
    /*
     *  Ignore C-style includes.  This allows "C" files to be processed
     *  for their "#define"s.
     */
    if ((*dir == '"') || (*dir == '<'))
        return scan_next;

    if (! SUCCESSFUL(
            find_file(dir, full_name, apzSfx, cctx->scx_fname))) {
        errno = ENOENT;
        fswarn("search for", cctx->scx_fname);
        return scan_next;
    }

    /*
     *  Make sure the specified file is a regular file and we can get
     *  the correct size for it.
     */
    inc_sz = file_size(full_name);
    if (inc_sz == 0)
        return scan_next;

    /*
     *  Get the space for the output data and for context overhead.
     *  This is an extra allocation and copy, but easier than rewriting
     *  'loadData()' for this special context.
     */
    {
        size_t sz = sizeof(scan_ctx_t) + 4 + inc_sz;
        new_ctx = (scan_ctx_t *)AGALOC(sz, "inc def head");

        memset(VOIDP(new_ctx), 0, sz);
        new_ctx->scx_line = 1;
    }

    /*
     *  Link it into the context stack
     */
    cctx->scx_scan     = scan_next;
    new_ctx->scx_next  = cctx;
    cctx               = new_ctx;
    AGDUPSTR(new_ctx->scx_fname, full_name, "def file");

    new_ctx->scx_scan  =
    new_ctx->scx_data  =
    scan_next          = (char *)(new_ctx + 1);

    /*
     *  Read all the data.  Usually in a single read, but loop
     *  in case multiple passes are required.
     */
    {
        FILE * fp = fopen(full_name, "r" FOPEN_TEXT_FLAG);
        char * pz = scan_next;

        if (fp == NULL)
            AG_CANT(DIRECT_INC_CANNOT_OPEN, full_name);

        if (dep_fp != NULL)
            add_source_file(full_name);

        do  {
            size_t rdct = fread(VOIDP(pz), (size_t)1, inc_sz, fp);

            if (rdct == 0)
                AG_CANT(DIRECT_INC_CANNOT_READ, full_name);

            pz += rdct;
            inc_sz -= rdct;
        } while (inc_sz > 0);

        fclose(fp);
        *pz = NUL;
    }

    return scan_next;
}