1 /**************************************************************************** 2 * Copyright 2018-2019,2020 Thomas E. Dickey * 3 * Copyright 1998-2016,2017 Free Software Foundation, Inc. * 4 * * 5 * Permission is hereby granted, free of charge, to any person obtaining a * 6 * copy of this software and associated documentation files (the * 7 * "Software"), to deal in the Software without restriction, including * 8 * without limitation the rights to use, copy, modify, merge, publish, * 9 * distribute, distribute with modifications, sublicense, and/or sell * 10 * copies of the Software, and to permit persons to whom the Software is * 11 * furnished to do so, subject to the following conditions: * 12 * * 13 * The above copyright notice and this permission notice shall be included * 14 * in all copies or substantial portions of the Software. * 15 * * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 19 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 22 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 23 * * 24 * Except as contained in this notice, the name(s) of the above copyright * 25 * holders shall not be used in advertising or otherwise to promote the * 26 * sale, use or other dealings in this Software without prior written * 27 * authorization. * 28 ****************************************************************************/ 29 30 /**************************************************************************** 31 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 32 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 33 * and: Thomas E. Dickey 1996-on * 34 * and: Juergen Pfeifer 2009 * 35 ****************************************************************************/ 36 37 /* 38 * tputs.c 39 * delay_output() 40 * _nc_outch() 41 * tputs() 42 * 43 */ 44 45 #include <curses.priv.h> 46 47 #ifndef CUR 48 #define CUR SP_TERMTYPE 49 #endif 50 51 #include <ctype.h> 52 #include <termcap.h> /* ospeed */ 53 #include <tic.h> 54 55 MODULE_ID("$Id: lib_tputs.c,v 1.103 2020/02/02 23:34:34 tom Exp $") 56 57 NCURSES_EXPORT_VAR(char) PC = 0; /* used by termcap library */ 58 NCURSES_EXPORT_VAR(NCURSES_OSPEED) ospeed = 0; /* used by termcap library */ 59 60 NCURSES_EXPORT_VAR(int) _nc_nulls_sent = 0; /* used by 'tack' program */ 61 62 #if NCURSES_NO_PADDING 63 NCURSES_EXPORT(void) 64 _nc_set_no_padding(SCREEN *sp) 65 { 66 bool no_padding = (getenv("NCURSES_NO_PADDING") != 0); 67 68 if (sp) 69 sp->_no_padding = no_padding; 70 else 71 _nc_prescreen._no_padding = no_padding; 72 73 TR(TRACE_CHARPUT | TRACE_MOVE, ("padding will%s be used", 74 GetNoPadding(sp) ? " not" : "")); 75 } 76 #endif 77 78 #if NCURSES_SP_FUNCS 79 #define SetOutCh(func) if (SP_PARM) SP_PARM->_outch = func; else _nc_prescreen._outch = func 80 #define GetOutCh() (SP_PARM ? SP_PARM->_outch : _nc_prescreen._outch) 81 #else 82 #define SetOutCh(func) static_outch = func 83 #define GetOutCh() static_outch 84 static NCURSES_SP_OUTC static_outch = NCURSES_SP_NAME(_nc_outch); 85 #endif 86 87 NCURSES_EXPORT(int) 88 NCURSES_SP_NAME(delay_output) (NCURSES_SP_DCLx int ms) 89 { 90 T((T_CALLED("delay_output(%p,%d)"), (void *) SP_PARM, ms)); 91 92 if (!HasTInfoTerminal(SP_PARM)) 93 returnCode(ERR); 94 95 if (no_pad_char) { 96 NCURSES_SP_NAME(_nc_flush) (NCURSES_SP_ARG); 97 napms(ms); 98 } else { 99 NCURSES_SP_OUTC my_outch = GetOutCh(); 100 register int nullcount; 101 102 nullcount = (ms * _nc_baudrate(ospeed)) / (BAUDBYTE * 1000); 103 for (_nc_nulls_sent += nullcount; nullcount > 0; nullcount--) 104 my_outch(NCURSES_SP_ARGx PC); 105 if (my_outch == NCURSES_SP_NAME(_nc_outch)) 106 NCURSES_SP_NAME(_nc_flush) (NCURSES_SP_ARG); 107 } 108 109 returnCode(OK); 110 } 111 112 #if NCURSES_SP_FUNCS 113 NCURSES_EXPORT(int) 114 delay_output(int ms) 115 { 116 return NCURSES_SP_NAME(delay_output) (CURRENT_SCREEN, ms); 117 } 118 #endif 119 120 NCURSES_EXPORT(void) 121 NCURSES_SP_NAME(_nc_flush) (NCURSES_SP_DCL0) 122 { 123 if (SP_PARM != 0 && SP_PARM->_ofd >= 0) { 124 if (SP_PARM->out_inuse) { 125 char *buf = SP_PARM->out_buffer; 126 size_t amount = SP->out_inuse; 127 128 SP->out_inuse = 0; 129 TR(TRACE_CHARPUT, ("flushing %ld bytes", (unsigned long) amount)); 130 while (amount) { 131 ssize_t res = write(SP_PARM->_ofd, buf, amount); 132 133 if (res > 0) { 134 /* if the write was incomplete, try again */ 135 amount -= (size_t) res; 136 buf += res; 137 } else if (errno == EAGAIN) { 138 continue; 139 } else if (errno == EINTR) { 140 continue; 141 } else { 142 break; /* an error we can not recover from */ 143 } 144 } 145 } 146 } else { 147 fflush(stdout); 148 } 149 } 150 151 #if NCURSES_SP_FUNCS 152 NCURSES_EXPORT(void) 153 _nc_flush(void) 154 { 155 NCURSES_SP_NAME(_nc_flush) (CURRENT_SCREEN); 156 } 157 #endif 158 159 NCURSES_EXPORT(int) 160 NCURSES_SP_NAME(_nc_outch) (NCURSES_SP_DCLx int ch) 161 { 162 int rc = OK; 163 164 COUNT_OUTCHARS(1); 165 166 if (HasTInfoTerminal(SP_PARM) 167 && SP_PARM != 0) { 168 if (SP_PARM->out_buffer != 0) { 169 if (SP_PARM->out_inuse + 1 >= SP_PARM->out_limit) 170 NCURSES_SP_NAME(_nc_flush) (NCURSES_SP_ARG); 171 SP_PARM->out_buffer[SP_PARM->out_inuse++] = (char) ch; 172 } else { 173 char tmp = (char) ch; 174 /* 175 * POSIX says write() is safe in a signal handler, but the 176 * buffered I/O is not. 177 */ 178 if (write(fileno(NC_OUTPUT(SP_PARM)), &tmp, (size_t) 1) == -1) 179 rc = ERR; 180 } 181 } else { 182 char tmp = (char) ch; 183 if (write(fileno(stdout), &tmp, (size_t) 1) == -1) 184 rc = ERR; 185 } 186 return rc; 187 } 188 189 #if NCURSES_SP_FUNCS 190 NCURSES_EXPORT(int) 191 _nc_outch(int ch) 192 { 193 return NCURSES_SP_NAME(_nc_outch) (CURRENT_SCREEN, ch); 194 } 195 #endif 196 197 /* 198 * This is used for the putp special case. 199 */ 200 NCURSES_EXPORT(int) 201 NCURSES_SP_NAME(_nc_putchar) (NCURSES_SP_DCLx int ch) 202 { 203 (void) SP_PARM; 204 return putchar(ch); 205 } 206 207 #if NCURSES_SP_FUNCS 208 NCURSES_EXPORT(int) 209 _nc_putchar(int ch) 210 { 211 return putchar(ch); 212 } 213 #endif 214 215 /* 216 * putp is special - per documentation it calls tputs with putchar as the 217 * parameter for outputting characters. This means that it uses stdio, which 218 * is not signal-safe. Applications call this entrypoint; we do not call it 219 * from within the library. 220 */ 221 NCURSES_EXPORT(int) 222 NCURSES_SP_NAME(putp) (NCURSES_SP_DCLx const char *string) 223 { 224 return NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx 225 string, 1, NCURSES_SP_NAME(_nc_putchar)); 226 } 227 228 #if NCURSES_SP_FUNCS 229 NCURSES_EXPORT(int) 230 putp(const char *string) 231 { 232 return NCURSES_SP_NAME(putp) (CURRENT_SCREEN, string); 233 } 234 #endif 235 236 /* 237 * Use these entrypoints rather than "putp" within the library. 238 */ 239 NCURSES_EXPORT(int) 240 NCURSES_SP_NAME(_nc_putp) (NCURSES_SP_DCLx 241 const char *name GCC_UNUSED, 242 const char *string) 243 { 244 int rc = ERR; 245 246 if (string != 0) { 247 TPUTS_TRACE(name); 248 rc = NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx 249 string, 1, NCURSES_SP_NAME(_nc_outch)); 250 } 251 return rc; 252 } 253 254 #if NCURSES_SP_FUNCS 255 NCURSES_EXPORT(int) 256 _nc_putp(const char *name, const char *string) 257 { 258 return NCURSES_SP_NAME(_nc_putp) (CURRENT_SCREEN, name, string); 259 } 260 #endif 261 262 NCURSES_EXPORT(int) 263 NCURSES_SP_NAME(tputs) (NCURSES_SP_DCLx 264 const char *string, 265 int affcnt, 266 NCURSES_SP_OUTC outc) 267 { 268 NCURSES_SP_OUTC my_outch = GetOutCh(); 269 bool always_delay; 270 bool normal_delay; 271 int number; 272 #if BSD_TPUTS 273 int trailpad; 274 #endif /* BSD_TPUTS */ 275 276 #ifdef TRACE 277 if (USE_TRACEF(TRACE_TPUTS)) { 278 char addrbuf[32]; 279 TR_FUNC_BFR(1); 280 281 if (outc == NCURSES_SP_NAME(_nc_outch)) { 282 _nc_STRCPY(addrbuf, "_nc_outch", sizeof(addrbuf)); 283 } else { 284 _nc_SPRINTF(addrbuf, _nc_SLIMIT(sizeof(addrbuf)) "%s", 285 TR_FUNC_ARG(0, outc)); 286 } 287 if (_nc_tputs_trace) { 288 _tracef("tputs(%s = %s, %d, %s) called", _nc_tputs_trace, 289 _nc_visbuf(string), affcnt, addrbuf); 290 } else { 291 _tracef("tputs(%s, %d, %s) called", _nc_visbuf(string), affcnt, addrbuf); 292 } 293 TPUTS_TRACE(NULL); 294 _nc_unlock_global(tracef); 295 } 296 #endif /* TRACE */ 297 298 if (SP_PARM != 0 && !HasTInfoTerminal(SP_PARM)) 299 return ERR; 300 301 if (!VALID_STRING(string)) 302 return ERR; 303 304 if ( 305 #if NCURSES_SP_FUNCS 306 (SP_PARM != 0 && SP_PARM->_term == 0) 307 #else 308 cur_term == 0 309 #endif 310 ) { 311 always_delay = FALSE; 312 normal_delay = TRUE; 313 } else { 314 always_delay = (string == bell) || (string == flash_screen); 315 normal_delay = 316 !xon_xoff 317 && padding_baud_rate 318 #if NCURSES_NO_PADDING 319 && !GetNoPadding(SP_PARM) 320 #endif 321 && (_nc_baudrate(ospeed) >= padding_baud_rate); 322 } 323 324 #if BSD_TPUTS 325 /* 326 * This ugly kluge deals with the fact that some ancient BSD programs 327 * (like nethack) actually do the likes of tputs("50") to get delays. 328 */ 329 trailpad = 0; 330 if (isdigit(UChar(*string))) { 331 while (isdigit(UChar(*string))) { 332 trailpad = trailpad * 10 + (*string - '0'); 333 string++; 334 } 335 trailpad *= 10; 336 if (*string == '.') { 337 string++; 338 if (isdigit(UChar(*string))) { 339 trailpad += (*string - '0'); 340 string++; 341 } 342 while (isdigit(UChar(*string))) 343 string++; 344 } 345 346 if (*string == '*') { 347 trailpad *= affcnt; 348 string++; 349 } 350 } 351 #endif /* BSD_TPUTS */ 352 353 SetOutCh(outc); /* redirect delay_output() */ 354 while (*string) { 355 if (*string != '$') 356 (*outc) (NCURSES_SP_ARGx *string); 357 else { 358 string++; 359 if (*string != '<') { 360 (*outc) (NCURSES_SP_ARGx '$'); 361 if (*string) 362 (*outc) (NCURSES_SP_ARGx *string); 363 } else { 364 bool mandatory; 365 366 string++; 367 if ((!isdigit(UChar(*string)) && *string != '.') 368 || !strchr(string, '>')) { 369 (*outc) (NCURSES_SP_ARGx '$'); 370 (*outc) (NCURSES_SP_ARGx '<'); 371 continue; 372 } 373 374 number = 0; 375 while (isdigit(UChar(*string))) { 376 number = number * 10 + (*string - '0'); 377 string++; 378 } 379 number *= 10; 380 if (*string == '.') { 381 string++; 382 if (isdigit(UChar(*string))) { 383 number += (*string - '0'); 384 string++; 385 } 386 while (isdigit(UChar(*string))) 387 string++; 388 } 389 390 mandatory = FALSE; 391 while (*string == '*' || *string == '/') { 392 if (*string == '*') { 393 number *= affcnt; 394 string++; 395 } else { /* if (*string == '/') */ 396 mandatory = TRUE; 397 string++; 398 } 399 } 400 401 if (number > 0 402 && (always_delay 403 || normal_delay 404 || mandatory)) 405 NCURSES_SP_NAME(delay_output) (NCURSES_SP_ARGx number / 10); 406 407 } /* endelse (*string == '<') */ 408 } /* endelse (*string == '$') */ 409 410 if (*string == '\0') 411 break; 412 413 string++; 414 } 415 416 #if BSD_TPUTS 417 /* 418 * Emit any BSD-style prefix padding that we've accumulated now. 419 */ 420 if (trailpad > 0 421 && (always_delay || normal_delay)) 422 delay_output(trailpad / 10); 423 #endif /* BSD_TPUTS */ 424 425 SetOutCh(my_outch); 426 return OK; 427 } 428 429 #if NCURSES_SP_FUNCS 430 NCURSES_EXPORT(int) 431 _nc_outc_wrapper(SCREEN *sp, int c) 432 { 433 if (0 == sp) { 434 return fputc(c, stdout); 435 } else { 436 return sp->jump(c); 437 } 438 } 439 440 NCURSES_EXPORT(int) 441 tputs(const char *string, int affcnt, int (*outc) (int)) 442 { 443 SetSafeOutcWrapper(outc); 444 return NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx string, affcnt, _nc_outc_wrapper); 445 } 446 #endif 447