|
|
|
/***************************************************************************
|
|
|
|
batchrenamer.cpp - description
|
|
|
|
-------------------
|
|
|
|
begin : Sat Aug 18 2001
|
|
|
|
copyright : (C) 2001 by Dominik Seichter
|
|
|
|
email : domseichter@web.de
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* *
|
|
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
|
|
* it under the terms of the GNU General Public License as published by *
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
|
|
* (at your option) any later version. *
|
|
|
|
* *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef VERSION
|
|
|
|
#define VERSION "unknown"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// OS includes
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <pwd.h>
|
|
|
|
#include <grp.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
// chmod:
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
|
|
|
// QT includes
|
|
|
|
#include <tqdir.h>
|
|
|
|
#include <tqregexp.h>
|
|
|
|
|
|
|
|
// KDE includes
|
|
|
|
#include <tdeapplication.h>
|
|
|
|
#include <tdeio/job.h>
|
|
|
|
#include <tdeio/netaccess.h>
|
|
|
|
#include <tdelocale.h>
|
|
|
|
|
|
|
|
// Own includes
|
|
|
|
#include "ProgressDialog.h"
|
|
|
|
#include "batchrenamer.h"
|
|
|
|
#include "fileoperation.h"
|
|
|
|
#include "pluginloader.h"
|
|
|
|
#include "kmylistview.h"
|
|
|
|
|
|
|
|
using namespace TDEIO;
|
|
|
|
|
|
|
|
BatchRenamer::BatchRenamer()
|
|
|
|
: m_index( 0 )
|
|
|
|
{
|
|
|
|
plug = PluginLoader::instance();
|
|
|
|
m_counter_index = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
BatchRenamer::~BatchRenamer()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void BatchRenamer::processFiles( ProgressDialog* p, TQObject* object )
|
|
|
|
{
|
|
|
|
delete object;
|
|
|
|
t.start();
|
|
|
|
|
|
|
|
m_counters.clear();
|
|
|
|
|
|
|
|
for( unsigned int i = 0; i < m_files.count(); i++)
|
|
|
|
{
|
|
|
|
m_counter_index = 0;
|
|
|
|
|
|
|
|
if( m_mode == RENAME ) {// final Path = source Path
|
|
|
|
m_files[i].dst.directory = m_files[i].src.directory;
|
|
|
|
m_files[i].dst.url = m_files[i].src.url;
|
|
|
|
m_files[i].dst.url.setFileName( TQString() );
|
|
|
|
} else {
|
|
|
|
m_files[i].dst.directory = dirname.path();
|
|
|
|
m_files[i].dst.url = dirname;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( i == 0 )
|
|
|
|
p->setDestination( m_files[i].dst.url );
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( m_reset )
|
|
|
|
findCounterReset( i );
|
|
|
|
}
|
|
|
|
|
|
|
|
m_files[i].dst.name = processString( text, m_files[i].src.name, i );
|
|
|
|
if( !extext.isEmpty() )
|
|
|
|
m_files[i].dst.extension = processString( extext, m_files[i].src.extension, i );
|
|
|
|
|
|
|
|
(void)applyManualChanges( i );
|
|
|
|
|
|
|
|
// Assemble filenames
|
|
|
|
parseSubdirs( &m_files[i] );
|
|
|
|
// TODO: DOM
|
|
|
|
// ESCAPE HERE
|
|
|
|
|
|
|
|
m_files[i].src.name = BatchRenamer::buildFilename( &m_files[i].src, true );
|
|
|
|
|
|
|
|
// Let's run the plugins that change the final filename,
|
|
|
|
// i.e the encodingsplugin
|
|
|
|
m_files[i].dst.name = parsePlugins( i, m_files[i].dst.name, TYPE_FINAL_FILENAME );
|
|
|
|
|
|
|
|
m_files[i].dst.name = BatchRenamer::buildFilename( &m_files[i].dst, true );
|
|
|
|
|
|
|
|
/*
|
|
|
|
* take care of renamed directories and
|
|
|
|
* correct the paths of their contents
|
|
|
|
*/
|
|
|
|
if( m_files[i].dir && (m_mode == RENAME || m_mode == MOVE) ) {
|
|
|
|
for( unsigned int c = i; c < m_files.count(); c++ ) {
|
|
|
|
if( m_files[c].src.directory.left( m_files[i].src.name.length() + 1 )
|
|
|
|
== ( m_files[i].src.name + "/" ) ) {
|
|
|
|
|
|
|
|
m_files[c].src.directory.replace( 0, m_files[i].src.name.length(), m_files[i].dst.name );
|
|
|
|
m_files[c].src.url.setPath( BatchRenamer::buildFilename( &m_files[c].src, true ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
p->print(i18n("Filenames Processed after %1 seconds.").arg(t.elapsed()/1000));
|
|
|
|
|
|
|
|
work( p );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString BatchRenamer::processString( TQString text, TQString oldname, int i )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Come on! Grep for this text and help me!
|
|
|
|
*
|
|
|
|
* note about krename escape sequences
|
|
|
|
* for certain characters:
|
|
|
|
*
|
|
|
|
* Krename will have problems with files
|
|
|
|
* which contain one of the following
|
|
|
|
* unicode characters: 60000, 60001, 60002
|
|
|
|
* 60003, 60004, 60005, 60006.
|
|
|
|
*
|
|
|
|
* This is not a good solution, if you have a
|
|
|
|
* better one please tell me about it!
|
|
|
|
*/
|
|
|
|
|
|
|
|
doEscape( oldname );
|
|
|
|
/*
|
|
|
|
* Call here all functions that handle
|
|
|
|
* arguments that are single tokens (&,%,...).
|
|
|
|
* or in [brackets]
|
|
|
|
*/
|
|
|
|
text = findBrackets( oldname, text, i );
|
|
|
|
text = findAndProcess( "$", text, oldname );
|
|
|
|
text = findAndProcess( "%", text, oldname.lower() );
|
|
|
|
text = findAndProcess( "&", text, oldname.upper() );
|
|
|
|
text = findAndProcess( "\\", text, oldname.stripWhiteSpace() );
|
|
|
|
text = findStar( oldname, text );
|
|
|
|
text = findNumbers( text, m_files.count(), i );
|
|
|
|
/*
|
|
|
|
* text is used as argument token for plugins!
|
|
|
|
*/
|
|
|
|
text = parsePlugins( i, text, TYPE_TOKEN );
|
|
|
|
/*
|
|
|
|
* Replace after Plugins !
|
|
|
|
* Replace shoud be the last the
|
|
|
|
* before re-escaping tokens !
|
|
|
|
*/
|
|
|
|
text = findReplace( text );
|
|
|
|
|
|
|
|
// convert special chars back (e.g. &,$)
|
|
|
|
// TODO: this is to early, because
|
|
|
|
// parseSubdirs creates subdirectories
|
|
|
|
// for "/" returned by plugins!!!!
|
|
|
|
// text = unEscape( text );
|
|
|
|
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString BatchRenamer::parsePlugins( int i, const TQString& text, int type )
|
|
|
|
{
|
|
|
|
TQPtrListIterator<PluginLoader::PluginLibrary> it( plug->libs );
|
|
|
|
TQString ret = text;
|
|
|
|
|
|
|
|
if( type == TYPE_FINAL_FILE )
|
|
|
|
ret = "";
|
|
|
|
|
|
|
|
for( ; it.current(); ++it )
|
|
|
|
if( (*it)->usePlugin && (*it)->plugin->type() == type )
|
|
|
|
{
|
|
|
|
ret = (*it)->plugin->processFile( this, i, text, type );
|
|
|
|
doEscape( ret );
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BatchRenamer::createPreview( TQListView* list )
|
|
|
|
{
|
|
|
|
KMyListViewItem* item1 = NULL;
|
|
|
|
TQString tmp;
|
|
|
|
|
|
|
|
m_counters.clear();
|
|
|
|
for( unsigned int i = 0; i < m_files.count(); i++)
|
|
|
|
{
|
|
|
|
m_counter_index = 0;
|
|
|
|
|
|
|
|
if( i && m_reset )
|
|
|
|
findCounterReset( i );
|
|
|
|
|
|
|
|
m_files[i].dst.name = processString( text, m_files[i].src.name, i );
|
|
|
|
if( !extext.isEmpty() )
|
|
|
|
m_files[i].dst.extension = processString( extext, m_files[i].src.extension, i );
|
|
|
|
|
|
|
|
bool modified = applyManualChanges( i );
|
|
|
|
|
|
|
|
|
|
|
|
TQString sname = BatchRenamer::buildFilename( &m_files[i].src, false );
|
|
|
|
|
|
|
|
// Let's run the plugins that change the final filename,
|
|
|
|
// i.e the encodingsplugin
|
|
|
|
m_files[i].dst.name = parsePlugins( i, m_files[i].dst.name, TYPE_FINAL_FILENAME );
|
|
|
|
TQString dname = BatchRenamer::buildFilename( &m_files[i].dst, false );
|
|
|
|
|
|
|
|
item1 = new KMyListViewItem( modified, list, item1, sname, dname );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void BatchRenamer::work( ProgressDialog* p )
|
|
|
|
{
|
|
|
|
// TODO: use CopyJob here
|
|
|
|
|
|
|
|
FileOperation fop;
|
|
|
|
TQFile* fundo ( NULL );
|
|
|
|
TQTextStream* tundo ( NULL );
|
|
|
|
|
|
|
|
if( undo ) {
|
|
|
|
// Create header for undo script
|
|
|
|
fundo = new TQFile( m_undoScript );
|
|
|
|
if( fundo->open( IO_WriteOnly ) ) {
|
|
|
|
tundo = new TQTextStream( fundo );
|
|
|
|
writeUndoScript( tundo );
|
|
|
|
} else {
|
|
|
|
undo = false;
|
|
|
|
p->error( i18n("Can't create undo script :") + fundo->name() );
|
|
|
|
delete fundo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int error = 0;
|
|
|
|
RenamedList* renamedFiles = new RenamedList[m_files.count()];
|
|
|
|
p->setProgressTotalSteps( m_files.count() + 1 );
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Give the user some information...
|
|
|
|
*/
|
|
|
|
if( m_mode == COPY)
|
|
|
|
p->print(i18n("Files will be copied to: %1").arg(m_files[0].dst.directory));
|
|
|
|
else if( m_mode == MOVE )
|
|
|
|
p->print(i18n("Files will be moved to: %1").arg(m_files[0].dst.directory));
|
|
|
|
else if( m_mode == LINK )
|
|
|
|
p->print(i18n("Symbolic links will be created in: %1").arg(m_files[0].dst.directory));
|
|
|
|
else if( m_mode == RENAME )
|
|
|
|
p->print(i18n("Input files will be renamed."));
|
|
|
|
|
|
|
|
unsigned int i;
|
|
|
|
for( i = 0; i < m_files.count(); i++) {
|
|
|
|
p->setProgress( i+1 );
|
|
|
|
|
|
|
|
if( p->wasCancelled() )
|
|
|
|
break;
|
|
|
|
|
|
|
|
KURL src = m_files[i].src.url;
|
|
|
|
KURL dst = m_files[i].dst.url;
|
|
|
|
dst.setPath( m_files[i].dst.name );
|
|
|
|
|
|
|
|
renamedFiles[i].src = src;
|
|
|
|
renamedFiles[i].dst = dst;
|
|
|
|
renamedFiles[i].dir = m_files[i].dir;
|
|
|
|
|
|
|
|
FileOperation fop;
|
|
|
|
if( !fop.start( src, dst, m_mode, overwrite ) ) {
|
|
|
|
p->error( fop.error() );
|
|
|
|
renamedFiles[i].error = true;
|
|
|
|
error++;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
renamedFiles[i].error = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: overwriting of files!
|
|
|
|
/*
|
|
|
|
* The renamed file should be on its correct location now,
|
|
|
|
* so that we can call the last plugins (e.g. for changing permissions)
|
|
|
|
*
|
|
|
|
* Remember, the token argument is the filename for this type of plugins!
|
|
|
|
*
|
|
|
|
* If the return value is not empty an error has occured!
|
|
|
|
* The plugin should return an error message in this case!
|
|
|
|
*/
|
|
|
|
|
|
|
|
TQString eplug = parsePlugins( i, TQString(), TYPE_FINAL_FILE );
|
|
|
|
if( !eplug.isEmpty() ) {
|
|
|
|
p->error( eplug );
|
|
|
|
error++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create the undo script now */
|
|
|
|
if( undo )
|
|
|
|
if( dst.isLocalFile() && src.isLocalFile() ) {
|
|
|
|
// Plugins ???
|
|
|
|
(*tundo) << "echo \"" << src.fileName()
|
|
|
|
<< " -> " << dst.fileName() << "\"" << endl;
|
|
|
|
(*tundo) << "mv --force -b --suffix=.krename_ \"" << m_files[i].dst.name
|
|
|
|
<< "\" \"" << m_files[i].src.name << "\"" << endl;
|
|
|
|
} else
|
|
|
|
p->warning(i18n("Undo is not possible for remote file: %1").arg(dst.prettyURL()));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !p->wasCancelled() ) {
|
|
|
|
TQPtrListIterator<PluginLoader::PluginLibrary> it( plug->libs );
|
|
|
|
for( ; it.current(); ++it ) {
|
|
|
|
if( (*it)->usePlugin )
|
|
|
|
(*it)->plugin->finished();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQString m = i18n("Renamed %1 files successfully.").arg(i-error);
|
|
|
|
( i - error ) ? p->print( m ) : p->warning( m );
|
|
|
|
|
|
|
|
if( error > 0 )
|
|
|
|
p->warning(i18n("%2 errors occurred!").arg(error));
|
|
|
|
|
|
|
|
p->print(i18n("Elapsed time: %1 seconds").arg( t.elapsed()/1000 ), "kalarm");
|
|
|
|
p->print(i18n("KRename finished the renaming process."), "krename");
|
|
|
|
p->print(i18n("Press close to quit!"));
|
|
|
|
p->setRenamedFiles( renamedFiles, m_files.count() );
|
|
|
|
|
|
|
|
if( undo ) {
|
|
|
|
(*tundo) << endl << "echo \"Finished undoing " << m_files.count() << " actions.\"" << endl;
|
|
|
|
delete tundo;
|
|
|
|
fundo->close();
|
|
|
|
|
|
|
|
// Make fundo exuteable
|
|
|
|
if (chmod(m_undoScript.local8Bit(), (mode_t)(S_IRUSR | S_IWUSR | S_IXUSR)))
|
|
|
|
p->error( i18n("Can't set executable bit on undo script.") );
|
|
|
|
delete fundo;
|
|
|
|
}
|
|
|
|
|
|
|
|
p->done( error, i-error, m_mode == MOVE || m_mode == RENAME );
|
|
|
|
m_files.clear();
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BatchRenamer::escape( TQString & text, const TQString & token, const TQString & sequence )
|
|
|
|
{
|
|
|
|
text.replace( token, sequence );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString & BatchRenamer::doEscape( TQString& text, bool filename )
|
|
|
|
{
|
|
|
|
if( filename ) {
|
|
|
|
BatchRenamer::escape( text, "&", TQChar( 60000 ) );
|
|
|
|
BatchRenamer::escape( text, "$", TQChar( 60001 ) );
|
|
|
|
BatchRenamer::escape( text, "%", TQChar( 60002 ) );
|
|
|
|
BatchRenamer::escape( text, "#", TQChar( 60004 ) );
|
|
|
|
BatchRenamer::escape( text, "[", TQChar( 60005 ) );
|
|
|
|
BatchRenamer::escape( text, "]", TQChar( 60006 ) );
|
|
|
|
BatchRenamer::escape( text, "\\", TQChar( 60007 ) );
|
|
|
|
BatchRenamer::escape( text, "/", TQChar( 60008 ) );
|
|
|
|
BatchRenamer::escape( text, "{", TQChar( 60009 ) );
|
|
|
|
BatchRenamer::escape( text, "}", TQChar( 60010 ) );
|
|
|
|
BatchRenamer::escape( text, "*", TQChar( 60011 ) );
|
|
|
|
} else {
|
|
|
|
BatchRenamer::escape( text, "\\&", TQChar( 60000 ) );
|
|
|
|
BatchRenamer::escape( text, "\\$", TQChar( 60001 ) );
|
|
|
|
BatchRenamer::escape( text, "\\%", TQChar( 60002 ) );
|
|
|
|
BatchRenamer::escape( text, "\\#", TQChar( 60004 ) );
|
|
|
|
BatchRenamer::escape( text, "\\[", TQChar( 60005 ) );
|
|
|
|
BatchRenamer::escape( text, "\\]", TQChar( 60006 ) );
|
|
|
|
BatchRenamer::escape( text, "\\\\", TQChar( 60007 ) );
|
|
|
|
BatchRenamer::escape( text, "\\/", TQChar( 60008 ) );
|
|
|
|
BatchRenamer::escape( text, "\\{", TQChar( 60009 ) );
|
|
|
|
BatchRenamer::escape( text, "\\}", TQChar( 60010 ) );
|
|
|
|
BatchRenamer::escape( text, "\\*", TQChar( 60011 ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString & BatchRenamer::unEscape( TQString & text )
|
|
|
|
{
|
|
|
|
BatchRenamer::escape( text, TQChar( 60000 ), "&" );
|
|
|
|
BatchRenamer::escape( text, TQChar( 60001 ), "$" );
|
|
|
|
BatchRenamer::escape( text, TQChar( 60002 ), "%" );
|
|
|
|
BatchRenamer::escape( text, TQChar( 60004 ), "#" );
|
|
|
|
BatchRenamer::escape( text, TQChar( 60005 ), "[" );
|
|
|
|
BatchRenamer::escape( text, TQChar( 60006 ), "]" );
|
|
|
|
BatchRenamer::escape( text, TQChar( 60007 ), "\\" );
|
|
|
|
// %252f == /, it seems that filenames on unix cannot contain
|
|
|
|
// a /. So I use %252f, at least konqui displays it correctly
|
|
|
|
// this was needed, so that plugins that return a slash do not cause errors
|
|
|
|
BatchRenamer::escape( text, TQChar( 60008 ), "%2f" );
|
|
|
|
BatchRenamer::escape( text, TQChar( 60009 ), "{" );
|
|
|
|
BatchRenamer::escape( text, TQChar( 60010 ), "}" );
|
|
|
|
BatchRenamer::escape( text, TQChar( 60011 ), "*" );
|
|
|
|
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
int BatchRenamer::getCharacters( int n )
|
|
|
|
{
|
|
|
|
TQString s;
|
|
|
|
s.sprintf( "%i", n );
|
|
|
|
return s.length();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString BatchRenamer::findAndProcess( const TQString & token, TQString text, const TQString & replace )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* pos can here be -1 because
|
|
|
|
* findRev is called with it as a
|
|
|
|
* value !
|
|
|
|
*/
|
|
|
|
text.replace( token, replace );
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString BatchRenamer::findNumbers( TQString text, int count, int i )
|
|
|
|
{
|
|
|
|
// Rewritten in Version 0.8
|
|
|
|
// Added numbers skipping in 1.3
|
|
|
|
// Changed again in Version 1.8 to optimize it and fix a bug with skipping numbers
|
|
|
|
int pos = 0, counter = 1;
|
|
|
|
tCounterValues countervalues;
|
|
|
|
countervalues.start = m_index;
|
|
|
|
countervalues.step = m_step;
|
|
|
|
|
|
|
|
if( text.contains( "#", FALSE ) <= 0 )
|
|
|
|
return text;
|
|
|
|
|
|
|
|
pos = text.find("#", pos);
|
|
|
|
pos++;
|
|
|
|
while( text[pos] == '#' ) {
|
|
|
|
text.remove(pos, 1);
|
|
|
|
counter++;
|
|
|
|
}
|
|
|
|
|
|
|
|
findNumberAppendix( text, pos, &countervalues.start, &countervalues.step );
|
|
|
|
|
|
|
|
pos = text.find("#", 0);
|
|
|
|
|
|
|
|
if( (signed int)m_counters.count() <= m_counter_index )
|
|
|
|
{
|
|
|
|
countervalues.value = countervalues.start - countervalues.step;
|
|
|
|
// other wise the counter would start at:
|
|
|
|
// start + step instead of start
|
|
|
|
m_counters.append( countervalues );
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
m_counters[m_counter_index].value += m_counters[m_counter_index].step;
|
|
|
|
} while( m_skip.contains( m_counters[m_counter_index].value ) );
|
|
|
|
|
|
|
|
/*
|
|
|
|
int v = start + (i*step) + m_skip_add[m_counter_index];
|
|
|
|
for( unsigned int z = 0; z < m_skip.count(); z++ ) {
|
|
|
|
if( m_skip[z] == v ) {
|
|
|
|
m_skip_add[m_counter_index] += step;
|
|
|
|
v += step;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
TQString temp;
|
|
|
|
temp.sprintf("%0*i", counter, m_counters[m_counter_index].value );
|
|
|
|
text.replace( pos, 1, temp);
|
|
|
|
|
|
|
|
++m_counter_index;
|
|
|
|
return findNumbers( text, count, i );
|
|
|
|
}
|
|
|
|
|
|
|
|
void BatchRenamer::findNumberAppendix( TQString & text, int pos, int* start, int* step )
|
|
|
|
{
|
|
|
|
TQString appendix = TQString();
|
|
|
|
int tmp = 0;
|
|
|
|
int end = 0;
|
|
|
|
bool ok = false;
|
|
|
|
|
|
|
|
if( text[pos] == '{' && (end = text.find( "}", pos )) > -1)
|
|
|
|
{
|
|
|
|
//tqDebug("Found an appendix:" + appendix );
|
|
|
|
appendix = text.mid( pos + 1, end - pos - 1);
|
|
|
|
text.remove( pos, end - pos + 1 );
|
|
|
|
|
|
|
|
tmp = appendix.section( ';', 0, 0 ).toInt( &ok ); // first section = start index
|
|
|
|
if( ok )
|
|
|
|
*start = tmp;
|
|
|
|
|
|
|
|
tmp = appendix.section( ';', 1, 1 ).toInt( &ok ); // second section = stepping
|
|
|
|
if( ok )
|
|
|
|
*step = tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString BatchRenamer::findStar( const TQString & oldname, TQString text )
|
|
|
|
{
|
|
|
|
int pos = -1;
|
|
|
|
do {
|
|
|
|
pos = text.findRev("*", pos);
|
|
|
|
if( pos >= 0 ) {
|
|
|
|
TQString tmp = oldname.lower();
|
|
|
|
if( tmp[0].isLetter() )
|
|
|
|
tmp[0] = tmp[0].upper();
|
|
|
|
|
|
|
|
for( unsigned int i = 0; i < tmp.length(); i++ )
|
|
|
|
if( tmp[i+1].isLetter() && !tmp[i].isLetter() &&
|
|
|
|
tmp[i] != '\'' && tmp[i] != '?' && tmp[i] != '`' )
|
|
|
|
tmp[i+1] = tmp[i+1].upper();
|
|
|
|
|
|
|
|
text.replace( pos, 1, tmp);
|
|
|
|
}
|
|
|
|
} while( pos >= 0 );
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString BatchRenamer::findBrackets( TQString oldname, TQString text, int i )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* looks for a statement in brackets [ ]
|
|
|
|
* and calls findToken() with this statement.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int num, pos = -1, a;
|
|
|
|
TQString token;
|
|
|
|
|
|
|
|
if( text.contains("]", FALSE) <= 0 || text.isEmpty() )
|
|
|
|
return text;
|
|
|
|
|
|
|
|
num = text.contains("[", FALSE);
|
|
|
|
if(num <= 0 )
|
|
|
|
return text;
|
|
|
|
|
|
|
|
pos = text.findRev("[", pos);
|
|
|
|
a = text.find("]", pos );
|
|
|
|
if( a < 0 && pos >= 0 )
|
|
|
|
return text;
|
|
|
|
|
|
|
|
if( pos < 0 && a >= 0 )
|
|
|
|
return text;
|
|
|
|
|
|
|
|
if( pos >= 0 && a >= 0 ) {
|
|
|
|
token = text.mid( pos+1, (a-pos)-1 );
|
|
|
|
|
|
|
|
// support [4-[length]]
|
|
|
|
token = findBrackets( oldname, token, i );
|
|
|
|
|
|
|
|
text.remove( pos, (a-pos)+1 );
|
|
|
|
text.insert( pos, findToken( oldname, token, i ));
|
|
|
|
}
|
|
|
|
return findBrackets( oldname, text, i );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString BatchRenamer::processToken( TQString token, TQString oldname, int i )
|
|
|
|
{
|
|
|
|
TQString tmp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Call here all functions that handle
|
|
|
|
* arguments in brackets.
|
|
|
|
*/
|
|
|
|
tmp = findPartStrings( oldname, token );
|
|
|
|
if( !tmp.isEmpty() )
|
|
|
|
return tmp;
|
|
|
|
|
|
|
|
tmp = findDirName( token, m_files[i].src.directory );
|
|
|
|
if( !tmp.isEmpty() )
|
|
|
|
return tmp;
|
|
|
|
|
|
|
|
tmp = findLength( token, m_files[i].src.name );
|
|
|
|
if( !tmp.isEmpty() )
|
|
|
|
return tmp;
|
|
|
|
|
|
|
|
Plugin* p = plug->findPlugin( token );
|
|
|
|
if( p )
|
|
|
|
{
|
|
|
|
tmp = p->processFile( this, i, token, TYPE_BRACKET );
|
|
|
|
if( !tmp.isNull() )
|
|
|
|
{
|
|
|
|
doEscape( tmp );
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Maybe I should remove this!
|
|
|
|
* Krename simply ignores unknown tokens!
|
|
|
|
* Usefull for the MP3 Plugin!
|
|
|
|
*/
|
|
|
|
return TQString();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString BatchRenamer::findToken( TQString oldname, TQString token, int i )
|
|
|
|
{
|
|
|
|
enum conversion { LOWER, UPPER, MIXED, STAR, STRIP, NONE, EMPTY, NUMBER };
|
|
|
|
unsigned int numwidth = 0;
|
|
|
|
|
|
|
|
conversion c = EMPTY;
|
|
|
|
if( !token.left(1).compare("$") )
|
|
|
|
c = NONE;
|
|
|
|
else if( !token.left(1).compare("%") )
|
|
|
|
c = LOWER;
|
|
|
|
else if( !token.left(1).compare("&") )
|
|
|
|
c = UPPER;
|
|
|
|
else if( !token.left(1).compare("") )
|
|
|
|
c = MIXED;
|
|
|
|
else if( !token.left(1).compare("*") )
|
|
|
|
c = STAR;
|
|
|
|
else if( !token.left(1).compare("\\") )
|
|
|
|
c = STRIP;
|
|
|
|
else if( !token.left(1).compare("#") ) {
|
|
|
|
while( !token.left(1).compare("#") ) {
|
|
|
|
token.remove( 0, 1 );
|
|
|
|
++numwidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
c = NUMBER;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( c != EMPTY && c != NUMBER )
|
|
|
|
token.remove( 0, 1 );
|
|
|
|
|
|
|
|
TQString save = token;
|
|
|
|
token = processToken( token, oldname, i );
|
|
|
|
|
|
|
|
switch( c ) {
|
|
|
|
case LOWER:
|
|
|
|
token = token.lower();
|
|
|
|
break;
|
|
|
|
case UPPER:
|
|
|
|
token = token.upper();
|
|
|
|
break;
|
|
|
|
case MIXED:
|
|
|
|
token = token.lower();
|
|
|
|
token.replace( 0, 1, token[0].upper());
|
|
|
|
break;
|
|
|
|
case STAR:
|
|
|
|
token = findStar( token, "*" );
|
|
|
|
break;
|
|
|
|
case STRIP:
|
|
|
|
token = token.stripWhiteSpace();
|
|
|
|
break;
|
|
|
|
case NUMBER:
|
|
|
|
{
|
|
|
|
bool b = false;
|
|
|
|
int n = token.toInt( &b );
|
|
|
|
if( b )
|
|
|
|
token = token.sprintf("%0*i", numwidth, n );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
doEscape( token );
|
|
|
|
return token;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString BatchRenamer::findPartStrings( TQString oldname, TQString token )
|
|
|
|
{
|
|
|
|
TQString first, second;
|
|
|
|
int pos = -1;
|
|
|
|
|
|
|
|
// parse things like [2;4{[dirname]}]
|
|
|
|
if( token.contains( "{" ) >= 1 && token.contains( "}" ) >= 1 ) {
|
|
|
|
int pos = token.find( "{" );
|
|
|
|
oldname = token.mid( pos + 1, token.findRev( "}" ) - pos - 1 );
|
|
|
|
token = token.left( pos );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( token.contains("-") ) {
|
|
|
|
pos = token.find( "-", 0 );
|
|
|
|
first = token.left( pos );
|
|
|
|
// ------- Code OK ^ !
|
|
|
|
|
|
|
|
second = token.mid( pos+1, token.length() );
|
|
|
|
|
|
|
|
// version < 1.7
|
|
|
|
// return oldname.mid( first.toInt()-1, second.toInt()-first.toInt() +1 );
|
|
|
|
// version > 1.7
|
|
|
|
//return oldname.mid( first.toInt()-1, second.toInt()-first.toInt() );
|
|
|
|
// version > 1.8
|
|
|
|
|
|
|
|
bool ok;
|
|
|
|
int sec = second.toInt( &ok );
|
|
|
|
if( !ok || sec == 0 )
|
|
|
|
sec = oldname.length();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* x should not be larger than the old name
|
|
|
|
* and not smaller than zero.
|
|
|
|
*/
|
|
|
|
int x = sec-first.toInt();
|
|
|
|
if( x > (signed int)oldname.length() || x < 0 )
|
|
|
|
x = oldname.length()-first.toInt();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if I would comment my code I would understand this line :)
|
|
|
|
* without this line, there is sometimes the last letter
|
|
|
|
* of a filename missing.
|
|
|
|
*/
|
|
|
|
if( x != -1 )
|
|
|
|
x++;
|
|
|
|
|
|
|
|
return oldname.mid( first.toInt()-1, x );
|
|
|
|
} else if( token.contains(";") ) {
|
|
|
|
pos = token.find( ";", 0 );
|
|
|
|
|
|
|
|
first = token.left( pos );
|
|
|
|
second = token.mid( pos+1, token.length() );
|
|
|
|
|
|
|
|
return oldname.mid( first.toInt()-1, second.toInt() );
|
|
|
|
} else {
|
|
|
|
bool ok = false;
|
|
|
|
int number = token.toInt( &ok );
|
|
|
|
|
|
|
|
if( ok && (number <= (signed int)oldname.length() && number > 0 ) )
|
|
|
|
return TQString(oldname[ number -1 ]);
|
|
|
|
else
|
|
|
|
return TQString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString BatchRenamer::findDirName( TQString token, TQString path )
|
|
|
|
{
|
|
|
|
if( token.left( 7 ).lower() == "dirname" ) {
|
|
|
|
if( path.right( 1 ) == "/" )
|
|
|
|
path = path.left( path.length() - 1);
|
|
|
|
|
|
|
|
int recursion = 1;
|
|
|
|
if( token.length() > 7 ) {
|
|
|
|
token = token.right( token.length() - 7 );
|
|
|
|
recursion = token.contains( "." );
|
|
|
|
if( recursion != (signed int)token.length() )
|
|
|
|
return TQString();
|
|
|
|
|
|
|
|
recursion++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return path.section( "/", recursion * -1, recursion * -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return TQString();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString BatchRenamer::findLength( const TQString & token, const TQString & name )
|
|
|
|
{
|
|
|
|
if( token.lower().startsWith( "length" ) ) {
|
|
|
|
int minus = 0;
|
|
|
|
if( token[6] == '-' ) {
|
|
|
|
bool n = false;
|
|
|
|
minus = token.mid( 7, token.length() - 7 ).toInt( &n );
|
|
|
|
if( !n )
|
|
|
|
minus = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TQString::number( name.length() - minus );
|
|
|
|
}
|
|
|
|
|
|
|
|
return TQString();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString BatchRenamer::findReplace( TQString text )
|
|
|
|
{
|
|
|
|
// Call for each element in replace strings doReplace with correct values
|
|
|
|
for( unsigned int i = 0; i < m_replace.count(); i++ ) {
|
|
|
|
replacestrings s = m_replace[i];
|
|
|
|
text = doReplace( text, unEscape( s.find ), s.replace, s.reg );
|
|
|
|
}
|
|
|
|
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString BatchRenamer::doReplace( TQString text, TQString find, TQString replace, bool reg )
|
|
|
|
{
|
|
|
|
if( !reg )
|
|
|
|
{
|
|
|
|
// we use the escaped text here because the user might want
|
|
|
|
// to find a "&" and replace it
|
|
|
|
text.replace( doEscape( find ), replace );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// no doEscape() here for the regexp, because it would destroy our regular expression
|
|
|
|
// other wise we will not find stuff like $, [ in the text
|
|
|
|
text = doEscape( unEscape( text ).replace( TQRegExp( find ), replace ) );
|
|
|
|
}
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BatchRenamer::writeUndoScript( TQTextStream* t )
|
|
|
|
{
|
|
|
|
// write header comments
|
|
|
|
(*t) << "#!/bin/bash" << endl
|
|
|
|
<< "# KRename Undo Script" << endl << "#" << endl
|
|
|
|
<< "# KRename was written by:" << endl
|
|
|
|
<< "# Dominik Seichter <domseichter@web.de>" << endl
|
|
|
|
<< "# http://krename.sourceforge.net" << endl << "#" << endl
|
|
|
|
<< "# Script generated by KRename Version: " << VERSION << endl << endl
|
|
|
|
<< "# This script must be started with the option --krename to work!" << endl;
|
|
|
|
|
|
|
|
// write functions:
|
|
|
|
(*t) << "echo \"KRename Undo Script\"" << endl
|
|
|
|
<< "echo \"http://krename.sourceforge.net\"" << endl
|
|
|
|
<< "echo \"\"" << endl;
|
|
|
|
|
|
|
|
(*t) << "if test --krename = $1 ; then" << endl
|
|
|
|
<< " echo \"\"" << endl
|
|
|
|
<< "else" << endl
|
|
|
|
<< " echo \"You have to start this script\"" << endl
|
|
|
|
<< " echo \"with the command line option\"" << endl
|
|
|
|
<< " echo \"--krename\"" << endl
|
|
|
|
<< " echo \"to undo a rename operation.\"" << endl
|
|
|
|
<< " exit" << endl
|
|
|
|
<< "fi" << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BatchRenamer::parseSubdirs( data* f )
|
|
|
|
{
|
|
|
|
int pos = 0;
|
|
|
|
if( (pos = f->dst.name.findRev( "/", -1 ) ) > 0 ) {
|
|
|
|
TQString dirs = f->dst.name.left( pos );
|
|
|
|
f->dst.name = f->dst.name.right( f->dst.name.length() - pos - 1 );
|
|
|
|
f->dst.directory += ( f->dst.directory.right( 1 ) == "/" ) ? "" : "/";
|
|
|
|
|
|
|
|
// create the missing subdir now
|
|
|
|
int i = 0;
|
|
|
|
TQString d = TQString::null;
|
|
|
|
for (d = dirs.section("/", i, i, TQString::SectionSkipEmpty);
|
|
|
|
!d.isEmpty();
|
|
|
|
i++, d = dirs.section("/", i, i, TQString::SectionSkipEmpty))
|
|
|
|
{
|
|
|
|
KURL url = f->dst.url;
|
|
|
|
// it is important to unescape here
|
|
|
|
// to support dirnames containing "&" or
|
|
|
|
// similar tokens
|
|
|
|
url.addPath( unEscape( d ) );
|
|
|
|
if (!NetAccess::exists(url, false, 0) && !NetAccess::mkdir(url, 0, -1))
|
|
|
|
// TODO: GUI bug report
|
|
|
|
tqDebug("Can't create %s", url.prettyURL().latin1() );
|
|
|
|
|
|
|
|
f->dst.url.addPath( d );
|
|
|
|
f->dst.directory.append( d + "/" );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString BatchRenamer::buildFilename( fileentry* entry, bool dir )
|
|
|
|
{
|
|
|
|
TQString filename = ( dir ? entry->directory : TQString() ) + entry->name + ( entry->extension.isEmpty() ? TQString() : TQString(".") ) + entry->extension;
|
|
|
|
// unescape here as filename is still escaped
|
|
|
|
unEscape( filename );
|
|
|
|
return filename;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool BatchRenamer::applyManualChanges( int i )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* The last step: make changes of
|
|
|
|
* the user visible
|
|
|
|
*/
|
|
|
|
|
|
|
|
if( !m_changes.isEmpty() )
|
|
|
|
for( unsigned int z = 0; z < m_changes.count(); z++ ) {
|
|
|
|
KURL file = m_changes[z].url;
|
|
|
|
if( file == m_files[i].src.url ) {
|
|
|
|
m_files[i].dst.name = m_changes[z].user;
|
|
|
|
// the file extension is already included
|
|
|
|
// in the users name
|
|
|
|
m_files[i].dst.extension = TQString();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BatchRenamer::findCounterReset( int i )
|
|
|
|
{
|
|
|
|
int z;
|
|
|
|
if( m_files[i-1].src.directory != m_files[i].src.directory )
|
|
|
|
for( z=0;z<(int)m_counters.count();z++ )
|
|
|
|
{
|
|
|
|
m_counters[z].value = m_counters[z].start - m_counters[z].step;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|