/* ** Copyright 2001, Brian Swetland ** All Rights Reserved. Please read the provided LICENSE file. */ #include #include #include #include #include WINDOW *wlog = 0; WINDOW *wstatus = 0; WINDOW *wcmd = 0; typedef struct lline lline; struct lline { lline *next; lline *prev; int len; char text[1]; }; lline *lroot; lline *lbottom; int lcount = 0; int lnumber = 0; lline *lhistory; lline *lactive; static char ui_status[16] = { 0 }; void oops(int sig) { endwin(); exit(0); } void adjust() { int i, w, h; getmaxyx(stdscr, h, w); if(wlog) { delwin(wlog); delwin(wstatus); delwin(wcmd); } wlog = subwin(stdscr, h-2, w-1, 0, 0); wstatus = subwin(stdscr, 1, w-1, h-2, 0); wcmd = subwin(stdscr, 1, w-1, h-1, 0); wattrset(wstatus, A_REVERSE); mvwhline(wstatus, 0, 0, '-', w - 1); keypad(wcmd, TRUE); } #define LINEMAX 1024 static char assemble[LINEMAX]; static int acount = 0; void log_repaint() { int w, h; lline *l = lbottom; werase(wlog); getmaxyx(wlog, h, w); while((h > 0) && (l != lroot)){ h--; mvwprintw(wlog, h, 0, "%s",l->text); l = l->next; } wrefresh(wlog); } void status_repaint() { int w, h; getmaxyx(wstatus, h, w); mvwhline(wstatus, 0, 0, '-', w); mvwprintw(wstatus, 0, 0, "-- REMOTE -- (%d/%d) ", lnumber, lcount); if((lbottom != lroot->next) && (lbottom != lroot)) wprintw(wstatus, "-- SCROLL "); wprintw(wstatus,"[%s] ", ui_status); } void log_newline(const char *text, int len) { lline *l = (lline*) malloc(len + sizeof(lline) + 1); l->next = lroot->next; l->prev = lroot; lroot->next->prev = l; lroot->next = l; l->len = len; memcpy(l->text, text, len); l->text[len] = 0; lcount++; /* only scroll if we're at the bottom of the screen */ if((lbottom == l->next) || (lbottom == lroot)) { lbottom = l; lnumber = lcount; log_repaint(); } } void history_append(const char *text) { int len; lline *l; len = strlen(text); l = (lline*) malloc(len + sizeof(lline) + 1); l->next = lhistory; l->prev = lhistory->prev; lhistory->prev->next = l; lhistory->prev = l; l->len = len; memcpy(l->text, text, len); l->text[len] = 0; lactive = lhistory; } const char *history_prev() { if(lactive->prev != lhistory){ lactive = lactive->prev; return lactive->text; } else { return 0; } } const char *history_next() { if(lactive != lhistory){ lactive = lactive->next; return lactive->text; } else { return 0; } } void log_append(const char *msg, int max) { while(*msg){ if(max-- == 0) break; if((*msg == '\r') || (*msg == '\n') || (acount == LINEMAX)){ if(acount > 0){ log_newline(assemble, acount); acount = 0; } } else { if(*msg < ' ') assemble[acount++] = '.'; else assemble[acount++] = *msg; } msg++; } } void debug_msg(const char *text, int len) { log_append(text, len); } void msg(const char *fmt, ...) { char buf[8192]; va_list ap; va_start(ap, fmt); vsprintf(buf, fmt, ap); va_end(ap); log_append(buf, 8192); } void status(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vsnprintf(ui_status, 15, fmt, ap); va_end(ap); ui_status[15] = 0; status_repaint(); wrefresh(wstatus); } #define MAXLINE 128 #define CTL(x) (x - 'a' + 1) void ui_home() { int w, h; getmaxyx(wlog, h, w); lbottom = lroot->prev; if(lbottom->prev == lroot){ lnumber = 0; } else { lnumber = 1; while((h > 1) && (lbottom->prev != lroot)){ lbottom = lbottom->prev; lnumber++; h--; } } log_repaint(); } void ui_end() { if(lbottom != lroot->next){ lbottom = lroot->next; lnumber = lcount; log_repaint(); } } void ui_pgup() { int w,h; getmaxyx(wlog, h, w); while(h > 0) { if(lbottom->next != lroot) { lbottom = lbottom->next; lnumber--; } h--; } log_repaint(); } void ui_pgdn() { int w,h; getmaxyx(wlog, h, w); while(h > 0) { if(lbottom->prev != lroot) { lbottom = lbottom->prev; lnumber++; } h--; } log_repaint(); } void ui_up() { if(lbottom->next != lroot) { lbottom = lbottom->next; lnumber--; log_repaint(); } } void ui_down() { if(lbottom->prev != lroot) { lbottom = lbottom->prev; lnumber++; log_repaint(); } } static char ui_line[MAXLINE+1]; /* current input line (asciiz) */ static int ui_last; /* == strlen(ui_last) */ static int ui_pos; /* x position in edit ( pos <= last ) */ static int ui_update; /* ui_line has changed and must redraw */ void ui_setline(const char *line) { if(line == 0) line = ""; strcpy(ui_line, line); ui_pos = ui_last = strlen(line); ui_update = 1; } void ui_history_up() { const char *x = history_prev(); if(x) ui_setline(x); } void ui_history_down() { const char *x = history_next(); if(x) ui_setline(x); } void ui_left() { if(ui_pos > 0) ui_pos--; } void ui_right() { if(ui_pos < ui_last) ui_pos++; } void ui_backspace() { if(ui_pos > 0){ if(ui_pos == ui_last){ ui_pos--; ui_last--; ui_line[ui_pos] = 0; } else { ui_pos--; ui_last--; memmove(ui_line + ui_pos, ui_line + ui_pos + 1, ui_last - ui_pos + 1); } ui_update = 1; } } void ui_cut_to_end() { ui_line[ui_pos] = 0; ui_last = ui_pos; ui_update = 1; } void ui_exec() { char tmp[MAXLINE + 3]; sprintf(tmp,"> %s",ui_line); status("BUSY"); history_append(ui_line); log_newline(tmp,strlen(tmp)); execute_command(ui_line); ui_setline(""); status("READY"); } void ui_cancel() { lactive = lhistory; ui_setline(""); } void ui_char(int x) { if((x >= ' ') && (x < 127) && (ui_last < MAXLINE)){ if(ui_pos == ui_last){ ui_line[ui_pos] = x; ui_pos++; ui_line[ui_pos] = 0; ui_last++; } else { memmove(ui_line + ui_pos + 1, ui_line + ui_pos, ui_last - ui_pos + 1); ui_line[ui_pos] = x; ui_last++; ui_pos++; } ui_update = 1; } else { beep(); } } void ui_run() { int x = wgetch(wcmd); switch(x){ case KEY_LEFT: ui_left(); break; case KEY_RIGHT: ui_right(); break; case CTL('p'): ui_up(); break; case CTL('n'): ui_down(); break; case CTL('k'): ui_cut_to_end(); break; case KEY_PPAGE: ui_pgup(); break; case KEY_NPAGE: ui_pgdn(); break; case KEY_HOME: ui_home(); break; case KEY_END: ui_end(); break; case KEY_BACKSPACE: case '\b': ui_backspace(); break; case '\r': case '\n': ui_exec(); break; case KEY_UP: ui_history_up(); break; case KEY_DOWN: ui_history_down(); break; case 27: ui_cancel(); break; case CTL('l'): /* ^L */ redrawwin(wstatus); redrawwin(wlog); redrawwin(wcmd); doupdate(); break; case 0: break; default: ui_char(x); break; } if(ui_update){ werase(wcmd); mvwprintw(wcmd, 0, 0, "> %s", ui_line); } status_repaint(); wrefresh(wstatus); wmove(wcmd, 0, 2 + ui_pos); wrefresh(wcmd); } void ui_init() { signal(SIGINT, oops); ui_line[0] = 0; ui_pos = 0; ui_last = 0; ui_update = 1; lroot = (lline*) malloc(sizeof(lline)); lroot->next = lroot; lroot->prev = lroot; lroot->len = 0; lroot->text[0] = 0; lbottom = lroot; lhistory = (lline*) malloc(sizeof(lline)); lhistory->next = lhistory; lhistory->prev = lhistory; lhistory->len = 0; lhistory->text[0] = 0; lactive = lhistory; initscr(); noecho(); nonl(); cbreak(); keypad(stdscr, TRUE); adjust(); refresh(); status_repaint(); wrefresh(wstatus); wmove(wcmd, 0, 2 + ui_pos); wrefresh(wcmd); status("READY"); }