nsgml: Resolve symbol collision when building with SunStudio 12.1
[oweals/cde.git] / cde / programs / dtfile / Directory.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: Directory.c /main/18 1999/12/09 13:05:34 mgreess $ */
24 /************************************<+>*************************************
25  ****************************************************************************
26  *
27  *   FILE:           Directory.c
28  *
29  *   COMPONENT_NAME: Desktop File Manager (dtfile)
30  *
31  *   Description:    Directory processing functions used by the File Browser.
32  *
33  *   FUNCTIONS: CheckDesktop
34  *              CheckDesktopPipeCallback
35  *              CheckDesktopProcess
36  *              CheckListCmp
37  *              DirectoryBeginModify
38  *              DirectoryBusy
39  *              DirectoryEndModify
40  *              DirectoryFileModified
41  *              DirectoryGone
42  *              DirectoryModifyTime
43  *              FileData2toFileData
44  *              FileWindowMapUnmap
45  *              FindDirectory
46  *              FreeDirectory
47  *              FreeFileData
48  *              GetDirectoryLogicalType
49  *              GetDirectoryPositionInfo
50  *              GetLongName
51  *              InitializeDirectoryRead
52  *              InitializePositionFileName
53  *              PipeReadFileData
54  *              PipeReadPositionInfo
55  *              PipeWriteFileData
56  *              PipeWritePositionInfo
57  *              ReadDir
58  *              ReadDirectory
59  *              ReadDirectoryFiles
60  *              ReadDirectoryProcess
61  *              ReadFileData
62  *              ReadFileData2
63  *              ReaddirPipeCallback
64  *              RereadDirectory
65  *              ScheduleActivity
66  *              ScheduleDirectoryActivity
67  *              SetDirectoryPositionInfo
68  *              SkipRefresh
69  *              SomeWindowMapped
70  *              StickyProcIdle
71  *              TimerEvent
72  *              TimerEventBrokenLinks
73  *              TimerEventProcess
74  *              TimerPipeCallback
75  *              UpdateAllProcess
76  *              UpdateCachedDirectories
77  *              UpdateDirectory
78  *              UpdateDirectorySet
79  *              UpdateSomeProcess
80  *              WritePosInfoPipeCallback
81  *              WritePosInfoProcess
82  *              _ReadDir
83  *              SelectDesktopFile
84  *
85  *   (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
86  *   (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
87  *   (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
88  *   (c) Copyright 1993, 1994, 1995 Novell, Inc.
89  *
90  ****************************************************************************
91  ************************************<+>*************************************/
92
93 #include <stdio.h>
94 #include <sys/types.h>
95 #include <sys/stat.h>
96 #include <sys/time.h>
97 #include <grp.h>
98 #include <pwd.h>
99 #include <time.h>
100 #include <dirent.h>
101 #include <errno.h>
102 #include <signal.h>
103 #include <unistd.h>
104 #include <limits.h>
105 #include <string.h>
106 #include <assert.h>
107
108 #include <Xm/Xm.h>
109
110 #include <Dt/Connect.h>
111 #include <Dt/DtNlUtils.h>
112 #include <Dt/Dts.h>
113 #include <Dt/Icon.h>
114 #include <Tt/tttk.h>
115
116 #include "Encaps.h"
117 #include "FileMgr.h"
118 #include "Desktop.h"
119 #include "IconicPath.h"
120 #include "Main.h"
121 #include "SharedMsgs.h"
122 #include "SharedProcs.h"
123 #include "Prefs.h"
124
125 extern Boolean removingTrash;
126
127
128 /*--------------------------------------------------------------------
129  * Constants and Types
130  *------------------------------------------------------------------*/
131
132 /* File modes */
133 #define OPTION_OFF '-'
134 #define WRITE_PRIV 'w'
135 #define READ_PRIV  'r'
136 #define EXEC_PRIV  'x'
137
138 /* prefix for the name of the position info file */
139 #define POSITION_FILE_PREFIX  ".!dt"
140
141 /* kinds of messages sent through the pipe */
142 #define PIPEMSG_ERROR               1
143 #define PIPEMSG_FILEDATA            2
144 #define PIPEMSG_DONE                3
145 #define PIPEMSG_PATH_LOGICAL_TYPES  4
146 #define PIPEMSG_POSITION_INFO       5
147 #define PIPEMSG_FILEDATA2           6
148 #define PIPEMSG_FILEDATA3           7
149 #define PIPEMSG_DESKTOP_REMOVED     8
150 #define PIPEMSG_DESKTOP_CHANGED     9
151
152 #ifndef FILEDATABUF
153 #define FILEDATABUF 50
154 #endif /* FILEDATABUF */
155
156 #define NILL '\0'
157
158 /*
159  * Background activities, ordered by priority:
160  * (activity_idle must be the last one in the list!)
161  */
162 typedef enum
163 {
164   activity_writing_posinfo,  /* writing position information file */
165   activity_reading,          /* reading the directory */
166   activity_update_all,       /* updating the directory */
167   activity_update_some,      /* updating selected files */
168   activity_checking_links,   /* checking for broken links */
169   activity_checking_desktop, /* checking desktop objects */
170   activity_checking_dir,     /* checking if the directory has changed */
171   activity_idle              /* no background activity */
172 } ActivityStatus;
173
174 /*  The internal directory structure and directory set list  */
175
176 typedef struct
177 {
178    FileMgrData * file_mgr_data;
179    Boolean       mapped;
180 } DirectoryView;
181
182 typedef struct
183 {
184    char           * host_name;
185    char           * directory_name;
186    char           * path_name;
187    char           * tt_path_name;
188    Boolean          viewed;
189    ActivityStatus   activity;
190    Boolean          busy[activity_idle];
191    Boolean          errmsg_needed;
192    int              errnum;
193    time_t           modify_time;
194    int              last_check;
195    Boolean          link_check_needed;
196    int              file_count;
197    FileData       * file_data;
198    FileData       * new_data;
199    FileData       * dir_data;
200    int              path_count;
201    char          ** path_logical_types;
202    int              position_count;
203    PositionInfo   * position_info;
204    int              modify_begin;
205    Boolean          was_up_to_date;
206    int              modified_count;
207    char          ** modified_list;
208    int              numOfViews;
209    DirectoryView  * directoryView;
210 } Directory;
211
212
213 /* data for keeping track of sticky background procs */
214 typedef struct _spd
215 {
216    pid_t child;
217    int pipe_s2m_fd;
218    int pipe_m2s_fd;
219    Boolean idle;
220    struct _spd *next;
221 } StickyProcDesc;
222
223 /* data for callback routines that handle background processes */
224 typedef struct
225 {
226    Directory *directory;
227    pid_t child;
228    StickyProcDesc *sticky_proc;
229    ActivityStatus activity;
230 } PipeCallbackData;
231
232
233 /* background procedure */
234 typedef int (*DirBackgroundProc)(int, Directory *, ActivityStatus);
235
236 extern void _DtFlushIconFileCache(String path);
237
238
239 /*--------------------------------------------------------------------
240  * Static Function Declarations
241  *------------------------------------------------------------------*/
242
243 static void TimerEvent(
244                         XtPointer client_data,
245                         XtIntervalId *id);
246 static void ScheduleActivity(
247                         Directory *directory);
248 static int WritePosInfoProcess(
249                         int pipe_fd,
250                         Directory *directory,
251                         ActivityStatus activity);
252 static int ReadDirectoryProcess(
253                         int pipe_fd,
254                         Directory *directory,
255                         ActivityStatus activity);
256 static int UpdateAllProcess(
257                         int pipe_fd,
258                         Directory *directory,
259                         ActivityStatus activity);
260 static int UpdateSomeProcess(
261                         int pipe_fd,
262                         Directory *directory,
263                         ActivityStatus activity);
264 static int TimerEventProcess(
265                         int pipe_fd,
266                         Directory *directory,
267                         ActivityStatus activity);
268 static int CheckDesktopProcess(
269                         int pipe_fd,
270                         Directory *directory,
271                         ActivityStatus activity);
272 static void WritePosInfoPipeCallback(
273                         XtPointer client_data,
274                         int *fd,
275                         XtInputId *id);
276 static void ReaddirPipeCallback(
277                         XtPointer client_data,
278                         int *fd,
279                         XtInputId *id);
280 static void TimerPipeCallback(
281                         XtPointer client_data,
282                         int *fd,
283                         XtInputId *id);
284 static void CheckDesktopPipeCallback(
285                         XtPointer client_data,
286                         int *fd,
287                         XtInputId *id);
288 static void SelectDesktopFile(FileMgrData *fmd);
289
290
291
292 /*--------------------------------------------------------------------
293  * Static Data
294  *------------------------------------------------------------------*/
295
296 int maxDirectoryProcesses = 10;
297 int maxRereadProcesses = 5;
298 int maxRereadProcsPerTick = 1;
299
300 XtIntervalId checkBrokenLinkTimerId = None;
301
302 static Directory ** directory_set = NULL;
303 static int          directory_count = 0;
304 static int          directory_set_size = 0;
305 static char       * positionFileName = NULL;
306 static XtAppContext app_context = None;
307 static int          tickTime = 0;
308 static int          ticksBetweenLinkChecks = 0;
309 static Boolean      timer_suspended = False;
310 static int          tick_count = 0;
311 static int          lastLinkCheckTick = 0;
312 static Directory  dummy_dir_struct =
313 {
314   "dummy_host",
315   "dummy_directory",
316   "dummy_path",
317   NULL,
318   False,
319   activity_idle
320 };
321 static Directory *dummy_directory = &dummy_dir_struct;
322
323 static struct
324 {
325    DirBackgroundProc main;
326    XtInputCallbackProc callback;
327    Boolean sticky;
328    StickyProcDesc *sticky_procs;
329 } ActivityTable[] =
330 {
331   { WritePosInfoProcess,  WritePosInfoPipeCallback, False,NULL },/* writing_posinfo*/
332   { ReadDirectoryProcess, ReaddirPipeCallback, False,NULL },     /* reading */
333   { UpdateAllProcess,     ReaddirPipeCallback, False,NULL },     /* update_all */
334   { UpdateSomeProcess,    ReaddirPipeCallback, False,NULL },     /* update_some */
335   { TimerEventProcess,    TimerPipeCallback, False,NULL },       /* checking_links */
336   { CheckDesktopProcess,  CheckDesktopPipeCallback, False,NULL },/* checking_desktop */
337   { TimerEventProcess,    TimerPipeCallback, True, NULL },  /* checking_dir */
338   { NULL,                 NULL, False, NULL }               /* idle */
339 };
340
341
342 /*====================================================================
343  *
344  * Initialization routines
345  *
346  *==================================================================*/
347
348 /*--------------------------------------------------------------------
349  *  InitializePositionFileName
350  *      Initialize the name under which the position info is stored.
351  *------------------------------------------------------------------*/
352
353 static void
354 InitializePositionFileName(void)
355 {
356    struct passwd * pwInfo;
357
358    /* Determine the name under which the position info is stored */
359    if (positionFileName == NULL)
360    {
361
362       pwInfo = getpwuid(getuid());
363       positionFileName = XtMalloc(strlen(pwInfo->pw_name) +
364                                   strlen(POSITION_FILE_PREFIX) + 1);
365       sprintf(positionFileName, "%s%s", POSITION_FILE_PREFIX, pwInfo->pw_name);
366    }
367 }
368
369
370 /*--------------------------------------------------------------------
371  *  InitializeDirectoryRead
372  *      Set up a timer used to automatically check the read in
373  *      directories to see if they have been modified.
374  *------------------------------------------------------------------*/
375
376 void
377 InitializeDirectoryRead(
378         Widget widget )
379
380 {
381    /* remeber application context */
382    app_context = XtWidgetToApplicationContext(widget);
383
384    /* start timer to check for modified directories and broken links */
385    tick_count = lastLinkCheckTick = 0;
386
387    if (rereadTime != 0)
388    {
389      tickTime = rereadTime;
390      ticksBetweenLinkChecks = checkBrokenLink / rereadTime;
391    }
392    else if (checkBrokenLink != 0)
393    {
394      tickTime = checkBrokenLink;
395      ticksBetweenLinkChecks = 1;
396    }
397    else
398      tickTime = 0;
399
400    if (tickTime != 0)
401       XtAppAddTimeOut (app_context, tickTime * 1000, TimerEvent, NULL);
402
403    /* start timer to check for broken desktop objects */
404    if( desktop_data->numIconsUsed > 0
405        && checkBrokenLink != 0
406      )
407    {
408      checkBrokenLinkTimerId = XtAppAddTimeOut( app_context,
409                                                checkBrokenLink * 1000,
410                                                TimerEventBrokenLinks,
411                                                NULL);
412    }
413    else
414    {
415      checkBrokenLinkTimerId = None;
416    }
417 }
418
419
420 /*====================================================================
421  *
422  * Utiltiy functions
423  *
424  *==================================================================*/
425
426 /*--------------------------------------------------------------------
427  *  FindDirectory
428  *    Given a host & directory name, find the directory in our cache.
429  *------------------------------------------------------------------*/
430
431 static Directory *
432 FindDirectory(
433         char *host_name,
434         char *directory_name)
435 {
436    int i;
437
438    /* See if the directory is in the directory set.  First, compare    */
439    /* the names from the directory entries ONLY.  There will be one    */
440    /* directory entry for every directory of a different name.  This   */
441    /* may mean that there is more than one directory entry for a       */
442    /* single directory (ie. if there is a directory that is a link or  */
443    /* a mount point in the system).                                    */
444    /* If this doesn't succeed, we may be getting a ToolTalk resolved   */
445    /* name. So run the comparison again but this time compare the      */
446    /* incoming name to the tt_path_name in the directory entries.      */
447    /* This algorithm has a limitation in that if ToolTalk has resolved */
448    /* a path name, the match will occur for the first entry in the     */
449    /* directory set whose tt_path_name matches our name, this MAY NOT  */
450    /* be the directory where the activity originated.  The user should */
451    /* only notice in the case where automatic refresh is turned off.   */
452    for (i = 0; i < directory_count; i++)
453    {
454       if (strcmp (host_name, directory_set[i]->host_name) == 0 &&
455           strcmp (directory_name, directory_set[i]->directory_name) == 0)
456       {
457          return directory_set[i];
458       }
459    }
460
461    for (i = 0; i < directory_count; i++)
462    {
463       if (directory_set[i]->tt_path_name != NULL &&
464           strcmp (host_name, home_host_name) == 0 &&
465           strcmp (directory_name, directory_set[i]->tt_path_name) == 0)
466       {
467          return directory_set[i];
468       }
469    }
470
471    /* not found */
472    return NULL;
473 }
474
475
476 /*--------------------------------------------------------------------
477  *  DirectoryGone
478  *    Check if a directory has been removed from the cache.
479  *------------------------------------------------------------------*/
480
481 static Boolean
482 DirectoryGone(
483         Directory *directory)
484 {
485    int i;
486
487    for (i = 0; i < directory_count; i++)
488       if (directory_set[i] == directory)
489          return False;
490
491    return True;
492 }
493
494
495 /*--------------------------------------------------------------------
496  *  FreeFileData
497  *      Free FileData structure.
498  *------------------------------------------------------------------*/
499
500 void
501 FreeFileData(
502         FileData *file_data,
503         Boolean free_all)
504 {
505    XtFree(file_data->file_name);
506    file_data->file_name = NULL;
507
508    XtFree(file_data->action_name);
509    file_data->action_name = NULL;
510
511    DtDtsFreeDataType(file_data->logical_type);
512    file_data->logical_type = NULL;
513
514    if ( file_data->final_link != NULL &&
515         file_data->final_link != file_data->link)
516    {
517       XtFree(file_data->final_link);
518       file_data->final_link = NULL;
519    }
520
521    if (file_data->link != NULL )
522    {
523      XtFree(file_data->link);
524      file_data->link = NULL;
525    }
526
527    if (free_all)
528       XtFree((char *)file_data);
529 }
530
531
532 /*--------------------------------------------------------------------
533  *  FreeDirectory
534  *      Free Directory structure.
535  *------------------------------------------------------------------*/
536
537 static void
538 FreeDirectory(
539         Directory *directory)
540 {
541    int i;
542    FileData *file_data, *next_file_data;
543
544    if( directory == NULL )
545      return;
546
547    XtFree (directory->host_name);
548    directory->host_name = NULL;
549
550    XtFree (directory->directory_name);
551    directory->directory_name = NULL;
552
553    XtFree (directory->path_name);
554    directory->path_name = NULL;
555
556    XtFree (directory->tt_path_name);
557    directory->tt_path_name = NULL;
558
559    for (i=0; i < directory->path_count; i++)
560       DtDtsFreeDataType(directory->path_logical_types[i]);
561    XtFree ((char *) directory->path_logical_types);
562    directory->path_logical_types = NULL;
563
564    for (i = 0; i < directory->position_count; i++)
565       XtFree(directory->position_info[i].name);
566    XtFree ((char *) directory->position_info);
567    directory->position_info = NULL;
568
569    for (i = 0; i < directory->modified_count; i++)
570       XtFree(directory->modified_list[i]);
571    XtFree ((char *) directory->modified_list);
572    directory->modified_list = NULL;
573
574    XtFree ((char *) directory->directoryView);
575    directory->directoryView = NULL;
576
577    if (directory->dir_data)
578    {
579       FreeFileData(directory->dir_data, True);
580       directory->dir_data = NULL;
581    }
582
583    file_data = directory->file_data;
584    while (file_data != NULL)
585    {
586       next_file_data = file_data->next;
587       FreeFileData(file_data, True);
588       file_data = next_file_data;
589    }
590    directory->file_data = NULL;
591
592    XtFree((char *) directory);
593 }
594
595
596 /*--------------------------------------------------------------------
597  *  SomeWindowMapped
598  *    Check if any cached directory is currently being viewed in
599  *    a window that is mapped (not iconified).  (If there is none,
600  *    we won't need to set a refresh timer).
601  *------------------------------------------------------------------*/
602
603 static Boolean
604 SomeWindowMapped(void)
605
606 {
607    int i, j;
608
609    for (i = 0; i < directory_count; i++)
610       for (j = 0; j < directory_set[i]->numOfViews; j++)
611          if (directory_set[i]->directoryView[j].mapped)
612             return True;
613
614    return False;
615 }
616
617
618 /*====================================================================
619  *
620  * Routines for reading a directory
621  *
622  *==================================================================*/
623
624 /*--------------------------------------------------------------------
625  *  PipeWrite...
626  *  PipeRead...
627  *      Directories are read in a background process that is connected
628  *      to the main dtfile process by a pipe.
629  *      The routines below are used to send directory entry information
630  *      through the pipe from the background to the main process.
631  *------------------------------------------------------------------*/
632
633 /* write FileData to the pipe */
634 void
635 PipeWriteFileData(
636         int fd,
637         FileData *file_data)
638 {
639    write(fd, file_data, sizeof(FileData));
640    PipeWriteString(fd, file_data->file_name);
641    PipeWriteString(fd, file_data->action_name);  /* @@@ ??? */
642    PipeWriteString(fd, file_data->logical_type);
643    if (file_data->link)
644       PipeWriteString(fd, file_data->link);
645    if (file_data->final_link)
646       PipeWriteString(fd, file_data->final_link);
647 }
648
649
650 /* read FileData from the pipe */
651 static FileData *
652 PipeReadFileData(
653         int fd)
654 {
655    FileData *file_data;
656    int n;
657
658    file_data = (FileData *)XtCalloc(1,sizeof(FileData));
659    n = PipeRead(fd, file_data, sizeof(FileData));
660    if (n < sizeof(FileData))
661    {
662       fprintf(stderr, "PipeReadFileData: n = %d, expected %ld\n",
663               n, (long)sizeof(FileData));
664    }
665    file_data->file_name = PipeReadString(fd);
666    file_data->action_name = PipeReadString(fd);
667    file_data->logical_type = PipeReadString(fd);
668    if (file_data->link)
669       file_data->link = PipeReadString(fd);
670    if (file_data->final_link)
671       file_data->final_link = PipeReadString(fd);
672
673    /* return the file data */
674    return file_data;
675 }
676
677 FileData *
678 FileData2toFileData(
679         FileData2 *file_data2,
680         int *l)
681 {
682    FileData *file_data;
683    int n;
684    char file_name_buf[MAXPATHLEN];
685    char action_name_buf[MAXPATHLEN];
686    char logical_type_buf[MAXPATHLEN];
687    char link_buf[MAXPATHLEN];
688    char final_link_buf[MAXPATHLEN];
689    char *textptr = file_data2->text;
690
691    file_data = (FileData *)XtCalloc(1,sizeof(FileData));
692
693    strncpy(file_name_buf, textptr, n = file_data2->file_name);
694    file_name_buf[n] = NILL;
695    textptr += n;
696
697    strncpy(action_name_buf, textptr, n = file_data2->action_name);
698    action_name_buf[n] = NILL;
699    textptr += n;
700
701    strncpy(logical_type_buf, textptr, n = file_data2->logical_type);
702    logical_type_buf[n] = NILL;
703    textptr += n;
704
705    strncpy(link_buf, textptr, n = file_data2->link);
706    link_buf[n] = NILL;
707    textptr += n;
708
709    strncpy(final_link_buf, textptr, n = file_data2->final_link);
710    final_link_buf[n] = NILL;
711    textptr += n;
712
713    file_data->next              = NULL;
714    file_data->file_name         = XtNewString(file_name_buf);
715    file_data->action_name       = file_data2->action_name
716                                 ? XtNewString(action_name_buf)
717                                 : NULL;
718    file_data->physical_type     = file_data2->physical_type;
719    file_data->logical_type      = XtNewString(logical_type_buf);
720    file_data->errnum            = file_data2->errnum;
721    file_data->stat              = file_data2->stat;
722
723    file_data->link              = file_data2->link
724                                 ? XtNewString(link_buf)
725                                 : NULL;
726
727    file_data->final_link        = file_data2->final_link
728                                 ? XtNewString(final_link_buf)
729                                 : NULL;
730    file_data->is_subdir         = file_data2->is_subdir;
731    file_data->is_broken         = file_data2->is_broken;
732
733    *l = sizeof(*file_data2) - sizeof(file_data2->text)
734          + file_data2->file_name + file_data2->action_name
735          + file_data2->logical_type + file_data2->link
736          + file_data2->final_link;
737
738    *l = (*l + sizeof(char *) - 1) & ~(sizeof(char *) - 1);
739
740    /* return the file data */
741    return file_data;
742 }
743
744 /* write PositionInfo to the pipe */
745 void
746 PipeWritePositionInfo(
747         int fd,
748         PositionInfo *position_info)
749 {
750    PipeWriteString(fd, position_info->name);
751    write(fd, &position_info->x, sizeof(Position));
752    write(fd, &position_info->y, sizeof(Position));
753    write(fd, &position_info->stacking_order, sizeof(int));
754 }
755
756
757 /* read PositionInfo from the pipe */
758 static void
759 PipeReadPositionInfo(
760         int fd,
761         PositionInfo *position_info)
762 {
763    position_info->name = PipeReadString(fd);
764    PipeRead(fd, &position_info->x, sizeof(Position));
765    PipeRead(fd, &position_info->y, sizeof(Position));
766    PipeRead(fd, &position_info->stacking_order, sizeof(int));
767 }
768
769 /*--------------------------------------------------------------------
770  *  ReadFileData
771  *    Given a path name, return FileData for a file.
772  *------------------------------------------------------------------*/
773
774 FileData *
775 ReadFileData(
776         char *full_directory_name,
777         char *file_name)
778 {
779    FileData *file_data;
780    char full_file_name[MAX_PATH];
781    char link_file_name[MAX_PATH];
782    char link_path[MAX_PATH];
783    int link_count;
784    char ** link_list;
785    int link_len;
786    char * end;
787    char * ptr;
788    Boolean recursive_link_found;
789    struct stat stat_buf;
790    struct stat stat_buf2;
791    int stat_result;
792    int stat_errno;
793    int i;
794
795    /*  Allocate a new file structure.  */
796    file_data = (FileData *)XtMalloc(sizeof(FileData));
797
798    /* get the full name of the file */
799    strcpy (full_file_name, full_directory_name);
800    if (file_name)
801    {
802       /* append file name to the directory */
803       if (strcmp(full_directory_name,"/") != 0)
804          strcat (full_file_name, "/");
805       strcat (full_file_name, file_name);
806    }
807    else
808    {
809       /* no file name passed: use last component of directory */
810       file_name = strrchr(full_file_name, '/');
811       if (file_name > full_file_name)
812          file_name++;
813       else
814          file_name = NULL;
815    }
816
817    /* Follow symbolic links to their ultimate destination */
818    link_count = 0;
819    link_list = NULL;
820    recursive_link_found = False;
821    strcpy(link_file_name, full_file_name);
822
823    stat_result = lstat (link_file_name, &stat_buf);
824    if (stat_result == 0 && (stat_buf.st_mode & S_IFMT) == S_IFLNK)
825    {
826      while ((link_len = readlink(link_file_name, link_path, MAX_PATH - 1)) > 0)
827      {
828        link_path[link_len] = 0;
829        link_list = (char **)XtRealloc((char *)link_list, sizeof(char *) *
830                                       (link_count + 2));
831
832        /* Force the link to be an absolute path, if necessary */
833        if (link_path[0] != '/')
834        {
835          /* Relative paths are relative to the current directory */
836          end = strrchr(link_file_name, '/') + 1;
837          *end = '\0';
838          strcat(link_file_name, link_path);
839        }
840        else
841          strcpy(link_file_name, link_path);
842
843        /* Check for a recursive loop; abort if found */
844        for (i = 0; i < link_count; i++)
845        {
846          if (strcmp(link_file_name, link_list[i]) == 0)
847          {
848            /* Back up to last non-recursive portion */
849            strcpy(link_file_name, link_list[link_count - 1]);
850            recursive_link_found = True;
851            break;
852          }
853        }
854
855        if (recursive_link_found)
856          break;
857
858        link_list[link_count++] = XtNewString(link_file_name);
859        link_list[link_count] = NULL;
860      }
861
862      /* try to stat the file that the link points to */
863      if (stat (link_file_name, &stat_buf2) == 0)
864      {
865        /* replace lstat result with the stat */
866        memcpy(&stat_buf, &stat_buf2, sizeof(struct stat));
867      }
868    }
869    stat_errno = errno;
870
871    /* fill in the FileData structure with the information we found */
872    file_data->next = NULL;
873    file_data->file_name = XtNewString(file_name? file_name: ".");
874    file_data->logical_type = NULL;
875    file_data->is_subdir = False;
876    file_data->action_name = NULL;
877
878    if (link_list)
879    {
880       file_data->link = XtNewString( link_list[0] );
881       file_data->final_link = XtNewString( link_list[link_count - 1] );
882       for (i = 0; i < link_count; i++)
883          XtFree(link_list[i]);
884       XtFree((char *)link_list);
885    } else
886       file_data->link = file_data->final_link = NULL;
887
888    if (stat_result == 0)
889    {
890       file_data->errnum = 0;
891       file_data->stat = stat_buf;
892
893       /*  Find and set the physical type of the file  */
894
895       if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
896       {
897          file_data->physical_type = DtDIRECTORY;
898          if (file_name == NULL ||
899              strcmp(file_name, ".") != 0 && strcmp(file_name, "..") != 0)
900          {
901             file_data->is_subdir = True;
902          }
903       }
904       else if ((stat_buf.st_mode & S_IFMT) == S_IFREG)
905       {
906          if ((stat_buf.st_mode & S_IXUSR) ||
907              (stat_buf.st_mode & S_IXGRP) ||
908              (stat_buf.st_mode & S_IXOTH))
909             file_data->physical_type = DtEXECUTABLE;
910          else
911             file_data->physical_type = DtDATA;
912       }
913       else
914          file_data->physical_type = DtDATA;
915
916       /*  Find and set the logical type of the file  */
917       if ((stat_buf.st_mode & S_IFMT) == S_IFLNK)
918       {
919          file_data->is_broken = True;
920          if (recursive_link_found)
921             file_data->logical_type = XtNewString(LT_RECURSIVE_LINK);
922          else
923             file_data->logical_type = XtNewString(LT_BROKEN_LINK);
924       }
925       else
926       {
927          file_data->is_broken = False;
928          if (file_data->link)
929             file_data->logical_type = (char *) DtDtsDataToDataType(
930                                                  file_data->link,
931                                                  NULL, 0, &stat_buf,
932                                                  file_data->final_link, NULL,
933                                                  NULL);
934          else
935             file_data->logical_type = (char *) DtDtsDataToDataType(
936                                                  full_file_name,
937                                                  NULL, 0, &stat_buf,
938                                                  NULL, NULL,
939                                                  NULL);
940 #if defined( DATATYPE_IS_FIXED )
941 #else
942          /* The problem here is there isn't a way for user to mask
943             only the OWNER READ bit in the MODE field of dtfile.dt file.
944             If the MODE field set to d&!r Then all READ permission
945             (S_IRUSR, S_IRGRP and S_IROTH)
946             bits has to be off in order for the above data typing to work.
947             Also data typing is unable to detect when the directory is not
948             the owners and only has execute permission by that owner.
949             The work around is manually checking it ourselves.
950             When the data typing code is fixed, please remove this check.
951          */
952          if( S_ISDIR( stat_buf.st_mode ) &&
953             (strcmp (file_data->logical_type, LT_DIRECTORY) == 0))
954          {
955            if( strcmp( file_name, ".." ) != 0
956                && strcmp( file_name, "." ) != 0 )
957            {
958              char * fullPathName;
959
960              if( file_data->link )
961                fullPathName = file_data->link;
962              else
963                fullPathName = full_file_name;
964
965              if( access( fullPathName, R_OK ) != 0 )
966              {
967                XtFree( file_data->logical_type );
968                file_data->logical_type = XtNewString( LT_FOLDER_LOCK );
969              }
970              else if( access( fullPathName, W_OK ) != 0 )
971              {
972                XtFree( file_data->logical_type );
973                file_data->logical_type = XtNewString( LT_NON_WRITABLE_FOLDER );
974              }
975            }
976          }
977 #endif
978       }
979
980       if(DtActionExists(file_data->logical_type))
981       {
982          file_data->action_name = (char *)DtActionLabel(file_data->file_name);
983       }
984       else
985       {
986          char *ptr = DtDtsDataTypeToAttributeValue(file_data->logical_type,
987                                                    DtDTS_DA_LABEL,
988                                                    NULL);
989          if (ptr)
990          {
991             file_data->action_name = XtNewString(ptr);
992             DtDtsFreeAttributeValue(ptr);
993          }
994       }
995    }
996    else
997    {
998       /* couldn't stat the file */
999       file_data->errnum = stat_errno;
1000       memset(&file_data->stat, 0, sizeof(file_data->stat));
1001       file_data->physical_type = DtUNKNOWN;
1002       file_data->is_broken = True;
1003       file_data->logical_type = XtNewString(DtDEFAULT_DATA_FT_NAME);
1004    }
1005
1006    return file_data;
1007 }
1008
1009
1010 /*--------------------------------------------------------------------
1011  *  ReadFileData2
1012  *    Given a path name, return FileData for a file.
1013  *------------------------------------------------------------------*/
1014
1015 int
1016 ReadFileData2(
1017         FileData2 *file_data2,
1018         char *full_directory_name,
1019         char *file_name,
1020         Boolean IsToolBox)
1021 {
1022    char full_file_name[MAX_PATH];
1023    char link_file_name[MAX_PATH];
1024    char link_path[MAX_PATH];
1025    char file_name_buf[MAXPATHLEN];
1026    char action_name_buf[MAXPATHLEN];
1027    char logical_type_buf[MAXPATHLEN];
1028    char link_buf[MAXPATHLEN];
1029    char final_link_buf[MAXPATHLEN];
1030    int link_count;
1031    char ** link_list;
1032    int link_len;
1033    char * end;
1034    char * ptr;
1035    Boolean recursive_link_found;
1036    struct stat stat_buf;
1037    int stat_result;
1038    int stat_errno;
1039    int i;
1040
1041    /*  Allocate a new file structure.  */
1042
1043    /* get the full name of the file */
1044    strcpy (full_file_name, full_directory_name);
1045
1046    if (file_name)
1047    {
1048       /* append file name to the directory */
1049       if (strcmp(full_directory_name,"/") != 0)
1050          strcat (full_file_name, "/");
1051       strcat (full_file_name, file_name);
1052    }
1053    else
1054    {
1055       /* no file name passed: use last component of directory */
1056       file_name = strrchr(full_file_name, '/');
1057       if (file_name > full_file_name)
1058          file_name++;
1059       else
1060          file_name = NULL;
1061    }
1062
1063    /* Follow symbolic links to their ultimate destination */
1064    link_count = 0;
1065    link_list = NULL;
1066    recursive_link_found = False;
1067    strcpy(link_file_name, full_file_name);
1068
1069    stat_result = lstat (link_file_name, &stat_buf);
1070    if ((stat_buf.st_mode & S_IFMT) == S_IFLNK)
1071    {
1072      while ((link_len = readlink(link_file_name, link_path, MAX_PATH - 1)) > 0)
1073      {
1074        link_path[link_len] = 0;
1075        link_list = (char **)XtRealloc((char *)link_list, sizeof(char *) *
1076                                       (link_count + 2));
1077
1078        /* Force the link to be an absolute path, if necessary */
1079        if (link_path[0] != '/')
1080        {
1081          /* Relative paths are relative to the current directory */
1082          end = strrchr(link_file_name, '/') + 1;
1083          *end = '\0';
1084          strcat(link_file_name, link_path);
1085        }
1086        else
1087          strcpy(link_file_name, link_path);
1088
1089        /* Check for a recursive loop; abort if found */
1090        for (i = 0; i < link_count; i++)
1091        {
1092          if (strcmp(link_file_name, link_list[i]) == 0)
1093          {
1094            /* Back up to last non-recursive portion */
1095            strcpy(link_file_name, link_list[link_count - 1]);
1096            recursive_link_found = True;
1097            break;
1098          }
1099        }
1100
1101        if (recursive_link_found)
1102          break;
1103
1104        link_list[link_count++] = XtNewString(link_file_name);
1105        link_list[link_count] = NULL;
1106      }
1107
1108      if ((stat_result = stat (link_file_name, &stat_buf)) != 0)
1109      {
1110       /* probably a broken link; try lstat */
1111        stat_result = lstat (full_file_name, &stat_buf);
1112        strcpy(link_file_name, full_file_name);
1113      }
1114    }
1115    stat_errno = errno;
1116
1117    /* fill in the FileData2 structure with the information we found */
1118    file_data2->next = NULL;
1119    strcpy(file_name_buf, (file_name ? file_name : "."));
1120    logical_type_buf[0] = NILL;
1121    file_data2->is_subdir = False;
1122    action_name_buf[0] = NILL;
1123
1124    if (link_list)
1125    {
1126       strcpy(link_buf, link_list[0]);
1127       strcpy(final_link_buf, link_list[link_count - 1]);
1128       for (i = 0; i < link_count; i++) {
1129          XtFree(link_list[i]);
1130       }
1131       XtFree((char *)link_list);
1132    } else {
1133       final_link_buf[0] = NILL;
1134       link_buf[0] = NILL;
1135    }
1136
1137    if (stat_result == 0)
1138    {
1139       file_data2->errnum = 0;
1140       file_data2->stat = stat_buf;
1141
1142       /*  Find and set the physical type of the file  */
1143
1144       if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
1145       {
1146          file_data2->physical_type = DtDIRECTORY;
1147          if (file_name == NULL ||
1148              strcmp(file_name, ".") != 0 && strcmp(file_name, "..") != 0)
1149          {
1150             file_data2->is_subdir = True;
1151          }
1152       }
1153       else if ((stat_buf.st_mode & S_IFMT) == S_IFREG)
1154       {
1155          if ((stat_buf.st_mode & S_IXUSR) ||
1156              (stat_buf.st_mode & S_IXGRP) ||
1157              (stat_buf.st_mode & S_IXOTH))
1158             file_data2->physical_type = DtEXECUTABLE;
1159          else
1160             file_data2->physical_type = DtDATA;
1161       }
1162       else
1163          file_data2->physical_type = DtDATA;
1164
1165       /*  Find and set the logical type of the file  */
1166       if ((stat_buf.st_mode & S_IFMT) == S_IFLNK)
1167       {
1168          file_data2->is_broken = True;
1169          if (recursive_link_found)
1170             strcpy(logical_type_buf, LT_RECURSIVE_LINK);
1171          else
1172             strcpy(logical_type_buf, LT_BROKEN_LINK);
1173       }
1174       else
1175       {
1176          char *ptr;
1177
1178          file_data2->is_broken = False;
1179          if (link_buf[0] == NILL)
1180             ptr = (char *) DtDtsDataToDataType(  full_file_name,
1181                                                  NULL, 0, &stat_buf,
1182                                                  NULL, NULL,
1183                                                  NULL);
1184          else
1185             ptr = (char *) DtDtsDataToDataType(  link_buf,
1186                                                  NULL, 0, &stat_buf,
1187                                                  final_link_buf, NULL,
1188                                                  NULL);
1189
1190 #if defined( DATATYPE_IS_FIXED )
1191          strcpy(logical_type_buf, ptr);
1192          free(ptr);
1193 #else
1194          /* The problem here is there isn't a way for user to mask
1195             only the OWNER READ bit in the MODE field of dtfile.dt file.
1196             If the MODE field set to d&!r Then all READ permission
1197             (S_IRUSR, S_IRGRP and S_IROTH)
1198             bits has to be off in order for the above data typing to work.
1199             Also data typing is unable to detect when the directory is not
1200             the owners and only has execute permission by that owner.
1201             The work around is manually checking it ourselves.
1202             When the data typing code is fixed, please remove this check.
1203          */
1204          if( !IsToolBox && S_ISDIR( stat_buf.st_mode ) &&
1205             (strcmp (ptr, LT_DIRECTORY) == 0))
1206          {
1207            if( strcmp( file_name, ".." ) != 0
1208                && strcmp( file_name, "." ) != 0 )
1209            {
1210              char * fullPathName;
1211
1212              if( link_buf[0] == NILL )
1213                fullPathName = full_file_name;
1214              else
1215                fullPathName = link_buf;
1216
1217              if( access( fullPathName, R_OK ) != 0 )
1218              {
1219                free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
1220                strcpy( logical_type_buf, LT_FOLDER_LOCK );
1221              }
1222              else if( access( fullPathName, W_OK ) != 0 )
1223              {
1224                free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
1225                strcpy( logical_type_buf, LT_NON_WRITABLE_FOLDER );
1226              }
1227              else
1228              {
1229                strcpy( logical_type_buf, ptr );
1230                free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
1231              }
1232            }
1233            else
1234            {
1235              strcpy( logical_type_buf, ptr );
1236              free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
1237            }
1238          }
1239          else
1240          {
1241            strcpy( logical_type_buf, ptr );
1242            free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
1243          }
1244 #endif
1245       }
1246
1247       if( DtActionExists(logical_type_buf) )
1248       {
1249         char *ptr = (char *)DtActionLabel(file_name_buf);
1250         strcpy(action_name_buf, ptr);
1251         free(ptr);
1252       }
1253       else
1254       {
1255         char *ptr = DtDtsDataTypeToAttributeValue(logical_type_buf,
1256                                                   DtDTS_DA_LABEL,
1257                                                   NULL);
1258         if (ptr)
1259         {
1260           strcpy(action_name_buf, ptr);
1261           DtDtsFreeAttributeValue(ptr);
1262         }
1263       }
1264    }
1265    else
1266    {
1267       /* couldn't stat the file */
1268       file_data2->errnum = stat_errno;
1269       memset(&file_data2->stat, 0, sizeof(file_data2->stat));
1270       file_data2->physical_type = DtUNKNOWN;
1271       file_data2->is_broken = True;
1272       strcpy(logical_type_buf, DtDEFAULT_DATA_FT_NAME);
1273    }
1274
1275         strcpy(file_data2->text, file_name_buf);
1276         file_data2->file_name = strlen(file_name_buf);
1277
1278         strcat(file_data2->text, action_name_buf);
1279         file_data2->action_name = strlen(action_name_buf);
1280
1281         strcat(file_data2->text, logical_type_buf);
1282         file_data2->logical_type = strlen(logical_type_buf);
1283
1284         strcat(file_data2->text, link_buf);
1285         file_data2->link = strlen(link_buf);
1286
1287         strcat(file_data2->text, final_link_buf);
1288         file_data2->final_link = strlen(final_link_buf);
1289
1290         i = sizeof(*file_data2) - sizeof(file_data2->text)
1291                 + file_data2->file_name + file_data2->action_name
1292                 + file_data2->logical_type + file_data2->link
1293                 + file_data2->final_link;
1294
1295         i = (i + sizeof(char *) - 1) & ~(sizeof(char *) - 1);
1296
1297         /*
1298          * This data marshalling operation relies on char[BUFSIZ]
1299          * being large enough for all the text pieces.  However,
1300          * BUFSIZ has nothing to do with the above operations so
1301          * we'll do this assert for now.
1302          */
1303         assert( (i <= sizeof(FileData2)) );
1304
1305         return i;
1306 }
1307
1308 /*--------------------------------------------------------------------
1309  *  GetTTPath
1310  *      Resolves the links in the path.
1311  *------------------------------------------------------------------*/
1312
1313 char *
1314 GetTTPath(char *path)
1315 {
1316    Tt_message dummy_msg;
1317    char *tmp, *tt_path;
1318    Tt_status tt_status;
1319
1320    dummy_msg = tt_message_create();
1321    tt_status = tt_message_file_set(dummy_msg, path);
1322    tmp = tt_message_file(dummy_msg);
1323
1324    tt_path = XtNewString(tmp);
1325
1326    tt_free(tmp);
1327    tt_message_destroy(dummy_msg);
1328
1329    return tt_path;
1330 }
1331
1332
1333 static int
1334 ReadDirectoryProcess(
1335         int pipe_fd,
1336         Directory *directory,
1337         ActivityStatus activity)
1338 {
1339 #ifdef DT_PERFORMANCE
1340    struct timeval update_time_s;
1341    struct timeval update_time_f;
1342 #endif
1343    char *host_name = directory->host_name;
1344    char *directory_name = directory->directory_name;
1345    struct stat stat_buf;
1346    long modify_time;
1347    int path_count;
1348    char **path_logical_types;
1349    char *full_directory_name;
1350    char *tt_path;
1351    DIR *dirp;
1352    struct dirent * dp;
1353    Boolean inDtDir;
1354    FileData *file_data;
1355    FileData2 file_data2;
1356    Boolean done;
1357    Boolean update_due;
1358    short file_data_count = 0;
1359    int i;
1360    char * ptr;
1361    char * namePtr;
1362    char file_name[MAX_PATH];
1363    int position_count;
1364    FILE * fptr;
1365    int x, y, stacking_order;
1366    short pipe_msg;
1367    int rc;
1368    char file_data_buffer[FILEDATABUF * sizeof(FileData2)];
1369    char *file_data_buf_ptr = file_data_buffer;
1370    char *file_data_count_ptr;
1371    struct timeval time1, time2;
1372    long diff;
1373    char *ptrOrig;
1374
1375    DPRINTF(("ReadDirectoryProcess(%d, \"%s\", \"%s\")\n",
1376             pipe_fd, host_name, directory_name));
1377
1378    /*
1379     * Get the logical data type of all components of the path;
1380     * We need only the last path component for (1) the tree root icon,
1381     * and (2) the current directory icon; the other path components
1382     * are needed for the iconic path icons.
1383     */
1384    path_count = 0;
1385    path_logical_types = NULL;
1386
1387    /* Don't muck with original string */
1388    ptrOrig = ptr = XtNewString(directory_name);
1389
1390    for (;;)
1391    {
1392       Tt_status tt_status;
1393
1394       if (ptr != NULL)
1395          *ptr = '\0';
1396
1397       if (ptrOrig[0] == '\0')
1398          namePtr = "/";
1399       else
1400          namePtr = ptrOrig;
1401
1402       /* get logical type of next path component */
1403       full_directory_name = ResolveLocalPathName( host_name,
1404                                                   namePtr,
1405                                                   NULL,
1406                                                   home_host_name,
1407                                                   &tt_status );
1408       if( TT_OK != tt_status )
1409         break;
1410
1411       DtEliminateDots (full_directory_name);
1412       path_logical_types = (char **) XtRealloc((char *)path_logical_types,
1413                                                (path_count + 1)*sizeof(char *));
1414       path_logical_types[path_count] =
1415          (char *) DtDtsDataToDataType(full_directory_name, NULL, 0, NULL, NULL,
1416                                       NULL, NULL);
1417 #if defined( DATATYPE_IS_FIXED )
1418 #else
1419       {
1420         if( stat( full_directory_name, &stat_buf ) == 0 )
1421         {
1422           if( S_ISDIR( stat_buf.st_mode ) &&
1423             (strcmp (path_logical_types[path_count], LT_DIRECTORY) == 0))
1424           {
1425             if( access( full_directory_name, R_OK ) != 0 )
1426             {
1427               XtFree( path_logical_types[path_count] );
1428               path_logical_types[path_count] = XtNewString( LT_FOLDER_LOCK );
1429             }
1430             else if( access( full_directory_name, W_OK ) != 0 )
1431             {
1432               XtFree( path_logical_types[path_count] );
1433               path_logical_types[path_count] = XtNewString( LT_NON_WRITABLE_FOLDER );
1434             }
1435           }
1436         }
1437       }
1438 #endif
1439       DPRINTF2(("ReadDirectoryProcess: path '%s', fullname '%s', type %s\n",
1440                 namePtr, full_directory_name, path_logical_types[path_count]));
1441
1442       XtFree( full_directory_name );
1443       path_count++;
1444
1445       if (ptr == NULL)
1446         break;
1447
1448       /* restore '/' */
1449       *ptr = '/';
1450
1451       /* find next component */
1452       if (strcmp(ptr, "/") == 0)
1453          break;
1454       ptr = DtStrchr(ptr + 1, '/');
1455    }
1456    XtFree(ptrOrig);
1457
1458    /* get the full name of the current directory */
1459    {
1460      Tt_status tt_status;
1461      full_directory_name = ResolveLocalPathName( host_name,
1462                                                  directory_name,
1463                                                  NULL,
1464                                                  home_host_name,
1465                                                  &tt_status );
1466      /* It's ok not to check for tt_status.
1467         The code below will handle it properly.
1468      */
1469    }
1470
1471    /* send the path_logical_types back through the pipe */
1472    pipe_msg = PIPEMSG_PATH_LOGICAL_TYPES;
1473    DPRINTF(("ReadDirectoryProcess: sending %d path_logical_types\n",
1474             path_count));
1475    write(pipe_fd, &pipe_msg, sizeof(short));
1476    write(pipe_fd, &path_count, sizeof(int));
1477    for(i = 0; i < path_count; i++)
1478    {
1479      PipeWriteString(pipe_fd, path_logical_types[i]);
1480      XtFree((char *) path_logical_types[i]);
1481    }
1482    XtFree((char *) path_logical_types);
1483
1484    /* send the tt_path */
1485    tt_path = GetTTPath(full_directory_name);
1486    PipeWriteString(pipe_fd, tt_path);
1487    XtFree(tt_path);
1488
1489    /*
1490     * Stat the directory to get its timestamp.
1491     * Also check if we have read and execute/search permisssion.
1492     */
1493    if (CheckAccess(full_directory_name, R_OK | X_OK) != 0 ||
1494        stat(full_directory_name, &stat_buf) != 0)
1495    {
1496       /* send an error code back through the pipe */
1497       pipe_msg = PIPEMSG_ERROR;
1498       rc = errno;
1499       modify_time = 0;
1500       DPRINTF(("ReadDirectoryProcess: sending errno %d (stat failed)\n", rc));
1501       write(pipe_fd, &pipe_msg, sizeof(short));
1502       write(pipe_fd, &rc, sizeof(int));
1503       write(pipe_fd, &modify_time, sizeof(long));
1504       return 1;
1505    }
1506
1507    modify_time = stat_buf.st_mtime;
1508
1509    /*
1510     * We never want to display the '~/.dt/Desktop' directory, so when we
1511     * are working with the .dt directory, add a special check for a
1512     * directory named 'Desktop'.
1513     */
1514    if ((ptr = strrchr(full_directory_name, '/')) &&
1515        (strcmp(ptr, "/.dt") == 0))
1516       inDtDir = True;
1517    else
1518       inDtDir = False;
1519
1520    /* try to open the directory */
1521    dirp = opendir (full_directory_name);
1522    if (dirp == NULL)
1523    {
1524       /* send an error code back through the pipe */
1525       pipe_msg = PIPEMSG_ERROR;
1526       rc = errno;
1527       DPRINTF(("ReadDirectoryProcess: sending errno %d (opendir failed)\n",
1528                rc));
1529       write(pipe_fd, &pipe_msg, sizeof(short));
1530       write(pipe_fd, &rc, sizeof(int));
1531       write(pipe_fd, &modify_time, sizeof(long));
1532       XtFree( full_directory_name );
1533       return 1;
1534    }
1535
1536    /*  Loop through the directory entries and update the file list  */
1537
1538 #ifdef DT_PERFORMANCE
1539    printf("  begin reading directory: %s\n", full_directory_name);
1540    gettimeofday(&update_time_s, NULL);
1541 #endif
1542
1543         /*
1544          *      FILEDATA3 creates a buffer of static FileData2 structures,
1545          *      then sends FILEDATABUF worth of FileData2 structs to the parent.
1546          *      FILEDATABUF appears to work the best when set to 50.
1547      *
1548      *  We send data to the parent at least every half seconds, even if
1549      *  less than FILEDATABUF worth of FileData2 structs have been read.
1550      *  This is to ensure that the file count in the status line gets
1551      *  updated every half seconds, even if the file system is slow.
1552          */
1553
1554    /* initialize pointer into file data buffer */
1555 #  define PIPEMSG_HDR_LEN (2*sizeof(short) + sizeof(int))
1556    file_data_buf_ptr = file_data_buffer + PIPEMSG_HDR_LEN;
1557
1558    /* get current time */
1559    gettimeofday(&time1, NULL);
1560
1561    done = False;
1562    do
1563    {
1564      int len = 0;
1565
1566      if ((dp = readdir (dirp)) != NULL)
1567      {
1568        Boolean IsToolBox;
1569        /* if Desktop skip */
1570        if (inDtDir && (strcmp(dp->d_name, "Desktop") == 0))
1571          continue;
1572
1573        /* get the info */
1574        if(directory->directoryView && directory->directoryView->file_mgr_data)
1575          IsToolBox = directory->directoryView->file_mgr_data->toolbox;
1576        else
1577          IsToolBox = False;
1578        len = ReadFileData2((FileData2 *)file_data_buf_ptr,
1579                            full_directory_name, dp->d_name,IsToolBox);
1580        file_data_buf_ptr += len;
1581        file_data_count++;
1582      }
1583      else
1584        done = True;
1585
1586      /* check if 0.4 seconds have passed since the last status line update */
1587      gettimeofday(&time2, NULL);
1588      diff = 1024*(time2.tv_sec - time1.tv_sec);
1589      diff += time2.tv_usec/1024;
1590      diff -= time1.tv_usec/1024;
1591      update_due = (diff >= 400);
1592
1593      /* check if we need to send the buffered data now */
1594      if (file_data_count == FILEDATABUF ||
1595          file_data_count > 0 && (done || update_due))
1596      {
1597        if (update_due)
1598          file_data_count |= 0x8000;
1599        len = file_data_buf_ptr - (file_data_buffer + PIPEMSG_HDR_LEN);
1600
1601        /* now send the file data through the pipe */
1602        *(short *)file_data_buffer = PIPEMSG_FILEDATA3;
1603        *(short *)(file_data_buffer + sizeof(short)) = file_data_count;
1604        *(int *)(file_data_buffer + 2*sizeof(short)) = len;
1605        write(pipe_fd, file_data_buffer,
1606              file_data_buf_ptr - file_data_buffer);
1607
1608        /* reset pointer to file data buffer, file count and time stamp */
1609        file_data_buf_ptr = file_data_buffer + PIPEMSG_HDR_LEN;
1610        file_data_count = 0;
1611        if (update_due)
1612          time1 = time2;
1613      }
1614    } while (!done);
1615
1616 #ifdef DT_PERFORMANCE
1617    gettimeofday(&update_time_f, NULL);
1618    if (update_time_s.tv_usec > update_time_f.tv_usec) {
1619       update_time_f.tv_usec += 1000000;
1620       update_time_f.tv_sec--;
1621    }
1622    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);
1623 #endif
1624
1625
1626    /* load position info, if available */
1627
1628    /* construct full name of the position info file */
1629    if (strcmp(full_directory_name,"/") != 0)
1630      sprintf( file_name, "%s/%s", full_directory_name, positionFileName );
1631    else
1632      sprintf( file_name, "%s%s", full_directory_name, positionFileName );
1633
1634    /* read the count from the position info file */
1635    position_count = 0;
1636    if ((fptr = fopen(file_name, "r")) != NULL)
1637    {
1638      PositionInfo * position_info;
1639      fscanf(fptr, "%d\n", &position_count);
1640
1641      if (position_count > 0)
1642      {
1643        /* allocate position info array */
1644        position_info = (PositionInfo *)
1645          XtMalloc(position_count * sizeof(PositionInfo));
1646
1647        /* read the position info from the file */
1648        i = 0;
1649        while (i < position_count)
1650        {
1651          if( fgets( file_name, MAX_PATH, fptr ) != NULL &&
1652              fscanf(fptr, "%d %d %d\n", &x, &y, &stacking_order ) == 3  )
1653          {
1654            int len = strlen(file_name);
1655            file_name[len-1] = 0x0;
1656            position_info[i].name = XtNewString(file_name);
1657            position_info[i].x = x,
1658            position_info[i].y = y,
1659            position_info[i].stacking_order = stacking_order;
1660            i++;
1661          }
1662          else
1663            break;
1664        }
1665        position_count = i;
1666      }
1667
1668      fclose(fptr);
1669
1670      /* send the position info back through the pipe */
1671      pipe_msg = PIPEMSG_POSITION_INFO;
1672      DPRINTF(("ReadDirectoryProcess: sending %d position_info\n",
1673               position_count));
1674      write(pipe_fd, &pipe_msg, sizeof(short));
1675      write(pipe_fd, &position_count, sizeof(int));
1676      for (i = 0; i < position_count; i++)
1677      {
1678        PipeWriteString( pipe_fd, position_info[i].name );
1679        XtFree( position_info[i].name );
1680        write( pipe_fd, &(position_info[i].x), sizeof(Position));
1681        write( pipe_fd, &(position_info[i].y), sizeof(Position));
1682        write( pipe_fd, &(position_info[i].stacking_order), sizeof(int));
1683
1684      }
1685      XtFree( (char *)position_info );
1686    }
1687
1688    XtFree(full_directory_name);
1689    closedir (dirp);
1690
1691    /* send a 'done' msg through the pipe */
1692    DPRINTF(("ReadDirectoryProcess: sending DONE\n"));
1693    pipe_msg = PIPEMSG_DONE;
1694    write(pipe_fd, &pipe_msg, sizeof(short));
1695    write(pipe_fd, &modify_time, sizeof(long));
1696    return 0;
1697 }
1698
1699
1700 /*--------------------------------------------------------------------
1701  *  UpdateAllProcess
1702  *    Main routine of the background process that checks the directory
1703  *    for new files or files that have disapeared.
1704  *------------------------------------------------------------------*/
1705
1706 static int
1707 UpdateAllProcess(
1708                  int pipe_fd,
1709                  Directory *directory,
1710                  ActivityStatus activity)
1711 {
1712    char *host_name = directory->host_name;
1713    char *directory_name = directory->directory_name;
1714    char *full_directory_name;
1715    struct stat stat_buf;
1716    long modify_time;
1717    DIR *dirp;
1718    struct dirent * dp;
1719    Boolean inDtDir;
1720    FileData *file_data;
1721    FileData *old_data, **old_pp;
1722    FileData2 file_data2;
1723    char *ptr;
1724    short pipe_msg;
1725    int n, i, rc=0;
1726    Tt_status tt_status;
1727    char **path_logical_types;
1728    int path_count;
1729    char *ptrOrig;
1730
1731    DPRINTF(("UpdateAllProcess(%d, \"%s\", \"%s\")\n",
1732             pipe_fd, host_name, directory_name));
1733
1734    /* if modified list contains "." or ".." arrange for the path_logical_types
1735       to get updated */
1736    if(directory->modified_count == 0 &&
1737       FindDirectory(directory->host_name, directory_name))
1738    {
1739        rc = 1;
1740    }
1741    else
1742    {
1743        for (i = 0; i < directory->modified_count; i++)
1744        {
1745            if (strcmp(directory->modified_list[i],".") == 0 ||
1746                strcmp(directory->modified_list[i],"..") == 0 )
1747            {
1748                rc = 1;
1749                break;
1750            }
1751        }
1752    }
1753    if(rc)
1754    {
1755       char *tt_path;
1756
1757       path_count = 0;
1758       path_logical_types = NULL;
1759       ptrOrig = ptr = XtNewString(directory_name);
1760       for (;;)
1761       {
1762          Tt_status tt_status;
1763          char *namePtr;
1764
1765          if (ptr != NULL)
1766             *ptr = '\0';
1767
1768          if (ptrOrig[0] == '\0')
1769             namePtr = "/";
1770          else
1771             namePtr = ptrOrig;
1772
1773          /* get logical type of next path component */
1774          full_directory_name = ResolveLocalPathName( host_name,
1775                                                   namePtr,
1776                                                   NULL,
1777                                                   home_host_name,
1778                                                   &tt_status );
1779          if( TT_OK != tt_status )
1780            break;
1781
1782          DtEliminateDots (full_directory_name);
1783          path_logical_types = (char **) XtRealloc((char *)path_logical_types,
1784                                                (path_count + 1)*sizeof(char *));
1785          path_logical_types[path_count] =
1786            (char *)DtDtsDataToDataType(full_directory_name, NULL, 0, NULL, NULL,
1787                                       NULL, NULL);
1788 #if defined( DATATYPE_IS_FIXED )
1789 #else
1790       {
1791         if( stat( full_directory_name, &stat_buf ) == 0 )
1792         {
1793           if( S_ISDIR( stat_buf.st_mode ) &&
1794             (strcmp (path_logical_types[path_count], LT_DIRECTORY) == 0))
1795           {
1796             if( access( full_directory_name, R_OK ) != 0 )
1797             {
1798               XtFree( path_logical_types[path_count] );
1799               path_logical_types[path_count] = XtNewString( LT_FOLDER_LOCK );
1800             }
1801             else if( access( full_directory_name, W_OK ) != 0 )
1802             {
1803               XtFree( path_logical_types[path_count] );
1804               path_logical_types[path_count] = XtNewString( LT_NON_WRITABLE_FOLDER );
1805             }
1806           }
1807         }
1808       }
1809 #endif
1810
1811          DPRINTF2(("ReadDirectoryProcess: path '%s', fullname '%s', type %s\n",
1812                 namePtr, full_directory_name, path_logical_types[path_count]));
1813
1814          XtFree(full_directory_name);
1815          path_count++;
1816
1817          if (ptr == NULL)
1818            break;
1819
1820          /* restore '/' */
1821          *ptr = '/';
1822
1823          /* find next component */
1824          if (strcmp(ptr, "/") == 0)
1825             break;
1826          ptr = DtStrchr(ptr + 1, '/');
1827       }
1828       XtFree(ptrOrig);
1829
1830       /* get the full name of the current directory */
1831       full_directory_name = ResolveLocalPathName( host_name,
1832                                                  directory_name,
1833                                                  NULL,
1834                                                  home_host_name,
1835                                                  &tt_status );
1836       /* It's ok not to check for tt_status.
1837          The code below will handle it properly.
1838       */
1839
1840       /* send the path_logical_types back through the pipe */
1841       pipe_msg = PIPEMSG_PATH_LOGICAL_TYPES;
1842       DPRINTF(("ReadDirectoryProcess: sending %d path_logical_types\n",
1843             path_count));
1844       write(pipe_fd, &pipe_msg, sizeof(short));
1845       write(pipe_fd, &path_count, sizeof(int));
1846       for(i = 0; i < path_count; i++)
1847       {
1848         PipeWriteString(pipe_fd, path_logical_types[i]);
1849         XtFree((char *) path_logical_types[i]);
1850       }
1851       XtFree((char *) path_logical_types);
1852
1853       /* send the tt_path */
1854       tt_path = GetTTPath(full_directory_name);
1855       PipeWriteString(pipe_fd, tt_path);
1856       XtFree(tt_path);
1857    }
1858    else
1859    {
1860         full_directory_name = ResolveLocalPathName( host_name,
1861                                                  directory_name,
1862                                                  NULL,
1863                                                  home_host_name,
1864                                                  &tt_status );
1865    }
1866
1867    if( TT_OK != tt_status )
1868    {
1869       pipe_msg = PIPEMSG_ERROR;
1870       rc = -1;
1871       modify_time = 0;
1872       DPRINTF(("UpdateAllProcess: sending errno %d (tooltalk failed)\n", rc));
1873       write(pipe_fd, &pipe_msg, sizeof(short));
1874       write(pipe_fd, &rc, sizeof(int));
1875       write(pipe_fd, &modify_time, sizeof(long));
1876       return 1;
1877    }
1878    (void) DtEliminateDots (full_directory_name);
1879
1880    /*
1881     * Stat the directory to get its timestamp.
1882     * Also check if we still have read and execute/search permisssion.
1883     */
1884    if (CheckAccess(full_directory_name, R_OK | X_OK) != 0 ||
1885        stat(full_directory_name, &stat_buf) != 0)
1886    {
1887       /* send an error code back through the pipe */
1888       pipe_msg = PIPEMSG_ERROR;
1889       rc = errno;
1890       modify_time = 0;
1891       DPRINTF(("UpdateAllProcess: sending errno %d (stat failed)\n", rc));
1892       write(pipe_fd, &pipe_msg, sizeof(short));
1893       write(pipe_fd, &rc, sizeof(int));
1894       write(pipe_fd, &modify_time, sizeof(long));
1895       XtFree( full_directory_name );
1896       return 1;
1897    }
1898    modify_time = stat_buf.st_mtime;
1899
1900    /* check if we are in the .dt directory */
1901    if ((ptr = strrchr(full_directory_name, '/')) &&
1902        (strcmp(ptr, "/.dt") == 0))
1903    {
1904       inDtDir = True;
1905    }
1906    else
1907       inDtDir = False;
1908
1909    /* try to open the directory */
1910    dirp = opendir (full_directory_name);
1911    if (dirp == NULL)
1912    {
1913       /* send an error code back through the pipe */
1914       pipe_msg = PIPEMSG_ERROR;
1915       rc = errno;
1916       DPRINTF(("UpdateAllProcess: sending errno %d (opendir failed)\n", rc));
1917       write(pipe_fd, &pipe_msg, sizeof(short));
1918       write(pipe_fd, &rc, sizeof(int));
1919       write(pipe_fd, &modify_time, sizeof(long));
1920       XtFree( full_directory_name );
1921       return 1;
1922    }
1923
1924    /*  Loop through the directory entries and update the file list  */
1925    while (dp = readdir (dirp))
1926    {
1927       /* if Desktop skip */
1928       if (inDtDir && (strcmp(dp->d_name, "Desktop") == 0))
1929          continue;
1930
1931       /* check if we already know this file */
1932       for (old_pp = &directory->file_data;
1933            (old_data = *old_pp) != NULL;
1934            old_pp = &old_data->next)
1935       {
1936          if (strcmp(dp->d_name, old_data->file_name) == 0)
1937          {
1938              char *tname;
1939              struct stat sbuf;
1940  
1941              /* check modified times */
1942              tname = XtMalloc(strlen(full_directory_name)+strlen(dp->d_name)+2);
1943              sprintf(tname,"%s/%s",full_directory_name,dp->d_name);
1944              if((lstat(tname,&sbuf)>=0)&&sbuf.st_mtime!=old_data->stat.st_mtime)
1945                  old_data = NULL;
1946              XtFree(tname);
1947              if(old_data == NULL)
1948                  break;
1949
1950             /* check if this file appears on the modified list */
1951             for (i = 0; i < directory->modified_count; i++)
1952                if (strcmp(dp->d_name, directory->modified_list[i]) == 0)
1953                {
1954                  /*
1955                   * This file is on the modified list.
1956                   * Pretend we didn't find it to force a refresh of this file.
1957                   */
1958                  old_data = NULL;
1959                  break;
1960                }
1961             break;
1962          }
1963       }
1964
1965       /* If this is a known file, remember we saw it and continue. */
1966       if (old_data != NULL)
1967       {
1968          /*
1969           * We remove the file from the old file list; thus, when we are done,
1970           * the files on the old file list will be onew that no longer exist.
1971           */
1972          *old_pp = old_data->next;
1973          continue;
1974       }
1975
1976       /* this is a new file */
1977       DPRINTF(("UpdateAllProcess: found new file \"%s\"\n", dp->d_name));
1978
1979       /* Fix for incorrect icons in App Manager */
1980       {
1981         Boolean IsToolBox;
1982
1983         if(directory->directoryView && directory->directoryView->file_mgr_data)
1984           IsToolBox = directory->directoryView->file_mgr_data->toolbox;
1985         else
1986           IsToolBox = False;
1987         ReadFileData2(&file_data2, full_directory_name, dp->d_name,IsToolBox);
1988       }
1989       file_data = FileData2toFileData(&file_data2, &n);
1990
1991       /* now send the file data through the pipe */
1992       pipe_msg = PIPEMSG_FILEDATA;
1993       write(pipe_fd, &pipe_msg, sizeof(short));
1994       PipeWriteFileData(pipe_fd, file_data);
1995
1996       FreeFileData(file_data, True);
1997    }
1998
1999    /* all files left in the old file list no longer exist */
2000    for (old_data = directory->file_data;
2001         old_data;
2002         old_data = old_data->next)
2003    {
2004       DPRINTF(("UpdateAllProcess: file gone \"%s\"\n", old_data->file_name));
2005       old_data->errnum = ENOENT;
2006       pipe_msg = PIPEMSG_FILEDATA;
2007       write(pipe_fd, &pipe_msg, sizeof(short));
2008       PipeWriteFileData(pipe_fd, old_data);
2009    }
2010
2011    /* free storage */
2012    XtFree(full_directory_name);
2013
2014    /* send a 'done' msg through the pipe */
2015    DPRINTF(("UpdateAllProcess: sending DONE\n"));
2016    pipe_msg = PIPEMSG_DONE;
2017    write(pipe_fd, &pipe_msg, sizeof(short));
2018    write(pipe_fd, &modify_time, sizeof(long));
2019
2020    if (dirp)
2021        closedir(dirp);
2022    return 0;
2023 }
2024
2025
2026 /*--------------------------------------------------------------------
2027  *  UpdateSomeProcess
2028  *    Main routine of the background process that updates a selected
2029  *    list of directory entries.
2030  *------------------------------------------------------------------*/
2031
2032 static int
2033 UpdateSomeProcess(
2034         int pipe_fd,
2035         Directory *directory,
2036         ActivityStatus activity)
2037 {
2038    char *host_name = directory->host_name;
2039    char *directory_name = directory->directory_name;
2040    char *full_directory_name;
2041    struct stat stat_buf;
2042    long modify_time;
2043    FileData *file_data;
2044    FileData2 file_data2;
2045    short pipe_msg;
2046    int i;
2047    int rc;
2048    Boolean IsToolBox;
2049
2050    DPRINTF(("UpdateSomeProcess(%d, \"%s\", \"%s\")\n",
2051             pipe_fd, host_name, directory_name));
2052
2053
2054    /* get the full name of the current directory */
2055    {
2056      Tt_status tt_status;
2057
2058      full_directory_name = ResolveLocalPathName( host_name,
2059                                                  directory_name,
2060                                                  NULL,
2061                                                  home_host_name,
2062                                                  &tt_status );
2063      if( TT_OK != tt_status )
2064      {
2065        pipe_msg = PIPEMSG_ERROR;
2066        rc = -1;
2067        modify_time = 0;
2068        DPRINTF(("UpdateSomeProcess: sending errno %d (stat failed)\n", rc));
2069        write(pipe_fd, &pipe_msg, sizeof(short));
2070        write(pipe_fd, &rc, sizeof(int));
2071        write(pipe_fd, &modify_time, sizeof(long));
2072        return 1;
2073      }
2074    }
2075    (void) DtEliminateDots (full_directory_name);
2076
2077    /* stat the directory to get the timestamp */
2078    if (stat(full_directory_name, &stat_buf) < 0 ||
2079        ! (stat_buf.st_mode & S_IXUSR) )
2080    {
2081       /* send an error code back through the pipe */
2082       pipe_msg = PIPEMSG_ERROR;
2083       rc = errno;
2084       modify_time = 0;
2085       DPRINTF(("UpdateSomeProcess: sending errno %d (stat failed)\n", rc));
2086       write(pipe_fd, &pipe_msg, sizeof(short));
2087       write(pipe_fd, &rc, sizeof(int));
2088       write(pipe_fd, &modify_time, sizeof(long));
2089       XtFree( full_directory_name );
2090       return 1;
2091    }
2092    modify_time = stat_buf.st_mtime;
2093
2094    /*  Loop through the list of modified files  */
2095    for (i = 0; i < directory->modified_count; i++)
2096    {
2097       /* get the info */
2098
2099       if(directory->directoryView && directory->directoryView->file_mgr_data)
2100         IsToolBox = directory->directoryView->file_mgr_data->toolbox;
2101       else
2102         IsToolBox = False;
2103
2104       ReadFileData2(&file_data2, full_directory_name,
2105                                directory->modified_list[i],IsToolBox);
2106
2107       /* now send the file data through the pipe */
2108       pipe_msg = PIPEMSG_FILEDATA2;
2109       write(pipe_fd, &pipe_msg, sizeof(short));
2110       write(pipe_fd, &file_data2, sizeof(FileData2));
2111    }
2112
2113    XtFree(full_directory_name);
2114
2115    /* send a 'done' msg through the pipe */
2116    DPRINTF(("UpdateSomeProcess: sending DONE\n"));
2117    pipe_msg = PIPEMSG_DONE;
2118    write(pipe_fd, &pipe_msg, sizeof(short));
2119    write(pipe_fd, &modify_time, sizeof(long));
2120    return 0;
2121 }
2122
2123
2124 /*--------------------------------------------------------------------
2125  *  ReaddirPipeCallback
2126  *      Callback routine that reads directory entry information sent
2127  *      through the pipe from the background process.
2128  *------------------------------------------------------------------*/
2129
2130 static void
2131 ReaddirPipeCallback(
2132    XtPointer client_data,
2133    int *fd,
2134    XtInputId *id)
2135 {
2136    static int whined_fd = 0;
2137    PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
2138    Directory *directory = pipe_data->directory;
2139    FileMgrData *file_mgr_data;
2140    FileMgrRec *file_mgr_rec;
2141    ActivityStatus activity;
2142    Boolean done;
2143    short msg;
2144    Boolean update_due;
2145    FileData *new_data, **new_nextp;
2146    FileData *old_data, **old_nextp;
2147    char *ptr;
2148    char *err_msg;
2149    int i, n;
2150    int rc;
2151    long modify_time = 0;
2152    char dirname[MAX_PATH];
2153    short file_data_count;
2154
2155
2156    /* verify that the directory still exists */
2157    if (DirectoryGone(directory))
2158    {
2159       /*
2160        * The directory is no longer present:
2161        * close the pipe and kill the reader.
2162        */
2163       close(*fd);
2164       XtRemoveInput(*id);
2165       kill(pipe_data->child, SIGKILL);
2166       XtFree( client_data );
2167       ScheduleActivity(NULL);
2168       return;
2169    }
2170
2171    /* read the next msg from the pipe */
2172    msg = -1;
2173    n = PipeRead(*fd, &msg, sizeof(short));
2174    activity = directory->activity;
2175    done = False;
2176
2177    switch (msg)
2178    {
2179       case PIPEMSG_PATH_LOGICAL_TYPES:
2180          /* get the number of path components */
2181          PipeRead(*fd, &n, sizeof(int));
2182
2183          /* free logical types */
2184          for (i = 0; i < directory->path_count; i++)
2185            XtFree(directory->path_logical_types[i]);
2186
2187          /* allocate array of the right size */
2188          if (directory->path_count != n)
2189          {
2190             directory->path_count = n;
2191             directory->path_logical_types = (char **)
2192               XtRealloc((char *)directory->path_logical_types,
2193                         n*sizeof(char *));
2194          }
2195
2196          /* get new logical types */
2197          for (i = 0; i < directory->path_count; i++)
2198            directory->path_logical_types[i] = PipeReadString(*fd);
2199
2200          /* get the tt_path */
2201          XtFree(directory->tt_path_name);
2202          directory->tt_path_name = PipeReadString(*fd);
2203
2204          /* update all views */
2205          for (i = 0; i < directory->numOfViews; i++)
2206          {
2207             file_mgr_data = directory->directoryView[i].file_mgr_data;
2208             file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2209             UpdateHeaders(file_mgr_rec, file_mgr_data, True);
2210             XmUpdateDisplay(file_mgr_rec->file_window);
2211          }
2212          XSync(XtDisplay(toplevel), False);
2213          break;
2214
2215           case PIPEMSG_FILEDATA:
2216           case PIPEMSG_FILEDATA2:
2217           case PIPEMSG_FILEDATA3:
2218
2219          if (msg == PIPEMSG_FILEDATA)
2220          {
2221            new_data = PipeReadFileData(*fd);
2222          }
2223          else if (msg == PIPEMSG_FILEDATA2)
2224          {
2225            FileData2 file_data2;
2226            int n;
2227
2228            n = PipeRead(*fd, &file_data2, sizeof(FileData2));
2229            if (n < sizeof(FileData2)) {
2230              perror("PipeRead");
2231              fprintf(stderr, "PipeReadFileData2: n = %d, expected %ld\n",
2232                      n, (long)sizeof(FileData2));
2233            }
2234
2235            new_data = FileData2toFileData(&file_data2, &n);
2236            }
2237
2238          if (msg == PIPEMSG_FILEDATA3)
2239          {
2240            int file_data_length;
2241            int n;
2242            char file_data_buffer[FILEDATABUF * sizeof(FileData2)];
2243            char *file_data_buf_ptr;
2244
2245
2246            n = PipeRead(*fd, &file_data_count, sizeof(short));
2247            n = PipeRead(*fd, &file_data_length, sizeof(int));
2248
2249            if (file_data_count & 0x8000)
2250            {
2251              file_data_count &= 0x7fff;
2252              update_due = True;
2253            }
2254            else
2255              update_due = False;
2256
2257            for (new_nextp = &directory->new_data;
2258                 *new_nextp;
2259                 new_nextp = &(*new_nextp)->next)
2260              ;
2261
2262            n = PipeRead(*fd, file_data_buffer, file_data_length);
2263            file_data_buf_ptr = file_data_buffer;
2264
2265            for (i = 0; i < file_data_count; i++)
2266            {
2267              /* get next FileData out of buffer */
2268              new_data =
2269                FileData2toFileData((FileData2 *)file_data_buf_ptr, &n);
2270              file_data_buf_ptr += n;
2271
2272              /* append new_data to end of list */
2273              *new_nextp = new_data;
2274              new_data->next = NULL;
2275              new_nextp = &new_data->next;
2276            }
2277          }
2278          else
2279          {
2280            /* append new_data to end of list */
2281            file_data_count = 1;
2282            update_due = False;
2283            for (new_nextp = &directory->new_data;
2284                 *new_nextp;
2285                 new_nextp = &(*new_nextp)->next)
2286              ;
2287            *new_nextp = new_data;
2288            new_data->next = NULL;
2289          }
2290
2291          if (activity == activity_reading)
2292          {
2293            /* update file counts in all views */
2294            for (i = 0; i < directory->numOfViews; i++)
2295            {
2296              file_mgr_data = directory->directoryView[i].file_mgr_data;
2297              file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2298              file_mgr_data->busy_detail += file_data_count;
2299              if (update_due &&
2300                  (file_mgr_data->busy_status == initiating_readdir ||
2301                   file_mgr_data->busy_status == busy_readdir) &&
2302                  file_mgr_data->busy_detail > 2)
2303              {
2304                if (file_mgr_data->show_status_line)
2305                {
2306                  char buf[256];
2307                  XmString label_string;
2308                  Arg args[2];
2309
2310                  GetStatusMsg(file_mgr_data, buf);
2311                  label_string =
2312                    XmStringCreateLocalized(buf);
2313                  XtSetArg (args[0], XmNlabelString, label_string);
2314                  XtSetValues(file_mgr_rec->status_line, args, 1);
2315                  XmStringFree(label_string);
2316                }
2317                else if (file_mgr_data->show_iconic_path)
2318                {
2319                  DtUpdateIconicPath(file_mgr_rec, file_mgr_data, False);
2320                }
2321                else if (file_mgr_data->show_current_dir)
2322                {
2323                  DrawCurrentDirectory(file_mgr_rec->current_directory,
2324                                       file_mgr_rec, file_mgr_data);
2325                }
2326              }
2327            }
2328          }
2329          break;
2330
2331       case PIPEMSG_POSITION_INFO:
2332          /* free old position info names */
2333          for (i = 0; i < directory->position_count; i++)
2334             XtFree(directory->position_info[i].name);
2335
2336          /* get number of positions and realloc array, if necessary */
2337          PipeRead(*fd, &n, sizeof(int));
2338          if (directory->position_count != n)
2339          {
2340             directory->position_count = n;
2341             directory->position_info = (PositionInfo *) XtRealloc(
2342                      (char *)directory->position_info, n*sizeof(PositionInfo));
2343          }
2344
2345          /* read new position info */
2346          for (i = 0; i < n; i++)
2347             PipeReadPositionInfo(*fd, &directory->position_info[i]);
2348          break;
2349
2350       case PIPEMSG_DONE:
2351          PipeRead(*fd, &modify_time, sizeof(long));
2352          directory->errnum = 0;
2353          directory->errmsg_needed = False;
2354          done = True;
2355          break;
2356
2357       case PIPEMSG_ERROR:
2358          PipeRead(*fd, &rc, sizeof(int));
2359          PipeRead(*fd, &modify_time, sizeof(long));
2360          if (rc != directory->errnum)
2361          {
2362             directory->errnum = rc;
2363             directory->errmsg_needed = True;
2364          }
2365          done = True;
2366          break;
2367
2368       default:
2369          if (whined_fd != *fd)
2370          {
2371              whined_fd = *fd;
2372              fprintf(stderr,
2373                "ReaddirPipeCallback: badmsg=%d, ppid=%d pid=%d fd=%d activ'y=%d\n",
2374                msg, getppid(), getpid(), *fd, activity);
2375          }
2376          directory->errnum = -1;
2377          directory->errmsg_needed = False;
2378          done = True;
2379    }
2380
2381    /* check if we are done */
2382    if (done)
2383    {
2384 #ifdef DT_PERFORMANCE
2385    /* Aloke Gupta: As suggested by Dana Dao */
2386       _DtPerfChkpntMsgSend("Done  Read Directory");
2387 #endif
2388       DPRINTF(("ReaddirPipeCallback: done, errno %d, time %ld\n",
2389                directory->errnum, modify_time));
2390
2391       /* close the pipe and cancel the callback */
2392       close(*fd);
2393       XtRemoveInput(*id);
2394
2395       /*
2396        * @@@ what if a drag is active ???
2397        */
2398
2399       /*
2400        * For files in the old list that still exist in the new
2401        * one we need to re-use the old FileData structures.
2402        * Reason: the code in GetFileData relies on this to
2403        * preserve the position_info and selection list.
2404        * The following loops through the new list of files
2405        * and replaces entries that also exist in the old list.
2406        */
2407       for (new_nextp = &directory->new_data;
2408            (new_data = *new_nextp) != NULL;
2409            new_nextp = &new_data->next)
2410       {
2411          for (old_nextp = &directory->file_data;
2412               (old_data = *old_nextp) != NULL;
2413               old_nextp = &old_data->next)
2414          {
2415            if( strcmp(old_data->file_name, new_data->file_name) == 0 )
2416             {
2417                *old_nextp = old_data->next;
2418
2419                FreeFileData(old_data, False);
2420                memcpy(old_data, new_data, sizeof(FileData));
2421
2422                XtFree((char *)new_data);
2423                *new_nextp = new_data = old_data;
2424
2425                break;
2426             }
2427          }
2428       }
2429
2430       /*
2431        * If this was a complete re-read, we free all FileData still left
2432        * in the old list.  Otherwise, if this was just a partial update,
2433        * we append the old data that's still left to the end of the
2434        * new list.
2435        */
2436       if (activity == activity_reading)
2437       {
2438          /* This was a complete re-read: free all old data still left. */
2439          while (directory->file_data)
2440          {
2441             old_data = directory->file_data;
2442             directory->file_data = old_data->next;
2443             FreeFileData(old_data, True);
2444          }
2445
2446          /* replace the old list by the new list */
2447          directory->file_data = directory->new_data;
2448          directory->new_data = NULL;
2449       }
2450       else
2451       {
2452          FileData * tmp_ptr = NULL;
2453
2454          /* remove any directory entries that no longer exist
2455             in the new list.
2456          */
2457          new_nextp = &directory->new_data;
2458          while ((new_data = *new_nextp) != NULL)
2459          {
2460             if (new_data->errnum == ENOENT)
2461             {
2462                *new_nextp = new_data->next;
2463                FreeFileData(new_data, True);
2464             }
2465             else
2466             {
2467                tmp_ptr = *new_nextp;
2468                new_nextp = &new_data->next;
2469             }
2470          }
2471
2472          /* remove any directory entries that no longer exist
2473             in the old list.
2474          */
2475          old_nextp = &directory->file_data;
2476          while ((old_data = *old_nextp) != NULL)
2477          {
2478             if (old_data->errnum == ENOENT)
2479             {
2480                *old_nextp = old_data->next;
2481                FreeFileData(old_data, True);
2482             }
2483             else
2484                old_nextp = &old_data->next;
2485          }
2486
2487          /* Append the old list to the end of the new list
2488             Replace the old list pointer with the new list pointer
2489          */
2490          if( tmp_ptr != NULL )
2491          {
2492             tmp_ptr->next = directory->file_data;
2493             directory->file_data = directory->new_data;
2494             directory->new_data = NULL;
2495          }
2496       }
2497
2498       /* update the file count */
2499       directory->file_count = 0;
2500       for (new_data = directory->file_data; new_data; new_data = new_data->next)
2501          directory->file_count++;
2502
2503       /* update directory timestamp */
2504       if (activity == activity_reading ||
2505           activity == activity_update_all ||
2506           directory->was_up_to_date)
2507       {
2508          if (modify_time != 0)
2509             directory->modify_time = modify_time;
2510       }
2511
2512       /* flush the icon cache */  /* @@@ Why? What does this do? @@@ */
2513       strcpy (dirname, directory->path_name);
2514       DtEliminateDots(dirname);
2515       /* We will not attempt to flush the icon cache until this */
2516       /* function has been fixed.                               */
2517
2518       _DtFlushIconFileCache(dirname);
2519
2520       /* reset busy flags */
2521       directory->busy[activity] = False;
2522       directory->activity = activity_idle;
2523       directory->was_up_to_date = True;
2524       directory->link_check_needed = False;
2525
2526
2527       /*
2528        * Fill dir_data field with information on the directory itself.
2529        * This data will be read when querying this view's top directory,
2530        * if the parent directory isn't already cached (tree mode)
2531        */
2532       for (new_data = directory->file_data;
2533            new_data != NULL;
2534            new_data = new_data->next)
2535         if (strcmp(new_data->file_name, ".") == 0)
2536         {
2537
2538             /*
2539              * Found current directory information, now we make
2540              * dir_data info from "." info
2541              */
2542
2543             /* If we already have allocated space for dir_data free it */
2544             if ( directory->dir_data != NULL )
2545               FreeFileData(directory->dir_data, True);
2546
2547             directory->dir_data = (FileData *)XtMalloc(sizeof(FileData));
2548
2549             memcpy(directory->dir_data, new_data, sizeof(FileData));
2550
2551             /*
2552              * Doctor up some of the information fields so that this doesn't
2553              * seem to be a "." entry
2554              */
2555             directory->dir_data->next = NULL;
2556             directory->dir_data->file_name =
2557                                  XtNewString(DName(directory->directory_name));
2558             directory->dir_data->action_name = NULL;
2559             if (directory->path_count > 0)
2560             {
2561               directory->dir_data->logical_type = XtNewString(
2562                      directory->path_logical_types[directory->path_count - 1]);
2563             }
2564             else
2565               directory->dir_data->logical_type = NULL;
2566             directory->dir_data->link = NULL;
2567             directory->dir_data->final_link = NULL;
2568             directory->dir_data->is_subdir = True;
2569
2570             break;
2571         }
2572
2573       /* cause all views on this directory to be redrawn */
2574       for (i = 0; i < directory->numOfViews; i++)
2575       {
2576          file_mgr_data = directory->directoryView[i].file_mgr_data;
2577          file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2578          FileMgrRedisplayFiles(file_mgr_rec, file_mgr_data, False);
2579          if(file_mgr_data->desktop_file)
2580          {
2581              SelectDesktopFile(file_mgr_data);
2582              XtFree(file_mgr_data->desktop_file);
2583              file_mgr_data->desktop_file = NULL;
2584          }
2585       }
2586       XtFree(client_data);
2587
2588       /* schedule the next background activity */
2589       ScheduleActivity(directory);
2590    }
2591 }
2592
2593
2594 /*--------------------------------------------------------------------
2595  *  ReadDirectoryFiles
2596  *    This routine is called to read a directory if the directory
2597  *    wasn't found in the cached, or if the directory has to be
2598  *    re-read because it changed.  This routine schedules a background
2599  *    process to be started that will do the actual work.
2600  *------------------------------------------------------------------*/
2601
2602 static void
2603 ReadDirectoryFiles(
2604         Widget w,
2605         Directory *directory)
2606 {
2607    FileMgrData *file_mgr_data;
2608    FileMgrRec *file_mgr_rec;
2609    int i;
2610
2611 #ifdef DT_PERFORMANCE
2612    /* Aloke Gupta */
2613       _DtPerfChkpntMsgSend("Begin Read Directory");
2614 #endif
2615
2616    /* make sure positionFileName is initialized */
2617    if (positionFileName == NULL)
2618       InitializePositionFileName();
2619
2620    /* mark the directory busy reading */
2621    directory->busy[activity_reading] = True;
2622
2623    /* arrange for background process to be started */
2624    ScheduleActivity(directory);
2625
2626    /* make sure all views on this directory are marked busy */
2627    for (i = 0; i < directory->numOfViews; i++)
2628    {
2629       file_mgr_data = directory->directoryView[i].file_mgr_data;
2630       if (file_mgr_data->busy_status == not_busy)
2631       {
2632          file_mgr_data->busy_status = busy_readdir;
2633          file_mgr_data->busy_detail = 0;
2634          file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2635          FileMgrRedisplayFiles(file_mgr_rec, file_mgr_data, False);
2636       }
2637       file_mgr_data->busy_status = busy_readdir;
2638    }
2639    return;
2640 }
2641
2642
2643 /*--------------------------------------------------------------------
2644  *  ReadDirectory
2645  *    Given a directory name, see if the directory is already cached.
2646  *    If so, return the file data list, otherwise, read the directory.
2647  *------------------------------------------------------------------*/
2648
2649 static Boolean
2650 ReadDirectory(
2651         Widget w,
2652         char *host_name,
2653         char *directory_name,
2654         FileData **file_data,
2655         int *file_count,
2656         FileMgrData *file_mgr_data)
2657 {
2658    Directory *directory;
2659    int i;
2660    char *err_msg;
2661
2662    /* initialize return values */
2663    if (file_data != NULL)
2664    {
2665       *file_count = 0;
2666       *file_data = NULL;
2667    }
2668
2669    /* see if the directory is already in the cache */
2670    directory = FindDirectory(host_name, directory_name);
2671
2672    if ((directory != NULL) &&
2673        (strcmp(directory_name, directory->directory_name) == 0))
2674    {
2675       /* The directory is already in the cache. */
2676       directory->viewed = True;
2677
2678       /* Look for the view in the view list */
2679       for (i = 0; i < directory->numOfViews; i++)
2680          if (directory->directoryView[i].file_mgr_data == file_mgr_data)
2681             break;
2682
2683       /* If view not found, add to the view list */
2684       if (i == directory->numOfViews)
2685       {
2686          directory->directoryView = (DirectoryView *)
2687                                  XtRealloc ((char *) directory->directoryView,
2688                                             sizeof(DirectoryView) * (i + 1));
2689          directory->numOfViews++;
2690          directory->directoryView[i].file_mgr_data = file_mgr_data;
2691       }
2692
2693       /* set mapped flag for the view */
2694       directory->directoryView[i].mapped = file_mgr_data->mapped;
2695
2696       /* check if we need to popup an error message */
2697       if (directory->errmsg_needed &&
2698           !directory->busy[activity_reading] &&
2699           w != NULL)
2700       {
2701          err_msg = XtNewString(GetSharedMessage(CANNOT_READ_DIRECTORY_ERROR));
2702          FileOperationError (w, err_msg, directory_name);
2703          XtFree(err_msg);
2704          directory->errmsg_needed = False;
2705       }
2706
2707       DPRINTF2(("ReadDirectory(\"%s\", \"%s\") returns cached\n",
2708                 host_name, directory_name));
2709    }
2710
2711    else
2712    {
2713       Tt_status tt_status;
2714
2715       /* The directory is not yet in the cache. */
2716
2717       /*  Expand the directory set array, if necessary.  */
2718       if (directory_count == directory_set_size)
2719       {
2720          directory_set_size += 10;
2721          directory_set = (Directory **) XtRealloc((char *)directory_set,
2722                                     sizeof(Directory **) * directory_set_size);
2723       }
2724
2725
2726       /*  Create and initialize a new directory entry  */
2727       directory_set[directory_count] = directory =
2728                                   (Directory *) XtMalloc (sizeof (Directory));
2729       directory_count++;
2730
2731       directory->host_name = XtNewString (host_name);
2732       directory->directory_name = XtNewString (directory_name);
2733       directory->path_name = ResolveLocalPathName (host_name,
2734                                                    directory_name,
2735                                                    NULL,
2736                                                    home_host_name,
2737                                                    &tt_status );
2738       if (directory->path_name == NULL)
2739       {
2740          directory->path_name = (char *) XtMalloc(sizeof(char));
2741          directory->path_name[0]='\0';
2742       }
2743       directory->tt_path_name = NULL;
2744       directory->viewed = True;
2745       directory->file_count = 0;
2746       directory->numOfViews = 1;
2747       directory->errnum = 0;
2748       directory->errmsg_needed = False;
2749       directory->last_check = 0;
2750       directory->link_check_needed = False;
2751       directory->file_count = 0;
2752       directory->file_data = NULL;
2753       directory->new_data = NULL;
2754       directory->dir_data = NULL;
2755       directory->path_count = 0;
2756       directory->path_logical_types = NULL;
2757       directory->position_count = 0;
2758       directory->position_info = NULL;
2759       directory->modify_begin = 0;
2760       directory->modified_count = 0;
2761       directory->was_up_to_date = True;
2762       directory->modified_list = NULL;
2763       directory->activity = activity_idle;
2764       for (i = 0; i < activity_idle; i++)
2765         directory->busy[i] = False;
2766
2767       directory->directoryView = (DirectoryView *)
2768                                        XtMalloc (sizeof(DirectoryView));
2769       directory->directoryView[0].file_mgr_data = file_mgr_data;
2770       directory->directoryView[0].mapped = file_mgr_data->mapped;
2771
2772       /*  Open the directory for reading and read the files.  */
2773       ReadDirectoryFiles (w, directory);
2774    }
2775
2776    /* Restart refresh timer, if necessary */
2777    if (file_mgr_data->mapped && timer_suspended)
2778    {
2779       XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
2780       timer_suspended = False;
2781    }
2782
2783    /* return the file data */
2784    if (file_data != NULL && !directory->busy[activity_reading])
2785    {
2786       *file_count = directory->file_count;
2787       *file_data = directory->file_data;
2788    }
2789
2790    return directory->busy[activity_reading];
2791 }
2792
2793
2794 /*--------------------------------------------------------------------
2795  *  _ReadDir
2796  *    Internal routine that recursively read a directory plus
2797  *    subdirectories down to a depth given by read_level.
2798  *------------------------------------------------------------------*/
2799
2800 static int
2801 _ReadDir(
2802   Widget w,
2803   FileMgrData *file_mgr_data,
2804   char *host_name,
2805   char *directory_name,
2806   FileViewData *dp,         /* directory info */
2807   int level,                /* tree level of this directory */
2808   int read_level,           /* deepest level to be read */
2809   char **branch_list)       /* list of tree branches to expand */
2810 /*
2811  * Recursively read a directory plus subdirectories down to a depth
2812  * given by read_level.
2813  */
2814 {
2815   char subdir_name[MAX_PATH];
2816   FileData *fp, *file_data;
2817   FileViewData **lp, *ip;
2818   int i, j, n, rc;
2819   TreeShow ts;
2820   Boolean busy_reading;
2821
2822   DPRINTF2(("_ReadDir(\"%s\", \"%s\"): level %d, read_level %d\n",
2823             host_name, directory_name, level, read_level));
2824
2825   /* initialize list of descendents and counts */
2826   if (dp)
2827   {
2828     dp->desc = NULL;
2829     dp->ndir = dp->nfile = 0;
2830     lp = &dp->desc;
2831   } else
2832     ip = NULL;
2833
2834   /* Read the directory content */
2835   busy_reading = ReadDirectory(w, host_name, directory_name,
2836                                &file_data, &n, file_mgr_data);
2837   if (busy_reading)
2838   {
2839     file_mgr_data->busy_status = busy_readdir;
2840     return 0;
2841   }
2842
2843   if (n <= 0)
2844     return -1;
2845
2846   level++;
2847
2848   for (i = 0, fp = file_data; i < n && fp; i++, fp = fp->next) {
2849
2850     /* initialize new dir entry */
2851     if (dp)
2852     {
2853        ip = (FileViewData *)XtMalloc(sizeof(FileViewData));
2854        memset(ip, 0, sizeof(FileViewData));
2855        ip->file_data = fp;
2856        ip->parent = dp;
2857        ip->ts = tsNotRead;
2858     }
2859
2860     /* read subdirectory */
2861     if (fp->is_subdir)
2862     {
2863         /* construct sub directory name */
2864         strncpy(subdir_name, directory_name, MAX_PATH - 1);
2865         if (strlen(subdir_name) > 0
2866             && subdir_name[strlen(subdir_name) - 1] != '/')
2867             strncat(subdir_name, "/", MAX_PATH - 1);
2868
2869         strncat(subdir_name, fp->file_name, MAX_PATH - 1);
2870         subdir_name[MAX_PATH - 1] = 0;
2871
2872       /* see if we know this entry from branch_list */
2873       if (!QueryBranchList(file_mgr_data, branch_list, subdir_name, &ts))
2874         /* not known: assume we shouldn't read this subdir */
2875         ts = tsNotRead;
2876
2877       if (level < read_level || ts != tsNotRead) {
2878
2879         rc = _ReadDir(w, file_mgr_data, host_name, subdir_name, ip,
2880                       level, read_level, branch_list);
2881         if (ip == NULL)
2882          ;
2883         else if (rc)
2884           ip->ts = tsError;
2885         else if (ts >= tsReading)
2886           ip->ts = ts;
2887         else if (level >= file_mgr_data->tree_show_level)
2888           ip->ts = tsNone;
2889         else if (file_mgr_data->tree_files == TREE_FILES_ALWAYS)
2890           ip->ts = tsAll;
2891         else
2892           ip->ts = tsDirs;
2893       }
2894     }
2895
2896     /* add new entry to linked list */
2897     if (dp)
2898     {
2899       *lp = ip;
2900       lp = &ip->next;
2901     }
2902   }
2903
2904   return 0;
2905 }
2906
2907
2908 /*--------------------------------------------------------------------
2909  *  ReadDir
2910  *    This is the main external entry point for reading directories.
2911  *------------------------------------------------------------------*/
2912
2913 int
2914 ReadDir(
2915   Widget w,
2916   FileMgrData *file_mgr_data,
2917   char *host_name,
2918   char *directory_name,
2919   FileViewData *dp,         /* directory info */
2920   int level,                /* tree level of this directory */
2921   int read_level,           /* deepest level to be read */
2922   char **branch_list)       /* list of tree branches to expand */
2923 {
2924    /* initially assume we are not busy */
2925    if (file_mgr_data->busy_status == not_busy)
2926       file_mgr_data->busy_detail = 0;
2927
2928    file_mgr_data->busy_status = initiating_readdir;
2929
2930    /* first pass: just check if any directory we need is busy */
2931    _ReadDir(w, file_mgr_data, host_name, directory_name, NULL, level,
2932             read_level, branch_list);
2933
2934    /* if a directory we need is busy, return now */
2935    if (file_mgr_data->busy_status == busy_readdir)
2936       return 0;
2937
2938    /*
2939     * All directories wee need are available.
2940     * Make a second pass for real.
2941     */
2942    file_mgr_data->busy_status = not_busy;
2943    return _ReadDir(w, file_mgr_data, host_name, directory_name, dp, level,
2944                    read_level, branch_list);
2945 }
2946
2947
2948 /*====================================================================
2949  *
2950  *  Routines that update the directory cache
2951  *
2952  *==================================================================*/
2953
2954 /*--------------------------------------------------------------------
2955  *  FileWindowMapUnmap
2956  *    Update mapped flag in view lists.
2957  *------------------------------------------------------------------*/
2958
2959 void
2960 FileWindowMapUnmap(
2961         FileMgrData *file_mgr_data)
2962
2963 {
2964    int i, j;
2965
2966    for (i = 0; i < directory_count; i++)
2967    {
2968       for (j = 0; j < directory_set[i]->numOfViews; j++)
2969       {
2970          if (file_mgr_data == directory_set[i]->directoryView[j].file_mgr_data)
2971          {
2972             directory_set[i]->directoryView[j].mapped = file_mgr_data->mapped;
2973             break;
2974          }
2975       }
2976    }
2977
2978    if (file_mgr_data->mapped && timer_suspended)
2979    {
2980       XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
2981       timer_suspended = False;
2982    }
2983 }
2984
2985
2986 /*--------------------------------------------------------------------
2987  *  RereadDirectory
2988  *    Read a directory already cached and update its contents.
2989  *------------------------------------------------------------------*/
2990
2991 void
2992 RereadDirectory(
2993         Widget w,
2994         char *host_name,
2995         char *directory_name )
2996 {
2997    Directory *directory;
2998
2999    DPRINTF(("RereadDirectory(%s, %s)\n", host_name, directory_name));
3000
3001    /*  Find the directory set entry.  */
3002    directory = FindDirectory(host_name, directory_name);
3003    if (directory != NULL)
3004    {
3005       /* reset errnum to make sure we'll get an error message */
3006       directory->errnum = 0;
3007
3008       /* Read the directory. */
3009       if (!directory->busy[activity_reading])
3010          ReadDirectoryFiles(w, directory);
3011    }
3012 }
3013
3014
3015 /*--------------------------------------------------------------------
3016  *  UpdateDirectory
3017  *    Check if any files were added or deleted in a directory
3018  *    and update the directory contents accordingly.
3019  *------------------------------------------------------------------*/
3020
3021 void
3022 UpdateDirectory(
3023         Widget w,
3024         char *host_name,
3025         char *directory_name )
3026 {
3027    Directory *directory;
3028
3029    DPRINTF(("UpdateDirectory(%s, %s)\n", host_name, directory_name));
3030
3031    /*  Find the directory set entry.  */
3032    directory = FindDirectory(host_name, directory_name);
3033    if (directory != NULL)
3034    {
3035       /* arrange for directory contents to be checked */
3036       if (!directory->busy[activity_update_all])
3037       {
3038          directory->busy[activity_update_all] = True;
3039          ScheduleActivity(directory);
3040       }
3041    }
3042 }
3043
3044
3045 /*====================================================================
3046  *
3047  *  Directory modification routines:
3048  *
3049  *  The following routines are provided to avoid unnecessary
3050  *  re-reads of whole directories.  For example, if the user
3051  *  renames a file, it's only necessary to remove the old file
3052  *  from the directory and add it back under its new name; there
3053  *  is no need to read the whole directory again.  Similarly,
3054  *  when a file is dropped on a directory, it's only necessary
3055  *  to add the one new file to the directory.
3056  *
3057  *  To accomplish this, the routines that rename or copy files
3058  *  make the following calls:
3059  *
3060  *    DirectoryBeginModify():  called before doing the operation
3061  *    DirectoryFileModified(): called once for each affected file
3062  *    DirectoryEndModify():    called when the operation is completed
3063  *
3064  *  The routines remember which files were modified, and when
3065  *  DirectoryEndModify is called, a background process is started,
3066  *  that re-stats and types just those files.
3067  *
3068  *  A complication arises from automatic re-reads triggered by
3069  *  a periodic timer (routine TimerEvent).  Since renaming or
3070  *  copying files changes the timestamp on the directory, the
3071  *  automatic re-read would re-read the whole directory soon after
3072  *  the operation is done, nullifying our efforts to avoid
3073  *  unnecessary re-reads.  Therefore:
3074  *
3075  *    - We don't do any automatic re-reads between calls to
3076  *      DirectoryBeginModify and DirectoryEndModify.
3077  *
3078  *    - If the directory timestamp hadn't changed at the time
3079  *      of the DirectoryBeginModify, then when the directory
3080  *      update triggered by DirectoryEndModify finishes, we
3081  *      set the modify_time in the directory_set to the current
3082  *      timestamp of the directory.  This means that the next
3083  *      automatic re-read won't be triggered unless the directory
3084  *      is modified again after the DirectoryEndModify.
3085  *
3086  *==================================================================*/
3087
3088 /*--------------------------------------------------------------------
3089  *  DirectoryAbortModify
3090  *    Decrement the modify_begin counter.
3091  *------------------------------------------------------------------*/
3092
3093 void
3094 DirectoryAbortModify(
3095         char *host_name,
3096         char *directory_name)
3097 {
3098    Directory *directory;
3099
3100    DPRINTF(("DirectoryAbortModify(%s, %s)\n", host_name, directory_name));
3101
3102    /*  Find the directory set entry.  */
3103    directory = FindDirectory(host_name, directory_name);
3104    if (directory != NULL)
3105    {
3106       directory->modify_begin--;
3107
3108       if (directory->modify_begin == 0)
3109          directory->was_up_to_date = True;
3110
3111       DPRINTF(("   modify_begin %d, up_to_date %d\n",
3112                directory->modify_begin, directory->was_up_to_date));
3113    }
3114 }
3115
3116
3117 /*--------------------------------------------------------------------
3118  *  DirectoryBeginModify
3119  *    Increment the modify_begin counter to suspend automatic
3120  *    re-reads until DirectoryEndModify is called.
3121  *------------------------------------------------------------------*/
3122
3123 void
3124 DirectoryBeginModify(
3125         char *host_name,
3126         char *directory_name)
3127 {
3128    Directory *directory;
3129
3130    DPRINTF(("DirectoryBeginModify(%s, %s)\n", host_name, directory_name));
3131
3132    /*  Find the directory set entry.  */
3133    directory = FindDirectory(host_name, directory_name);
3134    if (directory != NULL)
3135    {
3136       if (directory->modify_begin == 0)
3137          /* until we know better, assume the directory changed */
3138          directory->was_up_to_date = False;
3139
3140       /* increment the modify_begin counter */
3141       directory->modify_begin++;
3142
3143       DPRINTF(("   modify_begin %d, up_to_date %d\n",
3144                directory->modify_begin, directory->was_up_to_date));
3145    }
3146 }
3147
3148
3149 /*--------------------------------------------------------------------
3150  *  DirectoryModifyTime
3151  *    This routine should be called after DirectoryBeginModify and
3152  *    before doing any operation on the directory.  The parameter
3153  *    modify_time should be the current timestamp of the directory.
3154  *    By comparing the value to the modify_time stored in the
3155  *    directory set we decide whether the directory had already
3156  *    changed before the update operation began.
3157  *    Note: the reason for supplying a separate call for this check,
3158  *    instead of doing it inside DirectoryBeginModify(), is that we
3159  *    want to do the stat call that determines the current timestamp
3160  *    of the directory in a background process.  The background
3161  *    process that we start fo do the actual update is a convenient
3162  *    place to do this.
3163  *------------------------------------------------------------------*/
3164
3165 void
3166 DirectoryModifyTime(
3167         char *host_name,
3168         char *directory_name,
3169         long modify_time)
3170 {
3171    Directory *directory;
3172
3173    DPRINTF(("DirectoryModifyTime(%s, %s)\n", host_name, directory_name));
3174
3175 #ifdef SMART_DIR_UPDATE
3176    /*  Find the directory set entry.  */
3177    directory = FindDirectory(host_name, directory_name);
3178
3179    if (directory != NULL)
3180    {
3181       /* mark directory up-to-date if unchanged since last read */
3182       if (modify_time <= directory->modify_time)
3183          directory->was_up_to_date = True;
3184       DPRINTF(("   modify_begin %d, up_to_date %d\n",
3185                directory->modify_begin, directory->was_up_to_date));
3186    }
3187 #endif
3188 }
3189
3190
3191 /*--------------------------------------------------------------------
3192  *  DirectoryFileModified
3193  *    This routine is called when we know that a file in a directory
3194  *    has been modified, added or removed.  The file name is added
3195  *    to the list of modified files.  The next time an update
3196  *    background process is started, it will check all the files
3197  *    on the modfied list and update the corresponding FileData.
3198  *------------------------------------------------------------------*/
3199
3200 void
3201 DirectoryFileModified(
3202         char *host_name,
3203         char *directory_name,
3204         char *file_name)
3205 {
3206    Directory *directory;
3207    int i;
3208
3209    DPRINTF(("DirectoryFileModified(%s, %s, %s)\n",
3210             host_name, directory_name, file_name));
3211
3212    /*  Find the directory set entry.  */
3213    directory = FindDirectory(host_name, directory_name);
3214    if (directory != NULL)
3215    {
3216       /* see if the file is already on the list */
3217       for( i = 0; i < directory->modified_count; ++i )
3218         if( strcmp( directory->modified_list[i], file_name ) == 0 )
3219           return;
3220
3221       /* add the file to the modified_list */
3222       i = directory->modified_count++;
3223       directory->modified_list = (char **)
3224         XtRealloc((char *)directory->modified_list, (i + 1)*sizeof(char *));
3225       directory->modified_list[i] = XtNewString(file_name);
3226    }
3227 }
3228
3229
3230 /*--------------------------------------------------------------------
3231  *  DirectoryEndModify
3232  *    Start an update background process (will check all the files
3233  *    on the modfied list and update the corresponding FileData).
3234  *------------------------------------------------------------------*/
3235
3236 void
3237 DirectoryEndModify(
3238         char *host_name,
3239         char *directory_name)
3240 {
3241    Directory *directory;
3242
3243    DPRINTF(("DirectoryEndModify(%s, %s)\n", host_name, directory_name));
3244
3245    /*  Find the directory set entry.  */
3246    directory = FindDirectory(host_name, directory_name);
3247
3248    /* arrange for an update background process to be scheduled */
3249    if (directory != NULL)
3250    {
3251       directory->modify_begin--;
3252       DPRINTF(("   modify_begin %d, up_to_date %d, modified_count %d\n",
3253                directory->modify_begin,
3254                directory->was_up_to_date,
3255                directory->modified_count));
3256       if (directory->modified_count > 0)
3257       {
3258          Directory *subdir;
3259          char subdir_name[MAX_PATH + 1];
3260          char *p;
3261          int i;
3262
3263          /*
3264           * If any of the modifed files is a subdirectory that we have
3265           * cached, schedule an activity_checking_dir to make sure that
3266           * the subdirectory is still readable.
3267           */
3268          strcpy(subdir_name, directory_name);
3269          p = subdir_name + strlen(subdir_name);
3270          if (p[-1] != '/')
3271            *p++ = '/';
3272
3273          for (i = 0; i < directory->modified_count; i++)
3274          {
3275             strcpy(p, directory->modified_list[i]);
3276             subdir = FindDirectory(host_name, subdir_name);
3277             if (subdir)
3278             {
3279                DPRINTF(("   schedule check for subdir \"%s\"\n",
3280                         directory->modified_list[i]));
3281                subdir->busy[activity_checking_dir] = True;
3282                ScheduleActivity(subdir);
3283             }
3284          }
3285
3286 #ifdef SMART_DIR_UPDATE
3287          /* schedule a partial update of the modfied directory */
3288          if (directory->was_up_to_date)
3289             directory->busy[activity_update_some] = True;
3290          else
3291             directory->busy[activity_update_all] = True;
3292 #else
3293          /* schedule a full update of the modfied directory */
3294          directory->busy[activity_update_all] = True;
3295 #endif
3296          ScheduleActivity(directory);
3297       }
3298    }
3299 }
3300
3301
3302 /*--------------------------------------------------------------------
3303  *  UpdateDirectorySet
3304  *    We call this when we do a database update. It loops through
3305  *    the directory_set list and rereads each directory.
3306  *------------------------------------------------------------------*/
3307
3308 void
3309 UpdateDirectorySet( void )
3310 {
3311    int i;
3312
3313    DPRINTF(("UpdateDirectorySet ...\n"));
3314
3315    for (i = 0; i < directory_count; i++)
3316       if (!directory_set[i]->busy[activity_reading])
3317          ReadDirectoryFiles (NULL, directory_set[i]);
3318 }
3319
3320
3321
3322 /*--------------------------------------------------------------------
3323  *  UpdateCachedDirectories
3324  *    Update view list for all cached directories.
3325  *    Throw out any directories that are no longer being viewed.
3326  *------------------------------------------------------------------*/
3327
3328 void
3329 UpdateCachedDirectories(
3330         View **view_set,
3331         int view_count)
3332 {
3333    DialogData * dialog_data;
3334    FileMgrData * file_mgr_data;
3335    int i, j, k, n;
3336    Directory *directory;
3337
3338    /*
3339     * First step:
3340     *   clear the view list in all directory set entries
3341     */
3342    for (i = 0; i < directory_count; i++)
3343    {
3344       if( !(strcmp(directory_set[i]->directory_name, trash_dir) == 0) )
3345       {
3346          XtFree ((char *) directory_set[i]->directoryView);
3347          directory_set[i]->numOfViews = 0;
3348          directory_set[i]->directoryView = NULL;
3349          directory_set[i]->viewed = False;
3350       }
3351    }
3352
3353
3354    /*
3355     * Second step:
3356     *   reconstruct view lists by adding each directory found in the view
3357     *   set to the view list for the corresponding directory set entry
3358     */
3359    for (j = 0; j < view_count; j++)
3360    {
3361       dialog_data = (DialogData *) view_set[j]->dialog_data;
3362       file_mgr_data = (FileMgrData *) dialog_data->data;
3363
3364       /* loop through all direcories in this view */
3365       for (k = 0; k < file_mgr_data->directory_count; k++)
3366       {
3367          /* find the directory in the directory set */
3368          directory = FindDirectory(view_set[j]->host_name,
3369                                    file_mgr_data->directory_set[k]->name);
3370
3371          /* we expect the directory to be found; if not, something is wrong */
3372          if (directory == NULL)
3373          {
3374             fprintf(stderr, "Warning: %s:%s not found in directory set.\n",
3375                     view_set[j]->host_name,
3376                     file_mgr_data->directory_set[k]->name);
3377             continue;
3378          }
3379
3380          /* add the directory to the view list */
3381          n = directory->numOfViews;
3382          directory->directoryView = (DirectoryView *)
3383                                   XtRealloc ((char *) directory->directoryView,
3384                                              sizeof(DirectoryView) * (n + 1));
3385          directory->directoryView[n].file_mgr_data = file_mgr_data;
3386          directory->directoryView[n].mapped = file_mgr_data->mapped;
3387          directory->numOfViews++;
3388          directory->viewed = True;
3389       }
3390    }
3391
3392
3393    /*
3394     * Third step:
3395     *   remove all directories that have empty view lists
3396     */
3397    i = 0;
3398    while (i < directory_count)
3399    {
3400       if (directory_set[i]->numOfViews > 0 ||
3401           strcmp(directory_set[i]->directory_name, trash_dir) == 0)
3402       {
3403          /* Keep this directory in the directory set. */
3404          i++;
3405       }
3406       else
3407       {
3408          /* Delete the file data and remove from the directory set. */
3409
3410          DPRINTF(("UpdateCachedDirectories: removing %s:%s\n",
3411                   directory_set[i]->host_name,
3412                   directory_set[i]->directory_name));
3413
3414          FreeDirectory(directory_set[i]);
3415
3416          for (k = i; k < directory_count - 1; k++)
3417             directory_set[k] = directory_set[k + 1];
3418
3419          directory_count--;
3420       }
3421    }
3422
3423    /* Restart refresh timer, if necessary */
3424    if (timer_suspended && SomeWindowMapped())
3425    {
3426       XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
3427       timer_suspended = False;
3428    }
3429 }
3430
3431
3432 /*====================================================================
3433  *
3434  * Routines that return directory data
3435  *
3436  *==================================================================*/
3437
3438 /*--------------------------------------------------------------------
3439  * GetLongName
3440  *   Return a string that contains file information similar to "ls -l",
3441  *   including: permissions, owner, modified time, link (if any).
3442  *   Used for "view by attributes"
3443  *
3444  *   Example:
3445  *     -rw-r--r--  dld  staff  108314  Jul 26 15:16:36 1993 Directory.c
3446  *
3447  *------------------------------------------------------------------*/
3448
3449 char *
3450 GetLongName(
3451         FileData *file_data )
3452 {
3453 #ifdef NLS16
3454    struct tm * tms;
3455    char time_string[100];
3456 #else
3457    char * time_string;
3458 #endif /* NLS16 */
3459    char link_path[MAX_PATH + 5];
3460    static gid_t group_id = (gid_t)-1;
3461    static uid_t user_id = (uid_t)-1;
3462    struct group * group_data;
3463    struct passwd * user_data;
3464    static char group_name[20];
3465    static char user_name[20];
3466    char * long_name;
3467    time_t long_modify_time;
3468    char permission;
3469    char usr_read_priv, usr_write_priv, usr_exec_priv;
3470    char grp_read_priv, grp_write_priv, grp_exec_priv;
3471    char oth_read_priv, oth_write_priv, oth_exec_priv;
3472
3473    /*  Generate the long list name.  */
3474    long_name = (char *) XtMalloc(sizeof(char) * (MAX_PATH * 3));
3475    long_name[0]='\0';
3476
3477    /* Initially, assume their is not a soft link */
3478    link_path[0] = '\0';
3479
3480    if (file_data->errnum == 0)
3481    {
3482      if (file_data->stat.st_gid != group_id)
3483      {
3484        group_id = file_data->stat.st_gid;
3485        group_data = getgrgid (file_data->stat.st_gid);
3486        if (group_data)
3487        {
3488          strcpy (group_name, group_data->gr_name);
3489          if (strlen (group_name) == 0)
3490             strcpy (group_name, "root");
3491        }
3492        else
3493          strcpy (group_name, "root");
3494      }
3495
3496      if (file_data->stat.st_uid != user_id)
3497      {
3498        user_id = file_data->stat.st_uid;
3499        user_data = getpwuid (file_data->stat.st_uid);
3500        /* Initially, assume their is not a user name */
3501        user_name[0] = '\0';
3502        if (user_data)
3503          strcpy (user_name, user_data->pw_name);
3504        else
3505          sprintf(user_name,"%ld",(long)user_id);
3506      }
3507    }
3508    else
3509    {
3510       char error_msg[1024];
3511       int msg_len;
3512
3513       /* determine how much space we have for an error message */
3514       long_modify_time = 747616435;
3515                          /* just needed to determine the length of a date */
3516 #ifdef NLS16
3517       tms = localtime(&long_modify_time);
3518       strftime( time_string, 100,
3519                 GetSharedMessage(DIRECTORY_DATE_FORMAT),
3520                 tms);
3521 #else
3522       time_string = ctime ((time_t *)&long_modify_time);
3523       time_string[strlen(time_string)-1] = 0x0;
3524       time_string += 4;
3525 #endif
3526       msg_len = 10 + 3 + 9 + 1 + 9 + 1 + 9 + 1 + strlen(time_string);
3527
3528       /* generate the error message */
3529       strcpy(error_msg, "(");
3530       strncpy(error_msg + 1, strerror(file_data->errnum), msg_len - 2);
3531       error_msg[msg_len - 1] = '\0';
3532       strcat(error_msg, ")");
3533
3534       sprintf( long_name, "%-28.28s  %s  %9d %s",
3535                             file_data->file_name,
3536                             time_string,
3537                             0, error_msg );
3538
3539       return (long_name);
3540    }
3541
3542
3543    /* Build the permission string  */
3544    switch( file_data->stat.st_mode & S_IFMT )
3545    {
3546    case S_IFDIR:
3547      permission = 'd';
3548      break;
3549    case S_IFCHR:
3550      permission = 'c';
3551      break;
3552    case S_IFBLK:
3553      permission = 'b';
3554      break;
3555    case S_IFLNK:
3556      permission = 'l';
3557      break;
3558    default :
3559      permission = OPTION_OFF;
3560      break;
3561    }
3562
3563    if (file_data->stat.st_mode & S_IRUSR) usr_read_priv = READ_PRIV;
3564    else usr_read_priv = OPTION_OFF;
3565
3566    if (file_data->stat.st_mode & S_IWUSR) usr_write_priv = WRITE_PRIV;
3567    else usr_write_priv = OPTION_OFF;
3568
3569    if (file_data->stat.st_mode & S_IXUSR) usr_exec_priv = EXEC_PRIV;
3570    else usr_exec_priv = OPTION_OFF;
3571
3572
3573    if (file_data->stat.st_mode & S_IRGRP) grp_read_priv = READ_PRIV;
3574    else grp_read_priv = OPTION_OFF;
3575
3576    if (file_data->stat.st_mode & S_IWGRP) grp_write_priv = WRITE_PRIV;
3577    else grp_write_priv = OPTION_OFF;
3578
3579    if (file_data->stat.st_mode & S_IXGRP) grp_exec_priv = EXEC_PRIV;
3580    else grp_exec_priv = OPTION_OFF;
3581
3582
3583    if (file_data->stat.st_mode & S_IROTH) oth_read_priv = READ_PRIV;
3584    else oth_read_priv = OPTION_OFF;
3585
3586    if (file_data->stat.st_mode & S_IWOTH) oth_write_priv = WRITE_PRIV;
3587    else oth_write_priv = OPTION_OFF;
3588
3589    if (file_data->stat.st_mode & S_IXOTH) oth_exec_priv = EXEC_PRIV;
3590    else oth_exec_priv = OPTION_OFF;
3591
3592
3593    long_modify_time = file_data->stat.st_mtime;
3594 #ifdef NLS16
3595    tms = localtime(&long_modify_time);
3596    strftime( time_string, 100,
3597              GetSharedMessage(DIRECTORY_DATE_FORMAT),
3598              tms);
3599 #else
3600    time_string = ctime ((time_t *)&long_modify_time);
3601    time_string[strlen(time_string)-1] = 0x0;
3602    time_string += 4;
3603 #endif
3604
3605    /* Fill in the name of where the link goes */
3606    if (file_data->link)
3607    {
3608      strcpy( link_path, " -> " );
3609      strcpy( link_path + 4, file_data->link );
3610    }
3611
3612    {
3613 #define ELLIPSIS " (...) "
3614 #define NAME_PRECISION 28
3615 #ifdef MULTIBYTE
3616      if (! is_multibyte)
3617      {
3618 #endif
3619        int len = strlen( file_data->file_name );
3620        if( len > NAME_PRECISION )
3621        {
3622          int i;
3623          char name[NAME_PRECISION];
3624          sprintf( name, "%-20.20s%s", file_data->file_name, ELLIPSIS);
3625
3626          sprintf( long_name, "%-28.28s  %s  %9ld  %c%c%c%c%c%c%c%c%c%c  %-9s  %-9s  %s",
3627                   name,
3628                   time_string,
3629                   (long)file_data->stat.st_size,
3630                   permission,
3631                   usr_read_priv, usr_write_priv, usr_exec_priv,
3632                   grp_read_priv, grp_write_priv, grp_exec_priv,
3633                   oth_read_priv, oth_write_priv, oth_exec_priv,
3634                   user_name, group_name,
3635                   link_path );
3636        }
3637        else
3638        {
3639          sprintf( long_name, "%-28.28s  %s  %9ld  %c%c%c%c%c%c%c%c%c%c  %-9s  %-9s  %s",
3640                   file_data->file_name,
3641                   time_string,
3642                   (long)file_data->stat.st_size,
3643                   permission,
3644                   usr_read_priv, usr_write_priv, usr_exec_priv,
3645                   grp_read_priv, grp_write_priv, grp_exec_priv,
3646                   oth_read_priv, oth_write_priv, oth_exec_priv,
3647                   user_name, group_name,
3648                   link_path );
3649        }
3650 #ifdef MULTIBYTE
3651      } else {
3652        /* MULTIBYTE
3653         *
3654         * sprintf() counts width in bytes (not characters), moreover,
3655         * it fails (returns -1 and produces no output) if input string is not
3656         * a valid multibyte string (at least the glibc version), but we can't fail
3657         * to display a file because it's name has some invalid characters). So it looks
3658         * that instead of using sprintf() we have to format the file name part manually.
3659         */
3660        int len = DtCharCount( file_data->file_name );
3661        int copy_len =  len > NAME_PRECISION ?  NAME_PRECISION - sizeof(ELLIPSIS) + 1 : len;
3662        int byte_len = 0;
3663        int count;
3664
3665        long_name[0]='\0';
3666        /* properly copy copy_len characters of the multibyte string
3667           replacing invalid chars with '?' */
3668        for (count = 0;
3669             (count < copy_len) && *(file_data->file_name + byte_len);
3670             count ++)
3671        {
3672          int chr_bytes = mblen(file_data->file_name + byte_len, MB_CUR_MAX);
3673          if (chr_bytes > 0)
3674          {
3675            strncpy(long_name + byte_len, file_data->file_name + byte_len, chr_bytes);
3676          }
3677          else if (chr_bytes < 0)
3678          { /* invalid char */
3679            chr_bytes = 1;
3680            long_name[byte_len]='?';
3681          }
3682          else
3683          {
3684            /* null-wide character, won't really happen */
3685            break;
3686          }
3687          byte_len+=chr_bytes;
3688        }
3689        if (copy_len < len)
3690        {
3691          /* truncated name, add ellipsis */
3692          strncpy(long_name + byte_len, ELLIPSIS, sizeof(ELLIPSIS) - 1);
3693          byte_len+= sizeof(ELLIPSIS) - 1;
3694        }
3695        else
3696        {
3697          /* full name, pad it with spaces up to the proper length */
3698          for (; count <  NAME_PRECISION ; count++)
3699          {
3700            long_name[byte_len++]=' ';
3701          }
3702        }
3703        sprintf( long_name + byte_len, "  %s  %9ld  %c%c%c%c%c%c%c%c%c%c  %-9s  %-9s  %s",
3704                 time_string,
3705                 (long)file_data->stat.st_size,
3706                 permission,
3707                 usr_read_priv, usr_write_priv, usr_exec_priv,
3708                 grp_read_priv, grp_write_priv, grp_exec_priv,
3709                 oth_read_priv, oth_write_priv, oth_exec_priv,
3710                 user_name, group_name,
3711                 link_path );
3712      } /* is_multibyte */
3713 #endif  /* MULTIBYTE */
3714    }
3715
3716    return (long_name);
3717 }
3718
3719
3720
3721 /*--------------------------------------------------------------------
3722  *  DirectoryBusy
3723  *    See if path has a directory view of it or if any sub-directories
3724  *    of it are viewed.   The path parameter is of the for /foo/bar
3725  *------------------------------------------------------------------*/
3726
3727 Boolean
3728 DirectoryBusy(
3729         char *path )
3730 {
3731    FileMgrData * file_mgr_data;
3732    FileViewData  * sub_root;
3733    int i, j, k;
3734    int len = strlen(path);
3735
3736    for (i = 0; i < directory_count; i++)
3737    {
3738       /* check if this directory is equal to 'path' or a subdir of 'path' */
3739       if (directory_set[i]->viewed &&
3740           (strcmp (directory_set[i]->path_name, path) == 0 ||
3741            strncmp (directory_set[i]->path_name, path,len) == 0 &&
3742              directory_set[i]->path_name[len] == '/'
3743            ||
3744            directory_set[i]->tt_path_name != NULL &&
3745            (strcmp (directory_set[i]->tt_path_name, path) == 0 ||
3746             strncmp (directory_set[i]->tt_path_name, path,len) == 0 &&
3747               directory_set[i]->tt_path_name[len] == '/')))
3748       {
3749          /* check the views in the view list */
3750          for (j = 0; j < directory_set[i]->numOfViews; j++)
3751          {
3752             file_mgr_data = directory_set[i]->directoryView[j].file_mgr_data;
3753
3754             /* find the dir in the directory set for this view */
3755             for (k = 0; k < file_mgr_data->directory_count; k++)
3756                if (strcmp(file_mgr_data->directory_set[k]->name,
3757                           directory_set[i]->directory_name) == 0)
3758                {
3759                   break;
3760                }
3761             if (k == file_mgr_data->directory_count)
3762               continue;  /* not found ... something must be wrong! */
3763
3764             /*
3765              * Check if this directory is acutally visible.
3766              * If the directory is in a tree branch that is not currently
3767              * expanded, it is not visible and would not be considered busy.
3768              */
3769
3770             /* the tree root is always considered busy */
3771             if (k == 0)
3772               return True;
3773
3774             /* a subdir is considered busy if it is visible and at least
3775              * partially expanded */
3776             sub_root = file_mgr_data->directory_set[k]->sub_root;
3777             if (sub_root->displayed &&
3778                 (sub_root->ts == tsDirs && sub_root->ndir > 0  ||
3779                  sub_root->ts == tsAll &&
3780                    sub_root->ndir + sub_root->nfile > 0))
3781             {
3782               return True;
3783             }
3784          }
3785       }
3786    }
3787
3788    return (False);
3789 }
3790
3791
3792 /*--------------------------------------------------------------------
3793  *  GetDirectoryLogicalType
3794  *     Get logical type for the iconic path.
3795  *------------------------------------------------------------------*/
3796
3797 char *
3798 GetDirectoryLogicalType(
3799         FileMgrData *file_mgr_data,
3800         char *path)
3801 {
3802    int len;
3803    int n;
3804    Directory *directory;
3805    char *ptr;
3806
3807    /* 'path' must be a prefix of the current directory */
3808    len = strlen(path);
3809    if (strncmp(file_mgr_data->current_directory, path, len) != 0 ||
3810        (len > 1 &&
3811        file_mgr_data->current_directory[len] != '/' &&
3812        file_mgr_data->current_directory[len] != '\0'))
3813    {
3814       DPRINTF(("GetDirectoryLogicalType(%s): len %d, cur_dir %s\n",
3815                 path, len, file_mgr_data->current_directory));
3816       return NULL;
3817    }
3818
3819    /*  Find the directory set entry.  */
3820    directory = FindDirectory(file_mgr_data->host,
3821                              file_mgr_data->current_directory);
3822    if ((directory != NULL) &&
3823        (strcmp(file_mgr_data->current_directory,
3824                directory->directory_name) == 0))
3825    {
3826       /* if we don't have path_logical_types yet, we don't know */
3827       if (directory->path_logical_types == NULL)
3828          return NULL;
3829
3830       /* count the number of components in path */
3831       if (strcmp(path, "/") == 0)
3832          n = 0;
3833       else
3834       {
3835          n = 1;
3836          ptr = path + 1;
3837          while ((ptr = DtStrchr(ptr, '/')) != NULL)
3838          {
3839             ptr = ptr + 1;
3840             if (*ptr == '\0')
3841               break;
3842             else
3843               n++;
3844          }
3845       }
3846
3847       DPRINTF2(("GetDirectoryLogicalType(%s): n %d, type %s\n",
3848                 path, n, directory->path_logical_types[n]));
3849
3850       /* return type form path_logical_types array */
3851       return directory->path_logical_types[n];
3852    }
3853
3854    /* directory not found in directory_set */
3855
3856    return NULL;
3857 }
3858
3859
3860 /*====================================================================
3861  *
3862  * Routines for accessing position information
3863  *
3864  *==================================================================*/
3865
3866 /*--------------------------------------------------------------------
3867  *  GetDirectoryPositionInfo
3868  *     Get cached position info
3869  *------------------------------------------------------------------*/
3870
3871 int
3872 GetDirectoryPositionInfo(
3873         char *host_name,
3874         char *directory_name,
3875         PositionInfo **position_info)
3876 {
3877    Directory *directory;
3878
3879    directory = FindDirectory(host_name, directory_name);
3880    if (directory == NULL)
3881       return -1;
3882
3883    *position_info = directory->position_info;
3884
3885    return directory->position_count;
3886 }
3887
3888
3889 /*--------------------------------------------------------------------
3890  *  WritePosInfoProcess
3891  *    Main routine of the background process that writes the
3892  *    postion information file.
3893  *------------------------------------------------------------------*/
3894
3895 static int
3896 WritePosInfoProcess(
3897         int pipe_fd,
3898         Directory *directory,
3899         ActivityStatus activity)
3900 {
3901    char *fileName;
3902    int position_count = directory->position_count;
3903    PositionInfo *position_info = directory->position_info;
3904    FILE *f;
3905    int i, rc;
3906    Tt_status tt_status;
3907
3908    /* construct the full file name */
3909    fileName = ResolveLocalPathName( directory->host_name,
3910                                     directory->directory_name, positionFileName,
3911                                     home_host_name, &tt_status );
3912    /* Don't have to check for tt_status
3913       directory->host_name is home_host_name and ResolveLocalPathName will
3914       always return a good path
3915    */
3916    DPRINTF(("WritePosInfoProcess: count %d, file %s\n",
3917             position_count, fileName));
3918
3919    /* Remove old files, if no position information for this view */
3920    if (position_count <= 0)
3921       rc = unlink(fileName);
3922    else
3923    {
3924       /* open the file for writing */
3925       f = fopen(fileName, "w");
3926
3927       if (f == NULL)
3928       {
3929          /* Assume read-only directory, if we can't open the file */
3930          rc = 0;
3931       }
3932       else
3933       {
3934          chmod(fileName, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
3935
3936          fprintf(f, "%d\n", position_count);
3937          for (i = 0; i < position_count; i++)
3938          {
3939             fprintf(f, "%s\n%d %d %d\n",
3940                     position_info[i].name,
3941                     position_info[i].x,
3942                     position_info[i].y,
3943                     position_info[i].stacking_order);
3944          }
3945
3946          fclose(f);
3947          rc = 0;
3948       }
3949    }
3950
3951    /* send result back thorugh the pipe */
3952    DPRINTF(("WritePosInfoProcess: done (rc %d)\n", rc));
3953    write(pipe_fd, &rc, sizeof(int));
3954    XtFree( fileName );
3955    return 0;
3956 }
3957
3958
3959 /*--------------------------------------------------------------------
3960  *  WritePosInfoPipeCallback
3961  *    Callback routine that reads the return code sent through the
3962  *    pipe from the WritePosInfoProcess background process.
3963  *------------------------------------------------------------------*/
3964
3965 static void
3966 WritePosInfoPipeCallback(
3967    XtPointer client_data,
3968    int *fd,
3969    XtInputId *id)
3970 {
3971    PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
3972    Directory *directory = pipe_data->directory;
3973    int rc;
3974    int i;
3975
3976    /* get return code from the pipe */
3977    rc = -1;
3978    PipeRead(*fd, &rc, sizeof(int));
3979
3980    /* close the pipe and cancel the callback */
3981    close(*fd);
3982    XtFree( client_data );
3983    XtRemoveInput(*id);
3984
3985    /* verify that the directory still exists */
3986    if (DirectoryGone(directory))
3987    {
3988       ScheduleActivity(NULL);
3989       return;
3990    }
3991
3992    DPRINTF(("WritePosInfoPipeCallback: rc %d\n", rc));
3993
3994    /* reset the busy flag and schedule new work, if any */
3995    directory->busy[activity_writing_posinfo] = False;
3996    directory->activity = activity_idle;
3997    ScheduleActivity(directory);
3998 }
3999
4000
4001 /*--------------------------------------------------------------------
4002  *  SetDirectoryPositionInfo
4003  *     Update cached position info.  This routine schedules a
4004  *     background process that writes the modified information
4005  *     to the position info file.
4006  *------------------------------------------------------------------*/
4007
4008 int
4009 SetDirectoryPositionInfo(
4010         char *host_name,
4011         char *directory_name,
4012         int position_count,
4013         PositionInfo *position_info)
4014 {
4015    Directory *directory;
4016    Boolean unchanged;
4017    int i, j;
4018
4019    /* find the directory */
4020    directory = FindDirectory(host_name, directory_name);
4021    if (directory == NULL)
4022       return -1;
4023
4024    /* check if anything has changed */
4025    if (directory->position_count == position_count)
4026    {
4027       unchanged = True;
4028       for (i = 0; i < position_count && unchanged; i++)
4029       {
4030          for (j = 0; j < position_count; j++)
4031             if (strcmp(position_info[i].name,
4032                        directory->position_info[j].name) == 0)
4033             {
4034                break;
4035             }
4036
4037          if (j == position_count ||
4038              position_info[i].x != directory->position_info[j].x ||
4039              position_info[i].y != directory->position_info[j].y ||
4040              position_info[i].stacking_order !=
4041                                     directory->position_info[j].stacking_order)
4042          {
4043             unchanged = False;
4044          }
4045       }
4046
4047       /* if nothing changed, don't do anything */
4048       if (unchanged)
4049          return 0;
4050    }
4051
4052    /* free old position info  names*/
4053    for (i = 0; i < directory->position_count; i++)
4054       XtFree(directory->position_info[i].name);
4055
4056    /* realloc array, if necessary */
4057    if (directory->position_count != position_count)
4058    {
4059       directory->position_count = position_count;
4060       directory->position_info =
4061             (PositionInfo *) XtRealloc((char *)directory->position_info,
4062                                         position_count * sizeof(PositionInfo));
4063    }
4064
4065    /* replace old position info */
4066    for (i = 0; i < position_count; i++)
4067    {
4068       directory->position_info[i].name = XtNewString(position_info[i].name);
4069       directory->position_info[i].x = position_info[i].x;
4070       directory->position_info[i].y = position_info[i].y;
4071       directory->position_info[i].stacking_order =
4072                                               position_info[i].stacking_order;
4073    }
4074
4075    /* make sure positionFileName is initialized */
4076    if (positionFileName == NULL)
4077       InitializePositionFileName();
4078
4079    /* start background process that writes the position info file */
4080    directory->busy[activity_writing_posinfo] = True;
4081    ScheduleActivity(directory);
4082
4083    return 0;
4084 }
4085
4086
4087 /*====================================================================
4088  *
4089  * Timer functions
4090  *   These function are periodically called to scan all cached
4091  *   directories to see if any have been modified since last read.
4092  *   If any have, a background process is scheduled to re-read
4093  *   the directory.
4094  *
4095  *==================================================================*/
4096
4097 /*--------------------------------------------------------------------
4098  * SkipRefresh:
4099  *   Decide whether to skip an automatic re-read.
4100  *   (We don't do re-reads on directories that aren't currently
4101  *    being viewed and on the trash directory, if trash is currently
4102  *    being emptied.)
4103  *------------------------------------------------------------------*/
4104
4105 static Boolean
4106 SkipRefresh(
4107         Directory *directory)
4108 {
4109    int i;
4110
4111    /* don't refresh while the directory is being modified */
4112    if (directory->modify_begin > 0)
4113       return True;
4114
4115    /* verify that the directory is still being viewed */
4116    if (!directory->viewed)
4117       return True;
4118
4119    for (i = 0; i < directory->numOfViews; i++)
4120       if (directory->directoryView[i].mapped)
4121          break;
4122    if (i == directory->numOfViews)
4123       return True;
4124
4125    /* if trash is being emptied and this is the trash dir, skip it */
4126    if (removingTrash && strcmp(directory->directory_name, trash_dir) == 0)
4127       return True;
4128
4129    return False;
4130 }
4131
4132
4133 /*--------------------------------------------------------------------
4134  * TimerEventProcess
4135  *   Main routine for the background process that checks directory
4136  *   timestamps and the status of links.
4137  *------------------------------------------------------------------*/
4138
4139 static int
4140 TimerEventProcess(
4141         int pipe_fd,
4142         Directory *directory,
4143         ActivityStatus activity)
4144 {
4145    struct stat stat_buf;
4146    long modify_time;
4147    Boolean link_changed;
4148    Boolean was_broken;
4149    FileData *file_data;
4150    char full_name[MAX_PATH];
4151    char *namep;
4152    short pipe_msg;
4153    int prev_link_kind;
4154    int cur_link_kind;
4155    int link_rc;
4156    int rc;
4157
4158    /*
4159     * Do a stat on the directory to get its last-modified time.
4160     * Also check if we  still have read and execute/search permisssion.
4161     *
4162     * Note:
4163     *  It is important to get the timstamp in exactly the same way that the
4164     *  ReadDirectoryProcess does it; otherwise, we might get into a loop,
4165     *  where TimerEventProcess detects that the directory has changed
4166     *  and triggers ReadDirectoryProcess, but ReadDirectoryProcess won't
4167     *  be able to get a new timestamp and update the directory structure,
4168     *  so the next time TimerEventProcess runs it will trigger another
4169     *  ReadDirectoryProcess, and so on ...
4170     */
4171    if (CheckAccess(directory->path_name, R_OK | X_OK) != 0 ||
4172        stat(directory->path_name, &stat_buf) != 0)
4173    {
4174       /* stat or access failed */
4175       rc = errno;
4176       modify_time = 0;
4177    }
4178    else
4179    {
4180       /* stat succeeded and the directory is still readable */
4181       rc = 0;
4182       modify_time = stat_buf.st_mtime;
4183    }
4184
4185    /*
4186     * If requested, also check if any links broken.
4187     *
4188     * Again: it is important that we determine the kind of link
4189     * (valid, recursive, or broken) in exactly the same way that
4190     * ReadDirectoryProcess does it (see comment above)!
4191     */
4192    link_changed = False;
4193    if (rc == 0 && activity == activity_checking_links)
4194    {
4195       strcpy(full_name, directory->path_name);
4196       namep = full_name + strlen(full_name);
4197       if (namep[-1] != '/')
4198         *namep++ = '/';
4199
4200       /* check all links */
4201       for (file_data = directory->file_data;
4202            file_data && !link_changed;
4203            file_data = file_data->next)
4204       {
4205          /* Only worry about links */
4206          if (file_data->link == NULL)
4207             continue;
4208
4209          /* Check if the file is still a symbolic link */
4210          strcpy(namep, file_data->file_name);
4211          link_rc = lstat(full_name, &stat_buf);
4212          if (link_rc != 0 || (stat_buf.st_mode & S_IFMT) != S_IFLNK)
4213          {
4214             /* no longer a link */
4215             link_changed = True;
4216             break;
4217          }
4218
4219          /* Check what kind of link this was the last time we looked:
4220           * a normal link (1), a recursive link (2), or an otherwise
4221           * broken link (3) */
4222          if (strcmp(file_data->logical_type, LT_BROKEN_LINK) == 0)
4223             prev_link_kind = 3;
4224          else if (strcmp(file_data->logical_type, LT_RECURSIVE_LINK) == 0)
4225             prev_link_kind = 2;
4226          else
4227             prev_link_kind = 1;
4228
4229          /* Check what kind of link it is now */
4230          if (_DtFollowLink(full_name) == NULL)
4231             cur_link_kind = 2;  /* recursive link */
4232          else if (stat(full_name, &stat_buf) != 0)
4233             cur_link_kind = 3;  /* broken link */
4234          else
4235             cur_link_kind = 1;  /* a valid link */
4236
4237          /* now we can tell if the link has changed */
4238          if (prev_link_kind != cur_link_kind)
4239            link_changed = True;
4240       }
4241    }
4242
4243    /* send result back through the pipe */
4244    write(pipe_fd, &rc, sizeof(int));
4245    write(pipe_fd, &modify_time, sizeof(long));
4246    write(pipe_fd, &link_changed, sizeof(Boolean));
4247    return 0;
4248 }
4249
4250
4251 /*--------------------------------------------------------------------
4252  * StickyProcIdle:
4253  *   Mark sticky background process as idle.  If there are too
4254  *   may idle sticky procs, cause some of them to exit.
4255  *------------------------------------------------------------------*/
4256
4257 static void
4258 StickyProcIdle(
4259    ActivityStatus activity,
4260    StickyProcDesc *sticky_proc,
4261    int max_procs)
4262 {
4263    StickyProcDesc *p, **lp;
4264    int i, n;
4265
4266    /* mark the process as idle */
4267    sticky_proc->idle = True;
4268
4269    /* if there are too many idle procs, make some of them go away */
4270    n = 0;
4271    lp = &ActivityTable[activity].sticky_procs;
4272    for (p = *lp; p; p = *lp)
4273    {
4274       if (!p->idle)
4275          lp = &p->next;
4276       else if (n < max_procs)
4277       {
4278          n++;
4279          lp = &p->next;
4280       }
4281       else
4282       {
4283          DPRINTF2(("StickyProcIdle: end sticky proc %ld\n", (long)p->child));
4284          PipeWriteString(p->pipe_m2s_fd, NULL);
4285          close(p->pipe_s2m_fd);
4286          close(p->pipe_m2s_fd);
4287          *lp = p->next;
4288          XtFree((char *)p);
4289       }
4290    }
4291 }
4292
4293
4294 /*--------------------------------------------------------------------
4295  * TimerPipeCallback
4296  *   Callback routine that reads information sent through the
4297  *   pipe from the TimerEventProcess background process.
4298  *------------------------------------------------------------------*/
4299
4300 static void
4301 TimerPipeCallback(
4302    XtPointer client_data,
4303    int *fd,
4304    XtInputId *id)
4305 {
4306    PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
4307    Directory *directory = pipe_data->directory;
4308    int rc;
4309    long modify_time;
4310    Boolean link_changed;
4311    int i;
4312
4313    /* get return code from the pipe */
4314    rc = -1;
4315    PipeRead(*fd, &rc, sizeof(int));
4316    PipeRead(*fd, &modify_time, sizeof(long));
4317    PipeRead(*fd, &link_changed, sizeof(Boolean));
4318
4319    /* close the pipe and cancel the callback */
4320    if (pipe_data->sticky_proc)
4321       StickyProcIdle(pipe_data->activity, pipe_data->sticky_proc,
4322                      maxRereadProcsPerTick);
4323    else
4324       close(*fd);
4325    XtFree( client_data );
4326    XtRemoveInput(*id);
4327
4328    /* verify that the directory still exists */
4329    if (DirectoryGone(directory))
4330    {
4331       ScheduleActivity(NULL);
4332       return;
4333    }
4334
4335    DPRINTF2((
4336      "TimerPipeCallback: rc %d (was %d), time %ld (was %ld), link change %d\n",
4337      rc, directory->errnum, modify_time, (long)directory->modify_time, link_changed));
4338
4339    /* reset the busy flag and schedule new work, if any */
4340    directory->busy[directory->activity] = False;
4341    directory->activity = activity_idle;
4342    ScheduleActivity(directory);
4343
4344    /* if directory-read already in progress, nothing more to do here */
4345    if (directory->busy[activity_reading] ||
4346        directory->busy[activity_update_all])
4347       return;
4348
4349    /* skip this directory if it is no longer being viewed */
4350    if (SkipRefresh(directory))
4351       return;
4352
4353    /* if the directory was modified or links changed, re-read it */
4354    if (rc == 0)
4355    {
4356       if (link_changed)
4357       {
4358          DPRINTF(("TimerPipeCallback: %s link changed\n",
4359                   directory->directory_name));
4360          ReadDirectoryFiles(NULL, directory);
4361       }
4362       else if (modify_time != directory->modify_time || directory->errnum != 0)
4363       {
4364          DPRINTF(("TimerPipeCallback: %s modified\n",
4365                   directory->directory_name));
4366          directory->busy[activity_update_all] = True;
4367          ScheduleActivity(directory);
4368       }
4369    }
4370    else
4371    {
4372       if (directory->errnum == 0)
4373       {
4374          directory->errnum = rc;
4375          directory->errmsg_needed = True;
4376          ReadDirectoryFiles(NULL, directory);
4377       }
4378    }
4379 }
4380
4381
4382 /*--------------------------------------------------------------------
4383  * CheckDesktopProcess
4384  *   Main routine for the background process that checks each desktop
4385  *   objects to see if the file that it refers to has disappeared
4386  *   or has changed type.
4387  *------------------------------------------------------------------*/
4388
4389 static int
4390 CheckDesktopProcess(
4391         int pipe_fd,
4392         Directory *directory,
4393         ActivityStatus activity)
4394 {
4395    int i, n;
4396    DesktopRec *desktopWindow;
4397    FileViewData *file_view_data;
4398    char *full_path;
4399    Tt_status tt_status;
4400    struct stat stat_buf;
4401    short pipe_msg;
4402    FileData2 file_data2;
4403    FileData *old_data, *new_data;
4404
4405    for (i = 0; i < desktop_data->numIconsUsed; i++)
4406    {
4407       desktopWindow = desktop_data->desktopWindows[i];
4408       file_view_data = desktopWindow->file_view_data;
4409
4410       full_path = ResolveLocalPathName( desktopWindow->host,
4411                                         desktopWindow->dir_linked_to,
4412                                         desktopWindow->file_name,
4413                                         home_host_name, &tt_status);
4414
4415       /* Check if the file still exists */
4416       errno = 0;
4417       if (lstat(full_path, &stat_buf) < 0)
4418       {
4419          /* the real file no longer exists */
4420          DPRINTF2((
4421            "CheckDesktopProcess: sending PIPEMSG_DESKTOP_REMOVED for %s\n",
4422            full_path));
4423          pipe_msg = PIPEMSG_DESKTOP_REMOVED;
4424          write(pipe_fd, &pipe_msg, sizeof(short));
4425          PipeWriteString(pipe_fd, desktopWindow->host);
4426          PipeWriteString(pipe_fd, desktopWindow->dir_linked_to);
4427          PipeWriteString(pipe_fd, desktopWindow->file_name);
4428       }
4429       else
4430       {
4431          Boolean IsToolBox;
4432          /* See if the type has changed */
4433          old_data = file_view_data->file_data;
4434
4435          if(directory->directoryView && directory->directoryView->file_mgr_data)
4436              IsToolBox = directory->directoryView->file_mgr_data->toolbox;
4437          else
4438              IsToolBox = False;
4439
4440          ReadFileData2(&file_data2, full_path, NULL,IsToolBox);
4441          new_data = FileData2toFileData(&file_data2, &n);
4442
4443          if (new_data->physical_type != old_data->physical_type ||
4444              strcmp(new_data->logical_type, old_data->logical_type) != 0)
4445          {
4446             /* the type has changed */
4447             DPRINTF2((
4448               "CheckDesktopProcess: sending PIPEMSG_DESKTOP_CHANGED for %s\n",
4449               full_path));
4450             DPRINTF2((
4451               "  old type %d %s, new type %d %s\n",
4452               old_data->physical_type, old_data->logical_type,
4453               new_data->physical_type, new_data->logical_type));
4454
4455             pipe_msg = PIPEMSG_DESKTOP_CHANGED;
4456             write(pipe_fd, &pipe_msg, sizeof(short));
4457             PipeWriteString(pipe_fd, desktopWindow->host);
4458             PipeWriteString(pipe_fd, desktopWindow->dir_linked_to);
4459             PipeWriteString(pipe_fd, desktopWindow->file_name);
4460
4461             PipeWriteFileData(pipe_fd, new_data);
4462          }
4463
4464          FreeFileData(new_data, True);
4465       }
4466       XtFree(full_path);
4467       full_path = NULL;
4468    }
4469
4470    /* send a 'done' msg through the pipe */
4471    DPRINTF2(("CheckDesktopProcess: sending DONE\n"));
4472    pipe_msg = PIPEMSG_DONE;
4473    write(pipe_fd, &pipe_msg, sizeof(short));
4474    return 0;
4475 }
4476
4477
4478 /*--------------------------------------------------------------------
4479  * CheckDesktopPipeCallback
4480  *   Callback routine that reads information sent through the
4481  *   pipe from the CheckDesktopProcess background process.
4482  *------------------------------------------------------------------*/
4483
4484 static void
4485 CheckDesktopPipeCallback(
4486    XtPointer client_data,
4487    int *fd,
4488    XtInputId *id)
4489 {
4490    PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
4491    Directory *directory = pipe_data->directory;
4492    short msg;
4493    char *host, *dir_linked_to, *file_name;
4494    FileData *new_data, *old_data;
4495    Boolean found;
4496    DesktopRec *desktopWindow;
4497    int i, n;
4498
4499    /* read the next msg from the pipe */
4500    msg = -1;
4501    n = PipeRead(*fd, &msg, sizeof(short));
4502
4503    if (msg == PIPEMSG_DESKTOP_REMOVED ||
4504        msg == PIPEMSG_DESKTOP_CHANGED)
4505    {
4506       /* get information from pipe */
4507       host = PipeReadString(*fd);
4508       dir_linked_to = PipeReadString(*fd);
4509       file_name = PipeReadString(*fd);
4510       if (msg == PIPEMSG_DESKTOP_CHANGED)
4511          new_data = PipeReadFileData(*fd);
4512       else
4513          new_data = NULL;
4514
4515       DPRINTF2((
4516         "CheckDesktopPipeCallback: msg %d: host %s, dir %s, name %s\n",
4517         msg, host, dir_linked_to, file_name));
4518
4519
4520       /* find the desktop object */
4521       found = False;
4522       for (i = 0; i < desktop_data->numIconsUsed; i++)
4523       {
4524          desktopWindow = desktop_data->desktopWindows[i];
4525
4526          if (strcmp(host, desktopWindow->host) == 0 &&
4527              strcmp(dir_linked_to, desktopWindow->dir_linked_to) == 0 &&
4528              strcmp(file_name, desktopWindow->file_name) == 0)
4529          {
4530             found = True;
4531             break;
4532          }
4533       }
4534
4535       /* remove or update the desktop object, if found */
4536       if (! found)
4537       {
4538         /* nothing to do */
4539       }
4540       else if (msg == PIPEMSG_DESKTOP_REMOVED)
4541       {
4542          /* remove the desktop object */
4543          DesktopObjectRemoved(desktopWindow);
4544       }
4545       else /* msg == PIPEMSG_DESKTOP_CHANGED */
4546       {
4547          /* replace file data */
4548          old_data = desktopWindow->file_view_data->file_data;
4549          FreeFileData(old_data, False);
4550          memcpy(old_data, new_data, sizeof(FileData));
4551          XtFree((char *)new_data);
4552          new_data = NULL;
4553
4554          /* update the desktop object */
4555          DesktopObjectChanged(desktopWindow);
4556       }
4557
4558       /* free storage */
4559       XtFree(host);
4560       XtFree(dir_linked_to);
4561       XtFree(file_name);
4562       if (new_data)
4563         FreeFileData(new_data, True);
4564    }
4565
4566    else if (msg == PIPEMSG_DONE)
4567    {
4568       /* close the pipe and cancel the callback */
4569       close(*fd);
4570       XtFree( client_data );
4571       XtRemoveInput(*id);
4572
4573       /* reset the busy flag and schedule new work, if any */
4574       directory->busy[directory->activity] = False;
4575       directory->activity = activity_idle;
4576       ScheduleActivity(directory);
4577    }
4578 }
4579
4580
4581 /*--------------------------------------------------------------------
4582  *
4583  * CheckDesktop
4584  *   Arrange for a CheckDesktopProcess background process to be
4585  *   started (checks each desktop objects to see if the file that
4586  *   it refers to has disappeared or has changed type).
4587  *
4588  *------------------------------------------------------------------*/
4589
4590 void
4591 CheckDesktop( void )
4592 {
4593   dummy_directory->busy[activity_checking_desktop] = True;
4594   ScheduleActivity(dummy_directory);
4595 }
4596
4597
4598 /*--------------------------------------------------------------------
4599  * TimerEvent
4600  *   This routine is called periodically.  It schedules a
4601  *   TimerEventProcess background process to be started for every
4602  *   directory in the cache.
4603  *------------------------------------------------------------------*/
4604
4605 /* comparison routine for qsort */
4606 static int
4607 CheckListCmp(
4608         int *p1,
4609         int *p2 )
4610 {
4611    return directory_set[*p1]->last_check - directory_set[*p2]->last_check;
4612 }
4613
4614
4615 static void
4616 TimerEvent(
4617         XtPointer client_data,
4618         XtIntervalId *id )
4619 {
4620    static int *check_list = NULL;
4621    static int check_alloc = 0;
4622    int i, j, n;
4623
4624
4625    DPRINTF2(("Directory::TimerEvent\n"));
4626
4627    if (dragActive)
4628    {
4629       /*
4630        * Don't change any directories while a drag is active.
4631        *
4632        * Reason: drag callbacks are called with a pointer to a FileViewData
4633        * structure; if a directory is reread while a drag is active,
4634        * the pointer would become invalid, causing unpredictable behavior.
4635        *
4636        * Schedule the next TimerEvent in 1/2 second, so that check will
4637        * be done soon after the drag is finished.
4638        */
4639       XtAppAddTimeOut (app_context, 500, TimerEvent, NULL);
4640       return;
4641    }
4642
4643    /* update tick count */
4644    tick_count++;
4645
4646    /* determine if we should also check for broken links this time */
4647    if (checkBrokenLink > 0 &&
4648        tick_count >= lastLinkCheckTick + ticksBetweenLinkChecks)
4649    {
4650      /* set link_check_needed flag on all directores */
4651      for (i = 0; i < directory_count; i++)
4652      {
4653         /* skip this directory if no view is mapped */
4654         if (SkipRefresh(directory_set[i]))
4655            continue;
4656
4657         /* if a check is already in progress, don't start another one */
4658         if (directory_set[i]->busy[activity_checking_links])
4659            continue;
4660
4661         /* arrange for background process to be scheduled */
4662         directory_set[i]->link_check_needed = True;
4663      }
4664
4665      lastLinkCheckTick = tick_count;
4666    }
4667
4668    /* make sure check_list array is big enough */
4669    if (directory_count > check_alloc)
4670    {
4671       check_alloc = directory_count + 5;
4672       check_list =
4673          (int *)XtRealloc((char *)check_list, check_alloc*sizeof(int));
4674    }
4675
4676    /* get a list of all directories that need to be checked */
4677    n = 0;
4678    for (i = 0; i < directory_count; i++)
4679    {
4680       /* skip this directory if no view is mapped */
4681       if (SkipRefresh(directory_set[i]))
4682          continue;
4683
4684       /* if a stat is already in progress, don't start another one */
4685       if (directory_set[i]->busy[activity_checking_dir] ||
4686           directory_set[i]->busy[activity_checking_links])
4687          continue;
4688
4689       /* add this directory to the check list */
4690       check_list[n++] = i;
4691    }
4692
4693    /*
4694     * Next we want to schedule a background process to be started
4695     * for each directory in the check_list.  However, the variable
4696     * maxRereadProcsPerTick puts a limit on the number of such
4697     * background processes started per clock tick (i.e., per call
4698     * to this routine).  Hence we sort check_list by last_check
4699     * (records the tick count when a directory was last read or
4700     * checked) and schedule backround processes on those dirs that
4701     * haven't been checked in the longest time.
4702     */
4703    qsort(check_list, n, sizeof(int), (int (*)())CheckListCmp);
4704
4705    /* arrange for background process to be started */
4706    for (j = 0; j < n && j < maxRereadProcsPerTick; j++)
4707    {
4708       i = check_list[j];
4709       if (directory_set[i]->link_check_needed)
4710       {
4711          directory_set[i]->link_check_needed = False;
4712          directory_set[i]->busy[activity_checking_links] = True;
4713       }
4714       else
4715          directory_set[i]->busy[activity_checking_dir] = True;
4716       ScheduleActivity(directory_set[i]);
4717       directory_set[i]->last_check = tick_count;
4718    }
4719
4720    /*  Reset the timeout for the next interval.  */
4721    if (SomeWindowMapped())
4722       XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
4723    else
4724       timer_suspended = True;
4725 }
4726
4727
4728 /*--------------------------------------------------------------------
4729  * TimerEventBrokenLinks
4730  *   This routine is called periodically.  It checks whether any
4731  *   desktop object is broken (i.e., the object it refers to no
4732  *   longer exists.
4733  *------------------------------------------------------------------*/
4734
4735 void
4736 TimerEventBrokenLinks(
4737         XtPointer client_data,
4738         XtIntervalId *id )
4739 {
4740    int i;
4741
4742    DPRINTF2(("Directory::TimerEventBrokenLinks\n"));
4743
4744    if (!dragActive)
4745    {
4746       /* go check the desktop objects */
4747       if (desktop_data->numIconsUsed > 0)
4748          CheckDesktop();
4749    }
4750
4751    /*  Reset the timeout for the next interval.  */
4752    if (desktop_data->numIconsUsed > 0)
4753    {
4754      checkBrokenLinkTimerId = XtAppAddTimeOut( app_context,
4755                                                checkBrokenLink * 1000,
4756                                                TimerEventBrokenLinks,
4757                                                NULL );
4758    }
4759    else
4760    {
4761      checkBrokenLinkTimerId = None;
4762    }
4763 }
4764
4765
4766 /*====================================================================
4767  *
4768  * Background process scheduler
4769  *
4770  *   The routines below schedule background activity, making sure
4771  *   that there aren't too many processes running at the same time.
4772  *
4773  *==================================================================*/
4774
4775 /*--------------------------------------------------------------------
4776  *  ScheduleDirectoryActivity
4777  *    If there is any work to do for a directory, and if there is
4778  *    no backgroud process currently running for that directory,
4779  *    then fork a process to do the work.
4780  *------------------------------------------------------------------*/
4781
4782 static void
4783 ScheduleDirectoryActivity(
4784    Directory *directory)
4785 {
4786    static char *pname = "ScheduleActivity";
4787    ActivityStatus activity;
4788    PipeCallbackData *pipe_data;
4789    Boolean all_views_active;
4790    Boolean this_view_active;
4791    int i, j, k;
4792    int n_active, n_checking;
4793    int save_last_check = 0;
4794    FileMgrData *file_mgr_data;
4795    Boolean sticky;
4796    StickyProcDesc *p;
4797    int pipe_s2m_fd[2] = {-1, -1};  /* for msgs from backgroundnd proc (slave to master) */
4798    int pipe_m2s_fd[2] = {-1, -1};  /* for msgs to backgroundnd proc (master to slave) */
4799    pid_t pid = 0;
4800    char *s;
4801    int rc;
4802
4803    /* If already active, don't start anything new. */
4804    if (directory->activity != activity_idle)
4805       return;
4806
4807    /* Decide what to do next */
4808    for (activity = 0; activity < activity_idle; activity++)
4809       if (directory->busy[activity])
4810          break;
4811
4812    /* If nothing to do, return */
4813    if (activity == activity_idle)
4814       return;
4815
4816    DPRINTF2(("ScheduleActivity: activity %d, busy %c%c%c%c%c%c%c, dir %s\n",
4817              directory->activity,
4818              directory->busy[activity_writing_posinfo]? 'W': '-',
4819              directory->busy[activity_reading]?         'R': '-',
4820              directory->busy[activity_update_all]?      'A': '-',
4821              directory->busy[activity_update_some]?     'U': '-',
4822              directory->busy[activity_checking_links]?  'B': '-',
4823              directory->busy[activity_checking_desktop]? 'D': '-',
4824              directory->busy[activity_checking_dir]?    'C': '-',
4825              directory->directory_name));
4826
4827    /* Don't start more than a certain number of background processed */
4828    n_active = 0;
4829    n_checking = 0;
4830    for (j = 0; j < directory_count; j++)
4831    {
4832       if (directory_set[j]->activity != activity_idle)
4833          n_active++;
4834       if (directory_set[j]->activity == activity_checking_links ||
4835           directory_set[j]->activity == activity_checking_dir)
4836          n_checking++;
4837    }
4838    if (dummy_directory->activity != activity_idle)
4839    {
4840       n_active++;
4841       n_checking++;
4842    }
4843    if (n_active >= maxDirectoryProcesses ||
4844        n_checking >= maxRereadProcesses)
4845    {
4846       DPRINTF2(("ScheduleActivity:  too many processes\n"));
4847       return;
4848    }
4849
4850    /*
4851     * We don't want to start more than one background process per view.
4852     * In tree mode one view may show more than one directory.
4853     * Hence we go through the view list for this directory and for each
4854     * view, we check if the same view appears on the view list of some
4855     * other directory that currently has active background activity.
4856     * If all vies on this directory have other activity, then we won't
4857     * start anything new.
4858     */
4859    if (directory->numOfViews > 0)
4860    {
4861      all_views_active = True;
4862      for (i = 0; i < directory->numOfViews; i++)
4863      {
4864       /* get file_mgr_data for this view */
4865        file_mgr_data = directory->directoryView[i].file_mgr_data;
4866
4867       /* see if the same view appears in the view list of a non-idle dir */
4868        this_view_active = False;
4869        for (j = 0; j < directory_count && !this_view_active; j++)
4870        {
4871          /* we are only interested in directories that are not idle */
4872          if (directory_set[j]->activity == activity_idle)
4873            continue;
4874
4875          /* see if the view appears in the view list */
4876          for (k = 0; k < directory_set[j]->numOfViews; k++)
4877          {
4878            if (directory_set[j]->directoryView[k].file_mgr_data ==
4879                file_mgr_data)
4880            {
4881              this_view_active = True;
4882              break;
4883            }
4884          }
4885        }
4886
4887        if (!this_view_active)
4888        {
4889          all_views_active = False;
4890          break;
4891        }
4892      }
4893
4894      if (all_views_active)
4895      {
4896        DPRINTF2(("ScheduleActivity:  all views busy\n"));
4897        return;
4898      }
4899    }
4900
4901    /* now we are ready to start the next activity */
4902    directory->activity = activity;
4903    if (activity == activity_reading ||
4904        activity == activity_update_all ||
4905        activity == activity_checking_dir ||
4906        activity == activity_checking_links)
4907    {
4908       save_last_check = directory->last_check;
4909       directory->last_check = tick_count;
4910    }
4911
4912    /*
4913     * Special optimization for periodic background processes
4914     * (currently only used for activity_checking_dir):
4915     * Since this is done frequently, we don't want to fork new process each
4916     * time.  Hence, instead of exiting when it's done, the background process
4917     * is "sticky", i.e., it will stay around waiting for a message on stdin,
4918     * so it can be re-used the next time around.  A linked list of sticky
4919     * procs that are currently active is maintained in the ActivityTable.
4920     */
4921    sticky = ActivityTable[activity].sticky;
4922    if (sticky)
4923    {
4924       /* see if we can find an idle sticky proc that can do the work */
4925       for (p = ActivityTable[activity].sticky_procs; p; p = p->next)
4926          if (p->idle)
4927             break;
4928    }
4929    else
4930       p = NULL;
4931
4932    if (p)
4933    {
4934       /* We found an idle sticky proc that can be used */
4935       DPRINTF2(("ScheduleActivity:  use sticky proc %ld\n", (long)p->child));
4936
4937       /* Send the directory name to the sticky proc */
4938
4939       if (PipeWriteString(p->pipe_m2s_fd, directory->path_name) < 0) {
4940         StickyProcDesc *d;
4941
4942         /* the pipe is broken, remove the old proc then start a new one */
4943         for (d = ActivityTable[activity].sticky_procs; d && p; d = d->next) {
4944           if (d == p)
4945           {
4946             /* the proc listed 1st is dead, remove it */
4947             ActivityTable[activity].sticky_procs = p->next;
4948             XtFree((void *)p);
4949             p = NULL;
4950           }
4951           else if (d->next == p)
4952           {
4953             /* the process "p" is dead, remove it */
4954             d->next = p->next;
4955             XtFree((void *)p);
4956             p = NULL;
4957           }
4958         }
4959       }
4960       else
4961       {
4962         pipe_s2m_fd[0] = p->pipe_s2m_fd;
4963         pid = p->child;
4964         p->idle = False;
4965       }
4966    }
4967
4968
4969    if (!p)
4970    {
4971       /* Need to fork a new background process */
4972
4973       /* create a pipe for reading data from the background proc */
4974       pipe(pipe_s2m_fd);
4975
4976       /* creating a new sticky proc? */
4977       if (sticky)
4978       {
4979          /* also need a pipe for sending msgs to the sticky proc */
4980          pipe(pipe_m2s_fd);
4981
4982          /* add entry to the list of sticky procs */
4983          p = (StickyProcDesc *) XtMalloc(sizeof(StickyProcDesc));
4984          p->next = ActivityTable[activity].sticky_procs;
4985          ActivityTable[activity].sticky_procs = p;
4986
4987          p->pipe_s2m_fd = pipe_s2m_fd[0];
4988          p->pipe_m2s_fd = pipe_m2s_fd[1];
4989          p->idle = False;
4990       }
4991
4992       /* fork a background process */
4993       pid = fork();
4994
4995       if (pid == -1)
4996       {
4997           DBGFORK(("%s:  fork failed for activity %d: %s\n",
4998                     pname, activity, strerror(errno)));
4999
5000           fprintf(stderr,
5001                 "%s:  fork failed, ppid %d, pid %d, activity %d: error %d=%s\n",
5002                 pname, getppid(), getpid(), activity, errno, strerror(errno));
5003
5004           directory->activity = activity_idle;
5005           directory->last_check = save_last_check;
5006
5007           /* close unused pipe connections */
5008           close(pipe_s2m_fd[0]);    /* child won't read from this pipe */
5009           close(pipe_s2m_fd[1]);    /* parent won't write to this pipe */
5010           if (sticky)
5011           {
5012              close(pipe_m2s_fd[1]); /* child won't write to this pipe */
5013              close(pipe_m2s_fd[0]); /* parent won't read from this pipe */
5014              p->pipe_s2m_fd = 0;
5015              p->pipe_m2s_fd = 0;
5016              p->idle = True;
5017           }
5018           return;
5019       }
5020
5021       if (pid == 0)
5022       {
5023          /* child process */
5024          pid = getpid();
5025          DBGFORK(("%s:  child activity %d, s2m %d, m2s %d\n",
5026                   pname, activity, pipe_s2m_fd[1], pipe_m2s_fd[0]));
5027
5028          /* close unused pipe connections */
5029          close(pipe_s2m_fd[0]);    /* child won't read from this pipe */
5030          if (sticky)
5031             close(pipe_m2s_fd[1]); /* child won't write to this pipe */
5032
5033          /* run main routine for this activity from ActivityTable */
5034          for (;;)
5035          {
5036             rc = (*ActivityTable[activity].main)(pipe_s2m_fd[1],
5037                                                  directory, activity);
5038             if (!sticky || rc != 0)
5039                break;
5040
5041             /* wait for a message in the pipe */
5042             s = PipeReadString(pipe_m2s_fd[0]);
5043             if (s == NULL)
5044                break;
5045
5046             XtFree(directory->path_name);
5047             directory->path_name = s;
5048
5049             DPRINTF2(("StickyActivity:  activity %d, dir %s\n", activity, s));
5050          }
5051
5052          /* close pipes and end this process */
5053          close(pipe_s2m_fd[1]);
5054          if (sticky)
5055             close(pipe_m2s_fd[0]);
5056
5057          DBGFORK(("%s:  completed activity %d, (rc %d)\n",pname, activity, rc));
5058
5059          exit(rc);
5060       }
5061
5062       DBGFORK(("%s:  forked child<%d> for activity %d, s2m %d, m2s %d\n",
5063                   pname, pid, activity, pipe_s2m_fd[0], pipe_m2s_fd[1]));
5064
5065       /* parent process */
5066       if (sticky)
5067          p->child = pid;
5068
5069       /*
5070        * If a directory read or update was started:
5071        * clear the modifile_list, now that the
5072        * background process has it's own copy.
5073        */
5074       if (activity == activity_reading ||
5075           activity == activity_update_all ||
5076           activity == activity_update_some)
5077       {
5078          for (i = 0; i < directory->modified_count; i++)
5079             XtFree(directory->modified_list[i]);
5080          XtFree((char *)directory->modified_list);
5081
5082          directory->modified_count = 0;
5083          directory->modified_list = NULL;
5084       }
5085
5086       /* close unused pipe connections */
5087       close(pipe_s2m_fd[1]);    /* parent won't write to this pipe */
5088       if (sticky)
5089          close(pipe_m2s_fd[0]); /* parent won't read from this pipe */
5090
5091    }
5092
5093    /* set up callback to get the pipe data */
5094    DPRINTF2(("ScheduleActivity:  setting up pipe callback\n"));
5095    pipe_data = (PipeCallbackData *)XtMalloc(sizeof(PipeCallbackData));
5096    pipe_data->directory = directory;
5097    pipe_data->child = pid;
5098    pipe_data->sticky_proc = p;
5099    pipe_data->activity = activity;
5100
5101    XtAppAddInput(XtWidgetToApplicationContext(toplevel),
5102                  pipe_s2m_fd[0], (XtPointer)XtInputReadMask,
5103                  ActivityTable[activity].callback, (XtPointer)pipe_data);
5104 }
5105
5106
5107 /*--------------------------------------------------------------------
5108  *  ScheduleActivity
5109  *    See if any new background work should be started.
5110  *------------------------------------------------------------------*/
5111
5112 static void
5113 ScheduleActivity(
5114    Directory *directory)
5115 {
5116    int i;
5117
5118    /* first try to schedule new activity for this directory */
5119    if (directory != NULL)
5120      ScheduleDirectoryActivity(directory);
5121
5122    /* see if there is anything else we can schedule now */
5123    if (directory == NULL || directory->activity == activity_idle)
5124    {
5125       for (i = 0; i < directory_count; i++)
5126          if (directory_set[i] != directory)
5127             ScheduleDirectoryActivity(directory_set[i]);
5128    }
5129    ScheduleDirectoryActivity(dummy_directory);
5130  }
5131
5132 static void
5133 SelectDesktopFile(
5134 FileMgrData *file_mgr_data)
5135 {
5136     DirectorySet *directory_data;
5137     FileViewData *file_view_data;
5138     int j;
5139
5140     directory_data = file_mgr_data->directory_set[0];
5141
5142     for (j = 0; j < directory_data->file_count; j++)
5143     {
5144         file_view_data = directory_data->file_view_data[j];
5145
5146         if (file_view_data->filtered != True &&
5147             strcmp(file_mgr_data->desktop_file,
5148                    file_view_data->file_data->file_name) == 0)
5149         {
5150             SelectFile (file_mgr_data, file_view_data);
5151             break;
5152         }
5153     }
5154     ActivateSingleSelect(file_mgr_data->file_mgr_rec,
5155                          file_mgr_data->selection_list[0]->file_data->logical_type);
5156     PositionFileView(file_view_data, file_mgr_data);
5157 }