		/*********************************
		 *                               *
		 *   Visual Shell v1.17  10/92   *
		 *                               *
		 *     by Torsten Jürgeleit      *
		 *                               *
		 *         history part          *
		 *                               *
		 *********************************/

	/* Includes */

#include "includes.h"
#include "imports.h"
#include "protos.h"

	/* View ConMan command line history */

   VOID
action_history(VOID)
{
   struct FileRequest     *freq = &file_req[active_freq];
   struct HistoryRequest  *hreq = &history_req;
   BYTE  *force_line = NULL;
   SHORT error = VSH_STATUS_NORMAL;

   print_status(VSH_STATUS_HISTORY);
   hcomp_freq_cursor(freq);
   print_info_line(INFO_LINE_MODE_EMPTY);
   print_fkey_text(FKEY_MODE_NONE);
   if ((error = read_history(hreq)) == VSH_STATUS_NORMAL) {
      print_history_page(hreq);
      do {
	 ULONG signals = Wait(SIGF_INTUITION | SIGF_ACTION);

	 if (signals & SIGF_INTUITION) {   /* timing events from Intuition */
	    struct IntuiMessage  *msg;

	    if (msg = (struct IntuiMessage *)GetMsg(con_window->UserPort)) {
	       ULONG class = msg->Class;

	       ReplyMsg((struct Message *)msg);
	       switch (class) {
		  case REFRESHWINDOW :
		     print_history_page(hreq);
		     break;
		  case INTUITICKS :
		     if (status != VSH_STATUS_HISTORY) {
			if (error_delay-- < 0) {   /* remove error msg in status line */
			   print_status(VSH_STATUS_HISTORY);
			}
		     }
		     if (auto_repeat) {
			scroll_history_req();
		     }
		     break;
	       }
	    }
	 }
	 if (signals & SIGF_ACTION) {
	    switch (action) {
	       case VSH_ACTION_F1 :   /* jump to line */
		  jump_to_history_line(hreq);
		  break;
	       case VSH_ACTION_F2 :   /* start searching */
		  search_history_line(hreq, HISTORY_SEARCH_MODE_FIRST);
		  break;
	       case VSH_ACTION_F3 :   /* continue searching */
		  search_history_line(hreq, HISTORY_SEARCH_MODE_NEXT);
		  break;
	       case VSH_ACTION_F4 :   /* print current page */
		  print_history_text(hreq, PRINT_MODE_PAGE);
		  break;
	       case VSH_ACTION_F5 :   /* print whole text */
		  print_history_text(hreq, PRINT_MODE_ALL);
		  break;
	       case VSH_ACTION_NUM_ENTER :   /* force into command line */
		  force_line = get_history_line(hreq);
		  break;
	       case VSH_ACTION_SCROLL_UP :
	       case VSH_ACTION_SCROLL_DOWN :
	       case VSH_ACTION_SCROLL_PAGE_UP :
	       case VSH_ACTION_SCROLL_PAGE_DOWN :
	       case VSH_ACTION_SCROLL_TOP :
	       case VSH_ACTION_SCROLL_BOTTOM :
		  scroll_history_req();
		  break;
	    }
	 }
      } while (action != VSH_ACTION_QUIT && action != VSH_ACTION_ESC &&
							force_line == NULL);
      print_status(VSH_STATUS_FREE_LINE_LIST);
      free_list(hreq->hr_Display.d_List, (LONG)sizeof(struct LineNode));
      DosFreeMem(hreq->hr_Buffer);
      if (!force_line) {
	 status = error;
	 draw_requesters(DRAW_MODE_CLEAR);
      } else {
	 print_status(VSH_STATUS_EXECUTE_HISTORY_LINE);   /* execute selected history line */
	 action_display_mode();
	 print_fkey_text(FKEY_MODE_NONE);
	 error = force_string(force_line, (USHORT)strlen(force_line),
						   FORCE_MODE_NEWLINE_COPY);
      }
   } else {
      hcomp_freq_cursor(freq);
      print_info_line(INFO_LINE_MODE_NORMAL);
   }
   change_fkey_text();
   print_status(error);
}
	/* Print history page */

   VOID
print_history_page(struct HistoryRequest  *hreq)
{
   print_status(VSH_STATUS_HISTORY);
   SetAPen(con_rport, (LONG)COLOR0);
   RectFill(con_rport, (LONG)(BORDER_LEFT - 2), (LONG)(BORDER_TOP_HIDDEN
	  - 1), (LONG)(vsh_width - BORDER_RIGHT	+ 1), (LONG)(cli_vpos - 3));
   draw_line(COLOR1, (USHORT)2, (USHORT)(BORDER_TOP_HIDDEN - 2), (USHORT)
			  (vsh_width - 3), (USHORT)(BORDER_TOP_HIDDEN - 2));
   print_hreq_lines(hreq,
		     (struct LineNode *)hreq->hr_Display.d_FirstVisibleNode,
				(USHORT)0, hreq->hr_Display.d_VisibleLines);
   hcomp_hreq_cursor(hreq);
   print_fkey_text(FKEY_MODE_HISTORY);
}
	/* Read command line history into buffer and build line list */

   SHORT
read_history(struct HistoryRequest  *hreq)
{
   LONG  len;
   BYTE  *buffer;
   SHORT error;

   if ((struct CommandLineInterface *)BTOC(_parent_proc->pr_CLI) ==
								 save_cli) {   /* no EndCLI ? */
      if ((len = dos_packet(con_input_fhandle->fh_Type,
		  CONMAN_ACTION_READ_HISTORY, con_input_fhandle, (LONG)NULL,
							 0L)) == DOSFALSE) {
	 error = VSH_ERROR_WRONG_CONMAN_VERSION;
      } else {
	 if (!(buffer = DosAllocMem(len))) {
	    error = VSH_ERROR_OUT_OF_MEM;
	 } else {
	    hreq->hr_Buffer = buffer;
	    if (dos_packet(con_input_fhandle->fh_Type,
		      CONMAN_ACTION_READ_HISTORY, con_input_fhandle, buffer,
							 len) == DOSFALSE) {
	       error = VSH_ERROR_WRONG_CONMAN_VERSION;
	    } else {
	       print_status(VSH_STATUS_BUILD_LINE_LIST);
               if (build_history_line_list(hreq) == FALSE) {
		  error = VSH_ERROR_OUT_OF_MEM;
	       } else {
		  hreq->hr_Display.d_FirstVisibleNode = hreq->hr_Display.d_List->mlh_Head;
		  return(VSH_STATUS_NORMAL);
	       }
	       print_status(VSH_STATUS_FREE_LINE_LIST);
	       free_list(hreq->hr_Display.d_List, (LONG)
						   sizeof(struct LineNode));
	    }
	    DosFreeMem(buffer);
	 }
      }
   }
   return(error);
}
	/* Jump to an history line */

   VOID
jump_to_history_line(struct HistoryRequest  *hreq)
{
   ULONG  line, num_entries = hreq->hr_Display.d_NumEntries;
   USHORT vlines = hreq->hr_Display.d_VisibleLines;
   SHORT  error = VSH_STATUS_HISTORY;

   if (get_input(INPUT_MODE_HISTORY_LINE, NULL)) {
      line = gadget_info.LongInt;
      if (! line || line > num_entries) {
	 error = VSH_ERROR_INVALID_LINE_NUM;
      } else {
	 move_hreq_cursor(hreq, (struct LineNode *)
				      get_list_node(hreq->hr_Display.d_List,
				      hreq->hr_Display.d_NumEntries, line));
      }
   }
   print_fkey_text(FKEY_MODE_HISTORY);
   print_status(error);
}
	/* Search line with specified text in history */

   VOID
search_history_line(struct HistoryRequest  *hreq, USHORT mode)
{
   struct LineNode  *lnode = get_history_node_under_cursor(hreq);
   BYTE   *search_text, *default_input = &hreq->hr_LastSearchString[0];
   ULONG  num_entries = hreq->hr_Display.d_NumEntries;
   USHORT len, vlines = hreq->hr_Display.d_VisibleLines;
   SHORT  error = VSH_STATUS_HISTORY;

   if (mode == HISTORY_SEARCH_MODE_FIRST) {
      if (!(search_text = get_input(INPUT_MODE_HISTORY_SEARCH,
							  default_input))) {
	 len = 0;
      } else {
	 if (len = strlen(search_text)) {

	    /* Save new search text */
	    strcpy(default_input, search_text);
	 }
      }
   } else {
      print_fkey_text(FKEY_MODE_NONE);
      if (! lnode) {
	 len = 0;
      } else {
	 lnode       = (struct LineNode *)lnode->ln_Node.mln_Succ;
	 search_text = default_input;
	 len         = strlen(search_text);
      }
   }
   if (error == VSH_STATUS_HISTORY && len) {
      if (!(lnode = search_line_node(lnode, search_text, len))) {
	 error = VSH_ERROR_SEARCH_FAILED;
      } else {
	 move_hreq_cursor(hreq, lnode);
      }
   }
   print_fkey_text(FKEY_MODE_HISTORY);
   print_status(error);
}
	/* Print history text */

   VOID
print_history_text(struct HistoryRequest  *hreq, USHORT mode)
{
   struct LineNode  *lnode;
   ULONG i, end;
   BPTR  fh;
   SHORT error = VSH_STATUS_HISTORY;

   print_status(VSH_STATUS_PRINT_TEXT);
   print_fkey_text(FKEY_MODE_NONE);
   if (mode == PRINT_MODE_PAGE) {
      lnode = (struct LineNode *)history_req.hr_Display.d_FirstVisibleNode;
      end   = history_req.hr_Display.d_VisibleLines;
   } else {
      lnode = (struct LineNode *)history_req.hr_Display.d_List->mlh_Head;
      end   = history_req.hr_Display.d_NumEntries;
   }
   if (!(fh = (BPTR)Open("PRT:", (LONG)MODE_NEWFILE))) {
      error = VSH_ERROR_NO_PRINTER;
   } else {
      enable_abort = 1;
      for (i = 0; i < end && error == VSH_STATUS_HISTORY; i++) {
	 if (CheckAbort(NULL)) {
	    error = VSH_ERROR_ABORTED;
	 } else {
	    if (FPrintf(fh, "%s\n", build_hreq_line(hreq, lnode,
						  LINE_MODE_NO_FILL)) < 0) {
	       error = VSH_ERROR_PRINTING_FAILED;
	    } else {
	       lnode = (struct LineNode *)lnode->ln_Node.mln_Succ;
	    }
	 }
      }
      enable_abort = 0;
      Close(fh);
   }
   print_fkey_text(FKEY_MODE_HISTORY);
   print_status(error);
}
	/* Get history line under cursor */

   BYTE *
get_history_line(struct HistoryRequest  *hreq)
{
   struct LineNode  *lnode = get_history_node_under_cursor(hreq);
   BYTE   *buffer = &path1_buffer[0];
   USHORT len;

   if (!lnode) {
      buffer = NULL;
   } else {
      len = *(lnode->ln_Line - 1);
      strncpy(buffer, lnode->ln_Line, (size_t)len);
      *(buffer + len++) = '\n';   /* append new line char */
      *(buffer + len)   = '\0';
   }
   return(buffer);
}
	/* Get history node under cursor of specified history requester */

   struct LineNode *
get_history_node_under_cursor(struct HistoryRequest  *hreq)
{
   struct LineNode  *lnode = NULL;
   SHORT i, cursor_line = hreq->hr_CursorLine;

   if (cursor_line != -1) {
      lnode = (struct LineNode *)hreq->hr_Display.d_FirstVisibleNode;
      for (i = cursor_line; i; i--) {
	 lnode = (struct LineNode *)lnode->ln_Node.mln_Succ;
      }
   }
   return(lnode);
}
	/* Move cursor to specified entry in history requester */

   VOID
move_hreq_cursor(struct HistoryRequest  *hreq, struct LineNode  *lnode)
{
   ULONG  num_entries = hreq->hr_Display.d_NumEntries, cursor_line;
   USHORT i, vlines = hreq->hr_Display.d_VisibleLines;

   if (lnode) {
      hcomp_hreq_cursor(hreq);
      cursor_line = lnode->ln_Pos - 1;
      if (num_entries > vlines) {
	 if (cursor_line < (vlines / 2)) {
	    lnode = (struct LineNode *)hreq->hr_Display.d_List->mlh_Head;
	 } else {
	    if (cursor_line > (num_entries - vlines / 2)) {
	       lnode = (struct LineNode *)
				      hreq->hr_Display.d_List->mlh_TailPred;
	       i = vlines - 1;
	    } else {
	       i = vlines / 2;
	    }
	    for ( ; i; i--) {
	       lnode = (struct LineNode *)lnode->ln_Node.mln_Pred;
	    }
	    cursor_line -= lnode->ln_Pos - 1;
	 }
      } else {
	 lnode = (struct LineNode *)hreq->hr_Display.d_List->mlh_Head;
      }
      if (lnode != (struct LineNode *)hreq->hr_Display.d_FirstVisibleNode) {
	 hreq->hr_Display.d_FirstVisibleNode = (struct MinNode *)lnode;
	 print_hreq_lines(hreq, lnode, (USHORT)0, vlines);
      } else {
	 print_hreq_lines(hreq, (struct LineNode *)
		    hreq->hr_Display.d_FirstVisibleNode, (USHORT)0, vlines);
      }
      hreq->hr_CursorLine = cursor_line;
      hcomp_hreq_cursor(hreq);
   }
}
