00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 #include <iostream>
00020 
00021 #include <qfile.h>
00022 #include <qptrlist.h>
00023 
00024 #include <kaboutdata.h>
00025 #include <kapplication.h>
00026 #include <kcmdlineargs.h>
00027 #include <kfilemetainfo.h>
00028 #include <klocale.h>
00029 #include <kpropertiesdialog.h>
00030 
00031 #include "fileprops.h"
00032 
00033 #define KFILEVERSION "0.2"
00034 #define INDENT "\t"
00035 
00036 using namespace std;
00037 
00038 static QString beatifyValue( const QString& value )
00039 {
00040     if ( value.isNull() )
00041         return QString("(no value for key available)");
00042     else if ( value.isEmpty() )
00043         return QString("(empty)");
00044 
00045     return value;
00046 }
00047 
00048 FileProps::FileProps( const QString& path, const QStringList& suppliedGroups )
00049     : m_dirty( false )
00050 {
00051     m_info = new KFileMetaInfo(path, QString::null, KFileMetaInfo::Everything);
00052     m_userSuppliedGroups = !suppliedGroups.isEmpty();
00053     m_groupsToUse = m_userSuppliedGroups ? suppliedGroups : m_info->groups();
00054 }
00055 
00056 FileProps::~FileProps()
00057 {
00058     sync();
00059     delete m_info;
00060 }
00061 
00062 bool FileProps::sync()
00063 {
00064     if ( !m_dirty )
00065         return true;
00066 
00067     return m_info->applyChanges();
00068 }
00069 
00070 bool FileProps::isValid() const
00071 {
00072     return m_info->isValid();
00073 }
00074 
00075 QStringList FileProps::supportedGroups() const
00076 {
00077     return m_info->supportedGroups();
00078 }
00079 
00080 QStringList FileProps::availableGroups() const
00081 {
00082     return m_info->groups();
00083 }
00084 
00085 QStringList FileProps::supportedKeys( const QString& group ) const
00086 {
00087     KFileMetaInfoGroup g = m_info->group( group );
00088     return g.supportedKeys();
00089 }
00090 
00091 QStringList FileProps::availableKeys( const QString& group ) const
00092 {
00093     KFileMetaInfoGroup g = m_info->group( group );
00094     QStringList allKeys = g.keys();
00095     QStringList ret;
00096     QStringList::ConstIterator it = allKeys.begin();
00097     for ( ; it != allKeys.end(); ++it )
00098     {
00099         if ( g.item( *it ).isValid() )
00100             ret.append( *it );
00101     }
00102 
00103     return ret;
00104 }
00105 
00106 QStringList FileProps::preferredKeys( const QString& group ) const
00107 {
00108     KFileMetaInfoGroup g = m_info->group( group );
00109     return g.preferredKeys();
00110 }
00111 
00112 QString FileProps::getValue( const QString& group,
00113                              const QString& key ) const
00114 {
00115     KFileMetaInfoGroup g = m_info->group( group );
00116     return FileProps::createKeyValue( g, key );
00117 }
00118 
00119 bool FileProps::setValue( const QString& group,
00120                           const QString& key, const QString &value )
00121 {
00122     KFileMetaInfoGroup g = m_info->group( group );
00123     bool wasAdded = false;
00124     if ( !g.isValid() )
00125     {
00126         if ( m_info->addGroup( group ) )
00127         {
00128             wasAdded = true;
00129             g = m_info->group( group );
00130         }
00131         else
00132             return false;
00133     }
00134 
00135     bool ok = g[key].setValue( value );
00136 
00137     if ( !ok && wasAdded ) 
00138         (void) m_info->removeGroup( group );
00139         
00140     m_dirty |= ok;
00141     return ok;
00142 }
00143 
00144 QStringList FileProps::allValues( const QString& group ) const
00145 {
00146     KFileMetaInfoGroup g = m_info->group( group );
00147     return FileProps::createKeyValueList( g, g.keys() );
00148 }
00149 
00150 QStringList FileProps::preferredValues( const QString& group ) const
00151 {
00152     KFileMetaInfoGroup g = m_info->group( group );
00153     return FileProps::createKeyValueList( g, g.preferredKeys() );
00154 }
00155 
00156 
00157 
00158 
00159 QString FileProps::createKeyValue( const KFileMetaInfoGroup& g,
00160                                    const QString& key )
00161 {
00162     static const int MAX_SPACE = 25;
00163     KFileMetaInfoItem item = g.item( key );
00164 
00165     QString result("%1");
00166     result = result.arg( (item.isValid() ? item.translatedKey() : key) + ":",
00167                          -MAX_SPACE );
00168     result.append( beatifyValue( item.string() ) );
00169 
00170     QString group("%1");
00171     group = group.arg( g.translatedName() + ":", -MAX_SPACE );
00172     result.prepend( group );
00173 
00174     return result;
00175 }
00176 
00177 
00178 QStringList FileProps::createKeyValueList( const KFileMetaInfoGroup& g,
00179                                            const QStringList& keys )
00180 {
00181     QStringList result;
00182     QStringList::ConstIterator it = keys.begin();
00183 
00184     for ( ; it != keys.end(); ++it )
00185         result.append( FileProps::createKeyValue( g, *it ) );
00186 
00187     return result;
00188 }
00189 
00192 
00193 
00194 
00195 
00196 
00197 static KCmdLineOptions options[] =
00198 {
00199     { "m", 0, 0 }, 
00200     { "nomimetype", I18N_NOOP("Don't print the mimetype of the given file(s)"), 0 },
00201 
00202     { "ls", 0, 0 }, 
00203     { "listsupported <mimetype>",
00204       I18N_NOOP("List all supported metadata keys of the given file(s). "
00205                 "If mimetype is not specified, the mimetype of the given "
00206                 "files is used." ), "file" },
00207 
00208     { "lp", 0, 0 }, 
00209     { "listpreferred <mimetype>",
00210       I18N_NOOP("List all preferred metadata keys of the given file(s). "
00211                 "If mimetype is not specified, the mimetype of the given "
00212                 "files is used." ), "file" },
00213 
00214     { "la", 0, 0 }, 
00215     { "listavailable",
00216       I18N_NOOP("List all metadata keys which have a value in the given "
00217                 "file(s)."), 0 },
00218 
00219     { "sm", 0, 0 }, 
00220     { "supportedMimetypes",
00221       I18N_NOOP("Prints all mimetypes for which metadata support is "
00222                 "available."), 0 },
00223 
00224     { "q", 0, 0 }, 
00225     { "quiet",
00226       I18N_NOOP("Don't print a warning when more than one file was given "
00227                 "and they don't have all the same mimetype."), 0 },
00228 
00229     { "av", 0, 0 }, 
00230     { "allValues",
00231       I18N_NOOP("Prints all metadata values, available in the given "
00232                 "file(s)."), 0 },
00233 
00234     { "pv", 0, 0 }, 
00235     { "preferredValues",
00236       I18N_NOOP("Prints the preferred metadata values, available in the "
00237                 "given file(s)."), 0 },
00238 
00239     { "dialog",
00240       I18N_NOOP("Opens a KDE properties dialog to allow viewing and "
00241                 "modifying of metadata of the given file(s)"), 0 },
00242 
00243     { "getValue <key>",
00244       I18N_NOOP("Prints the value for 'key' of the given file(s). 'key' "
00245                 "may also be a comma-separated list of keys"), 0 },
00246 
00247     { "setValue <key=value>",
00248       I18N_NOOP("Attempts to set the value 'value' for the metadata key "
00249                 "'key' for the given file(s)"), 0 },
00250 
00251     { "!groups <arguments>", I18N_NOOP("The group to get values from or set values to."),
00252       0 },
00253 
00254     { "+[files]",
00255       I18N_NOOP("The file (or a number of files) to operate on."), 0 },
00256     KCmdLineLastOption
00257 };
00258 
00259 
00260 
00261 
00262 
00263 
00264 static void printSupportedMimeTypes()
00265 {
00266     QStringList allMimeTypes = KFileMetaInfoProvider::self()->supportedMimeTypes();
00267     if ( allMimeTypes.isEmpty() )
00268     {
00269         cout <<
00270             i18n("No support for metadata extraction found.").local8Bit()
00271              << endl;
00272         return;
00273     }
00274 
00275     cout << i18n("Supported MimeTypes:").local8Bit() << endl;
00276 
00277     QStringList::ConstIterator it = allMimeTypes.begin();
00278     for ( ; it != allMimeTypes.end(); it++ )
00279         cout << (*it).local8Bit() << endl;
00280 }
00281 
00282 
00283 static KFileItemList * fileItemList( const KCmdLineArgs *args )
00284 {
00285     KFileItemList * items = new KFileItemList();
00286     items->setAutoDelete( true );
00287     for ( int i = 0; i < args->count(); i++ )
00288         items->append( new KFileItem( KFileItem::Unknown,
00289                                      KFileItem::Unknown,
00290                                      args->url( i ) ));
00291     return items;
00292 }
00293 
00294 static void showPropertiesDialog( const KCmdLineArgs *args )
00295 {
00296     KFileItemList *items = fileItemList( args );
00297     new KPropertiesDialog( *items, 0L, "props dialog", true );
00298     delete items;
00299 }
00300 
00301 static void printMimeTypes( const KCmdLineArgs *args )
00302 {
00303     for ( int i = 0; i < args->count(); i++ )
00304     {
00305         KURL url = args->url( i );
00306         KMimeType::Ptr mt = KMimeType::findByURL( url );
00307         cout << args->arg(i) << ": " << mt->comment().local8Bit() << " ("
00308              << mt->name().local8Bit() << ")" << endl;
00309     }
00310 }
00311 
00312 static void printList( const QStringList& list )
00313 {
00314     QStringList::ConstIterator it = list.begin();
00315     for ( ; it != list.end(); ++it )
00316         cout << (*it).local8Bit() << endl;
00317     cout << endl;
00318 }
00319 
00320 static void processMetaDataOptions( const QPtrList<FileProps> propList,
00321                                     KCmdLineArgs *args )
00322 {
00323 
00324 
00325 
00326     QString line("-- -------------------------------------------------------");
00327     FileProps *props;
00328     QPtrListIterator<FileProps> it( propList );
00329     for ( ; (props = it.current()); ++it )
00330     {
00331         QString file = props->fileName() + " ";
00332         QString fileString = line.replace( 3, file.length(), file );
00333         cout << QFile::encodeName( fileString ) << endl;
00334             
00335         if ( args->isSet( "listsupported" ) )
00336         {
00337             cout << "=Supported Keys=" << endl;
00338             printList( props->supportedKeys() );
00339         }
00340         if ( args->isSet( "listpreferred" ) )
00341         {
00342             cout << "=Preferred Keys=" << endl;
00343             printList( props->preferredKeys() );
00344         }
00345         if ( args->isSet( "listavailable" ) )
00346         {
00347             cout << "=Available Keys=" << endl;
00348             QStringList groups = props->availableGroups();
00349             QStringList::ConstIterator git = groups.begin();
00350             for ( ; git != groups.end(); ++git )
00351             {
00352                 cout << "Group: " << (*git).local8Bit() << endl;
00353                 printList( props->availableKeys( *git ) );
00354             }
00355         }
00356 
00357 
00358 
00359 
00360         if ( args->isSet( "getValue" ) )
00361         {
00362             cout << "=Value=" << endl;
00363             QString key = QString::fromLocal8Bit( args->getOption("getValue"));
00364             QStringList::ConstIterator git = props->groupsToUse().begin();
00365             for ( ; git != props->groupsToUse().end(); ++git )
00366                 cout << props->getValue( *git, key ).local8Bit() << endl;
00367         }
00368 
00369         if ( args->isSet( "setValue" ) )
00370         {
00371             
00372             QString cmd = QString::fromLocal8Bit( args->getOption("setValue"));
00373             QString key = cmd.section( '=', 0, 0 );
00374             QString value = cmd.section( '=', 1 );
00375 
00376             
00377             
00378             QStringList groups = props->userSuppliedGroups() ?
00379                                  props->groupsToUse() :
00380                                  props->supportedGroups();
00381 
00382             QStringList::ConstIterator git = groups.begin();
00383             for ( ; git != groups.end(); ++git )
00384                 props->setValue( *git, key, value );
00385         }
00386 
00387         if ( args->isSet( "allValues" ) )
00388         {
00389             cout << "=All Values=" << endl;
00390             QStringList groups = props->availableGroups();
00391             QStringList::ConstIterator group = groups.begin();
00392             for ( ; group != groups.end(); ++group )
00393                 printList( props->allValues( *group ) );
00394         }
00395         if ( args->isSet( "preferredValues" ) && !args->isSet("allValues") )
00396         {
00397             cout << "=Preferred Values=" << endl;
00398             QStringList groups = props->availableGroups();
00399             QStringList::ConstIterator group = groups.begin();
00400             for ( ; group != groups.end(); ++group )
00401                 printList( props->preferredValues( *group ) );
00402         }
00403     }
00404 
00405 }
00406 
00407 int main( int argc, char **argv )
00408 {
00409     KAboutData about(
00410       "kfile", I18N_NOOP( "kfile" ), KFILEVERSION,
00411       I18N_NOOP("A commandline tool to read and modify metadata of files." ),
00412       KAboutData::License_LGPL, "(c) 2002, Carsten Pfeiffer",
00413       0 , "http://devel-home.kde.org/~pfeiffer/",
00414       "pfeiffer@kde.org" );
00415 
00416     about.addAuthor( "Carsten Pfeiffer", 0, "pfeiffer@kde.org",
00417              "http://devel-home.kde.org/~pfeiffer/" );
00418 
00419     KCmdLineArgs::init( argc, argv, &about );
00420 
00421     KCmdLineArgs::addCmdLineOptions( options );
00422 
00423     KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
00424     bool useGUI = args->isSet( "dialog" );
00425 
00426     KApplication app( useGUI, useGUI );
00427 
00428     QPtrList<FileProps> m_props;
00429     m_props.setAutoDelete( true );
00430 
00431     bool quiet = args->isSet( "quiet" );
00432 
00433     if ( args->isSet( "supportedMimetypes" ) )
00434         printSupportedMimeTypes();
00435 
00436     int files = args->count();
00437     if ( files == 0 )
00438         KCmdLineArgs::usage( i18n("No files specified") ); 
00439 
00440     if ( args->isSet( "dialog" ) )
00441     {
00442         showPropertiesDialog( args );
00443         return true;
00444     }
00445 
00446     QStringList groupsToUse;
00447     QCStringList suppliedGroups = args->getOptionList( "groups" );
00448     QCStringList::ConstIterator it = suppliedGroups.begin();
00449     for ( ; it != suppliedGroups.end(); ++it )
00450         groupsToUse.append( QString::fromLocal8Bit( (*it) ) );
00451 
00452     QString mimeType;
00453 
00454     for ( int i = 0; i < files; i++ )
00455     {
00456         if ( args->isSet( "mimetype" ) )
00457             printMimeTypes( args );
00458 
00459         FileProps *props = new FileProps( args->url(i).path(), groupsToUse );
00460         if ( props->isValid() )
00461             m_props.append( props );
00462         else
00463         {
00464             if ( !quiet )
00465             {
00466                 cerr << args->arg(i) << ": " <<
00467                 i18n("Cannot determine metadata").local8Bit() << endl;
00468             }
00469             delete props;
00470         }
00471     }
00472 
00473 
00474     processMetaDataOptions( m_props, args );
00475 
00476     m_props.clear(); 
00477     cout.flush();
00478 
00479     return 0;
00480 }