diff options
Diffstat (limited to 'agen5/tpProcess.c')
-rw-r--r-- | agen5/tpProcess.c | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/agen5/tpProcess.c b/agen5/tpProcess.c new file mode 100644 index 0000000..4eb4f6f --- /dev/null +++ b/agen5/tpProcess.c @@ -0,0 +1,413 @@ + +/** + * @file tpProcess.c + * + * Parse and process the template data descriptions + * + * Time-stamp: "2012-04-07 09:26:41 bkorb" + * + * This file is part of AutoGen. + * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved + * + * AutoGen is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AutoGen is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* = = = START-STATIC-FORWARD = = = */ +static void +trace_macro(templ_t * tpl, macro_t * mac); + +static void +do_stdout_tpl(templ_t * tpl); + +static void +open_output(out_spec_t * spec); +/* = = = END-STATIC-FORWARD = = = */ + +/** + * Generate all the text within a block. + * The caller must know the exact bounds of the block. + * + * @param tpl template containing block of macros + * @param mac first macro in series + * @param emac one past last macro in series + */ +LOCAL void +gen_block(templ_t * tpl, macro_t * mac, macro_t * emac) +{ + /* + * Set up the processing context for this block of macros. + * It is used by the Guile callback routines and the exception + * handling code. It is all for user friendly diagnostics. + */ + current_tpl = tpl; + + while ((mac != NULL) && (mac < emac)) { + mac_func_t fc = mac->md_code; + if (fc >= FUNC_CT) + fc = FTYP_BOGUS; + + if (OPT_VALUE_TRACE >= TRACE_EVERYTHING) + trace_macro(tpl, mac); + + cur_macro = mac; + mac = (*(load_procs[ fc ]))(tpl, mac); + ag_scribble_free(); + } +} + +/** + * Print out information about the invocation of a macro. + * Print up to the first 32 characters in the macro, for context. + * + * @param tpl template containing macros + * @param mac first macro in series + */ +static void +trace_macro(templ_t * tpl, macro_t * mac) +{ + mac_func_t fc = mac->md_code; + if (fc >= FUNC_CT) + fc = FTYP_BOGUS; + + fprintf(trace_fp, TRACE_MACRO_FMT, ag_fun_names[fc], mac->md_code, + tpl->td_file, mac->md_line); + + if (mac->md_txt_off > 0) { + char * pz = tpl->td_text + mac->md_txt_off; + char * pe = BRK_NEWLINE_CHARS(pz); + if (pe > pz + 32) + pz = pz + 32; + + putc(' ', trace_fp); putc(' ', trace_fp); + fwrite(pz, pe - pz, 1, trace_fp); + putc(NL, trace_fp); + } +} + +/** + * The template output goes to stdout. Perhaps because output + * is for a CGI script. In any case, this case must be handled + * specially. + * + * @param tpl template to be processed + */ +static void +do_stdout_tpl(templ_t * tpl) +{ + SCM res; + + last_scm_cmd = NULL; /* We cannot be in Scheme processing */ + + switch (setjmp(abort_jmp_buf)) { + case SUCCESS: + break; + + case PROBLEM: + if (*oops_pfx != NUL) { + fprintf(stdout, DO_STDOUT_TPL_ABANDONED, oops_pfx); + oops_pfx = zNil; + } + fclose(stdout); + return; + + default: + fprintf(stdout, DO_STDOUT_TPL_BADR, oops_pfx); + + case FAILURE: + exit(EXIT_FAILURE); + /* NOTREACHED */ + } + + curr_sfx = DO_STDOUT_TPL_NOSFX; + curr_def_ctx = root_def_ctx; + cur_fpstack = &out_root; + out_root.stk_fp = stdout; + out_root.stk_fname = DO_STDOUT_TPL_STDOUT; + out_root.stk_flags = FPF_NOUNLINK | FPF_STATIC_NM; + if (OPT_VALUE_TRACE >= TRACE_EVERYTHING) + fputs(DO_STDOUT_TPL_START_STD, trace_fp); + + /* + * 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 (*oops_pfx == NUL) + gen_block(tpl, tpl->td_macros, tpl->td_macros + tpl->td_mac_ct); + + else { + char const * pzRes; + (void)ag_scm_out_push_new(SCM_UNDEFINED); + + gen_block(tpl, tpl->td_macros, tpl->td_macros + tpl->td_mac_ct); + + /* + * 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 char prefix is: "content-type:" */ + if (strneqvcmp(pzRes, DO_STDOUT_TPL_CONTENT, 13) != 0) + fputs(DO_STDOUT_TPL_CONTENT, stdout); + + fwrite(pzRes, AG_SCM_STRLEN(res), 1, stdout); + } + + fclose(stdout); +} + +/** + * pop the current output spec structure. Deallocate it and the + * file name, too, if necessary. + */ +LOCAL out_spec_t * +next_out_spec(out_spec_t * os) +{ + out_spec_t * res = os->os_next; + + if (os->os_dealloc_fmt) + AGFREE(os->os_file_fmt); + + AGFREE(os); + return res; +} + +LOCAL void +process_tpl(templ_t * tpl) +{ + /* + * IF the template file does not specify any output suffixes, + * THEN we will generate to standard out with the suffix set to zNoSfx. + * With output going to stdout, we don't try to remove output on errors. + */ + if (output_specs == NULL) { + do_stdout_tpl(tpl); + return; + } + + do { + out_spec_t * ospec = output_specs; + + /* + * We cannot be in Scheme processing. We've either just started + * or we've made a long jump from our own code. If we've made a + * long jump, we've printed a message that is sufficient and we + * don't need to print any scheme expressions. + */ + last_scm_cmd = NULL; + + /* + * HOW was that we got here? + */ + switch (setjmp(abort_jmp_buf)) { + case SUCCESS: + if (OPT_VALUE_TRACE >= TRACE_EVERYTHING) { + fprintf(trace_fp, PROC_TPL_START, ospec->os_sfx); + fflush(trace_fp); + } + /* + * Set the output file name buffer. + * It may get switched inside open_output. + */ + open_output(ospec); + memcpy(&out_root, cur_fpstack, sizeof(out_root)); + AGFREE(cur_fpstack); + cur_fpstack = &out_root; + curr_sfx = ospec->os_sfx; + curr_def_ctx = root_def_ctx; + cur_fpstack->stk_flags &= ~FPF_FREE; + cur_fpstack->stk_prev = NULL; + gen_block(tpl, tpl->td_macros, tpl->td_macros+tpl->td_mac_ct); + + do { + out_close(false); /* keep output */ + } while (cur_fpstack->stk_prev != NULL); + break; + + case PROBLEM: + /* + * We got here by a long jump. Close/purge the open files. + */ + do { + out_close(true); /* discard output */ + } while (cur_fpstack->stk_prev != NULL); + last_scm_cmd = NULL; /* "problem" means "drop current output". */ + break; + + default: + fprintf(trace_fp, PROC_TPL_BOGUS_RET, oops_pfx); + oops_pfx = zNil; + /* FALLTHROUGH */ + + case FAILURE: + /* + * We got here by a long jump. Close/purge the open files. + */ + do { + out_close(true); /* discard output */ + } while (cur_fpstack->stk_prev != NULL); + + /* + * On failure (or unknown jump type), we quit the program, too. + */ + processing_state = PROC_STATE_ABORTING; + do ospec = next_out_spec(ospec); + while (ospec != NULL); + exit(EXIT_FAILURE); + /* NOTREACHED */ + } + + output_specs = next_out_spec(ospec); + } while (output_specs != NULL); +} + + +LOCAL void +out_close(bool purge) +{ + if ((cur_fpstack->stk_flags & FPF_NOCHMOD) == 0) + make_readonly(); + + if (OPT_VALUE_TRACE > TRACE_DEBUG_MESSAGE) + fprintf(trace_fp, OUT_CLOSE_TRACE_WRAP, __func__, + cur_fpstack->stk_fname); + + fclose(cur_fpstack->stk_fp); + + /* + * Only stdout and /dev/null are marked, "NOUNLINK" + */ + if ((cur_fpstack->stk_flags & FPF_NOUNLINK) == 0) { + /* + * IF we are told to purge the file OR the file is an AutoGen temp + * file, then get rid of the output. + */ + if (purge || ((cur_fpstack->stk_flags & FPF_UNLINK) != 0)) + unlink(cur_fpstack->stk_fname); + + else { + struct utimbuf tbuf; + + tbuf.actime = time(NULL); + tbuf.modtime = outfile_time; + + /* + * The putative start time is one second earlier than the + * earliest output file time, regardless of when that is. + */ + if (outfile_time <= start_time) + start_time = outfile_time - 1; + + utime(cur_fpstack->stk_fname, &tbuf); + } + } + + /* + * Do not deallocate statically allocated names + */ + if ((cur_fpstack->stk_flags & FPF_STATIC_NM) == 0) + AGFREE((void*)cur_fpstack->stk_fname); + + /* + * Do not deallocate the root entry. It is not allocated!! + */ + if ((cur_fpstack->stk_flags & FPF_FREE) != 0) { + out_stack_t* p = cur_fpstack; + cur_fpstack = p->stk_prev; + AGFREE((void*)p); + } +} + +/** + * Figure out what to use as the base name of the output file. + * If an argument is not provided, we use the base name of + * the definitions file. + */ +static void +open_output(out_spec_t * spec) +{ + static char const write_mode[] = "w" FOPEN_BINARY_FLAG "+"; + + char const * out_file = NULL; + + if (strcmp(spec->os_sfx, OPEN_OUTPUT_NULL) == 0) { + static int const flags = FPF_NOUNLINK | FPF_NOCHMOD | FPF_TEMPFILE; + null_open: + open_output_file(DEV_NULL, DEV_NULL_LEN, write_mode, flags); + return; + } + + /* + * IF we are to skip the current suffix, + * we will redirect the output to /dev/null and + * perform all the work. There may be side effects. + */ + if (HAVE_OPT(SKIP_SUFFIX)) { + int ct = STACKCT_OPT(SKIP_SUFFIX); + const char ** ppz = STACKLST_OPT(SKIP_SUFFIX); + + while (--ct >= 0) { + if (strcmp(spec->os_sfx, *ppz++) == 0) + goto null_open; + } + } + + /* + * Remove any suffixes in the last file name + */ + { + char const * def_file = OPT_ARG(BASE_NAME); + char z[AG_PATH_MAX]; + const char * pst = strrchr(def_file, '/'); + char * end; + + pst = (pst == NULL) ? def_file : (pst + 1); + + /* + * We allow users to specify a suffix with '-' and '_', but when + * stripping a suffix from the "base name", we do not recognize 'em. + */ + end = strchr(pst, '.'); + if (end != NULL) { + size_t len = (unsigned)(end - pst); + if (len >= sizeof(z)) + AG_ABEND("--base-name name is too long"); + + memcpy(z, pst, len); + z[ end - pst ] = NUL; + pst = z; + } + + /* + * Now formulate the output file name in the buffer + * provided as the input argument. + */ + out_file = aprf(spec->os_file_fmt, pst, spec->os_sfx); + if (out_file == NULL) + AG_ABEND(aprf(OPEN_OUTPUT_BAD_FMT, spec->os_file_fmt, pst, + spec->os_sfx)); + } + + open_output_file(out_file, strlen(out_file), write_mode, 0); + free((void *)out_file); +} +/* + * Local Variables: + * mode: C + * c-file-style: "stroustrup" + * indent-tabs-mode: nil + * End: + * end of agen5/tpProcess.c */ |