implement basic autoplay
[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 <ucx/string.h>
34
35
36 char* util_concat_path(const char *url_base, const char *p) {
37     sstr_t base = sstr((char*)url_base);
38     sstr_t path;
39     if(p) {
40         path = sstr((char*)p);
41     } else {
42         path = sstrn("", 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     sstr_t url;
57     if(add_separator) {
58         url = sstrcat(3, base, sstr("/"), path);
59     } else {
60         url = sstrcat(2, base, path);
61     }
62     
63     return url.ptr;
64 }
65
66 char* util_resource_name(char *url) {
67     sstr_t urlstr = sstr(url);
68     if(urlstr.ptr[urlstr.length-1] == '/') {
69         urlstr.length--;
70     }
71     sstr_t resname = sstrrchr(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     char *name = util_resource_name((char*)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 char* util_find_next_file(char *current_file) {
95     char *current_folder = util_parent_path(current_file);
96     char *current_file_name = util_resource_name(current_file);
97     DIR *dir = opendir(current_folder);
98     if(!dir) {
99         fprintf(stderr, "Error: Cannot open directory '%s': %s\n", current_folder, strerror(errno));
100         free(current_folder);
101         return NULL;
102     }
103     
104     size_t nfiles = 0;
105     size_t falloc = 64;
106     char **files = calloc(falloc, sizeof(char*));
107     
108     int abort = 0;
109     struct dirent *ent;
110     while((ent = readdir(dir))) {
111         if(ent->d_name[0] == '.') {
112             // skip '.', '..' and dot-files
113             continue;
114         }
115         
116         // stat
117         char *abs_path = util_concat_path(current_folder, ent->d_name);
118         struct stat s;
119         int r = stat(abs_path, &s);
120         free(abs_path);
121         if(r) {
122             continue;
123         }
124         
125         // we only want regular files
126         if(!S_ISREG(s.st_mode)) {
127             continue;
128         }
129         
130         // add file
131         if(nfiles >= falloc) {
132             falloc *= 2;
133             if(falloc > FIND_NEXT_MAX_FILES) {
134                 abort = 1;
135                 break;
136             }
137             files = realloc(files, falloc * sizeof(char*));
138         }
139         files[nfiles] = strdup(ent->d_name);
140         
141         nfiles++;
142     }
143     
144     char *result = NULL;
145     if(!abort) {
146         qsort(files, nfiles, sizeof(char*), (cmpfnc)strcmp);
147         // search array for current file and return the successor
148         for(int i=0;i<nfiles;i++) {
149             if(!strcmp(files[i], current_file_name)) {
150                 if(i + 1 < nfiles) {
151                     result = util_concat_path(current_folder, files[i+1]);
152                     break;
153                 }
154             }
155         }
156     }
157     
158     closedir(dir);
159     for(int i=0;i<nfiles;i++) {
160         free(files[i]);
161     }
162     free(files);
163     free(current_folder);
164     
165     return result;
166 }