add most files
This commit is contained in:
552
term.c
Normal file
552
term.c
Normal file
@ -0,0 +1,552 @@
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include "term.h"
|
||||
#include "myc_mem.h"
|
||||
#include "myc_str.h"
|
||||
#include "sys_select.h"
|
||||
#include "uselect.h"
|
||||
|
||||
#define ESC_TMO 1000
|
||||
#define REF_TMO 50
|
||||
#define HISTORY_LINES 256
|
||||
|
||||
#define L_ARROW 0200
|
||||
#define R_ARROW 0201
|
||||
#define U_ARROW 0202
|
||||
#define D_ARROW 0203
|
||||
#define EVT_CHAR '\0'
|
||||
#define DEL_CHAR '\b'
|
||||
#define RET_CHAR '\n'
|
||||
|
||||
static char esc_key='\0';
|
||||
|
||||
static fd_set regMask;
|
||||
static int minReg=0, maxReg=0;
|
||||
static char filehead[256]="";
|
||||
|
||||
static char *func[256]={NULL};
|
||||
|
||||
void term_reg_socket(int socket) {
|
||||
FD_SET(socket, ®Mask);
|
||||
if (socket > maxReg) {
|
||||
if (maxReg == 0) minReg = socket;
|
||||
maxReg = socket;
|
||||
} else if (socket < minReg) {
|
||||
minReg = socket;
|
||||
}
|
||||
}
|
||||
|
||||
void term_unr_socket(int socket) {
|
||||
FD_CLR(socket, ®Mask);
|
||||
}
|
||||
|
||||
int term_raw_key(int *key, int msecTmo) {
|
||||
fd_set mask;
|
||||
|
||||
mask=regMask;
|
||||
return(sys_select_or_key(&mask, msecTmo, key));
|
||||
}
|
||||
|
||||
int term_wait_socket(int socket, int msecTmo) {
|
||||
fd_set mask;
|
||||
struct timeval tmo;
|
||||
int i;
|
||||
|
||||
if (socket == 0) {
|
||||
mask = regMask;
|
||||
} else {
|
||||
FD_ZERO(&mask);
|
||||
FD_SET(socket, &mask);
|
||||
}
|
||||
FD_SET(STDIN_FILENO, &mask);
|
||||
tmo.tv_sec=msecTmo / 1000;
|
||||
tmo.tv_usec=(msecTmo % 1000) * 1000;
|
||||
i=uselect(FD_SETSIZE, &mask, NULL, NULL, &tmo);
|
||||
if (FD_ISSET(STDIN_FILENO, &mask)) {
|
||||
return 0;
|
||||
}
|
||||
if (i<0) return i;
|
||||
if (i) {
|
||||
if (socket && FD_ISSET(socket, &mask)) {
|
||||
return socket;
|
||||
}
|
||||
for (i=minReg; i<=maxReg; i++) {
|
||||
if (FD_ISSET(i, &mask)) {
|
||||
return i;
|
||||
};
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int term_get_key(int *key, int msecTmo) {
|
||||
int iret;
|
||||
int k;
|
||||
int kode;
|
||||
|
||||
fflush(stdout);
|
||||
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=0217; break; /* ss3 */
|
||||
case '[': k=0233; break; /* csi */
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (iret!=STDIN_FILENO) {
|
||||
esc_key=k;
|
||||
*key='\0';
|
||||
return(iret);
|
||||
}
|
||||
switch (k) {
|
||||
case 0233: /* csi */
|
||||
iret=term_raw_key(&k, ESC_TMO);
|
||||
kode=0;
|
||||
while (k>='0' && k <='9') {
|
||||
kode=kode*10+(k-'0');
|
||||
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:
|
||||
if (k=='~') {
|
||||
k = 128 + kode;
|
||||
} else {
|
||||
k += 128;
|
||||
}
|
||||
if (k>255) k='?';
|
||||
}
|
||||
break;
|
||||
case 0217: /* 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 += 128; if (k>255) k='?';
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0177: /* del */
|
||||
case '\b': /* bs */
|
||||
k=DEL_CHAR;
|
||||
break;
|
||||
case '\r': /* cr */
|
||||
case '\n': /* lf */
|
||||
k=RET_CHAR;
|
||||
break;
|
||||
case 4: /* ctrl-D, end of file */
|
||||
break;
|
||||
case EVT_CHAR: /* time out*/
|
||||
break;
|
||||
default:
|
||||
if (k>0176) {
|
||||
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 */
|
||||
|
||||
FILE *term_open_pref(int temp, char *head, char *mode) {
|
||||
char buf[PATH_MAX+1], old[PATH_MAX+1], wd[PATH_MAX];
|
||||
char *usr, *home, *p;
|
||||
int l;
|
||||
FILE *fil;
|
||||
mode_t oldmask;
|
||||
|
||||
usr = getenv("USER");
|
||||
if (temp && usr==NULL) return NULL;
|
||||
if (usr != NULL && *usr != '\0' && strstr(usr, "lnsg") != 0) {
|
||||
if (! getcwd(wd, sizeof wd)) return NULL;
|
||||
home=getenv("HOME");
|
||||
if (!home) return NULL;
|
||||
realpath(home, buf);
|
||||
l = strlen(buf);
|
||||
buf[l] = '/'; l++;
|
||||
buf[l]= '\0';
|
||||
if (wd[l] != '\0' && strncmp(wd, buf, l) == 0) { /* wd starts with $HOME */
|
||||
p = strchr(wd+l, '/');
|
||||
if (p) *p='\0'; /* determine 1st subdirectory of $HOME */
|
||||
if (temp) {
|
||||
str_append(wd, "_lnsg");
|
||||
usr = wd+l;
|
||||
} else {
|
||||
home = wd;
|
||||
}
|
||||
} else {
|
||||
home = buf;
|
||||
}
|
||||
} else {
|
||||
home=getenv("HOME");
|
||||
if (!home) return NULL;
|
||||
}
|
||||
if (temp) {
|
||||
/* old file name */
|
||||
str_copy(old, "/tmp/");
|
||||
str_append(old, head);
|
||||
str_append(old, ".");
|
||||
str_append(old, usr);
|
||||
/* new file name */
|
||||
str_copy(buf, "/tmp/six/");
|
||||
oldmask = umask(0);
|
||||
mkdir(buf, S_IRWXU+S_IRWXG+S_IRWXO);
|
||||
umask(oldmask);
|
||||
str_append(buf, head);
|
||||
str_append(buf, ".");
|
||||
str_append(buf, usr); /* usr is the username, or lnsg_<subdirectory> */
|
||||
} else {
|
||||
/* old file name */
|
||||
str_copy(old, home);
|
||||
str_append(old, "/.");
|
||||
str_append(old, head);
|
||||
/* new file name */
|
||||
str_copy(buf, home);
|
||||
str_append(buf, "/.six/");
|
||||
mkdir(buf, S_IRWXU+S_IRGRP+S_IXGRP+S_IROTH+S_IXOTH);
|
||||
str_append(buf, head);
|
||||
}
|
||||
rename(old, buf); /* if old file exists, rename it to new */
|
||||
if (*mode == 'd') {
|
||||
unlink(buf);
|
||||
return NULL;
|
||||
} else {
|
||||
fil = fopen(buf, mode);
|
||||
return fil;
|
||||
}
|
||||
}
|
||||
|
||||
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(1, 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, char *instr) {
|
||||
FILE *fil;
|
||||
int i;
|
||||
char buf[1024], *lin;
|
||||
|
||||
str_copy(filehead, id);
|
||||
str_append(filehead, "_hist_");
|
||||
str_append(filehead, instr);
|
||||
fil=term_open_pref(1, 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\033[0m", stdout); /* clear to end of line, clear colors */
|
||||
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 *lin;
|
||||
int key;
|
||||
int i,l,iret,buflen;
|
||||
char tmp[1024];
|
||||
static char back[512]="";
|
||||
|
||||
if (back[0] == '\0') {
|
||||
memset(back, '\b', sizeof back);
|
||||
}
|
||||
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 */
|
||||
snprintf(tmp, sizeof tmp, "%s%s%s%s%s%.*s",
|
||||
"\r\033[0m\033[1m", /* no color, bold */
|
||||
prompt, "\033[34m", /* blue */
|
||||
buf, "\033[K\033[0m", /* clear to end of line, clear colors */
|
||||
l - *pos, back);
|
||||
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;
|
||||
case 1: /* ctrl-A: jump to begin of line */
|
||||
*pos = 0;
|
||||
break;
|
||||
case 5: /* ctrl-E: jump to end of line */
|
||||
*pos = strlen(buf);
|
||||
break;
|
||||
case 4: /* ctrl-D: abort */
|
||||
return -2;
|
||||
default:
|
||||
if (key <' ' || key > 0176) { /* untreated special key */
|
||||
if (func[key] != NULL) {
|
||||
snprintf(buf, size, "%s", func[key]);
|
||||
key = RET_CHAR;
|
||||
continue;
|
||||
}
|
||||
key = '?';
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void term_define_key(char *cmd, int keyArg) {
|
||||
int iret;
|
||||
int key;
|
||||
|
||||
if (keyArg) {
|
||||
key = keyArg;
|
||||
} else {
|
||||
if (*cmd) {
|
||||
printf("\n# press key for command '%s' or return", cmd);
|
||||
} else {
|
||||
printf("\n# press key for which to remove command");
|
||||
}
|
||||
iret = term_get_key(&key, 5000);
|
||||
}
|
||||
switch (key) {
|
||||
case EVT_CHAR:
|
||||
case DEL_CHAR:
|
||||
case L_ARROW:
|
||||
case R_ARROW:
|
||||
case U_ARROW:
|
||||
case D_ARROW:
|
||||
break;
|
||||
case RET_CHAR:
|
||||
printf("\n# canceled\n");
|
||||
default:
|
||||
if (key <' ' || key > 0176) { /* special key */
|
||||
if (*cmd) {
|
||||
if (func[key]) free(func[key]);
|
||||
func[key] = strdup(cmd);
|
||||
if (!keyArg) {
|
||||
printf("\n# programmed key %d\n", key);
|
||||
}
|
||||
} else {
|
||||
if (func[key]) {
|
||||
if (!keyArg) {
|
||||
printf("\n# removed command '%s' from key %d\n", func[key], key);
|
||||
}
|
||||
free(func[key]);
|
||||
} else {
|
||||
if (!keyArg) {
|
||||
printf("\n# no command on key %d\n", key);
|
||||
}
|
||||
}
|
||||
func[key] = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
printf("\n# key %d is not programmable\n", key);
|
||||
}
|
||||
|
||||
void term_save_keys(FILE *fil, void (*out)(char *)) {
|
||||
int key;
|
||||
char buf[128];
|
||||
|
||||
for (key=0; key<256; key++) {
|
||||
if (func[key]) {
|
||||
fprintf(fil, "%d %s\n", key, func[key]);
|
||||
if (out) {
|
||||
snprintf(buf, sizeof buf, " key %d defined as '%s'\n", key, func[key]);
|
||||
out(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void term_load_keys(FILE *fil, void (*out)(char *)) {
|
||||
char line[136], buf[128];
|
||||
int key, l;
|
||||
|
||||
while (term_fgets(line, sizeof line, fil)) {
|
||||
key = 0;
|
||||
sscanf(line, "%d %n", &key, &l);
|
||||
if (key != 0) {
|
||||
term_define_key(line+l, key);
|
||||
if (out) {
|
||||
snprintf(buf, sizeof buf, " key %d defined as '%s'\n", key, line+l);
|
||||
out(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user