1 /*
2  *                           0BSD
3  *
4  *                    BSD Zero Clause License
5  *
6  *  Copyright (c) 2019 Hermann Meyer
7  *
8  * Permission to use, copy, modify, and/or distribute this software for any
9  * purpose with or without fee is hereby granted.
10 
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13  * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  *
19  */
20 
21 #include "xfilepicker.h"
22 
23 
fp_compare_fun(const void * p1,const void * p2)24 static inline int fp_compare_fun (const void *p1, const void *p2) {
25     return strcasecmp(*(const char**) p1, *(const char**) p2);
26 }
27 
fp_compare_hidden_dirs_fun(const void * p1,const void * p2)28 static inline int fp_compare_hidden_dirs_fun (const void *p1, const void *p2) {
29     if(strstr(*(const char**)p1, PATH_SEPARATOR".") && strstr(*(const char**)p2, PATH_SEPARATOR".")) return 0;
30     if(strstr(*(const char**)p1, PATH_SEPARATOR".")) return 1;
31     if(strstr(*(const char**)p2, PATH_SEPARATOR".")) return -1;
32     return strcasecmp(*(const char**) p1, *(const char**) p2);
33 }
34 
fp_compare_hidden_files_fun(const void * p1,const void * p2)35 static inline int fp_compare_hidden_files_fun (const void *p1, const void *p2) {
36     if(strncmp(*(const char**)p1, ".",1)==0 && strncmp(*(const char**)p2, ".",1)==0) return 0;
37     if(strncmp(*(const char**)p1, ".",1)==0 ) return 1;
38     if(strncmp(*(const char**)p2, ".",1)==0 ) return -1;
39     return strcasecmp(*(const char**) p1, *(const char**) p2);
40 }
41 
fp_show_hidden_files(FilePicker * filepicker,char * file)42 static inline bool fp_show_hidden_files(FilePicker *filepicker, char* file) {
43     return (filepicker->show_hidden) ? strcmp(file,".")!=0 : (file[0] != '.');
44 }
45 
fp_show_filter_files(FilePicker * filepicker,char * file)46 static inline bool fp_show_filter_files(FilePicker *filepicker, char* file) {
47     if (!filepicker->use_filter) {
48         return true;
49     } else {
50         if(strstr(filepicker->filter,"."))
51             return strstr(file, filepicker->filter);
52 #ifdef __XDG_MIME_H__
53         return strstr(xdg_mime_get_mime_type_from_file_name(file), filepicker->filter);
54 #else
55         return true;
56 #endif
57     }
58 }
59 
fp_sort_buffers(FilePicker * filepicker,int get_dirs)60 static void fp_sort_buffers(FilePicker *filepicker, int get_dirs) {
61     if (filepicker->dir_counter>1 && get_dirs) {
62         qsort (filepicker->dir_names, filepicker->dir_counter,
63           sizeof filepicker->dir_names[0], (filepicker->show_hidden) ?
64           fp_compare_hidden_dirs_fun : fp_compare_fun);
65     }
66     if (filepicker->file_counter>1) {
67         qsort (filepicker->file_names, filepicker->file_counter,
68           sizeof filepicker->file_names[0], (filepicker->show_hidden) ?
69           fp_compare_hidden_files_fun : fp_compare_fun);
70     }
71 }
72 
fp_clear_filebuffer(FilePicker * filepicker)73 static void fp_clear_filebuffer(FilePicker *filepicker) {
74     unsigned int j = 0;
75     for(; j<filepicker->file_counter;j++) {
76         free(filepicker->file_names[j]);
77         filepicker->file_names[j] = NULL;
78     }
79     if(filepicker->file_counter) {
80         free(filepicker->file_names);
81         filepicker->file_names = NULL;
82         filepicker->file_counter=0;
83     }
84 }
85 
fp_clear_dirbuffer(FilePicker * filepicker)86 static void fp_clear_dirbuffer(FilePicker *filepicker) {
87     unsigned int j = 0;
88     for(; j<filepicker->dir_counter;j++) {
89         free(filepicker->dir_names[j]);
90         filepicker->dir_names[j] = NULL;
91     }
92     if(filepicker->dir_counter) {
93         free(filepicker->dir_names);
94         filepicker->dir_names = NULL;
95         filepicker->dir_counter=0;
96     }
97 }
98 
fp_prefill_dirbuffer(FilePicker * filepicker,char * path)99 static inline int fp_prefill_dirbuffer(FilePicker *filepicker, char *path) {
100     int ret = 0;
101     if (strcmp (path, PATH_SEPARATOR) == 0) {
102         filepicker->dir_names = (char **)realloc(filepicker->dir_names,
103           (filepicker->dir_counter + 1) * sizeof(char *));
104         assert(filepicker->dir_names != NULL);
105         asprintf(&filepicker->dir_names[filepicker->dir_counter++], "%s",path);
106         assert(&filepicker->dir_names[filepicker->dir_counter-1] != NULL);
107     } else {
108         char *ho;
109         asprintf(&ho, "%s",path);
110         assert(ho != NULL);
111         while (strcmp (ho, PATH_SEPARATOR) != 0) {
112             filepicker->dir_names = (char **)realloc(filepicker->dir_names,
113               (filepicker->dir_counter + 1) * sizeof(char *));
114             assert(filepicker->dir_names != NULL);
115             asprintf(&filepicker->dir_names[filepicker->dir_counter++], "%s",dirname(ho));
116             assert(&filepicker->dir_names[filepicker->dir_counter-1] != NULL);
117             ret++;
118         }
119         if (strcmp (path, PATH_SEPARATOR) != 0) {
120             filepicker->dir_names = (char **)realloc(filepicker->dir_names,
121               (filepicker->dir_counter + 1) * sizeof(char *));
122             assert(filepicker->dir_names != NULL);
123             asprintf(&filepicker->dir_names[filepicker->dir_counter++], "%s",path);
124             assert(&filepicker->dir_names[filepicker->dir_counter-1] != NULL);
125         }
126         free(ho);
127     }
128     return ret;
129 }
130 
fp_check_link(char * path,struct dirent * dp)131 int fp_check_link(char *path, struct dirent *dp) {
132     if(dp -> d_type == DT_LNK) {
133         char s[256];
134         snprintf(s, 256, (strcmp(path, PATH_SEPARATOR) != 0) ?
135               "%s" PATH_SEPARATOR "%s" : "%s%s" , path,dp->d_name);
136         struct stat sb;
137         if (stat(s, &sb) == 0 && S_ISDIR(sb.st_mode)) {
138             return 1;
139         }
140     }
141     return 0;
142 }
143 
fp_check_dir(char * path,struct dirent * dp)144 int fp_check_dir(char *path, struct dirent *dp) {
145     if (dp->d_type == DT_UNKNOWN) {
146         char s[256];
147         snprintf(s, 256, (strcmp(path, PATH_SEPARATOR) != 0) ?
148               "%s" PATH_SEPARATOR "%s" : "%s%s" , path,dp->d_name);
149         struct stat sb;
150         if (stat(s, &sb) == 0 && S_ISDIR(sb.st_mode)) {
151             return 1;
152         } else {
153             return 2;
154         }
155     }
156     return 0;
157 }
158 
fp_get_files(FilePicker * filepicker,char * path,int get_dirs,int get_files)159 int fp_get_files(FilePicker *filepicker, char *path, int get_dirs, int get_files) {
160     int ret = 0;
161     fp_clear_filebuffer(filepicker);
162 
163     DIR *dirp;
164     struct dirent *dp;
165     if((dirp = opendir(path)) == NULL) {
166         path =(char*)PATH_SEPARATOR;
167         dirp = opendir(PATH_SEPARATOR);
168         assert(dirp);
169     }
170 
171     if(get_dirs) {
172         fp_clear_dirbuffer(filepicker);
173         ret = fp_prefill_dirbuffer(filepicker, path);
174     }
175 
176     while ((dp = readdir(dirp)) != NULL) {
177 
178         if((get_files && (dp->d_type != DT_DIR || (fp_check_dir(path, dp) == 2)) && strlen(dp->d_name)!=0
179           && strcmp(dp->d_name,"..")!=0 && fp_show_hidden_files(filepicker, dp->d_name)
180           && fp_show_filter_files(filepicker, dp->d_name) && !fp_check_link(path, dp)) ) {
181 
182             filepicker->file_names = (char **)realloc(filepicker->file_names,
183               (filepicker->file_counter + 1) * sizeof(char *));
184             assert(filepicker->file_names != NULL);
185             asprintf(&filepicker->file_names[filepicker->file_counter++],"%s",dp->d_name);
186             assert(&filepicker->file_names[filepicker->file_counter-1] != NULL);
187 
188         } else if(get_dirs && (dp -> d_type == DT_DIR || dp -> d_type == DT_LNK || (fp_check_dir(path, dp) == 1))
189           && strlen(dp->d_name)!=0 && strcmp(dp->d_name,"..")!=0 && fp_show_hidden_files(filepicker, dp->d_name)) {
190 
191             if (dp -> d_type == DT_LNK) {
192                 if (!fp_check_link(path, dp)) continue;
193             }
194             filepicker->file_names = (char **)realloc(filepicker->file_names,
195               (filepicker->file_counter + 1) * sizeof(char *));
196             assert(filepicker->file_names != NULL);
197             asprintf(&filepicker->file_names[filepicker->file_counter++], (strcmp(path, PATH_SEPARATOR) != 0) ?
198               "%s" PATH_SEPARATOR "%s" : "%s%s" , path,dp->d_name);
199             assert(&filepicker->dir_names[filepicker->file_counter-1] != NULL);
200         }
201     }
202     closedir(dirp);
203     fp_sort_buffers(filepicker, get_dirs);
204     return ret;
205 }
206 
fp_free(FilePicker * filepicker)207 void fp_free(FilePicker *filepicker) {
208     fp_clear_filebuffer(filepicker);
209     fp_clear_dirbuffer(filepicker);
210     free(filepicker->selected_file);
211     free(filepicker->path);
212     free(filepicker->filter);
213 }
214 
fp_init(FilePicker * filepicker,const char * path)215 void fp_init(FilePicker *filepicker, const char *path) {
216     filepicker->file_counter=0;
217     filepicker->dir_counter=0;
218     filepicker->use_filter = 0;
219     filepicker->show_hidden = false;
220     filepicker->file_names = NULL;
221     filepicker->dir_names = NULL;
222     filepicker->selected_file = NULL;
223     filepicker->path = NULL;
224     filepicker->filter = NULL;
225     asprintf(&filepicker->path, path);
226     assert(filepicker->path != NULL);
227 }
228