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