diff options
author | Karl Berry <karl@freefriends.org> | 2006-01-17 21:41:51 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2006-01-17 21:41:51 +0000 |
commit | 487ca4806cc046076293cf6cc5fbba0db282bac7 (patch) | |
tree | 847b412ab5158dd7bdd7ed7e5a4cc3fbca94be32 /Build/source/texk/xdvik/browser.c | |
parent | a3d3111bfe26b8e5f5bc6049dfb2a4ca2edc7881 (diff) |
texk 1
git-svn-id: svn://tug.org/texlive/trunk@1485 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/xdvik/browser.c')
-rw-r--r-- | Build/source/texk/xdvik/browser.c | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/Build/source/texk/xdvik/browser.c b/Build/source/texk/xdvik/browser.c new file mode 100644 index 00000000000..4fbbe342c1d --- /dev/null +++ b/Build/source/texk/xdvik/browser.c @@ -0,0 +1,273 @@ +/* routines for launching a browser to retrieve remote documents. + * Copyright(C) 2002-2004 the xdvik development team. + */ +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "xdvi-config.h" +#include "xdvi.h" + +#include <ctype.h> +#include "kpathsea/c-fopen.h" +#include "kpathsea/c-stat.h" + +#include <sys/types.h> +#include <sys/wait.h> /* for waitpid(), WEXITSTATUS */ + +#include "util.h" +#include "message-window.h" +#include "events.h" +#include "browser.h" +#include "string-utils.h" +#include "statusline.h" + +static const char *const default_browser_cmd = + "netscape -raise -remote \"openURL(%s,new-window)\"" + ":xterm -e lynx %s" + ":xterm -e wget %s"; + +static Boolean +do_fork_browser(char *argv[]) +{ + pid_t pid; + switch(pid = vfork()) { + case -1: /* forking error */ + perror("fork"); + return False; + case 0: /* child */ + execvp(argv[0], argv); + /* arrive here only if execvp failed */ + XDVI_ERROR((stderr, "Execution of %s failed: %s", argv[0], strerror(errno))); + _exit(EXIT_FAILURE); + return False; /* notreached - make compiler happy */ + default: /* parent */ + { + int timeout; + for (timeout = 0; timeout < 15; timeout++) { + int status; + if (waitpid(pid, &status, WNOHANG)) { + TRACE_HTEX((stderr, "waiting for %d: %d", (int)pid, status)); + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) { + fprintf(stderr, "Command `%s' exited with error status %d\n", + argv[0], WEXITSTATUS(status)); + return False; + } + else { + TRACE_HTEX((stderr, "Child exited OK.")); + return True; + } + } + else { /* when do we arrive here?? */ + sleep(1); + } + } + else { /* waiting for child to finish */ + sleep(1); + } + } + return True; /* child not finished in time */ + } + } +} + +static Boolean +fork_browser(const char *browser, const char *url) +{ + char *cmd, *orig_cmd, *last_arg; + char **argv = NULL; + int argv_len = 0; + int i = 0, j = 0; + int match = 0; + int retval; + + + cmd = xmalloc(strlen(browser) + strlen(url) + 1); + orig_cmd = cmd; /* for freeing it later */ + last_arg = cmd; + + /* skip over leading space */ + while (isspace((int)browser[i])) + i++; + + while (browser[i] != '\0') { + while (j >= argv_len) { + argv_len += 10; + argv = xrealloc(argv, argv_len * sizeof *argv); + } + /* chop into separate arguments at spaces */ + if (isspace((int)browser[i])) { + *cmd++ = '\0'; + argv[j++] = format_arg(last_arg, url, &match); + last_arg = cmd; + /* skip over multiple spaces */ + while (isspace((int)browser[i])) + i++; + } + /* remove quotes around arguments containing spaces */ + else if (browser[i] == '\'' || browser[i] == '"') { + int len = 0; + /* normalize %% and replace %s by URL */ + argv[j++] = unquote_arg(browser+i, url, &match, &len); + if (len == 0) { /* excess quote at end of arg; try to recover: */ + j--; + break; + } + i += len + 1; + } + else { + *cmd++ = browser[i++]; + } + } + *cmd = browser[i]; /* null-teminate */ + /* append last, unless it contained only skipped spaces */ + if (strlen(last_arg) > 0) { + argv[j++] = format_arg(last_arg, url, &match); + } + + if (match == 0) + argv[j++] = xstrdup(url); + argv[j++] = NULL; + + for (i = 0; argv[i] != NULL; i++) { + TRACE_HTEX((stderr, "arg[%d]: |%s|", i, argv[i])); + } + + /* This will wait for child exits: */ + retval = do_fork_browser(argv); + for (i = 0; argv[i] != NULL; i++) + free(argv[i]); + free(orig_cmd); + free(argv); + return retval; +} + +void +launch_browser(const char *filename) +{ + const char *browser; + int pid; + struct xchild *my_child; + struct xio *my_io; + int err_pipe[2]; + + /* try to set it from possible resources: First command-line argument + or X resource, then environment variables, then default_browser_cmd + */ + for (;;) { + if ((browser = resource.browser) != NULL) + break; + if ((browser = getenv("BROWSER")) != NULL) + break; + if ((browser = getenv("WWWBROWSER")) != NULL) + break; + XDVI_INFO((stderr, "Browser not set (xdvi.wwwBrowser, -browser or $BROWSER environment variable).")); + XDVI_INFO((stderr, "Using built-in default: `%s'", + default_browser_cmd)); + browser = default_browser_cmd; + break; + } + + /* fork first time so that we can wait for children, without + freezing the GUI. FIXME: this copies stuff from + fork_process for the inter-process communication stuff - + it would be better to have this in one function. */ + my_child = xmalloc(sizeof *my_child); + my_io = xmalloc(sizeof *my_io); + statusline_print(STATUS_MEDIUM, "Trying to launch browser ..."); + /* flush output buffers to avoid double buffering (i.e. data + waiting in the output buffer being written twice, by the parent + and the child) */ + fflush(stdout); + fflush(stderr); + + if (pipe(err_pipe) < 0) { + perror("pipe"); + _exit(-1); + } + + switch (pid = fork()) { + case -1: /* forking error */ + perror("fork"); + close(err_pipe[0]); + close(err_pipe[1]); + return; + case 0: /* child */ + { + char *tmp_browser = xstrdup(browser); + + close(err_pipe[0]); /* no reading from stderr */ + + /* make stderr of child go to err_pipe[1] */ + if (dup2(err_pipe[1], STDERR_FILENO) != STDERR_FILENO) { + perror("dup2 for stderr"); + _exit(EXIT_FAILURE); + return; /* make compiler happy */ + } + + /* + BROWSER is a colon-separated list of commands, in decreasing preference; + use the first that can be forked successfully. Note that the return + value of the command isn't used at all (with GUI programs, xdvi shouldn't + hang until they terminate!) + */ + while (tmp_browser != NULL) { + char *next = strchr(tmp_browser, ':'); + if (next != NULL) { + *next++ = '\0'; + } + TRACE_HTEX((stderr, "trying browser |%s|", tmp_browser)); + /* fork a second time to start the browser */ + if (fork_browser(tmp_browser, filename)) { /* foking worked */ + _exit(EXIT_SUCCESS); + } + tmp_browser = next; + } + /* child arrives here only if none of the commands worked */ + XDVI_WARNING((stderr, "None of the browser commands in the `browser' resource (%s) worked\n", + browser)); + free(tmp_browser); + _exit(EXIT_FAILURE); + } + default: /* parent */ + close(err_pipe[1]); /* no writing to stderr */ + + my_io->next = NULL; + my_io->fd = err_pipe[0]; + my_io->xio_events = XIO_IN; +#if HAVE_POLL + my_io->pfd = NULL; +#endif + my_io->read_proc = read_child_error; + my_io->write_proc = NULL; + + my_child->next = NULL; + my_child->pid = pid; + my_child->name = NULL; + my_child->proc = handle_child_exit; + my_child->data = NULL; + my_child->io = my_io; + + set_chld(my_child); + statusline_print(STATUS_MEDIUM, "Trying to launch browser ... done."); + } +} + + |