/* 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 #endif #ifdef USE_GPM #include #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 #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 #include #include #include #ifdef HAVE_SYS_FMUTEX_H #include #include #endif #ifdef X2 /* from xf86sup - XFree86 OS/2 support driver */ #include #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 #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 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 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 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 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(¶m_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