Add proper command line parsing (with quotes support) to programs and to the external audio player command line.

This commit is contained in:
Konstantin Isakov 2011-05-29 18:05:28 -07:00
parent 92e2317251
commit d4b687e966
6 changed files with 87 additions and 10 deletions

View file

@ -3,17 +3,18 @@
#include <QDir> #include <QDir>
#include "externalviewer.hh" #include "externalviewer.hh"
#include "parsecmdline.hh"
using std::vector; using std::vector;
ExternalViewer::ExternalViewer( QObject * parent, vector< char > const & data, ExternalViewer::ExternalViewer( QObject * parent, vector< char > const & data,
QString const & extension, QString const & extension,
QString const & viewerProgram_ ) QString const & viewerCmdLine_ )
throw( exCantCreateTempFile ): throw( exCantCreateTempFile ):
QObject( parent ), QObject( parent ),
tempFile( QDir::temp().filePath( QString( "gd-XXXXXXXX." ) + extension ) ), tempFile( QDir::temp().filePath( QString( "gd-XXXXXXXX." ) + extension ) ),
viewer( this ), viewer( this ),
viewerProgram( viewerProgram_ ) viewerCmdLine( viewerCmdLine_ )
{ {
if ( !tempFile.open() || tempFile.write( &data.front(), data.size() ) != data.size() ) if ( !tempFile.open() || tempFile.write( &data.front(), data.size() ) != data.size() )
throw exCantCreateTempFile(); throw exCantCreateTempFile();
@ -32,8 +33,17 @@ void ExternalViewer::start() throw( exCantRunViewer )
connect( &viewer, SIGNAL( error( QProcess::ProcessError ) ), connect( &viewer, SIGNAL( error( QProcess::ProcessError ) ),
this, SLOT( deleteLater() ) ); this, SLOT( deleteLater() ) );
viewer.start( viewerProgram, QStringList( tempFileName ), QIODevice::NotOpen ); QStringList args = parseCommandLine( viewerCmdLine );
if ( !args.isEmpty() )
{
QString program = args.first();
args.pop_front();
args.push_back( tempFileName );
viewer.start( program, args, QIODevice::NotOpen );
if ( !viewer.waitForStarted() ) if ( !viewer.waitForStarted() )
throw exCantRunViewer( viewerProgram.toStdString() ); throw exCantRunViewer( viewerCmdLine.toUtf8().data() );
}
else
throw exCantRunViewer( tr( "the viewer program name is empty" ).toUtf8().data() );
} }

View file

@ -17,7 +17,7 @@ class ExternalViewer: public QObject
QTemporaryFile tempFile; QTemporaryFile tempFile;
QProcess viewer; QProcess viewer;
QString viewerProgram; QString viewerCmdLine;
QString tempFileName; QString tempFileName;
public: public:
@ -27,7 +27,7 @@ public:
DEF_EX_STR( exCantRunViewer, "Couldn't run external viewer:", Ex ) DEF_EX_STR( exCantRunViewer, "Couldn't run external viewer:", Ex )
ExternalViewer( QObject * parent, std::vector< char > const & data, ExternalViewer( QObject * parent, std::vector< char > const & data,
QString const & extension, QString const & viewerProgram ) QString const & extension, QString const & viewerCmdLine )
throw( exCantCreateTempFile ); throw( exCantCreateTempFile );
// Once this is called, the object will be deleted when it's done, even if // Once this is called, the object will be deleted when it's done, even if

View file

@ -157,7 +157,8 @@ HEADERS += folding.hh \
forvo.hh \ forvo.hh \
country.hh \ country.hh \
about.hh \ about.hh \
programs.hh programs.hh \
parsecmdline.hh
FORMS += groups.ui \ FORMS += groups.ui \
dictgroupwidget.ui \ dictgroupwidget.ui \
mainwindow.ui \ mainwindow.ui \
@ -240,7 +241,8 @@ SOURCES += folding.cc \
forvo.cc \ forvo.cc \
country.cc \ country.cc \
about.cc \ about.cc \
programs.cc programs.cc \
parsecmdline.cc
win32 { win32 {
SOURCES += mouseover_win32/ThTypes.c SOURCES += mouseover_win32/ThTypes.c
HEADERS += mouseover_win32/ThTypes.h HEADERS += mouseover_win32/ThTypes.h

53
parsecmdline.cc Normal file
View file

@ -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;
}

11
parsecmdline.hh Normal file
View file

@ -0,0 +1,11 @@
#ifndef PARSECMDLINE_HH
#define PARSECMDLINE_HH
#include <QStringList>
/// 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

View file

@ -7,6 +7,7 @@
#include "htmlescape.hh" #include "htmlescape.hh"
#include "utf8.hh" #include "utf8.hh"
#include "wstring_qt.hh" #include "wstring_qt.hh"
#include "parsecmdline.hh"
namespace Programs { namespace Programs {
@ -105,7 +106,7 @@ ArticleRequest::ArticleRequest( QString const & word,
Config::Program const & prg_ ): Config::Program const & prg_ ):
prg( prg_ ), process( this ) prg( prg_ ), process( this )
{ {
QStringList args = prg.commandLine.split( ' ', QString::SkipEmptyParts ); QStringList args = parseCommandLine( prg.commandLine );
if ( !args.empty() ) if ( !args.empty() )
{ {