home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_100 / 182_01 / rb_215.c < prev    next >
Text File  |  1990-07-31  |  18KB  |  817 lines

  1. #define VERSION "2.15 03-04-85"
  2.  
  3. /*% cc -DUSG -DNFGVMIN -O -K % -o rb
  4.  *
  5.  * rb.c By Chuck Forsberg
  6.  *
  7.  * A program for Unix which can receive
  8.  *  files from computers running YAM or MODEM.
  9.  *  If no filename is given, YAM batch mode is assumed.
  10.  *
  11.  * Iff the program is invoked by rbCOMMAND, output is piped to 
  12.  * "COMMAND filename"
  13.  *
  14.  *  Supports the CRC option or regular checksum.
  15.  *  Received pathnames containing no lowercase letters will be changed to lower
  16.  *  case unless -u option is given.
  17.  *
  18.  *  Unless the -b (binary) option is given, \r is discarded and
  19.  *  ^Z (which is also discarded) acts as end of file.
  20.  *
  21.  *  Any slashes in the pathname are changed to underscore.
  22.  *  If the raw pathname ends in .MSG or .TXT, any existing file will
  23.  *  be appended to rather than replaced. Trailing periods are eliminated.
  24.  *
  25.  *  If the raw pathname ends in any of the extensions in Extensions,
  26.  *   or .?Q* (squeezed file), or if the first sector contains binary-like
  27.  *   data (parity bits or characters in the range 0 to 6 before ^Z is seen),
  28.  *   or if the transmitted file mode has the 0100000 but set,
  29.  *   that file will be received in binary mode anyway.
  30.  *
  31.  *
  32.  * A log of activities is appended to LOGFILE with the -v option
  33.  * If stdout and stderr refer to different devices, progress is displayed to
  34.  * stderr.
  35.  *
  36.  * rb is derived from yam2.c and sb.c
  37.  * rb uses Unix System III buffered input to reduce CPU time.
  38.  *  USG UNIX (3.0) ioctl conventions courtesy  Jeff Martin
  39.  *     cc -O -DV7  rb.c -o rb        Unix V7, BSD 2.8 - 4.2
  40.  *    cc -O -DUSG rb.c -o rb        USG (3.0) Unix
  41.  *    cc -o rb.c            Regulus
  42.  *        (Don't try this on Unix, you'll clobber the source!)
  43.  *  Unix is a trademark of Western Electric Company
  44.  *
  45.  *  Regulus conventions 1-10-83 CAF
  46.  *
  47.  *  Some systems (Venix, Coherent, Regulus) do not support tty raw mode
  48.  *  read(2) the same way as Unix. ONEREAD must be defined to force one
  49.  *  character reads for these systems. Added 7-01-84 CAF
  50.  *
  51.  *  Alarm signal handling changed to work with 4.2 BSD 7-15-84 CAF 
  52.  *
  53.  *  NFGVMIN Added 1-13-85 CAF for PC-AT Xenix systems where c_cc[VMIN]
  54.  *  doesn't seem to work (even though it compiles without error!).
  55.  */
  56. #define LOGFILE "/tmp/rblog"
  57.  
  58. #include <stdio.h>
  59. #include <signal.h>
  60. #include <setjmp.h>
  61. #include <ctype.h>
  62. FILE *popen();
  63.  
  64. #define OK 0
  65. #define FALSE 0
  66. #define TRUE 1
  67. #define ERROR (-1)
  68.  
  69. #define HOWMANY 133
  70. #include "rbsb.c"    /* most of the system dependent stuff here */
  71.  
  72. char *substr();
  73. FILE *fout;
  74.  
  75. char *Extensions[] = {
  76. ".A",
  77. ".ARC",
  78. ".CCC",
  79. ".CL",
  80. ".CMD",
  81. ".COM",
  82. ".CRL",
  83. ".DAT",
  84. ".DIR",
  85. ".EXE",
  86. ".O",
  87. ".OBJ",
  88. ".OVL",
  89. ".PAG",
  90. ".REL",
  91. ".SAV",
  92. ".SUB",
  93. ".SWP",
  94. ".SYS",
  95. ".TAR",
  96. ".UTL",
  97. ".a",
  98. ".o",
  99. ".tar",
  100. ""
  101. };
  102.  
  103. /* Ward Christensen / CP/M parameters - Don't change these! */
  104. #define ENQ 005
  105. #define CAN ('X'&037)
  106. #define XOFF ('s'&037)
  107. #define XON ('q'&037)
  108. #define SOH 1
  109. #define STX 2
  110. #define EOT 4
  111. #define ACK 6
  112. #define NAK 025
  113. #define CPMEOF 032
  114. #define WANTCRC 0103    /* send C not NAK to get crc not checksum */
  115. #define TIMEOUT (-2)
  116. #define ERRORMAX 5
  117. #define RETRYMAX 5
  118. #define WCEOT (-10)
  119. #define SECSIZ 128    /* cp/m's Magic Number record size */
  120. #define PATHLEN 257    /* ready for 4.2 bsd ? */
  121. #define KSIZE 1024    /* record size with k option */
  122. #define UNIXFILE 0x8000    /* happens to the the S_IFREG file mask bit for stat */
  123.  
  124. int Lastrx;
  125. int Crcflg;
  126. int Firstsec;
  127. int Eofseen;        /* indicates cpm eof (^Z) has been received */
  128. int totblocks;        /* total number of blocks received */
  129. int errors;
  130.  
  131. #define DEFBYTL 2000000000L    /* default rx file size */
  132. long Bytesleft;        /* number of bytes of incoming file left */
  133. long Modtime;        /* Unix style mod time for incoming file */
  134. short Filemode;        /* Unix style mode for incoming file */
  135. char Pathname[PATHLEN];
  136. char *Progname;        /* the name by which we were called */
  137.  
  138. int Batch=0;
  139. int Wcsmask=0377;
  140. int Topipe=0;
  141. int MakeLCPathname=TRUE;    /* make received pathname lower case */
  142. int Verbose=0;
  143. int Quiet=0;        /* overrides logic that would otherwise set verbose */
  144. int Rxbinary=FALSE;    /* receive all files in bin mode */
  145. int Thisbinary;        /* current file is to be received in bin mode */
  146. int Blklen;        /* record length of received packets */
  147. char secbuf[KSIZE];
  148. char linbuf[KSIZE];
  149. int Lleft=0;        /* number of characters in linbuf */
  150.  
  151. jmp_buf tohere;        /* For the interrupt on RX timeout */
  152.  
  153. unsigned short updcrc();
  154.  
  155. alrm()
  156. {
  157.     longjmp(tohere, -1);
  158. }
  159.  
  160. /* called by signal interrupt or terminate to clean things up */
  161. bibi(n)
  162. {
  163.     canit(); mode(0);
  164.     fprintf(stderr, "sb: caught signal %d; exiting", n);
  165.     exit(128+n);
  166. }
  167.  
  168. main(argc, argv)
  169. char *argv[];
  170. {
  171.     register char *cp;
  172.     register npats;
  173.     char *virgin, **patts;
  174.     int exitcode;
  175.  
  176.     setbuf(stderr, NULL);
  177.     chkinvok(virgin=argv[0]);    /* if called as [-]rbCOMMAND set flag */
  178.     npats = 0;
  179.     while (--argc) {
  180.         cp = *++argv;
  181.         if (*cp == '-') {
  182.             while( *++cp) {
  183.                 switch(*cp) {
  184.                 case '1':
  185.                     iofd = 1; break;
  186.                 case '7':
  187.                     Wcsmask = 0177;
  188.                 case 'b':
  189.                     Rxbinary=TRUE; break;
  190.                 case 'k':
  191.                 case 'c':
  192.                     Crcflg=TRUE; break;
  193.                 case 'q':
  194.                     Quiet=TRUE; Verbose=0; break;
  195.                 case 'u':
  196.                     MakeLCPathname=FALSE; break;
  197.                 case 'v':
  198.                     ++Verbose; break;
  199.                 default:
  200.                     usage();
  201.                 }
  202.             }
  203.         }
  204.         else if ( !npats && argc>0) {
  205.             if (argv[0][0]) {
  206.                 npats=argc;
  207.                 patts=argv;
  208.             }
  209.         }
  210.     }
  211.     if (npats > 1)
  212.         usage();
  213.     if (Verbose) {
  214.         if (freopen(LOGFILE, "a", stderr)==NULL) {
  215.             printf("Can't open log file %s\n",LOGFILE);
  216.             exit(0200);
  217.         }
  218.         setbuf(stderr, NULL);
  219.         fprintf(stderr, "argv[0]=%s Progname=%s\n", virgin, Progname);
  220.     }
  221.     if (fromcu() && !Quiet) {
  222.         if (Verbose == 0)
  223.             Verbose = 2;
  224.     }
  225.     mode(1);
  226.     if (signal(SIGINT, bibi) == SIG_IGN) {
  227.         signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN);
  228.     }
  229.     else {
  230.         signal(SIGINT, bibi); signal(SIGKILL, bibi);
  231.     }
  232.     if (wcreceive(npats, patts)==ERROR) {
  233.         exitcode=0200;
  234.         canit();
  235.     }
  236.     mode(0);
  237.     if (exitcode != 0)    /* bellow again with all thy might. */
  238.         canit();
  239. #ifdef REGULUS
  240.     else
  241.         printf("\6\6\6\6\6\n");    /* Regulus doesn't wait ... */
  242. #endif
  243.     exit(exitcode);
  244. }
  245.  
  246.  
  247. usage()
  248. {
  249.     fprintf(stderr,"%s %s by Chuck Forsberg\n", Progname, VERSION);
  250.     fprintf(stderr,"Usage:    rb [-17buv]\n\tor rb [-1bcuv] file\n");
  251.     exit(1);
  252. }
  253.  
  254. wcreceive(argc, argp)
  255. char **argp;
  256. {
  257.     if (Batch || argc==0) {
  258.         Crcflg=(Wcsmask==0377);
  259.         fprintf(stderr, "rb: ready ");
  260.         for (;;) {
  261.             totblocks=0;
  262.             if (wcrxpn(secbuf)== ERROR)
  263.                 goto fubar;
  264.             if (secbuf[0]==0)
  265.                 return OK;
  266.             if (procheader(secbuf) == ERROR)
  267.                 goto fubar;
  268.             if (wcrx()==ERROR)
  269.                 goto fubar;
  270.         }
  271.     } else {
  272.         totblocks=0; Bytesleft = DEFBYTL; Filemode = 0; Modtime = 0L;
  273.  
  274.         strcpy(Pathname, *argp);
  275.         if (fopen(*argp, "r") != NULL) {
  276.             fprintf(stderr, "rb: %s exists\n", Pathname);
  277.             goto fubar;
  278.         }
  279.         fprintf(stderr, "\nrb: ready to receive %s ", Pathname);
  280.         if ((fout=fopen(Pathname, "w")) == NULL)
  281.             return ERROR;
  282.         if (wcrx()==ERROR)
  283.             goto fubar;
  284.     }
  285.     return OK;
  286. fubar:
  287.     canit();
  288.     if (Topipe && fout) {
  289.         pclose(fout);  return ERROR;
  290.     }
  291.     if (fout)
  292.         fclose(fout);
  293.     return ERROR;
  294. }
  295.  
  296.  
  297. /*
  298.  * Fetch a pathname from the other end as a C ctyle ASCIZ string.
  299.  * Length is indeterminate as long as less than Blklen
  300.  * a null string represents no more files
  301.  */
  302. wcrxpn(rpn)
  303. char *rpn;    /* receive a pathname */
  304. {
  305.     register c;
  306.  
  307. #ifdef NFGVMIN
  308.     readline(1);
  309. #else
  310.     purgeline();
  311. #endif
  312.  
  313. et_tu:
  314.     Firstsec=TRUE;
  315.     sendline(Crcflg?WANTCRC:NAK);
  316.     while ((c = wcgetsec(rpn, 100)) != 0) {
  317.         log( "Pathname fetch returned %d\n", c);
  318.         if (c == WCEOT) {
  319.             sendline(ACK); readline(1); goto et_tu;
  320.         }
  321.         return ERROR;
  322.     }
  323.     sendline(ACK);
  324.     return OK;
  325. }
  326.  
  327. /*
  328.  * Adapted from CMODEM13.C, written by
  329.  * Jack M. Wierda and Roderick W. Hart
  330.  */
  331.  
  332. wcrx()
  333. {
  334.     register int sectnum, sectcurr;
  335.     register char sendchar;
  336.     register char *p;
  337.     int cblklen;            /* bytes to dump this block */
  338.     time_t timep[2];
  339.  
  340.     Firstsec=TRUE;sectnum=0; Eofseen=FALSE;
  341.     sendchar=Crcflg?WANTCRC:NAK;
  342.  
  343.     for (;;) {
  344.         sendline(sendchar);    /* send it now, we're ready! */
  345.         sectcurr=wcgetsec(secbuf, (sectnum&0177)?50:130);
  346.         report(sectcurr);
  347.         if (sectcurr==(sectnum+1 &Wcsmask)) {
  348.  
  349.             if (sectnum==0 && !Thisbinary)
  350.                 for (p=secbuf,sectcurr=Blklen;
  351.                   *p != 032 && --sectcurr>=0; ++p)
  352.                     if (*p < 07 || (*p & 0200)) {
  353.                         Thisbinary++;
  354.                         if (Verbose)
  355.                             fprintf(stderr, "Changed to BIN\n");
  356.                         break;
  357.                     }
  358.             sectnum++;
  359.             cblklen = Bytesleft>Blklen ? Blklen:Bytesleft;
  360.             if (putsec(secbuf, cblklen)==ERROR)
  361.                 return ERROR;
  362.             if ((Bytesleft-=cblklen) < 0)
  363.                 Bytesleft = 0;
  364.             sendchar=ACK;
  365.         }
  366.         else if (sectcurr==(sectnum&Wcsmask)) {
  367.             log( "Received dup Sector\n");
  368.             sendchar=ACK;
  369.         }
  370.         else if (sectcurr==WCEOT) {
  371.             if (Topipe) {
  372.                 if (pclose(fout)!=ERROR) {
  373.                     sendline(ACK);
  374.                     return OK;
  375.                 }
  376.                 canit(); return ERROR;
  377.             }
  378.             if (fclose(fout)==ERROR) {
  379.                 canit();
  380.                 fprintf(stderr, "file close ERROR\n");
  381.                 return ERROR;
  382.             }
  383.             if (Modtime) {
  384.                 timep[0] = time(NULL);
  385.                 timep[1] = Modtime;
  386.                 utime(Pathname, timep);
  387.             }
  388.             if (Filemode)
  389.                 chmod(Pathname, (07777 & Filemode));
  390.             sendline(ACK);
  391.             return OK;
  392.         }
  393.         else if (sectcurr==ERROR)
  394.             return ERROR;
  395.         else {
  396.             log( "Sync Error\n");
  397.             return ERROR;
  398.         }
  399.     }
  400. }
  401.  
  402. /*
  403.  * wcgetsec fetches a Ward Christensen type sector.
  404.  * Returns sector number encountered or ERROR if valid sector not received,
  405.  * or CAN CAN received
  406.  * or WCEOT if eot sector
  407.  * time is timeout for first char, set to 4 seconds thereafter
  408.  ***************** NO ACK IS SENT IF SECTOR IS RECEIVED OK **************
  409.  *    (Caller must do that when he is good and ready to get next sector)
  410.  */
  411.  
  412. wcgetsec(rxbuf, maxtime)
  413. char *rxbuf;
  414. int maxtime;
  415. {
  416.     register checksum, wcj, firstch;
  417.     register unsigned short oldcrc;
  418.     register char *p;
  419.     int sectcurr;
  420.  
  421.     for (Lastrx=errors=0; errors<RETRYMAX; errors++) {
  422.  
  423.         if ((firstch=readline(maxtime))==STX) {
  424.             Blklen=KSIZE; goto get2;
  425.         }
  426.         if (firstch==SOH) {
  427.             Blklen=SECSIZ;
  428. get2:
  429.             sectcurr=readline(1);
  430.             if ((sectcurr+(oldcrc=readline(1)))==Wcsmask) {
  431.                 oldcrc=checksum=0;
  432.                 for (p=rxbuf,wcj=Blklen; --wcj>=0; ) {
  433.                     if ((firstch=readline(1)) < 0)
  434.                         goto bilge;
  435.                     oldcrc=updcrc(firstch, oldcrc);
  436.                     checksum += (*p++ = firstch);
  437.                 }
  438.                 if ((firstch=readline(1)) < 0)
  439.                     goto bilge;
  440.                 if (Crcflg) {
  441.                     oldcrc=updcrc(firstch, oldcrc);
  442.                     if ((firstch=readline(1)) < 0)
  443.                         goto bilge;
  444.                     oldcrc=updcrc(firstch, oldcrc);
  445.                     if (oldcrc)
  446.                         log("CRC=0%o\n", oldcrc);
  447.                     else {
  448.                         Firstsec=FALSE;
  449.                         return sectcurr;
  450.                     }
  451.                 }
  452.                 else if (((checksum-firstch)&Wcsmask)==0) {
  453.                     Firstsec=FALSE;
  454.                     return sectcurr;
  455.                 }
  456.                 else
  457.                     log( "Checksum Error\n");
  458.             }
  459.             else
  460.                 log("Sector number garbled 0%o 0%o\n",
  461.                  sectcurr, oldcrc);
  462.         }
  463.         /* make sure eot really is eot and not just mixmash */
  464. #ifdef NFGVMIN
  465.         else if (firstch==EOT && readline(1)==TIMEOUT)
  466.             return WCEOT;
  467. #else
  468.         else if (firstch==EOT && Lleft==0)
  469.             return WCEOT;
  470. #endif
  471.         else if (firstch==CAN) {
  472.             if (Lastrx==CAN) {
  473.                 log( "Sender CANcelled\n");
  474.                 return ERROR;
  475.             } else {
  476.                 Lastrx=CAN;
  477.                 continue;
  478.             }
  479.         }
  480.         else if (firstch==TIMEOUT) {
  481.             if (Firstsec)
  482.                 goto humbug;
  483. bilge:
  484.             log( "Timeout\n");
  485.         }
  486.         else
  487.             log( "Got 0%o sector header\n", firstch);
  488.  
  489. humbug:
  490.         Lastrx=0;
  491.         while(readline(1)!=TIMEOUT)
  492.             ;
  493.         if (Firstsec)
  494.             sendline(Crcflg?WANTCRC:NAK);
  495.         else {
  496.             maxtime=40; sendline(NAK);
  497.         }
  498.     }
  499.     /* try to stop the bubble machine. */
  500.     canit();
  501.     return ERROR;
  502. }
  503.  
  504. /*
  505.  * This version of readline is reasoably well suited for
  506.  * reading many characters.
  507.  *  (except, currently, for the Regulus version!)
  508.  *
  509.  * timeout is in tenths of seconds
  510.  */
  511. readline(timeout)
  512. int timeout;
  513. {
  514.     register n;
  515.     static char *cdq;    /* pointer for removing chars from linbuf */
  516.  
  517.     if (--Lleft >= 0) {
  518.         if (Verbose > 8) {
  519.             fprintf(stderr, "%02x ", *cdq&0377);
  520.         }
  521.         return (*cdq++ & Wcsmask);
  522.     }
  523.     n = timeout/10;
  524.     if (n < 2)
  525.         n = 3;
  526.     if (Verbose > 3)
  527.         fprintf(stderr, "Calling read: n=%d ", n);
  528.     if (setjmp(tohere)) {
  529. #ifdef TIOCFLUSH
  530. /*        ioctl(iofd, TIOCFLUSH, 0); */
  531. #endif
  532.         Lleft = 0;
  533.         if (Verbose>1)
  534.             fprintf(stderr, "Readline:TIMEOUT\n");
  535.         return TIMEOUT;
  536.     }
  537.     signal(SIGALRM, alrm); alarm(n);
  538. #ifdef ONEREAD
  539.     /* Sorry, Regulus and some others don't work right in raw mode! */
  540.     Lleft=read(iofd, cdq=linbuf, 1);
  541. #else
  542.     Lleft=read(iofd, cdq=linbuf, KSIZE);
  543. #endif
  544.     alarm(0);
  545.     if (Verbose > 3) {
  546.         fprintf(stderr, "Read returned %d bytes\n", Lleft);
  547.     }
  548.     if (Lleft < 1)
  549.         return TIMEOUT;
  550.     --Lleft;
  551.     if (Verbose > 8) {
  552.         fprintf(stderr, "%02x ", *cdq&0377);
  553.     }
  554.     return (*cdq++ & Wcsmask);
  555. }
  556.  
  557.  
  558.  
  559.  
  560. purgeline()
  561. {
  562.     Lleft = 0;
  563. #ifdef USG
  564.     ioctl(iofd, TCFLSH, 0);
  565. #else
  566.     lseek(iofd, 0L, 2);
  567. #endif
  568. }
  569.  
  570. /* update CRC */
  571. unsigned short
  572. updcrc(c, crc)
  573. register c;
  574. register unsigned crc;
  575. {
  576.     register count;
  577.  
  578.     for (count=8; --count>=0;) {
  579.         if (crc & 0x8000) {
  580.             crc <<= 1;
  581.             crc += (((c<<=1) & 0400)  !=  0);
  582.             crc ^= 0x1021;
  583.         }
  584.         else {
  585.             crc <<= 1;
  586.             crc += (((c<<=1) & 0400)  !=  0);
  587.         }
  588.     }
  589.     return crc;    
  590. }
  591.  
  592. /*
  593.  * process incoming header
  594.  */
  595. procheader(name)
  596. char *name;
  597. {
  598.     register char *openmode, *p, **pp;
  599.  
  600.     /* set default parameters */
  601.     openmode = "w"; Thisbinary=Rxbinary;
  602.     Bytesleft = DEFBYTL; Filemode = 0; Modtime = 0L;
  603.  
  604.     p = name + 1 + strlen(name);
  605.     if (*p) {    /* file coming from Unix type system */
  606.         sscanf(p, "%ld%lo%o", &Bytesleft, &Modtime, &Filemode);
  607.         if (Filemode & UNIXFILE)
  608.             ++Thisbinary;
  609.         if (Verbose) {
  610.             fprintf(stderr,  "Incoming: %s %ld %lo %o\n",
  611.               name, Bytesleft, Modtime, Filemode);
  612.         }
  613.     }
  614.     else {        /* File coming from CP/M type system */
  615.         for (p=name; *p; ++p)        /* change / to _ */
  616.             if ( *p == '/')
  617.                 *p = '_';
  618.  
  619.         if ( *--p == '.')        /* zap trailing period */
  620.             *p = 0;
  621.     }
  622.  
  623.     /* scan for extensions that signify a binary file */
  624.     if (p=substr(name, "."))
  625.         for (pp=Extensions; **pp; ++pp)
  626.             if (strcmp(p, *pp)==0) {
  627.                 Thisbinary=TRUE; break;
  628.             }
  629.  
  630.     /* scan for files which should be appended */
  631.     if ( !Thisbinary
  632.       && (substr(name, ".TXT")
  633.       || substr(name, ".txt")
  634.       || substr(name, ".MSG")))
  635.         openmode = "a";
  636.     if (MakeLCPathname && !IsAnyLower(name))
  637.         uncaps(name);
  638.     if (Topipe) {
  639.         sprintf(Pathname, "%s %s", Progname+2, name);
  640.         if (Verbose)
  641.             fprintf(stderr,  "Topipe: %s %s\n",
  642.               Pathname, Thisbinary?"BIN":"ASCII");
  643.         if ((fout=popen(Pathname, "w")) == NULL)
  644.             return ERROR;
  645.     } else {
  646.         strcpy(Pathname, name);
  647.         if (Verbose) {
  648.             fprintf(stderr,  "Receiving %s %s %s\n",
  649.               name, Thisbinary?"BIN":"ASCII", openmode);
  650.         }
  651.         if ((fout=fopen(name, openmode)) == NULL)
  652.             return ERROR;
  653.     }
  654.     return OK;
  655. }
  656.  
  657. /* make string s lower case */
  658. uncaps(s)
  659. register char *s;
  660. {
  661.     for ( ; *s; ++s)
  662.         if (isupper(*s))
  663.             *s = tolower(*s);
  664. }
  665.  
  666.  
  667. /*
  668.  * IsAnyLower returns TRUE if string s has lower case letters.
  669.  */
  670. IsAnyLower(s)
  671. register char *s;
  672. {
  673.     for ( ; *s; ++s)
  674.         if (islower(*s))
  675.             return TRUE;
  676.     return FALSE;
  677. }
  678. /*
  679.  * putsec writes the n characters of buf to receive file fout.
  680.  *  If not in binary mode, carriage returns, and all characters
  681.  *  starting with CPMEOF are discarded.
  682.  */
  683. putsec(buf, n)
  684. char *buf;
  685. register n;
  686. {
  687.     register char *p;
  688.  
  689.     ++totblocks;
  690.     if (Thisbinary)
  691.     {
  692.         for (p=buf; --n>=0; )
  693.             putc( *p++, fout);
  694.     }
  695.     else {
  696.         if (Eofseen)
  697.             return OK;
  698.         for (p=buf; --n>=0; ++p ) {
  699.             if ( *p == '\r')
  700.                 continue;
  701.             if (*p == CPMEOF) {
  702.                 Eofseen=TRUE; return OK;
  703.             }
  704.             putc(*p ,fout);
  705.         }
  706.     }
  707.     return OK;
  708. }
  709. sendline(c)
  710. {
  711.     char d;
  712.  
  713.     d = c;
  714.     if (Verbose>2)
  715.         fprintf(stderr, "Sendline: %x\n", c);
  716.     write(1, &d, 1);
  717.     Lleft=0;    /* Do read next time ... */
  718. }
  719.  
  720.  
  721. /*
  722.  * substr(string, token) searches for token in string s
  723.  * returns pointer to token within string if found, NULL otherwise
  724.  */
  725. char *
  726. substr(s, t)
  727. register char *s,*t;
  728. {
  729.     register char *ss,*tt;
  730.     /* search for first char of token */
  731.     for (ss=s; *s; s++)
  732.         if (*s == *t)
  733.             /* compare token with substring */
  734.             for (ss=s,tt=t; ;) {
  735.                 if (*tt == 0)
  736.                     return s;
  737.                 if (*ss++ != *tt++)
  738.                     break;
  739.             }
  740.     return NULL;
  741. }
  742.  
  743. /*VARARGS1*/
  744. log(s,p,u)
  745. char *s, *p, *u;
  746. {
  747.     if (!Verbose)
  748.         return;
  749.     fprintf(stderr, "error %d: ", errors);
  750.     fprintf(stderr, s, p, u);
  751. }
  752.  
  753. /* send 10 CAN's to try to get the other end to shut up */
  754. canit()
  755. {
  756.     register n;
  757.     for (n=10; --n>=0; )
  758.         sendline(CAN);
  759. }
  760.  
  761. #ifdef REGULUS
  762. /*
  763.  * copies count bytes from s to d
  764.  * (No structure assignment in Regulus C compiler)
  765.  */
  766.  
  767. movmem(s, d, count)
  768. register char *s, *d;
  769. register count;
  770. {
  771.     while (--count >= 0)
  772.         *d++ = *s++;
  773. }
  774. #endif
  775.  
  776. /*
  777.  * return 1 iff stdout and stderr are different devices
  778.  *  indicating this program operating with a modem on a
  779.  *  different line
  780.  */
  781. fromcu()
  782. {
  783.     struct stat a, b;
  784.     fstat(1, &a); fstat(2, &b);
  785.     return (a.st_rdev != b.st_rdev);
  786. }
  787.  
  788. report(sct)
  789. int sct;
  790. {
  791.     if (Verbose>1)
  792.         fprintf(stderr,"%03d%c",sct,sct%10? ' ' : '\r');
  793. }
  794.  
  795. /*
  796.  * if called as [-][dir/../]vrbCOMMAND set Verbose to 1
  797.  * if called as [-][dir/../]rbCOMMAND set the pipe flag
  798.  */
  799. chkinvok(s)
  800. char *s;
  801. {
  802.     register char *p;
  803.  
  804.     p = s;
  805.     while (*p == '-')
  806.         s = ++p;
  807.     while (*p)
  808.         if (*p++ == '/')
  809.             s = p;
  810.     if (*s == 'v') {
  811.         Verbose=1; ++s;
  812.     }
  813.     Progname = s;
  814.     if (s[2] && s[0]=='r' && s[1]=='b')
  815.         Topipe=TRUE;
  816. }
  817.