diff options
Diffstat (limited to 'sub/unrar_exec.c')
-rw-r--r-- | sub/unrar_exec.c | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/sub/unrar_exec.c b/sub/unrar_exec.c new file mode 100644 index 0000000000..6de8c59bef --- /dev/null +++ b/sub/unrar_exec.c @@ -0,0 +1,235 @@ +/* + * List files and extract file from rars by using external executable unrar. + * + * Copyright (C) 2005 Jindrich Makovicka <makovick gmail com> + * Copyright (C) 2007 Ulion <ulion2002 gmail com> + * + * 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 <sys/wait.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <locale.h> +#include "unrar_exec.h" + +#include "mp_msg.h" + +#define UNRAR_LIST 1 +#define UNRAR_EXTRACT 2 + +char* unrar_executable = NULL; + +static FILE* launch_pipe(pid_t *apid, const char *executable, int action, + const char *archive, const char *filename) +{ + if (!executable || access(executable, R_OK | X_OK)) return NULL; + if (access(archive, R_OK)) return NULL; + { + 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 unrar executable. */ + close(mypipe[0]); + // Close MPlayer's stdin, stdout and stderr so the unrar binary + // can not mess them up. + // TODO: Close all other files except the pipe. + close(0); close(1); close(2); + // Assign new stdin, stdout and stderr and check they actually got the + // right descriptors. + if (open("/dev/null", O_RDONLY) != 0 || dup(mypipe[1]) != 1 + || open("/dev/null", O_WRONLY) != 2) + _exit(EXIT_FAILURE); + if (action == UNRAR_LIST) + execl(executable, executable, "v", 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 v %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"); + } +} + +#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 = 0; + FILE *rar_pipe; + + rar_pipe=launch_pipe(&pid,unrar_executable,UNRAR_EXTRACT,rarfile,filename); + if (!rar_pipe) return 0; + + *size = 0; + + *output = malloc(bufsize); + + while (*output) { + bytesread=fread(*output+*size, 1, bufsize-*size, rar_pipe); + if (bytesread <= 0) + break; + *size += bytesread; + if (*size == bufsize) { + char *p; + bufsize += ALLOC_INCR; + p = realloc(*output, bufsize); + if (!p) + free(*output); + *output = p; + } + } + fclose(rar_pipe); + pid = waitpid(pid, &status, 0); + if (!*output || !*size || (pid == -1 && errno != ECHILD) || + (pid > 0 && 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; +} + +#define PARSE_NAME 0 +#define PARSE_PROPS 1 + +int unrar_exec_list(const char *rarfile, ArchiveList_struct **list) +{ + char buf[1024], fname[1024]; + char *p; + pid_t pid; + int status = 0, file_num = -1, ignore_next_line = 0, state = PARSE_NAME; + FILE *rar_pipe; + ArchiveList_struct *alist = NULL, *current = NULL, *new; + + rar_pipe = launch_pipe(&pid, unrar_executable, UNRAR_LIST, rarfile, NULL); + if (!rar_pipe) return -1; + while (fgets(buf, sizeof(buf), rar_pipe)) { + int packsize, unpsize, ratio, day, month, year, hour, min; + int llen = strlen(buf); + // If read nothing, we got a file_num -1. + if (file_num == -1) + file_num = 0; + if (buf[llen-1] != '\n') + // The line is too long, ignore it. + ignore_next_line = 2; + if (ignore_next_line) { + --ignore_next_line; + state = PARSE_NAME; + continue; + } + // Trim the line. + while (llen > 0 && strchr(" \t\n\r\v\f", buf[llen-1])) + --llen; + buf[llen] = '\0'; + p = buf; + while (*p && strchr(" \t\n\r\v\f", *p)) + ++p; + if (!*p) { + state = PARSE_NAME; + continue; + } + + if (state == PARSE_PROPS && sscanf(p, "%d %d %d%% %d-%d-%d %d:%d", + &unpsize, &packsize, &ratio, &day, + &month, &year, &hour, &min) == 8) { + new = 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); + state = PARSE_NAME; + if (!current->item.Name) { + file_num = -1; + break; + } + current->item.PackSize = packsize; + current->item.UnpSize = unpsize; + ++file_num; + continue; + } + strcpy(fname, p); + state = PARSE_PROPS; + } + fclose(rar_pipe); + pid = waitpid(pid, &status, 0); + if (file_num < 0 || (pid == -1 && errno != ECHILD) || + (pid > 0 && 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; + } +} |