/** * 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); } }
/*=export_func optionStackArg * private: * * what: put option args on a stack * arg: + tOptions* + opts + program options descriptor + * arg: + tOptDesc* + od + the descriptor for this arg + * * doc: * Keep an entry-ordered list of option arguments. =*/ void optionStackArg(tOptions * opts, tOptDesc * od) { char * pz; if (INQUERY_CALL(opts, od)) return; if ((od->fOptState & OPTST_RESET) != 0) { tArgList * arg_list = (void*)od->optCookie; int ix; if (arg_list == NULL) return; ix = arg_list->useCt; while (--ix >= 0) AGFREE(arg_list->apzArgs[ix]); AGFREE(arg_list); } else { if (od->optArg.argString == NULL) return; AGDUPSTR(pz, od->optArg.argString, "stack arg"); addArgListEntry(&(od->optCookie), (void*)pz); } }
/* * optionFixupSavedOpts Really, it just wipes out option state for * options that are troublesome to copy. viz., stacked strings and * hierarcicaly valued option args. We do duplicate string args that * have been marked as allocated though. */ static void fixupSavedOptionArgs(tOptions* pOpts) { tOptions* p = pOpts->pSavedState; tOptDesc* pOD = pOpts->pOptDesc; int ct = pOpts->optCt; /* * Make sure that allocated stuff is only referenced in the * archived copy of the data. */ for (; ct-- > 0; pOD++) { switch (OPTST_GET_ARGTYPE(pOD->fOptState)) { case OPARG_TYPE_STRING: if (pOD->fOptState & OPTST_STACKED) { tOptDesc* q = p->pOptDesc + (pOD - pOpts->pOptDesc); q->optCookie = NULL; } if (pOD->fOptState & OPTST_ALLOC_ARG) { tOptDesc* q = p->pOptDesc + (pOD - pOpts->pOptDesc); AGDUPSTR(q->optArg.argString, pOD->optArg.argString, "arg"); } break; case OPARG_TYPE_HIERARCHY: { tOptDesc* q = p->pOptDesc + (pOD - pOpts->pOptDesc); q->optCookie = NULL; } } } }
/*=export_func optionStackArg * private: * * what: put option args on a stack * arg: + tOptions* + pOpts + program options descriptor + * arg: + tOptDesc* + pOptDesc + the descriptor for this arg + * * doc: * Keep an entry-ordered list of option arguments. =*/ void optionStackArg( tOptions* pOpts, tOptDesc* pOD ) { char * pz; if ((pOD->fOptState & OPTST_RESET) != 0) { tArgList* pAL = (void*)pOD->optCookie; int ix; if (pAL == NULL) return; ix = pAL->useCt; while (--ix >= 0) AGFREE(pAL->apzArgs[ix]); AGFREE(pAL); } else { if (pOD->optArg.argString == NULL) return; AGDUPSTR(pz, pOD->optArg.argString, "stack arg"); addArgListEntry( &(pOD->optCookie), (void*)pz ); } }
/** * 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; }
/*=directive line * * text: * * Alters the current line number and/or file name. You may wish to * use this directive if you extract definition source from other files. * @command{getdefs} uses this mechanism so AutoGen will report the correct * file and approximate line number of any errors found in extracted * definitions. =*/ static char* doDir_line(char* pzArg, char* pzScan) { /* * The sequence must be: #line <number> "file-name-string" * * Start by scanning up to and extracting the line number. */ while (IS_WHITESPACE_CHAR(*pzArg)) pzArg++; if (! IS_DEC_DIGIT_CHAR(*pzArg)) return pzScan; pCurCtx->lineNo = strtol(pzArg, &pzArg, 0); /* * Now extract the quoted file name string. * We dup the string so it won't disappear on us. */ while (IS_WHITESPACE_CHAR(*pzArg)) pzArg++; if (*(pzArg++) != '"') return pzScan; { char* pz = strchr(pzArg, '"'); if (pz == NULL) return pzScan; *pz = NUL; } AGDUPSTR(pCurCtx->pzCtxFname, pzArg, "#line file name"); return pzScan; }
static inline char * ao_xstrdup(char const * pz) { char * str; AGDUPSTR(str, pz, "time val str"); return str; }
/*=export_func optionLoadLine * * what: process a string for an option name and value * * arg: tOptions*, pOpts, program options descriptor * arg: char const*, pzLine, NUL-terminated text * * doc: * * This is a client program callable routine for setting options from, for * example, the contents of a file that they read in. Only one option may * appear in the text. It will be treated as a normal (non-preset) option. * * When passed a pointer to the option struct and a string, it will find * the option named by the first token on the string and set the option * argument to the remainder of the string. The caller must NUL terminate * the string. Any embedded new lines will be included in the option * argument. If the input looks like one or more quoted strings, then the * input will be "cooked". The "cooking" is identical to the string * formation used in AutoGen definition files (@pxref{basic expression}), * except that you may not use backquotes. * * err: Invalid options are silently ignored. Invalid option arguments * will cause a warning to print, but the function should return. =*/ void optionLoadLine(tOptions * pOpts, char const * pzLine) { tOptState st = OPTSTATE_INITIALIZER(SET); char* pz; AGDUPSTR(pz, pzLine, "user option line"); loadOptionLine(pOpts, &st, pz, DIRECTION_PROCESS, OPTION_LOAD_COOKED); AGFREE(pz); }
/** * Finish off the dependency file */ static void wrap_up_depends(void) { static mode_t const fil_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; static char const fmt[] = "\n\n%1$s_SList =%2$s\n" "\n%3$s : $(%1$s_SList)\n" "\n$(%1$s_TList) : %3$s\n" "\t@:\n"; fprintf(pfDepends, fmt, pz_targ_base, pzSourceList, pzDepTarget); fclose(pfDepends); pfDepends = NULL; /* * "startTime" is engineered to be 1 second earlier than the mod time * of any of the output files. */ { struct utimbuf tbuf = { .actime = time(NULL), .modtime = startTime }; utime(pzDepFile, &tbuf); /* * If the target is not the dependency file, then ensure that the * file exists and set its time to the same time. Ignore all errors. */ if (strcmp(pzDepFile, pzDepTarget) != 0) { if (access(pzDepTarget, R_OK) != 0) close( open(pzDepTarget, O_CREAT, fil_mode)); utime(pzDepTarget, &tbuf); } } chmod(pzDepFile, fil_mode); /* * Trim off the temporary suffix and rename the dependency file into * place. We used a mkstemp name in case autogen failed. */ { char * pze = strrchr(pzDepFile, '-'); char * pzn; *pze = NUL; AGDUPSTR(pzn, pzDepFile, "dep file"); *pze = '-'; rename(pzDepFile, pzn); AGFREE(pzn); } }
/*=export_func optionLoadLine * * what: process a string for an option name and value * * arg: tOptions *, opts, program options descriptor * arg: char const *, line, NUL-terminated text * * doc: * * This is a client program callable routine for setting options from, for * example, the contents of a file that they read in. Only one option may * appear in the text. It will be treated as a normal (non-preset) option. * * When passed a pointer to the option struct and a string, it will find * the option named by the first token on the string and set the option * argument to the remainder of the string. The caller must NUL terminate * the string. The caller need not skip over any introductory hyphens. * Any embedded new lines will be included in the option * argument. If the input looks like one or more quoted strings, then the * input will be "cooked". The "cooking" is identical to the string * formation used in AutoGen definition files (@pxref{basic expression}), * except that you may not use backquotes. * * err: Invalid options are silently ignored. Invalid option arguments * will cause a warning to print, but the function should return. =*/ void optionLoadLine(tOptions * opts, char const * line) { tOptState st = OPTSTATE_INITIALIZER(SET); char * pz; proc_state_mask_t sv_flags = opts->fOptSet; opts->fOptSet &= ~OPTPROC_ERRSTOP; AGDUPSTR(pz, line, "opt line"); load_opt_line(opts, &st, pz, DIRECTION_CALLED, OPTION_LOAD_COOKED); AGFREE(pz); opts->fOptSet = sv_flags; }
/*=gfunc string_contains_eqv_p * * what: caseless substring * general_use: * * exparg: text, text to test for pattern * exparg: match, pattern/substring to search for * * string: "*=*" * * doc: Test to see if a string contains an equivalent string. * `equivalent' means the strings match, but without regard * to character case and certain characters are considered `equivalent'. * Viz., '-', '_' and '^' are equivalent. =*/ static tSuccess Select_Equivalent(char const * sample, char const * pattern) { char* pz; tSuccess res = SUCCESS; AGDUPSTR(pz, sample, "equiv chars"); upString(pz); if (strstr(pz, pattern) == NULL) res = FAILURE; AGFREE((void*)pz); return res; }
static void loadScheme(void) { char* pzText = pCurCtx->pzScan; char* pzEnd = (char*)skipScheme(pzText, pzText + strlen(pzText)); char endCh = *pzEnd; int schemeLen = (pzEnd - pzText); int next_ln; SCM res; /* * NUL terminate the Scheme expression, run it, then restore * the NUL-ed character. */ if (*pzEnd == NUL) AG_ABEND(aprf(zErrMsg, pzProg, "end of Guile/scheme expression not found", pCurCtx->pzCtxFname, pCurCtx->lineNo)); *pzEnd = NUL; next_ln = pCurCtx->lineNo + count_nl(pzText); procState = PROC_STATE_GUILE_PRELOAD; res = ag_scm_c_eval_string_from_file_line( pzText, pCurCtx->pzCtxFname, pCurCtx->lineNo ); procState = PROC_STATE_LOAD_DEFS; *pzEnd = endCh; pCurCtx->pzScan = pzEnd; pzEnd = (char*)resolveSCM(res); /* ignore const-ness */ pCurCtx->lineNo = next_ln; if (strlen(pzEnd) >= schemeLen) { AGDUPSTR(pzEnd, pzEnd, "SCM Result"); pz_token = pzEnd; manageAllocatedData(pz_token); } else { /* * We know the result is smaller than the source. Copy in place. */ strcpy(pzText, pzEnd); pz_token = pzText; } lastToken = DP_EV_STRING; }
/** * 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 = (void *)(intptr_t)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 = (void *)(intptr_t)pOD->optArg.argString; else AGDUPSTR(pOD->optCookie, pOD->optArg.argString, "file name"); pOD->optArg.argFp = fp; pOD->fOptState &= ~OPTST_ALLOC_ARG; }
SCM ag_scm_string_contains_eqv_p(SCM text, SCM substr) { static char const zSrch[] = "search string"; char* pzSubstr; SCM res; AGDUPSTR(pzSubstr, ag_scm2zchars( substr, zSrch ), "substring"); upString(pzSubstr); if (SUCCESSFUL(Select_Equivalent(ag_scm2zchars(text, "sample text"), pzSubstr))) res = SCM_BOOL_T; else res = SCM_BOOL_F; AGFREE((void*)pzSubstr); return res; }
static void init_scm(void) { last_scm_cmd = SCHEME_INIT_TEXT; { SCM ini_res = ag_scm_c_eval_string_from_file_line( SCHEME_INIT_TEXT, AG_TEXT_STRTABLE_FILE, SCHEME_INIT_TEXT_LINENO); AGDUPSTR(libguile_ver, scm2display(ini_res), "ini res"); } { unsigned int maj, min, mic; switch (sscanf(libguile_ver, "%u.%u.%u", &maj, &min, &mic)) { case 2: case 3: break; default: AG_ABEND(aprf(GUILE_VERSION_BAD, libguile_ver)); /* NOT_REACHED */ } maj = min + (100 * maj); if ((GUILE_VERSION / 1000) != maj) AG_ABEND(aprf(GUILE_VERSION_WRONG, libguile_ver, MK_STR(GUILE_VERSION))); } { # if GUILE_VERSION >= 200000 # define SCHEME_INIT_DEBUG SCHEME_INIT_DEBUG_2_0 # else # define SCHEME_INIT_DEBUG SCHEME_INIT_DEBUG_1_6 # endif char * p = aprf(INIT_SCM_ERRS_FMT, SCHEME_INIT_DEBUG); # undef SCHEME_INIT_DEBUG last_scm_cmd = p; ag_scm_c_eval_string_from_file_line(p, __FILE__, __LINE__); AGFREE(p); } }
/*=export_func optionPrintParagraphs * private: * * what: Print a paragraph of usage text * arg: + char const * + text + a block of text that has bee i18n-ed + * arg: + bool + plain + false -> wrap text in fputs() + * arg: + FILE * + fp + the stream file pointer for output + * * doc: * This procedure is called in two contexts: when a full or short usage text * has been provided for display, and when autogen is assembling a list of * translatable texts in the optmain.tlib template. In the former case, \a * plain is set to \a true, otherwise \a false. * * Anything less than 256 characters in size is printed as a single unit. * Otherwise, paragraphs are detected. A paragraph break is defined as just * before a non-empty line preceded by two newlines or a line that starts * with at least one space character but fewer than 8 space characters. * Lines indented with tabs or more than 7 spaces are considered continuation * lines. * * If 'plain' is true, we are emitting text for a user to see. So, if it is * true and NLS is not enabled, then just write the whole thing at once. =*/ void optionPrintParagraphs(char const * text, bool plain, FILE * fp) { size_t len = strlen(text); char * buf; #ifndef ENABLE_NLS if (plain || (len < 256)) #else if (len < 256) #endif { print_one_paragraph(text, plain, fp); return; } AGDUPSTR(buf, text, "ppara"); text = buf; for (;;) { char * scan; if (len < 256) { done: print_one_paragraph(buf, plain, fp); break; } scan = buf; try_longer: scan = strchr(scan, NL); if (scan == NULL) goto done; if ((scan - buf) < 8) { scan++; goto try_longer; } scan++; if ((! isspace((int)*scan)) || (*scan == HT)) /* * line starts with tab or non-whitespace --> continuation */ goto try_longer; if (*scan == NL) { /* * Double newline -> paragraph break * Include all newlines in current paragraph. */ while (*++scan == NL) /*continue*/; } else { char * p = scan; int sp_ct = 0; while (*p == ' ') { if (++sp_ct >= 8) { /* * Too many spaces --> continuation line */ scan = p; goto try_longer; } p++; } } /* * "scan" points to the first character of a paragraph or the * terminating NUL byte. */ { char svch = *scan; *scan = NUL; print_one_paragraph(buf, plain, fp); len -= scan - buf; if (len <= 0) break; *scan = svch; buf = scan; } } AGFREE((void *)text); }
/** * Starting with the current directory, search the directory * list trying to find the base template file name. */ LOCAL tSuccess findFile(char const * pzFName, char * pzFullName, char const * const * papSuffixList, char const * pzReferrer) { char * pzRoot; char * pzSfx; void * deallocAddr = NULL; tSuccess res = SUCCESS; /* * Expand leading environment variables. * We will not mess with embedded ones. */ if (*pzFName == '$') { if (! optionMakePath(pzFullName, (int)AG_PATH_MAX, pzFName, autogenOptions.pzProgPath)) return FAILURE; AGDUPSTR(pzFName, pzFullName, "find file name"); deallocAddr = (void*)pzFName; /* * pzFName now points to the name the file system can use. * It must _not_ point to pzFullName because we will likely * rewrite that value using this pointer! */ } /* * Not a complete file name. If there is not already * a suffix for the file name, then append ".tpl". * Check for immediate access once again. */ pzRoot = strrchr(pzFName, '/'); pzSfx = (pzRoot != NULL) ? strchr(++pzRoot, '.') : strchr(pzFName, '.'); /* * The referrer is useful only if it includes a directory name. */ if (pzReferrer != NULL) { char * pz = strrchr(pzReferrer, '/'); if (pz == NULL) pzReferrer = NULL; else { AGDUPSTR(pzReferrer, pzReferrer, "pzReferrer"); pz = strrchr(pzReferrer, '/'); *pz = NUL; } } /* * IF the file name does not contain a directory separator, * then we will use the TEMPL_DIR search list to keep hunting. */ { static char const curdir[] = "."; static char const zDirFmt[] = "%s/%s"; /* * Search each directory in our directory search list * for the file. We always force one copy of this option. */ int ct = STACKCT_OPT(TEMPL_DIRS); char const ** ppzDir = STACKLST_OPT(TEMPL_DIRS) + ct - 1; char const * pzDir = curdir; if (*pzFName == '/') ct = 0; do { char * pzEnd; void * coerce; /* * IF one of our template paths starts with '$', then expand it. */ if (*pzDir == '$') { if (! optionMakePath(pzFullName, (int)AG_PATH_MAX, pzDir, autogenOptions.pzProgPath)) { coerce = (void *)pzDir; strcpy(coerce, curdir); } else { AGDUPSTR(pzDir, pzFullName, "find directory name"); coerce = (void *)ppzDir[1]; free(coerce); ppzDir[1] = pzDir; /* save the computed name for later */ } } if ((*pzFName == '/') || (pzDir == curdir)) { size_t nln = strlen(pzFName); if (nln >= AG_PATH_MAX - MAX_SUFFIX_LEN) break; memcpy(pzFullName, pzFName, nln); pzEnd = pzFullName + nln; *pzEnd = NUL; } else { pzEnd = pzFullName + snprintf(pzFullName, AG_PATH_MAX - MAX_SUFFIX_LEN, zDirFmt, pzDir, pzFName); } if (read_okay(pzFullName)) goto findFileReturn; /* * IF the file does not already have a suffix, * THEN try the ones that are okay for this file. */ if ((pzSfx == NULL) && (papSuffixList != NULL)) { char const * const * papSL = papSuffixList; *(pzEnd++) = '.'; do { strcpy(pzEnd, *(papSL++)); /* must fit */ if (read_okay(pzFullName)) goto findFileReturn; } while (*papSL != NULL); } if (*pzFName == '/') break; /* * IF there is a referrer, use it before the rest of the * TEMPL_DIRS We detect first time through by *both* pzDir value * and ct value. "ct" will have this same value next time * through and occasionally "pzDir" will be set to "curdir", but * never again when ct is STACKCT_OPT(TEMPL_DIRS) */ if ( (pzReferrer != NULL) && (pzDir == curdir) && (ct == STACKCT_OPT(TEMPL_DIRS))) { pzDir = pzReferrer; ct++; } else { pzDir = *(ppzDir--); } } while (--ct >= 0); } res = FAILURE; findFileReturn: AGFREE(deallocAddr); AGFREE(pzReferrer); return res; }
/** * Load an option from a block of text. The text must start with the * configurable/option name and be followed by its associated value. * That value may be processed in any of several ways. See "tOptionLoadMode" * in autoopts.h. * * @param[in,out] opts program options descriptor * @param[in,out] opt_state option processing state * @param[in,out] line source line with long option name in it * @param[in] direction current processing direction (preset or not) * @param[in] load_mode option loading mode (OPTION_LOAD_*) */ LOCAL void loadOptionLine( tOptions * opts, tOptState * opt_state, char * line, tDirection direction, tOptionLoadMode load_mode ) { line = SPN_LOAD_LINE_SKIP_CHARS(line); { char * arg = assemble_arg_val(line, load_mode); if (! SUCCESSFUL(opt_find_long(opts, line, opt_state))) return; if (opt_state->flags & OPTST_NO_INIT) return; opt_state->pzOptArg = arg; } switch (opt_state->flags & (OPTST_IMM|OPTST_DISABLE_IMM)) { case 0: /* * The selected option has no immediate action. * THEREFORE, if the direction is PRESETTING * THEN we skip this option. */ if (PRESETTING(direction)) return; break; case OPTST_IMM: if (PRESETTING(direction)) { /* * We are in the presetting direction with an option we handle * immediately for enablement, but normally for disablement. * Therefore, skip if disabled. */ if ((opt_state->flags & OPTST_DISABLED) == 0) return; } else { /* * We are in the processing direction with an option we handle * immediately for enablement, but normally for disablement. * Therefore, skip if NOT disabled. */ if ((opt_state->flags & OPTST_DISABLED) != 0) return; } break; case OPTST_DISABLE_IMM: if (PRESETTING(direction)) { /* * We are in the presetting direction with an option we handle * immediately for disablement, but normally for disablement. * Therefore, skip if NOT disabled. */ if ((opt_state->flags & OPTST_DISABLED) != 0) return; } else { /* * We are in the processing direction with an option we handle * immediately for disablement, but normally for disablement. * Therefore, skip if disabled. */ if ((opt_state->flags & OPTST_DISABLED) == 0) return; } break; case OPTST_IMM|OPTST_DISABLE_IMM: /* * The selected option is always for immediate action. * THEREFORE, if the direction is PROCESSING * THEN we skip this option. */ if (PROCESSING(direction)) return; break; } /* * Fix up the args. */ if (OPTST_GET_ARGTYPE(opt_state->pOD->fOptState) == OPARG_TYPE_NONE) { if (*opt_state->pzOptArg != NUL) return; opt_state->pzOptArg = NULL; } else if (opt_state->pOD->fOptState & OPTST_ARG_OPTIONAL) { if (*opt_state->pzOptArg == NUL) opt_state->pzOptArg = NULL; else { AGDUPSTR(opt_state->pzOptArg, opt_state->pzOptArg, "opt arg"); opt_state->flags |= OPTST_ALLOC_ARG; } } else { if (*opt_state->pzOptArg == NUL) opt_state->pzOptArg = zNil; else { AGDUPSTR(opt_state->pzOptArg, opt_state->pzOptArg, "opt arg"); opt_state->flags |= OPTST_ALLOC_ARG; } } { tOptionLoadMode sv = option_load_mode; option_load_mode = load_mode; handle_opt(opts, opt_state); option_load_mode = sv; } }
/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Load the macro array and file name. */ static void load_macs(tTemplate * pT, char const * pzF, char const * pzN, char const * pzData) { tMacro* pMac = pT->aMacros; { char* pzText = (char*)(pMac + pT->macroCt); size_t len; AGDUPSTR(pT->pzTplFile, pzF, "templ file"); len = strlen(pzN) + 1; memcpy((void*)pzText, (void*)pzN, len); pT->pzTplName = pzText; pzText += len; pT->pzTemplText = pzText; pT->pNext = pzText + 1; } pCurTemplate = pT; { tMacro* pMacEnd = parseTemplate(pMac, &pzData); int ct; /* * Make sure all of the input string was scanned. */ if (pzData != NULL) AG_ABEND("Template parse ended unexpectedly"); ct = pMacEnd - pMac; /* * IF there are empty macro slots, * THEN pack the text */ if (ct < pT->macroCt) { int delta = sizeof(tMacro) * (pT->macroCt - ct); void* data = (pT->pzTplName == NULL) ? pT->pzTemplText : pT->pzTplName; size_t size = pT->pNext - (char*)data; memmove((void*)pMacEnd, data, size); pT->pzTemplText -= delta; pT->pNext -= delta; pT->pzTplName -= delta; pT->macroCt = ct; } } pT->descSize = pT->pNext - (char*)pT; pT->pNext = NULL; /* * We cannot reallocate a smaller array because * the entries are all linked together and * realloc-ing it may cause it to move. */ #if defined(DEBUG_ENABLED) if (HAVE_OPT(SHOW_DEFS)) { static char const zSum[] = "loaded %d macros from %s\n" "\tBinary template size: 0x%zX\n\n"; fprintf(pfTrace, zSum, pT->macroCt, pzF, pT->descSize); } #endif }
/** * 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; }
/*=directive shell * * text: * Invokes @code{$SHELL} or @file{/bin/sh} on a script that should * generate AutoGen definitions. It does this using the same server * process that handles the back-quoted @code{`} text. * @strong{CAUTION}@: let not your @code{$SHELL} be @code{csh}. =*/ static char* doDir_shell(char* pzArg, char* pzScan) { static char const zShellText[] = "Computed Definitions"; static char const zEndShell[] = "\n#endshell"; tScanCtx* pCtx; char* pzText = pzScan; /* * The output time will always be the current time. * The dynamic content is always current :) */ outTime = 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, zEndShell+1, STRSIZE(zEndShell)-1) == 0) return pzScan; { static char const noend[] = "Missing #endshell after '#shell' in %s on line %d\n"; char* pz = strstr(pzScan, zEndShell); if (pz == NULL) AG_ABEND(aprf(noend, pCurCtx->pzCtxFname, pCurCtx->lineNo)); while (pzScan < pz) { if (*(pzScan++) == NL) pCurCtx->lineNo++; } *pzScan = NUL; } /* * Advance the scan pointer to the next line after '#endshell' * IF there is no such line, * THEN the scan will resume on a zero-length string. */ pzScan = strchr(pzScan + STRSIZE(zEndShell), NL); if (pzScan == NULL) pzScan = (void*)zNil; /* * Save the scan pointer into the current context */ pCurCtx->pzScan = pzScan; /* * Run the shell command. The output text becomes the * "file text" that is used for more definitions. */ pzText = runShell(pzText); if (pzText == NULL) return pzScan; if (*pzText == NUL) { AGFREE(pzText); return pzScan; } /* * Get the space for the output data and for context overhead. * This is an extra allocation and copy, but easier than rewriting * 'loadData()' for this special context. */ pCtx = (tScanCtx*)AGALOC(sizeof(tScanCtx) + strlen(pzText) + 4, "shell output"); /* * Link the new scan data into the context stack */ pCtx->pCtx = pCurCtx; pCurCtx = pCtx; /* * Set up the rest of the context structure */ AGDUPSTR(pCtx->pzCtxFname, zShellText, "shell text"); pCtx->pzScan = pCtx->pzData = (char*)(pCtx+1); pCtx->lineNo = 0; strcpy(pCtx->pzScan, pzText); AGFREE(pzText); return pCtx->pzScan; }
/** * 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; }
/* * Load an option from a block of text. The text must start with the * configurable/option name and be followed by its associated value. * That value may be processed in any of several ways. See "tOptionLoadMode" * in autoopts.h. */ LOCAL void loadOptionLine( tOptions* pOpts, tOptState* pOS, char* pzLine, tDirection direction, tOptionLoadMode load_mode ) { while (isspace( (int)*pzLine )) pzLine++; { char* pzArg = assembleArgValue( pzLine, load_mode ); if (! SUCCESSFUL( longOptionFind( pOpts, pzLine, pOS ))) return; if (pOS->flags & OPTST_NO_INIT) return; pOS->pzOptArg = pzArg; } switch (pOS->flags & (OPTST_IMM|OPTST_DISABLE_IMM)) { case 0: /* * The selected option has no immediate action. * THEREFORE, if the direction is PRESETTING * THEN we skip this option. */ if (PRESETTING(direction)) return; break; case OPTST_IMM: if (PRESETTING(direction)) { /* * We are in the presetting direction with an option we handle * immediately for enablement, but normally for disablement. * Therefore, skip if disabled. */ if ((pOS->flags & OPTST_DISABLED) == 0) return; } else { /* * We are in the processing direction with an option we handle * immediately for enablement, but normally for disablement. * Therefore, skip if NOT disabled. */ if ((pOS->flags & OPTST_DISABLED) != 0) return; } break; case OPTST_DISABLE_IMM: if (PRESETTING(direction)) { /* * We are in the presetting direction with an option we handle * immediately for disablement, but normally for disablement. * Therefore, skip if NOT disabled. */ if ((pOS->flags & OPTST_DISABLED) != 0) return; } else { /* * We are in the processing direction with an option we handle * immediately for disablement, but normally for disablement. * Therefore, skip if disabled. */ if ((pOS->flags & OPTST_DISABLED) == 0) return; } break; case OPTST_IMM|OPTST_DISABLE_IMM: /* * The selected option is always for immediate action. * THEREFORE, if the direction is PROCESSING * THEN we skip this option. */ if (PROCESSING(direction)) return; break; } /* * Fix up the args. */ if (OPTST_GET_ARGTYPE(pOS->pOD->fOptState) == OPARG_TYPE_NONE) { if (*pOS->pzOptArg != NUL) return; pOS->pzOptArg = NULL; } else if (pOS->pOD->fOptState & OPTST_ARG_OPTIONAL) { if (*pOS->pzOptArg == NUL) pOS->pzOptArg = NULL; else { AGDUPSTR( pOS->pzOptArg, pOS->pzOptArg, "option argument" ); pOS->flags |= OPTST_ALLOC_ARG; } } else { if (*pOS->pzOptArg == NUL) pOS->pzOptArg = zNil; else { AGDUPSTR( pOS->pzOptArg, pOS->pzOptArg, "option argument" ); pOS->flags |= OPTST_ALLOC_ARG; } } { tOptionLoadMode sv = option_load_mode; option_load_mode = load_mode; handleOption( pOpts, pOS ); option_load_mode = sv; } }
/** * Load a file into memory. Keep it in memory and try to reuse it * if we get called again. Likely, there will be several extractions * from a single file. */ static char const* load_extract_file(char const* pzNewFile) { static char const * pzFile = NULL; static char const * pzText = NULL; struct stat sbuf; char* pzIn; /* * Make sure that we: * * o got the file name from the SCM value * o return the old text if we are searching the same file * o have a regular file with some data * o can allocate the space we need... * * If we don't know about the current file, we leave the data * from any previous file we may have loaded. * * DO *NOT* include this file in dependency output. The output may vary * based on its contents, but since it is always optional input, it cannot * be made to be required by make. */ if (pzNewFile == NULL) return NULL; if ( (pzFile != NULL) && (strcmp(pzFile, pzNewFile) == 0)) return pzText; if ( (stat(pzNewFile, &sbuf) != 0) || (! S_ISREG(sbuf.st_mode)) || (sbuf.st_size < 10) ) return NULL; if (pzFile != NULL) { AGFREE((void*)pzFile); AGFREE((void*)pzText); pzFile = pzText = NULL; } AGDUPSTR(pzFile, pzNewFile, "extract file"); pzIn = (char*)AGALOC(sbuf.st_size + 1, "Extract Text"); if (! HAVE_OPT(WRITABLE)) SET_OPT_WRITABLE; pzText = (char const*)pzIn; /* * Suck up the file. We must read it all. */ { struct stat stbf; FILE* fp = fopen(pzNewFile, "r"); if (fp == NULL) goto bad_return; if (fstat(fileno(fp), &stbf) != 0) { fclose(fp); goto bad_return; } if (outTime <= stbf.st_mtime) outTime = stbf.st_mtime + 1; do { size_t sz = fread(pzIn, (size_t)1, (size_t)sbuf.st_size, fp); if (sz == 0) { fprintf(stderr, LD_EXTRACT_BAD_READ, errno, strerror(errno), (int)sbuf.st_size, pzFile); AG_ABEND(LD_EXTRACT_READ_FAIL); } pzIn += sz; sbuf.st_size -= sz; } while (sbuf.st_size > 0); *pzIn = NUL; fclose(fp); if (pfDepends != NULL) add_source_file(pzNewFile); } return pzText; bad_return: AGFREE((void*)pzFile); pzFile = NULL; AGFREE((void*)pzText); pzText = NULL; return pzText; }
/*=export_func genshelloptUsage * private: * what: The usage function for the genshellopt generated program * * arg: + tOptions* + pOpts + program options descriptor + * arg: + int + exitCode + 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* pOpts, int exitCode ) { #if ! defined(HAVE_WORKING_FORK) optionUsage( pOpts, exitCode ); #else /* * IF not EXIT_SUCCESS, * THEN emit the short form of usage. */ if (exitCode != EXIT_SUCCESS) optionUsage( pOpts, exitCode ); fflush( stderr ); fflush( stdout ); option_usage_fp = stdout; /* * First, print our usage */ switch (fork()) { case -1: optionUsage( pOpts, EXIT_FAILURE ); /* NOTREACHED */ _exit( EXIT_FAILURE ); case 0: pagerState = PAGER_STATE_CHILD; optionUsage( pOpts, 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; AGDUPSTR( pz, pShellParseOptions->pzPROGNAME, "program name" ); pShellParseOptions->pzProgName = pz; while (*pz != NUL) { *pz = tolower( *pz ); pz++; } } /* * Separate the makeshell usage from the client usage */ fprintf( option_usage_fp, zGenshell, pShellParseOptions->pzProgName ); fflush( option_usage_fp ); /* * Now, print the client usage. */ switch (fork()) { case 0: pagerState = PAGER_STATE_CHILD; /*FALLTHROUGH*/ case -1: optionUsage( pShellParseOptions, EXIT_FAILURE ); default: { int sts; wait( &sts ); } } exit( EXIT_SUCCESS ); #endif }
/* * process a single scheme expression, yielding text that gets processed * into AutoGen definitions. */ static void alist_to_autogen_def(void) { static char const zSchemeText[] = "Scheme Computed Definitions"; static char const zWrap[] = "(alist->autogen-def %s)"; char* pzText = ++(pCurCtx->pzScan); char* pzEnd = (char*)skipScheme(pzText, pzText + strlen(pzText)); SCM res; size_t res_len; tScanCtx* pCtx; /* * Wrap the scheme expression with the `alist->autogen-def' function */ { char endCh = *pzEnd; *pzEnd = NUL; pzText = aprf(zWrap, pzText); *pzEnd = endCh; } /* * Run the scheme expression. The result is autogen definition text. */ procState = PROC_STATE_GUILE_PRELOAD; res = ag_scm_c_eval_string_from_file_line( pzText, pCurCtx->pzCtxFname, pCurCtx->lineNo ); /* * The result *must* be a string, or we choke. */ if (! AG_SCM_STRING_P(res)) { static char const zEr[] = "Scheme definition expression does not yield string:\n"; AG_ABEND(zEr); } res_len = AG_SCM_STRLEN(res); procState = PROC_STATE_LOAD_DEFS; pCurCtx->pzScan = pzEnd; AGFREE(pzText); /* * Now, push the resulting string onto the input stack * and link the new scan data into the context stack */ pCtx = (tScanCtx*)AGALOC(sizeof(tScanCtx) + 4 + res_len, "lex scan ctx"); pCtx->pCtx = pCurCtx; pCurCtx = pCtx; /* * Set up the rest of the context structure */ AGDUPSTR(pCtx->pzCtxFname, zSchemeText, "scheme text"); pCtx->pzScan = \ pCtx->pzData = (char*)(pCtx+1); pCtx->lineNo = 0; memcpy((void*)(pCtx->pzScan), (void*)AG_SCM_CHARS(res), res_len); pCtx->pzScan[ res_len ] = NUL; /* * At this point, the next token will be obtained * from the newly allocated context structure. * When empty, input will resume from the '}' that we * left as the next input token in the old context. */ }
/*=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 }
static void do_env_opt(tOptState * os, char * env_name, tOptions * pOpts, teEnvPresetType type) { os->pzOptArg = getenv(env_name); if (os->pzOptArg == NULL) return; os->flags = OPTST_PRESET | OPTST_ALLOC_ARG | os->pOD->fOptState; os->optType = TOPT_UNDEFINED; if ( (os->pOD->pz_DisablePfx != NULL) && (streqvcmp(os->pzOptArg, os->pOD->pz_DisablePfx) == 0)) { os->flags |= OPTST_DISABLED; os->pzOptArg = NULL; } switch (type) { case ENV_IMM: /* * Process only immediate actions */ if (DO_IMMEDIATELY(os->flags)) break; return; case ENV_NON_IMM: /* * Process only NON immediate actions */ if (DO_NORMALLY(os->flags) || DO_SECOND_TIME(os->flags)) break; return; default: /* process everything */ break; } /* * Make sure the option value string is persistent and consistent. * * The interpretation of the option value depends * on the type of value argument the option takes */ if (OPTST_GET_ARGTYPE(os->pOD->fOptState) == OPARG_TYPE_NONE) { /* * Ignore any value. */ os->pzOptArg = NULL; } else if (os->pzOptArg[0] == NUL) { /* * If the argument is the empty string and the argument is * optional, then treat it as if the option was not specified. */ if ((os->pOD->fOptState & OPTST_ARG_OPTIONAL) == 0) return; os->pzOptArg = NULL; } else { AGDUPSTR(os->pzOptArg, os->pzOptArg, "option argument"); os->flags |= OPTST_ALLOC_ARG; } handle_opt(pOpts, os); }
/*=directive include * * arg: unadorned-file-name * * text: * This directive will insert definitions from another file into * the current collection. If the file name is adorned with * double quotes or angle brackets (as in a C program), then the * include is ignored. =*/ static char* doDir_include(char* pzArg, char* pzScan) { static char const * apzSfx[] = { "def", NULL }; tScanCtx* pCtx; size_t inclSize; char zFullName[ AG_PATH_MAX + 1 ]; /* * Ignore C-style includes. This allows "C" files to be processed * for their "#define"s. */ if ((*pzArg == '"') || (*pzArg == '<')) return pzScan; pCurCtx->pzScan = pzScan; if (! SUCCESSFUL( findFile(pzArg, zFullName, apzSfx, pCurCtx->pzCtxFname))) { fprintf(pfTrace, "WARNING: cannot find `%s' definitions file\n", pzArg); return pzScan; } /* * Make sure the specified file is a regular file and we can get * the correct size for it. */ { struct stat stbf; if (stat(zFullName, &stbf) != 0) { fprintf(pfTrace, "WARNING %d (%s): cannot stat `%s' " "for include\n", errno, strerror(errno), zFullName); return pzScan; } if (! S_ISREG(stbf.st_mode)) { fprintf(pfTrace, "WARNING: `%s' must be regular file to " "include\n", zFullName); return pzScan; } inclSize = stbf.st_size; if (outTime <= stbf.st_mtime) outTime = stbf.st_mtime + 1; } if (inclSize == 0) return pzScan; /* * Get the space for the output data and for context overhead. * This is an extra allocation and copy, but easier than rewriting * 'loadData()' for this special context. */ { size_t sz = sizeof(tScanCtx) + 4 + inclSize; pCtx = (tScanCtx*)AGALOC(sz, "include def header"); memset((void*)pCtx, 0, sz); pCtx->lineNo = 1; } /* * Link it into the context stack */ pCtx->pCtx = pCurCtx; pCurCtx = pCtx; AGDUPSTR(pCtx->pzCtxFname, zFullName, "def file name"); pCtx->pzScan = pCtx->pzData = pzScan = (char*)(pCtx + 1); /* * Read all the data. Usually in a single read, but loop * in case multiple passes are required. */ { FILE* fp = fopen(zFullName, "r" FOPEN_TEXT_FLAG); char* pz = pzScan; if (fp == NULL) AG_CANT("open file", zFullName); if (pfDepends != NULL) append_source_name(zFullName); do { size_t rdct = fread((void*)pz, (size_t)1, inclSize, fp); if (rdct == 0) AG_CANT("read file", zFullName); pz += rdct; inclSize -= rdct; } while (inclSize > 0); fclose(fp); *pz = NUL; } return pzScan; }