GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libcurses/tinfo/lib_setup.c Lines: 71 149 47.7 %
Date: 2017-11-13 Branches: 41 162 25.3 %

Line Branch Exec Source
1
/* $OpenBSD: lib_setup.c,v 1.12 2010/01/12 23:22:06 nicm Exp $ */
2
3
/****************************************************************************
4
 * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc.              *
5
 *                                                                          *
6
 * Permission is hereby granted, free of charge, to any person obtaining a  *
7
 * copy of this software and associated documentation files (the            *
8
 * "Software"), to deal in the Software without restriction, including      *
9
 * without limitation the rights to use, copy, modify, merge, publish,      *
10
 * distribute, distribute with modifications, sublicense, and/or sell       *
11
 * copies of the Software, and to permit persons to whom the Software is    *
12
 * furnished to do so, subject to the following conditions:                 *
13
 *                                                                          *
14
 * The above copyright notice and this permission notice shall be included  *
15
 * in all copies or substantial portions of the Software.                   *
16
 *                                                                          *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
18
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
19
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
20
 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
21
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
22
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
23
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
24
 *                                                                          *
25
 * Except as contained in this notice, the name(s) of the above copyright   *
26
 * holders shall not be used in advertising or otherwise to promote the     *
27
 * sale, use or other dealings in this Software without prior written       *
28
 * authorization.                                                           *
29
 ****************************************************************************/
30
31
/****************************************************************************
32
 *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
33
 *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
34
 *     and: Thomas E. Dickey                        1996-on                 *
35
 ****************************************************************************/
36
37
/*
38
 * Terminal setup routines common to termcap and terminfo:
39
 *
40
 *		use_env(bool)
41
 *		setupterm(char *, int, int *)
42
 */
43
44
#include <curses.priv.h>
45
#include <tic.h>		/* for MAX_NAME_SIZE */
46
#include <term_entry.h>
47
48
#if SVR4_TERMIO && !defined(_POSIX_SOURCE)
49
#define _POSIX_SOURCE
50
#endif
51
52
#if HAVE_LOCALE_H
53
#include <locale.h>
54
#endif
55
56
#include <term.h>		/* lines, columns, cur_term */
57
58
MODULE_ID("$Id: lib_setup.c,v 1.12 2010/01/12 23:22:06 nicm Exp $")
59
60
/****************************************************************************
61
 *
62
 * Terminal size computation
63
 *
64
 ****************************************************************************/
65
66
#if HAVE_SIZECHANGE
67
# if !defined(sun) || !TERMIOS
68
#  if HAVE_SYS_IOCTL_H
69
#   include <sys/ioctl.h>
70
#  endif
71
# endif
72
#endif
73
74
#if NEED_PTEM_H
75
 /* On SCO, they neglected to define struct winsize in termios.h -- it's only
76
  * in termio.h and ptem.h (the former conflicts with other definitions).
77
  */
78
# include <sys/stream.h>
79
# include <sys/ptem.h>
80
#endif
81
82
#if HAVE_LANGINFO_CODESET
83
#include <langinfo.h>
84
#endif
85
86
/*
87
 * SCO defines TIOCGSIZE and the corresponding struct.  Other systems (SunOS,
88
 * Solaris, IRIX) define TIOCGWINSZ and struct winsize.
89
 */
90
#ifdef TIOCGSIZE
91
# define IOCTL_WINSIZE TIOCGSIZE
92
# define STRUCT_WINSIZE struct ttysize
93
# define WINSIZE_ROWS(n) (int)n.ts_lines
94
# define WINSIZE_COLS(n) (int)n.ts_cols
95
#else
96
# ifdef TIOCGWINSZ
97
#  define IOCTL_WINSIZE TIOCGWINSZ
98
#  define STRUCT_WINSIZE struct winsize
99
#  define WINSIZE_ROWS(n) (int)n.ws_row
100
#  define WINSIZE_COLS(n) (int)n.ws_col
101
# endif
102
#endif
103
104
/*
105
 * Reduce explicit use of "cur_term" global variable.
106
 */
107
#undef CUR
108
#define CUR termp->type.
109
110
/*
111
 * Wrap global variables in this module.
112
 */
113
#if USE_REENTRANT
114
NCURSES_EXPORT(char *)
115
NCURSES_PUBLIC_VAR(ttytype) (void)
116
{
117
    static char empty[] = "";
118
    return cur_term ? cur_term->type.term_names : empty;
119
}
120
NCURSES_EXPORT(int *)
121
_nc_ptr_Lines(void)
122
{
123
    return ptrLines();
124
}
125
NCURSES_EXPORT(int)
126
NCURSES_PUBLIC_VAR(LINES) (void)
127
{
128
    return *_nc_ptr_Lines();
129
}
130
NCURSES_EXPORT(int *)
131
_nc_ptr_Cols(void)
132
{
133
    return ptrCols();
134
}
135
NCURSES_EXPORT(int)
136
NCURSES_PUBLIC_VAR(COLS) (void)
137
{
138
    return *_nc_ptr_Cols();
139
}
140
NCURSES_EXPORT(int)
141
NCURSES_PUBLIC_VAR(TABSIZE) (void)
142
{
143
    return SP ? SP->_TABSIZE : 8;
144
}
145
#else
146
NCURSES_EXPORT_VAR(char) ttytype[NAMESIZE] = "";
147
NCURSES_EXPORT_VAR(int) LINES = 0;
148
NCURSES_EXPORT_VAR(int) COLS = 0;
149
NCURSES_EXPORT_VAR(int) TABSIZE = 0;
150
#endif
151
152
#if NCURSES_EXT_FUNCS
153
NCURSES_EXPORT(int)
154
set_tabsize(int value)
155
{
156
    int code = OK;
157
#if USE_REENTRANT
158
    if (SP) {
159
	SP->_TABSIZE = value;
160
    } else {
161
	code = ERR;
162
    }
163
#else
164
    TABSIZE = value;
165
#endif
166
    return code;
167
}
168
#endif
169
170
#if USE_SIGWINCH
171
/*
172
 * If we have a pending SIGWINCH, set the flag in each screen.
173
 */
174
NCURSES_EXPORT(int)
175
_nc_handle_sigwinch(SCREEN *sp)
176
{
177
    SCREEN *scan;
178
179
    if (_nc_globals.have_sigwinch) {
180
	_nc_globals.have_sigwinch = 0;
181
182
	for (each_screen(scan)) {
183
	    scan->_sig_winch = TRUE;
184
	}
185
    }
186
187
    return (sp ? sp->_sig_winch : 0);
188
}
189
190
#endif
191
192
NCURSES_EXPORT(void)
193
use_env(bool f)
194
{
195
    T((T_CALLED("use_env()")));
196
    _nc_prescreen.use_env = f;
197
    returnVoid;
198
}
199
200
NCURSES_EXPORT(void)
201
_nc_get_screensize(SCREEN *sp, int *linep, int *colp)
202
/* Obtain lines/columns values from the environment and/or terminfo entry */
203
{
204
6
    TERMINAL *termp = cur_term;
205
    int my_tabsize;
206
207
    /* figure out the size of the screen */
208
    T(("screen size: terminfo lines = %d columns = %d", lines, columns));
209
210
3
    if (!_nc_prescreen.use_env) {
211
	*linep = (int) lines;
212
	*colp = (int) columns;
213
    } else {			/* usually want to query LINES and COLUMNS from environment */
214
	int value;
215
216
3
	*linep = *colp = 0;
217
218
	/* first, look for environment variables */
219
3
	if ((value = _nc_getenv_num("LINES")) > 0) {
220
	    *linep = value;
221
	}
222
3
	if ((value = _nc_getenv_num("COLUMNS")) > 0) {
223
	    *colp = value;
224
	}
225
	T(("screen size: environment LINES = %d COLUMNS = %d", *linep, *colp));
226
227
#ifdef __EMX__
228
	if (*linep <= 0 || *colp <= 0) {
229
	    int screendata[2];
230
	    _scrsize(screendata);
231
	    *colp = screendata[0];
232
	    *linep = screendata[1];
233
	    T(("EMX screen size: environment LINES = %d COLUMNS = %d",
234
	       *linep, *colp));
235
	}
236
#endif
237
#if HAVE_SIZECHANGE
238
	/* if that didn't work, maybe we can try asking the OS */
239

3
	if (*linep <= 0 || *colp <= 0) {
240
3
	    if (isatty(cur_term->Filedes)) {
241
2
		STRUCT_WINSIZE size;
242
243
2
		errno = 0;
244
2
		do {
245
2
		    if (ioctl(cur_term->Filedes, IOCTL_WINSIZE, &size) < 0
246
2
			&& errno != EINTR)
247
			goto failure;
248
2
		} while
249
2
		    (errno == EINTR);
250
251
		/*
252
		 * Solaris lets users override either dimension with an
253
		 * environment variable.
254
		 */
255
2
		if (*linep <= 0)
256

6
		    *linep = (sp != 0 && sp->_filtered) ? 1 : WINSIZE_ROWS(size);
257
2
		if (*colp <= 0)
258
2
		    *colp = WINSIZE_COLS(size);
259
4
	    }
260
	    /* FALLTHRU */
261
	  failure:;
262
	}
263
#endif /* HAVE_SIZECHANGE */
264
265
	/* if we can't get dynamic info about the size, use static */
266
3
	if (*linep <= 0) {
267
1
	    *linep = (int) lines;
268
1
	}
269
3
	if (*colp <= 0) {
270
1
	    *colp = (int) columns;
271
1
	}
272
273
	/* the ultimate fallback, assume fixed 24x80 size */
274
3
	if (*linep <= 0) {
275
	    *linep = 24;
276
	}
277
3
	if (*colp <= 0) {
278
	    *colp = 80;
279
	}
280
281
	/*
282
	 * Put the derived values back in the screen-size caps, so
283
	 * tigetnum() and tgetnum() will do the right thing.
284
	 */
285
3
	lines = (short) (*linep);
286
3
	columns = (short) (*colp);
287
    }
288
289
    T(("screen size is %dx%d", *linep, *colp));
290
291
3
    if (VALID_NUMERIC(init_tabs))
292
3
	my_tabsize = (int) init_tabs;
293
    else
294
	my_tabsize = 8;
295
296
#if USE_REENTRANT
297
    if (sp != 0)
298
	sp->_TABSIZE = my_tabsize;
299
#else
300
3
    TABSIZE = my_tabsize;
301
#endif
302
    T(("TABSIZE = %d", TABSIZE));
303
3
}
304
305
#if USE_SIZECHANGE
306
NCURSES_EXPORT(void)
307
_nc_update_screensize(SCREEN *sp)
308
{
309
    TERMINAL *termp = cur_term;
310
    int old_lines = lines;
311
    int new_lines;
312
    int old_cols = columns;
313
    int new_cols;
314
315
    _nc_get_screensize(sp, &new_lines, &new_cols);
316
317
    /*
318
     * See is_term_resized() and resizeterm().
319
     * We're doing it this way because those functions belong to the upper
320
     * ncurses library, while this resides in the lower terminfo library.
321
     */
322
    if (sp != 0
323
	&& sp->_resize != 0) {
324
	if ((new_lines != old_lines) || (new_cols != old_cols))
325
	    sp->_resize(new_lines, new_cols);
326
	sp->_sig_winch = FALSE;
327
    }
328
}
329
#endif
330
331
/****************************************************************************
332
 *
333
 * Terminal setup
334
 *
335
 ****************************************************************************/
336
337
#define ret_error(code, fmt, arg)	if (errret) {\
338
					    *errret = code;\
339
					    returnCode(ERR);\
340
					} else {\
341
					    fprintf(stderr, fmt, arg);\
342
					    exit(EXIT_FAILURE);\
343
					}
344
345
#define ret_error0(code, msg)		if (errret) {\
346
					    *errret = code;\
347
					    returnCode(ERR);\
348
					} else {\
349
					    fprintf(stderr, msg);\
350
					    exit(EXIT_FAILURE);\
351
					}
352
353
#if USE_DATABASE || USE_TERMCAP
354
/*
355
 * Return 1 if entry found, 0 if not found, -1 if database not accessible,
356
 * just like tgetent().
357
 */
358
static int
359
grab_entry(const char *const tn, TERMTYPE *const tp)
360
{
361
6
    char filename[PATH_MAX];
362
3
    int status = _nc_read_entry(tn, filename, tp);
363
364
    /*
365
     * If we have an entry, force all of the cancelled strings to null
366
     * pointers so we don't have to test them in the rest of the library.
367
     * (The terminfo compiler bypasses this logic, since it must know if
368
     * a string is cancelled, for merging entries).
369
     */
370
3
    if (status == TGETENT_YES) {
371
	unsigned n;
372
288
	for_each_boolean(n, tp) {
373
141
	    if (!VALID_BOOLEAN(tp->Booleans[n]))
374
		tp->Booleans[n] = FALSE;
375
	}
376
2532
	for_each_string(n, tp) {
377
1263
	    if (tp->Strings[n] == CANCELLED_STRING)
378
		tp->Strings[n] = ABSENT_STRING;
379
	}
380
3
    }
381
3
    return (status);
382
3
}
383
#endif
384
385
/*
386
**	do_prototype()
387
**
388
**	Take the real command character out of the CC environment variable
389
**	and substitute it in for the prototype given in 'command_character'.
390
*/
391
static void
392
do_prototype(TERMINAL * termp)
393
{
394
    unsigned i;
395
    char CC;
396
    char proto;
397
    char *tmp;
398
399
    if ((tmp = getenv("CC")) != 0) {
400
	if ((CC = *tmp) != 0) {
401
	    proto = *command_character;
402
403
	    for_each_string(i, &(termp->type)) {
404
		for (tmp = termp->type.Strings[i]; *tmp; tmp++) {
405
		    if (*tmp == proto)
406
			*tmp = CC;
407
		}
408
	    }
409
	}
410
    }
411
}
412
413
/*
414
 * Find the locale which is in effect.
415
 */
416
NCURSES_EXPORT(char *)
417
_nc_get_locale(void)
418
{
419
    char *env;
420
#if HAVE_LOCALE_H
421
    /*
422
     * This is preferable to using getenv() since it ensures that we are using
423
     * the locale which was actually initialized by the application.
424
     */
425
    env = setlocale(LC_CTYPE, 0);
426
#else
427
    if (((env = getenv("LC_ALL")) != 0 && *env != '\0')
428
	|| ((env = getenv("LC_CTYPE")) != 0 && *env != '\0')
429
	|| ((env = getenv("LANG")) != 0 && *env != '\0')) {
430
	;
431
    }
432
#endif
433
    T(("_nc_get_locale %s", _nc_visbuf(env)));
434
    return env;
435
}
436
437
/*
438
 * Check if we are running in a UTF-8 locale.
439
 */
440
NCURSES_EXPORT(int)
441
_nc_unicode_locale(void)
442
{
443
    int result = 0;
444
#if HAVE_LANGINFO_CODESET
445
    char *env = nl_langinfo(CODESET);
446
    result = !strcmp(env, "UTF-8");
447
    T(("_nc_unicode_locale(%s) ->%d", env, result));
448
#else
449
    char *env = _nc_get_locale();
450
    if (env != 0) {
451
	if (strstr(env, ".UTF-8") != 0) {
452
	    result = 1;
453
	    T(("_nc_unicode_locale(%s) ->%d", env, result));
454
	}
455
    }
456
#endif
457
    return result;
458
}
459
460
#define CONTROL_N(s) ((s) != 0 && strstr(s, "\016") != 0)
461
#define CONTROL_O(s) ((s) != 0 && strstr(s, "\017") != 0)
462
463
/*
464
 * Check for known broken cases where a UTF-8 locale breaks the alternate
465
 * character set.
466
 */
467
NCURSES_EXPORT(int)
468
_nc_locale_breaks_acs(TERMINAL * termp)
469
{
470
    char *env;
471
472
    if ((env = getenv("NCURSES_NO_UTF8_ACS")) != 0) {
473
	return atoi(env);
474
    } else if ((env = getenv("TERM")) != 0) {
475
	if (strstr(env, "linux"))
476
	    return 1;		/* always broken */
477
	if (strstr(env, "screen") != 0
478
	    && ((env = getenv("TERMCAP")) != 0
479
		&& strstr(env, "screen") != 0)
480
	    && strstr(env, "hhII00") != 0) {
481
	    if (CONTROL_N(enter_alt_charset_mode) ||
482
		CONTROL_O(enter_alt_charset_mode) ||
483
		CONTROL_N(set_attributes) ||
484
		CONTROL_O(set_attributes))
485
		return 1;
486
	}
487
    }
488
    return 0;
489
}
490
491
/*
492
 * This entrypoint is called from tgetent() to allow a special case of reusing
493
 * the same TERMINAL data (see comment).
494
 */
495
NCURSES_EXPORT(int)
496
_nc_setupterm(NCURSES_CONST char *tname, int Filedes, int *errret, bool reuse)
497
{
498
    TERMINAL *termp;
499
    int status;
500
501
    START_TRACE();
502
    T((T_CALLED("setupterm(%s,%d,%p)"), _nc_visbuf(tname), Filedes, errret));
503
504
3
    if (tname == 0) {
505
	tname = getenv("TERM");
506
	if (tname == 0 || *tname == '\0') {
507
	    ret_error0(TGETENT_ERR, "TERM environment variable not set.\n");
508
	}
509
    }
510
511
3
    if (strlen(tname) > MAX_NAME_SIZE) {
512
	ret_error(TGETENT_ERR,
513
		  "TERM environment must be <= %d characters.\n",
514
		  MAX_NAME_SIZE);
515
    }
516
517
    T(("your terminal name is %s", tname));
518
519
    /*
520
     * Allow output redirection.  This is what SVr3 does.  If stdout is
521
     * directed to a file, screen updates go to standard error.
522
     */
523

6
    if (Filedes == STDOUT_FILENO && !isatty(Filedes))
524
1
	Filedes = STDERR_FILENO;
525
526
    /*
527
     * Check if we have already initialized to use this terminal.  If so, we
528
     * do not need to re-read the terminfo entry, or obtain TTY settings.
529
     *
530
     * This is an improvement on SVr4 curses.  If an application mixes curses
531
     * and termcap calls, it may call both initscr and tgetent.  This is not
532
     * really a good thing to do, but can happen if someone tries using ncurses
533
     * with the readline library.  The problem we are fixing is that when
534
     * tgetent calls setupterm, the resulting Ottyb struct in cur_term is
535
     * zeroed.  A subsequent call to endwin uses the zeroed terminal settings
536
     * rather than the ones saved in initscr.  So we check if cur_term appears
537
     * to contain terminal settings for the same output file as our current
538
     * call - and copy those terminal settings.  (SVr4 curses does not do this,
539
     * however applications that are working around the problem will still work
540
     * properly with this feature).
541
     */
542
    if (reuse
543
6
	&& (termp = cur_term) != 0
544
3
	&& termp->Filedes == Filedes
545
	&& termp->_termname != 0
546
	&& !strcmp(termp->_termname, tname)
547
	&& _nc_name_match(termp->type.term_names, tname, "|")) {
548
	T(("reusing existing terminal information and mode-settings"));
549
    } else {
550
551
3
	termp = typeCalloc(TERMINAL, 1);
552
553
3
	if (termp == 0) {
554
	    ret_error0(TGETENT_ERR,
555
		       "Not enough memory to create terminal structure.\n");
556
	}
557
#if USE_DATABASE || USE_TERMCAP
558
3
	status = grab_entry(tname, &termp->type);
559
#else
560
	status = TGETENT_NO;
561
#endif
562
563
	/* try fallback list if entry on disk */
564
3
	if (status != TGETENT_YES) {
565
	    const TERMTYPE *fallback = _nc_fallback(tname);
566
567
	    if (fallback) {
568
		termp->type = *fallback;
569
		status = TGETENT_YES;
570
	    }
571
	}
572
573
3
	if (status != TGETENT_YES) {
574
	    del_curterm(termp);
575
	    if (status == TGETENT_ERR) {
576
		ret_error0(status, "terminals database is inaccessible\n");
577
	    } else if (status == TGETENT_NO) {
578
		ret_error(status, "'%s': unknown terminal type.\n", tname);
579
	    }
580
	}
581
#if !USE_REENTRANT
582
3
	strncpy(ttytype, termp->type.term_names, NAMESIZE - 1);
583
3
	ttytype[NAMESIZE - 1] = '\0';
584
#endif
585
586
3
	termp->Filedes = Filedes;
587
3
	termp->_termname = strdup(tname);
588
589
3
	set_curterm(termp);
590
591

3
	if (command_character && getenv("CC"))
592
	    do_prototype(termp);
593
594
	/*
595
	 * If an application calls setupterm() rather than initscr() or
596
	 * newterm(), we will not have the def_prog_mode() call in
597
	 * _nc_setupscreen().  Do it now anyway, so we can initialize the
598
	 * baudrate.
599
	 */
600
3
	if (isatty(Filedes)) {
601
2
	    def_prog_mode();
602
2
	    baudrate();
603
2
	}
604
    }
605
606
    /*
607
     * We should always check the screensize, just in case.
608
     */
609
3
    _nc_get_screensize(SP, ptrLines(), ptrCols());
610
611
3
    if (errret)
612
3
	*errret = TGETENT_YES;
613
614
3
    if (generic_type) {
615
	ret_error(TGETENT_NO, "'%s': I need something more specific.\n", tname);
616
    }
617
3
    if (hard_copy) {
618
	ret_error(TGETENT_YES, "'%s': I can't handle hardcopy terminals.\n", tname);
619
    }
620
3
    returnCode(OK);
621
3
}
622
623
/*
624
 *	setupterm(termname, Filedes, errret)
625
 *
626
 *	Find and read the appropriate object file for the terminal
627
 *	Make cur_term point to the structure.
628
 */
629
NCURSES_EXPORT(int)
630
setupterm(NCURSES_CONST char *tname, int Filedes, int *errret)
631
{
632
    return _nc_setupterm(tname, Filedes, errret, FALSE);
633
}