LCOV - code coverage report
Current view: top level - libpkg - pkg_version.c (source / functions) Hit Total Coverage
Test: rapport Lines: 102 134 76.1 %
Date: 2021-12-10 16:22:55 Functions: 4 5 80.0 %
Branches: 80 120 66.7 %

           Branch data     Line data    Source code
       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                 :       1948 : 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         [ +  - ]:       1948 :         if (pkgname == NULL) {
      56                 :          0 :                 pkg_emit_error("%s: Passed NULL pkgname.", __func__);
      57                 :          0 :                 return (NULL);
      58                 :            :         }
      59                 :            : 
      60                 :            :         /* Look for the last '-' the the pkgname */
      61                 :       1948 :         ch = strrchr(pkgname, '-');
      62                 :            :         /* Cheat if we are just passed a version, not a valid package name */
      63         [ -  + ]:       1948 :         versionstr = ch ? ch + 1 : pkgname;
      64                 :            : 
      65                 :            :         /*
      66                 :            :          * Look for the last '_' in the version string, advancing the
      67                 :            :          * end pointer.
      68                 :            :          */
      69                 :       1948 :         ch = strrchr(versionstr, '_');
      70                 :            : 
      71         [ -  + ]:       1948 :         if (revision != NULL)
      72         [ +  + ]:       1948 :                 *revision = ch ? strtoul(ch + 1, NULL, 10) : 0;
      73                 :            : 
      74                 :       1948 :         endversionstr = ch;
      75                 :            : 
      76                 :            :         /* Look for the last ',' in the remaining version string */
      77         [ +  + ]:       1948 :         ch = strrchr(endversionstr ? endversionstr + 1 : versionstr, ',');
      78                 :            : 
      79         [ -  + ]:       1948 :         if (epoch != NULL)
      80         [ +  + ]:       1948 :                 *epoch = ch ? strtoul(ch + 1, NULL, 10) : 0;
      81                 :            : 
      82   [ +  +  +  - ]:       1948 :         if (ch && !endversionstr)
      83                 :          4 :                 endversionstr = ch;
      84                 :            : 
      85                 :            :         /*
      86                 :            :          * set the pointer behind the last character of the version without
      87                 :            :          * revision or epoch
      88                 :            :          */
      89         [ -  + ]:       1948 :         if (endname)
      90         [ +  + ]:       1948 :                 *endname = endversionstr ? endversionstr :
      91                 :       1596 :                                            strrchr(versionstr, '\0');
      92                 :            : 
      93                 :       1948 :         return versionstr;
      94                 :       1948 : }
      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",  4,  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                 :       1438 : get_component(const char *position, version_component *component)
     163                 :            : {
     164                 :       1438 :         const char *pos = position;
     165                 :       1438 :         int hasstage = 0, haspatchlevel = 0;
     166                 :            : 
     167         [ +  - ]:       1438 :         if (!pos) {
     168                 :          0 :                 pkg_emit_error("%s: Passed NULL position.", __func__);
     169                 :          0 :                 return (NULL);
     170                 :            :         }
     171                 :            : 
     172                 :            :         /* handle version number */
     173         [ +  + ]:       1438 :         if (isdigit(*pos)) {
     174                 :            :                 char *endptr;
     175                 :            : #ifdef __LONG_LONG_SUPPORTED
     176                 :       1386 :                 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                 :       1386 :                 pos = endptr;
     182         [ -  + ]:       1438 :         } else if (*pos == '*') {
     183                 :          0 :                 component->n = -2;
     184                 :          0 :                 do {
     185                 :          0 :                         pos++;
     186   [ #  #  #  # ]:          0 :                 } while(*pos && *pos != '+');
     187                 :          0 :         } else {
     188                 :         52 :                 component->n = -1;
     189                 :         52 :                 hasstage = 1;
     190                 :            :         }
     191                 :            : 
     192                 :            :         /* handle letter */
     193         [ +  + ]:       1438 :         if (isalpha(*pos)) {
     194                 :         52 :                 int c = tolower(*pos);
     195                 :         52 :                 haspatchlevel = 1;
     196                 :            :                 /* handle special suffixes */
     197         [ -  + ]:         52 :                 if (isalpha(pos[1])) {
     198                 :            :                         unsigned int i;
     199         [ -  + ]:        180 :                         for (i = 0; i < nitems(stages); i++) {
     200                 :        180 :                                 const struct stage *stage = &stages[i];
     201                 :        180 :                                 size_t len = stage->namelen;
     202   [ +  +  -  + ]:        180 :                                 if (strncasecmp(pos, stage->name, len) == 0 &&
     203                 :         52 :                                     !isalpha(pos[stage->namelen])) {
     204         [ -  + ]:         52 :                                         if (hasstage) {
     205                 :            :                                                 /* stage to value */
     206                 :         52 :                                                 component->a = stage->value;
     207                 :         52 :                                                 pos += stage->namelen;
     208                 :         52 :                                         } else {
     209                 :            :                                                 /* insert dot */
     210                 :          0 :                                                 component->a = 0;
     211                 :          0 :                                                 haspatchlevel = 0;
     212                 :            :                                         }
     213                 :         52 :                                         c = 0;
     214                 :         52 :                                         break;
     215                 :            :                                 }
     216                 :        128 :                         }
     217                 :         52 :                 }
     218                 :            :                 /* unhandled above */
     219         [ +  - ]:         52 :                 if (c) {
     220                 :            :                         /* use the first letter and skip following */
     221                 :          0 :                         component->a = c - 'a' + ABASE;
     222                 :          0 :                         do {
     223                 :          0 :                                 ++pos;
     224         [ #  # ]:          0 :                         } while (isalpha(*pos));
     225                 :          0 :                 }
     226                 :         52 :         } else {
     227                 :       1386 :                 component->a = 0;
     228                 :       1386 :                 haspatchlevel = 0;
     229                 :            :         }
     230                 :            : 
     231         [ +  + ]:       1438 :         if (haspatchlevel) {
     232                 :            :                 /* handle patch number */
     233         [ +  - ]:         52 :                 if (isdigit(*pos)) {
     234                 :            :                         char *endptr;
     235                 :            : #ifdef __LONG_LONG_SUPPORTED
     236                 :         52 :                         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                 :         52 :                         pos = endptr;
     242                 :         52 :                 } else {
     243                 :          0 :                         component->pl = -1;
     244                 :            :                 }
     245                 :         52 :         } else {
     246                 :       1386 :                 component->pl = 0;
     247                 :            :         }
     248                 :            : 
     249                 :            :         /* skip trailing separators */
     250   [ +  +  +  +  :       2730 :         while (*pos && !isdigit(*pos) && !isalpha(*pos)
                   +  + ]
     251   [ +  +  -  + ]:        698 :             && *pos != '+' && *pos != '*')
     252                 :        646 :                 pos++;
     253                 :            : 
     254                 :       1438 :         return pos;
     255                 :       1438 : }
     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                 :        974 : 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                 :        974 :         int result = 0;
     285                 :            : 
     286                 :        974 :         v1 = split_version(pkg1, &ve1, &e1, &r1);
     287                 :        974 :         v2 = split_version(pkg2, &ve2, &e2, &r2);
     288                 :            : 
     289         [ +  - ]:        974 :         assert (v1 != NULL && v2 != NULL);
     290                 :            : 
     291                 :            :         /* Check epoch, port version, and port revision, in that order. */
     292         [ +  + ]:        974 :         if (e1 != e2)
     293                 :          4 :                 result = (e1 < e2 ? -1 : 1);
     294                 :            : 
     295                 :            :         /* Shortcut check for equality before invoking the parsing routines. */
     296   [ +  +  +  + ]:       1738 :         if (result == 0 &&
     297         [ +  + ]:        970 :             (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   [ +  +  +  +  :       2074 :                 while (result == 0 && (v1 < ve1 || v2 < ve2)) {
                   +  + ]
     303                 :        784 :                         int block_v1 = 0;
     304                 :        784 :                         int block_v2 = 0;
     305                 :        784 :                         version_component vc1 = {0, 0, 0};
     306                 :        784 :                         version_component vc2 = {0, 0, 0};
     307   [ +  +  -  + ]:        784 :                         if (v1 < ve1 && *v1 != '+') {
     308                 :        719 :                                 v1 = get_component(v1, &vc1);
     309         [ +  - ]:        719 :                                 assert (v1 != NULL);
     310                 :        719 :                         } else {
     311                 :         65 :                                 block_v1 = 1;
     312                 :            :                         }
     313   [ +  +  -  + ]:        784 :                         if (v2 < ve2 && *v2 != '+') {
     314                 :        719 :                                 v2 = get_component(v2, &vc2);
     315         [ +  - ]:        719 :                                 assert (v2 != NULL);
     316                 :        719 :                         } else {
     317                 :         65 :                                 block_v2 = 1;
     318                 :            :                         }
     319   [ +  +  +  - ]:        784 :                         if (block_v1 && block_v2) {
     320         [ #  # ]:          0 :                                 if (v1 < ve1)
     321                 :          0 :                                         v1++;
     322         [ #  # ]:          0 :                                 if (v2 < ve2)
     323                 :          0 :                                         v2++;
     324         [ +  + ]:        784 :                         } else if (vc1.n != vc2.n) {
     325                 :        482 :                                 result = (vc1.n < vc2.n ? -1 : 1);
     326         [ +  + ]:        784 :                         } else if (vc1.a != vc2.a) {
     327                 :         24 :                                 result = (vc1.a < vc2.a ? -1 : 1);
     328         [ +  - ]:        302 :                         } else if (vc1.pl != vc2.pl) {
     329                 :          0 :                                 result = (vc1.pl < vc2.pl ? -1 : 1);
     330                 :          0 :                         }
     331                 :            :                 }
     332                 :        506 :         }
     333                 :            : 
     334                 :            :         /* Compare FreeBSD revision numbers. */
     335   [ +  +  +  + ]:        974 :         if (result == 0 && r1 != r2)
     336                 :        140 :                 result = (r1 < r2 ? -1 : 1);
     337                 :            : 
     338                 :        974 :         return (result);
     339                 :            : }
     340                 :            : 
     341                 :            : pkg_change_t
     342                 :          0 : pkg_version_change(const struct pkg * restrict pkg)
     343                 :            : {
     344                 :            : 
     345         [ #  # ]:          0 :         if (pkg->old_version == NULL)
     346                 :          0 :                 return (PKG_REINSTALL);
     347                 :            : 
     348   [ #  #  #  # ]:          0 :         switch (pkg_version_cmp(pkg->old_version, pkg->version)) {
     349                 :            :         case -1:
     350                 :          0 :                 return (PKG_UPGRADE);
     351                 :            :         default:                /* placate the compiler */
     352                 :            :         case 0:
     353                 :          0 :                 return (PKG_REINSTALL);
     354                 :            :         case 1:
     355                 :          0 :                 return (PKG_DOWNGRADE);
     356                 :            :         }
     357                 :          0 : }
     358                 :            : 
     359                 :            : pkg_change_t
     360                 :        633 : pkg_version_change_between(const struct pkg * pkg1, const struct pkg *pkg2)
     361                 :            : {
     362         [ +  - ]:        633 :         if (pkg2 == NULL)
     363                 :          0 :                 return PKG_REINSTALL;
     364                 :            : 
     365   [ +  -  +  + ]:        633 :         switch (pkg_version_cmp(pkg2->version, pkg1->version)) {
     366                 :            :         case -1:
     367                 :        310 :                 return (PKG_UPGRADE);
     368                 :            :         default:                /* placate the compiler */
     369                 :            :         case 0:
     370                 :        236 :                 return (PKG_REINSTALL);
     371                 :            :         case 1:
     372                 :         87 :                 return (PKG_DOWNGRADE);
     373                 :            :         }
     374                 :        633 : }

Generated by: LCOV version 1.15