OSDN Git Service

3fff7b13587bb65f3de984280720978210ae0c40
[mingw/mingw-get.git] / src / pkgname.cpp
1 /*
2  * pkgname.cpp
3  *
4  * $Id$
5  *
6  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7  * Copyright (C) 2009, 2010, 2011, MinGW Project
8  *
9  *
10  * Implementation for the non-inherited components of the pkgXmlNode
11  * class, as declared in file "pkgdesc.h"; fundamentally, these are
12  * the accessors for package "tarname" properties, as specified in
13  * XML nodes identified as "release" elements.
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 <stdio.h>
31 #include <string.h>
32
33 #include "dmh.h"
34 #include "pkgbase.h"
35 #include "pkgkeys.h"
36 #include "pkginfo.h"
37 #include "pkgtask.h"
38
39 static
40 const char *pkgArchiveName( pkgXmlNode *rel, const char *tag, unsigned opt )
41 {
42   /* Local helper to establish actual release file names...
43    * applicable only to XML "release" elements.
44    */
45   if( ! rel->IsElementOfType( release_key ) )
46   {
47     /* The XML element type name is not "release"; identify it...
48      */
49     const char *reftype;
50     if( (reftype = rel->GetName()) == NULL )
51       /*
52        * ...or classify as "unknown", when given a NULL element.
53        */
54       reftype = value_unknown;
55
56     /* Complain that this XML element type is invalid, in this context...
57      */
58     dmh_control( DMH_BEGIN_DIGEST );
59     dmh_notify( DMH_ERROR, "internal package specification error\n" );
60     dmh_notify( DMH_ERROR, "can't get 'tarname' for non-release element %s\n", reftype );
61     dmh_notify( DMH_ERROR, "please report this to the package maintainer\n" );
62     dmh_control( DMH_END_DIGEST );
63
64     /* ...and bail out, telling the caller that no archive name is available...
65      */
66     return NULL;
67   }
68
69   /* Given a package release specification...
70    * First check that it relates to a real package, rather than to
71    * a virtual "meta-package"; such meta-packages exist solely as
72    * containers for requirements specifications, and have no
73    * associated archive.
74    */
75   pkgXmlNode *pkg = rel->GetParent();
76   while( (pkg != NULL) && ! pkg->IsElementOfType( package_key ) )
77     pkg = pkg->GetParent();
78
79   /* FIXME: we should probably provide some error handling here,
80    * to diagnose release elements without any package association;
81    * (these would be identified by pkg == NULL).
82    */
83   if( pkg != NULL )
84   {
85     /* We found the package association...
86      * Check its 'class' attribute, if any, and if classified as
87      * 'virtual', return the archive association as "none".
88      */
89     const char *package_class = pkg->GetPropVal( class_key, NULL );
90     if( (package_class != NULL) && (strcmp( package_class, value_virtual ) == 0) )
91       return value_none;
92   }
93
94   /* The given release specification relates to a real package...
95    * Determine the archive name for the tarball to be processed; this
96    * is retrieved from a child XML element with name specified by "tag";
97    * by default, if "opt" is non-zero, it is the canonical "tarname"
98    * assigned to the release element itself, unless an alternative
99    * specification is provided; if "opt" is zero, no default is
100    * assumed, and the return value is NULL if no alternative
101    * specification is provided.
102    */
103   unsigned matched = 0;
104   pkgXmlNode *dl = rel->GetChildren();
105   while( dl != NULL )
106   {
107     /* Visit all children of the release specification element,
108      * checking for the presence of an expected specification...
109      */
110     if( dl->IsElementOfType( tag ) )
111     {
112       /* Found one; ensure it is the only one...
113        */
114       if( matched++ )
115         /*
116          * ...else emit a warning, and ignore this one...
117          */
118         dmh_notify( DMH_WARNING, "%s: archive name reassignment ignored\n",
119             rel->GetPropVal( tarname_key, value_unknown )
120         );
121       else
122         /* ...ok; this is the first "tag" specification,
123          * accept it as the non-default source of the release's
124          * "tarname" property.
125          */
126         rel = dl;
127     }
128     /* Continue, until all children have been visited.
129      */
130     dl = dl->GetNext();
131   }
132   /* "rel" now points to the XML element having the appropriate
133    * "tarname" specification; return a pointer to it's value.
134    */
135   return (opt || matched) ? rel->GetPropVal( tarname_key, NULL ) : NULL;
136 }
137
138 EXTERN_C const char *pkgAssociateName( const char *, const char * );
139
140 static inline
141 const char *pkgResolvedName( pkgXmlNode *rel, const char *tag, const char *ext )
142 {
143   /* Local helper function to resolve the mapping from a released
144    * package name, as identified from the XML release element "rel",
145    * to its corresponding source or licence package name, according
146    * to the selection of "source" or "licence" specified by "tag",
147    * with "ext" passed a "src" or "lic" respectively.
148    */
149   const char *refname;
150   const char *retname = NULL;
151
152   /* First, we retrieve the released package name...
153    */
154   if( (refname = pkgArchiveName( rel, release_key, 1 )) != NULL )
155   {
156     /* ...and if successful, look for an explicit reference to
157      * the source or licence package, embedded within the release
158      * specification itself.
159      */
160     if( (retname = pkgArchiveName( rel, tag, 0 )) == NULL )
161     {
162       /* When this fails to identify the required mapping,
163        * then we look for a generic reference, defined for
164        * the containing package.
165        */
166       pkgXmlNode *enc = rel->GetParent();
167
168       /* A generic reference may be placed at any nesting
169        * level, between the enclosing package element and
170        * the release to which it relates; thus, starting
171        * at the first enclosing level...
172        */
173       rel = NULL;
174       while( enc != NULL )
175       {
176         /* ...enumerate reference specifications of the
177          * appropriate type, examining all children of
178          * the enclosing element.
179          */
180         unsigned matched = 0;
181         pkgXmlNode *child = enc->GetChildren();
182         while( child != NULL )
183         {
184           /* We have a child, which we have not examined...
185            */
186           if( child->IsElementOfType( tag ) )
187           {
188             /* ...and it is of the required "tag" type.
189              */
190             if( matched++ )
191               /*
192                * We already had a candidate match, so we
193                * diagnose but otherwise this duplicate...
194                */
195               dmh_notify( DMH_WARNING,
196                   "redundant %s specification ignored\n", tag
197                 );
198
199             else
200               /* This is the first candidate match found,
201                * so we accept it.
202                */
203               rel = child;
204           }
205           /* Continue examining child elements, until no more
206            * are present at the current nesting level.
207            */
208           child = child->GetNext();
209         }
210
211         /* When we've completed the examination of all children
212          * at a given nesting level, without finding a matching
213          * specification, and that level is still within the
214          * enclosing package element...
215          */
216         if( (rel == NULL) && ! enc->IsElementOfType( package_key ) )
217           /*
218            * ...then we extend the search to the next enclosing
219            * level of nesting...
220            */
221           enc = enc->GetParent();
222
223         else
224           /* ...otherwise, we abandon the search.
225            */
226           enc = NULL;
227       }
228
229       /* If we've searched all available nesting levels,
230        * and failed to locate the requisite specification...
231        */
232       if( rel == NULL )
233       {
234         /* ...then we assume that the requisite tarname
235          * is identical to the release tarname, with the
236          * appropriate "ext" substitution for the package
237          * class identification...
238          */
239         pkgSpecs resolved( refname );
240         resolved.SetComponentClass( ext );
241         /*
242          * ...so, having made the substitution,
243          * we return the resultant tarname, noting
244          * that this automatically allocates space
245          * on the heap, for the returned string.
246          */
247         return resolved.GetTarName();
248       }
249       else
250         /* We did find a mappingspecification, so we
251          * extract a tarname template from it.
252          */
253         retname = rel->GetPropVal( tarname_key, NULL );
254     }
255     else if( strcmp( retname, value_none ) == 0 )
256       /*
257        * The package is virtual, or an explicit mapping
258        * specification indicates that there is no related
259        * source or licence package; return NULL to advise
260        * the caller of this.
261        */
262       return NULL;
263
264     /* If we get to here, we found a mapping specification;
265      * it may be a template, so resolve any substitutions which
266      * it must inherit from the released package tarname, again
267      * noting that this allocates heap memory for the result.
268      */
269     retname = pkgAssociateName( retname, refname );
270   }
271
272   /* Finally, how ever we resolved the mapping, we return
273    * the result.
274    */
275   return retname;
276 }
277
278 const char *pkgXmlNode::SourceArchiveName( unsigned long category )
279 {
280   /* Applicable only for XML nodes designated as "release".
281    *
282    * Retrieve the source tarball name, if specified, from the
283    * "tarname" property of the contained "source" element, within
284    * an XML node designated as a "release" element.
285    *
286    * Returns a pointer to the text of the "tarname" property of the
287    * contained "source" element, or NULL if the containing node does
288    * not represent a "release", or if it does not have a contained
289    * "source" element specifying a "tarname" property.
290    */
291   const char *tag = "lic";
292   if( category != ACTION_LICENCE )
293   {
294     /* We may have been asked to explicitly override the "source"
295      * mapping, returning the "licence" reference instead; where
296      * this special exception is NOT requested, then we enforce
297      * the default case.
298      */
299     category = ACTION_SOURCE;
300     tag = "src";
301   }
302
303   /* In either case, pkgResolvedName() determines the appropriate
304    * archive name, which it automatically returns on the heap.
305    */
306   return pkgResolvedName( this, action_name( category ), tag );
307 }
308
309 const char *pkgXmlNode::ArchiveName()
310 {
311   /* Applicable only for XML nodes designated as "release".
312    *
313    * Retrieve the actual tarball name, if specified, from the
314    * "tarname" property of a contained "download" element, within
315    * an XML node designated as a "release" element.
316    *
317    * Returns a pointer to the text of the "tarname" property of the
318    * contained "download" element, or to the "tarname" property of
319    * the containing "release" element, if it does not contain an
320    * alternative specification within a "download" element; if
321    * unresolved to either of these, returns NULL.
322    */
323   return pkgArchiveName( this, download_key, 1 );
324 }
325
326 /* $RCSfile$: end of file */