home *** CD-ROM | disk | FTP | other *** search
/ Fish 'n' More 2 / fishmore-publicdomainlibraryvol.ii1991xetec.iso / dirs / xprzmodem_459.lzh / XprZmodem / utils.c < prev    next >
C/C++ Source or Header  |  1991-02-18  |  27KB  |  817 lines

  1. /*  Utils.c: Miscellaneous support routines for xprzmodem.library;
  2.     Version 2.10, 12 February 1991, by Rick Huebner.
  3.     Released to the Public Domain; do as you like with this code.  */
  4.  
  5.  
  6. #include <proto/all.h>
  7. #include <exec/types.h>
  8. #include <exec/memory.h>
  9. #include <ctype.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include "xproto.h"
  14. #include "zmodem.h"
  15. #include "xprzmodem.h"
  16.  
  17. /* Transfer options to use if XProtocolSetup not called */
  18. struct SetupVars Default_Config = {
  19.   NULL, NULL, 0,
  20.   { "C" }, { "N" }, { "16" }, { "0" }, { "10" },
  21.   { "N" }, { "N" }, { "Y" }, { "N" }, { "Y" }, { "" }
  22. };
  23.  
  24. #ifdef DEBUGLOG
  25. UBYTE DebugName[] = "Log:XDebug.log";
  26. void *DebugLog = NULL;
  27. #endif
  28.  
  29.  
  30. /* Called by comm program to set transfer options */
  31. long __saveds XProtocolSetup(struct XPR_IO *io) {
  32.   struct SetupVars *sv, tempsv;
  33.   struct xpr_option *option_ptrs[12], *optr, xo_hdr, xo_t, xo_o, xo_b, xo_f, xo_e, xo_s, xo_r, xo_a, xo_d, xo_k, xo_p;
  34.   UBYTE buf[256], *p;
  35.   long i, len;
  36.  
  37.   /* Allocate memory for transfer options string */
  38.   if (!(sv = (void *)io->xpr_data)) {
  39.     io->xpr_data = AllocMem((long)sizeof(struct SetupVars),MEMF_CLEAR);
  40.     if (!(sv = (void *)io->xpr_data)) {
  41.       ioerr(io,"Not enough memory");
  42.       return XPRS_FAILURE;
  43.     }
  44.     /* Start out with default options; merge user changes into defaults */
  45.     *sv = Default_Config;
  46.   }
  47.  
  48.   /* If options string passed by comm prog, use it; else prompt user */
  49.   if (io->xpr_filename)
  50.     strcpy(buf,io->xpr_filename);
  51.   else {
  52.     /* If xpr_options() implemented by comm program, use it */
  53.     if (io->xpr_extension >= 1 && io->xpr_options) {
  54.       /* Let user edit temp copy of options so we can ignore invalid entries */
  55.       /* Have to init all this crud the hard way 'cause it's got to be on the
  56.          stack in order to maintain reentrancy */
  57.       tempsv = *sv;
  58.       xo_hdr.xpro_description = "ZModem options:";
  59.       xo_hdr.xpro_type = XPRO_HEADER;
  60.       xo_hdr.xpro_value = NULL;
  61.       xo_hdr.xpro_length = 0;
  62.       option_ptrs[0] = &xo_hdr;
  63.       xo_t.xpro_description = "Text mode (Y,N,?,C):";
  64.       xo_t.xpro_type = XPRO_STRING;
  65.       xo_t.xpro_value = tempsv.option_t;
  66.       xo_t.xpro_length = sizeof(tempsv.option_t);
  67.       option_ptrs[1] = &xo_t;
  68.       xo_o.xpro_description = "Overwrite mode (Y,N,R,S):";
  69.       xo_o.xpro_type = XPRO_STRING;
  70.       xo_o.xpro_value = tempsv.option_o;
  71.       xo_o.xpro_length = sizeof(tempsv.option_o);
  72.       option_ptrs[2] = &xo_o;
  73.       xo_b.xpro_description = "I/O buffer size (KB):";
  74.       xo_b.xpro_type = XPRO_LONG;
  75.       xo_b.xpro_value = tempsv.option_b;
  76.       xo_b.xpro_length = sizeof(tempsv.option_b);
  77.       option_ptrs[3] = &xo_b;
  78.       xo_f.xpro_description = "Frame size (bytes):";
  79.       xo_f.xpro_type = XPRO_LONG;
  80.       xo_f.xpro_value = tempsv.option_f;
  81.       xo_f.xpro_length = sizeof(tempsv.option_f);
  82.       option_ptrs[4] = &xo_f;
  83.       xo_e.xpro_description = "Error limit:";
  84.       xo_e.xpro_type = XPRO_LONG;
  85.       xo_e.xpro_value = tempsv.option_e;
  86.       xo_e.xpro_length = sizeof(tempsv.option_e);
  87.       option_ptrs[5] = &xo_e;
  88.       xo_a.xpro_description = "Auto-activate receiver:";
  89.       xo_a.xpro_type = XPRO_BOOLEAN;
  90.       xo_a.xpro_value = tempsv.option_a;
  91.       xo_a.xpro_length = sizeof(tempsv.option_a);
  92.       option_ptrs[6] = &xo_a;
  93.       xo_d.xpro_description = "Delete after sending:";
  94.       xo_d.xpro_type = XPRO_BOOLEAN;
  95.       xo_d.xpro_value = tempsv.option_d;
  96.       xo_d.xpro_length = sizeof(tempsv.option_d);
  97.       option_ptrs[7] = &xo_d;
  98.       xo_k.xpro_description = "Keep partial files:";
  99.       xo_k.xpro_type = XPRO_BOOLEAN;
  100.       xo_k.xpro_value = tempsv.option_k;
  101.       xo_k.xpro_length = sizeof(tempsv.option_k);
  102.       option_ptrs[8] = &xo_k;
  103.       xo_s.xpro_description = "Send full path:";
  104.       xo_s.xpro_type = XPRO_BOOLEAN;
  105.       xo_s.xpro_value = tempsv.option_s;
  106.       xo_s.xpro_length = sizeof(tempsv.option_s);
  107.       option_ptrs[9] = &xo_s;
  108.       xo_r.xpro_description = "Use received path:";
  109.       xo_r.xpro_type = XPRO_BOOLEAN;
  110.       xo_r.xpro_value = tempsv.option_r;
  111.       xo_r.xpro_length = sizeof(tempsv.option_r);
  112.       option_ptrs[10] = &xo_r;
  113.       xo_p.xpro_description = "Default receive path:";
  114.       xo_p.xpro_type = XPRO_STRING;
  115.       xo_p.xpro_value = tempsv.option_p;
  116.       xo_p.xpro_length = sizeof(tempsv.option_p);
  117.       option_ptrs[11] = &xo_p;
  118.       /* Convert Y/N used elsewhere into "yes"/"no" required by spec */
  119.       for (i=6; i<=10; ++i) {
  120.         optr = option_ptrs[i];
  121.         strcpy(optr->xpro_value,(*optr->xpro_value == 'Y') ? "yes" : "no");
  122.       }
  123.       xpr_options(io,12L,option_ptrs);
  124.       /* Convert "yes"/"no" or "on"/"off" into Y/N */
  125.       for (i=6; i<=10; ++i) {
  126.         optr = option_ptrs[i];
  127.         strcpy(optr->xpro_value,(!stricmp(optr->xpro_value,"yes") || !stricmp(optr->xpro_value,"on")) ? "Y" : "N");
  128.       }
  129.       /* Convert xpr_options() results into parseable options string */
  130.       sprintf(buf,"T%s,O%s,B%s,F%s,E%s,A%s,D%s,K%s,S%s,R%s,P%s",tempsv.option_t,
  131.         tempsv.option_o,tempsv.option_b,tempsv.option_f,tempsv.option_e,
  132.         tempsv.option_a,tempsv.option_d,tempsv.option_k,tempsv.option_s,
  133.         tempsv.option_r,tempsv.option_p);
  134.     /* If xpr_options() not provided, try xpr_gets() instead */
  135.     } else {
  136.       /* Start buffer with current settings so user can see/edit them in place */
  137.       sprintf(buf,"T%s,O%s,B%s,F%s,E%s,A%s,D%s,K%s,S%s,R%s,P%s",sv->option_t,
  138.         sv->option_o,sv->option_b,sv->option_f,sv->option_e,
  139.         sv->option_a,sv->option_d,sv->option_k,sv->option_s,
  140.         sv->option_r,sv->option_p);
  141.       if (io->xpr_gets)
  142.         xpr_gets(io,"ZModem options:",buf);
  143.     }
  144.   }
  145.   /* Upshift options string for easier parsing */
  146.   strupr(buf);
  147.  
  148.   /* Merge new T(ext) option into current settings if given */
  149.   /* "TY" = Force Text mode on,
  150.      "TN" = Force Text mode off,
  151.      "T?" = Use other end's text mode suggestion (default to binary)
  152.      "TC" = Ask Comm program for file type */
  153.   if (p = find_option(buf,'T')) {
  154.     if (*p == 'Y' || *p == 'N' || *p == '?' || *p == 'C') *sv->option_t = *p;
  155.     else ioerr(io,"Invalid T flag ignored; should be Y, N, ?, or C");
  156.   }
  157.  
  158.   /* Merge new O(verwrite) option into current settings if given */
  159.   /* "OY" = Yes, delete old file and replace with new one,
  160.      "ON" = No, prevent overwrite by appending ".dup" to avoid name collision,
  161.      "OR" = Resume transfer at end of existing file,
  162.      "OS" = Skip file if it already exists; go on to next */
  163.   if (p = find_option(buf,'O')) {
  164.     if (*p == 'R' && !io->xpr_finfo) ioerr(io,"Can't Resume; xpr_finfo() not supported");
  165.     else if (*p == 'Y' || *p == 'N' || *p == 'R' || *p == 'S') *sv->option_o = *p;
  166.     else ioerr(io,"Invalid O flag ignored; should be Y, N, R, or S");
  167.   }
  168.  
  169.   /* Merge new B(uffer) setting into current settings if given */
  170.   /* Size of file I/O buffer in kilobytes */
  171.   if (p = find_option(buf,'B')) {
  172.     len = atol(p);
  173.     if (len < 1) len = 1;
  174.     sprintf(sv->option_b,"%ld",len);
  175.   }
  176.  
  177.   /* Merge new F(ramelength) setting into other settings if given */
  178.   /* Number of bytes we're willing to send or receive between ACKs.
  179.      0 = unlimited; nonstop streaming data */
  180.   if (p = find_option(buf,'F')) {
  181.     len = atol(p);
  182.     if (len < 0) len = 0;
  183.     if (len > 0 && len < MINBLOCK) len = MINBLOCK;
  184.     sprintf(sv->option_f,"%ld",len);
  185.   }
  186.  
  187.   /* Merge new E(rror limit) setting into other settings if given */
  188.   /* Number of sequential errors which will cause an abort */
  189.   if (p = find_option(buf,'E')) {
  190.     len = atol(p);
  191.     if (len < 1)
  192.       len = 1;
  193.     if (len > 32767)
  194.       len = 32767;
  195.     sprintf(sv->option_e,"%ld",len);
  196.   }
  197.  
  198.   /* Merge new A(uto-activate) setting into other settings if given */
  199.   /* "AY" = Automatically call XProtocolReceive() if ZRQINIT string received
  200.      "AN" = Don't look for ZRQINIT; user will explicitly activate receive */
  201.   if (p = find_option(buf,'A')) {
  202.     if (*p == 'Y' || *p == 'N') *sv->option_a = *p;
  203.     else ioerr(io,"Invalid A flag ignored; should be Y or N");
  204.   }
  205.  
  206.   /* Merge new D(elete after sending) setting into other options */
  207.   /* "DY" = Delete files after successfully sending them
  208.      "DN" = Don't delete files after sending */
  209.   if (p = find_option(buf,'D')) {
  210.     if (*p == 'Y' && (io->xpr_extension < 2 || !io->xpr_unlink))
  211.       ioerr(io,"Can't use DY; xpr_unlink() not supported");
  212.     else if (*p == 'Y' || *p == 'N') *sv->option_d = *p;
  213.     else ioerr(io,"Invalid D flag ignored; should be Y or N");
  214.   }
  215.  
  216.   /* Merge new K(eep partial files) setting into other options */
  217.   /* "KY" = Keep partially-received file fragments to allow later resumption
  218.      "KN" = Delete partially-received file fragments */
  219.   if (p = find_option(buf,'K')) {
  220.     if (*p == 'N' && (io->xpr_extension < 2 || !io->xpr_unlink))
  221.       ioerr(io,"Can't use KN; xpr_unlink() not supported");
  222.     else if (*p == 'Y' || *p == 'N') *sv->option_k = *p;
  223.     else ioerr(io,"Invalid K flag ignored; should be Y or N");
  224.   }
  225.  
  226.   /* Merge new S(end full path) setting into other options */
  227.   /* "SY" = Send full filename including directory path to receiver
  228.      "SN" = Send only simple filename portion, not including directory path */
  229.   if (p = find_option(buf,'S')) {
  230.     if (*p == 'Y' || *p == 'N') *sv->option_s = *p;
  231.     else ioerr(io,"Invalid S flag ignored; should be Y or N");
  232.   }
  233.  
  234.   /* Merge new R(eceive path) setting into other options */
  235.   /* "RY" = Use full filename exactly as received; don't use P option path
  236.      "RN" = Ignore received directory path if any; use path from P option */
  237.   if (p = find_option(buf,'R')) {
  238.     if (*p == 'Y' || *p == 'N') *sv->option_r = *p;
  239.     else ioerr(io,"Invalid R flag ignored; should be Y or N");
  240.   }
  241.  
  242.   /* Merge new P(ath) setting into other options */
  243.   /* "Pdir" = Receive files into directory "dir" if RN selected
  244.      "dir" can by any valid existing directory, with or without trailing "/" */
  245.   if (p = find_option(buf,'P')) {
  246.     strcpy(sv->option_p,p);
  247.     p = sv->option_p + strcspn(sv->option_p," ,\t\r\n");
  248.     *p = '\0';
  249.   }
  250.  
  251.   return (*sv->option_a == 'Y') ? XPRS_SUCCESS|XPRS_NORECREQ|XPRS_HOSTMON : XPRS_SUCCESS|XPRS_NORECREQ;
  252. }
  253.  
  254.  
  255. /* Called by comm program to give us a chance to clean up before program ends */
  256. long __saveds XProtocolCleanup(struct XPR_IO *io) {
  257.   /* Release option memory, if any */
  258.   if (io->xpr_data) {
  259.     FreeMem(io->xpr_data,(long)sizeof(struct SetupVars));
  260.     io->xpr_data = NULL;
  261.   }
  262.  
  263.   return XPRS_SUCCESS;
  264. }
  265.  
  266.  
  267.  
  268. /* Called by comm program upon our request (XPRS_HOSTMON) to let us monitor
  269.    the incoming data stream for our receiver auto-activation string (ZRQINIT packet).
  270.    We only ask for this to be called if option AY is set. */
  271. long __saveds XProtocolHostMon(struct XPR_IO *io,UBYTE *serbuff,long actual,long maxsize) {
  272.   static UBYTE startrcv[] = { ZPAD, ZDLE, ZHEX, "00" };
  273.   struct SetupVars *sv;
  274.  
  275.   if (!(sv = (void *)io->xpr_data))
  276.     return actual;  /* XProtocolSetup() never called?! */
  277.  
  278.   if (!sv->matchptr)
  279.     sv->matchptr = startrcv;
  280.  
  281.   /* Scan through serbuff to see if we can match all bytes in the start string in sequence */
  282.   for (sv->bufpos=serbuff; sv->bufpos < serbuff+actual; ++sv->bufpos) {
  283.     if (*sv->bufpos == *sv->matchptr) {  /* if data matches current position in match string */
  284.       ++sv->matchptr;          /* increment match position */
  285.       if (!*sv->matchptr) {    /* if at end of match string, it all matched */
  286.         sv->buflen = actual - (sv->bufpos - serbuff);
  287.         XProtocolReceive(io);
  288.         sv->matchptr = startrcv;
  289.         actual = 0;
  290.         break;
  291.       }
  292.     } else if (sv->matchptr > startrcv) { /* if mismatch, reset to start of match string */
  293.       sv->matchptr = startrcv;
  294.       if (*sv->bufpos == *sv->matchptr)
  295.         ++sv->matchptr;
  296.     }
  297.   }
  298.  
  299.   sv->bufpos = NULL;
  300.   return actual;
  301. }
  302.  
  303.  
  304. /* Called by comm program to let us monitor user's inputs; we never ask for
  305.    this to be called, but it's better to recover gracefully than guru the machine */
  306. long __saveds XProtocolUserMon(struct XPR_IO *io,UBYTE *serbuff,long actual,long maxsize) {
  307.   return actual;
  308. }
  309.  
  310.  
  311.  
  312. /* Perform setup and initializations common to both Send and Receive routines */
  313. struct Vars *setup(struct XPR_IO *io) {
  314.   static long bauds[] = { 110,300,1200,2400,4800,9600,19200,31250,38400,57600,76800,115200 };
  315.   struct SetupVars *sv;
  316.   struct Vars *v;
  317.   long origbuf, newstatus;
  318. #ifdef DEBUGLOG
  319.   long i, *lng;
  320. #endif
  321.  
  322.   /* Make sure comm program supports the required call-back functions */
  323.   if (!io->xpr_update) return NULL;
  324.   if (!io->xpr_fopen || !io->xpr_fclose || !io->xpr_fread || !io->xpr_fwrite ||
  325.       !io->xpr_fseek || !io->xpr_sread || !io->xpr_swrite) {
  326.     ioerr(io,"Comm prog missing required function(s); see docs");
  327.     return NULL;
  328.   }
  329.  
  330.   /* Hook in default transfer options if XProtocolSetup wasn't called */
  331.   if (!(sv = (void *)io->xpr_data)) {
  332.     io->xpr_data = AllocMem((long)sizeof(struct SetupVars),MEMF_CLEAR);
  333.     if (!(sv = (void *)io->xpr_data)) {
  334.       ioerr(io,"Not enough memory");
  335.       return NULL;
  336.     }
  337.     *sv = Default_Config;
  338.   }
  339.  
  340.   /* Allocate memory for our unshared variables, to provide reentrancy */  
  341.   if (!(v = AllocMem((long)sizeof(struct Vars),MEMF_CLEAR))) {
  342. nomem:
  343.     ioerr(io,"Not enough memory");
  344.     return NULL;
  345.   }
  346.   v->Modemchar = v->Modembuf;
  347.  
  348.   /* Allocate memory for our file I/O buffer; if we can't get as much as
  349.      requested, keep asking for less until we hit minimum before giving up */
  350.   v->Filebufmax = origbuf = atol(sv->option_b) * 1024;
  351.   while (!(v->Filebuf = AllocMem(v->Filebufmax,0L))) {
  352.     if (v->Filebufmax > 1024) v->Filebufmax -= 1024;
  353.     else {
  354.       FreeMem(v,(long)sizeof(struct Vars));
  355.       goto nomem;
  356.     }
  357.   }
  358.  
  359.   /* If framelength was intended to match buffer size, stay in sync */
  360.   v->Tframlen = atol(sv->option_f);
  361.   if (v->Tframlen && v->Tframlen == origbuf) v->Tframlen = v->Filebufmax;
  362.  
  363.   v->ErrorLimit = atol(sv->option_e);
  364.   
  365.   /* Copy caller's io struct into our Vars for easier passing */
  366.   v->io = *io;
  367.  
  368. #ifdef DEBUGLOG
  369.   if (!DebugLog) DebugLog = xpr_fopen(&v->io,DebugName,"w");
  370.   dlog(v,"XPR_IO struct:\n");
  371.   for (i=0,lng=(long *)io; i < sizeof(struct XPR_IO)/4; ++i) {
  372.     sprintf(v->Msgbuf,"  %08lx\n",*lng++);
  373.     dlog(v,v->Msgbuf);
  374.   }
  375. #endif
  376.  
  377.   /* Get baud rate; set serial port mode if necessary (and possible) */  
  378.   if (v->io.xpr_setserial) {
  379.     v->Oldstatus = xpr_setserial(&v->io,-1L);
  380.     if (v->Oldstatus != -1) {
  381.       /* ZModem requires 8 data bits, no parity (full transparency), 
  382.          leave other settings alone */
  383.       newstatus = v->Oldstatus & 0xFFFFE0BC;
  384.       /* newstatus |= on_flags; Here's where we'd turn bits on if we needed to */
  385.       if (newstatus != v->Oldstatus)
  386.         xpr_setserial(&v->io,newstatus);
  387.       v->Baud = bauds[(newstatus>>16) & 0xFF];
  388. #ifdef DEBUGLOG
  389.       sprintf(v->Msgbuf,"Old serial status = %lx, new = %lx, baud = %ld\n",v->Oldstatus,newstatus,v->Baud);
  390.       dlog(v,v->Msgbuf);      
  391. #endif
  392.     } else
  393.       v->Baud = 2400;
  394.   /* If no xpr_setserial(), muddle along with most likely guess */
  395.   } else
  396.     v->Baud = 2400;
  397.  
  398.   return v;
  399. }
  400.  
  401.  
  402.  
  403. /* Set text/binary mode flags in accordance with T option setting */
  404. void set_textmode(struct Vars *v) {
  405.   struct SetupVars *sv;
  406.   long i;
  407.  
  408.   sv = (void *)v->io.xpr_data;
  409.   switch(*sv->option_t) {
  410.     case 'Y':  /* Force text mode on receive; suggest text mode on send */
  411. TY:   v->Rxascii = TRUE;
  412.       v->Rxbinary = FALSE;
  413.       v->Lzconv = ZCNL;
  414.       break;
  415.     case 'N':  /* Force binary mode on receive; suggest binary mode on send */
  416. TN:   v->Rxascii = FALSE;
  417.       v->Rxbinary = TRUE;
  418.       v->Lzconv = ZCBIN;
  419.       break;
  420.     case 'C':  /* Ask comm program for proper mode for this file */
  421.       if (v->io.xpr_finfo) {
  422.         i = xpr_finfo(&v->io,v->Filename,2L);
  423.         if (i == 1) goto TN;  /* Comm program says use binary mode */
  424.         if (i == 2) goto TY;  /* Comm program says use text mode */
  425.       }
  426.       /* xpr_finfo() not provided (or failed); default to T? */
  427.     case '?':
  428.       v->Rxascii = v->Rxbinary = FALSE;
  429.       v->Lzconv = 0;
  430.       break;
  431.   }
  432. }
  433.  
  434.  
  435.  
  436. /* Search for specified option setting in string */
  437. UBYTE *find_option(UBYTE *buf,UBYTE option) {
  438.   while (*buf) {
  439.     buf += strspn(buf," ,\t\r\n");
  440.     if (*buf == option) return ++buf;
  441.     buf += strcspn(buf," ,\t\r\n");
  442.   }
  443.  
  444.   return NULL;
  445. }
  446.   
  447.  
  448.  
  449. /* send cancel string to get the other end to shut up */
  450. void canit(struct Vars *v) {
  451.   static char canistr[] = { 24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0 };
  452.  
  453.   zmputs(v,canistr);
  454. }
  455.  
  456.  
  457.  
  458. /* Send a string to the modem, with processing for \336 (sleep 1 sec)
  459.    and \335 (break signal, ignored since XPR spec doesn't support it) */
  460. void zmputs(struct Vars *v,UBYTE *s) {
  461.   UBYTE c;
  462.  
  463.   while (*s) {
  464.     switch (c = *s++) {
  465.       case '\336':
  466.         TimeOut(50L);
  467.       case '\335':
  468.         break;
  469.       default:
  470.         sendline(v,c);
  471.     }
  472.   }
  473.   sendbuf(v);
  474. }
  475.  
  476.  
  477.  
  478. /* Write one character to the modem */
  479. void xsendline(struct Vars *v,UBYTE c) {
  480.   v->Outbuf[v->Outbuflen++] = c;
  481.   if (v->Outbuflen >= sizeof(v->Outbuf))
  482.     sendbuf(v);
  483. }
  484.  
  485.  
  486.  
  487. /* Send any data waiting in modem output buffer */
  488. void sendbuf(struct Vars *v) {
  489.   if (v->Outbuflen) {
  490.     xpr_swrite(&v->io,v->Outbuf,(long)v->Outbuflen);
  491.     v->Outbuflen = 0;
  492.   }
  493. }
  494.  
  495.  
  496.  
  497. /* Get a byte from the modem;
  498.    return TIMEOUT if no read within timeout tenths of a second,
  499.    return RCDO if carrier lost or other fatal error (sread returns -1).
  500.    Added in some buffering so we wouldn't hammer the system with single-byte
  501.    serial port reads.  Also, the buffering makes char_avail() a lot easier to implement. */
  502. short readock(struct Vars *v,short tenths) {
  503.   long t;
  504.  
  505.   /* If there's data waiting in our buffer, return next byte */
  506.   if (v->Modemcount) {
  507. gotdata:
  508.     --v->Modemcount;
  509.     return (short)(*v->Modemchar++);
  510.   }
  511.  
  512.   /* Our buffer is empty; see if there's anything waiting in system buffer.
  513.      If the caller is in a hurry, don't wait around, but if it can spare
  514.      a half second, wait a bit and build up some input so we don't do as
  515.      many sread() calls. */
  516.   t = (tenths < 5) ? 0 : 500000;
  517. #ifdef DEBUGLOG
  518.   sprintf(v->Msgbuf,"Input buffer empty; calling sread for %ld bytes, %ld usec\n",(long)sizeof(v->Modembuf),t);
  519.   dlog(v,v->Msgbuf);
  520. #endif
  521.   v->Modemcount = xpr_sread(&v->io,v->Modembuf,(long)sizeof(v->Modembuf),t);
  522. #ifdef DEBUGLOG
  523.   sprintf(v->Msgbuf,"   sread returned %ld\n",v->Modemcount);
  524.   dlog(v,v->Msgbuf);
  525. #endif
  526.   if (v->Modemcount < 0) {  /* Carrier dropped or other fatal error; abort */
  527.     v->Modemcount = 0;
  528.     return RCDO;
  529.   } else if (!v->Modemcount) {   /* Nothing in system buffer; try waiting */
  530.     t = tenths * 100000L - t;
  531. #ifdef DEBUGLOG
  532.     sprintf(v->Msgbuf,"   calling sread for 1 byte, %ld usec\n",t);
  533.     dlog(v,v->Msgbuf);
  534. #endif
  535.     v->Modemcount = xpr_sread(&v->io,v->Modembuf,1L,t);
  536. #ifdef DEBUGLOG
  537.   sprintf(v->Msgbuf,"   sread returned %ld\n",v->Modemcount);
  538.   dlog(v,v->Msgbuf);
  539. #endif
  540.     if (v->Modemcount < 0) {
  541.       v->Modemcount = 0;
  542.       return RCDO;
  543.     } else if (!v->Modemcount) return TIMEOUT;  /* Nothing received in time */
  544.   }
  545.   v->Modemchar = v->Modembuf;  /* Reset buffer pointer to start of data */
  546.   goto gotdata;
  547. }
  548.  
  549.  
  550.  
  551. /* Check if there's anything available to read from the modem */
  552. char char_avail(struct Vars *v) {
  553.   if (v->Modemcount) return TRUE;
  554.  
  555.   /* No data in our buffer; check system's input buffer */  
  556.   v->Modemcount = xpr_sread(&v->io,v->Modembuf,(long)sizeof(v->Modembuf),0L);
  557.   if (v->Modemcount < 1) {  /* Nothing in system buffer either */
  558.     v->Modemcount = 0;
  559.     return FALSE;
  560.   } else {                  /* System buffer had something waiting for us */
  561.     v->Modemchar = v->Modembuf;
  562.     return TRUE;
  563.   }
  564. }
  565.  
  566.  
  567.  
  568. /* Update the elapsed time, expected total time, and effective data
  569.    transfer rate values for status display */
  570. void update_rate(struct Vars *v) {
  571.   ULONG sent, elapsed, expect;
  572.   short hr, min;
  573.   struct timeval tv;
  574.  
  575.   /* Compute effective data rate so far in characters per second */
  576.   sent = v->xpru.xpru_bytes - v->Strtpos;
  577.   GetSysTime(&tv);
  578.   elapsed = (tv.tv_secs & 0x7FFFFF) * 128 + tv.tv_micro / 8192;
  579.   elapsed -= (v->Starttime.tv_secs & 0x7FFFFF) * 128 + v->Starttime.tv_micro / 8192;
  580.   if (elapsed < 128)
  581.     elapsed = 128;
  582.   /* If we haven't transferred anything yet (just starting), make reasonable
  583.      guess (95% throughput); otherwise, compute actual effective transfer rate */
  584.   v->xpru.xpru_datarate = (sent) ? (sent * 128 / elapsed) : (v->Baud * 95 / 1000);
  585.  
  586.   /* Compute expected total transfer time based on data rate so far */
  587.   if (v->xpru.xpru_filesize < 0)
  588.     expect = 0; /* Don't know filesize; display time=0 */
  589.   else
  590.     expect = (v->xpru.xpru_filesize - v->Strtpos) / v->xpru.xpru_datarate;
  591.   hr = expect / 3600;   /* How many whole hours */
  592.   expect -= hr * 3600;  /* Remainder not counting hours */
  593.   min = expect / 60;    /* How many whole minutes */
  594.   expect -= min * 60;   /* Remaining seconds */
  595.   sprintf(v->Msgbuf,"%02ld:%02ld:%02ld",(long)hr,(long)min,expect);
  596.   v->xpru.xpru_expecttime = (char *)v->Msgbuf;
  597.  
  598.   /* Compute elapsed time for this transfer so far */
  599.   elapsed /= 128;
  600.   hr = elapsed / 3600;
  601.   elapsed -= hr * 3600;
  602.   min = elapsed / 60;
  603.   elapsed -= min * 60;
  604.   sprintf(v->Msgbuf+20,"%02ld:%02ld:%02ld",(long)hr,(long)min,elapsed);
  605.   v->xpru.xpru_elapsedtime = (char *)v->Msgbuf+20;
  606. }
  607.  
  608.  
  609.  
  610. /* Buffered file I/O fopen() interface routine */
  611. void *bfopen(struct Vars *v,UBYTE *mode) {
  612.   /* Initialize file-handling variables */
  613.   v->Filebufpos = v->Filebuflen = v->Filebufcnt = 0;
  614.   v->Fileflush = FALSE;
  615.   v->Filebufptr = v->Filebuf;
  616.   /* Open the file */
  617. #ifdef DEBUGLOG
  618.   sprintf(v->Msgbuf,"bfopen: %s %s\n",v->Filename,mode);
  619.   dlog(v,v->Msgbuf);
  620. #endif
  621.   return xpr_fopen(&v->io,v->Filename,mode);
  622. }
  623.  
  624.  
  625.  
  626. /* Buffered file I/O fclose() interface routine */
  627. void bfclose(struct Vars *v) {
  628.   if (v->File) {
  629.     /* If bfwrite() left data lingering in buffer, flush it out before closing */
  630.     if (v->Fileflush)
  631.       xpr_fwrite(&v->io,v->Filebuf,1L,v->Filebufcnt,v->File);
  632.     /* Close the file */
  633.     xpr_fclose(&v->io,v->File);
  634.     v->File = NULL;
  635.   }
  636. }
  637.  
  638.  
  639.  
  640. /* Buffered file I/O fseek() interface routine */
  641. void bfseek(struct Vars *v,long pos) {
  642.   long offset;
  643.  
  644.   /* If new file position is within currently buffered section, reset pointers */
  645.   if (pos >= v->Filebufpos && pos < v->Filebufpos + v->Filebuflen) {
  646.     offset = pos - v->Filebufpos;
  647.     v->Filebufptr = v->Filebuf + offset;
  648.     v->Filebufcnt = v->Filebuflen - offset;
  649.   /* Otherwise, fseek() file and discard buffer contents to force new read */
  650.   } else {
  651.     xpr_fseek(&v->io,v->File,pos,0L);
  652.     v->Filebuflen = v->Filebufcnt = 0;
  653.     v->Filebufpos = pos;
  654.   }
  655. }
  656.  
  657.  
  658.  
  659. /* Buffered file I/O fread() interface routine */
  660. long bfread(struct Vars *v,UBYTE *buf,long length) {
  661.   long count, total = 0;
  662.  
  663.   /* Keep going until entire request completed */
  664.   while (length > 0) {
  665.     /* Copy as much of the request as possible from the buffer */
  666.     count = (length <= v->Filebufcnt) ? length : v->Filebufcnt;
  667.     CopyMem(v->Filebufptr,buf,count);
  668. #ifdef DEBUGLOG
  669.     sprintf(v->Msgbuf,"bfread got %ld bytes from buffer\n",count);
  670.     dlog(v,v->Msgbuf);
  671. #endif
  672.     buf += count;
  673.     total += count;
  674.     length -= count;
  675.     v->Filebufptr += count;
  676.     v->Filebufcnt -= count;
  677.  
  678.     /* If we've emptied the buffer, read next buffer's worth */
  679.     if (!v->Filebufcnt) {
  680.       v->Filebufpos += v->Filebuflen;
  681.       v->Filebufptr = v->Filebuf;
  682.       v->Filebufcnt = v->Filebuflen = xpr_fread(&v->io,v->Filebuf,1L,v->Filebufmax,v->File);
  683. #ifdef DEBUGLOG
  684.       sprintf(v->Msgbuf,"bfread read %ld bytes\n",v->Filebufcnt);
  685.       dlog(v,v->Msgbuf);
  686. #endif
  687.       /* If we hit the EOF, return with however much we read so far */
  688.       if (!v->Filebufcnt) break;
  689.     }
  690.   }
  691.  
  692.   return total;
  693. }
  694.  
  695.  
  696.  
  697. /* Buffered file I/O fwrite() interface routine */
  698. long bfwrite(struct Vars *v,UBYTE *buf,long length) {
  699.   long count, total = 0;
  700.  
  701.   /* Keep going until entire request completed */
  702.   while (length > 0) {
  703.     /* Copy as much as will fit into the buffer */
  704.     count = v->Filebufmax - v->Filebufcnt;
  705.     if (length < count) count = length;
  706.     CopyMem(buf,v->Filebufptr,count);
  707. #ifdef DEBUGLOG
  708.     sprintf(v->Msgbuf,"bfwrite buffered %ld bytes\n",count);
  709.     dlog(v,v->Msgbuf);
  710. #endif
  711.     buf += count;
  712.     total += count;
  713.     length -= count;
  714.     v->Filebufptr += count;
  715.     v->Filebufcnt += count;
  716.     v->Fileflush = TRUE;
  717.  
  718.     /* If we've filled the buffer, write it out */
  719.     if (v->Filebufcnt == v->Filebufmax) {
  720.       count = xpr_fwrite(&v->io,v->Filebuf,1L,v->Filebufcnt,v->File);
  721. #ifdef DEBUGLOG
  722.       sprintf(v->Msgbuf,"bfwrite wrote %ld bytes\n",count);
  723.       dlog(v,v->Msgbuf);
  724. #endif
  725.       if (count < v->Filebufcnt) return -1;
  726.       v->Filebufptr = v->Filebuf;
  727.       v->Filebufcnt = 0;
  728.       v->Fileflush = FALSE;
  729.     }
  730.   }
  731.  
  732.   return total;
  733. }
  734.  
  735.     
  736.  
  737. /* Have the comm program display an error message for us, using a
  738.    temporary XPR_UPDATE structure; used to display errors before Vars 
  739.    gets allocated */
  740. void ioerr(struct XPR_IO *io,char *msg) {
  741.   struct XPR_UPDATE xpru;
  742.  
  743.   if (io->xpr_update) {
  744.     xpru.xpru_updatemask = XPRU_ERRORMSG;
  745.     xpru.xpru_errormsg = msg;
  746.     xpr_update(io,&xpru);
  747.   }
  748. }
  749.  
  750.  
  751.  
  752. /* Have the comm program display an error message for us, using the
  753.    normal XPR_IO structure allocated in Vars */
  754. void upderr(struct Vars *v,char *msg) {
  755.   v->xpru.xpru_updatemask = XPRU_ERRORMSG;
  756.   v->xpru.xpru_errormsg = msg;
  757.   if (msg == v->Msgbuf) /* Ensure message length < 50 */
  758.     msg[48] = '\0';
  759.   xpr_update(&v->io,&v->xpru);
  760. #ifdef DEBUGLOG
  761.   dlog(v,msg);
  762.   dlog(v,"\n");
  763. #endif
  764. }
  765.  
  766.  
  767.  
  768. /* Have the comm program display a normal message for us */
  769. void updmsg(struct Vars *v,char *msg) {
  770.   v->xpru.xpru_updatemask = XPRU_MSG;
  771.   v->xpru.xpru_msg = msg;
  772.   if (msg == v->Msgbuf) /* Ensure message length < 50 */
  773.     msg[48] = '\0';
  774.   xpr_update(&v->io,&v->xpru);
  775. #ifdef DEBUGLOG
  776.   dlog(v,msg);
  777.   dlog(v,"\n");
  778. #endif
  779. }
  780.  
  781.  
  782.  
  783. /* Figure out how many bytes are free on the drive we're uploading to.
  784.    Stubbed out for now; not supported by XPR spec. */
  785. long getfree(void) {
  786.   return 0x7FFFFFFF;
  787. }
  788.  
  789.  
  790.  
  791. /* Check whether file already exists; used to detect potential overwrites */
  792. char exist(struct Vars *v) {
  793.   void *file;
  794.  
  795.   file = xpr_fopen(&v->io,v->Filename,"r");
  796.   if (file) {
  797.     xpr_fclose(&v->io,file);
  798.     return TRUE;
  799.   } else return FALSE;
  800. }
  801.  
  802.  
  803.  
  804. #ifdef DEBUGLOG
  805. /* Write a message to the debug log */
  806. void dlog(struct Vars *v,UBYTE *s) {
  807.   /* Open the debug log if it isn't already open */
  808.   if (!DebugLog) DebugLog = xpr_fopen(&v->io,DebugName,"a");
  809.   xpr_fwrite(&v->io,s,1L,(long)strlen(s),DebugLog);
  810.   /* Close file to flush output buffer; comment these two lines out if
  811.      you aren't crashing your system and don't mind waiting until the
  812.      transfer finishes to look at your log file.
  813.   xpr_fclose(&v->io,DebugLog);
  814.   DebugLog = NULL; */
  815. }
  816. #endif
  817.