eb8dff76e99a3cdb09582f1059736fd4d162d3fd
[ncurses.git] / ncurses / tinfo / lib_setup.c
1 /****************************************************************************
2  * Copyright (c) 1998-2008,2009 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  *     and: Thomas E. Dickey                        1996-on                 *
33  *     and: Juergen Pfeifer                         2009                    *
34  ****************************************************************************/
35
36 /*
37  * Terminal setup routines common to termcap and terminfo:
38  *
39  *              use_env(bool)
40  *              setupterm(char *, int, int *)
41  */
42
43 #include <curses.priv.h>
44 #include <tic.h>                /* for MAX_NAME_SIZE */
45 #include <term_entry.h>
46
47 #if SVR4_TERMIO && !defined(_POSIX_SOURCE)
48 #define _POSIX_SOURCE
49 #endif
50
51 #if HAVE_LOCALE_H
52 #include <locale.h>
53 #endif
54
55 #include <term.h>               /* lines, columns, cur_term */
56
57 MODULE_ID("$Id: lib_setup.c,v 1.113 2009/02/14 22:21:40 tom Exp $")
58
59 /****************************************************************************
60  *
61  * Terminal size computation
62  *
63  ****************************************************************************/
64
65 #if HAVE_SIZECHANGE
66 # if !defined(sun) || !TERMIOS
67 #  if HAVE_SYS_IOCTL_H
68 #   include <sys/ioctl.h>
69 #  endif
70 # endif
71 #endif
72
73 #if NEED_PTEM_H
74  /* On SCO, they neglected to define struct winsize in termios.h -- it's only
75   * in termio.h and ptem.h (the former conflicts with other definitions).
76   */
77 # include <sys/stream.h>
78 # include <sys/ptem.h>
79 #endif
80
81 #if HAVE_LANGINFO_CODESET
82 #include <langinfo.h>
83 #endif
84
85 /*
86  * SCO defines TIOCGSIZE and the corresponding struct.  Other systems (SunOS,
87  * Solaris, IRIX) define TIOCGWINSZ and struct winsize.
88  */
89 #ifdef TIOCGSIZE
90 # define IOCTL_WINSIZE TIOCGSIZE
91 # define STRUCT_WINSIZE struct ttysize
92 # define WINSIZE_ROWS(n) (int)n.ts_lines
93 # define WINSIZE_COLS(n) (int)n.ts_cols
94 #else
95 # ifdef TIOCGWINSZ
96 #  define IOCTL_WINSIZE TIOCGWINSZ
97 #  define STRUCT_WINSIZE struct winsize
98 #  define WINSIZE_ROWS(n) (int)n.ws_row
99 #  define WINSIZE_COLS(n) (int)n.ws_col
100 # endif
101 #endif
102
103 /*
104  * Reduce explicit use of "cur_term" global variable.
105  */
106 #undef CUR
107 #define CUR termp->type.
108
109 /*
110  * Wrap global variables in this module.
111  */
112 #if USE_REENTRANT
113 NCURSES_EXPORT(char *)
114 NCURSES_PUBLIC_VAR(ttytype) (void)
115 {
116     static char empty[] = "";
117     return cur_term ? cur_term->type.term_names : empty;
118 }
119 NCURSES_EXPORT(int *)
120 _nc_ptr_Lines(void)
121 {
122     return ptrLines();
123 }
124 NCURSES_EXPORT(int)
125 NCURSES_PUBLIC_VAR(LINES) (void)
126 {
127     return *_nc_ptr_Lines();
128 }
129 NCURSES_EXPORT(int *)
130 _nc_ptr_Cols(void)
131 {
132     return ptrCols();
133 }
134 NCURSES_EXPORT(int)
135 NCURSES_PUBLIC_VAR(COLS) (void)
136 {
137     return *_nc_ptr_Cols();
138 }
139 NCURSES_EXPORT(int)
140 NCURSES_PUBLIC_VAR(TABSIZE) (void)
141 {
142     return SP ? SP->_TABSIZE : 8;
143 }
144 #else
145 NCURSES_EXPORT_VAR(char) ttytype[NAMESIZE] = "";
146 NCURSES_EXPORT_VAR(int) LINES = 0;
147 NCURSES_EXPORT_VAR(int) COLS = 0;
148 NCURSES_EXPORT_VAR(int) TABSIZE = 0;
149 #endif
150
151 #if NCURSES_EXT_FUNCS
152 NCURSES_EXPORT(int)
153 NCURSES_SP_NAME(set_tabsize) (NCURSES_SP_DCLx int value)
154 {
155     int code = OK;
156 #if USE_REENTRANT
157     if (SP_PARM) {
158         SP_PARM->_TABSIZE = value;
159     } else {
160         code = ERR;
161     }
162 #else
163     (void) SP_PARM;
164     TABSIZE = value;
165 #endif
166     return code;
167 }
168
169 #if NCURSES_SP_FUNCS
170 NCURSES_EXPORT(int)
171 set_tabsize(int value)
172 {
173     return NCURSES_SP_NAME(set_tabsize) (CURRENT_SCREEN, value);
174 }
175 #endif
176 #endif /* NCURSES_EXT_FUNCS */
177
178 #if USE_SIGWINCH
179 /*
180  * If we have a pending SIGWINCH, set the flag in each screen.
181  */
182 NCURSES_EXPORT(int)
183 _nc_handle_sigwinch(SCREEN *sp)
184 {
185     SCREEN *scan;
186
187     if (_nc_globals.have_sigwinch) {
188         _nc_globals.have_sigwinch = 0;
189
190         for (each_screen(scan)) {
191             scan->_sig_winch = TRUE;
192         }
193     }
194
195     return (sp ? sp->_sig_winch : 0);
196 }
197
198 #endif
199
200 NCURSES_EXPORT(void)
201 NCURSES_SP_NAME(use_env) (NCURSES_SP_DCLx bool f)
202 {
203     T((T_CALLED("use_env()")));
204     if (IsPreScreen(SP_PARM)) {
205         SP_PARM->_use_env = f;
206     } else {
207         _nc_prescreen.use_env = f;
208     }
209     returnVoid;
210 }
211
212 #if NCURSES_SP_FUNCS
213 NCURSES_EXPORT(void)
214 use_env(bool f)
215 {
216     NCURSES_SP_NAME(use_env) (CURRENT_SCREEN, f);
217 }
218 #endif
219
220 NCURSES_EXPORT(void)
221 _nc_get_screensize(SCREEN *sp, int *linep, int *colp)
222 /* Obtain lines/columns values from the environment and/or terminfo entry */
223 {
224     TERMINAL *termp = cur_term;
225     int my_tabsize;
226
227     /* figure out the size of the screen */
228     T(("screen size: terminfo lines = %d columns = %d", lines, columns));
229
230     if (!_nc_prescreen.use_env) {
231         *linep = (int) lines;
232         *colp = (int) columns;
233     } else {                    /* usually want to query LINES and COLUMNS from environment */
234         int value;
235
236         *linep = *colp = 0;
237
238         /* first, look for environment variables */
239         if ((value = _nc_getenv_num("LINES")) > 0) {
240             *linep = value;
241         }
242         if ((value = _nc_getenv_num("COLUMNS")) > 0) {
243             *colp = value;
244         }
245         T(("screen size: environment LINES = %d COLUMNS = %d", *linep, *colp));
246
247 #ifdef __EMX__
248         if (*linep <= 0 || *colp <= 0) {
249             int screendata[2];
250             _scrsize(screendata);
251             *colp = screendata[0];
252             *linep = screendata[1];
253             T(("EMX screen size: environment LINES = %d COLUMNS = %d",
254                *linep, *colp));
255         }
256 #endif
257 #if HAVE_SIZECHANGE
258         /* if that didn't work, maybe we can try asking the OS */
259         if (*linep <= 0 || *colp <= 0) {
260             if (isatty(cur_term->Filedes)) {
261                 STRUCT_WINSIZE size;
262
263                 errno = 0;
264                 do {
265                     if (ioctl(cur_term->Filedes, IOCTL_WINSIZE, &size) < 0
266                         && errno != EINTR)
267                         goto failure;
268                 } while
269                     (errno == EINTR);
270
271                 /*
272                  * Solaris lets users override either dimension with an
273                  * environment variable.
274                  */
275                 if (*linep <= 0)
276                     *linep = (sp != 0 && sp->_filtered) ? 1 : WINSIZE_ROWS(size);
277                 if (*colp <= 0)
278                     *colp = WINSIZE_COLS(size);
279             }
280             /* FALLTHRU */
281           failure:;
282         }
283 #endif /* HAVE_SIZECHANGE */
284
285         /* if we can't get dynamic info about the size, use static */
286         if (*linep <= 0) {
287             *linep = (int) lines;
288         }
289         if (*colp <= 0) {
290             *colp = (int) columns;
291         }
292
293         /* the ultimate fallback, assume fixed 24x80 size */
294         if (*linep <= 0) {
295             *linep = 24;
296         }
297         if (*colp <= 0) {
298             *colp = 80;
299         }
300
301         /*
302          * Put the derived values back in the screen-size caps, so
303          * tigetnum() and tgetnum() will do the right thing.
304          */
305         lines = (short) (*linep);
306         columns = (short) (*colp);
307     }
308
309     T(("screen size is %dx%d", *linep, *colp));
310
311     if (VALID_NUMERIC(init_tabs))
312         my_tabsize = (int) init_tabs;
313     else
314         my_tabsize = 8;
315
316 #if USE_REENTRANT
317     if (sp != 0)
318         sp->_TABSIZE = my_tabsize;
319 #else
320     TABSIZE = my_tabsize;
321 #endif
322     T(("TABSIZE = %d", TABSIZE));
323 }
324
325 #if USE_SIZECHANGE
326 NCURSES_EXPORT(void)
327 _nc_update_screensize(SCREEN *sp)
328 {
329     TERMINAL *termp = cur_term;
330     int old_lines = lines;
331     int new_lines;
332     int old_cols = columns;
333     int new_cols;
334
335     _nc_get_screensize(sp, &new_lines, &new_cols);
336
337     /*
338      * See is_term_resized() and resizeterm().
339      * We're doing it this way because those functions belong to the upper
340      * ncurses library, while this resides in the lower terminfo library.
341      */
342     if (sp != 0
343         && sp->_resize != 0) {
344         if ((new_lines != old_lines) || (new_cols != old_cols))
345             sp->_resize(new_lines, new_cols);
346         sp->_sig_winch = FALSE;
347     }
348 }
349 #endif
350
351 /****************************************************************************
352  *
353  * Terminal setup
354  *
355  ****************************************************************************/
356
357 #define ret_error(code, fmt, arg)       if (errret) {\
358                                             *errret = code;\
359                                             returnCode(ERR);\
360                                         } else {\
361                                             fprintf(stderr, fmt, arg);\
362                                             exit(EXIT_FAILURE);\
363                                         }
364
365 #define ret_error0(code, msg)           if (errret) {\
366                                             *errret = code;\
367                                             returnCode(ERR);\
368                                         } else {\
369                                             fprintf(stderr, msg);\
370                                             exit(EXIT_FAILURE);\
371                                         }
372
373 #if USE_DATABASE || USE_TERMCAP
374 /*
375  * Return 1 if entry found, 0 if not found, -1 if database not accessible,
376  * just like tgetent().
377  */
378 static int
379 grab_entry(const char *const tn, TERMTYPE *const tp)
380 {
381     char filename[PATH_MAX];
382     int status = _nc_read_entry(tn, filename, tp);
383
384     /*
385      * If we have an entry, force all of the cancelled strings to null
386      * pointers so we don't have to test them in the rest of the library.
387      * (The terminfo compiler bypasses this logic, since it must know if
388      * a string is cancelled, for merging entries).
389      */
390     if (status == TGETENT_YES) {
391         unsigned n;
392         for_each_boolean(n, tp) {
393             if (!VALID_BOOLEAN(tp->Booleans[n]))
394                 tp->Booleans[n] = FALSE;
395         }
396         for_each_string(n, tp) {
397             if (tp->Strings[n] == CANCELLED_STRING)
398                 tp->Strings[n] = ABSENT_STRING;
399         }
400     }
401     return (status);
402 }
403 #endif
404
405 /*
406 **      do_prototype()
407 **
408 **      Take the real command character out of the CC environment variable
409 **      and substitute it in for the prototype given in 'command_character'.
410 */
411 static void
412 do_prototype(TERMINAL * termp)
413 {
414     unsigned i;
415     char CC;
416     char proto;
417     char *tmp;
418
419     if ((tmp = getenv("CC")) != 0) {
420         if ((CC = *tmp) != 0) {
421             proto = *command_character;
422
423             for_each_string(i, &(termp->type)) {
424                 for (tmp = termp->type.Strings[i]; *tmp; tmp++) {
425                     if (*tmp == proto)
426                         *tmp = CC;
427                 }
428             }
429         }
430     }
431 }
432
433 /*
434  * Find the locale which is in effect.
435  */
436 NCURSES_EXPORT(char *)
437 _nc_get_locale(void)
438 {
439     char *env;
440 #if HAVE_LOCALE_H
441     /*
442      * This is preferable to using getenv() since it ensures that we are using
443      * the locale which was actually initialized by the application.
444      */
445     env = setlocale(LC_CTYPE, 0);
446 #else
447     if (((env = getenv("LC_ALL")) != 0 && *env != '\0')
448         || ((env = getenv("LC_CTYPE")) != 0 && *env != '\0')
449         || ((env = getenv("LANG")) != 0 && *env != '\0')) {
450         ;
451     }
452 #endif
453     T(("_nc_get_locale %s", _nc_visbuf(env)));
454     return env;
455 }
456
457 /*
458  * Check if we are running in a UTF-8 locale.
459  */
460 NCURSES_EXPORT(int)
461 _nc_unicode_locale(void)
462 {
463     int result = 0;
464 #if HAVE_LANGINFO_CODESET
465     char *env = nl_langinfo(CODESET);
466     result = !strcmp(env, "UTF-8");
467     T(("_nc_unicode_locale(%s) ->%d", env, result));
468 #else
469     char *env = _nc_get_locale();
470     if (env != 0) {
471         if (strstr(env, ".UTF-8") != 0) {
472             result = 1;
473             T(("_nc_unicode_locale(%s) ->%d", env, result));
474         }
475     }
476 #endif
477     return result;
478 }
479
480 #define CONTROL_N(s) ((s) != 0 && strstr(s, "\016") != 0)
481 #define CONTROL_O(s) ((s) != 0 && strstr(s, "\017") != 0)
482
483 /*
484  * Check for known broken cases where a UTF-8 locale breaks the alternate
485  * character set.
486  */
487 NCURSES_EXPORT(int)
488 _nc_locale_breaks_acs(TERMINAL * termp)
489 {
490     char *env;
491
492     if ((env = getenv("NCURSES_NO_UTF8_ACS")) != 0) {
493         return atoi(env);
494     } else if ((env = getenv("TERM")) != 0) {
495         if (strstr(env, "linux"))
496             return 1;           /* always broken */
497         if (strstr(env, "screen") != 0
498             && ((env = getenv("TERMCAP")) != 0
499                 && strstr(env, "screen") != 0)
500             && strstr(env, "hhII00") != 0) {
501             if (CONTROL_N(enter_alt_charset_mode) ||
502                 CONTROL_O(enter_alt_charset_mode) ||
503                 CONTROL_N(set_attributes) ||
504                 CONTROL_O(set_attributes))
505                 return 1;
506         }
507     }
508     return 0;
509 }
510
511 /*
512  * This entrypoint is called from tgetent() to allow a special case of reusing
513  * the same TERMINAL data (see comment).
514  */
515 NCURSES_EXPORT(int)
516 _nc_setupterm(NCURSES_CONST char *tname, int Filedes, int *errret, bool reuse)
517 {
518     TERMINAL *termp;
519     int status;
520
521     START_TRACE();
522     T((T_CALLED("setupterm(%s,%d,%p)"), _nc_visbuf(tname), Filedes, errret));
523
524     if (tname == 0) {
525         tname = getenv("TERM");
526         if (tname == 0 || *tname == '\0') {
527             ret_error0(TGETENT_ERR, "TERM environment variable not set.\n");
528         }
529     }
530
531     if (strlen(tname) > MAX_NAME_SIZE) {
532         ret_error(TGETENT_ERR,
533                   "TERM environment must be <= %d characters.\n",
534                   MAX_NAME_SIZE);
535     }
536
537     T(("your terminal name is %s", tname));
538
539     /*
540      * Allow output redirection.  This is what SVr3 does.  If stdout is
541      * directed to a file, screen updates go to standard error.
542      */
543     if (Filedes == STDOUT_FILENO && !isatty(Filedes))
544         Filedes = STDERR_FILENO;
545
546     /*
547      * Check if we have already initialized to use this terminal.  If so, we
548      * do not need to re-read the terminfo entry, or obtain TTY settings.
549      *
550      * This is an improvement on SVr4 curses.  If an application mixes curses
551      * and termcap calls, it may call both initscr and tgetent.  This is not
552      * really a good thing to do, but can happen if someone tries using ncurses
553      * with the readline library.  The problem we are fixing is that when
554      * tgetent calls setupterm, the resulting Ottyb struct in cur_term is
555      * zeroed.  A subsequent call to endwin uses the zeroed terminal settings
556      * rather than the ones saved in initscr.  So we check if cur_term appears
557      * to contain terminal settings for the same output file as our current
558      * call - and copy those terminal settings.  (SVr4 curses does not do this,
559      * however applications that are working around the problem will still work
560      * properly with this feature).
561      */
562     if (reuse
563         && (termp = cur_term) != 0
564         && termp->Filedes == Filedes
565         && termp->_termname != 0
566         && !strcmp(termp->_termname, tname)
567         && _nc_name_match(termp->type.term_names, tname, "|")) {
568         T(("reusing existing terminal information and mode-settings"));
569     } else {
570
571         termp = typeCalloc(TERMINAL, 1);
572
573         if (termp == 0) {
574             ret_error0(TGETENT_ERR,
575                        "Not enough memory to create terminal structure.\n");
576         }
577 #if USE_DATABASE || USE_TERMCAP
578         status = grab_entry(tname, &termp->type);
579 #else
580         status = TGETENT_NO;
581 #endif
582
583         /* try fallback list if entry on disk */
584         if (status != TGETENT_YES) {
585             const TERMTYPE *fallback = _nc_fallback(tname);
586
587             if (fallback) {
588                 termp->type = *fallback;
589                 status = TGETENT_YES;
590             }
591         }
592
593         if (status != TGETENT_YES) {
594             del_curterm(termp);
595             if (status == TGETENT_ERR) {
596                 ret_error0(status, "terminals database is inaccessible\n");
597             } else if (status == TGETENT_NO) {
598                 ret_error(status, "'%s': unknown terminal type.\n", tname);
599             }
600         }
601 #if !USE_REENTRANT
602         strncpy(ttytype, termp->type.term_names, NAMESIZE - 1);
603         ttytype[NAMESIZE - 1] = '\0';
604 #endif
605
606         termp->Filedes = Filedes;
607         termp->_termname = strdup(tname);
608
609         set_curterm(termp);
610
611         if (command_character && getenv("CC"))
612             do_prototype(termp);
613
614         /*
615          * If an application calls setupterm() rather than initscr() or
616          * newterm(), we will not have the def_prog_mode() call in
617          * _nc_setupscreen().  Do it now anyway, so we can initialize the
618          * baudrate.
619          */
620         if (isatty(Filedes)) {
621             def_prog_mode();
622             baudrate();
623         }
624     }
625
626     /*
627      * We should always check the screensize, just in case.
628      */
629     _nc_get_screensize(SP, ptrLines(), ptrCols());
630
631     if (errret)
632         *errret = TGETENT_YES;
633
634     if (generic_type) {
635         ret_error(TGETENT_NO, "'%s': I need something more specific.\n", tname);
636     }
637     if (hard_copy) {
638         ret_error(TGETENT_YES, "'%s': I can't handle hardcopy terminals.\n", tname);
639     }
640     returnCode(OK);
641 }
642
643 /*
644  *      setupterm(termname, Filedes, errret)
645  *
646  *      Find and read the appropriate object file for the terminal
647  *      Make cur_term point to the structure.
648  */
649 NCURSES_EXPORT(int)
650 setupterm(NCURSES_CONST char *tname, int Filedes, int *errret)
651 {
652     return _nc_setupterm(tname, Filedes, errret, FALSE);
653 }