This is am.c in view mode; [Download] [Up]
/* TEXT WAS MADE WITH 4*SPACE = 1*TAB
program : am.c
version : see the version.h file
date : Tue May 3 12:11:51 MET DST 1994
purpose : simulate an answering machine with fax- and datacall capabilities
author : by jolly ( who else )
ADPCM3 support included by kiwi@belly, April '94
*/
int modem; // fildes of modem
int timerstatus; // sigalarm timerstatus for recording and playing
int realtime; // sigalarm timer for recording and playing
int connected;
int amstatus;
#import <libc.h>
#import <mach/cthreads.h>
#import "precomp.h"
//extern int init_amserver();
int main(void)
{
int tty;
signal(SIGALRM ,(void *)sig_alrm);
signal(SIGINT ,(void *)sig_fatal);
signal(SIGTERM ,(void *)sig_fatal);
if(chdir(PATH)==-1)
fatal("Can't change working directory to %s\n",PATH);
/* forking server thread for status inquiries, kiwi@belly */
//cthread_detach(cthread_fork((cthread_fn_t)init_amserver, (any_t)NULL));
//tty=open("/dev/tty",O_RDWR);
//ioctl(tty, TIOCNOTTY, 0);
close(STD_IN);
close(STD_OUT);
close(STD_ERR);
#if NORMALMODE
tty=open("/dev/tty",O_RDWR);
ioctl(tty, TIOCNOTTY, 0);
#endif
#if DEBUG
log("*** Answering Machine started ***");
#endif
amstatus = 1;
#if NORMALMODE
while(1) wait_for_call();
#else
wait_for_call();
#endif
}
void wait_for_call(void)
{
int nfds=1;
fd_set fdset;
do
{
do
{
#ifndef NORMALMODE
close(STD_IN);
#endif
open_line(DIAL_OUT);
reset_modem();
unlock_line(DIAL_OUT);
/* set EDR to recognize CNG (FAX) and DTMF 0 (DATACALLS) */
/*ask("ATS40=%d",OK,8+16+32+64);*/
wait_for("XXX",200);
FD_ZERO(&fdset);
FD_SET(modem,&fdset);
#if NORMALMODE
select(nfds,&fdset,NULL,NULL,NULL);
#endif
}
while( tst_lock() && close_line(DIAL_OUT));
#if NORMALMODE
if(wait_for("NO CARRIER",100))
#else
if(1)
#endif
{
#if DEBUG > 2
log("answering call");
#endif
ask("AT+FCLASS=8","OK");
say("ATA");
}
else
{
ask("AT+FCLASS=8","OK");
ask("ATS0=%d","OK",RING);
}
}
#if NORMALMODE
/* Jedes Klingeln dauert 6 Sekunden? kiwi@belly,
oh Mann, diese 20000 hat mir den letzten Nerv geraubt! */
while( !wait_for("VCON",RING*6000) && close_line(DIAL_OUT) );
#else
while(0);
#endif
main_loop();
reset_modem();
close_line(DIAL_OUT);
}
/*************************************************************************************nifty fncts */
/* bug fixes by kiwi@belly, Fri May 6 15:26:27 MET DST 1994 */
int rec_message(char *name)
{
static char *buf=NULL;
FILE *feil;
static struct zyxel header;
static struct itimerval alrm,oalrm;
int pass=0, dle_recv=0;
char *i,*bend, *prev;
char dle = DLE;
amstatus = 2;
if(buf==NULL)
{
buf=(char *)malloc(sizeof(char)*BSIZE);
strcpy(header.title,"ZyXEL");
header.mode=ZYX_VOC;
switch(SPEECH)
{
case ADPCM_3 :header.voice=ZYX_AD3;break;
case ADPCM_2 :header.voice=ZYX_AD2;break;
case CELP :header.voice=ZYX_CLP;break;
default :header.voice=ZYX_AD2;break;
}
}
if((feil=fopen(name,"w"))==NULL)
fatal("Can't open File :%s",name);
fwrite(&header,sizeof(struct zyxel),1,feil);
ask("at+vsd=%d,%d","OK",SIL_TRES,SIL_TIME*10);
/*ask("at+vvt=%d","OK",40);
ask("at+vgt=%d","OK",100); seems to work only on internal speaker*/
ask(ATSP,"OK");
ask("at+vrx","CONNECT\r\n");
#if DEBUG
log("recording message : %s",name);
#endif
siginterrupt(SIGALRM,1);
alrm.it_interval.tv_sec=0;
alrm.it_interval.tv_usec=1000000/RPS;
alrm.it_value.tv_sec=0;
alrm.it_value.tv_usec=1000000/RPS;
if(setitimer(ITIMER_REAL,&alrm,&oalrm)==-1)
fatal("error creating timer\n");
/* who is going to remove this timer when exit on silence or similar circumstances?
kiwi@belly
*/
timerstatus =T_REC;
realtime =0;
do
{
do
{
sigpause(SIGALRM);
if(timerstatus==T_STP) {
fclose(feil);
ask("\x10\x03","VCON");
alrm.it_interval.tv_usec=0;
alrm.it_value.tv_usec=0;
if(setitimer(ITIMER_REAL,&alrm,&oalrm)==-1)
fatal("error creating timer\n");
return MAXTIME;
}
bend=buf+read(modem,buf,BSIZE);
}
while(bend<=buf);
prev = buf;
for(i=buf;i<bend;i++)
{
/* we have to handle a dle shielded code? */
if(dle_recv)
{
dle_recv = 0; /* we got it */
/* write the buffer without the last DLE */
if((i-prev-1)>0) fwrite(prev,1,i-prev-1,feil);
/* previous marker gets set after <DLE>x */
prev = i+1;
#if DEBUG
if(*i!=DLE) log("REC time=%d %c=%x",realtime,(char)*i,(char)*i);
#endif
switch(*i)
{
case 'e' :{fclose(feil); ask("\x10\x03","VCON");return DATACALL;} /* kiwi, ROM6.12 */
case 's' :{fclose(feil); ask("\x10\x03","VCON");return SILENCE;}
case 'q' :{fclose(feil); ask("\x10\x03","VCON");return QUIET;}
case '*' :{fclose(feil); ask("\x10\x03","VCON");return (pass)?pass:STAR;}
case '#' :pass=0;break;
case '1' :pass=pass*10+1;break;
case '2' :pass=pass*10+2;break;
case '3' :pass=pass*10+3;break;
case '4' :pass=pass*10+4;break;
case '5' :pass=pass*10+5;break;
case '6' :pass=pass*10+6;break;
case '7' :pass=pass*10+7;break;
case '8' :pass=pass*10+8;break;
case '9' :pass=pass*10+9;break;
case '0' :pass=pass*10;break;
case 'c' :{fclose(feil); ask("\x10\x03","VCON");return FAX;}
case 'b' :{fclose(feil); ask("\x10\x03","VCON");return BUSY;}
case 'h' :
case 'd' :{fclose(feil); ask("\x10\x03","VCON");return HANG_UP;}
case ETX :{fclose(feil); ask("\x10\x03","VCON");return EOS;}
case DLE :fwrite(&dle,1,1,feil);
fwrite(&dle,1,1,feil);break; /* write <DLE><DLE> verbatim */
/* 'h' is not documented?!?!
'd' is new since 6.11a and means Dialtone detected
*/
default :log("unknown <DLE> code, time=%d %c=%x",realtime,(char)*i,(char)*i);
break;
}
}
else
if(*i == DLE) dle_recv = 1;
} /* closed for loop */
/* there is really something to write?
if there is and no dle is received at the moment - write to end of buffer.
else don't write the <DLE> which is the last char in buffer
*/
if(bend>prev) fwrite(prev,1,bend-prev - (dle_recv?1:0),feil);
prev = buf;
}
while(1);
return 0; // never reached
}
int ply_message(char *name)
{
char *snd;
FILE *feil;
int mfl;
int pwd;
static char a_name[200];
static struct zyxel header;
strcpy(a_name,name);
strcat(a_name,Z_EXT);
amstatus = 3;
if((feil=fopen(a_name,"r"))==NULL)
{
log("Can't play Message :%s",a_name);
return Z_ERROR;
}
#if DEBUG
log("Playing Message :%s",a_name);
#endif
fseek(feil,0L,SEEK_END);
mfl=ftell(feil);
rewind(feil);
snd=(char *)malloc(sizeof(char)*mfl-sizeof(struct zyxel));
fread(&header,sizeof(struct zyxel),1,feil);
fread(snd,1,mfl,feil);
fclose(feil);
switch(isZyXEL(&header))
{
case ZYX_AD3 :ask("at+vsm=3","OK");break;
case ZYX_AD2 :ask("at+vsm=2","OK");break;
case ZYX_CLP :ask("at+vsm=1","OK");break;
case ZYX_NOSNDHEADER:return log("File:%s is not a voice file",name);
default :return log("File:%s voice format not supported",name);
}
/* set modem to send voice data */
ask("AT+VTX","CONNECT\r\n");
pwd=block_write(snd,mfl);
if(pwd==FAX)
{
free(snd);
ask("\x10\x03","VCON");
#if DEBUG
log("Detected Fax while playing a message");
#endif
try_fax_connection(NULL);
return FAX;
}
ask("\x10\x03","VCON");
free(snd);
return pwd;
}
int try_data_connection(char *rec_name)
{
amstatus = 4;
ask("at+fclass=0","OK");
ask("at&n0&d0&c0s2=32","OK");
close_line(DIAL_OUT);
open_line(DIAL_CONNECT);
ask("AT&D3","OK");
say("ATA");
if( (realtime<(SIL_TIME+5)*RPS) && wait_for("CONNECT",MAX_CD*1000))
{
char c=32;
#if DEBUG
log("Got Data Connection");
#endif
remove(rec_name);
write(modem,&c,1);usleep(500);
write(modem,&c,1);usleep(500);
write(modem,&c,1);usleep(500);
wait_for("OK",2000);
ask("at&c1","OK");
ask("ats2=255","OK");
say("ato");
execle("/usr/etc/getty","/usr/etc/getty",GETY,NULL,NULL);
exit(1);
}
else
{
#if DEBUG
log("Got no Data Connection");
#endif
close_line(DIAL_CONNECT);
open_line(DIAL_OUT);
return shorten_file(rec_name);
}
return EOC;
}
/* shortens the recorded message file by the given silence interval value.
I would like very much to add something to cut off the hangup noise at
the end of a recording but I fear that a fair amount of signal processing
has to be done to achieve this.
This procedure was moved from inside try_data_connection, because I needed
it for correct handling of <DLE>q which works since I upgraded to ROM 6.12
(it didn't work in 5.01 so _every_ recording was trying a data connection
in the end and the recording got truncated appropriately. Since <DLE>q
works, we have to truncate also on normal termination.)
kiwi@belly */
int shorten_file(char *rec_name)
{
FILE *feil;
char *tmp;
long len;
feil=fopen(rec_name,"r");
if(feil==NULL)
{
log("failed fopen can't open messagefile %s for reading",rec_name);
return Z_ERROR;
}
fseek(feil,0L,SEEK_END);
len=ftell(feil)-( (SIL_TIME)*BPS+(BPS/2) );
if(len<0)
{
fclose(feil);
remove(rec_name); // never again - this fuckin people that hangup after the welcome message !
}
else
{
rewind(feil);
tmp=(char *)malloc(sizeof(char)*len+1);
if(!tmp)
{
log("Cant alloc enough memory");
return Z_ERROR;
}
fread(tmp,1,len,feil);
fclose(feil);
remove(rec_name);
feil=fopen(rec_name,"w");
if(feil==NULL)
{
log("failed fopen can't open messagefile %s for writing",rec_name);
return Z_ERROR;
}
fwrite(tmp,1,len,feil);
fclose(feil);
free(tmp);
}
return EOC;
}
/*************************************************************************************low level fncts */
/* changed to log any ERROR message even ifndef DEBUG, kiwi@belly */
int wait_for(char *s,long t)
{
static char tst[100];
static char err[] = "ERROR";
static struct itimerval alrm,oalrm;
int b=0,a=0,ok,flg=0,len,errok;
siginterrupt(SIGALRM,1);
alrm.it_interval.tv_sec=t/1000; /* changed interval timer from 0, because it might happen */
alrm.it_interval.tv_usec=(t%1000)*1000; /* (in rare circumstances) that the timer expires during */
alrm.it_value.tv_sec=t/1000; /* the handling of the input (not during the read) which */
alrm.it_value.tv_usec=(t%1000)*1000; /* would cause this function to hang forever. kiwi@belly */
if(setitimer(ITIMER_REAL,&alrm,&oalrm)==-1)
fatal("error creating timer");
flg=0;
len=strlen(s);
tst[len]=0;
ok=0;
errok=0;
do
{
a=read(modem,tst+((++b)%len),1);
if(a<1) /* changed from a==-1 because a==0 means EOF (DTR dropped?).
There might be more reasons why the read failed than
SIGALRM, kiwi@belly. It really got stuck here!!! */
{
flg=1;
}
else
{
/*log("wait for recv: %c=%x",tst[b%len],tst[b%len]);*/
if(tst[b%len]==s[ok]) ok++;
else ok=0;
if(tst[b%len]==err[errok]) errok++;
else errok=0;
if(errok == 5)
{
log("Waited for (%s), got ERROR time=%d",s,oalrm.it_value.tv_usec);
flg = 1;
}
}
}
while(ok!=len && !flg);
alrm.it_interval.tv_sec=0;
alrm.it_interval.tv_usec=0;
alrm.it_value.tv_sec=0;
alrm.it_value.tv_usec=0;
if(setitimer(ITIMER_REAL,&alrm,&oalrm)==-1)
fatal("error removing timer");
#if DEBUG > 1
log("Waited for (%s) got (%s) == %s time=%d",s,tst,ok?"YES":"NO",oalrm.it_value.tv_usec);
#endif
return ok;
}
/* some variables we need for the communication between the sigalrm routine and block_write */
int modrecv_start = 0;
int modrecv_end = 0;
unsigned char modems_opinion[8]; /* ringbuffer for XON/XOFF requests from modem */
/* almost completely rewritten by kiwi@belly.
added XON/XOFF support.
Why don't we get XON/XOFF support by the tty driver when using RAW|TANDEM?
*/
int block_write(char * snd,int size)
{
int i=0,a,pass=0;
static struct itimerval alrm,oalrm;
char c;
int en_tx = 1, dle_recv = 0;
modrecv_start = modrecv_end = 0;
siginterrupt(SIGALRM,1);
alrm.it_interval.tv_sec=0;
alrm.it_interval.tv_usec=1000000/(PPS);
alrm.it_value.tv_sec=0;
alrm.it_value.tv_usec=1000000/(PPS);
if(setitimer(ITIMER_REAL,&alrm,&oalrm)==-1)
fatal("error creating timer");
realtime =PPS;
timerstatus =T_PLY;
do
{
/* if transmission enabled */
if(en_tx)
{
if((size-i)>=BPP)
a=write(modem,snd+i,BPP);
else
a=write(modem,snd+i,size-i);
if(a>0)
i+=a;
#if DEBUG > 4
else log("block write probably interrupted by sigalrm - subject to retry");
#endif
}
/* got something from modem */
if(modrecv_start != modrecv_end)
{
c = modems_opinion[modrecv_start];
modrecv_start = (++modrecv_start) & 7;
#if DEBUG > 2
log("checking modem input");
log("received from modem : %c = %x - realtime(s)=%f",c,c,1.0*realtime/PPS);
#endif
if(c=='\x13')
{
en_tx = 0; /* disable transmission */
#if DEBUG > 2
log("XOFF from modem - stopped");
#endif
}
else
if(c=='\x11')
{
en_tx = 1; /* enable transmission */
#if DEBUG > 2
log("XON from modem - started");
#endif
}
else
/* got something important? */
if(dle_recv)
{
#if DEBUG
log("PLY time=%d %c=%x",realtime,c,c);
#endif
dle_recv = 0;
switch(c)
{
case '*' :{return (pass)?pass:STAR;}
case '#' :pass=0;break;
case '1' :pass=pass*10+1;break;
case '2' :pass=pass*10+2;break;
case '3' :pass=pass*10+3;break;
case '4' :pass=pass*10+4;break;
case '5' :pass=pass*10+5;break;
case '6' :pass=pass*10+6;break;
case '7' :pass=pass*10+7;break;
case '8' :pass=pass*10+8;break;
case '9' :pass=pass*10+9;break;
case '0' :pass=pass*10;break;
case 'c' :return FAX;
case 'b' :return BUSY;
case 'h' :
case 'd' :return HANG_UP; /* 'd' = Dialtone (6.11a) */
case 'e' :return DATACALL; /* 'e' = Data Calling Tone (6.11a) */
case ETX :return EOS;
}
}
else
if(c==DLE) dle_recv=1;
}
}
while(i<size);
/*wenn einkommentiert bleibt am gelegentlich an dieser Stelle haengen (startrek.zyxel)
while(realtime*BPP<i)
sigpause(SIGALRM);*/
alrm.it_interval.tv_sec=0;
alrm.it_interval.tv_usec=0;
alrm.it_value.tv_sec=0;
alrm.it_value.tv_usec=0;
if(setitimer(ITIMER_REAL,&alrm,&oalrm)==-1)
fatal("error removing timer");
return EOC;
}
/*************************************************************************************signals */
int sig_alrm()
{
int val;
switch(timerstatus)
{
case T_REC :if(realtime++>MAXRECTIME) timerstatus=T_STP;break;
case T_PLY : /* In case of playing a message, the feedback from the modem gets
regularily checked if modem wants a pause (XOFF). The fcntl calls
make input non blocking for a moment (non blocking writes are
not what we want usually)
If we get something, modrecv_end gets incremented to reflect the number
of characters received. modrecv_start gets incremented after the
processing in block_write.
kiwi@belly
*/
realtime++;
val = fcntl(modem, F_GETFL, 0);
fcntl(modem, F_SETFL, val|O_NDELAY);
while (read(modem,&modems_opinion[modrecv_end],1)==1)
modrecv_end = (++modrecv_end) & 7;
fcntl(modem, F_SETFL, val);
break;
case T_FAX :realtime--;break;
}
return 0;
}
/* modified to reset modem even if signal occured during voice transmission
this makes up a better testing environment where we can simply kill the job without hanging the modem
kiwi@belly
*/
int sig_fatal()
{
log("sig_fatal");
ask("\x10\x03", "VCON");
ask("AT+VLS=0", "OK");
ask("AT+FCLASS=0", "OK");
fatal("Signal occured");
return 0;
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.