}
char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
- const char *i, *begin = NULL;
+ const char *begin = NULL;
enum {
STATE_OTHER,
STATE_ESCAPE,
STATE_CSO,
} state = STATE_OTHER;
char *obuf = NULL;
- size_t osz = 0, isz, shift[2] = {};
+ size_t osz = 0, isz, shift[2] = {}, n_carriage_returns = 0;
FILE *f;
assert(ibuf);
* 1. Replaces TABs by 8 spaces
* 2. Strips ANSI color sequences (a subset of CSI), i.e. ESC '[' … 'm' sequences
* 3. Strips ANSI operating system sequences (CSO), i.e. ESC ']' … BEL sequences
+ * 4. Strip trailing \r characters (since they would "move the cursor", but have no
+ * other effect).
*
* Everything else will be left as it is. In particular other ANSI sequences are left as they are, as
* are any other special characters. Truncated ANSI sequences are left-as is too. This call is
if (!f)
return NULL;
- for (i = *ibuf; i < *ibuf + isz + 1; i++) {
+ for (const char *i = *ibuf; i < *ibuf + isz + 1; i++) {
switch (state) {
case STATE_OTHER:
if (i >= *ibuf + isz) /* EOT */
break;
- else if (*i == '\x1B')
+
+ if (*i == '\r') {
+ n_carriage_returns++;
+ break;
+ } else if (*i == '\n')
+ /* Ignore carriage returns before new line */
+ n_carriage_returns = 0;
+ for (; n_carriage_returns > 0; n_carriage_returns--)
+ fputc('\r', f);
+
+ if (*i == '\x1B')
state = STATE_ESCAPE;
else if (*i == '\t') {
fputs(" ", f);
break;
case STATE_ESCAPE:
+ assert(n_carriage_returns == 0);
+
if (i >= *ibuf + isz) { /* EOT */
fputc('\x1B', f);
advance_offsets(i - *ibuf, highlight, shift, 1);
break;
case STATE_CSI:
+ assert(n_carriage_returns == 0);
if (i >= *ibuf + isz || /* EOT … */
!strchr("01234567890;m", *i)) { /* … or invalid chars in sequence */
break;
case STATE_CSO:
+ assert(n_carriage_returns == 0);
if (i >= *ibuf + isz || /* EOT … */
(*i != '\a' && (uint8_t) *i < 32U) || (uint8_t) *i > 126U) { /* … or invalid chars in sequence */
fclose(f);
return mfree(obuf);
}
-
fclose(f);
free_and_replace(*ibuf, obuf);
assert_se(streq(p, "\x1B[waldo"));
free(p);
+ assert_se(p = strdup("\r\rwaldo"));
+ assert_se(strip_tab_ansi(&p, NULL, NULL));
+ assert_se(streq(p, "\r\rwaldo"));
+ free(p);
+
+ assert_se(p = strdup("waldo\r\r"));
+ assert_se(strip_tab_ansi(&p, NULL, NULL));
+ assert_se(streq(p, "waldo"));
+ free(p);
+
+ assert_se(p = strdup("waldo\r\r\n\r\n"));
+ assert_se(strip_tab_ansi(&p, NULL, NULL));
+ assert_se(streq(p, "waldo\n\n"));
+ free(p);
+
assert_se(terminal_urlify_path("/etc/fstab", "i am a fabulous link", &urlified) >= 0);
assert_se(p = strjoin("something ", urlified, " something-else"));
assert_se(q = strdup(p));
assert_se(utf8_is_printable("\342\204\242", 3));
assert_se(!utf8_is_printable("\341\204", 2));
assert_se(utf8_is_printable("ąę", 4));
+ assert_se(!utf8_is_printable("\r", 1));
+ assert_se(utf8_is_printable("\n", 1));
+ assert_se(utf8_is_printable("\t", 1));
}
static void test_utf8_is_valid(void) {