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

	/* Includes */

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

	/* View file as ASCII or hex dump */

   VOID
action_view(VOID)
{
   struct FileRequest  *freq = &file_req[active_freq];
   struct FileNode     *fnode;

   if ((freq->fr_Mode == FREQ_MODE_NORMAL || freq->fr_Mode ==
			     FREQ_MODE_FIND) && freq->fr_CursorLine != -1) {
      fnode = get_file_node_under_cursor(freq);
      if (fnode->fn_Type == ENTRY_TYPE_FILE) {   /* file selected ? */
	 print_status(view_file(freq, fnode));
      }
   }
}
	/* View selected file */

   SHORT
view_file(struct FileRequest  *freq, struct FileNode  *fnode)
{
   struct ViewRequest  *vreq = &view_req;
   SHORT error = VSH_STATUS_NORMAL;

   print_status(VSH_STATUS_READ_FILE);
   hcomp_freq_cursor(freq);
   print_info_line(INFO_LINE_MODE_EMPTY);
   print_fkey_text(FKEY_MODE_NONE);
   if ((error = read_view_file(freq, fnode, vreq)) == VSH_STATUS_NORMAL) {
      USHORT vlines = vreq->vr_Display.d_VisibleLines;

      print_view_page(vreq);
      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_view_page(vreq);
		     break;
		  case INTUITICKS :
		     if (status != VSH_STATUS_NORMAL) {
			if (error_delay-- < 0) {   /* remove error msg in status line */
			   print_vreq_status(vreq);
			   status = VSH_STATUS_NORMAL;
			}
		     }
		     if (auto_repeat) {
			scroll_view_req();
		     }
		     break;
	       }
	    }
	 }
	 if (signals & SIGF_ACTION) {
	    if (vreq->vr_MarkedEntries) {
	       print_vreq_lines(vreq, vreq->vr_Display.d_FirstVisibleNode,
							 (USHORT)0, vlines);
	       vreq->vr_MarkedEntries = 0;
	    }
	    switch (action) {
	       case VSH_ACTION_F1 :   /* jump to line */
		  jump_to_view_line(vreq);
		  break;
	       case VSH_ACTION_F2 :   /* start searching */
		  search_view_line(vreq, VIEW_SEARCH_MODE_FIRST);
		  break;
	       case VSH_ACTION_F3 :   /* continue searching */
		  search_view_line(vreq, VIEW_SEARCH_MODE_NEXT);
		  break;
	       case VSH_ACTION_F4 :   /* change view mode */
		  error = change_view_mode(vreq);
		  break;
	       case VSH_ACTION_F5 :   /* print current page */
		  print_view_text(vreq, PRINT_MODE_PAGE);
		  break;
	       case VSH_ACTION_F6 :   /* print whole text */
		  print_view_text(vreq, PRINT_MODE_ALL);
		  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_view_req();
		  break;
	    }
	 }
      } while (error == VSH_STATUS_NORMAL && action != VSH_ACTION_QUIT &&
						  action != VSH_ACTION_ESC);
      print_status(VSH_STATUS_FREE_LINE_LIST);
      free_list(vreq->vr_Display.d_List, (LONG)sizeof(struct LineNode));
      FreeMem(vreq->vr_Buffer, vreq->vr_BufferSize);
      draw_requesters(DRAW_MODE_CLEAR);
   } else {
      hcomp_freq_cursor(freq);
      print_info_line(INFO_LINE_MODE_NORMAL);
   }
   change_fkey_text();
   return(error);
}
	/* Display view page */

   VOID
print_view_page(struct ViewRequest  *vreq)
{
   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_vreq_lines(vreq, vreq->vr_Display.d_FirstVisibleNode, (USHORT)0,
					   vreq->vr_Display.d_VisibleLines);
   draw_line(COLOR1, (USHORT)2, (USHORT)(cli_vpos - 2), (USHORT)
				   (vsh_width - 3), (USHORT)(cli_vpos - 2));
   print_fkey_text((USHORT)(FKEY_MODE_VIEW + vreq->vr_Mode));
   print_vreq_status(vreq);
}
	/* Read view file into buffer, check view mode and build line list */

   SHORT
read_view_file(struct FileRequest  *freq, struct FileNode  *fnode,
						  struct ViewRequest  *vreq)
{
   USHORT len;
   BPTR   lock;
   ULONG  size;
   BYTE   *buffer;
   BPTR   fhandle;
   SHORT  error;

   if (freq->fr_Mode == FREQ_MODE_FIND) {
      strcpy(&path1_buffer[0], fnode->fn_Path);
   } else {
      strcpy(&path1_buffer[0], &freq->fr_DirName[0]);   /* build path name */
   }
   len = strlen(&path1_buffer[0]);
   if (path1_buffer[len - 1] != ':') {
      path1_buffer[len] = '/';
      len++;
   }
   vreq->vr_FileName = &path1_buffer[len];
   strncpy(&path1_buffer[len], &fnode->fn_Text[0], (size_t)fnode->fn_NameLen);
   path1_buffer[len + fnode->fn_NameLen] = '\0';
   if (!(lock = quiet_lock(&path1_buffer[0], (LONG)SHARED_LOCK))) {
      error = VSH_ERROR_LOCK_FAILED;
   } else {
      if (Examine(lock, fib) == DOSFALSE) {
	 error = VSH_ERROR_EXAMINE_FAILED;
      } else {
	 if (!(size = fib->fib_Size)) {
	    error = VSH_ERROR_EMPTY_FILE;
	 } else {
	    if (!(buffer = AllocMem(size, MEMF_PUBLIC))) {
	       error = VSH_ERROR_OUT_OF_MEM;
	    } else {
	       if (!(fhandle = (BPTR)Open(&path1_buffer[0], (LONG)
							   MODE_OLDFILE))) {
		  error = VSH_ERROR_OPEN_FAILED;
	       } else {
		  if (Read(fhandle, buffer, size) == -1) {
		     error = VSH_ERROR_READ_FAILED;
		  } else {
		     vreq->vr_Buffer        = buffer;
		     vreq->vr_BufferSize    = size;
		     vreq->vr_MarkedEntries = 0;
		     print_status(VSH_STATUS_BUILD_LINE_LIST);
		     if (build_view_line_list(vreq, get_view_mode(vreq)) ==
								    FALSE) {
			error = VSH_ERROR_OUT_OF_MEM;
		     } else {
			vreq->vr_Display.d_FirstVisibleNode = vreq->vr_Display.d_List->mlh_Head;
			Close(fhandle);
			UnLock(lock);
			return(VSH_STATUS_NORMAL);
		     }
		     free_list(vreq->vr_Display.d_List, (LONG)
						   sizeof(struct LineNode));
		  }
		  Close(fhandle);
	       }
	       FreeMem(buffer, size);
	    }
	 }
      }
      UnLock(lock);
   }
   return(error);
}
	/* Jump to an line of viewed text or hex dump */

   VOID
jump_to_view_line(struct ViewRequest  *vreq)
{
   struct MinNode  *lnode;
   ULONG  line, num_entries = vreq->vr_Display.d_NumEntries;
   USHORT vlines = vreq->vr_Display.d_VisibleLines;

   if (num_entries > vlines) {
      if (!get_input(INPUT_MODE_VIEW_LINE, NULL)) {
	 print_vreq_status(vreq);
      } else {
	 line = gadget_info.LongInt;
	 if (! line || line > num_entries) {
	    print_status(VSH_ERROR_INVALID_LINE_NUM);
	 } else {
	    if (line > (num_entries - vlines)) {
	       line = num_entries - vlines + 1;
	    }
	    lnode = get_list_node(vreq->vr_Display.d_List,
				       vreq->vr_Display.d_NumEntries, line);
	    vreq->vr_Display.d_FirstVisibleNode = lnode;
	    print_vreq_lines(vreq, lnode, (USHORT)0, vlines);
	    print_vreq_status(vreq);
	 }
      }
      print_fkey_text((USHORT)(FKEY_MODE_VIEW + vreq->vr_Mode));
   }
}
	/* Search first line with specified text in viewed text or hex dump */

   VOID
search_view_line(struct ViewRequest  *vreq, USHORT mode)
{
   struct LineNode  *lnode = (struct LineNode *)
					vreq->vr_Display.d_FirstVisibleNode;
   BYTE   hi, lo, *ptr, *string = &vreq->vr_LastSearchString[0];
   ULONG  num_entries = vreq->vr_Display.d_NumEntries;
   USHORT i, len, vlines = vreq->vr_Display.d_VisibleLines;
   SHORT  error = VSH_STATUS_NORMAL;

   if (mode == VIEW_SEARCH_MODE_FIRST) {
      if (!(ptr = get_input(INPUT_MODE_VIEW_SEARCH, string))) {
	 len = 0;
      } else {
	 if (len = strlen(ptr)) {

	    /* Save new search text */
	    strcpy(string, ptr);
	 }
      }
   } else {
      print_fkey_text(FKEY_MODE_NONE);
      if ((lnode->ln_Pos + vlines) < num_entries) {
	 lnode = (struct LineNode *)lnode->ln_Node.mln_Succ;
      }
      len = strlen(string);
   }
   if (len) {

      /* Hex search ? */
      if (*string == '$') {
	 if (len == 1 || !(len & 1)) {
	    error = VSH_ERROR_INVALID_HEX_NUM;
	 } else {

	    /* Convert hex to binary */
	    for (i = 1; i < len; i += 2) {
	       hi = toupper(*++string);
	       if (hi >= 'A' && hi <= 'F') {
		  hi -= 'A' - 10;
	       } else {
		  if (hi >= '0' && hi <= '9') {
		     hi -= '0';
		  } else {
		     error = VSH_ERROR_INVALID_HEX_NUM;
		     break;
		  }
	       }
	       lo = toupper(*++string);
	       if (lo >= 'A' && lo <= 'F') {
		  lo -= 'A' - 10;
	       } else {
		  if (lo >= '0' && lo <= '9') {
		     lo -= '0';
		  } else {
		     error = VSH_ERROR_INVALID_HEX_NUM;
		     break;
		  }
	       }
	       *ptr++ = (hi << 4) + lo;
	    }
	    len    = (i - 1) / 2;
	    string = &gadget_buffer[0];
	 }
      }
      if (error == VSH_STATUS_NORMAL) {

	 /* Search and mark view lines */
	 if (!(lnode = search_line_node(lnode, string, len))) {
	    error = VSH_ERROR_SEARCH_FAILED;
	 } else {
	    mark_search_view_lines(vreq, lnode, string, len);
	 }
      }
   }
   print_fkey_text((USHORT)(FKEY_MODE_VIEW + vreq->vr_Mode));
   if (error == VSH_STATUS_NORMAL) {
      print_vreq_status(vreq);
   } else {
      print_status(error);
   }
}
	/* Mark search view lines */

   VOID
mark_search_view_lines(struct ViewRequest  *vreq, struct LineNode  *lnode,
						   BYTE *string, USHORT len)
{
   struct MinNode  *fv_lnode = (struct MinNode *)lnode;
   ULONG  fv_pos, num_entries = vreq->vr_Display.d_NumEntries;
   USHORT i, vlines = vreq->vr_Display.d_VisibleLines;

   if (num_entries > vlines) {
      if (lnode->ln_Pos > (num_entries - vlines)) {
	 for (i = lnode->ln_Pos - (num_entries - vlines) - 1; i; i--) {
	    fv_lnode = fv_lnode->mln_Pred;
	 }
      }
      vreq->vr_Display.d_FirstVisibleNode = fv_lnode;
      print_vreq_lines(vreq, fv_lnode, (USHORT)0, vlines);
   } else {
      fv_lnode = vreq->vr_Display.d_FirstVisibleNode;
   }
   fv_pos = ((struct LineNode *)fv_lnode)->ln_Pos;
   do {
      hcomp_vreq_line(vreq, (USHORT)(lnode->ln_Pos - fv_pos));
      vreq->vr_MarkedEntries++;
      lnode = search_line_node((struct LineNode *)lnode->ln_Node.mln_Succ,
							       string, len);
   } while (lnode && (lnode->ln_Pos - fv_pos) < vlines);
}
	/* Change view mode */

   SHORT
change_view_mode(struct ViewRequest  *vreq)
{
   struct LineNode  *fv_node;
   ULONG  num_entries, old_offset  = ((struct LineNode *)
			    vreq->vr_Display.d_FirstVisibleNode)->ln_Offset;
   USHORT i, vlines = vreq->vr_Display.d_VisibleLines;
   SHORT  error = VSH_STATUS_NORMAL;

   print_status(VSH_STATUS_CHANGE_VIEW_MODE);
   print_fkey_text(FKEY_MODE_NONE);
   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));
   vreq->vr_Mode = (vreq->vr_Mode == VIEW_MODE_ASCII ? VIEW_MODE_HEX :
							   VIEW_MODE_ASCII);
   free_list(vreq->vr_Display.d_List, (LONG)sizeof(struct LineNode));
   if (build_view_line_list(vreq, vreq->vr_Mode) == FALSE) {
      error  = VSH_ERROR_OUT_OF_MEM;
      action = VSH_ACTION_QUIT;   /* quit view */
   } else {
      num_entries = vreq->vr_Display.d_NumEntries;
      if (num_entries > vlines) {
	 fv_node = get_first_visible_line(vreq, old_offset);
	 if ((num_entries - fv_node->ln_Pos + 1) < vlines) {
	    for (i = vlines - (num_entries - fv_node->ln_Pos + 1); i; i--) {
	       fv_node = (struct LineNode *)fv_node->ln_Node.mln_Pred;
	    }
	 }
      } else {
	 fv_node = (struct LineNode *)vreq->vr_Display.d_List->mlh_Head;
      }
      vreq->vr_Display.d_FirstVisibleNode = (struct MinNode *)fv_node;
      print_vreq_lines(vreq, (struct MinNode *)fv_node, (USHORT)0,
					   vreq->vr_Display.d_VisibleLines);
      print_fkey_text((USHORT)(FKEY_MODE_VIEW + vreq->vr_Mode));
      print_vreq_status(vreq);
   }
   return(error);
}
	/* Print viewed text as ASCII or hex dump */

   VOID
print_view_text(struct ViewRequest  *vreq, USHORT mode)
{
   struct LineNode  *lnode;
   ULONG i, end;
   BPTR  fh;
   SHORT error = VSH_STATUS_NORMAL;

   print_status(VSH_STATUS_PRINT_TEXT);
   print_fkey_text(FKEY_MODE_NONE);
   if (mode == PRINT_MODE_PAGE) {
      lnode = (struct LineNode *)vreq->vr_Display.d_FirstVisibleNode;
      end   = vreq->vr_Display.d_NumEntries - lnode->ln_Pos + 1;

      /* Check if fewer lines are available than possible to view on page */
      if (end > vreq->vr_Display.d_VisibleLines) {
	 end = vreq->vr_Display.d_VisibleLines;
      }
   } else {
      lnode = (struct LineNode *)vreq->vr_Display.d_List->mlh_Head;
      end   = vreq->vr_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_NORMAL; i++) {
	 if (CheckAbort(NULL)) {
	    error = VSH_ERROR_ABORTED;
	 } else {
	    if (FPrintf(fh, "%s\n", build_vreq_line(vreq, 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((USHORT)(FKEY_MODE_VIEW + vreq->vr_Mode));
   if (error != VSH_STATUS_NORMAL) {
      print_status(error);
   } else {
      print_vreq_status(vreq);
   }
}
	/* Print quick view */

   VOID
print_quick_view(struct FileRequest  *freq1)
{
   struct FileRequest  *freq2 = (&file_req[0] == freq1 ? &file_req[1] :
							      &file_req[0]);
   struct FileNode     *fnode;
   BPTR   fhandle;
   BYTE   *buffer, *file = &path1_buffer[0];
   USHORT len = 0;
   LONG   buffer_size = freq2->fr_Display.d_VisibleLines *
						   MAX_QVIEW_BYTES_PER_LINE;
   SHORT  error = VSH_STATUS_NORMAL;

   if (!(buffer = AllocMem(buffer_size, MEMF_PUBLIC))) {
      error = VSH_ERROR_OUT_OF_MEM;
   } else {

      /* Valid file requester mode? */
      if (freq2->fr_Mode != FREQ_MODE_DEVS_ASN) {

	 /* Empty file requester? */
	 if (fnode = get_file_node_under_cursor(freq2)) {

	    /* File selected */
	    if (fnode->fn_Type == ENTRY_TYPE_FILE) {

	       /* Read start of file into allocated buffer */
	       build_path_name_from_file_node(file, freq2, fnode);
	       if (!(fhandle = (BPTR)Open(file, (LONG)MODE_OLDFILE))) {
		  error = VSH_ERROR_OPEN_FAILED;
	       } else {
		  if (Read(fhandle, buffer, buffer_size) != buffer_size) {
		     error = VSH_ERROR_READ_FAILED;
		  } else {
		     len = buffer_size;
		  }
		  Close(fhandle);
	       }
	    }
         }
      }
      print_quick_view_lines(freq1, buffer, len);
      FreeMem(buffer, buffer_size);
   }
   if (event_mode != EVENT_MODE_INPUT) {
      print_status(error);
   }
}
