Compress-Stream-Zstd

 view release on metacpan or  search on metacpan

ext/zstd/programs/util.c  view on Meta::CPAN

#if defined(_MSC_VER)
    #define chmod _chmod
#endif

#ifndef ZSTD_HAVE_FCHMOD
#if PLATFORM_POSIX_VERSION >= 199309L
#define ZSTD_HAVE_FCHMOD
#endif
#endif

#ifndef ZSTD_HAVE_FCHOWN
#if PLATFORM_POSIX_VERSION >= 200809L
#define ZSTD_HAVE_FCHOWN
#endif
#endif

/*-****************************************
*  Console log
******************************************/
int g_utilDisplayLevel;

int UTIL_requireUserConfirmation(const char* prompt, const char* abortMsg,
                                 const char* acceptableLetters, int hasStdinInput) {
    int ch, result;

    if (hasStdinInput) {
        UTIL_DISPLAY("stdin is an input - not proceeding.\n");
        return 1;
    }

    UTIL_DISPLAY("%s", prompt);
    ch = getchar();
    result = 0;
    if (strchr(acceptableLetters, ch) == NULL) {
        UTIL_DISPLAY("%s \n", abortMsg);
        result = 1;
    }
    /* flush the rest */
    while ((ch!=EOF) && (ch!='\n'))
        ch = getchar();
    return result;
}


/*-*************************************
*  Constants
***************************************/
#define LIST_SIZE_INCREASE   (8*1024)
#define MAX_FILE_OF_FILE_NAMES_SIZE (1<<20)*50


/*-*************************************
*  Functions
***************************************/

void UTIL_traceFileStat(void)
{
    g_traceFileStat = 1;
}

int UTIL_fstat(const int fd, const char* filename, stat_t* statbuf)
{
    int ret;
    UTIL_TRACE_CALL("UTIL_stat(%d, %s)", fd, filename);
#if defined(_MSC_VER)
    if (fd >= 0) {
        ret = !_fstat64(fd, statbuf);
    } else {
        ret = !_stat64(filename, statbuf);
    }
#elif defined(__MINGW32__) && defined (__MSVCRT__)
    if (fd >= 0) {
        ret = !_fstati64(fd, statbuf);
    } else {
        ret = !_stati64(filename, statbuf);
    }
#else
    if (fd >= 0) {
        ret = !fstat(fd, statbuf);
    } else {
        ret = !stat(filename, statbuf);
    }
#endif
    UTIL_TRACE_RET(ret);
    return ret;
}

int UTIL_stat(const char* filename, stat_t* statbuf)
{
    return UTIL_fstat(-1, filename, statbuf);
}

int UTIL_isRegularFile(const char* infilename)
{
    stat_t statbuf;
    int ret;
    UTIL_TRACE_CALL("UTIL_isRegularFile(%s)", infilename);
    ret = UTIL_stat(infilename, &statbuf) && UTIL_isRegularFileStat(&statbuf);
    UTIL_TRACE_RET(ret);
    return ret;
}

int UTIL_isRegularFileStat(const stat_t* statbuf)
{
#if defined(_MSC_VER)
    return (statbuf->st_mode & S_IFREG) != 0;
#else
    return S_ISREG(statbuf->st_mode) != 0;
#endif
}

/* like chmod, but avoid changing permission of /dev/null */
int UTIL_chmod(char const* filename, const stat_t* statbuf, mode_t permissions)
{
    return UTIL_fchmod(-1, filename, statbuf, permissions);
}

int UTIL_fchmod(const int fd, char const* filename, const stat_t* statbuf, mode_t permissions)
{
    stat_t localStatBuf;
    UTIL_TRACE_CALL("UTIL_chmod(%s, %#4o)", filename, (unsigned)permissions);
    if (statbuf == NULL) {
        if (!UTIL_fstat(fd, filename, &localStatBuf)) {
            UTIL_TRACE_RET(0);
            return 0;
        }
        statbuf = &localStatBuf;
    }
    if (!UTIL_isRegularFileStat(statbuf)) {
        UTIL_TRACE_RET(0);
        return 0; /* pretend success, but don't change anything */
    }
#ifdef ZSTD_HAVE_FCHMOD
    if (fd >= 0) {
        int ret;
        UTIL_TRACE_CALL("fchmod");
        ret = fchmod(fd, permissions);
        UTIL_TRACE_RET(ret);
        UTIL_TRACE_RET(ret);
        return ret;
    } else
#endif
    {
        int ret;
        UTIL_TRACE_CALL("chmod");
        ret = chmod(filename, permissions);
        UTIL_TRACE_RET(ret);
        UTIL_TRACE_RET(ret);
        return ret;
    }
}

/* set access and modification times */
int UTIL_utime(const char* filename, const stat_t *statbuf)
{
    int ret;
    UTIL_TRACE_CALL("UTIL_utime(%s)", filename);
    /* We check that st_mtime is a macro here in order to give us confidence
     * that struct stat has a struct timespec st_mtim member. We need this
     * check because there are some platforms that claim to be POSIX 2008
     * compliant but which do not have st_mtim... */
#if (PLATFORM_POSIX_VERSION >= 200809L) && defined(st_mtime)
    {
        /* (atime, mtime) */
        struct timespec timebuf[2] = { {0, UTIME_NOW} };
        timebuf[1] = statbuf->st_mtim;
        ret = utimensat(AT_FDCWD, filename, timebuf, 0);
    }
#else
    {
        struct utimbuf timebuf;
        timebuf.actime = time(NULL);
        timebuf.modtime = statbuf->st_mtime;
        ret = utime(filename, &timebuf);
    }
#endif
    errno = 0;
    UTIL_TRACE_RET(ret);
    return ret;
}

int UTIL_setFileStat(const char *filename, const stat_t *statbuf)
{
    return UTIL_setFDStat(-1, filename, statbuf);
}

int UTIL_setFDStat(const int fd, const char *filename, const stat_t *statbuf)
{
    int res = 0;
    stat_t curStatBuf;
    UTIL_TRACE_CALL("UTIL_setFileStat(%d, %s)", fd, filename);

    if (!UTIL_fstat(fd, filename, &curStatBuf) || !UTIL_isRegularFileStat(&curStatBuf)) {
        UTIL_TRACE_RET(-1);
        return -1;
    }

    /* Mimic gzip's behavior:
     *
     * "Change the group first, then the permissions, then the owner.
     * That way, the permissions will be correct on systems that allow
     * users to give away files, without introducing a security hole.
     * Security depends on permissions not containing the setuid or
     * setgid bits." */

#if !defined(_WIN32)
#ifdef ZSTD_HAVE_FCHOWN
    if (fd >= 0) {
        res += fchown(fd, -1, statbuf->st_gid);  /* Apply group ownership */
    } else
#endif
    {
        res += chown(filename, -1, statbuf->st_gid);  /* Apply group ownership */
    }
#endif

    res += UTIL_fchmod(fd, filename, &curStatBuf, statbuf->st_mode & 0777);  /* Copy file permissions */

#if !defined(_WIN32)
#ifdef ZSTD_HAVE_FCHOWN
    if (fd >= 0) {
        res += fchown(fd, statbuf->st_uid, -1);  /* Apply user ownership */
    } else
#endif
    {
        res += chown(filename, statbuf->st_uid, -1);  /* Apply user ownership */
    }
#endif

    errno = 0;
    UTIL_TRACE_RET(-res);
    return -res; /* number of errors is returned */
}

int UTIL_isDirectory(const char* infilename)
{
    stat_t statbuf;
    int ret;
    UTIL_TRACE_CALL("UTIL_isDirectory(%s)", infilename);
    ret = UTIL_stat(infilename, &statbuf) && UTIL_isDirectoryStat(&statbuf);
    UTIL_TRACE_RET(ret);
    return ret;
}

int UTIL_isDirectoryStat(const stat_t* statbuf)
{
    int ret;
    UTIL_TRACE_CALL("UTIL_isDirectoryStat()");
#if defined(_MSC_VER)
    ret = (statbuf->st_mode & _S_IFDIR) != 0;
#else
    ret = S_ISDIR(statbuf->st_mode) != 0;
#endif
    UTIL_TRACE_RET(ret);
    return ret;
}

int UTIL_compareStr(const void *p1, const void *p2) {
    return strcmp(* (char * const *) p1, * (char * const *) p2);
}

int UTIL_isSameFile(const char* fName1, const char* fName2)
{
    int ret;
    assert(fName1 != NULL); assert(fName2 != NULL);
    UTIL_TRACE_CALL("UTIL_isSameFile(%s, %s)", fName1, fName2);
#if defined(_MSC_VER) || defined(_WIN32)
    /* note : Visual does not support file identification by inode.
     *        inode does not work on Windows, even with a posix layer, like msys2.
     *        The following work-around is limited to detecting exact name repetition only,
     *        aka `filename` is considered different from `subdir/../filename` */
    ret = !strcmp(fName1, fName2);
#else
    {   stat_t file1Stat;
        stat_t file2Stat;
        ret =  UTIL_stat(fName1, &file1Stat)
            && UTIL_stat(fName2, &file2Stat)
            && UTIL_isSameFileStat(fName1, fName2, &file1Stat, &file2Stat);
    }
#endif
    UTIL_TRACE_RET(ret);
    return ret;
}

int UTIL_isSameFileStat(
        const char* fName1, const char* fName2,
        const stat_t* file1Stat, const stat_t* file2Stat)
{
    int ret;
    assert(fName1 != NULL); assert(fName2 != NULL);
    UTIL_TRACE_CALL("UTIL_isSameFileStat(%s, %s)", fName1, fName2);
#if defined(_MSC_VER) || defined(_WIN32)
    /* note : Visual does not support file identification by inode.
     *        inode does not work on Windows, even with a posix layer, like msys2.
     *        The following work-around is limited to detecting exact name repetition only,
     *        aka `filename` is considered different from `subdir/../filename` */
    (void)file1Stat;
    (void)file2Stat;
    ret = !strcmp(fName1, fName2);
#else
    {
        ret =  (file1Stat->st_dev == file2Stat->st_dev)
            && (file1Stat->st_ino == file2Stat->st_ino);
    }
#endif
    UTIL_TRACE_RET(ret);
    return ret;
}

/* UTIL_isFIFO : distinguish named pipes */
int UTIL_isFIFO(const char* infilename)
{
    UTIL_TRACE_CALL("UTIL_isFIFO(%s)", infilename);
/* macro guards, as defined in : https://linux.die.net/man/2/lstat */
#if PLATFORM_POSIX_VERSION >= 200112L
    {
        stat_t statbuf;
        if (UTIL_stat(infilename, &statbuf) && UTIL_isFIFOStat(&statbuf)) {
            UTIL_TRACE_RET(1);
            return 1;
        }
    }
#endif
    (void)infilename;
    UTIL_TRACE_RET(0);
    return 0;
}

/* UTIL_isFIFO : distinguish named pipes */
int UTIL_isFIFOStat(const stat_t* statbuf)
{
/* macro guards, as defined in : https://linux.die.net/man/2/lstat */
#if PLATFORM_POSIX_VERSION >= 200112L
    if (S_ISFIFO(statbuf->st_mode)) return 1;
#endif
    (void)statbuf;
    return 0;
}

/* UTIL_isBlockDevStat : distinguish named pipes */
int UTIL_isBlockDevStat(const stat_t* statbuf)
{
/* macro guards, as defined in : https://linux.die.net/man/2/lstat */
#if PLATFORM_POSIX_VERSION >= 200112L
    if (S_ISBLK(statbuf->st_mode)) return 1;
#endif
    (void)statbuf;
    return 0;
}

int UTIL_isLink(const char* infilename)
{
    UTIL_TRACE_CALL("UTIL_isLink(%s)", infilename);
/* macro guards, as defined in : https://linux.die.net/man/2/lstat */
#if PLATFORM_POSIX_VERSION >= 200112L
    {
        stat_t statbuf;
        int const r = lstat(infilename, &statbuf);
        if (!r && S_ISLNK(statbuf.st_mode)) {
            UTIL_TRACE_RET(1);
            return 1;
        }
    }
#endif
    (void)infilename;
    UTIL_TRACE_RET(0);
    return 0;
}

static int g_fakeStdinIsConsole = 0;
static int g_fakeStderrIsConsole = 0;
static int g_fakeStdoutIsConsole = 0;

int UTIL_isConsole(FILE* file)
{
    int ret;
    UTIL_TRACE_CALL("UTIL_isConsole(%d)", fileno(file));
    if (file == stdin && g_fakeStdinIsConsole)
        ret = 1;
    else if (file == stderr && g_fakeStderrIsConsole)
        ret = 1;
    else if (file == stdout && g_fakeStdoutIsConsole)
        ret = 1;
    else
        ret = IS_CONSOLE(file);
    UTIL_TRACE_RET(ret);
    return ret;
}

void UTIL_fakeStdinIsConsole(void)
{
    g_fakeStdinIsConsole = 1;
}
void UTIL_fakeStdoutIsConsole(void)
{
    g_fakeStdoutIsConsole = 1;
}
void UTIL_fakeStderrIsConsole(void)
{
    g_fakeStderrIsConsole = 1;
}

U64 UTIL_getFileSize(const char* infilename)
{
    stat_t statbuf;
    UTIL_TRACE_CALL("UTIL_getFileSize(%s)", infilename);
    if (!UTIL_stat(infilename, &statbuf)) {
        UTIL_TRACE_RET(-1);
        return UTIL_FILESIZE_UNKNOWN;
    }
    {
        U64 const size = UTIL_getFileSizeStat(&statbuf);
        UTIL_TRACE_RET((int)size);
        return size;
    }
}

U64 UTIL_getFileSizeStat(const stat_t* statbuf)
{
    if (!UTIL_isRegularFileStat(statbuf)) return UTIL_FILESIZE_UNKNOWN;
#if defined(_MSC_VER)
    if (!(statbuf->st_mode & S_IFREG)) return UTIL_FILESIZE_UNKNOWN;
#elif defined(__MINGW32__) && defined (__MSVCRT__)
    if (!(statbuf->st_mode & S_IFREG)) return UTIL_FILESIZE_UNKNOWN;
#else
    if (!S_ISREG(statbuf->st_mode)) return UTIL_FILESIZE_UNKNOWN;
#endif
    return (U64)statbuf->st_size;
}

UTIL_HumanReadableSize_t UTIL_makeHumanReadableSize(U64 size)
{
    UTIL_HumanReadableSize_t hrs;

    if (g_utilDisplayLevel > 3) {
        /* In verbose mode, do not scale sizes down, except in the case of
         * values that exceed the integral precision of a double. */
        if (size >= (1ull << 53)) {
            hrs.value = (double)size / (1ull << 20);
            hrs.suffix = " MiB";
            /* At worst, a double representation of a maximal size will be
             * accurate to better than tens of kilobytes. */
            hrs.precision = 2;
        } else {
            hrs.value = (double)size;
            hrs.suffix = " B";
            hrs.precision = 0;
        }
    } else {
        /* In regular mode, scale sizes down and use suffixes. */
        if (size >= (1ull << 60)) {
            hrs.value = (double)size / (1ull << 60);
            hrs.suffix = " EiB";
        } else if (size >= (1ull << 50)) {
            hrs.value = (double)size / (1ull << 50);
            hrs.suffix = " PiB";
        } else if (size >= (1ull << 40)) {
            hrs.value = (double)size / (1ull << 40);
            hrs.suffix = " TiB";
        } else if (size >= (1ull << 30)) {
            hrs.value = (double)size / (1ull << 30);
            hrs.suffix = " GiB";
        } else if (size >= (1ull << 20)) {
            hrs.value = (double)size / (1ull << 20);
            hrs.suffix = " MiB";
        } else if (size >= (1ull << 10)) {
            hrs.value = (double)size / (1ull << 10);

ext/zstd/programs/util.c  view on Meta::CPAN

/* condition : @file must be valid, and not have reached its end.
 * @return : length of line written into @buf, ended with `\0` instead of '\n',
 *           or 0, if there is no new line */
static size_t readLineFromFile(char* buf, size_t len, FILE* file)
{
    assert(!feof(file));
    if ( fgets(buf, (int) len, file) == NULL ) return 0;
    {   size_t linelen = strlen(buf);
        if (strlen(buf)==0) return 0;
        if (buf[linelen-1] == '\n') linelen--;
        buf[linelen] = '\0';
        return linelen+1;
    }
}

/* Conditions :
 *   size of @inputFileName file must be < @dstCapacity
 *   @dst must be initialized
 * @return : nb of lines
 *       or -1 if there's an error
 */
static int
readLinesFromFile(void* dst, size_t dstCapacity,
            const char* inputFileName)
{
    int nbFiles = 0;
    size_t pos = 0;
    char* const buf = (char*)dst;
    FILE* const inputFile = fopen(inputFileName, "r");

    assert(dst != NULL);

    if(!inputFile) {
        if (g_utilDisplayLevel >= 1) perror("zstd:util:readLinesFromFile");
        return -1;
    }

    while ( !feof(inputFile) ) {
        size_t const lineLength = readLineFromFile(buf+pos, dstCapacity-pos, inputFile);
        if (lineLength == 0) break;
        assert(pos + lineLength <= dstCapacity); /* '=' for inputFile not terminated with '\n' */
        pos += lineLength;
        ++nbFiles;
    }

    CONTROL( fclose(inputFile) == 0 );

    return nbFiles;
}

/*Note: buf is not freed in case function successfully created table because filesTable->fileNames[0] = buf*/
FileNamesTable*
UTIL_createFileNamesTable_fromFileName(const char* inputFileName)
{
    size_t nbFiles = 0;
    char* buf;
    size_t bufSize;
    size_t pos = 0;
    stat_t statbuf;

    if (!UTIL_stat(inputFileName, &statbuf) || !UTIL_isRegularFileStat(&statbuf))
        return NULL;

    {   U64 const inputFileSize = UTIL_getFileSizeStat(&statbuf);
        if(inputFileSize > MAX_FILE_OF_FILE_NAMES_SIZE)
            return NULL;
        bufSize = (size_t)(inputFileSize + 1); /* (+1) to add '\0' at the end of last filename */
    }

    buf = (char*) malloc(bufSize);
    CONTROL( buf != NULL );

    {   int const ret_nbFiles = readLinesFromFile(buf, bufSize, inputFileName);

        if (ret_nbFiles <= 0) {
          free(buf);
          return NULL;
        }
        nbFiles = (size_t)ret_nbFiles;
    }

    {   const char** filenamesTable = (const char**) malloc(nbFiles * sizeof(*filenamesTable));
        CONTROL(filenamesTable != NULL);

        {   size_t fnb;
            for (fnb = 0, pos = 0; fnb < nbFiles; fnb++) {
                filenamesTable[fnb] = buf+pos;
                pos += strlen(buf+pos)+1;  /* +1 for the finishing `\0` */
        }   }
        assert(pos <= bufSize);

        return UTIL_assembleFileNamesTable(filenamesTable, nbFiles, buf);
    }
}

static FileNamesTable*
UTIL_assembleFileNamesTable2(const char** filenames, size_t tableSize, size_t tableCapacity, char* buf)
{
    FileNamesTable* const table = (FileNamesTable*) malloc(sizeof(*table));
    CONTROL(table != NULL);
    table->fileNames = filenames;
    table->buf = buf;
    table->tableSize = tableSize;
    table->tableCapacity = tableCapacity;
    return table;
}

FileNamesTable*
UTIL_assembleFileNamesTable(const char** filenames, size_t tableSize, char* buf)
{
    return UTIL_assembleFileNamesTable2(filenames, tableSize, tableSize, buf);
}

void UTIL_freeFileNamesTable(FileNamesTable* table)
{
    if (table==NULL) return;
    free((void*)table->fileNames);
    free(table->buf);
    free(table);
}

ext/zstd/programs/util.c  view on Meta::CPAN

}

#endif /* #ifdef _WIN32 */

int UTIL_isCompressedFile(const char *inputName, const char *extensionList[])
{
  const char* ext = UTIL_getFileExtension(inputName);
  while(*extensionList!=NULL)
  {
    const int isCompressedExtension = strcmp(ext,*extensionList);
    if(isCompressedExtension==0)
      return 1;
    ++extensionList;
  }
   return 0;
}

/*Utility function to get file extension from file */
const char* UTIL_getFileExtension(const char* infilename)
{
   const char* extension = strrchr(infilename, '.');
   if(!extension || extension==infilename) return "";
   return extension;
}

static int pathnameHas2Dots(const char *pathname)
{
    /* We need to figure out whether any ".." present in the path is a whole
     * path token, which is the case if it is bordered on both sides by either
     * the beginning/end of the path or by a directory separator.
     */
    const char *needle = pathname;
    while (1) {
        needle = strstr(needle, "..");

        if (needle == NULL) {
            return 0;
        }

        if ((needle == pathname || needle[-1] == PATH_SEP)
         && (needle[2] == '\0' || needle[2] == PATH_SEP)) {
            return 1;
        }

        /* increment so we search for the next match */
        needle++;
    };
    return 0;
}

static int isFileNameValidForMirroredOutput(const char *filename)
{
    return !pathnameHas2Dots(filename);
}


#define DIR_DEFAULT_MODE 0755
static mode_t getDirMode(const char *dirName)
{
    stat_t st;
    if (!UTIL_stat(dirName, &st)) {
        UTIL_DISPLAY("zstd: failed to get DIR stats %s: %s\n", dirName, strerror(errno));
        return DIR_DEFAULT_MODE;
    }
    if (!UTIL_isDirectoryStat(&st)) {
        UTIL_DISPLAY("zstd: expected directory: %s\n", dirName);
        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)
{



( run in 0.825 second using v1.01-cache-2.11-cpan-39bf76dae61 )