1 /* os_dep.c
2  * (c) 2002 Mikulas Patocka
3  * This file is a part of the Links program, released under GPL.
4  */
5 
6 #include "links.h"
7 
8 #ifdef HAVE_SYS_IOCTL_H
9 #include <sys/ioctl.h>
10 #endif
11 
12 #ifdef USE_GPM
13 #include <gpm.h>
14 #endif
15 
16 #ifdef WIN
17 /*#define UNICODE*/
18 /*#define NO_STRICT*/
19 #include <windows.h>
20 #endif
21 
22 #if defined(__CYGWIN__) && defined(HAVE_LOCALE_H)
23 #include <locale.h>
24 #ifdef HAVE_LANGINFO_H
25 #include <langinfo.h>
26 #endif
27 
28 #endif
29 
30 #ifdef HAVE_PTHREADS
31 #include <pthread.h>
32 #endif
33 
34 #ifdef OS2
35 
36 #define INCL_MOU
37 #define INCL_VIO
38 #define INCL_DOSPROCESS
39 #define INCL_DOSERRORS
40 #define INCL_DOSMODULEMGR
41 #define INCL_DOSMISC
42 #define INCL_DOSNLS
43 #define INCL_WIN
44 #define INCL_WINCLIPBOARD
45 #define INCL_WINSWITCHLIST
46 #include <os2.h>
47 #include <io.h>
48 #include <process.h>
49 #include <sys/video.h>
50 #ifdef HAVE_SYS_FMUTEX_H
51 #include <sys/builtin.h>
52 #include <sys/fmutex.h>
53 #endif
54 
55 #ifdef X2
56 /* from xf86sup - XFree86 OS/2 support driver */
57 #include <pty.h>
58 #endif
59 
60 #define A_DECL(type, var) type var##1, var##2, *var = _THUNK_PTR_STRUCT_OK(&var##1) ? &var##1 : &var##2
61 
62 #endif
63 
64 #ifdef OS2
65 
66 /* The process crashes if we write to console from high address - so we must
67  * never do it.
68  * TCP/IP 4.0 returns EFAULT if we do I/O to/from high address - we test for
69  * EFAULT and retry with a bounce buffer.  */
70 
71 #define BOUNCE_BUFFER_SIZE	3584
72 
bounced_read(int fd,void * buf,size_t size)73 int bounced_read(int fd, void *buf, size_t size)
74 {
75 	unsigned char *bounce_buffer;
76 	int r;
77 
78 	if (fd < 3 && (my_uintptr_t)buf + size >= 0x20000000UL) goto bounce;
79 	r = _read(fd, buf, size);
80 	if (r == -1 && errno == EFAULT) goto bounce;
81 	return r;
82 
83 bounce:
84 	bounce_buffer = NULL;
85 	if (size > BOUNCE_BUFFER_SIZE) {
86 		bounce_buffer = os2_orig_malloc(size);
87 		if (!bounce_buffer)
88 			size = BOUNCE_BUFFER_SIZE;
89 	}
90 	if (!bounce_buffer)
91 		bounce_buffer = alloca(size);
92 	r = _read(fd, bounce_buffer, size);
93 	if (r > 0) memcpy(buf, bounce_buffer, r);
94 	if (size > BOUNCE_BUFFER_SIZE)
95 		free(bounce_buffer);
96 	return r;
97 }
98 
bounced_write(int fd,const void * buf,size_t size)99 int bounced_write(int fd, const void *buf, size_t size)
100 {
101 	unsigned char *bounce_buffer;
102 	int r;
103 
104 	if (fd < 3 && (my_uintptr_t)buf + size >= 0x20000000UL) goto bounce;
105 	r = _write(fd, buf, size);
106 	if (r == -1 && errno == EFAULT) goto bounce;
107 	return r;
108 
109 bounce:
110 	bounce_buffer = NULL;
111 	if (size > BOUNCE_BUFFER_SIZE) {
112 		bounce_buffer = os2_orig_malloc(size);
113 		if (!bounce_buffer)
114 			size = BOUNCE_BUFFER_SIZE;
115 	}
116 	if (!bounce_buffer)
117 		bounce_buffer = alloca(size);
118 	memcpy(bounce_buffer, buf, size);
119 	r = _write(fd, bounce_buffer, size);
120 	if (size > BOUNCE_BUFFER_SIZE)
121 		free(bounce_buffer);
122 	return r;
123 }
124 
portable_sleep(unsigned msec)125 void portable_sleep(unsigned msec)
126 {
127 	DosSleep(msec);
128 }
129 #endif
130 
131 #ifdef OS2_ADVANCED_HEAP
132 
133 #include <umalloc.h>
134 
135 #ifndef OBJ_ANY
136 #define OBJ_ANY		0x0400
137 #endif
138 
139 unsigned long mem_requested = 0;
140 unsigned long blocks_requested = 0;
141 
142 static int dosallocmem_attrib = PAG_READ | PAG_WRITE | PAG_COMMIT;
143 
144 #define HEAP_ALIGN		0x10000
145 #define HEAP_MAX_ALIGN		0x20000
146 
virtual_free(void * ptr,size_t len)147 void virtual_free(void *ptr, size_t len)
148 {
149 	int rc;
150 	rc = DosFreeMem(ptr);
151 	/*fprintf(stderr, "heap free %p -> %d\n", ptr, rc);*/
152 	if (rc)
153 		fatal_exit("DosFreeMem failed: %d", rc);
154 	if (len & 4095)
155 		len = (len | 4095) + 1;
156 	mem_requested -= len;
157 	blocks_requested--;
158 }
159 
heap_release(Heap_t h,void * ptr,size_t len)160 static void heap_release(Heap_t h, void *ptr, size_t len)
161 {
162 	virtual_free(ptr, len);
163 }
164 
virtual_alloc(size_t len)165 void *virtual_alloc(size_t len)
166 {
167 	void *result;
168 	int rc;
169 	rc = DosAllocMem(&result, len, dosallocmem_attrib);
170 	if (rc)
171 		return NULL;
172 	/*
173 	 * Hitting the shared arena has a negative impact on the whole
174 	 * system. Therefore, we fake failure (so that Links frees
175 	 * some caches) and try allocating near the shared arena only
176 	 * as a last resort.
177 	 */
178 	if ((unsigned long)result >= 0x12000000 &&
179 	    (unsigned long)result < 0x20000000) {
180 		if (!malloc_try_hard) {
181 			heap_release(NULL, result, len);
182 			return NULL;
183 		}
184 	}
185 	if (len & 4095)
186 		len = (len | 4095) + 1;
187 	mem_requested += len;
188 	blocks_requested++;
189 	return result;
190 }
191 
heap_alloc(Heap_t h,size_t * size,int * pclean)192 static void *heap_alloc(Heap_t h, size_t *size, int *pclean)
193 {
194 	void *result;
195 	/* If we rounded up to page size, EMX would join all allocations
196 	 * to one segment and refuse to free memory. So round up to
197 	 * page size - 1 */
198 	size_t real_size = *size;
199 	if (real_size < HEAP_MAX_ALIGN) {
200 		real_size = real_size | (HEAP_ALIGN - 1);
201 	} else {
202 		real_size |= 1;
203 	}
204 	result = virtual_alloc(real_size);
205 	/*fprintf(stderr, "heap alloc %d,%d -> %p\n", *size, real_size, result);*/
206 	if (result) {
207 		*size = real_size;
208 		*pclean = _BLOCK_CLEAN;
209 	}
210 	return result;
211 }
212 
213 static Heap_t original_heap = NULL;
214 
init_os2_heap(void)215 static void init_os2_heap(void)
216 {
217 	Heap_t new_heap;
218 	size_t init_size = _HEAP_MIN_SIZE;
219 	void *init_mem;
220 	int init_clean;
221 
222 	dosallocmem_attrib |= OBJ_ANY;
223 	init_mem = heap_alloc(NULL, &init_size, &init_clean);
224 	if (!init_mem) {
225 		dosallocmem_attrib &= ~OBJ_ANY;
226 		init_mem = heap_alloc(NULL, &init_size, &init_clean);
227 		if (!init_mem) {
228 			return;
229 		}
230 	}
231 	new_heap = _ucreate(init_mem, init_size, init_clean, _HEAP_REGULAR, heap_alloc, heap_release);
232 	if (!new_heap) {
233 		heap_release(NULL, init_mem, init_size);
234 		return;
235 	}
236 	if (_uopen(new_heap) == -1) {
237 #if defined(HAVE__UDESTROY) && defined(_FORCE)
238 		_udestroy(new_heap, _FORCE);
239 #else
240 		heap_release(NULL, init_mem, init_size);
241 #endif
242 		return;
243 	}
244 	if (dosallocmem_attrib & OBJ_ANY) {
245 		/* call malloc to initialize the default heap */
246 		void *p = malloc(1);
247 		if (p) free(p);
248 		original_heap = _udefault(new_heap);
249 	} else {
250 		_udefault(new_heap);
251 	}
252 }
253 
os2_orig_malloc(size_t len)254 void *os2_orig_malloc(size_t len)
255 {
256 	if (original_heap)
257 		return _umalloc(original_heap, len);
258 	return malloc(len);
259 }
260 
261 #endif
262 
263 
264 #ifdef OS2
265 static int os2_full_screen = 0;
266 static int os2_detached = 0;
267 static PTIB os2_tib = NULL;
268 PPIB os2_pib = NULL;
269 static HSWITCH os2_switchhandle = NULLHANDLE;
270 #ifdef HAVE_SYS_FMUTEX_H
271 static _fmutex fd_mutex;
272 #endif
273 #elif defined(HAVE_PTHREADS) && !defined(OPENVMS)
274 static pthread_mutex_t pth_mutex;
275 static void fd_lock(void);
276 static void fd_unlock(void);
fd_init(void)277 static void fd_init(void)
278 {
279 	int r;
280 	r = pthread_mutex_init(&pth_mutex, NULL);
281 	if (r)
282 		fatal_exit("pthread_mutex_create failed: %s", strerror(r));
283 }
284 #endif
285 
286 int page_size = 4096;
287 
init_page_size(void)288 void init_page_size(void)
289 {
290 	long getpg = -1;
291 #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
292 	if (getpg < 0)
293 		EINTRLOOP(getpg, sysconf(_SC_PAGESIZE));
294 #endif
295 #ifdef HAVE_GETPAGESIZE
296 	if (getpg < 0)
297 		getpg = getpagesize();
298 #endif
299 	if (getpg > 0 && !(getpg & (getpg - 1))) page_size = (int)getpg;
300 }
301 
302 #if !defined(OPENVMS) && !defined(DOS)
init_os(void)303 void init_os(void)
304 {
305 	/* Disable per-thread heap */
306 #if defined(HAVE_MALLOPT) && defined(M_ARENA_TEST)
307 	mallopt(M_ARENA_TEST, 1);
308 #endif
309 #if defined(HAVE_MALLOPT) && defined(M_ARENA_MAX)
310 	mallopt(M_ARENA_MAX, 1);
311 #endif
312 
313 #ifdef OS2
314 	DosGetInfoBlocks(&os2_tib, &os2_pib);
315 	if (os2_pib) {
316 		os2_switchhandle = WinQuerySwitchHandle(0, os2_pib->pib_ulpid);
317 		os2_full_screen = os2_pib->pib_ultype == 0;
318 		os2_detached = os2_pib->pib_ultype == 4;
319 		if (os2_pib->pib_ultype == 3) force_g = 1;
320 	}
321 #ifdef HAVE_SYS_FMUTEX_H
322 	if (_fmutex_create(&fd_mutex, 0))
323 		fatal_exit("failed to create fd mutex");
324 #endif
325 #elif defined(HAVE_PTHREADS) && !defined(OPENVMS)
326 	{
327 		int r;
328 		fd_init();
329 		r = pthread_atfork(fd_lock, fd_unlock, fd_init);
330 		if (r)
331 			fatal_exit("pthread_atfork failed: %s", strerror(r));
332 	}
333 #endif
334 
335 #ifdef OS2_ADVANCED_HEAP
336 	init_os2_heap();
337 #endif
338 
339 #ifdef WIN
340 	if (!GetConsoleCP())
341 		force_g = 1;
342 #if defined(__CYGWIN__) && defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
343 	{
344 		unsigned char *l = cast_uchar setlocale(LC_CTYPE, "");
345 		if (!l || !casestrcmp(l, cast_uchar "C")) {
346 			setlocale(LC_CTYPE, "en_US.utf-8");
347 		}
348 	}
349 #endif
350 #if defined(__CYGWIN__)
351 	/*
352 	 * When started from cmd.exe and the argument contains some characters
353 	 * not valid in the current locale, cygwin doesn't remove the quotation
354 	 * marks around the argument. So, we must remove the quotation marks
355 	 * here.
356 	 */
357 	if (getppid() == 1) {
358 		int i;
359 		for (i = 1; i < g_argc; i++) {
360 			unsigned char *a = cast_uchar g_argv[i];
361 			int l = (int)strlen(cast_const_char a);
362 			if (l >= 3 && a[0] == '"' && a[l - 1] == '"') {
363 				unsigned char *a2 = cast_uchar strdup(cast_const_char (a + 1));
364 				if (a2) {
365 					unsigned char *p, *q;
366 					a2[l - 2] = 0;
367 					for (p = q = a2; *p; p++) {
368 						if (p[0] == '\\' && p[1] == '"')
369 							continue;
370 						*q++ = *p;
371 					}
372 					*q = 0;
373 					g_argv[i] = cast_char a2;
374 				}
375 			}
376 		}
377 	}
378 #endif
379 #endif
380 #if defined(BEOS) || defined(HAIKU)
381 	if (getenv("TERM") == NULL) {
382 		/* probably launched from Tracker or Deskbar, force graphics mode */
383 		force_g = 1;
384 	}
385 #endif
386 }
387 #endif
388 
389 
is_safe_in_shell(unsigned char c)390 int is_safe_in_shell(unsigned char c)
391 {
392 	return c == '@' || c == '+' || c == '-' || c == '.' || c == ',' || c == '=' || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= 'a' && c <= 'z');
393 }
394 
is_safe_in_file(unsigned char c)395 static inline int is_safe_in_file(unsigned char c)
396 {
397 	return !(c < ' ' || c == '"' || c == '*' || c == '/' || c == ':' || c == '<' || c == '>' || c == '\\' || c == '|' || c >= 0x80);
398 }
399 
is_safe_in_url(unsigned char c)400 static inline int is_safe_in_url(unsigned char c)
401 {
402 	return is_safe_in_shell(c) || c == ':' || c == '/' || c >= 0x80;
403 }
404 
check_shell_security(unsigned char ** cmd)405 void check_shell_security(unsigned char **cmd)
406 {
407 	unsigned char *c = *cmd;
408 	while (*c) {
409 		if (!is_safe_in_shell(*c)) *c = '_';
410 		c++;
411 	}
412 }
413 
check_filename(unsigned char ** file)414 void check_filename(unsigned char **file)
415 {
416 	unsigned char *c = *file;
417 	while (*c) {
418 		if (!is_safe_in_file(*c)) *c = '_';
419 		c++;
420 	}
421 }
422 
check_shell_url(unsigned char * url)423 int check_shell_url(unsigned char *url)
424 {
425 	while (*url) {
426 		if (!is_safe_in_url(*url)) return -1;
427 		url++;
428 	}
429 	return 0;
430 }
431 
escape_path(unsigned char * path)432 unsigned char *escape_path(unsigned char *path)
433 {
434 	unsigned char *result;
435 	size_t i;
436 	if (strchr(cast_const_char path, '"')) return stracpy(path);
437 	for (i = 0; path[i]; i++) if (!is_safe_in_url(path[i])) goto do_esc;
438 	return stracpy(path);
439 	do_esc:
440 	result = stracpy(cast_uchar "\"");
441 	add_to_strn(&result, path);
442 	add_to_strn(&result, cast_uchar "\"");
443 	return result;
444 }
445 
get_e(unsigned char * env)446 static inline int get_e(unsigned char *env)
447 {
448 	unsigned char *v;
449 	if ((v = cast_uchar getenv(cast_const_char env))) return atoi(cast_const_char v);
450 	return 0;
451 }
452 
do_signal(int sig,void (* handler)(int))453 void do_signal(int sig, void (*handler)(int))
454 {
455 	errno = 0;
456 	while (signal(sig, handler) == SIG_ERR && errno == EINTR) errno = 0;
457 }
458 
ignore_signals(void)459 void ignore_signals(void)
460 {
461 	do_signal(SIGPIPE, SIG_IGN);
462 #ifdef SIGXFSZ
463 	do_signal(SIGXFSZ, SIG_IGN);
464 #endif
465 #ifdef OPENVMS
466 #ifdef SIGCHLD
467 	do_signal(SIGCHLD, SIG_IGN);
468 #endif
469 #ifdef SIGWINCH
470 	do_signal(SIGWINCH, SIG_IGN);
471 #endif
472 #endif
473 }
474 
get_absolute_seconds(void)475 time_t get_absolute_seconds(void)
476 {
477 	time_t t;
478 	errno = 0;
479 	EINTRLOOPX(t, time(NULL), (time_t)-1);
480 	return t;
481 }
482 
get_absolute_time(void)483 uttime get_absolute_time(void)
484 {
485 	struct timeval tv;
486 	int rs;
487 	EINTRLOOP(rs, gettimeofday(&tv, NULL));
488 	if (rs) fatal_exit("gettimeofday failed: %d", errno);
489 	return (uttime)tv.tv_sec * 1000 + (unsigned)tv.tv_usec / 1000;
490 }
491 
get_time(void)492 uttime get_time(void)
493 {
494 #if defined(OS2) || defined(WIN)
495 	static unsigned last_tim = 0;
496 	static uttime add = 0;
497 	unsigned tim;
498 #if defined(OS2)
499 	int rc;
500 	rc = DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, &tim, sizeof tim);
501 	if (rc) fatal_exit("DosQuerySysInfo failed: %d", rc);
502 #elif defined(WIN)
503 	tim = GetTickCount();
504 #endif
505 	if (tim < last_tim) {
506 		add += (uttime)1 << 31 << 1;
507 	}
508 	last_tim = tim;
509 	return tim | add;
510 #else
511 #if defined(HAVE_CLOCK_GETTIME) && defined(TIME_WITH_SYS_TIME) && (defined(CLOCK_MONOTONIC_RAW) || defined(CLOCK_MONOTONIC))
512 	struct timespec ts;
513 	int rs;
514 #if defined(CLOCK_MONOTONIC_RAW)
515 	EINTRLOOP(rs, clock_gettime(CLOCK_MONOTONIC_RAW, &ts));
516 	if (!rs) return (uttime)ts.tv_sec * 1000 + (unsigned)ts.tv_nsec / 1000000;
517 #endif
518 #if defined(CLOCK_MONOTONIC)
519 	EINTRLOOP(rs, clock_gettime(CLOCK_MONOTONIC, &ts));
520 	if (!rs) return (uttime)ts.tv_sec * 1000 + (unsigned)ts.tv_nsec / 1000000;
521 #endif
522 #endif
523 	return get_absolute_time();
524 #endif
525 }
526 
527 
528 static unsigned char *clipboard = NULL;
529 
os_free_clipboard(void)530 void os_free_clipboard(void)
531 {
532 	if (clipboard) mem_free(clipboard), clipboard = NULL;
533 }
534 
535 /* Terminal size */
536 
537 #if defined(UNIX) || defined(OS2) || defined(WIN) || defined(INTERIX) || defined(BEOS) || defined(RISCOS) || defined(ATHEOS) || defined(SPAD) || defined(OPENVMS) || defined(HAIKU)
538 static void (*terminal_resize_callback)(int, int);
539 #endif
540 
541 #if (defined(OS2) && defined(X2)) || defined(WIN) || defined(OPENVMS)
542 
543 #define TERMINAL_SIZE_POLLING
544 
545 /* Cygwin has a bug and loses SIGWINCH sometimes, so poll it */
546 
547 static struct timer *terminal_resize_timer = NULL;
548 static int old_xsize, old_ysize;
549 
terminal_resize_fn(void * p)550 static void terminal_resize_fn(void *p)
551 {
552 	int cur_xsize, cur_ysize;
553 	terminal_resize_timer = install_timer(TERMINAL_POLL_TIMEOUT, terminal_resize_fn, NULL);
554 	get_terminal_size(&cur_xsize, &cur_ysize);
555 	if ((old_xsize != cur_xsize) || (old_ysize != cur_ysize)) {
556 		old_xsize = cur_xsize;
557 		old_ysize = cur_ysize;
558 		terminal_resize_callback(cur_xsize, cur_ysize);
559 	}
560 }
561 
terminal_resize_poll(int x,int y)562 static void terminal_resize_poll(int x, int y)
563 {
564 	if (terminal_resize_timer)
565 		internal_error("terminal_resize_poll: timer already active");
566 	old_xsize = x;
567 	old_ysize = y;
568 #ifdef OS2
569 	if (!is_xterm())
570 		return;
571 #endif
572 	terminal_resize_timer = install_timer(TERMINAL_POLL_TIMEOUT, terminal_resize_fn, NULL);
573 }
574 
575 #endif
576 
577 #if defined(UNIX) || defined(OS2) || defined(WIN) || defined(INTERIX) || defined(BEOS) || defined(RISCOS) || defined(ATHEOS) || defined(SPAD) || defined(OPENVMS) || defined(HAIKU)
578 
579 #if defined(SIGWINCH) && !defined(OS2) && !defined(OPENVMS)
sigwinch(void * s)580 static void sigwinch(void *s)
581 {
582 	int cur_xsize, cur_ysize;
583 	get_terminal_size(&cur_xsize, &cur_ysize);
584 #ifdef TERMINAL_SIZE_POLLING
585 	old_xsize = cur_xsize;
586 	old_ysize = cur_ysize;
587 #endif
588 	terminal_resize_callback(cur_xsize, cur_ysize);
589 }
590 #endif
591 
handle_terminal_resize(void (* fn)(int,int),int * x,int * y)592 void handle_terminal_resize(void (*fn)(int, int), int *x, int *y)
593 {
594 	terminal_resize_callback = fn;
595 	get_terminal_size(x, y);
596 #if defined(SIGWINCH) && !defined(OS2) && !defined(OPENVMS)
597 	install_signal_handler(SIGWINCH, sigwinch, NULL, 0);
598 #endif
599 #ifdef TERMINAL_SIZE_POLLING
600 	terminal_resize_poll(*x, *y);
601 #endif
602 }
603 
unhandle_terminal_resize(void)604 void unhandle_terminal_resize(void)
605 {
606 #if defined(SIGWINCH) && !defined(OS2) && !defined(OPENVMS)
607 	install_signal_handler(SIGWINCH, NULL, NULL, 0);
608 #endif
609 #ifdef TERMINAL_SIZE_POLLING
610 	if (terminal_resize_timer) {
611 		kill_timer(terminal_resize_timer);
612 		terminal_resize_timer = NULL;
613 	}
614 #endif
615 }
616 
617 #if defined(OS2)
618 
get_terminal_size(int * x,int * y)619 void get_terminal_size(int *x, int *y)
620 {
621 	if (is_xterm()) {
622 #ifdef X2
623 		int arc;
624 		struct winsize win;
625 
626 		/* fd = STDIN_FILENO; */
627 		arc = ptioctl(1, TIOCGWINSZ, &win);
628 		if (arc) {
629 			*x = 80;
630 			*y = 24;
631 			return;
632 		}
633 		*y = win.ws_row;
634 		*x = win.ws_col;
635 		goto set_default;
636 #else
637 		*x = 80; *y = 24;
638 #endif
639 	} else {
640 		int a[2] = { 0, 0 };
641 		_scrsize(a);
642 		*x = a[0];
643 		*y = a[1];
644 #ifdef X2
645 		set_default:
646 #endif
647 		if (*x == 0) {
648 			*x = get_e("COLUMNS");
649 			if (*x == 0) *x = 80;
650 		}
651 		if (*y == 0) {
652 			*y = get_e("LINES");
653 			if (*y == 0) *y = 24;
654 		}
655 	}
656 }
657 
658 #elif !defined(OPENVMS)
659 
get_terminal_size(int * x,int * y)660 void get_terminal_size(int *x, int *y)
661 {
662 	int rs = -1;
663 #ifdef TIOCGWINSZ
664 	/* Sun Studio misoptimizes it */
665 	sun_volatile struct winsize ws;
666 	EINTRLOOP(rs, ioctl(1, TIOCGWINSZ, &ws));
667 #endif
668 	if ((rs == -1
669 #ifdef TIOCGWINSZ
670 		|| !(*x = ws.ws_col)
671 #endif
672 		) && !(*x = get_e(cast_uchar "COLUMNS"))) {
673 		*x = 80;
674 #ifdef _UWIN
675 		*x = 79;
676 #endif
677 	}
678 	if ((rs == -1
679 #ifdef TIOCGWINSZ
680 		|| !(*y = ws.ws_row)
681 #endif
682 		) && !(*y = get_e(cast_uchar "LINES"))) {
683 		*y = 24;
684 	}
685 }
686 
687 #endif
688 
689 #endif
690 
691 
692 #if defined(OS2) && defined(HAVE_SYS_FMUTEX_H)
693 
fd_lock(void)694 static void fd_lock(void)
695 {
696 	_fmutex_request(&fd_mutex, _FMR_IGNINT);
697 }
698 
fd_unlock(void)699 static void fd_unlock(void)
700 {
701 	_fmutex_release(&fd_mutex);
702 }
703 
704 #elif defined(HAVE_PTHREADS) && !defined(OPENVMS)
705 
fd_lock(void)706 static void fd_lock(void)
707 {
708 	int r;
709 	r = pthread_mutex_lock(&pth_mutex);
710 	if (r)
711 		fatal_exit("pthread_mutex_lock failed: %s", strerror(r));
712 }
713 
fd_unlock(void)714 static void fd_unlock(void)
715 {
716 	int r;
717 	r = pthread_mutex_unlock(&pth_mutex);
718 	if (r)
719 		fatal_exit("pthread_mutex_lock failed: %s", strerror(r));
720 }
721 
722 #else
723 
724 #define fd_lock()	do { } while (0)
725 #define fd_unlock()	do { } while (0)
726 
727 #endif
728 
new_fd_cloexec(int fd)729 static void new_fd_cloexec(int fd)
730 {
731 	int rs;
732 	EINTRLOOP(rs, fcntl(fd, F_SETFD, FD_CLOEXEC));
733 }
734 
new_fd_bin(int fd)735 static void new_fd_bin(int fd)
736 {
737 	new_fd_cloexec(fd);
738 #if defined(OS2) || (defined(WIN) && !defined(_UWIN)) || defined(DOS)
739 	setmode(fd, O_BINARY);
740 #endif
741 }
742 
743 #if !defined(OPENVMS)
744 
set_nonblock(int fd)745 void set_nonblock(int fd)
746 {
747 #ifdef O_NONBLOCK
748 	int rs;
749 	EINTRLOOP(rs, fcntl(fd, F_SETFL, O_NONBLOCK));
750 #elif defined(FIONBIO)
751 	int rs;
752 	int on = 1;
753 	EINTRLOOP(rs, ioctl(fd, FIONBIO, &on));
754 #endif
755 }
756 
757 #endif
758 
cleanup_fds(void)759 static int cleanup_fds(void)
760 {
761 #ifdef ENFILE
762 	if (errno == ENFILE) return abort_background_connections();
763 #endif
764 #ifdef EMFILE
765 	if (errno == EMFILE) return abort_background_connections();
766 #endif
767 	return 0;
768 }
769 
770 /* Pipe */
771 
c_pipe(int fd[2])772 int c_pipe(int fd[2])
773 {
774 	int r;
775 	do {
776 		fd_lock();
777 		EINTRLOOP(r, pipe(fd));
778 		if (!r) new_fd_bin(fd[0]), new_fd_bin(fd[1]);
779 		fd_unlock();
780 	} while (r == -1 && cleanup_fds());
781 	return r;
782 }
783 
c_dup(int oh)784 int c_dup(int oh)
785 {
786 	int h;
787 	do {
788 		fd_lock();
789 		EINTRLOOP(h, dup(oh));
790 		if (h != -1) new_fd_cloexec(h);
791 		fd_unlock();
792 	} while (h == -1 && cleanup_fds());
793 	return h;
794 }
795 
c_socket(int d,int t,int p)796 int c_socket(int d, int t, int p)
797 {
798 	int h;
799 	do {
800 		fd_lock();
801 		EINTRLOOP(h, socket(d, t, p));
802 		if (h != -1) new_fd_cloexec(h);
803 		fd_unlock();
804 	} while (h == -1 && cleanup_fds());
805 	return h;
806 }
807 
c_accept(int sh,struct sockaddr * addr,socklen_t * addrlen)808 int c_accept(int sh, struct sockaddr *addr, socklen_t *addrlen)
809 {
810 	int h;
811 	do {
812 		fd_lock();
813 		EINTRLOOP(h, accept(sh, addr, addrlen));
814 		if (h != -1) new_fd_cloexec(h);
815 		fd_unlock();
816 	} while (h == -1 && cleanup_fds());
817 	return h;
818 }
819 
c_open(unsigned char * path,int flags)820 int c_open(unsigned char *path, int flags)
821 {
822 	int h;
823 	do {
824 		fd_lock();
825 		EINTRLOOP(h, open(cast_const_char path, flags));
826 		if (h != -1) new_fd_bin(h);
827 		fd_unlock();
828 	} while (h == -1 && cleanup_fds());
829 	return h;
830 }
831 
c_open3(unsigned char * path,int flags,int mode)832 int c_open3(unsigned char *path, int flags, int mode)
833 {
834 	int h;
835 	do {
836 		fd_lock();
837 		EINTRLOOP(h, open(cast_const_char path, flags, mode));
838 		if (h != -1) new_fd_bin(h);
839 		fd_unlock();
840 	} while (h == -1 && cleanup_fds());
841 	return h;
842 }
843 
c_opendir(unsigned char * path)844 DIR *c_opendir(unsigned char *path)
845 {
846 	DIR *d;
847 	do {
848 		fd_lock();
849 		ENULLLOOP(d, opendir(cast_const_char path));
850 #ifdef HAVE_DIRFD
851 		if (d) {
852 			int h;
853 			EINTRLOOP(h, dirfd(d));
854 			if (h != -1) new_fd_cloexec(h);
855 		}
856 #endif
857 		fd_unlock();
858 	} while (!d && cleanup_fds());
859 	return d;
860 }
861 
862 #if defined(O_SIZE) && defined(__EMX__)
863 
open_prealloc(unsigned char * name,int flags,int mode,off_t siz)864 int open_prealloc(unsigned char *name, int flags, int mode, off_t siz)
865 {
866 	int h;
867 	fd_lock();
868 	EINTRLOOP(h, open(cast_const_char name, flags | O_SIZE, mode, (unsigned long)siz));
869 	if (h != -1) new_fd_bin(h);
870 	fd_unlock();
871 	return h;
872 }
873 
874 #elif defined(HAVE_OPEN_PREALLOC)
875 
open_prealloc(unsigned char * name,int flags,int mode,off_t siz)876 int open_prealloc(unsigned char *name, int flags, int mode, off_t siz)
877 {
878 	int h, rs;
879 	fd_lock();
880 	EINTRLOOP(h, open(cast_const_char name, flags, mode));
881 	if (h == -1) {
882 		fd_unlock();
883 		return -1;
884 	}
885 	new_fd_bin(h);
886 	fd_unlock();
887 #if defined(HAVE_FALLOCATE)
888 #if defined(FALLOC_FL_KEEP_SIZE)
889 	EINTRLOOP(rs, fallocate(h, FALLOC_FL_KEEP_SIZE, 0, siz));
890 #else
891 	EINTRLOOP(rs, fallocate(h, 0, 0, siz));
892 #endif
893 	if (!rs) return h;
894 #endif
895 #if defined(HAVE_POSIX_FALLOCATE)
896 	/* posix_fallocate may fall back to overwriting the file with zeros,
897 	   so don't use it on too big files */
898 	if (siz > 134217728)
899 		return h;
900 	do {
901 		rs = posix_fallocate(h, 0, siz);
902 	} while (rs == EINTR);
903 	if (!rs) return h;
904 #endif
905 	return h;
906 }
907 
908 #endif
909 
910 /* Exec */
911 
912 #if defined(UNIX) || defined(ATHEOS) || defined(INTERIX)
913 
is_twterm(void)914 int is_twterm(void)
915 {
916 	static int xt = -1;
917 	if (xt == -1) xt = !!getenv("TWDISPLAY");
918 	return xt;
919 }
920 
921 #else
922 
is_twterm(void)923 int is_twterm(void)
924 {
925 	return 0;
926 }
927 
928 #endif
929 
930 #if defined(UNIX) || defined(ATHEOS) || defined(INTERIX)
931 
is_screen(void)932 int is_screen(void)
933 {
934 	static int xt = -1;
935 	if (xt == -1) xt = !!getenv("STY");
936 	return xt;
937 }
938 
939 #else
940 
is_screen(void)941 int is_screen(void)
942 {
943 	return 0;
944 }
945 
946 #endif
947 
948 #if defined(UNIX) || defined(SPAD)
949 
is_xterm(void)950 int is_xterm(void)
951 {
952 	static int xt = -1;
953 	if (xt == -1) xt = getenv("DISPLAY") && *(char *)getenv("DISPLAY");
954 	return xt;
955 }
956 
957 #elif defined(BEOS) || defined(ATHEOS) || defined(DOS) || defined(HAIKU)
958 
is_xterm(void)959 int is_xterm(void)
960 {
961 	return 0;
962 }
963 
964 #elif defined(WIN) || defined(INTERIX) || defined(OS2)
965 
is_xterm(void)966 int is_xterm(void)
967 {
968 	static int xt = -1;
969 	if (xt == -1) xt = !!getenv("WINDOWID");
970 	return xt;
971 }
972 
973 #elif defined(RISCOS)
974 
is_xterm(void)975 int is_xterm(void)
976 {
977 	return 1;
978 }
979 
980 #endif
981 
982 #if defined(__linux__) || defined(__LINUX__)
983 static int cons_control[2] = { -1, -1 };
984 static int cons_status[2] = { -1, -1 };
985 #endif
986 
close_fork_tty(void)987 void close_fork_tty(void)
988 {
989 	struct terminal *t;
990 	struct list_head *lt;
991 	struct download *d;
992 	struct list_head *ld;
993 	struct connection *c;
994 	struct list_head *lc;
995 	struct k_conn *k;
996 	struct list_head *lk;
997 	int rs;
998 #ifndef NO_SIGNAL_HANDLERS
999 	if (signal_pipe[0] != -1) EINTRLOOP(rs, close(signal_pipe[0]));
1000 #ifndef __minix__
1001 	if (signal_pipe[1] != -1) EINTRLOOP(rs, close(signal_pipe[1]));
1002 #endif
1003 #endif
1004 #if defined(__linux__) || defined(__LINUX__)
1005 	if (cons_control[0] != -1) EINTRLOOP(rs, close(cons_control[0]));
1006 	if (cons_control[1] != -1) EINTRLOOP(rs, close(cons_control[1]));
1007 	if (cons_status[0] != -1) EINTRLOOP(rs, close(cons_status[0]));
1008 	if (cons_status[1] != -1) EINTRLOOP(rs, close(cons_status[1]));
1009 #endif
1010 #ifdef G
1011 	if (drv && drv->after_fork) drv->after_fork();
1012 #endif
1013 	if (terminal_pipe[1] != -1) EINTRLOOP(rs, close(terminal_pipe[1]));
1014 	if (s_unix_fd != -1) EINTRLOOP(rs, close(s_unix_fd));
1015 	foreach(struct terminal, t, lt, terminals) {
1016 		if (t->fdin > 0)
1017 			EINTRLOOP(rs, close(t->fdin));
1018 		if (t->handle_to_close >= 0)
1019 			EINTRLOOP(rs, close(t->handle_to_close));
1020 	}
1021 	foreach(struct download, d, ld, downloads) if (d->handle > 0)
1022 		EINTRLOOP(rs, close(d->handle));
1023 	foreach(struct connection, c, lc, queue) {
1024 		if (c->sock1 >= 0) EINTRLOOP(rs, close(c->sock1));
1025 		if (c->sock2 >= 0) EINTRLOOP(rs, close(c->sock2));
1026 	}
1027 	foreach(struct k_conn, k, lk, keepalive_connections)
1028 		EINTRLOOP(rs, close(k->conn));
1029 }
1030 
1031 
1032 #if defined(WIN)
1033 
get_path_to_exe(void)1034 void get_path_to_exe(void)
1035 {
1036 	/* Standard method (argv[0]) doesn't work, if links is executed from
1037 	   symlink --- it returns symlink name and cmd.exe is unable to start
1038 	   it */
1039 	unsigned r;
1040 	static unsigned char path[4096];
1041 	r = GetModuleFileNameA(NULL, cast_char path, sizeof path);
1042 	if (r <= 0 || r >= sizeof path) {
1043 		path_to_exe = cast_uchar g_argv[0];
1044 		return;
1045 	}
1046 	path_to_exe = path;
1047 }
1048 
1049 #elif defined(OS2)
1050 
get_path_to_exe(void)1051 void get_path_to_exe(void)
1052 {
1053 	/* If you spawn links with quotation marks from cmd.exe,
1054 	   the quotation marks will be present in g_argv[0] ... and will
1055 	   prevent executing it */
1056 	static unsigned char path[270];
1057 	path_to_exe = cast_uchar g_argv[0];
1058 	if (!os2_pib) return;
1059 	if (DosQueryModuleName(os2_pib->pib_hmte, sizeof path, path)) return;
1060 	path_to_exe = path;
1061 }
1062 
1063 static ULONG os2_old_type;
1064 static HAB os2_hab;
1065 static HMQ os2_hmq;
1066 
os2_init_pm(void)1067 static int os2_init_pm(void)
1068 {
1069 	if (!os2_pib) goto err0;
1070 	os2_old_type = os2_pib->pib_ultype;
1071 	os2_pib->pib_ultype = 3;
1072 	os2_hab = WinInitialize(0);
1073 	if (os2_hab == NULLHANDLE) goto err1;
1074 	os2_hmq = WinCreateMsgQueue(os2_hab, 0);
1075 	if (os2_hmq == NULLHANDLE) goto err2;
1076 	return 0;
1077 err2:
1078 	WinTerminate(os2_hab);
1079 err1:
1080 	os2_pib->pib_ultype = os2_old_type;
1081 err0:
1082 	return -1;
1083 }
1084 
os2_exit_pm(void)1085 static void os2_exit_pm(void)
1086 {
1087 	WinDestroyMsgQueue(os2_hmq);
1088 	WinTerminate(os2_hab);
1089 	os2_pib->pib_ultype = os2_old_type;
1090 }
1091 
os_get_system_name(unsigned char * buffer)1092 int os_get_system_name(unsigned char *buffer)
1093 {
1094 	ULONG version[3];
1095 	if (DosQuerySysInfo(QSV_VERSION_MAJOR, QSV_VERSION_REVISION, version, sizeof version))
1096 		return -1;
1097 	if (version[0] == 20) {
1098 		version[0] = 2;
1099 		if (version[1] == 10) {
1100 			version[1] = 1;
1101 		} else if (version[1] >= 30) {
1102 			version[0] = version[1] / 10;
1103 			version[1] %= 10;
1104 		}
1105 	}
1106 	sprintf(cast_char buffer, "OS/2 %d.%d i386", (int)version[0], (int)version[1]);
1107 	return 0;
1108 }
1109 
1110 #elif !defined(OPENVMS)
1111 
get_path_to_exe(void)1112 void get_path_to_exe(void)
1113 {
1114 	path_to_exe = cast_uchar g_argv[0];
1115 }
1116 
1117 #endif
1118 
init_os_terminal(void)1119 void init_os_terminal(void)
1120 {
1121 #ifdef INTERIX
1122 	{
1123 	/* Some sort of terminal bug in Interix, if we run xterm -e links,
1124 	   terminal doesn't switch to raw mode, executing "stty sane" fixes it.
1125 	   Don't do this workaround on console. */
1126 		unsigned char *term = cast_uchar getenv("TERM");
1127 		if (!term || casecmp(term, cast_uchar "interix", 7)) {
1128 			system("stty sane 2>/dev/null");
1129 		}
1130 	}
1131 #endif
1132 #ifdef OS2
1133 	if (os2_detached) {
1134 		fatal_exit("Links doesn't work in detached session");
1135 	}
1136 #endif
1137 }
1138 
1139 #ifdef INTERIX
1140 
cut_program_path(unsigned char * prog,unsigned char ** prog_start,unsigned char ** prog_end)1141 static inline void cut_program_path(unsigned char *prog, unsigned char **prog_start, unsigned char **prog_end)
1142 {
1143 	while (WHITECHAR(*prog)) prog++;
1144 	if (prog[0] == '"' || prog[0] == '\'') {
1145 		*prog_start = prog + 1;
1146 		*prog_end = cast_uchar strchr(cast_const_char(prog + 1), prog[0]);
1147 		if (!*prog_end)
1148 			*prog_end = cast_uchar strchr(cast_const_char prog, 0);
1149 	} else {
1150 		*prog_start = prog;
1151 		*prog_end = prog + strcspn(cast_const_char prog, " ");
1152 	}
1153 }
1154 
is_windows_drive(unsigned char * prog_start,unsigned char * prog_end)1155 static inline int is_windows_drive(unsigned char *prog_start, unsigned char *prog_end)
1156 {
1157 	if (prog_end - prog_start >= 3 && upcase(prog_start[0]) >= 'A' && upcase(prog_start[0]) <= 'Z' && prog_start[1] == ':')
1158 		return 1;
1159 	return 0;
1160 }
1161 
is_windows_program(unsigned char * prog_start,unsigned char * prog_end)1162 static inline int is_windows_program(unsigned char *prog_start, unsigned char *prog_end)
1163 {
1164 	if (prog_end - prog_start > 4 && (
1165 		!casecmp(prog_end - 4, cast_uchar ".exe", 4) ||
1166 		!casecmp(prog_end - 4, cast_uchar ".bat", 4)))
1167 			return 1;
1168 	return 0;
1169 }
1170 
1171 #endif
1172 
1173 #if defined(WIN) && defined(HAVE_CYGWIN_CONV_PATH)
1174 
os_conv_to_external_path(unsigned char * file,unsigned char * prog)1175 unsigned char *os_conv_to_external_path(unsigned char *file, unsigned char *prog)
1176 {
1177 	unsigned char *new_path;
1178 	ssize_t sz;
1179 	sz = cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, file, NULL, 0);
1180 	if (sz < 0 || sz >= MAXINT) return stracpy(file);
1181 	new_path = mem_alloc(sz);
1182 	sz = cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, file, new_path, sz);
1183 	if (sz < 0) {
1184 		mem_free(new_path);
1185 		return stracpy(file);
1186 	}
1187 	return new_path;
1188 }
1189 
1190 #elif defined(WIN) && defined(HAVE_CYGWIN_CONV_TO_FULL_WIN32_PATH)
1191 
os_conv_to_external_path(unsigned char * file,unsigned char * prog)1192 unsigned char *os_conv_to_external_path(unsigned char *file, unsigned char *prog)
1193 {
1194 #ifdef MAX_PATH
1195 	unsigned char new_path[MAX_PATH];
1196 #else
1197 	unsigned char new_path[1024];
1198 #endif
1199 	*new_path = 0;
1200 	cygwin_conv_to_full_win32_path(cast_const_char file, cast_char new_path);
1201 	if (!*new_path) return stracpy(file);
1202 	return stracpy(new_path);
1203 }
1204 
1205 #elif defined(WIN) && defined(HAVE_UWIN_PATH)
1206 
os_conv_to_external_path(unsigned char * file,unsigned char * prog)1207 unsigned char *os_conv_to_external_path(unsigned char *file, unsigned char *prog)
1208 {
1209 	unsigned char *new_path;
1210 	ssize_t sz, sz2;
1211 	sz = uwin_path(file, NULL, 0);
1212 	if (sz < 0 || sz >= MAXINT) return stracpy(file);
1213 	new_path = mem_alloc(sz + 1);
1214 	sz2 = uwin_path(file, new_path, sz + 1);
1215 	if (sz2 < 0 || sz2 > sz) {
1216 		mem_free(new_path);
1217 		return stracpy(file);
1218 	}
1219 	return new_path;
1220 }
1221 
1222 #elif defined(INTERIX) && defined(HAVE_UNIXPATH2WIN)
1223 
os_conv_to_external_path(unsigned char * file,unsigned char * prog)1224 unsigned char *os_conv_to_external_path(unsigned char *file, unsigned char *prog)
1225 {
1226 	unsigned char *prog_start, *prog_end;
1227 	if (!prog) prog = ".exe";
1228 	cut_program_path(prog, &prog_start, &prog_end);
1229 	/* Convert path only if the program has ".exe" or ".bat" extension */
1230 	if (is_windows_program(prog_start, prog_end)) {
1231 #ifdef MAX_PATH
1232 		unsigned char new_path[MAX_PATH];
1233 #else
1234 		unsigned char new_path[512];
1235 #endif
1236 		unsigned char *newstr;
1237 		int newstrl;
1238 		unsigned char *p;
1239 		if (unixpath2win(file, 0, new_path, sizeof(new_path)))
1240 			goto copy_path;
1241 		/*return stracpy(new_path);*/
1242 		newstr = init_str();
1243 		newstrl = 0;
1244 		for (p = new_path; *p; p++) {
1245 			/*
1246 			 * Unix shell hates backslash and Windows applications
1247 			 * accept '/'
1248 			 */
1249 			if (*p == '\\') add_chr_to_str(&newstr, &newstrl, '/');
1250 			else add_chr_to_str(&newstr, &newstrl, *p);
1251 		}
1252 		return newstr;
1253 	}
1254 	copy_path:
1255 	return stracpy(file);
1256 }
1257 
1258 #elif defined(OS2) || defined(DOS)
1259 
os_conv_to_external_path(unsigned char * file,unsigned char * prog)1260 unsigned char *os_conv_to_external_path(unsigned char *file, unsigned char *prog)
1261 {
1262 	unsigned char *f, *x;
1263 	f = stracpy(file);
1264 #ifdef OS2
1265 	if (prog)
1266 		return f;
1267 #endif
1268 	x = f;
1269 	while ((x = cast_uchar strchr(cast_const_char x, '/'))) *x = '\\';
1270 	return f;
1271 }
1272 
1273 
1274 #else
1275 
os_conv_to_external_path(unsigned char * file,unsigned char * prog)1276 unsigned char *os_conv_to_external_path(unsigned char *file, unsigned char *prog)
1277 {
1278 	return stracpy(file);
1279 }
1280 
1281 #endif
1282 
1283 #if defined(INTERIX) && defined(HAVE_WINPATH2UNIX)
1284 
os_fixup_external_program(unsigned char * prog)1285 unsigned char *os_fixup_external_program(unsigned char *prog)
1286 {
1287 	unsigned char *prog_start, *prog_end;
1288 	cut_program_path(prog, &prog_start, &prog_end);
1289 	if (is_windows_drive(prog_start, prog_end)) {
1290 #ifdef MAX_PATH
1291 		unsigned char new_path[MAX_PATH];
1292 #else
1293 		unsigned char new_path[1024];
1294 #endif
1295 		unsigned char *newstr;
1296 		int newstrl;
1297 		unsigned char *xpath;
1298 		if (is_windows_program(prog_start, prog_end)) {
1299 			/*
1300 			 * There is some bug in Interix. Executing Win32
1301 			 * binaries works from the console but doesn't work
1302 			 * from xterm. So we prepend "cmd /c" to the program
1303 			 * as a workaround.
1304 			 */
1305 			newstr = init_str();
1306 			newstrl = 0;
1307 			add_to_str(&newstr, &newstrl, cast_uchar "cmd /c ");
1308 			add_to_str(&newstr, &newstrl, prog);
1309 			return newstr;
1310 		}
1311 		xpath = memacpy(prog_start, prog_end - prog_start);
1312 		if (winpath2unix(xpath, 0, new_path, sizeof(new_path))) {
1313 			mem_free(xpath);
1314 			goto copy_prog;
1315 		}
1316 		mem_free(xpath);
1317 		newstr = init_str();
1318 		newstrl = 0;
1319 		add_bytes_to_str(&newstr, &newstrl, prog, prog_start - prog);
1320 		add_to_str(&newstr, &newstrl, new_path);
1321 		add_to_str(&newstr, &newstrl, prog_end);
1322 		return newstr;
1323 	}
1324 	copy_prog:
1325 	return stracpy(prog);
1326 }
1327 
1328 #else
1329 
os_fixup_external_program(unsigned char * prog)1330 unsigned char *os_fixup_external_program(unsigned char *prog)
1331 {
1332 	return stracpy(prog);
1333 }
1334 
1335 #endif
1336 
1337 
1338 #if defined(UNIX) || defined(INTERIX) || defined(BEOS) || defined(RISCOS) || defined(ATHEOS) || defined(SPAD) || defined(OPENVMS) || defined(DOS) || defined(HAIKU)
1339 
1340 #if defined(BEOS) && defined(HAVE_SETPGID)
1341 
exe(unsigned char * path,int fg)1342 int exe(unsigned char *path, int fg)
1343 {
1344 	pid_t p, rp;
1345 	int s, rs;
1346 	EINTRLOOP(p, fork());
1347 	if (!p) {
1348 		EINTRLOOP(rs, setpgid(0, 0));
1349 		system(path);
1350 		_exit(0);
1351 	}
1352 	if (p > 0) {
1353 		EINTRLOOP(rp, waitpid(p, &s, 0));
1354 	} else {
1355 		rs = system(path);
1356 		return rs;
1357 	}
1358 	return 0;
1359 }
1360 
1361 #else
1362 
1363 /* UNIX */
exe(unsigned char * path,int fg)1364 int exe(unsigned char *path, int fg)
1365 {
1366 #ifdef OPENVMS
1367 	if (!strcmp(cast_const_char path, DEFAULT_SHELL))
1368 		path = cast_uchar "";
1369 #endif
1370 #ifndef EXEC_IN_THREADS
1371 #ifdef SIGCHLD
1372 	do_signal(SIGCHLD, SIG_DFL);
1373 #endif
1374 	do_signal(SIGPIPE, SIG_DFL);
1375 #ifdef SIGXFSZ
1376 	do_signal(SIGXFSZ, SIG_DFL);
1377 #endif
1378 #ifdef SIGTSTP
1379 	do_signal(SIGTSTP, SIG_DFL);
1380 #endif
1381 #ifdef SIGCONT
1382 	do_signal(SIGCONT, SIG_DFL);
1383 #endif
1384 #ifdef SIGWINCH
1385 	do_signal(SIGWINCH, SIG_DFL);
1386 #endif
1387 #endif
1388 #ifdef G
1389 	if (F && drv->exec) return drv->exec(path, fg);
1390 #endif
1391 	return system(cast_const_char path);
1392 }
1393 
1394 #endif
1395 
1396 /* clipboard -> links */
get_clipboard_text(struct terminal * term)1397 unsigned char *get_clipboard_text(struct terminal *term)
1398 {
1399 #ifdef G
1400 	if (F && drv->get_clipboard_text) {
1401 		return drv->get_clipboard_text();
1402 	}
1403 #endif
1404 	if (!clipboard)
1405 		return NULL;
1406 	return convert(utf8_table, term_charset(term), clipboard, NULL);
1407 }
1408 
1409 /* links -> clipboard */
set_clipboard_text(struct terminal * term,unsigned char * data)1410 void set_clipboard_text(struct terminal *term, unsigned char *data)
1411 {
1412 #ifdef G
1413 	if (F && drv->set_clipboard_text) {
1414 		drv->set_clipboard_text(data);
1415 		return;
1416 	}
1417 #endif
1418 	if (clipboard) mem_free(clipboard);
1419 	clipboard = convert(term_charset(term), utf8_table, data, NULL);
1420 }
1421 
clipboard_support(struct terminal * term)1422 int clipboard_support(struct terminal *term)
1423 {
1424 #ifdef G
1425 	if (F && drv->set_clipboard_text) {
1426 		return 1;
1427 	}
1428 #endif
1429 	return 0;
1430 }
1431 
set_window_title(unsigned char * title)1432 void set_window_title(unsigned char *title)
1433 {
1434 	/* !!! FIXME */
1435 }
1436 
get_window_title(void)1437 unsigned char *get_window_title(void)
1438 {
1439 	/* !!! FIXME */
1440 	return NULL;
1441 }
1442 
resize_window(int x,int y)1443 int resize_window(int x, int y)
1444 {
1445 	return -1;
1446 }
1447 
1448 #elif defined(WIN)
1449 
is_winnt(void)1450 int is_winnt(void)
1451 {
1452 	OSVERSIONINFO v;
1453 	v.dwOSVersionInfoSize = sizeof v;
1454 	if (!GetVersionEx(&v)) return 0;
1455 	return v.dwPlatformId >= VER_PLATFORM_WIN32_NT;
1456 }
1457 
close_handles(int keep_output)1458 static void close_handles(int keep_output)
1459 {
1460 	int i, rs;
1461 	for (i = 0; i < FD_SETSIZE; i++) {
1462 		if (keep_output && (i == 1 || i == 2)) continue;
1463 		EINTRLOOP(rs, close(i));
1464 	}
1465 	EINTRLOOP(rs, open("nul", O_RDONLY));
1466 	if (!keep_output) {
1467 		EINTRLOOP(rs, open("nul", O_WRONLY));
1468 		EINTRLOOP(rs, open("nul", O_WRONLY));
1469 	}
1470 }
1471 
1472 #define WIN32_START_STRING	"start /wait "
1473 
exe(unsigned char * path,int fg)1474 int exe(unsigned char *path, int fg)
1475 {
1476 	/* This is very tricky. We must have exactly 3 arguments, the first
1477 	   one shell and the second one "/c", otherwise Cygwin would quote
1478 	   the arguments and trash them */
1479 	int ct = 0;
1480 	unsigned char buffer[1024];
1481 	unsigned char buffer2[1024];
1482 	size_t want_alloc;
1483 	pid_t pid, rp;
1484 #ifndef _UWIN
1485 	int rs;
1486 #endif
1487 	unsigned char *x1;
1488 	unsigned char *arg;
1489 	x1 = cast_uchar GETSHELL;
1490 	if (!x1) x1 = cast_uchar DEFAULT_SHELL;
1491 
1492 	want_alloc = strlen(cast_const_char WIN32_START_STRING) + 3 + strlen(cast_const_char path) + 1;
1493 #ifdef _UWIN
1494 	want_alloc += strlen(cast_const_char x1) + 4;
1495 	want_alloc *= 2;
1496 #endif
1497 
1498 	arg = malloc(want_alloc);
1499 	if (!arg) return -1;
1500 	*arg = 0;
1501 #ifdef _UWIN
1502 	strcat(cast_char arg, cast_const_char x1);
1503 	strcat(cast_char arg, " /c ");
1504 #endif
1505 	strcat(cast_char arg, cast_const_char WIN32_START_STRING);
1506 	if (*path == '"' && is_winnt()) strcat(cast_char arg, "\"\" ");
1507 	strcat(cast_char arg, cast_const_char path);
1508 	if (!is_winnt()) ct = GetConsoleTitleA(cast_char buffer, sizeof buffer);
1509 #if defined(_UWIN) && !defined(__DMC__)
1510 	{
1511 		unsigned char *q1 = arg, *q2 = arg;
1512 		while (*q1) {
1513 			if (*q1 == '\\') q2++;
1514 			q2++;
1515 			q1++;
1516 		}
1517 		while (1) {
1518 			*q2 = *q1;
1519 			if (*q1 == '\\') {
1520 				q2--;
1521 				*q2 = '\\';
1522 			}
1523 			if (q1 == arg) break;
1524 			q1--;
1525 			q2--;
1526 		}
1527 	}
1528 	/* UWin corrupts heap if we use threads and fork */
1529 	fd_lock();
1530 	pid = spawnl("/bin/sh", "/bin/sh", "-c", arg, (char *)NULL);
1531 	fd_unlock();
1532 #else
1533 #if 1		/* spawn breaks mouse, do this only in graphics mode */
1534 	if (F && is_winnt()) {
1535 		/* spawn crashes on Win98 */
1536 		fd_lock();
1537 		spawnlp(_P_WAIT, cast_const_char x1, cast_const_char x1, "/c", cast_const_char arg, (char *)NULL);
1538 		fd_unlock();
1539 		/*FreeConsole();*/
1540 		goto free_ret;
1541 	} else
1542 #endif
1543 	{
1544 		EINTRLOOP(pid, fork());
1545 		if (!pid) {
1546 	/* Win98 crashes if we spawn command.com and have some sockets open */
1547 			close_handles(0);
1548 			EINTRLOOP(rs, execlp(cast_const_char x1, cast_const_char x1, "/c", cast_const_char arg, (char *)NULL));
1549 			_exit(1);
1550 		}
1551 	}
1552 #endif
1553 	if (!is_winnt()) {
1554 		portable_sleep(1000);
1555 		if (ct && GetConsoleTitleA(cast_char buffer2, sizeof buffer2) && !casecmp(buffer2, cast_uchar "start", 5)) {
1556 			SetConsoleTitleA(cast_const_char buffer);
1557 		}
1558 	}
1559 	if (pid != -1) EINTRLOOP(rp, waitpid(pid, NULL, 0));
1560 	goto free_ret;
1561 
1562 	free_ret:
1563 	free(arg);
1564 	return 0;
1565 }
1566 
exe_on_background(unsigned char * path,unsigned char * del)1567 int exe_on_background(unsigned char *path, unsigned char *del)
1568 {
1569 #ifdef __CYGWIN__
1570 	unsigned char *x1;
1571 	unsigned char *arg;
1572 	unsigned char *delx;
1573 	int use_create_process = 0;
1574 #if CYGWIN_VERSION_API_MAJOR > 1 || CYGWIN_VERSION_API_MINOR >= 154
1575 	if (is_winnt()) {
1576 		use_create_process = 1;
1577 		if (cygwin_internal(CW_SYNC_WINENV))
1578 			use_create_process = 0;
1579 	}
1580 #endif
1581 
1582 	if (!is_winnt()) {
1583 		if (del && *del)
1584 			return -1;
1585 	}
1586 
1587 	x1 = cast_uchar GETSHELL;
1588 	if (!x1) x1 = cast_uchar DEFAULT_SHELL;
1589 
1590 	arg = stracpy(cast_uchar "");
1591 	if (use_create_process) {
1592 		add_to_strn(&arg, x1);
1593 		add_to_strn(&arg, cast_uchar " /c ");
1594 	}
1595 	add_to_strn(&arg, cast_uchar WIN32_START_STRING);
1596 	if (is_winnt()) if (*path == '"') add_to_strn(&arg, cast_uchar "\"\" ");
1597 	add_to_strn(&arg, path);
1598 	if (del && *del) {
1599 		add_to_strn(&arg, cast_uchar " & ");
1600 		add_to_strn(&arg, cast_uchar "del \"");
1601 		delx = os_conv_to_external_path(del, path);
1602 		add_to_strn(&arg, delx);
1603 		mem_free(delx);
1604 		add_to_strn(&arg, cast_uchar "\"");
1605 	}
1606 
1607 	/*debug("'%s'", arg);*/
1608 
1609 	if (use_create_process) {
1610 		PROCESS_INFORMATION pi;
1611 		STARTUPINFOA si;
1612 		memset(&pi, 0, sizeof pi);
1613 		memset(&si, 0, sizeof si);
1614 		si.cb = sizeof si;
1615 		fd_lock();
1616 		if (CreateProcessA(cast_char x1, cast_char arg, NULL, NULL, FALSE, CREATE_NO_WINDOW | (is_winnt() ? DETACHED_PROCESS : 0), NULL, NULL, &si, &pi)) {
1617 			CloseHandle(pi.hProcess);
1618 			CloseHandle(pi.hThread);
1619 		}
1620 		fd_unlock();
1621 	} else {
1622 	/* We need to fork here so that we can close handles */
1623 		pid_t pid;
1624 		/*int rs;*/
1625 		EINTRLOOP(pid, fork());
1626 		if (!pid) {
1627 			close_handles(0);
1628 			/*EINTRLOOP(rs, execlp(cast_const_char x1, cast_const_char x1, "/c", cast_const_char arg, (char *)NULL));*/
1629 			spawnlp(_P_DETACH, cast_const_char x1, cast_const_char x1, "/c", cast_const_char arg, (char *)NULL);
1630 			_exit(1);
1631 		}
1632 	}
1633 
1634 	mem_free(arg);
1635 	return 0;
1636 #else
1637 	return -1;
1638 #endif
1639 }
1640 
windows_charset(void)1641 int windows_charset(void)
1642 {
1643 #if defined(HAVE_NL_LANGINFO) && defined(HAVE_LANGINFO_H) && defined(CODESET)
1644 	int idx;
1645 	unsigned char *cp;
1646 	cp = cast_uchar nl_langinfo(CODESET);
1647 	idx = get_cp_index(cp);
1648 	if (idx >= 0)
1649 		return idx;
1650 #endif
1651 	return utf8_table;
1652 }
1653 
get_clipboard_text(struct terminal * term)1654 unsigned char *get_clipboard_text(struct terminal *term)
1655 {
1656 	unsigned char buffer[256];
1657 	unsigned char *str, *s, *d, *result;
1658 	int l;
1659 	int r;
1660 	int rs;
1661 	int h;
1662 	if (!clipboard_support(term)) {
1663 		str = stracpy(clipboard);
1664 		goto cvt_ret;
1665 	}
1666 	/* O_TEXT doesn't work on clipboard handle */
1667 	h = c_open(cast_uchar "/dev/clipboard", O_RDONLY);
1668 	if (h == -1) return stracpy(clipboard);
1669 	str = init_str();
1670 	l = 0;
1671 	/* Don't use hard_read because UWin has buggy end-of-file signalling.
1672 	   It resets the position to the beginning after signalling eof. */
1673 	while (1) {
1674 		EINTRLOOP(r, (int)read(h, buffer, sizeof buffer));
1675 		if (r <= 0) break;
1676 		add_bytes_to_str(&str, &l, buffer, r);
1677 	}
1678 	EINTRLOOP(rs, close(h));
1679 	for (s = str, d = str; *s; s++)
1680 		if (!(s[0] == '\r' && s[1] == '\n')) *d++ = *s;
1681 	*d = 0;
1682 
1683 	cvt_ret:
1684 	result = convert(windows_charset(), term_charset(term), str, NULL);
1685 	mem_free(str);
1686 	return result;
1687 }
1688 
set_clipboard_text(struct terminal * term,unsigned char * data)1689 void set_clipboard_text(struct terminal *term, unsigned char *data)
1690 {
1691 	unsigned char *p;
1692 	unsigned char *conv_data;
1693 	int l;
1694 	int h;
1695 	int rs;
1696 	if (clipboard) mem_free(clipboard);
1697 	clipboard = convert(term_charset(term), windows_charset(), data, NULL);
1698 	if (!clipboard_support(term)) return;
1699 	/* O_TEXT doesn't work on clipboard handle */
1700 	h = c_open(cast_uchar "/dev/clipboard", O_WRONLY);
1701 	if (h == -1) return;
1702 	conv_data = init_str();
1703 	l = 0;
1704 	for (p = clipboard; *p; p++) {
1705 		if (*p == '\n') add_to_str(&conv_data, &l, cast_uchar "\r\n");
1706 		else add_chr_to_str(&conv_data, &l, *p);
1707 	}
1708 	hard_write(h, conv_data, l);
1709 	mem_free(conv_data);
1710 	EINTRLOOP(rs, close(h));
1711 }
1712 
clipboard_support(struct terminal * term)1713 int clipboard_support(struct terminal *term)
1714 {
1715 	struct stat st;
1716 	int rs;
1717 	EINTRLOOP(rs, stat("/dev/clipboard", &st));
1718 	return !rs && S_ISCHR(st.st_mode);
1719 }
1720 
get_windows_cp(int cons)1721 int get_windows_cp(int cons)
1722 {
1723 	unsigned char str[8];
1724 	int cp, idx;
1725 	if (cons && is_winnt())
1726 		cp = GetConsoleOutputCP();
1727 	else
1728 		cp = GetACP();
1729 	if (cp <= 0 || cp >= 100000) return 0;
1730 	if (cp == 874) cp = 28605;
1731 	if (cp >= 28591 && cp <= 28605) {
1732 		sprintf(cast_char str, "8859-%d", cp - 28590);
1733 	} else {
1734 		sprintf(cast_char str, "%d", cp);
1735 	}
1736 	if ((idx = get_cp_index(str)) < 0) return 0;
1737 	return idx;
1738 }
1739 
set_window_title(unsigned char * title)1740 void set_window_title(unsigned char *title)
1741 {
1742 	unsigned char *t, *p;
1743 	if (!title) return;
1744 	if (is_xterm()) return;
1745 	t = convert(utf8_table, get_windows_cp(1), title, NULL);
1746 	for (p = cast_uchar strchr(cast_const_char t, 1); p; p = cast_uchar strchr(cast_const_char(p + 1), 1))
1747 		*p = ' ';
1748 	SetConsoleTitleA(cast_const_char t);
1749 	mem_free(t);
1750 }
1751 
get_window_title(void)1752 unsigned char *get_window_title(void)
1753 {
1754 	int r;
1755 	unsigned char buffer[1024];
1756 	if (is_xterm()) return NULL;
1757 	if (!(r = GetConsoleTitleA(cast_char buffer, sizeof buffer))) return NULL;
1758 	if (r >= 1024) r = 1023;
1759 	buffer[r] = 0;
1760 	return convert(get_windows_cp(1), utf8_table, buffer, NULL);
1761 }
1762 
call_resize(unsigned char * x1,int x,int y)1763 static void call_resize(unsigned char *x1, int x, int y)
1764 {
1765 	pid_t pid, rp;
1766 #ifndef _UWIN
1767 	int rs;
1768 #endif
1769 	unsigned char arg[64];
1770 #ifdef _UWIN
1771 	x++;
1772 #endif
1773 	snprintf(cast_char arg, (int)sizeof(arg), "mode %d,%d", x, y);
1774 #if defined(_UWIN) && !defined(__DMC__)
1775 	pid = spawnlp(x1, x1, "/c", arg, (char *)NULL);
1776 #else
1777 #if 0		/* spawn breaks mouse, don't use this */
1778 	if (is_winnt()) {
1779 		/* spawn crashes on Win98 */
1780 		fd_lock();
1781 		spawnlp(_P_WAIT, x1, x1, "/c", arg, (char *)NULL);
1782 		fd_unlock();
1783 		return;
1784 	} else
1785 #endif
1786 	{
1787 		EINTRLOOP(pid, fork());
1788 		if (!pid) {
1789 	/* Win98 crashes if we spawn command.com and have some sockets open */
1790 			close_handles(1);
1791 			EINTRLOOP(rs, execlp(cast_const_char x1, cast_const_char x1, "/c", cast_const_char arg, (char *)NULL));
1792 			_exit(1);
1793 		}
1794 	}
1795 #endif
1796 	if (pid != -1) EINTRLOOP(rp, waitpid(pid, NULL, 0));
1797 }
1798 
resize_window(int x,int y)1799 int resize_window(int x, int y)
1800 {
1801 	int old_x, old_y;
1802 	int ct = 0, fullscreen = 0;
1803 	unsigned char buffer[1024];
1804 	unsigned char *x1;
1805 	if (is_xterm()) return -1;
1806 	get_terminal_size(&old_x, &old_y);
1807 	x1 = cast_uchar GETSHELL;
1808 	if (!x1) x1 = cast_uchar DEFAULT_SHELL;
1809 	if (!is_winnt()) {
1810 		ct = GetConsoleTitleA(cast_char buffer, sizeof buffer);
1811 	}
1812 
1813 	call_resize(x1, x, y);
1814 	if (!is_winnt()) {
1815 		int new_x, new_y;
1816 	/* If we resize console on Win98 in fullscreen mode, it won't be
1817 	   notified by Cygwin (it is valid for all Cygwin apps). So we must
1818 	   switch to windowed mode, resize it again (twice, because resizing
1819 	   to the same size won't have an effect) and switch back to full-screen
1820 	   mode. */
1821 	/* I'm not sure what's the behavior on WinNT 4. Anybody wants to test?
1822 	   */
1823 		if (!fullscreen && (get_terminal_size(&new_x, &new_y), (new_x != x || new_y != y))) {
1824 			fullscreen = 1;
1825 #ifdef __CYGWIN__
1826 			keybd_event(VK_MENU, 0x38, 0, 0);
1827 			keybd_event(VK_RETURN, 0x1c, 0, 0);
1828 			keybd_event(VK_RETURN, 0x1c, KEYEVENTF_KEYUP, 0);
1829 			keybd_event(VK_MENU, 0x38, KEYEVENTF_KEYUP, 0);
1830 #endif
1831 			if (y != 25) call_resize(x1, 80, 25);
1832 			else call_resize(x1, 80, 50);
1833 			call_resize(x1, x, y);
1834 			get_terminal_size(&new_x, &new_y);
1835 			if (new_x != x || new_y != y) call_resize(x1, old_x, old_y);
1836 #ifdef __CYGWIN__
1837 			keybd_event(VK_MENU, 0x38, 0, 0);
1838 			keybd_event(VK_RETURN, 0x1c, 0, 0);
1839 			keybd_event(VK_RETURN, 0x1c, KEYEVENTF_KEYUP, 0);
1840 			keybd_event(VK_MENU, 0x38, KEYEVENTF_KEYUP, 0);
1841 #endif
1842 		}
1843 		if (ct) SetConsoleTitleA(cast_const_char buffer);
1844 	}
1845 	return 0;
1846 }
1847 
1848 #elif defined(OS2)
1849 
exe(unsigned char * path,int fg)1850 int exe(unsigned char *path, int fg)
1851 {
1852 	int flags = P_SESSION;
1853 	pid_t pid, rs;
1854 	int ret;
1855 #ifdef G
1856 	int old0 = 0, old1 = 1, old2 = 2;
1857 #endif
1858 	unsigned char *shell;
1859 	if (!(shell = GETSHELL)) shell = DEFAULT_SHELL;
1860 	if (is_xterm()) flags |= P_BACKGROUND;
1861 #ifdef G
1862 	if (F) {
1863 		old0 = c_dup(0);
1864 		old1 = c_dup(1);
1865 		old2 = c_dup(2);
1866 		fd_lock();
1867 		if (old0 >= 0) EINTRLOOP(rs, close(0));
1868 		if (old1 >= 0) EINTRLOOP(rs, close(1));
1869 		if (old2 >= 0) EINTRLOOP(rs, close(2));
1870 		if (old0 >= 0) EINTRLOOP(rs, open("con", O_RDONLY));
1871 		if (old1 >= 0) EINTRLOOP(rs, open("con", O_WRONLY));
1872 		if (old2 >= 0) EINTRLOOP(rs, open("con", O_WRONLY));
1873 	} else
1874 #endif
1875 	{
1876 		fd_lock();
1877 	}
1878 	pid = spawnlp(flags, shell, shell, "/c", path, (char *)NULL);
1879 #ifdef G
1880 	if (F) {
1881 		if (old0 >= 0) EINTRLOOP(rs, dup2(old0, 0));
1882 		if (old1 >= 0) EINTRLOOP(rs, dup2(old1, 1));
1883 		if (old2 >= 0) EINTRLOOP(rs, dup2(old2, 2));
1884 		if (old0 >= 0) EINTRLOOP(rs, close(old0));
1885 		if (old1 >= 0) EINTRLOOP(rs, close(old1));
1886 		if (old2 >= 0) EINTRLOOP(rs, close(old2));
1887 	}
1888 #endif
1889 	fd_unlock();
1890 	if (pid != -1) EINTRLOOP(rs, waitpid(pid, &ret, 0));
1891 	else ret = -1;
1892 	return ret;
1893 }
1894 
get_clipboard_text(struct terminal * term)1895 unsigned char *get_clipboard_text(struct terminal *term)
1896 {
1897 	unsigned char *ret = NULL;
1898 
1899 	if (os2_init_pm()) return NULL;
1900 
1901 	if (WinOpenClipbrd(os2_hab)) {
1902 		ULONG fmtInfo = 0;
1903 
1904 		if (WinQueryClipbrdFmtInfo(os2_hab, CF_TEXT, &fmtInfo)!=FALSE)
1905 		{
1906 			ULONG selClipText = WinQueryClipbrdData(os2_hab, CF_TEXT);
1907 
1908 			if (selClipText) {
1909 				unsigned char *u;
1910 				PCHAR pchClipText = (PCHAR)selClipText;
1911 				ret = stracpy(pchClipText);
1912 				while ((u = cast_uchar strchr(cast_const_char ret, 13))) memmove(u, u + 1, strlen(cast_const_char(u + 1)) + 1);
1913 			}
1914 		}
1915 
1916 		WinCloseClipbrd(os2_hab);
1917 	}
1918 
1919 #ifdef G
1920 	if (F && ret) {
1921 		static int cp = -1;
1922 		unsigned char *d;
1923 		if (cp == -1) {
1924 			int c = WinQueryCp(os2_hmq);
1925 			unsigned char a[8];
1926 			snprintf(cast_char a, sizeof a, "%d", c);
1927 			if ((cp = get_cp_index(a)) < 0 || cp == utf8_table) cp = 0;
1928 		}
1929 		d = convert(cp, utf8_table, ret, NULL);
1930 		mem_free(ret);
1931 		ret = d;
1932 	}
1933 #endif
1934 
1935 	os2_exit_pm();
1936 
1937 	return ret;
1938 }
1939 
set_clipboard_text(struct terminal * term,unsigned char * data)1940 void set_clipboard_text(struct terminal *term, unsigned char *data)
1941 {
1942 	unsigned char *d = NULL;
1943 
1944 	if (os2_init_pm()) return;
1945 
1946 #ifdef G
1947 	if (F) {
1948 		static int cp = -1;
1949 		unsigned char *p;
1950 		if (cp == -1) {
1951 			int c = WinQueryCp(os2_hmq);
1952 			unsigned char a[8];
1953 			snprintf(cast_char a, sizeof a, "%d", c);
1954 			if ((cp = get_cp_index(a)) < 0 || cp == utf8_table) cp = 0;
1955 		}
1956 		d = convert(utf8_table, cp, data, NULL);
1957 		for (p = cast_uchar strchr(cast_const_char d, 1); p; p = cast_uchar strchr(cast_const_char(p + 1), 1))
1958 			*p = ' ';
1959 		data = d;
1960 	}
1961 #endif
1962 	if (WinOpenClipbrd(os2_hab)) {
1963 		PVOID pvShrObject = NULL;
1964 		if (DosAllocSharedMem(&pvShrObject, NULL, strlen(cast_const_char data)+1, PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_GIVEABLE) == NO_ERROR) {
1965 			strcpy(cast_char pvShrObject, cast_const_char data);
1966 			WinEmptyClipbrd(os2_hab);
1967 			WinSetClipbrdData(os2_hab, (ULONG)pvShrObject, CF_TEXT, CFI_POINTER);
1968 		}
1969 		WinCloseClipbrd(os2_hab);
1970 	}
1971 
1972 	os2_exit_pm();
1973 
1974 	if (d) mem_free(d);
1975 }
1976 
clipboard_support(struct terminal * term)1977 int clipboard_support(struct terminal *term)
1978 {
1979 	return 1;
1980 }
1981 
get_window_title(void)1982 unsigned char *get_window_title(void)
1983 {
1984 #ifndef OS2_DEBUG
1985 	unsigned char *win_title = NULL;
1986 	SWCNTRL swData;
1987 
1988 	memset(&swData, 0, sizeof swData);
1989 	if (os2_switchhandle != NULLHANDLE && !WinQuerySwitchEntry(os2_switchhandle, &swData)) {
1990 		swData.szSwtitle[MAXNAMEL - 1] = 0;
1991 		win_title = stracpy(swData.szSwtitle);
1992 		if (swData.hwnd != NULLHANDLE && !os2_init_pm()) {
1993 			LONG len = WinQueryWindowTextLength(swData.hwnd);
1994 			if (len > 0 && len < MAXINT) {
1995 				mem_free(win_title);
1996 				win_title = mem_alloc(len + 1);
1997 				win_title[0] = 0;
1998 				WinQueryWindowText(swData.hwnd, len + 1, win_title);
1999 				win_title[len] = 0;
2000 			}
2001 			os2_exit_pm();
2002 		}
2003 	}
2004 
2005 	return win_title;
2006 #else
2007 	return NULL;
2008 #endif
2009 }
2010 
set_window_title(unsigned char * title)2011 void set_window_title(unsigned char *title)
2012 {
2013 #ifndef OS2_DEBUG
2014 	SWCNTRL swData;
2015 
2016 	if (!title) return;
2017 
2018 	memset(&swData, 0, sizeof swData);
2019 	if (os2_switchhandle != NULLHANDLE && !WinQuerySwitchEntry(os2_switchhandle, &swData)) {
2020 		safe_strncpy(swData.szSwtitle, title, MAXNAMEL);
2021 		WinChangeSwitchEntry(os2_switchhandle, &swData);
2022 		if (swData.hwnd != NULLHANDLE && !os2_init_pm()) {
2023 			WinSetWindowText(swData.hwnd, title);
2024 			os2_exit_pm();
2025 		}
2026 	}
2027 #endif
2028 }
2029 
2030 static tcount resize_count = 0;
2031 
resize_window(int x,int y)2032 int resize_window(int x, int y)
2033 {
2034 	int xfont, yfont;
2035 	A_DECL(VIOMODEINFO, vmi);
2036 	SWCNTRL swData;
2037 
2038 	resize_count++;
2039 	if (is_xterm()) return -1;
2040 	vmi->cb = sizeof(*vmi);
2041 	if (VioGetMode(vmi, 0)) return -1;
2042 	vmi->col = x;
2043 	vmi->row = y;
2044 	/*debug("%d %d %d", vmi->buf_length, vmi->full_length, vmi->partial_length);*/
2045 	for (xfont = 9; xfont >= 8; xfont--)
2046 		for (yfont = 16; yfont >= 8; yfont--) {
2047 			vmi->hres = x * xfont;
2048 			vmi->vres = y * yfont;
2049 			if (vmi->vres <= 400) vmi->vres = 400;
2050 			else if (vmi->vres <= 480) vmi->vres = 480;
2051 			vmi->buf_length = vmi->full_length = vmi->partial_length = x * ((vmi->vres + yfont - 1) / yfont) * 2;
2052 			vmi->full_length = (vmi->full_length + 4095) & ~4095;
2053 			vmi->partial_length = (vmi->partial_length + 4095) & ~4095;
2054 			if (!VioSetMode(vmi, 0)) goto resized;
2055 		}
2056 	return -1;
2057 
2058 	resized:
2059 	memset(&swData, 0, sizeof swData);
2060 	if (os2_switchhandle != NULLHANDLE && !WinQuerySwitchEntry(os2_switchhandle, &swData) && swData.hwnd != NULLHANDLE && !os2_init_pm()) {
2061 		SWP swp;
2062 		if (WinQueryWindowPos(swData.hwnd, &swp) && !(swp.fl & (SWP_MAXIMIZE | SWP_MINIMIZE | SWP_HIDE))) {
2063 			const int expand = 16383;
2064 			WinSetWindowPos(swData.hwnd, NULLHANDLE, swp.x, swp.y - expand, swp.cx + expand, swp.cy + expand, SWP_MOVE | SWP_SIZE);
2065 		}
2066 		os2_exit_pm();
2067 	}
2068 	return 0;
2069 }
2070 
2071 #endif
2072 
2073 /* Threads */
2074 
2075 #if (defined(HAVE_BEGINTHREAD) && defined(OS2)) || defined(BEOS) || defined(HAVE_PTHREADS) || defined(HAVE_ATHEOS_THREADS_H)
2076 
2077 struct tdata {
2078 	void (*fn)(void *, int);
2079 	int h;
2080 	int counted;
2081 	unsigned char data[1];
2082 };
2083 
bgt(void * t_)2084 static void bgt(void *t_)
2085 {
2086 	struct tdata *t = t_;
2087 	int rs;
2088 	ignore_signals();
2089 	t->fn(t->data, t->h);
2090 	EINTRLOOP(rs, (int)write(t->h, "x", 1));
2091 	EINTRLOOP(rs, close(t->h));
2092 	free(t);
2093 }
2094 
2095 #ifdef HAVE_ATHEOS_THREADS_H
2096 #include <atheos/threads.h>
bgat(void * t)2097 static uint32 bgat(void *t)
2098 {
2099 	bgt(t);
2100 	return 0;
2101 }
2102 #endif
2103 
2104 #endif
2105 
2106 #if defined(UNIX) || defined(OS2) || defined(WIN) || defined(INTERIX) || defined(RISCOS) || defined(ATHEOS) || defined(SPAD) || defined(HAIKU)
2107 
terminate_osdep(void)2108 void terminate_osdep(void)
2109 {
2110 }
2111 
2112 #endif
2113 
2114 #ifndef BEOS
2115 
block_stdin(void)2116 void block_stdin(void) {}
unblock_stdin(void)2117 void unblock_stdin(void) {}
2118 
2119 #endif
2120 
2121 #if defined(BEOS)
2122 
2123 #include <be/kernel/OS.h>
2124 
2125 static int thr_sem_init = 0;
2126 static sem_id thr_sem;
2127 
2128 static struct list_head active_threads = { &active_threads, &active_threads };
2129 
2130 struct active_thread {
2131 	list_entry_1st
2132 	thread_id tid;
2133 	void (*fn)(void *);
2134 	void *data;
2135 	list_entry_last
2136 };
2137 
started_thr(void * data)2138 static int32 started_thr(void *data)
2139 {
2140 	struct active_thread *thrd = data;
2141 	thrd->fn(thrd->data);
2142 	if (acquire_sem(thr_sem) < B_NO_ERROR) return 0;
2143 	del_from_list(thrd);
2144 	free(thrd);
2145 	release_sem(thr_sem);
2146 	return 0;
2147 }
2148 
start_thr(void (* fn)(void *),void * data,unsigned char * name)2149 int start_thr(void (*fn)(void *), void *data, unsigned char *name)
2150 {
2151 	struct active_thread *thrd;
2152 	int tid;
2153 	if (!thr_sem_init) {
2154 		if ((thr_sem = create_sem(0, "thread_sem")) < B_NO_ERROR) return -1;
2155 		thr_sem_init = 1;
2156 	} else if (acquire_sem(thr_sem) < B_NO_ERROR) return -1;
2157 	retry:
2158 	if (!(thrd = malloc(sizeof(struct active_thread)))) {
2159 		if (out_of_memory(0, NULL, 0))
2160 			goto retry;
2161 		goto err1;
2162 	}
2163 	thrd->fn = fn;
2164 	thrd->data = data;
2165 	if ((tid = thrd->tid = spawn_thread(started_thr, name, B_NORMAL_PRIORITY, thrd)) < B_NO_ERROR)
2166 		goto err2;
2167 	resume_thread(thrd->tid);
2168 	add_to_list(active_threads, thrd);
2169 	release_sem(thr_sem);
2170 	return tid;
2171 
2172 	err2:
2173 	free(thrd);
2174 	err1:
2175 	release_sem(thr_sem);
2176 	return -1;
2177 }
2178 
terminate_osdep(void)2179 void terminate_osdep(void)
2180 {
2181 	struct list_head *p;
2182 	struct active_thread *thrd;
2183 	struct list_head *lthrd;
2184 	if (acquire_sem(thr_sem) < B_NO_ERROR) return;
2185 	foreach(struct active_thread, thrd, lthrd, active_threads) kill_thread(thrd->tid);
2186 	while (!list_empty(active_threads)) {
2187 		thrd = list_struct(active_threads.next, struct active_thread);
2188 		del_from_list(thrd);
2189 		free(thrd);
2190 	}
2191 	release_sem(thr_sem);
2192 }
2193 
start_thread(void (* fn)(void *,int),void * ptr,int l,int counted)2194 int start_thread(void (*fn)(void *, int), void *ptr, int l, int counted)
2195 {
2196 	int p[2];
2197 	struct tdata *t;
2198 	int rs;
2199 	if (c_pipe(p) < 0) return -1;
2200 	retry:
2201 	if (!(t = malloc(sizeof(struct tdata) + l))) {
2202 		if (out_of_memory(0, NULL, 0))
2203 			goto retry;
2204 		goto err1;
2205 	}
2206 	t->fn = fn;
2207 	t->h = p[1];
2208 	t->counted = counted;
2209 	memcpy(t->data, ptr, l);
2210 	if (start_thr(bgt, t, cast_uchar "links_thread") < 0)
2211 		goto err2;
2212 	return p[0];
2213 
2214 	err2:
2215 	free(t);
2216 	err1:
2217 	EINTRLOOP(rs, close(p[0]));
2218 	EINTRLOOP(rs, close(p[1]));
2219 	return -1;
2220 }
2221 
2222 
2223 #elif defined(HAVE_BEGINTHREAD) && defined(OS2)
2224 
start_thread(void (* fn)(void *,int),void * ptr,int l,int counted)2225 int start_thread(void (*fn)(void *, int), void *ptr, int l, int counted)
2226 {
2227 	int p[2];
2228 	struct tdata *t;
2229 	int rs;
2230 	if (c_pipe(p) < 0) return -1;
2231 	retry:
2232 	if (!(t = malloc(sizeof(struct tdata) + l))) {
2233 		if (out_of_memory(0, NULL, 0))
2234 			goto retry;
2235 		goto err1;
2236 	}
2237 	t->fn = fn;
2238 	t->h = p[1];
2239 	t->counted = counted;
2240 	memcpy(t->data, ptr, l);
2241 	if (_beginthread(bgt, NULL, 65536, t) == -1)
2242 		goto err2;
2243 	return p[0];
2244 
2245 	err2:
2246 	free(t);
2247 	err1:
2248 	EINTRLOOP(rs, close(p[0]));
2249 	EINTRLOOP(rs, close(p[1]));
2250 	return -1;
2251 }
2252 
2253 #ifdef HAVE__READ_KBD
2254 
2255 static int tp = -1;
2256 static int ti = -1;
2257 
input_thread(void * p)2258 static void input_thread(void *p)
2259 {
2260 	unsigned char c[2];
2261 	int h = (int)p;
2262 	int rs;
2263 	ignore_signals();
2264 	while (1) {
2265 	   /* for the records:
2266 		 _read_kbd(0, 1, 1) will
2267 		 read a char, don't echo it, wait for one available and
2268 		 accept CTRL-C.
2269 		 Knowing that, I suggest we replace this call completly!
2270 	    */
2271 		*c = _read_kbd(0, 1, 1);
2272 		EINTRLOOP(rs, (int)write(h, c, 1));
2273 	}
2274 	EINTRLOOP(rs, close(h));
2275 }
2276 #endif /* #ifdef HAVE__READ_KBD */
2277 
2278 #if defined(HAVE_MOUOPEN) && defined(HAVE_BEGINTHREAD) && !defined(USE_GPM)
2279 
2280 #define USING_OS2_MOUSE
2281 
2282 static int mouse_h = -1;
2283 
2284 struct os2_mouse_spec {
2285 	int p[2];
2286 	void (*fn)(void *, unsigned char *, int);
2287 	void *data;
2288 	unsigned char buffer[sizeof(struct links_event)];
2289 	int bufptr;
2290 	int terminate;
2291 };
2292 
2293 #define MOU_EMULATE_CURSOR
2294 
2295 #ifdef MOU_EMULATE_CURSOR
2296 static int mouse_x = -1, mouse_y = -1;
2297 static unsigned char mouse_attr;
2298 #endif
2299 
mouse_remove_pointer(void)2300 static void mouse_remove_pointer(void)
2301 {
2302 #ifndef MOU_EMULATE_CURSOR
2303 	A_DECL(NOPTRRECT, pa);
2304 	static int x = -1, y = -1;
2305 	static tcount c = -1;
2306 	if (x == -1 || y == -1 || (c != resize_count)) get_terminal_size(&x, &y), c = resize_count;
2307 	pa->row = 0;
2308 	pa->col = 0;
2309 	pa->cRow = y - 1;
2310 	pa->cCol = x - 1;
2311 	MouRemovePtr(pa, mouse_h);
2312 #else
2313 	if (mouse_x >= 0 && mouse_y >= 0) {
2314 		VioWrtNAttr(&mouse_attr, 1, mouse_y, mouse_x, 0);
2315 	}
2316 	mouse_x = -1, mouse_y = -1;
2317 #endif
2318 }
2319 
mouse_draw_pointer(int x,int y)2320 static void mouse_draw_pointer(int x, int y)
2321 {
2322 #ifndef MOU_EMULATE_CURSOR
2323 	MouDrawPtr(mouse_h);
2324 #else
2325 	unsigned char str[4];
2326 	USHORT str_len;
2327 	unsigned char attr;
2328 	unsigned char fg, bg;
2329 	int r;
2330 	if (!os2_full_screen)
2331 		return;
2332 	DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
2333 	if (mouse_x == x && mouse_y == y)
2334 		return;
2335 	mouse_remove_pointer();
2336 	str_len = sizeof(str);
2337 	r = VioReadCellStr(str, &str_len, y, x, 0);
2338 	if (r || str_len < 2) return;
2339 	mouse_attr = str[1];
2340 	fg = mouse_attr & 0x07;
2341 	bg = (mouse_attr & 0x70) >> 4;
2342 	if (fg == bg) fg ^= 0x07, bg ^= 0x07;
2343 	attr = (mouse_attr & 0x88) | (fg << 4) | bg;
2344 	r = VioWrtNAttr(&attr, 1, y, x, 0);
2345 	if (r) return;
2346 	mouse_x = x, mouse_y = y, mouse_attr = str[1];
2347 #endif
2348 }
2349 
mouse_thread(void * p)2350 static void mouse_thread(void *p)
2351 {
2352 	int status;
2353 	int rs;
2354 	struct os2_mouse_spec *oms = p;
2355 	A_DECL(HMOU, mh);
2356 	A_DECL(MOUEVENTINFO, ms);
2357 	A_DECL(USHORT, rd);
2358 	A_DECL(USHORT, mask);
2359 	struct links_event ev;
2360 	ignore_signals();
2361 	ev.ev = EV_MOUSE;
2362 	if (MouOpen(NULL, mh)) goto ret;
2363 	mouse_h = *mh;
2364 	*mask = MOUSE_MOTION_WITH_BN1_DOWN | MOUSE_BN1_DOWN |
2365 		MOUSE_MOTION_WITH_BN2_DOWN | MOUSE_BN2_DOWN |
2366 		MOUSE_MOTION_WITH_BN3_DOWN | MOUSE_BN3_DOWN |
2367 		MOUSE_MOTION;
2368 	MouSetEventMask(mask, *mh);
2369 	*rd = MOU_WAIT;
2370 	status = -1;
2371 	while (1) {
2372 		/*int w, ww;*/
2373 		if (MouReadEventQue(ms, rd, *mh)) break;
2374 		fd_lock();
2375 		if (!oms->terminate) mouse_draw_pointer(ms->col, ms->row);
2376 		fd_unlock();
2377 		ev.x = ms->col;
2378 		ev.y = ms->row;
2379 		/*debug("status: %d %d %d", ms->col, ms->row, ms->fs);*/
2380 		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);
2381 		else if (ms->fs & (MOUSE_MOTION_WITH_BN1_DOWN | MOUSE_MOTION_WITH_BN2_DOWN | MOUSE_MOTION_WITH_BN3_DOWN)) {
2382 			int b = ms->fs & MOUSE_MOTION_WITH_BN1_DOWN ? B_LEFT : ms->fs & MOUSE_MOTION_WITH_BN2_DOWN ? B_RIGHT : B_MIDDLE;
2383 			if (status == -1) b |= B_DOWN;
2384 			else b |= B_DRAG;
2385 			ev.b = status = b;
2386 		}
2387 		else {
2388 			if (status == -1) continue;
2389 			ev.b = (status & BM_BUTT) | B_UP;
2390 			status = -1;
2391 		}
2392 		if (hard_write(oms->p[1], (unsigned char *)&ev, sizeof(struct links_event)) != sizeof(struct links_event)) break;
2393 	}
2394 	fd_lock();
2395 	mouse_h = -1;
2396 	MouClose(*mh);
2397 	fd_unlock();
2398 	ret:
2399 	EINTRLOOP(rs, close(oms->p[1]));
2400 	/*free(oms);*/
2401 }
2402 
mouse_handle(void * oms_)2403 static void mouse_handle(void *oms_)
2404 {
2405 	struct os2_mouse_spec *oms = (struct os2_mouse_spec *)oms_;
2406 	int r;
2407 	EINTRLOOP(r, (int)read(oms->p[0], oms->buffer + oms->bufptr, sizeof(struct links_event) - oms->bufptr));
2408 	if (r <= 0) {
2409 		unhandle_mouse(oms);
2410 		return;
2411 	}
2412 	if ((oms->bufptr += r) == sizeof(struct links_event)) {
2413 		oms->bufptr = 0;
2414 		oms->fn(oms->data, oms->buffer, sizeof(struct links_event));
2415 	}
2416 }
2417 
handle_mouse(int cons,void (* fn)(void *,unsigned char *,int),void * data)2418 void *handle_mouse(int cons, void (*fn)(void *, unsigned char *, int), void *data)
2419 {
2420 	struct os2_mouse_spec *oms;
2421 	if (is_xterm()) return NULL;
2422 		/* This is never freed but it's allocated only once */
2423 	retry:
2424 	if (!(oms = malloc(sizeof(struct os2_mouse_spec)))) {
2425 		if (out_of_memory(0, NULL, 0))
2426 			goto retry;
2427 		return NULL;
2428 	}
2429 	oms->fn = fn;
2430 	oms->data = data;
2431 	oms->bufptr = 0;
2432 	oms->terminate = 0;
2433 	if (c_pipe(oms->p)) {
2434 		free(oms);
2435 		return NULL;
2436 	}
2437 	if (_beginthread(mouse_thread, NULL, 0x10000, (void *)oms) == -1) {
2438 	}
2439 	set_handlers(oms->p[0], mouse_handle, NULL, oms);
2440 	return oms;
2441 }
2442 
unhandle_mouse(void * om)2443 void unhandle_mouse(void *om)
2444 {
2445 	struct os2_mouse_spec *oms = om;
2446 	want_draw();
2447 	oms->terminate = 1;
2448 	close_socket(&oms->p[0]);
2449 	done_draw();
2450 }
2451 
want_draw(void)2452 void want_draw(void)
2453 {
2454 	static int ansi = 0;
2455 	fd_lock();
2456 	if (!ansi) {
2457 		VioSetAnsi(1, 0);
2458 		ansi = 1;
2459 	}
2460 	if (mouse_h != -1) {
2461 		mouse_remove_pointer();
2462 	}
2463 }
2464 
done_draw(void)2465 void done_draw(void)
2466 {
2467 	fd_unlock();
2468 }
2469 
2470 #endif /* if HAVE_MOUOPEN */
2471 
2472 #elif defined(HAVE_PTHREADS)
2473 
2474 #ifdef OPENVMS
2475 #define THREAD_NEED_STACK_SIZE		65536
2476 int vms_thread_high_priority = 0;
2477 #endif
2478 
2479 #ifndef OPENVMS
2480 static unsigned thread_count = 0;
2481 #endif
2482 
reset_thread_count(void)2483 static inline void reset_thread_count(void)
2484 {
2485 #ifndef OPENVMS
2486 	fd_lock();
2487 	thread_count = 0;
2488 	fd_unlock();
2489 #endif
2490 }
2491 
inc_thread_count(void)2492 static void inc_thread_count(void)
2493 {
2494 #ifndef OPENVMS
2495 	fd_lock();
2496 	thread_count++;
2497 	fd_unlock();
2498 #endif
2499 }
2500 
dec_thread_count(void)2501 static void dec_thread_count(void)
2502 {
2503 #ifndef OPENVMS
2504 	fd_lock();
2505 	if (!thread_count)
2506 		internal_error("thread_count underflow");
2507 	thread_count--;
2508 	fd_unlock();
2509 #endif
2510 }
2511 
get_thread_count(void)2512 static inline unsigned get_thread_count(void)
2513 {
2514 #ifndef OPENVMS
2515 	unsigned val;
2516 	fd_lock();
2517 	val = thread_count;
2518 	fd_unlock();
2519 	return val;
2520 #else
2521 	return 0;
2522 #endif
2523 }
2524 
bgpt(void * t)2525 static void *bgpt(void *t)
2526 {
2527 	int counted = ((struct tdata *)t)->counted;
2528 	bgt(t);
2529 	if (counted) dec_thread_count();
2530 	return NULL;
2531 }
2532 
start_thread(void (* fn)(void *,int),void * ptr,int l,int counted)2533 int start_thread(void (*fn)(void *, int), void *ptr, int l, int counted)
2534 {
2535 	pthread_attr_t attr;
2536 	pthread_t thread;
2537 	struct tdata *t;
2538 	int p[2];
2539 	int rs;
2540 	if (c_pipe(p) < 0) return -1;
2541 	retry1:
2542 	if (!(t = malloc(sizeof(struct tdata) + l))) {
2543 		if (out_of_memory(0, NULL, 0))
2544 			goto retry1;
2545 		goto err1;
2546 	}
2547 	t->fn = fn;
2548 	t->h = p[1];
2549 	t->counted = counted;
2550 	memcpy(t->data, ptr, l);
2551 	retry2:
2552 	if (pthread_attr_init(&attr)) {
2553 		if (out_of_memory(0, NULL, 0))
2554 			goto retry2;
2555 		goto err2;
2556 	}
2557 	retry3:
2558 	if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
2559 		if (out_of_memory(0, NULL, 0))
2560 			goto retry3;
2561 		goto err3;
2562 	}
2563 #ifdef THREAD_NEED_STACK_SIZE
2564 	retry4:
2565 	if (pthread_attr_setstacksize(&attr, THREAD_NEED_STACK_SIZE)) {
2566 		if (out_of_memory(0, NULL, 0))
2567 			goto retry4;
2568 		goto err3;
2569 	}
2570 #endif
2571 #ifdef OPENVMS
2572 	if (vms_thread_high_priority) {
2573 		struct sched_param param;
2574 		memset(&param, 0, sizeof param);
2575 		if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED))
2576 			goto err3;
2577 		if (vms_thread_high_priority > 0) {
2578 			if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO))
2579 				goto err3;
2580 			param.sched_priority = PRI_FIFO_MIN;
2581 			if (pthread_attr_setschedparam(&attr, &param))
2582 				goto err3;
2583 		} else {
2584 			if (pthread_attr_setschedpolicy(&attr, SCHED_LFI_NP))
2585 				goto err3;
2586 			param.sched_priority = PRI_BG_MIN_NP;
2587 			if (pthread_attr_setschedparam(&attr, &param))
2588 				goto err3;
2589 		}
2590 	}
2591 #endif
2592 	if (counted) inc_thread_count();
2593 	if (pthread_create(&thread, &attr, bgpt, t)) {
2594 		if (counted) dec_thread_count();
2595 		goto err3;
2596 	}
2597 	pthread_attr_destroy(&attr);
2598 	return p[0];
2599 
2600 	err3:
2601 	pthread_attr_destroy(&attr);
2602 	err2:
2603 	free(t);
2604 	err1:
2605 	EINTRLOOP(rs, close(p[0]));
2606 	EINTRLOOP(rs, close(p[1]));
2607 	return -1;
2608 }
2609 
2610 #elif defined(HAVE_ATHEOS_THREADS_H) && defined(HAVE_SPAWN_THREAD) && defined(HAVE_RESUME_THREAD)
2611 
2612 #include <atheos/threads.h>
2613 
start_thread(void (* fn)(void *,int),void * ptr,int l,int counted)2614 int start_thread(void (*fn)(void *, int), void *ptr, int l, int counted)
2615 {
2616 	int p[2];
2617 	int rs;
2618 	thread_id f;
2619 	struct tdata *t;
2620 	if (c_pipe(p) < 0) return -1;
2621 	retry:
2622 	if (!(t = malloc(sizeof(struct tdata) + l))) {
2623 		if (out_of_memory(0, NULL, 0))
2624 			goto retry;
2625 		goto err1;
2626 	}
2627 	t->fn = fn;
2628 	t->h = p[1];
2629 	t->counted = counted;
2630 	memcpy(t->data, ptr, l);
2631 	if ((f = spawn_thread("links_lookup", bgat, 0, 0, t)) == -1)
2632 		goto err2;
2633 	resume_thread(f);
2634 	return p[0];
2635 
2636 	err2:
2637 	free(t);
2638 	err1:
2639 	EINTRLOOP(rs, close(p[0]));
2640 	EINTRLOOP(rs, close(p[1]));
2641 	return -1;
2642 }
2643 
2644 #elif defined(DOS)
2645 
start_thread(void (* fn)(void *,int),void * ptr,int l,int counted)2646 int start_thread(void (*fn)(void *, int), void *ptr, int l, int counted)
2647 {
2648 	int p[2];
2649 	int rs;
2650 	if (c_pipe(p) < 0) return -1;
2651 	fn(ptr, p[1]);
2652 	EINTRLOOP(rs, close(p[1]));
2653 	return p[0];
2654 }
2655 
2656 #else /* HAVE_BEGINTHREAD */
2657 
start_thread(void (* fn)(void *,int),void * ptr,int l,int counted)2658 int start_thread(void (*fn)(void *, int), void *ptr, int l, int counted)
2659 {
2660 	int p[2];
2661 	pid_t f;
2662 	int rs;
2663 	if (c_pipe(p) < 0) return -1;
2664 	EINTRLOOP(f, fork());
2665 	if (!f) {
2666 		close_fork_tty();
2667 		EINTRLOOP(rs, close(p[0]));
2668 		fn(ptr, p[1]);
2669 		EINTRLOOP(rs, (int)write(p[1], "x", 1));
2670 		EINTRLOOP(rs, close(p[1]));
2671 		_exit(0);
2672 	}
2673 	if (f == -1) {
2674 		EINTRLOOP(rs, close(p[0]));
2675 		EINTRLOOP(rs, close(p[1]));
2676 		return -1;
2677 	}
2678 	EINTRLOOP(rs, close(p[1]));
2679 	return p[0];
2680 }
2681 
2682 #endif
2683 
2684 #if !defined(USING_OS2_MOUSE) && !defined(DOS)
want_draw(void)2685 void want_draw(void) {}
done_draw(void)2686 void done_draw(void) {}
2687 #endif
2688 
get_output_handle(void)2689 int get_output_handle(void) { return 1; }
2690 
2691 #if defined(OS2)
2692 
get_ctl_handle(void)2693 int get_ctl_handle(void) { return get_input_handle(); }
2694 
2695 #else
2696 
get_ctl_handle(void)2697 int get_ctl_handle(void) { return 0; }
2698 
2699 #endif
2700 
2701 #if defined(BEOS)
2702 
2703 #elif defined(HAVE_BEGINTHREAD) && defined(HAVE__READ_KBD)
2704 
get_input_handle(void)2705 int get_input_handle(void)
2706 {
2707 	int rs;
2708 	int fd[2];
2709 	if (ti != -1) return ti;
2710 	if (is_xterm()) return 0;
2711 	if (c_pipe(fd) < 0) return 0;
2712 	ti = fd[0];
2713 	tp = fd[1];
2714 	if (_beginthread(input_thread, NULL, 0x10000, (void *)tp) == -1) {
2715 		EINTRLOOP(rs, close(fd[0]));
2716 		EINTRLOOP(rs, close(fd[1]));
2717 		return 0;
2718 	}
2719 /*
2720 #if defined(HAVE_MOUOPEN) && !defined(USE_GPM)
2721 	_beginthread(mouse_thread, NULL, 0x10000, (void *)tp);
2722 #endif
2723 */
2724 	return fd[0];
2725 }
2726 
2727 #elif !defined(OPENVMS)
2728 
get_input_handle(void)2729 int get_input_handle(void)
2730 {
2731 	return 0;
2732 }
2733 
2734 #endif /* defined(HAVE_BEGINTHREAD) && defined(HAVE__READ_KBD) */
2735 
2736 
2737 #if defined(__linux__) || defined(__LINUX__)
is_on_linux_console(void)2738 static unsigned char *is_on_linux_console(void)
2739 {
2740 	static unsigned char tty_name[MAX_STR_LEN];
2741 	int r = readlink("/proc/self/fd/0", cast_char tty_name, sizeof tty_name);
2742 	if (r >= 1 && r < (int)sizeof(tty_name)) {
2743 #define pfx1 "/dev/tty"
2744 #define pfx2 "/dev/vc/"
2745 		unsigned char c;
2746 		tty_name[r] = 0;
2747 		if (!memcmp(tty_name, pfx1, strlen(pfx1))) {
2748 			c = tty_name[strlen(pfx1)];
2749 			if (!(c >= '1' && c <= '9'))
2750 				return NULL;
2751 			return tty_name;
2752 		}
2753 		if (!memcmp(tty_name, pfx2, strlen(pfx2))) {
2754 			c = tty_name[strlen(pfx2)];
2755 			if (!(c >= '1' && c <= '9'))
2756 				return NULL;
2757 			return tty_name;
2758 		}
2759 #undef pfx1
2760 #undef pfx2
2761 	}
2762 	return NULL;
2763 }
2764 #endif
2765 
2766 
2767 #if defined(USE_GPM) || defined(GRDRV_FB)
2768 
2769 /* GPM installs its own signal handlers and we don't want them */
2770 
2771 static sigset_t gpm_sigset;
2772 static unsigned char gpm_sigset_valid;
2773 #ifdef SIGWINCH
2774 static struct sigaction gpm_winch;
2775 static unsigned char gpm_winch_valid;
2776 #endif
2777 #ifdef SIGTSTP
2778 static struct sigaction gpm_tstp;
2779 static unsigned char gpm_tstp_valid;
2780 #endif
2781 
save_gpm_signals(void)2782 void save_gpm_signals(void)
2783 {
2784 	sigset_t sig;
2785 	int rs;
2786 	sigemptyset(&sig);
2787 #ifdef SIGWINCH
2788 	sigaddset(&sig, SIGWINCH);
2789 #endif
2790 #ifdef SIGTSTP
2791 	sigaddset(&sig, SIGTSTP);
2792 #endif
2793 	EINTRLOOP(rs, do_sigprocmask(SIG_BLOCK, &sig, &gpm_sigset));
2794 	gpm_sigset_valid = !rs;
2795 #ifdef SIGWINCH
2796 	EINTRLOOP(rs, sigaction(SIGWINCH, NULL, &gpm_winch));
2797 	gpm_winch_valid = !rs;
2798 #endif
2799 #ifdef SIGTSTP
2800 	EINTRLOOP(rs, sigaction(SIGTSTP, NULL, &gpm_tstp));
2801 	gpm_tstp_valid = !rs;
2802 #endif
2803 }
2804 
restore_gpm_signals(void)2805 void restore_gpm_signals(void)
2806 {
2807 	int rs;
2808 #ifdef SIGWINCH
2809 	if (gpm_winch_valid)
2810 		EINTRLOOP(rs, sigaction(SIGWINCH, &gpm_winch, NULL));
2811 #endif
2812 #ifdef SIGTSTP
2813 	if (gpm_tstp_valid)
2814 		EINTRLOOP(rs, sigaction(SIGTSTP, &gpm_tstp, NULL));
2815 #endif
2816 	if (gpm_sigset_valid)
2817 		EINTRLOOP(rs, do_sigprocmask(SIG_SETMASK, &gpm_sigset, NULL));
2818 }
2819 
2820 #endif
2821 
2822 #ifdef USE_GPM
2823 
2824 struct gpm_mouse_spec {
2825 	int h;
2826 	void (*fn)(void *, unsigned char *, int);
2827 	void *data;
2828 };
2829 
gpm_mouse_in(void * gms_)2830 static void gpm_mouse_in(void *gms_)
2831 {
2832 	struct gpm_mouse_spec *gms = (struct gpm_mouse_spec *)gms_;
2833 	int g;
2834 	Gpm_Event gev;
2835 	struct links_event ev;
2836 	set_handlers(gms->h, NULL, NULL, NULL);
2837 	save_gpm_signals();
2838 	g = Gpm_GetEvent(&gev);
2839 	restore_gpm_signals();
2840 	if (g <= 0) {
2841 		gms->h = -1;
2842 		return;
2843 	}
2844 	set_handlers(gms->h, gpm_mouse_in, NULL, gms);
2845 	ev.ev = EV_MOUSE;
2846 	ev.x = gev.x - 1;
2847 	ev.y = gev.y - 1;
2848 	if (ev.x < 0) ev.x = 0;
2849 	if (ev.y < 0) ev.y = 0;
2850 	if (gev.buttons & GPM_B_LEFT) ev.b = B_LEFT;
2851 	else if (gev.buttons & GPM_B_MIDDLE) ev.b = B_MIDDLE;
2852 	else if (gev.buttons & GPM_B_RIGHT) ev.b = B_RIGHT;
2853 #ifdef GPM_B_FOURTH
2854 	else if (gev.buttons & GPM_B_FOURTH) ev.b = B_FOURTH;
2855 #endif
2856 #ifdef GPM_B_UP
2857 	else if (gev.buttons & GPM_B_UP) ev.b = B_FIFTH;
2858 #endif
2859 #ifdef GPM_B_DOWN
2860 	else if (gev.buttons & GPM_B_DOWN) ev.b = B_SIXTH;
2861 #endif
2862 	else return;
2863 	if ((int)gev.type & GPM_DOWN) ev.b |= B_DOWN;
2864 	else if ((int)gev.type & GPM_UP) ev.b |= B_UP;
2865 	else if ((int)gev.type & GPM_DRAG) ev.b |= B_DRAG;
2866 	else return;
2867 	gms->fn(gms->data, (unsigned char *)&ev, sizeof(struct links_event));
2868 }
2869 
handle_mouse(int cons,void (* fn)(void *,unsigned char *,int),void * data)2870 void *handle_mouse(int cons, void (*fn)(void *, unsigned char *, int), void *data)
2871 {
2872 	int h;
2873 	Gpm_Connect conn;
2874 	struct gpm_mouse_spec *gms;
2875 
2876 #if defined(__linux__) || defined(__LINUX__)
2877 	if (!is_on_linux_console())
2878 		return NULL;
2879 #endif
2880 
2881 	conn.eventMask = (unsigned short)~(unsigned)GPM_MOVE;
2882 	conn.defaultMask = GPM_MOVE;
2883 	conn.minMod = 0;
2884 	conn.maxMod = 0;
2885 	save_gpm_signals();
2886 	h = Gpm_Open(&conn, cons);
2887 	restore_gpm_signals();
2888 	if (h < 0) return NULL;
2889 	gms = mem_alloc(sizeof(struct gpm_mouse_spec));
2890 	gms->h = h;
2891 	gms->fn = fn;
2892 	gms->data = data;
2893 	set_handlers(h, gpm_mouse_in, NULL, gms);
2894 	return gms;
2895 }
2896 
unhandle_mouse(void * h)2897 void unhandle_mouse(void *h)
2898 {
2899 	struct gpm_mouse_spec *gms = h;
2900 	if (gms->h != -1) set_handlers(gms->h, NULL, NULL, NULL);
2901 	save_gpm_signals();
2902 	Gpm_Close();
2903 	restore_gpm_signals();
2904 	mem_free(gms);
2905 }
2906 
add_gpm_version(unsigned char ** s,int * l)2907 void add_gpm_version(unsigned char **s, int *l)
2908 {
2909 	add_to_str(s, l, cast_uchar "GPM");
2910 #ifdef HAVE_GPM_GETLIBVERSION
2911 	add_to_str(s, l, cast_uchar " (");
2912 	add_to_str(s, l, cast_uchar Gpm_GetLibVersion(NULL));
2913 	add_chr_to_str(s, l, ')');
2914 #endif
2915 }
2916 
2917 #elif !defined(USING_OS2_MOUSE) && !defined(DOS)
2918 
handle_mouse(int cons,void (* fn)(void *,unsigned char *,int),void * data)2919 void *handle_mouse(int cons, void (*fn)(void *, unsigned char *, int), void *data) { return NULL; }
unhandle_mouse(void * data)2920 void unhandle_mouse(void *data) { }
2921 
2922 #endif /* #ifdef USE_GPM */
2923 
2924 
2925 #if defined(WIN) || defined(INTERIX)
2926 
is_remote_connection(void)2927 static int is_remote_connection(void)
2928 {
2929 	return !!getenv("SSH_CONNECTION");
2930 }
2931 
2932 #endif
2933 
2934 
2935 #if defined(OS2)
2936 
get_system_env(void)2937 int get_system_env(void)
2938 {
2939 	if (is_xterm()) return 0;
2940 	return ENV_OS2VIO;		/* !!! FIXME: telnet */
2941 }
2942 
2943 #elif defined(BEOS)
2944 
get_system_env(void)2945 int get_system_env(void)
2946 {
2947 	unsigned char *term = cast_uchar getenv("TERM");
2948 	if (!term || (upcase(term[0]) == 'B' && upcase(term[1]) == 'E')) return ENV_BE;
2949 	return 0;
2950 }
2951 
2952 #elif defined(HAIKU)
2953 
get_system_env(void)2954 int get_system_env(void)
2955 {
2956 	if (!getenv("SSH_CONNECTION")) return ENV_BE;
2957 	return 0;
2958 }
2959 
2960 #elif defined(WIN)
2961 
get_system_env(void)2962 int get_system_env(void)
2963 {
2964 	if (is_xterm()) return 0;
2965 	if (is_remote_connection()) return 0;
2966 	return ENV_WIN32;
2967 }
2968 
2969 #elif defined(INTERIX)
2970 
2971 #define INTERIX_START_COMMAND	"/usr/contrib/win32/bin/start"
2972 
get_system_env(void)2973 int get_system_env(void)
2974 {
2975 	if (is_xterm()) return 0;
2976 	if (is_remote_connection()) return 0;
2977 	if (!access(INTERIX_START_COMMAND, X_OK)) return ENV_INTERIX;
2978 	return 0;
2979 }
2980 
2981 #else
2982 
get_system_env(void)2983 int get_system_env(void)
2984 {
2985 	return 0;
2986 }
2987 
2988 #endif
2989 
exec_new_links(struct terminal * term,unsigned char * xterm,unsigned char * exe,unsigned char * param)2990 static void exec_new_links(struct terminal *term, unsigned char *xterm, unsigned char *exe, unsigned char *param)
2991 {
2992 	unsigned char *str;
2993 	str = stracpy(cast_uchar "");
2994 	if (*xterm) {
2995 		add_to_strn(&str, xterm);
2996 		add_to_strn(&str, cast_uchar " ");
2997 	}
2998 	add_to_strn(&str, exe);
2999 	add_to_strn(&str, cast_uchar " ");
3000 	add_to_strn(&str, param);
3001 	exec_on_terminal(term, str, cast_uchar "", 2);
3002 	mem_free(str);
3003 }
3004 
open_in_new_twterm(struct terminal * term,unsigned char * exe,unsigned char * param)3005 static int open_in_new_twterm(struct terminal *term, unsigned char *exe, unsigned char *param)
3006 {
3007 	unsigned char *twterm;
3008 	if (!(twterm = cast_uchar getenv("LINKS_TWTERM"))) twterm = cast_uchar "twterm -e";
3009 	exec_new_links(term, twterm, exe, param);
3010 	return 0;
3011 }
3012 
links_xterm(void)3013 unsigned char *links_xterm(void)
3014 {
3015 	unsigned char *xterm;
3016 	if (!(xterm = cast_uchar getenv("LINKS_XTERM"))) {
3017 #ifdef OPENVMS
3018 		xterm = cast_uchar "CREATE /TERMINAL /WAIT";
3019 #elif defined(HAIKU)
3020 		xterm = cast_uchar "Terminal";
3021 #else
3022 		xterm = cast_uchar "xterm -e";
3023 #endif
3024 	}
3025 	return xterm;
3026 }
3027 
open_in_new_xterm(struct terminal * term,unsigned char * exe,unsigned char * param)3028 static int open_in_new_xterm(struct terminal *term, unsigned char *exe, unsigned char *param)
3029 {
3030 	exec_new_links(term, links_xterm(), exe, param);
3031 	return 0;
3032 }
3033 
open_in_new_screen(struct terminal * term,unsigned char * exe,unsigned char * param)3034 static int open_in_new_screen(struct terminal *term, unsigned char *exe, unsigned char *param)
3035 {
3036 	exec_new_links(term, cast_uchar "screen", exe, param);
3037 	return 0;
3038 }
3039 
3040 #ifdef OS2
open_in_new_vio(struct terminal * term,unsigned char * exe,unsigned char * param)3041 static int open_in_new_vio(struct terminal *term, unsigned char *exe, unsigned char *param)
3042 {
3043 	unsigned char *x = stracpy(cast_uchar "\"");
3044 	add_to_strn(&x, exe);
3045 	add_to_strn(&x, cast_uchar "\"");
3046 	exec_new_links(term, cast_uchar "start \"Links\" /c /f /win", x, param);
3047 	mem_free(x);
3048 	return 0;
3049 }
3050 
open_in_new_fullscreen(struct terminal * term,unsigned char * exe,unsigned char * param)3051 static int open_in_new_fullscreen(struct terminal *term, unsigned char *exe, unsigned char *param)
3052 {
3053 	unsigned char *x = stracpy(cast_uchar "\"");
3054 	add_to_strn(&x, exe);
3055 	add_to_strn(&x, cast_uchar "\"");
3056 	exec_new_links(term, cast_uchar "start \"Links\" /c /f /fs", x, param);
3057 	mem_free(x);
3058 	return 0;
3059 }
3060 #endif
3061 
3062 #ifdef WIN
open_in_new_win32(struct terminal * term,unsigned char * exe,unsigned char * param)3063 static int open_in_new_win32(struct terminal *term, unsigned char *exe, unsigned char *param)
3064 {
3065 	exec_new_links(term, cast_uchar "", exe, param);
3066 	return 0;
3067 }
3068 #endif
3069 
3070 #ifdef INTERIX
open_in_new_interix(struct terminal * term,unsigned char * exe,unsigned char * param)3071 static int open_in_new_interix(struct terminal *term, unsigned char *exe, unsigned char *param)
3072 {
3073 	unsigned char *param_x = stracpy(param);
3074 	add_to_strn(&param_x, cast_uchar "'");
3075 	exec_new_links(term, cast_uchar(INTERIX_START_COMMAND " '\"Links\"' posix /u /c /bin/sh -c '"), exe, param_x);
3076 	mem_free(param_x);
3077 	return 0;
3078 }
3079 #endif
3080 
3081 #if defined(BEOS) || defined(HAIKU)
open_in_new_be(struct terminal * term,unsigned char * exe,unsigned char * param)3082 static int open_in_new_be(struct terminal *term, unsigned char *exe, unsigned char *param)
3083 {
3084 	exec_new_links(term, cast_uchar "Terminal", exe, param);
3085 	return 0;
3086 }
3087 #endif
3088 
3089 #ifdef G
open_in_new_g(struct terminal * term,unsigned char * exe,unsigned char * param)3090 static int open_in_new_g(struct terminal *term, unsigned char *exe, unsigned char *param)
3091 {
3092 	void *info;
3093 	unsigned char *target = NULL;
3094 	int len;
3095 	int base = 0;
3096 	unsigned char *url;
3097 	if (!cmpbeg(param, cast_uchar "-base-session ")) {
3098 		param = cast_uchar strchr(cast_const_char param, ' ') + 1;
3099 		base = atoi(cast_const_char param);
3100 		param += strcspn(cast_const_char param, " ");
3101 		if (*param == ' ') param++;
3102 	}
3103 	if (!cmpbeg(param, cast_uchar "-target ")) {
3104 		param = cast_uchar strchr(cast_const_char param, ' ') + 1;
3105 		target = param;
3106 		param += strcspn(cast_const_char param, " ");
3107 		if (*param == ' ') *param++ = 0;
3108 	}
3109 	url = param;
3110 	info = create_session_info(base, url, target, &len);
3111 	return attach_g_terminal(term->cwd, info, len);
3112 }
3113 #endif
3114 
3115 static const struct {
3116 	int env;
3117 	int (*open_window_fn)(struct terminal *term, unsigned char *, unsigned char *);
3118 	unsigned char *text;
3119 	unsigned char *hk;
3120 } oinw[] = {
3121 	{ENV_XWIN, open_in_new_xterm, TEXT_(T_XTERM), TEXT_(T_HK_XTERM)},
3122 	{ENV_TWIN, open_in_new_twterm, TEXT_(T_TWTERM), TEXT_(T_HK_TWTERM)},
3123 	{ENV_SCREEN, open_in_new_screen, TEXT_(T_SCREEN), TEXT_(T_HK_SCREEN)},
3124 #ifdef OS2
3125 	{ENV_OS2VIO, open_in_new_vio, TEXT_(T_WINDOW), TEXT_(T_HK_WINDOW)},
3126 	{ENV_OS2VIO, open_in_new_fullscreen, TEXT_(T_FULL_SCREEN), TEXT_(T_HK_FULL_SCREEN)},
3127 #endif
3128 #ifdef WIN
3129 	{ENV_WIN32, open_in_new_win32, TEXT_(T_WINDOW), TEXT_(T_HK_WINDOW)},
3130 #endif
3131 #ifdef INTERIX
3132 	{ENV_INTERIX, open_in_new_interix, TEXT_(T_WINDOW), TEXT_(T_HK_WINDOW)},
3133 #endif
3134 #if defined(BEOS) || defined(HAIKU)
3135 	{ENV_BE, open_in_new_be, TEXT_(T_BEOS_TERMINAL), TEXT_(T_HK_BEOS_TERMINAL)},
3136 #endif
3137 #ifdef G
3138 	{ENV_G, open_in_new_g, TEXT_(T_WINDOW), TEXT_(T_HK_WINDOW)},
3139 #endif
3140 };
3141 
get_open_in_new(int environment)3142 struct open_in_new *get_open_in_new(int environment)
3143 {
3144 	int i;
3145 	struct open_in_new *oin = DUMMY;
3146 	int noin = 0;
3147 	if (anonymous) return NULL;
3148 	if (environment & ENV_G) environment = ENV_G;
3149 	for (i = 0; i < (int)array_elements(oinw); i++) if ((environment & oinw[i].env) == oinw[i].env) {
3150 		if ((unsigned)noin > MAXINT / sizeof(struct open_in_new) - 2) overalloc();
3151 		oin = mem_realloc(oin, (noin + 2) * sizeof(struct open_in_new));
3152 		oin[noin].text = oinw[i].text;
3153 		oin[noin].hk = oinw[i].hk;
3154 		oin[noin].open_window_fn = &oinw[i].open_window_fn;
3155 		noin++;
3156 		oin[noin].text = NULL;
3157 		oin[noin].hk = NULL;
3158 		oin[noin].open_window_fn = NULL;
3159 	}
3160 	if (oin == DUMMY) return NULL;
3161 	return oin;
3162 }
3163 
can_resize_window(struct terminal * term)3164 int can_resize_window(struct terminal *term)
3165 {
3166 #if defined(OS2) || defined(WIN)
3167 	if (!strncmp(cast_const_char term->term, "xterm", 5)) return 0;
3168 	if (term->environment & (ENV_OS2VIO | ENV_WIN32)) return 1;
3169 #endif
3170 	return 0;
3171 }
3172 
can_open_os_shell(int environment)3173 int can_open_os_shell(int environment)
3174 {
3175 #ifdef OS2
3176 	if (environment & ENV_XWIN) return 0;
3177 #endif
3178 #ifdef WIN
3179 	if (!F && !(environment & ENV_WIN32)) return 0;
3180 #endif
3181 #ifdef BEOS
3182 	if (!(environment & ENV_BE)) return 0;
3183 #endif
3184 #ifdef G
3185 	if (F && drv->flags & GD_NO_OS_SHELL) return 0;
3186 #endif
3187 	return 1;
3188 }
3189 
set_highpri(void)3190 void set_highpri(void)
3191 {
3192 #ifdef OS2
3193 	DosSetPriority(PRTYS_PROCESS, PRTYC_FOREGROUNDSERVER, 0, 0);
3194 #endif
3195 }
3196 
3197 #if !defined(DOS) && !defined(OPENVMS)
os_seed_random(unsigned char ** pool,int * pool_size)3198 void os_seed_random(unsigned char **pool, int *pool_size)
3199 {
3200 	*pool = DUMMY;
3201 	*pool_size = 0;
3202 }
3203 #endif
3204 
3205 #if defined(WIN)
os_send_fg_cookie(int h)3206 int os_send_fg_cookie(int h)
3207 {
3208 	DWORD pid;
3209 	pid = GetCurrentProcessId();
3210 	if (hard_write(h, (unsigned char *)&pid, sizeof pid) != sizeof pid)
3211 		return -1;
3212 	return 0;
3213 }
os_receive_fg_cookie(int h)3214 int os_receive_fg_cookie(int h)
3215 {
3216 	DWORD pid;
3217 	BOOL (WINAPI *fn_AllowSetForegroundWindow)(DWORD);
3218 	if (hard_read(h, (unsigned char *)&pid, sizeof pid) != sizeof pid)
3219 		return -1;
3220 	fn_AllowSetForegroundWindow = (BOOL (WINAPI *)(DWORD))(void *)GetProcAddress(GetModuleHandleA("user32.dll"), "AllowSetForegroundWindow");
3221 	if (fn_AllowSetForegroundWindow)
3222 		fn_AllowSetForegroundWindow(pid);
3223 	return 0;
3224 }
3225 #else
os_send_fg_cookie(int h)3226 int os_send_fg_cookie(int h)
3227 {
3228 	return 0;
3229 }
os_receive_fg_cookie(int h)3230 int os_receive_fg_cookie(int h)
3231 {
3232 	return 0;
3233 }
3234 #endif
3235 
3236 int need_detach_console = 0;
3237 
os_detach_console(void)3238 void os_detach_console(void)
3239 {
3240 #if defined(WIN)
3241 	if (is_winnt())
3242 		FreeConsole();
3243 #endif
3244 #if !defined(NO_FORK_ON_EXIT)
3245 	{
3246 		pid_t rp;
3247 	/* Intel and PathScale handle fork gracefully */
3248 #if !defined(__ICC) && !defined(__PATHSCALE__)
3249 		disable_openmp = 1;
3250 #endif
3251 		EINTRLOOP(rp, fork());
3252 		if (!rp) {
3253 			reinit_child();
3254 #if defined(HAVE_PTHREADS)
3255 			reset_thread_count();
3256 #endif
3257 		}
3258 		if (rp > 0) {
3259 #if defined(HAVE_PTHREADS)
3260 			while (get_thread_count()) {
3261 				portable_sleep(1000);
3262 			}
3263 #endif
3264 			_exit(0);
3265 		}
3266 	}
3267 #endif
3268 }
3269 
3270 #if defined(OS2) || defined(DOS)
3271 
get_country_language(int c)3272 int get_country_language(int c)
3273 {
3274 	static_const struct {
3275 		int code;
3276 		unsigned char *language;
3277 	} countries[] = {
3278 		{ 1, cast_uchar "English" },
3279 		{ 2, cast_uchar "French" },
3280 		{ 3, cast_uchar "Spanish" },
3281 		{ 4, cast_uchar "English" },
3282 		{ 7, cast_uchar "Russian" },
3283 		{ 27, cast_uchar "English" },
3284 		{ 30, cast_uchar "Greek" },
3285 		{ 31, cast_uchar "Dutch" },
3286 		{ 32, cast_uchar "Dutch" },
3287 		{ 33, cast_uchar "French" },
3288 		{ 34, cast_uchar "Spanish" },
3289 		{ 36, cast_uchar "Hungarian" },
3290 		{ 38, cast_uchar "Serbian" },
3291 		{ 39, cast_uchar "Italian" },
3292 		{ 40, cast_uchar "Romanian" },
3293 		{ 41, cast_uchar "Swiss German" },
3294 		{ 42, cast_uchar "Czech" },
3295 		{ 43, cast_uchar "German" },
3296 		{ 44, cast_uchar "English" },
3297 		{ 45, cast_uchar "Danish" },
3298 		{ 46, cast_uchar "Swedish" },
3299 		{ 47, cast_uchar "Norwegian" },
3300 		{ 48, cast_uchar "Polish" },
3301 		{ 49, cast_uchar "German" },
3302 		{ 52, cast_uchar "Spanish" },
3303 		{ 54, cast_uchar "Spanish" },
3304 		{ 55, cast_uchar "Brazilian Portuguese" },
3305 		{ 56, cast_uchar "Spanish" },
3306 		{ 57, cast_uchar "Spanish" },
3307 		{ 58, cast_uchar "Spanish" },
3308 		{ 61, cast_uchar "English" },
3309 		{ 64, cast_uchar "English" },
3310 		{ 65, cast_uchar "English" },
3311 		{ 90, cast_uchar "Turkish" },
3312 		{ 99, cast_uchar "English" },
3313 		{ 351, cast_uchar "Portuguese" },
3314 		{ 353, cast_uchar "English" },
3315 		{ 354, cast_uchar "Icelandic" },
3316 		{ 358, cast_uchar "Finnish" },
3317 		{ 359, cast_uchar "Bulgarian" },
3318 		{ 371, cast_uchar "Lithuanian" },
3319 		{ 372, cast_uchar "Estonian" },
3320 		{ 381, cast_uchar "Serbian" },
3321 		{ 384, cast_uchar "Croatian" },
3322 		{ 385, cast_uchar "Croatian" },
3323 #ifdef DOS
3324 		{ 421, cast_uchar "Slovak" },
3325 #else
3326 		{ 421, cast_uchar "Czech" },
3327 #endif
3328 		{ 422, cast_uchar "Slovak" },
3329 		{ 593, cast_uchar "Spanish" },
3330 	};
3331 	int idx, i;
3332 #define C_EQUAL(a, b)	countries[a].code == (b)
3333 #define C_ABOVE(a, b)	countries[a].code > (b)
3334 	BIN_SEARCH(array_elements(countries), C_EQUAL, C_ABOVE, c, idx);
3335 	if (idx == -1)
3336 		return -1;
3337 	for (i = 0; i < n_languages(); i++)
3338 		if (!casestrcmp(language_name(i), countries[idx].language))
3339 			return i;
3340 	return -1;
3341 }
3342 
3343 #endif
3344 
3345 #if defined(OS2)
3346 
os_default_language(void)3347 int os_default_language(void)
3348 {
3349 	COUNTRYCODE cc;
3350 	COUNTRYINFO ci;
3351 	ULONG ul;
3352 	int rc;
3353 	memset(&cc, 0, sizeof cc);
3354 	rc = DosQueryCtryInfo(sizeof ci, &cc, &ci, &ul);
3355 	if (!rc)
3356 		return get_country_language(ci.country);
3357 	return -1;
3358 }
3359 
3360 #elif defined(WIN)
3361 
os_default_language(void)3362 int os_default_language(void)
3363 {
3364 	LCID id;
3365 	unsigned char iso639[9];
3366 	unsigned char iso3166[9];
3367 	unsigned char loc[8 + 1 + 8 + 1];
3368 	unsigned char *lang;
3369 	lang = cast_uchar getenv("LANG");
3370 	if (lang) {
3371 		int l = get_language_from_lang(lang);
3372 		if (l >= 0)
3373 			return l;
3374 	}
3375 	id = GetUserDefaultUILanguage();
3376 	if (!GetLocaleInfoA(id, LOCALE_SISO639LANGNAME, cast_char iso639, 9))
3377 		return -1;
3378 	iso3166[0] = 0;
3379 	GetLocaleInfoA(id, LOCALE_SISO3166CTRYNAME, cast_char iso3166, 9);
3380 	strcpy(cast_char loc, cast_const_char iso639);
3381 	if (id >= 0x400 && iso3166[0]) {
3382 		strcat(cast_char loc, "_");
3383 		strcat(cast_char loc, cast_const_char iso3166);
3384 	}
3385 	return get_language_from_lang(loc);
3386 }
3387 
3388 #elif !defined(DOS)
3389 
os_default_language(void)3390 int os_default_language(void)
3391 {
3392 	return -1;
3393 }
3394 
3395 #endif
3396 
3397 #if defined(WIN) && defined(__CYGWIN__) && defined(HAVE_CYGWIN_CONV_PATH)
3398 
os_default_charset(void)3399 int os_default_charset(void)
3400 {
3401 	unsigned char *term = cast_uchar getenv("TERM");
3402 	if (term) {
3403 		if (!casestrcmp(term, cast_uchar "cygwin")) {
3404 #if defined(HAVE_NL_LANGINFO) && defined(HAVE_LANGINFO_H) && defined(CODESET)
3405 			return windows_charset();
3406 #endif
3407 		}
3408 		if (!casestrcmp(term, cast_uchar "xterm")) {
3409 			return utf8_table;
3410 		}
3411 	}
3412 	return -1;
3413 }
3414 
3415 #elif defined(OS2)
3416 
os_default_charset(void)3417 int os_default_charset(void)
3418 {
3419 	ULONG os2_cp[1];
3420 	ULONG size = 0;
3421 	int rc;
3422 
3423 	if (is_xterm())
3424 		return 0;
3425 
3426 	rc = DosQueryCp(sizeof(os2_cp), os2_cp, &size);
3427 	if ((!rc || rc == ERROR_CPLIST_TOO_SMALL) && size >= sizeof(ULONG)) {
3428 		unsigned char a[8];
3429 		int cp;
3430 		snprintf(cast_char a, sizeof a, "%lu", os2_cp[0]);
3431 		if ((cp = get_cp_index(a)) >= 0 && cp != utf8_table)
3432 			return cp;
3433 	}
3434 	return 0;
3435 }
3436 
3437 #elif !defined(DOS)
3438 
os_default_charset(void)3439 int os_default_charset(void)
3440 {
3441 	return -1;
3442 }
3443 
3444 #endif
3445 
3446 
3447 #if defined(__linux__) || defined(__LINUX__)
3448 
3449 static pid_t cons_pid = -1;
3450 
3451 static int cons_x = -1, cons_y = -1;
3452 
abort_save(void)3453 static void abort_save(void)
3454 {
3455 	if (cons_pid > 0)
3456 		kill(cons_pid, SIGKILL);
3457 	cons_pid = -1;
3458 	close_socket(&cons_control[0]);
3459 	close_socket(&cons_control[1]);
3460 	close_socket(&cons_status[0]);
3461 	close_socket(&cons_status[1]);
3462 }
3463 
3464 static const char * const cons_savers[] = {
3465 	"/usr/lib/mc/cons.saver",
3466 	"/usr/libexec/mc/cons.saver",
3467 };
3468 
save_terminal(void)3469 void save_terminal(void)
3470 {
3471 	int rs;
3472 	int p;
3473 	unsigned char *cons;
3474 	unsigned char st;
3475 
3476 	if (!(cons = is_on_linux_console()))
3477 		return;
3478 
3479 	for (p = 0; p < (int)array_elements(cons_savers); p++) {
3480 		EINTRLOOP(rs, access(cons_savers[p], X_OK));
3481 		if (!rs)
3482 			goto ok;
3483 	}
3484 	return;
3485 ok:
3486 
3487 	c_pipe(cons_control);
3488 	c_pipe(cons_status);
3489 
3490 	block_signals(SIGTERM, 0);
3491 
3492 	cons_pid = fork();
3493 	if (!cons_pid) {
3494 		EINTRLOOP(rs, dup2(cons_control[0], 0));
3495 		if (rs < 0) _exit(1);
3496 		EINTRLOOP(rs, dup2(cons_status[1], 1));
3497 		if (rs < 0) _exit(1);
3498 		close_fork_tty();
3499 
3500 		execl(cons_savers[p], cons_savers[p], cast_const_char cons, NULL);
3501 		_exit(1);
3502 	}
3503 	if (cons_pid < 0) {
3504 		abort_save();
3505 		goto ret;
3506 	}
3507 	close_socket(&cons_control[0]);
3508 	close_socket(&cons_status[1]);
3509 
3510 	if (hard_read(cons_status[0], &st, 1) != 1 || st != 3) {
3511 		abort_save();
3512 		goto ret;
3513 	}
3514 
3515 	if (hard_write(cons_control[1], cast_uchar "1", 1) != 1) {
3516 		abort_save();
3517 		goto ret;
3518 	}
3519 	if (hard_read(cons_status[0], &st, 1) != 1 || st != 3) {
3520 		abort_save();
3521 		goto ret;
3522 	}
3523 
3524 	if (hard_write(cons_control[1], cast_uchar "3", 1) != 1) {
3525 		abort_save();
3526 		goto ret;
3527 	}
3528 	if (hard_read(cons_status[0], &st, 1) != 1 || st != 3) {
3529 		abort_save();
3530 		goto ret;
3531 	}
3532 
3533 	get_terminal_size(&cons_x, &cons_y);
3534 
3535 ret:
3536 	unblock_signals();
3537 }
3538 
restore_terminal(void)3539 void restore_terminal(void)
3540 {
3541 	if (cons_pid > 0) {
3542 		int cx, cy;
3543 		unsigned char st;
3544 
3545 		get_terminal_size(&cx, &cy);
3546 		if (cons_x == cx && cons_y == cy) {
3547 			if (hard_write(cons_control[1], cast_uchar "4", 1) != 1) {
3548 				abort_save();
3549 				return;
3550 			}
3551 			if (hard_read(cons_status[0], &st, 1) != 1 || st != 3) {
3552 				abort_save();
3553 				return;
3554 			}
3555 		}
3556 		if (hard_write(cons_control[1], cast_uchar "2", 1) != 1) {
3557 			abort_save();
3558 			return;
3559 		}
3560 		cons_pid = -1;
3561 		close_socket(&cons_control[1]);
3562 		close_socket(&cons_status[0]);
3563 	}
3564 }
3565 
3566 #elif defined(OS2)
3567 
3568 static int saved_x, saved_y;
3569 static USHORT saved_len;
3570 static unsigned char *saved_buffer;
3571 static USHORT cursor_x, cursor_y;
3572 
save_terminal(void)3573 void save_terminal(void)
3574 {
3575 	size_t len;
3576 	if (is_xterm())
3577 		return;
3578 	get_terminal_size(&saved_x, &saved_y);
3579 	len = saved_x * saved_y * 4;
3580 	saved_len = len;
3581 	if (len != saved_len)
3582 		return;
3583 	saved_buffer = _tmalloc(saved_len);
3584 	if (!saved_buffer)
3585 		return;
3586 	if (VioReadCellStr(saved_buffer, &saved_len, 0, 0, 0))
3587 		goto x;
3588 	if (VioGetCurPos(&cursor_y, &cursor_x, 0))
3589 		goto x;
3590 	return;
3591 x:
3592 	_tfree(saved_buffer);
3593 	saved_buffer = NULL;
3594 }
3595 
restore_terminal(void)3596 void restore_terminal(void)
3597 {
3598 	int x, y;
3599 	if (is_xterm())
3600 		return;
3601 	if (!saved_buffer)
3602 		return;
3603 	get_terminal_size(&x, &y);
3604 	if (x != saved_x || y != saved_y)
3605 		goto x;
3606 	VioWrtCellStr(saved_buffer, saved_len, 0, 0, 0);
3607 	VioSetCurPos(cursor_y, cursor_x, 0);
3608 x:
3609 	_tfree(saved_buffer);
3610 	saved_buffer = NULL;
3611 }
3612 
3613 #elif defined(DOS)
3614 
3615 #else
3616 
save_terminal(void)3617 void save_terminal(void)
3618 {
3619 }
3620 
restore_terminal(void)3621 void restore_terminal(void)
3622 {
3623 }
3624 
3625 #endif
3626 
os_report_error_va(const char * caption,const char * msg,va_list l)3627 void os_report_error_va(const char *caption, const char *msg, va_list l)
3628 {
3629 #ifdef OS2
3630 	char msg_buffer[OS_REPORT_ERROR_BUFFER];
3631 	vsnprintf(msg_buffer, sizeof msg_buffer, msg, l);
3632 	if (os2_init_pm()) return;
3633 	WinMessageBox(HWND_DESKTOP, NULLHANDLE, msg_buffer, caption, 0, MB_CANCEL | MB_ERROR | MB_APPLMODAL | MB_MOVEABLE);
3634 	os2_exit_pm();
3635 #endif
3636 #if defined(WIN) && !(defined(_UWIN) && !defined(GRDRV_PMSHELL))
3637 	char msg_buffer[OS_REPORT_ERROR_BUFFER];
3638 	vsnprintf(msg_buffer, sizeof msg_buffer, msg, l);
3639 	MessageBoxA(NULL, msg_buffer, caption, MB_OK | MB_ICONEXCLAMATION);
3640 #endif
3641 }
3642 
os_report_error(const char * caption,const char * msg,...)3643 void os_report_error(const char *caption, const char *msg, ...)
3644 {
3645 	va_list l;
3646 	va_start(l, msg);
3647 	os_report_error_va(caption, msg, l);
3648 	va_end(l);
3649 }
3650