Update of /cvsroot/mingw/mingw-get/src In directory 23jxhf1.ch3.sourceforge.com:/tmp/cvs-serv19325/src Added Files: climain.cpp mkpath.c mkpath.h pkgbase.h pkgexec.cpp pkgspec.cpp pkgtask.h vercmp.cpp vercmp.h xmlfile.c Log Message: Add XML database bindings and preliminary action executive for CLI. --- NEW FILE: vercmp.cpp --- #include <stdio.h> /* * vercmp.cpp * * $Id: vercmp.cpp,v 1.1 2009/11/16 21:54:30 keithmarshall Exp $ * * Written by Keith Marshall <kei...@us...> * Copyright (C) 2009, MinGW Project * * * Implementation of package version comparator module. * * * 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 "vercmp.h" pkgVersionInfo::pkgVersionInfo( const char* version, const char* build ) { /* Constructor... * Decompose given version number string, storing components * within the specified class structure. */ const char *p = version ? version : ""; /* Walking over all version number constituent elements... */ for( int index = VERSION_MAJOR; index < VERSION_ELEMENT_COUNT; index++ ) { /* ...initialise default element value to zero. */ unsigned long value = 0L; /* When appropriate... */ if( (index == SNAPSHOT_DATE) && (*p == '\0') && (build != NULL) ) /* * ...select second argument for parsing. */ p = build; /* When parsing a numeric argument... */ while( (*p >= '0') && ((*p - '0') < 10) ) /* * ...accumulate its ultimate value. */ value = *p++ - '0' + 10 * value; /* Store it, and note presence of any suffix. */ version_elements[index].value = value; version_elements[index].suffix = p; /* Skip forward to next element field delimiter. */ while( *p && (*p != '.') && (*p != '-') ) ++p; if( (*p == '-') || ((*p == '\0') && (build != NULL)) ) /* * If we hit the end of the version number, * before we filled out all of its possible elements, * then zero the remainder, before we progress to * capture the build serial number. */ while( index < VERSION_PATCHLEVEL ) { version_elements[++index].value = 0L; version_elements[index].suffix = p; } /* Step over any delimiter, which demarcates the current * version number or build serial number element field. */ if( *p ) ++p; } } long pkgVersionInfo::Compare( const pkgVersionInfo& rhs, int index ) { /* Compare a given element of a package version specification with the * corresponding element of a reference (rhs) version specification; return * <0L, 0L or >0L for less than, equal to or greater than rhs respectively. */ long cmp = version_elements[index].value - rhs.version_elements[index].value; if( cmp == 0L ) { /* The specified element values are identically equal; * discriminate on suffixes, if any... */ const char *p = version_elements[index].suffix; const char *r = rhs.version_elements[index].suffix; /* Check that both version specifications include a suffix; * ( p == r implies both are NULL, hence neither has a suffix ); * if only one has, then that is the greater... */ if( p == r ) return 0L; if( p == NULL ) return 1L; if( r == NULL ) return -1L; /* Both DO have suffixes... */ while( *p && *r && (*p == *r) && (*p != '.') && (*p != '-') ) { /* ...scan both, until we find a mismatched character, * or the terminal delimiter for either. */ ++p; ++r; } /* Compute return value based on difference between the * mismatched characters, representing delimiters as NUL. */ cmp = (*p && (*p != '.') && (*p != '-')) ? (long)(*p) : 0L; cmp -= (*r && (*r != '.') && (*r != '-')) ? (long)(*r) : 0L; } return cmp; } bool pkgVersionInfo::operator<( const pkgVersionInfo& rhs ) { /* Comparison operator... * Does the version number under consideration represent a less * recent release than the specified reference version number. */ long cmp; for( int index = VERSION_MAJOR; index < VERSION_ELEMENT_COUNT; index++ ) if( (cmp = Compare( rhs, index )) != 0L ) return (cmp < 0L); /* If we get to here, lhs and rhs versions are identically equal, * and hence fail the lhs < rhs comparison. */ return false; } bool pkgVersionInfo::operator<=( const pkgVersionInfo& rhs ) { /* Comparison operator... * Does the version number under consideration represent the same, or * a less recent release than the specified reference version number. */ long cmp; for( int index = VERSION_MAJOR; index < VERSION_ELEMENT_COUNT; index++ ) if( (cmp = Compare( rhs, index )) != 0L ) return (cmp > 0L); /* If we get to here, lhs and rhs versions are identically equal, * and hence satisfy the lhs <= rhs comparison. */ return true; } bool pkgVersionInfo::operator==( const pkgVersionInfo& rhs ) { /* Comparison operator... * Does the version number under consideration exactly match * the specified reference version number. */ for( int index = VERSION_MAJOR; index < VERSION_ELEMENT_COUNT; index++ ) if( Compare( rhs, index ) != 0L ) return false; /* If we get to here, lhs and rhs versions are identically equal, * which is what we require to satisfy the lhs == rhs comparison. */ return true; } bool pkgVersionInfo::operator!=( const pkgVersionInfo& rhs ) { /* Comparison operator... * Does the version number under consideration differ from * the specified reference version number. */ for( int index = VERSION_MAJOR; index < VERSION_ELEMENT_COUNT; index++ ) if( Compare( rhs, index ) != 0L ) return true; /* If we get to here, lhs and rhs versions are identically equal, * which is the sole condition to fail the lhs != rhs comparison. */ return false; } bool pkgVersionInfo::operator>=( const pkgVersionInfo& rhs ) { /* Comparison operator... * Does the version number under consideration represent the same, or * a more recent release than the specified reference version number. */ long cmp; for( int index = VERSION_MAJOR; index < VERSION_ELEMENT_COUNT; index++ ) if( (cmp = Compare( rhs, index )) != 0L ) return (cmp > 0L); /* If we get to here, lhs and rhs versions are identically equal, * and hence satisfy the lhs >= rhs comparison. */ return true; } bool pkgVersionInfo::operator>( const pkgVersionInfo& rhs ) { /* Comparison operator... * Does the version number under consideration represent a more * recent release than the specified reference version number. */ long cmp; for( int index = VERSION_MAJOR; index < VERSION_ELEMENT_COUNT; index++ ) if( (cmp = Compare( rhs, index )) != 0L ) return (cmp > 0L); /* If we get to here, lhs and rhs versions are identically equal, * and hence fail the lhs > rhs comparison. */ return false; } /* $RCSfile: vercmp.cpp,v $: end of file */ --- NEW FILE: vercmp.h --- #ifndef VERCMP_H /* * vercmp.h * * $Id: vercmp.h,v 1.1 2009/11/16 21:54:30 keithmarshall Exp $ * * Written by Keith Marshall <kei...@us...> * Copyright (C) 2009, MinGW Project * * * Public interface for the package version comparator module, as * implemented in the "C++" language file "vercmp.cpp". * * * 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. * */ #define VERCMP_H 1 #include <stdlib.h> enum { /* The constituent elements of a package version number, * and build serial number, in sequential order as they appear * within a correctly formed package tarball name. */ VERSION_MAJOR = 0, VERSION_MINOR, VERSION_PATCHLEVEL, SNAPSHOT_DATE, SERIAL_NUMBER, /* Not a real element of the version/serial number sequence, * but this final entry in the enumeration provides a convenient * counter for the number of real elements listed above. */ VERSION_ELEMENT_COUNT }; struct version_t { /* Internal structure, used to capture an individual element * of a decomposed version number, or build serial number. */ unsigned long value; const char* suffix; }; #ifdef __cplusplus /* * Define the properties of the "C++" class API... */ class pkgVersionInfo { /* A class for capture and manipulation of package version numbers * in decomposed "major.minor.patch-datetamp-sequence" form. */ public: /* Constructor... * This expects either one or two "char *" arguments: * the first is the package version number, in "major.minor.patch" * format; the second is build serial number in "datestamp-sequence" * format. If the second is omitted, the build serial number may * be appended to the first, in the full format as above. */ pkgVersionInfo( const char* version = "", const char* build = NULL ); /* Package version comparison operators. */ bool operator<( const pkgVersionInfo& ); bool operator<=( const pkgVersionInfo& ); bool operator==( const pkgVersionInfo& ); bool operator!=( const pkgVersionInfo& ); bool operator>=( const pkgVersionInfo& ); bool operator>( const pkgVersionInfo& ); private: /* The decomposed version/serial number elements. */ struct version_t version_elements[VERSION_ELEMENT_COUNT]; /* An internal comparison helper function */ long Compare( const pkgVersionInfo&, int ); }; #endif /* __cplusplus */ #endif /* VERCMP_H: $RCSfile: vercmp.h,v $: end of file */ --- NEW FILE: mkpath.h --- #ifndef MKPATH_H /* * mkpath.h * * $Id: mkpath.h,v 1.1 2009/11/16 21:54:30 keithmarshall Exp $ * * Written by Keith Marshall <kei...@us...> * Copyright (C) 2009, MinGW Project * * * Prototype declarations for the path name constructor functions, * and the directory hierarchy and file creation functions, which are * implemented in mkpath.c * * * 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. * */ #define MKPATH_H 1 #ifndef EXTERN_C # ifdef __cplusplus # define EXTERN_C extern "C" # else # define EXTERN_C # endif #endif EXTERN_C int mkdir_recursive( const char *, int ); EXTERN_C int set_output_stream( const char *, int ); EXTERN_C int mkpath( char *, const char *, const char *, const char * ); #endif /* MKPATH_H: $RCSfile: mkpath.h,v $: end of file */ --- NEW FILE: xmlfile.c --- /* * xmlfile.c * * $Id: xmlfile.c,v 1.1 2009/11/16 21:54:30 keithmarshall Exp $ * * Written by Keith Marshall <kei...@us...> * Copyright (C) 2009, MinGW Project * * * Helper function for constructing path names to the XML data files * specifying the mingw-get configuration, and installation manifest. * * * 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 <stdlib.h> #include "mkpath.h" const char *xmlfile( const char *name, const char *modifier ) { /* Construct a full path name for the file specified by "name", * adding the mandatory ".xml" extension; the path is always based * at "${APPROOT}", with "modifier" specifying an optional subdirectory * of the standard path, (typically used as a temporary location for * internet downloads while in transit). */ const char *datapath = "%R" "var/lib/mingw-get/data" "%/M/%F.xml"; char *datafile = (char *)(malloc( mkpath( NULL, datapath, name, modifier ) )); mkpath( datafile, datapath, name, modifier ); return (const char *)(datafile); } /* $RCSfile: xmlfile.c,v $: end of file */ --- NEW FILE: climain.cpp --- /* * climain.cpp * * $Id: climain.cpp,v 1.1 2009/11/16 21:54:30 keithmarshall Exp $ * * Written by Keith Marshall <kei...@us...> * Copyright (C) 2009, MinGW Project * * * Implementation of the main program function, which is invoked by * the command line start-up stub when arguments are supplied; this * causes the application to continue running as a CLI process. * * * 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 <string.h> #include "dmh.h" #include "pkgbase.h" #include "pkgtask.h" EXTERN_C int climain( int argc, char **argv ) { /* Set up the diagnostic message handler, using the console's * `stderr' stream for notifications... */ dmh_init( DMH_SUBSYSTEM_TTY, *argv++ ); /* TODO: insert code here, to interpret any OPTIONS specified * on the command line. */ /* Interpret the `action keyword', specifying the action to be * performed on this invocation... */ int action = action_code( *argv ); if( action < 0 ) /* * The specified action keyword was invalid; * force an abort through a DMH_FATAL notification... */ dmh_notify( DMH_FATAL, "%s: unknown action keyword\n", *argv ); /* If we get to here, then the specified action identifies a * valid operation; load the package database, according to the * local `profile' configuration, and invoke the operation. */ const char *dfile; pkgXmlDocument dbase( dfile = xmlfile( "profile" ) ); if( dbase.IsOk() ) { /* We successfully loaded the basic settings... * The configuration file name was pushed on to the heap, * by xmlfile(); we don't need that any more, (because it * is reproduced within the database image itself), so * free the heap copy, to avoid memory leaks. */ free( (void *)(dfile) ); #if 0 /* Merge all package lists, as specified in the "repository" * section of the "profile", into the XML database tree... */ if( dbase.BindRepositories() == NULL ) /* * ...bailing out, on an invalid profile specification... */ dmh_notify( DMH_FATAL, "%s: invalid application profile\n", dbase.Value() ); /* Now schedule the specified action for each additionally * specified command line argument, (each of which is assumed * to represent a package name... */ while( --argc ) dbase.Schedule( (unsigned long)(action), *++argv ); /* ...and finally, execute all scheduled actions... */ dbase.ExecuteActions(); #endif /* If we get this far, then all actions completed successfully; * we are done... */ return EXIT_SUCCESS; } /* If we get to here, then the package database load failed; * once more, we force an abort through a DMH_FATAL notification... * * Note: although dmh_notify does not return, in the DMH_FATAL case, * GCC cannot know this, so we pretend that it gives us a return value, * to avoid a possible warning about reaching the end of a non-void * function without a return value assignment... */ return dmh_notify( DMH_FATAL, "%s: cannot load configuration\n", dfile ); } /* $RCSfile: climain.cpp,v $: end of file */ --- NEW FILE: pkgexec.cpp --- /* * pkgexec.cpp * * $Id: pkgexec.cpp,v 1.1 2009/11/16 21:54:30 keithmarshall Exp $ * * Written by Keith Marshall <kei...@us...> * Copyright (C) 2009, MinGW Project * * * Implementation of package management task scheduler and executive. * * * 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 "dmh.h" #include "mkpath.h" #include "pkgbase.h" #include "pkginfo.h" #include "pkgtask.h" EXTERN_C const char *action_name( unsigned long index ) { /* Define the keywords used on the mingw-get command line, * to specify the package management actions to be performed, * mapping each to a unique action code index. */ static const char* action_id[] = { "no change", /* unused; zero cannot test true in a bitwise test */ "remove", /* remove a previously installed package */ "install", /* install a new package */ "upgrade" /* update previously installed packages */ }; /* For specified "index", return a pointer to the associated keyword, * or NULL, if "index" is outside the defined action code range. */ return ((index >= 0) && (index < end_of_actions)) ? action_id[ index ] : NULL; } EXTERN_C int action_code( const char* request ) { /* Match an action keyword specified on the command line * to an entry from the above list... */ int lencode = strlen( request ); int index; for( index = 0; index < end_of_actions; index++ ) { /* Try all defined keywords in turn, until we find a match * or we run out of definitions... */ if( strncmp( request, action_name( index ), lencode ) == 0 ) /* * for a successful match... * immediately return the associated action code index. */ return index; } /* If we get to here, the specified keyword was not matched; * signal this, by returning -1. */ return -1; } pkgActionItem::pkgActionItem( pkgActionItem *after, pkgActionItem *before ) { /* Construct an appropriately initialised non-specific pkgActionItem... */ flags = 0; /* no specific action yet assigned */ min_wanted = NULL; /* no minimum package version constraint... */ max_wanted = NULL; /* nor any maximum version */ selection = NULL; /* no package selection yet, for this item */ /* Insert this item at a specified location in the actions list. */ prev = after; next = before; } pkgActionItem* pkgActionItem::Append( pkgActionItem *item ) { /* Add an "item" to an ActionItems list, attaching it immediately * after the item referenced by the "this" pointer; nominally "this" * refers to the last entry in the list, resulting in a new item * being appended to the list, but the implementation preserves * integrity of any following list items, thus also fulfilling * an "insert after this" function. */ if( this == NULL ) /* * No list exists yet; * return "item" as first and only entry in new list. */ return item; /* Ensure "item" physically exists, or if not, create a generic * placeholder in which to construct it... */ if( (item == NULL) && ((item = new pkgActionItem()) == NULL) ) /* * ...bailing out if no such placeholder can be created. */ return NULL; /* Maintain list integrity... */ if( (item->next = next) != NULL ) /* * ...moving any existing items which already follow the insertion * point in the list structure, to follow the newly added "item". */ next->prev = item; /* Set the new item's own reference pointer, to establish its list * attachment point... */ item->prev = this; /* ...and attach it immediately after that point. */ return next = item; } pkgActionItem* pkgActionItem::Insert( pkgActionItem *item ) { /* Add an "item" to an ActionItems list, inserting it immediately * before the item referenced by the "this" pointer. */ if( this == NULL ) /* * No list exists yet; * return "item" as first and only entry in new list. */ return item; /* Ensure "item" physically exists, or if not, create a generic * placeholder in which to construct it... */ if( (item == NULL) && ((item = new pkgActionItem()) == NULL) ) /* * ...bailing out if no such placeholder can be created. */ return NULL; /* Maintain list integrity... */ if( (item->prev = prev) != NULL ) /* * ...moving any existing items which already precede the insertion * point in the list structure, to precede the newly added "item". */ prev->next = item; /* Set the new item's own reference pointer, to establish the item * currently at the attachment point, as its immediate successor... */ item->next = this; /* ...and attach it, immediately preceding that point. */ return prev = item; } pkgActionItem* pkgActionItem::Schedule( unsigned long action, pkgActionItem& item ) { /* Make a copy of an action item template (which may exist in * a volatile scope) on the heap, assign the requested action, * and return it for inclusion in the task schedule. */ pkgActionItem *rtn = new pkgActionItem(); *rtn = item; rtn->flags = action | (rtn->flags & ~ACTION_MASK); return rtn; } pkgActionItem* pkgActionItem::GetReference( pkgActionItem& item ) { /* Check for a prior reference, within the task schedule, * for the package specified for processing by "item". */ pkgXmlNode* pkg; if( (pkg = item.selection->GetParent()) != NULL ) { /* We have a pointer to the XML database entry which identifies * the package containing the release specified as the selection * associated with "item"; walk the chain of prior entries in * the schedule... */ for( pkgActionItem* item = this; item != NULL; item = item->prev ) { /* ...and if we find another item holding an identical pointer, * (i.e. to the same package), we return it... */ if( item->selection->GetParent() == pkg ) return item; } } /* If we get to here, there is no prior action scheduled for the * specified package, so we return a NULL pointer... */ 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", 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", NULL )) == NULL) && ((min_wanted = req->GetPropVal( "gt", 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", NULL )) == NULL) && ((max_wanted = req->GetPropVal( "lt", 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, * provided it matches the specified selection criteria and it represents * a more recent release than any current selection. */ pkgSpecs test( package ); /* Establish the selection criteria... */ pkgSpecs min_fit( min_wanted ); pkgSpecs max_fit( max_wanted ); /* Choose one of the above, as a basis for identification of * a correct package-component match... */ pkgSpecs& fit = min_wanted ? min_fit : max_fit; /* Verify that "package" fulfills the selection criteria... */ if( match_if_explicit( test.GetComponentClass(), fit.GetComponentClass() ) && match_if_explicit( test.GetComponentVersion(), fit.GetComponentVersion() ) && ((max_wanted == NULL) || ((flags & STRICTLY_LT) ? (test < max_fit) : (test <= max_fit))) && ((flags & STRICTLY_GT) ? (test > min_fit) : (test >= min_fit)) ) { /* We have the correct package component, and it fits within * the allowed range of release versions... */ pkgSpecs last( selection ); if( test > last ) /* * It is also more recent than the current selection, * so we now replace that... */ selection = package; } /* Whatever choice we make, we return the resultant selection... */ return selection; } pkgActionItem* pkgXmlDocument::Schedule ( unsigned long action, pkgActionItem& item, pkgActionItem* rank ) { /* Schedule an action item with a specified ranking order in * the action list, (or at the end of the list if no ranking * position is specified)... */ pkgActionItem *ref = rank ? rank : actions; /* Record the requested action code... */ request = action; /* Don't reschedule, if we already have a prior matching item... */ if( (ref->GetReference( item ) == NULL) /* * ...but, when we don't, we raise a new scheduling request... */ && ((ref = ref->Schedule( action, item )) != NULL) ) { /* ...and, when successfully raised, add it to the task list... */ if( rank ) /* * ...at the specified ranking position, if any... */ return rank->Insert( ref ); else /* ...otherwise, at the end of the list. */ return actions = actions->Append( ref ); } /* If we get to here, then no new action was scheduled; we simply * return the current insertion point in the task list. */ return rank; } /* $RCSfile: pkgexec.cpp,v $: end of file */ --- NEW FILE: mkpath.c --- /* * mkpath.c * * $Id: mkpath.c,v 1.1 2009/11/16 21:54:30 keithmarshall Exp $ * * Written by Keith Marshall <kei...@us...> * Copyright (C) 2009, MinGW Project * * * Helper functions for constructing path names, creating directory * hierarchies, and preparing to write new files within any specified * file system hierarchy. * * * 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 "mkpath.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #ifdef _WIN32 /* * MS-Windows nuisances... * mkdir() function doesn't accept a `mode' argument; ignore it. */ # define mkdir( PATH, MODE ) _mkdir( PATH ) /* * MS-Windows _O_BINARY vs. _O_TEXT discrimination can't be explicitly * resolved in a simple `creat()' call; instead, we will use `_open()', * with the following explicit attribute set... */ # define _O_NEWFILE _O_RDWR | _O_CREAT | _O_TRUNC | _O_BINARY # define creat(P,M) _open( P, _O_NEWFILE, _map_posix_mode(M) ) /* Furthermore, MS-Windows protection modes are naive, in comparison * to the POSIX modes specified within tar archive headers; firstly, * there is no concept of `execute' permissions; secondly, there is * no concept of a `write-only' file, (thus _S_IREAD permission is * implicitly granted for all files); thirdly, `write' permission is * granted only by a single _S_IWRITE flag, so at best, we must set * this if any of POSIX's S_IWUSR, S_IWGRP or S_IWOTH flags are set * in the tar header; finally, MS-Windows has no counterpart for any * of POSIX's `suid', `sgid' or `sticky' bits. */ # define _S_IWANY 0222 /* eqv. POSIX's S_IWUSR | S_IWGRP | S_IWOTH */ # define _map_posix_mode(M) _S_IREAD | (((M) & _S_IWANY) ? _S_IWRITE : 0) #endif int mkpath( char *buf, const char *fmt, const char *file, const char *modifier ) { /* A helper function, for constructing package URL strings. * Return value is the length, in bytes, of the constructed string, * which is returned in "buf"; call with "buf = NULL" to determine * the size of buffer required, without storing the string. * * Constructed URL is copied from "fmt", with... * * %% replaced by a single literal "%" character; * %[/]F replaced by the string passed as "file"; * %[/]M replaced by the string passed as "modifier"; * %[/]R replaced by the "APPROOT" environment string. * * Any other character present in "fmt" is copied literally. In * the case of "%F", "%M" and "%R", inclusion of the optional "/" * flag causes a single "/" character to be inserted before the * substitute string, provided this is not NULL; ("\\" may be * used, but is not recommended, in place of the "/" flag). */ char c; int len = 0; /* Scan "fmt"... */ do { if( (c = *fmt++) == '%' ) { /* Interpret substitution tags... */ char flag = *fmt; const char *subst = NULL; /* ...checking for presence of a "/" flag... */ if( ((flag == '/') || (flag == '\\')) && fmt[1] ) /* * ...and, when found, with a possibly valid format spec, * advance to parse that spec... */ ++fmt; else /* ...establish absence of the flag. */ flag = '\0'; switch( c = *fmt++ ) { case 'F': /* * Schedule substitution of text specified as "file". */ subst = file; break; case 'M': /* * Schedule substitution of text specified as "modifier", */ subst = modifier; break; case 'R': /* * Schedule substitution from the "APPROOT" environment string, */ subst = getenv( "APPROOT" ); break; case '%': /* * Interpreting "%%", but may have been "%/%", which is invalid... */ if( flag == '\0' ) { /* * It was just "%%", so store a literal "%" character. */ if( buf != NULL ) *buf++ = '%'; ++len; break; } /* If we get to here, it was the invalid "%/%" form; backtrack, * and fall through to emit literal "%/", then resume parsing, * treating the second "%" as the possible starting character * of a new format specification. */ c = flag; --fmt; default: if( buf != NULL ) { /* Store the literal "%" character, * followed by the unrecognised tag character. */ *buf++ = '%'; *buf++ = c; } len += 2; } if( subst != NULL ) { /* Perform scheduled substitution of "file", "modifier" * or the APPROOT environment string... */ if( flag ) { ++len; if( buf != NULL ) *buf++ = flag; } while( *subst ) { /* ...counting and copying character by character. */ ++len; if( buf != NULL ) *buf++ = *subst; ++subst; } } } else { /* * Copy one literal character from "fmt"... */ if( buf != NULL ) /* * ...storing as necessary... */ *buf++ = c; /* ...and counting it anyway. */ ++len; } } while( c ); /* Always return the total number of characters which were, or would * have been transferred to "buf". */ return len; } static void create_parent_directory_hierarchy( const char *pathname, int mode ) { /* Recursive helper function to create a directory branch, including * all missing parent directories, (analogous to using "mkdir -p"). * * FIXME: We allow for either "/" or "\" as the directory separator; * do we also need to accommodate possible use of multibyte-character * encodings? (Hopefully, archives should rely exclusively on the * POSIX Portable Character set, i.e. 7-bit ASCII, so maybe not). */ char *parse, *parent, *mark = NULL, *stop = NULL; /* We work with a copy of the supplied "pathname", so we can guarantee * we have a modifiable string, and can accept a "const" input string. */ if( (parse = parent = strdup( pathname )) != NULL ) { /* Having obtained a valid copy of "pathname", we parse it... */ while( *parse ) { /* Set the "stop" mark at the first in the last sequence of * one or more directory separators detected, (if any)... */ stop = mark; /* * ...then step over any following characters which are * neither the string terminator, nor further separators. */ while( *parse && (*parse != '/') && (*parse != '\\') ) ++parse; /* If we haven't yet found the string terminator, then we * must have found a new sequence of one or more separators; * mark it as a new candidate "stop" mark location. */ if( *parse ) mark = parse; /* Now, step over all contiguous separators in the current * sequence, before restarting the outer loop, to parse the * next directory or file name, (if any). Note that we defer * updating the "stop" mark until the start of this new cycle; * this ensures that we correctly ignore any separators which * trail at the end of "pathname", with no following "name" * component. */ while( (*parse == '/') || (*parse == '\\') ) ++parse; } if( stop != NULL ) { /* We found a valid point, at which to split the current leaf * of "pathname" from its parent branch hierarchy; split it and * recurse through the "mkdir" function, to create the parent * directory hierarchy, as required. */ *stop = '\0'; mkdir_recursive( parent, mode ); } /* We are now done with our temporary copy of "pathname"; reclaim * the memory which was allocated to store it. */ free( parent ); } } int mkdir_recursive( const char *pathname, int mode ) { /* Public entry point for the recursive "mkdir" function. * * First, we attempt a simple "mkdir"; if this succeeds, the * parent directory branch is already in place, and we have * nothing more to do. */ if( mkdir( pathname, mode ) == 0 ) return 0; /* Otherwise... */ switch( errno ) { case ENOENT: /* * This indicates a gap in the parent branch hierarchy; * call the preceding helper, to fill the gap... */ create_parent_directory_hierarchy( pathname, mode ); /* * ...before making a further attempt to add the leaf. */ return mkdir( pathname, mode ); case EEXIST: { /* Here, the initial "mkdir" failed because a file * system entity called "pathname" already exists; if * this is already a directory, all is well; (there is * no need to create it again)... */ struct stat target; if( (stat( pathname, &target ) == 0) && S_ISDIR( target.st_mode ) ) return 0; } } /* ...otherwise we simply fall through and fail... */ return -1; } int set_output_stream( const char *pathname, int mode ) { /* Attach the extractor's output data stream to a specified file, * creating the parent directory branch hierarchy as required, and * return a file descriptor on the stream. */ int fd; /* First, simply attempt to create the destination file... */ if( ((fd = creat( pathname, mode )) < 0) && (errno == ENOENT) ) { /* ...but, on failure due to a gap in the directory structure * call the preceding helper to create the necessary hierarchy... */ create_parent_directory_hierarchy( pathname, 0755 ); /* * ...before making a further attempt to create the file. */ return creat( pathname, mode ); } /* Here, we will have the invalid file descriptor from the initial * failed attempt to create the file; we return it to indicate the * ultimate failure to create this file. */ return fd; } /* $RCSfile: mkpath.c,v $: end of file */ --- NEW FILE: pkgtask.h --- #ifndef PKGTASK_H /* * pkgtask.h * * $Id: pkgtask.h,v 1.1 2009/11/16 21:54:30 keithmarshall Exp $ * * Written by Keith Marshall <kei...@us...> * Copyright (C) 2009, MinGW Project * * * This header provides manifest definitions for the action codes, * which are used by the installer engine's task scheduler. * * * 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. * */ #define PKGTASK_H 1 enum { action_none = 0, action_remove, action_install, action_upgrade, end_of_actions }; #define ACTION_MASK 0x0F #define ACTION_NONE (unsigned long)(action_none) #define ACTION_REMOVE (unsigned long)(action_remove) #define ACTION_INSTALL (unsigned long)(action_install) #define ACTION_UPGRADE (unsigned long)(action_upgrade) #define STRICTLY_GT ACTION_MASK + 1 #define STRICTLY_LT STRICTLY_GT << 1 #ifndef EXTERN_C # ifdef __cplusplus # define EXTERN_C extern "C" # else # define EXTERN_C # endif #endif EXTERN_C const char *action_name( unsigned long ); EXTERN_C int action_code( const char* ); #endif /* PKGTASK_H: $RCSfile: pkgtask.h,v $: end of file */ --- NEW FILE: pkgbase.h --- #ifndef PKGBASE_H /* * pkgbase.h * * $Id: pkgbase.h,v 1.1 2009/11/16 21:54:30 keithmarshall Exp $ * * Written by Keith Marshall <kei...@us...> * Copyright (C) 2009, MinGW Project * * * Public interface for the package directory management routines; * declares the XML data structures, and their associated class APIs, * which are used to describe packages and their interdependencies. * * * 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. * */ #define PKGBASE_H 1 #include <tinyxml.h> #include <tinystr.h> #ifndef EXTERN_C # ifdef __cplusplus # define EXTERN_C extern "C" # else # define EXTERN_C # endif #endif class pkgXmlNode : public TiXmlElement { /* A minimal emulation of the wxXmlNode class, founded on * the tinyxml implementation of the TiXmlElement class, and * subsequently extended by application specific features. */ public: /* Constructors... */ inline pkgXmlNode( const char* name ):TiXmlElement( name ){} inline pkgXmlNode( const pkgXmlNode& src ):TiXmlElement( src ){} /* Accessors... */ inline const char* GetName() { /* Retrieve the identifying name of the XML tag; * tinyxml calls this the element "value"... */ return Value(); } inline pkgXmlNode* GetParent() { /* wxXmlNode provides this equivalant of tinyxml's * Parent() method. */ return (pkgXmlNode*)(Parent()); } inline pkgXmlNode* GetChildren() { /* wxXmlNode provides only this one method to access * the children of an element; it is equivalent to the * FirstChild() method in tinyxml's arsenal. */ return (pkgXmlNode*)(FirstChild()); } inline pkgXmlNode* GetNext() { /* This is wxXmlNode's method for visiting other children * of an element, after the first found by GetChildren(); * it is equivalent to tinyxml's NextSibling(). */ return (pkgXmlNode*)(NextSibling()); } inline const char* GetPropVal( const char* name, const char* subst ) { /* tinyxml has no direct equivalent for this wxXmlNode method, * (which substitutes default "subst" text for an omitted property), * but it may be trivially emulated, using the Attribute() method. */ const char* retval = Attribute( name ); return retval ? retval : subst; } /* Additional methods specific to the application. */ inline bool IsElementOfType( const char* tagname ) { /* Confirm if the owner XML node represents a data element * with the specified "tagname". */ return strcmp( GetName(), tagname ) == 0; } /* The following pair of methods provide an iterator * for enumerating the contained nodes, within the owner, * which themselves exhibit a specified tagname. */ pkgXmlNode* FindFirstAssociate( const char* tagname ); pkgXmlNode* FindNextAssociate( const char* tagname ); /* Specific to XML node elements of type "release", * the following pair of methods retrieve the actual name of * the release tarball, and its associated source code tarball, * as they are named on the project download servers. */ const char* ArchiveName(); const char* SourceArchiveName(); }; class pkgActionItem { /* A class implementing a bi-directionally linked list of * "action" descriptors, which is to be associated with the * pkgXmlDocument class, specifying actions to be performed * on the managed software installation. */ private: /* Pointers to predecessor and successor in the linked list * comprising the schedule of action items. */ pkgActionItem* prev; pkgActionItem* next; /* Flags define the specific action associated with this item. */ unsigned long flags; /* Criteria for selection of package versions associated with * this action item. */ const char* min_wanted; const char* max_wanted; /* Pointer to the XML database entry for the package selected * for processing by this action. */ pkgXmlNode* selection; /* Method for retrieving packages from a distribution server. */ void DownloadArchiveFiles( pkgActionItem* ); public: /* Constructor... */ pkgActionItem( pkgActionItem* = NULL, pkgActionItem* = NULL ); /* Methods for assembling action items into a linked list. */ pkgActionItem* Append( pkgActionItem* = NULL ); pkgActionItem* Insert( pkgActionItem* = NULL ); /* Methods for compiling the schedule of actions. */ pkgActionItem* GetReference( pkgActionItem& ); pkgActionItem* Schedule( unsigned long, pkgActionItem& ); /* Methods for defining the selection criteria for * packages to be processed. */ const char* SetRequirements( pkgXmlNode* ); pkgXmlNode* SelectIfMostRecentFit( pkgXmlNode* ); /* Method specifying where downloaded packages are stored. */ const char* ArchivePath(); /* Method for processing all scheduled actions. */ void Execute(); }; class pkgXmlDocument : public TiXmlDocument { /* Minimal emulation of the wxXmlDocument class, founded on * the tinyxml implementation of the TiXmlDocument class. */ public: /* Constructors... */ inline pkgXmlDocument( const char* name ) { /* tinyxml has a similar constructor, but unlike xwXmlDocument, * it DOES NOT automatically load the document; force it. */ LoadFile( name ); /* Always begin with an empty actions list. */ actions = NULL; } /* Accessors... */ inline bool IsOk() { /* tinyxml doesn't have this, but instead provides a complementary * `Error()' indicator, so to simulate `IsOk()'... */ return ! Error(); } inline pkgXmlNode* GetRoot() { /* This is wxXmlDocument's method for locating the document root; * it is equivalent to tinyxml's RootElement() method. */ return (pkgXmlNode *)(RootElement()); } private: /* Properties specifying the schedule of actions. */ unsigned long request; pkgActionItem* actions; /* Method to synchronise the state of the local package manifest * with the master copy held on the distribution server. */ void SyncRepository( const char*, pkgXmlNode* ); public: /* Method to merge content from repository-specific package lists * into the central XML package database. */ pkgXmlNode* BindRepositories(); /* Method to locate the XML database entry for a named package. */ pkgXmlNode* FindPackageByName( const char*, const char* = NULL ); /* Method to resolve the dependencies of a specified package, * by walking the chain of references specified by "requires" * elements in the respective package database entries. */ void ResolveDependencies( pkgXmlNode*, pkgActionItem* = NULL ); /* Methods for compiling a schedule of actions. */ void Schedule( unsigned long, const char* ); pkgActionItem* Schedule( unsigned long, pkgActionItem&, pkgActionItem* = NULL ); /* Method to execute a sequence of scheduled actions. */ inline void ExecuteActions(){ actions->Execute(); } }; EXTERN_C const char *xmlfile( const char*, const char* = NULL ); EXTERN_C int has_keyword( const char*, const char* ); static inline bool match_if_explicit( const char *value, const char *proto ) { /* Helper to compare a pair of "C" strings for equality, * accepting NULL as a match for anything. */ return (value == NULL) || (proto == NULL) || (strcmp( value, proto ) == 0); } #endif /* PKGBASE_H: $RCSfile: pkgbase.h,v $: end of file */ --- NEW FILE: pkgspec.cpp --- /* * pkgspec.cpp * * $Id: pkgspec.cpp,v 1.1 2009/11/16 21:54:30 keithmarshall Exp $ * * Written by Keith Marshall <kei...@us...> * Copyright (C) 2009, MinGW Project * * * Implementation for the "pkgTarName" class, as declared in header * file "pkginfo.h". * * * 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 "vercmp.h" #include <string.h> /* Constructors... */ pkgSpecs::pkgSpecs( const char *tarname ) { /* Parse the given tarball name, storing its constituent element * decomposition within the class' local "pkginfo" array structure. */ content = get_pkginfo( tarname ? tarname : "", specs ); } pkgSpecs::pkgSpecs( pkgXmlNode *release ) { /* Retrieve the "tarname" from an XML "release" specification, * then construct the "pkgSpecs" as if it were specified directly. */ const char *tarname = release ? release->GetPropVal( "tarname", NULL ) : NULL; content = get_pkginfo( tarname ? tarname : "", specs ); } /* Copy constructor... */ static void *clone_specs( char *content, pkginfo_t const src, pkginfo_t dst ) { /* Local helper function performs a deep copy of the "content" buffer, * and assigns the "specs" pointers to refer to it; this is the action * required to implement the copy constructor, and it is also used by * the assignment operator implentation. */ char *rtn; int count = PACKAGE_TAG_COUNT; /* Find the last allocated pointer in the source "specs" list; this * tells us where to find the last string in the "content" buffer... */ while( (count > 0) && (src[--count] == NULL) ) ; /* ...whence we may compute the size of the buffer, and allocate * a new buffer, into which to copy the data. */ count = src[count] + strlen( src[count] ) - content; if( (rtn = (char *)(malloc( count + 1))) != NULL ) { /* On successful buffer allocation, copy the data, * then walk the list of pointers... */ rtn = (char *)(memcpy( rtn, content, count )); for( count = 0; count < PACKAGE_TAG_COUNT; ++count ) { if( src[count] == NULL ) /* * ...propagating NULL pointers "as are"... */ dst[count] = NULL; else /* ...and non-NULL adjusted, as necessary, * to point into the copied data buffer... */ dst[count] = (char *)(rtn) + (src[count] - content); } } /* ...ultimately, returning the base address of the new buffer. */ return (void *)(rtn); } /* Formal implementation of the copy constructor... */ pkgSpecs::pkgSpecs( const pkgSpecs& src ) { /* ...requires no more than a call to the local helper function. */ content = clone_specs( (char *)(src.content), src.specs, specs ); } /* Assignment operator... */ pkgSpecs& pkgSpecs::operator=( const pkgSpecs& rhs ) { /* Provided the lhs and rhs represent distinct objects... */ if( this != &rhs ) { /* ...this is much the same as the copy constructor, except that, * while the constructor is guaranteed to be creating a new object, * assignment may be replacing an existing lhs object; this will * own a dynamically allocated data buffer, which must be freed, * to avoid leaking memory. */ free( content ); content = clone_specs( (char *)(rhs.content), rhs.specs, specs ); } return *this; } /* Destructor... */ pkgSpecs::~pkgSpecs() { /* Need to free the dynamic memory associated with the "specs" array, * and in which the actual tarname decomposition is stored. */ free( content ); } /* Comparison operators... */ static inline pkgVersionInfo version( pkgSpecs& pkg ) { /* Local helper, to construct package version descriptors * for use in version comparison operator implementations. */ return pkgVersionInfo( pkg.GetPackageVersion(), pkg.GetPackageBuild() ); } bool pkgSpecs::operator<( pkgSpecs& rhs ) { /* Check if the given package release is less recent, as indicated * by its version and build date/serial number, than another. */ return version( *this ) < version( rhs ); } bool pkgSpecs::operator<=( pkgSpecs& rhs ) { /* Check if the given package release is no more recent, as indicated * by its version and build date/serial number, than another. */ return version( *this ) <= version( rhs ); } bool pkgSpecs::operator>=( pkgSpecs& rhs ) { /* Check if the given package release is no less recent, as indicated * by its version and build date/serial number, than another. */ return version( *this ) >= version( rhs ); } bool pkgSpecs::operator>( pkgSpecs& rhs ) { /* Check if the given package release is more recent, as indicated * by its version and build date/serial number, than another. */ return version( *this ) > version( rhs ); } /* $RCSfile: pkgspec.cpp,v $: end of file */ |