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