lib_setup.c revision 97049
143967Sache/****************************************************************************
26527Sache * Copyright (c) 1998,1999,2000,2001 Free Software Foundation, Inc.         *
36527Sache *                                                                          *
46527Sache * Permission is hereby granted, free of charge, to any person obtaining a  *
56527Sache * copy of this software and associated documentation files (the            *
66527Sache * "Software"), to deal in the Software without restriction, including      *
76527Sache * without limitation the rights to use, copy, modify, merge, publish,      *
86527Sache * distribute, distribute with modifications, sublicense, and/or sell       *
96527Sache * copies of the Software, and to permit persons to whom the Software is    *
106527Sache * furnished to do so, subject to the following conditions:                 *
116527Sache *                                                                          *
126527Sache * The above copyright notice and this permission notice shall be included  *
136527Sache * in all copies or substantial portions of the Software.                   *
146527Sache *                                                                          *
156527Sache * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
166527Sache * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
176527Sache * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
186527Sache * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
196527Sache * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
206527Sache * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
216527Sache * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
226527Sache *                                                                          *
236527Sache * Except as contained in this notice, the name(s) of the above copyright   *
246527Sache * holders shall not be used in advertising or otherwise to promote the     *
256527Sache * sale, use or other dealings in this Software without prior written       *
266527Sache * authorization.                                                           *
276527Sache ****************************************************************************/
286527Sache
296527Sache/****************************************************************************
3087243Smarkm *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
3187243Smarkm *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
3287243Smarkm ****************************************************************************/
33175038Simp
3418950Sache/*
356527Sache * Terminal setup routines common to termcap and terminfo:
36101867Sache *
376527Sache *		use_env(bool)
386527Sache *		setupterm(char *, int, int *)
396527Sache */
4087012Sache
4116073Sphk#include <curses.priv.h>
426527Sache#include <tic.h>		/* for MAX_NAME_SIZE */
4343967Sache#include <term_entry.h>
4487052Sache
456527Sache#if SVR4_TERMIO && !defined(_POSIX_SOURCE)
466527Sache#define _POSIX_SOURCE
476527Sache#endif
486527Sache
496527Sache#include <term.h>		/* lines, columns, cur_term */
5087243Smarkm
516527SacheMODULE_ID("$Id: lib_setup.c,v 1.68 2001/12/08 22:14:18 tom Exp $")
526527Sache
5343967Sache/****************************************************************************
5443967Sache *
5543967Sache * Terminal size computation
5643967Sache *
576527Sache ****************************************************************************/
586527Sache
5918950Sache#if HAVE_SIZECHANGE
6043967Sache# if !defined(sun) || !TERMIOS
6143967Sache#  if HAVE_SYS_IOCTL_H
6243967Sache#   include <sys/ioctl.h>
6343967Sache#  endif
6443967Sache# endif
6543967Sache#endif
6643967Sache
6743967Sache#if NEED_PTEM_H
6818950Sache /* On SCO, they neglected to define struct winsize in termios.h -- it's only
6918950Sache  * in termio.h and ptem.h (the former conflicts with other definitions).
7018950Sache  */
7118950Sache# include <sys/stream.h>
7218950Sache# include <sys/ptem.h>
7318950Sache#endif
7418950Sache
7518950Sache/*
7618950Sache * SCO defines TIOCGSIZE and the corresponding struct.  Other systems (SunOS,
7718950Sache * Solaris, IRIX) define TIOCGWINSZ and struct winsize.
7818950Sache */
7918950Sache#ifdef TIOCGSIZE
8018950Sache# define IOCTL_WINSIZE TIOCGSIZE
8118950Sache# define STRUCT_WINSIZE struct ttysize
8243967Sache# define WINSIZE_ROWS(n) (int)n.ts_lines
8318950Sache# define WINSIZE_COLS(n) (int)n.ts_cols
8418950Sache#else
8518950Sache# ifdef TIOCGWINSZ
8643967Sache#  define IOCTL_WINSIZE TIOCGWINSZ
876527Sache#  define STRUCT_WINSIZE struct winsize
886527Sache#  define WINSIZE_ROWS(n) (int)n.ws_row
896527Sache#  define WINSIZE_COLS(n) (int)n.ws_col
906527Sache# endif
916527Sache#endif
926527Sache
93102299Sachestatic int _use_env = TRUE;
946527Sache
956527Sachestatic void do_prototype(void);
966527Sache
976527SacheNCURSES_EXPORT(void)
986527Sacheuse_env(bool f)
996527Sache{
100102299Sache    T((T_CALLED("use_env()")));
101102299Sache    _use_env = f;
10251890Sache    returnVoid;
10318950Sache}
10418950Sache
10518950SacheNCURSES_EXPORT_VAR(int) LINES = 0;
10618950SacheNCURSES_EXPORT_VAR(int) COLS = 0;
10718950SacheNCURSES_EXPORT_VAR(int) TABSIZE = 0;
10818950Sache
10918950Sachestatic void
11018950Sache_nc_get_screensize(int *linep, int *colp)
11118950Sache/* Obtain lines/columns values from the environment and/or terminfo entry */
11218950Sache{
1136527Sache    /* figure out the size of the screen */
11443940Sache    T(("screen size: terminfo lines = %d columns = %d", lines, columns));
1156527Sache
11618950Sache    if (!_use_env) {
11718950Sache	*linep = (int) lines;
11818950Sache	*colp = (int) columns;
11918950Sache    } else {			/* usually want to query LINES and COLUMNS from environment */
12043940Sache	int value;
12118950Sache
1226527Sache	*linep = *colp = 0;
1236527Sache
1246527Sache	/* first, look for environment variables */
1256527Sache	if ((value = _nc_getenv_num("LINES")) > 0) {
12643940Sache	    *linep = value;
1276527Sache	}
1286527Sache	if ((value = _nc_getenv_num("COLUMNS")) > 0) {
1296527Sache	    *colp = value;
1306527Sache	}
13118955Sache	T(("screen size: environment LINES = %d COLUMNS = %d", *linep, *colp));
13218955Sache
13318950Sache#ifdef __EMX__
13418955Sache	if (*linep <= 0 || *colp <= 0) {
13518950Sache	    int screendata[2];
1366527Sache	    _scrsize(screendata);
13787052Sache	    *colp = screendata[0];
13818955Sache	    *linep = screendata[1];
13918955Sache	    T(("EMX screen size: environment LINES = %d COLUMNS = %d",
14087052Sache	       *linep, *colp));
14118955Sache	}
14218955Sache#endif
14387052Sache#if HAVE_SIZECHANGE
14418955Sache	/* if that didn't work, maybe we can try asking the OS */
14543967Sache	if (*linep <= 0 || *colp <= 0) {
14643967Sache	    if (isatty(cur_term->Filedes)) {
14743967Sache		STRUCT_WINSIZE size;
14843967Sache
14918955Sache		errno = 0;
1506527Sache		do {
1516527Sache		    if (ioctl(cur_term->Filedes, IOCTL_WINSIZE, &size) < 0
1526527Sache			&& errno != EINTR)
1536527Sache			goto failure;
15443967Sache		} while
1556527Sache		    (errno == EINTR);
1566527Sache
15718950Sache		/*
15887243Smarkm		 * Solaris lets users override either dimension with an
15918950Sache		 * environment variable.
16018950Sache		 */
16118950Sache		if (*linep <= 0)
16218950Sache		    *linep = WINSIZE_ROWS(size);
1636527Sache		if (*colp <= 0)
16443940Sache		    *colp = WINSIZE_COLS(size);
16518950Sache	    }
1666527Sache	    /* FALLTHRU */
1676527Sache	  failure:;
16818950Sache	}
1696527Sache#endif /* HAVE_SIZECHANGE */
17043940Sache
1716527Sache	/* if we can't get dynamic info about the size, use static */
1726527Sache	if (*linep <= 0) {
1736527Sache	    *linep = (int) lines;
17418950Sache	}
1756527Sache	if (*colp <= 0) {
17643940Sache	    *colp = (int) columns;
1776527Sache	}
1786527Sache
1796527Sache	/* the ultimate fallback, assume fixed 24x80 size */
18018950Sache	if (*linep <= 0) {
1816527Sache	    *linep = 24;
18243940Sache	}
1836527Sache	if (*colp <= 0) {
1846527Sache	    *colp = 80;
1856527Sache	}
18618950Sache
1876527Sache	/*
18843940Sache	 * Put the derived values back in the screen-size caps, so
1896527Sache	 * tigetnum() and tgetnum() will do the right thing.
1906527Sache	 */
1916527Sache	lines = (short) (*linep);
19218950Sache	columns = (short) (*colp);
1936527Sache    }
19443940Sache
1956527Sache    T(("screen size is %dx%d", *linep, *colp));
1966527Sache
1976527Sache    if (VALID_NUMERIC(init_tabs))
19818950Sache	TABSIZE = (int) init_tabs;
1996527Sache    else
20043940Sache	TABSIZE = 8;
2016527Sache    T(("TABSIZE = %d", TABSIZE));
2026527Sache
2036527Sache}
20418950Sache
2056527Sache#if USE_SIZECHANGE
20643940SacheNCURSES_EXPORT(void)
2076527Sache_nc_update_screensize(void)
2086527Sache{
2096527Sache    int my_lines, my_cols;
21018950Sache
21187243Smarkm    _nc_get_screensize(&my_lines, &my_cols);
21218950Sache    if (SP != 0 && SP->_resize != 0)
21318950Sache	SP->_resize(my_lines, my_cols);
2146527Sache}
21518950Sache#endif
21687243Smarkm
21718950Sache/****************************************************************************
21818950Sache *
21918950Sache * Terminal setup
22018950Sache *
2216527Sache ****************************************************************************/
2226527Sache
2236527Sache#define ret_error(code, fmt, arg)	if (errret) {\
2246527Sache					    *errret = code;\
2256527Sache					    returnCode(ERR);\
22618950Sache					} else {\
2276527Sache					    fprintf(stderr, fmt, arg);\
2286527Sache					    exit(EXIT_FAILURE);\
2296527Sache					}
2306527Sache
2316527Sache#define ret_error0(code, msg)		if (errret) {\
23218950Sache					    *errret = code;\
23318950Sache					    returnCode(ERR);\
23443940Sache					} else {\
23518950Sache					    fprintf(stderr, msg);\
23618950Sache					    exit(EXIT_FAILURE);\
23718950Sache					}
23818950Sache
23918950Sache#if USE_DATABASE || USE_TERMCAP
24043940Sachestatic int
24118950Sachegrab_entry(const char *const tn, TERMTYPE * const tp)
24218950Sache/* return 1 if entry found, 0 if not found, -1 if database not accessible */
24318950Sache{
2446527Sache    char filename[PATH_MAX];
24518950Sache    int status;
24618950Sache
24718950Sache    /*
24818950Sache     * $TERM shouldn't contain pathname delimiters.
24918950Sache     */
25018950Sache    if (strchr(tn, '/'))
25118950Sache	return 0;
2526527Sache
2536527Sache#if USE_DATABASE
2546527Sache    if ((status = _nc_read_entry(tn, filename, tp)) != 1) {
25518950Sache
25618950Sache#if !PURE_TERMINFO
2576527Sache	/*
25818950Sache	 * Try falling back on the termcap file.
25918950Sache	 * Note:  allowing this call links the entire terminfo/termcap
2606527Sache	 * compiler into the startup code.  It's preferable to build a
2616527Sache	 * real terminfo database and use that.
26218950Sache	 */
26318950Sache	status = _nc_read_termcap_entry(tn, tp);
26418950Sache#endif /* PURE_TERMINFO */
26518950Sache
26618950Sache    }
26718950Sache#else
26818950Sache    status = _nc_read_termcap_entry(tn, tp);
26918950Sache#endif
2706527Sache
2716527Sache    /*
2726527Sache     * If we have an entry, force all of the cancelled strings to null
27318950Sache     * pointers so we don't have to test them in the rest of the library.
27418950Sache     * (The terminfo compiler bypasses this logic, since it must know if
27518950Sache     * a string is cancelled, for merging entries).
27618950Sache     */
2776527Sache    if (status == 1) {
2786527Sache	int n;
2796527Sache	for_each_boolean(n, tp) {
2806527Sache	    if (!VALID_BOOLEAN(tp->Booleans[n]))
2816527Sache		tp->Booleans[n] = FALSE;
2826527Sache	}
2836527Sache	for_each_string(n, tp) {
2846527Sache	    if (tp->Strings[n] == CANCELLED_STRING)
2856527Sache		tp->Strings[n] = ABSENT_STRING;
2866527Sache	}
2876527Sache    }
288    return (status);
289}
290#endif
291
292NCURSES_EXPORT_VAR(char) ttytype[NAMESIZE] = "";
293
294/*
295 *	setupterm(termname, Filedes, errret)
296 *
297 *	Find and read the appropriate object file for the terminal
298 *	Make cur_term point to the structure.
299 *
300 */
301
302NCURSES_EXPORT(int)
303setupterm(NCURSES_CONST char *tname, int Filedes, int *errret)
304{
305    struct term *term_ptr;
306    int status;
307
308    T((T_CALLED("setupterm(%s,%d,%p)"), _nc_visbuf(tname), Filedes, errret));
309
310    if (tname == 0) {
311	tname = getenv("TERM");
312	if (tname == 0 || *tname == '\0') {
313	    ret_error0(-1, "TERM environment variable not set.\n");
314	}
315    }
316    if (strlen(tname) > MAX_NAME_SIZE) {
317	ret_error(-1, "TERM environment must be <= %d characters.\n",
318		  MAX_NAME_SIZE);
319    }
320
321    T(("your terminal name is %s", tname));
322
323    term_ptr = typeCalloc(TERMINAL, 1);
324
325    if (term_ptr == 0) {
326	ret_error0(-1, "Not enough memory to create terminal structure.\n");
327    }
328#if USE_DATABASE || USE_TERMCAP
329    status = grab_entry(tname, &term_ptr->type);
330#else
331    status = 0;
332#endif
333
334    /* try fallback list if entry on disk */
335    if (status != 1) {
336	const TERMTYPE *fallback = _nc_fallback(tname);
337
338	if (fallback) {
339	    term_ptr->type = *fallback;
340	    status = 1;
341	}
342    }
343
344    if (status == -1) {
345	ret_error0(-1, "terminals database is inaccessible\n");
346    } else if (status == 0) {
347	ret_error(0, "'%s': unknown terminal type.\n", tname);
348    }
349
350    /*
351     * Improve on SVr4 curses.  If an application mixes curses and termcap
352     * calls, it may call both initscr and tgetent.  This is not really a
353     * good thing to do, but can happen if someone tries using ncurses with
354     * the readline library.  The problem we are fixing is that when
355     * tgetent calls setupterm, the resulting Ottyb struct in cur_term is
356     * zeroed.  A subsequent call to endwin uses the zeroed terminal
357     * settings rather than the ones saved in initscr.  So we check if
358     * cur_term appears to contain terminal settings for the same output
359     * file as our current call - and copy those terminal settings.  (SVr4
360     * curses does not do this, however applications that are working
361     * around the problem will still work properly with this feature).
362     */
363    if (cur_term != 0) {
364	if (cur_term->Filedes == Filedes)
365	    term_ptr->Ottyb = cur_term->Ottyb;
366    }
367
368    set_curterm(term_ptr);
369
370    if (command_character && getenv("CC"))
371	do_prototype();
372
373    strncpy(ttytype, cur_term->type.term_names, NAMESIZE - 1);
374    ttytype[NAMESIZE - 1] = '\0';
375
376    /*
377     * Allow output redirection.  This is what SVr3 does.
378     * If stdout is directed to a file, screen updates go
379     * to standard error.
380     */
381    if (Filedes == STDOUT_FILENO && !isatty(Filedes))
382	Filedes = STDERR_FILENO;
383    cur_term->Filedes = Filedes;
384
385    _nc_get_screensize(&LINES, &COLS);
386
387    if (errret)
388	*errret = 1;
389
390    T((T_CREATE("screen %s %dx%d"), tname, LINES, COLS));
391
392    if (generic_type) {
393	ret_error(0, "'%s': I need something more specific.\n", tname);
394    }
395    if (hard_copy) {
396	ret_error(1, "'%s': I can't handle hardcopy terminals.\n", tname);
397    }
398    returnCode(OK);
399}
400
401/*
402**	do_prototype()
403**
404**	Take the real command character out of the CC environment variable
405**	and substitute it in for the prototype given in 'command_character'.
406**
407*/
408
409static void
410do_prototype(void)
411{
412    int i;
413    char CC;
414    char proto;
415    char *tmp;
416
417    tmp = getenv("CC");
418    CC = *tmp;
419    proto = *command_character;
420
421    for_each_string(i, &(cur_term->type)) {
422	for (tmp = cur_term->type.Strings[i]; *tmp; tmp++) {
423	    if (*tmp == proto)
424		*tmp = CC;
425	}
426    }
427}
428