Coverage Report

Created: 2026-03-12 06:35

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