summaryrefslogtreecommitdiff
path: root/os_dep.c
diff options
context:
space:
mode:
Diffstat (limited to 'os_dep.c')
-rw-r--r--os_dep.c2689
1 files changed, 2689 insertions, 0 deletions
diff --git a/os_dep.c b/os_dep.c
new file mode 100644
index 0000000..791b2c0
--- /dev/null
+++ b/os_dep.c
@@ -0,0 +1,2689 @@
+/* os_dep.c
+ * (c) 2002 Mikulas Patocka
+ * This file is a part of the Links program, released under GPL.
+ */
+
+#include "links.h"
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef USE_GPM
+#include <gpm.h>
+#endif
+
+
+int is_safe_in_shell(unsigned char c)
+{
+ return c == '@' || c == '+' || c == '-' || c == '.' || c == ',' || c == '=' || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= 'a' && c <= 'z');
+}
+
+int is_safe_in_url(unsigned char c)
+{
+ return is_safe_in_shell(c) || c == ':' || c == '/' || c >= 0x80;
+}
+
+void check_shell_security(unsigned char **cmd)
+{
+ unsigned char *c = *cmd;
+ while (*c) {
+ if (!is_safe_in_shell(*c)) *c = '_';
+ c++;
+ }
+}
+
+int check_shell_url(unsigned char *url)
+{
+ while (*url) {
+ if (!is_safe_in_url(*url)) return -1;
+ url++;
+ }
+ return 0;
+}
+
+unsigned char *escape_path(unsigned char *path)
+{
+ unsigned char *result;
+ size_t i;
+ if (strchr(path, '"')) return stracpy(path);
+ for (i = 0; path[i]; i++) if (!is_safe_in_url(path[i])) goto do_esc;
+ return stracpy(path);
+ do_esc:
+ result = stracpy("\"");
+ add_to_strn(&result, path);
+ add_to_strn(&result, "\"");
+ return result;
+}
+
+static int get_e(unsigned char *env)
+{
+ unsigned char *v;
+ if ((v = getenv(env))) return atoi(v);
+ return 0;
+}
+
+void ignore_signals(void)
+{
+ errno = 0;
+ while (signal(SIGPIPE, SIG_IGN) == SIG_ERR && errno == EINTR) errno = 0;
+#ifdef SIGXFSZ
+ errno = 0;
+ while (signal(SIGXFSZ, SIG_IGN) == SIG_ERR && errno == EINTR) errno = 0;
+#endif
+}
+
+unsigned char *clipboard = NULL;
+
+#if defined(WIN32)
+#include <windows.h>
+#endif
+
+#if defined(OS2)
+
+#define INCL_MOU
+#define INCL_VIO
+#define INCL_DOSPROCESS
+#define INCL_DOSERRORS
+#define INCL_DOSMODULEMGR
+#define INCL_DOSMISC
+#define INCL_WIN
+#define INCL_WINCLIPBOARD
+#define INCL_WINSWITCHLIST
+#include <os2.h>
+#include <io.h>
+#include <process.h>
+#include <sys/video.h>
+#ifdef HAVE_SYS_FMUTEX_H
+#include <sys/builtin.h>
+#include <sys/fmutex.h>
+#endif
+
+#ifdef X2
+/* from xf86sup - XFree86 OS/2 support driver */
+#include <pty.h>
+#endif
+
+#endif
+
+
+#ifdef OS2
+
+/* The process crashes if we write to console from high address - so we must
+ * never do it.
+ * TCP/IP 4.0 returns EFAULT if we do I/O to/from high address - we test for
+ * EFAULT and retry with a bounce buffer. */
+
+#define BOUNCE_BUFFER_SIZE 256
+
+int bounced_read(int fd, void *buf, size_t size)
+{
+ unsigned char *bounce_buffer;
+ size_t xsiz;
+ int r;
+ if (fd < 3 && (unsigned long)buf + size > 0x20000000) goto bounce;
+ r = _read(fd, buf, size);
+ if (r == -1 && errno == EFAULT) goto bounce;
+ return r;
+ bounce:
+ xsiz = size > BOUNCE_BUFFER_SIZE ? BOUNCE_BUFFER_SIZE : size;
+ bounce_buffer = alloca(xsiz);
+ r = _read(fd, bounce_buffer, xsiz);
+ if (r > 0) memcpy(buf, bounce_buffer, r);
+ return r;
+}
+
+int bounced_write(int fd, const void *buf, size_t size)
+{
+ unsigned char *bounce_buffer;
+ size_t xsiz;
+ int r;
+ if (fd < 3 && (unsigned long)buf + size > 0x20000000) goto bounce;
+ r = _write(fd, buf, size);
+ if (r == -1 && errno == EFAULT) goto bounce;
+ return r;
+ bounce:
+ xsiz = size > BOUNCE_BUFFER_SIZE ? BOUNCE_BUFFER_SIZE : size;
+ bounce_buffer = alloca(xsiz);
+ memcpy(bounce_buffer, buf, xsiz);
+ return _write(fd, bounce_buffer, xsiz);
+}
+
+#endif
+
+#ifdef OS2_ADVANCED_HEAP
+
+#include <umalloc.h>
+
+#ifndef OBJ_ANY
+#define OBJ_ANY 0x0400
+#endif
+
+unsigned long mem_requested = 0;
+unsigned long blocks_requested = 0;
+
+static int dosallocmem_attrib = PAG_READ | PAG_WRITE | PAG_COMMIT;
+
+#define HEAP_ALIGN 0x10000
+#define HEAP_PAD 2
+#define HEAP_MAXPAD 0x1000000
+
+static void heap_release(Heap_t h, void *ptr, size_t len)
+{
+ int rc;
+ mem_requested -= ((len | 4095) + 1);
+ blocks_requested--;
+ rc = DosFreeMem(ptr);
+ /*fprintf(stderr, "heap free %p -> %d\n", ptr, rc);*/
+ if (rc) {
+ error("DosFreeMem failed: %d", rc);
+ fatal_tty_exit();
+ exit(RET_FATAL);
+ }
+}
+
+static void *heap_alloc(Heap_t h, size_t *size, int *pclean)
+{
+ void *result;
+ int rc;
+ /* If we rounded up to page size, EMX would join all allocations
+ * to one segment and refuse to free memory. So round up to
+ * page size - 1 */
+ size_t real_size = *size;
+ if (real_size < HEAP_MAXPAD / HEAP_PAD) {
+ real_size *= HEAP_PAD;
+ real_size = real_size | (HEAP_ALIGN - 1);
+ } else {
+ real_size |= 1;
+ }
+ rc = DosAllocMem(&result, real_size, dosallocmem_attrib);
+ /*fprintf(stderr, "heap alloc %d -> %p, %d\n", *size, result, rc);*/
+ if (!rc) {
+ /*
+ * Hitting the shared arena has a negative impact on the whole
+ * system. Therefore, we fake failure (so that Links frees
+ * some caches) and try allocating near the shared arena only
+ * as a last resort.
+ */
+ if ((unsigned long)result >= 0x12000000 &&
+ (unsigned long)result < 0x20000000) {
+ if (!malloc_try_hard) {
+ heap_release(NULL, result, real_size);
+ return NULL;
+ }
+ }
+ *size = real_size;
+ *pclean = _BLOCK_CLEAN;
+ mem_requested += ((real_size | 4095) + 1);
+ blocks_requested++;
+ return result;
+ } else {
+ return NULL;
+ }
+}
+
+static void init_os2_heap(void)
+{
+ Heap_t new_heap;
+ size_t init_size = _HEAP_MIN_SIZE;
+ void *init_mem;
+ int init_clean;
+ dosallocmem_attrib |= OBJ_ANY;
+ init_mem = heap_alloc(NULL, &init_size, &init_clean);
+ if (!init_mem) {
+ dosallocmem_attrib &= ~OBJ_ANY;
+ init_mem = heap_alloc(NULL, &init_size, &init_clean);
+ if (!init_mem) {
+ return;
+ }
+ }
+ new_heap = _ucreate(init_mem, init_size, init_clean, _HEAP_REGULAR, heap_alloc, heap_release);
+ if (!new_heap) {
+ heap_release(NULL, init_mem, init_size);
+ return;
+ }
+ if (_uopen(new_heap) == -1) {
+#if defined(HAVE__UDESTROY) && defined(_FORCE)
+ _udestroy(new_heap, _FORCE);
+#else
+ heap_release(NULL, init_mem, init_size);
+#endif
+ return;
+ }
+ _udefault(new_heap);
+}
+
+#endif
+
+#if defined(O_SIZE) && defined(__EMX__)
+
+int open_prealloc(unsigned char *name, int flags, int mode, off_t siz)
+{
+ int h;
+ EINTRLOOP(h, open(name, flags | O_SIZE, mode, (unsigned long)siz));
+ return h;
+}
+
+#endif
+
+/* Terminal size */
+
+#ifdef WIN32
+
+/* Cygwin has a bug and loses SIGWINCH sometimes, so poll it */
+
+static void winch_thread(void *p, int l)
+{
+ static int old_xsize, old_ysize;
+ static int cur_xsize, cur_ysize;
+ if (get_terminal_size(0, &old_xsize, &old_ysize)) return;
+ while (1) {
+ if (get_terminal_size(1, &cur_xsize, &cur_ysize)) return;
+ if ((old_xsize != cur_xsize) || (old_ysize != cur_ysize)) {
+ int rr;
+ old_xsize = cur_xsize;
+ old_ysize = cur_ysize;
+ EINTRLOOP(rr, raise(SIGWINCH));
+ }
+ sleep(1);
+ }
+}
+
+static void win32_resize_poll(void)
+{
+ static int winch_thread_running = 0;
+ if (!winch_thread_running) {
+ if (start_thread(winch_thread, NULL, 0) >= 0)
+ winch_thread_running = 1;
+ }
+}
+
+#endif
+
+#if defined(UNIX) || defined(WIN32) || defined(INTERIX) || defined(BEOS) || defined(RISCOS) || defined(ATHEOS) || defined(SPAD)
+
+static void sigwinch(void *s)
+{
+ ((void (*)(void))s)();
+}
+
+void handle_terminal_resize(int fd, void (*fn)(void))
+{
+ install_signal_handler(SIGWINCH, sigwinch, fn, 0);
+#ifdef WIN32
+ win32_resize_poll();
+#endif
+}
+
+void unhandle_terminal_resize(int fd)
+{
+ install_signal_handler(SIGWINCH, NULL, NULL, 0);
+}
+
+int get_terminal_size(int fd, int *x, int *y)
+{
+ volatile struct winsize ws; /* Sun Studio misoptimizes it */
+ int rs;
+ if (!x || !y) return -1;
+ EINTRLOOP(rs, ioctl(1, TIOCGWINSZ, &ws));
+ if (rs != -1) {
+ if (!(*x = ws.ws_col) && !(*x = get_e("COLUMNS"))) *x = 80;
+ if (!(*y = ws.ws_row) && !(*y = get_e("LINES"))) *y = 24;
+ return 0;
+ } else {
+ if (!(*x = get_e("COLUMNS"))) *x = 80;
+ if (!(*y = get_e("LINES"))) *y = 24;
+ }
+ return 0;
+}
+
+#elif defined(OS2)
+
+#define A_DECL(type, var) type var##1, var##2, *var = _THUNK_PTR_STRUCT_OK(&var##1) ? &var##1 : &var##2
+
+int is_xterm(void)
+{
+ static int xt = -1;
+ if (xt == -1) xt = !!getenv("WINDOWID");
+ return xt;
+}
+
+static int winch_pipe[2];
+static unsigned char winch_thread_running = 0;
+
+#define WINCH_SLEEPTIME 500 /* time in ms for winch thread to sleep */
+
+static void winch_thread(void)
+{
+ /* A thread which regularly checks whether the size of
+ window has changed. Then raise SIGWINCH or notifiy
+ the thread responsible to handle this. */
+ static int old_xsize, old_ysize;
+ static int cur_xsize, cur_ysize;
+
+ ignore_signals();
+ if (get_terminal_size(1, &old_xsize, &old_ysize)) return;
+ while (1) {
+ if (get_terminal_size(1, &cur_xsize, &cur_ysize)) return;
+ if ((old_xsize != cur_xsize) || (old_ysize != cur_ysize)) {
+ int wr;
+ old_xsize = cur_xsize;
+ old_ysize = cur_ysize;
+ EINTRLOOP(wr, write(winch_pipe[1], "x", 1));
+ /* Resizing may take some time. So don't send a flood
+ of requests?! */
+ _sleep2(2*WINCH_SLEEPTIME);
+ }
+ else
+ _sleep2(WINCH_SLEEPTIME);
+ }
+}
+
+static void winch(void *s)
+{
+ unsigned char c;
+ while (can_read(winch_pipe[0])) {
+ int rd;
+ EINTRLOOP(rd, read(winch_pipe[0], &c, 1));
+ if (rd != 1) break;
+ }
+ ((void (*)(void))s)();
+}
+
+void handle_terminal_resize(int fd, void (*fn)(void))
+{
+ if (!is_xterm()) return;
+ if (!winch_thread_running) {
+ if (c_pipe(winch_pipe) < 0) return;
+ winch_thread_running = 1;
+ if (_beginthread((void (*)(void *))winch_thread, NULL, 0x32000, NULL) == -1) {
+ }
+ }
+ set_handlers(winch_pipe[0], winch, NULL, NULL, fn);
+}
+
+void unhandle_terminal_resize(int fd)
+{
+ set_handlers(winch_pipe[0], NULL, NULL, NULL, NULL);
+}
+
+int get_terminal_size(int fd, int *x, int *y)
+{
+ if (!x || !y) return -1;
+ if (is_xterm()) {
+#ifdef X2
+ int arc;
+ struct winsize win;
+
+ /* fd = STDIN_FILENO; */
+ arc = ptioctl(1, TIOCGWINSZ, &win);
+ if (arc) {
+ *x = 80;
+ *y = 24;
+ return 0;
+ }
+ *y = win.ws_row;
+ *x = win.ws_col;
+ goto set_default;
+#else
+ *x = 80; *y = 24;
+ return 0;
+#endif
+ } else {
+ int a[2] = { 0, 0 };
+ _scrsize(a);
+ *x = a[0];
+ *y = a[1];
+#ifdef X2
+ set_default:
+#endif
+ if (*x == 0) {
+ *x = get_e("COLUMNS");
+ if (*x == 0) *x = 80;
+ }
+ if (*y == 0) {
+ *y = get_e("LINES");
+ if (*y == 0) *y = 24;
+ }
+ }
+ return 0;
+}
+
+#endif
+
+/* Pipe */
+
+#if defined(OS2) || (defined(WIN32) && !defined(_UWIN))
+
+void set_bin(int fd)
+{
+ setmode(fd, O_BINARY);
+}
+
+#else
+
+void set_bin(int fd)
+{
+}
+
+#endif
+
+int c_pipe(int *fd)
+{
+ int r;
+ EINTRLOOP(r, pipe(fd));
+ if (!r) set_bin(fd[0]), set_bin(fd[1]);
+ return r;
+}
+
+/* Exec */
+
+int can_twterm(void) /* Check if it make sense to call a twterm. */
+{
+ static int xt = -1;
+ if (xt == -1) xt = !!getenv("TWDISPLAY");
+ return xt;
+}
+
+
+#if defined(UNIX) || defined(SPAD)
+
+int is_xterm(void)
+{
+ static int xt = -1;
+ if (xt == -1) xt = getenv("DISPLAY") && *getenv("DISPLAY");
+ return xt;
+}
+
+#elif defined(BEOS) || defined(ATHEOS)
+
+int is_xterm(void)
+{
+ return 0;
+}
+
+#elif defined(WIN32) || defined(INTERIX)
+
+int is_xterm(void)
+{
+ static int xt = -1;
+ if (xt == -1) xt = !!getenv("WINDOWID");
+ return xt;
+}
+
+#elif defined(RISCOS)
+
+int is_xterm(void)
+{
+ return 1;
+}
+
+#endif
+
+void close_fork_tty(void)
+{
+ struct terminal *t;
+ struct download *d;
+ struct connection *c;
+ struct k_conn *k;
+ int rs;
+ foreach (t, terminals) if (t->fdin > 0)
+ EINTRLOOP(rs, close(t->fdin));
+ foreach (d, downloads) if (d->handle > 0)
+ EINTRLOOP(rs, close(d->handle));
+ foreach (c, queue) close_socket(&c->sock1), close_socket(&c->sock2);
+ foreach (k, keepalive_connections)
+ EINTRLOOP(rs, close(k->conn));
+}
+
+void init_os(void)
+{
+#ifdef OS2_ADVANCED_HEAP
+ init_os2_heap();
+#endif
+#if defined(RLIMIT_OFILE) && !defined(RLIMIT_NOFILE)
+#define RLIMIT_NOFILE RLIMIT_OFILE
+#endif
+#if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
+ struct rlimit limit;
+ int rs;
+ EINTRLOOP(rs, getrlimit(RLIMIT_NOFILE, &limit));
+ if (rs)
+ goto skip_limit;
+ if (limit.rlim_cur > FD_SETSIZE) {
+ limit.rlim_cur = FD_SETSIZE;
+ EINTRLOOP(rs, setrlimit(RLIMIT_NOFILE, &limit));
+ }
+skip_limit:;
+#endif
+}
+
+#if defined(WIN32)
+
+void get_path_to_exe(void)
+{
+ /* Standard method (argv[0]) doesn't work, if links is executed from
+ symlink --- it returns symlink name and cmd.exe is unable to start
+ it */
+ unsigned r;
+ static unsigned char path[4096];
+ r = GetModuleFileName(NULL, path, sizeof path);
+ if (r <= 0 || r >= sizeof path) {
+ path_to_exe = g_argv[0];
+ return;
+ }
+ path_to_exe = path;
+}
+
+#elif defined(OS2)
+
+static int os2_full_screen = 0;
+static int os2_detached = 0;
+
+void get_path_to_exe(void)
+{
+ /* If you spawn links with quotation marks from cmd.exe,
+ the quotation marks will be present in g_argv[0] ... and will
+ prevent executing it */
+ static unsigned char path[270];
+ PTIB tib = NULL;
+ PPIB pib = NULL;
+ path_to_exe = g_argv[0];
+ DosGetInfoBlocks(&tib, &pib);
+ if (!pib) return;
+ os2_full_screen = pib->pib_ultype == 0;
+ os2_detached = pib->pib_ultype == 4;
+ if (DosQueryModuleName(pib->pib_hmte, sizeof path, path)) return;
+ path_to_exe = path;
+}
+
+int os_get_system_name(unsigned char *buffer)
+{
+ ULONG version[3];
+ if (DosQuerySysInfo(QSV_VERSION_MAJOR, QSV_VERSION_REVISION, version, sizeof version))
+ return -1;
+ if (version[0] == 20) {
+ version[0] = 2;
+ if (version[1] == 10) {
+ version[1] = 1;
+ } else if (version[1] >= 30) {
+ version[0] = version[1] / 10;
+ version[1] %= 10;
+ }
+ }
+ sprintf(buffer, "OS/2 %d.%d i386", (int)version[0], (int)version[1]);
+ return 0;
+}
+
+#else
+
+void get_path_to_exe(void)
+{
+ path_to_exe = g_argv[0];
+}
+
+#endif
+
+void init_os_terminal(void)
+{
+#ifdef INTERIX
+ /* Some sort of terminal bug in Interix, if we run xterm -e links,
+ terminal doesn't switch to raw mode, executing "stty sane" fixes it.
+ Don't do this workaround on console. */
+ unsigned char *term = getenv("TERM");
+ if (!term || strncasecmp(term, "interix", 7)) {
+ int rr;
+ errno = 0;
+ EINTRLOOP(rr, system("stty sane 2>/dev/null"));
+ }
+#endif
+#ifdef OS2
+ if (os2_detached) {
+ error("Links doesn't work in detached session");
+ fatal_tty_exit();
+ exit(RET_FATAL);
+ }
+#endif
+}
+
+#ifdef INTERIX
+
+static inline void cut_program_path(unsigned char *prog, unsigned char **prog_start, unsigned char **prog_end)
+{
+ while (WHITECHAR(*prog)) prog++;
+ if (prog[0] == '"' || prog[0] == '\'') {
+ *prog_start = prog + 1;
+ *prog_end = strchr(prog + 1, prog[0]);
+ if (!*prog_end)
+ *prog_end = strchr(prog, 0);
+ } else {
+ *prog_start = prog;
+ *prog_end = prog + strcspn(prog, " ");
+ }
+}
+
+static inline int is_windows_drive(unsigned char *prog_start, unsigned char *prog_end)
+{
+ if (prog_end - prog_start >= 3 && upcase(prog_start[0]) >= 'A' && upcase(prog_start[0]) <= 'Z' && prog_start[1] == ':')
+ return 1;
+ return 0;
+}
+
+static inline int is_windows_program(unsigned char *prog_start, unsigned char *prog_end)
+{
+ if (prog_end - prog_start > 4 && (
+ !strncasecmp(prog_end - 4, ".exe", 4) ||
+ !strncasecmp(prog_end - 4, ".bat", 4)))
+ return 1;
+ return 0;
+}
+
+#endif
+
+#if defined(WIN32) && defined(HAVE_CYGWIN_CONV_PATH)
+
+unsigned char *os_conv_to_external_path(unsigned char *file, unsigned char *prog)
+{
+ unsigned char *new_path;
+ ssize_t sz;
+ sz = cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, file, NULL, 0);
+ if (sz < 0) return stracpy(file);
+ new_path = mem_alloc(sz);
+ sz = cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, file, new_path, sz);
+ if (sz < 0) {
+ mem_free(new_path);
+ return stracpy(file);
+ }
+ return new_path;
+}
+
+#elif defined(WIN32) && defined(HAVE_CYGWIN_CONV_TO_FULL_WIN32_PATH)
+
+unsigned char *os_conv_to_external_path(unsigned char *file, unsigned char *prog)
+{
+#ifdef MAX_PATH
+ unsigned char new_path[MAX_PATH];
+#else
+ unsigned char new_path[1024];
+#endif
+ *new_path = 0;
+ cygwin_conv_to_full_win32_path(file, new_path);
+ if (!*new_path) return stracpy(file);
+ return stracpy(new_path);
+}
+
+#elif defined(WIN32) && defined(HAVE_UWIN_PATH)
+
+unsigned char *os_conv_to_external_path(unsigned char *file, unsigned char *prog)
+{
+ unsigned char *new_path;
+ ssize_t sz, sz2;
+ sz = uwin_path(file, NULL, 0);
+ if (sz < 0) return stracpy(file);
+ new_path = mem_alloc(sz + 1);
+ sz2 = uwin_path(file, new_path, sz + 1);
+ if (sz2 < 0 || sz2 > sz) {
+ mem_free(new_path);
+ return stracpy(file);
+ }
+ return new_path;
+}
+
+#elif defined(INTERIX) && defined(HAVE_UNIXPATH2WIN)
+
+unsigned char *os_conv_to_external_path(unsigned char *file, unsigned char *prog)
+{
+ unsigned char *prog_start, *prog_end;
+ cut_program_path(prog, &prog_start, &prog_end);
+ /* Convert path only if the program has ".exe" or ".bat" extension */
+ if (is_windows_program(prog_start, prog_end)) {
+#ifdef MAX_PATH
+ unsigned char new_path[MAX_PATH];
+#else
+ unsigned char new_path[512];
+#endif
+ unsigned char *newstr;
+ int newstrl;
+ unsigned char *p;
+ if (unixpath2win(file, 0, new_path, sizeof(new_path)))
+ goto copy_path;
+ /*return stracpy(new_path);*/
+ newstr = init_str();
+ newstrl = 0;
+ for (p = new_path; *p; p++) {
+ /*
+ * Unix shell hates backslash and Windows applications
+ * accept '/'
+ */
+ if (*p == '\\') add_to_str(&newstr, &newstrl, "/");
+ else add_chr_to_str(&newstr, &newstrl, *p);
+ }
+ return newstr;
+ }
+ copy_path:
+ return stracpy(file);
+}
+
+#else
+
+unsigned char *os_conv_to_external_path(unsigned char *file, unsigned char *prog)
+{
+ return stracpy(file);
+}
+
+#endif
+
+#if defined(INTERIX) && defined(HAVE_WINPATH2UNIX)
+
+unsigned char *os_fixup_external_program(unsigned char *prog)
+{
+ unsigned char *prog_start, *prog_end;
+ cut_program_path(prog, &prog_start, &prog_end);
+ if (is_windows_drive(prog_start, prog_end)) {
+#ifdef MAX_PATH
+ unsigned char new_path[MAX_PATH];
+#else
+ unsigned char new_path[1024];
+#endif
+ unsigned char *newstr;
+ int newstrl;
+ unsigned char *xpath;
+ if (is_windows_program(prog_start, prog_end)) {
+ /*
+ * There is some bug in Interix. Executing Win32
+ * binaries works from the console but doesn't work
+ * from xterm. So we prepend "cmd /c" to the program
+ * as a workaround.
+ */
+ newstr = init_str();
+ newstrl = 0;
+ add_to_str(&newstr, &newstrl, "cmd /c ");
+ add_to_str(&newstr, &newstrl, prog);
+ return newstr;
+ }
+ xpath = memacpy(prog_start, prog_end - prog_start);
+ if (winpath2unix(xpath, 0, new_path, sizeof(new_path))) {
+ mem_free(xpath);
+ goto copy_prog;
+ }
+ mem_free(xpath);
+ newstr = init_str();
+ newstrl = 0;
+ add_bytes_to_str(&newstr, &newstrl, prog, prog_start - prog);
+ add_to_str(&newstr, &newstrl, new_path);
+ add_to_str(&newstr, &newstrl, prog_end);
+ return newstr;
+ }
+ copy_prog:
+ return stracpy(prog);
+}
+
+#else
+
+unsigned char *os_fixup_external_program(unsigned char *prog)
+{
+ return stracpy(prog);
+}
+
+#endif
+
+
+#if defined(UNIX) || defined(INTERIX) || defined(BEOS) || defined(RISCOS) || defined(ATHEOS) || defined(SPAD)
+
+#if defined(BEOS) && defined(HAVE_SETPGID)
+
+int exe(unsigned char *path, int fg)
+{
+ pid_t p, rp;
+ int s, rs;
+ EINTRLOOP(p, fork());
+ if (!p) {
+ EINTRLOOP(rs, setpgid(0, 0));
+ errno = 0;
+ EINTRLOOP(rs, system(path));
+ _exit(0);
+ }
+ if (p > 0) {
+ EINTRLOOP(rp, waitpid(p, &s, 0));
+ } else {
+ errno = 0;
+ EINTRLOOP(rs, system(path));
+ return rs;
+ }
+ return 0;
+}
+
+#else
+
+/* UNIX */
+int exe(unsigned char *path, int fg)
+{
+ int rs;
+#ifdef G
+ if (F && drv->exec) return drv->exec(path, fg);
+#endif
+#ifdef SIGTSTP
+ errno = 0;
+ while (signal(SIGTSTP, SIG_DFL) == SIG_ERR && errno == EINTR) errno = 0;
+#endif
+#ifdef SIGCONT
+ errno = 0;
+ while (signal(SIGCONT, SIG_DFL) == SIG_ERR && errno == EINTR) errno = 0;
+#endif
+#ifdef SIGWINCH
+ errno = 0;
+ while (signal(SIGWINCH, SIG_DFL) == SIG_ERR && errno == EINTR) errno = 0;
+#endif
+ errno = 0;
+ EINTRLOOP(rs, system(path));
+ return rs;
+}
+
+#endif
+
+/* clipboard -> links */
+unsigned char *get_clipboard_text(struct terminal *term)
+{
+#ifdef G
+ if (F && drv->get_clipboard_text) {
+ return drv->get_clipboard_text();
+ }
+#endif
+ return stracpy(clipboard);
+}
+
+/* links -> clipboard */
+void set_clipboard_text(struct terminal *term, unsigned char *data)
+{
+#ifdef G
+ if (F && drv->set_clipboard_text) {
+ drv->set_clipboard_text(term->dev, data);
+ return;
+ }
+#endif
+ if (clipboard) mem_free(clipboard);
+ clipboard = stracpy(data);
+}
+
+int clipboard_support(struct terminal *term)
+{
+#ifdef G
+ if (F && drv->set_clipboard_text) {
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+void set_window_title(unsigned char *title)
+{
+ /* !!! FIXME */
+}
+
+unsigned char *get_window_title(void)
+{
+ /* !!! FIXME */
+ return NULL;
+}
+
+int resize_window(int x, int y)
+{
+ return -1;
+}
+
+#elif defined(WIN32)
+
+static int is_winnt(void)
+{
+ OSVERSIONINFO v;
+ v.dwOSVersionInfoSize = sizeof v;
+ if (!GetVersionEx(&v)) return 0;
+ return v.dwPlatformId >= VER_PLATFORM_WIN32_NT;
+}
+
+#define WIN32_START_STRING "start /wait "
+
+int exe(unsigned char *path, int fg)
+{
+ /* This is very tricky. We must have exactly 3 arguments, the first
+ one shell and the second one "/c", otherwise Cygwin would quote
+ the arguments and trash them */
+ int ct;
+ unsigned char buffer[1024];
+ unsigned char buffer2[1024];
+ size_t want_alloc;
+ pid_t pid, rp;
+ int rs;
+ unsigned char *x1;
+ unsigned char *arg;
+ x1 = GETSHELL;
+ if (!x1) x1 = DEFAULT_SHELL;
+
+ want_alloc = strlen(WIN32_START_STRING) + 3 + strlen(path) + 1;
+#ifdef _UWIN
+ want_alloc += strlen(x1) + 4;
+ want_alloc *= 2;
+#endif
+
+ arg = malloc(want_alloc);
+ if (!arg) return -1;
+ *arg = 0;
+#ifdef _UWIN
+ strcat(arg, x1);
+ strcat(arg, " /c ");
+#endif
+ strcat(arg, WIN32_START_STRING);
+ if (*path == '"' && strlen(x1) >= 7 && !strcasecmp(x1 + strlen(x1) - 7, "cmd.exe")) strcat(arg, "\"\" ");
+ strcat(arg, path);
+ ct = GetConsoleTitle(buffer, sizeof buffer);
+#if defined(_UWIN) && !defined(__DMC__)
+ {
+ unsigned char *q1 = arg, *q2 = arg;
+ while (*q1) {
+ if (*q1 == '\\') q2++;
+ q2++;
+ q1++;
+ }
+ while (1) {
+ *q2 = *q1;
+ if (*q1 == '\\') {
+ q2--;
+ *q2 = '\\';
+ }
+ if (q1 == arg) break;
+ q1--;
+ q2--;
+ }
+ }
+ /* UWin corrupts heap if we use threads and fork */
+ pid = spawnl("/bin/sh", "/bin/sh", "-c", arg, NULL);
+#else
+#if 0 /* spawn breaks mouse, don't use this */
+ if (is_winnt()) {
+ /* spawn crashes on Win98 */
+ spawnlp(_P_WAIT, x1, x1, "/c", arg, NULL);
+ goto free_ret;
+ } else
+#endif
+ {
+ EINTRLOOP(pid, fork());
+ if (!pid) {
+ int i;
+ /* Win98 crashes if we spawn command.com and have some sockets open */
+ for (i = 0; i < FD_SETSIZE; i++)
+ EINTRLOOP(rs, close(i));
+ EINTRLOOP(rs, open("nul", O_RDONLY));
+ EINTRLOOP(rs, open("nul", O_WRONLY));
+ EINTRLOOP(rs, open("nul", O_WRONLY));
+ EINTRLOOP(rs, execlp(x1, x1, "/c", arg, NULL));
+ _exit(1);
+ }
+ }
+#endif
+ if (!is_winnt()) {
+ sleep(1);
+ if (ct && GetConsoleTitle(buffer2, sizeof buffer2) && !casecmp(buffer2, "start", 5)) {
+ SetConsoleTitle(buffer);
+ }
+ }
+ if (pid != -1) EINTRLOOP(rp, waitpid(pid, NULL, 0));
+ goto free_ret;
+
+ free_ret:
+ free(arg);
+ return 0;
+}
+
+unsigned char *get_clipboard_text(struct terminal *term)
+{
+ unsigned char buffer[256];
+ unsigned char *str, *s, *d;
+ int l;
+ int r;
+ int rs;
+ int h;
+ EINTRLOOP(h, open("/dev/clipboard", O_RDONLY));
+ if (h == -1) return stracpy(clipboard);
+ set_bin(h); /* O_TEXT doesn't work on clipboard handle */
+ str = init_str();
+ l = 0;
+ /* Don't use hard_read because UWin has buggy end-of-file signalling.
+ It resets the position to the beginning after signalling eof. */
+ while (1) {
+ EINTRLOOP(r, read(h, buffer, sizeof buffer));
+ if (r <= 0) break;
+ add_bytes_to_str(&str, &l, buffer, r);
+ }
+ EINTRLOOP(rs, close(h));
+ for (s = str, d = str; *s; s++)
+ if (!(s[0] == '\r' && s[1] == '\n')) *d++ = *s;
+ *d = 0;
+ return str;
+}
+
+/* Putting Czech characters to clipboard doesn't work, but it should be fixed
+ rather in Cygwin than here */
+void set_clipboard_text(struct terminal *term, unsigned char *data)
+{
+ unsigned char *conv_data;
+ int l;
+ int h;
+ int rs;
+ if (clipboard) mem_free(clipboard);
+ clipboard = stracpy(data);
+ EINTRLOOP(h, open("/dev/clipboard", O_WRONLY));
+ if (h == -1) return;
+ set_bin(h); /* O_TEXT doesn't work on clipboard handle */
+ conv_data = init_str();
+ l = 0;
+ for (; *data; data++)
+ if (*data == '\n') add_to_str(&conv_data, &l, "\r\n");
+ else add_chr_to_str(&conv_data, &l, *data);
+ hard_write(h, conv_data, l);
+ mem_free(conv_data);
+ EINTRLOOP(rs, close(h));
+}
+
+int clipboard_support(struct terminal *term)
+{
+ struct stat st;
+ int rs;
+ EINTRLOOP(rs, stat("/dev/clipboard", &st));
+ return !rs;
+}
+
+
+static int get_windows_cp(void)
+{
+ unsigned char str[6];
+ int cp, idx;
+ static int win_cp_idx = -1;
+ if (win_cp_idx != -1) return win_cp_idx;
+ if (is_winnt())
+ cp = GetConsoleOutputCP();
+ else
+ cp = GetACP();
+ if (cp <= 0 || cp >= 100000) return 0;
+ sprintf(str, "%d", cp);
+ if ((idx = get_cp_index(str)) < 0) return 0;
+ win_cp_idx = idx;
+ return idx;
+}
+
+void set_window_title(unsigned char *title)
+{
+ unsigned char *t, *p;
+ struct conv_table *ct;
+ if (!title) return;
+ if (is_xterm()) return;
+ ct = get_translation_table(utf8_table, get_windows_cp());
+ t = convert_string(ct, title, strlen(title), NULL);
+ for (p = strchr(t, 1); p; p = strchr(p + 1, 1))
+ *p = ' ';
+ SetConsoleTitle(t);
+ mem_free(t);
+}
+
+unsigned char *get_window_title(void)
+{
+ struct conv_table *ct;
+ int r;
+ unsigned char buffer[1024];
+ if (is_xterm()) return NULL;
+ if (!(r = GetConsoleTitle(buffer, sizeof buffer))) return NULL;
+ ct = get_translation_table(get_windows_cp(), utf8_table);
+ return convert_string(ct, buffer, r, NULL);
+}
+
+static void call_resize(unsigned char *x1, int x, int y)
+{
+ pid_t pid, rp;
+ int rs;
+ unsigned char arg[MAX_STR_LEN];
+#ifdef _UWIN
+ x++;
+#endif
+ sprintf(arg, "mode %d,%d", x, y);
+#if defined(_UWIN) && !defined(__DMC__)
+ pid = spawnlp(x1, x1, "/c", arg, NULL);
+#else
+#if 0 /* spawn breaks mouse, don't use this */
+ if (is_winnt()) {
+ /* spawn crashes on Win98 */
+ spawnlp(_P_WAIT, x1, x1, "/c", arg, NULL);
+ return;
+ } else
+#endif
+ {
+ EINTRLOOP(pid, fork());
+ if (!pid) {
+ int i;
+ /* Win98 crashes if we spawn command.com and have some sockets open */
+ for (i = 0; i < FD_SETSIZE; i++) if (i != 1 && i != 2)
+ EINTRLOOP(rs, close(i));
+ EINTRLOOP(rs, open("nul", O_WRONLY));
+ EINTRLOOP(rs, execlp(x1, x1, "/c", arg, NULL));
+ _exit(1);
+ }
+ }
+#endif
+ if (pid != -1) EINTRLOOP(rp, waitpid(pid, NULL, 0));
+}
+
+int resize_window(int x, int y)
+{
+ int old_x, old_y;
+ int ct = 0, fullscreen = 0;
+ unsigned char buffer[1024];
+ unsigned char *x1;
+ if (is_xterm()) return -1;
+ if (get_terminal_size(1, &old_x, &old_y)) return -1;
+ x1 = GETSHELL;
+ if (!x1) x1 = DEFAULT_SHELL;
+ if (!is_winnt()) {
+ ct = GetConsoleTitle(buffer, sizeof buffer);
+ }
+
+ call_resize(x1, x, y);
+ if (!is_winnt()) {
+ int new_x, new_y;
+ /* If we resize console on Win98 in fullscreen mode, it won't be
+ notified by Cygwin (it is valid for all Cygwin apps). So we must
+ switch to windowed mode, resize it again (twice, because resizing
+ to the same size won't have an effect) and switch back to full-screen
+ mode. */
+ /* I'm not sure what's the behavior on WinNT 4. Anybody wants to test?
+ */
+ if (!fullscreen && !get_terminal_size(1, &new_x, &new_y) && (new_x != x || new_y != y)) {
+ fullscreen = 1;
+#ifdef __CYGWIN__
+ keybd_event(VK_MENU, 0x38, 0, 0);
+ keybd_event(VK_RETURN, 0x1c, 0, 0);
+ keybd_event(VK_RETURN, 0x1c, KEYEVENTF_KEYUP, 0);
+ keybd_event(VK_MENU, 0x38, KEYEVENTF_KEYUP, 0);
+#endif
+ if (y != 25) call_resize(x1, 80, 25);
+ else call_resize(x1, 80, 50);
+ call_resize(x1, x, y);
+ if (get_terminal_size(1, &new_x, &new_y) || new_x != x || new_y != y) call_resize(x1, old_x, old_y);
+#ifdef __CYGWIN__
+ keybd_event(VK_MENU, 0x38, 0, 0);
+ keybd_event(VK_RETURN, 0x1c, 0, 0);
+ keybd_event(VK_RETURN, 0x1c, KEYEVENTF_KEYUP, 0);
+ keybd_event(VK_MENU, 0x38, KEYEVENTF_KEYUP, 0);
+#endif
+ }
+ if (ct) SetConsoleTitle(buffer);
+ }
+ return 0;
+}
+
+#elif defined(OS2)
+
+#ifdef G
+static _fmutex fd_mutex;
+static unsigned char fd_mutex_init = 0;
+#endif
+
+int exe(unsigned char *path, int fg)
+{
+ int flags = P_SESSION;
+ pid_t pid, rs;
+ int ret;
+#ifdef G
+ int old0 = 0, old1 = 1, old2 = 2;
+#endif
+ unsigned char *shell;
+ if (!(shell = GETSHELL)) shell = DEFAULT_SHELL;
+ if (is_xterm()) flags |= P_BACKGROUND;
+#ifdef G
+ if (F) {
+ if (!fd_mutex_init) {
+ if (_fmutex_create(&fd_mutex, 0)) return -1;
+ fd_mutex_init = 1;
+ }
+ _fmutex_request(&fd_mutex, _FMR_IGNINT);
+ EINTRLOOP(old0, dup(0));
+ EINTRLOOP(old1, dup(1));
+ EINTRLOOP(old2, dup(2));
+ if (old0 >= 0) EINTRLOOP(rs, close(0));
+ if (old1 >= 0) EINTRLOOP(rs, close(1));
+ if (old2 >= 0) EINTRLOOP(rs, close(2));
+ if (old0 >= 0) EINTRLOOP(rs, open("con", O_RDONLY));
+ if (old1 >= 0) EINTRLOOP(rs, open("con", O_WRONLY));
+ if (old2 >= 0) EINTRLOOP(rs, open("con", O_WRONLY));
+ }
+#endif
+ pid = spawnlp(flags, shell, shell, "/c", path, NULL);
+#ifdef G
+ if (F) {
+ if (old0 >= 0) EINTRLOOP(rs, dup2(old0, 0));
+ if (old1 >= 0) EINTRLOOP(rs, dup2(old1, 1));
+ if (old2 >= 0) EINTRLOOP(rs, dup2(old2, 2));
+ if (old0 >= 0) EINTRLOOP(rs, close(old0));
+ if (old1 >= 0) EINTRLOOP(rs, close(old1));
+ if (old2 >= 0) EINTRLOOP(rs, close(old2));
+ _fmutex_release(&fd_mutex);
+ }
+#endif
+ if (pid != -1) EINTRLOOP(rs, waitpid(pid, &ret, 0));
+ else ret = -1;
+ return ret;
+}
+
+unsigned char *get_clipboard_text(struct terminal *term)
+{
+ PTIB tib;
+ PPIB pib;
+ HAB hab;
+ HMQ hmq;
+ ULONG oldType;
+ unsigned char *ret = NULL;
+
+ DosGetInfoBlocks(&tib, &pib);
+
+ oldType = pib->pib_ultype;
+
+ pib->pib_ultype = 3;
+
+ if ((hab = WinInitialize(0)) != NULLHANDLE) {
+ if ((hmq = WinCreateMsgQueue(hab, 0)) != NULLHANDLE) {
+
+ if (WinOpenClipbrd(hab)) {
+ ULONG fmtInfo = 0;
+
+ if (WinQueryClipbrdFmtInfo(hab, CF_TEXT, &fmtInfo)!=FALSE)
+ {
+ ULONG selClipText = WinQueryClipbrdData(hab, CF_TEXT);
+
+ if (selClipText)
+ {
+ unsigned char *u;
+ PCHAR pchClipText = (PCHAR)selClipText;
+ ret = mem_alloc(strlen(pchClipText)+1);
+ strcpy(ret, pchClipText);
+ while ((u = strchr(ret, 13))) memmove(u, u + 1, strlen(u + 1) + 1);
+ }
+ }
+
+ WinCloseClipbrd(hab);
+ }
+
+#ifdef G
+ if (F && ret) {
+ static int cp = -1;
+ struct conv_table *ct;
+ unsigned char *d;
+ if (cp == -1) {
+ int c = WinQueryCp(hmq);
+ unsigned char a[64];
+ snprintf(a, 64, "%d", c);
+ if ((cp = get_cp_index(a)) < 0 || cp == utf8_table) cp = 0;
+ }
+ ct = get_translation_table(cp, utf8_table);
+ d = convert_string(ct, ret, strlen(ret), NULL);
+ mem_free(ret);
+ ret = d;
+ }
+#endif
+ WinDestroyMsgQueue(hmq);
+ }
+ WinTerminate(hab);
+ }
+
+ pib->pib_ultype = oldType;
+
+ return ret;
+}
+
+void set_clipboard_text(struct terminal * term, unsigned char *data)
+{
+ PTIB tib;
+ PPIB pib;
+ HAB hab;
+ HMQ hmq;
+ ULONG oldType;
+
+ unsigned char *d = NULL;
+
+ DosGetInfoBlocks(&tib, &pib);
+
+ oldType = pib->pib_ultype;
+
+ pib->pib_ultype = 3;
+
+ if ((hab = WinInitialize(0)) != NULLHANDLE) {
+ if ((hmq = WinCreateMsgQueue(hab, 0)) != NULLHANDLE) {
+#ifdef G
+ if (F) {
+ static int cp = -1;
+ struct conv_table *ct;
+ unsigned char *p;
+ if (cp == -1) {
+ int c = WinQueryCp(hmq);
+ unsigned char a[64];
+ snprintf(a, 64, "%d", c);
+ if ((cp = get_cp_index(a)) < 0 || cp == utf8_table) cp = 0;
+ }
+ ct = get_translation_table(utf8_table, cp);
+ d = convert_string(ct, data, strlen(data), NULL);
+ for (p = strchr(d, 1); p; p = strchr(p + 1, 1))
+ *p = ' ';
+ data = d;
+ }
+#endif
+ if (WinOpenClipbrd(hab)) {
+ PVOID pvShrObject = NULL;
+ if (DosAllocSharedMem(&pvShrObject, NULL, strlen(data)+1, PAG_COMMIT | PAG_WRITE | OBJ_GIVEABLE) == NO_ERROR) {
+ strcpy(pvShrObject, data);
+ WinEmptyClipbrd(hab);
+ WinSetClipbrdData(hab, (ULONG)pvShrObject, CF_TEXT, CFI_POINTER);
+ }
+ WinCloseClipbrd(hab);
+ }
+ WinDestroyMsgQueue(hmq);
+ }
+ WinTerminate(hab);
+ }
+
+ pib->pib_ultype = oldType;
+
+ if (d) mem_free(d);
+}
+
+int clipboard_support(struct terminal *term)
+{
+ return 1;
+}
+
+unsigned char *get_window_title(void)
+{
+#ifndef OS2_DEBUG
+ /*char *org_switch_title;*/
+ unsigned char *org_win_title = NULL;
+ static PTIB tib = NULL;
+ static PPIB pib = NULL;
+ ULONG oldType;
+ HSWITCH hSw = NULLHANDLE;
+ SWCNTRL swData;
+ HAB hab;
+ HMQ hmq;
+
+ /* save current process title */
+
+ if (!pib) DosGetInfoBlocks(&tib, &pib);
+ oldType = pib->pib_ultype;
+ memset(&swData, 0, sizeof swData);
+ if (hSw == NULLHANDLE) hSw = WinQuerySwitchHandle(0, pib->pib_ulpid);
+ if (hSw != NULLHANDLE && !WinQuerySwitchEntry(hSw, &swData)) {
+ /*org_switch_title = mem_alloc(strlen(swData.szSwtitle)+1);
+ strcpy(org_switch_title, swData.szSwtitle);*/
+ /* Go to PM */
+ pib->pib_ultype = 3;
+ if ((hab = WinInitialize(0)) != NULLHANDLE) {
+ if ((hmq = WinCreateMsgQueue(hab, 0)) != NULLHANDLE) {
+ org_win_title = mem_alloc(MAXNAMEL+1);
+ WinQueryWindowText(swData.hwnd, MAXNAMEL+1, org_win_title);
+ org_win_title[MAXNAMEL] = 0;
+ /* back From PM */
+ WinDestroyMsgQueue(hmq);
+ }
+ WinTerminate(hab);
+ }
+ pib->pib_ultype = oldType;
+ }
+ return org_win_title;
+#else
+ return NULL;
+#endif
+}
+
+void set_window_title(unsigned char *title)
+{
+#ifndef OS2_DEBUG
+ static PTIB tib;
+ static PPIB pib;
+ ULONG oldType;
+ static HSWITCH hSw;
+ SWCNTRL swData;
+ HAB hab;
+ HMQ hmq;
+ if (!title) return;
+ if (!pib) DosGetInfoBlocks(&tib, &pib);
+ oldType = pib->pib_ultype;
+ memset(&swData, 0, sizeof swData);
+ if (hSw == NULLHANDLE) hSw = WinQuerySwitchHandle(0, pib->pib_ulpid);
+ if (hSw!=NULLHANDLE && !WinQuerySwitchEntry(hSw, &swData)) {
+ safe_strncpy(swData.szSwtitle, title, MAXNAMEL);
+ WinChangeSwitchEntry(hSw, &swData);
+ /* Go to PM */
+ pib->pib_ultype = 3;
+ if ((hab = WinInitialize(0)) != NULLHANDLE) {
+ if ((hmq = WinCreateMsgQueue(hab, 0)) != NULLHANDLE) {
+ if(swData.hwnd)
+ WinSetWindowText(swData.hwnd, title);
+ /* back From PM */
+ WinDestroyMsgQueue(hmq);
+ }
+ WinTerminate(hab);
+ }
+ pib->pib_ultype = oldType;
+ }
+#endif
+}
+
+static tcount resize_count = 0;
+
+int resize_window(int x, int y)
+{
+ int xfont, yfont;
+ A_DECL(VIOMODEINFO, vmi);
+ resize_count++;
+ if (is_xterm()) return -1;
+ vmi->cb = sizeof(*vmi);
+ if (VioGetMode(vmi, 0)) return -1;
+ vmi->col = x;
+ vmi->row = y;
+ /*debug("%d %d %d", vmi->buf_length, vmi->full_length, vmi->partial_length);*/
+ for (xfont = 9; xfont >= 8; xfont--)
+ for (yfont = 16; yfont >= 8; yfont--) {
+ vmi->hres = x * xfont;
+ vmi->vres = y * yfont;
+ if (vmi->vres <= 400) vmi->vres = 400;
+ else if (vmi->vres <= 480) vmi->vres = 480;
+ vmi->buf_length = vmi->full_length = vmi->partial_length = x * ((vmi->vres + yfont - 1) / yfont) * 2;
+ vmi->full_length = (vmi->full_length + 4095) & ~4095;
+ vmi->partial_length = (vmi->partial_length + 4095) & ~4095;
+ if (!VioSetMode(vmi, 0)) return 0;
+ }
+ return -1;
+}
+
+#endif
+
+/* Threads */
+
+#if (defined(HAVE_BEGINTHREAD) && defined(OS2)) || defined(BEOS) || defined(HAVE_PTHREADS) || defined(HAVE_ATHEOS_THREADS_H)
+
+struct tdata {
+ void (*fn)(void *, int);
+ int h;
+ unsigned char data[1];
+};
+
+static void bgt(struct tdata *t)
+{
+ int rs;
+ ignore_signals();
+ t->fn(t->data, t->h);
+ EINTRLOOP(rs, write(t->h, "x", 1));
+ EINTRLOOP(rs, close(t->h));
+ free(t);
+}
+
+#ifdef HAVE_PTHREADS
+static void *bgpt(struct tdata *t)
+{
+ bgt(t);
+ return NULL;
+}
+#endif
+
+#ifdef HAVE_ATHEOS_THREADS_H
+#include <atheos/threads.h>
+static uint32 abgt(void *t)
+{
+ bgt(t);
+ return 0;
+}
+#endif
+
+#endif
+
+#if defined(UNIX) || defined(OS2) || defined(WIN32) || defined(INTERIX) || defined(RISCOS) || defined(ATHEOS) || defined(SPAD)
+
+void terminate_osdep(void) {}
+
+#endif
+
+#ifndef BEOS
+
+void block_stdin(void) {}
+void unblock_stdin(void) {}
+
+#endif
+
+#if defined(BEOS)
+
+#include <be/kernel/OS.h>
+
+static int thr_sem_init = 0;
+static sem_id thr_sem;
+
+static struct list_head active_threads = { &active_threads, &active_threads };
+
+struct active_thread {
+ struct active_thread *next;
+ struct active_thread *prev;
+ thread_id tid;
+ void (*fn)(void *);
+ void *data;
+};
+
+static int32 started_thr(void *data)
+{
+ struct active_thread *thrd = data;
+ thrd->fn(thrd->data);
+ if (acquire_sem(thr_sem) < B_NO_ERROR) return 0;
+ del_from_list(thrd);
+ free(thrd);
+ release_sem(thr_sem);
+ return 0;
+}
+
+int start_thr(void (*fn)(void *), void *data, unsigned char *name)
+{
+ struct active_thread *thrd;
+ int tid;
+ if (!thr_sem_init) {
+ if ((thr_sem = create_sem(0, "thread_sem")) < B_NO_ERROR) return -1;
+ thr_sem_init = 1;
+ } else if (acquire_sem(thr_sem) < B_NO_ERROR) return -1;
+ retry:
+ if (!(thrd = malloc(sizeof(struct active_thread)))) {
+ if (out_of_memory(NULL, 0))
+ goto retry;
+ goto rel;
+ }
+ thrd->fn = fn;
+ thrd->data = data;
+ if ((tid = thrd->tid = spawn_thread(started_thr, name, B_NORMAL_PRIORITY, thrd)) < B_NO_ERROR) {
+ free(thrd);
+ rel:
+ release_sem(thr_sem);
+ return -1;
+ }
+ resume_thread(thrd->tid);
+ add_to_list(active_threads, thrd);
+ release_sem(thr_sem);
+ return tid;
+}
+
+void terminate_osdep(void)
+{
+ struct list_head *p;
+ struct active_thread *thrd;
+ if (acquire_sem(thr_sem) < B_NO_ERROR) return;
+ foreach(thrd, active_threads) kill_thread(thrd->tid);
+ while ((p = active_threads.next) != &active_threads) {
+ del_from_list(p);
+ free(p);
+ }
+ release_sem(thr_sem);
+}
+
+int start_thread(void (*fn)(void *, int), void *ptr, int l)
+{
+ int p[2];
+ struct tdata *t;
+ int rs;
+ if (c_pipe(p) < 0) return -1;
+ retry:
+ if (!(t = malloc(sizeof(struct tdata) + l))) {
+ if (out_of_memory(NULL, 0))
+ goto retry;
+ return -1;
+ }
+ t->fn = fn;
+ t->h = p[1];
+ memcpy(t->data, ptr, l);
+ if (start_thr((void (*)(void *))bgt, t, "links_thread") < 0) {
+ EINTRLOOP(rs, close(p[0]));
+ EINTRLOOP(rs, close(p[1]));
+ free(t);
+ return -1;
+ }
+ return p[0];
+}
+
+
+#elif defined(HAVE_BEGINTHREAD) && defined(OS2)
+
+int start_thread(void (*fn)(void *, int), void *ptr, int l)
+{
+ int p[2];
+ struct tdata *t;
+ int rs;
+ if (c_pipe(p) < 0) return -1;
+ EINTRLOOP(rs, fcntl(p[0], F_SETFL, O_NONBLOCK));
+ EINTRLOOP(rs, fcntl(p[1], F_SETFL, O_NONBLOCK));
+ retry:
+ if (!(t = malloc(sizeof(struct tdata) + l))) {
+ if (out_of_memory(NULL, 0))
+ goto retry;
+ return -1;
+ }
+ t->fn = fn;
+ t->h = p[1];
+ memcpy(t->data, ptr, l);
+ if (_beginthread((void (*)(void *))bgt, NULL, 65536, t) == -1) {
+ EINTRLOOP(rs, close(p[0]));
+ EINTRLOOP(rs, close(p[1]));
+ free(t);
+ return -1;
+ }
+ return p[0];
+}
+
+#ifdef HAVE__READ_KBD
+
+static int tp = -1;
+static int ti = -1;
+
+static void input_thread(void *p)
+{
+ unsigned char c[2];
+ int h = (int)p;
+ int rs;
+ ignore_signals();
+ while (1) {
+ /* for the records:
+ _read_kbd(0, 1, 1) will
+ read a char, don't echo it, wait for one available and
+ accept CTRL-C.
+ Knowing that, I suggest we replace this call completly!
+ */
+ *c = _read_kbd(0, 1, 1);
+ EINTRLOOP(rs, write(h, c, 1));
+ }
+ EINTRLOOP(rs, close(h));
+}
+#endif /* #ifdef HAVE__READ_KBD */
+
+#if defined(HAVE_MOUOPEN) && !defined(USE_GPM)
+
+#define USING_OS2_MOUSE
+
+#ifdef HAVE_SYS_FMUTEX_H
+static _fmutex mouse_mutex;
+static unsigned char mouse_mutex_init = 0;
+#endif
+static int mouse_h = -1;
+
+struct os2_mouse_spec {
+ int p[2];
+ void (*fn)(void *, unsigned char *, int);
+ void *data;
+ unsigned char buffer[sizeof(struct event)];
+ int bufptr;
+ int terminate;
+};
+
+#define MOU_EMULATE_CURSOR
+
+#ifdef MOU_EMULATE_CURSOR
+static int mouse_x = -1, mouse_y = -1;
+static unsigned char mouse_attr;
+#endif
+
+static void mouse_remove_pointer(void)
+{
+#ifndef MOU_EMULATE_CURSOR
+ A_DECL(NOPTRRECT, pa);
+ static int x = -1, y = -1;
+ static tcount c = -1;
+ if (x == -1 || y == -1 || (c != resize_count)) get_terminal_size(1, &x, &y), c = resize_count;
+ pa->row = 0;
+ pa->col = 0;
+ pa->cRow = y - 1;
+ pa->cCol = x - 1;
+ MouRemovePtr(pa, mouse_h);
+#else
+ if (mouse_x >= 0 && mouse_y >= 0) {
+ VioWrtNAttr(&mouse_attr, 1, mouse_y, mouse_x, 0);
+ }
+ mouse_x = -1, mouse_y = -1;
+#endif
+}
+
+static void mouse_draw_pointer(int x, int y)
+{
+#ifndef MOU_EMULATE_CURSOR
+ MouDrawPtr(mouse_h);
+#else
+ unsigned char str[4];
+ USHORT str_len;
+ unsigned char attr;
+ unsigned char fg, bg;
+ int r;
+ if (!os2_full_screen)
+ return;
+ DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
+ if (mouse_x == x && mouse_y == y)
+ return;
+ mouse_remove_pointer();
+ str_len = sizeof(str);
+ r = VioReadCellStr(str, &str_len, y, x, 0);
+ if (r || str_len < 2) return;
+ mouse_attr = str[1];
+ fg = mouse_attr & 0x07;
+ bg = (mouse_attr & 0x70) >> 4;
+ if (fg == bg) fg ^= 0x07, bg ^= 0x07;
+ attr = (mouse_attr & 0x88) | (fg << 4) | bg;
+ r = VioWrtNAttr(&attr, 1, y, x, 0);
+ if (r) return;
+ mouse_x = x, mouse_y = y, mouse_attr = str[1];
+#endif
+}
+
+static void mouse_thread(void *p)
+{
+ int status;
+ int rs;
+ struct os2_mouse_spec *oms = p;
+ A_DECL(HMOU, mh);
+ A_DECL(MOUEVENTINFO, ms);
+ A_DECL(USHORT, rd);
+ A_DECL(USHORT, mask);
+ struct event ev;
+ ignore_signals();
+ ev.ev = EV_MOUSE;
+ if (MouOpen(NULL, mh)) goto ret;
+ mouse_h = *mh;
+ *mask = MOUSE_MOTION_WITH_BN1_DOWN | MOUSE_BN1_DOWN |
+ MOUSE_MOTION_WITH_BN2_DOWN | MOUSE_BN2_DOWN |
+ MOUSE_MOTION_WITH_BN3_DOWN | MOUSE_BN3_DOWN |
+ MOUSE_MOTION;
+ MouSetEventMask(mask, *mh);
+ *rd = MOU_WAIT;
+ status = -1;
+ while (1) {
+ /*int w, ww;*/
+ if (MouReadEventQue(ms, rd, *mh)) break;
+#ifdef HAVE_SYS_FMUTEX_H
+ _fmutex_request(&mouse_mutex, _FMR_IGNINT);
+#endif
+ if (!oms->terminate) mouse_draw_pointer(ms->col, ms->row);
+#ifdef HAVE_SYS_FMUTEX_H
+ _fmutex_release(&mouse_mutex);
+#endif
+ ev.x = ms->col;
+ ev.y = ms->row;
+ /*debug("status: %d %d %d", ms->col, ms->row, ms->fs);*/
+ if (ms->fs & (MOUSE_BN1_DOWN | MOUSE_BN2_DOWN | MOUSE_BN3_DOWN)) ev.b = status = B_DOWN | (ms->fs & MOUSE_BN1_DOWN ? B_LEFT : ms->fs & MOUSE_BN2_DOWN ? B_RIGHT : B_MIDDLE);
+ else if (ms->fs & (MOUSE_MOTION_WITH_BN1_DOWN | MOUSE_MOTION_WITH_BN2_DOWN | MOUSE_MOTION_WITH_BN3_DOWN)) {
+ int b = ms->fs & MOUSE_MOTION_WITH_BN1_DOWN ? B_LEFT : ms->fs & MOUSE_MOTION_WITH_BN2_DOWN ? B_RIGHT : B_MIDDLE;
+ if (status == -1) b |= B_DOWN;
+ else b |= B_DRAG;
+ ev.b = status = b;
+ }
+ else {
+ if (status == -1) continue;
+ ev.b = (status & BM_BUTT) | B_UP;
+ status = -1;
+ }
+ if (hard_write(oms->p[1], (unsigned char *)&ev, sizeof(struct event)) != sizeof(struct event)) break;
+ }
+#ifdef HAVE_SYS_FMUTEX_H
+ _fmutex_request(&mouse_mutex, _FMR_IGNINT);
+#endif
+ mouse_h = -1;
+ MouClose(*mh);
+#ifdef HAVE_SYS_FMUTEX_H
+ _fmutex_release(&mouse_mutex);
+#endif
+ ret:
+ EINTRLOOP(rs, close(oms->p[1]));
+ /*free(oms);*/
+}
+
+static void mouse_handle(struct os2_mouse_spec *oms)
+{
+ int r;
+ EINTRLOOP(r, read(oms->p[0], oms->buffer + oms->bufptr, sizeof(struct event) - oms->bufptr));
+ if (r <= 0) {
+ unhandle_mouse(oms);
+ return;
+ }
+ if ((oms->bufptr += r) == sizeof(struct event)) {
+ oms->bufptr = 0;
+ oms->fn(oms->data, oms->buffer, sizeof(struct event));
+ }
+}
+
+void *handle_mouse(int cons, void (*fn)(void *, unsigned char *, int), void *data)
+{
+ struct os2_mouse_spec *oms;
+ if (is_xterm()) return NULL;
+#ifdef HAVE_SYS_FMUTEX_H
+ if (!mouse_mutex_init) {
+ if (_fmutex_create(&mouse_mutex, 0)) return NULL;
+ mouse_mutex_init = 1;
+ }
+#endif
+ /* This is never freed but it's allocated only once */
+ retry:
+ if (!(oms = malloc(sizeof(struct os2_mouse_spec)))) {
+ if (out_of_memory(NULL, 0))
+ goto retry;
+ return NULL;
+ }
+ oms->fn = fn;
+ oms->data = data;
+ oms->bufptr = 0;
+ oms->terminate = 0;
+ if (c_pipe(oms->p)) {
+ free(oms);
+ return NULL;
+ }
+ if (_beginthread(mouse_thread, NULL, 0x10000, (void *)oms) == -1) {
+ }
+ set_handlers(oms->p[0], (void (*)(void *))mouse_handle, NULL, NULL, oms);
+ return oms;
+}
+
+void unhandle_mouse(void *om)
+{
+ struct os2_mouse_spec *oms = om;
+ int rs;
+ want_draw();
+ oms->terminate = 1;
+ set_handlers(oms->p[0], NULL, NULL, NULL, NULL);
+ EINTRLOOP(rs, close(oms->p[0]));
+ done_draw();
+}
+
+void want_draw(void)
+{
+ static int ansi = 0;
+#ifdef HAVE_SYS_FMUTEX_H
+ if (mouse_mutex_init) _fmutex_request(&mouse_mutex, _FMR_IGNINT);
+#endif
+ if (!ansi) {
+ VioSetAnsi(1, 0);
+ ansi = 1;
+ }
+ if (mouse_h != -1) {
+ mouse_remove_pointer();
+ }
+}
+
+void done_draw(void)
+{
+#ifdef HAVE_SYS_FMUTEX_H
+ if (mouse_mutex_init) _fmutex_release(&mouse_mutex);
+#endif
+}
+
+#endif /* if HAVE_MOUOPEN */
+
+#elif defined(HAVE_PTHREADS)
+
+#include <pthread.h>
+
+int start_thread(void (*fn)(void *, int), void *ptr, int l)
+{
+ pthread_t thread;
+ struct tdata *t;
+ int p[2];
+ int rs;
+ if (c_pipe(p) < 0) return -1;
+ EINTRLOOP(rs, fcntl(p[0], F_SETFL, O_NONBLOCK));
+ EINTRLOOP(rs, fcntl(p[1], F_SETFL, O_NONBLOCK));
+ retry:
+ if (!(t = malloc(sizeof(struct tdata) + l))) {
+ if (out_of_memory(NULL, 0))
+ goto retry;
+ return -1;
+ }
+ t->fn = fn;
+ t->h = p[1];
+ memcpy(t->data, ptr, l);
+ if (pthread_create(&thread, NULL, (void *(*)(void *))bgpt, t)) {
+ EINTRLOOP(rs, close(p[0]));
+ EINTRLOOP(rs, close(p[1]));
+ free(t);
+ return -1;
+ }
+ return p[0];
+}
+
+#elif defined(HAVE_ATHEOS_THREADS_H) && defined(HAVE_SPAWN_THREAD) && defined(HAVE_RESUME_THREAD)
+
+#include <atheos/threads.h>
+
+int start_thread(void (*fn)(void *, int), void *ptr, int l)
+{
+ int p[2];
+ int rs;
+ thread_id f;
+ struct tdata *t;
+ if (c_pipe(p) < 0) return -1;
+ EINTRLOOP(rs, fcntl(p[0], F_SETFL, O_NONBLOCK));
+ EINTRLOOP(rs, fcntl(p[1], F_SETFL, O_NONBLOCK));
+ retry:
+ if (!(t = malloc(sizeof(struct tdata) + l))) {
+ if (out_of_memory(NULL, 0))
+ goto retry;
+ return -1;
+ }
+ t->fn = fn;
+ t->h = p[1];
+ memcpy(t->data, ptr, l);
+ if ((f = spawn_thread("links_lookup", abgt, 0, 0, t)) == -1) {
+ EINTRLOOP(rs, close(p[0]));
+ EINTRLOOP(rs, close(p[1]));
+ mem_free(t);
+ return -1;
+ }
+ resume_thread(f);
+ return p[0];
+}
+
+#else /* HAVE_BEGINTHREAD */
+
+int start_thread(void (*fn)(void *, int), void *ptr, int l)
+{
+ int p[2];
+ pid_t f;
+ int rs;
+ if (c_pipe(p) < 0) return -1;
+ EINTRLOOP(rs, fcntl(p[0], F_SETFL, O_NONBLOCK));
+ EINTRLOOP(rs, fcntl(p[1], F_SETFL, O_NONBLOCK));
+ EINTRLOOP(f, fork());
+ if (!f) {
+ close_fork_tty();
+ EINTRLOOP(rs, close(p[0]));
+ fn(ptr, p[1]);
+ EINTRLOOP(rs, write(p[1], "x", 1));
+ EINTRLOOP(rs, close(p[1]));
+ _exit(0);
+ }
+ if (f == -1) {
+ EINTRLOOP(rs, close(p[0]));
+ EINTRLOOP(rs, close(p[1]));
+ return -1;
+ }
+ EINTRLOOP(rs, close(p[1]));
+ return p[0];
+}
+
+#endif
+
+#ifndef USING_OS2_MOUSE
+void want_draw(void) {}
+void done_draw(void) {}
+#endif
+
+int get_output_handle(void) { return 1; }
+
+#if defined(OS2)
+
+int get_ctl_handle(void) { return get_input_handle(); }
+
+#else
+
+int get_ctl_handle(void) { return 0; }
+
+#endif
+
+#if defined(BEOS)
+
+#elif defined(HAVE_BEGINTHREAD) && defined(HAVE__READ_KBD)
+int get_input_handle(void)
+{
+ int fd[2];
+ if (ti != -1) return ti;
+ if (is_xterm()) return 0;
+ if (c_pipe(fd) < 0) return 0;
+ ti = fd[0];
+ tp = fd[1];
+ _beginthread(input_thread, NULL, 0x10000, (void *)tp);
+/*
+#if defined(HAVE_MOUOPEN) && !defined(USE_GPM)
+ _beginthread(mouse_thread, NULL, 0x10000, (void *)tp);
+#endif
+*/
+ return fd[0];
+}
+
+#else
+
+int get_input_handle(void)
+{
+ return 0;
+}
+
+#endif /* defined(HAVE_BEGINTHREAD) && defined(HAVE__READ_KBD) */
+
+
+void os_cfmakeraw(struct termios *t)
+{
+#ifdef HAVE_CFMAKERAW
+ cfmakeraw(t);
+#ifdef VMIN
+ t->c_cc[VMIN] = 1;
+#endif
+#else
+ t->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
+ t->c_oflag &= ~OPOST;
+ t->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
+ t->c_cflag &= ~(CSIZE|PARENB);
+ t->c_cflag |= CS8;
+ t->c_cc[VMIN] = 1;
+ t->c_cc[VTIME] = 0;
+#endif
+#if defined(NO_CTRL_Z) && defined(VSUSP)
+ t->c_cc[VSUSP] = 0;
+#endif
+ /*fprintf(stderr, "\n");
+ fprintf(stderr, "%08x %08x %08x\n", t->c_iflag, t->c_oflag, t->c_lflag);
+ {
+ int i;
+ for (i = 0; i < sizeof(t->c_cc) / sizeof(*t->c_cc); i++) {
+ fprintf(stderr, "%d - %d\n", i, t->c_cc[i]);
+ }
+ }
+ sleep(10);*/
+}
+
+#ifdef USE_GPM
+
+struct gpm_mouse_spec {
+ int h;
+ void (*fn)(void *, unsigned char *, int);
+ void *data;
+};
+
+/* GPM installs its own signal handlers and we don't want them */
+
+static sigset_t gpm_sigset;
+static unsigned char gpm_sigset_valid;
+#ifdef SIGWINCH
+static struct sigaction gpm_winch;
+static unsigned char gpm_winch_valid;
+#endif
+#ifdef SIGTSTP
+static struct sigaction gpm_tstp;
+static unsigned char gpm_tstp_valid;
+#endif
+
+static void save_gpm_signals(void)
+{
+ sigset_t sig;
+ int rs;
+ sigemptyset(&sig);
+#ifdef SIGWINCH
+ sigaddset(&sig, SIGWINCH);
+#endif
+#ifdef SIGTSTP
+ sigaddset(&sig, SIGTSTP);
+#endif
+ EINTRLOOP(rs, sigprocmask(SIG_BLOCK, &sig, &gpm_sigset));
+ gpm_sigset_valid = !rs;
+#ifdef SIGWINCH
+ EINTRLOOP(rs, sigaction(SIGWINCH, NULL, &gpm_winch));
+ gpm_winch_valid = !rs;
+#endif
+#ifdef SIGTSTP
+ EINTRLOOP(rs, sigaction(SIGTSTP, NULL, &gpm_tstp));
+ gpm_tstp_valid = !rs;
+#endif
+}
+
+static void restore_gpm_signals(void)
+{
+ int rs;
+#ifdef SIGWINCH
+ if (gpm_winch_valid)
+ EINTRLOOP(rs, sigaction(SIGWINCH, &gpm_winch, NULL));
+#endif
+#ifdef SIGTSTP
+ if (gpm_tstp_valid)
+ EINTRLOOP(rs, sigaction(SIGTSTP, &gpm_tstp, NULL));
+#endif
+ if (gpm_sigset_valid)
+ EINTRLOOP(rs, sigprocmask(SIG_SETMASK, &gpm_sigset, NULL));
+}
+
+static void gpm_mouse_in(struct gpm_mouse_spec *gms)
+{
+ int g;
+ Gpm_Event gev;
+ struct event ev;
+ save_gpm_signals();
+ g = Gpm_GetEvent(&gev);
+ restore_gpm_signals();
+ if (g <= 0) {
+ set_handlers(gms->h, NULL, NULL, NULL, NULL);
+ gms->h = -1;
+ return;
+ }
+ ev.ev = EV_MOUSE;
+ ev.x = gev.x - 1;
+ ev.y = gev.y - 1;
+ if (ev.x < 0) ev.x = 0;
+ if (ev.y < 0) ev.y = 0;
+ if (gev.buttons & GPM_B_LEFT) ev.b = B_LEFT;
+ else if (gev.buttons & GPM_B_MIDDLE) ev.b = B_MIDDLE;
+ else if (gev.buttons & GPM_B_RIGHT) ev.b = B_RIGHT;
+ else return;
+ if (gev.type & GPM_DOWN) ev.b |= B_DOWN;
+ else if (gev.type & GPM_UP) ev.b |= B_UP;
+ else if (gev.type & GPM_DRAG) ev.b |= B_DRAG;
+ else return;
+ gms->fn(gms->data, (unsigned char *)&ev, sizeof(struct event));
+}
+
+void *handle_mouse(int cons, void (*fn)(void *, unsigned char *, int), void *data)
+{
+ int h;
+ Gpm_Connect conn;
+ struct gpm_mouse_spec *gms;
+ conn.eventMask = ~GPM_MOVE;
+ conn.defaultMask = GPM_MOVE;
+ conn.minMod = 0;
+ conn.maxMod = 0;
+ save_gpm_signals();
+ h = Gpm_Open(&conn, cons);
+ restore_gpm_signals();
+ if (h < 0) return NULL;
+ gms = mem_alloc(sizeof(struct gpm_mouse_spec));
+ gms->h = h;
+ gms->fn = fn;
+ gms->data = data;
+ set_handlers(h, (void (*)(void *))gpm_mouse_in, NULL, NULL, gms);
+ return gms;
+}
+
+void unhandle_mouse(void *h)
+{
+ struct gpm_mouse_spec *gms = h;
+ if (gms->h != -1) set_handlers(gms->h, NULL, NULL, NULL, NULL);
+ save_gpm_signals();
+ Gpm_Close();
+ restore_gpm_signals();
+ mem_free(gms);
+}
+
+void add_gpm_version(unsigned char **s, int *l)
+{
+ add_to_str(s, l, "GPM");
+#ifdef HAVE_GPM_GETLIBVERSION
+ add_to_str(s, l, " (");
+ add_to_str(s, l, (unsigned char *)Gpm_GetLibVersion(NULL));
+ add_to_str(s, l, ")");
+#endif
+}
+
+#elif !defined(USING_OS2_MOUSE)
+
+void *handle_mouse(int cons, void (*fn)(void *, unsigned char *, int), void *data) { return NULL; }
+void unhandle_mouse(void *data) { }
+
+#endif /* #ifdef USE_GPM */
+
+
+#if defined(WIN32) || defined(INTERIX)
+
+static int is_remote_connection(void)
+{
+ return !!getenv("SSH_CONNECTION");
+}
+
+#endif
+
+
+#if defined(OS2)
+
+int get_system_env(void)
+{
+ if (is_xterm()) return 0;
+ return ENV_OS2VIO; /* !!! FIXME: telnet */
+}
+
+#elif defined(BEOS)
+
+int get_system_env(void)
+{
+ unsigned char *term = getenv("TERM");
+ if (!term || (upcase(term[0]) == 'B' && upcase(term[1]) == 'E')) return ENV_BE;
+ return 0;
+}
+
+#elif defined(WIN32)
+
+int get_system_env(void)
+{
+ if (is_xterm()) return 0;
+ if (is_remote_connection()) return 0;
+ return ENV_WIN32;
+}
+
+#elif defined(INTERIX)
+
+#define INTERIX_START_COMMAND "/usr/contrib/win32/bin/start"
+
+int get_system_env(void)
+{
+ if (is_xterm()) return 0;
+ if (is_remote_connection()) return 0;
+ if (!access(INTERIX_START_COMMAND, X_OK)) return ENV_INTERIX;
+ return 0;
+}
+
+#else
+
+int get_system_env(void)
+{
+ return 0;
+}
+
+#endif
+
+static void exec_new_links(struct terminal *term, unsigned char *xterm, unsigned char *exe, unsigned char *param)
+{
+ unsigned char *str;
+ str = mem_alloc(strlen(xterm) + 1 + strlen(exe) + 1 + strlen(param) + 1);
+ if (*xterm) sprintf(str, "%s %s %s", xterm, exe, param);
+ else sprintf(str, "%s %s", exe, param);
+ exec_on_terminal(term, str, "", 2);
+ mem_free(str);
+}
+
+static int open_in_new_twterm(struct terminal *term, unsigned char *exe, unsigned char *param)
+{
+ unsigned char *twterm;
+ if (!(twterm = getenv("LINKS_TWTERM"))) twterm = "twterm -e";
+ exec_new_links(term, twterm, exe, param);
+ return 0;
+}
+
+static int open_in_new_xterm(struct terminal *term, unsigned char *exe, unsigned char *param)
+{
+ unsigned char *xterm;
+ if (!(xterm = getenv("LINKS_XTERM"))) xterm = "xterm -e";
+ exec_new_links(term, xterm, exe, param);
+ return 0;
+}
+
+static int open_in_new_screen(struct terminal *term, unsigned char *exe, unsigned char *param)
+{
+ exec_new_links(term, "screen", exe, param);
+ return 0;
+}
+
+#ifdef OS2
+static int open_in_new_vio(struct terminal *term, unsigned char *exe, unsigned char *param)
+{
+ unsigned char *x = stracpy("\"");
+ add_to_strn(&x, exe);
+ add_to_strn(&x, "\"");
+ exec_new_links(term, "start \"Links\" /c /f /win", x, param);
+ mem_free(x);
+ return 0;
+}
+
+static int open_in_new_fullscreen(struct terminal *term, unsigned char *exe, unsigned char *param)
+{
+ unsigned char *x = stracpy("\"");
+ add_to_strn(&x, exe);
+ add_to_strn(&x, "\"");
+ exec_new_links(term, "start \"Links\" /c /f /fs", x, param);
+ mem_free(x);
+ return 0;
+}
+#endif
+
+#ifdef WIN32
+static int open_in_new_win32(struct terminal *term, unsigned char *exe, unsigned char *param)
+{
+ exec_new_links(term, "", exe, param);
+ return 0;
+}
+#endif
+
+#ifdef INTERIX
+static int open_in_new_interix(struct terminal *term, unsigned char *exe, unsigned char *param)
+{
+ unsigned char *param_x = stracpy(param);
+ add_to_strn(&param_x, "'");
+ exec_new_links(term, INTERIX_START_COMMAND " '\"Links\"' posix /u /c /bin/sh -c '", exe, param_x);
+ mem_free(param_x);
+ return 0;
+}
+#endif
+
+#ifdef BEOS
+static int open_in_new_be(struct terminal *term, unsigned char *exe, unsigned char *param)
+{
+ exec_new_links(term, "Terminal", exe, param);
+ return 0;
+}
+#endif
+
+#ifdef G
+static int open_in_new_g(struct terminal *term, unsigned char *exe, unsigned char *param)
+{
+ void *info;
+ unsigned char *target = NULL;
+ int len;
+ int base = 0;
+ unsigned char *url;
+ if (!cmpbeg(param, "-base-session ")) {
+ param = strchr(param, ' ') + 1;
+ base = atoi(param);
+ param += strcspn(param, " ");
+ if (*param == ' ') param++;
+ }
+ if (!cmpbeg(param, "-target ")) {
+ param = strchr(param, ' ') + 1;
+ target = param;
+ param += strcspn(param, " ");
+ if (*param == ' ') *param++ = 0;
+ }
+ url = param;
+ if (!(info = create_session_info(base, url, target, &len)))
+ return -1;
+ return attach_g_terminal(info, len);
+}
+#endif
+
+static struct {
+ int env;
+ int (*open_window_fn)(struct terminal *term, unsigned char *, unsigned char *);
+ unsigned char *text;
+ unsigned char *hk;
+} oinw[] = {
+ {ENV_XWIN, open_in_new_xterm, TEXT_(T_XTERM), TEXT_(T_HK_XTERM)},
+ {ENV_TWIN, open_in_new_twterm, TEXT_(T_TWTERM), TEXT_(T_HK_TWTERM)},
+ {ENV_SCREEN, open_in_new_screen, TEXT_(T_SCREEN), TEXT_(T_HK_SCREEN)},
+#ifdef OS2
+ {ENV_OS2VIO, open_in_new_vio, TEXT_(T_WINDOW), TEXT_(T_HK_WINDOW)},
+ {ENV_OS2VIO, open_in_new_fullscreen, TEXT_(T_FULL_SCREEN), TEXT_(T_HK_FULL_SCREEN)},
+#endif
+#ifdef WIN32
+ {ENV_WIN32, open_in_new_win32, TEXT_(T_WINDOW), TEXT_(T_HK_WINDOW)},
+#endif
+#ifdef INTERIX
+ {ENV_INTERIX, open_in_new_interix, TEXT_(T_WINDOW), TEXT_(T_HK_WINDOW)},
+#endif
+#ifdef BEOS
+ {ENV_BE, open_in_new_be, TEXT_(T_BEOS_TERMINAL), TEXT_(T_HK_BEOS_TERMINAL)},
+#endif
+#ifdef G
+ {ENV_G, open_in_new_g, TEXT_(T_WINDOW), TEXT_(T_HK_WINDOW)},
+#endif
+ {0, NULL, NULL, NULL}
+};
+
+struct open_in_new *get_open_in_new(int environment)
+{
+ int i;
+ struct open_in_new *oin = DUMMY;
+ int noin = 0;
+ if (anonymous) return NULL;
+ if (environment & ENV_G) environment = ENV_G;
+ for (i = 0; oinw[i].env; i++) if ((environment & oinw[i].env) == oinw[i].env) {
+ if ((unsigned)noin > MAXINT / sizeof(struct open_in_new) - 2) overalloc();
+ oin = mem_realloc(oin, (noin + 2) * sizeof(struct open_in_new));
+ oin[noin].text = oinw[i].text;
+ oin[noin].hk = oinw[i].hk;
+ oin[noin].open_window_fn = oinw[i].open_window_fn;
+ noin++;
+ oin[noin].text = NULL;
+ oin[noin].hk = NULL;
+ oin[noin].open_window_fn = NULL;
+ }
+ if (oin == DUMMY) return NULL;
+ return oin;
+}
+
+int can_resize_window(struct terminal *term)
+{
+#if defined(OS2) || defined(WIN32)
+ if (!strncmp(term->term, "xterm", 5)) return 0;
+ if (term->environment & (ENV_OS2VIO | ENV_WIN32)) return 1;
+#endif
+ return 0;
+}
+
+int can_open_os_shell(int environment)
+{
+#ifdef OS2
+ if (environment & ENV_XWIN) return 0;
+#endif
+#ifdef WIN32
+ if (!(environment & ENV_WIN32)) return 0;
+#endif
+#ifdef BEOS
+ if (!(environment & ENV_BE)) return 0;
+#endif
+#ifdef G
+ if (F && drv->flags & GD_NO_OS_SHELL) return 0;
+#endif
+ return 1;
+}
+
+#ifndef OS2
+void set_highpri(void)
+{
+}
+#else
+void set_highpri(void)
+{
+ DosSetPriority(PRTYS_PROCESS, PRTYC_FOREGROUNDSERVER, 0, 0);
+}
+#endif
+
+#ifndef HAVE_SNPRINTF
+
+#define B_SZ 65536
+
+static char snprtintf_buffer[B_SZ];
+
+int my_snprintf(char *str, int n, char *f, ...)
+{
+ int i;
+ va_list l;
+ if (!n) return -1;
+ va_start(l, f);
+ vsprintf(snprtintf_buffer, f, l);
+ va_end(l);
+ i = strlen(snprtintf_buffer);
+ if (i >= B_SZ) {
+ error("String size too large!");
+ fatal_tty_exit();
+ exit(RET_FATAL);
+ }
+ if (i >= n) {
+ memcpy(str, snprtintf_buffer, n);
+ str[n - 1] = 0;
+ return -1;
+ }
+ strcpy(str, snprtintf_buffer);
+ return i;
+}
+
+#endif
+
+#ifndef HAVE_RAISE
+int raise(int s)
+{
+#ifdef HAVE_GETPID
+ pid_t p;
+ EINTRLOOP(p, getpid());
+ if (p == -1) return -1;
+ return kill(p, s);
+#else
+ return 0;
+#endif
+};
+#endif
+#ifndef HAVE_GETTIMEOFDAY
+int gettimeofday(struct timeval *tv, struct timezone *tz)
+{
+ time_t t;
+ EINTRLOOP(t, time(NULL));
+ if (tv) tv->tv_sec = t, tv->tv_usec = 0;
+ if (tz) tz->tz_minuteswest = tz->tz_dsttime = 0;
+ return 0;
+}
+#endif
+#ifndef HAVE_TEMPNAM
+char *tempnam(const char *dir, const char *pfx)
+{
+ static int counter = 0;
+ unsigned char *d, *s, *a;
+ int l;
+ if (!(d = getenv("TMPDIR"))) {
+ if (dir) d = (unsigned char *)dir;
+ else if (!(d = getenv("TMP")) && !(d = getenv("TEMP"))) {
+#ifdef P_tmpdir
+ d = P_tmpdir;
+#else
+ d = "/tmp";
+#endif
+ }
+ }
+ l = 0;
+ s = init_str();
+ add_to_str(&s, &l, d);
+ if (s[0] && s[strlen(s) - 1] != '/') add_chr_to_str(&s, &l, '/');
+ add_to_str(&s, &l, (unsigned char *)pfx);
+ add_num_to_str(&s, &l, counter++);
+ a = strdup(s);
+ mem_free(s);
+ return a;
+}
+#endif
+#ifndef HAVE_SIGFILLSET
+int sigfillset(sigset_t *set)
+{
+ memset(set, -1, sizeof(sigset_t));
+ return 0;
+}
+#endif
+#ifndef HAVE_STRTOUL
+unsigned long strtoul(const char *nptr, char **endptr, int base)
+{
+ if (*nptr == '-') {
+ if (endptr) *endptr = nptr;
+ return 0;
+ }
+ return (unsigned long)strtol(nptr,endptr,base);
+};
+#endif
+#ifndef HAVE_STRLEN
+size_t strlen(const char *s)
+{
+ size_t len = 0;
+ while (s[len]) len++;
+ return len;
+}
+#endif
+#ifndef HAVE_STRCPY
+char *strcpy(char *dst, const char *src)
+{
+ return memcpy(dst, src, strlen(src) + 1);
+}
+#endif
+#ifndef HAVE_STRCHR
+char *strchr(const char *s, int c)
+{
+ do {
+ if (*s == (char)c)
+ return (char *)s;
+ } while (*s++);
+ return NULL;
+}
+#endif
+#ifndef HAVE_STRRCHR
+char *strrchr(const char *s, int c)
+{
+ char *ret = NULL;
+ do {
+ if (*s == (char)c)
+ ret = (char *)s;
+ } while (*s++);
+ return ret;
+}
+#endif
+#ifndef HAVE_STRCMP
+int strcmp(const char *s1, const char *s2)
+{
+ while (1) {
+ if (*s1 != *s2) {
+ return (int)(unsigned char)*s1 - (int)(unsigned char)*s2;
+ }
+ if (!*s1) break;
+ s1++, s2++;
+ }
+ return 0;
+}
+#endif
+#ifndef HAVE_STRNCMP
+int strncmp(const char *s1, const char *s2, size_t n)
+{
+ while (n--) {
+ if (*s1 != *s2) {
+ return (int)(unsigned char)*s1 - (int)(unsigned char)*s2;
+ }
+ if (!*s1) break;
+ s1++, s2++;
+ }
+ return 0;
+}
+#endif
+#ifndef HAVE_STRCSPN
+size_t strcspn(const char *s, const char *reject)
+{
+ size_t r;
+ for (r = 0; *s; r++, s++) {
+ const char *rj;
+ for (rj = reject; *rj; rj++) if (*s == *rj) goto brk;
+ }
+ brk:
+ return r;
+}
+#endif
+#ifndef HAVE_STRSTR
+char *strstr(const char *haystack, const char *needle)
+{
+ size_t hs = strlen(haystack);
+ size_t ns = strlen(needle);
+ while (hs >= ns) {
+ if (!memcmp(haystack, needle, ns)) return (char *)haystack;
+ haystack++, hs--;
+ }
+ return NULL;
+}
+#endif
+#ifndef HAVE_MEMMOVE
+void *memmove(void *dst0, const void *src0, size_t length)
+{
+ unsigned char *dst = dst0;
+ const unsigned char *src = src0;
+
+ if ((const unsigned char *)dst == src || !length)
+ return dst0;
+
+ if ((const unsigned char *)dst <= src) {
+ while (length--) *dst++ = *src++;
+ } else {
+ dst += length - 1;
+ src += length - 1;
+ while (length--) *dst-- = *src--;
+ }
+ return dst0;
+}
+#endif
+#ifndef HAVE_STRERROR
+extern char *sys_errlist[];
+extern int sys_nerr;
+char *strerror(int errnum)
+{
+ if (errnum < 0 || errnum >= sys_nerr) return "Unknown error";
+ return sys_errlist[errnum];
+};
+#endif