/
kxld_copyright.c
289 lines (234 loc) · 8.81 KB
/
kxld_copyright.c
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
#include <string.h>
#include <sys/types.h>
#include <AssertMacros.h>
#if !KERNEL
#include <stdio.h>
#include <stdlib.h>
#include "kxld.h"
#include "kxld_types.h"
#else
#include <libkern/libkern.h>
#include <libkern/kxld.h>
#include <libkern/kxld_types.h>
#endif /* KERNEL */
#include "kxld_util.h"
/******************************************************************************
* Macros
******************************************************************************/
#define kCopyrightToken "Copyright © "
#define kRightsToken " Apple Inc. All rights reserved."
/******************************************************************************
* Globals
******************************************************************************/
#if TEST
#include <CoreFoundation/CoreFoundation.h>
CFStringRef passes[] = {
CFSTR("Copyright © 2008 Apple Inc. All rights reserved."),
CFSTR("Copyright © 2004-2008 Apple Inc. All rights reserved."),
CFSTR("Copyright © 2004,2006 Apple Inc. All rights reserved."),
CFSTR("Copyright © 2004,2006-2008 Apple Inc. All rights reserved."),
CFSTR("Copyright © 2004 , 2006-2008 Apple Inc. All rights reserved."),
CFSTR("Copyright © 1998,2000-2002,2004,2006-2008 Apple Inc. All rights reserved."),
CFSTR("IOPCIFamily 2.1; Copyright © 2004,2006-2008 Apple Inc. All rights reserved."),
CFSTR("Copyright © 2004,2006-2008 Apple Inc. All rights reserved. The quick brown fox jumped over the lazy dog."),
CFSTR("IOPCIFamily 2.1; Copyright © 2004,2006-2008 Apple Inc. All rights reserved. The quick brown fox jumped over the lazy dog.")
};
CFStringRef fails[] = {
CFSTR("Copyright © 2007-08 Apple Inc. All rights reserved."),
CFSTR("Copyright (c) 2007 Apple Inc. All rights reserved."),
CFSTR("Copyright © 2007- Apple Inc. All rights reserved."),
CFSTR("Copyright © 2007 - 2008 Apple Inc. All rights reserved.")
};
extern char *createUTF8CStringForCFString(CFStringRef aString);
#endif /* TEST */
/******************************************************************************
* Prototypes
******************************************************************************/
static boolean_t is_space(const char c)
__attribute__((const));
static boolean_t is_token_delimiter(const char c)
__attribute__((const));
static boolean_t is_token_break(const char *str)
__attribute__((pure, nonnull));
static boolean_t token_is_year(const char *str)
__attribute__((pure, nonnull));
static boolean_t token_is_yearRange(const char *str)
__attribute__((pure, nonnull));
static boolean_t dates_are_valid(const char *str, const u_long len)
__attribute__((pure, nonnull));
/******************************************************************************
******************************************************************************/
static boolean_t
is_space(const char c)
{
switch (c) {
case ' ':
case '\t':
case '\n':
case '\v':
case '\f':
case '\r':
return TRUE;
}
return FALSE;
}
/******************************************************************************
******************************************************************************/
static boolean_t
is_token_delimiter(const char c)
{
return (is_space(c) || (',' == c) || ('\0' == c));
}
/******************************************************************************
* A token break is defined to be the boundary where the current character is
* not a token delimiter and the next character is a token delimiter.
******************************************************************************/
static boolean_t
is_token_break(const char *str)
{
/* This is safe because '\0' is a token delimiter, so the second check
* will not execute if we reach the end of the string.
*/
return (!is_token_delimiter(str[0]) && is_token_delimiter(str[1]));
}
/******************************************************************************
* A year is defined by the following regular expression:
* /[0-9]{4}$/
******************************************************************************/
#define kYearLen 5
static boolean_t
token_is_year(const char *str)
{
boolean_t result = FALSE;
u_int i = 0;
for (i = 0; i < kYearLen - 1; ++i) {
if (str[i] < '0' || str[i] > '9') goto finish;
}
if (str[i] != '\0') goto finish;
result = TRUE;
finish:
return result;
}
/******************************************************************************
* A year range is defined by the following regular expression:
* /[0-9]{4}[-][0-9]{4}$/
******************************************************************************/
#define kYearRangeLen 10
static boolean_t
token_is_yearRange(const char *str)
{
boolean_t result = FALSE;
u_int i = 0;
for (i = 0; i < kYearLen - 1; ++i) {
if (str[i] < '0' || str[i] > '9') goto finish;
}
if (str[i] != '-') goto finish;
for (i = kYearLen; i < kYearRangeLen - 1; ++i) {
if (str[i] < '0' || str[i] > '9') goto finish;
}
if (str[i] != '\0') goto finish;
result = TRUE;
finish:
return result;
}
/******************************************************************************
* The dates_are_valid function takes as input a comma-delimited list of years
* and year ranges, and returns TRUE if all years and year ranges are valid
* and well-formed.
******************************************************************************/
static boolean_t
dates_are_valid(const char *str, const u_long len)
{
boolean_t result = FALSE;
const char *token_ptr = NULL;
char token_buffer[kYearRangeLen];
u_int token_index = 0;
token_index = 0;
for (token_ptr = str; token_ptr < str + len; ++token_ptr) {
if (is_token_delimiter(*token_ptr) && !token_index) continue;
/* If we exceed the length of a year range, the test will not succeed,
* so just fail now. This limits the length of the token buffer that
* we have to keep around.
*/
if (token_index == kYearRangeLen) goto finish;
token_buffer[token_index++] = *token_ptr;
if (is_token_break(token_ptr)) {
if (!token_index) continue;
token_buffer[token_index++] = '\0';
if (!token_is_year(token_buffer) &&
!token_is_yearRange(token_buffer))
{
goto finish;
}
token_index = 0;
}
}
result = TRUE;
finish:
return result;
}
/******************************************************************************
* The copyright string is composed of three parts:
* 1) A copyright notice, "Copyright ©"
* 2) One or more years or year ranges, e.g., "2004,2006-2008"
* 3) A rights reserved notice, "Apple Inc. All Rights Reserved."
* We check the validity of the string by searching for both the copyright
* notice and the rights reserved notice. If both are found, we then check that
* the text between the two notices contains only valid years and year ranges.
******************************************************************************/
boolean_t
kxld_validate_copyright_string(const char *str)
{
boolean_t result = FALSE;
const char *copyright = NULL;
const char *rights = NULL;
char *date_str = NULL;
u_long len = 0;
copyright = kxld_strstr(str, kCopyrightToken);
rights = kxld_strstr(str, kRightsToken);
if (!copyright || !rights || copyright > rights) goto finish;
str = copyright + const_strlen(kCopyrightToken);
len = rights - str;
date_str = kxld_alloc(len);
if (!date_str) goto finish;
strncpy(date_str, str, len);
date_str[len] = '\0';
if (!dates_are_valid(date_str, len)) goto finish;
result = TRUE;
finish:
if (date_str) kxld_free(date_str, len);
return result;
}
#if TEST
/******************************************************************************
******************************************************************************/
int
main(int argc __unused, char *argv[] __unused)
{
int result = 1;
CFStringRef the_string = NULL;
const char *str = NULL;
u_int i = 0;
printf("The following %lu strings should pass\n",
const_array_len(passes));
for (i = 0; i < const_array_len(passes); ++i) {
the_string = passes[i];
str = createUTF8CStringForCFString(the_string);
if (!str) goto finish;
printf("%s: %s\n",
(kxld_validate_copyright_string(str)) ? "pass" : "fail", str);
}
printf("\nThe following %lu strings should fail\n",
const_array_len(fails));
for (i = 0; i < const_array_len(fails); ++i) {
the_string = fails[i];
str = createUTF8CStringForCFString(the_string);
if (!str) goto finish;
printf("%s: %s\n",
(kxld_validate_copyright_string(str)) ? "pass" : "fail", str);
}
result = 0;
finish:
return result;
}
#endif /* TEST */