/** * AutoGen specific wrapper function for gettext. It relies on the macro _() * to convert from English to the target language, then strdup-duplicates the * result string. It tries the "libopts" domain first, then whatever has been * set via the \a textdomain(3) call. * * @param[in] pz the input text used as a lookup key. * @returns the translated text (if there is one), * or the original text (if not). */ static char * AO_gettext(char const * pz) { char * res; if (pz == NULL) return NULL; #ifdef HAVE_DCGETTEXT /* * While processing the option_xlateable_txt data, try to use the * "libopts" domain. Once we switch to the option descriptor data, * do *not* use that domain. */ if (option_xlateable_txt.field_ct != 0) { res = dgettext("libopts", pz); if (res == pz) res = (char *)VOIDP(_(pz)); } else res = (char *)VOIDP(_(pz)); #else res = (char *)VOIDP(_(pz)); #endif if (res == pz) return res; res = strdup(res); if (res == NULL) { fputs(_("No memory for duping translated strings\n"), stderr); exit(NTPQ_EXIT_FAILURE); } return res; }
/** * This is a _stable_ sort. The entries are sorted alphabetically, * but within entries of the same name the ordering is unchanged. * Typically, we also hope the input is sorted. */ static void sort_list(tArgList * arg_list) { int ix; int lm = arg_list->useCt; /* * This loop iterates "useCt" - 1 times. */ for (ix = 0; ++ix < lm;) { int iy = ix-1; tOptionValue * new_v = C(tOptionValue *, arg_list->apzArgs[ix]); tOptionValue * old_v = C(tOptionValue *, arg_list->apzArgs[iy]); /* * For as long as the new entry precedes the "old" entry, * move the old pointer. Stop before trying to extract the * "-1" entry. */ while (strcmp(old_v->pzName, new_v->pzName) > 0) { arg_list->apzArgs[iy+1] = VOIDP(old_v); old_v = (tOptionValue *)VOIDP(arg_list->apzArgs[--iy]); if (iy < 0) break; } /* * Always store the pointer. Sometimes it is redundant, * but the redundancy is cheaper than a test and branch sequence. */ arg_list->apzArgs[iy+1] = VOIDP(new_v); } }
/*=export_func optionNestedVal * private: * * what: parse a hierarchical option argument * arg: + tOptions * + opts + program options descriptor + * arg: + tOptDesc * + od + the descriptor for this arg + * * doc: * Nested value was found on the command line =*/ void optionNestedVal(tOptions * opts, tOptDesc * od) { if (opts < OPTPROC_EMIT_LIMIT) return; if (od->fOptState & OPTST_RESET) { tArgList * arg_list = od->optCookie; int ct; char const ** av; if (arg_list == NULL) return; ct = arg_list->useCt; av = arg_list->apzArgs; while (--ct >= 0) { void * p = VOIDP(*(av++)); optionUnloadNested((tOptionValue const *)p); } AGFREE(od->optCookie); } else { tOptionValue * opt_val = optionLoadNested( od->optArg.argString, od->pz_Name, strlen(od->pz_Name)); if (opt_val != NULL) addArgListEntry(&(od->optCookie), VOIDP(opt_val)); } }
/*=export_func optionNextValue * * what: get the next value from a hierarchical list * arg: + const tOptionValue * + pOptValue + a hierarchcal list value + * arg: + const tOptionValue * + pOldValue + a value from this list + * * ret_type: const tOptionValue * * ret_desc: a compound value structure * * doc: * This routine will return the next entry after the entry passed in. At the * end of the list, NULL will be returned. If the entry is not found on the * list, NULL will be returned and "@var{errno}" will be set to EINVAL. * The "@var{pOldValue}" must have been gotten from a prior call to this * routine or to "@code{opitonGetValue()}". * * err: * The returned result is NULL and errno is set: * @itemize @bullet * @item * @code{EINVAL} - the @code{pOptValue} does not point to a valid * hierarchical option value or @code{pOldValue} does not point to a * member of that option value. * @item * @code{ENOENT} - the supplied @code{pOldValue} pointed to the last entry. * @end itemize =*/ tOptionValue const * optionNextValue(tOptionValue const * ov_list,tOptionValue const * oov ) { tArgList * arg_list; const tOptionValue * res = NULL; int err = EINVAL; if ((ov_list == NULL) || (ov_list->valType != OPARG_TYPE_HIERARCHY)) { errno = EINVAL; return NULL; } arg_list = ov_list->v.nestVal; { int ct = arg_list->useCt; const void ** o_list = VOIDP(arg_list->apzArgs); while (ct-- > 0) { const tOptionValue * nov = *(o_list++); if (nov == oov) { if (ct == 0) { err = ENOENT; } else { err = 0; res = (const tOptionValue *)*o_list; } break; } } } if (err != 0) errno = err; return res; }
/*=export_func optionGetValue * * what: get a specific value from a hierarcical list * arg: + const tOptionValue * + pOptValue + a hierarchcal value + * arg: + char const * + valueName + name of value to get + * * ret_type: const tOptionValue * * ret_desc: a compound value structure * * doc: * This routine will find an entry in a nested value option or configurable. * If "valueName" is NULL, then the first entry is returned. Otherwise, * the first entry with a name that exactly matches the argument will be * returned. If there is no matching value, NULL is returned and errno is * set to ENOENT. If the provided option value is not a hierarchical value, * NULL is also returned and errno is set to EINVAL. * * err: * The returned result is NULL and errno is set: * @itemize @bullet * @item * @code{EINVAL} - the @code{pOptValue} does not point to a valid * hierarchical option value. * @item * @code{ENOENT} - no entry matched the given name. * @end itemize =*/ tOptionValue const * optionGetValue(tOptionValue const * oov, char const * vname) { tArgList * arg_list; const tOptionValue * res = NULL; if ((oov == NULL) || (oov->valType != OPARG_TYPE_HIERARCHY)) { errno = EINVAL; return res; } arg_list = oov->v.nestVal; if (arg_list->useCt > 0) { int ct = arg_list->useCt; const void ** ovlist = VOIDP(arg_list->apzArgs); if (vname == NULL) { res = (const tOptionValue *)*ovlist; } else do { const tOptionValue * opt_val = *(ovlist++); if (strcmp(opt_val->pzName, vname) == 0) { res = opt_val; break; } } while (--ct > 0); } if (res == NULL) errno = ENOENT; return res; }
static void print_membership(tOptions * pOpts, tOptDesc * pOD) { char const * svstr = pOD->optArg.argString; char const * pz; uintptr_t val = 1; printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME, (int)(uintptr_t)(pOD->optCookie)); pOD->optCookie = VOIDP(~0UL); (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD); pz = pOD->optArg.argString; while (*pz != NUL) { printf("readonly %s_", pOD->pz_NAME); pz = SPN_PLUS_N_SPACE_CHARS(pz); for (;;) { int ch = *(pz++); if (IS_LOWER_CASE_CHAR(ch)) fputc(toupper(ch), stdout); else if (IS_UPPER_CASE_CHAR(ch)) fputc(ch, stdout); else if (IS_PLUS_N_SPACE_CHAR(ch)) goto name_done; else if (ch == NUL) { pz--; goto name_done; } else fputc('_', stdout); } name_done:; printf(SHOW_VAL_FMT, (unsigned long)val); val <<= 1; } AGFREE(pOD->optArg.argString); pOD->optArg.argString = svstr; }
/** * Convert a name or number into a binary number. * "~0" and "-1" will be converted to the largest value in the enumeration. * * @param name the keyword name (number) to convert * @param pOpts the program's option descriptor * @param pOD the option descriptor for this option * @param paz_names the list of keywords for this option * @param name_ct the count of keywords */ static uintptr_t find_name(char const * name, tOptions * pOpts, tOptDesc * pOD, char const * const * paz_names, unsigned int name_ct) { /* * Return the matching index as a pointer sized integer. * The result gets stashed in a char * pointer. */ uintptr_t res = name_ct; size_t len = strlen((char *)name); uintptr_t idx; if (IS_DEC_DIGIT_CHAR(*name)) { char * pz = VOIDP(name); unsigned long val = strtoul(pz, &pz, 0); if ((*pz == NUL) && (val < name_ct)) return (uintptr_t)val; pz_enum_err_fmt = znum_too_large; option_usage_fp = stderr; enum_err(pOpts, pOD, paz_names, (int)name_ct); return name_ct; } if (IS_INVERSION_CHAR(*name) && (name[2] == NUL)) { if ( ((name[0] == '~') && (name[1] == '0')) || ((name[0] == '-') && (name[1] == '1'))) return (uintptr_t)(name_ct - 1); goto oops; } /* * Look for an exact match, but remember any partial matches. * Multiple partial matches means we have an ambiguous match. */ for (idx = 0; idx < name_ct; idx++) { if (strncmp((char *)paz_names[idx], (char *)name, len) == 0) { if (paz_names[idx][len] == NUL) return idx; /* full match */ if (res == name_ct) res = idx; /* save partial match */ else res = (uintptr_t)~0; /* may yet find full match */ } } if (res < name_ct) return res; /* partial match */ oops: pz_enum_err_fmt = (res == name_ct) ? zNoKey : zambiguous_key; option_usage_fp = stderr; enum_err(pOpts, pOD, paz_names, (int)name_ct); return name_ct; }
/*=export_func optionMemberList * what: Get the list of members of a bit mask set * * arg: tOptDesc *, od, the set membership option description * * ret_type: char * * ret_desc: the names of the set bits * * doc: This converts the OPT_VALUE_name mask value to a allocated string. * It is the caller's responsibility to free the string. =*/ char * optionMemberList(tOptDesc * od) { uintptr_t sv = od->optArg.argIntptr; char * res; (*(od->pOptProc))(OPTPROC_RETURN_VALNAME, od); res = VOIDP(od->optArg.argString); od->optArg.argIntptr = sv; return res; }
/** * Deallocate a list of option arguments. This must have been gotten from * a hierarchical option argument, not a stacked list of strings. It is * an internal call, so it is not validated. The caller is responsible for * knowing what they are doing. */ LOCAL void unload_arg_list(tArgList * arg_list) { int ct = arg_list->useCt; char const ** pnew_val = arg_list->apzArgs; while (ct-- > 0) { tOptionValue * new_val = (tOptionValue *)VOIDP(*(pnew_val++)); if (new_val->valType == OPARG_TYPE_HIERARCHY) unload_arg_list(new_val->v.nestVal); AGFREE(new_val); } AGFREE(arg_list); }
/** * Open the specified file with open(2) and save the FD. * * @param pOpts program option descriptor * @param pOD the option descriptor * @param mode the open mode (uses int flags value) */ static void open_file_fd(tOptions * pOpts, tOptDesc * pOD, tuFileMode mode) { int fd = open(pOD->optArg.argString, mode.file_flags); if (fd < 0) fserr_exit(pOpts->pzProgName, "open", pOD->optArg.argString); /* NOTREACHED */ if ((pOD->fOptState & OPTST_ALLOC_ARG) != 0) pOD->optCookie = VOIDP(pOD->optArg.argString); else AGDUPSTR(pOD->optCookie, pOD->optArg.argString, "file name"); pOD->optArg.argFd = fd; pOD->fOptState &= ~OPTST_ALLOC_ARG; }
/** * Open the specified file with open(2) and save the FD. * * @param pOpts program option descriptor * @param pOD the option descriptor * @param mode the open mode (uses "char *" mode value) */ static void fopen_file_fp(tOptions * pOpts, tOptDesc * pOD, tuFileMode mode) { FILE * fp = fopen(pOD->optArg.argString, mode.file_mode); if (fp == NULL) fserr_exit(pOpts->pzProgName, "fopen", pOD->optArg.argString); /* NOTREACHED */ if ((pOD->fOptState & OPTST_ALLOC_ARG) != 0) pOD->optCookie = VOIDP(pOD->optArg.argString); else AGDUPSTR(pOD->optCookie, pOD->optArg.argString, "file name"); pOD->optArg.argFp = fp; pOD->fOptState &= ~OPTST_ALLOC_ARG; }
/*=export_func optionFindValue * * what: find a hierarcicaly valued option instance * arg: + const tOptDesc * + odesc + an option with a nested arg type + * arg: + char const * + name + name of value to find + * arg: + char const * + val + the matching value + * * ret_type: const tOptionValue * * ret_desc: a compound value structure * * doc: * This routine will find an entry in a nested value option or configurable. * It will search through the list and return a matching entry. * * err: * The returned result is NULL and errno is set: * @itemize @bullet * @item * @code{EINVAL} - the @code{pOptValue} does not point to a valid * hierarchical option value. * @item * @code{ENOENT} - no entry matched the given name. * @end itemize =*/ const tOptionValue * optionFindValue(const tOptDesc * odesc, char const * name, char const * val) { const tOptionValue * res = NULL; if ( (odesc == NULL) || (OPTST_GET_ARGTYPE(odesc->fOptState) != OPARG_TYPE_HIERARCHY)) { errno = EINVAL; } else if (odesc->optCookie == NULL) { errno = ENOENT; } else do { tArgList * argl = odesc->optCookie; int argct = argl->useCt; const void ** poptv = VOIDP(argl->apzArgs); if (argct == 0) { errno = ENOENT; break; } if (name == NULL) { res = (const tOptionValue *)*poptv; break; } while (--argct >= 0) { const tOptionValue * ov = *(poptv++); const tOptionValue * rv = optionGetValue(ov, name); if (rv == NULL) continue; if (val == NULL) { res = ov; break; } } if (res == NULL) errno = ENOENT; } while (false); return res; }
/** * Load the previous shell script output file. We need to preserve any * hand-edited additions outside of the START_MARK and END_MARKs. * * @param[in] fname the output file name */ static char * load_old_output(char const * fname, char const * pname) { /* * IF we cannot stat the file, * THEN assume we are creating a new file. * Skip the loading of the old data. */ FILE * fp = fopen(fname, "r" FOPEN_BINARY_FLAG); struct stat stbf; char * text; char * scan; if (fp == NULL) return NULL; /* * If we opened it, we should be able to stat it and it needs * to be a regular file */ if ((fstat(fileno(fp), &stbf) != 0) || (! S_ISREG(stbf.st_mode))) fserr_exit(pname, "fstat", fname); scan = text = AGALOC(stbf.st_size + 1, "f data"); /* * Read in all the data as fast as our OS will let us. */ for (;;) { size_t inct = fread(VOIDP(scan), 1, (size_t)stbf.st_size, fp); if (inct == 0) break; stbf.st_size -= (ssize_t)inct; if (stbf.st_size == 0) break; scan += inct; } *scan = NUL; fclose(fp); return text; }
/*=export_func optionFileLoad * * what: Load the locatable config files, in order * * arg: + tOptions * + opts + program options descriptor + * arg: + char const * + prog + program name + * * ret_type: int * ret_desc: 0 -> SUCCESS, -1 -> FAILURE * * doc: * * This function looks in all the specified directories for a configuration * file ("rc" file or "ini" file) and processes any found twice. The first * time through, they are processed in reverse order (last file first). At * that time, only "immediate action" configurables are processed. For * example, if the last named file specifies not processing any more * configuration files, then no more configuration files will be processed. * Such an option in the @strong{first} named directory will have no effect. * * Once the immediate action configurables have been handled, then the * directories are handled in normal, forward order. In that way, later * config files can override the settings of earlier config files. * * See the AutoOpts documentation for a thorough discussion of the * config file format. * * Configuration files not found or not decipherable are simply ignored. * * err: Returns the value, "-1" if the program options descriptor * is out of date or indecipherable. Otherwise, the value "0" will * always be returned. =*/ int optionFileLoad(tOptions * opts, char const * prog) { if (! SUCCESSFUL(validate_struct(opts, prog))) return -1; /* * The pointer to the program name is "const". However, the * structure is in writable memory, so we coerce the address * of this pointer to point to writable memory. */ { char const ** pp = VOIDP(&(opts->pzProgName)); *pp = prog; } intern_file_load(opts); return 0; }
/** * Translate all the translatable strings in the ntpqOptions * structure defined above. This is done only once. */ static void translate_option_strings(void) { tOptions * const opts = &ntpqOptions; /* * Guard against re-translation. It won't work. The strings will have * been changed by the first pass through this code. One shot only. */ if (option_xlateable_txt.field_ct != 0) { /* * Do the translations. The first pointer follows the field count * field. The field count field is the size of a pointer. */ char ** ppz = (char**)VOIDP(&(option_xlateable_txt)); int ix = option_xlateable_txt.field_ct; do { ppz++; /* skip over field_ct */ *ppz = AO_gettext(*ppz); } while (--ix > 0); /* prevent re-translation and disable "libopts" domain lookup */ option_xlateable_txt.field_ct = 0; coerce_it(VOIDP(&(opts->pzCopyright))); coerce_it(VOIDP(&(opts->pzCopyNotice))); coerce_it(VOIDP(&(opts->pzFullVersion))); coerce_it(VOIDP(&(opts->pzUsageTitle))); coerce_it(VOIDP(&(opts->pzExplain))); coerce_it(VOIDP(&(opts->pzDetail))); { tOptDesc * od = opts->pOptDesc; for (ix = opts->optCt; ix > 0; ix--, od++) coerce_it(VOIDP(&(od->pzText))); } } }
/*=export_func optionFindNextValue * * FIXME: the handling of 'pzName' and 'pzVal' is just wrong. * * what: find a hierarcicaly valued option instance * arg: + const tOptDesc * + odesc + an option with a nested arg type + * arg: + const tOptionValue * + pPrevVal + the last entry + * arg: + char const * + name + name of value to find + * arg: + char const * + value + the matching value + * * ret_type: const tOptionValue * * ret_desc: a compound value structure * * doc: * This routine will find the next entry in a nested value option or * configurable. It will search through the list and return the next entry * that matches the criteria. * * err: * The returned result is NULL and errno is set: * @itemize @bullet * @item * @code{EINVAL} - the @code{pOptValue} does not point to a valid * hierarchical option value. * @item * @code{ENOENT} - no entry matched the given name. * @end itemize =*/ tOptionValue const * optionFindNextValue(const tOptDesc * odesc, const tOptionValue * pPrevVal, char const * pzName, char const * pzVal) { bool old_found = false; const tOptionValue * res = NULL; (void)pzName; (void)pzVal; if ( (odesc == NULL) || (OPTST_GET_ARGTYPE(odesc->fOptState) != OPARG_TYPE_HIERARCHY)) { errno = EINVAL; } else if (odesc->optCookie == NULL) { errno = ENOENT; } else do { tArgList * argl = odesc->optCookie; int ct = argl->useCt; const void ** poptv = VOIDP(argl->apzArgs); while (--ct >= 0) { const tOptionValue * pOV = *(poptv++); if (old_found) { res = pOV; break; } if (pOV == pPrevVal) old_found = true; } if (res == NULL) errno = ENOENT; } while (false); return res; }
/* * doPrognameEnv - check for preset values from the ${PROGNAME} * environment variable. This is accomplished by parsing the text into * tokens, temporarily replacing the arg vector and calling * immediate_opts and/or regular_opts. */ LOCAL void doPrognameEnv(tOptions * pOpts, teEnvPresetType type) { char const * env_opts = getenv(pOpts->pzPROGNAME); token_list_t * pTL; int sv_argc; proc_state_mask_t sv_flag; char ** sv_argv; /* * No such beast? Then bail now. */ if (env_opts == NULL) return; /* * Tokenize the string. If there's nothing of interest, we'll bail * here immediately. */ pTL = ao_string_tokenize(env_opts); if (pTL == NULL) return; /* * Substitute our $PROGNAME argument list for the real one */ sv_argc = (int)pOpts->origArgCt; sv_argv = pOpts->origArgVect; sv_flag = pOpts->fOptSet; /* * We add a bogus pointer to the start of the list. The program name * has already been pulled from "argv", so it won't get dereferenced. * The option scanning code will skip the "program name" at the start * of this list of tokens, so we accommodate this way .... */ { uintptr_t v = (uintptr_t)(pTL->tkn_list); pOpts->origArgVect = VOIDP(v - sizeof(char *)); } pOpts->origArgCt = (unsigned int)pTL->tkn_ct + 1; pOpts->fOptSet &= ~OPTPROC_ERRSTOP; pOpts->curOptIdx = 1; pOpts->pzCurOpt = NULL; switch (type) { case ENV_IMM: (void)immediate_opts(pOpts); break; case ENV_ALL: (void)immediate_opts(pOpts); pOpts->curOptIdx = 1; pOpts->pzCurOpt = NULL; /* FALLTHROUGH */ case ENV_NON_IMM: (void)regular_opts(pOpts); } /* * Free up the temporary arg vector and restore the original program args. */ free(pTL); pOpts->origArgVect = sv_argv; pOpts->origArgCt = (unsigned int)sv_argc; pOpts->fOptSet = sv_flag; }
/** * Invokes @code{$SHELL} or @file{/bin/sh} on a script that should * generate AutoGen definitions. It does this using the same server * process that handles the back-quoted @code{`} text. * The block of text handed to the shell is terminated with * the #endshell directive. * * @strong{CAUTION}@: let not your @code{$SHELL} be @code{csh}. */ char * doDir_shell(directive_enum_t id, char const * arg, char * scan_next) { static size_t const endshell_len = sizeof("\n#endshell") - 1; scan_ctx_t * pCtx; char * pzText = scan_next; (void)arg; (void)id; /* * The output time will always be the current time. * The dynamic content is always current :) */ maxfile_time = outfile_time = time(NULL); /* * IF there are no data after the '#shell' directive, * THEN we won't write any data * ELSE we have to find the end of the data. */ if (strncmp(pzText, DIRECT_SHELL_END_SHELL+1, endshell_len-1) == 0) return scan_next; { char * pz = strstr(scan_next, DIRECT_SHELL_END_SHELL); if (pz == NULL) AG_ABEND(aprf(DIRECT_SHELL_NOEND, cctx->scx_fname, cctx->scx_line)); while (scan_next < pz) { if (*(scan_next++) == NL) cctx->scx_line++; } *scan_next = NUL; } /* * Advance the scan pointer to the next line after '#endshell' * IF there is no such line, * THEN the scan will resume on a zero-length string. */ scan_next = strchr(scan_next + endshell_len, NL); if (scan_next == NULL) scan_next = VOIDP(zNil); /* * Save the scan pointer into the current context */ cctx->scx_scan = scan_next; /* * Run the shell command. The output text becomes the * "file text" that is used for more definitions. */ pzText = shell_cmd(pzText); if (pzText == NULL) return scan_next; if (*pzText == NUL) { AGFREE(pzText); 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. */ pCtx = (scan_ctx_t *)AGALOC(sizeof(scan_ctx_t) + strlen(pzText) + 4, "shell output"); /* * Link the new scan data into the context stack */ pCtx->scx_next = cctx; cctx = pCtx; /* * Set up the rest of the context structure */ AGDUPSTR(pCtx->scx_fname, DIRECT_SHELL_COMP_DEFS, DIRECT_SHELL_COMP_DEFS); pCtx->scx_scan = pCtx->scx_data = (char *)(pCtx + 1); pCtx->scx_line = 0; strcpy(pCtx->scx_scan, pzText); AGFREE(pzText); return pCtx->scx_scan; }
/** * 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; }
/*=export_func genshelloptUsage * private: * what: The usage function for the genshellopt generated program * * arg: + tOptions * + opts + program options descriptor + * arg: + int + exit_cd + usage text type to produce + * * doc: * This function is used to create the usage strings for the option * processing shell script code. Two child processes are spawned * each emitting the usage text in either the short (error exit) * style or the long style. The generated program will capture this * and create shell script variables containing the two types of text. =*/ void genshelloptUsage(tOptions * opts, int exit_cd) { #if ! defined(HAVE_WORKING_FORK) optionUsage(opts, exit_cd); #else /* * IF not EXIT_SUCCESS, * THEN emit the short form of usage. */ if (exit_cd != EXIT_SUCCESS) optionUsage(opts, exit_cd); fflush(stderr); fflush(stdout); if (ferror(stdout) || ferror(stderr)) option_exits(EXIT_FAILURE); option_usage_fp = stdout; /* * First, print our usage */ switch (fork()) { case -1: optionUsage(opts, EXIT_FAILURE); /* NOTREACHED */ case 0: pagerState = PAGER_STATE_CHILD; optionUsage(opts, EXIT_SUCCESS); /* NOTREACHED */ _exit(EXIT_FAILURE); default: { int sts; wait(&sts); } } /* * Generate the pzProgName, since optionProcess() normally * gets it from the command line */ { char * pz; char ** pp = VOIDP(&(optionParseShellOptions->pzProgName)); AGDUPSTR(pz, optionParseShellOptions->pzPROGNAME, "prog name"); *pp = pz; while (*pz != NUL) { *pz = (char)LOWER(*pz); pz++; } } /* * Separate the makeshell usage from the client usage */ fprintf(option_usage_fp, zGenshell, optionParseShellOptions->pzProgName); fflush(option_usage_fp); /* * Now, print the client usage. */ switch (fork()) { case 0: pagerState = PAGER_STATE_CHILD; /*FALLTHROUGH*/ case -1: optionUsage(optionParseShellOptions, EXIT_FAILURE); default: { int sts; wait(&sts); } } fflush(stdout); if (ferror(stdout)) fserr_exit(opts->pzProgName, zwriting, zstdout_name); option_exits(EXIT_SUCCESS); #endif }
/** * capture usage text in shell variables. * */ static void emit_usage(tOptions * opts) { char tm_nm_buf[AO_NAME_SIZE]; /* * First, switch stdout to the output file name. * Then, change the program name to the one defined * by the definitions (rather than the current * executable name). Down case the upper cased name. */ if (script_leader != NULL) fputs(script_leader, stdout); { char const * out_nm; { time_t c_tim = time(NULL); struct tm * ptm = localtime(&c_tim); strftime(tm_nm_buf, AO_NAME_SIZE, TIME_FMT, ptm ); } if (HAVE_GENSHELL_OPT(SCRIPT)) out_nm = GENSHELL_OPT_ARG(SCRIPT); else out_nm = STDOUT; if ((script_leader == NULL) && (shell_prog != NULL)) printf(SHELL_MAGIC, shell_prog); printf(PREAMBLE_FMT, START_MARK, out_nm, tm_nm_buf); } printf(END_PRE_FMT, opts->pzPROGNAME); /* * Get a copy of the original program name in lower case and * fill in an approximation of the program name from it. */ { char * pzPN = tm_nm_buf; char const * pz = opts->pzPROGNAME; char ** pp; /* Copy the program name into the time/name buffer */ for (;;) { if ((*pzPN++ = (char)tolower((unsigned char)*pz++)) == NUL) break; } pp = VOIDP(&(opts->pzProgPath)); *pp = tm_nm_buf; pp = VOIDP(&(opts->pzProgName)); *pp = tm_nm_buf; } text_to_var(opts, TT_LONGUSAGE, NULL); text_to_var(opts, TT_USAGE, NULL); { tOptDesc * pOptDesc = opts->pOptDesc; int optionCt = opts->optCt; for (;;) { if (pOptDesc->pOptProc == optionPrintVersion) { text_to_var(opts, TT_VERSION, pOptDesc); break; } if (--optionCt <= 0) break; pOptDesc++; } } }
/*=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); }
/** * "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 = VOIDP(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; load_opt_line(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 "load_opt_line()". * 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. */ load_opt_line(opts, ost, pzName, dir, mode); return txt; }
/*=gfunc join * * what: join string list with separator * general_use: * exparg: separator, string to insert between entries * exparg: list, list of strings to join,, list * * doc: With the first argument as the separator string, * joins together an a-list of strings into one long string. * The list may contain nested lists, partly because you * cannot always control that. =*/ SCM ag_scm_join(SCM sep, SCM list) { int l_len, sv_l_len; SCM car; SCM alist = list; size_t sep_len; size_t str_len; char * pzRes; char const * pzSep; char * pzScan; if (! AG_SCM_STRING_P(sep)) return SCM_UNDEFINED; sv_l_len = l_len = (int)scm_ilength(list); if (l_len == 0) return AG_SCM_STR02SCM(zNil); pzSep = scm_i_string_chars(sep); sep_len = AG_SCM_STRLEN(sep); str_len = 0; /* * Count up the lengths of all the strings to be joined. */ for (;;) { car = SCM_CAR(list); list = SCM_CDR(list); /* * This routine is listed as getting a list as the second * argument. That means that if someone builds a list and * hands it to us, it magically becomes a nested list. * This unravels that. */ if (! AG_SCM_STRING_P(car)) { if (car != SCM_UNDEFINED) car = ag_scm_join(sep, car); if (! AG_SCM_STRING_P(car)) return SCM_UNDEFINED; } str_len += AG_SCM_STRLEN(car); if (--l_len <= 0) break; str_len += sep_len; } l_len = sv_l_len; pzRes = pzScan = scribble_get((ssize_t)str_len); /* * Now, copy each one into the output */ for (;;) { size_t cpy_len; car = SCM_CAR(alist); alist = SCM_CDR(alist); /* * This unravels nested lists. */ if (! AG_SCM_STRING_P(car)) car = ag_scm_join(sep, car); cpy_len = AG_SCM_STRLEN(car); memcpy(VOIDP(pzScan), scm_i_string_chars(car), cpy_len); pzScan += cpy_len; /* * IF we reach zero, then do not insert a separation and bail out */ if (--l_len <= 0) break; memcpy(VOIDP(pzScan), VOIDP(pzSep), sep_len); pzScan += sep_len; } return AG_SCM_STR2SCM(pzRes, str_len); }