2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
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)
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
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
23 /* $TOG: Directory.c /main/18 1999/12/09 13:05:34 mgreess $ */
24 /************************************<+>*************************************
25 ****************************************************************************
29 * COMPONENT_NAME: Desktop File Manager (dtfile)
31 * Description: Directory processing functions used by the File Browser.
33 * FUNCTIONS: CheckDesktop
34 * CheckDesktopPipeCallback
37 * DirectoryBeginModify
40 * DirectoryFileModified
48 * GetDirectoryLogicalType
49 * GetDirectoryPositionInfo
51 * InitializeDirectoryRead
52 * InitializePositionFileName
54 * PipeReadPositionInfo
56 * PipeWritePositionInfo
60 * ReadDirectoryProcess
66 * ScheduleDirectoryActivity
67 * SetDirectoryPositionInfo
72 * TimerEventBrokenLinks
76 * UpdateCachedDirectories
80 * WritePosInfoPipeCallback
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.
90 ****************************************************************************
91 ************************************<+>*************************************/
94 #include <sys/types.h>
110 #include <Dt/Connect.h>
111 #include <Dt/DtNlUtils.h>
119 #include "IconicPath.h"
121 #include "SharedMsgs.h"
122 #include "SharedProcs.h"
125 extern Boolean removingTrash;
128 /*--------------------------------------------------------------------
129 * Constants and Types
130 *------------------------------------------------------------------*/
133 #define OPTION_OFF '-'
134 #define WRITE_PRIV 'w'
135 #define READ_PRIV 'r'
136 #define EXEC_PRIV 'x'
138 /* prefix for the name of the position info file */
139 #define POSITION_FILE_PREFIX ".!dt"
141 /* kinds of messages sent through the pipe */
142 #define PIPEMSG_ERROR 1
143 #define PIPEMSG_FILEDATA 2
144 #define PIPEMSG_DONE 3
145 #define PIPEMSG_PATH_LOGICAL_TYPES 4
146 #define PIPEMSG_POSITION_INFO 5
147 #define PIPEMSG_FILEDATA2 6
148 #define PIPEMSG_FILEDATA3 7
149 #define PIPEMSG_DESKTOP_REMOVED 8
150 #define PIPEMSG_DESKTOP_CHANGED 9
153 #define FILEDATABUF 50
154 #endif /* FILEDATABUF */
159 * Background activities, ordered by priority:
160 * (activity_idle must be the last one in the list!)
164 activity_writing_posinfo, /* writing position information file */
165 activity_reading, /* reading the directory */
166 activity_update_all, /* updating the directory */
167 activity_update_some, /* updating selected files */
168 activity_checking_links, /* checking for broken links */
169 activity_checking_desktop, /* checking desktop objects */
170 activity_checking_dir, /* checking if the directory has changed */
171 activity_idle /* no background activity */
174 /* The internal directory structure and directory set list */
178 FileMgrData * file_mgr_data;
185 char * directory_name;
189 ActivityStatus activity;
190 Boolean busy[activity_idle];
191 Boolean errmsg_needed;
195 Boolean link_check_needed;
197 FileData * file_data;
201 char ** path_logical_types;
203 PositionInfo * position_info;
205 Boolean was_up_to_date;
207 char ** modified_list;
209 DirectoryView * directoryView;
213 /* data for keeping track of sticky background procs */
223 /* data for callback routines that handle background processes */
226 Directory *directory;
228 StickyProcDesc *sticky_proc;
229 ActivityStatus activity;
233 /* background procedure */
234 typedef int (*DirBackgroundProc)(int, Directory *, ActivityStatus);
236 extern void _DtFlushIconFileCache(String path);
239 /*--------------------------------------------------------------------
240 * Static Function Declarations
241 *------------------------------------------------------------------*/
243 static void TimerEvent(
244 XtPointer client_data,
246 static void ScheduleActivity(
247 Directory *directory);
248 static int WritePosInfoProcess(
250 Directory *directory,
251 ActivityStatus activity);
252 static int ReadDirectoryProcess(
254 Directory *directory,
255 ActivityStatus activity);
256 static int UpdateAllProcess(
258 Directory *directory,
259 ActivityStatus activity);
260 static int UpdateSomeProcess(
262 Directory *directory,
263 ActivityStatus activity);
264 static int TimerEventProcess(
266 Directory *directory,
267 ActivityStatus activity);
268 static int CheckDesktopProcess(
270 Directory *directory,
271 ActivityStatus activity);
272 static void WritePosInfoPipeCallback(
273 XtPointer client_data,
276 static void ReaddirPipeCallback(
277 XtPointer client_data,
280 static void TimerPipeCallback(
281 XtPointer client_data,
284 static void CheckDesktopPipeCallback(
285 XtPointer client_data,
288 static void SelectDesktopFile(FileMgrData *fmd);
292 /*--------------------------------------------------------------------
294 *------------------------------------------------------------------*/
296 int maxDirectoryProcesses = 10;
297 int maxRereadProcesses = 5;
298 int maxRereadProcsPerTick = 1;
300 XtIntervalId checkBrokenLinkTimerId = None;
302 static Directory ** directory_set = NULL;
303 static int directory_count = 0;
304 static int directory_set_size = 0;
305 static char * positionFileName = NULL;
306 static XtAppContext app_context = None;
307 static int tickTime = 0;
308 static int ticksBetweenLinkChecks = 0;
309 static Boolean timer_suspended = False;
310 static int tick_count = 0;
311 static int lastLinkCheckTick = 0;
312 static Directory dummy_dir_struct =
321 static Directory *dummy_directory = &dummy_dir_struct;
325 DirBackgroundProc main;
326 XtInputCallbackProc callback;
328 StickyProcDesc *sticky_procs;
331 { WritePosInfoProcess, WritePosInfoPipeCallback, False,NULL },/* writing_posinfo*/
332 { ReadDirectoryProcess, ReaddirPipeCallback, False,NULL }, /* reading */
333 { UpdateAllProcess, ReaddirPipeCallback, False,NULL }, /* update_all */
334 { UpdateSomeProcess, ReaddirPipeCallback, False,NULL }, /* update_some */
335 { TimerEventProcess, TimerPipeCallback, False,NULL }, /* checking_links */
336 { CheckDesktopProcess, CheckDesktopPipeCallback, False,NULL },/* checking_desktop */
337 { TimerEventProcess, TimerPipeCallback, True, NULL }, /* checking_dir */
338 { NULL, NULL, False, NULL } /* idle */
342 /*====================================================================
344 * Initialization routines
346 *==================================================================*/
348 /*--------------------------------------------------------------------
349 * InitializePositionFileName
350 * Initialize the name under which the position info is stored.
351 *------------------------------------------------------------------*/
354 InitializePositionFileName(void)
356 struct passwd * pwInfo;
358 /* Determine the name under which the position info is stored */
359 if (positionFileName == NULL)
362 pwInfo = getpwuid(getuid());
363 positionFileName = XtMalloc(strlen(pwInfo->pw_name) +
364 strlen(POSITION_FILE_PREFIX) + 1);
365 sprintf(positionFileName, "%s%s", POSITION_FILE_PREFIX, pwInfo->pw_name);
370 /*--------------------------------------------------------------------
371 * InitializeDirectoryRead
372 * Set up a timer used to automatically check the read in
373 * directories to see if they have been modified.
374 *------------------------------------------------------------------*/
377 InitializeDirectoryRead(
381 /* remeber application context */
382 app_context = XtWidgetToApplicationContext(widget);
384 /* start timer to check for modified directories and broken links */
385 tick_count = lastLinkCheckTick = 0;
389 tickTime = rereadTime;
390 ticksBetweenLinkChecks = checkBrokenLink / rereadTime;
392 else if (checkBrokenLink != 0)
394 tickTime = checkBrokenLink;
395 ticksBetweenLinkChecks = 1;
401 XtAppAddTimeOut (app_context, tickTime * 1000, TimerEvent, NULL);
403 /* start timer to check for broken desktop objects */
404 if( desktop_data->numIconsUsed > 0
405 && checkBrokenLink != 0
408 checkBrokenLinkTimerId = XtAppAddTimeOut( app_context,
409 checkBrokenLink * 1000,
410 TimerEventBrokenLinks,
415 checkBrokenLinkTimerId = None;
420 /*====================================================================
424 *==================================================================*/
426 /*--------------------------------------------------------------------
428 * Given a host & directory name, find the directory in our cache.
429 *------------------------------------------------------------------*/
434 char *directory_name)
438 /* See if the directory is in the directory set. First, compare */
439 /* the names from the directory entries ONLY. There will be one */
440 /* directory entry for every directory of a different name. This */
441 /* may mean that there is more than one directory entry for a */
442 /* single directory (ie. if there is a directory that is a link or */
443 /* a mount point in the system). */
444 /* If this doesn't succeed, we may be getting a ToolTalk resolved */
445 /* name. So run the comparison again but this time compare the */
446 /* incoming name to the tt_path_name in the directory entries. */
447 /* This algorithm has a limitation in that if ToolTalk has resolved */
448 /* a path name, the match will occur for the first entry in the */
449 /* directory set whose tt_path_name matches our name, this MAY NOT */
450 /* be the directory where the activity originated. The user should */
451 /* only notice in the case where automatic refresh is turned off. */
452 for (i = 0; i < directory_count; i++)
454 if (strcmp (host_name, directory_set[i]->host_name) == 0 &&
455 strcmp (directory_name, directory_set[i]->directory_name) == 0)
457 return directory_set[i];
461 for (i = 0; i < directory_count; i++)
463 if (directory_set[i]->tt_path_name != NULL &&
464 strcmp (host_name, home_host_name) == 0 &&
465 strcmp (directory_name, directory_set[i]->tt_path_name) == 0)
467 return directory_set[i];
476 /*--------------------------------------------------------------------
478 * Check if a directory has been removed from the cache.
479 *------------------------------------------------------------------*/
483 Directory *directory)
487 for (i = 0; i < directory_count; i++)
488 if (directory_set[i] == directory)
495 /*--------------------------------------------------------------------
497 * Free FileData structure.
498 *------------------------------------------------------------------*/
505 XtFree(file_data->file_name);
506 file_data->file_name = NULL;
508 XtFree(file_data->action_name);
509 file_data->action_name = NULL;
511 DtDtsFreeDataType(file_data->logical_type);
512 file_data->logical_type = NULL;
514 if ( file_data->final_link != NULL &&
515 file_data->final_link != file_data->link)
517 XtFree(file_data->final_link);
518 file_data->final_link = NULL;
521 if (file_data->link != NULL )
523 XtFree(file_data->link);
524 file_data->link = NULL;
528 XtFree((char *)file_data);
532 /*--------------------------------------------------------------------
534 * Free Directory structure.
535 *------------------------------------------------------------------*/
539 Directory *directory)
542 FileData *file_data, *next_file_data;
544 if( directory == NULL )
547 XtFree (directory->host_name);
548 directory->host_name = NULL;
550 XtFree (directory->directory_name);
551 directory->directory_name = NULL;
553 XtFree (directory->path_name);
554 directory->path_name = NULL;
556 XtFree (directory->tt_path_name);
557 directory->tt_path_name = NULL;
559 for (i=0; i < directory->path_count; i++)
560 DtDtsFreeDataType(directory->path_logical_types[i]);
561 XtFree ((char *) directory->path_logical_types);
562 directory->path_logical_types = NULL;
564 for (i = 0; i < directory->position_count; i++)
565 XtFree(directory->position_info[i].name);
566 XtFree ((char *) directory->position_info);
567 directory->position_info = NULL;
569 for (i = 0; i < directory->modified_count; i++)
570 XtFree(directory->modified_list[i]);
571 XtFree ((char *) directory->modified_list);
572 directory->modified_list = NULL;
574 XtFree ((char *) directory->directoryView);
575 directory->directoryView = NULL;
577 if (directory->dir_data)
579 FreeFileData(directory->dir_data, True);
580 directory->dir_data = NULL;
583 file_data = directory->file_data;
584 while (file_data != NULL)
586 next_file_data = file_data->next;
587 FreeFileData(file_data, True);
588 file_data = next_file_data;
590 directory->file_data = NULL;
592 XtFree((char *) directory);
596 /*--------------------------------------------------------------------
598 * Check if any cached directory is currently being viewed in
599 * a window that is mapped (not iconified). (If there is none,
600 * we won't need to set a refresh timer).
601 *------------------------------------------------------------------*/
604 SomeWindowMapped(void)
609 for (i = 0; i < directory_count; i++)
610 for (j = 0; j < directory_set[i]->numOfViews; j++)
611 if (directory_set[i]->directoryView[j].mapped)
618 /*====================================================================
620 * Routines for reading a directory
622 *==================================================================*/
624 /*--------------------------------------------------------------------
627 * Directories are read in a background process that is connected
628 * to the main dtfile process by a pipe.
629 * The routines below are used to send directory entry information
630 * through the pipe from the background to the main process.
631 *------------------------------------------------------------------*/
633 /* write FileData to the pipe */
639 write(fd, file_data, sizeof(FileData));
640 PipeWriteString(fd, file_data->file_name);
641 PipeWriteString(fd, file_data->action_name); /* @@@ ??? */
642 PipeWriteString(fd, file_data->logical_type);
644 PipeWriteString(fd, file_data->link);
645 if (file_data->final_link)
646 PipeWriteString(fd, file_data->final_link);
650 /* read FileData from the pipe */
658 file_data = (FileData *)XtCalloc(1,sizeof(FileData));
659 n = PipeRead(fd, file_data, sizeof(FileData));
660 if (n < sizeof(FileData))
662 fprintf(stderr, "PipeReadFileData: n = %d, expected %ld\n",
663 n, (long)sizeof(FileData));
665 file_data->file_name = PipeReadString(fd);
666 file_data->action_name = PipeReadString(fd);
667 file_data->logical_type = PipeReadString(fd);
669 file_data->link = PipeReadString(fd);
670 if (file_data->final_link)
671 file_data->final_link = PipeReadString(fd);
673 /* return the file data */
679 FileData2 *file_data2,
684 char file_name_buf[MAXPATHLEN];
685 char action_name_buf[MAXPATHLEN];
686 char logical_type_buf[MAXPATHLEN];
687 char link_buf[MAXPATHLEN];
688 char final_link_buf[MAXPATHLEN];
689 char *textptr = file_data2->text;
691 file_data = (FileData *)XtCalloc(1,sizeof(FileData));
693 strncpy(file_name_buf, textptr, n = file_data2->file_name);
694 file_name_buf[n] = NILL;
697 strncpy(action_name_buf, textptr, n = file_data2->action_name);
698 action_name_buf[n] = NILL;
701 strncpy(logical_type_buf, textptr, n = file_data2->logical_type);
702 logical_type_buf[n] = NILL;
705 strncpy(link_buf, textptr, n = file_data2->link);
709 strncpy(final_link_buf, textptr, n = file_data2->final_link);
710 final_link_buf[n] = NILL;
713 file_data->next = NULL;
714 file_data->file_name = XtNewString(file_name_buf);
715 file_data->action_name = file_data2->action_name
716 ? XtNewString(action_name_buf)
718 file_data->physical_type = file_data2->physical_type;
719 file_data->logical_type = XtNewString(logical_type_buf);
720 file_data->errnum = file_data2->errnum;
721 file_data->stat = file_data2->stat;
723 file_data->link = file_data2->link
724 ? XtNewString(link_buf)
727 file_data->final_link = file_data2->final_link
728 ? XtNewString(final_link_buf)
730 file_data->is_subdir = file_data2->is_subdir;
731 file_data->is_broken = file_data2->is_broken;
733 *l = sizeof(*file_data2) - sizeof(file_data2->text)
734 + file_data2->file_name + file_data2->action_name
735 + file_data2->logical_type + file_data2->link
736 + file_data2->final_link;
738 *l = (*l + sizeof(char *) - 1) & ~(sizeof(char *) - 1);
740 /* return the file data */
744 /* write PositionInfo to the pipe */
746 PipeWritePositionInfo(
748 PositionInfo *position_info)
750 PipeWriteString(fd, position_info->name);
751 write(fd, &position_info->x, sizeof(Position));
752 write(fd, &position_info->y, sizeof(Position));
753 write(fd, &position_info->stacking_order, sizeof(int));
757 /* read PositionInfo from the pipe */
759 PipeReadPositionInfo(
761 PositionInfo *position_info)
763 position_info->name = PipeReadString(fd);
764 PipeRead(fd, &position_info->x, sizeof(Position));
765 PipeRead(fd, &position_info->y, sizeof(Position));
766 PipeRead(fd, &position_info->stacking_order, sizeof(int));
769 /*--------------------------------------------------------------------
771 * Given a path name, return FileData for a file.
772 *------------------------------------------------------------------*/
776 char *full_directory_name,
780 char full_file_name[MAX_PATH];
781 char link_file_name[MAX_PATH];
782 char link_path[MAX_PATH];
788 Boolean recursive_link_found;
789 struct stat stat_buf;
790 struct stat stat_buf2;
795 /* Allocate a new file structure. */
796 file_data = (FileData *)XtMalloc(sizeof(FileData));
798 /* get the full name of the file */
799 strcpy (full_file_name, full_directory_name);
802 /* append file name to the directory */
803 if (strcmp(full_directory_name,"/") != 0)
804 strcat (full_file_name, "/");
805 strcat (full_file_name, file_name);
809 /* no file name passed: use last component of directory */
810 file_name = strrchr(full_file_name, '/');
811 if (file_name > full_file_name)
817 /* Follow symbolic links to their ultimate destination */
820 recursive_link_found = False;
821 strcpy(link_file_name, full_file_name);
823 stat_result = lstat (link_file_name, &stat_buf);
824 if (stat_result == 0 && (stat_buf.st_mode & S_IFMT) == S_IFLNK)
826 while ((link_len = readlink(link_file_name, link_path, MAX_PATH)) > 0)
828 link_path[link_len] = '\0';
829 link_list = (char **)XtRealloc((char *)link_list, sizeof(char *) *
832 /* Force the link to be an absolute path, if necessary */
833 if (link_path[0] != '/')
835 /* Relative paths are relative to the current directory */
836 end = strrchr(link_file_name, '/') + 1;
838 strcat(link_file_name, link_path);
841 strcpy(link_file_name, link_path);
843 /* Check for a recursive loop; abort if found */
844 for (i = 0; i < link_count; i++)
846 if (strcmp(link_file_name, link_list[i]) == 0)
848 /* Back up to last non-recursive portion */
849 strcpy(link_file_name, link_list[link_count - 1]);
850 recursive_link_found = True;
855 if (recursive_link_found)
858 link_list[link_count++] = XtNewString(link_file_name);
859 link_list[link_count] = NULL;
862 /* try to stat the file that the link points to */
863 if (stat (link_file_name, &stat_buf2) == 0)
865 /* replace lstat result with the stat */
866 memcpy(&stat_buf, &stat_buf2, sizeof(struct stat));
871 /* fill in the FileData structure with the information we found */
872 file_data->next = NULL;
873 file_data->file_name = XtNewString(file_name? file_name: ".");
874 file_data->logical_type = NULL;
875 file_data->is_subdir = False;
876 file_data->action_name = NULL;
880 file_data->link = XtNewString( link_list[0] );
881 file_data->final_link = XtNewString( link_list[link_count - 1] );
882 for (i = 0; i < link_count; i++)
883 XtFree(link_list[i]);
884 XtFree((char *)link_list);
886 file_data->link = file_data->final_link = NULL;
888 if (stat_result == 0)
890 file_data->errnum = 0;
891 file_data->stat = stat_buf;
893 /* Find and set the physical type of the file */
895 if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
897 file_data->physical_type = DtDIRECTORY;
898 if (file_name == NULL ||
899 strcmp(file_name, ".") != 0 && strcmp(file_name, "..") != 0)
901 file_data->is_subdir = True;
904 else if ((stat_buf.st_mode & S_IFMT) == S_IFREG)
906 if ((stat_buf.st_mode & S_IXUSR) ||
907 (stat_buf.st_mode & S_IXGRP) ||
908 (stat_buf.st_mode & S_IXOTH))
909 file_data->physical_type = DtEXECUTABLE;
911 file_data->physical_type = DtDATA;
914 file_data->physical_type = DtDATA;
916 /* Find and set the logical type of the file */
917 if ((stat_buf.st_mode & S_IFMT) == S_IFLNK)
919 file_data->is_broken = True;
920 if (recursive_link_found)
921 file_data->logical_type = XtNewString(LT_RECURSIVE_LINK);
923 file_data->logical_type = XtNewString(LT_BROKEN_LINK);
927 file_data->is_broken = False;
929 file_data->logical_type = (char *) DtDtsDataToDataType(
932 file_data->final_link, NULL,
935 file_data->logical_type = (char *) DtDtsDataToDataType(
940 #if defined( DATATYPE_IS_FIXED )
942 /* The problem here is there isn't a way for user to mask
943 only the OWNER READ bit in the MODE field of dtfile.dt file.
944 If the MODE field set to d&!r Then all READ permission
945 (S_IRUSR, S_IRGRP and S_IROTH)
946 bits has to be off in order for the above data typing to work.
947 Also data typing is unable to detect when the directory is not
948 the owners and only has execute permission by that owner.
949 The work around is manually checking it ourselves.
950 When the data typing code is fixed, please remove this check.
952 if( S_ISDIR( stat_buf.st_mode ) &&
953 (strcmp (file_data->logical_type, LT_DIRECTORY) == 0))
955 if( strcmp( file_name, ".." ) != 0
956 && strcmp( file_name, "." ) != 0 )
960 if( file_data->link )
961 fullPathName = file_data->link;
963 fullPathName = full_file_name;
965 if( access( fullPathName, R_OK ) != 0 )
967 XtFree( file_data->logical_type );
968 file_data->logical_type = XtNewString( LT_FOLDER_LOCK );
970 else if( access( fullPathName, W_OK ) != 0 )
972 XtFree( file_data->logical_type );
973 file_data->logical_type = XtNewString( LT_NON_WRITABLE_FOLDER );
980 if(DtActionExists(file_data->logical_type))
982 file_data->action_name = (char *)DtActionLabel(file_data->file_name);
986 char *ptr = DtDtsDataTypeToAttributeValue(file_data->logical_type,
991 file_data->action_name = XtNewString(ptr);
992 DtDtsFreeAttributeValue(ptr);
998 /* couldn't stat the file */
999 file_data->errnum = stat_errno;
1000 memset(&file_data->stat, 0, sizeof(file_data->stat));
1001 file_data->physical_type = DtUNKNOWN;
1002 file_data->is_broken = True;
1003 file_data->logical_type = XtNewString(DtDEFAULT_DATA_FT_NAME);
1010 /*--------------------------------------------------------------------
1012 * Given a path name, return FileData for a file.
1013 *------------------------------------------------------------------*/
1017 FileData2 *file_data2,
1018 char *full_directory_name,
1022 char full_file_name[MAX_PATH];
1023 char link_file_name[MAX_PATH];
1024 char link_path[MAX_PATH];
1025 char file_name_buf[MAXPATHLEN];
1026 char action_name_buf[MAXPATHLEN];
1027 char logical_type_buf[MAXPATHLEN];
1028 char link_buf[MAXPATHLEN];
1029 char final_link_buf[MAXPATHLEN];
1035 Boolean recursive_link_found;
1036 struct stat stat_buf;
1041 /* Allocate a new file structure. */
1043 /* get the full name of the file */
1044 strcpy (full_file_name, full_directory_name);
1048 /* append file name to the directory */
1049 if (strcmp(full_directory_name,"/") != 0)
1050 strcat (full_file_name, "/");
1051 strcat (full_file_name, file_name);
1055 /* no file name passed: use last component of directory */
1056 file_name = strrchr(full_file_name, '/');
1057 if (file_name > full_file_name)
1063 /* Follow symbolic links to their ultimate destination */
1066 recursive_link_found = False;
1067 strcpy(link_file_name, full_file_name);
1069 stat_result = lstat (link_file_name, &stat_buf);
1070 if ((stat_buf.st_mode & S_IFMT) == S_IFLNK)
1072 while ((link_len = readlink(link_file_name, link_path, MAX_PATH)) > 0)
1074 link_path[link_len] = NILL;
1075 link_list = (char **)XtRealloc((char *)link_list, sizeof(char *) *
1078 /* Force the link to be an absolute path, if necessary */
1079 if (link_path[0] != '/')
1081 /* Relative paths are relative to the current directory */
1082 end = strrchr(link_file_name, '/') + 1;
1084 strcat(link_file_name, link_path);
1087 strcpy(link_file_name, link_path);
1089 /* Check for a recursive loop; abort if found */
1090 for (i = 0; i < link_count; i++)
1092 if (strcmp(link_file_name, link_list[i]) == 0)
1094 /* Back up to last non-recursive portion */
1095 strcpy(link_file_name, link_list[link_count - 1]);
1096 recursive_link_found = True;
1101 if (recursive_link_found)
1104 link_list[link_count++] = XtNewString(link_file_name);
1105 link_list[link_count] = NULL;
1108 if ((stat_result = stat (link_file_name, &stat_buf)) != 0)
1110 /* probably a broken link; try lstat */
1111 stat_result = lstat (full_file_name, &stat_buf);
1112 strcpy(link_file_name, full_file_name);
1117 /* fill in the FileData2 structure with the information we found */
1118 file_data2->next = NULL;
1119 strcpy(file_name_buf, (file_name ? file_name : "."));
1120 logical_type_buf[0] = NILL;
1121 file_data2->is_subdir = False;
1122 action_name_buf[0] = NILL;
1126 strcpy(link_buf, link_list[0]);
1127 strcpy(final_link_buf, link_list[link_count - 1]);
1128 for (i = 0; i < link_count; i++) {
1129 XtFree(link_list[i]);
1131 XtFree((char *)link_list);
1133 final_link_buf[0] = NILL;
1137 if (stat_result == 0)
1139 file_data2->errnum = 0;
1140 file_data2->stat = stat_buf;
1142 /* Find and set the physical type of the file */
1144 if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
1146 file_data2->physical_type = DtDIRECTORY;
1147 if (file_name == NULL ||
1148 strcmp(file_name, ".") != 0 && strcmp(file_name, "..") != 0)
1150 file_data2->is_subdir = True;
1153 else if ((stat_buf.st_mode & S_IFMT) == S_IFREG)
1155 if ((stat_buf.st_mode & S_IXUSR) ||
1156 (stat_buf.st_mode & S_IXGRP) ||
1157 (stat_buf.st_mode & S_IXOTH))
1158 file_data2->physical_type = DtEXECUTABLE;
1160 file_data2->physical_type = DtDATA;
1163 file_data2->physical_type = DtDATA;
1165 /* Find and set the logical type of the file */
1166 if ((stat_buf.st_mode & S_IFMT) == S_IFLNK)
1168 file_data2->is_broken = True;
1169 if (recursive_link_found)
1170 strcpy(logical_type_buf, LT_RECURSIVE_LINK);
1172 strcpy(logical_type_buf, LT_BROKEN_LINK);
1178 file_data2->is_broken = False;
1179 if (link_buf[0] == NILL)
1180 ptr = (char *) DtDtsDataToDataType( full_file_name,
1185 ptr = (char *) DtDtsDataToDataType( link_buf,
1187 final_link_buf, NULL,
1190 #if defined( DATATYPE_IS_FIXED )
1191 strcpy(logical_type_buf, ptr);
1194 /* The problem here is there isn't a way for user to mask
1195 only the OWNER READ bit in the MODE field of dtfile.dt file.
1196 If the MODE field set to d&!r Then all READ permission
1197 (S_IRUSR, S_IRGRP and S_IROTH)
1198 bits has to be off in order for the above data typing to work.
1199 Also data typing is unable to detect when the directory is not
1200 the owners and only has execute permission by that owner.
1201 The work around is manually checking it ourselves.
1202 When the data typing code is fixed, please remove this check.
1204 if( !IsToolBox && S_ISDIR( stat_buf.st_mode ) &&
1205 (strcmp (ptr, LT_DIRECTORY) == 0))
1207 if( strcmp( file_name, ".." ) != 0
1208 && strcmp( file_name, "." ) != 0 )
1210 char * fullPathName;
1212 if( link_buf[0] == NILL )
1213 fullPathName = full_file_name;
1215 fullPathName = link_buf;
1217 if( access( fullPathName, R_OK ) != 0 )
1219 free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
1220 strcpy( logical_type_buf, LT_FOLDER_LOCK );
1222 else if( access( fullPathName, W_OK ) != 0 )
1224 free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
1225 strcpy( logical_type_buf, LT_NON_WRITABLE_FOLDER );
1229 strcpy( logical_type_buf, ptr );
1230 free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
1235 strcpy( logical_type_buf, ptr );
1236 free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
1241 strcpy( logical_type_buf, ptr );
1242 free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
1247 if( DtActionExists(logical_type_buf) )
1249 char *ptr = (char *)DtActionLabel(file_name_buf);
1250 strcpy(action_name_buf, ptr);
1255 char *ptr = DtDtsDataTypeToAttributeValue(logical_type_buf,
1260 strcpy(action_name_buf, ptr);
1261 DtDtsFreeAttributeValue(ptr);
1267 /* couldn't stat the file */
1268 file_data2->errnum = stat_errno;
1269 memset(&file_data2->stat, 0, sizeof(file_data2->stat));
1270 file_data2->physical_type = DtUNKNOWN;
1271 file_data2->is_broken = True;
1272 strcpy(logical_type_buf, DtDEFAULT_DATA_FT_NAME);
1275 strcpy(file_data2->text, file_name_buf);
1276 file_data2->file_name = strlen(file_name_buf);
1278 strcat(file_data2->text, action_name_buf);
1279 file_data2->action_name = strlen(action_name_buf);
1281 strcat(file_data2->text, logical_type_buf);
1282 file_data2->logical_type = strlen(logical_type_buf);
1284 strcat(file_data2->text, link_buf);
1285 file_data2->link = strlen(link_buf);
1287 strcat(file_data2->text, final_link_buf);
1288 file_data2->final_link = strlen(final_link_buf);
1290 i = sizeof(*file_data2) - sizeof(file_data2->text)
1291 + file_data2->file_name + file_data2->action_name
1292 + file_data2->logical_type + file_data2->link
1293 + file_data2->final_link;
1295 i = (i + sizeof(char *) - 1) & ~(sizeof(char *) - 1);
1298 * This data marshalling operation relies on char[BUFSIZ]
1299 * being large enough for all the text pieces. However,
1300 * BUFSIZ has nothing to do with the above operations so
1301 * we'll do this assert for now.
1303 assert( (i <= sizeof(FileData2)) );
1308 /*--------------------------------------------------------------------
1310 * Resolves the links in the path.
1311 *------------------------------------------------------------------*/
1314 GetTTPath(char *path)
1316 Tt_message dummy_msg;
1317 char *tmp, *tt_path;
1318 Tt_status tt_status;
1320 dummy_msg = tt_message_create();
1321 tt_status = tt_message_file_set(dummy_msg, path);
1322 tmp = tt_message_file(dummy_msg);
1324 tt_path = XtNewString(tmp);
1327 tt_message_destroy(dummy_msg);
1334 ReadDirectoryProcess(
1336 Directory *directory,
1337 ActivityStatus activity)
1339 #ifdef DT_PERFORMANCE
1340 struct timeval update_time_s;
1341 struct timeval update_time_f;
1343 char *host_name = directory->host_name;
1344 char *directory_name = directory->directory_name;
1345 struct stat stat_buf;
1348 char **path_logical_types;
1349 char *full_directory_name;
1354 FileData *file_data;
1355 FileData2 file_data2;
1358 short file_data_count = 0;
1362 char file_name[MAX_PATH];
1365 int x, y, stacking_order;
1368 char file_data_buffer[FILEDATABUF * sizeof(FileData2)];
1369 char *file_data_buf_ptr = file_data_buffer;
1370 char *file_data_count_ptr;
1371 struct timeval time1, time2;
1375 DPRINTF(("ReadDirectoryProcess(%d, \"%s\", \"%s\")\n",
1376 pipe_fd, host_name, directory_name));
1379 * Get the logical data type of all components of the path;
1380 * We need only the last path component for (1) the tree root icon,
1381 * and (2) the current directory icon; the other path components
1382 * are needed for the iconic path icons.
1385 path_logical_types = NULL;
1387 /* Don't muck with original string */
1388 ptrOrig = ptr = XtNewString(directory_name);
1392 Tt_status tt_status;
1397 if (ptrOrig[0] == '\0')
1402 /* get logical type of next path component */
1403 full_directory_name = ResolveLocalPathName( host_name,
1408 if( TT_OK != tt_status )
1411 DtEliminateDots (full_directory_name);
1412 path_logical_types = (char **) XtRealloc((char *)path_logical_types,
1413 (path_count + 1)*sizeof(char *));
1414 path_logical_types[path_count] =
1415 (char *) DtDtsDataToDataType(full_directory_name, NULL, 0, NULL, NULL,
1417 #if defined( DATATYPE_IS_FIXED )
1420 if( stat( full_directory_name, &stat_buf ) == 0 )
1422 if( S_ISDIR( stat_buf.st_mode ) &&
1423 (strcmp (path_logical_types[path_count], LT_DIRECTORY) == 0))
1425 if( access( full_directory_name, R_OK ) != 0 )
1427 XtFree( path_logical_types[path_count] );
1428 path_logical_types[path_count] = XtNewString( LT_FOLDER_LOCK );
1430 else if( access( full_directory_name, W_OK ) != 0 )
1432 XtFree( path_logical_types[path_count] );
1433 path_logical_types[path_count] = XtNewString( LT_NON_WRITABLE_FOLDER );
1439 DPRINTF2(("ReadDirectoryProcess: path '%s', fullname '%s', type %s\n",
1440 namePtr, full_directory_name, path_logical_types[path_count]));
1442 XtFree( full_directory_name );
1451 /* find next component */
1452 if (strcmp(ptr, "/") == 0)
1454 ptr = DtStrchr(ptr + 1, '/');
1458 /* get the full name of the current directory */
1460 Tt_status tt_status;
1461 full_directory_name = ResolveLocalPathName( host_name,
1466 /* It's ok not to check for tt_status.
1467 The code below will handle it properly.
1471 /* send the path_logical_types back through the pipe */
1472 pipe_msg = PIPEMSG_PATH_LOGICAL_TYPES;
1473 DPRINTF(("ReadDirectoryProcess: sending %d path_logical_types\n",
1475 write(pipe_fd, &pipe_msg, sizeof(short));
1476 write(pipe_fd, &path_count, sizeof(int));
1477 for(i = 0; i < path_count; i++)
1479 PipeWriteString(pipe_fd, path_logical_types[i]);
1480 XtFree((char *) path_logical_types[i]);
1482 XtFree((char *) path_logical_types);
1484 /* send the tt_path */
1485 tt_path = GetTTPath(full_directory_name);
1486 PipeWriteString(pipe_fd, tt_path);
1490 * Stat the directory to get its timestamp.
1491 * Also check if we have read and execute/search permisssion.
1493 if (CheckAccess(full_directory_name, R_OK | X_OK) != 0 ||
1494 stat(full_directory_name, &stat_buf) != 0)
1496 /* send an error code back through the pipe */
1497 pipe_msg = PIPEMSG_ERROR;
1500 DPRINTF(("ReadDirectoryProcess: sending errno %d (stat failed)\n", rc));
1501 write(pipe_fd, &pipe_msg, sizeof(short));
1502 write(pipe_fd, &rc, sizeof(int));
1503 write(pipe_fd, &modify_time, sizeof(long));
1507 modify_time = stat_buf.st_mtime;
1510 * We never want to display the '~/.dt/Desktop' directory, so when we
1511 * are working with the .dt directory, add a special check for a
1512 * directory named 'Desktop'.
1514 if ((ptr = strrchr(full_directory_name, '/')) &&
1515 (strcmp(ptr, "/.dt") == 0))
1520 /* try to open the directory */
1521 dirp = opendir (full_directory_name);
1524 /* send an error code back through the pipe */
1525 pipe_msg = PIPEMSG_ERROR;
1527 DPRINTF(("ReadDirectoryProcess: sending errno %d (opendir failed)\n",
1529 write(pipe_fd, &pipe_msg, sizeof(short));
1530 write(pipe_fd, &rc, sizeof(int));
1531 write(pipe_fd, &modify_time, sizeof(long));
1532 XtFree( full_directory_name );
1536 /* Loop through the directory entries and update the file list */
1538 #ifdef DT_PERFORMANCE
1539 printf(" begin reading directory: %s\n", full_directory_name);
1540 gettimeofday(&update_time_s, NULL);
1544 * FILEDATA3 creates a buffer of static FileData2 structures,
1545 * then sends FILEDATABUF worth of FileData2 structs to the parent.
1546 * FILEDATABUF appears to work the best when set to 50.
1548 * We send data to the parent at least every half seconds, even if
1549 * less than FILEDATABUF worth of FileData2 structs have been read.
1550 * This is to ensure that the file count in the status line gets
1551 * updated every half seconds, even if the file system is slow.
1554 /* initialize pointer into file data buffer */
1555 # define PIPEMSG_HDR_LEN (2*sizeof(short) + sizeof(int))
1556 file_data_buf_ptr = file_data_buffer + PIPEMSG_HDR_LEN;
1558 /* get current time */
1559 gettimeofday(&time1, NULL);
1566 if ((dp = readdir (dirp)) != NULL)
1569 /* if Desktop skip */
1570 if (inDtDir && (strcmp(dp->d_name, "Desktop") == 0))
1574 if(directory->directoryView && directory->directoryView->file_mgr_data)
1575 IsToolBox = directory->directoryView->file_mgr_data->toolbox;
1578 len = ReadFileData2((FileData2 *)file_data_buf_ptr,
1579 full_directory_name, dp->d_name,IsToolBox);
1580 file_data_buf_ptr += len;
1586 /* check if 0.4 seconds have passed since the last status line update */
1587 gettimeofday(&time2, NULL);
1588 diff = 1024*(time2.tv_sec - time1.tv_sec);
1589 diff += time2.tv_usec/1024;
1590 diff -= time1.tv_usec/1024;
1591 update_due = (diff >= 400);
1593 /* check if we need to send the buffered data now */
1594 if (file_data_count == FILEDATABUF ||
1595 file_data_count > 0 && (done || update_due))
1598 file_data_count |= 0x8000;
1599 len = file_data_buf_ptr - (file_data_buffer + PIPEMSG_HDR_LEN);
1601 /* now send the file data through the pipe */
1602 *(short *)file_data_buffer = PIPEMSG_FILEDATA3;
1603 *(short *)(file_data_buffer + sizeof(short)) = file_data_count;
1604 *(int *)(file_data_buffer + 2*sizeof(short)) = len;
1605 write(pipe_fd, file_data_buffer,
1606 file_data_buf_ptr - file_data_buffer);
1608 /* reset pointer to file data buffer, file count and time stamp */
1609 file_data_buf_ptr = file_data_buffer + PIPEMSG_HDR_LEN;
1610 file_data_count = 0;
1616 #ifdef DT_PERFORMANCE
1617 gettimeofday(&update_time_f, NULL);
1618 if (update_time_s.tv_usec > update_time_f.tv_usec) {
1619 update_time_f.tv_usec += 1000000;
1620 update_time_f.tv_sec--;
1622 printf(" finished reading: %s, time: %ld.%ld\n\n", full_directory_name, update_time_f.tv_sec - update_time_s.tv_sec, update_time_f.tv_usec - update_time_s.tv_usec);
1626 /* load position info, if available */
1628 /* construct full name of the position info file */
1629 if (strcmp(full_directory_name,"/") != 0)
1630 sprintf( file_name, "%s/%s", full_directory_name, positionFileName );
1632 sprintf( file_name, "%s%s", full_directory_name, positionFileName );
1634 /* read the count from the position info file */
1636 if ((fptr = fopen(file_name, "r")) != NULL)
1638 PositionInfo * position_info;
1639 fscanf(fptr, "%d\n", &position_count);
1641 if (position_count > 0)
1643 /* allocate position info array */
1644 position_info = (PositionInfo *)
1645 XtMalloc(position_count * sizeof(PositionInfo));
1647 /* read the position info from the file */
1649 while (i < position_count)
1651 if( fgets( file_name, MAX_PATH, fptr ) != NULL &&
1652 fscanf(fptr, "%d %d %d\n", &x, &y, &stacking_order ) == 3 )
1654 int len = strlen(file_name);
1655 file_name[len-1] = 0x0;
1656 position_info[i].name = XtNewString(file_name);
1657 position_info[i].x = x,
1658 position_info[i].y = y,
1659 position_info[i].stacking_order = stacking_order;
1670 /* send the position info back through the pipe */
1671 pipe_msg = PIPEMSG_POSITION_INFO;
1672 DPRINTF(("ReadDirectoryProcess: sending %d position_info\n",
1674 write(pipe_fd, &pipe_msg, sizeof(short));
1675 write(pipe_fd, &position_count, sizeof(int));
1676 for (i = 0; i < position_count; i++)
1678 PipeWriteString( pipe_fd, position_info[i].name );
1679 XtFree( position_info[i].name );
1680 write( pipe_fd, &(position_info[i].x), sizeof(Position));
1681 write( pipe_fd, &(position_info[i].y), sizeof(Position));
1682 write( pipe_fd, &(position_info[i].stacking_order), sizeof(int));
1685 XtFree( (char *)position_info );
1688 XtFree(full_directory_name);
1691 /* send a 'done' msg through the pipe */
1692 DPRINTF(("ReadDirectoryProcess: sending DONE\n"));
1693 pipe_msg = PIPEMSG_DONE;
1694 write(pipe_fd, &pipe_msg, sizeof(short));
1695 write(pipe_fd, &modify_time, sizeof(long));
1700 /*--------------------------------------------------------------------
1702 * Main routine of the background process that checks the directory
1703 * for new files or files that have disapeared.
1704 *------------------------------------------------------------------*/
1709 Directory *directory,
1710 ActivityStatus activity)
1712 char *host_name = directory->host_name;
1713 char *directory_name = directory->directory_name;
1714 char *full_directory_name;
1715 struct stat stat_buf;
1720 FileData *file_data;
1721 FileData *old_data, **old_pp;
1722 FileData2 file_data2;
1726 Tt_status tt_status;
1727 char **path_logical_types;
1731 DPRINTF(("UpdateAllProcess(%d, \"%s\", \"%s\")\n",
1732 pipe_fd, host_name, directory_name));
1734 /* if modified list contains "." or ".." arrange for the path_logical_types
1736 if(directory->modified_count == 0 &&
1737 FindDirectory(directory->host_name, directory_name))
1743 for (i = 0; i < directory->modified_count; i++)
1745 if (strcmp(directory->modified_list[i],".") == 0 ||
1746 strcmp(directory->modified_list[i],"..") == 0 )
1758 path_logical_types = NULL;
1759 ptrOrig = ptr = XtNewString(directory_name);
1762 Tt_status tt_status;
1768 if (ptrOrig[0] == '\0')
1773 /* get logical type of next path component */
1774 full_directory_name = ResolveLocalPathName( host_name,
1779 if( TT_OK != tt_status )
1782 DtEliminateDots (full_directory_name);
1783 path_logical_types = (char **) XtRealloc((char *)path_logical_types,
1784 (path_count + 1)*sizeof(char *));
1785 path_logical_types[path_count] =
1786 (char *)DtDtsDataToDataType(full_directory_name, NULL, 0, NULL, NULL,
1788 #if defined( DATATYPE_IS_FIXED )
1791 if( stat( full_directory_name, &stat_buf ) == 0 )
1793 if( S_ISDIR( stat_buf.st_mode ) &&
1794 (strcmp (path_logical_types[path_count], LT_DIRECTORY) == 0))
1796 if( access( full_directory_name, R_OK ) != 0 )
1798 XtFree( path_logical_types[path_count] );
1799 path_logical_types[path_count] = XtNewString( LT_FOLDER_LOCK );
1801 else if( access( full_directory_name, W_OK ) != 0 )
1803 XtFree( path_logical_types[path_count] );
1804 path_logical_types[path_count] = XtNewString( LT_NON_WRITABLE_FOLDER );
1811 DPRINTF2(("ReadDirectoryProcess: path '%s', fullname '%s', type %s\n",
1812 namePtr, full_directory_name, path_logical_types[path_count]));
1814 XtFree(full_directory_name);
1823 /* find next component */
1824 if (strcmp(ptr, "/") == 0)
1826 ptr = DtStrchr(ptr + 1, '/');
1830 /* get the full name of the current directory */
1831 full_directory_name = ResolveLocalPathName( host_name,
1836 /* It's ok not to check for tt_status.
1837 The code below will handle it properly.
1840 /* send the path_logical_types back through the pipe */
1841 pipe_msg = PIPEMSG_PATH_LOGICAL_TYPES;
1842 DPRINTF(("ReadDirectoryProcess: sending %d path_logical_types\n",
1844 write(pipe_fd, &pipe_msg, sizeof(short));
1845 write(pipe_fd, &path_count, sizeof(int));
1846 for(i = 0; i < path_count; i++)
1848 PipeWriteString(pipe_fd, path_logical_types[i]);
1849 XtFree((char *) path_logical_types[i]);
1851 XtFree((char *) path_logical_types);
1853 /* send the tt_path */
1854 tt_path = GetTTPath(full_directory_name);
1855 PipeWriteString(pipe_fd, tt_path);
1860 full_directory_name = ResolveLocalPathName( host_name,
1867 if( TT_OK != tt_status )
1869 pipe_msg = PIPEMSG_ERROR;
1872 DPRINTF(("UpdateAllProcess: sending errno %d (tooltalk failed)\n", rc));
1873 write(pipe_fd, &pipe_msg, sizeof(short));
1874 write(pipe_fd, &rc, sizeof(int));
1875 write(pipe_fd, &modify_time, sizeof(long));
1878 (void) DtEliminateDots (full_directory_name);
1881 * Stat the directory to get its timestamp.
1882 * Also check if we still have read and execute/search permisssion.
1884 if (CheckAccess(full_directory_name, R_OK | X_OK) != 0 ||
1885 stat(full_directory_name, &stat_buf) != 0)
1887 /* send an error code back through the pipe */
1888 pipe_msg = PIPEMSG_ERROR;
1891 DPRINTF(("UpdateAllProcess: sending errno %d (stat failed)\n", rc));
1892 write(pipe_fd, &pipe_msg, sizeof(short));
1893 write(pipe_fd, &rc, sizeof(int));
1894 write(pipe_fd, &modify_time, sizeof(long));
1895 XtFree( full_directory_name );
1898 modify_time = stat_buf.st_mtime;
1900 /* check if we are in the .dt directory */
1901 if ((ptr = strrchr(full_directory_name, '/')) &&
1902 (strcmp(ptr, "/.dt") == 0))
1909 /* try to open the directory */
1910 dirp = opendir (full_directory_name);
1913 /* send an error code back through the pipe */
1914 pipe_msg = PIPEMSG_ERROR;
1916 DPRINTF(("UpdateAllProcess: sending errno %d (opendir failed)\n", rc));
1917 write(pipe_fd, &pipe_msg, sizeof(short));
1918 write(pipe_fd, &rc, sizeof(int));
1919 write(pipe_fd, &modify_time, sizeof(long));
1920 XtFree( full_directory_name );
1924 /* Loop through the directory entries and update the file list */
1925 while (dp = readdir (dirp))
1927 /* if Desktop skip */
1928 if (inDtDir && (strcmp(dp->d_name, "Desktop") == 0))
1931 /* check if we already know this file */
1932 for (old_pp = &directory->file_data;
1933 (old_data = *old_pp) != NULL;
1934 old_pp = &old_data->next)
1936 if (strcmp(dp->d_name, old_data->file_name) == 0)
1941 /* check modified times */
1942 tname = XtMalloc(strlen(full_directory_name)+strlen(dp->d_name)+2);
1943 sprintf(tname,"%s/%s",full_directory_name,dp->d_name);
1944 if((lstat(tname,&sbuf)>=0)&&sbuf.st_mtime!=old_data->stat.st_mtime)
1947 if(old_data == NULL)
1950 /* check if this file appears on the modified list */
1951 for (i = 0; i < directory->modified_count; i++)
1952 if (strcmp(dp->d_name, directory->modified_list[i]) == 0)
1955 * This file is on the modified list.
1956 * Pretend we didn't find it to force a refresh of this file.
1965 /* If this is a known file, remember we saw it and continue. */
1966 if (old_data != NULL)
1969 * We remove the file from the old file list; thus, when we are done,
1970 * the files on the old file list will be onew that no longer exist.
1972 *old_pp = old_data->next;
1976 /* this is a new file */
1977 DPRINTF(("UpdateAllProcess: found new file \"%s\"\n", dp->d_name));
1979 /* Fix for incorrect icons in App Manager */
1983 if(directory->directoryView && directory->directoryView->file_mgr_data)
1984 IsToolBox = directory->directoryView->file_mgr_data->toolbox;
1987 ReadFileData2(&file_data2, full_directory_name, dp->d_name,IsToolBox);
1989 file_data = FileData2toFileData(&file_data2, &n);
1991 /* now send the file data through the pipe */
1992 pipe_msg = PIPEMSG_FILEDATA;
1993 write(pipe_fd, &pipe_msg, sizeof(short));
1994 PipeWriteFileData(pipe_fd, file_data);
1996 FreeFileData(file_data, True);
1999 /* all files left in the old file list no longer exist */
2000 for (old_data = directory->file_data;
2002 old_data = old_data->next)
2004 DPRINTF(("UpdateAllProcess: file gone \"%s\"\n", old_data->file_name));
2005 old_data->errnum = ENOENT;
2006 pipe_msg = PIPEMSG_FILEDATA;
2007 write(pipe_fd, &pipe_msg, sizeof(short));
2008 PipeWriteFileData(pipe_fd, old_data);
2012 XtFree(full_directory_name);
2014 /* send a 'done' msg through the pipe */
2015 DPRINTF(("UpdateAllProcess: sending DONE\n"));
2016 pipe_msg = PIPEMSG_DONE;
2017 write(pipe_fd, &pipe_msg, sizeof(short));
2018 write(pipe_fd, &modify_time, sizeof(long));
2023 /*--------------------------------------------------------------------
2025 * Main routine of the background process that updates a selected
2026 * list of directory entries.
2027 *------------------------------------------------------------------*/
2032 Directory *directory,
2033 ActivityStatus activity)
2035 char *host_name = directory->host_name;
2036 char *directory_name = directory->directory_name;
2037 char *full_directory_name;
2038 struct stat stat_buf;
2040 FileData *file_data;
2041 FileData2 file_data2;
2047 DPRINTF(("UpdateSomeProcess(%d, \"%s\", \"%s\")\n",
2048 pipe_fd, host_name, directory_name));
2051 /* get the full name of the current directory */
2053 Tt_status tt_status;
2055 full_directory_name = ResolveLocalPathName( host_name,
2060 if( TT_OK != tt_status )
2062 pipe_msg = PIPEMSG_ERROR;
2065 DPRINTF(("UpdateSomeProcess: sending errno %d (stat failed)\n", rc));
2066 write(pipe_fd, &pipe_msg, sizeof(short));
2067 write(pipe_fd, &rc, sizeof(int));
2068 write(pipe_fd, &modify_time, sizeof(long));
2072 (void) DtEliminateDots (full_directory_name);
2074 /* stat the directory to get the timestamp */
2075 if (stat(full_directory_name, &stat_buf) < 0 ||
2076 ! (stat_buf.st_mode & S_IXUSR) )
2078 /* send an error code back through the pipe */
2079 pipe_msg = PIPEMSG_ERROR;
2082 DPRINTF(("UpdateSomeProcess: sending errno %d (stat failed)\n", rc));
2083 write(pipe_fd, &pipe_msg, sizeof(short));
2084 write(pipe_fd, &rc, sizeof(int));
2085 write(pipe_fd, &modify_time, sizeof(long));
2086 XtFree( full_directory_name );
2089 modify_time = stat_buf.st_mtime;
2091 /* Loop through the list of modified files */
2092 for (i = 0; i < directory->modified_count; i++)
2096 if(directory->directoryView && directory->directoryView->file_mgr_data)
2097 IsToolBox = directory->directoryView->file_mgr_data->toolbox;
2101 ReadFileData2(&file_data2, full_directory_name,
2102 directory->modified_list[i],IsToolBox);
2104 /* now send the file data through the pipe */
2105 pipe_msg = PIPEMSG_FILEDATA2;
2106 write(pipe_fd, &pipe_msg, sizeof(short));
2107 write(pipe_fd, &file_data2, sizeof(FileData2));
2110 XtFree(full_directory_name);
2112 /* send a 'done' msg through the pipe */
2113 DPRINTF(("UpdateSomeProcess: sending DONE\n"));
2114 pipe_msg = PIPEMSG_DONE;
2115 write(pipe_fd, &pipe_msg, sizeof(short));
2116 write(pipe_fd, &modify_time, sizeof(long));
2121 /*--------------------------------------------------------------------
2122 * ReaddirPipeCallback
2123 * Callback routine that reads directory entry information sent
2124 * through the pipe from the background process.
2125 *------------------------------------------------------------------*/
2128 ReaddirPipeCallback(
2129 XtPointer client_data,
2133 static int whined_fd = 0;
2134 PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
2135 Directory *directory = pipe_data->directory;
2136 FileMgrData *file_mgr_data;
2137 FileMgrRec *file_mgr_rec;
2138 ActivityStatus activity;
2142 FileData *new_data, **new_nextp;
2143 FileData *old_data, **old_nextp;
2149 char dirname[MAX_PATH];
2150 short file_data_count;
2153 /* verify that the directory still exists */
2154 if (DirectoryGone(directory))
2157 * The directory is no longer present:
2158 * close the pipe and kill the reader.
2162 kill(pipe_data->child, SIGKILL);
2163 XtFree( client_data );
2164 ScheduleActivity(NULL);
2168 /* read the next msg from the pipe */
2170 n = PipeRead(*fd, &msg, sizeof(short));
2171 activity = directory->activity;
2176 case PIPEMSG_PATH_LOGICAL_TYPES:
2177 /* get the number of path components */
2178 PipeRead(*fd, &n, sizeof(int));
2180 /* free logical types */
2181 for (i = 0; i < directory->path_count; i++)
2182 XtFree(directory->path_logical_types[i]);
2184 /* allocate array of the right size */
2185 if (directory->path_count != n)
2187 directory->path_count = n;
2188 directory->path_logical_types = (char **)
2189 XtRealloc((char *)directory->path_logical_types,
2193 /* get new logical types */
2194 for (i = 0; i < directory->path_count; i++)
2195 directory->path_logical_types[i] = PipeReadString(*fd);
2197 /* get the tt_path */
2198 XtFree(directory->tt_path_name);
2199 directory->tt_path_name = PipeReadString(*fd);
2201 /* update all views */
2202 for (i = 0; i < directory->numOfViews; i++)
2204 file_mgr_data = directory->directoryView[i].file_mgr_data;
2205 file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2206 UpdateHeaders(file_mgr_rec, file_mgr_data, True);
2207 XmUpdateDisplay(file_mgr_rec->file_window);
2209 XSync(XtDisplay(toplevel), False);
2212 case PIPEMSG_FILEDATA:
2213 case PIPEMSG_FILEDATA2:
2214 case PIPEMSG_FILEDATA3:
2216 if (msg == PIPEMSG_FILEDATA)
2218 new_data = PipeReadFileData(*fd);
2220 else if (msg == PIPEMSG_FILEDATA2)
2222 FileData2 file_data2;
2225 n = PipeRead(*fd, &file_data2, sizeof(FileData2));
2226 if (n < sizeof(FileData2)) {
2228 fprintf(stderr, "PipeReadFileData2: n = %d, expected %ld\n",
2229 n, (long)sizeof(FileData2));
2232 new_data = FileData2toFileData(&file_data2, &n);
2235 if (msg == PIPEMSG_FILEDATA3)
2237 int file_data_length;
2239 char file_data_buffer[FILEDATABUF * sizeof(FileData2)];
2240 char *file_data_buf_ptr;
2243 n = PipeRead(*fd, &file_data_count, sizeof(short));
2244 n = PipeRead(*fd, &file_data_length, sizeof(int));
2246 if (file_data_count & 0x8000)
2248 file_data_count &= 0x7fff;
2254 for (new_nextp = &directory->new_data;
2256 new_nextp = &(*new_nextp)->next)
2259 n = PipeRead(*fd, file_data_buffer, file_data_length);
2260 file_data_buf_ptr = file_data_buffer;
2262 for (i = 0; i < file_data_count; i++)
2264 /* get next FileData out of buffer */
2266 FileData2toFileData((FileData2 *)file_data_buf_ptr, &n);
2267 file_data_buf_ptr += n;
2269 /* append new_data to end of list */
2270 *new_nextp = new_data;
2271 new_data->next = NULL;
2272 new_nextp = &new_data->next;
2277 /* append new_data to end of list */
2278 file_data_count = 1;
2280 for (new_nextp = &directory->new_data;
2282 new_nextp = &(*new_nextp)->next)
2284 *new_nextp = new_data;
2285 new_data->next = NULL;
2288 if (activity == activity_reading)
2290 /* update file counts in all views */
2291 for (i = 0; i < directory->numOfViews; i++)
2293 file_mgr_data = directory->directoryView[i].file_mgr_data;
2294 file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2295 file_mgr_data->busy_detail += file_data_count;
2297 (file_mgr_data->busy_status == initiating_readdir ||
2298 file_mgr_data->busy_status == busy_readdir) &&
2299 file_mgr_data->busy_detail > 2)
2301 if (file_mgr_data->show_status_line)
2304 XmString label_string;
2307 GetStatusMsg(file_mgr_data, buf);
2309 XmStringCreateLocalized(buf);
2310 XtSetArg (args[0], XmNlabelString, label_string);
2311 XtSetValues(file_mgr_rec->status_line, args, 1);
2312 XmStringFree(label_string);
2314 else if (file_mgr_data->show_iconic_path)
2316 DtUpdateIconicPath(file_mgr_rec, file_mgr_data, False);
2318 else if (file_mgr_data->show_current_dir)
2320 DrawCurrentDirectory(file_mgr_rec->current_directory,
2321 file_mgr_rec, file_mgr_data);
2328 case PIPEMSG_POSITION_INFO:
2329 /* free old position info names */
2330 for (i = 0; i < directory->position_count; i++)
2331 XtFree(directory->position_info[i].name);
2333 /* get number of positions and realloc array, if necessary */
2334 PipeRead(*fd, &n, sizeof(int));
2335 if (directory->position_count != n)
2337 directory->position_count = n;
2338 directory->position_info = (PositionInfo *) XtRealloc(
2339 (char *)directory->position_info, n*sizeof(PositionInfo));
2342 /* read new position info */
2343 for (i = 0; i < n; i++)
2344 PipeReadPositionInfo(*fd, &directory->position_info[i]);
2348 PipeRead(*fd, &modify_time, sizeof(long));
2349 directory->errnum = 0;
2350 directory->errmsg_needed = False;
2355 PipeRead(*fd, &rc, sizeof(int));
2356 PipeRead(*fd, &modify_time, sizeof(long));
2357 if (rc != directory->errnum)
2359 directory->errnum = rc;
2360 directory->errmsg_needed = True;
2366 if (whined_fd != *fd)
2370 "ReaddirPipeCallback: badmsg=%d, ppid=%d pid=%d fd=%d activ'y=%d\n",
2371 msg, getppid(), getpid(), *fd, activity);
2373 directory->errnum = -1;
2374 directory->errmsg_needed = False;
2378 /* check if we are done */
2381 #ifdef DT_PERFORMANCE
2382 /* Aloke Gupta: As suggested by Dana Dao */
2383 _DtPerfChkpntMsgSend("Done Read Directory");
2385 DPRINTF(("ReaddirPipeCallback: done, errno %d, time %ld\n",
2386 directory->errnum, modify_time));
2388 /* close the pipe and cancel the callback */
2393 * @@@ what if a drag is active ???
2397 * For files in the old list that still exist in the new
2398 * one we need to re-use the old FileData structures.
2399 * Reason: the code in GetFileData relies on this to
2400 * preserve the position_info and selection list.
2401 * The following loops through the new list of files
2402 * and replaces entries that also exist in the old list.
2404 for (new_nextp = &directory->new_data;
2405 (new_data = *new_nextp) != NULL;
2406 new_nextp = &new_data->next)
2408 for (old_nextp = &directory->file_data;
2409 (old_data = *old_nextp) != NULL;
2410 old_nextp = &old_data->next)
2412 if( strcmp(old_data->file_name, new_data->file_name) == 0 )
2414 *old_nextp = old_data->next;
2416 FreeFileData(old_data, False);
2417 memcpy(old_data, new_data, sizeof(FileData));
2419 XtFree((char *)new_data);
2420 *new_nextp = new_data = old_data;
2428 * If this was a complete re-read, we free all FileData still left
2429 * in the old list. Otherwise, if this was just a partial update,
2430 * we append the old data that's still left to the end of the
2433 if (activity == activity_reading)
2435 /* This was a complete re-read: free all old data still left. */
2436 while (directory->file_data)
2438 old_data = directory->file_data;
2439 directory->file_data = old_data->next;
2440 FreeFileData(old_data, True);
2443 /* replace the old list by the new list */
2444 directory->file_data = directory->new_data;
2445 directory->new_data = NULL;
2449 FileData * tmp_ptr = NULL;
2451 /* remove any directory entries that no longer exist
2454 new_nextp = &directory->new_data;
2455 while ((new_data = *new_nextp) != NULL)
2457 if (new_data->errnum == ENOENT)
2459 *new_nextp = new_data->next;
2460 FreeFileData(new_data, True);
2464 tmp_ptr = *new_nextp;
2465 new_nextp = &new_data->next;
2469 /* remove any directory entries that no longer exist
2472 old_nextp = &directory->file_data;
2473 while ((old_data = *old_nextp) != NULL)
2475 if (old_data->errnum == ENOENT)
2477 *old_nextp = old_data->next;
2478 FreeFileData(old_data, True);
2481 old_nextp = &old_data->next;
2484 /* Append the old list to the end of the new list
2485 Replace the old list pointer with the new list pointer
2487 if( tmp_ptr != NULL )
2489 tmp_ptr->next = directory->file_data;
2490 directory->file_data = directory->new_data;
2491 directory->new_data = NULL;
2495 /* update the file count */
2496 directory->file_count = 0;
2497 for (new_data = directory->file_data; new_data; new_data = new_data->next)
2498 directory->file_count++;
2500 /* update directory timestamp */
2501 if (activity == activity_reading ||
2502 activity == activity_update_all ||
2503 directory->was_up_to_date)
2505 if (modify_time != 0)
2506 directory->modify_time = modify_time;
2509 /* flush the icon cache */ /* @@@ Why? What does this do? @@@ */
2510 strcpy (dirname, directory->path_name);
2511 DtEliminateDots(dirname);
2512 /* We will not attempt to flush the icon cache until this */
2513 /* function has been fixed. */
2515 _DtFlushIconFileCache(dirname);
2517 /* reset busy flags */
2518 directory->busy[activity] = False;
2519 directory->activity = activity_idle;
2520 directory->was_up_to_date = True;
2521 directory->link_check_needed = False;
2525 * Fill dir_data field with information on the directory itself.
2526 * This data will be read when querying this view's top directory,
2527 * if the parent directory isn't already cached (tree mode)
2529 for (new_data = directory->file_data;
2531 new_data = new_data->next)
2532 if (strcmp(new_data->file_name, ".") == 0)
2536 * Found current directory information, now we make
2537 * dir_data info from "." info
2540 /* If we already have allocated space for dir_data free it */
2541 if ( directory->dir_data != NULL )
2542 FreeFileData(directory->dir_data, True);
2544 directory->dir_data = (FileData *)XtMalloc(sizeof(FileData));
2546 memcpy(directory->dir_data, new_data, sizeof(FileData));
2549 * Doctor up some of the information fields so that this doesn't
2550 * seem to be a "." entry
2552 directory->dir_data->next = NULL;
2553 directory->dir_data->file_name =
2554 XtNewString(DName(directory->directory_name));
2555 directory->dir_data->action_name = NULL;
2556 if (directory->path_count > 0)
2558 directory->dir_data->logical_type = XtNewString(
2559 directory->path_logical_types[directory->path_count - 1]);
2562 directory->dir_data->logical_type = NULL;
2563 directory->dir_data->link = NULL;
2564 directory->dir_data->final_link = NULL;
2565 directory->dir_data->is_subdir = True;
2570 /* cause all views on this directory to be redrawn */
2571 for (i = 0; i < directory->numOfViews; i++)
2573 file_mgr_data = directory->directoryView[i].file_mgr_data;
2574 file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2575 FileMgrRedisplayFiles(file_mgr_rec, file_mgr_data, False);
2576 if(file_mgr_data->desktop_file)
2578 SelectDesktopFile(file_mgr_data);
2579 XtFree(file_mgr_data->desktop_file);
2580 file_mgr_data->desktop_file = NULL;
2583 XtFree(client_data);
2585 /* schedule the next background activity */
2586 ScheduleActivity(directory);
2591 /*--------------------------------------------------------------------
2592 * ReadDirectoryFiles
2593 * This routine is called to read a directory if the directory
2594 * wasn't found in the cached, or if the directory has to be
2595 * re-read because it changed. This routine schedules a background
2596 * process to be started that will do the actual work.
2597 *------------------------------------------------------------------*/
2602 Directory *directory)
2604 FileMgrData *file_mgr_data;
2605 FileMgrRec *file_mgr_rec;
2608 #ifdef DT_PERFORMANCE
2610 _DtPerfChkpntMsgSend("Begin Read Directory");
2613 /* make sure positionFileName is initialized */
2614 if (positionFileName == NULL)
2615 InitializePositionFileName();
2617 /* mark the directory busy reading */
2618 directory->busy[activity_reading] = True;
2620 /* arrange for background process to be started */
2621 ScheduleActivity(directory);
2623 /* make sure all views on this directory are marked busy */
2624 for (i = 0; i < directory->numOfViews; i++)
2626 file_mgr_data = directory->directoryView[i].file_mgr_data;
2627 if (file_mgr_data->busy_status == not_busy)
2629 file_mgr_data->busy_status = busy_readdir;
2630 file_mgr_data->busy_detail = 0;
2631 file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2632 FileMgrRedisplayFiles(file_mgr_rec, file_mgr_data, False);
2634 file_mgr_data->busy_status = busy_readdir;
2640 /*--------------------------------------------------------------------
2642 * Given a directory name, see if the directory is already cached.
2643 * If so, return the file data list, otherwise, read the directory.
2644 *------------------------------------------------------------------*/
2650 char *directory_name,
2651 FileData **file_data,
2653 FileMgrData *file_mgr_data)
2655 Directory *directory;
2659 /* initialize return values */
2660 if (file_data != NULL)
2666 /* see if the directory is already in the cache */
2667 directory = FindDirectory(host_name, directory_name);
2669 if ((directory != NULL) &&
2670 (strcmp(directory_name, directory->directory_name) == 0))
2672 /* The directory is already in the cache. */
2673 directory->viewed = True;
2675 /* Look for the view in the view list */
2676 for (i = 0; i < directory->numOfViews; i++)
2677 if (directory->directoryView[i].file_mgr_data == file_mgr_data)
2680 /* If view not found, add to the view list */
2681 if (i == directory->numOfViews)
2683 directory->directoryView = (DirectoryView *)
2684 XtRealloc ((char *) directory->directoryView,
2685 sizeof(DirectoryView) * (i + 1));
2686 directory->numOfViews++;
2687 directory->directoryView[i].file_mgr_data = file_mgr_data;
2690 /* set mapped flag for the view */
2691 directory->directoryView[i].mapped = file_mgr_data->mapped;
2693 /* check if we need to popup an error message */
2694 if (directory->errmsg_needed &&
2695 !directory->busy[activity_reading] &&
2698 err_msg = XtNewString(GetSharedMessage(CANNOT_READ_DIRECTORY_ERROR));
2699 FileOperationError (w, err_msg, directory_name);
2701 directory->errmsg_needed = False;
2704 DPRINTF2(("ReadDirectory(\"%s\", \"%s\") returns cached\n",
2705 host_name, directory_name));
2710 Tt_status tt_status;
2712 /* The directory is not yet in the cache. */
2714 /* Expand the directory set array, if necessary. */
2715 if (directory_count == directory_set_size)
2717 directory_set_size += 10;
2718 directory_set = (Directory **) XtRealloc((char *)directory_set,
2719 sizeof(Directory **) * directory_set_size);
2723 /* Create and initialize a new directory entry */
2724 directory_set[directory_count] = directory =
2725 (Directory *) XtMalloc (sizeof (Directory));
2728 directory->host_name = XtNewString (host_name);
2729 directory->directory_name = XtNewString (directory_name);
2730 directory->path_name = ResolveLocalPathName (host_name,
2735 if (directory->path_name == NULL)
2737 directory->path_name = (char *) XtMalloc(sizeof(char));
2738 directory->path_name[0]='\0';
2740 directory->tt_path_name = NULL;
2741 directory->viewed = True;
2742 directory->file_count = 0;
2743 directory->numOfViews = 1;
2744 directory->errnum = 0;
2745 directory->errmsg_needed = False;
2746 directory->last_check = 0;
2747 directory->link_check_needed = False;
2748 directory->file_count = 0;
2749 directory->file_data = NULL;
2750 directory->new_data = NULL;
2751 directory->dir_data = NULL;
2752 directory->path_count = 0;
2753 directory->path_logical_types = NULL;
2754 directory->position_count = 0;
2755 directory->position_info = NULL;
2756 directory->modify_begin = 0;
2757 directory->modified_count = 0;
2758 directory->was_up_to_date = True;
2759 directory->modified_list = NULL;
2760 directory->activity = activity_idle;
2761 for (i = 0; i < activity_idle; i++)
2762 directory->busy[i] = False;
2764 directory->directoryView = (DirectoryView *)
2765 XtMalloc (sizeof(DirectoryView));
2766 directory->directoryView[0].file_mgr_data = file_mgr_data;
2767 directory->directoryView[0].mapped = file_mgr_data->mapped;
2769 /* Open the directory for reading and read the files. */
2770 ReadDirectoryFiles (w, directory);
2773 /* Restart refresh timer, if necessary */
2774 if (file_mgr_data->mapped && timer_suspended)
2776 XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
2777 timer_suspended = False;
2780 /* return the file data */
2781 if (file_data != NULL && !directory->busy[activity_reading])
2783 *file_count = directory->file_count;
2784 *file_data = directory->file_data;
2787 return directory->busy[activity_reading];
2791 /*--------------------------------------------------------------------
2793 * Internal routine that recursively read a directory plus
2794 * subdirectories down to a depth given by read_level.
2795 *------------------------------------------------------------------*/
2800 FileMgrData *file_mgr_data,
2802 char *directory_name,
2803 FileViewData *dp, /* directory info */
2804 int level, /* tree level of this directory */
2805 int read_level, /* deepest level to be read */
2806 char **branch_list) /* list of tree branches to expand */
2808 * Recursively read a directory plus subdirectories down to a depth
2809 * given by read_level.
2812 char subdir_name[MAX_PATH];
2813 FileData *fp, *file_data;
2814 FileViewData **lp, *ip;
2817 Boolean busy_reading;
2819 DPRINTF2(("_ReadDir(\"%s\", \"%s\"): level %d, read_level %d\n",
2820 host_name, directory_name, level, read_level));
2822 /* initialize list of descendents and counts */
2826 dp->ndir = dp->nfile = 0;
2831 /* Read the directory content */
2832 busy_reading = ReadDirectory(w, host_name, directory_name,
2833 &file_data, &n, file_mgr_data);
2836 file_mgr_data->busy_status = busy_readdir;
2845 for (i = 0, fp = file_data; i < n && fp; i++, fp = fp->next) {
2847 /* initialize new dir entry */
2850 ip = (FileViewData *)XtMalloc(sizeof(FileViewData));
2851 memset(ip, 0, sizeof(FileViewData));
2857 /* read subdirectory */
2860 /* construct sub directory name */
2861 strcpy(subdir_name, directory_name);
2862 if (subdir_name[strlen(subdir_name) - 1] != '/')
2863 strcat(subdir_name, "/");
2864 strcat(subdir_name, fp->file_name);
2866 /* see if we know this entry from branch_list */
2867 if (!QueryBranchList(file_mgr_data, branch_list, subdir_name, &ts))
2868 /* not known: assume we shouldn't read this subdir */
2871 if (level < read_level || ts != tsNotRead) {
2873 rc = _ReadDir(w, file_mgr_data, host_name, subdir_name, ip,
2874 level, read_level, branch_list);
2879 else if (ts >= tsReading)
2881 else if (level >= file_mgr_data->tree_show_level)
2883 else if (file_mgr_data->tree_files == TREE_FILES_ALWAYS)
2890 /* add new entry to linked list */
2902 /*--------------------------------------------------------------------
2904 * This is the main external entry point for reading directories.
2905 *------------------------------------------------------------------*/
2910 FileMgrData *file_mgr_data,
2912 char *directory_name,
2913 FileViewData *dp, /* directory info */
2914 int level, /* tree level of this directory */
2915 int read_level, /* deepest level to be read */
2916 char **branch_list) /* list of tree branches to expand */
2918 /* initially assume we are not busy */
2919 if (file_mgr_data->busy_status == not_busy)
2920 file_mgr_data->busy_detail = 0;
2922 file_mgr_data->busy_status = initiating_readdir;
2924 /* first pass: just check if any directory we need is busy */
2925 _ReadDir(w, file_mgr_data, host_name, directory_name, NULL, level,
2926 read_level, branch_list);
2928 /* if a directory we need is busy, return now */
2929 if (file_mgr_data->busy_status == busy_readdir)
2933 * All directories wee need are available.
2934 * Make a second pass for real.
2936 file_mgr_data->busy_status = not_busy;
2937 return _ReadDir(w, file_mgr_data, host_name, directory_name, dp, level,
2938 read_level, branch_list);
2942 /*====================================================================
2944 * Routines that update the directory cache
2946 *==================================================================*/
2948 /*--------------------------------------------------------------------
2949 * FileWindowMapUnmap
2950 * Update mapped flag in view lists.
2951 *------------------------------------------------------------------*/
2955 FileMgrData *file_mgr_data)
2960 for (i = 0; i < directory_count; i++)
2962 for (j = 0; j < directory_set[i]->numOfViews; j++)
2964 if (file_mgr_data == directory_set[i]->directoryView[j].file_mgr_data)
2966 directory_set[i]->directoryView[j].mapped = file_mgr_data->mapped;
2972 if (file_mgr_data->mapped && timer_suspended)
2974 XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
2975 timer_suspended = False;
2980 /*--------------------------------------------------------------------
2982 * Read a directory already cached and update its contents.
2983 *------------------------------------------------------------------*/
2989 char *directory_name )
2991 Directory *directory;
2993 DPRINTF(("RereadDirectory(%s, %s)\n", host_name, directory_name));
2995 /* Find the directory set entry. */
2996 directory = FindDirectory(host_name, directory_name);
2997 if (directory != NULL)
2999 /* reset errnum to make sure we'll get an error message */
3000 directory->errnum = 0;
3002 /* Read the directory. */
3003 if (!directory->busy[activity_reading])
3004 ReadDirectoryFiles(w, directory);
3009 /*--------------------------------------------------------------------
3011 * Check if any files were added or deleted in a directory
3012 * and update the directory contents accordingly.
3013 *------------------------------------------------------------------*/
3019 char *directory_name )
3021 Directory *directory;
3023 DPRINTF(("UpdateDirectory(%s, %s)\n", host_name, directory_name));
3025 /* Find the directory set entry. */
3026 directory = FindDirectory(host_name, directory_name);
3027 if (directory != NULL)
3029 /* arrange for directory contents to be checked */
3030 if (!directory->busy[activity_update_all])
3032 directory->busy[activity_update_all] = True;
3033 ScheduleActivity(directory);
3039 /*====================================================================
3041 * Directory modification routines:
3043 * The following routines are provided to avoid unnecessary
3044 * re-reads of whole directories. For example, if the user
3045 * renames a file, it's only necessary to remove the old file
3046 * from the directory and add it back under its new name; there
3047 * is no need to read the whole directory again. Similarly,
3048 * when a file is dropped on a directory, it's only necessary
3049 * to add the one new file to the directory.
3051 * To accomplish this, the routines that rename or copy files
3052 * make the following calls:
3054 * DirectoryBeginModify(): called before doing the operation
3055 * DirectoryFileModified(): called once for each affected file
3056 * DirectoryEndModify(): called when the operation is completed
3058 * The routines remember which files were modified, and when
3059 * DirectoryEndModify is called, a background process is started,
3060 * that re-stats and types just those files.
3062 * A complication arises from automatic re-reads triggered by
3063 * a periodic timer (routine TimerEvent). Since renaming or
3064 * copying files changes the timestamp on the directory, the
3065 * automatic re-read would re-read the whole directory soon after
3066 * the operation is done, nullifying our efforts to avoid
3067 * unnecessary re-reads. Therefore:
3069 * - We don't do any automatic re-reads between calls to
3070 * DirectoryBeginModify and DirectoryEndModify.
3072 * - If the directory timestamp hadn't changed at the time
3073 * of the DirectoryBeginModify, then when the directory
3074 * update triggered by DirectoryEndModify finishes, we
3075 * set the modify_time in the directory_set to the current
3076 * timestamp of the directory. This means that the next
3077 * automatic re-read won't be triggered unless the directory
3078 * is modified again after the DirectoryEndModify.
3080 *==================================================================*/
3082 /*--------------------------------------------------------------------
3083 * DirectoryAbortModify
3084 * Decrement the modify_begin counter.
3085 *------------------------------------------------------------------*/
3088 DirectoryAbortModify(
3090 char *directory_name)
3092 Directory *directory;
3094 DPRINTF(("DirectoryAbortModify(%s, %s)\n", host_name, directory_name));
3096 /* Find the directory set entry. */
3097 directory = FindDirectory(host_name, directory_name);
3098 if (directory != NULL)
3100 directory->modify_begin--;
3102 if (directory->modify_begin == 0)
3103 directory->was_up_to_date = True;
3105 DPRINTF((" modify_begin %d, up_to_date %d\n",
3106 directory->modify_begin, directory->was_up_to_date));
3111 /*--------------------------------------------------------------------
3112 * DirectoryBeginModify
3113 * Increment the modify_begin counter to suspend automatic
3114 * re-reads until DirectoryEndModify is called.
3115 *------------------------------------------------------------------*/
3118 DirectoryBeginModify(
3120 char *directory_name)
3122 Directory *directory;
3124 DPRINTF(("DirectoryBeginModify(%s, %s)\n", host_name, directory_name));
3126 /* Find the directory set entry. */
3127 directory = FindDirectory(host_name, directory_name);
3128 if (directory != NULL)
3130 if (directory->modify_begin == 0)
3131 /* until we know better, assume the directory changed */
3132 directory->was_up_to_date = False;
3134 /* increment the modify_begin counter */
3135 directory->modify_begin++;
3137 DPRINTF((" modify_begin %d, up_to_date %d\n",
3138 directory->modify_begin, directory->was_up_to_date));
3143 /*--------------------------------------------------------------------
3144 * DirectoryModifyTime
3145 * This routine should be called after DirectoryBeginModify and
3146 * before doing any operation on the directory. The parameter
3147 * modify_time should be the current timestamp of the directory.
3148 * By comparing the value to the modify_time stored in the
3149 * directory set we decide whether the directory had already
3150 * changed before the update operation began.
3151 * Note: the reason for supplying a separate call for this check,
3152 * instead of doing it inside DirectoryBeginModify(), is that we
3153 * want to do the stat call that determines the current timestamp
3154 * of the directory in a background process. The background
3155 * process that we start fo do the actual update is a convenient
3157 *------------------------------------------------------------------*/
3160 DirectoryModifyTime(
3162 char *directory_name,
3165 Directory *directory;
3167 DPRINTF(("DirectoryModifyTime(%s, %s)\n", host_name, directory_name));
3169 #ifdef SMART_DIR_UPDATE
3170 /* Find the directory set entry. */
3171 directory = FindDirectory(host_name, directory_name);
3173 if (directory != NULL)
3175 /* mark directory up-to-date if unchanged since last read */
3176 if (modify_time <= directory->modify_time)
3177 directory->was_up_to_date = True;
3178 DPRINTF((" modify_begin %d, up_to_date %d\n",
3179 directory->modify_begin, directory->was_up_to_date));
3185 /*--------------------------------------------------------------------
3186 * DirectoryFileModified
3187 * This routine is called when we know that a file in a directory
3188 * has been modified, added or removed. The file name is added
3189 * to the list of modified files. The next time an update
3190 * background process is started, it will check all the files
3191 * on the modfied list and update the corresponding FileData.
3192 *------------------------------------------------------------------*/
3195 DirectoryFileModified(
3197 char *directory_name,
3200 Directory *directory;
3203 DPRINTF(("DirectoryFileModified(%s, %s, %s)\n",
3204 host_name, directory_name, file_name));
3206 /* Find the directory set entry. */
3207 directory = FindDirectory(host_name, directory_name);
3208 if (directory != NULL)
3210 /* see if the file is already on the list */
3211 for( i = 0; i < directory->modified_count; ++i )
3212 if( strcmp( directory->modified_list[i], file_name ) == 0 )
3215 /* add the file to the modified_list */
3216 i = directory->modified_count++;
3217 directory->modified_list = (char **)
3218 XtRealloc((char *)directory->modified_list, (i + 1)*sizeof(char *));
3219 directory->modified_list[i] = XtNewString(file_name);
3224 /*--------------------------------------------------------------------
3225 * DirectoryEndModify
3226 * Start an update background process (will check all the files
3227 * on the modfied list and update the corresponding FileData).
3228 *------------------------------------------------------------------*/
3233 char *directory_name)
3235 Directory *directory;
3237 DPRINTF(("DirectoryEndModify(%s, %s)\n", host_name, directory_name));
3239 /* Find the directory set entry. */
3240 directory = FindDirectory(host_name, directory_name);
3242 /* arrange for an update background process to be scheduled */
3243 if (directory != NULL)
3245 directory->modify_begin--;
3246 DPRINTF((" modify_begin %d, up_to_date %d, modified_count %d\n",
3247 directory->modify_begin,
3248 directory->was_up_to_date,
3249 directory->modified_count));
3250 if (directory->modified_count > 0)
3253 char subdir_name[MAX_PATH + 1];
3258 * If any of the modifed files is a subdirectory that we have
3259 * cached, schedule an activity_checking_dir to make sure that
3260 * the subdirectory is still readable.
3262 strcpy(subdir_name, directory_name);
3263 p = subdir_name + strlen(subdir_name);
3267 for (i = 0; i < directory->modified_count; i++)
3269 strcpy(p, directory->modified_list[i]);
3270 subdir = FindDirectory(host_name, subdir_name);
3273 DPRINTF((" schedule check for subdir \"%s\"\n",
3274 directory->modified_list[i]));
3275 subdir->busy[activity_checking_dir] = True;
3276 ScheduleActivity(subdir);
3280 #ifdef SMART_DIR_UPDATE
3281 /* schedule a partial update of the modfied directory */
3282 if (directory->was_up_to_date)
3283 directory->busy[activity_update_some] = True;
3285 directory->busy[activity_update_all] = True;
3287 /* schedule a full update of the modfied directory */
3288 directory->busy[activity_update_all] = True;
3290 ScheduleActivity(directory);
3296 /*--------------------------------------------------------------------
3297 * UpdateDirectorySet
3298 * We call this when we do a database update. It loops through
3299 * the directory_set list and rereads each directory.
3300 *------------------------------------------------------------------*/
3303 UpdateDirectorySet( void )
3307 DPRINTF(("UpdateDirectorySet ...\n"));
3309 for (i = 0; i < directory_count; i++)
3310 if (!directory_set[i]->busy[activity_reading])
3311 ReadDirectoryFiles (NULL, directory_set[i]);
3316 /*--------------------------------------------------------------------
3317 * UpdateCachedDirectories
3318 * Update view list for all cached directories.
3319 * Throw out any directories that are no longer being viewed.
3320 *------------------------------------------------------------------*/
3323 UpdateCachedDirectories(
3327 DialogData * dialog_data;
3328 FileMgrData * file_mgr_data;
3330 Directory *directory;
3334 * clear the view list in all directory set entries
3336 for (i = 0; i < directory_count; i++)
3338 if( !(strcmp(directory_set[i]->directory_name, trash_dir) == 0) )
3340 XtFree ((char *) directory_set[i]->directoryView);
3341 directory_set[i]->numOfViews = 0;
3342 directory_set[i]->directoryView = NULL;
3343 directory_set[i]->viewed = False;
3350 * reconstruct view lists by adding each directory found in the view
3351 * set to the view list for the corresponding directory set entry
3353 for (j = 0; j < view_count; j++)
3355 dialog_data = (DialogData *) view_set[j]->dialog_data;
3356 file_mgr_data = (FileMgrData *) dialog_data->data;
3358 /* loop through all direcories in this view */
3359 for (k = 0; k < file_mgr_data->directory_count; k++)
3361 /* find the directory in the directory set */
3362 directory = FindDirectory(view_set[j]->host_name,
3363 file_mgr_data->directory_set[k]->name);
3365 /* we expect the directory to be found; if not, something is wrong */
3366 if (directory == NULL)
3368 fprintf(stderr, "Warning: %s:%s not found in directory set.\n",
3369 view_set[j]->host_name,
3370 file_mgr_data->directory_set[k]->name);
3374 /* add the directory to the view list */
3375 n = directory->numOfViews;
3376 directory->directoryView = (DirectoryView *)
3377 XtRealloc ((char *) directory->directoryView,
3378 sizeof(DirectoryView) * (n + 1));
3379 directory->directoryView[n].file_mgr_data = file_mgr_data;
3380 directory->directoryView[n].mapped = file_mgr_data->mapped;
3381 directory->numOfViews++;
3382 directory->viewed = True;
3389 * remove all directories that have empty view lists
3392 while (i < directory_count)
3394 if (directory_set[i]->numOfViews > 0 ||
3395 strcmp(directory_set[i]->directory_name, trash_dir) == 0)
3397 /* Keep this directory in the directory set. */
3402 /* Delete the file data and remove from the directory set. */
3404 DPRINTF(("UpdateCachedDirectories: removing %s:%s\n",
3405 directory_set[i]->host_name,
3406 directory_set[i]->directory_name));
3408 FreeDirectory(directory_set[i]);
3410 for (k = i; k < directory_count - 1; k++)
3411 directory_set[k] = directory_set[k + 1];
3417 /* Restart refresh timer, if necessary */
3418 if (timer_suspended && SomeWindowMapped())
3420 XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
3421 timer_suspended = False;
3426 /*====================================================================
3428 * Routines that return directory data
3430 *==================================================================*/
3432 /*--------------------------------------------------------------------
3434 * Return a string that contains file information similar to "ls -l",
3435 * including: permissions, owner, modified time, link (if any).
3436 * Used for "view by attributes"
3439 * -rw-r--r-- dld staff 108314 Jul 26 15:16:36 1993 Directory.c
3441 *------------------------------------------------------------------*/
3445 FileData *file_data )
3449 char time_string[100];
3453 char link_path[MAX_PATH + 5];
3454 static gid_t group_id = (gid_t)-1;
3455 static uid_t user_id = (uid_t)-1;
3456 struct group * group_data;
3457 struct passwd * user_data;
3458 static char group_name[20];
3459 static char user_name[20];
3461 time_t long_modify_time;
3463 char usr_read_priv, usr_write_priv, usr_exec_priv;
3464 char grp_read_priv, grp_write_priv, grp_exec_priv;
3465 char oth_read_priv, oth_write_priv, oth_exec_priv;
3467 /* Generate the long list name. */
3468 long_name = (char *) XtMalloc(sizeof(char) * (MAX_PATH * 3));
3471 /* Initially, assume their is not a soft link */
3472 link_path[0] = '\0';
3474 if (file_data->errnum == 0)
3476 if (file_data->stat.st_gid != group_id)
3478 group_id = file_data->stat.st_gid;
3479 group_data = getgrgid (file_data->stat.st_gid);
3482 strcpy (group_name, group_data->gr_name);
3483 if (strlen (group_name) == 0)
3484 strcpy (group_name, "root");
3487 strcpy (group_name, "root");
3490 if (file_data->stat.st_uid != user_id)
3492 user_id = file_data->stat.st_uid;
3493 user_data = getpwuid (file_data->stat.st_uid);
3494 /* Initially, assume their is not a user name */
3495 user_name[0] = '\0';
3497 strcpy (user_name, user_data->pw_name);
3499 sprintf(user_name,"%ld",(long)user_id);
3504 char error_msg[1024];
3507 /* determine how much space we have for an error message */
3508 long_modify_time = 747616435;
3509 /* just needed to determine the length of a date */
3511 tms = localtime(&long_modify_time);
3512 strftime( time_string, 100,
3513 GetSharedMessage(DIRECTORY_DATE_FORMAT),
3516 time_string = ctime ((time_t *)&long_modify_time);
3517 time_string[strlen(time_string)-1] = 0x0;
3520 msg_len = 10 + 3 + 9 + 1 + 9 + 1 + 9 + 1 + strlen(time_string);
3522 /* generate the error message */
3523 strcpy(error_msg, "(");
3524 strncpy(error_msg + 1, strerror(file_data->errnum), msg_len - 2);
3525 error_msg[msg_len - 1] = '\0';
3526 strcat(error_msg, ")");
3528 sprintf( long_name, "%-28.28s %s %9d %s",
3529 file_data->file_name,
3537 /* Build the permission string */
3538 switch( file_data->stat.st_mode & S_IFMT )
3553 permission = OPTION_OFF;
3557 if (file_data->stat.st_mode & S_IRUSR) usr_read_priv = READ_PRIV;
3558 else usr_read_priv = OPTION_OFF;
3560 if (file_data->stat.st_mode & S_IWUSR) usr_write_priv = WRITE_PRIV;
3561 else usr_write_priv = OPTION_OFF;
3563 if (file_data->stat.st_mode & S_IXUSR) usr_exec_priv = EXEC_PRIV;
3564 else usr_exec_priv = OPTION_OFF;
3567 if (file_data->stat.st_mode & S_IRGRP) grp_read_priv = READ_PRIV;
3568 else grp_read_priv = OPTION_OFF;
3570 if (file_data->stat.st_mode & S_IWGRP) grp_write_priv = WRITE_PRIV;
3571 else grp_write_priv = OPTION_OFF;
3573 if (file_data->stat.st_mode & S_IXGRP) grp_exec_priv = EXEC_PRIV;
3574 else grp_exec_priv = OPTION_OFF;
3577 if (file_data->stat.st_mode & S_IROTH) oth_read_priv = READ_PRIV;
3578 else oth_read_priv = OPTION_OFF;
3580 if (file_data->stat.st_mode & S_IWOTH) oth_write_priv = WRITE_PRIV;
3581 else oth_write_priv = OPTION_OFF;
3583 if (file_data->stat.st_mode & S_IXOTH) oth_exec_priv = EXEC_PRIV;
3584 else oth_exec_priv = OPTION_OFF;
3587 long_modify_time = file_data->stat.st_mtime;
3589 tms = localtime(&long_modify_time);
3590 strftime( time_string, 100,
3591 GetSharedMessage(DIRECTORY_DATE_FORMAT),
3594 time_string = ctime ((time_t *)&long_modify_time);
3595 time_string[strlen(time_string)-1] = 0x0;
3599 /* Fill in the name of where the link goes */
3600 if (file_data->link)
3602 strcpy( link_path, " -> " );
3603 strcpy( link_path + 4, file_data->link );
3607 #define ELLIPSIS " (...) "
3608 #define NAME_PRECISION 28
3613 int len = strlen( file_data->file_name );
3614 if( len > NAME_PRECISION )
3617 char name[NAME_PRECISION];
3618 sprintf( name, "%-20.20s%s", file_data->file_name, ELLIPSIS);
3620 sprintf( long_name, "%-28.28s %s %9ld %c%c%c%c%c%c%c%c%c%c %-9s %-9s %s",
3623 (long)file_data->stat.st_size,
3625 usr_read_priv, usr_write_priv, usr_exec_priv,
3626 grp_read_priv, grp_write_priv, grp_exec_priv,
3627 oth_read_priv, oth_write_priv, oth_exec_priv,
3628 user_name, group_name,
3633 sprintf( long_name, "%-28.28s %s %9ld %c%c%c%c%c%c%c%c%c%c %-9s %-9s %s",
3634 file_data->file_name,
3636 (long)file_data->stat.st_size,
3638 usr_read_priv, usr_write_priv, usr_exec_priv,
3639 grp_read_priv, grp_write_priv, grp_exec_priv,
3640 oth_read_priv, oth_write_priv, oth_exec_priv,
3641 user_name, group_name,
3648 * sprintf() counts width in bytes (not characters), moreover,
3649 * it fails (returns -1 and produces no output) if input string is not
3650 * a valid multibyte string (at least the glibc version), but we can't fail
3651 * to display a file because it's name has some invalid characters). So it looks
3652 * that instead of using sprintf() we have to format the file name part manually.
3654 int len = DtCharCount( file_data->file_name );
3655 int copy_len = len > NAME_PRECISION ? NAME_PRECISION - sizeof(ELLIPSIS) + 1 : len;
3660 /* properly copy copy_len characters of the multibyte string
3661 replacing invalid chars with '?' */
3663 (count < copy_len) && *(file_data->file_name + byte_len);
3666 int chr_bytes = mblen(file_data->file_name + byte_len, MB_CUR_MAX);
3669 strncpy(long_name + byte_len, file_data->file_name + byte_len, chr_bytes);
3671 else if (chr_bytes < 0)
3672 { /* invalid char */
3674 long_name[byte_len]='?';
3678 /* null-wide character, won't really happen */
3681 byte_len+=chr_bytes;
3685 /* truncated name, add ellipsis */
3686 strncpy(long_name + byte_len, ELLIPSIS, sizeof(ELLIPSIS) - 1);
3687 byte_len+= sizeof(ELLIPSIS) - 1;
3691 /* full name, pad it with spaces up to the proper length */
3692 for (; count < NAME_PRECISION ; count++)
3694 long_name[byte_len++]=' ';
3697 sprintf( long_name + byte_len, " %s %9ld %c%c%c%c%c%c%c%c%c%c %-9s %-9s %s",
3699 (long)file_data->stat.st_size,
3701 usr_read_priv, usr_write_priv, usr_exec_priv,
3702 grp_read_priv, grp_write_priv, grp_exec_priv,
3703 oth_read_priv, oth_write_priv, oth_exec_priv,
3704 user_name, group_name,
3706 } /* is_multibyte */
3707 #endif /* MULTIBYTE */
3715 /*--------------------------------------------------------------------
3717 * See if path has a directory view of it or if any sub-directories
3718 * of it are viewed. The path parameter is of the for /foo/bar
3719 *------------------------------------------------------------------*/
3725 FileMgrData * file_mgr_data;
3726 FileViewData * sub_root;
3728 int len = strlen(path);
3730 for (i = 0; i < directory_count; i++)
3732 /* check if this directory is equal to 'path' or a subdir of 'path' */
3733 if (directory_set[i]->viewed &&
3734 (strcmp (directory_set[i]->path_name, path) == 0 ||
3735 strncmp (directory_set[i]->path_name, path,len) == 0 &&
3736 directory_set[i]->path_name[len] == '/'
3738 directory_set[i]->tt_path_name != NULL &&
3739 (strcmp (directory_set[i]->tt_path_name, path) == 0 ||
3740 strncmp (directory_set[i]->tt_path_name, path,len) == 0 &&
3741 directory_set[i]->tt_path_name[len] == '/')))
3743 /* check the views in the view list */
3744 for (j = 0; j < directory_set[i]->numOfViews; j++)
3746 file_mgr_data = directory_set[i]->directoryView[j].file_mgr_data;
3748 /* find the dir in the directory set for this view */
3749 for (k = 0; k < file_mgr_data->directory_count; k++)
3750 if (strcmp(file_mgr_data->directory_set[k]->name,
3751 directory_set[i]->directory_name) == 0)
3755 if (k == file_mgr_data->directory_count)
3756 continue; /* not found ... something must be wrong! */
3759 * Check if this directory is acutally visible.
3760 * If the directory is in a tree branch that is not currently
3761 * expanded, it is not visible and would not be considered busy.
3764 /* the tree root is always considered busy */
3768 /* a subdir is considered busy if it is visible and at least
3769 * partially expanded */
3770 sub_root = file_mgr_data->directory_set[k]->sub_root;
3771 if (sub_root->displayed &&
3772 (sub_root->ts == tsDirs && sub_root->ndir > 0 ||
3773 sub_root->ts == tsAll &&
3774 sub_root->ndir + sub_root->nfile > 0))
3786 /*--------------------------------------------------------------------
3787 * GetDirectoryLogicalType
3788 * Get logical type for the iconic path.
3789 *------------------------------------------------------------------*/
3792 GetDirectoryLogicalType(
3793 FileMgrData *file_mgr_data,
3798 Directory *directory;
3801 /* 'path' must be a prefix of the current directory */
3803 if (strncmp(file_mgr_data->current_directory, path, len) != 0 ||
3805 file_mgr_data->current_directory[len] != '/' &&
3806 file_mgr_data->current_directory[len] != '\0'))
3808 DPRINTF(("GetDirectoryLogicalType(%s): len %d, cur_dir %s\n",
3809 path, len, file_mgr_data->current_directory));
3813 /* Find the directory set entry. */
3814 directory = FindDirectory(file_mgr_data->host,
3815 file_mgr_data->current_directory);
3816 if ((directory != NULL) &&
3817 (strcmp(file_mgr_data->current_directory,
3818 directory->directory_name) == 0))
3820 /* if we don't have path_logical_types yet, we don't know */
3821 if (directory->path_logical_types == NULL)
3824 /* count the number of components in path */
3825 if (strcmp(path, "/") == 0)
3831 while ((ptr = DtStrchr(ptr, '/')) != NULL)
3841 DPRINTF2(("GetDirectoryLogicalType(%s): n %d, type %s\n",
3842 path, n, directory->path_logical_types[n]));
3844 /* return type form path_logical_types array */
3845 return directory->path_logical_types[n];
3848 /* directory not found in directory_set */
3854 /*====================================================================
3856 * Routines for accessing position information
3858 *==================================================================*/
3860 /*--------------------------------------------------------------------
3861 * GetDirectoryPositionInfo
3862 * Get cached position info
3863 *------------------------------------------------------------------*/
3866 GetDirectoryPositionInfo(
3868 char *directory_name,
3869 PositionInfo **position_info)
3871 Directory *directory;
3873 directory = FindDirectory(host_name, directory_name);
3874 if (directory == NULL)
3877 *position_info = directory->position_info;
3879 return directory->position_count;
3883 /*--------------------------------------------------------------------
3884 * WritePosInfoProcess
3885 * Main routine of the background process that writes the
3886 * postion information file.
3887 *------------------------------------------------------------------*/
3890 WritePosInfoProcess(
3892 Directory *directory,
3893 ActivityStatus activity)
3896 int position_count = directory->position_count;
3897 PositionInfo *position_info = directory->position_info;
3900 Tt_status tt_status;
3902 /* construct the full file name */
3903 fileName = ResolveLocalPathName( directory->host_name,
3904 directory->directory_name, positionFileName,
3905 home_host_name, &tt_status );
3906 /* Don't have to check for tt_status
3907 directory->host_name is home_host_name and ResolveLocalPathName will
3908 always return a good path
3910 DPRINTF(("WritePosInfoProcess: count %d, file %s\n",
3911 position_count, fileName));
3913 /* Remove old files, if no position information for this view */
3914 if (position_count <= 0)
3915 rc = unlink(fileName);
3918 /* open the file for writing */
3919 f = fopen(fileName, "w");
3923 /* Assume read-only directory, if we can't open the file */
3928 chmod(fileName, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
3930 fprintf(f, "%d\n", position_count);
3931 for (i = 0; i < position_count; i++)
3933 fprintf(f, "%s\n%d %d %d\n",
3934 position_info[i].name,
3937 position_info[i].stacking_order);
3945 /* send result back thorugh the pipe */
3946 DPRINTF(("WritePosInfoProcess: done (rc %d)\n", rc));
3947 write(pipe_fd, &rc, sizeof(int));
3953 /*--------------------------------------------------------------------
3954 * WritePosInfoPipeCallback
3955 * Callback routine that reads the return code sent through the
3956 * pipe from the WritePosInfoProcess background process.
3957 *------------------------------------------------------------------*/
3960 WritePosInfoPipeCallback(
3961 XtPointer client_data,
3965 PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
3966 Directory *directory = pipe_data->directory;
3970 /* get return code from the pipe */
3972 PipeRead(*fd, &rc, sizeof(int));
3974 /* close the pipe and cancel the callback */
3976 XtFree( client_data );
3979 /* verify that the directory still exists */
3980 if (DirectoryGone(directory))
3982 ScheduleActivity(NULL);
3986 DPRINTF(("WritePosInfoPipeCallback: rc %d\n", rc));
3988 /* reset the busy flag and schedule new work, if any */
3989 directory->busy[activity_writing_posinfo] = False;
3990 directory->activity = activity_idle;
3991 ScheduleActivity(directory);
3995 /*--------------------------------------------------------------------
3996 * SetDirectoryPositionInfo
3997 * Update cached position info. This routine schedules a
3998 * background process that writes the modified information
3999 * to the position info file.
4000 *------------------------------------------------------------------*/
4003 SetDirectoryPositionInfo(
4005 char *directory_name,
4007 PositionInfo *position_info)
4009 Directory *directory;
4013 /* find the directory */
4014 directory = FindDirectory(host_name, directory_name);
4015 if (directory == NULL)
4018 /* check if anything has changed */
4019 if (directory->position_count == position_count)
4022 for (i = 0; i < position_count && unchanged; i++)
4024 for (j = 0; j < position_count; j++)
4025 if (strcmp(position_info[i].name,
4026 directory->position_info[j].name) == 0)
4031 if (j == position_count ||
4032 position_info[i].x != directory->position_info[j].x ||
4033 position_info[i].y != directory->position_info[j].y ||
4034 position_info[i].stacking_order !=
4035 directory->position_info[j].stacking_order)
4041 /* if nothing changed, don't do anything */
4046 /* free old position info names*/
4047 for (i = 0; i < directory->position_count; i++)
4048 XtFree(directory->position_info[i].name);
4050 /* realloc array, if necessary */
4051 if (directory->position_count != position_count)
4053 directory->position_count = position_count;
4054 directory->position_info =
4055 (PositionInfo *) XtRealloc((char *)directory->position_info,
4056 position_count * sizeof(PositionInfo));
4059 /* replace old position info */
4060 for (i = 0; i < position_count; i++)
4062 directory->position_info[i].name = XtNewString(position_info[i].name);
4063 directory->position_info[i].x = position_info[i].x;
4064 directory->position_info[i].y = position_info[i].y;
4065 directory->position_info[i].stacking_order =
4066 position_info[i].stacking_order;
4069 /* make sure positionFileName is initialized */
4070 if (positionFileName == NULL)
4071 InitializePositionFileName();
4073 /* start background process that writes the position info file */
4074 directory->busy[activity_writing_posinfo] = True;
4075 ScheduleActivity(directory);
4081 /*====================================================================
4084 * These function are periodically called to scan all cached
4085 * directories to see if any have been modified since last read.
4086 * If any have, a background process is scheduled to re-read
4089 *==================================================================*/
4091 /*--------------------------------------------------------------------
4093 * Decide whether to skip an automatic re-read.
4094 * (We don't do re-reads on directories that aren't currently
4095 * being viewed and on the trash directory, if trash is currently
4097 *------------------------------------------------------------------*/
4101 Directory *directory)
4105 /* don't refresh while the directory is being modified */
4106 if (directory->modify_begin > 0)
4109 /* verify that the directory is still being viewed */
4110 if (!directory->viewed)
4113 for (i = 0; i < directory->numOfViews; i++)
4114 if (directory->directoryView[i].mapped)
4116 if (i == directory->numOfViews)
4119 /* if trash is being emptied and this is the trash dir, skip it */
4120 if (removingTrash && strcmp(directory->directory_name, trash_dir) == 0)
4127 /*--------------------------------------------------------------------
4129 * Main routine for the background process that checks directory
4130 * timestamps and the status of links.
4131 *------------------------------------------------------------------*/
4136 Directory *directory,
4137 ActivityStatus activity)
4139 struct stat stat_buf;
4141 Boolean link_changed;
4143 FileData *file_data;
4144 char full_name[MAX_PATH];
4153 * Do a stat on the directory to get its last-modified time.
4154 * Also check if we still have read and execute/search permisssion.
4157 * It is important to get the timstamp in exactly the same way that the
4158 * ReadDirectoryProcess does it; otherwise, we might get into a loop,
4159 * where TimerEventProcess detects that the directory has changed
4160 * and triggers ReadDirectoryProcess, but ReadDirectoryProcess won't
4161 * be able to get a new timestamp and update the directory structure,
4162 * so the next time TimerEventProcess runs it will trigger another
4163 * ReadDirectoryProcess, and so on ...
4165 if (CheckAccess(directory->path_name, R_OK | X_OK) != 0 ||
4166 stat(directory->path_name, &stat_buf) != 0)
4168 /* stat or access failed */
4174 /* stat succeeded and the directory is still readable */
4176 modify_time = stat_buf.st_mtime;
4180 * If requested, also check if any links broken.
4182 * Again: it is important that we determine the kind of link
4183 * (valid, recursive, or broken) in exactly the same way that
4184 * ReadDirectoryProcess does it (see comment above)!
4186 link_changed = False;
4187 if (rc == 0 && activity == activity_checking_links)
4189 strcpy(full_name, directory->path_name);
4190 namep = full_name + strlen(full_name);
4191 if (namep[-1] != '/')
4194 /* check all links */
4195 for (file_data = directory->file_data;
4196 file_data && !link_changed;
4197 file_data = file_data->next)
4199 /* Only worry about links */
4200 if (file_data->link == NULL)
4203 /* Check if the file is still a symbolic link */
4204 strcpy(namep, file_data->file_name);
4205 link_rc = lstat(full_name, &stat_buf);
4206 if (link_rc != 0 || (stat_buf.st_mode & S_IFMT) != S_IFLNK)
4208 /* no longer a link */
4209 link_changed = True;
4213 /* Check what kind of link this was the last time we looked:
4214 * a normal link (1), a recursive link (2), or an otherwise
4215 * broken link (3) */
4216 if (strcmp(file_data->logical_type, LT_BROKEN_LINK) == 0)
4218 else if (strcmp(file_data->logical_type, LT_RECURSIVE_LINK) == 0)
4223 /* Check what kind of link it is now */
4224 if (_DtFollowLink(full_name) == NULL)
4225 cur_link_kind = 2; /* recursive link */
4226 else if (stat(full_name, &stat_buf) != 0)
4227 cur_link_kind = 3; /* broken link */
4229 cur_link_kind = 1; /* a valid link */
4231 /* now we can tell if the link has changed */
4232 if (prev_link_kind != cur_link_kind)
4233 link_changed = True;
4237 /* send result back through the pipe */
4238 write(pipe_fd, &rc, sizeof(int));
4239 write(pipe_fd, &modify_time, sizeof(long));
4240 write(pipe_fd, &link_changed, sizeof(Boolean));
4245 /*--------------------------------------------------------------------
4247 * Mark sticky background process as idle. If there are too
4248 * may idle sticky procs, cause some of them to exit.
4249 *------------------------------------------------------------------*/
4253 ActivityStatus activity,
4254 StickyProcDesc *sticky_proc,
4257 StickyProcDesc *p, **lp;
4260 /* mark the process as idle */
4261 sticky_proc->idle = True;
4263 /* if there are too many idle procs, make some of them go away */
4265 lp = &ActivityTable[activity].sticky_procs;
4266 for (p = *lp; p; p = *lp)
4270 else if (n < max_procs)
4277 DPRINTF2(("StickyProcIdle: end sticky proc %ld\n", (long)p->child));
4278 PipeWriteString(p->pipe_m2s_fd, NULL);
4279 close(p->pipe_s2m_fd);
4280 close(p->pipe_m2s_fd);
4288 /*--------------------------------------------------------------------
4290 * Callback routine that reads information sent through the
4291 * pipe from the TimerEventProcess background process.
4292 *------------------------------------------------------------------*/
4296 XtPointer client_data,
4300 PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
4301 Directory *directory = pipe_data->directory;
4304 Boolean link_changed;
4307 /* get return code from the pipe */
4309 PipeRead(*fd, &rc, sizeof(int));
4310 PipeRead(*fd, &modify_time, sizeof(long));
4311 PipeRead(*fd, &link_changed, sizeof(Boolean));
4313 /* close the pipe and cancel the callback */
4314 if (pipe_data->sticky_proc)
4315 StickyProcIdle(pipe_data->activity, pipe_data->sticky_proc,
4316 maxRereadProcsPerTick);
4319 XtFree( client_data );
4322 /* verify that the directory still exists */
4323 if (DirectoryGone(directory))
4325 ScheduleActivity(NULL);
4330 "TimerPipeCallback: rc %d (was %d), time %ld (was %ld), link change %d\n",
4331 rc, directory->errnum, modify_time, (long)directory->modify_time, link_changed));
4333 /* reset the busy flag and schedule new work, if any */
4334 directory->busy[directory->activity] = False;
4335 directory->activity = activity_idle;
4336 ScheduleActivity(directory);
4338 /* if directory-read already in progress, nothing more to do here */
4339 if (directory->busy[activity_reading] ||
4340 directory->busy[activity_update_all])
4343 /* skip this directory if it is no longer being viewed */
4344 if (SkipRefresh(directory))
4347 /* if the directory was modified or links changed, re-read it */
4352 DPRINTF(("TimerPipeCallback: %s link changed\n",
4353 directory->directory_name));
4354 ReadDirectoryFiles(NULL, directory);
4356 else if (modify_time != directory->modify_time || directory->errnum != 0)
4358 DPRINTF(("TimerPipeCallback: %s modified\n",
4359 directory->directory_name));
4360 directory->busy[activity_update_all] = True;
4361 ScheduleActivity(directory);
4366 if (directory->errnum == 0)
4368 directory->errnum = rc;
4369 directory->errmsg_needed = True;
4370 ReadDirectoryFiles(NULL, directory);
4376 /*--------------------------------------------------------------------
4377 * CheckDesktopProcess
4378 * Main routine for the background process that checks each desktop
4379 * objects to see if the file that it refers to has disappeared
4380 * or has changed type.
4381 *------------------------------------------------------------------*/
4384 CheckDesktopProcess(
4386 Directory *directory,
4387 ActivityStatus activity)
4390 DesktopRec *desktopWindow;
4391 FileViewData *file_view_data;
4393 Tt_status tt_status;
4394 struct stat stat_buf;
4396 FileData2 file_data2;
4397 FileData *old_data, *new_data;
4399 for (i = 0; i < desktop_data->numIconsUsed; i++)
4401 desktopWindow = desktop_data->desktopWindows[i];
4402 file_view_data = desktopWindow->file_view_data;
4404 full_path = ResolveLocalPathName( desktopWindow->host,
4405 desktopWindow->dir_linked_to,
4406 desktopWindow->file_name,
4407 home_host_name, &tt_status);
4409 /* Check if the file still exists */
4411 if (lstat(full_path, &stat_buf) < 0)
4413 /* the real file no longer exists */
4415 "CheckDesktopProcess: sending PIPEMSG_DESKTOP_REMOVED for %s\n",
4417 pipe_msg = PIPEMSG_DESKTOP_REMOVED;
4418 write(pipe_fd, &pipe_msg, sizeof(short));
4419 PipeWriteString(pipe_fd, desktopWindow->host);
4420 PipeWriteString(pipe_fd, desktopWindow->dir_linked_to);
4421 PipeWriteString(pipe_fd, desktopWindow->file_name);
4426 /* See if the type has changed */
4427 old_data = file_view_data->file_data;
4429 if(directory->directoryView && directory->directoryView->file_mgr_data)
4430 IsToolBox = directory->directoryView->file_mgr_data->toolbox;
4434 ReadFileData2(&file_data2, full_path, NULL,IsToolBox);
4435 new_data = FileData2toFileData(&file_data2, &n);
4437 if (new_data->physical_type != old_data->physical_type ||
4438 strcmp(new_data->logical_type, old_data->logical_type) != 0)
4440 /* the type has changed */
4442 "CheckDesktopProcess: sending PIPEMSG_DESKTOP_CHANGED for %s\n",
4445 " old type %d %s, new type %d %s\n",
4446 old_data->physical_type, old_data->logical_type,
4447 new_data->physical_type, new_data->logical_type));
4449 pipe_msg = PIPEMSG_DESKTOP_CHANGED;
4450 write(pipe_fd, &pipe_msg, sizeof(short));
4451 PipeWriteString(pipe_fd, desktopWindow->host);
4452 PipeWriteString(pipe_fd, desktopWindow->dir_linked_to);
4453 PipeWriteString(pipe_fd, desktopWindow->file_name);
4455 PipeWriteFileData(pipe_fd, new_data);
4458 FreeFileData(new_data, True);
4464 /* send a 'done' msg through the pipe */
4465 DPRINTF2(("CheckDesktopProcess: sending DONE\n"));
4466 pipe_msg = PIPEMSG_DONE;
4467 write(pipe_fd, &pipe_msg, sizeof(short));
4472 /*--------------------------------------------------------------------
4473 * CheckDesktopPipeCallback
4474 * Callback routine that reads information sent through the
4475 * pipe from the CheckDesktopProcess background process.
4476 *------------------------------------------------------------------*/
4479 CheckDesktopPipeCallback(
4480 XtPointer client_data,
4484 PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
4485 Directory *directory = pipe_data->directory;
4487 char *host, *dir_linked_to, *file_name;
4488 FileData *new_data, *old_data;
4490 DesktopRec *desktopWindow;
4493 /* read the next msg from the pipe */
4495 n = PipeRead(*fd, &msg, sizeof(short));
4497 if (msg == PIPEMSG_DESKTOP_REMOVED ||
4498 msg == PIPEMSG_DESKTOP_CHANGED)
4500 /* get information from pipe */
4501 host = PipeReadString(*fd);
4502 dir_linked_to = PipeReadString(*fd);
4503 file_name = PipeReadString(*fd);
4504 if (msg == PIPEMSG_DESKTOP_CHANGED)
4505 new_data = PipeReadFileData(*fd);
4510 "CheckDesktopPipeCallback: msg %d: host %s, dir %s, name %s\n",
4511 msg, host, dir_linked_to, file_name));
4514 /* find the desktop object */
4516 for (i = 0; i < desktop_data->numIconsUsed; i++)
4518 desktopWindow = desktop_data->desktopWindows[i];
4520 if (strcmp(host, desktopWindow->host) == 0 &&
4521 strcmp(dir_linked_to, desktopWindow->dir_linked_to) == 0 &&
4522 strcmp(file_name, desktopWindow->file_name) == 0)
4529 /* remove or update the desktop object, if found */
4534 else if (msg == PIPEMSG_DESKTOP_REMOVED)
4536 /* remove the desktop object */
4537 DesktopObjectRemoved(desktopWindow);
4539 else /* msg == PIPEMSG_DESKTOP_CHANGED */
4541 /* replace file data */
4542 old_data = desktopWindow->file_view_data->file_data;
4543 FreeFileData(old_data, False);
4544 memcpy(old_data, new_data, sizeof(FileData));
4545 XtFree((char *)new_data);
4548 /* update the desktop object */
4549 DesktopObjectChanged(desktopWindow);
4554 XtFree(dir_linked_to);
4557 FreeFileData(new_data, True);
4560 else if (msg == PIPEMSG_DONE)
4562 /* close the pipe and cancel the callback */
4564 XtFree( client_data );
4567 /* reset the busy flag and schedule new work, if any */
4568 directory->busy[directory->activity] = False;
4569 directory->activity = activity_idle;
4570 ScheduleActivity(directory);
4575 /*--------------------------------------------------------------------
4578 * Arrange for a CheckDesktopProcess background process to be
4579 * started (checks each desktop objects to see if the file that
4580 * it refers to has disappeared or has changed type).
4582 *------------------------------------------------------------------*/
4585 CheckDesktop( void )
4587 dummy_directory->busy[activity_checking_desktop] = True;
4588 ScheduleActivity(dummy_directory);
4592 /*--------------------------------------------------------------------
4594 * This routine is called periodically. It schedules a
4595 * TimerEventProcess background process to be started for every
4596 * directory in the cache.
4597 *------------------------------------------------------------------*/
4599 /* comparison routine for qsort */
4605 return directory_set[*p1]->last_check - directory_set[*p2]->last_check;
4611 XtPointer client_data,
4614 static int *check_list = NULL;
4615 static int check_alloc = 0;
4619 DPRINTF2(("Directory::TimerEvent\n"));
4624 * Don't change any directories while a drag is active.
4626 * Reason: drag callbacks are called with a pointer to a FileViewData
4627 * structure; if a directory is reread while a drag is active,
4628 * the pointer would become invalid, causing unpredictable behavior.
4630 * Schedule the next TimerEvent in 1/2 second, so that check will
4631 * be done soon after the drag is finished.
4633 XtAppAddTimeOut (app_context, 500, TimerEvent, NULL);
4637 /* update tick count */
4640 /* determine if we should also check for broken links this time */
4641 if (checkBrokenLink > 0 &&
4642 tick_count >= lastLinkCheckTick + ticksBetweenLinkChecks)
4644 /* set link_check_needed flag on all directores */
4645 for (i = 0; i < directory_count; i++)
4647 /* skip this directory if no view is mapped */
4648 if (SkipRefresh(directory_set[i]))
4651 /* if a check is already in progress, don't start another one */
4652 if (directory_set[i]->busy[activity_checking_links])
4655 /* arrange for background process to be scheduled */
4656 directory_set[i]->link_check_needed = True;
4659 lastLinkCheckTick = tick_count;
4662 /* make sure check_list array is big enough */
4663 if (directory_count > check_alloc)
4665 check_alloc = directory_count + 5;
4667 (int *)XtRealloc((char *)check_list, check_alloc*sizeof(int));
4670 /* get a list of all directories that need to be checked */
4672 for (i = 0; i < directory_count; i++)
4674 /* skip this directory if no view is mapped */
4675 if (SkipRefresh(directory_set[i]))
4678 /* if a stat is already in progress, don't start another one */
4679 if (directory_set[i]->busy[activity_checking_dir] ||
4680 directory_set[i]->busy[activity_checking_links])
4683 /* add this directory to the check list */
4684 check_list[n++] = i;
4688 * Next we want to schedule a background process to be started
4689 * for each directory in the check_list. However, the variable
4690 * maxRereadProcsPerTick puts a limit on the number of such
4691 * background processes started per clock tick (i.e., per call
4692 * to this routine). Hence we sort check_list by last_check
4693 * (records the tick count when a directory was last read or
4694 * checked) and schedule backround processes on those dirs that
4695 * haven't been checked in the longest time.
4697 qsort(check_list, n, sizeof(int), (int (*)())CheckListCmp);
4699 /* arrange for background process to be started */
4700 for (j = 0; j < n && j < maxRereadProcsPerTick; j++)
4703 if (directory_set[i]->link_check_needed)
4705 directory_set[i]->link_check_needed = False;
4706 directory_set[i]->busy[activity_checking_links] = True;
4709 directory_set[i]->busy[activity_checking_dir] = True;
4710 ScheduleActivity(directory_set[i]);
4711 directory_set[i]->last_check = tick_count;
4714 /* Reset the timeout for the next interval. */
4715 if (SomeWindowMapped())
4716 XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
4718 timer_suspended = True;
4722 /*--------------------------------------------------------------------
4723 * TimerEventBrokenLinks
4724 * This routine is called periodically. It checks whether any
4725 * desktop object is broken (i.e., the object it refers to no
4727 *------------------------------------------------------------------*/
4730 TimerEventBrokenLinks(
4731 XtPointer client_data,
4736 DPRINTF2(("Directory::TimerEventBrokenLinks\n"));
4740 /* go check the desktop objects */
4741 if (desktop_data->numIconsUsed > 0)
4745 /* Reset the timeout for the next interval. */
4746 if (desktop_data->numIconsUsed > 0)
4748 checkBrokenLinkTimerId = XtAppAddTimeOut( app_context,
4749 checkBrokenLink * 1000,
4750 TimerEventBrokenLinks,
4755 checkBrokenLinkTimerId = None;
4760 /*====================================================================
4762 * Background process scheduler
4764 * The routines below schedule background activity, making sure
4765 * that there aren't too many processes running at the same time.
4767 *==================================================================*/
4769 /*--------------------------------------------------------------------
4770 * ScheduleDirectoryActivity
4771 * If there is any work to do for a directory, and if there is
4772 * no backgroud process currently running for that directory,
4773 * then fork a process to do the work.
4774 *------------------------------------------------------------------*/
4777 ScheduleDirectoryActivity(
4778 Directory *directory)
4780 static char *pname = "ScheduleActivity";
4781 ActivityStatus activity;
4782 PipeCallbackData *pipe_data;
4783 Boolean all_views_active;
4784 Boolean this_view_active;
4786 int n_active, n_checking;
4787 int save_last_check;
4788 FileMgrData *file_mgr_data;
4791 int pipe_s2m_fd[2]; /* for msgs from backgroundnd proc (slave to master) */
4792 int pipe_m2s_fd[2]; /* for msgs to backgroundnd proc (master to slave) */
4797 /* If already active, don't start anything new. */
4798 if (directory->activity != activity_idle)
4801 /* Decide what to do next */
4802 for (activity = 0; activity < activity_idle; activity++)
4803 if (directory->busy[activity])
4806 /* If nothing to do, return */
4807 if (activity == activity_idle)
4810 DPRINTF2(("ScheduleActivity: activity %d, busy %c%c%c%c%c%c%c, dir %s\n",
4811 directory->activity,
4812 directory->busy[activity_writing_posinfo]? 'W': '-',
4813 directory->busy[activity_reading]? 'R': '-',
4814 directory->busy[activity_update_all]? 'A': '-',
4815 directory->busy[activity_update_some]? 'U': '-',
4816 directory->busy[activity_checking_links]? 'B': '-',
4817 directory->busy[activity_checking_desktop]? 'D': '-',
4818 directory->busy[activity_checking_dir]? 'C': '-',
4819 directory->directory_name));
4821 /* Don't start more than a certain number of background processed */
4824 for (j = 0; j < directory_count; j++)
4826 if (directory_set[j]->activity != activity_idle)
4828 if (directory_set[j]->activity == activity_checking_links ||
4829 directory_set[j]->activity == activity_checking_dir)
4832 if (dummy_directory->activity != activity_idle)
4837 if (n_active >= maxDirectoryProcesses ||
4838 n_checking >= maxRereadProcesses)
4840 DPRINTF2(("ScheduleActivity: too many processes\n"));
4845 * We don't want to start more than one background process per view.
4846 * In tree mode one view may show more than one directory.
4847 * Hence we go through the view list for this directory and for each
4848 * view, we check if the same view appears on the view list of some
4849 * other directory that currently has active background activity.
4850 * If all vies on this directory have other activity, then we won't
4851 * start anything new.
4853 if (directory->numOfViews > 0)
4855 all_views_active = True;
4856 for (i = 0; i < directory->numOfViews; i++)
4858 /* get file_mgr_data for this view */
4859 file_mgr_data = directory->directoryView[i].file_mgr_data;
4861 /* see if the same view appears in the view list of a non-idle dir */
4862 this_view_active = False;
4863 for (j = 0; j < directory_count && !this_view_active; j++)
4865 /* we are only interested in directories that are not idle */
4866 if (directory_set[j]->activity == activity_idle)
4869 /* see if the view appears in the view list */
4870 for (k = 0; k < directory_set[j]->numOfViews; k++)
4872 if (directory_set[j]->directoryView[k].file_mgr_data ==
4875 this_view_active = True;
4881 if (!this_view_active)
4883 all_views_active = False;
4888 if (all_views_active)
4890 DPRINTF2(("ScheduleActivity: all views busy\n"));
4895 /* now we are ready to start the next activity */
4896 directory->activity = activity;
4897 if (activity == activity_reading ||
4898 activity == activity_update_all ||
4899 activity == activity_checking_dir ||
4900 activity == activity_checking_links)
4902 save_last_check = directory->last_check;
4903 directory->last_check = tick_count;
4907 * Special optimization for periodic background processes
4908 * (currently only used for activity_checking_dir):
4909 * Since this is done frequently, we don't want to fork new process each
4910 * time. Hence, instead of exiting when it's done, the background process
4911 * is "sticky", i.e., it will stay around waiting for a message on stdin,
4912 * so it can be re-used the next time around. A linked list of sticky
4913 * procs that are currently active is maintained in the ActivityTable.
4915 sticky = ActivityTable[activity].sticky;
4918 /* see if we can find an idle sticky proc that can do the work */
4919 for (p = ActivityTable[activity].sticky_procs; p; p = p->next)
4928 /* We found an idle sticky proc that can be used */
4929 DPRINTF2(("ScheduleActivity: use sticky proc %ld\n", (long)p->child));
4931 /* Send the directory name to the sticky proc */
4933 if (PipeWriteString(p->pipe_m2s_fd, directory->path_name) < 0) {
4936 /* the pipe is broken, remove the old proc then start a new one */
4937 for (d = ActivityTable[activity].sticky_procs; d && p; d = d->next) {
4940 /* the proc listed 1st is dead, remove it */
4941 ActivityTable[activity].sticky_procs = p->next;
4945 else if (d->next == p)
4947 /* the process "p" is dead, remove it */
4956 pipe_s2m_fd[0] = p->pipe_s2m_fd;
4965 /* Need to fork a new background process */
4967 /* create a pipe for reading data from the background proc */
4970 /* creating a new sticky proc? */
4973 /* also need a pipe for sending msgs to the sticky proc */
4976 /* add entry to the list of sticky procs */
4977 p = (StickyProcDesc *) XtMalloc(sizeof(StickyProcDesc));
4978 p->next = ActivityTable[activity].sticky_procs;
4979 ActivityTable[activity].sticky_procs = p;
4981 p->pipe_s2m_fd = pipe_s2m_fd[0];
4982 p->pipe_m2s_fd = pipe_m2s_fd[1];
4986 /* fork a background process */
4991 DBGFORK(("%s: fork failed for activity %d: %s\n",
4992 pname, activity, strerror(errno)));
4995 "%s: fork failed, ppid %d, pid %d, activity %d: error %d=%s\n",
4996 pname, getppid(), getpid(), activity, errno, strerror(errno));
4998 directory->activity = activity_idle;
4999 directory->last_check = save_last_check;
5001 /* close unused pipe connections */
5002 close(pipe_s2m_fd[0]); /* child won't read from this pipe */
5003 close(pipe_s2m_fd[1]); /* parent won't write to this pipe */
5006 close(pipe_m2s_fd[1]); /* child won't write to this pipe */
5007 close(pipe_m2s_fd[0]); /* parent won't read from this pipe */
5019 DBGFORK(("%s: child activity %d, s2m %d, m2s %d\n",
5020 pname, activity, pipe_s2m_fd[1], pipe_m2s_fd[0]));
5022 /* close unused pipe connections */
5023 close(pipe_s2m_fd[0]); /* child won't read from this pipe */
5025 close(pipe_m2s_fd[1]); /* child won't write to this pipe */
5027 /* run main routine for this activity from ActivityTable */
5030 rc = (*ActivityTable[activity].main)(pipe_s2m_fd[1],
5031 directory, activity);
5032 if (!sticky || rc != 0)
5035 /* wait for a message in the pipe */
5036 s = PipeReadString(pipe_m2s_fd[0]);
5040 XtFree(directory->path_name);
5041 directory->path_name = s;
5043 DPRINTF2(("StickyActivity: activity %d, dir %s\n", activity, s));
5046 /* close pipes and end this process */
5047 close(pipe_s2m_fd[1]);
5049 close(pipe_m2s_fd[0]);
5051 DBGFORK(("%s: completed activity %d, (rc %d)\n",pname, activity, rc));
5056 DBGFORK(("%s: forked child<%d> for activity %d, s2m %d, m2s %d\n",
5057 pname, pid, activity, pipe_s2m_fd[0], pipe_m2s_fd[1]));
5059 /* parent process */
5064 * If a directory read or update was started:
5065 * clear the modifile_list, now that the
5066 * background process has it's own copy.
5068 if (activity == activity_reading ||
5069 activity == activity_update_all ||
5070 activity == activity_update_some)
5072 for (i = 0; i < directory->modified_count; i++)
5073 XtFree(directory->modified_list[i]);
5074 XtFree((char *)directory->modified_list);
5076 directory->modified_count = 0;
5077 directory->modified_list = NULL;
5080 /* close unused pipe connections */
5081 close(pipe_s2m_fd[1]); /* parent won't write to this pipe */
5083 close(pipe_m2s_fd[0]); /* parent won't read from this pipe */
5087 /* set up callback to get the pipe data */
5088 DPRINTF2(("ScheduleActivity: setting up pipe callback\n"));
5089 pipe_data = (PipeCallbackData *)XtMalloc(sizeof(PipeCallbackData));
5090 pipe_data->directory = directory;
5091 pipe_data->child = pid;
5092 pipe_data->sticky_proc = p;
5093 pipe_data->activity = activity;
5095 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
5096 pipe_s2m_fd[0], (XtPointer)XtInputReadMask,
5097 ActivityTable[activity].callback, (XtPointer)pipe_data);
5101 /*--------------------------------------------------------------------
5103 * See if any new background work should be started.
5104 *------------------------------------------------------------------*/
5108 Directory *directory)
5112 /* first try to schedule new activity for this directory */
5113 if (directory != NULL)
5114 ScheduleDirectoryActivity(directory);
5116 /* see if there is anything else we can schedule now */
5117 if (directory == NULL || directory->activity == activity_idle)
5119 for (i = 0; i < directory_count; i++)
5120 if (directory_set[i] != directory)
5121 ScheduleDirectoryActivity(directory_set[i]);
5123 ScheduleDirectoryActivity(dummy_directory);
5128 FileMgrData *file_mgr_data)
5130 DirectorySet *directory_data;
5131 FileViewData *file_view_data;
5134 directory_data = file_mgr_data->directory_set[0];
5136 for (j = 0; j < directory_data->file_count; j++)
5138 file_view_data = directory_data->file_view_data[j];
5140 if (file_view_data->filtered != True &&
5141 strcmp(file_mgr_data->desktop_file,
5142 file_view_data->file_data->file_name) == 0)
5144 SelectFile (file_mgr_data, file_view_data);
5148 ActivateSingleSelect(file_mgr_data->file_mgr_rec,
5149 file_mgr_data->selection_list[0]->file_data->logical_type);
5150 PositionFileView(file_view_data, file_mgr_data);