OSDN Git Service

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