From d4b687e966646ad64ec6ded0a3962e6ecb77bce9 Mon Sep 17 00:00:00 2001 From: Konstantin Isakov Date: Sun, 29 May 2011 18:05:28 -0700 Subject: [PATCH] Add proper command line parsing (with quotes support) to programs and to the external audio player command line. --- externalviewer.cc | 20 +++++++++++++----- externalviewer.hh | 4 ++-- goldendict.pro | 6 ++++-- parsecmdline.cc | 53 +++++++++++++++++++++++++++++++++++++++++++++++ parsecmdline.hh | 11 ++++++++++ programs.cc | 3 ++- 6 files changed, 87 insertions(+), 10 deletions(-) create mode 100644 parsecmdline.cc create mode 100644 parsecmdline.hh diff --git a/externalviewer.cc b/externalviewer.cc index 262e7d42..37936667 100644 --- a/externalviewer.cc +++ b/externalviewer.cc @@ -3,17 +3,18 @@ #include #include "externalviewer.hh" +#include "parsecmdline.hh" using std::vector; ExternalViewer::ExternalViewer( QObject * parent, vector< char > const & data, QString const & extension, - QString const & viewerProgram_ ) + QString const & viewerCmdLine_ ) throw( exCantCreateTempFile ): QObject( parent ), tempFile( QDir::temp().filePath( QString( "gd-XXXXXXXX." ) + extension ) ), viewer( this ), - viewerProgram( viewerProgram_ ) + viewerCmdLine( viewerCmdLine_ ) { if ( !tempFile.open() || tempFile.write( &data.front(), data.size() ) != data.size() ) throw exCantCreateTempFile(); @@ -32,8 +33,17 @@ void ExternalViewer::start() throw( exCantRunViewer ) connect( &viewer, SIGNAL( error( QProcess::ProcessError ) ), this, SLOT( deleteLater() ) ); - viewer.start( viewerProgram, QStringList( tempFileName ), QIODevice::NotOpen ); + QStringList args = parseCommandLine( viewerCmdLine ); - if ( !viewer.waitForStarted() ) - throw exCantRunViewer( viewerProgram.toStdString() ); + if ( !args.isEmpty() ) + { + QString program = args.first(); + args.pop_front(); + args.push_back( tempFileName ); + viewer.start( program, args, QIODevice::NotOpen ); + if ( !viewer.waitForStarted() ) + throw exCantRunViewer( viewerCmdLine.toUtf8().data() ); + } + else + throw exCantRunViewer( tr( "the viewer program name is empty" ).toUtf8().data() ); } diff --git a/externalviewer.hh b/externalviewer.hh index 6e926e60..ac10cc24 100644 --- a/externalviewer.hh +++ b/externalviewer.hh @@ -17,7 +17,7 @@ class ExternalViewer: public QObject QTemporaryFile tempFile; QProcess viewer; - QString viewerProgram; + QString viewerCmdLine; QString tempFileName; public: @@ -27,7 +27,7 @@ public: DEF_EX_STR( exCantRunViewer, "Couldn't run external viewer:", Ex ) ExternalViewer( QObject * parent, std::vector< char > const & data, - QString const & extension, QString const & viewerProgram ) + QString const & extension, QString const & viewerCmdLine ) throw( exCantCreateTempFile ); // Once this is called, the object will be deleted when it's done, even if diff --git a/goldendict.pro b/goldendict.pro index 6790d93b..7c2df17d 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -157,7 +157,8 @@ HEADERS += folding.hh \ forvo.hh \ country.hh \ about.hh \ - programs.hh + programs.hh \ + parsecmdline.hh FORMS += groups.ui \ dictgroupwidget.ui \ mainwindow.ui \ @@ -240,7 +241,8 @@ SOURCES += folding.cc \ forvo.cc \ country.cc \ about.cc \ - programs.cc + programs.cc \ + parsecmdline.cc win32 { SOURCES += mouseover_win32/ThTypes.c HEADERS += mouseover_win32/ThTypes.h diff --git a/parsecmdline.cc b/parsecmdline.cc new file mode 100644 index 00000000..ce6ae3ab --- /dev/null +++ b/parsecmdline.cc @@ -0,0 +1,53 @@ +#include "parsecmdline.hh" + +QStringList parseCommandLine( QString const & commandLine ) +{ + // Parse arguments. Handle quotes correctly. + QStringList args; + bool openQuote = false; + bool possibleDoubleQuote = false; + bool startNew = true; + for( QString::const_iterator c = commandLine.begin(), + e = commandLine.end(); c != e; ) + { + if ( *c == '"' && !possibleDoubleQuote ) + { + ++c; + if ( !openQuote ) + { + openQuote = true; + if ( startNew ) + { + args.push_back( QString() ); + startNew = false; + } + } + else + possibleDoubleQuote = true; + } + else + if ( possibleDoubleQuote && *c != '"' ) + { + openQuote = false; + possibleDoubleQuote = false; + } + else + if ( *c == ' ' && !openQuote ) + { + ++c; + startNew = true; + } + else + { + if ( startNew ) + { + args.push_back( QString() ); + startNew = false; + } + args.last().push_back( *c++ ); + possibleDoubleQuote = false; + } + } + + return args; +} diff --git a/parsecmdline.hh b/parsecmdline.hh new file mode 100644 index 00000000..eda250fb --- /dev/null +++ b/parsecmdline.hh @@ -0,0 +1,11 @@ +#ifndef PARSECMDLINE_HH +#define PARSECMDLINE_HH + +#include + +/// Given a command line (name of the executable with optional arguments), +/// separates-out the name and all the arguments into a list. Supports quotes +/// and double-quotes. +QStringList parseCommandLine( QString const & ); + +#endif // PARSECMDLINE_HH diff --git a/programs.cc b/programs.cc index 87d9ff6b..1d9927e3 100644 --- a/programs.cc +++ b/programs.cc @@ -7,6 +7,7 @@ #include "htmlescape.hh" #include "utf8.hh" #include "wstring_qt.hh" +#include "parsecmdline.hh" namespace Programs { @@ -105,7 +106,7 @@ ArticleRequest::ArticleRequest( QString const & word, Config::Program const & prg_ ): prg( prg_ ), process( this ) { - QStringList args = prg.commandLine.split( ' ', QString::SkipEmptyParts ); + QStringList args = parseCommandLine( prg.commandLine ); if ( !args.empty() ) {