summaryrefslogtreecommitdiff
path: root/agen5/expMake.c
diff options
context:
space:
mode:
Diffstat (limited to 'agen5/expMake.c')
-rw-r--r--agen5/expMake.c429
1 files changed, 429 insertions, 0 deletions
diff --git a/agen5/expMake.c b/agen5/expMake.c
new file mode 100644
index 0000000..926b930
--- /dev/null
+++ b/agen5/expMake.c
@@ -0,0 +1,429 @@
+
+/**
+ * @file expMake.c
+ *
+ * Time-stamp: "2012-03-04 13:54:13 bkorb"
+ *
+ * This module implements Makefile construction functions.
+ *
+ * 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/>.
+ */
+
+/**
+ * Figure out how to handle the line continuation.
+ * If the line we just finished ends with a backslash, we're done.
+ * Just add the newline character. If it ends with a semi-colon or a
+ * doubled amphersand or doubled or-bar, then escape the newline with a
+ * backslash. If the line ends with one of the keywords "then", "in" or
+ * "else", also only add the escaped newline. Otherwise, add a
+ * semi-colon, backslash and newline.
+ *
+ * @param ppzi pointer to pointer to input text
+ * @param ppzo pointer to pointer to output text
+ * @param tabch line prefix (tab) character
+ * @param bol pointer to start of currently-being-output line
+ *
+ * @returns false to say the newline is dropped becase we're done
+ * true to say the line was appended with the newline.
+ */
+static bool
+handle_eol(char ** ppzi, char ** ppzo, char tabch, char * bol)
+{
+ char * pzScn = *ppzi;
+ char * pzOut = *ppzo;
+ int l_len = pzOut - bol;
+
+ /*
+ * Backup past trailing white space (other than newline).
+ */
+ while (IS_NON_NL_WHITE_CHAR(pzOut[-1]))
+ pzOut--;
+
+ /*
+ * Skip over empty lines, but leave leading white space
+ * on the next non-empty line.
+ */
+ {
+ char* pz = pzScn;
+ while (IS_WHITESPACE_CHAR(*pz)) {
+ if (*(pz++) == NL)
+ pzScn = pz;
+ }
+ }
+
+ /*
+ * The final newline is dropped.
+ */
+ if (*pzScn == NUL)
+ return false;
+
+ switch (pzOut[-1]) {
+ case '\\':
+ /*
+ * The newline is already escaped, so don't
+ * insert our extra command termination.
+ */
+ *(pzOut++) = NL;
+ break;
+
+ case '&':
+ /*
+ * A single ampersand is a backgrounded command. We must terminate
+ * those statements, but not statements conjoined with '&&'.
+ */
+ if ('&' != pzOut[-2])
+ goto append_statement_end;
+ /* FALLTHROUGH */
+
+ case '|':
+ case ';':
+ skip_semi_colon:
+ /*
+ * Whatever the reason for a final '|', '&' or ';',
+ * we will not add a semi-colon after it.
+ */
+ memcpy(pzOut, MAKE_SCRIPT_NL + 2, MAKE_SCRIPT_NL_LEN - 2);
+ pzOut += MAKE_SCRIPT_NL_LEN - 2;
+ break;
+
+ case 'n': // "then" or "in"
+ if (l_len < 3)
+ goto append_statement_end;
+
+ if (pzOut[-2] == 'i') {
+ if ((l_len == 3) || IS_WHITESPACE_CHAR(pzOut[-3]))
+ goto skip_semi_colon;
+ goto append_statement_end;
+ }
+
+ if ( (l_len < 5)
+ || ( (l_len > 5)
+ && ! IS_WHITESPACE_CHAR(pzOut[-5]) ))
+ goto append_statement_end;
+ if (strncmp(pzOut-4, HANDLE_EOL__THE, HANDLE_EOL__THE_LEN) == 0)
+ goto skip_semi_colon;
+ goto append_statement_end;
+
+ case 'e': // "else"
+ if ( (l_len < 5)
+ || ( (l_len > 5)
+ && ! IS_WHITESPACE_CHAR(pzOut[-5]) ))
+ goto append_statement_end;
+ if (strncmp(pzOut-4, HANDLE_EOL__ELS, HANDLE_EOL__ELS_LEN) == 0)
+ goto skip_semi_colon;
+ goto append_statement_end;
+
+ default:
+ append_statement_end:
+ /*
+ * Terminate the current command and escape the newline.
+ */
+ memcpy(pzOut, MAKE_SCRIPT_NL, MAKE_SCRIPT_NL_LEN);
+ pzOut += MAKE_SCRIPT_NL_LEN;
+ }
+
+ /*
+ * We have now started our next output line and there are still data.
+ * Indent with a tab, if called for. If we do insert a tab, then skip
+ * leading tabs on the line.
+ */
+ if (tabch) {
+ *(pzOut++) = tabch;
+ while (*pzScn == tabch) pzScn++;
+ }
+
+ *ppzi = pzScn;
+ *ppzo = pzOut;
+ return true;
+}
+
+/**
+ * Pass through untreated sedable lines. Sometimes it is just very useful
+ * to post-process Makefile files with sed(1) to clean it up.
+ *
+ * @param txt pointer to text. We skip initial white space.
+ * @param tab pointer to where we stash the tab character to use
+ * @returns true to say this was a sed line and was emitted,
+ * false to say it was not and needs to be copied out.
+ */
+static bool
+handle_sed_expr(char ** src_p, char ** out_p)
+{
+ char * src = *src_p;
+ char * out = *out_p;
+
+ switch (src[1]) {
+ case 'i':
+ if (strncmp(src+2, HANDLE_SED_IFNDEF, HANDLE_SED_IFNDEF_LEN) == 0)
+ break;
+ if (strncmp(src+2, HANDLE_SED_IFDEF, HANDLE_SED_IFDEF_LEN) == 0)
+ break;
+ return false;
+
+ case 'e':
+ if (strncmp(src+2, HANDLE_SED_ELSE, HANDLE_SED_ELSE_LEN) == 0)
+ break;
+ if (strncmp(src+2, HANDLE_SED_ENDIF, HANDLE_SED_ENDIF_LEN) == 0)
+ break;
+ /* FALLTHROUGH */
+ default:
+ return false;
+ }
+
+ {
+ char * p = BRK_NEWLINE_CHARS(src);
+ size_t l;
+ if (*p == NL) /* do not skip NUL */
+ p++;
+ l = p - src;
+ memcpy(out, src, l);
+ *src_p = src + l;
+ *out_p = out + l;
+ }
+
+ return true;
+}
+
+/**
+ * Compute a maximal size for the output script. Leading and trailing white
+ * space are trimmed. Dollar characters will likely be doubled and newlines
+ * may get as many as MAKE_SCRIPT_NL_LEN characters inserted. Make sure
+ * there's space.
+ *
+ * @param txt pointer to text. We skip initial white space.
+ * @param tab pointer to where we stash the tab character to use
+ * @returns the maximum number of bytes required to store result.
+ */
+static size_t
+script_size(char ** txt_p, char * tab)
+{
+ char * txt = *txt_p;
+ char * ptxte;
+ size_t sz = 0;
+
+ /*
+ * skip all blank lines and other initial white space
+ * in the source string.
+ */
+ if (! IS_WHITESPACE_CHAR(*txt))
+ *tab = TAB;
+ else {
+ txt = SPN_WHITESPACE_CHARS(txt + 1);
+ *tab = (txt[-1] == TAB) ? NUL : TAB;
+ }
+
+ /*
+ * Do nothing with empty input.
+ */
+ if (*txt == NUL)
+ return 0;
+
+ /*
+ * "txt" is now our starting point. Do not modify it any more.
+ */
+ *txt_p = txt;
+
+ for (ptxte = txt - 1;;) {
+ ptxte = BRK_MAKE_SCRIPT_CHARS(ptxte+1);
+ if (*ptxte == NUL)
+ break;
+ sz += (*ptxte == '$') ? 1 : MAKE_SCRIPT_NL_LEN;
+ }
+
+ ptxte = SPN_WHITESPACE_BACK(txt, ptxte);
+ *ptxte = NUL;
+ sz += (ptxte - txt);
+ return sz;
+}
+
+/*=gfunc makefile_script
+ *
+ * what: create makefile script
+ * general_use:
+ *
+ * exparg: text, the text of the script
+ *
+ * doc:
+ * This function will take ordinary shell script text and reformat it
+ * so that it will work properly inside of a makefile shell script.
+ * Not every shell construct can be supported; the intent is to have
+ * most ordinary scripts work without much, if any, alteration.
+ *
+ * The following transformations are performed on the source text:
+ *
+ * @enumerate
+ * @item
+ * Trailing whitespace on each line is stripped.
+ *
+ * @item
+ * Except for the last line, the string, " ; \\" is appended to the end of
+ * every line that does not end with certain special characters or keywords.
+ * Note that this will mutilate multi-line quoted strings, but @command{make}
+ * renders it impossible to use multi-line constructs anyway.
+ *
+ * @item
+ * If the line ends with a backslash, it is left alone.
+ *
+ * @item
+ * If the line ends with a semi-colon, conjunction operator, pipe (vertical
+ * bar) or one of the keywords "then", "else" or "in", then a space and a
+ * backslash is added, but no semi-colon.
+ *
+ * @item
+ * The dollar sign character is doubled, unless it immediately precedes an
+ * opening parenthesis or the single character make macros '*', '<', '@@',
+ * '?' or '%'. Other single character make macros that do not have enclosing
+ * parentheses will fail. For shell usage of the "$@@", "$?" and "$*"
+ * macros, you must enclose them with curly braces, e.g., "$@{?@}".
+ * The ksh construct @code{$(<command>)} will not work. Though some
+ * @command{make}s accept @code{$@{var@}} constructs, this function will
+ * assume it is for shell interpretation and double the dollar character.
+ * You must use @code{$(var)} for all @command{make} substitutions.
+ *
+ * @item
+ * Double dollar signs are replaced by four before the next character
+ * is examined.
+ *
+ * @item
+ * Every line is prefixed with a tab, unless the first line
+ * already starts with a tab.
+ *
+ * @item
+ * The newline character on the last line, if present, is suppressed.
+ *
+ * @item
+ * Blank lines are stripped.
+ *
+ * @item
+ * Lines starting with "@@ifdef", "@@ifndef", "@@else" and "@@endif" are
+ * presumed to be autoconf "sed" expression tags. These lines will be
+ * emitted as-is, with no tab prefix and no line splicing backslash.
+ * These lines can then be processed at configure time with
+ * @code{AC_CONFIG_FILES} sed expressions, similar to:
+ *
+ * @example
+ * sed "/^@@ifdef foo/d;/^@@endif foo/d;/^@@ifndef foo/,/^@@endif foo/d"
+ * @end example
+ * @end enumerate
+ *
+ * @noindent
+ * This function is intended to be used approximately as follows:
+ *
+ * @example
+ * $(TARGET) : $(DEPENDENCIES)
+ * <+ (out-push-new) +>
+ * ....mostly arbitrary shell script text....
+ * <+ (makefile-script (out-pop #t)) +>
+ * @end example
+=*/
+SCM
+ag_scm_makefile_script(SCM text_scm)
+{
+ char * res_str; /*@< result string */
+ char * out; /*@< output scanning ptr */
+ char * bol; /*@< start of last output line */
+ char tabch; /*@< char to use for start-of-line tab */
+
+ char * text = ag_scm2zchars(text_scm, "make script");
+ size_t sz = script_size(&text, &tabch);
+
+ if (sz == 0)
+ return AG_SCM_STR02SCM(zNil);
+
+ bol = out = res_str = ag_scribble(sz);
+
+ /*
+ * Force the initial line to start with a real tab.
+ */
+ *(out++) = TAB;
+
+ for (;;) {
+ char * p = BRK_MAKE_SCRIPT_CHARS(text);
+ size_t l = p - text;
+ if (l > 0) {
+ memcpy(out, text, l);
+ text = p;
+ out += l;
+ }
+
+ /*
+ * "text" now points to one of three characters:
+ * a newline, a dollar or a NUL byte.
+ */
+ if (*text == NUL)
+ break;
+
+ if (*text == NL) {
+ if (! handle_eol(&text, &out, tabch, bol))
+ break;
+
+ bol = out;
+
+ /*
+ * As a special "hack", if a line starts with "@ifdef", "@ifndef",
+ * "@else" or "@endif", then we assume post processing sed will
+ * fix it up. Those lines get left alone.
+ */
+ if (*text == '@') {
+ if (handle_sed_expr(&text, &out))
+ bol = out;
+ }
+
+ } else {
+ /*
+ * Quadruple a double dollar, leave alone make-interesting
+ * dollars, and double it otherwise.
+ */
+ switch (text[1]) {
+ case '(': case '*': case '@': case '<': case '%': case '?':
+ /* one only */
+ break;
+
+ case '$':
+ /*
+ * $$ in the shell means process id. Avoid having to do a
+ * backward scan on the second '$' by handling the next '$'
+ * now. We get FOUR '$' chars.
+ */
+ text++;
+ *(out++) = '$';
+ *(out++) = '$';
+ *(out++) = '$';
+ /* quadruple */
+ break;
+
+ default:
+ *(out++) = '$'; /* double */
+ }
+
+ *(out++) = *(text++);
+ }
+ }
+
+ {
+ SCM res = AG_SCM_STR2SCM(res_str, out - res_str);
+ return res;
+ }
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/expMake.c */