archive_util.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  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. #include "archive_platform.h"
  27. #ifdef HAVE_SYS_TYPES_H
  28. #include <sys/types.h>
  29. #endif
  30. #ifdef HAVE_ERRNO_H
  31. #include <errno.h>
  32. #endif
  33. #ifdef HAVE_FCNTL_H
  34. #include <fcntl.h>
  35. #endif
  36. #ifdef HAVE_STDLIB_H
  37. #include <stdlib.h>
  38. #endif
  39. #ifdef HAVE_STRING_H
  40. #include <string.h>
  41. #endif
  42. #if defined(_WIN32) && !defined(__CYGWIN__)
  43. #if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
  44. /* don't use bcrypt when XP needs to be supported */
  45. #include <bcrypt.h>
  46. /* Common in other bcrypt implementations, but missing from VS2008. */
  47. #ifndef BCRYPT_SUCCESS
  48. #define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
  49. #endif
  50. #elif defined(HAVE_WINCRYPT_H)
  51. #include <wincrypt.h>
  52. #endif
  53. #endif
  54. #ifdef HAVE_ZLIB_H
  55. #include <cm3p/zlib.h>
  56. #endif
  57. #ifdef HAVE_LZMA_H
  58. #include <cm3p/lzma.h>
  59. #endif
  60. #ifdef HAVE_BZLIB_H
  61. #include <cm3p/bzlib.h>
  62. #endif
  63. #ifdef HAVE_LZ4_H
  64. #include <lz4.h>
  65. #endif
  66. #include "archive.h"
  67. #include "archive_private.h"
  68. #include "archive_random_private.h"
  69. #include "archive_string.h"
  70. #ifndef O_CLOEXEC
  71. #define O_CLOEXEC 0
  72. #endif
  73. #if ARCHIVE_VERSION_NUMBER < 4000000
  74. static int __LA_LIBC_CC archive_utility_string_sort_helper(const void *, const void *);
  75. #endif
  76. /* Generic initialization of 'struct archive' objects. */
  77. int
  78. __archive_clean(struct archive *a)
  79. {
  80. archive_string_conversion_free(a);
  81. return (ARCHIVE_OK);
  82. }
  83. int
  84. archive_version_number(void)
  85. {
  86. return (ARCHIVE_VERSION_NUMBER);
  87. }
  88. const char *
  89. archive_version_string(void)
  90. {
  91. return (ARCHIVE_VERSION_STRING);
  92. }
  93. int
  94. archive_errno(struct archive *a)
  95. {
  96. return (a->archive_error_number);
  97. }
  98. const char *
  99. archive_error_string(struct archive *a)
  100. {
  101. if (a->error != NULL && *a->error != '\0')
  102. return (a->error);
  103. else
  104. return (NULL);
  105. }
  106. int
  107. archive_file_count(struct archive *a)
  108. {
  109. return (a->file_count);
  110. }
  111. int
  112. archive_format(struct archive *a)
  113. {
  114. return (a->archive_format);
  115. }
  116. const char *
  117. archive_format_name(struct archive *a)
  118. {
  119. return (a->archive_format_name);
  120. }
  121. int
  122. archive_compression(struct archive *a)
  123. {
  124. return archive_filter_code(a, 0);
  125. }
  126. const char *
  127. archive_compression_name(struct archive *a)
  128. {
  129. return archive_filter_name(a, 0);
  130. }
  131. /*
  132. * Return a count of the number of compressed bytes processed.
  133. */
  134. la_int64_t
  135. archive_position_compressed(struct archive *a)
  136. {
  137. return archive_filter_bytes(a, -1);
  138. }
  139. /*
  140. * Return a count of the number of uncompressed bytes processed.
  141. */
  142. la_int64_t
  143. archive_position_uncompressed(struct archive *a)
  144. {
  145. return archive_filter_bytes(a, 0);
  146. }
  147. void
  148. archive_clear_error(struct archive *a)
  149. {
  150. archive_string_empty(&a->error_string);
  151. a->error = NULL;
  152. a->archive_error_number = 0;
  153. }
  154. void
  155. archive_set_error(struct archive *a, int error_number, const char *fmt, ...)
  156. {
  157. va_list ap;
  158. a->archive_error_number = error_number;
  159. if (fmt == NULL) {
  160. a->error = NULL;
  161. return;
  162. }
  163. archive_string_empty(&(a->error_string));
  164. va_start(ap, fmt);
  165. archive_string_vsprintf(&(a->error_string), fmt, ap);
  166. va_end(ap);
  167. a->error = a->error_string.s;
  168. }
  169. void
  170. archive_copy_error(struct archive *dest, struct archive *src)
  171. {
  172. dest->archive_error_number = src->archive_error_number;
  173. archive_string_copy(&dest->error_string, &src->error_string);
  174. dest->error = dest->error_string.s;
  175. }
  176. void
  177. __archive_errx(int retvalue, const char *msg)
  178. {
  179. static const char msg1[] = "Fatal Internal Error in libarchive: ";
  180. size_t s;
  181. s = write(2, msg1, strlen(msg1));
  182. (void)s; /* UNUSED */
  183. s = write(2, msg, strlen(msg));
  184. (void)s; /* UNUSED */
  185. s = write(2, "\n", 1);
  186. (void)s; /* UNUSED */
  187. exit(retvalue);
  188. }
  189. /*
  190. * Create a temporary file
  191. */
  192. #if defined(_WIN32) && !defined(__CYGWIN__)
  193. /*
  194. * Do not use Windows tmpfile() function.
  195. * It will make a temporary file under the root directory
  196. * and it'll cause permission error if a user who is
  197. * non-Administrator creates temporary files.
  198. * Also Windows version of mktemp family including _mktemp_s
  199. * are not secure.
  200. */
  201. static int
  202. __archive_mktempx(const char *tmpdir, wchar_t *template)
  203. {
  204. static const wchar_t prefix[] = L"libarchive_";
  205. static const wchar_t suffix[] = L"XXXXXXXXXX";
  206. static const wchar_t num[] = {
  207. L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7',
  208. L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F',
  209. L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N',
  210. L'O', L'P', L'Q', L'R', L'S', L'T', L'U', L'V',
  211. L'W', L'X', L'Y', L'Z', L'a', L'b', L'c', L'd',
  212. L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l',
  213. L'm', L'n', L'o', L'p', L'q', L'r', L's', L't',
  214. L'u', L'v', L'w', L'x', L'y', L'z'
  215. };
  216. struct archive_wstring temp_name;
  217. wchar_t *ws;
  218. DWORD attr;
  219. wchar_t *xp, *ep;
  220. int fd;
  221. #if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
  222. BCRYPT_ALG_HANDLE hAlg = NULL;
  223. #else
  224. HCRYPTPROV hProv = (HCRYPTPROV)NULL;
  225. #endif
  226. fd = -1;
  227. ws = NULL;
  228. archive_string_init(&temp_name);
  229. if (template == NULL) {
  230. /* Get a temporary directory. */
  231. if (tmpdir == NULL) {
  232. size_t l;
  233. wchar_t *tmp;
  234. l = GetTempPathW(0, NULL);
  235. if (l == 0) {
  236. la_dosmaperr(GetLastError());
  237. goto exit_tmpfile;
  238. }
  239. tmp = malloc(l*sizeof(wchar_t));
  240. if (tmp == NULL) {
  241. errno = ENOMEM;
  242. goto exit_tmpfile;
  243. }
  244. GetTempPathW((DWORD)l, tmp);
  245. archive_wstrcpy(&temp_name, tmp);
  246. free(tmp);
  247. } else {
  248. if (archive_wstring_append_from_mbs(&temp_name, tmpdir,
  249. strlen(tmpdir)) < 0)
  250. goto exit_tmpfile;
  251. if (temp_name.length == 0 ||
  252. temp_name.s[temp_name.length-1] != L'/')
  253. archive_wstrappend_wchar(&temp_name, L'/');
  254. }
  255. /* Check if temp_name is a directory. */
  256. attr = GetFileAttributesW(temp_name.s);
  257. if (attr == (DWORD)-1) {
  258. if (GetLastError() != ERROR_FILE_NOT_FOUND) {
  259. la_dosmaperr(GetLastError());
  260. goto exit_tmpfile;
  261. }
  262. ws = __la_win_permissive_name_w(temp_name.s);
  263. if (ws == NULL) {
  264. errno = EINVAL;
  265. goto exit_tmpfile;
  266. }
  267. attr = GetFileAttributesW(ws);
  268. if (attr == (DWORD)-1) {
  269. la_dosmaperr(GetLastError());
  270. goto exit_tmpfile;
  271. }
  272. }
  273. if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
  274. errno = ENOTDIR;
  275. goto exit_tmpfile;
  276. }
  277. /*
  278. * Create a temporary file.
  279. */
  280. archive_wstrcat(&temp_name, prefix);
  281. archive_wstrcat(&temp_name, suffix);
  282. ep = temp_name.s + archive_strlen(&temp_name);
  283. xp = ep - wcslen(suffix);
  284. template = temp_name.s;
  285. } else {
  286. xp = wcschr(template, L'X');
  287. if (xp == NULL) /* No X, programming error */
  288. abort();
  289. for (ep = xp; *ep == L'X'; ep++)
  290. continue;
  291. if (*ep) /* X followed by non X, programming error */
  292. abort();
  293. }
  294. #if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
  295. if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM,
  296. NULL, 0))) {
  297. la_dosmaperr(GetLastError());
  298. goto exit_tmpfile;
  299. }
  300. #else
  301. if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
  302. CRYPT_VERIFYCONTEXT)) {
  303. la_dosmaperr(GetLastError());
  304. goto exit_tmpfile;
  305. }
  306. #endif
  307. for (;;) {
  308. wchar_t *p;
  309. HANDLE h;
  310. # if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
  311. CREATEFILE2_EXTENDED_PARAMETERS createExParams;
  312. #endif
  313. /* Generate a random file name through CryptGenRandom(). */
  314. p = xp;
  315. #if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
  316. if (!BCRYPT_SUCCESS(BCryptGenRandom(hAlg, (PUCHAR)p,
  317. (DWORD)(ep - p)*sizeof(wchar_t), 0))) {
  318. la_dosmaperr(GetLastError());
  319. goto exit_tmpfile;
  320. }
  321. #else
  322. if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t),
  323. (BYTE*)p)) {
  324. la_dosmaperr(GetLastError());
  325. goto exit_tmpfile;
  326. }
  327. #endif
  328. for (; p < ep; p++)
  329. *p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];
  330. free(ws);
  331. ws = __la_win_permissive_name_w(template);
  332. if (ws == NULL) {
  333. errno = EINVAL;
  334. goto exit_tmpfile;
  335. }
  336. if (template == temp_name.s) {
  337. attr = FILE_ATTRIBUTE_TEMPORARY |
  338. FILE_FLAG_DELETE_ON_CLOSE;
  339. } else {
  340. /* mkstemp */
  341. attr = FILE_ATTRIBUTE_NORMAL;
  342. }
  343. # if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
  344. ZeroMemory(&createExParams, sizeof(createExParams));
  345. createExParams.dwSize = sizeof(createExParams);
  346. createExParams.dwFileAttributes = attr & 0xFFFF;
  347. createExParams.dwFileFlags = attr & 0xFFF00000;
  348. h = CreateFile2(ws,
  349. GENERIC_READ | GENERIC_WRITE | DELETE,
  350. 0,/* Not share */
  351. CREATE_NEW,
  352. &createExParams);
  353. #else
  354. h = CreateFileW(ws,
  355. GENERIC_READ | GENERIC_WRITE | DELETE,
  356. 0,/* Not share */
  357. NULL,
  358. CREATE_NEW,/* Create a new file only */
  359. attr,
  360. NULL);
  361. #endif
  362. if (h == INVALID_HANDLE_VALUE) {
  363. /* The same file already exists. retry with
  364. * a new filename. */
  365. if (GetLastError() == ERROR_FILE_EXISTS)
  366. continue;
  367. /* Otherwise, fail creation temporary file. */
  368. la_dosmaperr(GetLastError());
  369. goto exit_tmpfile;
  370. }
  371. fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR);
  372. if (fd == -1) {
  373. la_dosmaperr(GetLastError());
  374. CloseHandle(h);
  375. goto exit_tmpfile;
  376. } else
  377. break;/* success! */
  378. }
  379. exit_tmpfile:
  380. #if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
  381. if (hAlg != NULL)
  382. BCryptCloseAlgorithmProvider(hAlg, 0);
  383. #else
  384. if (hProv != (HCRYPTPROV)NULL)
  385. CryptReleaseContext(hProv, 0);
  386. #endif
  387. free(ws);
  388. if (template == temp_name.s)
  389. archive_wstring_free(&temp_name);
  390. return (fd);
  391. }
  392. int
  393. __archive_mktemp(const char *tmpdir)
  394. {
  395. return __archive_mktempx(tmpdir, NULL);
  396. }
  397. int
  398. __archive_mkstemp(wchar_t *template)
  399. {
  400. return __archive_mktempx(NULL, template);
  401. }
  402. #else
  403. static int
  404. get_tempdir(struct archive_string *temppath)
  405. {
  406. const char *tmp;
  407. tmp = getenv("TMPDIR");
  408. if (tmp == NULL)
  409. #ifdef _PATH_TMP
  410. tmp = _PATH_TMP;
  411. #else
  412. tmp = "/tmp";
  413. #endif
  414. archive_strcpy(temppath, tmp);
  415. if (temppath->length == 0 || temppath->s[temppath->length-1] != '/')
  416. archive_strappend_char(temppath, '/');
  417. return (ARCHIVE_OK);
  418. }
  419. #if defined(HAVE_MKSTEMP)
  420. /*
  421. * We can use mkstemp().
  422. */
  423. int
  424. __archive_mktemp(const char *tmpdir)
  425. {
  426. struct archive_string temp_name;
  427. int fd = -1;
  428. archive_string_init(&temp_name);
  429. if (tmpdir == NULL) {
  430. if (get_tempdir(&temp_name) != ARCHIVE_OK)
  431. goto exit_tmpfile;
  432. } else {
  433. archive_strcpy(&temp_name, tmpdir);
  434. if (temp_name.length == 0 ||
  435. temp_name.s[temp_name.length-1] != '/')
  436. archive_strappend_char(&temp_name, '/');
  437. }
  438. #ifdef O_TMPFILE
  439. fd = open(temp_name.s, O_RDWR|O_CLOEXEC|O_TMPFILE|O_EXCL, 0600);
  440. if(fd >= 0)
  441. goto exit_tmpfile;
  442. #endif
  443. archive_strcat(&temp_name, "libarchive_XXXXXX");
  444. fd = mkstemp(temp_name.s);
  445. if (fd < 0)
  446. goto exit_tmpfile;
  447. __archive_ensure_cloexec_flag(fd);
  448. unlink(temp_name.s);
  449. exit_tmpfile:
  450. archive_string_free(&temp_name);
  451. return (fd);
  452. }
  453. int
  454. __archive_mkstemp(char *template)
  455. {
  456. int fd = -1;
  457. fd = mkstemp(template);
  458. if (fd >= 0)
  459. __archive_ensure_cloexec_flag(fd);
  460. return (fd);
  461. }
  462. #else /* !HAVE_MKSTEMP */
  463. /*
  464. * We use a private routine.
  465. */
  466. static int
  467. __archive_mktempx(const char *tmpdir, char *template)
  468. {
  469. static const char num[] = {
  470. '0', '1', '2', '3', '4', '5', '6', '7',
  471. '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
  472. 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
  473. 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
  474. 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
  475. 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
  476. 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
  477. 'u', 'v', 'w', 'x', 'y', 'z'
  478. };
  479. struct archive_string temp_name;
  480. struct stat st;
  481. int fd;
  482. char *tp, *ep;
  483. fd = -1;
  484. if (template == NULL) {
  485. archive_string_init(&temp_name);
  486. if (tmpdir == NULL) {
  487. if (get_tempdir(&temp_name) != ARCHIVE_OK)
  488. goto exit_tmpfile;
  489. } else
  490. archive_strcpy(&temp_name, tmpdir);
  491. if (temp_name.length > 0 && temp_name.s[temp_name.length-1] == '/') {
  492. temp_name.s[temp_name.length-1] = '\0';
  493. temp_name.length --;
  494. }
  495. if (la_stat(temp_name.s, &st) < 0)
  496. goto exit_tmpfile;
  497. if (!S_ISDIR(st.st_mode)) {
  498. errno = ENOTDIR;
  499. goto exit_tmpfile;
  500. }
  501. archive_strcat(&temp_name, "/libarchive_");
  502. tp = temp_name.s + archive_strlen(&temp_name);
  503. archive_strcat(&temp_name, "XXXXXXXXXX");
  504. ep = temp_name.s + archive_strlen(&temp_name);
  505. template = temp_name.s;
  506. } else {
  507. tp = strchr(template, 'X');
  508. if (tp == NULL) /* No X, programming error */
  509. abort();
  510. for (ep = tp; *ep == 'X'; ep++)
  511. continue;
  512. if (*ep) /* X followed by non X, programming error */
  513. abort();
  514. }
  515. do {
  516. char *p;
  517. p = tp;
  518. archive_random(p, ep - p);
  519. while (p < ep) {
  520. int d = *((unsigned char *)p) % sizeof(num);
  521. *p++ = num[d];
  522. }
  523. fd = open(template, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC,
  524. 0600);
  525. } while (fd < 0 && errno == EEXIST);
  526. if (fd < 0)
  527. goto exit_tmpfile;
  528. __archive_ensure_cloexec_flag(fd);
  529. if (template == temp_name.s)
  530. unlink(temp_name.s);
  531. exit_tmpfile:
  532. if (template == temp_name.s)
  533. archive_string_free(&temp_name);
  534. return (fd);
  535. }
  536. int
  537. __archive_mktemp(const char *tmpdir)
  538. {
  539. return __archive_mktempx(tmpdir, NULL);
  540. }
  541. int
  542. __archive_mkstemp(char *template)
  543. {
  544. return __archive_mktempx(NULL, template);
  545. }
  546. #endif /* !HAVE_MKSTEMP */
  547. #endif /* !_WIN32 || __CYGWIN__ */
  548. /*
  549. * Set FD_CLOEXEC flag to a file descriptor if it is not set.
  550. * We have to set the flag if the platform does not provide O_CLOEXEC
  551. * or F_DUPFD_CLOEXEC flags.
  552. *
  553. * Note: This function is absolutely called after creating a new file
  554. * descriptor even if the platform seemingly provides O_CLOEXEC or
  555. * F_DUPFD_CLOEXEC macros because it is possible that the platform
  556. * merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it.
  557. */
  558. void
  559. __archive_ensure_cloexec_flag(int fd)
  560. {
  561. #if defined(_WIN32) && !defined(__CYGWIN__)
  562. (void)fd; /* UNUSED */
  563. #else
  564. int flags;
  565. if (fd >= 0) {
  566. flags = fcntl(fd, F_GETFD);
  567. if (flags != -1 && (flags & FD_CLOEXEC) == 0)
  568. fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
  569. }
  570. #endif
  571. }
  572. #if ARCHIVE_VERSION_NUMBER < 4000000
  573. /*
  574. * Utility functions to sort a group of strings using quicksort.
  575. */
  576. static int
  577. __LA_LIBC_CC
  578. archive_utility_string_sort_helper(const void *p1, const void *p2)
  579. {
  580. const char * const * const s1 = p1;
  581. const char * const * const s2 = p2;
  582. return strcmp(*s1, *s2);
  583. }
  584. int
  585. archive_utility_string_sort(char **strings)
  586. {
  587. size_t size = 0;
  588. while (strings[size] != NULL)
  589. size++;
  590. qsort(strings, size, sizeof(char *),
  591. archive_utility_string_sort_helper);
  592. return (ARCHIVE_OK);
  593. }
  594. #endif