libansilove

Library for converting ANSI, ASCII, and other formats to PNG
Log | Files | Refs | README | LICENSE

commit 99edda3865bd2e6dd4f1d079e044e09d46b3849d
parent ab3cc58956427af1fb831bf0378cf6b061d1bc9f
Author: Frederic Cambus <fred@statdns.com>
Date:   Fri, 25 Sep 2020 22:43:31 +0200

Refactor the ANSI loader to use a state machine.

Diffstat:
Msrc/loaders/ansi.c | 545+++++++++++++++++++++++++++++++++++++++++--------------------------------------
1 file changed, 282 insertions(+), 263 deletions(-)

diff --git a/src/loaders/ansi.c b/src/loaders/ansi.c @@ -30,6 +30,10 @@ #define ANSI_SEQUENCE_MAX_LENGTH 14 #define ANSI_BUFFER_SIZE 65536 +#define STATE_TEXT 0 +#define STATE_SEQUENCE 1 +#define STATE_END 2 + /* Character structure */ struct ansiChar { int32_t column; @@ -83,6 +87,7 @@ ansilove_ansi(struct ansilove_ctx *ctx, struct ansilove_options *options) /* character definitions */ uint8_t current_character, character; + uint8_t *cursor, state = STATE_TEXT; uint8_t ansi_sequence_character; /* default color values */ @@ -97,6 +102,7 @@ ansilove_ansi(struct ansilove_ctx *ctx, struct ansilove_options *options) int32_t saved_row = 0, saved_column = 0; /* sequence parsing variables */ + uint32_t maxlength; uint32_t seqValue, seq_line, seq_column; char *seqGrab = NULL; char *seqTok = NULL; @@ -113,336 +119,349 @@ ansilove_ansi(struct ansilove_ctx *ctx, struct ansilove_options *options) /* ANSi interpreter */ while (loop < ctx->length) { current_character = ctx->buffer[loop]; + cursor = &ctx->buffer[loop]; if (column == options->columns) { row++; column = 0; } - switch (current_character) { - case LF: - row++; - column = 0; - break; - case CR: - break; - case TAB: - column += 8; - break; - case SUB: - loop = ctx->length; - break; - case ESC: /* ANSi sequence */ - if ((loop+1 < ctx->length) && ctx->buffer[loop + 1] == 91) { - - uint32_t maxlength = fmin(ctx->length - loop + 1, ANSI_SEQUENCE_MAX_LENGTH); - for (ansi_sequence_loop = 0; ansi_sequence_loop < maxlength; ansi_sequence_loop++) { - ansi_sequence_character = ctx->buffer[loop + 2 + ansi_sequence_loop]; - - /* cursor position */ - if (ansi_sequence_character == 'H' || ansi_sequence_character == 'f') { - /* create substring from the sequence's content */ - seq_line = 1; - seq_column = 1; - seqGrab = strndup((char *)ctx->buffer + loop + 2, ansi_sequence_loop); - - if (!strncmp(seqGrab, ";", 1)) { - seq_line = 1; - seqTok = strtok(seqGrab, ";"); - - if (seqTok) - seq_column = strtonum(seqTok, 0, UINT32_MAX, &errstr); + switch (state) { + case STATE_TEXT: + switch (*cursor) { + case LF: + row++; + column = 0; + break; + case CR: + break; + case TAB: + column += 8; + break; + case SUB: + state = STATE_END; + break; + case ESC: + /* ANSi sequence */ + if ((loop+1 < ctx->length) && ctx->buffer[loop + 1] == 91) { + state = STATE_SEQUENCE; + loop++; + } + break; + default: + /* record number of columns and lines used */ + if (column > columnMax) + columnMax = column; + + if (row > rowMax) + rowMax = row; + + /* write current character in ansiChar structure */ + if (!fontData.isAmigaFont || (current_character != 12 && current_character != 13)) { + /* reallocate structure array memory */ + if (structIndex == ansi_buffer_size) { + ansi_buffer_size += ANSI_BUFFER_SIZE; + + ptr = realloc(ansi_buffer, ansi_buffer_size * sizeof(struct ansiChar)); + + if (ptr == NULL) { + ctx->error = ANSILOVE_MEMORY_ERROR; + free(ansi_buffer); + return -1; } else { - seqTok = strtok(seqGrab, ";"); - if (seqTok) - seq_line = strtonum(seqTok, 0, UINT32_MAX, &errstr); - - seqTok = strtok(NULL, ";"); - if (seqTok) - seq_column = strtonum(seqTok, 0, UINT32_MAX, &errstr); + ansi_buffer = ptr; } - - /* set the positions */ - row = seq_line-1; - column = seq_column-1; - - loop += ansi_sequence_loop+2; - free(seqGrab); - break; } - /* cursor up */ - if (ansi_sequence_character == 'A') { - /* create substring from the sequence's content */ - seqGrab = strndup((char *)ctx->buffer + loop + 2, ansi_sequence_loop); + if (invert) { + ansi_buffer[structIndex].background = foreground % 8; + ansi_buffer[structIndex].foreground = background + (foreground & 8); + } else { + ansi_buffer[structIndex].background = + background24 ? background24 : background; - /* now get escape sequence's position value */ - uint32_t seq_line = strtonum(seqGrab, 0, UINT32_MAX, &errstr); - free(seqGrab); + ansi_buffer[structIndex].foreground = + foreground24 ? foreground24 : foreground; + } + ansi_buffer[structIndex].character = current_character; + ansi_buffer[structIndex].column = column; + ansi_buffer[structIndex].row = row; - row -= seq_line ? seq_line : 1; + structIndex++; + column++; + } + } + break; + case STATE_SEQUENCE: + maxlength = fmin(ctx->length - loop, ANSI_SEQUENCE_MAX_LENGTH); + for (ansi_sequence_loop = 0; ansi_sequence_loop < maxlength; ansi_sequence_loop++) { + ansi_sequence_character = ctx->buffer[loop + ansi_sequence_loop]; + + /* cursor position */ + if (ansi_sequence_character == 'H' || ansi_sequence_character == 'f') { + /* create substring from the sequence's content */ + seq_line = 1; + seq_column = 1; + seqGrab = strndup((char *)ctx->buffer + loop, ansi_sequence_loop); + + if (!strncmp(seqGrab, ";", 1)) { + seq_line = 1; + seqTok = strtok(seqGrab, ";"); - if (row < 0) - row = 0; + if (seqTok) + seq_column = strtonum(seqTok, 0, UINT32_MAX, &errstr); + } else { + seqTok = strtok(seqGrab, ";"); + if (seqTok) + seq_line = strtonum(seqTok, 0, UINT32_MAX, &errstr); - loop += ansi_sequence_loop+2; - break; + seqTok = strtok(NULL, ";"); + if (seqTok) + seq_column = strtonum(seqTok, 0, UINT32_MAX, &errstr); } - /* cursor down */ - if (ansi_sequence_character == 'B') { - /* create substring from the sequence's content */ - seqGrab = strndup((char *)ctx->buffer + loop + 2, ansi_sequence_loop); - - /* now get escape sequence's position value */ - uint32_t seq_line = strtonum(seqGrab, 0, UINT32_MAX, &errstr); - free(seqGrab); - - row += seq_line ? seq_line : 1; + /* set the positions */ + row = seq_line-1; + column = seq_column-1; - loop += ansi_sequence_loop+2; - break; - } + loop += ansi_sequence_loop; + free(seqGrab); + break; + } - /* cursor forward */ - if (ansi_sequence_character == 'C') { - /* create substring from the sequence's content */ - seqGrab = strndup((char *)ctx->buffer + loop + 2, ansi_sequence_loop); + /* cursor up */ + if (ansi_sequence_character == 'A') { + /* create substring from the sequence's content */ + seqGrab = strndup((char *)ctx->buffer + loop, ansi_sequence_loop); - /* now get escape sequence's position value */ - uint32_t seq_column = strtonum(seqGrab, 0, UINT32_MAX, &errstr); - free(seqGrab); + /* now get escape sequence's position value */ + uint32_t seq_line = strtonum(seqGrab, 0, UINT32_MAX, &errstr); + free(seqGrab); - column += seq_column ? seq_column : 1; + row -= seq_line ? seq_line : 1; - if (column > options->columns) - column = options->columns; + if (row < 0) + row = 0; - loop += ansi_sequence_loop+2; - break; - } + loop += ansi_sequence_loop; + break; + } - /* cursor backward */ - if (ansi_sequence_character == 'D') { - /* create substring from the sequence's content */ - seqGrab = strndup((char *)ctx->buffer + loop + 2, ansi_sequence_loop); + /* cursor down */ + if (ansi_sequence_character == 'B') { + /* create substring from the sequence's content */ + seqGrab = strndup((char *)ctx->buffer + loop, ansi_sequence_loop); - /* now get escape sequence's content length */ - uint32_t seq_column = strtonum(seqGrab, 0, UINT32_MAX, &errstr); - free(seqGrab); + /* now get escape sequence's position value */ + uint32_t seq_line = strtonum(seqGrab, 0, UINT32_MAX, &errstr); + free(seqGrab); - column -= seq_column ? seq_column : 1; + row += seq_line ? seq_line : 1; - if (column < 0) - column = 0; + loop += ansi_sequence_loop; + break; + } - loop += ansi_sequence_loop+2; - break; - } + /* cursor forward */ + if (ansi_sequence_character == 'C') { + /* create substring from the sequence's content */ + seqGrab = strndup((char *)ctx->buffer + loop, ansi_sequence_loop); - /* save cursor position */ - if (ansi_sequence_character == 's') { - saved_row = row; - saved_column = column; + /* now get escape sequence's position value */ + uint32_t seq_column = strtonum(seqGrab, 0, UINT32_MAX, &errstr); + free(seqGrab); - loop += ansi_sequence_loop+2; - break; - } + column += seq_column ? seq_column : 1; - /* restore cursor position */ - if (ansi_sequence_character == 'u') { - row = saved_row; - column = saved_column; + if (column > options->columns) + column = options->columns; - loop += ansi_sequence_loop+2; - break; - } + loop += ansi_sequence_loop; + break; + } - /* erase display */ - if (ansi_sequence_character == 'J') { - /* create substring from the sequence's content */ - seqGrab = strndup((char *)ctx->buffer + loop + 2, ansi_sequence_loop); + /* cursor backward */ + if (ansi_sequence_character == 'D') { + /* create substring from the sequence's content */ + seqGrab = strndup((char *)ctx->buffer + loop, ansi_sequence_loop); - /* convert grab to an integer */ - uint32_t eraseDisplayInt = strtonum(seqGrab, 0, UINT32_MAX, &errstr); - free(seqGrab); + /* now get escape sequence's content length */ + uint32_t seq_column = strtonum(seqGrab, 0, UINT32_MAX, &errstr); + free(seqGrab); - if (eraseDisplayInt == 2) { - column = 0; - row = 0; + column -= seq_column ? seq_column : 1; - columnMax = 0; - rowMax = 0; + if (column < 0) + column = 0; - /* reset ansi buffer */ - structIndex = 0; - } - loop += ansi_sequence_loop+2; - break; - } + loop += ansi_sequence_loop; + break; + } - /* set graphics mode */ - if (ansi_sequence_character == 'm') { - /* create substring from the sequence's content */ - seqGrab = strndup((char *)ctx->buffer + loop + 2, ansi_sequence_loop); + /* save cursor position */ + if (ansi_sequence_character == 's') { + saved_row = row; + saved_column = column; - seqTok = strtok(seqGrab, ";"); - while (seqTok) { - seqValue = strtonum(seqTok, 0, UINT32_MAX, &errstr); - - if (seqValue == 0) { - background = 0; - background24 = 0; - foreground = 7; - foreground24 = 0; - bold = false; - blink = false; - invert = false; - } + loop += ansi_sequence_loop; + break; + } - if (seqValue == 1) { - if (!workbench) { - foreground += 8; - } - bold = true; - foreground24 = 0; - } + /* restore cursor position */ + if (ansi_sequence_character == 'u') { + row = saved_row; + column = saved_column; - if (seqValue == 5) { - if (!workbench && options->icecolors) - background += 8; + loop += ansi_sequence_loop; + break; + } - blink = true; - background24 = 0; - } + /* erase display */ + if (ansi_sequence_character == 'J') { + /* create substring from the sequence's content */ + seqGrab = strndup((char *)ctx->buffer + loop, ansi_sequence_loop); - if (seqValue == 7) - invert = true; + /* convert grab to an integer */ + uint32_t eraseDisplayInt = strtonum(seqGrab, 0, UINT32_MAX, &errstr); + free(seqGrab); - if (seqValue == 27) - invert = false; + if (eraseDisplayInt == 2) { + column = 0; + row = 0; - if (seqValue > 29 && seqValue < 38) { - foreground = seqValue - 30; - foreground24 = 0; + columnMax = 0; + rowMax = 0; - if (bold) - foreground += 8; - } + /* reset ansi buffer */ + structIndex = 0; + } + loop += ansi_sequence_loop; + break; + } - if (seqValue > 39 && seqValue < 48) { - background = seqValue - 40; - background24 = 0; + /* set graphics mode */ + if (ansi_sequence_character == 'm') { + /* create substring from the sequence's content */ + seqGrab = strndup((char *)ctx->buffer + loop, ansi_sequence_loop); + + seqTok = strtok(seqGrab, ";"); + while (seqTok) { + seqValue = strtonum(seqTok, 0, UINT32_MAX, &errstr); + + if (seqValue == 0) { + background = 0; + background24 = 0; + foreground = 7; + foreground24 = 0; + bold = false; + blink = false; + invert = false; + } - if (blink && options->icecolors) - background += 8; + if (seqValue == 1) { + if (!workbench) { + foreground += 8; } - - seqTok = strtok(NULL, ";"); + bold = true; + foreground24 = 0; } - loop += ansi_sequence_loop+2; - free(seqGrab); - break; - } + if (seqValue == 5) { + if (!workbench && options->icecolors) + background += 8; - /* cursor (de)activation (Amiga ANSi) */ - if (ansi_sequence_character == 'p') { - loop += ansi_sequence_loop+2; - break; - } + blink = true; + background24 = 0; + } - /* skipping set mode and reset mode sequences */ - if (ansi_sequence_character == 'h' || ansi_sequence_character == 'l') { - loop += ansi_sequence_loop+2; - break; - } + if (seqValue == 7) + invert = true; - /* skipping erase in line (EL) sequences */ - if (ansi_sequence_character == 'K') { - loop += ansi_sequence_loop+2; - break; - } + if (seqValue == 27) + invert = false; - /* PabloDraw 24-bit ANSI sequences */ - if (ansi_sequence_character == 't') { - uint32_t color_R = 0, color_G = 0, color_B = 0; + if (seqValue > 29 && seqValue < 38) { + foreground = seqValue - 30; + foreground24 = 0; - /* create substring from the sequence's content */ - seqGrab = strndup((char *)ctx->buffer + loop + 2, ansi_sequence_loop); + if (bold) + foreground += 8; + } - seqTok = strtok(seqGrab, ";"); - if (seqTok) { - seqValue = strtonum(seqTok, 0, UCHAR_MAX, &errstr); - - seqTok = strtok(NULL, ";"); - color_R = seqTok ? strtonum(seqTok, 0, UCHAR_MAX, &errstr) & 0xff : 0; - seqTok = strtok(NULL, ";"); - color_G = seqTok ? strtonum(seqTok, 0, UCHAR_MAX, &errstr) & 0xff : 0; - seqTok = strtok(NULL, ";"); - color_B = seqTok ? strtonum(seqTok, 0, UCHAR_MAX, &errstr) & 0xff : 0; - - switch (seqValue) { - case 0: - background24 = color_R << 16 | color_G << 8 | color_B; - break; - case 1: - foreground24 = color_R << 16 | color_G << 8 | color_B; - break; - } + if (seqValue > 39 && seqValue < 48) { + background = seqValue - 40; + background24 = 0; - options->truecolor = true; + if (blink && options->icecolors) + background += 8; } - loop += ansi_sequence_loop+2; - free(seqGrab); - break; + seqTok = strtok(NULL, ";"); } + + loop += ansi_sequence_loop; + free(seqGrab); + break; } - } - break; - default: - /* record number of columns and lines used */ - if (column > columnMax) - columnMax = column; - - if (row > rowMax) - rowMax = row; - - /* write current character in ansiChar structure */ - if (!fontData.isAmigaFont || (current_character != 12 && current_character != 13)) { - /* reallocate structure array memory */ - if (structIndex == ansi_buffer_size) { - ansi_buffer_size += ANSI_BUFFER_SIZE; - - ptr = realloc(ansi_buffer, ansi_buffer_size * sizeof(struct ansiChar)); - - if (ptr == NULL) { - ctx->error = ANSILOVE_MEMORY_ERROR; - free(ansi_buffer); - return -1; - } else { - ansi_buffer = ptr; - } + + /* cursor (de)activation (Amiga ANSi) */ + if (ansi_sequence_character == 'p') { + loop += ansi_sequence_loop; + break; } - if (invert) { - ansi_buffer[structIndex].background = foreground % 8; - ansi_buffer[structIndex].foreground = background + (foreground & 8); - } else { - ansi_buffer[structIndex].background = - background24 ? background24 : background; + /* skipping set mode and reset mode sequences */ + if (ansi_sequence_character == 'h' || ansi_sequence_character == 'l') { + loop += ansi_sequence_loop; + break; + } - ansi_buffer[structIndex].foreground = - foreground24 ? foreground24 : foreground; + /* skipping erase in line (EL) sequences */ + if (ansi_sequence_character == 'K') { + loop += ansi_sequence_loop; + break; } - ansi_buffer[structIndex].character = current_character; - ansi_buffer[structIndex].column = column; - ansi_buffer[structIndex].row = row; - structIndex++; - column++; + /* PabloDraw 24-bit ANSI sequences */ + if (ansi_sequence_character == 't') { + uint32_t color_R = 0, color_G = 0, color_B = 0; + + /* create substring from the sequence's content */ + seqGrab = strndup((char *)ctx->buffer + loop, ansi_sequence_loop); + + seqTok = strtok(seqGrab, ";"); + if (seqTok) { + seqValue = strtonum(seqTok, 0, UCHAR_MAX, &errstr); + + seqTok = strtok(NULL, ";"); + color_R = seqTok ? strtonum(seqTok, 0, UCHAR_MAX, &errstr) & 0xff : 0; + seqTok = strtok(NULL, ";"); + color_G = seqTok ? strtonum(seqTok, 0, UCHAR_MAX, &errstr) & 0xff : 0; + seqTok = strtok(NULL, ";"); + color_B = seqTok ? strtonum(seqTok, 0, UCHAR_MAX, &errstr) & 0xff : 0; + + switch (seqValue) { + case 0: + background24 = color_R << 16 | color_G << 8 | color_B; + break; + case 1: + foreground24 = color_R << 16 | color_G << 8 | color_B; + break; + } + + options->truecolor = true; + } + + loop += ansi_sequence_loop; + free(seqGrab); + break; + } } + state = STATE_TEXT; + break; + case STATE_END: + loop = ctx->length; + break; } loop++;