xref: /freebsd/sys/contrib/zstd/programs/util.c (revision f7cd7fe51c4140960ebea00410ed62894f5625d1)
1a0483764SConrad Meyer /*
237f1f268SConrad Meyer  * Copyright (c) 2016-2020, 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 */
31*f7cd7fe5SConrad 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  */
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 
90*f7cd7fe5SConrad Meyer int UTIL_requireUserConfirmation(const char* prompt, const char* abortMsg,
91*f7cd7fe5SConrad Meyer                                  const char* acceptableLetters, int hasStdinInput) {
92*f7cd7fe5SConrad Meyer     int ch, result;
93*f7cd7fe5SConrad Meyer 
94*f7cd7fe5SConrad Meyer     if (hasStdinInput) {
95*f7cd7fe5SConrad Meyer         UTIL_DISPLAY("stdin is an input - not proceeding.\n");
96*f7cd7fe5SConrad Meyer         return 1;
97*f7cd7fe5SConrad Meyer     }
98*f7cd7fe5SConrad Meyer 
99*f7cd7fe5SConrad Meyer     UTIL_DISPLAY("%s", prompt);
100*f7cd7fe5SConrad Meyer     ch = getchar();
101*f7cd7fe5SConrad Meyer     result = 0;
102*f7cd7fe5SConrad Meyer     if (strchr(acceptableLetters, ch) == NULL) {
103*f7cd7fe5SConrad Meyer         UTIL_DISPLAY("%s", abortMsg);
104*f7cd7fe5SConrad Meyer         result = 1;
105*f7cd7fe5SConrad Meyer     }
106*f7cd7fe5SConrad Meyer     /* flush the rest */
107*f7cd7fe5SConrad Meyer     while ((ch!=EOF) && (ch!='\n'))
108*f7cd7fe5SConrad Meyer         ch = getchar();
109*f7cd7fe5SConrad Meyer     return result;
110*f7cd7fe5SConrad Meyer }
111*f7cd7fe5SConrad 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 
124*f7cd7fe5SConrad Meyer int UTIL_stat(const char* filename, stat_t* statbuf)
125a0483764SConrad Meyer {
126a0483764SConrad Meyer #if defined(_MSC_VER)
127*f7cd7fe5SConrad Meyer     return !_stat64(filename, statbuf);
128*f7cd7fe5SConrad Meyer #elif defined(__MINGW32__) && defined (__MSVCRT__)
129*f7cd7fe5SConrad Meyer     return !_stati64(filename, statbuf);
130a0483764SConrad Meyer #else
131*f7cd7fe5SConrad Meyer     return !stat(filename, statbuf);
132a0483764SConrad Meyer #endif
133a0483764SConrad Meyer }
134a0483764SConrad Meyer 
135a0483764SConrad Meyer int UTIL_isRegularFile(const char* infilename)
136a0483764SConrad Meyer {
137a0483764SConrad Meyer     stat_t statbuf;
138*f7cd7fe5SConrad Meyer     return UTIL_stat(infilename, &statbuf) && UTIL_isRegularFileStat(&statbuf);
139a0483764SConrad Meyer }
140a0483764SConrad Meyer 
141*f7cd7fe5SConrad Meyer int UTIL_isRegularFileStat(const stat_t* statbuf)
142a0483764SConrad Meyer {
143a0483764SConrad Meyer #if defined(_MSC_VER)
144*f7cd7fe5SConrad Meyer     return (statbuf->st_mode & S_IFREG) != 0;
145a0483764SConrad Meyer #else
146*f7cd7fe5SConrad 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 */
151*f7cd7fe5SConrad Meyer int UTIL_chmod(char const* filename, const stat_t* statbuf, mode_t permissions)
15237f1f268SConrad Meyer {
153*f7cd7fe5SConrad Meyer     stat_t localStatBuf;
154*f7cd7fe5SConrad Meyer     if (statbuf == NULL) {
155*f7cd7fe5SConrad Meyer         if (!UTIL_stat(filename, &localStatBuf)) return 0;
156*f7cd7fe5SConrad Meyer         statbuf = &localStatBuf;
157*f7cd7fe5SConrad Meyer     }
158*f7cd7fe5SConrad 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*f7cd7fe5SConrad Meyer int UTIL_setFileStat(const char *filename, const stat_t *statbuf)
163a0483764SConrad Meyer {
164a0483764SConrad Meyer     int res = 0;
165a0483764SConrad Meyer 
166*f7cd7fe5SConrad Meyer     stat_t curStatBuf;
167*f7cd7fe5SConrad Meyer     if (!UTIL_stat(filename, &curStatBuf) || !UTIL_isRegularFileStat(&curStatBuf))
168a0483764SConrad Meyer         return -1;
169a0483764SConrad Meyer 
1709cbefe25SConrad Meyer     /* set access and modification times */
17137f1f268SConrad Meyer     /* We check that st_mtime is a macro here in order to give us confidence
17237f1f268SConrad Meyer      * that struct stat has a struct timespec st_mtim member. We need this
17337f1f268SConrad Meyer      * check because there are some platforms that claim to be POSIX 2008
17437f1f268SConrad Meyer      * compliant but which do not have st_mtim... */
17537f1f268SConrad Meyer #if (PLATFORM_POSIX_VERSION >= 200809L) && defined(st_mtime)
1769cbefe25SConrad Meyer     {
1779cbefe25SConrad Meyer         /* (atime, mtime) */
1789cbefe25SConrad Meyer         struct timespec timebuf[2] = { {0, UTIME_NOW} };
1799cbefe25SConrad Meyer         timebuf[1] = statbuf->st_mtim;
1809cbefe25SConrad Meyer         res += utimensat(AT_FDCWD, filename, timebuf, 0);
1819cbefe25SConrad Meyer     }
18237f1f268SConrad Meyer #else
18337f1f268SConrad Meyer     {
18437f1f268SConrad Meyer         struct utimbuf timebuf;
18537f1f268SConrad Meyer         timebuf.actime = time(NULL);
18637f1f268SConrad Meyer         timebuf.modtime = statbuf->st_mtime;
18737f1f268SConrad Meyer         res += utime(filename, &timebuf);
18837f1f268SConrad Meyer     }
1899cbefe25SConrad Meyer #endif
190a0483764SConrad Meyer 
191a0483764SConrad Meyer #if !defined(_WIN32)
192a0483764SConrad Meyer     res += chown(filename, statbuf->st_uid, statbuf->st_gid);  /* Copy ownership */
193a0483764SConrad Meyer #endif
194a0483764SConrad Meyer 
195*f7cd7fe5SConrad Meyer     res += UTIL_chmod(filename, &curStatBuf, statbuf->st_mode & 07777);  /* Copy file permissions */
196a0483764SConrad Meyer 
197a0483764SConrad Meyer     errno = 0;
198a0483764SConrad Meyer     return -res; /* number of errors is returned */
199a0483764SConrad Meyer }
200a0483764SConrad Meyer 
20137f1f268SConrad Meyer int UTIL_isDirectory(const char* infilename)
202a0483764SConrad Meyer {
203a0483764SConrad Meyer     stat_t statbuf;
204*f7cd7fe5SConrad Meyer     return UTIL_stat(infilename, &statbuf) && UTIL_isDirectoryStat(&statbuf);
205*f7cd7fe5SConrad Meyer }
206*f7cd7fe5SConrad Meyer 
207*f7cd7fe5SConrad Meyer int UTIL_isDirectoryStat(const stat_t* statbuf)
208*f7cd7fe5SConrad Meyer {
209a0483764SConrad Meyer #if defined(_MSC_VER)
210*f7cd7fe5SConrad Meyer     return (statbuf->st_mode & _S_IFDIR) != 0;
211a0483764SConrad Meyer #else
212*f7cd7fe5SConrad Meyer     return S_ISDIR(statbuf->st_mode) != 0;
213a0483764SConrad Meyer #endif
214a0483764SConrad Meyer }
215a0483764SConrad Meyer 
2169cbefe25SConrad Meyer int UTIL_compareStr(const void *p1, const void *p2) {
2179cbefe25SConrad Meyer     return strcmp(* (char * const *) p1, * (char * const *) p2);
2189cbefe25SConrad Meyer }
2199cbefe25SConrad Meyer 
2209cbefe25SConrad Meyer int UTIL_isSameFile(const char* fName1, const char* fName2)
2212b9c00cbSConrad Meyer {
2229cbefe25SConrad Meyer     assert(fName1 != NULL); assert(fName2 != NULL);
2239cbefe25SConrad Meyer #if defined(_MSC_VER) || defined(_WIN32)
2242b9c00cbSConrad Meyer     /* note : Visual does not support file identification by inode.
2259cbefe25SConrad Meyer      *        inode does not work on Windows, even with a posix layer, like msys2.
2262b9c00cbSConrad Meyer      *        The following work-around is limited to detecting exact name repetition only,
2272b9c00cbSConrad Meyer      *        aka `filename` is considered different from `subdir/../filename` */
2289cbefe25SConrad Meyer     return !strcmp(fName1, fName2);
2292b9c00cbSConrad Meyer #else
2309cbefe25SConrad Meyer     {   stat_t file1Stat;
2312b9c00cbSConrad Meyer         stat_t file2Stat;
232*f7cd7fe5SConrad Meyer         return UTIL_stat(fName1, &file1Stat)
233*f7cd7fe5SConrad Meyer             && UTIL_stat(fName2, &file2Stat)
2342b9c00cbSConrad Meyer             && (file1Stat.st_dev == file2Stat.st_dev)
2352b9c00cbSConrad Meyer             && (file1Stat.st_ino == file2Stat.st_ino);
2369cbefe25SConrad Meyer     }
2372b9c00cbSConrad Meyer #endif
2382b9c00cbSConrad Meyer }
2392b9c00cbSConrad Meyer 
24037f1f268SConrad Meyer /* UTIL_isFIFO : distinguish named pipes */
24137f1f268SConrad Meyer int UTIL_isFIFO(const char* infilename)
2429cbefe25SConrad Meyer {
2439cbefe25SConrad Meyer /* macro guards, as defined in : https://linux.die.net/man/2/lstat */
2449cbefe25SConrad Meyer #if PLATFORM_POSIX_VERSION >= 200112L
2459cbefe25SConrad Meyer     stat_t statbuf;
246*f7cd7fe5SConrad Meyer     if (UTIL_stat(infilename, &statbuf) && UTIL_isFIFOStat(&statbuf)) return 1;
2479cbefe25SConrad Meyer #endif
2489cbefe25SConrad Meyer     (void)infilename;
2499cbefe25SConrad Meyer     return 0;
2509cbefe25SConrad Meyer }
2519cbefe25SConrad Meyer 
252*f7cd7fe5SConrad Meyer /* UTIL_isFIFO : distinguish named pipes */
253*f7cd7fe5SConrad Meyer int UTIL_isFIFOStat(const stat_t* statbuf)
254*f7cd7fe5SConrad Meyer {
255*f7cd7fe5SConrad Meyer /* macro guards, as defined in : https://linux.die.net/man/2/lstat */
256*f7cd7fe5SConrad Meyer #if PLATFORM_POSIX_VERSION >= 200112L
257*f7cd7fe5SConrad Meyer     if (S_ISFIFO(statbuf->st_mode)) return 1;
258*f7cd7fe5SConrad Meyer #endif
259*f7cd7fe5SConrad Meyer     (void)statbuf;
260*f7cd7fe5SConrad Meyer     return 0;
261*f7cd7fe5SConrad Meyer }
262*f7cd7fe5SConrad Meyer 
26337f1f268SConrad Meyer int UTIL_isLink(const char* infilename)
264a0483764SConrad Meyer {
265a0483764SConrad Meyer /* macro guards, as defined in : https://linux.die.net/man/2/lstat */
2664d3f1eafSConrad Meyer #if PLATFORM_POSIX_VERSION >= 200112L
267a0483764SConrad Meyer     stat_t statbuf;
26837f1f268SConrad Meyer     int const r = lstat(infilename, &statbuf);
269a0483764SConrad Meyer     if (!r && S_ISLNK(statbuf.st_mode)) return 1;
270a0483764SConrad Meyer #endif
271a0483764SConrad Meyer     (void)infilename;
272a0483764SConrad Meyer     return 0;
273a0483764SConrad Meyer }
274a0483764SConrad Meyer 
275a0483764SConrad Meyer U64 UTIL_getFileSize(const char* infilename)
276a0483764SConrad Meyer {
277*f7cd7fe5SConrad Meyer     stat_t statbuf;
278*f7cd7fe5SConrad Meyer     if (!UTIL_stat(infilename, &statbuf)) return UTIL_FILESIZE_UNKNOWN;
279*f7cd7fe5SConrad Meyer     return UTIL_getFileSizeStat(&statbuf);
280a0483764SConrad Meyer }
281*f7cd7fe5SConrad Meyer 
282*f7cd7fe5SConrad Meyer U64 UTIL_getFileSizeStat(const stat_t* statbuf)
283*f7cd7fe5SConrad Meyer {
284*f7cd7fe5SConrad Meyer     if (!UTIL_isRegularFileStat(statbuf)) return UTIL_FILESIZE_UNKNOWN;
285*f7cd7fe5SConrad Meyer #if defined(_MSC_VER)
286*f7cd7fe5SConrad Meyer     if (!(statbuf->st_mode & S_IFREG)) return UTIL_FILESIZE_UNKNOWN;
287*f7cd7fe5SConrad Meyer #elif defined(__MINGW32__) && defined (__MSVCRT__)
288*f7cd7fe5SConrad Meyer     if (!(statbuf->st_mode & S_IFREG)) return UTIL_FILESIZE_UNKNOWN;
289*f7cd7fe5SConrad Meyer #else
290*f7cd7fe5SConrad Meyer     if (!S_ISREG(statbuf->st_mode)) return UTIL_FILESIZE_UNKNOWN;
291*f7cd7fe5SConrad Meyer #endif
292*f7cd7fe5SConrad Meyer     return (U64)statbuf->st_size;
293a0483764SConrad Meyer }
294a0483764SConrad Meyer 
295a0483764SConrad Meyer 
29637f1f268SConrad Meyer U64 UTIL_getTotalFileSize(const char* const * fileNamesTable, unsigned nbFiles)
297a0483764SConrad Meyer {
298a0483764SConrad Meyer     U64 total = 0;
299a0483764SConrad Meyer     unsigned n;
300a0483764SConrad Meyer     for (n=0; n<nbFiles; n++) {
301a0483764SConrad Meyer         U64 const size = UTIL_getFileSize(fileNamesTable[n]);
30237f1f268SConrad Meyer         if (size == UTIL_FILESIZE_UNKNOWN) return UTIL_FILESIZE_UNKNOWN;
303a0483764SConrad Meyer         total += size;
304a0483764SConrad Meyer     }
30537f1f268SConrad Meyer     return total;
30637f1f268SConrad Meyer }
30737f1f268SConrad Meyer 
30837f1f268SConrad Meyer 
30937f1f268SConrad Meyer /* condition : @file must be valid, and not have reached its end.
31037f1f268SConrad Meyer  * @return : length of line written into @buf, ended with `\0` instead of '\n',
31137f1f268SConrad Meyer  *           or 0, if there is no new line */
31237f1f268SConrad Meyer static size_t readLineFromFile(char* buf, size_t len, FILE* file)
31337f1f268SConrad Meyer {
31437f1f268SConrad Meyer     assert(!feof(file));
31537f1f268SConrad Meyer     /* Work around Cygwin problem when len == 1 it returns NULL. */
31637f1f268SConrad Meyer     if (len <= 1) return 0;
31737f1f268SConrad Meyer     CONTROL( fgets(buf, (int) len, file) );
31837f1f268SConrad Meyer     {   size_t linelen = strlen(buf);
31937f1f268SConrad Meyer         if (strlen(buf)==0) return 0;
32037f1f268SConrad Meyer         if (buf[linelen-1] == '\n') linelen--;
32137f1f268SConrad Meyer         buf[linelen] = '\0';
32237f1f268SConrad Meyer         return linelen+1;
32337f1f268SConrad Meyer     }
32437f1f268SConrad Meyer }
32537f1f268SConrad Meyer 
32637f1f268SConrad Meyer /* Conditions :
32737f1f268SConrad Meyer  *   size of @inputFileName file must be < @dstCapacity
32837f1f268SConrad Meyer  *   @dst must be initialized
32937f1f268SConrad Meyer  * @return : nb of lines
33037f1f268SConrad Meyer  *       or -1 if there's an error
33137f1f268SConrad Meyer  */
33237f1f268SConrad Meyer static int
33337f1f268SConrad Meyer readLinesFromFile(void* dst, size_t dstCapacity,
33437f1f268SConrad Meyer             const char* inputFileName)
33537f1f268SConrad Meyer {
33637f1f268SConrad Meyer     int nbFiles = 0;
33737f1f268SConrad Meyer     size_t pos = 0;
33837f1f268SConrad Meyer     char* const buf = (char*)dst;
33937f1f268SConrad Meyer     FILE* const inputFile = fopen(inputFileName, "r");
34037f1f268SConrad Meyer 
34137f1f268SConrad Meyer     assert(dst != NULL);
34237f1f268SConrad Meyer 
34337f1f268SConrad Meyer     if(!inputFile) {
34437f1f268SConrad Meyer         if (g_utilDisplayLevel >= 1) perror("zstd:util:readLinesFromFile");
34537f1f268SConrad Meyer         return -1;
34637f1f268SConrad Meyer     }
34737f1f268SConrad Meyer 
34837f1f268SConrad Meyer     while ( !feof(inputFile) ) {
34937f1f268SConrad Meyer         size_t const lineLength = readLineFromFile(buf+pos, dstCapacity-pos, inputFile);
35037f1f268SConrad Meyer         if (lineLength == 0) break;
35137f1f268SConrad Meyer         assert(pos + lineLength < dstCapacity);
35237f1f268SConrad Meyer         pos += lineLength;
35337f1f268SConrad Meyer         ++nbFiles;
35437f1f268SConrad Meyer     }
35537f1f268SConrad Meyer 
35637f1f268SConrad Meyer     CONTROL( fclose(inputFile) == 0 );
35737f1f268SConrad Meyer 
35837f1f268SConrad Meyer     return nbFiles;
35937f1f268SConrad Meyer }
36037f1f268SConrad Meyer 
36137f1f268SConrad Meyer /*Note: buf is not freed in case function successfully created table because filesTable->fileNames[0] = buf*/
36237f1f268SConrad Meyer FileNamesTable*
36337f1f268SConrad Meyer UTIL_createFileNamesTable_fromFileName(const char* inputFileName)
36437f1f268SConrad Meyer {
36537f1f268SConrad Meyer     size_t nbFiles = 0;
36637f1f268SConrad Meyer     char* buf;
36737f1f268SConrad Meyer     size_t bufSize;
36837f1f268SConrad Meyer     size_t pos = 0;
369*f7cd7fe5SConrad Meyer     stat_t statbuf;
37037f1f268SConrad Meyer 
371*f7cd7fe5SConrad Meyer     if (!UTIL_stat(inputFileName, &statbuf) || !UTIL_isRegularFileStat(&statbuf))
37237f1f268SConrad Meyer         return NULL;
37337f1f268SConrad Meyer 
374*f7cd7fe5SConrad Meyer     {   U64 const inputFileSize = UTIL_getFileSizeStat(&statbuf);
37537f1f268SConrad Meyer         if(inputFileSize > MAX_FILE_OF_FILE_NAMES_SIZE)
37637f1f268SConrad Meyer             return NULL;
37737f1f268SConrad Meyer         bufSize = (size_t)(inputFileSize + 1); /* (+1) to add '\0' at the end of last filename */
37837f1f268SConrad Meyer     }
37937f1f268SConrad Meyer 
38037f1f268SConrad Meyer     buf = (char*) malloc(bufSize);
38137f1f268SConrad Meyer     CONTROL( buf != NULL );
38237f1f268SConrad Meyer 
38337f1f268SConrad Meyer     {   int const ret_nbFiles = readLinesFromFile(buf, bufSize, inputFileName);
38437f1f268SConrad Meyer 
38537f1f268SConrad Meyer         if (ret_nbFiles <= 0) {
38637f1f268SConrad Meyer           free(buf);
38737f1f268SConrad Meyer           return NULL;
38837f1f268SConrad Meyer         }
38937f1f268SConrad Meyer         nbFiles = (size_t)ret_nbFiles;
39037f1f268SConrad Meyer     }
39137f1f268SConrad Meyer 
39237f1f268SConrad Meyer     {   const char** filenamesTable = (const char**) malloc(nbFiles * sizeof(*filenamesTable));
39337f1f268SConrad Meyer         CONTROL(filenamesTable != NULL);
39437f1f268SConrad Meyer 
39537f1f268SConrad Meyer         {   size_t fnb;
39637f1f268SConrad Meyer             for (fnb = 0, pos = 0; fnb < nbFiles; fnb++) {
39737f1f268SConrad Meyer                 filenamesTable[fnb] = buf+pos;
39837f1f268SConrad Meyer                 pos += strlen(buf+pos)+1;  /* +1 for the finishing `\0` */
39937f1f268SConrad Meyer         }   }
40037f1f268SConrad Meyer         assert(pos <= bufSize);
40137f1f268SConrad Meyer 
40237f1f268SConrad Meyer         return UTIL_assembleFileNamesTable(filenamesTable, nbFiles, buf);
40337f1f268SConrad Meyer     }
40437f1f268SConrad Meyer }
40537f1f268SConrad Meyer 
40637f1f268SConrad Meyer static FileNamesTable*
40737f1f268SConrad Meyer UTIL_assembleFileNamesTable2(const char** filenames, size_t tableSize, size_t tableCapacity, char* buf)
40837f1f268SConrad Meyer {
40937f1f268SConrad Meyer     FileNamesTable* const table = (FileNamesTable*) malloc(sizeof(*table));
41037f1f268SConrad Meyer     CONTROL(table != NULL);
41137f1f268SConrad Meyer     table->fileNames = filenames;
41237f1f268SConrad Meyer     table->buf = buf;
41337f1f268SConrad Meyer     table->tableSize = tableSize;
41437f1f268SConrad Meyer     table->tableCapacity = tableCapacity;
41537f1f268SConrad Meyer     return table;
41637f1f268SConrad Meyer }
41737f1f268SConrad Meyer 
41837f1f268SConrad Meyer FileNamesTable*
41937f1f268SConrad Meyer UTIL_assembleFileNamesTable(const char** filenames, size_t tableSize, char* buf)
42037f1f268SConrad Meyer {
42137f1f268SConrad Meyer     return UTIL_assembleFileNamesTable2(filenames, tableSize, tableSize, buf);
42237f1f268SConrad Meyer }
42337f1f268SConrad Meyer 
42437f1f268SConrad Meyer void UTIL_freeFileNamesTable(FileNamesTable* table)
42537f1f268SConrad Meyer {
42637f1f268SConrad Meyer     if (table==NULL) return;
42737f1f268SConrad Meyer     free((void*)table->fileNames);
42837f1f268SConrad Meyer     free(table->buf);
42937f1f268SConrad Meyer     free(table);
43037f1f268SConrad Meyer }
43137f1f268SConrad Meyer 
43237f1f268SConrad Meyer FileNamesTable* UTIL_allocateFileNamesTable(size_t tableSize)
43337f1f268SConrad Meyer {
43437f1f268SConrad Meyer     const char** const fnTable = (const char**)malloc(tableSize * sizeof(*fnTable));
43537f1f268SConrad Meyer     FileNamesTable* fnt;
43637f1f268SConrad Meyer     if (fnTable==NULL) return NULL;
43737f1f268SConrad Meyer     fnt = UTIL_assembleFileNamesTable(fnTable, tableSize, NULL);
43837f1f268SConrad Meyer     fnt->tableSize = 0;   /* the table is empty */
43937f1f268SConrad Meyer     return fnt;
44037f1f268SConrad Meyer }
44137f1f268SConrad Meyer 
44237f1f268SConrad Meyer void UTIL_refFilename(FileNamesTable* fnt, const char* filename)
44337f1f268SConrad Meyer {
44437f1f268SConrad Meyer     assert(fnt->tableSize < fnt->tableCapacity);
44537f1f268SConrad Meyer     fnt->fileNames[fnt->tableSize] = filename;
44637f1f268SConrad Meyer     fnt->tableSize++;
44737f1f268SConrad Meyer }
44837f1f268SConrad Meyer 
44937f1f268SConrad Meyer static size_t getTotalTableSize(FileNamesTable* table)
45037f1f268SConrad Meyer {
45137f1f268SConrad Meyer     size_t fnb = 0, totalSize = 0;
45237f1f268SConrad Meyer     for(fnb = 0 ; fnb < table->tableSize && table->fileNames[fnb] ; ++fnb) {
45337f1f268SConrad Meyer         totalSize += strlen(table->fileNames[fnb]) + 1; /* +1 to add '\0' at the end of each fileName */
45437f1f268SConrad Meyer     }
45537f1f268SConrad Meyer     return totalSize;
45637f1f268SConrad Meyer }
45737f1f268SConrad Meyer 
45837f1f268SConrad Meyer FileNamesTable*
45937f1f268SConrad Meyer UTIL_mergeFileNamesTable(FileNamesTable* table1, FileNamesTable* table2)
46037f1f268SConrad Meyer {
46137f1f268SConrad Meyer     unsigned newTableIdx = 0;
46237f1f268SConrad Meyer     size_t pos = 0;
46337f1f268SConrad Meyer     size_t newTotalTableSize;
46437f1f268SConrad Meyer     char* buf;
46537f1f268SConrad Meyer 
46637f1f268SConrad Meyer     FileNamesTable* const newTable = UTIL_assembleFileNamesTable(NULL, 0, NULL);
46737f1f268SConrad Meyer     CONTROL( newTable != NULL );
46837f1f268SConrad Meyer 
46937f1f268SConrad Meyer     newTotalTableSize = getTotalTableSize(table1) + getTotalTableSize(table2);
47037f1f268SConrad Meyer 
47137f1f268SConrad Meyer     buf = (char*) calloc(newTotalTableSize, sizeof(*buf));
47237f1f268SConrad Meyer     CONTROL ( buf != NULL );
47337f1f268SConrad Meyer 
47437f1f268SConrad Meyer     newTable->buf = buf;
47537f1f268SConrad Meyer     newTable->tableSize = table1->tableSize + table2->tableSize;
47637f1f268SConrad Meyer     newTable->fileNames = (const char **) calloc(newTable->tableSize, sizeof(*(newTable->fileNames)));
47737f1f268SConrad Meyer     CONTROL ( newTable->fileNames != NULL );
47837f1f268SConrad Meyer 
47937f1f268SConrad Meyer     {   unsigned idx1;
48037f1f268SConrad Meyer         for( idx1=0 ; (idx1 < table1->tableSize) && table1->fileNames[idx1] && (pos < newTotalTableSize); ++idx1, ++newTableIdx) {
48137f1f268SConrad Meyer             size_t const curLen = strlen(table1->fileNames[idx1]);
48237f1f268SConrad Meyer             memcpy(buf+pos, table1->fileNames[idx1], curLen);
48337f1f268SConrad Meyer             assert(newTableIdx <= newTable->tableSize);
48437f1f268SConrad Meyer             newTable->fileNames[newTableIdx] = buf+pos;
48537f1f268SConrad Meyer             pos += curLen+1;
48637f1f268SConrad Meyer     }   }
48737f1f268SConrad Meyer 
48837f1f268SConrad Meyer     {   unsigned idx2;
48937f1f268SConrad Meyer         for( idx2=0 ; (idx2 < table2->tableSize) && table2->fileNames[idx2] && (pos < newTotalTableSize) ; ++idx2, ++newTableIdx) {
49037f1f268SConrad Meyer             size_t const curLen = strlen(table2->fileNames[idx2]);
49137f1f268SConrad Meyer             memcpy(buf+pos, table2->fileNames[idx2], curLen);
49237f1f268SConrad Meyer             assert(newTableIdx <= newTable->tableSize);
49337f1f268SConrad Meyer             newTable->fileNames[newTableIdx] = buf+pos;
49437f1f268SConrad Meyer             pos += curLen+1;
49537f1f268SConrad Meyer     }   }
49637f1f268SConrad Meyer     assert(pos <= newTotalTableSize);
49737f1f268SConrad Meyer     newTable->tableSize = newTableIdx;
49837f1f268SConrad Meyer 
49937f1f268SConrad Meyer     UTIL_freeFileNamesTable(table1);
50037f1f268SConrad Meyer     UTIL_freeFileNamesTable(table2);
50137f1f268SConrad Meyer 
50237f1f268SConrad Meyer     return newTable;
503a0483764SConrad Meyer }
504a0483764SConrad Meyer 
505a0483764SConrad Meyer #ifdef _WIN32
50637f1f268SConrad Meyer static int UTIL_prepareFileList(const char* dirName,
50737f1f268SConrad Meyer                                 char** bufStart, size_t* pos,
50837f1f268SConrad Meyer                                 char** bufEnd, int followLinks)
509a0483764SConrad Meyer {
510a0483764SConrad Meyer     char* path;
51137f1f268SConrad Meyer     size_t dirLength, pathLength;
51237f1f268SConrad Meyer     int nbFiles = 0;
513a0483764SConrad Meyer     WIN32_FIND_DATAA cFile;
514a0483764SConrad Meyer     HANDLE hFile;
515a0483764SConrad Meyer 
51637f1f268SConrad Meyer     dirLength = strlen(dirName);
517a0483764SConrad Meyer     path = (char*) malloc(dirLength + 3);
518a0483764SConrad Meyer     if (!path) return 0;
519a0483764SConrad Meyer 
520a0483764SConrad Meyer     memcpy(path, dirName, dirLength);
521a0483764SConrad Meyer     path[dirLength] = '\\';
522a0483764SConrad Meyer     path[dirLength+1] = '*';
523a0483764SConrad Meyer     path[dirLength+2] = 0;
524a0483764SConrad Meyer 
525a0483764SConrad Meyer     hFile=FindFirstFileA(path, &cFile);
526a0483764SConrad Meyer     if (hFile == INVALID_HANDLE_VALUE) {
527a0483764SConrad Meyer         UTIL_DISPLAYLEVEL(1, "Cannot open directory '%s'\n", dirName);
528a0483764SConrad Meyer         return 0;
529a0483764SConrad Meyer     }
530a0483764SConrad Meyer     free(path);
531a0483764SConrad Meyer 
532a0483764SConrad Meyer     do {
53337f1f268SConrad Meyer         size_t const fnameLength = strlen(cFile.cFileName);
534a0483764SConrad Meyer         path = (char*) malloc(dirLength + fnameLength + 2);
535a0483764SConrad Meyer         if (!path) { FindClose(hFile); return 0; }
536a0483764SConrad Meyer         memcpy(path, dirName, dirLength);
537a0483764SConrad Meyer         path[dirLength] = '\\';
538a0483764SConrad Meyer         memcpy(path+dirLength+1, cFile.cFileName, fnameLength);
539a0483764SConrad Meyer         pathLength = dirLength+1+fnameLength;
540a0483764SConrad Meyer         path[pathLength] = 0;
541a0483764SConrad Meyer         if (cFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
542a0483764SConrad Meyer             if ( strcmp (cFile.cFileName, "..") == 0
543a0483764SConrad Meyer               || strcmp (cFile.cFileName, ".") == 0 )
544a0483764SConrad Meyer                 continue;
545a0483764SConrad Meyer             /* Recursively call "UTIL_prepareFileList" with the new path. */
546a0483764SConrad Meyer             nbFiles += UTIL_prepareFileList(path, bufStart, pos, bufEnd, followLinks);
547a0483764SConrad Meyer             if (*bufStart == NULL) { free(path); FindClose(hFile); return 0; }
548a0483764SConrad Meyer         } else if ( (cFile.dwFileAttributes & FILE_ATTRIBUTE_NORMAL)
549a0483764SConrad Meyer                  || (cFile.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
550a0483764SConrad Meyer                  || (cFile.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) ) {
551a0483764SConrad Meyer             if (*bufStart + *pos + pathLength >= *bufEnd) {
552a0483764SConrad Meyer                 ptrdiff_t const newListSize = (*bufEnd - *bufStart) + LIST_SIZE_INCREASE;
553a0483764SConrad Meyer                 *bufStart = (char*)UTIL_realloc(*bufStart, newListSize);
554a0483764SConrad Meyer                 if (*bufStart == NULL) { free(path); FindClose(hFile); return 0; }
555a0483764SConrad Meyer                 *bufEnd = *bufStart + newListSize;
556a0483764SConrad Meyer             }
557a0483764SConrad Meyer             if (*bufStart + *pos + pathLength < *bufEnd) {
558a0483764SConrad Meyer                 memcpy(*bufStart + *pos, path, pathLength+1 /* include final \0 */);
559a0483764SConrad Meyer                 *pos += pathLength + 1;
560a0483764SConrad Meyer                 nbFiles++;
56137f1f268SConrad Meyer         }   }
562a0483764SConrad Meyer         free(path);
563a0483764SConrad Meyer     } while (FindNextFileA(hFile, &cFile));
564a0483764SConrad Meyer 
565a0483764SConrad Meyer     FindClose(hFile);
566a0483764SConrad Meyer     return nbFiles;
567a0483764SConrad Meyer }
568a0483764SConrad Meyer 
569a0483764SConrad Meyer #elif defined(__linux__) || (PLATFORM_POSIX_VERSION >= 200112L)  /* opendir, readdir require POSIX.1-2001 */
570a0483764SConrad Meyer 
57137f1f268SConrad Meyer static int UTIL_prepareFileList(const char *dirName,
57237f1f268SConrad Meyer                                 char** bufStart, size_t* pos,
57337f1f268SConrad Meyer                                 char** bufEnd, int followLinks)
574a0483764SConrad Meyer {
575a0483764SConrad Meyer     DIR* dir;
576a0483764SConrad Meyer     struct dirent * entry;
57737f1f268SConrad Meyer     size_t dirLength;
5789cbefe25SConrad Meyer     int nbFiles = 0;
579a0483764SConrad Meyer 
580a0483764SConrad Meyer     if (!(dir = opendir(dirName))) {
581a0483764SConrad Meyer         UTIL_DISPLAYLEVEL(1, "Cannot open directory '%s': %s\n", dirName, strerror(errno));
582a0483764SConrad Meyer         return 0;
583a0483764SConrad Meyer     }
584a0483764SConrad Meyer 
5859cbefe25SConrad Meyer     dirLength = strlen(dirName);
586a0483764SConrad Meyer     errno = 0;
587a0483764SConrad Meyer     while ((entry = readdir(dir)) != NULL) {
58837f1f268SConrad Meyer         char* path;
58937f1f268SConrad Meyer         size_t fnameLength, pathLength;
590a0483764SConrad Meyer         if (strcmp (entry->d_name, "..") == 0 ||
591a0483764SConrad Meyer             strcmp (entry->d_name, ".") == 0) continue;
5929cbefe25SConrad Meyer         fnameLength = strlen(entry->d_name);
593a0483764SConrad Meyer         path = (char*) malloc(dirLength + fnameLength + 2);
594a0483764SConrad Meyer         if (!path) { closedir(dir); return 0; }
595a0483764SConrad Meyer         memcpy(path, dirName, dirLength);
596a0483764SConrad Meyer 
597a0483764SConrad Meyer         path[dirLength] = '/';
598a0483764SConrad Meyer         memcpy(path+dirLength+1, entry->d_name, fnameLength);
599a0483764SConrad Meyer         pathLength = dirLength+1+fnameLength;
600a0483764SConrad Meyer         path[pathLength] = 0;
601a0483764SConrad Meyer 
602a0483764SConrad Meyer         if (!followLinks && UTIL_isLink(path)) {
603a0483764SConrad Meyer             UTIL_DISPLAYLEVEL(2, "Warning : %s is a symbolic link, ignoring\n", path);
6044d3f1eafSConrad Meyer             free(path);
605a0483764SConrad Meyer             continue;
606a0483764SConrad Meyer         }
607a0483764SConrad Meyer 
608a0483764SConrad Meyer         if (UTIL_isDirectory(path)) {
609a0483764SConrad Meyer             nbFiles += UTIL_prepareFileList(path, bufStart, pos, bufEnd, followLinks);  /* Recursively call "UTIL_prepareFileList" with the new path. */
610a0483764SConrad Meyer             if (*bufStart == NULL) { free(path); closedir(dir); return 0; }
611a0483764SConrad Meyer         } else {
612a0483764SConrad Meyer             if (*bufStart + *pos + pathLength >= *bufEnd) {
613a0483764SConrad Meyer                 ptrdiff_t newListSize = (*bufEnd - *bufStart) + LIST_SIZE_INCREASE;
6149cbefe25SConrad Meyer                 assert(newListSize >= 0);
6159cbefe25SConrad Meyer                 *bufStart = (char*)UTIL_realloc(*bufStart, (size_t)newListSize);
616a0483764SConrad Meyer                 *bufEnd = *bufStart + newListSize;
617a0483764SConrad Meyer                 if (*bufStart == NULL) { free(path); closedir(dir); return 0; }
618a0483764SConrad Meyer             }
619a0483764SConrad Meyer             if (*bufStart + *pos + pathLength < *bufEnd) {
620a0483764SConrad Meyer                 memcpy(*bufStart + *pos, path, pathLength + 1);  /* with final \0 */
621a0483764SConrad Meyer                 *pos += pathLength + 1;
622a0483764SConrad Meyer                 nbFiles++;
62337f1f268SConrad Meyer         }   }
624a0483764SConrad Meyer         free(path);
625a0483764SConrad Meyer         errno = 0; /* clear errno after UTIL_isDirectory, UTIL_prepareFileList */
626a0483764SConrad Meyer     }
627a0483764SConrad Meyer 
628a0483764SConrad Meyer     if (errno != 0) {
629a0483764SConrad Meyer         UTIL_DISPLAYLEVEL(1, "readdir(%s) error: %s \n", dirName, strerror(errno));
630a0483764SConrad Meyer         free(*bufStart);
631a0483764SConrad Meyer         *bufStart = NULL;
632a0483764SConrad Meyer     }
633a0483764SConrad Meyer     closedir(dir);
634a0483764SConrad Meyer     return nbFiles;
635a0483764SConrad Meyer }
636a0483764SConrad Meyer 
637a0483764SConrad Meyer #else
638a0483764SConrad Meyer 
63937f1f268SConrad Meyer static int UTIL_prepareFileList(const char *dirName,
64037f1f268SConrad Meyer                                 char** bufStart, size_t* pos,
64137f1f268SConrad Meyer                                 char** bufEnd, int followLinks)
642a0483764SConrad Meyer {
643a0483764SConrad Meyer     (void)bufStart; (void)bufEnd; (void)pos; (void)followLinks;
644a0483764SConrad Meyer     UTIL_DISPLAYLEVEL(1, "Directory %s ignored (compiled without _WIN32 or _POSIX_C_SOURCE) \n", dirName);
645a0483764SConrad Meyer     return 0;
646a0483764SConrad Meyer }
647a0483764SConrad Meyer 
648a0483764SConrad Meyer #endif /* #ifdef _WIN32 */
649a0483764SConrad Meyer 
6509cbefe25SConrad Meyer int UTIL_isCompressedFile(const char *inputName, const char *extensionList[])
6519cbefe25SConrad Meyer {
6529cbefe25SConrad Meyer   const char* ext = UTIL_getFileExtension(inputName);
6539cbefe25SConrad Meyer   while(*extensionList!=NULL)
6549cbefe25SConrad Meyer   {
6559cbefe25SConrad Meyer     const int isCompressedExtension = strcmp(ext,*extensionList);
6569cbefe25SConrad Meyer     if(isCompressedExtension==0)
6579cbefe25SConrad Meyer       return 1;
6589cbefe25SConrad Meyer     ++extensionList;
6599cbefe25SConrad Meyer   }
6609cbefe25SConrad Meyer    return 0;
6619cbefe25SConrad Meyer }
6629cbefe25SConrad Meyer 
6639cbefe25SConrad Meyer /*Utility function to get file extension from file */
6649cbefe25SConrad Meyer const char* UTIL_getFileExtension(const char* infilename)
6659cbefe25SConrad Meyer {
6669cbefe25SConrad Meyer    const char* extension = strrchr(infilename, '.');
6679cbefe25SConrad Meyer    if(!extension || extension==infilename) return "";
6689cbefe25SConrad Meyer    return extension;
6699cbefe25SConrad Meyer }
6709cbefe25SConrad Meyer 
671*f7cd7fe5SConrad Meyer static int pathnameHas2Dots(const char *pathname)
672*f7cd7fe5SConrad Meyer {
673*f7cd7fe5SConrad Meyer     return NULL != strstr(pathname, "..");
674*f7cd7fe5SConrad Meyer }
675*f7cd7fe5SConrad Meyer 
676*f7cd7fe5SConrad Meyer static int isFileNameValidForMirroredOutput(const char *filename)
677*f7cd7fe5SConrad Meyer {
678*f7cd7fe5SConrad Meyer     return !pathnameHas2Dots(filename);
679*f7cd7fe5SConrad Meyer }
680*f7cd7fe5SConrad Meyer 
681*f7cd7fe5SConrad Meyer 
682*f7cd7fe5SConrad Meyer #define DIR_DEFAULT_MODE 0755
683*f7cd7fe5SConrad Meyer static mode_t getDirMode(const char *dirName)
684*f7cd7fe5SConrad Meyer {
685*f7cd7fe5SConrad Meyer     stat_t st;
686*f7cd7fe5SConrad Meyer     if (!UTIL_stat(dirName, &st)) {
687*f7cd7fe5SConrad Meyer         UTIL_DISPLAY("zstd: failed to get DIR stats %s: %s\n", dirName, strerror(errno));
688*f7cd7fe5SConrad Meyer         return DIR_DEFAULT_MODE;
689*f7cd7fe5SConrad Meyer     }
690*f7cd7fe5SConrad Meyer     if (!UTIL_isDirectoryStat(&st)) {
691*f7cd7fe5SConrad Meyer         UTIL_DISPLAY("zstd: expected directory: %s\n", dirName);
692*f7cd7fe5SConrad Meyer         return DIR_DEFAULT_MODE;
693*f7cd7fe5SConrad Meyer     }
694*f7cd7fe5SConrad Meyer     return st.st_mode;
695*f7cd7fe5SConrad Meyer }
696*f7cd7fe5SConrad Meyer 
697*f7cd7fe5SConrad Meyer static int makeDir(const char *dir, mode_t mode)
698*f7cd7fe5SConrad Meyer {
699*f7cd7fe5SConrad Meyer #if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__)
700*f7cd7fe5SConrad Meyer     int ret = _mkdir(dir);
701*f7cd7fe5SConrad Meyer     (void) mode;
702*f7cd7fe5SConrad Meyer #else
703*f7cd7fe5SConrad Meyer     int ret = mkdir(dir, mode);
704*f7cd7fe5SConrad Meyer #endif
705*f7cd7fe5SConrad Meyer     if (ret != 0) {
706*f7cd7fe5SConrad Meyer         if (errno == EEXIST)
707*f7cd7fe5SConrad Meyer             return 0;
708*f7cd7fe5SConrad Meyer         UTIL_DISPLAY("zstd: failed to create DIR %s: %s\n", dir, strerror(errno));
709*f7cd7fe5SConrad Meyer     }
710*f7cd7fe5SConrad Meyer     return ret;
711*f7cd7fe5SConrad Meyer }
712*f7cd7fe5SConrad Meyer 
713*f7cd7fe5SConrad Meyer /* this function requires a mutable input string */
714*f7cd7fe5SConrad Meyer static void convertPathnameToDirName(char *pathname)
715*f7cd7fe5SConrad Meyer {
716*f7cd7fe5SConrad Meyer     size_t len = 0;
717*f7cd7fe5SConrad Meyer     char* pos = NULL;
718*f7cd7fe5SConrad Meyer     /* get dir name from pathname similar to 'dirname()' */
719*f7cd7fe5SConrad Meyer     assert(pathname != NULL);
720*f7cd7fe5SConrad Meyer 
721*f7cd7fe5SConrad Meyer     /* remove trailing '/' chars */
722*f7cd7fe5SConrad Meyer     len = strlen(pathname);
723*f7cd7fe5SConrad Meyer     assert(len > 0);
724*f7cd7fe5SConrad Meyer     while (pathname[len] == PATH_SEP) {
725*f7cd7fe5SConrad Meyer         pathname[len] = '\0';
726*f7cd7fe5SConrad Meyer         len--;
727*f7cd7fe5SConrad Meyer     }
728*f7cd7fe5SConrad Meyer     if (len == 0) return;
729*f7cd7fe5SConrad Meyer 
730*f7cd7fe5SConrad Meyer     /* if input is a single file, return '.' instead. i.e.
731*f7cd7fe5SConrad Meyer      * "xyz/abc/file.txt" => "xyz/abc"
732*f7cd7fe5SConrad Meyer        "./file.txt"       => "."
733*f7cd7fe5SConrad Meyer        "file.txt"         => "."
734*f7cd7fe5SConrad Meyer      */
735*f7cd7fe5SConrad Meyer     pos = strrchr(pathname, PATH_SEP);
736*f7cd7fe5SConrad Meyer     if (pos == NULL) {
737*f7cd7fe5SConrad Meyer         pathname[0] = '.';
738*f7cd7fe5SConrad Meyer         pathname[1] = '\0';
739*f7cd7fe5SConrad Meyer     } else {
740*f7cd7fe5SConrad Meyer         *pos = '\0';
741*f7cd7fe5SConrad Meyer     }
742*f7cd7fe5SConrad Meyer }
743*f7cd7fe5SConrad Meyer 
744*f7cd7fe5SConrad Meyer /* pathname must be valid */
745*f7cd7fe5SConrad Meyer static const char* trimLeadingRootChar(const char *pathname)
746*f7cd7fe5SConrad Meyer {
747*f7cd7fe5SConrad Meyer     assert(pathname != NULL);
748*f7cd7fe5SConrad Meyer     if (pathname[0] == PATH_SEP)
749*f7cd7fe5SConrad Meyer         return pathname + 1;
750*f7cd7fe5SConrad Meyer     return pathname;
751*f7cd7fe5SConrad Meyer }
752*f7cd7fe5SConrad Meyer 
753*f7cd7fe5SConrad Meyer /* pathname must be valid */
754*f7cd7fe5SConrad Meyer static const char* trimLeadingCurrentDirConst(const char *pathname)
755*f7cd7fe5SConrad Meyer {
756*f7cd7fe5SConrad Meyer     assert(pathname != NULL);
757*f7cd7fe5SConrad Meyer     if ((pathname[0] == '.') && (pathname[1] == PATH_SEP))
758*f7cd7fe5SConrad Meyer         return pathname + 2;
759*f7cd7fe5SConrad Meyer     return pathname;
760*f7cd7fe5SConrad Meyer }
761*f7cd7fe5SConrad Meyer 
762*f7cd7fe5SConrad Meyer static char*
763*f7cd7fe5SConrad Meyer trimLeadingCurrentDir(char *pathname)
764*f7cd7fe5SConrad Meyer {
765*f7cd7fe5SConrad Meyer     /* 'union charunion' can do const-cast without compiler warning */
766*f7cd7fe5SConrad Meyer     union charunion {
767*f7cd7fe5SConrad Meyer         char *chr;
768*f7cd7fe5SConrad Meyer         const char* cchr;
769*f7cd7fe5SConrad Meyer     } ptr;
770*f7cd7fe5SConrad Meyer     ptr.cchr = trimLeadingCurrentDirConst(pathname);
771*f7cd7fe5SConrad Meyer     return ptr.chr;
772*f7cd7fe5SConrad Meyer }
773*f7cd7fe5SConrad Meyer 
774*f7cd7fe5SConrad Meyer /* remove leading './' or '/' chars here */
775*f7cd7fe5SConrad Meyer static const char * trimPath(const char *pathname)
776*f7cd7fe5SConrad Meyer {
777*f7cd7fe5SConrad Meyer     return trimLeadingRootChar(
778*f7cd7fe5SConrad Meyer             trimLeadingCurrentDirConst(pathname));
779*f7cd7fe5SConrad Meyer }
780*f7cd7fe5SConrad Meyer 
781*f7cd7fe5SConrad Meyer static char* mallocAndJoin2Dir(const char *dir1, const char *dir2)
782*f7cd7fe5SConrad Meyer {
783*f7cd7fe5SConrad Meyer     const size_t dir1Size = strlen(dir1);
784*f7cd7fe5SConrad Meyer     const size_t dir2Size = strlen(dir2);
785*f7cd7fe5SConrad Meyer     char *outDirBuffer, *buffer, trailingChar;
786*f7cd7fe5SConrad Meyer 
787*f7cd7fe5SConrad Meyer     assert(dir1 != NULL && dir2 != NULL);
788*f7cd7fe5SConrad Meyer     outDirBuffer = (char *) malloc(dir1Size + dir2Size + 2);
789*f7cd7fe5SConrad Meyer     CONTROL(outDirBuffer != NULL);
790*f7cd7fe5SConrad Meyer 
791*f7cd7fe5SConrad Meyer     memcpy(outDirBuffer, dir1, dir1Size);
792*f7cd7fe5SConrad Meyer     outDirBuffer[dir1Size] = '\0';
793*f7cd7fe5SConrad Meyer 
794*f7cd7fe5SConrad Meyer     if (dir2[0] == '.')
795*f7cd7fe5SConrad Meyer         return outDirBuffer;
796*f7cd7fe5SConrad Meyer 
797*f7cd7fe5SConrad Meyer     buffer = outDirBuffer + dir1Size;
798*f7cd7fe5SConrad Meyer     trailingChar = *(buffer - 1);
799*f7cd7fe5SConrad Meyer     if (trailingChar != PATH_SEP) {
800*f7cd7fe5SConrad Meyer         *buffer = PATH_SEP;
801*f7cd7fe5SConrad Meyer         buffer++;
802*f7cd7fe5SConrad Meyer     }
803*f7cd7fe5SConrad Meyer     memcpy(buffer, dir2, dir2Size);
804*f7cd7fe5SConrad Meyer     buffer[dir2Size] = '\0';
805*f7cd7fe5SConrad Meyer 
806*f7cd7fe5SConrad Meyer     return outDirBuffer;
807*f7cd7fe5SConrad Meyer }
808*f7cd7fe5SConrad Meyer 
809*f7cd7fe5SConrad Meyer /* this function will return NULL if input srcFileName is not valid name for mirrored output path */
810*f7cd7fe5SConrad Meyer char* UTIL_createMirroredDestDirName(const char* srcFileName, const char* outDirRootName)
811*f7cd7fe5SConrad Meyer {
812*f7cd7fe5SConrad Meyer     char* pathname = NULL;
813*f7cd7fe5SConrad Meyer     if (!isFileNameValidForMirroredOutput(srcFileName))
814*f7cd7fe5SConrad Meyer         return NULL;
815*f7cd7fe5SConrad Meyer 
816*f7cd7fe5SConrad Meyer     pathname = mallocAndJoin2Dir(outDirRootName, trimPath(srcFileName));
817*f7cd7fe5SConrad Meyer 
818*f7cd7fe5SConrad Meyer     convertPathnameToDirName(pathname);
819*f7cd7fe5SConrad Meyer     return pathname;
820*f7cd7fe5SConrad Meyer }
821*f7cd7fe5SConrad Meyer 
822*f7cd7fe5SConrad Meyer static int
823*f7cd7fe5SConrad Meyer mirrorSrcDir(char* srcDirName, const char* outDirName)
824*f7cd7fe5SConrad Meyer {
825*f7cd7fe5SConrad Meyer     mode_t srcMode;
826*f7cd7fe5SConrad Meyer     int status = 0;
827*f7cd7fe5SConrad Meyer     char* newDir = mallocAndJoin2Dir(outDirName, trimPath(srcDirName));
828*f7cd7fe5SConrad Meyer     if (!newDir)
829*f7cd7fe5SConrad Meyer         return -ENOMEM;
830*f7cd7fe5SConrad Meyer 
831*f7cd7fe5SConrad Meyer     srcMode = getDirMode(srcDirName);
832*f7cd7fe5SConrad Meyer     status = makeDir(newDir, srcMode);
833*f7cd7fe5SConrad Meyer     free(newDir);
834*f7cd7fe5SConrad Meyer     return status;
835*f7cd7fe5SConrad Meyer }
836*f7cd7fe5SConrad Meyer 
837*f7cd7fe5SConrad Meyer static int
838*f7cd7fe5SConrad Meyer mirrorSrcDirRecursive(char* srcDirName, const char* outDirName)
839*f7cd7fe5SConrad Meyer {
840*f7cd7fe5SConrad Meyer     int status = 0;
841*f7cd7fe5SConrad Meyer     char* pp = trimLeadingCurrentDir(srcDirName);
842*f7cd7fe5SConrad Meyer     char* sp = NULL;
843*f7cd7fe5SConrad Meyer 
844*f7cd7fe5SConrad Meyer     while ((sp = strchr(pp, PATH_SEP)) != NULL) {
845*f7cd7fe5SConrad Meyer         if (sp != pp) {
846*f7cd7fe5SConrad Meyer             *sp = '\0';
847*f7cd7fe5SConrad Meyer             status = mirrorSrcDir(srcDirName, outDirName);
848*f7cd7fe5SConrad Meyer             if (status != 0)
849*f7cd7fe5SConrad Meyer                 return status;
850*f7cd7fe5SConrad Meyer             *sp = PATH_SEP;
851*f7cd7fe5SConrad Meyer         }
852*f7cd7fe5SConrad Meyer         pp = sp + 1;
853*f7cd7fe5SConrad Meyer     }
854*f7cd7fe5SConrad Meyer     status = mirrorSrcDir(srcDirName, outDirName);
855*f7cd7fe5SConrad Meyer     return status;
856*f7cd7fe5SConrad Meyer }
857*f7cd7fe5SConrad Meyer 
858*f7cd7fe5SConrad Meyer static void
859*f7cd7fe5SConrad Meyer makeMirroredDestDirsWithSameSrcDirMode(char** srcDirNames, unsigned nbFile, const char* outDirName)
860*f7cd7fe5SConrad Meyer {
861*f7cd7fe5SConrad Meyer     unsigned int i = 0;
862*f7cd7fe5SConrad Meyer     for (i = 0; i < nbFile; i++)
863*f7cd7fe5SConrad Meyer         mirrorSrcDirRecursive(srcDirNames[i], outDirName);
864*f7cd7fe5SConrad Meyer }
865*f7cd7fe5SConrad Meyer 
866*f7cd7fe5SConrad Meyer static int
867*f7cd7fe5SConrad Meyer firstIsParentOrSameDirOfSecond(const char* firstDir, const char* secondDir)
868*f7cd7fe5SConrad Meyer {
869*f7cd7fe5SConrad Meyer     size_t firstDirLen  = strlen(firstDir),
870*f7cd7fe5SConrad Meyer            secondDirLen = strlen(secondDir);
871*f7cd7fe5SConrad Meyer     return firstDirLen <= secondDirLen &&
872*f7cd7fe5SConrad Meyer            (secondDir[firstDirLen] == PATH_SEP || secondDir[firstDirLen] == '\0') &&
873*f7cd7fe5SConrad Meyer            0 == strncmp(firstDir, secondDir, firstDirLen);
874*f7cd7fe5SConrad Meyer }
875*f7cd7fe5SConrad Meyer 
876*f7cd7fe5SConrad Meyer static int compareDir(const void* pathname1, const void* pathname2) {
877*f7cd7fe5SConrad Meyer     /* sort it after remove the leading '/'  or './'*/
878*f7cd7fe5SConrad Meyer     const char* s1 = trimPath(*(char * const *) pathname1);
879*f7cd7fe5SConrad Meyer     const char* s2 = trimPath(*(char * const *) pathname2);
880*f7cd7fe5SConrad Meyer     return strcmp(s1, s2);
881*f7cd7fe5SConrad Meyer }
882*f7cd7fe5SConrad Meyer 
883*f7cd7fe5SConrad Meyer static void
884*f7cd7fe5SConrad Meyer makeUniqueMirroredDestDirs(char** srcDirNames, unsigned nbFile, const char* outDirName)
885*f7cd7fe5SConrad Meyer {
886*f7cd7fe5SConrad Meyer     unsigned int i = 0, uniqueDirNr = 0;
887*f7cd7fe5SConrad Meyer     char** uniqueDirNames = NULL;
888*f7cd7fe5SConrad Meyer 
889*f7cd7fe5SConrad Meyer     if (nbFile == 0)
890*f7cd7fe5SConrad Meyer         return;
891*f7cd7fe5SConrad Meyer 
892*f7cd7fe5SConrad Meyer     uniqueDirNames = (char** ) malloc(nbFile * sizeof (char *));
893*f7cd7fe5SConrad Meyer     CONTROL(uniqueDirNames != NULL);
894*f7cd7fe5SConrad Meyer 
895*f7cd7fe5SConrad Meyer     /* if dirs is "a/b/c" and "a/b/c/d", we only need call:
896*f7cd7fe5SConrad Meyer      * we just need "a/b/c/d" */
897*f7cd7fe5SConrad Meyer     qsort((void *)srcDirNames, nbFile, sizeof(char*), compareDir);
898*f7cd7fe5SConrad Meyer 
899*f7cd7fe5SConrad Meyer     uniqueDirNr = 1;
900*f7cd7fe5SConrad Meyer     uniqueDirNames[uniqueDirNr - 1] = srcDirNames[0];
901*f7cd7fe5SConrad Meyer     for (i = 1; i < nbFile; i++) {
902*f7cd7fe5SConrad Meyer         char* prevDirName = srcDirNames[i - 1];
903*f7cd7fe5SConrad Meyer         char* currDirName = srcDirNames[i];
904*f7cd7fe5SConrad Meyer 
905*f7cd7fe5SConrad Meyer         /* note: we alwasy compare trimmed path, i.e.:
906*f7cd7fe5SConrad Meyer          * src dir of "./foo" and "/foo" will be both saved into:
907*f7cd7fe5SConrad Meyer          * "outDirName/foo/" */
908*f7cd7fe5SConrad Meyer         if (!firstIsParentOrSameDirOfSecond(trimPath(prevDirName),
909*f7cd7fe5SConrad Meyer                                             trimPath(currDirName)))
910*f7cd7fe5SConrad Meyer             uniqueDirNr++;
911*f7cd7fe5SConrad Meyer 
912*f7cd7fe5SConrad Meyer         /* we need maintain original src dir name instead of trimmed
913*f7cd7fe5SConrad Meyer          * dir, so we can retrive the original src dir's mode_t */
914*f7cd7fe5SConrad Meyer         uniqueDirNames[uniqueDirNr - 1] = currDirName;
915*f7cd7fe5SConrad Meyer     }
916*f7cd7fe5SConrad Meyer 
917*f7cd7fe5SConrad Meyer     makeMirroredDestDirsWithSameSrcDirMode(uniqueDirNames, uniqueDirNr, outDirName);
918*f7cd7fe5SConrad Meyer 
919*f7cd7fe5SConrad Meyer     free(uniqueDirNames);
920*f7cd7fe5SConrad Meyer }
921*f7cd7fe5SConrad Meyer 
922*f7cd7fe5SConrad Meyer static void
923*f7cd7fe5SConrad Meyer makeMirroredDestDirs(char** srcFileNames, unsigned nbFile, const char* outDirName)
924*f7cd7fe5SConrad Meyer {
925*f7cd7fe5SConrad Meyer     unsigned int i = 0;
926*f7cd7fe5SConrad Meyer     for (i = 0; i < nbFile; ++i)
927*f7cd7fe5SConrad Meyer         convertPathnameToDirName(srcFileNames[i]);
928*f7cd7fe5SConrad Meyer     makeUniqueMirroredDestDirs(srcFileNames, nbFile, outDirName);
929*f7cd7fe5SConrad Meyer }
930*f7cd7fe5SConrad Meyer 
931*f7cd7fe5SConrad Meyer void UTIL_mirrorSourceFilesDirectories(const char** inFileNames, unsigned int nbFile, const char* outDirName)
932*f7cd7fe5SConrad Meyer {
933*f7cd7fe5SConrad Meyer     unsigned int i = 0, validFilenamesNr = 0;
934*f7cd7fe5SConrad Meyer     char** srcFileNames = (char **) malloc(nbFile * sizeof (char *));
935*f7cd7fe5SConrad Meyer     CONTROL(srcFileNames != NULL);
936*f7cd7fe5SConrad Meyer 
937*f7cd7fe5SConrad Meyer     /* check input filenames is valid */
938*f7cd7fe5SConrad Meyer     for (i = 0; i < nbFile; ++i) {
939*f7cd7fe5SConrad Meyer         if (isFileNameValidForMirroredOutput(inFileNames[i])) {
940*f7cd7fe5SConrad Meyer             char* fname = STRDUP(inFileNames[i]);
941*f7cd7fe5SConrad Meyer             CONTROL(fname != NULL);
942*f7cd7fe5SConrad Meyer             srcFileNames[validFilenamesNr++] = fname;
943*f7cd7fe5SConrad Meyer         }
944*f7cd7fe5SConrad Meyer     }
945*f7cd7fe5SConrad Meyer 
946*f7cd7fe5SConrad Meyer     if (validFilenamesNr > 0) {
947*f7cd7fe5SConrad Meyer         makeDir(outDirName, DIR_DEFAULT_MODE);
948*f7cd7fe5SConrad Meyer         makeMirroredDestDirs(srcFileNames, validFilenamesNr, outDirName);
949*f7cd7fe5SConrad Meyer     }
950*f7cd7fe5SConrad Meyer 
951*f7cd7fe5SConrad Meyer     for (i = 0; i < validFilenamesNr; i++)
952*f7cd7fe5SConrad Meyer         free(srcFileNames[i]);
953*f7cd7fe5SConrad Meyer     free(srcFileNames);
954*f7cd7fe5SConrad Meyer }
95537f1f268SConrad Meyer 
95637f1f268SConrad Meyer FileNamesTable*
95737f1f268SConrad Meyer UTIL_createExpandedFNT(const char** inputNames, size_t nbIfns, int followLinks)
958a0483764SConrad Meyer {
95937f1f268SConrad Meyer     unsigned nbFiles;
960a0483764SConrad Meyer     char* buf = (char*)malloc(LIST_SIZE_INCREASE);
961a0483764SConrad Meyer     char* bufend = buf + LIST_SIZE_INCREASE;
962a0483764SConrad Meyer 
963a0483764SConrad Meyer     if (!buf) return NULL;
964a0483764SConrad Meyer 
96537f1f268SConrad Meyer     {   size_t ifnNb, pos;
96637f1f268SConrad Meyer         for (ifnNb=0, pos=0, nbFiles=0; ifnNb<nbIfns; ifnNb++) {
96737f1f268SConrad Meyer             if (!UTIL_isDirectory(inputNames[ifnNb])) {
96837f1f268SConrad Meyer                 size_t const len = strlen(inputNames[ifnNb]);
969a0483764SConrad Meyer                 if (buf + pos + len >= bufend) {
970a0483764SConrad Meyer                     ptrdiff_t newListSize = (bufend - buf) + LIST_SIZE_INCREASE;
9719cbefe25SConrad Meyer                     assert(newListSize >= 0);
9729cbefe25SConrad Meyer                     buf = (char*)UTIL_realloc(buf, (size_t)newListSize);
973a0483764SConrad Meyer                     if (!buf) return NULL;
97437f1f268SConrad Meyer                     bufend = buf + newListSize;
975a0483764SConrad Meyer                 }
976a0483764SConrad Meyer                 if (buf + pos + len < bufend) {
97737f1f268SConrad Meyer                     memcpy(buf+pos, inputNames[ifnNb], len+1);  /* including final \0 */
978a0483764SConrad Meyer                     pos += len + 1;
979a0483764SConrad Meyer                     nbFiles++;
980a0483764SConrad Meyer                 }
981a0483764SConrad Meyer             } else {
98237f1f268SConrad Meyer                 nbFiles += (unsigned)UTIL_prepareFileList(inputNames[ifnNb], &buf, &pos, &bufend, followLinks);
983a0483764SConrad Meyer                 if (buf == NULL) return NULL;
98437f1f268SConrad Meyer     }   }   }
985a0483764SConrad Meyer 
98637f1f268SConrad Meyer     /* note : even if nbFiles==0, function returns a valid, though empty, FileNamesTable* object */
987a0483764SConrad Meyer 
98837f1f268SConrad Meyer     {   size_t ifnNb, pos;
98937f1f268SConrad Meyer         size_t const fntCapacity = nbFiles + 1;  /* minimum 1, allows adding one reference, typically stdin */
99037f1f268SConrad Meyer         const char** const fileNamesTable = (const char**)malloc(fntCapacity * sizeof(*fileNamesTable));
99137f1f268SConrad Meyer         if (!fileNamesTable) { free(buf); return NULL; }
992a0483764SConrad Meyer 
99337f1f268SConrad Meyer         for (ifnNb = 0, pos = 0; ifnNb < nbFiles; ifnNb++) {
99437f1f268SConrad Meyer             fileNamesTable[ifnNb] = buf + pos;
99537f1f268SConrad Meyer             if (buf + pos > bufend) { free(buf); free((void*)fileNamesTable); return NULL; }
99637f1f268SConrad Meyer             pos += strlen(fileNamesTable[ifnNb]) + 1;
997a0483764SConrad Meyer         }
99837f1f268SConrad Meyer         return UTIL_assembleFileNamesTable2(fileNamesTable, nbFiles, fntCapacity, buf);
999a0483764SConrad Meyer     }
10009cbefe25SConrad Meyer }
1001a0483764SConrad Meyer 
10022b9c00cbSConrad Meyer 
100337f1f268SConrad Meyer void UTIL_expandFNT(FileNamesTable** fnt, int followLinks)
100437f1f268SConrad Meyer {
100537f1f268SConrad Meyer     FileNamesTable* const newFNT = UTIL_createExpandedFNT((*fnt)->fileNames, (*fnt)->tableSize, followLinks);
100637f1f268SConrad Meyer     CONTROL(newFNT != NULL);
100737f1f268SConrad Meyer     UTIL_freeFileNamesTable(*fnt);
100837f1f268SConrad Meyer     *fnt = newFNT;
100937f1f268SConrad Meyer }
1010a0483764SConrad Meyer 
101137f1f268SConrad Meyer FileNamesTable* UTIL_createFNT_fromROTable(const char** filenames, size_t nbFilenames)
101237f1f268SConrad Meyer {
101337f1f268SConrad Meyer     size_t const sizeof_FNTable = nbFilenames * sizeof(*filenames);
101437f1f268SConrad Meyer     const char** const newFNTable = (const char**)malloc(sizeof_FNTable);
101537f1f268SConrad Meyer     if (newFNTable==NULL) return NULL;
101637f1f268SConrad Meyer     memcpy((void*)newFNTable, filenames, sizeof_FNTable);  /* void* : mitigate a Visual compiler bug or limitation */
101737f1f268SConrad Meyer     return UTIL_assembleFileNamesTable(newFNTable, nbFilenames, NULL);
101837f1f268SConrad Meyer }
1019a0483764SConrad Meyer 
10202b9c00cbSConrad Meyer 
1021a0483764SConrad Meyer /*-****************************************
10222b9c00cbSConrad Meyer *  count the number of physical cores
1023a0483764SConrad Meyer ******************************************/
1024a0483764SConrad Meyer 
1025a0483764SConrad Meyer #if defined(_WIN32) || defined(WIN32)
1026a0483764SConrad Meyer 
1027a0483764SConrad Meyer #include <windows.h>
1028a0483764SConrad Meyer 
1029a0483764SConrad Meyer typedef BOOL(WINAPI* LPFN_GLPI)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD);
1030a0483764SConrad Meyer 
1031a0483764SConrad Meyer int UTIL_countPhysicalCores(void)
1032a0483764SConrad Meyer {
1033a0483764SConrad Meyer     static int numPhysicalCores = 0;
1034a0483764SConrad Meyer     if (numPhysicalCores != 0) return numPhysicalCores;
1035a0483764SConrad Meyer 
1036a0483764SConrad Meyer     {   LPFN_GLPI glpi;
1037a0483764SConrad Meyer         BOOL done = FALSE;
1038a0483764SConrad Meyer         PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL;
1039a0483764SConrad Meyer         PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL;
1040a0483764SConrad Meyer         DWORD returnLength = 0;
1041a0483764SConrad Meyer         size_t byteOffset = 0;
1042a0483764SConrad Meyer 
10439cbefe25SConrad Meyer #if defined(_MSC_VER)
10449cbefe25SConrad Meyer /* Visual Studio does not like the following cast */
10459cbefe25SConrad Meyer #   pragma warning( disable : 4054 )  /* conversion from function ptr to data ptr */
10469cbefe25SConrad Meyer #   pragma warning( disable : 4055 )  /* conversion from data ptr to function ptr */
10479cbefe25SConrad Meyer #endif
10489cbefe25SConrad Meyer         glpi = (LPFN_GLPI)(void*)GetProcAddress(GetModuleHandle(TEXT("kernel32")),
1049a0483764SConrad Meyer                                                "GetLogicalProcessorInformation");
1050a0483764SConrad Meyer 
1051a0483764SConrad Meyer         if (glpi == NULL) {
1052a0483764SConrad Meyer             goto failed;
1053a0483764SConrad Meyer         }
1054a0483764SConrad Meyer 
1055a0483764SConrad Meyer         while(!done) {
1056a0483764SConrad Meyer             DWORD rc = glpi(buffer, &returnLength);
1057a0483764SConrad Meyer             if (FALSE == rc) {
1058a0483764SConrad Meyer                 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
1059a0483764SConrad Meyer                     if (buffer)
1060a0483764SConrad Meyer                         free(buffer);
1061a0483764SConrad Meyer                     buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc(returnLength);
1062a0483764SConrad Meyer 
1063a0483764SConrad Meyer                     if (buffer == NULL) {
1064a0483764SConrad Meyer                         perror("zstd");
1065a0483764SConrad Meyer                         exit(1);
1066a0483764SConrad Meyer                     }
1067a0483764SConrad Meyer                 } else {
1068a0483764SConrad Meyer                     /* some other error */
1069a0483764SConrad Meyer                     goto failed;
1070a0483764SConrad Meyer                 }
1071a0483764SConrad Meyer             } else {
1072a0483764SConrad Meyer                 done = TRUE;
107337f1f268SConrad Meyer         }   }
1074a0483764SConrad Meyer 
1075a0483764SConrad Meyer         ptr = buffer;
1076a0483764SConrad Meyer 
1077a0483764SConrad Meyer         while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) {
1078a0483764SConrad Meyer 
1079a0483764SConrad Meyer             if (ptr->Relationship == RelationProcessorCore) {
1080a0483764SConrad Meyer                 numPhysicalCores++;
1081a0483764SConrad Meyer             }
1082a0483764SConrad Meyer 
1083a0483764SConrad Meyer             ptr++;
1084a0483764SConrad Meyer             byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
1085a0483764SConrad Meyer         }
1086a0483764SConrad Meyer 
1087a0483764SConrad Meyer         free(buffer);
1088a0483764SConrad Meyer 
1089a0483764SConrad Meyer         return numPhysicalCores;
1090a0483764SConrad Meyer     }
1091a0483764SConrad Meyer 
1092a0483764SConrad Meyer failed:
1093a0483764SConrad Meyer     /* try to fall back on GetSystemInfo */
1094a0483764SConrad Meyer     {   SYSTEM_INFO sysinfo;
1095a0483764SConrad Meyer         GetSystemInfo(&sysinfo);
1096a0483764SConrad Meyer         numPhysicalCores = sysinfo.dwNumberOfProcessors;
1097a0483764SConrad Meyer         if (numPhysicalCores == 0) numPhysicalCores = 1; /* just in case */
1098a0483764SConrad Meyer     }
1099a0483764SConrad Meyer     return numPhysicalCores;
1100a0483764SConrad Meyer }
1101a0483764SConrad Meyer 
1102a0483764SConrad Meyer #elif defined(__APPLE__)
1103a0483764SConrad Meyer 
1104a0483764SConrad Meyer #include <sys/sysctl.h>
1105a0483764SConrad Meyer 
1106a0483764SConrad Meyer /* Use apple-provided syscall
1107a0483764SConrad Meyer  * see: man 3 sysctl */
1108a0483764SConrad Meyer int UTIL_countPhysicalCores(void)
1109a0483764SConrad Meyer {
1110a0483764SConrad Meyer     static S32 numPhysicalCores = 0; /* apple specifies int32_t */
1111a0483764SConrad Meyer     if (numPhysicalCores != 0) return numPhysicalCores;
1112a0483764SConrad Meyer 
1113a0483764SConrad Meyer     {   size_t size = sizeof(S32);
1114a0483764SConrad Meyer         int const ret = sysctlbyname("hw.physicalcpu", &numPhysicalCores, &size, NULL, 0);
1115a0483764SConrad Meyer         if (ret != 0) {
1116a0483764SConrad Meyer             if (errno == ENOENT) {
1117a0483764SConrad Meyer                 /* entry not present, fall back on 1 */
1118a0483764SConrad Meyer                 numPhysicalCores = 1;
1119a0483764SConrad Meyer             } else {
1120a0483764SConrad Meyer                 perror("zstd: can't get number of physical cpus");
1121a0483764SConrad Meyer                 exit(1);
1122a0483764SConrad Meyer             }
1123a0483764SConrad Meyer         }
1124a0483764SConrad Meyer 
1125a0483764SConrad Meyer         return numPhysicalCores;
1126a0483764SConrad Meyer     }
1127a0483764SConrad Meyer }
1128a0483764SConrad Meyer 
1129a0483764SConrad Meyer #elif defined(__linux__)
1130a0483764SConrad Meyer 
1131a0483764SConrad Meyer /* parse /proc/cpuinfo
1132a0483764SConrad Meyer  * siblings / cpu cores should give hyperthreading ratio
1133a0483764SConrad Meyer  * otherwise fall back on sysconf */
1134a0483764SConrad Meyer int UTIL_countPhysicalCores(void)
1135a0483764SConrad Meyer {
1136a0483764SConrad Meyer     static int numPhysicalCores = 0;
1137a0483764SConrad Meyer 
1138a0483764SConrad Meyer     if (numPhysicalCores != 0) return numPhysicalCores;
1139a0483764SConrad Meyer 
1140a0483764SConrad Meyer     numPhysicalCores = (int)sysconf(_SC_NPROCESSORS_ONLN);
1141a0483764SConrad Meyer     if (numPhysicalCores == -1) {
1142a0483764SConrad Meyer         /* value not queryable, fall back on 1 */
1143a0483764SConrad Meyer         return numPhysicalCores = 1;
1144a0483764SConrad Meyer     }
1145a0483764SConrad Meyer 
1146a0483764SConrad Meyer     /* try to determine if there's hyperthreading */
1147a0483764SConrad Meyer     {   FILE* const cpuinfo = fopen("/proc/cpuinfo", "r");
1148a0483764SConrad Meyer #define BUF_SIZE 80
1149a0483764SConrad Meyer         char buff[BUF_SIZE];
1150a0483764SConrad Meyer 
1151a0483764SConrad Meyer         int siblings = 0;
1152a0483764SConrad Meyer         int cpu_cores = 0;
1153a0483764SConrad Meyer         int ratio = 1;
1154a0483764SConrad Meyer 
1155a0483764SConrad Meyer         if (cpuinfo == NULL) {
1156a0483764SConrad Meyer             /* fall back on the sysconf value */
1157a0483764SConrad Meyer             return numPhysicalCores;
1158a0483764SConrad Meyer         }
1159a0483764SConrad Meyer 
1160a0483764SConrad Meyer         /* assume the cpu cores/siblings values will be constant across all
1161a0483764SConrad Meyer          * present processors */
1162a0483764SConrad Meyer         while (!feof(cpuinfo)) {
1163a0483764SConrad Meyer             if (fgets(buff, BUF_SIZE, cpuinfo) != NULL) {
1164a0483764SConrad Meyer                 if (strncmp(buff, "siblings", 8) == 0) {
1165a0483764SConrad Meyer                     const char* const sep = strchr(buff, ':');
11669cbefe25SConrad Meyer                     if (sep == NULL || *sep == '\0') {
1167a0483764SConrad Meyer                         /* formatting was broken? */
1168a0483764SConrad Meyer                         goto failed;
1169a0483764SConrad Meyer                     }
1170a0483764SConrad Meyer 
1171a0483764SConrad Meyer                     siblings = atoi(sep + 1);
1172a0483764SConrad Meyer                 }
1173a0483764SConrad Meyer                 if (strncmp(buff, "cpu cores", 9) == 0) {
1174a0483764SConrad Meyer                     const char* const sep = strchr(buff, ':');
11759cbefe25SConrad Meyer                     if (sep == NULL || *sep == '\0') {
1176a0483764SConrad Meyer                         /* formatting was broken? */
1177a0483764SConrad Meyer                         goto failed;
1178a0483764SConrad Meyer                     }
1179a0483764SConrad Meyer 
1180a0483764SConrad Meyer                     cpu_cores = atoi(sep + 1);
1181a0483764SConrad Meyer                 }
1182a0483764SConrad Meyer             } else if (ferror(cpuinfo)) {
1183a0483764SConrad Meyer                 /* fall back on the sysconf value */
1184a0483764SConrad Meyer                 goto failed;
118537f1f268SConrad Meyer         }   }
1186a0483764SConrad Meyer         if (siblings && cpu_cores) {
1187a0483764SConrad Meyer             ratio = siblings / cpu_cores;
1188a0483764SConrad Meyer         }
1189a0483764SConrad Meyer failed:
1190a0483764SConrad Meyer         fclose(cpuinfo);
1191a0483764SConrad Meyer         return numPhysicalCores = numPhysicalCores / ratio;
1192a0483764SConrad Meyer     }
1193a0483764SConrad Meyer }
1194a0483764SConrad Meyer 
11952b9c00cbSConrad Meyer #elif defined(__FreeBSD__)
1196a0483764SConrad Meyer 
11972b9c00cbSConrad Meyer #include <sys/param.h>
11982b9c00cbSConrad Meyer #include <sys/sysctl.h>
11992b9c00cbSConrad Meyer 
12002b9c00cbSConrad Meyer /* Use physical core sysctl when available
12012b9c00cbSConrad Meyer  * see: man 4 smp, man 3 sysctl */
12022b9c00cbSConrad Meyer int UTIL_countPhysicalCores(void)
12032b9c00cbSConrad Meyer {
12042b9c00cbSConrad Meyer     static int numPhysicalCores = 0; /* freebsd sysctl is native int sized */
12052b9c00cbSConrad Meyer     if (numPhysicalCores != 0) return numPhysicalCores;
12062b9c00cbSConrad Meyer 
12072b9c00cbSConrad Meyer #if __FreeBSD_version >= 1300008
12082b9c00cbSConrad Meyer     {   size_t size = sizeof(numPhysicalCores);
12092b9c00cbSConrad Meyer         int ret = sysctlbyname("kern.smp.cores", &numPhysicalCores, &size, NULL, 0);
12102b9c00cbSConrad Meyer         if (ret == 0) return numPhysicalCores;
12112b9c00cbSConrad Meyer         if (errno != ENOENT) {
12122b9c00cbSConrad Meyer             perror("zstd: can't get number of physical cpus");
12132b9c00cbSConrad Meyer             exit(1);
12142b9c00cbSConrad Meyer         }
12152b9c00cbSConrad Meyer         /* sysctl not present, fall through to older sysconf method */
12162b9c00cbSConrad Meyer     }
12172b9c00cbSConrad Meyer #endif
12182b9c00cbSConrad Meyer 
12192b9c00cbSConrad Meyer     numPhysicalCores = (int)sysconf(_SC_NPROCESSORS_ONLN);
12202b9c00cbSConrad Meyer     if (numPhysicalCores == -1) {
12212b9c00cbSConrad Meyer         /* value not queryable, fall back on 1 */
12222b9c00cbSConrad Meyer         numPhysicalCores = 1;
12232b9c00cbSConrad Meyer     }
12242b9c00cbSConrad Meyer     return numPhysicalCores;
12252b9c00cbSConrad Meyer }
12262b9c00cbSConrad Meyer 
122737f1f268SConrad Meyer #elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__CYGWIN__)
12282b9c00cbSConrad Meyer 
12292b9c00cbSConrad Meyer /* Use POSIX sysconf
12302b9c00cbSConrad Meyer  * see: man 3 sysconf */
1231a0483764SConrad Meyer int UTIL_countPhysicalCores(void)
1232a0483764SConrad Meyer {
1233a0483764SConrad Meyer     static int numPhysicalCores = 0;
1234a0483764SConrad Meyer 
1235a0483764SConrad Meyer     if (numPhysicalCores != 0) return numPhysicalCores;
1236a0483764SConrad Meyer 
1237a0483764SConrad Meyer     numPhysicalCores = (int)sysconf(_SC_NPROCESSORS_ONLN);
1238a0483764SConrad Meyer     if (numPhysicalCores == -1) {
1239a0483764SConrad Meyer         /* value not queryable, fall back on 1 */
1240a0483764SConrad Meyer         return numPhysicalCores = 1;
1241a0483764SConrad Meyer     }
1242a0483764SConrad Meyer     return numPhysicalCores;
1243a0483764SConrad Meyer }
1244a0483764SConrad Meyer 
1245a0483764SConrad Meyer #else
1246a0483764SConrad Meyer 
1247a0483764SConrad Meyer int UTIL_countPhysicalCores(void)
1248a0483764SConrad Meyer {
1249a0483764SConrad Meyer     /* assume 1 */
1250a0483764SConrad Meyer     return 1;
1251a0483764SConrad Meyer }
1252a0483764SConrad Meyer 
1253a0483764SConrad Meyer #endif
1254a0483764SConrad Meyer 
1255a0483764SConrad Meyer #if defined (__cplusplus)
1256a0483764SConrad Meyer }
1257a0483764SConrad Meyer #endif
1258