OSDN Git Service

Add more protection against NULL pointer abuse.
[mingw/mingw-get.git] / src / pkgbase.h
1 #ifndef PKGBASE_H
2 /*
3  * pkgbase.h
4  *
5  * $Id$
6  *
7  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
8  * Copyright (C) 2009, 2010, 2011, 2012, MinGW.org Project
9  *
10  *
11  * Public interface for the package directory management routines;
12  * declares the XML data structures, and their associated class APIs,
13  * which are used to describe packages and their interdependencies.
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 #define PKGBASE_H  1
31
32 #include <tinyxml.h>
33 #include <tinystr.h>
34
35 #ifndef EXTERN_C
36 # ifdef __cplusplus
37 #  define EXTERN_C extern "C"
38 # else
39 #  define EXTERN_C
40 # endif
41 #endif
42
43 /* Adopt sensible defaults for matching subsystem and file names...
44  */
45 #ifdef _WIN32
46   /*
47    * The MS-Windows file system is intrinsically case insensitive,
48    * so we prefer to match both subsystem and file names in a case
49    * insensitive manner...
50    */
51 # ifndef CASE_INSENSITIVE_SUBSYSTEMS
52 #  define CASE_INSENSITIVE_SUBSYSTEMS  1
53 # endif
54 # ifndef CASE_INSENSITIVE_FILESYSTEM
55 #  define CASE_INSENSITIVE_FILESYSTEM  1
56 # endif
57   /*
58    * The preferred name for MS-Windows' case insensitive string
59    * matching function, equivalent to POSIX strcasecmp().
60    */
61 # define strcasecmp  stricmp
62 #else
63   /* On other systems, we prefer to adopt case sensitive matching
64    * strategies for subsystem and file names.
65    */
66 # ifndef CASE_INSENSITIVE_SUBSYSTEMS
67 #  define CASE_INSENSITIVE_SUBSYSTEMS  0
68 # endif
69 # ifndef CASE_INSENSITIVE_FILESYSTEM
70 #  define CASE_INSENSITIVE_FILESYSTEM  0
71 # endif
72 #endif
73
74 /* Define an API for registering environment variables.
75  */
76 EXTERN_C int pkgPutEnv( int, char* );
77
78 #define PKG_PUTENV_DIRSEP_MSW           (0x01)
79 #define PKG_PUTENV_DIRSEP_POSIX         (0x02)
80 #define PKG_PUTENV_SCAN_VARNAME         (0x04)
81 #define PKG_PUTENV_NAME_TOUPPER         (0x08)
82
83 #define PKG_PUTENV_FLAGS_MASK           (0x0F)
84
85 /* Begin class declarations.
86  */
87 class pkgSpecs;
88 class pkgDirectory;
89
90 #ifndef GUIMAIN_H
91 class AppWindowMaker;
92 #endif
93
94 class pkgProgressMeter
95 {
96   /* An abstract base class, from which the controller class
97    * for a progress meter dialogue window may be derived.
98    */
99   public:
100     virtual void SetValue( int ) = 0;
101     virtual void SetRange( int, int ) = 0;
102     virtual int Annotate( const char *, ... ) = 0;
103
104   protected:
105     AppWindowMaker *referrer;
106     pkgProgressMeter( AppWindowMaker *ref = NULL ): referrer( ref ){}
107     ~pkgProgressMeter();
108 };
109
110 class pkgXmlNode : public TiXmlElement
111 {
112   /* A minimal emulation of the wxXmlNode class, founded on
113    * the tinyxml implementation of the TiXmlElement class, and
114    * subsequently extended by application specific features.
115    */
116   public:
117     /* Constructors...
118      */
119     inline pkgXmlNode( const char* name ):TiXmlElement( name ){}
120     inline pkgXmlNode( const pkgXmlNode& src ):TiXmlElement( src ){}
121
122     /* Accessors...
123      *
124      * Note that tinyxml is generally careless about checking for
125      * possible dereferencing of NULL pointers; thus, many of these
126      * wrappers include appropriate checks, to prevent this.
127      */
128     inline const char* GetName()
129     {
130       /* Retrieve the identifying name of the XML tag;
131        * tinyxml calls this the element "value"...
132        */
133       return this ? Value() : NULL;
134     }
135     inline pkgXmlNode* GetParent()
136     {
137       /* wxXmlNode provides this equivalant of tinyxml's
138        * Parent() method.
139        */
140       return this ? (pkgXmlNode*)(Parent()) : NULL;
141     }
142     inline pkgXmlNode* GetChildren()
143     {
144       /* wxXmlNode provides only this one method to access
145        * the children of an element; it is equivalent to the
146        * FirstChild() method in tinyxml's arsenal.
147        */
148       return this ? (pkgXmlNode*)(FirstChild()) : NULL;
149     }
150     inline pkgXmlNode* GetNext()
151     {
152       /* This is wxXmlNode's method for visiting other children
153        * of an element, after the first found by GetChildren();
154        * it is equivalent to tinyxml's NextSibling().
155        */
156       return this ? (pkgXmlNode*)(NextSibling()) : NULL;
157     }
158     inline const char* GetPropVal( const char* name, const char* subst )
159     {
160       /* tinyxml has no direct equivalent for this wxXmlNode method,
161        * (which substitutes default "subst" text for an omitted property),
162        * but it may be trivially emulated, using the Attribute() method.
163        */
164       const char* retval = this ? Attribute( name ) : subst;
165       return retval ? retval : subst;
166     }
167     inline pkgXmlNode* AddChild( TiXmlNode *child )
168     {
169       /* This is wxXmlNode's method for adding a child node, it is
170        * equivalent to tinyxml's LinkEndChild() method.
171        */
172       return this ? (pkgXmlNode*)(LinkEndChild( child )) : NULL;
173     }
174     inline bool DeleteChild( pkgXmlNode *child )
175     {
176       /* Both TiXmlNode and wxXmlNode have RemoveChild methods, but the
177        * implementations are semantically different; for tinyxml, we may
178        * simply use the RemoveChild method here, where for wxXmlNode, we
179        * would use RemoveChild followed by `delete child'.
180        */
181       return this ? RemoveChild( child ) : false;
182     }
183
184     /* Additional methods specific to the application.
185      */
186     inline pkgXmlNode *GetDocumentRoot()
187     {
188       /* Convenience method to retrieve a pointer to the document root.
189        */
190       return this ? (pkgXmlNode*)(GetDocument()->RootElement()) : NULL;
191     }
192     inline bool IsElementOfType( const char* tagname )
193     {
194       /* Confirm if the owner XML node represents a data element
195        * with the specified "tagname".
196        */
197       return this ? strcmp( GetName(), tagname ) == 0 : false;
198     }
199
200     /* Methods for retrieving the system root management records
201      * for a specified installed subsystem.
202      */
203     pkgXmlNode *GetSysRoot( const char* );
204     pkgXmlNode *GetInstallationRecord( const char* );
205
206     /* The following pair of methods provide an iterator
207      * for enumerating the contained nodes, within the owner,
208      * which themselves exhibit a specified tagname.
209      */
210     pkgXmlNode* FindFirstAssociate( const char* );
211     pkgXmlNode* FindNextAssociate( const char* );
212
213     /* Specific to XML node elements of type "release",
214      * the following pair of methods retrieve the actual name of
215      * the release tarball, and its associated source code tarball,
216      * as they are named on the project download servers.
217      */
218     const char* ArchiveName();
219     const char* SourceArchiveName( unsigned long );
220
221     /* The following retrieves an attribute which may have been
222      * specified on an ancestor (container) node; typically used to
223      * retrieve the package name or alias attributes which are to
224      * be associated with a release.
225      */
226     const char *GetContainerAttribute( const char*, const char* = NULL );
227
228     /* Any package may have associated scripts; the following
229      * method invokes them on demand.
230      */
231     inline int InvokeScript( const char *context )
232     {
233       /* The actual implementation is delegated to the following
234        * (private) overloaded method.
235        */
236       return InvokeScript( 0, context );
237     }
238
239   private:
240     /* Helpers used to implement the preceding InvokeScript() method.
241      */
242     int InvokeScript( int, const char* );
243     int DispatchScript( int, const char*, const char*, pkgXmlNode* );
244 };
245
246 enum { to_remove = 0, to_install, selection_types };
247
248 class pkgActionItem
249 {
250   /* A class implementing a bi-directionally linked list of
251    * "action" descriptors, which is to be associated with the
252    * pkgXmlDocument class, specifying actions to be performed
253    * on the managed software installation.
254    */
255   private:
256     /* Pointers to predecessor and successor in the linked list
257      * comprising the schedule of action items.
258      */
259     pkgActionItem* prev;
260     pkgActionItem* next;
261
262     /* Flags define the specific action associated with this item.
263      */
264     unsigned long flags;
265
266     /* Criteria for selection of package versions associated with
267      * this action item.
268      */
269     const char* min_wanted;
270     const char* max_wanted;
271
272     /* Pointers to the XML database entries for the package selected
273      * for processing by this action.
274      */
275     pkgXmlNode* selection[ selection_types ];
276
277     /* Methods for retrieving packages from a distribution server.
278      */
279     void DownloadArchiveFiles( pkgActionItem* );
280     void DownloadSingleArchive( const char*, const char* );
281
282   public:
283     /* Constructor...
284      */
285     pkgActionItem( pkgActionItem* = NULL, pkgActionItem* = NULL );
286
287     /* Methods for assembling action items into a linked list.
288      */
289     pkgActionItem* Append( pkgActionItem* = NULL );
290     pkgActionItem* Insert( pkgActionItem* = NULL );
291
292     /* Methods for compiling the schedule of actions.
293      */
294     unsigned long SetAuthorities( pkgActionItem* );
295     inline unsigned long HasAttribute( unsigned long required )
296     {
297       return (this != NULL) ? flags & required : 0UL;
298     }
299     pkgActionItem* GetReference( pkgXmlNode* );
300     pkgActionItem* GetReference( pkgActionItem& );
301     pkgActionItem* Schedule( unsigned long, pkgActionItem& );
302     inline unsigned long CancelScheduledAction( void );
303     inline void SetPrimary( pkgActionItem* );
304
305     /* Method to enumerate and identify pending changes,
306      * and/or check for residual unapplied changes.
307      */
308     unsigned long EnumeratePendingActions( int = 0 );
309
310     /* Methods for defining the selection criteria for
311      * packages to be processed.
312      */
313     void ApplyBounds( pkgXmlNode *, const char * );
314     pkgXmlNode* SelectIfMostRecentFit( pkgXmlNode* );
315     const char* SetRequirements( pkgXmlNode*, pkgSpecs* );
316     inline void SelectPackage( pkgXmlNode *pkg, int opt = to_install )
317     {
318       /* Mark a package as the selection for a specified action.
319        */
320       if (this != NULL) selection[ opt ] = pkg;
321     }
322     inline pkgXmlNode* Selection( int mode = to_install )
323     {
324       /* Retrieve the package selection for a specified action.
325        */
326       return (this != NULL) ? selection[ mode ] : NULL;
327     }
328     void ConfirmInstallationStatus();
329
330     /* Method to display the URI whence a package may be downloaded.
331      */
332     void PrintURI( const char*, int (*)( const char* ) = puts );
333
334     /* Methods to download and unpack one or more source archives.
335      */
336     void GetSourceArchive( pkgXmlNode*, unsigned long );
337     void GetScheduledSourceArchives( unsigned long );
338
339     /* Methods for processing all scheduled actions.
340      */
341     void Execute( bool = true );
342     inline void DownloadArchiveFiles( void );
343
344     /* Method to manipulate error trapping, control, and state
345      * flags for the schedule of actions.
346      */
347     void Assert( unsigned long, unsigned long = ~0UL, pkgActionItem* = NULL );
348
349     /* Method to filter actions from an action list: the default is to
350      * clear ALL entries; specify a value of ACTION_MASK for the second
351      * argument, to filter out entries with no assigned action.
352      */
353     pkgActionItem *Clear( pkgActionItem* = NULL, unsigned long = 0UL );
354     pkgActionItem *Clear( unsigned long mask ){ return Clear( this, mask ); }
355
356     /* Destructor...
357      */
358     ~pkgActionItem();
359 };
360
361 class pkgXmlDocument : public TiXmlDocument
362 {
363   /* Minimal emulation of the wxXmlDocument class, founded on
364    * the tinyxml implementation of the TiXmlDocument class.
365    */
366   public:
367     /* Constructors...
368      */
369     inline pkgXmlDocument(): progress_meter( NULL ){}
370     inline pkgXmlDocument( const char* name ): progress_meter( NULL )
371     {
372       /* tinyxml has a similar constructor, but unlike wxXmlDocument,
373        * it DOES NOT automatically load the document; force it.
374        */
375       LoadFile( name );
376
377       /* Always begin with an empty actions list.
378        */
379       actions = NULL;
380     }
381
382     /* Accessors...
383      */
384     inline bool IsOk()
385     {
386       /* tinyxml doesn't have this, but instead provides a complementary
387        * `Error()' indicator, so to simulate `IsOk()'...
388        */
389       return ! Error();
390     }
391     inline pkgXmlNode* GetRoot()
392     {
393       /* This is wxXmlDocument's method for locating the document root;
394        * it is equivalent to tinyxml's RootElement() method.
395        */
396       return (pkgXmlNode *)(RootElement());
397     }
398     inline void AddDeclaration
399     ( const char *version, const char *encoding, const char *standalone )
400     {
401       /* Not a standard method of either wxXmlDocumemnt or TiXmlDocument;
402        * this is a convenience method for setting up a new XML database.
403        */
404       LinkEndChild( new TiXmlDeclaration( version, encoding, standalone ) );
405     }
406     inline void SetRoot( TiXmlNode* root )
407     {
408       /* tinyxml has no direct equivalent for this wxXmlDocument method;
409        * to emulate it, we must first explicitly delete an existing root
410        * node, if any, then link the new root node as a document child.
411        */
412       pkgXmlNode *oldroot;
413       if( (oldroot = GetRoot()) != NULL )
414         delete oldroot;
415       LinkEndChild( root );
416     }
417     inline bool Save( const char *filename )
418     {
419       /* This wxXmlDocument method for saving the database is equivalent
420        * to the corresponding tinyxml SaveFile( const char* ) method.
421        */
422       return SaveFile( filename );
423     }
424
425   private:
426     /* Properties specifying the schedule of actions.
427      */
428     unsigned long request;
429     pkgActionItem* actions;
430
431   public:
432     /* Method to interpret user preferences for mingw-get processing
433      * options, which are specified within profile.xml rather than on
434      * the command line.
435      */
436     void EstablishPreferences();
437
438     /* Method to synchronise the state of the local package manifest
439      * with the master copy held on the distribution server.
440      */
441     void SyncRepository( const char*, pkgXmlNode* );
442
443     /* Method to merge content from repository-specific package lists
444      * into the central XML package database.
445      */
446     pkgXmlNode* BindRepositories( bool );
447
448     /* Method to load the system map, and the lists of installed
449      * packages associated with each specified sysroot.
450      */
451     void LoadSystemMap();
452
453     /* Complementary method, to update the saved sysroot data associated
454      * with the active system map.
455      */
456     void UpdateSystemMap();
457
458     /* Method to locate the XML database entry for a named package.
459      */
460     pkgXmlNode* FindPackageByName( const char*, const char* = NULL );
461
462     /* Methods to retrieve and display information about packages.
463      */
464     pkgDirectory *CatalogueAllPackages();
465     void DisplayPackageInfo( int, char** );
466
467     /* Method to resolve the dependencies of a specified package,
468      * by walking the chain of references specified by "requires"
469      * elements in the respective package database entries.
470      */
471     void ResolveDependencies( pkgXmlNode*, pkgActionItem* = NULL );
472
473     /* Methods for compiling a schedule of actions.
474      */
475     pkgActionItem* Schedule( unsigned long = 0UL, const char* = NULL );
476     pkgActionItem* Schedule( unsigned long, pkgActionItem&, pkgActionItem* = NULL );
477     void RescheduleInstalledPackages( unsigned long );
478
479     /* Method to execute a sequence of scheduled actions.
480      */
481     inline void ExecuteActions(){ actions->Execute(); }
482
483     /* Method to clear the list of scheduled actions.
484      */
485     inline pkgActionItem* ClearScheduledActions( unsigned long mask = 0UL )
486     {
487       return actions = actions->Clear( mask );
488     }
489
490     /* Methods to retrieve and optionally extract source archives
491      * for a collection of dependent packages.
492      */
493     void GetSourceArchive( const char*, unsigned long );
494     inline void GetScheduledSourceArchives( unsigned long category )
495     {
496       actions->GetScheduledSourceArchives( category );
497     }
498
499   /* Facility for monitoring of XML document processing operations.
500    */
501   private:
502     pkgProgressMeter* progress_meter;
503
504   public:
505     inline pkgProgressMeter *ProgressMeter( void )
506     {
507       return progress_meter;
508     }
509     inline pkgProgressMeter *AttachProgressMeter( pkgProgressMeter *attachment )
510     {
511       if( progress_meter == NULL )
512         progress_meter = attachment;
513       return progress_meter;
514     }
515     inline void DetachProgressMeter( pkgProgressMeter *attachment )
516     {
517       if( attachment == progress_meter )
518         progress_meter = NULL;
519     }
520 };
521
522 EXTERN_C const char *xmlfile( const char*, const char* = NULL );
523 EXTERN_C int has_keyword( const char*, const char* );
524
525 typedef int (*strcmp_function)( const char *, const char * );
526
527 static inline
528 bool safe_strcmp( strcmp_function strcmp, const char *value, const char *proto )
529 {
530   /* Helper to compare a pair of "C" strings for equality,
531    * accepting NULL as a match for anything; for non-NULL matches,
532    * case sensitivity is determined by choice of strcmp function.
533    *
534    * N.B. Unlike the 'strcmp' function which this calls, this is
535    * a boolean function, returning TRUE when the 'strcmp' result
536    * is zero, (i.e. the sense of the result is inverted).
537    */
538   return (value == NULL) || (proto == NULL) || (strcmp( value, proto ) == 0);
539 }
540
541 /* Define a safe_strcmp() alias for an explicitly case sensitive match.
542  */
543 #define match_if_explicit( A, B )  safe_strcmp( strcmp, (A), (B) )
544
545 /* Further safe_strcmp() aliases provide for matching subsystem names,
546  * with implementation dependent case sensitivity...
547  */
548 #if CASE_INSENSITIVE_SUBSYSTEMS
549 # define subsystem_strcmp( A, B )  safe_strcmp( strcasecmp, (A), (B) )
550 #else
551 # define subsystem_strcmp( A, B )  safe_strcmp( strcmp, (A), (B) )
552 #endif
553
554 /* ...and similarly, for matching of file names.
555  */
556 #if CASE_INSENSITIVE_FILESYSTEM
557 # define pkg_strcmp( A, B )  safe_strcmp( strcasecmp, (A), (B) )
558 #else
559 # define pkg_strcmp( A, B )  safe_strcmp( strcmp, (A), (B) )
560 #endif
561
562 #endif /* PKGBASE_H: $RCSfile$: end of file */