a383a2e320b2d98d8aeee601c31d73166ba4ae3a
[uwplayer.git] / application / utils.c
1 /*
2  * Copyright 2022 Olaf Wintermann
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a 
5  * copy of this software and associated documentation files (the "Software"), 
6  * to deal in the Software without restriction, including without limitation 
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
8  * and/or sell copies of the Software, and to permit persons to whom the 
9  * Software is furnished to do so, subject to the following conditions:
10  * 
11  * The above copyright notice and this permission notice shall be included in 
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
17  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
20  * DEALINGS IN THE SOFTWARE.
21  */
22
23 #include "utils.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
30 #include <dirent.h>
31 #include <errno.h>
32
33 #include <cx/string.h>
34
35
36 char* util_concat_path(const char *url_base, const char *p) {
37     cxstring base = cx_str(url_base);
38     cxstring path;
39     if(p) {
40         path = cx_str(p);
41     } else {
42         path = cx_strn("", 0);
43     }
44     
45     int add_separator = 0;
46     if(base.length != 0 && base.ptr[base.length-1] == '/') {
47         if(path.ptr[0] == '/') {
48             base.length--;
49         }
50     } else {
51         if(path.length == 0 || path.ptr[0] != '/') {
52             add_separator = 1;
53         }
54     }
55     
56     cxmutstr url;
57     if(add_separator) {
58         url = cx_strcat(3, base, cx_strn("/", 1), path);
59     } else {
60         url = cx_strcat(2, base, path);
61     }
62     
63     return url.ptr;
64 }
65
66 const char* util_resource_name(const char *url) {
67     cxstring urlstr = cx_str(url);
68     if(urlstr.ptr[urlstr.length-1] == '/') {
69         urlstr.length--;
70     }
71     cxstring resname = cx_strrchr(urlstr, '/');
72     if(resname.length > 1) {
73         return resname.ptr+1;
74     } else {
75         return url;
76     }
77 }
78
79 char* util_parent_path(const char *path) {
80     const char *name = util_resource_name(path);
81     size_t namelen = strlen(name);
82     size_t pathlen = strlen(path);
83     size_t parentlen = pathlen - namelen;
84     char *parent = malloc(parentlen + 1);
85     memcpy(parent, path, parentlen);
86     parent[parentlen] = '\0';
87     return parent;
88 }
89
90 #define FIND_NEXT_MAX_FILES 2000
91
92 typedef int (*cmpfnc)(const void *, const void *);
93
94 int fcmp(const void *d1, const void *d2) {
95     const char **f1 = (const char **)d1;
96     const char **f2 = (const char **)d2;
97     int r = strcmp(*f1, *f2);
98     return r;
99 }
100
101 char* util_find_next_file(char *current_file) {
102     char *current_folder = util_parent_path(current_file);
103     const char *current_file_name = util_resource_name(current_file);
104     DIR *dir = opendir(current_folder);
105     if(!dir) {
106         fprintf(stderr, "Error: Cannot open directory '%s': %s\n", current_folder, strerror(errno));
107         free(current_folder);
108         return NULL;
109     }
110     
111     size_t nfiles = 0;
112     size_t falloc = 64;
113     char **files = calloc(falloc, sizeof(char*));
114     
115     int abort = 0;
116     struct dirent *ent;
117     while((ent = readdir(dir))) {
118         if(ent->d_name[0] == '.') {
119             // skip '.', '..' and dot-files
120             continue;
121         }
122         
123         // stat
124         char *abs_path = util_concat_path(current_folder, ent->d_name);
125         struct stat s;
126         int r = stat(abs_path, &s);
127         free(abs_path);
128         if(r) {
129             continue;
130         }
131         
132         // we only want regular files
133         if(!S_ISREG(s.st_mode)) {
134             continue;
135         }
136         
137         // add file
138         if(nfiles >= falloc) {
139             falloc *= 2;
140             if(falloc > FIND_NEXT_MAX_FILES) {
141                 abort = 1;
142                 break;
143             }
144             files = realloc(files, falloc * sizeof(char*));
145         }
146         files[nfiles] = strdup(ent->d_name);
147         
148         nfiles++;
149     }
150     
151     char *result = NULL;
152     if(!abort) {
153         qsort(files, nfiles, sizeof(char*), fcmp);
154         // search array for current file and return the successor
155         for(int i=0;i<nfiles;i++) {
156             char *c = files[i];
157             if(!strcmp(files[i], current_file_name)) {
158                 if(i + 1 < nfiles) {
159                     result = util_concat_path(current_folder, files[i+1]);
160                     break;
161                 }
162             }
163         }
164     }
165     
166     closedir(dir);
167     for(int i=0;i<nfiles;i++) {
168         free(files[i]);
169     }
170     free(files);
171     free(current_folder);
172     
173     return result;
174 }