mirror of https://github.com/mpv-player/mpv
488 lines
11 KiB
C
488 lines
11 KiB
C
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
|
|
|
|
#include "config.h"
|
|
#include "mp_msg.h"
|
|
#include "help_mp.h"
|
|
|
|
#include "m_struct.h"
|
|
#include "m_option.h"
|
|
|
|
#include "libmpcodecs/img_format.h"
|
|
#include "libmpcodecs/mp_image.h"
|
|
|
|
#include "menu.h"
|
|
#include "menu_list.h"
|
|
#include "input/input.h"
|
|
#include "osdep/keycodes.h"
|
|
|
|
#define MENU_KEEP_PATH "/tmp/mp_current_path"
|
|
|
|
int menu_keepdir = 0;
|
|
char *menu_chroot = NULL;
|
|
extern char *filename;
|
|
|
|
struct list_entry_s {
|
|
struct list_entry p;
|
|
int d;
|
|
};
|
|
|
|
struct menu_priv_s {
|
|
menu_list_priv_t p;
|
|
char* dir; // current dir
|
|
/// Cfg fields
|
|
char* path;
|
|
char* title;
|
|
char* file_action;
|
|
char* dir_action;
|
|
char** actions;
|
|
char* filter;
|
|
};
|
|
|
|
static struct menu_priv_s cfg_dflt = {
|
|
MENU_LIST_PRIV_DFLT,
|
|
NULL,
|
|
|
|
NULL,
|
|
"Select a file: %p",
|
|
"loadfile '%p'",
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
#define ST_OFF(m) M_ST_OFF(struct menu_priv_s,m)
|
|
|
|
static m_option_t cfg_fields[] = {
|
|
MENU_LIST_PRIV_FIELDS,
|
|
{ "path", ST_OFF(path), CONF_TYPE_STRING, 0, 0, 0, NULL },
|
|
{ "title", ST_OFF(title), CONF_TYPE_STRING, 0, 0, 0, NULL },
|
|
{ "file-action", ST_OFF(file_action), CONF_TYPE_STRING, 0, 0, 0, NULL },
|
|
{ "dir-action", ST_OFF(dir_action), CONF_TYPE_STRING, 0, 0, 0, NULL },
|
|
{ "actions", ST_OFF(actions), CONF_TYPE_STRING_LIST, 0, 0, 0, NULL},
|
|
{ "filter", ST_OFF(filter), CONF_TYPE_STRING, 0, 0, 0, NULL},
|
|
{ NULL, NULL, NULL, 0,0,0,NULL }
|
|
};
|
|
|
|
#define mpriv (menu->priv)
|
|
|
|
static void free_entry(list_entry_t* entry) {
|
|
free(entry->p.txt);
|
|
free(entry);
|
|
}
|
|
|
|
static char* replace_path(char* title , char* dir) {
|
|
char *p = strstr(title,"%p");
|
|
if(p) {
|
|
int tl = strlen(title);
|
|
int dl = strlen(dir);
|
|
int t1l = p-title;
|
|
int l = tl - 2 + dl;
|
|
char *r, *n, *d = dir;
|
|
char term = *(p-1);
|
|
|
|
do {
|
|
if (*d == '\\' || *d == term)
|
|
l++;
|
|
} while (*d++);
|
|
r = malloc(l + 1);
|
|
n = r + t1l;
|
|
memcpy(r,title,t1l);
|
|
do {
|
|
if (*dir == '\\' || *dir == term)
|
|
*n++ = '\\';
|
|
} while ((*n++ = *dir++));
|
|
if(tl - t1l - 2 > 0)
|
|
strcpy(n-1,p+2);
|
|
return r;
|
|
} else
|
|
return title;
|
|
}
|
|
|
|
typedef int (*kill_warn)(const void*, const void*);
|
|
|
|
static int mylstat(char *dir, char *file,struct stat* st) {
|
|
int l = strlen(dir) + strlen(file);
|
|
char s[l+2];
|
|
if (!strcmp("..", file)) {
|
|
char *slash;
|
|
l -= 3;
|
|
strcpy(s, dir);
|
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
|
if (s[l] == '/' || s[l] == '\\')
|
|
#else
|
|
if (s[l] == '/')
|
|
#endif
|
|
s[l] = '\0';
|
|
slash = strrchr(s, '/');
|
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
|
if (!slash)
|
|
slash = strrchr(s,'\\');
|
|
#endif
|
|
if (!slash)
|
|
return stat(dir,st);
|
|
slash[1] = '\0';
|
|
return stat(s,st);
|
|
}
|
|
sprintf(s,"%s/%s",dir,file);
|
|
return stat(s,st);
|
|
}
|
|
|
|
static int compare(char **a, char **b){
|
|
if((*a)[strlen(*a) - 1] == '/') {
|
|
if((*b)[strlen(*b) - 1] == '/')
|
|
return strcmp(*b, *a) ;
|
|
else
|
|
return 1;
|
|
} else {
|
|
if((*b)[strlen(*b) - 1] == '/')
|
|
return -1;
|
|
else
|
|
return strcmp(*b, *a);
|
|
}
|
|
}
|
|
|
|
static char **get_extensions(menu_t *menu){
|
|
char **extensions, ext[32];
|
|
FILE *fp;
|
|
int n = 1;
|
|
|
|
if (!mpriv->filter)
|
|
return NULL;
|
|
|
|
fp = fopen(mpriv->filter, "r");
|
|
if(!fp)
|
|
return NULL;
|
|
|
|
extensions = (char **) malloc(sizeof(*extensions));
|
|
*extensions = NULL;
|
|
|
|
while(fgets(ext,sizeof(ext),fp)) {
|
|
char **l, *e;
|
|
int s = strlen (ext);
|
|
|
|
if(ext[s-1] == '\n') {
|
|
ext[s-1] = '\0';
|
|
s--;
|
|
}
|
|
e = (char *) malloc(s+1);
|
|
extensions = (char **) realloc(extensions, ++n * sizeof(*extensions));
|
|
extensions = (char **) realloc(extensions, ++n * sizeof(*extensions));
|
|
strcpy (e, ext);
|
|
for (l=extensions; *l; l++);
|
|
*l++ = e;
|
|
*l = NULL;
|
|
}
|
|
|
|
fclose (fp);
|
|
return extensions;
|
|
}
|
|
|
|
static void free_extensions(char **extensions){
|
|
if (extensions) {
|
|
char **l = extensions;
|
|
while (*l)
|
|
free (*l++);
|
|
free (extensions);
|
|
}
|
|
}
|
|
|
|
static int open_dir(menu_t* menu,char* args) {
|
|
char **namelist, **tp;
|
|
struct dirent *dp;
|
|
struct stat st;
|
|
int n;
|
|
int path_fp;
|
|
char* p = NULL;
|
|
list_entry_t* e;
|
|
DIR* dirp;
|
|
extern int file_filter;
|
|
char **extensions, **elem, *ext;
|
|
|
|
menu_list_init(menu);
|
|
|
|
if(mpriv->dir)
|
|
free(mpriv->dir);
|
|
mpriv->dir = strdup(args);
|
|
if(mpriv->p.title && mpriv->p.title != mpriv->title && mpriv->p.title != cfg_dflt.p.title)
|
|
free(mpriv->p.title);
|
|
p = strstr(mpriv->title,"%p");
|
|
|
|
mpriv->p.title = replace_path(mpriv->title,mpriv->dir);
|
|
|
|
if ((dirp = opendir (mpriv->dir)) == NULL){
|
|
mp_msg(MSGT_GLOBAL,MSGL_ERR,MSGTR_LIBMENU_OpendirError, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
if (menu_keepdir) {
|
|
path_fp = open (MENU_KEEP_PATH, O_CREAT | O_WRONLY | O_TRUNC, 0666);
|
|
if (path_fp >= 0) {
|
|
write (path_fp, mpriv->dir, strlen (mpriv->dir));
|
|
close (path_fp);
|
|
}
|
|
}
|
|
|
|
namelist = (char **) malloc(sizeof(char *));
|
|
extensions = get_extensions(menu);
|
|
|
|
n=0;
|
|
while ((dp = readdir(dirp)) != NULL) {
|
|
if(dp->d_name[0] == '.' && strcmp(dp->d_name,"..") != 0)
|
|
continue;
|
|
if (menu_chroot && !strcmp (dp->d_name,"..")) {
|
|
int len = strlen (menu_chroot);
|
|
if ((strlen (mpriv->dir) == len || strlen (mpriv->dir) == len + 1)
|
|
&& !strncmp (mpriv->dir, menu_chroot, len))
|
|
continue;
|
|
}
|
|
if (mylstat(args,dp->d_name,&st))
|
|
continue;
|
|
if (file_filter && extensions && !S_ISDIR(st.st_mode)) {
|
|
if((ext = strrchr(dp->d_name,'.')) == NULL)
|
|
continue;
|
|
ext++;
|
|
elem = extensions;
|
|
do {
|
|
if (!strcasecmp(ext, *elem))
|
|
break;
|
|
} while (*++elem);
|
|
if (*elem == NULL)
|
|
continue;
|
|
}
|
|
if(n%20 == 0){ // Get some more mem
|
|
if((tp = (char **) realloc(namelist, (n+20) * sizeof (char *)))
|
|
== NULL) {
|
|
mp_msg(MSGT_GLOBAL,MSGL_ERR,MSGTR_LIBMENU_ReallocError, strerror(errno));
|
|
n--;
|
|
goto bailout;
|
|
}
|
|
namelist=tp;
|
|
}
|
|
|
|
namelist[n] = (char *) malloc(strlen(dp->d_name) + 2);
|
|
if(namelist[n] == NULL){
|
|
mp_msg(MSGT_GLOBAL,MSGL_ERR,MSGTR_LIBMENU_MallocError, strerror(errno));
|
|
n--;
|
|
goto bailout;
|
|
}
|
|
|
|
strcpy(namelist[n], dp->d_name);
|
|
if(S_ISDIR(st.st_mode))
|
|
strcat(namelist[n], "/");
|
|
n++;
|
|
}
|
|
|
|
bailout:
|
|
free_extensions (extensions);
|
|
closedir(dirp);
|
|
|
|
qsort(namelist, n, sizeof(char *), (kill_warn)compare);
|
|
|
|
if (n < 0) {
|
|
mp_msg(MSGT_GLOBAL,MSGL_ERR,MSGTR_LIBMENU_ReaddirError,strerror(errno));
|
|
return 0;
|
|
}
|
|
while(n--) {
|
|
if((e = calloc(1,sizeof(list_entry_t))) != NULL){
|
|
e->p.next = NULL;
|
|
e->p.txt = strdup(namelist[n]);
|
|
if(strchr(namelist[n], '/') != NULL)
|
|
e->d = 1;
|
|
menu_list_add_entry(menu,e);
|
|
}else{
|
|
mp_msg(MSGT_GLOBAL,MSGL_ERR,MSGTR_LIBMENU_MallocError, strerror(errno));
|
|
}
|
|
free(namelist[n]);
|
|
}
|
|
free(namelist);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static char *action;
|
|
|
|
static void read_cmd(menu_t* menu,int cmd) {
|
|
switch(cmd) {
|
|
case MENU_CMD_LEFT:
|
|
mpriv->p.current = mpriv->p.menu; // Hack : we consider that the first entry is ../
|
|
case MENU_CMD_RIGHT:
|
|
case MENU_CMD_OK: {
|
|
// Directory
|
|
if(mpriv->p.current->d && !mpriv->dir_action) {
|
|
// Default action : open this dirctory ourself
|
|
int l = strlen(mpriv->dir);
|
|
char *slash = NULL, *p = NULL;
|
|
if(strcmp(mpriv->p.current->p.txt,"../") == 0) {
|
|
if(l <= 1) break;
|
|
mpriv->dir[l-1] = '\0';
|
|
slash = strrchr(mpriv->dir,'/');
|
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
|
if (!slash)
|
|
slash = strrchr(mpriv->dir,'\\');
|
|
#endif
|
|
if(!slash) break;
|
|
slash[1] = '\0';
|
|
p = strdup(mpriv->dir);
|
|
} else {
|
|
p = malloc(l + strlen(mpriv->p.current->p.txt) + 1);
|
|
sprintf(p,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
|
|
}
|
|
menu_list_uninit(menu,free_entry);
|
|
if(!open_dir(menu,p)) {
|
|
mp_msg(MSGT_GLOBAL,MSGL_ERR,MSGTR_LIBMENU_CantOpenDirectory,p);
|
|
menu->cl = 1;
|
|
}
|
|
free(p);
|
|
} else { // File and directory dealt with action string.
|
|
int fname_len = strlen(mpriv->dir) + strlen(mpriv->p.current->p.txt) + 1;
|
|
char filename[fname_len];
|
|
char *str;
|
|
char *action = mpriv->p.current->d ? mpriv->dir_action:mpriv->file_action;
|
|
sprintf(filename,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
|
|
str = replace_path(action, filename);
|
|
mp_input_parse_and_queue_cmds(str);
|
|
if (str != action)
|
|
free(str);
|
|
}
|
|
} break;
|
|
case MENU_CMD_ACTION: {
|
|
int fname_len = strlen(mpriv->dir) + strlen(mpriv->p.current->p.txt) + 1;
|
|
char filename[fname_len];
|
|
char *str;
|
|
sprintf(filename,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
|
|
str = replace_path(action, filename);
|
|
mp_input_parse_and_queue_cmds(str);
|
|
if(str != action)
|
|
free(str);
|
|
} break;
|
|
default:
|
|
menu_list_read_cmd(menu,cmd);
|
|
}
|
|
}
|
|
|
|
static int read_key(menu_t* menu,int c){
|
|
char **str;
|
|
for (str=mpriv->actions; str && *str; str++)
|
|
if (c == (*str)[0]) {
|
|
action = &(*str)[2];
|
|
read_cmd(menu,MENU_CMD_ACTION);
|
|
return 1;
|
|
}
|
|
if (menu_dflt_read_key(menu, c))
|
|
return 1;
|
|
return menu_list_jump_to_key(menu, c);
|
|
}
|
|
|
|
static void clos(menu_t* menu) {
|
|
menu_list_uninit(menu,free_entry);
|
|
free(mpriv->dir);
|
|
}
|
|
|
|
static int open_fs(menu_t* menu, char* args) {
|
|
char *path = mpriv->path, *freepath = NULL;
|
|
int r = 0;
|
|
char wd[PATH_MAX+1], b[PATH_MAX+1];
|
|
args = NULL; // Warning kill
|
|
|
|
menu->draw = menu_list_draw;
|
|
menu->read_cmd = read_cmd;
|
|
menu->read_key = read_key;
|
|
menu->close = clos;
|
|
|
|
if (menu_keepdir) {
|
|
if (!path || path[0] == '\0') {
|
|
struct stat st;
|
|
int path_fp;
|
|
|
|
path_fp = open (MENU_KEEP_PATH, O_RDONLY);
|
|
if (path_fp >= 0) {
|
|
if (!fstat (path_fp, &st) && (st.st_size > 0)) {
|
|
path = malloc(st.st_size+1);
|
|
if ((read(path_fp, path, st.st_size) == st.st_size) && path[0] == '/'
|
|
&& !stat(path, &st) && S_ISDIR(st.st_mode)){
|
|
freepath = path;
|
|
path[st.st_size] = '\0';
|
|
}
|
|
else {
|
|
free(path);
|
|
path = NULL;
|
|
}
|
|
}
|
|
close (path_fp);
|
|
}
|
|
}
|
|
}
|
|
|
|
getcwd(wd,PATH_MAX);
|
|
if (!path || path[0] == '\0') {
|
|
char *slash = NULL;
|
|
if (filename && !strstr(filename, "://") && (path=realpath(filename, b))) {
|
|
slash = strrchr(path, '/');
|
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
|
// FIXME: Do we need and can convert all '\\' in path to '/' on win32?
|
|
if (!slash)
|
|
slash = strrchr(path, '\\');
|
|
#endif
|
|
}
|
|
if (slash)
|
|
slash[1] = '\0';
|
|
else
|
|
path = wd;
|
|
}
|
|
if (path[0] != '/') {
|
|
if(path[strlen(path)-1] != '/')
|
|
snprintf(b,sizeof(b),"%s/%s/",wd,path);
|
|
else
|
|
snprintf(b,sizeof(b),"%s/%s",wd,path);
|
|
path = b;
|
|
} else if (path[strlen(path)-1]!='/') {
|
|
sprintf(b,"%s/",path);
|
|
path = b;
|
|
}
|
|
if (menu_chroot && menu_chroot[0] == '/') {
|
|
int l = strlen(menu_chroot);
|
|
if (l > 0 && menu_chroot[l-1] == '/')
|
|
--l;
|
|
if (strncmp(menu_chroot, path, l) || (path[l] != '\0' && path[l] != '/')) {
|
|
if (menu_chroot[l] == '/')
|
|
path = menu_chroot;
|
|
else {
|
|
sprintf(b,"%s/",menu_chroot);
|
|
path = b;
|
|
}
|
|
}
|
|
}
|
|
r = open_dir(menu,path);
|
|
|
|
if (freepath)
|
|
free(freepath);
|
|
|
|
return r;
|
|
}
|
|
|
|
const menu_info_t menu_info_filesel = {
|
|
"File seletor menu",
|
|
"filesel",
|
|
"Albeu",
|
|
"",
|
|
{
|
|
"fs_cfg",
|
|
sizeof(struct menu_priv_s),
|
|
&cfg_dflt,
|
|
cfg_fields
|
|
},
|
|
open_fs
|
|
};
|