/* * expMake.c * $Id: expMake.c,v 4.11 2007/07/04 20:51:11 bkorb Exp $ * * Time-stamp: "2007-07-04 11:19:20 bkorb" * Last Committed: $Date: 2007/07/04 20:51:11 $ * * This module implements Makefile construction functions. * * This file is part of AutoGen. * AutoGen copyright (c) 1992-2007 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 . */ /*=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 a backslash, semi-colon, * conjunction operator or pipe. 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 one of the excepted operators, then a space and * backslash is added. * * @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{$()} 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. * @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 ) { tSCC zNl[] = " ; \\\n"; SCM res; char* pzText = ag_scm2zchars( text, "GPL line prefix" ); char tabch; size_t sz = strlen( pzText ) + 2; /* * skip all blank lines and other initial white space * in the source string. */ if (! isspace( *pzText )) tabch = '\t'; else { while (isspace( *++pzText )) ; tabch = (pzText[-1] == '\t') ? NUL : '\t'; } if (*pzText == NUL) return AG_SCM_STR02SCM(zNil); { char* pz = strchr( pzText, '\n' ); size_t inc = ((*pzText == '\t') ? 0 : 1) + sizeof( zNl ) - 1; while (pz != NULL) { sz += inc; pz = strchr( pz+1, '\n' ); } pz = strchr( pzText, '$' ); while (pz != NULL) { sz++; pz = strchr( pz + 1, '$' ); } } { char* pzRes = AGALOC( sz, "makefile text" ); char* pzOut = pzRes; char* pzScn = pzText; /* * Force the initial line to start with a tab. */ *(pzOut++) = '\t'; for (;;) { char ch = *(pzScn++); switch (ch) { case '\n': /* * Backup past trailing white space (other than newline). */ while (isspace( pzOut[ -1 ]) && (pzOut[ -1 ] != '\n')) pzOut--; /* * Skip over empty lines, but leave leading white space * on the next non-empty line. */ { char* pz = pzScn; while (isspace( *pz )) { if (*(pz++) == '\n') pzScn = pz; } } /* * The final newline is dropped. */ if (*pzScn == NUL) goto copy_done; switch (pzOut[-1]) { case '\\': /* * The newline is already escaped, so don't * insert our extra command termination. */ *(pzOut++) = '\n'; 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 ';': /* * Whatever the reason for a final '|' or ';' we will not * add a semi-colon after it. */ strcpy( pzOut, zNl + 2 ); pzOut += sizeof( zNl ) - 3; break; default: append_statement_end: /* * Terminate the current command and escape the newline. */ strcpy( pzOut, zNl ); pzOut += sizeof( zNl ) - 1; } /* * 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++; } break; case NUL: goto copy_done; case '$': /* * Double the dollar -- IFF it is not a makefile macro */ switch (*pzScn) { case '(': case '*': case '@': case '<': case '%': case '?': break; case '$': /* * Another special 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. */ pzScn++; *(pzOut++) = '$'; *(pzOut++) = '$'; /* two down, two to go */ /* FALLTHROUGH */ default: *(pzOut++) = '$'; } /* FALLTHROUGH */ default: *(pzOut++) = ch; } } copy_done:; sz = (pzOut - pzRes); res = AG_SCM_STR2SCM( pzRes, sz ); AGFREE( pzRes ); } return res; } /* * Local Variables: * mode: C * c-file-style: "stroustrup" * indent-tabs-mode: nil * End: * end of agen5/expMake.c */