home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 112.lha / BModem / bmodem.c < prev    next >
Encoding:
C/C++ Source or Header  |  1986-11-20  |  8.9 KB  |  435 lines

  1. /* bmodem.c - the BIX modem program */
  2. /*
  3.     by David Betz, BYTE Magazine/BIX
  4. */
  5.  
  6. #include <stdio.h>
  7. #include <ctype.h>
  8. #include <setjmp.h>
  9. #include <stat.h>
  10. #include "dlink.h"
  11.  
  12. /* useful definitions */
  13. #define TRUE    1
  14. #define FALSE    0
  15.  
  16. /* transfer direction */
  17. #define NONE    0
  18. #define SND    1
  19. #define RCV    2
  20.  
  21. /* global variables */
  22. char mode[10];        /* transfer modes */
  23. int batch;        /* batch mode flag */
  24. int crc;        /* crc mode flag */
  25. int big;        /* big block mode flag */
  26. long baud = 9600L;    /* baud rate */
  27. FILE *fp;        /* open file pointer */
  28. long fsize;        /* size of current file */
  29. jmp_buf xfr_error;    /* transfer error escape */
  30.  
  31. /* external variables */
  32. extern int blknum;    /* current block number */
  33.  
  34. /* forward declarations */
  35. long filesize();
  36.  
  37. /* main - the main routine */
  38. main(argc,argv)
  39.   int argc; char *argv[];
  40. {
  41.     long atol();
  42.     int dir,src,dst,cnt,sts;
  43.     char *p;
  44.  
  45.     /* setup the defaults */
  46.     batch = FALSE;
  47.     crc = FALSE;
  48.     big = FALSE;
  49.     dir = NONE;
  50.     
  51.     /* process the arguments */
  52.     for (src = dst = 1, cnt = 0; src < argc; ++src) {
  53.  
  54.     /* handle options */
  55.     if (argv[src][0] == '-') {
  56.         for (p = &argv[src][1]; *p != '\0'; )
  57.             switch (*p++) {
  58.         case 'b':        /* baud rate */
  59.             if (*p == '\0') {
  60.             if (++src >= argc)
  61.                 usage();
  62.                 baud = atol(argv[src]);
  63.             }
  64.             else {
  65.             baud = atol(p);
  66.              p = "";
  67.             }
  68.             break;
  69.         case 'c':        /* crc */
  70.                 crc = TRUE;
  71.                 break;
  72.         case 'k':        /* 1K blocks */
  73.             big = TRUE;
  74.             crc = TRUE;
  75.                 break;
  76.         case 'r':        /* receive */
  77.             dir = RCV;
  78.             break;
  79.         case 's':        /* send */
  80.             dir = SND;
  81.             break;    
  82.         case 'y':        /* batch mode */
  83.             batch = TRUE;
  84.             big = TRUE;
  85.             crc = TRUE;
  86.                 break;
  87.         default:
  88.                 usage();
  89.         }
  90.     }
  91.  
  92.     /* handle a filename */
  93.     else {
  94.         argv[dst++] = argv[src];
  95.         ++cnt;
  96.     }
  97.     }
  98.  
  99.     /* setup the mode string (for log messages) */
  100.     mode[0] = '\0';
  101.     if (batch) strcat(mode,"y");
  102.     if (big) strcat(mode,"k");
  103.     if (crc) strcat(mode,"c");
  104.  
  105.     /* do the transfer(s) */
  106.     switch (dir) {
  107.     case SND:    /* send file(s) */
  108.     if (batch)
  109.         sts = snd_batch(cnt,&argv[1]);
  110.     else {
  111.         if (cnt == 0)
  112.         error("no file specified");
  113.         else if (cnt > 1)
  114.         error("more than one file specified");
  115.         sts = snd_single(argv[1]);
  116.     }
  117.     break;
  118.     case RCV:    /* receive file(s) */
  119.     if (batch) {
  120.         if (cnt != 0)
  121.         error("no filenames are allowed for batch receive");
  122.         sts = rcv_batch();
  123.     }
  124.     else {
  125.         if (cnt == 0)
  126.         error("no file specified");
  127.         else if (cnt > 1)
  128.         error("more than one file specified");
  129.         sts = rcv_single(argv[1]);
  130.     }
  131.     break;
  132.     default:
  133.     usage();
  134.     }
  135.  
  136.     /* exit with completion status */
  137.     exit(sts);
  138. }
  139.  
  140. /* rcv_single - receive a single file */
  141. int rcv_single(fname)
  142.   char *fname;
  143. {
  144.     long curtime;
  145.     int sts;
  146.  
  147.     /* open the output file */
  148.     if ((fp = fopen(fname,"w")) == NULL) {
  149.     printf("Can't open for output: %s\n",fname);
  150.     return (1);
  151.     }
  152.     log("receive, name=%s, mode=%s",fname,mode);
  153.     fsize = -1L;
  154.  
  155.     /* initialize the communications line */
  156.     md_init(baud);
  157.  
  158.     /* trap transfer errors */
  159.     if (setjmp(xfr_error)) {
  160.     printf("[ transfer aborted ]\n");
  161.     xy_cancel();
  162.     md_done();
  163.     return (ER_CANCEL);
  164.     }
  165.  
  166.     /* transfer the data */
  167.     sts = rcvdata();
  168.     md_done();
  169.  
  170.     /* close the file */
  171.     fclose(fp);
  172.  
  173.     /* report errors */
  174.     if (sts) {
  175.     printf("[ transfer failed: %s ]\n",errstring[sts]);
  176.     unlink(fname);
  177.     }
  178.  
  179.     /* log the transfer */
  180.     log("completion status=%s",errstring[sts]);
  181.  
  182.     /* return with completion status */
  183.     return (sts);
  184. }
  185.  
  186. /* rcv_batch - receive files in batch mode */
  187. int rcv_batch()
  188. {
  189.     char name[LBLKLEN];
  190.     int sts;
  191.  
  192.     /* initialize the communications line */
  193.     md_init(baud);
  194.  
  195.     /* trap transfer errors */
  196.     if (setjmp(xfr_error)) {
  197.     printf("[ transfer aborted ]\n");
  198.     xy_cancel();
  199.     md_done();
  200.     return (ER_CANCEL);
  201.     }
  202.  
  203.     /* transfer each file */
  204.     for (sts = ER_OK; sts == ER_OK; ) {
  205.     if ((sts = xy_rcvhdr(crc,name,&fsize)) == ER_OK) {
  206.         log("receive, name=%s, bytes=%ld, mode=%s",name,fsize,mode);
  207.         if (openfile(name)) {
  208.         xy_sndack();
  209.         sts = rcvdata();
  210.         fclose(fp);
  211.         }
  212.         else {
  213.         xy_cancel();
  214.         sts = ER_CANCEL;
  215.         }
  216.         log("completion status=%s",errstring[sts]);
  217.     }
  218.     }
  219.  
  220.     /* restore the communications line */
  221.     md_done();
  222.  
  223.     /* report errors */
  224.     if (sts != ER_DONE)
  225.     printf("[ transfer failed: %s ]\n",errstring[sts]);
  226.  
  227.     /* return status */
  228.     return (sts);
  229. }
  230.  
  231. /* snd_single - send a single file */
  232. int snd_single(fname)
  233.   char *fname;
  234. {
  235.     int blklen,sts;
  236.     long curtime;
  237.  
  238.     /* open the input file */
  239.     if ((fp = fopen(fname,"r")) == NULL) {
  240.     printf("Can't open for input: %s\n",fname);
  241.     return (1);
  242.     }
  243.  
  244.     /* show the file size */
  245.     fsize = filesize(fname);
  246.     blklen = (big ? LBLKLEN : SBLKLEN);
  247.     printf("[ bytes: %ld, blocks: %ld, block size: %d ]\n",
  248.        fsize,(fsize + (long)blklen - 1L) / (long)blklen,blklen);
  249.     log("send, name=%s, bytes=%ld, mode=%s",fname,fsize,mode);
  250.  
  251.     /* initialize the communications line */
  252.     md_init(baud);
  253.  
  254.     /* trap transfer errors */
  255.     if (setjmp(xfr_error)) {
  256.     printf("[ transfer aborted ]\n");
  257.     xy_cancel();
  258.     md_done();
  259.     return (ER_CANCEL);
  260.     }
  261.  
  262.     /* transfer the data */
  263.     sts = snddata(blklen);
  264.     md_done();
  265.  
  266.     /* close the file */
  267.     fclose(fp);
  268.  
  269.     /* report errors */
  270.     if (sts)
  271.     printf("[ transfer failed: %s ]\n",errstring[sts]);
  272.  
  273.     /* log the transfer */
  274.     log("completion status=%s",errstring[sts]);
  275.  
  276.     /* return the completion status */
  277.     return (sts);
  278. }
  279.  
  280. /* snd_batch - send files in batch mode */
  281. int snd_batch(argc,argv)
  282.   int argc; char **argv;
  283. {
  284.     long fsize,curtime;
  285.     int sts;
  286.  
  287.     /* initialize the communications line */
  288.     md_init(baud);
  289.  
  290.     /* trap transfer errors */
  291.     if (setjmp(xfr_error)) {
  292.     printf("[ transfer aborted ]\n");
  293.     xy_cancel();
  294.     md_done();
  295.     return (ER_CANCEL);
  296.     }
  297.  
  298.     /* send each file */
  299.     for (sts = ER_OK; sts == ER_OK && --argc >= 0; ++argv)
  300.     if (fp = fopen(*argv,"r")) {
  301.         fsize = filesize(*argv);
  302.         log("send, name=%s, bytes=%ld, mode=%s",*argv,fsize,mode);
  303.         if ((sts = xy_sndhdr(*argv,fsize)) == ER_OK)
  304.         sts = snddata(big ? LBLKLEN : SBLKLEN);
  305.         log("completion status=%s",errstring[sts]);
  306.         fclose(fp);
  307.     }
  308.  
  309.     /* end the batch transfer */
  310.     if (sts == ER_OK)
  311.     sts = xy_sndend();
  312.  
  313.     /* restore the communications line */
  314.     md_done();
  315.  
  316.     /* report errors */
  317.     if (sts)
  318.     printf("[ transfer failed: %s ]\n",errstring[sts]);
  319.  
  320.     /* return the completion status */
  321.     return (sts);
  322. }
  323.  
  324. /* openfile - open the next file for a batch receive */
  325. static int openfile(name)
  326.   char *name;
  327. {
  328.     char *src,*dst;
  329.  
  330.     /* strip the directory part of the filename */
  331.     for (src = &name[strlen(name)-1]; src >= name; --src)
  332.     if (*src == '/' || *src == '\\')
  333.         break;
  334.  
  335.     /* translate the filename to lowercase */
  336.     for (++src, dst = name; *src != '\0'; ++src)
  337.     *dst++ = (isupper(*src) ? tolower(*src) : *src);
  338.     *dst = '\0';
  339.  
  340.     /* open the output file */
  341.     if ((fp = fopen(name,"w")) == NULL)
  342.     return (error("can't open for output: %s",name));
  343.  
  344.     /* return successfully */
  345.     return (TRUE);
  346. }
  347.  
  348. /* rcvdata - receive data using x/ymodem */
  349. static int rcvdata()
  350. {
  351.     unsigned char buf[LBLKLEN],*bp;
  352.     int bc,sts;
  353.  
  354.     /* transfer the data */
  355.     if ((sts = xy_rinit(crc)) == ER_OK) {
  356.  
  357.     /* receive each data block */
  358.     while ((sts = xy_rcvblk(buf,&bc)) == ER_OK) {
  359.         for (bp = buf; --bc >= 0; )
  360.         if (fsize < 0L)
  361.             putc(*bp++,fp);
  362.         else if (fsize > 0L) {
  363.             putc(*bp++,fp);
  364.             --fsize;
  365.         }
  366.     }
  367.     }
  368.     return (sts == ER_EOF ? ER_OK : sts);
  369. }
  370.  
  371. /* snddata - send data using x/ymodem */
  372. static int snddata(blklen)
  373.   int blklen;    /* block length (either 128 or 1024) */
  374. {
  375.     unsigned char buf[LBLKLEN],*bp;
  376.     int bc,ch,sts;
  377.     long size;
  378.  
  379.     /* transfer the data */
  380.     if ((sts = xy_sinit()) == ER_OK) {
  381.  
  382.     /* send each byte */
  383.     for (bp = buf, bc = 0; (ch = getc(fp)) != EOF; ) {
  384.         *bp++ = ch;
  385.         if (++bc == blklen) {
  386.         if ((sts = xy_sndblk(buf,blklen)) != ER_OK)
  387.             return (sts);
  388.         bp = buf;
  389.         bc = 0;
  390.         }
  391.     }
  392.  
  393.     /* flush partial buffer */
  394.     if (bc > 0) {
  395.         while (++bc <= blklen)
  396.         *bp++ = '\0';
  397.         if ((sts = xy_sndblk(buf,blklen)) != ER_OK)
  398.         return (sts);
  399.     }
  400.  
  401.     /* send EOT and wait for ACK */
  402.     sts = xy_sndeot();
  403.     }
  404.     return (sts);
  405. }
  406.  
  407. /* usage - show how to use the program */
  408. usage()
  409. {
  410.     fprintf(stderr,"usage: bmodem -{sr}[bcky] filename...\n");
  411.     fprintf(stderr,"   s        - send\n");
  412.     fprintf(stderr,"   r        - receive\n");
  413.     fprintf(stderr,"   b <baud> - baud rate\n");
  414.     fprintf(stderr,"   c        - crc\n");
  415.     fprintf(stderr,"   k        - 1K blocks\n");
  416.     fprintf(stderr,"   y        - ymodem batch\n");
  417.     exit(1);
  418. }
  419.  
  420. /* error - report an error and exit */
  421. error(msg)
  422.   char *msg;
  423. {
  424.     fprintf(stderr,"error: %s\n",msg);
  425.     exit(1);
  426. }
  427.  
  428. /* filesize - return the size of a file */
  429. static long filesize(fname)
  430.   char *fname;
  431. {
  432.     struct stat buf;
  433.     return (stat(fname,&buf) == EOF ? 0L : buf.st_size);
  434. }
  435.