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