📄 xcscrpt.c
字号:
{"bind_function", bind_function}, {"bind_script", bind_script}, {"bind_string", bind_string}, {"capture", k_capture}, {"debug", k_debug}, {"dial", k_dial}, {"hangup", hangup}, {"linked", k_linked}, {"pause", k_pause}, {"quit", (int(*)())s_exit}, {"redial", redial}, {"seen", k_seen}, {"xmitbrk", xmitbrk}, {"transmit", k_transmit}, {"tty", k_tty}, {"type", k_type}, {"waitfor", k_waitfor}, {NIL(char), 0}};/* end of primitives *//* token types */typedef enum { NULLTOK, /* terminating '\0' in script buffer */ ACTION, /* an action (primitive or script cmd) */ AFFIRM, /* script 'affirm' */ BACKQUOT, /* script command substitution */ SBREAK, /* script 'break' */ CALL, /* script 'call' */ COMMENT, /* comment */ SCONTNUE, /* script 'continue' */ DECR, /* script 'decr' */ DO, /* script 'do' */ DONE, /* script 'done' */ ECHOS, /* script 'echo' */ EFLAG, /* '-n' switch for script 'echo' cmd */ ELSE, /* script 'else' */ ENDIF, /* script 'endif' */ ENDTRAP, /* script 'endtrap' */ EQ, /* operator "equals" */ EXIT, /* script 'exit' */ SFILE, /* script 'file' */ SFALSE, /* script 'false' */ IF, /* script 'if' */ INCR, /* script 'incr' */ LESSTHAN, /* operator "less than" */ LITERAL, /* a literal string (e.g. "abcde") */ MORETHAN, /* operator "greater than" */ NEGATE, /* negation operator for comparisons */ NEQ, /* operator "not equal to" */ NUMBER, /* a numeric constant (e.g. 12345) */ PIPE, /* script 'pipe' */ READ, /* script 'read' */ SHELL, /* script 'shell' */ SET, /* script 'assign' */ STRAP, /* script 'trap' */ TERMINAT, /* statement terminators (';' and '\n') */ THEN, /* script 'then' */ STRUE, /* script 'true' */ UNSET, /* script 'unset' */ UNTRAP, /* script 'untrap' */ XCSET, /* xc 'set' command */ VARNAME, /* a variable name */ WHILE, /* script 'while' */ TTERROR, /* unrecognizable token */ TIMEOUT /* script 'timeout' */} TOK_TYPE;/* token table */static struct { char *name; TOK_TYPE token;} s_toktab[] = { {"NULLTOK", NULLTOK}, {"ACTION", ACTION}, {"affirm", AFFIRM}, {"BACKQUOT", BACKQUOT}, {"break", SBREAK}, {"call", CALL}, {"COMMENT", COMMENT}, {"continue", SCONTNUE}, {"decr", DECR}, {"do", DO}, {"done", DONE}, {"echo", ECHOS}, {"-n", EFLAG}, {"else", ELSE}, {"endif", ENDIF}, {"fi", ENDIF}, {"ENDTRAP", ENDTRAP}, {"eq", EQ}, {"exit", EXIT}, {"false", SFALSE}, {"file", SFILE}, {"if", IF}, {"incr", INCR}, {"lessthan", LESSTHAN}, {"LITERAL", LITERAL}, {"morethan", MORETHAN}, {"!", NEGATE}, {"neq", NEQ}, {"NUMBER", NUMBER}, {"pipe", PIPE}, {"read", READ}, {"assign", SET}, {"shell", SHELL}, {"TRAP", STRAP}, /* 'trap' keyword left for later dev't */ {"TERMINAT", TERMINAT}, {"then", THEN}, {"timeout", TIMEOUT}, {"true", STRUE}, {"unassign", UNSET}, {"UNTRAP", UNTRAP}, {"set", XCSET}, {"VARNAME", VARNAME}, {"while", WHILE}, {"\0", TTERROR}};/* end token types *//* tok_value is set by lexan() in the following instances: TOK_TYPE NUMBER: (long) value of number TOK_TYPE LITERAL: pointer to beginning of quoted string TOK_TYPE ACTION: function pointer to appropriate vector TOK_TYPE VARNAME: pointer to VAR struct TOK_TYPE TTERROR: pointer to strange construction in script All other values of TOK_TYPE don't require further information.*/static union { long numval; /* numbers */ char *strptr; /* for literal strings (points to initial '"') */ int (*funcptr)(); /* vector for primitives */ VAR *varptr; /* for variables */} tok_value;/* lexan() is the lexical analyzer, which translates words into token types and sets tok_value appropriately. It's called repeatedly by the language parser, S_parse().*/static TOK_TYPElexan(pcptr)char **pcptr; /* address of script program counter */{ long nvalue, negpos = 1; VAR *varptr; FILE *bqpipe; int i, c; /* really a char, but 'int' to spot EOF */ static char *cptr, *lasttok, latoken[VNAMELEN+1], temp[VMAXSIZE+1], bqcmd[VMAXSIZE+1]; extern FILE *popen(); /* if in debug mode, echo script line to tfp */ if (debugflag && *pcptr>lasttok){ cptr = *pcptr - 1; while (*cptr==' ' || *cptr=='\t') --cptr; if (*cptr=='\n'){ fputs("+ ",tfp); ++cptr; while (*cptr!='\n' && *cptr) fputc(*(cptr++),tfp); fputc('\r',tfp), fputc('\n',tfp); } } /* skip to beginning of next token */ while (**pcptr==' ' || **pcptr=='\t') ++(*pcptr); tok_value.strptr = cptr = lasttok = *pcptr; /* save place */ /* negation operator for comparisons */ if (*cptr=='!' && (*(cptr+1)==' ' || *(cptr+1)=='\t')){ ++cptr; *pcptr = cptr; return NEGATE; } /* comment in script */ if (*cptr=='#'){ while (*cptr && *cptr!='\n') ++cptr; *pcptr = cptr; return TERMINAT; } /* statement terminator */ if (*cptr==';' || *cptr=='\n'){ ++cptr; *pcptr = cptr; return TERMINAT; } /* end of script */ if (*cptr=='\0') return NULLTOK; /* quoted literal string */ if (*cptr=='"'){ ++cptr; while (*cptr && *cptr!='\n' && !(*cptr=='"' && *(cptr-1)!='\\')) ++cptr; if (*cptr=='"'){ ++cptr; *pcptr = cptr; return LITERAL; } sprintf(Msg,"Unmatched quote"); S_abort(); } /* environment variable (treat as a literal) */ if (*cptr=='$'){ ++cptr; for (i=0; i<VMAXSIZE; ++i){ if (!*cptr || *cptr==' ' || *cptr=='\t' || *cptr=='\n' || *cptr=='\r' || *cptr==';') break; temp[i] = *(cptr++); } temp[i] = '\0'; tok_value.strptr = getenv(temp); if (!tok_value.strptr) tok_value.strptr = temp, sprintf(Msg,"%s: no such environment variable",temp), S2(Msg), tok_value.strptr = ""; *pcptr = cptr; return LITERAL; } /* back-quoted shell command (treat like env var) */ if (*cptr=='`'){ ++cptr; i = 0; while (*cptr && *cptr!='\n' && *cptr!='`' && (++i)<VMAXSIZE) ++cptr; if (*cptr=='`'){ for (i=0; i<VMAXSIZE; ++i){ /* tok_value ptr points */ bqcmd[i] = tok_value.strptr[i+1]; /* to leading '`' */ if (bqcmd[i]=='`'){ bqcmd[i] = '\0'; break; } } bqcmd[i] = '\0'; signal(SIGCHLD,SIG_DFL); if (!(bqpipe=popen(bqcmd,"r"))){ sprintf(Msg,"%s: cannot create pipe",bqcmd); S_abort(); } else { temp[0] = '\0'; i = 0; while (i<=VMAXSIZE && (c=fgetc(bqpipe))!=EOF && c!='\n') temp[i++] = c; fflush(bqpipe); pclose(bqpipe); temp[i] = '\0'; tok_value.strptr = temp; *pcptr = cptr + 1; return LITERAL; } } else { sprintf(Msg,"Unmatched back-quote:"); S_abort(); } } /* dialout port name */ if (!strncmp(cptr,"portname",8)){ tok_value.strptr =mport(NIL(char)); *pcptr += 8; return LITERAL; } /* leading hyphen, maybe a negative number? */ if (*cptr=='-') negpos = (-1), ++cptr; /* string beginning with a digit */ if (isdigit(*cptr)){ nvalue = (*cptr - '0') * negpos; while (*(++cptr)){ if (isdigit(*cptr)){ nvalue *= 10; nvalue += (*cptr - '0'); if (nvalue>0) nvalue *= negpos; continue; } else if (strchr(" \t\n;",*cptr)){ tok_value.numval = nvalue; *pcptr = cptr; return NUMBER; } sprintf(Msg,"Variable name cannot begin with a digit: "); S_abort(); } tok_value.numval = nvalue; *pcptr = cptr; return NUMBER; } /* check for '-n' switch for echo (type EFLAG) */ if (negpos<0){ if (*cptr=='N' || *cptr=='n'){ while (*cptr && !strchr(" \t\n;",*cptr)) ++cptr; *pcptr = cptr; return EFLAG; } sprintf(Msg,"Bad option to ECHO command"); S_abort(); } /* impermissible initial character */ if (!isalpha(*cptr)){ sprintf(Msg,"bad initial character: %c", *cptr); S_abort(); } /* remember that tok_value.strptr points to start of token */ for (i=1; i<(VNAMELEN+1); ++i){ /* jump to next field separator */ ++cptr; if (*cptr=='\0' || strchr(" \t\n;",*cptr)) break; } if (i>VNAMELEN){ /* word too long */ sprintf(Msg,"Variable name too long"); S_abort(); } strncpy(latoken,tok_value.strptr,i); /* copy word to array 'latoken' */ latoken[i] = '\0'; lc_word(latoken); /* cvt to lowercase */ /* script keywords */ /* scan table for keyword match */ for (i=0; *(s_toktab[i].name) && strcmp(latoken,s_toktab[i].name); ++i) ; if (*s_toktab[i].name) { /* lc keyword recognized */ if (s_toktab[i].token==STRUE) tok_value.numval = TRUE; if (s_toktab[i].token==SFALSE) tok_value.numval = FALSE; *pcptr = cptr; return s_toktab[i].token; } /* system primitive (ACTION) */ /* scan table for keyword match */ for (i=0;s_acttab[i].name && strcmp(latoken,s_acttab[i].name);++i) ; if (s_acttab[i].name){ /* primitive recognized */ tok_value.funcptr = s_acttab[i].funcptr; *pcptr = cptr; return ACTION; } /* user variable name */ if ((varptr=findvar(latoken))){ /* existing user variable */ tok_value.varptr = varptr; *pcptr = cptr; return VARNAME; } /* could this be the name of a new variable? */ tok_value.strptr = latoken; /* just in case this is 'on', 'off', etc. */ if (!setvar(latoken,VCHAR,"",0)) /* can't create it */ return TTERROR; if (!(varptr=findvar(latoken))) /* can't retrieve it */ return TTERROR; else { /* got it */ tok_value.varptr = varptr; *pcptr = cptr; return VARNAME; }}/* utility routines called by S_parse() *//* S_affirm is a placeholder. It's the function to get a yes or no response from the user.*/static intS_affirm(){ char c, junk; c = getchar(); fputc(c,tfp); while ((junk=getchar()) !='\n' && junk !='\r') fputc(junk,tfp); fputc('\r',tfp), fputc('\n',tfp); return(c=='y' || c=='Y');}/* S_addsub increments or decrements a numeric variable. It assumes that since the INCR or DECR directives call lexan() for the variable just before coming here, the tok_value structure contains a pointer to the variable whose value is to be changed.*/static intS_addsub(direction)int direction;{ long oldval = tok_value.varptr->u.num; oldval += direction; if (!setvar(tok_value.varptr->name,VNUM,"",&oldval)){ sprintf(Msg,"Error setting variable '%s'", tok_value.varptr->name); S_abort(); } return(tok_value.varptr->u.num ? SUCCESS : FAILURE);}/* S_qstrip returns a pointer to a (static) string with leading and trailing double-quote marks removed. If the string happens to lack a leading or trailing double-quote mark, then the string will be returned with its beginning unchanged, and its length equal to VMAXSIZE or the length of the string, whichever is shorter. Double-quote marks escaped with a backslash are included in the returned string and the backslash is excised.*/static char *S_qstrip(strptr)char *strptr;{ int i; static char strbuf[VMAXSIZE+2]; if (*strptr=='"') ++strptr; for (i=0; i<VMAXSIZE+1; ++i){ if (*strptr=='\\' && *(strptr+1)=='"' && *(strptr-1)!='\\') ++strptr; if ((*strptr=='"' && *(strptr-1)!='\\') || !*strptr || *strptr=='\n') break; strbuf[i] = *strptr; ++strptr; } strbuf[i] = '\0'; return strbuf;}/* S_read does the parsing grunts for the script 'read' directive. On entry, the 'read' token has been parsed, but that's all.*/static intS_read(pcptr)char **pcptr;{ int i; VAR *varptr1; static char strbuf[VMAXSIZE+2]; if (lexan(pcptr)!=VARNAME) S_abort(); varptr1 = tok_value.varptr; strbuf[0] = '\0'; for (i=0; i<VMAXSIZE; ++i){ strbuf[i] = getchar(); if (strbuf[i]==BS){ if (i>0) fputs("\b \b",tfp), i -= 2; else i = -1; continue; } fputc(strbuf[i],tfp); if (strbuf[i]=='\n' || strbuf[i]=='\r'){ strbuf[i] = '\0'; break; } } strbuf[VMAXSIZE] = '\0'; fputc('\r',tfp), fputc('\n',tfp); return(setvar(varptr1->name,VCHAR,strbuf,0));}/* S_perform invokes a system primitive and returns SUCCESS if the primitive succeeds, FAILURE if it fails. On entry, only the ACTION token has been parsed.*/static intS_perform(pcptr)char **pcptr;{ int (*fptr)(); char *cptr; VAR *varptr1; long n = -1; fptr = tok_value.funcptr; cptr = NIL(char); while (TRUE){ switch (lexan(pcptr)){ case TERMINAT: break; case NUMBER: if (n != -1) S_abort(); n = tok_value.numval; continue; case LITERAL: case TTERROR: cptr = S_qstrip(tok_value.strptr); continue; case VARNAME: varptr1 = tok_value.varptr; if (varptr1->type==VCHAR){ cptr = varptr1->u.str; break; } if (n != -1) S_abort(); n = varptr1->u.num; continue; default: S_abort(); } break; } return (*fptr)(n,cptr);}/* S_set does the parsing grunts for the script 'assign' directive. It's a separate function mostly to keep from cluttering up S_parse() too much. On entry, only the 'set' token has been received from the directive containing it.*/static intS_set(pcptr)char **pcptr; /* S_parse()'s program counter (p_pc) */{ TOK_TYPE nexttype; VAR *varptr1, *varptr2; char *setstr; if ((nexttype=lexan(pcptr))!=VARNAME) S_abort(); varptr1 = tok_value.varptr; if ((nexttype=lexan(pcptr))!=EQ) S_abort(); switch (nexttype = lexan(pcptr)){ case LITERAL: setstr = S_qstrip(tok_value.strptr); return(setvar(varptr1->name,VCHAR,setstr,0)); case ACTION: case AFFIRM: if (nexttype==ACTION) tok_value.numval = (long) S_perform(pcptr); else tok_value.numval = (long) S_affirm(); case NUMBER: case STRUE: case SFALSE: return(setvar(varptr1->name,VNUM,"",&tok_value.numval)); case VARNAME: varptr2 = tok_value.varptr; switch (varptr2->type){ case VCHAR: return(setvar(varptr1->name,VCHAR,varptr2->u.str,0)); default: return(setvar(varptr1->name,VNUM,"",&(varptr2->u.num))); } default: S_abort(); } return S_abort() ; /* not reached */ }/* S_varcmp() compares a variable's value with a string or numeric literal, or with the value of a second variable. Once again, this function does parsing grunts for S_parse().*/static intS_varcmp(varptr1, pcptr)VAR *varptr1;char **pcptr;{ TOK_TYPE compmode; long testnum = 0L ; static char strbuf[VMAXSIZE+1]; char *cmpstr = NULL ; int status, numvar; VAR *varptr2; numvar = (varptr1->type!=VCHAR); compmode = lexan(pcptr); switch (compmode){ case EQ: case NEQ: case MORETHAN: case LESSTHAN: break; case TERMINAT: if (numvar) return(varptr1->u.num ? TRUE : FALSE); else return(*varptr1->u.str ? TRUE : FALSE); default: S_abort(); } switch (lexan(pcptr)){ case LITERAL: if (numvar){ sprintf(Msg,"Error: %s is a numeric variable",varptr1->name); S_abort(); } ++tok_value.strptr; strncpy(strbuf,tok_value.strptr,VMAXSIZE); *(strchr(strbuf,'"')) = '\0'; cmpstr = strbuf; break; case NUMBER: case STRUE: case SFALSE: if (!numvar){ sprintf(Msg,"Error: %s is a string variable",varptr1->name); S_abort(); } testnum = tok_value.numval; break; case VARNAME: varptr2 = tok_value.varptr; if (numvar && varptr2->type==VCHAR){ sprintf(Msg,"Error: %s and %s are of different types", varptr1->name, varptr2->name); S_abort(); } if (numvar) testnum = varptr2->u.num; else cmpstr = varptr2->u.str; break; default: S_abort(); } if (numvar){ status = (varptr1->u.num==testnum); if (compmode==EQ) return status; if (compmode==NEQ) return !status; status = (varptr1->u.num > testnum); if (compmode==MORETHAN) return status; else return !status; } status = strcmp(varptr1->u.str,cmpstr); if (compmode==EQ) return(status==0); if (compmode==NEQ) return status; if (compmode==MORETHAN) return(status>0); else return(status<0);}static char *S_construct(pcptr)char **pcptr;{ char *cptr; static char newstring[VMAXSIZE+10]; TOK_TYPE nexttype; newstring[0] = '\0'; cptr = newstring; while ((nexttype=lexan(pcptr))!=NULLTOK){ if (strlen(newstring)>VMAXSIZE){ newstring[VMAXSIZE] = '\0'; break; } switch (nexttype){ case TERMINAT: break; case NUMBER: sprintf(cptr,"%ld",tok_value.numval); cptr += strlen(cptr); continue; case LITERAL: sprintf(cptr,"%s",S_qstrip(tok_value.strptr)); cptr += strlen(cptr);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -