TDE personal information management applications
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
19KB

  1. /*
  2. This file is part of tdepim.
  3. Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org>
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Library General Public
  6. License as published by the Free Software Foundation; either
  7. version 2 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Library General Public License for more details.
  12. You should have received a copy of the GNU Library General Public License
  13. along with this library; see the file COPYING.LIB. If not, write to
  14. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  15. Boston, MA 02110-1301, USA.
  16. */
  17. #include "code.h"
  18. #include "printer.h"
  19. #include "license.h"
  20. #include "automakefile.h"
  21. #include <tdeabc/stdaddressbook.h>
  22. #include <tdeaboutdata.h>
  23. #include <tdeapplication.h>
  24. #include <tdecmdlineargs.h>
  25. #include <tdeconfig.h>
  26. #include <kdebug.h>
  27. #include <tdeglobal.h>
  28. #include <tdelocale.h>
  29. #include <tdemessagebox.h>
  30. #include <kstandarddirs.h>
  31. #include <ksimpleconfig.h>
  32. #include <ksavefile.h>
  33. #include <kprocess.h>
  34. #include <tqfile.h>
  35. #include <tqtextstream.h>
  36. #include <tqfileinfo.h>
  37. #include <tqregexp.h>
  38. #include <iostream>
  39. static const TDECmdLineOptions options[] =
  40. {
  41. { "c", 0, 0 },
  42. { "create-class", I18N_NOOP("Create class"), 0 },
  43. { "d", 0, 0 },
  44. { "create-dialog", I18N_NOOP("Create dialog"), 0 },
  45. { "create-tdeioslave", I18N_NOOP("Create tdeioslave"), 0 },
  46. { "create-main", I18N_NOOP("Create main function template"), 0 },
  47. { "y", 0, 0 },
  48. { "codify", I18N_NOOP("Create generator code for given source"), 0 },
  49. { "add-property", I18N_NOOP("Add property to class"), 0 },
  50. { "inplace", I18N_NOOP("Change file in place"), 0 },
  51. { "author-email <name>", I18N_NOOP("Add author with given email address"), 0 },
  52. { "project <name>", I18N_NOOP("Name of project"), 0 },
  53. { "gpl", I18N_NOOP("Use GPL as license"), 0 },
  54. { "lgpl", I18N_NOOP("Use LGPL as license"), 0 },
  55. { "classname <name>", I18N_NOOP("Name of class"), 0 },
  56. { "filename <name>", I18N_NOOP("Name of file"), 0 },
  57. { "namespace <name>", I18N_NOOP("Namespace"), 0 },
  58. { "warning", I18N_NOOP("Create warning about code generation"), 0 },
  59. { "qt-exception", I18N_NOOP("Add TQt exception to GPL"), 0 },
  60. { "singleton", I18N_NOOP("Create a singleton class"), 0 },
  61. { "protocol", I18N_NOOP("tdeioslave protocol"), 0 },
  62. { "+[filename]", I18N_NOOP("Source code file name"), 0 },
  63. TDECmdLineLastOption
  64. };
  65. void addPropertyFunctions( TQString &out, const TQString &type,
  66. const TQString &name )
  67. {
  68. // FIXME: Use KODE::Function
  69. bool isReference = type.endsWith( "*" ) || type.endsWith( "&" );
  70. TQString argument;
  71. TQString upper = KODE::Style::upperFirst( name );
  72. if ( !isReference ) {
  73. argument = "const " + type + " &";
  74. } else argument = type;
  75. KODE::Code code;
  76. code.setIndent( 4 );
  77. code += "/**";
  78. code += " Set .";
  79. code += "*/";
  80. code += "void set" + upper + "( " + argument + "v )";
  81. code += "{";
  82. code += " m" + upper + " = v;";
  83. code += "}";
  84. code += "/**";
  85. code += " Get " + name + ". See set" + upper + "().";
  86. code += "*/";
  87. code += type + ( isReference ? "" : " " ) + name + "() const";
  88. code += "{";
  89. code += " return m" + upper + ";";
  90. code += "}";
  91. out += code.text();
  92. }
  93. void addPropertyVariable( TQString &out, const TQString &type,
  94. const TQString &name )
  95. {
  96. TQString upper = KODE::Style::upperFirst( name );
  97. bool isReference = type.endsWith( "*" ) || type.endsWith( "&" );
  98. KODE::Code code;
  99. code.setIndent( 4 );
  100. code += type + ( isReference ? "" : " " ) + "m" + upper + ";";
  101. out += code.text();
  102. }
  103. // FIXME: Put addProperty in PropertyAdder class and add endReadAhead function.
  104. int addProperty( TDECmdLineArgs *args )
  105. {
  106. if ( args->count() != 3 ) {
  107. std::cerr << "Usage: kode --add-property <class> <proprerty-type> "
  108. << "<property-name>" << std::endl;
  109. return 1;
  110. }
  111. TQString className = args->arg( 0 );
  112. TQString type = args->arg( 1 );
  113. TQString name = args->arg( 2 );
  114. kdDebug() << "Add property: class " << className << ": " << type << " " <<
  115. name << endl;
  116. TQString headerFileName = className.lower() + ".h";
  117. TQFile headerFile( headerFileName );
  118. if ( !headerFile.open( IO_ReadOnly ) ) {
  119. std::cerr << "Unable to open file '" << headerFileName.utf8().data() << "'" <<
  120. std::endl;
  121. return 1;
  122. }
  123. TQTextStream in( &headerFile );
  124. enum State { FindClass, FindConstructor, FindProperties, FindPrivate,
  125. FindVariables, Finish };
  126. State state = FindClass;
  127. TQString accessor;
  128. TQString mutator;
  129. TQString out;
  130. TQString readAhead;
  131. TQString line;
  132. while ( !( line = in.readLine() ).isNull() ) {
  133. // std::cout << line.utf8() << std::endl;
  134. kdDebug() << state << " LINE: " << line << endl;
  135. TQString readAheadPrevious = readAhead;
  136. readAhead += line + "\n";
  137. // out += line + "\n";
  138. switch( state ) {
  139. case FindClass:
  140. // if ( line.find( TQRegExp( className ) ) >= 0 ) {
  141. if ( line.find( TQRegExp( "^\\s*class\\s+" + className ) ) >= 0 ) {
  142. kdDebug() << " FOUND CLASS" << endl;
  143. state = FindConstructor;
  144. }
  145. break;
  146. case FindConstructor:
  147. if ( line.find( TQRegExp( "^\\s*" + className + "\\s*\\(" ) ) >= 0 ) {
  148. kdDebug() << " FOUND CONSTRUCTOR" << endl;
  149. out += readAhead;
  150. readAhead = TQString();
  151. state = FindProperties;
  152. }
  153. break;
  154. case FindProperties:
  155. {
  156. TQRegExp re( "(\\w+)\\s*\\(" );
  157. if ( re.search( line ) >= 0 ) {
  158. TQString function = TQString(re.cap( 1 )).lower();
  159. if ( !function.isEmpty() ) {
  160. kdDebug() << "Function: " << function << endl;
  161. if ( function == className || function == "~" + className ) {
  162. out += readAhead;
  163. readAhead = TQString();
  164. } else {
  165. if ( function.startsWith( "set" ) ) {
  166. mutator = function.mid( 3 );
  167. kdDebug() << "MUTATOR: " << mutator << endl;
  168. } else {
  169. if ( function == mutator ) {
  170. accessor = function;
  171. kdDebug() << "ACCESSOR: " << accessor << endl;
  172. out += readAhead;
  173. readAhead = TQString();
  174. } else {
  175. kdDebug() << "CREATE PROPERTY" << endl;
  176. out += readAheadPrevious;
  177. addPropertyFunctions( out, type, name );
  178. out += "\n";
  179. readAhead = line + "\n";
  180. state = FindPrivate;
  181. }
  182. }
  183. }
  184. }
  185. } else if ( line.find( TQRegExp( "\\s*protected" ) ) >= 0 ) {
  186. state = FindPrivate;
  187. } else if ( line.find( TQRegExp( "\\s*private" ) ) >= 0 ) {
  188. if ( accessor.isEmpty() ) {
  189. addPropertyFunctions( out, type, name );
  190. out += readAhead;
  191. readAhead = TQString();
  192. addPropertyVariable( out, type, name );
  193. state = Finish;
  194. } else {
  195. if ( accessor == mutator ) {
  196. out += readAheadPrevious;
  197. addPropertyFunctions( out, type, name );
  198. out += "\n";
  199. out += line + "\n";
  200. readAhead = TQString();
  201. }
  202. state = FindVariables;
  203. }
  204. }
  205. }
  206. break;
  207. case FindPrivate:
  208. if ( line.find( TQRegExp( "\\s*private" ) ) >= 0 ) {
  209. if ( accessor.isEmpty() ) {
  210. out += readAhead;
  211. readAhead = TQString();
  212. addPropertyVariable( out, type, name );
  213. state = Finish;
  214. } else {
  215. state = FindVariables;
  216. }
  217. }
  218. break;
  219. case FindVariables:
  220. {
  221. if ( line.find( "m" + accessor.lower(), 0, false ) >= 0 ) {
  222. out += readAhead;
  223. readAhead = TQString();
  224. addPropertyVariable( out, type, name );
  225. state = Finish;
  226. }
  227. }
  228. break;
  229. case Finish:
  230. break;
  231. }
  232. }
  233. headerFile.close();
  234. out += readAhead;
  235. if ( args->isSet( "inplace" ) ) {
  236. TQString headerFileNameOut = headerFileName + ".kodeorig" ;
  237. TDEProcess proc;
  238. proc << "cp" << TQFile::encodeName( headerFileName ).data() <<
  239. TQFile::encodeName( headerFileNameOut ).data();
  240. if ( !proc.start( TDEProcess::Block ) ) {
  241. kdError() << "Copy failed" << endl;
  242. } else {
  243. kdDebug() << "Write to original file." << endl;
  244. if ( !headerFile.open( IO_WriteOnly ) ) {
  245. kdError() << "Unable to open file '" << headerFileName <<
  246. "' for writing." << endl;
  247. return 1;
  248. }
  249. TQTextStream o( &headerFile );
  250. o << out << endl;
  251. }
  252. } else {
  253. std::cout << out.utf8().data() << std::endl;
  254. }
  255. return 0;
  256. }
  257. int codify( TDECmdLineArgs *args )
  258. {
  259. if ( args->count() != 1 ) {
  260. std::cerr << "Usage: kode --codify <sourcecodefile>" << std::endl;
  261. return 1;
  262. }
  263. TQString filename = args->arg( 0 );
  264. TQFile f( filename );
  265. if ( !f.open( IO_ReadOnly ) ) {
  266. kdError() << "Unable to open file '" << filename << "'." << endl;
  267. return 1;
  268. } else {
  269. std::cout << "KODE::Code code;" << std::endl;
  270. TQTextStream ts( &f );
  271. TQString line;
  272. while( !( line = ts.readLine() ).isNull() ) {
  273. line.replace( "\\", "\\\\" );
  274. line.replace( "\"", "\\\"" );
  275. line = "code += \"" + line;
  276. line.append( "\";" );
  277. std::cout << line.local8Bit().data() << std::endl;
  278. }
  279. }
  280. return 0;
  281. }
  282. int create( TDECmdLineArgs *args )
  283. {
  284. KODE::Printer p;
  285. if ( args->isSet( "warning" ) ) p.setCreationWarning( true );
  286. bool createKioslave = args->isSet( "create-tdeioslave" );
  287. bool createMain = args->isSet( "create-main" );
  288. TQString filename = args->getOption( "filename" );
  289. if ( createMain ) {
  290. if ( filename.isEmpty() ) {
  291. kdError() << "Error: No file name given." << endl;
  292. return 1;
  293. }
  294. if ( filename.endsWith( ".cpp" ) ) {
  295. filename = filename.left( filename.length() - 4 );
  296. }
  297. } else {
  298. if ( !args->isSet( "classname" ) ) {
  299. kdError() << "Error: No class name given." << endl;
  300. return 1;
  301. }
  302. }
  303. TQString className = args->getOption( "classname" );
  304. TQString protocol;
  305. if ( createKioslave ) {
  306. if ( !args->isSet( "protocol" ) ) {
  307. protocol = className.lower();
  308. kdWarning() << "Warning: No protocol for tdeioslave given. Assuming '"
  309. << protocol << "'" << endl;
  310. } else {
  311. protocol = args->getOption( "protocol" );
  312. }
  313. }
  314. KODE::File file;
  315. file.setProject( args->getOption( "project" ) );
  316. TQString authorEmail = args->getOption( "author-email" );
  317. TQString authorName;
  318. TDEABC::Addressee a;
  319. if ( authorEmail.isEmpty() ) {
  320. a = TDEABC::StdAddressBook::self()->whoAmI();
  321. authorEmail = a.preferredEmail();
  322. } else {
  323. TDEABC::Addressee::List as =
  324. TDEABC::StdAddressBook::self()->findByEmail( authorEmail );
  325. if ( as.isEmpty() ) {
  326. kdDebug() << "Unable to find '" << authorEmail << "' in address book."
  327. << endl;
  328. } else {
  329. a = as.first();
  330. }
  331. }
  332. if ( !a.isEmpty() ) {
  333. authorName = a.realName();
  334. }
  335. if ( !authorEmail.isEmpty() ) {
  336. file.addCopyright( TQDate::currentDate().year(), authorName, authorEmail );
  337. }
  338. KODE::License l;
  339. if ( args->isSet( "gpl" ) ) l = KODE::License( KODE::License::GPL );
  340. if ( args->isSet( "lgpl" ) ) l = KODE::License( KODE::License::LGPL );
  341. l.setTQtException( args->isSet( "qt-exception" ) );
  342. file.setLicense( l );
  343. file.setNameSpace( args->getOption( "namespace" ) );
  344. if ( createMain ) {
  345. file.addInclude( "tdeaboutdata.h" );
  346. file.addInclude( "tdeapplication.h" );
  347. file.addInclude( "kdebug" );
  348. file.addInclude( "klocale" );
  349. file.addInclude( "kcmdlineargs" );
  350. KODE::Code code;
  351. code += "static const TDECmdLineOptions options[] =";
  352. code += "{";
  353. code += " { \"verbose\", \"Verbose output\", 0 },";
  354. code += " TDECmdLineLastOption";
  355. code += "};";
  356. file.addFileCode( code );
  357. KODE::Function main( "main", "int" );
  358. main.addArgument( "int argc" );
  359. main.addArgument( "char **argv" );
  360. code.clear();
  361. code += "TDEAboutData aboutData(\"test\",\"Test\",\"0.1\");";
  362. code += "TDECmdLineArgs::init(argc,argv,&aboutData);";
  363. code += "TDECmdLineArgs::addCmdLineOptions( options );";
  364. code += "";
  365. code += "TDEApplication app;";
  366. code += "";
  367. code += "TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs();";
  368. code += "";
  369. code += "Q_UNUSED( args );";
  370. main.setBody( code );
  371. file.addFileFunction( main );
  372. file.setFilename( filename );
  373. p.printImplementation( file, false );
  374. return 0;
  375. }
  376. KODE::Class c( className );
  377. if ( args->isSet( "create-dialog" ) ) {
  378. c.addBaseClass( KODE::Class( "KDialogBase" ) );
  379. c.addInclude( "kdialogbase.h" );
  380. } else if ( createKioslave ) {
  381. c.setDocs( "This class implements a tdeioslave for ..." );
  382. c.addBaseClass( KODE::Class( "SlaveBase", "TDEIO" ) );
  383. c.addHeaderInclude( "tdeio/slavebase.h" );
  384. KODE::Function get( "get", "void" );
  385. get.addArgument( "const KURL &url" );
  386. KODE::Code code;
  387. code += "kdDebug(7000) << \"" + className + "::get()\" << endl;";
  388. code += "kdDebug(7000) << \" URL: \" << url.url() << endl;";
  389. code += "#if 1";
  390. code += "kdDebug(7000) << \" Path: \" << url.path() << endl;";
  391. code += "kdDebug(7000) << \" Query: \" << url.query() << endl;";
  392. code += "kdDebug(7000) << \" Protocol: \" << url.protocol() << endl;";
  393. code += "kdDebug(7000) << \" Filename: \" << url.filename() << endl;";
  394. code += "#endif";
  395. code.newLine();
  396. code += "mimeType( \"text/plain\" );";
  397. code.newLine();
  398. code += "TQCString str( \"Hello!\" );";
  399. code += "data( str );";
  400. code.newLine();
  401. code += "finished();";
  402. code.newLine();
  403. code += "kdDebug(7000) << \"" + className + "CgiProtocol::get() done\" << endl;";
  404. get.setBody( code );
  405. c.addFunction( get );
  406. c.addInclude( "kinstance.h" );
  407. c.addInclude( "kdebug.h" );
  408. c.addInclude( "sys/types.h" );
  409. c.addInclude( "unistd.h" );
  410. c.addInclude( "stdlib.h" );
  411. KODE::Function main( "kdemain", "int" );
  412. main.addArgument( "int argc" );
  413. main.addArgument( "char **argv" );
  414. code.clear();
  415. code += "TDEInstance instance( \"tdeio_" + protocol + "\" );";
  416. code += "";
  417. code += "kdDebug(7000) << \"Starting tdeio_" + protocol + "(pid: \" << getpid() << \")\" << endl;";
  418. code += "";
  419. code += "if (argc != 4) {";
  420. code.indent();
  421. code += "fprintf( stderr, \"Usage: tdeio_" + protocol + " protocol domain-socket1 domain-socket2\\n\");";
  422. code += "exit( -1 );";
  423. code.unindent();
  424. code += "}";
  425. code += "";
  426. code += className + " slave( argv[2], argv[3] );";
  427. code += "slave.dispatchLoop();";
  428. code += "";
  429. code += "return 0;";
  430. main.setBody( code );
  431. file.addFileFunction( main );
  432. file.addExternCDeclaration( p.functionSignature( main ) );
  433. }
  434. KODE::Function constructor( className );
  435. if ( args->isSet( "singleton" ) ) {
  436. constructor.setAccess( KODE::Function::Private );
  437. KODE::Function self( "self", className + " *" );
  438. self.setStatic( true );
  439. KODE::Code code;
  440. code += "if ( !mSelf ) {";
  441. code += " selfDeleter.setObject( mSelf, new " + className + "() );";
  442. code += "}";
  443. code += "return mSelf;";
  444. self.setBody( code );
  445. c.addFunction( self );
  446. KODE::MemberVariable selfVar( "mSelf", className + " *", true );
  447. selfVar.setInitializer( "0" );
  448. c.addMemberVariable( selfVar );
  449. KODE::Variable staticDeleter( "selfDeleter",
  450. "KStaticDeleter<" + className + ">",
  451. true );
  452. file.addFileVariable( staticDeleter );
  453. file.addInclude( "kstaticdeleter.h" );
  454. }
  455. if ( createKioslave ) {
  456. constructor.addArgument( "const TQCString &pool" );
  457. constructor.addArgument( "const TQCString &app" );
  458. constructor.addInitializer( "SlaveBase( \"" + protocol + "\", pool, app )" );
  459. }
  460. c.addFunction( constructor );
  461. file.insertClass( c );
  462. p.printHeader( file );
  463. p.printImplementation( file );
  464. if ( createKioslave ) {
  465. // Write automake Makefile
  466. KODE::AutoMakefile am;
  467. am.addEntry( "INCLUDES", "$(all_includes)" );
  468. am.newLine();
  469. am.addEntry( "noinst_HEADERS", className.lower() + ".h" );
  470. am.newLine();
  471. am.addEntry( "METASOURCES", "AUTO" );
  472. am.newLine();
  473. am.addEntry( "kdelnkdir", "$(kde_servicesdir)" );
  474. am.addEntry( "kdelnk_DATA", protocol + ".protocol" );
  475. KODE::AutoMakefile::Target t( "kde_module_LTLIBRARIES",
  476. "tdeio_" + protocol + ".la" );
  477. t.setSources( className.lower() + ".cpp" );
  478. t.setLibAdd( "$(LIB_TDEIO)" );
  479. t.setLdFlags( "$(all_libraries) -module $(KDE_PLUGIN)" );
  480. am.addTarget( t );
  481. p.printAutoMakefile( am );
  482. // Write protocol file
  483. TQString protocolFilename = protocol + ".protocol";
  484. TQFileInfo fi( protocolFilename );
  485. protocolFilename = fi.absFilePath();
  486. KSaveFile::backupFile( protocolFilename, TQString(), ".backup" );
  487. TQFile::remove( protocolFilename );
  488. KSimpleConfig protocolFile( protocolFilename );
  489. protocolFile.setGroup( "Protocol" );
  490. protocolFile.writeEntry( "exec", "tdeio_" + protocol );
  491. protocolFile.writeEntry( "protocol", protocol );
  492. protocolFile.writeEntry( "input", "none" );
  493. protocolFile.writeEntry( "output", "filesystem" );
  494. protocolFile.writeEntry( "reading", "true" );
  495. protocolFile.writeEntry( "DocPath", "tdeioslave/" + protocol + ".html" );
  496. protocolFile.sync();
  497. }
  498. return 0;
  499. }
  500. int main(int argc,char **argv)
  501. {
  502. TDEAboutData aboutData( "kode", I18N_NOOP("TDE Code Generator"), "0.1" );
  503. aboutData.addAuthor( "Cornelius Schumacher", 0, "schumacher@kde.org" );
  504. TDECmdLineArgs::init( argc, argv, &aboutData );
  505. TDECmdLineArgs::addCmdLineOptions( options );
  506. TDEApplication app;
  507. TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs();
  508. if ( args->isSet( "create-class" ) || args->isSet( "create-dialog" ) ||
  509. args->isSet( "create-tdeioslave" ) || args->isSet( "create-main" ) ) {
  510. return create( args );
  511. } else if ( args->isSet( "codify" ) ) {
  512. return codify( args );
  513. } else if ( args->isSet( "add-property" ) ) {
  514. return addProperty( args );
  515. } else {
  516. std::cerr << "Error: No command given." << std::endl;
  517. return 1;
  518. }
  519. }