You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
634 lines
18 KiB
634 lines
18 KiB
/*
|
|
This file is part of kdepim.
|
|
|
|
Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library 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
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "code.h"
|
|
#include "printer.h"
|
|
#include "license.h"
|
|
#include "automakefile.h"
|
|
|
|
#include <kabc/stdaddressbook.h>
|
|
|
|
#include <kaboutdata.h>
|
|
#include <kapplication.h>
|
|
#include <kcmdlineargs.h>
|
|
#include <kconfig.h>
|
|
#include <kdebug.h>
|
|
#include <kglobal.h>
|
|
#include <klocale.h>
|
|
#include <kmessagebox.h>
|
|
#include <kstandarddirs.h>
|
|
#include <ksimpleconfig.h>
|
|
#include <ksavefile.h>
|
|
#include <kprocess.h>
|
|
|
|
#include <tqfile.h>
|
|
#include <tqtextstream.h>
|
|
#include <tqfileinfo.h>
|
|
#include <tqregexp.h>
|
|
|
|
#include <iostream>
|
|
|
|
static const KCmdLineOptions options[] =
|
|
{
|
|
{ "c", 0, 0 },
|
|
{ "create-class", I18N_NOOP("Create class"), 0 },
|
|
{ "d", 0, 0 },
|
|
{ "create-dialog", I18N_NOOP("Create dialog"), 0 },
|
|
{ "create-kioslave", I18N_NOOP("Create kioslave"), 0 },
|
|
{ "create-main", I18N_NOOP("Create main function template"), 0 },
|
|
{ "y", 0, 0 },
|
|
{ "codify", I18N_NOOP("Create generator code for given source"), 0 },
|
|
{ "add-property", I18N_NOOP("Add property to class"), 0 },
|
|
{ "inplace", I18N_NOOP("Change file in place"), 0 },
|
|
{ "author-email <name>", I18N_NOOP("Add author with given email address"), 0 },
|
|
{ "project <name>", I18N_NOOP("Name of project"), 0 },
|
|
{ "gpl", I18N_NOOP("Use GPL as license"), 0 },
|
|
{ "lgpl", I18N_NOOP("Use LGPL as license"), 0 },
|
|
{ "classname <name>", I18N_NOOP("Name of class"), 0 },
|
|
{ "filename <name>", I18N_NOOP("Name of file"), 0 },
|
|
{ "namespace <name>", I18N_NOOP("Namespace"), 0 },
|
|
{ "warning", I18N_NOOP("Create warning about code generation"), 0 },
|
|
{ "qt-exception", I18N_NOOP("Add Qt exception to GPL"), 0 },
|
|
{ "singleton", I18N_NOOP("Create a singleton class"), 0 },
|
|
{ "protocol", I18N_NOOP("kioslave protocol"), 0 },
|
|
{ "+[filename]", I18N_NOOP("Source code file name"), 0 },
|
|
KCmdLineLastOption
|
|
};
|
|
|
|
void addPropertyFunctions( TQString &out, const TQString &type,
|
|
const TQString &name )
|
|
{
|
|
// FIXME: Use KODE::Function
|
|
|
|
bool isReference = type.endsWith( "*" ) || type.endsWith( "&" );
|
|
|
|
TQString argument;
|
|
TQString upper = KODE::Style::upperFirst( name );
|
|
if ( !isReference ) {
|
|
argument = "const " + type + " &";
|
|
} else argument = type;
|
|
|
|
KODE::Code code;
|
|
code.setIndent( 4 );
|
|
code += "/**";
|
|
code += " Set .";
|
|
code += "*/";
|
|
code += "void set" + upper + "( " + argument + "v )";
|
|
code += "{";
|
|
code += " m" + upper + " = v;";
|
|
code += "}";
|
|
|
|
code += "/**";
|
|
code += " Get " + name + ". See set" + upper + "().";
|
|
code += "*/";
|
|
code += type + ( isReference ? "" : " " ) + name + "() const";
|
|
code += "{";
|
|
code += " return m" + upper + ";";
|
|
code += "}";
|
|
|
|
out += code.text();
|
|
}
|
|
|
|
void addPropertyVariable( TQString &out, const TQString &type,
|
|
const TQString &name )
|
|
{
|
|
TQString upper = KODE::Style::upperFirst( name );
|
|
bool isReference = type.endsWith( "*" ) || type.endsWith( "&" );
|
|
|
|
KODE::Code code;
|
|
code.setIndent( 4 );
|
|
code += type + ( isReference ? "" : " " ) + "m" + upper + ";";
|
|
|
|
out += code.text();
|
|
}
|
|
|
|
// FIXME: Put addProperty in PropertyAdder class and add endReadAhead function.
|
|
int addProperty( KCmdLineArgs *args )
|
|
{
|
|
if ( args->count() != 3 ) {
|
|
std::cerr << "Usage: kode --add-property <class> <proprerty-type> "
|
|
<< "<property-name>" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
TQString className = args->arg( 0 );
|
|
TQString type = args->arg( 1 );
|
|
TQString name = args->arg( 2 );
|
|
|
|
kdDebug() << "Add property: class " << className << ": " << type << " " <<
|
|
name << endl;
|
|
|
|
TQString headerFileName = className.lower() + ".h";
|
|
|
|
TQFile headerFile( headerFileName );
|
|
if ( !headerFile.open( IO_ReadOnly ) ) {
|
|
std::cerr << "Unable to open file '" << headerFileName.utf8() << "'" <<
|
|
std::endl;
|
|
return 1;
|
|
}
|
|
|
|
TQTextStream in( &headerFile );
|
|
|
|
enum State { FindClass, FindConstructor, FindProperties, FindPrivate,
|
|
FindVariables, Finish };
|
|
State state = FindClass;
|
|
|
|
TQString accessor;
|
|
TQString mutator;
|
|
|
|
TQString out;
|
|
|
|
TQString readAhead;
|
|
|
|
TQString line;
|
|
while ( !( line = in.readLine() ).isNull() ) {
|
|
// std::cout << line.utf8() << std::endl;
|
|
kdDebug() << state << " LINE: " << line << endl;
|
|
TQString readAheadPrevious = readAhead;
|
|
readAhead += line + "\n";
|
|
// out += line + "\n";
|
|
switch( state ) {
|
|
case FindClass:
|
|
// if ( line.find( TQRegExp( className ) ) >= 0 ) {
|
|
if ( line.find( TQRegExp( "^\\s*class\\s+" + className ) ) >= 0 ) {
|
|
kdDebug() << " FOUND CLASS" << endl;
|
|
state = FindConstructor;
|
|
}
|
|
break;
|
|
case FindConstructor:
|
|
if ( line.find( TQRegExp( "^\\s*" + className + "\\s*\\(" ) ) >= 0 ) {
|
|
kdDebug() << " FOUND CONSTRUCTOR" << endl;
|
|
out += readAhead;
|
|
readAhead = TQString::null;
|
|
state = FindProperties;
|
|
}
|
|
break;
|
|
case FindProperties:
|
|
{
|
|
TQRegExp re( "(\\w+)\\s*\\(" );
|
|
if ( re.search( line ) >= 0 ) {
|
|
TQString function = re.cap( 1 ).lower();
|
|
if ( !function.isEmpty() ) {
|
|
kdDebug() << "Function: " << function << endl;
|
|
if ( function == className || function == "~" + className ) {
|
|
out += readAhead;
|
|
readAhead = TQString::null;
|
|
} else {
|
|
if ( function.startsWith( "set" ) ) {
|
|
mutator = function.mid( 3 );
|
|
kdDebug() << "MUTATOR: " << mutator << endl;
|
|
} else {
|
|
if ( function == mutator ) {
|
|
accessor = function;
|
|
kdDebug() << "ACCESSOR: " << accessor << endl;
|
|
out += readAhead;
|
|
readAhead = TQString::null;
|
|
} else {
|
|
kdDebug() << "CREATE PROPERTY" << endl;
|
|
out += readAheadPrevious;
|
|
addPropertyFunctions( out, type, name );
|
|
out += "\n";
|
|
readAhead = line + "\n";
|
|
state = FindPrivate;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if ( line.find( TQRegExp( "\\s*protected" ) ) >= 0 ) {
|
|
state = FindPrivate;
|
|
} else if ( line.find( TQRegExp( "\\s*private" ) ) >= 0 ) {
|
|
if ( accessor.isEmpty() ) {
|
|
addPropertyFunctions( out, type, name );
|
|
out += readAhead;
|
|
readAhead = TQString::null;
|
|
addPropertyVariable( out, type, name );
|
|
state = Finish;
|
|
} else {
|
|
if ( accessor == mutator ) {
|
|
out += readAheadPrevious;
|
|
addPropertyFunctions( out, type, name );
|
|
out += "\n";
|
|
out += line + "\n";
|
|
readAhead = TQString::null;
|
|
}
|
|
state = FindVariables;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case FindPrivate:
|
|
if ( line.find( TQRegExp( "\\s*private" ) ) >= 0 ) {
|
|
if ( accessor.isEmpty() ) {
|
|
out += readAhead;
|
|
readAhead = TQString::null;
|
|
addPropertyVariable( out, type, name );
|
|
state = Finish;
|
|
} else {
|
|
state = FindVariables;
|
|
}
|
|
}
|
|
break;
|
|
case FindVariables:
|
|
{
|
|
if ( line.find( "m" + accessor.lower(), 0, false ) >= 0 ) {
|
|
out += readAhead;
|
|
readAhead = TQString::null;
|
|
addPropertyVariable( out, type, name );
|
|
state = Finish;
|
|
}
|
|
}
|
|
break;
|
|
case Finish:
|
|
break;
|
|
}
|
|
}
|
|
|
|
headerFile.close();
|
|
|
|
out += readAhead;
|
|
|
|
if ( args->isSet( "inplace" ) ) {
|
|
TQString headerFileNameOut = headerFileName + ".kodeorig" ;
|
|
|
|
KProcess proc;
|
|
proc << "cp" << TQFile::encodeName( headerFileName ) <<
|
|
TQFile::encodeName( headerFileNameOut );
|
|
|
|
if ( !proc.start( KProcess::Block ) ) {
|
|
kdError() << "Copy failed" << endl;
|
|
} else {
|
|
kdDebug() << "Write to original file." << endl;
|
|
if ( !headerFile.open( IO_WriteOnly ) ) {
|
|
kdError() << "Unable to open file '" << headerFileName <<
|
|
"' for writing." << endl;
|
|
return 1;
|
|
}
|
|
TQTextStream o( &headerFile );
|
|
o << out << endl;
|
|
}
|
|
} else {
|
|
std::cout << out.utf8() << std::endl;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int codify( KCmdLineArgs *args )
|
|
{
|
|
if ( args->count() != 1 ) {
|
|
std::cerr << "Usage: kode --codify <sourcecodefile>" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
TQString filename = args->arg( 0 );
|
|
|
|
TQFile f( filename );
|
|
if ( !f.open( IO_ReadOnly ) ) {
|
|
kdError() << "Unable to open file '" << filename << "'." << endl;
|
|
return 1;
|
|
} else {
|
|
std::cout << "KODE::Code code;" << std::endl;
|
|
TQTextStream ts( &f );
|
|
TQString line;
|
|
while( !( line = ts.readLine() ).isNull() ) {
|
|
line.replace( "\\", "\\\\" );
|
|
line.replace( "\"", "\\\"" );
|
|
line = "code += \"" + line;
|
|
line.append( "\";" );
|
|
std::cout << line.local8Bit() << std::endl;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int create( KCmdLineArgs *args )
|
|
{
|
|
KODE::Printer p;
|
|
if ( args->isSet( "warning" ) ) p.setCreationWarning( true );
|
|
|
|
bool createKioslave = args->isSet( "create-kioslave" );
|
|
bool createMain = args->isSet( "create-main" );
|
|
|
|
TQString filename = args->getOption( "filename" );
|
|
|
|
if ( createMain ) {
|
|
if ( filename.isEmpty() ) {
|
|
kdError() << "Error: No file name given." << endl;
|
|
return 1;
|
|
}
|
|
|
|
if ( filename.endsWith( ".cpp" ) ) {
|
|
filename = filename.left( filename.length() - 4 );
|
|
}
|
|
} else {
|
|
if ( !args->isSet( "classname" ) ) {
|
|
kdError() << "Error: No class name given." << endl;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
TQString className = args->getOption( "classname" );
|
|
|
|
TQString protocol;
|
|
|
|
if ( createKioslave ) {
|
|
if ( !args->isSet( "protocol" ) ) {
|
|
protocol = className.lower();
|
|
kdWarning() << "Warning: No protocol for kioslave given. Assuming '"
|
|
<< protocol << "'" << endl;
|
|
} else {
|
|
protocol = args->getOption( "protocol" );
|
|
}
|
|
}
|
|
|
|
KODE::File file;
|
|
|
|
file.setProject( args->getOption( "project" ) );
|
|
|
|
TQString authorEmail = args->getOption( "author-email" );
|
|
TQString authorName;
|
|
KABC::Addressee a;
|
|
if ( authorEmail.isEmpty() ) {
|
|
a = KABC::StdAddressBook::self()->whoAmI();
|
|
authorEmail = a.preferredEmail();
|
|
} else {
|
|
KABC::Addressee::List as =
|
|
KABC::StdAddressBook::self()->findByEmail( authorEmail );
|
|
if ( as.isEmpty() ) {
|
|
kdDebug() << "Unable to find '" << authorEmail << "' in address book."
|
|
<< endl;
|
|
} else {
|
|
a = as.first();
|
|
}
|
|
}
|
|
if ( !a.isEmpty() ) {
|
|
authorName = a.realName();
|
|
}
|
|
if ( !authorEmail.isEmpty() ) {
|
|
file.addCopyright( TQDate::currentDate().year(), authorName, authorEmail );
|
|
}
|
|
|
|
KODE::License l;
|
|
if ( args->isSet( "gpl" ) ) l = KODE::License( KODE::License::GPL );
|
|
if ( args->isSet( "lgpl" ) ) l = KODE::License( KODE::License::LGPL );
|
|
l.setQtException( args->isSet( "qt-exception" ) );
|
|
file.setLicense( l );
|
|
|
|
file.setNameSpace( args->getOption( "namespace" ) );
|
|
|
|
if ( createMain ) {
|
|
file.addInclude( "kaboutdata.h" );
|
|
file.addInclude( "kapplication.h" );
|
|
file.addInclude( "kdebug" );
|
|
file.addInclude( "klocale" );
|
|
file.addInclude( "kcmdlineargs" );
|
|
|
|
KODE::Code code;
|
|
code += "static const KCmdLineOptions options[] =";
|
|
code += "{";
|
|
code += " { \"verbose\", \"Verbose output\", 0 },";
|
|
code += " KCmdLineLastOption";
|
|
code += "};";
|
|
file.addFileCode( code );
|
|
|
|
KODE::Function main( "main", "int" );
|
|
main.addArgument( "int argc" );
|
|
main.addArgument( "char **argv" );
|
|
|
|
code.clear();
|
|
code += "KAboutData aboutData(\"test\",\"Test\",\"0.1\");";
|
|
code += "KCmdLineArgs::init(argc,argv,&aboutData);";
|
|
code += "KCmdLineArgs::addCmdLineOptions( options );";
|
|
code += "";
|
|
code += "KApplication app;";
|
|
code += "";
|
|
code += "KCmdLineArgs *args = KCmdLineArgs::parsedArgs();";
|
|
code += "";
|
|
code += "Q_UNUSED( args );";
|
|
main.setBody( code );
|
|
|
|
file.addFileFunction( main );
|
|
|
|
file.setFilename( filename );
|
|
|
|
p.printImplementation( file, false );
|
|
|
|
return 0;
|
|
}
|
|
|
|
KODE::Class c( className );
|
|
|
|
if ( args->isSet( "create-dialog" ) ) {
|
|
c.addBaseClass( KODE::Class( "KDialogBase" ) );
|
|
c.addInclude( "kdialogbase.h" );
|
|
} else if ( createKioslave ) {
|
|
c.setDocs( "This class implements a kioslave for ..." );
|
|
|
|
c.addBaseClass( KODE::Class( "SlaveBase", "KIO" ) );
|
|
c.addHeaderInclude( "kio/slavebase.h" );
|
|
|
|
KODE::Function get( "get", "void" );
|
|
get.addArgument( "const KURL &url" );
|
|
|
|
KODE::Code code;
|
|
|
|
code += "kdDebug(7000) << \"" + className + "::get()\" << endl;";
|
|
code += "kdDebug(7000) << \" URL: \" << url.url() << endl;";
|
|
code += "#if 1";
|
|
code += "kdDebug(7000) << \" Path: \" << url.path() << endl;";
|
|
code += "kdDebug(7000) << \" Query: \" << url.query() << endl;";
|
|
code += "kdDebug(7000) << \" Protocol: \" << url.protocol() << endl;";
|
|
code += "kdDebug(7000) << \" Filename: \" << url.filename() << endl;";
|
|
code += "#endif";
|
|
code.newLine();
|
|
|
|
code += "mimeType( \"text/plain\" );";
|
|
code.newLine();
|
|
|
|
code += "TQCString str( \"Hello!\" );";
|
|
code += "data( str );";
|
|
code.newLine();
|
|
|
|
code += "finished();";
|
|
code.newLine();
|
|
|
|
code += "kdDebug(7000) << \"" + className + "CgiProtocol::get() done\" << endl;";
|
|
|
|
get.setBody( code );
|
|
|
|
c.addFunction( get );
|
|
|
|
|
|
c.addInclude( "kinstance.h" );
|
|
c.addInclude( "kdebug.h" );
|
|
c.addInclude( "sys/types.h" );
|
|
c.addInclude( "unistd.h" );
|
|
c.addInclude( "stdlib.h" );
|
|
|
|
KODE::Function main( "kdemain", "int" );
|
|
main.addArgument( "int argc" );
|
|
main.addArgument( "char **argv" );
|
|
|
|
code.clear();
|
|
|
|
code += "KInstance instance( \"kio_" + protocol + "\" );";
|
|
code += "";
|
|
code += "kdDebug(7000) << \"Starting kio_" + protocol + "(pid: \" << getpid() << \")\" << endl;";
|
|
code += "";
|
|
code += "if (argc != 4) {";
|
|
code.indent();
|
|
code += "fprintf( stderr, \"Usage: kio_" + protocol + " protocol domain-socket1 domain-socket2\\n\");";
|
|
code += "exit( -1 );";
|
|
code.unindent();
|
|
code += "}";
|
|
code += "";
|
|
code += className + " slave( argv[2], argv[3] );";
|
|
code += "slave.dispatchLoop();";
|
|
code += "";
|
|
code += "return 0;";
|
|
|
|
main.setBody( code );
|
|
|
|
file.addFileFunction( main );
|
|
|
|
file.addExternCDeclaration( p.functionSignature( main ) );
|
|
}
|
|
|
|
KODE::Function constructor( className );
|
|
|
|
if ( args->isSet( "singleton" ) ) {
|
|
constructor.setAccess( KODE::Function::Private );
|
|
|
|
KODE::Function self( "self", className + " *" );
|
|
self.setStatic( true );
|
|
|
|
KODE::Code code;
|
|
code += "if ( !mSelf ) {";
|
|
code += " selfDeleter.setObject( mSelf, new " + className + "() );";
|
|
code += "}";
|
|
code += "return mSelf;";
|
|
|
|
self.setBody( code );
|
|
|
|
c.addFunction( self );
|
|
|
|
KODE::MemberVariable selfVar( "mSelf", className + " *", true );
|
|
selfVar.setInitializer( "0" );
|
|
|
|
c.addMemberVariable( selfVar );
|
|
|
|
KODE::Variable staticDeleter( "selfDeleter",
|
|
"KStaticDeleter<" + className + ">",
|
|
true );
|
|
file.addFileVariable( staticDeleter );
|
|
file.addInclude( "kstaticdeleter.h" );
|
|
}
|
|
|
|
if ( createKioslave ) {
|
|
constructor.addArgument( "const TQCString &pool" );
|
|
constructor.addArgument( "const TQCString &app" );
|
|
|
|
constructor.addInitializer( "SlaveBase( \"" + protocol + "\", pool, app )" );
|
|
}
|
|
|
|
c.addFunction( constructor );
|
|
|
|
file.insertClass( c );
|
|
|
|
p.printHeader( file );
|
|
p.printImplementation( file );
|
|
|
|
if ( createKioslave ) {
|
|
// Write automake Makefile
|
|
KODE::AutoMakefile am;
|
|
|
|
am.addEntry( "INCLUDES", "$(all_includes)" );
|
|
am.newLine();
|
|
am.addEntry( "noinst_HEADERS", className.lower() + ".h" );
|
|
am.newLine();
|
|
am.addEntry( "METASOURCES", "AUTO" );
|
|
am.newLine();
|
|
am.addEntry( "kdelnkdir", "$(kde_servicesdir)" );
|
|
am.addEntry( "kdelnk_DATA", protocol + ".protocol" );
|
|
|
|
KODE::AutoMakefile::Target t( "kde_module_LTLIBRARIES",
|
|
"kio_" + protocol + ".la" );
|
|
t.setSources( className.lower() + ".cpp" );
|
|
t.setLibAdd( "$(LIB_KIO)" );
|
|
t.setLdFlags( "$(all_libraries) -module $(KDE_PLUGIN)" );
|
|
|
|
am.addTarget( t );
|
|
|
|
p.printAutoMakefile( am );
|
|
|
|
|
|
// Write protocol file
|
|
TQString protocolFilename = protocol + ".protocol";
|
|
|
|
TQFileInfo fi( protocolFilename );
|
|
protocolFilename = fi.absFilePath();
|
|
|
|
KSaveFile::backupFile( protocolFilename, TQString::null, ".backup" );
|
|
|
|
TQFile::remove( protocolFilename );
|
|
|
|
KSimpleConfig protocolFile( protocolFilename );
|
|
|
|
protocolFile.setGroup( "Protocol" );
|
|
protocolFile.writeEntry( "exec", "kio_" + protocol );
|
|
protocolFile.writeEntry( "protocol", protocol );
|
|
protocolFile.writeEntry( "input", "none" );
|
|
protocolFile.writeEntry( "output", "filesystem" );
|
|
protocolFile.writeEntry( "reading", "true" );
|
|
protocolFile.writeEntry( "DocPath", "kioslave/" + protocol + ".html" );
|
|
|
|
protocolFile.sync();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc,char **argv)
|
|
{
|
|
KAboutData aboutData( "kode", I18N_NOOP("KDE Code Generator"), "0.1" );
|
|
aboutData.addAuthor( "Cornelius Schumacher", 0, "schumacher@kde.org" );
|
|
|
|
KCmdLineArgs::init( argc, argv, &aboutData );
|
|
KCmdLineArgs::addCmdLineOptions( options );
|
|
|
|
KApplication app;
|
|
|
|
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
|
|
|
|
if ( args->isSet( "create-class" ) || args->isSet( "create-dialog" ) ||
|
|
args->isSet( "create-kioslave" ) || args->isSet( "create-main" ) ) {
|
|
return create( args );
|
|
} else if ( args->isSet( "codify" ) ) {
|
|
return codify( args );
|
|
} else if ( args->isSet( "add-property" ) ) {
|
|
return addProperty( args );
|
|
} else {
|
|
std::cerr << "Error: No command given." << std::endl;
|
|
return 1;
|
|
}
|
|
}
|