dtwm: basic multihead(xinerama only) 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 librararies 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)) > 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)) > 0)
1073      {
1074        link_path[link_len] = NILL;
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    return 0;
2020 }
2021
2022
2023 /*--------------------------------------------------------------------
2024  *  UpdateSomeProcess
2025  *    Main routine of the background process that updates a selected
2026  *    list of directory entries.
2027  *------------------------------------------------------------------*/
2028
2029 static int
2030 UpdateSomeProcess(
2031         int pipe_fd,
2032         Directory *directory,
2033         ActivityStatus activity)
2034 {
2035    char *host_name = directory->host_name;
2036    char *directory_name = directory->directory_name;
2037    char *full_directory_name;
2038    struct stat stat_buf;
2039    long modify_time;
2040    FileData *file_data;
2041    FileData2 file_data2;
2042    short pipe_msg;
2043    int i;
2044    int rc;
2045    Boolean IsToolBox;
2046
2047    DPRINTF(("UpdateSomeProcess(%d, \"%s\", \"%s\")\n",
2048             pipe_fd, host_name, directory_name));
2049
2050
2051    /* get the full name of the current directory */
2052    {
2053      Tt_status tt_status;
2054
2055      full_directory_name = ResolveLocalPathName( host_name,
2056                                                  directory_name,
2057                                                  NULL,
2058                                                  home_host_name,
2059                                                  &tt_status );
2060      if( TT_OK != tt_status )
2061      {
2062        pipe_msg = PIPEMSG_ERROR;
2063        rc = -1;
2064        modify_time = 0;
2065        DPRINTF(("UpdateSomeProcess: sending errno %d (stat failed)\n", rc));
2066        write(pipe_fd, &pipe_msg, sizeof(short));
2067        write(pipe_fd, &rc, sizeof(int));
2068        write(pipe_fd, &modify_time, sizeof(long));
2069        return 1;
2070      }
2071    }
2072    (void) DtEliminateDots (full_directory_name);
2073
2074    /* stat the directory to get the timestamp */
2075    if (stat(full_directory_name, &stat_buf) < 0 ||
2076        ! (stat_buf.st_mode & S_IXUSR) )
2077    {
2078       /* send an error code back through the pipe */
2079       pipe_msg = PIPEMSG_ERROR;
2080       rc = errno;
2081       modify_time = 0;
2082       DPRINTF(("UpdateSomeProcess: sending errno %d (stat failed)\n", rc));
2083       write(pipe_fd, &pipe_msg, sizeof(short));
2084       write(pipe_fd, &rc, sizeof(int));
2085       write(pipe_fd, &modify_time, sizeof(long));
2086       XtFree( full_directory_name );
2087       return 1;
2088    }
2089    modify_time = stat_buf.st_mtime;
2090
2091    /*  Loop through the list of modified files  */
2092    for (i = 0; i < directory->modified_count; i++)
2093    {
2094       /* get the info */
2095
2096       if(directory->directoryView && directory->directoryView->file_mgr_data)
2097         IsToolBox = directory->directoryView->file_mgr_data->toolbox;
2098       else
2099         IsToolBox = False;
2100
2101       ReadFileData2(&file_data2, full_directory_name,
2102                                directory->modified_list[i],IsToolBox);
2103
2104       /* now send the file data through the pipe */
2105       pipe_msg = PIPEMSG_FILEDATA2;
2106       write(pipe_fd, &pipe_msg, sizeof(short));
2107       write(pipe_fd, &file_data2, sizeof(FileData2));
2108    }
2109
2110    XtFree(full_directory_name);
2111
2112    /* send a 'done' msg through the pipe */
2113    DPRINTF(("UpdateSomeProcess: sending DONE\n"));
2114    pipe_msg = PIPEMSG_DONE;
2115    write(pipe_fd, &pipe_msg, sizeof(short));
2116    write(pipe_fd, &modify_time, sizeof(long));
2117    return 0;
2118 }
2119
2120
2121 /*--------------------------------------------------------------------
2122  *  ReaddirPipeCallback
2123  *      Callback routine that reads directory entry information sent
2124  *      through the pipe from the background process.
2125  *------------------------------------------------------------------*/
2126
2127 static void
2128 ReaddirPipeCallback(
2129    XtPointer client_data,
2130    int *fd,
2131    XtInputId *id)
2132 {
2133    static int whined_fd = 0;
2134    PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
2135    Directory *directory = pipe_data->directory;
2136    FileMgrData *file_mgr_data;
2137    FileMgrRec *file_mgr_rec;
2138    ActivityStatus activity;
2139    Boolean done;
2140    short msg;
2141    Boolean update_due;
2142    FileData *new_data, **new_nextp;
2143    FileData *old_data, **old_nextp;
2144    char *ptr;
2145    char *err_msg;
2146    int i, n;
2147    int rc;
2148    long modify_time;
2149    char dirname[MAX_PATH];
2150    short file_data_count;
2151
2152
2153    /* verify that the directory still exists */
2154    if (DirectoryGone(directory))
2155    {
2156       /*
2157        * The directory is no longer present:
2158        * close the pipe and kill the reader.
2159        */
2160       close(*fd);
2161       XtRemoveInput(*id);
2162       kill(pipe_data->child, SIGKILL);
2163       XtFree( client_data );
2164       ScheduleActivity(NULL);
2165       return;
2166    }
2167
2168    /* read the next msg from the pipe */
2169    msg = -1;
2170    n = PipeRead(*fd, &msg, sizeof(short));
2171    activity = directory->activity;
2172    done = False;
2173
2174    switch (msg)
2175    {
2176       case PIPEMSG_PATH_LOGICAL_TYPES:
2177          /* get the number of path components */
2178          PipeRead(*fd, &n, sizeof(int));
2179
2180          /* free logical types */
2181          for (i = 0; i < directory->path_count; i++)
2182            XtFree(directory->path_logical_types[i]);
2183
2184          /* allocate array of the right size */
2185          if (directory->path_count != n)
2186          {
2187             directory->path_count = n;
2188             directory->path_logical_types = (char **)
2189               XtRealloc((char *)directory->path_logical_types,
2190                         n*sizeof(char *));
2191          }
2192
2193          /* get new logical types */
2194          for (i = 0; i < directory->path_count; i++)
2195            directory->path_logical_types[i] = PipeReadString(*fd);
2196
2197          /* get the tt_path */
2198          XtFree(directory->tt_path_name);
2199          directory->tt_path_name = PipeReadString(*fd);
2200
2201          /* update all views */
2202          for (i = 0; i < directory->numOfViews; i++)
2203          {
2204             file_mgr_data = directory->directoryView[i].file_mgr_data;
2205             file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2206             UpdateHeaders(file_mgr_rec, file_mgr_data, True);
2207             XmUpdateDisplay(file_mgr_rec->file_window);
2208          }
2209          XSync(XtDisplay(toplevel), False);
2210          break;
2211
2212           case PIPEMSG_FILEDATA:
2213           case PIPEMSG_FILEDATA2:
2214           case PIPEMSG_FILEDATA3:
2215
2216          if (msg == PIPEMSG_FILEDATA)
2217          {
2218            new_data = PipeReadFileData(*fd);
2219          }
2220          else if (msg == PIPEMSG_FILEDATA2)
2221          {
2222            FileData2 file_data2;
2223            int n;
2224
2225            n = PipeRead(*fd, &file_data2, sizeof(FileData2));
2226            if (n < sizeof(FileData2)) {
2227              perror("PipeRead");
2228              fprintf(stderr, "PipeReadFileData2: n = %d, expected %ld\n",
2229                      n, (long)sizeof(FileData2));
2230            }
2231
2232            new_data = FileData2toFileData(&file_data2, &n);
2233            }
2234
2235          if (msg == PIPEMSG_FILEDATA3)
2236          {
2237            int file_data_length;
2238            int n;
2239            char file_data_buffer[FILEDATABUF * sizeof(FileData2)];
2240            char *file_data_buf_ptr;
2241
2242
2243            n = PipeRead(*fd, &file_data_count, sizeof(short));
2244            n = PipeRead(*fd, &file_data_length, sizeof(int));
2245
2246            if (file_data_count & 0x8000)
2247            {
2248              file_data_count &= 0x7fff;
2249              update_due = True;
2250            }
2251            else
2252              update_due = False;
2253
2254            for (new_nextp = &directory->new_data;
2255                 *new_nextp;
2256                 new_nextp = &(*new_nextp)->next)
2257              ;
2258
2259            n = PipeRead(*fd, file_data_buffer, file_data_length);
2260            file_data_buf_ptr = file_data_buffer;
2261
2262            for (i = 0; i < file_data_count; i++)
2263            {
2264              /* get next FileData out of buffer */
2265              new_data =
2266                FileData2toFileData((FileData2 *)file_data_buf_ptr, &n);
2267              file_data_buf_ptr += n;
2268
2269              /* append new_data to end of list */
2270              *new_nextp = new_data;
2271              new_data->next = NULL;
2272              new_nextp = &new_data->next;
2273            }
2274          }
2275          else
2276          {
2277            /* append new_data to end of list */
2278            file_data_count = 1;
2279            update_due = False;
2280            for (new_nextp = &directory->new_data;
2281                 *new_nextp;
2282                 new_nextp = &(*new_nextp)->next)
2283              ;
2284            *new_nextp = new_data;
2285            new_data->next = NULL;
2286          }
2287
2288          if (activity == activity_reading)
2289          {
2290            /* update file counts in all views */
2291            for (i = 0; i < directory->numOfViews; i++)
2292            {
2293              file_mgr_data = directory->directoryView[i].file_mgr_data;
2294              file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2295              file_mgr_data->busy_detail += file_data_count;
2296              if (update_due &&
2297                  (file_mgr_data->busy_status == initiating_readdir ||
2298                   file_mgr_data->busy_status == busy_readdir) &&
2299                  file_mgr_data->busy_detail > 2)
2300              {
2301                if (file_mgr_data->show_status_line)
2302                {
2303                  char buf[256];
2304                  XmString label_string;
2305                  Arg args[2];
2306
2307                  GetStatusMsg(file_mgr_data, buf);
2308                  label_string =
2309                    XmStringCreateLocalized(buf);
2310                  XtSetArg (args[0], XmNlabelString, label_string);
2311                  XtSetValues(file_mgr_rec->status_line, args, 1);
2312                  XmStringFree(label_string);
2313                }
2314                else if (file_mgr_data->show_iconic_path)
2315                {
2316                  DtUpdateIconicPath(file_mgr_rec, file_mgr_data, False);
2317                }
2318                else if (file_mgr_data->show_current_dir)
2319                {
2320                  DrawCurrentDirectory(file_mgr_rec->current_directory,
2321                                       file_mgr_rec, file_mgr_data);
2322                }
2323              }
2324            }
2325          }
2326          break;
2327
2328       case PIPEMSG_POSITION_INFO:
2329          /* free old position info names */
2330          for (i = 0; i < directory->position_count; i++)
2331             XtFree(directory->position_info[i].name);
2332
2333          /* get number of positions and realloc array, if necessary */
2334          PipeRead(*fd, &n, sizeof(int));
2335          if (directory->position_count != n)
2336          {
2337             directory->position_count = n;
2338             directory->position_info = (PositionInfo *) XtRealloc(
2339                      (char *)directory->position_info, n*sizeof(PositionInfo));
2340          }
2341
2342          /* read new position info */
2343          for (i = 0; i < n; i++)
2344             PipeReadPositionInfo(*fd, &directory->position_info[i]);
2345          break;
2346
2347       case PIPEMSG_DONE:
2348          PipeRead(*fd, &modify_time, sizeof(long));
2349          directory->errnum = 0;
2350          directory->errmsg_needed = False;
2351          done = True;
2352          break;
2353
2354       case PIPEMSG_ERROR:
2355          PipeRead(*fd, &rc, sizeof(int));
2356          PipeRead(*fd, &modify_time, sizeof(long));
2357          if (rc != directory->errnum)
2358          {
2359             directory->errnum = rc;
2360             directory->errmsg_needed = True;
2361          }
2362          done = True;
2363          break;
2364
2365       default:
2366          if (whined_fd != *fd)
2367          {
2368              whined_fd = *fd;
2369              fprintf(stderr,
2370                "ReaddirPipeCallback: badmsg=%d, ppid=%d pid=%d fd=%d activ'y=%d\n",
2371                msg, getppid(), getpid(), *fd, activity);
2372          }
2373          directory->errnum = -1;
2374          directory->errmsg_needed = False;
2375          done = True;
2376    }
2377
2378    /* check if we are done */
2379    if (done)
2380    {
2381 #ifdef DT_PERFORMANCE
2382    /* Aloke Gupta: As suggested by Dana Dao */
2383       _DtPerfChkpntMsgSend("Done  Read Directory");
2384 #endif
2385       DPRINTF(("ReaddirPipeCallback: done, errno %d, time %ld\n",
2386                directory->errnum, modify_time));
2387
2388       /* close the pipe and cancel the callback */
2389       close(*fd);
2390       XtRemoveInput(*id);
2391
2392       /*
2393        * @@@ what if a drag is active ???
2394        */
2395
2396       /*
2397        * For files in the old list that still exist in the new
2398        * one we need to re-use the old FileData structures.
2399        * Reason: the code in GetFileData relies on this to
2400        * preserve the position_info and selection list.
2401        * The following loops through the new list of files
2402        * and replaces entries that also exist in the old list.
2403        */
2404       for (new_nextp = &directory->new_data;
2405            (new_data = *new_nextp) != NULL;
2406            new_nextp = &new_data->next)
2407       {
2408          for (old_nextp = &directory->file_data;
2409               (old_data = *old_nextp) != NULL;
2410               old_nextp = &old_data->next)
2411          {
2412            if( strcmp(old_data->file_name, new_data->file_name) == 0 )
2413             {
2414                *old_nextp = old_data->next;
2415
2416                FreeFileData(old_data, False);
2417                memcpy(old_data, new_data, sizeof(FileData));
2418
2419                XtFree((char *)new_data);
2420                *new_nextp = new_data = old_data;
2421
2422                break;
2423             }
2424          }
2425       }
2426
2427       /*
2428        * If this was a complete re-read, we free all FileData still left
2429        * in the old list.  Otherwise, if this was just a partial update,
2430        * we append the old data that's still left to the end of the
2431        * new list.
2432        */
2433       if (activity == activity_reading)
2434       {
2435          /* This was a complete re-read: free all old data still left. */
2436          while (directory->file_data)
2437          {
2438             old_data = directory->file_data;
2439             directory->file_data = old_data->next;
2440             FreeFileData(old_data, True);
2441          }
2442
2443          /* replace the old list by the new list */
2444          directory->file_data = directory->new_data;
2445          directory->new_data = NULL;
2446       }
2447       else
2448       {
2449          FileData * tmp_ptr = NULL;
2450
2451          /* remove any directory entries that no longer exist
2452             in the new list.
2453          */
2454          new_nextp = &directory->new_data;
2455          while ((new_data = *new_nextp) != NULL)
2456          {
2457             if (new_data->errnum == ENOENT)
2458             {
2459                *new_nextp = new_data->next;
2460                FreeFileData(new_data, True);
2461             }
2462             else
2463             {
2464                tmp_ptr = *new_nextp;
2465                new_nextp = &new_data->next;
2466             }
2467          }
2468
2469          /* remove any directory entries that no longer exist
2470             in the old list.
2471          */
2472          old_nextp = &directory->file_data;
2473          while ((old_data = *old_nextp) != NULL)
2474          {
2475             if (old_data->errnum == ENOENT)
2476             {
2477                *old_nextp = old_data->next;
2478                FreeFileData(old_data, True);
2479             }
2480             else
2481                old_nextp = &old_data->next;
2482          }
2483
2484          /* Append the old list to the end of the new list
2485             Replace the old list pointer with the new list pointer
2486          */
2487          if( tmp_ptr != NULL )
2488          {
2489             tmp_ptr->next = directory->file_data;
2490             directory->file_data = directory->new_data;
2491             directory->new_data = NULL;
2492          }
2493       }
2494
2495       /* update the file count */
2496       directory->file_count = 0;
2497       for (new_data = directory->file_data; new_data; new_data = new_data->next)
2498          directory->file_count++;
2499
2500       /* update directory timestamp */
2501       if (activity == activity_reading ||
2502           activity == activity_update_all ||
2503           directory->was_up_to_date)
2504       {
2505          if (modify_time != 0)
2506             directory->modify_time = modify_time;
2507       }
2508
2509       /* flush the icon cache */  /* @@@ Why? What does this do? @@@ */
2510       strcpy (dirname, directory->path_name);
2511       DtEliminateDots(dirname);
2512       /* We will not attempt to flush the icon cache until this */
2513       /* function has been fixed.                               */
2514
2515       _DtFlushIconFileCache(dirname);
2516
2517       /* reset busy flags */
2518       directory->busy[activity] = False;
2519       directory->activity = activity_idle;
2520       directory->was_up_to_date = True;
2521       directory->link_check_needed = False;
2522
2523
2524       /*
2525        * Fill dir_data field with information on the directory itself.
2526        * This data will be read when querying this view's top directory,
2527        * if the parent directory isn't already cached (tree mode)
2528        */
2529       for (new_data = directory->file_data;
2530            new_data != NULL;
2531            new_data = new_data->next)
2532         if (strcmp(new_data->file_name, ".") == 0)
2533         {
2534
2535             /*
2536              * Found current directory information, now we make
2537              * dir_data info from "." info
2538              */
2539
2540             /* If we already have allocated space for dir_data free it */
2541             if ( directory->dir_data != NULL )
2542               FreeFileData(directory->dir_data, True);
2543
2544             directory->dir_data = (FileData *)XtMalloc(sizeof(FileData));
2545
2546             memcpy(directory->dir_data, new_data, sizeof(FileData));
2547
2548             /*
2549              * Doctor up some of the information fields so that this doesn't
2550              * seem to be a "." entry
2551              */
2552             directory->dir_data->next = NULL;
2553             directory->dir_data->file_name =
2554                                  XtNewString(DName(directory->directory_name));
2555             directory->dir_data->action_name = NULL;
2556             if (directory->path_count > 0)
2557             {
2558               directory->dir_data->logical_type = XtNewString(
2559                      directory->path_logical_types[directory->path_count - 1]);
2560             }
2561             else
2562               directory->dir_data->logical_type = NULL;
2563             directory->dir_data->link = NULL;
2564             directory->dir_data->final_link = NULL;
2565             directory->dir_data->is_subdir = True;
2566
2567             break;
2568         }
2569
2570       /* cause all views on this directory to be redrawn */
2571       for (i = 0; i < directory->numOfViews; i++)
2572       {
2573          file_mgr_data = directory->directoryView[i].file_mgr_data;
2574          file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2575          FileMgrRedisplayFiles(file_mgr_rec, file_mgr_data, False);
2576          if(file_mgr_data->desktop_file)
2577          {
2578              SelectDesktopFile(file_mgr_data);
2579              XtFree(file_mgr_data->desktop_file);
2580              file_mgr_data->desktop_file = NULL;
2581          }
2582       }
2583       XtFree(client_data);
2584
2585       /* schedule the next background activity */
2586       ScheduleActivity(directory);
2587    }
2588 }
2589
2590
2591 /*--------------------------------------------------------------------
2592  *  ReadDirectoryFiles
2593  *    This routine is called to read a directory if the directory
2594  *    wasn't found in the cached, or if the directory has to be
2595  *    re-read because it changed.  This routine schedules a background
2596  *    process to be started that will do the actual work.
2597  *------------------------------------------------------------------*/
2598
2599 static void
2600 ReadDirectoryFiles(
2601         Widget w,
2602         Directory *directory)
2603 {
2604    FileMgrData *file_mgr_data;
2605    FileMgrRec *file_mgr_rec;
2606    int i;
2607
2608 #ifdef DT_PERFORMANCE
2609    /* Aloke Gupta */
2610       _DtPerfChkpntMsgSend("Begin Read Directory");
2611 #endif
2612
2613    /* make sure positionFileName is initialized */
2614    if (positionFileName == NULL)
2615       InitializePositionFileName();
2616
2617    /* mark the directory busy reading */
2618    directory->busy[activity_reading] = True;
2619
2620    /* arrange for background process to be started */
2621    ScheduleActivity(directory);
2622
2623    /* make sure all views on this directory are marked busy */
2624    for (i = 0; i < directory->numOfViews; i++)
2625    {
2626       file_mgr_data = directory->directoryView[i].file_mgr_data;
2627       if (file_mgr_data->busy_status == not_busy)
2628       {
2629          file_mgr_data->busy_status = busy_readdir;
2630          file_mgr_data->busy_detail = 0;
2631          file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2632          FileMgrRedisplayFiles(file_mgr_rec, file_mgr_data, False);
2633       }
2634       file_mgr_data->busy_status = busy_readdir;
2635    }
2636    return;
2637 }
2638
2639
2640 /*--------------------------------------------------------------------
2641  *  ReadDirectory
2642  *    Given a directory name, see if the directory is already cached.
2643  *    If so, return the file data list, otherwise, read the directory.
2644  *------------------------------------------------------------------*/
2645
2646 static Boolean
2647 ReadDirectory(
2648         Widget w,
2649         char *host_name,
2650         char *directory_name,
2651         FileData **file_data,
2652         int *file_count,
2653         FileMgrData *file_mgr_data)
2654 {
2655    Directory *directory;
2656    int i;
2657    char *err_msg;
2658
2659    /* initialize return values */
2660    if (file_data != NULL)
2661    {
2662       *file_count = 0;
2663       *file_data = NULL;
2664    }
2665
2666    /* see if the directory is already in the cache */
2667    directory = FindDirectory(host_name, directory_name);
2668
2669    if ((directory != NULL) &&
2670        (strcmp(directory_name, directory->directory_name) == 0))
2671    {
2672       /* The directory is already in the cache. */
2673       directory->viewed = True;
2674
2675       /* Look for the view in the view list */
2676       for (i = 0; i < directory->numOfViews; i++)
2677          if (directory->directoryView[i].file_mgr_data == file_mgr_data)
2678             break;
2679
2680       /* If view not found, add to the view list */
2681       if (i == directory->numOfViews)
2682       {
2683          directory->directoryView = (DirectoryView *)
2684                                  XtRealloc ((char *) directory->directoryView,
2685                                             sizeof(DirectoryView) * (i + 1));
2686          directory->numOfViews++;
2687          directory->directoryView[i].file_mgr_data = file_mgr_data;
2688       }
2689
2690       /* set mapped flag for the view */
2691       directory->directoryView[i].mapped = file_mgr_data->mapped;
2692
2693       /* check if we need to popup an error message */
2694       if (directory->errmsg_needed &&
2695           !directory->busy[activity_reading] &&
2696           w != NULL)
2697       {
2698          err_msg = XtNewString(GetSharedMessage(CANNOT_READ_DIRECTORY_ERROR));
2699          FileOperationError (w, err_msg, directory_name);
2700          XtFree(err_msg);
2701          directory->errmsg_needed = False;
2702       }
2703
2704       DPRINTF2(("ReadDirectory(\"%s\", \"%s\") returns cached\n",
2705                 host_name, directory_name));
2706    }
2707
2708    else
2709    {
2710       Tt_status tt_status;
2711
2712       /* The directory is not yet in the cache. */
2713
2714       /*  Expand the directory set array, if necessary.  */
2715       if (directory_count == directory_set_size)
2716       {
2717          directory_set_size += 10;
2718          directory_set = (Directory **) XtRealloc((char *)directory_set,
2719                                     sizeof(Directory **) * directory_set_size);
2720       }
2721
2722
2723       /*  Create and initialize a new directory entry  */
2724       directory_set[directory_count] = directory =
2725                                   (Directory *) XtMalloc (sizeof (Directory));
2726       directory_count++;
2727
2728       directory->host_name = XtNewString (host_name);
2729       directory->directory_name = XtNewString (directory_name);
2730       directory->path_name = ResolveLocalPathName (host_name,
2731                                                    directory_name,
2732                                                    NULL,
2733                                                    home_host_name,
2734                                                    &tt_status );
2735       if (directory->path_name == NULL)
2736       {
2737          directory->path_name = (char *) XtMalloc(sizeof(char));
2738          directory->path_name[0]='\0';
2739       }
2740       directory->tt_path_name = NULL;
2741       directory->viewed = True;
2742       directory->file_count = 0;
2743       directory->numOfViews = 1;
2744       directory->errnum = 0;
2745       directory->errmsg_needed = False;
2746       directory->last_check = 0;
2747       directory->link_check_needed = False;
2748       directory->file_count = 0;
2749       directory->file_data = NULL;
2750       directory->new_data = NULL;
2751       directory->dir_data = NULL;
2752       directory->path_count = 0;
2753       directory->path_logical_types = NULL;
2754       directory->position_count = 0;
2755       directory->position_info = NULL;
2756       directory->modify_begin = 0;
2757       directory->modified_count = 0;
2758       directory->was_up_to_date = True;
2759       directory->modified_list = NULL;
2760       directory->activity = activity_idle;
2761       for (i = 0; i < activity_idle; i++)
2762         directory->busy[i] = False;
2763
2764       directory->directoryView = (DirectoryView *)
2765                                        XtMalloc (sizeof(DirectoryView));
2766       directory->directoryView[0].file_mgr_data = file_mgr_data;
2767       directory->directoryView[0].mapped = file_mgr_data->mapped;
2768
2769       /*  Open the directory for reading and read the files.  */
2770       ReadDirectoryFiles (w, directory);
2771    }
2772
2773    /* Restart refresh timer, if necessary */
2774    if (file_mgr_data->mapped && timer_suspended)
2775    {
2776       XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
2777       timer_suspended = False;
2778    }
2779
2780    /* return the file data */
2781    if (file_data != NULL && !directory->busy[activity_reading])
2782    {
2783       *file_count = directory->file_count;
2784       *file_data = directory->file_data;
2785    }
2786
2787    return directory->busy[activity_reading];
2788 }
2789
2790
2791 /*--------------------------------------------------------------------
2792  *  _ReadDir
2793  *    Internal routine that recursively read a directory plus
2794  *    subdirectories down to a depth given by read_level.
2795  *------------------------------------------------------------------*/
2796
2797 static int
2798 _ReadDir(
2799   Widget w,
2800   FileMgrData *file_mgr_data,
2801   char *host_name,
2802   char *directory_name,
2803   FileViewData *dp,         /* directory info */
2804   int level,                /* tree level of this directory */
2805   int read_level,           /* deepest level to be read */
2806   char **branch_list)       /* list of tree branches to expand */
2807 /*
2808  * Recursively read a directory plus subdirectories down to a depth
2809  * given by read_level.
2810  */
2811 {
2812   char subdir_name[MAX_PATH];
2813   FileData *fp, *file_data;
2814   FileViewData **lp, *ip;
2815   int i, j, n, rc;
2816   TreeShow ts;
2817   Boolean busy_reading;
2818
2819   DPRINTF2(("_ReadDir(\"%s\", \"%s\"): level %d, read_level %d\n",
2820             host_name, directory_name, level, read_level));
2821
2822   /* initialize list of descendents and counts */
2823   if (dp)
2824   {
2825     dp->desc = NULL;
2826     dp->ndir = dp->nfile = 0;
2827     lp = &dp->desc;
2828   } else
2829     ip = NULL;
2830
2831   /* Read the directory content */
2832   busy_reading = ReadDirectory(w, host_name, directory_name,
2833                                &file_data, &n, file_mgr_data);
2834   if (busy_reading)
2835   {
2836     file_mgr_data->busy_status = busy_readdir;
2837     return 0;
2838   }
2839
2840   if (n <= 0)
2841     return -1;
2842
2843   level++;
2844
2845   for (i = 0, fp = file_data; i < n && fp; i++, fp = fp->next) {
2846
2847     /* initialize new dir entry */
2848     if (dp)
2849     {
2850        ip = (FileViewData *)XtMalloc(sizeof(FileViewData));
2851        memset(ip, 0, sizeof(FileViewData));
2852        ip->file_data = fp;
2853        ip->parent = dp;
2854        ip->ts = tsNotRead;
2855     }
2856
2857     /* read subdirectory */
2858     if (fp->is_subdir)
2859     {
2860       /* construct sub directory name */
2861       strcpy(subdir_name, directory_name);
2862       if (subdir_name[strlen(subdir_name) - 1] != '/')
2863         strcat(subdir_name, "/");
2864       strcat(subdir_name, fp->file_name);
2865
2866       /* see if we know this entry from branch_list */
2867       if (!QueryBranchList(file_mgr_data, branch_list, subdir_name, &ts))
2868         /* not known: assume we shouldn't read this subdir */
2869         ts = tsNotRead;
2870
2871       if (level < read_level || ts != tsNotRead) {
2872
2873         rc = _ReadDir(w, file_mgr_data, host_name, subdir_name, ip,
2874                       level, read_level, branch_list);
2875         if (ip == NULL)
2876          ;
2877         else if (rc)
2878           ip->ts = tsError;
2879         else if (ts >= tsReading)
2880           ip->ts = ts;
2881         else if (level >= file_mgr_data->tree_show_level)
2882           ip->ts = tsNone;
2883         else if (file_mgr_data->tree_files == TREE_FILES_ALWAYS)
2884           ip->ts = tsAll;
2885         else
2886           ip->ts = tsDirs;
2887       }
2888     }
2889
2890     /* add new entry to linked list */
2891     if (dp)
2892     {
2893       *lp = ip;
2894       lp = &ip->next;
2895     }
2896   }
2897
2898   return 0;
2899 }
2900
2901
2902 /*--------------------------------------------------------------------
2903  *  ReadDir
2904  *    This is the main external entry point for reading directories.
2905  *------------------------------------------------------------------*/
2906
2907 int
2908 ReadDir(
2909   Widget w,
2910   FileMgrData *file_mgr_data,
2911   char *host_name,
2912   char *directory_name,
2913   FileViewData *dp,         /* directory info */
2914   int level,                /* tree level of this directory */
2915   int read_level,           /* deepest level to be read */
2916   char **branch_list)       /* list of tree branches to expand */
2917 {
2918    /* initially assume we are not busy */
2919    if (file_mgr_data->busy_status == not_busy)
2920       file_mgr_data->busy_detail = 0;
2921
2922    file_mgr_data->busy_status = initiating_readdir;
2923
2924    /* first pass: just check if any directory we need is busy */
2925    _ReadDir(w, file_mgr_data, host_name, directory_name, NULL, level,
2926             read_level, branch_list);
2927
2928    /* if a directory we need is busy, return now */
2929    if (file_mgr_data->busy_status == busy_readdir)
2930       return 0;
2931
2932    /*
2933     * All directories wee need are available.
2934     * Make a second pass for real.
2935     */
2936    file_mgr_data->busy_status = not_busy;
2937    return _ReadDir(w, file_mgr_data, host_name, directory_name, dp, level,
2938                    read_level, branch_list);
2939 }
2940
2941
2942 /*====================================================================
2943  *
2944  *  Routines that update the directory cache
2945  *
2946  *==================================================================*/
2947
2948 /*--------------------------------------------------------------------
2949  *  FileWindowMapUnmap
2950  *    Update mapped flag in view lists.
2951  *------------------------------------------------------------------*/
2952
2953 void
2954 FileWindowMapUnmap(
2955         FileMgrData *file_mgr_data)
2956
2957 {
2958    int i, j;
2959
2960    for (i = 0; i < directory_count; i++)
2961    {
2962       for (j = 0; j < directory_set[i]->numOfViews; j++)
2963       {
2964          if (file_mgr_data == directory_set[i]->directoryView[j].file_mgr_data)
2965          {
2966             directory_set[i]->directoryView[j].mapped = file_mgr_data->mapped;
2967             break;
2968          }
2969       }
2970    }
2971
2972    if (file_mgr_data->mapped && timer_suspended)
2973    {
2974       XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
2975       timer_suspended = False;
2976    }
2977 }
2978
2979
2980 /*--------------------------------------------------------------------
2981  *  RereadDirectory
2982  *    Read a directory already cached and update its contents.
2983  *------------------------------------------------------------------*/
2984
2985 void
2986 RereadDirectory(
2987         Widget w,
2988         char *host_name,
2989         char *directory_name )
2990 {
2991    Directory *directory;
2992
2993    DPRINTF(("RereadDirectory(%s, %s)\n", host_name, directory_name));
2994
2995    /*  Find the directory set entry.  */
2996    directory = FindDirectory(host_name, directory_name);
2997    if (directory != NULL)
2998    {
2999       /* reset errnum to make sure we'll get an error message */
3000       directory->errnum = 0;
3001
3002       /* Read the directory. */
3003       if (!directory->busy[activity_reading])
3004          ReadDirectoryFiles(w, directory);
3005    }
3006 }
3007
3008
3009 /*--------------------------------------------------------------------
3010  *  UpdateDirectory
3011  *    Check if any files were added or deleted in a directory
3012  *    and update the directory contents accordingly.
3013  *------------------------------------------------------------------*/
3014
3015 void
3016 UpdateDirectory(
3017         Widget w,
3018         char *host_name,
3019         char *directory_name )
3020 {
3021    Directory *directory;
3022
3023    DPRINTF(("UpdateDirectory(%s, %s)\n", host_name, directory_name));
3024
3025    /*  Find the directory set entry.  */
3026    directory = FindDirectory(host_name, directory_name);
3027    if (directory != NULL)
3028    {
3029       /* arrange for directory contents to be checked */
3030       if (!directory->busy[activity_update_all])
3031       {
3032          directory->busy[activity_update_all] = True;
3033          ScheduleActivity(directory);
3034       }
3035    }
3036 }
3037
3038
3039 /*====================================================================
3040  *
3041  *  Directory modification routines:
3042  *
3043  *  The following routines are provided to avoid unnecessary
3044  *  re-reads of whole directories.  For example, if the user
3045  *  renames a file, it's only necessary to remove the old file
3046  *  from the directory and add it back under its new name; there
3047  *  is no need to read the whole directory again.  Similarly,
3048  *  when a file is dropped on a directory, it's only necessary
3049  *  to add the one new file to the directory.
3050  *
3051  *  To accomplish this, the routines that rename or copy files
3052  *  make the following calls:
3053  *
3054  *    DirectoryBeginModify():  called before doing the operation
3055  *    DirectoryFileModified(): called once for each affected file
3056  *    DirectoryEndModify():    called when the operation is completed
3057  *
3058  *  The routines remember which files were modified, and when
3059  *  DirectoryEndModify is called, a background process is started,
3060  *  that re-stats and types just those files.
3061  *
3062  *  A complication arises from automatic re-reads triggered by
3063  *  a periodic timer (routine TimerEvent).  Since renaming or
3064  *  copying files changes the timestamp on the directory, the
3065  *  automatic re-read would re-read the whole directory soon after
3066  *  the operation is done, nullifying our efforts to avoid
3067  *  unnecessary re-reads.  Therefore:
3068  *
3069  *    - We don't do any automatic re-reads between calls to
3070  *      DirectoryBeginModify and DirectoryEndModify.
3071  *
3072  *    - If the directory timestamp hadn't changed at the time
3073  *      of the DirectoryBeginModify, then when the directory
3074  *      update triggered by DirectoryEndModify finishes, we
3075  *      set the modify_time in the directory_set to the current
3076  *      timestamp of the directory.  This means that the next
3077  *      automatic re-read won't be triggered unless the directory
3078  *      is modified again after the DirectoryEndModify.
3079  *
3080  *==================================================================*/
3081
3082 /*--------------------------------------------------------------------
3083  *  DirectoryAbortModify
3084  *    Decrement the modify_begin counter.
3085  *------------------------------------------------------------------*/
3086
3087 void
3088 DirectoryAbortModify(
3089         char *host_name,
3090         char *directory_name)
3091 {
3092    Directory *directory;
3093
3094    DPRINTF(("DirectoryAbortModify(%s, %s)\n", host_name, directory_name));
3095
3096    /*  Find the directory set entry.  */
3097    directory = FindDirectory(host_name, directory_name);
3098    if (directory != NULL)
3099    {
3100       directory->modify_begin--;
3101
3102       if (directory->modify_begin == 0)
3103          directory->was_up_to_date = True;
3104
3105       DPRINTF(("   modify_begin %d, up_to_date %d\n",
3106                directory->modify_begin, directory->was_up_to_date));
3107    }
3108 }
3109
3110
3111 /*--------------------------------------------------------------------
3112  *  DirectoryBeginModify
3113  *    Increment the modify_begin counter to suspend automatic
3114  *    re-reads until DirectoryEndModify is called.
3115  *------------------------------------------------------------------*/
3116
3117 void
3118 DirectoryBeginModify(
3119         char *host_name,
3120         char *directory_name)
3121 {
3122    Directory *directory;
3123
3124    DPRINTF(("DirectoryBeginModify(%s, %s)\n", host_name, directory_name));
3125
3126    /*  Find the directory set entry.  */
3127    directory = FindDirectory(host_name, directory_name);
3128    if (directory != NULL)
3129    {
3130       if (directory->modify_begin == 0)
3131          /* until we know better, assume the directory changed */
3132          directory->was_up_to_date = False;
3133
3134       /* increment the modify_begin counter */
3135       directory->modify_begin++;
3136
3137       DPRINTF(("   modify_begin %d, up_to_date %d\n",
3138                directory->modify_begin, directory->was_up_to_date));
3139    }
3140 }
3141
3142
3143 /*--------------------------------------------------------------------
3144  *  DirectoryModifyTime
3145  *    This routine should be called after DirectoryBeginModify and
3146  *    before doing any operation on the directory.  The parameter
3147  *    modify_time should be the current timestamp of the directory.
3148  *    By comparing the value to the modify_time stored in the
3149  *    directory set we decide whether the directory had already
3150  *    changed before the update operation began.
3151  *    Note: the reason for supplying a separate call for this check,
3152  *    instead of doing it inside DirectoryBeginModify(), is that we
3153  *    want to do the stat call that determines the current timestamp
3154  *    of the directory in a background process.  The background
3155  *    process that we start fo do the actual update is a convenient
3156  *    place to do this.
3157  *------------------------------------------------------------------*/
3158
3159 void
3160 DirectoryModifyTime(
3161         char *host_name,
3162         char *directory_name,
3163         long modify_time)
3164 {
3165    Directory *directory;
3166
3167    DPRINTF(("DirectoryModifyTime(%s, %s)\n", host_name, directory_name));
3168
3169 #ifdef SMART_DIR_UPDATE
3170    /*  Find the directory set entry.  */
3171    directory = FindDirectory(host_name, directory_name);
3172
3173    if (directory != NULL)
3174    {
3175       /* mark directory up-to-date if unchanged since last read */
3176       if (modify_time <= directory->modify_time)
3177          directory->was_up_to_date = True;
3178       DPRINTF(("   modify_begin %d, up_to_date %d\n",
3179                directory->modify_begin, directory->was_up_to_date));
3180    }
3181 #endif
3182 }
3183
3184
3185 /*--------------------------------------------------------------------
3186  *  DirectoryFileModified
3187  *    This routine is called when we know that a file in a directory
3188  *    has been modified, added or removed.  The file name is added
3189  *    to the list of modified files.  The next time an update
3190  *    background process is started, it will check all the files
3191  *    on the modfied list and update the corresponding FileData.
3192  *------------------------------------------------------------------*/
3193
3194 void
3195 DirectoryFileModified(
3196         char *host_name,
3197         char *directory_name,
3198         char *file_name)
3199 {
3200    Directory *directory;
3201    int i;
3202
3203    DPRINTF(("DirectoryFileModified(%s, %s, %s)\n",
3204             host_name, directory_name, file_name));
3205
3206    /*  Find the directory set entry.  */
3207    directory = FindDirectory(host_name, directory_name);
3208    if (directory != NULL)
3209    {
3210       /* see if the file is already on the list */
3211       for( i = 0; i < directory->modified_count; ++i )
3212         if( strcmp( directory->modified_list[i], file_name ) == 0 )
3213           return;
3214
3215       /* add the file to the modified_list */
3216       i = directory->modified_count++;
3217       directory->modified_list = (char **)
3218         XtRealloc((char *)directory->modified_list, (i + 1)*sizeof(char *));
3219       directory->modified_list[i] = XtNewString(file_name);
3220    }
3221 }
3222
3223
3224 /*--------------------------------------------------------------------
3225  *  DirectoryEndModify
3226  *    Start an update background process (will check all the files
3227  *    on the modfied list and update the corresponding FileData).
3228  *------------------------------------------------------------------*/
3229
3230 void
3231 DirectoryEndModify(
3232         char *host_name,
3233         char *directory_name)
3234 {
3235    Directory *directory;
3236
3237    DPRINTF(("DirectoryEndModify(%s, %s)\n", host_name, directory_name));
3238
3239    /*  Find the directory set entry.  */
3240    directory = FindDirectory(host_name, directory_name);
3241
3242    /* arrange for an update background process to be scheduled */
3243    if (directory != NULL)
3244    {
3245       directory->modify_begin--;
3246       DPRINTF(("   modify_begin %d, up_to_date %d, modified_count %d\n",
3247                directory->modify_begin,
3248                directory->was_up_to_date,
3249                directory->modified_count));
3250       if (directory->modified_count > 0)
3251       {
3252          Directory *subdir;
3253          char subdir_name[MAX_PATH + 1];
3254          char *p;
3255          int i;
3256
3257          /*
3258           * If any of the modifed files is a subdirectory that we have
3259           * cached, schedule an activity_checking_dir to make sure that
3260           * the subdirectory is still readable.
3261           */
3262          strcpy(subdir_name, directory_name);
3263          p = subdir_name + strlen(subdir_name);
3264          if (p[-1] != '/')
3265            *p++ = '/';
3266
3267          for (i = 0; i < directory->modified_count; i++)
3268          {
3269             strcpy(p, directory->modified_list[i]);
3270             subdir = FindDirectory(host_name, subdir_name);
3271             if (subdir)
3272             {
3273                DPRINTF(("   schedule check for subdir \"%s\"\n",
3274                         directory->modified_list[i]));
3275                subdir->busy[activity_checking_dir] = True;
3276                ScheduleActivity(subdir);
3277             }
3278          }
3279
3280 #ifdef SMART_DIR_UPDATE
3281          /* schedule a partial update of the modfied directory */
3282          if (directory->was_up_to_date)
3283             directory->busy[activity_update_some] = True;
3284          else
3285             directory->busy[activity_update_all] = True;
3286 #else
3287          /* schedule a full update of the modfied directory */
3288          directory->busy[activity_update_all] = True;
3289 #endif
3290          ScheduleActivity(directory);
3291       }
3292    }
3293 }
3294
3295
3296 /*--------------------------------------------------------------------
3297  *  UpdateDirectorySet
3298  *    We call this when we do a database update. It loops through
3299  *    the directory_set list and rereads each directory.
3300  *------------------------------------------------------------------*/
3301
3302 void
3303 UpdateDirectorySet( void )
3304 {
3305    int i;
3306
3307    DPRINTF(("UpdateDirectorySet ...\n"));
3308
3309    for (i = 0; i < directory_count; i++)
3310       if (!directory_set[i]->busy[activity_reading])
3311          ReadDirectoryFiles (NULL, directory_set[i]);
3312 }
3313
3314
3315
3316 /*--------------------------------------------------------------------
3317  *  UpdateCachedDirectories
3318  *    Update view list for all cached directories.
3319  *    Throw out any directories that are no longer being viewed.
3320  *------------------------------------------------------------------*/
3321
3322 void
3323 UpdateCachedDirectories(
3324         View **view_set,
3325         int view_count)
3326 {
3327    DialogData * dialog_data;
3328    FileMgrData * file_mgr_data;
3329    int i, j, k, n;
3330    Directory *directory;
3331
3332    /*
3333     * First step:
3334     *   clear the view list in all directory set entries
3335     */
3336    for (i = 0; i < directory_count; i++)
3337    {
3338       if( !(strcmp(directory_set[i]->directory_name, trash_dir) == 0) )
3339       {
3340          XtFree ((char *) directory_set[i]->directoryView);
3341          directory_set[i]->numOfViews = 0;
3342          directory_set[i]->directoryView = NULL;
3343          directory_set[i]->viewed = False;
3344       }
3345    }
3346
3347
3348    /*
3349     * Second step:
3350     *   reconstruct view lists by adding each directory found in the view
3351     *   set to the view list for the corresponding directory set entry
3352     */
3353    for (j = 0; j < view_count; j++)
3354    {
3355       dialog_data = (DialogData *) view_set[j]->dialog_data;
3356       file_mgr_data = (FileMgrData *) dialog_data->data;
3357
3358       /* loop through all direcories in this view */
3359       for (k = 0; k < file_mgr_data->directory_count; k++)
3360       {
3361          /* find the directory in the directory set */
3362          directory = FindDirectory(view_set[j]->host_name,
3363                                    file_mgr_data->directory_set[k]->name);
3364
3365          /* we expect the directory to be found; if not, something is wrong */
3366          if (directory == NULL)
3367          {
3368             fprintf(stderr, "Warning: %s:%s not found in directory set.\n",
3369                     view_set[j]->host_name,
3370                     file_mgr_data->directory_set[k]->name);
3371             continue;
3372          }
3373
3374          /* add the directory to the view list */
3375          n = directory->numOfViews;
3376          directory->directoryView = (DirectoryView *)
3377                                   XtRealloc ((char *) directory->directoryView,
3378                                              sizeof(DirectoryView) * (n + 1));
3379          directory->directoryView[n].file_mgr_data = file_mgr_data;
3380          directory->directoryView[n].mapped = file_mgr_data->mapped;
3381          directory->numOfViews++;
3382          directory->viewed = True;
3383       }
3384    }
3385
3386
3387    /*
3388     * Third step:
3389     *   remove all directories that have empty view lists
3390     */
3391    i = 0;
3392    while (i < directory_count)
3393    {
3394       if (directory_set[i]->numOfViews > 0 ||
3395           strcmp(directory_set[i]->directory_name, trash_dir) == 0)
3396       {
3397          /* Keep this directory in the directory set. */
3398          i++;
3399       }
3400       else
3401       {
3402          /* Delete the file data and remove from the directory set. */
3403
3404          DPRINTF(("UpdateCachedDirectories: removing %s:%s\n",
3405                   directory_set[i]->host_name,
3406                   directory_set[i]->directory_name));
3407
3408          FreeDirectory(directory_set[i]);
3409
3410          for (k = i; k < directory_count - 1; k++)
3411             directory_set[k] = directory_set[k + 1];
3412
3413          directory_count--;
3414       }
3415    }
3416
3417    /* Restart refresh timer, if necessary */
3418    if (timer_suspended && SomeWindowMapped())
3419    {
3420       XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
3421       timer_suspended = False;
3422    }
3423 }
3424
3425
3426 /*====================================================================
3427  *
3428  * Routines that return directory data
3429  *
3430  *==================================================================*/
3431
3432 /*--------------------------------------------------------------------
3433  * GetLongName
3434  *   Return a string that contains file information similar to "ls -l",
3435  *   including: permissions, owner, modified time, link (if any).
3436  *   Used for "view by attributes"
3437  *
3438  *   Example:
3439  *     -rw-r--r--  dld  staff  108314  Jul 26 15:16:36 1993 Directory.c
3440  *
3441  *------------------------------------------------------------------*/
3442
3443 char *
3444 GetLongName(
3445         FileData *file_data )
3446 {
3447 #ifdef NLS16
3448    struct tm * tms;
3449    char time_string[100];
3450 #else
3451    char * time_string;
3452 #endif /* NLS16 */
3453    char link_path[MAX_PATH + 5];
3454    static gid_t group_id = (gid_t)-1;
3455    static uid_t user_id = (uid_t)-1;
3456    struct group * group_data;
3457    struct passwd * user_data;
3458    static char group_name[20];
3459    static char user_name[20];
3460    char * long_name;
3461    time_t long_modify_time;
3462    char permission;
3463    char usr_read_priv, usr_write_priv, usr_exec_priv;
3464    char grp_read_priv, grp_write_priv, grp_exec_priv;
3465    char oth_read_priv, oth_write_priv, oth_exec_priv;
3466
3467    /*  Generate the long list name.  */
3468    long_name = (char *) XtMalloc(sizeof(char) * (MAX_PATH * 3));
3469    long_name[0]='\0';
3470
3471    /* Initially, assume their is not a soft link */
3472    link_path[0] = '\0';
3473
3474    if (file_data->errnum == 0)
3475    {
3476      if (file_data->stat.st_gid != group_id)
3477      {
3478        group_id = file_data->stat.st_gid;
3479        group_data = getgrgid (file_data->stat.st_gid);
3480        if (group_data)
3481        {
3482          strcpy (group_name, group_data->gr_name);
3483          if (strlen (group_name) == 0)
3484             strcpy (group_name, "root");
3485        }
3486        else
3487          strcpy (group_name, "root");
3488      }
3489
3490      if (file_data->stat.st_uid != user_id)
3491      {
3492        user_id = file_data->stat.st_uid;
3493        user_data = getpwuid (file_data->stat.st_uid);
3494        /* Initially, assume their is not a user name */
3495        user_name[0] = '\0';
3496        if (user_data)
3497          strcpy (user_name, user_data->pw_name);
3498        else
3499          sprintf(user_name,"%ld",(long)user_id);
3500      }
3501    }
3502    else
3503    {
3504       char error_msg[1024];
3505       int msg_len;
3506
3507       /* determine how much space we have for an error message */
3508       long_modify_time = 747616435;
3509                          /* just needed to determine the length of a date */
3510 #ifdef NLS16
3511       tms = localtime(&long_modify_time);
3512       strftime( time_string, 100,
3513                 GetSharedMessage(DIRECTORY_DATE_FORMAT),
3514                 tms);
3515 #else
3516       time_string = ctime ((time_t *)&long_modify_time);
3517       time_string[strlen(time_string)-1] = 0x0;
3518       time_string += 4;
3519 #endif
3520       msg_len = 10 + 3 + 9 + 1 + 9 + 1 + 9 + 1 + strlen(time_string);
3521
3522       /* generate the error message */
3523       strcpy(error_msg, "(");
3524       strncpy(error_msg + 1, strerror(file_data->errnum), msg_len - 2);
3525       error_msg[msg_len - 1] = '\0';
3526       strcat(error_msg, ")");
3527
3528       sprintf( long_name, "%-28.28s  %s  %9d %s",
3529                             file_data->file_name,
3530                             time_string,
3531                             0, error_msg );
3532
3533       return (long_name);
3534    }
3535
3536
3537    /* Build the permission string  */
3538    switch( file_data->stat.st_mode & S_IFMT )
3539    {
3540    case S_IFDIR:
3541      permission = 'd';
3542      break;
3543    case S_IFCHR:
3544      permission = 'c';
3545      break;
3546    case S_IFBLK:
3547      permission = 'b';
3548      break;
3549    case S_IFLNK:
3550      permission = 'l';
3551      break;
3552    default :
3553      permission = OPTION_OFF;
3554      break;
3555    }
3556
3557    if (file_data->stat.st_mode & S_IRUSR) usr_read_priv = READ_PRIV;
3558    else usr_read_priv = OPTION_OFF;
3559
3560    if (file_data->stat.st_mode & S_IWUSR) usr_write_priv = WRITE_PRIV;
3561    else usr_write_priv = OPTION_OFF;
3562
3563    if (file_data->stat.st_mode & S_IXUSR) usr_exec_priv = EXEC_PRIV;
3564    else usr_exec_priv = OPTION_OFF;
3565
3566
3567    if (file_data->stat.st_mode & S_IRGRP) grp_read_priv = READ_PRIV;
3568    else grp_read_priv = OPTION_OFF;
3569
3570    if (file_data->stat.st_mode & S_IWGRP) grp_write_priv = WRITE_PRIV;
3571    else grp_write_priv = OPTION_OFF;
3572
3573    if (file_data->stat.st_mode & S_IXGRP) grp_exec_priv = EXEC_PRIV;
3574    else grp_exec_priv = OPTION_OFF;
3575
3576
3577    if (file_data->stat.st_mode & S_IROTH) oth_read_priv = READ_PRIV;
3578    else oth_read_priv = OPTION_OFF;
3579
3580    if (file_data->stat.st_mode & S_IWOTH) oth_write_priv = WRITE_PRIV;
3581    else oth_write_priv = OPTION_OFF;
3582
3583    if (file_data->stat.st_mode & S_IXOTH) oth_exec_priv = EXEC_PRIV;
3584    else oth_exec_priv = OPTION_OFF;
3585
3586
3587    long_modify_time = file_data->stat.st_mtime;
3588 #ifdef NLS16
3589    tms = localtime(&long_modify_time);
3590    strftime( time_string, 100,
3591              GetSharedMessage(DIRECTORY_DATE_FORMAT),
3592              tms);
3593 #else
3594    time_string = ctime ((time_t *)&long_modify_time);
3595    time_string[strlen(time_string)-1] = 0x0;
3596    time_string += 4;
3597 #endif
3598
3599    /* Fill in the name of where the link goes */
3600    if (file_data->link)
3601    {
3602      strcpy( link_path, " -> " );
3603      strcpy( link_path + 4, file_data->link );
3604    }
3605
3606    {
3607 #define ELLIPSIS " (...) "
3608 #define NAME_PRECISION 28
3609 #ifdef MULTIBYTE
3610      if (! is_multibyte)
3611      {
3612 #endif
3613        int len = strlen( file_data->file_name );
3614        if( len > NAME_PRECISION )
3615        {
3616          int i;
3617          char name[NAME_PRECISION];
3618          sprintf( name, "%-20.20s%s", file_data->file_name, ELLIPSIS);
3619
3620          sprintf( long_name, "%-28.28s  %s  %9ld  %c%c%c%c%c%c%c%c%c%c  %-9s  %-9s  %s",
3621                   name,
3622                   time_string,
3623                   (long)file_data->stat.st_size,
3624                   permission,
3625                   usr_read_priv, usr_write_priv, usr_exec_priv,
3626                   grp_read_priv, grp_write_priv, grp_exec_priv,
3627                   oth_read_priv, oth_write_priv, oth_exec_priv,
3628                   user_name, group_name,
3629                   link_path );
3630        }
3631        else
3632        {
3633          sprintf( long_name, "%-28.28s  %s  %9ld  %c%c%c%c%c%c%c%c%c%c  %-9s  %-9s  %s",
3634                   file_data->file_name,
3635                   time_string,
3636                   (long)file_data->stat.st_size,
3637                   permission,
3638                   usr_read_priv, usr_write_priv, usr_exec_priv,
3639                   grp_read_priv, grp_write_priv, grp_exec_priv,
3640                   oth_read_priv, oth_write_priv, oth_exec_priv,
3641                   user_name, group_name,
3642                   link_path );
3643        }
3644 #ifdef MULTIBYTE
3645      } else {
3646        /* MULTIBYTE
3647         *
3648         * sprintf() counts width in bytes (not characters), moreover,
3649         * it fails (returns -1 and produces no output) if input string is not
3650         * a valid multibyte string (at least the glibc version), but we can't fail
3651         * to display a file because it's name has some invalid characters). So it looks
3652         * that instead of using sprintf() we have to format the file name part manually.
3653         */
3654        int len = DtCharCount( file_data->file_name );
3655        int copy_len =  len > NAME_PRECISION ?  NAME_PRECISION - sizeof(ELLIPSIS) + 1 : len;
3656        int byte_len = 0;
3657        int count;
3658
3659        long_name[0]='\0';
3660        /* properly copy copy_len characters of the multibyte string
3661           replacing invalid chars with '?' */
3662        for (count = 0;
3663             (count < copy_len) && *(file_data->file_name + byte_len);
3664             count ++)
3665        {
3666          int chr_bytes = mblen(file_data->file_name + byte_len, MB_CUR_MAX);
3667          if (chr_bytes > 0)
3668          {
3669            strncpy(long_name + byte_len, file_data->file_name + byte_len, chr_bytes);
3670          }
3671          else if (chr_bytes < 0)
3672          { /* invalid char */
3673            chr_bytes = 1;
3674            long_name[byte_len]='?';
3675          }
3676          else
3677          {
3678            /* null-wide character, won't really happen */
3679            break;
3680          }
3681          byte_len+=chr_bytes;
3682        }
3683        if (copy_len < len)
3684        {
3685          /* truncated name, add ellipsis */
3686          strncpy(long_name + byte_len, ELLIPSIS, sizeof(ELLIPSIS) - 1);
3687          byte_len+= sizeof(ELLIPSIS) - 1;
3688        }
3689        else
3690        {
3691          /* full name, pad it with spaces up to the proper length */
3692          for (; count <  NAME_PRECISION ; count++)
3693          {
3694            long_name[byte_len++]=' ';
3695          }
3696        }
3697        sprintf( long_name + byte_len, "  %s  %9ld  %c%c%c%c%c%c%c%c%c%c  %-9s  %-9s  %s",
3698                 time_string,
3699                 (long)file_data->stat.st_size,
3700                 permission,
3701                 usr_read_priv, usr_write_priv, usr_exec_priv,
3702                 grp_read_priv, grp_write_priv, grp_exec_priv,
3703                 oth_read_priv, oth_write_priv, oth_exec_priv,
3704                 user_name, group_name,
3705                 link_path );
3706      } /* is_multibyte */
3707 #endif  /* MULTIBYTE */
3708    }
3709
3710    return (long_name);
3711 }
3712
3713
3714
3715 /*--------------------------------------------------------------------
3716  *  DirectoryBusy
3717  *    See if path has a directory view of it or if any sub-directories
3718  *    of it are viewed.   The path parameter is of the for /foo/bar
3719  *------------------------------------------------------------------*/
3720
3721 Boolean
3722 DirectoryBusy(
3723         char *path )
3724 {
3725    FileMgrData * file_mgr_data;
3726    FileViewData  * sub_root;
3727    int i, j, k;
3728    int len = strlen(path);
3729
3730    for (i = 0; i < directory_count; i++)
3731    {
3732       /* check if this directory is equal to 'path' or a subdir of 'path' */
3733       if (directory_set[i]->viewed &&
3734           (strcmp (directory_set[i]->path_name, path) == 0 ||
3735            strncmp (directory_set[i]->path_name, path,len) == 0 &&
3736              directory_set[i]->path_name[len] == '/'
3737            ||
3738            directory_set[i]->tt_path_name != NULL &&
3739            (strcmp (directory_set[i]->tt_path_name, path) == 0 ||
3740             strncmp (directory_set[i]->tt_path_name, path,len) == 0 &&
3741               directory_set[i]->tt_path_name[len] == '/')))
3742       {
3743          /* check the views in the view list */
3744          for (j = 0; j < directory_set[i]->numOfViews; j++)
3745          {
3746             file_mgr_data = directory_set[i]->directoryView[j].file_mgr_data;
3747
3748             /* find the dir in the directory set for this view */
3749             for (k = 0; k < file_mgr_data->directory_count; k++)
3750                if (strcmp(file_mgr_data->directory_set[k]->name,
3751                           directory_set[i]->directory_name) == 0)
3752                {
3753                   break;
3754                }
3755             if (k == file_mgr_data->directory_count)
3756               continue;  /* not found ... something must be wrong! */
3757
3758             /*
3759              * Check if this directory is acutally visible.
3760              * If the directory is in a tree branch that is not currently
3761              * expanded, it is not visible and would not be considered busy.
3762              */
3763
3764             /* the tree root is always considered busy */
3765             if (k == 0)
3766               return True;
3767
3768             /* a subdir is considered busy if it is visible and at least
3769              * partially expanded */
3770             sub_root = file_mgr_data->directory_set[k]->sub_root;
3771             if (sub_root->displayed &&
3772                 (sub_root->ts == tsDirs && sub_root->ndir > 0  ||
3773                  sub_root->ts == tsAll &&
3774                    sub_root->ndir + sub_root->nfile > 0))
3775             {
3776               return True;
3777             }
3778          }
3779       }
3780    }
3781
3782    return (False);
3783 }
3784
3785
3786 /*--------------------------------------------------------------------
3787  *  GetDirectoryLogicalType
3788  *     Get logical type for the iconic path.
3789  *------------------------------------------------------------------*/
3790
3791 char *
3792 GetDirectoryLogicalType(
3793         FileMgrData *file_mgr_data,
3794         char *path)
3795 {
3796    int len;
3797    int n;
3798    Directory *directory;
3799    char *ptr;
3800
3801    /* 'path' must be a prefix of the current directory */
3802    len = strlen(path);
3803    if (strncmp(file_mgr_data->current_directory, path, len) != 0 ||
3804        (len > 1 &&
3805        file_mgr_data->current_directory[len] != '/' &&
3806        file_mgr_data->current_directory[len] != '\0'))
3807    {
3808       DPRINTF(("GetDirectoryLogicalType(%s): len %d, cur_dir %s\n",
3809                 path, len, file_mgr_data->current_directory));
3810       return NULL;
3811    }
3812
3813    /*  Find the directory set entry.  */
3814    directory = FindDirectory(file_mgr_data->host,
3815                              file_mgr_data->current_directory);
3816    if ((directory != NULL) &&
3817        (strcmp(file_mgr_data->current_directory,
3818                directory->directory_name) == 0))
3819    {
3820       /* if we don't have path_logical_types yet, we don't know */
3821       if (directory->path_logical_types == NULL)
3822          return NULL;
3823
3824       /* count the number of components in path */
3825       if (strcmp(path, "/") == 0)
3826          n = 0;
3827       else
3828       {
3829          n = 1;
3830          ptr = path + 1;
3831          while ((ptr = DtStrchr(ptr, '/')) != NULL)
3832          {
3833             ptr = ptr + 1;
3834             if (*ptr == '\0')
3835               break;
3836             else
3837               n++;
3838          }
3839       }
3840
3841       DPRINTF2(("GetDirectoryLogicalType(%s): n %d, type %s\n",
3842                 path, n, directory->path_logical_types[n]));
3843
3844       /* return type form path_logical_types array */
3845       return directory->path_logical_types[n];
3846    }
3847
3848    /* directory not found in directory_set */
3849
3850    return NULL;
3851 }
3852
3853
3854 /*====================================================================
3855  *
3856  * Routines for accessing position information
3857  *
3858  *==================================================================*/
3859
3860 /*--------------------------------------------------------------------
3861  *  GetDirectoryPositionInfo
3862  *     Get cached position info
3863  *------------------------------------------------------------------*/
3864
3865 int
3866 GetDirectoryPositionInfo(
3867         char *host_name,
3868         char *directory_name,
3869         PositionInfo **position_info)
3870 {
3871    Directory *directory;
3872
3873    directory = FindDirectory(host_name, directory_name);
3874    if (directory == NULL)
3875       return -1;
3876
3877    *position_info = directory->position_info;
3878
3879    return directory->position_count;
3880 }
3881
3882
3883 /*--------------------------------------------------------------------
3884  *  WritePosInfoProcess
3885  *    Main routine of the background process that writes the
3886  *    postion information file.
3887  *------------------------------------------------------------------*/
3888
3889 static int
3890 WritePosInfoProcess(
3891         int pipe_fd,
3892         Directory *directory,
3893         ActivityStatus activity)
3894 {
3895    char *fileName;
3896    int position_count = directory->position_count;
3897    PositionInfo *position_info = directory->position_info;
3898    FILE *f;
3899    int i, rc;
3900    Tt_status tt_status;
3901
3902    /* construct the full file name */
3903    fileName = ResolveLocalPathName( directory->host_name,
3904                                     directory->directory_name, positionFileName,
3905                                     home_host_name, &tt_status );
3906    /* Don't have to check for tt_status
3907       directory->host_name is home_host_name and ResolveLocalPathName will
3908       always return a good path
3909    */
3910    DPRINTF(("WritePosInfoProcess: count %d, file %s\n",
3911             position_count, fileName));
3912
3913    /* Remove old files, if no position information for this view */
3914    if (position_count <= 0)
3915       rc = unlink(fileName);
3916    else
3917    {
3918       /* open the file for writing */
3919       f = fopen(fileName, "w");
3920
3921       if (f == NULL)
3922       {
3923          /* Assume read-only directory, if we can't open the file */
3924          rc = 0;
3925       }
3926       else
3927       {
3928          chmod(fileName, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
3929
3930          fprintf(f, "%d\n", position_count);
3931          for (i = 0; i < position_count; i++)
3932          {
3933             fprintf(f, "%s\n%d %d %d\n",
3934                     position_info[i].name,
3935                     position_info[i].x,
3936                     position_info[i].y,
3937                     position_info[i].stacking_order);
3938          }
3939
3940          fclose(f);
3941          rc = 0;
3942       }
3943    }
3944
3945    /* send result back thorugh the pipe */
3946    DPRINTF(("WritePosInfoProcess: done (rc %d)\n", rc));
3947    write(pipe_fd, &rc, sizeof(int));
3948    XtFree( fileName );
3949    return 0;
3950 }
3951
3952
3953 /*--------------------------------------------------------------------
3954  *  WritePosInfoPipeCallback
3955  *    Callback routine that reads the return code sent through the
3956  *    pipe from the WritePosInfoProcess background process.
3957  *------------------------------------------------------------------*/
3958
3959 static void
3960 WritePosInfoPipeCallback(
3961    XtPointer client_data,
3962    int *fd,
3963    XtInputId *id)
3964 {
3965    PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
3966    Directory *directory = pipe_data->directory;
3967    int rc;
3968    int i;
3969
3970    /* get return code from the pipe */
3971    rc = -1;
3972    PipeRead(*fd, &rc, sizeof(int));
3973
3974    /* close the pipe and cancel the callback */
3975    close(*fd);
3976    XtFree( client_data );
3977    XtRemoveInput(*id);
3978
3979    /* verify that the directory still exists */
3980    if (DirectoryGone(directory))
3981    {
3982       ScheduleActivity(NULL);
3983       return;
3984    }
3985
3986    DPRINTF(("WritePosInfoPipeCallback: rc %d\n", rc));
3987
3988    /* reset the busy flag and schedule new work, if any */
3989    directory->busy[activity_writing_posinfo] = False;
3990    directory->activity = activity_idle;
3991    ScheduleActivity(directory);
3992 }
3993
3994
3995 /*--------------------------------------------------------------------
3996  *  SetDirectoryPositionInfo
3997  *     Update cached position info.  This routine schedules a
3998  *     background process that writes the modified information
3999  *     to the position info file.
4000  *------------------------------------------------------------------*/
4001
4002 int
4003 SetDirectoryPositionInfo(
4004         char *host_name,
4005         char *directory_name,
4006         int position_count,
4007         PositionInfo *position_info)
4008 {
4009    Directory *directory;
4010    Boolean unchanged;
4011    int i, j;
4012
4013    /* find the directory */
4014    directory = FindDirectory(host_name, directory_name);
4015    if (directory == NULL)
4016       return -1;
4017
4018    /* check if anything has changed */
4019    if (directory->position_count == position_count)
4020    {
4021       unchanged = True;
4022       for (i = 0; i < position_count && unchanged; i++)
4023       {
4024          for (j = 0; j < position_count; j++)
4025             if (strcmp(position_info[i].name,
4026                        directory->position_info[j].name) == 0)
4027             {
4028                break;
4029             }
4030
4031          if (j == position_count ||
4032              position_info[i].x != directory->position_info[j].x ||
4033              position_info[i].y != directory->position_info[j].y ||
4034              position_info[i].stacking_order !=
4035                                     directory->position_info[j].stacking_order)
4036          {
4037             unchanged = False;
4038          }
4039       }
4040
4041       /* if nothing changed, don't do anything */
4042       if (unchanged)
4043          return 0;
4044    }
4045
4046    /* free old position info  names*/
4047    for (i = 0; i < directory->position_count; i++)
4048       XtFree(directory->position_info[i].name);
4049
4050    /* realloc array, if necessary */
4051    if (directory->position_count != position_count)
4052    {
4053       directory->position_count = position_count;
4054       directory->position_info =
4055             (PositionInfo *) XtRealloc((char *)directory->position_info,
4056                                         position_count * sizeof(PositionInfo));
4057    }
4058
4059    /* replace old position info */
4060    for (i = 0; i < position_count; i++)
4061    {
4062       directory->position_info[i].name = XtNewString(position_info[i].name);
4063       directory->position_info[i].x = position_info[i].x;
4064       directory->position_info[i].y = position_info[i].y;
4065       directory->position_info[i].stacking_order =
4066                                               position_info[i].stacking_order;
4067    }
4068
4069    /* make sure positionFileName is initialized */
4070    if (positionFileName == NULL)
4071       InitializePositionFileName();
4072
4073    /* start background process that writes the position info file */
4074    directory->busy[activity_writing_posinfo] = True;
4075    ScheduleActivity(directory);
4076
4077    return 0;
4078 }
4079
4080
4081 /*====================================================================
4082  *
4083  * Timer functions
4084  *   These function are periodically called to scan all cached
4085  *   directories to see if any have been modified since last read.
4086  *   If any have, a background process is scheduled to re-read
4087  *   the directory.
4088  *
4089  *==================================================================*/
4090
4091 /*--------------------------------------------------------------------
4092  * SkipRefresh:
4093  *   Decide whether to skip an automatic re-read.
4094  *   (We don't do re-reads on directories that aren't currently
4095  *    being viewed and on the trash directory, if trash is currently
4096  *    being emptied.)
4097  *------------------------------------------------------------------*/
4098
4099 static Boolean
4100 SkipRefresh(
4101         Directory *directory)
4102 {
4103    int i;
4104
4105    /* don't refresh while the directory is being modified */
4106    if (directory->modify_begin > 0)
4107       return True;
4108
4109    /* verify that the directory is still being viewed */
4110    if (!directory->viewed)
4111       return True;
4112
4113    for (i = 0; i < directory->numOfViews; i++)
4114       if (directory->directoryView[i].mapped)
4115          break;
4116    if (i == directory->numOfViews)
4117       return True;
4118
4119    /* if trash is being emptied and this is the trash dir, skip it */
4120    if (removingTrash && strcmp(directory->directory_name, trash_dir) == 0)
4121       return True;
4122
4123    return False;
4124 }
4125
4126
4127 /*--------------------------------------------------------------------
4128  * TimerEventProcess
4129  *   Main routine for the background process that checks directory
4130  *   timestamps and the status of links.
4131  *------------------------------------------------------------------*/
4132
4133 static int
4134 TimerEventProcess(
4135         int pipe_fd,
4136         Directory *directory,
4137         ActivityStatus activity)
4138 {
4139    struct stat stat_buf;
4140    long modify_time;
4141    Boolean link_changed;
4142    Boolean was_broken;
4143    FileData *file_data;
4144    char full_name[MAX_PATH];
4145    char *namep;
4146    short pipe_msg;
4147    int prev_link_kind;
4148    int cur_link_kind;
4149    int link_rc;
4150    int rc;
4151
4152    /*
4153     * Do a stat on the directory to get its last-modified time.
4154     * Also check if we  still have read and execute/search permisssion.
4155     *
4156     * Note:
4157     *  It is important to get the timstamp in exactly the same way that the
4158     *  ReadDirectoryProcess does it; otherwise, we might get into a loop,
4159     *  where TimerEventProcess detects that the directory has changed
4160     *  and triggers ReadDirectoryProcess, but ReadDirectoryProcess won't
4161     *  be able to get a new timestamp and update the directory structure,
4162     *  so the next time TimerEventProcess runs it will trigger another
4163     *  ReadDirectoryProcess, and so on ...
4164     */
4165    if (CheckAccess(directory->path_name, R_OK | X_OK) != 0 ||
4166        stat(directory->path_name, &stat_buf) != 0)
4167    {
4168       /* stat or access failed */
4169       rc = errno;
4170       modify_time = 0;
4171    }
4172    else
4173    {
4174       /* stat succeeded and the directory is still readable */
4175       rc = 0;
4176       modify_time = stat_buf.st_mtime;
4177    }
4178
4179    /*
4180     * If requested, also check if any links broken.
4181     *
4182     * Again: it is important that we determine the kind of link
4183     * (valid, recursive, or broken) in exactly the same way that
4184     * ReadDirectoryProcess does it (see comment above)!
4185     */
4186    link_changed = False;
4187    if (rc == 0 && activity == activity_checking_links)
4188    {
4189       strcpy(full_name, directory->path_name);
4190       namep = full_name + strlen(full_name);
4191       if (namep[-1] != '/')
4192         *namep++ = '/';
4193
4194       /* check all links */
4195       for (file_data = directory->file_data;
4196            file_data && !link_changed;
4197            file_data = file_data->next)
4198       {
4199          /* Only worry about links */
4200          if (file_data->link == NULL)
4201             continue;
4202
4203          /* Check if the file is still a symbolic link */
4204          strcpy(namep, file_data->file_name);
4205          link_rc = lstat(full_name, &stat_buf);
4206          if (link_rc != 0 || (stat_buf.st_mode & S_IFMT) != S_IFLNK)
4207          {
4208             /* no longer a link */
4209             link_changed = True;
4210             break;
4211          }
4212
4213          /* Check what kind of link this was the last time we looked:
4214           * a normal link (1), a recursive link (2), or an otherwise
4215           * broken link (3) */
4216          if (strcmp(file_data->logical_type, LT_BROKEN_LINK) == 0)
4217             prev_link_kind = 3;
4218          else if (strcmp(file_data->logical_type, LT_RECURSIVE_LINK) == 0)
4219             prev_link_kind = 2;
4220          else
4221             prev_link_kind = 1;
4222
4223          /* Check what kind of link it is now */
4224          if (_DtFollowLink(full_name) == NULL)
4225             cur_link_kind = 2;  /* recursive link */
4226          else if (stat(full_name, &stat_buf) != 0)
4227             cur_link_kind = 3;  /* broken link */
4228          else
4229             cur_link_kind = 1;  /* a valid link */
4230
4231          /* now we can tell if the link has changed */
4232          if (prev_link_kind != cur_link_kind)
4233            link_changed = True;
4234       }
4235    }
4236
4237    /* send result back through the pipe */
4238    write(pipe_fd, &rc, sizeof(int));
4239    write(pipe_fd, &modify_time, sizeof(long));
4240    write(pipe_fd, &link_changed, sizeof(Boolean));
4241    return 0;
4242 }
4243
4244
4245 /*--------------------------------------------------------------------
4246  * StickyProcIdle:
4247  *   Mark sticky background process as idle.  If there are too
4248  *   may idle sticky procs, cause some of them to exit.
4249  *------------------------------------------------------------------*/
4250
4251 static void
4252 StickyProcIdle(
4253    ActivityStatus activity,
4254    StickyProcDesc *sticky_proc,
4255    int max_procs)
4256 {
4257    StickyProcDesc *p, **lp;
4258    int i, n;
4259
4260    /* mark the process as idle */
4261    sticky_proc->idle = True;
4262
4263    /* if there are too many idle procs, make some of them go away */
4264    n = 0;
4265    lp = &ActivityTable[activity].sticky_procs;
4266    for (p = *lp; p; p = *lp)
4267    {
4268       if (!p->idle)
4269          lp = &p->next;
4270       else if (n < max_procs)
4271       {
4272          n++;
4273          lp = &p->next;
4274       }
4275       else
4276       {
4277          DPRINTF2(("StickyProcIdle: end sticky proc %ld\n", (long)p->child));
4278          PipeWriteString(p->pipe_m2s_fd, NULL);
4279          close(p->pipe_s2m_fd);
4280          close(p->pipe_m2s_fd);
4281          *lp = p->next;
4282          XtFree((char *)p);
4283       }
4284    }
4285 }
4286
4287
4288 /*--------------------------------------------------------------------
4289  * TimerPipeCallback
4290  *   Callback routine that reads information sent through the
4291  *   pipe from the TimerEventProcess background process.
4292  *------------------------------------------------------------------*/
4293
4294 static void
4295 TimerPipeCallback(
4296    XtPointer client_data,
4297    int *fd,
4298    XtInputId *id)
4299 {
4300    PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
4301    Directory *directory = pipe_data->directory;
4302    int rc;
4303    long modify_time;
4304    Boolean link_changed;
4305    int i;
4306
4307    /* get return code from the pipe */
4308    rc = -1;
4309    PipeRead(*fd, &rc, sizeof(int));
4310    PipeRead(*fd, &modify_time, sizeof(long));
4311    PipeRead(*fd, &link_changed, sizeof(Boolean));
4312
4313    /* close the pipe and cancel the callback */
4314    if (pipe_data->sticky_proc)
4315       StickyProcIdle(pipe_data->activity, pipe_data->sticky_proc,
4316                      maxRereadProcsPerTick);
4317    else
4318       close(*fd);
4319    XtFree( client_data );
4320    XtRemoveInput(*id);
4321
4322    /* verify that the directory still exists */
4323    if (DirectoryGone(directory))
4324    {
4325       ScheduleActivity(NULL);
4326       return;
4327    }
4328
4329    DPRINTF2((
4330      "TimerPipeCallback: rc %d (was %d), time %ld (was %ld), link change %d\n",
4331      rc, directory->errnum, modify_time, (long)directory->modify_time, link_changed));
4332
4333    /* reset the busy flag and schedule new work, if any */
4334    directory->busy[directory->activity] = False;
4335    directory->activity = activity_idle;
4336    ScheduleActivity(directory);
4337
4338    /* if directory-read already in progress, nothing more to do here */
4339    if (directory->busy[activity_reading] ||
4340        directory->busy[activity_update_all])
4341       return;
4342
4343    /* skip this directory if it is no longer being viewed */
4344    if (SkipRefresh(directory))
4345       return;
4346
4347    /* if the directory was modified or links changed, re-read it */
4348    if (rc == 0)
4349    {
4350       if (link_changed)
4351       {
4352          DPRINTF(("TimerPipeCallback: %s link changed\n",
4353                   directory->directory_name));
4354          ReadDirectoryFiles(NULL, directory);
4355       }
4356       else if (modify_time != directory->modify_time || directory->errnum != 0)
4357       {
4358          DPRINTF(("TimerPipeCallback: %s modified\n",
4359                   directory->directory_name));
4360          directory->busy[activity_update_all] = True;
4361          ScheduleActivity(directory);
4362       }
4363    }
4364    else
4365    {
4366       if (directory->errnum == 0)
4367       {
4368          directory->errnum = rc;
4369          directory->errmsg_needed = True;
4370          ReadDirectoryFiles(NULL, directory);
4371       }
4372    }
4373 }
4374
4375
4376 /*--------------------------------------------------------------------
4377  * CheckDesktopProcess
4378  *   Main routine for the background process that checks each desktop
4379  *   objects to see if the file that it refers to has disappeared
4380  *   or has changed type.
4381  *------------------------------------------------------------------*/
4382
4383 static int
4384 CheckDesktopProcess(
4385         int pipe_fd,
4386         Directory *directory,
4387         ActivityStatus activity)
4388 {
4389    int i, n;
4390    DesktopRec *desktopWindow;
4391    FileViewData *file_view_data;
4392    char *full_path;
4393    Tt_status tt_status;
4394    struct stat stat_buf;
4395    short pipe_msg;
4396    FileData2 file_data2;
4397    FileData *old_data, *new_data;
4398
4399    for (i = 0; i < desktop_data->numIconsUsed; i++)
4400    {
4401       desktopWindow = desktop_data->desktopWindows[i];
4402       file_view_data = desktopWindow->file_view_data;
4403
4404       full_path = ResolveLocalPathName( desktopWindow->host,
4405                                         desktopWindow->dir_linked_to,
4406                                         desktopWindow->file_name,
4407                                         home_host_name, &tt_status);
4408
4409       /* Check if the file still exists */
4410       errno = 0;
4411       if (lstat(full_path, &stat_buf) < 0)
4412       {
4413          /* the real file no longer exists */
4414          DPRINTF2((
4415            "CheckDesktopProcess: sending PIPEMSG_DESKTOP_REMOVED for %s\n",
4416            full_path));
4417          pipe_msg = PIPEMSG_DESKTOP_REMOVED;
4418          write(pipe_fd, &pipe_msg, sizeof(short));
4419          PipeWriteString(pipe_fd, desktopWindow->host);
4420          PipeWriteString(pipe_fd, desktopWindow->dir_linked_to);
4421          PipeWriteString(pipe_fd, desktopWindow->file_name);
4422       }
4423       else
4424       {
4425          Boolean IsToolBox;
4426          /* See if the type has changed */
4427          old_data = file_view_data->file_data;
4428
4429          if(directory->directoryView && directory->directoryView->file_mgr_data)
4430              IsToolBox = directory->directoryView->file_mgr_data->toolbox;
4431          else
4432              IsToolBox = False;
4433
4434          ReadFileData2(&file_data2, full_path, NULL,IsToolBox);
4435          new_data = FileData2toFileData(&file_data2, &n);
4436
4437          if (new_data->physical_type != old_data->physical_type ||
4438              strcmp(new_data->logical_type, old_data->logical_type) != 0)
4439          {
4440             /* the type has changed */
4441             DPRINTF2((
4442               "CheckDesktopProcess: sending PIPEMSG_DESKTOP_CHANGED for %s\n",
4443               full_path));
4444             DPRINTF2((
4445               "  old type %d %s, new type %d %s\n",
4446               old_data->physical_type, old_data->logical_type,
4447               new_data->physical_type, new_data->logical_type));
4448
4449             pipe_msg = PIPEMSG_DESKTOP_CHANGED;
4450             write(pipe_fd, &pipe_msg, sizeof(short));
4451             PipeWriteString(pipe_fd, desktopWindow->host);
4452             PipeWriteString(pipe_fd, desktopWindow->dir_linked_to);
4453             PipeWriteString(pipe_fd, desktopWindow->file_name);
4454
4455             PipeWriteFileData(pipe_fd, new_data);
4456          }
4457
4458          FreeFileData(new_data, True);
4459       }
4460       XtFree(full_path);
4461       full_path = NULL;
4462    }
4463
4464    /* send a 'done' msg through the pipe */
4465    DPRINTF2(("CheckDesktopProcess: sending DONE\n"));
4466    pipe_msg = PIPEMSG_DONE;
4467    write(pipe_fd, &pipe_msg, sizeof(short));
4468    return 0;
4469 }
4470
4471
4472 /*--------------------------------------------------------------------
4473  * CheckDesktopPipeCallback
4474  *   Callback routine that reads information sent through the
4475  *   pipe from the CheckDesktopProcess background process.
4476  *------------------------------------------------------------------*/
4477
4478 static void
4479 CheckDesktopPipeCallback(
4480    XtPointer client_data,
4481    int *fd,
4482    XtInputId *id)
4483 {
4484    PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
4485    Directory *directory = pipe_data->directory;
4486    short msg;
4487    char *host, *dir_linked_to, *file_name;
4488    FileData *new_data, *old_data;
4489    Boolean found;
4490    DesktopRec *desktopWindow;
4491    int i, n;
4492
4493    /* read the next msg from the pipe */
4494    msg = -1;
4495    n = PipeRead(*fd, &msg, sizeof(short));
4496
4497    if (msg == PIPEMSG_DESKTOP_REMOVED ||
4498        msg == PIPEMSG_DESKTOP_CHANGED)
4499    {
4500       /* get information from pipe */
4501       host = PipeReadString(*fd);
4502       dir_linked_to = PipeReadString(*fd);
4503       file_name = PipeReadString(*fd);
4504       if (msg == PIPEMSG_DESKTOP_CHANGED)
4505          new_data = PipeReadFileData(*fd);
4506       else
4507          new_data = NULL;
4508
4509       DPRINTF2((
4510         "CheckDesktopPipeCallback: msg %d: host %s, dir %s, name %s\n",
4511         msg, host, dir_linked_to, file_name));
4512
4513
4514       /* find the desktop object */
4515       found = False;
4516       for (i = 0; i < desktop_data->numIconsUsed; i++)
4517       {
4518          desktopWindow = desktop_data->desktopWindows[i];
4519
4520          if (strcmp(host, desktopWindow->host) == 0 &&
4521              strcmp(dir_linked_to, desktopWindow->dir_linked_to) == 0 &&
4522              strcmp(file_name, desktopWindow->file_name) == 0)
4523          {
4524             found = True;
4525             break;
4526          }
4527       }
4528
4529       /* remove or update the desktop object, if found */
4530       if (! found)
4531       {
4532         /* nothing to do */
4533       }
4534       else if (msg == PIPEMSG_DESKTOP_REMOVED)
4535       {
4536          /* remove the desktop object */
4537          DesktopObjectRemoved(desktopWindow);
4538       }
4539       else /* msg == PIPEMSG_DESKTOP_CHANGED */
4540       {
4541          /* replace file data */
4542          old_data = desktopWindow->file_view_data->file_data;
4543          FreeFileData(old_data, False);
4544          memcpy(old_data, new_data, sizeof(FileData));
4545          XtFree((char *)new_data);
4546          new_data = NULL;
4547
4548          /* update the desktop object */
4549          DesktopObjectChanged(desktopWindow);
4550       }
4551
4552       /* free storage */
4553       XtFree(host);
4554       XtFree(dir_linked_to);
4555       XtFree(file_name);
4556       if (new_data)
4557         FreeFileData(new_data, True);
4558    }
4559
4560    else if (msg == PIPEMSG_DONE)
4561    {
4562       /* close the pipe and cancel the callback */
4563       close(*fd);
4564       XtFree( client_data );
4565       XtRemoveInput(*id);
4566
4567       /* reset the busy flag and schedule new work, if any */
4568       directory->busy[directory->activity] = False;
4569       directory->activity = activity_idle;
4570       ScheduleActivity(directory);
4571    }
4572 }
4573
4574
4575 /*--------------------------------------------------------------------
4576  *
4577  * CheckDesktop
4578  *   Arrange for a CheckDesktopProcess background process to be
4579  *   started (checks each desktop objects to see if the file that
4580  *   it refers to has disappeared or has changed type).
4581  *
4582  *------------------------------------------------------------------*/
4583
4584 void
4585 CheckDesktop( void )
4586 {
4587   dummy_directory->busy[activity_checking_desktop] = True;
4588   ScheduleActivity(dummy_directory);
4589 }
4590
4591
4592 /*--------------------------------------------------------------------
4593  * TimerEvent
4594  *   This routine is called periodically.  It schedules a
4595  *   TimerEventProcess background process to be started for every
4596  *   directory in the cache.
4597  *------------------------------------------------------------------*/
4598
4599 /* comparison routine for qsort */
4600 static int
4601 CheckListCmp(
4602         int *p1,
4603         int *p2 )
4604 {
4605    return directory_set[*p1]->last_check - directory_set[*p2]->last_check;
4606 }
4607
4608
4609 static void
4610 TimerEvent(
4611         XtPointer client_data,
4612         XtIntervalId *id )
4613 {
4614    static int *check_list = NULL;
4615    static int check_alloc = 0;
4616    int i, j, n;
4617
4618
4619    DPRINTF2(("Directory::TimerEvent\n"));
4620
4621    if (dragActive)
4622    {
4623       /*
4624        * Don't change any directories while a drag is active.
4625        *
4626        * Reason: drag callbacks are called with a pointer to a FileViewData
4627        * structure; if a directory is reread while a drag is active,
4628        * the pointer would become invalid, causing unpredictable behavior.
4629        *
4630        * Schedule the next TimerEvent in 1/2 second, so that check will
4631        * be done soon after the drag is finished.
4632        */
4633       XtAppAddTimeOut (app_context, 500, TimerEvent, NULL);
4634       return;
4635    }
4636
4637    /* update tick count */
4638    tick_count++;
4639
4640    /* determine if we should also check for broken links this time */
4641    if (checkBrokenLink > 0 &&
4642        tick_count >= lastLinkCheckTick + ticksBetweenLinkChecks)
4643    {
4644      /* set link_check_needed flag on all directores */
4645      for (i = 0; i < directory_count; i++)
4646      {
4647         /* skip this directory if no view is mapped */
4648         if (SkipRefresh(directory_set[i]))
4649            continue;
4650
4651         /* if a check is already in progress, don't start another one */
4652         if (directory_set[i]->busy[activity_checking_links])
4653            continue;
4654
4655         /* arrange for background process to be scheduled */
4656         directory_set[i]->link_check_needed = True;
4657      }
4658
4659      lastLinkCheckTick = tick_count;
4660    }
4661
4662    /* make sure check_list array is big enough */
4663    if (directory_count > check_alloc)
4664    {
4665       check_alloc = directory_count + 5;
4666       check_list =
4667          (int *)XtRealloc((char *)check_list, check_alloc*sizeof(int));
4668    }
4669
4670    /* get a list of all directories that need to be checked */
4671    n = 0;
4672    for (i = 0; i < directory_count; i++)
4673    {
4674       /* skip this directory if no view is mapped */
4675       if (SkipRefresh(directory_set[i]))
4676          continue;
4677
4678       /* if a stat is already in progress, don't start another one */
4679       if (directory_set[i]->busy[activity_checking_dir] ||
4680           directory_set[i]->busy[activity_checking_links])
4681          continue;
4682
4683       /* add this directory to the check list */
4684       check_list[n++] = i;
4685    }
4686
4687    /*
4688     * Next we want to schedule a background process to be started
4689     * for each directory in the check_list.  However, the variable
4690     * maxRereadProcsPerTick puts a limit on the number of such
4691     * background processes started per clock tick (i.e., per call
4692     * to this routine).  Hence we sort check_list by last_check
4693     * (records the tick count when a directory was last read or
4694     * checked) and schedule backround processes on those dirs that
4695     * haven't been checked in the longest time.
4696     */
4697    qsort(check_list, n, sizeof(int), (int (*)())CheckListCmp);
4698
4699    /* arrange for background process to be started */
4700    for (j = 0; j < n && j < maxRereadProcsPerTick; j++)
4701    {
4702       i = check_list[j];
4703       if (directory_set[i]->link_check_needed)
4704       {
4705          directory_set[i]->link_check_needed = False;
4706          directory_set[i]->busy[activity_checking_links] = True;
4707       }
4708       else
4709          directory_set[i]->busy[activity_checking_dir] = True;
4710       ScheduleActivity(directory_set[i]);
4711       directory_set[i]->last_check = tick_count;
4712    }
4713
4714    /*  Reset the timeout for the next interval.  */
4715    if (SomeWindowMapped())
4716       XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
4717    else
4718       timer_suspended = True;
4719 }
4720
4721
4722 /*--------------------------------------------------------------------
4723  * TimerEventBrokenLinks
4724  *   This routine is called periodically.  It checks whether any
4725  *   desktop object is broken (i.e., the object it refers to no
4726  *   longer exists.
4727  *------------------------------------------------------------------*/
4728
4729 void
4730 TimerEventBrokenLinks(
4731         XtPointer client_data,
4732         XtIntervalId *id )
4733 {
4734    int i;
4735
4736    DPRINTF2(("Directory::TimerEventBrokenLinks\n"));
4737
4738    if (!dragActive)
4739    {
4740       /* go check the desktop objects */
4741       if (desktop_data->numIconsUsed > 0)
4742          CheckDesktop();
4743    }
4744
4745    /*  Reset the timeout for the next interval.  */
4746    if (desktop_data->numIconsUsed > 0)
4747    {
4748      checkBrokenLinkTimerId = XtAppAddTimeOut( app_context,
4749                                                checkBrokenLink * 1000,
4750                                                TimerEventBrokenLinks,
4751                                                NULL );
4752    }
4753    else
4754    {
4755      checkBrokenLinkTimerId = None;
4756    }
4757 }
4758
4759
4760 /*====================================================================
4761  *
4762  * Background process scheduler
4763  *
4764  *   The routines below schedule background activity, making sure
4765  *   that there aren't too many processes running at the same time.
4766  *
4767  *==================================================================*/
4768
4769 /*--------------------------------------------------------------------
4770  *  ScheduleDirectoryActivity
4771  *    If there is any work to do for a directory, and if there is
4772  *    no backgroud process currently running for that directory,
4773  *    then fork a process to do the work.
4774  *------------------------------------------------------------------*/
4775
4776 static void
4777 ScheduleDirectoryActivity(
4778    Directory *directory)
4779 {
4780    static char *pname = "ScheduleActivity";
4781    ActivityStatus activity;
4782    PipeCallbackData *pipe_data;
4783    Boolean all_views_active;
4784    Boolean this_view_active;
4785    int i, j, k;
4786    int n_active, n_checking;
4787    int save_last_check;
4788    FileMgrData *file_mgr_data;
4789    Boolean sticky;
4790    StickyProcDesc *p;
4791    int pipe_s2m_fd[2];  /* for msgs from backgroundnd proc (slave to master) */
4792    int pipe_m2s_fd[2];  /* for msgs to backgroundnd proc (master to slave) */
4793    pid_t pid;
4794    char *s;
4795    int rc;
4796
4797    /* If already active, don't start anything new. */
4798    if (directory->activity != activity_idle)
4799       return;
4800
4801    /* Decide what to do next */
4802    for (activity = 0; activity < activity_idle; activity++)
4803       if (directory->busy[activity])
4804          break;
4805
4806    /* If nothing to do, return */
4807    if (activity == activity_idle)
4808       return;
4809
4810    DPRINTF2(("ScheduleActivity: activity %d, busy %c%c%c%c%c%c%c, dir %s\n",
4811              directory->activity,
4812              directory->busy[activity_writing_posinfo]? 'W': '-',
4813              directory->busy[activity_reading]?         'R': '-',
4814              directory->busy[activity_update_all]?      'A': '-',
4815              directory->busy[activity_update_some]?     'U': '-',
4816              directory->busy[activity_checking_links]?  'B': '-',
4817              directory->busy[activity_checking_desktop]? 'D': '-',
4818              directory->busy[activity_checking_dir]?    'C': '-',
4819              directory->directory_name));
4820
4821    /* Don't start more than a certain number of background processed */
4822    n_active = 0;
4823    n_checking = 0;
4824    for (j = 0; j < directory_count; j++)
4825    {
4826       if (directory_set[j]->activity != activity_idle)
4827          n_active++;
4828       if (directory_set[j]->activity == activity_checking_links ||
4829           directory_set[j]->activity == activity_checking_dir)
4830          n_checking++;
4831    }
4832    if (dummy_directory->activity != activity_idle)
4833    {
4834       n_active++;
4835       n_checking++;
4836    }
4837    if (n_active >= maxDirectoryProcesses ||
4838        n_checking >= maxRereadProcesses)
4839    {
4840       DPRINTF2(("ScheduleActivity:  too many processes\n"));
4841       return;
4842    }
4843
4844    /*
4845     * We don't want to start more than one background process per view.
4846     * In tree mode one view may show more than one directory.
4847     * Hence we go through the view list for this directory and for each
4848     * view, we check if the same view appears on the view list of some
4849     * other directory that currently has active background activity.
4850     * If all vies on this directory have other activity, then we won't
4851     * start anything new.
4852     */
4853    if (directory->numOfViews > 0)
4854    {
4855      all_views_active = True;
4856      for (i = 0; i < directory->numOfViews; i++)
4857      {
4858       /* get file_mgr_data for this view */
4859        file_mgr_data = directory->directoryView[i].file_mgr_data;
4860
4861       /* see if the same view appears in the view list of a non-idle dir */
4862        this_view_active = False;
4863        for (j = 0; j < directory_count && !this_view_active; j++)
4864        {
4865          /* we are only interested in directories that are not idle */
4866          if (directory_set[j]->activity == activity_idle)
4867            continue;
4868
4869          /* see if the view appears in the view list */
4870          for (k = 0; k < directory_set[j]->numOfViews; k++)
4871          {
4872            if (directory_set[j]->directoryView[k].file_mgr_data ==
4873                file_mgr_data)
4874            {
4875              this_view_active = True;
4876              break;
4877            }
4878          }
4879        }
4880
4881        if (!this_view_active)
4882        {
4883          all_views_active = False;
4884          break;
4885        }
4886      }
4887
4888      if (all_views_active)
4889      {
4890        DPRINTF2(("ScheduleActivity:  all views busy\n"));
4891        return;
4892      }
4893    }
4894
4895    /* now we are ready to start the next activity */
4896    directory->activity = activity;
4897    if (activity == activity_reading ||
4898        activity == activity_update_all ||
4899        activity == activity_checking_dir ||
4900        activity == activity_checking_links)
4901    {
4902       save_last_check = directory->last_check;
4903       directory->last_check = tick_count;
4904    }
4905
4906    /*
4907     * Special optimization for periodic background processes
4908     * (currently only used for activity_checking_dir):
4909     * Since this is done frequently, we don't want to fork new process each
4910     * time.  Hence, instead of exiting when it's done, the background process
4911     * is "sticky", i.e., it will stay around waiting for a message on stdin,
4912     * so it can be re-used the next time around.  A linked list of sticky
4913     * procs that are currently active is maintained in the ActivityTable.
4914     */
4915    sticky = ActivityTable[activity].sticky;
4916    if (sticky)
4917    {
4918       /* see if we can find an idle sticky proc that can do the work */
4919       for (p = ActivityTable[activity].sticky_procs; p; p = p->next)
4920          if (p->idle)
4921             break;
4922    }
4923    else
4924       p = NULL;
4925
4926    if (p)
4927    {
4928       /* We found an idle sticky proc that can be used */
4929       DPRINTF2(("ScheduleActivity:  use sticky proc %ld\n", (long)p->child));
4930
4931       /* Send the directory name to the sticky proc */
4932
4933       if (PipeWriteString(p->pipe_m2s_fd, directory->path_name) < 0) {
4934         StickyProcDesc *d;
4935
4936         /* the pipe is broken, remove the old proc then start a new one */
4937         for (d = ActivityTable[activity].sticky_procs; d && p; d = d->next) {
4938           if (d == p)
4939           {
4940             /* the proc listed 1st is dead, remove it */
4941             ActivityTable[activity].sticky_procs = p->next;
4942             XtFree((void *)p);
4943             p = NULL;
4944           }
4945           else if (d->next == p)
4946           {
4947             /* the process "p" is dead, remove it */
4948             d->next = p->next;
4949             XtFree((void *)p);
4950             p = NULL;
4951           }
4952         }
4953       }
4954       else
4955       {
4956         pipe_s2m_fd[0] = p->pipe_s2m_fd;
4957         pid = p->child;
4958         p->idle = False;
4959       }
4960    }
4961
4962
4963    if (!p)
4964    {
4965       /* Need to fork a new background process */
4966
4967       /* create a pipe for reading data from the background proc */
4968       pipe(pipe_s2m_fd);
4969
4970       /* creating a new sticky proc? */
4971       if (sticky)
4972       {
4973          /* also need a pipe for sending msgs to the sticky proc */
4974          pipe(pipe_m2s_fd);
4975
4976          /* add entry to the list of sticky procs */
4977          p = (StickyProcDesc *) XtMalloc(sizeof(StickyProcDesc));
4978          p->next = ActivityTable[activity].sticky_procs;
4979          ActivityTable[activity].sticky_procs = p;
4980
4981          p->pipe_s2m_fd = pipe_s2m_fd[0];
4982          p->pipe_m2s_fd = pipe_m2s_fd[1];
4983          p->idle = False;
4984       }
4985
4986       /* fork a background process */
4987       pid = fork();
4988
4989       if (pid == -1)
4990       {
4991           DBGFORK(("%s:  fork failed for activity %d: %s\n",
4992                     pname, activity, strerror(errno)));
4993
4994           fprintf(stderr,
4995                 "%s:  fork failed, ppid %d, pid %d, activity %d: error %d=%s\n",
4996                 pname, getppid(), getpid(), activity, errno, strerror(errno));
4997
4998           directory->activity = activity_idle;
4999           directory->last_check = save_last_check;
5000
5001           /* close unused pipe connections */
5002           close(pipe_s2m_fd[0]);    /* child won't read from this pipe */
5003           close(pipe_s2m_fd[1]);    /* parent won't write to this pipe */
5004           if (sticky)
5005           {
5006              close(pipe_m2s_fd[1]); /* child won't write to this pipe */
5007              close(pipe_m2s_fd[0]); /* parent won't read from this pipe */
5008              p->pipe_s2m_fd = 0;
5009              p->pipe_m2s_fd = 0;
5010              p->idle = True;
5011           }
5012           return;
5013       }
5014
5015       if (pid == 0)
5016       {
5017          /* child process */
5018          pid = getpid();
5019          DBGFORK(("%s:  child activity %d, s2m %d, m2s %d\n",
5020                   pname, activity, pipe_s2m_fd[1], pipe_m2s_fd[0]));
5021
5022          /* close unused pipe connections */
5023          close(pipe_s2m_fd[0]);    /* child won't read from this pipe */
5024          if (sticky)
5025             close(pipe_m2s_fd[1]); /* child won't write to this pipe */
5026
5027          /* run main routine for this activity from ActivityTable */
5028          for (;;)
5029          {
5030             rc = (*ActivityTable[activity].main)(pipe_s2m_fd[1],
5031                                                  directory, activity);
5032             if (!sticky || rc != 0)
5033                break;
5034
5035             /* wait for a message in the pipe */
5036             s = PipeReadString(pipe_m2s_fd[0]);
5037             if (s == NULL)
5038                break;
5039
5040             XtFree(directory->path_name);
5041             directory->path_name = s;
5042
5043             DPRINTF2(("StickyActivity:  activity %d, dir %s\n", activity, s));
5044          }
5045
5046          /* close pipes and end this process */
5047          close(pipe_s2m_fd[1]);
5048          if (sticky)
5049             close(pipe_m2s_fd[0]);
5050
5051          DBGFORK(("%s:  completed activity %d, (rc %d)\n",pname, activity, rc));
5052
5053          exit(rc);
5054       }
5055
5056       DBGFORK(("%s:  forked child<%d> for activity %d, s2m %d, m2s %d\n",
5057                   pname, pid, activity, pipe_s2m_fd[0], pipe_m2s_fd[1]));
5058
5059       /* parent process */
5060       if (sticky)
5061          p->child = pid;
5062
5063       /*
5064        * If a directory read or update was started:
5065        * clear the modifile_list, now that the
5066        * background process has it's own copy.
5067        */
5068       if (activity == activity_reading ||
5069           activity == activity_update_all ||
5070           activity == activity_update_some)
5071       {
5072          for (i = 0; i < directory->modified_count; i++)
5073             XtFree(directory->modified_list[i]);
5074          XtFree((char *)directory->modified_list);
5075
5076          directory->modified_count = 0;
5077          directory->modified_list = NULL;
5078       }
5079
5080       /* close unused pipe connections */
5081       close(pipe_s2m_fd[1]);    /* parent won't write to this pipe */
5082       if (sticky)
5083          close(pipe_m2s_fd[0]); /* parent won't read from this pipe */
5084
5085    }
5086
5087    /* set up callback to get the pipe data */
5088    DPRINTF2(("ScheduleActivity:  setting up pipe callback\n"));
5089    pipe_data = (PipeCallbackData *)XtMalloc(sizeof(PipeCallbackData));
5090    pipe_data->directory = directory;
5091    pipe_data->child = pid;
5092    pipe_data->sticky_proc = p;
5093    pipe_data->activity = activity;
5094
5095    XtAppAddInput(XtWidgetToApplicationContext(toplevel),
5096                  pipe_s2m_fd[0], (XtPointer)XtInputReadMask,
5097                  ActivityTable[activity].callback, (XtPointer)pipe_data);
5098 }
5099
5100
5101 /*--------------------------------------------------------------------
5102  *  ScheduleActivity
5103  *    See if any new background work should be started.
5104  *------------------------------------------------------------------*/
5105
5106 static void
5107 ScheduleActivity(
5108    Directory *directory)
5109 {
5110    int i;
5111
5112    /* first try to schedule new activity for this directory */
5113    if (directory != NULL)
5114      ScheduleDirectoryActivity(directory);
5115
5116    /* see if there is anything else we can schedule now */
5117    if (directory == NULL || directory->activity == activity_idle)
5118    {
5119       for (i = 0; i < directory_count; i++)
5120          if (directory_set[i] != directory)
5121             ScheduleDirectoryActivity(directory_set[i]);
5122    }
5123    ScheduleDirectoryActivity(dummy_directory);
5124  }
5125
5126 static void
5127 SelectDesktopFile(
5128 FileMgrData *file_mgr_data)
5129 {
5130     DirectorySet *directory_data;
5131     FileViewData *file_view_data;
5132     int j;
5133
5134     directory_data = file_mgr_data->directory_set[0];
5135
5136     for (j = 0; j < directory_data->file_count; j++)
5137     {
5138         file_view_data = directory_data->file_view_data[j];
5139
5140         if (file_view_data->filtered != True &&
5141             strcmp(file_mgr_data->desktop_file,
5142                    file_view_data->file_data->file_name) == 0)
5143         {
5144             SelectFile (file_mgr_data, file_view_data);
5145             break;
5146         }
5147     }
5148     ActivateSingleSelect(file_mgr_data->file_mgr_rec,
5149                          file_mgr_data->selection_list[0]->file_data->logical_type);
5150     PositionFileView(file_view_data, file_mgr_data);
5151 }