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