home *** CD-ROM | disk | FTP | other *** search
/ Amiga Elysian Archive / AmigaElysianArchive.iso / comm / term23_2.lha / Source_Code / XPRQuickB / xprlib.c < prev    next >
C/C++ Source or Header  |  1991-09-02  |  16KB  |  647 lines

  1. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  2. /* |_o_o|\\ Copyright (c) 1990 The Software Distillery.  All Rights Reserved */
  3. /* |. o.| || This program may not be distributed without the permission of   */
  4. /* | .    | || the authors:                  BBS: (919) 382-8265    */
  5. /* | o    | ||   Dave Baker      Alan Beale      Jim Cooper             */
  6. /* |  . |//    Jay Denebeim    Bruce Drake      Gordon Keener          */
  7. /* ======      John Mainwaring Andy Mercier      Jack Rouse             */
  8. /*           John Toebes     Mary Ellen Toebes  Doug Walker  Mike Whitcher */
  9. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  10.    
  11. #include "quickb.h"
  12. #include <exec/memory.h>
  13.  
  14. static void miniterm(struct XPR_DATA *xd, char *startup);
  15. static long setup_xd(struct XPR_IO *xio);
  16. static void edit_opts(struct XPR_DATA *xd, char *buf);
  17. static void set_optstr(struct XPR_DATA *xd, char *str);
  18.  
  19. /* protocol termination */
  20. long __saveds __asm XProtocolCleanup(register __a0 struct XPR_IO *xio)
  21. {
  22.     if (xio->xpr_data)
  23.         FreeMem(xio->xpr_data, (long)sizeof(struct XPR_DATA));
  24.     xio->xpr_data = 0;
  25.     return 1L;
  26. }
  27.  
  28. /* protocol setup */
  29. long __saveds __asm XProtocolSetup(register __a0 struct XPR_IO *xio)
  30. {
  31.     struct XPR_DATA *xd;
  32.     char buf[256];
  33.  
  34.     if (!setup_xd(xio)) return XPRS_FAILURE;
  35.     xd = (struct XPR_DATA *)xio->xpr_data;
  36.     if (xio->xpr_filename)
  37.     {
  38.         strcpy(buf, xio->xpr_filename);
  39.         set_optstr(xd, buf);
  40.     }
  41.     else
  42.         edit_opts(xd, buf);
  43.     return (xd->optAuto=='Y') ? XPRS_SUCCESS|XPRS_HOSTMON : XPRS_SUCCESS;
  44. }
  45.  
  46. /* protocol send request */
  47. long __saveds __asm XProtocolSend(register __a0 struct XPR_IO *xio)
  48. {
  49.     if (!setup_xd(xio)) return 0L;
  50.     miniterm((struct XPR_DATA *)xio->xpr_data, "upl/proto:qb");
  51.     return 1L;
  52. }
  53.  
  54. /* protocol receive request */
  55. long __saveds __asm XProtocolReceive(register __a0 struct XPR_IO *xio)
  56. {
  57.     if (!setup_xd(xio)) return 0L;
  58.     miniterm((struct XPR_DATA *)xio->xpr_data, "dow/proto:qb");
  59.     return 1L;
  60. }
  61.  
  62. /* look for protocol initiation */
  63. long __saveds __asm XProtocolHostMon(
  64.     register __a0 struct XPR_IO *xio,
  65.     register __a1 char *serbuff,
  66.     register __d0 long actual,
  67.     register __d1 long maxsize)
  68. {
  69.     struct XPR_DATA *xd;
  70.     int c, lastc;
  71.  
  72.     xd = (struct XPR_DATA *)xio->xpr_data;
  73.     if (!xd) return actual;
  74.     xd->nserbuf = actual;
  75.     xd->serbufp = serbuff;
  76.     while (--xd->nserbuf >= 0)
  77.     {
  78.         lastc = xd->cchar;
  79.         xd->cchar = c = *(xd->serbufp++) & 0x7F;
  80.         if (c == ENQ)
  81.         {
  82.              bp_ENQ(xd);
  83.             actual = 0;
  84.         }
  85.         else if (c == DLE)
  86.         {
  87.             bp_DLE(xd);
  88.             actual = 0;
  89.         }
  90.         else if (c == 'I' && lastc == 0x1B)
  91.         {
  92.              bp_ESC_I(xd);
  93.             actual = 0;
  94.         }
  95.     }
  96.  
  97.     return actual;
  98. }
  99.  
  100. long __saveds __asm XProtocolUserMon(
  101.     register __a0 struct XPR_IO *xio,
  102.     register __a1 char *serbuff,
  103.     register __d0 long actual,
  104.     register __d1 long maxsize)
  105. {
  106.     return actual;
  107. }
  108.  
  109. /* return failure */
  110. static long Fail()
  111. {
  112.     return 0L;
  113. }
  114.  
  115. /* write an error message */
  116. void errormsg(struct XPR_DATA *xd, char *msg)
  117. {
  118.     xd->xpru.xpru_updatemask = XPRU_ERRORMSG;
  119.     xd->xpru.xpru_errormsg = msg;
  120.     (*xd->xpr_update)(&xd->xpru);
  121. }
  122.  
  123. /* set up our data structures */
  124. static long setup_xd(struct XPR_IO *xio)
  125. {
  126.     struct XPR_UPDATE xpru;
  127.     register struct XPR_DATA *xd;
  128.  
  129.     if (!xio->xpr_data)
  130.     {
  131.         if ((xio->xpr_data = AllocMem((long)sizeof(struct XPR_DATA),
  132.                           MEMF_CLEAR)) == NULL)
  133.         {    /* no memory */
  134.             xpru.xpru_updatemask = XPRU_ERRORMSG;
  135.             xpru.xpru_errormsg = "Out of memory";
  136.             if (xio->xpr_update)
  137.                 (*xio->xpr_update)(&xpru);
  138.             return 0L;
  139.         }
  140.         memset(xio->xpr_data, 0, sizeof(struct XPR_DATA));
  141.         xd = (struct XPR_DATA *)xio->xpr_data;
  142.         xd->xio = xio;
  143.  
  144.         /* default options */
  145.         xd->maxiobuf = 16384;    /* 16K buffer */
  146.         xd->optText = 'H';    /* use CompuServe suggestion */
  147.         xd->optOwrt = 'N';    /* use new file name */
  148.         xd->optAuto = 'Y';    /* activate automatically */
  149.         xd->optDelt = 'N';    /* don't delete after send */
  150.         xd->optKeep = (xio->xpr_extension >= 2L && xio->xpr_unlink)
  151.                 ? 'N'
  152.                 : 'Y';    /* keep partial files */
  153.     }
  154.     else
  155.         xd = (struct XPR_DATA *)xio->xpr_data;
  156.  
  157.     /* do some initialization */
  158.     xd->Done = xd->Aborting = FALSE;
  159.     xd->nserbuf = 0;
  160.     xd->iobuf = NULL;
  161.     xd->iobufn = 0;
  162.  
  163.     /* following are optional */
  164.     xd->xpr_update = xio->xpr_update ? xio->xpr_update : Fail;
  165.     xd->xpr_chkabort = xio->xpr_chkabort ? xio->xpr_chkabort : Fail;
  166.     xd->xpr_chkmisc = xio->xpr_chkmisc ? xio->xpr_chkmisc : Fail;
  167.     xd->xpr_gets = xio->xpr_gets ? xio->xpr_gets : Fail;
  168.     xd->xpr_setserial = xio->xpr_setserial;    /* NOTE! no default to fail */
  169.     xd->xpr_finfo = xio->xpr_finfo ? xio->xpr_finfo : Fail;
  170.     xd->xpr_options = (xio->xpr_extension >= 1L)
  171.               ? xio->xpr_options : NULL;
  172.     xd->xpr_unlink = (xio->xpr_extension >= 2L)
  173.             ? xio->xpr_unlink : NULL;
  174.  
  175.     /* following are required */
  176.     if ((xd->xpr_fopen = xio->xpr_fopen) == NULL)  goto lose;
  177.     if ((xd->xpr_fclose = xio->xpr_fclose) == NULL) goto lose;
  178.     if ((xd->xpr_fread = xio->xpr_fread) == NULL)  goto lose;
  179.     if ((xd->xpr_fwrite = xio->xpr_fwrite) == NULL) goto lose;
  180.     if ((xd->xpr_sread = xio->xpr_sread) == NULL)  goto lose;
  181.     if ((xd->xpr_swrite = xio->xpr_swrite) == NULL) goto lose;
  182.  
  183.     return 1L;
  184.  
  185. lose:
  186.     errormsg(xd, "Insufficient XPR protocol support");
  187.     return 0L;
  188. }
  189.  
  190. /* validate a boolean return value */
  191. static int check_bool(struct XPR_DATA *xd, char *buf, char *which)
  192. {
  193.     if (strcmp(buf, "ON") == 0)
  194.         buf[0] = 'Y';
  195.     else if (strcmp(buf, "OFF") == 0)
  196.         buf[0] = 'N';
  197.     return TRUE;
  198. }
  199.  
  200. /* allow user to edit option string */
  201. static void edit_opts(struct XPR_DATA *xd, char *buf)
  202. {
  203.     char *p;
  204.     struct xpr_option *optptr[7];
  205.     struct xpr_option opt[7];
  206.     struct {
  207.         char Text[4];
  208.         char Owrt[4];
  209.         char Buff[8];
  210.         char Auto[4];
  211.         char Delt[4];
  212.         char Keep[4];
  213.     }    optbuf;
  214.     register int i;
  215.     long status;
  216.     long l;
  217.  
  218.     if (!xd->xpr_options)
  219.     {    /* format the current options */
  220.         p = buf;
  221.         *p++ = 'T'; *p++ = xd->optText; *p++ = ',';
  222.         *p++ = 'O'; *p++ = xd->optOwrt; *p++ = ',';
  223.         *p++ = 'B'; p += stcl_d(p, xd->maxiobuf >> 10); *p++ = ',';
  224.         *p++ = 'A'; *p++ = xd->optAuto; *p++ = ',';
  225.         *p++ = 'D'; *p++ = xd->optDelt; *p++ = ',';
  226.         *p++ = 'K'; *p++ = xd->optKeep; *p++ = '\0';
  227.  
  228.         /* let the user edit it */
  229.         if ((*xd->xpr_gets)("Quick B options:", buf))
  230.         {    /* set the option string */
  231.             set_optstr(xd, buf);
  232.         }
  233.         return;
  234.     }
  235.  
  236.     /* use the requester interface */
  237.     memset((char *)&optbuf, 0, sizeof(optbuf));
  238.     opt[0].xpro_description = "Quick B options:";
  239.     opt[0].xpro_type = XPRO_HEADER;
  240.     opt[0].xpro_value = NULL;
  241.     opt[0].xpro_length = 0;
  242.  
  243.     optbuf.Text[0] = xd->optText;
  244.     opt[1].xpro_description = "Text mode (Y,N,H,C):";
  245.     opt[1].xpro_type = XPRO_STRING;
  246.     opt[1].xpro_value = optbuf.Text;
  247.     opt[1].xpro_length = sizeof(optbuf.Text);
  248.  
  249.     optbuf.Owrt[0] = xd->optOwrt;
  250.     opt[2].xpro_description = "Overwrite mode (Y,N,S):";
  251.     opt[2].xpro_type = XPRO_STRING;
  252.     opt[2].xpro_value = optbuf.Owrt;
  253.     opt[2].xpro_length = sizeof(optbuf.Owrt);
  254.  
  255.     stcl_d(optbuf.Buff, xd->maxiobuf >> 10);
  256.     opt[3].xpro_description = "I/O buffer size (KB):";
  257.     opt[3].xpro_type = XPRO_LONG;
  258.     opt[3].xpro_value = optbuf.Buff;
  259.     opt[3].xpro_length = sizeof(optbuf.Buff);
  260.  
  261.     strcpy(optbuf.Auto, (xd->optAuto == 'Y') ? "yes" : "no");
  262.     opt[4].xpro_description = "Auto-activate transfers:";
  263.     opt[4].xpro_type = XPRO_BOOLEAN;
  264.     opt[4].xpro_value = optbuf.Auto;
  265.     opt[4].xpro_length = sizeof(optbuf.Auto);
  266.  
  267.     strcpy(optbuf.Delt, (xd->optDelt == 'Y') ? "yes" : "no");
  268.     opt[5].xpro_description = "Delete after sending:";
  269.     opt[5].xpro_type = XPRO_BOOLEAN;
  270.     opt[5].xpro_value = optbuf.Delt;
  271.     opt[5].xpro_length = sizeof(optbuf.Delt);
  272.  
  273.     strcpy(optbuf.Keep, (xd->optKeep == 'Y') ? "yes" : "no");
  274.     opt[6].xpro_description = "Keep partial files:";
  275.     opt[6].xpro_type = XPRO_BOOLEAN;
  276.     opt[6].xpro_value = optbuf.Keep;
  277.     opt[6].xpro_length = sizeof(optbuf.Keep);
  278.     
  279.     /* prompt for options */
  280.     for (i = 0; i < 7; ++i)
  281.         optptr[i] = &opt[i];
  282.     status = (*xd->xpr_options)(7L, optptr);
  283.     if (status <= 0) return;
  284.  
  285.     /* process options */
  286.     for (i = 1; i < 7; ++i)
  287.     {
  288.         if (!(status & (1 << i))) continue;
  289.         strupr(opt[i].xpro_value);
  290.         switch (i)
  291.         {
  292.         case 1:        /* Text */
  293.             if (strchr("YNCH", optbuf.Text[0]))
  294.                 xd->optText = optbuf.Text[0];
  295.             else
  296.                 errormsg(xd, "Text option ignored");
  297.             break;
  298.         case 2:        /* Overwrite */
  299.             if (strchr("YNS", optbuf.Owrt[0]))
  300.                 xd->optOwrt = optbuf.Owrt[0];
  301.             else
  302.                 errormsg(xd, "Overwrite option ignored");
  303.             break;
  304.         case 3:        /* Buffer size */
  305.             stcd_l(optbuf.Buff, &l);
  306.             if (l < 0 || l > 0x7FFFFFFF >> 10)
  307.                 errormsg(xd, "Buffer size ignored; out of range");
  308.             else
  309.                 xd->maxiobuf = l << 10;
  310.             break;
  311.         case 4:        /* Auto-Activate */
  312.             if (check_bool(xd, optbuf.Auto, "Auto-activate"))
  313.                 xd->optAuto = optbuf.Auto[0];
  314.             break;
  315.         case 5:        /* Delete after send */
  316.             if (check_bool(xd, optbuf.Delt, "Delete"))
  317.             {
  318.                 if (optbuf.Delt[0] != 'Y' || xd->xpr_unlink)
  319.                     xd->optDelt = optbuf.Delt[0];
  320.                 else
  321.                     errormsg(xd, "Delete option ignored; file deletion not available");
  322.             }
  323.             break;
  324.         case 6:        /* Keep partial files */
  325.             if (check_bool(xd, optbuf.Keep, "Keep"))
  326.             {
  327.                 if (optbuf.Keep[0] == 'Y' || xd->xpr_unlink)
  328.                     xd->optKeep = optbuf.Keep[0];
  329.                 else
  330.                     errormsg(xd, "Keep option ignored; file deletion not available");
  331.             }
  332.             break;
  333.         }
  334.     }
  335.  
  336.     return;
  337. }
  338.  
  339. /* parse and merge option string with current settings */
  340. static void set_optstr(struct XPR_DATA *xd, char *str)
  341. {
  342.     long l;
  343.  
  344.     /* convert to upper case for simpler parsing */
  345.     strupr(str);
  346.     for (;;)
  347.     {
  348.         str += strspn(str, " ,");
  349.         switch (*str++)
  350.         {
  351.         case 'T':    /* text mode */
  352.             if (strchr("YNCH", *str))
  353.                 xd->optText = *str;
  354.             else
  355.                 errormsg(xd, "T option ignored; use TY, TN, TH, or TC");
  356.             break;
  357.         case 'O':    /* overwrite */
  358.             if (strchr("YNS", *str))
  359.                 xd->optOwrt = *str;
  360.             else
  361.                 errormsg(xd, "O option ignored; use OY, ON, or OS");
  362.             break;
  363.         case 'B':    /* buffer size */
  364.             str += stcd_l(str, &l);
  365.             if (l < 0 || l > 0x7FFFFFFF >> 10)
  366.                 errormsg(xd, "Buffer size ignored; out of range");
  367.             else
  368.                 xd->maxiobuf = l << 10;
  369.             break;
  370.         case 'A':    /* auto-activate */
  371.             if (strchr("YN", *str))
  372.                 xd->optAuto = *str;
  373.             else
  374.                 errormsg(xd, "A option ignored; use AY or AN");
  375.             break;
  376.         case 'D':    /* delete after send */
  377.             if (!strchr("YN", *str))
  378.                 errormsg(xd, "D option ignored; use DY or DN");
  379.             else if (*str == 'Y' && !xd->xpr_unlink)
  380.                 errormsg(xd, "DY option ignored; file deletion not available");
  381.             else
  382.                 xd->optDelt = *str;
  383.             break;
  384.         case 'K':    /* keep partial receives */
  385.             if (strchr("YN", *str))
  386.                 xd->optKeep = *str;
  387.             else
  388.                 errormsg(xd, "K option ignored; use KY or KN");
  389.             break;
  390.         default:
  391.             return;
  392.         }
  393.  
  394.         /* skip rest of option */
  395.         str += strcspn(str, " ,");
  396.     }
  397. }
  398.  
  399. void cputc(struct XPR_DATA *xd, int c)
  400. {
  401.    char ch = c;
  402.    (*xd->xpr_swrite)(&ch, 1L);
  403. }
  404.  
  405. /* called by cgetc macro to refill the buffer */
  406. int cfilbuf(struct XPR_DATA *xd, long timeout)
  407. {
  408.     long n;
  409.     n = (*xd->xpr_sread)(xd->serbuf, (long)MAX_SERBUF, 0L);
  410.     if (n <= 0)
  411.         n = (*xd->xpr_sread)(xd->serbuf, 1L, timeout);
  412.     if (n <= 0)
  413.         return -1;
  414.     xd->nserbuf = n-1;
  415.     xd->serbufp = &xd->serbuf[1];
  416.     return (int)xd->serbuf[0];
  417. }
  418.  
  419. static void miniterm(struct XPR_DATA *xd, char *startup)
  420. {
  421.    int c = -1;
  422.    int lastc;
  423.    char buf[256];
  424.    char prompt[1024];
  425.    char *p;
  426.    int i;
  427.    int to;
  428.  
  429.    /* get the startup line */
  430.    strcpy(buf, startup);
  431.    if ((*xd->xpr_gets)("Startup line", buf))
  432.    {
  433.       for (p = buf; *p; ++p)
  434.          cputc(xd, *p);
  435.       cputc(xd, '\r');
  436.    }
  437.  
  438.    xd->xpru.xpru_updatemask = XPRU_MSG;
  439.    xd->xpru.xpru_msg = "Entering protocol";
  440.    (*xd->xpr_update)(&xd->xpru);
  441.  
  442.    i = 0;
  443.    to = 0;
  444.    do {
  445.       lastc = c;
  446.       c = cgetc(xd, 1000000L);
  447.       if (c < 0)
  448.       {  /* timed out */
  449.          if (++to == 10)
  450.             break;
  451.      if (to == 1 && i > 0)
  452.      {
  453.         prompt[i] = 0;
  454.         buf[0] = 0;
  455.         strcpy(buf, xd->xio->xpr_filename);
  456.             if ((*xd->xpr_gets)(prompt, buf))
  457.         {
  458.            for (p = buf; *p; ++p)
  459.               cputc(xd, *p);
  460.                  cputc(xd, '\r');
  461.         }
  462.         i = 0;
  463.      }
  464.       }
  465.       else
  466.       {
  467.            to = 0;
  468.  
  469.      if (c == ENQ)
  470.         bp_ENQ(xd);
  471.          else if (c == DLE)
  472.             bp_DLE(xd);
  473.          else if (c == 'I' && lastc == 0x1B)
  474.             bp_ESC_I(xd);
  475.      else if (c == '\n')
  476.      {
  477.         prompt[i] = 0;
  478.         xd->xpru.xpru_updatemask = XPRU_MSG;
  479.         xd->xpru.xpru_msg = prompt;
  480.         (*xd->xpr_update)(&xd->xpru);
  481.         i = 0;
  482.          }
  483.          else if (c != '\r' && i < sizeof(prompt)-1)
  484.             prompt[i++] = c;
  485.       }
  486.       if ((*xd->xpr_chkabort)())
  487.          xd->Aborting = TRUE;
  488.    }  while (!xd->Done && !xd->Aborting);
  489.  
  490.    xd->xpru.xpru_updatemask = XPRU_MSG;
  491.    xd->xpru.xpru_msg = "Timed out";
  492.    if (!xd->Done && !xd->Aborting)
  493.       (*xd->xpr_update)(&xd->xpru);
  494.  
  495.    /* reset last character so HostMon isn't confused */
  496.    xd->cchar = -1;
  497. }
  498.  
  499. /* open buffered file */
  500. long bfopen(struct XPR_DATA *xd, char *filename, char *mode)
  501. {
  502.     long file;
  503.     file = (*xd->xpr_fopen)(filename, mode);
  504.     if (!file) return file;
  505.     if (xd->maxiobuf)
  506.     {
  507.         xd->iobuf = AllocMem(xd->maxiobuf, 0L);
  508.         if (xd->iobuf == NULL)
  509.             errormsg(xd, "Not enough memory to buffer I/O");
  510.     }
  511.     if (!xd->iobuf || mode[0] == 'r')
  512.     {
  513.         xd->iobufn = 0;            /* bytes available */
  514.         xd->writing = FALSE;
  515.     }
  516.     else
  517.     {
  518.         xd->iobufn = 0;            /* bytes used */
  519.         xd->writing = TRUE;
  520.     }
  521.     xd->iobufp = xd->iobuf;
  522.     return file;
  523. }
  524.  
  525. /* buffered file close */
  526. long bfclose(struct XPR_DATA *xd, long file)
  527. {
  528.     if (xd->writing && xd->iobufn)
  529.         (*xd->xpr_fwrite)(xd->iobuf, xd->iobufn, 1L, file);
  530.     if (xd->iobuf)
  531.         FreeMem(xd->iobuf, xd->maxiobuf);
  532.     xd->iobuf = NULL;
  533.     return (*xd->xpr_fclose)(file);
  534. }
  535.  
  536. /* buffered file read */
  537. long bfread(struct XPR_DATA *xd, char *buf, long len, long file)
  538. {
  539.     register long n;
  540.     register long remain;
  541.     if (xd->iobuf == NULL)
  542.         return (*xd->xpr_fread)(buf, 1L, 1*len, file);
  543.     remain = len;
  544.     while (remain > 0)
  545.     {
  546.         if (xd->iobufn <= 0)
  547.         {    /* refill buffer */
  548.             xd->iobufn = (*xd->xpr_fread)(xd->iobuf, 1L,
  549.                         xd->maxiobuf, file);
  550.             if (xd->iobufn <= 0)
  551.                 return len - remain;
  552.             xd->iobufp = xd->iobuf;
  553.         }
  554.         n = min(xd->iobufn, remain);
  555.         memcpy(buf, xd->iobufp, (unsigned)n);
  556.         xd->iobufp += n;
  557.         xd->iobufn -= n;
  558.         buf += n;
  559.         remain -= n;
  560.     }
  561.     return len;
  562. }
  563.  
  564. /* buffered file read, with optional text processing */
  565. long xbfread(struct XPR_DATA *xd, char *buf, long len, long file)
  566. {
  567.     unsigned char c;
  568.     long n;
  569.     if (!xd->TextMode)
  570.         return bfread(xd, buf, len, file);
  571.     for (n = 0; n < len; ++n)
  572.     {
  573.         if (xd->TextC >= 0)
  574.         {
  575.             buf[n] = xd->TextC;
  576.             xd->TextC = -1;
  577.         }
  578.         else if (bfread(xd, (char *)&c, 1L, file) != 1)
  579.             break;
  580.         else if (c == LF)
  581.         {
  582.             buf[n] = CR;
  583.             xd->TextC = LF;
  584.         }
  585.         else
  586.             buf[n] = c;
  587.     }
  588.  
  589.     return n;
  590. }
  591.  
  592. /* buffered file write */
  593. long bfwrite(struct XPR_DATA *xd, char *buf, long len, long file)
  594. {
  595.     register long n;
  596.     register long remain;
  597.     if (xd->iobuf == NULL)
  598.         return (*xd->xpr_fwrite)(buf, 1L, 1*len, file);
  599.     remain = len;
  600.     for (;;)
  601.     {
  602.         if (xd->iobufn >= xd->maxiobuf)
  603.         {    /* flush buffer */
  604.             n = (*xd->xpr_fwrite)(xd->iobuf, xd->iobufn, 1L, file);
  605.             if (n != 1)
  606.                 return -1;
  607.             xd->iobufn = 0;
  608.             xd->iobufp = xd->iobuf;
  609.         }
  610.  
  611.         if (remain <= 0) break;
  612.  
  613.         n = xd->maxiobuf - xd->iobufn;
  614.         if (n > remain) n = remain;
  615.         memcpy(xd->iobufp, buf, (unsigned)n);
  616.         xd->iobufp += n;
  617.         xd->iobufn += n;
  618.         buf += n;
  619.         remain -= n;
  620.     }
  621.     return len;
  622. }
  623.  
  624. /* buffered file write with optional text processing */
  625. long xbfwrite(struct XPR_DATA *xd, char *buf, long len, long file)
  626. {
  627.     register int i;
  628.     if (!xd->TextMode)
  629.         return bfwrite(xd, buf, len, file);
  630.     if (xd->TextC == CPMEOF)
  631.         return len;
  632.     for (i = 0; i < len; ++i)
  633.     {
  634.         if (xd->TextC == CR && buf[i] != LF)
  635.         {    /* carriage return not part of CR-LF */
  636.             if (bfwrite(xd, "\n", 1, file) != 1)
  637.                 return i;
  638.         }
  639.         xd->TextC = buf[i];
  640.         if (buf[i] == CPMEOF)
  641.             break;
  642.         if (buf[i] != CR && bfwrite(xd, &buf[i], 1, file) != 1)
  643.             return i;
  644.     }
  645.     return len;
  646. }
  647.