ncurses 5.0
[ncurses.git] / ncurses / tinfo / lib_setup.c
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
52 MODULE_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
94 static int _use_env = TRUE;
95
96 static void do_prototype(void);
97
98 void use_env(bool f)
99 {
100         _use_env = f;
101 }
102
103 int LINES, COLS, TABSIZE;
104
105 static 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
205 void _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
238 static 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
283 char 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
293 int setupterm(NCURSES_CONST char *tname, int Filedes, int *errret)
294 {
295 struct term     *term_ptr;
296 int 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
404 static void
405 do_prototype(void)
406 {
407 int     i;
408 char    CC;
409 char    proto;
410 char    *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 }