OSDN Git Service

Implement installer function; use per-package file manifests.
authorKeith Marshall <keithmarshall@users.sourceforge.net>
Sun, 4 Apr 2010 15:25:36 +0000 (15:25 +0000)
committerKeith Marshall <keithmarshall@users.sourceforge.net>
Sun, 4 Apr 2010 15:25:36 +0000 (15:25 +0000)
ChangeLog
Makefile.in
src/pkgexec.cpp
src/pkginst.cpp [new file with mode: 0644]
src/pkgkeys.c
src/pkgkeys.h
src/pkgproc.h
src/tarinst.cpp [new file with mode: 0644]
src/tarproc.cpp

index f1367a6..2fbb7f9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,56 @@
+2010-04-04  Keith Marshall  <keithmarshall@users.sourceforge.net>
+
+       Implement installer function; use per-package file manifests.
+
+       * src/pkgproc.h (pkgManifest): New class; declare it.
+       (pkgArchiveProcessor::origin): New protected member variable.
+       (pkgArchiveProcessor::sysroot_len): New protected member variable.
+       (pkgArchiveProcessor::installed): Type changed to `pkgManifest *'.
+       (pkgTarArchiveInstaller::Process): Declare specialisation hook.
+       (pkgTarArchiveInstaller::UpdateInstallationManifest): Disused member
+       function; remove redundant declaration from class definition.
+       (pkgInstall): New generalised installation function; declare it.
+       (pkgRegister): New function; declare it.
+
+       * src/pkginst.cpp: New file.
+       (pkgManifest, pkgInstall, pkgRegister): Implement them.
+
+       * src/pkgexec.cpp (pkgActionItem::Execute): Use `pkgInstall' in place
+       of stub formerly provided here.
+
+       * src/pkgkeys.h (manifest_key, reference_key): New keys; declare them.
+       * src/pkgkeys.c (manifest_key, reference_key): Implement them.
+
+       * src/tarproc.cpp: Code refactored.
+       (pkgTarArchiveProcessor::sysroot_len): Initialise it.
+       (pkgTarArchiveProcessor::origin): Initialise it.
+       (pkgTarArchiveProcessor::installed): Initialise; associate it with a
+       pkgManifest class entity, and populate this as appropriate.
+       (pkgTarArchiveProcessor::~pkgTarArchiveProcessor): Add explicit clean
+       up of linked `pkgManifest *' entity referenced by `installed'.
+       (pkgTarArchiveProcessor::Process) [TAR_ENTITY_TYPE_DIRECTORY]: Add
+       braces to demarcate localised inner scope of string scan for removal
+       of trailing directory name separators; (thanks to Cesar Strauss for
+       reporting invalid local pointer declaration error in GCC >= 4.4).
+       (pkgTarArchiveInstaller::UpdateInstallationManifest): Deleted.
+       (pkgTarArchiveInstaller::pkgTarArchiveInstaller): Factor out.
+       (pkgTarArchiveInstaller::ProcessDirectory): Likewise.
+       (pkgTarArchiveInstaller::ProcessDataStream): Likewise.
+       (commit_saved_entity): Likewise.
+
+       * src/tarinst.cpp: New file; it implements...
+       (pkgTarArchiveInstaller::Process): ...this new specialisation
+       of the original pkgTarArchiveProcessor::Process method...
+       (pkgTarArchiveInstaller::pkgTarArchiveInstaller): ...and this
+       refactored code, abstracted from src/tarproc.cpp and modified to
+       use the pkgManifest class for installation tracking.
+       (pkgTarArchiveInstaller::ProcessDirectory): Likewise.
+       (pkgTarArchiveInstaller::ProcessDataStream): Likewise.
+       (commit_saved_entity): Relocated from src/tarproc.cpp.
+
+       * Makefile.in (CORE_DLL_OBJECTS): Add...
+       (pkginst.OBJEXT, tarinst.OBJEXT): ...these new object files.
+
 2010-03-31  Keith Marshall  <keithmarshall@users.sourceforge.net>
 
        Guard against NULL pointer dereferences in tinyxml code.
index bcc8d89..8d108a1 100644 (file)
@@ -56,10 +56,10 @@ CORE_DLL_OBJECTS = climain.$(OBJEXT) \
    pkgbind.$(OBJEXT) pkginet.$(OBJEXT) pkgstrm.$(OBJEXT) pkgname.$(OBJEXT) \
    pkgexec.$(OBJEXT) pkgfind.$(OBJEXT) pkginfo.$(OBJEXT) pkgspec.$(OBJEXT) \
    sysroot.$(OBJEXT) pkghash.$(OBJEXT) pkgkeys.$(OBJEXT) pkgdeps.$(OBJEXT) \
-   mkpath.$(OBJEXT)  tarproc.$(OBJEXT) xmlfile.$(OBJEXT) keyword.$(OBJEXT) \
+   mkpath.$(OBJEXT)  pkginst.$(OBJEXT) tarinst.$(OBJEXT) tarproc.$(OBJEXT) \
+   xmlfile.$(OBJEXT) keyword.$(OBJEXT) vercmp.$(OBJEXT)  dmh.$(OBJEXT) \
    tinyxml.$(OBJEXT) tinyxmlparser.$(OBJEXT) \
-   tinystr.$(OBJEXT) tinyxmlerror.$(OBJEXT) \
-   vercmp.$(OBJEXT)  dmh.$(OBJEXT)
+   tinystr.$(OBJEXT) tinyxmlerror.$(OBJEXT)
 
 all: pkginfo$(EXEEXT) mingw-get$(EXEEXT) mingw-get-0.dll
 
index cb8c212..7c048da 100644 (file)
@@ -380,30 +380,11 @@ void pkgActionItem::Execute()
       }
 
       if( (current->flags & ACTION_INSTALL) == ACTION_INSTALL )
-      {
-       /* The selected package has been marked for installation, either explicitly,
+       /*
+        * The selected package has been marked for installation, either explicitly,
         * or implicitly to complete a package upgrade.
-        *
-        * FIXME: Once more, this is a stub, to be extended to provide the working
-        * installer implementation.
         */
-       //dmh_printf( " installing %s\n", current->Selection()->GetPropVal( tarname_key, "<unknown>" ));
-       if( current->Selection( to_remove ) == NULL )
-       {
-         pkgXmlNode *pkg = current->Selection();
-         if( ! match_if_explicit( pkg->ArchiveName(), value_none ) )
-         {
-           pkgTarArchiveInstaller package( pkg );
-           if( package.IsOk() )
-             package.Process();
-         }
-       }
-       else
-         dmh_notify( DMH_ERROR,
-             "package %s is already installed\n",
-             current->Selection()->GetPropVal( tarname_key, "<unknown>" )
-           );
-      }
+       pkgInstall( current );
 
       /* Proceed to next package with scheduled actions.
        */
diff --git a/src/pkginst.cpp b/src/pkginst.cpp
new file mode 100644 (file)
index 0000000..488bd49
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * pkginst.cpp
+ *
+ * $Id$
+ *
+ * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+ * Copyright (C) 2010, MinGW Project
+ *
+ *
+ * Implementation of the primary package installation and package
+ * manifest recording methods.
+ *
+ *
+ * This is free software.  Permission is granted to copy, modify and
+ * redistribute this software, under the provisions of the GNU General
+ * Public License, Version 3, (or, at your option, any later version),
+ * as published by the Free Software Foundation; see the file COPYING
+ * for licensing details.
+ *
+ * Note, in particular, that this software is provided "as is", in the
+ * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
+ * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
+ * PARTICULAR PURPOSE.  Under no circumstances will the author, or the
+ * MinGW Project, accept liability for any damages, however caused,
+ * arising from the use of this software.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dmh.h"
+
+#include "pkginfo.h"
+#include "pkgkeys.h"
+#include "pkgproc.h"
+
+EXTERN_C const char *hashed_name( int, const char *, const char * );
+
+pkgManifest::pkgManifest( const char *tag, const char *tarname )
+{
+  /* Construct an in-memory image for processing a package manifest.
+   *
+   * We begin by initialising this pair of reference pointers,
+   * assuming that this manifest may become invalid...
+   */
+  manifest = NULL;
+  inventory = NULL;
+
+  /* Then we check that a package tarname has been provided...
+   */
+  if( tarname != NULL )
+  {
+    /* ...in which case, we proceed to set up the reference data...
+     */
+    int retry = 0;
+    while( retry < 16 )
+    {
+      /* Generate a hashed signature for the package manifest
+       * record, and derive an associated database file path name
+       * from it.  Note that this hash is returned in 'malloc'ed
+       * memory, which we must later free.  Also note that there
+       * are eight possible hashes, to mitigate hash collision,
+       * each of which is denoted by retry modulo eight; we make
+       * an initial pass through the possible hashes, looking for
+       * an existing installation map for this sysroot, loading
+       * it immediately if we find it.  Otherwise, we continue
+       * with a second cycle, (retry = 8..15), looking for the
+       * first generated hash with no associated file; we then
+       * use this to create a new installation record file.
+       */
+      pkgXmlDocument *chkfile;
+      const char *signame = hashed_name( retry++, manifest_key, tarname );
+      const char *sigfile = xmlfile( signame, NULL );
+
+      /* Check for an existing file associated with the hash value...
+       */
+      if( (chkfile = new pkgXmlDocument( sigfile ))->IsOk() )
+      {
+       /* ...such a file does exist, but we must still check
+        * that it relates to the specified package...
+        */
+       if( retry < 9 )
+       {
+         /* ...however, we only perform this check during the
+          * first pass through the possible hashes; (second time
+          * through, we are only interested in a hash which does
+          * not have an associated file; note that the first pass
+          * through is for retry = 0..7, but by the time we get
+          * to here we have already incremented 7 to become 8,
+          * hence the check for retry < 9).
+          */
+         pkgXmlNode *root, *rel;
+         const char *pkg_id, *pkg_tarname;
+         if( ((root = chkfile->GetRoot()) != NULL)
+         &&  ((root->IsElementOfType( tag )))
+         &&  ((pkg_id = root->GetPropVal( id_key, NULL )) != NULL)
+         &&  ((strcmp( pkg_id, signame ) == 0))
+         &&  ((rel = root->FindFirstAssociate( release_key )) != NULL)
+         &&  ((pkg_tarname = rel->GetPropVal( tarname_key, NULL )) != NULL)
+         &&  ((pkg_strcmp( pkg_tarname, tarname )))  )
+         {
+           /* This is the manifest file we require...
+            * assign it for return, and force an early exit
+            * from the retry loop.
+            */
+           manifest = chkfile;
+           retry = 16;
+         }
+       }
+      }
+
+      /* Once more noting the prior increment of retry, such
+       * that it has now become 8 for the hash generation with
+       * retry = 7...
+       */
+      else if( retry > 8 )
+      {
+       /* ...we have exhausted all possible hash references,
+        * finding no existing manifest for the specified package;
+        * immediately assign the current (unused) manifest name,
+        * and initialise its root element...
+        */
+       pkgXmlNode *root = new pkgXmlNode( tag );
+       (manifest = chkfile)->AddDeclaration( "1.0", "UTF-8", value_yes );
+       root->SetAttribute( id_key, signame );
+       manifest->SetRoot( root );
+
+       /* ...a container, to be filled later, for recording the
+        * data associated with the specific release of the package
+        * to which this manifest will refer...
+        */
+       pkgXmlNode *ref = new pkgXmlNode( release_key );
+       ref->SetAttribute( tarname_key, tarname );
+       root->AddChild( ref );
+
+       /* ...a further container, in which to record the sysroot
+        * associations for installed instances of this package...
+        */
+       ref = new pkgXmlNode( reference_key );
+       root->AddChild( ref );
+
+       /* ...and one in which to accumulate the content manifest.
+        */
+       inventory = new pkgXmlNode( manifest_key );
+       root->AddChild( inventory );
+
+       /* Finally, having constructed a skeletal manifest,
+        * force an immediate exit from the retry loop...
+        */
+       retry = 16;
+      }
+
+      /* Before abandoning our references to the current hash
+       * signature, and the path name for the associated XML file,
+       * free the memory allocated for them.
+       */
+      free( (void *)(sigfile) );
+      free( (void *)(signame) );
+
+      /* If we have not yet exhausted all possible hashed file names...
+       */
+      if( retry < 16 )
+       /*
+        * ...free the heap memory allocated for the current (unsuitable)
+        * association, so making its "chkfile" reference pointer available
+        * for the next trial, without incurring a memory leak.
+        */
+       delete chkfile;
+    }
+  }
+}
+
+void pkgManifest::BindSysRoot( pkgXmlNode *sysroot, const char *reference_tag )
+{
+  /* Identify the package associated with the current manifest as
+   * having been installed within the specified sysroot, by tagging
+   * with a manifest reference to the sysroot identification key.
+   */
+  const char *id;
+  if( ((id = sysroot->GetPropVal( id_key, NULL )) != NULL) && (*id != '\0') )
+  {
+    /* The specified sysroot has a non-NULL, non-blank key;
+     * check for a prior reference to the same key, within the
+     * "references" section of the manifest...
+     */
+    pkgXmlNode *map, *ref;
+    if(  ((ref = manifest->GetRoot()) != NULL)
+    &&   ((ref->IsElementOfType( reference_tag )))
+    &&   ((map = ref->FindFirstAssociate( reference_key )) == NULL)  )
+    {
+      /* This manifest doesn't yet have a "references" section;
+       * create and add one now...
+       */
+      map = new pkgXmlNode( reference_key );
+      ref->AddChild( map );
+    }
+
+    /* Examine any existing sysroot references within this manifest...
+     */
+    ref = map->FindFirstAssociate( sysroot_key );
+    while( (ref != NULL) && (strcmp( id, ref->GetPropVal( id_key, id )) != 0) )
+      /*
+       * ...progressing to the next reference, if any, until...
+       */
+      ref = ref->FindNextAssociate( sysroot_key );
+
+    /* ...we either matched the required sysroot key, or we
+     * ran out of existing references, without finding a match.
+     */
+    if( ref == NULL )
+    {
+      /* When no existing reference was found, add one.
+       */
+      ref = new pkgXmlNode( sysroot_key );
+      ref->SetAttribute( id_key, id );
+      map->AddChild( ref );
+    }
+  }
+}
+
+void pkgManifest::AddEntry( const char *key, const char *pathname )
+{
+  /* Method invoked by package installers, to add file or directory
+   * entries to the tracked inventory of package content.
+   *
+   * Tracking is enabled only if the manifest structure is valid,
+   * AND an inventory table has been allocated...
+   */
+  if( (this != NULL) && (inventory != NULL) )
+  {
+    /* ...in which case we allocate a new tracking record, with
+     * "dir" or "file" reference key as appropriate, fill it out
+     * with the associated path name attribute, and insert it in
+     * the inventory table.
+     */
+    pkgXmlNode *entry = new pkgXmlNode( key );
+    entry->SetAttribute( pathname_key, pathname );
+    inventory->AddChild( entry );
+  }
+}
+
+pkgManifest::~pkgManifest()
+{
+  /* Destructor for package manifest images; it releases
+   * the memory used while processing a manifest, after first
+   * committing the image to disk storage, or deleting such
+   * a disk image as appropriate.
+   */
+  pkgXmlNode *ref;
+  const char *sigfile;
+
+  /* First confirm that an identification signature has been
+   * assigned for this manifest...
+   */
+  if(  ((manifest != NULL)) && ((ref = manifest->GetRoot()) != NULL)
+  &&   ((sigfile = ref->GetPropVal( id_key, NULL )) != NULL)          )
+  {
+    /* ...and map this to a file system reference path name.
+     */
+    sigfile = xmlfile( sigfile );
+
+    /* Check if any current installation, as identified by
+     * its sysroot key, refers to this manifest...
+     */
+    if(  ((ref = ref->FindFirstAssociate( reference_key )) != NULL)
+    &&   ((ref = ref->FindFirstAssociate( sysroot_key )) != NULL)    )
+      /*
+       * ...and if so, commit this manifest to disk...
+       */
+      manifest->Save( sigfile );
+
+    else
+      /* ...otherwise, this manifest is defunct, so
+       * delete any current disk copy.
+       */
+      unlink( sigfile );
+
+    /* Release the memory used to identify the path name for
+     * the on-disk copy of this manifest...
+     */
+    free( (void *)(sigfile) );
+  }
+
+  /* ...and finally, expunge its in-memory image.
+   */
+  delete manifest;
+}
+
+static
+void record_dependencies( pkgXmlNode *origin, pkgXmlNode *list )
+{
+  /* Helper function to record dependency call-outs for the package
+   * which is specified by the XML descriptor reference at "origin",
+   * (which should nominally represent a "release" specification for
+   * the package); the call-out references are collected in the XML
+   * container referenced by "list".
+   */
+  if( (origin != NULL) && (list != NULL) )
+  {
+    /* Assume "origin" and "list" represent appropriate XML objects...
+     */
+    if( ! origin->IsElementOfType( package_key ) )
+      /*
+       * ...and walk back through the "origin" tree, until we locate
+       * the top level node in the "package" specification.
+       */
+      record_dependencies( origin->GetParent(), list );
+
+    /* As we unwind this recursive walk back search, we copy all
+     * "requires" elements, at each intervening level, from the top
+     * "package" node until we return to the original "release"...
+     */
+    pkgXmlNode *dep = origin->FindFirstAssociate( requires_key );
+    while( dep != NULL )
+    {
+      /* ...each as a simple clone within the "list" container,
+       * (and all at a single level within the "list")...
+       */
+      list->AddChild( dep->Clone() );
+
+      /* ...repeating for all specified "requires" elements
+       * at each level.
+       */
+      dep = dep->FindNextAssociate( requires_key );
+    }
+  }
+}
+
+EXTERN_C void pkgRegister
+( pkgXmlNode *sysroot, pkgXmlNode *origin, const char *tarname, const char *pkgfile )
+{
+  /* Search the installation records for the current sysroot...
+   */
+  const char *pkg_tarname = NULL;
+  pkgXmlNode *ref = sysroot->FindFirstAssociate( installed_key );
+  while(  (ref != NULL)
+    &&   ((pkg_tarname = ref->GetPropVal( tarname_key, NULL )) != NULL)
+    && !  (pkg_strcmp( tarname, pkg_tarname ))  )
+      /*
+       * ...continuing until we either run out of installation records,
+       * or we find an already existing reference to this package.
+       */
+      ref = ref->FindNextAssociate( installed_key );
+
+  /* When we didn't find an appropriate existing installation record,
+   * we instantiate a new one...
+   */
+  if( (ref == NULL) && ((ref = new pkgXmlNode( installed_key )) != NULL) )
+  {
+    /* Fill out its "tarname" attribution...
+     */
+    ref->SetAttribute( tarname_key, tarname );
+    if( pkgfile != tarname )
+    {
+      /* When the real package tarball name isn't identically
+       * the same as the canonical name, then we record the real
+       * file name too.
+       */
+      pkgXmlNode *dl = new pkgXmlNode( download_key );
+      dl->SetAttribute( tarname_key, pkgfile );
+      ref->AddChild( dl );
+    }
+
+    /* Record dependency call-outs for the installed package.
+     */
+    record_dependencies( origin, ref );
+
+    /* Set the 'modified' flag for, and attach the installation
+     * record to, the relevant sysroot record.
+     */
+    sysroot->SetAttribute( modified_key, value_yes );
+    sysroot->AddChild( ref );
+  }
+}
+
+EXTERN_C void pkgInstall( pkgActionItem *current )
+{
+  /* Common handler for all package installation tasks...
+   */
+  pkgXmlNode *pkg;
+  if( (pkg = current->Selection()) != NULL )
+  {
+    /* The current action item has a valid package association...
+     */
+    if( current->Selection( to_remove ) == NULL )
+    {
+      /* The selected package has either not yet been installed,
+       * or any prior installation has been removed in preparation
+       * for re-installation or upgrade.
+       */
+      const char *pkgfile, *tarname;
+      if(  match_if_explicit( pkgfile = pkg->ArchiveName(), value_none )
+      && ((tarname = pkg->GetPropVal( tarname_key, NULL )) != NULL)       )
+      {
+       /* In this case, the selected package has no associated archive,
+        * (i.e. it is a "virtual" package); provided we can identify an
+        * associated "sysroot"...
+        */
+       pkgXmlNode *sysroot;
+       pkgSpecs lookup( tarname );
+        if( (sysroot = pkg->GetSysRoot( lookup.GetSubSystemName() )) != NULL )
+         /*
+          * ...the installation process becomes a simple matter of
+          * recording the state of this virtual package as "installed",
+          * in the sysroot manifest, and itemising its prerequisites.
+          */
+         pkgRegister( sysroot, pkg, tarname, pkgfile );
+      }
+      else
+      {
+       /* Here we have a "real" (physical) package to install;
+        * for the time being, we assume it is packaged in our
+        * standard "tar" archive format.
+        */
+       pkgTarArchiveInstaller install( pkg );
+       if( install.IsOk() )
+         install.Process();
+      }
+    }
+    else
+      /* There is a prior installation of the selected package, which
+       * prevents us from proceeding; diagnose and otherwise ignore...
+       */
+      dmh_notify( DMH_ERROR,
+         "package %s is already installed\n",
+         current->Selection()->GetPropVal( tarname_key, "<unknown>" )
+       );
+  }
+}
+
+/* $RCSfile$: end of file */
index 02f63a5..832a927 100644 (file)
@@ -42,6 +42,7 @@ const char *installed_key         =   "installed";
 const char *issue_key              =   "issue";
 const char *le_key                 =   "le";
 const char *lt_key                 =   "lt";
+const char *manifest_key           =   "manifest";
 const char *mirror_key             =   "mirror";
 const char *modified_key           =   "modified";
 const char *name_key               =   "name";
@@ -50,6 +51,7 @@ const char *package_collection_key  = "package-collection";
 const char *package_list_key       =   "package-list";
 const char *pathname_key           =   "path";
 const char *profile_key                    =   "profile";
+const char *reference_key          =   "references";
 const char *release_key                    =   "release";
 const char *repository_key         =   "repository";
 const char *requires_key           =   "requires";
index a2a7510..2a6671f 100644 (file)
@@ -53,6 +53,7 @@ EXTERN_C_DECL const char *installed_key;
 EXTERN_C_DECL const char *issue_key;
 EXTERN_C_DECL const char *le_key;
 EXTERN_C_DECL const char *lt_key;
+EXTERN_C_DECL const char *manifest_key;
 EXTERN_C_DECL const char *mirror_key;
 EXTERN_C_DECL const char *modified_key;
 EXTERN_C_DECL const char *name_key;
@@ -61,6 +62,7 @@ EXTERN_C_DECL const char *package_collection_key;
 EXTERN_C_DECL const char *package_list_key;
 EXTERN_C_DECL const char *pathname_key;
 EXTERN_C_DECL const char *profile_key;
+EXTERN_C_DECL const char *reference_key;
 EXTERN_C_DECL const char *release_key;
 EXTERN_C_DECL const char *repository_key;
 EXTERN_C_DECL const char *requires_key;
index bdbe33d..ce23736 100644 (file)
 #include "pkgbase.h"
 #include "pkgstrm.h"
 
+EXTERN_C void pkgInstall( pkgActionItem* );
+EXTERN_C void pkgRegister( pkgXmlNode*, pkgXmlNode*, const char*, const char* );
+
+class pkgManifest
+{
+  /* A wrapper around the XML document class, with specialised methods
+   * for management of the package installation manifest.
+   */
+  public:
+    pkgManifest( const char*, const char* );
+    ~pkgManifest();
+
+    void AddEntry( const char*, const char* );
+    void BindSysRoot( pkgXmlNode*, const char* );
+    void DetachSysRoot( pkgXmlNode* );
+
+  private:
+    pkgXmlDocument *manifest;
+    pkgXmlNode     *inventory;
+};
+
 class pkgArchiveProcessor
 {
   /* A minimal generic abstract base class, from which we derive
@@ -45,17 +66,20 @@ class pkgArchiveProcessor
     virtual int Process() = 0;
 
   protected:
+    int sysroot_len;
+
     /* Pointers to the sysroot management records and installation
      * path template for a managed package; note that 'tarname' does
      * not explicitly refer only to tar archives; it is simply the
      * canonical name of the archive file, as recorded in the XML
      * 'tarname' property of the package identifier record.
      */
-    pkgXmlNode *sysroot;
-    const char *sysroot_path;
-    pkgXmlNode *installed;
-    const char *tarname;
-    const char *pkgfile;
+    pkgXmlNode  *origin;
+    pkgXmlNode  *sysroot;
+    const char  *sysroot_path;
+    pkgManifest *installed;
+    const char  *tarname;
+    const char  *pkgfile;
 };
 
 /* Our standard package format specifies the use of tar archives;
@@ -156,12 +180,13 @@ class pkgTarArchiveInstaller : public pkgTarArchiveProcessor
     pkgTarArchiveInstaller( pkgXmlNode* );
     virtual ~pkgTarArchiveInstaller(){}
 
+    virtual int Process();
+
   private:
     /* Specialised implementations of the archive processing methods...
      */
     virtual int ProcessDirectory( const char* );
     virtual int ProcessDataStream( const char* );
-    virtual void UpdateInstallationManifest( const char*, const char* );
 };
 
 class pkgTarArchiveUninstaller : public pkgTarArchiveProcessor
diff --git a/src/tarinst.cpp b/src/tarinst.cpp
new file mode 100644 (file)
index 0000000..c09d5d1
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * tarinst.cpp
+ *
+ * $Id$
+ *
+ * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+ * Copyright (C) 2009, 2010, MinGW Project
+ *
+ *
+ * Implementation of package installer methods for installation of
+ * packages which are distributed in the form of tar archives.
+ *
+ *
+ * This is free software.  Permission is granted to copy, modify and
+ * redistribute this software, under the provisions of the GNU General
+ * Public License, Version 3, (or, at your option, any later version),
+ * as published by the Free Software Foundation; see the file COPYING
+ * for licensing details.
+ *
+ * Note, in particular, that this software is provided "as is", in the
+ * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
+ * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
+ * PARTICULAR PURPOSE.  Under no circumstances will the author, or the
+ * MinGW Project, accept liability for any damages, however caused,
+ * arising from the use of this software.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <utime.h>
+
+#include "dmh.h"
+
+//#include "pkginfo.h"
+#include "pkgkeys.h"
+#include "pkgproc.h"
+
+static int commit_saved_entity( const char *pathname, time_t mtime )
+{
+  /* Helper to set the access and modification times for a file,
+   * after extraction from an archive, to match the specified "mtime";
+   * (typically "mtime" is as recorded within the archive).
+   */
+  struct utimbuf timestamp;
+
+  timestamp.actime = timestamp.modtime = mtime;
+  return utime( pathname, &timestamp );
+}
+
+pkgTarArchiveInstaller::
+pkgTarArchiveInstaller( pkgXmlNode *pkg ):pkgTarArchiveProcessor( pkg )
+{
+  /* Constructor: having successfully set up the pkgTarArchiveProcessor
+   * base class, we attach a pkgManifest to track the installation.
+   */
+  if( (tarname != NULL) && (sysroot != NULL) && stream->IsReady() )
+    installed = new pkgManifest( package_key, tarname );
+}
+
+int pkgTarArchiveInstaller::Process()
+{
+  /* Specialisation of the base class Process() method.
+   */
+  int status;
+  /* First, process the archive as for the base class...
+   */
+  if( (status = pkgTarArchiveProcessor::Process()) == 0 )
+  {
+    /* ...then, on successful completion...
+     *
+     * Update the package installation manifest, to record
+     * the installation in the current sysroot...
+     */
+    installed->BindSysRoot( sysroot, package_key );
+    pkgRegister( sysroot, origin, tarname, pkgfile );
+  }
+  return status;
+}
+
+int pkgTarArchiveInstaller::ProcessDirectory( const char *pathname )
+{
+  /* Create the directory infrastructure required to support
+   * a specific package installation.
+   */
+#if DEBUGLEVEL < 5
+  int status;
+
+  if( (status = mkdir_recursive( pathname, 0755 )) == 0 )
+    /*
+     * Either the specified directory already exists,
+     * or we just successfully created it; attach a reference
+     * in the installation manifest for the current package.
+     */
+    installed->AddEntry( dirname_key, pathname + sysroot_len );
+
+  else
+    /* A required subdirectory could not be created;
+     * diagnose this failure.
+     */
+    dmh_notify( DMH_ERROR, "cannot create directory `%s'\n", pathname );
+
+#else
+  /* Debugging stub...
+   *
+   * FIXME:maybe adapt for 'dry-run' or 'verbose' use.
+   */
+  int status = 0;
+
+  dmh_printf(
+      "FIXME:ProcessDirectory<stub>:not executing: mkdir -p %s\n",
+       pathname
+    );
+# if DEBUGLEVEL > 8
+  installed->AddEntry( dirname_key, pathname + sysroot_len );
+# endif
+#endif
+  return status;
+}
+
+int pkgTarArchiveInstaller::ProcessDataStream( const char *pathname )
+{
+  /* Extract file data from the archive, and copy it to the
+   * associated target file stream, if any.
+   */
+#if DEBUGLEVEL < 5
+  int fd = set_output_stream( pathname, octval( header.field.mode ) );
+  int status = ProcessEntityData( fd );
+  if( fd >= 0 )
+  {
+    /* File stream was written; close it...
+     */
+    close( fd );
+    if( status == 0 )
+    {
+      /* ...and on successful completion, commit it and
+       * record it in the installation database.
+       */
+      commit_saved_entity( pathname, octval( header.field.mtime ) );
+      installed->AddEntry( filename_key, pathname + sysroot_len );
+    }
+
+    else
+    {
+      /* The target file was not successfully and completely
+       * written; discard it, and diagnose failure.
+       */
+      unlink( pathname );
+      dmh_notify( DMH_ERROR, "%s: extraction failed\n", pathname );
+    }
+  }
+  return status;
+
+#else
+  /* Debugging stub...
+   *
+   * FIXME:maybe adapt for 'dry-run' or 'verbose' use.
+   */
+  dmh_printf(
+      "FIXME:ProcessDataStream<stub>:not extracting: %s\n",
+      pathname
+    );
+# if DEBUGLEVEL > 8
+  installed->AddEntry( filename_key, pathname + sysroot_len );
+# endif
+  return ProcessEntityData( -1 );
+#endif
+}
+
+/* $RCSfile$: end of file */
index d3d093e..5e8fd54 100644 (file)
@@ -52,16 +52,19 @@ pkgTarArchiveProcessor::pkgTarArchiveProcessor( pkgXmlNode *pkg )
    *
    * First, we anticipate an invalid initialisation state...
    */
+  sysroot_len = 0;
+
   sysroot = NULL;
   sysroot_path = NULL;
+  installed = NULL;
   stream = NULL;
 
   /* The 'pkg' XML database entry must be non-NULL, must
    * represent a package release, and must specify a canonical
    * tarname to identify the package...
    */
-  if(  (pkg != NULL) && pkg->IsElementOfType( release_key )
-  &&  ((tarname = pkg->GetPropVal( tarname_key, NULL )) != NULL)  )
+  if( ((origin = pkg) != NULL) && pkg->IsElementOfType( release_key )
+  &&  ((tarname = pkg->GetPropVal( tarname_key, NULL )) != NULL)       )
   {
     /* When these pre-conditions are satisfied, we may proceed
      * to identify and locate the sysroot record with which this
@@ -82,7 +85,7 @@ pkgTarArchiveProcessor::pkgTarArchiveProcessor( pkgXmlNode *pkg )
         */
        const char *template_format = "%F%%/M/%%F";
        char template_text[mkpath( NULL, template_format, prefix, NULL )];
-       mkpath( template_text, template_format, prefix, NULL );
+       sysroot_len = mkpath( template_text, template_format, prefix, NULL ) - 6;
        sysroot_path = strdup( template_text );
       }
     }
@@ -104,11 +107,13 @@ pkgTarArchiveProcessor::pkgTarArchiveProcessor( pkgXmlNode *pkg )
 
 pkgTarArchiveProcessor::~pkgTarArchiveProcessor()
 {
-  /* Destructor must release the heap memory allocated in the
-   * constructor, (by strdup), clean up the decompression filter
-   * state, and close the archive data stream.
+  /* Destructor must release the heap memory allocated in
+   * the constructor, (by strdup and pkgManifest), clean up
+   * the decompression filter state, and close the archive
+   * data stream.
    */
   free( (void *)(sysroot_path) );
+  delete installed;
   delete stream;
 }
 
@@ -237,16 +242,17 @@ int pkgTarArchiveProcessor::Process()
         * e.g. we may need to create a directory, or even a sequence
         * of directories, to establish a location within the sysroot
         * hierarchy...
-        *
-        * Note: Microsoft's implementation of stat() appears to choke
-        * on directory path names with trailing slashes; thus, before
-        * we invoke the directory processing routine, (which may need
-        * to call stat(), to check if the specified directory already
-        * exists), we remove any such trailing slashes.
         */
-        char *p = pathname + sizeof( pathname ) - 1;
-       while( (p > pathname) && ((*--p == '/') || (*p == '\\')) )
-         *p = '\0';
+        { /* Note: Microsoft's implementation of stat() appears to choke
+           * on directory path names with trailing slashes; thus, before
+           * we invoke the directory processing routine, (which may need
+           * to call stat(), to check if the specified directory already
+           * exists), we remove any such trailing slashes.
+           */
+          char *p = pathname + sizeof( pathname ) - 1;
+          while( (p > pathname) && ((*--p == '/') || (*p == '\\')) )
+            *p = '\0';
+        }
 
        /* We are now ready to process the directory path name entry...
         */
@@ -373,154 +379,4 @@ int pkgTarArchiveProcessor::ProcessEntityData( int fd )
   return status;
 }
 
-/* Here, we implement the methods for installing software from
- * packages which are distributed in the form of tar archives.
- *
- */
-#include <utime.h>
-
-static int commit_saved_entity( const char *pathname, time_t mtime )
-{
-  /* Helper to set the access and modification times for a file,
-   * after extraction from an archive, to match the specified "mtime";
-   * (typically "mtime" is as recorded within the archive).
-   */
-  struct utimbuf timestamp;
-
-  timestamp.actime = timestamp.modtime = mtime;
-  return utime( pathname, &timestamp );
-}
-
-pkgTarArchiveInstaller::
-pkgTarArchiveInstaller( pkgXmlNode *pkg ):pkgTarArchiveProcessor( pkg )
-{
-  /* Constructor: having set up the pkgTarArchiveProcessor base class,
-   * we add a package installation record to the sysroot entry in the
-   * XML database, and mark that sysroot entry as 'modified'.
-   */
-  if( (tarname != NULL) && (sysroot != NULL) && stream->IsReady() )
-  {
-    /* The installation record must identify, as a minimum,
-     * the canonical name of the package being installed.
-     */
-    installed = new pkgXmlNode( installed_key );
-    installed->SetAttribute( tarname_key, tarname );
-    if( pkgfile != tarname )
-    {
-      /* If the real package tarball name isn't identically
-       * the same as the canonical name, we record the real
-       * file name too.
-       */
-      pkgXmlNode *download = new pkgXmlNode( download_key );
-      download->SetAttribute( tarname_key, pkgfile );
-      installed->AddChild( download );
-    }
-
-    /* Set the 'modified' flag for, and attach the installation
-     * record to, the relevant sysroot record.
-     *
-     * FIXME: We should defer this until AFTER the archive has
-     * been successfully processed, (in the destructor, perhaps?),
-     * cleaning up, and not updating the installation manifest,
-     * in the event of an archive processing failure.
-     */
-    sysroot->SetAttribute( modified_key, yes_value );
-    sysroot->AddChild( installed );
-  }
-}
-
-int pkgTarArchiveInstaller::ProcessDirectory( const char *pathname )
-{
-  /* Create the directory infrastructure required to support
-   * a specific package installation.
-   */
-#if DEBUGLEVEL < 5
-  int status;
-
-  if( (status = mkdir_recursive( pathname, 0755 )) == 0 )
-    /*
-     * Either the specified directory already exists,
-     * or we just successfully created it; attach a reference
-     * in the installation manifest for the current package.
-     */
-    UpdateInstallationManifest( dirname_key, pathname );
-
-  else
-    /* A required subdirectory could not be created;
-     * diagnose this failure.
-     */
-    dmh_notify( DMH_ERROR, "cannot create directory `%s'\n", pathname );
-
-#else
-  /* Debugging stub...
-   *
-   * FIXME:maybe adapt for 'dry-run' or 'verbose' use.
-   */
-  int status = 0;
-
-  dmh_printf(
-      "FIXME:ProcessDirectory<stub>:not executing: mkdir -p %s\n",
-       pathname
-    );
-#endif
-  return status;
-}
-
-int pkgTarArchiveInstaller::ProcessDataStream( const char *pathname )
-{
-  /* Extract file data from the archive, and copy it to the
-   * associated target file stream, if any.
-   */
-#if DEBUGLEVEL < 5
-  int fd = set_output_stream( pathname, octval( header.field.mode ) );
-  int status = ProcessEntityData( fd );
-  if( fd >= 0 )
-  {
-    /* File stream was written; close it...
-     */
-    close( fd );
-    if( status == 0 )
-    {
-      /* ...and on successful completion, commit it and
-       * record it in the installation database.
-       */
-      commit_saved_entity( pathname, octval( header.field.mtime ) );
-      UpdateInstallationManifest( filename_key, pathname );
-    }
-
-    else
-    {
-      /* The target file was not successfully and completely
-       * written; discard it, and diagnose failure.
-       */
-      unlink( pathname );
-      dmh_notify( DMH_ERROR, "%s: extraction failed\n", pathname );
-    }
-  }
-  return status;
-
-#else
-  /* Debugging stub...
-   *
-   * FIXME:maybe adapt for 'dry-run' or 'verbose' use.
-   */
-  dmh_printf(
-      "FIXME:ProcessDataStream<stub>:not extracting: %s\n",
-      pathname
-    );
-  return ProcessEntityData( -1 );
-#endif
-}
-
-void pkgTarArchiveInstaller::
-UpdateInstallationManifest( const char *key, const char *pathname )
-{
-  /* Write installation database records for directory
-   * and file entities created during archive extraction.
-   */
-  pkgXmlNode *dir = new pkgXmlNode( key );
-  dir->SetAttribute( pathname_key, pathname );
-  installed->AddChild( dir );
-}
-
 /* $RCSfile$: end of file */