From now on, libmenu does not steal all input keys from input modules.
[mplayer/greg.git] / libmenu / menu_filesel.c
blob93cfa6c806771e5e0ddca47d997ad29f9d774a98
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <dirent.h>
5 #include <errno.h>
6 #include <string.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <ctype.h>
11 #include <unistd.h>
12 #include <limits.h>
15 #include "config.h"
16 #include "mp_msg.h"
17 #include "help_mp.h"
19 #include "m_struct.h"
20 #include "m_option.h"
22 #include "libmpcodecs/img_format.h"
23 #include "libmpcodecs/mp_image.h"
25 #include "menu.h"
26 #include "menu_list.h"
27 #include "input/input.h"
28 #include "osdep/keycodes.h"
30 #define MENU_KEEP_PATH "/tmp/mp_current_path"
32 int menu_keepdir = 0;
33 char *menu_chroot = NULL;
35 struct list_entry_s {
36 struct list_entry p;
37 int d;
40 struct menu_priv_s {
41 menu_list_priv_t p;
42 char* dir; // current dir
43 /// Cfg fields
44 char* path;
45 char* title;
46 char* file_action;
47 char* dir_action;
48 int auto_close;
49 char** actions;
50 char* filter;
53 static struct menu_priv_s cfg_dflt = {
54 MENU_LIST_PRIV_DFLT,
55 NULL,
57 NULL,
58 "Select a file: %p",
59 "loadfile '%p'",
60 NULL,
62 NULL,
63 NULL
66 #define ST_OFF(m) M_ST_OFF(struct menu_priv_s,m)
68 static m_option_t cfg_fields[] = {
69 MENU_LIST_PRIV_FIELDS,
70 { "path", ST_OFF(path), CONF_TYPE_STRING, 0, 0, 0, NULL },
71 { "title", ST_OFF(title), CONF_TYPE_STRING, 0, 0, 0, NULL },
72 { "file-action", ST_OFF(file_action), CONF_TYPE_STRING, 0, 0, 0, NULL },
73 { "dir-action", ST_OFF(dir_action), CONF_TYPE_STRING, 0, 0, 0, NULL },
74 { "auto-close", ST_OFF(auto_close), CONF_TYPE_FLAG, 0, 0, 1, NULL },
75 { "actions", ST_OFF(actions), CONF_TYPE_STRING_LIST, 0, 0, 0, NULL},
76 { "filter", ST_OFF(filter), CONF_TYPE_STRING, 0, 0, 0, NULL},
77 { NULL, NULL, NULL, 0,0,0,NULL }
80 #define mpriv (menu->priv)
82 static void free_entry(list_entry_t* entry) {
83 free(entry->p.txt);
84 free(entry);
87 static char* replace_path(char* title , char* dir) {
88 char *p = strstr(title,"%p");
89 if(p) {
90 int tl = strlen(title);
91 int dl = strlen(dir);
92 int t1l = p-title;
93 int l = tl - 2 + dl;
94 char *r, *n, *d = dir;
95 char term = *(p-1);
97 do {
98 if (*d == '\\' || *d == term)
99 l++;
100 } while (*d++);
101 r = malloc(l + 1);
102 n = r + t1l;
103 memcpy(r,title,t1l);
104 do {
105 if (*dir == '\\' || *dir == term)
106 *n++ = '\\';
107 } while ((*n++ = *dir++));
108 if(tl - t1l - 2 > 0)
109 strcpy(n-1,p+2);
110 return r;
111 } else
112 return title;
115 typedef int (*kill_warn)(const void*, const void*);
117 static int mylstat(char *dir, char *file,struct stat* st) {
118 int l = strlen(dir) + strlen(file);
119 char s[l+2];
120 if (!strcmp("..", file)) {
121 char *slash;
122 l -= 3;
123 strcpy(s, dir);
124 #if defined(__MINGW32__) || defined(__CYGWIN__)
125 if (s[l] == '/' || s[l] == '\\')
126 #else
127 if (s[l] == '/')
128 #endif
129 s[l] = '\0';
130 slash = strrchr(s, '/');
131 #if defined(__MINGW32__) || defined(__CYGWIN__)
132 if (!slash)
133 slash = strrchr(s,'\\');
134 #endif
135 if (!slash)
136 return stat(dir,st);
137 slash[1] = '\0';
138 return stat(s,st);
140 sprintf(s,"%s/%s",dir,file);
141 return stat(s,st);
144 static int compare(char **a, char **b){
145 if((*a)[strlen(*a) - 1] == '/') {
146 if((*b)[strlen(*b) - 1] == '/')
147 return strcmp(*b, *a) ;
148 else
149 return 1;
150 } else {
151 if((*b)[strlen(*b) - 1] == '/')
152 return -1;
153 else
154 return strcmp(*b, *a);
158 static char **get_extensions(menu_t *menu){
159 char **extensions, ext[32];
160 FILE *fp;
161 int n = 1;
163 if (!mpriv->filter)
164 return NULL;
166 fp = fopen(mpriv->filter, "r");
167 if(!fp)
168 return NULL;
170 extensions = (char **) malloc(sizeof(*extensions));
171 *extensions = NULL;
173 while(fgets(ext,sizeof(ext),fp)) {
174 char **l, *e;
175 int s = strlen (ext);
177 if(ext[s-1] == '\n') {
178 ext[s-1] = '\0';
179 s--;
181 e = (char *) malloc(s+1);
182 extensions = (char **) realloc(extensions, ++n * sizeof(*extensions));
183 extensions = (char **) realloc(extensions, ++n * sizeof(*extensions));
184 strcpy (e, ext);
185 for (l=extensions; *l; l++);
186 *l++ = e;
187 *l = NULL;
190 fclose (fp);
191 return extensions;
194 static void free_extensions(char **extensions){
195 if (extensions) {
196 char **l = extensions;
197 while (*l)
198 free (*l++);
199 free (extensions);
203 static int open_dir(menu_t* menu,char* args) {
204 char **namelist, **tp;
205 struct dirent *dp;
206 struct stat st;
207 int n;
208 int path_fp;
209 char* p = NULL;
210 list_entry_t* e;
211 DIR* dirp;
212 extern int file_filter;
213 char **extensions, **elem, *ext;
215 menu_list_init(menu);
217 if(mpriv->dir)
218 free(mpriv->dir);
219 mpriv->dir = strdup(args);
220 if(mpriv->p.title && mpriv->p.title != mpriv->title && mpriv->p.title != cfg_dflt.p.title)
221 free(mpriv->p.title);
222 p = strstr(mpriv->title,"%p");
224 mpriv->p.title = replace_path(mpriv->title,mpriv->dir);
226 if ((dirp = opendir (mpriv->dir)) == NULL){
227 mp_msg(MSGT_GLOBAL,MSGL_ERR,MSGTR_LIBMENU_OpendirError, strerror(errno));
228 return 0;
231 if (menu_keepdir) {
232 path_fp = open (MENU_KEEP_PATH, O_CREAT | O_WRONLY | O_TRUNC, 0666);
233 if (path_fp >= 0) {
234 write (path_fp, mpriv->dir, strlen (mpriv->dir));
235 close (path_fp);
239 namelist = (char **) malloc(sizeof(char *));
240 extensions = get_extensions(menu);
242 n=0;
243 while ((dp = readdir(dirp)) != NULL) {
244 if(dp->d_name[0] == '.' && strcmp(dp->d_name,"..") != 0)
245 continue;
246 if (menu_chroot && !strcmp (dp->d_name,"..")) {
247 int len = strlen (menu_chroot);
248 if ((strlen (mpriv->dir) == len || strlen (mpriv->dir) == len + 1)
249 && !strncmp (mpriv->dir, menu_chroot, len))
250 continue;
252 if (mylstat(args,dp->d_name,&st))
253 continue;
254 if (file_filter && extensions && !S_ISDIR(st.st_mode)) {
255 if((ext = strrchr(dp->d_name,'.')) == NULL)
256 continue;
257 ext++;
258 elem = extensions;
259 do {
260 if (!strcasecmp(ext, *elem))
261 break;
262 } while (*++elem);
263 if (*elem == NULL)
264 continue;
266 if(n%20 == 0){ // Get some more mem
267 if((tp = (char **) realloc(namelist, (n+20) * sizeof (char *)))
268 == NULL) {
269 mp_msg(MSGT_GLOBAL,MSGL_ERR,MSGTR_LIBMENU_ReallocError, strerror(errno));
270 n--;
271 goto bailout;
273 namelist=tp;
276 namelist[n] = (char *) malloc(strlen(dp->d_name) + 2);
277 if(namelist[n] == NULL){
278 mp_msg(MSGT_GLOBAL,MSGL_ERR,MSGTR_LIBMENU_MallocError, strerror(errno));
279 n--;
280 goto bailout;
283 strcpy(namelist[n], dp->d_name);
284 if(S_ISDIR(st.st_mode))
285 strcat(namelist[n], "/");
286 n++;
289 bailout:
290 free_extensions (extensions);
291 closedir(dirp);
293 qsort(namelist, n, sizeof(char *), (kill_warn)compare);
295 if (n < 0) {
296 mp_msg(MSGT_GLOBAL,MSGL_ERR,MSGTR_LIBMENU_ReaddirError,strerror(errno));
297 return 0;
299 while(n--) {
300 if((e = calloc(1,sizeof(list_entry_t))) != NULL){
301 e->p.next = NULL;
302 e->p.txt = strdup(namelist[n]);
303 if(strchr(namelist[n], '/') != NULL)
304 e->d = 1;
305 menu_list_add_entry(menu,e);
306 }else{
307 mp_msg(MSGT_GLOBAL,MSGL_ERR,MSGTR_LIBMENU_MallocError, strerror(errno));
309 free(namelist[n]);
311 free(namelist);
313 return 1;
316 static char *action;
318 static void read_cmd(menu_t* menu,int cmd) {
319 switch(cmd) {
320 case MENU_CMD_LEFT:
321 mpriv->p.current = mpriv->p.menu; // Hack : we consider that the first entry is ../
322 case MENU_CMD_RIGHT:
323 case MENU_CMD_OK: {
324 // Directory
325 if(mpriv->p.current->d && !mpriv->dir_action) {
326 // Default action : open this dirctory ourself
327 int l = strlen(mpriv->dir);
328 char *slash = NULL, *p = NULL;
329 if(strcmp(mpriv->p.current->p.txt,"../") == 0) {
330 if(l <= 1) break;
331 mpriv->dir[l-1] = '\0';
332 slash = strrchr(mpriv->dir,'/');
333 #if defined(__MINGW32__) || defined(__CYGWIN__)
334 if (!slash)
335 slash = strrchr(mpriv->dir,'\\');
336 #endif
337 if(!slash) break;
338 slash[1] = '\0';
339 p = strdup(mpriv->dir);
340 } else {
341 p = malloc(l + strlen(mpriv->p.current->p.txt) + 1);
342 sprintf(p,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
344 menu_list_uninit(menu,free_entry);
345 if(!open_dir(menu,p)) {
346 mp_msg(MSGT_GLOBAL,MSGL_ERR,MSGTR_LIBMENU_CantOpenDirectory,p);
347 menu->cl = 1;
349 free(p);
350 } else { // File and directory dealt with action string.
351 int fname_len = strlen(mpriv->dir) + strlen(mpriv->p.current->p.txt) + 1;
352 char filename[fname_len];
353 char *str;
354 char *action = mpriv->p.current->d ? mpriv->dir_action:mpriv->file_action;
355 sprintf(filename,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
356 str = replace_path(action, filename);
357 if (mp_input_parse_and_queue_cmds(str)) {
358 if(mpriv->auto_close)
359 menu->cl = 1;
361 if (str != action)
362 free(str);
364 } break;
365 case MENU_CMD_ACTION: {
366 int fname_len = strlen(mpriv->dir) + strlen(mpriv->p.current->p.txt) + 1;
367 char filename[fname_len];
368 char *str;
369 sprintf(filename,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
370 str = replace_path(action, filename);
371 mp_input_parse_and_queue_cmds(str);
372 if(str != action)
373 free(str);
374 } break;
375 default:
376 menu_list_read_cmd(menu,cmd);
380 static int read_key(menu_t* menu,int c){
381 char **str;
382 for (str=mpriv->actions; str && *str; str++)
383 if (c == (*str)[0]) {
384 action = &(*str)[2];
385 read_cmd(menu,MENU_CMD_ACTION);
386 return 1;
388 if (menu_dflt_read_key(menu, c))
389 return 1;
390 return menu_list_jump_to_key(menu, c);
393 static void clos(menu_t* menu) {
394 menu_list_uninit(menu,free_entry);
395 free(mpriv->dir);
398 static int open_fs(menu_t* menu, char* args) {
399 char *path = mpriv->path, *freepath = NULL;
400 int r = 0;
401 char wd[PATH_MAX+1], b[PATH_MAX+1];
402 args = NULL; // Warning kill
404 menu->draw = menu_list_draw;
405 menu->read_cmd = read_cmd;
406 menu->read_key = read_key;
407 menu->close = clos;
409 if (menu_keepdir) {
410 if (!path || path[0] == '\0') {
411 struct stat st;
412 int path_fp;
414 path_fp = open (MENU_KEEP_PATH, O_RDONLY);
415 if (path_fp >= 0) {
416 if (!fstat (path_fp, &st) && (st.st_size > 0)) {
417 path = malloc(st.st_size+1);
418 if ((read(path_fp, path, st.st_size) == st.st_size) && path[0] == '/'
419 && !stat(path, &st) && S_ISDIR(st.st_mode)){
420 freepath = path;
421 path[st.st_size] = '\0';
423 else {
424 free(path);
425 path = NULL;
428 close (path_fp);
433 getcwd(wd,PATH_MAX);
434 if (!path || path[0] == '\0')
435 path = wd;
436 if (path[0] != '/') {
437 if(path[strlen(path)-1] != '/')
438 snprintf(b,sizeof(b),"%s/%s/",wd,path);
439 else
440 snprintf(b,sizeof(b),"%s/%s",wd,path);
441 path = b;
442 } else if (path[strlen(path)-1]!='/') {
443 sprintf(b,"%s/",path);
444 path = b;
446 if (menu_chroot && menu_chroot[0] == '/') {
447 int l = strlen(menu_chroot);
448 if (l > 0 && menu_chroot[l-1] == '/')
449 --l;
450 if (strncmp(menu_chroot, path, l) || (path[l] != '\0' && path[l] != '/')) {
451 if (menu_chroot[l] == '/')
452 path = menu_chroot;
453 else {
454 sprintf(b,"%s/",menu_chroot);
455 path = b;
459 r = open_dir(menu,path);
461 if (freepath)
462 free(freepath);
464 return r;
467 const menu_info_t menu_info_filesel = {
468 "File seletor menu",
469 "filesel",
470 "Albeu",
473 "fs_cfg",
474 sizeof(struct menu_priv_s),
475 &cfg_dflt,
476 cfg_fields
478 open_fs