mirror of
https://https.git.savannah.gnu.org/git/libtool.git
synced 2026-01-28 18:34:52 +00:00
rework the quoting so you can emit eval-ed strings, too. write a common routing to handle the dlopen option.
564 lines
15 KiB
C
564 lines
15 KiB
C
/* ltmain.c - C implementation of GNU Libtool
|
|
*
|
|
* Copyright (C) 1998-2000 Free Software Foundation, Inc.
|
|
*
|
|
* This program 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 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program 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 software; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
|
* Boston, MA 02111-1307 USA
|
|
*
|
|
* As a special exception to the GNU General Public License, if you
|
|
* distribute this file as part of a program that contains a
|
|
* configuration script generated by Autoconf, you may include it under
|
|
* the same distribution terms that you use for the rest of that program.
|
|
*/
|
|
|
|
#include <stdio.h> /* printf */
|
|
#include <stdlib.h> /* exit */
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
#include <sys/wait.h>
|
|
#include <signal.h>
|
|
|
|
#include "ltopts.h"
|
|
#include "ltstr.h"
|
|
#include "ltconfig.x"
|
|
|
|
|
|
tSCC zFatalConfig[] =
|
|
"Fatal configuration error.\n\
|
|
ltmain: not configured to build any kind of library\n\
|
|
See the docs for more information.\n";
|
|
|
|
tSCC zBadOpt[] = "%s ERROR: option `--%s' invalid in this context\n";
|
|
tSCC zBadMode[] = "%s ERROR: could not infer mode\n";
|
|
tSCC zUnkn[] = "** UNKNOWN **";
|
|
char zShell[ 256 ];
|
|
|
|
static const char* pz_cmd_name = (char*)NULL;
|
|
int scriptStatus = EXIT_SUCCESS;
|
|
int signalReceived = 0;
|
|
|
|
/* BEGIN-STATIC-FORWARD */
|
|
LOCAL void
|
|
handleSignal LT_PARAMS((
|
|
int signo ));
|
|
|
|
LOCAL void
|
|
validateMode LT_PARAMS((
|
|
int argct,
|
|
char** argvec ));
|
|
|
|
/* END-STATIC-FORWARD */
|
|
|
|
int
|
|
main( argc, argv )
|
|
int argc;
|
|
char** argv;
|
|
{
|
|
pzHost = getenv( "host" );
|
|
if (pzHost == (char*)NULL)
|
|
pzHost = zUnkn;
|
|
|
|
signal( SIGPIPE, handleSignal );
|
|
signal( SIGBUS, handleSignal );
|
|
signal( SIGSEGV, handleSignal );
|
|
signal( SIGCHLD, SIG_DFL ); /* BOGUS SETTINGS CAN BE INHERITED */
|
|
|
|
/*
|
|
* Process the options, removing them from the arg list.
|
|
* Make sure the resulting state is sane.
|
|
*/
|
|
{
|
|
int ct = optionProcess( &libtoolOptions, argc, argv );
|
|
argc -= ct;
|
|
argv += ct;
|
|
if (! HAVE_OPT( MODE ))
|
|
validateMode( argc, argv );
|
|
}
|
|
|
|
switch (OPT_VALUE_MODE) {
|
|
case MODE_EXECUTE:
|
|
case MODE_CLEAN:
|
|
case MODE_FINISH:
|
|
case MODE_INSTALL:
|
|
case MODE_UNINSTALL:
|
|
/*
|
|
* Options prohibited for all states except link & compile
|
|
*/
|
|
if (HAVE_OPT( OUTPUT_FILE )) {
|
|
fprintf( stderr, zBadOpt, libtoolOptions.pzProgName, "output-file" );
|
|
USAGE( EXIT_FAILURE );
|
|
/* NOTREACHED */
|
|
}
|
|
if (HAVE_OPT( STATIC )) {
|
|
fprintf( stderr, zBadOpt, libtoolOptions.pzProgName, "static" );
|
|
USAGE( EXIT_FAILURE );
|
|
/* NOTREACHED */
|
|
}
|
|
if (HAVE_OPT( DYNAMIC )) {
|
|
fprintf( stderr, zBadOpt, libtoolOptions.pzProgName, "dynamic" );
|
|
USAGE( EXIT_FAILURE );
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
/*
|
|
* dlopen is allowed for execute, but nothing else. So, for
|
|
* other modes, fall through and check for DLOPEN.
|
|
*/
|
|
if (OPT_VALUE_MODE == MODE_EXECUTE)
|
|
break;
|
|
/* FALLTHROUGH */
|
|
|
|
case MODE_LINK:
|
|
case MODE_COMPILE:
|
|
if (HAVE_OPT( DLOPEN )) {
|
|
fprintf( stderr, zBadOpt, libtoolOptions.pzProgName, "dlopen" );
|
|
USAGE( EXIT_FAILURE );
|
|
/* NOTREACHED */
|
|
}
|
|
break;
|
|
|
|
case MODE_ECHO:
|
|
/*
|
|
* We ignore all conflicts for echo mode
|
|
*/
|
|
for (;;) {
|
|
fputs( *(argv++), stdout );
|
|
if (--argc <= 0)
|
|
break;
|
|
fputc( ' ', stdout );
|
|
}
|
|
|
|
fputc( '\n', stdout );
|
|
return EXIT_SUCCESS;
|
|
|
|
default:
|
|
fprintf( stderr, zBadMode, libtoolOptions.pzProgName );
|
|
USAGE( EXIT_FAILURE );
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
pz_cmd_name = argv[0];
|
|
|
|
{
|
|
emitScriptProc* pEP = ap_emitProc[ OPT_VALUE_MODE ];
|
|
if (pEP == NULL) {
|
|
fprintf( stderr, "We're in the wrong mode: %d\n",
|
|
OPT_VALUE_MODE );
|
|
USAGE( EXIT_FAILURE );
|
|
}
|
|
(*pEP)( argc, argv );
|
|
}
|
|
|
|
return scriptStatus;
|
|
}
|
|
|
|
|
|
/*
|
|
* Handle SIGPIPE and SIGSEGV by telling emitScript() to call close.
|
|
*/
|
|
LOCAL void
|
|
handleSignal( signo )
|
|
int signo; /*end-decl*/
|
|
{
|
|
signalReceived = signo;
|
|
}
|
|
|
|
|
|
/*
|
|
* Close script is called either right after a signal is handled above,
|
|
* or when we have finished writing to a popen-ed shell. If we get
|
|
* a signal on writes to stdout (rather than a popen-ed shell), then
|
|
* this routine will be called, too. However, it will choke. If that
|
|
* is a problem, then check `fp' for being stdout.
|
|
*/
|
|
EXPORT void
|
|
closeScript( fp )
|
|
FILE* fp; /*end-decl*/
|
|
{
|
|
int scriptStatus = pclose( fp );
|
|
|
|
if (scriptStatus == -1) {
|
|
fprintf( stderr, "%s EXIT FAILURE: pclose returned %d (%s)\n",
|
|
libtoolOptions.pzProgName, errno, strerror( errno ));
|
|
scriptStatus = EXIT_FAILURE;
|
|
return;
|
|
}
|
|
|
|
if (! WIFEXITED( scriptStatus )) {
|
|
fprintf( stderr, "%s EXIT FAILURE: %s did not exit - code %x\n",
|
|
libtoolOptions.pzProgName, pz_cmd_name, scriptStatus );
|
|
scriptStatus = EXIT_FAILURE;
|
|
return;
|
|
}
|
|
|
|
scriptStatus = WEXITSTATUS(scriptStatus);
|
|
if (scriptStatus != 0) {
|
|
fprintf( stderr, "%s BAD EXIT: %s exited %d\n",
|
|
libtoolOptions.pzProgName, pz_cmd_name, scriptStatus );
|
|
return;
|
|
}
|
|
|
|
switch (signalReceived) {
|
|
case 0: /* no signal received */
|
|
case SIGPIPE:
|
|
/*
|
|
* SIGPIPE just means the script exited before we could
|
|
* write out everything. This is terribly ugly, but correct. Ick.
|
|
*/
|
|
scriptStatus = EXIT_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
fprintf( stderr, "%s WRONG SIGNAL: %d\n",
|
|
libtoolOptions.pzProgName, signalReceived );
|
|
scriptStatus = EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
|
|
LOCAL void
|
|
validateMode( argct, argvec )
|
|
int argct;
|
|
char** argvec; /*end-decl*/
|
|
{
|
|
char* pzCmd = *argvec;
|
|
char* pzEnd = pzCmd + strlen( pzCmd );
|
|
|
|
if (HAVE_OPT( MODE ))
|
|
return;
|
|
|
|
/*
|
|
* IF we don't even have two letters in our command,
|
|
* THEN we won't try to figure out what the command is supposed to be.
|
|
* Tandem->Compaq->HP's C compiler fails this test, however.
|
|
*/
|
|
if ((pzEnd - pzCmd) < 2)
|
|
return;
|
|
|
|
do {
|
|
if ( ((pzEnd[-2] == 'c') && (pzEnd[-1] == 'c'))
|
|
|| ((pzEnd[-2] == '+') && (pzEnd[-1] == '+'))
|
|
|| (strncmp( pzCmd, "gcc", 3 ) == 0)
|
|
|| (strstr( pzCmd, "-gcc" ) != (char*)NULL) )
|
|
break;
|
|
|
|
if ( (strcmp( pzEnd-2, "db" ) == 0)
|
|
|| (strcmp( pzEnd-3, "dbx" ) == 0)
|
|
|| (strcmp( pzEnd-6, "strace" ) == 0)
|
|
|| (strcmp( pzEnd-5, "truss" ) == 0) ) {
|
|
SET_OPT_MODE( "execute" );
|
|
return;
|
|
}
|
|
|
|
if ( ((pzEnd[-2] == 'c') && (pzEnd[-1] == 'p'))
|
|
|| ((pzEnd[-2] == 'm') && (pzEnd[-1] == 'v'))
|
|
|| (strstr( pzCmd, "install" ) != (char*)NULL) ) {
|
|
SET_OPT_MODE( "install" );
|
|
return;
|
|
}
|
|
|
|
return;
|
|
} while (0);
|
|
|
|
while (--argct > 0) {
|
|
if (strcmp( *++argvec, "-c" ) == 0) {
|
|
SET_OPT_MODE( "compile" );
|
|
return;
|
|
}
|
|
}
|
|
|
|
SET_OPT_MODE( "link" );
|
|
}
|
|
|
|
|
|
/*
|
|
* This routine assumes that it is emitting text contained within
|
|
* double quotes for collection by the shell. Therefore, the only
|
|
* characters to worry about are $ ` \ and ".
|
|
*/
|
|
EXPORT void
|
|
emitDoubleQuoted( pzArg, outFp )
|
|
tCC* pzArg;
|
|
FILE* outFp; /*end-decl*/
|
|
{
|
|
for (;;) {
|
|
char ch = *(pzArg++);
|
|
switch (ch) {
|
|
case NUL:
|
|
return;
|
|
|
|
case '$':
|
|
case '\\':
|
|
case '`':
|
|
case '"':
|
|
fputc( '\\', outFp );
|
|
/* FALLTHROUGH */
|
|
|
|
default:
|
|
fputc( ch, outFp );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* This routine assumes that it is emitting text contained within
|
|
* single quotes for collection by the shell. Therefore, the only
|
|
* character to worry about is the single quote (apostrophe). We
|
|
* close the current string, emit an escaped apostrophe and then
|
|
* return to single quote mode.
|
|
*/
|
|
EXPORT void
|
|
emitRawQuoted( pzArg, outFp )
|
|
tCC* pzArg;
|
|
FILE* outFp; /*end-decl*/
|
|
{
|
|
for (;;) {
|
|
char ch = *(pzArg++);
|
|
switch (ch) {
|
|
case '\0':
|
|
return;
|
|
|
|
case '\'':
|
|
fputs( "'\\'", outFp );
|
|
while (*pzArg == '\'') {
|
|
fputs( "\\'", outFp );
|
|
pzArg++;
|
|
}
|
|
/* FALLTHROUGH */
|
|
|
|
default:
|
|
fputc( ch, outFp );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
EXPORT void
|
|
emitShellArg( pzArg, outFp, qChar )
|
|
tCC* pzArg;
|
|
FILE* outFp;
|
|
char qChar; /*end-decl*/
|
|
{
|
|
tSCC zMetas[] = "<>{}()[]|&^#~*;?$`'\"\\ \t\v\f\r\n";
|
|
tSCC zSpec[] = "$`\\\"";
|
|
|
|
/*
|
|
* Force a quoted string for the empty string
|
|
*/
|
|
if (*pzArg == NUL) {
|
|
if (qChar == NUL)
|
|
qChar = '\'';
|
|
fputc( qChar, outFp );
|
|
fputc( qChar, outFp );
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* IF there are no special characters in the string,
|
|
* THEN just emit it raw
|
|
*/
|
|
if (strpbrk( pzArg, zMetas ) == (char*)NULL) {
|
|
fputs( pzArg, outFp );
|
|
return;
|
|
}
|
|
|
|
switch (qChar) {
|
|
case NUL:
|
|
/*
|
|
* We are free to quote with either " or '
|
|
* Try for a simple double quoted string, then fall back
|
|
* to a raw quoted string.
|
|
*/
|
|
if (strpbrk( pzArg, zSpec ) == (char*)NULL) {
|
|
fprintf( outFp, "\"%s\"", pzArg );
|
|
break;
|
|
}
|
|
/* FALLTHROUGH -- try for a ' quoted string */
|
|
|
|
case '\'':
|
|
if (strchr( pzArg, '\'' ) == (char*)NULL) {
|
|
fprintf( outFp, "'%s'", pzArg );
|
|
}
|
|
else {
|
|
fputc( '\'', outFp );
|
|
emitRawQuoted( pzArg, outFp );
|
|
fputc( '\'', outFp );
|
|
}
|
|
break;
|
|
|
|
case '"':
|
|
fputc( '"', outFp );
|
|
emitDoubleQuoted( pzArg, outFp );
|
|
fputc( '"', outFp );
|
|
}
|
|
}
|
|
|
|
|
|
EXPORT void
|
|
emitScript( argc, argv )
|
|
int argc;
|
|
char** argv; /*end-decl*/
|
|
{
|
|
tSCC zDbgFmt[] = "set -x\n";
|
|
tSCC zQuiet[] = "run=\nshow=%s\n";
|
|
tSCC zDynFmt[] = "build_libtool_libs=%s\n";
|
|
tSCC zStatic[] = "build_old_libs=%s\n";
|
|
tSCC zDupDeps[] = "duplicate_deps=%s\n";
|
|
tSCC zModeName[] = "modename='%s: %s'\n";
|
|
tSCC zMode[] = "mode='%s'\n";
|
|
tSCC zCmdName[] = "nonopt='%s'\nset --";
|
|
|
|
/*
|
|
* When we emit our script, we want the interpreter to invoke *US*
|
|
* if echo does not work right.
|
|
*/
|
|
tSCC zChkEcho[] =
|
|
"\n\nif test \"X`($echo '\\t') 2>/dev/null`\" = 'X\\t'\n\
|
|
then :\n\
|
|
else echo='%s --echo --' ; fi\n";
|
|
|
|
FILE* fp = HAVE_OPT( DRY_RUN ) ? stdout : popen( pz_shell, "w" );
|
|
if (fp == (FILE*)NULL) {
|
|
tSCC zErr[] = "%s error: fs error %d (%s) on popen( \"%s\",\"w\")\n";
|
|
fprintf( stderr, zErr, libtoolOptions.pzProgPath, errno,
|
|
strerror( errno ), pz_shell );
|
|
exit( EXIT_FAILURE );
|
|
}
|
|
|
|
# define CKSERV if (signalReceived != 0) { \
|
|
closeScript( fp ); if (scriptStatus == 0) scriptStatus = EXIT_FAILURE; \
|
|
return; }
|
|
|
|
# define CLOSEOK if (signalReceived != 0) { closeScript( fp ); return; }
|
|
|
|
/*
|
|
* Emit the default configuration set up at program configuration time
|
|
*/
|
|
fputs( z_ltconfig, fp );
|
|
CKSERV;
|
|
fputs( apz_mode_cmd[ 0 ], fp );
|
|
CKSERV;
|
|
fprintf( fp, zChkEcho, libtoolOptions.pzProgPath );
|
|
CKSERV;
|
|
|
|
fprintf( fp, zQuiet, HAVE_OPT( QUIET ) ? ":" : "\"$echo\"" );
|
|
CKSERV;
|
|
|
|
/*
|
|
* IF we have DYNAMIC or STATIC, then we override the configured
|
|
* values. We emitted the configured values with `z_ltconfig'.
|
|
*/
|
|
if (HAVE_OPT( DYNAMIC ))
|
|
fprintf( fp, zDynFmt, ENABLED_OPT( DYNAMIC ) ? "yes" : "no" );
|
|
if (HAVE_OPT( STATIC ))
|
|
fprintf( fp, zStatic, ENABLED_OPT( STATIC ) ? "yes" : "no" );
|
|
if (HAVE_OPT( PRESERVE_DUP_DEPS ))
|
|
fprintf( fp, zDupDeps, "yes" );
|
|
else
|
|
fprintf( fp, zDupDeps, "no" );
|
|
|
|
if (HAVE_OPT( DEBUG )) {
|
|
fprintf( stderr, "%s: enabling shell trace mode\n",
|
|
libtoolOptions.pzProgName );
|
|
fputs( zDbgFmt, fp );
|
|
}
|
|
CKSERV;
|
|
|
|
if (HAVE_OPT( DLOPEN )) {
|
|
emitDlopenOption( fp );
|
|
CKSERV;
|
|
}
|
|
|
|
/*
|
|
* Insert our modal stuff and one shell option processing dinkleberry
|
|
* that one of the command scripts depends upon.
|
|
*/
|
|
fprintf( fp, zModeName, libtoolOptions.pzProgName,
|
|
apzModeName[ OPT_VALUE_MODE ]);
|
|
CKSERV;
|
|
fprintf( fp, zMode, libtoolOptions.pzProgName );
|
|
CKSERV;
|
|
|
|
/*
|
|
* Emit the real command. The original shell script shifts off the
|
|
* command name before it realizes what it has done. We emulate
|
|
* that behavior by setting `nonopt' to the command name and inserting
|
|
* the remaining arguments as arguments via `set -- $@'.
|
|
*/
|
|
fprintf( fp, zCmdName, argv[0] );
|
|
CKSERV;
|
|
|
|
while (--argc > 0) {
|
|
fputc( ' ', fp );
|
|
emitShellArg( *(++argv), fp, NUL ); /* either single or double quotes */
|
|
CKSERV;
|
|
}
|
|
|
|
emitCommands( fp, apz_mode_cmd[ OPT_VALUE_MODE ]);
|
|
}
|
|
|
|
|
|
EXPORT void
|
|
emitDlopenOption( fp )
|
|
FILE* fp; /*end-decl*/
|
|
{
|
|
tSCC zDlOpt[] = "execute_dlfiles='";
|
|
|
|
int ct = STACKCT_OPT( DLOPEN );
|
|
char** al = STACKLST_OPT( DLOPEN );
|
|
fputs( zDlOpt, fp );
|
|
for (;;) {
|
|
emitRawQuoted( *(al++), fp );
|
|
if (--ct <= 0)
|
|
break;
|
|
fputc( ' ', fp ); /* between each value only */
|
|
}
|
|
fputs( "'\n", fp );
|
|
}
|
|
|
|
|
|
EXPORT void
|
|
emitCommands( fp, pzCmds )
|
|
FILE* fp;
|
|
tCC* pzCmds; /*end-decl*/
|
|
{
|
|
fputc( '\n', fp );
|
|
fflush( fp );
|
|
CKSERV;
|
|
|
|
/*
|
|
* Up to now, we are just initializing variables. Here, we write
|
|
* a large chunk of text to the pipe and the shell may exit before
|
|
* we are done. If that happens, we get a SIGPIPE. The `CLOSEOK'
|
|
* macro will detect that, call closeScript() and return so as to
|
|
* avoid segfaults and more SIGPIPEs.
|
|
*/
|
|
fputs( pzCmds, fp );
|
|
CLOSEOK;
|
|
|
|
if (fp != stdout)
|
|
closeScript( fp );
|
|
}
|
|
/*
|
|
* Local Variables:
|
|
* c-file-style: "stroustrup"
|
|
* indent-tabs-mode: nil
|
|
* tab-width: 4
|
|
* End:
|
|
* end of ltmain.c */
|