lib_setup.c revision 50276
1/****************************************************************************
2 * Copyright (c) 1998 Free Software Foundation, Inc.                        *
3 *                                                                          *
4 * Permission is hereby granted, free of charge, to any person obtaining a  *
5 * copy of this software and associated documentation files (the            *
6 * "Software"), to deal in the Software without restriction, including      *
7 * without limitation the rights to use, copy, modify, merge, publish,      *
8 * distribute, distribute with modifications, sublicense, and/or sell       *
9 * copies of the Software, and to permit persons to whom the Software is    *
10 * furnished to do so, subject to the following conditions:                 *
11 *                                                                          *
12 * The above copyright notice and this permission notice shall be included  *
13 * in all copies or substantial portions of the Software.                   *
14 *                                                                          *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22 *                                                                          *
23 * Except as contained in this notice, the name(s) of the above copyright   *
24 * holders shall not be used in advertising or otherwise to promote the     *
25 * sale, use or other dealings in this Software without prior written       *
26 * authorization.                                                           *
27 ****************************************************************************/
28
29/****************************************************************************
30 *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
31 *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32 ****************************************************************************/
33
34
35/*
36 * Terminal setup routines common to termcap and terminfo:
37 *
38 *		use_env(bool)
39 *		setupterm(char *, int, int *)
40 */
41
42#include <curses.priv.h>
43#include <tic.h>	/* for MAX_NAME_SIZE */
44#include <term_entry.h>
45
46#if defined(SVR4_TERMIO) && !defined(_POSIX_SOURCE)
47#define _POSIX_SOURCE
48#endif
49
50#include <term.h>	/* lines, columns, cur_term */
51
52MODULE_ID("$Id: lib_setup.c,v 1.55 1999/08/21 23:06:08 tom Exp $")
53
54/****************************************************************************
55 *
56 * Terminal size computation
57 *
58 ****************************************************************************/
59
60#if HAVE_SIZECHANGE
61# if !defined(sun) || !TERMIOS
62#  if HAVE_SYS_IOCTL_H
63#   include <sys/ioctl.h>
64#  endif
65# endif
66#endif
67
68#if NEED_PTEM_H
69 /* On SCO, they neglected to define struct winsize in termios.h -- it's only
70  * in termio.h and ptem.h (the former conflicts with other definitions).
71  */
72# include <sys/stream.h>
73# include <sys/ptem.h>
74#endif
75
76/*
77 * SCO defines TIOCGSIZE and the corresponding struct.  Other systems (SunOS,
78 * Solaris, IRIX) define TIOCGWINSZ and struct winsize.
79 */
80#ifdef TIOCGSIZE
81# define IOCTL_WINSIZE TIOCGSIZE
82# define STRUCT_WINSIZE struct ttysize
83# define WINSIZE_ROWS(n) (int)n.ts_lines
84# define WINSIZE_COLS(n) (int)n.ts_cols
85#else
86# ifdef TIOCGWINSZ
87#  define IOCTL_WINSIZE TIOCGWINSZ
88#  define STRUCT_WINSIZE struct winsize
89#  define WINSIZE_ROWS(n) (int)n.ws_row
90#  define WINSIZE_COLS(n) (int)n.ws_col
91# endif
92#endif
93
94static int _use_env = TRUE;
95
96static void do_prototype(void);
97
98void use_env(bool f)
99{
100	_use_env = f;
101}
102
103int LINES, COLS, TABSIZE;
104
105static void _nc_get_screensize(int *linep, int *colp)
106/* Obtain lines/columns values from the environment and/or terminfo entry */
107{
108	/* figure out the size of the screen */
109	T(("screen size: terminfo lines = %d columns = %d", lines, columns));
110
111	if (!_use_env)
112	{
113	    *linep = (int)lines;
114	    *colp  = (int)columns;
115	}
116	else	/* usually want to query LINES and COLUMNS from environment */
117	{
118	    int value;
119
120	    *linep = *colp = 0;
121
122	    /* first, look for environment variables */
123	    if ((value = _nc_getenv_num("LINES")) > 0) {
124		    *linep = value;
125	    }
126	    if ((value = _nc_getenv_num("COLUMNS")) > 0) {
127		    *colp = value;
128	    }
129	    T(("screen size: environment LINES = %d COLUMNS = %d",*linep,*colp));
130
131#ifdef __EMX__
132	    if (*linep <= 0 || *colp <= 0)
133	    {
134		int screendata[2];
135		_scrsize(screendata);
136		*colp  = screendata[0];
137		*linep = screendata[1];
138	    	T(("EMX screen size: environment LINES = %d COLUMNS = %d",*linep,*colp));
139	    }
140#endif
141#if HAVE_SIZECHANGE
142	    /* if that didn't work, maybe we can try asking the OS */
143	    if (*linep <= 0 || *colp <= 0)
144	    {
145		if (isatty(cur_term->Filedes))
146		{
147		    STRUCT_WINSIZE size;
148
149		    errno = 0;
150		    do {
151			if (ioctl(cur_term->Filedes, IOCTL_WINSIZE, &size) < 0
152				&& errno != EINTR)
153			    goto failure;
154		    } while
155			(errno == EINTR);
156
157		    /*
158		     * Solaris lets users override either dimension with an
159		     * environment variable.
160		     */
161		    if (*linep <= 0)
162			*linep = WINSIZE_ROWS(size);
163		    if (*colp <= 0)
164			*colp  = WINSIZE_COLS(size);
165		}
166		/* FALLTHRU */
167	    failure:;
168	    }
169#endif /* HAVE_SIZECHANGE */
170
171	    /* if we can't get dynamic info about the size, use static */
172	    if (*linep <= 0 || *colp <= 0)
173		if (lines > 0 && columns > 0)
174		{
175		    *linep = (int)lines;
176		    *colp  = (int)columns;
177		}
178
179	    /* the ultimate fallback, assume fixed 24x80 size */
180	    if (*linep <= 0 || *colp <= 0)
181	    {
182		*linep = 24;
183		*colp  = 80;
184	    }
185
186	    /*
187	     * Put the derived values back in the screen-size caps, so
188	     * tigetnum() and tgetnum() will do the right thing.
189	     */
190	    lines   = (short)(*linep);
191	    columns = (short)(*colp);
192	}
193
194	T(("screen size is %dx%d", *linep, *colp));
195
196	if (init_tabs != -1)
197		TABSIZE = (int)init_tabs;
198	else
199		TABSIZE = 8;
200	T(("TABSIZE = %d", TABSIZE));
201
202}
203
204#if USE_SIZECHANGE
205void _nc_update_screensize(void)
206{
207	int my_lines, my_cols;
208
209	_nc_get_screensize(&my_lines, &my_cols);
210	if (SP != 0 && SP->_resize != 0)
211		SP->_resize(my_lines, my_cols);
212}
213#endif
214
215/****************************************************************************
216 *
217 * Terminal setup
218 *
219 ****************************************************************************/
220
221#define ret_error(code, fmt, arg)	if (errret) {\
222					    *errret = code;\
223					    returnCode(ERR);\
224					} else {\
225					    fprintf(stderr, fmt, arg);\
226					    exit(EXIT_FAILURE);\
227					}
228
229#define ret_error0(code, msg)		if (errret) {\
230					    *errret = code;\
231					    returnCode(ERR);\
232					} else {\
233					    fprintf(stderr, msg);\
234					    exit(EXIT_FAILURE);\
235					}
236
237#if USE_DATABASE
238static int grab_entry(const char *const tn, TERMTYPE *const tp)
239/* return 1 if entry found, 0 if not found, -1 if database not accessible */
240{
241	char	filename[PATH_MAX];
242	int	status;
243
244	/*
245	 * $TERM shouldn't contain pathname delimiters.
246	 */
247	if (strchr(tn, '/'))
248		return 0;
249
250	if ((status = _nc_read_entry(tn, filename, tp)) != 1) {
251
252#ifndef PURE_TERMINFO
253		/*
254		 * Try falling back on the termcap file.
255		 * Note:  allowing this call links the entire terminfo/termcap
256		 * compiler into the startup code.  It's preferable to build a
257		 * real terminfo database and use that.
258		 */
259		status = _nc_read_termcap_entry(tn, tp);
260#endif /* PURE_TERMINFO */
261
262	}
263
264	/*
265	 * If we have an entry, force all of the cancelled strings to null
266	 * pointers so we don't have to test them in the rest of the library.
267	 * (The terminfo compiler bypasses this logic, since it must know if
268	 * a string is cancelled, for merging entries).
269	 */
270	if (status == 1) {
271		int n;
272		for_each_boolean(n,tp)
273			if (!VALID_BOOLEAN(tp->Booleans[n]))
274				tp->Booleans[n] = FALSE;
275		for_each_string(n,tp)
276			if (tp->Strings[n] == CANCELLED_STRING)
277				tp->Strings[n] = ABSENT_STRING;
278	}
279	return(status);
280}
281#endif
282
283char ttytype[NAMESIZE];
284
285/*
286 *	setupterm(termname, Filedes, errret)
287 *
288 *	Find and read the appropriate object file for the terminal
289 *	Make cur_term point to the structure.
290 *
291 */
292
293int setupterm(NCURSES_CONST char *tname, int Filedes, int *errret)
294{
295struct term	*term_ptr;
296int status;
297
298	T((T_CALLED("setupterm(%s,%d,%p)"), _nc_visbuf(tname), Filedes, errret));
299
300	if (tname == 0) {
301		tname = getenv("TERM");
302		if (tname == 0 || *tname == '\0') {
303			ret_error0(-1, "TERM environment variable not set.\n");
304                }
305	}
306	if (strlen(tname) > MAX_NAME_SIZE) {
307		ret_error(-1, "TERM environment must be <= %d characters.\n",
308		    MAX_NAME_SIZE);
309	}
310
311	T(("your terminal name is %s", tname));
312
313	term_ptr = typeCalloc(TERMINAL, 1);
314
315	if (term_ptr == 0) {
316		ret_error0(-1, "Not enough memory to create terminal structure.\n") ;
317        }
318#if USE_DATABASE
319	status = grab_entry(tname, &term_ptr->type);
320#else
321	status = 0;
322#endif
323
324	/* try fallback list if entry on disk */
325	if (status != 1)
326	{
327	    const TERMTYPE	*fallback = _nc_fallback(tname);
328
329	    if (fallback)
330	    {
331		term_ptr->type = *fallback;
332		status = 1;
333	    }
334	}
335
336	if (status == -1)
337	{
338		ret_error0(-1, "terminals database is inaccessible\n");
339	}
340	else if (status == 0)
341	{
342		ret_error(0, "'%s': unknown terminal type.\n", tname);
343	}
344
345	/*
346	 * Improve on SVr4 curses.  If an application mixes curses and termcap
347	 * calls, it may call both initscr and tgetent.  This is not really a
348	 * good thing to do, but can happen if someone tries using ncurses with
349	 * the readline library.  The problem we are fixing is that when
350	 * tgetent calls setupterm, the resulting Ottyb struct in cur_term is
351	 * zeroed.  A subsequent call to endwin uses the zeroed terminal
352	 * settings rather than the ones saved in initscr.  So we check if
353	 * cur_term appears to contain terminal settings for the same output
354	 * file as our current call - and copy those terminal settings.  (SVr4
355	 * curses does not do this, however applications that are working
356	 * around the problem will still work properly with this feature).
357	 */
358	if (cur_term != 0) {
359		if (cur_term->Filedes == Filedes)
360			term_ptr->Ottyb = cur_term->Ottyb;
361	}
362
363	set_curterm(term_ptr);
364
365	if (command_character  &&  getenv("CC"))
366		do_prototype();
367
368	strncpy(ttytype, cur_term->type.term_names, NAMESIZE - 1);
369	ttytype[NAMESIZE - 1] = '\0';
370
371	/*
372	 * Allow output redirection.  This is what SVr3 does.
373	 * If stdout is directed to a file, screen updates go
374	 * to standard error.
375	 */
376	if (Filedes == STDOUT_FILENO && !isatty(Filedes))
377	    Filedes = STDERR_FILENO;
378	cur_term->Filedes = Filedes;
379
380	_nc_get_screensize(&LINES, &COLS);
381
382	if (errret)
383		*errret = 1;
384
385	T((T_CREATE("screen %s %dx%d"), tname, LINES, COLS));
386
387	if (generic_type) {
388		ret_error(0, "'%s': I need something more specific.\n", tname);
389	}
390	if (hard_copy) {
391		ret_error(1, "'%s': I can't handle hardcopy terminals.\n", tname);
392	}
393	returnCode(OK);
394}
395
396/*
397**	do_prototype()
398**
399**	Take the real command character out of the CC environment variable
400**	and substitute it in for the prototype given in 'command_character'.
401**
402*/
403
404static void
405do_prototype(void)
406{
407int	i;
408char	CC;
409char	proto;
410char    *tmp;
411
412	tmp = getenv("CC");
413	CC = *tmp;
414	proto = *command_character;
415
416	for_each_string(i, &(cur_term->type)) {
417	    for (tmp = cur_term->type.Strings[i]; *tmp; tmp++) {
418		if (*tmp == proto)
419		    *tmp = CC;
420	    }
421	}
422}
423