1 /*-
2  * Copyright (c) 2009-2012,2014 Michihiro NAKAJIMA
3  * Copyright (c) 2003-2007 Tim Kientzle
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "archive_platform.h"
28 
29 #ifdef HAVE_SYS_TYPES_H
30 #include <sys/types.h>
31 #endif
32 #ifdef HAVE_ERRNO_H
33 #include <errno.h>
34 #endif
35 #ifdef HAVE_FCNTL_H
36 #include <fcntl.h>
37 #endif
38 #ifdef HAVE_STDLIB_H
39 #include <stdlib.h>
40 #endif
41 #ifdef HAVE_STRING_H
42 #include <string.h>
43 #endif
44 #if defined(_WIN32) && !defined(__CYGWIN__)
45 #if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
46 /* don't use bcrypt when XP needs to be supported */
47 #include <bcrypt.h>
48 
49 /* Common in other bcrypt implementations, but missing from VS2008. */
50 #ifndef BCRYPT_SUCCESS
51 #define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
52 #endif
53 
54 #elif defined(HAVE_WINCRYPT_H)
55 #include <wincrypt.h>
56 #endif
57 #endif
58 #ifdef HAVE_ZLIB_H
59 #include <zlib.h>
60 #endif
61 #ifdef HAVE_LZMA_H
62 #include <lzma.h>
63 #endif
64 #ifdef HAVE_BZLIB_H
65 #include <bzlib.h>
66 #endif
67 #ifdef HAVE_LZ4_H
68 #include <lz4.h>
69 #endif
70 
71 #include "archive.h"
72 #include "archive_private.h"
73 #include "archive_random_private.h"
74 #include "archive_string.h"
75 
76 #ifndef O_CLOEXEC
77 #define O_CLOEXEC   0
78 #endif
79 
80 static int archive_utility_string_sort_helper(char **, unsigned int);
81 
82 /* Generic initialization of 'struct archive' objects. */
83 int
__archive_clean(struct archive * a)84 __archive_clean(struct archive *a)
85 {
86           archive_string_conversion_free(a);
87           return (ARCHIVE_OK);
88 }
89 
90 int
archive_version_number(void)91 archive_version_number(void)
92 {
93           return (ARCHIVE_VERSION_NUMBER);
94 }
95 
96 const char *
archive_version_string(void)97 archive_version_string(void)
98 {
99           return (ARCHIVE_VERSION_STRING);
100 }
101 
102 int
archive_errno(struct archive * a)103 archive_errno(struct archive *a)
104 {
105           return (a->archive_error_number);
106 }
107 
108 const char *
archive_error_string(struct archive * a)109 archive_error_string(struct archive *a)
110 {
111 
112           if (a->error != NULL  &&  *a->error != '\0')
113                     return (a->error);
114           else
115                     return (NULL);
116 }
117 
118 int
archive_file_count(struct archive * a)119 archive_file_count(struct archive *a)
120 {
121           return (a->file_count);
122 }
123 
124 int
archive_format(struct archive * a)125 archive_format(struct archive *a)
126 {
127           return (a->archive_format);
128 }
129 
130 const char *
archive_format_name(struct archive * a)131 archive_format_name(struct archive *a)
132 {
133           return (a->archive_format_name);
134 }
135 
136 
137 int
archive_compression(struct archive * a)138 archive_compression(struct archive *a)
139 {
140           return archive_filter_code(a, 0);
141 }
142 
143 const char *
archive_compression_name(struct archive * a)144 archive_compression_name(struct archive *a)
145 {
146           return archive_filter_name(a, 0);
147 }
148 
149 
150 /*
151  * Return a count of the number of compressed bytes processed.
152  */
153 la_int64_t
archive_position_compressed(struct archive * a)154 archive_position_compressed(struct archive *a)
155 {
156           return archive_filter_bytes(a, -1);
157 }
158 
159 /*
160  * Return a count of the number of uncompressed bytes processed.
161  */
162 la_int64_t
archive_position_uncompressed(struct archive * a)163 archive_position_uncompressed(struct archive *a)
164 {
165           return archive_filter_bytes(a, 0);
166 }
167 
168 void
archive_clear_error(struct archive * a)169 archive_clear_error(struct archive *a)
170 {
171           archive_string_empty(&a->error_string);
172           a->error = NULL;
173           a->archive_error_number = 0;
174 }
175 
176 void
archive_set_error(struct archive * a,int error_number,const char * fmt,...)177 archive_set_error(struct archive *a, int error_number, const char *fmt, ...)
178 {
179           va_list ap;
180 
181           a->archive_error_number = error_number;
182           if (fmt == NULL) {
183                     a->error = NULL;
184                     return;
185           }
186 
187           archive_string_empty(&(a->error_string));
188           va_start(ap, fmt);
189           archive_string_vsprintf(&(a->error_string), fmt, ap);
190           va_end(ap);
191           a->error = a->error_string.s;
192 }
193 
194 void
archive_copy_error(struct archive * dest,struct archive * src)195 archive_copy_error(struct archive *dest, struct archive *src)
196 {
197           dest->archive_error_number = src->archive_error_number;
198 
199           archive_string_copy(&dest->error_string, &src->error_string);
200           dest->error = dest->error_string.s;
201 }
202 
203 void
__archive_errx(int retvalue,const char * msg)204 __archive_errx(int retvalue, const char *msg)
205 {
206           static const char msg1[] = "Fatal Internal Error in libarchive: ";
207           size_t s;
208 
209           s = write(2, msg1, strlen(msg1));
210           (void)s; /* UNUSED */
211           s = write(2, msg, strlen(msg));
212           (void)s; /* UNUSED */
213           s = write(2, "\n", 1);
214           (void)s; /* UNUSED */
215           exit(retvalue);
216 }
217 
218 /*
219  * Create a temporary file
220  */
221 #if defined(_WIN32) && !defined(__CYGWIN__)
222 
223 /*
224  * Do not use Windows tmpfile() function.
225  * It will make a temporary file under the root directory
226  * and it'll cause permission error if a user who is
227  * non-Administrator creates temporary files.
228  * Also Windows version of mktemp family including _mktemp_s
229  * are not secure.
230  */
231 static int
__archive_mktempx(const char * tmpdir,wchar_t * template)232 __archive_mktempx(const char *tmpdir, wchar_t *template)
233 {
234           static const wchar_t prefix[] = L"libarchive_";
235           static const wchar_t suffix[] = L"XXXXXXXXXX";
236           static const wchar_t num[] = {
237                     L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7',
238                     L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F',
239                     L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N',
240                     L'O', L'P', L'Q', L'R', L'S', L'T', L'U', L'V',
241                     L'W', L'X', L'Y', L'Z', L'a', L'b', L'c', L'd',
242                     L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l',
243                     L'm', L'n', L'o', L'p', L'q', L'r', L's', L't',
244                     L'u', L'v', L'w', L'x', L'y', L'z'
245           };
246           struct archive_wstring temp_name;
247           wchar_t *ws;
248           DWORD attr;
249           wchar_t *xp, *ep;
250           int fd;
251 #if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
252           BCRYPT_ALG_HANDLE hAlg = NULL;
253 #else
254           HCRYPTPROV hProv = (HCRYPTPROV)NULL;
255 #endif
256           fd = -1;
257           ws = NULL;
258           archive_string_init(&temp_name);
259 
260           if (template == NULL) {
261                     /* Get a temporary directory. */
262                     if (tmpdir == NULL) {
263                               size_t l;
264                               wchar_t *tmp;
265 
266                               l = GetTempPathW(0, NULL);
267                               if (l == 0) {
268                                         la_dosmaperr(GetLastError());
269                                         goto exit_tmpfile;
270                               }
271                               tmp = malloc(l*sizeof(wchar_t));
272                               if (tmp == NULL) {
273                                         errno = ENOMEM;
274                                         goto exit_tmpfile;
275                               }
276                               GetTempPathW((DWORD)l, tmp);
277                               archive_wstrcpy(&temp_name, tmp);
278                               free(tmp);
279                     } else {
280                               if (archive_wstring_append_from_mbs(&temp_name, tmpdir,
281                                   strlen(tmpdir)) < 0)
282                                         goto exit_tmpfile;
283                               if (temp_name.length == 0 ||
284                                   temp_name.s[temp_name.length-1] != L'/')
285                                         archive_wstrappend_wchar(&temp_name, L'/');
286                     }
287 
288                     /* Check if temp_name is a directory. */
289                     attr = GetFileAttributesW(temp_name.s);
290                     if (attr == (DWORD)-1) {
291                               if (GetLastError() != ERROR_FILE_NOT_FOUND) {
292                                         la_dosmaperr(GetLastError());
293                                         goto exit_tmpfile;
294                               }
295                               ws = __la_win_permissive_name_w(temp_name.s);
296                               if (ws == NULL) {
297                                         errno = EINVAL;
298                                         goto exit_tmpfile;
299                               }
300                               attr = GetFileAttributesW(ws);
301                               if (attr == (DWORD)-1) {
302                                         la_dosmaperr(GetLastError());
303                                         goto exit_tmpfile;
304                               }
305                     }
306                     if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
307                               errno = ENOTDIR;
308                               goto exit_tmpfile;
309                     }
310 
311                     /*
312                      * Create a temporary file.
313                      */
314                     archive_wstrcat(&temp_name, prefix);
315                     archive_wstrcat(&temp_name, suffix);
316                     ep = temp_name.s + archive_strlen(&temp_name);
317                     xp = ep - wcslen(suffix);
318                     template = temp_name.s;
319           } else {
320                     xp = wcschr(template, L'X');
321                     if (xp == NULL)     /* No X, programming error */
322                               abort();
323                     for (ep = xp; *ep == L'X'; ep++)
324                               continue;
325                     if (*ep)  /* X followed by non X, programming error */
326                               abort();
327           }
328 
329 #if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
330           if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM,
331                     NULL, 0))) {
332                     la_dosmaperr(GetLastError());
333                     goto exit_tmpfile;
334           }
335 #else
336           if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
337                     CRYPT_VERIFYCONTEXT)) {
338                     la_dosmaperr(GetLastError());
339                     goto exit_tmpfile;
340           }
341 #endif
342 
343           for (;;) {
344                     wchar_t *p;
345                     HANDLE h;
346 # if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
347                     CREATEFILE2_EXTENDED_PARAMETERS createExParams;
348 #endif
349 
350                     /* Generate a random file name through CryptGenRandom(). */
351                     p = xp;
352 #if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
353                     if (!BCRYPT_SUCCESS(BCryptGenRandom(hAlg, (PUCHAR)p,
354                         (DWORD)(ep - p)*sizeof(wchar_t), 0))) {
355                               la_dosmaperr(GetLastError());
356                               goto exit_tmpfile;
357                     }
358 #else
359                     if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t),
360                         (BYTE*)p)) {
361                               la_dosmaperr(GetLastError());
362                               goto exit_tmpfile;
363                     }
364 #endif
365                     for (; p < ep; p++)
366                               *p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];
367 
368                     free(ws);
369                     ws = __la_win_permissive_name_w(template);
370                     if (ws == NULL) {
371                               errno = EINVAL;
372                               goto exit_tmpfile;
373                     }
374                     if (template == temp_name.s) {
375                               attr = FILE_ATTRIBUTE_TEMPORARY |
376                                      FILE_FLAG_DELETE_ON_CLOSE;
377                     } else {
378                               /* mkstemp */
379                               attr = FILE_ATTRIBUTE_NORMAL;
380                     }
381 # if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
382                     ZeroMemory(&createExParams, sizeof(createExParams));
383                     createExParams.dwSize = sizeof(createExParams);
384                     createExParams.dwFileAttributes = attr & 0xFFFF;
385                     createExParams.dwFileFlags = attr & 0xFFF00000;
386                     h = CreateFile2(ws,
387                         GENERIC_READ | GENERIC_WRITE | DELETE,
388                         0,/* Not share */
389                               CREATE_NEW,
390                               &createExParams);
391 #else
392                     h = CreateFileW(ws,
393                         GENERIC_READ | GENERIC_WRITE | DELETE,
394                         0,/* Not share */
395                         NULL,
396                         CREATE_NEW,/* Create a new file only */
397                         attr,
398                         NULL);
399 #endif
400                     if (h == INVALID_HANDLE_VALUE) {
401                               /* The same file already exists. retry with
402                                * a new filename. */
403                               if (GetLastError() == ERROR_FILE_EXISTS)
404                                         continue;
405                               /* Otherwise, fail creation temporary file. */
406                               la_dosmaperr(GetLastError());
407                               goto exit_tmpfile;
408                     }
409                     fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR);
410                     if (fd == -1) {
411                               la_dosmaperr(GetLastError());
412                               CloseHandle(h);
413                               goto exit_tmpfile;
414                     } else
415                               break;/* success! */
416           }
417 exit_tmpfile:
418 #if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
419           if (hAlg != NULL)
420                     BCryptCloseAlgorithmProvider(hAlg, 0);
421 #else
422           if (hProv != (HCRYPTPROV)NULL)
423                     CryptReleaseContext(hProv, 0);
424 #endif
425           free(ws);
426           if (template == temp_name.s)
427                     archive_wstring_free(&temp_name);
428           return (fd);
429 }
430 
431 int
__archive_mktemp(const char * tmpdir)432 __archive_mktemp(const char *tmpdir)
433 {
434           return __archive_mktempx(tmpdir, NULL);
435 }
436 
437 int
__archive_mkstemp(wchar_t * template)438 __archive_mkstemp(wchar_t *template)
439 {
440           return __archive_mktempx(NULL, template);
441 }
442 
443 #else
444 
445 static int
get_tempdir(struct archive_string * temppath)446 get_tempdir(struct archive_string *temppath)
447 {
448           const char *tmp;
449 
450           tmp = getenv("TMPDIR");
451           if (tmp == NULL)
452 #ifdef _PATH_TMP
453                     tmp = _PATH_TMP;
454 #else
455                 tmp = "/tmp";
456 #endif
457           archive_strcpy(temppath, tmp);
458           if (temppath->length == 0 || temppath->s[temppath->length-1] != '/')
459                     archive_strappend_char(temppath, '/');
460           return (ARCHIVE_OK);
461 }
462 
463 #if defined(HAVE_MKSTEMP)
464 
465 /*
466  * We can use mkstemp().
467  */
468 
469 int
__archive_mktemp(const char * tmpdir)470 __archive_mktemp(const char *tmpdir)
471 {
472           struct archive_string temp_name;
473           int fd = -1;
474 
475           archive_string_init(&temp_name);
476           if (tmpdir == NULL) {
477                     if (get_tempdir(&temp_name) != ARCHIVE_OK)
478                               goto exit_tmpfile;
479           } else {
480                     archive_strcpy(&temp_name, tmpdir);
481                     if (temp_name.length == 0 ||
482                         temp_name.s[temp_name.length-1] != '/')
483                               archive_strappend_char(&temp_name, '/');
484           }
485 #ifdef O_TMPFILE
486           fd = open(temp_name.s, O_RDWR|O_CLOEXEC|O_TMPFILE|O_EXCL, 0600);
487           if(fd >= 0)
488                     goto exit_tmpfile;
489 #endif
490           archive_strcat(&temp_name, "libarchive_XXXXXX");
491           fd = mkstemp(temp_name.s);
492           if (fd < 0)
493                     goto exit_tmpfile;
494           __archive_ensure_cloexec_flag(fd);
495           unlink(temp_name.s);
496 exit_tmpfile:
497           archive_string_free(&temp_name);
498           return (fd);
499 }
500 
501 int
__archive_mkstemp(char * template)502 __archive_mkstemp(char *template)
503 {
504           int fd = -1;
505           fd = mkstemp(template);
506           if (fd >= 0)
507                     __archive_ensure_cloexec_flag(fd);
508           return (fd);
509 }
510 
511 #else /* !HAVE_MKSTEMP */
512 
513 /*
514  * We use a private routine.
515  */
516 
517 static int
__archive_mktempx(const char * tmpdir,char * template)518 __archive_mktempx(const char *tmpdir, char *template)
519 {
520         static const char num[] = {
521                     '0', '1', '2', '3', '4', '5', '6', '7',
522                     '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
523                     'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
524                     'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
525                     'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
526                     'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
527                     'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
528                     'u', 'v', 'w', 'x', 'y', 'z'
529         };
530           struct archive_string temp_name;
531           struct stat st;
532           int fd;
533           char *tp, *ep;
534 
535           fd = -1;
536           if (template == NULL) {
537                     archive_string_init(&temp_name);
538                     if (tmpdir == NULL) {
539                               if (get_tempdir(&temp_name) != ARCHIVE_OK)
540                                         goto exit_tmpfile;
541                     } else
542                               archive_strcpy(&temp_name, tmpdir);
543                     if (temp_name.length > 0 && temp_name.s[temp_name.length-1] == '/') {
544                               temp_name.s[temp_name.length-1] = '\0';
545                               temp_name.length --;
546                     }
547                     if (la_stat(temp_name.s, &st) < 0)
548                               goto exit_tmpfile;
549                     if (!S_ISDIR(st.st_mode)) {
550                               errno = ENOTDIR;
551                               goto exit_tmpfile;
552                     }
553                     archive_strcat(&temp_name, "/libarchive_");
554                     tp = temp_name.s + archive_strlen(&temp_name);
555                     archive_strcat(&temp_name, "XXXXXXXXXX");
556                     ep = temp_name.s + archive_strlen(&temp_name);
557                     template = temp_name.s;
558           } else {
559                     tp = strchr(template, 'X');
560                     if (tp == NULL)     /* No X, programming error */
561                               abort();
562                     for (ep = tp; *ep == 'X'; ep++)
563                               continue;
564                     if (*ep)  /* X followed by non X, programming error */
565                               abort();
566           }
567 
568           do {
569                     char *p;
570 
571                     p = tp;
572                     archive_random(p, ep - p);
573                     while (p < ep) {
574                               int d = *((unsigned char *)p) % sizeof(num);
575                               *p++ = num[d];
576                     }
577                     fd = open(template, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC,
578                                 0600);
579           } while (fd < 0 && errno == EEXIST);
580           if (fd < 0)
581                     goto exit_tmpfile;
582           __archive_ensure_cloexec_flag(fd);
583           if (template == temp_name.s)
584                     unlink(temp_name.s);
585 exit_tmpfile:
586           if (template == temp_name.s)
587                     archive_string_free(&temp_name);
588           return (fd);
589 }
590 
591 int
__archive_mktemp(const char * tmpdir)592 __archive_mktemp(const char *tmpdir)
593 {
594           return __archive_mktempx(tmpdir, NULL);
595 }
596 
597 int
__archive_mkstemp(char * template)598 __archive_mkstemp(char *template)
599 {
600           return __archive_mktempx(NULL, template);
601 }
602 
603 #endif /* !HAVE_MKSTEMP */
604 #endif /* !_WIN32 || __CYGWIN__ */
605 
606 /*
607  * Set FD_CLOEXEC flag to a file descriptor if it is not set.
608  * We have to set the flag if the platform does not provide O_CLOEXEC
609  * or F_DUPFD_CLOEXEC flags.
610  *
611  * Note: This function is absolutely called after creating a new file
612  * descriptor even if the platform seemingly provides O_CLOEXEC or
613  * F_DUPFD_CLOEXEC macros because it is possible that the platform
614  * merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it.
615  */
616 void
__archive_ensure_cloexec_flag(int fd)617 __archive_ensure_cloexec_flag(int fd)
618 {
619 #if defined(_WIN32) && !defined(__CYGWIN__)
620           (void)fd; /* UNUSED */
621 #else
622           int flags;
623 
624           if (fd >= 0) {
625                     flags = fcntl(fd, F_GETFD);
626                     if (flags != -1 && (flags & FD_CLOEXEC) == 0)
627                               fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
628           }
629 #endif
630 }
631 
632 /*
633  * Utility function to sort a group of strings using quicksort.
634  */
635 static int
archive_utility_string_sort_helper(char ** strings,unsigned int n)636 archive_utility_string_sort_helper(char **strings, unsigned int n)
637 {
638           unsigned int i, lesser_count, greater_count;
639           char **lesser, **greater, **tmp, *pivot;
640           int retval1, retval2;
641 
642           /* A list of 0 or 1 elements is already sorted */
643           if (n <= 1)
644                     return (ARCHIVE_OK);
645 
646           lesser_count = greater_count = 0;
647           lesser = greater = NULL;
648           pivot = strings[0];
649           for (i = 1; i < n; i++)
650           {
651                     if (strcmp(strings[i], pivot) < 0)
652                     {
653                               lesser_count++;
654                               tmp = realloc(lesser, lesser_count * sizeof(*tmp));
655                               if (!tmp) {
656                                         free(greater);
657                                         free(lesser);
658                                         return (ARCHIVE_FATAL);
659                               }
660                               lesser = tmp;
661                               lesser[lesser_count - 1] = strings[i];
662                     }
663                     else
664                     {
665                               greater_count++;
666                               tmp = realloc(greater, greater_count * sizeof(*tmp));
667                               if (!tmp) {
668                                         free(greater);
669                                         free(lesser);
670                                         return (ARCHIVE_FATAL);
671                               }
672                               greater = tmp;
673                               greater[greater_count - 1] = strings[i];
674                     }
675           }
676 
677           /* quicksort(lesser) */
678           retval1 = archive_utility_string_sort_helper(lesser, lesser_count);
679           for (i = 0; i < lesser_count; i++)
680                     strings[i] = lesser[i];
681           free(lesser);
682 
683           /* pivot */
684           strings[lesser_count] = pivot;
685 
686           /* quicksort(greater) */
687           retval2 = archive_utility_string_sort_helper(greater, greater_count);
688           for (i = 0; i < greater_count; i++)
689                     strings[lesser_count + 1 + i] = greater[i];
690           free(greater);
691 
692           return (retval1 < retval2) ? retval1 : retval2;
693 }
694 
695 int
archive_utility_string_sort(char ** strings)696 archive_utility_string_sort(char **strings)
697 {
698             unsigned int size = 0;
699             while (strings[size] != NULL)
700                     size++;
701             return archive_utility_string_sort_helper(strings, size);
702 }
703