#include #include #include #include #include #include #include "term.h" #include "myc_mem.h" #include "myc_str.h" #include "sys_select.h" #define ESC_TMO 1000 #define REF_TMO 100 #define HISTORY_LINES 256 #define L_ARROW '\200' #define R_ARROW '\201' #define U_ARROW '\202' #define D_ARROW '\203' #define EVT_CHAR '\0' #define DEL_CHAR '\b' #define RET_CHAR '\n' static char esc_key='\0'; static fd_set regMask; static int lastFd=-1; void term_reg_socket(int fd) { FD_SET(fd, ®Mask); } void term_unr_socket(int fd) { FD_CLR(fd, ®Mask); } int term_raw_key(char *key, int msecTmo) { fd_set mask; mask=regMask; return(sys_select_or_key(&mask, msecTmo, key)); } int term_wait_fd(int fd, int msecTmo) { fd_set mask; struct timeval tmo; FD_ZERO(&mask); FD_SET(fd, &mask); tmo.tv_sec=msecTmo / 1000; tmo.tv_usec=(msecTmo % 1000) * 1000+1; return(select(FD_SETSIZE, &mask, NULL, NULL, &tmo)); } int term_get_key(char *key, int msecTmo) { int iret; char k; if (esc_key==0) { iret=term_raw_key(&k, msecTmo); } else { k=esc_key; } while (k==27) { /* esc */ iret=term_raw_key(&k, ESC_TMO); switch (k) { case 'O': k='\217'; break; /* ss3 */ case '[': k='\233'; break; /* csi */ default: break; } } if (iret!=STDIN_FILENO) { esc_key=k; *key='\0'; return(iret); } switch (k) { case '\233': /* csi */ iret=term_raw_key(&k, ESC_TMO); while (k>='0' && k <='9') { iret=term_raw_key(&k, ESC_TMO); } if (iret!=STDIN_FILENO) { esc_key=k; *key='\0'; return(iret); } switch (k) { /* L,R,U,D arrows */ case 'D': k=L_ARROW; break; case 'C': k=R_ARROW; break; case 'A': k=U_ARROW; break; case 'B': k=D_ARROW; break; default: k='?'; } break; case '\217': /* ss3 */ iret=term_raw_key(&k, ESC_TMO); if (iret!=STDIN_FILENO) { esc_key=k; *key='\0'; return(iret); } if (k=='M') { /* enter */ k=RET_CHAR; } else if (k >= 'l' && k <= 'y') { k=k-64; } else { switch (k) { /* L,R,U,D arrows */ case 'D': k=L_ARROW; break; case 'C': k=R_ARROW; break; case 'A': k=U_ARROW; break; case 'B': k=D_ARROW; break; default: k='?'; } } break; case '\177': /* del */ case '\b': /* bs */ k=DEL_CHAR; break; case '\r': /* cr */ case '\n': /* lf */ k=RET_CHAR; break; case EVT_CHAR: /* time out*/ break; default: if (k<' ' || k>'\176') k='?'; } *key=k; esc_key='\0'; return (STDIN_FILENO); } static char *history[HISTORY_LINES]={NULL}; /* history array: it's a cyclic buffer, when it is full, the oldest values are overwritten. */ static int hist_pos=0; /* position when scrolling through the history */ static int hist_end=0; /* end of history. Always history[hist_end]==NULL */ static char filehead[256]=""; FILE *term_open_pref(char *head, char *mode) { char buf[PATH_MAX], hom[PATH_MAX]; char *cret, *home, usr[256]; int i; cret=getenv("USER"); if (cret == NULL || *cret == '\0') return NULL; str_copy(usr, cret); if (strcmp(usr, "lnsg") == 0) { /* special case lnsg */ cret=getcwd(buf, sizeof(buf)); if (cret == NULL) return NULL; home=getenv("HOME"); realpath(home, hom); cret=strstr(buf,hom); if (cret == buf) { /* cwd starts with HOME, take subdirectory as usr */ cret+=strlen(hom)+1; str_copy(usr, "lnsg_"); str_append(usr, cret); cret=strchr(usr, '/'); if (cret != NULL) *cret='\0'; } } /* usr is now the username, or lnsg_ */ str_copy(buf, head); str_append(buf, usr); return fopen(buf, mode); } char *term_fgets(char *buf, int size, FILE *fil) { char *p, *ret; char skipbuf[256]; buf[0]='\0'; ret=fgets(buf, size, fil); if (ret==NULL) { return NULL; } p=strchr(buf,'\n'); if (p==NULL) { while (p==NULL) { /* skip rest of line */ ret=fgets(skipbuf, sizeof(skipbuf), fil); if (ret==NULL) { return NULL; } p=strchr(skipbuf,'\n'); } } else { *p='\0'; } return ret; } void term_save_hist(int trimlast) { FILE *fil; int i,n; if (filehead[0]=='\0') return; fil=term_open_pref(filehead, "w"); if (fil==NULL) return; n=HISTORY_LINES-1; if (trimlast) { n--; } i=hist_end; while (n>0) { n--; i++; if (i>=HISTORY_LINES) i=0; if (history[i]!=NULL) { fputs(history[i], fil); fputs("\n", fil); } } fclose(fil); } void term_read_hist(char *id) { FILE *fil; int i; char buf[1024], *lin; str_copy(filehead, "/tmp/"); str_append(filehead, id); str_append(filehead, "_hist."); fil=term_open_pref(filehead, "r"); if (fil==NULL) return; hist_end=0; while (hist_end0) { lin=MALLOC(i+1); strncpy(lin, buf, i+1); history[hist_end]=lin; hist_end++; } } fclose(fil); history[hist_end]=NULL; hist_pos=hist_end; } static int dirty=0; /* line is to be cleared through a call of term_clear */ void term_clear(void) { if (dirty) { fputs("\r\033[K", stdout); dirty=0; } } void term_off(void) { dirty=1; term_clear(); sys_keys_off(); } int term_get_line(char *buf, int size, int *pos, char *prompt, fd_set *mask) { char key, *lin; int i,j,l,iret,buflen; char tmp[512]; static int init=1; buf[size-1]='\0'; /* make sure buf is null terminated */ l=strlen(buf); if (*pos>l) { *pos=l; } else if (*pos<0) { *pos=0; } iret=term_get_key(&key, 0); while (1) { if (iret==-1 || key == RET_CHAR || key==EVT_CHAR) { /* refresh after a short timeout */ assert(l*2+20+strlen(prompt)*pos; i--) { tmp[j]='\b'; j++; } tmp[j]='\0'; fputs(tmp, stdout); if (iret==-1) { iret=term_get_key(&key, -1); /* no timeout */ } } switch (key) { case EVT_CHAR: /* caller must clear line before next write with term_clear() */ dirty=1; return(iret); /* interrupted EXIT */ case RET_CHAR: i=hist_end-1; if (i<0) i=HISTORY_LINES-1; if ((history[i]==NULL || 0!=strcmp(history[i], buf)) && buf[0]!='\0') { /* do not save equal and empty lines */ buflen=strlen(buf)+1; lin=MALLOC(buflen); strncpy(lin, buf, buflen); history[hist_end]=lin; hist_end++; if (hist_end>=HISTORY_LINES) hist_end=0; if (history[hist_end]!=NULL) { FREE(history[hist_end]); /* clear line at end of history */ } history[hist_end]==NULL; } hist_pos=hist_end; term_save_hist(0); return(STDIN_FILENO); /* normal EXIT */ case DEL_CHAR: if (*pos>0) { for (i=*pos; buf[i]!='\0'; i++) { buf[i-1] = buf [i]; } buf[i-1]='\0'; (*pos)--; l--; } break; case L_ARROW: if (*pos>0) (*pos)--; break; case R_ARROW: if (buf[*pos]!='\0') (*pos)++; break; case U_ARROW: case D_ARROW: if (key==U_ARROW) { i=hist_pos-1; if (i<0) i=HISTORY_LINES-1; } else { i=hist_pos+1; if (i>=HISTORY_LINES) i=0; } if (history[i]!=NULL) { strncpy(buf, history[i], size-1); buf[size-1]='\0'; hist_pos=i; l=strlen(buf); } else { buf[0]='\0'; l=0; if (history[hist_pos]!=NULL) { hist_pos=i; } } *pos=l; break; default: if (l*pos; i--) { buf[i]=buf[i-1]; } (*pos)++; l++; buf[i]=key; buf[l]='\0'; } break; } iret=term_get_key(&key, REF_TMO); } }