firmware/general/package/comgt/src/comgt.c

1751 lines
44 KiB
C

/*
* comgt version 0.31 - 3G/GPRS datacard management utility
*
* Copyright (C) 2003 Paul Hardwick <paul@peck.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* See comgt.doc for more configuration and usage information.
*
*/
/***************************************************************************
* $Id: comgt.c,v 1.4 2006/10/20 14:30:19 pharscape Exp $
****************************************************************************/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include "comgt.h"
#define MAXLABELS 3000 /* Maximum number of labels */
#define MAXGOSUBS 128 /* Max depth */
#define STRINGL 1024 /* String lengths. Also, max script line length */
#define MAXPATH 1024 /* Max filename length (less or equal to STRINGL) */
#define MAXTOKEN 20 /* Maximum token or label length */
#define GTDEVICE "/dev/modem"
#define BOOL unsigned char
#define NVARS 286 /* a-z, a0-z9 == 26*11 */
extern char *optarg;
extern int optind, opterr;
FILE *filep;
char *script;
int scriptspace;
BOOL ifres;
int lastpc,pc; /* program "counters" */
long resultcode=0; /* result code */
int ignorecase=1; /* no case sensitivity */
BOOL comecho=0; /* echo what's comin' in */
BOOL high_speed=0;
long senddelay=0; /* 0/100th second character delay for sending */
long number; /* For getonearg() returning an long */
char **label; /* Index of labels for goto and gosub */
int *labelpc; /* Positions of said labels in script */
int labels; /* Number of labels found in script */
long intvars[NVARS]; /* [a-z][0-9] integer variables */
char string[STRINGL]; /* For getstring() returns and misc. use (misuse) */
char *stringvars[NVARS]; /* $[a-z][0-9] string variables */
char cspeed[10]; /* Ascii representation of baudrate */
int speed=B0; /* Set to B110, B150, B300,..., B38400 */
char device[MAXPATH]; /* Comm device. May be "-" */
char token[MAXTOKEN]; /* For gettoken() returns */
char scriptfile[MAXPATH]; /* Script file name */
char scriptfilepath[MAXPATH]; /* temp storage for full path */
BOOL verbose=0; /* Log actions */
struct termios cons, stbuf, svbuf; /* termios: svbuf=before, stbuf=while */
int comfd=0; /* Communication file descriptor. Defaults to stdin. */
char msg[STRINGL]; /* Massage messages here */
int preturn,returns[MAXGOSUBS];
int clocal=0;
int parity=0, bits=CS8, stopbits=0;
unsigned long hstart,hset;
char NullString[]={ "" };
BOOL lastcharnl=1; /* Indicate that last char printed from getonebyte
was a nl, so no new one is needed */
BOOL tty=1;
//"open com \"/dev/modem\"\nset com 38400n81\nset senddelay 0.05\nsend \"ATi^m\"\nget 2 \" ^m\" $s\nprint \"Response : \",$s,\"\\n\"\nget 2 \" ^m\" $s\nprint \"Response :\",$s,\"\\n\"\nget 2 \" ^m\" $s\nprint \"Response : \",$s,\"\\n\"\n\n";
/* Prototypes. */
unsigned long htime(void);
void dormir(unsigned long microsecs);
void dotestkey(void);
void ext(long xtc);
void vmsg(char *text);
void skipline(void);
void printwhere(void);
void writecom(char *text);
int getonebyte(void);
void dodump(void);
void serror(char *text, int exitcode);
void skipspaces(void);
void getopen(void);
void getclose(void);
int gettoken(void);
void skiptoken(void);
long getvalue(void);
void getcomma(void);
void gethardstring(void);
void getstring(void);
unsigned long getdvalue(void);
void dolet(void);
int dowaitquiet(void);
int dowaitfor(void);
BOOL getonoroff(void);
void setcom(void);
void doset(void);
void dogoto(void);
void dogosub(void);
unsigned char getonearg(void);
void doif(void);
int getindex(void);
int getintindex(void);
int getstringindex(void);
void doget(void);
void doprint(int channel);
void doclose(void);
void opendevice(void);
void doopen(void);
int doscript(void);
char GTdevice[4][20] = {"/dev/noz2",
"/dev/ttyUSB2",
"/dev/modem",""}; /* default device names to search for */
/* Returns hundreds of seconds */
unsigned long htime(void) {
struct timeval timenow;
gettimeofday(&timenow,NULL);
return(100L*(timenow.tv_sec-hstart)+(timenow.tv_usec)/10000L-hset);
}
/* I use select() 'cause CX/UX 6.2 doesn't have usleep().
On Linux, usleep() uses select() anyway.
*/
void dormir(unsigned long microsecs) {
struct timeval timeout;
timeout.tv_sec=microsecs/1000000L;
timeout.tv_usec=microsecs-(timeout.tv_sec*1000000L);
select(1,0,0,0,&timeout);
}
/* Tests for ENTER key */
void dotestkey(void) {
fd_set fds;
struct timeval timeout;
timeout.tv_sec=0L;
timeout.tv_usec=10000L;
FD_ZERO(&fds);
FD_SET(0,&fds); /* Prepare to select() from stdin */
resultcode=select(1,&fds,0,0,&timeout);
if(resultcode) getchar();
}
/* Exit after resetting terminal settings */
void ext(long xtc) {
ioctl(1, TCSETS, &cons);
exit(xtc);
}
/* Log message if verbose is on */
void vmsg(char *text) {
time_t t;
char *ct;
if(verbose) {
if(lastcharnl==0) {
fprintf(stderr,"\n");
lastcharnl=1;
}
t=time(0);
ct=ctime(&t);
fprintf(stderr,"comgt %c%c:%c%c:%c%c -> %s\n",
ct[11],ct[12],ct[14],ct[15],ct[17],ct[18],
text);
}
}
/* Skip to next statement */
void skipline(void) {
while(script[pc]!='\n' && script[pc]!=0) pc++;
if(script[pc]) pc++;
}
void printwhere(void) {
int a,b,c;
sprintf(msg,"@%04d ",pc);
a=pc;
skipline();
b=pc-1;
pc=a;
c=strlen(msg);
for(;a<b;a++) msg[c++]=script[a];
msg[c]=0;
vmsg(msg);
}
/* Write a null-terminated string to communication device */
void writecom(char *text) {
int res;
unsigned int a;
char ch;
for(a=0;a<strlen(text);a++) {
ch=text[a];
res=write(comfd,&ch,1);
if(senddelay) dormir(senddelay);
if(res!=1) {
serror("Could not write to COM device",1);
}
}
}
/* Gets a single byte from comm. device. Return -1 if none avail. */
int getonebyte(void) {
fd_set rfds;
int res;
char ch;
comecho = 1;
struct timeval timeout;
timeout.tv_sec=0L;
timeout.tv_usec=10000;
FD_ZERO(&rfds);
FD_SET(comfd, &rfds);
res=select(comfd+1,&rfds,NULL,NULL,&timeout);
if(res) {
res=read(comfd,&ch,1);
if(res==1) {
if(comecho) {
if(ch=='\n') lastcharnl=1;
else {
if(ch!='\r') lastcharnl=0;
}
/*fputc(ch,stderr);*/
}
return(ch);
}
}
else {
return(-1); /* Nada. */
}
return(0);
}
void dodump(void) {
char lmsg[STRINGL];
int a,b,c;
c=verbose;
verbose=1;
sprintf(lmsg,"-- Integer variables --"); vmsg(lmsg);
for(a=0;a<26;a++) {
for(b=0;b<11;b++) {
if(intvars[b*26+a]) {
sprintf(lmsg," = %ld",intvars[b*26+a]);
lmsg[1]='a'+a;
lmsg[2]=' ';
if(b) lmsg[2]='0'+b-1;
vmsg(lmsg);
}
}
}
sprintf(lmsg,"-- String variables --"); vmsg(lmsg);
for(a=0;a<26;a++) {
for(b=0;b<11;b++) {
if(stringvars[b*26+a]!=NullString) {
sprintf(lmsg,"$ = \"%s\"",stringvars[b*26+a]);
lmsg[1]='a'+a;
lmsg[2]=' ';
if(b) lmsg[2]='0'+b-1;
vmsg(lmsg);
}
}
}
verbose=c;
}
/* Report script errors and quit */
void serror(char *text, int exitcode) {
int a,line;
char lmsg[STRINGL];
verbose=1;
//dodump();
vmsg("-- Error Report --");
a=pc;
pc=lastpc;
//printwhere();
pc=a;
while(pc!=0 && (script[pc]==' ' || script[pc]=='\t')) pc--;
strcpy(lmsg,"----> ");
for(a=6;a<STRINGL;a++) lmsg[a]=' ';
for(a=0;a<pc-lastpc;a++) {
if(script[a+lastpc]=='\t') lmsg[a+8]='\t';
}
a+=6;
lmsg[a++]='^';
lmsg[a]=0;
vmsg(lmsg);
a=0; line=1;
while(a<pc) {
if(script[a++]=='\n') {
if(a<pc) line++;
}
}
sprintf(lmsg,"Error @%d, line %d, %s. (%d)\n",pc,line,text,exitcode);
vmsg(lmsg);
ext(exitcode);
}
void skipspaces(void) {
while(script[pc]==' ' || script[pc]=='\t' ) pc++;
}
void getopen(void) {
int a;
a=pc;
skipspaces();
if(script[pc++]!='(') {
pc=a;
serror("Function requires open parenthesis",5);
}
}
void getclose(void) {
int a;
a=pc;
skipspaces();
if(script[pc++]!=')') {
pc=a;
serror("Function requires close parenthesis",5);
}
}
void skiptoken(void) {
skipspaces();
while(script[pc]!=' ' && script[pc]!='\t' && script[pc]!='\n' ) pc++;
skipspaces();
}
/* Parse script[pc] to get next statement. Resolve comments and labels... */
int gettoken(void) {
int tokenp=0;
skipspaces();
if(script[pc]=='=' || script[pc]=='<' || script[pc]=='>' || script[pc]=='!') {
token[0]=0;
while(script[pc]=='=' || script[pc]=='<' || script[pc]=='>' || script[pc]=='!') {
token[tokenp++]=script[pc++];
}
token[tokenp]=0;
return(0);
}
if(script[pc]=='#') {
strcpy(token,"rem");
pc++;
return(0);
}
if(script[pc]=='/' && script[pc+1]=='/') {
strcpy(token,"rem");
pc+=2;
return(0);
}
if(script[pc]==':') {
strcpy(token,"label");
pc++;
return(0);
}
while(script[pc]!=' ' && script[pc]!='\n' &&
script[pc]!='\t' && script[pc]!='(') {
token[tokenp]=script[pc++];
if(token[tokenp]>='A' && token[tokenp]<='Z') {
token[tokenp]=token[tokenp]-'A'+'a';
}
if(tokenp++==MAXTOKEN-1) serror("Token too long",5);
}
token[tokenp]=0;
if(strcmp(token,"then")==0) return(gettoken()); /* Ignore then for if */
return(0);
}
/* shitfaced recursive value parser. must write better one */
long getvalue(void) {
unsigned long p=0;
int goone=1;
unsigned int a;
int base,amode;
skipspaces();
if(script[pc]=='(') {
pc++;
p=getvalue();
getclose();
}
else if(script[pc]==')') {
return(p);
}
else if(script[pc]=='"' || script[pc]=='\'' || script[pc]=='$' ) {
serror("Did not expect a string",7);
}
else if( (script[pc]>='a' && script[pc]<='z' &&
script[pc+1]>='a' && script[pc+1]<='z') ||
(script[pc]>='A' && script[pc]<='Z' &&
script[pc+1]>='A' && script[pc+1]<='Z') ) {
gettoken();
getopen();
if(strcmp(token,"len")==0) {
char toto[STRINGL];
strcpy(toto,string);
getstring();
p=strlen(string);
strcpy(string,toto);
}
else if(strcmp(token,"htime")==0) {
p=htime();
}
else if(strcmp(token,"time")==0) {
p=time(0);
}
else if(strcmp(token,"pid")==0) {
p=getpid();
}
else if(strcmp(token,"ppid")==0) {
p=getppid();
}
else if(strcmp(token,"verbose")==0) {
p=verbose;
}
else if(strcmp(token,"isatty")==0) {
p=isatty(getvalue());
}
else if(strcmp(token,"baud")==0) {
p=atol(cspeed);
}
else if(strcmp(token,"access")==0) {
char toto[STRINGL];
char afile[STRINGL];
strcpy(toto,string);
getstring();
strcpy(afile,string);
getcomma();
getstring();
if(string[0]==0) serror("Missing access mode[s]",5);
amode=0;
for(a=0;a<strlen(string);a++) {
switch(string[a]) {
case 'R' :
case 'r' : amode|=R_OK; break;
case 'W' :
case 'w' : amode|=W_OK; break;
case 'X' :
case 'x' : amode|=X_OK; break;
case 'F' :
case 'f' : amode|=F_OK; break;
default : serror("Access modes are r,w,x, and f",5);
}
}
p=access(afile,amode);
strcpy(string,toto);
}
else if(strcmp(token,"val")==0 || strcmp(token,"atol")==0) {
char toto[STRINGL];
strcpy(toto,string);
getstring();
p=atol(string);
strcpy(string,toto);
}
else serror("Unknown Integer function",5);
getclose();
}
if(script[pc]=='%') {
pc++;
skipspaces();
p=resultcode;
}
while(goone) {
if(script[pc]=='+') {
pc++;
p+=getvalue();
}
else if(script[pc]=='-') {
pc++;
p-=getvalue();
}
else if(script[pc]=='^') {
pc++;
p^=getvalue();
}
else if(script[pc]=='&') {
pc++;
p&=getvalue();
}
else if(script[pc]=='|') {
pc++;
p|=getvalue();
}
else if(script[pc]=='*') {
pc++;
p*=getvalue();
}
else if(script[pc]=='/') {
pc++;
a=getvalue();
if(a==0) serror("Division by zero",6);
p/=a;
}
else if((script[pc]>='a' && script[pc]<='z') ||
(script[pc]>='A' && script[pc]<='Z') ) {
p=intvars[getintindex()];
}
else if(script[pc]>='0' && script[pc]<='9') {
base=10;
if(script[pc]=='0') {
base=8;
pc++;
if(script[pc]=='x' || script[pc]=='X') {
base=16;
pc++;
}
if(script[pc]=='%') {
base=2;
pc++;
}
}
while((script[pc]>='0' && script[pc]<='9') ||
(script[pc]>='a' && script[pc]<='f') ||
(script[pc]>='A' && script[pc]<='F')) {
if(script[pc]>='a' && script[pc]<='f') {
p=p*base+script[pc++]-'a'+10;
}
else if(script[pc]>='A' && script[pc]<='F') {
p=p*base+script[pc++]-'A'+10;
}
else {
p=p*base+script[pc++]-'0';
}
}
}
else {
goone=0;
}
}
return(p);
}
void getcomma(void) {
skipspaces();
if(script[pc++]!=',') serror("Comma expected",5);
}
void gethardstring(void) {
int a=0;
skipspaces();
while(script[pc]!=0 && script[pc]!=' ' && script[pc]!='\t' &&
script[pc]!='\n') {
string[a++]=script[pc++];
}
string[a]=0;
}
/* Parse a string from script[pc] */
void getstring(void) {
FILE *fp;
time_t t;
unsigned int a,b;
int c,p=0;
unsigned char ch,match;
string[0]=0;
skipspaces();
if(script[pc]!='"' && script[pc]!='\'' && script[pc]!='$' ) {
serror("Expected a string",7);
}
while(script[pc]!=' ' && script[pc]!='\t' && script[pc]!='\n' && script[pc]!=',' && script[pc]!=')' && script[pc]!='=' && script[pc]!='<' && script[pc]!='>' && script[pc]!='!' ) {
if(script[pc]=='+') pc++;
skipspaces();
if( (script[pc]=='$' && script[pc+1]>='a' && script[pc+1]<='z' &&
script[pc+2]>='a' && script[pc+2]<='z') ||
(script[pc]=='$' && script[pc+1]>='A' && script[pc+1]<='Z' &&
script[pc+2]>='A' && script[pc+2]<='Z') ) {
pc++;
gettoken();
getopen();
if(strcmp(token,"time")==0) {
t=time(0);
strcat(string,ctime(&t));
string[strlen(string)-1]=0;
}
else if(strcmp(token,"rpipe")==0) {
char toto[STRINGL];
strcpy(toto,string);
getstring();
if((fp=popen(string,"r"))==NULL) serror("Could not popen!",4);
fgets(string,STRINGL-1,fp);
string[strlen(string)-1]=0;
pclose(fp);
strcat(toto,string);
strcpy(string,toto);
}
else if(strcmp(token,"env")==0) {
char toto[STRINGL];
strcpy(toto,string);
getstring();
if(getenv(string)) strcat(toto,(char *)getenv(string));
strcpy(string,toto);
}
else if(strcmp(token,"hms")==0) {
long sec,min,hour;
sec=getvalue();
min=sec/60L;
sec-=min*60L;
hour=min/60L;
min-=hour*60L;
sprintf(string,"%s%02ld:%02ld:%02ld",string,hour,min,sec);
}
else if(strcmp(token,"dev")==0) {
strcat(string,device);
}
else if(strcmp(token,"cwd")==0) {
getcwd(string,STRINGL);
}
else if(strcmp(token,"baud")==0) {
strcat(string,cspeed);
}
else if(strcmp(token,"str")==0 || strcmp(token,"ltoa")==0) {
sprintf(string,"%s%ld",string,getvalue());
}
else if(strcmp(token,"hexu")==0) {
sprintf(string,"%s%lX",string,getvalue());
}
else if(strcmp(token,"hex")==0) {
sprintf(string,"%s%lx",string,getvalue());
}
else if(strcmp(token,"oct")==0) {
sprintf(string,"%s%lo",string,getvalue());
}
else if(strcmp(token,"dirname")==0) {
char toto[STRINGL];
strcpy(toto,string);
getstring();
b=0;
for(a=0;a<strlen(string);a++) {
if(string[a]=='/' || string[a]=='\\') b=a;
}
string[b]=0;
strcat(toto,string);
strcpy(string,toto);
}
else if(strcmp(token,"tolower")==0) {
char toto[STRINGL];
strcpy(toto,string);
getstring();
for(a=0;a<strlen(string);a++) {
if(string[a]>='A' && string[a]<='Z' ) string[a]=string[a]-'A'+'a';
}
strcat(toto,string);
strcpy(string,toto);
}
else if(strcmp(token,"toupper")==0) {
char toto[STRINGL];
strcpy(toto,string);
getstring();
for(a=0;a<strlen(string);a++) {
if(string[a]>='a' && string[a]<='z' ) string[a]=string[a]-'a'+'A';
}
strcat(toto,string);
strcpy(string,toto);
}
else if(strcmp(token,"basename")==0) {
char toto[STRINGL];
strcpy(toto,string);
getstring();
b=0;
for(a=0;a<strlen(string);a++) {
if(string[a]=='/' || string[a]=='\\') b=a+1;
}
a=strlen(toto);
while(string[b]) toto[a++]=string[b++];
toto[a]=0;
strcpy(string,toto);
}
else if(strcmp(token,"script")==0) {
strcat(string,scriptfile);
}
else if(strcmp(token,"right")==0) {
char toto[STRINGL];
strcpy(toto,string);
getstring();
getcomma();
b=getvalue();
if(b>strlen(string)) serror("String is shorter than second argument",7);
c=strlen(toto);
a=strlen(string)-b;
while(b!=0 && string[a]!=0) {
toto[c++]=string[a++];
b--;
}
toto[c]=0;
strcpy(string,toto);
}
else if(strcmp(token,"left")==0) {
char toto[STRINGL];
strcpy(toto,string);
getstring();
getcomma();
b=getvalue();
if(b>strlen(string)) serror("String is shorter than second argument",7);
c=strlen(toto);
a=0;
while(b!=0 && string[a]!=0) {
toto[c++]=string[a++];
b--;
}
toto[c]=0;
strcpy(string,toto);
}
else if(strcmp(token,"mid")==0) {
char toto[STRINGL];
strcpy(toto,string);
getstring();
getcomma();
a=getvalue();
getcomma();
b=getvalue();
if(a>strlen(string)) serror("String is shorter than second argument",7);
c=strlen(toto);
while(b!=0 && string[a]!=0) {
toto[c++]=string[a++];
b--;
}
toto[c]=0;
strcpy(string,toto);
}
else if(strcmp(token,"sex")==0) {
strcat(string,"You're a naughty boy, you!");
}
else serror("Invalid string funtion",5);
getclose();
}
else if(script[pc]=='$') {
strcat(string,stringvars[getstringindex()]);
}
else if(script[pc]=='"' || script[pc]=='\'') {
match=script[pc++];
while(script[pc]!=match) {
ch=script[pc++];
if(ch==0) serror("Umatched quote.",5);
if(ch=='\\') {
if(script[pc]<='7' && script[pc]>='0' &&
script[pc+1]<='7' && script[pc+1]>='0' ) {
ch=0;
while(script[pc]>='0' && script[pc]<='7') {
ch=8*ch+script[pc++]-'0';
}
}
else {
switch(script[pc]) {
case 'T' :
case 't' : ch=9; break;
case 'R' :
case 'r' : ch=13; break;
case 'N' :
case 'n' : ch=10; break;
case 'B' :
case 'b' : ch=8; break;
case 'F' :
case 'f' : ch=12; break;
case '"' :
case '^' :
case '\'' :
case '\\' : ch=script[pc]; break;
default : serror("Malformed escaped character",5);
}
pc++;
}
}
else if(ch=='^') {
ch=script[pc];
if(ch!='^' && ch!='"' && ch!='\'' && ch!='\\' ) {
ch=ch&31; /* Control char */
}
pc++;
}
p=strlen(string);
string[p++]=ch;
string[p]=0;
}
pc++; /* Space over quote */
}
else {
p=strlen(string);
string[p++]=script[pc++];
string[p]=0;
}
}
}
/* Get a value, multiply by a hundred (for time values) */
unsigned long getdvalue(void) {
float f;
gettoken();
skipspaces();
sscanf(token,"%f",&f);
f+=0.00001; /* Rounding errors */
return(100.0*f);
}
void dolet(void) {
int index;
BOOL svar=0;
skipspaces();
if(script[pc]=='$') {
svar=1;
index=getstringindex();
}
else index=getintindex();
skipspaces();
gettoken();
if(strcmp(token,"=")!=0) serror("Bad LET assignment, '=' missing",5);
skipspaces();
if(svar) {
getstring();
strcpy(stringvars[index],string);
}
else {
intvars[index]=getvalue();
}
}
/* See documentation for doXXX() functions */
int dowaitquiet(void) {
unsigned long timeout,timequiet,quiet,now;
int c,quit;
timeout=htime()+getdvalue();
quiet=getdvalue();
timequiet=htime()+quiet;
quit=1;
while(quit==1) {
now=htime();
c=getonebyte();
if(c!= -1) timequiet=now+quiet;
if(now>=timequiet) quit=0;
if(now>=timeout) quit=255;
}
return(quit);
}
int dowaitfor(void) {
char strings[20][80];
char buffer[128];
unsigned long timeout;
unsigned int a;
int b,c;
b=0;
buffer[127]=0;
skipspaces();
timeout=htime()+getdvalue();
while(script[pc]==',' || script[pc]=='$' || script[pc]=='"' ||
script[pc]=='\'' ) {
if(script[pc]==',') pc++;
getstring();
skipspaces();
strcpy(strings[b],string);
if(ignorecase) {
for(a=0;a<strlen(strings[b]);a++) {
if(strings[b][a]>='A' && strings[b][a]<='Z') {
strings[b][a]=strings[b][a]-'A'+'a';
}
}
}
b++;
}
strings[b][0]=0;
while(htime()<timeout) {
c=getonebyte();
//printf("Byte \"%c\" ",c);
if(c!= -1) {
if(ignorecase) {
if(c>='A' && c<='Z') c=c-'A'+'a';
}
for(a=0;a<127;a++) buffer[a]=buffer[a+1]; //shuffle down
buffer[126]=c;
b=0;
while(strings[b][0]) {
c=strlen(strings[b]);
if (strcmp(strings[b],&buffer[127-c]) == 0){
return(b);
}
b++;
}
}
}
return(-1);
}
/* Parse script for "on" or "off" wich are tokens, not strings */
BOOL getonoroff(void) {
int a,b;
b=pc;
gettoken();
if(strcmp(token,"on")==0) return(1);
if(strcmp(token,"off")==0) return(0);
pc=b;
a=getvalue();
if(a!=0 && a!=1) serror("Bad value (should be on or off, 1 or 0.)",5);
return(a);
}
void setcom(void) {
stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | CLOCAL | PARENB);
stbuf.c_cflag |= (speed | bits | CREAD | clocal | parity | stopbits );
if (tty && ioctl(comfd, TCSETS, &stbuf) < 0) {
serror("Can't ioctl set device",1);
}
}
void doset(void) {
struct termios console;
int a,b;
gettoken();
if(strcmp(token,"echo")==0) {
a=0;
if(getonoroff()) a=ECHO|ECHOE;
if(ioctl(0, TCGETS, &console)<0) {
serror("Can't ioctl FD zero!\n",2);
}
console.c_lflag &= ~(ECHO | ECHOE);
console.c_lflag |= a;
ioctl(0, TCSETS, &console);
}
else if(strcmp(token,"senddelay")==0) {
senddelay=10000L*getdvalue();
}
else if(strcmp(token,"clocal")==0) {
clocal=0;
if(getonoroff()) clocal=CLOCAL;
setcom();
}
else if(strcmp(token,"umask")==0) {
umask(getvalue()&0777);
}
else if(strcmp(token,"verbose")==0) {
verbose=getonoroff();
}
else if(strcmp(token,"comecho")==0) {
comecho=getonoroff();
}
else if(strcmp(token,"ignorecase")==0) {
ignorecase=getonoroff();
}
else if(strcmp(token,"com")==0) {
skipspaces();
if(script[pc]=='$' || script[pc]=='\'' || script[pc]=='"') {
getstring();
strcpy(token,string);
}
else gettoken();
a=0;
b=0;
while(token[b]>='0' && token[b]<='9') {
a=10*a+token[b++]-'0';
}
if(token[b]) {
switch(token[b]) {
case 'n': parity=0; break;
case 'e': parity=PARENB; break;
case 'o': parity=PARENB|PARODD; break;
default : serror("Parity can only ben E, N, or O",5);
}
b++;
if(token[b]) {
switch(token[b]) {
case '5' : bits=CS5; break;
case '6' : bits=CS6; break;
case '7' : bits=CS7; break;
case '8' : bits=CS8; break;
default : serror("Bits can only be 5, 6, 7, or 8",5);
}
b++;
if(token[b]) {
switch(token[b]) {
case '1': stopbits=0; break;
case '2': stopbits=CSTOPB; break;
default : serror("Stop bits can only be 1 or 2",5);
}
}
}
}
sprintf(cspeed,"%d",a);
switch(a) {
case 0: speed = B0;break;
case 50: speed = B50;break;
case 75: speed = B75;break;
case 110: speed = B110;break;
case 150: speed = B150;break;
case 300: speed = B300;break;
case 600: speed = B600;break;
case 1200: speed = B1200;break;
case 2400: speed = B2400;break;
case 4800: speed = B4800;break;
case 9600: speed = B9600;break;
case 19200: speed = B19200;break;
case 38400: speed = B38400;break;
case 57600: speed = B57600;break;
case 115200: {
if(high_speed == 0) speed = B115200;
else speed = B57600;
break;
}
case 460800: speed = B460800; break;
default: serror("Invalid baudrate",1);
}
setcom();
}
}
void dogoto(void) {
int a,originalpos;
originalpos=pc;
gettoken();
for(a=0;a<labels;a++) {
if(strcmp(token,label[a])==0) break;
}
if(a>=labels) {
pc=originalpos;
sprintf(msg,"Label \"%s\" not found",token);
serror(msg,5);
}
else {
pc=labelpc[a];
}
}
void dogosub(void) {
int a;
if(preturn==MAXGOSUBS) serror("Reached maximum GOSUB depth",3);
a=pc;
gettoken();
returns[preturn++]=pc;
pc=a;
dogoto();
}
/* Gets arguments and returns 0 for a string, 1 for an int. Used with if */
BOOL getonearg(void) {
if(script[pc]=='"' || script[pc]=='\'' || script[pc]=='$' ) {
getstring();
return(0);
}
else {
number=getvalue();
return(1);
}
}
void doif(void) {
char stringarg[STRINGL];
char tokencopy[MAXTOKEN];
int intarg;
skipspaces();
ifres=0;
if(getonearg()) {
intarg=number;
gettoken();
skipspaces();
if(getonearg()!=1) serror("Comparison mis-match",7);
if(strcmp(token,"<")==0) {
if(intarg<number) ifres=1;
}
if(strcmp(token,"<=")==0) {
if(intarg<=number) ifres=1;
}
else if(strcmp(token,"=")==0) {
if(intarg==number) ifres=1;
}
else if(strcmp(token,">")==0) {
if(intarg>number) ifres=1;
}
else if(strcmp(token,">=")==0) {
if(intarg>=number) ifres=1;
}
else if(strcmp(token,"<>")==0) {
if(intarg!=number) ifres=1;
}
else if(strcmp(token,"!=")==0 || strcmp(token,"<>")==0) {
if(intarg!=number) ifres=1;
}
}
else {
strcpy(stringarg,string);
gettoken();
strcpy(tokencopy,token);
skipspaces();
if(getonearg()!=0) serror("Comparison mis-match",7);
if(strcmp(tokencopy,"<")==0) {
if(strcmp(stringarg,string)<0) ifres=1;
}
if(strcmp(tokencopy,"<=")==0) {
if(strcmp(stringarg,string)<=0) ifres=1;
}
else if(strcmp(tokencopy,"=")==0) {
if(strcmp(stringarg,string)==0) ifres=1;
}
else if(strcmp(tokencopy,">")==0) {
if(strcmp(stringarg,string)>0) ifres=1;
}
else if(strcmp(tokencopy,">=")==0) {
if(strcmp(stringarg,string)>=0) ifres=1;
}
else if(strcmp(tokencopy,"!=")==0 || strcmp(tokencopy,"<>")==0) {
if(strcmp(stringarg,string)!=0) ifres=1;
}
}
if(!ifres) skipline();
}
int getindex(void) {
int index;
index=script[pc++];
if(index>='A' && index<='Z') index=index-'A'+'a';
if(index>'z' || index<'a') serror("Malformed variable name",7);
index=index-'a';
if(script[pc]>='0' && script[pc]<='9') {
index=index+(1+script[pc++]-'0')*26;
}
return(index);
}
/* Parse script to find integer variable index */
int getintindex(void) {
skipspaces();
if(script[pc]=='$') serror("Integer variable expected",7);
return(getindex());
}
/* Parse script to find string variable index and allocate memory for storage
as needed. */
int getstringindex(void) {
int index;
skipspaces();
if(script[pc++]!='$') serror("String variable expected",7);
index=getindex();
if(stringvars[index]==NullString) {
stringvars[index]=(char *)malloc(STRINGL);
if(stringvars[index]==NULL) serror("Could not malloc",3);
stringvars[index][0]=0;
}
return(index);
}
void doget(void) {
char terminators[STRINGL];
unsigned int a;
int b,c,index;
int goahead=1;
unsigned long timeout;
timeout=htime()+getdvalue();
getstring();
strcpy(terminators,string);
index=getstringindex();
string[0]=0;
b=0;
resultcode=0;
while(goahead && htime()<timeout) {
c=getonebyte();
if(c!= -1) {
for(a=0;a<strlen(terminators);a++) {
if(c==terminators[a]) goahead=0;
}
if(goahead==0 && b==0) goahead=1; /* Ignore terminators if nothing yet */
else if(goahead) {
string[b++]=c;
string[b]=0;
}
}
}
if(goahead) resultcode= -1;
strcpy(stringvars[index],string);
}
void doprint(int channel) {
skipspaces();
msg[0]=0;
while(script[pc]!=' ' && script[pc]!='\t' && script[pc]!='\n') {
if(script[pc]==',') pc++;
else {
if(script[pc]=='"' || script[pc]=='\'' || script[pc]=='$' ) {
getstring();
strcat(msg,string);
}
else {
sprintf(string,"%ld",getvalue());
strcat(msg,string);
}
}
}
switch(channel) {
case 1: printf("%s",msg); fflush(stdout); break;
case 2: fputs(msg,stderr); break;
case 3:
if(msg[strlen(msg)-1]=='\n') msg[strlen(msg)-1]=0;
vmsg(msg);
break;
case 4:
if(filep==NULL) serror("File not opened",4);
fputs(msg,filep);
break;
}
}
void doclose(void) {
gettoken();
if(strcmp(token,"hardcom")==0) {
if(comfd== -1) serror("Com device not open",1);
vmsg("Closing device");
if (tty && ioctl(comfd, TCSETS, &svbuf) < 0) {
sprintf(msg,"Can't ioctl set device %s.\n",device);
serror(msg,1);
}
close(comfd);
comfd= -1;
}
else if(strcmp(token,"com")==0) {
if(comfd== -1) serror("Com device not open",1);
vmsg("Closing com fd");
close(comfd);
comfd= -1;
}
else if(strcmp(token,"file")==0) {
if(filep==NULL) serror("Log file not open",4);
fclose(filep);
filep=NULL;
}
}
void opengt(void) {
int dcount = 0;
if(strcmp(device,"-")==0) { //no device on command line or env so try the list of devices
printf("Trying list of devices\n");
do{
strcpy(device,GTdevice[dcount]);
if ((comfd = open(device, O_RDWR|O_EXCL|O_NONBLOCK|O_NOCTTY)) >= 0)break;
dcount++;
}while(strlen(GTdevice[dcount]));
if (comfd < 0){
printf("Unable to locate default devices, try the -d option.\n");
ext(1);
}
}
else {
if ((comfd = open(device, O_RDWR|O_EXCL|O_NONBLOCK|O_NOCTTY)) <0) { //O_NONBLOCK|O_NOCTTY)) <0) {//
sprintf(msg,"Can't open device %s.\n",device);
printf(msg);
ext(1);
}
}
if (isatty (comfd))
tty=1;
else
tty=0;
if (tty && ioctl (comfd, TCGETS, &svbuf) < 0) {
sprintf(msg,"Can't control %s, please try again.\n",device);
serror(msg,1);
}
setenv("COMGTDEVICE",device,1);
if (tty)
ioctl(comfd, TCGETS, &stbuf);
speed=stbuf.c_cflag & CBAUD;
if (high_speed == 0) strcpy(cspeed,"115200");
else strcpy(cspeed,"57600");
bits=stbuf.c_cflag & CSIZE;
clocal=stbuf.c_cflag & CLOCAL;
stopbits=stbuf.c_cflag & CSTOPB;
parity=stbuf.c_cflag & (PARENB | PARODD);
stbuf.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXANY | IGNPAR );
stbuf.c_oflag &= ~(OPOST | OLCUC | OCRNL | ONLCR | ONLRET);
stbuf.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL);
stbuf.c_lflag &= ~(ECHO | ECHOE);
stbuf.c_cc[VMIN] = 1;
stbuf.c_cc[VTIME] = 0;
stbuf.c_cc[VEOF] = 1;
setcom();
dormir(200000); /* Wait a bit (DTR raise) */
sprintf(msg,"Opened %s as FD %d",device,comfd);
vmsg(msg);
}
void opendevice(void) {
if(strcmp(device,"-")!=0) {
if ((comfd = open(device, O_RDWR|O_EXCL|O_NONBLOCK|O_NOCTTY)) <0) { //O_NONBLOCK|O_NOCTTY)) <0) {//
sprintf(msg,"Can't open device %s.\n",device);
printf(msg);
ext(1);
}
}
else comfd=0;
if (isatty (comfd))
tty=1;
else
tty=0;
if (tty && ioctl (comfd, TCGETS, &svbuf) < 0) {
sprintf(msg,"Can't ioctl get device %s.\n",device);
serror(msg,1);
}
if (tty)
ioctl(comfd, TCGETS, &stbuf);
speed=stbuf.c_cflag & CBAUD;
switch(speed) {
case B0: strcpy(cspeed,"0");break;
case B50: strcpy(cspeed,"50");break;
case B75: strcpy(cspeed,"75");break;
case B110: strcpy(cspeed,"110");break;
case B300: strcpy(cspeed,"300");break;
case B600: strcpy(cspeed,"600");break;
case B1200: strcpy(cspeed,"1200");break;
case B2400: strcpy(cspeed,"2400");break;
case B4800: strcpy(cspeed,"4800");break;
case B9600: strcpy(cspeed,"9600");break;
case B19200: strcpy(cspeed,"19200");break;
case B38400: strcpy(cspeed,"38400");break;
case B115200:
{
if (high_speed == 0) strcpy(cspeed,"115200");
else strcpy(cspeed,"57600");
break;
}
case B460800: strcpy(cspeed, "460800");break;
}
bits=stbuf.c_cflag & CSIZE;
clocal=stbuf.c_cflag & CLOCAL;
stopbits=stbuf.c_cflag & CSTOPB;
parity=stbuf.c_cflag & (PARENB | PARODD);
stbuf.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXANY | IGNPAR );
stbuf.c_oflag &= ~(OPOST | OLCUC | OCRNL | ONLCR | ONLRET);
stbuf.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL);
stbuf.c_lflag &= ~(ECHO | ECHOE);
stbuf.c_cc[VMIN] = 1;
stbuf.c_cc[VTIME] = 0;
stbuf.c_cc[VEOF] = 1;
setcom();
dormir(200000); /* Wait a bit (DTR raise) */
sprintf(msg,"Opened %s as FD %d",device,comfd);
vmsg(msg);
}
void doopen(void) {
gettoken();
if(strcmp(token,"com")==0) {
skipspaces();
if(script[pc]=='$' || script[pc]=='\'' || script[pc]=='"') {
getstring();
}
else gethardstring();
strcpy(device,string);
opendevice();
}
else if (strcmp(token,"file")==0) {
if(filep!=NULL) serror("File already open",4);
getstring();
if((filep=fopen(string,"a"))==NULL) serror("Could not open file",4);
}
else serror("OPEN only takes com or file argument",5);
}
int doscript(void) {
int a,b;
int exitcode=0;
char line[STRINGL];
pc=0;
while(script[pc]) {
if(script[pc]=='\n') pc++;
lastpc=pc;
skipspaces();
if(verbose) printwhere();
if(gettoken()) serror("Could not gettoken()",5);
if(strcmp(token,"rem")==0) {
skipline();
}
else if (strcmp(token,"label")==0) {
skiptoken(); /* Get rid of keyword */
}
else if(strcmp(token,"open")==0) {
doopen();
}
else if(strcmp(token,"opengt")==0) {
opengt();
}
else if(strcmp(token,"close")==0) {
doclose();
}
else if(strcmp(token,"exec")==0) {
getstring();
strcpy(msg,"exec ");
strcat(msg,string); /* Let sh do all the command line work! */
execl("/bin/sh","sh","-c",msg,(char *)0);
serror("Could not execl /bin/sh!",8);
}
else if(strcmp(token,"exit")==0) {
ext(getvalue());
}
else if(strcmp(token,"testkey")==0) {
dotestkey();
}
else if(strcmp(token,"kill")==0) {
a=getvalue();
resultcode=kill(getvalue(),a);
}
else if(strcmp(token,"fork")==0) {
resultcode=fork();
}
else if(strcmp(token,"hset")==0) {
hset=0;
hstart=time(0);
hset=htime()-getvalue();
}
else if(strcmp(token,"cd")==0) {
getstring();
resultcode=chdir(string);
}
else if(strcmp(token,"putenv")==0) {
getstring();
strcpy(line,string);
resultcode=putenv(line); /* putenv can't read from global string[] */
}
else if(strcmp(token,"wait")==0) {
resultcode=wait(0);
}
else if(strcmp(token,"system")==0) {
getstring();
system(string);
}
else if(strcmp(token,"input")==0) {
FILE *infd = fdopen(0,"r");
fgets(stringvars[getstringindex()],1024,infd);
//fclose(infd);
}
else if(strcmp(token,"get")==0) {
doget();
}
else if(strcmp(token,"print")==0) {
doprint(1);
}
else if(strcmp(token,"eprint")==0) {
doprint(2);
}
else if(strcmp(token,"lprint")==0) {
doprint(3);
}
else if(strcmp(token,"fprint")==0) {
doprint(4);
}
else if(strcmp(token,"if")==0) {
doif();
}
else if(strcmp(token,"else")==0) {
if(ifres) skipline();
}
else if(strcmp(token,"gosub")==0) {
dogosub();
}
else if(strcmp(token,"return")==0) {
if(preturn==0) serror("RETURN without gosub",5);
pc=returns[--preturn];
}
else if(strcmp(token,"goto")==0) {
dogoto();
}
else if(strcmp(token,"waitfor")==0) {
resultcode=dowaitfor();
}
else if(strcmp(token,"waitquiet")==0) {
resultcode=dowaitquiet();
}
else if(strcmp(token,"set")==0) {
doset();
}
else if(strcmp(token,"dec")==0) {
intvars[getintindex()]--;
}
else if(strcmp(token,"inc")==0) {
intvars[getintindex()]++;
}
else if(strcmp(token,"let")==0) {
dolet();
}
else if(strcmp(token,"dump")==0) {
dodump();
}
else if(strcmp(token,"abort")==0) {
vmsg("Aborting");
abort();
}
else if(strcmp(token,"send")==0) {
getstring();
writecom(string);
}
else if(strcmp(token,"flash")==0) {
b=speed;
speed=0;
setcom();
a=getdvalue();
dormir(10000L*a);
speed=b;
setcom();
}
else if(strcmp(token,"sleep")==0) {
a=getdvalue();
if(a<10000) dormir(10000L*a);
else sleep(a/100); /* I guess it's the same. Oh well, past 100 secs,
use sleep instead. */
}
else {
/* Humour is the spice of life. */
switch(time(0)&7) {
case 0 : serror("That's human mumbo-jumbo to me",5); break;
case 1 : serror("Lovely but incomprehensible",5); break;
case 2 : serror("What's that, governor?",5); break;
case 3 : serror("Very funny. I don't get it",5); break;
case 4 : serror("Huh?",5); break;
case 5 : serror("comgt doesn't speak spanish",5); break;
case 6 : serror("Mais, qu'est-ce que vous dites?",5); break;
default: serror("%E-6837-% : Corrupted human data detected",5); break;
}
}
skipspaces();
while(script[pc]=='\n') pc++;
}
return(exitcode);
}
int main(int argc,char **argv) {
unsigned int a;
int aa,b,i,skip_default;
unsigned char ch;
unsigned char terminator='\n';
char *devenv,line[STRINGL];
//Load up the COMGT device env variable if it exists
devenv = getenv("COMGTDEVICE");
if (devenv != NULL && strlen(devenv)){
strcpy(device,devenv);
}
else strcpy(device,"-");
FILE *fp;
hstart=time(0);
hset=htime();
preturn=0;
skip_default=0;
filep=NULL;
scriptspace=4096;
ioctl(1, TCGETS, &cons);
if((script=( char *)malloc(scriptspace))==NULL) {
serror("Could not malloc()",3);
}
for(a=0;a<NVARS;a++) {
intvars[a]=0;
stringvars[a]=NullString;
}
strcpy(cspeed,"0");
scriptfile[0]=0;
b=0; a=0;
for(a=0;a<strlen(argv[0]);a++) {
if(argv[0][a]=='/') b=a+1;
}
while((aa=getopt(argc,argv,"xheVvd:t:sb:"))!= -1) {
switch(aa) {
case 0:
ext(0);
break;
case 't':
terminator=optarg[0];
sprintf(msg,"Alternate line terminator set to \"%c\"",terminator);
vmsg(msg);
break;
case 'd':
strcpy(device,optarg);
//opendevice();
break;
case 'e':
comecho=1;
vmsg("Communication echo turned on");
break;
case 'v':
verbose=1;
vmsg("Verbose output enabled");
break;
case 's':
skip_default=1;
break;
case 'V':
printf("comgt version %s Copyright Paul Hardwick (c) %s\n",COMGT_VERSION,YEARS);
ext(1);
break;
case 'h':
printf("comgt version %s Copyright Paul Hardwick (c) %s\n",COMGT_VERSION,YEARS);
printf("\nType 'comgt help' for more information\n");
ext(1);
break;
case 'x':
printf("High speed overide (115200 is now 57600).\n");
high_speed =1;
break;
default:
ext(1);
}
}
if(optind<argc) {
strcpy(scriptfile,argv[optind++]);
sprintf(msg,"Script file: %s",scriptfile);
vmsg(msg);
}
char * code;
code = get_code(scriptfile);
if (code != NULL){
scriptspace=strlen(code)+2;
if((script=( char *)realloc(script,scriptspace))==0) {
serror("Could not allocate memory for script.",3);
}
strcpy(script,code);
for(aa=0;aa<scriptspace;aa++) {
if(script[aa]==terminator) script[aa]='\n';
}
//scriptfile[0] = '\0';
}
else if (scriptfile[0]) {
code = get_code("default");
if (code != NULL && !skip_default){
scriptspace=strlen(code)+2;
if((script=( char *)realloc(script,scriptspace))==0) {
serror("Could not malloc()",3);
}
strcpy(script,code);
for(aa=0;aa<scriptspace;aa++) {
if(script[aa]==terminator) script[aa]='\n';
}
//scriptfile[0] = '\0';
}
if((fp=fopen(scriptfile,"r"))==NULL) {
strcpy(scriptfilepath,"/etc/comgt/");
strcat(scriptfilepath,scriptfile);
if((fp=fopen(scriptfilepath,"r"))==NULL) {
sprintf(msg,"Could not open scriptfile \"%s\".\n",scriptfile);
serror(msg,1);
}
}
i=strlen(script);
if(i) {
script[i++]='\n'; script[i]=0;
}
while((fgets(line,STRINGL-1,fp))!=NULL) {
b=strlen(line);
if((scriptspace-i)<STRINGL) {
scriptspace+=STRINGL+STRINGL;
if((script=(char *)realloc(script,scriptspace))==NULL) {
serror("Could not realloc()",3);
}
}
for(aa=0;aa<b;aa++) {
script[i]=line[aa];
if(script[i]==terminator) script[i]='\n';
i++;
}
}
script[i]=0;
fclose(fp);
}
if(script[0]) {
i=strlen(script)-1;
while((script[i]=='\n' || script[i]==' ' || script[i]=='\t') && i!=0) i--;
script[++i]='\n';
script[++i]=0;
}
i=strlen(script); /* Script is one huge string */
/* Indexing labels */
label=(char **)malloc(sizeof(char *)*MAXLABELS);
labelpc=(int *)malloc(sizeof(int *)*MAXLABELS);
if(label==NULL || labelpc==NULL) {
serror("Can't malloc",3);
}
labels=0;
pc=0;
while(script[pc]) {
lastpc=pc;
gettoken();
if(strcmp(token,"label")==0) {
gettoken();
for(aa=0;aa<labels;aa++) {
if(strcmp(token,label[aa])==0) {
pc=lastpc;
serror("Duplicate label",5);
}
}
if(labels==MAXLABELS) serror("Maximum number of labels reached",3);
labelpc[labels]=lastpc;
label[labels]=(char *)malloc(strlen(token)+1);
if(label[labels]==NULL) serror("Can't malloc one label",3);
strcpy(label[labels],token);
labels++;
}
skipline();
}
//printf(script);
if(verbose) {
sprintf(msg,"argc:%d",argc);
vmsg(msg);
for(aa=0;aa<argc;aa++) {
sprintf(msg,"argv[%d]=%s",aa,argv[aa]);
vmsg(msg);
}
vmsg(" ---Script---");
aa=0; b=0; ch='\n';
while(aa<i) {
if(ch=='\n' && script[aa]!=0) {
fprintf(stderr,"%4d@%04d ",++b,aa);
}
ch=script[aa++];
fputc(ch,stderr);
}
vmsg(" ---End of script---");
}
if(script[0]==0) {
fprintf(stderr,"No script!\n");
ext(1);
}
a=doscript();
dormir(200000);
if(comfd!= -1) close(comfd);
sprintf(msg,"Exit with code %d.\n",a);
vmsg(msg);
ext(a);
}