/* TUI display source window.

   Copyright 1998, 1999, 2000, 2001, 2002 Free Software Foundation,
   Inc.

   Contributed by Hewlett-Packard Company.

   This file is part of GDB.

   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., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

/* FIXME: cagney/2002-02-28: The GDB coding standard indicates that
   "defs.h" should be included first.  Unfortunatly some systems
   (currently Debian GNU/Linux) include the <stdbool.h> via <curses.h>
   and they clash with "bfd.h"'s definiton of true/false.  The correct
   fix is to remove true/false from "bfd.h", however, until that
   happens, hack around it by including "config.h" and <curses.h>
   first.  */

#include "config.h"
#ifdef HAVE_NCURSES_H       
#include <ncurses.h>
#else
#ifdef HAVE_CURSES_H
#include <curses.h>
#endif
#endif

#include "defs.h"
#include <ctype.h>
#include "symtab.h"
#include "frame.h"
#include "breakpoint.h"
#include "source.h"
#include "symtab.h"

#include "tui.h"
#include "tuiData.h"
#include "tuiStack.h"
#include "tuiSourceWin.h"
#include "tuiSource.h"


/*****************************************
** EXTERNAL DATA DECLS                    **
******************************************/
extern int current_source_line;
extern struct symtab *current_source_symtab;


/*****************************************
** STATIC LOCAL FUNCTIONS FORWARD DECLS    **
******************************************/

static struct breakpoint *_hasBreak (char *, int);


/*****************************************
** STATIC LOCAL DATA                    **
******************************************/


/*****************************************
** PUBLIC FUNCTIONS                     **
******************************************/

/*********************************
** SOURCE/DISASSEM  FUNCTIONS    **
*********************************/

/*
   ** tuiSetSourceContent().
   **    Function to display source in the source window.
 */
TuiStatus
tuiSetSourceContent (struct symtab *s, int lineNo, int noerror)
{
  TuiStatus ret = TUI_FAILURE;

  if (s != (struct symtab *) NULL && s->filename != (char *) NULL)
    {
      register FILE *stream;
      register int i, desc, c, lineWidth, nlines;
      register char *srcLine;

      if ((ret = tuiAllocSourceBuffer (srcWin)) == TUI_SUCCESS)
	{
	  lineWidth = srcWin->generic.width - 1;
	  /*
	     ** Take hilite (window border) into account, when calculating
	     ** the number of lines
	   */
	  nlines = (lineNo + (srcWin->generic.height - 2)) - lineNo;
	  desc = open_source_file (s);
	  if (desc < 0)
	    {
	      if (!noerror)
		{
		  char *name = alloca (strlen (s->filename) + 100);
		  sprintf (name, "%s:%d", s->filename, lineNo);
		  print_sys_errmsg (name, errno);
		}
	      ret = TUI_FAILURE;
	    }
	  else
	    {
	      if (s->line_charpos == 0)
		find_source_lines (s, desc);

	      if (lineNo < 1 || lineNo > s->nlines)
		{
		  close (desc);
		  printf_unfiltered (
			  "Line number %d out of range; %s has %d lines.\n",
				      lineNo, s->filename, s->nlines);
		}
	      else if (lseek (desc, s->line_charpos[lineNo - 1], 0) < 0)
		{
		  close (desc);
		  perror_with_name (s->filename);
		}
	      else
		{
		  register int offset, curLineNo, curLine, curLen, threshold;
		  TuiGenWinInfoPtr locator = locatorWinInfoPtr ();
		  /*
		     ** Determine the threshold for the length of the line
		     ** and the offset to start the display
		   */
		  offset = srcWin->detail.sourceInfo.horizontalOffset;
		  threshold = (lineWidth - 1) + offset;
		  stream = fdopen (desc, FOPEN_RT);
		  clearerr (stream);
		  curLine = 0;
		  curLineNo =
		    srcWin->detail.sourceInfo.startLineOrAddr.lineNo = lineNo;
		  if (offset > 0)
		    srcLine = (char *) xmalloc (
					   (threshold + 1) * sizeof (char));
		  while (curLine < nlines)
		    {
		      TuiWinElementPtr element = (TuiWinElementPtr)
		      srcWin->generic.content[curLine];
		      struct breakpoint *bp;

		      /* get the first character in the line */
		      c = fgetc (stream);

		      if (offset == 0)
			srcLine = ((TuiWinElementPtr)
				   srcWin->generic.content[
					curLine])->whichElement.source.line;
		      /* Init the line with the line number */
		      sprintf (srcLine, "%-6d", curLineNo);
		      curLen = strlen (srcLine);
		      i = curLen -
			((curLen / tuiDefaultTabLen ()) * tuiDefaultTabLen ());
		      while (i < tuiDefaultTabLen ())
			{
			  srcLine[curLen] = ' ';
			  i++;
			  curLen++;
			}
		      srcLine[curLen] = (char) 0;

		      /*
		         ** Set whether element is the execution point and
		         ** whether there is a break point on it.
		       */
		      element->whichElement.source.lineOrAddr.lineNo =
			curLineNo;
		      element->whichElement.source.isExecPoint =
			(strcmp (((TuiWinElementPtr)
			locator->content[0])->whichElement.locator.fileName,
				 s->filename) == 0
			 && curLineNo == ((TuiWinElementPtr)
			 locator->content[0])->whichElement.locator.lineNo);
		      bp = _hasBreak (s->filename, curLineNo);
		      element->whichElement.source.hasBreak =
			(bp != (struct breakpoint *) NULL &&
			 (!element->whichElement.source.isExecPoint ||
			  (bp->disposition != disp_del || bp->hit_count <= 0)));
		      if (c != EOF)
			{
			  i = strlen (srcLine) - 1;
			  do
			    {
			      if ((c != '\n') &&
				  (c != '\r') && (++i < threshold))
				{
				  if (c < 040 && c != '\t')
				    {
				      srcLine[i++] = '^';
				      srcLine[i] = c + 0100;
				    }
				  else if (c == 0177)
				    {
				      srcLine[i++] = '^';
				      srcLine[i] = '?';
				    }
				  else
				    {	/*
					   ** Store the charcter in the line
					   ** buffer.  If it is a tab, then
					   ** translate to the correct number of
					   ** chars so we don't overwrite our
					   ** buffer.
					 */
				      if (c == '\t')
					{
					  int j, maxTabLen = tuiDefaultTabLen ();

					  for (j = i - (
					       (i / maxTabLen) * maxTabLen);
					       ((j < maxTabLen) &&
						i < threshold);
					       i++, j++)
					    srcLine[i] = ' ';
					  i--;
					}
				      else
					srcLine[i] = c;
				    }
				  srcLine[i + 1] = 0;
				}
			      else
				{	/*
					   ** if we have not reached EOL, then eat
					   ** chars until we do
					 */
				  while (c != EOF && c != '\n' && c != '\r')
				    c = fgetc (stream);
				}
			    }
			  while (c != EOF && c != '\n' && c != '\r' &&
				 i < threshold && (c = fgetc (stream)));
			}
		      /* Now copy the line taking the offset into account */
		      if (strlen (srcLine) > offset)
			strcpy (((TuiWinElementPtr) srcWin->generic.content[
					curLine])->whichElement.source.line,
				&srcLine[offset]);
		      else
			((TuiWinElementPtr)
			 srcWin->generic.content[
			  curLine])->whichElement.source.line[0] = (char) 0;
		      curLine++;
		      curLineNo++;
		    }
		  if (offset > 0)
		    tuiFree (srcLine);
		  fclose (stream);
		  srcWin->generic.contentSize = nlines;
		  ret = TUI_SUCCESS;
		}
	    }
	}
    }
  return ret;
}				/* tuiSetSourceContent */


/* elz: this function sets the contents of the source window to empty
   except for a line in the middle with a warning message about the
   source not being available. This function is called by
   tuiEraseSourceContents, which in turn is invoked when the source files
   cannot be accessed */

void
tuiSetSourceContentNil (TuiWinInfoPtr winInfo, char *warning_string)
{
  int lineWidth;
  int nLines;
  int curr_line = 0;

  lineWidth = winInfo->generic.width - 1;
  nLines = winInfo->generic.height - 2;

  /* set to empty each line in the window, except for the one
     which contains the message */
  while (curr_line < winInfo->generic.contentSize)
    {
      /* set the information related to each displayed line
         to null: i.e. the line number is 0, there is no bp,
         it is not where the program is stopped */

      TuiWinElementPtr element =
      (TuiWinElementPtr) winInfo->generic.content[curr_line];
      element->whichElement.source.lineOrAddr.lineNo = 0;
      element->whichElement.source.isExecPoint = FALSE;
      element->whichElement.source.hasBreak = FALSE;

      /* set the contents of the line to blank */
      element->whichElement.source.line[0] = (char) 0;

      /* if the current line is in the middle of the screen, then we want to
         display the 'no source available' message in it.
         Note: the 'weird' arithmetic with the line width and height comes from
         the function tuiEraseSourceContent. We need to keep the screen and the
         window's actual contents in synch */

      if (curr_line == (nLines / 2 + 1))
	{
	  int i;
	  int xpos;
	  int warning_length = strlen (warning_string);
	  char *srcLine;

	  srcLine = element->whichElement.source.line;

	  if (warning_length >= ((lineWidth - 1) / 2))
	    xpos = 1;
	  else
	    xpos = (lineWidth - 1) / 2 - warning_length;

	  for (i = 0; i < xpos; i++)
	    srcLine[i] = ' ';

	  sprintf (srcLine + i, "%s", warning_string);

	  for (i = xpos + warning_length; i < lineWidth; i++)
	    srcLine[i] = ' ';

	  srcLine[i] = '\n';

	}			/* end if */

      curr_line++;

    }				/* end while */

}				/*tuiSetSourceContentNil */




/*
   ** tuiShowSource().
   **        Function to display source in the source window.  This function
   **        initializes the horizontal scroll to 0.
 */
void
tuiShowSource (struct symtab *s, TuiLineOrAddress line, int noerror)
{
  srcWin->detail.sourceInfo.horizontalOffset = 0;
  tuiUpdateSourceWindowAsIs(srcWin, s, line, noerror);

  return;
}				/* tuiShowSource */


/*
   ** tuiSourceIsDisplayed().
   **        Answer whether the source is currently displayed in the source window.
 */
int
tuiSourceIsDisplayed (char *fname)
{
  return (srcWin->generic.contentInUse &&
	  (strcmp (((TuiWinElementPtr) (locatorWinInfoPtr ())->
		  content[0])->whichElement.locator.fileName, fname) == 0));
}				/* tuiSourceIsDisplayed */


/*
   ** tuiVerticalSourceScroll().
   **      Scroll the source forward or backward vertically
 */
void
tuiVerticalSourceScroll (TuiScrollDirection scrollDirection,
                         int numToScroll)
{
  if (srcWin->generic.content != (OpaquePtr) NULL)
    {
      TuiLineOrAddress l;
      struct symtab *s;
      TuiWinContent content = (TuiWinContent) srcWin->generic.content;

      if (current_source_symtab == (struct symtab *) NULL)
	s = find_pc_symtab (selected_frame->pc);
      else
	s = current_source_symtab;

      if (scrollDirection == FORWARD_SCROLL)
	{
	  l.lineNo = content[0]->whichElement.source.lineOrAddr.lineNo +
	    numToScroll;
	  if (l.lineNo > s->nlines)
	    /*line = s->nlines - winInfo->generic.contentSize + 1; */
	    /*elz: fix for dts 23398 */
	    l.lineNo = content[0]->whichElement.source.lineOrAddr.lineNo;
	}
      else
	{
	  l.lineNo = content[0]->whichElement.source.lineOrAddr.lineNo -
	    numToScroll;
	  if (l.lineNo <= 0)
	    l.lineNo = 1;
	}
      if (identify_source_line (s, l.lineNo, 0, -1) == 1)
        tuiUpdateSourceWindowAsIs (srcWin, s, l, FALSE);
    }

  return;
}				/* tuiVerticalSourceScroll */


/*****************************************
** STATIC LOCAL FUNCTIONS                 **
******************************************/

/*
   ** _hasBreak().
   **        Answer whether there is a break point at the input line in
   **        the source file indicated
 */
static struct breakpoint *
_hasBreak (char *sourceFileName, int lineNo)
{
  struct breakpoint *bpWithBreak = (struct breakpoint *) NULL;
  struct breakpoint *bp;
  extern struct breakpoint *breakpoint_chain;


  for (bp = breakpoint_chain;
       (bp != (struct breakpoint *) NULL &&
	bpWithBreak == (struct breakpoint *) NULL);
       bp = bp->next)
    if (bp->source_file
	&& (strcmp (sourceFileName, bp->source_file) == 0)
	&& (lineNo == bp->line_number))
      bpWithBreak = bp;

  return bpWithBreak;
}				/* _hasBreak */
