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