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