Subject: v13i059: Hardware-independant modem routines Newsgroups: comp.sources.unix Sender: sources Approved: rsalz@uunet.UU.NET Submitted-by: The Beach Bum Posting-number: Volume 13, Issue 59 Archive-name: modemcap This is a modem-independant dial(3) package, with a termcap-style description file. No manual page -- refer to the relevant (SystemV) manuals. #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # README # makefile # modemcap # modemtype # modemcap.h # mgetent.c # mgetstr.c # mgetflag.c # mgetnum.c # mdial.c # merror.c # initmodem.c # hangup.c # dial.c # call.c # This archive created: Mon Nov 2 13:32:08 1987 export PATH; PATH=/bin:/usr/bin:$PATH if test -f 'README' then echo shar: "will not over-write existing file 'README'" else cat << \SHAR_EOF > 'README' This package is a modem independent dial(3) package. It provides a device independent method for dialing and manipulating modems. The format of the description file is the ever present ;-) termcaps format. You may want to read the modemcap file to see just what capabilities are present. This is Release 1.1 of this software. Please bear in mind that I haven't released this to the public before this release. --< Start of the legal conditions of this software >-- This software is copyright 1987 John F. Haugh II, all rights reserved. Use, duplication and disclosure subject to the terms and conditions of the license agreement below. This notice is intended to have legal significance, and not to be taken lightly. If you have any questions, please contact the author at the address below. This copyright covers the entire software distribution it is enclosed with. Removing this copyright is a violation of federal copyright law. Please consult an attorney if you have any questions regarding the legal ramifications of this agreement. This software is licensed subject to the following terms and conditions. In order that this product may expand into the universe to fill the current (void) in this area, it is my intention that this software be widely distributed, and maintained in a consistent fashion. To this end, you are authorized to redistribute this software in either source or binary format, provided that, this agreement is retained as part of the release, no direct profit is realized from the sale or transfer of this software, and that credit is given the author for this work. To facilitate this package coming into acceptance, you must provide library versions of this software and any documentation included with this package when distributing binary versions embedded in your products. Paying royalties would be nice and you won't do it anyway, so don't even pretend to be nice people unless you really want me to stay on top of this thing. Author retains all rights to derived works, and as a condition to your making modifications to the source code, you are required by this agreement to provide detailed notes concerning the actual modifications and the motivation behind the modification itself. Making the documentation look better is a nice idea also. And since I'm very lazy, please send me your documentation. You don't have to, but standardized documentation is a Good Thing. The author provides this software without warrantee. The user accepts by use all responsibility for the performance (or lack thereof ;-) of this software, including loss of profits, reputation, or job. --< End of the legal stuff, now for the documentation. >-- call.c - a test program. do a `make all' to create the library and compile the call program. read all the documentation before trying it out. dial(3L) - a version of the standard dial(3) command which should be fairly compatible with the one in your manual ... has both dial() and undial() routines. see your manpage for more details. hangup.c/hangup(3L) - a routine that attempts to absolutely hangup a modem. it uses the modem capabilities database to figure out the best way to hangup a modem. it supports both hangup on DTR and hangup on command. initmodem.c/initmodem(3L) - this routine takes a character pointer to a modem name as it appears in the database and a file descriptor from a open() call, and loads the database information. after the database is loaded, the modem is placed in the command mode. beware, initmodem() may make an alarm(2) call if the InitializationDelay requirement is present. mdial.c/mdial(3L) - this routine builds a dial command string to give to the dialer. this is the weakest routine in the package, because i don't have that many different modems around here, and hayes is pretty damned common, so i don't see many others. the first argument is a character string telephone number, digits only, or if you understand your modem (boo on me) you can put in pause commands. the second argument is a file descriptor from an open() call. this routine _will_ be the first to get fixed. merror.c/merror(3L) - this routine is a modemcap replacement for perror. an `int merrno' variable is maintained by the routines. after an error return, you can call merror with a character pointer just like perror(3) and have a message printed on your standard error output. mgetent.c/mgetent(3L) - this is a low level routine you shouldn't be calling anyway. it works like tgetent(3), only different. mostly, it doesn't have the neat things. the first argument is a pointer to a character buffer where the entry will be placed. the second argument is a pointer to the modem name. mgetflag.c/mgetflag(3L) - just like tgetflag. this is a low level routine you might just want to use. in particular, `if (mgetflag ("hc"))' tests for modem hanging up on DTR being negated. `if (HC)' does the same thing after mgetent() ... mgetnum.c/mgetnum(3L) - just like tgetnum. don't see much use for using it. mgetstr.c/mgetstr(3L) - just like tgetstr, except it has a few quirks. octal escapes are all three digits. the format '^c' where 'c' is some character, only works with upper case letters. correctly at least. ain't no way to get a null into the string, no how. /etc/modemcap - a modem capabilities database. read the file for more details. /etc/modemtype - a modem/port mapping table. look at the example file. you will need to make entries for your devices in the L-devices file. the speed listed must be a legal speed according to the modemcap file. any compilation errors or warnings should be brought to my attention. i didn't get any the last time i compiled all this stuff. the source is well enough (i'm lieing) documented for anyone to fix. please don't add your local improvement. if you insist, make the mods and send me context diffs. i'll tell you what i think about the change. remember, according to the license you must let me in on what you are doing. this is in everyones best interest. --< End of the documentation, now for my personal philosophical b.s. >-- It is not my intention to limit the use of this software or your profits, except where such use is inconsistent with the spirit of humanity, whatever the hell that means. I don't care how much money you make selling your new terminal program, just don't go jacking the price up because you have added this thing. I will only work on this package if I get feedback and I have some motivation, other than being a nice guy. I think I've been nice enough by putting this thing out there. If what you want makes sense in a real way, I will probably include your suggestions, and if you are polite, I might just give you credit ;-) So don't be a jerk and pretend that anyone actually works for free. I have a car note just like you. By way of this license and the legal mumbo-jumbo, I hope to keep people from ripping me off, and totally trashing and perverting the integrity of the code. Please, don't construe this agreement to be limiting in a negative way. I hope to provide just enough limits to keep the code consistent and portable across all machine environments, and hopefully, you will use this thing enough that it becomes better and more useful and fills the need for such a thing. Who knows, we both might just make some money off of this thing. -- John F. Haugh II QUOTE: "The important thing is to not stop 7825 McCallum Blvd. questioning" -- Albert Einstein Apt. 510 TELCO: (214) 250-3311 Dallas, TX 75252 UUCP: { backbone } !ihnp4!killer!jfh SHAR_EOF fi if test -f 'makefile' then echo shar: "will not over-write existing file 'makefile'" else cat << \SHAR_EOF > 'makefile' # Your library directory. LIBDIR=/usr/lib # Your local command directory. LBIN=/usr/local/bin OSFLAG=-DUNIX_S5 # For System V machines. # OSFLAG=-DUNIX_V7 # For Version 7 machines. # For those poor people who need ranlib # RANLIB=ranlib $(LIBDIR)/libmodemcap.a # Standard Bourne shell. SHELL=/bin/sh CFLAGS=-O LDFLAGS=-s OFILES=mgetent.o mgetstr.o mgetflag.o mgetnum.o mdial.o merror.o \ initmodem.o hangup.o dial.o CFILES=mgetent.c mgetstr.c mgetflag.c mgetnum.c mdial.c merror.c \ initmodem.c hangup.c dial.c LFILES=libmodemcap.a(mgetent.o)\ libmodemcap.a(mgetstr.o)\ libmodemcap.a(mgetflag.o)\ libmodemcap.a(mgetnum.o)\ libmodemcap.a(mdial.o)\ libmodemcap.a(merror.o)\ libmodemcap.a(initmodem.o)\ libmodemcap.a(hangup.o)\ libmodemcap.a(dial.o) all: $(LFILES) install: all call cp modemcap.h /usr/include cp modemcap /etc/modemcap cp modemtype /etc/modemtype cp libmodemcap.a $(LIBDIR) chmod 644 /usr/include/modemcap.h /etc/modemcap /etc/modemtype $(LIBDIR)/libmodemcap.a cp call $(LBIN)/call chmod 711 $(LBIN)/call call: call.c libmodemcap.a cc $(LDFLAGS) $(CFLAGS) call.c libmodemcap.a -o call initmodem.o: initmodem.c /usr/include/modemcap.h mdial.o: mdial.c /usr/include/modemcap.h hangup.o: hangup.c /usr/include/modemcap.h dial.o: dial.c /usr/include/modemcap.h shar: README $(CFILES) makefile modemcap modemtype modemcap.h call.c shar README makefile modemcap modemtype modemcap.h $(CFILES) call.c > modem.shar SHAR_EOF fi if test -f 'modemcap' then echo shar: "will not over-write existing file 'modemcap'" else cat << \SHAR_EOF > 'modemcap' # # @(#)modemcap 1.0 # # First attempt at a modem capabilities database. # # Capabilities are: # # Name Type Meaning # # as flag Numbers are in ASCII, not binary # at string Attention string, forces model into command mode from online mode # ad number Delay after AS # bd number Highest online baud rate # bl number Alternate lower baud rate # cs string Command start string # ce string Command end string (required if CS is present) # co string String from modem on remote connection at BD baud rate # cl string String from modem on remote connection at BL baud rate # di flag Modem has a dialer # ds string Start dial command string # de string End dial command string (required if DS is present) # is string Initialization string, resets modem to offline, ready to dial # id number Delay after IS # hc flag Modem hangs up when DTR drops # hu string Hangup command # tt flag Modem dials touchtone by default (or DS is set that way) # # All commands, such as DS (dial command) and HU (hang up) will be prefixed by # CS and ended with CE. If there is a common prefix and suffix, use this feature. # Otherwise, each command will have to have the entire string built in. # hy|hayes|Hayes Smartmodem 1200:\ :as:at=+++:ad#6:bd#1200:bl#300:cs=AT:ce=\r:co=CONNECT:\ :cl=CONNECT:di:ds=DT :de=:is=ATQ0 V1 E1\r:id#2:\ :hc:hu=H0 V0 E0 Q1:tt: si|mk12|Signalman Mark XII:\ :as:at=+++:ad#6:bd#1200:bl#300:cs=AT:ce=\r:co=CONNECT 1200:\ :cl=CONNECT:di:ds=DT :de=:is=ATQ0 V1 E1\r:id#2:\ :hu=H0 V0 E0 Q1:tt: ds|dc300|Radio Shack Direct-Connect 300 Modem:\ :bd#300:bl#110: SHAR_EOF fi if test -f 'modemtype' then echo shar: "will not over-write existing file 'modemtype'" else cat << \SHAR_EOF > 'modemtype' hayes1200 tty38 hayes1200 tty39 ch1770 modem SHAR_EOF fi if test -f 'modemcap.h' then echo shar: "will not over-write existing file 'modemcap.h'" else cat << \SHAR_EOF > 'modemcap.h' /* * @(#)modemcap.h 1.0 * * names of variables and whatnots for modemcap file */ char AS, /* True if numbers dialed in ASCII, False for binary digits */ DI, /* True if modem can dial numbers, False otherwise */ HC, /* True if modem hangs up when DTR drops, False otherwise */ TT; /* True if modem uses touchtone by default, False for pulse */ char *AT, /* Enter command state when online */ *CS, /* Command start string */ *CE, /* Command end string - must be present if CS is */ *DS, /* Dial command string */ *DE, /* End of dial command string - must be present if DS is */ *CO, /* Connection made at primary baud rate */ *CL, /* Connection made at secondary (lower) baud rate */ *IS, /* Initialization string - reset modem to onhook and ready */ *HU; /* Hangup command */ int AD, /* Delay after AT string before next command */ BD, /* Highest communications baud rate */ BL, /* Another, lower baud rate */ ID; /* Delay time after initialization */ /* * The dial command is the principle string that must be built. * The routines will build a dial command as follows: * * * * Note that the DE and CE strings are present ALWAYS. * This procedure will be used to dial phone numbers if the DI flag is true. * If this isn't the way to dial numbers, * DO NOT SET DI IN THE MODEMCAP FILE!!! */ /* * The type of modem is determined by reading the file "_MODEMTYPE_". This * is similiar to the way curses works by reading the ttytype file. */ #define _MODEMTYPE_ "/etc/modemtype" /* * a hangup command will be performed as follows: * * 1). any attention string (AT) will be sent followed by the delay (AD) * 2). modem should now be in command state, send hangup (HU) command * 3). send initialization string (IS) followed by the delay (ID) * * It is important that you determine a correct AT and HU string * to perform this function. * If the modem hangs up when DTR falls (even if there is a HU string), * declare the flag HC (for Hangup on Close). */ SHAR_EOF fi if test -f 'mgetent.c' then echo shar: "will not over-write existing file 'mgetent.c'" else cat << \SHAR_EOF > 'mgetent.c' #include char *__modemcap; char *MODEMCAP = "/etc/modemcap"; static isent (ent, name) char *ent; char *name; { char buf[16]; register int i; while (*ent != ':' && *ent != 0) { for (i = 0;*ent != ':' && *ent != '|' && *ent != 0 && i < 15;i++) buf[i] = *ent++; if (*ent == '|') ent++; buf[i] = 0; if (strcmp (buf, name) == 0) return (1); } return (0); } mgetent (bp, name) char *bp; char *name; { char buf[1024]; register char *cp; register FILE *modemcap; register int i; char *getenv (); if ((cp = getenv ("MODEMCAP")) != NULL) { if (*cp != '/') { if (isent (cp, name)) { strcpy (buf, cp); return (1); } } MODEMCAP = cp; } if ((modemcap = fopen (MODEMCAP, "r")) == NULL) return (-1); while (fgets (buf, 512, modemcap) != NULL) { if (buf[0] == '#') /* skip all comment lines */ continue; i = strlen (buf) - 1; /* find last character in line */ buf[i] = 0; /* remove trailing newline */ if (i == 0) /* ignore blank lines */ continue; while (buf[(i = strlen (buf) - 1)] == '\\') { /* is last character a \\, still more */ cp = &buf[i]; /* find last character */ cp[0] = 0; /* nullify, end of this part */ if (fgets (cp, 512, modemcap) == NULL) /* end of file? ... */ break; /* ... end of entry */ cp[strlen (cp) - 1] = 0; /* remove trailing newline */ if (cp[0] == '#') { /* comment line? ... */ cp[0] = 0; /* remove that line */ continue; /* go get another line */ } } if (isent (buf, name)) { __modemcap = bp; strcpy (bp, buf); fclose (modemcap); return (1); } } fclose (modemcap); return (0); } SHAR_EOF fi if test -f 'mgetstr.c' then echo shar: "will not over-write existing file 'mgetstr.c'" else cat << \SHAR_EOF > 'mgetstr.c' extern char *__modemcap; char *mgetstr (id, area) register char *id; register char **area; { register char *cp = __modemcap; register char *str = *area; /* start of current string */ if (__modemcap == (char *) 0) /* has mgetent() been called? ... */ return ((char *) 0); /* ... no, can't find string */ while (*cp != ':' && *cp != 0) /* find first entry in cap */ cp++; if (*cp == 0) /* empty entry??? */ return ((char *) 0); /* ... yes, bad modemcap entry */ else cp++; /* point to first character in next */ while (*cp != 0) { /* until entry found or end of entry */ if (cp[0] == id[0] && cp[1] == id[1]) { /* found entry!!! */ if (cp[2] != '=') /* is it a string value??? */ return ((char *) 0); /* no, something else */ else break; /* yes, entry was found */ } else { /* not entry, skip this entire entry */ while (*cp != ':' && *cp != 0) cp++; /* search for end of current entry */ if (*cp != 0) cp++; /* skip terminating character */ } } if (*cp == 0) /* end of modem cap entry */ return ((char *) 0); cp += 3; /* point to actual string */ while (*cp != ':' && *cp != 0) { /* for every character in string ... */ if (*cp == '\\') { /* translate escaped character */ cp++; switch (*cp) { case 'n': /* newline */ **area = '\n'; (*area)++; cp++; break; case 'r': /* carriage return */ **area = '\r'; (*area)++; cp++; break; case 'b': /* backspace */ **area = '\b'; (*area)++; cp++; break; case 'f': /* form feed */ **area = '\f'; (*area)++; cp++; break; case 't': /* tab */ **area = '\t'; (*area)++; cp++; break; case 'E': /* Escape character */ **area = 033; (*area)++; cp++; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': **area = ((cp[0] - '0') << 6) + ((cp[1] - '0') << 3) + (cp[2] - '0'); (*area)++; cp += 3; break; default: **area = *cp++; (*area)++; break; } } else if (*cp == '^') { /* some control character */ cp++; if (*cp >= '@' && *cp <= '_') { **area = *cp - '@'; (*area)++; } cp++; } else { /* some normal character */ **area = *cp++; /* put character in area */ (*area)++; } } *((*area)++) = 0; /* null terminate area and string */ return (str); /* return pointer to start of string */ } SHAR_EOF fi if test -f 'mgetflag.c' then echo shar: "will not over-write existing file 'mgetflag.c'" else cat << \SHAR_EOF > 'mgetflag.c' extern char *__modemcap; mgetflag (id) register char *id; { register char *cp = __modemcap; if (__modemcap == (char *) 0) /* has mgetent() been called? ... */ return (-1); /* ... no, can't find number */ while (*cp != ':' && *cp != 0) /* find first entry in cap */ cp++; if (*cp == 0) /* empty entry??? */ return (0); /* ... yes, bad modemcap entry */ else cp++; /* point to first character in next */ while (*cp != 0) { /* until entry found or end of entry */ if (cp[0] == id[0] && cp[1] == id[1]) /* found entry!!! */ return (1); /* return true */ else { /* not entry, skip this entire entry */ while (*cp != ':' && *cp != 0) cp++; /* search for end of current entry */ if (*cp != 0) cp++; /* skip terminating character */ } } return (0); } SHAR_EOF fi if test -f 'mgetnum.c' then echo shar: "will not over-write existing file 'mgetnum.c'" else cat << \SHAR_EOF > 'mgetnum.c' extern char *__modemcap; mgetnum (id) register char *id; { register char *cp = __modemcap; if (__modemcap == (char *) 0) /* has mgetent() been called? ... */ return (-1); /* ... no, can't find number */ while (*cp != ':' && *cp != 0) /* find first entry in cap */ cp++; if (*cp == 0) /* empty entry??? */ return (-1); /* ... yes, bad modemcap entry */ else cp++; /* point to first character in next */ while (*cp != 0) { /* until entry found or end of entry */ if (cp[0] == id[0] && cp[1] == id[1]) { /* found entry!!! */ if (cp[2] != '#') /* is it a numeric value??? */ return (-1); /* no, something else */ return (atoi (&cp[3])); /* return value (just after #) */ } else { /* not entry, skip this entire entry */ while (*cp != ':' && *cp != 0) cp++; /* search for end of current entry */ if (*cp != 0) cp++; /* skip terminating character */ } } return (-1); } SHAR_EOF fi if test -f 'mdial.c' then echo shar: "will not over-write existing file 'mdial.c'" else cat << \SHAR_EOF > 'mdial.c' #include "modemcap.h" #include #include #include static jmp_buf env; /* long jump buffer if timeout in read */ extern int merrno; static timeout () { longjmp (env, 1); } mdial (telno, fd) char *telno; int fd; { char buf[64]; /* telephone buffer if AS is false */ char command[80]; /* dial command buffer */ int i, j; /* index and length of telephone number */ char c; /* single character for connection verification */ if (! DI) return (merrno = A_PROB); /* can't dial phone anyhow */ if (! AS) /* never used any other kind of modem */ return (merrno = A_PROB); else /* normal ascii character phone numbers */ strcpy (buf, telno); sprintf (command, "%s%s%s%s%s", CS, DS, buf, DE, CE); if (setjmp (env) != 0) { signal (SIGALRM, SIG_DFL); return (merrno = D_HUNG); } signal (SIGALRM, timeout); alarm (10); write (fd, command, strlen (command)); if (CO) { /* verify connection */ if (setjmp (env) != 0) { signal (SIGALRM, SIG_DFL); return (merrno = A_PROB); } signal (SIGALRM, timeout); if (TT) alarm (30); else alarm (30 + strlen (telno) / 2); for (i = 0;CO[i] != 0;) { if (read (fd, &c, 1) != 1) continue; if (CO[i] == c) i++; else i = 0; } return (0); } return (0); } SHAR_EOF fi if test -f 'merror.c' then echo shar: "will not over-write existing file 'merror.c'" else cat << \SHAR_EOF > 'merror.c' #include int merrno; char *_merr_list[] = { "No error", "Interrupt occurred", "Dialer Hung", "No answer", "Illegal baud rate", "ACU Problem", "Line Problem", "Can't open LDEVS file", "Requested device not available", "Requested device not known", "No device available at requested baud", "No device known at requested baud" }; int _msys_nerr = (sizeof (_merr_list) / sizeof (char *)); merror (s) char *s; { int i = - merrno; if (0 <= i && i < _msys_nerr) fprintf (stderr, "%s: %s\n", s, _merr_list[i]); else fprintf (stderr, "%s: Error %d\n", s, merrno); } SHAR_EOF fi if test -f 'initmodem.c' then echo shar: "will not over-write existing file 'initmodem.c'" else cat << \SHAR_EOF > 'initmodem.c' #include "modemcap.h" static char f_names[] = "asditthc"; static char *f_caps[] = { &AS, &DI, &TT, &HC }; static char i_names[] = "bdblidad"; static int *i_caps[] = { &BD, &BL, &ID, &AD }; static char c_names[] = "cscedsdeiscoclathu"; static char **c_caps[] = { &CS, &CE, &DS, &DE, &IS, &CO, &CL, &AT, &HU }; initmodem (modem, fd) char *modem; /* name of modem */ int fd; /* channel to modem */ { static char mcapbuf[1024]; static char area[1024]; char *ap = area; register char *cp; register int i, j; register char *s; char *mgetstr (); if (mgetent (mcapbuf, modem) != 1) return (0); for (i = 0, cp = f_names;*cp;i++, cp += 2) *(f_caps[i]) = mgetflag (cp); for (i = 0, cp = i_names;*cp;i++, cp += 2) { j = mgetnum (cp); if (j >= 0) *(i_caps[i]) = j; else *(i_caps[i]) = 0; } for (i = 0, cp = c_names;*cp;i++, cp += 2) *(c_caps[i]) = mgetstr (cp, &ap); if (IS != (char *) 0) { write (fd, IS, strlen (IS)); if (ID) sleep (ID); } return (1); } SHAR_EOF fi if test -f 'hangup.c' then echo shar: "will not over-write existing file 'hangup.c'" else cat << \SHAR_EOF > 'hangup.c' #include "modemcap.h" #ifdef UNIX_S5 #include #endif #ifdef UNIX_V7 #include #endif extern int merrno; hangup (fd) int fd; { #ifdef UNIX_S5 struct termio termio; struct termio hupcl; #endif #ifdef UNIX_V7 struct sgttyb termio; struct sgttyb hupcl; #endif if (HU == (char *) 0 && HC == 0) { undial (fd); return (0); } if (AT != (char *) 0) { write (fd, AT, strlen (AT)); if (AD) sleep (AD); } if (HU) { if (CS) write (fd, CS, strlen (CS)); write (fd, HU, strlen (HU)); if (CE) write (fd, CE, strlen (CE)); if (IS) { write (fd, IS, strlen (IS)); if (ID) sleep (ID); } undial (fd); return (1); } #ifdef UNIX_S5 ioctl (fd, TCGETA, &termio); ioctl (fd, TCGETA, &hupcl); hupcl.c_cflag &= ~CBAUD; hupcl.c_cflag |= HUPCL; ioctl (fd, TCSETA, &hupcl); sleep (2); ioctl (fd, TCSETA, &termio); #endif #ifdef UNIX_V7 gtty (fd, &termio); gtty (fd, &hupcl); hupcl.sg_ispeed = B0; hupcl.sg_ospeed = B0; stty (fd, &hupcl); sleep (2); stty (fd, &termio); #endif undial (fd); return (1); } SHAR_EOF fi if test -f 'dial.c' then echo shar: "will not over-write existing file 'dial.c'" else cat << \SHAR_EOF > 'dial.c' #include #include #include #include #include "modemcap.h" #ifdef UNIX_V7 typedef int void; #endif static char lockfile[64]; static int modemfd = -1; extern int merrno; static findline (line, baud) char *line; int baud; { int exists = 0; int l_baud; char l_line[DVC_LEN+1]; char l_type[DVC_LEN+1]; char buf[64]; FILE *fp; if ((fp = fopen (LDEVS, "r")) == NULL) return (merrno = NO_Ldv); while (fgets (buf, 64, fp) != NULL) { if (buf[0] == '#') /* ignore comment lines */ continue; if (sscanf (buf, "%s%s%*s%d", l_type, l_line, &l_baud) != 3) continue; /* mangled line */ if (strcmp (l_type, "DIR") != 0) continue; /* not a direct connect line */ if (strcmp (l_line, line) == 0) exists++; /* say device exists at some baud rate */ else continue; /* wrong device */ if (l_baud == baud) { /* found device at desired baud rate */ fclose (fp); return (1); } } if (exists) return (merrno = ILL_BD); else return (merrno = DV_NT_K); } static char *findmodem (line) { static char modemtype[16]; char device[DVC_LEN + 1]; char buf[82]; FILE *fp; if ((fp = fopen (_MODEMTYPE_, "r")) == NULL) return (NULL); while (fgets (buf, 82, fp) != NULL) { if (buf[0] == '#') continue; sscanf (buf, "%s%s\n", modemtype, device); if (strcmp (line, device) == 0) { fclose (fp); return (modemtype); } } fclose (fp); return (NULL); } struct speedlist { int value; int name; } speeds[] = { { 0, 0}, { 110, B110}, { 300, B300}, { 600, B600}, { 1200, B1200}, { 2400, B2400}, { 4800, B4800}, { 9600, B9600}, {19200, EXTA}, {38400, EXTB}, { -1, -1} }; static findspeed (speed) int speed; { register struct speedlist *ps; for (ps = speeds; ps->value >= 0; ps++) if (ps->value == speed) return (ps->name); return (0); } alarmcatch () { long timebuf[2]; time (&timebuf[0]); timebuf[1] = timebuf[0]; utime (lockfile, timebuf); signal (SIGALRM, alarmcatch); alarm (3600); } hupcatch () { close (modemfd); unlink (lockfile); signal (SIGHUP, SIG_DFL); } int dial (call) CALL *call; { char modemline[64]; /* device name */ char *modemname; /* modemcap name of modem */ char *strcpy (), *strcat (); #ifdef UNIX_S5 struct termio termio; #endif #ifdef UNIX_V7 struct sgttyb termio; #endif int fd; int err; strcat (strcpy (modemline, DEVDIR), call->line); strcat (strcpy (lockfile, LOCK), call->line); /* * FIX - Version 7 does not have three operand open(), write * conditional compilation for this here ... */ if (access (lockfile, 0) == 0 || (fd = creat (lockfile, 0)) == -1) return (merrno = DV_NT_A); /* lock existed or couldn't be created */ else close (fd); /* created lock, now close descriptor */ fd = -1; /* channel illegal until line is opened */ if ((err = findline (call->line, call->baud)) <= 0) goto error; if ((modemname = findmodem (call->line)) == NULL) { /* can't determine the type of modem */ err = DV_NT_K; goto error; } if ((fd = open (modemline, O_RDWR)) < 0) { /* can't open modem line */ err = L_PROB; goto error; } #ifdef UNIX_S5 if (call->attr != (struct termio *) 0) { /* set attributes */ if (ioctl (fd, TCSETA, call->attr) == -1) { /* some ioctl() problem */ err = L_PROB; goto error; } } else { ioctl (fd, TCGETA, &termio); if ((termio.c_cflag = findspeed (call->baud)) == 0) { err = ILL_BD; goto error; } termio.c_cflag |= (CS8|CREAD|HUPCL); termio.c_iflag = 0; termio.c_oflag = 0; termio.c_lflag = 0; termio.c_cc[VMIN] = 1; termio.c_cc[VTIME] = 1; if (ioctl (fd, TCSETA, &termio) == -1) { err = L_PROB; goto error; } } #endif #ifdef UNIX_V7 if (call->attr != (struct sgttyb *) 0) { /* set attributes */ if (gtty (fd, call->attr) == -1) { /* some gtty() problem */ err = L_PROB; goto error; } } else { gtty (fd, &termio); if ((termio.sg_ispeed = findspeed (call->baud)) == 0) { err = ILL_BD; goto error; } termio.sg_ospeed = termio.sg_ispeed; termio.sg_flags = RAW|ANYP; termio.sg_erase = -1; termio.sg_kill = -1; if (stty (fd, &termio) == -1) { err = L_PROB; goto error; } } #endif initmodem (modemname, fd); /* setup modemcap variables */ if (call->telno == (char *) 0) /* no phone number, connection complete */ goto okay; if (! DI) { /* modem has no ACU!!! */ err = A_PROB; /* no ACU to attach to */ goto error; } if (BD != call->baud) { /* is connection desired at high speed? */ if (BL != call->baud) { /* is connection desired at low speed? */ err = ILL_BD; /* modem can't handle this speed */ goto error; } BD = BL; /* set baud to low baud rate */ CO = CL; /* set connect reply to low baud reply */ } if (err = mdial (call->telno, fd)) /* some error trying to dial */ goto error; signal (SIGALRM, alarmcatch); /* set catcher for ALARM clock */ signal (SIGHUP, hupcatch); /* set catcher for HANG UP */ alarm (3600); /* set clock for 1 hour to touch lock */ okay: return (modemfd = fd); error: unlink (lockfile); if (fd > 2) close (fd); return (merrno = err); } void undial (fd) int fd; { if (fd > 2) close (fd); unlink (lockfile); alarm (0); } SHAR_EOF fi if test -f 'call.c' then echo shar: "will not over-write existing file 'call.c'" else cat << \SHAR_EOF > 'call.c' #include #include CALL call; main (argc, argv) int argc; char **argv; { if (strcmp (argv[0], "call") == 0) exit (do_call (argc, argv)); else if (strcmp (argv[0], "hangup") == 0) exit (do_hup (argv, argv)); fprintf (stderr, "usage: call tty baud telno\n"); fprintf (stderr, " hangup tty baud\n"); exit (1); } do_call (argc, argv) int argc; char **argv; { int fd; if (argc < 4) { fprintf (stderr, "usage: call tty baud telno\n"); exit (1); } call.line = argv[1]; call.baud = atoi (argv[2]); call.telno = argv[3]; fd = dial (&call); if (fd < 0) { merror (argv[0]); exit (2); } undial (fd); return (0); } do_hup (argc, argv) int argc; char **argv; { int fd; if (argc < 3) { fprintf (stderr, "usage: hangup tty baud\n"); exit (1); } call.line = argv[1]; call.baud = atoi (argv[2]); fd = dial (&call); if (fd < 0) { merror (argv[0]); exit (2); } hangup (fd); undial (fd); return (0); } SHAR_EOF fi exit 0 # End of shell archive -- John F. Haugh II HECI Exploration Co. Inc. UUCP: ...!ihnp4!killer!jfh 11910 Greenville Ave, Suite 600 "Don't Have an Oil Well?" Dallas, TX. 75243 " ... Then Buy One!" (214) 231-0993