Adapted classes from liby2util, some of them to be later replaced by
authorJiri Srain <jsrain@suse.cz>
Thu, 8 Dec 2005 14:38:59 +0000 (14:38 +0000)
committerJiri Srain <jsrain@suse.cz>
Thu, 8 Dec 2005 14:38:59 +0000 (14:38 +0000)
Blocxx

13 files changed:
zypp/Digest.cc [new file with mode: 0644]
zypp/Digest.h [new file with mode: 0644]
zypp/ExternalProgram.cc [new file with mode: 0644]
zypp/ExternalProgram.h [new file with mode: 0644]
zypp/Makefile.am
zypp/PathInfo.cc [new file with mode: 0644]
zypp/PathInfo.h [new file with mode: 0644]
zypp/Pathname.cc [new file with mode: 0644]
zypp/Pathname.h [new file with mode: 0644]
zypp/base/Makefile.am
zypp/base/stringutil.cc [new file with mode: 0644]
zypp/base/stringutil.h [new file with mode: 0644]
zypp/media/Makefile.am [new file with mode: 0644]

diff --git a/zypp/Digest.cc b/zypp/Digest.cc
new file mode 100644 (file)
index 0000000..ac9d848
--- /dev/null
@@ -0,0 +1,237 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Digest.cc
+ *
+ * \todo replace by Blocxx
+ *
+*/
+
+#include <cstdio> // snprintf
+#include <openssl/evp.h>
+#include <string>
+
+#include <iostream>
+
+#ifdef DIGEST_TESTSUITE
+#include <fstream>
+#endif
+
+#include "zypp/Digest.h"
+
+namespace zypp {
+
+    // private data
+    class Digest::P
+    {
+       P(const P& p);
+       const P& operator=(const P& p);
+      public:
+       P();
+       ~P();
+    
+       EVP_MD_CTX mdctx;
+    
+       const EVP_MD *md;
+       unsigned char md_value[EVP_MAX_MD_SIZE];
+       unsigned md_len;
+    
+       bool initialized : 1;
+       bool finalized : 1;
+       static bool openssl_digests_added;
+    
+       std::string name;
+    
+       inline bool maybeInit();
+       inline void cleanup();
+    };
+    
+    
+    using namespace std;
+    
+    bool Digest::P::openssl_digests_added = false;
+    
+    Digest::P::P() :
+      md(NULL),
+      initialized(false),
+      finalized(false)
+    {
+    }
+    
+    Digest::P::~P()
+    {
+      cleanup();
+    }
+    
+    bool Digest::P::maybeInit()
+    {
+      if(!openssl_digests_added)
+      {
+       OpenSSL_add_all_digests();
+       openssl_digests_added = true;
+      }
+    
+      if(!initialized)
+      {
+       md = EVP_get_digestbyname(name.c_str());
+       if(!md)
+           return false;
+    
+       EVP_MD_CTX_init(&mdctx);
+    
+       if(!EVP_DigestInit_ex(&mdctx, md, NULL))
+           return false;
+    
+       md_len = 0;
+       ::memset(md_value, 0, sizeof(md_value));
+       initialized = true;
+      }
+    
+      return true;
+    }
+    
+    void Digest::P::cleanup()
+    {
+      if(initialized)
+      {
+       EVP_MD_CTX_cleanup(&mdctx);
+       initialized = false;
+      }
+    }
+    
+    Digest::Digest() : _dp(new P())
+    {
+    }
+    
+    Digest::~Digest()
+    {
+      delete _dp;
+    }
+    
+    bool Digest::create(const std::string& name)
+    {
+      if(name.empty()) return false;
+    
+      if(_dp->initialized)
+       _dp->cleanup();
+    
+      _dp->name = name;
+    
+      return _dp->maybeInit();
+    }
+    
+    const std::string& Digest::name()
+    {
+      return _dp->name;
+    }
+    
+    std::string Digest::digest()
+    {
+      if(!_dp->maybeInit())
+       return false;
+    
+      if(!_dp->finalized)
+      {
+       if(!EVP_DigestFinal_ex(&_dp->mdctx, _dp->md_value, &_dp->md_len))
+           return false;
+    
+       _dp->finalized = true;
+      }
+    
+      char mdtxt[_dp->md_len*2 + 1];
+      mdtxt[_dp->md_len*2] = '\0';
+    
+      for(unsigned i = 0; i < _dp->md_len; ++i)
+      {
+       ::snprintf(mdtxt + i*2, 3, "%02hhx", _dp->md_value[i]);
+      }
+    
+      return std::string(mdtxt);
+    }
+    
+    bool Digest::update(const char* bytes, size_t len)
+    {
+      if(!bytes)
+      {
+       return false;
+      }
+    
+      if(!_dp->maybeInit())
+       return false;
+    
+      if(_dp->finalized)
+      {
+       _dp->cleanup();
+       if(!_dp->maybeInit())
+           return false;
+    
+      }
+      if(!EVP_DigestUpdate(&_dp->mdctx, reinterpret_cast<const unsigned char*>(bytes), len))
+       return false;
+    
+      return true;
+    }
+    
+    std::string Digest::digest(const std::string& name, std::istream& is, size_t bufsize)
+    {
+      if(name.empty() || !is)
+       return string();
+    
+      char buf[bufsize];
+      size_t num;
+    
+      Digest digest;
+      if(!digest.create(name))
+       return string();
+    
+      while(is.good())
+      {
+       for(num = 0; num < bufsize && is.get(buf[num]).good(); ++num);
+    
+       if(num && !digest.update(buf, num))
+           return string();
+      }
+    
+      return digest.digest();
+    }
+    
+#ifdef DIGEST_TESTSUITE
+    int main(int argc, char *argv[])
+    {
+      bool openssl = false;
+      unsigned argpos = 1;
+    
+      if(argc > 1 && string(argv[argpos]) == "--openssl")
+      {
+       openssl = true;
+       ++argpos;
+      }
+    
+      if(argc - argpos < 2)
+      {
+       cerr << "Usage: " << argv[0] << " <DIGESTNAME> <FILE>" << endl;
+       return 1;
+      }
+    
+      const char* digestname = argv[argpos++];
+      const char* fn = argv[argpos++];
+    
+      ifstream file(fn);
+    
+      string digest = Digest::digest(digestname, file);
+    
+      if(openssl)
+       cout << digestname << "(" << fn << ")= " << digest << endl;
+      else
+       cout << digest << "  " << fn << endl;
+    
+      return 0;
+    }
+#endif
+    
+} // namespace zypp
diff --git a/zypp/Digest.h b/zypp/Digest.h
new file mode 100644 (file)
index 0000000..784b478
--- /dev/null
@@ -0,0 +1,95 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp//Digest.h
+ *
+ * \todo replace by Blocxx
+ *
+*/
+
+#ifndef ZYPP_MEDIA_DIGEST_H
+#define ZYPP_MEDIA_DIGEST_H
+
+#include <string>
+#include <iosfwd>
+
+namespace zypp {
+
+    /** \brief Compute Message Digests (MD5, SHA1 etc)
+     *
+     * The computation works by initializing the algorithm using create(). This
+     * will construct an internal state. successive calls to update() deliver the
+     * data for which the digest is to be computed. After all data has been
+     * deliverd, a call to digest() finalizes the computation and returns the
+     * result
+     * */
+    class Digest
+    {
+      private:
+       class P;
+       P* _dp;
+    
+       // disabled
+       Digest(const Digest& d);
+       // disabled
+       const Digest& operator=(const Digest& d);
+    
+      public:
+       Digest();
+       ~Digest();
+    
+       /** \brief initialize creation of a new message digest
+        * 
+        * Since openssl is used as backend you may use anything that openssl
+        * supports (see man 1 dgst). Common examples are md5 or sha1. sha1
+        * should be preferred when creating digests to verify the authenticity
+        * of something.
+        *
+        * successive calls to this funcion will destroy the internal state and
+        * reinit from scratch
+        *
+        * @param name name of the message digest algorithm.
+        * @return whether an error occured
+        * */
+       bool create(const std::string& name);
+    
+       /** \brief get the name of the current digest algorithm */
+       const std::string& name();
+    
+       /** \brief feed data into digest computation algorithm
+        * @param bytes
+        * @param len
+        * @return whether an error occured
+        * */
+       bool update(const char* bytes, size_t len);
+    
+       /** \brief get hex string representation of the digest
+        *
+        * this function will finalize the digest computation. calls to update
+        * after this function will start from scratch
+        *
+        * @return hex string representation of the digest
+        * */
+       std::string digest();
+       
+       /** \brief compute digest of a stream. convenience function
+        *
+        * calls create, update and digest in one function. The data for the
+        * computation is read from the stream
+        *
+        * @param name name of the digest algorithm, \see create
+        * @param is an input stream to get the data from
+        * @param bufsize size of the buffer used for update(). Be careful, this is on the stack.
+        * @return the digest or empty on error
+        * */
+       static std::string digest(const std::string& name, std::istream& is, size_t bufsize = 4096);
+    };
+    
+} // namespace zypp
+
+#endif
diff --git a/zypp/ExternalProgram.cc b/zypp/ExternalProgram.cc
new file mode 100644 (file)
index 0000000..5118ecf
--- /dev/null
@@ -0,0 +1,395 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/ExternalProgram.cc
+ *
+ * \todo replace by Blocxx
+ *
+*/
+
+#define _GNU_SOURCE 1 // for ::getline
+
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <pty.h> // openpty
+#include <stdlib.h> // setenv
+
+#include <cstring> // strsignal
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/ExternalProgram.h"
+
+using namespace std;
+
+namespace zypp {
+
+    ExternalProgram::ExternalProgram (string commandline,
+                                 Stderr_Disposition stderr_disp, bool use_pty,
+                                 int stderr_fd, bool default_locale,
+                                 const Pathname& root)
+      : use_pty (use_pty)
+    {
+      const char *argv[4];
+      argv[0] = "/bin/sh";
+      argv[1] = "-c";
+      argv[2] = commandline.c_str();
+      argv[3] = 0;
+    
+      const char* rootdir = NULL;
+      if(!root.empty() && root != "/")
+      {
+       rootdir = root.asString().c_str();
+      }
+      Environment environment;
+      start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
+    }
+    
+    
+    ExternalProgram::ExternalProgram (const char *const *argv,
+                                 Stderr_Disposition stderr_disp, bool use_pty,
+                                 int stderr_fd, bool default_locale,
+                                 const Pathname& root)
+      : use_pty (use_pty)
+    {
+      const char* rootdir = NULL;
+      if(!root.empty() && root != "/")
+      {
+       rootdir = root.asString().c_str();
+      }
+      Environment environment;
+      start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
+    }
+    
+    
+    ExternalProgram::ExternalProgram (const char *const *argv, const Environment & environment,
+                                 Stderr_Disposition stderr_disp, bool use_pty,
+                                 int stderr_fd, bool default_locale,
+                                 const Pathname& root)
+      : use_pty (use_pty)
+    {
+      const char* rootdir = NULL;
+      if(!root.empty() && root != "/")
+      {
+       rootdir = root.asString().c_str();
+      }
+      start_program (argv, environment, stderr_disp, stderr_fd, default_locale, rootdir);
+    }
+    
+    
+    ExternalProgram::ExternalProgram (const char *binpath, const char *const *argv_1,
+                                 bool use_pty)
+      : use_pty (use_pty)
+    {
+      int i = 0;
+      while (argv_1[i++])
+       ;
+      const char *argv[i + 1];
+      argv[0] = binpath;
+      memcpy (&argv[1], argv_1, (i - 1) * sizeof (char *));
+      Environment environment;
+      start_program (argv, environment);
+    }
+    
+    
+    ExternalProgram::ExternalProgram (const char *binpath, const char *const *argv_1, const Environment & environment,
+                                 bool use_pty)
+      : use_pty (use_pty)
+    {
+      int i = 0;
+      while (argv_1[i++])
+       ;
+      const char *argv[i + 1];
+      argv[0] = binpath;
+      memcpy (&argv[1], argv_1, (i - 1) * sizeof (char *));
+      start_program (argv, environment);
+    }
+    
+    
+    ExternalProgram::~ExternalProgram()
+    {
+    }
+    
+    
+    void
+    ExternalProgram::start_program (const char *const *argv, const Environment & environment,
+                               Stderr_Disposition stderr_disp,
+                               int stderr_fd, bool default_locale, const char* root)
+    {
+      pid = -1;
+      _exitStatus = 0;
+      int to_external[2], from_external[2];  // fds for pair of pipes
+      int master_tty,  slave_tty;         // fds for pair of ttys
+    
+      if (use_pty)
+      {
+       // Create pair of ttys
+          DBG << "Using ttys for communication with " << argv[0] << endl;
+       if (openpty (&master_tty, &slave_tty, 0, 0, 0) != 0)
+       {
+           ERR << "openpty failed" << endl;
+           return;
+       }
+      }
+      else
+      {
+       // Create pair of pipes
+       if (pipe (to_external) != 0 || pipe (from_external) != 0)
+       {
+           ERR << "pipe failed" << endl;
+           return;
+       }
+      }
+    
+      // do not remove the single quotes around every argument, copy&paste of
+      // command to shell will not work otherwise!
+      DBG << "Executing ";
+      for (int i = 0; argv[i]; i++)
+      {
+       if (i>0) DBG << ' ';
+       DBG << '\'';
+       DBG << argv[i];
+       DBG << '\'';
+      }
+      DBG << endl;
+    
+      // Create module process
+      if ((pid = fork()) == 0)
+      {
+       if (use_pty)
+       {
+           setsid();
+           if(slave_tty != 1)
+               dup2 (slave_tty, 1);      // set new stdout
+           renumber_fd (slave_tty, 0);   // set new stdin
+           ::close(master_tty);          // Belongs to father process
+    
+           // We currently have no controlling terminal (due to setsid).
+           // The first open call will also set the new ctty (due to historical
+           // unix guru knowledge ;-) )
+    
+           char name[512];
+           ttyname_r(slave_tty, name, sizeof(name));
+           ::close(open(name, O_RDONLY));
+       }
+       else
+       {
+           renumber_fd (to_external[0], 0); // set new stdin
+           ::close(from_external[0]);    // Belongs to father process
+    
+           renumber_fd (from_external[1], 1); // set new stdout
+           ::close(to_external  [1]);    // Belongs to father process
+       }
+    
+       // Handle stderr
+       if (stderr_disp == Discard_Stderr)
+       {
+           int null_fd = open("/dev/null", O_WRONLY);
+           dup2(null_fd, 2);
+           ::close(null_fd);
+       }
+       else if (stderr_disp == Stderr_To_Stdout)
+       {
+           dup2(1, 2);
+       }
+       else if (stderr_disp == Stderr_To_FileDesc)
+       {
+           // Note: We don't have to close anything regarding stderr_fd.
+           // Our caller is responsible for that.
+           dup2 (stderr_fd, 2);
+       }
+    
+       for ( Environment::const_iterator it = environment.begin(); it != environment.end(); ++it ) {
+         setenv( it->first.c_str(), it->second.c_str(), 1 );
+       }
+    
+       if(default_locale)
+               setenv("LC_ALL","C",1);
+    
+       if(root)
+       {
+           if(chroot(root) == -1)
+           {
+               ERR << "chroot to " << root << " failed: " << strerror(errno) << endl;
+               _exit (3);                      // No sense in returning! I am forked away!!
+           }
+           if(chdir("/") == -1)
+           {
+               ERR << "chdir to / inside chroot failed: " << strerror(errno) << endl;
+               _exit (4);                      // No sense in returning! I am forked away!!
+           }
+       }
+    
+       // close all filedesctiptors above stderr
+       for ( int i = ::getdtablesize() - 1; i > 2; --i ) {
+         ::close( i );
+       }
+    
+       execvp(argv[0], const_cast<char *const *>(argv));
+       ERR << "Cannot execute external program "
+                << argv[0] << ":" << strerror(errno) << endl;
+       _exit (5);                      // No sense in returning! I am forked away!!
+      }
+    
+      else if (pid == -1)       // Fork failed, close everything.
+      {
+       if (use_pty) {
+           ::close(master_tty);
+           ::close(slave_tty);
+       }
+       else {
+           ::close(to_external[0]);
+           ::close(to_external[1]);
+           ::close(from_external[0]);
+           ::close(from_external[1]);
+       }
+       ERR << "Cannot fork " << strerror(errno) << endl;
+      }
+    
+      else {
+       if (use_pty)
+       {
+           ::close(slave_tty);        // belongs to child process
+           inputfile  = fdopen(master_tty, "r");
+           outputfile = fdopen(master_tty, "w");
+       }
+       else
+       {
+           ::close(to_external[0]);   // belongs to child process
+           ::close(from_external[1]); // belongs to child process
+           inputfile = fdopen(from_external[0], "r");
+           outputfile = fdopen(to_external[1], "w");
+       }
+    
+       DBG << "pid " << pid << " launched" << endl;
+    
+       if (!inputfile || !outputfile)
+       {
+           ERR << "Cannot create streams to external program " << argv[0] << endl;
+           close();
+       }
+      }
+    }
+    
+    
+    int
+    ExternalProgram::close()
+    {
+      if (pid > 0)
+      {
+       ExternalDataSource::close();
+       // Wait for child to exit
+       int ret;
+          int status = 0;
+       do
+       {
+           ret = waitpid(pid, &status, 0);
+       }
+       while (ret == -1 && errno == EINTR);
+    
+       if (ret != -1)
+       {
+           status = checkStatus( status );
+       }
+          pid = -1;
+          return status;
+      }
+      else
+      {
+          return _exitStatus;
+      }
+    }
+    
+    
+    int ExternalProgram::checkStatus( int status )
+    {
+      if (WIFEXITED (status))
+      {
+       status = WEXITSTATUS (status);
+       if(status)
+       {
+           DBG << "pid " << pid << " exited with status " << status << endl;
+       }
+       else
+       {
+           // if 'launch' is logged, completion should be logged,
+           // even if successfull.
+           DBG << "pid " << pid << " successfully completed" << endl;
+       }
+      }
+      else if (WIFSIGNALED (status))
+      {
+       status = WTERMSIG (status);
+       WAR << "pid " << pid << " was killed by signal " << status
+               << " (" << strsignal(status);
+       if (WCOREDUMP (status))
+       {
+           WAR << ", core dumped";
+       }
+       WAR << ")" << endl;
+       status+=128;
+      }
+      else {
+       ERR << "pid " << pid << " exited with unknown error" << endl;
+      }
+    
+      return status;
+    }
+    
+    bool
+    ExternalProgram::kill()
+    {
+      if (pid > 0)
+      {
+       ::kill(pid, SIGKILL);
+       close();
+      }
+      return true;
+    }
+    
+    
+    bool
+    ExternalProgram::running()
+    {
+      if ( pid < 0 ) return false;
+    
+      int status = 0;
+      int p = waitpid( pid, &status, WNOHANG );
+      if ( p < 0 ) return false;
+    
+      status = checkStatus( status );
+    
+      if ( status == 0 )
+      {
+          return true;
+      }
+      else
+      {
+          _exitStatus = status;
+          pid = -1;
+          return false;
+      }
+    }
+    
+    // origfd will be accessible as newfd and closed (unless they were equal)
+    void ExternalProgram::renumber_fd (int origfd, int newfd)
+    {
+      // It may happen that origfd is already the one we want
+      // (Although in our circumstances, that would mean somebody has closed
+      // our stdin or stdout... weird but has appened to Cray, #49797)
+      if (origfd != newfd)
+      {
+       dup2 (origfd, newfd);
+       ::close (origfd);
+      }
+    }
+    
+} // namespace zypp
diff --git a/zypp/ExternalProgram.h b/zypp/ExternalProgram.h
new file mode 100644 (file)
index 0000000..256764b
--- /dev/null
@@ -0,0 +1,137 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/ExternalProgram.h
+ *
+ * \todo replace by Blocxx
+ *
+*/
+
+
+#ifndef ZYPP_EXTERNALPROGRAM_H
+#define ZYPP_EXTERNALPROGRAM_H
+
+#include <map>
+#include <string>
+
+#include "zypp/base/ExternalDataSource.h"
+#include "zypp/Pathname.h"
+
+namespace zypp {
+
+    /**
+     * @short Execute a program and give access to its io
+     * An object of this class encapsulates the execution of
+     * an external program. It starts the program using fork
+     * and some exec.. call, gives you access to the program's
+     * stdio and closes the program after use.
+     */
+    class ExternalProgram : public zypp::externalprogram::ExternalDataSource
+    {
+    
+    public:
+      /**
+       * Define symbols for different policies on the handling
+       * of stderr
+       */
+      enum Stderr_Disposition {
+       Normal_Stderr,
+       Discard_Stderr,
+       Stderr_To_Stdout,
+       Stderr_To_FileDesc
+      };
+    
+      /**
+       * For passing additional environment variables to set
+       */
+      typedef std::map<std::string,std::string> Environment;
+    
+      /**
+       * Start the external program by using the shell <tt>/bin/sh<tt>
+       * with the option <tt>-c</tt>. You can use io direction symbols < and >.
+       * @param commandline a shell commandline that is appended to
+       * <tt>/bin/sh -c</tt>.
+       * @param default_locale whether to set LC_ALL=C before starting
+       * @param root directory to chroot into, / or empty to not chroot
+       */
+      ExternalProgram (std::string commandline,
+                    Stderr_Disposition stderr_disp = Normal_Stderr,
+                    bool use_pty = false, int stderr_fd = -1, bool default_locale = false,
+                    const Pathname& root = "");
+    
+      /**
+       * Start an external program by giving the arguments as an arry of char *pointers.
+       * If environment is provided, varaiables will be added to the childs environment,
+       * overwriting existing ones.
+       */
+      ExternalProgram (const char *const *argv,
+                    Stderr_Disposition stderr_disp = Normal_Stderr,
+                    bool use_pty = false, int stderr_fd = -1, bool default_locale = false,
+                    const Pathname& root = "");
+    
+      ExternalProgram (const char *const *argv, const Environment & environment,
+                    Stderr_Disposition stderr_disp = Normal_Stderr,
+                    bool use_pty = false, int stderr_fd = -1, bool default_locale = false,
+                    const Pathname& root = "");
+    
+      ExternalProgram (const char *binpath, const char *const *argv_1,
+                    bool use_pty = false);
+    
+    
+      ExternalProgram (const char *binpath, const char *const *argv_1, const Environment & environment,
+                    bool use_pty = false);
+    
+    
+      ~ExternalProgram();
+    
+      int close();
+    
+      /**
+       * Kill the program
+       */
+      bool kill();
+    
+      /**
+       * Return whether program is running
+       */
+      bool running();
+    
+      /**
+       * return pid
+       * */
+      pid_t getpid() { return pid; }
+    
+      /**
+       * origfd will be accessible as newfd and closed (unless they were equal)
+       */
+      static void renumber_fd (int origfd, int newfd);
+    
+    protected:
+      int checkStatus( int );
+    
+    private:
+    
+      /**
+       * Set to true, if a pair of ttys is used for communication
+       * instead of a pair of pipes.
+       */
+      bool use_pty;
+    
+      pid_t pid;
+      int _exitStatus;
+    
+      void start_program (const char *const *argv, const Environment & environment,
+                       Stderr_Disposition stderr_disp = Normal_Stderr,
+                       int stderr_fd = -1, bool default_locale = false,
+                       const char* root = NULL);
+    
+    };
+    
+} // namespace zypp
+
+#endif // ZYPP_EXTERNALPROGRAM_H
index caac95d..56f709c 100644 (file)
@@ -22,7 +22,12 @@ include_HEADERS = NeedAType.h \
        Script.h        \
        Patch.h         \
        Product.h       \
-       Changelog.h
+       Changelog.h     \
+                       \
+       ExternalProgram.h       \
+       Pathname.cc     \
+       PathInfo.cc     \
+       Digest.cc
 
 ## ##################################################
 
@@ -47,7 +52,12 @@ lib@PACKAGE@_la_SOURCES = \
        Script.cc       \
        Patch.cc        \
        Product.cc      \
-       Changelog.cc
+       Changelog.cc    \
+                       \
+       ExternalProgram.cc      \
+       Pathname.cc     \
+       PathInfo.cc     \
+       Digest.cc
 
 lib@PACKAGE@_la_LDFLAGS =      @LIB_VERSION_INFO@
 
diff --git a/zypp/PathInfo.cc b/zypp/PathInfo.cc
new file mode 100644 (file)
index 0000000..c4d1294
--- /dev/null
@@ -0,0 +1,775 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/PathInfo.cc
+ *
+ * \todo replace by Blocxx
+ *
+*/
+
+#include <iostream>
+#include <fstream>
+#include <iomanip>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/stringutil.h"
+#include "zypp/ExternalProgram.h"
+
+#include "zypp/PathInfo.h"
+#include "zypp/Digest.h"
+
+using namespace std;
+using namespace zypp::base;
+
+namespace zypp {
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::PathInfo
+//     METHOD TYPE : Constructor
+//
+//     DESCRIPTION :
+//
+PathInfo::PathInfo( const Pathname & path, Mode initial )
+    : path_t( path )
+    , mode_e( initial )
+    , error_i( -1 )
+{
+  operator()();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::PathInfo
+//     METHOD TYPE : Constructor
+//
+//     DESCRIPTION :
+//
+PathInfo::PathInfo( const string & path, Mode initial )
+    : path_t( path )
+    , mode_e( initial )
+    , error_i( -1 )
+{
+  operator()();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::PathInfo
+//     METHOD TYPE : Constructor
+//
+//     DESCRIPTION :
+//
+PathInfo::PathInfo( const char * path, Mode initial )
+    : path_t( path )
+    , mode_e( initial )
+    , error_i( -1 )
+{
+  operator()();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::~PathInfo
+//     METHOD TYPE : Destructor
+//
+//     DESCRIPTION :
+//
+PathInfo::~PathInfo()
+{
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::operator()
+//     METHOD TYPE : bool
+//
+//     DESCRIPTION :
+//
+bool PathInfo::operator()()
+{
+  if ( path_t.empty() ) {
+    error_i = -1;
+  } else {
+    switch ( mode_e ) {
+    case STAT:
+      error_i = ::stat( path_t.asString().c_str(), &statbuf_C );
+      break;
+    case LSTAT:
+      error_i = ::lstat( path_t.asString().c_str(), &statbuf_C );
+      break;
+    }
+    if ( error_i == -1 )
+      error_i = errno;
+  }
+  return !error_i;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::fileType
+//     METHOD TYPE : PathInfo::file_type
+//
+PathInfo::file_type PathInfo::fileType() const
+{
+  if ( isExist() )
+    return stat_mode( st_mode() ).fileType();
+  return NOT_EXIST;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::userMay
+//     METHOD TYPE : mode_t
+//
+//     DESCRIPTION :
+//
+mode_t PathInfo::userMay() const
+{
+  if ( !isExist() )
+    return 0;
+  if ( owner() == getuid() ) {
+    return( uperm()/0100 );
+  } else if ( group() == getgid() ) {
+    return( gperm()/010 );
+  }
+  return operm();
+}
+
+/******************************************************************
+**
+**
+**     FUNCTION NAME : operator<<
+**     FUNCTION TYPE : ostream &
+**
+**     DESCRIPTION :
+*/
+ostream & operator<<( ostream & str, const PathInfo & obj )
+{
+  ios::fmtflags state_ii = str.flags();
+
+  str << obj.asString() << "{";
+  if ( !obj.isExist() ) {
+    str << "does not exist}";
+  } else {
+    str << PathInfo::stat_mode( obj.st_mode() ) << " " << dec << obj.owner() << "/" << obj.group();
+
+    if ( obj.isFile() )
+      str << " size " << obj.size();
+
+    str << "}";
+  }
+  str.flags( state_ii );
+  return str;
+}
+
+/******************************************************************
+**
+**
+**     FUNCTION NAME : operator<<
+**     FUNCTION TYPE : std::ostream &
+**
+**     DESCRIPTION :
+*/
+ostream & operator<<( ostream & str, PathInfo::file_type obj )
+{
+  switch ( obj ) {
+#define EMUMOUT(T) case PathInfo::T: return str << #T; break
+    EMUMOUT( NOT_AVAIL );
+    EMUMOUT( NOT_EXIST );
+    EMUMOUT( T_FILE );
+    EMUMOUT( T_DIR );
+    EMUMOUT( T_CHARDEV );
+    EMUMOUT( T_BLOCKDEV );
+    EMUMOUT( T_FIFO );
+    EMUMOUT( T_LINK );
+    EMUMOUT( T_SOCKET );
+#undef EMUMOUT
+  }
+  return str;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::stat_mode::fileType
+//     METHOD TYPE : PathInfo::file_type
+//
+PathInfo::file_type PathInfo::stat_mode::fileType() const
+{
+  if ( isFile() )
+    return T_FILE;
+  if ( isDir() )
+    return T_DIR;
+  if ( isLink() )
+    return T_LINK;
+  if ( isChr() )
+    return T_CHARDEV;
+  if ( isBlk() )
+    return T_BLOCKDEV;
+  if ( isFifo() )
+    return T_FIFO;
+  if ( isSock() )
+    return T_SOCKET ;
+
+  return NOT_AVAIL;
+}
+
+/******************************************************************
+**
+**
+**     FUNCTION NAME : operator<<
+**     FUNCTION TYPE : std::ostream &
+**
+**     DESCRIPTION :
+*/
+std::ostream & operator<<( std::ostream & str, const PathInfo::stat_mode & obj )
+{
+  ios::fmtflags state_ii = str.flags();
+  char t = '?';
+  if ( obj.isFile() )
+    t = '-';
+  else if ( obj.isDir() )
+    t = 'd';
+  else if ( obj.isLink() )
+    t = 'l';
+  else if ( obj.isChr() )
+    t = 'c';
+  else if ( obj.isBlk() )
+    t = 'b';
+  else if ( obj.isFifo() )
+    t = 'p';
+  else if ( obj.isSock() )
+    t = 's';
+
+  str << t << " " << setfill( '0' ) << setw( 4 ) << oct << obj.perm();
+  str.flags( state_ii );
+  return str;
+}
+
+/******************************************************************
+**
+**
+**     FUNCTION NAME : _Log_Result
+**     FUNCTION TYPE : int
+**
+**     DESCRIPTION : Helper function to log return values.
+*/
+inline int _Log_Result( const int res, const char * rclass = "errno" )
+{
+  if ( res )
+    DBG << " FAILED: " << rclass << " " << res;
+  DBG << endl;
+  return res;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::mkdir
+//     METHOD TYPE : int
+//
+//     DESCRIPTION :
+//
+int PathInfo::mkdir( const Pathname & path, unsigned mode )
+{
+  DBG << "mkdir " << path << ' ' << stringutil::octstring( mode );
+  if ( ::mkdir( path.asString().c_str(), mode ) == -1 ) {
+    return _Log_Result( errno );
+  }
+  return _Log_Result( 0 );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::assert_dir()
+//     METHOD TYPE : int
+//
+//     DESCRIPTION :
+//
+int PathInfo::assert_dir( const Pathname & path, unsigned mode )
+{
+    string::size_type pos, lastpos = 0;
+    string spath = path.asString()+"/";
+    int ret = 0;
+
+    if(path.empty())
+       return ENOENT;
+
+    // skip ./
+    if(path.relative())
+       lastpos=2;
+    // skip /
+    else
+       lastpos=1;
+
+//    DBG << "about to create " << spath << endl;
+    while((pos = spath.find('/',lastpos)) != string::npos )
+    {
+       string dir = spath.substr(0,pos);
+       ret = ::mkdir(dir.c_str(), mode);
+       if(ret == -1)
+       {
+           // ignore errors about already existing directorys
+           if(errno == EEXIST)
+               ret=0;
+           else
+               ret=errno;
+       }
+//     DBG << "creating directory " << dir << (ret?" failed":" succeeded") << endl;
+       lastpos = pos+1;
+    }
+    return ret;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::rmdir
+//     METHOD TYPE : int
+//
+//     DESCRIPTION :
+//
+int PathInfo::rmdir( const Pathname & path )
+{
+  DBG << "rmdir " << path;
+  if ( ::rmdir( path.asString().c_str() ) == -1 ) {
+    return _Log_Result( errno );
+  }
+  return _Log_Result( 0 );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::recursive_rmdir
+//     METHOD TYPE : int
+//
+//     DESCRIPTION :
+//
+int PathInfo::recursive_rmdir( const Pathname & path )
+{
+  DBG << "recursive_rmdir " << path << ' ';
+  PathInfo p( path );
+
+  if ( !p.isExist() ) {
+    return _Log_Result( 0 );
+  }
+
+  if ( !p.isDir() ) {
+    return _Log_Result( ENOTDIR );
+  }
+
+  const char *const argv[] = {
+    "/bin/rm",
+    "-rf",
+    "--preserve-root",
+    "--",
+    path.asString().c_str(),
+    NULL
+  };
+
+  ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
+  for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
+    DBG << "  " << output;
+  }
+  int ret = prog.close();
+  return _Log_Result( ret, "returned" );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::clean_dir
+//     METHOD TYPE : int
+//
+//     DESCRIPTION :
+//
+int PathInfo::clean_dir( const Pathname & path )
+{
+  DBG << "clean_dir " << path << ' ';
+  PathInfo p( path );
+
+  if ( !p.isExist() ) {
+    return _Log_Result( 0 );
+  }
+
+  if ( !p.isDir() ) {
+    return _Log_Result( ENOTDIR );
+  }
+
+  string cmd( stringutil::form( "cd '%s' && rm -rf --preserve-root -- *", path.asString().c_str() ) );
+  ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
+  for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
+    DBG << "  " << output;
+  }
+  int ret = prog.close();
+  return _Log_Result( ret, "returned" );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::copy_dir
+//     METHOD TYPE : int
+//
+//     DESCRIPTION :
+//
+int PathInfo::copy_dir( const Pathname & srcpath, const Pathname & destpath )
+{
+  DBG << "copy_dir " << srcpath << " -> " << destpath << ' ';
+
+  PathInfo sp( srcpath );
+  if ( !sp.isDir() ) {
+    return _Log_Result( ENOTDIR );
+  }
+
+  PathInfo dp( destpath );
+  if ( !dp.isDir() ) {
+    return _Log_Result( ENOTDIR );
+  }
+
+  PathInfo tp( destpath + srcpath.basename() );
+  if ( tp.isExist() ) {
+    return _Log_Result( EEXIST );
+  }
+
+
+  const char *const argv[] = {
+    "/bin/cp",
+    "-dR",
+    "--",
+    srcpath.asString().c_str(),
+    destpath.asString().c_str(),
+    NULL
+  };
+  ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
+  for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
+    DBG << "  " << output;
+  }
+  int ret = prog.close();
+  return _Log_Result( ret, "returned" );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::readdir
+//     METHOD TYPE : int
+//
+//     DESCRIPTION :
+//
+int PathInfo::readdir( std::list<std::string> & retlist,
+                      const Pathname & path, bool dots )
+{
+  retlist.clear();
+
+  DBG << "readdir " << path << ' ';
+
+  DIR * dir = ::opendir( path.asString().c_str() );
+  if ( ! dir ) {
+    return _Log_Result( errno );
+  }
+
+  struct dirent *entry;
+  while ( (entry = ::readdir( dir )) != 0 ) {
+
+    if ( entry->d_name[0] == '.' ) {
+      if ( !dots )
+       continue;
+      if ( entry->d_name[1] == '\0'
+          || (    entry->d_name[1] == '.'
+               && entry->d_name[2] == '\0' ) )
+       continue;
+    }
+    retlist.push_back( entry->d_name );
+  }
+
+  ::closedir( dir );
+
+  return _Log_Result( 0 );
+}
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::readdir
+//     METHOD TYPE : int
+//
+int PathInfo::readdir( dircontent & retlist, const Pathname & path,
+                      bool dots, Mode statmode )
+{
+  retlist.clear();
+
+  list<string> content;
+  int res = readdir( content, path, dots );
+
+  if ( !res ) {
+    for ( list<string>::const_iterator it = content.begin(); it != content.end(); ++it ) {
+      PathInfo p( path + *it, statmode );
+      retlist.push_back( direntry( *it, p.fileType() ) );
+    }
+  }
+
+  return res;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::unlink
+//     METHOD TYPE : int
+//
+//     DESCRIPTION :
+//
+int PathInfo::unlink( const Pathname & path )
+{
+  DBG << "unlink " << path;
+  if ( ::unlink( path.asString().c_str() ) == -1 ) {
+    return _Log_Result( errno );
+  }
+  return _Log_Result( 0 );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::rename
+//     METHOD TYPE : int
+//
+//     DESCRIPTION :
+//
+int PathInfo::rename( const Pathname & oldpath, const Pathname & newpath )
+{
+  DBG << "rename " << oldpath << " -> " << newpath;
+  if ( ::rename( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
+    return _Log_Result( errno );
+  }
+  return _Log_Result( 0 );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::copy
+//     METHOD TYPE : int
+//
+//     DESCRIPTION :
+//
+int PathInfo::copy( const Pathname & file, const Pathname & dest )
+{
+  DBG << "copy " << file << " -> " << dest << ' ';
+
+  PathInfo sp( file );
+  if ( !sp.isFile() ) {
+    return _Log_Result( EINVAL );
+  }
+
+  PathInfo dp( dest );
+  if ( dp.isDir() ) {
+    return _Log_Result( EISDIR );
+  }
+
+  const char *const argv[] = {
+    "/bin/cp",
+    "--",
+    file.asString().c_str(),
+    dest.asString().c_str(),
+    NULL
+  };
+  ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
+  for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
+    DBG << "  " << output;
+  }
+  int ret = prog.close();
+  return _Log_Result( ret, "returned" );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::symlink
+//     METHOD TYPE : int
+//
+//     DESCRIPTION :
+//
+int PathInfo::symlink( const Pathname & oldpath, const Pathname & newpath )
+{
+  DBG << "symlink " << newpath << " -> " << oldpath;
+  if ( ::symlink( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
+    return _Log_Result( errno );
+  }
+  return _Log_Result( 0 );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::hardlink
+//     METHOD TYPE : int
+//
+//     DESCRIPTION :
+//
+int PathInfo::hardlink( const Pathname & oldpath, const Pathname & newpath )
+{
+  DBG << "hardlink " << newpath << " -> " << oldpath;
+  if ( ::link( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
+    return _Log_Result( errno );
+  }
+  return _Log_Result( 0 );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::copy_file2dir
+//     METHOD TYPE : int
+//
+//     DESCRIPTION :
+//
+int PathInfo::copy_file2dir( const Pathname & file, const Pathname & dest )
+{
+  DBG << "copy_file2dir " << file << " -> " << dest << ' ';
+
+  PathInfo sp( file );
+  if ( !sp.isFile() ) {
+    return _Log_Result( EINVAL );
+  }
+
+  PathInfo dp( dest );
+  if ( !dp.isDir() ) {
+    return _Log_Result( ENOTDIR );
+  }
+
+  const char *const argv[] = {
+    "/bin/cp",
+    "--",
+    file.asString().c_str(),
+    dest.asString().c_str(),
+    NULL
+  };
+  ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
+  for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
+    DBG << "  " << output;
+  }
+  int ret = prog.close();
+  return _Log_Result( ret, "returned" );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::md5sum
+//     METHOD TYPE : std::string
+//
+std::string PathInfo::md5sum( const Pathname & file )
+{
+  if ( ! PathInfo( file ).isFile() ) {
+    return string();
+  }
+  ifstream istr( file.asString().c_str() );
+  if ( ! istr ) {
+    return string();
+  }
+  return Digest::digest( "MD5", istr );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::sha1sum
+//     METHOD TYPE : std::string
+//
+std::string PathInfo::sha1sum( const Pathname & file )
+{
+  if ( ! PathInfo( file ).isFile() ) {
+    return string();
+  }
+  ifstream istr( file.asString().c_str() );
+  if ( ! istr ) {
+    return string();
+  }
+  return Digest::digest( "SHA1", istr );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::erase
+//     METHOD TYPE : int
+//
+//     DESCRIPTION :
+//
+int PathInfo::erase( const Pathname & path )
+{
+  int res = 0;
+  PathInfo p( path, LSTAT );
+  if ( p.isExist() )
+    {
+      if ( p.isDir() )
+        res = PathInfo::recursive_rmdir( path );
+      else
+        res = PathInfo::unlink( path );
+    }
+  return res;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::chmod
+//     METHOD TYPE : int
+//
+int PathInfo::chmod( const Pathname & path, mode_t mode )
+{
+  DBG << "chmod " << path << ' ' << stringutil::octstring( mode );
+  if ( ::chmod( path.asString().c_str(), mode ) == -1 ) {
+    return _Log_Result( errno );
+  }
+  return _Log_Result( 0 );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : PathInfo::zipType
+//     METHOD TYPE : PathInfo::ZIP_TYPE
+//
+PathInfo::ZIP_TYPE PathInfo::zipType( const Pathname & file )
+{
+  ZIP_TYPE ret = ZT_NONE;
+
+  int fd = open( file.asString().c_str(), O_RDONLY );
+
+  if ( fd != -1 ) {
+    const int magicSize = 3;
+    unsigned char magic[magicSize];
+    memset( magic, 0, magicSize );
+    if ( read( fd, magic, magicSize ) == magicSize ) {
+      if ( magic[0] == 0037 && magic[1] == 0213 ) {
+       ret = ZT_GZ;
+      } else if ( magic[0] == 'B' && magic[1] == 'Z' && magic[2] == 'h' ) {
+       ret = ZT_BZ2;
+      }
+    }
+    close( fd );
+  }
+
+  return ret;
+}
+
+} // namespace zypp
diff --git a/zypp/PathInfo.h b/zypp/PathInfo.h
new file mode 100644 (file)
index 0000000..397b62e
--- /dev/null
@@ -0,0 +1,489 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/PathInfo.h
+ *
+ * \todo replace by Blocxx
+ *
+*/
+#ifndef ZYPP_PATHINFO_H
+#define ZYPP_PATHINFO_H
+
+extern "C"
+{
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+}
+
+#include <cerrno>
+#include <iosfwd>
+#include <list>
+#include <set>
+#include <map>
+
+#include "zypp/Pathname.h"
+
+namespace zypp {
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : PathInfo
+/**
+ * @short Wrapper class for ::stat/::lstat and other file/directory related operations.
+ **/
+class PathInfo {
+
+  friend std::ostream & operator<<( std::ostream & str, const PathInfo & obj );
+
+  public:
+
+    enum Mode { STAT, LSTAT };
+
+    enum file_type {
+      NOT_AVAIL  = 0x00, // no typeinfo available
+      NOT_EXIST  = 0x01, // file does not exist
+      T_FILE     = 0x02,
+      T_DIR      = 0x04,
+      T_CHARDEV  = 0x08,
+      T_BLOCKDEV = 0x10,
+      T_FIFO     = 0x20,
+      T_LINK     = 0x40,
+      T_SOCKET   = 0x80
+    };
+    friend std::ostream & operator<<( std::ostream & str, file_type obj );
+
+    /**
+     * Wrapper class for mode_t values as derived from ::stat
+     **/
+    class stat_mode;
+
+    /**
+     * Simple cache remembering device/inode to detect hardlinks.
+     **/
+    class devino_cache;
+
+  private:
+
+    Pathname    path_t;
+
+    struct stat statbuf_C;
+    Mode        mode_e;
+    int         error_i;
+
+  public:
+
+    PathInfo( const Pathname & path = "", Mode initial = STAT );
+    PathInfo( const std::string & path, Mode initial = STAT );
+    PathInfo( const char * path, Mode initial = STAT );
+    virtual ~PathInfo();
+
+    const Pathname &    path()     const { return path_t; }
+    const std::string & asString() const { return path_t.asString(); }
+    Mode                mode()     const { return mode_e; }
+    int                 error()    const { return error_i; }
+
+    void setPath( const Pathname & path ) { if ( path != path_t ) error_i = -1; path_t = path; }
+    void setMode( Mode mode )             { if ( mode != mode_e ) error_i = -1; mode_e = mode; }
+
+    bool stat      ( const Pathname & path ) { setPath( path ); setMode( STAT );  return operator()(); }
+    bool lstat     ( const Pathname & path ) { setPath( path ); setMode( LSTAT ); return operator()(); }
+    bool operator()( const Pathname & path ) { setPath( path ); return operator()(); }
+
+    bool stat()   { setMode( STAT );  return operator()(); }
+    bool lstat()  { setMode( LSTAT ); return operator()(); }
+    bool operator()();
+
+  public:
+
+    bool   isExist() const { return !error_i; }
+
+    // file type
+    file_type fileType() const;
+
+    bool   isFile()  const { return isExist() && S_ISREG( statbuf_C.st_mode ); }
+    bool   isDir ()  const { return isExist() && S_ISDIR( statbuf_C.st_mode ); }
+    bool   isLink()  const { return isExist() && S_ISLNK( statbuf_C.st_mode ); }
+    bool   isChr()   const { return isExist() && S_ISCHR( statbuf_C.st_mode ); }
+    bool   isBlk()   const { return isExist() && S_ISBLK( statbuf_C.st_mode ); }
+    bool   isFifo()  const { return isExist() && S_ISFIFO( statbuf_C.st_mode ); }
+    bool   isSock()  const { return isExist() && S_ISSOCK( statbuf_C.st_mode ); }
+
+    nlink_t nlink()  const { return isExist() ? statbuf_C.st_nlink : 0; }
+
+    // owner
+    uid_t  owner()   const { return isExist() ? statbuf_C.st_uid : 0; }
+    gid_t  group()   const { return isExist() ? statbuf_C.st_gid : 0; }
+
+    // permission
+    bool   isRUsr()  const { return isExist() && (statbuf_C.st_mode & S_IRUSR); }
+    bool   isWUsr()  const { return isExist() && (statbuf_C.st_mode & S_IWUSR); }
+    bool   isXUsr()  const { return isExist() && (statbuf_C.st_mode & S_IXUSR); }
+
+    bool   isR()     const { return isRUsr(); }
+    bool   isW()     const { return isWUsr(); }
+    bool   isX()     const { return isXUsr(); }
+
+    bool   isRGrp()  const { return isExist() && (statbuf_C.st_mode & S_IRGRP); }
+    bool   isWGrp()  const { return isExist() && (statbuf_C.st_mode & S_IWGRP); }
+    bool   isXGrp()  const { return isExist() && (statbuf_C.st_mode & S_IXGRP); }
+
+    bool   isROth()  const { return isExist() && (statbuf_C.st_mode & S_IROTH); }
+    bool   isWOth()  const { return isExist() && (statbuf_C.st_mode & S_IWOTH); }
+    bool   isXOth()  const { return isExist() && (statbuf_C.st_mode & S_IXOTH); }
+
+    bool   isUid()   const { return isExist() && (statbuf_C.st_mode & S_ISUID); }
+    bool   isGid()   const { return isExist() && (statbuf_C.st_mode & S_ISGID); }
+    bool   isVtx()   const { return isExist() && (statbuf_C.st_mode & S_ISVTX); }
+
+    mode_t uperm()   const { return isExist() ? (statbuf_C.st_mode & S_IRWXU) : 0; }
+    mode_t gperm()   const { return isExist() ? (statbuf_C.st_mode & S_IRWXG) : 0; }
+    mode_t operm()   const { return isExist() ? (statbuf_C.st_mode & S_IRWXO) : 0; }
+    mode_t perm()    const { return isExist() ? (statbuf_C.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID|S_ISVTX)) : 0; }
+
+    bool   isPerm ( mode_t m ) const { return (m == perm()); }
+    bool   hasPerm( mode_t m ) const { return (m == (m & perm())); }
+
+    mode_t st_mode() const { return isExist() ? statbuf_C.st_mode : 0; }
+
+    // permission according to current uid/gid (returns [0-7])
+    mode_t userMay() const;
+
+    bool   userMayR() const { return( userMay() & 01 ); }
+    bool   userMayW() const { return( userMay() & 02 ); }
+    bool   userMayX() const { return( userMay() & 04 ); }
+
+    bool   userMayRW()  const { return( (userMay() & 03) == 03 ); }
+    bool   userMayRX()  const { return( (userMay() & 05) == 05 ); }
+    bool   userMayWX()  const { return( (userMay() & 06) == 06 ); }
+
+    bool   userMayRWX() const { return( userMay() == 07 ); }
+
+    // device
+    dev_t  dev()     const { return isExist() ? statbuf_C.st_dev  : 0; }
+    dev_t  rdev()    const { return isExist() ? statbuf_C.st_rdev : 0; }
+    ino_t  ino()     const { return isExist() ? statbuf_C.st_ino  : 0; }
+
+    // size
+    off_t         size()    const { return isExist() ? statbuf_C.st_size : 0; }
+    unsigned long blksize() const { return isExist() ? statbuf_C.st_blksize : 0; }
+    unsigned long blocks()  const { return isExist() ? statbuf_C.st_blocks  : 0; }
+
+    // time
+    time_t atime()   const { return isExist() ? statbuf_C.st_atime : 0; } /* time of last access */
+    time_t mtime()   const { return isExist() ? statbuf_C.st_mtime : 0; } /* time of last modification */
+    time_t ctime()   const { return isExist() ? statbuf_C.st_ctime : 0; }
+
+  public:
+
+    ///////////////////////////////////////////////////////////////////
+    // convenience stuff
+    ///////////////////////////////////////////////////////////////////
+    // static functions as they may or may not invalidate any stat info
+    // stored by a PathiInfo.
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    // Directories
+    ///////////////////////////////////////////////////////////////////
+
+    /**
+     * Like '::mkdir'. Attempt to create a new directory named path. mode
+     * specifies the permissions to use. It is modified by the process's
+     * umask in the usual way.
+     *
+     * @return 0 on success, errno on failure
+     **/
+    static int mkdir( const Pathname & path, unsigned mode = 0755 );
+
+    /**
+     * Like 'mkdir -p'. No error if directory exists. Make parent directories
+     * as needed. mode specifies the permissions to use, if directories have to
+     * be created. It is modified by the process's umask in the usual way.
+     *
+     * @return 0 on success, errno on failure
+     **/
+    static int assert_dir( const Pathname & path, unsigned mode = 0755 );
+
+    /**
+     * Like '::rmdir'. Delete a directory, which must be empty.
+     *
+     * @return 0 on success, errno on failure
+     **/
+    static int rmdir( const Pathname & path );
+
+    /**
+     * Like 'rm -r DIR'. Delete a directory, recursively removing its contents.
+     *
+     * @return 0 on success, ENOTDIR if path is not a directory, otherwise the
+     * commands return value.
+     **/
+    static int recursive_rmdir( const Pathname & path );
+
+    /**
+     * Like 'rm -r DIR/ *'. Delete directory contents, but keep the directory itself.
+     *
+     * @return 0 on success, ENOTDIR if path is not a directory, otherwise the
+     * commands return value.
+     **/
+    static int clean_dir( const Pathname & path );
+
+    /**
+     * Like 'cp -a srcpath destpath'. Copy directory tree. srcpath/destpath must be
+     * directories. 'basename srcpath' must not exist in destpath.
+     *
+     * @return 0 on success, ENOTDIR if srcpath/destpath is not a directory, EEXIST if
+     * 'basename srcpath' exists in destpath, otherwise the commands return value.
+     **/
+    static int copy_dir( const Pathname & srcpath, const Pathname & destpath );
+
+    /**
+     * Return content of directory via retlist. If dots is false
+     * entries starting with '.' are not reported. "." and ".."
+     * are never reported.
+     *
+     * @return 0 on success, errno on failure.
+     **/
+    static int readdir( std::list<std::string> & retlist,
+                       const Pathname & path, bool dots = true );
+
+    struct direntry {
+      std::string name;
+      file_type   type;
+      direntry( const std::string & name_r = std::string(), file_type type_r = NOT_AVAIL )
+       : name( name_r )
+       , type( type_r )
+      {}
+    };
+
+    typedef std::list<direntry> dircontent;
+
+    /**
+     * Return content of directory via retlist. If dots is false
+     * entries starting with '.' are not reported. "." and ".."
+     * are never reported.
+     *
+     * The type of individual directory entries is determined accoding to
+     * statmode (i.e. via stat or lstat).
+     *
+     * @return 0 on success, errno on failure.
+     **/
+    static int readdir( dircontent & retlist, const Pathname & path,
+                       bool dots = true, Mode statmode = STAT );
+
+    ///////////////////////////////////////////////////////////////////
+    // Files
+    ///////////////////////////////////////////////////////////////////
+
+    /**
+     * Like '::unlink'. Delete a file (symbolic link, socket, fifo or device).
+     *
+     * @return 0 on success, errno on failure
+     **/
+    static int unlink( const Pathname & path );
+
+    /**
+     * Like '::rename'. Renames a file, moving it between directories if required.
+     *
+     * @return 0 on success, errno on failure
+     **/
+    static int rename( const Pathname & oldpath, const Pathname & newpath );
+
+    /**
+     * Like 'cp file dest'. Copy file to destination file.
+     *
+     * @return 0 on success, EINVAL if file is not a file, EISDIR if
+     * destiantion is a directory, otherwise the commands return value.
+     **/
+    static int copy( const Pathname & file, const Pathname & dest );
+
+    /**
+     * Like '::symlink'. Creates a symbolic link named newpath which contains
+     * the string oldpath. If newpath exists it will not be overwritten.
+     *
+     * @return 0 on success, errno on failure.
+     **/
+    static int symlink( const Pathname & oldpath, const Pathname & newpath );
+
+    /**
+     * Like '::link'. Creates a hard link named newpath to an existing file
+     * oldpath. If newpath exists it will not be overwritten.
+     *
+     * @return 0 on success, errno on failure.
+     **/
+    static int hardlink( const Pathname & oldpath, const Pathname & newpath );
+
+    /**
+     * Like 'cp file dest'. Copy file to dest dir.
+     *
+     * @return 0 on success, EINVAL if file is not a file, ENOTDIR if dest
+     * is no directory, otherwise the commands return value.
+     **/
+    static int copy_file2dir( const Pathname & file, const Pathname & dest );
+
+    /**
+     * Compute a files md5sum.
+     *
+     * @return the files md5sum on success, otherwise an empty string..
+     **/
+    static std::string md5sum( const Pathname & file );
+
+    /**
+     * Compute a files sha1sum.
+     *
+     * @return the files sha1sum on success, otherwise an empty string..
+     **/
+    static std::string sha1sum( const Pathname & file );
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    /**
+     * Erase whatever happens to be located at path (file or directory).
+     *
+     * @return 0 on success.
+     **/
+    static int erase( const Pathname & path );
+
+    ///////////////////////////////////////////////////////////////////
+    // permissions
+    ///////////////////////////////////////////////////////////////////
+
+    /**
+     * Like '::chmod'. The mode of the file given by path is changed.
+     *
+     * @return 0 on success, errno on failure
+     **/
+    static int chmod( const Pathname & path, mode_t mode );
+
+    ///////////////////////////////////////////////////////////////////
+    // magic
+    ///////////////////////////////////////////////////////////////////
+
+    /**
+     * Test whether a file is compressed (gzip/bzip2).
+     *
+     * @return ZT_GZ, ZT_BZ2 if file is compressed, otherwise ZT_NONE.
+     **/
+    enum ZIP_TYPE { ZT_NONE, ZT_GZ, ZT_BZ2 };
+
+    static ZIP_TYPE zipType( const Pathname & file );
+};
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : PathInfo::stat_mode
+/**
+ * @short Wrapper class for mode_t values as derived from ::stat
+ **/
+class PathInfo::stat_mode {
+  friend std::ostream & operator<<( std::ostream & str, const stat_mode & obj );
+  private:
+    mode_t _mode;
+  public:
+    stat_mode( const mode_t & mode_r = 0 ) : _mode( mode_r ) {}
+  public:
+    // file type
+    file_type fileType() const;
+
+    bool   isFile()  const { return S_ISREG( _mode ); }
+    bool   isDir ()  const { return S_ISDIR( _mode ); }
+    bool   isLink()  const { return S_ISLNK( _mode ); }
+    bool   isChr()   const { return S_ISCHR( _mode ); }
+    bool   isBlk()   const { return S_ISBLK( _mode ); }
+    bool   isFifo()  const { return S_ISFIFO( _mode ); }
+    bool   isSock()  const { return S_ISSOCK( _mode ); }
+
+    // permission
+    bool   isRUsr()  const { return (_mode & S_IRUSR); }
+    bool   isWUsr()  const { return (_mode & S_IWUSR); }
+    bool   isXUsr()  const { return (_mode & S_IXUSR); }
+
+    bool   isR()     const { return isRUsr(); }
+    bool   isW()     const { return isWUsr(); }
+    bool   isX()     const { return isXUsr(); }
+
+    bool   isRGrp()  const { return (_mode & S_IRGRP); }
+    bool   isWGrp()  const { return (_mode & S_IWGRP); }
+    bool   isXGrp()  const { return (_mode & S_IXGRP); }
+
+    bool   isROth()  const { return (_mode & S_IROTH); }
+    bool   isWOth()  const { return (_mode & S_IWOTH); }
+    bool   isXOth()  const { return (_mode & S_IXOTH); }
+
+    bool   isUid()   const { return (_mode & S_ISUID); }
+    bool   isGid()   const { return (_mode & S_ISGID); }
+    bool   isVtx()   const { return (_mode & S_ISVTX); }
+
+    mode_t uperm()   const { return (_mode & S_IRWXU); }
+    mode_t gperm()   const { return (_mode & S_IRWXG); }
+    mode_t operm()   const { return (_mode & S_IRWXO); }
+    mode_t perm()    const { return (_mode & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID|S_ISVTX)); }
+
+    bool   isPerm ( mode_t m ) const { return (m == perm()); }
+    bool   hasPerm( mode_t m ) const { return (m == (m & perm())); }
+
+    mode_t st_mode() const { return _mode; }
+};
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : PathInfo::devino_cache
+/**
+ * @short Simple cache remembering device/inode to detect hardlinks.
+ * <pre>
+ *     PathInfo::devino_cache trace;
+ *     for ( all files ) {
+ *       if ( trace.insert( file.device, file.inode ) ) {
+ *         // 1st occurance of file
+ *       }
+ *         // else: hardlink; already counted this device/inode
+ *       }
+ *     }
+ * </pre>
+ **/
+class PathInfo::devino_cache {
+
+  private:
+
+    std::map<dev_t,std::set<ino_t> > _devino;
+
+  public:
+    /**
+     * Constructor
+     **/
+    devino_cache() {}
+
+    /**
+     * Clear cache
+     **/
+    void clear() { _devino.clear(); }
+
+    /**
+     * Remember dev/ino. Return <code>true</code> if it's inserted the first
+     * time, <code>false</code> if alredy present in cache (a hardlink to a
+     * previously remembered file.
+     **/
+    bool insert( const dev_t & dev_r, const ino_t & ino_r ) {
+      return _devino[dev_r].insert( ino_r ).second;
+    }
+};
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+} // namespace zypp
+
+#endif // ZYPP_PATHINFO_H
diff --git a/zypp/Pathname.cc b/zypp/Pathname.cc
new file mode 100644 (file)
index 0000000..6c0c999
--- /dev/null
@@ -0,0 +1,292 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Pathname.cc
+ *
+ * \todo replace by Blocxx
+ *
+*/
+
+#include <iostream>
+
+#include "zypp/Pathname.h"
+
+using namespace std;
+
+namespace zypp {
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : DirStack
+//
+//     DESCRIPTION :
+//
+class DirStack {
+
+  struct Dir {
+
+    Dir *  up;
+    Dir *  dn;
+    string name;
+
+    Dir( const string & n = "" ) {
+      name = n;
+      up = dn = 0;
+    }
+
+    ~Dir() {
+      if ( up )
+       up->dn = dn;
+      if ( dn )
+       dn->up = up;
+    }
+  };
+
+  Dir *  top;
+  Dir *  bot;
+
+  void Pop() {
+    if ( !top )
+      return;
+    top = top->dn;
+    if ( top )
+      delete top->up;
+    else {
+      delete bot;
+      bot = 0;
+    }
+  }
+
+  public:
+
+    DirStack() { top = bot = 0; }
+    ~DirStack() {
+      while ( bot )
+       Pop();
+    }
+
+    void Push( const string & n ) {
+      if ( n.empty() || n == "." ) { // '.' or '/' only for bot
+       if ( bot )
+         return;
+      } else if ( n == ".." && top ) {
+       if ( top->name == "" )          // "/.."        ==> "/"
+         return;
+
+       if ( top->name != "." && top->name != ".." ) {      // "somedir/.." ==> ""
+         Pop();
+         return;
+       }
+       // "../.." "./.." stays
+      }
+
+      Dir * d = new Dir( n );
+      if ( !top )
+       top = bot = d;
+      else {
+       top->up = d;
+       d->dn = top;
+       d->up = 0;
+       top = d;
+      }
+    }
+
+    string str() {
+      if ( !bot )
+       return "";
+      string ret;
+      for ( Dir * d = bot; d; d = d->up ) {
+       if ( d != bot )
+         ret += "/";
+       ret += d->name;
+      }
+      if ( ret.empty() )
+       return "/";
+      return ret;
+    }
+};
+
+///////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : Pathname::_assign
+//     METHOD TYPE : void
+//
+//     DESCRIPTION :
+//
+void Pathname::_assign( const string & name_tv )
+{
+  prfx_i = 0;
+  name_t = name_tv;
+
+  if ( name_t.empty() )
+    return;
+
+  string   Tprfx;
+  DirStack Stack_Ci;
+
+  char *       Buf_aci    = new char[name_tv.length() + 1];
+  char *       W_pci      = Buf_aci;
+  const char * R_pci      = name_tv.c_str();
+
+  // check for prefix
+  if (    name_t.length() >= 2
+       && name_t[1] == ':'
+       && (    'a' <= name_t[0] && name_t[0] <= 'z'
+           || 'A' <= name_t[0] && name_t[0] <= 'Z' ) ) {
+    Tprfx  = name_t.substr( 0, 2 );
+    prfx_i = 2;
+    R_pci += 2;
+  }
+
+  // rel or abs path
+  if ( *R_pci == '/' || *R_pci == '\\' ) {
+    Stack_Ci.Push( "" );
+    ++R_pci;
+  } else {
+    Stack_Ci.Push( "." );
+  }
+
+  do {
+    switch ( *R_pci ) {
+    case '/':
+    case '\\':
+    case '\0':
+      if ( W_pci != Buf_aci ) {
+       *W_pci = '\0';
+       W_pci = Buf_aci;
+       Stack_Ci.Push( Buf_aci );
+      }
+      break;
+
+    default:
+      *W_pci++ = *R_pci;
+      break;
+    }
+  } while( *R_pci++ );
+
+  delete Buf_aci;
+  name_t = Tprfx + Stack_Ci.str();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : Pathname::dirname
+//     METHOD TYPE : Pathname
+//
+//     DESCRIPTION :
+//
+Pathname Pathname::dirname( const Pathname & name_tv )
+{
+  if ( name_tv.empty() )
+    return "";
+
+  Pathname ret_t( name_tv );
+  string::size_type idx = ret_t.name_t.find_last_of( '/' );
+
+  if ( idx == string::npos ) {
+    ret_t.name_t.erase( ret_t.prfx_i );
+    ret_t.name_t += ".";
+  } else if ( idx == ret_t.prfx_i ) {
+    ret_t.name_t.erase( ret_t.prfx_i );
+    ret_t.name_t += "/";
+  } else {
+    ret_t.name_t.erase( idx );
+  }
+
+  return ret_t;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : Pathname::basename
+//     METHOD TYPE : string
+//
+//     DESCRIPTION :
+//
+string Pathname::basename( const Pathname & name_tv )
+{
+  if ( name_tv.empty() )
+    return "";
+
+  string ret_t( name_tv.asString() );
+  ret_t.erase( 0, name_tv.prfx_i );
+  string::size_type idx = ret_t.find_last_of( '/' );
+  if ( idx != string::npos ) {
+    ret_t.erase( 0, idx+1 );
+  }
+
+  return ret_t;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : Pathname::cat
+//     METHOD TYPE : Pathname
+//
+//     DESCRIPTION :
+//
+Pathname Pathname::cat( const Pathname & name_tv, const Pathname & add_tv )
+{
+  if ( add_tv.empty() )
+    return name_tv;
+  if ( name_tv.empty() )
+    return add_tv;
+
+  string ret_ti( add_tv.asString() );
+  ret_ti.replace( 0, add_tv.prfx_i, "/" );
+
+  return name_tv.asString() + ret_ti;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : Pathname::Extend
+//     METHOD TYPE : Pathname
+//
+//     DESCRIPTION :
+//
+Pathname Pathname::extend( const Pathname & l, const string & r )
+{
+  return l.asString() + r;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//      METHOD NAME : Pathname::equal
+//      METHOD TYPE : bool
+//
+//      DESCRIPTION :
+//
+bool Pathname::equal( const Pathname & l, const Pathname & r )
+{
+  return l.asString() == r.asString();
+}
+
+/******************************************************************
+**
+**
+**     FUNCTION NAME : operator<<
+**     FUNCTION TYPE : inline std::ostream &
+**
+**     DESCRIPTION :
+*/
+ostream & operator<<( ostream & str, const Pathname & obj )
+{
+  return str << obj.asString();
+}
+
+} // namespace zypp
diff --git a/zypp/Pathname.h b/zypp/Pathname.h
new file mode 100644 (file)
index 0000000..5490283
--- /dev/null
@@ -0,0 +1,121 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/Pathname.h
+ *
+ * \todo replace by Blocxx
+ *
+*/
+
+#ifndef ZYPP_PATHNAME_H
+#define ZYPP_PATHNAME_H
+
+#include <iosfwd>
+#include <string>
+
+namespace zypp {
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : Pathname
+//
+//     DESCRIPTION :
+//
+class Pathname {
+
+  private:
+
+    std::string::size_type prfx_i;
+    std::string            name_t;
+
+  protected:
+
+    void _assign( const std::string & name_tv );
+
+  public:
+
+    virtual ~Pathname() {}
+
+    Pathname() {
+      prfx_i = 0;
+      name_t = "";
+    }
+    Pathname( const Pathname & path_tv ) {
+      prfx_i = path_tv.prfx_i;
+      name_t = path_tv.name_t;
+    }
+    Pathname( const std::string & name_tv ) {
+      _assign( name_tv );
+    }
+    Pathname( const char * name_tv ) {
+      _assign( name_tv ? name_tv : "" );
+    }
+
+    Pathname & operator= ( const Pathname & path_tv );
+    Pathname & operator+=( const Pathname & path_tv );
+
+    const std::string & asString() const { return name_t; }
+
+    bool empty()    const { return !name_t.size(); }
+    bool absolute() const { return !empty() && name_t[prfx_i] == '/'; }
+    bool relative() const { return !empty() && name_t[prfx_i] != '/'; }
+
+    Pathname    dirname()       const { return dirname( *this ); }
+    std::string basename()      const { return basename( *this ); }
+    Pathname    absolutename()  const { return absolutename( *this ); }
+    Pathname    relativename()  const { return relativename( *this ); }
+
+    static Pathname    dirname     ( const Pathname & name_tv );
+    static std::string basename    ( const Pathname & name_tv );
+    static Pathname    absolutename( const Pathname & name_tv ) { return name_tv.relative() ? cat( "/", name_tv ) : name_tv; }
+    static Pathname    relativename( const Pathname & name_tv ) { return name_tv.absolute() ? cat( ".", name_tv ) : name_tv; }
+
+    Pathname        cat( const Pathname & r ) const { return cat( *this, r ); }
+    static Pathname cat( const Pathname & l, const Pathname & r );
+
+    Pathname        extend( const std::string & r ) const { return extend( *this, r ); }
+    static Pathname extend( const Pathname & l, const std::string & r );
+
+    bool            equal( const Pathname & r ) const { return equal( *this, r ); }
+    static bool     equal( const Pathname & l, const Pathname & r );
+};
+
+///////////////////////////////////////////////////////////////////
+
+inline bool operator==( const Pathname & l, const Pathname & r ) {
+  return Pathname::equal( l, r );
+}
+
+inline bool operator!=( const Pathname & l, const Pathname & r ) {
+  return !Pathname::equal( l, r );
+}
+
+inline Pathname operator+( const Pathname & l, const Pathname & r ) {
+  return Pathname::cat( l, r );
+}
+
+inline Pathname & Pathname::operator=( const Pathname & path_tv ) {
+  if ( &path_tv != this ) {
+    prfx_i = path_tv.prfx_i;
+    name_t = path_tv.name_t;
+  }
+  return *this;
+}
+
+inline Pathname & Pathname::operator+=( const Pathname & path_tv ) {
+  return( *this = *this + path_tv );
+}
+
+///////////////////////////////////////////////////////////////////
+
+extern std::ostream & operator<<( std::ostream & str, const Pathname & obj );
+
+///////////////////////////////////////////////////////////////////
+} // namespace zypp
+
+#endif // ZYPP_PATHNAME_H
index 56f01dd..127a2ed 100644 (file)
@@ -14,7 +14,11 @@ include_HEADERS =    \
        PtrTypes.h      \
        ReferenceCounted.h      \
        String.h        \
-       StringVal.h
+       StringVal.h             \
+                               \
+                               \
+       ExternalDataSource.h    \
+       stringutil.h
 
 
 noinst_LTLIBRARIES =   lib@PACKAGE@_base.la
@@ -25,7 +29,11 @@ lib@PACKAGE@_base_la_SOURCES =       \
        Logger.cc       \
        Exception.cc    \
        String.cc       \
-       StringVal.cc
+       StringVal.cc    \
+                       \
+                       \
+       ExternalDataSource.cc   \
+       stringutil.cc
 
 lib@PACKAGE@_base_la_LIBADD = -lboost_regex
 
diff --git a/zypp/base/stringutil.cc b/zypp/base/stringutil.cc
new file mode 100644 (file)
index 0000000..e33857e
--- /dev/null
@@ -0,0 +1,298 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/base/stringutil.cc
+ *
+ * \todo replace by Blocxx
+ *
+*/
+
+#include <iostream>
+#include <fstream>
+
+#include "zypp/base/stringutil.h"
+
+using namespace std;
+///////////////////////////////////////////////////////////////////
+namespace zypp {
+  namespace base {
+    namespace stringutil {
+///////////////////////////////////////////////////////////////////
+
+      const unsigned tmpBuffLen = 1024;
+      char           tmpBuff[tmpBuffLen];
+      
+      /******************************************************************
+      **
+      **
+      **       FUNCTION NAME : getline
+      **       FUNCTION TYPE : std::string
+      **
+      **       DESCRIPTION :
+      */
+      static inline std::string _getline( std::istream & str, const Trim trim_r )
+      {
+        string ret;
+        do {
+          str.clear();
+          str.getline( tmpBuff, tmpBuffLen ); // always writes '\0' terminated
+          ret += tmpBuff;
+        } while( str.rdstate() == ios::failbit );
+      
+        return trim( ret, trim_r );
+      }
+      
+      std::string getline( std::istream & str, const Trim trim_r )
+      {
+        return _getline(str, trim_r);
+      }
+      
+      std::string getline( std::istream & str, bool trim )
+      {
+        return _getline(str, trim?TRIM:NO_TRIM);
+      }
+      
+      /******************************************************************
+      **
+      **
+      **       FUNCTION NAME : split
+      **       FUNCTION TYPE : unsigned
+      **
+      **       DESCRIPTION :
+      */
+      unsigned split( const std::string          line_tv,
+               std::vector<std::string> & words_Vtr,
+               const std::string &        sep_tv,
+               const bool                 singlesep_bv )
+      {
+        words_Vtr.clear();
+        if ( line_tv.empty() )
+          return words_Vtr.size();
+      
+        struct sepctrl {
+          const string & sep_t;
+          sepctrl( const string & sep_tv ) : sep_t( sep_tv ) {}
+          // Note that '\0' ist neither Sep nor NonSep
+          inline bool isSep     ( const char c )    const { return( sep_t.find( c ) != string::npos ); }
+          inline bool isNonSep  ( const char c )    const { return( c && !isSep(c) ); }
+          inline void skipSep   ( const char *& p ) const { while ( isSep( *p ) ) ++p; }
+          inline void skipNonSep( const char *& p ) const { while ( isNonSep( *p ) ) ++p; }
+        };
+      
+        sepctrl      sep_Ci( sep_tv );
+        const char * s_pci = line_tv.c_str();
+        const char * c_pci = s_pci;
+      
+        // Start with c_pci at the beginning of the 1st field to add.
+        // In singlesep the beginning might be equal to the next sep,
+        // which makes an empty field before the sep.
+        if ( !singlesep_bv && sep_Ci.isSep( *c_pci ) ) {
+          sep_Ci.skipSep( c_pci );
+        }
+      
+        for ( s_pci = c_pci; *s_pci; s_pci = c_pci ) {
+          sep_Ci.skipNonSep( c_pci );
+          words_Vtr.push_back( string( s_pci, c_pci - s_pci ) );
+          if ( *c_pci ) {
+            if ( singlesep_bv ) {
+              if ( !*(++c_pci) ) {
+                // line ends with a sep -> add the empty field behind
+                words_Vtr.push_back( "" );
+              }
+            } else
+              sep_Ci.skipSep( c_pci );
+          }
+        }
+      
+        return words_Vtr.size();
+      }
+      
+      /******************************************************************
+      **
+      **
+      **       FUNCTION NAME : join
+      **       FUNCTION TYPE : std::string
+      **
+      **       DESCRIPTION :
+      */
+      std::string join( const std::vector<std::string> & words_r,
+                 const std::string & sep_r )
+      {
+        if ( words_r.empty() )
+          return "";
+      
+        string ret( words_r[0] );
+      
+        for ( unsigned i = 1; i < words_r.size(); ++i ) {
+          ret += sep_r + words_r[i];
+        }
+      
+        return ret;
+      }
+      
+      /******************************************************************
+      **
+      **
+      **       FUNCTION NAME : stripFirstWord
+      **       FUNCTION TYPE : std::string
+      **
+      **       DESCRIPTION :
+      */
+      string stripFirstWord( string & line, const bool ltrim_first )
+      {
+        if ( ltrim_first )
+          line = ltrim( line );
+      
+        if ( line.empty() )
+          return line;
+      
+        string ret;
+        string::size_type p = line.find_first_of( " \t" );
+      
+        if ( p == string::npos ) {
+          // no ws on line
+          ret = line;
+          line.erase();
+        } else if ( p == 0 ) {
+          // starts with ws
+          // ret remains empty
+          line = ltrim( line );
+        }
+        else {
+          // strip word and ltim line
+          ret = line.substr( 0, p );
+          line = ltrim( line.erase( 0, p ) );
+        }
+        return ret;
+      }
+      
+      /******************************************************************
+      **
+      **
+      **       FUNCTION NAME : ltrim
+      **       FUNCTION TYPE : std::string
+      **
+      **       DESCRIPTION :
+      */
+      std::string ltrim( const std::string & s )
+      {
+        if ( s.empty() )
+          return s;
+      
+        string::size_type p = s.find_first_not_of( " \t\n" );
+        if ( p == string::npos )
+          return "";
+      
+        return s.substr( p );
+      }
+      
+      /******************************************************************
+      **
+      **
+      **       FUNCTION NAME : rtrim
+      **       FUNCTION TYPE : std::string
+      **
+      **       DESCRIPTION :
+      */
+      std::string rtrim( const std::string & s )
+      {
+        if ( s.empty() )
+          return s;
+      
+        string::size_type p = s.find_last_not_of( " \t\n" );
+        if ( p == string::npos )
+          return "";
+      
+        return s.substr( 0, p+1 );
+      }
+      
+      /******************************************************************
+      **
+      **
+      **       FUNCTION NAME : toLower
+      **       FUNCTION TYPE : std::string
+      **
+      **       DESCRIPTION :
+      */
+      std::string toLower( const std::string & s )
+      {
+        if ( s.empty() )
+          return s;
+      
+        string ret( s );
+        for ( string::size_type i = 0; i < ret.length(); ++i ) {
+          if ( isupper( ret[i] ) )
+            ret[i] = static_cast<char>(tolower( ret[i] ));
+        }
+        return ret;
+      }
+      
+      /******************************************************************
+      **
+      **
+      **       FUNCTION NAME : toUpper
+      **       FUNCTION TYPE : std::string
+      **
+      **       DESCRIPTION :
+      */
+      std::string toUpper( const std::string & s )
+      {
+        if ( s.empty() )
+          return s;
+      
+        string ret( s );
+        for ( string::size_type i = 0; i < ret.length(); ++i ) {
+          if ( islower( ret[i] ) )
+            ret[i] = static_cast<char>(toupper( ret[i] ));
+        }
+        return ret;
+      }
+      
+      /******************************************************************
+      **
+      **
+      **       FUNCTION NAME : dumpOn
+      **       FUNCTION TYPE : std::ostream &
+      **
+      **       DESCRIPTION :
+      */
+      std::ostream & dumpOn( std::ostream & str, const std::list<std::string> & l, const bool numbered )
+      {
+        unsigned i = 0;
+        for ( std::list<std::string>::const_iterator it = l.begin(); it != l.end(); ++it, ++i ) {
+          if ( numbered )
+            str << '[' << i << ']';
+          str << *it << endl;
+        }
+        return str;
+      }
+      
+      /******************************************************************
+      **
+      **
+      **       FUNCTION NAME : dumpOn
+      **       FUNCTION TYPE : std::ostream &
+      **
+      **       DESCRIPTION :
+      */
+      std::ostream & dumpOn( std::ostream & str, const std::vector<std::string> & l, const bool numbered )
+      {
+        for ( unsigned i = 0; i < l.size(); ++i ) {
+          if ( numbered )
+            str << '[' << i << ']';
+          str << l[i] << endl;
+        }
+        return str;
+      }
+
+///////////////////////////////////////////////////////////////////
+    }  // namespace stringutil
+///////////////////////////////////////////////////////////////////
+  } // namespace base
+} // namespace zypp
+
diff --git a/zypp/base/stringutil.h b/zypp/base/stringutil.h
new file mode 100644 (file)
index 0000000..9f79473
--- /dev/null
@@ -0,0 +1,326 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/base/stringutil.h
+ *
+ * \todo replace by Blocxx
+ *
+*/
+
+#ifndef ZYPP_BASE_STRINGUTIL_H
+#define ZYPP_BASE_STRINGUTIL_H
+
+#include <cstdio>
+#include <cstdarg>
+
+#include <iosfwd>
+#include <vector>
+#include <string>
+#include <list>
+
+/**
+ * Utility functions for std::strings. Most of them based on stringutil::form.
+ **/
+///////////////////////////////////////////////////////////////////
+namespace zypp {
+  namespace base {
+    namespace stringutil {
+//////////////////////////////////////////////////////////////////
+
+      enum Trim {
+        NO_TRIM = 0x00,
+        L_TRIM  = 0x01,
+        R_TRIM  = 0x02,
+        TRIM    = (L_TRIM|R_TRIM)
+      };
+      
+      inline std::string form( const char * format, ... )
+          __attribute__ ((format (printf, 1, 2)));
+      
+      /**
+       * Printf style building of strings via format string.
+       * <PRE>
+       * std::string ex( stringutil::form( "Example number %d", 1 ) );
+       * std::cout << ex << stringutil::form( " and number %d.", 2 ) << endl;
+       *
+       * Will print: Example number 1 and number 2.
+       * </PRE>
+       **/
+      inline std::string form( const char * format, ... ) {
+        char * buf = 0;
+        std::string val;
+      
+        va_list ap;
+        va_start( ap, format );
+      
+      #if 1
+        vasprintf( &buf, format, ap );
+        if ( buf ) {
+          val = buf;
+          free( buf );
+        }
+      #else
+        // Don't know wheter we actually nedd two va_lists, one to
+        // evaluate the buffer size needed, and one to actually fill
+        // the buffer. Maybe there's a save way to reuse a va_lists.
+        va_list ap1;
+        va_start( ap1, format );
+        buf = new char[vsnprintf( NULL, 0, format, ap ) + 1];
+        vsprintf( buf, format, ap1 );
+        val = buf;
+        delete [] buf;
+        va_end( ap1 );
+      #endif
+      
+        va_end( ap );
+        return val;
+      }
+      
+      /**
+       * Print number. Optional second argument sets the minimal string width (' ' padded).
+       * Negative values will cause the number to be left adjusted within the string. Default
+       * width is 0.
+       * <PRE>
+       * numstring(42)           -> "42"
+       * numstring(42, 4)        -> "  42"
+       * numstring(42,-4)        -> "42  "
+       * </PRE>
+       **/
+      inline std::string numstring( char n,               int w = 0 ) { return form( "%*hhd",  w, n ); }
+      inline std::string numstring( unsigned char n,      int w = 0 ) { return form( "%*hhu",  w, n ); }
+      inline std::string numstring( short n,              int w = 0 ) { return form( "%*hd",   w, n ); }
+      inline std::string numstring( unsigned short n,     int w = 0 ) { return form( "%*hu",   w, n ); }
+      inline std::string numstring( int n,                int w = 0 ) { return form( "%*d",    w, n ); }
+      inline std::string numstring( unsigned n,           int w = 0 ) { return form( "%*u",    w, n ); }
+      inline std::string numstring( long n,               int w = 0 ) { return form( "%*ld",   w, n ); }
+      inline std::string numstring( unsigned long n,      int w = 0 ) { return form( "%*lu",   w, n ); }
+      inline std::string numstring( long long n,          int w = 0 ) { return form( "%*lld",  w, n ); }
+      inline std::string numstring( unsigned long long n, int w = 0 ) { return form( "%*llu",  w, n ); }
+      
+      /**
+       * Print number as hex value with leading '0x'. Optional second argument sets the minimal
+       * string width (0 padded). Negative values will cause the number to be left adjusted
+       * within the string. Default width is 10 (4 for char).
+       * <PRE>
+       * hexstring(42)           -> "0x0000002a"
+       * hexstring(42, 4)        -> "0x2a"
+       * hexstring(42,-4)        -> "0x2a"
+       * </PRE>
+       **/
+      inline std::string hexstring( char n,               int w = 4 ) { return form( "%#0*hhx", w, n ); }
+      inline std::string hexstring( unsigned char n,      int w = 4 ) { return form( "%#0*hhx", w, n ); }
+      inline std::string hexstring( short n,              int w = 10 ){ return form( "%#0*hx",  w, n ); }
+      inline std::string hexstring( unsigned short n,     int w = 10 ){ return form( "%#0*hx",  w, n ); }
+      inline std::string hexstring( int n,                int w = 10 ){ return form( "%#0*x",   w, n ); }
+      inline std::string hexstring( unsigned n,           int w = 10 ){ return form( "%#0*x",   w, n ); }
+      inline std::string hexstring( long n,               int w = 10 ){ return form( "%#0*lx",  w, n ); }
+      inline std::string hexstring( unsigned long n,      int w = 10 ){ return form( "%#0*lx",  w, n ); }
+      inline std::string hexstring( long long n,          int w = 0 ) { return form( "%#0*llx", w, n ); }
+      inline std::string hexstring( unsigned long long n, int w = 0 ) { return form( "%#0*llx", w, n ); }
+      
+      /**
+       * Print number as octal value with leading '0'. Optional second argument sets the minimal
+       * string width (0 padded). Negative values will cause the number to be left adjusted
+       * within the string. Default width is 5 (4 for char).
+       * <PRE>
+       * octstring(42)           -> "00052"
+       * octstring(42, 4)        -> "0052"
+       * octstring(42,-4)        -> "052 "
+       * </PRE>
+       **/
+      inline std::string octstring( char n,               int w = 4 ) { return form( "%#0*hho",  w, n ); }
+      inline std::string octstring( unsigned char n,      int w = 4 ) { return form( "%#0*hho",  w, n ); }
+      inline std::string octstring( short n,              int w = 5 ) { return form( "%#0*ho",   w, n ); }
+      inline std::string octstring( unsigned short n,     int w = 5 ) { return form( "%#0*ho",   w, n ); }
+      inline std::string octstring( int n,                int w = 5 ) { return form( "%#0*o",    w, n ); }
+      inline std::string octstring( unsigned n,           int w = 5 ) { return form( "%#0*o",    w, n ); }
+      inline std::string octstring( long n,               int w = 5 ) { return form( "%#0*lo",   w, n ); }
+      inline std::string octstring( unsigned long n,      int w = 5 ) { return form( "%#0*lo",   w, n ); }
+      inline std::string octstring( long long n,          int w = 0 ) { return form( "%#0*llo",  w, n ); }
+      inline std::string octstring( unsigned long long n, int w = 0 ) { return form( "%#0*llo",  w, n ); }
+      
+      /**
+       * String to integer type determined by template arg: time_t t = strtonum<time_t>( "42" );
+       **/
+      template<typename _It>
+        inline _It strtonum( const std::string & str );
+      
+      template<>
+        inline short              strtonum( const std::string & str ) { return ::strtol  ( str.c_str(), NULL, 0 ); }
+      template<>
+        inline int                strtonum( const std::string & str ) { return ::strtol  ( str.c_str(), NULL, 0 ); }
+      template<>
+        inline long               strtonum( const std::string & str ) { return ::strtol  ( str.c_str(), NULL, 0 ); }
+      template<>
+        inline long long          strtonum( const std::string & str ) { return ::strtoll ( str.c_str(), NULL, 0 ); }
+      
+      template<>
+        inline unsigned short     strtonum( const std::string & str ) { return ::strtoul ( str.c_str(), NULL, 0 ); }
+      template<>
+        inline unsigned           strtonum( const std::string & str ) { return ::strtoul ( str.c_str(), NULL, 0 ); }
+      template<>
+        inline unsigned long      strtonum( const std::string & str ) { return ::strtoul ( str.c_str(), NULL, 0 ); }
+      template<>
+        inline unsigned long long strtonum( const std::string & str ) { return ::strtoull( str.c_str(), NULL, 0 ); }
+      
+      /**
+       * String to integer type detemined function arg: time_t t; strtonum( "42", t );
+       **/
+      template<typename _It>
+        inline _It strtonum( const std::string & str, _It & i ) { return i = strtonum<_It>( str ); }
+      
+      /** \brief read one line from a stream
+       * Return one line read from istream. Afterwards the streampos is behind the delimiting '\n'
+       * (or at EOF). The delimiting '\n' is <b>not</b> returned.
+       *
+       * If trim is true, the string returned is trimmed (surrounding whitespaces removed).
+       * <PRE>
+       * ifstream s( "somefile" );
+       *
+       * while ( s ) {
+       *   string l = getline( s );
+       *   if ( !(s.fail() || s.bad()) ) {
+       *
+       *     // l contains valid data to be consumed.
+       *     // In case it makes any difference to you:
+       *     if ( s.good() ) {
+       *       // A delimiting '\n' was read.
+       *     } else {
+       *       // s.eof() is set: There's no '\n' at the end of file.
+       *       // Note: The line returned may netvertheless be empty if trimed is true.
+       *     }
+       *   }
+       * }
+       * </PRE>
+       **/
+      extern std::string getline( std::istream & str, bool trim = false );
+      
+      /** \brief read one line from a stream
+       *
+       * like above but with allows to specify trimming direction
+       * */
+      extern std::string getline( std::istream & str, const Trim trim_r );
+      
+      /**
+       * Split line into words
+       *
+       * <b>singlesep_r = false</b>: Separator is any nonenmpty sequence of characters listed in sep_t.
+       * Leading trailing separators are ignored.
+       *
+       * <b>Example:</b> singlesep_r = false, sep_t = ":"
+       * <PRE>
+       * ""        -> words 0
+       * ":"       -> words 0
+       * "a"       -> words 1  |a|
+       * "::a"     -> words 1  |a|
+       * "::a::"   -> words 1  |a|
+       * ":a::b:c:"-> words 3  |a|b|c|
+       * </PRE>
+       *
+       * <b>singlesep_r = true</b>: Separator is any single character occuring in sep_t.
+       * Leading trailing separators are not ignored (i.e will cause an empty word).
+       *
+       * <b>Example:</b> singlesep_r = true, sep_t = ":"
+       * <PRE>
+       * ""        -> words 0
+       * ":"       -> words 2  |||
+       * "a"       -> words 1  |a|
+       * ":a"      -> words 2  ||a|
+       * "a:"      -> words 2  |a||
+       * ":a:"     -> words 3  ||a||
+       * </PRE>
+       *
+       **/
+      extern unsigned split( const std::string          line_r,
+                             std::vector<std::string> & words_r,
+                             const std::string &        sep_t       = " \t",
+                             const bool                 singlesep_r = false );
+      
+      /**
+       * Join strinngs in words_r using separator sep_r
+       **/
+      extern std::string join( const std::vector<std::string> & words_r,
+                        const std::string & sep_r = " " );
+      
+      
+      /**
+       * Split string into a list of lines using <b>any<\b> char in sep_r as line
+       * delimiter. The delimiter is stripped from the line.
+       *
+       * <PRE>
+       * splitToLines( "start\n\nend" ) -> { "start", "", "end" }
+       * </PRE>
+       **/
+      inline std::list<std::string> splitToLines( const std::string text_r, const std::string & sep_r = "\n" )
+      {
+        std::vector<std::string> lines;
+        stringutil::split( text_r, lines, "\n", true );
+        std::list<std::string> ret;
+        for ( unsigned i = 0; i < lines.size(); ++i ) {
+          ret.push_back( lines[i] );
+        }
+        return ret;
+      }
+      
+      /**
+       * Strip the first word (delimited by blank or tab) from value, and return it.
+       * Adjust value to start with the second word afterwards.
+       *
+       * If value starts with blank or tab, the <b>first word is empty</b> and value will be
+       * ltrimmed afterwards.
+       *
+       * If ltrim_first is true, value will be ltrimmed before stripping the first word. Thus
+       * first word is empty, iff value is empty or contains whitespace only.
+       *
+       * <PRE>
+       * stripFirstWord( "1st" )             ==  "1st" and value truncated to ""
+       * stripFirstWord( "1st word" )        ==  "1st" and value truncated to "word"
+       * stripFirstWord( " 1st word" )       ==  ""    and value truncated to "1st word"
+       * stripFirstWord( " 1st word", true ) ==  "1st" and value truncated to "word"
+       * </PRE>
+       **/
+      extern std::string stripFirstWord( std::string & value, const bool ltrim_first = false );
+      
+      /**
+       * Return string with leading/trailing/surrounding whitespace removed
+       **/
+      extern std::string ltrim( const std::string & s );
+      extern std::string rtrim( const std::string & s );
+      inline std::string  trim( const std::string & s, const Trim trim_r = TRIM ) {
+        switch ( trim_r ) {
+        case L_TRIM:
+          return ltrim( s );
+        case R_TRIM:
+          return rtrim( s );
+        case TRIM:
+          return ltrim( rtrim( s ) );
+        case NO_TRIM:
+          break;
+        }
+        return s;
+      }
+      
+      /**
+       * Return string converted to lower/upper case
+       **/
+      extern std::string toLower( const std::string & s );
+      extern std::string toUpper( const std::string & s );
+      
+      /**
+       * Helper for stream output
+       **/
+      extern std::ostream & dumpOn( std::ostream & str, const std::list<std::string> & l, const bool numbered = false );
+      extern std::ostream & dumpOn( std::ostream & str, const std::vector<std::string> & l, const bool numbered = false );
+      
+///////////////////////////////////////////////////////////////////
+    }  // namespace stringutil
+  } // namespace base
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+
+#endif // ZYPP_BASE_STRINGUTIL_H
diff --git a/zypp/media/Makefile.am b/zypp/media/Makefile.am
new file mode 100644 (file)
index 0000000..6d61982
--- /dev/null
@@ -0,0 +1,28 @@
+## Process this file with automake to produce Makefile.in
+## ##################################################
+
+SUBDIRS =
+
+INCLUDES = -I$(oldincludedir)/libxml2
+
+## ##################################################
+
+include_HEADERS =              \
+       MediaException.h        \
+       MediaAccess.h           \
+       MediaHandler.h          \
+       MediaCurl.h
+
+
+noinst_LTLIBRARIES =   lib@PACKAGE@_media.la
+
+## ##################################################
+
+lib@PACKAGE@_media_la_SOURCES = \
+       MediaAccess.cc          \
+       MediaHandler.cc         \
+       MediaCurl.cc
+
+lib@PACKAGE@_media_la_LIBADD = -lcurl
+
+## ##################################################