/// Detect whether the escape sequence sets one of the terminal attributes that affects how text is /// displayed other than the color. static bool is_visual_escape_seq(const wchar_t *code, size_t *resulting_length) { if (!cur_term) return false; const char *const esc2[] = { enter_bold_mode, exit_attribute_mode, enter_underline_mode, exit_underline_mode, enter_standout_mode, exit_standout_mode, flash_screen, enter_subscript_mode, exit_subscript_mode, enter_superscript_mode, exit_superscript_mode, enter_blink_mode, enter_italics_mode, exit_italics_mode, enter_reverse_mode, enter_shadow_mode, exit_shadow_mode, enter_standout_mode, exit_standout_mode, enter_secure_mode, enter_dim_mode, enter_blink_mode, enter_protected_mode, enter_alt_charset_mode, exit_alt_charset_mode}; for (size_t p = 0; p < sizeof esc2 / sizeof *esc2; p++) { if (!esc2[p]) continue; // Test both padded and unpadded version, just to be safe. Most versions of tparm don't // actually seem to do anything these days. size_t esc_seq_len = std::max(try_sequence(tparm((char *)esc2[p]), code), try_sequence(esc2[p], code)); if (esc_seq_len) { *resulting_length = esc_seq_len; return true; } } return false; }
int main(int argc, char *argv[]) { int max_used_memory; int allocation_factor; int memory_required; SEQLIST *test_sequence; max_used_memory = 2000; allocation_factor = 11; printf("running with MAX_USED_MEMORY=%d and ALLOCATION_FACTOR=%d\n", max_used_memory, allocation_factor); test_sequence = generate_sequence(max_used_memory, allocation_factor); if (VERBOSE) seq_print(test_sequence); // check that allocation can actually do something. // This becomes upper bound on binary search. if (try_sequence(test_sequence, max_used_memory * allocation_factor * 2)) { // binary search for smallest MEMORY_SIZE which can accommodate memory_required = binary_search_required_memory(test_sequence, max_used_memory - 1, max_used_memory * allocation_factor * 2); // run it one more time at the identified size. // this makes sure that the data is set from a successful run. if (try_sequence(test_sequence, memory_required)) { // check if data contents are intact if (check_data(test_sequence)) { printf("Data integrity FAIL.\n"); } else { printf("Data integrity PASS.\n"); } // print statistics printf("Memory utilization: (%d/%d)=%f\n", max_used_memory, memory_required, ((double) max_used_memory / (double) memory_required)); } else { printf("Consistency problem: binary_search_reqruired_memory " "returned %d, but final test failed\n", memory_required); } } else { printf("Requires more memory than the no-free case.\n"); } }
// PCA for term256 support, let's just detect the escape codes directly static int is_term256_escape(const wchar_t *str) { // An escape code looks like this: \x1b[38;5;<num>m // or like this: \x1b[48;5;<num>m // parse out the required prefix int len = try_sequence("\x1b[38;5;", str); if (! len) len = try_sequence("\x1b[48;5;", str); if (! len) return 0; // now try parsing out a string of digits // we need at least one if (! iswdigit(str[len])) return 0; while (iswdigit(str[len])) len++; // look for the terminating m if (str[len++] != L'm') return 0; // success return len; }
/// Detect whether the escape sequence sets foreground/background color. Note that 24-bit color /// sequences are detected by `is_csi_style_escape_seq()` if they use the ANSI X3.64 pattern for /// such sequences. This function only handles those escape sequences for setting color that rely on /// the terminfo definition and which might use a different pattern. static bool is_color_escape_seq(const wchar_t *code, size_t *resulting_length) { if (!cur_term) return false; // Detect these terminfo color escapes with parameter value up to max_colors, all of which // don't move the cursor. const char *const esc[] = { set_a_foreground, set_a_background, set_foreground, set_background, }; for (size_t p = 0; p < sizeof esc / sizeof *esc; p++) { if (!esc[p]) continue; for (int k = 0; k < max_colors; k++) { size_t esc_seq_len = try_sequence(tparm((char *)esc[p], k), code); if (esc_seq_len) { *resulting_length = esc_seq_len; return true; } } } return false; }
// search over memory sizes between low and high // report smallest size that can accommodate the sequence int binary_search_required_memory(SEQLIST *test_sequence, int low, int high) { // invariant: low not achievable, high is achievable int mid; if (low + 1 == high) { // nothing in between, we've found the smallest return high; } else { mid = (low + high + 1) / 2; if (try_sequence(test_sequence, mid)) { if (VERBOSE) printf("\tSucceeded for %d\n", mid); return(binary_search_required_memory(test_sequence, low, mid)); } else { if (VERBOSE) printf("\tFailed for %d\n", mid); return(binary_search_required_memory(test_sequence, mid, high)); } } }
/** Read a character from keyboard */ static wint_t readch() { struct mapping { const char *seq; wint_t bnd; } ; struct mapping m[]= { { "\x1b[A", LINE_UP } , { key_up, LINE_UP } , { "\x1b[B", LINE_DOWN } , { key_down, LINE_DOWN } , { key_ppage, PAGE_UP } , { key_npage, PAGE_DOWN } , { " ", PAGE_DOWN } , { "\t", PAGE_DOWN } , { 0, 0 } } ; int i; for( i=0; m[i].bnd; i++ ) { if( !m[i].seq ) { continue; } if( try_sequence(m[i].seq ) ) return m[i].bnd; } return input_common_readch(0); }
/** Calculate the width of the specified prompt. Does some clever magic to detect common escape sequences that may be embeded in a prompt, such as color codes. */ static int calc_prompt_width( wchar_t *prompt ) { int res = 0; int j, k; for( j=0; prompt[j]; j++ ) { if( prompt[j] == L'\x1b' ) { /* This is the start of an escape code. Try to guess it's width. */ int l; int len=0; int found = 0; /* Detect these terminfo color escapes with parameter value 0..7, all of which don't move the cursor */ char * esc[] = { set_a_foreground, set_a_background, set_foreground, set_background, } ; /* Detect these semi-common terminfo escapes without any parameter values, all of which don't move the cursor */ char *esc2[] = { enter_bold_mode, exit_attribute_mode, enter_underline_mode, exit_underline_mode, enter_standout_mode, exit_standout_mode, flash_screen, enter_subscript_mode, exit_subscript_mode, enter_superscript_mode, exit_superscript_mode, enter_blink_mode, enter_italics_mode, exit_italics_mode, enter_reverse_mode, enter_shadow_mode, exit_shadow_mode, enter_standout_mode, exit_standout_mode, enter_secure_mode } ; for( l=0; l < (sizeof(esc)/sizeof(char *)) && !found; l++ ) { if( !esc[l] ) continue; for( k=0; k<8; k++ ) { len = try_sequence( tparm(esc[l],k), &prompt[j] ); if( len ) { j += (len-1); found = 1; break; } } } for( l=0; l < (sizeof(esc2)/sizeof(char *)) && !found; l++ ) { if( !esc2[l] ) continue; /* Test both padded and unpadded version, just to be safe. Most versions of tparm don't actually seem to do anything these days. */ len = maxi( try_sequence( tparm(esc2[l]), &prompt[j] ), try_sequence( esc2[l], &prompt[j] )); if( len ) { j += (len-1); found = 1; } } if( !found ) { if( prompt[j+1] == L'k' ) { wchar_t *term_name = env_get( L"TERM" ); if( term_name && wcsstr( term_name, L"screen" ) == term_name ) { wchar_t *end; j+=2; found = 1; end = wcsstr( &prompt[j], L"\x1b\\" ); if( end ) { /* You'd thing this should be '(end-prompt)+2', in order to move j past the end of the string, but there is a 'j++' at the end of each lap, so j should always point to the last menged character, e.g. +1. */ j = (end-prompt)+1; } else { break; } } } } } else if( prompt[j] == L'\t' ) { res = next_tab_stop( res ); } else if( prompt[j] == L'\n' ) { res = 0; } else { /* Ordinary decent character. Just add width. */ res += wcwidth( prompt[j] ); } } return res; }
/// Returns the number of characters in the escape code starting at 'code' (which should initially /// contain \x1b). size_t escape_code_length(const wchar_t *code) { assert(code != NULL); // The only escape codes we recognize start with \x1b. if (code[0] != L'\x1b') return 0; size_t resulting_length = 0; bool found = false; if (cur_term != NULL) { // Detect these terminfo color escapes with parameter value 0..7, all of which don't move // the cursor. char *const esc[] = { set_a_foreground, set_a_background, set_foreground, set_background, }; for (size_t p = 0; p < sizeof esc / sizeof *esc && !found; p++) { if (!esc[p]) continue; for (size_t k = 0; k < 8; k++) { size_t len = try_sequence(tparm(esc[p], k), code); if (len) { resulting_length = len; found = true; break; } } } } if (cur_term != NULL) { // Detect these semi-common terminfo escapes without any parameter values, all of which // don't move the cursor. char *const esc2[] = {enter_bold_mode, exit_attribute_mode, enter_underline_mode, exit_underline_mode, enter_standout_mode, exit_standout_mode, flash_screen, enter_subscript_mode, exit_subscript_mode, enter_superscript_mode, exit_superscript_mode, enter_blink_mode, enter_italics_mode, exit_italics_mode, enter_reverse_mode, enter_shadow_mode, exit_shadow_mode, enter_standout_mode, exit_standout_mode, enter_secure_mode}; for (size_t p = 0; p < sizeof esc2 / sizeof *esc2 && !found; p++) { if (!esc2[p]) continue; // Test both padded and unpadded version, just to be safe. Most versions of tparm don't // actually seem to do anything these days. size_t len = maxi(try_sequence(tparm(esc2[p]), code), try_sequence(esc2[p], code)); if (len) { resulting_length = len; found = true; } } } if (!found) found = is_screen_name_escape_seq(code, &resulting_length); if (!found) found = is_iterm2_escape_seq(code, &resulting_length); if (!found) found = is_single_byte_escape_seq(code, &resulting_length); if (!found) found = is_csi_style_escape_seq(code, &resulting_length); if (!found) found = is_two_byte_escape_seq(code, &resulting_length); return resulting_length; }
/* Returns the number of characters in the escape code starting at 'code' (which should initially contain \x1b) */ size_t escape_code_length(const wchar_t *code) { assert(code != NULL); /* The only escape codes we recognize start with \x1b */ if (code[0] != L'\x1b') return 0; size_t resulting_length = 0; bool found = false; if (cur_term != NULL) { /* Detect these terminfo color escapes with parameter value 0..7, all of which don't move the cursor */ char * const esc[] = { set_a_foreground, set_a_background, set_foreground, set_background, }; for (size_t p=0; p < sizeof esc / sizeof *esc && !found; p++) { if (!esc[p]) continue; for (size_t k=0; k<8; k++) { size_t len = try_sequence(tparm(esc[p],k), code); if (len) { resulting_length = len; found = true; break; } } } } if (cur_term != NULL) { /* Detect these semi-common terminfo escapes without any parameter values, all of which don't move the cursor */ char * const esc2[] = { enter_bold_mode, exit_attribute_mode, enter_underline_mode, exit_underline_mode, enter_standout_mode, exit_standout_mode, flash_screen, enter_subscript_mode, exit_subscript_mode, enter_superscript_mode, exit_superscript_mode, enter_blink_mode, enter_italics_mode, exit_italics_mode, enter_reverse_mode, enter_shadow_mode, exit_shadow_mode, enter_standout_mode, exit_standout_mode, enter_secure_mode }; for (size_t p=0; p < sizeof esc2 / sizeof *esc2 && !found; p++) { if (!esc2[p]) continue; /* Test both padded and unpadded version, just to be safe. Most versions of tparm don't actually seem to do anything these days. */ size_t len = maxi(try_sequence(tparm(esc2[p]), code), try_sequence(esc2[p], code)); if (len) { resulting_length = len; found = true; } } } if (!found) { if (code[1] == L'k') { /* This looks like the escape sequence for setting a screen name */ const env_var_t term_name = env_get_string(L"TERM"); if (!term_name.missing() && string_prefixes_string(L"screen", term_name)) { const wchar_t * const screen_name_end_sentinel = L"\x1b\\"; const wchar_t *screen_name_end = wcsstr(&code[2], screen_name_end_sentinel); if (screen_name_end != NULL) { const wchar_t *escape_sequence_end = screen_name_end + wcslen(screen_name_end_sentinel); resulting_length = escape_sequence_end - code; } else { /* Consider just <esc>k to be the code */ resulting_length = 2; } found = true; } } } if (! found) { /* Generic VT100 one byte sequence: CSI followed by something in the range @ through _ */ if (code[1] == L'[' && (code[2] >= L'@' && code[2] <= L'_')) { resulting_length = 3; found = true; } } if (! found) { /* Generic VT100 CSI-style sequence. <esc>, followed by zero or more ASCII characters NOT in the range [@,_], followed by one character in that range */ if (code[1] == L'[') { // Start at 2 to skip over <esc>[ size_t cursor = 2; for (; code[cursor] != L'\0'; cursor++) { /* Consume a sequence of ASCII characters not in the range [@, ~] */ wchar_t c = code[cursor]; /* If we're not in ASCII, just stop */ if (c > 127) break; /* If we're the end character, then consume it and then stop */ if (c >= L'@' && c <= L'~') { cursor++; break; } } /* curs now indexes just beyond the end of the sequence (or at the terminating zero) */ found = true; resulting_length = cursor; } } if (! found) { /* Generic VT100 two byte sequence: <esc> followed by something in the range @ through _ */ if (code[1] >= L'@' && code[1] <= L'_') { resulting_length = 2; found = true; } } return resulting_length; }
/** Calculate layout information for the given prompt. Does some clever magic to detect common escape sequences that may be embeded in a prompt, such as color codes. */ static prompt_layout_t calc_prompt_layout(const wchar_t *prompt) { size_t current_line_width = 0; size_t j, k; prompt_layout_t prompt_layout = {}; prompt_layout.line_count = 1; for (j=0; prompt[j]; j++) { if (prompt[j] == L'\x1b') { /* This is the start of an escape code. Try to guess its width. */ size_t p; int len=0; bool found = false; /* Detect these terminfo color escapes with parameter value 0..7, all of which don't move the cursor */ char * const esc[] = { set_a_foreground, set_a_background, set_foreground, set_background, } ; /* Detect these semi-common terminfo escapes without any parameter values, all of which don't move the cursor */ char * const esc2[] = { enter_bold_mode, exit_attribute_mode, enter_underline_mode, exit_underline_mode, enter_standout_mode, exit_standout_mode, flash_screen, enter_subscript_mode, exit_subscript_mode, enter_superscript_mode, exit_superscript_mode, enter_blink_mode, enter_italics_mode, exit_italics_mode, enter_reverse_mode, enter_shadow_mode, exit_shadow_mode, enter_standout_mode, exit_standout_mode, enter_secure_mode } ; for (p=0; p < sizeof esc / sizeof *esc && !found; p++) { if (!esc[p]) continue; for (k=0; k<8; k++) { len = try_sequence(tparm(esc[p],k), &prompt[j]); if (len) { j += (len-1); found = true; break; } } } /* PCA for term256 support, let's just detect the escape codes directly */ if (! found) { len = is_term256_escape(&prompt[j]); if (len) { j += (len - 1); found = true; } } for (p=0; p < (sizeof(esc2)/sizeof(char *)) && !found; p++) { if (!esc2[p]) continue; /* Test both padded and unpadded version, just to be safe. Most versions of tparm don't actually seem to do anything these days. */ len = maxi(try_sequence(tparm(esc2[p]), &prompt[j]), try_sequence(esc2[p], &prompt[j])); if (len) { j += (len-1); found = true; } } if (!found) { if (prompt[j+1] == L'k') { const env_var_t term_name = env_get_string(L"TERM"); if (!term_name.missing() && wcsstr(term_name.c_str(), L"screen") == term_name) { const wchar_t *end; j+=2; found = true; end = wcsstr(&prompt[j], L"\x1b\\"); if (end) { /* You'd thing this should be '(end-prompt)+2', in order to move j past the end of the string, but there is a 'j++' at the end of each lap, so j should always point to the last menged character, e.g. +1. */ j = (end-prompt)+1; } else { break; } } } } } else if (prompt[j] == L'\t') { current_line_width = next_tab_stop(current_line_width); } else if (prompt[j] == L'\n' || prompt[j] == L'\f') { /* PCA: At least one prompt uses \f\r as a newline. It's unclear to me what this is meant to do, but terminals seem to treat it as a newline so we do the same. */ current_line_width = 0; prompt_layout.line_count += 1; } else if (prompt[j] == L'\r') { current_line_width = 0; } else { /* Ordinary decent character. Just add width. This returns -1 for a control character - don't add that. */ current_line_width += fish_wcwidth_min_0(prompt[j]); prompt_layout.max_line_width = maxi(prompt_layout.max_line_width, current_line_width); } } prompt_layout.last_line_width = current_line_width; return prompt_layout; }