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

	/* Includes */

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

	/* Lock given name with disabled system requesters */

   BPTR
quiet_lock(BYTE *name, LONG mode)
{
   BPTR lock;
   APTR old_window, *ptr = &((struct Process *)main_task)->pr_WindowPtr;

   old_window = *ptr;
   *ptr       = (APTR)-1L;
   lock       = Lock(name, mode);
   *ptr       = old_window;
   return(lock);
}
	/* Examine given lock with disabled system requesters */

   SHORT
quiet_examine(BPTR lock, struct FileInfoBlock  *fib)
{
   APTR old_window, *ptr = &((struct Process *)main_task)->pr_WindowPtr;
   SHORT error;

   old_window = *ptr;
   *ptr       = (APTR)-1L;
   if (Examine(lock, fib) == DOSFALSE) {
      error = VSH_ERROR_EXAMINE_FAILED;
   } else {
      error = VSH_STATUS_NORMAL;
   }
   *ptr = old_window;
   return(error);
}
	/* Check given path name for directory */

   SHORT
check_for_dir(BYTE *dir_name)
{
   BPTR  lock;
   SHORT error;

   if (!(lock = quiet_lock(dir_name, (LONG)SHARED_LOCK))) {
      error = VSH_ERROR_DIR_NOT_FOUND;
   } else {
      if (Examine(lock, fib) == DOSFALSE) {
	 error = VSH_ERROR_EXAMINE_FAILED;
      } else {
	 if (fib->fib_DirEntryType < 0) {
	    error = VSH_ERROR_NO_DIR;
	 } else {
	    error = VSH_STATUS_NORMAL;
	 }
      }
      UnLock(lock);
   }
   return(error);
}
	/* Check a file for a load file */

   SHORT
check_for_load_file(BYTE *file_name)
{
   BPTR  lock, fh;
   ULONG data;
   SHORT error = VSH_STATUS_NORMAL;

   if (!(lock = quiet_lock(file_name, (LONG)SHARED_LOCK))) {
      error = VSH_ERROR_LOCK_FAILED;
   } else {
      if (Examine(lock, fib) == DOSFALSE) {
	 error = VSH_ERROR_EXAMINE_FAILED;
      } else {

	 /* Check execute protection */
	 if (fib->fib_Protection & FIBF_EXECUTE) {
	    error = VSH_ERROR_EXECUTE_PROTECTED;
	 } else {

	    /* Open file and read first long word */
	    if (!(fh = Open(file_name, (LONG)MODE_OLDFILE))) {
	       error = VSH_ERROR_OPEN_FAILED;
	    } else {
	       if (Read(fh, (BYTE *)&data, 4L) != 4) {
		  error = VSH_ERROR_READ_FAILED;
	       } else {

		  /* Check for file header hunk */
		  if (data != 0x03f3) {
		     error = VSH_ERROR_NO_LOAD_FILE;
		  }
	       }
	       Close(fh);
	    }
	 }
      }
      UnLock(lock);
   }
   return(get_dos_error(error));
}
	/* Read new directory for specified filerequester */

   SHORT
read_new_dir(struct FileRequest *freq, BYTE *dir_name, USHORT mode)
{
   SHORT error;

   print_status(VSH_STATUS_READ_DIR);
   if (mode != READ_DIR_MODE_NO_OUTPUT) {
      hcomp_freq_cursor(freq);
      print_info_line(INFO_LINE_MODE_EMPTY);
      print_fkey_text(FKEY_MODE_NONE);
   }
   if (! dir_name) {
      dir_name = &freq->fr_DirName[0];
   }
   if ((error = new_dir(freq, dir_name)) == VSH_STATUS_NORMAL) {
      if ((error = read_dir(freq)) == VSH_STATUS_NORMAL) {
	 if ((error = get_dir_info(freq)) == VSH_STATUS_NORMAL && freq ==
						   &file_req[active_freq]) {   /* active file req ? */
	    if (mode != READ_DIR_MODE_NO_CD) {
	       error = change_current_dir();
	    }
	 }
      }
   }
   print_dir_name(freq, NULL);
   refresh_dir_info(freq);
   if (mode == READ_DIR_MODE_NORMAL || mode == READ_DIR_MODE_NO_CD ||
	   (mode == READ_DIR_MODE_NO_PRINT && error != VSH_STATUS_NORMAL)) {
      print_freq_lines(freq, freq->fr_Display.d_FirstVisibleNode, (USHORT)0,
					   freq->fr_Display.d_VisibleLines);
      hcomp_freq_cursor(freq);
      print_info_line(INFO_LINE_MODE_NORMAL);
      change_fkey_text();
   }
   return(error);
}
	/* Read the same directory once again */

   SHORT
read_same_dir(struct FileRequest *freq, USHORT mode)
{
   struct FileNode  *fnode;
   struct MinList   *old_file_list;
   ULONG old_marked_entries, old_node_pos;
   BYTE  *old_node_name = &file1_buffer[0];
   SHORT old_cursor_line, error = VSH_STATUS_NORMAL;

   if (freq->fr_DirName[0] != '\0') {   /* dir name != "" ? */
      old_marked_entries      = freq->fr_MarkedEntries;
      old_file_list           = freq->fr_Display.d_List;
      freq->fr_Display.d_List = NULL;
      if (mode != READ_SAME_DIR_MODE_NO_CURSOR) {
	 if ((old_cursor_line = freq->fr_CursorLine) != -1) {
	    fnode        = get_file_node_under_cursor(freq);
	    old_node_pos = fnode->fn_Pos;
	    strncpy(old_node_name, &fnode->fn_Text[0], (size_t)
							 fnode->fn_NameLen);
	    *(old_node_name + fnode->fn_NameLen) = '\0';
	 }
      }
      if ((error = read_new_dir(freq, NULL, (mode ==
			READ_SAME_DIR_MODE_NORMAL ? READ_DIR_MODE_NO_PRINT :
			  READ_DIR_MODE_NO_OUTPUT))) == VSH_STATUS_NORMAL) {
	 duplicate_marks(freq, old_file_list, old_marked_entries);
	 if (mode != READ_SAME_DIR_MODE_NO_CURSOR) {
	    duplicate_cursor_line(freq, old_node_name, old_node_pos,
							   old_cursor_line);
	 }
         free_list(old_file_list, (ULONG)sizeof(struct FileNode));
	 print_freq_lines(freq, freq->fr_Display.d_FirstVisibleNode,
				(USHORT)0, freq->fr_Display.d_VisibleLines);
	 if (mode == READ_SAME_DIR_MODE_NORMAL) {
	    hcomp_freq_cursor(freq);
	    print_info_line(INFO_LINE_MODE_NORMAL);
	    change_fkey_text();
	 }
      }
   }
   return(error);
}
	/* Enter new directory */

   SHORT
new_dir(struct FileRequest *freq, BYTE *dir_name)
{
   struct MinList  *file_list;
   BPTR  dir_lock;
   SHORT error;

   if (!(dir_lock = quiet_lock(dir_name, (LONG)SHARED_LOCK))) {
      error = VSH_ERROR_DIR_NOT_FOUND;
   } else {
      dir_name = &path1_buffer[0];
      if ((error = build_path_name_from_lock(dir_name, dir_lock)) ==
							VSH_STATUS_NORMAL) {
	 if (Examine(dir_lock, fib) == DOSFALSE) {
	    error = VSH_ERROR_EXAMINE_FAILED;
	 } else {
	    if (fib->fib_DirEntryType < 0) {
	       error = VSH_ERROR_NO_DIR;
	    } else {
	       if (!(file_list = (struct MinList *)AllocMem((LONG)
		       sizeof(struct MinList), MEMF_PUBLIC | MEMF_CLEAR))) {
		  error = VSH_ERROR_OUT_OF_MEM;
	       } else {
		  NewList((struct List *)file_list);
		  free_dir(freq);
		  strcpy(&freq->fr_DirName[0], dir_name);
		  freq->fr_Display.d_List = file_list;
		  freq->fr_DirLock        = dir_lock;
		  CopyMem((BYTE *)&fib->fib_Date, (BYTE *)
					&freq->fr_Info.i_LastChanged, (LONG)
						  sizeof(struct DateStamp));
		  return(VSH_STATUS_NORMAL);
	       }
	    }
	 }
      }
      UnLock(dir_lock);
   }
   return(error);
}
	/* Build file list for specified filerequester */

   SHORT
read_dir(struct FileRequest *freq)
{
   struct AnchorPath     *ap   = &freq->fr_AnchorPath;
   struct FileInfoBlock  *info = &ap->ap_Info;
   struct MinList        *list = freq->fr_Display.d_List;
   struct FileNode       *fnode;
   BPTR  cd_lock;
   LONG  rc;
   SHORT error = VSH_STATUS_NORMAL;

   cd_lock = CurrentDir(freq->fr_DirLock);
   if (!(rc = FindFirst(&freq->fr_FilePattern[0], ap))) {
      do {
	 if (!(fnode = build_file_node(info))) {
	    error = VSH_ERROR_OUT_OF_MEM;
	 } else {
	    insert_file_node(fnode, list);
	 }
      } while (error == VSH_STATUS_NORMAL && !(rc = FindNext(ap)));
   }
   FreeAnchorChain(ap);
   CurrentDir(cd_lock);
   if (error == VSH_STATUS_NORMAL) {
      if (rc != ERROR_NO_MORE_ENTRIES) {
	 error = VSH_ERROR_FINDFIRST_OR_FINDNEXT_FAILED;
      } else {
	 if (freq->fr_Display.d_NumEntries = count_file_nodes(list)) {
	    freq->fr_Display.d_FirstVisibleNode = list->mlh_Head;
	    freq->fr_CursorLine                 = 0;
	 }
      }
   }
   return(error);
}
	/* Get infos about directory */

   SHORT
get_dir_info(struct FileRequest *freq)
{
   struct Info  *info = &freq->fr_Info;
   SHORT error = VSH_STATUS_NORMAL;

   if (Info(freq->fr_DirLock, idata) == DOSFALSE) {
      error = VSH_ERROR_INFO_FAILED;
   } else {
      info->i_Valid         = 1;   /* mark info data as valid */
      info->i_NumSoftErrors = idata->id_NumSoftErrors;
      info->i_DiskState     = idata->id_DiskState;
      info->i_BytesPerBlock = idata->id_BytesPerBlock;
      info->i_TotalSize     = idata->id_NumBlocks * idata->id_BytesPerBlock;
      info->i_FreeSize      = info->i_TotalSize - idata->id_NumBlocksUsed
						  * idata->id_BytesPerBlock;
      BtoCStr(&info->i_VolumeName[0], ((struct DosList *)
	    BTOC(idata->id_VolumeNode))->dol_Name, (LONG)MAX_FILE_NAME_LEN);
      sum_file_sizes(freq);
   }
   return(error);
}
	/* Free file list for specified filerequester */

   VOID
free_dir(struct FileRequest *freq)
{
   if (freq->fr_DirLock) {
      UnLock(freq->fr_DirLock);
      freq->fr_DirLock = NULL;
   }
   free_list(freq->fr_Display.d_List, (LONG)sizeof(struct FileNode));
   freq->fr_Display.d_List = NULL;
   free_list(freq->fr_SaveFileList, (LONG)sizeof(struct FileNode));
   freq->fr_SaveFileList               = NULL;
   freq->fr_DirName[0]                 = '\0';
   freq->fr_Info.i_Valid               = 0;   /* mark info data as invalid */
   freq->fr_Display.d_NumEntries       = 0;
   freq->fr_SaveNumEntries             = 0;
   freq->fr_Display.d_FirstVisibleNode = NULL;
   freq->fr_SaveFirstVisibleNode       = NULL;
   freq->fr_CursorLine                 = -1;
   freq->fr_SaveCursorLine             = -1;
   freq->fr_MarkedEntries              = 0;
   freq->fr_SaveMarkedEntries          = 0;
   freq->fr_MarkedSize                 = 0;
   freq->fr_SaveMarkedSize             = 0;
   freq->fr_Mode                       = FREQ_MODE_NORMAL;
}
	/* Free the complete list and the list header */

   VOID
free_list(struct MinList *list, ULONG size)
{
   struct Node  *node;
   BOOL path_flag = FALSE;

   if (list && size) {
      if (size == sizeof(struct FileNode)) {
	 path_flag = TRUE;   /* free path buffer - allocated by Find */
      }
      while (node = RemHead((struct List *)list)) {
	 if (path_flag == TRUE) {
	    DosFreeMem(((struct FileNode *)node)->fn_Path);
	 }
	 FreeMem(node, size);
      }
      FreeMem(list, (LONG)sizeof(struct MinList));
   }
}
	/* Build full path name from given lock */

   SHORT
build_path_name_from_lock(BYTE *buffer, BPTR lock)
{
   BYTE  temp[MAX_PATH_NAME_LEN + 1];
   SHORT error;

   if (!PathName(lock, &temp[0], (LONG)MAX_PATH_NAME_LEN / FSIZE)) {
      error = VSH_ERROR_NO_PATH_NAME;
   } else {
      if ((error = build_device_name(buffer, lock)) == VSH_STATUS_NORMAL) {
	 strcat(buffer, strchr(&temp[0], (USHORT)':') + 1);
      }
   }
   return(error);
}
	/* Build full path name from given file node */

   VOID
build_path_name_from_file_node(BYTE *buffer, struct FileRequest  *freq,
						    struct FileNode  *fnode)
{
   if (freq->fr_Mode == FREQ_MODE_FIND) {
      strcpy(buffer, fnode->fn_Path);
   } else {
      strcpy(buffer, &freq->fr_DirName[0]);
   }
   append_base_name(buffer, fnode);
}
	/* Build full path name from path relative to current directory */

   SHORT
build_path_name_from_relative_path(BYTE *buffer, BYTE *path)
{
   BPTR  lock;
   SHORT error;

   if (!(lock = quiet_lock(path, (LONG)SHARED_LOCK))) {
      error = VSH_ERROR_LOCK_FAILED;
   } else {
      error = build_path_name_from_lock(buffer, lock);
      UnLock(lock);
   }
   return(error);
}
	/* Append full path of specified entry at given pointer */

   BYTE *
append_path_name(BYTE *buffer, struct FileRequest *freq,
					struct FileNode *fnode, USHORT mode)
{
   BPTR lock;
   BYTE *ptr;

   if (mode == APPEND_MODE_QUOTED) {
      *buffer++ = '"';
   }
   if (freq->fr_Mode == FREQ_MODE_FIND) {
      strcpy(buffer, fnode->fn_Path);
   } else {
      strcpy(buffer, &freq->fr_DirName[0]);
   }
   ptr = append_base_name(buffer, fnode);
   if (!(lock = quiet_lock(buffer, (LONG)SHARED_LOCK))) {
      ptr = NULL;
   } else {
      if (mode == APPEND_MODE_QUOTED) {
	 *ptr++ = '"';
	 *ptr   = '\0';
      }
      UnLock(lock);
   }
   return(ptr);
}
	/* Append base name of specified entry at given pointer */

   BYTE *
append_base_name(BYTE *buffer, struct FileNode *fnode)
{
   BYTE c;

   buffer += strlen(buffer);
   c    = *(buffer - 1);
   if (c != ':' && c != '/') {
      *buffer++ = '/';
   }
   strncpy(buffer, &fnode->fn_Text[0], (size_t)fnode->fn_NameLen);
   *(buffer += fnode->fn_NameLen) = '\0';
   return(buffer);
}
	/* Build device name for given lock */

   SHORT
build_device_name(BYTE *buffer, BPTR lock)
{
   struct DevInfo  *dvi;
   struct Task     *task;
   SHORT error = VSH_ERROR_NO_PATH_NAME;

   /* First get ptr to file system task the lock belongs to */
   if (!lock) {   /* ZERO lock -> boot device */
      task = (struct Task *)_parent_proc->pr_FileSystemTask;
   } else {
      task = (struct Task *)((struct FileLock *)BTOC(lock))->fl_Task;
   }

   /* Search device with this file system task in AmigaDOS device list */
   Forbid();
   for (dvi = (struct DevInfo *)GetDevInfo(NULL); dvi;
						dvi = BTOC(dvi->dvi_Next)) {
      if (dvi->dvi_Type == DLT_DEVICE && (struct Task *)dvi->dvi_Task ==
						    task && dvi->dvi_Name) {
	 /* Convert device name to C string and append ':' */
	 BtoCStr(buffer, dvi->dvi_Name, (LONG)MAX_FILE_NAME_LEN);
	 buffer   += strlen(buffer);
	 *buffer++ = ':';
	 *buffer   = '\0';
	 error     = VSH_STATUS_NORMAL;
	 break;
      }
   }
   Permit();
   return(error);
}
	/* Build device list for specified filerequester */

   SHORT
read_devs(struct FileRequest *freq)
{
   struct DevInfo   *dvi = NULL;
   struct MinList   *list;
   struct FileNode  *fnode;
   BPTR  lock;
   BYTE  *path = &path2_buffer[0];
   BOOL  ram_flag = FALSE;
   SHORT error = VSH_STATUS_NORMAL;

   print_status(VSH_STATUS_READ_DEVS);
   print_fkey_text(FKEY_MODE_NONE);
   freq->fr_Display.d_List             = NULL;
   freq->fr_Display.d_NumEntries       = 0;
   freq->fr_Display.d_FirstVisibleNode = NULL;
   freq->fr_CursorLine                 = -1;
   freq->fr_Mode                       = FREQ_MODE_DEVS_ASN;
   if (!(list = (struct MinList *)AllocMem((LONG)
		       sizeof(struct MinList), MEMF_PUBLIC | MEMF_CLEAR))) {
      error = VSH_ERROR_OUT_OF_MEM;
   } else {
      NewList((struct List *)list);
      freq->fr_Display.d_List = list;
      while (dvi = (struct DevInfo *)GetDevInfo((struct DeviceList *)dvi)) {
	 switch (dvi->dvi_Type) {
	    case DLT_DEVICE :
	       if (dvi->dvi_Task && dvi->dvi_Name) {
		  if (!(fnode = build_dev_node(dvi))) {
		     return(VSH_ERROR_OUT_OF_MEM);
		  } else {
		     if (! strncmp(&fnode->fn_Text[0], "RAM:", 4L)) {
			ram_flag = TRUE;
		     }
		     insert_file_node(fnode, list);
		  }
	       }
	       break;
	    case DLT_VOLUME :
	       if (dvi->dvi_Task && dvi->dvi_Name) {
		  if (!(fnode = build_dev_node(dvi))) {
		     return(VSH_ERROR_OUT_OF_MEM);
		  } else {
		     insert_file_node(fnode, list);
		  }
	       }
	       break;
	    case DLT_DIRECTORY :
	       BtoCStr(path, dvi->dvi_Name, (LONG)MAX_PATH_NAME_LEN);
	       strcat(path, (BYTE *)":");
	       if (lock = quiet_lock(path, (LONG)SHARED_LOCK)) {
		  if (Examine(lock, fib) == DOSTRUE) {
		     if (fib->fib_DirEntryType > 0) {   /* dir ? */
			if (!(fnode = build_dev_node(dvi))) {
			   UnLock(lock);
			   return(VSH_ERROR_OUT_OF_MEM);
			} else {
			   insert_file_node(fnode, list);
			}
		     }
		  }
		  UnLock(lock);
	       }
	       break;
	 }
      }
      if (ram_flag == FALSE) {
	 if (!(fnode = build_dev_node(NULL))) {
	    return(VSH_ERROR_OUT_OF_MEM);
	 } else {
	    insert_file_node(fnode, list);
	 }
      }
      if (freq->fr_Display.d_NumEntries = count_file_nodes(list)) {
	 freq->fr_Display.d_FirstVisibleNode = list->mlh_Head;
	 freq->fr_CursorLine                 = 0;
      }
   }
   return(error);
}
	/* Get file node under cursor of specified filerequester */

   struct FileNode *
get_file_node_under_cursor(struct FileRequest *freq)
{
   struct FileNode  *fnode = NULL;
   SHORT i, cursor_line = freq->fr_CursorLine;

   if (cursor_line != -1) {
      fnode = (struct FileNode *)freq->fr_Display.d_FirstVisibleNode;
      for (i = cursor_line; i; i--) {
	 fnode = (struct FileNode *)fnode->fn_Node.mln_Succ;
      }
   }
   return(fnode);
}
	/* Remove file node from file list of specified filerequester */

   struct FileNode *
remove_file_node(struct FileRequest *freq, struct FileNode *fnode)
{
   struct FileNode  *next_fnode;
   struct MinNode   *fv_node = freq->fr_Display.d_FirstVisibleNode;
   USHORT vlines = freq->fr_Display.d_VisibleLines;
   ULONG  num_entries = freq->fr_Display.d_NumEntries,
	  fv_pos = ((struct FileNode *)fv_node)->fn_Pos, pos = fnode->fn_Pos;

   if (pos == fv_pos) {   /* change first visible node */
      if (pos > 1 && (num_entries - pos) < vlines) {
	 fv_node = fv_node->mln_Pred;
      } else {
	 fv_node = fv_node->mln_Succ;
      }
   } else {
      if (num_entries > vlines && pos > fv_pos && fv_pos == (num_entries -
							      vlines + 1)) {
	 fv_node = fv_node->mln_Pred;
      }
   }
   freq->fr_Display.d_FirstVisibleNode = fv_node;
   freq->fr_Display.d_NumEntries       = recount_file_nodes(fnode);
   if (fnode->fn_Type == ENTRY_TYPE_DIR) {   /* dir ? */
      freq->fr_Info.i_Dirs--;
   } else {
      freq->fr_Info.i_FileSizes -= fnode->fn_Size;
      freq->fr_Info.i_Files--;
   }
   next_fnode = (struct FileNode *)fnode->fn_Node.mln_Succ;
   Remove((struct Node *)fnode);
   FreeMem(fnode, (LONG)sizeof(struct FileNode));
   print_freq_lines(freq, fv_node, (USHORT)0, vlines);
   refresh_dir_info(freq);
   return(next_fnode);
}
	/* Rename file node from file list of specified filerequester */

   VOID
rename_file_node(struct FileRequest *freq, struct FileNode *fnode,
								 BYTE *name)
{
   struct MinList  *flist = freq->fr_Display.d_List;
   struct MinNode  *fv_node = freq->fr_Display.d_FirstVisibleNode;
   BYTE   c, *ptr = &fnode->fn_Text[0];
   USHORT i, vlines = freq->fr_Display.d_VisibleLines;
   ULONG  num_entries = freq->fr_Display.d_NumEntries,
	  fv_pos = ((struct FileNode *)fv_node)->fn_Pos,
	  old_pos = fnode->fn_Pos, new_pos;

   Remove((struct Node *)fnode);
   for (i = 0; i < MAX_FILE_NAME_LEN; i++) {   /* insert new fnode name */
      if (!(c = *name++)) {
	 break;
      } else {
	 *ptr++ = c;
      }
   }
   fnode->fn_NameLen = i;
   for ( ; i < MAX_FILE_NAME_LEN; i++) {   /* clear rest of file name */
      *ptr++ = ' ';
   }
   insert_file_node(fnode, flist);
   if ((new_pos = fnode->fn_Pos) != old_pos) {   /* position changed ? */
      if (old_pos == fv_pos) {   /* change first visible node */
	 if (old_pos > 1 && (num_entries - old_pos) < vlines) {
	    fv_pos = old_pos - 1;
	 } else {
	    fv_pos = old_pos + 1;
	 }
	 fv_node = get_list_node(flist, num_entries, fv_pos);
      } else {
	 fv_pos = ((struct FileNode *)fv_node)->fn_Pos;
	 if (new_pos < fv_pos && (num_entries - fv_pos) < vlines) {
	    fv_node = fv_node->mln_Pred;
	 }
      }
   }
   freq->fr_Display.d_FirstVisibleNode = fv_node;
   freq->fr_Display.d_NumEntries       = count_file_nodes(flist);
   print_freq_lines(freq, fv_node, (USHORT)0, vlines);
}
	/* Duplicate file list from one filerequester to another one */

   SHORT
duplicate_file_list(struct FileRequest *freq1, struct FileRequest *freq2)
{
   struct FileNode  *fnode1, *fnode2;
   struct MinList   *flist1, *flist2;
   BPTR  dir_lock;
   ULONG num_entries, marked_entries, marked_size;
   SHORT error = VSH_STATUS_NORMAL;

   if (!(dir_lock = DupLock(freq1->fr_DirLock))) {
      error = VSH_ERROR_OUT_OF_MEM;
   } else {
      if (!(flist2 = (struct MinList *)AllocMem((LONG)
				    sizeof(struct MinList), MEMF_PUBLIC))) {
	 error = VSH_ERROR_OUT_OF_MEM;
      } else {
	 NewList((struct List *)flist2);
	 if (freq1->fr_Mode == FREQ_MODE_DEVS_ASN || freq1->fr_Mode ==
							   FREQ_MODE_FIND) {
	    flist1         = freq1->fr_SaveFileList;
	    num_entries    = freq1->fr_SaveNumEntries;
	    marked_entries = freq1->fr_SaveMarkedEntries;
	    marked_size    = freq1->fr_SaveMarkedSize;
	 } else {
	    flist1         = freq1->fr_Display.d_List;
	    num_entries    = freq1->fr_Display.d_NumEntries;
	    marked_entries = freq1->fr_MarkedEntries;
	    marked_size    = freq1->fr_MarkedSize;
	 }
	 fnode1 = (struct FileNode *)flist1->mlh_Head;
	 while (fnode1->fn_Node.mln_Succ && error == VSH_STATUS_NORMAL) {
	    if (!(fnode2 = (struct FileNode *)AllocMem((LONG)
		      sizeof(struct FileNode), MEMF_PUBLIC | MEMF_CLEAR))) {
	       error = VSH_ERROR_OUT_OF_MEM;
	    } else {
	       strcpy(&fnode2->fn_Text[0], &fnode1->fn_Text[0]);   /* copy file node */
	       CopyMem((BYTE *)fnode1, (BYTE *)fnode2, (LONG)
						   sizeof(struct FileNode));
	       AddTail((struct List *)flist2, (struct Node *)fnode2);
	       fnode1 = (struct FileNode *)fnode1->fn_Node.mln_Succ;
	    }
	 }
	 if (error == VSH_STATUS_NORMAL) {
	    free_dir(freq2);
	    strcpy(&freq2->fr_DirName[0], &freq1->fr_DirName[0]);
	    freq2->fr_DirLock                    = dir_lock;
	    freq2->fr_Display.d_List             = flist2;
	    freq2->fr_Display.d_NumEntries       = num_entries;
	    freq2->fr_Display.d_FirstVisibleNode = flist2->mlh_Head;
	    if (num_entries) {
	       freq2->fr_CursorLine = 0;
	    } else {
	       freq2->fr_CursorLine = -1;
	    }
	    freq2->fr_MarkedEntries = marked_entries;
	    freq2->fr_MarkedSize    = marked_size;
	    CopyMem((BYTE *)&freq1->fr_Info, (BYTE *)&freq2->fr_Info,
						 (LONG)sizeof(struct Info));
	    return(VSH_STATUS_NORMAL);
	 }
	 free_list(flist2, (LONG)sizeof(struct FileNode));
	 FreeMem(flist2, (LONG)sizeof(struct MinList));
      }
      UnLock(dir_lock);
   }
   return(error);
}
	/* Duplicate marks from one file list to another */

   VOID
duplicate_marks(struct FileRequest *freq, struct MinList *old_file_list,
						   ULONG old_marked_entries)
{
   struct FileNode  *fnode, *old_fnode = (struct FileNode *)
		 				    old_file_list->mlh_Head,
		    *new_fnode = (struct FileNode *)
					  freq->fr_Display.d_List->mlh_Head;
   if (old_marked_entries) {
      while (old_fnode->fn_Node.mln_Succ) {
	 if (old_fnode->fn_Marked) {
	    if (fnode = search_file_node(new_fnode, &old_fnode->fn_Text[0],
						   old_fnode->fn_NameLen)) {
	       fnode->fn_Marked     = 1;
	       freq->fr_MarkedSize += fnode->fn_Size;
	       freq->fr_MarkedEntries++;
	       new_fnode = fnode;
	    }
	 }
	 old_fnode = (struct FileNode *)old_fnode->fn_Node.mln_Succ;
      }
   }
}
	/* Duplicate cursor position from one filerequester to another */

   VOID
duplicate_cursor_line(struct FileRequest *freq, BYTE *old_name,
				       ULONG old_pos, SHORT old_cursor_line)
{
   struct FileNode  *fnode;

   if (old_cursor_line != -1) {
      if (!(fnode = search_file_node((struct FileNode *)
		       freq->fr_Display.d_List->mlh_Head, old_name, (USHORT)
						       strlen(old_name)))) {
	 fnode = (struct FileNode *)get_list_node(freq->fr_Display.d_List,
				    freq->fr_Display.d_NumEntries, old_pos);
      }
      move_freq_cursor(freq, fnode, MOVE_MODE_NO_OUTPUT);
   }
}
	/* Restore old cursor position for given filerequester */

   VOID
restore_old_cursor_line(struct FileRequest *freq, BYTE *old_name,
				       ULONG old_pos, SHORT old_cursor_line)
{
   struct FileNode  *fnode;

   if (old_cursor_line == -1 || !freq->fr_Display.d_NumEntries) {
      freq->fr_CursorLine = -1;
      print_info_line(INFO_LINE_MODE_EMPTY);
   } else {
      if (!(fnode = search_file_node((struct FileNode *)
		       freq->fr_Display.d_List->mlh_Head, old_name, (USHORT)
						       strlen(old_name)))) {
	 fnode = (struct FileNode *)get_list_node(freq->fr_Display.d_List,
				    freq->fr_Display.d_NumEntries, old_pos);
      }
      move_freq_cursor(freq, fnode, MOVE_MODE_NO_HCOMP);
   }
}
	/* Jump to an specified entry in file requester */

   VOID
jump_to_freq_entry(BYTE *name, USHORT mode)
{
   struct FileRequest  *freq = &file_req[active_freq];
   struct FileNode     *fnode;

   if (mode == JUMP_MODE_FIRST) {
      fnode = search_file_node((struct FileNode *)
			freq->fr_Display.d_List->mlh_Head, name, (USHORT)0);
   } else {
      fnode = (struct FileNode *)
			 get_file_node_under_cursor(freq)->fn_Node.mln_Succ;

      /* End of list ? */
      if (fnode->fn_Node.mln_Succ) {
	 if (!(fnode = search_file_node(fnode, name, (USHORT)0))) {
	    fnode = search_file_node((struct FileNode *)
			freq->fr_Display.d_List->mlh_Head, name, (USHORT)0);
	 }
      } else {
	 fnode = search_file_node((struct FileNode *)
			freq->fr_Display.d_List->mlh_Head, name, (USHORT)0);
      }
   }
   move_freq_cursor(freq, fnode, MOVE_MODE_NORMAL);
}
	/* Move cursor to specified entry in file requester */

   VOID
move_freq_cursor(struct FileRequest *freq, struct FileNode *fnode,
							        USHORT mode)
{
   ULONG  cursor_line, num_entries = freq->fr_Display.d_NumEntries;
   USHORT i, vlines = freq->fr_Display.d_VisibleLines,
	  vlines_half = vlines >> 1;

   if (mode == MOVE_MODE_NORMAL) {
      hcomp_freq_cursor(freq);
      print_info_line(INFO_LINE_MODE_EMPTY);
   }
   if (fnode) {
      cursor_line = fnode->fn_Pos - 1;
      if (num_entries > vlines) {
	 if (cursor_line < vlines_half) {
	    fnode = (struct FileNode *)freq->fr_Display.d_List->mlh_Head;
	 } else {
	    if (cursor_line >= (num_entries - vlines_half)) {
	       fnode = (struct FileNode *)
				      freq->fr_Display.d_List->mlh_TailPred;
	       i = vlines - 1;
	    } else {
	       i = vlines_half - (!(vlines & 1) ? 1 : 0);   /* dec for even num of visible lines */
	    }
	    for ( ; i; i--) {
	       fnode = (struct FileNode *)fnode->fn_Node.mln_Pred;
	    }
	    cursor_line -= fnode->fn_Pos - 1;
	 }
      } else {
	 fnode = (struct FileNode *)freq->fr_Display.d_List->mlh_Head;

      }
   } else {
      if (freq->fr_DirLock && freq->fr_Display.d_NumEntries) {
         fnode = (struct FileNode *)freq->fr_Display.d_List->mlh_Head;
	 cursor_line = 0;
      } else {
	 cursor_line = -1;
      }
   }
   if (!fnode || fnode != (struct FileNode *)
				      freq->fr_Display.d_FirstVisibleNode) {
      freq->fr_Display.d_FirstVisibleNode = (struct MinNode *)fnode;
      if (mode != MOVE_MODE_NO_OUTPUT) {
	 print_freq_lines(freq, (struct MinNode *)fnode, (USHORT)0, vlines);
      }
   } else {
      if (mode == MOVE_MODE_PRINT) {
	 print_freq_lines(freq, freq->fr_Display.d_FirstVisibleNode,
							 (USHORT)0, vlines);
      }
   }
   freq->fr_CursorLine = cursor_line;
   if (mode != MOVE_MODE_NO_OUTPUT && mode != MOVE_MODE_NO_CURSOR) {
      hcomp_freq_cursor(freq);
      print_info_line(INFO_LINE_MODE_NORMAL);
   }
}
	/* Check last changed date of directory for given file requester */

   SHORT
check_date_stamp(struct FileRequest *freq, USHORT mode)
{
   struct DateStamp  *old_date = &freq->fr_Info.i_LastChanged,
		     *new_date = &fib->fib_Date;
   SHORT error = VSH_STATUS_NORMAL;

   if (freq->fr_Mode == FREQ_MODE_NORMAL && freq->fr_DirName[0] != '\0') {
      if ((error = quiet_examine(freq->fr_DirLock, fib)) ==
							VSH_STATUS_NORMAL) {
	 if (old_date->ds_Days != new_date->ds_Days || old_date->ds_Minute
				 != new_date->ds_Minute || old_date->ds_Tick
						     != new_date->ds_Tick) {
	    if (mode == READ_SAME_DIR_MODE_NO_READ) {
	       error = VSH_STATUS_READ_DIR;   /* indicate to read same dir again */
	    } else {
	       error = read_same_dir(freq, mode);
	    }
	 }
      }
   }
   return(error);
}
	/* Set last changed date of directory for given file requester */

   SHORT
set_date_stamp(struct FileRequest *freq)
{
   SHORT error;

   if ((error = quiet_examine(freq->fr_DirLock, fib)) == VSH_STATUS_NORMAL) {
      CopyMem((BYTE *)&fib->fib_Date, (BYTE *)&freq->fr_Info.i_LastChanged,
					    (LONG)sizeof(struct DateStamp));
   }
   return(error);
}
	/* Execute program with arguments */

   SHORT
execute(BYTE *prog, BYTE *args)
{
   BYTE  cmd[MAX_DIR_NAME_LEN + MAX_FILE_NAME_LEN + 1], *ptr = &cmd[0];
   SHORT error;

   if (wshell_flag && FindPort("REXX") && *(USHORT *)((UBYTE *)DOSBase -
							   222)	== 0x4ef9) {
      *ptr++ = '[';   /* needed for WShell + ARexx + patched Execute() vector */
   }
   *ptr++ = '"';
   strcpy(ptr, prog);
   ptr   += strlen(prog);
   *ptr++ = '"';
   *ptr++ = ' ';
   strcpy(ptr, args);
   if (Execute(&cmd[0], nil_handle, Output()) == DOSFALSE) {
      error = VSH_ERROR_EXECUTE_FAILED;
   } else {
      error = VSH_STATUS_NORMAL;
   }
/*
Printf("Execute(%s)=%d\n", &cmd[0], error);
*/
   return(error);
}
	/* Find files or dirs by pattern, start from path in active freq */

   SHORT
find(struct FileRequest *freq, BYTE *pattern)
{
   struct AnchorPath     *ap;
   struct FileInfoBlock  *info;
   struct MinList        *list;
   struct FileNode       *fnode;
   BYTE  *path, *path_buffer, *buffer;
   LONG  rc;
   BPTR  cd_lock;
   BOOL  keepon = TRUE;
   SHORT error = VSH_STATUS_NORMAL;

   print_fkey_text(FKEY_MODE_NONE);
   freq->fr_Display.d_NumEntries       = 0;
   freq->fr_Display.d_FirstVisibleNode = NULL;
   freq->fr_CursorLine                 = -1;
   freq->fr_MarkedEntries              = 0;
   freq->fr_MarkedSize                 = 0;
   freq->fr_Mode                       = FREQ_MODE_FIND;
   if (!(list = (struct MinList *)AllocMem((LONG)sizeof(struct MinList),
					       MEMF_PUBLIC | MEMF_CLEAR))) {
      error = VSH_ERROR_OUT_OF_MEM;
   } else {
      NewList((struct List *)list);
      freq->fr_Display.d_List = list;
      if (!(ap = (struct AnchorPath *)AllocMem((LONG)
			     (sizeof(struct AnchorPath) + MAX_DIR_NAME_LEN +
		       MAX_FILE_NAME_LEN + 1), MEMF_PUBLIC | MEMF_CLEAR))) {
	 error = VSH_ERROR_OUT_OF_MEM;
      } else {
	 ap->ap_BreakBits = SIGBREAKF_CTRL_C;
	 ap->ap_StrLen    = MAX_DIR_NAME_LEN + MAX_FILE_NAME_LEN;
	 ap->ap_Flags     = APF_DoWild;   /* traverse through ALL dirs */
	 info             = &ap->ap_Info;
	 path             = &ap->ap_Buf[0];
	 PreParse(pattern, &line1_buffer[0]);
	 pattern = &line1_buffer[0];   /* compiled pattern */
	 buffer  = &path1_buffer[0];
	 cd_lock = CurrentDir(freq->fr_DirLock);
	 enable_abort = 1;
	 if (!(rc = FindFirst(star, ap))) {
	    do {

	       /* Check if entry matches pattern */
	       if ((info->fib_DirEntryType < 0 || (info->fib_DirEntryType > 0
		 			&& !(ap->ap_Flags & APF_DidDir))) &&
			    PatternMatch(pattern, &info->fib_FileName[0]) ==
								     TRUE) {
		  /* Build full path name and display it in status line */
		  if ((error = build_path_name_from_relative_path(buffer,
					      path)) != VSH_STATUS_NORMAL) {
		     keepon = FALSE;
		  } else {
		     print_dos_status(DOS_MODE_FIND, buffer, NULL);

		     /* Strip file name from path and build file node from it */
		     *(BaseName(buffer) - 1) = '\0';
		     if (!(path_buffer = DosAllocMem((LONG)(strlen(buffer) +
								     1)))) {
			keepon = FALSE;
			error  = VSH_ERROR_OUT_OF_MEM;
		     } else {
			if (!(fnode = build_file_node(info))) {
			   keepon = FALSE;
			   error  = VSH_ERROR_OUT_OF_MEM;
			} else {
			   strcpy(path_buffer, buffer);
			   fnode->fn_Path = path_buffer;
			   insert_file_node(fnode, list);
			}
		     }
		  }
	       }

	       /* If directory then check if already scanned */
	       if (info->fib_DirEntryType > 0) {
		  if (ap->ap_Flags & APF_DidDir) {
		     ap->ap_Flags &= ~APF_DidDir;   /* don't enter dir again */
		  } else {
		     ap->ap_Flags |= APF_DoDir;   /* enter dir */
		  }
	       }
	    } while (keepon == TRUE && !(rc = FindNext(ap)));
	 }
	 enable_abort = 0;
	 FreeAnchorChain(ap);
	 CurrentDir(cd_lock);
	 if (error == VSH_STATUS_NORMAL) {
	    switch (rc) {
	       case ERROR_BREAK :
		  error = VSH_ERROR_ABORTED;
	       case ERROR_NO_MORE_ENTRIES :
		  if (freq->fr_Display.d_NumEntries =
						   count_file_nodes(list)) {
		     freq->fr_Display.d_FirstVisibleNode = list->mlh_Head;
		     freq->fr_CursorLine                 = 0;
		  }
		  break;
	       default :
		  error = VSH_ERROR_FINDFIRST_OR_FINDNEXT_FAILED;
		  break;
	    }
	 }
	 FreeMem(ap, (LONG)(sizeof(struct AnchorPath) + MAX_DIR_NAME_LEN +
						    MAX_FILE_NAME_LEN + 1));
      }
   }
   return(error);
}
	/* Clear console read buffer */

   VOID
clear_input_line(VOID)
{
   if ((struct CommandLineInterface *)BADDR(_parent_proc->pr_CLI) ==
								 save_cli) {   /* no EndCLI ? */
      dos_packet(con_input_fhandle->fh_Type, (LONG)ACTION_FLUSH);
   }
}
	/* Force string into command line */

   SHORT
force_string(BYTE *string, USHORT len, USHORT mode)
{
   BYTE  *path = &path2_buffer[0];
   SHORT error = VSH_STATUS_NORMAL;

   if ((struct CommandLineInterface *)BADDR(_parent_proc->pr_CLI) ==
								 save_cli) {   /* no EndCLI ? */

      if (mode == FORCE_MODE_NEWLINE_APPEND || mode ==
						  FORCE_MODE_NEWLINE_COPY) {
	 if (dos_packet(con_input_fhandle->fh_Type, (LONG)ACTION_FLUSH) ==
								 DOSFALSE) {
	    error = VSH_ERROR_WRONG_CONMAN_VERSION;
	 } else {
	    if (mode == FORCE_MODE_NEWLINE_APPEND) {
	       *(string + len++) = '\n';   /* append new line char */
	    } else {
	       strncpy(path, string, (size_t)len);   /* copy string with new line char */
	       *(path + len++) = (BYTE)'\n';
	       string          = path;
	    }
	 }
      }
      if (error == VSH_STATUS_NORMAL) {
	 if (dos_packet(con_input_fhandle->fh_Type, CONMAN_ACTION_FORCE,
			con_input_fhandle, string, (LONG)len) == DOSFALSE) {
	    error = VSH_ERROR_WRONG_CONMAN_VERSION;
	 }
      }
   }
   return(error);
}
