From d6e65a89e8f8e87b2dc91159a743ca98dfef63ff Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 5 Jan 2019 20:42:26 -0800 Subject: [PATCH] Added search functionality. --- ascii.c | 329 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 217 insertions(+), 112 deletions(-) diff --git a/ascii.c b/ascii.c index fe7a6d4..fedf85a 100644 --- a/ascii.c +++ b/ascii.c @@ -35,6 +35,11 @@ #include #include +typedef enum {BROWSE_MODE, INSERT_MODE, SEARCH_MODE} Mode; + +#define DELETE_KEY 127 +#define ESCAPE_KEY 27 + // The descriptive names of the ASCII characters const char *NAMES[128][2] = { {"NUL", "null"}, // 0 @@ -81,7 +86,8 @@ static int new_colorpair(int fg, int bg) } int DECIMAL_COLORS, HEX_COLORS, CHAR_COLORS, DESCRIPTION_COLORS, OUTPUT_CHAR_COLORS, - OUTPUT_ESCAPE_COLORS, OUTPUT_LABEL_COLORS, BROWSE_MODE_COLORS, INSERT_MODE_COLORS; + OUTPUT_ESCAPE_COLORS, OUTPUT_LABEL_COLORS, SEARCH_BAR_COLORS, BROWSE_MODE_COLORS, + INSERT_MODE_COLORS, SEARCH_MODE_COLORS; const int COL_WIDTH = 42; int W = 0; @@ -158,25 +164,30 @@ static void redraw(int selected) last_selected = selected; } -void draw_output(char *start, char *stop) +static int find_ascii_match(int start, int fallback, int direction, char *searchbuf) { - attron(OUTPUT_LABEL_COLORS); - mvprintw(H-1, 0, " Output:"); - attroff(OUTPUT_LABEL_COLORS); - addch(' '); - attroff(OUTPUT_LABEL_COLORS); - for (char *c = start; c < stop; c++) { - if (' ' <= *c && *c <= '~') { // printable range - printwattrs(OUTPUT_CHAR_COLORS, "%c", *c); - } else { - printwattrs(OUTPUT_ESCAPE_COLORS, "\\x%02X", *c); + if (searchbuf[0] == '\0') return fallback; + char tmp[COL_WIDTH+6]; + for (int i = 0; i <= 127; i++) { + int c = (start + i*direction) % 128; // relative to starting point + if (searchbuf[0] == c && searchbuf[1] == '\0') + return c; + // Only do full match for search strings longer than 1 + if (searchbuf[1]) { + if (NAMES[c][1]) { + sprintf(tmp, "%d 0x%02X 0x%02x %s %s", c, c, c, NAMES[c][0] ? NAMES[c][0] : "", NAMES[c][1]); + } else if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) { + sprintf(tmp, "%d 0x%02X 0x%02x %s The letter %c", c, c, c, NAMES[c][0] ? NAMES[c][0] : "", c); + } else if ('0' <= c && c <= '9') { + sprintf(tmp, "%d 0x%02X 0x%02x %s The number %c", c, c, c, NAMES[c][0] ? NAMES[c][0] : "", c); + } else { + sprintf(tmp, "%d 0x%02X 0x%02x %s The %c character", c, c, c, NAMES[c][0] ? NAMES[c][0] : "", c); + } + if (strstr(tmp, searchbuf) != NULL) + return c; } } - int y, x; - getyx(stdscr, y, x); - // Clear to end of line - for (int x2 = x; x2 < W; x2++) mvaddch(y, x2, ' '); - move(y, x); + return fallback; } int main(int argc, char *argv[]) @@ -211,8 +222,10 @@ int main(int argc, char *argv[]) OUTPUT_CHAR_COLORS = new_colorpair(COLOR_BLACK, COLOR_WHITE) | A_BOLD; OUTPUT_ESCAPE_COLORS = new_colorpair(COLOR_RED, COLOR_WHITE) | A_BOLD; OUTPUT_LABEL_COLORS = new_colorpair(COLOR_BLACK, COLOR_YELLOW); + SEARCH_BAR_COLORS = new_colorpair(COLOR_BLACK, COLOR_GREEN); BROWSE_MODE_COLORS = new_colorpair(COLOR_BLACK, COLOR_BLUE); INSERT_MODE_COLORS = new_colorpair(COLOR_WHITE, COLOR_RED) | A_BOLD; + SEARCH_MODE_COLORS = new_colorpair(COLOR_WHITE, COLOR_GREEN) | A_BOLD; size_t chunk = 256; size_t buf_size = chunk, buf_i = 0; @@ -235,6 +248,9 @@ int main(int argc, char *argv[]) } } + char searchbuf[1024] = {'\0'}; + size_t searchlen = 0; + // If any command line args, treat them as bytes to append to // the output buffer for (int i = 1; i < argc; i++) { @@ -246,118 +262,207 @@ int main(int argc, char *argv[]) } } - int insert_mode = 0; - int selected = 0; + Mode mode = BROWSE_MODE; + int prev_selected = 0, selected = 0; while (1) { redraw(selected); // Title bar move(0,0); - if (insert_mode) { - const char *str = "ASCII TABLE (typing, press 'esc' to browse)"; - int spacer = W/2-strlen(str)/2; - printwattrs(INSERT_MODE_COLORS, "%*s%s%*s", spacer, "", str, spacer+1, ""); + char *title; + int title_colors; + switch (mode) { + case INSERT_MODE: + title = "ASCII TABLE (typing, 'esc' to browse)"; + title_colors = INSERT_MODE_COLORS; + break; + case SEARCH_MODE: + title = "ASCII TABLE (searching, 'esc' to browse)"; + title_colors = SEARCH_MODE_COLORS; + break; + case BROWSE_MODE: + title = "ASCII TABLE (browsing, 'esc' to exit)"; + title_colors = BROWSE_MODE_COLORS; + break; + } + int spacer = W/2-strlen(title)/2; + printwattrs(title_colors, "%*s%s%*s", spacer, "", title, spacer+1, ""); + + if (mode == SEARCH_MODE) { + attron(SEARCH_BAR_COLORS); + mvprintw(H-1, 0, " Search:"); + attroff(SEARCH_BAR_COLORS); + addch(' '); + printwattrs(OUTPUT_CHAR_COLORS, "%s", searchbuf); } else { - const char *str = "ASCII TABLE (browsing, press 'esc' to exit)"; - int spacer = W/2-strlen(str)/2; - printwattrs(BROWSE_MODE_COLORS, "%*s%s%*s", spacer, "", str, spacer+1, ""); + // Bar at the bottom of the screen showing the output text: + // left-truncate output so the end of it fits on screen: + char *visible = &outbuf[buf_i]; + for (int space = W-strlen(" Output: ")-1-5; space > 0 && visible > outbuf; visible--) { + int chlen = (' ' <= *visible && *visible <= '~') ? 1 : 4; // printable range + if (chlen > space) break; + else space -= chlen; + } + attron(mode == INSERT_MODE ? INSERT_MODE_COLORS : OUTPUT_LABEL_COLORS); + mvprintw(H-1, 0, " Output:"); + attroff(mode == INSERT_MODE ? INSERT_MODE_COLORS : OUTPUT_LABEL_COLORS); + addch(' '); + for (char *c = visible; c < &outbuf[buf_i]; c++) { + if (' ' <= *c && *c <= '~') { // printable range + printwattrs(OUTPUT_CHAR_COLORS, "%c", *c); + } else { + printwattrs(OUTPUT_ESCAPE_COLORS, "\\x%02X", *c); + } + } } - // Bar at the bottom of the screen showing the output text: - // left-truncate output so the end of it fits on screen: - char *visible = &outbuf[buf_i]; - for (int space = W-strlen(" Output: ")-1-5; space > 0 && visible > outbuf; visible--) { - int chlen = (' ' <= *visible && *visible <= '~') ? 1 : 4; // printable range - if (chlen > space) break; - else space -= chlen; - } - draw_output(visible, &outbuf[buf_i]); + int y, x; + getyx(stdscr, y, x); + // Clear to end of line + for (int x2 = x; x2 < W; x2++) mvaddch(y, x2, ' '); + move(y, x); + refresh(); next_input:; int key = getch(); - if (insert_mode) { - if (key == 27) { - insert_mode = 0; - continue; - } else if (key == 127) { - // Backspace - if (buf_i > 0) buf_i--; - continue; - } else if (0 <= key && key < 127) { - outbuf[buf_i++] = (char)key; - if (buf_i >= buf_size) { - buf_size *= 2; - outbuf = realloc(outbuf, buf_size); - } - continue; - } - } - switch (key) { - case 'j': case KEY_DOWN: - selected++; - break; - - case 'J': - selected += 10; - break; - - case 'k': case KEY_UP: - selected--; - break; - - case 'K': - selected -= 10; - break; - - case 'l': case 'L': case KEY_RIGHT: case KEY_SRIGHT: - selected += H-2; - break; - - case 'h': case 'H': case KEY_LEFT: case KEY_SLEFT: - selected -= H-2; - break; - - case 'i': case 'a': - insert_mode = 1; - break; - - case KEY_RESIZE: - // To prevent wasted work, wait until 200ms has elapsed - // with no additional resizes before doing anything - timeout(0); - do { - // Wait 200ms - usleep(2e5); - // Peek if there's another resize event - int k = getch(); - if (k == KEY_RESIZE) continue; - if (k > 0) ungetch(k); - } while (0); - // Back to blocking input - timeout(-1); - W = getmaxx(stdscr); - H = getmaxy(stdscr); - break; - - case KEY_ENTER: case '\n': - outbuf[buf_i++] = (char)selected; - if (buf_i >= buf_size) { - buf_size *= 2; - outbuf = realloc(outbuf, buf_size); + switch (mode) { + case INSERT_MODE: + if (key == ESCAPE_KEY) { + mode = BROWSE_MODE; + } else if (key == DELETE_KEY) { // Backspace + if (buf_i > 0) buf_i--; + } else if (0 <= key && key < 127) { + outbuf[buf_i++] = (char)key; + if (buf_i >= buf_size) { + buf_size *= 2; + outbuf = realloc(outbuf, buf_size); + } + } else { + goto next_input; // skip redraw } break; - case KEY_BACKSPACE: case KEY_DC: case 127: - if (buf_i > 0) buf_i--; + + case SEARCH_MODE: + switch (key) { + case ESCAPE_KEY: // Escape + selected = prev_selected; + searchbuf[0] = '\0'; + searchlen = 0; + mode = BROWSE_MODE; + break; + + case '\n': + mode = BROWSE_MODE; + break; + + case DELETE_KEY: + if (searchlen > 0) { + searchlen--; + searchbuf[searchlen] = '\0'; + selected = find_ascii_match(prev_selected, prev_selected, 1, searchbuf); + } + break; + + default: + if (0 <= key && key < 127) { + if (searchlen < sizeof(searchbuf)-1) { + searchbuf[searchlen++] = (char)key; + searchbuf[searchlen] = '\0'; + } + selected = find_ascii_match(prev_selected, prev_selected, 1, searchbuf); + break; + } + goto next_input; // skip redraw + } break; - case 'q': case 'Q': case KEY_CANCEL: case KEY_CLOSE: case KEY_EXIT: case 27: - endwin(); - write(STDOUT_FILENO, outbuf, buf_i); - return 0; - default: goto next_input; + case BROWSE_MODE: + switch (key) { + case 'j': case KEY_DOWN: + selected++; + break; + + case 'J': + selected += 10; + break; + + case 'k': case KEY_UP: + selected--; + break; + + case 'K': + selected -= 10; + break; + + case 'l': case 'L': case KEY_RIGHT: case KEY_SRIGHT: + selected += H-2; + break; + + case 'h': case 'H': case KEY_LEFT: case KEY_SLEFT: + selected -= H-2; + break; + + case 'i': case 'a': + mode = INSERT_MODE; + prev_selected = selected; + break; + + case '/': + mode = SEARCH_MODE; + searchbuf[0] = '\0'; + searchlen = 0; + break; + + case 'n': + selected = find_ascii_match(selected+1, selected, 1, searchbuf); + break; + + case 'N': + selected = find_ascii_match(selected-1, selected, -1, searchbuf); + break; + + case KEY_RESIZE: + // To prevent wasted work, wait until 200ms has elapsed + // with no additional resizes before doing anything + timeout(0); + do { + // Wait 200ms + usleep(2e5); + // Peek if there's another resize event + int k = getch(); + if (k == KEY_RESIZE) continue; + if (k > 0) ungetch(k); + } while (0); + // Back to blocking input + timeout(-1); + W = getmaxx(stdscr); + H = getmaxy(stdscr); + break; + + case KEY_ENTER: case '\n': + outbuf[buf_i++] = (char)selected; + if (buf_i >= buf_size) { + buf_size *= 2; + outbuf = realloc(outbuf, buf_size); + } + break; + + case KEY_BACKSPACE: case KEY_DC: case DELETE_KEY: + if (buf_i > 0) buf_i--; + break; + + case 'q': case 'Q': case KEY_CANCEL: case KEY_CLOSE: case KEY_EXIT: case ESCAPE_KEY: + endwin(); + write(STDOUT_FILENO, outbuf, buf_i); + return 0; + + default: + goto next_input; // skip redraw + } + break; } if (selected < 0) selected = 0; - if (selected >= 128) selected = 127; + if (selected > 127) selected = 127; } endwin();