OSDN Git Service

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