OSDN Git Service

Guard against NULL pointer dereferences in tinyxml code.
[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, MinGW 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 class pkgXmlNode : public TiXmlElement
75 {
76   /* A minimal emulation of the wxXmlNode class, founded on
77    * the tinyxml implementation of the TiXmlElement class, and
78    * subsequently extended by application specific features.
79    */
80   public:
81     /* Constructors...
82      */
83     inline pkgXmlNode( const char* name ):TiXmlElement( name ){}
84     inline pkgXmlNode( const pkgXmlNode& src ):TiXmlElement( src ){}
85
86     /* Accessors...
87      *
88      * Note that tinyxml is generally careless about checking for
89      * possible dereferencing of NULL pointers; thus, many of these
90      * wrappers include appropriate checks, to prevent this.
91      */
92     inline const char* GetName()
93     {
94       /* Retrieve the identifying name of the XML tag;
95        * tinyxml calls this the element "value"...
96        */
97       return this ? Value() : NULL;
98     }
99     inline pkgXmlNode* GetParent()
100     {
101       /* wxXmlNode provides this equivalant of tinyxml's
102        * Parent() method.
103        */
104       return this ? (pkgXmlNode*)(Parent()) : NULL;
105     }
106     inline pkgXmlNode* GetChildren()
107     {
108       /* wxXmlNode provides only this one method to access
109        * the children of an element; it is equivalent to the
110        * FirstChild() method in tinyxml's arsenal.
111        */
112       return this ? (pkgXmlNode*)(FirstChild()) : NULL;
113     }
114     inline pkgXmlNode* GetNext()
115     {
116       /* This is wxXmlNode's method for visiting other children
117        * of an element, after the first found by GetChildren();
118        * it is equivalent to tinyxml's NextSibling().
119        */
120       return this ? (pkgXmlNode*)(NextSibling()) : NULL;
121     }
122     inline const char* GetPropVal( const char* name, const char* subst )
123     {
124       /* tinyxml has no direct equivalent for this wxXmlNode method,
125        * (which substitutes default "subst" text for an omitted property),
126        * but it may be trivially emulated, using the Attribute() method.
127        */
128       const char* retval = this ? Attribute( name ) : subst;
129       return retval ? retval : subst;
130     }
131     inline pkgXmlNode* AddChild( TiXmlNode *child )
132     {
133       /* This is wxXmlNode's method for adding a child node, it is
134        * equivalent to tinyxml's LinkEndChild() method.
135        */
136       return this ? (pkgXmlNode*)(LinkEndChild( child )) : NULL;
137     }
138     inline bool DeleteChild( pkgXmlNode *child )
139     {
140       /* Both TiXmlNode and wxXmlNode have RemoveChild methods, but the
141        * implementations are semantically different; for tinyxml, we may
142        * simply use the RemoveChild method here, where for wxXmlNode, we
143        * would use RemoveChild followed by `delete child'.
144        */
145       return this ? RemoveChild( child ) : false;
146     }
147
148     /* Additional methods specific to the application.
149      */
150     inline pkgXmlNode *GetDocumentRoot()
151     {
152       /* Convenience method to retrieve a pointer to the document root.
153        */
154       return this ? (pkgXmlNode*)(GetDocument()->RootElement()) : NULL;
155     }
156     inline bool IsElementOfType( const char* tagname )
157     {
158       /* Confirm if the owner XML node represents a data element
159        * with the specified "tagname".
160        */
161       return this ? strcmp( GetName(), tagname ) == 0 : false;
162     }
163
164     /* Methods for retrieving the system root management records
165      * for a specified installed subsystem.
166      */
167     pkgXmlNode *GetSysRoot( const char *subsystem );
168     pkgXmlNode *GetInstallationRecord( const char* );
169
170     /* The following pair of methods provide an iterator
171      * for enumerating the contained nodes, within the owner,
172      * which themselves exhibit a specified tagname.
173      */
174     pkgXmlNode* FindFirstAssociate( const char* tagname );
175     pkgXmlNode* FindNextAssociate( const char* tagname );
176
177     /* Specific to XML node elements of type "release",
178      * the following pair of methods retrieve the actual name of
179      * the release tarball, and its associated source code tarball,
180      * as they are named on the project download servers.
181      */
182     const char* ArchiveName();
183     const char* SourceArchiveName();
184 };
185
186 enum { to_remove = 0, to_install, selection_types };
187
188 class pkgActionItem
189 {
190   /* A class implementing a bi-directionally linked list of
191    * "action" descriptors, which is to be associated with the
192    * pkgXmlDocument class, specifying actions to be performed
193    * on the managed software installation.
194    */
195   private:
196     /* Pointers to predecessor and successor in the linked list
197      * comprising the schedule of action items.
198      */
199     pkgActionItem* prev;
200     pkgActionItem* next;
201
202     /* Flags define the specific action associated with this item.
203      */
204     unsigned long flags;
205
206     /* Criteria for selection of package versions associated with
207      * this action item.
208      */
209     const char* min_wanted;
210     const char* max_wanted;
211
212     /* Pointers to the XML database entries for the package selected
213      * for processing by this action.
214      */
215     pkgXmlNode* selection[ selection_types ];
216
217     /* Method for retrieving packages from a distribution server.
218      */
219     void DownloadArchiveFiles( pkgActionItem* );
220
221   public:
222     /* Constructor...
223      */
224     pkgActionItem( pkgActionItem* = NULL, pkgActionItem* = NULL );
225
226     /* Methods for assembling action items into a linked list.
227      */
228     pkgActionItem* Append( pkgActionItem* = NULL );
229     pkgActionItem* Insert( pkgActionItem* = NULL );
230
231     /* Methods for compiling the schedule of actions.
232      */
233     pkgActionItem* GetReference( pkgActionItem& );
234     pkgActionItem* Schedule( unsigned long, pkgActionItem& );
235
236     /* Methods for defining the selection criteria for
237      * packages to be processed.
238      */
239     const char* SetRequirements( pkgXmlNode* );
240     pkgXmlNode* SelectIfMostRecentFit( pkgXmlNode* );
241     inline void SelectPackage( pkgXmlNode *pkg, int opt = to_install )
242     {
243       /* Mark a package as the selection for a specified action.
244        */
245       selection[ opt ] = pkg;
246     }
247     inline pkgXmlNode* Selection( int mode = to_install )
248     {
249       /* Retrieve the package selection for a specified action.
250        */
251       return selection[ mode ];
252     }
253
254     /* Method for processing all scheduled actions.
255      */
256     void Execute();
257 };
258
259 class pkgXmlDocument : public TiXmlDocument
260 {
261   /* Minimal emulation of the wxXmlDocument class, founded on
262    * the tinyxml implementation of the TiXmlDocument class.
263    */
264   public:
265     /* Constructors...
266      */
267     inline pkgXmlDocument(){}
268     inline pkgXmlDocument( const char* name )
269     {
270       /* tinyxml has a similar constructor, but unlike wxXmlDocument,
271        * it DOES NOT automatically load the document; force it.
272        */
273       LoadFile( name );
274
275       /* Always begin with an empty actions list.
276        */
277       actions = NULL;
278     }
279
280     /* Accessors...
281      */
282     inline bool IsOk()
283     {
284       /* tinyxml doesn't have this, but instead provides a complementary
285        * `Error()' indicator, so to simulate `IsOk()'...
286        */
287       return ! Error();
288     }
289     inline pkgXmlNode* GetRoot()
290     {
291       /* This is wxXmlDocument's method for locating the document root;
292        * it is equivalent to tinyxml's RootElement() method.
293        */
294       return (pkgXmlNode *)(RootElement());
295     }
296     inline void AddDeclaration
297     ( const char *version, const char *encoding, const char *standalone )
298     {
299       /* Not a standard method of either wxXmlDocumemnt or TiXmlDocument;
300        * this is a convenience method for setting up a new XML database.
301        */
302       LinkEndChild( new TiXmlDeclaration( version, encoding, standalone ) );
303     }
304     inline void SetRoot( TiXmlNode* root )
305     {
306       /* tinyxml has no direct equivalent for this wxXmlDocument method;
307        * to emulate it, we must first explicitly delete an existing root
308        * node, if any, then link the new root node as a document child.
309        */
310       pkgXmlNode *oldroot;
311       if( (oldroot = GetRoot()) != NULL )
312         delete oldroot;
313       LinkEndChild( root );
314     }
315     inline bool Save( const char *filename )
316     {
317       /* This wxXmlDocument method for saving the database is equivalent
318        * to the corresponding tinyxml SaveFile( const char* ) method.
319        */
320       return SaveFile( filename );
321     }
322
323   private:
324     /* Properties specifying the schedule of actions.
325      */
326     unsigned long request;
327     pkgActionItem* actions;
328
329     /* Method to synchronise the state of the local package manifest
330      * with the master copy held on the distribution server.
331      */
332     void SyncRepository( const char*, pkgXmlNode* );
333
334   public:
335     /* Method to merge content from repository-specific package lists
336      * into the central XML package database.
337      */
338     pkgXmlNode* BindRepositories( bool );
339
340     /* Method to load the system map, and the lists of installed
341      * packages associated with each specified sysroot.
342      */
343     void LoadSystemMap();
344
345     /* Complementary method, to update the saved sysroot data associated
346      * with the active system map.
347      */
348     void UpdateSystemMap();
349
350     /* Method to locate the XML database entry for a named package.
351      */
352     pkgXmlNode* FindPackageByName( const char*, const char* = NULL );
353
354     /* Method to resolve the dependencies of a specified package,
355      * by walking the chain of references specified by "requires"
356      * elements in the respective package database entries.
357      */
358     void ResolveDependencies( pkgXmlNode*, pkgActionItem* = NULL );
359
360     /* Methods for compiling a schedule of actions.
361      */
362     void Schedule( unsigned long, const char* );
363     pkgActionItem* Schedule( unsigned long, pkgActionItem&, pkgActionItem* = NULL );
364
365     /* Method to execute a sequence of scheduled actions.
366      */
367     inline void ExecuteActions(){ actions->Execute(); }
368 };
369
370 EXTERN_C const char *xmlfile( const char*, const char* = NULL );
371 EXTERN_C int has_keyword( const char*, const char* );
372
373 typedef int (*strcmp_function)( const char *, const char * );
374
375 static inline
376 bool safe_strcmp( strcmp_function strcmp, const char *value, const char *proto )
377 {
378   /* Helper to compare a pair of "C" strings for equality,
379    * accepting NULL as a match for anything; for non-NULL matches,
380    * case sensitivity is determined by choice of strcmp function.
381    *
382    * N.B. Unlike the 'strcmp' function which this calls, this is
383    * a boolean function, returning TRUE when the 'strcmp' result
384    * is zero, (i.e. the sense of the result is inverted).
385    */
386   return (value == NULL) || (proto == NULL) || (strcmp( value, proto ) == 0);
387 }
388
389 /* Define a safe_strcmp() alias for an explicitly case sensitive match.
390  */
391 #define match_if_explicit( A, B )  safe_strcmp( strcmp, (A), (B) )
392
393 /* Further safe_strcmp() aliases provide for matching subsystem names,
394  * with implementation dependent case sensitivity...
395  */
396 #if CASE_INSENSITIVE_SUBSYSTEMS
397 # define subsystem_strcmp( A, B )  safe_strcmp( strcasecmp, (A), (B) )
398 #else
399 # define subsystem_strcmp( A, B )  safe_strcmp( strcmp, (A), (B) )
400 #endif
401
402 /* ...and similarly, for matching of file names.
403  */
404 #if CASE_INSENSITIVE_FILESYSTEM
405 # define pkg_strcmp( A, B )  safe_strcmp( strcasecmp, (A), (B) )
406 #else
407 # define pkg_strcmp( A, B )  safe_strcmp( strcmp, (A), (B) )
408 #endif
409
410 #endif /* PKGBASE_H: $RCSfile$: end of file */