static teDirectives findDirective(char* pzDirName) { teDirectives res = (teDirectives)0; const tDirTable * pTbl = dirTable; do { if (strneqvcmp(pzDirName, pTbl->pzDirName, pTbl->nameSize) != 0) continue; /* * A directive name ends with either white space or a NUL */ if (IS_END_TOKEN_CHAR(pzDirName[ pTbl->nameSize ])) return res; } while (pTbl++, ++res < DIRECTIVE_CT); { char ch; if (strlen(pzDirName) > 32) { ch = pzDirName[32]; pzDirName[32] = NUL; } else { ch = NUL; } fprintf(pfTrace, "WARNING: in %s on line %d unknown directive:\n" "\t#%s\n", pCurCtx->pzCtxFname, pCurCtx->lineNo, pzDirName); if (ch != NUL) pzDirName[32] = ch; } return res; }
/*=gfunc string_starts_eqv_p * * what: caseless string start * general_use: * * exparg: text, text to test for pattern * exparg: match, pattern/substring to search for * * string: "=*" * * doc: Test to see if a string starts with an equivalent string. =*/ static tSuccess Select_Equivalent_Start(char const * sample, char const * pattern) { size_t vlen = strlen(pattern); return (strneqvcmp(sample, pattern, (int)vlen) == 0) ? SUCCESS : FAILURE; }
/** * 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; } } }
/** * print out the options that match the given name. * * @param pOpts option data * @param opt_name name of option to look for */ static void opt_ambiguities(tOptions * opts, char const * name, int nm_len) { char const * const hyph = NAMED_OPTS(opts) ? "" : LONG_OPT_MARKER; tOptDesc * pOD = opts->pOptDesc; int idx = 0; fputs(zambig_list_msg, stderr); do { if (pOD->pz_Name == NULL) continue; /* doc option */ if (strneqvcmp(name, pOD->pz_Name, nm_len) == 0) fprintf(stderr, zambig_file, hyph, pOD->pz_Name); else if ( (pOD->pz_DisableName != NULL) && (strneqvcmp(name, pOD->pz_DisableName, nm_len) == 0) ) fprintf(stderr, zambig_file, hyph, pOD->pz_DisableName); } while (pOD++, (++idx < opts->optCt)); }
/* * set_first_idx * * Go through all our different kinds of defines. On the first occurrence * of each different name, check for an index value. If not supplied, * then insert ``[OPT_VALUE_FIRST_INDEX]'' after the object name. */ static void set_first_idx(void) { char zNm[ 128 ] = { NUL }; int nmLn = 1; int ct = blkUseCt; char** ppz = papzBlocks; if (ct == 0) exit(EXIT_FAILURE); for (; --ct >= 0; ppz++) { char * pzOld = *ppz; int changed = (strneqvcmp(pzOld, zNm, nmLn) != 0); char * pzNew; /* * IF the name still matches, then check the following character. * If it is whitespace or an open bracket, then * it's the old type. Continue to the next entry. */ if (! changed) { if (isspace(pzOld[ nmLn ]) || (pzOld[nmLn] == '[')) continue; } pzNew = zNm; nmLn = 0; while (isalnum(*pzOld) || (*pzOld == '_') || (*pzOld == '-') || (*pzOld == '^')) { nmLn++; *(pzNew++) = *(pzOld++); } *pzNew = NUL; /* * IF the source has specified its own index, then do not * supply our own new one. */ if (*pzOld != '[') { pzNew = (char*)malloc(strlen(pzOld) + nmLn + 10); sprintf(pzNew, "%s[%d]%s", zNm, (int)OPT_VALUE_FIRST_INDEX, pzOld); free((void*)(*ppz)); *ppz = pzNew; } } }
/* 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) || (! isspace( (int)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 (isspace((int)*pzText)) { while (isspace((int)*pzText)) 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; }
/* * eval_true - should a string be interpreted as TRUE? * * It is always true unless: * * 1. it is the empty string * 2. it starts with a digit and the number evaluates to zero * 3. it starts with either "#f" or "#F" * 4. For its length or its first five characters (whichever is less) * it matches the string "false" */ static ag_bool eval_true(void) { ag_bool needFree; ag_bool res = AG_TRUE; char const * pz = evalExpression(&needFree); if (IS_DEC_DIGIT_CHAR(*pz)) res = (atoi(pz) == 0) ? AG_FALSE : AG_TRUE; else switch (*pz) { case NUL: res = AG_FALSE; break; case '#': if ((pz[1] == 'f') || (pz[1] == 'F')) res = AG_FALSE; break; case 'f': case 'F': { int len = strlen(pz); if (len > 5) len = 5; if (strneqvcmp(EVAL_TRUE_FALSE_STR, pz, len) == 0) res = AG_FALSE; break; } } if (needFree) AGFREE(pz); return res; }
/** * 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; }
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * HUNT FOR OPTIONS IN THE ARGUMENT LIST * * The next four procedures are "private" to nextOption(). * nextOption() uses findOptDesc() to find the next descriptor and it, in * turn, uses longOptionFind() and shortOptionFind() to actually do the hunt. * * longOptionFind * * Find the long option descriptor for the current option */ LOCAL tSuccess longOptionFind(tOptions* pOpts, char* pzOptName, tOptState* pOptState) { ag_bool disable = AG_FALSE; char* pzEq = strchr(pzOptName, '='); tOptDesc* pOD = pOpts->pOptDesc; int idx = 0; int idxLim = pOpts->optCt; int matchCt = 0; int matchIdx = 0; int nameLen; char opt_name_buf[128]; /* * IF the value is attached to the name, * copy it off so we can NUL terminate. */ if (pzEq != NULL) { nameLen = (int)(pzEq - pzOptName); if (nameLen >= sizeof(opt_name_buf)) return FAILURE; memcpy(opt_name_buf, pzOptName, nameLen); opt_name_buf[nameLen] = NUL; pzOptName = opt_name_buf; pzEq++; } else nameLen = strlen(pzOptName); do { /* * If option disabled or a doc option, skip to next */ if (pOD->pz_Name == NULL) continue; if ( SKIP_OPT(pOD) && (pOD->fOptState != (OPTST_OMITTED | OPTST_NO_INIT))) continue; if (strneqvcmp(pzOptName, pOD->pz_Name, nameLen) == 0) { /* * IF we have a complete match * THEN it takes priority over any already located partial */ if (pOD->pz_Name[ nameLen ] == NUL) { matchCt = 1; matchIdx = idx; break; } } /* * IF there is a disable name * *AND* no argument value has been supplied * (disabled options may have no argument) * *AND* the option name matches the disable name * THEN ... */ else if ( (pOD->pz_DisableName != NULL) && (strneqvcmp(pzOptName, pOD->pz_DisableName, nameLen) == 0) ) { disable = AG_TRUE; /* * IF we have a complete match * THEN it takes priority over any already located partial */ if (pOD->pz_DisableName[ nameLen ] == NUL) { matchCt = 1; matchIdx = idx; break; } } else continue; /* * We found a partial match, either regular or disabling. * Remember the index for later. */ matchIdx = idx; if (++matchCt > 1) break; } while (pOD++, (++idx < idxLim)); /* * Make sure we either found an exact match or found only one partial */ if (matchCt == 1) { pOD = pOpts->pOptDesc + matchIdx; if (SKIP_OPT(pOD)) { fprintf(stderr, zDisabledErr, pOpts->pzProgName, pOD->pz_Name); if (pOD->pzText != NULL) fprintf(stderr, " -- %s", pOD->pzText); fputc('\n', stderr); (*pOpts->pUsageProc)(pOpts, EXIT_FAILURE); /* NOTREACHED */ } /* * IF we found a disablement name, * THEN set the bit in the callers' flag word */ if (disable) pOptState->flags |= OPTST_DISABLED; pOptState->pOD = pOD; pOptState->pzOptArg = pzEq; pOptState->optType = TOPT_LONG; return SUCCESS; } /* * IF there is no equal sign * *AND* we are using named arguments * *AND* there is a default named option, * THEN return that option. */ if ( (pzEq == NULL) && NAMED_OPTS(pOpts) && (pOpts->specOptIdx.default_opt != NO_EQUIVALENT)) { pOptState->pOD = pOpts->pOptDesc + pOpts->specOptIdx.default_opt; pOptState->pzOptArg = pzOptName; pOptState->optType = TOPT_DEFAULT; return SUCCESS; } /* * IF we are to stop on errors (the default, actually) * THEN call the usage procedure. */ if ((pOpts->fOptSet & OPTPROC_ERRSTOP) != 0) { fprintf(stderr, (matchCt == 0) ? zIllOptStr : zAmbigOptStr, pOpts->pzProgPath, pzOptName); (*pOpts->pUsageProc)(pOpts, EXIT_FAILURE); } return FAILURE; }
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++; } } }
/** * The template output goes to stdout. Perhaps because output * is for a CGI script. In any case, this case must be handled * specially. */ static void do_stdout_tpl(tTemplate * pTF) { char const * pzRes; SCM res; pzLastScheme = NULL; /* We cannot be in Scheme processing */ switch (setjmp (fileAbort)) { case SUCCESS: break; case PROBLEM: if (*pzOopsPrefix != NUL) { fprintf(stdout, DO_STDOUT_TPL_ABANDONED, pzOopsPrefix); pzOopsPrefix = zNil; } fclose(stdout); return; default: fprintf(stdout, DO_STDOUT_TPL_BADR, pzOopsPrefix); case FAILURE: exit(EXIT_FAILURE); } pzCurSfx = DO_STDOUT_TPL_NOSFX; currDefCtx = rootDefCtx; pCurFp = &fpRoot; fpRoot.pFile = stdout; fpRoot.pzOutName = DO_STDOUT_TPL_STDOUT; fpRoot.flags = FPF_NOUNLINK | FPF_STATIC_NM; if (OPT_VALUE_TRACE >= TRACE_EVERYTHING) fputs(DO_STDOUT_TPL_START_STD, pfTrace); /* * IF there is a CGI prefix for error messages, * THEN divert all output to a temporary file so that * the output will be clean for any error messages we have to emit. */ if (*pzOopsPrefix == NUL) generateBlock(pTF, pTF->aMacros, pTF->aMacros + pTF->macroCt); else { (void)ag_scm_out_push_new(SCM_UNDEFINED); generateBlock(pTF, pTF->aMacros, pTF->aMacros + pTF->macroCt); /* * Read back in the spooled output. Make sure it starts with * a content-type: prefix. If not, we supply our own HTML prefix. */ res = ag_scm_out_pop(SCM_BOOL_T); pzRes = AG_SCM_CHARS(res); /* 13: "content-type:" */ if (strneqvcmp(pzRes, DO_STDOUT_TPL_CONTENT, 13) != 0) fputs(DO_STDOUT_TPL_CONTENT, stdout); fwrite(pzRes, AG_SCM_STRLEN(res), (size_t)1, stdout); } fclose(stdout); }
/** * Determine the number of options that match the name * * @param pOpts option data * @param opt_name name of option to look for * @param nm_len length of provided name * @param index pointer to int for option index * @param disable pointer to bool to mark disabled option * @return count of options that match */ static int opt_match_ct(tOptions * opts, char const * name, int nm_len, int * ixp, bool * disable) { int matchCt = 0; int idx = 0; int idxLim = opts->optCt; tOptDesc * pOD = opts->pOptDesc; do { /* * If option disabled or a doc option, skip to next */ if (pOD->pz_Name == NULL) continue; if ( SKIP_OPT(pOD) && (pOD->fOptState != (OPTST_OMITTED | OPTST_NO_INIT))) continue; if (strneqvcmp(name, pOD->pz_Name, nm_len) == 0) { /* * IF we have a complete match * THEN it takes priority over any already located partial */ if (pOD->pz_Name[ nm_len ] == NUL) { *ixp = idx; return 1; } } /* * IF there is a disable name * *AND* the option name matches the disable name * THEN ... */ else if ( (pOD->pz_DisableName != NULL) && (strneqvcmp(name, pOD->pz_DisableName, nm_len) == 0) ) { *disable = true; /* * IF we have a complete match * THEN it takes priority over any already located partial */ if (pOD->pz_DisableName[ nm_len ] == NUL) { *ixp = idx; return 1; } } else continue; /* does not match any option */ /* * We found a full or partial match, either regular or disabling. * Remember the index for later. */ *ixp = idx; ++matchCt; } while (pOD++, (++idx < idxLim)); return matchCt; }