/*
 * Modula-2 Error Handling, M. Schaub, JR, 16.8.88
 *
 * Wenn man dies liest, könnte man denken, die einzige
 * C-Anweisung sei der Typecast!! /bp
 *
 */

#include <stdio.h>
#include <intuition/intuition.h>
#include <exec/ports.h>
#include <libraries/dos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/exec_pragmas.h>
#include <clib/dos_pragmas.h>
#include <rexx/storage.h>
#include "m2amiga.h"
#include "ed.h"

extern struct MsgPort *CreatePort(char *name, int pri);

#define ERR       0xc1
#define STR       0xc2
#define ILL       0xcf
#define ILLTAG    0xcfcf
#define ERREOF    0xff
#define LIST      3
#define MSGS      4
#define APPEND    "E"


typedef struct   ERROR {
        CARDINAL tag;
        CARDINAL ofs;
        LINE     *lp;
        CARDINAL errno;
        CHAR     err[1];
} ERROR, *ERRORP;


CHAR     ch, errNo[32], ohmProject[32];
ERRMSG   *errMsg = NULL;

struct Library *M2AmigaBase = NULL;
static int wasFromLib = 0;

void CleanupErrDat(CHAR *p); /* FORWARD */

/* onexit() procedure /bp */
static void ReleaseM2Amiga()
{
  if (M2AmigaBase != NULL) {
    if (wasFromLib) {
      FreeErrMsgs();
      wasFromLib = 0;
    }
    CloseLibrary(M2AmigaBase);
    M2AmigaBase = NULL;
  }
}

/* ok */
SetLinePos(ERROR *err)
{
  register LINE *lp;
  register long cp = 0;

#ifdef DEBUG
  printf("SetLinePos(%08lx)\n", err);
#endif
  lp = lforw(curbp->b_linep);
  while ((lp != curbp->b_linep) && ((cp+(lp->l_used)) < (int)(err->lp))) {
    cp += lp->l_used;
    cp++;
    lp = lforw(lp);
  }
#ifdef DEBUG
  printf("SetLinePos: writing into %08lx %08lx\n", &(err->ofs), &(err->lp));
#endif
  err->ofs = (long)(err->lp) - cp;
  err->lp = lp;
#ifdef DEBUG
  printf("err: %08lx %3d %08x %3d %5d\n",
         lp,lp->l_used,err,err->ofs,err->errno);
#endif
  return(TRUE);
}

/* korrigiert /bp */
ERROR *scanList(CHAR *h)
{
  if (h) {
    while (*h != ERREOF) {
      if (*h == ERR) {
#ifdef DEBUG
        printf("scanList: %08lx\n", h);
#endif
        return (ERROR *) h;
      }
      else if (*h == STR) {      /* skip string, always word aligned */
        while ( *h++ );
        if ( (long)h & 1) h++;   /* h is odd ? */
      }
      else if (*h == ILL) {      /* skip illegal/already used error */
        h += 8;
      }
      else {                     /* skip integer */
        h += 2;
      }
    }
  } /* end if(h) */
 /*
  * 19.7.92/bp
  * Wenn h == NIL wird ein Random-Value zurueckgeliefert oder was??
  * Also die schließende if (h) - Klammer hierüber gesetzt.
  */
#ifdef DEBUG
  printf("scanList: (NULL)\n");
#endif
  return((ERROR *)NULL);
}

/* ok /bp */
beforeDot(LINE *lp)
{
  LINE *l;

#ifdef DEBUG
  printf("beforeDot (%08lx)\n", lp);
#endif
  l = lforw(curbp->b_linep);
  for(;;) {
    if (l == curwp->w_dotp) {
      return (FALSE);
    }
    if (l == lp) {
      return (TRUE);
    }
    l = lforw(l);
  }
}

/* Dicken Hammer korrigiert /bp */
NextM2Error()
{
  ERROR *next;
  int i;

#ifdef DEBUG
  printf("NextM2Error()\n");
#endif
  if (curbp->b_err == NULL)
    ReadErrDat();

 /* get next error */
  next = scanList( (CHAR *)(curbp->b_nextErr) );

 /* is it below cursor or is anything else left below cursor? */
  while ( (next) && (beforeDot(next->lp)) ) {
    /* 19.7.92/bp
     * Und hier haben wir auch die Ursache, warum so lange Strings
     * in printErrMsg auftauchen können!!!!
     * next +8 ist nämlich in Assembler next+96!!!!!!
     * In C ist ++ oder +x nämlich nicht +1 oder +x, sondern
     * + x*sizeof(worauf er zeigt) !!!!
     * Also ging er u.U. ins Unterholz und fand dort sehr lange
     * Random-Strings.
     *
     * Hier wird ja nur durchgewatschelt, wenn vor der Cursorposition
     * noch Fehler liegen, deshalb habe ich den Fehler nie mit
     * Absicht reproduzieren können!
     * So war es:  next = scanList((CHAR *)(next + (8L))); +96 Bytes!!!
     */
    next = scanList((CHAR *)((CHAR *)next + (8L)) );
  }
  /* nothing found below cursor, now show first error from top */
  if (next == NULL) {
    next = scanList((CHAR *)(curbp->b_nextErr));
  }
  if (next) {
    for (i=0,curgoal=0; (i < next->ofs) && (i < llength(next->lp)); i++) {
      if (lgetc(next->lp,i) == '\t')
        curgoal |= 0x07;
      curgoal++;
    }
    curwp->w_dotp = next->lp;
    curwp->w_doto = getgoal(curwp->w_dotp);
    curwp->w_flag |= WFHARD;
    printErrMsg(next);
    next->tag = ILLTAG;
  }
  else {
#ifdef DEUTSCH
    mlwrite("Kein (weiterer) Fehler gefunden");
#else
    mlwrite("M2Error: no (more) error found");
#endif
    CleanupErrDat((CHAR *)-1);
  }
  return TRUE;
}

/* ok /bp */
CHAR *GetErrMsg(errno)
{
  ERRMSG *errmsg;

#ifdef DEBUG
  printf("GetErrMsg(%d)\n", errno);
#endif
  for (errmsg=errMsg; (errmsg)&&(errmsg->no!=errno); errmsg=errmsg->next);
  if (errmsg) {
    return(errmsg->msg);
  }
  else {
    sprintf(errNo, "%d", errno);
    return(errNo);
  }
}

/* Etliches geändert /bp */
printErrMsg(ERROR *error)
/* 19.7.92/bp
 * msg braucht nur 100 statt 256 Zeichen, dafür beim Kopieren überall
 * einen Test eingebaut. Dies ist ja nicht zeitkritisch!
 */
{
  CHAR msg[100], *err, *cp1, *cp2,
       *cend = &msg[96]; /* security!! */
  CARDINAL *errno;

#ifdef DEBUG
  printf("printErrMsg\n");
#endif
  cp1 = &msg[0]; *cp1 = 0;
  err = (CHAR *)error + 8;
  while ((*err != ERREOF) && (*err != ERR) && (*err != ILL)) {

   /* 19.7.92/bp Hier ist EIN Hammer!
    * Es wird auf Überlauf abgefragt, aber dann ein ganzer String
    * kopiert, der locker den Stack zerschießen kann!!
    * Die Frage ist aber noch, woher es kommen kann, daß eine
    * Fehlermeldung so enorm lang wird!?
    * Da nach den User-Meldungen immer ein LINE BUFFER OVERFLOW
    * vor dem Absturz erscheint, ist die ABSTURZ-Ursache hiermit
    * klar. Ufff!
    * Der ursächliche Fehler ist in NextM2Error() gewesen!
    */

    if (cp1 >= cend) {
      mlwrite("m2error BUG: LINE BUFFER OVERFLOW");
      return(FALSE); /* Und ab ins Nirwana /bp */
    }
    if (*err == STR) {
      err++; /* skip STR */
      while ((*cp1++ = *err++) && (cp1 < cend));
      cp1--;
      if (*cp1) { /* \0 ist nicht kopiert worden! */
        while (*err++); /* also err noch weiterskippen */
      }
      *cp1++ = ' ';
      *cp1 = 0;
      if ((long)err & 1) err++; /* word aligned */
    }
    else {
      errno = (CARDINAL *)err;
      cp2 = GetErrMsg((int)*errno);
      while ((*cp1++ = *cp2++) && (cp1 < cend));
      cp1--;
      *cp1++ = ' ';
      *cp1 = 0;
      err += 2; /* adjust err */
    }
  } /* while */
  msg[72]='\0'; /* Mit Nummer maximal 79 Zeichen */
  mlwrite("%d: %s",error->errno,msg);
}

/* ok /bp */
int ReadErrDat()
{
  BPTR f;
  long pos;
  int ret=TRUE;
  CHAR fname[NFILEN], *h;
  ERROR *errList;

#ifdef DEBUG
  printf("ReadErrDat\n");
#endif
  CleanupErrDat((CHAR *)-1);
  strcpy(fname,curbp->b_fname); strcat(fname,APPEND);
  if (f = Open(fname, MODE_OLDFILE)) {
    Seek(f, 0, OFFSET_END);
    pos = Seek(f, 0, OFFSET_BEGINNING);
    if (pos <= 0) {
#ifdef DEUTSCH
      mlwrite("Ungültiges Dateiformat der Fehlerliste");
#else
      mlwrite("illegal error file format");
#endif
      ret = FALSE;
    }
    else if (errList = (ERROR *)(malloc(pos))) {
      Read(f, (CHAR *)errList, pos);
#ifdef DEBUG
      printf("ReadErrDat: reading %d bytes into %08lx\n", pos, errList);
#endif
      if (*(long *)errList == LIST) {
        curbp->b_err = (CHAR *)errList;
        curbp->b_nextErr = (ERROR *)((CHAR *)errList + 4L);
        h = (CHAR *)scanList((CHAR *)(curbp->b_nextErr));
        while (h) {
          SetLinePos((ERROR *)h);
          h = (CHAR *)scanList(h+8);
        }
        ReadErrMsg();
      }
      else {
#ifdef DEUTSCH
        mlwrite("Ungültiges Dateiformat der Fehlerliste");
#else
        mlwrite("illegal error file format");
#endif
        free((CHAR *)errList);
      }
    }
    else {
#ifdef DEUTSCH
      mlwrite("Speichermangel");
#else
      mlwrite("Cannot allocate memory");
#endif
      ret = FALSE;
    }
#ifdef DEBUG
    printf("Close File\n");
#endif
    Close(f);
  }
  else {
#ifdef DEUTSCH
    mlwrite("Keine Fehlerliste gefunden");
#else
    mlwrite("Cannot read error listing");
#endif
    ret = FALSE;
  }
#ifdef DEBUG
  printf("ReadErrDat: ret = %d\n", ret);
#endif
  return (ret);
}

/* ok /bp */
void CleanupErrDat(CHAR *p)
{
#ifdef DEBUG
  printf("CleanupErrDat()\n");
#endif
  if ((curbp->b_err != (CHAR *)-1) && (curbp->b_err != NULL))
    free(curbp->b_err);
  curbp->b_err = (CHAR *)p; curbp->b_nextErr = NULL;
}


/* m2amigalib öffnen und errMsg holen /bp */
static void GetM2Amiga()
{
  if (errMsg) return;
  wasFromLib = 0;
  if (M2AmigaBase == NULL) {
    M2AmigaBase = OpenLibrary(M2AmigaName, M2AmigaVersion);
    if (M2AmigaBase != NULL) {
      onexit(ReleaseM2Amiga);
      errMsg = GetErrMsgs();
      if (errMsg) {
        wasFromLib = 1;
      } /* got em! */
    } /* opened now */
  } /* M2AmigaBase == NULL */
}

/* Nutzt nun auch m2amiga.lib /bp */
int ReadErrMsg()
{
  BPTR f;
  int ret=TRUE;
  long pos, bytesRead;
  register struct ERRMSG *errmsg;

#ifdef DEBUG
  printf("ReadErrMsg\n");
#endif

  GetM2Amiga();
  if (errMsg) return(TRUE);

  if (f = Open(MSGNAME, 1005)) {
    Seek(f, 0, OFFSET_END);
    pos = Seek(f, 0, OFFSET_BEGINNING);
    if (errMsg = (ERRMSG *)(malloc(pos))) {
      bytesRead = Read(f, (CHAR *)errMsg, pos);
#ifdef DEBUG
      printf("ReadErrMsg: reading %ld of %ld bytes into %08lx\n",
      	bytesRead, pos, errMsg);
#endif
      for (errmsg = errMsg; (errmsg->next); errmsg = errmsg->next) {
#ifdef DEBUG
        printf("errmsg = %08lx ; next = %08lx ; base = %08lx\n",
            (ULONG)errmsg,(ULONG)errmsg->next,(ULONG)errMsg);
#endif
        errmsg->next = (ERRMSG *) ((ULONG)(errmsg->next)+(ULONG)errMsg);
      }
#ifdef DEBUG
      printf("ReadErrMsg: adjustements done\n");
#endif
    }
    else {
#ifdef DEUTSCH
      mlwrite("Speichermangel");
#else
      mlwrite("Cannot allocate memory");
#endif
      ret = FALSE;
    }
    Close(f);
  }
  else {
#ifdef DEUTSCH
    mlwrite("Keine Fehlermeldungen gefunden");
#else
    mlwrite("Cannot read error messages");
#endif
    ret = FALSE;
  }
#ifdef DEBUG
  printf("ReadErrMsg: returning %d\n", ret);
#endif
  return(ret);
}

/* ok /bp */
ERROR *findErr(ERROR *err, LINE *from)
{
#ifdef DEBUG
  printf("findErr(%08x,%08x)\n",err,from);
#endif

  err = scanList((CHAR *)err);
  while (err) {
    if (err->lp == from)
      return (err);
    else
      err = scanList((CHAR *)err + 8);
  }
  return ((ERROR *)NULL);
}

/* unklar!!! /bp */
/* Auch hier waere ein RetChk noetig!! */
int lineChanged(LINE *from, LINE *to, CARDINAL doto, CARDINAL split)
{
  ERROR *err;

#ifdef DEBUG
  printf("lineChanged(%08x %08x %d %d)\n",from,to,doto,split);
#endif

  err = (ERROR *)curbp->b_err;
  if ((err == NULL) || (err == (ERROR *)-1)) return(0);

  /* Für ersten Fehler vorbereiten /bp */
  err = (ERROR *)( (CHAR *)err - 4 );

  for (;;) {
    err = findErr((ERROR *)((CHAR *)err + 8),from);
    if (err) {
      if ((to) && (err->ofs <= llength(from))) {
        if (split) {
          if (doto<=err->ofs) {
            err->ofs -= doto;
          }
          else {
            err->lp = to;
          }
        }
        else {
          err->lp = to;
          err->ofs += doto;
        }
      }
      else {
        err->tag = ILLTAG;
        err->lp = NULL;
      }
    }
    else {
      return (0);
    }
  } /* end forever */
}


/*
 * Interface to the background compiler. This interface is used to send
 * a message containing the pointer to the first line of the buffer and
 * the filename to the compiler for compilation of the text.
 * At the end of the compilation the message is returned and the error
 * list is read and the first error will be displayed.
 */
 /* evtl. noch ändern /bp */
int export()
{
  struct MyMessage {
    struct Message msg;
    LINE *line;
    long currentDir;
    CHAR *fileName;
  } *msg;
  struct MsgPort *dp, *sp;

  if ((dp = FindPort("m2c")) || (dp = FindPort("M2C")) ) {
    if (sp = CreatePort(NULL,0)) {
      if (msg = (struct MyMessage *)malloc( sizeof(struct MyMessage) )) {
        msg->msg.mn_ReplyPort = sp;
        msg->msg.mn_Length = sizeof(*msg);
        msg->msg.mn_Node.ln_Name = "m2emacs";

        msg->line = curbp->b_linep;
        msg->currentDir = Lock("",-2);
        msg->fileName = (CHAR *)&(curbp->b_fname);

        PutMsg(dp,(struct Message *)msg);
        WaitPort(sp);
        GetMsg(sp);
        UnLock(msg->currentDir);
        free(msg);
        Delay(50);
        ReadErrDat();
        NextM2Error();
      }
      else {
#ifdef DEUTSCH
        mlwrite("EXPORT: Nicht genügend Speicher");
#else
        mlwrite("EXPORT: Not enough memory");
#endif
      }
      DeletePort(sp);
    }
    else {
#ifdef DEUTSCH
      mlwrite("EXPORT: Kann keinen Port erzeugen");
#else
      mlwrite("EXPORT: Cannot create msgport");
#endif
    }
  }
  else {
#ifdef DEUTSCH
    mlwrite("EXPORT: Kein Port für 'm2c' gefunden");
#else
    mlwrite("EXPORT: No port 'm2c' around");
#endif
  }
  return (0);
}

/* ok /bp */
int call_ohm(f,n)
{
  struct MsgPort *dp, *sp;
  struct RexxMsg *msg;
  extern char lastWord[];
  /* war screenPtr[10], das reicht nicht! 20.7.92/bp */
  char screenPtr[12];
  extern struct Window *Window;

  getword();

  /* Hole den ScreenPtr und lege eine ASCII Version ab */
  sprintf(screenPtr, "%ld", Window->WScreen);

  if ( dp = FindPort("OHM") ) {
    if (sp = CreatePort(NULL,0)) {
      if (msg = (struct RexxMsg *)malloc( sizeof(struct RexxMsg) )) {
        msg->rm_Node.mn_ReplyPort = sp;
        msg->rm_Node.mn_Length = sizeof(*msg);
        msg->rm_Node.mn_Node.ln_Name = "REXX";

        mlwrite("O_SHW %s", lastWord);
        msg->rm_Action  = 0;
        msg->rm_Args[0] = (STRPTR)"O_SHW";           /* Funktion */
        msg->rm_Args[1] = (STRPTR)"M2Amiga";         /* Identifikation  */
        msg->rm_Args[2] = (STRPTR)&lastWord[0];      /* Schlüsselwort */
        msg->rm_Args[3] = (STRPTR)&ohmProject;       /* Projekt */
        msg->rm_Args[4] = (STRPTR)&screenPtr[0];     /* ScreenPointer */

        PutMsg(dp,(struct Message *)msg);
        WaitPort(sp);
        GetMsg(sp);

        free(msg);
      }
      else {
#ifdef DEUTSCH
        mlwrite("CALL_OHM: Nicht genügend Speicher");
#else
        mlwrite("CALL_OHM: Not enough memory");
#endif
      }
      DeletePort(sp);
    }
    else {
#ifdef DEUTSCH
      mlwrite("CALL_OHM: Kann keinen Port erzeugen");
#else
      mlwrite("CALL_OHM: Cannot create msgport");
#endif
    }
  }
  else {
#ifdef DEUTSCH
    mlwrite("CALL_OHM: Kein Port für 'OHM' gefunden");
#else
    mlwrite("CALL_OHM: No port 'OHM' around");
#endif
  }
  return (0);
}
