xref: /freebsd/sys/contrib/zstd/programs/util.c (revision af23369a6deaaeb612ab266eb88b8bb8d560c322)
1 /*
2  * Copyright (c) Przemyslaw Skibinski, Yann Collet, Facebook, Inc.
3  * All rights reserved.
4  *
5  * This source code is licensed under both the BSD-style license (found in the
6  * LICENSE file in the root directory of this source tree) and the GPLv2 (found
7  * in the COPYING file in the root directory of this source tree).
8  * You may select, at your option, one of the above-listed licenses.
9  */
10 
11 #if defined (__cplusplus)
12 extern "C" {
13 #endif
14 
15 
16 /*-****************************************
17 *  Dependencies
18 ******************************************/
19 #include "util.h"       /* note : ensure that platform.h is included first ! */
20 #include <stdlib.h>     /* malloc, realloc, free */
21 #include <stdio.h>      /* fprintf */
22 #include <time.h>       /* clock_t, clock, CLOCKS_PER_SEC, nanosleep */
23 #include <errno.h>
24 #include <assert.h>
25 
26 #if defined(_WIN32)
27 #  include <sys/utime.h>  /* utime */
28 #  include <io.h>         /* _chmod */
29 #else
30 #  include <unistd.h>     /* chown, stat */
31 #  if PLATFORM_POSIX_VERSION < 200809L || !defined(st_mtime)
32 #    include <utime.h>    /* utime */
33 #  else
34 #    include <fcntl.h>    /* AT_FDCWD */
35 #    include <sys/stat.h> /* utimensat */
36 #  endif
37 #endif
38 
39 #if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__)
40 #include <direct.h>     /* needed for _mkdir in windows */
41 #endif
42 
43 #if defined(__linux__) || (PLATFORM_POSIX_VERSION >= 200112L)  /* opendir, readdir require POSIX.1-2001 */
44 #  include <dirent.h>       /* opendir, readdir */
45 #  include <string.h>       /* strerror, memcpy */
46 #endif /* #ifdef _WIN32 */
47 
48 /*-****************************************
49 *  Internal Macros
50 ******************************************/
51 
52 /* CONTROL is almost like an assert(), but is never disabled.
53  * It's designed for failures that may happen rarely,
54  * but we don't want to maintain a specific error code path for them,
55  * such as a malloc() returning NULL for example.
56  * Since it's always active, this macro can trigger side effects.
57  */
58 #define CONTROL(c)  {         \
59     if (!(c)) {               \
60         UTIL_DISPLAYLEVEL(1, "Error : %s, %i : %s",  \
61                           __FILE__, __LINE__, #c);   \
62         exit(1);              \
63 }   }
64 
65 /* console log */
66 #define UTIL_DISPLAY(...)         fprintf(stderr, __VA_ARGS__)
67 #define UTIL_DISPLAYLEVEL(l, ...) { if (g_utilDisplayLevel>=l) { UTIL_DISPLAY(__VA_ARGS__); } }
68 
69 /* A modified version of realloc().
70  * If UTIL_realloc() fails the original block is freed.
71  */
72 UTIL_STATIC void* UTIL_realloc(void *ptr, size_t size)
73 {
74     void *newptr = realloc(ptr, size);
75     if (newptr) return newptr;
76     free(ptr);
77     return NULL;
78 }
79 
80 #if defined(_MSC_VER)
81     #define chmod _chmod
82 #endif
83 
84 
85 /*-****************************************
86 *  Console log
87 ******************************************/
88 int g_utilDisplayLevel;
89 
90 int UTIL_requireUserConfirmation(const char* prompt, const char* abortMsg,
91                                  const char* acceptableLetters, int hasStdinInput) {
92     int ch, result;
93 
94     if (hasStdinInput) {
95         UTIL_DISPLAY("stdin is an input - not proceeding.\n");
96         return 1;
97     }
98 
99     UTIL_DISPLAY("%s", prompt);
100     ch = getchar();
101     result = 0;
102     if (strchr(acceptableLetters, ch) == NULL) {
103         UTIL_DISPLAY("%s", abortMsg);
104         result = 1;
105     }
106     /* flush the rest */
107     while ((ch!=EOF) && (ch!='\n'))
108         ch = getchar();
109     return result;
110 }
111 
112 
113 /*-*************************************
114 *  Constants
115 ***************************************/
116 #define LIST_SIZE_INCREASE   (8*1024)
117 #define MAX_FILE_OF_FILE_NAMES_SIZE (1<<20)*50
118 
119 
120 /*-*************************************
121 *  Functions
122 ***************************************/
123 
124 int UTIL_stat(const char* filename, stat_t* statbuf)
125 {
126 #if defined(_MSC_VER)
127     return !_stat64(filename, statbuf);
128 #elif defined(__MINGW32__) && defined (__MSVCRT__)
129     return !_stati64(filename, statbuf);
130 #else
131     return !stat(filename, statbuf);
132 #endif
133 }
134 
135 int UTIL_isRegularFile(const char* infilename)
136 {
137     stat_t statbuf;
138     return UTIL_stat(infilename, &statbuf) && UTIL_isRegularFileStat(&statbuf);
139 }
140 
141 int UTIL_isRegularFileStat(const stat_t* statbuf)
142 {
143 #if defined(_MSC_VER)
144     return (statbuf->st_mode & S_IFREG) != 0;
145 #else
146     return S_ISREG(statbuf->st_mode) != 0;
147 #endif
148 }
149 
150 /* like chmod, but avoid changing permission of /dev/null */
151 int UTIL_chmod(char const* filename, const stat_t* statbuf, mode_t permissions)
152 {
153     stat_t localStatBuf;
154     if (statbuf == NULL) {
155         if (!UTIL_stat(filename, &localStatBuf)) return 0;
156         statbuf = &localStatBuf;
157     }
158     if (!UTIL_isRegularFileStat(statbuf)) return 0; /* pretend success, but don't change anything */
159     return chmod(filename, permissions);
160 }
161 
162 /* set access and modification times */
163 int UTIL_utime(const char* filename, const stat_t *statbuf)
164 {
165     int ret;
166     /* We check that st_mtime is a macro here in order to give us confidence
167      * that struct stat has a struct timespec st_mtim member. We need this
168      * check because there are some platforms that claim to be POSIX 2008
169      * compliant but which do not have st_mtim... */
170 #if (PLATFORM_POSIX_VERSION >= 200809L) && defined(st_mtime)
171     /* (atime, mtime) */
172     struct timespec timebuf[2] = { {0, UTIME_NOW} };
173     timebuf[1] = statbuf->st_mtim;
174     ret = utimensat(AT_FDCWD, filename, timebuf, 0);
175 #else
176     struct utimbuf timebuf;
177     timebuf.actime = time(NULL);
178     timebuf.modtime = statbuf->st_mtime;
179     ret = utime(filename, &timebuf);
180 #endif
181     errno = 0;
182     return ret;
183 }
184 
185 int UTIL_setFileStat(const char *filename, const stat_t *statbuf)
186 {
187     int res = 0;
188 
189     stat_t curStatBuf;
190     if (!UTIL_stat(filename, &curStatBuf) || !UTIL_isRegularFileStat(&curStatBuf))
191         return -1;
192 
193     /* set access and modification times */
194     res += UTIL_utime(filename, statbuf);
195 
196 #if !defined(_WIN32)
197     res += chown(filename, statbuf->st_uid, statbuf->st_gid);  /* Copy ownership */
198 #endif
199 
200     res += UTIL_chmod(filename, &curStatBuf, statbuf->st_mode & 07777);  /* Copy file permissions */
201 
202     errno = 0;
203     return -res; /* number of errors is returned */
204 }
205 
206 int UTIL_isDirectory(const char* infilename)
207 {
208     stat_t statbuf;
209     return UTIL_stat(infilename, &statbuf) && UTIL_isDirectoryStat(&statbuf);
210 }
211 
212 int UTIL_isDirectoryStat(const stat_t* statbuf)
213 {
214 #if defined(_MSC_VER)
215     return (statbuf->st_mode & _S_IFDIR) != 0;
216 #else
217     return S_ISDIR(statbuf->st_mode) != 0;
218 #endif
219 }
220 
221 int UTIL_compareStr(const void *p1, const void *p2) {
222     return strcmp(* (char * const *) p1, * (char * const *) p2);
223 }
224 
225 int UTIL_isSameFile(const char* fName1, const char* fName2)
226 {
227     assert(fName1 != NULL); assert(fName2 != NULL);
228 #if defined(_MSC_VER) || defined(_WIN32)
229     /* note : Visual does not support file identification by inode.
230      *        inode does not work on Windows, even with a posix layer, like msys2.
231      *        The following work-around is limited to detecting exact name repetition only,
232      *        aka `filename` is considered different from `subdir/../filename` */
233     return !strcmp(fName1, fName2);
234 #else
235     {   stat_t file1Stat;
236         stat_t file2Stat;
237         return UTIL_stat(fName1, &file1Stat)
238             && UTIL_stat(fName2, &file2Stat)
239             && (file1Stat.st_dev == file2Stat.st_dev)
240             && (file1Stat.st_ino == file2Stat.st_ino);
241     }
242 #endif
243 }
244 
245 /* UTIL_isFIFO : distinguish named pipes */
246 int UTIL_isFIFO(const char* infilename)
247 {
248 /* macro guards, as defined in : https://linux.die.net/man/2/lstat */
249 #if PLATFORM_POSIX_VERSION >= 200112L
250     stat_t statbuf;
251     if (UTIL_stat(infilename, &statbuf) && UTIL_isFIFOStat(&statbuf)) return 1;
252 #endif
253     (void)infilename;
254     return 0;
255 }
256 
257 /* UTIL_isFIFO : distinguish named pipes */
258 int UTIL_isFIFOStat(const stat_t* statbuf)
259 {
260 /* macro guards, as defined in : https://linux.die.net/man/2/lstat */
261 #if PLATFORM_POSIX_VERSION >= 200112L
262     if (S_ISFIFO(statbuf->st_mode)) return 1;
263 #endif
264     (void)statbuf;
265     return 0;
266 }
267 
268 /* UTIL_isBlockDevStat : distinguish named pipes */
269 int UTIL_isBlockDevStat(const stat_t* statbuf)
270 {
271 /* macro guards, as defined in : https://linux.die.net/man/2/lstat */
272 #if PLATFORM_POSIX_VERSION >= 200112L
273     if (S_ISBLK(statbuf->st_mode)) return 1;
274 #endif
275     (void)statbuf;
276     return 0;
277 }
278 
279 int UTIL_isLink(const char* infilename)
280 {
281 /* macro guards, as defined in : https://linux.die.net/man/2/lstat */
282 #if PLATFORM_POSIX_VERSION >= 200112L
283     stat_t statbuf;
284     int const r = lstat(infilename, &statbuf);
285     if (!r && S_ISLNK(statbuf.st_mode)) return 1;
286 #endif
287     (void)infilename;
288     return 0;
289 }
290 
291 U64 UTIL_getFileSize(const char* infilename)
292 {
293     stat_t statbuf;
294     if (!UTIL_stat(infilename, &statbuf)) return UTIL_FILESIZE_UNKNOWN;
295     return UTIL_getFileSizeStat(&statbuf);
296 }
297 
298 U64 UTIL_getFileSizeStat(const stat_t* statbuf)
299 {
300     if (!UTIL_isRegularFileStat(statbuf)) return UTIL_FILESIZE_UNKNOWN;
301 #if defined(_MSC_VER)
302     if (!(statbuf->st_mode & S_IFREG)) return UTIL_FILESIZE_UNKNOWN;
303 #elif defined(__MINGW32__) && defined (__MSVCRT__)
304     if (!(statbuf->st_mode & S_IFREG)) return UTIL_FILESIZE_UNKNOWN;
305 #else
306     if (!S_ISREG(statbuf->st_mode)) return UTIL_FILESIZE_UNKNOWN;
307 #endif
308     return (U64)statbuf->st_size;
309 }
310 
311 UTIL_HumanReadableSize_t UTIL_makeHumanReadableSize(U64 size)
312 {
313     UTIL_HumanReadableSize_t hrs;
314 
315     if (g_utilDisplayLevel > 3) {
316         /* In verbose mode, do not scale sizes down, except in the case of
317          * values that exceed the integral precision of a double. */
318         if (size >= (1ull << 53)) {
319             hrs.value = (double)size / (1ull << 20);
320             hrs.suffix = " MiB";
321             /* At worst, a double representation of a maximal size will be
322              * accurate to better than tens of kilobytes. */
323             hrs.precision = 2;
324         } else {
325             hrs.value = (double)size;
326             hrs.suffix = " B";
327             hrs.precision = 0;
328         }
329     } else {
330         /* In regular mode, scale sizes down and use suffixes. */
331         if (size >= (1ull << 60)) {
332             hrs.value = (double)size / (1ull << 60);
333             hrs.suffix = " EiB";
334         } else if (size >= (1ull << 50)) {
335             hrs.value = (double)size / (1ull << 50);
336             hrs.suffix = " PiB";
337         } else if (size >= (1ull << 40)) {
338             hrs.value = (double)size / (1ull << 40);
339             hrs.suffix = " TiB";
340         } else if (size >= (1ull << 30)) {
341             hrs.value = (double)size / (1ull << 30);
342             hrs.suffix = " GiB";
343         } else if (size >= (1ull << 20)) {
344             hrs.value = (double)size / (1ull << 20);
345             hrs.suffix = " MiB";
346         } else if (size >= (1ull << 10)) {
347             hrs.value = (double)size / (1ull << 10);
348             hrs.suffix = " KiB";
349         } else {
350             hrs.value = (double)size;
351             hrs.suffix = " B";
352         }
353 
354         if (hrs.value >= 100 || (U64)hrs.value == size) {
355             hrs.precision = 0;
356         } else if (hrs.value >= 10) {
357             hrs.precision = 1;
358         } else if (hrs.value > 1) {
359             hrs.precision = 2;
360         } else {
361             hrs.precision = 3;
362         }
363     }
364 
365     return hrs;
366 }
367 
368 U64 UTIL_getTotalFileSize(const char* const * fileNamesTable, unsigned nbFiles)
369 {
370     U64 total = 0;
371     unsigned n;
372     for (n=0; n<nbFiles; n++) {
373         U64 const size = UTIL_getFileSize(fileNamesTable[n]);
374         if (size == UTIL_FILESIZE_UNKNOWN) return UTIL_FILESIZE_UNKNOWN;
375         total += size;
376     }
377     return total;
378 }
379 
380 
381 /* condition : @file must be valid, and not have reached its end.
382  * @return : length of line written into @buf, ended with `\0` instead of '\n',
383  *           or 0, if there is no new line */
384 static size_t readLineFromFile(char* buf, size_t len, FILE* file)
385 {
386     assert(!feof(file));
387     if ( fgets(buf, (int) len, file) == NULL ) return 0;
388     {   size_t linelen = strlen(buf);
389         if (strlen(buf)==0) return 0;
390         if (buf[linelen-1] == '\n') linelen--;
391         buf[linelen] = '\0';
392         return linelen+1;
393     }
394 }
395 
396 /* Conditions :
397  *   size of @inputFileName file must be < @dstCapacity
398  *   @dst must be initialized
399  * @return : nb of lines
400  *       or -1 if there's an error
401  */
402 static int
403 readLinesFromFile(void* dst, size_t dstCapacity,
404             const char* inputFileName)
405 {
406     int nbFiles = 0;
407     size_t pos = 0;
408     char* const buf = (char*)dst;
409     FILE* const inputFile = fopen(inputFileName, "r");
410 
411     assert(dst != NULL);
412 
413     if(!inputFile) {
414         if (g_utilDisplayLevel >= 1) perror("zstd:util:readLinesFromFile");
415         return -1;
416     }
417 
418     while ( !feof(inputFile) ) {
419         size_t const lineLength = readLineFromFile(buf+pos, dstCapacity-pos, inputFile);
420         if (lineLength == 0) break;
421         assert(pos + lineLength < dstCapacity);
422         pos += lineLength;
423         ++nbFiles;
424     }
425 
426     CONTROL( fclose(inputFile) == 0 );
427 
428     return nbFiles;
429 }
430 
431 /*Note: buf is not freed in case function successfully created table because filesTable->fileNames[0] = buf*/
432 FileNamesTable*
433 UTIL_createFileNamesTable_fromFileName(const char* inputFileName)
434 {
435     size_t nbFiles = 0;
436     char* buf;
437     size_t bufSize;
438     size_t pos = 0;
439     stat_t statbuf;
440 
441     if (!UTIL_stat(inputFileName, &statbuf) || !UTIL_isRegularFileStat(&statbuf))
442         return NULL;
443 
444     {   U64 const inputFileSize = UTIL_getFileSizeStat(&statbuf);
445         if(inputFileSize > MAX_FILE_OF_FILE_NAMES_SIZE)
446             return NULL;
447         bufSize = (size_t)(inputFileSize + 1); /* (+1) to add '\0' at the end of last filename */
448     }
449 
450     buf = (char*) malloc(bufSize);
451     CONTROL( buf != NULL );
452 
453     {   int const ret_nbFiles = readLinesFromFile(buf, bufSize, inputFileName);
454 
455         if (ret_nbFiles <= 0) {
456           free(buf);
457           return NULL;
458         }
459         nbFiles = (size_t)ret_nbFiles;
460     }
461 
462     {   const char** filenamesTable = (const char**) malloc(nbFiles * sizeof(*filenamesTable));
463         CONTROL(filenamesTable != NULL);
464 
465         {   size_t fnb;
466             for (fnb = 0, pos = 0; fnb < nbFiles; fnb++) {
467                 filenamesTable[fnb] = buf+pos;
468                 pos += strlen(buf+pos)+1;  /* +1 for the finishing `\0` */
469         }   }
470         assert(pos <= bufSize);
471 
472         return UTIL_assembleFileNamesTable(filenamesTable, nbFiles, buf);
473     }
474 }
475 
476 static FileNamesTable*
477 UTIL_assembleFileNamesTable2(const char** filenames, size_t tableSize, size_t tableCapacity, char* buf)
478 {
479     FileNamesTable* const table = (FileNamesTable*) malloc(sizeof(*table));
480     CONTROL(table != NULL);
481     table->fileNames = filenames;
482     table->buf = buf;
483     table->tableSize = tableSize;
484     table->tableCapacity = tableCapacity;
485     return table;
486 }
487 
488 FileNamesTable*
489 UTIL_assembleFileNamesTable(const char** filenames, size_t tableSize, char* buf)
490 {
491     return UTIL_assembleFileNamesTable2(filenames, tableSize, tableSize, buf);
492 }
493 
494 void UTIL_freeFileNamesTable(FileNamesTable* table)
495 {
496     if (table==NULL) return;
497     free((void*)table->fileNames);
498     free(table->buf);
499     free(table);
500 }
501 
502 FileNamesTable* UTIL_allocateFileNamesTable(size_t tableSize)
503 {
504     const char** const fnTable = (const char**)malloc(tableSize * sizeof(*fnTable));
505     FileNamesTable* fnt;
506     if (fnTable==NULL) return NULL;
507     fnt = UTIL_assembleFileNamesTable(fnTable, tableSize, NULL);
508     fnt->tableSize = 0;   /* the table is empty */
509     return fnt;
510 }
511 
512 void UTIL_refFilename(FileNamesTable* fnt, const char* filename)
513 {
514     assert(fnt->tableSize < fnt->tableCapacity);
515     fnt->fileNames[fnt->tableSize] = filename;
516     fnt->tableSize++;
517 }
518 
519 static size_t getTotalTableSize(FileNamesTable* table)
520 {
521     size_t fnb = 0, totalSize = 0;
522     for(fnb = 0 ; fnb < table->tableSize && table->fileNames[fnb] ; ++fnb) {
523         totalSize += strlen(table->fileNames[fnb]) + 1; /* +1 to add '\0' at the end of each fileName */
524     }
525     return totalSize;
526 }
527 
528 FileNamesTable*
529 UTIL_mergeFileNamesTable(FileNamesTable* table1, FileNamesTable* table2)
530 {
531     unsigned newTableIdx = 0;
532     size_t pos = 0;
533     size_t newTotalTableSize;
534     char* buf;
535 
536     FileNamesTable* const newTable = UTIL_assembleFileNamesTable(NULL, 0, NULL);
537     CONTROL( newTable != NULL );
538 
539     newTotalTableSize = getTotalTableSize(table1) + getTotalTableSize(table2);
540 
541     buf = (char*) calloc(newTotalTableSize, sizeof(*buf));
542     CONTROL ( buf != NULL );
543 
544     newTable->buf = buf;
545     newTable->tableSize = table1->tableSize + table2->tableSize;
546     newTable->fileNames = (const char **) calloc(newTable->tableSize, sizeof(*(newTable->fileNames)));
547     CONTROL ( newTable->fileNames != NULL );
548 
549     {   unsigned idx1;
550         for( idx1=0 ; (idx1 < table1->tableSize) && table1->fileNames[idx1] && (pos < newTotalTableSize); ++idx1, ++newTableIdx) {
551             size_t const curLen = strlen(table1->fileNames[idx1]);
552             memcpy(buf+pos, table1->fileNames[idx1], curLen);
553             assert(newTableIdx <= newTable->tableSize);
554             newTable->fileNames[newTableIdx] = buf+pos;
555             pos += curLen+1;
556     }   }
557 
558     {   unsigned idx2;
559         for( idx2=0 ; (idx2 < table2->tableSize) && table2->fileNames[idx2] && (pos < newTotalTableSize) ; ++idx2, ++newTableIdx) {
560             size_t const curLen = strlen(table2->fileNames[idx2]);
561             memcpy(buf+pos, table2->fileNames[idx2], curLen);
562             assert(newTableIdx <= newTable->tableSize);
563             newTable->fileNames[newTableIdx] = buf+pos;
564             pos += curLen+1;
565     }   }
566     assert(pos <= newTotalTableSize);
567     newTable->tableSize = newTableIdx;
568 
569     UTIL_freeFileNamesTable(table1);
570     UTIL_freeFileNamesTable(table2);
571 
572     return newTable;
573 }
574 
575 #ifdef _WIN32
576 static int UTIL_prepareFileList(const char* dirName,
577                                 char** bufStart, size_t* pos,
578                                 char** bufEnd, int followLinks)
579 {
580     char* path;
581     size_t dirLength, pathLength;
582     int nbFiles = 0;
583     WIN32_FIND_DATAA cFile;
584     HANDLE hFile;
585 
586     dirLength = strlen(dirName);
587     path = (char*) malloc(dirLength + 3);
588     if (!path) return 0;
589 
590     memcpy(path, dirName, dirLength);
591     path[dirLength] = '\\';
592     path[dirLength+1] = '*';
593     path[dirLength+2] = 0;
594 
595     hFile=FindFirstFileA(path, &cFile);
596     if (hFile == INVALID_HANDLE_VALUE) {
597         UTIL_DISPLAYLEVEL(1, "Cannot open directory '%s'\n", dirName);
598         return 0;
599     }
600     free(path);
601 
602     do {
603         size_t const fnameLength = strlen(cFile.cFileName);
604         path = (char*) malloc(dirLength + fnameLength + 2);
605         if (!path) { FindClose(hFile); return 0; }
606         memcpy(path, dirName, dirLength);
607         path[dirLength] = '\\';
608         memcpy(path+dirLength+1, cFile.cFileName, fnameLength);
609         pathLength = dirLength+1+fnameLength;
610         path[pathLength] = 0;
611         if (cFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
612             if ( strcmp (cFile.cFileName, "..") == 0
613               || strcmp (cFile.cFileName, ".") == 0 )
614                 continue;
615             /* Recursively call "UTIL_prepareFileList" with the new path. */
616             nbFiles += UTIL_prepareFileList(path, bufStart, pos, bufEnd, followLinks);
617             if (*bufStart == NULL) { free(path); FindClose(hFile); return 0; }
618         } else if ( (cFile.dwFileAttributes & FILE_ATTRIBUTE_NORMAL)
619                  || (cFile.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
620                  || (cFile.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) ) {
621             if (*bufStart + *pos + pathLength >= *bufEnd) {
622                 ptrdiff_t const newListSize = (*bufEnd - *bufStart) + LIST_SIZE_INCREASE;
623                 *bufStart = (char*)UTIL_realloc(*bufStart, newListSize);
624                 if (*bufStart == NULL) { free(path); FindClose(hFile); return 0; }
625                 *bufEnd = *bufStart + newListSize;
626             }
627             if (*bufStart + *pos + pathLength < *bufEnd) {
628                 memcpy(*bufStart + *pos, path, pathLength+1 /* include final \0 */);
629                 *pos += pathLength + 1;
630                 nbFiles++;
631         }   }
632         free(path);
633     } while (FindNextFileA(hFile, &cFile));
634 
635     FindClose(hFile);
636     return nbFiles;
637 }
638 
639 #elif defined(__linux__) || (PLATFORM_POSIX_VERSION >= 200112L)  /* opendir, readdir require POSIX.1-2001 */
640 
641 static int UTIL_prepareFileList(const char *dirName,
642                                 char** bufStart, size_t* pos,
643                                 char** bufEnd, int followLinks)
644 {
645     DIR* dir;
646     struct dirent * entry;
647     size_t dirLength;
648     int nbFiles = 0;
649 
650     if (!(dir = opendir(dirName))) {
651         UTIL_DISPLAYLEVEL(1, "Cannot open directory '%s': %s\n", dirName, strerror(errno));
652         return 0;
653     }
654 
655     dirLength = strlen(dirName);
656     errno = 0;
657     while ((entry = readdir(dir)) != NULL) {
658         char* path;
659         size_t fnameLength, pathLength;
660         if (strcmp (entry->d_name, "..") == 0 ||
661             strcmp (entry->d_name, ".") == 0) continue;
662         fnameLength = strlen(entry->d_name);
663         path = (char*) malloc(dirLength + fnameLength + 2);
664         if (!path) { closedir(dir); return 0; }
665         memcpy(path, dirName, dirLength);
666 
667         path[dirLength] = '/';
668         memcpy(path+dirLength+1, entry->d_name, fnameLength);
669         pathLength = dirLength+1+fnameLength;
670         path[pathLength] = 0;
671 
672         if (!followLinks && UTIL_isLink(path)) {
673             UTIL_DISPLAYLEVEL(2, "Warning : %s is a symbolic link, ignoring\n", path);
674             free(path);
675             continue;
676         }
677 
678         if (UTIL_isDirectory(path)) {
679             nbFiles += UTIL_prepareFileList(path, bufStart, pos, bufEnd, followLinks);  /* Recursively call "UTIL_prepareFileList" with the new path. */
680             if (*bufStart == NULL) { free(path); closedir(dir); return 0; }
681         } else {
682             if (*bufStart + *pos + pathLength >= *bufEnd) {
683                 ptrdiff_t newListSize = (*bufEnd - *bufStart) + LIST_SIZE_INCREASE;
684                 assert(newListSize >= 0);
685                 *bufStart = (char*)UTIL_realloc(*bufStart, (size_t)newListSize);
686                 *bufEnd = *bufStart + newListSize;
687                 if (*bufStart == NULL) { free(path); closedir(dir); return 0; }
688             }
689             if (*bufStart + *pos + pathLength < *bufEnd) {
690                 memcpy(*bufStart + *pos, path, pathLength + 1);  /* with final \0 */
691                 *pos += pathLength + 1;
692                 nbFiles++;
693         }   }
694         free(path);
695         errno = 0; /* clear errno after UTIL_isDirectory, UTIL_prepareFileList */
696     }
697 
698     if (errno != 0) {
699         UTIL_DISPLAYLEVEL(1, "readdir(%s) error: %s \n", dirName, strerror(errno));
700         free(*bufStart);
701         *bufStart = NULL;
702     }
703     closedir(dir);
704     return nbFiles;
705 }
706 
707 #else
708 
709 static int UTIL_prepareFileList(const char *dirName,
710                                 char** bufStart, size_t* pos,
711                                 char** bufEnd, int followLinks)
712 {
713     (void)bufStart; (void)bufEnd; (void)pos; (void)followLinks;
714     UTIL_DISPLAYLEVEL(1, "Directory %s ignored (compiled without _WIN32 or _POSIX_C_SOURCE) \n", dirName);
715     return 0;
716 }
717 
718 #endif /* #ifdef _WIN32 */
719 
720 int UTIL_isCompressedFile(const char *inputName, const char *extensionList[])
721 {
722   const char* ext = UTIL_getFileExtension(inputName);
723   while(*extensionList!=NULL)
724   {
725     const int isCompressedExtension = strcmp(ext,*extensionList);
726     if(isCompressedExtension==0)
727       return 1;
728     ++extensionList;
729   }
730    return 0;
731 }
732 
733 /*Utility function to get file extension from file */
734 const char* UTIL_getFileExtension(const char* infilename)
735 {
736    const char* extension = strrchr(infilename, '.');
737    if(!extension || extension==infilename) return "";
738    return extension;
739 }
740 
741 static int pathnameHas2Dots(const char *pathname)
742 {
743     /* We need to figure out whether any ".." present in the path is a whole
744      * path token, which is the case if it is bordered on both sides by either
745      * the beginning/end of the path or by a directory separator.
746      */
747     const char *needle = pathname;
748     while (1) {
749         needle = strstr(needle, "..");
750 
751         if (needle == NULL) {
752             return 0;
753         }
754 
755         if ((needle == pathname || needle[-1] == PATH_SEP)
756          && (needle[2] == '\0' || needle[2] == PATH_SEP)) {
757             return 1;
758         }
759 
760         /* increment so we search for the next match */
761         needle++;
762     };
763     return 0;
764 }
765 
766 static int isFileNameValidForMirroredOutput(const char *filename)
767 {
768     return !pathnameHas2Dots(filename);
769 }
770 
771 
772 #define DIR_DEFAULT_MODE 0755
773 static mode_t getDirMode(const char *dirName)
774 {
775     stat_t st;
776     if (!UTIL_stat(dirName, &st)) {
777         UTIL_DISPLAY("zstd: failed to get DIR stats %s: %s\n", dirName, strerror(errno));
778         return DIR_DEFAULT_MODE;
779     }
780     if (!UTIL_isDirectoryStat(&st)) {
781         UTIL_DISPLAY("zstd: expected directory: %s\n", dirName);
782         return DIR_DEFAULT_MODE;
783     }
784     return st.st_mode;
785 }
786 
787 static int makeDir(const char *dir, mode_t mode)
788 {
789 #if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__)
790     int ret = _mkdir(dir);
791     (void) mode;
792 #else
793     int ret = mkdir(dir, mode);
794 #endif
795     if (ret != 0) {
796         if (errno == EEXIST)
797             return 0;
798         UTIL_DISPLAY("zstd: failed to create DIR %s: %s\n", dir, strerror(errno));
799     }
800     return ret;
801 }
802 
803 /* this function requires a mutable input string */
804 static void convertPathnameToDirName(char *pathname)
805 {
806     size_t len = 0;
807     char* pos = NULL;
808     /* get dir name from pathname similar to 'dirname()' */
809     assert(pathname != NULL);
810 
811     /* remove trailing '/' chars */
812     len = strlen(pathname);
813     assert(len > 0);
814     while (pathname[len] == PATH_SEP) {
815         pathname[len] = '\0';
816         len--;
817     }
818     if (len == 0) return;
819 
820     /* if input is a single file, return '.' instead. i.e.
821      * "xyz/abc/file.txt" => "xyz/abc"
822        "./file.txt"       => "."
823        "file.txt"         => "."
824      */
825     pos = strrchr(pathname, PATH_SEP);
826     if (pos == NULL) {
827         pathname[0] = '.';
828         pathname[1] = '\0';
829     } else {
830         *pos = '\0';
831     }
832 }
833 
834 /* pathname must be valid */
835 static const char* trimLeadingRootChar(const char *pathname)
836 {
837     assert(pathname != NULL);
838     if (pathname[0] == PATH_SEP)
839         return pathname + 1;
840     return pathname;
841 }
842 
843 /* pathname must be valid */
844 static const char* trimLeadingCurrentDirConst(const char *pathname)
845 {
846     assert(pathname != NULL);
847     if ((pathname[0] == '.') && (pathname[1] == PATH_SEP))
848         return pathname + 2;
849     return pathname;
850 }
851 
852 static char*
853 trimLeadingCurrentDir(char *pathname)
854 {
855     /* 'union charunion' can do const-cast without compiler warning */
856     union charunion {
857         char *chr;
858         const char* cchr;
859     } ptr;
860     ptr.cchr = trimLeadingCurrentDirConst(pathname);
861     return ptr.chr;
862 }
863 
864 /* remove leading './' or '/' chars here */
865 static const char * trimPath(const char *pathname)
866 {
867     return trimLeadingRootChar(
868             trimLeadingCurrentDirConst(pathname));
869 }
870 
871 static char* mallocAndJoin2Dir(const char *dir1, const char *dir2)
872 {
873     const size_t dir1Size = strlen(dir1);
874     const size_t dir2Size = strlen(dir2);
875     char *outDirBuffer, *buffer, trailingChar;
876 
877     assert(dir1 != NULL && dir2 != NULL);
878     outDirBuffer = (char *) malloc(dir1Size + dir2Size + 2);
879     CONTROL(outDirBuffer != NULL);
880 
881     memcpy(outDirBuffer, dir1, dir1Size);
882     outDirBuffer[dir1Size] = '\0';
883 
884     if (dir2[0] == '.')
885         return outDirBuffer;
886 
887     buffer = outDirBuffer + dir1Size;
888     trailingChar = *(buffer - 1);
889     if (trailingChar != PATH_SEP) {
890         *buffer = PATH_SEP;
891         buffer++;
892     }
893     memcpy(buffer, dir2, dir2Size);
894     buffer[dir2Size] = '\0';
895 
896     return outDirBuffer;
897 }
898 
899 /* this function will return NULL if input srcFileName is not valid name for mirrored output path */
900 char* UTIL_createMirroredDestDirName(const char* srcFileName, const char* outDirRootName)
901 {
902     char* pathname = NULL;
903     if (!isFileNameValidForMirroredOutput(srcFileName))
904         return NULL;
905 
906     pathname = mallocAndJoin2Dir(outDirRootName, trimPath(srcFileName));
907 
908     convertPathnameToDirName(pathname);
909     return pathname;
910 }
911 
912 static int
913 mirrorSrcDir(char* srcDirName, const char* outDirName)
914 {
915     mode_t srcMode;
916     int status = 0;
917     char* newDir = mallocAndJoin2Dir(outDirName, trimPath(srcDirName));
918     if (!newDir)
919         return -ENOMEM;
920 
921     srcMode = getDirMode(srcDirName);
922     status = makeDir(newDir, srcMode);
923     free(newDir);
924     return status;
925 }
926 
927 static int
928 mirrorSrcDirRecursive(char* srcDirName, const char* outDirName)
929 {
930     int status = 0;
931     char* pp = trimLeadingCurrentDir(srcDirName);
932     char* sp = NULL;
933 
934     while ((sp = strchr(pp, PATH_SEP)) != NULL) {
935         if (sp != pp) {
936             *sp = '\0';
937             status = mirrorSrcDir(srcDirName, outDirName);
938             if (status != 0)
939                 return status;
940             *sp = PATH_SEP;
941         }
942         pp = sp + 1;
943     }
944     status = mirrorSrcDir(srcDirName, outDirName);
945     return status;
946 }
947 
948 static void
949 makeMirroredDestDirsWithSameSrcDirMode(char** srcDirNames, unsigned nbFile, const char* outDirName)
950 {
951     unsigned int i = 0;
952     for (i = 0; i < nbFile; i++)
953         mirrorSrcDirRecursive(srcDirNames[i], outDirName);
954 }
955 
956 static int
957 firstIsParentOrSameDirOfSecond(const char* firstDir, const char* secondDir)
958 {
959     size_t firstDirLen  = strlen(firstDir),
960            secondDirLen = strlen(secondDir);
961     return firstDirLen <= secondDirLen &&
962            (secondDir[firstDirLen] == PATH_SEP || secondDir[firstDirLen] == '\0') &&
963            0 == strncmp(firstDir, secondDir, firstDirLen);
964 }
965 
966 static int compareDir(const void* pathname1, const void* pathname2) {
967     /* sort it after remove the leading '/'  or './'*/
968     const char* s1 = trimPath(*(char * const *) pathname1);
969     const char* s2 = trimPath(*(char * const *) pathname2);
970     return strcmp(s1, s2);
971 }
972 
973 static void
974 makeUniqueMirroredDestDirs(char** srcDirNames, unsigned nbFile, const char* outDirName)
975 {
976     unsigned int i = 0, uniqueDirNr = 0;
977     char** uniqueDirNames = NULL;
978 
979     if (nbFile == 0)
980         return;
981 
982     uniqueDirNames = (char** ) malloc(nbFile * sizeof (char *));
983     CONTROL(uniqueDirNames != NULL);
984 
985     /* if dirs is "a/b/c" and "a/b/c/d", we only need call:
986      * we just need "a/b/c/d" */
987     qsort((void *)srcDirNames, nbFile, sizeof(char*), compareDir);
988 
989     uniqueDirNr = 1;
990     uniqueDirNames[uniqueDirNr - 1] = srcDirNames[0];
991     for (i = 1; i < nbFile; i++) {
992         char* prevDirName = srcDirNames[i - 1];
993         char* currDirName = srcDirNames[i];
994 
995         /* note: we always compare trimmed path, i.e.:
996          * src dir of "./foo" and "/foo" will be both saved into:
997          * "outDirName/foo/" */
998         if (!firstIsParentOrSameDirOfSecond(trimPath(prevDirName),
999                                             trimPath(currDirName)))
1000             uniqueDirNr++;
1001 
1002         /* we need maintain original src dir name instead of trimmed
1003          * dir, so we can retrieve the original src dir's mode_t */
1004         uniqueDirNames[uniqueDirNr - 1] = currDirName;
1005     }
1006 
1007     makeMirroredDestDirsWithSameSrcDirMode(uniqueDirNames, uniqueDirNr, outDirName);
1008 
1009     free(uniqueDirNames);
1010 }
1011 
1012 static void
1013 makeMirroredDestDirs(char** srcFileNames, unsigned nbFile, const char* outDirName)
1014 {
1015     unsigned int i = 0;
1016     for (i = 0; i < nbFile; ++i)
1017         convertPathnameToDirName(srcFileNames[i]);
1018     makeUniqueMirroredDestDirs(srcFileNames, nbFile, outDirName);
1019 }
1020 
1021 void UTIL_mirrorSourceFilesDirectories(const char** inFileNames, unsigned int nbFile, const char* outDirName)
1022 {
1023     unsigned int i = 0, validFilenamesNr = 0;
1024     char** srcFileNames = (char **) malloc(nbFile * sizeof (char *));
1025     CONTROL(srcFileNames != NULL);
1026 
1027     /* check input filenames is valid */
1028     for (i = 0; i < nbFile; ++i) {
1029         if (isFileNameValidForMirroredOutput(inFileNames[i])) {
1030             char* fname = STRDUP(inFileNames[i]);
1031             CONTROL(fname != NULL);
1032             srcFileNames[validFilenamesNr++] = fname;
1033         }
1034     }
1035 
1036     if (validFilenamesNr > 0) {
1037         makeDir(outDirName, DIR_DEFAULT_MODE);
1038         makeMirroredDestDirs(srcFileNames, validFilenamesNr, outDirName);
1039     }
1040 
1041     for (i = 0; i < validFilenamesNr; i++)
1042         free(srcFileNames[i]);
1043     free(srcFileNames);
1044 }
1045 
1046 FileNamesTable*
1047 UTIL_createExpandedFNT(const char* const* inputNames, size_t nbIfns, int followLinks)
1048 {
1049     unsigned nbFiles;
1050     char* buf = (char*)malloc(LIST_SIZE_INCREASE);
1051     char* bufend = buf + LIST_SIZE_INCREASE;
1052 
1053     if (!buf) return NULL;
1054 
1055     {   size_t ifnNb, pos;
1056         for (ifnNb=0, pos=0, nbFiles=0; ifnNb<nbIfns; ifnNb++) {
1057             if (!UTIL_isDirectory(inputNames[ifnNb])) {
1058                 size_t const len = strlen(inputNames[ifnNb]);
1059                 if (buf + pos + len >= bufend) {
1060                     ptrdiff_t newListSize = (bufend - buf) + LIST_SIZE_INCREASE;
1061                     assert(newListSize >= 0);
1062                     buf = (char*)UTIL_realloc(buf, (size_t)newListSize);
1063                     if (!buf) return NULL;
1064                     bufend = buf + newListSize;
1065                 }
1066                 if (buf + pos + len < bufend) {
1067                     memcpy(buf+pos, inputNames[ifnNb], len+1);  /* including final \0 */
1068                     pos += len + 1;
1069                     nbFiles++;
1070                 }
1071             } else {
1072                 nbFiles += (unsigned)UTIL_prepareFileList(inputNames[ifnNb], &buf, &pos, &bufend, followLinks);
1073                 if (buf == NULL) return NULL;
1074     }   }   }
1075 
1076     /* note : even if nbFiles==0, function returns a valid, though empty, FileNamesTable* object */
1077 
1078     {   size_t ifnNb, pos;
1079         size_t const fntCapacity = nbFiles + 1;  /* minimum 1, allows adding one reference, typically stdin */
1080         const char** const fileNamesTable = (const char**)malloc(fntCapacity * sizeof(*fileNamesTable));
1081         if (!fileNamesTable) { free(buf); return NULL; }
1082 
1083         for (ifnNb = 0, pos = 0; ifnNb < nbFiles; ifnNb++) {
1084             fileNamesTable[ifnNb] = buf + pos;
1085             if (buf + pos > bufend) { free(buf); free((void*)fileNamesTable); return NULL; }
1086             pos += strlen(fileNamesTable[ifnNb]) + 1;
1087         }
1088         return UTIL_assembleFileNamesTable2(fileNamesTable, nbFiles, fntCapacity, buf);
1089     }
1090 }
1091 
1092 
1093 void UTIL_expandFNT(FileNamesTable** fnt, int followLinks)
1094 {
1095     FileNamesTable* const newFNT = UTIL_createExpandedFNT((*fnt)->fileNames, (*fnt)->tableSize, followLinks);
1096     CONTROL(newFNT != NULL);
1097     UTIL_freeFileNamesTable(*fnt);
1098     *fnt = newFNT;
1099 }
1100 
1101 FileNamesTable* UTIL_createFNT_fromROTable(const char** filenames, size_t nbFilenames)
1102 {
1103     size_t const sizeof_FNTable = nbFilenames * sizeof(*filenames);
1104     const char** const newFNTable = (const char**)malloc(sizeof_FNTable);
1105     if (newFNTable==NULL) return NULL;
1106     memcpy((void*)newFNTable, filenames, sizeof_FNTable);  /* void* : mitigate a Visual compiler bug or limitation */
1107     return UTIL_assembleFileNamesTable(newFNTable, nbFilenames, NULL);
1108 }
1109 
1110 
1111 /*-****************************************
1112 *  count the number of cores
1113 ******************************************/
1114 
1115 #if defined(_WIN32) || defined(WIN32)
1116 
1117 #include <windows.h>
1118 
1119 typedef BOOL(WINAPI* LPFN_GLPI)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD);
1120 
1121 DWORD CountSetBits(ULONG_PTR bitMask)
1122 {
1123     DWORD LSHIFT = sizeof(ULONG_PTR)*8 - 1;
1124     DWORD bitSetCount = 0;
1125     ULONG_PTR bitTest = (ULONG_PTR)1 << LSHIFT;
1126     DWORD i;
1127 
1128     for (i = 0; i <= LSHIFT; ++i)
1129     {
1130         bitSetCount += ((bitMask & bitTest)?1:0);
1131         bitTest/=2;
1132     }
1133 
1134     return bitSetCount;
1135 }
1136 
1137 int UTIL_countCores(int logical)
1138 {
1139     static int numCores = 0;
1140     if (numCores != 0) return numCores;
1141 
1142     {   LPFN_GLPI glpi;
1143         BOOL done = FALSE;
1144         PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL;
1145         PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL;
1146         DWORD returnLength = 0;
1147         size_t byteOffset = 0;
1148 
1149 #if defined(_MSC_VER)
1150 /* Visual Studio does not like the following cast */
1151 #   pragma warning( disable : 4054 )  /* conversion from function ptr to data ptr */
1152 #   pragma warning( disable : 4055 )  /* conversion from data ptr to function ptr */
1153 #endif
1154         glpi = (LPFN_GLPI)(void*)GetProcAddress(GetModuleHandle(TEXT("kernel32")),
1155                                                "GetLogicalProcessorInformation");
1156 
1157         if (glpi == NULL) {
1158             goto failed;
1159         }
1160 
1161         while(!done) {
1162             DWORD rc = glpi(buffer, &returnLength);
1163             if (FALSE == rc) {
1164                 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
1165                     if (buffer)
1166                         free(buffer);
1167                     buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc(returnLength);
1168 
1169                     if (buffer == NULL) {
1170                         perror("zstd");
1171                         exit(1);
1172                     }
1173                 } else {
1174                     /* some other error */
1175                     goto failed;
1176                 }
1177             } else {
1178                 done = TRUE;
1179         }   }
1180 
1181         ptr = buffer;
1182 
1183         while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) {
1184 
1185             if (ptr->Relationship == RelationProcessorCore) {
1186                 if (logical)
1187                     numCores += CountSetBits(ptr->ProcessorMask);
1188                 else
1189                     numCores++;
1190             }
1191 
1192             ptr++;
1193             byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
1194         }
1195 
1196         free(buffer);
1197 
1198         return numCores;
1199     }
1200 
1201 failed:
1202     /* try to fall back on GetSystemInfo */
1203     {   SYSTEM_INFO sysinfo;
1204         GetSystemInfo(&sysinfo);
1205         numCores = sysinfo.dwNumberOfProcessors;
1206         if (numCores == 0) numCores = 1; /* just in case */
1207     }
1208     return numCores;
1209 }
1210 
1211 #elif defined(__APPLE__)
1212 
1213 #include <sys/sysctl.h>
1214 
1215 /* Use apple-provided syscall
1216  * see: man 3 sysctl */
1217 int UTIL_countCores(int logical)
1218 {
1219     static S32 numCores = 0; /* apple specifies int32_t */
1220     if (numCores != 0) return numCores;
1221 
1222     {   size_t size = sizeof(S32);
1223         int const ret = sysctlbyname(logical ? "hw.logicalcpu" : "hw.physicalcpu", &numCores, &size, NULL, 0);
1224         if (ret != 0) {
1225             if (errno == ENOENT) {
1226                 /* entry not present, fall back on 1 */
1227                 numCores = 1;
1228             } else {
1229                 perror("zstd: can't get number of cpus");
1230                 exit(1);
1231             }
1232         }
1233 
1234         return numCores;
1235     }
1236 }
1237 
1238 #elif defined(__linux__)
1239 
1240 /* parse /proc/cpuinfo
1241  * siblings / cpu cores should give hyperthreading ratio
1242  * otherwise fall back on sysconf */
1243 int UTIL_countCores(int logical)
1244 {
1245     static int numCores = 0;
1246 
1247     if (numCores != 0) return numCores;
1248 
1249     numCores = (int)sysconf(_SC_NPROCESSORS_ONLN);
1250     if (numCores == -1) {
1251         /* value not queryable, fall back on 1 */
1252         return numCores = 1;
1253     }
1254 
1255     /* try to determine if there's hyperthreading */
1256     {   FILE* const cpuinfo = fopen("/proc/cpuinfo", "r");
1257 #define BUF_SIZE 80
1258         char buff[BUF_SIZE];
1259 
1260         int siblings = 0;
1261         int cpu_cores = 0;
1262         int ratio = 1;
1263 
1264         if (cpuinfo == NULL) {
1265             /* fall back on the sysconf value */
1266             return numCores;
1267         }
1268 
1269         /* assume the cpu cores/siblings values will be constant across all
1270          * present processors */
1271         while (!feof(cpuinfo)) {
1272             if (fgets(buff, BUF_SIZE, cpuinfo) != NULL) {
1273                 if (strncmp(buff, "siblings", 8) == 0) {
1274                     const char* const sep = strchr(buff, ':');
1275                     if (sep == NULL || *sep == '\0') {
1276                         /* formatting was broken? */
1277                         goto failed;
1278                     }
1279 
1280                     siblings = atoi(sep + 1);
1281                 }
1282                 if (strncmp(buff, "cpu cores", 9) == 0) {
1283                     const char* const sep = strchr(buff, ':');
1284                     if (sep == NULL || *sep == '\0') {
1285                         /* formatting was broken? */
1286                         goto failed;
1287                     }
1288 
1289                     cpu_cores = atoi(sep + 1);
1290                 }
1291             } else if (ferror(cpuinfo)) {
1292                 /* fall back on the sysconf value */
1293                 goto failed;
1294         }   }
1295         if (siblings && cpu_cores && siblings > cpu_cores) {
1296             ratio = siblings / cpu_cores;
1297         }
1298 
1299         if (ratio && numCores > ratio && !logical) {
1300             numCores = numCores / ratio;
1301         }
1302 
1303 failed:
1304         fclose(cpuinfo);
1305         return numCores;
1306     }
1307 }
1308 
1309 #elif defined(__FreeBSD__)
1310 
1311 #include <sys/param.h>
1312 #include <sys/sysctl.h>
1313 
1314 /* Use physical core sysctl when available
1315  * see: man 4 smp, man 3 sysctl */
1316 int UTIL_countCores(int logical)
1317 {
1318     static int numCores = 0; /* freebsd sysctl is native int sized */
1319 #if __FreeBSD_version >= 1300008
1320     static int perCore = 1;
1321 #endif
1322     if (numCores != 0) return numCores;
1323 
1324 #if __FreeBSD_version >= 1300008
1325     {   size_t size = sizeof(numCores);
1326         int ret = sysctlbyname("kern.smp.cores", &numCores, &size, NULL, 0);
1327         if (ret == 0) {
1328             if (logical) {
1329                 ret = sysctlbyname("kern.smp.threads_per_core", &perCore, &size, NULL, 0);
1330                 /* default to physical cores if logical cannot be read */
1331                 if (ret == 0)
1332                     numCores *= perCore;
1333             }
1334 
1335             return numCores;
1336         }
1337         if (errno != ENOENT) {
1338             perror("zstd: can't get number of cpus");
1339             exit(1);
1340         }
1341         /* sysctl not present, fall through to older sysconf method */
1342     }
1343 #else
1344     /* suppress unused parameter warning */
1345     (void) logical;
1346 #endif
1347 
1348     numCores = (int)sysconf(_SC_NPROCESSORS_ONLN);
1349     if (numCores == -1) {
1350         /* value not queryable, fall back on 1 */
1351         numCores = 1;
1352     }
1353     return numCores;
1354 }
1355 
1356 #elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__CYGWIN__)
1357 
1358 /* Use POSIX sysconf
1359  * see: man 3 sysconf */
1360 int UTIL_countCores(int logical)
1361 {
1362     static int numCores = 0;
1363 
1364     /* suppress unused parameter warning */
1365     (void)logical;
1366 
1367     if (numCores != 0) return numCores;
1368 
1369     numCores = (int)sysconf(_SC_NPROCESSORS_ONLN);
1370     if (numCores == -1) {
1371         /* value not queryable, fall back on 1 */
1372         return numCores = 1;
1373     }
1374     return numCores;
1375 }
1376 
1377 #else
1378 
1379 int UTIL_countCores(int logical)
1380 {
1381     /* assume 1 */
1382     return 1;
1383 }
1384 
1385 #endif
1386 
1387 int UTIL_countPhysicalCores(void)
1388 {
1389     return UTIL_countCores(0);
1390 }
1391 
1392 int UTIL_countLogicalCores(void)
1393 {
1394     return UTIL_countCores(1);
1395 }
1396 
1397 #if defined (__cplusplus)
1398 }
1399 #endif
1400