/*************************************************************************** krquery.cpp ------------------- copyright : (C) 2001 by Shie Erlich & Rafi Yanai email : krusader@users.sourceforge.net web site : http://krusader.sourceforge.net --------------------------------------------------------------------------- Description *************************************************************************** A db dD d8888b. db db .d8888. .d8b. d8888b. d88888b d8888b. 88 ,8P' 88 `8D 88 88 88' YP d8' `8b 88 `8D 88' 88 `8D 88,8P 88oobY' 88 88 `8bo. 88ooo88 88 88 88ooooo 88oobY' 88`8b 88`8b 88 88 `Y8b. 88~~~88 88 88 88~~~~~ 88`8b 88 `88. 88 `88. 88b d88 db 8D 88 88 88 .8D 88. 88 `88. YP YD 88 YD ~Y8888P' `8888Y' YP YP Y8888D' Y88888P 88 YD S o u r c e F i l e *************************************************************************** * * * 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. * * * ***************************************************************************/ #include "krquery.h" #include "../krusader.h" #include "../resources.h" #include "vfs.h" #include "krarchandler.h" #include "krpermhandler.h" #include #include #include #include #include #include #include #include #include #include #define STATUS_SEND_DELAY 250 #define MAX_LINE_LEN 500 // set the defaults KRQuery::KRQuery(): TQObject(), matchesCaseSensitive(true), bNull( true ), contain(TQString()),containCaseSensetive(true), containWholeWord(false),containOnRemote(false), minSize(0),maxSize(0),newerThen(0),olderThen(0), owner(TQString()),group(TQString()),perm(TQString()), type(TQString()),inArchive(false),recurse(true), followLinksP(true), receivedBuffer( 0 ), processEventsConnected(0) {} // set the defaults KRQuery::KRQuery( const TQString &name, bool matchCase ) : TQObject(), bNull( true ),contain(TQString()),containCaseSensetive(true), containWholeWord(false), containOnRemote(false), minSize(0),maxSize(0),newerThen(0),olderThen(0), owner(TQString()),group(TQString()),perm(TQString()), type(TQString()),inArchive(false),recurse(true), followLinksP(true), receivedBuffer( 0 ), processEventsConnected(0) { setNameFilter( name, matchCase ); } KRQuery::KRQuery( const KRQuery & that ) : TQObject(), receivedBuffer( 0 ), processEventsConnected(0) { *this = that; } KRQuery::~KRQuery() { if( receivedBuffer ) delete []receivedBuffer; receivedBuffer = 0; } KRQuery& KRQuery::operator=(const KRQuery &old) { matches = old.matches; excludes = old.excludes; includedDirs = old.includedDirs; excludedDirs = old.excludedDirs; matchesCaseSensitive = old.matchesCaseSensitive; bNull = old.bNull; contain = old.contain; containCaseSensetive = old.containCaseSensetive; containWholeWord = old.containWholeWord; containOnRemote = old.containOnRemote; minSize = old.minSize; maxSize = old.maxSize; newerThen = old.newerThen; olderThen = old.olderThen; owner = old.owner; group = old.group; perm = old.perm; type = old.type; customType = old.customType; inArchive = old.inArchive; recurse = old.recurse; followLinksP = old.followLinksP; whereToSearch = old.whereToSearch; whereNotToSearch = old.whereNotToSearch; origFilter = old.origFilter; return *this; } void KRQuery::connectNotify ( const char * signal ) { TQString signalString = TQString( signal ).replace( " ", "" ); TQString processString = TQString( TQT_SIGNAL( processEvents( bool & ) ) ).replace( " ", "" ); if( signalString == processString ) processEventsConnected++; } void KRQuery::disconnectNotify ( const char * signal ) { TQString signalString = TQString( signal ).replace( " ", "" ); TQString processString = TQString( TQT_SIGNAL( processEvents( bool & ) ) ).replace( " ", "" ); if( signalString == processString ) processEventsConnected--; } bool KRQuery::checkPerm( TQString filePerm ) const { for ( int i = 0; i < 9; ++i ) if ( perm[ i ] != '?' && perm[ i ] != filePerm[ i + 1 ] ) return false; return true; } bool KRQuery::checkType( TQString mime ) const { if ( type == mime ) return true; if ( type == i18n( "Archives" ) ) return KRarcHandler::arcSupported( mime.right( 4 ) ); if ( type == i18n( "Directories" ) ) return mime.contains( "directory" ); if ( type == i18n( "Image Files" ) ) return mime.contains( "image/" ); if ( type == i18n( "Text Files" ) ) return mime.contains( "text/" ); if ( type == i18n( "Video Files" ) ) return mime.contains( "video/" ); if ( type == i18n( "Audio Files" ) ) return mime.contains( "audio/" ); if ( type == i18n( "Custom" ) ) return customType.contains( mime ); return false; } bool KRQuery::match( const TQString & name ) const { return matchCommon( name, matches, excludes ); } bool KRQuery::matchDirName( const TQString & name ) const { return matchCommon( name, includedDirs, excludedDirs ); } bool KRQuery::matchCommon( const TQString &nameIn, const TQStringList &matchList, const TQStringList &excludeList ) const { if( excludeList.count() == 0 && matchList.count() == 0 ) /* true if there's no match condition */ return true; TQString name( nameIn ); int ndx = nameIn.findRev( '/' ); // virtual filenames may contain '/' if( ndx != -1 ) // but the end of the filename is OK name = nameIn.mid( ndx + 1 ); unsigned int len; for ( unsigned int i = 0; i < excludeList.count(); ++i ) { TQRegExp( *excludeList.at( i ), matchesCaseSensitive, true ).match( name, 0, ( int* ) & len ); if ( len == name.length() ) return false; } if( matchList.count() == 0 ) return true; for ( unsigned int i = 0; i < matchList.count(); ++i ) { TQRegExp( *matchList.at( i ), matchesCaseSensitive, true ).match( name, 0, ( int* ) & len ); if ( len == name.length() ) return true; } return false; } bool KRQuery::match( vfile *vf ) const { if( vf->vfile_isDir() && !matchDirName( vf->vfile_getName() ) ) return false; // see if the name matches if ( !match( vf->vfile_getName() ) ) return false; // checking the mime if( !type.isEmpty() && !checkType( vf->vfile_getMime( true ) ) ) return false; // check that the size fit TDEIO::filesize_t size = vf->vfile_getSize(); if ( minSize && size < minSize ) return false; if ( maxSize && size > maxSize ) return false; // check the time frame time_t mtime = vf->vfile_getTime_t(); if ( olderThen && mtime > olderThen ) return false; if ( newerThen && mtime < newerThen ) return false; // check owner name if ( !owner.isEmpty() && vf->vfile_getOwner() != owner ) return false; // check group name if ( !group.isEmpty() && vf->vfile_getGroup() != group ) return false; //check permission if ( !perm.isEmpty() && !checkPerm( vf->vfile_getPerm() ) ) return false; if ( !contain.isEmpty() ) { if( (totalBytes = vf->vfile_getSize()) == 0 ) totalBytes++; // sanity receivedBytes = 0; if( receivedBuffer ) { delete []receivedBuffer; receivedBuffer = 0; } fileName = vf->vfile_getName(); timer.start(); if( vf->vfile_getUrl().isLocalFile() ) { if( !containsContent( vf->vfile_getUrl().path() ) ) return false; } else { if( containOnRemote ) { if( processEventsConnected == 0 ) return false; if( !containsContent( vf->vfile_getUrl() ) ) return false; } } } return true; } bool KRQuery::match( KFileItem *kfi ) const { mode_t mode = kfi->mode() | kfi->permissions(); TQString perm = KRpermHandler::mode2TQString( mode ); if ( kfi->isDir() ) perm[ 0 ] = 'd'; vfile temp( kfi->text(), kfi->size(), perm, kfi->time( TDEIO::UDS_MODIFICATION_TIME ), kfi->isLink(), kfi->user(), kfi->group(), kfi->user(), kfi->mimetype(), kfi->linkDest(), mode ); return match( &temp ); } // takes the string and adds BOLD to it, so that when it is displayed, // the grepped text will be bold void fixFoundTextForDisplay(TQString& haystack, int start, int length) { TQString before = haystack.left( start ); TQString text = haystack.mid( start, length ); TQString after = haystack.mid( start + length ); before.replace( '&', "&" ); before.replace( '<', "<" ); before.replace( '>', ">" ); text.replace( '&', "&" ); text.replace( '<', "<" ); text.replace( '>', ">" ); after.replace( '&', "&" ); after.replace( '<', "<" ); after.replace( '>', ">" ); haystack = (""+before+""+text+""+after+"" ); } bool KRQuery::checkBuffer( const char *buf, int len ) const { if( len == 0 ) { // last block? if( receivedBuffer ) { bool result = checkLines( receivedBuffer, receivedBufferLen ); delete []receivedBuffer; receivedBuffer = 0; return result; } return false; } int after = len; while( buf[ after-1 ] != '\n' && buf[ after-1 ] != 0 ) { after--; if( after <= 0 || after <= len - MAX_LINE_LEN ) { after = len; // if there's no in MAX_LINE_LEN, we break the line break; // breaking the line causes problems at Unicode characters } } if( receivedBuffer ) { int previous = 0; while( previous < after && previous < MAX_LINE_LEN && buf[previous] != '\n' && buf[previous] != 0 ) previous++; char * str = new char[ receivedBufferLen + previous ]; memcpy( str, receivedBuffer, receivedBufferLen ); delete []receivedBuffer; receivedBuffer = 0; memcpy( str + receivedBufferLen, buf, previous ); if( checkLines( str, receivedBufferLen+previous ) ) { delete []str; return true; } delete []str; if( after > previous && checkLines( buf+previous, after-previous ) ) return true; } else if( checkLines( buf, after ) ) return true; if( after < len ) { receivedBufferLen = len-after; receivedBuffer = new char [ receivedBufferLen ]; memcpy( receivedBuffer, buf+after, receivedBufferLen ); } return false; } bool KRQuery::checkLines( const char * buf, int len ) const { TQStringList list; int start = 0; int k = 0; while( k < len ) { if( buf[ k ] == 0 || buf[ k ] == '\n' ) { if( k != start ) { TQString line = TQTextCodec::codecForLocale()->toUnicode( buf + start, k - start ); if( !line.isEmpty() ) list << line; } start = k + 1; } k++; } if( start != k ) { TQString line = TQTextCodec::codecForLocale()->toUnicode( buf + start, k - start ); if( !line.isEmpty() ) list << line; } for( unsigned int i=0; i != list.count(); i++ ) { TQString line = list[ i ]; int ndx = 0; if ( line.isNull() ) return false; if ( containWholeWord ) { while ( ( ndx = line.find( contain, ndx, containCaseSensetive ) ) != -1 ) { TQChar before = line.at( ndx - 1 ); TQChar after = line.at( ndx + contain.length() ); if ( !before.isLetterOrNumber() && !after.isLetterOrNumber() && after != '_' && before != '_' ) { lastSuccessfulGrep = line; fixFoundTextForDisplay(lastSuccessfulGrep, ndx, contain.length()); return true; } ndx++; } } else if ( (ndx = line.find( contain, 0, containCaseSensetive )) != -1 ) { lastSuccessfulGrep = line; fixFoundTextForDisplay(lastSuccessfulGrep, ndx, contain.length()); return true; } } return false; } bool KRQuery::containsContent( TQString file ) const { TQFile qf( file ); if( !qf.open( IO_ReadOnly ) ) return false; char buffer[ 1440 ]; // 2k buffer while ( !qf.atEnd() ) { int bytes = qf.readBlock( buffer, sizeof( buffer ) ); if( bytes <= 0 ) break; receivedBytes += bytes; if( checkBuffer( buffer, bytes ) ) return true; if( checkTimer() ) { bool stopped = false; emit ((KRQuery *)this)->processEvents( stopped ); if( stopped ) return false; } } if( checkBuffer( buffer, 0 ) ) return true; lastSuccessfulGrep = TQString(); // nothing was found return false; } bool KRQuery::containsContent( KURL url ) const { TDEIO::TransferJob *contentReader = TDEIO::get( url, false, false ); connect(contentReader, TQT_SIGNAL(data(TDEIO::Job *, const TQByteArray &)), this, TQT_SLOT(containsContentData(TDEIO::Job *, const TQByteArray &))); connect(contentReader, TQT_SIGNAL( result( TDEIO::Job* ) ), this, TQT_SLOT(containsContentFinished( TDEIO::Job* ) ) ); busy = true; containsContentResult = false; bool stopped = false; while( busy && !stopped ) { checkTimer(); emit ((KRQuery *)this)->processEvents( stopped ); } if( busy ) { contentReader->kill(); busy = false; } return containsContentResult; } void KRQuery::containsContentData(TDEIO::Job *job, const TQByteArray &array) { receivedBytes += array.size(); if( checkBuffer( array.data(), array.size() ) ) { containsContentResult = true; containsContentFinished( job ); job->kill(); return; } checkTimer(); } void KRQuery::containsContentFinished( TDEIO::Job * ) { busy = false; } bool KRQuery::checkTimer() const { if( timer.elapsed() >= STATUS_SEND_DELAY ) { int pcnt = (int)(100.*(double)receivedBytes/(double)totalBytes + .5); TQString message = i18n( "Searching content of '%1' (%2%)" ) .arg( fileName ).arg( pcnt ); timer.start(); emit ((KRQuery *)this)->status( message ); return true; } return false; } TQStringList KRQuery::split( TQString str ) { TQStringList list; unsigned splitNdx = 0; unsigned startNdx = 0; bool quotation = false; while( splitNdx < str.length() ) { if( str[ splitNdx ] == '"' ) quotation = !quotation; if( !quotation && str[ splitNdx ] == ' ') { TQString section = str.mid( startNdx, splitNdx - startNdx ); startNdx = splitNdx+1; if( section.startsWith( "\"" ) && section.endsWith( "\"" ) && section.length() >=2 ) section = section.mid( 1, section.length()-2 ); if( !section.isEmpty() ) list.append( section ); } splitNdx++; } if( startNdx < splitNdx ) { TQString section = str.mid( startNdx, splitNdx - startNdx ); if( section.startsWith( "\"" ) && section.endsWith( "\"" ) && section.length() >=2 ) section = section.mid( 1, section.length()-2 ); if( !section.isEmpty() ) list.append( section ); } return list; } void KRQuery::setNameFilter( const TQString &text, bool cs ) { bNull = false; matchesCaseSensitive = cs; origFilter = text; TQString matchText = text; TQString excludeText; unsigned excludeNdx = 0; bool quotationMark = 0; while( excludeNdx < matchText.length() ) { if( matchText[ excludeNdx ] == '"' ) quotationMark = !quotationMark; if( !quotationMark ) { if( matchText[ excludeNdx ] == '|' ) break; } excludeNdx++; } if( excludeNdx < matchText.length() ) { excludeText = matchText.mid( excludeNdx + 1 ).stripWhiteSpace(); matchText.truncate( excludeNdx ); matchText = matchText.stripWhiteSpace(); if( matchText.isEmpty() ) matchText = "*"; } unsigned i; matches = split( matchText ); includedDirs.clear(); for( i=0; i < matches.count(); ) { if( matches[ i ].endsWith( "/" ) ) { includedDirs.push_back( matches[ i ].left( matches[ i ].length() - 1 ) ); matches.remove( matches.at( i ) ); continue; } if( !matches[ i ].contains( "*" ) && !matches[ i ].contains( "?" ) ) matches[ i ] = "*" + matches[ i ] + "*"; i++; } excludes = split( excludeText ); excludedDirs.clear(); for( i=0; i < excludes.count(); ) { if( excludes[ i ].endsWith( "/" ) ) { excludedDirs.push_back( excludes[ i ].left( excludes[ i ].length() - 1 ) ); excludes.remove( excludes.at( i ) ); continue; } if( !excludes[ i ].contains( "*" ) && !excludes[ i ].contains( "?" ) ) excludes[ i ] = "*" + excludes[ i ] + "*"; i++; } } void KRQuery::setContent( const TQString &content, bool cs, bool wholeWord, bool remoteSearch ) { bNull = false; contain = content; containCaseSensetive = cs; containWholeWord = wholeWord; containOnRemote = remoteSearch; } void KRQuery::setMinimumFileSize( TDEIO::filesize_t minimumSize ) { bNull = false; minSize = minimumSize; } void KRQuery::setMaximumFileSize( TDEIO::filesize_t maximumSize ) { bNull = false; maxSize = maximumSize; } void KRQuery::setNewerThan( time_t time ) { bNull = false; newerThen = time; } void KRQuery::setOlderThan( time_t time ) { bNull = false; olderThen = time; } void KRQuery::setOwner( const TQString &ownerIn ) { bNull = false; owner = ownerIn; } void KRQuery::setGroup( const TQString &groupIn ) { bNull = false; group = groupIn; } void KRQuery::setPermissions( const TQString &permIn ) { bNull = false; perm = permIn; } void KRQuery::setMimeType( const TQString &typeIn, TQStringList customList ) { bNull = false; type = typeIn; customType = customList; } bool KRQuery::isExcluded( const KURL &url ) { for ( unsigned int i = 0; i < whereNotToSearch.count(); ++i ) if( whereNotToSearch [ i ].isParentOf( url ) || url.equals( whereNotToSearch [ i ], true ) ) return true; if( !matchDirName( url.fileName() ) ) return true; return false; } void KRQuery::setSearchInDirs( const KURL::List &urls ) { whereToSearch.clear(); for( unsigned int i = 0; i < urls.count(); ++i ) { TQString url = urls[ i ].url(); KURL completed = vfs::fromPathOrURL( KURLCompletion::replacedPath( url, true, true ) ); whereToSearch.append( completed ); } } void KRQuery::setDontSearchInDirs( const KURL::List &urls ) { whereNotToSearch.clear(); for( unsigned int i = 0; i < urls.count(); ++i ) { TQString url = urls[ i ].url(); KURL completed = vfs::fromPathOrURL( KURLCompletion::replacedPath( url, true, true ) ); whereNotToSearch.append( completed ); } } #include "krquery.moc"