382 lines
8.1 KiB
C
382 lines
8.1 KiB
C
#include <limits.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#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;
|
|
int i;
|
|
|
|
FD_ZERO(&mask);
|
|
FD_SET(fd, &mask);
|
|
FD_SET(STDIN_FILENO, &mask);
|
|
tmo.tv_sec=msecTmo / 1000;
|
|
tmo.tv_usec=(msecTmo % 1000) * 1000+1;
|
|
i=select(FD_SETSIZE, &mask, NULL, NULL, &tmo);
|
|
if (FD_ISSET(STDIN_FILENO, &mask)) {
|
|
return 0;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
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_<subdirectory> */
|
|
|
|
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_end<HISTORY_LINES-1 && !feof(fil)) {
|
|
term_fgets(buf, sizeof(buf), fil);
|
|
i=strlen(buf);
|
|
if (i>0) {
|
|
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)<sizeof(tmp));
|
|
tmp[0]='\r';
|
|
tmp[1]='\0';
|
|
strcat(tmp, prompt);
|
|
strcat(tmp, buf);
|
|
strcat(tmp,"\033[K");
|
|
j=strlen(tmp);
|
|
for (i=l; i>*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);
|
|
printf("\r\033[K\n");
|
|
for (i = strlen(prompt) - 1; i > 0; i--) {
|
|
printf("-");
|
|
}
|
|
printf(" %s", buf);
|
|
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<size-1) {
|
|
for (i=l; i>*pos; i--) {
|
|
buf[i]=buf[i-1];
|
|
}
|
|
(*pos)++; l++;
|
|
buf[i]=key;
|
|
buf[l]='\0';
|
|
}
|
|
break;
|
|
}
|
|
iret=term_get_key(&key, REF_TMO);
|
|
}
|
|
}
|
|
|