OSDN Git Service

678347d4003bfc0182eb4aeac4176d84d25a1a5b
[mingw/mingw-get.git] / src / pkgdeps.cpp
1 /*
2  * pkgdeps.cpp
3  *
4  * $Id$
5  *
6  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7  * Copyright (C) 2009, 2010, 2011, 2012, MinGW Project
8  *
9  *
10  * Implementation of the package dependency resolver method, of the
11  * "pkgXmlDocument" class; includes the interface to the action item
12  * task scheduler, which is called to ensure that processing for any
13  * identified prerequisite packages is appropriately scheduled.
14  *
15  *
16  * This is free software.  Permission is granted to copy, modify and
17  * redistribute this software, under the provisions of the GNU General
18  * Public License, Version 3, (or, at your option, any later version),
19  * as published by the Free Software Foundation; see the file COPYING
20  * for licensing details.
21  *
22  * Note, in particular, that this software is provided "as is", in the
23  * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
24  * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
25  * PARTICULAR PURPOSE.  Under no circumstances will the author, or the
26  * MinGW Project, accept liability for any damages, however caused,
27  * arising from the use of this software.
28  *
29  */
30 #include <string.h>
31
32 #include "dmh.h"
33 #include "dmhmsgs.h"
34 #include "debug.h"
35
36 #include "pkginfo.h"
37 #include "pkgbase.h"
38 #include "pkgkeys.h"
39 #include "pkgtask.h"
40 #include "pkgopts.h"
41
42 /* Define supplementary action codes, which may be used exclusively
43  * by pkgXmlDocument::ResolveDependencies(), to ensure that recursive
44  * actions are appropriately scheduled; these apply to user specified
45  * actions "upgrade --recursive", "install --recursive --reinstall",
46  * and "upgrade --recursive --reinstall" respectively.
47  */
48 #define ACTION_RECURSIVE_UPGRADE        (ACTION_UPGRADE | OPTION_RECURSIVE)
49 #define ACTION_RECURSIVE_REINSTALL      (ACTION_INSTALL | OPTION_ALL_DEPS)
50 #define ACTION_RECURSIVE_REPLACE        (ACTION_UPGRADE | OPTION_ALL_DEPS)
51
52 /* FIXME: the following declaration belongs in a future "pkgmsgs.h"
53  * header file, with the function implementation in a separate C or C++
54  * messages source file, with appropriate internationalisation...
55  */
56 EXTERN_C const char *pkgMsgUnknownPackage( void );
57
58 const char *pkgMsgUnknownPackage( void )
59 {
60   /* FIXME: (see note above); return English language only, for now.
61    */
62   return "%s: unknown package\n";
63 }
64
65 /* Provide a convenience macro for declaring static inline functions
66  * which we want to always expand inline.
67  */
68 #define STATIC_INLINE   static inline __attribute__((__always_inline__))
69
70 static bool is_installed( pkgXmlNode *release )
71 {
72   /* Helper to check installation status of a specified package release.
73    */
74   const char *status;
75   /*
76    * First, check for any 'installed' attribute which may have been
77    * explicitly specified for the 'release' record...
78    */
79   if( (status = release->GetPropVal( installed_key, NULL )) != NULL )
80     /*
81      * ...immediately returning the status deduced from it,
82      * when present.
83      */
84     return (strcmp( status, yes_value ) == 0);
85
86   /* When the package definition itself doesn't bear an explicit
87    * 'installed' attribute, then we must check the system map for
88    * an associated installation record...
89    */
90   const char *pkgname;
91   if( ((pkgname = release->GetPropVal( tarname_key, NULL )) != NULL)
92   &&   (release->GetInstallationRecord( pkgname ) != NULL)  )
93   {
94     /* ...and, when one is found, we can mark this package with
95      * an explicit status attribute for future reference, before
96      * we return confirmation of the installed status...
97      */
98     release->SetAttribute( installed_key, yes_value );
99     return true;
100   }
101
102   /* If we get to here, we may deduce that the package is not
103    * installed; once again, we set the explicit attribute value
104    * to convey this for future reference, before returning the
105    * appropriate status flag.
106    */
107   release->SetAttribute( installed_key, no_value );
108   return false;
109 }
110
111 pkgXmlNode *pkgXmlNode::GetInstallationRecord( const char *pkgname )
112 {
113   /* Retrieve the installation record, if any, for the package
114    * specified by fully qualified canonical 'pkgname'.
115    *
116    * First, break down the specified package name, and retrieve
117    * the sysroot database entry for its associated subsystem.
118    */
119   pkgXmlNode *sysroot;
120   pkgSpecs lookup( pkgname );
121   if( (sysroot = GetSysRoot( lookup.GetSubSystemName() )) != NULL )
122   {
123     /* We successfully retrieved a sysroot entry; now we must
124      * search the associated list of installed packages, for one
125      * with the appropriate canonical package name.
126      */
127     pkgXmlNode *pkg = sysroot->FindFirstAssociate( installed_key );
128     while( pkg != NULL )
129     {
130       /* We found an installed package entry; check if it has
131        * the correct canonical name...
132        */
133       const char *installed = pkg->GetPropVal( tarname_key, NULL );
134       if( (installed != NULL) && (strcmp( installed, pkgname ) == 0) )
135         /*
136          * ...returning this entry if so...
137          */
138         return pkg;
139
140       /* ...otherwise, move on to the next entry, if any.
141        */
142       pkg = pkg->FindNextAssociate( installed_key );
143     }
144   }
145
146   /* If we get to here, we didn't find an entry for the required
147    * package; return NULL, indicating that it is not installed.
148    */
149   return NULL;
150 }
151
152 const char *pkgXmlNode::GetContainerAttribute( const char *key, const char *sub )
153 {
154   /* Walk the XML path from current element, back towards the document root,
155    * until we find the innermost element which has an attribute matching "key";
156    * if such an element is found, return the value of the attribute; if we have
157    * traversed the entire path, all the way to the document root, and we have
158    * not found any element with the "key" attribute, return "sub".
159    */
160   pkgXmlNode *pkg = this;
161   pkgXmlNode *root = pkg->GetDocumentRoot();
162   while( pkg != NULL )
163   {
164     /* We haven't yet tried to search beyond the document root;
165      * try matching "key" to an attribute of the current element...
166      */
167     const char *retval = pkg->GetPropVal( key, NULL );
168     if( retval != NULL )
169       /*
170        * ...returning its value, if such an attribute is found...
171        */
172       return retval;
173
174     /* ...otherwise,
175      * take a further step back towards the document root.
176      */
177     pkg = (pkg == root) ? NULL : pkg->GetParent();
178   }
179
180   /* If we get to here, then no element with the required "key"
181    * attribute could be found; substitute the specified default.
182    */
183   return sub;
184 }
185
186 DEBUG_INVOKED static int indent = -1;
187
188 DEBUG_INVOKED static void
189 DEBUG_INVOKED show_required( pkgSpecs *req )
190 DEBUG_INVOKED {
191 DEBUG_INVOKED   const char *tarname = NULL;
192 DEBUG_INVOKED   dmh_printf( "%*s require: %s\n", indent, "", req->GetTarName( tarname ) );
193 DEBUG_INVOKED   free( (void *)(tarname) );
194 DEBUG_INVOKED }
195
196 static inline
197 bool is_abi_compatible( pkgSpecs *refdata, const char *version )
198 {
199   /* Local helper, used by pkgXmlDocument::ResolveDependencies(),
200    * to confirm that the ABI identification number of a selected
201    * component package is an exact match to a requirement spec.
202    */
203   const char *ref_version;
204   if( (ref_version = refdata->GetComponentVersion()) == NULL )
205     /*
206      * Here, confirm that both are unversioned...
207      */
208     return (version == NULL);
209
210   /* ...otherwise, fall through to check that both bear IDENTICALLY
211    * the same ABI version number.
212    */
213   return ((version != NULL) && (strcmp( version, ref_version ) == 0));
214 }
215
216 STATIC_INLINE unsigned long action_class
217 ( unsigned long requested, unsigned long viable )
218 {
219   /* Helper function for use by pkgXmlDocument::ResolveDependencies();
220    * it classifies each requested action with respect to any previously
221    * installed version of each package, to ensure that reinstallation
222    * and recursive requests are dispatched appropriately.
223    */
224   return viable ? requested : ACTION_RECURSIVE_UPGRADE;
225 }
226
227 STATIC_INLINE unsigned long with_request_flags( unsigned long request )
228 {
229   /* Helper function for use by pkgXmlDocument::ResolveDependencies();
230    * it isolates the request flags from the action code, to accommodate
231    * promotion of an alternative action with matching flags.
232    */
233   return request & ~(ACTION_MASK | ACTION_DOWNLOAD);
234 }
235
236 STATIC_INLINE unsigned long with_download( unsigned long action_code )
237 {
238   /* Helper function for use by pkgXmlDocument::ResolveDependencies();
239    * it adds the download request flag,when promoting any action which
240    * may require a package archive to be downloaded.
241    */
242   return  action_code | (ACTION_DOWNLOAD);
243 }
244
245 STATIC_INLINE unsigned long promote
246 ( unsigned long request, unsigned long action_code )
247 {
248   /* Helper function for use by pkgXmlDocument::ResolveDependencies();
249    * it promotes an alternative action to that explicitly requested by
250    * the user, when this is necessary to satisfy a dependency.
251    */
252   return with_request_flags( request ) | with_download( action_code );
253 }
254
255 void
256 pkgXmlDocument::ResolveDependencies( pkgXmlNode* package, pkgActionItem* rank )
257 {
258   /* For the specified "package", (nominally a "release"), identify its
259    * prerequisites, (as specified by "requires" tags), and schedule actions
260    * to process them; repeat recursively, to identify further dependencies
261    * of such prerequisites, and finally, extend the search to capture
262    * additional dependencies common to the containing package group.
263    */
264   pkgSpecs *refdata = NULL;
265   pkgXmlNode *refpkg = package;
266
267   DEBUG_INVOKED ++indent;
268
269   /* Capture the state of global option settings controlling the scope
270    * of recursive behaviour and reinstallation requests, so that we may
271    * implicitly extend the effect when processing virtual packages...
272    */
273   int request_mode = pkgOptions()->Test( OPTION_ALL_DEPS );
274   if( match_if_explicit( package->ArchiveName(), value_none ) )
275     /*
276      * ...such that the effect of an upgrade or a reinstall implicitly
277      * applies, through a single level of recursion, to the first level
278      * of requisite dependencies.
279      */
280     request_mode |= OPTION_RECURSIVE;
281   request_mode |= request & ACTION_MASK;
282
283   while( package != NULL )
284   {
285     /* Collect any debugging messages, relating to this dependency,
286      * into a single message digest.
287      */
288     DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
289         dmh_control( DMH_BEGIN_DIGEST )
290       );
291
292     /* We have a valid XML entity, which may identify dependencies;
293      * check if it includes any "requires" specification...
294      */
295     pkgXmlNode *dep = package->FindFirstAssociate( requires_key );
296     while( dep != NULL )
297     {
298       /* We found a dependency specification...
299        * Initially, assume this package is not installed.
300        */
301       pkgXmlNode *installed = NULL;
302       unsigned installed_is_viable = 0;
303
304       /* To facilitate resolution of "%" version matching wildcards
305        * in the requirements specification, we need to parse the version
306        * specification for the current dependent package...
307        */
308       if( refdata == NULL )
309       {
310         /* ...but we deferred that until we knew for sure that it would
311          * be needed; it is, so parse it now.
312          */
313         const char *refname;
314         if( (refname = refpkg->GetPropVal( tarname_key, NULL )) != NULL )
315         {
316           DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
317               dmh_printf( "%*s%s: resolve dependencies\n", indent, "", refname )
318             );
319           refdata = new pkgSpecs( refname );
320         }
321       }
322
323       /* Identify the prerequisite package, from its canonical name...
324        */
325       pkgActionItem wanted; pkgXmlNode *selected;
326       pkgSpecs req( wanted.SetRequirements( dep, refdata ) );
327       DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
328           show_required( &req )
329         );
330       /*
331        * (Both the package name, and subsystem if specified, must match)...
332        */
333       selected = FindPackageByName( req.GetPackageName(), req.GetSubSystemName() );
334
335       /* When we've identified the appropriate package...
336        */
337       if( selected != NULL )
338       {
339         /* ...and, more significantly, the appropriate component package,
340          * where applicable...
341          */
342         pkgXmlNode *component; const char *reqclass;
343         if( (reqclass = req.GetComponentClass()) == NULL )
344           reqclass = value_unknown;
345
346         if( (component = selected->FindFirstAssociate( component_key )) == NULL )
347           /*
348            * ...but if no separate component package exists,
349            * consider the parent package itself, as sole component.
350            */
351           component = selected;
352
353         /* At this point, we have no more than a tentative package selection;
354          * it may not provide a component to fit the requirements specification.
355          * Thus, kill the selection, pending reaffirmation...
356          */
357         selected = NULL;
358         while( component != NULL )
359         {
360           /* ...by stepping through the "releases" of this component package...
361           */
362           pkgXmlNode *required = component->FindFirstAssociate( release_key );
363           while( required != NULL )
364           {
365             /* ...noting if we find one already marked as "installed"...
366             */
367             const char *tstclass;
368             DEBUG_INVOKED const char *already_installed = "";
369             pkgSpecs tst( tstclass = required->GetPropVal( tarname_key, NULL ) );
370             DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
371                 dmh_printf( "%*s  considering: %s", indent, "", tstclass )
372               );
373             if( (tstclass = tst.GetComponentClass()) == NULL )
374               tstclass = value_unknown;
375
376             if( is_installed( required ) && (strcmp( tstclass, reqclass ) == 0)
377             /*
378              * We found an installed version of the requisite component,
379              * but we ignore it unless it is ABI version compatible with
380              * the version we need; (the intent of ABI versioning is to
381              * accommodate multiple concurrent installations of shared
382              * objects supporting the differing ABI specifications).
383              */
384             &&  is_abi_compatible( &tst, req.GetComponentVersion() )  )
385             {
386               installed = required;
387               DEBUG_INVOKED already_installed = " (already installed)";
388             }
389             /* ...and identify the most suitable candidate "release"
390              * to satisfy the current dependency...
391              */
392             if( wanted.SelectIfMostRecentFit( required ) == required )
393               selected = component = required;
394
395             if( required == installed )
396               installed_is_viable = wanted.HasAttribute( ACTION_MAY_SELECT );
397
398             DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
399                 dmh_printf( "%s%s\n", wanted.HasAttribute( ACTION_MAY_SELECT )
400                   ? ": viable candidate"
401                   : "",
402                   already_installed
403                 )
404               );
405
406             /* ...continuing, until all available "releases"
407              * have been evaluated accordingly.
408              */
409             required = required->FindNextAssociate( release_key );
410           }
411
412           /* Where multiple component packages do exist,
413            * continue until all have been inspected.
414            */
415           component = component->FindNextAssociate( component_key );
416         }
417
418         /* We have now identified the most suitable candidate package,
419          * to resolve the current dependency...
420          */
421         if( installed )
422         {
423           /* ...this package is already installed, so we may schedule
424            * a resolved dependency match, with no pending action...
425            */
426           unsigned long fallback = with_request_flags( request );
427           switch( action_class( request_mode, installed_is_viable ) )
428           {
429             /* ...except that...
430              */
431             case ACTION_RECURSIVE_REINSTALL:
432               /*
433                * ...when the action is "install", with "--reinstall"
434                * and "--recursive" options in effect, we update the
435                * package selection to favour the already installed
436                * version over any available upgrade...
437                */
438               wanted.SelectPackage( selected = installed );
439               /*
440                * ...falling through to...
441                */
442
443             case ACTION_RECURSIVE_REPLACE:
444             case ACTION_RECURSIVE_UPGRADE:
445               /*
446                * ...schedule removal of the already installed version,
447                * for replacement by a fresh copy of the same version,
448                * or an upgraded version, as appropriate.
449                */
450               DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
451                   dmh_printf( "%*s%s: schedule replacement\n", indent + 2, "",
452                       installed->GetPropVal( tarname_key, value_unknown )
453                     )
454                 );
455               wanted.SelectPackage( installed, to_remove );
456               fallback |= with_download( ACTION_UPGRADE );
457               break;
458
459             default:
460               /* In any other case, the currently installed version is
461                * to be left in place; we must ensure that its dependencies,
462                * (if any), will be resolved with respect to this already
463                * installed version.
464                */
465               wanted.SelectPackage( selected = installed );
466           }
467
468           /* Schedule the appropriate fallback action, (which may be none),
469            * for the already installed package.
470            */
471           rank = Schedule( fallback, wanted, rank );
472         }
473
474 #       if 0
475         /* FIXME: this change in logic may introduce a regression; I (KDM)
476          * may need to re-evaluate the conceptual effect of PRIMARY actions
477          * vs. SECONDARY actions in this context, but this pre-v0.5 logic
478          * breaks the handling of meta-packages in v0.5
479          */
480         else if( ((request & ACTION_MASK) == ACTION_INSTALL)
481           /*
482            * The required package is not installed, so when
483            * we are performing an installation, ...
484            */
485         || ((request & (ACTION_PRIMARY | ACTION_INSTALL)) == ACTION_INSTALL) )
486 #       endif
487         else if( (request & ACTION_INSTALL) == ACTION_INSTALL )
488         {
489           /* ...or when this is a new requirement of a package
490            * which is being upgraded, then we must schedule it
491            * for installation now; (we may simply ignore it, if
492            * we are performing a removal).
493            */
494           DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
495               dmh_printf( "%*s%s: schedule installation (flags=0x%08x)\n",
496                   indent + 2, "", selected->GetPropVal( tarname_key, value_unknown ),
497                   promote( request, ACTION_INSTALL )
498                 )
499             );
500           rank = Schedule( promote( request, ACTION_INSTALL ), wanted, rank );
501         }
502
503         /* Regardless of the action scheduled, we must recursively
504          * consider further dependencies of the resolved prerequisite;
505          * (but note that attempting to do this would be pointless if
506          * the prerequisite package could not be identified).
507          *
508          * FIXME: do we need to do this, when performing a removal?
509          * Right now, I (KDM) don't think so...
510          */
511         if( (selected != NULL) && ((request & ACTION_INSTALL) != 0) )
512           ResolveDependencies( selected, rank );
513       }
514
515       if( selected == NULL )
516       {
517         /* No package matching the selection criteria could be found;
518          * report a dependency resolution failure in respect of each
519          * specified criterion...
520          */
521         const char *ref, *key[] = { lt_key, le_key, eq_key, ge_key, gt_key };
522         const char *requestor = refpkg->GetPropVal( tarname_key, value_unknown );
523
524         dmh_control( DMH_BEGIN_DIGEST );
525         dmh_notify( DMH_ERROR, PKGMSG_SPECIFICATION_ERROR );
526         dmh_notify( DMH_ERROR, "%s: requires...\n", requestor );
527         for( int i = 0; i < sizeof( key ) / sizeof( char* ); i++ )
528           if( (ref = dep->GetPropVal( key[i], NULL )) != NULL )
529           {
530             dmh_notify( DMH_ERROR, "%s: unresolved dependency (type '%s')\n", ref, key[i] );
531             dmh_notify( DMH_ERROR, "%s: cannot identify any providing package\n" );
532           }
533         dmh_notify( DMH_ERROR, "please report this to the package maintainer\n" );
534         dmh_control( DMH_END_DIGEST );
535       }
536
537       /* Continue, until all prerequisites of the current package
538        * have been evaluated.
539        */
540       dep = dep->FindNextAssociate( requires_key );
541     }
542
543     /* Flush out any digest of debugging messages, which may
544      * have been collected, in respect of this dependency.
545      */
546     DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_DEPENDENCIES ),
547         dmh_control( DMH_END_DIGEST )
548       );
549
550     /* Also consider any dependencies which may be common to
551      * all releases, or all components, of the current package;
552      * we do this by walking back through the XML hierarchy,
553      * searching for "requires" elements in all containing
554      * contexts, until we reach the root element.
555      */
556     package = (package == GetRoot()) ? NULL : package->GetParent();
557   }
558   DEBUG_INVOKED --indent;
559   delete refdata;
560 }
561
562 STATIC_INLINE bool if_noref( const char *name )
563 {
564   /* Helper function, used exclusively by the following assert_unmatched()
565    * function; it is used to confirm that "name" represents nothing.
566    */
567   return (name == NULL) || (*name == '\0');
568 }
569
570 STATIC_INLINE bool if_match( const char *ref, const char *name )
571 {
572   /* Helper function, used exclusively by the following assert_unmatched()
573    * function; it is used to confirm that the package identified by "name"
574    * is an exact match for that represented by "ref".
575    */
576   return (name != NULL) && (strcmp( ref, name ) == 0);
577 }
578
579 STATIC_INLINE bool if_alias( const char *ref, const char *list )
580 {
581   /* Helper function, used exclusively by the following assert_unmatched()
582    * function; it is used to confirm that the "list" of package name aliases
583    * includes one which is an exact match for "ref".
584    */
585   return (list != NULL) && has_keyword( ref, list );
586 }
587
588 STATIC_INLINE bool assert_unmatched
589 ( const char *ref, const char *val, const char *name, const char *alias )
590 {
591   /* Helper for the following "assert_installed" function; it determines if
592    * the reference name specified by "ref" matches either the corresponding
593    * field value "val" from a tarname look-up, or in the case of a package
594    * name reference, the containing package "name" attribute or any of its
595    * specified "alias" names.  The return value is false, in the case of a
596    * match, or true when unmatched.
597    */
598   return (ref == NULL)
599
600     ? /* When "ref" is NULL, then a match requires all specified candidates
601        * for matching to also be NULL, or to be pointers to empty strings.
602        */
603       !( if_noref( val ) && if_noref( name ) && if_noref( alias ))
604
605     : /* Otherwise, when "ref" is not NULL, then a match is identified when
606        * any one candidate is found to match.
607        */
608       !( if_match( ref, val ) || if_match( ref, name ) || if_alias( ref, alias ));
609 }
610
611 static
612 pkgXmlNode *assert_installed( pkgXmlNode *current, pkgXmlNode *installed )
613 {
614   /* Validation hook for pkgXmlDocument::Schedule(); it checks for
615    * possible prior installation of an obsolete version of a current
616    * package, (i.e. the package itself is listed in the distribution
617    * manifest, but the listing for the installed version has been
618    * removed).
619    *
620    * Note that, by the time this helper is called, an installation
621    * may have been identified already, by a release reference which
622    * is still present in the distribution manifest; we perform this
623    * check, only if no such identification was possible.
624    */
625   if( current && (installed == NULL) )
626   {
627     /* This is the specific case where we have selected a current
628      * package for processing, but we HAVE NOT been able to identify
629      * a prior installation through a distribution manifest reference;
630      * thus, we must perform the further check for prior installation
631      * of an obsolete version.
632      *
633      * Starting from the sysroot record for the specified release...
634      */
635     pkgXmlNode *sysroot; const char *tarname;
636     pkgSpecs lookup( current->GetPropVal( tarname_key, NULL ) );
637     if( (sysroot = current->GetSysRoot( lookup.GetSubSystemName() )) != NULL )
638     {
639       /* ...identify the first, if any, package installation record.
640        */
641       pkgXmlNode *ref = sysroot->FindFirstAssociate( installed_key );
642       if( ref != NULL )
643       {
644         /* When at least one installation record exists,
645          * establish references for the "tarname" fields which
646          * we must match, to identify a prior installation.
647          */
648         const char *refname = lookup.GetPackageName();
649         const char *cptname = lookup.GetComponentClass();
650         const char *version = lookup.GetComponentVersion();
651
652         /* Also identify the formal name for the containing package,
653          * and any aliases by which it may also be known, so that we
654          * may be able to identify a prior installation which may
655          * have borne a deprecated package name.
656          */
657         const char *pkgname = current->GetContainerAttribute( name_key );
658         const char *alias = current->GetContainerAttribute( alias_key );
659
660         /* For each candidate installation record found...
661          */
662         while( ref != NULL )
663         {
664           /* ...check if it matches the look-up criteria.
665            */
666           pkgSpecs chk( tarname = ref->GetPropVal( tarname_key, NULL ) );
667           if( assert_unmatched( chk.GetPackageName(), refname, pkgname, alias )
668           ||  assert_unmatched( chk.GetComponentClass(), cptname, NULL, NULL )
669           ||  assert_unmatched( chk.GetComponentVersion(), version, NULL, NULL )  )
670             /*
671              * This candidate isn't a match; try the next, if any...
672              */
673             ref = ref->FindNextAssociate( installed_key );
674
675           else
676           { /* We found a prior installation of a deprecated version;
677              * back-build a corresponding reference within the associated
678              * package or component-package inventory, in the internal
679              * copy of the distribution manifest...
680              */
681             if( (installed = new pkgXmlNode( release_key )) != NULL )
682             {
683               installed->SetAttribute( tarname_key, tarname );
684               installed->SetAttribute( installed_key, value_yes );
685               if( (ref = current->GetParent()) != NULL )
686                 installed = ref->AddChild( installed );
687             }
688             /* Having found a prior installation, there is no need to
689              * check any further installation records; force "ref" to
690              * NULL, to inhibit further searching.
691              */
692             ref = NULL;
693           }
694         }
695       }
696     }
697   }
698   /* However we get to here, we always return the pointer to the installed
699    * package entry identified by the dependency resolver, which may, or may
700    * not have been modified by this function.
701    */
702   return installed;
703 }
704
705 void pkgActionItem::ConfirmInstallationStatus()
706 {
707   /* Set the "to_remove" selection in an action item to match the installed
708    * package entry, even when the release in question is no longer enumerated
709    * in the package catalogue; (used to identify any installed version when
710    * compiling a package reference listing).
711    */
712   selection[to_remove]
713     = assert_installed( selection[to_install], selection[to_remove] );
714 }
715
716 static inline
717 const char *get_version_bounds( const char *name )
718 {
719   /* Helper to locate any version bounds specification which may
720    * have been appended to a package name command line argument.
721    */
722   if( (name != NULL) && *name )
723     /*
724      * If the name is specified, and not zero-length, then
725      * the bounds specification begins at the first '<', '=',
726      * or '>' character, (if any)...
727      */
728     do { if( (*name == '<') || (*name == '=') || (*name == '>') )
729            /*
730             * ...in which case, we return that location.
731             */
732            return name;
733        } while( *++name );
734
735   /* Otherwise we fall through, returning NULL to indicate
736    * that no bounds specification is present.
737    */
738   return NULL;
739 }
740
741 void pkgActionItem::ApplyBounds( pkgXmlNode *release, const char *bounds )
742 {
743   /* Method to interpret a user specified version requirement,
744    * and attach it to a primary action item, much as if it were
745    * an internally identified requirement, as identified by the
746    * dependency resolver.
747    */
748   const char *refname;
749   pkgSpecs refspec( release );
750
751   while( (bounds != NULL) && *bounds )
752   {
753     /* Parse the user specified requirement, formulating a specification
754      * which may be interpreted by "pkginfo"; (use an arbitrary package
755      * name of "x", since we care only about the version number)...
756      */
757     const char *condition = NULL;
758     char spec_string[7 + strlen( bounds )]; strcpy( spec_string, "x-" );
759     char *p = spec_string + strlen( spec_string ) - 1;
760     switch( *bounds )
761     {
762       /* ...identifying it as...
763        */
764       case '=':
765         /*
766          * ...an equality requirement...
767          */
768         condition = eq_key;
769         ++bounds;
770         break;
771
772       case '<':
773         if( *++bounds == '=' )
774         {
775           /* ...less than or equal
776            * (<=; inclusive upper bound)...
777            */
778           condition = le_key;
779           ++bounds;
780         }
781         else
782           /* ...less than (exclusive upper bound)...
783            */
784           condition = lt_key;
785         break;
786
787       case '>':
788         if( *++bounds == '=' )
789         {
790           /* ...greater than or equal
791            * (>=; inclusive lower bound)...
792            */
793           condition = ge_key;
794           ++bounds;
795         }
796         else
797           /* ...or greater than (exclusive lower bound).
798            */
799           condition = gt_key;
800         break;
801     }
802     do { /* Accumulate characters into the local copy of the specification,
803           * until we encounter the end of the user specified string, or the
804           * start of a second specification, (e.g. the second limit for a
805           * version number range specification).
806           */
807          *++p = ((*bounds == '<') || (*bounds == '=') || (*bounds == '>'))
808            ? '\0' : *bounds;
809          if( *p ) ++bounds;
810        } while( *p );
811
812     /* Append an arbitrary component classification of "y", and an archive
813      * type of "z", because "pkginfo" requires them, then interpret as a
814      * "pkginfo" data structure...
815      */
816     strcpy( p, "-y.z" );
817     pkgSpecs usrspec( spec_string );
818
819     /* ...then extract the version fields of interest, and insert them
820      * into the actual working reference specification...
821      */
822     refspec.SetPackageVersion( usrspec.GetPackageVersion() );
823     refspec.SetPackageBuild( usrspec.GetPackageBuild() );
824     if( (refname = usrspec.GetSubSystemVersion()) != NULL )
825     {
826       /* ...including the subsystem version, if any, which the user may
827        * have specified...
828        */
829       refspec.SetSubSystemVersion( refname );
830       refspec.SetSubSystemBuild( usrspec.GetSubSystemBuild() );
831     }
832     else
833       /* ...or allowing a wild-card match otherwise.
834        */
835       refspec.SetSubSystemVersion( "*" );
836
837     /* Convert the reference specification to "tarname" format...
838      */
839     if( (refname = refspec.GetTarName()) != NULL )
840     {
841       /* ...and construct a temporary "requires" specification from it.
842        */
843       pkgXmlNode requisite( requires_key );
844       requisite.SetAttribute( condition, refname );
845
846       /* Set the action item requirements to honour this...
847        */
848       SetRequirements( &requisite, &refspec );
849
850       /* ...then release the heap memory used to temporarily store the
851        * "tarname" attribute for this; (the remaining data associated
852        * with this wil be automatically discarded, when the temporary
853        * specification goes out of scope).
854        */
855       free( (void *)(refname) );
856     }
857   }
858 }
859
860 pkgActionItem *pkgActionItem::GetReference( pkgXmlNode *package )
861 {
862   /* Method to locate a scheduled action, if any, which relates
863    * to a specified package.
864    */
865   if( this != NULL )
866   {
867     /* The schedule of actions is not empty.  Assume that the
868      * initial 'this' pointer is closer to the end, than to the
869      * beginning of the list of scheduled actions, and...
870      */
871     pkgActionItem *item = this;
872     while( item->next != NULL )
873       /*
874        * ...advance, to locate the very last entry in the list.
875        */
876       item = item->next;
877
878     /* Now, working backward toward the beginning of the list...
879      */
880     while( item != NULL )
881     {
882       /* ...identify a "release" specification associated with
883        * each action item in turn...
884        */
885       pkgXmlNode *ref = item->Selection();
886       if( (ref != NULL) || ((ref = item->Selection( to_remove )) != NULL) )
887       {
888         /* ...convert this to an actual component package, or
889          * full package, reference...
890          */
891         while( ref->IsElementOfType( release_key ) )
892           ref = ref->GetParent();
893
894         /* ...and, if it matches the search target, return it.
895          */
896         if( ref == package ) return item;
897       }
898       /* ...or, when we haven't yet found a matching package,
899        * try the preceding scheduled action item, if any.
900        */
901       item = item->prev;
902     }
903   }
904   /* If we fall through to here, then we found no action to be
905    * performed on the specified package; report accordingly.
906    */
907   return NULL;
908 }
909
910 static void dmh_notify_no_match
911 ( const char *name, pkgXmlNode *package, const char *bounds )
912 {
913   /* Diagnostic helper, called when the user has requested a specific
914    * package version for which there is no exactly matching release.
915    */
916   dmh_control( DMH_BEGIN_DIGEST );
917   dmh_notify( DMH_ERROR, "there is no release matching %s%s\n",
918       name, (bounds == NULL) ? "" : bounds
919     );
920   if( (package = package->FindFirstAssociate( release_key )) != NULL )
921   {
922     /* To assist the user in formalising a more appropriate
923      * version specification...
924      */
925     dmh_notify( DMH_ERROR, "available candidate releases are...\n" );
926     while( package )
927     {
928       /* ...display a list of available release tarballs...
929        */
930       const char *tarname = package->GetPropVal( tarname_key, NULL );
931       if( tarname != NULL )
932         dmh_notify( DMH_ERROR, " %s\n", tarname );
933
934       /* ...cycling, until all have been identified.
935        */
936       package = package->FindNextAssociate( release_key );
937     }
938   }
939   dmh_control( DMH_END_DIGEST );
940 }
941
942 pkgActionItem* pkgXmlDocument::Schedule( unsigned long action, const char* name )
943 {
944   /* Task scheduler interface; schedules actions to process all
945    * dependencies for the package specified by "name", honouring
946    * any appended version bounds specified for the parent.
947    */
948   if( this == NULL )
949     /*
950      * An unassigned XML database document cannot have any
951      * assigned action; bail out, with appropriate status.
952      */
953     return NULL;
954
955   /* We may call this method without any assigned package name,
956    * in which case, we interpret it as a request to return the
957    * list of previously scheduled actions...
958    */
959   if( name != NULL )
960   {
961     /* ...but when a package name is specified, then we
962      * proceed to schedule the specified action on it.
963      */
964     char scratch_pad[strlen( name )];
965     const char *bounds_specification = get_version_bounds( name );
966     if( bounds_specification != NULL )
967     {
968       /* Separate any version bounds specification from
969        * the original package name specification.
970        */
971       size_t scratch_pad_len = bounds_specification - name;
972       name = (const char *)(memcpy( scratch_pad, name, scratch_pad_len ));
973       scratch_pad[scratch_pad_len] = '\0';
974     }
975
976     pkgXmlNode *release;
977     if( (release = FindPackageByName( name )) != NULL )
978     {
979       /* We found the specification for the named package...
980        */
981       pkgXmlNode *component = release->FindFirstAssociate( component_key );
982       if( component != NULL )
983         /*
984          * When it is subdivided into component-packages,
985          * we need to consider each as a possible candidate
986          * for task scheduling.
987          */
988         release = component;
989
990       while( release != NULL )
991       {
992         /* Within each candidate package or component-package...
993          */
994         pkgXmlNode *package = release;
995         if( (release = release->FindFirstAssociate( release_key )) != NULL )
996         {
997           /* ...initially assume it is not installed, and that
998            * no installable upgrade is available.
999            */
1000           pkgActionItem latest;
1001           pkgXmlNode *installed = NULL, *upgrade = NULL;
1002
1003           /* Establish the action for which dependency resolution is
1004            * to be performed; note that this may be promoted to a more
1005            * inclusive class, during resolution, so we need to reset
1006            * it for each new dependency which may be encountered.
1007            */
1008           request = action;
1009
1010           /* Any action request processed here is, by definition,
1011            * a request for a primary action; mark it as such.
1012            */
1013           action |= ACTION_PRIMARY;
1014
1015           /* When the user has given a version bounds specification,
1016            * then we must assign appropriate action item requirements.
1017            */
1018           if( bounds_specification != NULL )
1019             latest.ApplyBounds( release, bounds_specification );
1020
1021           /* For each candidate release in turn...
1022            */
1023           while( release != NULL )
1024           {
1025             /* ...inspect it to identify any which is already installed,
1026              * and also the latest available...
1027              */
1028             if( is_installed( release ) )
1029               /*
1030                * ...i.e. here we have identified a release
1031                * which is currently installed...
1032                */
1033               latest.SelectPackage( installed = release, to_remove );
1034
1035             if( latest.SelectIfMostRecentFit( release ) == release )
1036               /*
1037                * ...while this is the most recent we have
1038                * encountered so far.
1039                */
1040               upgrade = release;
1041
1042             /* Continue with the next specified release, if any.
1043              */
1044             release = release->FindNextAssociate( release_key );
1045           }
1046
1047           if( (installed = assert_installed( upgrade, installed )) == NULL )
1048           {
1049             /* There is no installed version...
1050              * therefore, there is nothing to do for any action
1051              * other than ACTION_INSTALL...
1052              */
1053             if( (action & ACTION_MASK) == ACTION_INSTALL )
1054             {
1055               /*
1056                * ...in which case, we must recursively resolve
1057                * any dependencies for the scheduled "upgrade".
1058                */
1059               if( latest.Selection() == NULL )
1060                 dmh_notify_no_match( name, package, bounds_specification );
1061               else
1062                 ResolveDependencies(
1063                     upgrade, Schedule( with_download( action ), latest )
1064                   );
1065             }
1066             else
1067             { /* attempting ACTION_UPGRADE or ACTION_REMOVE
1068                * is an error; diagnose it.
1069                */
1070               if( component == NULL )
1071                 /*
1072                  * In this case, the user explicitly specified a single
1073                  * package component, so it's a simple error...
1074                  */
1075                 dmh_notify( DMH_ERROR, "%s %s: package is not installed\n",
1076                     action_name( action & ACTION_MASK ), name
1077                   );
1078               else
1079               {
1080                 /* ...but here, the user specified only the package name,
1081                  * which implicitly applies to all associated components;
1082                  * since some may be installed, prefer to issue a warning
1083                  * in respect of any which aren't.
1084                  */
1085                 const char *extname = component->GetPropVal( class_key, "" );
1086                 char full_package_name[2 + strlen( name ) + strlen( extname )];
1087                 sprintf( full_package_name, *extname ? "%s-%s" : "%s", name, extname );
1088
1089                 dmh_control( DMH_BEGIN_DIGEST );
1090                 dmh_notify( DMH_WARNING, "%s %s: request ignored...\n",
1091                     extname = action_name( action & ACTION_MASK ), full_package_name
1092                   );
1093                 dmh_notify( DMH_WARNING, "%s: package was not previously installed\n",
1094                     full_package_name
1095                   );
1096                 dmh_notify( DMH_WARNING, "%s: it will remain this way until you...\n",
1097                     full_package_name
1098                   );
1099                 dmh_notify( DMH_WARNING, "use 'mingw-get install %s' to install it\n",
1100                     full_package_name
1101                   );
1102                 dmh_control( DMH_END_DIGEST );
1103               }
1104             }
1105           }
1106           else if( upgrade && (upgrade != installed) )
1107           {
1108             /* There is an installed version, but an upgrade to a newer
1109              * version is available; when performing ACTION_UPGRADE...
1110              */
1111             if( (action & ACTION_MASK) == ACTION_UPGRADE )
1112               /*
1113                * ...we must recursively resolve any dependencies...
1114                */
1115               ResolveDependencies( upgrade,
1116                   Schedule( with_download( action ), latest )
1117                 );
1118
1119             else if( (action & ACTION_MASK) == ACTION_REMOVE )
1120             {
1121               /* ...while for ACTION_REMOVE, we have little to do,
1122                * beyond scheduling the removal; (we don't extend the
1123                * scope of a remove request to prerequisite packages,
1124                * so there is no need to resolve dependencies)...
1125                */
1126               latest.SelectPackage( installed );
1127               Schedule( action, latest );
1128             }
1129             else
1130             { /* ...but, we decline to proceed with ACTION_INSTALL
1131                * unless the --reinstall option is enabled...
1132                */
1133               if( pkgOptions()->Test( OPTION_REINSTALL ) )
1134               {
1135                 /* ...in which case, we resolve dependencies for,
1136                  * and reschedule a reinstallation of the currently
1137                  * installed version...
1138                  */
1139                 latest.SelectPackage( installed );
1140                 ResolveDependencies( installed,
1141                     Schedule( with_download( action | ACTION_REMOVE ), latest )
1142                   );
1143               }
1144               else
1145               { /* ...otherwise, we reformulate the appropriate
1146                  * fully qualified package name...
1147                  */
1148                 const char *extname = ( component != NULL )
1149                   ? component->GetPropVal( class_key, "" )
1150                   : "";
1151                 char full_package_name[2 + strlen( name ) + strlen( extname )];
1152                 sprintf( full_package_name, *extname ? "%s-%s" : "%s", name, extname );
1153                 /*
1154                  * ...which we then incorporate into an advisory
1155                  * diagnostic message, which serves both to inform
1156                  * the user of this error condition, and also to
1157                  * suggest appropriate corrective action.
1158                  */
1159                 dmh_control( DMH_BEGIN_DIGEST );
1160                 dmh_notify( DMH_ERROR, "%s: package is already installed\n",
1161                     full_package_name
1162                   );
1163                 dmh_notify( DMH_ERROR, "use 'mingw-get upgrade %s' to upgrade it\n",
1164                     full_package_name
1165                   );
1166                 dmh_notify( DMH_ERROR, "or 'mingw-get install --reinstall %s'\n",
1167                     full_package_name
1168                   );
1169                 dmh_notify( DMH_ERROR, "to reinstall the currently installed version\n" 
1170                   );
1171                 dmh_control( DMH_END_DIGEST );
1172               }
1173             }
1174           }
1175           else
1176           { /* In this case, the package is already installed,
1177              * and no more recent release is available; we still
1178              * recursively resolve its dependencies, to capture
1179              * any potential upgrades for them.
1180              */
1181             if( latest.Selection() == NULL )
1182               dmh_notify_no_match( name, package, bounds_specification );
1183             else
1184               ResolveDependencies( upgrade, Schedule( action, latest ));
1185           }
1186         }
1187
1188         if( (component = component->FindNextAssociate( component_key )) != NULL )
1189           /*
1190            * When evaluating a component-package, we extend our
1191            * evaluation, to consider for any further components of
1192            * the current package.
1193            */
1194           release = component;
1195       }
1196     }
1197
1198     else
1199       /* We found no information on the requested package;
1200        * diagnose as a non-fatal error.
1201        */
1202       dmh_notify( DMH_ERROR, pkgMsgUnknownPackage(), name );
1203   }
1204   /* Finally, we return a pointer to the currently scheduled
1205    * actions list, if any.
1206    */
1207   return actions;
1208 }
1209
1210 void pkgXmlDocument::RescheduleInstalledPackages( unsigned long action )
1211 {
1212   /* Wrapper function to retrieve the list of all installed packages,
1213    * passing each entry in turn to the standard task scheduler.  We
1214    * begin by locating the first sysroot entry in the XML database...
1215    */
1216   pkgXmlNode *sysroot = GetRoot()->FindFirstAssociate( sysroot_key );
1217
1218   /* ...then, while we have sysroots to examine...
1219    */
1220   while( sysroot != NULL )
1221   {
1222     /* ...we retrieve the first package installation record within
1223      * the current sysroot data set.
1224      */
1225     pkgXmlNode *package = sysroot->FindFirstAssociate( installed_key );
1226
1227     /* Within each sysroot, until we've retrieved all embedded
1228      * installation records...
1229      */
1230     while( package != NULL )
1231     {
1232       /* ...we read the canonical tarname for the package,
1233        * and when it is appropriately specified...
1234        */
1235       const char *tarname = package->GetPropVal( tarname_key, NULL );
1236       if( tarname != NULL )
1237       {
1238         /* ...we decode it, to determine the package name,
1239          * subsystem name and component class.
1240          */
1241         pkgSpecs decode( tarname );
1242         const char *pkgname = decode.GetPackageName();
1243         const char *sysname = decode.GetSubSystemName();
1244         const char *cptname = decode.GetComponentClass();
1245
1246         /* From these three, we need to reconstruct an effective
1247          * package name for the scheduler look-up; this reconstruction
1248          * is performed using the following formatted buffer.
1249          */
1250         const char *fmt = "%s-%s";
1251         char refname[3 + strlen( sysname ) + strlen( pkgname ) + strlen( cptname )];
1252         if( FindPackageByName( pkgname, sysname ) == NULL )
1253         {
1254           /* The package name alone is insufficient for a successful
1255            * look-up; assume that the effective package name has been
1256            * defined by prefixing the sysroot name.
1257            */
1258           sprintf( refname, fmt, sysname, pkgname );
1259           pkgname = refname;
1260         }
1261         if( cptname != NULL )
1262         {
1263           /* A fully qualified logical package name should include
1264            * the component class name, abstracted from the canonical
1265            * tarname, and appended to the package name.
1266            */
1267           sprintf( refname, fmt, pkgname, cptname );
1268           pkgname = refname;
1269         }
1270
1271         /* Having constructed the effective logical package name,
1272          * we schedule the requested action on the package...
1273          */
1274         Schedule( action, pkgname );
1275       }
1276       /* ...then move on to the next installed package, if any,
1277        * within the current sysroot data set...
1278        */
1279       package = package->FindNextAssociate( installed_key );
1280     }
1281     /* ...and ultimately, to the next sysroot, if any, in the
1282      * XML database.
1283      */
1284     sysroot = sysroot->FindNextAssociate( sysroot_key );
1285   }
1286 }
1287
1288 /* $RCSfile$: end of file */