chiark / gitweb /
Release 2.0.6.
[mLib] / versioncmp.c
CommitLineData
0683223a
MW
1/* -*-c-*-
2 *
3 * $Id$
4 *
5 * Compare version numbers using the Debian algorithm
6 *
7 * (c) 2007 Straylight/Edgeware
8 */
9
d4efbcd9 10/*----- Licensing notice --------------------------------------------------*
0683223a
MW
11 *
12 * This file is part of the mLib utilities library.
13 *
14 * mLib is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
d4efbcd9 18 *
0683223a
MW
19 * mLib is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
d4efbcd9 23 *
0683223a
MW
24 * You should have received a copy of the GNU Library General Public
25 * License along with mLib; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27 * MA 02111-1307, USA.
28 */
29
30/*----- Header files ------------------------------------------------------*/
31
32#include <ctype.h>
33#include <string.h>
34
35#include "versioncmp.h"
36
37/*----- Main code ---------------------------------------------------------*/
38
39/* --- @versioncmp@ --- *
40 *
41 * Arguments: @const char *va, *vb@ = two version strings
42 *
43 * Returns: Less than, equal to, or greater than zero, according to
44 * whether @va@ is less than, equal to, or greater than @vb@.
45 *
46 * Use: Compares version number strings.
47 *
48 * The algorithm is an extension of the Debian version
49 * comparison algorithm. A version number consists of three
50 * components:
51 *
52 * [EPOCH :] MAIN [- SUB]
53 *
54 * The MAIN part may contain colons or hyphens if there is an
55 * EPOCH or SUB, respectively. Version strings are compared
56 * componentwise: first epochs, then main parts, and finally
57 * subparts.
58 *
59 * The component comparison is done as follows. First, the
60 * initial subsequence of nondigit characters is extracted from
61 * each string, and these are compared lexicographically, using
62 * ASCII ordering, except that letters precede non-letters. If
63 * both are the same, an initial sequence of digits is extracted
64 * from the remaining parts of the version strings, and these
65 * are compared numerically (an empty sequence being considered
66 * to have the value zero). This process is repeated until we
67 * have a winner or until both strings are exhausted.
68 */
69
70struct vinfo {
71 const char *e, *el;
72 const char *m, *ml;
73 const char *s, *sl;
74};
75
76static int vint(const char **vv, const char *vl)
77{
78 int n = 0;
79 const char *v = *vv;
80 int ch;
81
82 while (v < vl) {
83 ch = *v;
84 if (!isdigit((unsigned char)ch))
85 break;
86 v++;
87 n = n * 10 + (ch - '0');
88 }
89 *vv = v;
90 return (n);
91}
92
93static const char *vchr(const char **vv, const char *vl)
94{
95 const char *v = *vv;
96 const char *b = v;
97 int ch;
98
99 while (v < vl) {
100 ch = *v;
101 if (isdigit((unsigned char)ch))
102 break;
103 v++;
104 }
105 *vv = v;
106 return (b);
107}
108
109#define CMP(x, y) ((x) < (y) ? -1 : +1)
110
111static int vcmp(const char *va, const char *val,
112 const char *vb, const char *vbl)
113{
114 const char *pa, *pb;
115 int ia, ib;
116
117 for (;;) {
118
119 /* --- See if we're done --- */
120
121 if (va == val && vb == vbl)
122 return (0);
123
124 /* --- Compare nondigit portions --- */
125
126 pa = vchr(&va, val); pb = vchr(&vb, vbl);
127 for (;;) {
3048fcf9
MW
128 if (pa == va) ia = 1;
129 else if (isalpha((unsigned char)*pa)) ia = 2;
130 else if (*pa == '~') ia = 0;
131 else ia = 3;
132
133 if (pb == vb) ib = 1;
134 else if (isalpha((unsigned char)*pb)) ib = 2;
135 else if (*pb == '~') ib = 0;
136 else ib = 3;
137
138 if (ia != ib) return (CMP(ia, ib));
139 else if (pa == va && pb == vb) break;
140 else if (*pa != *pb) return (CMP(*pa, *pb));
141 pa++; pb++;
0683223a
MW
142 }
143
144 /* --- Compare digit portions --- */
145
146 ia = vint(&va, val); ib = vint(&vb, vbl);
3048fcf9 147 if (ia != ib) return (CMP(ia, ib));
0683223a
MW
148 }
149}
150
151static void vsplit(const char *v, struct vinfo *vi)
152{
153 const char *p;
154 size_t n;
155
156 if ((p = strchr(v, ':')) == 0)
157 vi->e = vi->el = 0;
158 else {
159 vi->e = v;
160 vi->el = p;
161 v = p + 1;
162 }
163
164 n = strlen(v);
165 if ((p = strrchr(v, '-')) == 0)
166 vi->s = vi->sl = 0;
d4efbcd9 167 else {
0683223a
MW
168 vi->s = p + 1;
169 vi->sl = v + n;
170 n = p - v;
171 }
172
173 vi->m = v;
174 vi->ml = v + n;
175}
176
177int versioncmp(const char *va, const char *vb)
178{
179 struct vinfo via, vib;
180 int rc;
181
182 vsplit(va, &via); vsplit(vb, &vib);
183 if ((rc = vcmp(via.e, via.el, vib.e, vib.el)) != 0 ||
184 (rc = vcmp(via.m, via.ml, vib.m, vib.ml)) != 0 ||
185 (rc = vcmp(via.s, via.sl, vib.s, vib.sl)) != 0)
186 return (rc);
187 return (0);
188}
189
190/*----- That's all, folks -------------------------------------------------*/