xref: /freebsd/contrib/libarchive/libarchive/archive_util.c (revision 0d4ad64077bcddcff5a170ee97273db95b9cab55)
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
84 __archive_clean(struct archive *a)
85 {
86 	archive_string_conversion_free(a);
87 	return (ARCHIVE_OK);
88 }
89 
90 int
91 archive_version_number(void)
92 {
93 	return (ARCHIVE_VERSION_NUMBER);
94 }
95 
96 const char *
97 archive_version_string(void)
98 {
99 	return (ARCHIVE_VERSION_STRING);
100 }
101 
102 int
103 archive_errno(struct archive *a)
104 {
105 	return (a->archive_error_number);
106 }
107 
108 const char *
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
119 archive_file_count(struct archive *a)
120 {
121 	return (a->file_count);
122 }
123 
124 int
125 archive_format(struct archive *a)
126 {
127 	return (a->archive_format);
128 }
129 
130 const char *
131 archive_format_name(struct archive *a)
132 {
133 	return (a->archive_format_name);
134 }
135 
136 
137 int
138 archive_compression(struct archive *a)
139 {
140 	return archive_filter_code(a, 0);
141 }
142 
143 const char *
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
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
163 archive_position_uncompressed(struct archive *a)
164 {
165 	return archive_filter_bytes(a, 0);
166 }
167 
168 void
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
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
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
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
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 
259 	if (template == NULL) {
260 		archive_string_init(&temp_name);
261 
262 		/* Get a temporary directory. */
263 		if (tmpdir == NULL) {
264 			size_t l;
265 			wchar_t *tmp;
266 
267 			l = GetTempPathW(0, NULL);
268 			if (l == 0) {
269 				la_dosmaperr(GetLastError());
270 				goto exit_tmpfile;
271 			}
272 			tmp = malloc(l*sizeof(wchar_t));
273 			if (tmp == NULL) {
274 				errno = ENOMEM;
275 				goto exit_tmpfile;
276 			}
277 			GetTempPathW((DWORD)l, tmp);
278 			archive_wstrcpy(&temp_name, tmp);
279 			free(tmp);
280 		} else {
281 			if (archive_wstring_append_from_mbs(&temp_name, tmpdir,
282 			    strlen(tmpdir)) < 0)
283 				goto exit_tmpfile;
284 			if (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
432 __archive_mktemp(const char *tmpdir)
433 {
434 	return __archive_mktempx(tmpdir, NULL);
435 }
436 
437 int
438 __archive_mkstemp(wchar_t *template)
439 {
440 	return __archive_mktempx(NULL, template);
441 }
442 
443 #else
444 
445 static int
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->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
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.s[temp_name.length-1] != '/')
482 			archive_strappend_char(&temp_name, '/');
483 	}
484 #ifdef O_TMPFILE
485 	fd = open(temp_name.s, O_RDWR|O_CLOEXEC|O_TMPFILE|O_EXCL, 0600);
486 	if(fd >= 0)
487 		goto exit_tmpfile;
488 #endif
489 	archive_strcat(&temp_name, "libarchive_XXXXXX");
490 	fd = mkstemp(temp_name.s);
491 	if (fd < 0)
492 		goto exit_tmpfile;
493 	__archive_ensure_cloexec_flag(fd);
494 	unlink(temp_name.s);
495 exit_tmpfile:
496 	archive_string_free(&temp_name);
497 	return (fd);
498 }
499 
500 int
501 __archive_mkstemp(char *template)
502 {
503 	int fd = -1;
504 	fd = mkstemp(template);
505 	if (fd >= 0)
506 		__archive_ensure_cloexec_flag(fd);
507 	return (fd);
508 }
509 
510 #else /* !HAVE_MKSTEMP */
511 
512 /*
513  * We use a private routine.
514  */
515 
516 static int
517 __archive_mktempx(const char *tmpdir, char *template)
518 {
519         static const char num[] = {
520 		'0', '1', '2', '3', '4', '5', '6', '7',
521 		'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
522 		'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
523 		'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
524 		'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
525 		'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
526 		'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
527 		'u', 'v', 'w', 'x', 'y', 'z'
528         };
529 	struct archive_string temp_name;
530 	struct stat st;
531 	int fd;
532 	char *tp, *ep;
533 
534 	fd = -1;
535 	if (template == NULL) {
536 		archive_string_init(&temp_name);
537 		if (tmpdir == NULL) {
538 			if (get_tempdir(&temp_name) != ARCHIVE_OK)
539 				goto exit_tmpfile;
540 		} else
541 			archive_strcpy(&temp_name, tmpdir);
542 		if (temp_name.s[temp_name.length-1] == '/') {
543 			temp_name.s[temp_name.length-1] = '\0';
544 			temp_name.length --;
545 		}
546 		if (la_stat(temp_name.s, &st) < 0)
547 			goto exit_tmpfile;
548 		if (!S_ISDIR(st.st_mode)) {
549 			errno = ENOTDIR;
550 			goto exit_tmpfile;
551 		}
552 		archive_strcat(&temp_name, "/libarchive_");
553 		tp = temp_name.s + archive_strlen(&temp_name);
554 		archive_strcat(&temp_name, "XXXXXXXXXX");
555 		ep = temp_name.s + archive_strlen(&temp_name);
556 		template = temp_name.s;
557 	} else {
558 		tp = strchr(template, 'X');
559 		if (tp == NULL)	/* No X, programming error */
560 			abort();
561 		for (ep = tp; *ep == 'X'; ep++)
562 			continue;
563 		if (*ep)	/* X followed by non X, programming error */
564 			abort();
565 	}
566 
567 	do {
568 		char *p;
569 
570 		p = tp;
571 		archive_random(p, ep - p);
572 		while (p < ep) {
573 			int d = *((unsigned char *)p) % sizeof(num);
574 			*p++ = num[d];
575 		}
576 		fd = open(template, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC,
577 			  0600);
578 	} while (fd < 0 && errno == EEXIST);
579 	if (fd < 0)
580 		goto exit_tmpfile;
581 	__archive_ensure_cloexec_flag(fd);
582 	if (template == temp_name.s)
583 		unlink(temp_name.s);
584 exit_tmpfile:
585 	if (template == temp_name.s)
586 		archive_string_free(&temp_name);
587 	return (fd);
588 }
589 
590 int
591 __archive_mktemp(const char *tmpdir)
592 {
593 	return __archive_mktempx(tmpdir, NULL);
594 }
595 
596 int
597 __archive_mkstemp(char *template)
598 {
599 	return __archive_mktempx(NULL, template);
600 }
601 
602 #endif /* !HAVE_MKSTEMP */
603 #endif /* !_WIN32 || __CYGWIN__ */
604 
605 /*
606  * Set FD_CLOEXEC flag to a file descriptor if it is not set.
607  * We have to set the flag if the platform does not provide O_CLOEXEC
608  * or F_DUPFD_CLOEXEC flags.
609  *
610  * Note: This function is absolutely called after creating a new file
611  * descriptor even if the platform seemingly provides O_CLOEXEC or
612  * F_DUPFD_CLOEXEC macros because it is possible that the platform
613  * merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it.
614  */
615 void
616 __archive_ensure_cloexec_flag(int fd)
617 {
618 #if defined(_WIN32) && !defined(__CYGWIN__)
619 	(void)fd; /* UNUSED */
620 #else
621 	int flags;
622 
623 	if (fd >= 0) {
624 		flags = fcntl(fd, F_GETFD);
625 		if (flags != -1 && (flags & FD_CLOEXEC) == 0)
626 			fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
627 	}
628 #endif
629 }
630 
631 /*
632  * Utility function to sort a group of strings using quicksort.
633  */
634 static int
635 archive_utility_string_sort_helper(char **strings, unsigned int n)
636 {
637 	unsigned int i, lesser_count, greater_count;
638 	char **lesser, **greater, **tmp, *pivot;
639 	int retval1, retval2;
640 
641 	/* A list of 0 or 1 elements is already sorted */
642 	if (n <= 1)
643 		return (ARCHIVE_OK);
644 
645 	lesser_count = greater_count = 0;
646 	lesser = greater = NULL;
647 	pivot = strings[0];
648 	for (i = 1; i < n; i++)
649 	{
650 		if (strcmp(strings[i], pivot) < 0)
651 		{
652 			lesser_count++;
653 			tmp = (char **)realloc(lesser,
654 				lesser_count * sizeof(char *));
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 = (char **)realloc(greater,
667 				greater_count * sizeof(char *));
668 			if (!tmp) {
669 				free(greater);
670 				free(lesser);
671 				return (ARCHIVE_FATAL);
672 			}
673 			greater = tmp;
674 			greater[greater_count - 1] = strings[i];
675 		}
676 	}
677 
678 	/* quicksort(lesser) */
679 	retval1 = archive_utility_string_sort_helper(lesser, lesser_count);
680 	for (i = 0; i < lesser_count; i++)
681 		strings[i] = lesser[i];
682 	free(lesser);
683 
684 	/* pivot */
685 	strings[lesser_count] = pivot;
686 
687 	/* quicksort(greater) */
688 	retval2 = archive_utility_string_sort_helper(greater, greater_count);
689 	for (i = 0; i < greater_count; i++)
690 		strings[lesser_count + 1 + i] = greater[i];
691 	free(greater);
692 
693 	return (retval1 < retval2) ? retval1 : retval2;
694 }
695 
696 int
697 archive_utility_string_sort(char **strings)
698 {
699 	  unsigned int size = 0;
700 	  while (strings[size] != NULL)
701 		size++;
702 	  return archive_utility_string_sort_helper(strings, size);
703 }
704