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