6 * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7 * Copyright (C) 2009, 2010, 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( newlen > oldlen )
130 /* The buffer size must increase; thus, there is insufficient space
131 * at the location of the field to be modified, to accomodate its new
132 * value, without corrupting the content of following fields. To avoid
133 * this, we use a temporary buffer in which we construct an image of
134 * the modified content...
136 char newbuf[ newlen ];
138 * ...first copying the content of all fields which precede that
139 * which is to be modified...
141 p = bufcpy( 0, index, newbuf, specs );
143 * ...appending the new value to be assigned to the modified field...
145 p = fieldcpy( p, repl );
147 * ...and then the content of any following unmodified fields.
149 bufcpy( index + 1, PACKAGE_TAG_COUNT, p, specs );
151 /* Having created this temporary image of the content buffer, as
152 * it is to become, adjust the allocation size of the original buffer,
153 * and copy its modified content into place.
155 if( (p = (char *)(content = realloc( content, newlen ))) != NULL )
156 memcpy( p, newbuf, newlen );
160 { /* In this case, the size of content buffer will either shrink, or
161 * it will remain unchanged; we may modify it in place, beginning at
162 * the location of the field which is to be modified, by writing its
163 * new value, if any, into place.
166 if( (repl != NULL) && (*repl != '\0') )
167 p = fieldcpy( p, repl );
169 /* If the new content of the modified field is exactly the same length
170 * as the content it has replaced, then the overall size of the content
171 * buffer does not change...
173 if( newlen == oldlen )
175 * ...so we have nothing more to do, to complete the modification...
177 return (char *)(content);
179 /* ...otherwise, there is residual data from the original content at
180 * the location now indicated by "p", and preceding the start of the
181 * content of the next populated field, (if any); clear this, moving
182 * the content of all following fields up to fill the gap...
184 bufcpy( index + 1, PACKAGE_TAG_COUNT, p, specs );
186 /* ...and adjust the allocation size to the reduced data length.
188 p = (char *)(content = realloc( content, newlen ));
191 /* Irrespective of which of the preceding paths we followed to get here,
192 * the size of the content buffer was adjusted, so...
196 /* ...provided it was not lost altogether...
198 if( (repl == NULL) || (*repl == '\0') )
200 * ...we either mark the content of the modified field as "deleted",
201 * when appropriate...
206 /* ...or we ensure that it is marked as having SOME content, in case
207 * it may have been empty beforehand; (note that this does NOT assign
208 * the correct pointer value -- any arbitrary non-NULL pointer value
209 * will suffice here, so we just adopt the physical start address of
210 * the content buffer).
214 /* Now, we walk through the content buffer, to fix up the data pointers
215 * for the content of any fields for which the start address may have
216 * changed, as a result of buffer size adjustment...
218 for( index = 0; index < PACKAGE_TAG_COUNT; index++ )
220 /* ...thus, for each non-empty field...
222 if( specs[index] != NULL )
224 /* ...assign the start address of the next available block of
225 * populated content data...
229 /* ...and advance the data pointer to the start of the next
230 * available block of content, (if any).
232 p += strlen( p ) + 1;
237 /* Finally, we return the address of the physical starting location
238 * of the (possibly relocated) pkgSpecs content buffer.
240 return (char*)(content);
243 const char *pkgSpecs::GetTarName( const char *buf )
245 /* Reconstitute the canonical tarname for the package
246 * identified by the current pkgSpecs record, returning
247 * the result in a C string buffer allocated by malloc();
248 * "buf" may be specified as NULL, otherwise it MUST point
249 * to a similarly allocated buffer, which is recycled.
251 char *dest = (char*)(realloc( (void*)(buf), buflen( specs ) ));
252 if( (buf = dest) != NULL )
254 * We have a valid buffer suitable for returning the result...
256 for( int index = PACKAGE_NAME; index < PACKAGE_TAG_COUNT; index++ )
258 /* For each field in the package specification...
260 char *src = specs[index];
261 if( (src != NULL) && (*src != '\0') )
263 /* ...for which a non-empty value is defined...
267 * ...when not the first such field, insert the
268 * appropriate field separator character...
270 *dest++ = (index < PACKAGE_FORMAT) ? '-' : '.';
272 /* ...then copy the field content to the result buffer.
274 while( (*dest = *src++) != '\0' )
279 /* Return the fully reconstituted tarname, or NULL if the
280 * return buffer allocation failed.
289 * Class Implementation: pkgActionItem; SetRequirements method
295 /* Definition of the inheritance flags identifying those elements
296 * of the package and subsystem version fields, within a requirements
297 * specification, which are to be matched by a "%" wildcard.
299 * These flags apply to any ONE version number specification, which
300 * may represent EITHER the package version OR the subsystem version
301 * within a requirements specification; it takes the form:
303 * major.minor.patch-datestamp-index
305 * and the flags are interpreted to indicate...
307 INHERIT_NONE = 0, /* no "%" wildcard match requested */
308 INHERIT_VERSION, /* "major.minor.patch" matches a "%" wildcard */
309 INHERIT_BUILD, /* "datestamp-index" matches a "%" wildcard */
310 INHERIT_ALL /* "%" matches the entire version string */
314 enum inherit_mode inherited( const char *ver, const char *bld )
316 /* Local helper function to assign "%" wildcard inheritance flags
317 * to a specified version number, where:
319 * "ver" represents the "major.minor.patch" part of the version
320 * "bld" represents the optional "datestamp-index" extension
322 enum inherit_mode retval = INHERIT_NONE;
324 /* Noting that the "%" wildcard must represent the respective part
325 * of the version specification in its entirety...
327 if( (ver != NULL) && (ver[0] == '%') && (ver[1] == '\0') )
329 * ...flag a match on the "major.minor.patch" specification.
331 retval = INHERIT_VERSION;
333 /* Considering the optional "datestamp-index" specification...
337 /* ...when it isn't specified at all, then it is implicitly
338 * governed by a match on the "major.minor.patch" element...
340 if( retval == INHERIT_VERSION )
342 * ...thus promoting this to become a match on the entire
343 * "major.minor.patch-datestamp-index" specification.
345 retval = INHERIT_ALL;
347 else if( (bld[0] == '%') && (bld[1] == '\0') )
349 * ...otherwise we may, (less usefully, perhaps and therefore
350 * not to be encouraged), require a "%" wildcard match on the
351 * "datestamp-index" element alone, (with some other criterion
352 * applied to the "major.minor.patch" element -- perhaps even
353 * redundantly "%-%", which is equivalent to the simpler form
354 * of "%" alone, to match the entire version specification).
356 retval = (enum inherit_mode)(retval | INHERIT_BUILD);
358 /* The inheritance state is now fully specified; pass it back
365 const char *requirement( const char *wanted, pkgSpecs *dep )
367 /* Local helper function to generate "min_wanted" and "max_wanted"
368 * dependency specifications, for assignment within a pkgActionItem;
369 * propagation of version number fields inherited from the dependant,
370 * as specified by the "%" wildcard, is appropriately enforced. The
371 * resultant specification is ALWAYS returned on the heap, in memory
372 * dynamically allocated by malloc().
374 pkgSpecs id( wanted );
376 /* Evaluate inheritance of the PACKAGE version number specification...
378 enum inherit_mode clone;
379 clone = inherited( id.GetPackageVersion(), id.GetPackageBuild() );
380 if( (clone & INHERIT_VERSION) > INHERIT_NONE )
382 * ...propagating its "major.minor.patch" element as required...
384 id.SetPackageVersion( dep->GetPackageVersion() );
386 if( (clone & INHERIT_BUILD) > INHERIT_NONE )
388 * ...and similarly, its "datestamp-index" element.
390 id.SetPackageBuild( dep->GetPackageBuild() );
392 /* Similarly, for the SUBSYSTEM version number specification...
394 clone = inherited( id.GetSubSystemVersion(), id.GetSubSystemBuild() );
395 if( (clone & INHERIT_VERSION) > INHERIT_NONE )
397 * ...propagate its "major.minor.patch" element as required...
399 id.SetSubSystemVersion( dep->GetSubSystemVersion() );
401 if( (clone & INHERIT_BUILD) > INHERIT_NONE )
403 * ...and similarly, its "datestamp-index" element.
405 id.SetSubSystemBuild( dep->GetSubSystemBuild() );
407 /* Finally, reconstitute the canonical tarname representation of the
408 * specification, from its decomposed representation; (as a side effect,
409 * this automatically places the return string on the heap).
411 return id.GetTarName();
414 const char * pkgActionItem::SetRequirements( pkgXmlNode *req, pkgSpecs *dep )
416 /* Establish the selection criteria, for association of any
417 * particular package release with an action item.
419 flags &= ACTION_MASK;
421 /* First check for a strict equality requirement...
423 if( (min_wanted = req->GetPropVal( eq_key, NULL )) != NULL )
425 * ...and if specified, set the selection range such that only one
426 * specific release can be matched; evaluate any specified dependency
427 * relationship, to ensure that inherited version numbers are correctly
428 * propagated, and assign the resultant dynamically allocated tarname
429 * specifications to the respective constraints.
431 return max_wanted = min_wanted = requirement( min_wanted, dep );
434 { /* Check for either an inclusive, or a strictly exclusive,
435 * minimum requirement (release "greater" than) specification,
436 * setting the minimum release selector...
438 if( ((min_wanted = req->GetPropVal( ge_key, NULL )) == NULL)
439 && ((min_wanted = req->GetPropVal( gt_key, NULL )) != NULL) )
441 * ...and its selection mode flag accordingly.
443 flags |= STRICTLY_GT;
445 /* Similarly, check for an inclusive, or a strictly exclusive,
446 * maximum requirement (release "less" than) specification,
447 * setting the maximum release selector...
449 if( ((max_wanted = req->GetPropVal( le_key, NULL )) == NULL)
450 && ((max_wanted = req->GetPropVal( lt_key, NULL )) != NULL) )
452 * ...and its selection mode flag accordingly.
454 flags |= STRICTLY_LT;
457 /* Check the minimum required version specification...
459 if( min_wanted != NULL )
461 * ...ensuring that inherited version numbers are propagated, as
462 * required from the dependant, and assign the canonical tarname
463 * representation of the final specification on the heap.
465 min_wanted = requirement( min_wanted, dep );
469 if( max_wanted != NULL )
471 * ...likewise for the maximum required version specification.
473 max_wanted = requirement( max_wanted, dep );
475 /* Return a canonical representation of the requirements spec.
477 return (min_wanted == NULL) ? max_wanted : min_wanted;
480 /* $RCSfile$: end of file */