[SCM] exiv2 packaging branch, master, updated. debian/0.25-3.1-3734-gdcbc29a
Maximiliano Curia
maxy at moszumanska.debian.org
Thu Jul 13 17:36:53 UTC 2017
Gitweb-URL: http://git.debian.org/?p=pkg-kde/kde-extras/exiv2.git;a=commitdiff;h=3a6b642
The following commit has been merged in the master branch:
commit 3a6b642c6aae27037af88b315d01e847eb45cdcd
Author: Andreas Huggel <ahuggel at gmx.net>
Date: Tue Jan 4 16:28:51 2005 +0000
Added modify action to exiv2 tool. Implements feature #406
---
src/actions.cpp | 125 ++++++++++++++++++++++++++++++++
src/actions.hpp | 31 +++++++-
src/cmd.txt | 30 ++++++++
src/exiv2.cpp | 220 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
src/exiv2.hpp | 32 ++++++++-
src/types.cpp | 15 +++-
src/types.hpp | 3 +
7 files changed, 450 insertions(+), 6 deletions(-)
diff --git a/src/actions.cpp b/src/actions.cpp
index 758dc72..e2cd7af 100644
--- a/src/actions.cpp
+++ b/src/actions.cpp
@@ -59,6 +59,7 @@ EXIV2_RCSID("@(#) $Id$");
#include <cstdio>
#include <ctime>
#include <cmath>
+#include <cassert>
#include <sys/types.h> // for stat()
#include <sys/stat.h> // for stat()
#ifdef HAVE_UNISTD_H
@@ -131,6 +132,7 @@ namespace Action {
registerTask(erase, Task::AutoPtr(new Erase));
registerTask(extract, Task::AutoPtr(new Extract));
registerTask(insert, Task::AutoPtr(new Insert));
+ registerTask(modify, Task::AutoPtr(new Modify));
} // TaskFactory c'tor
Task::AutoPtr TaskFactory::create(TaskType type)
@@ -917,6 +919,129 @@ namespace Action {
return new Insert(*this);
}
+ int Modify::run(const std::string& path)
+ try {
+ if (!Util::fileExists(path, true)) {
+ std::cerr << path
+ << ": Failed to open the file
";
+ return -1;
+ }
+
+ // Read both exif and iptc metadata (ignore return code)
+ exifData_.read(path);
+ iptcData_.read(path);
+
+ // loop through command table and apply each command
+ ModifyCmds& modifyCmds = Params::instance().modifyCmds_;
+ ModifyCmds::const_iterator i = modifyCmds.begin();
+ ModifyCmds::const_iterator end = modifyCmds.end();
+ for (; i != end; ++i) {
+ switch (i->cmdId_) {
+ case add:
+ addMetadatum(*i);
+ break;
+ case set:
+ setMetadatum(*i);
+ break;
+ case del:
+ delMetadatum(*i);
+ break;
+ default:
+ // Todo: complain
+ break;
+ }
+ }
+
+ // Save both exif and iptc metadata
+ int rc = exifData_.write(path);
+ if (rc) {
+ std::cerr << Exiv2::ExifData::strError(rc, path) << "
";
+ }
+ rc = iptcData_.write(path);
+ if (rc) {
+ std::cerr << Exiv2::IptcData::strError(rc, path) << "
";
+ }
+ return rc;
+ }
+ catch(const Exiv2::Error& e)
+ {
+ std::cerr << "Exif exception in modify action for file " << path
+ << ":
" << e << "
";
+ return 1;
+ } // Modify::run
+
+ void Modify::addMetadatum(const ModifyCmd& modifyCmd)
+ {
+ if (Params::instance().verbose_) {
+ std::cout << "Add " << modifyCmd.key_ << " \""
+ << modifyCmd.value_ << "\" ("
+ << Exiv2::TypeInfo::typeName(modifyCmd.typeId_)
+ << ")" << std::endl;
+ }
+ Exiv2::Value::AutoPtr value = Exiv2::Value::create(modifyCmd.typeId_);
+ value->read(modifyCmd.value_);
+ if (modifyCmd.metadataId_ == exif) {
+ exifData_.add(Exiv2::ExifKey(modifyCmd.key_), value.get());
+ }
+ if (modifyCmd.metadataId_ == iptc) {
+ iptcData_.add(Exiv2::IptcKey(modifyCmd.key_), value.get());
+ }
+ }
+
+ void Modify::setMetadatum(const ModifyCmd& modifyCmd)
+ {
+ if (Params::instance().verbose_) {
+ std::cout << "Set " << modifyCmd.key_ << " \""
+ << modifyCmd.value_ << "\" ("
+ << Exiv2::TypeInfo::typeName(modifyCmd.typeId_)
+ << ")" << std::endl;
+ }
+ Exiv2::Metadatum* metadatum = 0;
+ if (modifyCmd.metadataId_ == exif) {
+ metadatum = &exifData_[modifyCmd.key_];
+ }
+ if (modifyCmd.metadataId_ == iptc) {
+ metadatum = &iptcData_[modifyCmd.key_];
+ }
+ assert(metadatum);
+ Exiv2::Value::AutoPtr value = metadatum->getValue();
+ // If a type was explicitly requested, use it; else
+ // use the current type of the metadatum, if any;
+ // or the default type
+ if (modifyCmd.explicitType_ || value.get() == 0) {
+ value = Exiv2::Value::create(modifyCmd.typeId_);
+ }
+ value->read(modifyCmd.value_);
+ metadatum->setValue(value.get());
+ }
+
+ void Modify::delMetadatum(const ModifyCmd& modifyCmd)
+ {
+ if (Params::instance().verbose_) {
+ std::cout << "Del " << modifyCmd.key_ << std::endl;
+ }
+ if (modifyCmd.metadataId_ == exif) {
+ Exiv2::ExifData::iterator pos =
+ exifData_.findKey(Exiv2::ExifKey(modifyCmd.key_));
+ if (pos != exifData_.end()) exifData_.erase(pos);
+ }
+ if (modifyCmd.metadataId_ == iptc) {
+ Exiv2::IptcData::iterator pos =
+ iptcData_.findKey(Exiv2::IptcKey(modifyCmd.key_));
+ if (pos != iptcData_.end()) iptcData_.erase(pos);
+ }
+ }
+
+ Modify::AutoPtr Modify::clone() const
+ {
+ return AutoPtr(clone_());
+ }
+
+ Modify* Modify::clone_() const
+ {
+ return new Modify(*this);
+ }
+
int Adjust::run(const std::string& path)
try {
adjustment_ = Params::instance().adjustment_;
diff --git a/src/actions.hpp b/src/actions.hpp
index 5a83faf..ad4e279 100644
--- a/src/actions.hpp
+++ b/src/actions.hpp
@@ -37,6 +37,10 @@
#include <string>
#include <map>
+#include "exiv2.hpp"
+#include "exif.hpp"
+#include "iptc.hpp"
+
// *****************************************************************************
// class declarations
@@ -53,7 +57,7 @@ namespace Exiv2 {
namespace Action {
//! Enumerates all tasks
- enum TaskType { none, adjust, print, rename, erase, extract, insert };
+ enum TaskType { none, adjust, print, rename, erase, extract, insert, modify };
// *****************************************************************************
// class definitions
@@ -289,6 +293,31 @@ namespace Action {
}; // class Insert
+ /*!
+ @brief %Modify the Exif data according to the commands in the
+ modification table.
+ */
+ class Modify : public Task {
+ public:
+ virtual ~Modify() {}
+ virtual int run(const std::string& path);
+ typedef std::auto_ptr<Modify> AutoPtr;
+ AutoPtr clone() const;
+
+ private:
+ virtual Modify* clone_() const;
+
+ //! Add a metadatum according to \em modifyCmd
+ void addMetadatum(const ModifyCmd& modifyCmd);
+ //! Set a metadatum according to \em modifyCmd
+ void setMetadatum(const ModifyCmd& modifyCmd);
+ //! Delete a metadatum according to \em modifyCmd
+ void delMetadatum(const ModifyCmd& modifyCmd);
+
+ Exiv2::ExifData exifData_; //!< Exif metadata
+ Exiv2::IptcData iptcData_; //!< Iptc metadata
+ }; // class Modify
+
} // namespace Action
#endif // #ifndef ACTIONS_HPP_
diff --git a/src/cmd.txt b/src/cmd.txt
new file mode 100644
index 0000000..c964d5b
--- /dev/null
+++ b/src/cmd.txt
@@ -0,0 +1,30 @@
+# Sample Exiv2 command file
+# Run exiv2 -m cmd.txt file ...
+# to apply the commands to each file.
+#
+# Command file format
+# Empty lines and lines starting with # are ignored
+# Each remaining line is a command. The format for command lines is
+# <cmd> <key> [[<type>] <value>]
+# cmd = set|add|del
+# set will set the value of an existing tag of the given key or add a tag
+# add will add a tag (unless the key is a non-repeatable Iptc key)
+# del will delete a tag
+# key = Exiv2 Exif or Iptc key
+# type = Byte|Ascii|Short|Long|Rational|Undefined|SShort|SLong|SRational for Exif
+# String|Date|Time|Short|Undefined for Iptc
+# A default type is used if none is explicitely given. The default for Exif
+# keys is Ascii, that for Iptc keys is determined based on the key itself.
+# value
+# The remaining text on the line is the value. It can optionally be enclosed in
+# double quotes ("value")
+
+add Iptc.Application2.Credit String "mee too! (1)"
+add Iptc.Application2.Credit mee too! (2)
+del Iptc.Application2.Headline
+
+add Exif.Image.WhitePoint Short 32 12 4 5 6
+
+ set Exif.Image.DateTime Ascii "Zwanzig nach fuenf"
+ set Exif.Image.Artist Ascii nobody
+ set Exif.Image.Artist "Vincent van Gogh"
diff --git a/src/exiv2.cpp b/src/exiv2.cpp
index 9313c53..c140db5 100644
--- a/src/exiv2.cpp
+++ b/src/exiv2.cpp
@@ -46,6 +46,7 @@ EXIV2_RCSID("@(#) $Id$");
#include <string>
#include <iostream>
+#include <fstream>
#include <iomanip>
#include <cstring>
#include <cassert>
@@ -54,6 +55,17 @@ EXIV2_RCSID("@(#) $Id$");
// local declarations
namespace {
+ //! List of all command itentifiers and corresponding strings
+ static const CmdIdAndString cmdIdAndString[] = {
+ add, "add",
+ set, "set",
+ del, "del",
+ invalidCmdId, "invalidCmd" // End of list marker
+ };
+
+ // Return a command Id for a command string
+ CmdId commandId(const std::string& cmdString);
+
// Evaluate [-]HH[:MM[:SS]], returns true and sets time to the value
// in seconds if successful, else returns false.
bool parseTime(const std::string& ts, long& time);
@@ -66,6 +78,25 @@ namespace {
*/
int parseCommonTargets(const std::string& optarg,
const std::string& action);
+
+ /*!
+ @brief Parse metadata modification commands from a file
+ @param modifyCmds Reference to a structure to store the parsed commands
+ @param filename Name of the command file
+ */
+ bool parseCommands(ModifyCmds& modifyCmds,
+ const std::string& filename);
+
+ /*!
+ @brief Parse one line of the command file
+ @param modifyCmd Reference to a command structure to store the parsed
+ command
+ @param line Input line
+ @param num Line number (used for error output)
+ */
+ bool parseLine(ModifyCmd& modifyCmd,
+ const std::string& line, int num);
+
}
// *****************************************************************************
@@ -123,7 +154,7 @@ Params& Params::instance()
void Params::version(std::ostream& os) const
{
os << PACKAGE_STRING << ", "
- << "Copyright (C) 2004 Andreas Huggel.
"
+ << "Copyright (C) 2004, 2005 Andreas Huggel.
"
<< "This is free software; see the source for copying conditions. "
<< "There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR "
<< "A PARTICULAR PURPOSE.
";
@@ -148,6 +179,8 @@ void Params::help(std::ostream& os) const
<< " ex | extract Extract metadata to *.exv and thumbnail image files.
"
<< " mv | rename Rename files according to the Exif create timestamp.
"
<< " The filename format can be set with -r format.
"
+ << " mo | modify Apply commands to modify (add, set, delete) the Exif
"
+ << " and Iptc metadata of image files. Requires option -m
"
<< "
Options:
"
<< " -h Display this help and exit.
"
<< " -V Show the program version and exit.
"
@@ -175,7 +208,9 @@ void Params::help(std::ostream& os) const
<< " are the same as those for the -d option.
"
<< " -r fmt Filename format for the `rename' action. The format string
"
<< " follows strftime(3). Default filename format is "
- << format_ << ".
";
+ << format_ << ".
"
+ << " -m file Command file for the modify action. The format for the commands
"
+ << " set|add|del <key> [[<Type>] <value>].
";
} // Params::help
int Params::option(int opt, const std::string& optarg, int optopt)
@@ -323,6 +358,23 @@ int Params::option(int opt, const std::string& optarg, int optopt)
break;
}
break;
+ case 'm':
+ switch (action_) {
+ case Action::none:
+ action_ = Action::modify;
+ cmdFile_ = optarg; // parse the file later
+ break;
+ case Action::modify:
+ std::cerr << progname()
+ << ": Ignoring surplus option -m " << optarg << "
";
+ break;
+ default:
+ std::cerr << progname()
+ << ": Option -m is not compatible with a previous option
";
+ rc = 1;
+ break;
+ }
+ break;
case ':':
std::cerr << progname() << ": Option -" << static_cast<char>(optopt)
<< " requires an argument
";
@@ -404,6 +456,15 @@ int Params::nonoption(const std::string& argv)
action = true;
action_ = Action::rename;
}
+ if (argv == "mo" || argv == "modify") {
+ if (action_ != Action::none && action_ != Action::modify) {
+ std::cerr << progname() << ": Action modify is not "
+ << "compatible with the given options
";
+ rc = 1;
+ }
+ action = true;
+ action_ = Action::modify;
+ }
if (action_ == Action::none) {
// if everything else fails, assume print as the default action
action_ = Action::print;
@@ -430,10 +491,22 @@ int Params::getopt(int argc, char* const argv[])
<< ": Adjust action requires option -a time
";
rc = 1;
}
+ if (action_ == Action::modify && cmdFile_.empty()) {
+ std::cerr << progname()
+ << ": Modify action requires option -m file
";
+ rc = 1;
+ }
if (0 == files_.size()) {
std::cerr << progname() << ": At least one file is required
";
rc = 1;
}
+ if (rc == 0 && action_ == Action::modify) {
+ if (!parseCommands(modifyCmds_, cmdFile_)) {
+ std::cerr << progname() << ": Error parsing -m option argument `"
+ << cmdFile_ << "'
";
+ rc = 1;
+ }
+ }
return rc;
} // Params::getopt
@@ -505,4 +578,147 @@ namespace {
return rc ? rc : target;
} // parseCommonTargets
+ bool parseCommands(ModifyCmds& modifyCmds,
+ const std::string& filename)
+ {
+ try {
+ std::ifstream file(filename.c_str());
+ if (!file) {
+ std::cerr << filename
+ << ": Failed to open command file for reading
";
+ return false;
+ }
+ int num = 0;
+ std::string line;
+ while (std::getline(file, line)) {
+ ModifyCmd modifyCmd;
+ if (parseLine(modifyCmd, line, ++num)) {
+ modifyCmds.push_back(modifyCmd);
+ }
+ }
+ return true;
+ }
+ catch (const Exiv2::Error& error) {
+ std::cerr << filename << ", " << error << "
";
+ return false;
+ }
+ } // parseCommands
+
+ bool parseLine(ModifyCmd& modifyCmd, const std::string& line, int num)
+ {
+ const std::string delim = " ";
+
+ // Skip empty lines and comments
+ std::string::size_type cmdStart = line.find_first_not_of(delim);
+ if (cmdStart == std::string::npos || line[cmdStart] == '#') return false;
+
+ // Get command and key
+ std::string::size_type cmdEnd = line.find_first_of(delim, cmdStart+1);
+ std::string::size_type keyStart = line.find_first_not_of(delim, cmdEnd+1);
+ std::string::size_type keyEnd = line.find_first_of(delim, keyStart+1);
+ if ( cmdStart == std::string::npos
+ || cmdEnd == std::string::npos
+ || keyStart == std::string::npos) {
+ throw Exiv2::Error("line " + Exiv2::toString(num)
+ + ": Invalid command line");
+ }
+
+ std::string cmd(line.substr(cmdStart, cmdEnd-cmdStart));
+ CmdId cmdId = commandId(cmd);
+ if (cmdId == invalidCmdId) {
+ throw Exiv2::Error("line " + Exiv2::toString(num)
+ + ": Invalid command `" + cmd + "'");
+ }
+
+ Exiv2::TypeId defaultType = Exiv2::invalidTypeId;
+ std::string key(line.substr(keyStart, keyEnd-keyStart));
+ MetadataId metadataId = invalidMetadataId;
+ try {
+ Exiv2::IptcKey iptcKey(key);
+ metadataId = iptc;
+ defaultType = Exiv2::IptcDataSets::dataSetType(iptcKey.tag(),
+ iptcKey.record());
+ }
+ catch (const Exiv2::Error&) {}
+ if (metadataId == invalidMetadataId) {
+ try {
+ Exiv2::ExifKey exifKey(key);
+ metadataId = exif;
+ defaultType = Exiv2::asciiString;
+ }
+ catch (const Exiv2::Error&) {}
+ }
+ if (metadataId == invalidMetadataId) {
+ throw Exiv2::Error("line " + Exiv2::toString(num)
+ + ": Invalid key `" + key + "'");
+ }
+
+ std::string value;
+ Exiv2::TypeId type = Exiv2::invalidTypeId;
+ bool explicitType = true;
+ if (cmdId != del) {
+ // Get type and value
+ std::string::size_type typeStart
+ = line.find_first_not_of(delim, keyEnd+1);
+ std::string::size_type typeEnd
+ = line.find_first_of(delim, typeStart+1);
+ std::string::size_type valStart = typeStart;
+ std::string::size_type valEnd = line.find_last_not_of(delim);
+
+ if ( keyEnd == std::string::npos
+ || typeStart == std::string::npos
+ || typeEnd == std::string::npos
+ || valStart == std::string::npos) {
+ throw Exiv2::Error("line " + Exiv2::toString(num)
+ + ": Invalid command line");
+ }
+
+ std::string typeStr(line.substr(typeStart, typeEnd-typeStart));
+ type = Exiv2::TypeInfo::typeId(typeStr);
+ if (type != Exiv2::invalidTypeId) {
+ valStart = line.find_first_not_of(delim, typeEnd+1);
+ if (valStart == std::string::npos) {
+ throw Exiv2::Error("line " + Exiv2::toString(num)
+ + ": Invalid command line");
+ }
+ }
+ else {
+ type = defaultType;
+ explicitType = false;
+ }
+ if (type == Exiv2::invalidTypeId) {
+ throw Exiv2::Error("line " + Exiv2::toString(num)
+ + ": Invalid type");
+ }
+
+ value = line.substr(valStart, valEnd+1-valStart);
+ std::string::size_type last = value.length()-1;
+ if ( (value[0] == '"' || value[last] == '"')
+ && value[0] != value[last]) {
+ throw Exiv2::Error("line " + Exiv2::toString(num)
+ + ": Unbalanced quotes");
+ }
+ if (value[0] == '"') {
+ value = value.substr(1, value.length()-2);
+ }
+ }
+
+ modifyCmd.cmdId_ = cmdId;
+ modifyCmd.key_ = key;
+ modifyCmd.metadataId_ = metadataId;
+ modifyCmd.typeId_ = type;
+ modifyCmd.explicitType_ = explicitType;
+ modifyCmd.value_ = value;
+
+ return true;
+ } // parseLine
+
+ CmdId commandId(const std::string& cmdString)
+ {
+ int i = 0;
+ for (; cmdIdAndString[i].cmdId_ != invalidCmdId
+ && cmdIdAndString[i].cmdString_ != cmdString; ++i) {}
+ return cmdIdAndString[i].cmdId_;
+ }
+
}
diff --git a/src/exiv2.hpp b/src/exiv2.hpp
index 539c27b..7c323ec 100644
--- a/src/exiv2.hpp
+++ b/src/exiv2.hpp
@@ -32,6 +32,7 @@
// *****************************************************************************
// included header files
#include "utils.hpp"
+#include "types.hpp"
// + standard includes
#include <string>
@@ -40,6 +41,33 @@
// *****************************************************************************
// class definitions
+
+//! Command identifiers
+enum CmdId { invalidCmdId, add, set, del };
+//! Metadata identifiers
+enum MetadataId { invalidMetadataId, iptc, exif };
+//! Structure for one parsed modification command
+struct ModifyCmd {
+ //! C'tor
+ ModifyCmd() :
+ cmdId_(invalidCmdId), metadataId_(invalidMetadataId),
+ typeId_(Exiv2::invalidTypeId), explicitType_(false) {}
+ CmdId cmdId_; //!< Command identifier
+ std::string key_; //!< Exiv2 key string
+ MetadataId metadataId_; //!< Metadata identifier
+ Exiv2::TypeId typeId_; //!< Exiv2 type identifier
+ //! Flag to indicate if the type was explicitely specified (true)
+ bool explicitType_;
+ std::string value_; //!< Data
+};
+//! Container for modification commands
+typedef std::vector<ModifyCmd> ModifyCmds;
+//! Structure to link command identifiers to strings
+struct CmdIdAndString {
+ CmdId cmdId_; //!< Commands identifier
+ std::string cmdString_; //!< Command string
+};
+
/*!
@brief Implements the command line handling for the program.
@@ -103,6 +131,8 @@ public:
long adjustment_; //!< Adjustment in seconds.
std::string format_; //!< Filename format (-r option arg).
+ std::string cmdFile_; //!< Name of the modification command file
+ ModifyCmds modifyCmds_; //!< Parsed modification commands
//! Container to store filenames.
typedef std::vector<std::string> Files;
@@ -114,7 +144,7 @@ private:
@brief Default constructor. Note that optstring_ is initialized here.
The c'tor is private to force instantiation through instance().
*/
- Params() : optstring_(":hVvfa:r:p:d:e:i:"),
+ Params() : optstring_(":hVvfa:r:p:d:e:i:m:"),
help_(false),
version_(false),
verbose_(false),
diff --git a/src/types.cpp b/src/types.cpp
index 253158a..503c40f 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -58,14 +58,16 @@ namespace Exiv2 {
TypeInfoTable(unsignedShort, "Short", 2),
TypeInfoTable(unsignedLong, "Long", 4),
TypeInfoTable(unsignedRational, "Rational", 8),
- TypeInfoTable(invalid6, "Invalid (6)", 1),
+ TypeInfoTable(invalid6, "Invalid(6)", 1),
TypeInfoTable(undefined, "Undefined", 1),
TypeInfoTable(signedShort, "SShort", 2),
TypeInfoTable(signedLong, "SLong", 4),
TypeInfoTable(signedRational, "SRational", 8),
TypeInfoTable(string, "String", 1),
TypeInfoTable(date, "Date", 8),
- TypeInfoTable(time, "Time", 11)
+ TypeInfoTable(time, "Time", 11),
+ // End of list marker
+ TypeInfoTable(lastTypeId, "(Unknown)", 0)
};
const char* TypeInfo::typeName(TypeId typeId)
@@ -73,6 +75,15 @@ namespace Exiv2 {
return typeInfoTable_[ typeId < lastTypeId ? typeId : 0 ].name_;
}
+ TypeId TypeInfo::typeId(const std::string& typeName)
+ {
+ int i = 0;
+ for (; typeInfoTable_[i].typeId_ != lastTypeId
+ && typeInfoTable_[i].name_ != typeName; ++i) {}
+ return typeInfoTable_[i].typeId_ == lastTypeId ?
+ invalidTypeId : typeInfoTable_[i].typeId_;
+ }
+
long TypeInfo::typeSize(TypeId typeId)
{
return typeInfoTable_[ typeId < lastTypeId ? typeId : 0 ].size_;
diff --git a/src/types.hpp b/src/types.hpp
index 6d5fe5f..13b086c 100644
--- a/src/types.hpp
+++ b/src/types.hpp
@@ -46,6 +46,7 @@
#include <iosfwd>
#include <utility>
#include <sstream>
+#include <cstdio>
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
@@ -113,6 +114,8 @@ namespace Exiv2 {
public:
//! Return the name of the type
static const char* typeName(TypeId typeId);
+ //! Return the type id for a type name
+ static TypeId typeId(const std::string& typeName);
//! Return the size in bytes of one element of this type
static long typeSize(TypeId typeId);
--
exiv2 packaging
More information about the pkg-kde-commits
mailing list