| /* Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <string.h> |
| #include <stdlib.h> |
| #include <ctype.h> |
| #include <stdio.h> |
| #include <stdarg.h> |
| #include <errno.h> |
| #include <sys/stat.h> |
| |
| #ifndef true |
| #define true 1 |
| #define false 0 |
| #endif |
| |
| #include "CFCUtil.h" |
| |
| void |
| CFCUtil_null_check(const void *arg, const char *name, const char *file, |
| int line) { |
| if (!arg) { |
| CFCUtil_die("%s cannot be NULL at %s line %d", name, file, line); |
| } |
| } |
| |
| char* |
| CFCUtil_strdup(const char *string) { |
| if (!string) { return NULL; } |
| return CFCUtil_strndup(string, strlen(string)); |
| } |
| |
| char* |
| CFCUtil_strndup(const char *string, size_t len) { |
| if (!string) { return NULL; } |
| char *copy = (char*)MALLOCATE(len + 1); |
| memcpy(copy, string, len); |
| copy[len] = '\0'; |
| return copy; |
| } |
| |
| char* |
| CFCUtil_cat(char *string, ...) { |
| va_list args; |
| char *appended; |
| CFCUTIL_NULL_CHECK(string); |
| size_t size = strlen(string) + 1; |
| va_start(args, string); |
| while (NULL != (appended = va_arg(args, char*))) { |
| size += strlen(appended); |
| string = (char*)REALLOCATE(string, size); |
| strcat(string, appended); |
| } |
| va_end(args); |
| return string; |
| } |
| |
| void |
| CFCUtil_trim_whitespace(char *text) { |
| if (!text) { |
| return; |
| } |
| |
| // Find start. |
| char *ptr = text; |
| while (*ptr != '\0' && isspace(*ptr)) { ptr++; } |
| |
| // Find end. |
| size_t orig_len = strlen(text); |
| char *limit = text + orig_len; |
| for (; limit > text; limit--) { |
| if (!isspace(*(limit - 1))) { break; } |
| } |
| |
| // Modify string in place and NULL-terminate. |
| while (ptr < limit) { |
| *text++ = *ptr++; |
| } |
| *text = '\0'; |
| } |
| |
| void* |
| CFCUtil_wrapped_malloc(size_t count, const char *file, int line) { |
| void *pointer = malloc(count); |
| if (pointer == NULL && count != 0) { |
| if (sizeof(long) >= sizeof(size_t)) { |
| fprintf(stderr, "Can't malloc %lu bytes at %s line %d\n", |
| (unsigned long)count, file, line); |
| } |
| else { |
| fprintf(stderr, "malloc failed at %s line %d\n", file, line); |
| } |
| exit(1); |
| } |
| return pointer; |
| } |
| |
| void* |
| CFCUtil_wrapped_calloc(size_t count, size_t size, const char *file, int line) { |
| void *pointer = calloc(count, size); |
| if (pointer == NULL && count != 0) { |
| if (sizeof(long) >= sizeof(size_t)) { |
| fprintf(stderr, |
| "Can't calloc %lu elements of size %lu at %s line %d\n", |
| (unsigned long)count, (unsigned long)size, file, line); |
| } |
| else { |
| fprintf(stderr, "calloc failed at %s line %d\n", file, line); |
| } |
| exit(1); |
| } |
| return pointer; |
| } |
| |
| void* |
| CFCUtil_wrapped_realloc(void *ptr, size_t size, const char *file, int line) { |
| void *pointer = realloc(ptr, size); |
| if (pointer == NULL && size != 0) { |
| if (sizeof(long) >= sizeof(size_t)) { |
| fprintf(stderr, "Can't realloc %lu bytes at %s line %d\n", |
| (unsigned long)size, file, line); |
| } |
| else { |
| fprintf(stderr, "realloc failed at %s line %d\n", file, line); |
| } |
| exit(1); |
| } |
| return pointer; |
| } |
| |
| void |
| CFCUtil_wrapped_free(void *ptr) { |
| free(ptr); |
| } |
| |
| int |
| CFCUtil_current(const char *orig, const char *dest) { |
| // If the destination file doesn't exist, we're not current. |
| struct stat dest_stat; |
| if (stat(dest, &dest_stat) == -1) { |
| return false; |
| } |
| |
| // If the source file is newer than the dest, we're not current. |
| struct stat orig_stat; |
| if (stat(orig, &orig_stat) == -1) { |
| CFCUtil_die("Missing source file '%s'", orig); |
| } |
| if (orig_stat.st_mtime > dest_stat.st_mtime) { |
| return false; |
| } |
| |
| // Current! |
| return 1; |
| } |
| |
| void |
| CFCUtil_write_file(const char *filename, const char *content, size_t len) { |
| FILE *fh = fopen(filename, "w+"); |
| if (fh == NULL) { |
| CFCUtil_die("Couldn't open '%s': %s", filename, strerror(errno)); |
| } |
| fwrite(content, sizeof(char), len, fh); |
| if (fclose(fh)) { |
| CFCUtil_die("Error when closing '%s': %s", filename, strerror(errno)); |
| } |
| } |
| |
| char* |
| CFCUtil_slurp_text(const char *file_path, size_t *len_ptr) { |
| FILE *const file = fopen(file_path, "r"); |
| char *contents; |
| size_t binary_len; |
| long text_len; |
| |
| /* Sanity check. */ |
| if (file == NULL) { |
| CFCUtil_die("Error opening file '%s': %s", file_path, strerror(errno)); |
| } |
| |
| /* Find length; return NULL if the file has a zero-length. */ |
| binary_len = CFCUtil_flength(file); |
| if (binary_len == 0) { |
| *len_ptr = 0; |
| return NULL; |
| } |
| |
| /* Allocate memory and read the file. */ |
| contents = (char*)MALLOCATE(binary_len * sizeof(char) + 1); |
| text_len = fread(contents, sizeof(char), binary_len, file); |
| |
| /* Weak error check, because CRLF might result in fewer chars read. */ |
| if (text_len <= 0) { |
| CFCUtil_die("Tried to read %ld bytes of '%s', got return code %ld", |
| (long)binary_len, file_path, (long)text_len); |
| } |
| |
| /* NULL-terminate. */ |
| contents[text_len] = '\0'; |
| |
| /* Set length pointer for benefit of caller. */ |
| *len_ptr = text_len; |
| |
| /* Clean up. */ |
| if (fclose(file)) { |
| CFCUtil_die("Error closing file '%s': %s", file_path, strerror(errno)); |
| } |
| |
| return contents; |
| } |
| |
| void |
| CFCUtil_write_if_changed(const char *path, const char *content, size_t len) { |
| FILE *f = fopen(path, "r"); |
| if (f) { // Does file exist? |
| if (fclose(f)) { |
| CFCUtil_die("Error closing file '%s': %s", path, strerror(errno)); |
| } |
| size_t existing_len; |
| char *existing = CFCUtil_slurp_text(path, &existing_len); |
| int changed = true; |
| if (existing_len == len && strcmp(content, existing) == 0) { |
| changed = false; |
| } |
| FREEMEM(existing); |
| if (changed == false) { |
| return; |
| } |
| } |
| CFCUtil_write_file(path, content, len); |
| } |
| |
| long |
| CFCUtil_flength(void *file) { |
| FILE *f = (FILE*)file; |
| const long bookmark = (long)ftell(f); |
| long check_val; |
| long len; |
| |
| /* Seek to end of file and check length. */ |
| check_val = fseek(f, 0, SEEK_END); |
| if (check_val == -1) { CFCUtil_die("fseek error : %s\n", strerror(errno)); } |
| len = (long)ftell(f); |
| if (len == -1) { CFCUtil_die("ftell error : %s\n", strerror(errno)); } |
| |
| /* Return to where we were. */ |
| check_val = fseek(f, bookmark, SEEK_SET); |
| if (check_val == -1) { CFCUtil_die("fseek error : %s\n", strerror(errno)); } |
| |
| return len; |
| } |
| |
| // Note: this has to be defined before including the Perl headers because they |
| // redefine stat() in an incompatible way on certain systems (Windows). |
| int |
| CFCUtil_is_dir(const char *path) { |
| struct stat stat_buf; |
| int stat_check = stat(path, &stat_buf); |
| if (stat_check == -1) { |
| return false; |
| } |
| return (stat_buf.st_mode & S_IFDIR) ? true : false; |
| } |
| |
| int |
| CFCUtil_make_path(const char *path) { |
| CFCUTIL_NULL_CHECK(path); |
| char *target = CFCUtil_strdup(path); |
| size_t orig_len = strlen(target); |
| size_t len = orig_len; |
| for (size_t i = 0; i <= len; i++) { |
| #ifndef WIN32 |
| if (target[i] == '\\') { |
| i++; |
| continue; |
| } |
| #endif |
| if (target[i] == CFCUTIL_PATH_SEP_CHAR || i == len) { |
| target[i] = 0; // NULL-terminate. |
| struct stat stat_buf; |
| int stat_check = stat(target, &stat_buf); |
| if (stat_check != -1) { |
| if (!(stat_buf.st_mode & S_IFDIR)) { |
| CFCUtil_die("%s isn't a directory", target); |
| } |
| } |
| else { |
| int success = CFCUtil_make_dir(target); |
| if (!success) { |
| FREEMEM(target); |
| return false; |
| } |
| } |
| target[i] = CFCUTIL_PATH_SEP_CHAR; |
| } |
| } |
| |
| FREEMEM(target); |
| return true; |
| } |
| |
| /******************************** WINDOWS **********************************/ |
| #ifdef _WIN32 |
| |
| #include <direct.h> |
| #include <windows.h> |
| |
| typedef struct WinDH { |
| HANDLE handle; |
| WIN32_FIND_DATA *find_data; |
| char path[MAX_PATH + 1]; |
| int first_time; |
| } WinDH; |
| |
| int |
| CFCUtil_make_dir(const char *dir) { |
| return !mkdir(dir); |
| } |
| |
| void* |
| CFCUtil_opendir(const char *dir) { |
| size_t dirlen = strlen(dir); |
| if (dirlen >= MAX_PATH - 2) { |
| CFCUtil_die("Exceeded MAX_PATH(%d): %s", (int)MAX_PATH, dir); |
| } |
| WinDH *dh = (WinDH*)CALLOCATE(1, sizeof(WinDH)); |
| dh->find_data = (WIN32_FIND_DATA*)MALLOCATE(sizeof(WIN32_FIND_DATA)); |
| |
| // Tack on wildcard needed by FindFirstFile. |
| sprintf(dh->path, "%s\\*", dir); |
| |
| dh->handle = FindFirstFile(dh->path, dh->find_data); |
| if (dh->handle == INVALID_HANDLE_VALUE) { |
| CFCUtil_die("Can't open dir '%s'", dh->path); |
| } |
| dh->first_time = true; |
| |
| return dh; |
| } |
| |
| const char* |
| CFCUtil_dirnext(void *dirhandle) { |
| WinDH *dh = (WinDH*)dirhandle; |
| if (dh->first_time) { |
| dh->first_time = false; |
| } |
| else { |
| if ((FindNextFile(dh->handle, dh->find_data) == 0)) { |
| if (GetLastError() != ERROR_NO_MORE_FILES) { |
| CFCUtil_die("Error occurred while reading '%s'", |
| dh->path); |
| } |
| return NULL; |
| } |
| } |
| return dh->find_data->cFileName; |
| } |
| |
| void |
| CFCUtil_closedir(void *dirhandle, const char *dir) { |
| WinDH *dh = (WinDH*)dirhandle; |
| if (!FindClose(dh->handle)) { |
| CFCUtil_die("Error occurred while closing dir '%s'", dir); |
| } |
| FREEMEM(dh->find_data); |
| FREEMEM(dh); |
| } |
| |
| /******************************** UNIXEN ***********************************/ |
| #else |
| |
| #include <dirent.h> |
| |
| int |
| CFCUtil_make_dir(const char *dir) { |
| return !mkdir(dir, 0777); |
| } |
| |
| |
| void* |
| CFCUtil_opendir(const char *dir) { |
| DIR *dirhandle = opendir(dir); |
| if (!dirhandle) { |
| CFCUtil_die("Failed to opendir for '%s': %s", dir, strerror(errno)); |
| } |
| return dirhandle; |
| } |
| |
| const char* |
| CFCUtil_dirnext(void *dirhandle) { |
| struct dirent *entry = readdir((DIR*)dirhandle); |
| return entry ? entry->d_name : NULL; |
| } |
| |
| void |
| CFCUtil_closedir(void *dirhandle, const char *dir) { |
| if (closedir((DIR*)dirhandle) == -1) { |
| CFCUtil_die("Error closing dir '%s': %s", dir, strerror(errno)); |
| } |
| } |
| |
| #endif /* Windows vs. Unix. */ |
| |
| /***************************************************************************/ |
| |
| #ifdef CFCPERL |
| |
| #include "EXTERN.h" |
| #include "perl.h" |
| #include "XSUB.h" |
| #include "ppport.h" |
| |
| void |
| CFCUtil_die(const char* format, ...) { |
| SV *errsv = get_sv("@", 1); |
| va_list args; |
| va_start(args, format); |
| sv_vsetpvf_mg(errsv, format, &args); |
| va_end(args); |
| croak(NULL); |
| } |
| |
| void |
| CFCUtil_warn(const char* format, ...) { |
| SV *mess = newSVpv("", 0); |
| va_list args; |
| va_start(args, format); |
| sv_vsetpvf(mess, format, &args); |
| va_end(args); |
| fprintf(stderr, "%s\n", SvPV_nolen(mess)); |
| SvREFCNT_dec(mess); |
| } |
| |
| #else |
| |
| void |
| CFCUtil_die(const char* format, ...) { |
| va_list args; |
| va_start(args, format); |
| vfprintf(stderr, format, args); |
| va_end(args); |
| fprintf(stderr, "\n"); |
| exit(1); |
| } |
| |
| void |
| CFCUtil_warn(const char* format, ...) { |
| va_list args; |
| va_start(args, format); |
| vfprintf(stderr, format, args); |
| va_end(args); |
| fprintf(stderr, "\n"); |
| } |
| |
| #endif /* CFCPERL */ |
| |