/* * Copyright 2022 Olaf Wintermann * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "utils.h" #include #include #include #include #include #include #include #include char* util_concat_path(const char *url_base, const char *p) { cxstring base = cx_str(url_base); cxstring path; if(p) { path = cx_str(p); } else { path = cx_strn("", 0); } int add_separator = 0; if(base.length != 0 && base.ptr[base.length-1] == '/') { if(path.ptr[0] == '/') { base.length--; } } else { if(path.length == 0 || path.ptr[0] != '/') { add_separator = 1; } } cxmutstr url; if(add_separator) { url = cx_strcat(3, base, cx_strn("/", 1), path); } else { url = cx_strcat(2, base, path); } return url.ptr; } const char* util_resource_name(const char *url) { cxstring urlstr = cx_str(url); if(urlstr.length == 0) { return url; } if(urlstr.ptr[urlstr.length-1] == '/') { urlstr.length--; } cxstring resname = cx_strrchr(urlstr, '/'); if(resname.length > 1) { return resname.ptr+1; } else { return url; } } char* util_parent_path(const char *path) { const char *name = util_resource_name(path); size_t namelen = strlen(name); size_t pathlen = strlen(path); size_t parentlen = pathlen - namelen; char *parent = malloc(parentlen + 1); memcpy(parent, path, parentlen); parent[parentlen] = '\0'; return parent; } #define FIND_NEXT_MAX_FILES 2000 typedef int (*cmpfnc)(const void *, const void *); int fcmp(const void *d1, const void *d2) { const char **f1 = (const char **)d1; const char **f2 = (const char **)d2; int r = strcmp(*f1, *f2); return r; } char* util_find_next_file(char *current_file) { char *current_folder = util_parent_path(current_file); const char *current_file_name = util_resource_name(current_file); DIR *dir = opendir(current_folder); if(!dir) { fprintf(stderr, "Error: Cannot open directory '%s': %s\n", current_folder, strerror(errno)); free(current_folder); return NULL; } size_t nfiles = 0; size_t falloc = 64; char **files = calloc(falloc, sizeof(char*)); int abort = 0; struct dirent *ent; while((ent = readdir(dir))) { if(ent->d_name[0] == '.') { // skip '.', '..' and dot-files continue; } // stat char *abs_path = util_concat_path(current_folder, ent->d_name); struct stat s; int r = stat(abs_path, &s); free(abs_path); if(r) { continue; } // we only want regular files if(!S_ISREG(s.st_mode)) { continue; } // add file if(nfiles >= falloc) { falloc *= 2; if(falloc > FIND_NEXT_MAX_FILES) { abort = 1; break; } files = realloc(files, falloc * sizeof(char*)); } files[nfiles] = strdup(ent->d_name); nfiles++; } char *result = NULL; if(!abort) { qsort(files, nfiles, sizeof(char*), fcmp); // search array for current file and return the successor for(int i=0;i