Compress-Stream-Zstd

 view release on metacpan or  search on metacpan

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

/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under both the BSD-style license (found in the
 * LICENSE file in the root directory of this source tree) and the GPLv2 (found
 * in the COPYING file in the root directory of this source tree).
 * You may select, at your option, one of the above-listed licenses.
 */

#if defined (__cplusplus)
extern "C" {
#endif


/*-****************************************
*  Dependencies
******************************************/
#include "util.h"       /* note : ensure that platform.h is included first ! */
#include <stdlib.h>     /* malloc, realloc, free */
#include <stdio.h>      /* fprintf */
#include <time.h>       /* clock_t, clock, CLOCKS_PER_SEC, nanosleep */
#include <errno.h>
#include <assert.h>

#if defined(_WIN32)
#  include <sys/utime.h>  /* utime */
#  include <io.h>         /* _chmod */
#else
#  include <unistd.h>     /* chown, stat */
#  if PLATFORM_POSIX_VERSION < 200809L || !defined(st_mtime)
#    include <utime.h>    /* utime */
#  else
#    include <fcntl.h>    /* AT_FDCWD */
#    include <sys/stat.h> /* utimensat */
#  endif
#endif

#if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__)
#include <direct.h>     /* needed for _mkdir in windows */
#endif

#if defined(__linux__) || (PLATFORM_POSIX_VERSION >= 200112L)  /* opendir, readdir require POSIX.1-2001 */
#  include <dirent.h>       /* opendir, readdir */
#  include <string.h>       /* strerror, memcpy */
#endif /* #ifdef _WIN32 */

/*-****************************************
*  Internal Macros
******************************************/

/* CONTROL is almost like an assert(), but is never disabled.
 * It's designed for failures that may happen rarely,
 * but we don't want to maintain a specific error code path for them,
 * such as a malloc() returning NULL for example.
 * Since it's always active, this macro can trigger side effects.
 */
#define CONTROL(c)  {         \
    if (!(c)) {               \
        UTIL_DISPLAYLEVEL(1, "Error : %s, %i : %s",  \
                          __FILE__, __LINE__, #c);   \
        exit(1);              \
}   }

/* console log */
#define UTIL_DISPLAY(...)         fprintf(stderr, __VA_ARGS__)
#define UTIL_DISPLAYLEVEL(l, ...) { if (g_utilDisplayLevel>=l) { UTIL_DISPLAY(__VA_ARGS__); } }

static int g_traceDepth = 0;
int g_traceFileStat = 0;

#define UTIL_TRACE_CALL(...)                                         \
    {                                                                \
        if (g_traceFileStat) {                                       \
            UTIL_DISPLAY("Trace:FileStat: %*s> ", g_traceDepth, ""); \
            UTIL_DISPLAY(__VA_ARGS__);                               \
            UTIL_DISPLAY("\n");                                      \
            ++g_traceDepth;                                          \
        }                                                            \
    }

#define UTIL_TRACE_RET(ret)                                                     \
    {                                                                           \
        if (g_traceFileStat) {                                                  \
            --g_traceDepth;                                                     \
            UTIL_DISPLAY("Trace:FileStat: %*s< %d\n", g_traceDepth, "", (ret)); \
        }                                                                      \
    }

/* A modified version of realloc().
 * If UTIL_realloc() fails the original block is freed.
 */
UTIL_STATIC void* UTIL_realloc(void *ptr, size_t size)
{
    void *newptr = realloc(ptr, size);
    if (newptr) return newptr;
    free(ptr);
    return NULL;
}

#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)



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