Fix vf_tcdump's compilation
[mplayer/kovensky.git] / libmenu / menu_filesel.c
blobfd16642ddfa15ee03a093e42a300872645e56ee6
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 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 if(mpriv->dir)
242 free(mpriv->dir);
243 mpriv->dir = strdup(args);
244 if(mpriv->p.title && mpriv->p.title != mpriv->title && mpriv->p.title != cfg_dflt.p.title)
245 free(mpriv->p.title);
246 p = strstr(mpriv->title,"%p");
248 mpriv->p.title = replace_path(mpriv->title,mpriv->dir,0);
250 if ((dirp = opendir (mpriv->dir)) == NULL){
251 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] opendir error: %s\n", strerror(errno));
252 return 0;
255 if (menu_keepdir) {
256 path_fp = open (MENU_KEEP_PATH, O_CREAT | O_WRONLY | O_TRUNC, 0666);
257 if (path_fp >= 0) {
258 write (path_fp, mpriv->dir, strlen (mpriv->dir));
259 close (path_fp);
263 namelist = malloc(sizeof(char *));
264 extensions = get_extensions(menu);
266 n=0;
267 while ((dp = readdir(dirp)) != NULL) {
268 if(dp->d_name[0] == '.' && strcmp(dp->d_name,"..") != 0)
269 continue;
270 if (menu_chroot && !strcmp (dp->d_name,"..")) {
271 size_t len = strlen (menu_chroot);
272 if ((strlen (mpriv->dir) == len || strlen (mpriv->dir) == len + 1)
273 && !strncmp (mpriv->dir, menu_chroot, len))
274 continue;
276 if (mylstat(args,dp->d_name,&st))
277 continue;
278 if (file_filter && extensions && !S_ISDIR(st.st_mode)) {
279 if((ext = strrchr(dp->d_name,'.')) == NULL)
280 continue;
281 ext++;
282 elem = extensions;
283 do {
284 if (!strcasecmp(ext, *elem))
285 break;
286 } while (*++elem);
287 if (*elem == NULL)
288 continue;
290 if(n%20 == 0){ // Get some more mem
291 if((tp = realloc(namelist, (n+20) * sizeof (char *)))
292 == NULL) {
293 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] realloc error: %s\n", strerror(errno));
294 n--;
295 goto bailout;
297 namelist=tp;
300 namelist[n] = malloc(strlen(dp->d_name) + 2);
301 if(namelist[n] == NULL){
302 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] memory allocation error: %s\n", strerror(errno));
303 n--;
304 goto bailout;
307 strcpy(namelist[n], dp->d_name);
308 if(S_ISDIR(st.st_mode))
309 strcat(namelist[n], "/");
310 n++;
313 bailout:
314 free_extensions (extensions);
315 closedir(dirp);
317 qsort(namelist, n, sizeof(char *), (kill_warn)compare);
319 if (n < 0) {
320 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] readdir error: %s\n",strerror(errno));
321 return 0;
323 while(n--) {
324 if((e = calloc(1,sizeof(list_entry_t))) != NULL){
325 e->p.next = NULL;
326 e->p.txt = strdup(namelist[n]);
327 if(strchr(namelist[n], '/') != NULL)
328 e->d = 1;
329 menu_list_add_entry(menu,e);
330 }else{
331 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] memory allocation error: %s\n", strerror(errno));
333 free(namelist[n]);
335 free(namelist);
337 return 1;
340 static char *action;
342 static void read_cmd(menu_t* menu,int cmd) {
343 switch(cmd) {
344 case MENU_CMD_LEFT:
345 mpriv->p.current = mpriv->p.menu; // Hack : we consider that the first entry is ../
346 case MENU_CMD_RIGHT:
347 case MENU_CMD_OK: {
348 // Directory
349 if(mpriv->p.current->d && !mpriv->dir_action) {
350 // Default action : open this dirctory ourself
351 int l = strlen(mpriv->dir);
352 char *slash = NULL, *p = NULL;
353 if(strcmp(mpriv->p.current->p.txt,"../") == 0) {
354 if(l <= 1) break;
355 mpriv->dir[l-1] = '\0';
356 slash = strrchr(mpriv->dir,'/');
357 #if HAVE_DOS_PATHS
358 if (!slash)
359 slash = strrchr(mpriv->dir,'\\');
360 #endif
361 if(!slash) break;
362 slash[1] = '\0';
363 p = strdup(mpriv->dir);
364 } else {
365 p = malloc(l + strlen(mpriv->p.current->p.txt) + 1);
366 sprintf(p,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
368 menu_list_uninit(menu,free_entry);
369 if(!open_dir(menu,p)) {
370 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] Can't open directory %s.\n",p);
371 menu->cl = 1;
373 free(p);
374 } else { // File and directory dealt with action string.
375 int fname_len = strlen(mpriv->dir) + strlen(mpriv->p.current->p.txt) + 1;
376 char filename[fname_len];
377 char *str;
378 char *action = mpriv->p.current->d ? mpriv->dir_action:mpriv->file_action;
379 sprintf(filename,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
380 str = replace_path(action, filename,1);
381 mp_input_parse_and_queue_cmds(menu->input_ctx, str);
382 if (str != action)
383 free(str);
385 } break;
386 case MENU_CMD_ACTION: {
387 int fname_len = strlen(mpriv->dir) + strlen(mpriv->p.current->p.txt) + 1;
388 char filename[fname_len];
389 char *str;
390 sprintf(filename,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
391 str = replace_path(action, filename,1);
392 mp_input_parse_and_queue_cmds(menu->input_ctx, str);
393 if(str != action)
394 free(str);
395 } break;
396 default:
397 menu_list_read_cmd(menu,cmd);
401 static int read_key(menu_t* menu,int c){
402 char **str;
403 for (str=mpriv->actions; str && *str; str++)
404 if (c == (*str)[0]) {
405 action = &(*str)[2];
406 read_cmd(menu,MENU_CMD_ACTION);
407 return 1;
409 if (menu_dflt_read_key(menu, c))
410 return 1;
411 return menu_list_jump_to_key(menu, c);
414 static void clos(menu_t* menu) {
415 menu_list_uninit(menu,free_entry);
416 free(mpriv->dir);
419 static int open_fs(menu_t* menu, char* args) {
420 char *path = mpriv->path;
421 int r = 0;
422 char wd[PATH_MAX+1], b[PATH_MAX+1];
423 args = NULL; // Warning kill
425 menu->draw = menu_list_draw;
426 menu->read_cmd = read_cmd;
427 menu->read_key = read_key;
428 menu->close = clos;
430 if (menu_keepdir) {
431 if (!path || path[0] == '\0') {
432 struct stat st;
433 int path_fp;
435 path_fp = open (MENU_KEEP_PATH, O_RDONLY);
436 if (path_fp >= 0) {
437 if (!fstat (path_fp, &st) && (st.st_size > 0)) {
438 path = malloc(st.st_size+1);
439 path[st.st_size] = '\0';
440 if (!((read(path_fp, path, st.st_size) == st.st_size) && path[0] == '/'
441 && !stat(path, &st) && S_ISDIR(st.st_mode))) {
442 free(path);
443 path = NULL;
446 close (path_fp);
451 getcwd(wd,PATH_MAX);
452 if (!path || path[0] == '\0') {
453 #if 0
454 char *slash = NULL;
455 if (filename && !strstr(filename, "://") && (path=realpath(filename, b))) {
456 slash = strrchr(path, '/');
457 #if HAVE_DOS_PATHS
458 // FIXME: Do we need and can convert all '\\' in path to '/' on win32?
459 if (!slash)
460 slash = strrchr(path, '\\');
461 #endif
463 if (slash)
464 slash[1] = '\0';
465 else
466 #endif
467 path = wd;
469 if (path[0] != '/') {
470 if(path[strlen(path)-1] != '/')
471 snprintf(b,sizeof(b),"%s/%s/",wd,path);
472 else
473 snprintf(b,sizeof(b),"%s/%s",wd,path);
474 path = b;
475 } else if (path[strlen(path)-1]!='/') {
476 sprintf(b,"%s/",path);
477 path = b;
479 if (menu_chroot && menu_chroot[0] == '/') {
480 int l = strlen(menu_chroot);
481 if (l > 0 && menu_chroot[l-1] == '/')
482 --l;
483 if (strncmp(menu_chroot, path, l) || (path[l] != '\0' && path[l] != '/')) {
484 if (menu_chroot[l] == '/')
485 path = menu_chroot;
486 else {
487 sprintf(b,"%s/",menu_chroot);
488 path = b;
492 r = open_dir(menu,path);
494 return r;
497 const menu_info_t menu_info_filesel = {
498 "File seletor menu",
499 "filesel",
500 "Albeu",
503 "fs_cfg",
504 sizeof(struct menu_priv_s),
505 &cfg_dflt,
506 cfg_fields
508 open_fs