OSDN Git Service

Support assignment of DEBUGLEVEL at configure time.
[mingw/mingw-get.git] / src / pkgreqs.cpp
1 /*
2  * pkgreqs.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  * Implements the SetRequirements() method for the pkgActionItem class,
11  * together with additional components of the pkgSpecs class which are
12  * required specifically to support it.
13  *
14  *
15  * This is free software.  Permission is granted to copy, modify and
16  * redistribute this software, under the provisions of the GNU General
17  * Public License, Version 3, (or, at your option, any later version),
18  * as published by the Free Software Foundation; see the file COPYING
19  * for licensing details.
20  *
21  * Note, in particular, that this software is provided "as is", in the
22  * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
23  * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
24  * PARTICULAR PURPOSE.  Under no circumstances will the author, or the
25  * MinGW Project, accept liability for any damages, however caused,
26  * arising from the use of this software.
27  *
28  */
29 #include "pkginfo.h"
30 #include "pkgkeys.h"
31 #include "pkgtask.h"
32
33 #include <stdlib.h>
34 #include <string.h>
35
36 /*
37  ******************
38  *
39  * Class Implementation: pkgSpecs; SetProperty and GetTarName methods
40  *
41  */
42
43 static inline
44 int buflen( pkginfo_t specs )
45 {
46   /* Local helper function, to determine the size of the buffer used
47    * to store the data content of all pkgSpecs fields; (it is also the
48    * length of the C string representing the associated package tarname,
49    * INCLUDING its NUL terminator).
50    */
51   int index = PACKAGE_TAG_COUNT;
52   while( (index-- > 0) && (specs[index] == NULL) )
53     /*
54      * Locate the last populated data field in the content buffer...
55      */ ;
56     /* ...then compute the total buffer length, from the start of
57      * the first field, up to the NUL terminator of the last.
58      */
59   return specs[index] ? specs[index] - *specs + strlen( specs[index] ) + 1 : 0;
60 }
61
62 static inline
63 int fieldlen( const char *string )
64 {
65   /* NULL-safe local helper function to determine the length of
66    * the C string representing a pkgSpecs data field, (EXCLUDING
67    * its NUL terminator); returns zero in the case of an unassigned
68    * field, identified by a NULL reference pointer.
69    */
70   return (string == NULL) ? 0 : strlen( string );
71 }
72
73 static inline
74 char *fieldcpy( char *dest, const char *src )
75 {
76   /* Local helper function to copy the content of a single
77    * pkgSpecs data field...
78    */
79   while( (*dest++ = *src++) != '\0' )
80     /*
81      * ...copying byte by byte, including the terminating NUL,
82      * to the destination buffer...
83      */ ;
84   /* ...and ultimately, return the next available location
85    * within the destination buffer.
86    */
87   return dest;
88 }
89
90 static inline
91 char *bufcpy( int index, int end, char *buf, pkginfo_t specs )
92 {
93   /* Local helper function to copy a specified field range from
94    * its original location within the pkgSpecs content buffer, to
95    * an alternate location specified by "buf".
96    */
97   do { if( specs[index] != NULL )
98          /*
99           * Copy each non-empty data field...
100           */
101          buf = fieldcpy( buf, specs[index] );
102
103        /* ...until all specified fields in the source range
104         * have been copied.
105         */
106      } while( ++index < end );
107
108   /* Finally, return the location at which any further data should
109    * be appended to the data just copied.
110    */
111   return buf;
112 }
113
114 const char *pkgSpecs::SetProperty( int index, const char *repl )
115 {
116   /* A private method to modify the content of any single data field
117    * within a pkgSpecs data buffer; it provides the core implementation
118    * for each of the public inline field manipulator methods.
119    */
120   char *p;
121
122   /* We begin by computing the current size of the content buffer,
123    * and the size it will become after making the change.
124    */
125   int oldlen = buflen( specs );
126   int newlen = oldlen + fieldlen( repl ) - fieldlen( specs[index] );
127
128   if( (repl != NULL) && (specs[index] == NULL) )
129     /*
130      * When inserting a non-empty value into a previously empty field,
131      * we need to allow an additional byte for an extra field separator.
132      */
133     ++newlen;
134
135   if( newlen > oldlen )
136   {
137     /* The buffer size must increase; thus, there is insufficient space
138      * at the location of the field to be modified, to accomodate its new
139      * value, without corrupting the content of following fields.  To avoid
140      * this, we use a temporary buffer in which we construct an image of
141      * the modified content...
142      */
143     char newbuf[ newlen ];
144     /*
145      * ...first copying the content of all fields which precede that
146      * which is to be modified...
147      */
148     p = bufcpy( 0, index, newbuf, specs );
149     /*
150      * ...appending the new value to be assigned to the modified field...
151      */
152     p = fieldcpy( p, repl );
153     /*
154      * ...and then the content of any following unmodified fields.
155      */
156     bufcpy( index + 1, PACKAGE_TAG_COUNT, p, specs );
157
158     /* Having created this temporary image of the content buffer, as
159      * it is to become, adjust the allocation size of the original buffer,
160      * and copy its modified content into place.
161      */
162     if( (p = (char *)(content = realloc( content, newlen ))) != NULL )
163       memcpy( p, newbuf, newlen );
164   }
165
166   else
167   { /* In this case, the size of content buffer will either shrink, or
168      * it will remain unchanged; we may modify it in place, beginning at
169      * the location of the field which is to be modified, by writing its
170      * new value, if any, into place.
171      */
172     p = specs[index];
173     if( (repl != NULL) && (*repl != '\0') )
174       p = fieldcpy( p, repl );
175
176     /* If the new content of the modified field is exactly the same length
177      * as the content it has replaced, then the overall size of the content
178      * buffer does not change...
179      */
180     if( newlen == oldlen )
181       /*
182        * ...so we have nothing more to do, to complete the modification...
183        */
184       return (char *)(content);
185
186     /* ...otherwise, there is residual data from the original content at
187      * the location now indicated by "p", and preceding the start of the
188      * content of the next populated field, (if any); clear this, moving
189      * the content of all following fields up to fill the gap...
190      */
191     bufcpy( index + 1, PACKAGE_TAG_COUNT, p, specs );
192
193     /* ...and adjust the allocation size to the reduced data length.
194      */
195     p = (char *)(content = realloc( content, newlen ));
196   }
197
198   /* Irrespective of which of the preceding paths we followed to get here,
199    * the size of the content buffer was adjusted, so...
200    */
201   if( p != NULL )
202   {
203     /* ...provided it was not lost altogether...
204      */
205     if( (repl == NULL) || (*repl == '\0') )
206       /*
207        * ...we either mark the content of the modified field as "deleted",
208        * when appropriate...
209        */
210       specs[index] = NULL;
211
212     else
213       /* ...or we ensure that it is marked as having SOME content, in case
214        * it may have been empty beforehand; (note that this does NOT assign
215        * the correct pointer value -- any arbitrary non-NULL pointer value
216        * will suffice here, so we just adopt the physical start address of
217        * the content buffer).
218        */
219       specs[index] = p;
220
221     /* Now, we walk through the content buffer, to fix up the data pointers
222      * for the content of any fields for which the start address may have
223      * changed, as a result of buffer size adjustment...
224      */
225     for( index = 0; index < PACKAGE_TAG_COUNT; index++ )
226     {
227       /* ...thus, for each non-empty field...
228        */
229       if( specs[index] != NULL )
230       {
231         /* ...assign the start address of the next available block of
232          * populated content data...
233          */
234         specs[index] = p;
235
236         /* ...and advance the data pointer to the start of the next
237          * available block of content, (if any).
238          */
239         p += strlen( p ) + 1;
240       }
241     }
242   }
243
244   /* Finally, we return the address of the physical starting location
245    * of the (possibly relocated) pkgSpecs content buffer.
246    */
247   return (char*)(content);
248 }
249
250 const char *pkgSpecs::GetTarName( const char *buf )
251 {
252   /* Reconstitute the canonical tarname for the package
253    * identified by the current pkgSpecs record, returning
254    * the result in a C string buffer allocated by malloc();
255    * "buf" may be specified as NULL, otherwise it MUST point
256    * to a similarly allocated buffer, which is recycled.
257    */
258   char *dest = (char*)(realloc( (void*)(buf), buflen( specs ) ));
259   if( (buf = dest) != NULL )
260     /*
261      * We have a valid buffer suitable for returning the result...
262      */
263     for( int index = PACKAGE_NAME; index < PACKAGE_TAG_COUNT; index++ )
264     {
265       /* For each field in the package specification...
266        */
267       char *src = specs[index];
268       if( (src != NULL) && (*src != '\0') )
269       {
270         /* ...for which a non-empty value is defined...
271          */
272         if( dest > buf )
273           /*
274            * ...when not the first such field, insert the
275            * appropriate field separator character...
276            */
277           *dest++ = (index < PACKAGE_FORMAT) ? '-' : '.';
278
279         /* ...then, noting that the release status may have an
280          * initial '$' token which was inserted by get_pkginfo(),...
281          */
282         if( (index == PACKAGE_RELEASE_STATUS) && (*src == '$') )
283           /*
284            * ...and which we don't therefore want to include within
285            * this reverse transformation...
286            */
287           ++src;
288         /*
289          * ...copy the field content to the result buffer.
290          */
291         while( (*dest = *src++) != '\0' )
292           ++dest;
293       }
294     }
295
296   /* Return the fully reconstituted tarname, or NULL if the
297    * return buffer allocation failed.
298    */
299   return buf;
300 }
301
302
303 /*
304  ******************
305  *
306  * Class Implementation: pkgActionItem; SetRequirements method
307  *
308  */
309
310 enum inherit_mode
311 {
312   /* Definition of the inheritance flags identifying those elements
313    * of the package and subsystem version fields, within a requirements
314    * specification, which are to be matched by a "%" wildcard.
315    *
316    * These flags apply to any ONE version number specification, which
317    * may represent EITHER the package version OR the subsystem version
318    * within a requirements specification; it takes the form:
319    *
320    *   major.minor.patch-datestamp-index
321    *
322    * and the flags are interpreted to indicate...
323    */
324   INHERIT_NONE = 0,     /* no "%" wildcard match requested            */
325   INHERIT_VERSION,      /* "major.minor.patch" matches a "%" wildcard */
326   INHERIT_BUILD,        /* "datestamp-index" matches a "%" wildcard   */
327   INHERIT_ALL           /* "%" matches the entire version string      */
328 };
329
330 static
331 enum inherit_mode inherited( const char *ver, const char *bld )
332 {
333   /* Local helper function to assign "%" wildcard inheritance flags
334    * to a specified version number, where:
335    *
336    *   "ver" represents the "major.minor.patch" part of the version
337    *   "bld" represents the optional "datestamp-index" extension
338    */
339   enum inherit_mode retval = INHERIT_NONE;
340
341   /* Noting that the "%" wildcard must represent the respective part
342    * of the version specification in its entirety...
343    */
344   if( (ver != NULL) && (ver[0] == '%') && (ver[1] == '\0') )
345     /*
346      * ...flag a match on the "major.minor.patch" specification.
347      */
348     retval = INHERIT_VERSION;
349
350   /* Considering the optional "datestamp-index" specification...
351    */
352   if( bld == NULL )
353   {
354     /* ...when it isn't specified at all, then it is implicitly
355      * governed by a match on the "major.minor.patch" element...
356      */
357     if( retval == INHERIT_VERSION )
358       /*
359        * ...thus promoting this to become a match on the entire
360        * "major.minor.patch-datestamp-index" specification.
361        */
362       retval = INHERIT_ALL;
363   }
364   else if( (bld[0] == '%') && (bld[1] == '\0') )
365     /*
366      * ...otherwise we may, (less usefully, perhaps and therefore
367      * not to be encouraged), require a "%" wildcard match on the
368      * "datestamp-index" element alone, (with some other criterion
369      * applied to the "major.minor.patch" element -- perhaps even
370      * redundantly "%-%", which is equivalent to the simpler form
371      * of "%" alone, to match the entire version specification).
372      */
373     retval = (enum inherit_mode)(retval | INHERIT_BUILD);
374
375   /* The inheritance state is now fully specified; pass it back
376    * to the caller.
377    */
378   return retval;
379 }
380
381 static
382 const char *requirement( const char *wanted, pkgSpecs *dep )
383 {
384   /* Local helper function to generate "min_wanted" and "max_wanted"
385    * dependency specifications, for assignment within a pkgActionItem;
386    * propagation of version number fields inherited from the dependant,
387    * as specified by the "%" wildcard, is appropriately enforced.  The
388    * resultant specification is ALWAYS returned on the heap, in memory
389    * dynamically allocated by malloc().
390    */
391   pkgSpecs id( wanted );
392
393   /* Evaluate inheritance of the PACKAGE version number specification...
394    */
395   enum inherit_mode clone;
396   clone = inherited( id.GetPackageVersion(), id.GetPackageBuild() );
397   if( (clone & INHERIT_VERSION) > INHERIT_NONE )
398     /*
399      * ...propagating its "major.minor.patch" element as required...
400      */
401     id.SetPackageVersion( dep->GetPackageVersion() );
402
403   if( (clone & INHERIT_BUILD) > INHERIT_NONE )
404     /*
405      * ...and similarly, its "datestamp-index" element.
406      */
407     id.SetPackageBuild( dep->GetPackageBuild() );
408
409   /* Similarly, for the SUBSYSTEM version number specification...
410    */
411   clone = inherited( id.GetSubSystemVersion(), id.GetSubSystemBuild() );
412   if( (clone & INHERIT_VERSION) > INHERIT_NONE )
413     /*
414      * ...propagate its "major.minor.patch" element as required...
415      */
416     id.SetSubSystemVersion( dep->GetSubSystemVersion() );
417
418   if( (clone & INHERIT_BUILD) > INHERIT_NONE )
419     /*
420      * ...and similarly, its "datestamp-index" element.
421      */
422     id.SetSubSystemBuild( dep->GetSubSystemBuild() );
423
424   /* Finally, reconstitute the canonical tarname representation of the
425    * specification, from its decomposed representation; (as a side effect,
426    * this automatically places the return string on the heap).
427    */
428   return id.GetTarName();
429 }
430
431 const char * pkgActionItem::SetRequirements( pkgXmlNode *req, pkgSpecs *dep )
432 {
433   /* Establish the selection criteria, for association of any
434    * particular package release with an action item.
435    */
436   flags &= ACTION_MASK;
437
438   /* First check for a strict equality requirement...
439    */
440   if( (min_wanted = req->GetPropVal( eq_key, NULL )) != NULL )
441     /*
442      * ...and if specified, set the selection range such that only one
443      * specific release can be matched; evaluate any specified dependency
444      * relationship, to ensure that inherited version numbers are correctly
445      * propagated, and assign the resultant dynamically allocated tarname
446      * specifications to the respective constraints.
447      */
448     return max_wanted = min_wanted = requirement( min_wanted, dep );
449
450   else
451   { /* Check for either an inclusive, or a strictly exclusive,
452      * minimum requirement (release "greater" than) specification,
453      * setting the minimum release selector...
454      */
455     if( ((min_wanted = req->GetPropVal( ge_key, NULL )) == NULL)
456     &&  ((min_wanted = req->GetPropVal( gt_key, NULL )) != NULL)  )
457       /*
458        * ...and its selection mode flag accordingly.
459        */
460       flags |= STRICTLY_GT;
461
462     /* Similarly, check for an inclusive, or a strictly exclusive,
463      * maximum requirement (release "less" than) specification,
464      * setting the maximum release selector...
465      */
466     if( ((max_wanted = req->GetPropVal( le_key, NULL )) == NULL)
467     &&  ((max_wanted = req->GetPropVal( lt_key, NULL )) != NULL)  )
468       /*
469        * ...and its selection mode flag accordingly.
470        */
471       flags |= STRICTLY_LT;
472   }
473
474   /* Check the minimum required version specification...
475    */
476   if( min_wanted != NULL )
477     /*
478      * ...ensuring that inherited version numbers are propagated, as
479      * required from the dependant, and assign the canonical tarname
480      * representation of the final specification on the heap.
481      */
482     min_wanted = requirement( min_wanted, dep );
483
484   /* And...
485    */
486   if( max_wanted != NULL )
487     /*
488      * ...likewise for the maximum required version specification.
489      */
490     max_wanted = requirement( max_wanted, dep );
491
492   /* Return a canonical representation of the requirements spec.
493    */
494   return (min_wanted == NULL) ? max_wanted : min_wanted;
495 }
496
497 /* $RCSfile$: end of file */