Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/base/nsVersionComparator.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "nsVersionComparator.h"
8
9
#include <stdlib.h>
10
#include <string.h>
11
#include <stdint.h>
12
#if defined(XP_WIN) && !defined(UPDATER_NO_STRING_GLUE_STL)
13
#include <wchar.h>
14
#include "nsString.h"
15
#endif
16
17
struct VersionPart
18
{
19
  int32_t     numA;
20
21
  const char* strB;    // NOT null-terminated, can be a null pointer
22
  uint32_t    strBlen;
23
24
  int32_t     numC;
25
26
  char*       extraD;  // null-terminated
27
};
28
29
#ifdef XP_WIN
30
struct VersionPartW
31
{
32
  int32_t     numA;
33
34
  wchar_t*    strB;    // NOT null-terminated, can be a null pointer
35
  uint32_t    strBlen;
36
37
  int32_t     numC;
38
39
  wchar_t*    extraD;  // null-terminated
40
41
};
42
#endif
43
44
/**
45
 * Parse a version part into a number and "extra text".
46
 *
47
 * @returns A pointer to the next versionpart, or null if none.
48
 */
49
static char*
50
ParseVP(char* aPart, VersionPart& aResult)
51
300
{
52
300
  char* dot;
53
300
54
300
  aResult.numA = 0;
55
300
  aResult.strB = nullptr;
56
300
  aResult.strBlen = 0;
57
300
  aResult.numC = 0;
58
300
  aResult.extraD = nullptr;
59
300
60
300
  if (!aPart) {
61
0
    return aPart;
62
0
  }
63
300
64
300
  dot = strchr(aPart, '.');
65
300
  if (dot) {
66
288
    *dot = '\0';
67
288
  }
68
300
69
300
  if (aPart[0] == '*' && aPart[1] == '\0') {
70
0
    aResult.numA = INT32_MAX;
71
0
    aResult.strB = "";
72
300
  } else {
73
300
    aResult.numA = strtol(aPart, const_cast<char**>(&aResult.strB), 10);
74
300
  }
75
300
76
300
  if (!*aResult.strB) {
77
288
    aResult.strB = nullptr;
78
288
    aResult.strBlen = 0;
79
288
  } else {
80
12
    if (aResult.strB[0] == '+') {
81
0
      static const char kPre[] = "pre";
82
0
83
0
      ++aResult.numA;
84
0
      aResult.strB = kPre;
85
0
      aResult.strBlen = sizeof(kPre) - 1;
86
12
    } else {
87
12
      const char* numstart = strpbrk(aResult.strB, "0123456789+-");
88
12
      if (!numstart) {
89
0
        aResult.strBlen = strlen(aResult.strB);
90
12
      } else {
91
12
        aResult.strBlen = numstart - aResult.strB;
92
12
93
12
        aResult.numC = strtol(numstart, &aResult.extraD, 10);
94
12
        if (!*aResult.extraD) {
95
12
          aResult.extraD = nullptr;
96
12
        }
97
12
      }
98
12
    }
99
12
  }
100
300
101
300
  if (dot) {
102
288
    ++dot;
103
288
104
288
    if (!*dot) {
105
0
      dot = nullptr;
106
0
    }
107
288
  }
108
300
109
300
  return dot;
110
300
}
111
112
113
/**
114
 * Parse a version part into a number and "extra text".
115
 *
116
 * @returns A pointer to the next versionpart, or null if none.
117
 */
118
#ifdef XP_WIN
119
static wchar_t*
120
ParseVP(wchar_t* aPart, VersionPartW& aResult)
121
{
122
123
  wchar_t* dot;
124
125
  aResult.numA = 0;
126
  aResult.strB = nullptr;
127
  aResult.strBlen = 0;
128
  aResult.numC = 0;
129
  aResult.extraD = nullptr;
130
131
  if (!aPart) {
132
    return aPart;
133
  }
134
135
  dot = wcschr(aPart, '.');
136
  if (dot) {
137
    *dot = '\0';
138
  }
139
140
  if (aPart[0] == '*' && aPart[1] == '\0') {
141
    static wchar_t kEmpty[] = L"";
142
143
    aResult.numA = INT32_MAX;
144
    aResult.strB = kEmpty;
145
  } else {
146
    aResult.numA = wcstol(aPart, const_cast<wchar_t**>(&aResult.strB), 10);
147
  }
148
149
  if (!*aResult.strB) {
150
    aResult.strB = nullptr;
151
    aResult.strBlen = 0;
152
  } else {
153
    if (aResult.strB[0] == '+') {
154
      static wchar_t kPre[] = L"pre";
155
156
      ++aResult.numA;
157
      aResult.strB = kPre;
158
      aResult.strBlen = sizeof(kPre) - 1;
159
    } else {
160
      const wchar_t* numstart = wcspbrk(aResult.strB, L"0123456789+-");
161
      if (!numstart) {
162
        aResult.strBlen = wcslen(aResult.strB);
163
      } else {
164
        aResult.strBlen = numstart - aResult.strB;
165
166
        aResult.numC = wcstol(numstart, &aResult.extraD, 10);
167
        if (!*aResult.extraD) {
168
          aResult.extraD = nullptr;
169
        }
170
      }
171
    }
172
  }
173
174
  if (dot) {
175
    ++dot;
176
177
    if (!*dot) {
178
      dot = nullptr;
179
    }
180
  }
181
182
  return dot;
183
}
184
#endif
185
186
// compare two null-terminated strings, which may be null pointers
187
static int32_t
188
ns_strcmp(const char* aStr1, const char* aStr2)
189
12
{
190
12
  // any string is *before* no string
191
12
  if (!aStr1) {
192
12
    return aStr2 != 0;
193
12
  }
194
0
195
0
  if (!aStr2) {
196
0
    return -1;
197
0
  }
198
0
199
0
  return strcmp(aStr1, aStr2);
200
0
}
201
202
// compare two length-specified string, which may be null pointers
203
static int32_t
204
ns_strnncmp(const char* aStr1, uint32_t aLen1,
205
            const char* aStr2, uint32_t aLen2)
206
12
{
207
12
  // any string is *before* no string
208
12
  if (!aStr1) {
209
6
    return aStr2 != 0;
210
6
  }
211
6
212
6
  if (!aStr2) {
213
0
    return -1;
214
0
  }
215
6
216
12
  for (; aLen1 && aLen2; --aLen1, --aLen2, ++aStr1, ++aStr2) {
217
6
    if (*aStr1 < *aStr2) {
218
0
      return -1;
219
0
    }
220
6
221
6
    if (*aStr1 > *aStr2) {
222
0
      return 1;
223
0
    }
224
6
  }
225
6
226
6
  if (aLen1 == 0) {
227
6
    return aLen2 == 0 ? 0 : -1;
228
6
  }
229
0
230
0
  return 1;
231
0
}
232
233
// compare two int32_t
234
static int32_t
235
ns_cmp(int32_t aNum1, int32_t aNum2)
236
162
{
237
162
  if (aNum1 < aNum2) {
238
6
    return -1;
239
6
  }
240
156
241
156
  return aNum1 != aNum2;
242
156
}
243
244
/**
245
 * Compares two VersionParts
246
 */
247
static int32_t
248
CompareVP(VersionPart& aVer1, VersionPart& aVer2)
249
150
{
250
150
  int32_t r = ns_cmp(aVer1.numA, aVer2.numA);
251
150
  if (r) {
252
138
    return r;
253
138
  }
254
12
255
12
  r = ns_strnncmp(aVer1.strB, aVer1.strBlen, aVer2.strB, aVer2.strBlen);
256
12
  if (r) {
257
0
    return r;
258
0
  }
259
12
260
12
  r = ns_cmp(aVer1.numC, aVer2.numC);
261
12
  if (r) {
262
0
    return r;
263
0
  }
264
12
265
12
  return ns_strcmp(aVer1.extraD, aVer2.extraD);
266
12
}
267
268
/**
269
 * Compares two VersionParts
270
 */
271
#ifdef XP_WIN
272
static int32_t
273
CompareVP(VersionPartW& aVer1, VersionPartW& aVer2)
274
{
275
  int32_t r = ns_cmp(aVer1.numA, aVer2.numA);
276
  if (r) {
277
    return r;
278
  }
279
280
  r = wcsncmp(aVer1.strB, aVer2.strB, XPCOM_MIN(aVer1.strBlen, aVer2.strBlen));
281
  if (r) {
282
    return r;
283
  }
284
285
  r = ns_cmp(aVer1.numC, aVer2.numC);
286
  if (r) {
287
    return r;
288
  }
289
290
  if (!aVer1.extraD) {
291
    return aVer2.extraD != 0;
292
  }
293
294
  if (!aVer2.extraD) {
295
    return -1;
296
  }
297
298
  return wcscmp(aVer1.extraD, aVer2.extraD);
299
}
300
#endif
301
302
namespace mozilla {
303
304
#ifdef XP_WIN
305
int32_t
306
CompareVersions(const char16_t* aStrA, const char16_t* aStrB)
307
{
308
  wchar_t* A2 = wcsdup(char16ptr_t(aStrA));
309
  if (!A2) {
310
    return 1;
311
  }
312
313
  wchar_t* B2 = wcsdup(char16ptr_t(aStrB));
314
  if (!B2) {
315
    free(A2);
316
    return 1;
317
  }
318
319
  int32_t result;
320
  wchar_t* a = A2;
321
  wchar_t* b = B2;
322
323
  do {
324
    VersionPartW va, vb;
325
326
    a = ParseVP(a, va);
327
    b = ParseVP(b, vb);
328
329
    result = CompareVP(va, vb);
330
    if (result) {
331
      break;
332
    }
333
334
  } while (a || b);
335
336
  free(A2);
337
  free(B2);
338
339
  return result;
340
}
341
#endif
342
343
int32_t
344
CompareVersions(const char* aStrA, const char* aStrB)
345
144
{
346
144
  char* A2 = strdup(aStrA);
347
144
  if (!A2) {
348
0
    return 1;
349
0
  }
350
144
351
144
  char* B2 = strdup(aStrB);
352
144
  if (!B2) {
353
0
    free(A2);
354
0
    return 1;
355
0
  }
356
144
357
144
  int32_t result;
358
144
  char* a = A2;
359
144
  char* b = B2;
360
144
361
150
  do {
362
150
    VersionPart va, vb;
363
150
364
150
    a = ParseVP(a, va);
365
150
    b = ParseVP(b, vb);
366
150
367
150
    result = CompareVP(va, vb);
368
150
    if (result) {
369
138
      break;
370
138
    }
371
12
372
12
  } while (a || b);
373
144
374
144
  free(A2);
375
144
  free(B2);
376
144
377
144
  return result;
378
144
}
379
380
} // namespace mozilla
381