1 /* 2 * Copyright (C) 1984-2000 Mark Nudelman 3 * 4 * You may distribute under the terms of either the GNU General Public 5 * License or the Less License, as specified in the README file. 6 * 7 * For more information about less, or for information on how to 8 * contact the author, see the README file. 9 */ 10 11 12 /* 13 * High level routines dealing with the output to the screen. 14 */ 15 16 #include "less.h" 17 #if MSDOS_COMPILER==WIN32C 18 #include "windows.h" 19 #endif 20 21 public int errmsgs; /* Count of messages displayed by error() */ 22 public int need_clr; 23 public int final_attr; 24 25 extern int sigs; 26 extern int sc_width; 27 extern int so_s_width, so_e_width; 28 extern int screen_trashed; 29 extern int any_display; 30 extern int is_tty; 31 32 /* 33 * Display the line which is in the line buffer. 34 */ 35 public void 36 put_line() 37 { 38 register int c; 39 register int i; 40 int a; 41 int curr_attr; 42 43 if (ABORT_SIGS()) 44 { 45 /* 46 * Don't output if a signal is pending. 47 */ 48 screen_trashed = 1; 49 return; 50 } 51 52 curr_attr = AT_NORMAL; 53 54 for (i = 0; (c = gline(i, &a)) != '\0'; i++) 55 { 56 if (a != curr_attr) 57 { 58 /* 59 * Changing attributes. 60 * Display the exit sequence for the old attribute 61 * and the enter sequence for the new one. 62 */ 63 switch (curr_attr) 64 { 65 case AT_UNDERLINE: ul_exit(); break; 66 case AT_BOLD: bo_exit(); break; 67 case AT_BLINK: bl_exit(); break; 68 case AT_STANDOUT: so_exit(); break; 69 } 70 switch (a) 71 { 72 case AT_UNDERLINE: ul_enter(); break; 73 case AT_BOLD: bo_enter(); break; 74 case AT_BLINK: bl_enter(); break; 75 case AT_STANDOUT: so_enter(); break; 76 } 77 curr_attr = a; 78 } 79 if (curr_attr == AT_INVIS) 80 continue; 81 if (c == '\b') 82 putbs(); 83 else 84 putchr(c); 85 } 86 87 switch (curr_attr) 88 { 89 case AT_UNDERLINE: ul_exit(); break; 90 case AT_BOLD: bo_exit(); break; 91 case AT_BLINK: bl_exit(); break; 92 case AT_STANDOUT: so_exit(); break; 93 } 94 final_attr = curr_attr; 95 } 96 97 static char obuf[OUTBUF_SIZE]; 98 static char *ob = obuf; 99 100 /* 101 * Flush buffered output. 102 * 103 * If we haven't displayed any file data yet, 104 * output messages on error output (file descriptor 2), 105 * otherwise output on standard output (file descriptor 1). 106 * 107 * This has the desirable effect of producing all 108 * error messages on error output if standard output 109 * is directed to a file. It also does the same if 110 * we never produce any real output; for example, if 111 * the input file(s) cannot be opened. If we do 112 * eventually produce output, code in edit() makes 113 * sure these messages can be seen before they are 114 * overwritten or scrolled away. 115 */ 116 public void 117 flush() 118 { 119 register int n; 120 register int fd; 121 122 n = ob - obuf; 123 if (n == 0) 124 return; 125 #if MSDOS_COMPILER==WIN32C 126 if (is_tty && any_display) 127 { 128 char *p; 129 char *op; 130 DWORD nwritten = 0; 131 CONSOLE_SCREEN_BUFFER_INFO scr; 132 DWORD nchars; 133 COORD cpos; 134 WORD nm_attr; 135 int olen; 136 extern HANDLE con_out; 137 extern int nm_fg_color; 138 extern int nm_bg_color; 139 #define MAKEATTR(fg,bg) ((WORD)((fg)|((bg)<<4))) 140 141 *ob = '\0'; 142 olen = ob - obuf; 143 /* 144 * To avoid color problems, if we're scrolling the screen, 145 * we write only up to the char that causes the scroll, 146 * (a newline or a char in the last column), then fill 147 * the bottom line with the "normal" attribute, then 148 * write the rest. 149 * When Windows scrolls, it takes the attributes for the 150 * new line from the first char of the (previously) 151 * bottom line. 152 * 153 * {{ This still doesn't work correctly in all cases! }} 154 */ 155 nm_attr = MAKEATTR(nm_fg_color, nm_bg_color); 156 for (op = obuf; *op != '\0'; ) 157 { 158 GetConsoleScreenBufferInfo(con_out, &scr); 159 /* Find the next newline. */ 160 p = strchr(op, '\n'); 161 if (p == NULL && 162 scr.dwCursorPosition.X + olen >= sc_width) 163 { 164 /* 165 * No newline, but writing in the 166 * last column causes scrolling. 167 */ 168 p = op + sc_width - scr.dwCursorPosition.X - 1; 169 } 170 if (scr.dwCursorPosition.Y != scr.srWindow.Bottom || 171 p == NULL) 172 { 173 /* Write the entire buffer. */ 174 WriteConsole(con_out, op, olen, 175 &nwritten, NULL); 176 op += olen; 177 } else 178 { 179 /* Write only up to the scrolling char. */ 180 WriteConsole(con_out, op, p - op + 1, 181 &nwritten, NULL); 182 cpos.X = 0; 183 cpos.Y = scr.dwCursorPosition.Y; 184 FillConsoleOutputAttribute(con_out, nm_attr, 185 sc_width, cpos, &nchars); 186 olen -= p - op + 1; 187 op = p + 1; 188 } 189 } 190 ob = obuf; 191 return; 192 } 193 #else 194 #if MSDOS_COMPILER==MSOFTC 195 if (is_tty && any_display) 196 { 197 *ob = '\0'; 198 _outtext(obuf); 199 ob = obuf; 200 return; 201 } 202 #else 203 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 204 if (is_tty && any_display) 205 { 206 *ob = '\0'; 207 cputs(obuf); 208 ob = obuf; 209 return; 210 } 211 #endif 212 #endif 213 #endif 214 fd = (any_display) ? 1 : 2; 215 if (write(fd, obuf, n) != n) 216 screen_trashed = 1; 217 ob = obuf; 218 } 219 220 /* 221 * Output a character. 222 */ 223 public int 224 putchr(c) 225 int c; 226 { 227 if (need_clr) 228 { 229 need_clr = 0; 230 clear_bot(); 231 } 232 #if MSDOS_COMPILER 233 if (c == '\n' && is_tty) 234 { 235 /* remove_top(1); */ 236 putchr('\r'); 237 } 238 #else 239 #ifdef _OSK 240 if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */ 241 putchr(0x0A); 242 #endif 243 #endif 244 /* 245 * Some versions of flush() write to *ob, so we must flush 246 * when we are still one char from the end of obuf. 247 */ 248 if (ob >= &obuf[sizeof(obuf)-1]) 249 flush(); 250 *ob++ = c; 251 return (c); 252 } 253 254 /* 255 * Output a string. 256 */ 257 public void 258 putstr(s) 259 register char *s; 260 { 261 while (*s != '\0') 262 putchr(*s++); 263 } 264 265 266 /* 267 * Output an integer in a given radix. 268 */ 269 static int 270 iprintnum(num, radix) 271 int num; 272 int radix; 273 { 274 register char *s; 275 int r; 276 int neg; 277 char buf[10]; 278 279 neg = (num < 0); 280 if (neg) 281 num = -num; 282 283 s = buf; 284 do 285 { 286 *s++ = (num % radix) + '0'; 287 } while ((num /= radix) != 0); 288 289 if (neg) 290 *s++ = '-'; 291 r = s - buf; 292 293 while (s > buf) 294 putchr(*--s); 295 return (r); 296 } 297 298 /* 299 * This function implements printf-like functionality 300 * using a more portable argument list mechanism than printf's. 301 */ 302 static int 303 less_printf(fmt, parg) 304 register char *fmt; 305 PARG *parg; 306 { 307 register char *s; 308 register int n; 309 register int col; 310 311 col = 0; 312 while (*fmt != '\0') 313 { 314 if (*fmt != '%') 315 { 316 putchr(*fmt++); 317 col++; 318 } else 319 { 320 ++fmt; 321 switch (*fmt++) { 322 case 's': 323 s = parg->p_string; 324 parg++; 325 while (*s != '\0') 326 { 327 putchr(*s++); 328 col++; 329 } 330 break; 331 case 'd': 332 n = parg->p_int; 333 parg++; 334 col += iprintnum(n, 10); 335 break; 336 } 337 } 338 } 339 return (col); 340 } 341 342 /* 343 * Get a RETURN. 344 * If some other non-trivial char is pressed, unget it, so it will 345 * become the next command. 346 */ 347 public void 348 get_return() 349 { 350 int c; 351 352 #if ONLY_RETURN 353 while ((c = getchr()) != '\n' && c != '\r') 354 bell(); 355 #else 356 c = getchr(); 357 if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR) 358 ungetcc(c); 359 #endif 360 } 361 362 /* 363 * Output a message in the lower left corner of the screen 364 * and wait for carriage return. 365 */ 366 public void 367 error(fmt, parg) 368 char *fmt; 369 PARG *parg; 370 { 371 int col = 0; 372 static char return_to_continue[] = " (press RETURN)"; 373 374 errmsgs++; 375 376 if (any_display && is_tty) 377 { 378 clear_bot(); 379 so_enter(); 380 col += so_s_width; 381 } 382 383 col += less_printf(fmt, parg); 384 385 if (!(any_display && is_tty)) 386 { 387 putchr('\n'); 388 return; 389 } 390 391 putstr(return_to_continue); 392 so_exit(); 393 col += sizeof(return_to_continue) + so_e_width; 394 395 get_return(); 396 lower_left(); 397 398 if (col >= sc_width) 399 /* 400 * Printing the message has probably scrolled the screen. 401 * {{ Unless the terminal doesn't have auto margins, 402 * in which case we just hammered on the right margin. }} 403 */ 404 screen_trashed = 1; 405 406 flush(); 407 } 408 409 static char intr_to_abort[] = "... (interrupt to abort)"; 410 411 /* 412 * Output a message in the lower left corner of the screen 413 * and don't wait for carriage return. 414 * Usually used to warn that we are beginning a potentially 415 * time-consuming operation. 416 */ 417 public void 418 ierror(fmt, parg) 419 char *fmt; 420 PARG *parg; 421 { 422 clear_bot(); 423 so_enter(); 424 (void) less_printf(fmt, parg); 425 putstr(intr_to_abort); 426 so_exit(); 427 flush(); 428 need_clr = 1; 429 } 430 431 /* 432 * Output a message in the lower left corner of the screen 433 * and return a single-character response. 434 */ 435 public int 436 query(fmt, parg) 437 char *fmt; 438 PARG *parg; 439 { 440 register int c; 441 int col = 0; 442 443 if (any_display && is_tty) 444 clear_bot(); 445 446 (void) less_printf(fmt, parg); 447 c = getchr(); 448 449 if (!(any_display && is_tty)) 450 { 451 putchr('\n'); 452 return (c); 453 } 454 455 lower_left(); 456 if (col >= sc_width) 457 screen_trashed = 1; 458 flush(); 459 460 return (c); 461 } 462