/*
 * The routines in this file
 * handle the reading and writing of
 * disk files. All of details about the
 * reading and writing of the disk are
 * in "fileio.c".
 */
#include <stdio.h>
#include <dos.h>
#include <libraries/asl.h>
#include <clib/asl_protos.h>
#include <clib/dos_protos.h>
#include <clib/asl_pragmas.h>
#include <clib/dos_pragmas.h>
#include <utility/tagitem.h>
#include "ed.h"
#include "fcntl.h"

static char  fname[NFILEN];  /* the last used filename  */
/* static char  dname[NFILEN];  the last used directory */
/* static char  fnpat[16];      the filename pattern    */


#ifdef DEUTSCH
#define OPEN_FILE        "Öffne Datei: "
#define VISIT_FILE       "Lies Datei: "
#define SAVE_FILE        "Sichern als: "
#define INSERT_FILE      "Datei Einfügen: "
#else
#define OPEN_FILE        "Open File: "
#define VISIT_FILE       "Visit File: "
#define SAVE_FILE        "Save As: "
#define INSERT_FILE      "Insert File: "
#endif

extern struct Window *Window;

static char   iNameDir[128];
struct FileRequester *filereq = NULL;

static struct TagItem initTags[] = {
 { ASL_Window,    NULL },
 { ASL_Pattern,   &"#?"},
 { ASL_FuncFlags, FILF_PATGAD},
 { ASL_Dir,       &iNameDir },
 { TAG_END,       NULL },
};

static struct TagItem callTags[] = {
 { ASL_Hail,    NULL },
 { TAG_END,     NULL },
};

/*
 * CheckTxt -- Gibt es ein 'txt' Unterverzeichnis ?
 */
static int CheckTxt()
{
  long lock, Lock();

  lock = Lock("txt", -2);
  if (lock) {
    UnLock(lock);
    return TRUE;
  }
  else
    return FALSE;
}

static void FreeRequest()
{
  FreeAslRequest(filereq);
}

static int ReadFileName(prompt, pattern, filename)
char *prompt;
char *pattern;
char *filename;
{
  int s;

  if (requester) {
    EraseCursor();

    if (AslBase != NULL) {

      if (filereq == NULL) {
        initTags[0].ti_Data = (ULONG)Window;
        filereq = AllocAslRequest(ASL_FileRequest, initTags);
        onexit(FreeRequest);
      }

      if (filereq) {
        callTags[0].ti_Data = (ULONG)prompt;

        s = (int)AslRequest(filereq, callTags);

        if (s) {
          if (filereq->rf_Dir[0] == '\0') {
            strcpy(filename, filereq->rf_File);
          }
          else {
            char c;

            strcpy(filename, filereq->rf_Dir);
            c = filename[strlen(filename)-1];
            if (c != ':' && c != '/')
              strcat(filename, "/");
            strcat(filename, filereq->rf_File);
          }
        }
        else
          filename[0] = '\0';
      }
      else
        s = FALSE;
    }
    else { /* No Asl Library around */
      if (filename[0] == '\0')
        strcpy(filename, iNameDir);

      s = stdfile(prompt, filename, pattern, filename);
    }
  }
  else {
    s = mlreply(prompt, filename, NFILEN);
  }
  return s;
}


/*
 * 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".
 */
fileread(f, n)
{
  if (ReadFileName(OPEN_FILE, NULL, fname))
    return(READIN());
}

/*
 * Select a file for editing.
 * Look around to see if you can find the
 * file in another buffer; if you can find it
 * just switch to the buffer. If you cannot find
 * the file, create a new buffer, read in the
 * text, and switch to the new buffer.
 * Bound to C-X C-V.
 */
filevisit(f, n)
{
  register BUFFER *bp;
  register WINDOW *wp;
  register LINE  *lp;
  register int i;
  register int s;
  char bname[NBUFN];

  if (s = ReadFileName(VISIT_FILE, NULL, fname)) {
    for (bp=bheadp; bp!=NULL; 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;
        }
        prev_bp = curbp;
        curbp = bp;
        curwp->w_bufp = bp;
        if (bp->b_nwnd++ == 0) {
          curwp->w_dotp = bp->b_dotp;
          curwp->w_doto = bp->b_doto;
          curwp->w_markp = bp->b_markp;
          curwp->w_marko = bp->b_marko;
        }
        else {
          wp = wheadp;
          while (wp != NULL) {
            if (wp!=curwp && wp->w_bufp==bp) {
              curwp->w_dotp = wp->w_dotp;
              curwp->w_doto = wp->w_doto;
              curwp->w_markp = wp->w_markp;
              curwp->w_marko = wp->w_marko;
              break;
            }
          wp = wp->w_wndp;
          }
        }
        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;
#ifdef DEUTSCH
        mlwrite("[Alter Buffer]");
#else
        mlwrite("[Old buffer]");
#endif
        return (TRUE);
      }
    }
    makename(bname, fname);   /* New buffer name.  */
    while ((bp=bfind(bname, FALSE, 0)) != NULL) {
#ifdef DEUTSCH
      s = mlreply("Buffer Name: ", bname, NBUFN);
#else
      s = mlreply("Buffer name: ", bname, NBUFN);
#endif
      if (s == ABORT)   /* ^G to just quit */
        return (s);
      if (s == FALSE) {   /* CR to clobber it  */
        makename(bname, fname);
        break;
      }
    }
    if (bp==NULL && (bp=bfind(bname, TRUE, 0))==NULL) {
#ifdef DEUTSCH
      mlwrite("Ich kann keinen neuen Buffer eröffnen");
#else
      mlwrite("Cannot create buffer");
#endif
      return (FALSE);
    }
    remwind(curwp,FALSE);
    prev_bp = curbp;
    curbp = bp;     /* Switch to it. */
    curwp->w_bufp = bp;
    curbp->b_nwnd++;
    return (READIN());   /* Read it in.  */
  }
  else {
    return FALSE;
  }
}

/*
 * Read file "fname" into the current
 * buffer, blowing away any text found there. Called
 * by both the read and visit 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.
 */
readin(char *name)
{
  char  *tmp, *tmp2;
  char   buf[128];
  int    s;

 /*
  * 25.10.90/ms
  * 28.2.91/ms
  *
  * Supports txt subdirectory
  */
  strcpy(fname, name);
  if (((s = ffropen(name)) != FIOSUC)             /* File not found. */
      && (strchr(name, ':') == NULL)              /* No path ?       */
      && (strchr(name, '/') == NULL) )
  {
    strcpy(fname, "txt/");
    strcat(fname, name);
    if ( (s = ffropen(fname)) != FIOSUC ) {
      strcpy(fname, "/txt/");
      strcat(fname, name);
      s = ffropen(fname);
    }

    if (s != FIOSUC)
      strcpy(fname, name);
  }

  ffclose();

 /*
  * Get Path out of the file name
  */
  strcpy(buf, fname);
  tmp = (char *)strchr(buf, ':');
  if (tmp != NULL) {
    *(tmp+1) = '\0';
    strcpy(iNameDir, buf);
  }
  strcpy(buf, fname);
  tmp = NULL;
  tmp2 = (char *)strchr(buf, '/');
  while (tmp2 != NULL) {
    tmp = tmp2;
    tmp2 = (char *)strchr((tmp2+1), '/');
  }
  if (tmp != NULL) {
    *(tmp+1) = '\0';
    strcpy(iNameDir, buf);
  }
  return READIN();
}

static READIN()
{
  register LINE   *lp1;
  register LINE   *lp2;
  register int     i;
  register WINDOW *wp;
  register BUFFER *bp;
  register int     s;
  long             nbytes;
  register int     nline;
  char             line[NLINE];
  struct FILEINFO *fib;

  bp = curbp;     /* Cheap.   */
  if ((s = bclear(bp)) != TRUE)   /* Might be old. */
    return (s);
  bp->b_flag &= ~(BFTEMP|BFCHG);

  strcpy(bp->b_fname, fname);

  if ((s=ffropen(fname)) == FIOERR)  /* Hard file open. */
    goto out;

  if (s == FIOFNF) {   /* File not found. */

#ifdef DEUTSCH
    mlwrite("[Neue Datei]");
#else
   mlwrite("[New file]");
#endif
   goto out;
 }
 /*
  * Get the name again
  */
  strcpy(bp->b_fname, fname);

 /*
  * 27.2.91/ms
  * Get protection bits of the file
  * 8.8.92/bp
  * Get Comment of the file, too
  */
  fib = (struct FILEINFO *)malloc(sizeof(struct FILEINFO));
  if (dfind(fib, bp->b_fname, 0) == 0) {
    bp->b_prot = fib->fib_Protection;
    strcpy(bp->b_filenote, fib->fib_Comment);
  } else {
    bp->b_prot = -1;
    strcpy(bp->b_filenote, "");
  }
  free(fib);

#ifdef DEUTSCH
 mlwrite("[Einlesen der Datei]");
#else
 mlwrite("[Reading file]");
#endif
 nline = 0;
 while ((s=ffgetline(line, NLINE,&nbytes)) == FIOSUC) {
  if ((lp1=lalloc(nbytes)) == NULL) {
   s = FIOERR;   /* Keep message on the */
   break;   /* display.   */
  }
  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;
  if (((short)nline % 50) == 0) {
#ifdef DEUTSCH
    mlwrite("[%d Zeilen gelesen]", nline);
#else
    mlwrite("[Read %d lines]", nline);
#endif
  }
 }
 ffclose();    /* Ignore errors.  */
 if (s == FIOEOF) {   /* Don't zap message!  */
  if (nline == 1)
#ifdef DEUTSCH
   mlwrite("[1 Zeile gelesen]");
#else
   mlwrite("[Read 1 line]");
#endif
  else
#ifdef DEUTSCH
   mlwrite("[%d Zeilen gelesen]", nline);
#else
   mlwrite("[Read %d lines]", nline);
#endif
  ReadErrDat();
 }
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)   /* False if error. */
  return (FALSE);
 if (curbp->b_nextErr) {
  Delay(50L);
  NextM2Error();
 }
 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.
 */
makename(bname, fname)
char bname[];
char fname[];
{
 register char  *cp1;
 register char  *cp2;

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

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

/*
 * 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".
 */
filewrite(f, n)
{
  WINDOW    *wp;
  int        s;

  if (s = ReadFileName(SAVE_FILE, NULL, fname)) {
    if ((s = writeout(fname)) == TRUE) {
      strcpy(curbp->b_fname, fname);
      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".
 */
filesave(f, n)
{
 register WINDOW *wp;
 register int s;

 if ((curbp->b_flag&BFCHG) == 0)  /* Return, no changes. */
  return (TRUE);
 if (curbp->b_fname[0] == 0) {   /* Must have a name. */
#ifdef DEUTSCH
  mlwrite("Kein Dateiname vorhanden");
#else
  mlwrite("No file name");
#endif
  return (FALSE);
 }
 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.
 * Updated to do reliable writing using a backup file whose name is
 * fn with a 'O' appended.
 */
writeout(fn)
char *fn;
{
  register int s;
  register LINE  *lp;
  register int nline;
  char fno[NFILEN+1], fnn[NFILEN+1];

  /* create file names */
  strcpy(fnn,fn); strcat( fnn, "N"); /* for new file */
  strcpy(fno,fn); strcat( fno, "O"); /* for old file */
  if ((s=ffwopen(fnn)) != FIOSUC)  /* Open writes message. */
    return (FALSE);
  lp = lforw(curbp->b_linep);   /* First line.  */
  nline = 0;    /* Number of lines.  */
  while (lp != curbp->b_linep) {
   /*
    * 19.8.89/ms stripLine engefügt.
    */
    if (stripline)
      stripLine(lp);

    if (llength(lp) > NLINE) {
#ifdef DEUTSCH
      mlwrite("Datei enthält überlange Zeilen");
#else
      mlwrite("File has long line");
#endif
    }
    if ((s=ffputline(&lp->l_text[0], llength(lp))) != FIOSUC)
      break;
    ++nline;
    if ((short)nline % 50 == 0) {
#ifdef DEUTSCH
      mlwrite("[%d Zeilen geschrieben]", nline);
#else
      mlwrite("[Wrote %d lines]", nline);
#endif
    }
    lp = lforw(lp);
  }
  if (s == FIOSUC) {   /* No write error. */
    s = ffclose();
    if (s == FIOSUC) {  /* No close error. */
      if (nline == 1)
#ifdef DEUTSCH
        mlwrite("[1 Zeile geschrieben]");
#else
        mlwrite("[Wrote 1 line]");
#endif
      else
#ifdef DEUTSCH
       mlwrite("[%d Zeilen geschrieben]", nline);
#else
       mlwrite("[Wrote %d lines]", nline);
#endif
    }
  }
  else     /* Ignore close error  */
    ffclose();   /* if a write error. */

  if (s != FIOSUC)   /* Some sort of error. */
    return FALSE;

  {
    if (access( fn, 0) == 0) {  /* original file exists */
      if (keepBackup) {
       /*
        * delete old back up and rename original to back up name
        */
        unlink( fno);
        if (rename( fn, fno) == -1) {
#ifdef DEUTSCH
          mlwrite("Fehler bei Umbennenung der alten Datei");
#else
          mlwrite("Old file rename failed");
#endif
          return FALSE;
        }
      }
      else /* delete previous version */
        unlink(fn);
    }
    else
      keepBackup = FALSE; /* no backup if no file around */
  }

 /* rename new to original name */
  if (rename( fnn, fn) == -1) {
#ifdef DEUTSCH
    mlwrite("Fehler bei der Umbennenung der neuen Datei");
#else
    mlwrite("New file rename failed");
#endif
    return FALSE;
  }

 /*
  * Set the protection bits on the file as
  * they were before. Do _NOT_ copy the
  * achive flag though.
  * 8.8.92/bp
  * Set old Comment, too
  */
  if (curbp->b_prot != -1) {
    curbp->b_prot &= ~FIBF_ARCHIVE;
    SetProtection(fn, curbp->b_prot);
    SetComment(fn, curbp->b_filenote);
  }

  if (keepBackup && icon) {
    copyicon(fn, fno);
  }
  if (icon)
    makeicon(fn);

  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.
 */
filename(f, n)
{
 register WINDOW *wp;
 register int s;
 char  fname[NFILEN];

 strcpy(fname, curbp->b_fname);
#ifdef DEUTSCH
 if ((s=mlreply("Neuer Dateiname: ", fname, NFILEN)) == ABORT)
#else
 if ((s=mlreply("Name: ", fname, NFILEN)) == ABORT)
#endif
  return (s);
 if (s == FALSE)
  strcpy(curbp->b_fname, "");
 else
  strcpy(curbp->b_fname, fname);
 wp = wheadp;    /* Update mode lines.  */
 while (wp != NULL) {
  if (wp->w_bufp == curbp)
   wp->w_flag |= WFMODE;
  wp = wp->w_wndp;
 }
 curbp->b_flag |= BFCHG;
 mlwrite("");
 return (TRUE);
}

fileinsert(f,n)
{
  int    i;
  int    s;
  int    nline;
  LINE   *lp1, *lp2;
  long   nbytes;
  char   line[NLINE];

  if (s = ReadFileName(INSERT_FILE, NULL, fname)) {
    if ((s = ffropen(fname)) == FIOERR) { /* Hard file open. */
#ifdef DEUTSCH
      mlwrite("Datei <%s> konnte nicht geöffnet werden", fname);
#else
      mlwrite("File <%s> could not be opened", fname);
#endif
      return FALSE;
    }

    nline = 0;
    while ((s = ffgetline(line, NLINE,&nbytes)) == FIOSUC) {
      if ((lp1=lalloc(nbytes)) == NULL) {
        s = FIOERR;   /* Keep message on the */
        break;   /* display.   */
      }

      lp2 = lback(curwp->w_dotp);
      lp2->l_fp = lp1;
      lp1->l_fp = curwp->w_dotp;
      lp1->l_bp = lp2;
      curwp->w_dotp->l_bp = lp1;
      curbp->b_flag |= BFCHG;

      for (i=0; i<nbytes; ++i)
        lputc(lp1, i, line[i]);
      ++nline;
      if ((nline % 10) == 0) {
#ifdef DEUTSCH
        mlwrite("[%d Zeilen gelesen]", nline);
#else
        mlwrite("[Read %d lines]", nline);
#endif
      }
    }
    sgarbf = TRUE;
    ffclose();    /* Ignore errors.  */
  }
}


void ohm_project()
{
  mlreply("OHM Projekt: ", ohmProject, 32);
}
