/*
    Clif - A C-like Interpreter Framework
    Copyright (C) 1998 L. Koren

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/
#include <stdio.h>
#include "global.h"
#include "allocx.h"
#include "printfx.h"
#include "dbg-out.h"

extern FILEATTR spf[];
extern int getcx PROTO((FILE *));

typedef struct dbg_list DBGLIST;

struct dbg_list
{
  int line_counter;
  char *codp;
  DBGLIST *next;
};

typedef struct dbg_files DBGFILES;

struct dbg_files
{
  char *filename;
  DBGLIST *info;
  DBGFILES *next;
} *dbg_info;

static void store_pos PROTO((DBGLIST **));

static void
store_pos (pos)
  DBGLIST **pos;
{
  register DBGLIST *walk;
  
  if (*pos == NULL)
    {
      /* Beginning of the list. It is a pointer in DBGFILES structure
	 to the info field. */
      (*pos) = mallocx (sizeof (DBGLIST));
      (*pos)->line_counter = spf[s].line_counter;
      (*pos)->codp = kodp;
      (*pos)->next = NULL;
      return;
    }
  /* Going through the list of DBGLIST structures. Adding new
     information about the line number of the current line and code
     pointer where the virtual machine code is generated. */
  walk = *pos;
  while (walk->next != NULL)
    walk = walk->next;
  walk->next = mallocx (sizeof (DBGLIST));
  walk = walk->next;
  walk->line_counter = spf[s].line_counter;
  walk->codp = kodp;
  walk->next = NULL;
}


void
dbg_create ()
{
  DBGFILES *walk;

  if (dbg_info == NULL)
    {
      /* We are creating a list of files. In the list, there is
	 another list with line number and code pointer information. */
      dbg_info = mallocx (sizeof (DBGFILES));
      dbg_info->filename = spf[s].name;
      dbg_info->info = NULL;
      dbg_info->next = NULL;
      store_pos (&dbg_info->info);
      return;
    }
  /* If there is a list just walk through until we find the one with
     current file name. Then put new information in it. */
  walk = dbg_info;
  while (walk->filename != spf[s].name)
    {
      if (walk->next == NULL)
	{
	  walk->next = mallocx (sizeof (DBGFILES));
	  walk = walk->next;
	  walk->filename = spf[s].name;
	  walk->info = NULL;
	  walk->next = NULL;
	}
      else
	walk = walk->next;
    }
  store_pos (&walk->info);
}


/* Trying to figure out the source line for the current instruction
   executed. It compare program counter with stored information and if
   it matches, it returns this info. */
static DBGLIST *
get_pos (info, pc)
  DBGLIST *info;
  char *pc;
{
  while (info->next != NULL)
    {
      if (info->codp <= pc && info->next->codp > pc
	  && info->codp != info->next->codp)
	return info;
      info = info->next;
    }
  return NULL;
}


void
dbg_print (pc)
  char *pc;
{
  DBGFILES *walk = dbg_info;
  DBGLIST *info = NULL;
  FILE *fp;
  char buf[1024];
  int i,
    line_counter = 1;
  static int line_printed;
  
  /* Find the source line according to the current value of the
     program counter. */
  for (; walk != NULL; walk = walk->next)
    {
      info = get_pos (walk->info, pc);
      if (info != NULL)
	break;
    }

  if (walk == NULL)
    return;
  /* If the last printed line was the same, don't do it again. */
  if (info->line_counter == line_printed)
    return;
  fp = fopen (walk->filename, "r");
  if (fp == NULL)
    return;
  /* Throw away all chars up to line which has to be printed. */
  while (line_counter < info->line_counter)
    {
      if (getcx (fp) == '\n')
	line_counter++;
    }
  /* Read the currently executed line in the buffer. */
  for (i = 0; i < 1024; i++)
    {
      buf[i] = getcx (fp);
      if (buf[i] == '\n')
	{
	  buf[i] = '\0';
	  break;
	}
    }
  /* If the buffer overflowed, just return. */
  if (i == 1024)
    return;
  
  fprintfx (stderr, "%d: %s\n", info->line_counter, buf);
  line_printed = info->line_counter;
  fclose (fp);
}
