/*
 *Copyright (c) 1996 by Oracle Corporation. All Rights Reserved. \
  $Header: filep.c 23-aug-96.12:52:57 aswang Exp $
*/

/*
 * Routines to open/close/read/write local file
 * For binary transmission, we use the UNIX open/read/write system calls
 * For ascii transmission, we use the UNIX standard i/o routine 
 * fopen/getc/putc
 */

#ifndef TFTPDEF
# include "tftpdef.h"
#endif

#include <string.h>
#ifdef VMS
#include <unixio.h>
#endif

int err_dump();

static int lastcr = 0;  /* 1 if last char was a carriage-return */
static int nextchar = 0;
 
FILE *file_open(fname, mode, initblknum)
char *fname;
char *mode;
int initblknum;
{
  register FILE *fp;

  if (strcmp(fname, "-") == 0)
    fp  = stdout;
  else if ((fp = fopen(fname, mode)) == NULL)
           return ((FILE *)0);

  nextblknum = initblknum;  /* for first data or ACK packet */
  lastcr  = 0;              /* for file_write()             */
  nextchar = -1;            /* for file_read()              */

  DEBUG2("file_open: opened %s, mode = %s", fname, mode);
  return(fp);
}

void file_close(fp)
FILE *fp;
{
  if (lastcr)
    err_dump("final character was a CR");
  if (nextchar >= 0)
    err_dump("nextchar >= 0");

  if (fp  == stdout)
    return;
  else if (fclose(fp) == EOF)
    err_dump("fclose error");
}


/*
 * Read data from local file.
 * Here we handle any conversion between the file's mode on the local system
 * and the network mode
 *
 * Return the number of bytes read (between 1 and maxnbytes, inclusive) 
 * or 0 on EOF
 */
int file_read(fp, ptr, maxnbytes, mode)
FILE *fp;
char *ptr;
int maxnbytes;
int mode;
{
  register int c, count;

  if (mode == MODE_BINARY) {
#ifndef WIN32
    count = read(fileno(fp), ptr, maxnbytes);
#else
    count = _read(fileno(fp), ptr, maxnbytes);
#endif
    if (count < 0)
      err_dump("read error from local file");
    return (count);
  }
  else if (mode == MODE_ASCII)
  {
    /*
     * For files that are transferred in netascii, we must perform the reverse
     * conversions that file_write() does. Note that we have to use the
     * global "nextchar" to remember if the next character to output is a 
     * linefeed or a null, since the second byte of a 2-byte sequence may not 
     * fit in the current buffer, and may have to go as the first byte of 
     * the next buffer (i.e., we have to remember this fact from one call
     * to the next)
     */
    for (count = 0; count < maxnbytes; count++)
    {
      if (nextchar >=0 )
      {
        *ptr++ = nextchar;
        nextchar = -1; 
        continue;
      }
      c = getc(fp);

      if (c == EOF) /* EOF return means eof or error */
      {
        if (ferror(fp))
          err_dump("read error from getc on local file");
        return(count);
      }
      else if (c == '\n')
      {
        c = '\r';        /* newline -> CR, LF */
        nextchar = '\n';
      }
      else if (c == '\r')
      {
        nextchar = '\0';         /* CR -> CR, NULL */
      }
      else
        nextchar = -1;
      
      *ptr++ = c;
    }
    return(count);
  }
  else 
    err_dump("unknown Mode value");
}


/*
 * Write data to the local file.
 * Here we handle any conversion between the mode of the file on the network
 * and the local system's conversion.
 */
void file_write(fp, ptr, nbytes, mode)
FILE *fp;
char *ptr;
int nbytes;
int mode;
{
  register int c, i;

  if (mode == MODE_BINARY)
  {
#ifndef WIN32
    i = write(fileno(fp), ptr, nbytes);
#else
    i = _write(fileno(fp), ptr, nbytes);
#endif
    if (i != nbytes)
      err_dump("write error to local file, i = %d", i);

  }
  else if (mode == MODE_ASCII)
  {
    /*
     * For files that are transferred in netascii, we must perform the 
     * following conversion
     * CR, LF   -> newline = '\n'
     * CR, NULL -> CR      = '\r'
     * CR, anything else -> undefined
     *
     * Note that we have to use the global "lastcr" to remember if the last
     * character was a CR, LF or not, since if the last character of a buffer
     * is a CR, we have to remember that when we are called for the next 
     * buffer.
     */

    for (i = 0; i < nbytes; i++)
    {
      c = *ptr++;
      if (lastcr)
      {
        if (c == '\n')
          c = '\n';
        else if ( c == '\0')
          c = '\r';
        else 
          err_dump("CR followed by 0x%02x", c);
        lastcr = 0;
      }
      else if ( c == '\r')
      {
        lastcr = 1;
        continue;      /* get next char */
      }
      if (putc(c, fp) == EOF)
        err_dump("write error to local file");
    }
  }
  else
    err_dump("Unknown MODE value");
}
