home *** CD-ROM | disk | FTP | other *** search
- /* bmodem.c - the BIX modem program */
- /*
- by David Betz, BYTE Magazine/BIX
- */
-
- #include <stdio.h>
- #include <ctype.h>
- #include <setjmp.h>
- #include <stat.h>
- #include "dlink.h"
-
- /* useful definitions */
- #define TRUE 1
- #define FALSE 0
-
- /* transfer direction */
- #define NONE 0
- #define SND 1
- #define RCV 2
-
- /* global variables */
- char mode[10]; /* transfer modes */
- int batch; /* batch mode flag */
- int crc; /* crc mode flag */
- int big; /* big block mode flag */
- long baud = 9600L; /* baud rate */
- FILE *fp; /* open file pointer */
- long fsize; /* size of current file */
- jmp_buf xfr_error; /* transfer error escape */
-
- /* external variables */
- extern int blknum; /* current block number */
-
- /* forward declarations */
- long filesize();
-
- /* main - the main routine */
- main(argc,argv)
- int argc; char *argv[];
- {
- long atol();
- int dir,src,dst,cnt,sts;
- char *p;
-
- /* setup the defaults */
- batch = FALSE;
- crc = FALSE;
- big = FALSE;
- dir = NONE;
-
- /* process the arguments */
- for (src = dst = 1, cnt = 0; src < argc; ++src) {
-
- /* handle options */
- if (argv[src][0] == '-') {
- for (p = &argv[src][1]; *p != '\0'; )
- switch (*p++) {
- case 'b': /* baud rate */
- if (*p == '\0') {
- if (++src >= argc)
- usage();
- baud = atol(argv[src]);
- }
- else {
- baud = atol(p);
- p = "";
- }
- break;
- case 'c': /* crc */
- crc = TRUE;
- break;
- case 'k': /* 1K blocks */
- big = TRUE;
- crc = TRUE;
- break;
- case 'r': /* receive */
- dir = RCV;
- break;
- case 's': /* send */
- dir = SND;
- break;
- case 'y': /* batch mode */
- batch = TRUE;
- big = TRUE;
- crc = TRUE;
- break;
- default:
- usage();
- }
- }
-
- /* handle a filename */
- else {
- argv[dst++] = argv[src];
- ++cnt;
- }
- }
-
- /* setup the mode string (for log messages) */
- mode[0] = '\0';
- if (batch) strcat(mode,"y");
- if (big) strcat(mode,"k");
- if (crc) strcat(mode,"c");
-
- /* do the transfer(s) */
- switch (dir) {
- case SND: /* send file(s) */
- if (batch)
- sts = snd_batch(cnt,&argv[1]);
- else {
- if (cnt == 0)
- error("no file specified");
- else if (cnt > 1)
- error("more than one file specified");
- sts = snd_single(argv[1]);
- }
- break;
- case RCV: /* receive file(s) */
- if (batch) {
- if (cnt != 0)
- error("no filenames are allowed for batch receive");
- sts = rcv_batch();
- }
- else {
- if (cnt == 0)
- error("no file specified");
- else if (cnt > 1)
- error("more than one file specified");
- sts = rcv_single(argv[1]);
- }
- break;
- default:
- usage();
- }
-
- /* exit with completion status */
- exit(sts);
- }
-
- /* rcv_single - receive a single file */
- int rcv_single(fname)
- char *fname;
- {
- long curtime;
- int sts;
-
- /* open the output file */
- if ((fp = fopen(fname,"w")) == NULL) {
- printf("Can't open for output: %s\n",fname);
- return (1);
- }
- log("receive, name=%s, mode=%s",fname,mode);
- fsize = -1L;
-
- /* initialize the communications line */
- md_init(baud);
-
- /* trap transfer errors */
- if (setjmp(xfr_error)) {
- printf("[ transfer aborted ]\n");
- xy_cancel();
- md_done();
- return (ER_CANCEL);
- }
-
- /* transfer the data */
- sts = rcvdata();
- md_done();
-
- /* close the file */
- fclose(fp);
-
- /* report errors */
- if (sts) {
- printf("[ transfer failed: %s ]\n",errstring[sts]);
- unlink(fname);
- }
-
- /* log the transfer */
- log("completion status=%s",errstring[sts]);
-
- /* return with completion status */
- return (sts);
- }
-
- /* rcv_batch - receive files in batch mode */
- int rcv_batch()
- {
- char name[LBLKLEN];
- int sts;
-
- /* initialize the communications line */
- md_init(baud);
-
- /* trap transfer errors */
- if (setjmp(xfr_error)) {
- printf("[ transfer aborted ]\n");
- xy_cancel();
- md_done();
- return (ER_CANCEL);
- }
-
- /* transfer each file */
- for (sts = ER_OK; sts == ER_OK; ) {
- if ((sts = xy_rcvhdr(crc,name,&fsize)) == ER_OK) {
- log("receive, name=%s, bytes=%ld, mode=%s",name,fsize,mode);
- if (openfile(name)) {
- xy_sndack();
- sts = rcvdata();
- fclose(fp);
- }
- else {
- xy_cancel();
- sts = ER_CANCEL;
- }
- log("completion status=%s",errstring[sts]);
- }
- }
-
- /* restore the communications line */
- md_done();
-
- /* report errors */
- if (sts != ER_DONE)
- printf("[ transfer failed: %s ]\n",errstring[sts]);
-
- /* return status */
- return (sts);
- }
-
- /* snd_single - send a single file */
- int snd_single(fname)
- char *fname;
- {
- int blklen,sts;
- long curtime;
-
- /* open the input file */
- if ((fp = fopen(fname,"r")) == NULL) {
- printf("Can't open for input: %s\n",fname);
- return (1);
- }
-
- /* show the file size */
- fsize = filesize(fname);
- blklen = (big ? LBLKLEN : SBLKLEN);
- printf("[ bytes: %ld, blocks: %ld, block size: %d ]\n",
- fsize,(fsize + (long)blklen - 1L) / (long)blklen,blklen);
- log("send, name=%s, bytes=%ld, mode=%s",fname,fsize,mode);
-
- /* initialize the communications line */
- md_init(baud);
-
- /* trap transfer errors */
- if (setjmp(xfr_error)) {
- printf("[ transfer aborted ]\n");
- xy_cancel();
- md_done();
- return (ER_CANCEL);
- }
-
- /* transfer the data */
- sts = snddata(blklen);
- md_done();
-
- /* close the file */
- fclose(fp);
-
- /* report errors */
- if (sts)
- printf("[ transfer failed: %s ]\n",errstring[sts]);
-
- /* log the transfer */
- log("completion status=%s",errstring[sts]);
-
- /* return the completion status */
- return (sts);
- }
-
- /* snd_batch - send files in batch mode */
- int snd_batch(argc,argv)
- int argc; char **argv;
- {
- long fsize,curtime;
- int sts;
-
- /* initialize the communications line */
- md_init(baud);
-
- /* trap transfer errors */
- if (setjmp(xfr_error)) {
- printf("[ transfer aborted ]\n");
- xy_cancel();
- md_done();
- return (ER_CANCEL);
- }
-
- /* send each file */
- for (sts = ER_OK; sts == ER_OK && --argc >= 0; ++argv)
- if (fp = fopen(*argv,"r")) {
- fsize = filesize(*argv);
- log("send, name=%s, bytes=%ld, mode=%s",*argv,fsize,mode);
- if ((sts = xy_sndhdr(*argv,fsize)) == ER_OK)
- sts = snddata(big ? LBLKLEN : SBLKLEN);
- log("completion status=%s",errstring[sts]);
- fclose(fp);
- }
-
- /* end the batch transfer */
- if (sts == ER_OK)
- sts = xy_sndend();
-
- /* restore the communications line */
- md_done();
-
- /* report errors */
- if (sts)
- printf("[ transfer failed: %s ]\n",errstring[sts]);
-
- /* return the completion status */
- return (sts);
- }
-
- /* openfile - open the next file for a batch receive */
- static int openfile(name)
- char *name;
- {
- char *src,*dst;
-
- /* strip the directory part of the filename */
- for (src = &name[strlen(name)-1]; src >= name; --src)
- if (*src == '/' || *src == '\\')
- break;
-
- /* translate the filename to lowercase */
- for (++src, dst = name; *src != '\0'; ++src)
- *dst++ = (isupper(*src) ? tolower(*src) : *src);
- *dst = '\0';
-
- /* open the output file */
- if ((fp = fopen(name,"w")) == NULL)
- return (error("can't open for output: %s",name));
-
- /* return successfully */
- return (TRUE);
- }
-
- /* rcvdata - receive data using x/ymodem */
- static int rcvdata()
- {
- unsigned char buf[LBLKLEN],*bp;
- int bc,sts;
-
- /* transfer the data */
- if ((sts = xy_rinit(crc)) == ER_OK) {
-
- /* receive each data block */
- while ((sts = xy_rcvblk(buf,&bc)) == ER_OK) {
- for (bp = buf; --bc >= 0; )
- if (fsize < 0L)
- putc(*bp++,fp);
- else if (fsize > 0L) {
- putc(*bp++,fp);
- --fsize;
- }
- }
- }
- return (sts == ER_EOF ? ER_OK : sts);
- }
-
- /* snddata - send data using x/ymodem */
- static int snddata(blklen)
- int blklen; /* block length (either 128 or 1024) */
- {
- unsigned char buf[LBLKLEN],*bp;
- int bc,ch,sts;
- long size;
-
- /* transfer the data */
- if ((sts = xy_sinit()) == ER_OK) {
-
- /* send each byte */
- for (bp = buf, bc = 0; (ch = getc(fp)) != EOF; ) {
- *bp++ = ch;
- if (++bc == blklen) {
- if ((sts = xy_sndblk(buf,blklen)) != ER_OK)
- return (sts);
- bp = buf;
- bc = 0;
- }
- }
-
- /* flush partial buffer */
- if (bc > 0) {
- while (++bc <= blklen)
- *bp++ = '\0';
- if ((sts = xy_sndblk(buf,blklen)) != ER_OK)
- return (sts);
- }
-
- /* send EOT and wait for ACK */
- sts = xy_sndeot();
- }
- return (sts);
- }
-
- /* usage - show how to use the program */
- usage()
- {
- fprintf(stderr,"usage: bmodem -{sr}[bcky] filename...\n");
- fprintf(stderr," s - send\n");
- fprintf(stderr," r - receive\n");
- fprintf(stderr," b <baud> - baud rate\n");
- fprintf(stderr," c - crc\n");
- fprintf(stderr," k - 1K blocks\n");
- fprintf(stderr," y - ymodem batch\n");
- exit(1);
- }
-
- /* error - report an error and exit */
- error(msg)
- char *msg;
- {
- fprintf(stderr,"error: %s\n",msg);
- exit(1);
- }
-
- /* filesize - return the size of a file */
- static long filesize(fname)
- char *fname;
- {
- struct stat buf;
- return (stat(fname,&buf) == EOF ? 0L : buf.st_size);
- }
-