core: fix audio-only + framestep weird behavior
[mplayer/glamo.git] / libmenu / menu_filesel.c
blob047ddaca977d891e7ecc60cb996471cf1d4850ff
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"
35 #include "m_struct.h"
36 #include "m_option.h"
38 #include "libmpcodecs/img_format.h"
39 #include "libmpcodecs/mp_image.h"
41 #include "menu.h"
42 #include "menu_list.h"
43 #include "input/input.h"
44 #include "osdep/keycodes.h"
46 #define MENU_KEEP_PATH "/tmp/mp_current_path"
48 int menu_keepdir = 0;
49 char *menu_chroot = NULL;
51 struct list_entry_s {
52 struct list_entry p;
53 int d;
56 struct menu_priv_s {
57 menu_list_priv_t p;
58 char* dir; // current dir
59 /// Cfg fields
60 char* path;
61 char* title;
62 char* file_action;
63 char* dir_action;
64 char** actions;
65 char* filter;
68 static struct menu_priv_s cfg_dflt = {
69 MENU_LIST_PRIV_DFLT,
70 NULL,
72 NULL,
73 "Select a file: %p",
74 "loadfile '%p'",
75 NULL,
76 NULL,
77 NULL
80 #define ST_OFF(m) M_ST_OFF(struct menu_priv_s,m)
82 static const m_option_t cfg_fields[] = {
83 MENU_LIST_PRIV_FIELDS,
84 { "path", ST_OFF(path), CONF_TYPE_STRING, 0, 0, 0, NULL },
85 { "title", ST_OFF(title), CONF_TYPE_STRING, 0, 0, 0, NULL },
86 { "file-action", ST_OFF(file_action), CONF_TYPE_STRING, 0, 0, 0, NULL },
87 { "dir-action", ST_OFF(dir_action), CONF_TYPE_STRING, 0, 0, 0, NULL },
88 { "actions", ST_OFF(actions), CONF_TYPE_STRING_LIST, 0, 0, 0, NULL},
89 { "filter", ST_OFF(filter), CONF_TYPE_STRING, 0, 0, 0, NULL},
90 { NULL, NULL, NULL, 0,0,0,NULL }
93 #define mpriv (menu->priv)
95 static void free_entry(list_entry_t* entry) {
96 free(entry->p.txt);
97 free(entry);
100 static char* replace_path(char* title , char* dir , int escape) {
101 char *p = strstr(title,"%p");
102 if(p) {
103 int tl = strlen(title);
104 int dl = strlen(dir);
105 int t1l = p-title;
106 int l = tl - 2 + dl;
107 char *r, *n, *d = dir;
109 if (escape) {
110 do {
111 if (*d == '\\')
112 l++;
113 else if (*d == '\'') /* ' -> \'\\\'\' */
114 l+=7;
115 } while (*d++);
117 r = malloc(l + 1);
118 n = r + t1l;
119 memcpy(r,title,t1l);
120 do {
121 if (escape) {
122 if (*dir == '\\')
123 *n++ = '\\';
124 else if (*dir == '\'') { /* ' -> \'\\\'\' */
125 *n++ = '\\'; *n++ = '\'';
126 *n++ = '\\'; *n++ = '\\';
127 *n++ = '\\'; *n++ = '\'';
128 *n++ = '\\';
131 } while ((*n++ = *dir++));
132 if(tl - t1l - 2 > 0)
133 strcpy(n-1,p+2);
134 return r;
135 } else
136 return title;
139 typedef int (*kill_warn)(const void*, const void*);
141 static int mylstat(char *dir, char *file,struct stat* st) {
142 int l = strlen(dir) + strlen(file);
143 char s[l+2];
144 if (!strcmp("..", file)) {
145 char *slash;
146 l -= 3;
147 strcpy(s, dir);
148 #if HAVE_DOS_PATHS
149 if (s[l] == '/' || s[l] == '\\')
150 #else
151 if (s[l] == '/')
152 #endif
153 s[l] = '\0';
154 slash = strrchr(s, '/');
155 #if HAVE_DOS_PATHS
156 if (!slash)
157 slash = strrchr(s,'\\');
158 #endif
159 if (!slash)
160 return stat(dir,st);
161 slash[1] = '\0';
162 return stat(s,st);
164 sprintf(s,"%s/%s",dir,file);
165 return stat(s,st);
168 static int compare(char **a, char **b){
169 if((*a)[strlen(*a) - 1] == '/') {
170 if((*b)[strlen(*b) - 1] == '/')
171 return strcmp(*b, *a) ;
172 else
173 return 1;
174 } else {
175 if((*b)[strlen(*b) - 1] == '/')
176 return -1;
177 else
178 return strcmp(*b, *a);
182 static char **get_extensions(menu_t *menu){
183 char **extensions, ext[32];
184 FILE *fp;
185 int n = 1;
187 if (!mpriv->filter)
188 return NULL;
190 fp = fopen(mpriv->filter, "r");
191 if(!fp)
192 return NULL;
194 extensions = malloc(sizeof(*extensions));
195 *extensions = NULL;
197 while(fgets(ext,sizeof(ext),fp)) {
198 char **l, *e;
199 int s = strlen (ext);
201 if(ext[s-1] == '\n') {
202 ext[s-1] = '\0';
203 s--;
205 e = malloc(s+1);
206 extensions = realloc(extensions, ++n * sizeof(*extensions));
207 extensions = realloc(extensions, ++n * sizeof(*extensions));
208 strcpy (e, ext);
209 for (l=extensions; *l; l++);
210 *l++ = e;
211 *l = NULL;
214 fclose (fp);
215 return extensions;
218 static void free_extensions(char **extensions){
219 if (extensions) {
220 char **l = extensions;
221 while (*l)
222 free (*l++);
223 free (extensions);
227 static int open_dir(menu_t* menu,char* args) {
228 char **namelist, **tp;
229 struct dirent *dp;
230 struct stat st;
231 int n;
232 int path_fp;
233 char* p = NULL;
234 list_entry_t* e;
235 DIR* dirp;
236 extern int file_filter;
237 char **extensions, **elem, *ext;
239 menu_list_init(menu);
241 free(mpriv->dir);
242 mpriv->dir = strdup(args);
243 if(mpriv->p.title && mpriv->p.title != mpriv->title && mpriv->p.title != cfg_dflt.p.title)
244 free(mpriv->p.title);
245 p = strstr(mpriv->title,"%p");
247 mpriv->p.title = replace_path(mpriv->title,mpriv->dir,0);
249 if ((dirp = opendir (mpriv->dir)) == NULL){
250 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] opendir error: %s\n", strerror(errno));
251 return 0;
254 if (menu_keepdir) {
255 path_fp = open (MENU_KEEP_PATH, O_CREAT | O_WRONLY | O_TRUNC, 0666);
256 if (path_fp >= 0) {
257 write (path_fp, mpriv->dir, strlen (mpriv->dir));
258 close (path_fp);
262 namelist = malloc(sizeof(char *));
263 extensions = get_extensions(menu);
265 n=0;
266 while ((dp = readdir(dirp)) != NULL) {
267 if(dp->d_name[0] == '.' && strcmp(dp->d_name,"..") != 0)
268 continue;
269 if (menu_chroot && !strcmp (dp->d_name,"..")) {
270 size_t len = strlen (menu_chroot);
271 if ((strlen (mpriv->dir) == len || strlen (mpriv->dir) == len + 1)
272 && !strncmp (mpriv->dir, menu_chroot, len))
273 continue;
275 if (mylstat(args,dp->d_name,&st))
276 continue;
277 if (file_filter && extensions && !S_ISDIR(st.st_mode)) {
278 if((ext = strrchr(dp->d_name,'.')) == NULL)
279 continue;
280 ext++;
281 elem = extensions;
282 do {
283 if (!strcasecmp(ext, *elem))
284 break;
285 } while (*++elem);
286 if (*elem == NULL)
287 continue;
289 if(n%20 == 0){ // Get some more mem
290 if((tp = realloc(namelist, (n+20) * sizeof (char *)))
291 == NULL) {
292 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] realloc error: %s\n", strerror(errno));
293 n--;
294 goto bailout;
296 namelist=tp;
299 namelist[n] = malloc(strlen(dp->d_name) + 2);
300 if(namelist[n] == NULL){
301 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] memory allocation error: %s\n", strerror(errno));
302 n--;
303 goto bailout;
306 strcpy(namelist[n], dp->d_name);
307 if(S_ISDIR(st.st_mode))
308 strcat(namelist[n], "/");
309 n++;
312 bailout:
313 free_extensions (extensions);
314 closedir(dirp);
316 qsort(namelist, n, sizeof(char *), (kill_warn)compare);
318 if (n < 0) {
319 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] readdir error: %s\n",strerror(errno));
320 return 0;
322 while(n--) {
323 if((e = calloc(1,sizeof(list_entry_t))) != NULL){
324 e->p.next = NULL;
325 e->p.txt = strdup(namelist[n]);
326 if(strchr(namelist[n], '/') != NULL)
327 e->d = 1;
328 menu_list_add_entry(menu,e);
329 }else{
330 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] memory allocation error: %s\n", strerror(errno));
332 free(namelist[n]);
334 free(namelist);
336 return 1;
339 static char *action;
341 static void read_cmd(menu_t* menu,int cmd) {
342 switch(cmd) {
343 case MENU_CMD_LEFT:
344 mpriv->p.current = mpriv->p.menu; // Hack : we consider that the first entry is ../
345 case MENU_CMD_RIGHT:
346 case MENU_CMD_OK: {
347 // Directory
348 if(mpriv->p.current->d && !mpriv->dir_action) {
349 // Default action : open this dirctory ourself
350 int l = strlen(mpriv->dir);
351 char *slash = NULL, *p = NULL;
352 if(strcmp(mpriv->p.current->p.txt,"../") == 0) {
353 if(l <= 1) break;
354 mpriv->dir[l-1] = '\0';
355 slash = strrchr(mpriv->dir,'/');
356 #if HAVE_DOS_PATHS
357 if (!slash)
358 slash = strrchr(mpriv->dir,'\\');
359 #endif
360 if(!slash) break;
361 slash[1] = '\0';
362 p = strdup(mpriv->dir);
363 } else {
364 p = malloc(l + strlen(mpriv->p.current->p.txt) + 1);
365 sprintf(p,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
367 menu_list_uninit(menu,free_entry);
368 if(!open_dir(menu,p)) {
369 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] Can't open directory %s.\n",p);
370 menu->cl = 1;
372 free(p);
373 } else { // File and directory dealt with action string.
374 int fname_len = strlen(mpriv->dir) + strlen(mpriv->p.current->p.txt) + 1;
375 char filename[fname_len];
376 char *str;
377 char *action = mpriv->p.current->d ? mpriv->dir_action:mpriv->file_action;
378 sprintf(filename,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
379 str = replace_path(action, filename,1);
380 mp_input_parse_and_queue_cmds(menu->input_ctx, str);
381 if (str != action)
382 free(str);
384 } break;
385 case MENU_CMD_ACTION: {
386 int fname_len = strlen(mpriv->dir) + strlen(mpriv->p.current->p.txt) + 1;
387 char filename[fname_len];
388 char *str;
389 sprintf(filename,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
390 str = replace_path(action, filename,1);
391 mp_input_parse_and_queue_cmds(menu->input_ctx, str);
392 if(str != action)
393 free(str);
394 } break;
395 default:
396 menu_list_read_cmd(menu,cmd);
400 static int read_key(menu_t* menu,int c){
401 char **str;
402 for (str=mpriv->actions; str && *str; str++)
403 if (c == (*str)[0]) {
404 action = &(*str)[2];
405 read_cmd(menu,MENU_CMD_ACTION);
406 return 1;
408 if (menu_dflt_read_key(menu, c))
409 return 1;
410 return menu_list_jump_to_key(menu, c);
413 static void clos(menu_t* menu) {
414 menu_list_uninit(menu,free_entry);
415 free(mpriv->dir);
418 static int open_fs(menu_t* menu, char* args) {
419 char *path = mpriv->path;
420 int r = 0;
421 char wd[PATH_MAX+1], b[PATH_MAX+1];
422 args = NULL; // Warning kill
424 menu->draw = menu_list_draw;
425 menu->read_cmd = read_cmd;
426 menu->read_key = read_key;
427 menu->close = clos;
429 if (menu_keepdir) {
430 if (!path || path[0] == '\0') {
431 struct stat st;
432 int path_fp;
434 path_fp = open (MENU_KEEP_PATH, O_RDONLY);
435 if (path_fp >= 0) {
436 if (!fstat (path_fp, &st) && (st.st_size > 0)) {
437 path = malloc(st.st_size+1);
438 path[st.st_size] = '\0';
439 if (!((read(path_fp, path, st.st_size) == st.st_size) && path[0] == '/'
440 && !stat(path, &st) && S_ISDIR(st.st_mode))) {
441 free(path);
442 path = NULL;
445 close (path_fp);
450 getcwd(wd,PATH_MAX);
451 if (!path || path[0] == '\0') {
452 #if 0
453 char *slash = NULL;
454 if (filename && !strstr(filename, "://") && (path=realpath(filename, b))) {
455 slash = strrchr(path, '/');
456 #if HAVE_DOS_PATHS
457 // FIXME: Do we need and can convert all '\\' in path to '/' on win32?
458 if (!slash)
459 slash = strrchr(path, '\\');
460 #endif
462 if (slash)
463 slash[1] = '\0';
464 else
465 #endif
466 path = wd;
468 if (path[0] != '/') {
469 if(path[strlen(path)-1] != '/')
470 snprintf(b,sizeof(b),"%s/%s/",wd,path);
471 else
472 snprintf(b,sizeof(b),"%s/%s",wd,path);
473 path = b;
474 } else if (path[strlen(path)-1]!='/') {
475 sprintf(b,"%s/",path);
476 path = b;
478 if (menu_chroot && menu_chroot[0] == '/') {
479 int l = strlen(menu_chroot);
480 if (l > 0 && menu_chroot[l-1] == '/')
481 --l;
482 if (strncmp(menu_chroot, path, l) || (path[l] != '\0' && path[l] != '/')) {
483 if (menu_chroot[l] == '/')
484 path = menu_chroot;
485 else {
486 sprintf(b,"%s/",menu_chroot);
487 path = b;
491 r = open_dir(menu,path);
493 return r;
496 const menu_info_t menu_info_filesel = {
497 "File seletor menu",
498 "filesel",
499 "Albeu",
502 "fs_cfg",
503 sizeof(struct menu_priv_s),
504 &cfg_dflt,
505 cfg_fields
507 open_fs