summaryrefslogtreecommitdiff
path: root/agen5/expExtract.c
diff options
context:
space:
mode:
Diffstat (limited to 'agen5/expExtract.c')
-rw-r--r--agen5/expExtract.c360
1 files changed, 360 insertions, 0 deletions
diff --git a/agen5/expExtract.c b/agen5/expExtract.c
new file mode 100644
index 0000000..f2aa7a4
--- /dev/null
+++ b/agen5/expExtract.c
@@ -0,0 +1,360 @@
+/**
+ * @file expExtract.c
+ *
+ * Time-stamp: "2012-03-04 09:16:59 bkorb"
+ *
+ * This module implements a file extraction function.
+ *
+ * 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 char const*
+load_extract_file(char const* pzNewFile);
+
+static SCM
+mk_empty_text(char const* pzStart, char const* pzEnd, SCM def);
+
+static SCM
+get_text(char const* pzText, char const* pzStart, char const* pzEnd, SCM def);
+/* = = = END-STATIC-FORWARD = = = */
+
+/**
+ * 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 (outfile_time < stbf.st_mtime)
+ outfile_time = stbf.st_mtime;
+
+ 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 (dep_fp != NULL)
+ add_source_file(pzNewFile);
+ }
+
+ return pzText;
+
+ bad_return:
+
+ AGFREE((void*)pzFile);
+ pzFile = NULL;
+ AGFREE((void*)pzText);
+ pzText = NULL;
+
+ return pzText;
+}
+
+
+/**
+ * Could not find the file or could not find the markers.
+ * Either way, emit an empty enclosure.
+ */
+static SCM
+mk_empty_text(char const* pzStart, char const* pzEnd, SCM def)
+{
+ size_t mlen = strlen(pzStart) + strlen(pzEnd) + 3;
+ char* pzOut;
+
+ if (! AG_SCM_STRING_P(def)) {
+ pzOut = ag_scribble(mlen);
+ sprintf(pzOut, LINE_CONCAT3_FMT+3, pzStart, pzEnd);
+
+ } else {
+ char const * pzDef = ag_scm2zchars(def, "dft extr str");
+ mlen += AG_SCM_STRLEN(def) + 1;
+ pzOut = ag_scribble(mlen);
+ sprintf(pzOut, LINE_CONCAT3_FMT, pzStart, pzDef, pzEnd);
+ }
+
+ return AG_SCM_STR02SCM(pzOut);
+}
+
+
+/*
+ * If we got it, emit it.
+ */
+static SCM
+get_text(char const* pzText, char const* pzStart, char const* pzEnd, SCM def)
+{
+ char const* pzS = strstr(pzText, pzStart);
+ char const* pzE;
+
+ if (pzS == NULL)
+ return mk_empty_text(pzStart, pzEnd, def);
+
+ pzE = strstr(pzS, pzEnd);
+ if (pzE == NULL)
+ return mk_empty_text(pzStart, pzEnd, def);
+
+ pzE += strlen(pzEnd);
+
+ return AG_SCM_STR2SCM(pzS, (size_t)(pzE - pzS));
+}
+
+
+/*=gfunc extract
+ *
+ * what: extract text from another file
+ * general_use:
+ * exparg: file-name, name of file with text
+ * exparg: marker-fmt, format for marker text
+ * exparg: caveat, warn about changing marker, opt
+ * exparg: default, default initial text, opt
+ *
+ * doc:
+ *
+ * This function is used to help construct output files that may contain
+ * text that is carried from one version of the output to the next.
+ *
+ * The first two arguments are required, the second are optional:
+ *
+ * @itemize @bullet
+ * @item
+ * The @code{file-name} argument is used to name the file that
+ * contains the demarcated text.
+ * @item
+ * The @code{marker-fmt} is a formatting string that is used to construct
+ * the starting and ending demarcation strings. The sprintf function is
+ * given the @code{marker-fmt} with two arguments. The first is either
+ * "START" or "END". The second is either "DO NOT CHANGE THIS COMMENT"
+ * or the optional @code{caveat} argument.
+ * @item
+ * @code{caveat} is presumed to be absent if it is the empty string
+ * (@code{""}). If absent, ``DO NOT CHANGE THIS COMMENT'' is used
+ * as the second string argument to the @code{marker-fmt}.
+ * @item
+ * When a @code{default} argument is supplied and no pre-existing text
+ * is found, then this text will be inserted between the START and END
+ * markers.
+ * @end itemize
+ *
+ * @noindent
+ * The resulting strings are presumed to be unique within
+ * the subject file. As a simplified example:
+ *
+ * @example
+ * [+ (extract "fname" "// %s - SOMETHING - %s" ""
+ * "example default") +]
+ * @end example
+ * @noindent
+ * will result in the following text being inserted into the output:
+ *
+ * @example
+ * // START - SOMETHING - DO NOT CHANGE THIS COMMENT
+ * example default
+ * // END - SOMETHING - DO NOT CHANGE THIS COMMENT
+ * @end example
+ *
+ * @noindent
+ * The ``@code{example default}'' string can then be carried forward to
+ * the next generation of the output, @strong{@i{provided}} the output
+ * is not named "@code{fname}" @i{and} the old output is renamed to
+ * "@code{fname}" before AutoGen-eration begins.
+ *
+ * @table @strong
+ * @item NB:
+ * You can set aside previously generated source files inside the pseudo
+ * macro with a Guile/scheme function, extract the text you want to keep
+ * with this extract function. Just remember you should delete it at the
+ * end, too. Here is an example from my Finite State Machine generator:
+ *
+ * @example
+ * [+ AutoGen5 Template -*- Mode: text -*-
+ * h=%s-fsm.h c=%s-fsm.c
+ * (shellf
+ * "test -f %1$s-fsm.h && mv -f %1$s-fsm.h .fsm.head
+ * test -f %1$s-fsm.c && mv -f %1$s-fsm.c .fsm.code" (base-name))
+ * +]
+ * @end example
+ *
+ * This code will move the two previously produced output files to files
+ * named ".fsm.head" and ".fsm.code". At the end of the 'c' output
+ * processing, I delete them.
+ *
+ * @item also NB:
+ * This function presumes that the output file ought to be editable so
+ * that the code between the @code{START} and @code{END} marks can be edited
+ * by the template user. Consequently, when the @code{(extract ...)} function
+ * is invoked, if the @code{writable} option has not been specified, then
+ * it will be set at that point. If this is not the desired behavior, the
+ * @code{--not-writable} command line option will override this.
+ * Also, you may use the guile function @code{(chmod "file" mode-value)}
+ * to override whatever AutoGen is using for the result mode.
+ * @end table
+=*/
+SCM
+ag_scm_extract(SCM file, SCM marker, SCM caveat, SCM def)
+{
+ char const * pzStart;
+ char const * pzEnd;
+ char const * pzText;
+
+ if (! AG_SCM_STRING_P(file) || ! AG_SCM_STRING_P(marker))
+ return SCM_UNDEFINED;
+
+ pzText = load_extract_file(ag_scm2zchars(file, "extr file"));
+
+ {
+ char const * pzMarker = ag_scm2zchars(marker, "marker");
+ char const * pzCaveat = EXTRACT_CAVEAT;
+
+ if (AG_SCM_STRING_P(caveat) && (AG_SCM_STRLEN(caveat) > 0))
+ pzCaveat = ag_scm2zchars(caveat, "caveat");
+
+ pzStart = aprf(pzMarker, EXTRACT_START, pzCaveat);
+ pzEnd = aprf(pzMarker, EXTRACT_END, pzCaveat);
+ }
+
+ {
+ SCM res;
+
+ if (pzText == NULL)
+ res = mk_empty_text(pzStart, pzEnd, def);
+ else res = get_text(pzText, pzStart, pzEnd, def);
+
+ AGFREE((void*)pzStart);
+ AGFREE((void*)pzEnd);
+ return res;
+ }
+}
+
+
+/*=gfunc find_file
+ *
+ * what: locate a file in the search path
+ * exparg: file-name, name of file with text
+ * exparg: @suffix @ file suffix to try, too @ opt @
+ *
+ * doc:
+ *
+ * AutoGen has a search path that it uses to locate template and definition
+ * files. This function will search the same list for @file{file-name}, both
+ * with and without the @file{.suffix}, if provided.
+=*/
+SCM
+ag_scm_find_file(SCM file, SCM suffix)
+{
+ SCM res = SCM_UNDEFINED;
+
+ if (! AG_SCM_STRING_P(file))
+ scm_wrong_type_arg(FIND_FILE_NAME, 1, file);
+
+ {
+ char z[ AG_PATH_MAX+1 ];
+ char const * pz = ag_scm2zchars(file, "file-name");
+
+ /*
+ * The suffix is optional. If provided, it will be a string.
+ */
+ if (AG_SCM_STRING_P(suffix)) {
+ char* apz[2];
+ apz[0] = (char *)ag_scm2zchars(suffix, "file suffix");
+ apz[1] = NULL;
+ if (SUCCESSFUL(find_file(pz, z, (char const **)apz, NULL)))
+ res = AG_SCM_STR02SCM(z);
+
+ } else if (SUCCESSFUL(find_file(pz, z, NULL, NULL)))
+ res = AG_SCM_STR02SCM(z);
+ }
+
+ return res;
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/expExtract.c */