3751 lines
81 KiB
C
3751 lines
81 KiB
C
/* 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
|
|
|
|
#ifdef WIN
|
|
/*#define UNICODE*/
|
|
/*#define NO_STRICT*/
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#if defined(__CYGWIN__) && defined(HAVE_LOCALE_H)
|
|
#include <locale.h>
|
|
#ifdef HAVE_LANGINFO_H
|
|
#include <langinfo.h>
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_PTHREADS
|
|
#include <pthread.h>
|
|
#endif
|
|
|
|
#ifdef OS2
|
|
|
|
#define INCL_MOU
|
|
#define INCL_VIO
|
|
#define INCL_DOSPROCESS
|
|
#define INCL_DOSERRORS
|
|
#define INCL_DOSMODULEMGR
|
|
#define INCL_DOSMISC
|
|
#define INCL_DOSNLS
|
|
#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
|
|
|
|
#define A_DECL(type, var) type var##1, var##2, *var = _THUNK_PTR_STRUCT_OK(&var##1) ? &var##1 : &var##2
|
|
|
|
#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 3584
|
|
|
|
int bounced_read(int fd, void *buf, size_t size)
|
|
{
|
|
unsigned char *bounce_buffer;
|
|
int r;
|
|
|
|
if (fd < 3 && (my_uintptr_t)buf + size >= 0x20000000UL) goto bounce;
|
|
r = _read(fd, buf, size);
|
|
if (r == -1 && errno == EFAULT) goto bounce;
|
|
return r;
|
|
|
|
bounce:
|
|
bounce_buffer = NULL;
|
|
if (size > BOUNCE_BUFFER_SIZE) {
|
|
bounce_buffer = os2_orig_malloc(size);
|
|
if (!bounce_buffer)
|
|
size = BOUNCE_BUFFER_SIZE;
|
|
}
|
|
if (!bounce_buffer)
|
|
bounce_buffer = alloca(size);
|
|
r = _read(fd, bounce_buffer, size);
|
|
if (r > 0) memcpy(buf, bounce_buffer, r);
|
|
if (size > BOUNCE_BUFFER_SIZE)
|
|
free(bounce_buffer);
|
|
return r;
|
|
}
|
|
|
|
int bounced_write(int fd, const void *buf, size_t size)
|
|
{
|
|
unsigned char *bounce_buffer;
|
|
int r;
|
|
|
|
if (fd < 3 && (my_uintptr_t)buf + size >= 0x20000000UL) goto bounce;
|
|
r = _write(fd, buf, size);
|
|
if (r == -1 && errno == EFAULT) goto bounce;
|
|
return r;
|
|
|
|
bounce:
|
|
bounce_buffer = NULL;
|
|
if (size > BOUNCE_BUFFER_SIZE) {
|
|
bounce_buffer = os2_orig_malloc(size);
|
|
if (!bounce_buffer)
|
|
size = BOUNCE_BUFFER_SIZE;
|
|
}
|
|
if (!bounce_buffer)
|
|
bounce_buffer = alloca(size);
|
|
memcpy(bounce_buffer, buf, size);
|
|
r = _write(fd, bounce_buffer, size);
|
|
if (size > BOUNCE_BUFFER_SIZE)
|
|
free(bounce_buffer);
|
|
return r;
|
|
}
|
|
|
|
void portable_sleep(unsigned msec)
|
|
{
|
|
DosSleep(msec);
|
|
}
|
|
#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_MAX_ALIGN 0x20000
|
|
|
|
void virtual_free(void *ptr, size_t len)
|
|
{
|
|
int rc;
|
|
rc = DosFreeMem(ptr);
|
|
/*fprintf(stderr, "heap free %p -> %d\n", ptr, rc);*/
|
|
if (rc)
|
|
fatal_exit("DosFreeMem failed: %d", rc);
|
|
if (len & 4095)
|
|
len = (len | 4095) + 1;
|
|
mem_requested -= len;
|
|
blocks_requested--;
|
|
}
|
|
|
|
static void heap_release(Heap_t h, void *ptr, size_t len)
|
|
{
|
|
virtual_free(ptr, len);
|
|
}
|
|
|
|
void *virtual_alloc(size_t len)
|
|
{
|
|
void *result;
|
|
int rc;
|
|
rc = DosAllocMem(&result, len, dosallocmem_attrib);
|
|
if (rc)
|
|
return NULL;
|
|
/*
|
|
* 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, len);
|
|
return NULL;
|
|
}
|
|
}
|
|
if (len & 4095)
|
|
len = (len | 4095) + 1;
|
|
mem_requested += len;
|
|
blocks_requested++;
|
|
return result;
|
|
}
|
|
|
|
static void *heap_alloc(Heap_t h, size_t *size, int *pclean)
|
|
{
|
|
void *result;
|
|
/* 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_MAX_ALIGN) {
|
|
real_size = real_size | (HEAP_ALIGN - 1);
|
|
} else {
|
|
real_size |= 1;
|
|
}
|
|
result = virtual_alloc(real_size);
|
|
/*fprintf(stderr, "heap alloc %d,%d -> %p\n", *size, real_size, result);*/
|
|
if (result) {
|
|
*size = real_size;
|
|
*pclean = _BLOCK_CLEAN;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static Heap_t original_heap = 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;
|
|
}
|
|
if (dosallocmem_attrib & OBJ_ANY) {
|
|
/* call malloc to initialize the default heap */
|
|
void *p = malloc(1);
|
|
if (p) free(p);
|
|
original_heap = _udefault(new_heap);
|
|
} else {
|
|
_udefault(new_heap);
|
|
}
|
|
}
|
|
|
|
void *os2_orig_malloc(size_t len)
|
|
{
|
|
if (original_heap)
|
|
return _umalloc(original_heap, len);
|
|
return malloc(len);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef OS2
|
|
static int os2_full_screen = 0;
|
|
static int os2_detached = 0;
|
|
static PTIB os2_tib = NULL;
|
|
PPIB os2_pib = NULL;
|
|
static HSWITCH os2_switchhandle = NULLHANDLE;
|
|
#ifdef HAVE_SYS_FMUTEX_H
|
|
static _fmutex fd_mutex;
|
|
#endif
|
|
#elif defined(HAVE_PTHREADS) && !defined(OPENVMS)
|
|
static pthread_mutex_t pth_mutex;
|
|
static void fd_lock(void);
|
|
static void fd_unlock(void);
|
|
static void fd_init(void)
|
|
{
|
|
int r;
|
|
r = pthread_mutex_init(&pth_mutex, NULL);
|
|
if (r)
|
|
fatal_exit("pthread_mutex_create failed: %s", strerror(r));
|
|
}
|
|
#endif
|
|
|
|
int page_size = 4096;
|
|
|
|
void init_page_size(void)
|
|
{
|
|
long getpg = -1;
|
|
#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
|
|
if (getpg < 0)
|
|
EINTRLOOP(getpg, sysconf(_SC_PAGESIZE));
|
|
#endif
|
|
#ifdef HAVE_GETPAGESIZE
|
|
if (getpg < 0)
|
|
getpg = getpagesize();
|
|
#endif
|
|
if (getpg > 0 && !(getpg & (getpg - 1))) page_size = (int)getpg;
|
|
}
|
|
|
|
#if !defined(OPENVMS) && !defined(DOS)
|
|
void init_os(void)
|
|
{
|
|
/* Disable per-thread heap */
|
|
#if defined(HAVE_MALLOPT) && defined(M_ARENA_TEST)
|
|
mallopt(M_ARENA_TEST, 1);
|
|
#endif
|
|
#if defined(HAVE_MALLOPT) && defined(M_ARENA_MAX)
|
|
mallopt(M_ARENA_MAX, 1);
|
|
#endif
|
|
|
|
#ifdef OS2
|
|
DosGetInfoBlocks(&os2_tib, &os2_pib);
|
|
if (os2_pib) {
|
|
os2_switchhandle = WinQuerySwitchHandle(0, os2_pib->pib_ulpid);
|
|
os2_full_screen = os2_pib->pib_ultype == 0;
|
|
os2_detached = os2_pib->pib_ultype == 4;
|
|
if (os2_pib->pib_ultype == 3) force_g = 1;
|
|
}
|
|
#ifdef HAVE_SYS_FMUTEX_H
|
|
if (_fmutex_create(&fd_mutex, 0))
|
|
fatal_exit("failed to create fd mutex");
|
|
#endif
|
|
#elif defined(HAVE_PTHREADS) && !defined(OPENVMS)
|
|
{
|
|
int r;
|
|
fd_init();
|
|
r = pthread_atfork(fd_lock, fd_unlock, fd_init);
|
|
if (r)
|
|
fatal_exit("pthread_atfork failed: %s", strerror(r));
|
|
}
|
|
#endif
|
|
|
|
#ifdef OS2_ADVANCED_HEAP
|
|
init_os2_heap();
|
|
#endif
|
|
|
|
#ifdef WIN
|
|
if (!GetConsoleCP())
|
|
force_g = 1;
|
|
#if defined(__CYGWIN__) && defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
|
|
{
|
|
unsigned char *l = cast_uchar setlocale(LC_CTYPE, "");
|
|
if (!l || !casestrcmp(l, cast_uchar "C")) {
|
|
setlocale(LC_CTYPE, "en_US.utf-8");
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(__CYGWIN__)
|
|
/*
|
|
* When started from cmd.exe and the argument contains some characters
|
|
* not valid in the current locale, cygwin doesn't remove the quotation
|
|
* marks around the argument. So, we must remove the quotation marks
|
|
* here.
|
|
*/
|
|
if (getppid() == 1) {
|
|
int i;
|
|
for (i = 1; i < g_argc; i++) {
|
|
unsigned char *a = cast_uchar g_argv[i];
|
|
int l = (int)strlen(cast_const_char a);
|
|
if (l >= 3 && a[0] == '"' && a[l - 1] == '"') {
|
|
unsigned char *a2 = cast_uchar strdup(cast_const_char (a + 1));
|
|
if (a2) {
|
|
unsigned char *p, *q;
|
|
a2[l - 2] = 0;
|
|
for (p = q = a2; *p; p++) {
|
|
if (p[0] == '\\' && p[1] == '"')
|
|
continue;
|
|
*q++ = *p;
|
|
}
|
|
*q = 0;
|
|
g_argv[i] = cast_char a2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
#if defined(BEOS) || defined(HAIKU)
|
|
if (getenv("TERM") == NULL) {
|
|
/* probably launched from Tracker or Deskbar, force graphics mode */
|
|
force_g = 1;
|
|
}
|
|
#endif
|
|
}
|
|
#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');
|
|
}
|
|
|
|
static inline int is_safe_in_file(unsigned char c)
|
|
{
|
|
return !(c < ' ' || c == '"' || c == '*' || c == '/' || c == ':' || c == '<' || c == '>' || c == '\\' || c == '|' || c >= 0x80);
|
|
}
|
|
|
|
static inline 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++;
|
|
}
|
|
}
|
|
|
|
void check_filename(unsigned char **file)
|
|
{
|
|
unsigned char *c = *file;
|
|
while (*c) {
|
|
if (!is_safe_in_file(*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(cast_const_char 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(cast_uchar "\"");
|
|
add_to_strn(&result, path);
|
|
add_to_strn(&result, cast_uchar "\"");
|
|
return result;
|
|
}
|
|
|
|
static inline int get_e(unsigned char *env)
|
|
{
|
|
unsigned char *v;
|
|
if ((v = cast_uchar getenv(cast_const_char env))) return atoi(cast_const_char v);
|
|
return 0;
|
|
}
|
|
|
|
void do_signal(int sig, void (*handler)(int))
|
|
{
|
|
errno = 0;
|
|
while (signal(sig, handler) == SIG_ERR && errno == EINTR) errno = 0;
|
|
}
|
|
|
|
void ignore_signals(void)
|
|
{
|
|
do_signal(SIGPIPE, SIG_IGN);
|
|
#ifdef SIGXFSZ
|
|
do_signal(SIGXFSZ, SIG_IGN);
|
|
#endif
|
|
#ifdef OPENVMS
|
|
#ifdef SIGCHLD
|
|
do_signal(SIGCHLD, SIG_IGN);
|
|
#endif
|
|
#ifdef SIGWINCH
|
|
do_signal(SIGWINCH, SIG_IGN);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
time_t get_absolute_seconds(void)
|
|
{
|
|
time_t t;
|
|
errno = 0;
|
|
EINTRLOOPX(t, time(NULL), (time_t)-1);
|
|
return t;
|
|
}
|
|
|
|
uttime get_absolute_time(void)
|
|
{
|
|
struct timeval tv;
|
|
int rs;
|
|
EINTRLOOP(rs, gettimeofday(&tv, NULL));
|
|
if (rs) fatal_exit("gettimeofday failed: %d", errno);
|
|
return (uttime)tv.tv_sec * 1000 + (unsigned)tv.tv_usec / 1000;
|
|
}
|
|
|
|
uttime get_time(void)
|
|
{
|
|
#if defined(OS2) || defined(WIN)
|
|
static unsigned last_tim = 0;
|
|
static uttime add = 0;
|
|
unsigned tim;
|
|
#if defined(OS2)
|
|
int rc;
|
|
rc = DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, &tim, sizeof tim);
|
|
if (rc) fatal_exit("DosQuerySysInfo failed: %d", rc);
|
|
#elif defined(WIN)
|
|
tim = GetTickCount();
|
|
#endif
|
|
if (tim < last_tim) {
|
|
add += (uttime)1 << 31 << 1;
|
|
}
|
|
last_tim = tim;
|
|
return tim | add;
|
|
#else
|
|
#if defined(HAVE_CLOCK_GETTIME) && defined(TIME_WITH_SYS_TIME) && (defined(CLOCK_MONOTONIC_RAW) || defined(CLOCK_MONOTONIC))
|
|
struct timespec ts;
|
|
int rs;
|
|
#if defined(CLOCK_MONOTONIC_RAW)
|
|
EINTRLOOP(rs, clock_gettime(CLOCK_MONOTONIC_RAW, &ts));
|
|
if (!rs) return (uttime)ts.tv_sec * 1000 + (unsigned)ts.tv_nsec / 1000000;
|
|
#endif
|
|
#if defined(CLOCK_MONOTONIC)
|
|
EINTRLOOP(rs, clock_gettime(CLOCK_MONOTONIC, &ts));
|
|
if (!rs) return (uttime)ts.tv_sec * 1000 + (unsigned)ts.tv_nsec / 1000000;
|
|
#endif
|
|
#endif
|
|
return get_absolute_time();
|
|
#endif
|
|
}
|
|
|
|
|
|
static unsigned char *clipboard = NULL;
|
|
|
|
void os_free_clipboard(void)
|
|
{
|
|
if (clipboard) mem_free(clipboard), clipboard = NULL;
|
|
}
|
|
|
|
/* Terminal size */
|
|
|
|
#if defined(UNIX) || defined(OS2) || defined(WIN) || defined(INTERIX) || defined(BEOS) || defined(RISCOS) || defined(ATHEOS) || defined(SPAD) || defined(OPENVMS) || defined(HAIKU)
|
|
static void (*terminal_resize_callback)(int, int);
|
|
#endif
|
|
|
|
#if (defined(OS2) && defined(X2)) || defined(WIN) || defined(OPENVMS)
|
|
|
|
#define TERMINAL_SIZE_POLLING
|
|
|
|
/* Cygwin has a bug and loses SIGWINCH sometimes, so poll it */
|
|
|
|
static struct timer *terminal_resize_timer = NULL;
|
|
static int old_xsize, old_ysize;
|
|
|
|
static void terminal_resize_fn(void *p)
|
|
{
|
|
int cur_xsize, cur_ysize;
|
|
terminal_resize_timer = install_timer(TERMINAL_POLL_TIMEOUT, terminal_resize_fn, NULL);
|
|
get_terminal_size(&cur_xsize, &cur_ysize);
|
|
if ((old_xsize != cur_xsize) || (old_ysize != cur_ysize)) {
|
|
old_xsize = cur_xsize;
|
|
old_ysize = cur_ysize;
|
|
terminal_resize_callback(cur_xsize, cur_ysize);
|
|
}
|
|
}
|
|
|
|
static void terminal_resize_poll(int x, int y)
|
|
{
|
|
if (terminal_resize_timer)
|
|
internal_error("terminal_resize_poll: timer already active");
|
|
old_xsize = x;
|
|
old_ysize = y;
|
|
#ifdef OS2
|
|
if (!is_xterm())
|
|
return;
|
|
#endif
|
|
terminal_resize_timer = install_timer(TERMINAL_POLL_TIMEOUT, terminal_resize_fn, NULL);
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(UNIX) || defined(OS2) || defined(WIN) || defined(INTERIX) || defined(BEOS) || defined(RISCOS) || defined(ATHEOS) || defined(SPAD) || defined(OPENVMS) || defined(HAIKU)
|
|
|
|
#if defined(SIGWINCH) && !defined(OS2) && !defined(OPENVMS)
|
|
static void sigwinch(void *s)
|
|
{
|
|
int cur_xsize, cur_ysize;
|
|
get_terminal_size(&cur_xsize, &cur_ysize);
|
|
#ifdef TERMINAL_SIZE_POLLING
|
|
old_xsize = cur_xsize;
|
|
old_ysize = cur_ysize;
|
|
#endif
|
|
terminal_resize_callback(cur_xsize, cur_ysize);
|
|
}
|
|
#endif
|
|
|
|
void handle_terminal_resize(void (*fn)(int, int), int *x, int *y)
|
|
{
|
|
terminal_resize_callback = fn;
|
|
get_terminal_size(x, y);
|
|
#if defined(SIGWINCH) && !defined(OS2) && !defined(OPENVMS)
|
|
install_signal_handler(SIGWINCH, sigwinch, NULL, 0);
|
|
#endif
|
|
#ifdef TERMINAL_SIZE_POLLING
|
|
terminal_resize_poll(*x, *y);
|
|
#endif
|
|
}
|
|
|
|
void unhandle_terminal_resize(void)
|
|
{
|
|
#if defined(SIGWINCH) && !defined(OS2) && !defined(OPENVMS)
|
|
install_signal_handler(SIGWINCH, NULL, NULL, 0);
|
|
#endif
|
|
#ifdef TERMINAL_SIZE_POLLING
|
|
if (terminal_resize_timer) {
|
|
kill_timer(terminal_resize_timer);
|
|
terminal_resize_timer = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if defined(OS2)
|
|
|
|
void get_terminal_size(int *x, int *y)
|
|
{
|
|
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;
|
|
}
|
|
*y = win.ws_row;
|
|
*x = win.ws_col;
|
|
goto set_default;
|
|
#else
|
|
*x = 80; *y = 24;
|
|
#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;
|
|
}
|
|
}
|
|
}
|
|
|
|
#elif !defined(OPENVMS)
|
|
|
|
void get_terminal_size(int *x, int *y)
|
|
{
|
|
int rs = -1;
|
|
#ifdef TIOCGWINSZ
|
|
/* Sun Studio misoptimizes it */
|
|
sun_volatile struct winsize ws;
|
|
EINTRLOOP(rs, ioctl(1, TIOCGWINSZ, &ws));
|
|
#endif
|
|
if ((rs == -1
|
|
#ifdef TIOCGWINSZ
|
|
|| !(*x = ws.ws_col)
|
|
#endif
|
|
) && !(*x = get_e(cast_uchar "COLUMNS"))) {
|
|
*x = 80;
|
|
#ifdef _UWIN
|
|
*x = 79;
|
|
#endif
|
|
}
|
|
if ((rs == -1
|
|
#ifdef TIOCGWINSZ
|
|
|| !(*y = ws.ws_row)
|
|
#endif
|
|
) && !(*y = get_e(cast_uchar "LINES"))) {
|
|
*y = 24;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
#if defined(OS2) && defined(HAVE_SYS_FMUTEX_H)
|
|
|
|
static void fd_lock(void)
|
|
{
|
|
_fmutex_request(&fd_mutex, _FMR_IGNINT);
|
|
}
|
|
|
|
static void fd_unlock(void)
|
|
{
|
|
_fmutex_release(&fd_mutex);
|
|
}
|
|
|
|
#elif defined(HAVE_PTHREADS) && !defined(OPENVMS)
|
|
|
|
static void fd_lock(void)
|
|
{
|
|
int r;
|
|
r = pthread_mutex_lock(&pth_mutex);
|
|
if (r)
|
|
fatal_exit("pthread_mutex_lock failed: %s", strerror(r));
|
|
}
|
|
|
|
static void fd_unlock(void)
|
|
{
|
|
int r;
|
|
r = pthread_mutex_unlock(&pth_mutex);
|
|
if (r)
|
|
fatal_exit("pthread_mutex_lock failed: %s", strerror(r));
|
|
}
|
|
|
|
#else
|
|
|
|
#define fd_lock() do { } while (0)
|
|
#define fd_unlock() do { } while (0)
|
|
|
|
#endif
|
|
|
|
static void new_fd_cloexec(int fd)
|
|
{
|
|
int rs;
|
|
EINTRLOOP(rs, fcntl(fd, F_SETFD, FD_CLOEXEC));
|
|
}
|
|
|
|
static void new_fd_bin(int fd)
|
|
{
|
|
new_fd_cloexec(fd);
|
|
#if defined(OS2) || (defined(WIN) && !defined(_UWIN)) || defined(DOS)
|
|
setmode(fd, O_BINARY);
|
|
#endif
|
|
}
|
|
|
|
#if !defined(OPENVMS)
|
|
|
|
void set_nonblock(int fd)
|
|
{
|
|
#ifdef O_NONBLOCK
|
|
int rs;
|
|
EINTRLOOP(rs, fcntl(fd, F_SETFL, O_NONBLOCK));
|
|
#elif defined(FIONBIO)
|
|
int rs;
|
|
int on = 1;
|
|
EINTRLOOP(rs, ioctl(fd, FIONBIO, &on));
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|
|
static int cleanup_fds(void)
|
|
{
|
|
#ifdef ENFILE
|
|
if (errno == ENFILE) return abort_background_connections();
|
|
#endif
|
|
#ifdef EMFILE
|
|
if (errno == EMFILE) return abort_background_connections();
|
|
#endif
|
|
#ifndef EMFILE
|
|
if (errno == EIO) return abort_background_connections();
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/* Pipe */
|
|
|
|
int c_pipe(int fd[2])
|
|
{
|
|
int r;
|
|
do {
|
|
fd_lock();
|
|
EINTRLOOP(r, pipe(fd));
|
|
if (!r) new_fd_bin(fd[0]), new_fd_bin(fd[1]);
|
|
fd_unlock();
|
|
if (verify_handle(fd[0]) || verify_handle(fd[1])) {
|
|
EINTRLOOP(r, close(fd[0]));
|
|
EINTRLOOP(r, close(fd[1]));
|
|
r = -1;
|
|
#ifdef EMFILE
|
|
errno = EMFILE;
|
|
#else
|
|
errno = EIO;
|
|
#endif
|
|
}
|
|
} while (r == -1 && cleanup_fds());
|
|
return r;
|
|
}
|
|
|
|
int c_dup(int oh)
|
|
{
|
|
int h;
|
|
do {
|
|
fd_lock();
|
|
EINTRLOOP(h, dup(oh));
|
|
if (h != -1) new_fd_cloexec(h);
|
|
fd_unlock();
|
|
if (verify_handle(h)) {
|
|
int r;
|
|
EINTRLOOP(r, close(h));
|
|
h = -1;
|
|
#ifdef EMFILE
|
|
errno = EMFILE;
|
|
#else
|
|
errno = EIO;
|
|
#endif
|
|
}
|
|
} while (h == -1 && cleanup_fds());
|
|
return h;
|
|
}
|
|
|
|
int c_socket(int d, int t, int p)
|
|
{
|
|
int h;
|
|
do {
|
|
fd_lock();
|
|
EINTRLOOP(h, socket(d, t, p));
|
|
if (h != -1) new_fd_cloexec(h);
|
|
fd_unlock();
|
|
if (verify_handle(h)) {
|
|
int r;
|
|
EINTRLOOP(r, close(h));
|
|
h = -1;
|
|
#ifdef EMFILE
|
|
errno = EMFILE;
|
|
#else
|
|
errno = EIO;
|
|
#endif
|
|
}
|
|
} while (h == -1 && cleanup_fds());
|
|
return h;
|
|
}
|
|
|
|
int c_accept(int sh, struct sockaddr *addr, socklen_t *addrlen)
|
|
{
|
|
int h;
|
|
do {
|
|
fd_lock();
|
|
EINTRLOOP(h, accept(sh, addr, addrlen));
|
|
if (h != -1) new_fd_cloexec(h);
|
|
fd_unlock();
|
|
if (verify_handle(h)) {
|
|
int r;
|
|
EINTRLOOP(r, close(h));
|
|
h = -1;
|
|
#ifdef EMFILE
|
|
errno = EMFILE;
|
|
#else
|
|
errno = EIO;
|
|
#endif
|
|
}
|
|
} while (h == -1 && cleanup_fds());
|
|
return h;
|
|
}
|
|
|
|
int c_open(unsigned char *path, int flags)
|
|
{
|
|
int h;
|
|
do {
|
|
fd_lock();
|
|
EINTRLOOP(h, open(cast_const_char path, flags));
|
|
if (h != -1) new_fd_bin(h);
|
|
fd_unlock();
|
|
if (verify_handle(h)) {
|
|
int r;
|
|
EINTRLOOP(r, close(h));
|
|
h = -1;
|
|
#ifdef EMFILE
|
|
errno = EMFILE;
|
|
#else
|
|
errno = EIO;
|
|
#endif
|
|
}
|
|
} while (h == -1 && cleanup_fds());
|
|
return h;
|
|
}
|
|
|
|
int c_open3(unsigned char *path, int flags, int mode)
|
|
{
|
|
int h;
|
|
do {
|
|
fd_lock();
|
|
EINTRLOOP(h, open(cast_const_char path, flags, mode));
|
|
if (h != -1) new_fd_bin(h);
|
|
fd_unlock();
|
|
if (verify_handle(h)) {
|
|
int r;
|
|
EINTRLOOP(r, close(h));
|
|
h = -1;
|
|
#ifdef EMFILE
|
|
errno = EMFILE;
|
|
#else
|
|
errno = EIO;
|
|
#endif
|
|
}
|
|
} while (h == -1 && cleanup_fds());
|
|
return h;
|
|
}
|
|
|
|
DIR *c_opendir(unsigned char *path)
|
|
{
|
|
DIR *d;
|
|
do {
|
|
fd_lock();
|
|
ENULLLOOP(d, opendir(cast_const_char path));
|
|
#ifdef HAVE_DIRFD
|
|
if (d) {
|
|
int h;
|
|
EINTRLOOP(h, dirfd(d));
|
|
if (h != -1) new_fd_cloexec(h);
|
|
}
|
|
#endif
|
|
fd_unlock();
|
|
} while (!d && cleanup_fds());
|
|
return d;
|
|
}
|
|
|
|
#if defined(O_SIZE) && defined(__EMX__)
|
|
|
|
int open_prealloc(unsigned char *name, int flags, int mode, off_t siz)
|
|
{
|
|
int h;
|
|
fd_lock();
|
|
EINTRLOOP(h, open(cast_const_char name, flags | O_SIZE, mode, (unsigned long)siz));
|
|
if (h != -1) new_fd_bin(h);
|
|
fd_unlock();
|
|
return h;
|
|
}
|
|
|
|
#elif defined(HAVE_OPEN_PREALLOC)
|
|
|
|
int open_prealloc(unsigned char *name, int flags, int mode, off_t siz)
|
|
{
|
|
int h, rs;
|
|
fd_lock();
|
|
EINTRLOOP(h, open(cast_const_char name, flags, mode));
|
|
if (h == -1) {
|
|
fd_unlock();
|
|
return -1;
|
|
}
|
|
new_fd_bin(h);
|
|
fd_unlock();
|
|
#if defined(HAVE_FALLOCATE)
|
|
#if defined(FALLOC_FL_KEEP_SIZE)
|
|
EINTRLOOP(rs, fallocate(h, FALLOC_FL_KEEP_SIZE, 0, siz));
|
|
#else
|
|
EINTRLOOP(rs, fallocate(h, 0, 0, siz));
|
|
#endif
|
|
if (!rs) return h;
|
|
#endif
|
|
#if defined(HAVE_POSIX_FALLOCATE)
|
|
/* posix_fallocate may fall back to overwriting the file with zeros,
|
|
so don't use it on too big files */
|
|
if (siz > 134217728)
|
|
return h;
|
|
do {
|
|
rs = posix_fallocate(h, 0, siz);
|
|
} while (rs == EINTR);
|
|
if (!rs) return h;
|
|
#endif
|
|
return h;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* Exec */
|
|
|
|
#if defined(UNIX) || defined(ATHEOS) || defined(INTERIX)
|
|
|
|
int is_twterm(void)
|
|
{
|
|
static int xt = -1;
|
|
if (xt == -1) xt = !!getenv("TWDISPLAY");
|
|
return xt;
|
|
}
|
|
|
|
#else
|
|
|
|
int is_twterm(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(UNIX) || defined(ATHEOS) || defined(INTERIX)
|
|
|
|
int is_screen(void)
|
|
{
|
|
static int xt = -1;
|
|
if (xt == -1) xt = !!getenv("STY");
|
|
return xt;
|
|
}
|
|
|
|
#else
|
|
|
|
int is_screen(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(UNIX) || defined(SPAD)
|
|
|
|
int is_xterm(void)
|
|
{
|
|
static int xt = -1;
|
|
if (xt == -1) xt = getenv("DISPLAY") && *(char *)getenv("DISPLAY");
|
|
return xt;
|
|
}
|
|
|
|
#elif defined(BEOS) || defined(ATHEOS) || defined(DOS) || defined(HAIKU)
|
|
|
|
int is_xterm(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#elif defined(WIN) || defined(INTERIX) || defined(OS2)
|
|
|
|
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
|
|
|
|
#if defined(__linux__) || defined(__LINUX__)
|
|
static int cons_control[2] = { -1, -1 };
|
|
static int cons_status[2] = { -1, -1 };
|
|
#endif
|
|
|
|
void close_fork_tty(void)
|
|
{
|
|
struct terminal *t;
|
|
struct list_head *lt;
|
|
struct download *d;
|
|
struct list_head *ld;
|
|
struct connection *c;
|
|
struct list_head *lc;
|
|
struct k_conn *k;
|
|
struct list_head *lk;
|
|
int rs;
|
|
#ifndef NO_SIGNAL_HANDLERS
|
|
if (signal_pipe[0] != -1) EINTRLOOP(rs, close(signal_pipe[0]));
|
|
#ifndef __minix__
|
|
if (signal_pipe[1] != -1) EINTRLOOP(rs, close(signal_pipe[1]));
|
|
#endif
|
|
#endif
|
|
#if defined(__linux__) || defined(__LINUX__)
|
|
if (cons_control[0] != -1) EINTRLOOP(rs, close(cons_control[0]));
|
|
if (cons_control[1] != -1) EINTRLOOP(rs, close(cons_control[1]));
|
|
if (cons_status[0] != -1) EINTRLOOP(rs, close(cons_status[0]));
|
|
if (cons_status[1] != -1) EINTRLOOP(rs, close(cons_status[1]));
|
|
#endif
|
|
#ifdef G
|
|
if (drv && drv->after_fork) drv->after_fork();
|
|
#endif
|
|
if (terminal_pipe[1] != -1) EINTRLOOP(rs, close(terminal_pipe[1]));
|
|
if (s_unix_fd != -1) EINTRLOOP(rs, close(s_unix_fd));
|
|
foreach(struct terminal, t, lt, terminals) {
|
|
if (t->fdin > 0)
|
|
EINTRLOOP(rs, close(t->fdin));
|
|
if (t->handle_to_close >= 0)
|
|
EINTRLOOP(rs, close(t->handle_to_close));
|
|
}
|
|
foreach(struct download, d, ld, downloads) if (d->handle > 0)
|
|
EINTRLOOP(rs, close(d->handle));
|
|
foreach(struct connection, c, lc, queue) {
|
|
if (c->sock1 >= 0) EINTRLOOP(rs, close(c->sock1));
|
|
if (c->sock2 >= 0) EINTRLOOP(rs, close(c->sock2));
|
|
}
|
|
foreach(struct k_conn, k, lk, keepalive_connections)
|
|
EINTRLOOP(rs, close(k->conn));
|
|
}
|
|
|
|
|
|
#if defined(WIN)
|
|
|
|
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 = GetModuleFileNameA(NULL, cast_char path, sizeof path);
|
|
if (r <= 0 || r >= sizeof path) {
|
|
path_to_exe = cast_uchar g_argv[0];
|
|
return;
|
|
}
|
|
path_to_exe = path;
|
|
}
|
|
|
|
#elif defined(OS2)
|
|
|
|
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];
|
|
path_to_exe = cast_uchar g_argv[0];
|
|
if (!os2_pib) return;
|
|
if (DosQueryModuleName(os2_pib->pib_hmte, sizeof path, path)) return;
|
|
path_to_exe = path;
|
|
}
|
|
|
|
static ULONG os2_old_type;
|
|
static HAB os2_hab;
|
|
static HMQ os2_hmq;
|
|
|
|
static int os2_init_pm(void)
|
|
{
|
|
if (!os2_pib) goto err0;
|
|
os2_old_type = os2_pib->pib_ultype;
|
|
os2_pib->pib_ultype = 3;
|
|
os2_hab = WinInitialize(0);
|
|
if (os2_hab == NULLHANDLE) goto err1;
|
|
os2_hmq = WinCreateMsgQueue(os2_hab, 0);
|
|
if (os2_hmq == NULLHANDLE) goto err2;
|
|
return 0;
|
|
err2:
|
|
WinTerminate(os2_hab);
|
|
err1:
|
|
os2_pib->pib_ultype = os2_old_type;
|
|
err0:
|
|
return -1;
|
|
}
|
|
|
|
static void os2_exit_pm(void)
|
|
{
|
|
WinDestroyMsgQueue(os2_hmq);
|
|
WinTerminate(os2_hab);
|
|
os2_pib->pib_ultype = os2_old_type;
|
|
}
|
|
|
|
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(cast_char buffer, "OS/2 %d.%d i386", (int)version[0], (int)version[1]);
|
|
return 0;
|
|
}
|
|
|
|
#elif !defined(OPENVMS)
|
|
|
|
void get_path_to_exe(void)
|
|
{
|
|
path_to_exe = cast_uchar 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 = cast_uchar getenv("TERM");
|
|
if (!term || casecmp(term, cast_uchar "interix", 7)) {
|
|
system("stty sane 2>/dev/null");
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef OS2
|
|
if (os2_detached) {
|
|
fatal_exit("Links doesn't work in detached session");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef WIN
|
|
void translate_win32_to_unix(unsigned char **str)
|
|
{
|
|
#ifdef HAVE_CYGWIN_CONV_PATH
|
|
unsigned char *new_path;
|
|
ssize_t sz = cygwin_conv_path(CCP_WIN_A_TO_POSIX | CCP_ABSOLUTE, *str, NULL, 0);
|
|
if (sz < 0 || sz >= MAXINT)
|
|
return;
|
|
new_path = mem_alloc(sz);
|
|
sz = cygwin_conv_path(CCP_WIN_A_TO_POSIX | CCP_ABSOLUTE, *str, new_path, sz);
|
|
if (sz < 0) {
|
|
mem_free(new_path);
|
|
return;
|
|
}
|
|
mem_free(*str);
|
|
*str = new_path;
|
|
#endif
|
|
}
|
|
#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 = cast_uchar strchr(cast_const_char(prog + 1), prog[0]);
|
|
if (!*prog_end)
|
|
*prog_end = cast_uchar strchr(cast_const_char prog, 0);
|
|
} else {
|
|
*prog_start = prog;
|
|
*prog_end = prog + strcspn(cast_const_char 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 && (
|
|
!casecmp(prog_end - 4, cast_uchar ".exe", 4) ||
|
|
!casecmp(prog_end - 4, cast_uchar ".bat", 4)))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(WIN) && 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 || sz >= MAXINT) 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(WIN) && 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(cast_const_char file, cast_char new_path);
|
|
if (!*new_path) return stracpy(file);
|
|
return stracpy(new_path);
|
|
}
|
|
|
|
#elif defined(WIN) && 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 || sz >= MAXINT) 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;
|
|
if (!prog) prog = ".exe";
|
|
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_chr_to_str(&newstr, &newstrl, '/');
|
|
else add_chr_to_str(&newstr, &newstrl, *p);
|
|
}
|
|
return newstr;
|
|
}
|
|
copy_path:
|
|
return stracpy(file);
|
|
}
|
|
|
|
#elif defined(OS2) || defined(DOS)
|
|
|
|
unsigned char *os_conv_to_external_path(unsigned char *file, unsigned char *prog)
|
|
{
|
|
unsigned char *f, *x;
|
|
f = stracpy(file);
|
|
#ifdef OS2
|
|
if (prog)
|
|
return f;
|
|
#endif
|
|
x = f;
|
|
while ((x = cast_uchar strchr(cast_const_char x, '/'))) *x = '\\';
|
|
return f;
|
|
}
|
|
|
|
|
|
#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, cast_uchar "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) || defined(OPENVMS) || defined(DOS) || defined(HAIKU)
|
|
|
|
#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));
|
|
system(path);
|
|
_exit(0);
|
|
}
|
|
if (p > 0) {
|
|
EINTRLOOP(rp, waitpid(p, &s, 0));
|
|
} else {
|
|
rs = system(path);
|
|
return rs;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
/* UNIX */
|
|
int exe(unsigned char *path, int fg)
|
|
{
|
|
#ifdef OPENVMS
|
|
if (!strcmp(cast_const_char path, DEFAULT_SHELL))
|
|
path = cast_uchar "";
|
|
#endif
|
|
#ifndef EXEC_IN_THREADS
|
|
#ifdef SIGCHLD
|
|
do_signal(SIGCHLD, SIG_DFL);
|
|
#endif
|
|
do_signal(SIGPIPE, SIG_DFL);
|
|
#ifdef SIGXFSZ
|
|
do_signal(SIGXFSZ, SIG_DFL);
|
|
#endif
|
|
#ifdef SIGTSTP
|
|
do_signal(SIGTSTP, SIG_DFL);
|
|
#endif
|
|
#ifdef SIGCONT
|
|
do_signal(SIGCONT, SIG_DFL);
|
|
#endif
|
|
#ifdef SIGWINCH
|
|
do_signal(SIGWINCH, SIG_DFL);
|
|
#endif
|
|
#endif
|
|
#ifdef G
|
|
if (F && drv->exec) return drv->exec(path, fg);
|
|
#endif
|
|
return system(cast_const_char path);
|
|
}
|
|
|
|
#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
|
|
if (!clipboard)
|
|
return NULL;
|
|
return convert(utf8_table, term_charset(term), clipboard, NULL);
|
|
}
|
|
|
|
/* links -> clipboard */
|
|
void set_clipboard_text(struct terminal *term, unsigned char *data)
|
|
{
|
|
#ifdef G
|
|
if (F && drv->set_clipboard_text) {
|
|
drv->set_clipboard_text(data);
|
|
return;
|
|
}
|
|
#endif
|
|
if (clipboard) mem_free(clipboard);
|
|
clipboard = convert(term_charset(term), utf8_table, data, NULL);
|
|
}
|
|
|
|
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(WIN)
|
|
|
|
int is_winnt(void)
|
|
{
|
|
OSVERSIONINFO v;
|
|
v.dwOSVersionInfoSize = sizeof v;
|
|
if (!GetVersionEx(&v)) return 0;
|
|
return v.dwPlatformId >= VER_PLATFORM_WIN32_NT;
|
|
}
|
|
|
|
static void close_handles(int keep_output)
|
|
{
|
|
int i, rs;
|
|
for (i = 0; i < FD_SETSIZE; i++) {
|
|
if (keep_output && (i == 1 || i == 2)) continue;
|
|
EINTRLOOP(rs, close(i));
|
|
}
|
|
EINTRLOOP(rs, open("nul", O_RDONLY));
|
|
if (!keep_output) {
|
|
EINTRLOOP(rs, open("nul", O_WRONLY));
|
|
EINTRLOOP(rs, open("nul", O_WRONLY));
|
|
}
|
|
}
|
|
|
|
#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 = 0;
|
|
unsigned char buffer[1024];
|
|
unsigned char buffer2[1024];
|
|
size_t want_alloc;
|
|
pid_t pid, rp;
|
|
#ifndef _UWIN
|
|
int rs;
|
|
#endif
|
|
unsigned char *x1;
|
|
unsigned char *arg;
|
|
x1 = cast_uchar GETSHELL;
|
|
if (!x1) x1 = cast_uchar DEFAULT_SHELL;
|
|
|
|
want_alloc = strlen(cast_const_char WIN32_START_STRING) + 3 + strlen(cast_const_char path) + 1;
|
|
#ifdef _UWIN
|
|
want_alloc += strlen(cast_const_char x1) + 4;
|
|
want_alloc *= 2;
|
|
#endif
|
|
|
|
arg = malloc(want_alloc);
|
|
if (!arg) return -1;
|
|
*arg = 0;
|
|
#ifdef _UWIN
|
|
strcat(cast_char arg, cast_const_char x1);
|
|
strcat(cast_char arg, " /c ");
|
|
#endif
|
|
strcat(cast_char arg, cast_const_char WIN32_START_STRING);
|
|
if (*path == '"' && is_winnt()) strcat(cast_char arg, "\"\" ");
|
|
strcat(cast_char arg, cast_const_char path);
|
|
if (!is_winnt()) ct = GetConsoleTitleA(cast_char 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 */
|
|
fd_lock();
|
|
pid = spawnl("/bin/sh", "/bin/sh", "-c", arg, (char *)NULL);
|
|
fd_unlock();
|
|
#else
|
|
#if 1 /* spawn breaks mouse, do this only in graphics mode */
|
|
if (F && is_winnt()) {
|
|
/* spawn crashes on Win98 */
|
|
fd_lock();
|
|
spawnlp(_P_WAIT, cast_const_char x1, cast_const_char x1, "/c", cast_const_char arg, (char *)NULL);
|
|
fd_unlock();
|
|
/*FreeConsole();*/
|
|
goto free_ret;
|
|
} else
|
|
#endif
|
|
{
|
|
EINTRLOOP(pid, fork());
|
|
if (!pid) {
|
|
/* Win98 crashes if we spawn command.com and have some sockets open */
|
|
close_handles(0);
|
|
EINTRLOOP(rs, execlp(cast_const_char x1, cast_const_char x1, "/c", cast_const_char arg, (char *)NULL));
|
|
_exit(1);
|
|
}
|
|
}
|
|
#endif
|
|
if (!is_winnt()) {
|
|
portable_sleep(1000);
|
|
if (ct && GetConsoleTitleA(cast_char buffer2, sizeof buffer2) && !casecmp(buffer2, cast_uchar "start", 5)) {
|
|
SetConsoleTitleA(cast_const_char buffer);
|
|
}
|
|
}
|
|
if (pid != -1) EINTRLOOP(rp, waitpid(pid, NULL, 0));
|
|
goto free_ret;
|
|
|
|
free_ret:
|
|
free(arg);
|
|
return 0;
|
|
}
|
|
|
|
int exe_on_background(unsigned char *path, unsigned char *del, int fg)
|
|
{
|
|
#ifdef __CYGWIN__
|
|
unsigned char *x1;
|
|
unsigned char *arg;
|
|
unsigned char *delx;
|
|
int use_create_process = 0;
|
|
#if CYGWIN_VERSION_API_MAJOR > 1 || CYGWIN_VERSION_API_MINOR >= 154
|
|
if (is_winnt()) {
|
|
use_create_process = 1;
|
|
if (cygwin_internal(CW_SYNC_WINENV))
|
|
use_create_process = 0;
|
|
}
|
|
#endif
|
|
|
|
if (!is_winnt()) {
|
|
if (del && *del)
|
|
return -1;
|
|
}
|
|
|
|
x1 = cast_uchar GETSHELL;
|
|
if (!x1) x1 = cast_uchar DEFAULT_SHELL;
|
|
|
|
arg = stracpy(cast_uchar "");
|
|
if (use_create_process) {
|
|
add_to_strn(&arg, x1);
|
|
add_to_strn(&arg, cast_uchar " /c ");
|
|
}
|
|
add_to_strn(&arg, cast_uchar WIN32_START_STRING);
|
|
if (is_winnt()) if (*path == '"') add_to_strn(&arg, cast_uchar "\"\" ");
|
|
add_to_strn(&arg, path);
|
|
if (del && *del) {
|
|
add_to_strn(&arg, cast_uchar " & ");
|
|
add_to_strn(&arg, cast_uchar "del \"");
|
|
delx = os_conv_to_external_path(del, path);
|
|
add_to_strn(&arg, delx);
|
|
mem_free(delx);
|
|
add_to_strn(&arg, cast_uchar "\"");
|
|
}
|
|
|
|
/*debug("'%s'", arg);*/
|
|
|
|
if (use_create_process) {
|
|
PROCESS_INFORMATION pi;
|
|
STARTUPINFOA si;
|
|
unsigned char *cwd;
|
|
memset(&pi, 0, sizeof pi);
|
|
memset(&si, 0, sizeof si);
|
|
si.cb = sizeof si;
|
|
cwd = NULL;
|
|
if (fg == 2) {
|
|
unsigned char *bs;
|
|
cwd = stracpy(path_to_exe);
|
|
bs = cast_uchar strrchr(cast_const_char cwd, '\\');
|
|
if (bs) {
|
|
if (cwd[1] == ':' && cwd[2] == '\\' && bs == &cwd[2])
|
|
bs++;
|
|
*bs = 0;
|
|
}
|
|
}
|
|
fd_lock();
|
|
if (CreateProcessA(cast_char x1, cast_char arg, NULL, NULL, FALSE, CREATE_NO_WINDOW | (is_winnt() ? DETACHED_PROCESS : 0), NULL, cast_const_char cwd, &si, &pi)) {
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
}
|
|
fd_unlock();
|
|
if (cwd)
|
|
mem_free(cwd);
|
|
} else {
|
|
/* We need to fork here so that we can close handles */
|
|
pid_t pid;
|
|
/*int rs;*/
|
|
EINTRLOOP(pid, fork());
|
|
if (!pid) {
|
|
close_handles(0);
|
|
/*EINTRLOOP(rs, execlp(cast_const_char x1, cast_const_char x1, "/c", cast_const_char arg, (char *)NULL));*/
|
|
spawnlp(_P_DETACH, cast_const_char x1, cast_const_char x1, "/c", cast_const_char arg, (char *)NULL);
|
|
_exit(1);
|
|
}
|
|
}
|
|
|
|
mem_free(arg);
|
|
return 0;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int windows_charset(void)
|
|
{
|
|
#if defined(HAVE_NL_LANGINFO) && defined(HAVE_LANGINFO_H) && defined(CODESET)
|
|
int idx;
|
|
unsigned char *cp;
|
|
cp = cast_uchar nl_langinfo(CODESET);
|
|
idx = get_cp_index(cp);
|
|
if (idx >= 0)
|
|
return idx;
|
|
#endif
|
|
return utf8_table;
|
|
}
|
|
|
|
unsigned char *get_clipboard_text(struct terminal *term)
|
|
{
|
|
unsigned char buffer[256];
|
|
unsigned char *str, *s, *d, *result;
|
|
int l;
|
|
int r;
|
|
int rs;
|
|
int h;
|
|
if (!clipboard_support(term)) {
|
|
str = stracpy(clipboard);
|
|
goto cvt_ret;
|
|
}
|
|
/* O_TEXT doesn't work on clipboard handle */
|
|
h = c_open(cast_uchar "/dev/clipboard", O_RDONLY);
|
|
if (h == -1) return stracpy(clipboard);
|
|
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, (int)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;
|
|
|
|
cvt_ret:
|
|
result = convert(windows_charset(), term_charset(term), str, NULL);
|
|
mem_free(str);
|
|
return result;
|
|
}
|
|
|
|
void set_clipboard_text(struct terminal *term, unsigned char *data)
|
|
{
|
|
unsigned char *p;
|
|
unsigned char *conv_data;
|
|
int l;
|
|
int h;
|
|
int rs;
|
|
if (clipboard) mem_free(clipboard);
|
|
clipboard = convert(term_charset(term), windows_charset(), data, NULL);
|
|
if (!clipboard_support(term)) return;
|
|
/* O_TEXT doesn't work on clipboard handle */
|
|
h = c_open(cast_uchar "/dev/clipboard", O_WRONLY);
|
|
if (h == -1) return;
|
|
conv_data = init_str();
|
|
l = 0;
|
|
for (p = clipboard; *p; p++) {
|
|
if (*p == '\n') add_to_str(&conv_data, &l, cast_uchar "\r\n");
|
|
else add_chr_to_str(&conv_data, &l, *p);
|
|
}
|
|
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 && S_ISCHR(st.st_mode);
|
|
}
|
|
|
|
int get_windows_cp(int cons)
|
|
{
|
|
unsigned char str[8];
|
|
int cp, idx;
|
|
if (cons && is_winnt())
|
|
cp = GetConsoleOutputCP();
|
|
else
|
|
cp = GetACP();
|
|
if (cp <= 0 || cp >= 100000) return 0;
|
|
if (cp == 874) cp = 28605;
|
|
if (cp >= 28591 && cp <= 28605) {
|
|
sprintf(cast_char str, "8859-%d", cp - 28590);
|
|
} else {
|
|
sprintf(cast_char str, "%d", cp);
|
|
}
|
|
if ((idx = get_cp_index(str)) < 0) return 0;
|
|
return idx;
|
|
}
|
|
|
|
void set_window_title(unsigned char *title)
|
|
{
|
|
unsigned char *t, *p;
|
|
if (!title) return;
|
|
if (is_xterm()) return;
|
|
t = convert(utf8_table, get_windows_cp(1), title, NULL);
|
|
for (p = cast_uchar strchr(cast_const_char t, 1); p; p = cast_uchar strchr(cast_const_char(p + 1), 1))
|
|
*p = ' ';
|
|
SetConsoleTitleA(cast_const_char t);
|
|
mem_free(t);
|
|
}
|
|
|
|
unsigned char *get_window_title(void)
|
|
{
|
|
int r;
|
|
unsigned char buffer[1024];
|
|
if (is_xterm()) return NULL;
|
|
if (!(r = GetConsoleTitleA(cast_char buffer, sizeof buffer))) return NULL;
|
|
if (r >= 1024) r = 1023;
|
|
buffer[r] = 0;
|
|
return convert(get_windows_cp(1), utf8_table, buffer, NULL);
|
|
}
|
|
|
|
static void call_resize(unsigned char *x1, int x, int y)
|
|
{
|
|
pid_t pid, rp;
|
|
#ifndef _UWIN
|
|
int rs;
|
|
#endif
|
|
unsigned char arg[64];
|
|
#ifdef _UWIN
|
|
x++;
|
|
#endif
|
|
snprintf(cast_char arg, (int)sizeof(arg), "mode %d,%d", x, y);
|
|
#if defined(_UWIN) && !defined(__DMC__)
|
|
pid = spawnlp(x1, x1, "/c", arg, (char *)NULL);
|
|
#else
|
|
#if 0 /* spawn breaks mouse, don't use this */
|
|
if (is_winnt()) {
|
|
/* spawn crashes on Win98 */
|
|
fd_lock();
|
|
spawnlp(_P_WAIT, x1, x1, "/c", arg, (char *)NULL);
|
|
fd_unlock();
|
|
return;
|
|
} else
|
|
#endif
|
|
{
|
|
EINTRLOOP(pid, fork());
|
|
if (!pid) {
|
|
/* Win98 crashes if we spawn command.com and have some sockets open */
|
|
close_handles(1);
|
|
EINTRLOOP(rs, execlp(cast_const_char x1, cast_const_char x1, "/c", cast_const_char arg, (char *)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;
|
|
get_terminal_size(&old_x, &old_y);
|
|
x1 = cast_uchar GETSHELL;
|
|
if (!x1) x1 = cast_uchar DEFAULT_SHELL;
|
|
if (!is_winnt()) {
|
|
ct = GetConsoleTitleA(cast_char 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(&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);
|
|
get_terminal_size(&new_x, &new_y);
|
|
if (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) SetConsoleTitleA(cast_const_char buffer);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#elif defined(OS2)
|
|
|
|
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) {
|
|
old0 = c_dup(0);
|
|
old1 = c_dup(1);
|
|
old2 = c_dup(2);
|
|
fd_lock();
|
|
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));
|
|
} else
|
|
#endif
|
|
{
|
|
fd_lock();
|
|
}
|
|
pid = spawnlp(flags, shell, shell, "/c", path, (char *)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));
|
|
}
|
|
#endif
|
|
fd_unlock();
|
|
if (pid != -1) EINTRLOOP(rs, waitpid(pid, &ret, 0));
|
|
else ret = -1;
|
|
return ret;
|
|
}
|
|
|
|
unsigned char *get_clipboard_text(struct terminal *term)
|
|
{
|
|
unsigned char *ret = NULL;
|
|
|
|
if (os2_init_pm()) return NULL;
|
|
|
|
if (WinOpenClipbrd(os2_hab)) {
|
|
ULONG fmtInfo = 0;
|
|
|
|
if (WinQueryClipbrdFmtInfo(os2_hab, CF_TEXT, &fmtInfo)!=FALSE)
|
|
{
|
|
ULONG selClipText = WinQueryClipbrdData(os2_hab, CF_TEXT);
|
|
|
|
if (selClipText) {
|
|
unsigned char *u;
|
|
PCHAR pchClipText = (PCHAR)selClipText;
|
|
ret = stracpy(pchClipText);
|
|
while ((u = cast_uchar strchr(cast_const_char ret, 13))) memmove(u, u + 1, strlen(cast_const_char(u + 1)) + 1);
|
|
}
|
|
}
|
|
|
|
WinCloseClipbrd(os2_hab);
|
|
}
|
|
|
|
#ifdef G
|
|
if (F && ret) {
|
|
static int cp = -1;
|
|
unsigned char *d;
|
|
if (cp == -1) {
|
|
int c = WinQueryCp(os2_hmq);
|
|
unsigned char a[8];
|
|
snprintf(cast_char a, sizeof a, "%d", c);
|
|
if ((cp = get_cp_index(a)) < 0 || cp == utf8_table) cp = 0;
|
|
}
|
|
d = convert(cp, utf8_table, ret, NULL);
|
|
mem_free(ret);
|
|
ret = d;
|
|
}
|
|
#endif
|
|
|
|
os2_exit_pm();
|
|
|
|
return ret;
|
|
}
|
|
|
|
void set_clipboard_text(struct terminal *term, unsigned char *data)
|
|
{
|
|
unsigned char *d = NULL;
|
|
|
|
if (os2_init_pm()) return;
|
|
|
|
#ifdef G
|
|
if (F) {
|
|
static int cp = -1;
|
|
unsigned char *p;
|
|
if (cp == -1) {
|
|
int c = WinQueryCp(os2_hmq);
|
|
unsigned char a[8];
|
|
snprintf(cast_char a, sizeof a, "%d", c);
|
|
if ((cp = get_cp_index(a)) < 0 || cp == utf8_table) cp = 0;
|
|
}
|
|
d = convert(utf8_table, cp, data, NULL);
|
|
for (p = cast_uchar strchr(cast_const_char d, 1); p; p = cast_uchar strchr(cast_const_char(p + 1), 1))
|
|
*p = ' ';
|
|
data = d;
|
|
}
|
|
#endif
|
|
if (WinOpenClipbrd(os2_hab)) {
|
|
PVOID pvShrObject = NULL;
|
|
if (DosAllocSharedMem(&pvShrObject, NULL, strlen(cast_const_char data)+1, PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_GIVEABLE) == NO_ERROR) {
|
|
strcpy(cast_char pvShrObject, cast_const_char data);
|
|
WinEmptyClipbrd(os2_hab);
|
|
WinSetClipbrdData(os2_hab, (ULONG)pvShrObject, CF_TEXT, CFI_POINTER);
|
|
}
|
|
WinCloseClipbrd(os2_hab);
|
|
}
|
|
|
|
os2_exit_pm();
|
|
|
|
if (d) mem_free(d);
|
|
}
|
|
|
|
int clipboard_support(struct terminal *term)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
unsigned char *get_window_title(void)
|
|
{
|
|
#ifndef OS2_DEBUG
|
|
unsigned char *win_title = NULL;
|
|
SWCNTRL swData;
|
|
|
|
memset(&swData, 0, sizeof swData);
|
|
if (os2_switchhandle != NULLHANDLE && !WinQuerySwitchEntry(os2_switchhandle, &swData)) {
|
|
swData.szSwtitle[MAXNAMEL - 1] = 0;
|
|
win_title = stracpy(swData.szSwtitle);
|
|
if (swData.hwnd != NULLHANDLE && !os2_init_pm()) {
|
|
LONG len = WinQueryWindowTextLength(swData.hwnd);
|
|
if (len > 0 && len < MAXINT) {
|
|
mem_free(win_title);
|
|
win_title = mem_alloc(len + 1);
|
|
win_title[0] = 0;
|
|
WinQueryWindowText(swData.hwnd, len + 1, win_title);
|
|
win_title[len] = 0;
|
|
}
|
|
os2_exit_pm();
|
|
}
|
|
}
|
|
|
|
return win_title;
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
void set_window_title(unsigned char *title)
|
|
{
|
|
#ifndef OS2_DEBUG
|
|
SWCNTRL swData;
|
|
|
|
if (!title) return;
|
|
|
|
memset(&swData, 0, sizeof swData);
|
|
if (os2_switchhandle != NULLHANDLE && !WinQuerySwitchEntry(os2_switchhandle, &swData)) {
|
|
safe_strncpy(swData.szSwtitle, title, MAXNAMEL);
|
|
WinChangeSwitchEntry(os2_switchhandle, &swData);
|
|
if (swData.hwnd != NULLHANDLE && !os2_init_pm()) {
|
|
WinSetWindowText(swData.hwnd, title);
|
|
os2_exit_pm();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static tcount resize_count = 0;
|
|
|
|
int resize_window(int x, int y)
|
|
{
|
|
int xfont, yfont;
|
|
A_DECL(VIOMODEINFO, vmi);
|
|
SWCNTRL swData;
|
|
|
|
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)) goto resized;
|
|
}
|
|
return -1;
|
|
|
|
resized:
|
|
memset(&swData, 0, sizeof swData);
|
|
if (os2_switchhandle != NULLHANDLE && !WinQuerySwitchEntry(os2_switchhandle, &swData) && swData.hwnd != NULLHANDLE && !os2_init_pm()) {
|
|
SWP swp;
|
|
if (WinQueryWindowPos(swData.hwnd, &swp) && !(swp.fl & (SWP_MAXIMIZE | SWP_MINIMIZE | SWP_HIDE))) {
|
|
const int expand = 16383;
|
|
WinSetWindowPos(swData.hwnd, NULLHANDLE, swp.x, swp.y - expand, swp.cx + expand, swp.cy + expand, SWP_MOVE | SWP_SIZE);
|
|
}
|
|
os2_exit_pm();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#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;
|
|
int counted;
|
|
unsigned char data[1];
|
|
};
|
|
|
|
static void bgt(void *t_)
|
|
{
|
|
struct tdata *t = t_;
|
|
int rs;
|
|
ignore_signals();
|
|
t->fn(t->data, t->h);
|
|
EINTRLOOP(rs, (int)write(t->h, "x", 1));
|
|
EINTRLOOP(rs, close(t->h));
|
|
free(t);
|
|
}
|
|
|
|
#ifdef HAVE_ATHEOS_THREADS_H
|
|
#include <atheos/threads.h>
|
|
static uint32 bgat(void *t)
|
|
{
|
|
bgt(t);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#if defined(UNIX) || defined(OS2) || defined(WIN) || defined(INTERIX) || defined(RISCOS) || defined(ATHEOS) || defined(SPAD) || defined(HAIKU)
|
|
|
|
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 {
|
|
list_entry_1st
|
|
thread_id tid;
|
|
void (*fn)(void *);
|
|
void *data;
|
|
list_entry_last
|
|
};
|
|
|
|
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(0, NULL, 0))
|
|
goto retry;
|
|
goto err1;
|
|
}
|
|
thrd->fn = fn;
|
|
thrd->data = data;
|
|
if ((tid = thrd->tid = spawn_thread(started_thr, name, B_NORMAL_PRIORITY, thrd)) < B_NO_ERROR)
|
|
goto err2;
|
|
resume_thread(thrd->tid);
|
|
add_to_list(active_threads, thrd);
|
|
release_sem(thr_sem);
|
|
return tid;
|
|
|
|
err2:
|
|
free(thrd);
|
|
err1:
|
|
release_sem(thr_sem);
|
|
return -1;
|
|
}
|
|
|
|
void terminate_osdep(void)
|
|
{
|
|
struct list_head *p;
|
|
struct active_thread *thrd;
|
|
struct list_head *lthrd;
|
|
if (acquire_sem(thr_sem) < B_NO_ERROR) return;
|
|
foreach(struct active_thread, thrd, lthrd, active_threads) kill_thread(thrd->tid);
|
|
while (!list_empty(active_threads)) {
|
|
thrd = list_struct(active_threads.next, struct active_thread);
|
|
del_from_list(thrd);
|
|
free(thrd);
|
|
}
|
|
release_sem(thr_sem);
|
|
}
|
|
|
|
int start_thread(void (*fn)(void *, int), void *ptr, int l, int counted)
|
|
{
|
|
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(0, NULL, 0))
|
|
goto retry;
|
|
goto err1;
|
|
}
|
|
t->fn = fn;
|
|
t->h = p[1];
|
|
t->counted = counted;
|
|
memcpy(t->data, ptr, l);
|
|
if (start_thr(bgt, t, cast_uchar "links_thread") < 0)
|
|
goto err2;
|
|
return p[0];
|
|
|
|
err2:
|
|
free(t);
|
|
err1:
|
|
EINTRLOOP(rs, close(p[0]));
|
|
EINTRLOOP(rs, close(p[1]));
|
|
return -1;
|
|
}
|
|
|
|
|
|
#elif defined(HAVE_BEGINTHREAD) && defined(OS2)
|
|
|
|
int start_thread(void (*fn)(void *, int), void *ptr, int l, int counted)
|
|
{
|
|
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(0, NULL, 0))
|
|
goto retry;
|
|
goto err1;
|
|
}
|
|
t->fn = fn;
|
|
t->h = p[1];
|
|
t->counted = counted;
|
|
memcpy(t->data, ptr, l);
|
|
if (_beginthread(bgt, NULL, 65536, t) == -1)
|
|
goto err2;
|
|
return p[0];
|
|
|
|
err2:
|
|
free(t);
|
|
err1:
|
|
EINTRLOOP(rs, close(p[0]));
|
|
EINTRLOOP(rs, close(p[1]));
|
|
return -1;
|
|
}
|
|
|
|
#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, (int)write(h, c, 1));
|
|
}
|
|
EINTRLOOP(rs, close(h));
|
|
}
|
|
#endif /* #ifdef HAVE__READ_KBD */
|
|
|
|
#if defined(HAVE_MOUOPEN) && defined(HAVE_BEGINTHREAD) && !defined(USE_GPM)
|
|
|
|
#define USING_OS2_MOUSE
|
|
|
|
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 links_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(&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 links_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;
|
|
fd_lock();
|
|
if (!oms->terminate) mouse_draw_pointer(ms->col, ms->row);
|
|
fd_unlock();
|
|
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 links_event)) != sizeof(struct links_event)) break;
|
|
}
|
|
fd_lock();
|
|
mouse_h = -1;
|
|
MouClose(*mh);
|
|
fd_unlock();
|
|
ret:
|
|
EINTRLOOP(rs, close(oms->p[1]));
|
|
/*free(oms);*/
|
|
}
|
|
|
|
static void mouse_handle(void *oms_)
|
|
{
|
|
struct os2_mouse_spec *oms = (struct os2_mouse_spec *)oms_;
|
|
int r;
|
|
EINTRLOOP(r, (int)read(oms->p[0], oms->buffer + oms->bufptr, sizeof(struct links_event) - oms->bufptr));
|
|
if (r <= 0) {
|
|
unhandle_mouse(oms);
|
|
return;
|
|
}
|
|
if ((oms->bufptr += r) == sizeof(struct links_event)) {
|
|
oms->bufptr = 0;
|
|
oms->fn(oms->data, oms->buffer, sizeof(struct links_event));
|
|
}
|
|
}
|
|
|
|
void *handle_mouse(int cons, void (*fn)(void *, unsigned char *, int), void *data)
|
|
{
|
|
struct os2_mouse_spec *oms;
|
|
if (is_xterm()) return NULL;
|
|
/* This is never freed but it's allocated only once */
|
|
retry:
|
|
if (!(oms = malloc(sizeof(struct os2_mouse_spec)))) {
|
|
if (out_of_memory(0, 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], mouse_handle, NULL, oms);
|
|
return oms;
|
|
}
|
|
|
|
void unhandle_mouse(void *om)
|
|
{
|
|
struct os2_mouse_spec *oms = om;
|
|
want_draw();
|
|
oms->terminate = 1;
|
|
close_socket(&oms->p[0]);
|
|
done_draw();
|
|
}
|
|
|
|
void want_draw(void)
|
|
{
|
|
static int ansi = 0;
|
|
fd_lock();
|
|
if (!ansi) {
|
|
VioSetAnsi(1, 0);
|
|
ansi = 1;
|
|
}
|
|
if (mouse_h != -1) {
|
|
mouse_remove_pointer();
|
|
}
|
|
}
|
|
|
|
void done_draw(void)
|
|
{
|
|
fd_unlock();
|
|
}
|
|
|
|
#endif /* if HAVE_MOUOPEN */
|
|
|
|
#elif defined(HAVE_PTHREADS)
|
|
|
|
#ifdef OPENVMS
|
|
#define THREAD_NEED_STACK_SIZE 65536
|
|
int vms_thread_high_priority = 0;
|
|
#endif
|
|
|
|
#ifndef OPENVMS
|
|
static unsigned thread_count = 0;
|
|
#endif
|
|
|
|
static inline void reset_thread_count(void)
|
|
{
|
|
#ifndef OPENVMS
|
|
fd_lock();
|
|
thread_count = 0;
|
|
fd_unlock();
|
|
#endif
|
|
}
|
|
|
|
static void inc_thread_count(void)
|
|
{
|
|
#ifndef OPENVMS
|
|
fd_lock();
|
|
thread_count++;
|
|
fd_unlock();
|
|
#endif
|
|
}
|
|
|
|
static void dec_thread_count(void)
|
|
{
|
|
#ifndef OPENVMS
|
|
fd_lock();
|
|
if (!thread_count)
|
|
internal_error("thread_count underflow");
|
|
thread_count--;
|
|
fd_unlock();
|
|
#endif
|
|
}
|
|
|
|
static inline unsigned get_thread_count(void)
|
|
{
|
|
#ifndef OPENVMS
|
|
unsigned val;
|
|
fd_lock();
|
|
val = thread_count;
|
|
fd_unlock();
|
|
return val;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static void *bgpt(void *t)
|
|
{
|
|
int counted = ((struct tdata *)t)->counted;
|
|
bgt(t);
|
|
if (counted) dec_thread_count();
|
|
return NULL;
|
|
}
|
|
|
|
int start_thread(void (*fn)(void *, int), void *ptr, int l, int counted)
|
|
{
|
|
pthread_attr_t attr;
|
|
pthread_t thread;
|
|
struct tdata *t;
|
|
int p[2];
|
|
int rs;
|
|
if (c_pipe(p) < 0) return -1;
|
|
retry1:
|
|
if (!(t = malloc(sizeof(struct tdata) + l))) {
|
|
if (out_of_memory(0, NULL, 0))
|
|
goto retry1;
|
|
goto err1;
|
|
}
|
|
t->fn = fn;
|
|
t->h = p[1];
|
|
t->counted = counted;
|
|
memcpy(t->data, ptr, l);
|
|
retry2:
|
|
if (pthread_attr_init(&attr)) {
|
|
if (out_of_memory(0, NULL, 0))
|
|
goto retry2;
|
|
goto err2;
|
|
}
|
|
retry3:
|
|
if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
|
|
if (out_of_memory(0, NULL, 0))
|
|
goto retry3;
|
|
goto err3;
|
|
}
|
|
#ifdef THREAD_NEED_STACK_SIZE
|
|
retry4:
|
|
if (pthread_attr_setstacksize(&attr, THREAD_NEED_STACK_SIZE)) {
|
|
if (out_of_memory(0, NULL, 0))
|
|
goto retry4;
|
|
goto err3;
|
|
}
|
|
#endif
|
|
#ifdef OPENVMS
|
|
if (vms_thread_high_priority) {
|
|
struct sched_param param;
|
|
memset(¶m, 0, sizeof param);
|
|
if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED))
|
|
goto err3;
|
|
if (vms_thread_high_priority > 0) {
|
|
if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO))
|
|
goto err3;
|
|
param.sched_priority = PRI_FIFO_MIN;
|
|
if (pthread_attr_setschedparam(&attr, ¶m))
|
|
goto err3;
|
|
} else {
|
|
if (pthread_attr_setschedpolicy(&attr, SCHED_LFI_NP))
|
|
goto err3;
|
|
param.sched_priority = PRI_BG_MIN_NP;
|
|
if (pthread_attr_setschedparam(&attr, ¶m))
|
|
goto err3;
|
|
}
|
|
}
|
|
#endif
|
|
if (counted) inc_thread_count();
|
|
if (pthread_create(&thread, &attr, bgpt, t)) {
|
|
if (counted) dec_thread_count();
|
|
goto err3;
|
|
}
|
|
pthread_attr_destroy(&attr);
|
|
return p[0];
|
|
|
|
err3:
|
|
pthread_attr_destroy(&attr);
|
|
err2:
|
|
free(t);
|
|
err1:
|
|
EINTRLOOP(rs, close(p[0]));
|
|
EINTRLOOP(rs, close(p[1]));
|
|
return -1;
|
|
}
|
|
|
|
#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 counted)
|
|
{
|
|
int p[2];
|
|
int rs;
|
|
thread_id f;
|
|
struct tdata *t;
|
|
if (c_pipe(p) < 0) return -1;
|
|
retry:
|
|
if (!(t = malloc(sizeof(struct tdata) + l))) {
|
|
if (out_of_memory(0, NULL, 0))
|
|
goto retry;
|
|
goto err1;
|
|
}
|
|
t->fn = fn;
|
|
t->h = p[1];
|
|
t->counted = counted;
|
|
memcpy(t->data, ptr, l);
|
|
if ((f = spawn_thread("links_lookup", bgat, 0, 0, t)) == -1)
|
|
goto err2;
|
|
resume_thread(f);
|
|
return p[0];
|
|
|
|
err2:
|
|
free(t);
|
|
err1:
|
|
EINTRLOOP(rs, close(p[0]));
|
|
EINTRLOOP(rs, close(p[1]));
|
|
return -1;
|
|
}
|
|
|
|
#elif defined(DOS)
|
|
|
|
int start_thread(void (*fn)(void *, int), void *ptr, int l, int counted)
|
|
{
|
|
int p[2];
|
|
int rs;
|
|
if (c_pipe(p) < 0) return -1;
|
|
fn(ptr, p[1]);
|
|
EINTRLOOP(rs, close(p[1]));
|
|
return p[0];
|
|
}
|
|
|
|
#else /* HAVE_BEGINTHREAD */
|
|
|
|
int start_thread(void (*fn)(void *, int), void *ptr, int l, int counted)
|
|
{
|
|
int p[2];
|
|
pid_t f;
|
|
int rs;
|
|
if (c_pipe(p) < 0) return -1;
|
|
EINTRLOOP(f, fork());
|
|
if (!f) {
|
|
close_fork_tty();
|
|
EINTRLOOP(rs, close(p[0]));
|
|
fn(ptr, p[1]);
|
|
EINTRLOOP(rs, (int)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
|
|
|
|
#if !defined(USING_OS2_MOUSE) && !defined(DOS)
|
|
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 rs;
|
|
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];
|
|
if (_beginthread(input_thread, NULL, 0x10000, (void *)tp) == -1) {
|
|
EINTRLOOP(rs, close(fd[0]));
|
|
EINTRLOOP(rs, close(fd[1]));
|
|
return 0;
|
|
}
|
|
/*
|
|
#if defined(HAVE_MOUOPEN) && !defined(USE_GPM)
|
|
_beginthread(mouse_thread, NULL, 0x10000, (void *)tp);
|
|
#endif
|
|
*/
|
|
return fd[0];
|
|
}
|
|
|
|
#elif !defined(OPENVMS)
|
|
|
|
int get_input_handle(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#endif /* defined(HAVE_BEGINTHREAD) && defined(HAVE__READ_KBD) */
|
|
|
|
|
|
#if defined(__linux__) || defined(__LINUX__)
|
|
static unsigned char *is_on_linux_console(void)
|
|
{
|
|
static unsigned char tty_name[MAX_STR_LEN];
|
|
int r;
|
|
EINTRLOOP(r, (int)readlink("/proc/self/fd/0", cast_char tty_name, sizeof tty_name));
|
|
if (r >= 1 && r < (int)sizeof(tty_name)) {
|
|
#define pfx1 "/dev/tty"
|
|
#define pfx2 "/dev/vc/"
|
|
unsigned char c;
|
|
tty_name[r] = 0;
|
|
if (!memcmp(tty_name, pfx1, strlen(pfx1))) {
|
|
c = tty_name[strlen(pfx1)];
|
|
if (!(c >= '1' && c <= '9'))
|
|
return NULL;
|
|
return tty_name;
|
|
}
|
|
if (!memcmp(tty_name, pfx2, strlen(pfx2))) {
|
|
c = tty_name[strlen(pfx2)];
|
|
if (!(c >= '1' && c <= '9'))
|
|
return NULL;
|
|
return tty_name;
|
|
}
|
|
#undef pfx1
|
|
#undef pfx2
|
|
}
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
|
|
#if defined(USE_GPM) || defined(GRDRV_FB)
|
|
|
|
/* 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
|
|
|
|
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, do_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
|
|
}
|
|
|
|
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, do_sigprocmask(SIG_SETMASK, &gpm_sigset, NULL));
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef USE_GPM
|
|
|
|
struct gpm_mouse_spec {
|
|
int h;
|
|
void (*fn)(void *, unsigned char *, int);
|
|
void *data;
|
|
};
|
|
|
|
static void gpm_mouse_in(void *gms_)
|
|
{
|
|
struct gpm_mouse_spec *gms = (struct gpm_mouse_spec *)gms_;
|
|
int g;
|
|
Gpm_Event gev;
|
|
struct links_event ev;
|
|
set_handlers(gms->h, NULL, NULL, NULL);
|
|
save_gpm_signals();
|
|
g = Gpm_GetEvent(&gev);
|
|
restore_gpm_signals();
|
|
if (g <= 0) {
|
|
gms->h = -1;
|
|
return;
|
|
}
|
|
set_handlers(gms->h, gpm_mouse_in, NULL, gms);
|
|
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;
|
|
#ifdef GPM_B_FOURTH
|
|
else if (gev.buttons & GPM_B_FOURTH) ev.b = B_FOURTH;
|
|
#endif
|
|
#ifdef GPM_B_UP
|
|
else if (gev.buttons & GPM_B_UP) ev.b = B_FIFTH;
|
|
#endif
|
|
#ifdef GPM_B_DOWN
|
|
else if (gev.buttons & GPM_B_DOWN) ev.b = B_SIXTH;
|
|
#endif
|
|
else return;
|
|
if ((int)gev.type & GPM_DOWN) ev.b |= B_DOWN;
|
|
else if ((int)gev.type & GPM_UP) ev.b |= B_UP;
|
|
else if ((int)gev.type & GPM_DRAG) ev.b |= B_DRAG;
|
|
else return;
|
|
gms->fn(gms->data, (unsigned char *)&ev, sizeof(struct links_event));
|
|
}
|
|
|
|
void *handle_mouse(int cons, void (*fn)(void *, unsigned char *, int), void *data)
|
|
{
|
|
int h;
|
|
Gpm_Connect conn;
|
|
struct gpm_mouse_spec *gms;
|
|
|
|
#if defined(__linux__) || defined(__LINUX__)
|
|
if (!is_on_linux_console())
|
|
return NULL;
|
|
#endif
|
|
|
|
conn.eventMask = (unsigned short)~(unsigned)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, gpm_mouse_in, 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);
|
|
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, cast_uchar "GPM");
|
|
#ifdef HAVE_GPM_GETLIBVERSION
|
|
add_to_str(s, l, cast_uchar " (");
|
|
add_to_str(s, l, cast_uchar Gpm_GetLibVersion(NULL));
|
|
add_chr_to_str(s, l, ')');
|
|
#endif
|
|
}
|
|
|
|
#elif !defined(USING_OS2_MOUSE) && !defined(DOS)
|
|
|
|
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(OS2) || defined(WIN) || 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 = cast_uchar getenv("TERM");
|
|
if (!term || (upcase(term[0]) == 'B' && upcase(term[1]) == 'E')) return ENV_BE;
|
|
return 0;
|
|
}
|
|
|
|
#elif defined(HAIKU)
|
|
|
|
int get_system_env(void)
|
|
{
|
|
if (!getenv("SSH_CONNECTION")) return ENV_BE;
|
|
return 0;
|
|
}
|
|
|
|
#elif defined(WIN)
|
|
|
|
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 = stracpy(cast_uchar "");
|
|
if (*xterm) {
|
|
add_to_strn(&str, xterm);
|
|
add_to_strn(&str, cast_uchar " ");
|
|
}
|
|
add_to_strn(&str, exe);
|
|
add_to_strn(&str, cast_uchar " ");
|
|
add_to_strn(&str, param);
|
|
exec_on_terminal(term, str, cast_uchar "", 2);
|
|
mem_free(str);
|
|
}
|
|
|
|
static int open_in_new_twterm(struct terminal *term, unsigned char *exe, unsigned char *param)
|
|
{
|
|
unsigned char *twterm;
|
|
if (!(twterm = cast_uchar getenv("LINKS_TWTERM"))) twterm = cast_uchar "twterm -e";
|
|
exec_new_links(term, twterm, exe, param);
|
|
return 0;
|
|
}
|
|
|
|
unsigned char *links_xterm(void)
|
|
{
|
|
unsigned char *xterm;
|
|
if (!(xterm = cast_uchar getenv("LINKS_XTERM"))) {
|
|
#ifdef OPENVMS
|
|
xterm = cast_uchar "CREATE /TERMINAL /WAIT";
|
|
#elif defined(HAIKU)
|
|
xterm = cast_uchar "Terminal";
|
|
#else
|
|
xterm = cast_uchar "xterm -e";
|
|
#endif
|
|
}
|
|
return xterm;
|
|
}
|
|
|
|
static int open_in_new_xterm(struct terminal *term, unsigned char *exe, unsigned char *param)
|
|
{
|
|
exec_new_links(term, links_xterm(), exe, param);
|
|
return 0;
|
|
}
|
|
|
|
static int open_in_new_screen(struct terminal *term, unsigned char *exe, unsigned char *param)
|
|
{
|
|
exec_new_links(term, cast_uchar "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(cast_uchar "\"");
|
|
add_to_strn(&x, exe);
|
|
add_to_strn(&x, cast_uchar "\"");
|
|
exec_new_links(term, cast_uchar "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(cast_uchar "\"");
|
|
add_to_strn(&x, exe);
|
|
add_to_strn(&x, cast_uchar "\"");
|
|
exec_new_links(term, cast_uchar "start \"Links\" /c /f /fs", x, param);
|
|
mem_free(x);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WIN
|
|
static int open_in_new_win32(struct terminal *term, unsigned char *exe, unsigned char *param)
|
|
{
|
|
exec_new_links(term, cast_uchar "", 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, cast_uchar "'");
|
|
exec_new_links(term, cast_uchar(INTERIX_START_COMMAND " '\"Links\"' posix /u /c /bin/sh -c '"), exe, param_x);
|
|
mem_free(param_x);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if defined(BEOS) || defined(HAIKU)
|
|
static int open_in_new_be(struct terminal *term, unsigned char *exe, unsigned char *param)
|
|
{
|
|
exec_new_links(term, cast_uchar "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, cast_uchar "-base-session ")) {
|
|
param = cast_uchar strchr(cast_const_char param, ' ') + 1;
|
|
base = atoi(cast_const_char param);
|
|
param += strcspn(cast_const_char param, " ");
|
|
if (*param == ' ') param++;
|
|
}
|
|
if (!cmpbeg(param, cast_uchar "-target ")) {
|
|
param = cast_uchar strchr(cast_const_char param, ' ') + 1;
|
|
target = param;
|
|
param += strcspn(cast_const_char param, " ");
|
|
if (*param == ' ') *param++ = 0;
|
|
}
|
|
url = param;
|
|
info = create_session_info(base, url, target, &len);
|
|
return attach_g_terminal(term->cwd, info, len);
|
|
}
|
|
#endif
|
|
|
|
static const 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 WIN
|
|
{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
|
|
#if defined(BEOS) || defined(HAIKU)
|
|
{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
|
|
};
|
|
|
|
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; i < (int)array_elements(oinw); 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(WIN)
|
|
if (!strncmp(cast_const_char 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 WIN
|
|
if (!F && !(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;
|
|
}
|
|
|
|
void set_highpri(void)
|
|
{
|
|
#ifdef OS2
|
|
DosSetPriority(PRTYS_PROCESS, PRTYC_FOREGROUNDSERVER, 0, 0);
|
|
#endif
|
|
}
|
|
|
|
#if !defined(DOS) && !defined(OPENVMS)
|
|
void os_seed_random(unsigned char **pool, int *pool_size)
|
|
{
|
|
*pool = DUMMY;
|
|
*pool_size = 0;
|
|
}
|
|
#endif
|
|
|
|
#if defined(WIN)
|
|
int os_send_fg_cookie(int h)
|
|
{
|
|
DWORD pid;
|
|
pid = GetCurrentProcessId();
|
|
if (hard_write(h, (unsigned char *)&pid, sizeof pid) != sizeof pid)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
int os_receive_fg_cookie(int h)
|
|
{
|
|
DWORD pid;
|
|
BOOL (WINAPI *fn_AllowSetForegroundWindow)(DWORD);
|
|
if (hard_read(h, (unsigned char *)&pid, sizeof pid) != sizeof pid)
|
|
return -1;
|
|
fn_AllowSetForegroundWindow = (BOOL (WINAPI *)(DWORD))(void *)GetProcAddress(GetModuleHandleA("user32.dll"), "AllowSetForegroundWindow");
|
|
if (fn_AllowSetForegroundWindow)
|
|
fn_AllowSetForegroundWindow(pid);
|
|
return 0;
|
|
}
|
|
#else
|
|
int os_send_fg_cookie(int h)
|
|
{
|
|
return 0;
|
|
}
|
|
int os_receive_fg_cookie(int h)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int need_detach_console = 0;
|
|
|
|
void os_detach_console(void)
|
|
{
|
|
#if defined(WIN)
|
|
if (is_winnt())
|
|
FreeConsole();
|
|
#endif
|
|
#if !defined(NO_FORK_ON_EXIT)
|
|
{
|
|
pid_t rp;
|
|
/* Intel and PathScale handle fork gracefully */
|
|
#if !defined(__ICC) && !defined(__PATHSCALE__)
|
|
disable_openmp = 1;
|
|
#endif
|
|
EINTRLOOP(rp, fork());
|
|
if (!rp) {
|
|
reinit_child();
|
|
#if defined(HAVE_PTHREADS)
|
|
reset_thread_count();
|
|
#endif
|
|
}
|
|
if (rp > 0) {
|
|
#if defined(HAVE_PTHREADS)
|
|
while (get_thread_count()) {
|
|
portable_sleep(1000);
|
|
}
|
|
#endif
|
|
_exit(0);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if defined(OS2) || defined(DOS)
|
|
|
|
int get_country_language(int c)
|
|
{
|
|
static_const struct {
|
|
int code;
|
|
unsigned char *language;
|
|
} countries[] = {
|
|
{ 1, cast_uchar "English" },
|
|
{ 2, cast_uchar "French" },
|
|
{ 3, cast_uchar "Spanish" },
|
|
{ 4, cast_uchar "English" },
|
|
{ 7, cast_uchar "Russian" },
|
|
{ 27, cast_uchar "English" },
|
|
{ 30, cast_uchar "Greek" },
|
|
{ 31, cast_uchar "Dutch" },
|
|
{ 32, cast_uchar "Dutch" },
|
|
{ 33, cast_uchar "French" },
|
|
{ 34, cast_uchar "Spanish" },
|
|
{ 36, cast_uchar "Hungarian" },
|
|
{ 38, cast_uchar "Serbian" },
|
|
{ 39, cast_uchar "Italian" },
|
|
{ 40, cast_uchar "Romanian" },
|
|
{ 41, cast_uchar "Swiss German" },
|
|
{ 42, cast_uchar "Czech" },
|
|
{ 43, cast_uchar "German" },
|
|
{ 44, cast_uchar "English" },
|
|
{ 45, cast_uchar "Danish" },
|
|
{ 46, cast_uchar "Swedish" },
|
|
{ 47, cast_uchar "Norwegian" },
|
|
{ 48, cast_uchar "Polish" },
|
|
{ 49, cast_uchar "German" },
|
|
{ 52, cast_uchar "Spanish" },
|
|
{ 54, cast_uchar "Spanish" },
|
|
{ 55, cast_uchar "Brazilian Portuguese" },
|
|
{ 56, cast_uchar "Spanish" },
|
|
{ 57, cast_uchar "Spanish" },
|
|
{ 58, cast_uchar "Spanish" },
|
|
{ 61, cast_uchar "English" },
|
|
{ 64, cast_uchar "English" },
|
|
{ 65, cast_uchar "English" },
|
|
{ 90, cast_uchar "Turkish" },
|
|
{ 99, cast_uchar "English" },
|
|
{ 351, cast_uchar "Portuguese" },
|
|
{ 353, cast_uchar "English" },
|
|
{ 354, cast_uchar "Icelandic" },
|
|
{ 358, cast_uchar "Finnish" },
|
|
{ 359, cast_uchar "Bulgarian" },
|
|
{ 371, cast_uchar "Lithuanian" },
|
|
{ 372, cast_uchar "Estonian" },
|
|
{ 381, cast_uchar "Serbian" },
|
|
{ 384, cast_uchar "Croatian" },
|
|
{ 385, cast_uchar "Croatian" },
|
|
#ifdef DOS
|
|
{ 421, cast_uchar "Slovak" },
|
|
#else
|
|
{ 421, cast_uchar "Czech" },
|
|
#endif
|
|
{ 422, cast_uchar "Slovak" },
|
|
{ 593, cast_uchar "Spanish" },
|
|
};
|
|
int idx, i;
|
|
#define C_EQUAL(a, b) countries[a].code == (b)
|
|
#define C_ABOVE(a, b) countries[a].code > (b)
|
|
BIN_SEARCH(array_elements(countries), C_EQUAL, C_ABOVE, c, idx);
|
|
if (idx == -1)
|
|
return -1;
|
|
for (i = 0; i < n_languages(); i++)
|
|
if (!casestrcmp(language_name(i), countries[idx].language))
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(OS2)
|
|
|
|
int os_default_language(void)
|
|
{
|
|
COUNTRYCODE cc;
|
|
COUNTRYINFO ci;
|
|
ULONG ul;
|
|
int rc;
|
|
memset(&cc, 0, sizeof cc);
|
|
rc = DosQueryCtryInfo(sizeof ci, &cc, &ci, &ul);
|
|
if (!rc)
|
|
return get_country_language(ci.country);
|
|
return -1;
|
|
}
|
|
|
|
#elif defined(WIN)
|
|
|
|
int os_default_language(void)
|
|
{
|
|
LCID id;
|
|
unsigned char iso639[9];
|
|
unsigned char iso3166[9];
|
|
unsigned char loc[8 + 1 + 8 + 1];
|
|
unsigned char *lang;
|
|
lang = cast_uchar getenv("LANG");
|
|
if (lang) {
|
|
int l = get_language_from_lang(lang);
|
|
if (l >= 0)
|
|
return l;
|
|
}
|
|
id = GetUserDefaultUILanguage();
|
|
if (!GetLocaleInfoA(id, LOCALE_SISO639LANGNAME, cast_char iso639, 9))
|
|
return -1;
|
|
iso3166[0] = 0;
|
|
GetLocaleInfoA(id, LOCALE_SISO3166CTRYNAME, cast_char iso3166, 9);
|
|
strcpy(cast_char loc, cast_const_char iso639);
|
|
if (id >= 0x400 && iso3166[0]) {
|
|
strcat(cast_char loc, "_");
|
|
strcat(cast_char loc, cast_const_char iso3166);
|
|
}
|
|
return get_language_from_lang(loc);
|
|
}
|
|
|
|
#elif !defined(DOS)
|
|
|
|
int os_default_language(void)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(WIN) && defined(__CYGWIN__) && defined(HAVE_CYGWIN_CONV_PATH)
|
|
|
|
int os_default_charset(void)
|
|
{
|
|
unsigned char *term = cast_uchar getenv("TERM");
|
|
if (term) {
|
|
if (!casestrcmp(term, cast_uchar "cygwin")) {
|
|
#if defined(HAVE_NL_LANGINFO) && defined(HAVE_LANGINFO_H) && defined(CODESET)
|
|
return windows_charset();
|
|
#endif
|
|
}
|
|
if (!casestrcmp(term, cast_uchar "xterm")) {
|
|
return utf8_table;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
#elif defined(OS2)
|
|
|
|
int os_default_charset(void)
|
|
{
|
|
ULONG os2_cp[1];
|
|
ULONG size = 0;
|
|
int rc;
|
|
|
|
if (is_xterm())
|
|
return 0;
|
|
|
|
rc = DosQueryCp(sizeof(os2_cp), os2_cp, &size);
|
|
if ((!rc || rc == ERROR_CPLIST_TOO_SMALL) && size >= sizeof(ULONG)) {
|
|
unsigned char a[8];
|
|
int cp;
|
|
snprintf(cast_char a, sizeof a, "%lu", os2_cp[0]);
|
|
if ((cp = get_cp_index(a)) >= 0 && cp != utf8_table)
|
|
return cp;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#elif !defined(DOS)
|
|
|
|
int os_default_charset(void)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#if defined(__linux__) || defined(__LINUX__)
|
|
|
|
static pid_t cons_pid = -1;
|
|
|
|
static int cons_x = -1, cons_y = -1;
|
|
|
|
static void abort_save(void)
|
|
{
|
|
if (cons_pid > 0)
|
|
kill(cons_pid, SIGKILL);
|
|
cons_pid = -1;
|
|
close_socket(&cons_control[0]);
|
|
close_socket(&cons_control[1]);
|
|
close_socket(&cons_status[0]);
|
|
close_socket(&cons_status[1]);
|
|
}
|
|
|
|
static const char * const cons_savers[] = {
|
|
"/usr/lib/mc/cons.saver",
|
|
"/usr/libexec/mc/cons.saver",
|
|
};
|
|
|
|
void save_terminal(void)
|
|
{
|
|
int rs;
|
|
int p;
|
|
unsigned char *cons;
|
|
unsigned char st;
|
|
|
|
if (!(cons = is_on_linux_console()))
|
|
return;
|
|
|
|
for (p = 0; p < (int)array_elements(cons_savers); p++) {
|
|
EINTRLOOP(rs, access(cons_savers[p], X_OK));
|
|
if (!rs)
|
|
goto ok;
|
|
}
|
|
return;
|
|
ok:
|
|
|
|
c_pipe(cons_control);
|
|
c_pipe(cons_status);
|
|
|
|
block_signals(SIGTERM, 0);
|
|
|
|
cons_pid = fork();
|
|
if (!cons_pid) {
|
|
EINTRLOOP(rs, dup2(cons_control[0], 0));
|
|
if (rs < 0) _exit(1);
|
|
EINTRLOOP(rs, dup2(cons_status[1], 1));
|
|
if (rs < 0) _exit(1);
|
|
close_fork_tty();
|
|
|
|
execl(cons_savers[p], cons_savers[p], cast_const_char cons, NULL);
|
|
_exit(1);
|
|
}
|
|
if (cons_pid < 0) {
|
|
abort_save();
|
|
goto ret;
|
|
}
|
|
close_socket(&cons_control[0]);
|
|
close_socket(&cons_status[1]);
|
|
|
|
if (hard_read(cons_status[0], &st, 1) != 1 || st != 3) {
|
|
abort_save();
|
|
goto ret;
|
|
}
|
|
|
|
if (hard_write(cons_control[1], cast_uchar "1", 1) != 1) {
|
|
abort_save();
|
|
goto ret;
|
|
}
|
|
if (hard_read(cons_status[0], &st, 1) != 1 || st != 3) {
|
|
abort_save();
|
|
goto ret;
|
|
}
|
|
|
|
if (hard_write(cons_control[1], cast_uchar "3", 1) != 1) {
|
|
abort_save();
|
|
goto ret;
|
|
}
|
|
if (hard_read(cons_status[0], &st, 1) != 1 || st != 3) {
|
|
abort_save();
|
|
goto ret;
|
|
}
|
|
|
|
get_terminal_size(&cons_x, &cons_y);
|
|
|
|
ret:
|
|
unblock_signals();
|
|
}
|
|
|
|
void restore_terminal(void)
|
|
{
|
|
if (cons_pid > 0) {
|
|
int cx, cy;
|
|
unsigned char st;
|
|
|
|
get_terminal_size(&cx, &cy);
|
|
if (cons_x == cx && cons_y == cy) {
|
|
if (hard_write(cons_control[1], cast_uchar "4", 1) != 1) {
|
|
abort_save();
|
|
return;
|
|
}
|
|
if (hard_read(cons_status[0], &st, 1) != 1 || st != 3) {
|
|
abort_save();
|
|
return;
|
|
}
|
|
}
|
|
if (hard_write(cons_control[1], cast_uchar "2", 1) != 1) {
|
|
abort_save();
|
|
return;
|
|
}
|
|
cons_pid = -1;
|
|
close_socket(&cons_control[1]);
|
|
close_socket(&cons_status[0]);
|
|
}
|
|
}
|
|
|
|
#elif defined(OS2)
|
|
|
|
static int saved_x, saved_y;
|
|
static USHORT saved_len;
|
|
static unsigned char *saved_buffer;
|
|
static USHORT cursor_x, cursor_y;
|
|
|
|
void save_terminal(void)
|
|
{
|
|
size_t len;
|
|
if (is_xterm())
|
|
return;
|
|
get_terminal_size(&saved_x, &saved_y);
|
|
len = saved_x * saved_y * 4;
|
|
saved_len = len;
|
|
if (len != saved_len)
|
|
return;
|
|
saved_buffer = _tmalloc(saved_len);
|
|
if (!saved_buffer)
|
|
return;
|
|
if (VioReadCellStr(saved_buffer, &saved_len, 0, 0, 0))
|
|
goto x;
|
|
if (VioGetCurPos(&cursor_y, &cursor_x, 0))
|
|
goto x;
|
|
return;
|
|
x:
|
|
_tfree(saved_buffer);
|
|
saved_buffer = NULL;
|
|
}
|
|
|
|
void restore_terminal(void)
|
|
{
|
|
int x, y;
|
|
if (is_xterm())
|
|
return;
|
|
if (!saved_buffer)
|
|
return;
|
|
get_terminal_size(&x, &y);
|
|
if (x != saved_x || y != saved_y)
|
|
goto x;
|
|
VioWrtCellStr(saved_buffer, saved_len, 0, 0, 0);
|
|
VioSetCurPos(cursor_y, cursor_x, 0);
|
|
x:
|
|
_tfree(saved_buffer);
|
|
saved_buffer = NULL;
|
|
}
|
|
|
|
#elif defined(DOS)
|
|
|
|
#else
|
|
|
|
void save_terminal(void)
|
|
{
|
|
}
|
|
|
|
void restore_terminal(void)
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
void os_report_error_va(const char *caption, const char *msg, va_list l)
|
|
{
|
|
#ifdef OS2
|
|
if (!is_remote_connection()) {
|
|
char msg_buffer[OS_REPORT_ERROR_BUFFER];
|
|
vsnprintf(msg_buffer, sizeof msg_buffer, msg, l);
|
|
if (os2_init_pm()) return;
|
|
WinMessageBox(HWND_DESKTOP, NULLHANDLE, msg_buffer, caption, 0, MB_CANCEL | MB_ERROR | MB_APPLMODAL | MB_MOVEABLE);
|
|
os2_exit_pm();
|
|
}
|
|
#endif
|
|
#if defined(WIN) && !(defined(_UWIN) && !defined(GRDRV_PMSHELL))
|
|
if (!is_remote_connection()) {
|
|
char msg_buffer[OS_REPORT_ERROR_BUFFER];
|
|
vsnprintf(msg_buffer, sizeof msg_buffer, msg, l);
|
|
MessageBoxA(NULL, msg_buffer, caption, MB_OK | MB_ICONEXCLAMATION);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void os_report_error(const char *caption, const char *msg, ...)
|
|
{
|
|
va_list l;
|
|
va_start(l, msg);
|
|
os_report_error_va(caption, msg, l);
|
|
va_end(l);
|
|
}
|