From: Wolf Vollprecht Date: Wed, 5 Jan 2022 17:09:24 +0000 (+0100) Subject: make tests build on Windows X-Git-Tag: archive/raspbian/1.2.1+ds1-1+rpi1^2~7^2~1^2~10^2~17 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=13ca71af959a771820f03cca870400e88f84e84a;p=zchunk.git make tests build on Windows --- diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d33adad..fd4315c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,6 +18,11 @@ jobs: name: Compile and Run Tests steps: - uses: actions/checkout@v2 + - uses: actions/checkout@v2 + with: + repository: wolfv/argp-standalone + path: argp-standalone + - name: install mamba uses: mamba-org/provision-with-micromamba@main with: @@ -35,6 +40,15 @@ jobs: run: | cd builddir ninja test + - name: Compile argp-standalone + shell: cmd /C CALL {0} + if: runner.os == 'Windows' + run: | + CALL micromamba activate zchunk_test_env + cd argp-standalone + meson setup builddir + meson compile -C builddir --prefix=%CONDA_PREFIX%\Library + meson install -C builddir - name: Compile zchunk shell: cmd /C CALL {0} if: runner.os == 'Windows' diff --git a/include/zck.h.in b/include/zck.h.in index 0ca5c5f..263c2ea 100644 --- a/include/zck.h.in +++ b/include/zck.h.in @@ -75,6 +75,12 @@ typedef size_t (*zck_wcb)(void *ptr, size_t l, size_t c, void *dl_v); #define ZCK_PUBLIC_API __attribute__((visibility("default"))) #endif +#ifdef _WIN32 +#define ZCK_LOG_ERROR(...) fprintf(stderr, __VA_ARGS__) +#else +#define ZCK_LOG_ERROR(...) dprintf(STDERR_FILENO, __VA_ARGS__) +#endif + /******************************************************************* * Reading a zchunk file *******************************************************************/ diff --git a/meson.build b/meson.build index 34fecbe..02d704e 100644 --- a/meson.build +++ b/meson.build @@ -40,14 +40,6 @@ else curl_dep = dependency('libcurl') endif -if build_machine.system() != 'windows' - if build_machine.system() == 'darwin' or build_machine.system() == 'freebsd' or not cc.links('#include \nstatic error_t parse_opt (int key, char *arg, struct argp_state *state) { argp_usage(state); return 0; }; void main() {}') - argplib = cc.find_library('argp') - else - argplib = dependency('', required : false) - endif -endif - if get_option('with-openssl') == 'disabled' openssl_dep = dependency('', required : false) else @@ -67,9 +59,16 @@ if host_machine.system() == 'windows' inc += include_directories('src/lib/win32') endif +if build_machine.system() == 'windows' or build_machine.system() == 'darwin' or build_machine.system() == 'freebsd' or not cc.links('#include \nstatic error_t parse_opt (int key, char *arg, struct argp_state *state) { argp_usage(state); return 0; }; void main() {}') + inc += include_directories(get_option('prefix') / 'include') + argplib = cc.find_library('argp', dirs : get_option('prefix') / 'lib') +else + argplib = dependency('', required : false) +endif + subdir('include') subdir('src') -if not get_option('coverity') and host_machine.system() != 'windows' +if not get_option('coverity') subdir('test') endif diff --git a/src/lib/win32/basename.c b/src/lib/win32/basename.c new file mode 100644 index 0000000..0d6a8e5 --- /dev/null +++ b/src/lib/win32/basename.c @@ -0,0 +1,24 @@ +#include +#include + +char* basename(char* path) +{ + // char full_path[MAX_PATH], drive[MAX_PATH], dir[MAX_PATH], filename[MAX_PATH], ext[MAX_PATH]; + + // printf("Input: %s", path); + + // _fullpath(full_path, path, MAX_PATH); + // printf("Input: %s", full_path); + + // _splitpath(full_path, drive, dir, filename, ext); + + // printf("%s, %s, %s, %s", drive, dir, filename, ext); + + // const char* res = malloc(MAX_PATH); + // sprintf(res, "%s%s", filename, ext); + // printf("Result: %s", res); + char *p = strrchr (path, '\\'); + return p ? p + 1 : (char *) path; + + // return res; +} \ No newline at end of file diff --git a/src/lib/win32/dirent.h b/src/lib/win32/dirent.h new file mode 100644 index 0000000..a26f7a7 --- /dev/null +++ b/src/lib/win32/dirent.h @@ -0,0 +1,1027 @@ +/* + * Dirent interface for Microsoft Visual Studio + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* Hide warnings about unreferenced local functions */ +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +#elif defined(_MSC_VER) +# pragma warning(disable:4505) +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of the file name without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX+1]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX+1]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + + +/* Dirent functions */ +static DIR *opendir(const char *dirname); +static _WDIR *_wopendir(const wchar_t *dirname); + +static struct dirent *readdir(DIR *dirp); +static struct _wdirent *_wreaddir(_WDIR *dirp); + +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result); +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); + +static int closedir(DIR *dirp); +static int _wclosedir(_WDIR *dirp); + +static void rewinddir(DIR* dirp); +static void _wrewinddir(_WDIR* dirp); + +static int scandir(const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)); + +static int alphasort(const struct dirent **a, const struct dirent **b); + +static int versionsort(const struct dirent **a, const struct dirent **b); + +static int strverscmp(const char *a, const char *b); + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + +/* Compatibility with older Microsoft compilers and non-Microsoft compilers */ +#if !defined(_MSC_VER) || _MSC_VER < 1400 +# define wcstombs_s dirent_wcstombs_s +# define mbstowcs_s dirent_mbstowcs_s +#endif + +/* Optimize dirent_set_errno() away on modern Microsoft compilers */ +#if defined(_MSC_VER) && _MSC_VER >= 1400 +# define dirent_set_errno _set_errno +#endif + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp); + +#if !defined(_MSC_VER) || _MSC_VER < 1400 +static int dirent_mbstowcs_s( + size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords, + const char *mbstr, size_t count); +#endif + +#if !defined(_MSC_VER) || _MSC_VER < 1400 +static int dirent_wcstombs_s( + size_t *pReturnValue, char *mbstr, size_t sizeInBytes, + const wchar_t *wcstr, size_t count); +#endif + +#if !defined(_MSC_VER) || _MSC_VER < 1400 +static void dirent_set_errno(int error); +#endif + + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR *_wopendir(const wchar_t *dirname) +{ + wchar_t *p; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno(ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + _WDIR *dirp = (_WDIR*) malloc(sizeof(struct _WDIR)); + if (!dirp) + return NULL; + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* + * Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + DWORD n = GetFullPathNameW(dirname, 0, NULL, NULL); +#else + /* WinRT */ + size_t n = wcslen(dirname); +#endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc(sizeof(wchar_t) * n + 16); + if (dirp->patt == NULL) + goto exit_closedir; + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW(dirname, n, dirp->patt, NULL); + if (n <= 0) + goto exit_closedir; +#else + /* WinRT */ + wcsncpy_s(dirp->patt, n+1, dirname, n); +#endif + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (!dirent_first(dirp)) + goto exit_closedir; + + /* Success */ + return dirp; + + /* Failure */ +exit_closedir: + _wclosedir(dirp); + return NULL; +} + +/* + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritten by + * subsequent calls to _wreaddir(). + */ +static struct _wdirent *_wreaddir(_WDIR *dirp) +{ + /* + * Read directory entry to buffer. We can safely ignore the return + * value as entry will be set to NULL in case of error. + */ + struct _wdirent *entry; + (void) _wreaddir_r(dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to NULL and returns zero. + */ +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result) +{ + /* Read next directory entry */ + WIN32_FIND_DATAW *datap = dirent_next(dirp); + if (!datap) { + /* Return NULL to indicate end of directory */ + *result = NULL; + return /*OK*/0; + } + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + size_t n = 0; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entry->d_name[n] = datap->cFileName[n]; + n++; + } + entry->d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n; + + /* File type */ + DWORD attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) + entry->d_type = DT_CHR; + else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) + entry->d_type = DT_DIR; + else + entry->d_type = DT_REG; + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof(struct _wdirent); + + /* Set result address */ + *result = entry; + return /*OK*/0; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int _wclosedir(_WDIR *dirp) +{ + if (!dirp) { + dirent_set_errno(EBADF); + return /*failure*/-1; + } + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) + FindClose(dirp->handle); + + /* Release search pattern */ + free(dirp->patt); + + /* Release directory structure */ + free(dirp); + return /*success*/0; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void _wrewinddir(_WDIR* dirp) +{ + if (!dirp) + return; + + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) + FindClose(dirp->handle); + + /* Open new search handle */ + dirent_first(dirp); +} + +/* Get first directory entry */ +static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp) +{ + if (!dirp) + return NULL; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle == INVALID_HANDLE_VALUE) + goto error; + + /* A directory entry is now waiting in memory */ + dirp->cached = 1; + return &dirp->data; + +error: + /* Failed to open directory: no directory entry in memory */ + dirp->cached = 0; + + /* Set error code */ + DWORD errorcode = GetLastError(); + switch (errorcode) { + case ERROR_ACCESS_DENIED: + /* No read access to directory */ + dirent_set_errno(EACCES); + break; + + case ERROR_DIRECTORY: + /* Directory name is invalid */ + dirent_set_errno(ENOTDIR); + break; + + case ERROR_PATH_NOT_FOUND: + default: + /* Cannot find the file */ + dirent_set_errno(ENOENT); + } + return NULL; +} + +/* Get next directory entry */ +static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp) +{ + /* Is the next directory entry already in cache? */ + if (dirp->cached) { + /* Yes, a valid directory entry found in memory */ + dirp->cached = 0; + return &dirp->data; + } + + /* No directory entry in cache */ + if (dirp->handle == INVALID_HANDLE_VALUE) + return NULL; + + /* Read the next directory entry from stream */ + if (FindNextFileW(dirp->handle, &dirp->data) == FALSE) + goto exit_close; + + /* Success */ + return &dirp->data; + + /* Failure */ +exit_close: + FindClose(dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + return NULL; +} + +/* Open directory stream using plain old C-string */ +static DIR *opendir(const char *dirname) +{ + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno(ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + struct DIR *dirp = (DIR*) malloc(sizeof(struct DIR)); + if (!dirp) + return NULL; + + /* Convert directory name to wide-character string */ + wchar_t wname[PATH_MAX + 1]; + size_t n; + int error = mbstowcs_s(&n, wname, PATH_MAX + 1, dirname, PATH_MAX+1); + if (error) + goto exit_failure; + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir(wname); + if (!dirp->wdirp) + goto exit_failure; + + /* Success */ + return dirp; + + /* Failure */ +exit_failure: + free(dirp); + return NULL; +} + +/* Read next directory entry */ +static struct dirent *readdir(DIR *dirp) +{ + /* + * Read directory entry to buffer. We can safely ignore the return + * value as entry will be set to NULL in case of error. + */ + struct dirent *entry; + (void) readdir_r(dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on success. If the end of directory stream is reached, then + * sets result to NULL and returns zero. + */ +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result) +{ + /* Read next directory entry */ + WIN32_FIND_DATAW *datap = dirent_next(dirp->wdirp); + if (!datap) { + /* No more directory entries */ + *result = NULL; + return /*OK*/0; + } + + /* Attempt to convert file name to multi-byte string */ + size_t n; + int error = wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cFileName, PATH_MAX + 1); + + /* + * If the file name cannot be represented by a multi-byte string, then + * attempt to use old 8+3 file name. This allows the program to + * access files although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file name + * unless the file system provides one. At least VirtualBox shared + * folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); + } + + if (!error) { + /* Length of file name excluding zero terminator */ + entry->d_namlen = n - 1; + + /* File attributes */ + DWORD attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) + entry->d_type = DT_CHR; + else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) + entry->d_type = DT_DIR; + else + entry->d_type = DT_REG; + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof(struct dirent); + } else { + /* + * Cannot convert file name to multi-byte string so construct + * an erroneous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_UNKNOWN; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + } + + /* Return pointer to directory entry */ + *result = entry; + return /*OK*/0; +} + +/* Close directory stream */ +static int closedir(DIR *dirp) +{ + int ok; + + if (!dirp) + goto exit_failure; + + /* Close wide-character directory stream */ + ok = _wclosedir(dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free(dirp); + return ok; + +exit_failure: + /* Invalid directory stream */ + dirent_set_errno(EBADF); + return /*failure*/-1; +} + +/* Rewind directory stream to beginning */ +static void rewinddir(DIR* dirp) +{ + if (!dirp) + return; + + /* Rewind wide-character string directory stream */ + _wrewinddir(dirp->wdirp); +} + +/* Scan directory for entries */ +static int scandir( + const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)) +{ + int result; + + /* Open directory stream */ + DIR *dir = opendir(dirname); + if (!dir) { + /* Cannot open directory */ + return /*Error*/ -1; + } + + /* Read directory entries to memory */ + struct dirent *tmp = NULL; + struct dirent **files = NULL; + size_t size = 0; + size_t allocated = 0; + while (1) { + /* Allocate room for a temporary directory entry */ + if (!tmp) { + tmp = (struct dirent*) malloc(sizeof(struct dirent)); + if (!tmp) + goto exit_failure; + } + + /* Read directory entry to temporary area */ + struct dirent *entry; + if (readdir_r(dir, tmp, &entry) != /*OK*/0) + goto exit_failure; + + /* Stop if we already read the last directory entry */ + if (entry == NULL) + goto exit_success; + + /* Determine whether to include the entry in results */ + if (filter && !filter(tmp)) + continue; + + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + /* Compute number of entries in the new table */ + size_t num_entries = size * 2 + 16; + + /* Allocate new pointer table or enlarge existing */ + void *p = realloc(files, sizeof(void*) * num_entries); + if (!p) + goto exit_failure; + + /* Got the memory */ + files = (dirent**) p; + allocated = num_entries; + } + + /* Store the temporary entry to ptr table */ + files[size++] = tmp; + tmp = NULL; + } + +exit_failure: + /* Release allocated file entries */ + for (size_t i = 0; i < size; i++) { + free(files[i]); + } + + /* Release the pointer table */ + free(files); + files = NULL; + + /* Exit with error code */ + result = /*error*/ -1; + goto exit_status; + +exit_success: + /* Sort directory entries */ + qsort(files, size, sizeof(void*), + (int (*) (const void*, const void*)) compare); + + /* Pass pointer table to caller */ + if (namelist) + *namelist = files; + + /* Return the number of directory entries read */ + result = (int) size; + +exit_status: + /* Release temporary directory entry, if we had one */ + free(tmp); + + /* Close directory stream */ + closedir(dir); + return result; +} + +/* Alphabetical sorting */ +static int alphasort(const struct dirent **a, const struct dirent **b) +{ + return strcoll((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int versionsort(const struct dirent **a, const struct dirent **b) +{ + return strverscmp((*a)->d_name, (*b)->d_name); +} + +/* Compare strings */ +static int strverscmp(const char *a, const char *b) +{ + size_t i = 0; + size_t j; + + /* Find first difference */ + while (a[i] == b[i]) { + if (a[i] == '\0') { + /* No difference */ + return 0; + } + ++i; + } + + /* Count backwards and find the leftmost digit */ + j = i; + while (j > 0 && isdigit(a[j-1])) { + --j; + } + + /* Determine mode of comparison */ + if (a[j] == '0' || b[j] == '0') { + /* Find the next non-zero digit */ + while (a[j] == '0' && a[j] == b[j]) { + j++; + } + + /* String with more digits is smaller, e.g 002 < 01 */ + if (isdigit(a[j])) { + if (!isdigit(b[j])) { + return -1; + } + } else if (isdigit(b[j])) { + return 1; + } + } else if (isdigit(a[j]) && isdigit(b[j])) { + /* Numeric comparison */ + size_t k1 = j; + size_t k2 = j; + + /* Compute number of digits in each string */ + while (isdigit(a[k1])) { + k1++; + } + while (isdigit(b[k2])) { + k2++; + } + + /* Number with more digits is bigger, e.g 999 < 1000 */ + if (k1 < k2) + return -1; + else if (k1 > k2) + return 1; + } + + /* Alphabetical comparison */ + return (int) ((unsigned char) a[i]) - ((unsigned char) b[i]); +} + +/* Convert multi-byte string to wide character string */ +#if !defined(_MSC_VER) || _MSC_VER < 1400 +static int dirent_mbstowcs_s( + size_t *pReturnValue, wchar_t *wcstr, + size_t sizeInWords, const char *mbstr, size_t count) +{ + /* Older Visual Studio or non-Microsoft compiler */ + size_t n = mbstowcs(wcstr, mbstr, sizeInWords); + if (wcstr && n >= count) + return /*error*/ 1; + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) + n = sizeInWords - 1; + wcstr[n] = 0; + } + + /* Length of multi-byte string with zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + return 0; +} +#endif + +/* Convert wide-character string to multi-byte string */ +#if !defined(_MSC_VER) || _MSC_VER < 1400 +static int dirent_wcstombs_s( + size_t *pReturnValue, char *mbstr, + size_t sizeInBytes, const wchar_t *wcstr, size_t count) +{ + /* Older Visual Studio or non-Microsoft compiler */ + size_t n = wcstombs(mbstr, wcstr, sizeInBytes); + if (mbstr && n >= count) + return /*error*/1; + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + return 0; +} +#endif + +/* Set errno variable */ +#if !defined(_MSC_VER) || _MSC_VER < 1400 +static void dirent_set_errno(int error) +{ + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; +} +#endif + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/ \ No newline at end of file diff --git a/src/lib/win32/ftruncate.c b/src/lib/win32/ftruncate.c new file mode 100644 index 0000000..3227732 --- /dev/null +++ b/src/lib/win32/ftruncate.c @@ -0,0 +1,21 @@ +#include +#include + +#include + +int ftruncate(int fd, LONGLONG length) +{ + FILE* fp = fdopen(fd, "wb"); + LARGE_INTEGER lisize; + lisize.QuadPart = length; + if (lisize.QuadPart < 0) { + return -1; + } + if (!fp) { + return -1; + } + else if (SetFilePointerEx(fp, lisize, NULL, FILE_BEGIN) == 0 || SetEndOfFile(fp) == 0) { + return -1; + } + return 0; +} \ No newline at end of file diff --git a/src/lib/zck_private.h b/src/lib/zck_private.h index 889efe0..77a8c5f 100644 --- a/src/lib/zck_private.h +++ b/src/lib/zck_private.h @@ -111,7 +111,9 @@ struct zckHash { void *ctx; }; +#ifndef CURLINC_CURL_H typedef void CURL; +#endif typedef struct zckMP { int state; diff --git a/src/memmem.c b/src/memmem.c index e72501b..f7dc20f 100644 --- a/src/memmem.c +++ b/src/memmem.c @@ -37,7 +37,11 @@ void *memmem(const void *haystack, size_t n, const void *needle, size_t m) if (m > n || !m || !n) return NULL; +#ifndef _WIN32 if (__builtin_expect((m > 1), 1)) { +#else + if (m > 1) { +#endif const unsigned char* y = (const unsigned char*) haystack; const unsigned char* x = (const unsigned char*) needle; size_t j = 0; diff --git a/src/meson.build b/src/meson.build index b0316dd..bea4ea4 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,52 +1,56 @@ subdir('lib') -if host_machine.system() != 'windows' - zck = executable( - 'zck', - ['zck.c', 'util_common.c'], - include_directories: inc, - dependencies: argplib, - link_with: zcklib, - install: true - ) - unzck = executable( - 'unzck', - ['unzck.c', 'util_common.c'], - include_directories: inc, - dependencies: argplib, - link_with: zcklib, - install: true - ) - zckdl = executable( - 'zckdl', - ['zck_dl.c', 'util_common.c'], - include_directories: inc, - dependencies: [argplib, curl_dep], - link_with: zcklib, - install: true - ) - zck_gen_zdict = executable( - 'zck_gen_zdict', - ['zck_gen_zdict.c', 'util_common.c'], - include_directories: inc, - dependencies: argplib, - link_with: zcklib, - install: true - ) - zck_read_header = executable( - 'zck_read_header', - ['zck_read_header.c', 'util_common.c'], - include_directories: inc, - dependencies: argplib, - link_with: zcklib, - install: true - ) - zck_delta_size = executable( - 'zck_delta_size', - ['zck_delta_size.c', 'util_common.c', 'memmem.c'], - include_directories: inc, - dependencies: argplib, - link_with: zcklib, - install: true - ) +if host_machine.system() == 'windows' + extra_win_src = ['lib/win32/basename.c', 'lib/win32/ftruncate.c'] +else + extra_win_src = [] endif + +zck = executable( + 'zck', + ['zck.c', 'util_common.c', 'memmem.c'] + extra_win_src, + include_directories: inc, + dependencies: argplib, + link_with: zcklib, + install: true +) +unzck = executable( + 'unzck', + ['unzck.c', 'util_common.c'] + extra_win_src, + include_directories: inc, + dependencies: argplib, + link_with: zcklib, + install: true +) +zckdl = executable( + 'zckdl', + ['zck_dl.c', 'util_common.c'] + extra_win_src, + include_directories: inc, + dependencies: [argplib, curl_dep], + link_with: zcklib, + install: true +) +zck_gen_zdict = executable( + 'zck_gen_zdict', + ['zck_gen_zdict.c', 'util_common.c'] + extra_win_src, + include_directories: inc, + dependencies: argplib, + link_with: zcklib, + install: true +) +zck_read_header = executable( + 'zck_read_header', + ['zck_read_header.c', 'util_common.c'] + extra_win_src, + include_directories: inc, + dependencies: argplib, + link_with: zcklib, + install: true +) +zck_delta_size = executable( + 'zck_delta_size', + ['zck_delta_size.c', 'util_common.c', 'memmem.c'] + extra_win_src, + include_directories: inc, + dependencies: argplib, + link_with: zcklib, + install: true +) diff --git a/src/unzck.c b/src/unzck.c index 7cfe9e1..5c9f0fd 100644 --- a/src/unzck.c +++ b/src/unzck.c @@ -25,6 +25,7 @@ */ #define _GNU_SOURCE +#define STDERR_FILENO 2 #include #include @@ -35,7 +36,9 @@ #include #include #include +#ifndef _WIN32 #include +#endif #include #include #include @@ -124,17 +127,20 @@ int main (int argc, char *argv[]) { if(!arguments.std_out) { if(strlen(arguments.args[0]) < 5 || strcmp(arguments.args[0] + strlen(arguments.args[0]) - 4, ".zck") != 0) { - dprintf(STDERR_FILENO, "Not a *.zck file: %s\n", arguments.args[0]); + ZCK_LOG_ERROR("Not a *.zck file: %s\n", arguments.args[0]); exit(1); } } - int src_fd = open(arguments.args[0], O_RDONLY); + int src_fd = open(arguments.args[0], O_RDONLY | O_BINARY); if(src_fd < 0) { - dprintf(STDERR_FILENO, "Unable to open %s\n", arguments.args[0]); + ZCK_LOG_ERROR("Unable to open %s\n", arguments.args[0]); perror(""); exit(1); } - char *base_name = basename(arguments.args[0]); + // char *base_name = basename(arguments.args[0]); + char* base_name = arguments.args[0]; + // printf("Basename pointer: %d", base_name); + // printf("Got basename: %s", base_name); char *out_name = NULL; if(arguments.dict) out_name = calloc(strlen(base_name) + 3, 1); // len .zck -> .zdict = +2 @@ -145,11 +151,15 @@ int main (int argc, char *argv[]) { if(arguments.dict) snprintf(out_name + strlen(base_name) - 4, 7, ".zdict"); +#ifdef _WIN32 + int dst_fd = _fileno(stdout); +#else int dst_fd = STDOUT_FILENO; +#endif if(!arguments.std_out) { - dst_fd = open(out_name, O_TRUNC | O_WRONLY | O_CREAT, 0666); + dst_fd = open(out_name, O_TRUNC | O_WRONLY | O_CREAT | O_BINARY, 0666); if(dst_fd < 0) { - dprintf(STDERR_FILENO, "Unable to open %s", out_name); + ZCK_LOG_ERROR("Unable to open %s", out_name); perror(""); free(out_name); exit(1); @@ -161,7 +171,7 @@ int main (int argc, char *argv[]) { char *data = NULL; zckCtx *zck = zck_create(); if(!zck_init_read(zck, src_fd)) { - dprintf(STDERR_FILENO, "%s", zck_get_error(zck)); + ZCK_LOG_ERROR("%s", zck_get_error(zck)); goto error2; } @@ -170,7 +180,7 @@ int main (int argc, char *argv[]) { zckChunk *dict = zck_get_first_chunk(zck); ssize_t dict_size = zck_get_chunk_size(dict); if(dict_size < 0) { - dprintf(STDERR_FILENO, "%s", zck_get_error(zck)); + ZCK_LOG_ERROR("%s", zck_get_error(zck)); goto error2; } data = calloc(dict_size, 1); @@ -178,24 +188,24 @@ int main (int argc, char *argv[]) { ssize_t read_size = zck_get_chunk_data(dict, data, dict_size); if(read_size != dict_size) { if(read_size < 0) - dprintf(STDERR_FILENO, "%s", zck_get_error(zck)); + ZCK_LOG_ERROR("%s", zck_get_error(zck)); else - dprintf(STDERR_FILENO, + ZCK_LOG_ERROR( "Dict size doesn't match expected size: %li != %li\n", read_size, dict_size); goto error2; } if(write(dst_fd, data, dict_size) != dict_size) { - dprintf(STDERR_FILENO, "Error writing to %s\n", out_name); + ZCK_LOG_ERROR("Error writing to %s\n", out_name); goto error2; } if(dict_size > 0) { int ret = zck_get_chunk_valid(dict); if(ret < 1) { if(ret == -1) - dprintf(STDERR_FILENO, "Data checksum failed verification\n"); + ZCK_LOG_ERROR("Data checksum failed verification\n"); else - dprintf(STDERR_FILENO, "%s", zck_get_error(zck)); + ZCK_LOG_ERROR("%s", zck_get_error(zck)); goto error2; } } @@ -205,7 +215,7 @@ int main (int argc, char *argv[]) { int ret = zck_validate_data_checksum(zck); if(ret < 1) { if(ret == -1) - dprintf(STDERR_FILENO, "Data checksum failed verification\n"); + ZCK_LOG_ERROR("Data checksum failed verification\n"); goto error2; } @@ -215,23 +225,23 @@ int main (int argc, char *argv[]) { while(true) { ssize_t read = zck_read(zck, data, BUF_SIZE); if(read < 0) { - dprintf(STDERR_FILENO, "%s", zck_get_error(zck)); + ZCK_LOG_ERROR("%s", zck_get_error(zck)); goto error2; } if(read == 0) break; if(write(dst_fd, data, read) != read) { - dprintf(STDERR_FILENO, "Error writing to %s\n", out_name); + ZCK_LOG_ERROR("Error writing to %s\n", out_name); goto error2; } total += read; } if(!zck_close(zck)) { - dprintf(STDERR_FILENO, "%s", zck_get_error(zck)); + ZCK_LOG_ERROR("%s", zck_get_error(zck)); goto error2; } if(arguments.log_level <= ZCK_LOG_INFO) - dprintf(STDERR_FILENO, "Decompressed %lu bytes\n", (unsigned long)total); + ZCK_LOG_ERROR("Decompressed %lu bytes\n", (unsigned long)total); good_exit = true; error2: free(data); @@ -244,4 +254,4 @@ error2: if(!good_exit) exit(1); exit(0); -} +} \ No newline at end of file diff --git a/src/util_common.h b/src/util_common.h index 82e132d..60d2403 100644 --- a/src/util_common.h +++ b/src/util_common.h @@ -8,6 +8,10 @@ #define BUF_SIZE 32768 #endif +#ifndef O_BINARY +#define O_BINARY 0 +#endif + void version(); #endif diff --git a/src/zck.c b/src/zck.c index c6cf5c5..e3ae55b 100644 --- a/src/zck.c +++ b/src/zck.c @@ -35,7 +35,9 @@ #include #include #include +#ifndef _WIN32 #include +#endif #include #include #include @@ -106,7 +108,7 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) { else if (!strcmp(arg, "sha512_128")) arguments->chunk_hashtype = ZCK_HASH_SHA512_128; else { - dprintf(STDERR_FILENO, "Wrong value for chunk hashtype. \n " + ZCK_LOG_ERROR("Wrong value for chunk hashtype. \n " "It should be one of sha1|sha256|sha512|sha512_128 instead of %s\n", arg); return -EINVAL; } @@ -170,7 +172,8 @@ int main (int argc, char *argv[]) { char *base_name = NULL; char *out_name = NULL; if(arguments.output == NULL) { - base_name = basename(arguments.args[0]); + // base_name = basename(arguments.args[0]); + base_name = arguments.args[0]; out_name = malloc(strlen(base_name) + 5); assert(out_name); snprintf(out_name, strlen(base_name) + 5, "%s.zck", base_name); @@ -185,10 +188,10 @@ int main (int argc, char *argv[]) { char *dict = NULL; off_t dict_size = 0; if(arguments.dict != NULL) { - int dict_fd = open(arguments.dict, O_RDONLY); + int dict_fd = open(arguments.dict, O_RDONLY | O_BINARY); if(dict_fd < 0) { - dprintf(STDERR_FILENO, "Unable to open dictionary %s for reading", - arguments.dict); + ZCK_LOG_ERROR("Unable to open dictionary %s for reading", + arguments.dict); perror(""); exit(1); } @@ -211,9 +214,9 @@ int main (int argc, char *argv[]) { close(dict_fd); } - int dst_fd = open(out_name, O_TRUNC | O_WRONLY | O_CREAT, 0666); + int dst_fd = open(out_name, O_TRUNC | O_WRONLY | O_CREAT | O_BINARY, 0666); if(dst_fd < 0) { - dprintf(STDERR_FILENO, "Unable to open %s", out_name); + ZCK_LOG_ERROR("Unable to open %s", out_name); perror(""); if(dict) { free(dict); @@ -225,12 +228,12 @@ int main (int argc, char *argv[]) { zckCtx *zck = zck_create(); if(zck == NULL) { - dprintf(STDERR_FILENO, "%s", zck_get_error(NULL)); + ZCK_LOG_ERROR("%s", zck_get_error(NULL)); zck_clear_error(NULL); exit(1); } if(!zck_init_write(zck, dst_fd)) { - dprintf(STDERR_FILENO, "Unable to write to %s: %s", out_name, + ZCK_LOG_ERROR("Unable to write to %s: %s", out_name, zck_get_error(zck)); exit(1); } @@ -239,56 +242,56 @@ int main (int argc, char *argv[]) { if(arguments.compression_format) { if(strncmp(arguments.compression_format, "zstd", 4) == 0) { if(!zck_set_ioption(zck, ZCK_COMP_TYPE, ZCK_COMP_ZSTD)) { - dprintf(STDERR_FILENO, "%s\n", zck_get_error(zck)); + ZCK_LOG_ERROR("%s\n", zck_get_error(zck)); exit(1); } } else if(strncmp(arguments.compression_format, "none", 4) == 0) { if(!zck_set_ioption(zck, ZCK_COMP_TYPE, ZCK_COMP_NONE)) { - dprintf(STDERR_FILENO, "%s\n", zck_get_error(zck)); + ZCK_LOG_ERROR("%s\n", zck_get_error(zck)); exit(1); } } else { - dprintf(STDERR_FILENO, "Unknown compression type: %s\n", arguments.compression_format); + ZCK_LOG_ERROR("Unknown compression type: %s\n", arguments.compression_format); exit(1); } } if(dict_size > 0) { if(!zck_set_soption(zck, ZCK_COMP_DICT, dict, dict_size)) { - dprintf(STDERR_FILENO, "%s\n", zck_get_error(zck)); + ZCK_LOG_ERROR("%s\n", zck_get_error(zck)); exit(1); } } free(dict); if(arguments.manual_chunk) { if(!zck_set_ioption(zck, ZCK_MANUAL_CHUNK, 1)) { - dprintf(STDERR_FILENO, "%s\n", zck_get_error(zck)); + ZCK_LOG_ERROR("%s\n", zck_get_error(zck)); exit(1); } } if(arguments.uncompressed) { if(!zck_set_ioption(zck, ZCK_UNCOMP_HEADER, 1)) { - dprintf(STDERR_FILENO, "%s\n", zck_get_error(zck)); + ZCK_LOG_ERROR("%s\n", zck_get_error(zck)); exit(1); } } if (arguments.chunk_hashtype != ZCK_HASH_UNKNOWN) { if(!zck_set_ioption(zck, ZCK_HASH_CHUNK_TYPE, arguments.chunk_hashtype)) { - dprintf(STDERR_FILENO, "Unable to set hash type %s\n", zck_get_error(zck)); + ZCK_LOG_ERROR("Unable to set hash type %s\n", zck_get_error(zck)); exit(1); } } char *data; - int in_fd = open(arguments.args[0], O_RDONLY); + int in_fd = open(arguments.args[0], O_RDONLY | O_BINARY); off_t in_size = 0; if(in_fd < 0) { - dprintf(STDERR_FILENO, "Unable to open %s for reading", + ZCK_LOG_ERROR("Unable to open %s for reading", arguments.args[0]); perror(""); exit(1); } in_size = lseek(in_fd, 0, SEEK_END); if(in_size < 0) { - dprintf(STDERR_FILENO, "Unable to seek to end of input file"); + ZCK_LOG_ERROR("Unable to seek to end of input file"); exit(1); } if(lseek(in_fd, 0, SEEK_SET) < 0) { @@ -299,7 +302,7 @@ int main (int argc, char *argv[]) { data = malloc(in_size); assert(data); if(read(in_fd, data, in_size) < in_size) { - dprintf(STDERR_FILENO, "Unable to read from input file\n"); + ZCK_LOG_ERROR("Unable to read from input file\n"); exit(1); } close(in_fd); @@ -331,7 +334,7 @@ int main (int argc, char *argv[]) { /* Buzhash rolling window */ } else { if(zck_write(zck, data, in_size) < 0) { - dprintf(STDERR_FILENO, "%s", zck_get_error(zck)); + ZCK_LOG_ERROR("%s", zck_get_error(zck)); exit(1); } } @@ -342,7 +345,7 @@ int main (int argc, char *argv[]) { exit(1); } if(arguments.log_level <= ZCK_LOG_INFO) { - dprintf(STDERR_FILENO, "Wrote %lu bytes in %lu chunks\n", + ZCK_LOG_ERROR("Wrote %lu bytes in %lu chunks\n", (unsigned long)(zck_get_data_length(zck) + zck_get_header_length(zck)), (long)zck_get_chunk_count(zck)); @@ -350,4 +353,4 @@ int main (int argc, char *argv[]) { zck_free(&zck); close(dst_fd); -} +} \ No newline at end of file diff --git a/src/zck_delta_size.c b/src/zck_delta_size.c index 81fb390..8fdc538 100644 --- a/src/zck_delta_size.c +++ b/src/zck_delta_size.c @@ -109,42 +109,42 @@ int main (int argc, char *argv[]) { zck_set_log_level(arguments.log_level); - int src_fd = open(arguments.args[0], O_RDONLY); + int src_fd = open(arguments.args[0], O_RDONLY | O_BINARY); if(src_fd < 0) { - dprintf(STDERR_FILENO, "Unable to open %s\n", arguments.args[0]); + ZCK_LOG_ERROR("Unable to open %s\n", arguments.args[0]); perror(""); exit(1); } zckCtx *zck_src = zck_create(); if(zck_src == NULL) { - dprintf(STDERR_FILENO, "%s", zck_get_error(NULL)); + ZCK_LOG_ERROR("%s", zck_get_error(NULL)); zck_clear_error(NULL); exit(1); } if(!zck_init_read(zck_src, src_fd)) { - dprintf(STDERR_FILENO, "Error reading %s: %s", arguments.args[0], + ZCK_LOG_ERROR("Error reading %s: %s", arguments.args[0], zck_get_error(zck_src)); zck_free(&zck_src); exit(1); } close(src_fd); - int tgt_fd = open(arguments.args[1], O_RDONLY); + int tgt_fd = open(arguments.args[1], O_RDONLY | O_BINARY); if(tgt_fd < 0) { - dprintf(STDERR_FILENO, "Unable to open %s\n", arguments.args[1]); + ZCK_LOG_ERROR("Unable to open %s\n", arguments.args[1]); perror(""); zck_free(&zck_src); exit(1); } zckCtx *zck_tgt = zck_create(); if(zck_tgt == NULL) { - dprintf(STDERR_FILENO, "%s", zck_get_error(NULL)); + ZCK_LOG_ERROR("%s", zck_get_error(NULL)); zck_clear_error(NULL); zck_free(&zck_src); exit(1); } if(!zck_init_read(zck_tgt, tgt_fd)) { - dprintf(STDERR_FILENO, "Error reading %s: %s", arguments.args[1], + ZCK_LOG_ERROR("Error reading %s: %s", arguments.args[1], zck_get_error(zck_tgt)); zck_free(&zck_src); zck_free(&zck_tgt); @@ -153,10 +153,10 @@ int main (int argc, char *argv[]) { close(tgt_fd); if(zck_get_chunk_hash_type(zck_tgt) != zck_get_chunk_hash_type(zck_src)) { - dprintf(STDERR_FILENO, "ERROR: Chunk hash types don't match:\n"); - dprintf(STDERR_FILENO, " %s: %s\n", arguments.args[0], + ZCK_LOG_ERROR("ERROR: Chunk hash types don't match:\n"); + ZCK_LOG_ERROR(" %s: %s\n", arguments.args[0], zck_hash_name_from_type(zck_get_chunk_hash_type(zck_tgt))); - dprintf(STDERR_FILENO, " %s: %s\n", arguments.args[1], + ZCK_LOG_ERROR(" %s: %s\n", arguments.args[1], zck_hash_name_from_type(zck_get_chunk_hash_type(zck_src))); zck_free(&zck_src); zck_free(&zck_tgt); @@ -168,7 +168,7 @@ int main (int argc, char *argv[]) { exit(1); if(!zck_compare_chunk_digest(tgt_idx, src_idx)) - dprintf(STDERR_FILENO, "WARNING: Dicts don't match\n"); + ZCK_LOG_ERROR("WARNING: Dicts don't match\n"); ssize_t dl_size = zck_get_header_length(zck_tgt); if(dl_size < 0) exit(1); @@ -197,4 +197,4 @@ int main (int argc, char *argv[]) { (long unsigned)zck_get_chunk_count(zck_tgt)); zck_free(&zck_tgt); zck_free(&zck_src); -} +} \ No newline at end of file diff --git a/src/zck_dl.c b/src/zck_dl.c index b19471e..8fdc538 100644 --- a/src/zck_dl.c +++ b/src/zck_dl.c @@ -1,5 +1,5 @@ /* - * Copyright 2018, 2020 Jonathan Dieter + * Copyright 2018 Jonathan Dieter * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -24,8 +24,6 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#define _GNU_SOURCE - #include #include #include @@ -34,43 +32,27 @@ #include #include #include -#include #include -#include #include #include -#include #include "util_common.h" -static char doc[] = "zckdl - Download zchunk file"; +static char doc[] = "zck_delta_size - Calculate the difference between" + " two zchunk files"; -static char args_doc[] = ""; +static char args_doc[] = " "; static struct argp_option options[] = { - {"verbose", 'v', 0, 0, "Increase verbosity"}, - {"quiet", 'q', 0, 0, - "Only show warnings (can be specified twice to only show errors)"}, - {"source", 's', "FILE", 0, "File to use as delta source"}, - {"fail-no-ranges", 1000, 0, 0, - "If server doesn't support ranges, fail instead of downloading full file"}, - {"version", 'V', 0, 0, "Show program version"}, + {"verbose", 'v', 0, 0, + "Increase verbosity (can be specified more than once for debugging)"}, + {"version", 'V', 0, 0, "Show program version"}, { 0 } }; -static int range_attempt[] = { - 255, - 127, - 7, - 2, - 1 -}; - struct arguments { - char *args[1]; + char *args[2]; zck_log_type log_level; - char *source; - int fail_no_ranges; bool exit; }; @@ -82,31 +64,17 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) { switch (key) { case 'v': - if(arguments->log_level > ZCK_LOG_INFO) - arguments->log_level = ZCK_LOG_INFO; arguments->log_level--; if(arguments->log_level < ZCK_LOG_DDEBUG) arguments->log_level = ZCK_LOG_DDEBUG; break; - case 'q': - if(arguments->log_level < ZCK_LOG_INFO) - arguments->log_level = ZCK_LOG_INFO; - arguments->log_level += 1; - if(arguments->log_level > ZCK_LOG_NONE) - arguments->log_level = ZCK_LOG_NONE; - break; - case 's': - arguments->source = arg; - break; case 'V': version(); arguments->exit = true; break; - case 1000: - arguments->fail_no_ranges = 1; - break; + case ARGP_KEY_ARG: - if (state->arg_num >= 1) { + if (state->arg_num >= 2) { argp_usage (state); return EINVAL; } @@ -115,7 +83,7 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) { break; case ARGP_KEY_END: - if (state->arg_num < 1) { + if (state->arg_num < 2) { argp_usage (state); return EINVAL; } @@ -129,355 +97,104 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) { static struct argp argp = {options, parse_opt, args_doc, doc}; -typedef struct dlCtx { - CURL *curl; - zckDL *dl; - int fail_no_ranges; - int range_fail; - int max_ranges; -} dlCtx; - -/* Fail if dl_ctx->fail_no_ranges is set and we get a 200 response */ -size_t dl_header_cb(char *b, size_t l, size_t c, void *dl_v) { - dlCtx *dl_ctx = (dlCtx*)dl_v; - if(dl_ctx->fail_no_ranges) { - long code = -1; - curl_easy_getinfo(dl_ctx->curl, CURLINFO_RESPONSE_CODE, &code); - if(code == 200) { - dl_ctx->range_fail = 1; - return 0; - } - } - return zck_header_cb(b, l, c, dl_ctx->dl); -} - -/* Return 0 on error, -1 on 200 response (if dl_ctx->fail_no_ranges), - * and 1 on complete success */ -int dl_range(dlCtx *dl_ctx, char *url, char *range, int is_chunk) { - if(dl_ctx == NULL || dl_ctx->dl == NULL) { - free(range); - dprintf(STDERR_FILENO, "Struct not defined\n"); - return 0; - } - - CURL *curl = dl_ctx->curl; - CURLcode res; - - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, dl_header_cb); - curl_easy_setopt(curl, CURLOPT_HEADERDATA, dl_ctx); - if(is_chunk) - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, zck_write_chunk_cb); - else - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, zck_write_zck_header_cb); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, dl_ctx->dl); - curl_easy_setopt(curl, CURLOPT_RANGE, range); - res = curl_easy_perform(curl); - free(range); - - if(dl_ctx->range_fail) - return -1; - - if(res != CURLE_OK) { - dprintf(STDERR_FILENO, "Download failed: %s\n", - curl_easy_strerror(res)); - return 0; - } - long code; - curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &code); - if (code != 206 && code != 200) { - dprintf(STDERR_FILENO, "HTTP Error: %li when downloading %s\n", code, - url); - return 0; - } - - - return 1; -} - - -int dl_byte_range(dlCtx *dl_ctx, char *url, int start, int end) { - char *range = NULL; - zck_dl_reset(dl_ctx->dl); - if(start > -1 && end > -1) - range = zck_get_range(start, end); - return dl_range(dl_ctx, url, range, 0); -} - -int dl_bytes(dlCtx *dl_ctx, char *url, size_t bytes, size_t start, - size_t *buffer_len, int log_level) { - if(start + bytes > *buffer_len) { - zckDL *dl = dl_ctx->dl; - - int fd = zck_get_fd(zck_dl_get_zck(dl)); - - if(lseek(fd, *buffer_len, SEEK_SET) == -1) { - dprintf(STDERR_FILENO, "Seek to download location failed: %s\n", - strerror(errno)); - return 0; - } - if(*buffer_len >= start + bytes) - return 1; - - int retval = dl_byte_range(dl_ctx, url, *buffer_len, - (start + bytes) - 1); - if(retval < 1) - return retval; - - if(log_level <= ZCK_LOG_DEBUG) - dprintf(STDERR_FILENO, "Downloading %lu bytes at position %lu\n", - (unsigned long)start+bytes-*buffer_len, - (unsigned long)*buffer_len); - *buffer_len += start + bytes - *buffer_len; - if(lseek(fd, start, SEEK_SET) == -1) { - dprintf(STDERR_FILENO, - "Seek to byte %lu of temporary file failed: %s\n", - (unsigned long)start, strerror(errno)); - return 0; - } - } - return 1; -} - -int dl_header(CURL *curl, zckDL *dl, char *url, int fail_no_ranges, - int log_level) { - size_t buffer_len = 0; - size_t start = 0; - - dlCtx dl_ctx = {0}; - dl_ctx.fail_no_ranges = 1; - dl_ctx.dl = dl; - dl_ctx.curl = curl; - dl_ctx.max_ranges = 1; - - /* Download minimum download size and read magic and hash type */ - int retval = dl_bytes(&dl_ctx, url, zck_get_min_download_size(), start, - &buffer_len, log_level); - if(retval < 1) - return retval; - - zckCtx *zck = zck_dl_get_zck(dl); - if(zck == NULL) - return 0; - - if(!zck_read_lead(zck)) - return 0; - start = zck_get_lead_length(zck); - if(!dl_bytes(&dl_ctx, url, zck_get_header_length(zck) - start, - start, &buffer_len, log_level)) - return 0; - if(!zck_read_header(zck)) - return 0; - return 1; -} - int main (int argc, char *argv[]) { struct arguments arguments = {0}; /* Defaults */ - arguments.log_level = ZCK_LOG_INFO; + arguments.log_level = ZCK_LOG_ERROR; - int retval = argp_parse (&argp, argc, argv, 0, 0, &arguments); + int retval = argp_parse(&argp, argc, argv, 0, 0, &arguments); if(retval || arguments.exit) exit(retval); - curl_global_init(CURL_GLOBAL_ALL); - zck_set_log_level(arguments.log_level); - zckCtx *zck_src = NULL; - if(arguments.source) { - int src_fd = open(arguments.source, O_RDONLY); - if(src_fd < 0) { - dprintf(STDERR_FILENO, "Unable to open %s\n", arguments.source); - perror(""); - exit(10); - } - zck_src = zck_create(); - if(zck_src == NULL) { - dprintf(STDERR_FILENO, "%s", zck_get_error(NULL)); - zck_clear_error(NULL); - exit(10); - } - if(!zck_init_read(zck_src, src_fd)) { - dprintf(STDERR_FILENO, "Unable to open %s: %s", arguments.source, - zck_get_error(zck_src)); - exit(10); - } + int src_fd = open(arguments.args[0], O_RDONLY | O_BINARY); + if(src_fd < 0) { + ZCK_LOG_ERROR("Unable to open %s\n", arguments.args[0]); + perror(""); + exit(1); } - - CURL *curl_ctx = curl_easy_init(); - if(!curl_ctx) { - dprintf(STDERR_FILENO, "Unable to allocate %lu bytes for curl context\n", - (unsigned long)sizeof(CURL)); - exit(10); + zckCtx *zck_src = zck_create(); + if(zck_src == NULL) { + ZCK_LOG_ERROR("%s", zck_get_error(NULL)); + zck_clear_error(NULL); + exit(1); } + if(!zck_init_read(zck_src, src_fd)) { + ZCK_LOG_ERROR("Error reading %s: %s", arguments.args[0], + zck_get_error(zck_src)); + zck_free(&zck_src); + exit(1); + } + close(src_fd); - char *outname = basename(arguments.args[0]); - int dst_fd = open(outname, O_RDWR | O_CREAT, 0666); - if(dst_fd < 0) { - dprintf(STDERR_FILENO, "Unable to open %s: %s\n", outname, - strerror(errno)); - exit(10); + int tgt_fd = open(arguments.args[1], O_RDONLY | O_BINARY); + if(tgt_fd < 0) { + ZCK_LOG_ERROR("Unable to open %s\n", arguments.args[1]); + perror(""); + zck_free(&zck_src); + exit(1); } zckCtx *zck_tgt = zck_create(); if(zck_tgt == NULL) { - dprintf(STDERR_FILENO, "%s", zck_get_error(NULL)); - zck_clear_error(NULL); - exit(10); - } - if(!zck_init_adv_read(zck_tgt, dst_fd)) { - dprintf(STDERR_FILENO, "%s", zck_get_error(zck_tgt)); - exit(10); - } - - zckDL *dl = zck_dl_init(zck_tgt); - if(dl == NULL) { - dprintf(STDERR_FILENO, "%s", zck_get_error(NULL)); + ZCK_LOG_ERROR("%s", zck_get_error(NULL)); zck_clear_error(NULL); - exit(10); - } - - int exit_val = 0; - - retval = dl_header(curl_ctx, dl, arguments.args[0], - arguments.fail_no_ranges, arguments.log_level); - if(!retval) { - exit_val = 10; - goto out; - } - - /* The server doesn't support ranges */ - if(retval == -1) { - if(arguments.fail_no_ranges) { - dprintf(STDERR_FILENO, - "Server doesn't support ranges and --fail-no-ranges was " - "set\n"); - exit_val = 2; - goto out; - } - /* Download the full file */ - lseek(dst_fd, 0, SEEK_SET); - if(ftruncate(dst_fd, 0) < 0) { - perror(NULL); - exit_val = 10; - goto out; - } - dlCtx dl_ctx = {0}; - dl_ctx.dl = dl; - dl_ctx.curl = curl_ctx; - dl_ctx.max_ranges = 0; - if(!dl_byte_range(&dl_ctx, arguments.args[0], -1, -1)) { - exit_val = 10; - goto out; - } - lseek(dst_fd, 0, SEEK_SET); - if(!zck_read_lead(zck_tgt) || !zck_read_header(zck_tgt)) { - exit_val = 10; - goto out; - } - } else { - /* If file is already fully downloaded, let's get out of here! */ - int retval = zck_find_valid_chunks(zck_tgt); - if(retval == 0) { - exit_val = 10; - goto out; - } - if(retval == 1) { - printf("Missing chunks: 0\n"); - printf("Downloaded %lu bytes\n", - (long unsigned)zck_dl_get_bytes_downloaded(dl)); - if(ftruncate(dst_fd, zck_get_length(zck_tgt)) < 0) { - perror(NULL); - exit_val = 10; - goto out; + zck_free(&zck_src); + exit(1); + } + if(!zck_init_read(zck_tgt, tgt_fd)) { + ZCK_LOG_ERROR("Error reading %s: %s", arguments.args[1], + zck_get_error(zck_tgt)); + zck_free(&zck_src); + zck_free(&zck_tgt); + exit(1); + } + close(tgt_fd); + + if(zck_get_chunk_hash_type(zck_tgt) != zck_get_chunk_hash_type(zck_src)) { + ZCK_LOG_ERROR("ERROR: Chunk hash types don't match:\n"); + ZCK_LOG_ERROR(" %s: %s\n", arguments.args[0], + zck_hash_name_from_type(zck_get_chunk_hash_type(zck_tgt))); + ZCK_LOG_ERROR(" %s: %s\n", arguments.args[1], + zck_hash_name_from_type(zck_get_chunk_hash_type(zck_src))); + zck_free(&zck_src); + zck_free(&zck_tgt); + exit(1); + } + zckChunk *tgt_idx = zck_get_first_chunk(zck_tgt); + zckChunk *src_idx = zck_get_first_chunk(zck_src); + if(tgt_idx == NULL || src_idx == NULL) + exit(1); + + if(!zck_compare_chunk_digest(tgt_idx, src_idx)) + ZCK_LOG_ERROR("WARNING: Dicts don't match\n"); + ssize_t dl_size = zck_get_header_length(zck_tgt); + if(dl_size < 0) + exit(1); + ssize_t total_size = zck_get_header_length(zck_tgt); + ssize_t matched_chunks = 0; + for(tgt_idx = zck_get_first_chunk(zck_tgt); tgt_idx; + tgt_idx = zck_get_next_chunk(tgt_idx)) { + bool found = false; + for(src_idx = zck_get_first_chunk(zck_src); src_idx; + src_idx = zck_get_next_chunk(src_idx)) { + if(zck_compare_chunk_digest(tgt_idx, src_idx)) { + found = true; + break; } - exit_val = 0; - goto out; - } - if(zck_src && !zck_copy_chunks(zck_src, zck_tgt)) { - exit_val = 10; - goto out; } - zck_reset_failed_chunks(zck_tgt); - dlCtx dl_ctx = {0}; - dl_ctx.dl = dl; - dl_ctx.curl = curl_ctx; - dl_ctx.max_ranges = range_attempt[0]; - dl_ctx.fail_no_ranges = 1; - int ra_index = 0; - printf("Missing chunks: %i\n", zck_missing_chunks(zck_tgt)); - while(zck_missing_chunks(zck_tgt) > 0) { - dl_ctx.range_fail = 0; - zck_dl_reset(dl); - zckRange *range = zck_get_missing_range(zck_tgt, dl_ctx.max_ranges); - if(range == NULL || !zck_dl_set_range(dl, range)) { - exit_val = 10; - goto out; - } - while(range_attempt[ra_index] > 1 && - range_attempt[ra_index+1] > zck_get_range_count(range)) - ra_index++; - char *range_string = zck_get_range_char(zck_src, range); - if(range_string == NULL) { - exit_val = 10; - goto out; - } - int retval = dl_range(&dl_ctx, arguments.args[0], range_string, 1); - if(retval == -1) { - if(dl_ctx.max_ranges > 1) { - ra_index += 1; - dl_ctx.max_ranges = range_attempt[ra_index]; - } - dprintf(STDERR_FILENO, - "Tried downloading too many ranges, reducing to %i\n", - dl_ctx.max_ranges); - } - if(!zck_dl_set_range(dl, NULL)) { - exit_val = 10; - goto out; - } - zck_range_free(&range); - if(!retval) { - exit_val = 1; - goto out; - } + if(!found) { + dl_size += zck_get_chunk_comp_size(tgt_idx); + } else { + matched_chunks += 1; } + total_size += zck_get_chunk_comp_size(tgt_idx); } - printf("Downloaded %lu bytes\n", - (long unsigned)zck_dl_get_bytes_downloaded(dl)); - if(ftruncate(dst_fd, zck_get_length(zck_tgt)) < 0) { - perror(NULL); - exit_val = 10; - goto out; - } - - switch(zck_validate_data_checksum(zck_tgt)) { - case -1: - exit_val = 1; - break; - case 0: - exit_val = 1; - break; - default: - break; - } -out: - if(exit_val > 0) { - if(zck_is_error(NULL)) { - dprintf(STDERR_FILENO, "%s", zck_get_error(NULL)); - zck_clear_error(NULL); - } - if(zck_is_error(zck_src)) - dprintf(STDERR_FILENO, "%s", zck_get_error(zck_src)); - if(zck_is_error(zck_tgt)) - dprintf(STDERR_FILENO, "%s", zck_get_error(zck_tgt)); - } - zck_dl_free(&dl); + printf("Would download %li of %li bytes\n", (long)dl_size, + (long)total_size); + printf("Matched %li of %lu chunks\n", (long)matched_chunks, + (long unsigned)zck_get_chunk_count(zck_tgt)); zck_free(&zck_tgt); zck_free(&zck_src); - curl_easy_cleanup(curl_ctx); - curl_global_cleanup(); - exit(exit_val); -} +} \ No newline at end of file diff --git a/src/zck_gen_zdict.c b/src/zck_gen_zdict.c index e1885a0..a20ee3d 100644 --- a/src/zck_gen_zdict.c +++ b/src/zck_gen_zdict.c @@ -25,6 +25,7 @@ */ #define _GNU_SOURCE +#define STDERR_FILENO 2 #include #include @@ -34,9 +35,13 @@ #include #include #include +#ifndef _WIN32 #include +#endif #include +#ifndef _WIN32 #include +#endif #include #include #include @@ -119,8 +124,12 @@ static struct argp argp = {options, parse_opt, args_doc, doc}; char *get_tmp_dir(char *old_dir) { char *dir = NULL; if(old_dir == NULL) { - char template[] = "zcktempXXXXXX"; + char* template = "zcktempXXXXXX"; + #ifdef _WIN32 + char *tmpdir = getenv("TEMP"); + #else char *tmpdir = getenv("TMPDIR"); + #endif if(tmpdir == NULL) { tmpdir = "/tmp/"; @@ -129,6 +138,37 @@ char *get_tmp_dir(char *old_dir) { return NULL; } +#ifdef _WIN32 + char* prev_cwd; + + // Get the current working directory: + if ((prev_cwd = _getcwd( NULL, 0 )) == NULL) + { + ZCK_LOG_ERROR("Could not find current workdir"); + return NULL; + } + if (chdir(tmpdir) != 0) + { + ZCK_LOG_ERROR("Could not change to Temp Dir"); + return NULL; + } + printf("generating temp name: ... \n"); + errno_t err = _mktemp_s(template, 14); + if (err) + { + ZCK_LOG_ERROR("Could not generate temporary name"); + return NULL; + } + if (_mkdir(template) != 0) + { + ZCK_LOG_ERROR("Could not create temp folder"); + return NULL; + } + assert(chdir(template) == 0); + char* res = _getcwd(NULL, 0); + assert(chdir(prev_cwd)); + return res; +#else char *base_dir = calloc(strlen(template) + strlen(tmpdir) + 2, 1); assert(base_dir); int i=0; @@ -146,6 +186,7 @@ char *get_tmp_dir(char *old_dir) { perror("ERROR: "); return NULL; } +#endif } else { dir = calloc(strlen(old_dir) + 1, 1); assert(dir); @@ -169,9 +210,9 @@ int main (int argc, char *argv[]) { zck_set_log_level(arguments.log_level); - int src_fd = open(arguments.args[0], O_RDONLY); + int src_fd = open(arguments.args[0], O_RDONLY | O_BINARY); if(src_fd < 0) { - dprintf(STDERR_FILENO, "Unable to open %s\n", arguments.args[0]); + ZCK_LOG_ERROR("Unable to open %s\n", arguments.args[0]); perror(""); exit(1); } @@ -191,14 +232,14 @@ int main (int argc, char *argv[]) { char *data = NULL; zckCtx *zck = zck_create(); if(!zck_init_read(zck, src_fd)) { - dprintf(STDERR_FILENO, "%s", zck_get_error(zck)); + ZCK_LOG_ERROR("%s", zck_get_error(zck)); goto error2; } int ret = zck_validate_data_checksum(zck); if(ret < 1) { if(ret == -1) - dprintf(STDERR_FILENO, "Data checksum failed verification\n"); + ZCK_LOG_ERROR("Data checksum failed verification\n"); goto error2; } @@ -209,7 +250,7 @@ int main (int argc, char *argv[]) { continue; ssize_t chunk_size = zck_get_chunk_size(idx); if(chunk_size < 0) { - dprintf(STDERR_FILENO, "%s", zck_get_error(zck)); + ZCK_LOG_ERROR("%s", zck_get_error(zck)); goto error2; } data = calloc(chunk_size, 1); @@ -217,9 +258,9 @@ int main (int argc, char *argv[]) { ssize_t read_size = zck_get_chunk_data(idx, data, chunk_size); if(read_size != chunk_size) { if(read_size < 0) - dprintf(STDERR_FILENO, "%s", zck_get_error(zck)); + ZCK_LOG_ERROR("%s", zck_get_error(zck)); else - dprintf(STDERR_FILENO, + ZCK_LOG_ERROR( "Chunk %li size doesn't match expected size: %li != %li\n", zck_get_chunk_number(idx), read_size, chunk_size); goto error2; @@ -229,15 +270,15 @@ int main (int argc, char *argv[]) { assert(dict_block); snprintf(dict_block, strlen(dir) + strlen(out_name) + 12, "%s/%s.%li", dir, out_name, zck_get_chunk_number(idx)); - int dst_fd = open(dict_block, O_TRUNC | O_WRONLY | O_CREAT, 0666); + int dst_fd = open(dict_block, O_TRUNC | O_WRONLY | O_CREAT | O_BINARY, 0666); if(dst_fd < 0) { - dprintf(STDERR_FILENO, "Unable to open %s", dict_block); + ZCK_LOG_ERROR("Unable to open %s", dict_block); perror(""); free(dict_block); goto error2; } if(write(dst_fd, data, chunk_size) != chunk_size) { - dprintf(STDERR_FILENO, "Error writing to %s\n", dict_block); + ZCK_LOG_ERROR("Error writing to %s\n", dict_block); free(dict_block); goto error2; } @@ -248,36 +289,47 @@ int main (int argc, char *argv[]) { snprintf(out_name + strlen(base_name) - 4, 7, ".zdict"); if(!zck_close(zck)) { - dprintf(STDERR_FILENO, "%s", zck_get_error(zck)); + ZCK_LOG_ERROR("%s", zck_get_error(zck)); goto error2; } /* Create dictionary */ +#ifdef _WIN32 + char buf[5000]; + sprintf(buf, "zstd --train '%s' -r -o '%s'", dir, out_name); + int w = system(buf); + if (w < 0) + { + ZCK_LOG_ERROR("Error generating dict\n"); + goto error2; + } +#else int pid = fork(); if(pid == 0) { execl("/usr/bin/zstd", "zstd", "--train", dir, "-r", "-o", out_name, NULL); - dprintf(STDERR_FILENO, "Unable to find /usr/bin/zstd\n"); + ZCK_LOG_ERROR("Unable to find /usr/bin/zstd\n"); exit(1); } int wstatus = 0; int w = waitpid(pid, &wstatus, 0); + if (w == -1) { - dprintf(STDERR_FILENO, "Error waiting for zstd\n"); + ZCK_LOG_ERROR("Error waiting for zstd\n"); perror(""); goto error2; } if(WEXITSTATUS(wstatus) != 0) { - dprintf(STDERR_FILENO, "Error generating dict\n"); + ZCK_LOG_ERROR("Error generating dict\n"); goto error2; } - +#endif /* Clean up temporary directory */ if(!arguments.dir) { struct dirent *dp; DIR *dfd; if ((dfd = opendir(dir)) == NULL) { - dprintf(STDERR_FILENO, "Unable to read %s\n", dir); + ZCK_LOG_ERROR("Unable to read %s\n", dir); goto error2; } @@ -289,23 +341,23 @@ int main (int argc, char *argv[]) { snprintf(full_path, strlen(dir) + strlen(dp->d_name) + 2, "%s/%s", dir, dp->d_name); if(unlink(full_path) != 0) { - dprintf(STDERR_FILENO, "Unable to remove %s\n", full_path); + ZCK_LOG_ERROR("Unable to remove %s\n", full_path); perror(""); err = true; } else { if(arguments.log_level <= ZCK_LOG_INFO) - dprintf(STDERR_FILENO, "Removed %s\n", full_path); + ZCK_LOG_ERROR("Removed %s\n", full_path); } free(full_path); } closedir(dfd); if(!err) { if(rmdir(dir) != 0) { - dprintf(STDERR_FILENO, "Unable to remove %s\n", dir); + ZCK_LOG_ERROR("Unable to remove %s\n", dir); perror(""); } } else { - dprintf(STDERR_FILENO, "Errors encountered, not removing %s\n", + ZCK_LOG_ERROR("Errors encountered, not removing %s\n", dir); } } @@ -320,4 +372,4 @@ error2: if(!good_exit) exit(1); exit(0); -} +} \ No newline at end of file diff --git a/src/zck_read_header.c b/src/zck_read_header.c index 5a3b37c..5c6acbb 100644 --- a/src/zck_read_header.c +++ b/src/zck_read_header.c @@ -23,6 +23,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ +#define STDERR_FILENO 2 #include #include @@ -123,7 +124,7 @@ int main (int argc, char *argv[]) { zck_set_log_level(arguments.log_level); - int src_fd = open(arguments.args[0], O_RDONLY); + int src_fd = open(arguments.args[0], O_RDONLY | O_BINARY); if(src_fd < 0) { printf("Unable to open %s\n", arguments.args[0]); perror(""); @@ -132,12 +133,12 @@ int main (int argc, char *argv[]) { zckCtx *zck = zck_create(); if(zck == NULL) { - dprintf(STDERR_FILENO, "%s", zck_get_error(NULL)); + ZCK_LOG_ERROR("%s", zck_get_error(NULL)); zck_clear_error(NULL); exit(1); } if(!zck_init_read(zck, src_fd)) { - dprintf(STDERR_FILENO, "Error reading zchunk header: %s", + ZCK_LOG_ERROR("Error reading zchunk header: %s", zck_get_error(zck)); zck_free(&zck); exit(1); @@ -172,16 +173,16 @@ int main (int argc, char *argv[]) { chk=zck_get_next_chunk(chk)) { char *digest = zck_get_chunk_digest(chk); if(digest == NULL) { - dprintf(STDERR_FILENO, "%s", zck_get_error(zck)); + ZCK_LOG_ERROR("%s", zck_get_error(zck)); exit(1); } char *digest_uncompressed = zck_get_chunk_digest_uncompressed(chk); if (!digest_uncompressed) digest_uncompressed = ""; - if (chk == zck_get_first_chunk(zck)) { - bool has_uncompressed = (strlen(digest_uncompressed) > 0); - if (has_uncompressed) + if (chk == zck_get_first_chunk(zck)) { + bool has_uncompressed = (strlen(digest_uncompressed) > 0); + if (has_uncompressed) printf(" Chunk Checksum %*cChecksum uncompressed %*c Start Comp size Size\n", (((int)zck_get_chunk_digest_size(zck) * 2) - (int)strlen("Checksum")), ' ', ((int)zck_get_chunk_digest_size(zck) * 2) - (int)strlen("Uncompressed Checksum"), ' '); @@ -189,7 +190,7 @@ int main (int argc, char *argv[]) { printf(" Chunk Checksum %*c Start Comp size Size\n", (((int)zck_get_chunk_digest_size(zck) * 2) - (int)strlen("Checksum")), ' '); - } + } printf("%12lu %s %s %12lu %12lu %12lu", (long unsigned)zck_get_chunk_number(chk), digest, @@ -215,4 +216,4 @@ int main (int argc, char *argv[]) { } zck_free(&zck); return 1-valid_cks; -} +} \ No newline at end of file diff --git a/test/copy_chunks.c b/test/copy_chunks.c index 111132c..f489069 100644 --- a/test/copy_chunks.c +++ b/test/copy_chunks.c @@ -32,10 +32,11 @@ #include #include #include +#ifndef _WIN32 #include +#endif #include #include -#include #include "zck_private.h" #include "util.h" @@ -45,12 +46,12 @@ int main (int argc, char *argv[]) { strcpy(path, argv[1]); char *base_name = basename(path); - int in = open(argv[1], O_RDONLY); + int in = open(argv[1], O_RDONLY | O_BINARY); if(in < 0) { perror("Unable to open LICENSE.header.new.nodict.fodt.zck for reading"); exit(1); } - int tgt = open(base_name, O_RDWR | O_CREAT, 0666); + int tgt = open(base_name, O_RDWR | O_CREAT | O_BINARY, 0666); if(tgt < 0) { perror("Unable to open LICENSE.header.new.nodict.fodt.zck for writing"); exit(1); @@ -75,7 +76,7 @@ int main (int argc, char *argv[]) { } /* Open source zchunk file and read header */ - int src = open(argv[2], O_RDONLY); + int src = open(argv[2], O_RDONLY | O_BINARY); if(src < 0) { perror("Unable to open LICENSE.nodict.fodt.zck for reading"); exit(1); @@ -120,4 +121,4 @@ int main (int argc, char *argv[]) { zck_free(&src_zck); free(path); return 0; -} +} \ No newline at end of file diff --git a/test/empty.c b/test/empty.c index 92bf2ae..28dbac2 100644 --- a/test/empty.c +++ b/test/empty.c @@ -41,7 +41,7 @@ static char *checksum="8efaeb8e7b3d51a943353f7e6ca4a22266f18c3ef10478b20d50040f4 int main (int argc, char *argv[]) { /* Create empty zchunk file */ - int out = open("empty.zck", O_WRONLY | O_CREAT | O_TRUNC, 0666); + int out = open("empty.zck", O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); if(out < 0) { perror("Unable to open empty.zck for writing"); exit(1); @@ -60,7 +60,7 @@ int main (int argc, char *argv[]) { zck_free(&zck); /* Open zchunk file and check that checksum matches */ - int in = open("empty.zck", O_RDONLY); + int in = open("empty.zck", O_RDONLY | O_BINARY); if(in < 0) { perror("Unable to open empty.zck for reading"); exit(1); @@ -108,4 +108,4 @@ int main (int argc, char *argv[]) { zck_free(&zck); free(data); return 0; -} +} \ No newline at end of file diff --git a/test/invalid_input_checksum.c b/test/invalid_input_checksum.c index 0e08081..de07252 100644 --- a/test/invalid_input_checksum.c +++ b/test/invalid_input_checksum.c @@ -39,7 +39,7 @@ int main (int argc, char *argv[]) { /* Open zchunk file and verify that an invalid checksum will fail */ - int in = open(argv[1], O_RDONLY); + int in = open(argv[1], O_RDONLY | O_BINARY); if(in < 0) { perror("Unable to open empty.optelems.zck for reading"); exit(1); diff --git a/test/lib/util.h b/test/lib/util.h index 6e52574..e291237 100644 --- a/test/lib/util.h +++ b/test/lib/util.h @@ -23,7 +23,18 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ +#ifdef _WIN32 +#define ZCK_WARN_UNUSED +#else +#define ZCK_WARN_UNUSED __attribute__ ((warn_unused_result)) +#endif + +// O_BINARY is not defined on UNIX systems so we define it here for +// portability purposes +#ifndef O_BINARY +#define O_BINARY 0 +#endif char *get_hash(char *data, size_t length, int type) - __attribute__ ((warn_unused_result)); + ZCK_WARN_UNUSED; diff --git a/test/meson.build b/test/meson.build index ffef3d9..ceaa53d 100644 --- a/test/meson.build +++ b/test/meson.build @@ -1,35 +1,59 @@ util_sources = [] subdir('lib') -incdir = include_directories(['lib', '../src/lib', '../include']) +incdir = [include_directories(['lib', '../src/lib', '../include'])] +if build_machine.system() == 'windows' + incdir += include_directories('../src/lib/win32') +endif + +if host_machine.system() == 'windows' + preprocessor_defines = '-DZCHUNK_STATIC_LIB=1' + win_basename = ['../src/lib/win32/basename.c'] +else + preprocessor_defines = [] + win_basename = [] +endif + + empty = executable('empty', ['empty.c'] + util_sources, include_directories: incdir, - dependencies: [zstd_dep, openssl_dep]) + dependencies: [zstd_dep, openssl_dep], + c_args: preprocessor_defines) optelems = executable('optelems', ['optelems.c'] + util_sources, include_directories: incdir, - dependencies: [zstd_dep, openssl_dep]) -copy_chunks = executable('copy_chunks', ['copy_chunks.c'] + util_sources, + dependencies: [zstd_dep, openssl_dep], + c_args: preprocessor_defines) +copy_chunks = executable('copy_chunks', ['copy_chunks.c'] + win_basename + util_sources, include_directories: incdir, - dependencies: [zstd_dep, openssl_dep]) + dependencies: [zstd_dep, openssl_dep], + c_args: preprocessor_defines) invalid_input_checksum = executable('invalid_input_checksum', ['invalid_input_checksum.c'] + util_sources, include_directories: incdir, - dependencies: [zstd_dep, openssl_dep]) + dependencies: [zstd_dep, openssl_dep], + c_args: preprocessor_defines) read_single_chunk = executable('read_single_chunk', ['read_single_chunk.c'] + util_sources, include_directories: incdir, - dependencies: [zstd_dep, openssl_dep]) + dependencies: [zstd_dep, openssl_dep], + c_args: preprocessor_defines) read_single_comp_chunk = executable('read_single_comp_chunk', ['read_single_comp_chunk.c'] + util_sources, include_directories: incdir, - dependencies: [zstd_dep, openssl_dep]) -shacheck = executable('shacheck', ['shacheck.c'] + util_sources, include_directories: incdir, dependencies: [zstd_dep, openssl_dep]) + dependencies: [zstd_dep, openssl_dep], + c_args: preprocessor_defines) +shacheck = executable('shacheck', + ['shacheck.c'] + util_sources, + include_directories: incdir, + dependencies: [zstd_dep, openssl_dep], + c_args: preprocessor_defines) zck_cmp_uncomp = executable( 'zck_cmp_uncomp', ['zck_cmp_uncomp.c'], - include_directories: incdir, + include_directories: inc + incdir, dependencies: argplib, link_with: zcklib, + c_args: preprocessor_defines, install: false ) file_path = join_paths(meson.source_root(), 'test/files') diff --git a/test/optelems.c b/test/optelems.c index ab96890..f6d5316 100644 --- a/test/optelems.c +++ b/test/optelems.c @@ -42,7 +42,7 @@ int main (int argc, char *argv[]) { char data[1000] = {0}; /* Open zchunk file and verify that zck->has_optional_elems is set */ - int in = open(argv[1], O_RDONLY); + int in = open(argv[1], O_RDONLY | O_BINARY); if(in < 0) { perror("Unable to open empty.optelems.zck for reading"); exit(1); diff --git a/test/read_single_chunk.c b/test/read_single_chunk.c index 432cb5d..6d19799 100644 --- a/test/read_single_chunk.c +++ b/test/read_single_chunk.c @@ -42,7 +42,7 @@ char *echecksum = int main (int argc, char *argv[]) { /* Open zchunk file and verify second checksum */ - int in = open(argv[1], O_RDONLY); + int in = open(argv[1], O_RDONLY | O_BINARY); if(in < 0) { perror("Unable to open LICENSE.dict.fodt.zck for reading"); exit(1); diff --git a/test/read_single_comp_chunk.c b/test/read_single_comp_chunk.c index 82c94ed..61ecedc 100644 --- a/test/read_single_comp_chunk.c +++ b/test/read_single_comp_chunk.c @@ -42,7 +42,7 @@ char *echecksum = int main (int argc, char *argv[]) { /* Open zchunk file and verify second checksum */ - int in = open(argv[1], O_RDONLY); + int in = open(argv[1], O_RDONLY | O_BINARY); if(in < 0) { perror("Unable to open LICENSE.dict.fodt.zck for reading"); exit(1); diff --git a/test/shacheck.c b/test/shacheck.c index 5b098d4..9954342 100644 --- a/test/shacheck.c +++ b/test/shacheck.c @@ -34,7 +34,9 @@ #include #include #include +#ifndef _WIN32 #include +#endif #include "zck_private.h" #include "util.h" @@ -65,6 +67,9 @@ int main (int argc, char *argv[]) { args[i] = untaint(argv[i+3]); int status; +#ifdef _WIN32 + status = system(cmd); +#else pid_t child_pid; child_pid = fork(); @@ -78,13 +83,14 @@ int main (int argc, char *argv[]) { } else { waitpid(child_pid, &status, 0); } +#endif if (status != 0) { printf("Error running command\n"); exit(1); } /* Open zchunk file and check that checksum matches */ - int in = open(outf, O_RDONLY); + int in = open(outf, O_RDONLY | O_BINARY); if(in < 0) { perror(""); printf("Unable to open %s for reading", outf); @@ -111,4 +117,4 @@ int main (int argc, char *argv[]) { free(args[i]); free(args); return 0; -} +} \ No newline at end of file diff --git a/test/zck_cmp_uncomp.c b/test/zck_cmp_uncomp.c index f7952c1..38dceed 100644 --- a/test/zck_cmp_uncomp.c +++ b/test/zck_cmp_uncomp.c @@ -35,10 +35,13 @@ #include #include #include +#ifndef _WIN32 #include +#endif #include #include #include +#include "util.h" #define BUFSIZE 16384 static char doc[] = "zck_gen_header - Compare a file with a zck and reports which chunks changed"; @@ -119,30 +122,30 @@ int main (int argc, char *argv[]) { exit(retval); if (argc < 1) { - dprintf(STDERR_FILENO, "Usage : %s filename", argv[0]); - exit(1); + ZCK_LOG_ERROR("Usage : %s filename", argv[0]); + exit(1); } zck_set_log_level(arguments.log_level); - int dst_fd = open("/dev/null", O_TRUNC | O_WRONLY | O_CREAT, 0666); + int dst_fd = open("/dev/null", O_TRUNC | O_WRONLY | O_CREAT | O_BINARY, 0666); zckCtx *zckSrc = zck_create(); zckCtx *zckDst = zck_create(); if(!zckSrc || !zckDst) { - dprintf(STDERR_FILENO, "%s", zck_get_error(NULL)); + ZCK_LOG_ERROR("%s", zck_get_error(NULL)); zck_clear_error(NULL); exit(1); } if(!zck_init_write(zckSrc, dst_fd)) { - dprintf(STDERR_FILENO, "Unable to write %s ", + ZCK_LOG_ERROR("Unable to write %s ", zck_get_error(zckSrc)); exit(1); } - int in_fd = open(arguments.args[0], O_RDONLY); + int in_fd = open(arguments.args[0], O_RDONLY | O_BINARY); off_t in_size = 0; if(in_fd < 0) { - dprintf(STDERR_FILENO, "Unable to open %s for reading", + ZCK_LOG_ERROR("Unable to open %s for reading", arguments.args[0]); perror(""); exit(1); @@ -151,16 +154,16 @@ int main (int argc, char *argv[]) { /* * Read header in the zck file */ - int zck_fd = open(arguments.args[1], O_RDONLY); + int zck_fd = open(arguments.args[1], O_RDONLY | O_BINARY); if(zck_fd < 0) { - dprintf(STDERR_FILENO, "Unable to open %s for reading", + ZCK_LOG_ERROR("Unable to open %s for reading", arguments.args[1]); perror(""); exit(1); } if(!zck_init_read(zckDst, zck_fd)) { - dprintf(STDERR_FILENO, "Error reading zchunk header: %s", + ZCK_LOG_ERROR("Error reading zchunk header: %s", zck_get_error(zckDst)); zck_free(&zckSrc); zck_free(&zckDst); @@ -169,7 +172,7 @@ int main (int argc, char *argv[]) { in_size = lseek(in_fd, 0, SEEK_END); if(in_size < 0) { - dprintf(STDERR_FILENO, "Unable to seek to end of input file"); + ZCK_LOG_ERROR("Unable to seek to end of input file"); exit(1); } if(lseek(in_fd, 0, SEEK_SET) < 0) { @@ -178,33 +181,33 @@ int main (int argc, char *argv[]) { } if(!zck_set_ioption(zckSrc, ZCK_UNCOMP_HEADER, 1)) { - dprintf(STDERR_FILENO, "%s\n", zck_get_error(zckSrc)); + ZCK_LOG_ERROR("%s\n", zck_get_error(zckSrc)); exit(1); } if(!zck_set_ioption(zckSrc, ZCK_COMP_TYPE, ZCK_COMP_NONE)) exit(1); if(!zck_set_ioption(zckSrc, ZCK_HASH_CHUNK_TYPE, ZCK_HASH_SHA256)) { - dprintf(STDERR_FILENO, "Unable to set hash type %s\n", zck_get_error(zckSrc)); + ZCK_LOG_ERROR("Unable to set hash type %s\n", zck_get_error(zckSrc)); exit(1); } char *buf = malloc(BUFSIZE); if (!buf) { - dprintf(STDERR_FILENO, "Unable to allocate buffer\n"); + ZCK_LOG_ERROR("Unable to allocate buffer\n"); exit(1); } ssize_t n; while ((n = read(in_fd, buf, BUFSIZE)) > 0) { if (zck_write(zckSrc, buf, n) < 0) { - dprintf(STDERR_FILENO, "zck_write failed: %s\n", zck_get_error(zckSrc)); + ZCK_LOG_ERROR("zck_write failed: %s\n", zck_get_error(zckSrc)); exit(1); } } /* * Start comparison */ - dprintf(STDOUT_FILENO, "Compare original image with chunks in zck\n"); - dprintf(STDOUT_FILENO, "-----------------------------------------\n"); + printf("Compare original image with chunks in zck\n"); + printf("-----------------------------------------\n"); zck_generate_hashdb(zckSrc); zck_find_matching_chunks(zckSrc, zckDst); @@ -213,7 +216,7 @@ int main (int argc, char *argv[]) { size_t todwl = 0; size_t reuse = 0; while (iter) { - dprintf(STDOUT_FILENO, "%12lu %s %s %12lu %12lu\n", + printf("%12lu %s %s %12lu %12lu\n", zck_get_chunk_number(iter), zck_get_chunk_valid(iter) ? "SRC" : "DST", zck_get_chunk_digest_uncompressed(iter), @@ -229,12 +232,12 @@ int main (int argc, char *argv[]) { iter = zck_get_next_chunk(iter); } - dprintf (STDOUT_FILENO, "\n\nTotal to be reused : %12lu\n", reuse); - dprintf (STDOUT_FILENO, "Total to be downloaded : %12lu\n", todwl); + printf("\n\nTotal to be reused : %12lu\n", reuse); + printf("Total to be downloaded : %12lu\n", todwl); close(in_fd); close(zck_fd); zck_free(&zckSrc); zck_free(&zckDst); close(dst_fd); -} +} \ No newline at end of file