Do not count ANSI sequences towards line length

This allows the usage of boxes with basic ANSI SGR colors and formating.

The change was made as a quick hack for myself, so it ended up not super-clean.

The problem here is that before, boxes only had a concept of line->len that would store both the
width of the line and the number of bytes contained since they were assumed to be the same. Now,
there are visible and invisble characters - sometimes the code needs to work with the visible line
length and sometimes with the byte count.

Because of that, at this point the code could use some refactoring to make it a little more
obvious which line length (visible or bytes) is being used at a given time.

So, if anybody feels like cleaning up the mess I introduced - that would be appreciated.

Works with `lolcat -f`

Tested on:
 + debian: xterm, gnome-terminal, terminator, locally and via ssh
 + win64/cygwin: compiled and tested locally and via ssh
 + win64/putty: connected to debian server

Fixes ascii-boxes/boxes#65
This commit is contained in:
Ben Dimbeck 2020-07-12 23:57:16 +02:00 committed by Thomas Jensen
parent 7ef0b3517f
commit 2356251a27
4 changed files with 78 additions and 23 deletions

View File

@ -1278,9 +1278,13 @@ static int apply_substitutions (const int mode)
perror (PROJECT);
return 1;
}
input.lines[k].vischar += buf_len - input.lines[k].len;
input.lines[k].len = buf_len;
if (input.lines[k].len > input.maxline)
input.maxline = input.lines[k].len;
if (input.lines[k].vischar > input.maxline)
input.maxline = input.lines[k].vischar;
#ifdef REGEXP_DEBUG
fprintf (stderr, "input.lines[%d] == {%d, \"%s\"}\n", k,
input.lines[k].len, input.lines[k].text);
@ -1350,6 +1354,9 @@ static int read_all_input (const int use_stdin)
*/
{
char buf[LINE_MAX+2]; /* input buffer */
char c;
size_t invis; /* counts invisible characters */
int ansipos; /* progression of ansi sequence */
size_t input_size = 0; /* number of elements allocated */
line_t *tmp = NULL;
char *temp = NULL; /* string resulting from tab exp. */
@ -1410,11 +1417,51 @@ static int read_all_input (const int use_stdin)
input.lines[input.anz_lines].text = (char *) strdup (buf);
}
/*
* Find ANSI CSI/ESC sequences
*/
invis = 0;
ansipos = 0;
for (i=0; i<input.lines[input.anz_lines].len; ++i) {
c = input.lines[input.anz_lines].text[i];
if (ansipos == 0 && c == 0x1b){
/* Found an ESC char, count it as invisible and move 1 forward in the
* detection of CSI sequences */
ansipos++;
invis++;
} else if (ansipos == 1 && c == '[') {
/* Found '[' char after ESC. A CSI sequence has started. */
ansipos++;
invis++;
} else if (ansipos == 1 && c >= 0x40 && c <= 0x5f) {
/* Found a byte designating the end of a two-byte
* escape sequence */
invis++;
ansipos = 0;
} else if (ansipos == 2) {
/* Inside CSI sequence - Keep counting bytes as invisible */
invis++;
/* A char between 0x40 and 0x7e signals the end of an CSI or escape sequence */
if (c >= 0x40 && c <= 0x7e)
ansipos = 0;
}
}
/* Save the count of invisible chars and visible chars.
* I'm happy about suggestions for a more elegant handling
* of this and the use of .invis and .vischar (and .len)
* in the other functions.
*/
input.lines[input.anz_lines].invis = invis;
input.lines[input.anz_lines].vischar = input.lines[input.anz_lines].len - invis;
/*
* Update length of longest line
*/
if (input.lines[input.anz_lines].len > input.maxline)
input.maxline = input.lines[input.anz_lines].len;
if (input.lines[input.anz_lines].vischar > input.maxline) {
input.maxline = input.lines[input.anz_lines].vischar;
}
/*
* next please

View File

@ -147,6 +147,8 @@ extern opt_t opt;
typedef struct {
size_t len; /* length of text in characters */
char *text; /* line content, tabs expanded */
size_t invis; /* number of characters part of an ansi sequence */
size_t vischar; /* number of normal printable characters */
size_t *tabpos; /* tab positions in expanded work strings */
size_t tabpos_len; /* number of tabs in a line */
size_t num_leading_blanks; /* number of spaces at the start of the line after justification */

View File

@ -709,30 +709,32 @@ static int justify_line (line_t *line, int skew)
case 'l':
if (opt.design->indentmode == 't') {
memmove (line->text+input.indent, p, newlen+1);
line->len = newlen + input.indent;
line->vischar = newlen + input.indent - line->invis;
line->len = line->vischar;
line->num_leading_blanks = input.indent;
}
else {
memmove (line->text, p, newlen+1);
line->len = newlen;
line->vischar = newlen - line->invis;
line->len = line->vischar;
line->num_leading_blanks = 0;
}
break;
case 'c':
if (opt.design->indentmode == 't') {
shift = (input.maxline-input.indent-newlen) / 2 + input.indent;
shift = (input.maxline - input.indent - newlen + line->invis) / 2 + input.indent;
skew -= input.indent;
if ((input.maxline-input.indent-newlen) % 2 && skew == 1)
if ((input.maxline-input.indent - newlen) % 2 && skew == 1)
++shift;
}
else {
shift = (input.maxline - newlen) / 2;
if ((input.maxline - newlen) % 2 && skew == 1)
shift = (input.maxline - newlen + line->invis) / 2;
if ((input.maxline - newlen +line->invis) % 2 && skew == 1)
++shift;
}
newtext = (char *) calloc (shift + newlen + 1, sizeof(char));
newtext = (char *) calloc (shift + newlen + line->invis + 1, sizeof(char));
if (newtext == NULL) {
perror (PROJECT);
return 2;
@ -750,18 +752,19 @@ static int justify_line (line_t *line, int skew)
newlen, shift, spaces);
#endif
strncpy (newtext, spaces, shift);
strncat (newtext, p, newlen);
newtext[shift+newlen] = '\0';
strncat (newtext, p, newlen+line->invis);
newtext[shift+newlen+line->invis] = '\0';
BFREE (spaces);
BFREE (line->text);
line->text = newtext;
line->len = shift + newlen;
line->len = shift + newlen - line->invis;
line->vischar = line->len;
line->num_leading_blanks = shift;
break;
case 'r':
shift = input.maxline - newlen;
newtext = (char *) calloc (input.maxline+1, sizeof(char));
shift = input.maxline - (newlen - line->invis);
newtext = (char *) calloc (input.maxline+1000, sizeof(char));
if (newtext == NULL) {
perror (PROJECT);
return 2;
@ -776,10 +779,11 @@ static int justify_line (line_t *line, int skew)
spaces[shift] = '\0';
strncpy (newtext, spaces, shift);
strncat (newtext, p, newlen);
newtext[input.maxline] = '\0';
newtext[input.maxline+line->invis] = '\0';
BFREE (spaces);
BFREE (line->text);
line->text = newtext;
line->vischar = input.maxline;
line->len = input.maxline;
line->num_leading_blanks = shift;
break;
@ -796,8 +800,9 @@ static int justify_line (line_t *line, int skew)
size_t k;
input.maxline = 0;
for (k=0; k<input.anz_lines; ++k) {
if (input.lines[k].len > input.maxline)
if (input.lines[k].len > input.maxline) {
input.maxline = input.lines[k].len;
}
}
}
@ -1016,7 +1021,7 @@ int output_box (const sentry_t *thebox)
rc = justify_line (input.lines+ti, hpr-hpl);
if (rc)
return rc;
r = input.maxline - input.lines[ti].len;
r = input.maxline - input.lines[ti].vischar;
trailspc[r] = '\0';
restored_indent = tabbify_indent (ti, indentspc, indentspclen);
if (input.lines[ti].num_leading_blanks == SIZE_MAX) {

View File

@ -805,13 +805,13 @@ int remove_box()
input.maxline += opt.design->shape[NE].width;
for (j=0; j<input.anz_lines; ++j) {
input.lines[j].text = (char *)
realloc (input.lines[j].text, input.maxline+1);
realloc (input.lines[j].text, input.maxline+input.lines[j].invis+1);
if (input.lines[j].text == NULL) {
perror (PROJECT);
return 1;
}
memset (input.lines[j].text + input.lines[j].len, ' ',
input.maxline - input.lines[j].len);
input.maxline - input.lines[j].len + input.lines[j].invis);
input.lines[j].text[input.maxline] = '\0';
input.lines[j].len = input.maxline;
}
@ -983,8 +983,9 @@ int remove_box()
}
input.maxline = 0;
for (j=0; j<input.anz_lines; ++j) {
if (input.lines[j].len > input.maxline)
input.maxline = input.lines[j].len;
if (input.lines[j].len - input.lines[j].invis > input.maxline) {
input.maxline = input.lines[j].len - input.lines[j].invis;
}
}
memset (input.lines + input.anz_lines, 0,
(BMAX (textstart - boxstart, 0) + BMAX (boxend - textend, 0)) *