Index: unrar_exec.c =================================================================== --- unrar_exec.c (revision 25389) +++ unrar_exec.c (working copy) @@ -38,12 +38,182 @@ char* unrar_executable = NULL; +#ifdef __MINGW32__ +static int cmd_escape_append(char *cmd, int buflen, const char *parameter) +{ + if (*parameter == '\0') { + // Use empty quotes pair to express an empty parameter. + if (buflen < 4) + return -1; + *cmd++ = '"'; + *cmd++ = '"'; + *cmd++ = ' '; + *cmd = '\0'; + return 3; + } + else { + int rlen, ret = -1; + int plen = strlen(parameter); + wchar_t *wcsrc = NULL, *wcdst = NULL, *src, *dst; + char *ori_locale = NULL; + + if (buflen < plen + 1) + return -1; + + ori_locale = setlocale(LC_CTYPE, NULL); + if (ori_locale) + ori_locale = strdup(ori_locale); + // Make sure we're using system default codepage. + setlocale(LC_CTYPE, ""); + + rlen = mbstowcs(NULL, parameter, 0); + if (rlen < 0) { + mp_msg(MSGT_GLOBAL, MSGL_WARN, + "UnRAR: convert cmd parameter [%s] mbstowcs failed: %d\n", + parameter, rlen); + goto err_out; + } + wcsrc = calloc(rlen + 1, sizeof(wchar_t)); + if (!wcsrc) + goto err_out; + ret = mbstowcs(wcsrc, parameter, rlen); + if (ret < 0) { + mp_msg(MSGT_GLOBAL, MSGL_WARN, + "UnRAR: convert cmd parameter [%s] mbstowcs failed: %d,%d\n", + parameter, ret, rlen); + goto err_out; + } + ret = -1; + wcdst = malloc(rlen * 3 + 2); + if (!wcdst) + goto err_out; + src = wcsrc; + dst = wcdst; + while (*src) { + // ./executable_name is not recognized by cmd.exe without quotes. + // So we convert it to .\executable_name to fix this. + if (*src == L'/') { + *dst++ = L'\\'; + ++src; + continue; + } + // Spaces need be quoted. + if (wcschr(L" \t\r\n", *src)) { + // Need to count how many continual '\' before current position. + // If count '\' is N, then we write additional N '\'. + // Then we have 2N '\' followed by a quote, that will result + // N '\' and following quote works as it is not '\' before it. + const wchar_t *q = dst - 1; + while (q >= wcdst && *q-- == L'\\') + *dst++ = L'\\'; + *dst++ = L'"'; + while (*src && wcschr(L" \t\r\n", *src)) + *dst++ = *src++; + *dst++ = L'"'; + continue; + } + // '"' has special escape char, it can only be escaped by '\' + if (*src == L'"') { + // Need to count how many continual '\' before current position. + // If count '\' is N, then we write additional N+1 '\'. + // Then we have 2N + 1 '\' followed by a quote, that will result + // N '\' followed by a quote. + const wchar_t *q = dst - 1; + while (q >= wcdst && *q-- == L'\\') + *dst++ = L'\\'; + *dst++ = L'\\'; + } + else if (wcschr(L"&()[]{}^=;!'+,`~<>@|%#$*?", *src)) + *dst++ = L'^'; + *dst++ = *src++; + } + *dst++ = L' '; + *dst = L'\0'; + rlen = wcstombs(NULL, wcdst, 0); + if (rlen < 0 || rlen + 1 >= buflen) { + mp_msg(MSGT_GLOBAL, MSGL_WARN, + "UnRAR: convert cmd param [%s][%S] wcstombs failed:%d,%d\n", + parameter, wcdst, rlen, buflen); + goto err_out; + } + ret = wcstombs(cmd, wcdst, buflen - 1); + if (ret < 0) { + mp_msg(MSGT_GLOBAL, MSGL_WARN, + "UnRAR: convert cmd param [%s][%S] wcstombs fail:%d,%d,%d\n", + parameter, wcdst, ret, rlen, buflen); + goto err_out; + } + cmd[ret] = '\0'; +err_out: + free(wcsrc); + free(wcdst); + if (ori_locale) { + setlocale(LC_CTYPE, ori_locale); + free(ori_locale); + } + return ret; + } +} +#endif + 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; { +#ifdef __MINGW32__ + /* + * 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. + */ + static const char list_cmd[] = "v "; + static const char extract_cmd[] = "p -inul -p- "; + int mlen = (strlen(executable) + strlen(archive) + + (filename ? strlen(filename) : 0)) * 3 + 100; + char cmdline[mlen]; + int rlen, clen = 0; + cmdline[clen++] = '"'; + cmdline[clen] = '\0'; + rlen = cmd_escape_append(cmdline + clen, mlen - clen, executable); + if (rlen < 0) + return NULL; + clen += rlen; + if (action == UNRAR_LIST) { + if (clen + sizeof(list_cmd) >= mlen) + return NULL; + strcpy(cmdline + clen, list_cmd); + clen += sizeof(list_cmd) - 1; + } + else if (action == UNRAR_EXTRACT) { + if (clen + sizeof(extract_cmd) >= mlen) + return NULL; + strcpy(cmdline + clen, extract_cmd); + clen += sizeof(extract_cmd) - 1; + } + else + return NULL; + rlen = cmd_escape_append(cmdline + clen, mlen - clen, archive); + if (rlen < 0) + return NULL; + clen += rlen; + if (action == UNRAR_EXTRACT) { + rlen = cmd_escape_append(cmdline + clen, mlen - clen, filename); + if (rlen < 0) + return NULL; + clen += rlen; + } + cmdline[clen++] = '\"'; + cmdline[clen] = '\0'; + 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; @@ -90,6 +260,7 @@ "UnRAR: call unrar with command line: %s p -inul -p- %s %s\n", executable, archive, filename); return fdopen(mypipe[0], "r"); +#endif } } @@ -123,8 +294,12 @@ *output = p; } } +#ifdef __MINGW32__ + pclose(rar_pipe); +#else fclose(rar_pipe); pid = waitpid(pid, &status, 0); +#endif if (!*output || !*size || (pid == -1 && errno != ECHILD) || (pid > 0 && status)) { free(*output); @@ -208,8 +383,12 @@ strcpy(fname, p); state = PARSE_PROPS; } +#ifdef __MINGW32__ + pclose(rar_pipe); +#else fclose(rar_pipe); pid = waitpid(pid, &status, 0); +#endif if (file_num < 0 || (pid == -1 && errno != ECHILD) || (pid > 0 && status)) { unrar_exec_freelist(alist);