xref: /freebsd/contrib/libarchive/libarchive/archive_util.c (revision bd66c1b43e33540205dbc1187c2f2a15c58b57ba)
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