OSDN Git Service

Eliminate invalid comparisons of "this" with nullptr.
[mingw/mingw-get.git] / src / pkgdeps.cpp
1 /*
2  * pkgdeps.cpp
3  *
4  * $Id$
5  *
6  * Written by Keith Marshall <keith@users.osdn.me>
7  * Copyright (C) 2009-2012, 2020, MinGW.org 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 to
863    * a specified package.  Assume that the initial 'this' pointer is
864    * closer to the end, than to the beginning of the current list of
865    * scheduled actions, (which MUST NOT be empty), and...
866    */
867   pkgActionItem *item = this;
868   while( item->next != NULL )
869     /*
870      * ...advance, to locate the very last entry in the list.
871      */
872     item = item->next;
873
874   /* Now, working backward toward the beginning of the list...
875    */
876   while( item != NULL )
877   {
878     /* ...identify a "release" specification associated with
879      * each action item in turn...
880      */
881     pkgXmlNode *ref = item->Selection();
882     if( (ref != NULL) || ((ref = item->Selection( to_remove )) != NULL) )
883     {
884       /* ...convert this to an actual component package, or
885        * full package, reference...
886        */
887       while( ref->IsElementOfType( release_key ) )
888         ref = ref->GetParent();
889
890       /* ...and, if it matches the search target, return it.
891        */
892       if( ref == package ) return item;
893     }
894     /* ...or, when we haven't yet found a matching package,
895      * try the preceding scheduled action item, if any.
896      */
897     item = item->prev;
898   }
899   /* If we fall through to here, then we found no action to be
900    * performed on the specified package; report accordingly.
901    */
902   return NULL;
903 }
904
905 static void dmh_notify_no_match
906 ( const char *name, pkgXmlNode *package, const char *bounds )
907 {
908   /* Diagnostic helper, called when the user has requested a specific
909    * package version for which there is no exactly matching release.
910    */
911   dmh_control( DMH_BEGIN_DIGEST );
912   dmh_notify( DMH_ERROR, "there is no release matching %s%s\n",
913       name, (bounds == NULL) ? "" : bounds
914     );
915   if( (package = package->FindFirstAssociate( release_key )) != NULL )
916   {
917     /* To assist the user in formalising a more appropriate
918      * version specification...
919      */
920     dmh_notify( DMH_ERROR, "available candidate releases are...\n" );
921     while( package )
922     {
923       /* ...display a list of available release tarballs...
924        */
925       const char *tarname = package->GetPropVal( tarname_key, NULL );
926       if( tarname != NULL )
927         dmh_notify( DMH_ERROR, " %s\n", tarname );
928
929       /* ...cycling, until all have been identified.
930        */
931       package = package->FindNextAssociate( release_key );
932     }
933   }
934   dmh_control( DMH_END_DIGEST );
935 }
936
937 pkgActionItem* pkgXmlDocument::Schedule( unsigned long action, const char* name )
938 {
939   /* Task scheduler interface; schedules actions to process all
940    * dependencies for the package specified by "name", honouring
941    * any appended version bounds specified for the parent.
942    */
943
944   /* We may call this method without any assigned package name,
945    * in which case, we interpret it as a request to return the
946    * list of previously scheduled actions...
947    */
948   if( name != NULL )
949   {
950     /* ...but when a package name is specified, then we
951      * proceed to schedule the specified action on it.
952      */
953     char scratch_pad[strlen( name )];
954     const char *bounds_specification = get_version_bounds( name );
955     if( bounds_specification != NULL )
956     {
957       /* Separate any version bounds specification from
958        * the original package name specification.
959        */
960       size_t scratch_pad_len = bounds_specification - name;
961       name = (const char *)(memcpy( scratch_pad, name, scratch_pad_len ));
962       scratch_pad[scratch_pad_len] = '\0';
963     }
964
965     pkgXmlNode *release;
966     if( (release = FindPackageByName( name )) != NULL )
967     {
968       /* We found the specification for the named package...
969        */
970       pkgXmlNode *component = release->FindFirstAssociate( component_key );
971       if( component != NULL )
972         /*
973          * When it is subdivided into component-packages,
974          * we need to consider each as a possible candidate
975          * for task scheduling.
976          */
977         release = component;
978
979       while( release != NULL )
980       {
981         /* Within each candidate package or component-package...
982          */
983         pkgXmlNode *package = release;
984         if( (release = release->FindFirstAssociate( release_key )) != NULL )
985         {
986           /* ...initially assume it is not installed, and that
987            * no installable upgrade is available.
988            */
989           pkgActionItem latest;
990           pkgXmlNode *installed = NULL, *upgrade = NULL;
991
992           /* Establish the action for which dependency resolution is
993            * to be performed; note that this may be promoted to a more
994            * inclusive class, during resolution, so we need to reset
995            * it for each new dependency which may be encountered.
996            */
997           request = action;
998
999           /* Any action request processed here is, by definition,
1000            * a request for a primary action; mark it as such.
1001            */
1002           action |= ACTION_PRIMARY;
1003
1004           /* When the user has given a version bounds specification,
1005            * then we must assign appropriate action item requirements.
1006            */
1007           if( bounds_specification != NULL )
1008             latest.ApplyBounds( release, bounds_specification );
1009
1010           /* For each candidate release in turn...
1011            */
1012           while( release != NULL )
1013           {
1014             /* ...inspect it to identify any which is already installed,
1015              * and also the latest available...
1016              */
1017             if( is_installed( release ) )
1018               /*
1019                * ...i.e. here we have identified a release
1020                * which is currently installed...
1021                */
1022               latest.SelectPackage( installed = release, to_remove );
1023
1024             if( latest.SelectIfMostRecentFit( release ) == release )
1025               /*
1026                * ...while this is the most recent we have
1027                * encountered so far.
1028                */
1029               upgrade = release;
1030
1031             /* Continue with the next specified release, if any.
1032              */
1033             release = release->FindNextAssociate( release_key );
1034           }
1035
1036           if( (installed = assert_installed( upgrade, installed )) == NULL )
1037           {
1038             /* There is no installed version...
1039              * therefore, there is nothing to do for any action
1040              * other than ACTION_INSTALL...
1041              */
1042             if( (action & ACTION_MASK) == ACTION_INSTALL )
1043             {
1044               /*
1045                * ...in which case, we must recursively resolve
1046                * any dependencies for the scheduled "upgrade".
1047                */
1048               if( latest.Selection() == NULL )
1049                 dmh_notify_no_match( name, package, bounds_specification );
1050               else
1051                 ResolveDependencies(
1052                     upgrade, Schedule( with_download( action ), latest )
1053                   );
1054             }
1055             else
1056             { /* attempting ACTION_UPGRADE or ACTION_REMOVE
1057                * is an error; diagnose it.
1058                */
1059               if( component == NULL )
1060                 /*
1061                  * In this case, the user explicitly specified a single
1062                  * package component, so it's a simple error...
1063                  */
1064                 dmh_notify( DMH_ERROR, "%s %s: package is not installed\n",
1065                     action_name( action & ACTION_MASK ), name
1066                   );
1067               else
1068               {
1069                 /* ...but here, the user specified only the package name,
1070                  * which implicitly applies to all associated components;
1071                  * since some may be installed, prefer to issue a warning
1072                  * in respect of any which aren't.
1073                  */
1074                 const char *extname = component->GetPropVal( class_key, "" );
1075                 char full_package_name[2 + strlen( name ) + strlen( extname )];
1076                 sprintf( full_package_name, *extname ? "%s-%s" : "%s", name, extname );
1077
1078                 dmh_control( DMH_BEGIN_DIGEST );
1079                 dmh_notify( DMH_WARNING, "%s %s: request ignored...\n",
1080                     extname = action_name( action & ACTION_MASK ), full_package_name
1081                   );
1082                 dmh_notify( DMH_WARNING, "%s: package was not previously installed\n",
1083                     full_package_name
1084                   );
1085                 dmh_notify( DMH_WARNING, "%s: it will remain this way until you...\n",
1086                     full_package_name
1087                   );
1088                 dmh_notify( DMH_WARNING, "use 'mingw-get install %s' to install it\n",
1089                     full_package_name
1090                   );
1091                 dmh_control( DMH_END_DIGEST );
1092               }
1093             }
1094           }
1095           else if( upgrade && (upgrade != installed) )
1096           {
1097             /* There is an installed version, but an upgrade to a newer
1098              * version is available; when performing ACTION_UPGRADE...
1099              */
1100             if( (action & ACTION_MASK) == ACTION_UPGRADE )
1101               /*
1102                * ...we must recursively resolve any dependencies...
1103                */
1104               ResolveDependencies( upgrade,
1105                   Schedule( with_download( action ), latest )
1106                 );
1107
1108             else if( (action & ACTION_MASK) == ACTION_REMOVE )
1109             {
1110               /* ...while for ACTION_REMOVE, we have little to do,
1111                * beyond scheduling the removal; (we don't extend the
1112                * scope of a remove request to prerequisite packages,
1113                * so there is no need to resolve dependencies)...
1114                */
1115               latest.SelectPackage( installed );
1116               Schedule( action, latest );
1117             }
1118             else
1119             { /* ...but, we decline to proceed with ACTION_INSTALL
1120                * unless the --reinstall option is enabled...
1121                */
1122               if( pkgOptions()->Test( OPTION_REINSTALL ) )
1123               {
1124                 /* ...in which case, we resolve dependencies for,
1125                  * and reschedule a reinstallation of the currently
1126                  * installed version...
1127                  */
1128                 latest.SelectPackage( installed );
1129                 ResolveDependencies( installed,
1130                     Schedule( with_download( action | ACTION_REMOVE ), latest )
1131                   );
1132               }
1133               else
1134               { /* ...otherwise, we reformulate the appropriate
1135                  * fully qualified package name...
1136                  */
1137                 const char *extname = ( component != NULL )
1138                   ? component->GetPropVal( class_key, "" )
1139                   : "";
1140                 char full_package_name[2 + strlen( name ) + strlen( extname )];
1141                 sprintf( full_package_name, *extname ? "%s-%s" : "%s", name, extname );
1142                 /*
1143                  * ...which we then incorporate into an advisory
1144                  * diagnostic message, which serves both to inform
1145                  * the user of this error condition, and also to
1146                  * suggest appropriate corrective action.
1147                  */
1148                 dmh_control( DMH_BEGIN_DIGEST );
1149                 dmh_notify( DMH_ERROR, "%s: package is already installed\n",
1150                     full_package_name
1151                   );
1152                 dmh_notify( DMH_ERROR, "use 'mingw-get upgrade %s' to upgrade it\n",
1153                     full_package_name
1154                   );
1155                 dmh_notify( DMH_ERROR, "or 'mingw-get install --reinstall %s'\n",
1156                     full_package_name
1157                   );
1158                 dmh_notify( DMH_ERROR, "to reinstall the currently installed version\n" 
1159                   );
1160                 dmh_control( DMH_END_DIGEST );
1161               }
1162             }
1163           }
1164           else
1165           { /* In this case, the package is already installed,
1166              * and no more recent release is available; we still
1167              * recursively resolve its dependencies, to capture
1168              * any potential upgrades for them.
1169              */
1170             if( latest.Selection() == NULL )
1171               dmh_notify_no_match( name, package, bounds_specification );
1172             else
1173               ResolveDependencies( upgrade, Schedule( action, latest ));
1174           }
1175         }
1176         if(  (component != NULL)
1177         &&  ((component = component->FindNextAssociate( component_key )) != NULL)  )
1178           /*
1179            * When evaluating a component-package, we extend our
1180            * evaluation, to consider for any further components of
1181            * the current package.
1182            */
1183           release = component;
1184       }
1185     }
1186
1187     else
1188       /* We found no information on the requested package;
1189        * diagnose as a non-fatal error.
1190        */
1191       dmh_notify( DMH_ERROR, pkgMsgUnknownPackage(), name );
1192   }
1193   /* Finally, we return a pointer to the currently scheduled
1194    * actions list, if any.
1195    */
1196   return actions;
1197 }
1198
1199 void pkgXmlDocument::RescheduleInstalledPackages( unsigned long action )
1200 {
1201   /* Wrapper function to retrieve the list of all installed packages,
1202    * passing each entry in turn to the standard task scheduler.  We
1203    * begin by locating the first sysroot entry in the XML database...
1204    */
1205   pkgXmlNode *sysroot = GetRoot()->FindFirstAssociate( sysroot_key );
1206
1207   /* ...then, while we have sysroots to examine...
1208    */
1209   while( sysroot != NULL )
1210   {
1211     /* ...we retrieve the first package installation record within
1212      * the current sysroot data set.
1213      */
1214     pkgXmlNode *package = sysroot->FindFirstAssociate( installed_key );
1215
1216     /* Within each sysroot, until we've retrieved all embedded
1217      * installation records...
1218      */
1219     while( package != NULL )
1220     {
1221       /* ...we read the canonical tarname for the package,
1222        * and when it is appropriately specified...
1223        */
1224       const char *tarname = package->GetPropVal( tarname_key, NULL );
1225       if( tarname != NULL )
1226       {
1227         /* ...we decode it, to determine the package name,
1228          * subsystem name and component class.
1229          */
1230         pkgSpecs decode( tarname );
1231         const char *pkgname = decode.GetPackageName();
1232         const char *sysname = decode.GetSubSystemName();
1233         const char *cptname = decode.GetComponentClass();
1234
1235         /* From these three, we need to reconstruct an effective
1236          * package name for the scheduler look-up; this reconstruction
1237          * is performed using the following formatted buffer.
1238          */
1239         const char *fmt = "%s-%s";
1240         char refname[3 + strlen( sysname ) + strlen( pkgname ) + strlen( cptname )];
1241         if( FindPackageByName( pkgname, sysname ) == NULL )
1242         {
1243           /* The package name alone is insufficient for a successful
1244            * look-up; assume that the effective package name has been
1245            * defined by prefixing the sysroot name.
1246            */
1247           sprintf( refname, fmt, sysname, pkgname );
1248           pkgname = refname;
1249         }
1250         if( cptname != NULL )
1251         {
1252           /* A fully qualified logical package name should include
1253            * the component class name, abstracted from the canonical
1254            * tarname, and appended to the package name.
1255            */
1256           sprintf( refname, fmt, pkgname, cptname );
1257           pkgname = refname;
1258         }
1259
1260         /* Having constructed the effective logical package name,
1261          * we schedule the requested action on the package...
1262          */
1263         Schedule( action, pkgname );
1264       }
1265       /* ...then move on to the next installed package, if any,
1266        * within the current sysroot data set...
1267        */
1268       package = package->FindNextAssociate( installed_key );
1269     }
1270     /* ...and ultimately, to the next sysroot, if any, in the
1271      * XML database.
1272      */
1273     sysroot = sysroot->FindNextAssociate( sysroot_key );
1274   }
1275 }
1276
1277 /* $RCSfile$: end of file */