KRename – powerful batch renamer
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.

903 lines
27KB

  1. /***************************************************************************
  2. batchrenamer.cpp - description
  3. -------------------
  4. begin : Sat Aug 18 2001
  5. copyright : (C) 2001 by Dominik Seichter
  6. email : domseichter@web.de
  7. ***************************************************************************/
  8. /***************************************************************************
  9. * *
  10. * This program is free software; you can redistribute it and/or modify *
  11. * it under the terms of the GNU General Public License as published by *
  12. * the Free Software Foundation; either version 2 of the License, or *
  13. * (at your option) any later version. *
  14. * *
  15. ***************************************************************************/
  16. #ifdef HAVE_CONFIG_H
  17. #include <config.h>
  18. #endif
  19. #ifndef VERSION
  20. #define VERSION "unknown"
  21. #endif
  22. // OS includes
  23. #include <stdio.h>
  24. #include <pwd.h>
  25. #include <grp.h>
  26. #include <unistd.h>
  27. // chmod:
  28. #include <sys/types.h>
  29. #include <sys/stat.h>
  30. // QT includes
  31. #include <tqdir.h>
  32. #include <tqregexp.h>
  33. // KDE includes
  34. #include <tdeapplication.h>
  35. #include <tdeio/job.h>
  36. #include <tdeio/netaccess.h>
  37. #include <tdelocale.h>
  38. // Own includes
  39. #include "ProgressDialog.h"
  40. #include "batchrenamer.h"
  41. #include "fileoperation.h"
  42. #include "pluginloader.h"
  43. #include "kmylistview.h"
  44. using namespace TDEIO;
  45. BatchRenamer::BatchRenamer()
  46. : m_index( 0 )
  47. {
  48. plug = PluginLoader::instance();
  49. m_counter_index = 0;
  50. }
  51. BatchRenamer::~BatchRenamer()
  52. {
  53. }
  54. void BatchRenamer::processFiles( ProgressDialog* p, TQObject* object )
  55. {
  56. delete object;
  57. t.start();
  58. m_counters.clear();
  59. for( unsigned int i = 0; i < m_files.count(); i++)
  60. {
  61. m_counter_index = 0;
  62. if( m_mode == RENAME ) {// final Path = source Path
  63. m_files[i].dst.directory = m_files[i].src.directory;
  64. m_files[i].dst.url = m_files[i].src.url;
  65. m_files[i].dst.url.setFileName( TQString() );
  66. } else {
  67. m_files[i].dst.directory = dirname.path();
  68. m_files[i].dst.url = dirname;
  69. }
  70. if( i == 0 )
  71. p->setDestination( m_files[i].dst.url );
  72. else
  73. {
  74. if( m_reset )
  75. findCounterReset( i );
  76. }
  77. m_files[i].dst.name = processString( text, m_files[i].src.name, i );
  78. if( !extext.isEmpty() )
  79. m_files[i].dst.extension = processString( extext, m_files[i].src.extension, i );
  80. (void)applyManualChanges( i );
  81. // Assemble filenames
  82. parseSubdirs( &m_files[i] );
  83. // TODO: DOM
  84. // ESCAPE HERE
  85. m_files[i].src.name = BatchRenamer::buildFilename( &m_files[i].src, true );
  86. // Let's run the plugins that change the final filename,
  87. // i.e the encodingsplugin
  88. m_files[i].dst.name = parsePlugins( i, m_files[i].dst.name, TYPE_FINAL_FILENAME );
  89. m_files[i].dst.name = BatchRenamer::buildFilename( &m_files[i].dst, true );
  90. /*
  91. * take care of renamed directories and
  92. * correct the paths of their contents
  93. */
  94. if( m_files[i].dir && (m_mode == RENAME || m_mode == MOVE) ) {
  95. for( unsigned int c = i; c < m_files.count(); c++ ) {
  96. if( m_files[c].src.directory.left( m_files[i].src.name.length() + 1 )
  97. == ( m_files[i].src.name + "/" ) ) {
  98. m_files[c].src.directory.replace( 0, m_files[i].src.name.length(), m_files[i].dst.name );
  99. m_files[c].src.url.setPath( BatchRenamer::buildFilename( &m_files[c].src, true ) );
  100. }
  101. }
  102. }
  103. }
  104. p->print(i18n("Filenames Processed after %1 seconds.").arg(t.elapsed()/1000));
  105. work( p );
  106. }
  107. TQString BatchRenamer::processString( TQString text, TQString oldname, int i )
  108. {
  109. /*
  110. * Come on! Grep for this text and help me!
  111. *
  112. * note about krename escape sequences
  113. * for certain characters:
  114. *
  115. * Krename will have problems with files
  116. * which contain one of the following
  117. * unicode characters: 60000, 60001, 60002
  118. * 60003, 60004, 60005, 60006.
  119. *
  120. * This is not a good solution, if you have a
  121. * better one please tell me about it!
  122. */
  123. doEscape( oldname );
  124. /*
  125. * Call here all functions that handle
  126. * arguments that are single tokens (&,%,...).
  127. * or in [brackets]
  128. */
  129. text = findBrackets( oldname, text, i );
  130. text = findAndProcess( "$", text, oldname );
  131. text = findAndProcess( "%", text, oldname.lower() );
  132. text = findAndProcess( "&", text, oldname.upper() );
  133. text = findAndProcess( "\\", text, oldname.stripWhiteSpace() );
  134. text = findStar( oldname, text );
  135. text = findNumbers( text, m_files.count(), i );
  136. /*
  137. * text is used as argument token for plugins!
  138. */
  139. text = parsePlugins( i, text, TYPE_TOKEN );
  140. /*
  141. * Replace after Plugins !
  142. * Replace shoud be the last the
  143. * before re-escaping tokens !
  144. */
  145. text = findReplace( text );
  146. // convert special chars back (e.g. &,$)
  147. // TODO: this is to early, because
  148. // parseSubdirs creates subdirectories
  149. // for "/" returned by plugins!!!!
  150. // text = unEscape( text );
  151. return text;
  152. }
  153. TQString BatchRenamer::parsePlugins( int i, const TQString& text, int type )
  154. {
  155. TQPtrListIterator<PluginLoader::PluginLibrary> it( plug->libs );
  156. TQString ret = text;
  157. if( type == TYPE_FINAL_FILE )
  158. ret = "";
  159. for( ; it.current(); ++it )
  160. if( (*it)->usePlugin && (*it)->plugin->type() == type )
  161. {
  162. ret = (*it)->plugin->processFile( this, i, text, type );
  163. doEscape( ret );
  164. }
  165. return ret;
  166. }
  167. void BatchRenamer::createPreview( TQListView* list )
  168. {
  169. KMyListViewItem* item1 = NULL;
  170. TQString tmp;
  171. m_counters.clear();
  172. for( unsigned int i = 0; i < m_files.count(); i++)
  173. {
  174. m_counter_index = 0;
  175. if( i && m_reset )
  176. findCounterReset( i );
  177. m_files[i].dst.name = processString( text, m_files[i].src.name, i );
  178. if( !extext.isEmpty() )
  179. m_files[i].dst.extension = processString( extext, m_files[i].src.extension, i );
  180. bool modified = applyManualChanges( i );
  181. TQString sname = BatchRenamer::buildFilename( &m_files[i].src, false );
  182. // Let's run the plugins that change the final filename,
  183. // i.e the encodingsplugin
  184. m_files[i].dst.name = parsePlugins( i, m_files[i].dst.name, TYPE_FINAL_FILENAME );
  185. TQString dname = BatchRenamer::buildFilename( &m_files[i].dst, false );
  186. item1 = new KMyListViewItem( modified, list, item1, sname, dname );
  187. }
  188. }
  189. void BatchRenamer::work( ProgressDialog* p )
  190. {
  191. // TODO: use CopyJob here
  192. FileOperation fop;
  193. TQFile* fundo ( NULL );
  194. TQTextStream* tundo ( NULL );
  195. if( undo ) {
  196. // Create header for undo script
  197. fundo = new TQFile( m_undoScript );
  198. if( fundo->open( IO_WriteOnly ) ) {
  199. tundo = new TQTextStream( fundo );
  200. writeUndoScript( tundo );
  201. } else {
  202. undo = false;
  203. p->error( i18n("Can't create undo script :") + fundo->name() );
  204. delete fundo;
  205. }
  206. }
  207. int error = 0;
  208. RenamedList* renamedFiles = new RenamedList[m_files.count()];
  209. p->setProgressTotalSteps( m_files.count() + 1 );
  210. /*
  211. * Give the user some information...
  212. */
  213. if( m_mode == COPY)
  214. p->print(i18n("Files will be copied to: %1").arg(m_files[0].dst.directory));
  215. else if( m_mode == MOVE )
  216. p->print(i18n("Files will be moved to: %1").arg(m_files[0].dst.directory));
  217. else if( m_mode == LINK )
  218. p->print(i18n("Symbolic links will be created in: %1").arg(m_files[0].dst.directory));
  219. else if( m_mode == RENAME )
  220. p->print(i18n("Input files will be renamed."));
  221. unsigned int i;
  222. for( i = 0; i < m_files.count(); i++) {
  223. p->setProgress( i+1 );
  224. if( p->wasCancelled() )
  225. break;
  226. KURL src = m_files[i].src.url;
  227. KURL dst = m_files[i].dst.url;
  228. dst.setPath( m_files[i].dst.name );
  229. renamedFiles[i].src = src;
  230. renamedFiles[i].dst = dst;
  231. renamedFiles[i].dir = m_files[i].dir;
  232. FileOperation fop;
  233. if( !fop.start( src, dst, m_mode, overwrite ) ) {
  234. p->error( fop.error() );
  235. renamedFiles[i].error = true;
  236. error++;
  237. continue;
  238. } else {
  239. renamedFiles[i].error = false;
  240. }
  241. // TODO: overwriting of files!
  242. /*
  243. * The renamed file should be on its correct location now,
  244. * so that we can call the last plugins (e.g. for changing permissions)
  245. *
  246. * Remember, the token argument is the filename for this type of plugins!
  247. *
  248. * If the return value is not empty an error has occured!
  249. * The plugin should return an error message in this case!
  250. */
  251. TQString eplug = parsePlugins( i, TQString(), TYPE_FINAL_FILE );
  252. if( !eplug.isEmpty() ) {
  253. p->error( eplug );
  254. error++;
  255. }
  256. /* Create the undo script now */
  257. if( undo )
  258. if( dst.isLocalFile() && src.isLocalFile() ) {
  259. // Plugins ???
  260. (*tundo) << "echo \"" << src.fileName()
  261. << " -> " << dst.fileName() << "\"" << endl;
  262. (*tundo) << "mv --force -b --suffix=.krename_ \"" << m_files[i].dst.name
  263. << "\" \"" << m_files[i].src.name << "\"" << endl;
  264. } else
  265. p->warning(i18n("Undo is not possible for remote file: %1").arg(dst.prettyURL()));
  266. }
  267. if( !p->wasCancelled() ) {
  268. TQPtrListIterator<PluginLoader::PluginLibrary> it( plug->libs );
  269. for( ; it.current(); ++it ) {
  270. if( (*it)->usePlugin )
  271. (*it)->plugin->finished();
  272. }
  273. }
  274. const TQString m = i18n("Renamed %1 files successfully.").arg(i-error);
  275. ( i - error ) ? p->print( m ) : p->warning( m );
  276. if( error > 0 )
  277. p->warning(i18n("%2 errors occurred!").arg(error));
  278. p->print(i18n("Elapsed time: %1 seconds").arg( t.elapsed()/1000 ), "kalarm");
  279. p->print(i18n("KRename finished the renaming process."), "krename");
  280. p->print(i18n("Press close to quit!"));
  281. p->setRenamedFiles( renamedFiles, m_files.count() );
  282. if( undo ) {
  283. (*tundo) << endl << "echo \"Finished undoing " << m_files.count() << " actions.\"" << endl;
  284. delete tundo;
  285. fundo->close();
  286. // Make fundo exuteable
  287. if (chmod(m_undoScript.local8Bit(), (mode_t)(S_IRUSR | S_IWUSR | S_IXUSR)))
  288. p->error( i18n("Can't set executable bit on undo script.") );
  289. delete fundo;
  290. }
  291. p->done( error, i-error, m_mode == MOVE || m_mode == RENAME );
  292. m_files.clear();
  293. delete this;
  294. }
  295. void BatchRenamer::escape( TQString & text, const TQString & token, const TQString & sequence )
  296. {
  297. text.replace( token, sequence );
  298. }
  299. TQString & BatchRenamer::doEscape( TQString& text, bool filename )
  300. {
  301. if( filename ) {
  302. BatchRenamer::escape( text, "&", TQChar( 60000 ) );
  303. BatchRenamer::escape( text, "$", TQChar( 60001 ) );
  304. BatchRenamer::escape( text, "%", TQChar( 60002 ) );
  305. BatchRenamer::escape( text, "#", TQChar( 60004 ) );
  306. BatchRenamer::escape( text, "[", TQChar( 60005 ) );
  307. BatchRenamer::escape( text, "]", TQChar( 60006 ) );
  308. BatchRenamer::escape( text, "\\", TQChar( 60007 ) );
  309. BatchRenamer::escape( text, "/", TQChar( 60008 ) );
  310. BatchRenamer::escape( text, "{", TQChar( 60009 ) );
  311. BatchRenamer::escape( text, "}", TQChar( 60010 ) );
  312. BatchRenamer::escape( text, "*", TQChar( 60011 ) );
  313. } else {
  314. BatchRenamer::escape( text, "\\&", TQChar( 60000 ) );
  315. BatchRenamer::escape( text, "\\$", TQChar( 60001 ) );
  316. BatchRenamer::escape( text, "\\%", TQChar( 60002 ) );
  317. BatchRenamer::escape( text, "\\#", TQChar( 60004 ) );
  318. BatchRenamer::escape( text, "\\[", TQChar( 60005 ) );
  319. BatchRenamer::escape( text, "\\]", TQChar( 60006 ) );
  320. BatchRenamer::escape( text, "\\\\", TQChar( 60007 ) );
  321. BatchRenamer::escape( text, "\\/", TQChar( 60008 ) );
  322. BatchRenamer::escape( text, "\\{", TQChar( 60009 ) );
  323. BatchRenamer::escape( text, "\\}", TQChar( 60010 ) );
  324. BatchRenamer::escape( text, "\\*", TQChar( 60011 ) );
  325. }
  326. return text;
  327. }
  328. TQString & BatchRenamer::unEscape( TQString & text )
  329. {
  330. BatchRenamer::escape( text, TQChar( 60000 ), "&" );
  331. BatchRenamer::escape( text, TQChar( 60001 ), "$" );
  332. BatchRenamer::escape( text, TQChar( 60002 ), "%" );
  333. BatchRenamer::escape( text, TQChar( 60004 ), "#" );
  334. BatchRenamer::escape( text, TQChar( 60005 ), "[" );
  335. BatchRenamer::escape( text, TQChar( 60006 ), "]" );
  336. BatchRenamer::escape( text, TQChar( 60007 ), "\\" );
  337. // %252f == /, it seems that filenames on unix cannot contain
  338. // a /. So I use %252f, at least konqui displays it correctly
  339. // this was needed, so that plugins that return a slash do not cause errors
  340. BatchRenamer::escape( text, TQChar( 60008 ), "%2f" );
  341. BatchRenamer::escape( text, TQChar( 60009 ), "{" );
  342. BatchRenamer::escape( text, TQChar( 60010 ), "}" );
  343. BatchRenamer::escape( text, TQChar( 60011 ), "*" );
  344. return text;
  345. }
  346. int BatchRenamer::getCharacters( int n )
  347. {
  348. TQString s;
  349. s.sprintf( "%i", n );
  350. return s.length();
  351. }
  352. TQString BatchRenamer::findAndProcess( const TQString & token, TQString text, const TQString & replace )
  353. {
  354. /*
  355. * pos can here be -1 because
  356. * findRev is called with it as a
  357. * value !
  358. */
  359. text.replace( token, replace );
  360. return text;
  361. }
  362. TQString BatchRenamer::findNumbers( TQString text, int count, int i )
  363. {
  364. // Rewritten in Version 0.8
  365. // Added numbers skipping in 1.3
  366. // Changed again in Version 1.8 to optimize it and fix a bug with skipping numbers
  367. int pos = 0, counter = 1;
  368. tCounterValues countervalues;
  369. countervalues.start = m_index;
  370. countervalues.step = m_step;
  371. if( text.contains( "#", FALSE ) <= 0 )
  372. return text;
  373. pos = text.find("#", pos);
  374. pos++;
  375. while( text[pos] == '#' ) {
  376. text.remove(pos, 1);
  377. counter++;
  378. }
  379. findNumberAppendix( text, pos, &countervalues.start, &countervalues.step );
  380. pos = text.find("#", 0);
  381. if( (signed int)m_counters.count() <= m_counter_index )
  382. {
  383. countervalues.value = countervalues.start - countervalues.step;
  384. // other wise the counter would start at:
  385. // start + step instead of start
  386. m_counters.append( countervalues );
  387. }
  388. do {
  389. m_counters[m_counter_index].value += m_counters[m_counter_index].step;
  390. } while( m_skip.contains( m_counters[m_counter_index].value ) );
  391. /*
  392. int v = start + (i*step) + m_skip_add[m_counter_index];
  393. for( unsigned int z = 0; z < m_skip.count(); z++ ) {
  394. if( m_skip[z] == v ) {
  395. m_skip_add[m_counter_index] += step;
  396. v += step;
  397. }
  398. }
  399. */
  400. TQString temp;
  401. temp.sprintf("%0*i", counter, m_counters[m_counter_index].value );
  402. text.replace( pos, 1, temp);
  403. ++m_counter_index;
  404. return findNumbers( text, count, i );
  405. }
  406. void BatchRenamer::findNumberAppendix( TQString & text, int pos, int* start, int* step )
  407. {
  408. TQString appendix = TQString();
  409. int tmp = 0;
  410. int end = 0;
  411. bool ok = false;
  412. if( text[pos] == '{' && (end = text.find( "}", pos )) > -1)
  413. {
  414. //tqDebug("Found an appendix:" + appendix );
  415. appendix = text.mid( pos + 1, end - pos - 1);
  416. text.remove( pos, end - pos + 1 );
  417. tmp = appendix.section( ';', 0, 0 ).toInt( &ok ); // first section = start index
  418. if( ok )
  419. *start = tmp;
  420. tmp = appendix.section( ';', 1, 1 ).toInt( &ok ); // second section = stepping
  421. if( ok )
  422. *step = tmp;
  423. }
  424. }
  425. TQString BatchRenamer::findStar( const TQString & oldname, TQString text )
  426. {
  427. int pos = -1;
  428. do {
  429. pos = text.findRev("*", pos);
  430. if( pos >= 0 ) {
  431. TQString tmp = oldname.lower();
  432. if( tmp[0].isLetter() )
  433. tmp[0] = tmp[0].upper();
  434. for( unsigned int i = 0; i < tmp.length(); i++ )
  435. if( tmp[i+1].isLetter() && !tmp[i].isLetter() &&
  436. tmp[i] != '\'' && tmp[i] != '?' && tmp[i] != '`' )
  437. tmp[i+1] = tmp[i+1].upper();
  438. text.replace( pos, 1, tmp);
  439. }
  440. } while( pos >= 0 );
  441. return text;
  442. }
  443. TQString BatchRenamer::findBrackets( TQString oldname, TQString text, int i )
  444. {
  445. /*
  446. * looks for a statement in brackets [ ]
  447. * and calls findToken() with this statement.
  448. */
  449. int num, pos = -1, a;
  450. TQString token;
  451. if( text.contains("]", FALSE) <= 0 || text.isEmpty() )
  452. return text;
  453. num = text.contains("[", FALSE);
  454. if(num <= 0 )
  455. return text;
  456. pos = text.findRev("[", pos);
  457. a = text.find("]", pos );
  458. if( a < 0 && pos >= 0 )
  459. return text;
  460. if( pos < 0 && a >= 0 )
  461. return text;
  462. if( pos >= 0 && a >= 0 ) {
  463. token = text.mid( pos+1, (a-pos)-1 );
  464. // support [4-[length]]
  465. token = findBrackets( oldname, token, i );
  466. text.remove( pos, (a-pos)+1 );
  467. text.insert( pos, findToken( oldname, token, i ));
  468. }
  469. return findBrackets( oldname, text, i );
  470. }
  471. TQString BatchRenamer::processToken( TQString token, TQString oldname, int i )
  472. {
  473. TQString tmp;
  474. /*
  475. * Call here all functions that handle
  476. * arguments in brackets.
  477. */
  478. tmp = findPartStrings( oldname, token );
  479. if( !tmp.isEmpty() )
  480. return tmp;
  481. tmp = findDirName( token, m_files[i].src.directory );
  482. if( !tmp.isEmpty() )
  483. return tmp;
  484. tmp = findLength( token, m_files[i].src.name );
  485. if( !tmp.isEmpty() )
  486. return tmp;
  487. Plugin* p = plug->findPlugin( token );
  488. if( p )
  489. {
  490. tmp = p->processFile( this, i, token, TYPE_BRACKET );
  491. if( !tmp.isNull() )
  492. {
  493. doEscape( tmp );
  494. return tmp;
  495. }
  496. }
  497. /*
  498. * Maybe I should remove this!
  499. * Krename simply ignores unknown tokens!
  500. * Usefull for the MP3 Plugin!
  501. */
  502. return TQString();
  503. }
  504. TQString BatchRenamer::findToken( TQString oldname, TQString token, int i )
  505. {
  506. enum conversion { LOWER, UPPER, MIXED, STAR, STRIP, NONE, EMPTY, NUMBER };
  507. unsigned int numwidth = 0;
  508. conversion c = EMPTY;
  509. if( !token.left(1).compare("$") )
  510. c = NONE;
  511. else if( !token.left(1).compare("%") )
  512. c = LOWER;
  513. else if( !token.left(1).compare("&") )
  514. c = UPPER;
  515. else if( !token.left(1).compare("") )
  516. c = MIXED;
  517. else if( !token.left(1).compare("*") )
  518. c = STAR;
  519. else if( !token.left(1).compare("\\") )
  520. c = STRIP;
  521. else if( !token.left(1).compare("#") ) {
  522. while( !token.left(1).compare("#") ) {
  523. token.remove( 0, 1 );
  524. ++numwidth;
  525. }
  526. c = NUMBER;
  527. }
  528. if( c != EMPTY && c != NUMBER )
  529. token.remove( 0, 1 );
  530. TQString save = token;
  531. token = processToken( token, oldname, i );
  532. switch( c ) {
  533. case LOWER:
  534. token = token.lower();
  535. break;
  536. case UPPER:
  537. token = token.upper();
  538. break;
  539. case MIXED:
  540. token = token.lower();
  541. token.replace( 0, 1, token[0].upper());
  542. break;
  543. case STAR:
  544. token = findStar( token, "*" );
  545. break;
  546. case STRIP:
  547. token = token.stripWhiteSpace();
  548. break;
  549. case NUMBER:
  550. {
  551. bool b = false;
  552. int n = token.toInt( &b );
  553. if( b )
  554. token = token.sprintf("%0*i", numwidth, n );
  555. }
  556. break;
  557. default:
  558. break;
  559. }
  560. doEscape( token );
  561. return token;
  562. }
  563. TQString BatchRenamer::findPartStrings( TQString oldname, TQString token )
  564. {
  565. TQString first, second;
  566. int pos = -1;
  567. // parse things like [2;4{[dirname]}]
  568. if( token.contains( "{" ) >= 1 && token.contains( "}" ) >= 1 ) {
  569. int pos = token.find( "{" );
  570. oldname = token.mid( pos + 1, token.findRev( "}" ) - pos - 1 );
  571. token = token.left( pos );
  572. }
  573. if( token.contains("-") ) {
  574. pos = token.find( "-", 0 );
  575. first = token.left( pos );
  576. // ------- Code OK ^ !
  577. second = token.mid( pos+1, token.length() );
  578. // version < 1.7
  579. // return oldname.mid( first.toInt()-1, second.toInt()-first.toInt() +1 );
  580. // version > 1.7
  581. //return oldname.mid( first.toInt()-1, second.toInt()-first.toInt() );
  582. // version > 1.8
  583. bool ok;
  584. int sec = second.toInt( &ok );
  585. if( !ok || sec == 0 )
  586. sec = oldname.length();
  587. /*
  588. * x should not be larger than the old name
  589. * and not smaller than zero.
  590. */
  591. int x = sec-first.toInt();
  592. if( x > (signed int)oldname.length() || x < 0 )
  593. x = oldname.length()-first.toInt();
  594. /*
  595. * if I would comment my code I would understand this line :)
  596. * without this line, there is sometimes the last letter
  597. * of a filename missing.
  598. */
  599. if( x != -1 )
  600. x++;
  601. return oldname.mid( first.toInt()-1, x );
  602. } else if( token.contains(";") ) {
  603. pos = token.find( ";", 0 );
  604. first = token.left( pos );
  605. second = token.mid( pos+1, token.length() );
  606. return oldname.mid( first.toInt()-1, second.toInt() );
  607. } else {
  608. bool ok = false;
  609. int number = token.toInt( &ok );
  610. if( ok && (number <= (signed int)oldname.length() && number > 0 ) )
  611. return TQString(oldname[ number -1 ]);
  612. else
  613. return TQString();
  614. }
  615. }
  616. TQString BatchRenamer::findDirName( TQString token, TQString path )
  617. {
  618. if( token.left( 7 ).lower() == "dirname" ) {
  619. if( path.right( 1 ) == "/" )
  620. path = path.left( path.length() - 1);
  621. int recursion = 1;
  622. if( token.length() > 7 ) {
  623. token = token.right( token.length() - 7 );
  624. recursion = token.contains( "." );
  625. if( recursion != (signed int)token.length() )
  626. return TQString();
  627. recursion++;
  628. }
  629. return path.section( "/", recursion * -1, recursion * -1);
  630. }
  631. return TQString();
  632. }
  633. TQString BatchRenamer::findLength( const TQString & token, const TQString & name )
  634. {
  635. if( token.lower().startsWith( "length" ) ) {
  636. int minus = 0;
  637. if( token[6] == '-' ) {
  638. bool n = false;
  639. minus = token.mid( 7, token.length() - 7 ).toInt( &n );
  640. if( !n )
  641. minus = 0;
  642. }
  643. return TQString::number( name.length() - minus );
  644. }
  645. return TQString();
  646. }
  647. TQString BatchRenamer::findReplace( TQString text )
  648. {
  649. // Call for each element in replace strings doReplace with correct values
  650. for( unsigned int i = 0; i < m_replace.count(); i++ ) {
  651. replacestrings s = m_replace[i];
  652. text = doReplace( text, unEscape( s.find ), s.replace, s.reg );
  653. }
  654. return text;
  655. }
  656. TQString BatchRenamer::doReplace( TQString text, TQString find, TQString replace, bool reg )
  657. {
  658. if( !reg )
  659. {
  660. // we use the escaped text here because the user might want
  661. // to find a "&" and replace it
  662. text.replace( doEscape( find ), replace );
  663. }
  664. else
  665. {
  666. // no doEscape() here for the regexp, because it would destroy our regular expression
  667. // other wise we will not find stuff like $, [ in the text
  668. text = doEscape( unEscape( text ).replace( TQRegExp( find ), replace ) );
  669. }
  670. return text;
  671. }
  672. void BatchRenamer::writeUndoScript( TQTextStream* t )
  673. {
  674. // write header comments
  675. (*t) << "#!/bin/bash" << endl
  676. << "# KRename Undo Script" << endl << "#" << endl
  677. << "# KRename was written by:" << endl
  678. << "# Dominik Seichter <domseichter@web.de>" << endl
  679. << "# http://krename.sourceforge.net" << endl << "#" << endl
  680. << "# Script generated by KRename Version: " << VERSION << endl << endl
  681. << "# This script must be started with the option --krename to work!" << endl;
  682. // write functions:
  683. (*t) << "echo \"KRename Undo Script\"" << endl
  684. << "echo \"http://krename.sourceforge.net\"" << endl
  685. << "echo \"\"" << endl;
  686. (*t) << "if test --krename = $1 ; then" << endl
  687. << " echo \"\"" << endl
  688. << "else" << endl
  689. << " echo \"You have to start this script\"" << endl
  690. << " echo \"with the command line option\"" << endl
  691. << " echo \"--krename\"" << endl
  692. << " echo \"to undo a rename operation.\"" << endl
  693. << " exit" << endl
  694. << "fi" << endl;
  695. }
  696. void BatchRenamer::parseSubdirs( data* f )
  697. {
  698. int pos = 0;
  699. if( (pos = f->dst.name.findRev( "/", -1 ) ) > 0 ) {
  700. TQString dirs = f->dst.name.left( pos );
  701. f->dst.name = f->dst.name.right( f->dst.name.length() - pos - 1 );
  702. f->dst.directory += ( f->dst.directory.right( 1 ) == "/" ) ? "" : "/";
  703. // create the missing subdir now
  704. int i = 0;
  705. TQString d = TQString::null;
  706. for (d = dirs.section("/", i, i, TQString::SectionSkipEmpty);
  707. !d.isEmpty();
  708. i++, d = dirs.section("/", i, i, TQString::SectionSkipEmpty))
  709. {
  710. KURL url = f->dst.url;
  711. // it is important to unescape here
  712. // to support dirnames containing "&" or
  713. // similar tokens
  714. url.addPath( unEscape( d ) );
  715. if (!NetAccess::exists(url, false, 0) && !NetAccess::mkdir(url, 0, -1))
  716. // TODO: GUI bug report
  717. tqDebug("Can't create %s", url.prettyURL().latin1() );
  718. f->dst.url.addPath( d );
  719. f->dst.directory.append( d + "/" );
  720. }
  721. }
  722. }
  723. TQString BatchRenamer::buildFilename( fileentry* entry, bool dir )
  724. {
  725. TQString filename = ( dir ? entry->directory : TQString() ) + entry->name + ( entry->extension.isEmpty() ? TQString() : TQString(".") ) + entry->extension;
  726. // unescape here as filename is still escaped
  727. unEscape( filename );
  728. return filename;
  729. }
  730. bool BatchRenamer::applyManualChanges( int i )
  731. {
  732. /*
  733. * The last step: make changes of
  734. * the user visible
  735. */
  736. if( !m_changes.isEmpty() )
  737. for( unsigned int z = 0; z < m_changes.count(); z++ ) {
  738. KURL file = m_changes[z].url;
  739. if( file == m_files[i].src.url ) {
  740. m_files[i].dst.name = m_changes[z].user;
  741. // the file extension is already included
  742. // in the users name
  743. m_files[i].dst.extension = TQString();
  744. return true;
  745. }
  746. }
  747. return false;
  748. }
  749. void BatchRenamer::findCounterReset( int i )
  750. {
  751. int z;
  752. if( m_files[i-1].src.directory != m_files[i].src.directory )
  753. for( z=0;z<(int)m_counters.count();z++ )
  754. {
  755. m_counters[z].value = m_counters[z].start - m_counters[z].step;
  756. }
  757. }