#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define  EDITOR_SUPPORT 1

// This is Hamish Dewar's syntax checker for the subset of Imp80 and Imp77 agreed upon
// by the ERCC and the Department of Computer Science.

// Note the small differences between chimps.imp77 and chimps.imp80 - the latter allows
// the X'nnnn' style of constants, whereas this and chimps.imp77 do not.

// Unfortunately the current Imp to C converter loses comments.  That will be fixed
// eventually, the underlying parser does preserve them.  I've added them manually
// for now, but not the many comments which comprised of commented-out Imp code...

// This translation is now almost all automatic.  The small manual tweaks needed were
// for arrays with non-0 lower bounds, and treating %name parameters to procedures
// properly.  See the chimps77.c.PENDING-DIFF file for all changes.

// Obviously a main() harness had to be added as we are not calling this from Vecce or EMAS.
// I'm not entirely sure I know what the params should be. This is just a guess.

// -GT


// IMP syntax checker - based on M68000 IMP compiler
//                               (qv for fuller annotation)
//   Hamish Dewar  Computer Science   Edinburgh University  1983
// 
typedef struct _imp_string {
  unsigned char Length;
  unsigned char Charno[255];
} _imp_string;

const int Maxname = 127;
/*
%recordformat EDFILE(%integer start1,lim1, {part 1}
                              start2,lim2, {part2}
                              lim, {VMLIM}
                              lbeg,fp,change,flag,
                              line  {line number of current pos},
                              diff  {diff between LINE and ROW},
                              shift {right shift of window on file},
                %byteinteger  top  {top row of sub_window},
                              win  {floating top},
                              bot  {bottom row +1 of sub_window},
                              min  {minimum window size},
                              row  {last row position},
                              col  {last col position},
             %string(maxname) name)
 */
typedef struct Edfile {
  int Start1, Lim1;
  int Start2, Lim2;
  int Lim;
  int Lbeg, Fp, Change, Flag;
  int Line;
  int Diff;
  int Shift;
  unsigned char Top;
  unsigned char Win;
  unsigned char Bot;
  unsigned char Min;
  unsigned char Row;
  unsigned char Col;
  //unsigned char align1;
  //unsigned char align2;
  _imp_string Name;
} Edfile;
#ifdef EDITOR_SUPPORT
void _EDI(Edfile *Main, Edfile *Sec, _imp_string Message);    // <---- TO DO!
#endif

void _CONNECTEDFILE(Edfile *F);                               // <----
void _DISCONNECTEDFILE(Edfile *F); /* (unused) */

#ifdef EDITOR_SUPPORT
void _EDI(Edfile *Main, Edfile *Sec, _imp_string Message) {
  /*
  This is the procedure used to call the editor itself.
  MAIN identifies the file being edited and SEC any secondary input file
  supplied at the outset (a dummy edfile record set to zero will suffice
  if not relevant).  Message provides text to be printed at the bottom
  of the screen at the outset.
  Apart from the fields set up by CONNECT EDFILE (and subsequently modified by
  the editor or, compatibly, by other programs), the following fields are
  relevant:

  FP   : the current position in the file, which must always satisfy
          either START1 <= FP < LIM1
          or     START2 <= FP <= LIM2
  LINE : the line number of the current line (if known)
         If zero, the editor will establish the line number afresh

  On return from the editor, any of the pointer values may have changed
  and the field CHANGE will identify the earliest position in the file
  modified during the last edit.  LIM1 is guaranteed to co-incide with
  a line break.  (Programs which cannot cope with a file split in two,
  even at a guaranteed line break, are free to consolidate the file
  by copying part 1 DOWN to be adjacent to part2 and adjusting all
  pointers accordingly).

  FLAG will be negative if the user has abandoned the edit.
  */
}
#endif

void _CONNECTEDFILE(Edfile *F) { // CONNECT EDFILE - Load included file F->Name here?
  /*
  This routine is used to connect (ie read into store) a file to be edited.
  Before the call, the field NAME should be set to the name of the file,
  and the field FLAG should be set to the number of extra bytes to be
  allowed for expansion (32k is a typical figure).
  On successful return, the file specified will have been read into store
  at a system-selected position, the field LIM1 will be equal to START1,
  and the fields START2 and LIM2 will delimit the whole of the file.
  The fields LINE, CHANGE and FLAG will be zero.
  If FLAG is non-zero, this indicates a failure to connect; a report
  will already have been made.
  */
  char buff[1000000]; // space on stack large enough for file.
  FILE *src;
  int i, size = 0;
  F->Name.Charno[F->Name.Length] = '\0';
  //fprintf(stderr, "Opening %s\n", F->Name.Charno);
  src = fopen(F->Name.Charno, "r");
  if (src == NULL) {
    F->Flag = errno;
    fprintf(stderr, "chimps: %s - %s\n", F->Name.Charno, strerror(errno));
    return;
  }
  for (;;) {
    int c = fgetc(src);
    if (c == EOF) break;
    buff[size++] = c&255;
    //fputc(c&255, stderr);
  }

  // Now we know the size, transfer from stack to heap.
  F->Start1 = (int)malloc(size+2)+1;

  *(char *)(F->Start1-1) = '\n'; // add an extra NL before and after buffer (for Ecce to bounce off)
  for (i = 0; i < size; i++) *(char *)(F->Start1+i) = buff[i];
  *(char *)(F->Start1+size) = '\n';

  F->Lbeg   = F->Start1;
  F->Lim1   = F->Start1+size;
  F->Lim    = F->Lim1;
  F->Fp     = F->Start1;
  F->Start2 = F->Lim1;
  F->Lim2   = F->Lim1;
  //fprintf(stderr, "Checking %s - %d bytes from %d to %d\n", F->Name.cstr.s, size, F->Start1, F->Lim1);
  /*
  F->Change;
  F->Flag;
  F->Line;
  F->Diff;
  F->Shift;
  F->Top;
  F->Win;
  F->Bot;
  F->Min;
  F->Row;
  F->Col;
  */
}
void _DISCONNECTEDFILE(Edfile *F) { // DISCONNECT EDFILE - Unstack?
  /*
  This routine is used to close off an edited file.  It is particularly
  important to ensure that this procedure is called in all cases, even
  if the associated program is being abandoned, since otherwise changes
  made to the file are all lost.
  On return, NAME will indicate the name under which the file was actually
  written (maybe different from that originally specified).
  */
  free((char *)(F->Start1-1));
}
