Rev 9952 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
9837 | turbocat | 1 | #include |
2 | #include |
||
3 | #include |
||
4 | #include |
||
5 | #include |
||
6 | #include |
||
7 | #include |
||
8 | #include |
||
9 | #include |
||
10 | #include |
||
11 | #ifndef __MINGW32__ |
||
12 | #include |
||
13 | #include |
||
14 | #include |
||
15 | #include |
||
16 | #ifdef HAVE_GLOB_H |
||
17 | #include |
||
18 | #endif |
||
19 | #else |
||
20 | #include |
||
21 | #include |
||
22 | #endif |
||
23 | #ifdef WITH_LIBARCHIVE |
||
24 | #include |
||
25 | /* For backward compatibility. */ |
||
26 | #if ARCHIVE_VERSION_NUMBER < 3001000 |
||
27 | #define archive_read_free(...) \ |
||
28 | archive_read_finish(__VA_ARGS__) |
||
29 | #define archive_read_support_filter_all(...) \ |
||
30 | archive_read_support_compression_all(__VA_ARGS__) |
||
31 | #endif |
||
32 | #endif |
||
33 | #include "system.h" |
||
34 | |||
35 | #ifdef __MINGW32__ |
||
36 | #define mkdir(a, b) mkdir(a) |
||
37 | #if MAX_PATH < PATH_MAX |
||
38 | #error MAX_PATH < PATH_MAX. You should use MAX_PATH. |
||
39 | #endif |
||
40 | #endif |
||
41 | |||
42 | static const char *fopen_mode(unsigned int mode) |
||
43 | { |
||
44 | static const char *modes[4][2] = { |
||
45 | { "ab", "a" }, |
||
46 | { "w+b", "w+" }, |
||
47 | { "rb", "r" }, |
||
48 | { NULL, NULL } |
||
49 | }; |
||
50 | const char *(*cmode)[2] = &modes[0]; |
||
51 | |||
52 | if (!(mode & DGEN_APPEND)) { |
||
53 | ++cmode; |
||
54 | if (!(mode & DGEN_WRITE)) { |
||
55 | ++cmode; |
||
56 | if (!(mode & DGEN_READ)) |
||
57 | ++cmode; |
||
58 | } |
||
59 | } |
||
60 | return (*cmode)[(!!(mode & DGEN_TEXT))]; |
||
61 | } |
||
62 | |||
63 | enum path_type { |
||
64 | PATH_TYPE_UNSPECIFIED, |
||
65 | PATH_TYPE_RELATIVE, |
||
66 | PATH_TYPE_ABSOLUTE |
||
67 | }; |
||
68 | |||
69 | #ifdef __MINGW32__ |
||
70 | |||
71 | /** |
||
72 | * Check whether a path is absolute or relative. |
||
73 | * |
||
74 | * Examples: |
||
75 | * /foo/bar, \\foo\\bar, c:/foo/bar are absolute, |
||
76 | * ./foo/bar, ., .., are relative. |
||
77 | * |
||
78 | * @param[in] path Path to parse. |
||
79 | * @param len Length of path. |
||
80 | * @return Path type (PATH_TYPE_ABSOLUTE, PATH_TYPE_RELATIVE or |
||
81 | * PATH_TYPE_UNSPECIFIED). |
||
82 | */ |
||
83 | enum path_type path_type(const char *path, size_t len) |
||
84 | { |
||
85 | if ((len == 0) || (path[0] == '\0')) |
||
86 | return PATH_TYPE_UNSPECIFIED; |
||
87 | if ((path[0] == '\\') || (path[0] == '/')) |
||
88 | return PATH_TYPE_ABSOLUTE; |
||
89 | if ((path[0] == '.') && |
||
90 | (((len == 1) || |
||
91 | (path[1] == '\0') || (path[1] == '\\') || (path[1] == '/')) || |
||
92 | ((path[1] == '.') && |
||
93 | ((len == 2) || |
||
94 | (path[2] == '\0') || (path[2] == '\\') || (path[2] == '/'))))) |
||
95 | return PATH_TYPE_RELATIVE; |
||
96 | do { |
||
97 | if (*(++path) == ':') |
||
98 | return PATH_TYPE_ABSOLUTE; |
||
99 | --len; |
||
100 | } |
||
101 | while ((len) && (*path != '\0') && (*path != '\\') && (*path != '/')); |
||
102 | return PATH_TYPE_UNSPECIFIED; |
||
103 | } |
||
104 | |||
105 | #else /* __MINGW32__ */ |
||
106 | |||
107 | /** |
||
108 | * Check whether a path is absolute or relative. |
||
109 | * |
||
110 | * Examples: |
||
111 | * /foo/bar, \\foo\\bar are absolute, |
||
112 | * ./foo/bar, ., .., are relative. |
||
113 | * |
||
114 | * @param[in] path Path to parse. |
||
115 | * @param len Length of path. |
||
116 | * @return Path type (PATH_TYPE_ABSOLUTE, PATH_TYPE_RELATIVE or |
||
117 | * PATH_TYPE_UNSPECIFIED). |
||
118 | */ |
||
119 | enum path_type path_type(const char *path, size_t len) |
||
120 | { |
||
121 | if ((len == 0) || (path[0] == '\0')) |
||
122 | return PATH_TYPE_UNSPECIFIED; |
||
123 | if (path[0] == '/') |
||
124 | return PATH_TYPE_ABSOLUTE; |
||
125 | if ((path[0] == '.') && |
||
126 | (((len == 1) || (path[1] == '\0') || (path[1] == '/')) || |
||
127 | ((path[1] == '.') && |
||
128 | ((len == 2) || (path[2] == '\0') || (path[2] == '/'))))) |
||
129 | return PATH_TYPE_RELATIVE; |
||
130 | return PATH_TYPE_UNSPECIFIED; |
||
131 | } |
||
132 | |||
133 | #endif /* __MINGW32__ */ |
||
134 | |||
135 | /** |
||
136 | * Return user's home directory. |
||
137 | * The returned string doesn't have a trailing '/' and must be freed using |
||
138 | * free() (unless "buf" is provided). |
||
139 | * |
||
140 | * @param[in,out] buf Used to store path in. If NULL, memory is allocated. |
||
141 | * @param[in,out] size Size of "buf" when provided, then the returned path |
||
142 | * size. |
||
143 | * @return User's home directory (either as "buf" or a new buffer), |
||
144 | * NULL in case of error. |
||
145 | */ |
||
146 | char *dgen_userdir(char *buf, size_t *size) |
||
147 | { |
||
148 | char *path; |
||
149 | size_t sz_dir; |
||
150 | size_t sz; |
||
151 | #if !defined __MINGW32__ && !defined _KOLIBRI |
||
152 | struct passwd *pwd = getpwuid(geteuid()); |
||
153 | |||
154 | if ((pwd == NULL) || (pwd->pw_dir == NULL)) |
||
155 | return NULL; |
||
156 | sz_dir = strlen(pwd->pw_dir); |
||
9970 | turbocat | 157 | #elif defined _KOLIBRI |
158 | char *kos_home = getenv("HOME"); |
||
159 | if (!kos_home) |
||
160 | return NULL; |
||
161 | sz_dir = strlen(kos_home); |
||
9837 | turbocat | 162 | #endif |
163 | if (buf != NULL) { |
||
164 | sz = *size; |
||
165 | #if defined __MINGW32__ || defined _KOLIBRI |
||
166 | if (sz < PATH_MAX) |
||
167 | return NULL; |
||
168 | #else |
||
169 | if (sz < (sz_dir + 1)) |
||
170 | return NULL; |
||
171 | #endif |
||
172 | path = buf; |
||
173 | } |
||
174 | else { |
||
175 | #if defined __MINGW32__ || defined _KOLIBRI |
||
176 | sz = PATH_MAX; |
||
177 | #else |
||
178 | sz = (sz_dir + 1); |
||
179 | #endif |
||
180 | if ((path = malloc(sz)) == NULL) |
||
181 | return NULL; |
||
182 | } |
||
183 | #ifndef __MINGW32__ |
||
184 | #ifdef _KOLIBRI |
||
9970 | turbocat | 185 | strncpy(path, kos_home, sz_dir); |
9837 | turbocat | 186 | #else |
187 | strncpy(path, pwd->pw_dir, sz_dir); |
||
188 | #endif |
||
189 | #else |
||
190 | if (SHGetFolderPath(NULL, (CSIDL_PROFILE | CSIDL_FLAG_CREATE), |
||
191 | 0, 0, path) != S_OK) { |
||
192 | if (buf == NULL) |
||
193 | free(path); |
||
194 | return NULL; |
||
195 | } |
||
196 | sz_dir = strlen(path); |
||
197 | if (sz < (sz_dir + 1)) { |
||
198 | if (buf == NULL) |
||
199 | free(path); |
||
200 | return NULL; |
||
201 | } |
||
202 | #endif |
||
203 | path[sz_dir] = '\0'; |
||
204 | if (size != NULL) |
||
205 | *size = sz_dir; |
||
206 | return path; |
||
207 | } |
||
208 | |||
209 | /** |
||
210 | * Return DGen's home directory with an optional subdirectory (or file). |
||
211 | * The returned string doesn't have a trailing '/' and must be freed using |
||
212 | * free() (unless "buf" is provided). |
||
213 | * |
||
214 | * @param[in,out] buf Buffer to store result in. If NULL, memory is allocated. |
||
215 | * @param[in,out] size Size of "buf" when provided, then the returned path |
||
216 | * size. |
||
217 | * @param[in] sub NUL-terminated string to append to the path. |
||
218 | * @return DGen's home directory (either as "buf" or a new buffer), |
||
219 | * NULL in case of error. |
||
220 | */ |
||
221 | char *dgen_dir(char *buf, size_t *size, const char *sub) |
||
222 | { |
||
223 | char *path; |
||
224 | size_t sz_dir; |
||
225 | size_t sz_sub; |
||
226 | const size_t sz_bd = strlen(DGEN_BASEDIR); |
||
227 | size_t sz; |
||
228 | #ifndef __MINGW32__ |
||
229 | #ifndef _KOLIBRI |
||
230 | struct passwd *pwd = getpwuid(geteuid()); |
||
231 | if ((pwd == NULL) || (pwd->pw_dir == NULL)) |
||
232 | return NULL; |
||
233 | sz_dir = strlen(pwd->pw_dir); |
||
234 | #else |
||
9970 | turbocat | 235 | char *kos_home = getenv("HOME"); |
236 | if (!kos_home) |
||
237 | return NULL; |
||
238 | sz_dir = strlen(kos_home); |
||
9837 | turbocat | 239 | #endif |
240 | #endif |
||
241 | |||
242 | if (sub != NULL) |
||
243 | sz_sub = strlen(sub); |
||
244 | else |
||
245 | sz_sub = 0; |
||
246 | if (buf != NULL) { |
||
247 | sz = *size; |
||
248 | #if defined(__MINGW32__) || defined(_KOLIBRI) |
||
249 | if (sz < PATH_MAX) |
||
250 | return NULL; |
||
251 | #else |
||
252 | if (sz < (sz_dir + 1 + sz_bd + !!sz_sub + sz_sub + 1)) |
||
253 | return NULL; |
||
254 | #endif |
||
255 | path = buf; |
||
256 | } |
||
257 | else { |
||
258 | #if defined(__MINGW32__) || defined(_KOLIBRI) |
||
259 | sz = PATH_MAX; |
||
260 | #else |
||
261 | sz = (sz_dir + 1 + sz_bd + !!sz_sub + sz_sub + 1); |
||
262 | #endif |
||
263 | if ((path = malloc(sz)) == NULL) |
||
264 | return NULL; |
||
265 | } |
||
266 | #ifndef __MINGW32__ |
||
267 | #ifndef _KOLIBRI |
||
268 | strncpy(path, pwd->pw_dir, sz_dir); |
||
269 | #else |
||
9970 | turbocat | 270 | strncpy(path, kos_home, sz_dir); |
9837 | turbocat | 271 | #endif |
272 | #else |
||
273 | if (SHGetFolderPath(NULL, (CSIDL_APPDATA | CSIDL_FLAG_CREATE), |
||
274 | 0, 0, path) != S_OK) { |
||
275 | if (buf == NULL) |
||
276 | free(path); |
||
277 | return NULL; |
||
278 | } |
||
279 | sz_dir = strlen(path); |
||
280 | if (sz < (sz_dir + 1 + sz_bd + !!sz_sub + sz_sub + 1)) { |
||
281 | if (buf == NULL) |
||
282 | free(path); |
||
283 | return NULL; |
||
284 | } |
||
285 | #endif |
||
286 | path[(sz_dir++)] = DGEN_DIRSEP[0]; |
||
287 | memcpy(&path[sz_dir], DGEN_BASEDIR, sz_bd); |
||
288 | sz_dir += sz_bd; |
||
289 | if (sz_sub) { |
||
290 | path[(sz_dir++)] = DGEN_DIRSEP[0]; |
||
291 | memcpy(&path[sz_dir], sub, sz_sub); |
||
292 | sz_dir += sz_sub; |
||
293 | } |
||
294 | path[sz_dir] = '\0'; |
||
295 | if (size != NULL) |
||
296 | *size = sz_dir; |
||
297 | return path; |
||
298 | } |
||
299 | |||
300 | /** |
||
301 | * Open a file relative to DGen's home directory (when "relative" is NULL or |
||
302 | * path_type(relative) returns PATH_TYPE_UNSPECIFIED) and create the directory |
||
303 | * hierarchy if necessary, unless the file name is already relative to |
||
304 | * something or found in the current directory if mode contains DGEN_CURRENT. |
||
305 | * |
||
306 | * @param[in] relative Subdirectory to look in. |
||
307 | * @param[in] file File name to open. |
||
308 | * @param mode Mode flags to use (DGEN_READ, DGEN_WRITE and others). |
||
309 | * @return File pointer, or NULL in case of error. |
||
310 | * @see dgen_freopen() |
||
311 | * @see system.h |
||
312 | */ |
||
313 | FILE *dgen_fopen(const char *relative, const char *file, unsigned int mode) |
||
314 | { |
||
315 | return dgen_freopen(relative, file, mode, NULL); |
||
316 | } |
||
317 | |||
318 | /** |
||
319 | * @see dgen_fopen() |
||
320 | */ |
||
321 | FILE *dgen_freopen(const char *relative, const char *file, unsigned int mode, |
||
322 | FILE *f) |
||
323 | { |
||
324 | size_t size; |
||
325 | size_t file_size; |
||
326 | char *tmp; |
||
327 | int e = errno; |
||
328 | const char *fmode = fopen_mode(mode); |
||
329 | char *path = NULL; |
||
330 | |||
331 | if ((file == NULL) || (file[0] == '\0') || (fmode == NULL)) |
||
332 | goto error; |
||
333 | /* |
||
334 | Try to open the file in the current directory if DGEN_CURRENT |
||
335 | is specified. |
||
336 | */ |
||
337 | if (mode & DGEN_CURRENT) { |
||
338 | FILE *fd; |
||
339 | |||
340 | if (f == NULL) |
||
341 | fd = fopen(file, fmode); |
||
342 | else |
||
343 | fd = freopen(file, fmode, f); |
||
344 | if (fd != NULL) |
||
345 | return fd; |
||
346 | } |
||
347 | if (path_type(file, ~0u) != PATH_TYPE_UNSPECIFIED) |
||
348 | size = 0; |
||
349 | else if ((relative == NULL) || |
||
350 | (path_type(relative, ~0u) == PATH_TYPE_UNSPECIFIED)) { |
||
351 | if ((path = dgen_dir(NULL, &size, relative)) == NULL) |
||
352 | goto error; |
||
353 | } |
||
354 | else { |
||
355 | if ((path = strdup(relative)) == NULL) |
||
356 | goto error; |
||
357 | size = strlen(path); |
||
358 | } |
||
9970 | turbocat | 359 | |
9837 | turbocat | 360 | if ((mode & (DGEN_WRITE | DGEN_APPEND)) && (path != NULL)) |
361 | mkdir(path, 0777); /* XXX make that recursive */ |
||
9970 | turbocat | 362 | |
9837 | turbocat | 363 | file_size = strlen(file); |
364 | if ((tmp = realloc(path, (size + !!size + file_size + 1))) == NULL) |
||
365 | goto error; |
||
366 | path = tmp; |
||
367 | if (size) |
||
368 | path[(size++)] = DGEN_DIRSEP[0]; |
||
369 | memcpy(&path[size], file, file_size); |
||
370 | size += file_size; |
||
371 | path[size] = '\0'; |
||
372 | errno = e; |
||
373 | if (f == NULL) |
||
374 | f = fopen(path, fmode); |
||
375 | else |
||
376 | f = freopen(path, fmode, f); |
||
377 | e = errno; |
||
378 | free(path); |
||
379 | errno = e; |
||
380 | return f; |
||
381 | error: |
||
382 | free(path); |
||
383 | errno = EACCES; |
||
384 | return NULL; |
||
385 | } |
||
386 | |||
387 | /** |
||
388 | * Return the base name in path, like basename() but without allocating |
||
389 | * anything nor modifying the "path" argument. |
||
390 | * |
||
391 | * @param[in] path Path to extract the last component from. |
||
392 | * @return Last component from "path". |
||
393 | */ |
||
394 | const char *dgen_basename(const char *path) |
||
395 | { |
||
396 | char *tmp; |
||
397 | |||
398 | while ((tmp = strpbrk(path, DGEN_DIRSEP)) != NULL) |
||
399 | path = (tmp + 1); |
||
400 | return path; |
||
401 | } |
||
402 | |||
403 | #define CHUNK_SIZE BUFSIZ |
||
404 | |||
405 | struct chunk { |
||
406 | size_t size; |
||
407 | struct chunk *next; |
||
408 | struct chunk *prev; |
||
409 | uint8_t data[]; |
||
410 | }; |
||
411 | |||
412 | /** |
||
413 | * Unload pointer returned by load(). |
||
414 | * |
||
415 | * @param[in] data Pointer to unload. |
||
416 | */ |
||
417 | void unload(uint8_t *data) |
||
418 | { |
||
419 | struct chunk *chunk = ((struct chunk *)data - 1); |
||
420 | |||
421 | assert(chunk->next == chunk); |
||
422 | assert(chunk->prev == chunk); |
||
423 | free(chunk); |
||
424 | } |
||
425 | |||
426 | #ifdef HAVE_FTELLO |
||
427 | #define FTELL(f) ftello(f) |
||
428 | #define FSEEK(f, o, w) fseeko((f), (o), (w)) |
||
429 | #define FOFFT off_t |
||
430 | #else |
||
431 | #define FTELL(f) ftell(f) |
||
432 | #define FSEEK(f, o, w) fseek((f), (o), (w)) |
||
433 | #define FOFFT long |
||
434 | #endif |
||
435 | |||
436 | /** |
||
437 | * Call this when you're done with your file. |
||
438 | * |
||
439 | * @param[in,out] context Context returned by load(). |
||
440 | */ |
||
441 | void load_finish(void **context) |
||
442 | { |
||
443 | #ifdef WITH_LIBARCHIVE |
||
444 | struct archive *archive = *context; |
||
445 | |||
446 | if (archive != NULL) |
||
447 | archive_read_free(archive); |
||
448 | #endif |
||
449 | *context = NULL; |
||
450 | } |
||
451 | |||
452 | /** |
||
453 | * Return the remaining file size from the current file offset. |
||
454 | * |
||
455 | * @param[in] file File pointer. |
||
456 | */ |
||
457 | static size_t load_size(FILE *file) |
||
458 | { |
||
459 | FOFFT old = FTELL(file); |
||
460 | FOFFT pos; |
||
461 | size_t ret = 0; |
||
462 | |||
463 | if ((old == (FOFFT)-1) || |
||
464 | (FSEEK(file, 0, SEEK_END) == -1)) |
||
465 | return 0; |
||
466 | if (((pos = FTELL(file)) != (FOFFT)-1) && (pos >= old)) |
||
467 | ret = (size_t)(pos - old); |
||
468 | FSEEK(file, old, SEEK_SET); |
||
469 | return ret; |
||
470 | } |
||
471 | |||
472 | /** |
||
473 | * Allocate a buffer and stuff the file inside using transparent decompression |
||
474 | * if libarchive is available. If file_size is non-NULL, store the final size |
||
475 | * there. If max_size is nonzero, refuse to load anything larger. |
||
476 | * In case the returned value is NULL, errno should contain the error. |
||
477 | * |
||
478 | * If an error is returned but errno is 0, EOF has been reached. |
||
479 | * |
||
480 | * @param[in,out] context On first call of load() this should point to NULL. |
||
481 | * @param[out] file_size Final size. |
||
482 | * @param[in] file File pointer to load data from. |
||
483 | * @param max_size If nonzero, refuse to load anything larger. |
||
484 | * @return Buffer containing loaded data. |
||
485 | */ |
||
486 | uint8_t *load(void **context, |
||
487 | size_t *file_size, FILE *file, size_t max_size) |
||
488 | { |
||
489 | size_t pos; |
||
490 | size_t size = 0; |
||
491 | struct chunk *chunk; |
||
492 | struct chunk head = { 0, &head, &head }; |
||
493 | size_t chunk_size = load_size(file); |
||
494 | int error = 0; |
||
495 | #ifdef WITH_LIBARCHIVE |
||
496 | struct archive *archive = *context; |
||
497 | struct archive_entry *archive_entry; |
||
498 | |||
499 | if (archive != NULL) |
||
500 | goto init_ok; |
||
501 | archive = archive_read_new(); |
||
502 | *context = archive; |
||
503 | if (archive == NULL) { |
||
504 | error = ENOMEM; |
||
505 | goto error; |
||
506 | } |
||
507 | archive_read_support_filter_all(archive); |
||
508 | archive_read_support_format_all(archive); |
||
509 | archive_read_support_format_raw(archive); |
||
510 | if (archive_read_open_FILE(archive, file) != ARCHIVE_OK) { |
||
511 | error = EIO; |
||
512 | goto error; |
||
513 | } |
||
514 | init_ok: |
||
515 | switch (archive_read_next_header(archive, &archive_entry)) { |
||
516 | case ARCHIVE_OK: |
||
517 | break; |
||
518 | case ARCHIVE_EOF: |
||
519 | error = 0; |
||
520 | goto error; |
||
521 | default: |
||
522 | error = EIO; |
||
523 | goto error; |
||
524 | } |
||
525 | #else |
||
526 | *context = (void *)0xffff; |
||
527 | #endif |
||
528 | if (chunk_size == 0) |
||
529 | chunk_size = CHUNK_SIZE; |
||
530 | else if ((max_size != 0) && (chunk_size > max_size)) |
||
531 | chunk_size = max_size; |
||
532 | while (1) { |
||
533 | pos = 0; |
||
534 | chunk = malloc(sizeof(*chunk) + chunk_size); |
||
535 | if (chunk == NULL) { |
||
536 | error = errno; |
||
537 | goto error; |
||
538 | } |
||
539 | chunk->size = chunk_size; |
||
540 | chunk->next = &head; |
||
541 | chunk->prev = head.prev; |
||
542 | chunk->prev->next = chunk; |
||
543 | head.prev = chunk; |
||
544 | do { |
||
545 | size_t i; |
||
546 | #ifdef WITH_LIBARCHIVE |
||
547 | ssize_t j; |
||
548 | |||
549 | j = archive_read_data(archive, &chunk->data[pos], |
||
550 | (chunk->size - pos)); |
||
551 | /* |
||
552 | Don't bother with ARCHIVE_WARN and ARCHIVE_RETRY, |
||
553 | consider any negative value an error. |
||
554 | */ |
||
555 | if (j < 0) { |
||
556 | error = EIO; |
||
557 | goto error; |
||
558 | } |
||
559 | i = (size_t)j; |
||
560 | #else |
||
561 | i = fread(&chunk->data[pos], 1, (chunk->size - pos), |
||
562 | file); |
||
563 | #endif |
||
564 | if (i == 0) { |
||
565 | chunk->size = pos; |
||
566 | #ifndef WITH_LIBARCHIVE |
||
567 | if (ferror(file)) { |
||
568 | error = EIO; |
||
569 | goto error; |
||
570 | } |
||
571 | assert(feof(file)); |
||
572 | #endif |
||
573 | goto process; |
||
574 | } |
||
575 | pos += i; |
||
576 | size += i; |
||
577 | if ((max_size != 0) && (size > max_size)) { |
||
578 | error = EFBIG; |
||
579 | goto error; |
||
580 | } |
||
581 | } |
||
582 | while (pos != chunk->size); |
||
583 | chunk_size = CHUNK_SIZE; |
||
584 | } |
||
585 | process: |
||
586 | chunk = realloc(head.next, (sizeof(*chunk) + size)); |
||
587 | if (chunk == NULL) { |
||
588 | error = errno; |
||
589 | goto error; |
||
590 | } |
||
591 | chunk->next->prev = chunk; |
||
592 | head.next = chunk; |
||
593 | pos = chunk->size; |
||
594 | chunk->size = size; |
||
595 | chunk = chunk->next; |
||
596 | while (chunk != &head) { |
||
597 | struct chunk *next = chunk->next; |
||
598 | |||
599 | memcpy(&head.next->data[pos], chunk->data, chunk->size); |
||
600 | pos += chunk->size; |
||
601 | chunk->next->prev = chunk->prev; |
||
602 | chunk->prev->next = chunk->next; |
||
603 | free(chunk); |
||
604 | chunk = next; |
||
605 | } |
||
606 | chunk = head.next; |
||
607 | chunk->prev = chunk; |
||
608 | chunk->next = chunk; |
||
609 | if (file_size != NULL) |
||
610 | *file_size = chunk->size; |
||
611 | return chunk->data; |
||
612 | error: |
||
613 | #ifdef WITH_LIBARCHIVE |
||
614 | load_finish(context); |
||
615 | #endif |
||
616 | chunk = head.next; |
||
617 | while (chunk != &head) { |
||
618 | struct chunk *next = chunk->next; |
||
619 | |||
620 | free(chunk); |
||
621 | chunk = next; |
||
622 | } |
||
623 | errno = error; |
||
624 | return NULL; |
||
625 | } |
||
626 | |||
627 | /** |
||
628 | * Free NULL-terminated list of strings and set source pointer to NULL. |
||
629 | * This function can skip a given number of indices (starting from 0) |
||
630 | * which won't be freed. |
||
631 | * |
||
632 | * @param[in,out] pppc Pointer to an array of strings. |
||
633 | * @param skip Number of indices to skip in *pppc[]. |
||
634 | */ |
||
635 | static void free_pppc(char ***pppc, size_t skip) |
||
636 | { |
||
637 | char **p = *pppc; |
||
638 | size_t i; |
||
639 | |||
640 | if (p == NULL) |
||
641 | return; |
||
642 | *pppc = NULL; |
||
643 | for (i = 0; (p[i] != NULL); ++i) { |
||
644 | if (skip == 0) |
||
645 | free(p[i]); |
||
646 | else |
||
647 | --skip; |
||
648 | } |
||
649 | free(p); |
||
650 | } |
||
651 | |||
652 | /** |
||
653 | * Return a list of path names that match "len" characters of "path" on the |
||
654 | * file system, or NULL if none was found or if an error occured. |
||
655 | * |
||
656 | * @param[in] path Path name to match. |
||
657 | * @param len Number of characters in "path" to match. |
||
658 | * @return List of matching path names or NULL. |
||
659 | */ |
||
660 | static char **complete_path_simple(const char *path, size_t len) |
||
661 | { |
||
662 | size_t rlen; |
||
663 | const char *cpl; |
||
664 | char *root; |
||
665 | struct dirent *dent; |
||
666 | DIR *dir; |
||
667 | char **ret = NULL; |
||
668 | size_t ret_size = 256; |
||
669 | size_t ret_used = 0; |
||
670 | struct stat st; |
||
671 | |||
672 | if ((rlen = strlen(path)) < len) |
||
673 | len = rlen; |
||
674 | cpl = path; |
||
675 | while (((root = strpbrk(cpl, DGEN_DIRSEP)) != NULL) && |
||
676 | (root < (path + len))) |
||
677 | cpl = (root + 1); |
||
678 | rlen = (cpl - path); |
||
679 | len -= rlen; |
||
680 | if (rlen == 0) { |
||
681 | path = "." DGEN_DIRSEP; |
||
682 | rlen = 2; |
||
683 | } |
||
684 | if ((root = malloc(rlen + 1)) == NULL) |
||
685 | return NULL; |
||
686 | memcpy(root, path, rlen); |
||
687 | root[rlen] = '\0'; |
||
688 | if (((dir = opendir(root)) == NULL) || |
||
689 | ((ret = malloc(sizeof(*ret) * ret_size)) == NULL)) |
||
690 | goto error; |
||
691 | ret[(ret_used++)] = NULL; |
||
692 | while ((dent = readdir(dir)) != NULL) { |
||
693 | size_t i; |
||
694 | char *t; |
||
695 | |||
696 | if ((cpl[0] != '\0') && (strncmp(cpl, dent->d_name, len))) |
||
697 | continue; |
||
698 | /* Remove "." and ".." entries. */ |
||
699 | if ((dent->d_name[0] == '.') && |
||
700 | ((dent->d_name[1] == '\0') || |
||
701 | ((dent->d_name[1] == '.') && (dent->d_name[2] == '\0')))) |
||
702 | continue; |
||
703 | if (ret_used == ret_size) { |
||
704 | char **rt; |
||
705 | |||
706 | ret_size *= 2; |
||
707 | if ((rt = realloc(ret, |
||
708 | (sizeof(*rt) * ret_size))) == NULL) |
||
709 | break; |
||
710 | ret = rt; |
||
711 | } |
||
712 | i = strlen(dent->d_name); |
||
713 | /* Allocate one extra char in case it's a directory. */ |
||
714 | if ((t = malloc(rlen + i + 1 + 1)) == NULL) |
||
715 | break; |
||
716 | memcpy(t, root, rlen); |
||
717 | memcpy(&t[rlen], dent->d_name, i); |
||
718 | t[(rlen + i)] = '\0'; |
||
719 | if ((stat(t, &st) != -1) && (S_ISDIR(st.st_mode))) { |
||
720 | t[(rlen + (i++))] = DGEN_DIRSEP[0]; |
||
721 | t[(rlen + i)] = '\0'; |
||
722 | } |
||
723 | for (i = 0; (ret[i] != NULL); ++i) |
||
724 | if (strcmp(dent->d_name, &ret[i][rlen]) < 0) |
||
725 | break; |
||
726 | memmove(&ret[(i + 1)], &ret[i], |
||
727 | (sizeof(*ret) * (ret_used - i))); |
||
728 | ret[i] = t; |
||
729 | ++ret_used; |
||
730 | } |
||
731 | closedir(dir); |
||
732 | free(root); |
||
733 | if (ret[0] != NULL) |
||
734 | return ret; |
||
735 | free(ret); |
||
736 | return NULL; |
||
737 | error: |
||
738 | if (dir != NULL) |
||
739 | closedir(dir); |
||
740 | free(root); |
||
741 | if (ret != NULL) { |
||
742 | while (*ret != NULL) |
||
743 | free(*(ret++)); |
||
744 | free(ret); |
||
745 | } |
||
9970 | turbocat | 746 | |
9837 | turbocat | 747 | return NULL; |
748 | } |
||
749 | |||
9952 | turbocat | 750 | #if defined(HAVE_GLOB_H) && !defined(__MINGW32__) |
9837 | turbocat | 751 | |
752 | #define COMPLETE_USERDIR_TILDE 0x01 |
||
753 | #define COMPLETE_USERDIR_EXACT 0x02 |
||
754 | #define COMPLETE_USERDIR_ALL 0x04 |
||
755 | |||
756 | /** |
||
757 | * Return the list of home directories that match "len" characters of a |
||
758 | * user's name ("prefix"). |
||
759 | * COMPLETE_USERDIR_TILDE - Instead of directories, the returned strings are |
||
760 | * tilde-prefixed user names. |
||
761 | * COMPLETE_USERDIR_EXACT - Prefix must exactly match a user name. |
||
762 | * COMPLETE_USERDIR_ALL - When prefix length is 0, return all user names |
||
763 | * instead of the current user only. |
||
764 | * |
||
765 | * @param[in] prefix Path name to match. |
||
766 | * @param len Number of characters to match in "path". |
||
767 | * @return List of home directories that match "len" characters of "prefix". |
||
768 | */ |
||
769 | static char **complete_userdir(const char *prefix, size_t len, int flags) |
||
770 | { |
||
771 | char **ret = NULL; |
||
772 | char *s; |
||
773 | struct passwd *pwd; |
||
774 | size_t n; |
||
775 | size_t i; |
||
776 | int tilde = !!(flags & COMPLETE_USERDIR_TILDE); |
||
777 | int exact = !!(flags & COMPLETE_USERDIR_EXACT); |
||
778 | int all = !!(flags & COMPLETE_USERDIR_ALL); |
||
779 | |||
780 | setpwent(); |
||
781 | if ((!all) && (len == 0)) { |
||
782 | if (((pwd = getpwuid(geteuid())) == NULL) || |
||
783 | ((ret = calloc(2, sizeof(ret[0]))) == NULL)) |
||
784 | goto err; |
||
785 | if (tilde) |
||
786 | s = pwd->pw_name; |
||
787 | else |
||
788 | s = pwd->pw_dir; |
||
789 | i = strlen(s); |
||
790 | if ((ret[0] = calloc((tilde + i + 1), |
||
791 | sizeof(*ret[0]))) == NULL) |
||
792 | goto err; |
||
793 | if (tilde) |
||
794 | ret[0][0] = '~'; |
||
795 | memcpy(&ret[0][tilde], s, i); |
||
796 | ret[0][(tilde + i)] = '\0'; |
||
797 | goto end; |
||
798 | } |
||
799 | n = 64; |
||
800 | if ((ret = calloc(n, sizeof(ret[0]))) == NULL) |
||
801 | goto err; |
||
802 | i = 0; |
||
803 | while ((pwd = getpwent()) != NULL) { |
||
804 | size_t j; |
||
805 | |||
806 | if (exact) { |
||
807 | if (strncmp(pwd->pw_name, prefix, |
||
808 | strlen(pwd->pw_name))) |
||
809 | continue; |
||
810 | } |
||
811 | else if (strncmp(pwd->pw_name, prefix, len)) |
||
812 | continue; |
||
813 | if (i == (n - 1)) { |
||
814 | char **tmp; |
||
815 | |||
816 | n += 64; |
||
817 | if ((tmp = realloc(ret, (sizeof(ret[0]) * n))) == NULL) |
||
818 | goto end; |
||
819 | ret = tmp; |
||
820 | } |
||
821 | if (tilde) |
||
822 | s = pwd->pw_name; |
||
823 | else |
||
824 | s = pwd->pw_dir; |
||
825 | j = strlen(s); |
||
826 | if ((ret[i] = calloc((tilde + j + 1), |
||
827 | sizeof(*ret[0]))) == NULL) |
||
828 | break; |
||
829 | if (tilde) |
||
830 | ret[i][0] = '~'; |
||
831 | memcpy(&ret[i][tilde], s, j); |
||
832 | ret[i][(tilde + j)] = '\0'; |
||
833 | ++i; |
||
834 | } |
||
835 | if (i == 0) { |
||
836 | free(ret); |
||
837 | ret = NULL; |
||
838 | } |
||
839 | end: |
||
840 | endpwent(); |
||
841 | return ret; |
||
842 | err: |
||
843 | endpwent(); |
||
844 | free_pppc(&ret, 0); |
||
845 | return NULL; |
||
846 | } |
||
847 | |||
848 | /** |
||
849 | * Return a list of pathnames that match "len" characters of "prefix" on the |
||
850 | * file system, or NULL if none was found or if an error occured. This is done |
||
851 | * using glob() in order to handle wildcard characters in "prefix". |
||
852 | * |
||
853 | * When "prefix" isn't explicitly relative nor absolute, if "relative" is |
||
854 | * non-NULL, then the path will be completed as if "prefix" was a subdirectory |
||
855 | * of "relative". If "relative" is NULL, DGen's home directory will be used. |
||
856 | * |
||
857 | * If "relative" isn't explicitly relative nor absolute, it will be considered |
||
858 | * a subdirectory of DGen's home directory. |
||
859 | * |
||
860 | * @param[in] prefix Path name to match. |
||
861 | * @param len Number of characters to match in "path". |
||
862 | * @param[in] relative If non-NULL, consider path relative to this. |
||
863 | * @return List of path names that match "len" characters of "prefix". |
||
864 | */ |
||
865 | char **complete_path(const char *prefix, size_t len, const char *relative) |
||
866 | { |
||
867 | char *s; |
||
868 | char **ret; |
||
869 | size_t i; |
||
870 | glob_t g; |
||
871 | size_t strip; |
||
872 | |||
873 | (void)complete_path_simple; /* unused */ |
||
874 | if ((i = strlen(prefix)) < len) |
||
875 | len = i; |
||
876 | else |
||
877 | i = len; |
||
878 | if (((s = strchr(prefix, '/')) != NULL) && ((i = (s - prefix)) > len)) |
||
879 | i = len; |
||
880 | if ((len == 0) || |
||
881 | ((prefix[0] != '~') && |
||
882 | (strncmp(prefix, ".", i)) && |
||
883 | (strncmp(prefix, "..", i)))) { |
||
884 | size_t n; |
||
885 | |||
886 | if ((relative == NULL) || |
||
887 | (path_type(relative, ~0u) == PATH_TYPE_UNSPECIFIED)) { |
||
888 | char *x = dgen_dir(NULL, &n, relative); |
||
889 | |||
890 | if ((x == NULL) || |
||
891 | ((s = realloc(x, (n + 1 + len + 2))) == NULL)) { |
||
892 | free(x); |
||
893 | return NULL; |
||
894 | } |
||
895 | } |
||
896 | else { |
||
897 | n = strlen(relative); |
||
898 | if ((s = malloc(n + 1 + len + 2)) == NULL) |
||
899 | return NULL; |
||
900 | memcpy(s, relative, n); |
||
901 | } |
||
902 | s[(n++)] = '/'; |
||
903 | strip = n; |
||
904 | memcpy(&s[n], prefix, len); |
||
905 | len += n; |
||
906 | s[(len++)] = '*'; |
||
907 | s[len] = '\0'; |
||
908 | } |
||
909 | else if (prefix[0] == '~') { |
||
910 | char **ud; |
||
911 | size_t n; |
||
912 | |||
913 | if (s == NULL) |
||
914 | return complete_userdir(&prefix[1], (i - 1), |
||
915 | (COMPLETE_USERDIR_TILDE | |
||
916 | COMPLETE_USERDIR_ALL)); |
||
917 | ud = complete_userdir(&prefix[1], (i - 1), |
||
918 | COMPLETE_USERDIR_EXACT); |
||
919 | if (ud == NULL) |
||
920 | goto no_userdir; |
||
921 | n = strlen(ud[0]); |
||
922 | if ((s = realloc(ud[0], (n + (len - i) + 2))) == NULL) { |
||
923 | free_pppc(&ud, 0); |
||
924 | goto no_userdir; |
||
925 | } |
||
926 | free_pppc(&ud, 1); |
||
927 | len -= i; |
||
928 | strip = 0; |
||
929 | memcpy(&s[n], &prefix[i], len); |
||
930 | len += n; |
||
931 | s[(len++)] = '*'; |
||
932 | s[len] = '\0'; |
||
933 | } |
||
934 | else { |
||
935 | no_userdir: |
||
936 | if ((s = malloc(len + 2)) == NULL) |
||
937 | return NULL; |
||
938 | memcpy(s, prefix, len); |
||
939 | s[(len++)] = '*'; |
||
940 | s[len] = '\0'; |
||
941 | strip = 0; |
||
942 | } |
||
943 | switch (glob(s, (GLOB_MARK | GLOB_NOESCAPE), NULL, &g)) { |
||
944 | case 0: |
||
945 | break; |
||
946 | case GLOB_NOSPACE: |
||
947 | case GLOB_ABORTED: |
||
948 | case GLOB_NOMATCH: |
||
949 | default: |
||
950 | free(s); |
||
951 | return NULL; |
||
952 | } |
||
953 | free(s); |
||
954 | if ((ret = calloc((g.gl_pathc + 1), sizeof(ret[0]))) == NULL) |
||
955 | goto err; |
||
956 | for (i = 0; (g.gl_pathv[i] != NULL); ++i) { |
||
957 | size_t j; |
||
958 | |||
959 | len = strlen(g.gl_pathv[i]); |
||
960 | if (strip > len) |
||
961 | break; |
||
962 | j = (len - strip); |
||
963 | if ((ret[i] = calloc((j + 1), sizeof(ret[i][0]))) == NULL) |
||
964 | break; |
||
965 | memcpy(ret[i], &(g.gl_pathv[i][strip]), j); |
||
966 | ret[i][j] = '\0'; |
||
967 | } |
||
968 | if (i == 0) |
||
969 | goto err; |
||
970 | globfree(&g); |
||
971 | return ret; |
||
972 | err: |
||
973 | globfree(&g); |
||
974 | free_pppc(&ret, 0); |
||
975 | return NULL; |
||
976 | } |
||
977 | |||
978 | #else /* defined(HAVE_GLOB_H) && !defined(__MINGW32__) */ |
||
979 | |||
980 | /** |
||
981 | * Return a list of pathnames that match "len" characters of "prefix" on the |
||
982 | * file system, or NULL if none was found or if an error occured. |
||
983 | * |
||
984 | * When "prefix" isn't explicitly relative nor absolute, if "relative" is |
||
985 | * non-NULL, then the path will be completed as if "prefix" was a subdirectory |
||
986 | * of "relative". If "relative" is NULL, DGen's home directory will be used. |
||
987 | * |
||
988 | * If "relative" isn't explicitly relative nor absolute, it will be considered |
||
989 | * a subdirectory of DGen's home directory. |
||
990 | * |
||
991 | * @param[in] prefix Path name to match. |
||
992 | * @param len Number of characters to match in "path". |
||
993 | * @param[in] relative If non-NULL, consider path relative to this. |
||
994 | * @return List of path names that match "len" characters of "prefix". |
||
995 | */ |
||
996 | char **complete_path(const char *prefix, size_t len, const char *relative) |
||
997 | { |
||
998 | char *s; |
||
999 | char **ret; |
||
1000 | size_t i; |
||
1001 | size_t n; |
||
1002 | size_t strip; |
||
1003 | enum path_type pt; |
||
1004 | |||
1005 | if ((i = strlen(prefix)) < len) |
||
1006 | len = i; |
||
1007 | if (((pt = path_type(prefix, len)) == PATH_TYPE_ABSOLUTE) || |
||
1008 | (pt == PATH_TYPE_RELATIVE)) |
||
1009 | return complete_path_simple(prefix, len); |
||
1010 | if ((len != 0) && (prefix[0] == '~') && |
||
1011 | ((len == 1) || |
||
1012 | (prefix[1] == '\0') || |
||
1013 | (strpbrk(prefix, DGEN_DIRSEP) == &prefix[1]))) { |
||
1014 | char *x = dgen_userdir(NULL, &n); |
||
1015 | |||
1016 | if ((x == NULL) || |
||
1017 | ((s = realloc(x, (n + 1 + 2 + len + 1))) == NULL)) { |
||
1018 | free(x); |
||
1019 | return NULL; |
||
1020 | } |
||
1021 | ++prefix; |
||
1022 | --len; |
||
1023 | strip = 0; |
||
1024 | } |
||
1025 | else if ((relative == NULL) || |
||
1026 | (path_type(relative, ~0u) == PATH_TYPE_UNSPECIFIED)) { |
||
1027 | char *x = dgen_dir(NULL, &n, relative); |
||
1028 | |||
1029 | if ((x == NULL) || |
||
1030 | ((s = realloc(x, (n + 1 + len + 1))) == NULL)) { |
||
1031 | free(x); |
||
1032 | return NULL; |
||
1033 | } |
||
1034 | strip = (n + 1); |
||
1035 | } |
||
1036 | else { |
||
1037 | n = strlen(relative); |
||
1038 | if ((s = malloc(n + 1 + len + 1)) == NULL) |
||
1039 | return NULL; |
||
1040 | memcpy(s, relative, n); |
||
1041 | strip = (n + 1); |
||
1042 | } |
||
1043 | s[(n++)] = DGEN_DIRSEP[0]; |
||
1044 | memcpy(&s[n], prefix, len); |
||
1045 | len += n; |
||
1046 | s[len] = '\0'; |
||
1047 | ret = complete_path_simple(s, len); |
||
1048 | free(s); |
||
1049 | if (ret == NULL) |
||
1050 | return NULL; |
||
1051 | if (strip == 0) |
||
1052 | return ret; |
||
1053 | for (i = 0; (ret[i] != NULL); ++i) |
||
1054 | memmove(ret[i], &ret[i][strip], |
||
1055 | ((strlen(ret[i]) - strip) + 1)); |
||
1056 | return ret; |
||
1057 | } |
||
1058 | |||
1059 | #endif /* defined(HAVE_GLOB_H) && !defined(__MINGW32__) */ |
||
1060 | |||
1061 | /** |
||
1062 | * Free return value of complete*() functions. |
||
1063 | * |
||
1064 | * @param[in, out] cp Buffer to pass to free_pppc(). |
||
1065 | */ |
||
1066 | void complete_path_free(char **cp) |
||
1067 | { |
||
1068 | free_pppc(&cp, 0); |
||
1069 | } |
||
1070 | |||
1071 | /** |
||
1072 | * Create an escaped version of a string. |
||
1073 | * When not NULL, "pos" refers to an offset in string "src". It is updated |
||
1074 | * with its new offset value in the escaped string. |
||
1075 | * |
||
1076 | * @param[in] src String to escape. |
||
1077 | * @param size Number of characters from "src" to process. |
||
1078 | * @param flags BACKSLASHIFY_* flags. |
||
1079 | * @param[in, out] pos Offset in string "src" to update. |
||
1080 | * @return Escaped version of "src", NULL on error. |
||
1081 | */ |
||
1082 | char *backslashify(const uint8_t *src, size_t size, unsigned int flags, |
||
1083 | size_t *pos) |
||
1084 | { |
||
1085 | char *dst = NULL; |
||
1086 | char *tmp; |
||
1087 | size_t i; |
||
1088 | size_t j; |
||
1089 | char buf[5]; |
||
1090 | |||
1091 | again: |
||
1092 | for (i = 0, j = 0; (i < size); ++i) { |
||
1093 | switch (src[i]) { |
||
1094 | case '\a': |
||
1095 | tmp = "\\a"; |
||
1096 | break; |
||
1097 | case '\b': |
||
1098 | tmp = "\\b"; |
||
1099 | break; |
||
1100 | case '\f': |
||
1101 | tmp = "\\f"; |
||
1102 | break; |
||
1103 | case '\n': |
||
1104 | tmp = "\\n"; |
||
1105 | break; |
||
1106 | case '\r': |
||
1107 | tmp = "\\r"; |
||
1108 | break; |
||
1109 | case '\t': |
||
1110 | tmp = "\\t"; |
||
1111 | break; |
||
1112 | case '\v': |
||
1113 | tmp = "\\v"; |
||
1114 | break; |
||
1115 | case '\'': |
||
1116 | if (flags & BACKSLASHIFY_NOQUOTES) |
||
1117 | goto noquotes; |
||
1118 | tmp = "\\'"; |
||
1119 | break; |
||
1120 | case '"': |
||
1121 | if (flags & BACKSLASHIFY_NOQUOTES) |
||
1122 | goto noquotes; |
||
1123 | tmp = "\\\""; |
||
1124 | break; |
||
1125 | case ' ': |
||
1126 | if (flags & BACKSLASHIFY_NOQUOTES) |
||
1127 | tmp = " "; |
||
1128 | else |
||
1129 | tmp = "\\ "; |
||
1130 | break; |
||
1131 | case '\0': |
||
1132 | tmp = "\\0"; |
||
1133 | break; |
||
1134 | case '\\': |
||
1135 | if (flags & BACKSLASHIFY_NOQUOTES) |
||
1136 | goto noquotes; |
||
1137 | tmp = "\\\\"; |
||
1138 | break; |
||
1139 | default: |
||
1140 | noquotes: |
||
1141 | tmp = buf; |
||
1142 | if (isgraph(src[i])) { |
||
1143 | tmp[0] = src[i]; |
||
1144 | tmp[1] = '\0'; |
||
1145 | break; |
||
1146 | } |
||
1147 | tmp[0] = '\\'; |
||
1148 | tmp[1] = 'x'; |
||
1149 | snprintf(&tmp[2], 3, "%02x", src[i]); |
||
1150 | break; |
||
1151 | } |
||
1152 | if (dst != NULL) |
||
1153 | strncpy(&dst[j], tmp, strlen(tmp)); |
||
1154 | if ((pos != NULL) && (i == *pos)) { |
||
1155 | *pos = j; |
||
1156 | pos = NULL; |
||
1157 | } |
||
1158 | j += strlen(tmp); |
||
1159 | } |
||
1160 | if ((pos != NULL) && (i == *pos)) { |
||
1161 | *pos = j; |
||
1162 | pos = NULL; |
||
1163 | } |
||
1164 | if (dst == NULL) { |
||
1165 | dst = malloc(j + 1); |
||
1166 | if (dst == NULL) |
||
1167 | return NULL; |
||
1168 | dst[j] = '\0'; |
||
1169 | goto again; |
||
1170 | } |
||
1171 | return dst; |
||
1172 | } |
||
1173 | |||
1174 | /** |
||
1175 | * Convert a UTF-8 character to its 32 bit representation. |
||
1176 | * Return the number of valid bytes for this character. |
||
1177 | * On error, u32 is set to (uint32_t)-1. |
||
1178 | * |
||
1179 | * @param[out] u32 Converted character, (uint32_t)-1 on error. |
||
1180 | * @param[in] u8 Multibyte character to convert. |
||
1181 | * @return Number of bytes read. |
||
1182 | */ |
||
1183 | size_t utf8u32(uint32_t *u32, const uint8_t *u8) |
||
1184 | { |
||
1185 | static const uint8_t fb[] = { |
||
1186 | /* first byte: mask, expected value, size */ |
||
1187 | 0x80, 0x00, 1, |
||
1188 | 0xe0, 0xc0, 2, |
||
1189 | 0xf0, 0xe0, 3, |
||
1190 | 0xf8, 0xf0, 4, |
||
1191 | 0xfc, 0xf8, 5, |
||
1192 | 0xfe, 0xfc, 6, |
||
1193 | 0xff, 0x00, 0 |
||
1194 | }; |
||
1195 | const uint8_t *s = fb; |
||
1196 | size_t i = 0; |
||
1197 | size_t rem; |
||
1198 | uint32_t ret; |
||
1199 | |||
1200 | while ((*u8 & s[0]) != s[1]) |
||
1201 | s += 3; |
||
1202 | rem = s[2]; |
||
1203 | if (!rem) |
||
1204 | goto error; |
||
1205 | ret = (*u8 & ~s[0]); |
||
1206 | while (++i != rem) { |
||
1207 | ++u8; |
||
1208 | if ((*u8 & 0xc0) != 0x80) |
||
1209 | goto error; |
||
1210 | ret <<= 6; |
||
1211 | ret |= (*u8 & ~0xc0); |
||
1212 | } |
||
1213 | if (((ret & ~0x07ff) == 0xd800) || |
||
1214 | ((ret & ~0x0001) == 0xfffe)) |
||
1215 | goto error; |
||
1216 | *u32 = ret; |
||
1217 | return i; |
||
1218 | error: |
||
1219 | *u32 = (uint32_t)-1; |
||
1220 | return i; |
||
1221 | } |
||
1222 | |||
1223 | /** |
||
1224 | * The opposite of utf8u32(). |
||
1225 | * |
||
1226 | * @param[out] u8 Converted character. |
||
1227 | * @param u32 Character to convert. |
||
1228 | * @return Number of characters written to "u8", 0 on error. |
||
1229 | */ |
||
1230 | size_t utf32u8(uint8_t *u8, uint32_t u32) |
||
1231 | { |
||
1232 | size_t l; |
||
1233 | size_t i; |
||
1234 | uint8_t fb; |
||
1235 | uint32_t u; |
||
1236 | |||
1237 | if ((u32 & 0x80000000) || |
||
1238 | ((u32 & ~0x07ff) == 0xd800) || |
||
1239 | ((u32 & ~0x0001) == 0xfffe)) |
||
1240 | return 0; |
||
1241 | if (u32 < 0x80) { |
||
1242 | if (u8 != NULL) |
||
1243 | *u8 = u32; |
||
1244 | return 1; |
||
1245 | } |
||
1246 | for (l = 0, u = u32; (u & ~0x3c); ++l) |
||
1247 | u >>= 6; |
||
1248 | if (u8 == NULL) |
||
1249 | return l; |
||
1250 | for (i = l, fb = 0; (--i); u32 >>= 6, fb >>= 1, fb |= 0xc0) |
||
1251 | u8[i] = (0x80 | (u32 & 0x3f)); |
||
1252 | u8[0] = (fb | u32); |
||
1253 | return l; |
||
1254 | } |
||
1255 | |||
1256 | /** |
||
1257 | * Look for the longest common prefix between a string and an array |
||
1258 | * of strings while ignoring case. |
||
1259 | * |
||
1260 | * @param[in] str String to compare argv entries to. |
||
1261 | * @param[in] argv NULL-terminated array of prefixes to match. |
||
1262 | * @return Index in argv or -1 if nothing matches. |
||
1263 | */ |
||
1264 | int prefix_casematch(const char *str, const char *argv[]) |
||
1265 | { |
||
1266 | unsigned int i; |
||
1267 | size_t ret_len = 0; |
||
1268 | int ret = -1; |
||
1269 | |||
1270 | for (i = 0; (argv[i] != NULL); ++i) { |
||
1271 | size_t len = strlen(argv[i]); |
||
1272 | |||
1273 | if ((len < ret_len) || |
||
1274 | (strncasecmp(str, argv[i], len))) |
||
1275 | continue; |
||
1276 | ret_len = len; |
||
1277 | ret = i; |
||
1278 | } |
||
1279 | return ret; |
||
1280 | } |
||
1281 | |||
1282 | /** |
||
1283 | * Read number from initial portion of a string and convert it. |
||
1284 | * |
||
1285 | * @param[in] str String to read from. |
||
1286 | * @param[out] num If not NULL, stores the converted number. |
||
1287 | * @return Length of the number in str, 0 on error. |
||
1288 | */ |
||
1289 | size_t prefix_getuint(const char *str, unsigned int *num) |
||
1290 | { |
||
1291 | size_t len = 0; |
||
1292 | unsigned int ret = 0; |
||
1293 | |||
1294 | while (isdigit(str[len])) { |
||
1295 | ret *= 10; |
||
1296 | ret += (str[len] - '0'); |
||
1297 | ++len; |
||
1298 | } |
||
1299 | if (len == 0) |
||
1300 | return 0; |
||
1301 | if (num != NULL) |
||
1302 | *num = ret; |
||
1303 | return len; |
||
1304 | }>>=><=>>>>>>>>>>>>>>>>> |