/* This file is part of tdepim. Copyright (c) 2004 Cornelius Schumacher 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const TDECmdLineOptions options[] = { { "c", 0, 0 }, { "create-class", I18N_NOOP("Create class"), 0 }, { "d", 0, 0 }, { "create-dialog", I18N_NOOP("Create dialog"), 0 }, { "create-tdeioslave", I18N_NOOP("Create tdeioslave"), 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 ", I18N_NOOP("Add author with given email address"), 0 }, { "project ", I18N_NOOP("Name of project"), 0 }, { "gpl", I18N_NOOP("Use GPL as license"), 0 }, { "lgpl", I18N_NOOP("Use LGPL as license"), 0 }, { "classname ", I18N_NOOP("Name of class"), 0 }, { "filename ", I18N_NOOP("Name of file"), 0 }, { "namespace ", I18N_NOOP("Namespace"), 0 }, { "warning", I18N_NOOP("Create warning about code generation"), 0 }, { "qt-exception", I18N_NOOP("Add TQt exception to GPL"), 0 }, { "singleton", I18N_NOOP("Create a singleton class"), 0 }, { "protocol", I18N_NOOP("tdeioslave protocol"), 0 }, { "+[filename]", I18N_NOOP("Source code file name"), 0 }, TDECmdLineLastOption }; 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( TDECmdLineArgs *args ) { if ( args->count() != 3 ) { std::cerr << "Usage: kode --add-property " << "" << 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().data() << "'" << 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(); state = FindProperties; } break; case FindProperties: { TQRegExp re( "(\\w+)\\s*\\(" ); if ( re.search( line ) >= 0 ) { TQString function = TQString(re.cap( 1 )).lower(); if ( !function.isEmpty() ) { kdDebug() << "Function: " << function << endl; if ( function == className || function == "~" + className ) { out += readAhead; readAhead = TQString(); } 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(); } 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(); addPropertyVariable( out, type, name ); state = Finish; } else { if ( accessor == mutator ) { out += readAheadPrevious; addPropertyFunctions( out, type, name ); out += "\n"; out += line + "\n"; readAhead = TQString(); } state = FindVariables; } } } break; case FindPrivate: if ( line.find( TQRegExp( "\\s*private" ) ) >= 0 ) { if ( accessor.isEmpty() ) { out += readAhead; readAhead = TQString(); 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(); addPropertyVariable( out, type, name ); state = Finish; } } break; case Finish: break; } } headerFile.close(); out += readAhead; if ( args->isSet( "inplace" ) ) { TQString headerFileNameOut = headerFileName + ".kodeorig" ; TDEProcess proc; proc << "cp" << TQFile::encodeName( headerFileName ).data() << TQFile::encodeName( headerFileNameOut ).data(); if ( !proc.start( TDEProcess::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().data() << std::endl; } return 0; } int codify( TDECmdLineArgs *args ) { if ( args->count() != 1 ) { std::cerr << "Usage: kode --codify " << 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().data() << std::endl; } } return 0; } int create( TDECmdLineArgs *args ) { KODE::Printer p; if ( args->isSet( "warning" ) ) p.setCreationWarning( true ); bool createKioslave = args->isSet( "create-tdeioslave" ); 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 tdeioslave 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; TDEABC::Addressee a; if ( authorEmail.isEmpty() ) { a = TDEABC::StdAddressBook::self()->whoAmI(); authorEmail = a.preferredEmail(); } else { TDEABC::Addressee::List as = TDEABC::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.setTQtException( args->isSet( "qt-exception" ) ); file.setLicense( l ); file.setNameSpace( args->getOption( "namespace" ) ); if ( createMain ) { file.addInclude( "tdeaboutdata.h" ); file.addInclude( "tdeapplication.h" ); file.addInclude( "kdebug" ); file.addInclude( "klocale" ); file.addInclude( "kcmdlineargs" ); KODE::Code code; code += "static const TDECmdLineOptions options[] ="; code += "{"; code += " { \"verbose\", \"Verbose output\", 0 },"; code += " TDECmdLineLastOption"; code += "};"; file.addFileCode( code ); KODE::Function main( "main", "int" ); main.addArgument( "int argc" ); main.addArgument( "char **argv" ); code.clear(); code += "TDEAboutData aboutData(\"test\",\"Test\",\"0.1\");"; code += "TDECmdLineArgs::init(argc,argv,&aboutData);"; code += "TDECmdLineArgs::addCmdLineOptions( options );"; code += ""; code += "TDEApplication app;"; code += ""; code += "TDECmdLineArgs *args = TDECmdLineArgs::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 tdeioslave for ..." ); c.addBaseClass( KODE::Class( "SlaveBase", "TDEIO" ) ); c.addHeaderInclude( "tdeio/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 += "TDEInstance instance( \"tdeio_" + protocol + "\" );"; code += ""; code += "kdDebug(7000) << \"Starting tdeio_" + protocol + "(pid: \" << getpid() << \")\" << endl;"; code += ""; code += "if (argc != 4) {"; code.indent(); code += "fprintf( stderr, \"Usage: tdeio_" + 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", "tdeio_" + protocol + ".la" ); t.setSources( className.lower() + ".cpp" ); t.setLibAdd( "$(LIB_TDEIO)" ); 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(), ".backup" ); TQFile::remove( protocolFilename ); KSimpleConfig protocolFile( protocolFilename ); protocolFile.setGroup( "Protocol" ); protocolFile.writeEntry( "exec", "tdeio_" + protocol ); protocolFile.writeEntry( "protocol", protocol ); protocolFile.writeEntry( "input", "none" ); protocolFile.writeEntry( "output", "filesystem" ); protocolFile.writeEntry( "reading", "true" ); protocolFile.writeEntry( "DocPath", "tdeioslave/" + protocol + ".html" ); protocolFile.sync(); } return 0; } int main(int argc,char **argv) { TDEAboutData aboutData( "kode", I18N_NOOP("TDE Code Generator"), "0.1" ); aboutData.addAuthor( "Cornelius Schumacher", 0, "schumacher@kde.org" ); TDECmdLineArgs::init( argc, argv, &aboutData ); TDECmdLineArgs::addCmdLineOptions( options ); TDEApplication app; TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs(); if ( args->isSet( "create-class" ) || args->isSet( "create-dialog" ) || args->isSet( "create-tdeioslave" ) || 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; } }