diff --git a/less.nro.VER b/less.nro.VER index c8356cef..8d8f4eb0 100644 --- a/less.nro.VER +++ b/less.nro.VER @@ -570,10 +570,7 @@ The \-d option does not otherwise change the behavior of .I less on a dumb terminal. .IP "\-D\fBx\fP\fIcolor\fP or \-\-color=\fBx\fP\fIcolor\fP" -[this section is for non-MS-DOS systems only; for MS-DOS systems see below] -.br -When the \-\-use-color option is enabled, this option -changes the color of different parts of the displayed text. +Changes the color of different parts of the displayed text. \fBx\fP is a single character which selects the type of text whose color is being set: .RS @@ -595,12 +592,30 @@ The rscroll character. Search results. .IP "W" The highlight enabled via the \-w option. +.IP "d" +Bold text. +.IP "k" +Blinking text. +.IP "s" +Standout text. +.IP "u" +Underlined text. .RE -.PP + .RS -\fIcolor\fP is either a 4-bit color string or a 6-bit color string: +The uppercase letters can be used only when the \-\-use-color option is enabled. +When text color is specified by both an uppercase letter and a lowercase letter, +the uppercase letter takes precedence. +For example, error messages are normally displayed as standout text. +So if both "s" and "E" are given a color, the "E" color applies +to error messages, and the "s" color applies to other standout text. +The "d" and "u" letters refer to bold and underline text formed by +overstriking with backspaces (see the \-u option), +not to text using ANSI escape sequences with the \-R option. +.PP +\fIcolor\fP is either a 4-bit color string or a 8-bit color string: .PP -A 4-bit color string is one or two characters where +A 4-bit color string is zero, one or two characters, where the first character specifies the foreground color and the second specifies the background color as follows: .IP "b" @@ -621,38 +636,27 @@ White Yellow .PP The corresponding upper-case letter denotes a brighter shade of the color. -For example, \-DLGk displays line numbers as bright green text on a black +For example, \-DNGk displays line numbers as bright green text on a black background, and \-DEbR displays error messages as blue text on a bright red background. -If either character is a "-", the corresponding color is not changed. -If the background character is omitted, the background color is not changed. +If either character is a "-" or is omitted, the corresponding color +is set to that of normal text. .PP -A 6-bit color string is one or two decimal integers separated by a dot, +A 8-bit color string is one or two decimal integers separated by a dot, where the first integer specifies the foreground color and the second specifies the background color. Each integer is a value between 0 and 255 inclusive which selects a CSI 38;5 color (see .br https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters) -If either integer is a "-", the corresponding color is not changed. -If the dot and background integer are omitted, the background color is not changed. +If either integer is a "-" or is omitted, +the corresponding color is set to that of normal text. +On MS-DOS versions of +.IR less , +8-bit color is not supported; instead, decimal values are interpreted as 4-bit +CHAR_INFO.Attributes values +(see https://docs.microsoft.com/en-us/windows/console/char-info-str). .RE -.IP "\-D\fBx\fP\fIcolor\fP or \-\-color=\fBx\fP\fIcolor\fP" -[this section is for MS-DOS systems only] -.br -Sets the color of the text displayed. -\fBx\fP is a single character which selects the type of text whose color is -being set: n=normal, s=standout, d=bold, u=underlined, k=blink. -\fIcolor\fP is a pair of numbers separated by a period. -The first number selects the foreground color and the second selects -the background color of the text. -A single number \fIN\fP is the same as \fIN.M\fP, -where \fIM\fP is the normal background color. -The color may start or end with \fBu\fP to use underline (with the normal -color, if by itself), if the system supports it (Windows only). -\fBx\fP may also be \fBa\fP to toggle strict ANSI sequence rendering -(SGR mode). -. .IP "\-e or \-\-quit-at-eof" Causes .I less diff --git a/line.c b/line.c index 48db500b..55a825cc 100644 --- a/line.c +++ b/line.c @@ -94,6 +94,10 @@ static char color_map[AT_NUM_COLORS][12] = { "kC", /* AT_COLOR_PROMPT */ "kc", /* AT_COLOR_RSCROLL */ "kG", /* AT_COLOR_SEARCH */ + "", /* AT_UNDERLINE */ + "", /* AT_BOLD */ + "", /* AT_BLINK */ + "", /* AT_STANDOUT */ }; /* State while processing an ANSI escape sequence */ @@ -953,7 +957,8 @@ store_bs(ch, rep, pos) { if (bs_mode == BS_CONTROL) return store_control_char(ch, rep, pos); - if (linebuf.end <= linebuf.print || + if (linebuf.end > 0 && + (linebuf.end <= linebuf.print && linebuf.buf[linebuf.end-1] == '\0') || (linebuf.end > 0 && linebuf.attr[linebuf.end - 1] & (AT_ANSI|AT_BINARY))) STORE_PRCHAR('\b', pos); else if (bs_mode == BS_NORMAL) @@ -1407,10 +1412,30 @@ rrshift(VOID_PARAM) color_index(attr) int attr; { - int cx = (attr & AT_COLOR) >> AT_COLOR_SHIFT; - if (cx == 0 || cx > AT_NUM_COLORS) - return -1; - return cx-1; + if (use_color) + { + switch (attr & AT_COLOR) + { + case AT_COLOR_ATTN: return 0; + case AT_COLOR_BIN: return 1; + case AT_COLOR_CTRL: return 2; + case AT_COLOR_ERROR: return 3; + case AT_COLOR_LINENUM: return 4; + case AT_COLOR_MARK: return 5; + case AT_COLOR_PROMPT: return 6; + case AT_COLOR_RSCROLL: return 7; + case AT_COLOR_SEARCH: return 8; + } + } + if (attr & AT_UNDERLINE) + return 9; + if (attr & AT_BOLD) + return 10; + if (attr & AT_BLINK) + return 11; + if (attr & AT_STANDOUT) + return 12; + return -1; } static int @@ -1431,10 +1456,11 @@ set_color_map(attr, colorstr) int cx = color_index(attr); if (cx < 0) return -1; - if (tput_color(colorstr, null_putc) < 0) + if (*colorstr != '\0' && tput_color(colorstr, null_putc) < 0) + return -1; + if (strlen(colorstr)+1 > sizeof(color_map[cx])) return -1; - strncpy(color_map[cx], colorstr, sizeof(color_map[cx])); - color_map[cx][strlen(colorstr)] = '\0'; + strcpy(color_map[cx], colorstr); return 0; } @@ -1445,10 +1471,7 @@ set_color_map(attr, colorstr) get_color_map(attr) int attr; { - int cx; - if (!use_color) - return NULL; - cx = color_index(attr); + int cx = color_index(attr); if (cx < 0) return NULL; return color_map[cx]; diff --git a/optfunc.c b/optfunc.c index 657628e7..1c416da8 100644 --- a/optfunc.c +++ b/optfunc.c @@ -50,6 +50,7 @@ extern int wheel_lines; extern int less_is_more; extern int linenum_width; extern int status_col_width; +extern int use_color; #if LOGFILE extern char *namelogfile; extern int force_logfile; @@ -585,59 +586,7 @@ colordesc(s, fg_color, bg_color) *fg_color = fg; *bg_color = bg; } - -/* - * Handler for the -D option. - */ - /*ARGSUSED*/ - public void -opt_D(type, s) - int type; - char *s; -{ - PARG p; - - switch (type) - { - case INIT: - case TOGGLE: - switch (*s++) - { - case 'n': - colordesc(s, &nm_fg_color, &nm_bg_color); - break; - case 'd': - colordesc(s, &bo_fg_color, &bo_bg_color); - break; - case 'u': - colordesc(s, &ul_fg_color, &ul_bg_color); - break; - case 'k': - colordesc(s, &bl_fg_color, &bl_bg_color); - break; - case 's': - colordesc(s, &so_fg_color, &so_bg_color); - break; - case 'a': - sgr_mode = !sgr_mode; - break; - default: - error("-D must be followed by n, d, u, k, s or a", NULL_PARG); - break; - } - if (type == TOGGLE) - { - at_enter(AT_STANDOUT); - at_exit(); - } - break; - case QUERY: - p.p_string = (sgr_mode) ? "on" : "off"; - error("SGR mode is %s", &p); - break; - } -} -#else +#endif static int color_from_namechar(namechar) @@ -654,6 +603,10 @@ color_from_namechar(namechar) case 'P': return AT_COLOR_PROMPT; case 'R': return AT_COLOR_RSCROLL; case 'S': return AT_COLOR_SEARCH; + case 's': return AT_STANDOUT; + case 'd': return AT_BOLD; + case 'u': return AT_UNDERLINE; + case 'k': return AT_BLINK; default: return 0; } } @@ -674,13 +627,53 @@ opt_D(type, s) { case INIT: case TOGGLE: +#if MSDOS_COMPILER + if (*s == 'a') + { + sgr_mode = !sgr_mode; + break; + } +#endif attr = color_from_namechar(s[0]); - if (attr <= 0) + if (attr == 0) { p.p_char = s[0]; error("Invalid color specifier '%c'", &p); return; } + if (!use_color && (attr & AT_COLOR)) + { + error("Set --use-color before changing colors", NULL_PARG); + return; + } +#if MSDOS_COMPILER + if (!(attr & AT_COLOR)) + { + switch (attr) + { + case AT_NORMAL: + colordesc(s, &nm_fg_color, &nm_bg_color); + break; + case AT_BOLD: + colordesc(s, &bo_fg_color, &bo_bg_color); + break; + case AT_UNDERLINE: + colordesc(s, &ul_fg_color, &ul_bg_color); + break; + case AT_BLINK: + colordesc(s, &bl_fg_color, &bl_bg_color); + break; + case AT_STANDOUT: + colordesc(s, &so_fg_color, &so_bg_color); + break; + } + if (type == TOGGLE) + { + at_enter(AT_STANDOUT); + at_exit(); + } + } else +#endif if (set_color_map(attr, s+1) < 0) { p.p_string = s+1; @@ -688,9 +681,14 @@ opt_D(type, s) return; } break; +#if MSDOS_COMPILER + case QUERY: + p.p_string = (sgr_mode) ? "on" : "off"; + error("SGR mode is %s", &p); + break; +#endif } } -#endif /* * Handler for the -x option. diff --git a/screen.c b/screen.c index 35ef8f6c..9f89f250 100644 --- a/screen.c +++ b/screen.c @@ -88,7 +88,9 @@ static char *windowid; static int videopages; static long msec_loops; static int flash_created = 0; -#define SETCOLORS(fg,bg) { _settextcolor(fg); _setbkcolor(bg); } +#define SET_FG_COLOR(fg) _settextcolor(fg) +#define SET_BG_COLOR(bg) _setbkcolor(bg) +#define SETCOLORS(fg,bg) { SET_FG_COLOR(fg); SET_BG_COLOR(bg); } #endif #if MSDOS_COMPILER==BORLANDC @@ -99,7 +101,9 @@ static int flash_created = 0; #define _settextposition(y,x) gotoxy(x,y) #define _clearscreen(m) clrscr() #define _outtext(s) cputs(s) -#define SETCOLORS(fg,bg) { textcolor(fg); textbackground(bg); } +#define SET_FG_COLOR(fg) textcolor(fg) +#define SET_BG_COLOR(bg) textbackground(bg) +#define SETCOLORS(fg,bg) { SET_FG_COLOR(fg); SET_BG_COLOR(bg); } extern int sc_height; #endif @@ -132,9 +136,11 @@ static void win32_deinit_term(); #define FG_COLORS (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY) #define BG_COLORS (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY) #define MAKEATTR(fg,bg) ((WORD)((fg)|((bg)<<4))) -#define SETCOLORS(fg,bg) { curr_attr = MAKEATTR(fg,bg); \ - if (SetConsoleTextAttribute(con_out, curr_attr) == 0) \ - error("SETCOLORS failed", NULL_PARG); } +#define APPLY_COLORS() { if (SetConsoleTextAttribute(con_out, curr_attr) == 0) \ + error("SETCOLORS failed", NULL_PARG); } +#define SET_FG_COLOR(fg) { curr_attr |= (fg); APPLY_COLORS(); } +#define SET_BG_COLOR(bg) { curr_attr |= ((bg)<<4); APPLY_COLORS(); } +#define SETCOLORS(fg,bg) { curr_attr = MAKEATTR(fg,bg); APPLY_COLORS(); } #endif #if MSDOS_COMPILER @@ -2348,7 +2354,59 @@ clear_bot(VOID_PARAM) } } -#if !MSDOS_COMPILER +#if MSDOS_COMPILER + + static int +win_4bit_color(ch) + char ch; +{ + int bright = (ch >= 'A' && ch <= 'Z') ? FOREGROUND_BRIGHT : 0; + if (bright) + ch += 'a' - 'A'; + switch (ch) + { + case 'k': return bright; + case 'r': return bright|FOREGROUND_RED; + case 'g': return bright|FOREGROUND_GREEN; + case 'y': return bright|FOREGROUND_RED|FOREGROUND_GREEN; + case 'b': return bright|FOREGROUND_BLUE; + case 'm': return bright|FOREGROUND_RED|FOREGROUND_BLUE; + case 'c': return bright|FOREGROUND_GREEN|FOREGROUND_BLUE; + case 'w': return bright|FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE; + case '-': return -2; /* no change */ + default: return -1; + } +} + + static void +win_set_4bit_color(attr) + int attr; +{ + int fg_color; + int bg_color; + int out = FALSE; + char *colorstr = get_color_map(attr); + if (colorstr == NULL || colorstr[0] == '\0') + return FALSE; + fg_color = win_4bit_color(colorstr[0]); + bg_color = win_4bit_color((strlen(colorstr) < 2) ? '-' : colorstr[1]); + if (fg_color >= 0 && bg_color >= 0) + { + SETCOLORS(fg_color, bg_color); + out = TRUE; + } else if (fg_color >= 0) + { + SET_FG_COLOR(fg_color); + out = TRUE; + } else if (bg_color >= 0) + { + SET_BG_COLOR(bg_color); + out = TRUE; + } + return out; +} + +#else /* MSDOS_COMPILER */ static int sgr_4bit_color(ch) @@ -2372,7 +2430,7 @@ sgr_4bit_color(ch) case 'M': return 95; case 'C': return 96; case 'W': return 97; - case '-': return 0; + case '-': return 0; /* no change */ default: return -1; } } @@ -2410,14 +2468,15 @@ tput_color(colorstr, f_putc) char buf[16]; int fg_color; int bg_color; + int out = FALSE; - if (colorstr == NULL) - return -1; - if (*colorstr == '\0') + if (colorstr == NULL || colorstr[0] == '\0') + return FALSE; + if (strcmp(colorstr, "*") == 0) { /* Special case: reset to normal */ ltputs(ESCS"[m", 1, f_putc); - return 0; + return TRUE; } fg_color = sgr_4bit_color(colorstr[0]); bg_color = sgr_4bit_color((strlen(colorstr) < 2) ? '-' : colorstr[1]); @@ -2428,11 +2487,13 @@ tput_color(colorstr, f_putc) { SNPRINTF1(buf, sizeof(buf), ESCS"[%dm", fg_color); ltputs(buf, 1, f_putc); + out = TRUE; } if (bg_color > 0) { SNPRINTF1(buf, sizeof(buf), ESCS"[%dm", bg_color+10); ltputs(buf, 1, f_putc); + out = TRUE; } } else { @@ -2445,94 +2506,92 @@ tput_color(colorstr, f_putc) { SNPRINTF1(buf, sizeof(buf), ESCS"[38;5;%dm", fg_color); ltputs(buf, 1, f_putc); + out = TRUE; } if (bg_color > 0) { SNPRINTF1(buf, sizeof(buf), ESCS"[48;5;%dm", bg_color); ltputs(buf, 1, f_putc); + out = TRUE; } - } else - return -1; + } } - return 0; + return out; } - static void -tput_attr_color(attr, f_putc) + static int +tput_mode(mode_str, enter, attr, f_putc) + char *mode_str; + int enter; int attr; int (*f_putc)(int); { - char *colorstr = (attr & AT_COLOR) ? get_color_map(attr) : ""; - tput_color(colorstr, f_putc); + char *colorstr = enter ? get_color_map(attr) : "*"; + if (colorstr == NULL || *colorstr == '\0') + { + ltputs(mode_str, 1, f_putc); + return TRUE; + } + /* Color overrides mode string */ + return tput_color(colorstr, f_putc); } -#endif + +#endif /* MSDOS_COMPILER */ public void at_enter(attr) int attr; { - int in_color; attr = apply_at_specials(attr); - in_color = use_color && (attr & AT_COLOR); #if !MSDOS_COMPILER /* The one with the most priority is last. */ - if (attr & AT_UNDERLINE) - ltputs(sc_u_in, 1, putchr); - if (attr & AT_BOLD) - ltputs(sc_b_in, 1, putchr); - if (attr & AT_BLINK) - ltputs(sc_bl_in, 1, putchr); - if ((attr & AT_STANDOUT) && !in_color) - ltputs(sc_s_in, 1, putchr); - if ((attr & (AT_UNDERLINE|AT_BOLD)) == 0 && in_color) - tput_attr_color(attr, putchr); + if ((attr & AT_UNDERLINE) && tput_mode(sc_u_in, TRUE, AT_UNDERLINE, putchr)) + attrmode |= AT_UNDERLINE; + if ((attr & AT_BOLD) && tput_mode(sc_b_in, TRUE, AT_BOLD, putchr)) + attrmode |= AT_BOLD; + if ((attr & AT_BLINK) && tput_mode(sc_bl_in, TRUE, AT_BLINK, putchr)) + attrmode |= AT_BLINK; + /* Don't use standout and color at the same time. */ + if ((attr & AT_COLOR) && use_color && tput_color(get_color_map(attr), putchr)) + attrmode |= (attr & AT_COLOR); + else if ((attr & AT_STANDOUT) && tput_mode(sc_s_in, TRUE, AT_STANDOUT, putchr)) + attrmode |= AT_STANDOUT; #else flush(); /* The one with the most priority is first. */ - if (attr & AT_STANDOUT) - { + if ((attr & AT_COLOR) && use_color) + win_set_4bit_color(attr); + else if (attr & AT_STANDOUT) SETCOLORS(so_fg_color, so_bg_color); - } else if (attr & AT_BLINK) - { + else if (attr & AT_BLINK) SETCOLORS(bl_fg_color, bl_bg_color); - } else if (attr & AT_BOLD) - { SETCOLORS(bo_fg_color, bo_bg_color); - } else if (attr & AT_UNDERLINE) - { SETCOLORS(ul_fg_color, ul_bg_color); - } #endif - - attrmode = attr; } public void at_exit(VOID_PARAM) { #if !MSDOS_COMPILER - int in_color = use_color && (attrmode & AT_COLOR); - /* Undo things in the reverse order we did them. */ - if ((attrmode & (AT_UNDERLINE|AT_BOLD)) == 0 && in_color) - tput_attr_color(0, putchr); - if ((attrmode & AT_STANDOUT) && !in_color) - ltputs(sc_s_out, 1, putchr); - if (attrmode & AT_BLINK) - ltputs(sc_bl_out, 1, putchr); - if (attrmode & AT_BOLD) - ltputs(sc_b_out, 1, putchr); - if (attrmode & AT_UNDERLINE) - ltputs(sc_u_out, 1, putchr); + if ((attrmode & AT_COLOR) && tput_color("*", putchr)) + attrmode &= ~AT_COLOR; + if ((attrmode & AT_STANDOUT) && tput_mode(sc_s_out, FALSE, AT_STANDOUT, putchr)) + attrmode &= ~AT_STANDOUT; + if ((attrmode & AT_BLINK) && tput_mode(sc_bl_out, FALSE, AT_BLINK, putchr)) + attrmode &= ~AT_BLINK; + if ((attrmode & AT_BOLD) && tput_mode(sc_b_out, FALSE, AT_BOLD, putchr)) + attrmode &= ~AT_BOLD; + if ((attrmode & AT_UNDERLINE) && tput_mode(sc_u_out, FALSE, AT_UNDERLINE, putchr)) + attrmode &= ~AT_UNDERLINE; #else flush(); SETCOLORS(nm_fg_color, nm_bg_color); #endif - - attrmode = AT_NORMAL; } public void @@ -2573,57 +2632,6 @@ apply_at_specials(attr) return attr; } -#if 0 /* No longer used */ -/* - * Erase the character to the left of the cursor - * and move the cursor left. - */ - public void -backspace(VOID_PARAM) -{ -#if !MSDOS_COMPILER - /* - * Erase the previous character by overstriking with a space. - */ - ltputs(sc_backspace, 1, putchr); - putchr(' '); - ltputs(sc_backspace, 1, putchr); -#else -#if MSDOS_COMPILER==MSOFTC - struct rccoord tpos; - - flush(); - tpos = _gettextposition(); - if (tpos.col <= 1) - return; - _settextposition(tpos.row, tpos.col-1); - _outtext(" "); - _settextposition(tpos.row, tpos.col-1); -#else -#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC - cputs("\b"); -#else -#if MSDOS_COMPILER==WIN32C - COORD cpos; - DWORD cChars; - CONSOLE_SCREEN_BUFFER_INFO scr; - - flush(); - GetConsoleScreenBufferInfo(con_out, &scr); - cpos = scr.dwCursorPosition; - if (cpos.X <= 0) - return; - cpos.X--; - SetConsoleCursorPosition(con_out, cpos); - FillConsoleOutputCharacter(con_out, (TCHAR)' ', 1, cpos, &cChars); - SetConsoleCursorPosition(con_out, cpos); -#endif -#endif -#endif -#endif -} -#endif /* 0 */ - /* * Output a plain backspace, without erasing the previous char. */