From 875be8166b919a05395f1754da3678a18758fab8 Mon Sep 17 00:00:00 2001 From: Akira Kakuto Date: Tue, 2 Aug 2011 09:34:31 +0000 Subject: win32lib.c and win32lib.h git-svn-id: svn://tug.org/texlive/trunk@23313 c570f23f-e606-0410-a88d-b1316a301751 --- Build/source/texk/kpathsea/win32lib.c | 1304 ++++++++++++++++++++++++++++++++- Build/source/texk/kpathsea/win32lib.h | 253 ++++++- 2 files changed, 1548 insertions(+), 9 deletions(-) (limited to 'Build/source') diff --git a/Build/source/texk/kpathsea/win32lib.c b/Build/source/texk/kpathsea/win32lib.c index 5d4cb7c546d..a7321e3b7ab 100644 --- a/Build/source/texk/kpathsea/win32lib.c +++ b/Build/source/texk/kpathsea/win32lib.c @@ -1,7 +1,7 @@ /* win32lib.c: bits and pieces for win32 and msvc. - Copyright 1996, xxxx Fabrice Popineau. - Copyright xxxx, 2011 Akira Kakuto. + Copyright 1996, 1997. 1998 Fabrice Popineau. + Copyright 2006, 2011 Akira Kakuto. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -17,8 +17,1306 @@ along with this library; if not, see . */ #include +#include +#include +FILE * __cdecl kpathsea_win32_popen (kpathsea kpse, char *cmd, char *mode) +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; + FILE *f = NULL; + int fno, i; + HANDLE child_in, child_out; + HANDLE father_in, father_out; + HANDLE father_in_dup, father_out_dup; + HANDLE current_in, current_out; + HANDLE current_pid; + char *env_path; + int binary_mode; + char *new_cmd, *app_name = NULL; + char *p, *q; + struct _popen_elt *new_process; + char pname[MAXPATHLEN], *fp; + char *suffixes[] = { ".bat", ".cmd", ".com", ".exe", NULL }; + char **s; + BOOL go_on; + + /* We should look for the application name along the PATH, + and decide to prepend "%COMSPEC% /c " or not to the command line. + Do nothing for the moment. */ + + /* Another way to do that would be to try CreateProcess first without + invoking cmd, and look at the error code. If it fails because of + command not found, try to prepend "cmd /c" to the cmd line. + */ + /* Look for the application name */ + for (p = (char *)cmd; *p && isspace(*p); p++); + if (*p == '"') { + q = ++p; + while(*p && *p != '"') p++; + if (*p == '\0') { + fprintf(stderr, "popen: malformed command (\" not terminated)\n"); + return NULL; + } + } + else + for (q = p; *p && !isspace(*p); p++); + /* q points to the beginning of appname, p to the last + 1 char */ + if ((app_name = malloc(p - q + 1)) == NULL) { + fprintf(stderr, "xpopen: malloc(app_name) failed.\n"); + return NULL; + } + strncpy(app_name, q, p - q ); + app_name[p - q] = '\0'; + pname[0] = '\0'; +#ifdef TRACE + fprintf(stderr, "popen: app_name = %s\n", app_name); +#endif + + { + char *tmp = getenv("PATH"); + env_path = xmalloc(strlen(tmp) + 3); + strcpy(env_path, tmp); + strcat(env_path, ";."); + } + + /* Looking for appname on the path */ + for (s = suffixes, go_on = TRUE; go_on; *s++) { + if (SearchPath(env_path, /* Address of search path */ + app_name, /* Address of filename */ + *s, /* Address of extension */ + MAXPATHLEN, /* Size of destination buffer */ + pname, /* Address of destination buffer */ + &fp) /* File part of app_name */ + != 0 && *s) { /* added *s in order not to allow no suffix. + --ak 2009/10/24 */ +#ifdef TRACE + fprintf(stderr, "%s found with suffix %s\nin %s\n", app_name, *s, pname); +#endif + new_cmd = xstrdup(cmd); + free(app_name); + app_name = xstrdup(pname); + break; + } + go_on = (*s != NULL); + } + if (go_on == FALSE) { + /* the app_name was not found */ +#ifdef TRACE + fprintf(stderr, "%s not found, concatenating comspec\n", app_name); +#endif + if(_osver & 0x8000) + new_cmd = concatn("command.com", " /c ", cmd, NULL); + else + new_cmd = concatn("cmd.exe", " /c ", cmd, NULL); + free(app_name); + app_name = NULL; + } + if (env_path) free(env_path); + +#ifdef TRACE + fprintf(stderr, "popen: app_name = %s\n", app_name); + fprintf(stderr, "popen: cmd_line = %s\n", new_cmd); +#endif + current_in = GetStdHandle(STD_INPUT_HANDLE); + current_out = GetStdHandle(STD_OUTPUT_HANDLE); + current_pid = GetCurrentProcess(); + ZeroMemory( &si, sizeof(STARTUPINFO) ); + si.cb = sizeof(STARTUPINFO); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + + if (strchr(mode, 'b')) + binary_mode = _O_BINARY; + else + binary_mode = _O_TEXT; + + /* Opening the pipe for writing */ + if (strchr(mode, 'w')) { + binary_mode |= _O_WRONLY; + if (CreatePipe(&child_in, &father_out, &sa, 0) == FALSE) { + fprintf(stderr, "popen: error CreatePipe\n"); + return NULL; + } + + si.hStdInput = child_in; + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + if (DuplicateHandle(current_pid, father_out, + current_pid, &father_out_dup, + 0, FALSE, DUPLICATE_SAME_ACCESS) == FALSE) { + fprintf(stderr, "popen: error DuplicateHandle father_out\n"); + return NULL; + } + CloseHandle(father_out); + fno = _open_osfhandle((long)father_out_dup, binary_mode); + f = _fdopen(fno, mode); + i = setvbuf( f, NULL, _IONBF, 0 ); + } + /* Opening the pipe for reading */ + else if (strchr(mode, 'r')) { + binary_mode |= _O_RDONLY; + if (CreatePipe(&father_in, &child_out, &sa, 0) == FALSE) { + fprintf(stderr, "popen: error CreatePipe\n"); + return NULL; + } + + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.hStdOutput = child_out; + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + if (DuplicateHandle(current_pid, father_in, + current_pid, &father_in_dup, + 0, FALSE, DUPLICATE_SAME_ACCESS) == FALSE) { + fprintf(stderr, "popen: error DuplicateHandle father_in\n"); + return NULL; + } + CloseHandle(father_in); + fno = _open_osfhandle((long)father_in_dup, binary_mode); + f = _fdopen(fno, mode); + i = setvbuf( f, NULL, _IONBF, 0 ); + } + else { + fprintf(stderr, "popen: invalid mode %s\n", mode); + return NULL; + } + + /* creating child process */ + if (CreateProcess(app_name, /* pointer to name of executable module */ + new_cmd, /* pointer to command line string */ + NULL, /* pointer to process security attributes */ + NULL, /* pointer to thread security attributes */ + TRUE, /* handle inheritance flag */ + 0, /* creation flags, do not touch this again ! (16/06/98) */ + NULL, /* pointer to environment */ + NULL, /* pointer to current directory */ + &si, /* pointer to STARTUPINFO */ + &pi /* pointer to PROCESS_INFORMATION */ + ) == FALSE) { + fprintf(stderr, "popen: CreateProcess %x\n", GetLastError()); + return NULL; + } + + /* Only the process handle is needed */ + if (CloseHandle(pi.hThread) == FALSE) { + fprintf(stderr, "popen: error closing thread handle\n"); + return NULL; + } + + if (new_cmd) free(new_cmd); + if (app_name) free(app_name); + + /* Add the pair (f, pi.hProcess) to the list */ + if ((new_process = malloc(sizeof(struct _popen_elt))) == NULL) { + fprintf (stderr, "popen: malloc(new_process) error\n"); + return NULL; + } + /* Saving the FILE * pointer, access key for retrieving the process + handle later on */ + new_process->f = f; + /* Closing the unnecessary part of the pipe */ + if (strchr(mode, 'r')) { + CloseHandle(child_out); + } + else if (strchr(mode, 'w')) { + CloseHandle(child_in); + } + /* Saving the process handle */ + new_process->hp = pi.hProcess; + /* Linking it to the list of popen() processes */ + new_process->next = kpse->_popen_list; + kpse->_popen_list = new_process; + + return f; + +} + +int __cdecl kpathsea_win32_pclose (kpathsea kpse, FILE *f) +{ + struct _popen_elt *p, *q; + int exit_code; + + /* Look for f is the access key in the linked list */ + for (q = NULL, p = kpse->_popen_list; + p != &kpse->_z_p_open && p->f != f; + q = p, p = p->next); + + if (p == &kpse->_z_p_open) { + fprintf(stderr, "pclose: error, file not found."); + return -1; + } + + /* Closing the FILE pointer */ + fclose(f); + + /* Waiting for the process to terminate */ + if (WaitForSingleObject(p->hp, INFINITE) != WAIT_OBJECT_0) { + fprintf(stderr, "pclose: error, process still active\n"); + return -1; + } + + /* retrieving the exit code */ + if (GetExitCodeProcess(p->hp, &exit_code) == 0) { + fprintf(stderr, "pclose: can't get process exit code\n"); + return -1; + } + + /* Closing the process handle, this will cause the system to + remove the process from memory */ + if (CloseHandle(p->hp) == FALSE) { + fprintf(stderr, "pclose: error closing process handle\n"); + return -1; + } + + /* remove the elt from the list */ + if (q != NULL) + q->next = p->next; + else + kpse->_popen_list = p->next; + free(p); + + return exit_code; +} + +/* large file support */ + +void +xfseek64 (FILE *f, __int64 offset, int wherefrom, char *filename) +{ + fflush(f); + if (_lseeki64(fileno(f), offset, wherefrom) < (__int64)0) + FATAL_PERROR(filename); +} + +__int64 +xftell64 (FILE *f, char *filename) +{ + __int64 where; + fflush(f); + where = _telli64(fileno(f)); + if (where < (__int64)0) + FATAL_PERROR(filename); + + return where; +} + +/* special TeXLive Ghostscript */ + +static int is_dir (char *buff) +{ + HANDLE h; + WIN32_FIND_DATA w32fd; + + if (((h = FindFirstFile (buff, &w32fd)) + != INVALID_HANDLE_VALUE) && + (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + FindClose (h); + return (1); + } else { + FindClose (h); + return (0); + } +} + +/* + TeXlive uses its own gs in + $SELFAUTOPARENT/tlpkg/tlgs +*/ +void texlive_gs_init(void) +{ + char *nptr, *path; + char tlgsbindir[512]; + char tlgslibdir[512]; + nptr = kpse_var_value("SELFAUTOPARENT"); + if (nptr) { + strcpy(tlgsbindir, nptr); + strcat(tlgsbindir,"/tlpkg/tlgs"); + if(is_dir(tlgsbindir)) { + strcpy(tlgslibdir, tlgsbindir); + strcat(tlgslibdir, "/lib;"); + strcat(tlgslibdir, tlgsbindir); + strcat(tlgslibdir, "/fonts"); + strcat(tlgsbindir, "/bin;"); + free(nptr); + for(nptr = tlgsbindir; *nptr; nptr++) { + if(*nptr == '/') *nptr = '\\'; + } + nptr = getenv("PATH"); + path = (char *)malloc(strlen(nptr) + strlen(tlgsbindir) + 6); + strcpy(path, tlgsbindir); + strcat(path, nptr); + xputenv("PATH", path); + xputenv("GS_LIB", tlgslibdir); + } + } +} + +/* + path name in char *input is changed into the long + name format and returned in char *buff. + return value: 0 if failed + 1 if succeeded +*/ + +int getlongpath(char *buff, char *input, int len) +{ + HANDLE hnd; + WIN32_FIND_DATA ffd; + int cnt = 0; + char *p, *q, *r; + + buff[0] = '\0'; +/* +temporarily change directory separators into back slashs +*/ + for(p = input; *p; p++) { + if(*p == '/') + *p = '\\'; + } + + p = q = input; + r = buff; + +/* +UNC name +*/ + if(q[0] == '\\' && q[1] == '\\') { + cnt += 2; + if(cnt > len) return 0; + buff[0] = '/'; + buff[1] = '/'; + p += 2; + r += 2; + while(*p != '\\' && *p) { + if (IS_KANJI(p)) { + cnt++; + if(cnt > len) return 0; + *r++ = *p++; + } + cnt++; + if(cnt > len) return 0; + *r++ = *p++; + } + cnt++; + if(cnt > len) return 0; + *r++ = '/'; + if(*p) p++; + while(*p != '\\' && *p) { + if (IS_KANJI(p)) { + cnt++; + if(cnt > len) return 0; + *r++ = *p++; + } + cnt++; + if(cnt > len) return 0; + *r++ = *p++; + } + cnt++; + if(cnt > len) return 0; + *r++ = '/'; + *r= '\0'; + if(*p) p++; /* - * To be filled in +drive name +*/ + } else if(isalpha(q[0]) && q[1] == ':' && q[2] == '\\') { + *r++ = q[0]; + *r++ = ':'; + *r++ = '/'; + *r = '\0'; + p += 3; + cnt += 3; + if(cnt > len) return 0; + } + + for( ; *p; p++) { + if(IS_KANJI(p)) { + p++; + continue; + } + if(*p == '\\') { + *p = '\0'; + if((hnd = FindFirstFile(q, &ffd)) == INVALID_HANDLE_VALUE) { + return 0; + } + FindClose(hnd); + cnt += strlen(ffd.cFileName); + cnt++; + if(cnt > len) return 0; + strcat(buff, ffd.cFileName); + strcat(buff, "/"); + *p = '\\'; + } + } + +/* +file itself +*/ + if((hnd = FindFirstFile(q, &ffd)) == INVALID_HANDLE_VALUE) { + return 0; + } + FindClose(hnd); + cnt += strlen(ffd.cFileName); + if(cnt > len) return 0; + strcat(buff, ffd.cFileName); + return 1; +} + +/* user info */ + +/* Adapted for XEmacs by David Hobley */ +/* Sync'ed with Emacs 19.34.6 by Marc Paquette */ +/* Adapted to fpTeX 0.4 by Fabrice Popineau */ + +char * get_home_directory() +{ + char *p; + char *home = getenv("HOME"); + if(!home) + home = getenv("USERPROFILE"); + if(home) { + home = xstrdup(home); + for(p = home; *p; p++) { + if(IS_KANJI(p)) { + p++; + continue; + } + if(*p == '\\') + *p = '/'; + } + } + return home; +} + +int +kpathsea_getuid (kpathsea kpse) +{ + return kpse->the_passwd.pw_uid; +} + +int +kpathsea_getgid (kpathsea kpse) +{ + return kpse->the_passwd.pw_gid; +} + +struct passwd * +kpathsea_getpwuid (kpathsea kpse, int uid) +{ + if (uid == kpse->the_passwd.pw_uid) + return &kpse->the_passwd; + return NULL; +} + +struct passwd * +kpathsea_getpwnam (kpathsea kpse, char *name) +{ + struct passwd *pw; + + pw = kpathsea_getpwuid (kpse, kpathsea_getuid (kpse)); + if (!pw) + return pw; + + if (stricmp (name, pw->pw_name)) + return NULL; + + return pw; +} + +void +kpathsea_init_user_info (kpathsea kpse) +{ + char *home; + DWORD nSize = 256; + + if (!GetUserName (kpse->the_passwd.pw_name, &nSize)) + strcpy (kpse->the_passwd.pw_name, "unknown"); + kpse->the_passwd.pw_uid = 123; + kpse->the_passwd.pw_gid = 123; + + /* Ensure HOME and SHELL are defined. */ + + home = get_home_directory(); + if (home) { + putenv(concat("HOME=", home)); + } + else { + putenv ("HOME=c:/"); + } + + if (getenv ("SHELL") == NULL) + putenv ((GetVersion () & 0x80000000) ? "SHELL=command" : "SHELL=cmd"); + + { + /* If TEXMFTEMP is defined, then use it as the TEMP and TMP variables. */ + char *p; + if ((p = getenv("TEXMFTEMP")) != NULL) { + putenv(concat("TEMP=", p)); + putenv(concat("TMP=", p)); + } + } + + /* Set dir and shell from environment variables. */ + strcpy (kpse->the_passwd.pw_dir, get_home_directory()); + strcpy (kpse->the_passwd.pw_shell, getenv ("SHELL")); +} + +/* lookcmd */ + +BOOL +look_for_cmd(const char *cmd, char **app, char **new) +{ + char *env_path; + char *p, *q; + char pname[MAXPATHLEN], *fp; + char *suffixes[] = { ".bat", ".cmd", ".com", ".exe", NULL }; + char **s; + char *app_name, *new_cmd; + + BOOL go_on; + + *new = *app = NULL; + new_cmd = app_name = NULL; + + /* We should look for the application name along the PATH, + and decide to prepend "%COMSPEC% /c " or not to the command line. + Do nothing for the moment. */ + + /* Another way to do that would be to try CreateProcess first without + invoking cmd, and look at the error code. If it fails because of + command not found, try to prepend "cmd /c" to the cmd line. + */ + + /* Look for the application name */ + for (p = (char *)cmd; *p && isspace(*p); p++); + if (*p == '"') { + q = ++p; + while(*p && *p != '"') p++; + if (*p == '\0') { + fprintf(stderr, "Look_for_cmd: malformed command (\" not terminated)\n"); + return FALSE; + } + } + else + for (q = p; *p && !isspace(*p); p++); + /* q points to the beginning of appname, p to the last + 1 char */ + if ((app_name = malloc(p - q + 1)) == NULL) { + fprintf(stderr, "Look_for_cmd: malloc(app_name) failed.\n"); + return FALSE; + } + strncpy(app_name, q, p - q ); + app_name[p - q] = '\0'; + pname[0] = '\0'; +#ifdef TRACE + fprintf(stderr, "popen: app_name = %s\n", app_name); +#endif + + { + char *tmp = getenv("PATH"); + env_path = xmalloc(strlen(tmp) + 3); + strcpy(env_path, tmp); + strcat(env_path, ";."); + } + + /* Looking for appname on the path */ + for (s = suffixes, go_on = TRUE; go_on; *s++) { + if (SearchPath(env_path, /* Address of search path */ + app_name, /* Address of filename */ + *s, /* Address of extension */ + MAXPATHLEN, /* Size of destination buffer */ + pname, /* Address of destination buffer */ + &fp) /* File part of app_name */ + != 0 && *s) { /* added *s in order not to allow no suffix. + --ak 2009/07/08 */ +#ifdef TRACE + fprintf(stderr, "%s found with suffix %s\nin %s\n", app_name, *s, pname); +#endif + new_cmd = xstrdup(cmd); + free(app_name); + app_name = xstrdup(pname); + break; + } + go_on = (*s != NULL); + } + if (go_on == FALSE) { + /* the app_name was not found */ +#ifdef TRACE + fprintf(stderr, "%s not found, concatenating comspec\n", app_name); +#endif + if(_osver & 0x8000) + new_cmd = concatn("command.com", " /c ", cmd, NULL); + else + new_cmd = concatn("cmd.exe", " /c ", cmd, NULL); + free(app_name); + app_name = NULL; + } + if (env_path) free(env_path); + + *new = new_cmd; + *app = app_name; + + return TRUE; + +} + +/* + Command parser. Borrowed from DJGPP. */ +typedef enum { + EMPTY, + WORDARG, + REDIR_INPUT, + REDIR_APPEND, + REDIR_OUTPUT, + PIPE, + SEMICOLON, + UNMATCHED_QUOTE, + EOL +} cmd_sym_t; + +/* Return a copy of a word between BEG and (excluding) END with all + quoting characters removed from it. */ + +static char * +kpathsea__unquote (kpathsea kpse, char *to, const char *beg, const char *end) +{ + const char *s = beg; + char *d = to; + int quote = 0; + + while (s < end) + { + switch (*s) + { + case '"': + case '\'': + if (!quote) + quote = *s; + else if (quote == *s) + quote = 0; + s++; + break; + case '\\': + if (s[1] == '"' || s[1] == '\'' + || (s[1] == ';' + && (kpse->__system_allow_multiple_cmds))) + s++; + /* Fall-through. */ + default: + *d++ = *s++; + break; + } + } + + *d = 0; + return to; +} + +/* A poor-man's lexical analyzer for simplified command processing. + + It only knows about these: + + redirection and pipe symbols + semi-colon `;' (that possibly ends a command) + argument quoting rules with quotes and `\' + whitespace delimiters of words (except in quoted args) + + Returns the type of next symbol and pointers to its first and (one + after) the last characters. + + Only `get_sym' and `unquote' should know about quoting rules. */ + +static cmd_sym_t +kpathsea_get_sym (kpathsea kpse, char *s, char **beg, char **end) +{ + int in_a_word = 0; + + while (isspace (*s)) + s++; + + *beg = s; + + do { + *end = s + 1; + + if (in_a_word + && (!*s || strchr ("<>| \t\n", *s) + || ((kpse->__system_allow_multiple_cmds) && *s == ';'))) + { + --*end; + return WORDARG; + } + + switch (*s) + { + case '<': + return REDIR_INPUT; + case '>': + if (**end == '>') + { + ++*end; + return REDIR_APPEND; + } + return REDIR_OUTPUT; + case '|': + return PIPE; + case ';': + if (kpse->__system_allow_multiple_cmds) + return SEMICOLON; + else + in_a_word = 1; + break; + case '\0': + --*end; + return EOL; + case '\\': + if (s[1] == '"' || s[1] == '\'' + || (s[1] == ';' && (kpse->__system_allow_multiple_cmds))) + s++; + in_a_word = 1; + break; + case '\'': + case '"': + { + char quote = *s++; + + while (*s && *s != quote) + { + if (*s++ == '\\' && (*s == '"' || *s == '\'')) + s++; + } + *end = s; + if (!*s) + return UNMATCHED_QUOTE; + in_a_word = 1; + break; + } + default: + in_a_word = 1; + break; + } + + s++; + + } while (1); +} + +/* + What we allow : + [cmd] [arg1] ... [argn] < [redinput] | [cmd2] | ... | [cmdn] > [redoutput] +*/ +void *kpathsea_parse_cmdline(kpathsea kpse, char *line, char **input, + char **output, int *is_cmd_pipe) +{ + BOOL again, needcmd = TRUE, bSuccess = TRUE, append_out = FALSE; + char *beg = line, *end, *new_end; + cmd_sym_t token = EMPTY, prev_token = EMPTY; + int ncmd = 0, narg = 1; + char **fp; + char ***cmd; + char *dummy_input; /* So that we could pass NULL */ + char *dummy_output; /* instead of a real ??put */ + + if (input == NULL) input = &dummy_input; + if (output == NULL) output = &dummy_output; + + *input = NULL; + *output = NULL; + cmd = xmalloc(MAX_PIPES*sizeof(char **)); + cmd[ncmd] = NULL; +#ifdef TRACE + fprintf(stderr, "line = %s\n", line); +#endif + do { + again = FALSE; + prev_token = token; + token = kpathsea_get_sym (kpse, beg, &beg, &end); /* get next symbol */ +#ifdef TRACE + fprintf(stderr, "token = %s\n", beg); +#endif + switch (token) { + case WORDARG: + if (prev_token == REDIR_INPUT + || prev_token == REDIR_OUTPUT) { + fprintf(stderr, "Ambigous input/output redirect."); + bSuccess = FALSE; + goto leave; + } + /* First word we see is the program to run. */ + if (needcmd) { + narg = 1; + cmd[ncmd] = xmalloc(narg * sizeof(char *)); + cmd[ncmd][narg - 1] = xmalloc(end - beg + 1); + kpathsea__unquote (kpse, cmd[ncmd][narg - 1], beg, end); + /* unquote and copy to prog */ + if (cmd[ncmd][narg - 1][0] == '(') { + fprintf(stderr, "parse_cmdline(%s): Parenthesized groups not allowed.\n", line); + bSuccess = FALSE; + goto leave; + } + needcmd = FALSE; + } + else { + narg++; + cmd[ncmd] = xrealloc(cmd[ncmd], narg * sizeof(char *)); + cmd[ncmd][narg - 1] = xmalloc(end - beg + 1); + kpathsea__unquote (kpse, cmd[ncmd][narg - 1], beg, end); + /* unquote and copy to prog */ + } + beg = end; /* go forward */ + again = TRUE; + break; + + case REDIR_INPUT: + case REDIR_OUTPUT: + case REDIR_APPEND: + if (token == REDIR_INPUT) { + if (*input) { + fprintf(stderr, "Ambiguous input redirect."); + errno = EINVAL; + bSuccess = FALSE; + goto leave; + } + fp = input; + } + else if (token == REDIR_OUTPUT || token == REDIR_APPEND) { + if (*output) { + fprintf(stderr, "Ambiguous output redirect."); + errno = EINVAL; + bSuccess = FALSE; + goto leave; + } + fp = output; + if (token == REDIR_APPEND) + append_out = TRUE; + } + if (kpathsea_get_sym (kpse, end, &end, &new_end) != WORDARG) { + fprintf(stderr, "Target of redirect is not a filename."); + errno = EINVAL; + bSuccess = FALSE; + goto leave; + } + *fp = (char *)xmalloc (new_end - end + 1); + memcpy (*fp, end, new_end - end); + (*fp)[new_end - end] = '\0'; + beg = new_end; + again = TRUE; + break; + case PIPE: + *is_cmd_pipe = 1; /* --ak 2009/07/08 */ + if (*output) { + fprintf(stderr, "Ambiguous output redirect."); + errno = EINVAL; + bSuccess = FALSE; + goto leave; + } + narg++; + cmd[ncmd] = xrealloc(cmd[ncmd], narg * sizeof(char *)); + cmd[ncmd][narg - 1] = NULL; + ncmd++; + needcmd = TRUE; + beg = end; + again = TRUE; + break; + case SEMICOLON: + case EOL: + if (needcmd) { + fprintf(stderr, "No command name seen."); + errno = EINVAL; + bSuccess = FALSE; + goto leave; + } + narg++; + cmd[ncmd] = xrealloc(cmd[ncmd], narg * sizeof(char *)); + cmd[ncmd][narg - 1] = NULL; + ncmd++; + cmd[ncmd] = NULL; + again = FALSE; + break; + + case UNMATCHED_QUOTE: + fprintf(stderr, "Unmatched quote character."); + errno = EINVAL; + bSuccess = FALSE; + goto leave; + default: + fprintf(stderr, "I cannot grok this."); + errno = EINVAL; + bSuccess = FALSE; + goto leave; + + } + + } while (again); + + leave: + if (!bSuccess) { + int i; + char **p; + /* Need to free everything that was allocated */ + for (i = 0; i < ncmd; i++) { + for (p = cmd[i]; *p; p++) + free(*p); + free(cmd[i]); + } + if (cmd[ncmd]) { + for (i = 0; i < narg; i++) + free(cmd[ncmd][i]); + free(cmd[ncmd]); + } + free(cmd); + *cmd = NULL; + } + return cmd; +} + +static int need_qq(char c) +{ + int ret; + ret = ((c=='&') || + (c=='(') || + (c==')') || + (c=='[') || + (c==']') || + (c=='{') || + (c=='}') || + (c=='^') || + (c==';') || + (c=='!') || + (c=='\'') || + (c=='+') || + (c=='`') || + (c=='~') || + (c=='%') || + (c=='@')); + return ret; +} + +static char * +quote_elt(char *elt) +{ + char *p; + int qok; + + if(!strcmp(elt,"|") || + !strcmp(elt,"||") || + !strcmp(elt,"&") || + !strcmp(elt,"&&") || + !strcmp(elt,";")) { + qok = 0; + } else { + qok = 1; + } + for (p = elt; *p; p++) + if (isspace(*p) || (need_qq(*p) && qok)) + return concat3("\"", elt, "\""); + + return xstrdup(elt); +} + +static char * +quote_args(char **argv) +{ + int i; + char *line = NULL, *new_line; + char *new_argv; + + if (!argv) + return line; + + line = quote_elt(argv[0]); + for (i = 1; argv[i]; i++) { + new_argv = quote_elt(argv[i]); + new_line = concat3(line, " ", new_argv); + free(line); + free(new_argv); + line = new_line; + } + + return line; +} + +char * +build_cmdline(char ***cmd, char *input, char *output) +{ + int ncmd; + char *line = NULL, *new_line; + + if (!cmd) + return line; + + line = quote_args(cmd[0]); + if (input) { + new_line = concat3(line, "<", quote_elt(input)); + free(line); + line = new_line; + } + for(ncmd = 1; cmd[ncmd]; ncmd++) { + new_line = concat3(line, " | ", quote_args(cmd[ncmd])); + free(line); + line = new_line; + } + + if (output) { + new_line = concat3(line, ">", quote_elt(output)); + free(line); + line = new_line; + } + + return line; +} + +/* win32_system */ + +/* + It has been proven that system() fails to retrieve exit codes + under Win9x. This is a workaround for this bug. +*/ + +int __cdecl kpathsea_win32_system(kpathsea kpse, char *cmd) +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + DWORD ret = 0; + HANDLE hIn, hOut, hPipeIn, hPipeOut; + SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; + int i; + int is_cmd_pipe; + char *app_name, *new_cmd; + char ***cmd_pipe; + char *red_i, *red_o; + char *red_input = NULL; + char *red_output = NULL; + char *sp, *dp; + + /* Reset errno ??? */ + errno = 0; + + /* Admittedly, the command interpreter will allways be found. */ + if (! cmd) { + errno = 0; +#ifdef _TRACE + fprintf(stderr, "system: (null) command.\n"); +#endif + return 1; + } + + if (look_for_cmd(cmd, &app_name, &new_cmd) == FALSE) { + /* Failed to find the command or malformed cmd */ + errno = ENOEXEC; +#ifdef _TRACE + fprintf(stderr, "system: failed to find command.\n"); +#endif + return -1; + } + + is_cmd_pipe = 0; + cmd_pipe = (char ***)kpathsea_parse_cmdline(kpse, new_cmd, &red_i, &red_o, + &is_cmd_pipe); + + if(red_i) { + red_input = (char *)malloc(strlen(red_i) + 1); + for(sp = red_i, dp = red_input; *sp; sp++) { + if(*sp != '\"') *dp++ = *sp; + } + *dp = '\0'; + } + + if(red_o) { + red_output = (char *)malloc(strlen(red_o) + 1); + for(sp = red_o, dp = red_output; *sp; sp++) { + if(*sp != '\"') *dp++ = *sp; + } + *dp = '\0'; + } + + for (i = 0; cmd_pipe[i]; i++) { + + /* free the cmd and build the current one */ + if (new_cmd) free(new_cmd); + + if(is_cmd_pipe) + new_cmd = (char *)build_cmdline(&cmd_pipe[i], red_input, red_output); + else + new_cmd = (char *)build_cmdline(&cmd_pipe[i], NULL, NULL); + + /* First time, use red_input if available */ + if (i == 0) { + if (is_cmd_pipe == 0) { + if (red_input) { + hIn = CreateFile(red_input, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + &sa, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hIn == INVALID_HANDLE_VALUE) { +#ifdef _TRACE + fprintf(stderr, "system: failed to open hIn (%s) with error %d.\n", + red_input, GetLastError()); +#endif + errno = EIO; + return -1; + } + } + else { + hIn = GetStdHandle(STD_INPUT_HANDLE); + } + } + } + /* Last time, use red_output if available */ + if (cmd_pipe[i+1] == NULL) { + if (is_cmd_pipe == 0) { + if (red_output) { + hOut = CreateFile(red_output, + GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + &sa, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hOut == INVALID_HANDLE_VALUE) { +#ifdef _TRACE + fprintf(stderr, "system: failed to open hOut (%s) with error %d.\n", + red_output, GetLastError()); +#endif + errno = EIO; + return -1; + } + } + else { + hOut = GetStdHandle(STD_OUTPUT_HANDLE); + } + } + } + + /* In the case of pipe, we use spawnvp(), as the original + system() --ak 2009/07/08 */ + + if(is_cmd_pipe) { + char *av[4]; + if(_osver & 0x8000) + av[0] = strdup("command.com"); + else + av[0] = strdup("cmd.exe"); + av[1] = strdup("/c"); + av[2] = new_cmd; + av[3] = NULL; + ret = spawnvp(_P_WAIT, av[0], av); + free(av[0]); + free(av[1]); + if(new_cmd) free(new_cmd); + if(app_name) free(app_name); + if(red_input) free(red_input); + if(red_output) free(red_output); + return ret; + } + + ZeroMemory( &si, sizeof(STARTUPINFO) ); + si.cb = sizeof(STARTUPINFO); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOW; + si.hStdInput = hIn; + si.hStdOutput = hOut; + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + +#ifdef _TRACE + fprintf(stderr, "Executing: %s\n", new_cmd); +#endif + if (CreateProcess(app_name, + new_cmd, + NULL, + NULL, + TRUE, + 0, + NULL, + NULL, + &si, + &pi) == 0) { + fprintf(stderr, "win32_system(%s) call failed (Error %d).\n", cmd, GetLastError()); + return -1; + } + + /* Only the process handle is needed */ + CloseHandle(pi.hThread); + + if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_OBJECT_0) { + if (GetExitCodeProcess(pi.hProcess, &ret) == 0) { + fprintf(stderr, "Failed to retrieve exit code: %s (Error %d)\n", cmd, GetLastError()); + ret = -1; + } + } + else { + fprintf(stderr, "Failed to wait for process termination: %s (Error %d)\n", cmd, GetLastError()); + ret = -1; + } + CloseHandle(pi.hProcess); + + if (red_input) CloseHandle(hIn); + if (red_output) CloseHandle(hOut); + } + + if (new_cmd) free(new_cmd); + if (app_name) free(app_name); + + return ret; +} + +#if defined (KPSE_COMPAT_API) +FILE * __cdecl win32_popen (char *cmd, char *mode) +{ + return kpathsea_win32_popen(kpse_def, cmd, mode); +} + +int __cdecl win32_pclose (FILE *f) +{ + return kpathsea_win32_pclose(kpse_def, f); +} + +int +getuid (void) +{ + return kpathsea_getuid(kpse_def); +} + +int +geteuid (void) +{ + /* I could imagine arguing for checking to see whether the user is + in the Administrators group and returning a UID of 0 for that + case, but I don't know how wise that would be in the long run. */ + return getuid (); +} + +int +getgid (void) +{ + return kpathsea_getgid (kpse_def); +} + +int +getegid (void) +{ + return getgid (); +} + +struct passwd * +getpwuid (int uid) +{ + return kpathsea_getpwuid (kpse_def, uid); +} + +struct passwd * +getpwnam (char *name) +{ + return kpathsea_getpwnam (kpse_def, name); +} + +int __cdecl win32_system(char *cmd) +{ + return kpathsea_win32_system (kpse_def, cmd); +} +#endif /* KPSE_COMPAT_API */ diff --git a/Build/source/texk/kpathsea/win32lib.h b/Build/source/texk/kpathsea/win32lib.h index ed2e1ffe579..89d35166a4b 100644 --- a/Build/source/texk/kpathsea/win32lib.h +++ b/Build/source/texk/kpathsea/win32lib.h @@ -1,7 +1,7 @@ /* win32lib.h: bits and pieces for win32 and msvc. - Copyright 1996, xxxx Fabrice Popineau. - Copyright xxxx, 2011 Akira Kakuto. + Copyright 1996, 1997, 1998 Fabrice Popineau. + Copyright 2006, 2011 Akira Kakuto. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -16,11 +16,252 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, see . */ -#ifndef KPATHSEA_WIN32LIBH -#define KPATHSEA_WIN32LIBH + +#ifndef KPATHSEA_WIN32LIB_H +#define KPATHSEA_WIN32LIB_H + +#ifndef KPSE_COMPAT_API +#define KPSE_COMPAT_API 1 +#endif + +#pragma warning( disable : 4007 4096 4018 4244 ) + +#include +#include /* - * To be filled in + * Define symbols to identify the version of Unix this is. + * Define all the symbols that apply correctly. */ -#endif /* not KPATHSEA_WIN32LIBH */ +#ifndef DOSISH +#define DOSISH +#endif + +#ifndef MAXPATHLEN +#define MAXPATHLEN _MAX_PATH +#endif + +#define HAVE_DUP2 1 +#define HAVE_RENAME 1 +#define HAVE_RMDIR 1 +#define HAVE_MKDIR 1 +#define HAVE_GETHOSTNAME 1 +#define HAVE_RANDOM 1 +#define USE_UTIME 1 +#define HAVE_MOUSE 1 +#define HAVE_TZNAME 1 + +/* These have to be defined because our compilers treat __STDC__ as being + defined (most of them anyway). */ + +#define access _access +#define alloca _alloca +#define chdir _chdir +#define chmod _chmod +#define close _close +#define creat _creat +#define daylight _daylight +#define dup _dup +#define dup2 _dup2 +#define execlp _execlp +#define execvp _execvp +#define fcloseall _fcloseall +#define fdopen _fdopen +#define fileno _fileno +#define flushall _flushall +#define fstat _fstat +#define ftime _ftime +#define getpid _getpid +#define getcwd _getcwd +#define getwd(dir) GetCurrentDirectory(MAXPATHLEN, dir) +#define inline __inline +#define isascii __isascii +#define isatty _isatty +#define itoa _itoa +#define link _link +#define lseek _lseek +#define memicmp _memicmp +#define mkdir _mkdir +#define mktemp _mktemp +#define open _open +#define pipe _pipe + +#define putenv _putenv +#define read _read +#define rmdir _rmdir +#define setmode _setmode +#define spawnlp _spawnlp +#define stat _stat +#define stricmp _stricmp +#ifdef strcasecmp +#undef strcasecmp +#endif +#define strcasecmp _stricmp +#define strdup _strdup +#define strlwr _strlwr +#define strncasecmp _strnicmp +#define strnicmp _strnicmp +#define tempnam _tempnam +#define timeb _timeb +#define timezone _timezone +#define unlink _unlink +#define umask _umask +#define utime _utime +#define write _write + +#ifndef S_IFMT +#define S_IFMT _S_IFMT +#endif +#ifndef S_IFDIR +#define S_IFDIR _S_IFDIR +#endif +#ifndef S_IFCHR +#define S_IFCHR _S_IFCHR +#endif +#ifndef S_IFIFO +#define S_IFIFO _S_IFIFO +#endif +#ifndef S_IFREG +#define S_IFREG _S_IFREG +#endif +#ifndef S_IREAD +#define S_IREAD _S_IREAD +#endif +#ifndef S_IWRITE +#define S_IWRITE _S_IWRITE +#endif +#define S_IEXEC _S_IEXEC +#ifndef S_IXUSR +#define S_IXUSR _S_IEXEC +#endif +#ifndef S_IXGRP +#define S_IXGRP _S_IEXEC +#endif +#ifndef S_IXOTH +#define S_IXOTH _S_IEXEC +#endif +#ifndef S_IRUSR +#define S_IRUSR _S_IREAD +#endif +#ifndef S_IWUSR +#define S_IWUSR _S_IWRITE +#endif +#ifndef S_IROTH +#define S_IROTH _S_IREAD +#endif +#ifndef S_IWOTH +#define S_IWOTH _S_IWRITE +#endif +#ifndef S_IRGRP +#define S_IRGRP _S_IREAD +#endif +#ifndef S_IWGRP +#define S_IWGRP _S_IWRITE +#endif +#ifndef O_RDWR +#define O_RDWR _O_RDWR +#endif +#ifndef O_CREAT +#define O_CREAT _O_CREAT +#endif +#ifndef O_TRUNC +#define O_TRUNC _O_TRUNC +#endif +#ifndef O_RDONLY +#define O_RDONLY _O_RDONLY +#endif +#ifndef O_WRONLY +#define O_WRONLY _O_WRONLY +#endif +#ifndef O_APPEND +#define O_APPEND _O_APPEND +#endif +#ifndef O_TEXT +#define O_TEXT _O_TEXT +#endif +#ifndef O_BINARY +#define O_BINARY _O_BINARY +#endif +#ifndef O_EXCL +#define O_EXCL _O_EXCL +#endif + +#if defined (S_IFBLK) && !defined (S_ISBLK) +#define S_ISBLK(m) (((m)&S_IFMT) == S_IFBLK) /* block device */ +#endif + +#if defined (S_IFCHR) && !defined (S_ISCHR) +#define S_ISCHR(m) (((m)&S_IFMT) == S_IFCHR) /* character device */ +#endif + +#if defined (S_IFDIR) && !defined (S_ISDIR) +#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) /* directory */ +#endif + +#if defined (S_IFREG) && !defined (S_ISREG) +#define S_ISREG(m) (((m)&S_IFMT) == S_IFREG) /* file */ +#endif + +#if defined (S_IFIFO) && !defined (S_ISFIFO) +#define S_ISFIFO(m) (((m)&S_IFMT) == S_IFIFO) /* fifo - named pipe */ +#endif + +#if defined (S_IFLNK) && !defined (S_ISLNK) +#define S_ISLNK(m) (((m)&S_IFMT) == S_IFLNK) /* symbolic link */ +#endif + +#if defined (S_IFSOCK) && !defined (S_ISSOCK) +#define S_ISSOCK(m) (((m)&S_IFMT) == S_IFSOCK) /* socket */ +#endif + +/* Define this so that winsock.h definitions don't get included when + windows.h is... For this to have proper effect, config.h must + always be included before windows.h. */ +#if !defined(_WINSOCKAPI_) && !defined(WWW_WIN_DLL) +#define _WINSOCKAPI_ 1 +#endif + +#include + +/* Defines size_t and alloca (). */ +#include + +/* For proper declaration of environ. */ +#include +#include +#include +#include +#include + +/* Web2C takes care of ensuring that these are defined. */ +#ifdef max +#undef max +#undef min +#endif + +#if defined (KPSE_COMPAT_API) +extern KPSEDLL struct passwd *getpwnam (char *name); +#define MAX_PIPES 128 +#define system(p) win32_system(p) +#define popen(cmd, mode) win32_popen(cmd, mode) +#define pclose(file) win32_pclose(file) +/* Functions for WIN32 */ +extern KPSEDLL FILE *popen(char * str, char * str2); +extern KPSEDLL int pclose(FILE * f); +extern KPSEDLL int system(char * cmd); +#endif /* KPSE_COMPAT_API */ + +extern KPSEDLL void xfseek64 (FILE *f, __int64 offset, int wherefrom, char *fname); +extern KPSEDLL __int64 xftell64 (FILE *f, char *fname); +extern KPSEDLL void texlive_gs_init(void); +extern KPSEDLL int getlongpath (char *output, char *input, int len); +extern KPSEDLL char *get_home_directory (void); + +/* I don't define xfseeko and xftello in order to find where they are + used and to rewrite them by hand using xfseek64 and xftell64. +#define xfseeko xfseek64 +#define xftello xftell64 +*/ + +#endif /* not KPATHSEA_WIN32LIB_H */ -- cgit v1.2.3