diff --git a/instr_hosts.c b/instr_hosts.c new file mode 100644 index 0000000..75c0ffe --- /dev/null +++ b/instr_hosts.c @@ -0,0 +1,320 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "instr_hosts.h" + +static char *service; +static char *crit; +static char *input; +static int foundcount; +static int home; +static char hostport[128]; +static char *instr; +static int instr_len; +static char *host; +static int host_len; +static int port; +static char remoteinstrument[128]; +static char hostname[128]; +static char remotehostbuf[128]; +static char *remotehost; + +static char *getItem(char *line, char *name, char *value, int value_len) { + /* get the named item for line, or check if the name/value pair is present + * the found value is returned, if found + * if value_len != 0, the result is copied to value + * if value is not empty, it must be matched + */ + char find[64]; + char *found, *p, *sp, *result; + int l; + + if (value == NULL) + value="(NULL)"; + p = strchr(value, '.'); + if (p) { + l = p - value; + } else { + l = strlen(value); + } + snprintf(find, sizeof find, "%s=%.*s", name, l, value); + for (p = find; *p; p++) *p = tolower(*p); + found = strstr(line, find); + if (found && *value != '\0' && found[strlen(find)] > ' ' && found[strlen(find)] != '.') { + /* when value is given, it must be complete, (a dot or a space terminates the value) */ + found = NULL; + } + if (found) { + found += strlen(name)+1; + if (value_len == 0) { + result = found; + } else { + sp = strchr(found, ' '); + if (sp && sp < found + value_len) { + l = sp - found; + } else { + l = strlen(found); + } + if (l < value_len) { + strncpy(value, found, l); + value[l] = '\0'; + result = value; + } else { + value[0] = '\0'; + result = NULL; + } + } + } else { + if (value_len > 0) { + *value = '\0'; + } + result = NULL; + } + return result; +} + +void scanHostList(void (*func)(char *)) { + int i; + FILE *fil, *lfil; + char *hostlist; + char localhostlist[512]; + char line[512], lline[512]; + int dirty; + + hostlist = getenv("InstrumentHostList"); + if (!hostlist) { + hostlist="/afs/psi.ch/project/sinq/common/lib/sea/hostlist"; + } + fil = fopen(hostlist, "r"); + snprintf(localhostlist, sizeof localhostlist, "%s/.six/hostlist", getenv("HOME")); + lfil = fopen(localhostlist, "r"); + dirty = 0; + if (fil) { + if (!lfil) dirty = 1; + } else { + if (!lfil) return; + fil = lfil; + lfil = NULL; + } + while (NULL != fgets(line, sizeof line, fil)) { + if (!dirty && lfil + && (fgets(lline, sizeof lline, lfil) == NULL + || strcmp(line, lline) != 0)) { + dirty = 1; + } + if (line[0] != '#' && line[0] != 0) { + i = strlen(line) - 1; + if (line[i] == '\n') line[i] = 0; + func(line); + } + } + if (fil) fclose(fil); + if (lfil) fclose(lfil); + if (dirty) { + fil = fopen(hostlist, "r"); + if (fil) { + lfil = fopen(localhostlist, "w"); + if (lfil) { + while( EOF != (i = getc(fil)) ) { /* copy file to local file */ + putc(i, lfil); + } + fclose(lfil); + } + fclose(fil); + } + } +} + +char *GetHostName(void) { + char *pend; + + if (!hostname[0]) { + gethostname(hostname, sizeof hostname); + hostname[sizeof hostname - 1] = 0; + pend = strchr(hostname, '.'); + if (pend) + *pend = 0; + } + return hostname; +} + +char *InstrHostRemoteName(void) { + struct hostent *res; + struct in_addr inadr; + char *pend; + + if (remotehost == NULL) { + remotehost = getenv("REMOTEHOST"); + if (!remotehost) { + remotehost = getenv("SSH_CLIENT"); + if (remotehost) { + snprintf(remotehostbuf, sizeof remotehostbuf, "%s", remotehost); + pend = strchr(remotehostbuf, ' '); + if (pend) + *pend = 0; + remotehost = remotehostbuf; + } + } + if (!remotehost) { + remotehost = GetHostName(); + } + if (!remotehost) { + remotehost = ""; + } else { + if (inet_aton(remotehost, &inadr)) { + if ((res = gethostbyaddr(&inadr, sizeof inadr, AF_INET))) { + remotehost = res->h_name; + } + } + } + if (remotehost != remotehostbuf) { + snprintf(remotehostbuf, sizeof remotehostbuf, "%s", remotehost); + remotehost = remotehostbuf; + } + pend = strchr(remotehostbuf, '.'); + if (pend) { + if (strcmp(pend, ".psi.ch") == 0) { + *pend = 0; + } else { + strcpy(remotehostbuf, ""); + } + } + } + return remotehost; +} + +static void findCrit(char *line) { + char *colon; + int i; + char *h; + + if (getItem(line, crit, input, 0) != NULL) { + hostport[0]='\0'; + if (getItem(line, service, hostport, sizeof hostport) != NULL) { + instr[0] = '\0'; + if (foundcount == 0) { + getItem(line, "instr", instr, instr_len); + } + colon = strchr(hostport, ':'); + if (colon) { + i = atoi(colon+1); + if (i > 0) { + *colon='\0'; + if (foundcount == 0) { + snprintf(host, host_len, "%s", hostport); + port = i; + } + foundcount++; + GetHostName(); + if (getItem(line, "host", hostname, sizeof hostname)) { + home = 1; + } + } + } + } + } + h = getItem(line, "host", remotehost, 0); + if (h) { + if (remoteinstrument[0]) { + /* more than one entry: no real instrument */ + strcpy(remoteinstrument, "-"); + } else { + getItem(line, "instr", remoteinstrument, sizeof remoteinstrument); + } + } +} + +int InstrHost(char *serviceArg, char *inputArg, char *instrArg, int instr_lenArg, + char *hostArg, int host_lenArg, int *portArg) { + service = serviceArg; + input = inputArg; + instr = instrArg; + instr_len = instr_lenArg; + host = hostArg; + host_len = host_lenArg; + port = 0; + home = 0; + foundcount = 0; + remoteinstrument[0] = 0; + + if (input == NULL || input[0] <= ' ') { + crit = "host"; + input = GetHostName(); + } else { + crit = "instr"; + } + InstrHostRemoteName(); + scanHostList(findCrit); + if (strcmp(remoteinstrument, "-") == 0) { + remoteinstrument[0] = 0; + } + *portArg = port; + return home * foundcount; +} + +char *InstrHostRemoteInstr(void) { + return strdup(remoteinstrument); +} + +char *InstrHostName(void) { + return strdup(hostname); +} + +static char *list; +static int list_len; + +static void listInstr(char *line) { + int l; + *list='\0'; + if (getItem(line, service, "", 0) && + getItem(line, "instr", list, list_len)) { + l = strlen(list); + list += l; + list_len -= l; + } else if (line[0] > ' ') { + return; + } + if (list_len > 0) { + *list = ' '; + list++; + list_len--; + } +} + +void InstrList(char *serviceArg, char *listArg, int list_lenArg) { + service = serviceArg; + list = listArg; + list_len = list_lenArg; + scanHostList(listInstr); + if (list > listArg) list--; /* remove trailing blank */ + *list = '\0'; +} + +#ifdef MYC_FORTRAN +/* compile only when fortran c interface stuff is defined */ + +#include "myc_fortran.h" + +int F_FUN(instr_host)(F_CHAR(service), F_CHAR(input), F_CHAR(instr), F_CHAR(host), int *port + , int service_len, int input_len, int instr_len, int host_len) { + char buf[256], in[256], ho[256], sv[256]; + int iRet; + + STR_TO_C(sv, service); + STR_TO_C(buf, input); + iRet=InstrHost(sv, buf, in, sizeof(in), ho, sizeof(ho), port); + if (*port>0) { + STR_TO_F(instr, in); + STR_TO_F(host, ho); + } + return iRet; +} + +#endif diff --git a/instr_hosts.h b/instr_hosts.h new file mode 100644 index 0000000..5b2eebc --- /dev/null +++ b/instr_hosts.h @@ -0,0 +1,35 @@ +#ifndef _INSTR_HOSTS_H_ +#define _INSTR_HOSTS_H_ + +int InstrHost(char *service, char *input, char *instr, int instr_len, + char *host, int host_len, int *port); +/* service (in) is a service name (tecs, sics, sea) + input (in) may be a host or instrument name + instr (out) is the instrument name + host (out) is the host name + port (out) is the port name + the return value is 1, called from a computer related to the instrument, + 0 else +*/ + +void InstrList(char *service, char *list, int list_len); +/* get an instrument list supporting named service, separated with blanks */ + +char *InstrHostRemoteName(void); +/* + returns the remote host name (or the host name, if not valid) +*/ + +char *InstrHostRemoteInstr(void); +/* + called after InstrHost, get the instrument running on remotehost + or an empty string otherwise. the result is allocated, please free +*/ + +char *InstrHostName(void); +/* + called after InstrHost, get the full host name where sea was started. + the result is allocated, please free +*/ + +#endif /* _INSTR_HOSTS_H_ */ diff --git a/make_gen b/make_gen new file mode 100644 index 0000000..5d3b040 --- /dev/null +++ b/make_gen @@ -0,0 +1,31 @@ +#--------------------------------------------------------------------------- +# Makefile (sytem independent part) for the TECS Client library and TecsServer +# included in a machine-specific makefile +# +# Markus Zolliker, March 2003 +#-------------------------------------------------------------------------- + +.SUFFIXES: +.SUFFIXES: .o .c .f + +CC = gcc + +SRC = term sys_select instr_hosts uselect myc_err myc_str +SYSSRC = $(addprefix $(linuxsys)/,$(SRC)) +OBJ = $(addsuffix .o,$(SYSSRC)) +DFIL = $(addsuffix .d,$(SYSSRC)) + +all: $(linuxsys)/six + +six: $(linuxsys)/six + +-include $(DFIL) + +$(linuxsys)/six: six.c $(OBJ) + $(CC) $(CFLAGS) -o $@ $^ + +$(linuxsys)/%.o: %.c + $(CC) $(CFLAGS) -o $@ -c $< + +clean: + rm -f $(linuxsys)/*.o $(linuxsys)/*.a $(linuxsys)/*.d $(linuxsys)/six diff --git a/makefile b/makefile new file mode 100644 index 0000000..1b17bf2 --- /dev/null +++ b/makefile @@ -0,0 +1,4 @@ +# when linuxsys is defined: use this makefile version +# if not: show usage (see makefile_) + +include makefile_$(linuxsys) diff --git a/makefile_ b/makefile_ new file mode 100644 index 0000000..2c99373 --- /dev/null +++ b/makefile_ @@ -0,0 +1,25 @@ +# M. Zolliker 03.2005 +# this file shows the correct usage of make in the sics source directory + +%: usage + @echo + +%.o: usage + @echo + +default: usage + +usage: + @ echo "" + @ echo "Usage:" + @ echo "" + @ echo " make -f makefile_xxx [target]" + @ echo "" + @ echo ' where makefile_xxx is one of' + @ echo "" + @ ls -1 makefile_?* | pr -t -o 4 + @ echo "" + @ echo " or use make without args:" + @ echo "" + @ echo " setenv SICS_MAKE_VERSION slinux" + @ echo " make [target]" diff --git a/makefile_rhel7 b/makefile_rhel7 new file mode 100644 index 0000000..2c1893b --- /dev/null +++ b/makefile_rhel7 @@ -0,0 +1,40 @@ +#--------------------------------------------------------------------------- +# machine-dependent part +# +# Markus Zolliker, March 2003 +#-------------------------------------------------------------------------- + +include sllinux_def + +SICS=.. + +CC = gcc +FC = g77 +CFLAGS = -DLINUX -g $(DFORTIFY) -I../.. -I.. -I. -Wall -Wno-missing-braces \ + -Wno-unused-but-set-variable +FFLAGS = -Wimplicit -g +ARFLAGS = cr + +# -- system dependent routines + +# -- PGPLOT library additions +PGLIB = -L/usr/X11R6/lib -lX11 + +# -- library for ASYNSRV +HARDSUPLIB=../hardsup/libhlib.a + +# -- readline library +RDLIB =-lreadline -ltermcap + +# -- the following macros are used as a replacement for some automatic variables +# due to different make versions. +# This is for GNU make. +# +# list of prerequisites including paths for VPATH +Q=$^ + +# Fortran/C source file name +F=$< +C=$< + +include make_gen diff --git a/myc_err.c b/myc_err.c new file mode 100644 index 0000000..d0f661c --- /dev/null +++ b/myc_err.c @@ -0,0 +1,136 @@ +#include +#include +#include + +#include "myc_str.h" +#include "myc_err.h" + +#define SLEN 64 +#define MLEN 64 + +static char *txt[SLEN]; +static int sp=0; +static int stack_empty=1; + +int ErrCode; +char *ErrMessage=NULL; +void (*outrtn)(void *, char *)=NULL; +void *outarg; + +void ErrTxt(char *text, int systemError) +{ + if (systemError) { + stack_empty = 0; + sp=0; ErrCode=errno; ErrMessage=strerror(errno); + } + if (stack_empty && sp>0) { + sp=0; + stack_empty=0; + } + if (sp +#include + +/* ErrHDL Error handling utilities + ------------------------------- + Makes code more readable by hiding annoying error condition checks. + +Macros and routines: + + Spelling in uppercase indicates, that it the program flow + may be modified (jump to OnError label or program exit). + + + ERR_x + + Usage Error condition Error message taken from + ----------------------------------------------------------------------------------------- + ERR_SI(res=routine1(...)) res<0 errno + ERR_SP(ptr=routine2(...)) ptr==NULL errno + ERR_I(res=routine3(...)) res<0 stored by routine3 using errhdl mechanism + ERR_P(ptr=routine4(...)) ptr==NULL stored by routine4 using errhdl mechanism + + The result assignment "res=" or "ptr=" is optional. + + Description: + The routine routineX is called. + If the result indicates an error, the source text is saved and the + program continues at the OnError label. + The error message and the source code of the calling instructions is + saved for a later call to ErrShow or ErrExit. + + ERR_EXIT("program_name") + + Show error and exit program. + + ERR_MSG("message") + + Signals an error condition. If "message" is replaced by a variable, + take care that it is not modified until ErrShow is called. + + ERR_COD(cod) + + Signals an error condition as code from errno.h + + ErrShow("program_name") + + Show actual error message with traceback information to stdout + or a file fil + +Global Variables (read only) + + int ErrCode + + actual error message code + = errno for system errors or + = -1 for custom errors signaled by ERRMSG + + char *ErrMessage + + actual error message +*/ + +#define ERR_SI(R) { if(0>(R)) { ErrTxt(#R,1); goto OnError; }; } +#define ERR_SP(R) { if(NULL==(R)) { ErrTxt(#R,1); goto OnError; }; } +#define ERR_I(R) { if(0>(R)) { ErrTxt(#R,0); goto OnError; }; } +#define ERR_P(R) { if(NULL==(R)) { ErrTxt(#R,0); goto OnError; }; } +#define ERR_MSG(R) { ErrMsg(R); goto OnError; } +#define ERR_COD(R) { ErrCod(R); goto OnError; } + +void ErrTxt(char *text, int systemError); +void ErrMsg(char *msg); +void ErrCod(int code); +void ErrShow(char *text); /* write out error message with stack info */ +void ErrShort(char *msg); /* write out short error message */ +void ERR_EXIT(char *text); +void ErrSetOutRtn(void (*rtn)(void *,char *), void *arg); +void ErrSetOutFile(FILE *file); + +extern int ErrCode; +extern char *ErrMessage; + +#endif /* _ERR_HANDLING_H_ */ diff --git a/myc_mem.h b/myc_mem.h new file mode 100644 index 0000000..c734f2e --- /dev/null +++ b/myc_mem.h @@ -0,0 +1,30 @@ +#ifndef _MEM_UTIL_H_ +#define _MEM_UTIL_H_ + +#include +#include + +#ifdef FORTIFY +#include "fortify.h" +#endif + +/* ------------------------------------------------------------ + these macros help for safer dynamic memory + you may change these macros if you want to log dynamic memory access + +*/ + +#define NEW(PTR,TYP) {TYP _0_={0}; ERR_SP(PTR=malloc(sizeof(*PTR))); *PTR=_0_; } +/* + allocates and initializes an object of type TYP and make PTR point to it + TYP must be defined with an appropriate typedef declaration, and + INIT(TYP) must follow the declaration to initialize a dummy initializer + object. +*/ + +#define NEW_STR(TO,FROM) {ERR_SP(TO=malloc(strlen(FROM)+1)); strcpy(TO,FROM); } + +#define MALLOC(SIZE) malloc(SIZE) +#define FREE(PTR) { free(PTR); PTR=NULL; } + +#endif /* _MEM_UTIL_H_ */ diff --git a/myc_str.c b/myc_str.c new file mode 100644 index 0000000..06c4be4 --- /dev/null +++ b/myc_str.c @@ -0,0 +1,252 @@ +#include +#include +#include +#include +#include +#include +#include +#include "myc_err.h" +#include "myc_str.h" +#include "myc_mem.h" + +char *str_splitx(char *str, char sep, char *list[], int *n) { + int i; + char *s, *e; + + s=str; + for (i=0; i<*n; i++) { + list[i]=s; + e=strchr(s, sep); + if (e==NULL) { *n=i+1; return(NULL); } + s=e+1; + e--; + while (e>str && *e==' ') e--; /* trim sequence */ + e[1]='\0'; + } + return(s); +} + +char *str_split1(char *str, char sep) { + char *s, *e; + + e=strchr(str, sep); + if (e==NULL) { + s=NULL; + e=str+strlen(str); + } else { + s=e+1; + } + e--; + while (e>str && *e==' ') e--; /* trim sequence */ + e[1]='\0'; + return(s); +} + +int str_ntrim(char *dest, const char *src, int ldest, int lsrc) { + int i; + + if (lsrc>=ldest) lsrc=ldest-1; + if (dest!=src) strncpy(dest, src, lsrc); + dest[lsrc]='\0'; + i=strlen(dest)-1; + while (i>=0 && dest[i]==' ') i--; /* trim sequence */ + i++; + dest[i]='\0'; + return(i); +} + +int str_npad(char *dest, const char *src, int ldest) { + int i, lsrc; + + lsrc=strlen(src); + if (lsrc>=ldest) { + if (dest!=src) strncpy(dest, src, ldest); + lsrc=ldest; + } else { + if (dest!=src) strcpy(dest, src); + for (i=lsrc; i=dstlen) { + str_copy(dst, src); + } else { + strncpy(dst, src, i); + dst[i]='\0'; + } + return(s); +} + +char *str_read_until(FILE *fil, char *term, char *buf, char *end) { + char fmt[24]; + int i, l, siz; + char ch; + + siz=end-buf-1; + if (siz<1) return(NULL); + sprintf(fmt, "%s%d[^%s%s", "%", siz, term, "]%n%c"); + i=fscanf(fil, fmt, buf, &l, &ch); + if (i<0) { /* eof */ + buf[0]='\0'; + return(&buf[0]); + } else if (i==0) { /* fscanf returns 0 if first char is terminator */ + buf[0]=fgetc(fil); + return(&buf[0]); + } else if (i==1) { /* terminator not found -> read until eof */ + buf[l]='\0'; + return(&buf[l]); + } else { + buf[l]=ch; + if (l==siz && NULL==strchr(term, ch)) return(NULL); + return(&buf[l]); + } +} + +char *str_read_file(char *file) { + FILE *fil; + char *str, *s, *e, *p, *q; + int i, size; + struct stat statbuf; + + i=stat(file, &statbuf); + if (i<0) ERR_MSG("file not found"); + size=statbuf.st_size+4; + ERR_SP(str=MALLOC(size)); + e=&str[size-1]; + ERR_SP(fil=fopen(file, "r")); + s=str; + while (1) { + p=str_read_until(fil, "!", s, e); + if (p==NULL) break; + if (*p=='!') { + q=str_read_until(fil, "\n", p, e); + if (q==NULL) { p=NULL; break; } + s=p; *s='\n'; s++; + } else { + assert(*p=='\0'); + break; + } + } + ERR_SI(fclose(fil)); + assert(strlen(str)reslen) ERR_MSG("result buffer too short"); + strncpy(r, p, l); + r+=l; reslen-=l; + if (ln>reslen) ERR_MSG("result buffer too short"); + strncpy(r, new, reslen); + r+=ln; reslen-=ln; + p=s+lo; + s=strstr(p, old); + } + l=strlen(p); + if (l>reslen) ERR_MSG("result buffer too short"); + strncpy(r, p, l); + r+=l; + *r='\0'; + return(r-result); + OnError: + result[0]='\0'; + return(-1); +} + +void str_nupcase(char *dst, const char *src, int dstlen) { + dstlen--; /* space for trailing nul */ + while (*src!='\0' && dstlen>0) { + *dst=toupper((int)*src); + dst++; src++; + dstlen--; + } + *dst='\0'; +} + +void str_nlowcase(char *dst, const char *src, int dstlen) { + dstlen--; /* space for trailing nul */ + while (*src!='\0' && dstlen>0) { + *dst=tolower((int)*src); + dst++; src++; + dstlen--; + } + *dst='\0'; +} + +#ifndef __GNUC__ +int strcasecmp(const char *str1, const char *str2) { + int i; + char ch1, ch2; + ch1=tolower(*(str1++)); ch2=tolower(*(str2++)); + i=1; + while (ch1!='\0' && ch2!='\0' && ch1==ch2) { + ch1=tolower(*(str1++)); ch2=tolower(*(str2++)); i++; + } + if (ch1ch2) { + return(i); + } + return(0); +} +#endif + +int str_ncpy(char *dst, const char *src, int maxdest) { + strncpy(dst, src, maxdest); + if (dst[maxdest-1]!='\0') { + dst[maxdest-1]='\0'; + ERR_MSG("destination string too short"); + } + return(0); + OnError: return(-1); +} + +int str_ncat(char *dst, const char *src, int maxdest) { + dst[maxdest-1]='\0'; + strncat(dst, src, maxdest-strlen(dst)-1); + if (dst[maxdest-1]!='\0') { + dst[maxdest-1]='\0'; + ERR_MSG("destination string too short"); + } + return(0); + OnError: return(-1); +} diff --git a/myc_str.h b/myc_str.h new file mode 100644 index 0000000..95bdbfa --- /dev/null +++ b/myc_str.h @@ -0,0 +1,122 @@ +#ifndef _MYC_STR_H_ +#define _MYC_STR_H_ + +#define MYC_NAN (-1.125/1024./1024./1024.) + +/* + use these macros if DST is a fixed length character array +*/ + +#define str_trim(DST,SRC,L) str_ntrim(DST,SRC,sizeof(DST),L) +#define str_pad(DST,SRC) str_npad(DST,SRC,sizeof(DST)) +#define str_split(DST,SRC,SEP) str_nsplit(DST,SRC,SEP,sizeof(DST)) +#define str_substitute(DST,SRC,OLD,NEW) str_nsubstitute(DST,SRC,OLD,NEW,sizeof(DST)) +#define str_upcase(DST,SRC) str_nupcase(DST,SRC,sizeof(DST)) +#define str_lowcase(DST,SRC) str_nlowcase(DST,SRC,sizeof(DST)) +#define str_copy(DST,SRC) str_ncpy(DST,SRC,sizeof(DST)) +#define str_append(DST,SRC) str_ncat(DST,SRC,sizeof(DST)) + + +char *str_split1(char *str, char separator); +/* + trims text before separator in *str and returns + a pointer to the first character after separator +*/ + +char *str_splitx(char *str, char sep, char *list[], int *n); +/* + split string into *n strings using separator sep. + spaces at the end of the elements are trimmed + attention: *str is modified ('\0' placed at the end of the elements) + + if *n separators are found, result points to string after *n-th separator + else result is NULL + *n contains number of elements stored in list +*/ + +int str_ntrim(char *dest, const char *src, int ldest, int lsrc); +/* + copy characters 0 to lsrc-1 from src to dest (max ldest chars). +*/ + +int str_npad(char *dest, const char *src, int ldest); +/* + copy src to dest and fill with spaces (fortran string format) +*/ + +char *str_nsplit(char *dst, const char *src, char sep, int dstlen); +/* + returns a pointer to the text after the separator sep in *src + and copies the text before the separator to *dst + when *src does not contain the separator sep + NULL is returned, and *dst is a copy of *src +*/ + +char *str_read_file(char *file); +/* + return one string containing the contents of file *file + comments separated by '!' are omitted. The caller must + free the result after use. +*/ + +void str_replace_char(char *str, char ch, char rep); +/* + replace all occurences of character ch by character rep in string *str +*/ + +int str_nsubstitute(char *result, char *str, char *old, char *new, int reslen); +/* + replace every instance of old in str by new. + the result must not overlap + if the result would be longer than reslen, the result is en empty string + and the return value is -1; + else the return value is the length of the result. + return one string containing the contents of file *file + the contents are treated in the following way: + - #0,#1,...#n is replaced by the corresponding argument *args[n] (n=0..nargs-1, nargs<10) + - at the end of each line spaces and comments separated by ! are trimmed +*/ + +void str_nupcase(char *dst, const char *src, int dstlen); +/* + convert *str to uppercase +*/ + +void str_nlowcase(char *dst, const char *src, int dstlen); +/* + convert *str to lowercase +*/ + +#ifdef __VMS_VER +#if __VMS_VER<70000000 + +int strcasecmp(const char *str1, const char *str2); +/* + compare *str1 with *str2 + the comparison is not case sensitive + if result=0: strings are equal + else + result>0 <==> *str1>*str2 + first different character is at position abs(result)-1 +*/ + +#else +#include +#endif /* __VMS_VER<70000000 */ +#else +#include +#endif /* __VMS_VER */ + +int str_ncpy(char *dst, const char *src, int maxdest); +/* + copy *src to *dest, maximal maxdest characters, + it is guaranteed, that dst contains '\0' +*/ + +int str_ncat(char *dst, const char *src, int maxdest); +/* + append *src to *dest, maximal maxdest characters, + it is guaranteed, that dst contains '\0' +*/ + +#endif /* _MYC_STR_H_ */ diff --git a/publish b/publish new file mode 100755 index 0000000..cf8a5a6 --- /dev/null +++ b/publish @@ -0,0 +1,4 @@ +#!/bin/tcsh +cp -f $linuxsys/six $sys/stow/sea/bin/six + + diff --git a/six.c b/six.c new file mode 100644 index 0000000..428ca0f --- /dev/null +++ b/six.c @@ -0,0 +1,945 @@ +/* a simple commandline client for sics and sea + + Markus Zolliker + Mar. 2003 first version + Aug. 2005 visible difference between client for SICS and sea +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "term.h" +#include "myc_err.h" +#include "myc_str.h" +#include "instr_hosts.h" + +typedef enum { NORMAL, SPY, CLIENT, NMODE } Mode; + +#define MAXMSG 8192 + +static char *clcname="six", *servername="sics"; +static int clclen=3; +static char host[128]; +static char instr[32]; +static int sock[2]; /* main socket and spy socket */ +static int level=0; /* undefined level */ +static int deflevel=0; +static int remember=1; +static int hidecom=1; +static char user1[32]=""; +static char pswd1[32]=""; +static char user2[32]=""; +static char pswd2[32]=""; +static char status[MAXMSG]="Busy"; +static int busy[2]; +static int port; +static char msg0[MAXMSG]; +static char msg1[MAXMSG]; /* contains messages for main and spy */ +static int pos0 = 0; +static int pos1 = 0; +static char *prefix[NMODE+1]={"", "| ", "# ", ""}; +static int newline[NMODE+1]={1,1,1,1}; +static char *sim=""; + +static char *pw=NULL; +static char *us=""; + +void Put(Mode mode, char *text) { + char chr; + + while (*text) { + if (newline[mode]) { + fputs("\033[0m", stdout); + fputs(prefix[mode], stdout); + newline[mode] = 0; + if (strncmp(text, "ERROR:", 6) == 0) { + fputs("\033[31;1m", stdout); + } + } + chr = *text; text++; + fputc(chr, stdout); + if (chr == '\r' || chr == '\n') { + newline[mode] = 1; + } + } + fflush(stdout); +} + +void PutC(char *text) { + Put(CLIENT, text); +} + +void PutClear(void) { + Put(NMODE, ""); +} + +void UsageNote(void) { + PutC(" (for usage type: "); + PutC(clcname); + PutC(" help)\n"); +} + +void Usage(int cmds_only) { + if (!cmds_only) { + PutC(" \n "); + PutC(clcname); + PutC( + " commandline options:\n" + " -x login as spy\n" + " -u login as user\n" + " -m login as manager\n" + " help show this help text\n" + " -a or a ask always for username/password, forget passwords\n" + " -c or c use background color instead of # and |\n" + " -s or s simulation mode (on some instruments)\n" + " connect to the server for instr\n" + " -h connect to a server on a different host\n" + " -p connect to a server on a different port\n" + " -n do only a minimal login (no check of instrument)\n" + " no option login with default privilege\n" + ); + } + PutC( + "\n" + " Special commands treated by "); + PutC(clcname); PutC(" (these are no "); PutC(servername); PutC(" commands!)\n" + "\n" + " quit return to unix prompt\n" + " exit return to unix prompt\n" + " stop interrupt driving\n" + " "); PutC(clcname); + PutC(" help show this help text\n" + " "); PutC(clcname); + PutC(" def stop define a key for stop command\n" + " "); PutC(clcname); + PutC(" save save stop key and connection (host/port/access)\n" + " + increase privilege\n" + " - decrease privilege\n" + "\n" + " The "); PutC(servername); + PutC(" status is shown, if it is not 'Eager to execute commands'.\n" + " A shown status does not prohibit to enter commands.\n" + "\n" + " When "); PutC(servername); + PutC(" is busy, a vertical bar '|' is shown at the left of the line.\n" + " You may then enter more commands, but only under Spy privilege.\n" + " All messages from the client are prefixed with a hash symbol '#'.\n" + "\n" + " Markus Zolliker, Aug. 2005\n" + "\n" + ); + Put(-1, ""); /* clear colors */ +} + +int CocCreateSockAdr( + struct sockaddr_in *sockaddrPtr, /* Socket address */ + const char *host, /* Host. NULL implies INADDR_ANY */ + int port) /* Port number */ +{ + struct hostent *hostent; /* Host database entry */ + struct in_addr addr; /* For 64/32 bit madness */ + + (void) memset((char *) sockaddrPtr, '\0', sizeof(struct sockaddr_in)); + sockaddrPtr->sin_family = AF_INET; + sockaddrPtr->sin_port = htons((unsigned short) (port & 0xFFFF)); + if (host == NULL || host[0]=='\0') { + addr.s_addr = INADDR_ANY; + } else { + hostent = gethostbyname(host); + if (hostent != NULL) { + memcpy((char *) &addr, + (char *) hostent->h_addr_list[0], (size_t) hostent->h_length); + } else { + addr.s_addr = inet_addr(host); + if (addr.s_addr == (unsigned long)-1) { + return -1; /* error */ + } + } + } + /* + * There is a rumor that this assignment may require care on + * some 64 bit machines. + */ + + sockaddrPtr->sin_addr.s_addr = addr.s_addr; + return 0; +} + +char* readWrite(int tmo, int skip, char *find) { + int lbuf, go, n, shortTmo, l, socket; + Mode idx; + char *msg, *p, *q; + char chr; + static char result[MAXMSG]; + int hide; + static int laststat=0, laststatlen=0; +/* + * read from registered sockets and write to stdout until a key pressed, + * a timeout occurred (tmo [msec]), or a TRANSACTIONFINISHED message + * received. Handle also status and TRANSACTIONSTART messages. + * + * skip=0: skip response that does not match + * skip=1: skip all responses + * skip=2: skip all responses, quit on match + * + * if the response starts with the contents of find + * the second part of the line is returned + * if the response contain the contents of find, but not at the beginning + * "1" is returned + * if the text is not found, or find=NULL, an empty line is returned + */ + n=0; + go=0; + shortTmo = 0; + str_copy(result, ""); + socket=term_wait_socket(0, tmo); + while (socket > 0) { + idx = (socket == sock[1]); + ERR_SI(lbuf=recv(socket, &chr, 1, 0)); + if (lbuf==0) return "0"; + while (lbuf>0) { + if (chr == '\n') break; + if (idx) { + if (chr != '\r' && pos1 < MAXMSG-1) { /* cut lines longer than MAXMSG */ + msg1[pos1]=chr; + pos1++; + } + } else { + if (chr != '\r' && pos0 < MAXMSG-1) { /* cut lines longer than MAXMSG */ + msg0[pos0]=chr; + pos0++; + } + } + socket=term_wait_socket(0, tmo); + if (socket <= 0) break; + idx = (socket == sock[1]); + ERR_SI(lbuf=recv(socket, &chr, 1, 0)); + } + hide=0; + if (idx) { /* spy mode */ + msg1[pos1] = '\0'; + pos1 = 0; + msg = msg1; + if (!busy[1]) { /* skip spy messages if not busy (they will be doubled) */ + hide = 1; + } + } else { + msg0[pos0] = '\0'; + pos0 = 0; + msg = msg0; + } +/* printf("[%s]\n", msg); */ + if (strncmp(msg, "TRANSACTIONSTART", 16) == 0) { + busy[idx] = 1; + hide = 1; + } else if (strncmp(msg, "TRANSACTIONFINISHED", 19) == 0) { + busy[idx] = 0; + if (skip != 2) go = 1; + shortTmo = 1; + hide = 1; + } else if (idx == 0 && strncmp(msg, "status = ", 9) == 0) { + strcpy(status, msg+9); + l = strlen(msg); + if (status[0] == 'E') { + } else if (laststat!=status[0] || laststatlen != l) { + laststat = status[0]; + laststatlen = l; + } + hide = 1; + } + if (idx == 0 && find != NULL) { + p=strstr(msg, find); + + if (p!=NULL) { /* copy message to result */ + if (p==msg) { + str_copy(result, msg+strlen(find)); + q=strchr(result,'\r'); + if (q != NULL) *q='\0'; + } else { + str_copy(result, "1"); + } + hide=1; + if (skip == 2) go = 1; + } + } + if (hidecom && (skip || hide)) { /* skip text */ + n+=strlen(msg); + } else { + term_clear(); + Put(idx, msg); + if (chr == '\n') { + Put(idx, "\033[K\n"); + } + } + if (go) break; + socket = term_wait_socket(0, shortTmo?100:tmo); + shortTmo = 0; + } + fflush(stdout); + if (result==NULL) laststat=0; + return result; + OnError: return NULL; +} + +void PrintCmd(char *buf, int mode) { + Put(mode, "\r\033[K\n"); + Put(mode, "\033[1;34m>> "); + Put(mode, buf); + Put(mode, "\033[K\033[0m\n"); +} + +int sendCmd(int socket, char *cmd) { + int l; + char buf[256]; + + snprintf(buf, sizeof buf, "fulltransact %s\n", cmd); + ERR_SI(l=send(socket, buf, strlen(buf), 0)); + return l; + OnError: return -1; +} + +int scramble(char *buf) { + int i, n, cnt, chr; + int x; +/* Scrambles a string. Twice scramble gives original. + It does never convert a plain char to a ctrl char + and all standard ascii-codes to special chars and vice versa. + returns the number of special chars on output +*/ + + n=strlen(buf); + x=23; + cnt=0; + for (i=0; i 0) scramble(buf); +} + +void putscrambled(char *buf, FILE *fil) { + char cvt[256]; + str_copy(cvt, buf); + scramble(cvt); + fprintf(fil, "%s\n", cvt); +} + +int setrights(int gotolevel) { + char *p; + char prefhead[128], buf[128]; + FILE *fil; + int ask; + static char user[32]=""; + static char pswd[32]=""; + int savepw; + + if (pw == NULL) pw=pswd; + str_copy(prefhead, clcname); + str_append(prefhead, "."); + str_append(prefhead, instr); + + if (remember) { + fil=term_open_pref(0, prefhead, "r"); + if (fil != NULL) { + term_fgets(buf, sizeof(buf), fil); + getscrambled(user1, sizeof(user1), fil); + getscrambled(pswd1, sizeof(pswd1), fil); + getscrambled(user2, sizeof(user2), fil); + getscrambled(pswd2, sizeof(pswd2), fil); + fclose(fil); + } + if (deflevel==0) { + deflevel=buf[0]-'0'; + if (deflevel<1 || deflevel>3) deflevel=2; + } + } else { + deflevel=2; + } + if (gotolevel == 0) gotolevel = deflevel; + if (deflevel == 0) deflevel = 3; + if (level != gotolevel) { + if (gotolevel==1) { + if (user1[0]=='\0') { + if (strcmp(clcname, "six") == 0) { + str_copy(user1, "lnsmanager"); + } else { + str_copy(user2, servername); + str_append(user2, "manager"); + } + } + us=user1; + pw=pswd1; + } else if (gotolevel==2) { + if (user2[0]=='\0') { + if (strcmp(clcname, "six") == 0) { + str_copy(user2, instr); + str_lowcase(user2, user2); + str_append(user2, "user"); + } else { + str_copy(user2, servername); + str_append(user2, "user"); + } + } + us=user2; + pw=pswd2; + } else if (gotolevel==3) { + us="Spy"; + pw="007"; + } + ask=1; + savepw = 1; + if (us[0]!='\0' && pw[0]!='\0' && remember) { + sprintf(buf, "config rights %s %s", us, pw); + ERR_I(sendCmd(sock[0], buf)); + ERR_P(p=readWrite(12000,0,"Acknowledged")); + PutC("."); + if (*p=='\0') { + if (0==strcmp(us, user1)) { + user1[0]='\0'; + pswd1[0]='\0'; + } + if (0==strcmp(us, user2)) { + user2[0]='\0'; + pswd2[0]='\0'; + } + } else { + ask=0; + savepw = 0; + } + } + if (ask) { + PutC("\r"); + PutC(servername); + PutC(" username"); + if (us[0]!='\0') { + PutC(" ["); + PutC(us); + PutC("]"); + } + PutC(": "); + term_fgets(user, sizeof(user), stdin); + newline[CLIENT]=1; + if (0==strcmp(user, "quit")) return 1; + if (0==strcmp(user, "exit")) return 1; + if (user[0]=='\0') { + str_copy(user, us); + } + PutC("password: "); + term_fgets(pswd, sizeof(pswd), stdin); + newline[CLIENT]=1; + if (0==strcmp(pswd, "quit")) return 1; + if (0==strcmp(pswd, "exit")) return 1; + if (pswd[0]!='\0') { + sprintf(buf, "config rights %s %s", user, pswd); + ERR_I(sendCmd(sock[0], buf)); + ERR_P(p=readWrite(12000,0,"Acknowledged")); + if (*p=='\0') { /* no success */ + gotolevel=3; + if (0==strcmp(user, user1)) { + user1[0]='\0'; + pswd1[0]='\0'; + } + if (0==strcmp(user, user2)) { + user2[0]='\0'; + pswd2[0]='\0'; + } + pw=NULL; + } else { /* success */ + if (0==strcmp(user, user1)) { + str_copy(pswd1, pswd); + } + if (0==strcmp(user, user2)) { + str_copy(pswd2, pswd); + } + us=user; + pw=pswd; + } + } else { + gotolevel=3; + } + } + level=3; + ERR_I(sendCmd(sock[0], "config myrights")); + ERR_P(p=readWrite(12000,1,"UserRights = ")); + PutC("."); + if (*p!='\0') { + level=*p-'0'; + } + if (level==3) { + us="Spy"; + } + } + if (pw!=NULL && savepw) { + if (level==1) { + str_copy(user1, us); + str_copy(pswd1, pw); + if (0==strcmp(user1, user2)) { + user2[0]='\0'; + pswd2[0]='\0'; + } + } else if (level==2) { + str_copy(user2, us); + str_copy(pswd2, pw); + if (0==strcmp(user2, user1)) { + user1[0]='\0'; + pswd1[0]='\0'; + } + } + } + fil=term_open_pref(0, prefhead, "w"); + if (fil!=NULL) { + fprintf(fil, "%d\n", deflevel); + if (remember) { + putscrambled(user1, fil); + putscrambled(pswd1, fil); + putscrambled(user2, fil); + putscrambled(pswd2, fil); + } + fclose(fil); + } + return 0; + OnError: return -1; +} + +int SavePrefs(void) { + char prefhead[128], name[128]; + FILE *fil; + + ERR_SI(gethostname(name, sizeof name)); /* get ip name of this host */ + str_copy(prefhead, clcname); + str_append(prefhead, "_"); + str_append(prefhead, sim); + str_append(prefhead, name); + + ERR_SP(fil=term_open_pref(0, prefhead, "w")); + fprintf(fil, "keys\n"); + term_save_keys(fil, PutC); + fclose(fil); + return 0; + OnError: return -1; +} + +int LoadPrefs(void) { + char prefhead[128], name[128], line[128]; + FILE *fil; + + ERR_SI(gethostname(name, sizeof name)); /* get ip name of this host */ + str_copy(prefhead, clcname); + str_append(prefhead, "_"); + str_append(prefhead, sim); + str_append(prefhead, name); + fil=term_open_pref(0, prefhead, "r"); + if (fil) { + while (term_fgets(line, sizeof(line), fil)) { + if (0 == strcmp(line, "keys")) { + term_load_keys(fil, PutC); + } + } + fclose(fil); + } + return 0; + OnError: return -1; +} + +int Connect(void) { + int sock; + struct sockaddr_in sadr; + char *p; + + ERR_I(CocCreateSockAdr(&sadr, host, port)); + ERR_SI(sock=socket(AF_INET, SOCK_STREAM, 0)); + term_reg_socket(sock); + ERR_SI(connect(sock, (struct sockaddr *)&sadr, sizeof(sadr))); + ERR_SI(send(sock, "Spy 007\n", 8, 0)); + ERR_P(p=readWrite(12000,2,"Login O")); + if (*p!='K') { /* looking for the 'K' of 'Login OK' */ + PutC("rejected\n"); + return 0; + } + return sock; + OnError: return -1; +} + +int main (int argc, char *argv[]) { + struct hostent *ent; + int iret, pos; + fd_set mask; + int i, j, gotolevel, sicslogin; + int home; + int savehist = 0; + char buf[512], lbuf[16]; + char stdPrompt[128]="", prompt[256]=""; + char *p; + char *bar; + char *pnam[4]={"0", "MANAGER", "user", "spy"}; + char *hostArg; + char *subcmd; + char *service; + + atexit(term_off); + port=-1; + home=0; + sicslogin=1; + deflevel=0; + gotolevel=0; + hostArg=NULL; + sock[1]=0; /* do not yet connect 2nd connection */ + for (i=1; i=argc) { + PutC("missing host"); + UsageNote(); return 0; + } + hostArg = argv[i]; + } else if (0==strcmp(argv[i], "-p")) { + i++; + if (i>=argc) { + PutC("missing port"); + UsageNote(); return 0; + } + port=atoi(argv[i]); + if (port == 0) { + PutC("illegal port"); + UsageNote(); return 0; + } + } else if (0==strcmp(argv[i], "-n")) { + sicslogin=0; + } else { + if (strlen(argv[i])>=32) { + PutC("argument too long"); + UsageNote(); return 0; + } else if (argv[i][0] == '-') { + PutC("unknown option: "); + PutC(argv[i]); + UsageNote(); return 0; + } else if (argv[i][1] == '\0' && isdigit(argv[i][0])) { + port = atoi(argv[i]); + } else { + hostArg = argv[i]; + } + } + } + if (*sim != '\0') { + service = "simsics"; + } else { + service = servername; + } + home = InstrHost(service, hostArg, instr, sizeof instr + , host, sizeof host, &port); + ent = gethostbyname(host); + if (!ent) { + if (hostArg == NULL || hostArg[0] <= ' ') { + snprintf(host, sizeof host, "%s", getenv("HOST")); + hostArg = host; + } + PutC(service); + PutC(" on "); + PutC(hostArg); + PutC(" not found in InstrumentHostList\n"); + p=getenv("InstrumentHostList"); + if (p) { + PutC("("); + PutC(p); + PutC(")\n"); + } + return 0; + } + snprintf(host, sizeof host, "%s", ent->h_name); + LoadPrefs(); + + PutC( "---------------------------------------------------\n"); + if (strcmp(clcname, "six") == 0) { + PutC("six, a fast sics commandline client (doc: six help)\n"); + if (port == -1) { + if (*sim == '\0') { + port = 2911; + } else { + port = 2927; + } + home = 1; + } + } else if (strcmp(clcname, "seacmd") == 0) { + PutC("seacmd, a sea commandline client (doc: seacmd help)\n"); + if (port == -1) { + port = 8641; + } else if (port > 0 && port < 9) { + port += 8640; + } + home = 1; + } else { + PutC("graphcmd, a graph commandline client (doc: graphcmd help)\n"); + if (port == -1) { + port = 8741; + } else if (port > 0 && port < 9) { + port += 8740; + } + home = 1; + } + PutC( "---------------------------------------------------\n"); + PutC("."); + ERR_I(sock[0]=Connect()); + PutC("."); + if (sock[0] == 0) return 0; + if (sicslogin) { + + ERR_I(sendCmd(sock[0], "Instrument")); + ERR_P(p=readWrite(12000,0,"Instrument = ")); + PutC("."); + str_copy(instr, p); + if (*instr=='\0') { + PutC("can not detect instrument\n"); + return 0; + } + str_lowcase(instr,instr); + p=strchr(instr,' '); + if (p!=NULL) *p='\0'; + if (0==strcmp(instr,"SANS-II")) { + str_copy(instr, "SANS2"); + } + if (!home) { + deflevel = 3; + gotolevel = 3; + } + ERR_I(i=setrights(gotolevel)); + PutC("."); + PutC("\rlogged in to "); PutC(servername); PutC(" as "); + PutC(pnam[level]); + PutC(" on "); + PutC(instr); + PutC("\n"); + + sprintf(stdPrompt, "%s[%s] ", clcname, instr); + + ERR_I(sendCmd(sock[0], "status interest")); + ERR_P(readWrite(12000,0,"OK")); + + ERR_I(sendCmd(sock[0], "status")); + ERR_P(readWrite(12000,0,NULL)); + + term_read_hist(clcname, instr); + } else { + sprintf(stdPrompt, "%s[%s] ", clcname, host); + status[0]='E'; status[1]='\0'; + term_read_hist(clcname, host); + } + + iret=1; + buf[0]='\0'; + pos=0; + + savehist = 1; + + while (1) { + if (busy[0]) { + bar = prefix[SPY]; + } else { + bar = prefix[NORMAL]; + } + if (status[0] == 'E') { /* Eager to ... */ + sprintf(prompt, "%s%s", bar, stdPrompt); + } else { + sprintf(prompt, "%s%s(%s) ", bar, stdPrompt, status); + } + FD_ZERO(&mask); + FD_SET(sock[0], &mask); + iret=term_get_line(buf, sizeof(buf)-2, &pos, prompt, &mask); + if (iret==STDIN_FILENO) { /* input line terminated */ + str_lowcase(lbuf, buf); + if (0==strcmp(lbuf,"quit") || 0==strcmp(lbuf,"exit")) { + PrintCmd(buf, CLIENT); + break; + } + p=""; + if (0==strcmp(lbuf,"stop")) { + PrintCmd(buf, CLIENT); + ERR_SI(send(sock[0], "INT1712 3\n", 10, 0)); + buf[0]='\0'; + } else if (0==strcmp(buf, "-") || + 0==strcmp(buf, "--") || + 0==strcmp(buf, "+") || + 0==strcmp(buf, "++")) { + PrintCmd(buf, CLIENT); + if (level == 0) { + gotolevel = 3; + } else { + gotolevel = level; + } + if (buf[0]=='-') { + if (gotolevel<3) gotolevel++; + } else { + if (gotolevel>1) gotolevel--; + } + if (strlen(buf)==2) { + deflevel=gotolevel; + } + term_off(); + j = level; + ERR_I(i=setrights(gotolevel)); /* level might be changed */ + if (j!=level) { + PutC("\rswitched to "); + } else { + PutC("\rremain at "); + } + PutC(pnam[level]); + PutC(" privilege\n"); + buf[0]='\0'; + } else if (0==strncmp(buf, "help ", 5) && 0==strcmp(buf+5, clcname)) { + PrintCmd(buf, CLIENT); + Usage(1); + buf[0]='\0'; + } else if (0==strncmp(buf, clcname, clclen) && buf[clclen] == ' ') { + subcmd = buf + clclen; + while (*subcmd <= ' ' && *subcmd != 0) { + subcmd++; + } + if (0==strcmp(subcmd, "help")) { + PrintCmd(buf, CLIENT); + Usage(0); + buf[0]='\0'; + } else if (0==strncmp(subcmd, "def ", 4)) { + PrintCmd(buf, CLIENT); + term_define_key(subcmd+4, 0); + buf[0]='\0'; + } else if (0==strcmp(subcmd, "def")) { + PrintCmd(buf, CLIENT); + term_define_key("", 0); + buf[0]='\0'; + } else if (0==strcmp(subcmd, "save")) { + PrintCmd(buf, CLIENT); + PutC("defaults for next calls to "); + PutC(clcname); PutC(":\n"); + ERR_I(SavePrefs()); + if (level == 0) { + gotolevel = 3; + } else { + gotolevel = level; + } + deflevel = gotolevel; + ERR_I(setrights(gotolevel)); + PutC(" login as "); + PutC(us); + PutC(" with "); + PutC(pnam[level]); + PutC(" privilege\n"); + buf[0]='\0'; + } else { + PrintCmd(buf, CLIENT); + PutC("ERROR: unknown command\n"); + buf[0]='\0'; + } + } else if (buf[0] == '\0') { /* empty command */ + PrintCmd(buf, CLIENT); + ERR_P(p=readWrite(200,0,NULL)); /* just wait a little */ + } + if (buf[0] != '\0') { + if (busy[0]) { + if (!sock[1]) { + ERR_I(sock[1] = Connect()); + if (!sock[1]) { + PutC("spy connection rejected\n"); + } + } + if (busy[1]) { + /* print in red bold */ + PrintCmd(buf, CLIENT); + PutC("\033[1;31mBUSY (already 2 commands pending)\033[0m\n"); + buf[0]='\0'; + } else if (sock[1]) { + PrintCmd(buf, SPY); + ERR_I(sendCmd(sock[1], buf)); + } else { + PrintCmd(buf, CLIENT); + buf[0]='\0'; + } + } else { + PrintCmd(buf, NORMAL); + ERR_I(sendCmd(sock[0], buf)); + } + } + if (buf[0] != '\0') { + ERR_P(p=readWrite(500,0,NULL)); + } + buf[0]='\0'; + pos=0; + } else if (iret>0) { /* something arrived from sockets */ + if (iret == sock[0] || iret == sock[1]) { + ERR_P(p=readWrite(500,0,NULL)); + } + } else if (iret==-2) { + goto SaveHist; + } else if (iret<0) { + ERR_MSG("term_get_line failed"); + } + if (strcmp(p, "0") == 0) { + term_clear(); + PutC("\nconnection lost\n"); + goto SaveHist; + } + } + if (savehist) term_save_hist(1); /* save history without last line */ + goto Bye; + + OnError: + ErrShow("end"); + SaveHist: + if (savehist) term_save_hist(0); /* save history with last line */ + Bye: + term_off(); + if (stdPrompt[0]) { + PutC("\nexit "); + PutC(stdPrompt); + PutC("\n"); + PutClear(); /* clear colors */ + } + return 0; +} diff --git a/sys_select.c b/sys_select.c new file mode 100644 index 0000000..4f85571 --- /dev/null +++ b/sys_select.c @@ -0,0 +1,106 @@ +#include +#include +#include +#include +#include "uselect.h" + +static int lastFd=-1; +static struct termios atts; +static int echo=1; + +void sys_keys_on(void) { + int iret; + + struct termios attr; + if (echo) { + iret=tcgetattr(STDIN_FILENO,&attr); + atts=attr; /* save term. attr. */ + if (iret!=0) { perror("***\n");} + attr.c_lflag &= ~(ICANON) & ~(ECHO); /* canonical mode off, echo off */ + attr.c_cc[VMIN]=0; + attr.c_cc[VTIME]=1; /* 0.1 sec */ + iret= tcsetattr(STDIN_FILENO,TCSANOW,&attr); + if (iret!=0) {perror("***\n");} + echo=0; + } +} + +void sys_keys_off(void) { + int iret; + + if (!echo) { + iret=tcsetattr(STDIN_FILENO,TCSANOW,&atts); /* restore term. attributes */ + if (iret!=0) {perror("***\n");}; + echo=1; + } +} + +static int getChar(void) { + unsigned char c; + int rt; + + rt = read(STDIN_FILENO, &c, 1); + if (rt<=0) { + return EOF; + } else { + return c; + } +} + +int sys_select_or_key(fd_set *mask, int msecTmo, int *key) { + int fd, iret, fd1, chr; + struct timeval tmo, tmo0={0,0}; + fd_set rmask; + + sys_keys_on(); + rmask=*mask; + iret=uselect(FD_SETSIZE, &rmask, NULL, NULL, &tmo0); + if (iret<0) { + FD_ZERO(&rmask); + perror("error in select"); + } + fd1=STDIN_FILENO; + /* chr=fgetc(stdin); */ + chr = getChar(); + if (chr == EOF && iret==0) { /* */ + rmask=*mask; + FD_SET(STDIN_FILENO, &rmask); + if (msecTmo>=0) { + if (msecTmo>100) { /* reduce 100 ms for the 1 tenth second in fgetc */ + msecTmo=msecTmo-100; + } else { + msecTmo=1; + } + tmo.tv_sec=msecTmo / 1000; + tmo.tv_usec=(msecTmo%1000)*1000; + iret=uselect(FD_SETSIZE, &rmask, NULL, NULL, &tmo); + } else { + iret=uselect(FD_SETSIZE, &rmask, NULL, NULL, NULL); + } + if (iret<0) { + FD_ZERO(&rmask); + perror("error in select"); + } + if (FD_ISSET(STDIN_FILENO, &rmask)) { + chr = getChar(); + /* chr=fgetc(stdin); */ + } + FD_CLR(STDIN_FILENO, &rmask); + } + if (chr==EOF) { + chr=0; + fd1=-1; + for (fd=1; fdlastFd) { + fd1=fd; break; + } + } + } + } + *mask=rmask; + *key=chr; + return(fd1); +} diff --git a/sys_select.h b/sys_select.h new file mode 100644 index 0000000..051350d --- /dev/null +++ b/sys_select.h @@ -0,0 +1,20 @@ +#ifndef SYS_SELECT_H_ +#define SYS_SELECT_H_ + +#include + +int sys_select_or_key(fd_set *mask, int msecTmo, int *key); +/* + wait for read event on sockets included in mask or from keyboard or a timeout + result is negative for timeout, + STDIN_FILENO for a key pressed, + else socket number + switches the terminal to no-echo no-canonical +*/ + +void sys_keys_off(void); +/* + switch back terminal to normal state +*/ + +#endif /* SYS_SELECT_H_ */ diff --git a/term.c b/term.c new file mode 100644 index 0000000..2dadc06 --- /dev/null +++ b/term.c @@ -0,0 +1,552 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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_ */ + } 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_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\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*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); + } + } + } +} diff --git a/term.h b/term.h new file mode 100644 index 0000000..54010dc --- /dev/null +++ b/term.h @@ -0,0 +1,49 @@ +#ifndef TERM_H_ +#define TERM_H_ + +#include + +void term_reg_socket(int socket); +/* register socket */ + +void term_unr_socket(int socket); +/* unregister socket */ + +int term_raw_key(int *key, int msecTmo); + +int term_wait_socket(int socket, int msecTmo); +/* wait for a read event on socket or timeout + special case socket=0: wait for a read event on any socket + return socket number + or 0 for timeout +*/ + +int term_get_key(int *key, int msecTmo); + +void term_clear(void); + +int term_get_line(char *buf, int size, int *pos, char *prompt, fd_set *mask); + +FILE *term_open_pref(int temp, char *head, char *mode); +/* open a user specific preferences or temporary file */ + +char *term_fgets(char *buf, int size, FILE *fil); +/* fgets without newline */ + +void term_read_hist(char *id, char *instr); +/* read history from temporary file with id */ + +void term_save_hist(int trimlast); +/* store history (without last line if trimlast) */ + +void term_off(void); + +void term_define_key(char *cmd, int keyArg); + +void term_save_keys(FILE *fil, void (*out)(char *)); +/* save keys on file */ + +void term_load_keys(FILE *fil, void (*out)(char *)); +/* load keys on file */ + +#endif /* TERM_H_ */ diff --git a/uselect.c b/uselect.c new file mode 100644 index 0000000..4173a80 --- /dev/null +++ b/uselect.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include "uselect.h" + +/* an uninterruptable version of select. M.Z. Oct 2008 */ + +int uselect(int nfds, + fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + struct timeval *timeout) { + + sigset_t sigmask; + struct timespec tmo, *tmoPtr; + + sigfillset(&sigmask); + if (timeout) { + tmo.tv_sec = timeout->tv_sec; + tmo.tv_nsec = timeout->tv_usec * 1000; + tmoPtr = &tmo; + } else { + tmoPtr = NULL; + } + return pselect(nfds, readfds, writefds, exceptfds, tmoPtr, &sigmask); +} diff --git a/uselect.h b/uselect.h new file mode 100644 index 0000000..4d848d9 --- /dev/null +++ b/uselect.h @@ -0,0 +1,7 @@ +#include + +/* an uninterruptable version of select. M.Z. Oct 2008 */ + +int uselect(int nfds, + fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + struct timeval *timeout);