1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1995, by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 /*
28  * newterm.c
29  *
30  * XCurses Library
31  *
32  * Copyright 1990, 1995 by Mortice Kern Systems Inc.  All rights reserved.
33  *
34  */
35 
36 #ifdef M_RCSID
37 #ifndef lint
38 static char const rcsID[] = "$Header: /rd/src/libc/xcurses/rcs/newterm.c 1.15 1995/07/25 19:54:00 ant Exp $";
39 #endif
40 #endif
41 
42 #include <private.h>
43 #include <m_wio.h>
44 #include <errno.h>
45 #include <signal.h>
46 #include <stdlib.h>
47 #include <string.h>
48 
49 int LINES, COLS;
50 int COLORS, COLOR_PAIRS;
51 
52 WINDOW *curscr;
53 WINDOW *stdscr;
54 SCREEN *__m_screen;
55 
56 static short assume_one_line = FALSE;
57 
58 /*
59  * Assume terminal has only one screen line by restricting those
60  * capabilities that assume more than one line.  This function must
61  * be called before initscr() or newterm().
62  *
63  * This flag will reset after initscr() or newterm() so that subsequent
64  * calls to newterm(), without a preceding call to filter(), will load
65  * an unmodified terminal.  THIS IS NOT HISTORICAL PRACTICE, BUT DEEMED
66  * USEFUL.
67  */
68 void
filter(void)69 filter(void)
70 {
71 #ifdef M_CURSES_TRACE
72 	__m_trace("filter(void)");
73 #endif
74 	assume_one_line = TRUE;
75 	__m_return_void("filter");
76 }
77 
78 /*f
79  * SIGTSTP Handler.
80  */
81 void
tstp(signo)82 tstp(signo)
83 int signo;
84 {
85 #ifdef SIGTSTP
86 	/* Only permit SIGTSTP if the curent process is the process
87 	 * group leader.  If the process is not the current group
88 	 * leader, then suspending the current process will suspend
89 	 * other members of the process group, such as the parent
90 	 * process.
91 	 */
92 	if (getpid() == getpgrp()) {
93 		(void) endwin();
94 
95 #ifdef SIG_UNBLOCK
96 {
97 		sigset_t unblock;
98 
99 		(void) sigemptyset(&unblock);
100 		(void) sigaddset(&unblock, SIGTSTP);
101 		(void) sigprocmask(SIG_UNBLOCK, &unblock, (sigset_t *) 0);
102 }
103 #endif /* SIG_UNBLOCK */
104 		(void) signal(SIGTSTP, SIG_DFL);
105 		(void) kill(0, SIGTSTP);
106 	} else {
107 		(void) beep();
108 	}
109 
110 	(void) signal(SIGTSTP, tstp);
111 	(void) wrefresh(curscr);
112 #else /* no SIGTSTP */
113 	(void) beep();
114 #endif /* SIGTSTP */
115 }
116 
117 int __m_slk_format = -1;
118 
119 /*
120  * Do real soft label key initialisation once setupterm() have been called
121  * to load the current terminal.  Determine whether the terminal supplies
122  * soft label keys, or whether we have to fake it by using the last line
123  * of a terminal screen.
124  */
125 int
__m_slk_init(SCREEN * sp,int style)126 __m_slk_init(SCREEN *sp, int style)
127 {
128 	int code;
129 
130 #ifdef M_CURSES_TRACE
131 	__m_trace("__m_slk_init(%d)", style );
132 #endif
133 
134 	code = ERR;
135 
136 	/* Does the terminal have a method to program the soft label key? */
137 	if (plab_norm != (char *) 0 || pkey_plab  != (char *) 0) {
138 		code = OK;
139 		goto done;
140 	}
141 
142 	/* We have to fake it. */
143 	if (lines < 2)
144 		goto done;
145 
146 	sp->_slk._w = subwin(sp->_newscr, 1, 0, --lines, 0);
147 	if (sp->_slk._w == (WINDOW *) 0)
148 		goto done;
149 
150 	code = OK;
151 done:
152 	return __m_return_code("__m_slk_init", code);
153 }
154 
155 /*
156  * The XCurses specification is unclear how ripoffline() would
157  * affect newterm().  We assume that it can't be used with newterm()
158  * and that it only affects initscr(), which is responsible for
159  * creating stdscr.
160  */
161 static t_rip rip = { 0 };
162 
163 /*
164  * If line is positive (1), one line is removed from the beginning of
165  * stdscr; else if line is negative (-1), one line is removed from the end.
166  */
167 int
ripoffline(int line,int (* init)(WINDOW *,int))168 ripoffline(int line, int (*init)(WINDOW *, int))
169 {
170 	int i;
171 
172 #ifdef M_CURSES_TRACE
173 	__m_trace("ripoffline(%d, %p)", line, init);
174 #endif
175 
176 	i = rip.top - rip.bottom;
177 
178 	if (line != 0 && i + 1 < M_CURSES_MAX_RIPOFFLINE) {
179 		rip.line[i].init = init;
180 		if (line < 0)
181 			rip.line[i].dy = --rip.bottom;
182 		else
183 			rip.line[i].dy = ++rip.top;
184 	}
185 
186 	return __m_return_code("ripoffline", OK);
187 }
188 
189 /*f
190  * Create a new terminal screen.  Used if a program is going to be sending
191  * output to more than one terminal.  It returns a SCREEN* for the terminal.
192  * The parameters are a terminal name, output FILE*, and input FILE*.  If
193  * the terminal name is null then $TERM is used.  The program must also
194  * call endwin() for each terminal being used before exiting from curses.
195  * If newterm() is called more than once for the same terminal, the first
196  * terminal referred to must be the last one for which endwin() is called.
197  */
198 SCREEN *
newterm(term,out_fp,in_fp)199 newterm(term, out_fp, in_fp)
200 char *term;
201 FILE *out_fp, *in_fp;
202 {
203 	WINDOW *w;
204 	t_wide_io *wio;
205 	SCREEN *sp, *osp;
206 	int i, n, y, errret;
207 
208 #ifdef M_CURSES_TRACE
209 	__m_trace(
210 		"newterm(%s, %p, %p)",
211 		term == (char *) 0 ? "NULL" : term, out_fp, in_fp
212 	);
213 #endif
214 
215 	/* Input stream should be unbuffered so that m_tfgetc() works
216 	 * correctly on BSD and SUN systems.
217 	 */
218 	(void) SETVBUF(in_fp, (char *) 0, _IONBF, BUFSIZ);
219 #if 0
220 /*
221  * Not sure whether we really want to concern ourselves with the output
222  * buffer scheme.  Might be best to leave it upto the application to
223  * deal with buffer schemes and when to perform flushes.
224  *
225  * MKS Vi uses MKS Curses and so must support the ability to switch in
226  * and out of Curses mode when switching from Vi to Ex and back.
227  * Problem is that in Vi mode you would prefer full buffered output to
228  * give updates a smoother appearance and Ex mode you require line
229  * buffered in order to see prompts and messages.
230  */
231 	(void) SETVBUF(out_fp, (char *) 0, _IOLBF, BUFSIZ);
232 #endif
233 	errno = 0;
234 
235 	if (__m_setupterm(term, fileno(in_fp), fileno(out_fp), &errret)==ERR) {
236 		switch (errret) {
237 		case -1:
238 			errno = ENOMEM;
239 			break;
240 		case 2:
241 			errno = ENAMETOOLONG;
242 			break;
243 		case 0:
244 		default:
245 			errno = ENOENT;
246 			break;
247 		}
248 		goto error1;
249 	}
250 
251 	if (__m_doupdate_init())
252 		goto error1;
253 
254 	if ((sp = (SCREEN *) calloc(1, sizeof *sp)) == (SCREEN *) 0)
255 		goto error1;
256 
257 	sp->_if = in_fp;
258 	sp->_of = out_fp;
259 	sp->_term = cur_term;
260 
261 	sp->_unget._size = __m_decode_init((t_decode **) &sp->_decode);
262 
263 	/* Maximum length of a multbyte key sequence, including
264 	 * multibyte characters and terminal function keys.
265 	 */
266 	if (sp->_unget._size < MB_LEN_MAX)
267 		sp->_unget._size = MB_LEN_MAX;
268 
269 	sp->_unget._stack = calloc(
270 		(size_t) sp->_unget._size,  sizeof *sp->_unget._stack
271 	);
272 	if (sp->_unget._stack == (void *) 0)
273 		goto error2;
274 
275 	if ((wio = (t_wide_io *) calloc(1, sizeof *wio)) == (t_wide_io *) 0)
276 		goto error2;
277 
278 	/* Setup wide input for XCurses. */
279 	wio->get = (int (*)(void *)) wgetch;
280 	wio->unget = __xc_ungetc;
281 	wio->reset = __xc_clearerr;
282 	wio->iserror = __xc_ferror;
283 	wio->iseof = __xc_feof;
284 	sp->_in = wio;
285 
286 	if (assume_one_line) {
287 		/* Assume only one line. */
288 		lines = 1;
289 
290 		/* Disable capabilities that assume more than one line. */
291 		clear_screen =
292 		clr_eos =
293 		cursor_up =
294 		cursor_down =
295 		cursor_home =
296 		cursor_to_ll =
297 		cursor_address =
298 		row_address =
299 		parm_up_cursor =
300 		parm_down_cursor = (char *) 0;
301 
302 		/* Re-evaluate the cursor motion costs. */
303 		__m_mvcur_cost();
304 
305 		/* Reset flag for subsequent calls to newterm(). */
306 		assume_one_line = FALSE;
307 	}
308 
309 	if ((sp->_curscr = newwin(lines, columns, 0, 0)) == (WINDOW *) 0)
310 		goto error2;
311 
312 	if ((sp->_newscr = newwin(lines, columns, 0, 0)) == (WINDOW *) 0)
313 		goto error2;
314 
315 	sp->_hash = (unsigned long *) calloc(lines, sizeof *sp->_hash);
316 	if (sp->_hash == (unsigned long *) 0)
317 		goto error2;
318 
319 	if (0 <= __m_slk_format && __m_slk_init(sp, __m_slk_format) == ERR)
320 		goto error2;
321 
322 	/* doupdate() will perform the final screen preparations like
323 	 * enter_ca_mode, reset_prog_mode() (to assert the termios
324 	 * changes), etc.
325 	 */
326 	sp->_flags |= S_ENDWIN;
327 
328 #ifdef SIGTSTP
329 	(void) signal(SIGTSTP, tstp);
330 #endif
331 	/* Assert that __m_screen is set to the new terminal. */
332 	osp = set_term(sp);
333 
334 	/* Disable echo in tty driver, Curses does software echo. */
335 	cur_term->_prog.c_lflag &= ~ECHO;
336 
337 	/* Enable mappnig of cr -> nl on input and nl -> crlf on output. */
338         cur_term->_prog.c_iflag |= ICRNL;
339         cur_term->_prog.c_oflag |= OPOST;
340 #ifdef ONLCR
341         cur_term->_prog.c_oflag |= ONLCR;
342 #endif
343 	cur_term->_flags |= __TERM_NL_IS_CRLF;
344 
345 #ifdef TAB0
346 	/* Use real tabs. */
347 	cur_term->_prog.c_oflag &= ~(TAB1|TAB2|TAB3);
348 #endif
349 
350 	(void) __m_tty_set(&cur_term->_prog);
351 	(void) __m_set_echo(1);
352 	(void) typeahead(fileno(in_fp));
353 
354 	if (stdscr == (WINDOW *) 0) {
355 		n = rip.top - rip.bottom;
356 		stdscr = newwin(lines - n, 0, rip.top, 0);
357 		if (stdscr == (WINDOW *) 0)
358 			goto error3;
359 
360 		/* Create and initialise ripped off line windows.
361 		 * It is the application's responsiblity to free the
362 		 * windows when the application terminates.
363 		 */
364 		for (i = 0; i < n; ++i) {
365 			y = rip.line[i].dy;
366 			if (y < 0)
367 				y += lines;
368 
369 			w = newwin(1, 0, y, 0);
370 			if (rip.line[i].init != (int (*)(WINDOW *, int)) 0)
371 				(void) (*rip.line[i].init)(w, columns);
372 		}
373 	}
374 
375 	return __m_return_pointer("newterm", sp);
376 error3:
377 	(void) set_term(osp);
378 error2:
379 	delscreen(sp);
380 error1:
381 	return __m_return_pointer("newterm", (SCREEN *) 0);
382 }
383 
384 /*f
385  * Free storage associated with a screen structure.
386  * NOTE endwin() does not do this.
387  */
388 void
delscreen(sp)389 delscreen(sp)
390 SCREEN *sp;
391 {
392 #ifdef M_CURSES_TRACE
393 	__m_trace("delscreen(%p)", sp);
394 #endif
395 
396 	if (sp != (SCREEN *) 0) {
397 		if (sp->_slk._w != (WINDOW *) 0)
398 			(void) delwin(sp->_slk._w);
399 
400 		(void) delwin(sp->_newscr);
401 		(void) delwin(sp->_curscr);
402 		(void) del_curterm(sp->_term);
403 
404 		__m_decode_free((t_decode **) &sp->_decode);
405 
406 		if (sp->_hash != (unsigned long *) 0)
407 			free(sp->_hash);
408 
409 		if (sp->_unget._stack != (int *) 0)
410 			free(sp->_unget._stack);
411 
412 		if (sp->_in != (void *) 0)
413 			free(sp->_in);
414 
415 		free(sp);
416 	}
417 
418 	__m_return_void("delscreen");
419 }
420 
421 /*f
422  * Switch current terminal for Curses layer.
423  */
424 SCREEN *
set_term(screen)425 set_term(screen)
426 SCREEN *screen;
427 {
428     	SCREEN *osp = __m_screen;
429 
430 #ifdef M_CURSES_TRACE
431 	__m_trace("set_term(%p)", screen);
432 #endif
433 	if (screen != (SCREEN *) 0) {
434 		(void) set_curterm(screen->_term);
435 		curscr = screen->_curscr;
436 		__m_screen = screen;
437 
438 		LINES = lines;
439 		COLS = columns;
440 		COLORS = max_colors;
441 		COLOR_PAIRS = max_pairs;
442 	}
443 
444 	return __m_return_pointer("set_term", osp);
445 }
446 
447 int
typeahead(fd)448 typeahead(fd)
449 int fd;
450 {
451 #ifdef M_CURSES_TRACE
452 	__m_trace("typeahead(%d)", fd);
453 #endif
454 
455 	__m_screen->_kfd = fd;
456 	__m_screen->_flags &= ~S_ISATTY;
457 
458 	if (fd != -1 && isatty(fd))
459 		__m_screen->_flags |= S_ISATTY;
460 
461 	return __m_return_code("typeahead", OK);
462 }
463 
464 int
__m_set_echo(bf)465 __m_set_echo(bf)
466 int bf;
467 {
468 	int old;
469 
470 	old = (__m_screen->_flags & S_ECHO) == S_ECHO;
471 
472 	__m_screen->_flags &= ~S_ECHO;
473 	if (bf)
474 		__m_screen->_flags |= S_ECHO;
475 
476 	return old;
477 }
478 
479