/* This file is in the public domain. */

/*
 * The routines in this file handle the reading and writing of disk files.
 * All details about the reading and writing of the disk are in "fileio.c"
 */

#include <string.h>		/* strncpy(3) */
#include "estruct.h"
#include "edef.h"

extern int mlreply(char *prompt, char *buf, int nbuf);
extern int swbuffer(BUFFER *bp);
extern void mlwrite();
extern int bclear(BUFFER *bp);
extern int ffropen(char *fn);
extern int ffgetline(char buf[], int nbuf);
extern int ffwopen(char *fn);
extern int ffclose();
extern int ffputline(char buf[], int nbuf);
extern BUFFER *bfind();
extern LINE *lalloc();

int fileread(int f, int n);
int insfile(int f, int n);
int getfile(char fname[]);
int readin(char fname[]);
void makename(char bname[], char fname[]);
int filewrite(int f, int n);
int filesave(int f, int n);
int writeout(char *fn);
int filename(int f, int n);
int ifile(char fname[]);

/*
 * Read a file into the current buffer. This is really easy; all you do it
 * find the name of the file, and call the standard "read a file into the
 * current buffer" code. Bound to "C-X C-R"
 */
int fileread(int f, int n)
{
  int s;
  char fname[NFILEN];

  if ((s = mlreply("Read file: ", fname, NFILEN)) != TRUE)
    return (s);
  return (readin(fname));
}

/*
 * Insert a file into the current buffer. This is really easy; all you do it
 * find the name of the file, and call the standard "insert a file into the
 * current buffer" code. Bound to "C-X C-I".
 */
int insfile(int f, int n)
{
  int s;
  char fname[NFILEN];

  if ((s = mlreply("Insert file: ", fname, NFILEN)) != TRUE)
    return (s);
  return (ifile(fname));
}

int getfile(char fname[])
{
  BUFFER *bp;
  LINE *lp;
  char bname[NBUFN];		/* buffer name to put file */
  int i, s;

  for (bp = bheadp; bp != (BUFFER*)0; bp = bp->b_bufp)
    {
      if ((bp->b_flag & BFTEMP) == 0 && strcmp(bp->b_fname, fname) == 0)
	{
	  if (--curbp->b_nwnd == 0)
	    {
	      curbp->b_dotp = curwp->w_dotp;
	      curbp->b_doto = curwp->w_doto;
	      curbp->b_markp = curwp->w_markp;
	      curbp->b_marko = curwp->w_marko;
	    }
	  swbuffer(bp);
	  lp = curwp->w_dotp;
	  i = curwp->w_ntrows / 2;
	  while (i-- && lback(lp) != curbp->b_linep)
	    lp = lback(lp);
	  curwp->w_linep = lp;
	  curwp->w_flag |= WFMODE | WFHARD;
	  mlwrite("[Old buffer]");
	  return (TRUE);
	}
    }
  makename(bname, fname);	/* New buffer name */
  while ((bp = bfind(bname, FALSE, 0)) != (BUFFER*)0)
    {
      s = mlreply("Buffer name: ", bname, NBUFN);
      if (s == ABORT)		/* ^G to just quit */
	return (s);
      if (s == FALSE)
	{			/* CR to clobber it */
	  makename(bname, fname);
	  break;
	}
    }
  if (bp == (BUFFER*)0 && (bp = bfind(bname, TRUE, 0)) == (BUFFER*)0)
    {
      mlwrite("Cannot create buffer");
      return (FALSE);
    }
  if (--curbp->b_nwnd == 0)
    {				/* Undisplay */
      curbp->b_dotp = curwp->w_dotp;
      curbp->b_doto = curwp->w_doto;
      curbp->b_markp = curwp->w_markp;
      curbp->b_marko = curwp->w_marko;
    }
  curbp = bp;			/* Switch to it */
  curwp->w_bufp = bp;
  curbp->b_nwnd++;
  return (readin(fname));	/* Read it in */
}

/*
 * Read file "fname" into the current buffer, blowing away any text found
 * there. Called by both the read and find commands. Return the final status
 * of the read. Also called by the mainline, to read in a file specified on
 * the command line as an argument.
 */
int readin(char fname[])
{
  LINE *lp1, *lp2;
  WINDOW *wp;
  BUFFER *bp;
  char line[NLINE];
  int nbytes, s, i;
  int nline = 0;		/* initialize here to silence a gcc warning */
  int lflag;			/* any lines longer than allowed? */

  bp = curbp;			/* Cheap */
  if ((s = bclear(bp)) != TRUE) /* Might be old */
    return (s);
  bp->b_flag &= ~(BFTEMP | BFCHG);
  strncpy(bp->b_fname, fname, NFILEN);
  if ((s = ffropen(fname)) == FIOERR) /* Hard file open */
    goto out;
  if (s == FIOFNF)
    {				/* File not found */
      mlwrite("[New file]");
      goto out;
    }
  mlwrite("[Reading file]");
  lflag = FALSE;
  while ((s = ffgetline(line, NLINE)) == FIOSUC || s == FIOLNG)
    {
      if (s == FIOLNG)
	lflag = TRUE;
      nbytes = strlen(line);
      if ((lp1 = lalloc(nbytes)) == NULL)
	{
	  s = FIOERR;		/* Keep message on the display */
	  break;
	}
      lp2 = lback(curbp->b_linep);
      lp2->l_fp = lp1;
      lp1->l_fp = curbp->b_linep;
      lp1->l_bp = lp2;
      curbp->b_linep->l_bp = lp1;
      for (i = 0; i < nbytes; ++i)
	lputc(lp1, i, line[i]);
      ++nline;
    }
  ffclose();			/* Ignore errors */
  if (s == FIOEOF)
    {				/* Don't zap message! */
      if (nline != 1)
	mlwrite("[Read %d lines]", nline);
      else
	mlwrite("[Read 1 line]");
    }
  if (lflag)
    {
      if (nline != 1)
	mlwrite("[Read %d lines: Long lines wrapped]", nline);
      else
	mlwrite("[Read 1 line: Long lines wrapped]");
    }
  curwp->w_bufp->b_lines = nline;
 out:
  for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
    {
      if (wp->w_bufp == curbp)
	{
	  wp->w_linep = lforw(curbp->b_linep);
	  wp->w_dotp = lforw(curbp->b_linep);
	  wp->w_doto = 0;
	  wp->w_markp = NULL;
	  wp->w_marko = 0;
	  wp->w_flag |= WFMODE | WFHARD;
	}
    }
  if (s == FIOERR || s == FIOFNF) /* False if error */
    return (FALSE);
  return (TRUE);
}

/*
 * Take a file name, and from it fabricate a buffer name. This routine knows
 * about the syntax of file names on the target system. I suppose that this
 * information could be put in a better place than a line of code.
 */
void makename(char bname[], char fname[])
{
  char *cp1, *cp2;

  cp1 = &fname[0];
  while (*cp1 != 0)
    ++cp1;

  while (cp1 != &fname[0] && cp1[-1] != '/')
    --cp1;
  cp2 = &bname[0];
  while (cp2 != &bname[NBUFN - 1] && *cp1 != 0 && *cp1 != ';')
    *cp2++ = *cp1++;
  *cp2 = 0;
}

/*
 * Ask for a file name, and write the contents of the current buffer to that
 * file. Update the remembered file name and clear the buffer changed flag.
 * This handling of file names is different from the earlier versions, and is
 * more compatable with Gosling EMACS than with ITS EMACS. Bound to "C-X C-W".
 */
int filewrite(int f, int n)
{
  WINDOW *wp;
  char fname[NFILEN];
  int s;

  if ((s = mlreply("Write file: ", fname, NFILEN)) != TRUE)
    return (s);
  if ((s = writeout(fname)) == TRUE)
    {
      strncpy(curbp->b_fname, fname, NFILEN);
      curbp->b_flag &= ~BFCHG;
      wp = wheadp;		/* Update mode lines */
      while (wp != NULL)
	{
	  if (wp->w_bufp == curbp)
	    wp->w_flag |= WFMODE;
	  wp = wp->w_wndp;
	}
    }
  return (s);
}

/*
 * Save the contents of the current buffer in its associatd file. No nothing
 * if nothing has changed (this may be a bug, not a feature). Error if there
 * is no remembered file name for the buffer. Bound to "C-X C-S". May get
 * called by "C-Z"
 */
int filesave(int f, int n)
{
  WINDOW *wp;
  int s;

  if ((curbp->b_flag & BFCHG) == 0) /* Return, no changes */
    return (TRUE);
  if (curbp->b_fname[0] == 0)
      filename(f, n);		/* Must have a name */
  if ((s = writeout(curbp->b_fname)) == TRUE)
    {
      curbp->b_flag &= ~BFCHG;
      wp = wheadp;		/* Update mode lines */
      while (wp != NULL)
	{
	  if (wp->w_bufp == curbp)
	    wp->w_flag |= WFMODE;
	  wp = wp->w_wndp;
	}
    }
  return (s);
}

/*
 * This function performs the details of file writing. Uses the file
 * management routines in the "fileio.c" package. The number of lines written
 * is displayed. Sadly, it looks inside a LINE; provide a macro for this. Most
 * of the grief is error checking of some sort.
 */
int writeout(char *fn)
{
  LINE *lp;
  int nline, s;

  if ((s = ffwopen(fn)) != FIOSUC) /* Open writes message */
    return (FALSE);
  mlwrite("[Writing]");		/* tell us were writing */
  lp = lforw(curbp->b_linep);	/* First line */
  nline = 0;			/* Number of lines */
  while (lp != curbp->b_linep)
    {
      if ((s = ffputline(&lp->l_text[0], llength(lp))) != FIOSUC)
	break;
      ++nline;
      lp = lforw(lp);
    }
  if (s == FIOSUC)
    {				/* No write error */
      s = ffclose();
      if (s == FIOSUC)
	{			/* No close error */
	  if (nline != 1)
	    mlwrite("[Wrote %d lines]", nline);
	  else
	    mlwrite("[Wrote 1 line]");
	}
    }
  else				/* ignore close error */
    ffclose();			/* if a write error */
  if (s != FIOSUC)		/* some sort of error */
    return (FALSE);
  return (TRUE);
}

/*
 * The command allows the user to modify the file name associated with the
 * current buffer. It is like the "f" command in UNIX "ed". The operation is
 * simple; just zap the name in the BUFFER structure, and mark the windows as
 * needing an update. You can type a blank line at the prompt if you wish.
 */
int filename(int f, int n)
{
  WINDOW *wp;
  char fname[NFILEN];
  int s;

  if ((s = mlreply("Name: ", fname, NFILEN)) == ABORT)
    return (s);
  if (s == FALSE)
    strncpy(curbp->b_fname, "", 1);
  else
    strncpy(curbp->b_fname, fname, NFILEN);
  wp = wheadp;			/* update mode lines */
  while (wp != NULL)
    {
      if (wp->w_bufp == curbp)
	wp->w_flag |= WFMODE;
      wp = wp->w_wndp;
    }
  return (TRUE);
}

/*
 * Insert file "fname" into the current buffer, Called by insert file command.
 * Return the final status of the read.
 */
int ifile(char fname[])
{
  LINE *lp0, *lp1, *lp2;
  BUFFER *bp;
  char line[NLINE];
  int i, s, nbytes;
  int nline = 0;
  int lflag;			/* any lines longer than allowed? */

  bp = curbp;			/* Cheap */
  bp->b_flag |= BFCHG;		/* we have changed */
  bp->b_flag &= ~BFTEMP;	/* and are not temporary */
  if ((s = ffropen(fname)) == FIOERR) /* Hard file open */
    goto out;
  if (s == FIOFNF)
    {				/* File not found */
      mlwrite("[No such file]");
      return (FALSE);
    }
  mlwrite("[Inserting file]");

  /* back up a line and save the mark here */
  curwp->w_dotp = lback(curwp->w_dotp);
  curwp->w_doto = 0;
  curwp->w_markp = curwp->w_dotp;
  curwp->w_marko = 0;

  lflag = FALSE;
  while ((s = ffgetline(line, NLINE)) == FIOSUC || s == FIOLNG)
    {
      if (s == FIOLNG)
	lflag = TRUE;
      nbytes = strlen(line);
      if ((lp1 = lalloc(nbytes)) == NULL)
	{
	  s = FIOERR;		/* keep message on the */
	  break;		/* display */
	}
      lp0 = curwp->w_dotp;	/* line previous to insert */
      lp2 = lp0->l_fp;		/* line after insert */

      /* re-link new line between lp0 and lp2 */
      lp2->l_bp = lp1;
      lp0->l_fp = lp1;
      lp1->l_bp = lp0;
      lp1->l_fp = lp2;

      /* and advance and write out the current line */
      curwp->w_dotp = lp1;
      for (i = 0; i < nbytes; ++i)
	lputc(lp1, i, line[i]);
      ++nline;
    }
  ffclose();			/* Ignore errors */
  curwp->w_markp = lforw(curwp->w_markp);
  if (s == FIOEOF)
    {				/* Don't zap message! */
      if (nline != 1)
	mlwrite("[Inserted %d lines]", nline);
      else
	mlwrite("[Inserted 1 line]");
    }
  if (lflag)
    {
      if (nline != 1)
	mlwrite("[Inserted %d lines: Long lines wrapped]", nline);
      else
	mlwrite("[Inserted 1 line: Long lines wrapped]");
    }
 out:
  /* advance to the next line and mark the window for changes */
  curwp->w_dotp = lforw(curwp->w_dotp);
  curwp->w_flag |= WFHARD;

  /* copy window parameters back to the buffer structure */
  curbp->b_dotp = curwp->w_dotp;
  curbp->b_doto = curwp->w_doto;
  curbp->b_markp = curwp->w_markp;
  curbp->b_marko = curwp->w_marko;

  /* we need to update number of lines in the buffer */
  curwp->w_bufp->b_lines += nline;

  if (s == FIOERR)		/* False if error */
    return (FALSE);
  return (TRUE);
}
