home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume13 / xmodem3.4 / part03 < prev    next >
Encoding:
Internet Message Format  |  1988-03-13  |  34.1 KB

  1. Subject:  v13i095:  Full featured xmodem program, v3.4, Part03/03
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: Steve Grandi <grandi@noao.arizona.edu>
  7. Posting-number: Volume 13, Issue 95
  8. Archive-name: xmodem3.4/part03
  9.  
  10.  
  11.  
  12. : This is a shar archive.  Extract with sh, not csh.
  13. echo x - receive.c
  14. sed -e 's/^X//' > receive.c << '!Funky!Stuff!'
  15. X#include "xmodem.h"
  16. X
  17. X/**  receive a file  **/
  18. X
  19. X/* returns TRUE if in the midst of a batch transfer */
  20. X/* returns FALSE if no more files are coming */
  21. X
  22. X/* This routine is one HUGE do-while loop with far to many indented levels.
  23. X * I chose this route to facilitate error processing and to avoid GOTOs.
  24. X * Given the troubles I've had keeping the nested IF statements straight,
  25. X * I was probably mistaken...
  26. X */
  27. X
  28. Xrfile(name)
  29. Xchar *name;
  30. X    {
  31. X
  32. X    char *sectdisp();
  33. X    char *cpm_unix();
  34. X    char *strcpy();
  35. X    char *ctime();
  36. X    time_t time();
  37. X
  38. X    int fd,     /* file descriptor for created file */
  39. X    checksum,   /* packet checksum */
  40. X    firstchar,  /* first character of a packet */
  41. X    sectnum,    /* number of last received packet (modulo 128) */
  42. X    sectcurr,   /* second byte of packet--should be packet number (mod 128) */
  43. X    sectcomp,   /* third byte of packet--should be complement of sectcurr */
  44. X    tmode,      /* text mode if true, binary mode if false */
  45. X    errors,     /* running count of errors (reset when 1st packet starts */
  46. X    errorflag,  /* set true when packet (or first char of putative packet) is invalid */
  47. X    fatalerror, /* set within main "read-packet" Do-While when bad error found */
  48. X    inchecksum, /* incoming checksum or CRC */
  49. X    expsect,    /* expected number of sectors (YMODEM batch) */
  50. X    firstwait,  /* seconds to wait for first character in a packet */
  51. X    bufsize;    /* packet size (128 or 1024) */
  52. X    long recvsectcnt;   /* running sector count (128 byte sectors) */
  53. X    long modtime;   /* Unix style file mod time from YMODEM header */
  54. X    int filemode; /* Unix style file mode from YMODEM header */
  55. X    long readbackup;    /* "backup" value for characters read in file */
  56. X    time_t timep[2];    /* used in setting mod time of received file */
  57. X    char *p;    /* generic pointer */
  58. X    int bufctr; /* number of real chars in read packet */
  59. X    unsigned char *nameptr; /* ptr in filename for MODEM7 protocol */
  60. X    time_t start;   /* starting time of transfer */
  61. X    int openflag = FALSE;   /* is file open for writing? */
  62. X
  63. X    logit("----\nXMODEM File Receive Function\n");
  64. X    if (CRCMODE)
  65. X        logit("CRC mode requested\n");
  66. X
  67. X    BATCH = FALSE;          /* don't know if really are in batch mode ! */
  68. X    fatalerror = FALSE;
  69. X    firstwait = WAITFIRST;  /* For first packet, wait short time */
  70. X    sectnum = errors = recvsectcnt = 0;
  71. X    bufsize = 128;
  72. X    modtime = 0l; filemode = 0;
  73. X    filelength = 0l; fileread =0l; CHECKLENGTH = FALSE;
  74. X
  75. X    tmode = (XMITTYPE == 't') ? TRUE : FALSE;
  76. X
  77. X    /* start up transfer */
  78. X    if (CRCMODE)        
  79. X    {
  80. X        sendbyte(CRCCHR);
  81. X    if (LONGPACK && !MDM7BAT)
  82. X        sendbyte(KCHR);
  83. X    }
  84. X    else
  85. X        sendbyte(NAK);
  86. X
  87. X
  88. X    do                  /* start of MAIN Do-While loop to read packets */
  89. X        {   
  90. X        errorflag = FALSE;
  91. X        do              /* start by reading first byte in packet */
  92. X            {
  93. X            firstchar = readbyte(firstwait);
  94. X            } 
  95. X            while ((firstchar != SOH) 
  96. X                && (firstchar != STX) 
  97. X                && (firstchar != EOT) 
  98. X                && (firstchar != ACK || recvsectcnt > 0) 
  99. X                && (firstchar != TIMEOUT) 
  100. X                && (firstchar != CAN || recvsectcnt > 0));
  101. X
  102. X        if (firstchar == EOT)           /* check for REAL EOT */
  103. X            {
  104. X            sendbyte(NAK);                /* NAK the EOT */
  105. X            if ((firstchar = readbyte(3)) != EOT)    /* check next character */
  106. X                {
  107. X                logit("Spurious EOT detected; ignored\n");
  108. X                if ((firstchar == SOH) || (firstchar == STX) ||
  109. X                    (firstchar == ACK && recvsectcnt == 0) ||
  110. X                    (firstchar == CAN && recvsectcnt == 0) ||
  111. X                    (firstchar == TIMEOUT))
  112. X                        break;
  113. X                else
  114. X                    {
  115. X                    firstchar = 0;
  116. X                    errorflag = TRUE;
  117. X                    }
  118. X                }
  119. X            }
  120. X
  121. X        if (firstchar == TIMEOUT)       /* timeout? */
  122. X            {  
  123. X            if (recvsectcnt > 0)
  124. X                logitarg("Timeout on Sector %s\n", sectdisp(recvsectcnt,bufsize,1));
  125. X            errorflag = TRUE;
  126. X            }
  127. X
  128. X        if (firstchar == CAN)           /* bailing out? (only at beginning) */
  129. X            {
  130. X            if ((readbyte(3) & 0x7f) == CAN)
  131. X                error("Reception canceled at user's request",TRUE);
  132. X            else
  133. X                {
  134. X                errorflag = TRUE;
  135. X                logit("Received single CAN character\n");
  136. X                }
  137. X            }
  138. X
  139. X        if (firstchar == ACK)           /* MODEM7 batch? (only at beginning) */
  140. X            {
  141. X            int i,c; 
  142. X
  143. X            logit("MODEM7 Batch Protocol\n");
  144. X            nameptr = buff;
  145. X            checksum = 0;
  146. X
  147. X            for (i=0; i<NAMSIZ; i++)
  148. X                {
  149. X                c = readbyte(3);
  150. X
  151. X                if (c == CAN)
  152. X                    {
  153. X                    if (readbyte(3) == CAN)
  154. X                        error("Program Canceled by User", TRUE);
  155. X                    else
  156. X                        {
  157. X                        logit("Received single CAN character in MODEM7 filename\n");
  158. X                        errorflag = TRUE;
  159. X                        break;
  160. X                        }
  161. X                    }
  162. X
  163. X                if (c == EOT && i == 0)
  164. X                    {
  165. X            sendbyte(NAK);    /* NAK the EOT to force verification */
  166. X            if ((c=readbyte(3)) != EOT)
  167. X            {
  168. X            logit("Spurious EOT detected in MODEM7 filename\n");
  169. X            errorflag = TRUE;
  170. X            break;
  171. X            }
  172. X                    sendbyte(ACK);            /* acknowledge EOT */
  173. X                    while (readbyte(1) != TIMEOUT)    /* flush garbage */
  174. X                        ;
  175. X                    logit("MODEM7 Batch Receive Complete\n");
  176. X                    return (FALSE);
  177. X                    }
  178. X
  179. X                if (c == TIMEOUT)
  180. X                    {
  181. X                    logit("Timeout waiting for MODEM7 filename character\n");
  182. X                    errorflag = TRUE;
  183. X                    break;
  184. X                    }
  185. X
  186. X                if (c == BAD_NAME)
  187. X                    {
  188. X                    logit("Error during MODEM7 filename transfer\n");
  189. X                    errorflag = TRUE;
  190. X                    break;
  191. X                    }
  192. X
  193. X                *nameptr++ = c;
  194. X                checksum += c;
  195. X                sendbyte(ACK);
  196. X                }
  197. X
  198. X            if (!errorflag)
  199. X                {
  200. X                c = readbyte(3);
  201. X                if (c == CTRLZ)     /* OK; end of string found */
  202. X                    {
  203. X                    sendbyte(checksum + CTRLZ);
  204. X                    if (readbyte(15) == ACK)     /* file name found! */
  205. X                        {
  206. X                        xmdebug("MODEM7 file name OK");
  207. X                        *nameptr = '\000';    /* unixify the file name */
  208. X                        name = cpm_unix(buff);
  209. X                        BATCH = TRUE;
  210. X                        logitarg("MODEM7 file name: %s\n", name);
  211. X            errors = 0;        /* restart crc handshake */
  212. X            sleep(2);        /* give other side a chance */
  213. X                        }
  214. X                    else
  215. X                        {
  216. X                        logit("Checksum error in MODEM7 filename\n");
  217. X                        errorflag = TRUE;
  218. X                        }
  219. X                    }
  220. X                else
  221. X                    {
  222. X                    logit("Length error in MODEM7 fielname\n");
  223. X                    errorflag = TRUE;
  224. X                    }
  225. X                }
  226. X            }
  227. X            
  228. X
  229. X        if (firstchar == SOH || firstchar == STX)  /* start reading packet */
  230. X            {
  231. X            bufsize = (firstchar == SOH) ? 128 : 1024;
  232. X
  233. X            if (recvsectcnt == 0)           /* 1st data packet, initialize */
  234. X                {
  235. X                if (bufsize == 1024)
  236. X                    logit("1K packet mode chosen\n");
  237. X                start = time((time_t *) 0);
  238. X                errors = 0;
  239. X                firstwait = 5;
  240. X                }
  241. X
  242. X        sectcurr = readbyte(3);
  243. X        sectcomp = readbyte(3);
  244. X        if ((sectcurr + sectcomp) == 0xff)  /* is packet number checksum correct? */
  245. X            {  
  246. X            if (sectcurr == ((sectnum+1) & 0xff))   /* is packet number correct? */
  247. X                {  
  248. X                if (DEBUG)
  249. X                    fprintf(LOGFP,"DEBUG: packet %d started\n", sectnum);
  250. X
  251. X            /* Read, process and calculate checksum for a buffer of data */
  252. X
  253. X                            readbackup = fileread;
  254. X                if (readbuf(bufsize, 1, tmode, recvsectcnt, &checksum, &bufctr) != TIMEOUT) 
  255. X                                {
  256. X
  257. X            /* verify checksum or CRC */
  258. X
  259. X                if (CRCMODE) 
  260. X                                    {
  261. X                    checksum &= 0xffff;
  262. X                    inchecksum = readbyte(3);  /* get 16-bit CRC */
  263. X                    inchecksum = (inchecksum<<8) | readbyte(3);
  264. X                    }
  265. X                        
  266. X                else
  267. X                    inchecksum = readbyte(3);  /* get simple 8-bit checksum */
  268. X
  269. X                if (inchecksum == checksum) /* good checksum, hence good packet */
  270. X                    {  
  271. X                    xmdebug("checksum ok");
  272. X                    errors = 0;
  273. X                    recvsectcnt += (bufsize == 128) ? 1 : 8;
  274. X                    sectnum = sectcurr; 
  275. X
  276. X                    if (!openflag)      /* open output file if necessary */
  277. X                    {
  278. X                    openflag = TRUE;
  279. X                    if ((fd = creat(name, CREATMODE)) < 0)
  280. X                        {
  281. X                        sendbyte(CAN); sendbyte(CAN); sendbyte(CAN);
  282. X                        error("Can't create file for receive", TRUE);
  283. X                        }
  284. X                                        if (!BATCH)
  285. X                        logitarg("File Name: %s\n", name);
  286. X                    }
  287. X
  288. X                    if (write(fd, (char *) buff, bufctr) != bufctr)
  289. X                    {
  290. X                    close(fd);
  291. X                    unlink(name);
  292. X                    error("File Write Error", TRUE);
  293. X                    }
  294. X                    else
  295. X                    sendbyte(ACK);      /* ACK the received packet */
  296. X                    }
  297. X
  298. X            /* Start handling various errors and special conditions */
  299. X
  300. X                else        /* bad checksum */
  301. X                    {  
  302. X                    logitarg("Checksum Error on Sector %s:  ", sectdisp(recvsectcnt,bufsize,1));
  303. X                    logitarg("sent=%x  ", inchecksum);
  304. X                    logitarg("recvd=%x\n", checksum);
  305. X                                    fileread = readbackup;
  306. X                    errorflag = TRUE;
  307. X                    }
  308. X                                }
  309. X
  310. X                else    /* read timeout */
  311. X                {
  312. X                logitarg("Timeout while reading sector %s\n",sectdisp(recvsectcnt,bufsize,1));
  313. X                                fileread = readbackup;
  314. X                errorflag = TRUE;
  315. X                }
  316. X                }
  317. X
  318. X                        else        /* sector number is wrong OR Ymodem filename */
  319. X                            { 
  320. X                if (sectcurr == 0 && recvsectcnt == 0)  /* Ymodem file-name packet */
  321. X                {
  322. X                logit("YMODEM Batch Protocol\n");
  323. X
  324. X                /* Read and process a file-name packet */
  325. X
  326. X                if (readbuf(bufsize, 1, FALSE, recvsectcnt, &checksum, &bufctr) != TIMEOUT) 
  327. X                                    {
  328. X
  329. X                    /* verify checksum or CRC */
  330. X
  331. X                    if (CRCMODE) 
  332. X                                        {
  333. X                    checksum &= 0xffff;
  334. X                    inchecksum = readbyte(3);  /* get 16-bit CRC */
  335. X                    inchecksum = (inchecksum<<8) | readbyte(3);
  336. X                        }
  337. X                        
  338. X                    else
  339. X                    inchecksum = readbyte(3);  /* get simple 8-bit checksum */
  340. X
  341. X                    if (inchecksum == checksum) /* good checksum, hence good filename */
  342. X                    {
  343. X                    xmdebug("checksum ok");
  344. X                    strcpy(name, (char *)buff);
  345. X                                        expsect = ((buff[bufsize-1]<<8) | buff[bufsize-2]);
  346. X                    BATCH = TRUE;
  347. X                    if (strlen(name) == 0)  /* check for no more files */
  348. X                        {
  349. X                        sendbyte(ACK);      /* ACK the packet */
  350. X                        logit("YMODEM Batch Receive Complete\n");
  351. X                        return (FALSE);
  352. X                        }
  353. X                    unixify(name);       /* make filename canonical */
  354. X
  355. X                                        /* read rest of YMODEM header */
  356. X                    p = (char *)buff + strlen((char *)buff) + 1;
  357. X                    sscanf(p, "%ld%lo%o", &filelength, &modtime, &filemode);
  358. X                    logitarg("YMODEM file name: %s\n", name);
  359. X                                        fileread = 0l;
  360. X                                        if (filelength)
  361. X                                            {
  362. X                                            CHECKLENGTH = TRUE;
  363. X                                            logitarg("YMODEM file size: %ld\n", filelength);
  364. X                                            }
  365. X                                        else
  366. X                        logitarg("YMODEM estimated file length %d sectors\n", expsect);
  367. X                                        if (modtime)
  368. X                                            {
  369. X                                            logitarg("YMODEM file date: %s", ctime(&modtime));
  370. X                                            }
  371. X                                        if (filemode)
  372. X                                            logitarg("YMODEM file mode: %o", filemode);
  373. X
  374. X                    sendbyte(ACK);      /* ACK the packet */
  375. X                    }
  376. X
  377. X                    else                /* bad filename checksum */
  378. X                    {
  379. X                    logit("checksum error on filename sector\n");
  380. X                    errorflag = TRUE;
  381. X                    }
  382. X                    }
  383. X                else
  384. X                    {
  385. X                    logit("Timeout while reading filename packet\n");
  386. X                    errorflag = TRUE;
  387. X                                    }
  388. X                }
  389. X
  390. X                else if (sectcurr == sectnum)   /* duplicate sector? */
  391. X                {  
  392. X                logitarg("Duplicate sector %s flushed\n", sectdisp(recvsectcnt,bufsize,0));
  393. X                while(readbyte(3) != TIMEOUT)
  394. X                    ;
  395. X                sendbyte(ACK);
  396. X                }
  397. X                else                /* no, real phase error */
  398. X                {
  399. X                logitarg("Phase Error - Expected packet is %s\n", sectdisp(recvsectcnt,bufsize,1));
  400. X                errorflag = TRUE;
  401. X                fatalerror = TRUE;
  402. X                }
  403. X                }
  404. X            }
  405. X
  406. X        else        /* bad packet number checksum */
  407. X            {  
  408. X            logitarg("Header Sector Number Error on Sector %s\n", sectdisp(recvsectcnt, bufsize,1));
  409. X            errorflag = TRUE;
  410. X            }
  411. X
  412. X        }           /* END reading packet loop */
  413. X    
  414. X    if ((errorflag && !fatalerror) ||        /* check on errors or batch transfers */
  415. X      ((recvsectcnt == 0) && (firstchar != EOT)))    
  416. X        {  
  417. X        if (errorflag)
  418. X            errors++;
  419. X        if (recvsectcnt != 0)
  420. X        while (readbyte(3) != TIMEOUT)  /* wait for line to settle if not beginning */
  421. X            ;
  422. X
  423. X        if (recvsectcnt == 0 && errors >= STERRORMAX)
  424. X        fatalerror = TRUE;
  425. X        else if (CRCMODE && recvsectcnt == 0 && errors == CRCSWMAX)
  426. X        {
  427. X        CRCMODE = FALSE;
  428. X        logit("Sender not accepting CRC request, changing to checksum\n");
  429. X        sendbyte(NAK);
  430. X        }
  431. X        else if (!CRCMODE && recvsectcnt == 0 && errors == CRCSWMAX)
  432. X        {
  433. X        CRCMODE = TRUE;
  434. X        logit("Sender not accepting checksum request, changing to CRC\n");
  435. X        sendbyte(CRCCHR);
  436. X        }
  437. X        else if (CRCMODE && recvsectcnt == 0)
  438. X        sendbyte(CRCCHR);
  439. X        else
  440. X        sendbyte(NAK);
  441. X        }
  442. X
  443. X        if (errors > ERRORMAX && recvsectcnt != 0)  /* over error limit? */
  444. X            fatalerror = TRUE;
  445. X    }
  446. X        while ((firstchar != EOT) && !fatalerror);   /* end of MAIN Do-While */
  447. X
  448. X    if ((firstchar == EOT) && !fatalerror)  /* normal exit? */
  449. X        {
  450. X        if (openflag)       /* close the file */
  451. X            close(fd);
  452. X        sendbyte(ACK);    /* ACK the EOT */
  453. X        logit("Receive Complete\n");
  454. X        prtime (recvsectcnt, time((time_t *) 0) - start);
  455. X
  456. X            if (openflag && modtime)   /* set file modification time */
  457. X                {
  458. X                timep[0] = time((time_t *) 0);
  459. X                timep[1] = modtime;
  460. X                utime(name, timep);
  461. X                }
  462. X        
  463. X        if (BATCH)          /* send appropriate return code */
  464. X        return(TRUE);
  465. X        else
  466. X        return(FALSE);
  467. X        }
  468. X    else                /* no, error exit */
  469. X        { 
  470. X        if (recvsectcnt != 0)
  471. X        sendbyte(CAN); sendbyte(CAN); sendbyte(CAN); sendbyte(CAN); sendbyte(CAN);
  472. X        if (openflag)
  473. X            {
  474. X            close(fd);
  475. X            unlink(name);
  476. X            }
  477. X        error("ABORTED -- Too Many Errors--deleting file", TRUE);
  478. X        return (FALSE);
  479. X        }
  480. X    }
  481. !Funky!Stuff!
  482. echo x - misc.c
  483. sed -e 's/^X//' > misc.c << '!Funky!Stuff!'
  484. X#include "xmodem.h"
  485. X
  486. X/*  Print Help Message  */
  487. Xhelp()
  488. X    {
  489. X    printf("\nUsage:  \n\txmodem ");
  490. X    printf("-[rb!rt!sb!st][options] filename\n");
  491. X    printf("\nMajor Commands --");
  492. X    printf("\n\trb <-- Receive Binary");
  493. X    printf("\n\trt <-- Receive Text");
  494. X    printf("\n\tsb <-- Send Binary");
  495. X    printf("\n\tst <-- Send Text");
  496. X    printf("\nOptions --");
  497. X    printf("\n\ty  <-- Use YMODEM Batch Mode on transmit");
  498. X    printf("\n\tm  <-- Use MODEM7 Batch Mode on transmit");
  499. X    printf("\n\tk  <-- Use 1K packets on transmit");
  500. X    printf("\n\tc  <-- Select CRC mode on receive");
  501. X    printf("\n\tt  <-- Indicate a TOO BUSY Unix system");
  502. X    printf("\n\td  <-- Delete xmodem.log file before starting");
  503. X    printf("\n\tl  <-- (ell) Turn OFF Log File Entries");
  504. X    printf("\n\tx  <-- Include copious debugging information in log file");
  505. X    printf("\n");
  506. X    }
  507. X
  508. X/* get type of transmission requested (text or binary) */
  509. Xgettype(ichar)
  510. Xchar ichar;
  511. X    {
  512. X    if (ichar == 't' || ichar == 'T') return('t');
  513. X    if (ichar == 'b' || ichar == 'B') return('b');
  514. X    error("Invalid Send/Receive Parameter - not t or b", FALSE);
  515. X    return(0);
  516. X    }
  517. X
  518. X/* print error message and exit; if mode == TRUE, restore normal tty modes */
  519. Xerror(msg, mode)
  520. Xchar *msg;
  521. Xint mode;
  522. X    {
  523. X    if (mode)
  524. X        restoremodes(TRUE);  /* put back normal tty modes */
  525. X    printf("\r\n%s\n", msg);
  526. X    if ((LOGFLAG || DEBUG) && (LOGFP != NULL))
  527. X        {   
  528. X        fprintf(LOGFP, "XMODEM Fatal Error:  %s\n", msg);
  529. X            fclose(LOGFP);
  530. X        }
  531. X    exit(-1);
  532. X    }
  533. X
  534. X
  535. X/* Construct a proper (i.e. pretty) sector count for messages */
  536. X
  537. Xchar
  538. X*sectdisp(recvsectcnt, bufsize, plus1)
  539. Xlong recvsectcnt;
  540. Xint bufsize, plus1;
  541. X    {
  542. X    static char string[20];
  543. X    if (plus1)
  544. X        recvsectcnt += (bufsize == 128) ? 1 : 8;
  545. X    if (bufsize == 128 || recvsectcnt == 0)
  546. X        sprintf (string, "%d", recvsectcnt);
  547. X    else
  548. X        sprintf (string, "%d-%d", recvsectcnt-7, recvsectcnt);
  549. X    return(string);
  550. X    }
  551. X
  552. X/* type out debugging info */
  553. Xxmdebug(str)
  554. Xchar *str;
  555. X    {
  556. X    if (DEBUG && (LOGFP != NULL))
  557. X        fprintf(LOGFP,"DEBUG: '%s'\n",str);
  558. X    }
  559. X
  560. X/* print elapsed time and rate of transfer in logfile */
  561. X
  562. Xint quant[] = { 60, 60, 24};    
  563. Xchar sep[3][10] = { "second", "minute", "hour" };
  564. X
  565. Xprtime (numsect, seconds)
  566. Xlong numsect;
  567. Xtime_t seconds;
  568. X
  569. X{
  570. X    register int i;
  571. X    register int Seconds;
  572. X    int nums[3];
  573. X    int rate;
  574. X
  575. X    if (!LOGFLAG || numsect == 0)
  576. X        return(0);
  577. X
  578. X    Seconds = (int)seconds;
  579. X    Seconds = (Seconds > 0) ? Seconds : 0;
  580. X
  581. X    rate = (Seconds != 0) ? 128 * numsect/Seconds : 0;
  582. X
  583. X    for (i=0; i<3; i++) {
  584. X        nums[i] = (Seconds % quant[i]);
  585. X        Seconds /= quant[i];
  586. X    }
  587. X
  588. X    fprintf (LOGFP, "%ld Sectors Transfered in ", numsect);
  589. X
  590. X    if (rate == 0)
  591. X        fprintf (LOGFP, "0 seconds");
  592. X    else
  593. X        while (--i >= 0)
  594. X            if (nums[i])
  595. X                fprintf (LOGFP, "%d %s%c ", nums[i], &sep[i][0],
  596. X                    nums[i] == 1 ? ' ' : 's');
  597. X    fprintf (LOGFP, "\n");
  598. X
  599. X    if (rate != 0)
  600. X        fprintf (LOGFP, "Transfer Rate = %d Characters per Second\n", rate);
  601. X
  602. X    return(0);
  603. X}
  604. X
  605. X/* Print elapsed time estimate */
  606. X
  607. Xprojtime (numsect, fd)
  608. Xlong numsect;
  609. XFILE *fd;
  610. X    {
  611. X    register int i;
  612. X    register int seconds;
  613. X    int nums[3];
  614. X
  615. X    if (numsect == 0)
  616. X        return (0);
  617. X
  618. X/* constant below should really be 1280; reduced to 90% to account for time lost in overhead */
  619. X
  620. X    seconds = 1422 * numsect / ttyspeed + 1;
  621. X
  622. X    for (i=0; i<3; i++) {
  623. X        nums[i] = (seconds % quant[i]);
  624. X        seconds /= quant[i];
  625. X    }
  626. X
  627. X    fprintf (fd, "Estimated transmission time ");
  628. X
  629. X    while (--i >= 0)
  630. X        if (nums[i])
  631. X            fprintf (fd, "%d %s%c ", nums[i], &sep[i][0],
  632. X                nums[i] == 1 ? ' ' : 's');
  633. X    fprintf (fd, "\n");
  634. X    return (0);
  635. X    }
  636. !Funky!Stuff!
  637. echo x - tip.diffs
  638. sed -e 's/^X//' > tip.diffs << '!Funky!Stuff!'
  639. X
  640. X*** cmdtab.c.ORIG    Mon May  5 11:01:59 1986
  641. X--- cmdtab.c    Thu Aug 21 10:08:50 1986
  642. X***************
  643. X*** 15,16 ****
  644. X--- 15,17 ----
  645. X  extern    int cu_take(), cu_put(), dollar(), genbrk(), suspend();
  646. X+ extern    int rtfile(), stfile(), rbfile(), sbfile();
  647. X  
  648. X***************
  649. X*** 35,36 ****
  650. X--- 36,41 ----
  651. X      { '#',    NORM,    "send break",             genbrk },
  652. X+     { '{',    NORM,    "receive xmodem text file",      rtfile },
  653. X+     { '}',    NORM,    "send xmodem text file",      stfile },
  654. X+     { '(',    NORM,    "receive xmodem binary file",     rbfile },
  655. X+     { ')',    NORM,    "send xmodem binary file",      sbfile },
  656. X      { 0, 0, 0 }
  657. X
  658. X
  659. X
  660. X*** cmds.c.ORIG    Mon May  5 11:01:56 1986
  661. X--- cmds.c    Tue Sep 30 10:17:11 1986
  662. X***************
  663. X*** 855,856 ****
  664. X--- 865,1412 ----
  665. X      return(0);
  666. X+ }
  667. X+ 
  668. X+ 
  669. X+ /*  XMODEM stuff; sag sept 1984    - Feb 1985
  670. X+  *    Taken from Brian Kantor's (sdccsu3!brian) xmodem version 1.0
  671. X+  *    Note that this code will work on version 4.2 ONLY (uses select)
  672. X+  */
  673. X+ 
  674. X+ #include <sys/stat.h>
  675. X+ #include <sys/time.h>
  676. X+ 
  677. X+ /*  ASCII Constants  */
  678. X+ #define      SOH      001 
  679. X+ #define         STX    002
  680. X+ #define         ETX    003
  681. X+ #define      EOT    004
  682. X+ #define         ENQ    005
  683. X+ #define      ACK      006
  684. X+ #define         LF        012   /* Unix LF/NL */
  685. X+ #define         CR        015  
  686. X+ #define      NAK      025
  687. X+ #define         SYN    026
  688. X+ #define         CAN    030
  689. X+ #define         ESC    033
  690. X+ #define         CTRLZ    032   /* CP/M EOF for text (usually!) */
  691. X+ 
  692. X+ /*  XMODEM Constants  */
  693. X+ #define      TIMEOUT      -1
  694. X+ #define      ERRORMAX      10    /* maximum errors tolerated */
  695. X+ #define      RETRYMAX      10    /* maximum retries to be made */
  696. X+ #define         BBUFSIZ    128   /* buffer size -- do not change! */
  697. X+ #define      DEBUG    0     /* 1 for debugging output */
  698. X+ #define      GETERR    -10   /* error code for getbyte routine */
  699. X+ 
  700. X+ char buff[BBUFSIZ];
  701. X+ 
  702. X+ int nbchr;    /* number of chars read so far for buffered read */
  703. X+ 
  704. X+ /* Receive a text file */
  705. X+ rtfile(c)
  706. X+ char c;
  707. X+ {
  708. X+     putchar (c);
  709. X+     rfile ('t');
  710. X+ }
  711. X+ 
  712. X+ /* Receive a binary file */
  713. X+ rbfile(c)
  714. X+ char c;
  715. X+ {
  716. X+     putchar (c);
  717. X+     rfile ('b');
  718. X+ }
  719. X+ 
  720. X+ /* Send a text file */
  721. X+ stfile(c)
  722. X+ char c;
  723. X+ {
  724. X+     putchar (c);
  725. X+     sfile ('t');
  726. X+ }
  727. X+ 
  728. X+ /* Send a binary file */
  729. X+ sbfile(c)
  730. X+ char c;
  731. X+ {
  732. X+     putchar (c);
  733. X+     sfile ('b');
  734. X+ }
  735. X+ 
  736. X+ /* print error message and cleanup for exit */
  737. X+ error(msg)
  738. X+ char *msg;
  739. X+     {
  740. X+     printf("\r\nXMODEM:  %s\n", msg);
  741. X+     ioctl (0, TIOCSETC, &tchars);
  742. X+     write (fildes[1], (char *)&ccc, 1);
  743. X+     signal (SIGINT, SIG_DFL);
  744. X+     }
  745. X+ 
  746. X+ /*
  747. X+  *
  748. X+  *    Get a byte from the specified file.  Buffer the read so we don't
  749. X+  *    have to use a system call for each character.
  750. X+  *
  751. X+  */
  752. X+ getbyte(fildes, ch)                /* Buffered disk read */
  753. X+ int fildes;
  754. X+ char *ch;
  755. X+ 
  756. X+     {
  757. X+     static char buf[BUFSIZ];    /* Remember buffer */
  758. X+     static char *bufp = buf;    /* Remember where we are in buffer */
  759. X+     
  760. X+     if (nbchr == 0)            /* Buffer exausted; read some more */
  761. X+         {
  762. X+         if ((nbchr = read(fildes, buf, BUFSIZ)) < 0)
  763. X+             {
  764. X+             error("File Read Error");
  765. X+             return (GETERR);
  766. X+              }
  767. X+         bufp = buf;        /* Set pointer to start of array */
  768. X+         }
  769. X+     if (--nbchr >= 0)
  770. X+         {
  771. X+         *ch = *bufp++;
  772. X+         return(0);
  773. X+         }
  774. X+     else
  775. X+         return(EOF);
  776. X+     }
  777. X+ 
  778. X+ /**  receive a file  **/
  779. X+ rfile(mode)
  780. X+ char mode;
  781. X+     {
  782. X+     register int bufctr, checksum;
  783. X+     register int c;
  784. X+     int j, firstchar, sectnum, sectcurr, tmode;
  785. X+     int sectcomp, errors, errorflag, recfin;
  786. X+     int fatalerror, inchecksum;
  787. X+     long recvsectcnt;
  788. X+ 
  789. X+     int (*f) ();
  790. X+     char *cp, *expand();
  791. X+     time_t start;
  792. X+ 
  793. X+     if (prompt (" Receive local file name? ", copyname))
  794. X+         return;
  795. X+     cp = expand (copyname);
  796. X+     if ((sfd = creat (cp, 0666)) < 0)
  797. X+         {
  798. X+         printf ("\r\n%s: Cannot creat\r\n", copyname);
  799. X+         return;
  800. X+         }
  801. X+ 
  802. X+     kill (pid, SIGIOT);
  803. X+     read (repdes[0], (char *)&ccc, 1); /* wait until read process stops */
  804. X+     ioctl (0, TIOCSETC, &defchars);       /* set tty modes */
  805. X+     f = signal (SIGINT, intcopy);       /* intercept control-c */
  806. X+ 
  807. X+     start = time(0);
  808. X+     quit = 0;
  809. X+     (void) setjmp(intbuf);           /* set control-c catcher */
  810. X+ 
  811. X+     if (quit)
  812. X+         {
  813. X+         error("Control-C; Reception canceled");
  814. X+         close (sfd);
  815. X+         return;
  816. X+         }
  817. X+ 
  818. X+     printf("XMODEM:  Ready to RECEIVE File %s", cp);
  819. X+     puts("\r\nControl-C to cancel.\n");
  820. X+ 
  821. X+     recfin = FALSE;
  822. X+     sectnum = errors = 0;
  823. X+     fatalerror = FALSE;  /* NO fatal errors */
  824. X+     recvsectcnt = 0;  /* number of received sectors */
  825. X+ 
  826. X+     if (mode == 't')
  827. X+         tmode = TRUE;
  828. X+     else
  829. X+         tmode = FALSE;
  830. X+ 
  831. X+     sendbyte(NAK);  /* Start up the sender's first block */
  832. X+ 
  833. X+         do
  834. X+             {   
  835. X+         errorflag = FALSE;
  836. X+                 do 
  837. X+             {
  838. X+                       firstchar = readbyte(6);
  839. X+                     } 
  840. X+             while ((firstchar != SOH) 
  841. X+                 && (firstchar != EOT) 
  842. X+                 && (firstchar != TIMEOUT) 
  843. X+                 && ((firstchar & 0x7f) != CAN));
  844. X+ 
  845. X+                 if (firstchar == TIMEOUT)
  846. X+                 {  
  847. X+             printf("\r\nTimeout on Sector %d\n", sectnum+1);
  848. X+                        errorflag = TRUE;
  849. X+                 }
  850. X+ 
  851. X+                 if ((firstchar & 0x7f) == CAN)
  852. X+                 {  
  853. X+             error("Reception canceled at user's request");
  854. X+             close (sfd);
  855. X+             return;
  856. X+                 }
  857. X+ 
  858. X+                 if (firstchar == SOH)
  859. X+                {
  860. X+                        sectcurr = readbyte(3);       /* get sector numbers */
  861. X+                        sectcomp = readbyte(3);
  862. X+                        if ((sectcurr + sectcomp) == 0xff)
  863. X+                            {  
  864. X+                 if (sectcurr == ((sectnum+1) & 0xff))
  865. X+                      {  
  866. X+                     checksum = 0;
  867. X+                          for (j = bufctr = 0; j < BBUFSIZ; j++)
  868. X+                                    {  
  869. X+                         buff[bufctr] = c = readbyte(3);
  870. X+                             checksum = ((checksum+c) & 0xff);
  871. X+                         if (!tmode)  /* binary mode */
  872. X+                             {  
  873. X+                             bufctr++;
  874. X+                                    continue;
  875. X+                                 }
  876. X+                         if (c == CR)
  877. X+                                continue;  /* skip CR's */
  878. X+                         if (c == CTRLZ)  /* CP/M EOF char */
  879. X+                             {  
  880. X+                             recfin = TRUE;  /* flag EOF */
  881. X+                                    continue;
  882. X+                                 }
  883. X+                             if (!recfin)
  884. X+                                bufctr++;
  885. X+                              }
  886. X+                          inchecksum = readbyte(3);  /* get checksum */
  887. X+                         if (checksum == inchecksum)  /* good checksum */
  888. X+                              {  
  889. X+                         errors = 0;
  890. X+                         recvsectcnt++;
  891. X+                             sectnum = sectcurr; 
  892. X+                         if (DEBUG) printf ("\n");
  893. X+                         printf ("\rreceived sector %-4d", recvsectcnt);
  894. X+                         if (write(sfd, buff, bufctr) < 0) 
  895. X+                             {
  896. X+                             sendbyte(CAN);
  897. X+                                error("File Write Error");
  898. X+                             close (sfd);
  899. X+                             return;
  900. X+                             }
  901. X+                             else 
  902. X+                                sendbyte(ACK);
  903. X+ 
  904. X+                              }
  905. X+                          else
  906. X+                              {  
  907. X+                         printf("\r\nChecksum Error on Sector %d\n", sectnum+1);
  908. X+                             errorflag = TRUE;
  909. X+                              }
  910. X+                               }
  911. X+                           else
  912. X+                               { 
  913. X+                     if (sectcurr == sectnum)
  914. X+                                     {  
  915. X+                         while(readbyte(3) != TIMEOUT)
  916. X+                             ;
  917. X+                                        sendbyte(ACK);
  918. X+                                     }
  919. X+                                 else
  920. X+                             {  
  921. X+                         printf("\r\nPhase Error - Received Sector is ");
  922. X+                           printf("%d while Expected Sector is %d\n",
  923. X+                                sectcurr, ((sectnum+1) & 0xff));
  924. X+                         errorflag = TRUE;
  925. X+                         fatalerror = TRUE;
  926. X+                         sendbyte(CAN);
  927. X+                             }
  928. X+                           }
  929. X+                        }
  930. X+             else
  931. X+                    {  
  932. X+                 printf("\r\nHeader Sector Number Error on Sector %d\n",
  933. X+                        sectnum+1);
  934. X+                            errorflag = TRUE;
  935. X+                    }
  936. X+                 }
  937. X+     
  938. X+             if (errorflag)
  939. X+                 {  
  940. X+             errors++;
  941. X+                while (readbyte(3) != TIMEOUT)
  942. X+                 ;
  943. X+             sendbyte(NAK);
  944. X+                 }
  945. X+           }
  946. X+           while ((firstchar != EOT) && (errors < ERRORMAX) && !fatalerror);
  947. X+ 
  948. X+       if ((firstchar == EOT) && (errors < ERRORMAX) && !fatalerror)
  949. X+           {
  950. X+              close(sfd);
  951. X+         sendbyte(ACK);
  952. X+         printf("\n\rReceive Complete");
  953. X+         printf("\n\r%ld CP/M Records ", recvsectcnt);
  954. X+         prtime(" transferred in ", time(0)-start);
  955. X+ 
  956. X+         ioctl (0, TIOCSETC, &tchars);        /* reset ttys */
  957. X+         write (fildes[1], (char *)&ccc, 1);    /* wakeup tip */
  958. X+         signal (SIGINT, SIG_DFL);        /* reset control-c catcher */
  959. X+           }
  960. X+       else
  961. X+           { 
  962. X+         sendbyte(CAN);
  963. X+              error("\r\nABORTED -- Too Many Errors");
  964. X+         close (sfd);
  965. X+         return;
  966. X+           }
  967. X+     }
  968. X+ 
  969. X+ /**  send a file  **/
  970. X+ sfile(mode)
  971. X+ char mode;
  972. X+     {
  973. X+     register int bufctr, checksum, sectnum;
  974. X+     char blockbuf[134];
  975. X+     int fd, attempts;
  976. X+     int nlflag, sendfin, tmode;
  977. X+     int bbufcnt;
  978. X+     int firstchar;
  979. X+     int getretrn;
  980. X+     char c;
  981. X+     int sendresp;  /* response char to sent block */
  982. X+ 
  983. X+     int (*f) ();
  984. X+     char *cp, *expand();
  985. X+     time_t start;
  986. X+ 
  987. X+     if (prompt (" Send local file name? ", copyname))
  988. X+         return;
  989. X+     cp = expand (copyname);
  990. X+     if ((fd = open(cp, 0)) < 0)
  991. X+         {  
  992. X+                 printf("Can't open file for send\n");
  993. X+         return;
  994. X+         }
  995. X+ 
  996. X+     kill (pid, SIGIOT);
  997. X+     read (repdes[0], (char *)&ccc, 1); /* wait until read process stops */
  998. X+     ioctl (0, TIOCSETC, &defchars);    /* setup tty */
  999. X+     f = signal (SIGINT, intcopy);       /* prepare to catch control-c */
  1000. X+     start = time(0);
  1001. X+     quit = 0;
  1002. X+     (void) setjmp(intbuf);           /* setup control-c catcher */
  1003. X+ 
  1004. X+     nbchr = 0;  /* clear buffered read char count */
  1005. X+         
  1006. X+     if (quit)
  1007. X+         {
  1008. X+         sendbyte(CAN);
  1009. X+         error("Control-C; Send canceled");
  1010. X+         close (fd);
  1011. X+         return;
  1012. X+         }
  1013. X+ 
  1014. X+     printf("XMODEM:  File %s Ready to SEND", cp);
  1015. X+     prfilestat(cp);  /* print file size statistics */
  1016. X+     puts("\r\nControl-C to cancel.\n");
  1017. X+ 
  1018. X+     if (mode == 't')
  1019. X+        tmode = TRUE;
  1020. X+     else
  1021. X+        tmode = FALSE;
  1022. X+ 
  1023. X+         sendfin = nlflag = FALSE;
  1024. X+       attempts = 0;
  1025. X+ 
  1026. X+     while (((firstchar=readbyte(30)) != NAK) && (firstchar != CAN))
  1027. X+         {
  1028. X+         if (++attempts > RETRYMAX) {
  1029. X+             error("Remote System Not Responding");
  1030. X+             close (fd);
  1031. X+             return;
  1032. X+         }
  1033. X+         }
  1034. X+ 
  1035. X+     if ((firstchar & 0x7f) == CAN)
  1036. X+         {
  1037. X+         error("Send cancelled at user's request.");
  1038. X+         close (fd);
  1039. X+         return;
  1040. X+         }
  1041. X+ 
  1042. X+     sectnum = 1;  /* first sector number */
  1043. X+     attempts = 0;
  1044. X+ 
  1045. X+         do 
  1046. X+         {   
  1047. X+         for (bufctr=0; bufctr < BBUFSIZ;)
  1048. X+                 {
  1049. X+             if (nlflag)
  1050. X+                     {  
  1051. X+                 buff[bufctr++] = LF;  /* leftover newline */
  1052. X+                        nlflag = FALSE;
  1053. X+                     }
  1054. X+             getretrn = getbyte(fd, &c);
  1055. X+             if (getretrn == GETERR)
  1056. X+                 {
  1057. X+                 sendbyte(CAN);
  1058. X+                 error ("Read error on local file");
  1059. X+                 close (fd);
  1060. X+                 return;
  1061. X+                 }
  1062. X+             if (getretrn == EOF)
  1063. X+                 { 
  1064. X+                 sendfin = TRUE;  /* this is the last sector */
  1065. X+                    if (!bufctr)  /* if EOF on sector boundary */
  1066. X+                           break;  /* avoid sending extra sector */
  1067. X+                    if (tmode)
  1068. X+                           buff[bufctr++] = CTRLZ;  /* Control-Z for CP/M EOF */
  1069. X+                        else
  1070. X+                           bufctr++;
  1071. X+                    continue;
  1072. X+                       }
  1073. X+ 
  1074. X+             if (tmode && c == LF)  /* text mode & Unix newline? */
  1075. X+                     {
  1076. X+                 buff[bufctr++] = CR;  /* insert carriage return */
  1077. X+                      if (bufctr < BBUFSIZ)
  1078. X+                             buff[bufctr++] = LF;  /* insert LF */
  1079. X+                        else
  1080. X+                         nlflag = TRUE;  /* insert on next sector */
  1081. X+                    }    
  1082. X+             else
  1083. X+                 buff[bufctr++] = c;  /* copy the char without change */
  1084. X+                 }
  1085. X+ 
  1086. X+                 attempts = 0;
  1087. X+     
  1088. X+             if (!bufctr)  /* if EOF on sector boundary */
  1089. X+                       break;  /* avoid sending empty sector */
  1090. X+ 
  1091. X+                 do
  1092. X+                     {
  1093. X+             bbufcnt = 0;        /* start building block to be sent */
  1094. X+             blockbuf[bbufcnt++] = SOH;        /* start of packet char */
  1095. X+             blockbuf[bbufcnt++] = sectnum;        /* current sector # */
  1096. X+             blockbuf[bbufcnt++] = -sectnum-1;   /* and its complement */
  1097. X+ 
  1098. X+                     checksum = 0;  /* init checksum */
  1099. X+                     for (bufctr=0; bufctr < BBUFSIZ; bufctr++)
  1100. X+                            {
  1101. X+                 blockbuf[bbufcnt++] = buff[bufctr];
  1102. X+                          checksum = ((checksum+buff[bufctr]) & 0xff);
  1103. X+                      }
  1104. X+ 
  1105. X+             blockbuf[bbufcnt++] = checksum;
  1106. X+             write(FD, blockbuf, 132);  /* write the block */
  1107. X+             ioctl(FD,TIOCFLUSH,0);
  1108. X+ 
  1109. X+                     attempts++;
  1110. X+             sendresp = readbyte(10);  /* get response */
  1111. X+             if (sendresp != ACK)
  1112. X+                    {
  1113. X+                    printf("\r\nNon-ACK Received on Sector %d\n",sectnum);
  1114. X+                    if (sendresp == TIMEOUT)
  1115. X+                     printf("\r\nThis Non-ACK was a TIMEOUT\n");
  1116. X+                    }
  1117. X+                     }
  1118. X+             while((sendresp != ACK) && (attempts < RETRYMAX));
  1119. X+ 
  1120. X+                sectnum++;  /* increment to next sector number */
  1121. X+         if (DEBUG) printf ("\n");
  1122. X+         printf ("\rsent sector %-4d", sectnum-1);
  1123. X+             }
  1124. X+         while (!sendfin && (attempts < RETRYMAX));
  1125. X+ 
  1126. X+         if (attempts >= RETRYMAX) 
  1127. X+         {
  1128. X+         error("Remote System Not Responding");
  1129. X+         close (fd);
  1130. X+         return;
  1131. X+         }
  1132. X+ 
  1133. X+         attempts = 0;
  1134. X+         sendbyte(EOT);  /* send 1st EOT */
  1135. X+     
  1136. X+         while ((readbyte(15) != ACK) && (attempts++ < RETRYMAX))
  1137. X+            sendbyte(EOT);
  1138. X+ 
  1139. X+         if (attempts >= RETRYMAX)
  1140. X+            error("Remote System Not Responding on Completion");
  1141. X+ 
  1142. X+         close(fd);
  1143. X+         printf("\r\nSend Complete\r\n");
  1144. X+     prtime("Data transferred in ", time(0)-start);
  1145. X+ 
  1146. X+     ioctl (0, TIOCSETC, &tchars);        /* restore tty */
  1147. X+     write (fildes[1], (char *)&ccc, 1);    /* wakeup tip */
  1148. X+     signal (SIGINT, SIG_DFL);        /* reset control-c catcher */
  1149. X+         sleep(5);  /* give other side time to return to terminal mode */
  1150. X+ 
  1151. X+     }
  1152. X+ 
  1153. X+ /*  print file size status information  */
  1154. X+ prfilestat(name)
  1155. X+ char *name;
  1156. X+     {
  1157. X+     struct stat filestatbuf; /* file status info */
  1158. X+ 
  1159. X+     stat(name, &filestatbuf);  /* get file status bytes */
  1160. X+     printf("\r\nEstimated File Size %ldK, %ld Records, %ld Bytes",
  1161. X+           (filestatbuf.st_size/1024)+1, (filestatbuf.st_size/128)+1,
  1162. X+           filestatbuf.st_size);
  1163. X+         return;
  1164. X+     }
  1165. X+ 
  1166. X+ /* get a byte from data stream -- timeout if "seconds" elapses */
  1167. X+ int readbyte(seconds)
  1168. X+ int seconds;
  1169. X+     {
  1170. X+     int i, readfd;
  1171. X+     char c;
  1172. X+     struct timeval tmout;
  1173. X+ 
  1174. X+     tmout.tv_sec = seconds;
  1175. X+     tmout.tv_usec = 0;
  1176. X+ 
  1177. X+     readfd = 1 << FD;
  1178. X+ 
  1179. X+     if ((i=select(FD+1, &readfd, 0, 0, &tmout)) == 0)
  1180. X+         {
  1181. X+         return(TIMEOUT);
  1182. X+         }
  1183. X+ 
  1184. X+     read(FD, &c, 1);
  1185. X+ 
  1186. X+     return(c & 0xff);  /* return the char */
  1187. X+     }
  1188. X+ 
  1189. X+ /* send a byte to data stream */
  1190. X+ sendbyte(data)
  1191. X+ char data;
  1192. X+     {
  1193. X+     write(FD, &data, 1);      /* write the byte */
  1194. X+     ioctl(FD,TIOCFLUSH,0);    /* flush so it really happens now! */
  1195. X+     return;
  1196. X+     }
  1197. X+ 
  1198. X+ /*
  1199. X+  * "nap" for specified time
  1200. X+  */
  1201. X+ nap (milliseconds)
  1202. X+ int    milliseconds;
  1203. X+ {
  1204. X+     struct    timeval    timeout;
  1205. X+ 
  1206. X+     if (milliseconds == 0)
  1207. X+         return;
  1208. X+     timeout.tv_sec = 0;
  1209. X+     timeout.tv_usec = milliseconds * 1000;
  1210. X+ 
  1211. X+     (void) select(1, 0, 0, 0, &timeout);
  1212. X  }
  1213. !Funky!Stuff!
  1214. exit
  1215.  
  1216.