rust-most/vendor/most-5.1.0/src/line.c

688 lines
14 KiB
C

/*
This file is part of MOST.
Copyright (c) 1991, 1999, 2002, 2005-2018, 2019 John E. Davis
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc., 675
Mass Ave, Cambridge, MA 02139, USA.
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <slang.h>
#include "jdmacros.h"
#include "most.h"
#include "line.h"
#include "window.h"
#include "display.h"
int Most_Tab_Width = 8;
int Most_Selective_Display = 0;
int Most_Show_Wrap_Marker = 1;
#define IS_BYTE_PRINTABLE(b) \
((((b) >= ' ') && ((b) < 0x7F)) \
|| ((Most_UTF8_Mode == 0) && ((b) >= SLsmg_Display_Eight_Bit)))
/* take 16 binary characters and put them in displayable form */
static void binary_format_line (unsigned char *beg, unsigned char *end,
char *buf)
{
unsigned char *b;
char *s, *s1;
unsigned char ch;
int count;
count = 0;
b = beg;
s = buf;
while (b < end)
{
if (count == 4)
{
*s++ = ' ';
count = 0;
}
count++;
ch = *b++;
if ((Most_V_Opt == 0)
|| (ch & 0x80))
{
sprintf (s, "%02X", ch);
s += 2;
continue;
}
if ((ch >= ' ') && (ch < 0x7F))
{
*s++ = ' ';
*s++ = (char) ch;
continue;
}
*s++ = '^';
if (ch < ' ') ch += '@';
else ch = '?';
*s++ = ch;
}
s1 = buf + (9 * 4) + 4;
while (s < s1)
*s++ = ' ';
b = beg;
while (b < end)
{
ch = *b++;
if (IS_BYTE_PRINTABLE(ch))
{
*s++ = ch;
continue;
}
*s++ = '.';
}
*s = 0;
}
static void output_binary_formatted_line (void)
{
unsigned char *beg, *end;
char buf[256];
beg = Most_Beg + Most_C_Offset;
end = beg + 16;
if (end > Most_Eob) end = Most_Eob;
sprintf (buf, "0x%08lX: ", (unsigned long) Most_C_Offset);
binary_format_line (beg, end, buf + 12);
SLsmg_write_string (buf);
SLsmg_erase_eol ();
}
/* Here *begp points to the char after \e.
* The general escape sequence parsed here is assumed to look like:
* \e[ XX ; ... m
* If 30 <= XX <= 37, then it specifies the foreground color
* If 40 <= XX <= 47, then a background color is specified
* If 0 <= XX <= 8, then an attribute (e.g, 8) is specified.
* These numbers will be encoded as:
* offset + (FG-30 + 8*(BG-40 + 9*attribute))
*/
int most_parse_color_escape (unsigned char **begp, unsigned char *end, int *colorp)
{
unsigned char *beg = *begp;
int fg = 38, bg = 48, at = 0;
int xx;
if ((beg >= end) || (*beg != '['))
return -1;
beg++; /* skip [ */
#if 1
if ((beg < end) && (*beg == 'K'))
{
if (colorp != NULL) *colorp = -1;
*begp = beg + 1;
return 0;
}
#endif
while (1)
{
xx = 0;
while ((beg < end) && isdigit (*beg))
{
xx = xx*10 + (*beg - '0');
beg++;
}
if ((xx >= 0) && (xx <= 8))
at = xx;
else if ((xx >= 20) && (xx <= 28))
xx = 0;
else if ((xx >= 30) && (xx <= 37))
fg = xx;
else if ((xx >= 40) && (xx <= 47))
bg = xx;
else return -1;
if ((beg < end) && (*beg == ';'))
{
beg++;
continue;
}
if ((beg < end) && ((*beg == 'm') || (*beg == ']')))
{
*begp = beg + 1;
if (colorp != NULL)
{
if ((fg != 38) || (bg != 48))
xx = ((fg-30) + 9*((bg-40) + 9*at));
if (xx != 0)
xx += MOST_EMBEDDED_COLOR_OFFSET;
*colorp = xx;
}
return 0;
}
return -1;
}
}
typedef struct
{
unsigned char *bytes;
unsigned char byte; /* used if bytes is NULL */
unsigned int len;
int color;
}
Multibyte_Cell_Type;
static int most_analyse_line (unsigned char *begg, unsigned char *endd,
Multibyte_Cell_Type *cells, unsigned int num_cols, int *start_colorp)
{
unsigned char *beg, *end;
unsigned int min_col, max_col, prev_width;
unsigned int col, max_col_reached;
int default_attr;
Multibyte_Cell_Type *cell, *max_cell;
beg = begg;
end = endd;
col = max_col_reached = 0;
cell = cells;
max_cell = cell;
min_col = Most_Column - 1;
max_col = min_col + num_cols;
default_attr = *start_colorp;
prev_width = 1;
while (beg < end)
{
int attr = default_attr;
unsigned char ch;
unsigned char *pch = beg++;
char buf[16];
if ('\n' == (ch = *pch))
break;
if ((ch == '\r') && (Most_V_Opt == 0))
{
if (col > max_col_reached) max_col_reached = col;
col = 0;
prev_width = 1;
continue;
}
if ((ch == '\b') && (Most_V_Opt == 0))
{
if (col > max_col_reached) max_col_reached = col;
if (col < prev_width)
col = 0;
else
col -= prev_width;
continue;
}
if (col < max_col_reached) /* overstrike */
{
attr = MOST_BOLD_COLOR;
if ((col >= min_col) && (col < max_col))
{
cell = cells + (col-min_col);
if (cell->bytes[0] == '_')
attr = MOST_ULINE_COLOR;
else if (ch == '_')
{
cell->color = MOST_ULINE_COLOR;
col++;
continue;
}
}
/* drop */
}
if (IS_BYTE_PRINTABLE(ch))
{
if ((col >= min_col) && (col < max_col))
{
cell = cells + (col-min_col);
cell->bytes = pch;
cell->len = 1;
cell->color = attr;
if (cell >= max_cell)
max_cell = cell + 1;
}
col++;
prev_width = 1;
continue;
}
if ((ch == '\t') && (Most_T_Opt == 0) && (Most_Tab_Width))
{
int nspaces = Most_Tab_Width * (col/Most_Tab_Width + 1) - col;
prev_width = nspaces;
while (nspaces > 0)
{
if ((col >= min_col) && (col < max_col))
{
cell = cells + (col-min_col);
cell->bytes = &cell->byte;
cell->byte = ' ';
cell->color = attr;
cell->len = 1;
if (cell >= max_cell)
max_cell = cell + 1;
}
col++;
nspaces--;
}
continue;
}
#if 1
if ((ch == 033) && (Most_V_Opt == 0))
{
int color;
if (0 == most_parse_color_escape (&beg, end, &color))
{
if (color != -1) default_attr = color;
continue;
}
/* drop */
}
#endif
if (ch & 0x80)
{
SLwchar_Type wch;
if ((Most_UTF8_Mode)
&& (NULL != SLutf8_decode (pch, end, &wch, NULL)))
{
int width = SLwchar_wcwidth (wch);
beg = SLutf8_skip_chars (pch, end, 1, NULL, 1);
prev_width = width;
if (width == 0)
{
col--;
if ((col >= min_col) && (col < max_col))
{
cell = cells + (col-min_col);
cell->len += beg-pch;
}
col++;
continue;
}
if ((col >= min_col) && (col < max_col))
{
cell = cells + (col-min_col);
cell->bytes = pch;
cell->color = attr;
cell->len = beg - pch;
if (cell >= max_cell)
max_cell = cell + 1;
}
col++;
if (width > 1)
{
if ((col >= min_col) && (col < max_col))
{
cell = cells + (col-min_col);
cell->bytes = pch;
cell->color = attr;
cell->len = 0;
if (cell >= max_cell)
max_cell = cell + 1;
}
col++;
}
continue;
}
/* Otherwise, this displays as <XX> and takes up 4 character cells */
sprintf (buf, "<%02X>", (unsigned int) ch);
prev_width = 4;
/* drop */
}
else
{
/* Otherwise we have a Ctrl-char displayed as ^X */
if (ch == 0x7F) ch = '?';
else ch += '@';
sprintf (buf, "^%c", ch);
prev_width = 2;
}
pch = (unsigned char *)buf;
while (*pch)
{
if ((col >= min_col) && (col < max_col))
{
cell = cells + (col-min_col);
cell->bytes = &cell->byte;
cell->byte = *pch;
cell->color = attr;
cell->len = 1;
if (cell >= max_cell)
max_cell = cell + 1;
}
col++;
pch++;
}
}
if (col < max_col_reached)
col = max_col_reached;
else
max_col_reached = col;
/* Now add "..." if selective display. To do that, the next line needs to
* be dealt with to determine whether or not it will be hidden.
*/
if (Most_Selective_Display
&& (Most_W_Opt == 0)
&& (beg < Most_Eob)
&& ((col >= min_col) && (col < max_col)))
{
if (*beg == '\n') beg++;
while ((beg < Most_Eob)
&& ((*beg == ' ') || (*beg == '\t') || (*beg == '\r')))
beg++;
if ((beg >= Most_Eob) || (*beg == '\n')
|| (most_apparant_distance(beg) >= Most_Selective_Display))
{
max_col_reached = col + 3;
while (col < max_col_reached)
{
if (col < max_col)
{
cell = cells + (col-min_col);
cell->bytes = &cell->byte;
cell->byte = '.';
cell->color = 0;
cell->len = 1;
if (cell >= max_cell)
max_cell = cell + 1;
}
col++;
}
}
}
*start_colorp = default_attr;
return max_cell - cells;
}
static void display_cells (Multibyte_Cell_Type *cell, unsigned int n, char dollar)
{
Multibyte_Cell_Type *cell_max;
int last_color = -1;
cell_max = cell + n;
while (cell < cell_max)
{
if (last_color != cell->color)
{
last_color = cell->color;
SLsmg_set_color (last_color);
}
SLsmg_write_chars (cell->bytes, cell->bytes + cell->len);
cell++;
}
if (last_color != 0)
SLsmg_set_color (0);
SLsmg_erase_eol ();
if (dollar)
{
SLsmg_gotorc (SLsmg_get_row (), SLtt_Screen_Cols-1);
SLsmg_write_nchars (&dollar, 1);
}
}
void most_display_line (int reset)
{
unsigned char *beg, *end;
unsigned char dollar;
static Multibyte_Cell_Type *cells;
static unsigned int num_cells;
unsigned int screen_cols;
unsigned int num_cells_set;
static int last_color = 0; /* used for a line that wrapped */
if (Most_B_Opt)
{
output_binary_formatted_line ();
return;
}
screen_cols = SLtt_Screen_Cols;
if (num_cells != screen_cols + 1)
{
num_cells = screen_cols + 1;
SLfree ((char *) cells);
if (NULL == (cells = (Multibyte_Cell_Type *)SLcalloc (num_cells, sizeof (Multibyte_Cell_Type))))
most_exit_error ("Out of memory");
}
(void) most_extract_line (&beg, &end);
if (reset || (Most_W_Opt == 0))
last_color = 0;
num_cells_set = most_analyse_line (beg, end, cells, num_cells, &last_color);
dollar = 0;
if (Most_W_Opt)
{
if (Most_Show_Wrap_Marker
&& (end < Most_Eob)
&& (*end != '\n'))
dollar = '\\';
}
else if (num_cells_set > screen_cols)
dollar = '$';
display_cells (cells, num_cells_set, dollar);
}
/* given a position in a line, return apparent distance from bol
expanding tabs, etc... up to pos */
int most_apparant_distance (unsigned char *pos)
{
int i, prev_width;
unsigned char *save_pos, ch;
unsigned int save_offset;
save_offset = Most_C_Offset;
save_pos = pos;
Most_C_Offset = (unsigned int) (pos - Most_Beg);
pos = most_beg_of_line();
Most_C_Offset = save_offset;
i = 0;
prev_width = 1;
while (pos < save_pos)
{
ch = *pos++;
if (IS_BYTE_PRINTABLE(ch))
{
i++;
prev_width = 1;
continue;
}
if ((ch == '\b') && (Most_V_Opt == 0))
{
i -= prev_width;
if (i < 0) i = 0;
continue;
}
if ((ch == '\r') && (Most_V_Opt == 0))
{
if (i != 1) i = 0;
prev_width = 1;
continue;
}
if ((ch == '\t') && (Most_T_Opt == 0))
{
prev_width = Most_Tab_Width * (i/Most_Tab_Width + 1) - i; /* Most_Tab_Width column tabs */
i += prev_width;
continue;
}
if ((ch == 033) && (Most_V_Opt == 0)
&& (0 == most_parse_color_escape (&pos, save_pos, NULL)))
continue;
if (ch & 0x80)
{
SLwchar_Type wch;
if ((Most_UTF8_Mode)
&& (NULL != SLutf8_decode (pos-1, save_pos, &wch, NULL)))
{
prev_width = SLwchar_wcwidth (wch);
pos = SLutf8_skip_chars (pos-1, save_pos, 1, NULL, 1);
i += prev_width;
continue;
}
prev_width = 4;
i += prev_width; /* <XX> */
continue;
}
prev_width = 2;
i += prev_width; /* ^X */
}
return i;
}
/*
* Returns a pointer to the num_cols'th character after the one
* pointed at b. Invisible character runs are not counted toward this
* limit, i.e. strings that represent attributes, such as "_\b" for
* underlines.
*
* If multi_column is non-zero, characters spanning more than one
* column will add their extra width to the column count.
*
* If there the end of the buffer is reached, as delimited by argument
* e, then e is returned.
*/
unsigned char *most_forward_columns (unsigned char *b, unsigned char *e, unsigned int num_cols)
{
unsigned int col = 0;
unsigned int prev_width = 1;
while (b < e)
{
unsigned char ch = *b++;
if (col >=num_cols)
{
if ((ch == 033) && (Most_V_Opt == 0))
{
while ((ch == 033)
&& (0 == most_parse_color_escape (&b, e, NULL))
&& (b < e))
ch = *b++;
}
b--;
break;
}
if (IS_BYTE_PRINTABLE(ch))
{
col++;
prev_width = 1;
continue;
}
if (ch & 0x80)
{
SLwchar_Type wch;
if ((Most_UTF8_Mode)
&& (NULL != SLutf8_decode (b-1, e, &wch, NULL)))
{
b = SLutf8_skip_chars (b-1, e, 1, NULL, 1);
prev_width = SLwchar_wcwidth (wch);
col += prev_width;
continue;
}
prev_width = 4;
col += prev_width; /* <XX> */
continue;
}
if (ch == '\b')
{
if (Most_V_Opt == 0)
{
if (col < prev_width)
col = 0;
else
col -= prev_width;
}
else col += 2; /* ^H */
continue;
}
if (ch == '\r')
{
if (Most_V_Opt == 0)
{
prev_width = 1;
col = 0;
}
else col += 2; /* ^M */
continue;
}
if (ch == '\t')
{
if (Most_T_Opt == 0)
{
prev_width = Most_Tab_Width * (col/Most_Tab_Width + 1) - col;
col += prev_width;
}
else
col += 2; /* ^I */
continue;
}
if ((ch == 033) && (Most_V_Opt == 0)
&& (0 == most_parse_color_escape (&b, e, NULL)))
continue;
/* Ctrl-char ^X */
prev_width = 2;
col += prev_width;
}
return b;
}