view th_file.c @ 742:64af66cae541

Always clear stat structure in th_stat_path().
author Matti Hamalainen <ccr@tnsp.org>
date Wed, 14 Dec 2022 01:38:08 +0200
parents 0852bd106034
children db1a132c7754
line wrap: on
line source

/*
 * File, directory etc helper functions
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2016-2022 Tecnic Software productions (TNSP)
 *
 * Please read file 'COPYING' for information on license and distribution.
 */
#include "th_file.h"
#include "th_string.h"
#include <unistd.h>
#if defined(TH_PLAT_WINDOWS)
#  include <shlwapi.h>
#  include <shfolder.h>
#elif defined(TH_PLAT_DOS)
#  include <sys/stat.h>
#  include <sys/types.h>
#else
//#  include <sys/wait.h>
#  include <sys/stat.h>
#  include <sys/types.h>
#endif


char * th_get_home_dir(void)
{
#if defined(TH_PLAT_WINDOWS)
    char tmpPath[MAX_PATH];
    if (SHGetFolderPath(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, tmpPath) == S_OK)
        return th_strdup(tmpPath);
    else
        return th_strdup("");

#elif defined(TH_PLAT_DOS)
    return th_strdup("C:\\");
#else
    return th_strdup(getenv("HOME"));
#endif
}


char * th_get_data_dir()
{
#if defined(TH_PLAT_WINDOWS)
    char tmpPath[MAX_PATH];
    if (SHGetFolderPath(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, tmpPath) == S_OK)
        return th_strdup(tmpPath);
    else
        return th_strdup("");

#elif defined(TH_PLAT_DOS)
    return th_strdup("C:\\");
#else
    return th_strdup(getenv("HOME"));
#endif
}


char * th_get_config_dir(void)
{
#if defined(TH_PLAT_WINDOWS)
    // For Windows, we just use the appdata directory
    return th_get_data_dir();
#elif defined(USE_XDG)
    const char *xdgConfigDir = getenv("XDG_CONFIG_HOME");

    // If XDG is enabled, try the environment variable first
    if (xdgConfigDir != NULL && strcmp(xdgConfigDir, ""))
    {
        return th_strdup_printf("%s%c",
            xdgConfigDir, TH_DIR_SEPARATOR_CHR);
    }
    else
    {
        // Nope, try the obvious alternative
        char *data = th_get_data_dir();
        char *dir = th_strdup_printf("%s%c%s%c",
            data, TH_DIR_SEPARATOR_CHR,
            ".config", TH_DIR_SEPARATOR_CHR);
        th_free(data);
        return dir;
    }
#else
    // XDG not enabled
    return th_get_data_dir();
#endif
}


#ifdef TH_PLAT_WINDOWS
static uint64_t th_convert_windows_time(FILETIME ftime)
{
    uint64_t value = (((uint64_t) ftime.dwHighDateTime) << 32ULL) | ((uint64_t) ftime.dwLowDateTime);

    // Naive conversion (1000 ns / 100) * ns - difference_between_win_to_unix_epoch
    return (value / ((1000 / 100) * 1000 * 1000)) - 11644473600ULL;;
}
#endif


bool th_stat_path(const char *path, th_stat_data *data)
{
    memset(data, 0, sizeof(*data));

#if defined(TH_PLAT_WINDOWS)
    WIN32_FILE_ATTRIBUTE_DATA fdata;
    if (!GetFileAttributesExA(path, GetFileExInfoStandard, &fdata))
        return false;

    data->size   = (((uint64_t) fdata.nFileSizeHigh) << 32ULL) | ((uint64_t) fdata.nFileSizeLow);
    data->ctime  = th_convert_windows_time(fdata.ftCreationTime);
    data->atime  = th_convert_windows_time(fdata.ftLastAccessTime);
    data->mtime  = th_convert_windows_time(fdata.ftLastWriteTime);

    data->flags  = (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? TH_IS_DIR : 0;
    data->flags |= (fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? 0 : TH_IS_WRITABLE;
    data->flags |= TH_IS_READABLE;

#else
#if defined(TH_PLAT_UNIX)
    uid_t uid = geteuid();
    gid_t gid = getegid();
#endif

    struct stat sb;
    if (stat(path, &sb) < 0)
        return false;

    data->size   = sb.st_size;
    data->ctime  = sb.st_ctime;
    data->atime  = sb.st_atime;
    data->mtime  = sb.st_mtime;

    data->flags  = S_ISDIR(sb.st_mode) ? TH_IS_DIR : 0;

#if defined(TH_PLAT_UNIX)
    if ((uid == sb.st_uid && (sb.st_mode & S_IWUSR)) ||
        (gid == sb.st_gid && (sb.st_mode & S_IWGRP)) ||
        (sb.st_mode & S_IWOTH))
        data->flags |= TH_IS_WRITABLE;

    if ((uid == sb.st_uid && (sb.st_mode & S_IRUSR)) ||
        (gid == sb.st_gid && (sb.st_mode & S_IRGRP)) ||
        (sb.st_mode & S_IROTH))
        data->flags |= TH_IS_READABLE;
#elif defined(TH_PLAT_DOS)
    data->flags |= TH_IS_READABLE | TH_IS_WRITABLE;
#endif

#endif

    return true;
}


int th_mkdir_path(const char *cpath, const int mode)
{
    char save, *path = th_strdup(cpath);
    size_t start = 0, end;
    int res = THERR_OK;

    if (path == NULL)
        return THERR_MALLOC;

#if defined(TH_PLAT_WINDOWS)
    (void) mode;
#endif

    // Start creating the directory stucture
    do
    {
        // Split foremost path element out
        for (save = 0, end = start; path[end] != 0; end++)
        if (path[end] == '/' || path[end] == '\\')
        {
            save = path[end];
            path[end] = 0;
            break;
        }

        // If the element is there, create it
        if (path[start] != 0)
        {
            th_stat_data sdata;
            bool exists = th_stat_path(path, &sdata);
            if (exists && (sdata.flags & TH_IS_DIR) == 0)
            {
                res = THERR_FWRITE;
                goto out;
            }

            if (!exists)
            {
#if defined(TH_PLAT_WINDOWS)
                if (!CreateDirectory(path, NULL))
#else
                if (mkdir(path, mode != 0 ? mode : 0711) < 0)
#endif
                {
                    res = th_get_error();
                    goto out;
                }
            }
        }

        // Restore separator character and jump to next element
        path[end] = save;
        start = end + 1;
    } while (save != 0);

out:
    th_free(path);
    return res;
}