/ libpkg / pkg_version.c
pkg_version.c
  1  /*-
  2   * Copyright (c) 2011 Philippe Pepiot <phil@philpep.org>
  3   * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
  4   * All rights reserved.
  5   * 
  6   * Redistribution and use in source and binary forms, with or without
  7   * modification, are permitted provided that the following conditions
  8   * are met:
  9   * 1. Redistributions of source code must retain the above copyright
 10   *    notice, this list of conditions and the following disclaimer
 11   *    in this position and unchanged.
 12   * 2. Redistributions in binary form must reproduce the above copyright
 13   *    notice, this list of conditions and the following disclaimer in the
 14   *    documentation and/or other materials provided with the distribution.
 15   * 
 16   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 17   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 18   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 19   * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 20   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 21   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 22   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 23   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 24   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 25   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 26   */
 27  
 28  
 29  #include <assert.h>
 30  #include <string.h>
 31  #include <stdlib.h>
 32  #include <ctype.h>
 33  
 34  #include "pkg.h"
 35  #include "private/event.h"
 36  #include "private/pkg.h"
 37  
 38  /*
 39   * split_version(pkgname, endname, epoch, revision) returns a pointer to
 40   * the version portion of a package name and the two special components.
 41   *
 42   * Syntax is:  ${PORTNAME}-${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
 43   *
 44   * Written by Oliver Eikemeier
 45   * Based on work of Jeremy D. Lea.
 46   */
 47  static const char *
 48  split_version(const char *pkgname, const char **endname,
 49      unsigned long *epoch, unsigned long *revision)
 50  {
 51  	char *ch;
 52  	const char *versionstr;
 53  	const char *endversionstr;
 54  
 55  	if (pkgname == NULL) {
 56  		pkg_emit_error("%s: Passed NULL pkgname.", __func__);
 57  		return (NULL);
 58  	}
 59  
 60  	/* Look for the last '-' the the pkgname */
 61  	ch = strrchr(pkgname, '-');
 62  	/* Cheat if we are just passed a version, not a valid package name */
 63  	versionstr = ch ? ch + 1 : pkgname;
 64  
 65  	/*
 66  	 * Look for the last '_' in the version string, advancing the
 67  	 * end pointer.
 68  	 */
 69  	ch = strrchr(versionstr, '_');
 70  
 71  	if (revision != NULL)
 72  		*revision = ch ? strtoul(ch + 1, NULL, 10) : 0;
 73  
 74  	endversionstr = ch;
 75  
 76  	/* Look for the last ',' in the remaining version string */
 77  	ch = strrchr(endversionstr ? endversionstr + 1 : versionstr, ',');
 78  
 79  	if (epoch != NULL)
 80  		*epoch = ch ? strtoul(ch + 1, NULL, 10) : 0;
 81  
 82  	if (ch && !endversionstr)
 83  		endversionstr = ch;
 84  
 85  	/*
 86  	 * set the pointer behind the last character of the version without
 87  	 * revision or epoch
 88  	 */
 89  	if (endname)
 90  		*endname = endversionstr ? endversionstr :
 91  					   strrchr(versionstr, '\0');
 92  
 93  	return versionstr;
 94  }
 95  
 96  /*
 97   * PORTVERSIONs are composed of components separated by dots. A component
 98   * consists of a version number, a letter and a patchlevel number. This does
 99   * not conform to the porter's handbook, but let us formulate rules that
100   * fit the current practice and are far simpler than to make decisions
101   * based on the order of netters and lumbers. Besides, people use versions
102   * like 10b2 in the ports...
103   */
104  
105  typedef struct {
106  #ifdef __LONG_LONG_SUPPORTED
107  	long long n;
108  	long long pl;
109  #else
110  	long n;
111  	long pl;
112  #endif
113  	int a;
114  } version_component;
115  
116  /*
117   * get_component(position, component) gets the value of the next component
118   * (number - letter - number triple) and returns a pointer to the next character
119   * after any leading separators
120   *
121   * - components are separated by dots
122   * - characters !~ [a-zA-Z0-9.+*] are treated as separators
123   *   (1.0:2003.09.16 = 1.0.2003.09.16), this may not be what you expect:
124   *   1.0.1:2003.09.16 < 1.0:2003.09.16
125   * - consecutive separators are collapsed (10..1 = 10.1)
126   * - missing separators are inserted, essentially
127   *   letter number letter => letter number . letter (10a1b2 = 10a1.b2)
128   * - missing components are assumed to be equal to 0 (10 = 10.0 = 10.0.0)
129   * - the letter sort order is: [none], a, b, ..., z; numbers without letters
130   *   sort first (10 < 10a < 10b)
131   * - missing version numbers (in components starting with a letter) sort as -1
132   *   (a < 0, 10.a < 10)
133   * - a separator is inserted before the special strings "pl", "alpha", "beta",
134   *   "pre" and "rc".
135   * - "pl" sorts before every other letter, "alpha", "beta", "pre" and "rc"
136   *   sort as a, b, p and r. (10alpha = 10.a < 10, but 10 < 10a; pl11 < alpha3
137   *   < 0.1beta2 = 0.1.b2 < 0.1)
138   * - other strings use only the first letter for sorting, case is ignored
139   *   (1.d2 = 1.dev2 = 1.Development2)
140   * - The special component `*' is guaranteed to be the smallest possible
141   *   component (2.* < 2pl1 < 2alpha3 < 2.9f7 < 3.*)
142   * - components separated by `+' are handled by version_cmp below
143   *
144   * Oliver Eikemeier
145   */
146  
147  static const struct stage {
148  	const char *name;
149  	size_t namelen;
150  	int value;
151  } stages[] = {
152  	{ "pl",    2,  0            },
153  	{ "snap",  6,  1            },
154  #define	ABASE	2	/* Last special early-sorted prefix + 1 */
155  	{ "alpha", 5, 'a'-'a'+ABASE },
156  	{ "beta",  4, 'b'-'a'+ABASE },
157  	{ "pre",   3, 'p'-'a'+ABASE },
158  	{ "rc",    2, 'r'-'a'+ABASE },
159  };
160  
161  static const char *
162  get_component(const char *position, version_component *component)
163  {
164  	const char *pos = position;
165  	int hasstage = 0, haspatchlevel = 0;
166  
167  	if (!pos) {
168  		pkg_emit_error("%s: Passed NULL position.", __func__);
169  		return (NULL);
170  	}
171  
172  	/* handle version number */
173  	if (isdigit(*pos)) {
174  		char *endptr;
175  #ifdef __LONG_LONG_SUPPORTED
176  		component->n = strtoll(pos, &endptr, 10);
177  #else
178  		component->n = strtol(pos, &endptr, 10);
179  #endif
180  		/* should we test for errno == ERANGE? */
181  		pos = endptr;
182  	} else if (*pos == '*') {
183  		component->n = -2;
184  		do {
185  			pos++;
186  		} while(*pos && *pos != '+');
187  	} else {
188  		component->n = -1;
189  		hasstage = 1;
190  	}
191  
192  	/* handle letter */
193  	if (isalpha(*pos)) {
194  		int c = tolower(*pos);
195  		haspatchlevel = 1;
196  		/* handle special suffixes */
197  		if (isalpha(pos[1])) {
198  			unsigned int i;
199  			for (i = 0; i < NELEM(stages); i++) {
200  				const struct stage *stage = &stages[i];
201  				size_t len = stage->namelen;
202  				if (strncasecmp(pos, stage->name, len) == 0 &&
203  				    !isalpha(pos[stage->namelen])) {
204  					if (hasstage) {
205  						/* stage to value */
206  						component->a = stage->value;
207  						pos += stage->namelen;
208  					} else {
209  						/* insert dot */
210  						component->a = 0;
211  						haspatchlevel = 0;
212  					}
213  					c = 0;
214  					break;
215  				}
216  			}
217  		}
218  		/* unhandled above */
219  		if (c) {
220  			/* use the first letter and skip following */
221  			component->a = c - 'a' + ABASE;
222  			do {
223  				++pos;
224  			} while (isalpha(*pos));
225  		}
226  	} else {
227  		component->a = 0;
228  		haspatchlevel = 0;
229  	}
230  
231  	if (haspatchlevel) {
232  		/* handle patch number */
233  		if (isdigit(*pos)) {
234  			char *endptr;
235  #ifdef __LONG_LONG_SUPPORTED
236  			component->pl = strtoll(pos, &endptr, 10);
237  #else
238  			component->pl = strtol(pos, &endptr, 10);
239  #endif
240  			/* should we test for errno == ERANGE? */
241  			pos = endptr;
242  		} else {
243  			component->pl = -1;
244  		}
245  	} else {
246  		component->pl = 0;
247  	}
248  
249  	/* skip trailing separators */
250  	while (*pos && !isdigit(*pos) && !isalpha(*pos)
251  	    && *pos != '+' && *pos != '*')
252  		pos++;
253  
254  	return pos;
255  }
256  
257  /*
258   * version_cmp(pkg1, pkg2) returns -1, 0 or 1 depending on if the version
259   * components of pkg1 is less than, equal to or greater than pkg2. No
260   * comparison of the basenames is done.
261   *
262   * The port version is defined by:
263   * ${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
264   * ${PORTEPOCH} supersedes ${PORTVERSION} supersedes ${PORTREVISION}.
265   * See the commit log for revision 1.349 of ports/Mk/bsd.port.mk
266   * for more information.
267   *
268   * The epoch and revision are defined to be a single number, while the rest
269   * of the version should conform to the porting guidelines. It can contain
270   * multiple components, separated by a period, including letters.
271   *
272   * The tests allow for significantly more latitude in the version numbers
273   * than is allowed in the guidelines. No point in enforcing them here.
274   * That's what portlint is for.
275   *
276   * Jeremy D. Lea.
277   * reimplemented by Oliver Eikemeier
278   */
279  int
280  pkg_version_cmp(const char * const pkg1, const char * const pkg2)
281  {
282  	const char *v1, *v2, *ve1, *ve2;
283  	unsigned long e1, e2, r1, r2;
284  	int result = 0;
285  
286  	v1 = split_version(pkg1, &ve1, &e1, &r1);
287  	v2 = split_version(pkg2, &ve2, &e2, &r2);
288  
289  	assert (v1 != NULL && v2 != NULL);
290  
291  	/* Check epoch, port version, and port revision, in that order. */
292  	if (e1 != e2)
293  		result = (e1 < e2 ? -1 : 1);
294  
295  	/* Shortcut check for equality before invoking the parsing routines. */
296  	if (result == 0 &&
297  	    (ve1 - v1 != ve2 - v2 || strncasecmp(v1, v2, ve1 - v1) != 0)) {
298  		/*
299  		 * Loop over different components (the parts separated by dots).
300  		 * If any component differs, there is an inequality.
301  		 */
302  		while (result == 0 && (v1 < ve1 || v2 < ve2)) {
303  			int block_v1 = 0;
304  			int block_v2 = 0;
305  			version_component vc1 = {0, 0, 0};
306  			version_component vc2 = {0, 0, 0};
307  			if (v1 < ve1 && *v1 != '+') {
308  				v1 = get_component(v1, &vc1);
309  				assert (v1 != NULL);
310  			} else {
311  				block_v1 = 1;
312  			}
313  			if (v2 < ve2 && *v2 != '+') {
314  				v2 = get_component(v2, &vc2);
315  				assert (v2 != NULL);
316  			} else {
317  				block_v2 = 1;
318  			}
319  			if (block_v1 && block_v2) {
320  				if (v1 < ve1)
321  					v1++;
322  				if (v2 < ve2)
323  					v2++;
324  			} else if (vc1.n != vc2.n) {
325  				result = (vc1.n < vc2.n ? -1 : 1);
326  			} else if (vc1.a != vc2.a) {
327  				result = (vc1.a < vc2.a ? -1 : 1);
328  			} else if (vc1.pl != vc2.pl) {
329  				result = (vc1.pl < vc2.pl ? -1 : 1);
330  			}
331  		}
332  	}
333  
334  	/* Compare FreeBSD revision numbers. */
335  	if (result == 0 && r1 != r2)
336  		result = (r1 < r2 ? -1 : 1);
337  
338  	return (result);
339  }
340  
341  pkg_change_t
342  pkg_version_change_between(const struct pkg * pkg1, const struct pkg *pkg2)
343  {
344  	if (pkg2 == NULL)
345  		return PKG_REINSTALL;
346  
347  	switch (pkg_version_cmp(pkg2->version, pkg1->version)) {
348  	case -1:
349  		return (PKG_UPGRADE);
350  	default:		/* placate the compiler */
351  	case 0:
352  		return (PKG_REINSTALL);
353  	case 1:
354  		return (PKG_DOWNGRADE);
355  	}
356  }