chiark / gitweb /
cleanup: All the whitespace fixes, all at once.
[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 (;;) {
128 if (pa == va && pb == vb)
129 break;
130 else if (pa == va)
131 return (-1);
132 else if (pb == vb)
133 return (+1);
134 else if (*pa == *pb) {
135 pa++; pb++;
136 continue;
137 } else if (isalpha((unsigned char)*pa) == isalpha((unsigned char)*pb))
138 return (CMP(*pa, *pb));
139 else if (isalpha((unsigned char)*pa))
140 return (-1);
141 else
142 return (+1);
143 }
144
145 /* --- Compare digit portions --- */
146
147 ia = vint(&va, val); ib = vint(&vb, vbl);
148 if (ia != ib)
149 return (CMP(ia, ib));
150 }
151}
152
153static void vsplit(const char *v, struct vinfo *vi)
154{
155 const char *p;
156 size_t n;
157
158 if ((p = strchr(v, ':')) == 0)
159 vi->e = vi->el = 0;
160 else {
161 vi->e = v;
162 vi->el = p;
163 v = p + 1;
164 }
165
166 n = strlen(v);
167 if ((p = strrchr(v, '-')) == 0)
168 vi->s = vi->sl = 0;
d4efbcd9 169 else {
0683223a
MW
170 vi->s = p + 1;
171 vi->sl = v + n;
172 n = p - v;
173 }
174
175 vi->m = v;
176 vi->ml = v + n;
177}
178
179int versioncmp(const char *va, const char *vb)
180{
181 struct vinfo via, vib;
182 int rc;
183
184 vsplit(va, &via); vsplit(vb, &vib);
185 if ((rc = vcmp(via.e, via.el, vib.e, vib.el)) != 0 ||
186 (rc = vcmp(via.m, via.ml, vib.m, vib.ml)) != 0 ||
187 (rc = vcmp(via.s, via.sl, vib.s, vib.sl)) != 0)
188 return (rc);
189 return (0);
190}
191
192/*----- That's all, folks -------------------------------------------------*/