Merge svn changes up to r30663
[mplayer/kovensky.git] / libmenu / menu_filesel.c
blob3b239d11d6a21af24e089acb72222df18ac449c2
1 /*
2 * This file is part of MPlayer.
4 * MPlayer is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * MPlayer is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <dirent.h>
22 #include <errno.h>
23 #include <string.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <ctype.h>
28 #include <unistd.h>
29 #include <limits.h>
32 #include "config.h"
33 #include "mp_msg.h"
34 #include "help_mp.h"
36 #include "m_struct.h"
37 #include "m_option.h"
39 #include "libmpcodecs/img_format.h"
40 #include "libmpcodecs/mp_image.h"
42 #include "menu.h"
43 #include "menu_list.h"
44 #include "input/input.h"
45 #include "osdep/keycodes.h"
47 #define MENU_KEEP_PATH "/tmp/mp_current_path"
49 int menu_keepdir = 0;
50 char *menu_chroot = NULL;
52 struct list_entry_s {
53 struct list_entry p;
54 int d;
57 struct menu_priv_s {
58 menu_list_priv_t p;
59 char* dir; // current dir
60 /// Cfg fields
61 char* path;
62 char* title;
63 char* file_action;
64 char* dir_action;
65 char** actions;
66 char* filter;
69 static struct menu_priv_s cfg_dflt = {
70 MENU_LIST_PRIV_DFLT,
71 NULL,
73 NULL,
74 "Select a file: %p",
75 "loadfile '%p'",
76 NULL,
77 NULL,
78 NULL
81 #define ST_OFF(m) M_ST_OFF(struct menu_priv_s,m)
83 static m_option_t cfg_fields[] = {
84 MENU_LIST_PRIV_FIELDS,
85 { "path", ST_OFF(path), CONF_TYPE_STRING, 0, 0, 0, NULL },
86 { "title", ST_OFF(title), CONF_TYPE_STRING, 0, 0, 0, NULL },
87 { "file-action", ST_OFF(file_action), CONF_TYPE_STRING, 0, 0, 0, NULL },
88 { "dir-action", ST_OFF(dir_action), CONF_TYPE_STRING, 0, 0, 0, NULL },
89 { "actions", ST_OFF(actions), CONF_TYPE_STRING_LIST, 0, 0, 0, NULL},
90 { "filter", ST_OFF(filter), CONF_TYPE_STRING, 0, 0, 0, NULL},
91 { NULL, NULL, NULL, 0,0,0,NULL }
94 #define mpriv (menu->priv)
96 static void free_entry(list_entry_t* entry) {
97 free(entry->p.txt);
98 free(entry);
101 static char* replace_path(char* title , char* dir , int escape) {
102 char *p = strstr(title,"%p");
103 if(p) {
104 int tl = strlen(title);
105 int dl = strlen(dir);
106 int t1l = p-title;
107 int l = tl - 2 + dl;
108 char *r, *n, *d = dir;
110 if (escape) {
111 do {
112 if (*d == '\\')
113 l++;
114 else if (*d == '\'') /* ' -> \'\\\'\' */
115 l+=7;
116 } while (*d++);
118 r = malloc(l + 1);
119 n = r + t1l;
120 memcpy(r,title,t1l);
121 do {
122 if (escape) {
123 if (*dir == '\\')
124 *n++ = '\\';
125 else if (*dir == '\'') { /* ' -> \'\\\'\' */
126 *n++ = '\\'; *n++ = '\'';
127 *n++ = '\\'; *n++ = '\\';
128 *n++ = '\\'; *n++ = '\'';
129 *n++ = '\\';
132 } while ((*n++ = *dir++));
133 if(tl - t1l - 2 > 0)
134 strcpy(n-1,p+2);
135 return r;
136 } else
137 return title;
140 typedef int (*kill_warn)(const void*, const void*);
142 static int mylstat(char *dir, char *file,struct stat* st) {
143 int l = strlen(dir) + strlen(file);
144 char s[l+2];
145 if (!strcmp("..", file)) {
146 char *slash;
147 l -= 3;
148 strcpy(s, dir);
149 #if HAVE_DOS_PATHS
150 if (s[l] == '/' || s[l] == '\\')
151 #else
152 if (s[l] == '/')
153 #endif
154 s[l] = '\0';
155 slash = strrchr(s, '/');
156 #if HAVE_DOS_PATHS
157 if (!slash)
158 slash = strrchr(s,'\\');
159 #endif
160 if (!slash)
161 return stat(dir,st);
162 slash[1] = '\0';
163 return stat(s,st);
165 sprintf(s,"%s/%s",dir,file);
166 return stat(s,st);
169 static int compare(char **a, char **b){
170 if((*a)[strlen(*a) - 1] == '/') {
171 if((*b)[strlen(*b) - 1] == '/')
172 return strcmp(*b, *a) ;
173 else
174 return 1;
175 } else {
176 if((*b)[strlen(*b) - 1] == '/')
177 return -1;
178 else
179 return strcmp(*b, *a);
183 static char **get_extensions(menu_t *menu){
184 char **extensions, ext[32];
185 FILE *fp;
186 int n = 1;
188 if (!mpriv->filter)
189 return NULL;
191 fp = fopen(mpriv->filter, "r");
192 if(!fp)
193 return NULL;
195 extensions = (char **) malloc(sizeof(*extensions));
196 *extensions = NULL;
198 while(fgets(ext,sizeof(ext),fp)) {
199 char **l, *e;
200 int s = strlen (ext);
202 if(ext[s-1] == '\n') {
203 ext[s-1] = '\0';
204 s--;
206 e = (char *) malloc(s+1);
207 extensions = (char **) realloc(extensions, ++n * sizeof(*extensions));
208 extensions = (char **) realloc(extensions, ++n * sizeof(*extensions));
209 strcpy (e, ext);
210 for (l=extensions; *l; l++);
211 *l++ = e;
212 *l = NULL;
215 fclose (fp);
216 return extensions;
219 static void free_extensions(char **extensions){
220 if (extensions) {
221 char **l = extensions;
222 while (*l)
223 free (*l++);
224 free (extensions);
228 static int open_dir(menu_t* menu,char* args) {
229 char **namelist, **tp;
230 struct dirent *dp;
231 struct stat st;
232 int n;
233 int path_fp;
234 char* p = NULL;
235 list_entry_t* e;
236 DIR* dirp;
237 extern int file_filter;
238 char **extensions, **elem, *ext;
240 menu_list_init(menu);
242 if(mpriv->dir)
243 free(mpriv->dir);
244 mpriv->dir = strdup(args);
245 if(mpriv->p.title && mpriv->p.title != mpriv->title && mpriv->p.title != cfg_dflt.p.title)
246 free(mpriv->p.title);
247 p = strstr(mpriv->title,"%p");
249 mpriv->p.title = replace_path(mpriv->title,mpriv->dir,0);
251 if ((dirp = opendir (mpriv->dir)) == NULL){
252 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] opendir error: %s\n", strerror(errno));
253 return 0;
256 if (menu_keepdir) {
257 path_fp = open (MENU_KEEP_PATH, O_CREAT | O_WRONLY | O_TRUNC, 0666);
258 if (path_fp >= 0) {
259 write (path_fp, mpriv->dir, strlen (mpriv->dir));
260 close (path_fp);
264 namelist = (char **) malloc(sizeof(char *));
265 extensions = get_extensions(menu);
267 n=0;
268 while ((dp = readdir(dirp)) != NULL) {
269 if(dp->d_name[0] == '.' && strcmp(dp->d_name,"..") != 0)
270 continue;
271 if (menu_chroot && !strcmp (dp->d_name,"..")) {
272 size_t len = strlen (menu_chroot);
273 if ((strlen (mpriv->dir) == len || strlen (mpriv->dir) == len + 1)
274 && !strncmp (mpriv->dir, menu_chroot, len))
275 continue;
277 if (mylstat(args,dp->d_name,&st))
278 continue;
279 if (file_filter && extensions && !S_ISDIR(st.st_mode)) {
280 if((ext = strrchr(dp->d_name,'.')) == NULL)
281 continue;
282 ext++;
283 elem = extensions;
284 do {
285 if (!strcasecmp(ext, *elem))
286 break;
287 } while (*++elem);
288 if (*elem == NULL)
289 continue;
291 if(n%20 == 0){ // Get some more mem
292 if((tp = (char **) realloc(namelist, (n+20) * sizeof (char *)))
293 == NULL) {
294 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] realloc error: %s\n", strerror(errno));
295 n--;
296 goto bailout;
298 namelist=tp;
301 namelist[n] = (char *) malloc(strlen(dp->d_name) + 2);
302 if(namelist[n] == NULL){
303 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] memory allocation error: %s\n", strerror(errno));
304 n--;
305 goto bailout;
308 strcpy(namelist[n], dp->d_name);
309 if(S_ISDIR(st.st_mode))
310 strcat(namelist[n], "/");
311 n++;
314 bailout:
315 free_extensions (extensions);
316 closedir(dirp);
318 qsort(namelist, n, sizeof(char *), (kill_warn)compare);
320 if (n < 0) {
321 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] readdir error: %s\n",strerror(errno));
322 return 0;
324 while(n--) {
325 if((e = calloc(1,sizeof(list_entry_t))) != NULL){
326 e->p.next = NULL;
327 e->p.txt = strdup(namelist[n]);
328 if(strchr(namelist[n], '/') != NULL)
329 e->d = 1;
330 menu_list_add_entry(menu,e);
331 }else{
332 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] memory allocation error: %s\n", strerror(errno));
334 free(namelist[n]);
336 free(namelist);
338 return 1;
341 static char *action;
343 static void read_cmd(menu_t* menu,int cmd) {
344 switch(cmd) {
345 case MENU_CMD_LEFT:
346 mpriv->p.current = mpriv->p.menu; // Hack : we consider that the first entry is ../
347 case MENU_CMD_RIGHT:
348 case MENU_CMD_OK: {
349 // Directory
350 if(mpriv->p.current->d && !mpriv->dir_action) {
351 // Default action : open this dirctory ourself
352 int l = strlen(mpriv->dir);
353 char *slash = NULL, *p = NULL;
354 if(strcmp(mpriv->p.current->p.txt,"../") == 0) {
355 if(l <= 1) break;
356 mpriv->dir[l-1] = '\0';
357 slash = strrchr(mpriv->dir,'/');
358 #if HAVE_DOS_PATHS
359 if (!slash)
360 slash = strrchr(mpriv->dir,'\\');
361 #endif
362 if(!slash) break;
363 slash[1] = '\0';
364 p = strdup(mpriv->dir);
365 } else {
366 p = malloc(l + strlen(mpriv->p.current->p.txt) + 1);
367 sprintf(p,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
369 menu_list_uninit(menu,free_entry);
370 if(!open_dir(menu,p)) {
371 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] Can't open directory %s.\n",p);
372 menu->cl = 1;
374 free(p);
375 } else { // File and directory dealt with action string.
376 int fname_len = strlen(mpriv->dir) + strlen(mpriv->p.current->p.txt) + 1;
377 char filename[fname_len];
378 char *str;
379 char *action = mpriv->p.current->d ? mpriv->dir_action:mpriv->file_action;
380 sprintf(filename,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
381 str = replace_path(action, filename,1);
382 mp_input_parse_and_queue_cmds(menu->input_ctx, str);
383 if (str != action)
384 free(str);
386 } break;
387 case MENU_CMD_ACTION: {
388 int fname_len = strlen(mpriv->dir) + strlen(mpriv->p.current->p.txt) + 1;
389 char filename[fname_len];
390 char *str;
391 sprintf(filename,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
392 str = replace_path(action, filename,1);
393 mp_input_parse_and_queue_cmds(menu->input_ctx, str);
394 if(str != action)
395 free(str);
396 } break;
397 default:
398 menu_list_read_cmd(menu,cmd);
402 static int read_key(menu_t* menu,int c){
403 char **str;
404 for (str=mpriv->actions; str && *str; str++)
405 if (c == (*str)[0]) {
406 action = &(*str)[2];
407 read_cmd(menu,MENU_CMD_ACTION);
408 return 1;
410 if (menu_dflt_read_key(menu, c))
411 return 1;
412 return menu_list_jump_to_key(menu, c);
415 static void clos(menu_t* menu) {
416 menu_list_uninit(menu,free_entry);
417 free(mpriv->dir);
420 static int open_fs(menu_t* menu, char* args) {
421 char *path = mpriv->path;
422 int r = 0;
423 char wd[PATH_MAX+1], b[PATH_MAX+1];
424 args = NULL; // Warning kill
426 menu->draw = menu_list_draw;
427 menu->read_cmd = read_cmd;
428 menu->read_key = read_key;
429 menu->close = clos;
431 if (menu_keepdir) {
432 if (!path || path[0] == '\0') {
433 struct stat st;
434 int path_fp;
436 path_fp = open (MENU_KEEP_PATH, O_RDONLY);
437 if (path_fp >= 0) {
438 if (!fstat (path_fp, &st) && (st.st_size > 0)) {
439 path = malloc(st.st_size+1);
440 path[st.st_size] = '\0';
441 if (!((read(path_fp, path, st.st_size) == st.st_size) && path[0] == '/'
442 && !stat(path, &st) && S_ISDIR(st.st_mode))) {
443 free(path);
444 path = NULL;
447 close (path_fp);
452 getcwd(wd,PATH_MAX);
453 if (!path || path[0] == '\0') {
454 #if 0
455 char *slash = NULL;
456 if (filename && !strstr(filename, "://") && (path=realpath(filename, b))) {
457 slash = strrchr(path, '/');
458 #if HAVE_DOS_PATHS
459 // FIXME: Do we need and can convert all '\\' in path to '/' on win32?
460 if (!slash)
461 slash = strrchr(path, '\\');
462 #endif
464 if (slash)
465 slash[1] = '\0';
466 else
467 #endif
468 path = wd;
470 if (path[0] != '/') {
471 if(path[strlen(path)-1] != '/')
472 snprintf(b,sizeof(b),"%s/%s/",wd,path);
473 else
474 snprintf(b,sizeof(b),"%s/%s",wd,path);
475 path = b;
476 } else if (path[strlen(path)-1]!='/') {
477 sprintf(b,"%s/",path);
478 path = b;
480 if (menu_chroot && menu_chroot[0] == '/') {
481 int l = strlen(menu_chroot);
482 if (l > 0 && menu_chroot[l-1] == '/')
483 --l;
484 if (strncmp(menu_chroot, path, l) || (path[l] != '\0' && path[l] != '/')) {
485 if (menu_chroot[l] == '/')
486 path = menu_chroot;
487 else {
488 sprintf(b,"%s/",menu_chroot);
489 path = b;
493 r = open_dir(menu,path);
495 return r;
498 const menu_info_t menu_info_filesel = {
499 "File seletor menu",
500 "filesel",
501 "Albeu",
504 "fs_cfg",
505 sizeof(struct menu_priv_s),
506 &cfg_dflt,
507 cfg_fields
509 open_fs