/* 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 /* printf */ #include /* exit */ #include #include #include #include #include #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; /* * Handle SIGPIPE and SIGSEGV by telling emitScript() to call close. */ void handleSignal( int signo ) { 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. */ void closeScript( FILE* fp ) { int scriptStatus = pclose( fp ); if (scriptStatus == -1) { fprintf( stderr, "%s EXIT FAILURE: pclose returned %d (%s)\n", ltmainOptions.pzProgName, errno, strerror( errno )); scriptStatus = EXIT_FAILURE; return; } if (! WIFEXITED( scriptStatus )) { fprintf( stderr, "%s EXIT FAILURE: %s did not exit - code %x\n", ltmainOptions.pzProgName, pz_cmd_name, scriptStatus ); scriptStatus = EXIT_FAILURE; return; } scriptStatus = WEXITSTATUS(scriptStatus); if (scriptStatus != 0) { fprintf( stderr, "%s BAD EXIT: %s exited %d\n", ltmainOptions.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", ltmainOptions.pzProgName, signalReceived ); scriptStatus = EXIT_FAILURE; } } /* * The usage text varies a bit, depending on operational mode. * Therefore, catch the usage call, do the variation and then * finish up with the library routine. */ void modalUsage( pOpts, exitCode ) tOptions* pOpts; int exitCode; { tSCC zFmt[] = "%s --mode=%s"; char z[ 256 ]; if (HAVE_OPT( MODE )) { sprintf( z, zFmt, pOpts->pzProgName, pOpts->pOptDesc[ OPT_VALUE_MODE ].pz_Name ); pOpts->pzProgName = z; pOpts->pzExplain = apz_mode_explain[ OPT_VALUE_MODE ]; } optionUsage( pOpts, exitCode ); } void validateMode( argct, argvec ) int argct; char** argvec; { 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'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" ); } void emitShellQuoted( pzArg, outFp ) tCC* pzArg; FILE* outFp; { for (;;) { char ch = *(pzArg++); switch (ch) { case '\0': return; case '\'': fputs( "'\\'", outFp ); while (*pzArg == '\'') { fputs( "\\'", outFp ); pzArg++; } /* FALLTHROUGH */ default: fputc( ch, outFp ); } } } void emitShellArg( pzArg, outFp ) tCC* pzArg; FILE* outFp; { tSCC zMetas[] = "<>{}()[]|&^#~*;?$`'\"\\ \t\v\f\r\n"; if (strpbrk( pzArg, zMetas ) == (char*)NULL) { fputs( pzArg, outFp ); return; } if (strchr( pzArg, '\'' ) == (char*)NULL) { fprintf( outFp, "'%s'", pzArg ); return; } fputc( '\'', outFp ); emitShellQuoted( pzArg, outFp ); fputc( '\'', outFp ); } void emitScript( argc, argv ) int argc; char** argv; { 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 zModeName[] = "modename='%s: %s'\n"; tSCC zMode[] = "mode='%s'\n"; tSCC zCmdName[] = "nonopt='%s'\nset --"; tSCC zDlOpt[] = "execute_dlfiles='"; /* * 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, ltmainOptions.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, ltmainOptions.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( DEBUG )) { fprintf( stderr, "%s: enabling shell trace mode\n", ltmainOptions.pzProgName ); fputs( zDbgFmt, fp ); } CKSERV; if (HAVE_OPT( DLOPEN )) { int ct = STACKCT_OPT( DLOPEN ); char** al = STACKLST_OPT( DLOPEN ); fputs( zDlOpt, fp ); for (;;) { emitShellQuoted( *(al++), fp ); if (--ct <= 0) break; fputc( ' ', fp ); /* between each value only */ } fputs( "'\n", fp ); } CKSERV; /* * Insert our modal stuff and one shell option processing dinkleberry * that one of the command scripts depends upon. */ fprintf( fp, zModeName, ltmainOptions.pzProgName, ltmainOptions.pOptDesc[ WHICH_OPT_COMPILE ].pz_Name ); CKSERV; fprintf( fp, zMode, ltmainOptions.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 ); CKSERV; } 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( apz_mode_cmd[ WHICH_OPT_COMPILE ], fp ); CLOSEOK; fputc( '\n', fp ); CLOSEOK; fflush( fp ); if (fp != stdout) closeScript( fp ); } int main( argc, argv ) int argc; char** argv; { pzHost = getenv( "host" ); if (pzHost == (char*)NULL) pzHost = zUnkn; /* * Process the options, removing them from the arg list. * Make sure the resulting state is sane. */ { int ct = optionProcess( <mainOptions, 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, ltmainOptions.pzProgName, "output-file" ); USAGE( EXIT_FAILURE ); /* NOTREACHED */ } if (HAVE_OPT( STATIC )) { fprintf( stderr, zBadOpt, ltmainOptions.pzProgName, "static" ); USAGE( EXIT_FAILURE ); /* NOTREACHED */ } if (HAVE_OPT( DYNAMIC )) { fprintf( stderr, zBadOpt, ltmainOptions.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, ltmainOptions.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, ltmainOptions.pzProgName ); USAGE( EXIT_FAILURE ); /* NOTREACHED */ } pz_cmd_name = argv[0]; signal( SIGPIPE, handleSignal ); signal( SIGBUS, handleSignal ); signal( SIGSEGV, handleSignal ); emitScript( argc, argv ); return scriptStatus; }