xref: /freebsd/sys/contrib/zstd/programs/util.c (revision 5ff13fbc199bdf5f0572845351c68ee5ca828e71)
1a0483764SConrad Meyer /*
2*5ff13fbcSAllan Jude  * Copyright (c) Przemyslaw Skibinski, Yann Collet, Facebook, Inc.
3a0483764SConrad Meyer  * All rights reserved.
4a0483764SConrad Meyer  *
5a0483764SConrad Meyer  * This source code is licensed under both the BSD-style license (found in the
6a0483764SConrad Meyer  * LICENSE file in the root directory of this source tree) and the GPLv2 (found
7a0483764SConrad Meyer  * in the COPYING file in the root directory of this source tree).
8a0483764SConrad Meyer  * You may select, at your option, one of the above-listed licenses.
9a0483764SConrad Meyer  */
10a0483764SConrad Meyer 
11a0483764SConrad Meyer #if defined (__cplusplus)
12a0483764SConrad Meyer extern "C" {
13a0483764SConrad Meyer #endif
14a0483764SConrad Meyer 
15a0483764SConrad Meyer 
16a0483764SConrad Meyer /*-****************************************
17a0483764SConrad Meyer *  Dependencies
18a0483764SConrad Meyer ******************************************/
19a0483764SConrad Meyer #include "util.h"       /* note : ensure that platform.h is included first ! */
2037f1f268SConrad Meyer #include <stdlib.h>     /* malloc, realloc, free */
2137f1f268SConrad Meyer #include <stdio.h>      /* fprintf */
2237f1f268SConrad Meyer #include <time.h>       /* clock_t, clock, CLOCKS_PER_SEC, nanosleep */
23a0483764SConrad Meyer #include <errno.h>
24a0483764SConrad Meyer #include <assert.h>
25a0483764SConrad Meyer 
2637f1f268SConrad Meyer #if defined(_WIN32)
2737f1f268SConrad Meyer #  include <sys/utime.h>  /* utime */
2837f1f268SConrad Meyer #  include <io.h>         /* _chmod */
2937f1f268SConrad Meyer #else
3037f1f268SConrad Meyer #  include <unistd.h>     /* chown, stat */
31f7cd7fe5SConrad Meyer #  if PLATFORM_POSIX_VERSION < 200809L || !defined(st_mtime)
3237f1f268SConrad Meyer #    include <utime.h>    /* utime */
3337f1f268SConrad Meyer #  else
3437f1f268SConrad Meyer #    include <fcntl.h>    /* AT_FDCWD */
3537f1f268SConrad Meyer #    include <sys/stat.h> /* utimensat */
3637f1f268SConrad Meyer #  endif
3737f1f268SConrad Meyer #endif
3837f1f268SConrad Meyer 
399cbefe25SConrad Meyer #if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__)
409cbefe25SConrad Meyer #include <direct.h>     /* needed for _mkdir in windows */
419cbefe25SConrad Meyer #endif
42a0483764SConrad Meyer 
4337f1f268SConrad Meyer #if defined(__linux__) || (PLATFORM_POSIX_VERSION >= 200112L)  /* opendir, readdir require POSIX.1-2001 */
4437f1f268SConrad Meyer #  include <dirent.h>       /* opendir, readdir */
4537f1f268SConrad Meyer #  include <string.h>       /* strerror, memcpy */
4637f1f268SConrad Meyer #endif /* #ifdef _WIN32 */
4737f1f268SConrad Meyer 
4837f1f268SConrad Meyer /*-****************************************
4937f1f268SConrad Meyer *  Internal Macros
5037f1f268SConrad Meyer ******************************************/
5137f1f268SConrad Meyer 
5237f1f268SConrad Meyer /* CONTROL is almost like an assert(), but is never disabled.
5337f1f268SConrad Meyer  * It's designed for failures that may happen rarely,
5437f1f268SConrad Meyer  * but we don't want to maintain a specific error code path for them,
5537f1f268SConrad Meyer  * such as a malloc() returning NULL for example.
5637f1f268SConrad Meyer  * Since it's always active, this macro can trigger side effects.
5737f1f268SConrad Meyer  */
5837f1f268SConrad Meyer #define CONTROL(c)  {         \
5937f1f268SConrad Meyer     if (!(c)) {               \
6037f1f268SConrad Meyer         UTIL_DISPLAYLEVEL(1, "Error : %s, %i : %s",  \
6137f1f268SConrad Meyer                           __FILE__, __LINE__, #c);   \
6237f1f268SConrad Meyer         exit(1);              \
6337f1f268SConrad Meyer }   }
6437f1f268SConrad Meyer 
6537f1f268SConrad Meyer /* console log */
6637f1f268SConrad Meyer #define UTIL_DISPLAY(...)         fprintf(stderr, __VA_ARGS__)
6737f1f268SConrad Meyer #define UTIL_DISPLAYLEVEL(l, ...) { if (g_utilDisplayLevel>=l) { UTIL_DISPLAY(__VA_ARGS__); } }
6837f1f268SConrad Meyer 
6937f1f268SConrad Meyer /* A modified version of realloc().
7037f1f268SConrad Meyer  * If UTIL_realloc() fails the original block is freed.
7137f1f268SConrad Meyer  */
UTIL_realloc(void * ptr,size_t size)7237f1f268SConrad Meyer UTIL_STATIC void* UTIL_realloc(void *ptr, size_t size)
7337f1f268SConrad Meyer {
7437f1f268SConrad Meyer     void *newptr = realloc(ptr, size);
7537f1f268SConrad Meyer     if (newptr) return newptr;
7637f1f268SConrad Meyer     free(ptr);
7737f1f268SConrad Meyer     return NULL;
7837f1f268SConrad Meyer }
7937f1f268SConrad Meyer 
8037f1f268SConrad Meyer #if defined(_MSC_VER)
8137f1f268SConrad Meyer     #define chmod _chmod
8237f1f268SConrad Meyer #endif
8337f1f268SConrad Meyer 
8437f1f268SConrad Meyer 
8537f1f268SConrad Meyer /*-****************************************
8637f1f268SConrad Meyer *  Console log
8737f1f268SConrad Meyer ******************************************/
8837f1f268SConrad Meyer int g_utilDisplayLevel;
8937f1f268SConrad Meyer 
UTIL_requireUserConfirmation(const char * prompt,const char * abortMsg,const char * acceptableLetters,int hasStdinInput)90f7cd7fe5SConrad Meyer int UTIL_requireUserConfirmation(const char* prompt, const char* abortMsg,
91f7cd7fe5SConrad Meyer                                  const char* acceptableLetters, int hasStdinInput) {
92f7cd7fe5SConrad Meyer     int ch, result;
93f7cd7fe5SConrad Meyer 
94f7cd7fe5SConrad Meyer     if (hasStdinInput) {
95f7cd7fe5SConrad Meyer         UTIL_DISPLAY("stdin is an input - not proceeding.\n");
96f7cd7fe5SConrad Meyer         return 1;
97f7cd7fe5SConrad Meyer     }
98f7cd7fe5SConrad Meyer 
99f7cd7fe5SConrad Meyer     UTIL_DISPLAY("%s", prompt);
100f7cd7fe5SConrad Meyer     ch = getchar();
101f7cd7fe5SConrad Meyer     result = 0;
102f7cd7fe5SConrad Meyer     if (strchr(acceptableLetters, ch) == NULL) {
103f7cd7fe5SConrad Meyer         UTIL_DISPLAY("%s", abortMsg);
104f7cd7fe5SConrad Meyer         result = 1;
105f7cd7fe5SConrad Meyer     }
106f7cd7fe5SConrad Meyer     /* flush the rest */
107f7cd7fe5SConrad Meyer     while ((ch!=EOF) && (ch!='\n'))
108f7cd7fe5SConrad Meyer         ch = getchar();
109f7cd7fe5SConrad Meyer     return result;
110f7cd7fe5SConrad Meyer }
111f7cd7fe5SConrad Meyer 
11237f1f268SConrad Meyer 
11337f1f268SConrad Meyer /*-*************************************
11437f1f268SConrad Meyer *  Constants
11537f1f268SConrad Meyer ***************************************/
11637f1f268SConrad Meyer #define LIST_SIZE_INCREASE   (8*1024)
11737f1f268SConrad Meyer #define MAX_FILE_OF_FILE_NAMES_SIZE (1<<20)*50
11837f1f268SConrad Meyer 
11937f1f268SConrad Meyer 
12037f1f268SConrad Meyer /*-*************************************
12137f1f268SConrad Meyer *  Functions
12237f1f268SConrad Meyer ***************************************/
12337f1f268SConrad Meyer 
UTIL_stat(const char * filename,stat_t * statbuf)124f7cd7fe5SConrad Meyer int UTIL_stat(const char* filename, stat_t* statbuf)
125a0483764SConrad Meyer {
126a0483764SConrad Meyer #if defined(_MSC_VER)
127f7cd7fe5SConrad Meyer     return !_stat64(filename, statbuf);
128f7cd7fe5SConrad Meyer #elif defined(__MINGW32__) && defined (__MSVCRT__)
129f7cd7fe5SConrad Meyer     return !_stati64(filename, statbuf);
130a0483764SConrad Meyer #else
131f7cd7fe5SConrad Meyer     return !stat(filename, statbuf);
132a0483764SConrad Meyer #endif
133a0483764SConrad Meyer }
134a0483764SConrad Meyer 
UTIL_isRegularFile(const char * infilename)135a0483764SConrad Meyer int UTIL_isRegularFile(const char* infilename)
136a0483764SConrad Meyer {
137a0483764SConrad Meyer     stat_t statbuf;
138f7cd7fe5SConrad Meyer     return UTIL_stat(infilename, &statbuf) && UTIL_isRegularFileStat(&statbuf);
139a0483764SConrad Meyer }
140a0483764SConrad Meyer 
UTIL_isRegularFileStat(const stat_t * statbuf)141f7cd7fe5SConrad Meyer int UTIL_isRegularFileStat(const stat_t* statbuf)
142a0483764SConrad Meyer {
143a0483764SConrad Meyer #if defined(_MSC_VER)
144f7cd7fe5SConrad Meyer     return (statbuf->st_mode & S_IFREG) != 0;
145a0483764SConrad Meyer #else
146f7cd7fe5SConrad Meyer     return S_ISREG(statbuf->st_mode) != 0;
147a0483764SConrad Meyer #endif
148a0483764SConrad Meyer }
149a0483764SConrad Meyer 
15037f1f268SConrad Meyer /* like chmod, but avoid changing permission of /dev/null */
UTIL_chmod(char const * filename,const stat_t * statbuf,mode_t permissions)151f7cd7fe5SConrad Meyer int UTIL_chmod(char const* filename, const stat_t* statbuf, mode_t permissions)
15237f1f268SConrad Meyer {
153f7cd7fe5SConrad Meyer     stat_t localStatBuf;
154f7cd7fe5SConrad Meyer     if (statbuf == NULL) {
155f7cd7fe5SConrad Meyer         if (!UTIL_stat(filename, &localStatBuf)) return 0;
156f7cd7fe5SConrad Meyer         statbuf = &localStatBuf;
157f7cd7fe5SConrad Meyer     }
158f7cd7fe5SConrad Meyer     if (!UTIL_isRegularFileStat(statbuf)) return 0; /* pretend success, but don't change anything */
15937f1f268SConrad Meyer     return chmod(filename, permissions);
16037f1f268SConrad Meyer }
16137f1f268SConrad Meyer 
162*5ff13fbcSAllan Jude /* set access and modification times */
UTIL_utime(const char * filename,const stat_t * statbuf)163*5ff13fbcSAllan Jude int UTIL_utime(const char* filename, const stat_t *statbuf)
164*5ff13fbcSAllan Jude {
165*5ff13fbcSAllan Jude     int ret;
166*5ff13fbcSAllan Jude     /* We check that st_mtime is a macro here in order to give us confidence
167*5ff13fbcSAllan Jude      * that struct stat has a struct timespec st_mtim member. We need this
168*5ff13fbcSAllan Jude      * check because there are some platforms that claim to be POSIX 2008
169*5ff13fbcSAllan Jude      * compliant but which do not have st_mtim... */
170*5ff13fbcSAllan Jude #if (PLATFORM_POSIX_VERSION >= 200809L) && defined(st_mtime)
171*5ff13fbcSAllan Jude     /* (atime, mtime) */
172*5ff13fbcSAllan Jude     struct timespec timebuf[2] = { {0, UTIME_NOW} };
173*5ff13fbcSAllan Jude     timebuf[1] = statbuf->st_mtim;
174*5ff13fbcSAllan Jude     ret = utimensat(AT_FDCWD, filename, timebuf, 0);
175*5ff13fbcSAllan Jude #else
176*5ff13fbcSAllan Jude     struct utimbuf timebuf;
177*5ff13fbcSAllan Jude     timebuf.actime = time(NULL);
178*5ff13fbcSAllan Jude     timebuf.modtime = statbuf->st_mtime;
179*5ff13fbcSAllan Jude     ret = utime(filename, &timebuf);
180*5ff13fbcSAllan Jude #endif
181*5ff13fbcSAllan Jude     errno = 0;
182*5ff13fbcSAllan Jude     return ret;
183*5ff13fbcSAllan Jude }
184*5ff13fbcSAllan Jude 
UTIL_setFileStat(const char * filename,const stat_t * statbuf)185f7cd7fe5SConrad Meyer int UTIL_setFileStat(const char *filename, const stat_t *statbuf)
186a0483764SConrad Meyer {
187a0483764SConrad Meyer     int res = 0;
188a0483764SConrad Meyer 
189f7cd7fe5SConrad Meyer     stat_t curStatBuf;
190f7cd7fe5SConrad Meyer     if (!UTIL_stat(filename, &curStatBuf) || !UTIL_isRegularFileStat(&curStatBuf))
191a0483764SConrad Meyer         return -1;
192a0483764SConrad Meyer 
1939cbefe25SConrad Meyer     /* set access and modification times */
194*5ff13fbcSAllan Jude     res += UTIL_utime(filename, statbuf);
195a0483764SConrad Meyer 
196a0483764SConrad Meyer #if !defined(_WIN32)
197a0483764SConrad Meyer     res += chown(filename, statbuf->st_uid, statbuf->st_gid);  /* Copy ownership */
198a0483764SConrad Meyer #endif
199a0483764SConrad Meyer 
200f7cd7fe5SConrad Meyer     res += UTIL_chmod(filename, &curStatBuf, statbuf->st_mode & 07777);  /* Copy file permissions */
201a0483764SConrad Meyer 
202a0483764SConrad Meyer     errno = 0;
203a0483764SConrad Meyer     return -res; /* number of errors is returned */
204a0483764SConrad Meyer }
205a0483764SConrad Meyer 
UTIL_isDirectory(const char * infilename)20637f1f268SConrad Meyer int UTIL_isDirectory(const char* infilename)
207a0483764SConrad Meyer {
208a0483764SConrad Meyer     stat_t statbuf;
209f7cd7fe5SConrad Meyer     return UTIL_stat(infilename, &statbuf) && UTIL_isDirectoryStat(&statbuf);
210f7cd7fe5SConrad Meyer }
211f7cd7fe5SConrad Meyer 
UTIL_isDirectoryStat(const stat_t * statbuf)212f7cd7fe5SConrad Meyer int UTIL_isDirectoryStat(const stat_t* statbuf)
213f7cd7fe5SConrad Meyer {
214a0483764SConrad Meyer #if defined(_MSC_VER)
215f7cd7fe5SConrad Meyer     return (statbuf->st_mode & _S_IFDIR) != 0;
216a0483764SConrad Meyer #else
217f7cd7fe5SConrad Meyer     return S_ISDIR(statbuf->st_mode) != 0;
218a0483764SConrad Meyer #endif
219a0483764SConrad Meyer }
220a0483764SConrad Meyer 
UTIL_compareStr(const void * p1,const void * p2)2219cbefe25SConrad Meyer int UTIL_compareStr(const void *p1, const void *p2) {
2229cbefe25SConrad Meyer     return strcmp(* (char * const *) p1, * (char * const *) p2);
2239cbefe25SConrad Meyer }
2249cbefe25SConrad Meyer 
UTIL_isSameFile(const char * fName1,const char * fName2)2259cbefe25SConrad Meyer int UTIL_isSameFile(const char* fName1, const char* fName2)
2262b9c00cbSConrad Meyer {
2279cbefe25SConrad Meyer     assert(fName1 != NULL); assert(fName2 != NULL);
2289cbefe25SConrad Meyer #if defined(_MSC_VER) || defined(_WIN32)
2292b9c00cbSConrad Meyer     /* note : Visual does not support file identification by inode.
2309cbefe25SConrad Meyer      *        inode does not work on Windows, even with a posix layer, like msys2.
2312b9c00cbSConrad Meyer      *        The following work-around is limited to detecting exact name repetition only,
2322b9c00cbSConrad Meyer      *        aka `filename` is considered different from `subdir/../filename` */
2339cbefe25SConrad Meyer     return !strcmp(fName1, fName2);
2342b9c00cbSConrad Meyer #else
2359cbefe25SConrad Meyer     {   stat_t file1Stat;
2362b9c00cbSConrad Meyer         stat_t file2Stat;
237f7cd7fe5SConrad Meyer         return UTIL_stat(fName1, &file1Stat)
238f7cd7fe5SConrad Meyer             && UTIL_stat(fName2, &file2Stat)
2392b9c00cbSConrad Meyer             && (file1Stat.st_dev == file2Stat.st_dev)
2402b9c00cbSConrad Meyer             && (file1Stat.st_ino == file2Stat.st_ino);
2419cbefe25SConrad Meyer     }
2422b9c00cbSConrad Meyer #endif
2432b9c00cbSConrad Meyer }
2442b9c00cbSConrad Meyer 
24537f1f268SConrad Meyer /* UTIL_isFIFO : distinguish named pipes */
UTIL_isFIFO(const char * infilename)24637f1f268SConrad Meyer int UTIL_isFIFO(const char* infilename)
2479cbefe25SConrad Meyer {
2489cbefe25SConrad Meyer /* macro guards, as defined in : https://linux.die.net/man/2/lstat */
2499cbefe25SConrad Meyer #if PLATFORM_POSIX_VERSION >= 200112L
2509cbefe25SConrad Meyer     stat_t statbuf;
251f7cd7fe5SConrad Meyer     if (UTIL_stat(infilename, &statbuf) && UTIL_isFIFOStat(&statbuf)) return 1;
2529cbefe25SConrad Meyer #endif
2539cbefe25SConrad Meyer     (void)infilename;
2549cbefe25SConrad Meyer     return 0;
2559cbefe25SConrad Meyer }
2569cbefe25SConrad Meyer 
257f7cd7fe5SConrad Meyer /* UTIL_isFIFO : distinguish named pipes */
UTIL_isFIFOStat(const stat_t * statbuf)258f7cd7fe5SConrad Meyer int UTIL_isFIFOStat(const stat_t* statbuf)
259f7cd7fe5SConrad Meyer {
260f7cd7fe5SConrad Meyer /* macro guards, as defined in : https://linux.die.net/man/2/lstat */
261f7cd7fe5SConrad Meyer #if PLATFORM_POSIX_VERSION >= 200112L
262f7cd7fe5SConrad Meyer     if (S_ISFIFO(statbuf->st_mode)) return 1;
263f7cd7fe5SConrad Meyer #endif
264f7cd7fe5SConrad Meyer     (void)statbuf;
265f7cd7fe5SConrad Meyer     return 0;
266f7cd7fe5SConrad Meyer }
267f7cd7fe5SConrad Meyer 
268*5ff13fbcSAllan Jude /* UTIL_isBlockDevStat : distinguish named pipes */
UTIL_isBlockDevStat(const stat_t * statbuf)269*5ff13fbcSAllan Jude int UTIL_isBlockDevStat(const stat_t* statbuf)
270*5ff13fbcSAllan Jude {
271*5ff13fbcSAllan Jude /* macro guards, as defined in : https://linux.die.net/man/2/lstat */
272*5ff13fbcSAllan Jude #if PLATFORM_POSIX_VERSION >= 200112L
273*5ff13fbcSAllan Jude     if (S_ISBLK(statbuf->st_mode)) return 1;
274*5ff13fbcSAllan Jude #endif
275*5ff13fbcSAllan Jude     (void)statbuf;
276*5ff13fbcSAllan Jude     return 0;
277*5ff13fbcSAllan Jude }
278*5ff13fbcSAllan Jude 
UTIL_isLink(const char * infilename)27937f1f268SConrad Meyer int UTIL_isLink(const char* infilename)
280a0483764SConrad Meyer {
281a0483764SConrad Meyer /* macro guards, as defined in : https://linux.die.net/man/2/lstat */
2824d3f1eafSConrad Meyer #if PLATFORM_POSIX_VERSION >= 200112L
283a0483764SConrad Meyer     stat_t statbuf;
28437f1f268SConrad Meyer     int const r = lstat(infilename, &statbuf);
285a0483764SConrad Meyer     if (!r && S_ISLNK(statbuf.st_mode)) return 1;
286a0483764SConrad Meyer #endif
287a0483764SConrad Meyer     (void)infilename;
288a0483764SConrad Meyer     return 0;
289a0483764SConrad Meyer }
290a0483764SConrad Meyer 
UTIL_getFileSize(const char * infilename)291a0483764SConrad Meyer U64 UTIL_getFileSize(const char* infilename)
292a0483764SConrad Meyer {
293f7cd7fe5SConrad Meyer     stat_t statbuf;
294f7cd7fe5SConrad Meyer     if (!UTIL_stat(infilename, &statbuf)) return UTIL_FILESIZE_UNKNOWN;
295f7cd7fe5SConrad Meyer     return UTIL_getFileSizeStat(&statbuf);
296a0483764SConrad Meyer }
297f7cd7fe5SConrad Meyer 
UTIL_getFileSizeStat(const stat_t * statbuf)298f7cd7fe5SConrad Meyer U64 UTIL_getFileSizeStat(const stat_t* statbuf)
299f7cd7fe5SConrad Meyer {
300f7cd7fe5SConrad Meyer     if (!UTIL_isRegularFileStat(statbuf)) return UTIL_FILESIZE_UNKNOWN;
301f7cd7fe5SConrad Meyer #if defined(_MSC_VER)
302f7cd7fe5SConrad Meyer     if (!(statbuf->st_mode & S_IFREG)) return UTIL_FILESIZE_UNKNOWN;
303f7cd7fe5SConrad Meyer #elif defined(__MINGW32__) && defined (__MSVCRT__)
304f7cd7fe5SConrad Meyer     if (!(statbuf->st_mode & S_IFREG)) return UTIL_FILESIZE_UNKNOWN;
305f7cd7fe5SConrad Meyer #else
306f7cd7fe5SConrad Meyer     if (!S_ISREG(statbuf->st_mode)) return UTIL_FILESIZE_UNKNOWN;
307f7cd7fe5SConrad Meyer #endif
308f7cd7fe5SConrad Meyer     return (U64)statbuf->st_size;
309a0483764SConrad Meyer }
310a0483764SConrad Meyer 
UTIL_makeHumanReadableSize(U64 size)311*5ff13fbcSAllan Jude UTIL_HumanReadableSize_t UTIL_makeHumanReadableSize(U64 size)
312*5ff13fbcSAllan Jude {
313*5ff13fbcSAllan Jude     UTIL_HumanReadableSize_t hrs;
314*5ff13fbcSAllan Jude 
315*5ff13fbcSAllan Jude     if (g_utilDisplayLevel > 3) {
316*5ff13fbcSAllan Jude         /* In verbose mode, do not scale sizes down, except in the case of
317*5ff13fbcSAllan Jude          * values that exceed the integral precision of a double. */
318*5ff13fbcSAllan Jude         if (size >= (1ull << 53)) {
319*5ff13fbcSAllan Jude             hrs.value = (double)size / (1ull << 20);
320*5ff13fbcSAllan Jude             hrs.suffix = " MiB";
321*5ff13fbcSAllan Jude             /* At worst, a double representation of a maximal size will be
322*5ff13fbcSAllan Jude              * accurate to better than tens of kilobytes. */
323*5ff13fbcSAllan Jude             hrs.precision = 2;
324*5ff13fbcSAllan Jude         } else {
325*5ff13fbcSAllan Jude             hrs.value = (double)size;
326*5ff13fbcSAllan Jude             hrs.suffix = " B";
327*5ff13fbcSAllan Jude             hrs.precision = 0;
328*5ff13fbcSAllan Jude         }
329*5ff13fbcSAllan Jude     } else {
330*5ff13fbcSAllan Jude         /* In regular mode, scale sizes down and use suffixes. */
331*5ff13fbcSAllan Jude         if (size >= (1ull << 60)) {
332*5ff13fbcSAllan Jude             hrs.value = (double)size / (1ull << 60);
333*5ff13fbcSAllan Jude             hrs.suffix = " EiB";
334*5ff13fbcSAllan Jude         } else if (size >= (1ull << 50)) {
335*5ff13fbcSAllan Jude             hrs.value = (double)size / (1ull << 50);
336*5ff13fbcSAllan Jude             hrs.suffix = " PiB";
337*5ff13fbcSAllan Jude         } else if (size >= (1ull << 40)) {
338*5ff13fbcSAllan Jude             hrs.value = (double)size / (1ull << 40);
339*5ff13fbcSAllan Jude             hrs.suffix = " TiB";
340*5ff13fbcSAllan Jude         } else if (size >= (1ull << 30)) {
341*5ff13fbcSAllan Jude             hrs.value = (double)size / (1ull << 30);
342*5ff13fbcSAllan Jude             hrs.suffix = " GiB";
343*5ff13fbcSAllan Jude         } else if (size >= (1ull << 20)) {
344*5ff13fbcSAllan Jude             hrs.value = (double)size / (1ull << 20);
345*5ff13fbcSAllan Jude             hrs.suffix = " MiB";
346*5ff13fbcSAllan Jude         } else if (size >= (1ull << 10)) {
347*5ff13fbcSAllan Jude             hrs.value = (double)size / (1ull << 10);
348*5ff13fbcSAllan Jude             hrs.suffix = " KiB";
349*5ff13fbcSAllan Jude         } else {
350*5ff13fbcSAllan Jude             hrs.value = (double)size;
351*5ff13fbcSAllan Jude             hrs.suffix = " B";
352*5ff13fbcSAllan Jude         }
353*5ff13fbcSAllan Jude 
354*5ff13fbcSAllan Jude         if (hrs.value >= 100 || (U64)hrs.value == size) {
355*5ff13fbcSAllan Jude             hrs.precision = 0;
356*5ff13fbcSAllan Jude         } else if (hrs.value >= 10) {
357*5ff13fbcSAllan Jude             hrs.precision = 1;
358*5ff13fbcSAllan Jude         } else if (hrs.value > 1) {
359*5ff13fbcSAllan Jude             hrs.precision = 2;
360*5ff13fbcSAllan Jude         } else {
361*5ff13fbcSAllan Jude             hrs.precision = 3;
362*5ff13fbcSAllan Jude         }
363*5ff13fbcSAllan Jude     }
364*5ff13fbcSAllan Jude 
365*5ff13fbcSAllan Jude     return hrs;
366*5ff13fbcSAllan Jude }
367a0483764SConrad Meyer 
UTIL_getTotalFileSize(const char * const * fileNamesTable,unsigned nbFiles)36837f1f268SConrad Meyer U64 UTIL_getTotalFileSize(const char* const * fileNamesTable, unsigned nbFiles)
369a0483764SConrad Meyer {
370a0483764SConrad Meyer     U64 total = 0;
371a0483764SConrad Meyer     unsigned n;
372a0483764SConrad Meyer     for (n=0; n<nbFiles; n++) {
373a0483764SConrad Meyer         U64 const size = UTIL_getFileSize(fileNamesTable[n]);
37437f1f268SConrad Meyer         if (size == UTIL_FILESIZE_UNKNOWN) return UTIL_FILESIZE_UNKNOWN;
375a0483764SConrad Meyer         total += size;
376a0483764SConrad Meyer     }
37737f1f268SConrad Meyer     return total;
37837f1f268SConrad Meyer }
37937f1f268SConrad Meyer 
38037f1f268SConrad Meyer 
38137f1f268SConrad Meyer /* condition : @file must be valid, and not have reached its end.
38237f1f268SConrad Meyer  * @return : length of line written into @buf, ended with `\0` instead of '\n',
38337f1f268SConrad Meyer  *           or 0, if there is no new line */
readLineFromFile(char * buf,size_t len,FILE * file)38437f1f268SConrad Meyer static size_t readLineFromFile(char* buf, size_t len, FILE* file)
38537f1f268SConrad Meyer {
38637f1f268SConrad Meyer     assert(!feof(file));
387*5ff13fbcSAllan Jude     if ( fgets(buf, (int) len, file) == NULL ) return 0;
38837f1f268SConrad Meyer     {   size_t linelen = strlen(buf);
38937f1f268SConrad Meyer         if (strlen(buf)==0) return 0;
39037f1f268SConrad Meyer         if (buf[linelen-1] == '\n') linelen--;
39137f1f268SConrad Meyer         buf[linelen] = '\0';
39237f1f268SConrad Meyer         return linelen+1;
39337f1f268SConrad Meyer     }
39437f1f268SConrad Meyer }
39537f1f268SConrad Meyer 
39637f1f268SConrad Meyer /* Conditions :
39737f1f268SConrad Meyer  *   size of @inputFileName file must be < @dstCapacity
39837f1f268SConrad Meyer  *   @dst must be initialized
39937f1f268SConrad Meyer  * @return : nb of lines
40037f1f268SConrad Meyer  *       or -1 if there's an error
40137f1f268SConrad Meyer  */
40237f1f268SConrad Meyer static int
readLinesFromFile(void * dst,size_t dstCapacity,const char * inputFileName)40337f1f268SConrad Meyer readLinesFromFile(void* dst, size_t dstCapacity,
40437f1f268SConrad Meyer             const char* inputFileName)
40537f1f268SConrad Meyer {
40637f1f268SConrad Meyer     int nbFiles = 0;
40737f1f268SConrad Meyer     size_t pos = 0;
40837f1f268SConrad Meyer     char* const buf = (char*)dst;
40937f1f268SConrad Meyer     FILE* const inputFile = fopen(inputFileName, "r");
41037f1f268SConrad Meyer 
41137f1f268SConrad Meyer     assert(dst != NULL);
41237f1f268SConrad Meyer 
41337f1f268SConrad Meyer     if(!inputFile) {
41437f1f268SConrad Meyer         if (g_utilDisplayLevel >= 1) perror("zstd:util:readLinesFromFile");
41537f1f268SConrad Meyer         return -1;
41637f1f268SConrad Meyer     }
41737f1f268SConrad Meyer 
41837f1f268SConrad Meyer     while ( !feof(inputFile) ) {
41937f1f268SConrad Meyer         size_t const lineLength = readLineFromFile(buf+pos, dstCapacity-pos, inputFile);
42037f1f268SConrad Meyer         if (lineLength == 0) break;
42137f1f268SConrad Meyer         assert(pos + lineLength < dstCapacity);
42237f1f268SConrad Meyer         pos += lineLength;
42337f1f268SConrad Meyer         ++nbFiles;
42437f1f268SConrad Meyer     }
42537f1f268SConrad Meyer 
42637f1f268SConrad Meyer     CONTROL( fclose(inputFile) == 0 );
42737f1f268SConrad Meyer 
42837f1f268SConrad Meyer     return nbFiles;
42937f1f268SConrad Meyer }
43037f1f268SConrad Meyer 
43137f1f268SConrad Meyer /*Note: buf is not freed in case function successfully created table because filesTable->fileNames[0] = buf*/
43237f1f268SConrad Meyer FileNamesTable*
UTIL_createFileNamesTable_fromFileName(const char * inputFileName)43337f1f268SConrad Meyer UTIL_createFileNamesTable_fromFileName(const char* inputFileName)
43437f1f268SConrad Meyer {
43537f1f268SConrad Meyer     size_t nbFiles = 0;
43637f1f268SConrad Meyer     char* buf;
43737f1f268SConrad Meyer     size_t bufSize;
43837f1f268SConrad Meyer     size_t pos = 0;
439f7cd7fe5SConrad Meyer     stat_t statbuf;
44037f1f268SConrad Meyer 
441f7cd7fe5SConrad Meyer     if (!UTIL_stat(inputFileName, &statbuf) || !UTIL_isRegularFileStat(&statbuf))
44237f1f268SConrad Meyer         return NULL;
44337f1f268SConrad Meyer 
444f7cd7fe5SConrad Meyer     {   U64 const inputFileSize = UTIL_getFileSizeStat(&statbuf);
44537f1f268SConrad Meyer         if(inputFileSize > MAX_FILE_OF_FILE_NAMES_SIZE)
44637f1f268SConrad Meyer             return NULL;
44737f1f268SConrad Meyer         bufSize = (size_t)(inputFileSize + 1); /* (+1) to add '\0' at the end of last filename */
44837f1f268SConrad Meyer     }
44937f1f268SConrad Meyer 
45037f1f268SConrad Meyer     buf = (char*) malloc(bufSize);
45137f1f268SConrad Meyer     CONTROL( buf != NULL );
45237f1f268SConrad Meyer 
45337f1f268SConrad Meyer     {   int const ret_nbFiles = readLinesFromFile(buf, bufSize, inputFileName);
45437f1f268SConrad Meyer 
45537f1f268SConrad Meyer         if (ret_nbFiles <= 0) {
45637f1f268SConrad Meyer           free(buf);
45737f1f268SConrad Meyer           return NULL;
45837f1f268SConrad Meyer         }
45937f1f268SConrad Meyer         nbFiles = (size_t)ret_nbFiles;
46037f1f268SConrad Meyer     }
46137f1f268SConrad Meyer 
46237f1f268SConrad Meyer     {   const char** filenamesTable = (const char**) malloc(nbFiles * sizeof(*filenamesTable));
46337f1f268SConrad Meyer         CONTROL(filenamesTable != NULL);
46437f1f268SConrad Meyer 
46537f1f268SConrad Meyer         {   size_t fnb;
46637f1f268SConrad Meyer             for (fnb = 0, pos = 0; fnb < nbFiles; fnb++) {
46737f1f268SConrad Meyer                 filenamesTable[fnb] = buf+pos;
46837f1f268SConrad Meyer                 pos += strlen(buf+pos)+1;  /* +1 for the finishing `\0` */
46937f1f268SConrad Meyer         }   }
47037f1f268SConrad Meyer         assert(pos <= bufSize);
47137f1f268SConrad Meyer 
47237f1f268SConrad Meyer         return UTIL_assembleFileNamesTable(filenamesTable, nbFiles, buf);
47337f1f268SConrad Meyer     }
47437f1f268SConrad Meyer }
47537f1f268SConrad Meyer 
47637f1f268SConrad Meyer static FileNamesTable*
UTIL_assembleFileNamesTable2(const char ** filenames,size_t tableSize,size_t tableCapacity,char * buf)47737f1f268SConrad Meyer UTIL_assembleFileNamesTable2(const char** filenames, size_t tableSize, size_t tableCapacity, char* buf)
47837f1f268SConrad Meyer {
47937f1f268SConrad Meyer     FileNamesTable* const table = (FileNamesTable*) malloc(sizeof(*table));
48037f1f268SConrad Meyer     CONTROL(table != NULL);
48137f1f268SConrad Meyer     table->fileNames = filenames;
48237f1f268SConrad Meyer     table->buf = buf;
48337f1f268SConrad Meyer     table->tableSize = tableSize;
48437f1f268SConrad Meyer     table->tableCapacity = tableCapacity;
48537f1f268SConrad Meyer     return table;
48637f1f268SConrad Meyer }
48737f1f268SConrad Meyer 
48837f1f268SConrad Meyer FileNamesTable*
UTIL_assembleFileNamesTable(const char ** filenames,size_t tableSize,char * buf)48937f1f268SConrad Meyer UTIL_assembleFileNamesTable(const char** filenames, size_t tableSize, char* buf)
49037f1f268SConrad Meyer {
49137f1f268SConrad Meyer     return UTIL_assembleFileNamesTable2(filenames, tableSize, tableSize, buf);
49237f1f268SConrad Meyer }
49337f1f268SConrad Meyer 
UTIL_freeFileNamesTable(FileNamesTable * table)49437f1f268SConrad Meyer void UTIL_freeFileNamesTable(FileNamesTable* table)
49537f1f268SConrad Meyer {
49637f1f268SConrad Meyer     if (table==NULL) return;
49737f1f268SConrad Meyer     free((void*)table->fileNames);
49837f1f268SConrad Meyer     free(table->buf);
49937f1f268SConrad Meyer     free(table);
50037f1f268SConrad Meyer }
50137f1f268SConrad Meyer 
UTIL_allocateFileNamesTable(size_t tableSize)50237f1f268SConrad Meyer FileNamesTable* UTIL_allocateFileNamesTable(size_t tableSize)
50337f1f268SConrad Meyer {
50437f1f268SConrad Meyer     const char** const fnTable = (const char**)malloc(tableSize * sizeof(*fnTable));
50537f1f268SConrad Meyer     FileNamesTable* fnt;
50637f1f268SConrad Meyer     if (fnTable==NULL) return NULL;
50737f1f268SConrad Meyer     fnt = UTIL_assembleFileNamesTable(fnTable, tableSize, NULL);
50837f1f268SConrad Meyer     fnt->tableSize = 0;   /* the table is empty */
50937f1f268SConrad Meyer     return fnt;
51037f1f268SConrad Meyer }
51137f1f268SConrad Meyer 
UTIL_refFilename(FileNamesTable * fnt,const char * filename)51237f1f268SConrad Meyer void UTIL_refFilename(FileNamesTable* fnt, const char* filename)
51337f1f268SConrad Meyer {
51437f1f268SConrad Meyer     assert(fnt->tableSize < fnt->tableCapacity);
51537f1f268SConrad Meyer     fnt->fileNames[fnt->tableSize] = filename;
51637f1f268SConrad Meyer     fnt->tableSize++;
51737f1f268SConrad Meyer }
51837f1f268SConrad Meyer 
getTotalTableSize(FileNamesTable * table)51937f1f268SConrad Meyer static size_t getTotalTableSize(FileNamesTable* table)
52037f1f268SConrad Meyer {
52137f1f268SConrad Meyer     size_t fnb = 0, totalSize = 0;
52237f1f268SConrad Meyer     for(fnb = 0 ; fnb < table->tableSize && table->fileNames[fnb] ; ++fnb) {
52337f1f268SConrad Meyer         totalSize += strlen(table->fileNames[fnb]) + 1; /* +1 to add '\0' at the end of each fileName */
52437f1f268SConrad Meyer     }
52537f1f268SConrad Meyer     return totalSize;
52637f1f268SConrad Meyer }
52737f1f268SConrad Meyer 
52837f1f268SConrad Meyer FileNamesTable*
UTIL_mergeFileNamesTable(FileNamesTable * table1,FileNamesTable * table2)52937f1f268SConrad Meyer UTIL_mergeFileNamesTable(FileNamesTable* table1, FileNamesTable* table2)
53037f1f268SConrad Meyer {
53137f1f268SConrad Meyer     unsigned newTableIdx = 0;
53237f1f268SConrad Meyer     size_t pos = 0;
53337f1f268SConrad Meyer     size_t newTotalTableSize;
53437f1f268SConrad Meyer     char* buf;
53537f1f268SConrad Meyer 
53637f1f268SConrad Meyer     FileNamesTable* const newTable = UTIL_assembleFileNamesTable(NULL, 0, NULL);
53737f1f268SConrad Meyer     CONTROL( newTable != NULL );
53837f1f268SConrad Meyer 
53937f1f268SConrad Meyer     newTotalTableSize = getTotalTableSize(table1) + getTotalTableSize(table2);
54037f1f268SConrad Meyer 
54137f1f268SConrad Meyer     buf = (char*) calloc(newTotalTableSize, sizeof(*buf));
54237f1f268SConrad Meyer     CONTROL ( buf != NULL );
54337f1f268SConrad Meyer 
54437f1f268SConrad Meyer     newTable->buf = buf;
54537f1f268SConrad Meyer     newTable->tableSize = table1->tableSize + table2->tableSize;
54637f1f268SConrad Meyer     newTable->fileNames = (const char **) calloc(newTable->tableSize, sizeof(*(newTable->fileNames)));
54737f1f268SConrad Meyer     CONTROL ( newTable->fileNames != NULL );
54837f1f268SConrad Meyer 
54937f1f268SConrad Meyer     {   unsigned idx1;
55037f1f268SConrad Meyer         for( idx1=0 ; (idx1 < table1->tableSize) && table1->fileNames[idx1] && (pos < newTotalTableSize); ++idx1, ++newTableIdx) {
55137f1f268SConrad Meyer             size_t const curLen = strlen(table1->fileNames[idx1]);
55237f1f268SConrad Meyer             memcpy(buf+pos, table1->fileNames[idx1], curLen);
55337f1f268SConrad Meyer             assert(newTableIdx <= newTable->tableSize);
55437f1f268SConrad Meyer             newTable->fileNames[newTableIdx] = buf+pos;
55537f1f268SConrad Meyer             pos += curLen+1;
55637f1f268SConrad Meyer     }   }
55737f1f268SConrad Meyer 
55837f1f268SConrad Meyer     {   unsigned idx2;
55937f1f268SConrad Meyer         for( idx2=0 ; (idx2 < table2->tableSize) && table2->fileNames[idx2] && (pos < newTotalTableSize) ; ++idx2, ++newTableIdx) {
56037f1f268SConrad Meyer             size_t const curLen = strlen(table2->fileNames[idx2]);
56137f1f268SConrad Meyer             memcpy(buf+pos, table2->fileNames[idx2], curLen);
56237f1f268SConrad Meyer             assert(newTableIdx <= newTable->tableSize);
56337f1f268SConrad Meyer             newTable->fileNames[newTableIdx] = buf+pos;
56437f1f268SConrad Meyer             pos += curLen+1;
56537f1f268SConrad Meyer     }   }
56637f1f268SConrad Meyer     assert(pos <= newTotalTableSize);
56737f1f268SConrad Meyer     newTable->tableSize = newTableIdx;
56837f1f268SConrad Meyer 
56937f1f268SConrad Meyer     UTIL_freeFileNamesTable(table1);
57037f1f268SConrad Meyer     UTIL_freeFileNamesTable(table2);
57137f1f268SConrad Meyer 
57237f1f268SConrad Meyer     return newTable;
573a0483764SConrad Meyer }
574a0483764SConrad Meyer 
575a0483764SConrad Meyer #ifdef _WIN32
UTIL_prepareFileList(const char * dirName,char ** bufStart,size_t * pos,char ** bufEnd,int followLinks)57637f1f268SConrad Meyer static int UTIL_prepareFileList(const char* dirName,
57737f1f268SConrad Meyer                                 char** bufStart, size_t* pos,
57837f1f268SConrad Meyer                                 char** bufEnd, int followLinks)
579a0483764SConrad Meyer {
580a0483764SConrad Meyer     char* path;
58137f1f268SConrad Meyer     size_t dirLength, pathLength;
58237f1f268SConrad Meyer     int nbFiles = 0;
583a0483764SConrad Meyer     WIN32_FIND_DATAA cFile;
584a0483764SConrad Meyer     HANDLE hFile;
585a0483764SConrad Meyer 
58637f1f268SConrad Meyer     dirLength = strlen(dirName);
587a0483764SConrad Meyer     path = (char*) malloc(dirLength + 3);
588a0483764SConrad Meyer     if (!path) return 0;
589a0483764SConrad Meyer 
590a0483764SConrad Meyer     memcpy(path, dirName, dirLength);
591a0483764SConrad Meyer     path[dirLength] = '\\';
592a0483764SConrad Meyer     path[dirLength+1] = '*';
593a0483764SConrad Meyer     path[dirLength+2] = 0;
594a0483764SConrad Meyer 
595a0483764SConrad Meyer     hFile=FindFirstFileA(path, &cFile);
596a0483764SConrad Meyer     if (hFile == INVALID_HANDLE_VALUE) {
597a0483764SConrad Meyer         UTIL_DISPLAYLEVEL(1, "Cannot open directory '%s'\n", dirName);
598a0483764SConrad Meyer         return 0;
599a0483764SConrad Meyer     }
600a0483764SConrad Meyer     free(path);
601a0483764SConrad Meyer 
602a0483764SConrad Meyer     do {
60337f1f268SConrad Meyer         size_t const fnameLength = strlen(cFile.cFileName);
604a0483764SConrad Meyer         path = (char*) malloc(dirLength + fnameLength + 2);
605a0483764SConrad Meyer         if (!path) { FindClose(hFile); return 0; }
606a0483764SConrad Meyer         memcpy(path, dirName, dirLength);
607a0483764SConrad Meyer         path[dirLength] = '\\';
608a0483764SConrad Meyer         memcpy(path+dirLength+1, cFile.cFileName, fnameLength);
609a0483764SConrad Meyer         pathLength = dirLength+1+fnameLength;
610a0483764SConrad Meyer         path[pathLength] = 0;
611a0483764SConrad Meyer         if (cFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
612a0483764SConrad Meyer             if ( strcmp (cFile.cFileName, "..") == 0
613a0483764SConrad Meyer               || strcmp (cFile.cFileName, ".") == 0 )
614a0483764SConrad Meyer                 continue;
615a0483764SConrad Meyer             /* Recursively call "UTIL_prepareFileList" with the new path. */
616a0483764SConrad Meyer             nbFiles += UTIL_prepareFileList(path, bufStart, pos, bufEnd, followLinks);
617a0483764SConrad Meyer             if (*bufStart == NULL) { free(path); FindClose(hFile); return 0; }
618a0483764SConrad Meyer         } else if ( (cFile.dwFileAttributes & FILE_ATTRIBUTE_NORMAL)
619a0483764SConrad Meyer                  || (cFile.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
620a0483764SConrad Meyer                  || (cFile.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) ) {
621a0483764SConrad Meyer             if (*bufStart + *pos + pathLength >= *bufEnd) {
622a0483764SConrad Meyer                 ptrdiff_t const newListSize = (*bufEnd - *bufStart) + LIST_SIZE_INCREASE;
623a0483764SConrad Meyer                 *bufStart = (char*)UTIL_realloc(*bufStart, newListSize);
624a0483764SConrad Meyer                 if (*bufStart == NULL) { free(path); FindClose(hFile); return 0; }
625a0483764SConrad Meyer                 *bufEnd = *bufStart + newListSize;
626a0483764SConrad Meyer             }
627a0483764SConrad Meyer             if (*bufStart + *pos + pathLength < *bufEnd) {
628a0483764SConrad Meyer                 memcpy(*bufStart + *pos, path, pathLength+1 /* include final \0 */);
629a0483764SConrad Meyer                 *pos += pathLength + 1;
630a0483764SConrad Meyer                 nbFiles++;
63137f1f268SConrad Meyer         }   }
632a0483764SConrad Meyer         free(path);
633a0483764SConrad Meyer     } while (FindNextFileA(hFile, &cFile));
634a0483764SConrad Meyer 
635a0483764SConrad Meyer     FindClose(hFile);
636a0483764SConrad Meyer     return nbFiles;
637a0483764SConrad Meyer }
638a0483764SConrad Meyer 
639a0483764SConrad Meyer #elif defined(__linux__) || (PLATFORM_POSIX_VERSION >= 200112L)  /* opendir, readdir require POSIX.1-2001 */
640a0483764SConrad Meyer 
UTIL_prepareFileList(const char * dirName,char ** bufStart,size_t * pos,char ** bufEnd,int followLinks)64137f1f268SConrad Meyer static int UTIL_prepareFileList(const char *dirName,
64237f1f268SConrad Meyer                                 char** bufStart, size_t* pos,
64337f1f268SConrad Meyer                                 char** bufEnd, int followLinks)
644a0483764SConrad Meyer {
645a0483764SConrad Meyer     DIR* dir;
646a0483764SConrad Meyer     struct dirent * entry;
64737f1f268SConrad Meyer     size_t dirLength;
6489cbefe25SConrad Meyer     int nbFiles = 0;
649a0483764SConrad Meyer 
650a0483764SConrad Meyer     if (!(dir = opendir(dirName))) {
651a0483764SConrad Meyer         UTIL_DISPLAYLEVEL(1, "Cannot open directory '%s': %s\n", dirName, strerror(errno));
652a0483764SConrad Meyer         return 0;
653a0483764SConrad Meyer     }
654a0483764SConrad Meyer 
6559cbefe25SConrad Meyer     dirLength = strlen(dirName);
656a0483764SConrad Meyer     errno = 0;
657a0483764SConrad Meyer     while ((entry = readdir(dir)) != NULL) {
65837f1f268SConrad Meyer         char* path;
65937f1f268SConrad Meyer         size_t fnameLength, pathLength;
660a0483764SConrad Meyer         if (strcmp (entry->d_name, "..") == 0 ||
661a0483764SConrad Meyer             strcmp (entry->d_name, ".") == 0) continue;
6629cbefe25SConrad Meyer         fnameLength = strlen(entry->d_name);
663a0483764SConrad Meyer         path = (char*) malloc(dirLength + fnameLength + 2);
664a0483764SConrad Meyer         if (!path) { closedir(dir); return 0; }
665a0483764SConrad Meyer         memcpy(path, dirName, dirLength);
666a0483764SConrad Meyer 
667a0483764SConrad Meyer         path[dirLength] = '/';
668a0483764SConrad Meyer         memcpy(path+dirLength+1, entry->d_name, fnameLength);
669a0483764SConrad Meyer         pathLength = dirLength+1+fnameLength;
670a0483764SConrad Meyer         path[pathLength] = 0;
671a0483764SConrad Meyer 
672a0483764SConrad Meyer         if (!followLinks && UTIL_isLink(path)) {
673a0483764SConrad Meyer             UTIL_DISPLAYLEVEL(2, "Warning : %s is a symbolic link, ignoring\n", path);
6744d3f1eafSConrad Meyer             free(path);
675a0483764SConrad Meyer             continue;
676a0483764SConrad Meyer         }
677a0483764SConrad Meyer 
678a0483764SConrad Meyer         if (UTIL_isDirectory(path)) {
679a0483764SConrad Meyer             nbFiles += UTIL_prepareFileList(path, bufStart, pos, bufEnd, followLinks);  /* Recursively call "UTIL_prepareFileList" with the new path. */
680a0483764SConrad Meyer             if (*bufStart == NULL) { free(path); closedir(dir); return 0; }
681a0483764SConrad Meyer         } else {
682a0483764SConrad Meyer             if (*bufStart + *pos + pathLength >= *bufEnd) {
683a0483764SConrad Meyer                 ptrdiff_t newListSize = (*bufEnd - *bufStart) + LIST_SIZE_INCREASE;
6849cbefe25SConrad Meyer                 assert(newListSize >= 0);
6859cbefe25SConrad Meyer                 *bufStart = (char*)UTIL_realloc(*bufStart, (size_t)newListSize);
686a0483764SConrad Meyer                 *bufEnd = *bufStart + newListSize;
687a0483764SConrad Meyer                 if (*bufStart == NULL) { free(path); closedir(dir); return 0; }
688a0483764SConrad Meyer             }
689a0483764SConrad Meyer             if (*bufStart + *pos + pathLength < *bufEnd) {
690a0483764SConrad Meyer                 memcpy(*bufStart + *pos, path, pathLength + 1);  /* with final \0 */
691a0483764SConrad Meyer                 *pos += pathLength + 1;
692a0483764SConrad Meyer                 nbFiles++;
69337f1f268SConrad Meyer         }   }
694a0483764SConrad Meyer         free(path);
695a0483764SConrad Meyer         errno = 0; /* clear errno after UTIL_isDirectory, UTIL_prepareFileList */
696a0483764SConrad Meyer     }
697a0483764SConrad Meyer 
698a0483764SConrad Meyer     if (errno != 0) {
699a0483764SConrad Meyer         UTIL_DISPLAYLEVEL(1, "readdir(%s) error: %s \n", dirName, strerror(errno));
700a0483764SConrad Meyer         free(*bufStart);
701a0483764SConrad Meyer         *bufStart = NULL;
702a0483764SConrad Meyer     }
703a0483764SConrad Meyer     closedir(dir);
704a0483764SConrad Meyer     return nbFiles;
705a0483764SConrad Meyer }
706a0483764SConrad Meyer 
707a0483764SConrad Meyer #else
708a0483764SConrad Meyer 
UTIL_prepareFileList(const char * dirName,char ** bufStart,size_t * pos,char ** bufEnd,int followLinks)70937f1f268SConrad Meyer static int UTIL_prepareFileList(const char *dirName,
71037f1f268SConrad Meyer                                 char** bufStart, size_t* pos,
71137f1f268SConrad Meyer                                 char** bufEnd, int followLinks)
712a0483764SConrad Meyer {
713a0483764SConrad Meyer     (void)bufStart; (void)bufEnd; (void)pos; (void)followLinks;
714a0483764SConrad Meyer     UTIL_DISPLAYLEVEL(1, "Directory %s ignored (compiled without _WIN32 or _POSIX_C_SOURCE) \n", dirName);
715a0483764SConrad Meyer     return 0;
716a0483764SConrad Meyer }
717a0483764SConrad Meyer 
718a0483764SConrad Meyer #endif /* #ifdef _WIN32 */
719a0483764SConrad Meyer 
UTIL_isCompressedFile(const char * inputName,const char * extensionList[])7209cbefe25SConrad Meyer int UTIL_isCompressedFile(const char *inputName, const char *extensionList[])
7219cbefe25SConrad Meyer {
7229cbefe25SConrad Meyer   const char* ext = UTIL_getFileExtension(inputName);
7239cbefe25SConrad Meyer   while(*extensionList!=NULL)
7249cbefe25SConrad Meyer   {
7259cbefe25SConrad Meyer     const int isCompressedExtension = strcmp(ext,*extensionList);
7269cbefe25SConrad Meyer     if(isCompressedExtension==0)
7279cbefe25SConrad Meyer       return 1;
7289cbefe25SConrad Meyer     ++extensionList;
7299cbefe25SConrad Meyer   }
7309cbefe25SConrad Meyer    return 0;
7319cbefe25SConrad Meyer }
7329cbefe25SConrad Meyer 
7339cbefe25SConrad Meyer /*Utility function to get file extension from file */
UTIL_getFileExtension(const char * infilename)7349cbefe25SConrad Meyer const char* UTIL_getFileExtension(const char* infilename)
7359cbefe25SConrad Meyer {
7369cbefe25SConrad Meyer    const char* extension = strrchr(infilename, '.');
7379cbefe25SConrad Meyer    if(!extension || extension==infilename) return "";
7389cbefe25SConrad Meyer    return extension;
7399cbefe25SConrad Meyer }
7409cbefe25SConrad Meyer 
pathnameHas2Dots(const char * pathname)741f7cd7fe5SConrad Meyer static int pathnameHas2Dots(const char *pathname)
742f7cd7fe5SConrad Meyer {
743*5ff13fbcSAllan Jude     /* We need to figure out whether any ".." present in the path is a whole
744*5ff13fbcSAllan Jude      * path token, which is the case if it is bordered on both sides by either
745*5ff13fbcSAllan Jude      * the beginning/end of the path or by a directory separator.
746*5ff13fbcSAllan Jude      */
747*5ff13fbcSAllan Jude     const char *needle = pathname;
748*5ff13fbcSAllan Jude     while (1) {
749*5ff13fbcSAllan Jude         needle = strstr(needle, "..");
750*5ff13fbcSAllan Jude 
751*5ff13fbcSAllan Jude         if (needle == NULL) {
752*5ff13fbcSAllan Jude             return 0;
753*5ff13fbcSAllan Jude         }
754*5ff13fbcSAllan Jude 
755*5ff13fbcSAllan Jude         if ((needle == pathname || needle[-1] == PATH_SEP)
756*5ff13fbcSAllan Jude          && (needle[2] == '\0' || needle[2] == PATH_SEP)) {
757*5ff13fbcSAllan Jude             return 1;
758*5ff13fbcSAllan Jude         }
759*5ff13fbcSAllan Jude 
760*5ff13fbcSAllan Jude         /* increment so we search for the next match */
761*5ff13fbcSAllan Jude         needle++;
762*5ff13fbcSAllan Jude     };
763*5ff13fbcSAllan Jude     return 0;
764f7cd7fe5SConrad Meyer }
765f7cd7fe5SConrad Meyer 
isFileNameValidForMirroredOutput(const char * filename)766f7cd7fe5SConrad Meyer static int isFileNameValidForMirroredOutput(const char *filename)
767f7cd7fe5SConrad Meyer {
768f7cd7fe5SConrad Meyer     return !pathnameHas2Dots(filename);
769f7cd7fe5SConrad Meyer }
770f7cd7fe5SConrad Meyer 
771f7cd7fe5SConrad Meyer 
772f7cd7fe5SConrad Meyer #define DIR_DEFAULT_MODE 0755
getDirMode(const char * dirName)773f7cd7fe5SConrad Meyer static mode_t getDirMode(const char *dirName)
774f7cd7fe5SConrad Meyer {
775f7cd7fe5SConrad Meyer     stat_t st;
776f7cd7fe5SConrad Meyer     if (!UTIL_stat(dirName, &st)) {
777f7cd7fe5SConrad Meyer         UTIL_DISPLAY("zstd: failed to get DIR stats %s: %s\n", dirName, strerror(errno));
778f7cd7fe5SConrad Meyer         return DIR_DEFAULT_MODE;
779f7cd7fe5SConrad Meyer     }
780f7cd7fe5SConrad Meyer     if (!UTIL_isDirectoryStat(&st)) {
781f7cd7fe5SConrad Meyer         UTIL_DISPLAY("zstd: expected directory: %s\n", dirName);
782f7cd7fe5SConrad Meyer         return DIR_DEFAULT_MODE;
783f7cd7fe5SConrad Meyer     }
784f7cd7fe5SConrad Meyer     return st.st_mode;
785f7cd7fe5SConrad Meyer }
786f7cd7fe5SConrad Meyer 
makeDir(const char * dir,mode_t mode)787f7cd7fe5SConrad Meyer static int makeDir(const char *dir, mode_t mode)
788f7cd7fe5SConrad Meyer {
789f7cd7fe5SConrad Meyer #if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__)
790f7cd7fe5SConrad Meyer     int ret = _mkdir(dir);
791f7cd7fe5SConrad Meyer     (void) mode;
792f7cd7fe5SConrad Meyer #else
793f7cd7fe5SConrad Meyer     int ret = mkdir(dir, mode);
794f7cd7fe5SConrad Meyer #endif
795f7cd7fe5SConrad Meyer     if (ret != 0) {
796f7cd7fe5SConrad Meyer         if (errno == EEXIST)
797f7cd7fe5SConrad Meyer             return 0;
798f7cd7fe5SConrad Meyer         UTIL_DISPLAY("zstd: failed to create DIR %s: %s\n", dir, strerror(errno));
799f7cd7fe5SConrad Meyer     }
800f7cd7fe5SConrad Meyer     return ret;
801f7cd7fe5SConrad Meyer }
802f7cd7fe5SConrad Meyer 
803f7cd7fe5SConrad Meyer /* this function requires a mutable input string */
convertPathnameToDirName(char * pathname)804f7cd7fe5SConrad Meyer static void convertPathnameToDirName(char *pathname)
805f7cd7fe5SConrad Meyer {
806f7cd7fe5SConrad Meyer     size_t len = 0;
807f7cd7fe5SConrad Meyer     char* pos = NULL;
808f7cd7fe5SConrad Meyer     /* get dir name from pathname similar to 'dirname()' */
809f7cd7fe5SConrad Meyer     assert(pathname != NULL);
810f7cd7fe5SConrad Meyer 
811f7cd7fe5SConrad Meyer     /* remove trailing '/' chars */
812f7cd7fe5SConrad Meyer     len = strlen(pathname);
813f7cd7fe5SConrad Meyer     assert(len > 0);
814f7cd7fe5SConrad Meyer     while (pathname[len] == PATH_SEP) {
815f7cd7fe5SConrad Meyer         pathname[len] = '\0';
816f7cd7fe5SConrad Meyer         len--;
817f7cd7fe5SConrad Meyer     }
818f7cd7fe5SConrad Meyer     if (len == 0) return;
819f7cd7fe5SConrad Meyer 
820f7cd7fe5SConrad Meyer     /* if input is a single file, return '.' instead. i.e.
821f7cd7fe5SConrad Meyer      * "xyz/abc/file.txt" => "xyz/abc"
822f7cd7fe5SConrad Meyer        "./file.txt"       => "."
823f7cd7fe5SConrad Meyer        "file.txt"         => "."
824f7cd7fe5SConrad Meyer      */
825f7cd7fe5SConrad Meyer     pos = strrchr(pathname, PATH_SEP);
826f7cd7fe5SConrad Meyer     if (pos == NULL) {
827f7cd7fe5SConrad Meyer         pathname[0] = '.';
828f7cd7fe5SConrad Meyer         pathname[1] = '\0';
829f7cd7fe5SConrad Meyer     } else {
830f7cd7fe5SConrad Meyer         *pos = '\0';
831f7cd7fe5SConrad Meyer     }
832f7cd7fe5SConrad Meyer }
833f7cd7fe5SConrad Meyer 
834f7cd7fe5SConrad Meyer /* pathname must be valid */
trimLeadingRootChar(const char * pathname)835f7cd7fe5SConrad Meyer static const char* trimLeadingRootChar(const char *pathname)
836f7cd7fe5SConrad Meyer {
837f7cd7fe5SConrad Meyer     assert(pathname != NULL);
838f7cd7fe5SConrad Meyer     if (pathname[0] == PATH_SEP)
839f7cd7fe5SConrad Meyer         return pathname + 1;
840f7cd7fe5SConrad Meyer     return pathname;
841f7cd7fe5SConrad Meyer }
842f7cd7fe5SConrad Meyer 
843f7cd7fe5SConrad Meyer /* pathname must be valid */
trimLeadingCurrentDirConst(const char * pathname)844f7cd7fe5SConrad Meyer static const char* trimLeadingCurrentDirConst(const char *pathname)
845f7cd7fe5SConrad Meyer {
846f7cd7fe5SConrad Meyer     assert(pathname != NULL);
847f7cd7fe5SConrad Meyer     if ((pathname[0] == '.') && (pathname[1] == PATH_SEP))
848f7cd7fe5SConrad Meyer         return pathname + 2;
849f7cd7fe5SConrad Meyer     return pathname;
850f7cd7fe5SConrad Meyer }
851f7cd7fe5SConrad Meyer 
852f7cd7fe5SConrad Meyer static char*
trimLeadingCurrentDir(char * pathname)853f7cd7fe5SConrad Meyer trimLeadingCurrentDir(char *pathname)
854f7cd7fe5SConrad Meyer {
855f7cd7fe5SConrad Meyer     /* 'union charunion' can do const-cast without compiler warning */
856f7cd7fe5SConrad Meyer     union charunion {
857f7cd7fe5SConrad Meyer         char *chr;
858f7cd7fe5SConrad Meyer         const char* cchr;
859f7cd7fe5SConrad Meyer     } ptr;
860f7cd7fe5SConrad Meyer     ptr.cchr = trimLeadingCurrentDirConst(pathname);
861f7cd7fe5SConrad Meyer     return ptr.chr;
862f7cd7fe5SConrad Meyer }
863f7cd7fe5SConrad Meyer 
864f7cd7fe5SConrad Meyer /* remove leading './' or '/' chars here */
trimPath(const char * pathname)865f7cd7fe5SConrad Meyer static const char * trimPath(const char *pathname)
866f7cd7fe5SConrad Meyer {
867f7cd7fe5SConrad Meyer     return trimLeadingRootChar(
868f7cd7fe5SConrad Meyer             trimLeadingCurrentDirConst(pathname));
869f7cd7fe5SConrad Meyer }
870f7cd7fe5SConrad Meyer 
mallocAndJoin2Dir(const char * dir1,const char * dir2)871f7cd7fe5SConrad Meyer static char* mallocAndJoin2Dir(const char *dir1, const char *dir2)
872f7cd7fe5SConrad Meyer {
873f7cd7fe5SConrad Meyer     const size_t dir1Size = strlen(dir1);
874f7cd7fe5SConrad Meyer     const size_t dir2Size = strlen(dir2);
875f7cd7fe5SConrad Meyer     char *outDirBuffer, *buffer, trailingChar;
876f7cd7fe5SConrad Meyer 
877f7cd7fe5SConrad Meyer     assert(dir1 != NULL && dir2 != NULL);
878f7cd7fe5SConrad Meyer     outDirBuffer = (char *) malloc(dir1Size + dir2Size + 2);
879f7cd7fe5SConrad Meyer     CONTROL(outDirBuffer != NULL);
880f7cd7fe5SConrad Meyer 
881f7cd7fe5SConrad Meyer     memcpy(outDirBuffer, dir1, dir1Size);
882f7cd7fe5SConrad Meyer     outDirBuffer[dir1Size] = '\0';
883f7cd7fe5SConrad Meyer 
884f7cd7fe5SConrad Meyer     if (dir2[0] == '.')
885f7cd7fe5SConrad Meyer         return outDirBuffer;
886f7cd7fe5SConrad Meyer 
887f7cd7fe5SConrad Meyer     buffer = outDirBuffer + dir1Size;
888f7cd7fe5SConrad Meyer     trailingChar = *(buffer - 1);
889f7cd7fe5SConrad Meyer     if (trailingChar != PATH_SEP) {
890f7cd7fe5SConrad Meyer         *buffer = PATH_SEP;
891f7cd7fe5SConrad Meyer         buffer++;
892f7cd7fe5SConrad Meyer     }
893f7cd7fe5SConrad Meyer     memcpy(buffer, dir2, dir2Size);
894f7cd7fe5SConrad Meyer     buffer[dir2Size] = '\0';
895f7cd7fe5SConrad Meyer 
896f7cd7fe5SConrad Meyer     return outDirBuffer;
897f7cd7fe5SConrad Meyer }
898f7cd7fe5SConrad Meyer 
899f7cd7fe5SConrad Meyer /* this function will return NULL if input srcFileName is not valid name for mirrored output path */
UTIL_createMirroredDestDirName(const char * srcFileName,const char * outDirRootName)900f7cd7fe5SConrad Meyer char* UTIL_createMirroredDestDirName(const char* srcFileName, const char* outDirRootName)
901f7cd7fe5SConrad Meyer {
902f7cd7fe5SConrad Meyer     char* pathname = NULL;
903f7cd7fe5SConrad Meyer     if (!isFileNameValidForMirroredOutput(srcFileName))
904f7cd7fe5SConrad Meyer         return NULL;
905f7cd7fe5SConrad Meyer 
906f7cd7fe5SConrad Meyer     pathname = mallocAndJoin2Dir(outDirRootName, trimPath(srcFileName));
907f7cd7fe5SConrad Meyer 
908f7cd7fe5SConrad Meyer     convertPathnameToDirName(pathname);
909f7cd7fe5SConrad Meyer     return pathname;
910f7cd7fe5SConrad Meyer }
911f7cd7fe5SConrad Meyer 
912f7cd7fe5SConrad Meyer static int
mirrorSrcDir(char * srcDirName,const char * outDirName)913f7cd7fe5SConrad Meyer mirrorSrcDir(char* srcDirName, const char* outDirName)
914f7cd7fe5SConrad Meyer {
915f7cd7fe5SConrad Meyer     mode_t srcMode;
916f7cd7fe5SConrad Meyer     int status = 0;
917f7cd7fe5SConrad Meyer     char* newDir = mallocAndJoin2Dir(outDirName, trimPath(srcDirName));
918f7cd7fe5SConrad Meyer     if (!newDir)
919f7cd7fe5SConrad Meyer         return -ENOMEM;
920f7cd7fe5SConrad Meyer 
921f7cd7fe5SConrad Meyer     srcMode = getDirMode(srcDirName);
922f7cd7fe5SConrad Meyer     status = makeDir(newDir, srcMode);
923f7cd7fe5SConrad Meyer     free(newDir);
924f7cd7fe5SConrad Meyer     return status;
925f7cd7fe5SConrad Meyer }
926f7cd7fe5SConrad Meyer 
927f7cd7fe5SConrad Meyer static int
mirrorSrcDirRecursive(char * srcDirName,const char * outDirName)928f7cd7fe5SConrad Meyer mirrorSrcDirRecursive(char* srcDirName, const char* outDirName)
929f7cd7fe5SConrad Meyer {
930f7cd7fe5SConrad Meyer     int status = 0;
931f7cd7fe5SConrad Meyer     char* pp = trimLeadingCurrentDir(srcDirName);
932f7cd7fe5SConrad Meyer     char* sp = NULL;
933f7cd7fe5SConrad Meyer 
934f7cd7fe5SConrad Meyer     while ((sp = strchr(pp, PATH_SEP)) != NULL) {
935f7cd7fe5SConrad Meyer         if (sp != pp) {
936f7cd7fe5SConrad Meyer             *sp = '\0';
937f7cd7fe5SConrad Meyer             status = mirrorSrcDir(srcDirName, outDirName);
938f7cd7fe5SConrad Meyer             if (status != 0)
939f7cd7fe5SConrad Meyer                 return status;
940f7cd7fe5SConrad Meyer             *sp = PATH_SEP;
941f7cd7fe5SConrad Meyer         }
942f7cd7fe5SConrad Meyer         pp = sp + 1;
943f7cd7fe5SConrad Meyer     }
944f7cd7fe5SConrad Meyer     status = mirrorSrcDir(srcDirName, outDirName);
945f7cd7fe5SConrad Meyer     return status;
946f7cd7fe5SConrad Meyer }
947f7cd7fe5SConrad Meyer 
948f7cd7fe5SConrad Meyer static void
makeMirroredDestDirsWithSameSrcDirMode(char ** srcDirNames,unsigned nbFile,const char * outDirName)949f7cd7fe5SConrad Meyer makeMirroredDestDirsWithSameSrcDirMode(char** srcDirNames, unsigned nbFile, const char* outDirName)
950f7cd7fe5SConrad Meyer {
951f7cd7fe5SConrad Meyer     unsigned int i = 0;
952f7cd7fe5SConrad Meyer     for (i = 0; i < nbFile; i++)
953f7cd7fe5SConrad Meyer         mirrorSrcDirRecursive(srcDirNames[i], outDirName);
954f7cd7fe5SConrad Meyer }
955f7cd7fe5SConrad Meyer 
956f7cd7fe5SConrad Meyer static int
firstIsParentOrSameDirOfSecond(const char * firstDir,const char * secondDir)957f7cd7fe5SConrad Meyer firstIsParentOrSameDirOfSecond(const char* firstDir, const char* secondDir)
958f7cd7fe5SConrad Meyer {
959f7cd7fe5SConrad Meyer     size_t firstDirLen  = strlen(firstDir),
960f7cd7fe5SConrad Meyer            secondDirLen = strlen(secondDir);
961f7cd7fe5SConrad Meyer     return firstDirLen <= secondDirLen &&
962f7cd7fe5SConrad Meyer            (secondDir[firstDirLen] == PATH_SEP || secondDir[firstDirLen] == '\0') &&
963f7cd7fe5SConrad Meyer            0 == strncmp(firstDir, secondDir, firstDirLen);
964f7cd7fe5SConrad Meyer }
965f7cd7fe5SConrad Meyer 
compareDir(const void * pathname1,const void * pathname2)966f7cd7fe5SConrad Meyer static int compareDir(const void* pathname1, const void* pathname2) {
967f7cd7fe5SConrad Meyer     /* sort it after remove the leading '/'  or './'*/
968f7cd7fe5SConrad Meyer     const char* s1 = trimPath(*(char * const *) pathname1);
969f7cd7fe5SConrad Meyer     const char* s2 = trimPath(*(char * const *) pathname2);
970f7cd7fe5SConrad Meyer     return strcmp(s1, s2);
971f7cd7fe5SConrad Meyer }
972f7cd7fe5SConrad Meyer 
973f7cd7fe5SConrad Meyer static void
makeUniqueMirroredDestDirs(char ** srcDirNames,unsigned nbFile,const char * outDirName)974f7cd7fe5SConrad Meyer makeUniqueMirroredDestDirs(char** srcDirNames, unsigned nbFile, const char* outDirName)
975f7cd7fe5SConrad Meyer {
976f7cd7fe5SConrad Meyer     unsigned int i = 0, uniqueDirNr = 0;
977f7cd7fe5SConrad Meyer     char** uniqueDirNames = NULL;
978f7cd7fe5SConrad Meyer 
979f7cd7fe5SConrad Meyer     if (nbFile == 0)
980f7cd7fe5SConrad Meyer         return;
981f7cd7fe5SConrad Meyer 
982f7cd7fe5SConrad Meyer     uniqueDirNames = (char** ) malloc(nbFile * sizeof (char *));
983f7cd7fe5SConrad Meyer     CONTROL(uniqueDirNames != NULL);
984f7cd7fe5SConrad Meyer 
985f7cd7fe5SConrad Meyer     /* if dirs is "a/b/c" and "a/b/c/d", we only need call:
986f7cd7fe5SConrad Meyer      * we just need "a/b/c/d" */
987f7cd7fe5SConrad Meyer     qsort((void *)srcDirNames, nbFile, sizeof(char*), compareDir);
988f7cd7fe5SConrad Meyer 
989f7cd7fe5SConrad Meyer     uniqueDirNr = 1;
990f7cd7fe5SConrad Meyer     uniqueDirNames[uniqueDirNr - 1] = srcDirNames[0];
991f7cd7fe5SConrad Meyer     for (i = 1; i < nbFile; i++) {
992f7cd7fe5SConrad Meyer         char* prevDirName = srcDirNames[i - 1];
993f7cd7fe5SConrad Meyer         char* currDirName = srcDirNames[i];
994f7cd7fe5SConrad Meyer 
995*5ff13fbcSAllan Jude         /* note: we always compare trimmed path, i.e.:
996f7cd7fe5SConrad Meyer          * src dir of "./foo" and "/foo" will be both saved into:
997f7cd7fe5SConrad Meyer          * "outDirName/foo/" */
998f7cd7fe5SConrad Meyer         if (!firstIsParentOrSameDirOfSecond(trimPath(prevDirName),
999f7cd7fe5SConrad Meyer                                             trimPath(currDirName)))
1000f7cd7fe5SConrad Meyer             uniqueDirNr++;
1001f7cd7fe5SConrad Meyer 
1002f7cd7fe5SConrad Meyer         /* we need maintain original src dir name instead of trimmed
1003*5ff13fbcSAllan Jude          * dir, so we can retrieve the original src dir's mode_t */
1004f7cd7fe5SConrad Meyer         uniqueDirNames[uniqueDirNr - 1] = currDirName;
1005f7cd7fe5SConrad Meyer     }
1006f7cd7fe5SConrad Meyer 
1007f7cd7fe5SConrad Meyer     makeMirroredDestDirsWithSameSrcDirMode(uniqueDirNames, uniqueDirNr, outDirName);
1008f7cd7fe5SConrad Meyer 
1009f7cd7fe5SConrad Meyer     free(uniqueDirNames);
1010f7cd7fe5SConrad Meyer }
1011f7cd7fe5SConrad Meyer 
1012f7cd7fe5SConrad Meyer static void
makeMirroredDestDirs(char ** srcFileNames,unsigned nbFile,const char * outDirName)1013f7cd7fe5SConrad Meyer makeMirroredDestDirs(char** srcFileNames, unsigned nbFile, const char* outDirName)
1014f7cd7fe5SConrad Meyer {
1015f7cd7fe5SConrad Meyer     unsigned int i = 0;
1016f7cd7fe5SConrad Meyer     for (i = 0; i < nbFile; ++i)
1017f7cd7fe5SConrad Meyer         convertPathnameToDirName(srcFileNames[i]);
1018f7cd7fe5SConrad Meyer     makeUniqueMirroredDestDirs(srcFileNames, nbFile, outDirName);
1019f7cd7fe5SConrad Meyer }
1020f7cd7fe5SConrad Meyer 
UTIL_mirrorSourceFilesDirectories(const char ** inFileNames,unsigned int nbFile,const char * outDirName)1021f7cd7fe5SConrad Meyer void UTIL_mirrorSourceFilesDirectories(const char** inFileNames, unsigned int nbFile, const char* outDirName)
1022f7cd7fe5SConrad Meyer {
1023f7cd7fe5SConrad Meyer     unsigned int i = 0, validFilenamesNr = 0;
1024f7cd7fe5SConrad Meyer     char** srcFileNames = (char **) malloc(nbFile * sizeof (char *));
1025f7cd7fe5SConrad Meyer     CONTROL(srcFileNames != NULL);
1026f7cd7fe5SConrad Meyer 
1027f7cd7fe5SConrad Meyer     /* check input filenames is valid */
1028f7cd7fe5SConrad Meyer     for (i = 0; i < nbFile; ++i) {
1029f7cd7fe5SConrad Meyer         if (isFileNameValidForMirroredOutput(inFileNames[i])) {
1030f7cd7fe5SConrad Meyer             char* fname = STRDUP(inFileNames[i]);
1031f7cd7fe5SConrad Meyer             CONTROL(fname != NULL);
1032f7cd7fe5SConrad Meyer             srcFileNames[validFilenamesNr++] = fname;
1033f7cd7fe5SConrad Meyer         }
1034f7cd7fe5SConrad Meyer     }
1035f7cd7fe5SConrad Meyer 
1036f7cd7fe5SConrad Meyer     if (validFilenamesNr > 0) {
1037f7cd7fe5SConrad Meyer         makeDir(outDirName, DIR_DEFAULT_MODE);
1038f7cd7fe5SConrad Meyer         makeMirroredDestDirs(srcFileNames, validFilenamesNr, outDirName);
1039f7cd7fe5SConrad Meyer     }
1040f7cd7fe5SConrad Meyer 
1041f7cd7fe5SConrad Meyer     for (i = 0; i < validFilenamesNr; i++)
1042f7cd7fe5SConrad Meyer         free(srcFileNames[i]);
1043f7cd7fe5SConrad Meyer     free(srcFileNames);
1044f7cd7fe5SConrad Meyer }
104537f1f268SConrad Meyer 
104637f1f268SConrad Meyer FileNamesTable*
UTIL_createExpandedFNT(const char * const * inputNames,size_t nbIfns,int followLinks)1047*5ff13fbcSAllan Jude UTIL_createExpandedFNT(const char* const* inputNames, size_t nbIfns, int followLinks)
1048a0483764SConrad Meyer {
104937f1f268SConrad Meyer     unsigned nbFiles;
1050a0483764SConrad Meyer     char* buf = (char*)malloc(LIST_SIZE_INCREASE);
1051a0483764SConrad Meyer     char* bufend = buf + LIST_SIZE_INCREASE;
1052a0483764SConrad Meyer 
1053a0483764SConrad Meyer     if (!buf) return NULL;
1054a0483764SConrad Meyer 
105537f1f268SConrad Meyer     {   size_t ifnNb, pos;
105637f1f268SConrad Meyer         for (ifnNb=0, pos=0, nbFiles=0; ifnNb<nbIfns; ifnNb++) {
105737f1f268SConrad Meyer             if (!UTIL_isDirectory(inputNames[ifnNb])) {
105837f1f268SConrad Meyer                 size_t const len = strlen(inputNames[ifnNb]);
1059a0483764SConrad Meyer                 if (buf + pos + len >= bufend) {
1060a0483764SConrad Meyer                     ptrdiff_t newListSize = (bufend - buf) + LIST_SIZE_INCREASE;
10619cbefe25SConrad Meyer                     assert(newListSize >= 0);
10629cbefe25SConrad Meyer                     buf = (char*)UTIL_realloc(buf, (size_t)newListSize);
1063a0483764SConrad Meyer                     if (!buf) return NULL;
106437f1f268SConrad Meyer                     bufend = buf + newListSize;
1065a0483764SConrad Meyer                 }
1066a0483764SConrad Meyer                 if (buf + pos + len < bufend) {
106737f1f268SConrad Meyer                     memcpy(buf+pos, inputNames[ifnNb], len+1);  /* including final \0 */
1068a0483764SConrad Meyer                     pos += len + 1;
1069a0483764SConrad Meyer                     nbFiles++;
1070a0483764SConrad Meyer                 }
1071a0483764SConrad Meyer             } else {
107237f1f268SConrad Meyer                 nbFiles += (unsigned)UTIL_prepareFileList(inputNames[ifnNb], &buf, &pos, &bufend, followLinks);
1073a0483764SConrad Meyer                 if (buf == NULL) return NULL;
107437f1f268SConrad Meyer     }   }   }
1075a0483764SConrad Meyer 
107637f1f268SConrad Meyer     /* note : even if nbFiles==0, function returns a valid, though empty, FileNamesTable* object */
1077a0483764SConrad Meyer 
107837f1f268SConrad Meyer     {   size_t ifnNb, pos;
107937f1f268SConrad Meyer         size_t const fntCapacity = nbFiles + 1;  /* minimum 1, allows adding one reference, typically stdin */
108037f1f268SConrad Meyer         const char** const fileNamesTable = (const char**)malloc(fntCapacity * sizeof(*fileNamesTable));
108137f1f268SConrad Meyer         if (!fileNamesTable) { free(buf); return NULL; }
1082a0483764SConrad Meyer 
108337f1f268SConrad Meyer         for (ifnNb = 0, pos = 0; ifnNb < nbFiles; ifnNb++) {
108437f1f268SConrad Meyer             fileNamesTable[ifnNb] = buf + pos;
108537f1f268SConrad Meyer             if (buf + pos > bufend) { free(buf); free((void*)fileNamesTable); return NULL; }
108637f1f268SConrad Meyer             pos += strlen(fileNamesTable[ifnNb]) + 1;
1087a0483764SConrad Meyer         }
108837f1f268SConrad Meyer         return UTIL_assembleFileNamesTable2(fileNamesTable, nbFiles, fntCapacity, buf);
1089a0483764SConrad Meyer     }
10909cbefe25SConrad Meyer }
1091a0483764SConrad Meyer 
10922b9c00cbSConrad Meyer 
UTIL_expandFNT(FileNamesTable ** fnt,int followLinks)109337f1f268SConrad Meyer void UTIL_expandFNT(FileNamesTable** fnt, int followLinks)
109437f1f268SConrad Meyer {
109537f1f268SConrad Meyer     FileNamesTable* const newFNT = UTIL_createExpandedFNT((*fnt)->fileNames, (*fnt)->tableSize, followLinks);
109637f1f268SConrad Meyer     CONTROL(newFNT != NULL);
109737f1f268SConrad Meyer     UTIL_freeFileNamesTable(*fnt);
109837f1f268SConrad Meyer     *fnt = newFNT;
109937f1f268SConrad Meyer }
1100a0483764SConrad Meyer 
UTIL_createFNT_fromROTable(const char ** filenames,size_t nbFilenames)110137f1f268SConrad Meyer FileNamesTable* UTIL_createFNT_fromROTable(const char** filenames, size_t nbFilenames)
110237f1f268SConrad Meyer {
110337f1f268SConrad Meyer     size_t const sizeof_FNTable = nbFilenames * sizeof(*filenames);
110437f1f268SConrad Meyer     const char** const newFNTable = (const char**)malloc(sizeof_FNTable);
110537f1f268SConrad Meyer     if (newFNTable==NULL) return NULL;
110637f1f268SConrad Meyer     memcpy((void*)newFNTable, filenames, sizeof_FNTable);  /* void* : mitigate a Visual compiler bug or limitation */
110737f1f268SConrad Meyer     return UTIL_assembleFileNamesTable(newFNTable, nbFilenames, NULL);
110837f1f268SConrad Meyer }
1109a0483764SConrad Meyer 
11102b9c00cbSConrad Meyer 
1111a0483764SConrad Meyer /*-****************************************
1112*5ff13fbcSAllan Jude *  count the number of cores
1113a0483764SConrad Meyer ******************************************/
1114a0483764SConrad Meyer 
1115a0483764SConrad Meyer #if defined(_WIN32) || defined(WIN32)
1116a0483764SConrad Meyer 
1117a0483764SConrad Meyer #include <windows.h>
1118a0483764SConrad Meyer 
1119a0483764SConrad Meyer typedef BOOL(WINAPI* LPFN_GLPI)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD);
1120a0483764SConrad Meyer 
CountSetBits(ULONG_PTR bitMask)1121*5ff13fbcSAllan Jude DWORD CountSetBits(ULONG_PTR bitMask)
1122a0483764SConrad Meyer {
1123*5ff13fbcSAllan Jude     DWORD LSHIFT = sizeof(ULONG_PTR)*8 - 1;
1124*5ff13fbcSAllan Jude     DWORD bitSetCount = 0;
1125*5ff13fbcSAllan Jude     ULONG_PTR bitTest = (ULONG_PTR)1 << LSHIFT;
1126*5ff13fbcSAllan Jude     DWORD i;
1127*5ff13fbcSAllan Jude 
1128*5ff13fbcSAllan Jude     for (i = 0; i <= LSHIFT; ++i)
1129*5ff13fbcSAllan Jude     {
1130*5ff13fbcSAllan Jude         bitSetCount += ((bitMask & bitTest)?1:0);
1131*5ff13fbcSAllan Jude         bitTest/=2;
1132*5ff13fbcSAllan Jude     }
1133*5ff13fbcSAllan Jude 
1134*5ff13fbcSAllan Jude     return bitSetCount;
1135*5ff13fbcSAllan Jude }
1136*5ff13fbcSAllan Jude 
UTIL_countCores(int logical)1137*5ff13fbcSAllan Jude int UTIL_countCores(int logical)
1138*5ff13fbcSAllan Jude {
1139*5ff13fbcSAllan Jude     static int numCores = 0;
1140*5ff13fbcSAllan Jude     if (numCores != 0) return numCores;
1141a0483764SConrad Meyer 
1142a0483764SConrad Meyer     {   LPFN_GLPI glpi;
1143a0483764SConrad Meyer         BOOL done = FALSE;
1144a0483764SConrad Meyer         PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL;
1145a0483764SConrad Meyer         PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL;
1146a0483764SConrad Meyer         DWORD returnLength = 0;
1147a0483764SConrad Meyer         size_t byteOffset = 0;
1148a0483764SConrad Meyer 
11499cbefe25SConrad Meyer #if defined(_MSC_VER)
11509cbefe25SConrad Meyer /* Visual Studio does not like the following cast */
11519cbefe25SConrad Meyer #   pragma warning( disable : 4054 )  /* conversion from function ptr to data ptr */
11529cbefe25SConrad Meyer #   pragma warning( disable : 4055 )  /* conversion from data ptr to function ptr */
11539cbefe25SConrad Meyer #endif
11549cbefe25SConrad Meyer         glpi = (LPFN_GLPI)(void*)GetProcAddress(GetModuleHandle(TEXT("kernel32")),
1155a0483764SConrad Meyer                                                "GetLogicalProcessorInformation");
1156a0483764SConrad Meyer 
1157a0483764SConrad Meyer         if (glpi == NULL) {
1158a0483764SConrad Meyer             goto failed;
1159a0483764SConrad Meyer         }
1160a0483764SConrad Meyer 
1161a0483764SConrad Meyer         while(!done) {
1162a0483764SConrad Meyer             DWORD rc = glpi(buffer, &returnLength);
1163a0483764SConrad Meyer             if (FALSE == rc) {
1164a0483764SConrad Meyer                 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
1165a0483764SConrad Meyer                     if (buffer)
1166a0483764SConrad Meyer                         free(buffer);
1167a0483764SConrad Meyer                     buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc(returnLength);
1168a0483764SConrad Meyer 
1169a0483764SConrad Meyer                     if (buffer == NULL) {
1170a0483764SConrad Meyer                         perror("zstd");
1171a0483764SConrad Meyer                         exit(1);
1172a0483764SConrad Meyer                     }
1173a0483764SConrad Meyer                 } else {
1174a0483764SConrad Meyer                     /* some other error */
1175a0483764SConrad Meyer                     goto failed;
1176a0483764SConrad Meyer                 }
1177a0483764SConrad Meyer             } else {
1178a0483764SConrad Meyer                 done = TRUE;
117937f1f268SConrad Meyer         }   }
1180a0483764SConrad Meyer 
1181a0483764SConrad Meyer         ptr = buffer;
1182a0483764SConrad Meyer 
1183a0483764SConrad Meyer         while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) {
1184a0483764SConrad Meyer 
1185a0483764SConrad Meyer             if (ptr->Relationship == RelationProcessorCore) {
1186*5ff13fbcSAllan Jude                 if (logical)
1187*5ff13fbcSAllan Jude                     numCores += CountSetBits(ptr->ProcessorMask);
1188*5ff13fbcSAllan Jude                 else
1189*5ff13fbcSAllan Jude                     numCores++;
1190a0483764SConrad Meyer             }
1191a0483764SConrad Meyer 
1192a0483764SConrad Meyer             ptr++;
1193a0483764SConrad Meyer             byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
1194a0483764SConrad Meyer         }
1195a0483764SConrad Meyer 
1196a0483764SConrad Meyer         free(buffer);
1197a0483764SConrad Meyer 
1198*5ff13fbcSAllan Jude         return numCores;
1199a0483764SConrad Meyer     }
1200a0483764SConrad Meyer 
1201a0483764SConrad Meyer failed:
1202a0483764SConrad Meyer     /* try to fall back on GetSystemInfo */
1203a0483764SConrad Meyer     {   SYSTEM_INFO sysinfo;
1204a0483764SConrad Meyer         GetSystemInfo(&sysinfo);
1205*5ff13fbcSAllan Jude         numCores = sysinfo.dwNumberOfProcessors;
1206*5ff13fbcSAllan Jude         if (numCores == 0) numCores = 1; /* just in case */
1207a0483764SConrad Meyer     }
1208*5ff13fbcSAllan Jude     return numCores;
1209a0483764SConrad Meyer }
1210a0483764SConrad Meyer 
1211a0483764SConrad Meyer #elif defined(__APPLE__)
1212a0483764SConrad Meyer 
1213a0483764SConrad Meyer #include <sys/sysctl.h>
1214a0483764SConrad Meyer 
1215a0483764SConrad Meyer /* Use apple-provided syscall
1216a0483764SConrad Meyer  * see: man 3 sysctl */
UTIL_countCores(int logical)1217*5ff13fbcSAllan Jude int UTIL_countCores(int logical)
1218a0483764SConrad Meyer {
1219*5ff13fbcSAllan Jude     static S32 numCores = 0; /* apple specifies int32_t */
1220*5ff13fbcSAllan Jude     if (numCores != 0) return numCores;
1221a0483764SConrad Meyer 
1222a0483764SConrad Meyer     {   size_t size = sizeof(S32);
1223*5ff13fbcSAllan Jude         int const ret = sysctlbyname(logical ? "hw.logicalcpu" : "hw.physicalcpu", &numCores, &size, NULL, 0);
1224a0483764SConrad Meyer         if (ret != 0) {
1225a0483764SConrad Meyer             if (errno == ENOENT) {
1226a0483764SConrad Meyer                 /* entry not present, fall back on 1 */
1227*5ff13fbcSAllan Jude                 numCores = 1;
1228a0483764SConrad Meyer             } else {
1229*5ff13fbcSAllan Jude                 perror("zstd: can't get number of cpus");
1230a0483764SConrad Meyer                 exit(1);
1231a0483764SConrad Meyer             }
1232a0483764SConrad Meyer         }
1233a0483764SConrad Meyer 
1234*5ff13fbcSAllan Jude         return numCores;
1235a0483764SConrad Meyer     }
1236a0483764SConrad Meyer }
1237a0483764SConrad Meyer 
1238a0483764SConrad Meyer #elif defined(__linux__)
1239a0483764SConrad Meyer 
1240a0483764SConrad Meyer /* parse /proc/cpuinfo
1241a0483764SConrad Meyer  * siblings / cpu cores should give hyperthreading ratio
1242a0483764SConrad Meyer  * otherwise fall back on sysconf */
UTIL_countCores(int logical)1243*5ff13fbcSAllan Jude int UTIL_countCores(int logical)
1244a0483764SConrad Meyer {
1245*5ff13fbcSAllan Jude     static int numCores = 0;
1246a0483764SConrad Meyer 
1247*5ff13fbcSAllan Jude     if (numCores != 0) return numCores;
1248a0483764SConrad Meyer 
1249*5ff13fbcSAllan Jude     numCores = (int)sysconf(_SC_NPROCESSORS_ONLN);
1250*5ff13fbcSAllan Jude     if (numCores == -1) {
1251a0483764SConrad Meyer         /* value not queryable, fall back on 1 */
1252*5ff13fbcSAllan Jude         return numCores = 1;
1253a0483764SConrad Meyer     }
1254a0483764SConrad Meyer 
1255a0483764SConrad Meyer     /* try to determine if there's hyperthreading */
1256a0483764SConrad Meyer     {   FILE* const cpuinfo = fopen("/proc/cpuinfo", "r");
1257a0483764SConrad Meyer #define BUF_SIZE 80
1258a0483764SConrad Meyer         char buff[BUF_SIZE];
1259a0483764SConrad Meyer 
1260a0483764SConrad Meyer         int siblings = 0;
1261a0483764SConrad Meyer         int cpu_cores = 0;
1262a0483764SConrad Meyer         int ratio = 1;
1263a0483764SConrad Meyer 
1264a0483764SConrad Meyer         if (cpuinfo == NULL) {
1265a0483764SConrad Meyer             /* fall back on the sysconf value */
1266*5ff13fbcSAllan Jude             return numCores;
1267a0483764SConrad Meyer         }
1268a0483764SConrad Meyer 
1269a0483764SConrad Meyer         /* assume the cpu cores/siblings values will be constant across all
1270a0483764SConrad Meyer          * present processors */
1271a0483764SConrad Meyer         while (!feof(cpuinfo)) {
1272a0483764SConrad Meyer             if (fgets(buff, BUF_SIZE, cpuinfo) != NULL) {
1273a0483764SConrad Meyer                 if (strncmp(buff, "siblings", 8) == 0) {
1274a0483764SConrad Meyer                     const char* const sep = strchr(buff, ':');
12759cbefe25SConrad Meyer                     if (sep == NULL || *sep == '\0') {
1276a0483764SConrad Meyer                         /* formatting was broken? */
1277a0483764SConrad Meyer                         goto failed;
1278a0483764SConrad Meyer                     }
1279a0483764SConrad Meyer 
1280a0483764SConrad Meyer                     siblings = atoi(sep + 1);
1281a0483764SConrad Meyer                 }
1282a0483764SConrad Meyer                 if (strncmp(buff, "cpu cores", 9) == 0) {
1283a0483764SConrad Meyer                     const char* const sep = strchr(buff, ':');
12849cbefe25SConrad Meyer                     if (sep == NULL || *sep == '\0') {
1285a0483764SConrad Meyer                         /* formatting was broken? */
1286a0483764SConrad Meyer                         goto failed;
1287a0483764SConrad Meyer                     }
1288a0483764SConrad Meyer 
1289a0483764SConrad Meyer                     cpu_cores = atoi(sep + 1);
1290a0483764SConrad Meyer                 }
1291a0483764SConrad Meyer             } else if (ferror(cpuinfo)) {
1292a0483764SConrad Meyer                 /* fall back on the sysconf value */
1293a0483764SConrad Meyer                 goto failed;
129437f1f268SConrad Meyer         }   }
1295*5ff13fbcSAllan Jude         if (siblings && cpu_cores && siblings > cpu_cores) {
1296a0483764SConrad Meyer             ratio = siblings / cpu_cores;
1297a0483764SConrad Meyer         }
1298*5ff13fbcSAllan Jude 
1299*5ff13fbcSAllan Jude         if (ratio && numCores > ratio && !logical) {
1300*5ff13fbcSAllan Jude             numCores = numCores / ratio;
1301*5ff13fbcSAllan Jude         }
1302*5ff13fbcSAllan Jude 
1303a0483764SConrad Meyer failed:
1304a0483764SConrad Meyer         fclose(cpuinfo);
1305*5ff13fbcSAllan Jude         return numCores;
1306a0483764SConrad Meyer     }
1307a0483764SConrad Meyer }
1308a0483764SConrad Meyer 
13092b9c00cbSConrad Meyer #elif defined(__FreeBSD__)
1310a0483764SConrad Meyer 
13112b9c00cbSConrad Meyer #include <sys/param.h>
13122b9c00cbSConrad Meyer #include <sys/sysctl.h>
13132b9c00cbSConrad Meyer 
13142b9c00cbSConrad Meyer /* Use physical core sysctl when available
13152b9c00cbSConrad Meyer  * see: man 4 smp, man 3 sysctl */
UTIL_countCores(int logical)1316*5ff13fbcSAllan Jude int UTIL_countCores(int logical)
13172b9c00cbSConrad Meyer {
1318*5ff13fbcSAllan Jude     static int numCores = 0; /* freebsd sysctl is native int sized */
1319*5ff13fbcSAllan Jude #if __FreeBSD_version >= 1300008
1320*5ff13fbcSAllan Jude     static int perCore = 1;
1321*5ff13fbcSAllan Jude #endif
1322*5ff13fbcSAllan Jude     if (numCores != 0) return numCores;
13232b9c00cbSConrad Meyer 
13242b9c00cbSConrad Meyer #if __FreeBSD_version >= 1300008
1325*5ff13fbcSAllan Jude     {   size_t size = sizeof(numCores);
1326*5ff13fbcSAllan Jude         int ret = sysctlbyname("kern.smp.cores", &numCores, &size, NULL, 0);
1327*5ff13fbcSAllan Jude         if (ret == 0) {
1328*5ff13fbcSAllan Jude             if (logical) {
1329*5ff13fbcSAllan Jude                 ret = sysctlbyname("kern.smp.threads_per_core", &perCore, &size, NULL, 0);
1330*5ff13fbcSAllan Jude                 /* default to physical cores if logical cannot be read */
1331*5ff13fbcSAllan Jude                 if (ret == 0)
1332*5ff13fbcSAllan Jude                     numCores *= perCore;
1333*5ff13fbcSAllan Jude             }
1334*5ff13fbcSAllan Jude 
1335*5ff13fbcSAllan Jude             return numCores;
1336*5ff13fbcSAllan Jude         }
13372b9c00cbSConrad Meyer         if (errno != ENOENT) {
1338*5ff13fbcSAllan Jude             perror("zstd: can't get number of cpus");
13392b9c00cbSConrad Meyer             exit(1);
13402b9c00cbSConrad Meyer         }
13412b9c00cbSConrad Meyer         /* sysctl not present, fall through to older sysconf method */
13422b9c00cbSConrad Meyer     }
1343*5ff13fbcSAllan Jude #else
1344*5ff13fbcSAllan Jude     /* suppress unused parameter warning */
1345*5ff13fbcSAllan Jude     (void) logical;
13462b9c00cbSConrad Meyer #endif
13472b9c00cbSConrad Meyer 
1348*5ff13fbcSAllan Jude     numCores = (int)sysconf(_SC_NPROCESSORS_ONLN);
1349*5ff13fbcSAllan Jude     if (numCores == -1) {
13502b9c00cbSConrad Meyer         /* value not queryable, fall back on 1 */
1351*5ff13fbcSAllan Jude         numCores = 1;
13522b9c00cbSConrad Meyer     }
1353*5ff13fbcSAllan Jude     return numCores;
13542b9c00cbSConrad Meyer }
13552b9c00cbSConrad Meyer 
135637f1f268SConrad Meyer #elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__CYGWIN__)
13572b9c00cbSConrad Meyer 
13582b9c00cbSConrad Meyer /* Use POSIX sysconf
13592b9c00cbSConrad Meyer  * see: man 3 sysconf */
UTIL_countCores(int logical)1360*5ff13fbcSAllan Jude int UTIL_countCores(int logical)
1361a0483764SConrad Meyer {
1362*5ff13fbcSAllan Jude     static int numCores = 0;
1363a0483764SConrad Meyer 
1364*5ff13fbcSAllan Jude     /* suppress unused parameter warning */
1365*5ff13fbcSAllan Jude     (void)logical;
1366a0483764SConrad Meyer 
1367*5ff13fbcSAllan Jude     if (numCores != 0) return numCores;
1368*5ff13fbcSAllan Jude 
1369*5ff13fbcSAllan Jude     numCores = (int)sysconf(_SC_NPROCESSORS_ONLN);
1370*5ff13fbcSAllan Jude     if (numCores == -1) {
1371a0483764SConrad Meyer         /* value not queryable, fall back on 1 */
1372*5ff13fbcSAllan Jude         return numCores = 1;
1373a0483764SConrad Meyer     }
1374*5ff13fbcSAllan Jude     return numCores;
1375a0483764SConrad Meyer }
1376a0483764SConrad Meyer 
1377a0483764SConrad Meyer #else
1378a0483764SConrad Meyer 
UTIL_countCores(int logical)1379*5ff13fbcSAllan Jude int UTIL_countCores(int logical)
1380a0483764SConrad Meyer {
1381a0483764SConrad Meyer     /* assume 1 */
1382a0483764SConrad Meyer     return 1;
1383a0483764SConrad Meyer }
1384a0483764SConrad Meyer 
1385a0483764SConrad Meyer #endif
1386a0483764SConrad Meyer 
UTIL_countPhysicalCores(void)1387*5ff13fbcSAllan Jude int UTIL_countPhysicalCores(void)
1388*5ff13fbcSAllan Jude {
1389*5ff13fbcSAllan Jude     return UTIL_countCores(0);
1390*5ff13fbcSAllan Jude }
1391*5ff13fbcSAllan Jude 
UTIL_countLogicalCores(void)1392*5ff13fbcSAllan Jude int UTIL_countLogicalCores(void)
1393*5ff13fbcSAllan Jude {
1394*5ff13fbcSAllan Jude     return UTIL_countCores(1);
1395*5ff13fbcSAllan Jude }
1396*5ff13fbcSAllan Jude 
1397a0483764SConrad Meyer #if defined (__cplusplus)
1398a0483764SConrad Meyer }
1399a0483764SConrad Meyer #endif
1400