/* Copyright (C) 1996,1997 Robert H”hne, see COPYING.RH for details */
/* This file is part of RHIDE. */
#include <libgdb.h>
#include <librhgdb.h>
#include <rhgdbint.h>

#include <string.h>
#include <stdlib.h>
#include <unistd.h>

/* This function is taken from symtab.c */

static struct symtab_and_line
find_function_start_sal (sym, funfirstline)
     struct symbol *sym;
     int funfirstline;
{
  CORE_ADDR pc;
  struct symtab_and_line sal;

  pc = BLOCK_START (SYMBOL_BLOCK_VALUE (sym));
  if (funfirstline)
    {
      pc += FUNCTION_START_OFFSET;
      SKIP_PROLOGUE (pc);
    }
  sal = find_pc_line (pc, 0);

#ifdef PROLOGUE_FIRSTLINE_OVERLAP
  /* Convex: no need to suppress code on first line, if any */
  sal.pc = pc;
#else
  /* Check if SKIP_PROLOGUE left us in mid-line, and the next
     line is still part of the same function.  */
  if (sal.pc != pc
      && BLOCK_START (SYMBOL_BLOCK_VALUE (sym)) <= sal.end
      && sal.end < BLOCK_END (SYMBOL_BLOCK_VALUE (sym)))
    {
      /* First pc of next line */
      pc = sal.end;
      /* Recalculate the line number (might not be N+1).  */
      sal = find_pc_line (pc, 0);
    }
  sal.pc = pc;
#endif

  return sal;
}


#define block_size 16

static char **filenames = NULL;
static int file_count = 0;
static int file_size = 0;
static struct function_entry *Functions = NULL;
static int function_count = 0;
static int function_size = 0;

static void free_functions()
{
  int i;
  for (i=0;i<function_count;i++)
  {
    free(Functions[i].demangled_name);
    free(Functions[i].linkage_name);
    free(Functions[i].return_name);
  }
  function_count = 0;
  free(Functions);
  function_size = 0;
  for (i=0;i<file_count;i++)
  {
    free(filenames[i]);
  }
  file_count = 0;
  free(filenames);
  file_size = 0;
}

static void insertFunction(char *file_name,struct symbol *sym,int bl,struct objfile *objfile)
{
  int i = file_count;
  char *fname = NULL,*tmp,*temp;
  static char dname[1024]; /* I hope this is enough */
  while (i>0)
  {
    i--;
    if (strcmp(filenames[i],file_name)) continue;
    fname = filenames[i];
    break;
  }
  if (!fname)
  {
    fname = strdup(file_name);
    if (file_count == file_size)
    {
      file_size += block_size;
      filenames = (char **)realloc(filenames,file_size*sizeof(char **));
    }
    filenames[file_count++] = fname;
  }
  if (function_count == function_size)
  {
    function_size += block_size;
    Functions = (struct function_entry *)realloc(Functions,
                          function_size*sizeof(struct function_entry));
  }
  if (bl == STATIC_BLOCK)
  {
    strcpy(dname,"static ");
  }
  else dname[0] = 0;
  /* The following is needed, until GDB will set the correct language for
     symbol, when it is read from a coff file with SDB debug info*/
  if (SYMBOL_LANGUAGE(sym) == language_unknown)
  {
      SYMBOL_INIT_LANGUAGE_SPECIFIC(sym,deduce_language_from_filename(file_name));
      SYMBOL_INIT_DEMANGLED_NAME(sym,&objfile->symbol_obstack);
  }
  reset_gdb_output();
  type_print( SYMBOL_TYPE (sym),
 	     (SYMBOL_CLASS (sym) == LOC_TYPEDEF
	      ? "" : SYMBOL_SOURCE_NAME (sym)),gdb_stdout, 0);
  strcat(dname,gdb_output_buffer);
  tmp = strchr(dname,'(');
  temp = tmp-1;
  while (*temp != ' ') temp--;
  temp++;
  while (*temp == '*' || *temp == '&') temp++;
  Functions[function_count].demangled_name = strdup(temp);
  *temp = 0; 
  Functions[function_count].return_name = strdup(dname);
  Functions[function_count].linkage_name = strdup(SYMBOL_NAME(sym));
  Functions[function_count].file_name = fname;
  Functions[function_count].line_number = find_function_start_sal(sym,1).line;
  function_count++;
}

static void
list_symbols (char *regexp, int class)
{
  register struct symtab *s;
  register struct partial_symtab *ps;
  register struct blockvector *bv;
  struct blockvector *prev_bv = 0;
  register struct block *b;
  register int i, j;
  register struct symbol *sym;
  struct partial_symbol **psym;
  struct objfile *objfile;
  struct minimal_symbol *msymbol;
  
  int found_misc = 0;
  static enum minimal_symbol_type types[]
    = {mst_data, mst_text, mst_abs, mst_unknown};
  static enum minimal_symbol_type types2[]
    = {mst_bss,  mst_file_text, mst_abs, mst_unknown};
  static enum minimal_symbol_type types3[]
    = {mst_file_data,  mst_solib_trampoline, mst_abs, mst_unknown};
  static enum minimal_symbol_type types4[]
    = {mst_file_bss,   mst_text, mst_abs, mst_unknown};
  enum minimal_symbol_type ourtype = types[class];
  enum minimal_symbol_type ourtype2 = types2[class];
  enum minimal_symbol_type ourtype3 = types3[class];
  enum minimal_symbol_type ourtype4 = types4[class];

  if (regexp != NULL) re_comp(regexp);

  /* Search through the partial symtabs *first* for all symbols
     matching the regexp.  That way we don't have to reproduce all of
     the machinery below. */

  ALL_PSYMTABS (objfile, ps)
    {
      struct partial_symbol **bound, **gbound, **sbound;
      int keep_going = 1;
      
      if (ps->readin) continue;
      
      gbound = objfile->global_psymbols.list + ps->globals_offset + ps->n_global_syms;
      sbound = objfile->static_psymbols.list + ps->statics_offset + ps->n_static_syms;
      bound = gbound;
      
      /* Go through all of the symbols stored in a partial
	 symtab in one loop. */
      psym = objfile->global_psymbols.list + ps->globals_offset;
      while (keep_going)
	{
	  if (psym >= bound)
	    {
	      if (bound == gbound && ps->n_static_syms != 0)
		{
		  psym = objfile->static_psymbols.list + ps->statics_offset;
		  bound = sbound;
		}
	      else
		keep_going = 0;
	      continue;
	    }
	  else
	    {
/*	      QUIT; */

	      /* If it would match (logic taken from loop below)
		 load the file and go on to the next one */
	      if ((regexp == NULL || SYMBOL_MATCHES_REGEXP (*psym))
		  && ((class == 0 && SYMBOL_CLASS (*psym) != LOC_TYPEDEF
		       && SYMBOL_CLASS (*psym) != LOC_BLOCK)
		      || (class == 1 && SYMBOL_CLASS (*psym) == LOC_BLOCK)
		      || (class == 2 && SYMBOL_CLASS (*psym) == LOC_TYPEDEF)
		      || (class == 3 && SYMBOL_CLASS (*psym) == LOC_BLOCK)))
		{
		  PSYMTAB_TO_SYMTAB(ps);
		  keep_going = 0;
		}
	    }
	  psym++;
	}
    }

  /* Here, we search through the minimal symbol tables for functions
     and variables that match, and force their symbols to be read.
     This is in particular necessary for demangled variable names,
     which are no longer put into the partial symbol tables.
     The symbol will then be found during the scan of symtabs below.

     For functions, find_pc_symtab should succeed if we have debug info
     for the function, for variables we have to call lookup_symbol
     to determine if the variable has debug info.
     If the lookup fails, set found_misc so that we will rescan to print
     any matching symbols without debug info.
  */

  if (class == 0 || class == 1)
    {
      ALL_MSYMBOLS (objfile, msymbol)
	{
	  if (MSYMBOL_TYPE (msymbol) == ourtype ||
	      MSYMBOL_TYPE (msymbol) == ourtype2 ||
	      MSYMBOL_TYPE (msymbol) == ourtype3 ||
	      MSYMBOL_TYPE (msymbol) == ourtype4)
	    {
	      if (regexp == NULL || SYMBOL_MATCHES_REGEXP (msymbol))
		{
		  if (0 == find_pc_symtab (SYMBOL_VALUE_ADDRESS (msymbol)))
		    {
		      if (class == 1
			  || lookup_symbol (SYMBOL_NAME (msymbol), 
					    (struct block *) NULL,
					    VAR_NAMESPACE,
					    0, (struct symtab **) NULL) == NULL)
		        found_misc = 1;
		    }
		}
	    }
	}
    }

  ALL_SYMTABS (objfile, s)
    {
      bv = BLOCKVECTOR (s);
      /* Often many files share a blockvector.
	 Scan each blockvector only once so that
	 we don't get every symbol many times.
	 It happens that the first symtab in the list
	 for any given blockvector is the main file.  */
      if (bv != prev_bv)
	for (i = GLOBAL_BLOCK; i <= STATIC_BLOCK; i++)
	  {
	    b = BLOCKVECTOR_BLOCK (bv, i);
	    /* Skip the sort if this block is always sorted.  */
	    if (!BLOCK_SHOULD_SORT (b))
	      sort_block_syms (b);
	    for (j = 0; j < BLOCK_NSYMS (b); j++)
	      {
		sym = BLOCK_SYM (b, j);
		if (class == 1 && SYMBOL_CLASS (sym) == LOC_BLOCK && 
		    (regexp == NULL || SYMBOL_MATCHES_REGEXP (sym)))
		  {
                    insertFunction(s->filename,sym,i,objfile);
		  }
	      }
	  }
      prev_bv = bv;
    }
}

static int function_compare(const void *key1,const void *key2)
{
#define F1 ((struct function_entry *)key1)
#define F2 ((struct function_entry *)key2)
  return strcmp(F1->demangled_name,F2->demangled_name);
#undef F1
#undef F2
}

static void sort_functions()
{
  qsort(Functions,function_count,sizeof(struct function_entry),function_compare);
}

void ListFunctions(const char *regex,struct function_entry **list,int *count)
{
  free_functions();
  if (!debugger_started)
  {
    if (!InitRHGDB())
    {
      *count = 0;
      *list = NULL;
      return;
    }
  }
  list_symbols((char *)regex,1);
  if (function_count > 0)
    sort_functions();
  *count = function_count;
  *list = Functions;
}

#ifdef TEST

#include <stdio.h>

static char *progname;

static char *GetProgName()
{
  return progname;
}

void main(int argc,char *argv[])
{
  int count,i;
  struct function_entry *functions;
  progname = argv[1];
  _GetProgName = GetProgName;
  InitRHGDB();
  ListFunctions(argc>2 ? argv[2] : NULL,&functions,&count); 
  for (i=0;i<count;i++)
  {
    fprintf(stderr,"demangled: %s linkage: %s file: %s\n",
                   functions[i].demangled_name,
                   functions[i].linkage_name,
                   functions[i].file_name);
  }
}

#endif
