remove ultrix support
[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      if (! is_multibyte)
3616      {
3617        int len = strlen( file_data->file_name );
3618        if( len > NAME_PRECISION )
3619        {
3620          int i;
3621          char name[NAME_PRECISION];
3622          sprintf( name, "%-20.20s%s", file_data->file_name, ELLIPSIS);
3623
3624          sprintf( long_name, "%-28.28s  %s  %9ld  %c%c%c%c%c%c%c%c%c%c  %-9s  %-9s  %s",
3625                   name,
3626                   time_string,
3627                   (long)file_data->stat.st_size,
3628                   permission,
3629                   usr_read_priv, usr_write_priv, usr_exec_priv,
3630                   grp_read_priv, grp_write_priv, grp_exec_priv,
3631                   oth_read_priv, oth_write_priv, oth_exec_priv,
3632                   user_name, group_name,
3633                   link_path );
3634        }
3635        else
3636        {
3637          sprintf( long_name, "%-28.28s  %s  %9ld  %c%c%c%c%c%c%c%c%c%c  %-9s  %-9s  %s",
3638                   file_data->file_name,
3639                   time_string,
3640                   (long)file_data->stat.st_size,
3641                   permission,
3642                   usr_read_priv, usr_write_priv, usr_exec_priv,
3643                   grp_read_priv, grp_write_priv, grp_exec_priv,
3644                   oth_read_priv, oth_write_priv, oth_exec_priv,
3645                   user_name, group_name,
3646                   link_path );
3647        }
3648      } else {
3649        /* MULTIBYTE
3650         *
3651         * sprintf() counts width in bytes (not characters), moreover,
3652         * it fails (returns -1 and produces no output) if input string is not
3653         * a valid multibyte string (at least the glibc version), but we can't fail
3654         * to display a file because it's name has some invalid characters). So it looks
3655         * that instead of using sprintf() we have to format the file name part manually.
3656         */
3657        int len = DtCharCount( file_data->file_name );
3658        int copy_len =  len > NAME_PRECISION ?  NAME_PRECISION - sizeof(ELLIPSIS) + 1 : len;
3659        int byte_len = 0;
3660        int count;
3661
3662        long_name[0]='\0';
3663        /* properly copy copy_len characters of the multibyte string
3664           replacing invalid chars with '?' */
3665        for (count = 0;
3666             (count < copy_len) && *(file_data->file_name + byte_len);
3667             count ++)
3668        {
3669          int chr_bytes = mblen(file_data->file_name + byte_len, MB_CUR_MAX);
3670          if (chr_bytes > 0)
3671          {
3672            strncpy(long_name + byte_len, file_data->file_name + byte_len, chr_bytes);
3673          }
3674          else if (chr_bytes < 0)
3675          { /* invalid char */
3676            chr_bytes = 1;
3677            long_name[byte_len]='?';
3678          }
3679          else
3680          {
3681            /* null-wide character, won't really happen */
3682            break;
3683          }
3684          byte_len+=chr_bytes;
3685        }
3686        if (copy_len < len)
3687        {
3688          /* truncated name, add ellipsis */
3689          strncpy(long_name + byte_len, ELLIPSIS, sizeof(ELLIPSIS) - 1);
3690          byte_len+= sizeof(ELLIPSIS) - 1;
3691        }
3692        else
3693        {
3694          /* full name, pad it with spaces up to the proper length */
3695          for (; count <  NAME_PRECISION ; count++)
3696          {
3697            long_name[byte_len++]=' ';
3698          }
3699        }
3700        sprintf( long_name + byte_len, "  %s  %9ld  %c%c%c%c%c%c%c%c%c%c  %-9s  %-9s  %s",
3701                 time_string,
3702                 (long)file_data->stat.st_size,
3703                 permission,
3704                 usr_read_priv, usr_write_priv, usr_exec_priv,
3705                 grp_read_priv, grp_write_priv, grp_exec_priv,
3706                 oth_read_priv, oth_write_priv, oth_exec_priv,
3707                 user_name, group_name,
3708                 link_path );
3709      } /* is_multibyte */
3710    }
3711
3712    return (long_name);
3713 }
3714
3715
3716
3717 /*--------------------------------------------------------------------
3718  *  DirectoryBusy
3719  *    See if path has a directory view of it or if any sub-directories
3720  *    of it are viewed.   The path parameter is of the for /foo/bar
3721  *------------------------------------------------------------------*/
3722
3723 Boolean
3724 DirectoryBusy(
3725         char *path )
3726 {
3727    FileMgrData * file_mgr_data;
3728    FileViewData  * sub_root;
3729    int i, j, k;
3730    int len = strlen(path);
3731
3732    for (i = 0; i < directory_count; i++)
3733    {
3734       /* check if this directory is equal to 'path' or a subdir of 'path' */
3735       if (directory_set[i]->viewed &&
3736           (strcmp (directory_set[i]->path_name, path) == 0 ||
3737            strncmp (directory_set[i]->path_name, path,len) == 0 &&
3738              directory_set[i]->path_name[len] == '/'
3739            ||
3740            directory_set[i]->tt_path_name != NULL &&
3741            (strcmp (directory_set[i]->tt_path_name, path) == 0 ||
3742             strncmp (directory_set[i]->tt_path_name, path,len) == 0 &&
3743               directory_set[i]->tt_path_name[len] == '/')))
3744       {
3745          /* check the views in the view list */
3746          for (j = 0; j < directory_set[i]->numOfViews; j++)
3747          {
3748             file_mgr_data = directory_set[i]->directoryView[j].file_mgr_data;
3749
3750             /* find the dir in the directory set for this view */
3751             for (k = 0; k < file_mgr_data->directory_count; k++)
3752                if (strcmp(file_mgr_data->directory_set[k]->name,
3753                           directory_set[i]->directory_name) == 0)
3754                {
3755                   break;
3756                }
3757             if (k == file_mgr_data->directory_count)
3758               continue;  /* not found ... something must be wrong! */
3759
3760             /*
3761              * Check if this directory is acutally visible.
3762              * If the directory is in a tree branch that is not currently
3763              * expanded, it is not visible and would not be considered busy.
3764              */
3765
3766             /* the tree root is always considered busy */
3767             if (k == 0)
3768               return True;
3769
3770             /* a subdir is considered busy if it is visible and at least
3771              * partially expanded */
3772             sub_root = file_mgr_data->directory_set[k]->sub_root;
3773             if (sub_root->displayed &&
3774                 (sub_root->ts == tsDirs && sub_root->ndir > 0  ||
3775                  sub_root->ts == tsAll &&
3776                    sub_root->ndir + sub_root->nfile > 0))
3777             {
3778               return True;
3779             }
3780          }
3781       }
3782    }
3783
3784    return (False);
3785 }
3786
3787
3788 /*--------------------------------------------------------------------
3789  *  GetDirectoryLogicalType
3790  *     Get logical type for the iconic path.
3791  *------------------------------------------------------------------*/
3792
3793 char *
3794 GetDirectoryLogicalType(
3795         FileMgrData *file_mgr_data,
3796         char *path)
3797 {
3798    int len;
3799    int n;
3800    Directory *directory;
3801    char *ptr;
3802
3803    /* 'path' must be a prefix of the current directory */
3804    len = strlen(path);
3805    if (strncmp(file_mgr_data->current_directory, path, len) != 0 ||
3806        (len > 1 &&
3807        file_mgr_data->current_directory[len] != '/' &&
3808        file_mgr_data->current_directory[len] != '\0'))
3809    {
3810       DPRINTF(("GetDirectoryLogicalType(%s): len %d, cur_dir %s\n",
3811                 path, len, file_mgr_data->current_directory));
3812       return NULL;
3813    }
3814
3815    /*  Find the directory set entry.  */
3816    directory = FindDirectory(file_mgr_data->host,
3817                              file_mgr_data->current_directory);
3818    if ((directory != NULL) &&
3819        (strcmp(file_mgr_data->current_directory,
3820                directory->directory_name) == 0))
3821    {
3822       /* if we don't have path_logical_types yet, we don't know */
3823       if (directory->path_logical_types == NULL)
3824          return NULL;
3825
3826       /* count the number of components in path */
3827       if (strcmp(path, "/") == 0)
3828          n = 0;
3829       else
3830       {
3831          n = 1;
3832          ptr = path + 1;
3833          while ((ptr = DtStrchr(ptr, '/')) != NULL)
3834          {
3835             ptr = ptr + 1;
3836             if (*ptr == '\0')
3837               break;
3838             else
3839               n++;
3840          }
3841       }
3842
3843       DPRINTF2(("GetDirectoryLogicalType(%s): n %d, type %s\n",
3844                 path, n, directory->path_logical_types[n]));
3845
3846       /* return type form path_logical_types array */
3847       return directory->path_logical_types[n];
3848    }
3849
3850    /* directory not found in directory_set */
3851
3852    return NULL;
3853 }
3854
3855
3856 /*====================================================================
3857  *
3858  * Routines for accessing position information
3859  *
3860  *==================================================================*/
3861
3862 /*--------------------------------------------------------------------
3863  *  GetDirectoryPositionInfo
3864  *     Get cached position info
3865  *------------------------------------------------------------------*/
3866
3867 int
3868 GetDirectoryPositionInfo(
3869         char *host_name,
3870         char *directory_name,
3871         PositionInfo **position_info)
3872 {
3873    Directory *directory;
3874
3875    directory = FindDirectory(host_name, directory_name);
3876    if (directory == NULL)
3877       return -1;
3878
3879    *position_info = directory->position_info;
3880
3881    return directory->position_count;
3882 }
3883
3884
3885 /*--------------------------------------------------------------------
3886  *  WritePosInfoProcess
3887  *    Main routine of the background process that writes the
3888  *    postion information file.
3889  *------------------------------------------------------------------*/
3890
3891 static int
3892 WritePosInfoProcess(
3893         int pipe_fd,
3894         Directory *directory,
3895         ActivityStatus activity)
3896 {
3897    char *fileName;
3898    int position_count = directory->position_count;
3899    PositionInfo *position_info = directory->position_info;
3900    FILE *f;
3901    int i, rc;
3902    Tt_status tt_status;
3903
3904    /* construct the full file name */
3905    fileName = ResolveLocalPathName( directory->host_name,
3906                                     directory->directory_name, positionFileName,
3907                                     home_host_name, &tt_status );
3908    /* Don't have to check for tt_status
3909       directory->host_name is home_host_name and ResolveLocalPathName will
3910       always return a good path
3911    */
3912    DPRINTF(("WritePosInfoProcess: count %d, file %s\n",
3913             position_count, fileName));
3914
3915    /* Remove old files, if no position information for this view */
3916    if (position_count <= 0)
3917       rc = unlink(fileName);
3918    else
3919    {
3920       /* open the file for writing */
3921       f = fopen(fileName, "w");
3922
3923       if (f == NULL)
3924       {
3925          /* Assume read-only directory, if we can't open the file */
3926          rc = 0;
3927       }
3928       else
3929       {
3930          chmod(fileName, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
3931
3932          fprintf(f, "%d\n", position_count);
3933          for (i = 0; i < position_count; i++)
3934          {
3935             fprintf(f, "%s\n%d %d %d\n",
3936                     position_info[i].name,
3937                     position_info[i].x,
3938                     position_info[i].y,
3939                     position_info[i].stacking_order);
3940          }
3941
3942          fclose(f);
3943          rc = 0;
3944       }
3945    }
3946
3947    /* send result back thorugh the pipe */
3948    DPRINTF(("WritePosInfoProcess: done (rc %d)\n", rc));
3949    write(pipe_fd, &rc, sizeof(int));
3950    XtFree( fileName );
3951    return 0;
3952 }
3953
3954
3955 /*--------------------------------------------------------------------
3956  *  WritePosInfoPipeCallback
3957  *    Callback routine that reads the return code sent through the
3958  *    pipe from the WritePosInfoProcess background process.
3959  *------------------------------------------------------------------*/
3960
3961 static void
3962 WritePosInfoPipeCallback(
3963    XtPointer client_data,
3964    int *fd,
3965    XtInputId *id)
3966 {
3967    PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
3968    Directory *directory = pipe_data->directory;
3969    int rc;
3970    int i;
3971
3972    /* get return code from the pipe */
3973    rc = -1;
3974    PipeRead(*fd, &rc, sizeof(int));
3975
3976    /* close the pipe and cancel the callback */
3977    close(*fd);
3978    XtFree( client_data );
3979    XtRemoveInput(*id);
3980
3981    /* verify that the directory still exists */
3982    if (DirectoryGone(directory))
3983    {
3984       ScheduleActivity(NULL);
3985       return;
3986    }
3987
3988    DPRINTF(("WritePosInfoPipeCallback: rc %d\n", rc));
3989
3990    /* reset the busy flag and schedule new work, if any */
3991    directory->busy[activity_writing_posinfo] = False;
3992    directory->activity = activity_idle;
3993    ScheduleActivity(directory);
3994 }
3995
3996
3997 /*--------------------------------------------------------------------
3998  *  SetDirectoryPositionInfo
3999  *     Update cached position info.  This routine schedules a
4000  *     background process that writes the modified information
4001  *     to the position info file.
4002  *------------------------------------------------------------------*/
4003
4004 int
4005 SetDirectoryPositionInfo(
4006         char *host_name,
4007         char *directory_name,
4008         int position_count,
4009         PositionInfo *position_info)
4010 {
4011    Directory *directory;
4012    Boolean unchanged;
4013    int i, j;
4014
4015    /* find the directory */
4016    directory = FindDirectory(host_name, directory_name);
4017    if (directory == NULL)
4018       return -1;
4019
4020    /* check if anything has changed */
4021    if (directory->position_count == position_count)
4022    {
4023       unchanged = True;
4024       for (i = 0; i < position_count && unchanged; i++)
4025       {
4026          for (j = 0; j < position_count; j++)
4027             if (strcmp(position_info[i].name,
4028                        directory->position_info[j].name) == 0)
4029             {
4030                break;
4031             }
4032
4033          if (j == position_count ||
4034              position_info[i].x != directory->position_info[j].x ||
4035              position_info[i].y != directory->position_info[j].y ||
4036              position_info[i].stacking_order !=
4037                                     directory->position_info[j].stacking_order)
4038          {
4039             unchanged = False;
4040          }
4041       }
4042
4043       /* if nothing changed, don't do anything */
4044       if (unchanged)
4045          return 0;
4046    }
4047
4048    /* free old position info  names*/
4049    for (i = 0; i < directory->position_count; i++)
4050       XtFree(directory->position_info[i].name);
4051
4052    /* realloc array, if necessary */
4053    if (directory->position_count != position_count)
4054    {
4055       directory->position_count = position_count;
4056       directory->position_info =
4057             (PositionInfo *) XtRealloc((char *)directory->position_info,
4058                                         position_count * sizeof(PositionInfo));
4059    }
4060
4061    /* replace old position info */
4062    for (i = 0; i < position_count; i++)
4063    {
4064       directory->position_info[i].name = XtNewString(position_info[i].name);
4065       directory->position_info[i].x = position_info[i].x;
4066       directory->position_info[i].y = position_info[i].y;
4067       directory->position_info[i].stacking_order =
4068                                               position_info[i].stacking_order;
4069    }
4070
4071    /* make sure positionFileName is initialized */
4072    if (positionFileName == NULL)
4073       InitializePositionFileName();
4074
4075    /* start background process that writes the position info file */
4076    directory->busy[activity_writing_posinfo] = True;
4077    ScheduleActivity(directory);
4078
4079    return 0;
4080 }
4081
4082
4083 /*====================================================================
4084  *
4085  * Timer functions
4086  *   These function are periodically called to scan all cached
4087  *   directories to see if any have been modified since last read.
4088  *   If any have, a background process is scheduled to re-read
4089  *   the directory.
4090  *
4091  *==================================================================*/
4092
4093 /*--------------------------------------------------------------------
4094  * SkipRefresh:
4095  *   Decide whether to skip an automatic re-read.
4096  *   (We don't do re-reads on directories that aren't currently
4097  *    being viewed and on the trash directory, if trash is currently
4098  *    being emptied.)
4099  *------------------------------------------------------------------*/
4100
4101 static Boolean
4102 SkipRefresh(
4103         Directory *directory)
4104 {
4105    int i;
4106
4107    /* don't refresh while the directory is being modified */
4108    if (directory->modify_begin > 0)
4109       return True;
4110
4111    /* verify that the directory is still being viewed */
4112    if (!directory->viewed)
4113       return True;
4114
4115    for (i = 0; i < directory->numOfViews; i++)
4116       if (directory->directoryView[i].mapped)
4117          break;
4118    if (i == directory->numOfViews)
4119       return True;
4120
4121    /* if trash is being emptied and this is the trash dir, skip it */
4122    if (removingTrash && strcmp(directory->directory_name, trash_dir) == 0)
4123       return True;
4124
4125    return False;
4126 }
4127
4128
4129 /*--------------------------------------------------------------------
4130  * TimerEventProcess
4131  *   Main routine for the background process that checks directory
4132  *   timestamps and the status of links.
4133  *------------------------------------------------------------------*/
4134
4135 static int
4136 TimerEventProcess(
4137         int pipe_fd,
4138         Directory *directory,
4139         ActivityStatus activity)
4140 {
4141    struct stat stat_buf;
4142    long modify_time;
4143    Boolean link_changed;
4144    Boolean was_broken;
4145    FileData *file_data;
4146    char full_name[MAX_PATH];
4147    char *namep;
4148    short pipe_msg;
4149    int prev_link_kind;
4150    int cur_link_kind;
4151    int link_rc;
4152    int rc;
4153
4154    /*
4155     * Do a stat on the directory to get its last-modified time.
4156     * Also check if we  still have read and execute/search permisssion.
4157     *
4158     * Note:
4159     *  It is important to get the timstamp in exactly the same way that the
4160     *  ReadDirectoryProcess does it; otherwise, we might get into a loop,
4161     *  where TimerEventProcess detects that the directory has changed
4162     *  and triggers ReadDirectoryProcess, but ReadDirectoryProcess won't
4163     *  be able to get a new timestamp and update the directory structure,
4164     *  so the next time TimerEventProcess runs it will trigger another
4165     *  ReadDirectoryProcess, and so on ...
4166     */
4167    if (CheckAccess(directory->path_name, R_OK | X_OK) != 0 ||
4168        stat(directory->path_name, &stat_buf) != 0)
4169    {
4170       /* stat or access failed */
4171       rc = errno;
4172       modify_time = 0;
4173    }
4174    else
4175    {
4176       /* stat succeeded and the directory is still readable */
4177       rc = 0;
4178       modify_time = stat_buf.st_mtime;
4179    }
4180
4181    /*
4182     * If requested, also check if any links broken.
4183     *
4184     * Again: it is important that we determine the kind of link
4185     * (valid, recursive, or broken) in exactly the same way that
4186     * ReadDirectoryProcess does it (see comment above)!
4187     */
4188    link_changed = False;
4189    if (rc == 0 && activity == activity_checking_links)
4190    {
4191       strcpy(full_name, directory->path_name);
4192       namep = full_name + strlen(full_name);
4193       if (namep[-1] != '/')
4194         *namep++ = '/';
4195
4196       /* check all links */
4197       for (file_data = directory->file_data;
4198            file_data && !link_changed;
4199            file_data = file_data->next)
4200       {
4201          /* Only worry about links */
4202          if (file_data->link == NULL)
4203             continue;
4204
4205          /* Check if the file is still a symbolic link */
4206          strcpy(namep, file_data->file_name);
4207          link_rc = lstat(full_name, &stat_buf);
4208          if (link_rc != 0 || (stat_buf.st_mode & S_IFMT) != S_IFLNK)
4209          {
4210             /* no longer a link */
4211             link_changed = True;
4212             break;
4213          }
4214
4215          /* Check what kind of link this was the last time we looked:
4216           * a normal link (1), a recursive link (2), or an otherwise
4217           * broken link (3) */
4218          if (strcmp(file_data->logical_type, LT_BROKEN_LINK) == 0)
4219             prev_link_kind = 3;
4220          else if (strcmp(file_data->logical_type, LT_RECURSIVE_LINK) == 0)
4221             prev_link_kind = 2;
4222          else
4223             prev_link_kind = 1;
4224
4225          /* Check what kind of link it is now */
4226          if (_DtFollowLink(full_name) == NULL)
4227             cur_link_kind = 2;  /* recursive link */
4228          else if (stat(full_name, &stat_buf) != 0)
4229             cur_link_kind = 3;  /* broken link */
4230          else
4231             cur_link_kind = 1;  /* a valid link */
4232
4233          /* now we can tell if the link has changed */
4234          if (prev_link_kind != cur_link_kind)
4235            link_changed = True;
4236       }
4237    }
4238
4239    /* send result back through the pipe */
4240    write(pipe_fd, &rc, sizeof(int));
4241    write(pipe_fd, &modify_time, sizeof(long));
4242    write(pipe_fd, &link_changed, sizeof(Boolean));
4243    return 0;
4244 }
4245
4246
4247 /*--------------------------------------------------------------------
4248  * StickyProcIdle:
4249  *   Mark sticky background process as idle.  If there are too
4250  *   may idle sticky procs, cause some of them to exit.
4251  *------------------------------------------------------------------*/
4252
4253 static void
4254 StickyProcIdle(
4255    ActivityStatus activity,
4256    StickyProcDesc *sticky_proc,
4257    int max_procs)
4258 {
4259    StickyProcDesc *p, **lp;
4260    int i, n;
4261
4262    /* mark the process as idle */
4263    sticky_proc->idle = True;
4264
4265    /* if there are too many idle procs, make some of them go away */
4266    n = 0;
4267    lp = &ActivityTable[activity].sticky_procs;
4268    for (p = *lp; p; p = *lp)
4269    {
4270       if (!p->idle)
4271          lp = &p->next;
4272       else if (n < max_procs)
4273       {
4274          n++;
4275          lp = &p->next;
4276       }
4277       else
4278       {
4279          DPRINTF2(("StickyProcIdle: end sticky proc %ld\n", (long)p->child));
4280          PipeWriteString(p->pipe_m2s_fd, NULL);
4281          close(p->pipe_s2m_fd);
4282          close(p->pipe_m2s_fd);
4283          *lp = p->next;
4284          XtFree((char *)p);
4285       }
4286    }
4287 }
4288
4289
4290 /*--------------------------------------------------------------------
4291  * TimerPipeCallback
4292  *   Callback routine that reads information sent through the
4293  *   pipe from the TimerEventProcess background process.
4294  *------------------------------------------------------------------*/
4295
4296 static void
4297 TimerPipeCallback(
4298    XtPointer client_data,
4299    int *fd,
4300    XtInputId *id)
4301 {
4302    PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
4303    Directory *directory = pipe_data->directory;
4304    int rc;
4305    long modify_time;
4306    Boolean link_changed;
4307    int i;
4308
4309    /* get return code from the pipe */
4310    rc = -1;
4311    PipeRead(*fd, &rc, sizeof(int));
4312    PipeRead(*fd, &modify_time, sizeof(long));
4313    PipeRead(*fd, &link_changed, sizeof(Boolean));
4314
4315    /* close the pipe and cancel the callback */
4316    if (pipe_data->sticky_proc)
4317       StickyProcIdle(pipe_data->activity, pipe_data->sticky_proc,
4318                      maxRereadProcsPerTick);
4319    else
4320       close(*fd);
4321    XtFree( client_data );
4322    XtRemoveInput(*id);
4323
4324    /* verify that the directory still exists */
4325    if (DirectoryGone(directory))
4326    {
4327       ScheduleActivity(NULL);
4328       return;
4329    }
4330
4331    DPRINTF2((
4332      "TimerPipeCallback: rc %d (was %d), time %ld (was %ld), link change %d\n",
4333      rc, directory->errnum, modify_time, (long)directory->modify_time, link_changed));
4334
4335    /* reset the busy flag and schedule new work, if any */
4336    directory->busy[directory->activity] = False;
4337    directory->activity = activity_idle;
4338    ScheduleActivity(directory);
4339
4340    /* if directory-read already in progress, nothing more to do here */
4341    if (directory->busy[activity_reading] ||
4342        directory->busy[activity_update_all])
4343       return;
4344
4345    /* skip this directory if it is no longer being viewed */
4346    if (SkipRefresh(directory))
4347       return;
4348
4349    /* if the directory was modified or links changed, re-read it */
4350    if (rc == 0)
4351    {
4352       if (link_changed)
4353       {
4354          DPRINTF(("TimerPipeCallback: %s link changed\n",
4355                   directory->directory_name));
4356          ReadDirectoryFiles(NULL, directory);
4357       }
4358       else if (modify_time != directory->modify_time || directory->errnum != 0)
4359       {
4360          DPRINTF(("TimerPipeCallback: %s modified\n",
4361                   directory->directory_name));
4362          directory->busy[activity_update_all] = True;
4363          ScheduleActivity(directory);
4364       }
4365    }
4366    else
4367    {
4368       if (directory->errnum == 0)
4369       {
4370          directory->errnum = rc;
4371          directory->errmsg_needed = True;
4372          ReadDirectoryFiles(NULL, directory);
4373       }
4374    }
4375 }
4376
4377
4378 /*--------------------------------------------------------------------
4379  * CheckDesktopProcess
4380  *   Main routine for the background process that checks each desktop
4381  *   objects to see if the file that it refers to has disappeared
4382  *   or has changed type.
4383  *------------------------------------------------------------------*/
4384
4385 static int
4386 CheckDesktopProcess(
4387         int pipe_fd,
4388         Directory *directory,
4389         ActivityStatus activity)
4390 {
4391    int i, n;
4392    DesktopRec *desktopWindow;
4393    FileViewData *file_view_data;
4394    char *full_path;
4395    Tt_status tt_status;
4396    struct stat stat_buf;
4397    short pipe_msg;
4398    FileData2 file_data2;
4399    FileData *old_data, *new_data;
4400
4401    for (i = 0; i < desktop_data->numIconsUsed; i++)
4402    {
4403       desktopWindow = desktop_data->desktopWindows[i];
4404       file_view_data = desktopWindow->file_view_data;
4405
4406       full_path = ResolveLocalPathName( desktopWindow->host,
4407                                         desktopWindow->dir_linked_to,
4408                                         desktopWindow->file_name,
4409                                         home_host_name, &tt_status);
4410
4411       /* Check if the file still exists */
4412       errno = 0;
4413       if (lstat(full_path, &stat_buf) < 0)
4414       {
4415          /* the real file no longer exists */
4416          DPRINTF2((
4417            "CheckDesktopProcess: sending PIPEMSG_DESKTOP_REMOVED for %s\n",
4418            full_path));
4419          pipe_msg = PIPEMSG_DESKTOP_REMOVED;
4420          write(pipe_fd, &pipe_msg, sizeof(short));
4421          PipeWriteString(pipe_fd, desktopWindow->host);
4422          PipeWriteString(pipe_fd, desktopWindow->dir_linked_to);
4423          PipeWriteString(pipe_fd, desktopWindow->file_name);
4424       }
4425       else
4426       {
4427          Boolean IsToolBox;
4428          /* See if the type has changed */
4429          old_data = file_view_data->file_data;
4430
4431          if(directory->directoryView && directory->directoryView->file_mgr_data)
4432              IsToolBox = directory->directoryView->file_mgr_data->toolbox;
4433          else
4434              IsToolBox = False;
4435
4436          ReadFileData2(&file_data2, full_path, NULL,IsToolBox);
4437          new_data = FileData2toFileData(&file_data2, &n);
4438
4439          if (new_data->physical_type != old_data->physical_type ||
4440              strcmp(new_data->logical_type, old_data->logical_type) != 0)
4441          {
4442             /* the type has changed */
4443             DPRINTF2((
4444               "CheckDesktopProcess: sending PIPEMSG_DESKTOP_CHANGED for %s\n",
4445               full_path));
4446             DPRINTF2((
4447               "  old type %d %s, new type %d %s\n",
4448               old_data->physical_type, old_data->logical_type,
4449               new_data->physical_type, new_data->logical_type));
4450
4451             pipe_msg = PIPEMSG_DESKTOP_CHANGED;
4452             write(pipe_fd, &pipe_msg, sizeof(short));
4453             PipeWriteString(pipe_fd, desktopWindow->host);
4454             PipeWriteString(pipe_fd, desktopWindow->dir_linked_to);
4455             PipeWriteString(pipe_fd, desktopWindow->file_name);
4456
4457             PipeWriteFileData(pipe_fd, new_data);
4458          }
4459
4460          FreeFileData(new_data, True);
4461       }
4462       XtFree(full_path);
4463       full_path = NULL;
4464    }
4465
4466    /* send a 'done' msg through the pipe */
4467    DPRINTF2(("CheckDesktopProcess: sending DONE\n"));
4468    pipe_msg = PIPEMSG_DONE;
4469    write(pipe_fd, &pipe_msg, sizeof(short));
4470    return 0;
4471 }
4472
4473
4474 /*--------------------------------------------------------------------
4475  * CheckDesktopPipeCallback
4476  *   Callback routine that reads information sent through the
4477  *   pipe from the CheckDesktopProcess background process.
4478  *------------------------------------------------------------------*/
4479
4480 static void
4481 CheckDesktopPipeCallback(
4482    XtPointer client_data,
4483    int *fd,
4484    XtInputId *id)
4485 {
4486    PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
4487    Directory *directory = pipe_data->directory;
4488    short msg;
4489    char *host, *dir_linked_to, *file_name;
4490    FileData *new_data, *old_data;
4491    Boolean found;
4492    DesktopRec *desktopWindow;
4493    int i, n;
4494
4495    /* read the next msg from the pipe */
4496    msg = -1;
4497    n = PipeRead(*fd, &msg, sizeof(short));
4498
4499    if (msg == PIPEMSG_DESKTOP_REMOVED ||
4500        msg == PIPEMSG_DESKTOP_CHANGED)
4501    {
4502       /* get information from pipe */
4503       host = PipeReadString(*fd);
4504       dir_linked_to = PipeReadString(*fd);
4505       file_name = PipeReadString(*fd);
4506       if (msg == PIPEMSG_DESKTOP_CHANGED)
4507          new_data = PipeReadFileData(*fd);
4508       else
4509          new_data = NULL;
4510
4511       DPRINTF2((
4512         "CheckDesktopPipeCallback: msg %d: host %s, dir %s, name %s\n",
4513         msg, host, dir_linked_to, file_name));
4514
4515
4516       /* find the desktop object */
4517       found = False;
4518       for (i = 0; i < desktop_data->numIconsUsed; i++)
4519       {
4520          desktopWindow = desktop_data->desktopWindows[i];
4521
4522          if (strcmp(host, desktopWindow->host) == 0 &&
4523              strcmp(dir_linked_to, desktopWindow->dir_linked_to) == 0 &&
4524              strcmp(file_name, desktopWindow->file_name) == 0)
4525          {
4526             found = True;
4527             break;
4528          }
4529       }
4530
4531       /* remove or update the desktop object, if found */
4532       if (! found)
4533       {
4534         /* nothing to do */
4535       }
4536       else if (msg == PIPEMSG_DESKTOP_REMOVED)
4537       {
4538          /* remove the desktop object */
4539          DesktopObjectRemoved(desktopWindow);
4540       }
4541       else /* msg == PIPEMSG_DESKTOP_CHANGED */
4542       {
4543          /* replace file data */
4544          old_data = desktopWindow->file_view_data->file_data;
4545          FreeFileData(old_data, False);
4546          memcpy(old_data, new_data, sizeof(FileData));
4547          XtFree((char *)new_data);
4548          new_data = NULL;
4549
4550          /* update the desktop object */
4551          DesktopObjectChanged(desktopWindow);
4552       }
4553
4554       /* free storage */
4555       XtFree(host);
4556       XtFree(dir_linked_to);
4557       XtFree(file_name);
4558       if (new_data)
4559         FreeFileData(new_data, True);
4560    }
4561
4562    else if (msg == PIPEMSG_DONE)
4563    {
4564       /* close the pipe and cancel the callback */
4565       close(*fd);
4566       XtFree( client_data );
4567       XtRemoveInput(*id);
4568
4569       /* reset the busy flag and schedule new work, if any */
4570       directory->busy[directory->activity] = False;
4571       directory->activity = activity_idle;
4572       ScheduleActivity(directory);
4573    }
4574 }
4575
4576
4577 /*--------------------------------------------------------------------
4578  *
4579  * CheckDesktop
4580  *   Arrange for a CheckDesktopProcess background process to be
4581  *   started (checks each desktop objects to see if the file that
4582  *   it refers to has disappeared or has changed type).
4583  *
4584  *------------------------------------------------------------------*/
4585
4586 void
4587 CheckDesktop( void )
4588 {
4589   dummy_directory->busy[activity_checking_desktop] = True;
4590   ScheduleActivity(dummy_directory);
4591 }
4592
4593
4594 /*--------------------------------------------------------------------
4595  * TimerEvent
4596  *   This routine is called periodically.  It schedules a
4597  *   TimerEventProcess background process to be started for every
4598  *   directory in the cache.
4599  *------------------------------------------------------------------*/
4600
4601 /* comparison routine for qsort */
4602 static int
4603 CheckListCmp(
4604         int *p1,
4605         int *p2 )
4606 {
4607    return directory_set[*p1]->last_check - directory_set[*p2]->last_check;
4608 }
4609
4610
4611 static void
4612 TimerEvent(
4613         XtPointer client_data,
4614         XtIntervalId *id )
4615 {
4616    static int *check_list = NULL;
4617    static int check_alloc = 0;
4618    int i, j, n;
4619
4620
4621    DPRINTF2(("Directory::TimerEvent\n"));
4622
4623    if (dragActive)
4624    {
4625       /*
4626        * Don't change any directories while a drag is active.
4627        *
4628        * Reason: drag callbacks are called with a pointer to a FileViewData
4629        * structure; if a directory is reread while a drag is active,
4630        * the pointer would become invalid, causing unpredictable behavior.
4631        *
4632        * Schedule the next TimerEvent in 1/2 second, so that check will
4633        * be done soon after the drag is finished.
4634        */
4635       XtAppAddTimeOut (app_context, 500, TimerEvent, NULL);
4636       return;
4637    }
4638
4639    /* update tick count */
4640    tick_count++;
4641
4642    /* determine if we should also check for broken links this time */
4643    if (checkBrokenLink > 0 &&
4644        tick_count >= lastLinkCheckTick + ticksBetweenLinkChecks)
4645    {
4646      /* set link_check_needed flag on all directores */
4647      for (i = 0; i < directory_count; i++)
4648      {
4649         /* skip this directory if no view is mapped */
4650         if (SkipRefresh(directory_set[i]))
4651            continue;
4652
4653         /* if a check is already in progress, don't start another one */
4654         if (directory_set[i]->busy[activity_checking_links])
4655            continue;
4656
4657         /* arrange for background process to be scheduled */
4658         directory_set[i]->link_check_needed = True;
4659      }
4660
4661      lastLinkCheckTick = tick_count;
4662    }
4663
4664    /* make sure check_list array is big enough */
4665    if (directory_count > check_alloc)
4666    {
4667       check_alloc = directory_count + 5;
4668       check_list =
4669          (int *)XtRealloc((char *)check_list, check_alloc*sizeof(int));
4670    }
4671
4672    /* get a list of all directories that need to be checked */
4673    n = 0;
4674    for (i = 0; i < directory_count; i++)
4675    {
4676       /* skip this directory if no view is mapped */
4677       if (SkipRefresh(directory_set[i]))
4678          continue;
4679
4680       /* if a stat is already in progress, don't start another one */
4681       if (directory_set[i]->busy[activity_checking_dir] ||
4682           directory_set[i]->busy[activity_checking_links])
4683          continue;
4684
4685       /* add this directory to the check list */
4686       check_list[n++] = i;
4687    }
4688
4689    /*
4690     * Next we want to schedule a background process to be started
4691     * for each directory in the check_list.  However, the variable
4692     * maxRereadProcsPerTick puts a limit on the number of such
4693     * background processes started per clock tick (i.e., per call
4694     * to this routine).  Hence we sort check_list by last_check
4695     * (records the tick count when a directory was last read or
4696     * checked) and schedule backround processes on those dirs that
4697     * haven't been checked in the longest time.
4698     */
4699    qsort(check_list, n, sizeof(int), (int (*)())CheckListCmp);
4700
4701    /* arrange for background process to be started */
4702    for (j = 0; j < n && j < maxRereadProcsPerTick; j++)
4703    {
4704       i = check_list[j];
4705       if (directory_set[i]->link_check_needed)
4706       {
4707          directory_set[i]->link_check_needed = False;
4708          directory_set[i]->busy[activity_checking_links] = True;
4709       }
4710       else
4711          directory_set[i]->busy[activity_checking_dir] = True;
4712       ScheduleActivity(directory_set[i]);
4713       directory_set[i]->last_check = tick_count;
4714    }
4715
4716    /*  Reset the timeout for the next interval.  */
4717    if (SomeWindowMapped())
4718       XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
4719    else
4720       timer_suspended = True;
4721 }
4722
4723
4724 /*--------------------------------------------------------------------
4725  * TimerEventBrokenLinks
4726  *   This routine is called periodically.  It checks whether any
4727  *   desktop object is broken (i.e., the object it refers to no
4728  *   longer exists.
4729  *------------------------------------------------------------------*/
4730
4731 void
4732 TimerEventBrokenLinks(
4733         XtPointer client_data,
4734         XtIntervalId *id )
4735 {
4736    int i;
4737
4738    DPRINTF2(("Directory::TimerEventBrokenLinks\n"));
4739
4740    if (!dragActive)
4741    {
4742       /* go check the desktop objects */
4743       if (desktop_data->numIconsUsed > 0)
4744          CheckDesktop();
4745    }
4746
4747    /*  Reset the timeout for the next interval.  */
4748    if (desktop_data->numIconsUsed > 0)
4749    {
4750      checkBrokenLinkTimerId = XtAppAddTimeOut( app_context,
4751                                                checkBrokenLink * 1000,
4752                                                TimerEventBrokenLinks,
4753                                                NULL );
4754    }
4755    else
4756    {
4757      checkBrokenLinkTimerId = None;
4758    }
4759 }
4760
4761
4762 /*====================================================================
4763  *
4764  * Background process scheduler
4765  *
4766  *   The routines below schedule background activity, making sure
4767  *   that there aren't too many processes running at the same time.
4768  *
4769  *==================================================================*/
4770
4771 /*--------------------------------------------------------------------
4772  *  ScheduleDirectoryActivity
4773  *    If there is any work to do for a directory, and if there is
4774  *    no backgroud process currently running for that directory,
4775  *    then fork a process to do the work.
4776  *------------------------------------------------------------------*/
4777
4778 static void
4779 ScheduleDirectoryActivity(
4780    Directory *directory)
4781 {
4782    static char *pname = "ScheduleActivity";
4783    ActivityStatus activity;
4784    PipeCallbackData *pipe_data;
4785    Boolean all_views_active;
4786    Boolean this_view_active;
4787    int i, j, k;
4788    int n_active, n_checking;
4789    int save_last_check = 0;
4790    FileMgrData *file_mgr_data;
4791    Boolean sticky;
4792    StickyProcDesc *p;
4793    int pipe_s2m_fd[2] = {-1, -1};  /* for msgs from backgroundnd proc (slave to master) */
4794    int pipe_m2s_fd[2] = {-1, -1};  /* for msgs to backgroundnd proc (master to slave) */
4795    pid_t pid = 0;
4796    char *s;
4797    int rc;
4798
4799    /* If already active, don't start anything new. */
4800    if (directory->activity != activity_idle)
4801       return;
4802
4803    /* Decide what to do next */
4804    for (activity = 0; activity < activity_idle; activity++)
4805       if (directory->busy[activity])
4806          break;
4807
4808    /* If nothing to do, return */
4809    if (activity == activity_idle)
4810       return;
4811
4812    DPRINTF2(("ScheduleActivity: activity %d, busy %c%c%c%c%c%c%c, dir %s\n",
4813              directory->activity,
4814              directory->busy[activity_writing_posinfo]? 'W': '-',
4815              directory->busy[activity_reading]?         'R': '-',
4816              directory->busy[activity_update_all]?      'A': '-',
4817              directory->busy[activity_update_some]?     'U': '-',
4818              directory->busy[activity_checking_links]?  'B': '-',
4819              directory->busy[activity_checking_desktop]? 'D': '-',
4820              directory->busy[activity_checking_dir]?    'C': '-',
4821              directory->directory_name));
4822
4823    /* Don't start more than a certain number of background processed */
4824    n_active = 0;
4825    n_checking = 0;
4826    for (j = 0; j < directory_count; j++)
4827    {
4828       if (directory_set[j]->activity != activity_idle)
4829          n_active++;
4830       if (directory_set[j]->activity == activity_checking_links ||
4831           directory_set[j]->activity == activity_checking_dir)
4832          n_checking++;
4833    }
4834    if (dummy_directory->activity != activity_idle)
4835    {
4836       n_active++;
4837       n_checking++;
4838    }
4839    if (n_active >= maxDirectoryProcesses ||
4840        n_checking >= maxRereadProcesses)
4841    {
4842       DPRINTF2(("ScheduleActivity:  too many processes\n"));
4843       return;
4844    }
4845
4846    /*
4847     * We don't want to start more than one background process per view.
4848     * In tree mode one view may show more than one directory.
4849     * Hence we go through the view list for this directory and for each
4850     * view, we check if the same view appears on the view list of some
4851     * other directory that currently has active background activity.
4852     * If all vies on this directory have other activity, then we won't
4853     * start anything new.
4854     */
4855    if (directory->numOfViews > 0)
4856    {
4857      all_views_active = True;
4858      for (i = 0; i < directory->numOfViews; i++)
4859      {
4860       /* get file_mgr_data for this view */
4861        file_mgr_data = directory->directoryView[i].file_mgr_data;
4862
4863       /* see if the same view appears in the view list of a non-idle dir */
4864        this_view_active = False;
4865        for (j = 0; j < directory_count && !this_view_active; j++)
4866        {
4867          /* we are only interested in directories that are not idle */
4868          if (directory_set[j]->activity == activity_idle)
4869            continue;
4870
4871          /* see if the view appears in the view list */
4872          for (k = 0; k < directory_set[j]->numOfViews; k++)
4873          {
4874            if (directory_set[j]->directoryView[k].file_mgr_data ==
4875                file_mgr_data)
4876            {
4877              this_view_active = True;
4878              break;
4879            }
4880          }
4881        }
4882
4883        if (!this_view_active)
4884        {
4885          all_views_active = False;
4886          break;
4887        }
4888      }
4889
4890      if (all_views_active)
4891      {
4892        DPRINTF2(("ScheduleActivity:  all views busy\n"));
4893        return;
4894      }
4895    }
4896
4897    /* now we are ready to start the next activity */
4898    directory->activity = activity;
4899    if (activity == activity_reading ||
4900        activity == activity_update_all ||
4901        activity == activity_checking_dir ||
4902        activity == activity_checking_links)
4903    {
4904       save_last_check = directory->last_check;
4905       directory->last_check = tick_count;
4906    }
4907
4908    /*
4909     * Special optimization for periodic background processes
4910     * (currently only used for activity_checking_dir):
4911     * Since this is done frequently, we don't want to fork new process each
4912     * time.  Hence, instead of exiting when it's done, the background process
4913     * is "sticky", i.e., it will stay around waiting for a message on stdin,
4914     * so it can be re-used the next time around.  A linked list of sticky
4915     * procs that are currently active is maintained in the ActivityTable.
4916     */
4917    sticky = ActivityTable[activity].sticky;
4918    if (sticky)
4919    {
4920       /* see if we can find an idle sticky proc that can do the work */
4921       for (p = ActivityTable[activity].sticky_procs; p; p = p->next)
4922          if (p->idle)
4923             break;
4924    }
4925    else
4926       p = NULL;
4927
4928    if (p)
4929    {
4930       /* We found an idle sticky proc that can be used */
4931       DPRINTF2(("ScheduleActivity:  use sticky proc %ld\n", (long)p->child));
4932
4933       /* Send the directory name to the sticky proc */
4934
4935       if (PipeWriteString(p->pipe_m2s_fd, directory->path_name) < 0) {
4936         StickyProcDesc *d;
4937
4938         /* the pipe is broken, remove the old proc then start a new one */
4939         for (d = ActivityTable[activity].sticky_procs; d && p; d = d->next) {
4940           if (d == p)
4941           {
4942             /* the proc listed 1st is dead, remove it */
4943             ActivityTable[activity].sticky_procs = p->next;
4944             XtFree((void *)p);
4945             p = NULL;
4946           }
4947           else if (d->next == p)
4948           {
4949             /* the process "p" is dead, remove it */
4950             d->next = p->next;
4951             XtFree((void *)p);
4952             p = NULL;
4953           }
4954         }
4955       }
4956       else
4957       {
4958         pipe_s2m_fd[0] = p->pipe_s2m_fd;
4959         pid = p->child;
4960         p->idle = False;
4961       }
4962    }
4963
4964
4965    if (!p)
4966    {
4967       /* Need to fork a new background process */
4968
4969       /* create a pipe for reading data from the background proc */
4970       pipe(pipe_s2m_fd);
4971
4972       /* creating a new sticky proc? */
4973       if (sticky)
4974       {
4975          /* also need a pipe for sending msgs to the sticky proc */
4976          pipe(pipe_m2s_fd);
4977
4978          /* add entry to the list of sticky procs */
4979          p = (StickyProcDesc *) XtMalloc(sizeof(StickyProcDesc));
4980          p->next = ActivityTable[activity].sticky_procs;
4981          ActivityTable[activity].sticky_procs = p;
4982
4983          p->pipe_s2m_fd = pipe_s2m_fd[0];
4984          p->pipe_m2s_fd = pipe_m2s_fd[1];
4985          p->idle = False;
4986       }
4987
4988       /* fork a background process */
4989       pid = fork();
4990
4991       if (pid == -1)
4992       {
4993           DBGFORK(("%s:  fork failed for activity %d: %s\n",
4994                     pname, activity, strerror(errno)));
4995
4996           fprintf(stderr,
4997                 "%s:  fork failed, ppid %d, pid %d, activity %d: error %d=%s\n",
4998                 pname, getppid(), getpid(), activity, errno, strerror(errno));
4999
5000           directory->activity = activity_idle;
5001           directory->last_check = save_last_check;
5002
5003           /* close unused pipe connections */
5004           close(pipe_s2m_fd[0]);    /* child won't read from this pipe */
5005           close(pipe_s2m_fd[1]);    /* parent won't write to this pipe */
5006           if (sticky)
5007           {
5008              close(pipe_m2s_fd[1]); /* child won't write to this pipe */
5009              close(pipe_m2s_fd[0]); /* parent won't read from this pipe */
5010              p->pipe_s2m_fd = 0;
5011              p->pipe_m2s_fd = 0;
5012              p->idle = True;
5013           }
5014           return;
5015       }
5016
5017       if (pid == 0)
5018       {
5019          /* child process */
5020          pid = getpid();
5021          DBGFORK(("%s:  child activity %d, s2m %d, m2s %d\n",
5022                   pname, activity, pipe_s2m_fd[1], pipe_m2s_fd[0]));
5023
5024          /* close unused pipe connections */
5025          close(pipe_s2m_fd[0]);    /* child won't read from this pipe */
5026          if (sticky)
5027             close(pipe_m2s_fd[1]); /* child won't write to this pipe */
5028
5029          /* run main routine for this activity from ActivityTable */
5030          for (;;)
5031          {
5032             rc = (*ActivityTable[activity].main)(pipe_s2m_fd[1],
5033                                                  directory, activity);
5034             if (!sticky || rc != 0)
5035                break;
5036
5037             /* wait for a message in the pipe */
5038             s = PipeReadString(pipe_m2s_fd[0]);
5039             if (s == NULL)
5040                break;
5041
5042             XtFree(directory->path_name);
5043             directory->path_name = s;
5044
5045             DPRINTF2(("StickyActivity:  activity %d, dir %s\n", activity, s));
5046          }
5047
5048          /* close pipes and end this process */
5049          close(pipe_s2m_fd[1]);
5050          if (sticky)
5051             close(pipe_m2s_fd[0]);
5052
5053          DBGFORK(("%s:  completed activity %d, (rc %d)\n",pname, activity, rc));
5054
5055          exit(rc);
5056       }
5057
5058       DBGFORK(("%s:  forked child<%d> for activity %d, s2m %d, m2s %d\n",
5059                   pname, pid, activity, pipe_s2m_fd[0], pipe_m2s_fd[1]));
5060
5061       /* parent process */
5062       if (sticky)
5063          p->child = pid;
5064
5065       /*
5066        * If a directory read or update was started:
5067        * clear the modifile_list, now that the
5068        * background process has it's own copy.
5069        */
5070       if (activity == activity_reading ||
5071           activity == activity_update_all ||
5072           activity == activity_update_some)
5073       {
5074          for (i = 0; i < directory->modified_count; i++)
5075             XtFree(directory->modified_list[i]);
5076          XtFree((char *)directory->modified_list);
5077
5078          directory->modified_count = 0;
5079          directory->modified_list = NULL;
5080       }
5081
5082       /* close unused pipe connections */
5083       close(pipe_s2m_fd[1]);    /* parent won't write to this pipe */
5084       if (sticky)
5085          close(pipe_m2s_fd[0]); /* parent won't read from this pipe */
5086
5087    }
5088
5089    /* set up callback to get the pipe data */
5090    DPRINTF2(("ScheduleActivity:  setting up pipe callback\n"));
5091    pipe_data = (PipeCallbackData *)XtMalloc(sizeof(PipeCallbackData));
5092    pipe_data->directory = directory;
5093    pipe_data->child = pid;
5094    pipe_data->sticky_proc = p;
5095    pipe_data->activity = activity;
5096
5097    XtAppAddInput(XtWidgetToApplicationContext(toplevel),
5098                  pipe_s2m_fd[0], (XtPointer)XtInputReadMask,
5099                  ActivityTable[activity].callback, (XtPointer)pipe_data);
5100 }
5101
5102
5103 /*--------------------------------------------------------------------
5104  *  ScheduleActivity
5105  *    See if any new background work should be started.
5106  *------------------------------------------------------------------*/
5107
5108 static void
5109 ScheduleActivity(
5110    Directory *directory)
5111 {
5112    int i;
5113
5114    /* first try to schedule new activity for this directory */
5115    if (directory != NULL)
5116      ScheduleDirectoryActivity(directory);
5117
5118    /* see if there is anything else we can schedule now */
5119    if (directory == NULL || directory->activity == activity_idle)
5120    {
5121       for (i = 0; i < directory_count; i++)
5122          if (directory_set[i] != directory)
5123             ScheduleDirectoryActivity(directory_set[i]);
5124    }
5125    ScheduleDirectoryActivity(dummy_directory);
5126  }
5127
5128 static void
5129 SelectDesktopFile(
5130 FileMgrData *file_mgr_data)
5131 {
5132     DirectorySet *directory_data;
5133     FileViewData *file_view_data;
5134     int j;
5135
5136     directory_data = file_mgr_data->directory_set[0];
5137
5138     for (j = 0; j < directory_data->file_count; j++)
5139     {
5140         file_view_data = directory_data->file_view_data[j];
5141
5142         if (file_view_data->filtered != True &&
5143             strcmp(file_mgr_data->desktop_file,
5144                    file_view_data->file_data->file_name) == 0)
5145         {
5146             SelectFile (file_mgr_data, file_view_data);
5147             break;
5148         }
5149     }
5150     ActivateSingleSelect(file_mgr_data->file_mgr_rec,
5151                          file_mgr_data->selection_list[0]->file_data->logical_type);
5152     PositionFileView(file_view_data, file_mgr_data);
5153 }