OSDN Git Service

Ignore spin wait requests with no designated referrer.
[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 inline
331 bool inherited_requirement( const char *field )
332 {
333   /* Local helper function to identify a template field which is
334    * specified exactly as "%", and therefore must inherit its value
335    * from the referring package specification.
336    */
337   return ((field != NULL) && (field[0] == '%') && (field[1] == '\0'));
338 }
339
340 static
341 enum inherit_mode inherited( const char *ver, const char *bld )
342 {
343   /* Local helper function to assign "%" wildcard inheritance flags
344    * to a specified version number, where:
345    *
346    *   "ver" represents the "major.minor.patch" part of the version
347    *   "bld" represents the optional "datestamp-index" extension
348    */
349   enum inherit_mode retval = INHERIT_NONE;
350
351   /* Noting that the "%" wildcard must represent the respective part
352    * of the version specification in its entirety...
353    */
354   if( inherited_requirement( ver ) )
355     /*
356      * ...flag a match on the "major.minor.patch" specification.
357      */
358     retval = INHERIT_VERSION;
359
360   /* Considering the optional "datestamp-index" specification...
361    */
362   if( bld == NULL )
363   {
364     /* ...when it isn't specified at all, then it is implicitly
365      * governed by a match on the "major.minor.patch" element...
366      */
367     if( retval == INHERIT_VERSION )
368       /*
369        * ...thus promoting this to become a match on the entire
370        * "major.minor.patch-datestamp-index" specification.
371        */
372       retval = INHERIT_ALL;
373   }
374   else if( (bld[0] == '%') && (bld[1] == '\0') )
375     /*
376      * ...otherwise we may, (less usefully, perhaps and therefore
377      * not to be encouraged), require a "%" wildcard match on the
378      * "datestamp-index" element alone, (with some other criterion
379      * applied to the "major.minor.patch" element -- perhaps even
380      * redundantly "%-%", which is equivalent to the simpler form
381      * of "%" alone, to match the entire version specification).
382      */
383     retval = (enum inherit_mode)(retval | INHERIT_BUILD);
384
385   /* The inheritance state is now fully specified; pass it back
386    * to the caller.
387    */
388   return retval;
389 }
390
391 static
392 const char *requirement( const char *wanted, pkgSpecs *dep )
393 {
394   /* Local helper function to generate "min_wanted" and "max_wanted"
395    * dependency specifications, for assignment within a pkgActionItem;
396    * propagation of version number fields inherited from the dependant,
397    * as specified by the "%" wildcard, is appropriately enforced.  The
398    * resultant specification is ALWAYS returned on the heap, in memory
399    * dynamically allocated by malloc().
400    */
401   pkgSpecs id( wanted );
402
403   /* Evaluate inheritance of the PACKAGE version number specification...
404    */
405   enum inherit_mode clone;
406   clone = inherited( id.GetPackageVersion(), id.GetPackageBuild() );
407   if( (clone & INHERIT_VERSION) > INHERIT_NONE )
408   {
409     /* ...propagating its "major.minor.patch" element as required.
410      */
411     id.SetPackageVersion( dep->GetPackageVersion() );
412
413     /* We also consider any release status specification to represent
414      * an extension to the normal package version specification, so...
415      */
416     if( id.GetReleaseStatus() == NULL )
417     {
418       /* ...when this isn't explicitly overridden in the requirement
419        * specification, we propagate it, together with any associated
420        * release reference index, from the reference specification.
421        */
422       id.SetReleaseStatus( dep->GetReleaseStatus() );
423       id.SetReleaseIndex( dep->GetReleaseIndex() );
424     }
425   }
426   if( (clone & INHERIT_BUILD) > INHERIT_NONE )
427     /*
428      * Likewise, for any associated "datestamp-index" element.
429      */
430     id.SetPackageBuild( dep->GetPackageBuild() );
431
432   /* Similarly, for the SUBSYSTEM version number specification...
433    */
434   clone = inherited( id.GetSubSystemVersion(), id.GetSubSystemBuild() );
435   if( (clone & INHERIT_VERSION) > INHERIT_NONE )
436     /*
437      * ...propagate its "major.minor.patch" element as required...
438      */
439     id.SetSubSystemVersion( dep->GetSubSystemVersion() );
440
441   if( (clone & INHERIT_BUILD) > INHERIT_NONE )
442     /*
443      * ...and similarly, its "datestamp-index" element.
444      */
445     id.SetSubSystemBuild( dep->GetSubSystemBuild() );
446
447   /* To establish association between source and binary package names,
448    * we may also need to propagate archive and compression type fields...
449    */
450   if( inherited_requirement( id.GetPackageFormat()) )
451   {
452     id.SetPackageFormat( dep->GetPackageFormat() );
453     if( id.GetCompressionType() == NULL )
454       id.SetCompressionType( dep->GetCompressionType() );
455   }
456   else if( inherited_requirement( id.GetCompressionType()) )
457     id.SetCompressionType( dep->GetCompressionType() );
458
459   /* Finally, reconstitute the canonical tarname representation of the
460    * specification, from its decomposed representation; (as a side effect,
461    * this automatically places the return string on the heap).
462    */
463   return id.GetTarName();
464 }
465
466 const char * pkgActionItem::SetRequirements( pkgXmlNode *req, pkgSpecs *dep )
467 {
468   /* Establish the selection criteria, for association of any
469    * particular package release with an action item.
470    */
471   flags &= ACTION_MASK;
472
473   /* First check for a strict equality requirement...
474    */
475   if( (min_wanted = req->GetPropVal( eq_key, NULL )) != NULL )
476     /*
477      * ...and if specified, set the selection range such that only one
478      * specific release can be matched; evaluate any specified dependency
479      * relationship, to ensure that inherited version numbers are correctly
480      * propagated, and assign the resultant dynamically allocated tarname
481      * specifications to the respective constraints.
482      */
483     return max_wanted = min_wanted = requirement( min_wanted, dep );
484
485   else
486   { /* Check for either an inclusive, or a strictly exclusive,
487      * minimum requirement (release "greater" than) specification,
488      * setting the minimum release selector...
489      */
490     if( ((min_wanted = req->GetPropVal( ge_key, NULL )) == NULL)
491     &&  ((min_wanted = req->GetPropVal( gt_key, NULL )) != NULL)  )
492       /*
493        * ...and its selection mode flag accordingly.
494        */
495       flags |= STRICTLY_GT;
496
497     /* Similarly, check for an inclusive, or a strictly exclusive,
498      * maximum requirement (release "less" than) specification,
499      * setting the maximum release selector...
500      */
501     if( ((max_wanted = req->GetPropVal( le_key, NULL )) == NULL)
502     &&  ((max_wanted = req->GetPropVal( lt_key, NULL )) != NULL)  )
503       /*
504        * ...and its selection mode flag accordingly.
505        */
506       flags |= STRICTLY_LT;
507   }
508
509   /* Check the minimum required version specification...
510    */
511   if( min_wanted != NULL )
512     /*
513      * ...ensuring that inherited version numbers are propagated, as
514      * required from the dependant, and assign the canonical tarname
515      * representation of the final specification on the heap.
516      */
517     min_wanted = requirement( min_wanted, dep );
518
519   /* And...
520    */
521   if( max_wanted != NULL )
522     /*
523      * ...likewise for the maximum required version specification.
524      */
525     max_wanted = requirement( max_wanted, dep );
526
527   /* Return a canonical representation of the requirements spec.
528    */
529   return (min_wanted == NULL) ? max_wanted : min_wanted;
530 }
531
532 EXTERN_C const char *pkgAssociateName( const char *map, const char *from )
533 {
534   /* Public function for derivation of the name of an associate package,
535    * such as a source package, from the name of the primary package, based
536    * on the relationship specified by the template passed via "map".
537    */
538   pkgSpecs ref( from );
539   return requirement( map, &ref );
540 }
541
542 /* $RCSfile$: end of file */