/*

THIS IS "DBUGLIB.C"

Written 7/86 Darren New.
PUBLIC DOMAIN -- May be freely distributed.
See DBugLib.Doc for instructions and details.

*/

#include        <exec/types.h>
#include        <exec/memory.h>
#include        <libraries/dos.h>


void (*DEBUG_FUNC[10])();       /* List of debugging functions */

static char * default_console = "RAW:0/130/640/70/Debug Console ";
   /* You may want to change that and put the window at the top */
static long console;   /* Actually, this should be a file handle */
#define MUL 65000      /* Multiplyer for delays between flashes */

static char last_answer;           /* last char typed -- used by 'C' */
static char last_was_c;            /* true if 'C'ing, false otherwise */
static char last_was_skip;         /* true if 'S'kiping, false otherwise */
static char * nest_list;           /* by TRACEBACK */
static int  nest_level;            /* by TRACEBACK */
static int  skip_level;            /* for SKIP */
static long skip_con_hold;         /* for SKIP */
static int  max_nest_level = 9999; /* by ENTER, RETURN */
static int  max_detail = 9999;     /* by DEBUGF */
static long db_file_handle;        /* handle to debug file */
static long db_prt_handle;         /* handle to printer */
static char * laststr;             /* pointer to last string typed */
static long laststralloc;          /* space allocated for laststr */
static char nosave;                /* CONWRITE will not append to laststr */
static char cleol;                 /* just to pass stuff around */

static void addon(s)
register char * s;
{
   register char * newstr;
   /* add S to the end of laststr */
   if (nosave) return;
  again:
   if (strlen(laststr) + strlen(s) + 1 >= laststralloc) {
      newstr = (char *) AllocMem(laststralloc + strlen(s) + 25,
         MEMF_CLEAR);
      if (newstr == 0) {
         if (laststralloc == 0) {
            /* out of memory */
            return;
            }
         FreeMem(laststr, laststralloc);
         laststralloc = 0; laststr = "";
         goto again;
         }
      strcpy(newstr, laststr);
      FreeMem(laststr, laststralloc);
      laststralloc += strlen(s) + 25;
      laststr = newstr;
      }
   strcat(laststr, s);
   }


static void conwrite(s)
char * s;
{
   if (console != 0) {
      Write(console, s, strlen(s));
      addon(s);
      }
   }


static void conwriteint(i)
register int i;
{
   char s[10];
   if (i > 32760) i = 32760;
   s[0] = '0' + i / 10000     ;
   s[1] = '0' + i /  1000 % 10;
   s[2] = '0' + i /   100 % 10;
   s[3] = '0' + i /    10 % 10;
   s[4] = '0' + i         % 10;
   s[5] = '\0';
   conwrite(s);
   }


static void conwritechar(c)
char c;
{
   char buf[2];
   buf[0] = c; buf[1] = '\0';
   conwrite(buf);
   }


static void sendto(a)
long a;
{
   register int i;
   if (console == 0) return;
   if (strlen(laststr) != Write(a, laststr, strlen(laststr))) {
      nosave = 1;
      last_was_c = 0;
      i = IoErr();
      conwrite("\033[1mError during output: ");
      conwriteint(i);
      conwrite("\nTry again...\033[0m\n");
      nosave = 0;
      }
   }


static void isyt()
{
   nosave = 1;
   cleol = 1;
   conwrite("\033[1m  It's still your turn...\033[0m\r");
   nosave = 0;
   }



static void getresp()
{
   char buf;
   register int i;
   register int oncet;
   extern void DEBUG_TRACEBACK();
   if (console == 0) return;
   oncet = 0;
  again:
   /* This gives him 1/10 seconds for each debug in continuous mode */
   if (! last_was_c || WaitForChar(console, 100000)) {
      last_was_c = 0;
      Read(console, &buf, 1);
      }
   else if (oncet) {
      buf = ' ';
      }
   else {
      oncet = 1;
      buf = last_answer;
      }
   if (cleol) {
      nosave = 1; conwrite("\033[0m\033[K"); nosave = 0;
      cleol = 0;
      }
   /* Process buffer here */
   switch (buf) {
      case 'N':
      case 'n':
         nosave = 1;
         conwrite("\033[1mEnter maximum nest level or ' ' for all:\033[0m ");
         Read(console, &buf, 1);
         if (buf <= '9' && buf >= '0') {
            conwritechar(buf);
            max_nest_level = buf - '0';
            }
         else if (buf == ' ')
            max_nest_level = 99999;
         else
            conwritechar('\007', NULL);
         isyt();
         goto again;
      case 'D':
      case 'd':
         nosave = 1;
         conwrite("\033[1mEnter maximum detail level or ' ' for all:\033[0m ");
         Read(console, &buf, 1);
         if (buf <= '9' && buf >= '0') {
            conwritechar(buf, NULL);
            max_detail = buf - '0';
            }
         else if (buf == ' ')
            max_detail = 9999;
         else
            conwritechar('\007', NULL);
         isyt();
         goto again;
      case 'S':
      case 's':
         skip_con_hold = console;
         last_was_skip = 1;
         skip_level = nest_level;
         console = 0; /* temp suspend debugging till return reached */
         goto outness; /* Do NOT try to attempt any more output */
      case 'P':
      case 'p':
         last_answer = buf;
         if (db_prt_handle == 0)
            db_prt_handle = Open("PRT:", MODE_OLDFILE);
         if (db_prt_handle == 0) {
            i = IoErr();
            nosave = 1;
            last_was_c = 0;
            conwrite("\033[1mError opening printer: ");
            conwriteint(i);
            conwrite("\n\033[0m\007");
            nosave = 0;
            isyt();
            goto again;
            }
         sendto(db_prt_handle);
         isyt();
         goto again;
      case 'F':
      case 'f':
         last_answer = buf;
         if (db_file_handle == 0)
            db_file_handle = Open(":t/DebugOut", MODE_OLDFILE);
         if (db_file_handle != 0)
            Seek(db_file_handle, 0, OFFSET_END);
         if (db_file_handle == 0) {
            i = IoErr();
            if (i == ERROR_OBJECT_NOT_FOUND)
               db_file_handle = Open(":t/DebugOut", MODE_NEWFILE);
            }
         if (db_file_handle == 0) {
            last_was_c = 0;
            i = IoErr();
            nosave = 1;
            conwrite("\033[1mError opening file: ");
            conwriteint(i);
            conwrite("\n\033[0m\007");
            nosave = 0;
            isyt();
            goto again;
            }
         sendto(db_file_handle);
         isyt();
         goto again;
      case 'C':
      case 'c':
         last_was_c = 1;
         break;
      case 'Q':
      case 'q':
         Close(console);
         console = 0; last_answer = 0; last_was_c = 0;
         break;
      case 'T':
      case 't':
         DEBUG_TRACEBACK((long) 0xBADD1E);
         isyt();
         goto again;
      case '0':  case '5':
      case '1':  case '6':
      case '2':  case '7':
      case '3':  case '8':
      case '4':  case '9':
         (*DEBUG_FUNC[(int) buf - '0'])();
         isyt();
         break;
      case '\0':  /* continuous */
      case '\r':
      case '\n':
      case ' ':
         break;
      default:
         nosave = 1;
         conwrite("\033[1mI don't understand ");
         conwriteint(buf);
         conwrite("\033[0m\n\007");
         nosave = 0;
         isyt();
         goto again;
      }
  outness:
   if (laststralloc > 0) {
      FreeMem(laststr, laststralloc);
      laststr = "";
      laststralloc = 0;
      }
   }


/******* CODE FROM AUSTIN CODE WORKS, MODIFIED BY DHN *******/

#define EOS             ('\0')
#define BITS_PER_CHAR   8

/*
 * WORKSIZE is the dimension of work[] which must hold enough characters
 * to store a long integer (expanded using the %b format) + '-' and EOS
 */
#define WORKSIZE        (((sizeof (long)) * BITS_PER_CHAR) + 2)

struct  fmtbuf {
        char    f_type;         /* Format type ('d', etc.)              */
        char    f_width;        /* Argument width (INT or LONG)         */
        char    f_radix;        /* Argument radix (8, 10, etc.)         */
        };

#define INT     0
#define LONG    1
#define NEG     2               /* Signal for output of '-'             */

/*
 * formatinfo[] stores information about the "value" formats
 */
static struct fmtbuf formatinfo[] = {
        { 'b',  INT,     2 },
        { 'd',  INT,    10 },
        { 'o',  INT,     8 },
        { 'u',  INT,    10 },
        { 'x',  INT,    16 },
        { 'B',  LONG,    2 },
        { 'D',  LONG,   10 },
        { 'O',  LONG,    8 },
        { 'U',  LONG,   10 },
        { 'X',  LONG,   16 },
        { EOS,  0,       0 },
        };


void c_doprnt(format, argp, func)
char            *format;                /* Format string                */
register int    *argp;                  /* Argument vector pointer      */
int             (*func)();              /* Output Function              */
{
        register int             c;
        register struct fmtbuf * pfmt;
        register char *          presult;
        char            ljust;          /* TRUE for left-justification  */
        char            work[WORKSIZE]; /* Number buffer                */
        char            fill;           /* '0' or ' ' for fill          */
        int             prec;           /* Precision                    */
        int             slen;           /* Field length                 */
        int             width;          /* Argument width               */
        unsigned long   value;
        int             temp;           /* Set if '-' needed.           */
        short           radix;          /* Conversion radix             */

        while ((c = *format++) != EOS) {
            /*
             * Check for conversion specifications.
             */
            if (c == '%') {
                /*
                 * Check for various options.
                 */
                c = *format++;
                ljust = 0;                      /* No left adjustment   */
                fill = ' ';                     /* Fill with spaces     */
                if (c == '-') {                 /* %-   left justify    */
                    ljust++;
                    c = *format++;
                    }
                if (c == '0') {                 /* %0d  zero fill arg.  */
                    fill = c;
                    c = *format++;
                    }
                if (c == '?' || c == '*') {     /* %*   width from arg  */
                    width = *argp++;
                    c = *format++;
                    }
                else {                          /* %n   field width     */
                    width = 0;
                    while (isdigit(c)) {
                        width *= 10;
                        width += (c - '0');
                        c = *format++;
                        }
                    }
                if (c == '.') {                 /* %n.n precision       */
                    c = *format++;              /* %n.* (from arg)      */
                    if (c == '?' || c == '*') {
                        prec = *argp++;
                        c = *format++;
                        }
                    else {
                        prec = 0;
                        while (isdigit(c)) {
                            prec *= 10;
                            prec += (c - '0');
                            c = *format++;
                            }
                        }
                    }
                else {
                    prec = 32767;               /* No prec. specified   */
                    }
                /*
                 * Process conversion chars, understanding longs.
                 */
                if (c == 'l' || c == 'L') {
                    switch (c = *format++) {
                        case 'b':
                        case 'd':
                        case 'o':
                        case 'u':
                        case 'x':
                            c = toupper(c); /* Make %lb %B          */
                        case 'B':
                        case 'D':
                        case 'O':
                        case 'U':
                        case 'X':
                            break;
                        default:            /* Here on %Lfoo ==> %Ufoo      */
                            c = 'U';
                            format--;
                            break;
                        }
                    }
                /*
                 * Search the numeric format structure for this conversion.
                 */
                for (pfmt = formatinfo;
                        (pfmt->f_type != c && pfmt->f_type != EOS);
                        pfmt++)
                    ;
                if (pfmt->f_type != EOS) {
                    /*
                     * A numeric conversion was found.
                     * Get the value and expand it into the work area.
                     */
                    radix = pfmt->f_radix;
                    temp = pfmt->f_width;
                    presult = &work[WORKSIZE];
                    *--presult = EOS;          /* Terminate result     */
                    if (temp == INT) {
                        if (c == 'd' && *argp < 0) {
                            value = -*argp++;
                            temp = NEG;         /* Remember signal      */
                            }
                        else
                            value = (unsigned) *argp++;
                        }
                    else {
                        value = * (long *) argp;
                        argp += (sizeof(long) / sizeof(*argp));
                        }
                    if (value == 0)
                        *--presult = '0';
                    else {
                        /*
                         * Convert an unsigned non-zero number.
                         */
                        do {
                            c = value % radix;
                            *--presult = c + ((c < 10) ? '0' : ('A' - 10));
                            } while ((value /= radix) != 0);
                        if (temp == NEG)
                            *--presult = '-';
                        }
                    }
                else {
                    /*
                     * String or something
                     */
                    switch (c) {
                        case 'q':                   /* Funny int value      */
                            /*
                             * Convert a word as a pair of octal bytes.
                             */
                            c = 16384;
                            presult = work;
                            temp = *argp++;
                            while (c > 0) {
                                *presult++ = (temp / c) + '0';
                                temp %= c;
                                if (c == 256) {
                                    c = 64;
                                    *presult++ = '.';
                                    }
                                else
                                    c >>= 3;
                                }
                            *presult = EOS;
                            presult = work;
                            break;

                        case 'r':                   /* Remote format        */
                            argp = (int *) *argp;
                            format = (char *) *argp++;
                            continue;               /* No print here        */

                        case 's':                   /* String               */
                            if ((presult = (char *) *argp++) == NULL)
                                presult = "{NULL}";        /* Bug hack     */
                            break;

                        case 'c':                   /* Character            */
                            c = *argp++;
                            /*
                             * Fall through
                             */
                        default:                    /* %% or whatever       */
                            presult = work;
                            *presult = c;
                            work[1] = EOS;
                            break;
                        }
                    }
                /*
                 * presult -> first byte of string to output.
                 * Reuse c as a register temp.
                 */
                c = strlen(presult);           /* True result length   */
                slen = (c > prec) ? prec : c;   /* Field length         */
                if (!ljust) {                   /* Right justify?       */
                    while (--width >= slen) {
                        (*func)(fill);
                        }
                    }
                /*
                 * Output the string (up to "prec" bytes)
                 */
                for (c = prec; *presult != EOS && --c >= 0;) {
                    (*func)(*presult++);
                    }
                if (ljust) {                    /* Left justify?        */
                    while (--width >= slen) {
                        (*func)(' ');
                        }
                    }
                }
            else {
                /*
                 * Not in a % thing, just output the byte.
                 */
                (*func)(c);
                }
            }
        return;
        }

/******** END CODE FROM AUSTIN CODE WORKS *********/


void DEBUG_OPEN(s)
register char * s;
{
   register long tl1;       /* temp loop var 1 */
   register long tl2;       /* temp loop var 2 */
   register int ioerr;

   /* Open debugging console, if not already open.  Init stuff */
   if (skip_con_hold) {
      /* open stops skipping */
      console = skip_con_hold;
      skip_con_hold = 0; last_was_skip = 0;
      skip_level = 0;
      }
   if (s == NULL) {
      s = default_console;
      }
   last_answer = '\0'; last_was_c = 0;
   if (laststralloc > 0) {
      FreeMem(laststr, laststralloc);
      laststr = "";
      laststralloc = 0;
      }
   laststr = "";
   nosave = 0;
   if (console != 0) return;

   console = Open(s, MODE_NEWFILE);
   if (console == 0) {     /* could not open */
      ioerr = IoErr();
      /* Flash hundreds */
      for (tl1 = 0; tl1 < ioerr / 100; tl1++) {
         Write(Output(), "\007", 1);
         for (tl2 = 0; tl2 < MUL; tl2++) ;
         }
      for (tl2 = 0; tl2 < 2 * MUL; tl2++) ;
      /* Flash tens */
      for (tl1 = 0; tl1 < ioerr / 10 % 10; tl1++) {
         Write(Output(), "\007", 1);
         for (tl2 = 0; tl2 < MUL; tl2++) ;
         }
      for (tl2 = 0; tl2 < 2 * MUL; tl2++) ;
      /* Flash ones */
      for (tl1 = 0; tl1 < ioerr % 10; tl1++) {
         Write(Output(), "\007", 1);
         for (tl2 = 0; tl2 < MUL; tl2++) ;
         }
      }
   else {
      conwrite("\033[1mConsole is open.\033[0m\n");
      }
   }


void DEBUG_TRACEBACK(i)
long i;
{
   char * holdit;
   if (nest_level < 1) {
      conwrite("DEBUG_TRACEBACK(): Too many returns!\n");
      if (i != 0xBADD1E) getresp();
      return;
      }
   if (nest_level <= max_nest_level || i == 0xBADD1E) {
      /* Display nesting only if enters and returns showing, or if
         called directly from keyboard */
      conwrite("Stack traceback: current nest level is ");
      conwriteint(nest_level);
      holdit = nest_list;
      while(holdit != NULL) {
         conwrite("\n    ");
         conwrite(holdit + sizeof(char *));
         holdit = * (char * *) holdit;
         }
      conwrite("\nEnd of stack traceback.\n");
      if (i != 0xBADD1E) getresp();
      }
   }


void DEBUGF(sevlev, fmtstr, args)
int sevlev;
char * fmtstr;
int args;
{
   if (console == 0 || sevlev > max_detail) return;
   if (fmtstr == NULL) return;
   c_doprnt(fmtstr, &args, conwritechar);
   if (fmtstr[strlen(fmtstr)-1] != '\n')
      conwrite("\n");
   getresp();
   }



void DEBUG_ENTER(s, sf, args)
register char * s;
char * sf;
int args;
{
   register char * newmem;
   register int i;
   if (s == NULL) return;
   if (nest_level == 0) DEBUG_OPEN(NULL);
   if (nest_level <= max_nest_level) {
      conwriteint(nest_level);
      for (i = 0; i < nest_level; i++)
         conwrite("=");
      conwrite(">");
      conwrite(s);
      conwrite("\n");
      if (sf != NULL) {
         conwrite("     |--> ");
         c_doprnt(sf, &args, conwritechar);
         conwrite("\n");
         }
      }
   newmem = (char *) AllocMem(sizeof(char *) + strlen(s) + 2, 0);
   if (newmem == NULL) return;
   (* (char * *) newmem) = nest_list;
   strcpy(newmem + sizeof(char *), s);
   nest_list = newmem;
   nest_level += 1;
   if (nest_level - 1 <= max_nest_level) getresp();
   }


void DEBUG_CLOSE()
{
   last_answer = '\0'; last_was_c = 0;
   if (db_file_handle != 0) {
      Close(db_file_handle);
      conwrite("DB File is closed.\n");
      db_file_handle = 0;
      }
   if (db_prt_handle != 0) {
      Close(db_prt_handle);
      conwrite("Printer is closed.\n");
      db_prt_handle = 0;
      }
   if (skip_con_hold) {
      console = skip_con_hold;
      skip_con_hold = last_was_skip = skip_level = 0;
      }
   if (console != 0) {
      conwrite("Console is closed.\n");
      Close(console);
      }
   if (laststralloc > 0) {
      FreeMem(laststr, laststralloc);
      laststr = "";
      laststralloc = 0;
      }
   }


void DEBUG_RETURN(sf, args)
char * sf;
int args;
{
   int i;
   char * holdit;
   if (nest_level < 1) {
      conwrite("DEBUG_RETURN(): Too many returns!\n");
      getresp();
      return;
      }
   nest_level -= 1;
   if (last_was_skip && nest_level < skip_level) {
      console = skip_con_hold;
      skip_con_hold = 0; last_was_skip = 0; skip_level = 0;
      }
   if (nest_level <= max_nest_level) {
      conwriteint(nest_level);
      for (i = 0; i < nest_level; i++)
         conwrite("=");
      conwrite("<");
      conwrite(nest_list + sizeof(char *));
      conwrite("\n");
      }
   if (sf != NULL && nest_level <= max_nest_level) {
      conwrite("     <---| ");
      c_doprnt(sf, &args, conwritechar);
      conwrite("\n");
      }
   if (nest_list != NULL) {
      holdit = nest_list;
      nest_list = * (char * *) nest_list;
      FreeMem(holdit, sizeof(char *) + strlen(holdit +
         sizeof(char *)) + 2);
      }
   if (nest_level <= max_nest_level) getresp();
   if (nest_level == 0) DEBUG_CLOSE();
   }


void DEBUG_EXIT()
{
   if (skip_con_hold) console = skip_con_hold;
   DEBUG_CLOSE();
   while (nest_level > 0) DEBUG_RETURN();
   }



