/*
 * CDE - Common Desktop Environment
 *
 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these libraries and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 */
/* $TOG: Directory.c /main/18 1999/12/09 13:05:34 mgreess $ */
/************************************<+>*************************************
 ****************************************************************************
 *
 *   FILE:           Directory.c
 *
 *   COMPONENT_NAME: Desktop File Manager (dtfile)
 *
 *   Description:    Directory processing functions used by the File Browser.
 *
 *   FUNCTIONS: CheckDesktop
 *              CheckDesktopPipeCallback
 *              CheckDesktopProcess
 *              CheckListCmp
 *              DirectoryBeginModify
 *              DirectoryBusy
 *              DirectoryEndModify
 *              DirectoryFileModified
 *              DirectoryGone
 *              DirectoryModifyTime
 *              FileData2toFileData
 *              FileWindowMapUnmap
 *              FindDirectory
 *              FreeDirectory
 *              FreeFileData
 *              GetDirectoryLogicalType
 *              GetDirectoryPositionInfo
 *              GetLongName
 *              InitializeDirectoryRead
 *              InitializePositionFileName
 *              PipeReadFileData
 *              PipeReadPositionInfo
 *              PipeWriteFileData
 *              PipeWritePositionInfo
 *              ReadDir
 *              ReadDirectory
 *              ReadDirectoryFiles
 *              ReadDirectoryProcess
 *              ReadFileData
 *              ReadFileData2
 *              ReaddirPipeCallback
 *              RereadDirectory
 *              ScheduleActivity
 *              ScheduleDirectoryActivity
 *              SetDirectoryPositionInfo
 *              SkipRefresh
 *              SomeWindowMapped
 *              StickyProcIdle
 *              TimerEvent
 *              TimerEventBrokenLinks
 *              TimerEventProcess
 *              TimerPipeCallback
 *              UpdateAllProcess
 *              UpdateCachedDirectories
 *              UpdateDirectory
 *              UpdateDirectorySet
 *              UpdateSomeProcess
 *              WritePosInfoPipeCallback
 *              WritePosInfoProcess
 *              _ReadDir
 *              SelectDesktopFile
 *
 *   (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
 *   (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
 *   (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
 *   (c) Copyright 1993, 1994, 1995 Novell, Inc.
 *
 ****************************************************************************
 ************************************<+>*************************************/

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <grp.h>
#include <pwd.h>
#include <time.h>
#include <dirent.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <limits.h>
#include <string.h>
#include <assert.h>

#include <Xm/Xm.h>

#include <Dt/Connect.h>
#include <Dt/DtNlUtils.h>
#include <Dt/Dts.h>
#include <Dt/Icon.h>
#include <Tt/tttk.h>

#include "Encaps.h"
#include "FileMgr.h"
#include "Desktop.h"
#include "IconicPath.h"
#include "Main.h"
#include "SharedMsgs.h"
#include "SharedProcs.h"
#include "Prefs.h"

extern Boolean removingTrash;


/*--------------------------------------------------------------------
 * Constants and Types
 *------------------------------------------------------------------*/

/* File modes */
#define OPTION_OFF '-'
#define WRITE_PRIV 'w'
#define READ_PRIV  'r'
#define EXEC_PRIV  'x'

/* prefix for the name of the position info file */
#define POSITION_FILE_PREFIX  ".!dt"

/* kinds of messages sent through the pipe */
#define PIPEMSG_ERROR               1
#define PIPEMSG_FILEDATA            2
#define PIPEMSG_DONE                3
#define PIPEMSG_PATH_LOGICAL_TYPES  4
#define PIPEMSG_POSITION_INFO       5
#define PIPEMSG_FILEDATA2           6
#define PIPEMSG_FILEDATA3           7
#define PIPEMSG_DESKTOP_REMOVED     8
#define PIPEMSG_DESKTOP_CHANGED     9

#ifndef	FILEDATABUF
#define	FILEDATABUF 50
#endif /* FILEDATABUF */

#define	NILL '\0'

/*
 * Background activities, ordered by priority:
 * (activity_idle must be the last one in the list!)
 */
typedef enum
{
  activity_writing_posinfo,  /* writing position information file */
  activity_reading,          /* reading the directory */
  activity_update_all,       /* updating the directory */
  activity_update_some,      /* updating selected files */
  activity_checking_links,   /* checking for broken links */
  activity_checking_desktop, /* checking desktop objects */
  activity_checking_dir,     /* checking if the directory has changed */
  activity_idle              /* no background activity */
} ActivityStatus;

/*  The internal directory structure and directory set list  */

typedef struct
{
   FileMgrData * file_mgr_data;
   Boolean       mapped;
} DirectoryView;

typedef struct
{
   char           * host_name;
   char           * directory_name;
   char           * path_name;
   char           * tt_path_name;
   Boolean          viewed;
   ActivityStatus   activity;
   Boolean          busy[activity_idle];
   Boolean          errmsg_needed;
   int              errnum;
   time_t           modify_time;
   int              last_check;
   Boolean          link_check_needed;
   int              file_count;
   FileData       * file_data;
   FileData       * new_data;
   FileData       * dir_data;
   int              path_count;
   char          ** path_logical_types;
   int              position_count;
   PositionInfo   * position_info;
   int              modify_begin;
   Boolean          was_up_to_date;
   int              modified_count;
   char          ** modified_list;
   int              numOfViews;
   DirectoryView  * directoryView;
} Directory;


/* data for keeping track of sticky background procs */
typedef struct _spd
{
   pid_t child;
   int pipe_s2m_fd;
   int pipe_m2s_fd;
   Boolean idle;
   struct _spd *next;
} StickyProcDesc;

/* data for callback routines that handle background processes */
typedef struct
{
   Directory *directory;
   pid_t child;
   StickyProcDesc *sticky_proc;
   ActivityStatus activity;
} PipeCallbackData;


/* background procedure */
typedef int (*DirBackgroundProc)(int, Directory *, ActivityStatus);

extern void _DtFlushIconFileCache(String path);


/*--------------------------------------------------------------------
 * Static Function Declarations
 *------------------------------------------------------------------*/

static void TimerEvent(
                        XtPointer client_data,
                        XtIntervalId *id);
static void ScheduleActivity(
			Directory *directory);
static int WritePosInfoProcess(
			int pipe_fd,
			Directory *directory,
			ActivityStatus activity);
static int ReadDirectoryProcess(
			int pipe_fd,
			Directory *directory,
			ActivityStatus activity);
static int UpdateAllProcess(
			int pipe_fd,
			Directory *directory,
			ActivityStatus activity);
static int UpdateSomeProcess(
			int pipe_fd,
			Directory *directory,
			ActivityStatus activity);
static int TimerEventProcess(
			int pipe_fd,
			Directory *directory,
			ActivityStatus activity);
static int CheckDesktopProcess(
			int pipe_fd,
			Directory *directory,
			ActivityStatus activity);
static void WritePosInfoPipeCallback(
			XtPointer client_data,
			int *fd,
			XtInputId *id);
static void ReaddirPipeCallback(
			XtPointer client_data,
			int *fd,
			XtInputId *id);
static void TimerPipeCallback(
			XtPointer client_data,
			int *fd,
			XtInputId *id);
static void CheckDesktopPipeCallback(
			XtPointer client_data,
			int *fd,
			XtInputId *id);
static void SelectDesktopFile(FileMgrData *fmd);



/*--------------------------------------------------------------------
 * Static Data
 *------------------------------------------------------------------*/

int maxDirectoryProcesses = 10;
int maxRereadProcesses = 5;
int maxRereadProcsPerTick = 1;

XtIntervalId checkBrokenLinkTimerId = None;

static Directory ** directory_set = NULL;
static int          directory_count = 0;
static int          directory_set_size = 0;
static char       * positionFileName = NULL;
static XtAppContext app_context = None;
static int          tickTime = 0;
static int          ticksBetweenLinkChecks = 0;
static Boolean      timer_suspended = False;
static int          tick_count = 0;
static int          lastLinkCheckTick = 0;
static Directory  dummy_dir_struct =
{
  "dummy_host",
  "dummy_directory",
  "dummy_path",
  NULL,
  False,
  activity_idle
};
static Directory *dummy_directory = &dummy_dir_struct;

static struct
{
   DirBackgroundProc main;
   XtInputCallbackProc callback;
   Boolean sticky;
   StickyProcDesc *sticky_procs;
} ActivityTable[] =
{
  { WritePosInfoProcess,  WritePosInfoPipeCallback, False,NULL },/* writing_posinfo*/
  { ReadDirectoryProcess, ReaddirPipeCallback, False,NULL },     /* reading */
  { UpdateAllProcess,     ReaddirPipeCallback, False,NULL },     /* update_all */
  { UpdateSomeProcess,    ReaddirPipeCallback, False,NULL },     /* update_some */
  { TimerEventProcess,    TimerPipeCallback, False,NULL },       /* checking_links */
  { CheckDesktopProcess,  CheckDesktopPipeCallback, False,NULL },/* checking_desktop */
  { TimerEventProcess,    TimerPipeCallback, True, NULL },  /* checking_dir */
  { NULL,                 NULL, False, NULL }               /* idle */
};


/*====================================================================
 *
 * Initialization routines
 *
 *==================================================================*/

/*--------------------------------------------------------------------
 *  InitializePositionFileName
 *	Initialize the name under which the position info is stored.
 *------------------------------------------------------------------*/

static void
InitializePositionFileName(void)
{
   struct passwd * pwInfo;

   /* Determine the name under which the position info is stored */
   if (positionFileName == NULL)
   {

      pwInfo = getpwuid(getuid());
      positionFileName = XtMalloc(strlen(pwInfo->pw_name) +
                                  strlen(POSITION_FILE_PREFIX) + 1);
      sprintf(positionFileName, "%s%s", POSITION_FILE_PREFIX, pwInfo->pw_name);
   }
}


/*--------------------------------------------------------------------
 *  InitializeDirectoryRead
 *	Set up a timer used to automatically check the read in
 *	directories to see if they have been modified.
 *------------------------------------------------------------------*/

void
InitializeDirectoryRead(
        Widget widget )

{
   /* remeber application context */
   app_context = XtWidgetToApplicationContext(widget);

   /* start timer to check for modified directories and broken links */
   tick_count = lastLinkCheckTick = 0;

   if (rereadTime != 0)
   {
     tickTime = rereadTime;
     ticksBetweenLinkChecks = checkBrokenLink / rereadTime;
   }
   else if (checkBrokenLink != 0)
   {
     tickTime = checkBrokenLink;
     ticksBetweenLinkChecks = 1;
   }
   else
     tickTime = 0;

   if (tickTime != 0)
      XtAppAddTimeOut (app_context, tickTime * 1000, TimerEvent, NULL);

   /* start timer to check for broken desktop objects */
   if( desktop_data->numIconsUsed > 0
       && checkBrokenLink != 0
     )
   {
     checkBrokenLinkTimerId = XtAppAddTimeOut( app_context,
                                               checkBrokenLink * 1000,
                                               TimerEventBrokenLinks,
                                               NULL);
   }
   else
   {
     checkBrokenLinkTimerId = None;
   }
}


/*====================================================================
 *
 * Utiltiy functions
 *
 *==================================================================*/

/*--------------------------------------------------------------------
 *  FindDirectory
 *    Given a host & directory name, find the directory in our cache.
 *------------------------------------------------------------------*/

static Directory *
FindDirectory(
        char *host_name,
        char *directory_name)
{
   int i;

   /* See if the directory is in the directory set.  First, compare    */
   /* the names from the directory entries ONLY.  There will be one    */
   /* directory entry for every directory of a different name.  This   */
   /* may mean that there is more than one directory entry for a       */
   /* single directory (ie. if there is a directory that is a link or  */
   /* a mount point in the system).                                    */
   /* If this doesn't succeed, we may be getting a ToolTalk resolved   */
   /* name. So run the comparison again but this time compare the      */
   /* incoming name to the tt_path_name in the directory entries.      */
   /* This algorithm has a limitation in that if ToolTalk has resolved */
   /* a path name, the match will occur for the first entry in the     */
   /* directory set whose tt_path_name matches our name, this MAY NOT  */
   /* be the directory where the activity originated.  The user should */
   /* only notice in the case where automatic refresh is turned off.   */
   for (i = 0; i < directory_count; i++)
   {
      if (strcmp (host_name, directory_set[i]->host_name) == 0 &&
          strcmp (directory_name, directory_set[i]->directory_name) == 0)
      {
         return directory_set[i];
      }
   }

   for (i = 0; i < directory_count; i++)
   {
      if (directory_set[i]->tt_path_name != NULL &&
          strcmp (host_name, home_host_name) == 0 &&
          strcmp (directory_name, directory_set[i]->tt_path_name) == 0)
      {
         return directory_set[i];
      }
   }

   /* not found */
   return NULL;
}


/*--------------------------------------------------------------------
 *  DirectoryGone
 *    Check if a directory has been removed from the cache.
 *------------------------------------------------------------------*/

static Boolean
DirectoryGone(
        Directory *directory)
{
   int i;

   for (i = 0; i < directory_count; i++)
      if (directory_set[i] == directory)
         return False;

   return True;
}


/*--------------------------------------------------------------------
 *  FreeFileData
 *	Free FileData structure.
 *------------------------------------------------------------------*/

void
FreeFileData(
        FileData *file_data,
        Boolean free_all)
{
   XtFree(file_data->file_name);
   file_data->file_name = NULL;

   XtFree(file_data->action_name);
   file_data->action_name = NULL;

   DtDtsFreeDataType(file_data->logical_type);
   file_data->logical_type = NULL;

   if ( file_data->final_link != NULL &&
        file_data->final_link != file_data->link)
   {
      XtFree(file_data->final_link);
      file_data->final_link = NULL;
   }

   if (file_data->link != NULL )
   {
     XtFree(file_data->link);
     file_data->link = NULL;
   }

   if (free_all)
      XtFree((char *)file_data);
}


/*--------------------------------------------------------------------
 *  FreeDirectory
 *	Free Directory structure.
 *------------------------------------------------------------------*/

static void
FreeDirectory(
        Directory *directory)
{
   int i;
   FileData *file_data, *next_file_data;

   if( directory == NULL )
     return;

   XtFree (directory->host_name);
   directory->host_name = NULL;

   XtFree (directory->directory_name);
   directory->directory_name = NULL;

   XtFree (directory->path_name);
   directory->path_name = NULL;

   XtFree (directory->tt_path_name);
   directory->tt_path_name = NULL;

   for (i=0; i < directory->path_count; i++)
      DtDtsFreeDataType(directory->path_logical_types[i]);
   XtFree ((char *) directory->path_logical_types);
   directory->path_logical_types = NULL;

   for (i = 0; i < directory->position_count; i++)
      XtFree(directory->position_info[i].name);
   XtFree ((char *) directory->position_info);
   directory->position_info = NULL;

   for (i = 0; i < directory->modified_count; i++)
      XtFree(directory->modified_list[i]);
   XtFree ((char *) directory->modified_list);
   directory->modified_list = NULL;

   XtFree ((char *) directory->directoryView);
   directory->directoryView = NULL;

   if (directory->dir_data)
   {
      FreeFileData(directory->dir_data, True);
      directory->dir_data = NULL;
   }

   file_data = directory->file_data;
   while (file_data != NULL)
   {
      next_file_data = file_data->next;
      FreeFileData(file_data, True);
      file_data = next_file_data;
   }
   directory->file_data = NULL;

   XtFree((char *) directory);
}


/*--------------------------------------------------------------------
 *  SomeWindowMapped
 *    Check if any cached directory is currently being viewed in
 *    a window that is mapped (not iconified).  (If there is none,
 *    we won't need to set a refresh timer).
 *------------------------------------------------------------------*/

static Boolean
SomeWindowMapped(void)

{
   int i, j;

   for (i = 0; i < directory_count; i++)
      for (j = 0; j < directory_set[i]->numOfViews; j++)
         if (directory_set[i]->directoryView[j].mapped)
            return True;

   return False;
}


/*====================================================================
 *
 * Routines for reading a directory
 *
 *==================================================================*/

/*--------------------------------------------------------------------
 *  PipeWrite...
 *  PipeRead...
 *      Directories are read in a background process that is connected
 *      to the main dtfile process by a pipe.
 *	The routines below are used to send directory entry information
 *      through the pipe from the background to the main process.
 *------------------------------------------------------------------*/

/* write FileData to the pipe */
void
PipeWriteFileData(
	int fd,
	FileData *file_data)
{
   write(fd, file_data, sizeof(FileData));
   PipeWriteString(fd, file_data->file_name);
   PipeWriteString(fd, file_data->action_name);  /* @@@ ??? */
   PipeWriteString(fd, file_data->logical_type);
   if (file_data->link)
      PipeWriteString(fd, file_data->link);
   if (file_data->final_link)
      PipeWriteString(fd, file_data->final_link);
}


/* read FileData from the pipe */
static FileData *
PipeReadFileData(
	int fd)
{
   FileData *file_data;
   int n;

   file_data = (FileData *)XtCalloc(1,sizeof(FileData));
   n = PipeRead(fd, file_data, sizeof(FileData));
   if (n < sizeof(FileData))
   {
      fprintf(stderr, "PipeReadFileData: n = %d, expected %ld\n",
              n, (long)sizeof(FileData));
   }
   file_data->file_name = PipeReadString(fd);
   file_data->action_name = PipeReadString(fd);
   file_data->logical_type = PipeReadString(fd);
   if (file_data->link)
      file_data->link = PipeReadString(fd);
   if (file_data->final_link)
      file_data->final_link = PipeReadString(fd);

   /* return the file data */
   return file_data;
}

FileData *
FileData2toFileData(
	FileData2 *file_data2,
	int *l)
{
   FileData *file_data;
   int n;
   char file_name_buf[MAXPATHLEN];
   char action_name_buf[MAXPATHLEN];
   char logical_type_buf[MAXPATHLEN];
   char link_buf[MAXPATHLEN];
   char final_link_buf[MAXPATHLEN];
   char *textptr = file_data2->text;

   file_data = (FileData *)XtCalloc(1,sizeof(FileData));

   strncpy(file_name_buf, textptr, n = file_data2->file_name);
   file_name_buf[n] = NILL;
   textptr += n;

   strncpy(action_name_buf, textptr, n = file_data2->action_name);
   action_name_buf[n] = NILL;
   textptr += n;

   strncpy(logical_type_buf, textptr, n = file_data2->logical_type);
   logical_type_buf[n] = NILL;
   textptr += n;

   strncpy(link_buf, textptr, n = file_data2->link);
   link_buf[n] = NILL;
   textptr += n;

   strncpy(final_link_buf, textptr, n = file_data2->final_link);
   final_link_buf[n] = NILL;
   textptr += n;

   file_data->next		= NULL;
   file_data->file_name		= XtNewString(file_name_buf);
   file_data->action_name	= file_data2->action_name
				? XtNewString(action_name_buf)
				: NULL;
   file_data->physical_type	= file_data2->physical_type;
   file_data->logical_type	= XtNewString(logical_type_buf);
   file_data->errnum		= file_data2->errnum;
   file_data->stat		= file_data2->stat;

   file_data->link		= file_data2->link
				? XtNewString(link_buf)
				: NULL;

   file_data->final_link	= file_data2->final_link
				? XtNewString(final_link_buf)
				: NULL;
   file_data->is_subdir		= file_data2->is_subdir;
   file_data->is_broken		= file_data2->is_broken;

   *l = sizeof(*file_data2) - sizeof(file_data2->text)
         + file_data2->file_name + file_data2->action_name
         + file_data2->logical_type + file_data2->link
         + file_data2->final_link;

   *l = (*l + sizeof(char *) - 1) & ~(sizeof(char *) - 1);

   /* return the file data */
   return file_data;
}

/* write PositionInfo to the pipe */
void
PipeWritePositionInfo(
	int fd,
	PositionInfo *position_info)
{
   PipeWriteString(fd, position_info->name);
   write(fd, &position_info->x, sizeof(Position));
   write(fd, &position_info->y, sizeof(Position));
   write(fd, &position_info->stacking_order, sizeof(int));
}


/* read PositionInfo from the pipe */
static void
PipeReadPositionInfo(
	int fd,
	PositionInfo *position_info)
{
   position_info->name = PipeReadString(fd);
   PipeRead(fd, &position_info->x, sizeof(Position));
   PipeRead(fd, &position_info->y, sizeof(Position));
   PipeRead(fd, &position_info->stacking_order, sizeof(int));
}

/*--------------------------------------------------------------------
 *  ReadFileData
 *    Given a path name, return FileData for a file.
 *------------------------------------------------------------------*/

FileData *
ReadFileData(
	char *full_directory_name,
	char *file_name)
{
   FileData *file_data;
   char full_file_name[MAX_PATH];
   char link_file_name[MAX_PATH];
   char link_path[MAX_PATH];
   int link_count;
   char ** link_list;
   int link_len;
   char * end;
   char * ptr;
   Boolean recursive_link_found;
   struct stat stat_buf;
   struct stat stat_buf2;
   int stat_result;
   int stat_errno;
   int i;

   /*  Allocate a new file structure.  */
   file_data = (FileData *)XtMalloc(sizeof(FileData));

   /* get the full name of the file */
   strcpy (full_file_name, full_directory_name);
   if (file_name)
   {
      /* append file name to the directory */
      if (strcmp(full_directory_name,"/") != 0)
         strcat (full_file_name, "/");
      strcat (full_file_name, file_name);
   }
   else
   {
      /* no file name passed: use last component of directory */
      file_name = strrchr(full_file_name, '/');
      if (file_name > full_file_name)
         file_name++;
      else
         file_name = NULL;
   }

   /* Follow symbolic links to their ultimate destination */
   link_count = 0;
   link_list = NULL;
   recursive_link_found = False;
   strcpy(link_file_name, full_file_name);

   stat_result = lstat (link_file_name, &stat_buf);
   if (stat_result == 0 && (stat_buf.st_mode & S_IFMT) == S_IFLNK)
   {
     while ((link_len = readlink(link_file_name, link_path, MAX_PATH - 1)) > 0)
     {
       link_path[link_len] = 0;
       link_list = (char **)XtRealloc((char *)link_list, sizeof(char *) *
                                      (link_count + 2));

       /* Force the link to be an absolute path, if necessary */
       if (link_path[0] != '/')
       {
         /* Relative paths are relative to the current directory */
         end = strrchr(link_file_name, '/') + 1;
         *end = '\0';
         strcat(link_file_name, link_path);
       }
       else
         strcpy(link_file_name, link_path);

       /* Check for a recursive loop; abort if found */
       for (i = 0; i < link_count; i++)
       {
         if (strcmp(link_file_name, link_list[i]) == 0)
         {
           /* Back up to last non-recursive portion */
           strcpy(link_file_name, link_list[link_count - 1]);
           recursive_link_found = True;
           break;
         }
       }

       if (recursive_link_found)
         break;

       link_list[link_count++] = XtNewString(link_file_name);
       link_list[link_count] = NULL;
     }

     /* try to stat the file that the link points to */
     if (stat (link_file_name, &stat_buf2) == 0)
     {
       /* replace lstat result with the stat */
       memcpy(&stat_buf, &stat_buf2, sizeof(struct stat));
     }
   }
   stat_errno = errno;

   /* fill in the FileData structure with the information we found */
   file_data->next = NULL;
   file_data->file_name = XtNewString(file_name? file_name: ".");
   file_data->logical_type = NULL;
   file_data->is_subdir = False;
   file_data->action_name = NULL;

   if (link_list)
   {
      file_data->link = XtNewString( link_list[0] );
      file_data->final_link = XtNewString( link_list[link_count - 1] );
      for (i = 0; i < link_count; i++)
         XtFree(link_list[i]);
      XtFree((char *)link_list);
   } else
      file_data->link = file_data->final_link = NULL;

   if (stat_result == 0)
   {
      file_data->errnum = 0;
      file_data->stat = stat_buf;

      /*  Find and set the physical type of the file  */

      if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
      {
         file_data->physical_type = DtDIRECTORY;
         if (file_name == NULL ||
             strcmp(file_name, ".") != 0 && strcmp(file_name, "..") != 0)
         {
            file_data->is_subdir = True;
         }
      }
      else if ((stat_buf.st_mode & S_IFMT) == S_IFREG)
      {
         if ((stat_buf.st_mode & S_IXUSR) ||
             (stat_buf.st_mode & S_IXGRP) ||
             (stat_buf.st_mode & S_IXOTH))
            file_data->physical_type = DtEXECUTABLE;
         else
            file_data->physical_type = DtDATA;
      }
      else
         file_data->physical_type = DtDATA;

      /*  Find and set the logical type of the file  */
      if ((stat_buf.st_mode & S_IFMT) == S_IFLNK)
      {
         file_data->is_broken = True;
         if (recursive_link_found)
            file_data->logical_type = XtNewString(LT_RECURSIVE_LINK);
         else
            file_data->logical_type = XtNewString(LT_BROKEN_LINK);
      }
      else
      {
         file_data->is_broken = False;
         if (file_data->link)
            file_data->logical_type = (char *) DtDtsDataToDataType(
                                                 file_data->link,
                                                 NULL, 0, &stat_buf,
                                                 file_data->final_link, NULL,
                                                 NULL);
         else
            file_data->logical_type = (char *) DtDtsDataToDataType(
                                                 full_file_name,
                                                 NULL, 0, &stat_buf,
                                                 NULL, NULL,
                                                 NULL);
#if defined( DATATYPE_IS_FIXED )
#else
         /* The problem here is there isn't a way for user to mask
            only the OWNER READ bit in the MODE field of dtfile.dt file.
            If the MODE field set to d&!r Then all READ permission
            (S_IRUSR, S_IRGRP and S_IROTH)
            bits has to be off in order for the above data typing to work.
            Also data typing is unable to detect when the directory is not
            the owners and only has execute permission by that owner.
            The work around is manually checking it ourselves.
            When the data typing code is fixed, please remove this check.
         */
         if( S_ISDIR( stat_buf.st_mode ) &&
	    (strcmp (file_data->logical_type, LT_DIRECTORY) == 0))
         {
           if( strcmp( file_name, ".." ) != 0
               && strcmp( file_name, "." ) != 0 )
           {
             char * fullPathName;

             if( file_data->link )
               fullPathName = file_data->link;
             else
               fullPathName = full_file_name;

             if( access( fullPathName, R_OK ) != 0 )
             {
               XtFree( file_data->logical_type );
               file_data->logical_type = XtNewString( LT_FOLDER_LOCK );
             }
             else if( access( fullPathName, W_OK ) != 0 )
             {
               XtFree( file_data->logical_type );
               file_data->logical_type = XtNewString( LT_NON_WRITABLE_FOLDER );
             }
           }
         }
#endif
      }

      if(DtActionExists(file_data->logical_type))
      {
         file_data->action_name = (char *)DtActionLabel(file_data->file_name);
      }
      else
      {
         char *ptr = DtDtsDataTypeToAttributeValue(file_data->logical_type,
                                                   DtDTS_DA_LABEL,
                                                   NULL);
         if (ptr)
         {
            file_data->action_name = XtNewString(ptr);
            DtDtsFreeAttributeValue(ptr);
         }
      }
   }
   else
   {
      /* couldn't stat the file */
      file_data->errnum = stat_errno;
      memset(&file_data->stat, 0, sizeof(file_data->stat));
      file_data->physical_type = DtUNKNOWN;
      file_data->is_broken = True;
      file_data->logical_type = XtNewString(DtDEFAULT_DATA_FT_NAME);
   }

   return file_data;
}


/*--------------------------------------------------------------------
 *  ReadFileData2
 *    Given a path name, return FileData for a file.
 *------------------------------------------------------------------*/

int
ReadFileData2(
	FileData2 *file_data2,
	char *full_directory_name,
	char *file_name,
        Boolean IsToolBox)
{
   char full_file_name[MAX_PATH];
   char link_file_name[MAX_PATH];
   char link_path[MAX_PATH];
   char file_name_buf[MAXPATHLEN];
   char action_name_buf[MAXPATHLEN];
   char logical_type_buf[MAXPATHLEN];
   char link_buf[MAXPATHLEN];
   char final_link_buf[MAXPATHLEN];
   int link_count;
   char ** link_list;
   int link_len;
   char * end;
   char * ptr;
   Boolean recursive_link_found;
   struct stat stat_buf;
   int stat_result;
   int stat_errno;
   int i;

   /*  Allocate a new file structure.  */

   /* get the full name of the file */
   strcpy (full_file_name, full_directory_name);

   if (file_name)
   {
      /* append file name to the directory */
      if (strcmp(full_directory_name,"/") != 0)
         strcat (full_file_name, "/");
      strcat (full_file_name, file_name);
   }
   else
   {
      /* no file name passed: use last component of directory */
      file_name = strrchr(full_file_name, '/');
      if (file_name > full_file_name)
         file_name++;
      else
         file_name = NULL;
   }

   /* Follow symbolic links to their ultimate destination */
   link_count = 0;
   link_list = NULL;
   recursive_link_found = False;
   strcpy(link_file_name, full_file_name);

   stat_result = lstat (link_file_name, &stat_buf);
   if ((stat_buf.st_mode & S_IFMT) == S_IFLNK)
   {
     while ((link_len = readlink(link_file_name, link_path, MAX_PATH - 1)) > 0)
     {
       link_path[link_len] = 0;
       link_list = (char **)XtRealloc((char *)link_list, sizeof(char *) *
                                      (link_count + 2));

       /* Force the link to be an absolute path, if necessary */
       if (link_path[0] != '/')
       {
         /* Relative paths are relative to the current directory */
         end = strrchr(link_file_name, '/') + 1;
         *end = '\0';
         strcat(link_file_name, link_path);
       }
       else
         strcpy(link_file_name, link_path);

       /* Check for a recursive loop; abort if found */
       for (i = 0; i < link_count; i++)
       {
         if (strcmp(link_file_name, link_list[i]) == 0)
         {
           /* Back up to last non-recursive portion */
           strcpy(link_file_name, link_list[link_count - 1]);
           recursive_link_found = True;
           break;
         }
       }

       if (recursive_link_found)
         break;

       link_list[link_count++] = XtNewString(link_file_name);
       link_list[link_count] = NULL;
     }

     if ((stat_result = stat (link_file_name, &stat_buf)) != 0)
     {
      /* probably a broken link; try lstat */
       stat_result = lstat (full_file_name, &stat_buf);
       strcpy(link_file_name, full_file_name);
     }
   }
   stat_errno = errno;

   /* fill in the FileData2 structure with the information we found */
   file_data2->next = NULL;
   strcpy(file_name_buf, (file_name ? file_name : "."));
   logical_type_buf[0] = NILL;
   file_data2->is_subdir = False;
   action_name_buf[0] = NILL;

   if (link_list)
   {
      strcpy(link_buf, link_list[0]);
      strcpy(final_link_buf, link_list[link_count - 1]);
      for (i = 0; i < link_count; i++) {
         XtFree(link_list[i]);
      }
      XtFree((char *)link_list);
   } else {
      final_link_buf[0] = NILL;
      link_buf[0] = NILL;
   }

   if (stat_result == 0)
   {
      file_data2->errnum = 0;
      file_data2->stat = stat_buf;

      /*  Find and set the physical type of the file  */

      if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
      {
         file_data2->physical_type = DtDIRECTORY;
         if (file_name == NULL ||
             strcmp(file_name, ".") != 0 && strcmp(file_name, "..") != 0)
         {
            file_data2->is_subdir = True;
         }
      }
      else if ((stat_buf.st_mode & S_IFMT) == S_IFREG)
      {
         if ((stat_buf.st_mode & S_IXUSR) ||
             (stat_buf.st_mode & S_IXGRP) ||
             (stat_buf.st_mode & S_IXOTH))
            file_data2->physical_type = DtEXECUTABLE;
         else
            file_data2->physical_type = DtDATA;
      }
      else
         file_data2->physical_type = DtDATA;

      /*  Find and set the logical type of the file  */
      if ((stat_buf.st_mode & S_IFMT) == S_IFLNK)
      {
         file_data2->is_broken = True;
         if (recursive_link_found)
            strcpy(logical_type_buf, LT_RECURSIVE_LINK);
         else
            strcpy(logical_type_buf, LT_BROKEN_LINK);
      }
      else
      {
         char *ptr;

         file_data2->is_broken = False;
         if (link_buf[0] == NILL)
            ptr = (char *) DtDtsDataToDataType(  full_file_name,
                                                 NULL, 0, &stat_buf,
                                                 NULL, NULL,
                                                 NULL);
         else
            ptr = (char *) DtDtsDataToDataType(  link_buf,
                                                 NULL, 0, &stat_buf,
                                                 final_link_buf, NULL,
                                                 NULL);

#if defined( DATATYPE_IS_FIXED )
         strcpy(logical_type_buf, ptr);
         free(ptr);
#else
         /* The problem here is there isn't a way for user to mask
            only the OWNER READ bit in the MODE field of dtfile.dt file.
            If the MODE field set to d&!r Then all READ permission
            (S_IRUSR, S_IRGRP and S_IROTH)
            bits has to be off in order for the above data typing to work.
            Also data typing is unable to detect when the directory is not
            the owners and only has execute permission by that owner.
            The work around is manually checking it ourselves.
            When the data typing code is fixed, please remove this check.
         */
         if( !IsToolBox && S_ISDIR( stat_buf.st_mode ) &&
	    (strcmp (ptr, LT_DIRECTORY) == 0))
         {
           if( strcmp( file_name, ".." ) != 0
               && strcmp( file_name, "." ) != 0 )
           {
             char * fullPathName;

             if( link_buf[0] == NILL )
               fullPathName = full_file_name;
             else
               fullPathName = link_buf;

             if( access( fullPathName, R_OK ) != 0 )
             {
               free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
               strcpy( logical_type_buf, LT_FOLDER_LOCK );
             }
             else if( access( fullPathName, W_OK ) != 0 )
             {
               free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
               strcpy( logical_type_buf, LT_NON_WRITABLE_FOLDER );
             }
             else
             {
               strcpy( logical_type_buf, ptr );
               free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
             }
           }
           else
           {
             strcpy( logical_type_buf, ptr );
             free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
           }
         }
         else
         {
           strcpy( logical_type_buf, ptr );
           free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
         }
#endif
      }

      if( DtActionExists(logical_type_buf) )
      {
        char *ptr = (char *)DtActionLabel(file_name_buf);
        strcpy(action_name_buf, ptr);
        free(ptr);
      }
      else
      {
        char *ptr = DtDtsDataTypeToAttributeValue(logical_type_buf,
                                                  DtDTS_DA_LABEL,
                                                  NULL);
        if (ptr)
        {
          strcpy(action_name_buf, ptr);
          DtDtsFreeAttributeValue(ptr);
        }
      }
   }
   else
   {
      /* couldn't stat the file */
      file_data2->errnum = stat_errno;
      memset(&file_data2->stat, 0, sizeof(file_data2->stat));
      file_data2->physical_type = DtUNKNOWN;
      file_data2->is_broken = True;
      strcpy(logical_type_buf, DtDEFAULT_DATA_FT_NAME);
   }

	strcpy(file_data2->text, file_name_buf);
	file_data2->file_name = strlen(file_name_buf);

	strcat(file_data2->text, action_name_buf);
	file_data2->action_name = strlen(action_name_buf);

	strcat(file_data2->text, logical_type_buf);
	file_data2->logical_type = strlen(logical_type_buf);

	strcat(file_data2->text, link_buf);
	file_data2->link = strlen(link_buf);

	strcat(file_data2->text, final_link_buf);
	file_data2->final_link = strlen(final_link_buf);

	i = sizeof(*file_data2) - sizeof(file_data2->text)
		+ file_data2->file_name + file_data2->action_name
		+ file_data2->logical_type + file_data2->link
		+ file_data2->final_link;

	i = (i + sizeof(char *) - 1) & ~(sizeof(char *) - 1);

	/*
	 * This data marshalling operation relies on char[BUFSIZ]
	 * being large enough for all the text pieces.  However,
	 * BUFSIZ has nothing to do with the above operations so
	 * we'll do this assert for now.
	 */
	assert( (i <= sizeof(FileData2)) );

	return i;
}

/*--------------------------------------------------------------------
 *  GetTTPath
 *      Resolves the links in the path.
 *------------------------------------------------------------------*/

char *
GetTTPath(char *path)
{
   Tt_message dummy_msg;
   char *tmp, *tt_path;
   Tt_status tt_status;

   dummy_msg = tt_message_create();
   tt_status = tt_message_file_set(dummy_msg, path);
   tmp = tt_message_file(dummy_msg);

   tt_path = XtNewString(tmp);

   tt_free(tmp);
   tt_message_destroy(dummy_msg);

   return tt_path;
}


static int
ReadDirectoryProcess(
        int pipe_fd,
        Directory *directory,
	ActivityStatus activity)
{
#ifdef DT_PERFORMANCE
   struct timeval update_time_s;
   struct timeval update_time_f;
#endif
   char *host_name = directory->host_name;
   char *directory_name = directory->directory_name;
   struct stat stat_buf;
   long modify_time;
   int path_count;
   char **path_logical_types;
   char *full_directory_name;
   char *tt_path;
   DIR *dirp;
   struct dirent * dp;
   Boolean inDtDir;
   FileData *file_data;
   FileData2 file_data2;
   Boolean done;
   Boolean update_due;
   short file_data_count = 0;
   int i;
   char * ptr;
   char * namePtr;
   char file_name[MAX_PATH];
   int position_count;
   FILE * fptr;
   int x, y, stacking_order;
   short pipe_msg;
   int rc;
   char file_data_buffer[FILEDATABUF * sizeof(FileData2)];
   char *file_data_buf_ptr = file_data_buffer;
   char *file_data_count_ptr;
   struct timeval time1, time2;
   long diff;
   char *ptrOrig;

   DPRINTF(("ReadDirectoryProcess(%d, \"%s\", \"%s\")\n",
            pipe_fd, host_name, directory_name));

   /*
    * Get the logical data type of all components of the path;
    * We need only the last path component for (1) the tree root icon,
    * and (2) the current directory icon; the other path components
    * are needed for the iconic path icons.
    */
   path_count = 0;
   path_logical_types = NULL;

   /* Don't muck with original string */
   ptrOrig = ptr = XtNewString(directory_name);

   for (;;)
   {
      Tt_status tt_status;

      if (ptr != NULL)
         *ptr = '\0';

      if (ptrOrig[0] == '\0')
         namePtr = "/";
      else
         namePtr = ptrOrig;

      /* get logical type of next path component */
      full_directory_name = ResolveLocalPathName( host_name,
                                                  namePtr,
                                                  NULL,
                                                  home_host_name,
                                                  &tt_status );
      if( TT_OK != tt_status )
        break;

      DtEliminateDots (full_directory_name);
      path_logical_types = (char **) XtRealloc((char *)path_logical_types,
                                               (path_count + 1)*sizeof(char *));
      path_logical_types[path_count] =
         (char *) DtDtsDataToDataType(full_directory_name, NULL, 0, NULL, NULL,
                                      NULL, NULL);
#if defined( DATATYPE_IS_FIXED )
#else
      {
        if( stat( full_directory_name, &stat_buf ) == 0 )
        {
          if( S_ISDIR( stat_buf.st_mode ) &&
	    (strcmp (path_logical_types[path_count], LT_DIRECTORY) == 0))
          {
            if( access( full_directory_name, R_OK ) != 0 )
            {
              XtFree( path_logical_types[path_count] );
              path_logical_types[path_count] = XtNewString( LT_FOLDER_LOCK );
            }
            else if( access( full_directory_name, W_OK ) != 0 )
            {
              XtFree( path_logical_types[path_count] );
              path_logical_types[path_count] = XtNewString( LT_NON_WRITABLE_FOLDER );
            }
          }
        }
      }
#endif
      DPRINTF2(("ReadDirectoryProcess: path '%s', fullname '%s', type %s\n",
                namePtr, full_directory_name, path_logical_types[path_count]));

      XtFree( full_directory_name );
      path_count++;

      if (ptr == NULL)
        break;

      /* restore '/' */
      *ptr = '/';

      /* find next component */
      if (strcmp(ptr, "/") == 0)
         break;
      ptr = DtStrchr(ptr + 1, '/');
   }
   XtFree(ptrOrig);

   /* get the full name of the current directory */
   {
     Tt_status tt_status;
     full_directory_name = ResolveLocalPathName( host_name,
                                                 directory_name,
                                                 NULL,
                                                 home_host_name,
                                                 &tt_status );
     /* It's ok not to check for tt_status.
        The code below will handle it properly.
     */
   }

   /* send the path_logical_types back through the pipe */
   pipe_msg = PIPEMSG_PATH_LOGICAL_TYPES;
   DPRINTF(("ReadDirectoryProcess: sending %d path_logical_types\n",
            path_count));
   write(pipe_fd, &pipe_msg, sizeof(short));
   write(pipe_fd, &path_count, sizeof(int));
   for(i = 0; i < path_count; i++)
   {
     PipeWriteString(pipe_fd, path_logical_types[i]);
     XtFree((char *) path_logical_types[i]);
   }
   XtFree((char *) path_logical_types);

   /* send the tt_path */
   tt_path = GetTTPath(full_directory_name);
   PipeWriteString(pipe_fd, tt_path);
   XtFree(tt_path);

   /*
    * Stat the directory to get its timestamp.
    * Also check if we have read and execute/search permisssion.
    */
   if (CheckAccess(full_directory_name, R_OK | X_OK) != 0 ||
       stat(full_directory_name, &stat_buf) != 0)
   {
      /* send an error code back through the pipe */
      pipe_msg = PIPEMSG_ERROR;
      rc = errno;
      modify_time = 0;
      DPRINTF(("ReadDirectoryProcess: sending errno %d (stat failed)\n", rc));
      write(pipe_fd, &pipe_msg, sizeof(short));
      write(pipe_fd, &rc, sizeof(int));
      write(pipe_fd, &modify_time, sizeof(long));
      return 1;
   }

   modify_time = stat_buf.st_mtime;

   /*
    * We never want to display the '~/.dt/Desktop' directory, so when we
    * are working with the .dt directory, add a special check for a
    * directory named 'Desktop'.
    */
   if ((ptr = strrchr(full_directory_name, '/')) &&
       (strcmp(ptr, "/.dt") == 0))
      inDtDir = True;
   else
      inDtDir = False;

   /* try to open the directory */
   dirp = opendir (full_directory_name);
   if (dirp == NULL)
   {
      /* send an error code back through the pipe */
      pipe_msg = PIPEMSG_ERROR;
      rc = errno;
      DPRINTF(("ReadDirectoryProcess: sending errno %d (opendir failed)\n",
               rc));
      write(pipe_fd, &pipe_msg, sizeof(short));
      write(pipe_fd, &rc, sizeof(int));
      write(pipe_fd, &modify_time, sizeof(long));
      XtFree( full_directory_name );
      return 1;
   }

   /*  Loop through the directory entries and update the file list  */

#ifdef DT_PERFORMANCE
   printf("  begin reading directory: %s\n", full_directory_name);
   gettimeofday(&update_time_s, NULL);
#endif

	/*
	 *	FILEDATA3 creates a buffer of static FileData2 structures,
	 *	then sends FILEDATABUF worth of FileData2 structs to the parent.
	 *	FILEDATABUF appears to work the best when set to 50.
     *
     *  We send data to the parent at least every half seconds, even if
     *  less than FILEDATABUF worth of FileData2 structs have been read.
     *  This is to ensure that the file count in the status line gets
     *  updated every half seconds, even if the file system is slow.
	 */

   /* initialize pointer into file data buffer */
#  define PIPEMSG_HDR_LEN (2*sizeof(short) + sizeof(int))
   file_data_buf_ptr = file_data_buffer + PIPEMSG_HDR_LEN;

   /* get current time */
   gettimeofday(&time1, NULL);

   done = False;
   do
   {
     int len = 0;

     if ((dp = readdir (dirp)) != NULL)
     {
       Boolean IsToolBox;
       /* if Desktop skip */
       if (inDtDir && (strcmp(dp->d_name, "Desktop") == 0))
         continue;

       /* get the info */
       if(directory->directoryView && directory->directoryView->file_mgr_data)
	 IsToolBox = directory->directoryView->file_mgr_data->toolbox;
       else
	 IsToolBox = False;
       len = ReadFileData2((FileData2 *)file_data_buf_ptr,
                           full_directory_name, dp->d_name,IsToolBox);
       file_data_buf_ptr += len;
       file_data_count++;
     }
     else
       done = True;

     /* check if 0.4 seconds have passed since the last status line update */
     gettimeofday(&time2, NULL);
     diff = 1024*(time2.tv_sec - time1.tv_sec);
     diff += time2.tv_usec/1024;
     diff -= time1.tv_usec/1024;
     update_due = (diff >= 400);

     /* check if we need to send the buffered data now */
     if (file_data_count == FILEDATABUF ||
         file_data_count > 0 && (done || update_due))
     {
       if (update_due)
         file_data_count |= 0x8000;
       len = file_data_buf_ptr - (file_data_buffer + PIPEMSG_HDR_LEN);

       /* now send the file data through the pipe */
       *(short *)file_data_buffer = PIPEMSG_FILEDATA3;
       *(short *)(file_data_buffer + sizeof(short)) = file_data_count;
       *(int *)(file_data_buffer + 2*sizeof(short)) = len;
       write(pipe_fd, file_data_buffer,
             file_data_buf_ptr - file_data_buffer);

       /* reset pointer to file data buffer, file count and time stamp */
       file_data_buf_ptr = file_data_buffer + PIPEMSG_HDR_LEN;
       file_data_count = 0;
       if (update_due)
         time1 = time2;
     }
   } while (!done);

#ifdef DT_PERFORMANCE
   gettimeofday(&update_time_f, NULL);
   if (update_time_s.tv_usec > update_time_f.tv_usec) {
      update_time_f.tv_usec += 1000000;
      update_time_f.tv_sec--;
   }
   printf("    finished reading: %s, time: %ld.%ld\n\n", full_directory_name, update_time_f.tv_sec - update_time_s.tv_sec, update_time_f.tv_usec - update_time_s.tv_usec);
#endif


   /* load position info, if available */

   /* construct full name of the position info file */
   if (strcmp(full_directory_name,"/") != 0)
     sprintf( file_name, "%s/%s", full_directory_name, positionFileName );
   else
     sprintf( file_name, "%s%s", full_directory_name, positionFileName );

   /* read the count from the position info file */
   position_count = 0;
   if ((fptr = fopen(file_name, "r")) != NULL)
   {
     PositionInfo * position_info = NULL;
     fscanf(fptr, "%d\n", &position_count);

     if (position_count > 0)
     {
       /* allocate position info array */
       position_info = (PositionInfo *)
         XtMalloc(position_count * sizeof(PositionInfo));

       /* read the position info from the file */
       i = 0;
       while (i < position_count)
       {
         if( fgets( file_name, MAX_PATH, fptr ) != NULL &&
             fscanf(fptr, "%d %d %d\n", &x, &y, &stacking_order ) == 3  )
         {
           int len = strlen(file_name);
           file_name[len-1] = 0x0;
           position_info[i].name = XtNewString(file_name);
           position_info[i].x = x,
           position_info[i].y = y,
           position_info[i].stacking_order = stacking_order;
           i++;
         }
         else
           break;
       }
       position_count = i;
     }

     fclose(fptr);

     /* send the position info back through the pipe */
     pipe_msg = PIPEMSG_POSITION_INFO;
     DPRINTF(("ReadDirectoryProcess: sending %d position_info\n",
              position_count));
     write(pipe_fd, &pipe_msg, sizeof(short));
     write(pipe_fd, &position_count, sizeof(int));
     for (i = 0; i < position_count; i++)
     {
       PipeWriteString( pipe_fd, position_info[i].name );
       XtFree( position_info[i].name );
       write( pipe_fd, &(position_info[i].x), sizeof(Position));
       write( pipe_fd, &(position_info[i].y), sizeof(Position));
       write( pipe_fd, &(position_info[i].stacking_order), sizeof(int));

     }
     XtFree( (char *)position_info );
   }

   XtFree(full_directory_name);
   closedir (dirp);

   /* send a 'done' msg through the pipe */
   DPRINTF(("ReadDirectoryProcess: sending DONE\n"));
   pipe_msg = PIPEMSG_DONE;
   write(pipe_fd, &pipe_msg, sizeof(short));
   write(pipe_fd, &modify_time, sizeof(long));
   return 0;
}


/*--------------------------------------------------------------------
 *  UpdateAllProcess
 *    Main routine of the background process that checks the directory
 *    for new files or files that have disapeared.
 *------------------------------------------------------------------*/

static int
UpdateAllProcess(
                 int pipe_fd,
                 Directory *directory,
                 ActivityStatus activity)
{
   char *host_name = directory->host_name;
   char *directory_name = directory->directory_name;
   char *full_directory_name;
   struct stat stat_buf;
   long modify_time;
   DIR *dirp;
   struct dirent * dp;
   Boolean inDtDir;
   FileData *file_data;
   FileData *old_data, **old_pp;
   FileData2 file_data2;
   char *ptr;
   short pipe_msg;
   int n, i, rc=0;
   Tt_status tt_status;
   char **path_logical_types;
   int path_count;
   char *ptrOrig;

   DPRINTF(("UpdateAllProcess(%d, \"%s\", \"%s\")\n",
            pipe_fd, host_name, directory_name));

   /* if modified list contains "." or ".." arrange for the path_logical_types
      to get updated */
   if(directory->modified_count == 0 &&
      FindDirectory(directory->host_name, directory_name))
   {
       rc = 1;
   }
   else
   {
       for (i = 0; i < directory->modified_count; i++)
       {
           if (strcmp(directory->modified_list[i],".") == 0 ||
               strcmp(directory->modified_list[i],"..") == 0 )
           {
               rc = 1;
               break;
           }
       }
   }
   if(rc)
   {
      char *tt_path;

      path_count = 0;
      path_logical_types = NULL;
      ptrOrig = ptr = XtNewString(directory_name);
      for (;;)
      {
         Tt_status tt_status;
         char *namePtr;

         if (ptr != NULL)
            *ptr = '\0';

         if (ptrOrig[0] == '\0')
            namePtr = "/";
         else
            namePtr = ptrOrig;

         /* get logical type of next path component */
         full_directory_name = ResolveLocalPathName( host_name,
                                                  namePtr,
                                                  NULL,
                                                  home_host_name,
                                                  &tt_status );
         if( TT_OK != tt_status )
           break;

         DtEliminateDots (full_directory_name);
         path_logical_types = (char **) XtRealloc((char *)path_logical_types,
                                               (path_count + 1)*sizeof(char *));
         path_logical_types[path_count] =
           (char *)DtDtsDataToDataType(full_directory_name, NULL, 0, NULL, NULL,
                                      NULL, NULL);
#if defined( DATATYPE_IS_FIXED )
#else
      {
        if( stat( full_directory_name, &stat_buf ) == 0 )
        {
          if( S_ISDIR( stat_buf.st_mode ) &&
	    (strcmp (path_logical_types[path_count], LT_DIRECTORY) == 0))
          {
            if( access( full_directory_name, R_OK ) != 0 )
            {
              XtFree( path_logical_types[path_count] );
              path_logical_types[path_count] = XtNewString( LT_FOLDER_LOCK );
            }
            else if( access( full_directory_name, W_OK ) != 0 )
            {
              XtFree( path_logical_types[path_count] );
              path_logical_types[path_count] = XtNewString( LT_NON_WRITABLE_FOLDER );
            }
          }
        }
      }
#endif

         DPRINTF2(("ReadDirectoryProcess: path '%s', fullname '%s', type %s\n",
                namePtr, full_directory_name, path_logical_types[path_count]));

         XtFree(full_directory_name);
         path_count++;

         if (ptr == NULL)
           break;

         /* restore '/' */
         *ptr = '/';

         /* find next component */
         if (strcmp(ptr, "/") == 0)
            break;
         ptr = DtStrchr(ptr + 1, '/');
      }
      XtFree(ptrOrig);

      /* get the full name of the current directory */
      full_directory_name = ResolveLocalPathName( host_name,
                                                 directory_name,
                                                 NULL,
                                                 home_host_name,
                                                 &tt_status );
      /* It's ok not to check for tt_status.
         The code below will handle it properly.
      */

      /* send the path_logical_types back through the pipe */
      pipe_msg = PIPEMSG_PATH_LOGICAL_TYPES;
      DPRINTF(("ReadDirectoryProcess: sending %d path_logical_types\n",
            path_count));
      write(pipe_fd, &pipe_msg, sizeof(short));
      write(pipe_fd, &path_count, sizeof(int));
      for(i = 0; i < path_count; i++)
      {
        PipeWriteString(pipe_fd, path_logical_types[i]);
        XtFree((char *) path_logical_types[i]);
      }
      XtFree((char *) path_logical_types);

      /* send the tt_path */
      tt_path = GetTTPath(full_directory_name);
      PipeWriteString(pipe_fd, tt_path);
      XtFree(tt_path);
   }
   else
   {
        full_directory_name = ResolveLocalPathName( host_name,
                                                 directory_name,
                                                 NULL,
                                                 home_host_name,
                                                 &tt_status );
   }

   if( TT_OK != tt_status )
   {
      pipe_msg = PIPEMSG_ERROR;
      rc = -1;
      modify_time = 0;
      DPRINTF(("UpdateAllProcess: sending errno %d (tooltalk failed)\n", rc));
      write(pipe_fd, &pipe_msg, sizeof(short));
      write(pipe_fd, &rc, sizeof(int));
      write(pipe_fd, &modify_time, sizeof(long));
      return 1;
   }
   (void) DtEliminateDots (full_directory_name);

   /*
    * Stat the directory to get its timestamp.
    * Also check if we still have read and execute/search permisssion.
    */
   if (CheckAccess(full_directory_name, R_OK | X_OK) != 0 ||
       stat(full_directory_name, &stat_buf) != 0)
   {
      /* send an error code back through the pipe */
      pipe_msg = PIPEMSG_ERROR;
      rc = errno;
      modify_time = 0;
      DPRINTF(("UpdateAllProcess: sending errno %d (stat failed)\n", rc));
      write(pipe_fd, &pipe_msg, sizeof(short));
      write(pipe_fd, &rc, sizeof(int));
      write(pipe_fd, &modify_time, sizeof(long));
      XtFree( full_directory_name );
      return 1;
   }
   modify_time = stat_buf.st_mtime;

   /* check if we are in the .dt directory */
   if ((ptr = strrchr(full_directory_name, '/')) &&
       (strcmp(ptr, "/.dt") == 0))
   {
      inDtDir = True;
   }
   else
      inDtDir = False;

   /* try to open the directory */
   dirp = opendir (full_directory_name);
   if (dirp == NULL)
   {
      /* send an error code back through the pipe */
      pipe_msg = PIPEMSG_ERROR;
      rc = errno;
      DPRINTF(("UpdateAllProcess: sending errno %d (opendir failed)\n", rc));
      write(pipe_fd, &pipe_msg, sizeof(short));
      write(pipe_fd, &rc, sizeof(int));
      write(pipe_fd, &modify_time, sizeof(long));
      XtFree( full_directory_name );
      return 1;
   }

   /*  Loop through the directory entries and update the file list  */
   while (dp = readdir (dirp))
   {
      /* if Desktop skip */
      if (inDtDir && (strcmp(dp->d_name, "Desktop") == 0))
         continue;

      /* check if we already know this file */
      for (old_pp = &directory->file_data;
           (old_data = *old_pp) != NULL;
           old_pp = &old_data->next)
      {
         if (strcmp(dp->d_name, old_data->file_name) == 0)
         {
             char *tname;
             struct stat sbuf;
 
             /* check modified times */
             tname = XtMalloc(strlen(full_directory_name)+strlen(dp->d_name)+2);
             sprintf(tname,"%s/%s",full_directory_name,dp->d_name);
             if((lstat(tname,&sbuf)>=0)&&sbuf.st_mtime!=old_data->stat.st_mtime)
                 old_data = NULL;
             XtFree(tname);
             if(old_data == NULL)
                 break;

            /* check if this file appears on the modified list */
            for (i = 0; i < directory->modified_count; i++)
               if (strcmp(dp->d_name, directory->modified_list[i]) == 0)
               {
                 /*
                  * This file is on the modified list.
                  * Pretend we didn't find it to force a refresh of this file.
                  */
                 old_data = NULL;
                 break;
               }
            break;
         }
      }

      /* If this is a known file, remember we saw it and continue. */
      if (old_data != NULL)
      {
         /*
          * We remove the file from the old file list; thus, when we are done,
          * the files on the old file list will be onew that no longer exist.
          */
         *old_pp = old_data->next;
         continue;
      }

      /* this is a new file */
      DPRINTF(("UpdateAllProcess: found new file \"%s\"\n", dp->d_name));

      /* Fix for incorrect icons in App Manager */
      {
	Boolean IsToolBox;

        if(directory->directoryView && directory->directoryView->file_mgr_data)
          IsToolBox = directory->directoryView->file_mgr_data->toolbox;
        else
          IsToolBox = False;
        ReadFileData2(&file_data2, full_directory_name, dp->d_name,IsToolBox);
      }
      file_data = FileData2toFileData(&file_data2, &n);

      /* now send the file data through the pipe */
      pipe_msg = PIPEMSG_FILEDATA;
      write(pipe_fd, &pipe_msg, sizeof(short));
      PipeWriteFileData(pipe_fd, file_data);

      FreeFileData(file_data, True);
   }

   /* all files left in the old file list no longer exist */
   for (old_data = directory->file_data;
        old_data;
        old_data = old_data->next)
   {
      DPRINTF(("UpdateAllProcess: file gone \"%s\"\n", old_data->file_name));
      old_data->errnum = ENOENT;
      pipe_msg = PIPEMSG_FILEDATA;
      write(pipe_fd, &pipe_msg, sizeof(short));
      PipeWriteFileData(pipe_fd, old_data);
   }

   /* free storage */
   XtFree(full_directory_name);

   /* send a 'done' msg through the pipe */
   DPRINTF(("UpdateAllProcess: sending DONE\n"));
   pipe_msg = PIPEMSG_DONE;
   write(pipe_fd, &pipe_msg, sizeof(short));
   write(pipe_fd, &modify_time, sizeof(long));

   if (dirp)
       closedir(dirp);
   return 0;
}


/*--------------------------------------------------------------------
 *  UpdateSomeProcess
 *    Main routine of the background process that updates a selected
 *    list of directory entries.
 *------------------------------------------------------------------*/

static int
UpdateSomeProcess(
        int pipe_fd,
        Directory *directory,
	ActivityStatus activity)
{
   char *host_name = directory->host_name;
   char *directory_name = directory->directory_name;
   char *full_directory_name;
   struct stat stat_buf;
   long modify_time;
   FileData *file_data;
   FileData2 file_data2;
   short pipe_msg;
   int i;
   int rc;
   Boolean IsToolBox;

   DPRINTF(("UpdateSomeProcess(%d, \"%s\", \"%s\")\n",
            pipe_fd, host_name, directory_name));


   /* get the full name of the current directory */
   {
     Tt_status tt_status;

     full_directory_name = ResolveLocalPathName( host_name,
                                                 directory_name,
                                                 NULL,
                                                 home_host_name,
                                                 &tt_status );
     if( TT_OK != tt_status )
     {
       pipe_msg = PIPEMSG_ERROR;
       rc = -1;
       modify_time = 0;
       DPRINTF(("UpdateSomeProcess: sending errno %d (stat failed)\n", rc));
       write(pipe_fd, &pipe_msg, sizeof(short));
       write(pipe_fd, &rc, sizeof(int));
       write(pipe_fd, &modify_time, sizeof(long));
       return 1;
     }
   }
   (void) DtEliminateDots (full_directory_name);

   /* stat the directory to get the timestamp */
   if (stat(full_directory_name, &stat_buf) < 0 ||
       ! (stat_buf.st_mode & S_IXUSR) )
   {
      /* send an error code back through the pipe */
      pipe_msg = PIPEMSG_ERROR;
      rc = errno;
      modify_time = 0;
      DPRINTF(("UpdateSomeProcess: sending errno %d (stat failed)\n", rc));
      write(pipe_fd, &pipe_msg, sizeof(short));
      write(pipe_fd, &rc, sizeof(int));
      write(pipe_fd, &modify_time, sizeof(long));
      XtFree( full_directory_name );
      return 1;
   }
   modify_time = stat_buf.st_mtime;

   /*  Loop through the list of modified files  */
   for (i = 0; i < directory->modified_count; i++)
   {
      /* get the info */

      if(directory->directoryView && directory->directoryView->file_mgr_data)
        IsToolBox = directory->directoryView->file_mgr_data->toolbox;
      else
        IsToolBox = False;

      ReadFileData2(&file_data2, full_directory_name,
                               directory->modified_list[i],IsToolBox);

      /* now send the file data through the pipe */
      pipe_msg = PIPEMSG_FILEDATA2;
      write(pipe_fd, &pipe_msg, sizeof(short));
      write(pipe_fd, &file_data2, sizeof(FileData2));
   }

   XtFree(full_directory_name);

   /* send a 'done' msg through the pipe */
   DPRINTF(("UpdateSomeProcess: sending DONE\n"));
   pipe_msg = PIPEMSG_DONE;
   write(pipe_fd, &pipe_msg, sizeof(short));
   write(pipe_fd, &modify_time, sizeof(long));
   return 0;
}


/*--------------------------------------------------------------------
 *  ReaddirPipeCallback
 *	Callback routine that reads directory entry information sent
 *	through the pipe from the background process.
 *------------------------------------------------------------------*/

static void
ReaddirPipeCallback(
   XtPointer client_data,
   int *fd,
   XtInputId *id)
{
   static int whined_fd = 0;
   PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
   Directory *directory = pipe_data->directory;
   FileMgrData *file_mgr_data;
   FileMgrRec *file_mgr_rec;
   ActivityStatus activity;
   Boolean done;
   short msg;
   Boolean update_due;
   FileData *new_data = NULL, **new_nextp;
   FileData *old_data, **old_nextp;
   char *ptr;
   char *err_msg;
   int i, n;
   int rc;
   long modify_time = 0;
   char dirname[MAX_PATH];
   short file_data_count;


   /* verify that the directory still exists */
   if (DirectoryGone(directory))
   {
      /*
       * The directory is no longer present:
       * close the pipe and kill the reader.
       */
      close(*fd);
      XtRemoveInput(*id);
      kill(pipe_data->child, SIGKILL);
      XtFree( client_data );
      ScheduleActivity(NULL);
      return;
   }

   /* read the next msg from the pipe */
   msg = -1;
   n = PipeRead(*fd, &msg, sizeof(short));
   activity = directory->activity;
   done = False;

   switch (msg)
   {
      case PIPEMSG_PATH_LOGICAL_TYPES:
         /* get the number of path components */
         PipeRead(*fd, &n, sizeof(int));

         /* free logical types */
         for (i = 0; i < directory->path_count; i++)
           XtFree(directory->path_logical_types[i]);

         /* allocate array of the right size */
         if (directory->path_count != n)
         {
            directory->path_count = n;
            directory->path_logical_types = (char **)
              XtRealloc((char *)directory->path_logical_types,
                        n*sizeof(char *));
         }

         /* get new logical types */
         for (i = 0; i < directory->path_count; i++)
           directory->path_logical_types[i] = PipeReadString(*fd);

         /* get the tt_path */
         XtFree(directory->tt_path_name);
         directory->tt_path_name = PipeReadString(*fd);

         /* update all views */
         for (i = 0; i < directory->numOfViews; i++)
         {
            file_mgr_data = directory->directoryView[i].file_mgr_data;
            file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
            UpdateHeaders(file_mgr_rec, file_mgr_data, True);
            XmUpdateDisplay(file_mgr_rec->file_window);
         }
         XSync(XtDisplay(toplevel), False);
         break;

          case PIPEMSG_FILEDATA:
          case PIPEMSG_FILEDATA2:
          case PIPEMSG_FILEDATA3:

         if (msg == PIPEMSG_FILEDATA)
         {
           new_data = PipeReadFileData(*fd);
         }
         else if (msg == PIPEMSG_FILEDATA2)
         {
           FileData2 file_data2;
           int n;

           n = PipeRead(*fd, &file_data2, sizeof(FileData2));
           if (n < sizeof(FileData2)) {
             perror("PipeRead");
             fprintf(stderr, "PipeReadFileData2: n = %d, expected %ld\n",
                     n, (long)sizeof(FileData2));
           }

           new_data = FileData2toFileData(&file_data2, &n);
           }

         if (msg == PIPEMSG_FILEDATA3)
         {
           int file_data_length;
           int n;
           char file_data_buffer[FILEDATABUF * sizeof(FileData2)];
           char *file_data_buf_ptr;


           n = PipeRead(*fd, &file_data_count, sizeof(short));
           n = PipeRead(*fd, &file_data_length, sizeof(int));

           if (file_data_count & 0x8000)
           {
             file_data_count &= 0x7fff;
             update_due = True;
           }
           else
             update_due = False;

           for (new_nextp = &directory->new_data;
                *new_nextp;
                new_nextp = &(*new_nextp)->next)
             ;

           n = PipeRead(*fd, file_data_buffer, file_data_length);
           file_data_buf_ptr = file_data_buffer;

           for (i = 0; i < file_data_count; i++)
           {
             /* get next FileData out of buffer */
             new_data =
               FileData2toFileData((FileData2 *)file_data_buf_ptr, &n);
             file_data_buf_ptr += n;

             /* append new_data to end of list */
             *new_nextp = new_data;
             new_data->next = NULL;
             new_nextp = &new_data->next;
           }
         }
         else
         {
           /* append new_data to end of list */
           file_data_count = 1;
           update_due = False;
           for (new_nextp = &directory->new_data;
                *new_nextp;
                new_nextp = &(*new_nextp)->next)
             ;
           *new_nextp = new_data;
           if(new_data ) {
             new_data->next = NULL;
           }
         }

         if (activity == activity_reading)
         {
           /* update file counts in all views */
           for (i = 0; i < directory->numOfViews; i++)
           {
             file_mgr_data = directory->directoryView[i].file_mgr_data;
             file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
             file_mgr_data->busy_detail += file_data_count;
             if (update_due &&
                 (file_mgr_data->busy_status == initiating_readdir ||
                  file_mgr_data->busy_status == busy_readdir) &&
                 file_mgr_data->busy_detail > 2)
             {
               if (file_mgr_data->show_status_line)
               {
                 char buf[256];
                 XmString label_string;
                 Arg args[2];

                 GetStatusMsg(file_mgr_data, buf);
                 label_string =
                   XmStringCreateLocalized(buf);
                 XtSetArg (args[0], XmNlabelString, label_string);
                 XtSetValues(file_mgr_rec->status_line, args, 1);
                 XmStringFree(label_string);
               }
               else if (file_mgr_data->show_iconic_path)
               {
                 DtUpdateIconicPath(file_mgr_rec, file_mgr_data, False);
               }
               else if (file_mgr_data->show_current_dir)
               {
                 DrawCurrentDirectory(file_mgr_rec->current_directory,
                                      file_mgr_rec, file_mgr_data);
               }
             }
           }
         }
         break;

      case PIPEMSG_POSITION_INFO:
         /* free old position info names */
         for (i = 0; i < directory->position_count; i++)
            XtFree(directory->position_info[i].name);

         /* get number of positions and realloc array, if necessary */
         PipeRead(*fd, &n, sizeof(int));
         if (directory->position_count != n)
         {
            directory->position_count = n;
            directory->position_info = (PositionInfo *) XtRealloc(
                     (char *)directory->position_info, n*sizeof(PositionInfo));
         }

         /* read new position info */
         for (i = 0; i < n; i++)
            PipeReadPositionInfo(*fd, &directory->position_info[i]);
         break;

      case PIPEMSG_DONE:
         PipeRead(*fd, &modify_time, sizeof(long));
         directory->errnum = 0;
         directory->errmsg_needed = False;
         done = True;
         break;

      case PIPEMSG_ERROR:
         PipeRead(*fd, &rc, sizeof(int));
         PipeRead(*fd, &modify_time, sizeof(long));
         if (rc != directory->errnum)
         {
            directory->errnum = rc;
            directory->errmsg_needed = True;
         }
         done = True;
         break;

      default:
	 if (whined_fd != *fd)
	 {
	     whined_fd = *fd;
             fprintf(stderr,
	       "ReaddirPipeCallback: badmsg=%d, ppid=%d pid=%d fd=%d activ'y=%d\n",
	       msg, getppid(), getpid(), *fd, activity);
	 }
         directory->errnum = -1;
         directory->errmsg_needed = False;
         done = True;
   }

   /* check if we are done */
   if (done)
   {
#ifdef DT_PERFORMANCE
   /* Aloke Gupta: As suggested by Dana Dao */
      _DtPerfChkpntMsgSend("Done  Read Directory");
#endif
      DPRINTF(("ReaddirPipeCallback: done, errno %d, time %ld\n",
               directory->errnum, modify_time));

      /* close the pipe and cancel the callback */
      close(*fd);
      XtRemoveInput(*id);

      /*
       * @@@ what if a drag is active ???
       */

      /*
       * For files in the old list that still exist in the new
       * one we need to re-use the old FileData structures.
       * Reason: the code in GetFileData relies on this to
       * preserve the position_info and selection list.
       * The following loops through the new list of files
       * and replaces entries that also exist in the old list.
       */
      for (new_nextp = &directory->new_data;
           (new_data = *new_nextp) != NULL;
           new_nextp = &new_data->next)
      {
         for (old_nextp = &directory->file_data;
              (old_data = *old_nextp) != NULL;
              old_nextp = &old_data->next)
         {
           if( strcmp(old_data->file_name, new_data->file_name) == 0 )
            {
               *old_nextp = old_data->next;

               FreeFileData(old_data, False);
               memcpy(old_data, new_data, sizeof(FileData));

               XtFree((char *)new_data);
               *new_nextp = new_data = old_data;

               break;
            }
         }
      }

      /*
       * If this was a complete re-read, we free all FileData still left
       * in the old list.  Otherwise, if this was just a partial update,
       * we append the old data that's still left to the end of the
       * new list.
       */
      if (activity == activity_reading)
      {
         /* This was a complete re-read: free all old data still left. */
         while (directory->file_data)
         {
            old_data = directory->file_data;
            directory->file_data = old_data->next;
            FreeFileData(old_data, True);
         }

         /* replace the old list by the new list */
         directory->file_data = directory->new_data;
         directory->new_data = NULL;
      }
      else
      {
         FileData * tmp_ptr = NULL;

         /* remove any directory entries that no longer exist
            in the new list.
         */
         new_nextp = &directory->new_data;
         while ((new_data = *new_nextp) != NULL)
         {
            if (new_data->errnum == ENOENT)
            {
               *new_nextp = new_data->next;
               FreeFileData(new_data, True);
            }
            else
            {
               tmp_ptr = *new_nextp;
               new_nextp = &new_data->next;
            }
         }

         /* remove any directory entries that no longer exist
            in the old list.
         */
         old_nextp = &directory->file_data;
         while ((old_data = *old_nextp) != NULL)
         {
            if (old_data->errnum == ENOENT)
            {
               *old_nextp = old_data->next;
               FreeFileData(old_data, True);
            }
            else
               old_nextp = &old_data->next;
         }

         /* Append the old list to the end of the new list
            Replace the old list pointer with the new list pointer
         */
         if( tmp_ptr != NULL )
         {
            tmp_ptr->next = directory->file_data;
            directory->file_data = directory->new_data;
            directory->new_data = NULL;
         }
      }

      /* update the file count */
      directory->file_count = 0;
      for (new_data = directory->file_data; new_data; new_data = new_data->next)
         directory->file_count++;

      /* update directory timestamp */
      if (activity == activity_reading ||
          activity == activity_update_all ||
          directory->was_up_to_date)
      {
         if (modify_time != 0)
            directory->modify_time = modify_time;
      }

      /* flush the icon cache */  /* @@@ Why? What does this do? @@@ */
      strcpy (dirname, directory->path_name);
      DtEliminateDots(dirname);
      /* We will not attempt to flush the icon cache until this */
      /* function has been fixed.                               */

      _DtFlushIconFileCache(dirname);

      /* reset busy flags */
      directory->busy[activity] = False;
      directory->activity = activity_idle;
      directory->was_up_to_date = True;
      directory->link_check_needed = False;


      /*
       * Fill dir_data field with information on the directory itself.
       * This data will be read when querying this view's top directory,
       * if the parent directory isn't already cached (tree mode)
       */
      for (new_data = directory->file_data;
           new_data != NULL;
           new_data = new_data->next)
        if (strcmp(new_data->file_name, ".") == 0)
        {

	    /*
	     * Found current directory information, now we make
	     * dir_data info from "." info
	     */

	    /* If we already have allocated space for dir_data free it */
	    if ( directory->dir_data != NULL )
	      FreeFileData(directory->dir_data, True);

	    directory->dir_data = (FileData *)XtMalloc(sizeof(FileData));

	    memcpy(directory->dir_data, new_data, sizeof(FileData));

	    /*
	     * Doctor up some of the information fields so that this doesn't
	     * seem to be a "." entry
	     */
	    directory->dir_data->next = NULL;
	    directory->dir_data->file_name =
	                         XtNewString(DName(directory->directory_name));
	    directory->dir_data->action_name = NULL;
	    if (directory->path_count > 0)
	    {
	      directory->dir_data->logical_type = XtNewString(
	             directory->path_logical_types[directory->path_count - 1]);
	    }
	    else
	      directory->dir_data->logical_type = NULL;
	    directory->dir_data->link = NULL;
	    directory->dir_data->final_link = NULL;
	    directory->dir_data->is_subdir = True;

	    break;
        }

      /* cause all views on this directory to be redrawn */
      for (i = 0; i < directory->numOfViews; i++)
      {
         file_mgr_data = directory->directoryView[i].file_mgr_data;
         file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
         FileMgrRedisplayFiles(file_mgr_rec, file_mgr_data, False);
 	 if(file_mgr_data->desktop_file)
 	 {
             SelectDesktopFile(file_mgr_data);
             XtFree(file_mgr_data->desktop_file);
             file_mgr_data->desktop_file = NULL;
         }
      }
      XtFree(client_data);

      /* schedule the next background activity */
      ScheduleActivity(directory);
   }
}


/*--------------------------------------------------------------------
 *  ReadDirectoryFiles
 *    This routine is called to read a directory if the directory
 *    wasn't found in the cached, or if the directory has to be
 *    re-read because it changed.  This routine schedules a background
 *    process to be started that will do the actual work.
 *------------------------------------------------------------------*/

static void
ReadDirectoryFiles(
        Widget w,
        Directory *directory)
{
   FileMgrData *file_mgr_data;
   FileMgrRec *file_mgr_rec;
   int i;

#ifdef DT_PERFORMANCE
   /* Aloke Gupta */
      _DtPerfChkpntMsgSend("Begin Read Directory");
#endif

   /* make sure positionFileName is initialized */
   if (positionFileName == NULL)
      InitializePositionFileName();

   /* mark the directory busy reading */
   directory->busy[activity_reading] = True;

   /* arrange for background process to be started */
   ScheduleActivity(directory);

   /* make sure all views on this directory are marked busy */
   for (i = 0; i < directory->numOfViews; i++)
   {
      file_mgr_data = directory->directoryView[i].file_mgr_data;
      if (file_mgr_data->busy_status == not_busy)
      {
         file_mgr_data->busy_status = busy_readdir;
         file_mgr_data->busy_detail = 0;
         file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
         FileMgrRedisplayFiles(file_mgr_rec, file_mgr_data, False);
      }
      file_mgr_data->busy_status = busy_readdir;
   }
   return;
}


/*--------------------------------------------------------------------
 *  ReadDirectory
 *    Given a directory name, see if the directory is already cached.
 *    If so, return the file data list, otherwise, read the directory.
 *------------------------------------------------------------------*/

static Boolean
ReadDirectory(
        Widget w,
        char *host_name,
        char *directory_name,
        FileData **file_data,
        int *file_count,
        FileMgrData *file_mgr_data)
{
   Directory *directory;
   int i;
   char *err_msg;

   /* initialize return values */
   if (file_data != NULL)
   {
      *file_count = 0;
      *file_data = NULL;
   }

   /* see if the directory is already in the cache */
   directory = FindDirectory(host_name, directory_name);

   if ((directory != NULL) &&
       (strcmp(directory_name, directory->directory_name) == 0))
   {
      /* The directory is already in the cache. */
      directory->viewed = True;

      /* Look for the view in the view list */
      for (i = 0; i < directory->numOfViews; i++)
         if (directory->directoryView[i].file_mgr_data == file_mgr_data)
            break;

      /* If view not found, add to the view list */
      if (i == directory->numOfViews)
      {
         directory->directoryView = (DirectoryView *)
                                 XtRealloc ((char *) directory->directoryView,
                                            sizeof(DirectoryView) * (i + 1));
         directory->numOfViews++;
         directory->directoryView[i].file_mgr_data = file_mgr_data;
      }

      /* set mapped flag for the view */
      directory->directoryView[i].mapped = file_mgr_data->mapped;

      /* check if we need to popup an error message */
      if (directory->errmsg_needed &&
          !directory->busy[activity_reading] &&
          w != NULL)
      {
         err_msg = XtNewString(GetSharedMessage(CANNOT_READ_DIRECTORY_ERROR));
         FileOperationError (w, err_msg, directory_name);
         XtFree(err_msg);
         directory->errmsg_needed = False;
      }

      DPRINTF2(("ReadDirectory(\"%s\", \"%s\") returns cached\n",
                host_name, directory_name));
   }

   else
   {
      Tt_status tt_status;

      /* The directory is not yet in the cache. */

      /*  Expand the directory set array, if necessary.  */
      if (directory_count == directory_set_size)
      {
         directory_set_size += 10;
         directory_set = (Directory **) XtRealloc((char *)directory_set,
                                    sizeof(Directory **) * directory_set_size);
      }


      /*  Create and initialize a new directory entry  */
      directory_set[directory_count] = directory =
                                  (Directory *) XtMalloc (sizeof (Directory));
      directory_count++;

      directory->host_name = XtNewString (host_name);
      directory->directory_name = XtNewString (directory_name);
      directory->path_name = ResolveLocalPathName (host_name,
                                                   directory_name,
                                                   NULL,
                                                   home_host_name,
                                                   &tt_status );
      if (directory->path_name == NULL)
      {
         directory->path_name = (char *) XtMalloc(sizeof(char));
         directory->path_name[0]='\0';
      }
      directory->tt_path_name = NULL;
      directory->viewed = True;
      directory->file_count = 0;
      directory->numOfViews = 1;
      directory->errnum = 0;
      directory->errmsg_needed = False;
      directory->last_check = 0;
      directory->link_check_needed = False;
      directory->file_count = 0;
      directory->file_data = NULL;
      directory->new_data = NULL;
      directory->dir_data = NULL;
      directory->path_count = 0;
      directory->path_logical_types = NULL;
      directory->position_count = 0;
      directory->position_info = NULL;
      directory->modify_begin = 0;
      directory->modified_count = 0;
      directory->was_up_to_date = True;
      directory->modified_list = NULL;
      directory->activity = activity_idle;
      for (i = 0; i < activity_idle; i++)
        directory->busy[i] = False;

      directory->directoryView = (DirectoryView *)
                                       XtMalloc (sizeof(DirectoryView));
      directory->directoryView[0].file_mgr_data = file_mgr_data;
      directory->directoryView[0].mapped = file_mgr_data->mapped;

      /*  Open the directory for reading and read the files.  */
      ReadDirectoryFiles (w, directory);
   }

   /* Restart refresh timer, if necessary */
   if (file_mgr_data->mapped && timer_suspended)
   {
      XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
      timer_suspended = False;
   }

   /* return the file data */
   if (file_data != NULL && !directory->busy[activity_reading])
   {
      *file_count = directory->file_count;
      *file_data = directory->file_data;
   }

   return directory->busy[activity_reading];
}


/*--------------------------------------------------------------------
 *  _ReadDir
 *    Internal routine that recursively read a directory plus
 *    subdirectories down to a depth given by read_level.
 *------------------------------------------------------------------*/

static int
_ReadDir(
  Widget w,
  FileMgrData *file_mgr_data,
  char *host_name,
  char *directory_name,
  FileViewData *dp,         /* directory info */
  int level,                /* tree level of this directory */
  int read_level,           /* deepest level to be read */
  char **branch_list)       /* list of tree branches to expand */
/*
 * Recursively read a directory plus subdirectories down to a depth
 * given by read_level.
 */
{
  char subdir_name[MAX_PATH];
  FileData *fp, *file_data;
  FileViewData **lp = NULL, *ip;
  int i, j, n, rc;
  TreeShow ts;
  Boolean busy_reading;

  DPRINTF2(("_ReadDir(\"%s\", \"%s\"): level %d, read_level %d\n",
            host_name, directory_name, level, read_level));

  /* initialize list of descendents and counts */
  if (dp)
  {
    dp->desc = NULL;
    dp->ndir = dp->nfile = 0;
    lp = &dp->desc;
  } else
    ip = NULL;

  /* Read the directory content */
  busy_reading = ReadDirectory(w, host_name, directory_name,
		               &file_data, &n, file_mgr_data);
  if (busy_reading)
  {
    file_mgr_data->busy_status = busy_readdir;
    return 0;
  }

  if (n <= 0)
    return -1;

  level++;

  for (i = 0, fp = file_data; i < n && fp; i++, fp = fp->next) {

    /* initialize new dir entry */
    if (dp)
    {
       ip = (FileViewData *)XtMalloc(sizeof(FileViewData));
       memset(ip, 0, sizeof(FileViewData));
       ip->file_data = fp;
       ip->parent = dp;
       ip->ts = tsNotRead;
    }

    /* read subdirectory */
    if (fp->is_subdir)
    {
        /* construct sub directory name */
        strncpy(subdir_name, directory_name, MAX_PATH - 1);
        if (strlen(subdir_name) > 0
            && subdir_name[strlen(subdir_name) - 1] != '/')
            strncat(subdir_name, "/", MAX_PATH - 1);

        strncat(subdir_name, fp->file_name, MAX_PATH - 1);
        subdir_name[MAX_PATH - 1] = 0;

      /* see if we know this entry from branch_list */
      if (!QueryBranchList(file_mgr_data, branch_list, subdir_name, &ts))
        /* not known: assume we shouldn't read this subdir */
        ts = tsNotRead;

      if (level < read_level || ts != tsNotRead) {

        rc = _ReadDir(w, file_mgr_data, host_name, subdir_name, ip,
                      level, read_level, branch_list);
        if (ip == NULL)
         ;
        else if (rc)
          ip->ts = tsError;
        else if (ts >= tsReading)
          ip->ts = ts;
        else if (level >= file_mgr_data->tree_show_level)
          ip->ts = tsNone;
        else if (file_mgr_data->tree_files == TREE_FILES_ALWAYS)
          ip->ts = tsAll;
        else
          ip->ts = tsDirs;
      }
    }

    /* add new entry to linked list */
    if (dp && lp)
    {
      *lp = ip;
      lp = &ip->next;
    }
  }

  return 0;
}


/*--------------------------------------------------------------------
 *  ReadDir
 *    This is the main external entry point for reading directories.
 *------------------------------------------------------------------*/

int
ReadDir(
  Widget w,
  FileMgrData *file_mgr_data,
  char *host_name,
  char *directory_name,
  FileViewData *dp,         /* directory info */
  int level,                /* tree level of this directory */
  int read_level,           /* deepest level to be read */
  char **branch_list)       /* list of tree branches to expand */
{
   /* initially assume we are not busy */
   if (file_mgr_data->busy_status == not_busy)
      file_mgr_data->busy_detail = 0;

   file_mgr_data->busy_status = initiating_readdir;

   /* first pass: just check if any directory we need is busy */
   _ReadDir(w, file_mgr_data, host_name, directory_name, NULL, level,
            read_level, branch_list);

   /* if a directory we need is busy, return now */
   if (file_mgr_data->busy_status == busy_readdir)
      return 0;

   /*
    * All directories wee need are available.
    * Make a second pass for real.
    */
   file_mgr_data->busy_status = not_busy;
   return _ReadDir(w, file_mgr_data, host_name, directory_name, dp, level,
                   read_level, branch_list);
}


/*====================================================================
 *
 *  Routines that update the directory cache
 *
 *==================================================================*/

/*--------------------------------------------------------------------
 *  FileWindowMapUnmap
 *    Update mapped flag in view lists.
 *------------------------------------------------------------------*/

void
FileWindowMapUnmap(
        FileMgrData *file_mgr_data)

{
   int i, j;

   for (i = 0; i < directory_count; i++)
   {
      for (j = 0; j < directory_set[i]->numOfViews; j++)
      {
         if (file_mgr_data == directory_set[i]->directoryView[j].file_mgr_data)
         {
            directory_set[i]->directoryView[j].mapped = file_mgr_data->mapped;
            break;
         }
      }
   }

   if (file_mgr_data->mapped && timer_suspended)
   {
      XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
      timer_suspended = False;
   }
}


/*--------------------------------------------------------------------
 *  RereadDirectory
 *    Read a directory already cached and update its contents.
 *------------------------------------------------------------------*/

void
RereadDirectory(
        Widget w,
        char *host_name,
        char *directory_name )
{
   Directory *directory;

   DPRINTF(("RereadDirectory(%s, %s)\n", host_name, directory_name));

   /*  Find the directory set entry.  */
   directory = FindDirectory(host_name, directory_name);
   if (directory != NULL)
   {
      /* reset errnum to make sure we'll get an error message */
      directory->errnum = 0;

      /* Read the directory. */
      if (!directory->busy[activity_reading])
         ReadDirectoryFiles(w, directory);
   }
}


/*--------------------------------------------------------------------
 *  UpdateDirectory
 *    Check if any files were added or deleted in a directory
 *    and update the directory contents accordingly.
 *------------------------------------------------------------------*/

void
UpdateDirectory(
        Widget w,
        char *host_name,
        char *directory_name )
{
   Directory *directory;

   DPRINTF(("UpdateDirectory(%s, %s)\n", host_name, directory_name));

   /*  Find the directory set entry.  */
   directory = FindDirectory(host_name, directory_name);
   if (directory != NULL)
   {
      /* arrange for directory contents to be checked */
      if (!directory->busy[activity_update_all])
      {
         directory->busy[activity_update_all] = True;
         ScheduleActivity(directory);
      }
   }
}


/*====================================================================
 *
 *  Directory modification routines:
 *
 *  The following routines are provided to avoid unnecessary
 *  re-reads of whole directories.  For example, if the user
 *  renames a file, it's only necessary to remove the old file
 *  from the directory and add it back under its new name; there
 *  is no need to read the whole directory again.  Similarly,
 *  when a file is dropped on a directory, it's only necessary
 *  to add the one new file to the directory.
 *
 *  To accomplish this, the routines that rename or copy files
 *  make the following calls:
 *
 *    DirectoryBeginModify():  called before doing the operation
 *    DirectoryFileModified(): called once for each affected file
 *    DirectoryEndModify():    called when the operation is completed
 *
 *  The routines remember which files were modified, and when
 *  DirectoryEndModify is called, a background process is started,
 *  that re-stats and types just those files.
 *
 *  A complication arises from automatic re-reads triggered by
 *  a periodic timer (routine TimerEvent).  Since renaming or
 *  copying files changes the timestamp on the directory, the
 *  automatic re-read would re-read the whole directory soon after
 *  the operation is done, nullifying our efforts to avoid
 *  unnecessary re-reads.  Therefore:
 *
 *    - We don't do any automatic re-reads between calls to
 *      DirectoryBeginModify and DirectoryEndModify.
 *
 *    - If the directory timestamp hadn't changed at the time
 *      of the DirectoryBeginModify, then when the directory
 *      update triggered by DirectoryEndModify finishes, we
 *      set the modify_time in the directory_set to the current
 *      timestamp of the directory.  This means that the next
 *      automatic re-read won't be triggered unless the directory
 *      is modified again after the DirectoryEndModify.
 *
 *==================================================================*/

/*--------------------------------------------------------------------
 *  DirectoryAbortModify
 *    Decrement the modify_begin counter.
 *------------------------------------------------------------------*/

void
DirectoryAbortModify(
        char *host_name,
        char *directory_name)
{
   Directory *directory;

   DPRINTF(("DirectoryAbortModify(%s, %s)\n", host_name, directory_name));

   /*  Find the directory set entry.  */
   directory = FindDirectory(host_name, directory_name);
   if (directory != NULL)
   {
      directory->modify_begin--;

      if (directory->modify_begin == 0)
         directory->was_up_to_date = True;

      DPRINTF(("   modify_begin %d, up_to_date %d\n",
               directory->modify_begin, directory->was_up_to_date));
   }
}


/*--------------------------------------------------------------------
 *  DirectoryBeginModify
 *    Increment the modify_begin counter to suspend automatic
 *    re-reads until DirectoryEndModify is called.
 *------------------------------------------------------------------*/

void
DirectoryBeginModify(
        char *host_name,
        char *directory_name)
{
   Directory *directory;

   DPRINTF(("DirectoryBeginModify(%s, %s)\n", host_name, directory_name));

   /*  Find the directory set entry.  */
   directory = FindDirectory(host_name, directory_name);
   if (directory != NULL)
   {
      if (directory->modify_begin == 0)
         /* until we know better, assume the directory changed */
         directory->was_up_to_date = False;

      /* increment the modify_begin counter */
      directory->modify_begin++;

      DPRINTF(("   modify_begin %d, up_to_date %d\n",
               directory->modify_begin, directory->was_up_to_date));
   }
}


/*--------------------------------------------------------------------
 *  DirectoryModifyTime
 *    This routine should be called after DirectoryBeginModify and
 *    before doing any operation on the directory.  The parameter
 *    modify_time should be the current timestamp of the directory.
 *    By comparing the value to the modify_time stored in the
 *    directory set we decide whether the directory had already
 *    changed before the update operation began.
 *    Note: the reason for supplying a separate call for this check,
 *    instead of doing it inside DirectoryBeginModify(), is that we
 *    want to do the stat call that determines the current timestamp
 *    of the directory in a background process.  The background
 *    process that we start fo do the actual update is a convenient
 *    place to do this.
 *------------------------------------------------------------------*/

void
DirectoryModifyTime(
        char *host_name,
        char *directory_name,
        long modify_time)
{
   Directory *directory;

   DPRINTF(("DirectoryModifyTime(%s, %s)\n", host_name, directory_name));

#ifdef SMART_DIR_UPDATE
   /*  Find the directory set entry.  */
   directory = FindDirectory(host_name, directory_name);

   if (directory != NULL)
   {
      /* mark directory up-to-date if unchanged since last read */
      if (modify_time <= directory->modify_time)
         directory->was_up_to_date = True;
      DPRINTF(("   modify_begin %d, up_to_date %d\n",
               directory->modify_begin, directory->was_up_to_date));
   }
#endif
}


/*--------------------------------------------------------------------
 *  DirectoryFileModified
 *    This routine is called when we know that a file in a directory
 *    has been modified, added or removed.  The file name is added
 *    to the list of modified files.  The next time an update
 *    background process is started, it will check all the files
 *    on the modfied list and update the corresponding FileData.
 *------------------------------------------------------------------*/

void
DirectoryFileModified(
        char *host_name,
        char *directory_name,
        char *file_name)
{
   Directory *directory;
   int i;

   DPRINTF(("DirectoryFileModified(%s, %s, %s)\n",
            host_name, directory_name, file_name));

   /*  Find the directory set entry.  */
   directory = FindDirectory(host_name, directory_name);
   if (directory != NULL)
   {
      /* see if the file is already on the list */
      for( i = 0; i < directory->modified_count; ++i )
        if( strcmp( directory->modified_list[i], file_name ) == 0 )
          return;

      /* add the file to the modified_list */
      i = directory->modified_count++;
      directory->modified_list = (char **)
        XtRealloc((char *)directory->modified_list, (i + 1)*sizeof(char *));
      directory->modified_list[i] = XtNewString(file_name);
   }
}


/*--------------------------------------------------------------------
 *  DirectoryEndModify
 *    Start an update background process (will check all the files
 *    on the modfied list and update the corresponding FileData).
 *------------------------------------------------------------------*/

void
DirectoryEndModify(
        char *host_name,
        char *directory_name)
{
   Directory *directory;

   DPRINTF(("DirectoryEndModify(%s, %s)\n", host_name, directory_name));

   /*  Find the directory set entry.  */
   directory = FindDirectory(host_name, directory_name);

   /* arrange for an update background process to be scheduled */
   if (directory != NULL)
   {
      directory->modify_begin--;
      DPRINTF(("   modify_begin %d, up_to_date %d, modified_count %d\n",
               directory->modify_begin,
               directory->was_up_to_date,
               directory->modified_count));
      if (directory->modified_count > 0)
      {
         Directory *subdir;
         char subdir_name[MAX_PATH + 1];
         char *p;
         int i;

         /*
          * If any of the modifed files is a subdirectory that we have
          * cached, schedule an activity_checking_dir to make sure that
          * the subdirectory is still readable.
          */
         strcpy(subdir_name, directory_name);
         p = subdir_name + strlen(subdir_name);
         if (p[-1] != '/')
           *p++ = '/';

         for (i = 0; i < directory->modified_count; i++)
         {
            strcpy(p, directory->modified_list[i]);
            subdir = FindDirectory(host_name, subdir_name);
            if (subdir)
            {
               DPRINTF(("   schedule check for subdir \"%s\"\n",
                        directory->modified_list[i]));
               subdir->busy[activity_checking_dir] = True;
               ScheduleActivity(subdir);
            }
         }

#ifdef SMART_DIR_UPDATE
         /* schedule a partial update of the modfied directory */
         if (directory->was_up_to_date)
            directory->busy[activity_update_some] = True;
         else
            directory->busy[activity_update_all] = True;
#else
         /* schedule a full update of the modfied directory */
         directory->busy[activity_update_all] = True;
#endif
         ScheduleActivity(directory);
      }
   }
}


/*--------------------------------------------------------------------
 *  UpdateDirectorySet
 *    We call this when we do a database update. It loops through
 *    the directory_set list and rereads each directory.
 *------------------------------------------------------------------*/

void
UpdateDirectorySet( void )
{
   int i;

   DPRINTF(("UpdateDirectorySet ...\n"));

   for (i = 0; i < directory_count; i++)
      if (!directory_set[i]->busy[activity_reading])
         ReadDirectoryFiles (NULL, directory_set[i]);
}



/*--------------------------------------------------------------------
 *  UpdateCachedDirectories
 *    Update view list for all cached directories.
 *    Throw out any directories that are no longer being viewed.
 *------------------------------------------------------------------*/

void
UpdateCachedDirectories(
        View **view_set,
        int view_count)
{
   DialogData * dialog_data;
   FileMgrData * file_mgr_data;
   int i, j, k, n;
   Directory *directory;

   /*
    * First step:
    *   clear the view list in all directory set entries
    */
   for (i = 0; i < directory_count; i++)
   {
      if( !(strcmp(directory_set[i]->directory_name, trash_dir) == 0) )
      {
         XtFree ((char *) directory_set[i]->directoryView);
         directory_set[i]->numOfViews = 0;
         directory_set[i]->directoryView = NULL;
         directory_set[i]->viewed = False;
      }
   }


   /*
    * Second step:
    *   reconstruct view lists by adding each directory found in the view
    *   set to the view list for the corresponding directory set entry
    */
   for (j = 0; j < view_count; j++)
   {
      dialog_data = (DialogData *) view_set[j]->dialog_data;
      file_mgr_data = (FileMgrData *) dialog_data->data;

      /* loop through all direcories in this view */
      for (k = 0; k < file_mgr_data->directory_count; k++)
      {
         /* find the directory in the directory set */
         directory = FindDirectory(view_set[j]->host_name,
                                   file_mgr_data->directory_set[k]->name);

         /* we expect the directory to be found; if not, something is wrong */
         if (directory == NULL)
         {
            fprintf(stderr, "Warning: %s:%s not found in directory set.\n",
                    view_set[j]->host_name,
                    file_mgr_data->directory_set[k]->name);
            continue;
         }

         /* add the directory to the view list */
         n = directory->numOfViews;
         directory->directoryView = (DirectoryView *)
                                  XtRealloc ((char *) directory->directoryView,
                                             sizeof(DirectoryView) * (n + 1));
         directory->directoryView[n].file_mgr_data = file_mgr_data;
         directory->directoryView[n].mapped = file_mgr_data->mapped;
         directory->numOfViews++;
         directory->viewed = True;
      }
   }


   /*
    * Third step:
    *   remove all directories that have empty view lists
    */
   i = 0;
   while (i < directory_count)
   {
      if (directory_set[i]->numOfViews > 0 ||
          strcmp(directory_set[i]->directory_name, trash_dir) == 0)
      {
         /* Keep this directory in the directory set. */
         i++;
      }
      else
      {
         /* Delete the file data and remove from the directory set. */

         DPRINTF(("UpdateCachedDirectories: removing %s:%s\n",
                  directory_set[i]->host_name,
                  directory_set[i]->directory_name));

         FreeDirectory(directory_set[i]);

         for (k = i; k < directory_count - 1; k++)
            directory_set[k] = directory_set[k + 1];

         directory_count--;
      }
   }

   /* Restart refresh timer, if necessary */
   if (timer_suspended && SomeWindowMapped())
   {
      XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
      timer_suspended = False;
   }
}


/*====================================================================
 *
 * Routines that return directory data
 *
 *==================================================================*/

/*--------------------------------------------------------------------
 * GetLongName
 *   Return a string that contains file information similar to "ls -l",
 *   including: permissions, owner, modified time, link (if any).
 *   Used for "view by attributes"
 *
 *   Example:
 *     -rw-r--r--  dld  staff  108314  Jul 26 15:16:36 1993 Directory.c
 *
 *------------------------------------------------------------------*/

char *
GetLongName(
        FileData *file_data )
{
#ifdef NLS16
   struct tm * tms;
   char time_string[100];
#else
   char * time_string;
#endif /* NLS16 */
   char link_path[MAX_PATH + 5];
   static gid_t group_id = (gid_t)-1;
   static uid_t user_id = (uid_t)-1;
   struct group * group_data;
   struct passwd * user_data;
   static char group_name[20];
   static char user_name[20];
   char * long_name;
   time_t long_modify_time;
   char permission;
   char usr_read_priv, usr_write_priv, usr_exec_priv;
   char grp_read_priv, grp_write_priv, grp_exec_priv;
   char oth_read_priv, oth_write_priv, oth_exec_priv;

   /*  Generate the long list name.  */
   long_name = (char *) XtMalloc(sizeof(char) * (MAX_PATH * 3));
   long_name[0]='\0';

   /* Initially, assume their is not a soft link */
   link_path[0] = '\0';

   if (file_data->errnum == 0)
   {
     if (file_data->stat.st_gid != group_id)
     {
       group_id = file_data->stat.st_gid;
       group_data = getgrgid (file_data->stat.st_gid);
       if (group_data)
       {
         strcpy (group_name, group_data->gr_name);
         if (strlen (group_name) == 0)
            strcpy (group_name, "root");
       }
       else
         strcpy (group_name, "root");
     }

     if (file_data->stat.st_uid != user_id)
     {
       user_id = file_data->stat.st_uid;
       user_data = getpwuid (file_data->stat.st_uid);
       /* Initially, assume their is not a user name */
       user_name[0] = '\0';
       if (user_data)
         strcpy (user_name, user_data->pw_name);
       else
         sprintf(user_name,"%ld",(long)user_id);
     }
   }
   else
   {
      char error_msg[1024];
      int msg_len;

      /* determine how much space we have for an error message */
      long_modify_time = 747616435;
                         /* just needed to determine the length of a date */
#ifdef NLS16
      tms = localtime(&long_modify_time);
      strftime( time_string, 100,
                GetSharedMessage(DIRECTORY_DATE_FORMAT),
                tms);
#else
      time_string = ctime ((time_t *)&long_modify_time);
      time_string[strlen(time_string)-1] = 0x0;
      time_string += 4;
#endif
      msg_len = 10 + 3 + 9 + 1 + 9 + 1 + 9 + 1 + strlen(time_string);

      /* generate the error message */
      strcpy(error_msg, "(");
      strncpy(error_msg + 1, strerror(file_data->errnum), msg_len - 2);
      error_msg[msg_len - 1] = '\0';
      strcat(error_msg, ")");

      sprintf( long_name, "%-28.28s  %s  %9d %s",
                            file_data->file_name,
                            time_string,
                            0, error_msg );

      return (long_name);
   }


   /* Build the permission string  */
   switch( file_data->stat.st_mode & S_IFMT )
   {
   case S_IFDIR:
     permission = 'd';
     break;
   case S_IFCHR:
     permission = 'c';
     break;
   case S_IFBLK:
     permission = 'b';
     break;
   case S_IFLNK:
     permission = 'l';
     break;
   default :
     permission = OPTION_OFF;
     break;
   }

   if (file_data->stat.st_mode & S_IRUSR) usr_read_priv = READ_PRIV;
   else usr_read_priv = OPTION_OFF;

   if (file_data->stat.st_mode & S_IWUSR) usr_write_priv = WRITE_PRIV;
   else usr_write_priv = OPTION_OFF;

   if (file_data->stat.st_mode & S_IXUSR) usr_exec_priv = EXEC_PRIV;
   else usr_exec_priv = OPTION_OFF;


   if (file_data->stat.st_mode & S_IRGRP) grp_read_priv = READ_PRIV;
   else grp_read_priv = OPTION_OFF;

   if (file_data->stat.st_mode & S_IWGRP) grp_write_priv = WRITE_PRIV;
   else grp_write_priv = OPTION_OFF;

   if (file_data->stat.st_mode & S_IXGRP) grp_exec_priv = EXEC_PRIV;
   else grp_exec_priv = OPTION_OFF;


   if (file_data->stat.st_mode & S_IROTH) oth_read_priv = READ_PRIV;
   else oth_read_priv = OPTION_OFF;

   if (file_data->stat.st_mode & S_IWOTH) oth_write_priv = WRITE_PRIV;
   else oth_write_priv = OPTION_OFF;

   if (file_data->stat.st_mode & S_IXOTH) oth_exec_priv = EXEC_PRIV;
   else oth_exec_priv = OPTION_OFF;


   long_modify_time = file_data->stat.st_mtime;
#ifdef NLS16
   tms = localtime(&long_modify_time);
   strftime( time_string, 100,
             GetSharedMessage(DIRECTORY_DATE_FORMAT),
             tms);
#else
   time_string = ctime ((time_t *)&long_modify_time);
   time_string[strlen(time_string)-1] = 0x0;
   time_string += 4;
#endif

   /* Fill in the name of where the link goes */
   if (file_data->link)
   {
     strcpy( link_path, " -> " );
     strcpy( link_path + 4, file_data->link );
   }

   {
#define ELLIPSIS " (...) "
#define NAME_PRECISION 28
     if (! is_multibyte)
     {
       int len = strlen( file_data->file_name );
       if( len > NAME_PRECISION )
       {
	 int i;
	 char name[NAME_PRECISION];
	 sprintf( name, "%-20.20s%s", file_data->file_name, ELLIPSIS);

	 sprintf( long_name, "%-28.28s  %s  %9ld  %c%c%c%c%c%c%c%c%c%c  %-9s  %-9s  %s",
		  name,
		  time_string,
		  (long)file_data->stat.st_size,
		  permission,
		  usr_read_priv, usr_write_priv, usr_exec_priv,
		  grp_read_priv, grp_write_priv, grp_exec_priv,
		  oth_read_priv, oth_write_priv, oth_exec_priv,
		  user_name, group_name,
		  link_path );
       }
       else
       {
	 sprintf( long_name, "%-28.28s  %s  %9ld  %c%c%c%c%c%c%c%c%c%c  %-9s  %-9s  %s",
		  file_data->file_name,
		  time_string,
		  (long)file_data->stat.st_size,
		  permission,
		  usr_read_priv, usr_write_priv, usr_exec_priv,
		  grp_read_priv, grp_write_priv, grp_exec_priv,
		  oth_read_priv, oth_write_priv, oth_exec_priv,
		  user_name, group_name,
		  link_path );
       }
     } else {
       /* MULTIBYTE
	*
	* sprintf() counts width in bytes (not characters), moreover,
	* it fails (returns -1 and produces no output) if input string is not
	* a valid multibyte string (at least the glibc version), but we can't fail
	* to display a file because it's name has some invalid characters). So it looks
	* that instead of using sprintf() we have to format the file name part manually.
	*/
       int len = DtCharCount( file_data->file_name );
       int copy_len =  len > NAME_PRECISION ?  NAME_PRECISION - sizeof(ELLIPSIS) + 1 : len;
       int byte_len = 0;
       int count;

       long_name[0]='\0';
       /* properly copy copy_len characters of the multibyte string
	  replacing invalid chars with '?' */
       for (count = 0;
	    (count < copy_len) && *(file_data->file_name + byte_len);
	    count ++)
       {
	 int chr_bytes = mblen(file_data->file_name + byte_len, MB_CUR_MAX);
	 if (chr_bytes > 0)
	 {
	   strncpy(long_name + byte_len, file_data->file_name + byte_len, chr_bytes);
	 }
	 else if (chr_bytes < 0)
	 { /* invalid char */
	   chr_bytes = 1;
	   long_name[byte_len]='?';
	 }
	 else
	 {
	   /* null-wide character, won't really happen */
	   break;
	 }
	 byte_len+=chr_bytes;
       }
       if (copy_len < len)
       {
	 /* truncated name, add ellipsis */
	 strncpy(long_name + byte_len, ELLIPSIS, sizeof(ELLIPSIS) - 1);
	 byte_len+= sizeof(ELLIPSIS) - 1;
       }
       else
       {
	 /* full name, pad it with spaces up to the proper length */
	 for (; count <  NAME_PRECISION ; count++)
	 {
	   long_name[byte_len++]=' ';
	 }
       }
       sprintf( long_name + byte_len, "  %s  %9ld  %c%c%c%c%c%c%c%c%c%c  %-9s  %-9s  %s",
		time_string,
		(long)file_data->stat.st_size,
		permission,
		usr_read_priv, usr_write_priv, usr_exec_priv,
		grp_read_priv, grp_write_priv, grp_exec_priv,
		oth_read_priv, oth_write_priv, oth_exec_priv,
		user_name, group_name,
		link_path );
     } /* is_multibyte */
   }

   return (long_name);
}



/*--------------------------------------------------------------------
 *  DirectoryBusy
 *    See if path has a directory view of it or if any sub-directories
 *    of it are viewed.   The path parameter is of the for /foo/bar
 *------------------------------------------------------------------*/

Boolean
DirectoryBusy(
        char *path )
{
   FileMgrData * file_mgr_data;
   FileViewData  * sub_root;
   int i, j, k;
   int len = strlen(path);

   for (i = 0; i < directory_count; i++)
   {
      /* check if this directory is equal to 'path' or a subdir of 'path' */
      if (directory_set[i]->viewed &&
          (strcmp (directory_set[i]->path_name, path) == 0 ||
           strncmp (directory_set[i]->path_name, path,len) == 0 &&
             directory_set[i]->path_name[len] == '/'
           ||
           directory_set[i]->tt_path_name != NULL &&
           (strcmp (directory_set[i]->tt_path_name, path) == 0 ||
            strncmp (directory_set[i]->tt_path_name, path,len) == 0 &&
              directory_set[i]->tt_path_name[len] == '/')))
      {
         /* check the views in the view list */
         for (j = 0; j < directory_set[i]->numOfViews; j++)
         {
            file_mgr_data = directory_set[i]->directoryView[j].file_mgr_data;

            /* find the dir in the directory set for this view */
            for (k = 0; k < file_mgr_data->directory_count; k++)
               if (strcmp(file_mgr_data->directory_set[k]->name,
                          directory_set[i]->directory_name) == 0)
               {
                  break;
               }
            if (k == file_mgr_data->directory_count)
              continue;  /* not found ... something must be wrong! */

            /*
             * Check if this directory is acutally visible.
             * If the directory is in a tree branch that is not currently
             * expanded, it is not visible and would not be considered busy.
             */

            /* the tree root is always considered busy */
            if (k == 0)
              return True;

            /* a subdir is considered busy if it is visible and at least
             * partially expanded */
            sub_root = file_mgr_data->directory_set[k]->sub_root;
            if (sub_root->displayed &&
                (sub_root->ts == tsDirs && sub_root->ndir > 0  ||
                 sub_root->ts == tsAll &&
                   sub_root->ndir + sub_root->nfile > 0))
            {
              return True;
            }
         }
      }
   }

   return (False);
}


/*--------------------------------------------------------------------
 *  GetDirectoryLogicalType
 *     Get logical type for the iconic path.
 *------------------------------------------------------------------*/

char *
GetDirectoryLogicalType(
	FileMgrData *file_mgr_data,
	char *path)
{
   int len;
   int n;
   Directory *directory;
   char *ptr;

   /* 'path' must be a prefix of the current directory */
   len = strlen(path);
   if (strncmp(file_mgr_data->current_directory, path, len) != 0 ||
       (len > 1 &&
       file_mgr_data->current_directory[len] != '/' &&
       file_mgr_data->current_directory[len] != '\0'))
   {
      DPRINTF(("GetDirectoryLogicalType(%s): len %d, cur_dir %s\n",
                path, len, file_mgr_data->current_directory));
      return NULL;
   }

   /*  Find the directory set entry.  */
   directory = FindDirectory(file_mgr_data->host,
                             file_mgr_data->current_directory);
   if ((directory != NULL) &&
       (strcmp(file_mgr_data->current_directory,
               directory->directory_name) == 0))
   {
      /* if we don't have path_logical_types yet, we don't know */
      if (directory->path_logical_types == NULL)
         return NULL;

      /* count the number of components in path */
      if (strcmp(path, "/") == 0)
         n = 0;
      else
      {
         n = 1;
         ptr = path + 1;
         while ((ptr = DtStrchr(ptr, '/')) != NULL)
         {
            ptr = ptr + 1;
            if (*ptr == '\0')
              break;
            else
              n++;
         }
      }

      DPRINTF2(("GetDirectoryLogicalType(%s): n %d, type %s\n",
                path, n, directory->path_logical_types[n]));

      /* return type form path_logical_types array */
      return directory->path_logical_types[n];
   }

   /* directory not found in directory_set */

   return NULL;
}


/*====================================================================
 *
 * Routines for accessing position information
 *
 *==================================================================*/

/*--------------------------------------------------------------------
 *  GetDirectoryPositionInfo
 *     Get cached position info
 *------------------------------------------------------------------*/

int
GetDirectoryPositionInfo(
        char *host_name,
        char *directory_name,
        PositionInfo **position_info)
{
   Directory *directory;

   directory = FindDirectory(host_name, directory_name);
   if (directory == NULL)
      return -1;

   *position_info = directory->position_info;

   return directory->position_count;
}


/*--------------------------------------------------------------------
 *  WritePosInfoProcess
 *    Main routine of the background process that writes the
 *    postion information file.
 *------------------------------------------------------------------*/

static int
WritePosInfoProcess(
        int pipe_fd,
	Directory *directory,
	ActivityStatus activity)
{
   char *fileName;
   int position_count = directory->position_count;
   PositionInfo *position_info = directory->position_info;
   FILE *f;
   int i, rc;
   Tt_status tt_status;

   /* construct the full file name */
   fileName = ResolveLocalPathName( directory->host_name,
                                    directory->directory_name, positionFileName,
                                    home_host_name, &tt_status );
   /* Don't have to check for tt_status
      directory->host_name is home_host_name and ResolveLocalPathName will
      always return a good path
   */
   DPRINTF(("WritePosInfoProcess: count %d, file %s\n",
            position_count, fileName));

   /* Remove old files, if no position information for this view */
   if (position_count <= 0)
      rc = unlink(fileName);
   else
   {
      /* open the file for writing */
      f = fopen(fileName, "w");

      if (f == NULL)
      {
         /* Assume read-only directory, if we can't open the file */
         rc = 0;
      }
      else
      {
         chmod(fileName, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);

         fprintf(f, "%d\n", position_count);
         for (i = 0; i < position_count; i++)
         {
            fprintf(f, "%s\n%d %d %d\n",
                    position_info[i].name,
                    position_info[i].x,
                    position_info[i].y,
                    position_info[i].stacking_order);
         }

         fclose(f);
         rc = 0;
      }
   }

   /* send result back thorugh the pipe */
   DPRINTF(("WritePosInfoProcess: done (rc %d)\n", rc));
   write(pipe_fd, &rc, sizeof(int));
   XtFree( fileName );
   return 0;
}


/*--------------------------------------------------------------------
 *  WritePosInfoPipeCallback
 *    Callback routine that reads the return code sent through the
 *    pipe from the WritePosInfoProcess background process.
 *------------------------------------------------------------------*/

static void
WritePosInfoPipeCallback(
   XtPointer client_data,
   int *fd,
   XtInputId *id)
{
   PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
   Directory *directory = pipe_data->directory;
   int rc;
   int i;

   /* get return code from the pipe */
   rc = -1;
   PipeRead(*fd, &rc, sizeof(int));

   /* close the pipe and cancel the callback */
   close(*fd);
   XtFree( client_data );
   XtRemoveInput(*id);

   /* verify that the directory still exists */
   if (DirectoryGone(directory))
   {
      ScheduleActivity(NULL);
      return;
   }

   DPRINTF(("WritePosInfoPipeCallback: rc %d\n", rc));

   /* reset the busy flag and schedule new work, if any */
   directory->busy[activity_writing_posinfo] = False;
   directory->activity = activity_idle;
   ScheduleActivity(directory);
}


/*--------------------------------------------------------------------
 *  SetDirectoryPositionInfo
 *     Update cached position info.  This routine schedules a
 *     background process that writes the modified information
 *     to the position info file.
 *------------------------------------------------------------------*/

int
SetDirectoryPositionInfo(
        char *host_name,
        char *directory_name,
        int position_count,
        PositionInfo *position_info)
{
   Directory *directory;
   Boolean unchanged;
   int i, j;

   /* find the directory */
   directory = FindDirectory(host_name, directory_name);
   if (directory == NULL)
      return -1;

   /* check if anything has changed */
   if (directory->position_count == position_count)
   {
      unchanged = True;
      for (i = 0; i < position_count && unchanged; i++)
      {
         for (j = 0; j < position_count; j++)
            if (strcmp(position_info[i].name,
                       directory->position_info[j].name) == 0)
            {
               break;
            }

         if (j == position_count ||
             position_info[i].x != directory->position_info[j].x ||
             position_info[i].y != directory->position_info[j].y ||
             position_info[i].stacking_order !=
                                    directory->position_info[j].stacking_order)
         {
            unchanged = False;
         }
      }

      /* if nothing changed, don't do anything */
      if (unchanged)
         return 0;
   }

   /* free old position info  names*/
   for (i = 0; i < directory->position_count; i++)
      XtFree(directory->position_info[i].name);

   /* realloc array, if necessary */
   if (directory->position_count != position_count)
   {
      directory->position_count = position_count;
      directory->position_info =
            (PositionInfo *) XtRealloc((char *)directory->position_info,
                                        position_count * sizeof(PositionInfo));
   }

   /* replace old position info */
   for (i = 0; i < position_count; i++)
   {
      directory->position_info[i].name = XtNewString(position_info[i].name);
      directory->position_info[i].x = position_info[i].x;
      directory->position_info[i].y = position_info[i].y;
      directory->position_info[i].stacking_order =
                                              position_info[i].stacking_order;
   }

   /* make sure positionFileName is initialized */
   if (positionFileName == NULL)
      InitializePositionFileName();

   /* start background process that writes the position info file */
   directory->busy[activity_writing_posinfo] = True;
   ScheduleActivity(directory);

   return 0;
}


/*====================================================================
 *
 * Timer functions
 *   These function are periodically called to scan all cached
 *   directories to see if any have been modified since last read.
 *   If any have, a background process is scheduled to re-read
 *   the directory.
 *
 *==================================================================*/

/*--------------------------------------------------------------------
 * SkipRefresh:
 *   Decide whether to skip an automatic re-read.
 *   (We don't do re-reads on directories that aren't currently
 *    being viewed and on the trash directory, if trash is currently
 *    being emptied.)
 *------------------------------------------------------------------*/

static Boolean
SkipRefresh(
	Directory *directory)
{
   int i;

   /* don't refresh while the directory is being modified */
   if (directory->modify_begin > 0)
      return True;

   /* verify that the directory is still being viewed */
   if (!directory->viewed)
      return True;

   for (i = 0; i < directory->numOfViews; i++)
      if (directory->directoryView[i].mapped)
         break;
   if (i == directory->numOfViews)
      return True;

   /* if trash is being emptied and this is the trash dir, skip it */
   if (removingTrash && strcmp(directory->directory_name, trash_dir) == 0)
      return True;

   return False;
}


/*--------------------------------------------------------------------
 * TimerEventProcess
 *   Main routine for the background process that checks directory
 *   timestamps and the status of links.
 *------------------------------------------------------------------*/

static int
TimerEventProcess(
        int pipe_fd,
	Directory *directory,
	ActivityStatus activity)
{
   struct stat stat_buf;
   long modify_time;
   Boolean link_changed;
   Boolean was_broken;
   FileData *file_data;
   char full_name[MAX_PATH];
   char *namep;
   short pipe_msg;
   int prev_link_kind;
   int cur_link_kind;
   int link_rc;
   int rc;

   /*
    * Do a stat on the directory to get its last-modified time.
    * Also check if we  still have read and execute/search permisssion.
    *
    * Note:
    *  It is important to get the timstamp in exactly the same way that the
    *  ReadDirectoryProcess does it; otherwise, we might get into a loop,
    *  where TimerEventProcess detects that the directory has changed
    *  and triggers ReadDirectoryProcess, but ReadDirectoryProcess won't
    *  be able to get a new timestamp and update the directory structure,
    *  so the next time TimerEventProcess runs it will trigger another
    *  ReadDirectoryProcess, and so on ...
    */
   if (CheckAccess(directory->path_name, R_OK | X_OK) != 0 ||
       stat(directory->path_name, &stat_buf) != 0)
   {
      /* stat or access failed */
      rc = errno;
      modify_time = 0;
   }
   else
   {
      /* stat succeeded and the directory is still readable */
      rc = 0;
      modify_time = stat_buf.st_mtime;
   }

   /*
    * If requested, also check if any links broken.
    *
    * Again: it is important that we determine the kind of link
    * (valid, recursive, or broken) in exactly the same way that
    * ReadDirectoryProcess does it (see comment above)!
    */
   link_changed = False;
   if (rc == 0 && activity == activity_checking_links)
   {
      strcpy(full_name, directory->path_name);
      namep = full_name + strlen(full_name);
      if (namep[-1] != '/')
        *namep++ = '/';

      /* check all links */
      for (file_data = directory->file_data;
           file_data && !link_changed;
           file_data = file_data->next)
      {
         /* Only worry about links */
         if (file_data->link == NULL)
            continue;

         /* Check if the file is still a symbolic link */
         strcpy(namep, file_data->file_name);
         link_rc = lstat(full_name, &stat_buf);
         if (link_rc != 0 || (stat_buf.st_mode & S_IFMT) != S_IFLNK)
         {
            /* no longer a link */
            link_changed = True;
            break;
         }

         /* Check what kind of link this was the last time we looked:
          * a normal link (1), a recursive link (2), or an otherwise
          * broken link (3) */
         if (strcmp(file_data->logical_type, LT_BROKEN_LINK) == 0)
            prev_link_kind = 3;
         else if (strcmp(file_data->logical_type, LT_RECURSIVE_LINK) == 0)
            prev_link_kind = 2;
         else
            prev_link_kind = 1;

         /* Check what kind of link it is now */
         if (_DtFollowLink(full_name) == NULL)
            cur_link_kind = 2;  /* recursive link */
         else if (stat(full_name, &stat_buf) != 0)
            cur_link_kind = 3;  /* broken link */
         else
            cur_link_kind = 1;  /* a valid link */

         /* now we can tell if the link has changed */
         if (prev_link_kind != cur_link_kind)
           link_changed = True;
      }
   }

   /* send result back through the pipe */
   write(pipe_fd, &rc, sizeof(int));
   write(pipe_fd, &modify_time, sizeof(long));
   write(pipe_fd, &link_changed, sizeof(Boolean));
   return 0;
}


/*--------------------------------------------------------------------
 * StickyProcIdle:
 *   Mark sticky background process as idle.  If there are too
 *   may idle sticky procs, cause some of them to exit.
 *------------------------------------------------------------------*/

static void
StickyProcIdle(
   ActivityStatus activity,
   StickyProcDesc *sticky_proc,
   int max_procs)
{
   StickyProcDesc *p, **lp;
   int i, n;

   /* mark the process as idle */
   sticky_proc->idle = True;

   /* if there are too many idle procs, make some of them go away */
   n = 0;
   lp = &ActivityTable[activity].sticky_procs;
   for (p = *lp; p; p = *lp)
   {
      if (!p->idle)
         lp = &p->next;
      else if (n < max_procs)
      {
         n++;
         lp = &p->next;
      }
      else
      {
         DPRINTF2(("StickyProcIdle: end sticky proc %ld\n", (long)p->child));
         PipeWriteString(p->pipe_m2s_fd, NULL);
         close(p->pipe_s2m_fd);
         close(p->pipe_m2s_fd);
         *lp = p->next;
         XtFree((char *)p);
      }
   }
}


/*--------------------------------------------------------------------
 * TimerPipeCallback
 *   Callback routine that reads information sent through the
 *   pipe from the TimerEventProcess background process.
 *------------------------------------------------------------------*/

static void
TimerPipeCallback(
   XtPointer client_data,
   int *fd,
   XtInputId *id)
{
   PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
   Directory *directory = pipe_data->directory;
   int rc;
   long modify_time;
   Boolean link_changed;
   int i;

   /* get return code from the pipe */
   rc = -1;
   PipeRead(*fd, &rc, sizeof(int));
   PipeRead(*fd, &modify_time, sizeof(long));
   PipeRead(*fd, &link_changed, sizeof(Boolean));

   /* close the pipe and cancel the callback */
   if (pipe_data->sticky_proc)
      StickyProcIdle(pipe_data->activity, pipe_data->sticky_proc,
                     maxRereadProcsPerTick);
   else
      close(*fd);
   XtFree( client_data );
   XtRemoveInput(*id);

   /* verify that the directory still exists */
   if (DirectoryGone(directory))
   {
      ScheduleActivity(NULL);
      return;
   }

   DPRINTF2((
     "TimerPipeCallback: rc %d (was %d), time %ld (was %ld), link change %d\n",
     rc, directory->errnum, modify_time, (long)directory->modify_time, link_changed));

   /* reset the busy flag and schedule new work, if any */
   directory->busy[directory->activity] = False;
   directory->activity = activity_idle;
   ScheduleActivity(directory);

   /* if directory-read already in progress, nothing more to do here */
   if (directory->busy[activity_reading] ||
       directory->busy[activity_update_all])
      return;

   /* skip this directory if it is no longer being viewed */
   if (SkipRefresh(directory))
      return;

   /* if the directory was modified or links changed, re-read it */
   if (rc == 0)
   {
      if (link_changed)
      {
         DPRINTF(("TimerPipeCallback: %s link changed\n",
                  directory->directory_name));
         ReadDirectoryFiles(NULL, directory);
      }
      else if (modify_time != directory->modify_time || directory->errnum != 0)
      {
         DPRINTF(("TimerPipeCallback: %s modified\n",
                  directory->directory_name));
         directory->busy[activity_update_all] = True;
         ScheduleActivity(directory);
      }
   }
   else
   {
      if (directory->errnum == 0)
      {
         directory->errnum = rc;
         directory->errmsg_needed = True;
         ReadDirectoryFiles(NULL, directory);
      }
   }
}


/*--------------------------------------------------------------------
 * CheckDesktopProcess
 *   Main routine for the background process that checks each desktop
 *   objects to see if the file that it refers to has disappeared
 *   or has changed type.
 *------------------------------------------------------------------*/

static int
CheckDesktopProcess(
        int pipe_fd,
	Directory *directory,
	ActivityStatus activity)
{
   int i, n;
   DesktopRec *desktopWindow;
   FileViewData *file_view_data;
   char *full_path;
   Tt_status tt_status;
   struct stat stat_buf;
   short pipe_msg;
   FileData2 file_data2;
   FileData *old_data, *new_data;

   for (i = 0; i < desktop_data->numIconsUsed; i++)
   {
      desktopWindow = desktop_data->desktopWindows[i];
      file_view_data = desktopWindow->file_view_data;

      full_path = ResolveLocalPathName( desktopWindow->host,
                                        desktopWindow->dir_linked_to,
                                        desktopWindow->file_name,
                                        home_host_name, &tt_status);

      /* Check if the file still exists */
      errno = 0;
      if (lstat(full_path, &stat_buf) < 0)
      {
         /* the real file no longer exists */
         DPRINTF2((
           "CheckDesktopProcess: sending PIPEMSG_DESKTOP_REMOVED for %s\n",
           full_path));
         pipe_msg = PIPEMSG_DESKTOP_REMOVED;
         write(pipe_fd, &pipe_msg, sizeof(short));
         PipeWriteString(pipe_fd, desktopWindow->host);
         PipeWriteString(pipe_fd, desktopWindow->dir_linked_to);
         PipeWriteString(pipe_fd, desktopWindow->file_name);
      }
      else
      {
         Boolean IsToolBox;
         /* See if the type has changed */
         old_data = file_view_data->file_data;

         if(directory->directoryView && directory->directoryView->file_mgr_data)
             IsToolBox = directory->directoryView->file_mgr_data->toolbox;
         else
             IsToolBox = False;

         ReadFileData2(&file_data2, full_path, NULL,IsToolBox);
         new_data = FileData2toFileData(&file_data2, &n);

         if (new_data->physical_type != old_data->physical_type ||
             strcmp(new_data->logical_type, old_data->logical_type) != 0)
         {
            /* the type has changed */
            DPRINTF2((
              "CheckDesktopProcess: sending PIPEMSG_DESKTOP_CHANGED for %s\n",
              full_path));
            DPRINTF2((
              "  old type %d %s, new type %d %s\n",
              old_data->physical_type, old_data->logical_type,
              new_data->physical_type, new_data->logical_type));

            pipe_msg = PIPEMSG_DESKTOP_CHANGED;
            write(pipe_fd, &pipe_msg, sizeof(short));
            PipeWriteString(pipe_fd, desktopWindow->host);
            PipeWriteString(pipe_fd, desktopWindow->dir_linked_to);
            PipeWriteString(pipe_fd, desktopWindow->file_name);

            PipeWriteFileData(pipe_fd, new_data);
         }

         FreeFileData(new_data, True);
      }
      XtFree(full_path);
      full_path = NULL;
   }

   /* send a 'done' msg through the pipe */
   DPRINTF2(("CheckDesktopProcess: sending DONE\n"));
   pipe_msg = PIPEMSG_DONE;
   write(pipe_fd, &pipe_msg, sizeof(short));
   return 0;
}


/*--------------------------------------------------------------------
 * CheckDesktopPipeCallback
 *   Callback routine that reads information sent through the
 *   pipe from the CheckDesktopProcess background process.
 *------------------------------------------------------------------*/

static void
CheckDesktopPipeCallback(
   XtPointer client_data,
   int *fd,
   XtInputId *id)
{
   PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
   Directory *directory = pipe_data->directory;
   short msg;
   char *host, *dir_linked_to, *file_name;
   FileData *new_data, *old_data;
   Boolean found;
   DesktopRec *desktopWindow;
   int i, n;

   /* read the next msg from the pipe */
   msg = -1;
   n = PipeRead(*fd, &msg, sizeof(short));

   if (msg == PIPEMSG_DESKTOP_REMOVED ||
       msg == PIPEMSG_DESKTOP_CHANGED)
   {
      /* get information from pipe */
      host = PipeReadString(*fd);
      dir_linked_to = PipeReadString(*fd);
      file_name = PipeReadString(*fd);
      if (msg == PIPEMSG_DESKTOP_CHANGED)
         new_data = PipeReadFileData(*fd);
      else
         new_data = NULL;

      DPRINTF2((
        "CheckDesktopPipeCallback: msg %d: host %s, dir %s, name %s\n",
        msg, host, dir_linked_to, file_name));


      /* find the desktop object */
      found = False;
      for (i = 0; i < desktop_data->numIconsUsed; i++)
      {
         desktopWindow = desktop_data->desktopWindows[i];

         if (strcmp(host, desktopWindow->host) == 0 &&
             strcmp(dir_linked_to, desktopWindow->dir_linked_to) == 0 &&
             strcmp(file_name, desktopWindow->file_name) == 0)
         {
            found = True;
            break;
         }
      }

      /* remove or update the desktop object, if found */
      if (! found)
      {
        /* nothing to do */
      }
      else if (msg == PIPEMSG_DESKTOP_REMOVED)
      {
         /* remove the desktop object */
         DesktopObjectRemoved(desktopWindow);
      }
      else /* msg == PIPEMSG_DESKTOP_CHANGED */
      {
         /* replace file data */
         old_data = desktopWindow->file_view_data->file_data;
         FreeFileData(old_data, False);
         memcpy(old_data, new_data, sizeof(FileData));
         XtFree((char *)new_data);
         new_data = NULL;

         /* update the desktop object */
         DesktopObjectChanged(desktopWindow);
      }

      /* free storage */
      XtFree(host);
      XtFree(dir_linked_to);
      XtFree(file_name);
      if (new_data)
        FreeFileData(new_data, True);
   }

   else if (msg == PIPEMSG_DONE)
   {
      /* close the pipe and cancel the callback */
      close(*fd);
      XtFree( client_data );
      XtRemoveInput(*id);

      /* reset the busy flag and schedule new work, if any */
      directory->busy[directory->activity] = False;
      directory->activity = activity_idle;
      ScheduleActivity(directory);
   }
}


/*--------------------------------------------------------------------
 *
 * CheckDesktop
 *   Arrange for a CheckDesktopProcess background process to be
 *   started (checks each desktop objects to see if the file that
 *   it refers to has disappeared or has changed type).
 *
 *------------------------------------------------------------------*/

void
CheckDesktop( void )
{
  dummy_directory->busy[activity_checking_desktop] = True;
  ScheduleActivity(dummy_directory);
}


/*--------------------------------------------------------------------
 * TimerEvent
 *   This routine is called periodically.  It schedules a
 *   TimerEventProcess background process to be started for every
 *   directory in the cache.
 *------------------------------------------------------------------*/

/* comparison routine for qsort */
static int
CheckListCmp(
        int *p1,
        int *p2 )
{
   return directory_set[*p1]->last_check - directory_set[*p2]->last_check;
}


static void
TimerEvent(
        XtPointer client_data,
        XtIntervalId *id )
{
   static int *check_list = NULL;
   static int check_alloc = 0;
   int i, j, n;


   DPRINTF2(("Directory::TimerEvent\n"));

   if (dragActive)
   {
      /*
       * Don't change any directories while a drag is active.
       *
       * Reason: drag callbacks are called with a pointer to a FileViewData
       * structure; if a directory is reread while a drag is active,
       * the pointer would become invalid, causing unpredictable behavior.
       *
       * Schedule the next TimerEvent in 1/2 second, so that check will
       * be done soon after the drag is finished.
       */
      XtAppAddTimeOut (app_context, 500, TimerEvent, NULL);
      return;
   }

   /* update tick count */
   tick_count++;

   /* determine if we should also check for broken links this time */
   if (checkBrokenLink > 0 &&
       tick_count >= lastLinkCheckTick + ticksBetweenLinkChecks)
   {
     /* set link_check_needed flag on all directores */
     for (i = 0; i < directory_count; i++)
     {
        /* skip this directory if no view is mapped */
        if (SkipRefresh(directory_set[i]))
           continue;

        /* if a check is already in progress, don't start another one */
        if (directory_set[i]->busy[activity_checking_links])
           continue;

        /* arrange for background process to be scheduled */
        directory_set[i]->link_check_needed = True;
     }

     lastLinkCheckTick = tick_count;
   }

   /* make sure check_list array is big enough */
   if (directory_count > check_alloc)
   {
      check_alloc = directory_count + 5;
      check_list =
         (int *)XtRealloc((char *)check_list, check_alloc*sizeof(int));
   }

   /* get a list of all directories that need to be checked */
   n = 0;
   for (i = 0; i < directory_count; i++)
   {
      /* skip this directory if no view is mapped */
      if (SkipRefresh(directory_set[i]))
         continue;

      /* if a stat is already in progress, don't start another one */
      if (directory_set[i]->busy[activity_checking_dir] ||
          directory_set[i]->busy[activity_checking_links])
         continue;

      /* add this directory to the check list */
      check_list[n++] = i;
   }

   /*
    * Next we want to schedule a background process to be started
    * for each directory in the check_list.  However, the variable
    * maxRereadProcsPerTick puts a limit on the number of such
    * background processes started per clock tick (i.e., per call
    * to this routine).  Hence we sort check_list by last_check
    * (records the tick count when a directory was last read or
    * checked) and schedule backround processes on those dirs that
    * haven't been checked in the longest time.
    */
   qsort(check_list, n, sizeof(int), (int (*)())CheckListCmp);

   /* arrange for background process to be started */
   for (j = 0; j < n && j < maxRereadProcsPerTick; j++)
   {
      i = check_list[j];
      if (directory_set[i]->link_check_needed)
      {
         directory_set[i]->link_check_needed = False;
         directory_set[i]->busy[activity_checking_links] = True;
      }
      else
         directory_set[i]->busy[activity_checking_dir] = True;
      ScheduleActivity(directory_set[i]);
      directory_set[i]->last_check = tick_count;
   }

   /*  Reset the timeout for the next interval.  */
   if (SomeWindowMapped())
      XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
   else
      timer_suspended = True;
}


/*--------------------------------------------------------------------
 * TimerEventBrokenLinks
 *   This routine is called periodically.  It checks whether any
 *   desktop object is broken (i.e., the object it refers to no
 *   longer exists.
 *------------------------------------------------------------------*/

void
TimerEventBrokenLinks(
        XtPointer client_data,
        XtIntervalId *id )
{
   int i;

   DPRINTF2(("Directory::TimerEventBrokenLinks\n"));

   if (!dragActive)
   {
      /* go check the desktop objects */
      if (desktop_data->numIconsUsed > 0)
         CheckDesktop();
   }

   /*  Reset the timeout for the next interval.  */
   if (desktop_data->numIconsUsed > 0)
   {
     checkBrokenLinkTimerId = XtAppAddTimeOut( app_context,
                                               checkBrokenLink * 1000,
                                               TimerEventBrokenLinks,
                                               NULL );
   }
   else
   {
     checkBrokenLinkTimerId = None;
   }
}


/*====================================================================
 *
 * Background process scheduler
 *
 *   The routines below schedule background activity, making sure
 *   that there aren't too many processes running at the same time.
 *
 *==================================================================*/

/*--------------------------------------------------------------------
 *  ScheduleDirectoryActivity
 *    If there is any work to do for a directory, and if there is
 *    no backgroud process currently running for that directory,
 *    then fork a process to do the work.
 *------------------------------------------------------------------*/

static void
ScheduleDirectoryActivity(
   Directory *directory)
{
   static char *pname = "ScheduleActivity";
   ActivityStatus activity;
   PipeCallbackData *pipe_data;
   Boolean all_views_active;
   Boolean this_view_active;
   int i, j, k;
   int n_active, n_checking;
   int save_last_check = 0;
   FileMgrData *file_mgr_data;
   Boolean sticky;
   StickyProcDesc *p;
   int pipe_s2m_fd[2] = {-1, -1};  /* for msgs from backgroundnd proc (slave to master) */
   int pipe_m2s_fd[2] = {-1, -1};  /* for msgs to backgroundnd proc (master to slave) */
   pid_t pid = 0;
   char *s;
   int rc;

   /* If already active, don't start anything new. */
   if (directory->activity != activity_idle)
      return;

   /* Decide what to do next */
   for (activity = 0; activity < activity_idle; activity++)
      if (directory->busy[activity])
         break;

   /* If nothing to do, return */
   if (activity == activity_idle)
      return;

   DPRINTF2(("ScheduleActivity: activity %d, busy %c%c%c%c%c%c%c, dir %s\n",
             directory->activity,
             directory->busy[activity_writing_posinfo]? 'W': '-',
             directory->busy[activity_reading]?         'R': '-',
             directory->busy[activity_update_all]?      'A': '-',
             directory->busy[activity_update_some]?     'U': '-',
             directory->busy[activity_checking_links]?  'B': '-',
             directory->busy[activity_checking_desktop]? 'D': '-',
             directory->busy[activity_checking_dir]?    'C': '-',
             directory->directory_name));

   /* Don't start more than a certain number of background processed */
   n_active = 0;
   n_checking = 0;
   for (j = 0; j < directory_count; j++)
   {
      if (directory_set[j]->activity != activity_idle)
         n_active++;
      if (directory_set[j]->activity == activity_checking_links ||
          directory_set[j]->activity == activity_checking_dir)
         n_checking++;
   }
   if (dummy_directory->activity != activity_idle)
   {
      n_active++;
      n_checking++;
   }
   if (n_active >= maxDirectoryProcesses ||
       n_checking >= maxRereadProcesses)
   {
      DPRINTF2(("ScheduleActivity:  too many processes\n"));
      return;
   }

   /*
    * We don't want to start more than one background process per view.
    * In tree mode one view may show more than one directory.
    * Hence we go through the view list for this directory and for each
    * view, we check if the same view appears on the view list of some
    * other directory that currently has active background activity.
    * If all vies on this directory have other activity, then we won't
    * start anything new.
    */
   if (directory->numOfViews > 0)
   {
     all_views_active = True;
     for (i = 0; i < directory->numOfViews; i++)
     {
      /* get file_mgr_data for this view */
       file_mgr_data = directory->directoryView[i].file_mgr_data;

      /* see if the same view appears in the view list of a non-idle dir */
       this_view_active = False;
       for (j = 0; j < directory_count && !this_view_active; j++)
       {
         /* we are only interested in directories that are not idle */
         if (directory_set[j]->activity == activity_idle)
           continue;

         /* see if the view appears in the view list */
         for (k = 0; k < directory_set[j]->numOfViews; k++)
         {
           if (directory_set[j]->directoryView[k].file_mgr_data ==
               file_mgr_data)
           {
             this_view_active = True;
             break;
           }
         }
       }

       if (!this_view_active)
       {
         all_views_active = False;
         break;
       }
     }

     if (all_views_active)
     {
       DPRINTF2(("ScheduleActivity:  all views busy\n"));
       return;
     }
   }

   /* now we are ready to start the next activity */
   directory->activity = activity;
   if (activity == activity_reading ||
       activity == activity_update_all ||
       activity == activity_checking_dir ||
       activity == activity_checking_links)
   {
      save_last_check = directory->last_check;
      directory->last_check = tick_count;
   }

   /*
    * Special optimization for periodic background processes
    * (currently only used for activity_checking_dir):
    * Since this is done frequently, we don't want to fork new process each
    * time.  Hence, instead of exiting when it's done, the background process
    * is "sticky", i.e., it will stay around waiting for a message on stdin,
    * so it can be re-used the next time around.  A linked list of sticky
    * procs that are currently active is maintained in the ActivityTable.
    */
   sticky = ActivityTable[activity].sticky;
   if (sticky)
   {
      /* see if we can find an idle sticky proc that can do the work */
      for (p = ActivityTable[activity].sticky_procs; p; p = p->next)
         if (p->idle)
            break;
   }
   else
      p = NULL;

   if (p)
   {
      /* We found an idle sticky proc that can be used */
      DPRINTF2(("ScheduleActivity:  use sticky proc %ld\n", (long)p->child));

      /* Send the directory name to the sticky proc */

      if (PipeWriteString(p->pipe_m2s_fd, directory->path_name) < 0) {
        StickyProcDesc *d;

        /* the pipe is broken, remove the old proc then start a new one */
        for (d = ActivityTable[activity].sticky_procs; d && p; d = d->next) {
          if (d == p)
          {
            /* the proc listed 1st is dead, remove it */
            ActivityTable[activity].sticky_procs = p->next;
            XtFree((void *)p);
            p = NULL;
          }
          else if (d->next == p)
          {
            /* the process "p" is dead, remove it */
            d->next = p->next;
            XtFree((void *)p);
            p = NULL;
          }
        }
      }
      else
      {
        pipe_s2m_fd[0] = p->pipe_s2m_fd;
        pid = p->child;
        p->idle = False;
      }
   }


   if (!p)
   {
      /* Need to fork a new background process */

      /* create a pipe for reading data from the background proc */
      pipe(pipe_s2m_fd);

      /* creating a new sticky proc? */
      if (sticky)
      {
         /* also need a pipe for sending msgs to the sticky proc */
         pipe(pipe_m2s_fd);

         /* add entry to the list of sticky procs */
         p = (StickyProcDesc *) XtMalloc(sizeof(StickyProcDesc));
         p->next = ActivityTable[activity].sticky_procs;
         ActivityTable[activity].sticky_procs = p;

         p->pipe_s2m_fd = pipe_s2m_fd[0];
         p->pipe_m2s_fd = pipe_m2s_fd[1];
         p->idle = False;
      }

      /* fork a background process */
      pid = fork();

      if (pid == -1)
      {
          DBGFORK(("%s:  fork failed for activity %d: %s\n",
		    pname, activity, strerror(errno)));

          fprintf(stderr,
		"%s:  fork failed, ppid %d, pid %d, activity %d: error %d=%s\n",
		pname, getppid(), getpid(), activity, errno, strerror(errno));

	  directory->activity = activity_idle;
          directory->last_check = save_last_check;

          /* close unused pipe connections */
          close(pipe_s2m_fd[0]);    /* child won't read from this pipe */
          close(pipe_s2m_fd[1]);    /* parent won't write to this pipe */
          if (sticky)
	  {
             close(pipe_m2s_fd[1]); /* child won't write to this pipe */
             close(pipe_m2s_fd[0]); /* parent won't read from this pipe */
             p->pipe_s2m_fd = 0;
             p->pipe_m2s_fd = 0;
             p->idle = True;
	  }
	  return;
      }

      if (pid == 0)
      {
         /* child process */
         pid = getpid();
         DBGFORK(("%s:  child activity %d, s2m %d, m2s %d\n",
		  pname, activity, pipe_s2m_fd[1], pipe_m2s_fd[0]));

         /* close unused pipe connections */
         close(pipe_s2m_fd[0]);    /* child won't read from this pipe */
         if (sticky)
            close(pipe_m2s_fd[1]); /* child won't write to this pipe */

         /* run main routine for this activity from ActivityTable */
         for (;;)
         {
            rc = (*ActivityTable[activity].main)(pipe_s2m_fd[1],
                                                 directory, activity);
            if (!sticky || rc != 0)
               break;

            /* wait for a message in the pipe */
            s = PipeReadString(pipe_m2s_fd[0]);
            if (s == NULL)
               break;

            XtFree(directory->path_name);
            directory->path_name = s;

            DPRINTF2(("StickyActivity:  activity %d, dir %s\n", activity, s));
         }

         /* close pipes and end this process */
         close(pipe_s2m_fd[1]);
         if (sticky)
            close(pipe_m2s_fd[0]);

         DBGFORK(("%s:  completed activity %d, (rc %d)\n",pname, activity, rc));

         exit(rc);
      }

      DBGFORK(("%s:  forked child<%d> for activity %d, s2m %d, m2s %d\n",
		  pname, pid, activity, pipe_s2m_fd[0], pipe_m2s_fd[1]));

      /* parent process */
      if (sticky)
         p->child = pid;

      /*
       * If a directory read or update was started:
       * clear the modifile_list, now that the
       * background process has it's own copy.
       */
      if (activity == activity_reading ||
          activity == activity_update_all ||
          activity == activity_update_some)
      {
         for (i = 0; i < directory->modified_count; i++)
            XtFree(directory->modified_list[i]);
         XtFree((char *)directory->modified_list);

         directory->modified_count = 0;
         directory->modified_list = NULL;
      }

      /* close unused pipe connections */
      close(pipe_s2m_fd[1]);    /* parent won't write to this pipe */
      if (sticky)
         close(pipe_m2s_fd[0]); /* parent won't read from this pipe */

   }

   /* set up callback to get the pipe data */
   DPRINTF2(("ScheduleActivity:  setting up pipe callback\n"));
   pipe_data = (PipeCallbackData *)XtMalloc(sizeof(PipeCallbackData));
   pipe_data->directory = directory;
   pipe_data->child = pid;
   pipe_data->sticky_proc = p;
   pipe_data->activity = activity;

   XtAppAddInput(XtWidgetToApplicationContext(toplevel),
                 pipe_s2m_fd[0], (XtPointer)XtInputReadMask,
                 ActivityTable[activity].callback, (XtPointer)pipe_data);
}


/*--------------------------------------------------------------------
 *  ScheduleActivity
 *    See if any new background work should be started.
 *------------------------------------------------------------------*/

static void
ScheduleActivity(
   Directory *directory)
{
   int i;

   /* first try to schedule new activity for this directory */
   if (directory != NULL)
     ScheduleDirectoryActivity(directory);

   /* see if there is anything else we can schedule now */
   if (directory == NULL || directory->activity == activity_idle)
   {
      for (i = 0; i < directory_count; i++)
         if (directory_set[i] != directory)
            ScheduleDirectoryActivity(directory_set[i]);
   }
   ScheduleDirectoryActivity(dummy_directory);
 }

static void
SelectDesktopFile(
FileMgrData *file_mgr_data)
{
    DirectorySet *directory_data;
    FileViewData *file_view_data = NULL;
    int j;

    directory_data = file_mgr_data->directory_set[0];

    for (j = 0; j < directory_data->file_count; j++)
    {
        file_view_data = directory_data->file_view_data[j];

        if (file_view_data->filtered != True &&
            strcmp(file_mgr_data->desktop_file,
                   file_view_data->file_data->file_name) == 0)
        {
            SelectFile (file_mgr_data, file_view_data);
            break;
        }
    }
    ActivateSingleSelect(file_mgr_data->file_mgr_rec,
                         file_mgr_data->selection_list[0]->file_data->logical_type);
    if(file_view_data) {
        PositionFileView(file_view_data, file_mgr_data);
    }
}
