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] != 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] != 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] != NULL);
125         }
126         free(ho);
127     }
128     return ret;
129 }
130 
fp_get_files(FilePicker * filepicker,char * path,int get_dirs)131 int fp_get_files(FilePicker *filepicker, char *path, int get_dirs) {
132     int ret = 0;
133     fp_clear_filebuffer(filepicker);
134 
135     DIR *dirp;
136     struct dirent *dp;
137     if((dirp = opendir(path)) == NULL) {
138         path =(char*)PATH_SEPARATOR;
139         dirp = opendir(PATH_SEPARATOR);
140         assert(dirp);
141     }
142 
143     if(get_dirs) {
144         fp_clear_dirbuffer(filepicker);
145         ret = fp_prefill_dirbuffer(filepicker, path);
146     }
147 
148     while ((dp = readdir(dirp)) != NULL) {
149 
150         if(dp-> d_type != DT_DIR && strlen(dp->d_name)!=0 && dp->d_type != DT_UNKNOWN
151           && strcmp(dp->d_name,"..")!=0 && fp_show_hidden_files(filepicker, dp->d_name) &&
152           fp_show_filter_files(filepicker, dp->d_name)) {
153 
154             filepicker->file_names = (char **)realloc(filepicker->file_names,
155               (filepicker->file_counter + 1) * sizeof(char *));
156             assert(filepicker->file_names != NULL);
157             asprintf(&filepicker->file_names[filepicker->file_counter++],"%s",dp->d_name);
158             assert(&filepicker->file_names[filepicker->file_counter] != NULL);
159 
160         } else if(get_dirs && dp -> d_type == DT_DIR && strlen(dp->d_name)!=0
161           && strcmp(dp->d_name,"..")!=0 && fp_show_hidden_files(filepicker, dp->d_name)) {
162 
163             filepicker->dir_names = (char **)realloc(filepicker->dir_names,
164               (filepicker->dir_counter + 1) * sizeof(char *));
165             assert(filepicker->dir_names != NULL);
166             asprintf(&filepicker->dir_names[filepicker->dir_counter++], (strcmp(path, PATH_SEPARATOR) != 0) ?
167               "%s" PATH_SEPARATOR "%s" : "%s%s" , path,dp->d_name);
168             assert(&filepicker->dir_names[filepicker->dir_counter] != NULL);
169         }
170     }
171     closedir(dirp);
172     fp_sort_buffers(filepicker, get_dirs);
173     return ret;
174 }
175 
fp_free(FilePicker * filepicker)176 void fp_free(FilePicker *filepicker) {
177     fp_clear_filebuffer(filepicker);
178     fp_clear_dirbuffer(filepicker);
179     free(filepicker->selected_file);
180     free(filepicker->path);
181     free(filepicker->filter);
182 }
183 
fp_init(FilePicker * filepicker,const char * path)184 void fp_init(FilePicker *filepicker, const char *path) {
185     filepicker->file_counter=0;
186     filepicker->dir_counter=0;
187     filepicker->use_filter = 0;
188     filepicker->show_hidden = false;
189     filepicker->file_names = NULL;
190     filepicker->dir_names = NULL;
191     filepicker->selected_file = NULL;
192     filepicker->path = NULL;
193     filepicker->filter = NULL;
194     asprintf(&filepicker->path, path);
195     assert(filepicker->path != NULL);
196 }
197