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