sources-request@mirror.UUCP (08/04/86)
Submitted by: ihnp4!quest!gene Mod.sources: Volume 6, Issue 88 Archive-name: sysVdial/Part3 [ I did not try to compile this, as we run BSD exlusively. It appears that the hardest part of doing the port will be emulating the timed- out reads (c_cc[VTIME]) in dial.c --r$ ] # Generic Modem Dialer subroutine and support programs for system V. # # Modem configuration is done in the user configured file # dialinfo. Should be able to dial any modem (eg Vadic, Hayes) # with a built-in auto dialer. Replaces ATT dial(3C). # Works with CU, uucico, lp, etc. # # This is part 3 of 3. # # The parts are: # # 1) README file and all documentation. # 2) Makefile, dialinfo, *.h, some *.c files # 3) dial.c # #--------CUT---------CUT---------CUT---------CUT--------# ######################################################### # # # This is a shell archive file. To extract files: # # # # 1) Create an empty directory for the files. # # 2) Write a file, such as "file.shar", containing # # this archive file into the directory. # # 3) Type "sh file.shar". Do not use csh. # # # ######################################################### echo Creating: dial.c sed -e 's/^#//' >dial.c <<'end_dial.c' #/**************************************************************** # * Copyright 1986, Gene H. Olson, Quest Research, Burnsville * # * Minnesota. Permission to copy and distribute this * # * program, all associated code in source and binary form, * # * for any purpose, so long as this notice is preserved. * # ****************************************************************/ # # #/******************************************************************** # * System V dial(3) generic modem dialer. * # ********************************************************************/ # ##include <stdio.h> ##include <time.h> ##include <setjmp.h> ##include <signal.h> ##include <fcntl.h> ##include <ctype.h> ##include <errno.h> ##include <termio.h> # ##include "dial.h" ##include "dialinfo.h" # ##define loop for(;;) # ##define YIPE(x) { if ((x) < 0) yipe(__LINE__); } ##define NS(x) ( (x) ? (x) : nullstring ) # #extern int errno ; #extern int sys_nerr ; #extern char *sys_errlist[] ; # #extern void perror() ; #extern int (*signal())() ; #extern struct tm *localtime() ; #extern char *getenv() ; #extern unsigned alarm() ; #extern long time() ; #extern char *ttyname() ; #extern char *strcat() ; #extern char *strcpy() ; #extern char *strncpy() ; #extern char *strncat() ; #extern void exit() ; #extern unsigned sleep() ; # #extern char *parseshort() ; #extern void dialfree() ; # # #/* # * Externally visible stuff. # */ # #int nolock ; /* Disable lockfile usage */ # #char *ldevice[9] ; /* L-devices pointer array */ # #char dialdev[50]; /* Device pathname */ #char diallock[50]; /* Lockfile pathname */ #char dialnum[50] ; /* Telephone number */ # #int dialfd ; /* Dialer file descriptor */ # #/* # * Local storage. # */ # #static jmp_buf jmpstate ; /* Interrupt recovery */ # #static char ldbuf[100] ; /* L-devices buffer */ # #static int iflags[4] ; /* Initial termio c_cflag */ #static int cflags[4] ; /* Current termio c_cflag */ # #static int carrier ; /* Carrier detect option */ #static int debug ; /* Selected debug options */ #static int gotalarm ; /* Dialer alarms */ #static int inline ; /* In middle of output line */ # #static char nullstring[] = "(null)" ; # #/* # * Match list definition. # */ # ##define NMATCH 20 /* Max # of match strings */ # #typedef struct { # short m_index ; /* Number of chars */ # short m_state ; /* Associated state value */ # char* m_string ; /* Match string */ # } MATCH ; # #/* # * Dialer state stuff. # */ # ##define NBUF 1000 /* Size of temporary buffer */ # ##define DS_NONE 1001 /* No state defined */ ##define DS_FAIL 1003 /* Fail state */ ##define DS_SUCCEED 1004 /* Succeed state */ ##define DS_TIMEOUT 1005 /* Timeout state */ ##define DS_HANGUP 1006 /* Hangup state */ # #typedef struct { # short ds_retry ; /* Retry counter */ # short ds_sec ; /* Timeout in seconds */ # short ds_hangup ; /* Hangup state */ # short ds_timeout ; /* Timeout state */ # short ds_nmatch ; /* Number of match items */ # MATCH ds_match[NMATCH] ; /* Match item array */ # char *ds_buf ; /* Current buffer pointer */ # char ds_failmsg[80] ; /* Failure message */ # char ds_buffer[NBUF] ; /* Scratch buffer */ # } DSTATE ; # #/* # * Dialer debug options. # * # * The user may specify a DIALDEBUG variable in # * the environment, using one or more of the characters # * listed in the table below. When this is done, # * debugging output is produced. # */ # ##define D_ALL 0xff /* Show everything */ ##define D_DEVICE 0x01 /* Show L-device decisions */ ##define D_STATE 0x02 /* Show state transitions */ ##define D_LINE 0x04 /* Show line control actions */ ##define D_MATCH 0x08 /* Show matched strings */ ##define D_COMM 0x10 /* Show transmitted/received comm */ # #static char dtable[] = { # 'd', D_DEVICE, # 's', D_STATE, # 'l', D_LINE, # 'm', D_MATCH, # 'c', (D_COMM|D_LINE|D_MATCH), # 'a', (D_DEVICE|D_STATE|D_LINE|D_MATCH|D_COMM), # 0 } ; # # #/* # * Dialer baud rate table. # */ # #static short btable[] = { # 50, CSTOPB|B50, # 75, CSTOPB|B75, # 110, CSTOPB|B110, # 134, CSTOPB|B134, # 150, B150, # 200, B200, # 300, B300, # 600, B600, # 1200, B1200, # 1800, B1800, # 2400, B2400, # 4800, B4800, # 9600, B9600, # 0 } ; # # # #/****** # * getbaud - Get baud rate entry. # */ # #static int #getbaud(rate) #int rate ; #{ # register short *sp ; # # for (sp = btable ;; sp += 2) { # if (sp[0] == 0) return(-1) ; # if (sp[0] == rate) return(sp[1]) ; # } # } # # # #/***** # * errstring - Get system error string. # */ # #static #char * #errstring() #{ # static char buf[20] ; # # if ((unsigned) errno <= sys_nerr) return(sys_errlist[errno]) ; # # (void) sprintf(buf, "errno=%d", errno) ; # return(buf) ; # } # # # #/***** # * dialmsg - Dialer debug output routine. # */ # #/*VARARGS1*/ #void #dialmsg(s, args) #char *s ; /* Printf-style string */ #struct { char _arg[16] ; } args ; /* Printf-style arguments */ #{ # struct tm tm ; # long clock ; # # /* # * Finish up any line in progress. # */ # # if (inline) { # (void) putc('\n', stderr) ; # inline = 0 ; # } # # /* # * Note the time. # */ # # if (debug) { # (void) time(&clock) ; # tm = *localtime(&clock) ; # # (void) fprintf(stderr,"%02d:%02d:%02d ", # tm.tm_hour, tm.tm_min, tm.tm_sec # ) ; # } # # /* # * Output the user's message. # */ # # (void) fprintf(stderr, s, args) ; # (void) fflush(stderr) ; # } # # # #/***** # * yipe - Print error messages for unexpected errors. # */ # #static void #yipe(lineno) #int lineno ; #{ # dialmsg("dial.c line %d: %s\n", lineno, errstring()) ; # } # # # #/***** # * wakeup - Wakeup on alarm interrupts. # */ # #static int #wakeup() #{ # /* # * Note that we got the interrupt, re-arm for # * the next interrupt, and schedule another one # * so we can't get caught in a window where the # * interrupt is lost. # */ # # gotalarm = 1 ; # (void) signal(SIGALRM, wakeup) ; # return alarm(1) ; # } # # # #/***** # * catch - Catch SIGHUP, SIGINT, SIGQUIT, SIGTERM. # */ # #static int #catch(num) #{ # /* # * Do a longjump back to "dial" which will then # * return failure. # */ # # if (debug) dialmsg("Got signal: %d\n", num) ; # longjmp(jmpstate,num) ; # } # # # #/***** # * showchar - Display a character in unambiguous format. # */ # #static void #showchar(ch) #register int ch ; #{ # if (ch < 0x20) { # (void) putc('^', stderr) ; # ch |= 0x40 ; # } # # (void) putc(ch, stderr) ; # } # # # #/***** # * showstring - Show a string in an unambigous format. # */ # #static void #showstring(str) #register char *str ; #{ # while (*str) showchar(*str++) ; # } # # # #/***** # * dialerror - Handle communication error. # */ # #static void #dialerror() #{ # /* # * Flush the tty and make sure we aren't blocked # * by XOFF. # */ # # YIPE(ioctl(dialfd,TCFLSH,2)) ; # YIPE(ioctl(dialfd,TCXONC,1)) ; # # if (debug & D_LINE) { # dialmsg("Flushed device.\n") ; # } # } # # # #/****** # * setflags - Set termio flags on communication line. # */ # #static void #setflags() #{ # static struct termio tio ; # # tio.c_iflag = IGNPAR|IGNBRK|ISTRIP ; # tio.c_oflag = 0 ; # tio.c_cflag = cflags[carrier] ; # tio.c_lflag = NOFLSH ; # # tio.c_cc[VMIN] = 10 ; # tio.c_cc[VTIME] = 1 ; # # YIPE(ioctl(dialfd,TCSETA,&tio)) ; # # if (debug & D_LINE) dialmsg("Control flags now: %06o\n", tio.c_cflag) ; # } # # # #/***** # * dropdtr - Drop DTR for a "n" seconds. # */ # #void #dropdtr(n) #short n ; /* Number of seconds to hang up */ #{ # /* # * Hang up the line. # */ # # carrier = 0 ; # cflags[0] = 0 ; # setflags() ; # # /* # * Sleep for the specified interval. # */ # # if (debug & D_LINE) dialmsg("Dropping DTR for %d seconds\n", n) ; # # (void) sleep((unsigned) n) ; # # /* # * Raise DTR, and restore the baud rate. # */ # # cflags[0] = iflags[0] ; # cflags[1] = iflags[1] ; # cflags[2] = iflags[2] ; # cflags[3] = iflags[3] ; # # setflags() ; # # /* # * For some reason, this open appears to be necessary, # * at least on Plexus. # */ # # YIPE(close(open(dialdev, O_RDWR))) ; # } # # # #/**** # * setcarrier - Set carrier detect option. # * # * state 0 = set default c_cflags and CLOCAL. # * 1 = set user c_cflags and CLOCAL. # * 2 = set user c_cflags and call.modem CLOCAL option. # * 3 = use user c_cflags and ~CLOCAL. # */ # #static void #setcarrier(state) #int state ; /* Set carrier detect */ #{ # /* # * Enable or disable carrier sense. # */ # # if (state != carrier) { # carrier = state ; # # setflags() ; # # if (debug & D_LINE) dialmsg("Set connect option %d\n", state) ; # } # } # # # #/***** # * newbaud - Change baud rate. # */ # #static int #newbaud(baud) #int baud ; /* New baud rate */ #{ # register int i ; # # i = getbaud(baud) ; # if (i == -1) return(A_PROB) ; # # cflags[0] = (cflags[0] & ~(CSTOPB|CBAUD)) | i ; # cflags[1] = (cflags[1] & ~(CSTOPB|CBAUD)) | i ; # cflags[2] = (cflags[2] & ~(CSTOPB|CBAUD)) | i ; # cflags[3] = (cflags[3] & ~(CSTOPB|CBAUD)) | i ; # # setflags() ; # # if (debug & D_LINE) dialmsg("New baud rate: %d\n", baud) ; # return(0) ; # } # # # #/***** # * waitcarrier - Wait for carrier. # */ # #static int #waitcarrier(timeout) #short timeout ; /* Max time to wait */ #{ # register int fd ; # # if (debug & D_LINE) dialmsg("Waiting for carrier detect.\n") ; # # (void) alarm((unsigned)timeout) ; # # fd = open(dialdev, O_RDWR) ; # if (fd < 0) { # if (debug & D_LINE) dialmsg("%s: %s\n", errstring(), dialdev) ; # } # else { # (void) close(fd) ; # if (debug & D_LINE) dialmsg("Carrier detected.\n") ; # } # # (void) alarm(0) ; # # return(fd) ; # } # # # #/***** # * dialopen - Open communication line device. # */ # #static int #dialopen() #{ # /* # * Open the tty communication line device with O_NDELAY. # * Once the device is open, turn off O_NDELAY. # */ # # dialfd = open(dialdev, O_RDWR|O_NDELAY) ; # if (dialfd < 0) { # if (debug) dialmsg("%s: %s\n", errstring(), dialdev) ; # return(-1) ; # } # # if (debug & D_LINE) dialmsg("Opened comm line.\n") ; # # /* # * Turn off carrier sense. # */ # # carrier = -1 ; # setcarrier(0) ; # # /* # * For some reason, this open appears to be necessary, # * at least on Plexus. # */ # # YIPE(close(open(dialdev, O_RDWR))) ; # # /* # * Turn off NDELAY, flush I/O and unblock output. # */ # # YIPE(fcntl(dialfd, F_SETFL, O_RDWR)) ; # YIPE(ioctl(dialfd, TCFLSH, 2)) ; # YIPE(ioctl(dialfd, TCXONC, 1)) ; # # return(0) ; # } # # # #/***** # * dialbreak - Send break. # */ # #static void #dialbreak() #{ # YIPE(ioctl(dialfd,TCSBRK,0)) ; # # if (debug & (D_LINE|D_COMM)) dialmsg("Sent break signal.\n") ; # } # # # #/**** # * dialclose - Close communication line. # */ # #static void #dialclose() #{ # YIPE(ioctl(dialfd,TCFLSH,2)) ; # YIPE(close(dialfd)) ; # dialfd = -1 ; # # if (debug & D_LINE) { # dialmsg("Closed comm device.\n") ; # } # } # # # #/***** # * send - Send characters to the comm line. # */ # #static int #send(buf) #char *buf ; /* Buffer to send */ #{ # register short nsend ; # register short nbyte ; # # /* # * Flush I/O queues and make sure we are not blocked # * by an XOFF. # */ # # YIPE(ioctl(dialfd,TCFLSH,2)) ; # YIPE(ioctl(dialfd,TCXONC,1)) ; # # /* # * Setup an alarm to detect transmit failure and # * write the data. # */ # # gotalarm = 0 ; # (void) alarm(10) ; # # nsend = strlen(buf) ; # nbyte = write(dialfd, buf, (unsigned) nsend) ; # # (void) alarm(0) ; # # /* # * Debug print. # */ # # if (debug & D_COMM) { # dialmsg("Sent: ") ; # showstring(buf) ; # (void) putc('\n', stderr) ; # } # # /* # * Return status. # */ # # return (nbyte != nsend || gotalarm) ? -1 : 0 ; # } # # # #/**** # * receive - Receive data from the comm line until a # * recognized state, a hangup, or a timeout # * is detected. # */ # #static int #receive(nmatch, match, sec) #short nmatch ; /* Number of match items */ #MATCH *match ; /* Match string array */ #short sec ; /* Timeout */ #{ # register MATCH *mp ; # register char *s ; # register short ch ; # register short index ; # register short i ; # register short last ; # register short l ; # register short n ; # short state ; # char buf[4] ; # # state = DS_NONE ; # match += nmatch ; # # gotalarm = 0 ; # (void) alarm((unsigned)sec) ; # # /* # * Major loop - read data and scan for a state. # */ # # loop { # /* # * Read the next input character. This is done # * unbuffered deliberately, so we don't read # * ahead any data we want to leave in the buffer # * for the caller. # */ # # if (gotalarm == 0) { # n = read(dialfd, buf, 1) ; # if (n < 0 && (debug & D_LINE)) { # dialmsg("Read: %d, %s\n", n, errstring()) ; # } # } # # /* # * Handle timeout condition. # */ # # if (gotalarm) { # if (debug & (D_LINE|D_MATCH)) dialmsg("Receive timeout.\n") ; # dialerror() ; # state = DS_TIMEOUT ; # break ; # } # # /* # * Handle hangup condition. # */ # # if (n == 0) { # if (debug & (D_LINE|D_MATCH)) dialmsg("Lost carrier.\n") ; # dialerror() ; # state = DS_HANGUP ; # break ; # } # # /* # * Handle unexpected error and fail. # */ # # if (n != 1) { # dialmsg("Read returned %d, %s\n", n, errstring) ; # dialerror() ; # state = DS_FAIL ; # break ; # } # # /* # * Utterly ignore nulls and rubouts. # */ # # ch = buf[0] ; # if (ch == 0 || ch == 0x7f) continue ; # # /* # * Debug print. # */ # # if (debug & D_COMM) { # if (inline == 0) { # dialmsg("Got: ") ; # inline = 1 ; # } # showchar(ch) ; # if (ch == '\n') { # (void) putc('\n', stderr) ; # (void) fflush(stderr) ; # inline = 0 ; # } # } # # /* # * Match the received character against all # * the expected receive match strings. # * # * Each match item contains an "index" which # * keeps track of the number of characters matched # * so far. # * # * When all characters in a string are matched, # * we return the associated "state". # * # * When a character match fails, "index" is # * backed down accordingly. # */ # # n = nmatch ; # mp = match ; # # while (--n >= 0) { # --mp ; # index = mp->m_index ; # s = mp->m_string ; # # if (ch == s[index]) { # index++ ; # if (s[index] == 0) { # state = mp->m_state ; # break ; # } # } # # else if (index != 0) { # last = index ; # index-- ; # loop { # if (ch == s[index]) { # i = index ; # l = last ; # while (--i >= 0 && s[i] == s[--l]) ; # if (i < 0) break ; # } # if (--index < 0) break ; # } # index++ ; # } # # mp->m_index = index ; # } # # if (state != DS_NONE) { # if (debug & D_MATCH) { # dialmsg("Matched: ") ; # showstring(s) ; # (void) putc('\n', stderr) ; # (void) fflush(stderr) ; # } # break ; # } # } # # (void) alarm(0) ; # # if (inline) { # (void) putc('\n', stderr) ; # (void) fflush(stderr) ; # } # # return(state) ; # } # # # #/****** # * statestr - Generate state string representation. # */ # #static void #statestr(str, state) #char *str ; /* Destination string */ #int state ; /* State to display */ #{ # if (state == DS_FAIL) (void) strcpy(str, "-") ; # else if (state == DS_SUCCEED) (void) strcpy(str, "+") ; # else (void) sprintf(str, "%d", state) ; # } # # # #/****** # * expand - Expand string. # */ # #static void #expand(dest, source, di) #register char *dest ; /* Destination string */ #register char *source ; /* Source string */ #DINFO *di ; /* Dial info structure */ #{ # register char *cp ; # register char *rp ; # register short ch ; # register short c ; # register int i ; # # rp = 0 ; # # while (ch = *source++) { # # /* # * Get control character escapes. # */ # # if (ch == '^') ch = *source++ & 0x1f ; # # /* # * Handle escape characters. # */ # # else if (ch == '\\') { # # switch (ch = *source++) { # # /* # * Standard "C" escapes. # */ # # case 'f': # ch = '\f' ; # break ; # # case 'b': # ch = '\b' ; # break ; # # case 'n': # ch = '\n' ; # break ; # # case 'r': # ch = '\r' ; # break ; # # case 't': # ch = '\t' ; # break ; # ##ifndef lint # /* # * Hex character constant. # */ # # case '$': # ch = 0 ; # i = 2 ; # loop { # c = *source ; # if ('0' <= c && c <= '9') c -= '0' ; # else if ('A' <= c && c <= 'F') c -= 'A' - 10 ; # else if ('a' <= c && c <= 'f') c -= 'a' - 10 ; # else break ; # ch = 16 * ch + c ; # source++ ; # if (--i == 0) break ; # } # break ; ##endif # # /* # * Octal character constant. # */ # # case '0': # case '1': # case '2': # case '3': # case '4': # case '5': # case '6': # case '7': # ch = 0 ; # i = 3 ; # loop { # c = *source ; # if ('0' <= c && c <= '7') c -= '0' ; # else break ; # ch = 8 * ch + c ; # source++ ; # if (--i == 0) break ; # } # break ; # # default: # ch = *source++ ; # } # } # # /* # * Handle %x escapes. # */ # # else if (ch == '%') { # # switch(ch = *source++) { # # /* # * Columns 1-9 of L-devices. # */ # # case '1': # case '2': # case '3': # case '4': # case '5': # case '6': # case '7': # case '8': # case '9': # rp = ldevice[ch - '0'] ; # break ; # # /* # * Environment variable. # */ # # case '{': # ch = 0 ; # cp = source ; # rp = dest ; # loop { # if (*cp == 0) { # rp = "%{"; # break; # } # if (*cp == '}') { # *rp = 0; # rp = getenv(dest); # source = cp + 1; # break; # } # *rp++ = *cp++; # } # break; # # /* # * Untranslated phone number. # */ # # case 'n': # rp = dialnum ; # break; # # /* # * Translated telephone number. # */ # # case 'N': # cp = dialnum; # while (ch = *cp++) { # switch (ch) { # case '*': # case ':': # case 's': # rp = di->di_star ; # break ; # # case '#': # case ';': # case 'p': # rp = di->di_pound ; # break ; # # case '-': # case 'd': # rp = di->di_delay ; # break ; # # case '=': # case 'w': # rp = di->di_wait ; # break ; # # case 'f': # rp = di->di_flash ; # break ; # } # # if (rp != 0) { # while (*rp) *dest++ = *rp++ ; # rp = 0 ; # } # else *dest++ = ch ; # } # break; # # /* # * %% becomes %. # */ # # case '%': # break; # # /* # * Other %x escapes pass through untouched. # */ # # default: # *dest++ = '%'; # break; # } # } # # /* # * Output translate string, translated character, # * or original character untouched. # */ # # if (rp != 0) { # while (*rp) *dest++ = *rp++ ; # rp = 0; # } # # else if (ch != 0) *dest++ = ch ; # } # # *dest = 0 ; # } # # # ##if 0 #/****** # * sysname - Read L.sys file and look up phone number as # * system name. # */ # #static void #sysname(telno) #register char *telno ; #{ # register FILE *file ; # register short ch ; # register short i ; # char *word[5] ; # register char *bp ; # char buf[80] ; # static char fname[] = "/usr/lib/uucp/L.sys" ; # # file = fopen(fname, "r") ; # if (file == 0) { # if (errno != EACCES) dialmsg("%s: %s\n", errstring(), fname) ; # return ; # } # # /* # * Read all lines from the file. # */ # # for (;;) { # # /* # * Read a line from the file and break it out # * into 5 fields separated by white space. # */ # # ch = getc(file) ; # bp = buf ; # # for (i = 0 ; i < 5 ; i++) { # word[i] = bp ; # while (ch != ' ' && ch != '\t' && ch != '\n') { # if (ch == EOF) goto done ; # if (bp < buf+75) *bp++ = ch ; # ch = getc(file) ; # } # *bp++ = 0 ; # while (ch == ' ' || ch == '\t') ch = getc(file) ; # } # # while (ch != '\n') { # ch = getc(file) ; # if (ch == EOF) goto done ; # } # ##if 0 # (void) printf("L.sys: %s %s %s %s %s\n", # word[0], word[1], word[2], word[3], word[4] # ) ; ##endif # # /* # * If the system name matches, and not a direct # * entry, replace the phone number with the phone # * number for the system. # */ # # if ( strcmp(word[0], telno) == 0 # && strcmp(word[2], "DIR") != 0 # ) # { # (void) strcpy(telno, word[4]) ; # break ; # } # } # #done: # (void) fclose(file) ; # } ##endif # # # #/****** # * dialcodes - Read dialcodes file and fool with phone number. # */ # #static void #dialcodes(telno) #register char *telno ; #{ # register FILE *file ; # register char *cp ; # register int ch ; # register int n ; # char buf[100] ; # static char fname[] = "/usr/lib/uucp/L-dialcodes" ; # # /* # * Open the dialcodes file. # */ # # file = fopen(fname, "r") ; # if (file == 0) { # dialmsg("%s: %s\n", errstring(), fname) ; # return ; # } # # /* # * Read all lines of the file until a match # * or end-of-file is reached. # */ # # loop { # # /* # * Search for a space-terminated code in the file # * which matches the phone number prefix. # * # * When a match is found, replace the match prefix # * with the remainder of the line. # */ # # cp = telno ; # loop { # ch = getc(file) ; # if (ch == ' ') { # (void) strncpy(buf, cp, sizeof(buf)) ; # n = sizeof(buf) - 1 ; # loop { # ch = getc(file) ; # if (ch == EOF || ch == '\n' || --n < 0) break ; # *telno++ = ch ; # } # cp = buf ; # while (*cp && --n >= 0) *telno++ = *cp++ ; # *telno = 0 ; # goto done ; # } # if (ch != *cp++) break ; # } # # /* # * Skip to end-of-line. # */ # # while (ch != '\n') { # if (ch == EOF) goto done ; # ch = getc(file) ; # } # } # #done: # (void) fclose(file) ; # } # # # #static int #dostate(state, di, ds, depth) #int state ; /* State number */ #register DINFO *di ; /* Dialer information */ #register DSTATE *ds ; /* State variables */ #int depth ; /* Use state depth */ #{ # register char *cp ; # register char *bp ; # register MATCH *mp ; # char *errp ; # register short ch ; # short i ; # # cp = di->di_state[state] ; # # if (cp == 0) { # dialmsg("Transferred to undefined state: %d\n", state) ; # return (A_PROB) ; # } # # if (depth > 10) { # dialmsg("Use command depth overflow, state: %d\n", state) ; # return(A_PROB) ; # } # # if (debug & D_STATE) { # dialmsg("using: s%d=%s\n", state, cp) ; # } # # /* # * Loop over all commands in string. # */ # # loop { # # /* # * Skip over white space. # */ # # ch = *cp++ ; # while (isspace(ch)) ch = *cp++ ; # # if (ch == 0) return(DS_NONE) ; # # errp = cp - 1 ; # # /* # * Parse next command in state string. # */ # # if (islower(ch)) ch = toupper(ch) ; # # switch (ch) { # # /* # * Parse Break. # */ # # case 'B': # break ; # # /* # * Error messages and modem string. # */ # # case 'E': # case 'M': # case 'F': # if (*cp++ != '"') goto error ; # bp = ds->ds_buf ; # loop { # if (*cp == '"') break ; # if (*cp == 0) goto error ; # if (cp[0] == '\\' && cp[1] == ']') cp++ ; # *bp++ = *cp++ ; # } # *bp++ = 0 ; # cp++ ; # break ; # # /* # * Carrier sense. # */ # # case 'C': # cp = parseshort(cp, &i, 0, 4) ; # break ; # # /* # * Retry decrement. # */ # # case 'R': # cp = parseshort(cp, &i, 1, 1000) ; # break ; # # /* # * Baud rate. # */ # # case 'N': # cp = parseshort(cp, &i, 50, 30000) ; # break ; # # /* # * Delay time, Pause time, and Seconds of timeout. # */ # # case 'D': # case 'P': # case 'S': # cp = parseshort(cp, &i, 1, 120) ; # break ; # # /* # * Pattern match. # */ # # case '[': # bp = ds->ds_buf + 50 ; # loop { # if (*cp == ']') break ; # if (*cp == 0) goto error ; # if (cp[0] == '\\' && cp[1] == ']') cp++ ; # *bp++ = *cp++ ; # } # *bp = 0 ; # cp++ ; # # /* # * Goto, Hangup, and Timeout states. # */ # # case 'G': # case 'H': # case 'T': # if (*cp == '+') { # cp++ ; # i = DS_SUCCEED ; # break ; # } # if (*cp == '-') { # cp++ ; # i = DS_FAIL ; # break ; # } # # /* # * Use other state. # */ # # case 'U': # cp = parseshort(cp, &i, 0, NSTATE-1) ; # break ; # # default: # cp = 0 ; # } # # /* # * Handle parsing error. # */ # # if (cp == 0) goto error ; # # /* # * Action parsed okay, now execute it. # */ # # switch (ch) { # # /* # * Send a break signal to the remote. # */ # # case 'B': # dialbreak() ; # break ; # # /* # * Change carrier sense. # */ # # case 'C': # if (i <= 3) setcarrier(i) ; # else { # setcarrier(3) ; # i = waitcarrier(ds->ds_sec) ; # if (i < 0) return(DS_TIMEOUT) ; # } # break ; # # /* # * Drop DTR time. # */ # # case 'D': # dropdtr(i) ; # break ; # # /* # * String to standard error. # */ # # case 'E': # expand(bp, ds->ds_buf, di) ; # dialmsg("%s\n", bp) ; # break ; # # /* # * Fail message string. # */ # # case 'F': # expand(ds->ds_failmsg, ds->ds_buf, di) ; # break ; # # /* # * Unconditional goto. # */ # # case 'G': # return(i) ; # # /* # * Hangup state. # */ # # case 'H': # ds->ds_hangup = i ; # break ; # # /* # * Send control string to modem. # */ # # case 'M': # expand(bp, ds->ds_buf, di) ; # if (send(bp) < 0) return(DS_TIMEOUT) ; # break ; # # /* # * Adopt new baud rate. # */ # # case 'N': # if (newbaud(i) < 0) goto error ; # break ; # # /* # * Pause for specified interval. # */ # # case 'P': # if (debug & D_LINE) # dialmsg("Pause for %d seconds\n", i) ; # (void) sleep((unsigned) i) ; # break ; # # /* # * Decrement retry count. If it goes negative, # * return failure. # */ # # case 'R': # ds->ds_retry -= i ; # if (debug & D_STATE) # dialmsg("Decrement retry by %d to %d\n", i, ds->ds_retry) ; # if (ds->ds_retry < 0) return(DS_FAIL) ; # break ; # # /* # * Seconds of timeout. # */ # # case 'S': # ds->ds_sec = i ; # break ; # # /* # * Timeout state. # */ # # case 'T': # ds->ds_timeout = i ; # break ; # # /* # * Use another state definition string. # */ # # case 'U': # i = dostate(i, di, ds, depth+1) ; # if (i != DS_NONE) return(i) ; # break ; # # /* # * Setup match strings. # */ # # case '[': # expand(ds->ds_buf, ds->ds_buf+50, di) ; # if (ds->ds_nmatch >= NMATCH) goto error ; # mp = &ds->ds_match[ds->ds_nmatch++] ; # mp->m_string = ds->ds_buf ; # mp->m_index = 0 ; # mp->m_state = i ; # ds->ds_buf += strlen(ds->ds_buf) + 1 ; # if (ds->ds_buf >= &ds->ds_buffer[NBUF-100]) goto error ; # break ; # # default: # goto error ; # } # } # # /* # * Print diagnostic error. # */ # #error: # dialmsg("Problem in dialer %s, state %d, field %s\n", # ldevice[2], state, errp) ; # return(A_PROB) ; # } # # # #/****** # * dialstate - Dialer state machine. # * # * Returns: open file descriptor >= 0 for success. # * error code < 0 for failure. # */ # #static int #dialstate(di) #DINFO *di ; /* Dialer info structure */ #{ # register short state ; # DSTATE ds ; # register int i ; # register MATCH *mp ; # char str1[10], str2[10] ; # # /* # * Initialize state machine. # */ # # ds.ds_retry = (di->di_retry > 0) ? di->di_retry : 1 ; # ds.ds_sec = 30 ; # ds.ds_timeout = DS_FAIL ; # ds.ds_hangup = DS_FAIL ; # ds.ds_failmsg[0] = 0 ; # # cflags[0] = iflags[0] ; # cflags[1] = iflags[1] ; # cflags[2] = iflags[2] ; # cflags[3] = iflags[3] ; # # /* # * Begin in state 0 with the communication line open. # */ # # state = 0 ; # if (dialopen() < 0) return(L_PROB) ; # # /* # * Process dial states until success or failure. # */ # # loop { # # if (debug & D_STATE) { # dialmsg("Entering state: %d\n", state) ; # } # # /* # * Initialize the match list. # */ # # ds.ds_nmatch = 0 ; # ds.ds_buf = &ds.ds_buffer[0] ; # # /* # * Execute the specified state. # */ # # state = dostate(state, di, &ds, 1) ; # # /* # * Do the receive with the user-specified # * timeout value. # */ # # if (state == DS_NONE) { # if (debug & D_MATCH) { # statestr(str1, ds.ds_hangup) ; # statestr(str2, ds.ds_timeout) ; # dialmsg("Receive parameters: C%d H%s S%d T%s\n", # carrier, str1, ds.ds_sec, str2 # ) ; # dialmsg("Patterns:") ; # i = ds.ds_nmatch ; # mp = ds.ds_match ; # while (--i >= 0) { # statestr(str1, mp->m_state) ; # (void) fprintf(stderr, " [%s]%s", mp->m_string, str1) ; # mp++ ; # } # (void) fprintf(stderr, "\n") ; # } # state = receive(ds.ds_nmatch, ds.ds_match, ds.ds_sec) ; # } # # /* # * Determine next state. # */ # # if (state == DS_TIMEOUT) { # state = ds.ds_timeout ; # } # else if (state == DS_HANGUP) { # state = ds.ds_hangup ; # } # # if (state == DS_NONE) state = 0 ; # # if (state == DS_SUCCEED) return(dialfd) ; # # if ((unsigned) state >= NSTATE) { # if (*ds.ds_failmsg) dialmsg("%s\n", ds.ds_failmsg) ; # dialclose() ; # return(state >= 0 ? NO_ANS : state) ; # } # } # } # # # #/****** # * dial - System V dial(3). # */ # #int #dial(call) #CALL call ; /* See dial(3) */ #{ # register char *cp ; # register char *dp ; # register int i ; # register int n ; # register int ch ; # register FILE *file ; # int lockfd ; # int gotint ; # int gotdinfo ; # int rtn ; # int num ; # int speed ; # unsigned alarmval ; # int (*alarmproc)() ; # int found ; # int (*save)() ; # int (*sigsave[4])() ; # struct termio attr ; # DINFO dinfo ; # # static short signum[4] = { SIGHUP, SIGINT, SIGQUIT, SIGTERM } ; # # /* # * Setup debug print options. # */ # # cp = getenv("DIALDEBUG") ; # if (cp) { # while (ch = *cp++) { # for (dp = dtable ; *dp ; dp += 2) { # if (dp[0] == ch) debug |= dp[1] ; # } # } # } # # /* # * Debug print call parameters. # */ # # if (debug) { # dialmsg("Dial called with:\n") ; # dialmsg("\tbaud=%d, speed=%d, line=%s, telno=%s\n", # call.baud, call.speed, NS(call.line), NS(call.telno)) ; # dialmsg("\tmodem=%d, device=%s, devlen=%d\n", # call.modem, NS(call.device), call.dev_len) ; # } # # /* # * Remove any pathname stuff from the beginning of # * the line device name. # */ # # if (call.line != 0) { # cp = call.line ; # while (*cp) if (*cp++ == '/') call.line = cp ; # } # # /* # * Setup to catch interrupts. # */ # # lockfd = -1 ; # dialfd = -1 ; # file = 0 ; # gotdinfo = 0 ; # # alarmval = alarm(0) ; # if ((alarmproc = signal(SIGALRM, SIG_IGN)) != SIG_IGN) # (void) signal(SIGALRM, wakeup) ; # # if (gotint = setjmp(jmpstate)) { # rtn = INTRPT ; # goto done ; # } # # for (i = 0 ; i < 4 ; i++) # if ((sigsave[i] = signal(signum[i], SIG_IGN)) != SIG_IGN) # (void) signal(signum[i], catch) ; # # /* # * Validate baud rates. # */ # # if (call.speed <= 0) call.speed = call.baud ; # if (call.baud <= 0) call.baud = call.speed ; # # if ( (call.speed > 0 && getbaud(call.speed) == -1) # || (call.baud > 0 && getbaud(call.baud) == -1) # ) # { # rtn = ILL_BD ; # goto done ; # } # # /* # * Open and read L-devices file. # */ # # file = fopen("/usr/lib/uucp/L-devices", "r") ; # if (file == 0) { # rtn = NO_Ldv ; # goto done ; # } # # /* # * Traverse the L-devices file. # */ # # found = 0 ; # # loop { # # /* # * Get the first 9 fields in each line. # */ # # ch = getc(file) ; # n = sizeof(ldbuf) - 9 ; # cp = ldbuf ; # # for (i = 0 ; i < 9 ; i++) { # ldevice[i] = cp ; # # while (ch == ' ' || ch == '\t') ch = getc(file) ; # # while (ch != EOF && ch != ' ' && ch != '\t' && ch != '\n') { # if (--n > 0) *cp++ = ch ; # ch = getc(file) ; # } # # *cp++ = 0 ; # n++ ; # } # # while (ch != EOF && ch != '\n') ch = getc(file) ; # # /* # * If we have read to EOF, and still haven't found # * a compatible device, exit with an error. # */ # # if (ch == EOF) { # if (debug) dialmsg("No entry found in L-devices.\n") ; # rtn = # ( (call.line == 0 || *call.line == 0) # ? (found ? NO_BD_A : NO_BD_K) # : (found ? DV_NT_A : DV_NT_K) # ) ; # goto done ; # } # # /* # * Check for compatibility between the user's # * request, and the current entry. # */ # # speed = atoi(ldevice[3]) ; # # if ( ( (call.telno == 0 || *call.telno == 0) # ? strcmp(ldevice[0], "DIR") == 0 # : strcmp(ldevice[0], "ACU") == 0 # ) # && ( call.line == 0 # || *call.line == 0 # || strcmp(ldevice[1], call.line) == 0 # ) # && (call.speed <= 0 || speed == call.speed) # ) # { # /* # * This looks like a compatible entry. # */ # # found++ ; # # if (debug & D_DEVICE) { # dialmsg("Using L-device: %s %s %s %s\n", # ldevice[0], ldevice[1], ldevice[2], ldevice[3]) ; # } # # /* # * Attempt to create the lock file. If we # * can't, advance to the next entry. # */ # # (void) strcpy(diallock, "/usr/spool/uucp/LCK..") ; # (void) strncat(diallock, ldevice[1], 20) ; # # if (nolock == 0) { # lockfd = open(diallock, O_WRONLY|O_CREAT|O_EXCL, 0444) ; # if (lockfd < 0) { # if (debug & D_DEVICE) { # dialmsg("%s: %s\n", errstring(), diallock) ; # } # continue ; # } # # (void) close(lockfd) ; # # if (debug & (D_LINE|D_DEVICE)) # dialmsg("Created lock file: %s\n", diallock) ; # } # # /* # * Get dialer characteristics from dialinfo file. # */ # # gotdinfo = 1 ; # if (dialinfo(&dinfo, ldevice[2]) < 0) { # rtn = A_PROB ; # goto done ; # } # # /* # * Figure out what the termio(7) c_cflags # * byte should contain based on the user's # * requests. # */ # # iflags[0] = getbaud(speed) ; # if (iflags[0] == -1) { # rtn = ILL_BD ; # goto done ; # } # # iflags[0] |= CS8 | CREAD | CLOCAL | HUPCL ; # # if (call.attr != 0) { # iflags[1] = call.attr->c_cflag | CREAD | CLOCAL | HUPCL ; # if ((iflags[1] & CBAUD) == 0) { # iflags[1] |= iflags[0] & (CBAUD|CSTOPB) ; # } # if (call.baud > 0) { # iflags[1] &= ~CBAUD ; # iflags[1] |= getbaud(call.baud) ; # } # if ((iflags[1] & CSIZE) == 0) { # iflags[1] |= iflags[0] & CSIZE ; # } # } # else { # iflags[1] = iflags[0] ; # } # # iflags[3] = iflags[1] & ~CLOCAL ; # # iflags[2] = ((call.telno && *call.telno) || call.modem) # ? iflags[3] : iflags[1] ; # # /* # * Attempt dial out on all specified phone # * numbers. # * # * The "telno" string is a list of phone numbers # * separated by commas. Run through the list # * until we get a connect or we get an error # * more serious than "no answer". # */ # # (void) strcpy(dialdev, "/dev/") ; # (void) strcat(dialdev, ldevice[1]) ; # # if (call.telno == 0) { # dialnum[0] = 0 ; # rtn = dialstate(&dinfo) ; # } # else { # cp = call.telno ; # loop { # dp = dialnum ; # i = sizeof(dialnum) ; # while (*cp != 0 && *cp != ',' && --i > 0) # *dp++ = *cp++ ; # *dp = 0 ; ##if 0 # sysname(dialnum) ; ##endif # dialcodes(dialnum) ; # # if (debug & D_DEVICE) dialmsg("Telno: %s\n", dialnum) ; # # rtn = dialstate(&dinfo) ; # if (rtn != NO_ANS || *cp == 0) break ; # cp++ ; # } # } # # if (rtn < 0) goto done ; # # /* # * Set the user's termio options. # */ # # if (call.attr != 0) { # attr = *call.attr ; # attr.c_cflag = cflags[carrier] ; # YIPE(ioctl(dialfd, TCSETA, &attr)) ; # } # goto done ; # } # } # # /* # * Universal return point. Remove the lock file, # * and restore all the caller's signals. If we # * got an interrupt apparently for the user, either # * ignore it, call his processing routine, or exit # * depending on his original options. # */ # #done: # if (gotdinfo) dialfree(&dinfo) ; # # if (file != 0) (void) fclose(file) ; # # if (rtn < 0) { # if (lockfd >= 0 && nolock == 0) { # if (unlink(diallock) < 0) # dialmsg("Could not unlink %s: %s\n", diallock, errstring()) ; # else if (debug & (D_LINE|D_DEVICE)) # dialmsg("Removed lock file: %s\n", diallock) ; # } # if (dialfd >= 0) dialclose() ; # } # else if (call.device != 0 && call.dev_len > 0) # (void) strncpy(call.device, dialdev, call.dev_len) ; # # for (i = 0 ; i < 4 ; i++) { # num = signum[i] ; # save = sigsave[i] ; # if (save != SIG_IGN) { # (void) signal(num, save) ; # if (num == gotint) { # if (save == SIG_DFL) exit(1) ; # else (*save)(gotint) ; # } # } # } # # (void) signal(SIGALRM, alarmproc) ; # (void) alarm((unsigned)alarmval) ; # # if (debug) dialmsg("Dial returned: %d\n", rtn) ; # # return(rtn) ; # } # # # #/***** # * undial - Clean up after successful dial. # */ # #void #undial(fd) #register int fd ; /* File descriptor to close */ #{ # register char *tty ; # register char *cp ; # char buf[50] ; # # /* # * Get the file descriptor tty name and # * convert it to its uucp lockfile name. # */ # # tty = ttyname(fd) ; # # if (tty == 0) { # dialmsg("Undial fd=%d: not a tty\n", fd) ; # return ; # } # # cp = tty ; # while (*cp) { # if (*cp++ == '/') tty = cp ; # } # # (void) strcpy(buf, "/usr/spool/uucp/LCK..") ; # (void) strcat(buf, tty) ; # # if (!nolock && unlink(buf) < 0) # dialmsg("%s: %s", buf, errstring()) ; # # /* # * Close the file. # */ # # if (close(fd) < 0) # dialmsg("Undial fd=%d: %s\n", fd, errstring()) ; # # if (debug & D_LINE) # dialmsg("Undial complete: fd=%d, %s\n", fd, tty) ; # } end_dial.c