home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 592b.lha / XprZmodem_v2.50 / Utils.c < prev    next >
C/C++ Source or Header  |  1991-11-23  |  36KB  |  1,134 lines

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