6 * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7 * Copyright (C) 2009, 2010, 2011, MinGW Project
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.
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.
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.
39 * Class Implementation: pkgSpecs; SetProperty and GetTarName methods
44 int buflen( pkginfo_t specs )
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).
51 int index = PACKAGE_TAG_COUNT;
52 while( (index-- > 0) && (specs[index] == NULL) )
54 * Locate the last populated data field in the content buffer...
56 /* ...then compute the total buffer length, from the start of
57 * the first field, up to the NUL terminator of the last.
59 return specs[index] ? specs[index] - *specs + strlen( specs[index] ) + 1 : 0;
63 int fieldlen( const char *string )
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.
70 return (string == NULL) ? 0 : strlen( string );
74 char *fieldcpy( char *dest, const char *src )
76 /* Local helper function to copy the content of a single
77 * pkgSpecs data field...
79 while( (*dest++ = *src++) != '\0' )
81 * ...copying byte by byte, including the terminating NUL,
82 * to the destination buffer...
84 /* ...and ultimately, return the next available location
85 * within the destination buffer.
91 char *bufcpy( int index, int end, char *buf, pkginfo_t specs )
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".
97 do { if( specs[index] != NULL )
99 * Copy each non-empty data field...
101 buf = fieldcpy( buf, specs[index] );
103 /* ...until all specified fields in the source range
106 } while( ++index < end );
108 /* Finally, return the location at which any further data should
109 * be appended to the data just copied.
114 const char *pkgSpecs::SetProperty( int index, const char *repl )
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.
122 /* We begin by computing the current size of the content buffer,
123 * and the size it will become after making the change.
125 int oldlen = buflen( specs );
126 int newlen = oldlen + fieldlen( repl ) - fieldlen( specs[index] );
128 if( (repl != NULL) && (specs[index] == NULL) )
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.
135 if( newlen > oldlen )
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...
143 char newbuf[ newlen ];
145 * ...first copying the content of all fields which precede that
146 * which is to be modified...
148 p = bufcpy( 0, index, newbuf, specs );
150 * ...appending the new value to be assigned to the modified field...
152 p = fieldcpy( p, repl );
154 * ...and then the content of any following unmodified fields.
156 bufcpy( index + 1, PACKAGE_TAG_COUNT, p, specs );
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.
162 if( (p = (char *)(content = realloc( content, newlen ))) != NULL )
163 memcpy( p, newbuf, newlen );
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.
173 if( (repl != NULL) && (*repl != '\0') )
174 p = fieldcpy( p, repl );
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...
180 if( newlen == oldlen )
182 * ...so we have nothing more to do, to complete the modification...
184 return (char *)(content);
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...
191 bufcpy( index + 1, PACKAGE_TAG_COUNT, p, specs );
193 /* ...and adjust the allocation size to the reduced data length.
195 p = (char *)(content = realloc( content, newlen ));
198 /* Irrespective of which of the preceding paths we followed to get here,
199 * the size of the content buffer was adjusted, so...
203 /* ...provided it was not lost altogether...
205 if( (repl == NULL) || (*repl == '\0') )
207 * ...we either mark the content of the modified field as "deleted",
208 * when appropriate...
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).
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...
225 for( index = 0; index < PACKAGE_TAG_COUNT; index++ )
227 /* ...thus, for each non-empty field...
229 if( specs[index] != NULL )
231 /* ...assign the start address of the next available block of
232 * populated content data...
236 /* ...and advance the data pointer to the start of the next
237 * available block of content, (if any).
239 p += strlen( p ) + 1;
244 /* Finally, we return the address of the physical starting location
245 * of the (possibly relocated) pkgSpecs content buffer.
247 return (char*)(content);
250 const char *pkgSpecs::GetTarName( const char *buf )
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.
258 char *dest = (char*)(realloc( (void*)(buf), buflen( specs ) ));
259 if( (buf = dest) != NULL )
261 * We have a valid buffer suitable for returning the result...
263 for( int index = PACKAGE_NAME; index < PACKAGE_TAG_COUNT; index++ )
265 /* For each field in the package specification...
267 char *src = specs[index];
268 if( (src != NULL) && (*src != '\0') )
270 /* ...for which a non-empty value is defined...
274 * ...when not the first such field, insert the
275 * appropriate field separator character...
277 *dest++ = (index < PACKAGE_FORMAT) ? '-' : '.';
279 /* ...then, noting that the release status may have an
280 * initial '$' token which was inserted by get_pkginfo(),...
282 if( (index == PACKAGE_RELEASE_STATUS) && (*src == '$') )
284 * ...and which we don't therefore want to include within
285 * this reverse transformation...
289 * ...copy the field content to the result buffer.
291 while( (*dest = *src++) != '\0' )
296 /* Return the fully reconstituted tarname, or NULL if the
297 * return buffer allocation failed.
306 * Class Implementation: pkgActionItem; SetRequirements method
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.
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:
320 * major.minor.patch-datestamp-index
322 * and the flags are interpreted to indicate...
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 */
331 enum inherit_mode inherited( const char *ver, const char *bld )
333 /* Local helper function to assign "%" wildcard inheritance flags
334 * to a specified version number, where:
336 * "ver" represents the "major.minor.patch" part of the version
337 * "bld" represents the optional "datestamp-index" extension
339 enum inherit_mode retval = INHERIT_NONE;
341 /* Noting that the "%" wildcard must represent the respective part
342 * of the version specification in its entirety...
344 if( (ver != NULL) && (ver[0] == '%') && (ver[1] == '\0') )
346 * ...flag a match on the "major.minor.patch" specification.
348 retval = INHERIT_VERSION;
350 /* Considering the optional "datestamp-index" specification...
354 /* ...when it isn't specified at all, then it is implicitly
355 * governed by a match on the "major.minor.patch" element...
357 if( retval == INHERIT_VERSION )
359 * ...thus promoting this to become a match on the entire
360 * "major.minor.patch-datestamp-index" specification.
362 retval = INHERIT_ALL;
364 else if( (bld[0] == '%') && (bld[1] == '\0') )
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).
373 retval = (enum inherit_mode)(retval | INHERIT_BUILD);
375 /* The inheritance state is now fully specified; pass it back
382 const char *requirement( const char *wanted, pkgSpecs *dep )
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().
391 pkgSpecs id( wanted );
393 /* Evaluate inheritance of the PACKAGE version number specification...
395 enum inherit_mode clone;
396 clone = inherited( id.GetPackageVersion(), id.GetPackageBuild() );
397 if( (clone & INHERIT_VERSION) > INHERIT_NONE )
399 * ...propagating its "major.minor.patch" element as required...
401 id.SetPackageVersion( dep->GetPackageVersion() );
403 if( (clone & INHERIT_BUILD) > INHERIT_NONE )
405 * ...and similarly, its "datestamp-index" element.
407 id.SetPackageBuild( dep->GetPackageBuild() );
409 /* Similarly, for the SUBSYSTEM version number specification...
411 clone = inherited( id.GetSubSystemVersion(), id.GetSubSystemBuild() );
412 if( (clone & INHERIT_VERSION) > INHERIT_NONE )
414 * ...propagate its "major.minor.patch" element as required...
416 id.SetSubSystemVersion( dep->GetSubSystemVersion() );
418 if( (clone & INHERIT_BUILD) > INHERIT_NONE )
420 * ...and similarly, its "datestamp-index" element.
422 id.SetSubSystemBuild( dep->GetSubSystemBuild() );
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).
428 return id.GetTarName();
431 const char * pkgActionItem::SetRequirements( pkgXmlNode *req, pkgSpecs *dep )
433 /* Establish the selection criteria, for association of any
434 * particular package release with an action item.
436 flags &= ACTION_MASK;
438 /* First check for a strict equality requirement...
440 if( (min_wanted = req->GetPropVal( eq_key, NULL )) != NULL )
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.
448 return max_wanted = min_wanted = requirement( min_wanted, dep );
451 { /* Check for either an inclusive, or a strictly exclusive,
452 * minimum requirement (release "greater" than) specification,
453 * setting the minimum release selector...
455 if( ((min_wanted = req->GetPropVal( ge_key, NULL )) == NULL)
456 && ((min_wanted = req->GetPropVal( gt_key, NULL )) != NULL) )
458 * ...and its selection mode flag accordingly.
460 flags |= STRICTLY_GT;
462 /* Similarly, check for an inclusive, or a strictly exclusive,
463 * maximum requirement (release "less" than) specification,
464 * setting the maximum release selector...
466 if( ((max_wanted = req->GetPropVal( le_key, NULL )) == NULL)
467 && ((max_wanted = req->GetPropVal( lt_key, NULL )) != NULL) )
469 * ...and its selection mode flag accordingly.
471 flags |= STRICTLY_LT;
474 /* Check the minimum required version specification...
476 if( min_wanted != NULL )
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.
482 min_wanted = requirement( min_wanted, dep );
486 if( max_wanted != NULL )
488 * ...likewise for the maximum required version specification.
490 max_wanted = requirement( max_wanted, dep );
492 /* Return a canonical representation of the requirements spec.
494 return (min_wanted == NULL) ? max_wanted : min_wanted;
497 /* $RCSfile$: end of file */