forked from wyday/wyupdate
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathVersionTools.cs
337 lines (275 loc) · 11.8 KB
/
VersionTools.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
using System;
using System.Collections;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
namespace wyUpdate.Common
{
public static class VersionTools
{
/// <summary>Hashtable of the greek letters for quick lookup.</summary>
static readonly Hashtable greek_ltrs = new Hashtable
{
{"alpha", 0}, {"beta", 1}, {"gamma", 2},
{"delta", 3}, {"epsilon", 4}, {"zeta", 5},
{"eta", 6}, {"theta", 7}, {"iota", 8},
{"kappa", 9}, {"lambda", 10}, {"mu", 11},
{"nu", 12}, {"xi", 13}, {"omicron", 14},
{"pi", 15}, {"rho", 16}, {"sigma", 17},
{"tau", 18}, {"upsilon", 19}, {"phi", 20},
{"chi", 21}, {"psi", 22}, {"omega", 23},
{"rc", 24} // RC = release candidate
};
/// <summary>Compares two versions and returns an integer that indicates their relationship in the sort order.</summary>
/// <param name="versionA">The first verison to compare.</param>
/// <param name="versionB">The second version to compare.</param>
/// <returns>Return a negative number if versionA is less than versionB, 0 if they're equal, a positive number if versionA is greater than versionB.</returns>
public static int Compare(string versionA, string versionB)
{
if (versionA == null) return -1;
if (versionB == null) return 1;
// Convert version to lowercase, and
// replace all instances of "release candidate" with "rc"
versionA = Regex.Replace(versionA.ToLowerInvariant(), @"release[\s]+candidate", "rc");
versionB = Regex.Replace(versionB.ToLowerInvariant(), @"release[\s]+candidate", "rc");
//compare indices
int iVerA = 0, iVerB = 0;
bool lastAWasLetter = true, lastBWasLetter = true;
for (;;)
{
//store index before GetNextObject just in case we need to rollback
int greekIndA = iVerA;
int greekIndB = iVerB;
string objA = GetNextObject(versionA, ref iVerA, ref lastAWasLetter);
string objB = GetNextObject(versionB, ref iVerB, ref lastBWasLetter);
//normalize versions so comparing integer against integer,
//(i.e. "1 a" is expanded to "1.0.0 a" when compared with "1.0.0 XXX")
//also, rollback the index on the version modified
if ((!lastBWasLetter && objB != null) && (objA == null || lastAWasLetter))
{
objA = "0";
iVerA = greekIndA;
}
else if ((!lastAWasLetter && objA != null) && (objB == null || lastBWasLetter))
{
objB = "0";
iVerB = greekIndB;
}
// find greek index for A and B
greekIndA = lastAWasLetter ? GetGreekIndex(objA) : -1;
greekIndB = lastBWasLetter ? GetGreekIndex(objB) : -1;
if (objA == null && objB == null)
return 0; //versions are equal
if (objA == null) // objB != null
{
//if versionB has a greek word, then A is greater
if (greekIndB != -1)
return 1;
return -1;
}
if (objB == null) // objA != null
{
//if versionA has a greek word, then B is greater
if (greekIndA != -1)
return -1;
return 1;
}
if (char.IsDigit(objA[0]) == char.IsDigit(objB[0]))
{
int strComp;
if (char.IsDigit(objA[0]))
{
//compare integers
strComp = IntCompare(objA, objB);
if (strComp != 0)
return strComp;
}
else
{
if (greekIndA == -1 && greekIndB == -1)
{
//compare non-greek strings
strComp = string.Compare(objA, objB, StringComparison.Ordinal);
if (strComp != 0)
return strComp;
}
else if (greekIndA == -1)
return 1; //versionB has a greek word, thus A is newer
else if (greekIndB == -1)
return -1; //versionA has a greek word, thus B is newer
else
{
//compare greek words
if (greekIndA > greekIndB)
return 1;
if (greekIndB > greekIndA)
return -1;
}
}
}
else if (char.IsDigit(objA[0]))
return 1; //versionA is newer than versionB
else
return -1; //verisonB is newer than versionA
}
}
static string GetNextObject(string version, ref int index, ref bool lastWasLetter)
{
//1 == string, 2 == int, -1 == neither
int StringOrInt = -1;
int startIndex = index;
while (version.Length != index)
{
if (StringOrInt == -1)
{
if (char.IsLetter(version[index]))
{
startIndex = index;
StringOrInt = 1;
}
else if (char.IsDigit(version[index]))
{
startIndex = index;
StringOrInt = 2;
}
else if (lastWasLetter && !char.IsWhiteSpace(version[index]))
{
index++;
lastWasLetter = false;
return "0";
}
}
else if (StringOrInt == 1 && !char.IsLetter(version[index]))
break;
else if (StringOrInt == 2 && !char.IsDigit(version[index]))
break;
index++;
}
// set the last "type" retrieved
lastWasLetter = StringOrInt == 1;
// return the retitrved sub-string
if (StringOrInt == 1 || StringOrInt == 2)
return version.Substring(startIndex, index - startIndex);
// was neither a string nor and int
return null;
}
/// <summary>Checks if the string chunk is a greek letter (e.g. "beta") and if so, return the relative index used for comparison.</summary>
/// <param name="str">The string chunk to check.</param>
/// <returns>Returns -1 if it's not a greek letter. Otherwise the relative index is returned.</returns>
static int GetGreekIndex(object str)
{
object val = greek_ltrs[str];
if (val == null)
return -1;
return (int)val;
}
/// <summary>Compare integers of "infinite" length without converting to an integer type.</summary>
/// <param name="a">The first integer to compare.</param>
/// <param name="b">The second integer to compare.</param>
/// <returns>Returns less than 0 if "a" is less than "b", zero if "a" == "b", greater than zero if "a" is greater than "b".</returns>
static int IntCompare(string a, string b)
{
int lastZero = -1;
// Clear any preceding zeros
for (int i = 0; i < a.Length; i++)
{
if (a[i] != '0')
break;
lastZero = i;
}
if (lastZero != -1)
a = a.Substring(lastZero + 1, a.Length - (lastZero + 1));
lastZero = -1;
for (int i = 0; i < b.Length; i++)
{
if (b[i] != '0')
break;
lastZero = i;
}
if (lastZero != -1)
b = b.Substring(lastZero + 1, b.Length - (lastZero + 1));
if (a.Length > b.Length)
return 1;
if (a.Length < b.Length)
return -1;
return string.Compare(a, b, StringComparison.Ordinal);
}
#if DESIGNER && !WBCMD
/// <summary>Increments the version number.</summary>
/// <param name="version">The version to increment.</param>
/// <returns>Returns the next logical verison (e.g. 1.0.2 -> 1.0.3, or 1.1 beta -> 1.1 beta 2)</returns>
public static string VerisonPlusPlus(string version)
{
int previ, i = 0;
string prevObj, obj = null;
bool junkBool = false;
do
{
previ = i;
prevObj = obj;
obj = GetNextObject(version, ref i, ref junkBool);
} while (obj != null);
if (prevObj != null)
{
// try to increment the final digit (e.g. 1.0.2 -> 1.0.3)
if (char.IsDigit(prevObj[0]))
return version.Substring(0, previ - prevObj.Length) + NumberPlusOne(prevObj);
// otherwise just tack on a 2 (e.g. 1.0 beta -> 1.0 beta 2)
return version + " 2";
}
return version;
}
static string NumberPlusOne(string number)
{
StringBuilder sb = new StringBuilder(number.Length + 2);
int i = number.Length - 1;
int tempInt = 1;
// process the number
for (; i >= 0; i--)
{
tempInt += number[i] - '0';
if (tempInt == 10)
{
sb.Insert(0, '0');
tempInt = 1;
}
else
{
sb.Insert(0, (char)(tempInt + '0'));
tempInt = 0;
break;
}
}
if (tempInt != 0)
// e.g. 99 + 1
sb.Insert(0, '1');
else if (i > 0)
// insert the higher digits that didn't need process
// e.g. 573 + 1 = 574, the leading '57' is copied over
sb.Insert(0, number.Substring(0, i));
return sb.ToString();
}
#endif
static string thisVersion;
/// <summary>Gets the file version of the currently executing assembly.</summary>
/// <returns>The version of the currently executing assembly.</returns>
public static string FromExecutingAssembly()
{
return thisVersion ??
(thisVersion =
FileVersionInfo.GetVersionInfo(SelfLocation).FileVersion);
}
// cache the location of the running executable
// this is used often and has a pretty heavy overhead.
static string selfLocation;
public static string SelfLocation
{
get
{
return selfLocation ??
(selfLocation =
System.Windows.Forms.Application.ExecutablePath.Replace('/', '\\'));
}
}
}
}