add single instance mode
[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.length == 0) {
69         return url;
70     }
71     if(urlstr.ptr[urlstr.length-1] == '/') {
72         urlstr.length--;
73     }
74     cxstring resname = cx_strrchr(urlstr, '/');
75     if(resname.length > 1) {
76         return resname.ptr+1;
77     } else {
78         return url;
79     }
80 }
81
82 char* util_parent_path(const char *path) {
83     const char *name = util_resource_name(path);
84     size_t namelen = strlen(name);
85     size_t pathlen = strlen(path);
86     size_t parentlen = pathlen - namelen;
87     char *parent = malloc(parentlen + 1);
88     memcpy(parent, path, parentlen);
89     parent[parentlen] = '\0';
90     return parent;
91 }
92
93 #define FIND_NEXT_MAX_FILES 2000
94
95 typedef int (*cmpfnc)(const void *, const void *);
96
97 int fcmp(const void *d1, const void *d2) {
98     const char **f1 = (const char **)d1;
99     const char **f2 = (const char **)d2;
100     int r = strcmp(*f1, *f2);
101     return r;
102 }
103
104 char* util_find_next_file(char *current_file) {
105     char *current_folder = util_parent_path(current_file);
106     const char *current_file_name = util_resource_name(current_file);
107     DIR *dir = opendir(current_folder);
108     if(!dir) {
109         fprintf(stderr, "Error: Cannot open directory '%s': %s\n", current_folder, strerror(errno));
110         free(current_folder);
111         return NULL;
112     }
113     
114     size_t nfiles = 0;
115     size_t falloc = 64;
116     char **files = calloc(falloc, sizeof(char*));
117     
118     int abort = 0;
119     struct dirent *ent;
120     while((ent = readdir(dir))) {
121         if(ent->d_name[0] == '.') {
122             // skip '.', '..' and dot-files
123             continue;
124         }
125         
126         // stat
127         char *abs_path = util_concat_path(current_folder, ent->d_name);
128         struct stat s;
129         int r = stat(abs_path, &s);
130         free(abs_path);
131         if(r) {
132             continue;
133         }
134         
135         // we only want regular files
136         if(!S_ISREG(s.st_mode)) {
137             continue;
138         }
139         
140         // add file
141         if(nfiles >= falloc) {
142             falloc *= 2;
143             if(falloc > FIND_NEXT_MAX_FILES) {
144                 abort = 1;
145                 break;
146             }
147             files = realloc(files, falloc * sizeof(char*));
148         }
149         files[nfiles] = strdup(ent->d_name);
150         
151         nfiles++;
152     }
153     
154     char *result = NULL;
155     if(!abort) {
156         qsort(files, nfiles, sizeof(char*), fcmp);
157         // search array for current file and return the successor
158         for(int i=0;i<nfiles;i++) {
159             char *c = files[i];
160             if(!strcmp(files[i], current_file_name)) {
161                 if(i + 1 < nfiles) {
162                     result = util_concat_path(current_folder, files[i+1]);
163                     break;
164                 }
165             }
166         }
167     }
168     
169     closedir(dir);
170     for(int i=0;i<nfiles;i++) {
171         free(files[i]);
172     }
173     free(files);
174     free(current_folder);
175     
176     return result;
177 }