/* -*- c-file-style: "xxdiff" -*- */
/******************************************************************************\
 * $RCSfile$
 *
 * Copyright (C) 1999-2003  Martin Blais <blais@furius.ca>
 *
 * 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 of the License, 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 program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 ******************************************************************************/

/*==============================================================================
 * EXTERNAL DECLARATIONS
 *============================================================================*/

#include <cmdline.h>
#include <exceptions.h>
#include <resParser.h>
#include <help.h>

#ifndef INCL_RESPARSER_Y
#include <resParser_yacc.h> // For FONT_TEXT
#define INCL_RESPARSER_Y
#endif

#include <kdeSupport.h>

#include <QApplication>
#include <QByteArray>
#include <QIODevice>
#include <QTextStream>

/*#define getopt xxdiff_getopt*/
#include <getopt.h>
#include <stdlib.h>


XX_NAMESPACE_BEGIN

/*==============================================================================
 * LOCAL DECLARATIONS
 *============================================================================*/

namespace {

//------------------------------------------------------------------------------
//
int initOptions(
   const XxCmdline::Option* options,
   const int                nbOptions,
   struct option*           go_longopts,
   QString&                 go_shortopts
)
{
   // Note: no bounds checking is done.
   int ii;
   for ( ii = 0; ii < nbOptions; ++ii ) {
      go_longopts[ii].name = const_cast<char*>( options[ii]._longname );
      // const_cast is needed for solaris-cc compiler.
      go_longopts[ii].has_arg = ( options[ii]._has_arg ? 1 : 0 );
      go_longopts[ii].flag = 0;
      go_longopts[ii].val = options[ii]._val;

      if ( options[ii]._shortname != 0 ) {
         go_shortopts.append( options[ii]._shortname );
         if ( options[ii]._has_arg ) {
            go_shortopts.append( ":" );
         }
      }
   }
   go_longopts[ii].name = 0;
   go_longopts[ii].has_arg = 0;
   go_longopts[ii].flag = 0;
   go_longopts[ii].val = 0;

   return nbOptions;
}

};

/*==============================================================================
 * PUBLIC FUNCTIONS
 *============================================================================*/

/*==============================================================================
 * CLASS XxCmdline
 *============================================================================*/

/*----- static data members -----*/

// Free simple switch characters: 'k', 'p', 'q', 'x', 'y'

//
// Generic options.
//
XxCmdline::Option XxCmdline::_optionsGeneric[] = {
   { "help", 0, false, 'h',
     "Show help about options."
   },
   { "help-qt", 0, false, 'Z',
     "Show Qt specific options."
   },
   { "help-all", 0, false, 'Y',
     "Show all options."
   },
   { "help-html", 0, false, 'H',
     "Output documentation in HTML."
   },
//#define XX_KDE_CMDLINE
#ifdef XX_KDE_CMDLINE
   { "author", 0, false, 'z',
     "Show author information."
   },
#endif
   { "version", 'v', false, 'v',
     "Show version information."
   },
#ifdef XX_KDE_CMDLINE
   { "license", 0, false, 'W',
     "Show license information."
   },
#endif
};

//
// Xxdiff options
//
XxCmdline::Option XxCmdline::_optionsXxdiff[] = {
   { "no-rcfile", 0, false, 'n',
     "Don't query rcfile resources (.xxdiffrc)."
   },
   { "list-resources", 0, false, 'l',
     "Lists all the supported resources and default values."
   },
   { "exit-on-same", 'D', false, 'D',
     "If there are no differences then exit quietly with exit code of 0."
   },
   { "exit-if-no-conflicts", 'E', false, 'E',
     "If there are no conflicts after making automatic merge selections, then "
     "exit quietly with exit code of 0."
   },
   { "exit-with-merge-status", 'X', false, 'X',
     "If all diff hunks are selected and no unsaved selections exist, then "
     "exit with code of 0. Normally, xxdiff will pass back the diff return "
     "code."
   },
   { "single", 'S', false, 'S',
     "Load a single file for display. This is a strange feature for those who "
     "like the display of code with xxdiff."
   },
   { "merge", 'm', false, 'm',
     "Automatically select regions that would end up being selected by "
     "an automatic merge."
   },
   { "unmerge", 'U', false, 'U',
     "Invoke on a single file with CVS-like merge conflicts, splitting the "
     "conflicts into two files for display.  If this is specified, only a "
     "single file can then be given as argument."
   },
   { "unmerge3", 0, false, 'u',
     "Invoke on a single file with merge conflicts generated by diff3 with the "
     "show-all option, splitting the conflicts into three files for display. "
     "If this is specified, only a single file can then be given as argument."
   },
   { "resource", 'R', true, 'R',
     "Pass on string 'str' to resource parser.  Resources given in this manner "
     "on the command line supersede other resource mechanisms.  One can "
     "specify multiple resource settings by repeating this option."
   },
   { "merged-filename", 'M', true, 'M',
     "Specifies the filename of the merged file for output."
   },
   { "decision", 'O', false, 'O',
     "Forces the user to take a decision upon exit. The user can choose "
     "between ACCEPT, REJECT or MERGED, in which case saving to the "
     "merged filename is forced (if the merged file exists, no warning "
     "is given about overwriting the file). If there are unresolved "
     "conflicts, a popup dialog will appear.  If ACCEPT or REJECT are "
     "selected, no merged file is required.  In all cases, a single line "
     "is output with the decision that was made.  This generic "
     "functionality is useful for better integration with scripts that "
     "require some form of decision to be made by the user."
   },
   { "indicate-input-processed", 0, false, 'j',
     "Indicate that the input files have been entirely processed and are not "
     "needed anymore by printing out the string INPUT-PROCESSED on stdout as "
     "soon as possible. This can be used by scripts which create temporary "
     "files to delete those as soon as xxdiff has finished reading them. "
     "This only works from startup."
   },
   { "use-temporary-files", 't', false, 't',
     "Copies the input streams/files into temporary files to perform diffing. "
     "This is useful if you want to diff FIFOs."
   },
// Hack for os x - programs are run with an argument line -psn_0_36306945
   { "prompt-for-files", 'p', true, 'p',
     "If no files are specified on the command line, show a file dialog so that "
     "the user can select them. This option is ignored if any files are specified."
   },
};

//
// Display options
//
XxCmdline::Option XxCmdline::_optionsDisplay[] = {
   { "title1", 0, true, '1',
     "Display 'str' instead of filename in filename label 1 (left)."
   },
   { "title2", 0, true, '2',
     "Display 'str' instead of filename in filename label 2 (middle)."
   },
   { "title3", 0, true, '3',
     "Display 'str' instead of filename in filename label 3 (right)."
   },
   { "titlein", 'N', true, 'N', // This is kept for xdiff compatibility only.
     "Display 'str' for filename in given in stdin."
   },
   { "orig-xdiff", 0, false, 'o',
     "Use settings as close as possible to original xdiff "
     "(for the romantics longing the old days of SGI... snif snif)."
   },
   { "show-merged-pane", 0, false, 'c',
     "Display the merged pane on startup."
   },
};

//
// GNU diff options.
//
XxCmdline::Option XxCmdline::_optionsDiff[] = {
   { "ignore-all-space", 'w', false, 'w',
     "Option passed to 2-files diff(1). "
     "Ignore white space when comparing lines."
   },
   { "ignore-space-change", 'b', false, 'b',
     "Option passed to 2-files diff(1). "
     "Ignore changes in amount of white space."
   },
   { "ignore-case", 'i', false, 'i',
     "Option passed to 2-files diff(1). "
     "Ignore changes in case; consider upper- and lower-case to be the same."
   },
   { "ignore-blank-lines", 'B', false, 'B',
     "Option passed to 2-files diff(1). "
     "Ignore changes that just insert or delete blank lines."
   },
   { "recursive", 'r', false, 'r',
     "Option passed to 2-files diff(1). "
     "This is only meaningful for directory diffs."
   },
   { "text", 'a', false, 'a',
     "Option passed to 2-files diff(1). "
     "Treat all files as text and compare them "
     "line-by-line, even if they do not appear to be text."
   },
   { "exclude", 'e', true, 'e',
     "When comparing directories, ignore files and subdirectories whose "
     "basenames match pattern."
   },
   { "exclude-from", 0, true, 'f',
     "When comparing directories, ignore files and subdirectories whose "
     "basenames match any pattern contained in file."
   },
   { "args", 'A', true, 'A',
     "Pass on argument to the subordinate diff program."
   },

};

//
// Qt options.
//
XxCmdline::Option XxCmdline::_optionsQt[] = {
   { "qt", 'Q', true, 'Q',
     "Set options to be given to Qt application options parser. "
     "See Qt manual, class QApplication for a list of supported options."
   },
   { "display", 0, true, 'd',
     "Sets the X display (default is $DISPLAY)."
   },
   { "style", 0, true, 's',
     "Sets the application GUI style. Possible values are fusion, windows, "
     "or QtCurve when that style has been installed. "
   },
   { "platform", 0, true, 'x',
      "Sets the platform plugin to be used. For instance, on OS X one can use "
      "cocoa or xcb as platform plugins."
   },
   { "geometry", 0, true, 'g',
     "Sets the client geometry of the main widget."
   },
   { "font", 0, true, 'F',
     "Defines the application font (for widgets)."
   },
   { "textfont", 0, true, 'J',
     "Defines the text font (for diff text)."
   },
   { "name", 0, false, 'L',
     "Sets the application name."
   },
   { "visual", 0, true, 'V',
     "Forces the application to use a particular visual on an 8-bit display "
     "(e.g. TrueColor)."
   },
   { "ncols", 0, true, 'I',
     "Limits the number of colors allocated in the color cube on a 8-bit "
     "display."
     // ", if the application is using the QApplication::ManyColor color "
     // "specification. If count is 216 then a 6x6x6 color cube is used (ie. 6 "
     // "levels of red, 6 of green, and 6 of blue); for other values, a cube "
     // "approximately proportional to a 2x3x1 cube is used."
   },
   { "cmap", 0, false, 'P',
     "Causes the application to install a private color map on an 8-bit "
     "display."
   },
   { "nograb", 0, false, 'K',
     "Tells Qt to never grab the mouse or the keyboard."
   },
   { "dograb", 0, false, 'T',
     "Running under a debugger can cause an implicit -nograb, use -dograb "
     "to override."
   },
   { "sync", 0, false, 'C',
     "Switches to synchronous mode for debugging."
   },
   { "graphicssystem", 0, true, 'G',
     "Sets the application graphics system. Possible values are raster, "
     "opengl (experimental), opengl1 (obsolete), x11, native. "
   },
};

//------------------------------------------------------------------------------
//
XxCmdline::XxCmdline() :
   _forcePlatform( false ),
   _forceStyle( false ),
   _forceGeometry( false ),
   _forceFont( false ),
   _originalXdiff( false ),
   _useRcfile( true ),
   _extraDiffArgs( "" ),
   _single( false ),
   _unmerge( false ),
   _unmergeNbFiles( 2 ),
   _forceDecision( false ),
   _indicateInputProcessed( false ),
   _useTemporaryFiles( false ),
   _promptForFiles( false ),
   _nbQtOptions( 0 ),
   _qtOptions()
{
   _userFilenames[0] = "";
   _userFilenames[1] = "";
   _userFilenames[2] = "";
   _stdinFilename = "(stdin)";
}

//------------------------------------------------------------------------------
//
XxCmdline::~XxCmdline()
{
   for ( int ii = 0; ii < _nbQtOptions; ++ii ) {
      assert( _qtOptions[ii] != 0 );
#ifndef WINDOWS
      /* We don't know why Windows barfs on the free here.*/

      // free(), because they are allocated with qstrdup().
      free( _qtOptions[ii] );
#endif
   }
}

//------------------------------------------------------------------------------
//
bool XxCmdline::parseCommandLine( const int argc, char* const* argv )
{
   XX_ASSERT( argc > 0 );
   _qtOptions[ _nbQtOptions++ ] = qstrdup( argv[0] );

   // The command-line parsing has to be carried out before the resource
   // building because some of the command-line options affect the way resources
   // are built up (e.g. --no-rcfile).  So command-line options that should
   // supersede resources and explicit resources specified on the command-line
   // are stored in the _cmdlineResources.

   int optionIndex = 0;
   const int nbTotalOptions =
      ( sizeof(_optionsGeneric) +
        sizeof(_optionsXxdiff) +
        sizeof(_optionsDisplay) +
        sizeof(_optionsDiff) +
        sizeof(_optionsQt) ) / sizeof(XxCmdline::Option);
   struct option longOptions[ nbTotalOptions + 1 ];
   QString shortOptions = "";
   int nbopt = 0;
   nbopt += initOptions(
      _optionsGeneric,
      sizeof(_optionsGeneric) / sizeof(XxCmdline::Option),
      &( longOptions[nbopt] ),
      shortOptions
   );
   nbopt += initOptions(
      _optionsXxdiff,
      sizeof(_optionsXxdiff) / sizeof(XxCmdline::Option),
      &( longOptions[nbopt] ),
      shortOptions
   );
   nbopt += initOptions(
      _optionsDisplay,
      sizeof(_optionsDisplay) / sizeof(XxCmdline::Option),
      &( longOptions[nbopt] ),
      shortOptions
   );
   nbopt += initOptions(
      _optionsDiff,
      sizeof(_optionsDiff) / sizeof(XxCmdline::Option),
      &( longOptions[nbopt] ),
      shortOptions
   );
   nbopt += initOptions(
      _optionsQt,
      sizeof(_optionsQt) / sizeof(XxCmdline::Option),
      &( longOptions[nbopt] ),
      shortOptions
   );

   // Do the parsing.
   while ( true ) {
      int c = getopt_long( argc,
                           argv,
                           shortOptions.toLocal8Bit().constData(),
                           longOptions,
                           &optionIndex );
      if ( c == -1 ) {
         break;
      }

      switch ( c ) {
         //
         // Generic options.
         //
         case 'h':
            throw XxUsageError( XX_EXC_PARAMS, QString(), OPT_DEFAULT );

         case 'Z':
            throw XxUsageError( XX_EXC_PARAMS,
                                QString(),
                                1 << OPT_GENERIC | 1 << OPT_QT );

         case 'Y':
            throw XxUsageError( XX_EXC_PARAMS, QString(), OPT_ALL );

         case 'H': {
            printHtmlHelp();
            return false;
         } break;

         case 'v': {
            printVersion();
            return false;
         } break;

         //
         // Xxdiff options.
         //
         case 'n':
            _useRcfile = false;
            break;

         case 'l': {
            listResources();
            return false;
         } break;

         case 'D': {
            QTextStream oss( &_cmdlineResources, QIODevice::WriteOnly | QIODevice::Append );
            oss << XxResParser::getBoolOptName( BOOL_EXIT_ON_SAME )
                << ": true" << Qt::endl;
         } break;

         case 'E': {
            QTextStream oss( &_cmdlineResources, QIODevice::WriteOnly | QIODevice::Append );
            oss << XxResParser::getBoolOptName( BOOL_EXIT_IF_NO_CONFLICTS )
                << ": true" << Qt::endl;
         } break;

         case 'X': {
            QTextStream oss( &_cmdlineResources, QIODevice::WriteOnly | QIODevice::Append );
            oss << XxResParser::getBoolOptName( BOOL_EXIT_WITH_MERGE_STATUS )
                << ": true" << Qt::endl;
         } break;

         case 'm': {
            QTextStream oss( &_cmdlineResources, QIODevice::WriteOnly | QIODevice::Append );
            oss << XxResParser::getBoolOptName( BOOL_SELECT_MERGE )
                << ": true" << Qt::endl;
         } break;

         case 'U': {
            _unmerge = true;
            _unmergeNbFiles = 2;
         } break;

         case 'u': {
            _unmerge = true;
            _unmergeNbFiles = 3;
         } break;

         case 'S': {
            _single = true;
         } break;

         case '1':
            if ( !optarg ) {
               throw XxUsageError( XX_EXC_PARAMS,
                                   "Missing argument for title option." );
            }
            _userFilenames[0] = optarg;
            break;

         case '2':
            if ( !optarg ) {
               throw XxUsageError( XX_EXC_PARAMS,
                                   "Missing argument for title option." );
            }
            _userFilenames[1] = optarg;
            break;

         case '3':
            if ( !optarg ) {
               throw XxUsageError( XX_EXC_PARAMS,
                                   "Missing argument for title option." );
            }
            _userFilenames[2] = optarg;
            break;

         case 'N':
            if ( !optarg ) {
               throw XxUsageError( XX_EXC_PARAMS,
                                   "Missing argument for title option." );
            }
            _stdinFilename = optarg;
            break;

         case 'R': {
            QTextStream oss( &_cmdlineResources, QIODevice::WriteOnly | QIODevice::Append );
            oss << optarg << Qt::endl << Qt::flush;
         } break;

         case 'A': {
            if ( !optarg ) {
               throw XxUsageError( XX_EXC_PARAMS,
                                   "Missing argument for sub. diff program." );
            }
            _extraDiffArgs.append( optarg );
         } break;

         case 'o': {
            _originalXdiff = true;
         } break;

         case 'c': {
            QTextStream oss( &_cmdlineResources, QIODevice::WriteOnly | QIODevice::Append );
            oss << XxResParser::getShowOptName( SHOW_PANE_MERGED_VIEW )
                << ": true" << Qt::endl;
         } break;

         case 'M': {
            if ( !optarg ) {
               throw XxUsageError( XX_EXC_PARAMS,
                                   "Missing argument for merged filename." );
            }
            _mergedFilename = optarg;
         } break;

         case 'O': {
            _forceDecision = true;
         } break;

         case 'p': {
            _promptForFiles = true;
         } break;

         //
         // GNU diff options.
         //
         case 'w':
         case 'b':
         case 'i':
         case 'a':
         case 'B':
            char optionString[4];
            optionString[0] = ' ';
            optionString[1] = '-';
            optionString[2] = c;
            optionString[3] = 0;
            _extraDiffArgs.append( optionString );
            break;

         case 'r': {
            QTextStream oss( &_cmdlineResources, QIODevice::WriteOnly | QIODevice::Append );
            oss << XxResParser::getBoolOptName( BOOL_DIRDIFF_RECURSIVE )
                << ": true" << Qt::endl;
         } break;

         case 'e': {
            _extraDiffArgs.append( " --exclude=" );
            _extraDiffArgs.append( QByteArray( optarg ) );
         } break;

         case 'f': {
            _extraDiffArgs.append( " --exclude-from=" );
            _extraDiffArgs.append( QByteArray( optarg ) );
         } break;

         //
         // Qt options.
         //
         case 'Q': {
            // Split string to spaces and add as individual options.
            QByteArray qtopts( optarg );
            qtopts = qtopts.simplified();
            int previdx = 0;
            int idx = -1;
            while ( ( idx = qtopts.indexOf( ' ', idx+1 ) ) != -1 ) {
               QByteArray qtopt = qtopts.mid( previdx, idx - previdx );
               _qtOptions[ _nbQtOptions++ ] =
                  qstrdup( qtopt.constData() );
               XX_ASSERT( _nbQtOptions < 64 ); // just check
               previdx = idx+1;
            }
            QByteArray qtopt = qtopts.mid( previdx );
            _qtOptions[ _nbQtOptions++ ] =
               qstrdup( qtopt.constData() );
            XX_ASSERT( _nbQtOptions < (64-1) ); // just check
         } break;

         case 'd':
         case 's':  // --style
         case 'x':  // --platform
         case 'G':
         case 'g':
         case 'F':
         case 'L':
         case 'V':
         case 'I':
         case 'P':
         case 'K':
         case 'T':
         case 'C': {
            int oidx = searchForOption(
               _optionsQt, sizeof(_optionsQt)/sizeof(Option), c
            );
            XX_ASSERT( oidx != -1 );

            QByteArray qtopt( "-" );
            qtopt += _optionsQt[ oidx ]._longname;

            // Add argument too.

            if ( _optionsQt[ oidx ]._has_arg == true ) {
               _qtOptions[ _nbQtOptions++ ] =
                  qstrdup( qtopt.constData() );
               XX_ASSERT( _nbQtOptions < (64-1) ); // just check

               _qtOptions[ _nbQtOptions++ ] = qstrdup( optarg );
               XX_ASSERT( _nbQtOptions < (64-1) ); // just check
            }
            else {
               _qtOptions[ _nbQtOptions++ ] =
                  qstrdup( qtopt.constData() );
               XX_ASSERT( _nbQtOptions < (64-1) ); // just check
            }

         } break;

         case 'J': {
            if ( !optarg ) {
               throw XxUsageError( XX_EXC_PARAMS,
                                   "Missing argument for text font." );
            }

            QTextStream oss( &_cmdlineResources, QIODevice::WriteOnly | QIODevice::Append );
            oss << XxResParser::getKwdName( FONT_TEXT )
                << ": \"" << optarg << "\"" << Qt::endl;
         } break;

         case 'j': {
            _indicateInputProcessed = true;
         } break;

         case 't': {
            _useTemporaryFiles = true;
         } break;

         case 0:
            throw XxInternalError( XX_EXC_PARAMS );

         case ':':
         case '?': {
            throw XxUsageError( XX_EXC_PARAMS, "Argument error." );
         } break;

         default:
            throw XxUsageError( XX_EXC_PARAMS );
      }
   }

   // Count number of filenames specified on the cmdline.
   _nbFilenames = argc - optind;

   // End qt options with a marker.
   _qtOptions[ _nbQtOptions ] = 0;

   int minfn = ( _unmerge || _single ) ? 1 : 2;
   int maxfn = ( _unmerge || _single ) ? 1 : 3;

   if ( _unmerge && _single ) {
      QString msg;
      {
         QTextStream oss( &msg );
         oss << "You cannot ask for both unmerge and single.";
      }
      throw XxUsageError( XX_EXC_PARAMS, msg );
   }

   // Turn off the prompt-for-files option if any files are specified
   if ( _promptForFiles && _nbFilenames != 0 ) {
      _promptForFiles = false;
   }

   if ( !_promptForFiles && _nbFilenames < minfn ) {
      QString msg;
      {
         QTextStream oss( &msg );
         oss << "You must specify at least " << minfn << " filenames.";
      }
      throw XxUsageError( XX_EXC_PARAMS, msg );
   }

   if ( _nbFilenames > maxfn ) {
      QString msg;
      {
         QTextStream oss( &msg );
         oss << "You can specify at most " << maxfn << " filenames." << Qt::endl;
         oss << "Extra arguments: \"";
         for ( int ii = maxfn; ii < _nbFilenames; ++ii ) {
            oss << " " << argv[ optind + ii ];
         }
         oss << "\"";
      }
      throw XxUsageError( XX_EXC_PARAMS, msg );
   }

   // Read filenames.
   int ii;
   if ( !_promptForFiles )
   {
      for ( ii = 0; ii < _nbFilenames; ++ii ) {
         _filenames[ ii ] = QString::fromLocal8Bit( argv[ optind + ii ] ).trimmed();
      }
   }

   // Detect qt options.
   for ( ii = 0; ii < _nbQtOptions; ++ii ) {
      if ( strncmp( _qtOptions[ ii ], "-style", 6 ) == 0 ) {
         _forceStyle = true;
      }
      else if ( strncmp( _qtOptions[ ii ], "-platform", 9 ) == 0 ) {
         _forcePlatform = true;
      }
      else if ( strncmp( _qtOptions[ ii ], "-geometry", 9 ) == 0 ) {
         _forceGeometry = true;
      }
      else if ( strncmp( _qtOptions[ ii ], "-fn", 3 ) == 0 ||
                strncmp( _qtOptions[ ii ], "-font", 5 ) == 0 ) {
         _forceFont = true;
      }
   }

   return true;
}

//------------------------------------------------------------------------------
//
void XxCmdline::getQtOptions( int& argc, char**& argv )
{
   // We make a copy because Qt likes to remove the options it parses.
   for ( int ii = 0; ii < _nbQtOptions; ++ii ) {
      _qtOptionsCopy[ii] = qstrdup( _qtOptions[ii] );
   }

   argc = _nbQtOptions;
   argv = _qtOptionsCopy;
}

//------------------------------------------------------------------------------
//
void XxCmdline::listResources()
{
   // Open display connection, we need that for font and color conversions.
   int argc = 0;
   QkApplication app( argc, 0 );

   // This lists the default resources, not the ones parsed from the
   // rcfile or command-line.
   QTextStream oss_cout( stdout, QIODevice::WriteOnly );
   XxResParser::listResources( oss_cout );
   oss_cout << Qt::flush;
}

//------------------------------------------------------------------------------
//
void XxCmdline::printHtmlHelp()
{
   // Note: we do not require a display connection here.

   {
      QTextStream os( stdout, QIODevice::WriteOnly );
      os << XxHelp::getManual();
   }
}

//------------------------------------------------------------------------------
//
void XxCmdline::printVersion()
{
   QTextStream oss_cout( stdout, QIODevice::WriteOnly );
   oss_cout << "xxdiff " << XxHelp::getVersion()
#ifdef XX_DEBUG
            << " [debug]"
#endif
#ifdef XX_KDE
            << " [kde]"
#endif
           << Qt::endl;
   oss_cout << QString("  (Qt: %1)").arg( qVersion() ) << Qt::endl;
   oss_cout << "  Written by Martin Blais <blais@furius.ca>" << Qt::endl
            << Qt::flush;
}

//------------------------------------------------------------------------------
//
XxCmdline::Option* XxCmdline::getOptionList( OptType otype, int& nbOptions )
{
   XxCmdline::Option* options = 0;
   switch ( otype ) {
      case OPT_GENERIC: {
         nbOptions = sizeof(_optionsGeneric);
         options = _optionsGeneric;
      } break;
      case OPT_XXDIFF: {
         nbOptions = sizeof(_optionsXxdiff);
         options = _optionsXxdiff;
      } break;
      case OPT_DISPLAY: {
         nbOptions = sizeof(_optionsDisplay);
         options = _optionsDisplay;
      } break;
      case OPT_DIFF: {
         nbOptions = sizeof(_optionsDiff);
         options = _optionsDiff;
      } break;
      case OPT_QT: {
         nbOptions = sizeof(_optionsQt);
         options = _optionsQt;
      } break;
      default: XX_ABORT();
   }
   nbOptions /= sizeof(XxCmdline::Option);
   return options;
}

//------------------------------------------------------------------------------
//
int XxCmdline::searchForOption(
   const XxCmdline::Option* options,
   const int                nbOptions,
   const int                c
)
{
   for ( int ii = 0; ii < nbOptions; ++ii ) {
      if ( options[ii]._val == c ) {
         return ii;
      }
   }
   return -1;
}

XX_NAMESPACE_END
