OSDN Git Service

Handle "%" wildcard matches in package and subsystem version strings.
authorKeith Marshall <keithmarshall@users.sourceforge.net>
Wed, 5 May 2010 20:34:17 +0000 (20:34 +0000)
committerKeith Marshall <keithmarshall@users.sourceforge.net>
Wed, 5 May 2010 20:34:17 +0000 (20:34 +0000)
ChangeLog
Makefile.in
src/pkgbase.h
src/pkgdeps.cpp
src/pkgexec.cpp
src/pkginfo/pkginfo.h
src/pkginfo/pkginfo.l
src/pkgreqs.cpp [new file with mode: 0644]

index 56f8441..74353a2 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,38 @@
+2010-05-05  Keith Marshall  <keithmarshall@users.sourceforge.net>
+
+       Handle "%" wildcard matches in package and subsystem version strings.
+
+       * src/pkgbase.h (pkgSpecs): Forward declare it.
+       (pkgActionItem::~pkgActionItem): Declare explicit destructor.
+       (pkgActionItem::SetRequirements): Add extra `pkgSpecs*' argument;
+       update references, passing additional argument in...
+
+       * src/pkgdeps.cpp (pkgXmlDocument::ResolveDependencies): ...this.
+
+       * src/pkgexec.cpp (pkgActionItem::~pkgActionItem): Implement it.
+       (pkgActionItem::SetRequirements): Delete obsolete implementation.
+
+       * src/pkginfo/pkginfo.h: Update copyright notice; add year 2010.
+       (pkgSpecs::GetTarName): New public method; declare it.
+       (pkgSpecs::SetProperty): New private method; declare it.
+       (pkgSpecs::SetPackageName): New inline method; implement it.
+       (pkgSpecs::SetPackageVersion, pkgSpecs::SetPackageBuild): Likewise.
+       (pkgSpecs::SetSubSystemName, pkgSpecs::SetSubSystemVersion): Likewise.
+       (pkgSpecs::SetSubSystemBuild, pkgSpecs::SetReleaseStatus): Likewise.
+       (pkgSpecs::SetReleaseIndex, pkgSpecs::SetComponentClass): Likewise.
+       (pkgSpecs::SetComponentVersion, pkgSpecs::SetPackageFormat): Likewise.
+       (pkgSpecs::SetCompressionType): Likewise.
+
+       * src/pkginfo/pkginfo.l: Update copyright notice; add year 2010.
+       (TRANS): Interpret "%" as wildcard in version string matches.
+
+       * src/pkgreqs.cpp: New file.
+       (pkgActionItem::SetRequirements): Reimplement per new declaration;
+       it now always allocates heap memory for requirements specifications.
+       (pkgSpecs::GetTarName, pkgSpecs::SetProperty): Implement them.
+
+       * Makefile.in (CORE_DLL_OBJECTS): Add `pkgreqs.$(OBJEXT)'.
+
 2010-04-30  Keith Marshall  <keithmarshall@users.sourceforge.net>
 
        Revert 2010-04-04 refactoring of tarproc.cpp and tarinst.cpp
index 7824b98..8810670 100644 (file)
@@ -56,8 +56,8 @@ 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)  pkgreqs.$(OBJEXT) pkginst.$(OBJEXT) tarproc.$(OBJEXT) \
    xmlfile.$(OBJEXT) keyword.$(OBJEXT) vercmp.$(OBJEXT)  dmh.$(OBJEXT) \
-   mkpath.$(OBJEXT)  pkginst.$(OBJEXT) tarproc.$(OBJEXT) \
    tinyxml.$(OBJEXT) tinyxmlparser.$(OBJEXT) \
    tinystr.$(OBJEXT) tinyxmlerror.$(OBJEXT)
 
index ff78a52..9035062 100644 (file)
@@ -71,6 +71,8 @@
 # endif
 #endif
 
+class pkgSpecs;
+
 class pkgXmlNode : public TiXmlElement
 {
   /* A minimal emulation of the wxXmlNode class, founded on
@@ -236,8 +238,8 @@ class pkgActionItem
     /* Methods for defining the selection criteria for
      * packages to be processed.
      */
-    const char* SetRequirements( pkgXmlNode* );
     pkgXmlNode* SelectIfMostRecentFit( pkgXmlNode* );
+    const char* SetRequirements( pkgXmlNode*, pkgSpecs* );
     inline void SelectPackage( pkgXmlNode *pkg, int opt = to_install )
     {
       /* Mark a package as the selection for a specified action.
@@ -254,6 +256,10 @@ class pkgActionItem
     /* Method for processing all scheduled actions.
      */
     void Execute();
+
+    /* Destructor...
+     */
+    ~pkgActionItem();
 };
 
 class pkgXmlDocument : public TiXmlDocument
index 6ba5df7..61c775a 100644 (file)
@@ -127,6 +127,9 @@ pkgXmlDocument::ResolveDependencies( pkgXmlNode* package, pkgActionItem* rank )
    * of such prerequisites, and finally, extend the search to capture
    * additional dependencies common to the containing package group.
    */
+  pkgSpecs *refdata = NULL;
+  pkgXmlNode *refpkg = package;
+
   while( package != NULL )
   {
     /* We have a valid XML entity, which may identify dependencies;
@@ -140,10 +143,24 @@ pkgXmlDocument::ResolveDependencies( pkgXmlNode* package, pkgActionItem* rank )
        */
       pkgXmlNode *installed = NULL;
 
+      /* To facilitate resolution of "%" version matching wildcards
+       * in the requirements specification, we need to parse the version
+       * specification for the current dependent package...
+       */
+      if( refdata == NULL )
+      {
+       /* ...but we deferred that until we knew for sure that it would
+        * be needed; it is, so parse it now.
+        */
+       const char *refname;
+       if( (refname = refpkg->GetPropVal( tarname_key, NULL )) != NULL )
+         refdata = new pkgSpecs( refname );
+      }
+
       /* Identify the prerequisite package, from its canonical name...
        */
       pkgActionItem wanted; pkgXmlNode *selected;
-      pkgSpecs req( wanted.SetRequirements( dep ) );
+      pkgSpecs req( wanted.SetRequirements( dep, refdata ) );
       /*
        * (Both the package name, and subsystem if specified, must match)...
        */
@@ -246,6 +263,7 @@ pkgXmlDocument::ResolveDependencies( pkgXmlNode* package, pkgActionItem* rank )
      */
     package = package->GetParent();
   }
+  delete refdata;
 }
 
 void pkgXmlDocument::Schedule( unsigned long action, const char* name )
index 7c048da..fc65bbe 100644 (file)
@@ -230,51 +230,6 @@ pkgActionItem::GetReference( pkgActionItem& item )
   return NULL;
 }
 
-const char * pkgActionItem::SetRequirements( pkgXmlNode *req )
-{
-  /* Establish the selection criteria, for association of any
-   * particular package release with an action item.
-   */
-  flags &= ACTION_MASK;
-
-  /* First check for a strict equality requirement...
-   */
-  if( (min_wanted = req->GetPropVal( eq_key, NULL )) != NULL )
-    /*
-     * ...and if specified, set the selection range such that
-     * only the one specific release can match.
-     */
-    max_wanted = min_wanted;
-
-  else
-  { /* Check for either an inclusive, or a strictly exclusive,
-     * minimum requirement (release "greater" than) specification,
-     * setting the minimum release selector...
-     */
-    if( ((min_wanted = req->GetPropVal( ge_key, NULL )) == NULL)
-    &&  ((min_wanted = req->GetPropVal( gt_key, NULL )) != NULL)  )
-      /*
-       * ...and its selection mode flag accordingly.
-       */
-      flags |= STRICTLY_GT;
-
-    /* Similarly, check for an inclusive, or a strictly exclusive,
-     * maximum requirement (release "less" than) specification,
-     * setting the maximum release selector...
-     */
-    if( ((max_wanted = req->GetPropVal( le_key, NULL )) == NULL)
-    &&  ((max_wanted = req->GetPropVal( lt_key, NULL )) != NULL)  )
-      /*
-       * ...and its selection mode flag accordingly.
-       */
-      flags |= STRICTLY_LT;
-  }
-
-  /* Return a canonical representation of the requirements spec.
-   */
-  return (min_wanted == NULL) ? max_wanted : min_wanted;
-}
-
 pkgXmlNode *pkgActionItem::SelectIfMostRecentFit( pkgXmlNode *package )
 {
   /* Assign "package" as the "selection" for the referring action item,
@@ -393,4 +348,28 @@ void pkgActionItem::Execute()
   }
 }
 
+pkgActionItem::~pkgActionItem()
+{
+  /* Destructor...
+   * The package version range selectors, "min_wanted" and "max_wanted",
+   * are always allocated storage space on the heap; we need to free that,
+   * before we destroy the reference pointers.
+   */
+  if( (max_wanted != NULL) && (max_wanted != min_wanted) )
+    /*
+     * "max_wanted" is non-NULL, and is distinct, (i.e. it doesn't
+     * represent an equality constraint which shares a reference with
+     * "min_wanted"); we need to free it independently.
+     */
+    free( (void *)(max_wanted) );
+
+  if( min_wanted != NULL )
+    /*
+     * "min_wanted" is non-NULL; we don't care if it is distinct,
+     * because if not, freeing it is required anyway, to also free
+     * the same memory referenced by "max_wanted".
+     */
+    free( (void *)(min_wanted) );
+}
+
 /* $RCSfile$: end of file */
index 36eb49b..2868e1a 100644 (file)
@@ -5,7 +5,7 @@
  * $Id$
  *
  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
- * Copyright (C) 2009, MinGW Project
+ * Copyright (C) 2009, 2010, MinGW Project
  *
  *
  * Public interface for the package tarname interpreter.  Provides
  * which is implemented as a "flex" scanner in file "pkginfo.l", to
  * be accessed via the "get_pkginfo()" function.
  *
- * When included by "C++" code, it also defines the interface
- * for the "pkgSpecs" class, which is used by the package manager,
- * for convenient evaluation and comparison of package attributes, 
- * based on examination of the package tarname.
+ * When included by "C++" code, it also defines the interface for
+ * the "pkgSpecs" class, which is used by the package manager, for
+ * convenient evaluation, comparison and manipulation of package
+ * attributes, based on examination of the package tarname.
  *
  *
  * This is free software.  Permission is granted to copy, modify and
@@ -109,6 +109,11 @@ class pkgSpecs
     pkginfo_t  specs;          /* pkginfo data array */
     void      *content;                /* buffer to hold its content */
 
+    /* Internal method to facilitate manipulation of individual
+     * property field values.
+     */
+    const char *SetProperty( int, const char* );
+
   public:
     /* Constructors...
      */
@@ -126,18 +131,105 @@ class pkgSpecs
 
     /* Accessors...
      */
-    inline const char *GetPackageName(){ return specs[PACKAGE_NAME]; }
-    inline const char *GetPackageVersion(){ return specs[PACKAGE_VERSION]; }
-    inline const char *GetPackageBuild(){ return specs[PACKAGE_BUILD]; }
-    inline const char *GetSubSystemName(){ return specs[PACKAGE_SUBSYSTEM_NAME]; }
-    inline const char *GetSubSystemVersion(){ return specs[PACKAGE_SUBSYSTEM_VERSION]; }
-    inline const char *GetSubSystemBuild(){ return specs[PACKAGE_SUBSYSTEM_BUILD]; }
-    inline const char *GetReleaseStatus(){ return specs[PACKAGE_RELEASE_STATUS]; }
-    inline const char *GetReleaseIndex(){ return specs[PACKAGE_RELEASE_INDEX]; }
-    inline const char *GetComponentClass(){ return specs[PACKAGE_COMPONENT_CLASS]; }
-    inline const char *GetComponentVersion(){ return specs[PACKAGE_COMPONENT_VERSION]; }
-    inline const char *GetPackageFormat(){ return specs[PACKAGE_FORMAT]; }
-    inline const char *GetCompressionType(){ return specs[PACKAGE_COMPRESSION_TYPE]; }
+    inline const char *GetPackageName()
+    {
+      return specs[PACKAGE_NAME];
+    }
+    inline const char *GetPackageVersion()
+    {
+      return specs[PACKAGE_VERSION];
+    }
+    inline const char *GetPackageBuild()
+    {
+      return specs[PACKAGE_BUILD];
+    }
+    inline const char *GetSubSystemName()
+    {
+      return specs[PACKAGE_SUBSYSTEM_NAME];
+    }
+    inline const char *GetSubSystemVersion()
+    {
+      return specs[PACKAGE_SUBSYSTEM_VERSION];
+    }
+    inline const char *GetSubSystemBuild()
+    {
+      return specs[PACKAGE_SUBSYSTEM_BUILD];
+    }
+    inline const char *GetReleaseStatus()
+    {
+      return specs[PACKAGE_RELEASE_STATUS];
+    }
+    inline const char *GetReleaseIndex()
+    {
+      return specs[PACKAGE_RELEASE_INDEX];
+    }
+    inline const char *GetComponentClass()
+    {
+      return specs[PACKAGE_COMPONENT_CLASS];
+    }
+    inline const char *GetComponentVersion()
+    {
+      return specs[PACKAGE_COMPONENT_VERSION];
+    }
+    inline const char *GetPackageFormat()
+    {
+      return specs[PACKAGE_FORMAT];
+    }
+    inline const char *GetCompressionType()
+    {
+      return specs[PACKAGE_COMPRESSION_TYPE];
+    }
+
+    /* Manipulators...
+     */
+    inline const char *SetPackageName( const char *value )
+    {
+      return SetProperty( PACKAGE_NAME, value );
+    }
+    inline const char *SetPackageVersion( const char *value )
+    {
+      return SetProperty( PACKAGE_VERSION, value );
+    }
+    inline const char *SetPackageBuild( const char *value )
+    {
+      return SetProperty( PACKAGE_BUILD, value );
+    }
+    inline const char *SetSubSystemName( const char *value )
+    {
+      return SetProperty( PACKAGE_SUBSYSTEM_NAME, value );
+    }
+    inline const char *SetSubSystemVersion( const char *value )
+    {
+      return SetProperty( PACKAGE_SUBSYSTEM_VERSION, value );
+    }
+    inline const char *SetSubSystemBuild( const char *value )
+    {
+      return SetProperty( PACKAGE_SUBSYSTEM_BUILD, value );
+    }
+    inline const char *SetReleaseStatus( const char *value )
+    {
+      return SetProperty( PACKAGE_RELEASE_STATUS, value );
+    }
+    inline const char *SetReleaseIndex( const char *value )
+    {
+      return SetProperty( PACKAGE_RELEASE_INDEX, value );
+    }
+    inline const char *SetComponentClass( const char *value )
+    {
+      return SetProperty( PACKAGE_COMPONENT_CLASS, value );
+    }
+    inline const char *SetComponentVersion( const char *value )
+    {
+      return SetProperty( PACKAGE_COMPONENT_VERSION, value );
+    }
+    inline const char *SetPackageFormat( const char *value )
+    {
+      return SetProperty( PACKAGE_FORMAT, value );
+    }
+    inline const char *SetCompressionType( const char *value )
+    {
+      return SetProperty( PACKAGE_COMPRESSION_TYPE, value );
+    }
 
     /* Comparators...
      */
@@ -146,6 +238,16 @@ class pkgSpecs
     bool operator==( pkgSpecs& );
     bool operator>=( pkgSpecs& );
     bool operator>( pkgSpecs& );
+
+    /* Helper to reconstitute the associated tarname...
+     * The optional argument MUST either be a NULL pointer, or
+     * it MUST point to memory previously allocated by malloc();
+     * (the returned value is ALWAYS such a pointer, and the
+     * intent of the argument is to recycle the memory used
+     * for previous results, rather than to provide any
+     * pre-allocated buffer).
+     */
+    const char *GetTarName( const char* = NULL );
 };
 
 #endif /* __cplusplus */
index b803c6d..451a2c9 100644 (file)
@@ -4,7 +4,7 @@
  * $Id$
  *
  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
- * Copyright (C) 2009, MinGW Project
+ * Copyright (C) 2009, 2010, MinGW Project
  *
  *
  * A simple lexical analyser for decomposing package archive names into their
@@ -140,7 +140,7 @@ STATUS_KEYWORDS (alpha|beta|stable)
     yyless( 0 );
   }
 
-<TRANS>([&*]|[^-0-9.][^-.]+)(-[0-9][^-.]*){0,1}(\.[^-.]+){1,2}\? {
+<TRANS>([%&*]|[^-0-9.][^-.]+)(-[0-9][^-.]*){0,1}(\.[^-.]+){1,2}\? {
     /*
      * Transitional case rule...
      * Identify a following terminal <type-id> sequence; set up
@@ -154,7 +154,7 @@ STATUS_KEYWORDS (alpha|beta|stable)
     yyless( 0 );
   }
 
-<TRANS>([&*][.-])|([0-9]+[.-]) {
+<TRANS>([%&*][.-])|([0-9]+[.-]) {
     /*
      * Transitional case rule...
      * Found a purely numeric following element, such as a <major>
diff --git a/src/pkgreqs.cpp b/src/pkgreqs.cpp
new file mode 100644 (file)
index 0000000..b1a6c5d
--- /dev/null
@@ -0,0 +1,480 @@
+/*
+ * pkgreqs.cpp
+ *
+ * $Id$
+ *
+ * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+ * Copyright (C) 2009, 2010, MinGW Project
+ *
+ *
+ * Implements the SetRequirements() method for the pkgActionItem class,
+ * together with additional components of the pkgSpecs class which are
+ * required specifically to support it.
+ *
+ *
+ * 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 "pkginfo.h"
+#include "pkgkeys.h"
+#include "pkgtask.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ ******************
+ *
+ * Class Implementation: pkgSpecs; SetProperty and GetTarName methods
+ *
+ */
+
+static inline
+int buflen( pkginfo_t specs )
+{
+  /* Local helper function, to determine the size of the buffer used
+   * to store the data content of all pkgSpecs fields; (it is also the
+   * length of the C string representing the associated package tarname,
+   * INCLUDING its NUL terminator).
+   */
+  int index = PACKAGE_TAG_COUNT;
+  while( (index-- > 0) && (specs[index] == NULL) )
+    /*
+     * Locate the last populated data field in the content buffer...
+     */ ;
+    /* ...then compute the total buffer length, from the start of
+     * the first field, up to the NUL terminator of the last.
+     */
+  return specs[index] ? specs[index] - *specs + strlen( specs[index] ) + 1 : 0;
+}
+
+static inline
+int fieldlen( const char *string )
+{
+  /* NULL-safe local helper function to determine the length of
+   * the C string representing a pkgSpecs data field, (EXCLUDING
+   * its NUL terminator); returns zero in the case of an unassigned
+   * field, identified by a NULL reference pointer.
+   */
+  return (string == NULL) ? 0 : strlen( string );
+}
+
+static inline
+char *fieldcpy( char *dest, const char *src )
+{
+  /* Local helper function to copy the content of a single
+   * pkgSpecs data field...
+   */
+  while( (*dest++ = *src++) != '\0' )
+    /*
+     * ...copying byte by byte, including the terminating NUL,
+     * to the destination buffer...
+     */ ;
+  /* ...and ultimately, return the next available location
+   * within the destination buffer.
+   */
+  return dest;
+}
+
+static inline
+char *bufcpy( int index, int end, char *buf, pkginfo_t specs )
+{
+  /* Local helper function to copy a specified field range from
+   * its original location within the pkgSpecs content buffer, to
+   * an alternate location specified by "buf".
+   */
+  do { if( specs[index] != NULL )
+         /*
+         * Copy each non-empty data field...
+         */
+         buf = fieldcpy( buf, specs[index] );
+
+       /* ...until all specified fields in the source range
+       * have been copied.
+       */
+     } while( ++index < end );
+
+  /* Finally, return the location at which any further data should
+   * be appended to the data just copied.
+   */
+  return buf;
+}
+
+const char *pkgSpecs::SetProperty( int index, const char *repl )
+{
+  /* A private method to modify the content of any single data field
+   * within a pkgSpecs data buffer; it provides the core implementation
+   * for each of the public inline field manipulator methods.
+   */
+  char *p;
+
+  /* We begin by computing the current size of the content buffer,
+   * and the size it will become after making the change.
+   */
+  int oldlen = buflen( specs );
+  int newlen = oldlen + fieldlen( repl ) - fieldlen( specs[index] );
+
+  if( newlen > oldlen )
+  {
+    /* The buffer size must increase; thus, there is insufficient space
+     * at the location of the field to be modified, to accomodate its new
+     * value, without corrupting the content of following fields.  To avoid
+     * this, we use a temporary buffer in which we construct an image of
+     * the modified content...
+     */
+    char newbuf[ newlen ];
+    /*
+     * ...first copying the content of all fields which precede that
+     * which is to be modified...
+     */
+    p = bufcpy( 0, index, newbuf, specs );
+    /*
+     * ...appending the new value to be assigned to the modified field...
+     */
+    p = fieldcpy( p, repl );
+    /*
+     * ...and then the content of any following unmodified fields.
+     */
+    bufcpy( index + 1, PACKAGE_TAG_COUNT, p, specs );
+
+    /* Having created this temporary image of the content buffer, as
+     * it is to become, adjust the allocation size of the original buffer,
+     * and copy its modified content into place.
+     */
+    if( (p = (char *)(content = realloc( content, newlen ))) != NULL )
+      memcpy( p, newbuf, newlen );
+  }
+
+  else
+  { /* In this case, the size of content buffer will either shrink, or
+     * it will remain unchanged; we may modify it in place, beginning at
+     * the location of the field which is to be modified, by writing its
+     * new value, if any, into place.
+     */
+    p = specs[index];
+    if( (repl != NULL) && (*repl != '\0') )
+      p = fieldcpy( p, repl );
+
+    /* If the new content of the modified field is exactly the same length
+     * as the content it has replaced, then the overall size of the content
+     * buffer does not change...
+     */
+    if( newlen == oldlen )
+      /*
+       * ...so we have nothing more to do, to complete the modification...
+       */
+      return (char *)(content);
+
+    /* ...otherwise, there is residual data from the original content at
+     * the location now indicated by "p", and preceding the start of the
+     * content of the next populated field, (if any); clear this, moving
+     * the content of all following fields up to fill the gap...
+     */
+    bufcpy( index + 1, PACKAGE_TAG_COUNT, p, specs );
+
+    /* ...and adjust the allocation size to the reduced data length.
+     */
+    p = (char *)(content = realloc( content, newlen ));
+  }
+
+  /* Irrespective of which of the preceding paths we followed to get here,
+   * the size of the content buffer was adjusted, so...
+   */
+  if( p != NULL )
+  {
+    /* ...provided it was not lost altogether...
+     */
+    if( (repl == NULL) || (*repl == '\0') )
+      /*
+       * ...we either mark the content of the modified field as "deleted",
+       * when appropriate...
+       */
+      specs[index] = NULL;
+
+    else
+      /* ...or we ensure that it is marked as having SOME content, in case
+       * it may have been empty beforehand; (note that this does NOT assign
+       * the correct pointer value -- any arbitrary non-NULL pointer value
+       * will suffice here, so we just adopt the physical start address of
+       * the content buffer).
+       */
+      specs[index] = p;
+
+    /* Now, we walk through the content buffer, to fix up the data pointers
+     * for the content of any fields for which the start address may have
+     * changed, as a result of buffer size adjustment...
+     */
+    for( index = 0; index < PACKAGE_TAG_COUNT; index++ )
+    {
+      /* ...thus, for each non-empty field...
+       */
+      if( specs[index] != NULL )
+      {
+       /* ...assign the start address of the next available block of
+        * populated content data...
+        */
+       specs[index] = p;
+
+       /* ...and advance the data pointer to the start of the next
+        * available block of content, (if any).
+        */
+       p += strlen( p ) + 1;
+      }
+    }
+  }
+
+  /* Finally, we return the address of the physical starting location
+   * of the (possibly relocated) pkgSpecs content buffer.
+   */
+  return (char*)(content);
+}
+
+const char *pkgSpecs::GetTarName( const char *buf )
+{
+  /* Reconstitute the canonical tarname for the package
+   * identified by the current pkgSpecs record, returning
+   * the result in a C string buffer allocated by malloc();
+   * "buf" may be specified as NULL, otherwise it MUST point
+   * to a similarly allocated buffer, which is recycled.
+   */
+  char *dest = (char*)(realloc( (void*)(buf), buflen( specs ) ));
+  if( (buf = dest) != NULL )
+    /*
+     * We have a valid buffer suitable for returning the result...
+     */
+    for( int index = PACKAGE_NAME; index < PACKAGE_TAG_COUNT; index++ )
+    {
+      /* For each field in the package specification...
+       */
+      char *src = specs[index];
+      if( (src != NULL) && (*src != '\0') )
+      {
+       /* ...for which a non-empty value is defined...
+        */
+       if( dest > buf )
+         /*
+          * ...when not the first such field, insert the
+          * appropriate field separator character...
+          */
+         *dest++ = (index < PACKAGE_FORMAT) ? '-' : '.';
+
+       /* ...then copy the field content to the result buffer.
+        */
+       while( (*dest = *src++) != '\0' )
+         ++dest;
+      }
+    }
+
+  /* Return the fully reconstituted tarname, or NULL if the
+   * return buffer allocation failed.
+   */
+  return buf;
+}
+
+
+/*
+ ******************
+ *
+ * Class Implementation: pkgActionItem; SetRequirements method
+ *
+ */
+
+enum inherit_mode
+{
+  /* Definition of the inheritance flags identifying those elements
+   * of the package and subsystem version fields, within a requirements
+   * specification, which are to be matched by a "%" wildcard.
+   *
+   * These flags apply to any ONE version number specification, which
+   * may represent EITHER the package version OR the subsystem version
+   * within a requirements specification; it takes the form:
+   *
+   *   major.minor.patch-datestamp-index
+   *
+   * and the flags are interpreted to indicate...
+   */
+  INHERIT_NONE = 0,    /* no "%" wildcard match requested            */
+  INHERIT_VERSION,     /* "major.minor.patch" matches a "%" wildcard */
+  INHERIT_BUILD,       /* "datestamp-index" matches a "%" wildcard   */
+  INHERIT_ALL          /* "%" matches the entire version string      */
+};
+
+static
+enum inherit_mode inherited( const char *ver, const char *bld )
+{
+  /* Local helper function to assign "%" wildcard inheritance flags
+   * to a specified version number, where:
+   *
+   *   "ver" represents the "major.minor.patch" part of the version
+   *   "bld" represents the optional "datestamp-index" extension
+   */
+  enum inherit_mode retval = INHERIT_NONE;
+
+  /* Noting that the "%" wildcard must represent the respective part
+   * of the version specification in its entirety...
+   */
+  if( (ver != NULL) && (ver[0] == '%') && (ver[1] == '\0') )
+    /*
+     * ...flag a match on the "major.minor.patch" specification.
+     */
+    retval = INHERIT_VERSION;
+
+  /* Considering the optional "datestamp-index" specification...
+   */
+  if( bld == NULL )
+  {
+    /* ...when it isn't specified at all, then it is implicitly
+     * governed by a match on the "major.minor.patch" element...
+     */
+    if( retval == INHERIT_VERSION )
+      /*
+       * ...thus promoting this to become a match on the entire
+       * "major.minor.patch-datestamp-index" specification.
+       */
+      retval = INHERIT_ALL;
+  }
+  else if( (bld[0] == '%') && (bld[1] == '\0') )
+    /*
+     * ...otherwise we may, (less usefully, perhaps and therefore
+     * not to be encouraged), require a "%" wildcard match on the
+     * "datestamp-index" element alone, (with some other criterion
+     * applied to the "major.minor.patch" element -- perhaps even
+     * redundantly "%-%", which is equivalent to the simpler form
+     * of "%" alone, to match the entire version specification).
+     */
+    retval = (enum inherit_mode)(retval | INHERIT_BUILD);
+
+  /* The inheritance state is now fully specified; pass it back
+   * to the caller.
+   */
+  return retval;
+}
+
+static
+const char *requirement( const char *wanted, pkgSpecs *dep )
+{
+  /* Local helper function to generate "min_wanted" and "max_wanted"
+   * dependency specifications, for assignment within a pkgActionItem;
+   * propagation of version number fields inherited from the dependant,
+   * as specified by the "%" wildcard, is appropriately enforced.  The
+   * resultant specification is ALWAYS returned on the heap, in memory
+   * dynamically allocated by malloc().
+   */
+  pkgSpecs id( wanted );
+
+  /* Evaluate inheritance of the PACKAGE version number specification...
+   */
+  enum inherit_mode clone;
+  clone = inherited( id.GetPackageVersion(), id.GetPackageBuild() );
+  if( (clone & INHERIT_VERSION) > INHERIT_NONE )
+    /*
+     * ...propagating its "major.minor.patch" element as required...
+     */
+    id.SetPackageVersion( dep->GetPackageVersion() );
+
+  if( (clone & INHERIT_BUILD) > INHERIT_NONE )
+    /*
+     * ...and similarly, its "datestamp-index" element.
+     */
+    id.SetPackageBuild( dep->GetPackageBuild() );
+
+  /* Similarly, for the SUBSYSTEM version number specification...
+   */
+  clone = inherited( id.GetSubSystemVersion(), id.GetSubSystemBuild() );
+  if( (clone & INHERIT_VERSION) > INHERIT_NONE )
+    /*
+     * ...propagate its "major.minor.patch" element as required...
+     */
+    id.SetSubSystemVersion( dep->GetSubSystemVersion() );
+
+  if( (clone & INHERIT_BUILD) > INHERIT_NONE )
+    /*
+     * ...and similarly, its "datestamp-index" element.
+     */
+    id.SetSubSystemBuild( dep->GetSubSystemBuild() );
+
+  /* Finally, reconstitute the canonical tarname representation of the
+   * specification, from its decomposed representation; (as a side effect,
+   * this automatically places the return string on the heap).
+   */
+  return id.GetTarName();
+}
+
+const char * pkgActionItem::SetRequirements( pkgXmlNode *req, pkgSpecs *dep )
+{
+  /* Establish the selection criteria, for association of any
+   * particular package release with an action item.
+   */
+  flags &= ACTION_MASK;
+
+  /* First check for a strict equality requirement...
+   */
+  if( (min_wanted = req->GetPropVal( eq_key, NULL )) != NULL )
+    /*
+     * ...and if specified, set the selection range such that only one
+     * specific release can be matched; evaluate any specified dependency
+     * relationship, to ensure that inherited version numbers are correctly
+     * propagated, and assign the resultant dynamically allocated tarname
+     * specifications to the respective constraints.
+     */
+    return max_wanted = min_wanted = requirement( min_wanted, dep );
+
+  else
+  { /* Check for either an inclusive, or a strictly exclusive,
+     * minimum requirement (release "greater" than) specification,
+     * setting the minimum release selector...
+     */
+    if( ((min_wanted = req->GetPropVal( ge_key, NULL )) == NULL)
+    &&  ((min_wanted = req->GetPropVal( gt_key, NULL )) != NULL)  )
+      /*
+       * ...and its selection mode flag accordingly.
+       */
+      flags |= STRICTLY_GT;
+
+    /* Similarly, check for an inclusive, or a strictly exclusive,
+     * maximum requirement (release "less" than) specification,
+     * setting the maximum release selector...
+     */
+    if( ((max_wanted = req->GetPropVal( le_key, NULL )) == NULL)
+    &&  ((max_wanted = req->GetPropVal( lt_key, NULL )) != NULL)  )
+      /*
+       * ...and its selection mode flag accordingly.
+       */
+      flags |= STRICTLY_LT;
+  }
+
+  /* Check the minimum required version specification...
+   */
+  if( min_wanted != NULL )
+    /*
+     * ...ensuring that inherited version numbers are propagated, as
+     * required from the dependant, and assign the canonical tarname
+     * representation of the final specification on the heap.
+     */
+    min_wanted = requirement( min_wanted, dep );
+
+  /* And...
+   */
+  if( max_wanted != NULL )
+    /*
+     * ...likewise for the maximum required version specification.
+     */
+    max_wanted = requirement( max_wanted, dep );
+
+  /* Return a canonical representation of the requirements spec.
+   */
+  return (min_wanted == NULL) ? max_wanted : min_wanted;
+}
+
+/* $RCSfile$: end of file */