/*
 * Copyright (c) 1992 Purdue University
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by Purdue University.  The name of the University may not be used
 * to endorse or promote products derived * from this software without
 * specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * Note: this copyright applies to portions of this software developed
 * at Purdue beyond the software covered by the original copyright.
 */

#include <stdio.h>
#include <malloc.h>
#include <fcntl.h>
#include <termios.h>

char *argv0;
int verbose, sc;

main(argc, argv)
int argc;
char **argv;
{
    char *dev;
    char *file;
    int baud;
    int c;
    int err = 0;
    extern int optind;

    argv0 = argv[0];
    while ((c = getopt(argc, argv, "v")) >= 0)
	switch (c) {
	 case 'v':
	    verbose++;
	    break;
	 case 's':
	    sc++;
	    break;
	 case '?':
	    err++;
	    break;
	}
    argv += optind;
    argc -= optind;
    if (err || argc < 2) {
	fprintf(stderr, "usage: %s [-v] scriptfile device [baud]\n", argv0);
	exit(33);
    }
    file = *argv++;
    if (**argv == '/')
	dev = *argv++;
    else {
	dev = malloc(strlen(*argv)+sizeof("/dev/"));
	(void)sprintf(dev, "/dev/%s", *argv++);
    }
    baud = argc > 2 ? atoi(*argv) : 38400;
    setmodem(dev, file, baud);
    if (verbose)
	fprintf(stderr, "Modem configuration complete\n");
    exit(0);
}

int orig_soft;

setmodem(dev, file, baud)
char *dev, *file;
int baud;
{
    FILE *od, *id, *f;
    char lnbuf[256], *l;
    int d;

    if ((d = open(dev, O_RDWR|O_NDELAY, 0)) < 0) {
	perror(dev);
	exit(34);
    }
    if (!(id = fdopen(d, "r"))) {
	perror(dev);
	exit(34);
    }
    if (!(od = fdopen(d, "w"))) {
	perror(dev);
	exit(34);
    }
    if (!(f = fopen(file, "r"))) {
	perror(file);
	exit(35);
    }
    orig_soft = softcar(d, 1);
    setbaud(d, baud);
    sendcom(od, id, "AT");
    while (fgets(lnbuf, sizeof(lnbuf), f)) {
	l = lnbuf + strlen(lnbuf) - 1;
	if (*l = '\n')
	    *l = '\0';
	sendcom(od, id, lnbuf);
    }
    softcar(d, orig_soft);
}

softcar(d, soft)
int d, soft;
{
    int osoft;
    if (!sc)
	return 0;
    if (ioctl(d, TIOCGSOFTCAR, &osoft) < 0)
	perror("TIOCGSOFTCAR");
    if (soft != osoft && ioctl(d, TIOCSSOFTCAR, &soft) < 0)
	perror("TIOCSSOFTCAR");
    return osoft;
}

#include <errno.h>

#define	TIMEOUT 10

sendcom(o, i, com)
FILE *o, *i;
char *com;
{
    char ln[128], *l;
    int c, n;
    int baud = -1;
    int timeout_ok = 0;
    char *mfgets();
    while (*com == '#') {
	if (strncmp(com, "#T/", 3) == 0) {
	    timeout_ok = 1;
	    com += 3;
	}
	else if (sscanf(com, "#(%d)%n", &baud, &n) == 1)
	    com += n;
	else
	    return;
    }
    if (*com) {
	if (verbose)
	    fprintf(stderr, "Send: %s\n", com);
	fputs(com, o);
	putc('\r', o);
	fflush(o);
    }
    if (baud > 0) {
	setbaud(fileno(o), baud);
	if (verbose)
	    fprintf(stderr, "Setting baud rate to: %d\n", baud);
    }
    if (*com) {
	errno = 0;
	while (mfgets(ln, sizeof(ln), i)) {
	    l = ln + strlen(ln) - 1;
	    while (l >= ln && (*l == '\n' || *l == '\r'))
		*l-- = '\0';
	    if (verbose && ln[0])
		fprintf(stderr, "Recv: %s\n", ln);
	    if (strcmp(ln, "OK") == 0)
		return;
	}
	if (errno) {
	    if (errno == EINTR) {
		if (timeout_ok)
		    return;
		fprintf(stderr, "%s: modem timeout\n", argv0);
	    }
	    else {
		fprintf(stderr, "%s: ", argv0);
		perror("Error reading from modem");
	    }
	}
	else {
	    fprintf(stderr, "%s: EOF from modem\n", argv0);
	}
	softcar(fileno(o), orig_soft);
	exit(38);
    }
}

char *
mfgets(l, ls, fi)
char *l;
int ls;
FILE *fi;
{
    static char buf[512];
    static char *bp;
    static int nread;
    char *lp = l;
    int ln = 0;
    char c;
    int i = fileno(fi);
    int ns = 0;

    for ( ; ; ) {
	while (nread > 0 && ln < ls - 1) {
	    c = *lp++ = *bp++;
	    nread--;
	    ln++;
	    if (c == '\n' || ln == ls - 1) {
		*lp = '\0';
		return l;
	    }
	}
	while (nread <= 0) {
	    nread = read(i, buf, sizeof(buf));
	    if (nread < 0) {
		fprintf(stderr, "read error %d %d\n", nread, errno);
		if (ln) {
		    *lp = '\0';
		    return l;
		}
		else
		    return (char *)0;
	    }
	    if (nread == 0) {
		if (++ns > TIMEOUT) {
		    if (ln) {
			*lp = '\0';
			return l;
		    }
		    else
			return (char *)0;
		}
		sleep(1);
	    }
	    else
		ns = 0;
	}
	bp = buf;
    }
}

setbaud(d, baud)
int d, baud;
{
    int b;
    struct termios t;
    static int got_t;
    int baudext = 0;
    switch (baud) {
     case 300:	  b = B300; break;
     case 1200:   b = B1200; break;
     case 9600:   b = B9600; break;
     case 19200:  b = B19200; break;
     default:
		  fprintf(stderr,
			  "Invalid baud rate %d, setting baud rate to 38400\n",
			  baud);
     case 38400:  b = B38400; break;
#ifdef	STS
     case 57600:  b = B38400; baudext = 1; break;
     case 115200: b = B38400; baudext = 2; break;
     case 230400: b = B38400; baudext = 3; break;
#endif	STS
    }
    if (!got_t && tcgetattr(d, &t) < 0) {
	perror("tcgetattr");
	softcar(d, orig_soft);
	exit(36);
    }
    got_t++;
    t.c_cflag &= ~(PARENB|CSIZE|HUPCL);
    t.c_cflag |= CS8;
    cfsetispeed(&t, b);
    cfsetospeed(&t, b);
    t.c_oflag &= ~OPOST;
    t.c_lflag &= ~(ICANON|ISIG|ECHO);
    t.c_cc[VTIME] = TIMEOUT*10;
    t.c_cc[VMIN] = 0;
    if (tcsetattr(d, TCSAFLUSH, &t) < 0) {
	perror("tcsetattr");
	softcar(d, orig_soft);
	exit(36);
    }
#ifdef	STS
    (void)stsbaudext(d, baudext);
#endif  STS
}
