/* ==================================================================== * Copyright (c) 2003-2006, Martin Hauner * http://subcommander.tigris.org * * Subcommander is licensed as described in the file doc/COPYING, which * you should have received as part of this distribution. * ==================================================================== */ // sys #include #include // apr #include #include #include // svn #include // sc #include "config.h" #include "MainWindow.h" #include "Diff3Widget.h" #include "DiffParam.h" #include "ConfigManager.h" #include "sublib/MacStyle.h" #include "sublib/Utility.h" #include "sublib/MsgHandler.h" #include "sublib/ErrorDialog.h" #include "util/apr.h" #include "util/Exception.h" // qt #include ///////////////////////////////////////////////////////////////////// typedef struct sc_options_state { sc_options_state() { help = false; debug = false; whitespace = true; encoding = "*"; // means => APR_LOCALE_CHARSET; labels[0] = 0; labels[1] = 0; labels[2] = 0; } bool help; bool debug; bool whitespace; // diff whitespace sc::String encoding; const char* labels[3]; } sc_options_state_t; typedef int (*cmd_func)( sc_options_state_t *optState, apr_getopt_t* opt ); const int MaxAliases = 3; const int MaxOptions = 10; typedef struct sc_command { const char* name; cmd_func func; const char* aliases[MaxAliases]; int options[MaxOptions]; const char* desc; const char* usage; } sc_command_t; ///////////////////////////////////////////////////////////////////// const sc_command_t* lookupCommand( const sc_command_t table[], const char* command ) { assert(table); if( ! command ) { return 0; } for( int i = 0; table[i].name; i++ ) { if( strcasecmp(command, table[i].name) == 0 ) // stricmp { return &table[i]; } for( int a = 0; a < MaxAliases; a++ ) { if( ! table[i].aliases[a] ) { break; } if( strcasecmp( command, table[i].aliases[a] ) == 0 ) //stricmp { return &table[i]; } } } return 0; } ///////////////////////////////////////////////////////////////////// int nop( sc_options_state_t *optState, apr_getopt_t* os ); int help( sc_options_state_t *optState, apr_getopt_t* os ); int diff( sc_options_state_t *optState, apr_getopt_t* os ); int diff3( sc_options_state_t *optState, apr_getopt_t* os ); //////////////////////////////////////////////////////////////////// const int WhitespaceOff = 300; const int WhitespaceOn = 301; const apr_getopt_option_t sc_options[] = { { "help", 'h', 0, "show help on a subcommand" }, { NULL, '?', 0, "show help on a subcommand" }, { "w-", WhitespaceOff, 0, "whitespaces off (ignore)" }, { "w+", WhitespaceOn, 0, "whitespaces on" }, { "encoding", 'e', 1, "encoding/codepage (iconv)" }, { "label", 'L', 1, "label for a given file (per file)" }, { "debug", 'd', 0, "print debug info to stderr" }, { 0, 0, 0, 0 } }; const sc_command_t sc_commands[] = { { "diff", diff, {"diff2", 0}, { 'e', 'L', WhitespaceOn, WhitespaceOff, '\0' }, "display differences of two files.\n", "usage: diff original modified\n" }, { "diff3", diff3, {"merge", 0}, { 'e', 'L', WhitespaceOn, WhitespaceOff,'\0' }, "visually merge differences of modifed and latest.\n", "usage: diff3 original modified latest merged\n" }, { "help", help, {"?", "h", 0}, {0}, "display this usage message.\n", "usage: help [SUBCOMMAND...]\n" }, { 0 } }; sc_command_t nullCommand = { "null", nop, {0}, {0}, "do nothing.\n" }; ///////////////////////////////////////////////////////////////////// const char helpHeader[] = "usage: submerge [options] [args]\n" "Type 'submerge help ' for help on a specific subcommand.\n" "\n" "Available subcommands:\n"; const char helpFooter[] = "\n" "Subcommander is a Subversion gui client, diff and merge tool.\n" "For additional information, see http://subcommander.tigris.org/\n" "\n" "Subversion is a tool for revision control.\n" "For additional information, see http://subversion.tigris.org/\n" "\n"; ///////////////////////////////////////////////////////////////////// // cmd_funcs const int Failure = -1; const int Success = 0; const int Close = 1; int nop( sc_options_state_t *optState, apr_getopt_t* os ) { return Success; } void printfAliases( const sc_command_t* cmd ) { bool firstOpt = true; for( int cntAl = 0; cntAl < MaxAliases; cntAl++ ) { if( ! cmd->aliases[cntAl] ) { break; } if( firstOpt ) { printf( " (" ); firstOpt = false; } else { printf( ", " ); } printf( "%s", cmd->aliases[cntAl] ); } if( !firstOpt ) { printf( ")" ); } } const apr_getopt_option_t* getOption( int option ) { int i = 0; while( sc_options[i].optch != 0 ) { if( sc_options[i].optch == option ) { return &sc_options[i]; } i++; } return NULL; } void printfOptions( const sc_command_t* cmd ) { if( cmd->options[0] ) { printf( "Valid options:\n" ); } for( int cnt = 0; cnt < MaxOptions; cnt++ ) { if( ! cmd->options[cnt] ) { break; } sc::String text; char tmp[200]; const apr_getopt_option_t* opt = getOption(cmd->options[cnt]); bool optch = false; if( opt->optch < 255 ) { sprintf( tmp, "-%c ", opt->optch ); text += tmp; optch = true; } if( opt->name && optch ) { sprintf( tmp, "[--%s] ", opt->name ); text += tmp; } else if( opt->name ) { sprintf( tmp, "--%s ", opt->name ); text += tmp; } if( opt->has_arg ) { text += "arg"; } printf( " %-25s : %s\n", (const char*)text, opt->description ? opt->description : "missing description!" ); } } int help( sc_options_state_t *optState, apr_getopt_t* os ) { const char* firstArg = 0; if( ! optState || ! os ) { goto short_help; } firstArg = os->argv[os->ind++]; if( firstArg ) { const sc_command_t* command = lookupCommand( sc_commands, firstArg ); if( !command ) { fprintf( stderr, "unknown command: %s\n", firstArg ); goto short_help; } printf( "%s: %s", command->name, command->desc ); printf( command->usage ); printf( "\n\n" ); printfOptions( command ); } else { printf( helpHeader ); for( int cntCmd = 0; sc_commands[cntCmd].name; cntCmd++ ) { printf( " %s", sc_commands[cntCmd].name ); printfAliases( &sc_commands[cntCmd] ); printf( "\n" ); } printf( helpFooter ); } return Success; short_help: fprintf( stderr, "type 'sm help' for usage.\n" ); return Failure; } int help() { return help( NULL, NULL ); } int diff( sc_options_state_t *optState, apr_getopt_t* os ) { if( os->argc - os->ind != 2 ) { // todo print usage return Failure; } MainWindow* mw = (MainWindow*)qApp->mainWidget(); DiffParamPtr param( new DiffParam() ); param->_type = DiffParam::Diff; param->_original = FileDataPtr( new FileData( sc::String(os->argv[os->ind]), sc::String(optState->encoding) ) ); param->_modified = FileDataPtr( new FileData( sc::String(os->argv[os->ind+1]), sc::String(optState->encoding) ) ); param->_encoding = optState->encoding; param->_whitespace = optState->whitespace; param->_originalLabel = optState->labels[0]; param->_modifiedLabel = optState->labels[1]; const sc::Error* err = mw->diff( param ); if( err ) { return Close; } return Success; } int diff3( sc_options_state_t *optState, apr_getopt_t* os ) { if( os->argc - os->ind != 4 ) { return Failure; } MainWindow* mw = (MainWindow*)qApp->mainWidget(); DiffParamPtr param( new DiffParam() ); param->_type = DiffParam::Diff3; param->_original = FileDataPtr( new FileData( sc::String(os->argv[os->ind]), sc::String(optState->encoding) ) ); param->_modified = FileDataPtr( new FileData( sc::String(os->argv[os->ind+1]), sc::String(optState->encoding) ) ); param->_latest = FileDataPtr( new FileData( sc::String(os->argv[os->ind+2]), sc::String(optState->encoding) ) ); param->_merged = os->argv[os->ind+3]; param->_encoding = optState->encoding; param->_whitespace = optState->whitespace; param->_originalLabel = optState->labels[0]; param->_modifiedLabel = optState->labels[1]; param->_latestLabel = optState->labels[2]; const sc::Error* err = mw->diff3( param ); if( err ) { return Close; } return Success; } /////////////////////////////////////////////////////////////////////////////// // entry point static apr_pool_t* pool; void cleanup() { apr::destroyPool(pool); } int main( int argc, char* argv[] ) { QApplication* app = 0; try { setLocale(); // init stacktrace initStackProcess(); // init apr apr::initialize(argc,argv); // init qt app = new QApplication( argc, argv ); setAppName("submerge"); // redirect qDebug etc. installMessageHandler(); #if !defined(Q_WS_X11) QApplication::setStyle( new MacStyle() ); #endif // Q_WS_X11 // read config ConfigManager config; config.load(); // set font // if we set the font allthough it is the same font as the default (system) font // we get a different font in popup menus. Looks like a bug in Qt 3. if( qApp->font() != config.getGeneralFont() ) { QApplication::setFont( config.getGeneralFont() ); } // adjust locale path setLocalePath( config.getL10n() ? app->applicationDirPath() : "/no_l10n" ); // setup gui MainWindow* mw = new MainWindow(&config); app->setMainWidget(mw); // create pool for parsing command line pool = apr::createPool(); atexit( cleanup ); // parse options... apr_getopt_t* os = 0; sc_options_state_t optState; apr_status_t status; optState.whitespace = config.getOptWhitespace(); status = apr_getopt_init( &os, pool, argc, argv ); os->interleave = 1; while( true ) { int option_ch; const char* option_arg; status = apr_getopt_long( os, sc_options, &option_ch, &option_arg ); if( APR_STATUS_IS_EOF(status) ) { break; } else if( status != APR_SUCCESS ) { help(); return EXIT_FAILURE; } char* dup = apr_pstrdup( pool, option_arg ); switch( option_ch ) { case 'e': { optState.encoding = dup; break; } case 'd': { optState.debug = true; break; } case WhitespaceOff: { optState.whitespace = false; break; } case WhitespaceOn: { optState.whitespace = true; break; } case 'L': { static int cnt = 0; if( cnt < 3 ) { optState.labels[cnt] = dup; cnt++; } break; } case 'h': case '?': { optState.help = true; break; } } } if( optState.debug ) { fprintf( stderr, "submerge dbg: arguments,\n" ); for( int i=0; i < argc; i++ ) { fprintf( stderr, "%02d: %s\n", i, argv[i] ); } } // lookup the command const sc_command_t* command = NULL; if( optState.help ) { command = lookupCommand( sc_commands, "help" ); } if( !command ) { const char* firstArg = os->argv[os->ind++]; command = lookupCommand( sc_commands, firstArg ); if( !command ) { command = &nullCommand; //if( appType == atConsole ) //{ //help(); //return EXIT_FAILURE; //} } } if( optState.debug ) { fprintf( stderr, "submerge dbg: running cmd %s\n", command->name ); } // at last, call the command.... int res = command->func( &optState, os ); // if it was a help command, just return. if( optState.help || strcmp(command->name, "help")==0 ) { return res; } // run the gui if( res == Success ) { mw->show(); int result = app->exec(); config.save(); delete app; return result; } else if( res == Close ) { delete app; return EXIT_SUCCESS; } else { fprintf( stderr, "submerge: failed to run %s command.\n", command->name ); return EXIT_FAILURE; } } catch( sc::Exception& e ) { ErrorDialog* dlg = new ErrorDialog(e); dlg->exec(); return EXIT_FAILURE; } }