Index: Makefile =================================================================== --- Makefile (revision 25290) +++ Makefile (working copy) @@ -35,6 +35,7 @@ vobsub.c \ SRCS_COMMON-$(UNRARLIB) += unrarlib.c +SRCS_COMMON-$(UNRAR_EXEC) += unrar_exec.c SRCS_MPLAYER = mplayer.c \ m_property.c \ Index: cfg-mplayer.h =================================================================== --- cfg-mplayer.h (revision 25290) +++ cfg-mplayer.h (working copy) @@ -35,6 +35,8 @@ extern int menu_flip_hebrew; extern int menu_fribidi_flip_commas; +extern char *unrar_executable; + extern int vo_zr_parseoption(const m_option_t* conf, char *opt, char * param); extern void vo_zr_revertoption(const m_option_t* opt,char* pram); @@ -267,6 +269,9 @@ // these should be moved to -common, and supported in MEncoder {"vobsub", &vobsub_name, CONF_TYPE_STRING, 0, 0, 0, NULL}, {"vobsubid", &vobsub_id, CONF_TYPE_INT, CONF_RANGE, 0, 31, NULL}, +#ifdef USE_UNRAR_EXEC + {"unrarexec", &unrar_executable, CONF_TYPE_STRING, 0, 0, 0, NULL}, +#endif {"sstep", &step_sec, CONF_TYPE_INT, CONF_MIN, 0, 0, NULL}, Index: configure =================================================================== --- configure (revision 25290) +++ configure (working copy) @@ -256,6 +256,7 @@ --disable-freetype disable FreeType 2 font rendering [autodetect] --disable-fontconfig disable fontconfig font lookup [autodetect] --disable-unrarlib disable Unique RAR File Library [enabled] + --disable-unrarexec disable using of UnRAR executable [enabled] --enable-menu enable OSD menu (not DVD menu) [disabled] --disable-sortsub disable subtitle sorting [enabled] --enable-fribidi enable the FriBiDi libs [autodetect] @@ -591,6 +592,7 @@ _alsa=auto _fastmemcpy=yes _unrarlib=yes +_unrar_exec=yes _win32dll=auto _select=yes _radio=no @@ -1094,6 +1096,8 @@ --disable-fontconfig) _fontconfig=no ;; --enable-unrarlib) _unrarlib=yes ;; --disable-unrarlib) _unrarlib=no ;; + --enable-unrarexec) _unrar_exec=yes ;; + --disable-unrarexec) _unrar_exec=no ;; --enable-ftp) _ftp=yes ;; --disable-ftp) _ftp=no ;; --enable-vstream) _vstream=yes ;; @@ -6694,6 +6699,14 @@ fi echores "$_unrarlib" +echocheck "UnRAR executable" +if test "$_unrar_exec" = yes ; then + _def_unrar_exec='#define USE_UNRAR_EXEC 1' +else + _def_unrar_exec='#undef USE_UNRAR_EXEC' +fi +echores "$_unrar_exec" + echocheck "TV interface" if test "$_tv" = yes ; then _def_tv='#define USE_TV 1' @@ -7589,6 +7603,7 @@ MUSEPACK = $_musepack UNRARLIB = $_unrarlib +UNRAR_EXEC = $_unrar_exec PNG = $_png JPEG = $_jpeg GIF = $_gif @@ -8143,6 +8158,9 @@ /* Use unrarlib for Vobsubs */ $_def_unrarlib +/* Use UnRAR executable for Vobsubs */ +$_def_unrar_exec + /* gui support, please do not edit this option */ $_def_gui $_def_gtk2_gui Index: unrar_exec.c =================================================================== --- unrar_exec.c (revision 0) +++ unrar_exec.c (revision 0) @@ -0,0 +1,296 @@ +/* + * List files and extract file from rars by using external executable unrar. + * + * Copyright (C) 2005 Jindrich Makovicka + * Copyright (C) 2007 Ulion + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include "unrar_exec.h" + +#include "mp_msg.h" + +#ifdef WIN32 +#define S_IXGRP S_IXUSR +#define S_IXOTH S_IXUSR +#define IS_EXECUTABLE(MODE) (((MODE)&S_IXUSR)==S_IXUSR) +#define IS_REGULAR_FILE_OR_LINK(MODE) (((MODE)&S_IFREG)==S_IFREG) +#define my_pclose pclose +#define waitpid(a,b,c) (a) +#define IS_EXIT_OK(status) (1 || status) +#else +#define IS_EXECUTABLE(MODE) (((MODE)&S_IXUSR)==S_IXUSR || \ + ((MODE)&S_IXGRP)==S_IXGRP || \ + ((MODE)&S_IXOTH)==S_IXOTH) +#define IS_REGULAR_FILE_OR_LINK(MODE) (((MODE)&S_IFREG)==S_IFREG || \ + ((MODE)&S_IFLNK)==S_IFLNK) +#define my_pclose fclose +#define IS_EXIT_OK(status) (WIFEXITED(status) && !WEXITSTATUS(status)) +#endif + + +#define UNRAR_LIST 1 +#define UNRAR_EXTRACT 2 + +char* unrar_executable = NULL; + +static int file_readable(const char *fname) +{ + struct stat st; + return !stat(fname, &st) && IS_REGULAR_FILE_OR_LINK(st.st_mode); +} + +static int file_executable(const char *fname) +{ + struct stat st; + return !stat(fname, &st) && IS_REGULAR_FILE_OR_LINK(st.st_mode) + && IS_EXECUTABLE(st.st_mode); +} + +#ifdef WIN32 +static int cmd_escape_append(char *cmd, const char *parameter) +{ + char *p = cmd; + if (*parameter=='\0') + return 0; + while (*parameter) { + if (*parameter == ' ') { + *p++ = '"'; + while (*parameter==' ') + *p++ = *parameter++; + *p++ = '"'; + continue; + } + // ./executable_name is not recognised by cmd.exe without quotes. + // So we convert it to .\executable_name to fix this. + if (*parameter == '/') { + *p++ = '\\'; + ++parameter; + continue; + } + if (strchr("\"<>&|^@()", *parameter)) + *p++ = '^'; + *p++ = *parameter++; + } + *p++ = ' '; + *p = '\0'; + return p - cmd; +} +#endif + +static FILE* launch_pipe(pid_t *apid, const char *executable, int action, + const char *archive, const char *filename) +{ +#ifdef WIN32 + /* + * If not using popen, the only other way is to use CreateProcess. + * This need a cmdline as parameter also, so we use popen on win32 + * and escape the command line. + * On win32, popen equals 'cmd.exe /C cmdline'. + * Check the output of 'cmd.exe /?' to see how quotes on the command + * line are processed. + */ + int mlen = (strlen(executable)+strlen(archive)+filename?strlen(filename):0)*3+100; + char cmdline[mlen]; + cmdline[0] = '"'; + cmdline[1] = '\0'; + cmd_escape_append(cmdline + strlen(cmdline), executable); + if (action == UNRAR_LIST) + strcat(cmdline, "l "); + else if (action == UNRAR_EXTRACT) + strcat(cmdline, "p -inul -p- "); + else + return NULL; + cmd_escape_append(cmdline + strlen(cmdline), archive); + if (action == UNRAR_EXTRACT) + cmd_escape_append(cmdline + strlen(cmdline), filename); + strcat(cmdline, "\""); + mp_msg(MSGT_GLOBAL, MSGL_V, "UnRAR: call unrar with command line: %s\n", + cmdline); + *apid = 1; + return popen(cmdline, "rb"); +#else + int mypipe[2]; + pid_t pid; + + if (pipe(mypipe)) { + mp_msg(MSGT_GLOBAL, MSGL_ERR, "UnRAR: Cannot create pipe.\n"); + return NULL; + } + + pid = fork(); + if (pid == 0) { + /* This is the child process. Execute the shell command. */ + close(mypipe[0]); + dup2(mypipe[1], 1); + close(2); /* suppress stderr messages */ + if (action == UNRAR_LIST) + execl(executable, executable, "l", archive, NULL); + else if (action == UNRAR_EXTRACT) + execl(executable, executable, "p", "-inul", "-p-", + archive,filename,NULL); + mp_msg(MSGT_GLOBAL, MSGL_ERR, "UnRAR: Cannot execute %s\n", executable); + _exit(EXIT_FAILURE); + } + if (pid < 0) { + /* The fork failed. Report failure. */ + mp_msg(MSGT_GLOBAL, MSGL_ERR, "UnRAR: Fork failed\n"); + return NULL; + } + /* This is the parent process. Prepare the pipe stream. */ + close(mypipe[1]); + *apid = pid; + if (action == UNRAR_LIST) + mp_msg(MSGT_GLOBAL, MSGL_V, + "UnRAR: call unrar with command line: %s l %s\n", + executable, archive); + else if (action == UNRAR_EXTRACT) + mp_msg(MSGT_GLOBAL, MSGL_V, + "UnRAR: call unrar with command line: %s p -inul -p- %s %s\n", + executable, archive, filename); + return fdopen(mypipe[0], "r"); +#endif +} + +#define ALLOC_INCR 1 * 1024 * 1024 +int unrar_exec_get(unsigned char **output, unsigned long *size, + const char *filename, const char *rarfile) +{ + int bufsize = ALLOC_INCR, bytesread; + pid_t pid; + int status = -1; + FILE *rar_pipe; + + if (!unrar_executable || !file_executable(unrar_executable)) return 0; + if (!file_readable(rarfile)) return 0; + + rar_pipe=launch_pipe(&pid,unrar_executable,UNRAR_EXTRACT,rarfile,filename); + if (!rar_pipe) return 0; + + *size = 0; + + *output = (char*)malloc(bufsize); + if (!*output) goto byebye; + + while ((bytesread=fread(*output+*size, 1, bufsize-*size, rar_pipe)) > 0) { + *size += bytesread; + if (bytesread == ALLOC_INCR) { + char *p; + bufsize += ALLOC_INCR; + p = (char*)realloc(*output, bufsize); + if (!p) { + free(*output); + *output = NULL; + goto byebye; + } + *output = p; + } + } +byebye: + my_pclose(rar_pipe); + pid = waitpid(pid, &status, 0); + if (!*output || !*size || (pid == -1 && errno != ECHILD) || + (pid > 0 && !IS_EXIT_OK(status))) { + free(*output); + *output = NULL; + *size = 0; + return 0; + } + if (bufsize > *size) { + char *p = realloc(*output, *size); + if (p) + *output = p; + } + mp_msg(MSGT_GLOBAL, MSGL_V, "UnRAR: got file %s len %lu\n", filename,*size); + return 1; +} + +int unrar_exec_list(const char *rarfile, ArchiveList_struct **list) +{ + char buf[1024]; + pid_t pid; + int status = -1, file_num = 0; + FILE *rar_pipe; + ArchiveList_struct *alist = NULL, *current = NULL, *new; + + if (!unrar_executable || !file_executable(unrar_executable)) return -1; + if (!file_readable(rarfile)) return -1; + + rar_pipe = launch_pipe(&pid, unrar_executable, UNRAR_LIST, rarfile, NULL); + if (!rar_pipe) return -1; + while (fgets(buf, 1024, rar_pipe)) { + char fname[1024]; + int packsize, unpsize, ratio; + int day, month, year, hour, min; + + if (sscanf(buf, "%1023s %d %d %d%% %d-%d-%d %d:%d", + fname, &unpsize, &packsize, &ratio, + &day, &month, &year, &hour, &min) == 9) { + new = (ArchiveList_struct *)calloc(1, sizeof(ArchiveList_struct)); + if (!new) { + file_num = -1; + break; + } + if (!current) + alist = new; + else + current->next = new; + current = new; + current->item.Name = strdup(fname); + if (!current->item.Name) { + file_num = -1; + break; + } + current->item.PackSize = packsize; + current->item.UnpSize = unpsize; + ++file_num; + } + } + my_pclose(rar_pipe); + pid = waitpid(pid, &status, 0); + if (file_num < 0 || (pid == -1 && errno != ECHILD) || + (pid > 0 && !IS_EXIT_OK(status))) { + unrar_exec_freelist(alist); + return -1; + } + if (!alist) + return -1; + *list = alist; + mp_msg(MSGT_GLOBAL, MSGL_V, "UnRAR: list got %d files\n", file_num); + return file_num; +} + +void unrar_exec_freelist(ArchiveList_struct *list) +{ + ArchiveList_struct* tmp; + + while (list) { + tmp = list->next; + free(list->item.Name); + free(list); + list = tmp; + } +} + Index: unrar_exec.h =================================================================== --- unrar_exec.h (revision 0) +++ unrar_exec.h (revision 0) @@ -0,0 +1,38 @@ +/* + * List files and extract file from rars by using external executable unrar. + * + * Copyright (C) 2005 Jindrich Makovicka + * Copyright (C) 2007 Ulion + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef UNRAR_EXEC_H +#define UNRAR_EXEC_H + +#include "unrarlib.h" + +extern char* unrar_executable; + +int unrar_exec_get(unsigned char **output, unsigned long *size, + const char *filename, const char *rarfile); + +int unrar_exec_list(const char *rarfile, ArchiveList_struct **list); + +void unrar_exec_freelist(ArchiveList_struct *list); + +#endif /* UNRAR_EXEC_H */ Index: vobsub.c =================================================================== --- vobsub.c (revision 25290) +++ vobsub.c (working copy) @@ -20,7 +20,9 @@ #include "vobsub.h" #include "spudec.h" #include "mp_msg.h" -#ifdef USE_UNRARLIB +#ifdef USE_UNRAR_EXEC +#include "unrar_exec.h" +#elif defined(USE_UNRARLIB) #include "unrarlib.h" #endif #include "libavutil/common.h" @@ -35,7 +38,7 @@ * The RAR file must have the same basename as the file to open * See **********************************************************************/ -#ifdef USE_UNRARLIB +#if defined(USE_UNRARLIB) || defined(USE_UNRAR_EXEC) typedef struct { FILE *file; unsigned char *data; @@ -59,7 +62,7 @@ if (stream->file == NULL) { char *rar_filename; const char *p; - int rc; + int rc = 0; /* Guess the RAR archive filename */ rar_filename = NULL; p = strrchr(filename, '.'); @@ -88,15 +91,27 @@ } else { p++; } +#ifdef USE_UNRAR_EXEC + rc = unrar_exec_get(&stream->data, &stream->size, p, rar_filename); +#endif +#ifdef USE_UNRARLIB + if (!rc) rc = urarlib_get(&stream->data, &stream->size, (char*) p, rar_filename, ""); +#endif if (!rc) { /* There is no matching filename in the archive. However, sometimes * the files we are looking for have been given arbitrary names in the archive. * Let's look for a file with an exact match in the extension only. */ - int i, num_files, name_len; + int i, num_files = -1, name_len; ArchiveList_struct *list, *lp; +#ifdef USE_UNRARLIB /* the cast in the next line is a hack to overcome a design flaw (IMHO) in unrarlib */ num_files = urarlib_list (rar_filename, (ArchiveList_struct *)&list); +#endif +#ifdef USE_UNRAR_EXEC + if (num_files <= 0) + num_files = unrar_exec_list(rar_filename, &list); +#endif if (num_files > 0) { char *demanded_ext; demanded_ext = strrchr (p, '.'); @@ -105,13 +120,24 @@ for (i=0, lp=list; inext) { name_len = strlen (lp->item.Name); if (name_len >= demanded_ext_len && !strcasecmp (lp->item.Name + name_len - demanded_ext_len, demanded_ext)) { - if ((rc = urarlib_get(&stream->data, &stream->size, lp->item.Name, rar_filename, ""))) { - break; - } +#ifdef USE_UNRAR_EXEC + rc = unrar_exec_get(&stream->data, &stream->size, + lp->item.Name, rar_filename); + if (rc) break; +#endif +#ifdef USE_UNRARLIB + rc = urarlib_get(&stream->data, &stream->size, + lp->item.Name, rar_filename, ""); + if (rc) break; +#endif } } } +#ifdef USE_UNRARLIB urarlib_freelist (list); +#else + unrar_exec_freelist(list); +#endif } if (!rc) { free(rar_filename);