OpenCore  1.0.4
OpenCore Bootloader
Loading...
Searching...
No Matches
VersionCompare.c
Go to the documentation of this file.
1
8#include "LinuxBootInternal.h"
9
10#include <Uefi.h>
11#include <Library/BaseLib.h>
12#include <Library/DebugLib.h>
13
14#define IS_SECTION_BREAK(Ch) ((Ch) == '-' || (Ch) == '~' || (Ch) == '\0')
15
16// TODO: (?) Make this one OC library function, and
17// confirm nothing similar already in OC or EDK-II.
18STATIC
19INTN
21 CONST CHAR8 *FirstString,
22 CONST CHAR8 *FirstStringEnd,
23 CONST CHAR8 *SecondString,
24 CONST CHAR8 *SecondStringEnd
25 )
26{
27 ASSERT (FirstString != NULL);
28 ASSERT (SecondString != NULL);
29 ASSERT (FirstStringEnd >= FirstString);
30 ASSERT (SecondStringEnd >= SecondString);
31
32 while ((FirstString != FirstStringEnd) &&
33 (SecondString != SecondStringEnd) &&
34 (*FirstString == *SecondString))
35 {
36 FirstString++;
37 SecondString++;
38 }
39
40 if (FirstString == FirstStringEnd) {
41 if (SecondString == SecondStringEnd) {
42 return 0;
43 } else {
44 return -*SecondString;
45 }
46 }
47
48 if (SecondString == SecondStringEnd) {
49 return *FirstString;
50 }
51
52 return *FirstString - *SecondString;
53}
54
55STATIC
56VOID
58 IN CONST CHAR8 **Pos,
59 OUT CONST CHAR8 **FragmentStart,
60 OUT CONST CHAR8 **FragmentEnd,
61 OUT BOOLEAN *IsAlphaFragment,
62 OUT BOOLEAN *IsSectionBreak,
63 OUT CHAR8 *SectionChar
64 )
65{
66 CHAR8 Ch;
67
68 ASSERT (Pos != NULL);
69 ASSERT (*Pos != NULL);
70 ASSERT (FragmentStart != NULL);
71 ASSERT (FragmentEnd != NULL);
72 ASSERT (IsAlphaFragment != NULL);
73 ASSERT (IsSectionBreak != NULL);
74 ASSERT (SectionChar != NULL);
75
76 Ch = **Pos;
77 if (IS_SECTION_BREAK (Ch)) {
78 *IsSectionBreak = TRUE;
79 *SectionChar = Ch;
80 if (Ch != '\0') {
81 ++(*Pos);
82 }
83
84 return;
85 }
86
87 *IsSectionBreak = FALSE;
88 *FragmentStart = *Pos;
89
90 if (IS_ALPHA (Ch) || IS_DIGIT (Ch)) {
91 *IsAlphaFragment = IS_ALPHA (Ch);
92 do {
93 ++(*Pos);
94 Ch = **Pos;
95 } while (*IsAlphaFragment ? IS_ALPHA (Ch) : IS_DIGIT (Ch));
96 } else {
97 *IsAlphaFragment = TRUE;
98 }
99
100 *FragmentEnd = *Pos;
101 if (!(IS_ALPHA (Ch) || IS_DIGIT (Ch) || IS_SECTION_BREAK (Ch))) {
102 ++(*Pos);
103 }
104
105 return;
106}
107
108//
109// Return +1 if Version1 is higher than Version2.
110// Return -1 if Version2 is higher than Version1.
111// Return 0 if they sort equally.
112// Non-identical strings may sort as the same (e.g. v000 and v0).
113//
114// Useful info on how GRUB sorts versions at https://askubuntu.com/a/1277440/693497 .
115//
116// This is not a replication of that logic, but does something reasonable and
117// behaviourally similar (hopefully the same, for reasonable version strings).
118//
119// - Versions come in sections a.b.c.d... .
120// - If matched so far, having 'more' version fragments in the same section sorts higher.
121// - - & ~ are section breaks.
122// - Any non-alpha non-digit (not just '.') is a fragment separator.
123// - Changing from alpha to digit or vice-versa is also a fragment seprator.
124// - If matched up to a section break, having a ~ section next is lower than
125// anything else (means pre-release), and having a - section next is higher
126// (just means 'more' in the version).
127//
128STATIC
129INTN
131 IN CONST CHAR8 *Version1,
132 IN CONST CHAR8 *Version2
133 )
134{
135 EFI_STATUS Status;
136 CONST CHAR8 *Pos1;
137 CONST CHAR8 *Pos2;
138 CONST CHAR8 *FragmentStart1;
139 CONST CHAR8 *FragmentEnd1;
140 CONST CHAR8 *FragmentStart2;
141 CONST CHAR8 *FragmentEnd2;
142 UINTN VersionFragment1;
143 UINTN VersionFragment2;
144 BOOLEAN IsAlphaFragment1;
145 BOOLEAN IsAlphaFragment2;
146 BOOLEAN IsSectionBreak1;
147 BOOLEAN IsSectionBreak2;
148 CHAR8 SectionChar1;
149 CHAR8 SectionChar2;
150 BOOLEAN IsRescue1;
151 BOOLEAN IsRescue2;
152 INTN Compare;
153 CHAR8 ChSave;
154
155 ASSERT (Version1 != NULL);
156 ASSERT (Version2 != NULL);
157 if ((Version1 == NULL) || (Version2 == NULL)) {
158 return 0;
159 }
160
161 //
162 // Match some of what GRUB script does, c.f. askubuntu link above.
163 // We can't used EndsWith as we're not as clean at getting the version
164 // out without the machine-id tacked on afterwards.
165 //
166 IsRescue1 = AsciiStrStr (Version1, "rescue") != NULL;
167 IsRescue2 = AsciiStrStr (Version2, "rescue") != NULL;
168
169 if (IsRescue1 != IsRescue2) {
170 return IsRescue2 ? +1 : -1;
171 }
172
173 Pos1 = Version1;
174 Pos2 = Version2;
175
176 while (TRUE) {
177 GetNextFragment (&Pos1, &FragmentStart1, &FragmentEnd1, &IsAlphaFragment1, &IsSectionBreak1, &SectionChar1);
178 GetNextFragment (&Pos2, &FragmentStart2, &FragmentEnd2, &IsAlphaFragment2, &IsSectionBreak2, &SectionChar2);
179
180 if (IsSectionBreak1 != IsSectionBreak2) {
181 return IsSectionBreak2 ? +1 : -1;
182 } else if (IsSectionBreak1) {
183 if (SectionChar1 != SectionChar2) {
184 if ((SectionChar1 == '-') || (SectionChar2 == '~')) {
185 return +1;
186 }
187
188 if ((SectionChar1 == '~') || (SectionChar2 == '-')) {
189 return -1;
190 }
191
192 ASSERT (FALSE);
193 return 0;
194 }
195
196 if (SectionChar1 == '\0') {
197 return 0;
198 }
199 } else {
200 if ((IsAlphaFragment1 == IsAlphaFragment2) && !IsAlphaFragment1) {
201 ChSave = *FragmentEnd1;
202 *((CHAR8 *)FragmentEnd1) = '\0';
203 Status = AsciiStrDecimalToUintnS (FragmentStart1, NULL, &VersionFragment1);
204 *((CHAR8 *)FragmentEnd1) = ChSave;
205 ASSERT_EFI_ERROR (Status);
206
207 ChSave = *FragmentEnd2;
208 *((CHAR8 *)FragmentEnd2) = '\0';
209 Status = AsciiStrDecimalToUintnS (FragmentStart2, NULL, &VersionFragment2);
210 *((CHAR8 *)FragmentEnd2) = ChSave;
211 ASSERT_EFI_ERROR (Status);
212
213 Compare = VersionFragment1 - VersionFragment2;
214 } else {
215 Compare = BoundedAsciiStrCmp (FragmentStart1, FragmentEnd1, FragmentStart2, FragmentEnd2);
216 }
217
218 if (Compare != 0) {
219 return Compare;
220 }
221 }
222 }
223}
224
225INTN
226EFIAPI
228 IN CONST VOID *Version1,
229 IN CONST VOID *Version2
230 )
231{
232 return DoVersionCompare (*((CONST CHAR8 **)Version1), *((CONST CHAR8 **)Version2));
233}
234
235INTN
236EFIAPI
238 IN CONST VOID *Version1,
239 IN CONST VOID *Version2
240 )
241{
242 return -DoVersionCompare (*((CONST CHAR8 **)Version1), *((CONST CHAR8 **)Version2));
243}
#define IS_DIGIT(c)
#define IS_ALPHA(c)
INTN EFIAPI InternalVersionCompare(IN CONST VOID *Version1, IN CONST VOID *Version2)
STATIC INTN DoVersionCompare(IN CONST CHAR8 *Version1, IN CONST CHAR8 *Version2)
#define IS_SECTION_BREAK(Ch)
INTN EFIAPI InternalReverseVersionCompare(IN CONST VOID *Version1, IN CONST VOID *Version2)
STATIC VOID GetNextFragment(IN CONST CHAR8 **Pos, OUT CONST CHAR8 **FragmentStart, OUT CONST CHAR8 **FragmentEnd, OUT BOOLEAN *IsAlphaFragment, OUT BOOLEAN *IsSectionBreak, OUT CHAR8 *SectionChar)
STATIC INTN BoundedAsciiStrCmp(CONST CHAR8 *FirstString, CONST CHAR8 *FirstStringEnd, CONST CHAR8 *SecondString, CONST CHAR8 *SecondStringEnd)
#define ASSERT(x)
Definition coder.h:55
ush Pos
Definition deflate.h:92