mirror of
https://github.com/facebook/zstd.git
synced 2025-07-30 22:23:13 +03:00
Add output-dir-mirror option
This commit is contained in:
303
programs/util.c
303
programs/util.c
@ -45,7 +45,6 @@ extern "C" {
|
||||
# include <string.h> /* strerror, memcpy */
|
||||
#endif /* #ifdef _WIN32 */
|
||||
|
||||
|
||||
/*-****************************************
|
||||
* Internal Macros
|
||||
******************************************/
|
||||
@ -130,6 +129,18 @@ int UTIL_getFileStat(const char* infilename, stat_t *statbuf)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int UTIL_getDirectoryStat(const char* infilename, stat_t *statbuf)
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
int const r = _stat64(infilename, statbuf);
|
||||
if (!r && (statbuf->st_mode & _S_IFDIR)) return 1;
|
||||
#else
|
||||
int const r = stat(infilename, statbuf);
|
||||
if (!r && S_ISDIR(statbuf->st_mode)) return 1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* like chmod, but avoid changing permission of /dev/null */
|
||||
int UTIL_chmod(char const* filename, mode_t permissions)
|
||||
{
|
||||
@ -178,14 +189,7 @@ int UTIL_setFileStat(const char *filename, stat_t *statbuf)
|
||||
int UTIL_isDirectory(const char* infilename)
|
||||
{
|
||||
stat_t statbuf;
|
||||
#if defined(_MSC_VER)
|
||||
int const r = _stat64(infilename, &statbuf);
|
||||
if (!r && (statbuf.st_mode & _S_IFDIR)) return 1;
|
||||
#else
|
||||
int const r = stat(infilename, &statbuf);
|
||||
if (!r && S_ISDIR(statbuf.st_mode)) return 1;
|
||||
#endif
|
||||
return 0;
|
||||
return UTIL_getDirectoryStat(infilename, &statbuf);
|
||||
}
|
||||
|
||||
int UTIL_compareStr(const void *p1, const void *p2) {
|
||||
@ -633,6 +637,287 @@ const char* UTIL_getFileExtension(const char* infilename)
|
||||
return extension;
|
||||
}
|
||||
|
||||
static int pathnameHas2Dots(const char *pathname)
|
||||
{
|
||||
return NULL != strstr(pathname, "..");
|
||||
}
|
||||
|
||||
static int isFileNameValidForMirroredOutput(const char *filename)
|
||||
{
|
||||
return !pathnameHas2Dots(filename);
|
||||
}
|
||||
|
||||
|
||||
#define DIR_DEFAULT_MODE 0755
|
||||
static mode_t getDirMode(const char *dirName)
|
||||
{
|
||||
stat_t st;
|
||||
int ret = UTIL_getDirectoryStat(dirName, &st);
|
||||
if (!ret) {
|
||||
UTIL_DISPLAY("zstd: failed to get DIR stats %s: %s\n", dirName, strerror(errno));
|
||||
return DIR_DEFAULT_MODE;
|
||||
}
|
||||
return st.st_mode;
|
||||
}
|
||||
|
||||
static int makeDir(const char *dir, mode_t mode)
|
||||
{
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__)
|
||||
int ret = _mkdir(dir);
|
||||
(void) mode;
|
||||
#else
|
||||
int ret = mkdir(dir, mode);
|
||||
#endif
|
||||
if (ret != 0) {
|
||||
if (errno == EEXIST)
|
||||
return 0;
|
||||
UTIL_DISPLAY("zstd: failed to create DIR %s: %s\n", dir, strerror(errno));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* this function requires a mutable input string */
|
||||
static void convertPathnameToDirName(char *pathname)
|
||||
{
|
||||
size_t len = 0;
|
||||
char* pos = NULL;
|
||||
/* get dir name from pathname similar to 'dirname()' */
|
||||
assert(pathname != NULL);
|
||||
|
||||
/* remove trailing '/' chars */
|
||||
len = strlen(pathname);
|
||||
assert(len > 0);
|
||||
while (pathname[len] == PATH_SEP) {
|
||||
pathname[len] = '\0';
|
||||
len--;
|
||||
}
|
||||
if (len == 0) return;
|
||||
|
||||
/* if input is a single file, return '.' instead. i.e.
|
||||
* "xyz/abc/file.txt" => "xyz/abc"
|
||||
"./file.txt" => "."
|
||||
"file.txt" => "."
|
||||
*/
|
||||
pos = strrchr(pathname, PATH_SEP);
|
||||
if (pos == NULL) {
|
||||
pathname[0] = '.';
|
||||
pathname[1] = '\0';
|
||||
} else {
|
||||
*pos = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/* pathname must be valid */
|
||||
static const char* trimLeadingRootChar(const char *pathname)
|
||||
{
|
||||
assert(pathname != NULL);
|
||||
if (pathname[0] == PATH_SEP)
|
||||
return pathname + 1;
|
||||
return pathname;
|
||||
}
|
||||
|
||||
/* pathname must be valid */
|
||||
static const char* trimLeadingCurrentDirConst(const char *pathname)
|
||||
{
|
||||
assert(pathname != NULL);
|
||||
if ((pathname[0] == '.') && (pathname[1] == PATH_SEP))
|
||||
return pathname + 2;
|
||||
return pathname;
|
||||
}
|
||||
|
||||
static char*
|
||||
trimLeadingCurrentDir(char *pathname)
|
||||
{
|
||||
/* 'union charunion' can do const-cast without compiler warning */
|
||||
union charunion {
|
||||
char *chr;
|
||||
const char* cchr;
|
||||
} ptr;
|
||||
ptr.cchr = trimLeadingCurrentDirConst(pathname);
|
||||
return ptr.chr;
|
||||
}
|
||||
|
||||
/* remove leading './' or '/' chars here */
|
||||
static const char * trimPath(const char *pathname)
|
||||
{
|
||||
return trimLeadingRootChar(
|
||||
trimLeadingCurrentDirConst(pathname));
|
||||
}
|
||||
|
||||
static char* mallocAndJoin2Dir(const char *dir1, const char *dir2)
|
||||
{
|
||||
const size_t dir1Size = strlen(dir1);
|
||||
const size_t dir2Size = strlen(dir2);
|
||||
char *outDirBuffer, *buffer, trailingChar;
|
||||
|
||||
assert(dir1 != NULL && dir2 != NULL);
|
||||
outDirBuffer = (char *) malloc(dir1Size + dir2Size + 2);
|
||||
CONTROL(outDirBuffer != NULL);
|
||||
|
||||
strncpy(outDirBuffer, dir1, dir1Size);
|
||||
outDirBuffer[dir1Size] = '\0';
|
||||
|
||||
if (dir2[0] == '.')
|
||||
return outDirBuffer;
|
||||
|
||||
buffer = outDirBuffer + dir1Size;
|
||||
trailingChar = *(buffer - 1);
|
||||
if (trailingChar != PATH_SEP) {
|
||||
*buffer = PATH_SEP;
|
||||
buffer++;
|
||||
}
|
||||
strncpy(buffer, dir2, dir2Size);
|
||||
buffer[dir2Size] = '\0';
|
||||
|
||||
return outDirBuffer;
|
||||
}
|
||||
|
||||
/* this function will return NULL if input srcFileName is not valid name for mirrored output path */
|
||||
char* UTIL_createMirroredDestDirName(const char* srcFileName, const char* outDirRootName)
|
||||
{
|
||||
char* pathname = NULL;
|
||||
if (!isFileNameValidForMirroredOutput(srcFileName))
|
||||
return NULL;
|
||||
|
||||
pathname = mallocAndJoin2Dir(outDirRootName, trimPath(srcFileName));
|
||||
|
||||
convertPathnameToDirName(pathname);
|
||||
return pathname;
|
||||
}
|
||||
|
||||
static int
|
||||
mirrorSrcDir(char* srcDirName, const char* outDirName)
|
||||
{
|
||||
mode_t srcMode;
|
||||
int status = 0;
|
||||
char* newDir = mallocAndJoin2Dir(outDirName, trimPath(srcDirName));
|
||||
if (!newDir)
|
||||
return -ENOMEM;
|
||||
|
||||
srcMode = getDirMode(srcDirName);
|
||||
status = makeDir(newDir, srcMode);
|
||||
free(newDir);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int
|
||||
mirrorSrcDirRecursive(char* srcDirName, const char* outDirName)
|
||||
{
|
||||
int status = 0;
|
||||
char* pp = trimLeadingCurrentDir(srcDirName);
|
||||
char* sp = NULL;
|
||||
|
||||
while ((sp = strchr(pp, PATH_SEP)) != NULL) {
|
||||
if (sp != pp) {
|
||||
*sp = '\0';
|
||||
status = mirrorSrcDir(srcDirName, outDirName);
|
||||
if (status != 0)
|
||||
return status;
|
||||
*sp = PATH_SEP;
|
||||
}
|
||||
pp = sp + 1;
|
||||
}
|
||||
status = mirrorSrcDir(srcDirName, outDirName);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
makeMirroredDestDirsWithSameSrcDirMode(char** srcDirNames, unsigned nbFile, const char* outDirName)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
for (i = 0; i < nbFile; i++)
|
||||
mirrorSrcDirRecursive(srcDirNames[i], outDirName);
|
||||
}
|
||||
|
||||
static int
|
||||
firstIsParentOrSameDirOfSecond(const char* firstDir, const char* secondDir)
|
||||
{
|
||||
size_t firstDirLen = strlen(firstDir),
|
||||
secondDirLen = strlen(secondDir);
|
||||
return firstDirLen <= secondDirLen &&
|
||||
(secondDir[firstDirLen] == PATH_SEP || secondDir[firstDirLen] == '\0') &&
|
||||
0 == strncmp(firstDir, secondDir, firstDirLen);
|
||||
}
|
||||
|
||||
static int compareDir(const void* pathname1, const void* pathname2) {
|
||||
/* sort it after remove the leading '/' or './'*/
|
||||
const char* s1 = trimPath(*(char * const *) pathname1);
|
||||
const char* s2 = trimPath(*(char * const *) pathname2);
|
||||
return strcmp(s1, s2);
|
||||
}
|
||||
|
||||
static void
|
||||
makeUniqueMirroredDestDirs(char** srcDirNames, unsigned nbFile, const char* outDirName)
|
||||
{
|
||||
unsigned int i = 0, uniqueDirNr = 0;
|
||||
char** uniqueDirNames = NULL;
|
||||
|
||||
if (nbFile == 0)
|
||||
return;
|
||||
|
||||
uniqueDirNames = (char** ) malloc(nbFile * sizeof (char *));
|
||||
CONTROL(uniqueDirNames != NULL);
|
||||
|
||||
/* if dirs is "a/b/c" and "a/b/c/d", we only need call:
|
||||
* we just need "a/b/c/d" */
|
||||
qsort((void *)srcDirNames, nbFile, sizeof(char*), compareDir);
|
||||
|
||||
uniqueDirNr = 1;
|
||||
uniqueDirNames[uniqueDirNr - 1] = srcDirNames[0];
|
||||
for (i = 1; i < nbFile; i++) {
|
||||
char* prevDirName = srcDirNames[i - 1];
|
||||
char* currDirName = srcDirNames[i];
|
||||
|
||||
/* note: we alwasy compare trimmed path, i.e.:
|
||||
* src dir of "./foo" and "/foo" will be both saved into:
|
||||
* "outDirName/foo/" */
|
||||
if (!firstIsParentOrSameDirOfSecond(trimPath(prevDirName),
|
||||
trimPath(currDirName)))
|
||||
uniqueDirNr++;
|
||||
|
||||
/* we need maintain original src dir name instead of trimmed
|
||||
* dir, so we can retrive the original src dir's mode_t */
|
||||
uniqueDirNames[uniqueDirNr - 1] = currDirName;
|
||||
}
|
||||
|
||||
makeMirroredDestDirsWithSameSrcDirMode(uniqueDirNames, uniqueDirNr, outDirName);
|
||||
|
||||
free(uniqueDirNames);
|
||||
}
|
||||
|
||||
static void
|
||||
makeMirroredDestDirs(char** srcFileNames, unsigned nbFile, const char* outDirName)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
for (i = 0; i < nbFile; ++i)
|
||||
convertPathnameToDirName(srcFileNames[i]);
|
||||
makeUniqueMirroredDestDirs(srcFileNames, nbFile, outDirName);
|
||||
}
|
||||
|
||||
void UTIL_mirrorSourceFilesDirectories(const char** inFileNames, unsigned int nbFile, const char* outDirName)
|
||||
{
|
||||
unsigned int i = 0, validFilenamesNr = 0;
|
||||
char** srcFileNames = (char **) malloc(nbFile * sizeof (char *));
|
||||
CONTROL(srcFileNames != NULL);
|
||||
|
||||
/* check input filenames is valid */
|
||||
for (i = 0; i < nbFile; ++i) {
|
||||
if (isFileNameValidForMirroredOutput(inFileNames[i])) {
|
||||
char* fname = STRDUP(inFileNames[i]);
|
||||
CONTROL(fname != NULL);
|
||||
srcFileNames[validFilenamesNr++] = fname;
|
||||
}
|
||||
}
|
||||
|
||||
if (validFilenamesNr > 0) {
|
||||
makeDir(outDirName, DIR_DEFAULT_MODE);
|
||||
makeMirroredDestDirs(srcFileNames, validFilenamesNr, outDirName);
|
||||
}
|
||||
|
||||
for (i = 0; i < validFilenamesNr; i++)
|
||||
free(srcFileNames[i]);
|
||||
free(srcFileNames);
|
||||
}
|
||||
|
||||
FileNamesTable*
|
||||
UTIL_createExpandedFNT(const char** inputNames, size_t nbIfns, int followLinks)
|
||||
|
Reference in New Issue
Block a user