1 /**************************************************************************** 2 * Copyright 2018-2020,2021 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.107 2021/04/03 18:45:53 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 T((T_CALLED("_nc_flush(%p)"), (void *) SP_PARM)); 124 if (SP_PARM != 0 && SP_PARM->_ofd >= 0) { 125 TR(TRACE_CHARPUT, ("ofd:%d inuse:%lu buffer:%p", 126 SP_PARM->_ofd, 127 (unsigned long) SP_PARM->out_inuse, 128 SP_PARM->out_buffer)); 129 if (SP_PARM->out_inuse) { 130 char *buf = SP_PARM->out_buffer; 131 size_t amount = SP->out_inuse; 132 133 SP->out_inuse = 0; 134 TR(TRACE_CHARPUT, ("flushing %ld/%ld bytes", 135 (unsigned long) amount, _nc_outchars)); 136 while (amount) { 137 ssize_t res = write(SP_PARM->_ofd, buf, amount); 138 if (res > 0) { 139 /* if the write was incomplete, try again */ 140 amount -= (size_t) res; 141 buf += res; 142 } else if (errno == EAGAIN) { 143 continue; 144 } else if (errno == EINTR) { 145 continue; 146 } else { 147 break; /* an error we can not recover from */ 148 } 149 } 150 } else if (SP_PARM->out_buffer == 0) { 151 TR(TRACE_CHARPUT, ("flushing stdout")); 152 fflush(stdout); 153 } 154 } else { 155 TR(TRACE_CHARPUT, ("flushing stdout")); 156 fflush(stdout); 157 } 158 returnVoid; 159 } 160 161 #if NCURSES_SP_FUNCS 162 NCURSES_EXPORT(void) 163 _nc_flush(void) 164 { 165 NCURSES_SP_NAME(_nc_flush) (CURRENT_SCREEN); 166 } 167 #endif 168 169 NCURSES_EXPORT(int) 170 NCURSES_SP_NAME(_nc_outch) (NCURSES_SP_DCLx int ch) 171 { 172 int rc = OK; 173 174 COUNT_OUTCHARS(1); 175 176 if (HasTInfoTerminal(SP_PARM) 177 && SP_PARM != 0) { 178 if (SP_PARM->out_buffer != 0) { 179 if (SP_PARM->out_inuse + 1 >= SP_PARM->out_limit) 180 NCURSES_SP_NAME(_nc_flush) (NCURSES_SP_ARG); 181 SP_PARM->out_buffer[SP_PARM->out_inuse++] = (char) ch; 182 } else { 183 char tmp = (char) ch; 184 /* 185 * POSIX says write() is safe in a signal handler, but the 186 * buffered I/O is not. 187 */ 188 if (write(fileno(NC_OUTPUT(SP_PARM)), &tmp, (size_t) 1) == -1) 189 rc = ERR; 190 } 191 } else { 192 char tmp = (char) ch; 193 if (write(fileno(stdout), &tmp, (size_t) 1) == -1) 194 rc = ERR; 195 } 196 return rc; 197 } 198 199 #if NCURSES_SP_FUNCS 200 NCURSES_EXPORT(int) 201 _nc_outch(int ch) 202 { 203 return NCURSES_SP_NAME(_nc_outch) (CURRENT_SCREEN, ch); 204 } 205 #endif 206 207 /* 208 * This is used for the putp special case. 209 */ 210 NCURSES_EXPORT(int) 211 NCURSES_SP_NAME(_nc_putchar) (NCURSES_SP_DCLx int ch) 212 { 213 (void) SP_PARM; 214 return putchar(ch); 215 } 216 217 #if NCURSES_SP_FUNCS 218 NCURSES_EXPORT(int) 219 _nc_putchar(int ch) 220 { 221 return putchar(ch); 222 } 223 #endif 224 225 /* 226 * putp is special - per documentation it calls tputs with putchar as the 227 * parameter for outputting characters. This means that it uses stdio, which 228 * is not signal-safe. Applications call this entrypoint; we do not call it 229 * from within the library. 230 */ 231 NCURSES_EXPORT(int) 232 NCURSES_SP_NAME(putp) (NCURSES_SP_DCLx const char *string) 233 { 234 return NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx 235 string, 1, NCURSES_SP_NAME(_nc_putchar)); 236 } 237 238 #if NCURSES_SP_FUNCS 239 NCURSES_EXPORT(int) 240 putp(const char *string) 241 { 242 return NCURSES_SP_NAME(putp) (CURRENT_SCREEN, string); 243 } 244 #endif 245 246 /* 247 * Use these entrypoints rather than "putp" within the library. 248 */ 249 NCURSES_EXPORT(int) 250 NCURSES_SP_NAME(_nc_putp) (NCURSES_SP_DCLx 251 const char *name GCC_UNUSED, 252 const char *string) 253 { 254 int rc = ERR; 255 256 if (string != 0) { 257 TPUTS_TRACE(name); 258 rc = NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx 259 string, 1, NCURSES_SP_NAME(_nc_outch)); 260 } 261 return rc; 262 } 263 264 #if NCURSES_SP_FUNCS 265 NCURSES_EXPORT(int) 266 _nc_putp(const char *name, const char *string) 267 { 268 return NCURSES_SP_NAME(_nc_putp) (CURRENT_SCREEN, name, string); 269 } 270 #endif 271 272 NCURSES_EXPORT(int) 273 NCURSES_SP_NAME(tputs) (NCURSES_SP_DCLx 274 const char *string, 275 int affcnt, 276 NCURSES_SP_OUTC outc) 277 { 278 NCURSES_SP_OUTC my_outch = GetOutCh(); 279 bool always_delay; 280 bool normal_delay; 281 int number; 282 #if BSD_TPUTS 283 int trailpad; 284 #endif /* BSD_TPUTS */ 285 286 #ifdef TRACE 287 if (USE_TRACEF(TRACE_TPUTS)) { 288 char addrbuf[32]; 289 TR_FUNC_BFR(1); 290 291 if (outc == NCURSES_SP_NAME(_nc_outch)) { 292 _nc_STRCPY(addrbuf, "_nc_outch", sizeof(addrbuf)); 293 } else { 294 _nc_SPRINTF(addrbuf, _nc_SLIMIT(sizeof(addrbuf)) "%s", 295 TR_FUNC_ARG(0, outc)); 296 } 297 if (_nc_tputs_trace) { 298 _tracef("tputs(%s = %s, %d, %s) called", _nc_tputs_trace, 299 _nc_visbuf(string), affcnt, addrbuf); 300 } else { 301 _tracef("tputs(%s, %d, %s) called", _nc_visbuf(string), affcnt, addrbuf); 302 } 303 TPUTS_TRACE(NULL); 304 _nc_unlock_global(tracef); 305 } 306 #endif /* TRACE */ 307 308 if (SP_PARM != 0 && !HasTInfoTerminal(SP_PARM)) 309 return ERR; 310 311 if (!VALID_STRING(string)) 312 return ERR; 313 314 if ( 315 #if NCURSES_SP_FUNCS 316 (SP_PARM != 0 && SP_PARM->_term == 0) 317 #else 318 cur_term == 0 319 #endif 320 ) { 321 always_delay = FALSE; 322 normal_delay = TRUE; 323 } else { 324 always_delay = (string == bell) || (string == flash_screen); 325 normal_delay = 326 !xon_xoff 327 && padding_baud_rate 328 #if NCURSES_NO_PADDING 329 && !GetNoPadding(SP_PARM) 330 #endif 331 && (_nc_baudrate(ospeed) >= padding_baud_rate); 332 } 333 334 #if BSD_TPUTS 335 /* 336 * This ugly kluge deals with the fact that some ancient BSD programs 337 * (like nethack) actually do the likes of tputs("50") to get delays. 338 */ 339 trailpad = 0; 340 if (isdigit(UChar(*string))) { 341 while (isdigit(UChar(*string))) { 342 trailpad = trailpad * 10 + (*string - '0'); 343 string++; 344 } 345 trailpad *= 10; 346 if (*string == '.') { 347 string++; 348 if (isdigit(UChar(*string))) { 349 trailpad += (*string - '0'); 350 string++; 351 } 352 while (isdigit(UChar(*string))) 353 string++; 354 } 355 356 if (*string == '*') { 357 trailpad *= affcnt; 358 string++; 359 } 360 } 361 #endif /* BSD_TPUTS */ 362 363 SetOutCh(outc); /* redirect delay_output() */ 364 while (*string) { 365 if (*string != '$') 366 (*outc) (NCURSES_SP_ARGx *string); 367 else { 368 string++; 369 if (*string != '<') { 370 (*outc) (NCURSES_SP_ARGx '$'); 371 if (*string) 372 (*outc) (NCURSES_SP_ARGx *string); 373 } else { 374 bool mandatory; 375 376 string++; 377 if ((!isdigit(UChar(*string)) && *string != '.') 378 || !strchr(string, '>')) { 379 (*outc) (NCURSES_SP_ARGx '$'); 380 (*outc) (NCURSES_SP_ARGx '<'); 381 continue; 382 } 383 384 number = 0; 385 while (isdigit(UChar(*string))) { 386 number = number * 10 + (*string - '0'); 387 string++; 388 } 389 number *= 10; 390 if (*string == '.') { 391 string++; 392 if (isdigit(UChar(*string))) { 393 number += (*string - '0'); 394 string++; 395 } 396 while (isdigit(UChar(*string))) 397 string++; 398 } 399 400 mandatory = FALSE; 401 while (*string == '*' || *string == '/') { 402 if (*string == '*') { 403 number *= affcnt; 404 string++; 405 } else { /* if (*string == '/') */ 406 mandatory = TRUE; 407 string++; 408 } 409 } 410 411 if (number > 0 412 && (always_delay 413 || normal_delay 414 || mandatory)) 415 NCURSES_SP_NAME(delay_output) (NCURSES_SP_ARGx number / 10); 416 417 } /* endelse (*string == '<') */ 418 } /* endelse (*string == '$') */ 419 420 if (*string == '\0') 421 break; 422 423 string++; 424 } 425 426 #if BSD_TPUTS 427 /* 428 * Emit any BSD-style prefix padding that we've accumulated now. 429 */ 430 if (trailpad > 0 431 && (always_delay || normal_delay)) 432 NCURSES_SP_NAME(delay_output) (NCURSES_SP_ARGx trailpad / 10); 433 #endif /* BSD_TPUTS */ 434 435 SetOutCh(my_outch); 436 return OK; 437 } 438 439 #if NCURSES_SP_FUNCS 440 NCURSES_EXPORT(int) 441 _nc_outc_wrapper(SCREEN *sp, int c) 442 { 443 if (0 == sp) { 444 return fputc(c, stdout); 445 } else { 446 return sp->jump(c); 447 } 448 } 449 450 NCURSES_EXPORT(int) 451 tputs(const char *string, int affcnt, int (*outc) (int)) 452 { 453 SetSafeOutcWrapper(outc); 454 return NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx string, affcnt, _nc_outc_wrapper); 455 } 456 #endif 457