playtree: make some char * function arguments const
[mplayer.git] / libmenu / menu_filesel.c
blob7c63954d1d57a72c310c686b7ce8264614977ccb
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 "input/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 list_entry_t* e;
234 DIR* dirp;
235 extern int file_filter;
236 char **extensions, **elem, *ext;
238 menu_list_init(menu);
240 free(mpriv->dir);
241 mpriv->dir = strdup(args);
242 if(mpriv->p.title && mpriv->p.title != mpriv->title && mpriv->p.title != cfg_dflt.p.title)
243 free(mpriv->p.title);
245 mpriv->p.title = replace_path(mpriv->title,mpriv->dir,0);
247 if ((dirp = opendir (mpriv->dir)) == NULL){
248 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] opendir error: %s\n", strerror(errno));
249 return 0;
252 if (menu_keepdir) {
253 path_fp = open (MENU_KEEP_PATH, O_CREAT | O_WRONLY | O_TRUNC, 0666);
254 if (path_fp >= 0) {
255 write (path_fp, mpriv->dir, strlen (mpriv->dir));
256 close (path_fp);
260 namelist = malloc(sizeof(char *));
261 extensions = get_extensions(menu);
263 n=0;
264 while ((dp = readdir(dirp)) != NULL) {
265 if(dp->d_name[0] == '.' && strcmp(dp->d_name,"..") != 0)
266 continue;
267 if (menu_chroot && !strcmp (dp->d_name,"..")) {
268 size_t len = strlen (menu_chroot);
269 if ((strlen (mpriv->dir) == len || strlen (mpriv->dir) == len + 1)
270 && !strncmp (mpriv->dir, menu_chroot, len))
271 continue;
273 if (mylstat(args,dp->d_name,&st))
274 continue;
275 if (file_filter && extensions && !S_ISDIR(st.st_mode)) {
276 if((ext = strrchr(dp->d_name,'.')) == NULL)
277 continue;
278 ext++;
279 elem = extensions;
280 do {
281 if (!strcasecmp(ext, *elem))
282 break;
283 } while (*++elem);
284 if (*elem == NULL)
285 continue;
287 if(n%20 == 0){ // Get some more mem
288 if((tp = realloc(namelist, (n+20) * sizeof (char *)))
289 == NULL) {
290 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] realloc error: %s\n", strerror(errno));
291 n--;
292 goto bailout;
294 namelist=tp;
297 namelist[n] = malloc(strlen(dp->d_name) + 2);
298 if(namelist[n] == NULL){
299 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] memory allocation error: %s\n", strerror(errno));
300 n--;
301 goto bailout;
304 strcpy(namelist[n], dp->d_name);
305 if(S_ISDIR(st.st_mode))
306 strcat(namelist[n], "/");
307 n++;
310 bailout:
311 free_extensions (extensions);
312 closedir(dirp);
314 qsort(namelist, n, sizeof(char *), (kill_warn)compare);
316 if (n < 0) {
317 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] readdir error: %s\n",strerror(errno));
318 return 0;
320 while(n--) {
321 if((e = calloc(1,sizeof(list_entry_t))) != NULL){
322 e->p.next = NULL;
323 e->p.txt = strdup(namelist[n]);
324 if(strchr(namelist[n], '/') != NULL)
325 e->d = 1;
326 menu_list_add_entry(menu,e);
327 }else{
328 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] memory allocation error: %s\n", strerror(errno));
330 free(namelist[n]);
332 free(namelist);
334 return 1;
337 static char *action;
339 static void read_cmd(menu_t* menu,int cmd) {
340 switch(cmd) {
341 case MENU_CMD_LEFT:
342 mpriv->p.current = mpriv->p.menu; // Hack : we consider that the first entry is ../
343 case MENU_CMD_RIGHT:
344 case MENU_CMD_OK: {
345 // Directory
346 if(mpriv->p.current->d && !mpriv->dir_action) {
347 // Default action : open this dirctory ourself
348 int l = strlen(mpriv->dir);
349 char *slash = NULL, *p = NULL;
350 if(strcmp(mpriv->p.current->p.txt,"../") == 0) {
351 if(l <= 1) break;
352 mpriv->dir[l-1] = '\0';
353 slash = strrchr(mpriv->dir,'/');
354 #if HAVE_DOS_PATHS
355 if (!slash)
356 slash = strrchr(mpriv->dir,'\\');
357 #endif
358 if(!slash) break;
359 slash[1] = '\0';
360 p = strdup(mpriv->dir);
361 } else {
362 p = malloc(l + strlen(mpriv->p.current->p.txt) + 1);
363 sprintf(p,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
365 menu_list_uninit(menu,free_entry);
366 if(!open_dir(menu,p)) {
367 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] Can't open directory %s.\n",p);
368 menu->cl = 1;
370 free(p);
371 } else { // File and directory dealt with action string.
372 int fname_len = strlen(mpriv->dir) + strlen(mpriv->p.current->p.txt) + 1;
373 char filename[fname_len];
374 char *str;
375 char *action = mpriv->p.current->d ? mpriv->dir_action:mpriv->file_action;
376 sprintf(filename,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
377 str = replace_path(action, filename,1);
378 mp_input_parse_and_queue_cmds(menu->input_ctx, str);
379 if (str != action)
380 free(str);
382 } break;
383 case MENU_CMD_ACTION: {
384 int fname_len = strlen(mpriv->dir) + strlen(mpriv->p.current->p.txt) + 1;
385 char filename[fname_len];
386 char *str;
387 sprintf(filename,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
388 str = replace_path(action, filename,1);
389 mp_input_parse_and_queue_cmds(menu->input_ctx, str);
390 if(str != action)
391 free(str);
392 } break;
393 default:
394 menu_list_read_cmd(menu,cmd);
398 static int read_key(menu_t* menu,int c){
399 char **str;
400 for (str=mpriv->actions; str && *str; str++)
401 if (c == (*str)[0]) {
402 action = &(*str)[2];
403 read_cmd(menu,MENU_CMD_ACTION);
404 return 1;
406 if (menu_dflt_read_key(menu, c))
407 return 1;
408 return menu_list_jump_to_key(menu, c);
411 static void clos(menu_t* menu) {
412 menu_list_uninit(menu,free_entry);
413 free(mpriv->dir);
416 static int open_fs(menu_t* menu, char* args) {
417 char *path = mpriv->path;
418 int r = 0;
419 char wd[PATH_MAX+1], b[PATH_MAX+1];
420 args = NULL; // Warning kill
422 menu->draw = menu_list_draw;
423 menu->read_cmd = read_cmd;
424 menu->read_key = read_key;
425 menu->close = clos;
427 if (menu_keepdir) {
428 if (!path || path[0] == '\0') {
429 struct stat st;
430 int path_fp;
432 path_fp = open (MENU_KEEP_PATH, O_RDONLY);
433 if (path_fp >= 0) {
434 if (!fstat (path_fp, &st) && (st.st_size > 0)) {
435 path = malloc(st.st_size+1);
436 path[st.st_size] = '\0';
437 if (!((read(path_fp, path, st.st_size) == st.st_size) && path[0] == '/'
438 && !stat(path, &st) && S_ISDIR(st.st_mode))) {
439 free(path);
440 path = NULL;
443 close (path_fp);
448 getcwd(wd,PATH_MAX);
449 if (!path || path[0] == '\0') {
450 #if 0
451 char *slash = NULL;
452 if (filename && !strstr(filename, "://") && (path=realpath(filename, b))) {
453 slash = strrchr(path, '/');
454 #if HAVE_DOS_PATHS
455 // FIXME: Do we need and can convert all '\\' in path to '/' on win32?
456 if (!slash)
457 slash = strrchr(path, '\\');
458 #endif
460 if (slash)
461 slash[1] = '\0';
462 else
463 #endif
464 path = wd;
466 if (path[0] != '/') {
467 if(path[strlen(path)-1] != '/')
468 snprintf(b,sizeof(b),"%s/%s/",wd,path);
469 else
470 snprintf(b,sizeof(b),"%s/%s",wd,path);
471 path = b;
472 } else if (path[strlen(path)-1]!='/') {
473 sprintf(b,"%s/",path);
474 path = b;
476 if (menu_chroot && menu_chroot[0] == '/') {
477 int l = strlen(menu_chroot);
478 if (l > 0 && menu_chroot[l-1] == '/')
479 --l;
480 if (strncmp(menu_chroot, path, l) || (path[l] != '\0' && path[l] != '/')) {
481 if (menu_chroot[l] == '/')
482 path = menu_chroot;
483 else {
484 sprintf(b,"%s/",menu_chroot);
485 path = b;
489 r = open_dir(menu,path);
491 return r;
494 const menu_info_t menu_info_filesel = {
495 "File seletor menu",
496 "filesel",
497 "Albeu",
500 "fs_cfg",
501 sizeof(struct menu_priv_s),
502 &cfg_dflt,
503 cfg_fields
505 open_fs