1a5f0fb15SPaul Saab /* 2*c77c4889SXin LI * Copyright (C) 1984-2024 Mark Nudelman 3a5f0fb15SPaul Saab * 4a5f0fb15SPaul Saab * You may distribute under the terms of either the GNU General Public 5a5f0fb15SPaul Saab * License or the Less License, as specified in the README file. 6a5f0fb15SPaul Saab * 796e55cc7SXin LI * For more information, see the README file. 8a5f0fb15SPaul Saab */ 9a5f0fb15SPaul Saab 10a5f0fb15SPaul Saab 11a5f0fb15SPaul Saab /* 12a5f0fb15SPaul Saab * High level routines dealing with the output to the screen. 13a5f0fb15SPaul Saab */ 14a5f0fb15SPaul Saab 15a5f0fb15SPaul Saab #include "less.h" 16a5f0fb15SPaul Saab #if MSDOS_COMPILER==WIN32C 17a5f0fb15SPaul Saab #include "windows.h" 18b2ea2440SXin LI #ifndef COMMON_LVB_UNDERSCORE 19b2ea2440SXin LI #define COMMON_LVB_UNDERSCORE 0x8000 20b2ea2440SXin LI #endif 21a5f0fb15SPaul Saab #endif 22a5f0fb15SPaul Saab 23a5f0fb15SPaul Saab public int errmsgs; /* Count of messages displayed by error() */ 24a5f0fb15SPaul Saab public int need_clr; 25a5f0fb15SPaul Saab public int final_attr; 267f074f9cSXin LI public int at_prompt; 27a5f0fb15SPaul Saab 28a5f0fb15SPaul Saab extern int sigs; 29a5f0fb15SPaul Saab extern int sc_width; 30a5f0fb15SPaul Saab extern int so_s_width, so_e_width; 31a5f0fb15SPaul Saab extern int is_tty; 327f074f9cSXin LI extern int oldbot; 33*c77c4889SXin LI extern int utf_mode; 34d713e089SXin LI extern char intr_char; 35a5f0fb15SPaul Saab 367374caaaSXin LI #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 37000ba3e8STim J. Robbins extern int ctldisp; 38000ba3e8STim J. Robbins extern int nm_fg_color, nm_bg_color; 39000ba3e8STim J. Robbins extern int bo_fg_color, bo_bg_color; 40000ba3e8STim J. Robbins extern int ul_fg_color, ul_bg_color; 41000ba3e8STim J. Robbins extern int so_fg_color, so_bg_color; 42000ba3e8STim J. Robbins extern int bl_fg_color, bl_bg_color; 43f6b74a7dSXin LI extern int sgr_mode; 44b2ea2440SXin LI #if MSDOS_COMPILER==WIN32C 452235c7feSXin LI extern int vt_enabled; 46b2ea2440SXin LI #endif 47000ba3e8STim J. Robbins #endif 48000ba3e8STim J. Robbins 49a5f0fb15SPaul Saab /* 50a5f0fb15SPaul Saab * Display the line which is in the line buffer. 51a5f0fb15SPaul Saab */ 52d713e089SXin LI public void put_line(void) 53a5f0fb15SPaul Saab { 541ea31627SRobert Watson int c; 55*c77c4889SXin LI size_t i; 56a5f0fb15SPaul Saab int a; 57a5f0fb15SPaul Saab 58a5f0fb15SPaul Saab if (ABORT_SIGS()) 59a5f0fb15SPaul Saab { 60a5f0fb15SPaul Saab /* 61a5f0fb15SPaul Saab * Don't output if a signal is pending. 62a5f0fb15SPaul Saab */ 63*c77c4889SXin LI screen_trashed(); 64a5f0fb15SPaul Saab return; 65a5f0fb15SPaul Saab } 66a5f0fb15SPaul Saab 676dcb072bSXin LI final_attr = AT_NORMAL; 68a5f0fb15SPaul Saab 69a5f0fb15SPaul Saab for (i = 0; (c = gline(i, &a)) != '\0'; i++) 70a5f0fb15SPaul Saab { 716dcb072bSXin LI at_switch(a); 726dcb072bSXin LI final_attr = a; 73a5f0fb15SPaul Saab if (c == '\b') 74a5f0fb15SPaul Saab putbs(); 75a5f0fb15SPaul Saab else 76a5f0fb15SPaul Saab putchr(c); 77a5f0fb15SPaul Saab } 78a5f0fb15SPaul Saab 796dcb072bSXin LI at_exit(); 80a5f0fb15SPaul Saab } 81a5f0fb15SPaul Saab 82*c77c4889SXin LI /* 83*c77c4889SXin LI * win_flush has at least one non-critical issue when an escape sequence 84*c77c4889SXin LI * begins at the last char of the buffer, and possibly more issues. 85*c77c4889SXin LI * as a temporary measure to reduce likelyhood of encountering end-of-buffer 86*c77c4889SXin LI * issues till the SGR parser is replaced, OUTBUF_SIZE is 8K on Windows. 87*c77c4889SXin LI */ 88a5f0fb15SPaul Saab static char obuf[OUTBUF_SIZE]; 89a5f0fb15SPaul Saab static char *ob = obuf; 902235c7feSXin LI static int outfd = 2; /* stderr */ 91a5f0fb15SPaul Saab 927374caaaSXin LI #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 93*c77c4889SXin LI 94*c77c4889SXin LI typedef unsigned t_attr; 95*c77c4889SXin LI 96*c77c4889SXin LI #define A_BOLD (1u<<0) 97*c77c4889SXin LI #define A_ITALIC (1u<<1) 98*c77c4889SXin LI #define A_UNDERLINE (1u<<2) 99*c77c4889SXin LI #define A_BLINK (1u<<3) 100*c77c4889SXin LI #define A_INVERSE (1u<<4) 101*c77c4889SXin LI #define A_CONCEAL (1u<<5) 102*c77c4889SXin LI 103*c77c4889SXin LI /* long is guaranteed 32 bits, and we reserve bits for type + RGB */ 104*c77c4889SXin LI typedef unsigned long t_color; 105*c77c4889SXin LI 106*c77c4889SXin LI #define T_DEFAULT 0ul 107*c77c4889SXin LI #define T_ANSI 1ul /* colors 0-7 */ 108*c77c4889SXin LI 109*c77c4889SXin LI #define CGET_ANSI(c) ((c) & 0x7) 110*c77c4889SXin LI 111*c77c4889SXin LI #define C_DEFAULT (T_DEFAULT <<24) /* 0 */ 112*c77c4889SXin LI #define C_ANSI(c) ((T_ANSI <<24) | (c)) 113*c77c4889SXin LI 114*c77c4889SXin LI /* attr/fg/bg/all 0 is the default attr/fg/bg/all, respectively */ 115*c77c4889SXin LI typedef struct t_sgr { 116*c77c4889SXin LI t_attr attr; 117*c77c4889SXin LI t_color fg; 118*c77c4889SXin LI t_color bg; 119*c77c4889SXin LI } t_sgr; 120*c77c4889SXin LI 121*c77c4889SXin LI static constant t_sgr SGR_DEFAULT; /* = {0} */ 122*c77c4889SXin LI 123*c77c4889SXin LI /* returns 0 on success, non-0 on unknown SGR code */ 124*c77c4889SXin LI static int update_sgr(t_sgr *sgr, long code) 125a5f0fb15SPaul Saab { 126*c77c4889SXin LI switch (code) 127000ba3e8STim J. Robbins { 128*c77c4889SXin LI case 0: *sgr = SGR_DEFAULT; break; 129*c77c4889SXin LI 130*c77c4889SXin LI case 1: sgr->attr |= A_BOLD; break; 131*c77c4889SXin LI case 22: sgr->attr &= ~A_BOLD; break; 132*c77c4889SXin LI 133*c77c4889SXin LI case 3: sgr->attr |= A_ITALIC; break; 134*c77c4889SXin LI case 23: sgr->attr &= ~A_ITALIC; break; 135*c77c4889SXin LI 136*c77c4889SXin LI case 4: sgr->attr |= A_UNDERLINE; break; 137*c77c4889SXin LI case 24: sgr->attr &= ~A_UNDERLINE; break; 138*c77c4889SXin LI 139*c77c4889SXin LI case 6: /* fast-blink, fallthrough */ 140*c77c4889SXin LI case 5: sgr->attr |= A_BLINK; break; 141*c77c4889SXin LI case 25: sgr->attr &= ~A_BLINK; break; 142*c77c4889SXin LI 143*c77c4889SXin LI case 7: sgr->attr |= A_INVERSE; break; 144*c77c4889SXin LI case 27: sgr->attr &= ~A_INVERSE; break; 145*c77c4889SXin LI 146*c77c4889SXin LI case 8: sgr->attr |= A_CONCEAL; break; 147*c77c4889SXin LI case 28: sgr->attr &= ~A_CONCEAL; break; 148*c77c4889SXin LI 149*c77c4889SXin LI case 39: sgr->fg = C_DEFAULT; break; 150*c77c4889SXin LI case 49: sgr->bg = C_DEFAULT; break; 151*c77c4889SXin LI 152*c77c4889SXin LI case 30: case 31: case 32: case 33: 153*c77c4889SXin LI case 34: case 35: case 36: case 37: 154*c77c4889SXin LI sgr->fg = C_ANSI(code - 30); 155*c77c4889SXin LI break; 156*c77c4889SXin LI 157*c77c4889SXin LI case 40: case 41: case 42: case 43: 158*c77c4889SXin LI case 44: case 45: case 46: case 47: 159*c77c4889SXin LI sgr->bg = C_ANSI(code - 40); 160*c77c4889SXin LI break; 161*c77c4889SXin LI default: 162*c77c4889SXin LI return 1; 163*c77c4889SXin LI } 164*c77c4889SXin LI 165*c77c4889SXin LI return 0; 166*c77c4889SXin LI } 167*c77c4889SXin LI 168*c77c4889SXin LI static void set_win_colors(t_sgr *sgr) 169*c77c4889SXin LI { 1707374caaaSXin LI #if MSDOS_COMPILER==WIN32C 1717374caaaSXin LI /* Screen colors used by 3x and 4x SGR commands. */ 1727374caaaSXin LI static unsigned char screen_color[] = { 1737374caaaSXin LI 0, /* BLACK */ 1747374caaaSXin LI FOREGROUND_RED, 1757374caaaSXin LI FOREGROUND_GREEN, 1767374caaaSXin LI FOREGROUND_RED|FOREGROUND_GREEN, 1777374caaaSXin LI FOREGROUND_BLUE, 1787374caaaSXin LI FOREGROUND_BLUE|FOREGROUND_RED, 1797374caaaSXin LI FOREGROUND_BLUE|FOREGROUND_GREEN, 1807374caaaSXin LI FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED 1817374caaaSXin LI }; 1827374caaaSXin LI #else 183000ba3e8STim J. Robbins static enum COLORS screen_color[] = { 184000ba3e8STim J. Robbins BLACK, RED, GREEN, BROWN, 185000ba3e8STim J. Robbins BLUE, MAGENTA, CYAN, LIGHTGRAY 186000ba3e8STim J. Robbins }; 1877374caaaSXin LI #endif 188000ba3e8STim J. Robbins 189*c77c4889SXin LI int fg, bg, tmp; /* Windows colors */ 190*c77c4889SXin LI 191*c77c4889SXin LI /* Not "SGR mode": apply -D<x> to default fg+bg with one attribute */ 192*c77c4889SXin LI if (!sgr_mode && sgr->fg == C_DEFAULT && sgr->bg == C_DEFAULT) 193f6b74a7dSXin LI { 194*c77c4889SXin LI switch (sgr->attr) 195*c77c4889SXin LI { 196*c77c4889SXin LI case A_BOLD: 197*c77c4889SXin LI WIN32setcolors(bo_fg_color, bo_bg_color); 198*c77c4889SXin LI return; 199*c77c4889SXin LI case A_UNDERLINE: 200*c77c4889SXin LI WIN32setcolors(ul_fg_color, ul_bg_color); 201*c77c4889SXin LI return; 202*c77c4889SXin LI case A_BLINK: 203*c77c4889SXin LI WIN32setcolors(bl_fg_color, bl_bg_color); 204*c77c4889SXin LI return; 205*c77c4889SXin LI case A_INVERSE: 206*c77c4889SXin LI WIN32setcolors(so_fg_color, so_bg_color); 207*c77c4889SXin LI return; 208*c77c4889SXin LI /* 209*c77c4889SXin LI * There's no -Di so italic should not be here, but to 210*c77c4889SXin LI * preserve legacy behavior, apply -Ds to italic too. 211*c77c4889SXin LI */ 212*c77c4889SXin LI case A_ITALIC: 213*c77c4889SXin LI WIN32setcolors(so_fg_color, so_bg_color); 214*c77c4889SXin LI return; 215f6b74a7dSXin LI } 216*c77c4889SXin LI } 217*c77c4889SXin LI 218*c77c4889SXin LI /* generic application of the SGR state as Windows colors */ 219*c77c4889SXin LI 220*c77c4889SXin LI fg = sgr->fg == C_DEFAULT ? nm_fg_color 221*c77c4889SXin LI : screen_color[CGET_ANSI(sgr->fg)]; 222*c77c4889SXin LI 223*c77c4889SXin LI bg = sgr->bg == C_DEFAULT ? nm_bg_color 224*c77c4889SXin LI : screen_color[CGET_ANSI(sgr->bg)]; 225*c77c4889SXin LI 226*c77c4889SXin LI if (sgr->attr & A_BOLD) 227*c77c4889SXin LI fg |= 8; 228*c77c4889SXin LI 229*c77c4889SXin LI if (sgr->attr & (A_BLINK | A_UNDERLINE)) 230*c77c4889SXin LI bg |= 8; /* TODO: can be illegible */ 231*c77c4889SXin LI 232*c77c4889SXin LI if (sgr->attr & (A_INVERSE | A_ITALIC)) 233*c77c4889SXin LI { 234*c77c4889SXin LI tmp = fg; 235*c77c4889SXin LI fg = bg; 236*c77c4889SXin LI bg = tmp; 237*c77c4889SXin LI } 238*c77c4889SXin LI 239*c77c4889SXin LI if (sgr->attr & A_CONCEAL) 240*c77c4889SXin LI fg = bg ^ 8; 241*c77c4889SXin LI 242*c77c4889SXin LI WIN32setcolors(fg, bg); 243*c77c4889SXin LI } 244*c77c4889SXin LI 245*c77c4889SXin LI /* like is_ansi_end, but doesn't assume c != 0 (returns 0 for c == 0) */ 246*c77c4889SXin LI static int is_ansi_end_0(char c) 247*c77c4889SXin LI { 248*c77c4889SXin LI return c && is_ansi_end((unsigned char)c); 249*c77c4889SXin LI } 250*c77c4889SXin LI 251*c77c4889SXin LI static void win_flush(void) 252*c77c4889SXin LI { 253*c77c4889SXin LI if (ctldisp != OPT_ONPLUS 254*c77c4889SXin LI #if MSDOS_COMPILER==WIN32C 255*c77c4889SXin LI || (vt_enabled && sgr_mode) 256*c77c4889SXin LI #endif 257*c77c4889SXin LI ) 258*c77c4889SXin LI WIN32textout(obuf, ptr_diff(ob, obuf)); 259*c77c4889SXin LI else 260*c77c4889SXin LI { 261*c77c4889SXin LI /* 262*c77c4889SXin LI * Digest text, apply embedded SGR sequences as Windows-colors. 263*c77c4889SXin LI * By default - when -Da ("SGR mode") is unset - also apply 264*c77c4889SXin LI * translation of -D command-line options (at set_win_colors) 265*c77c4889SXin LI */ 266*c77c4889SXin LI char *anchor, *p, *p_next; 267*c77c4889SXin LI static t_sgr sgr; 268*c77c4889SXin LI 269*c77c4889SXin LI /* when unsupported SGR value is encountered, like 38/48 for 270*c77c4889SXin LI * 256/true colors, then we abort processing this sequence, 271*c77c4889SXin LI * because it may expect followup values, but we don't know 272*c77c4889SXin LI * how many, so we've lost sync of this sequence parsing. 273*c77c4889SXin LI * Without VT enabled it's OK because we can't do much anyway, 274*c77c4889SXin LI * but with VT enabled we choose to passthrough this sequence 275*c77c4889SXin LI * to the terminal - which can handle it better than us. 276*c77c4889SXin LI * however, this means that our "sgr" var is no longer in sync 277*c77c4889SXin LI * with the actual terminal state, which can lead to broken 278*c77c4889SXin LI * colors with future sequences which we _can_ fully parse. 279*c77c4889SXin LI * in such case, once it happens, we keep passthrough sequences 280*c77c4889SXin LI * until we know we're in sync again - on a valid reset. 281*c77c4889SXin LI */ 282*c77c4889SXin LI static int sgr_bad_sync; 283*c77c4889SXin LI 284000ba3e8STim J. Robbins for (anchor = p_next = obuf; 2857374caaaSXin LI (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; ) 286000ba3e8STim J. Robbins { 287000ba3e8STim J. Robbins p = p_next; 2887374caaaSXin LI if (p[1] == '[') /* "ESC-[" sequence */ 289000ba3e8STim J. Robbins { 290*c77c4889SXin LI /* 291*c77c4889SXin LI * unknown SGR code ignores the rest of the seq, 292*c77c4889SXin LI * and allows ignoring sequences such as 293*c77c4889SXin LI * ^[[38;5;123m or ^[[38;2;5;6;7m 294*c77c4889SXin LI * (prior known codes at the same seq do apply) 295*c77c4889SXin LI */ 296*c77c4889SXin LI int bad_code = 0; 297*c77c4889SXin LI 2987374caaaSXin LI if (p > anchor) 299000ba3e8STim J. Robbins { 300000ba3e8STim J. Robbins /* 301000ba3e8STim J. Robbins * If some chars seen since 302000ba3e8STim J. Robbins * the last escape sequence, 3037374caaaSXin LI * write them out to the screen. 304000ba3e8STim J. Robbins */ 305*c77c4889SXin LI WIN32textout(anchor, ptr_diff(p, anchor)); 306000ba3e8STim J. Robbins anchor = p; 307000ba3e8STim J. Robbins } 3087374caaaSXin LI p += 2; /* Skip the "ESC-[" */ 309*c77c4889SXin LI if (is_ansi_end_0(*p)) 3107374caaaSXin LI { 3117374caaaSXin LI /* 3127374caaaSXin LI * Handle null escape sequence 313*c77c4889SXin LI * "ESC[m" as if it was "ESC[0m" 3147374caaaSXin LI */ 3157374caaaSXin LI p++; 3167374caaaSXin LI anchor = p_next = p; 317*c77c4889SXin LI update_sgr(&sgr, 0); 318*c77c4889SXin LI set_win_colors(&sgr); 319*c77c4889SXin LI sgr_bad_sync = 0; 3207374caaaSXin LI continue; 3217374caaaSXin LI } 322000ba3e8STim J. Robbins p_next = p; 3237374caaaSXin LI 3247374caaaSXin LI /* 325*c77c4889SXin LI * Parse and apply SGR values to the SGR state 3267374caaaSXin LI * based on the escape sequence. 3277374caaaSXin LI */ 328*c77c4889SXin LI while (!is_ansi_end_0(*p)) 329000ba3e8STim J. Robbins { 330000ba3e8STim J. Robbins char *q; 331000ba3e8STim J. Robbins long code = strtol(p, &q, 10); 332000ba3e8STim J. Robbins 3337374caaaSXin LI if (*q == '\0') 334000ba3e8STim J. Robbins { 335000ba3e8STim J. Robbins /* 336000ba3e8STim J. Robbins * Incomplete sequence. 337000ba3e8STim J. Robbins * Leave it unprocessed 338000ba3e8STim J. Robbins * in the buffer. 339000ba3e8STim J. Robbins */ 340*c77c4889SXin LI size_t slop = ptr_diff(q, anchor); 341*c77c4889SXin LI memmove(obuf, anchor, slop); 342000ba3e8STim J. Robbins ob = &obuf[slop]; 343000ba3e8STim J. Robbins return; 344000ba3e8STim J. Robbins } 345000ba3e8STim J. Robbins 3467374caaaSXin LI if (q == p || 347*c77c4889SXin LI (!is_ansi_end_0(*q) && *q != ';')) 348000ba3e8STim J. Robbins { 349*c77c4889SXin LI /* 350*c77c4889SXin LI * can't parse. passthrough 351*c77c4889SXin LI * till the end of the buffer 352*c77c4889SXin LI */ 353000ba3e8STim J. Robbins p_next = q; 354000ba3e8STim J. Robbins break; 355000ba3e8STim J. Robbins } 356000ba3e8STim J. Robbins if (*q == ';') 357000ba3e8STim J. Robbins q++; 358000ba3e8STim J. Robbins 359*c77c4889SXin LI if (!bad_code) 360*c77c4889SXin LI bad_code = update_sgr(&sgr, code); 361*c77c4889SXin LI 362*c77c4889SXin LI if (bad_code) 363*c77c4889SXin LI sgr_bad_sync = 1; 364*c77c4889SXin LI else if (code == 0) 365*c77c4889SXin LI sgr_bad_sync = 0; 366*c77c4889SXin LI 367000ba3e8STim J. Robbins p = q; 368000ba3e8STim J. Robbins } 369*c77c4889SXin LI if (!is_ansi_end_0(*p) || p == p_next) 370000ba3e8STim J. Robbins break; 371*c77c4889SXin LI 372*c77c4889SXin LI if (sgr_bad_sync && vt_enabled) { 373*c77c4889SXin LI /* this or a prior sequence had unknown 374*c77c4889SXin LI * SGR value. passthrough all sequences 375*c77c4889SXin LI * until we're in-sync again 376f6b74a7dSXin LI */ 377*c77c4889SXin LI WIN32textout(anchor, ptr_diff(p+1, anchor)); 378*c77c4889SXin LI } else { 379*c77c4889SXin LI set_win_colors(&sgr); 380f6b74a7dSXin LI } 3817374caaaSXin LI p_next = anchor = p + 1; 382000ba3e8STim J. Robbins } else 383000ba3e8STim J. Robbins p_next++; 384000ba3e8STim J. Robbins } 385000ba3e8STim J. Robbins 386000ba3e8STim J. Robbins /* Output what's left in the buffer. */ 387*c77c4889SXin LI WIN32textout(anchor, ptr_diff(ob, anchor)); 388000ba3e8STim J. Robbins } 389a5f0fb15SPaul Saab ob = obuf; 3902235c7feSXin LI } 3912235c7feSXin LI #endif 3922235c7feSXin LI 3932235c7feSXin LI /* 3942235c7feSXin LI * Flush buffered output. 3952235c7feSXin LI * 3962235c7feSXin LI * If we haven't displayed any file data yet, 3972235c7feSXin LI * output messages on error output (file descriptor 2), 3982235c7feSXin LI * otherwise output on standard output (file descriptor 1). 3992235c7feSXin LI * 4002235c7feSXin LI * This has the desirable effect of producing all 4012235c7feSXin LI * error messages on error output if standard output 4022235c7feSXin LI * is directed to a file. It also does the same if 4032235c7feSXin LI * we never produce any real output; for example, if 4042235c7feSXin LI * the input file(s) cannot be opened. If we do 4052235c7feSXin LI * eventually produce output, code in edit() makes 4062235c7feSXin LI * sure these messages can be seen before they are 4072235c7feSXin LI * overwritten or scrolled away. 4082235c7feSXin LI */ 409d713e089SXin LI public void flush(void) 4102235c7feSXin LI { 411*c77c4889SXin LI size_t n; 4122235c7feSXin LI 413*c77c4889SXin LI n = ptr_diff(ob, obuf); 4142235c7feSXin LI if (n == 0) 4152235c7feSXin LI return; 4162235c7feSXin LI ob = obuf; 4172235c7feSXin LI 4182235c7feSXin LI #if MSDOS_COMPILER==MSOFTC 4192235c7feSXin LI if (interactive()) 4202235c7feSXin LI { 4212235c7feSXin LI obuf[n] = '\0'; 4222235c7feSXin LI _outtext(obuf); 4232235c7feSXin LI return; 4242235c7feSXin LI } 4252235c7feSXin LI #else 4262235c7feSXin LI #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 4272235c7feSXin LI if (interactive()) 4282235c7feSXin LI { 4292235c7feSXin LI ob = obuf + n; 4302235c7feSXin LI *ob = '\0'; 4312235c7feSXin LI win_flush(); 432a5f0fb15SPaul Saab return; 433a5f0fb15SPaul Saab } 434a5f0fb15SPaul Saab #endif 435a5f0fb15SPaul Saab #endif 4362235c7feSXin LI 4372235c7feSXin LI if (write(outfd, obuf, n) != n) 438*c77c4889SXin LI screen_trashed(); 4392235c7feSXin LI } 4402235c7feSXin LI 4412235c7feSXin LI /* 4422235c7feSXin LI * Set the output file descriptor (1=stdout or 2=stderr). 4432235c7feSXin LI */ 444d713e089SXin LI public void set_output(int fd) 4452235c7feSXin LI { 4462235c7feSXin LI flush(); 4472235c7feSXin LI outfd = fd; 448a5f0fb15SPaul Saab } 449a5f0fb15SPaul Saab 450a5f0fb15SPaul Saab /* 451a5f0fb15SPaul Saab * Output a character. 452*c77c4889SXin LI * ch is int for compatibility with tputs. 453a5f0fb15SPaul Saab */ 454*c77c4889SXin LI public int putchr(int ch) 455a5f0fb15SPaul Saab { 456*c77c4889SXin LI char c = (char) ch; 4576dcb072bSXin LI #if 0 /* fake UTF-8 output for testing */ 4586dcb072bSXin LI extern int utf_mode; 4596dcb072bSXin LI if (utf_mode) 4606dcb072bSXin LI { 4616dcb072bSXin LI static char ubuf[MAX_UTF_CHAR_LEN]; 4626dcb072bSXin LI static int ubuf_len = 0; 4636dcb072bSXin LI static int ubuf_index = 0; 4646dcb072bSXin LI if (ubuf_len == 0) 4656dcb072bSXin LI { 4666dcb072bSXin LI ubuf_len = utf_len(c); 4676dcb072bSXin LI ubuf_index = 0; 4686dcb072bSXin LI } 4696dcb072bSXin LI ubuf[ubuf_index++] = c; 4706dcb072bSXin LI if (ubuf_index < ubuf_len) 4716dcb072bSXin LI return c; 4726dcb072bSXin LI c = get_wchar(ubuf) & 0xFF; 4736dcb072bSXin LI ubuf_len = 0; 4746dcb072bSXin LI } 4756dcb072bSXin LI #endif 47630a1828cSXin LI clear_bot_if_needed(); 477a5f0fb15SPaul Saab #if MSDOS_COMPILER 478a5f0fb15SPaul Saab if (c == '\n' && is_tty) 479a5f0fb15SPaul Saab { 480a5f0fb15SPaul Saab /* remove_top(1); */ 481a5f0fb15SPaul Saab putchr('\r'); 482a5f0fb15SPaul Saab } 483a5f0fb15SPaul Saab #else 484a5f0fb15SPaul Saab #ifdef _OSK 485a5f0fb15SPaul Saab if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */ 486a5f0fb15SPaul Saab putchr(0x0A); 487a5f0fb15SPaul Saab #endif 488a5f0fb15SPaul Saab #endif 489a5f0fb15SPaul Saab /* 490a5f0fb15SPaul Saab * Some versions of flush() write to *ob, so we must flush 491a5f0fb15SPaul Saab * when we are still one char from the end of obuf. 492a5f0fb15SPaul Saab */ 493a5f0fb15SPaul Saab if (ob >= &obuf[sizeof(obuf)-1]) 494a5f0fb15SPaul Saab flush(); 495a5f0fb15SPaul Saab *ob++ = c; 4967f074f9cSXin LI at_prompt = 0; 497a5f0fb15SPaul Saab return (c); 498a5f0fb15SPaul Saab } 499a5f0fb15SPaul Saab 500d713e089SXin LI public void clear_bot_if_needed(void) 50130a1828cSXin LI { 50230a1828cSXin LI if (!need_clr) 50330a1828cSXin LI return; 50430a1828cSXin LI need_clr = 0; 50530a1828cSXin LI clear_bot(); 50630a1828cSXin LI } 50730a1828cSXin LI 508a5f0fb15SPaul Saab /* 509a5f0fb15SPaul Saab * Output a string. 510a5f0fb15SPaul Saab */ 511d713e089SXin LI public void putstr(constant char *s) 512a5f0fb15SPaul Saab { 513a5f0fb15SPaul Saab while (*s != '\0') 514a5f0fb15SPaul Saab putchr(*s++); 515a5f0fb15SPaul Saab } 516a5f0fb15SPaul Saab 517a5f0fb15SPaul Saab 518a5f0fb15SPaul Saab /* 519000ba3e8STim J. Robbins * Convert an integral type to a string. 520000ba3e8STim J. Robbins */ 521000ba3e8STim J. Robbins #define TYPE_TO_A_FUNC(funcname, type) \ 522d713e089SXin LI void funcname(type num, char *buf, int radix) \ 523000ba3e8STim J. Robbins { \ 524000ba3e8STim J. Robbins int neg = (num < 0); \ 525000ba3e8STim J. Robbins char tbuf[INT_STRLEN_BOUND(num)+2]; \ 5261ea31627SRobert Watson char *s = tbuf + sizeof(tbuf); \ 527000ba3e8STim J. Robbins if (neg) num = -num; \ 528000ba3e8STim J. Robbins *--s = '\0'; \ 529000ba3e8STim J. Robbins do { \ 530d713e089SXin LI *--s = "0123456789ABCDEF"[num % radix]; \ 531d713e089SXin LI } while ((num /= radix) != 0); \ 532000ba3e8STim J. Robbins if (neg) *--s = '-'; \ 533000ba3e8STim J. Robbins strcpy(buf, s); \ 534000ba3e8STim J. Robbins } 535000ba3e8STim J. Robbins 536000ba3e8STim J. Robbins TYPE_TO_A_FUNC(postoa, POSITION) 537000ba3e8STim J. Robbins TYPE_TO_A_FUNC(linenumtoa, LINENUM) 538000ba3e8STim J. Robbins TYPE_TO_A_FUNC(inttoa, int) 539000ba3e8STim J. Robbins 540000ba3e8STim J. Robbins /* 541d713e089SXin LI * Convert a string to an integral type. Return ((type) -1) on overflow. 542b7780dbeSXin LI */ 543*c77c4889SXin LI #define STR_TO_TYPE_FUNC(funcname, cfuncname, type) \ 544*c77c4889SXin LI type cfuncname(constant char *buf, constant char **ebuf, int radix) \ 545b7780dbeSXin LI { \ 546b7780dbeSXin LI type val = 0; \ 547*c77c4889SXin LI lbool v = 0; \ 5482235c7feSXin LI for (;; buf++) { \ 5492235c7feSXin LI char c = *buf; \ 550d713e089SXin LI int digit = (c >= '0' && c <= '9') ? c - '0' : (c >= 'a' && c <= 'f') ? c - 'a' + 10 : (c >= 'A' && c <= 'F') ? c - 'A' + 10 : -1; \ 551d713e089SXin LI if (digit < 0 || digit >= radix) break; \ 552*c77c4889SXin LI v = v || ckd_mul(&val, val, radix); \ 553*c77c4889SXin LI v = v || ckd_add(&val, val, digit); \ 554b7780dbeSXin LI } \ 555b7780dbeSXin LI if (ebuf != NULL) *ebuf = buf; \ 556*c77c4889SXin LI return v ? (type)(-1) : val; \ 557*c77c4889SXin LI } \ 558*c77c4889SXin LI type funcname(char *buf, char **ebuf, int radix) \ 559*c77c4889SXin LI { \ 560*c77c4889SXin LI constant char *cbuf = buf; \ 561*c77c4889SXin LI type r = cfuncname(cbuf, &cbuf, radix); \ 562*c77c4889SXin LI if (ebuf != NULL) *ebuf = (char *) cbuf; /*{{const-issue}}*/ \ 563*c77c4889SXin LI return r; \ 564b7780dbeSXin LI } 565b7780dbeSXin LI 566*c77c4889SXin LI STR_TO_TYPE_FUNC(lstrtopos, lstrtoposc, POSITION) 567*c77c4889SXin LI STR_TO_TYPE_FUNC(lstrtoi, lstrtoic, int) 568*c77c4889SXin LI STR_TO_TYPE_FUNC(lstrtoul, lstrtoulc, unsigned long) 569b7780dbeSXin LI 570b7780dbeSXin LI /* 571d713e089SXin LI * Print an integral type. 572a5f0fb15SPaul Saab */ 573d713e089SXin LI #define IPRINT_FUNC(funcname, type, typetoa) \ 574d713e089SXin LI static int funcname(type num, int radix) \ 575d713e089SXin LI { \ 576d713e089SXin LI char buf[INT_STRLEN_BOUND(num)]; \ 577d713e089SXin LI typetoa(num, buf, radix); \ 578d713e089SXin LI putstr(buf); \ 579d713e089SXin LI return (int) strlen(buf); \ 580000ba3e8STim J. Robbins } 581a5f0fb15SPaul Saab 582d713e089SXin LI IPRINT_FUNC(iprint_int, int, inttoa) 583d713e089SXin LI IPRINT_FUNC(iprint_linenum, LINENUM, linenumtoa) 584a5f0fb15SPaul Saab 585a5f0fb15SPaul Saab /* 586a5f0fb15SPaul Saab * This function implements printf-like functionality 587a5f0fb15SPaul Saab * using a more portable argument list mechanism than printf's. 5882235c7feSXin LI * 5892235c7feSXin LI * {{ This paranoia about the portability of printf dates from experiences 5902235c7feSXin LI * with systems in the 1980s and is of course no longer necessary. }} 591a5f0fb15SPaul Saab */ 592*c77c4889SXin LI public int less_printf(constant char *fmt, PARG *parg) 593a5f0fb15SPaul Saab { 594*c77c4889SXin LI constant char *s; 595*c77c4889SXin LI constant char *es; 5961ea31627SRobert Watson int col; 597a5f0fb15SPaul Saab 598a5f0fb15SPaul Saab col = 0; 599a5f0fb15SPaul Saab while (*fmt != '\0') 600a5f0fb15SPaul Saab { 601a5f0fb15SPaul Saab if (*fmt != '%') 602a5f0fb15SPaul Saab { 603a5f0fb15SPaul Saab putchr(*fmt++); 604a5f0fb15SPaul Saab col++; 605a5f0fb15SPaul Saab } else 606a5f0fb15SPaul Saab { 607a5f0fb15SPaul Saab ++fmt; 608000ba3e8STim J. Robbins switch (*fmt++) 609000ba3e8STim J. Robbins { 610a5f0fb15SPaul Saab case 's': 611a5f0fb15SPaul Saab s = parg->p_string; 612*c77c4889SXin LI es = s + strlen(s); 613a5f0fb15SPaul Saab parg++; 614a5f0fb15SPaul Saab while (*s != '\0') 615a5f0fb15SPaul Saab { 616*c77c4889SXin LI LWCHAR ch = step_charc(&s, +1, es); 617*c77c4889SXin LI constant char *ps = utf_mode ? prutfchar(ch) : prchar(ch); 618*c77c4889SXin LI while (*ps != '\0') 619*c77c4889SXin LI { 620*c77c4889SXin LI putchr(*ps++); 621a5f0fb15SPaul Saab col++; 622a5f0fb15SPaul Saab } 623*c77c4889SXin LI } 624a5f0fb15SPaul Saab break; 625a5f0fb15SPaul Saab case 'd': 626d713e089SXin LI col += iprint_int(parg->p_int, 10); 627d713e089SXin LI parg++; 628d713e089SXin LI break; 629d713e089SXin LI case 'x': 630d713e089SXin LI col += iprint_int(parg->p_int, 16); 631a5f0fb15SPaul Saab parg++; 632000ba3e8STim J. Robbins break; 633000ba3e8STim J. Robbins case 'n': 634d713e089SXin LI col += iprint_linenum(parg->p_linenum, 10); 635000ba3e8STim J. Robbins parg++; 636a5f0fb15SPaul Saab break; 6372235c7feSXin LI case 'c': 638*c77c4889SXin LI s = prchar((LWCHAR) parg->p_char); 63995270f73SXin LI parg++; 64095270f73SXin LI while (*s != '\0') 64195270f73SXin LI { 64295270f73SXin LI putchr(*s++); 6432235c7feSXin LI col++; 64495270f73SXin LI } 6452235c7feSXin LI break; 646b2ea2440SXin LI case '%': 647b2ea2440SXin LI putchr('%'); 648b2ea2440SXin LI break; 649a5f0fb15SPaul Saab } 650a5f0fb15SPaul Saab } 651a5f0fb15SPaul Saab } 652a5f0fb15SPaul Saab return (col); 653a5f0fb15SPaul Saab } 654a5f0fb15SPaul Saab 655a5f0fb15SPaul Saab /* 656a5f0fb15SPaul Saab * Get a RETURN. 657a5f0fb15SPaul Saab * If some other non-trivial char is pressed, unget it, so it will 658a5f0fb15SPaul Saab * become the next command. 659a5f0fb15SPaul Saab */ 660d713e089SXin LI public void get_return(void) 661a5f0fb15SPaul Saab { 662a5f0fb15SPaul Saab int c; 663a5f0fb15SPaul Saab 664a5f0fb15SPaul Saab #if ONLY_RETURN 665a5f0fb15SPaul Saab while ((c = getchr()) != '\n' && c != '\r') 666a5f0fb15SPaul Saab bell(); 667a5f0fb15SPaul Saab #else 668a5f0fb15SPaul Saab c = getchr(); 669a5f0fb15SPaul Saab if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR) 670*c77c4889SXin LI ungetcc((char) c); 671a5f0fb15SPaul Saab #endif 672a5f0fb15SPaul Saab } 673a5f0fb15SPaul Saab 674a5f0fb15SPaul Saab /* 675a5f0fb15SPaul Saab * Output a message in the lower left corner of the screen 676a5f0fb15SPaul Saab * and wait for carriage return. 677a5f0fb15SPaul Saab */ 678*c77c4889SXin LI public void error(constant char *fmt, PARG *parg) 679a5f0fb15SPaul Saab { 680a5f0fb15SPaul Saab int col = 0; 681a5f0fb15SPaul Saab static char return_to_continue[] = " (press RETURN)"; 682a5f0fb15SPaul Saab 683a5f0fb15SPaul Saab errmsgs++; 684a5f0fb15SPaul Saab 6852235c7feSXin LI if (!interactive()) 686a5f0fb15SPaul Saab { 6872235c7feSXin LI less_printf(fmt, parg); 688a5f0fb15SPaul Saab putchr('\n'); 689a5f0fb15SPaul Saab return; 690a5f0fb15SPaul Saab } 691a5f0fb15SPaul Saab 6922235c7feSXin LI if (!oldbot) 6932235c7feSXin LI squish_check(); 6942235c7feSXin LI at_exit(); 6952235c7feSXin LI clear_bot(); 6962235c7feSXin LI at_enter(AT_STANDOUT|AT_COLOR_ERROR); 6972235c7feSXin LI col += so_s_width; 6982235c7feSXin LI col += less_printf(fmt, parg); 699a5f0fb15SPaul Saab putstr(return_to_continue); 7006dcb072bSXin LI at_exit(); 701*c77c4889SXin LI col += (int) sizeof(return_to_continue) + so_e_width; 702a5f0fb15SPaul Saab 703a5f0fb15SPaul Saab get_return(); 704a5f0fb15SPaul Saab lower_left(); 70533096f16SXin LI clear_eol(); 706a5f0fb15SPaul Saab 707a5f0fb15SPaul Saab if (col >= sc_width) 708a5f0fb15SPaul Saab /* 709a5f0fb15SPaul Saab * Printing the message has probably scrolled the screen. 710a5f0fb15SPaul Saab * {{ Unless the terminal doesn't have auto margins, 711a5f0fb15SPaul Saab * in which case we just hammered on the right margin. }} 712a5f0fb15SPaul Saab */ 713*c77c4889SXin LI screen_trashed(); 714a5f0fb15SPaul Saab 715a5f0fb15SPaul Saab flush(); 716a5f0fb15SPaul Saab } 717a5f0fb15SPaul Saab 718a5f0fb15SPaul Saab /* 719a5f0fb15SPaul Saab * Output a message in the lower left corner of the screen 720a5f0fb15SPaul Saab * and don't wait for carriage return. 721a5f0fb15SPaul Saab * Usually used to warn that we are beginning a potentially 722a5f0fb15SPaul Saab * time-consuming operation. 723a5f0fb15SPaul Saab */ 724*c77c4889SXin LI static void ierror_suffix(constant char *fmt, PARG *parg, constant char *suffix1, constant char *suffix2, constant char *suffix3) 725a5f0fb15SPaul Saab { 7266dcb072bSXin LI at_exit(); 727a5f0fb15SPaul Saab clear_bot(); 7282235c7feSXin LI at_enter(AT_STANDOUT|AT_COLOR_ERROR); 729a5f0fb15SPaul Saab (void) less_printf(fmt, parg); 730d713e089SXin LI putstr(suffix1); 731d713e089SXin LI putstr(suffix2); 732d713e089SXin LI putstr(suffix3); 7336dcb072bSXin LI at_exit(); 734a5f0fb15SPaul Saab flush(); 735a5f0fb15SPaul Saab need_clr = 1; 736a5f0fb15SPaul Saab } 737a5f0fb15SPaul Saab 738*c77c4889SXin LI public void ierror(constant char *fmt, PARG *parg) 739d713e089SXin LI { 740d713e089SXin LI ierror_suffix(fmt, parg, "... (interrupt to abort)", "", ""); 741d713e089SXin LI } 742d713e089SXin LI 743*c77c4889SXin LI public void ixerror(constant char *fmt, PARG *parg) 744d713e089SXin LI { 745d713e089SXin LI if (!supports_ctrl_x()) 746d713e089SXin LI ierror(fmt, parg); 747d713e089SXin LI else 748*c77c4889SXin LI { 749*c77c4889SXin LI char ichar[MAX_PRCHAR_LEN+1]; 750*c77c4889SXin LI strcpy(ichar, prchar((LWCHAR) intr_char)); 751*c77c4889SXin LI ierror_suffix(fmt, parg, "... (", ichar, " or interrupt to abort)"); 752*c77c4889SXin LI } 753d713e089SXin LI } 754d713e089SXin LI 755a5f0fb15SPaul Saab /* 756a5f0fb15SPaul Saab * Output a message in the lower left corner of the screen 757a5f0fb15SPaul Saab * and return a single-character response. 758a5f0fb15SPaul Saab */ 759*c77c4889SXin LI public int query(constant char *fmt, PARG *parg) 760a5f0fb15SPaul Saab { 7611ea31627SRobert Watson int c; 762a5f0fb15SPaul Saab int col = 0; 763a5f0fb15SPaul Saab 7642235c7feSXin LI if (interactive()) 765a5f0fb15SPaul Saab clear_bot(); 766a5f0fb15SPaul Saab 767a5f0fb15SPaul Saab (void) less_printf(fmt, parg); 768a5f0fb15SPaul Saab c = getchr(); 769a5f0fb15SPaul Saab 7702235c7feSXin LI if (interactive()) 771a5f0fb15SPaul Saab { 772a5f0fb15SPaul Saab lower_left(); 773a5f0fb15SPaul Saab if (col >= sc_width) 774*c77c4889SXin LI screen_trashed(); 775a5f0fb15SPaul Saab flush(); 7762235c7feSXin LI } else 7772235c7feSXin LI { 7782235c7feSXin LI putchr('\n'); 7792235c7feSXin LI } 780a5f0fb15SPaul Saab 7812235c7feSXin LI if (c == 'Q') 7822235c7feSXin LI quit(QUIT_OK); 783a5f0fb15SPaul Saab return (c); 784a5f0fb15SPaul Saab } 785