09cbbc2589d1896743618c863f69ef1bcd54b2bf
[ncurses.git] / ncurses / tinfo / lib_tputs.c
1 /****************************************************************************
2  * Copyright (c) 1998-2013,2015 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  *      tputs.c
38  *              delay_output()
39  *              _nc_outch()
40  *              tputs()
41  *
42  */
43
44 #include <curses.priv.h>
45
46 #ifndef CUR
47 #define CUR SP_TERMTYPE
48 #endif
49
50 #include <ctype.h>
51 #include <termcap.h>            /* ospeed */
52 #include <tic.h>
53
54 MODULE_ID("$Id: lib_tputs.c,v 1.96 2015/01/03 23:51:23 tom Exp $")
55
56 NCURSES_EXPORT_VAR(char) PC = 0;              /* used by termcap library */
57 NCURSES_EXPORT_VAR(NCURSES_OSPEED) ospeed = 0;        /* used by termcap library */
58
59 NCURSES_EXPORT_VAR(int) _nc_nulls_sent = 0;   /* used by 'tack' program */
60
61 #if NCURSES_NO_PADDING
62 NCURSES_EXPORT(void)
63 _nc_set_no_padding(SCREEN *sp)
64 {
65     bool no_padding = (getenv("NCURSES_NO_PADDING") != 0);
66
67     if (sp)
68         sp->_no_padding = no_padding;
69     else
70         _nc_prescreen._no_padding = no_padding;
71
72     TR(TRACE_CHARPUT | TRACE_MOVE, ("padding will%s be used",
73                                     GetNoPadding(sp) ? " not" : ""));
74 }
75 #endif
76
77 #if NCURSES_SP_FUNCS
78 #define SetOutCh(func) if (SP_PARM) SP_PARM->_outch = func; else _nc_prescreen._outch = func
79 #define GetOutCh()     (SP_PARM ? SP_PARM->_outch : _nc_prescreen._outch)
80 #else
81 #define SetOutCh(func) static_outch = func
82 #define GetOutCh()     static_outch
83 static NCURSES_SP_OUTC static_outch = NCURSES_SP_NAME(_nc_outch);
84 #endif
85
86 NCURSES_EXPORT(int)
87 NCURSES_SP_NAME(delay_output) (NCURSES_SP_DCLx int ms)
88 {
89     T((T_CALLED("delay_output(%p,%d)"), (void *) SP_PARM, ms));
90
91     if (!HasTInfoTerminal(SP_PARM))
92         returnCode(ERR);
93
94     if (no_pad_char) {
95         NCURSES_SP_NAME(_nc_flush) (NCURSES_SP_ARG);
96         napms(ms);
97     } else {
98         NCURSES_SP_OUTC my_outch = GetOutCh();
99         register int nullcount;
100
101         nullcount = (ms * _nc_baudrate(ospeed)) / (BAUDBYTE * 1000);
102         for (_nc_nulls_sent += nullcount; nullcount > 0; nullcount--)
103             my_outch(NCURSES_SP_ARGx PC);
104         if (my_outch == NCURSES_SP_NAME(_nc_outch))
105             NCURSES_SP_NAME(_nc_flush) (NCURSES_SP_ARG);
106     }
107
108     returnCode(OK);
109 }
110
111 #if NCURSES_SP_FUNCS
112 NCURSES_EXPORT(int)
113 delay_output(int ms)
114 {
115     return NCURSES_SP_NAME(delay_output) (CURRENT_SCREEN, ms);
116 }
117 #endif
118
119 NCURSES_EXPORT(void)
120 NCURSES_SP_NAME(_nc_flush) (NCURSES_SP_DCL0)
121 {
122     if (SP_PARM != 0 && SP_PARM->_ofd >= 0) {
123         if (SP_PARM->out_inuse) {
124             char *buf = SP_PARM->out_buffer;
125             size_t amount = SP->out_inuse;
126             ssize_t res;
127
128             SP->out_inuse = 0;
129             while (amount) {
130                 res = write(SP_PARM->_ofd, buf, amount);
131
132                 if (res > 0) {
133                     /* if the write was incomplete, try again */
134                     amount -= (size_t) res;
135                     buf += res;
136                 } else if (errno == EAGAIN) {
137                     continue;
138                 } else if (errno == EINTR) {
139                     continue;
140                 } else {
141                     break;      /* an error we can not recover from */
142                 }
143             }
144         }
145     }
146 }
147
148 #if NCURSES_SP_FUNCS
149 NCURSES_EXPORT(void)
150 _nc_flush(void)
151 {
152     NCURSES_SP_NAME(_nc_flush) (CURRENT_SCREEN);
153 }
154 #endif
155
156 NCURSES_EXPORT(int)
157 NCURSES_SP_NAME(_nc_outch) (NCURSES_SP_DCLx int ch)
158 {
159     int rc = OK;
160
161     COUNT_OUTCHARS(1);
162
163     if (HasTInfoTerminal(SP_PARM)
164         && SP_PARM != 0) {
165         if (SP_PARM->out_buffer != 0) {
166             if (SP_PARM->out_inuse + 1 >= SP_PARM->out_limit)
167                 NCURSES_SP_NAME(_nc_flush) (NCURSES_SP_ARG);
168             SP_PARM->out_buffer[SP_PARM->out_inuse++] = (char) ch;
169         } else {
170             char tmp = (char) ch;
171             /*
172              * POSIX says write() is safe in a signal handler, but the
173              * buffered I/O is not.
174              */
175             if (write(fileno(NC_OUTPUT(SP_PARM)), &tmp, (size_t) 1) == -1)
176                 rc = ERR;
177         }
178     } else {
179         char tmp = (char) ch;
180         if (write(fileno(stdout), &tmp, (size_t) 1) == -1)
181             rc = ERR;
182     }
183     return rc;
184 }
185
186 #if NCURSES_SP_FUNCS
187 NCURSES_EXPORT(int)
188 _nc_outch(int ch)
189 {
190     return NCURSES_SP_NAME(_nc_outch) (CURRENT_SCREEN, ch);
191 }
192 #endif
193
194 /*
195  * This is used for the putp special case.
196  */
197 NCURSES_EXPORT(int)
198 NCURSES_SP_NAME(_nc_putchar) (NCURSES_SP_DCLx int ch)
199 {
200     (void) SP_PARM;
201     return putchar(ch);
202 }
203
204 #if NCURSES_SP_FUNCS
205 NCURSES_EXPORT(int)
206 _nc_putchar(int ch)
207 {
208     return putchar(ch);
209 }
210 #endif
211
212 /*
213  * putp is special - per documentation it calls tputs with putchar as the
214  * parameter for outputting characters.  This means that it uses stdio, which
215  * is not signal-safe.  Applications call this entrypoint; we do not call it
216  * from within the library.
217  */
218 NCURSES_EXPORT(int)
219 NCURSES_SP_NAME(putp) (NCURSES_SP_DCLx const char *string)
220 {
221     return NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
222                                    string, 1, NCURSES_SP_NAME(_nc_putchar));
223 }
224
225 #if NCURSES_SP_FUNCS
226 NCURSES_EXPORT(int)
227 putp(const char *string)
228 {
229     return NCURSES_SP_NAME(putp) (CURRENT_SCREEN, string);
230 }
231 #endif
232
233 /*
234  * Use these entrypoints rather than "putp" within the library.
235  */
236 NCURSES_EXPORT(int)
237 NCURSES_SP_NAME(_nc_putp) (NCURSES_SP_DCLx
238                            const char *name GCC_UNUSED,
239                            const char *string)
240 {
241     int rc = ERR;
242
243     if (string != 0) {
244         TPUTS_TRACE(name);
245         rc = NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
246                                      string, 1, NCURSES_SP_NAME(_nc_outch));
247     }
248     return rc;
249 }
250
251 #if NCURSES_SP_FUNCS
252 NCURSES_EXPORT(int)
253 _nc_putp(const char *name, const char *string)
254 {
255     return NCURSES_SP_NAME(_nc_putp) (CURRENT_SCREEN, name, string);
256 }
257 #endif
258
259 NCURSES_EXPORT(int)
260 NCURSES_SP_NAME(tputs) (NCURSES_SP_DCLx
261                         const char *string,
262                         int affcnt,
263                         NCURSES_SP_OUTC outc)
264 {
265     NCURSES_SP_OUTC my_outch = GetOutCh();
266     bool always_delay;
267     bool normal_delay;
268     int number;
269 #if BSD_TPUTS
270     int trailpad;
271 #endif /* BSD_TPUTS */
272
273 #ifdef TRACE
274     char addrbuf[32];
275
276     if (USE_TRACEF(TRACE_TPUTS)) {
277         if (outc == NCURSES_SP_NAME(_nc_outch))
278             _nc_STRCPY(addrbuf, "_nc_outch", sizeof(addrbuf));
279         else
280             _nc_SPRINTF(addrbuf, _nc_SLIMIT(sizeof(addrbuf)) "%p", outc);
281         if (_nc_tputs_trace) {
282             _tracef("tputs(%s = %s, %d, %s) called", _nc_tputs_trace,
283                     _nc_visbuf(string), affcnt, addrbuf);
284         } else {
285             _tracef("tputs(%s, %d, %s) called", _nc_visbuf(string), affcnt, addrbuf);
286         }
287         TPUTS_TRACE(NULL);
288         _nc_unlock_global(tracef);
289     }
290 #endif /* TRACE */
291
292     if (SP_PARM != 0 && !HasTInfoTerminal(SP_PARM))
293         return ERR;
294
295     if (!VALID_STRING(string))
296         return ERR;
297
298     if (
299 #if NCURSES_SP_FUNCS
300            (SP_PARM != 0 && SP_PARM->_term == 0)
301 #else
302            cur_term == 0
303 #endif
304         ) {
305         always_delay = FALSE;
306         normal_delay = TRUE;
307     } else {
308         always_delay = (string == bell) || (string == flash_screen);
309         normal_delay =
310             !xon_xoff
311             && padding_baud_rate
312 #if NCURSES_NO_PADDING
313             && !GetNoPadding(SP_PARM)
314 #endif
315             && (_nc_baudrate(ospeed) >= padding_baud_rate);
316     }
317
318 #if BSD_TPUTS
319     /*
320      * This ugly kluge deals with the fact that some ancient BSD programs
321      * (like nethack) actually do the likes of tputs("50") to get delays.
322      */
323     trailpad = 0;
324     if (isdigit(UChar(*string))) {
325         while (isdigit(UChar(*string))) {
326             trailpad = trailpad * 10 + (*string - '0');
327             string++;
328         }
329         trailpad *= 10;
330         if (*string == '.') {
331             string++;
332             if (isdigit(UChar(*string))) {
333                 trailpad += (*string - '0');
334                 string++;
335             }
336             while (isdigit(UChar(*string)))
337                 string++;
338         }
339
340         if (*string == '*') {
341             trailpad *= affcnt;
342             string++;
343         }
344     }
345 #endif /* BSD_TPUTS */
346
347     SetOutCh(outc);             /* redirect delay_output() */
348     while (*string) {
349         if (*string != '$')
350             (*outc) (NCURSES_SP_ARGx *string);
351         else {
352             string++;
353             if (*string != '<') {
354                 (*outc) (NCURSES_SP_ARGx '$');
355                 if (*string)
356                     (*outc) (NCURSES_SP_ARGx *string);
357             } else {
358                 bool mandatory;
359
360                 string++;
361                 if ((!isdigit(UChar(*string)) && *string != '.')
362                     || !strchr(string, '>')) {
363                     (*outc) (NCURSES_SP_ARGx '$');
364                     (*outc) (NCURSES_SP_ARGx '<');
365                     continue;
366                 }
367
368                 number = 0;
369                 while (isdigit(UChar(*string))) {
370                     number = number * 10 + (*string - '0');
371                     string++;
372                 }
373                 number *= 10;
374                 if (*string == '.') {
375                     string++;
376                     if (isdigit(UChar(*string))) {
377                         number += (*string - '0');
378                         string++;
379                     }
380                     while (isdigit(UChar(*string)))
381                         string++;
382                 }
383
384                 mandatory = FALSE;
385                 while (*string == '*' || *string == '/') {
386                     if (*string == '*') {
387                         number *= affcnt;
388                         string++;
389                     } else {    /* if (*string == '/') */
390                         mandatory = TRUE;
391                         string++;
392                     }
393                 }
394
395                 if (number > 0
396                     && (always_delay
397                         || normal_delay
398                         || mandatory))
399                     NCURSES_SP_NAME(delay_output) (NCURSES_SP_ARGx number / 10);
400
401             }                   /* endelse (*string == '<') */
402         }                       /* endelse (*string == '$') */
403
404         if (*string == '\0')
405             break;
406
407         string++;
408     }
409
410 #if BSD_TPUTS
411     /*
412      * Emit any BSD-style prefix padding that we've accumulated now.
413      */
414     if (trailpad > 0
415         && (always_delay || normal_delay))
416         delay_output(trailpad / 10);
417 #endif /* BSD_TPUTS */
418
419     SetOutCh(my_outch);
420     return OK;
421 }
422
423 #if NCURSES_SP_FUNCS
424 NCURSES_EXPORT(int)
425 _nc_outc_wrapper(SCREEN *sp, int c)
426 {
427     if (0 == sp) {
428         return (ERR);
429     } else {
430         return sp->jump(c);
431     }
432 }
433
434 NCURSES_EXPORT(int)
435 tputs(const char *string, int affcnt, int (*outc) (int))
436 {
437     SetSafeOutcWrapper(outc);
438     return NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx string, affcnt, _nc_outc_wrapper);
439 }
440 #endif