Raise LIBASS_VERSION, forgotten in r31293.
[mplayer/glamo.git] / libmenu / menu_filesel.c
blob4c4e793af9f905fffec5fb1c211b1d54fcd874da
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;
51 extern char *filename;
53 struct list_entry_s {
54 struct list_entry p;
55 int d;
58 struct menu_priv_s {
59 menu_list_priv_t p;
60 char* dir; // current dir
61 /// Cfg fields
62 char* path;
63 char* title;
64 char* file_action;
65 char* dir_action;
66 char** actions;
67 char* filter;
70 static struct menu_priv_s cfg_dflt = {
71 MENU_LIST_PRIV_DFLT,
72 NULL,
74 NULL,
75 "Select a file: %p",
76 "loadfile '%p'",
77 NULL,
78 NULL,
79 NULL
82 #define ST_OFF(m) M_ST_OFF(struct menu_priv_s,m)
84 static const m_option_t cfg_fields[] = {
85 MENU_LIST_PRIV_FIELDS,
86 { "path", ST_OFF(path), CONF_TYPE_STRING, 0, 0, 0, NULL },
87 { "title", ST_OFF(title), CONF_TYPE_STRING, 0, 0, 0, NULL },
88 { "file-action", ST_OFF(file_action), CONF_TYPE_STRING, 0, 0, 0, NULL },
89 { "dir-action", ST_OFF(dir_action), CONF_TYPE_STRING, 0, 0, 0, NULL },
90 { "actions", ST_OFF(actions), CONF_TYPE_STRING_LIST, 0, 0, 0, NULL},
91 { "filter", ST_OFF(filter), CONF_TYPE_STRING, 0, 0, 0, NULL},
92 { NULL, NULL, NULL, 0,0,0,NULL }
95 #define mpriv (menu->priv)
97 static void free_entry(list_entry_t* entry) {
98 free(entry->p.txt);
99 free(entry);
102 static char* replace_path(char* title , char* dir , int escape) {
103 char *p = strstr(title,"%p");
104 if(p) {
105 int tl = strlen(title);
106 int dl = strlen(dir);
107 int t1l = p-title;
108 int l = tl - 2 + dl;
109 char *r, *n, *d = dir;
111 if (escape) {
112 do {
113 if (*d == '\\')
114 l++;
115 else if (*d == '\'') /* ' -> \'\\\'\' */
116 l+=7;
117 } while (*d++);
119 r = malloc(l + 1);
120 n = r + t1l;
121 memcpy(r,title,t1l);
122 do {
123 if (escape) {
124 if (*dir == '\\')
125 *n++ = '\\';
126 else if (*dir == '\'') { /* ' -> \'\\\'\' */
127 *n++ = '\\'; *n++ = '\'';
128 *n++ = '\\'; *n++ = '\\';
129 *n++ = '\\'; *n++ = '\'';
130 *n++ = '\\';
133 } while ((*n++ = *dir++));
134 if(tl - t1l - 2 > 0)
135 strcpy(n-1,p+2);
136 return r;
137 } else
138 return title;
141 typedef int (*kill_warn)(const void*, const void*);
143 static int mylstat(char *dir, char *file,struct stat* st) {
144 int l = strlen(dir) + strlen(file);
145 char s[l+2];
146 if (!strcmp("..", file)) {
147 char *slash;
148 l -= 3;
149 strcpy(s, dir);
150 #if HAVE_DOS_PATHS
151 if (s[l] == '/' || s[l] == '\\')
152 #else
153 if (s[l] == '/')
154 #endif
155 s[l] = '\0';
156 slash = strrchr(s, '/');
157 #if HAVE_DOS_PATHS
158 if (!slash)
159 slash = strrchr(s,'\\');
160 #endif
161 if (!slash)
162 return stat(dir,st);
163 slash[1] = '\0';
164 return stat(s,st);
166 sprintf(s,"%s/%s",dir,file);
167 return stat(s,st);
170 static int compare(char **a, char **b){
171 if((*a)[strlen(*a) - 1] == '/') {
172 if((*b)[strlen(*b) - 1] == '/')
173 return strcmp(*b, *a) ;
174 else
175 return 1;
176 } else {
177 if((*b)[strlen(*b) - 1] == '/')
178 return -1;
179 else
180 return strcmp(*b, *a);
184 static char **get_extensions(menu_t *menu){
185 char **extensions, ext[32];
186 FILE *fp;
187 int n = 1;
189 if (!mpriv->filter)
190 return NULL;
192 fp = fopen(mpriv->filter, "r");
193 if(!fp)
194 return NULL;
196 extensions = malloc(sizeof(*extensions));
197 *extensions = NULL;
199 while(fgets(ext,sizeof(ext),fp)) {
200 char **l, *e;
201 int s = strlen (ext);
203 if(ext[s-1] == '\n') {
204 ext[s-1] = '\0';
205 s--;
207 e = malloc(s+1);
208 extensions = realloc(extensions, ++n * sizeof(*extensions));
209 extensions = realloc(extensions, ++n * sizeof(*extensions));
210 strcpy (e, ext);
211 for (l=extensions; *l; l++);
212 *l++ = e;
213 *l = NULL;
216 fclose (fp);
217 return extensions;
220 static void free_extensions(char **extensions){
221 if (extensions) {
222 char **l = extensions;
223 while (*l)
224 free (*l++);
225 free (extensions);
229 static int open_dir(menu_t* menu,char* args) {
230 char **namelist, **tp;
231 struct dirent *dp;
232 struct stat st;
233 int n;
234 int path_fp;
235 char* p = NULL;
236 list_entry_t* e;
237 DIR* dirp;
238 extern int file_filter;
239 char **extensions, **elem, *ext;
241 menu_list_init(menu);
243 if(mpriv->dir)
244 free(mpriv->dir);
245 mpriv->dir = strdup(args);
246 if(mpriv->p.title && mpriv->p.title != mpriv->title && mpriv->p.title != cfg_dflt.p.title)
247 free(mpriv->p.title);
248 p = strstr(mpriv->title,"%p");
250 mpriv->p.title = replace_path(mpriv->title,mpriv->dir,0);
252 if ((dirp = opendir (mpriv->dir)) == NULL){
253 mp_msg(MSGT_GLOBAL,MSGL_ERR,MSGTR_LIBMENU_OpendirError, strerror(errno));
254 return 0;
257 if (menu_keepdir) {
258 path_fp = open (MENU_KEEP_PATH, O_CREAT | O_WRONLY | O_TRUNC, 0666);
259 if (path_fp >= 0) {
260 write (path_fp, mpriv->dir, strlen (mpriv->dir));
261 close (path_fp);
265 namelist = malloc(sizeof(char *));
266 extensions = get_extensions(menu);
268 n=0;
269 while ((dp = readdir(dirp)) != NULL) {
270 if(dp->d_name[0] == '.' && strcmp(dp->d_name,"..") != 0)
271 continue;
272 if (menu_chroot && !strcmp (dp->d_name,"..")) {
273 size_t len = strlen (menu_chroot);
274 if ((strlen (mpriv->dir) == len || strlen (mpriv->dir) == len + 1)
275 && !strncmp (mpriv->dir, menu_chroot, len))
276 continue;
278 if (mylstat(args,dp->d_name,&st))
279 continue;
280 if (file_filter && extensions && !S_ISDIR(st.st_mode)) {
281 if((ext = strrchr(dp->d_name,'.')) == NULL)
282 continue;
283 ext++;
284 elem = extensions;
285 do {
286 if (!strcasecmp(ext, *elem))
287 break;
288 } while (*++elem);
289 if (*elem == NULL)
290 continue;
292 if(n%20 == 0){ // Get some more mem
293 if((tp = realloc(namelist, (n+20) * sizeof (char *)))
294 == NULL) {
295 mp_msg(MSGT_GLOBAL,MSGL_ERR,MSGTR_LIBMENU_ReallocError, strerror(errno));
296 n--;
297 goto bailout;
299 namelist=tp;
302 namelist[n] = malloc(strlen(dp->d_name) + 2);
303 if(namelist[n] == NULL){
304 mp_msg(MSGT_GLOBAL,MSGL_ERR,MSGTR_LIBMENU_MallocError, strerror(errno));
305 n--;
306 goto bailout;
309 strcpy(namelist[n], dp->d_name);
310 if(S_ISDIR(st.st_mode))
311 strcat(namelist[n], "/");
312 n++;
315 bailout:
316 free_extensions (extensions);
317 closedir(dirp);
319 qsort(namelist, n, sizeof(char *), (kill_warn)compare);
321 if (n < 0) {
322 mp_msg(MSGT_GLOBAL,MSGL_ERR,MSGTR_LIBMENU_ReaddirError,strerror(errno));
323 return 0;
325 while(n--) {
326 if((e = calloc(1,sizeof(list_entry_t))) != NULL){
327 e->p.next = NULL;
328 e->p.txt = strdup(namelist[n]);
329 if(strchr(namelist[n], '/') != NULL)
330 e->d = 1;
331 menu_list_add_entry(menu,e);
332 }else{
333 mp_msg(MSGT_GLOBAL,MSGL_ERR,MSGTR_LIBMENU_MallocError, strerror(errno));
335 free(namelist[n]);
337 free(namelist);
339 return 1;
342 static char *action;
344 static void read_cmd(menu_t* menu,int cmd) {
345 switch(cmd) {
346 case MENU_CMD_LEFT:
347 mpriv->p.current = mpriv->p.menu; // Hack : we consider that the first entry is ../
348 case MENU_CMD_RIGHT:
349 case MENU_CMD_OK: {
350 // Directory
351 if(mpriv->p.current->d && !mpriv->dir_action) {
352 // Default action : open this dirctory ourself
353 int l = strlen(mpriv->dir);
354 char *slash = NULL, *p = NULL;
355 if(strcmp(mpriv->p.current->p.txt,"../") == 0) {
356 if(l <= 1) break;
357 mpriv->dir[l-1] = '\0';
358 slash = strrchr(mpriv->dir,'/');
359 #if HAVE_DOS_PATHS
360 if (!slash)
361 slash = strrchr(mpriv->dir,'\\');
362 #endif
363 if(!slash) break;
364 slash[1] = '\0';
365 p = strdup(mpriv->dir);
366 } else {
367 p = malloc(l + strlen(mpriv->p.current->p.txt) + 1);
368 sprintf(p,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
370 menu_list_uninit(menu,free_entry);
371 if(!open_dir(menu,p)) {
372 mp_msg(MSGT_GLOBAL,MSGL_ERR,MSGTR_LIBMENU_CantOpenDirectory,p);
373 menu->cl = 1;
375 free(p);
376 } else { // File and directory dealt with action string.
377 int fname_len = strlen(mpriv->dir) + strlen(mpriv->p.current->p.txt) + 1;
378 char filename[fname_len];
379 char *str;
380 char *action = mpriv->p.current->d ? mpriv->dir_action:mpriv->file_action;
381 sprintf(filename,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
382 str = replace_path(action, filename,1);
383 mp_input_parse_and_queue_cmds(str);
384 if (str != action)
385 free(str);
387 } break;
388 case MENU_CMD_ACTION: {
389 int fname_len = strlen(mpriv->dir) + strlen(mpriv->p.current->p.txt) + 1;
390 char filename[fname_len];
391 char *str;
392 sprintf(filename,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
393 str = replace_path(action, filename,1);
394 mp_input_parse_and_queue_cmds(str);
395 if(str != action)
396 free(str);
397 } break;
398 default:
399 menu_list_read_cmd(menu,cmd);
403 static int read_key(menu_t* menu,int c){
404 char **str;
405 for (str=mpriv->actions; str && *str; str++)
406 if (c == (*str)[0]) {
407 action = &(*str)[2];
408 read_cmd(menu,MENU_CMD_ACTION);
409 return 1;
411 if (menu_dflt_read_key(menu, c))
412 return 1;
413 return menu_list_jump_to_key(menu, c);
416 static void clos(menu_t* menu) {
417 menu_list_uninit(menu,free_entry);
418 free(mpriv->dir);
421 static int open_fs(menu_t* menu, char* args) {
422 char *path = mpriv->path;
423 int r = 0;
424 char wd[PATH_MAX+1], b[PATH_MAX+1];
425 args = NULL; // Warning kill
427 menu->draw = menu_list_draw;
428 menu->read_cmd = read_cmd;
429 menu->read_key = read_key;
430 menu->close = clos;
432 if (menu_keepdir) {
433 if (!path || path[0] == '\0') {
434 struct stat st;
435 int path_fp;
437 path_fp = open (MENU_KEEP_PATH, O_RDONLY);
438 if (path_fp >= 0) {
439 if (!fstat (path_fp, &st) && (st.st_size > 0)) {
440 path = malloc(st.st_size+1);
441 path[st.st_size] = '\0';
442 if (!((read(path_fp, path, st.st_size) == st.st_size) && path[0] == '/'
443 && !stat(path, &st) && S_ISDIR(st.st_mode))) {
444 free(path);
445 path = NULL;
448 close (path_fp);
453 getcwd(wd,PATH_MAX);
454 if (!path || path[0] == '\0') {
455 #if 0
456 char *slash = NULL;
457 if (filename && !strstr(filename, "://") && (path=realpath(filename, b))) {
458 slash = strrchr(path, '/');
459 #if HAVE_DOS_PATHS
460 // FIXME: Do we need and can convert all '\\' in path to '/' on win32?
461 if (!slash)
462 slash = strrchr(path, '\\');
463 #endif
465 if (slash)
466 slash[1] = '\0';
467 else
468 #endif
469 path = wd;
471 if (path[0] != '/') {
472 if(path[strlen(path)-1] != '/')
473 snprintf(b,sizeof(b),"%s/%s/",wd,path);
474 else
475 snprintf(b,sizeof(b),"%s/%s",wd,path);
476 path = b;
477 } else if (path[strlen(path)-1]!='/') {
478 sprintf(b,"%s/",path);
479 path = b;
481 if (menu_chroot && menu_chroot[0] == '/') {
482 int l = strlen(menu_chroot);
483 if (l > 0 && menu_chroot[l-1] == '/')
484 --l;
485 if (strncmp(menu_chroot, path, l) || (path[l] != '\0' && path[l] != '/')) {
486 if (menu_chroot[l] == '/')
487 path = menu_chroot;
488 else {
489 sprintf(b,"%s/",menu_chroot);
490 path = b;
494 r = open_dir(menu,path);
496 return r;
499 const menu_info_t menu_info_filesel = {
500 "File seletor menu",
501 "filesel",
502 "Albeu",
505 "fs_cfg",
506 sizeof(struct menu_priv_s),
507 &cfg_dflt,
508 cfg_fields
510 open_fs