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 libraries 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 - 1)) > 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 - 1)) > 0)
1074 link_path[link_len] = 0;
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));
2026 /*--------------------------------------------------------------------
2028 * Main routine of the background process that updates a selected
2029 * list of directory entries.
2030 *------------------------------------------------------------------*/
2035 Directory *directory,
2036 ActivityStatus activity)
2038 char *host_name = directory->host_name;
2039 char *directory_name = directory->directory_name;
2040 char *full_directory_name;
2041 struct stat stat_buf;
2043 FileData *file_data;
2044 FileData2 file_data2;
2050 DPRINTF(("UpdateSomeProcess(%d, \"%s\", \"%s\")\n",
2051 pipe_fd, host_name, directory_name));
2054 /* get the full name of the current directory */
2056 Tt_status tt_status;
2058 full_directory_name = ResolveLocalPathName( host_name,
2063 if( TT_OK != tt_status )
2065 pipe_msg = PIPEMSG_ERROR;
2068 DPRINTF(("UpdateSomeProcess: sending errno %d (stat failed)\n", rc));
2069 write(pipe_fd, &pipe_msg, sizeof(short));
2070 write(pipe_fd, &rc, sizeof(int));
2071 write(pipe_fd, &modify_time, sizeof(long));
2075 (void) DtEliminateDots (full_directory_name);
2077 /* stat the directory to get the timestamp */
2078 if (stat(full_directory_name, &stat_buf) < 0 ||
2079 ! (stat_buf.st_mode & S_IXUSR) )
2081 /* send an error code back through the pipe */
2082 pipe_msg = PIPEMSG_ERROR;
2085 DPRINTF(("UpdateSomeProcess: sending errno %d (stat failed)\n", rc));
2086 write(pipe_fd, &pipe_msg, sizeof(short));
2087 write(pipe_fd, &rc, sizeof(int));
2088 write(pipe_fd, &modify_time, sizeof(long));
2089 XtFree( full_directory_name );
2092 modify_time = stat_buf.st_mtime;
2094 /* Loop through the list of modified files */
2095 for (i = 0; i < directory->modified_count; i++)
2099 if(directory->directoryView && directory->directoryView->file_mgr_data)
2100 IsToolBox = directory->directoryView->file_mgr_data->toolbox;
2104 ReadFileData2(&file_data2, full_directory_name,
2105 directory->modified_list[i],IsToolBox);
2107 /* now send the file data through the pipe */
2108 pipe_msg = PIPEMSG_FILEDATA2;
2109 write(pipe_fd, &pipe_msg, sizeof(short));
2110 write(pipe_fd, &file_data2, sizeof(FileData2));
2113 XtFree(full_directory_name);
2115 /* send a 'done' msg through the pipe */
2116 DPRINTF(("UpdateSomeProcess: sending DONE\n"));
2117 pipe_msg = PIPEMSG_DONE;
2118 write(pipe_fd, &pipe_msg, sizeof(short));
2119 write(pipe_fd, &modify_time, sizeof(long));
2124 /*--------------------------------------------------------------------
2125 * ReaddirPipeCallback
2126 * Callback routine that reads directory entry information sent
2127 * through the pipe from the background process.
2128 *------------------------------------------------------------------*/
2131 ReaddirPipeCallback(
2132 XtPointer client_data,
2136 static int whined_fd = 0;
2137 PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
2138 Directory *directory = pipe_data->directory;
2139 FileMgrData *file_mgr_data;
2140 FileMgrRec *file_mgr_rec;
2141 ActivityStatus activity;
2145 FileData *new_data, **new_nextp;
2146 FileData *old_data, **old_nextp;
2151 long modify_time = 0;
2152 char dirname[MAX_PATH];
2153 short file_data_count;
2156 /* verify that the directory still exists */
2157 if (DirectoryGone(directory))
2160 * The directory is no longer present:
2161 * close the pipe and kill the reader.
2165 kill(pipe_data->child, SIGKILL);
2166 XtFree( client_data );
2167 ScheduleActivity(NULL);
2171 /* read the next msg from the pipe */
2173 n = PipeRead(*fd, &msg, sizeof(short));
2174 activity = directory->activity;
2179 case PIPEMSG_PATH_LOGICAL_TYPES:
2180 /* get the number of path components */
2181 PipeRead(*fd, &n, sizeof(int));
2183 /* free logical types */
2184 for (i = 0; i < directory->path_count; i++)
2185 XtFree(directory->path_logical_types[i]);
2187 /* allocate array of the right size */
2188 if (directory->path_count != n)
2190 directory->path_count = n;
2191 directory->path_logical_types = (char **)
2192 XtRealloc((char *)directory->path_logical_types,
2196 /* get new logical types */
2197 for (i = 0; i < directory->path_count; i++)
2198 directory->path_logical_types[i] = PipeReadString(*fd);
2200 /* get the tt_path */
2201 XtFree(directory->tt_path_name);
2202 directory->tt_path_name = PipeReadString(*fd);
2204 /* update all views */
2205 for (i = 0; i < directory->numOfViews; i++)
2207 file_mgr_data = directory->directoryView[i].file_mgr_data;
2208 file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2209 UpdateHeaders(file_mgr_rec, file_mgr_data, True);
2210 XmUpdateDisplay(file_mgr_rec->file_window);
2212 XSync(XtDisplay(toplevel), False);
2215 case PIPEMSG_FILEDATA:
2216 case PIPEMSG_FILEDATA2:
2217 case PIPEMSG_FILEDATA3:
2219 if (msg == PIPEMSG_FILEDATA)
2221 new_data = PipeReadFileData(*fd);
2223 else if (msg == PIPEMSG_FILEDATA2)
2225 FileData2 file_data2;
2228 n = PipeRead(*fd, &file_data2, sizeof(FileData2));
2229 if (n < sizeof(FileData2)) {
2231 fprintf(stderr, "PipeReadFileData2: n = %d, expected %ld\n",
2232 n, (long)sizeof(FileData2));
2235 new_data = FileData2toFileData(&file_data2, &n);
2238 if (msg == PIPEMSG_FILEDATA3)
2240 int file_data_length;
2242 char file_data_buffer[FILEDATABUF * sizeof(FileData2)];
2243 char *file_data_buf_ptr;
2246 n = PipeRead(*fd, &file_data_count, sizeof(short));
2247 n = PipeRead(*fd, &file_data_length, sizeof(int));
2249 if (file_data_count & 0x8000)
2251 file_data_count &= 0x7fff;
2257 for (new_nextp = &directory->new_data;
2259 new_nextp = &(*new_nextp)->next)
2262 n = PipeRead(*fd, file_data_buffer, file_data_length);
2263 file_data_buf_ptr = file_data_buffer;
2265 for (i = 0; i < file_data_count; i++)
2267 /* get next FileData out of buffer */
2269 FileData2toFileData((FileData2 *)file_data_buf_ptr, &n);
2270 file_data_buf_ptr += n;
2272 /* append new_data to end of list */
2273 *new_nextp = new_data;
2274 new_data->next = NULL;
2275 new_nextp = &new_data->next;
2280 /* append new_data to end of list */
2281 file_data_count = 1;
2283 for (new_nextp = &directory->new_data;
2285 new_nextp = &(*new_nextp)->next)
2287 *new_nextp = new_data;
2288 new_data->next = NULL;
2291 if (activity == activity_reading)
2293 /* update file counts in all views */
2294 for (i = 0; i < directory->numOfViews; i++)
2296 file_mgr_data = directory->directoryView[i].file_mgr_data;
2297 file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2298 file_mgr_data->busy_detail += file_data_count;
2300 (file_mgr_data->busy_status == initiating_readdir ||
2301 file_mgr_data->busy_status == busy_readdir) &&
2302 file_mgr_data->busy_detail > 2)
2304 if (file_mgr_data->show_status_line)
2307 XmString label_string;
2310 GetStatusMsg(file_mgr_data, buf);
2312 XmStringCreateLocalized(buf);
2313 XtSetArg (args[0], XmNlabelString, label_string);
2314 XtSetValues(file_mgr_rec->status_line, args, 1);
2315 XmStringFree(label_string);
2317 else if (file_mgr_data->show_iconic_path)
2319 DtUpdateIconicPath(file_mgr_rec, file_mgr_data, False);
2321 else if (file_mgr_data->show_current_dir)
2323 DrawCurrentDirectory(file_mgr_rec->current_directory,
2324 file_mgr_rec, file_mgr_data);
2331 case PIPEMSG_POSITION_INFO:
2332 /* free old position info names */
2333 for (i = 0; i < directory->position_count; i++)
2334 XtFree(directory->position_info[i].name);
2336 /* get number of positions and realloc array, if necessary */
2337 PipeRead(*fd, &n, sizeof(int));
2338 if (directory->position_count != n)
2340 directory->position_count = n;
2341 directory->position_info = (PositionInfo *) XtRealloc(
2342 (char *)directory->position_info, n*sizeof(PositionInfo));
2345 /* read new position info */
2346 for (i = 0; i < n; i++)
2347 PipeReadPositionInfo(*fd, &directory->position_info[i]);
2351 PipeRead(*fd, &modify_time, sizeof(long));
2352 directory->errnum = 0;
2353 directory->errmsg_needed = False;
2358 PipeRead(*fd, &rc, sizeof(int));
2359 PipeRead(*fd, &modify_time, sizeof(long));
2360 if (rc != directory->errnum)
2362 directory->errnum = rc;
2363 directory->errmsg_needed = True;
2369 if (whined_fd != *fd)
2373 "ReaddirPipeCallback: badmsg=%d, ppid=%d pid=%d fd=%d activ'y=%d\n",
2374 msg, getppid(), getpid(), *fd, activity);
2376 directory->errnum = -1;
2377 directory->errmsg_needed = False;
2381 /* check if we are done */
2384 #ifdef DT_PERFORMANCE
2385 /* Aloke Gupta: As suggested by Dana Dao */
2386 _DtPerfChkpntMsgSend("Done Read Directory");
2388 DPRINTF(("ReaddirPipeCallback: done, errno %d, time %ld\n",
2389 directory->errnum, modify_time));
2391 /* close the pipe and cancel the callback */
2396 * @@@ what if a drag is active ???
2400 * For files in the old list that still exist in the new
2401 * one we need to re-use the old FileData structures.
2402 * Reason: the code in GetFileData relies on this to
2403 * preserve the position_info and selection list.
2404 * The following loops through the new list of files
2405 * and replaces entries that also exist in the old list.
2407 for (new_nextp = &directory->new_data;
2408 (new_data = *new_nextp) != NULL;
2409 new_nextp = &new_data->next)
2411 for (old_nextp = &directory->file_data;
2412 (old_data = *old_nextp) != NULL;
2413 old_nextp = &old_data->next)
2415 if( strcmp(old_data->file_name, new_data->file_name) == 0 )
2417 *old_nextp = old_data->next;
2419 FreeFileData(old_data, False);
2420 memcpy(old_data, new_data, sizeof(FileData));
2422 XtFree((char *)new_data);
2423 *new_nextp = new_data = old_data;
2431 * If this was a complete re-read, we free all FileData still left
2432 * in the old list. Otherwise, if this was just a partial update,
2433 * we append the old data that's still left to the end of the
2436 if (activity == activity_reading)
2438 /* This was a complete re-read: free all old data still left. */
2439 while (directory->file_data)
2441 old_data = directory->file_data;
2442 directory->file_data = old_data->next;
2443 FreeFileData(old_data, True);
2446 /* replace the old list by the new list */
2447 directory->file_data = directory->new_data;
2448 directory->new_data = NULL;
2452 FileData * tmp_ptr = NULL;
2454 /* remove any directory entries that no longer exist
2457 new_nextp = &directory->new_data;
2458 while ((new_data = *new_nextp) != NULL)
2460 if (new_data->errnum == ENOENT)
2462 *new_nextp = new_data->next;
2463 FreeFileData(new_data, True);
2467 tmp_ptr = *new_nextp;
2468 new_nextp = &new_data->next;
2472 /* remove any directory entries that no longer exist
2475 old_nextp = &directory->file_data;
2476 while ((old_data = *old_nextp) != NULL)
2478 if (old_data->errnum == ENOENT)
2480 *old_nextp = old_data->next;
2481 FreeFileData(old_data, True);
2484 old_nextp = &old_data->next;
2487 /* Append the old list to the end of the new list
2488 Replace the old list pointer with the new list pointer
2490 if( tmp_ptr != NULL )
2492 tmp_ptr->next = directory->file_data;
2493 directory->file_data = directory->new_data;
2494 directory->new_data = NULL;
2498 /* update the file count */
2499 directory->file_count = 0;
2500 for (new_data = directory->file_data; new_data; new_data = new_data->next)
2501 directory->file_count++;
2503 /* update directory timestamp */
2504 if (activity == activity_reading ||
2505 activity == activity_update_all ||
2506 directory->was_up_to_date)
2508 if (modify_time != 0)
2509 directory->modify_time = modify_time;
2512 /* flush the icon cache */ /* @@@ Why? What does this do? @@@ */
2513 strcpy (dirname, directory->path_name);
2514 DtEliminateDots(dirname);
2515 /* We will not attempt to flush the icon cache until this */
2516 /* function has been fixed. */
2518 _DtFlushIconFileCache(dirname);
2520 /* reset busy flags */
2521 directory->busy[activity] = False;
2522 directory->activity = activity_idle;
2523 directory->was_up_to_date = True;
2524 directory->link_check_needed = False;
2528 * Fill dir_data field with information on the directory itself.
2529 * This data will be read when querying this view's top directory,
2530 * if the parent directory isn't already cached (tree mode)
2532 for (new_data = directory->file_data;
2534 new_data = new_data->next)
2535 if (strcmp(new_data->file_name, ".") == 0)
2539 * Found current directory information, now we make
2540 * dir_data info from "." info
2543 /* If we already have allocated space for dir_data free it */
2544 if ( directory->dir_data != NULL )
2545 FreeFileData(directory->dir_data, True);
2547 directory->dir_data = (FileData *)XtMalloc(sizeof(FileData));
2549 memcpy(directory->dir_data, new_data, sizeof(FileData));
2552 * Doctor up some of the information fields so that this doesn't
2553 * seem to be a "." entry
2555 directory->dir_data->next = NULL;
2556 directory->dir_data->file_name =
2557 XtNewString(DName(directory->directory_name));
2558 directory->dir_data->action_name = NULL;
2559 if (directory->path_count > 0)
2561 directory->dir_data->logical_type = XtNewString(
2562 directory->path_logical_types[directory->path_count - 1]);
2565 directory->dir_data->logical_type = NULL;
2566 directory->dir_data->link = NULL;
2567 directory->dir_data->final_link = NULL;
2568 directory->dir_data->is_subdir = True;
2573 /* cause all views on this directory to be redrawn */
2574 for (i = 0; i < directory->numOfViews; i++)
2576 file_mgr_data = directory->directoryView[i].file_mgr_data;
2577 file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2578 FileMgrRedisplayFiles(file_mgr_rec, file_mgr_data, False);
2579 if(file_mgr_data->desktop_file)
2581 SelectDesktopFile(file_mgr_data);
2582 XtFree(file_mgr_data->desktop_file);
2583 file_mgr_data->desktop_file = NULL;
2586 XtFree(client_data);
2588 /* schedule the next background activity */
2589 ScheduleActivity(directory);
2594 /*--------------------------------------------------------------------
2595 * ReadDirectoryFiles
2596 * This routine is called to read a directory if the directory
2597 * wasn't found in the cached, or if the directory has to be
2598 * re-read because it changed. This routine schedules a background
2599 * process to be started that will do the actual work.
2600 *------------------------------------------------------------------*/
2605 Directory *directory)
2607 FileMgrData *file_mgr_data;
2608 FileMgrRec *file_mgr_rec;
2611 #ifdef DT_PERFORMANCE
2613 _DtPerfChkpntMsgSend("Begin Read Directory");
2616 /* make sure positionFileName is initialized */
2617 if (positionFileName == NULL)
2618 InitializePositionFileName();
2620 /* mark the directory busy reading */
2621 directory->busy[activity_reading] = True;
2623 /* arrange for background process to be started */
2624 ScheduleActivity(directory);
2626 /* make sure all views on this directory are marked busy */
2627 for (i = 0; i < directory->numOfViews; i++)
2629 file_mgr_data = directory->directoryView[i].file_mgr_data;
2630 if (file_mgr_data->busy_status == not_busy)
2632 file_mgr_data->busy_status = busy_readdir;
2633 file_mgr_data->busy_detail = 0;
2634 file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2635 FileMgrRedisplayFiles(file_mgr_rec, file_mgr_data, False);
2637 file_mgr_data->busy_status = busy_readdir;
2643 /*--------------------------------------------------------------------
2645 * Given a directory name, see if the directory is already cached.
2646 * If so, return the file data list, otherwise, read the directory.
2647 *------------------------------------------------------------------*/
2653 char *directory_name,
2654 FileData **file_data,
2656 FileMgrData *file_mgr_data)
2658 Directory *directory;
2662 /* initialize return values */
2663 if (file_data != NULL)
2669 /* see if the directory is already in the cache */
2670 directory = FindDirectory(host_name, directory_name);
2672 if ((directory != NULL) &&
2673 (strcmp(directory_name, directory->directory_name) == 0))
2675 /* The directory is already in the cache. */
2676 directory->viewed = True;
2678 /* Look for the view in the view list */
2679 for (i = 0; i < directory->numOfViews; i++)
2680 if (directory->directoryView[i].file_mgr_data == file_mgr_data)
2683 /* If view not found, add to the view list */
2684 if (i == directory->numOfViews)
2686 directory->directoryView = (DirectoryView *)
2687 XtRealloc ((char *) directory->directoryView,
2688 sizeof(DirectoryView) * (i + 1));
2689 directory->numOfViews++;
2690 directory->directoryView[i].file_mgr_data = file_mgr_data;
2693 /* set mapped flag for the view */
2694 directory->directoryView[i].mapped = file_mgr_data->mapped;
2696 /* check if we need to popup an error message */
2697 if (directory->errmsg_needed &&
2698 !directory->busy[activity_reading] &&
2701 err_msg = XtNewString(GetSharedMessage(CANNOT_READ_DIRECTORY_ERROR));
2702 FileOperationError (w, err_msg, directory_name);
2704 directory->errmsg_needed = False;
2707 DPRINTF2(("ReadDirectory(\"%s\", \"%s\") returns cached\n",
2708 host_name, directory_name));
2713 Tt_status tt_status;
2715 /* The directory is not yet in the cache. */
2717 /* Expand the directory set array, if necessary. */
2718 if (directory_count == directory_set_size)
2720 directory_set_size += 10;
2721 directory_set = (Directory **) XtRealloc((char *)directory_set,
2722 sizeof(Directory **) * directory_set_size);
2726 /* Create and initialize a new directory entry */
2727 directory_set[directory_count] = directory =
2728 (Directory *) XtMalloc (sizeof (Directory));
2731 directory->host_name = XtNewString (host_name);
2732 directory->directory_name = XtNewString (directory_name);
2733 directory->path_name = ResolveLocalPathName (host_name,
2738 if (directory->path_name == NULL)
2740 directory->path_name = (char *) XtMalloc(sizeof(char));
2741 directory->path_name[0]='\0';
2743 directory->tt_path_name = NULL;
2744 directory->viewed = True;
2745 directory->file_count = 0;
2746 directory->numOfViews = 1;
2747 directory->errnum = 0;
2748 directory->errmsg_needed = False;
2749 directory->last_check = 0;
2750 directory->link_check_needed = False;
2751 directory->file_count = 0;
2752 directory->file_data = NULL;
2753 directory->new_data = NULL;
2754 directory->dir_data = NULL;
2755 directory->path_count = 0;
2756 directory->path_logical_types = NULL;
2757 directory->position_count = 0;
2758 directory->position_info = NULL;
2759 directory->modify_begin = 0;
2760 directory->modified_count = 0;
2761 directory->was_up_to_date = True;
2762 directory->modified_list = NULL;
2763 directory->activity = activity_idle;
2764 for (i = 0; i < activity_idle; i++)
2765 directory->busy[i] = False;
2767 directory->directoryView = (DirectoryView *)
2768 XtMalloc (sizeof(DirectoryView));
2769 directory->directoryView[0].file_mgr_data = file_mgr_data;
2770 directory->directoryView[0].mapped = file_mgr_data->mapped;
2772 /* Open the directory for reading and read the files. */
2773 ReadDirectoryFiles (w, directory);
2776 /* Restart refresh timer, if necessary */
2777 if (file_mgr_data->mapped && timer_suspended)
2779 XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
2780 timer_suspended = False;
2783 /* return the file data */
2784 if (file_data != NULL && !directory->busy[activity_reading])
2786 *file_count = directory->file_count;
2787 *file_data = directory->file_data;
2790 return directory->busy[activity_reading];
2794 /*--------------------------------------------------------------------
2796 * Internal routine that recursively read a directory plus
2797 * subdirectories down to a depth given by read_level.
2798 *------------------------------------------------------------------*/
2803 FileMgrData *file_mgr_data,
2805 char *directory_name,
2806 FileViewData *dp, /* directory info */
2807 int level, /* tree level of this directory */
2808 int read_level, /* deepest level to be read */
2809 char **branch_list) /* list of tree branches to expand */
2811 * Recursively read a directory plus subdirectories down to a depth
2812 * given by read_level.
2815 char subdir_name[MAX_PATH];
2816 FileData *fp, *file_data;
2817 FileViewData **lp, *ip;
2820 Boolean busy_reading;
2822 DPRINTF2(("_ReadDir(\"%s\", \"%s\"): level %d, read_level %d\n",
2823 host_name, directory_name, level, read_level));
2825 /* initialize list of descendents and counts */
2829 dp->ndir = dp->nfile = 0;
2834 /* Read the directory content */
2835 busy_reading = ReadDirectory(w, host_name, directory_name,
2836 &file_data, &n, file_mgr_data);
2839 file_mgr_data->busy_status = busy_readdir;
2848 for (i = 0, fp = file_data; i < n && fp; i++, fp = fp->next) {
2850 /* initialize new dir entry */
2853 ip = (FileViewData *)XtMalloc(sizeof(FileViewData));
2854 memset(ip, 0, sizeof(FileViewData));
2860 /* read subdirectory */
2863 /* construct sub directory name */
2864 strncpy(subdir_name, directory_name, MAX_PATH - 1);
2865 if (strlen(subdir_name) > 0
2866 && subdir_name[strlen(subdir_name) - 1] != '/')
2867 strncat(subdir_name, "/", MAX_PATH - 1);
2869 strncat(subdir_name, fp->file_name, MAX_PATH - 1);
2870 subdir_name[MAX_PATH - 1] = 0;
2872 /* see if we know this entry from branch_list */
2873 if (!QueryBranchList(file_mgr_data, branch_list, subdir_name, &ts))
2874 /* not known: assume we shouldn't read this subdir */
2877 if (level < read_level || ts != tsNotRead) {
2879 rc = _ReadDir(w, file_mgr_data, host_name, subdir_name, ip,
2880 level, read_level, branch_list);
2885 else if (ts >= tsReading)
2887 else if (level >= file_mgr_data->tree_show_level)
2889 else if (file_mgr_data->tree_files == TREE_FILES_ALWAYS)
2896 /* add new entry to linked list */
2908 /*--------------------------------------------------------------------
2910 * This is the main external entry point for reading directories.
2911 *------------------------------------------------------------------*/
2916 FileMgrData *file_mgr_data,
2918 char *directory_name,
2919 FileViewData *dp, /* directory info */
2920 int level, /* tree level of this directory */
2921 int read_level, /* deepest level to be read */
2922 char **branch_list) /* list of tree branches to expand */
2924 /* initially assume we are not busy */
2925 if (file_mgr_data->busy_status == not_busy)
2926 file_mgr_data->busy_detail = 0;
2928 file_mgr_data->busy_status = initiating_readdir;
2930 /* first pass: just check if any directory we need is busy */
2931 _ReadDir(w, file_mgr_data, host_name, directory_name, NULL, level,
2932 read_level, branch_list);
2934 /* if a directory we need is busy, return now */
2935 if (file_mgr_data->busy_status == busy_readdir)
2939 * All directories wee need are available.
2940 * Make a second pass for real.
2942 file_mgr_data->busy_status = not_busy;
2943 return _ReadDir(w, file_mgr_data, host_name, directory_name, dp, level,
2944 read_level, branch_list);
2948 /*====================================================================
2950 * Routines that update the directory cache
2952 *==================================================================*/
2954 /*--------------------------------------------------------------------
2955 * FileWindowMapUnmap
2956 * Update mapped flag in view lists.
2957 *------------------------------------------------------------------*/
2961 FileMgrData *file_mgr_data)
2966 for (i = 0; i < directory_count; i++)
2968 for (j = 0; j < directory_set[i]->numOfViews; j++)
2970 if (file_mgr_data == directory_set[i]->directoryView[j].file_mgr_data)
2972 directory_set[i]->directoryView[j].mapped = file_mgr_data->mapped;
2978 if (file_mgr_data->mapped && timer_suspended)
2980 XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
2981 timer_suspended = False;
2986 /*--------------------------------------------------------------------
2988 * Read a directory already cached and update its contents.
2989 *------------------------------------------------------------------*/
2995 char *directory_name )
2997 Directory *directory;
2999 DPRINTF(("RereadDirectory(%s, %s)\n", host_name, directory_name));
3001 /* Find the directory set entry. */
3002 directory = FindDirectory(host_name, directory_name);
3003 if (directory != NULL)
3005 /* reset errnum to make sure we'll get an error message */
3006 directory->errnum = 0;
3008 /* Read the directory. */
3009 if (!directory->busy[activity_reading])
3010 ReadDirectoryFiles(w, directory);
3015 /*--------------------------------------------------------------------
3017 * Check if any files were added or deleted in a directory
3018 * and update the directory contents accordingly.
3019 *------------------------------------------------------------------*/
3025 char *directory_name )
3027 Directory *directory;
3029 DPRINTF(("UpdateDirectory(%s, %s)\n", host_name, directory_name));
3031 /* Find the directory set entry. */
3032 directory = FindDirectory(host_name, directory_name);
3033 if (directory != NULL)
3035 /* arrange for directory contents to be checked */
3036 if (!directory->busy[activity_update_all])
3038 directory->busy[activity_update_all] = True;
3039 ScheduleActivity(directory);
3045 /*====================================================================
3047 * Directory modification routines:
3049 * The following routines are provided to avoid unnecessary
3050 * re-reads of whole directories. For example, if the user
3051 * renames a file, it's only necessary to remove the old file
3052 * from the directory and add it back under its new name; there
3053 * is no need to read the whole directory again. Similarly,
3054 * when a file is dropped on a directory, it's only necessary
3055 * to add the one new file to the directory.
3057 * To accomplish this, the routines that rename or copy files
3058 * make the following calls:
3060 * DirectoryBeginModify(): called before doing the operation
3061 * DirectoryFileModified(): called once for each affected file
3062 * DirectoryEndModify(): called when the operation is completed
3064 * The routines remember which files were modified, and when
3065 * DirectoryEndModify is called, a background process is started,
3066 * that re-stats and types just those files.
3068 * A complication arises from automatic re-reads triggered by
3069 * a periodic timer (routine TimerEvent). Since renaming or
3070 * copying files changes the timestamp on the directory, the
3071 * automatic re-read would re-read the whole directory soon after
3072 * the operation is done, nullifying our efforts to avoid
3073 * unnecessary re-reads. Therefore:
3075 * - We don't do any automatic re-reads between calls to
3076 * DirectoryBeginModify and DirectoryEndModify.
3078 * - If the directory timestamp hadn't changed at the time
3079 * of the DirectoryBeginModify, then when the directory
3080 * update triggered by DirectoryEndModify finishes, we
3081 * set the modify_time in the directory_set to the current
3082 * timestamp of the directory. This means that the next
3083 * automatic re-read won't be triggered unless the directory
3084 * is modified again after the DirectoryEndModify.
3086 *==================================================================*/
3088 /*--------------------------------------------------------------------
3089 * DirectoryAbortModify
3090 * Decrement the modify_begin counter.
3091 *------------------------------------------------------------------*/
3094 DirectoryAbortModify(
3096 char *directory_name)
3098 Directory *directory;
3100 DPRINTF(("DirectoryAbortModify(%s, %s)\n", host_name, directory_name));
3102 /* Find the directory set entry. */
3103 directory = FindDirectory(host_name, directory_name);
3104 if (directory != NULL)
3106 directory->modify_begin--;
3108 if (directory->modify_begin == 0)
3109 directory->was_up_to_date = True;
3111 DPRINTF((" modify_begin %d, up_to_date %d\n",
3112 directory->modify_begin, directory->was_up_to_date));
3117 /*--------------------------------------------------------------------
3118 * DirectoryBeginModify
3119 * Increment the modify_begin counter to suspend automatic
3120 * re-reads until DirectoryEndModify is called.
3121 *------------------------------------------------------------------*/
3124 DirectoryBeginModify(
3126 char *directory_name)
3128 Directory *directory;
3130 DPRINTF(("DirectoryBeginModify(%s, %s)\n", host_name, directory_name));
3132 /* Find the directory set entry. */
3133 directory = FindDirectory(host_name, directory_name);
3134 if (directory != NULL)
3136 if (directory->modify_begin == 0)
3137 /* until we know better, assume the directory changed */
3138 directory->was_up_to_date = False;
3140 /* increment the modify_begin counter */
3141 directory->modify_begin++;
3143 DPRINTF((" modify_begin %d, up_to_date %d\n",
3144 directory->modify_begin, directory->was_up_to_date));
3149 /*--------------------------------------------------------------------
3150 * DirectoryModifyTime
3151 * This routine should be called after DirectoryBeginModify and
3152 * before doing any operation on the directory. The parameter
3153 * modify_time should be the current timestamp of the directory.
3154 * By comparing the value to the modify_time stored in the
3155 * directory set we decide whether the directory had already
3156 * changed before the update operation began.
3157 * Note: the reason for supplying a separate call for this check,
3158 * instead of doing it inside DirectoryBeginModify(), is that we
3159 * want to do the stat call that determines the current timestamp
3160 * of the directory in a background process. The background
3161 * process that we start fo do the actual update is a convenient
3163 *------------------------------------------------------------------*/
3166 DirectoryModifyTime(
3168 char *directory_name,
3171 Directory *directory;
3173 DPRINTF(("DirectoryModifyTime(%s, %s)\n", host_name, directory_name));
3175 #ifdef SMART_DIR_UPDATE
3176 /* Find the directory set entry. */
3177 directory = FindDirectory(host_name, directory_name);
3179 if (directory != NULL)
3181 /* mark directory up-to-date if unchanged since last read */
3182 if (modify_time <= directory->modify_time)
3183 directory->was_up_to_date = True;
3184 DPRINTF((" modify_begin %d, up_to_date %d\n",
3185 directory->modify_begin, directory->was_up_to_date));
3191 /*--------------------------------------------------------------------
3192 * DirectoryFileModified
3193 * This routine is called when we know that a file in a directory
3194 * has been modified, added or removed. The file name is added
3195 * to the list of modified files. The next time an update
3196 * background process is started, it will check all the files
3197 * on the modfied list and update the corresponding FileData.
3198 *------------------------------------------------------------------*/
3201 DirectoryFileModified(
3203 char *directory_name,
3206 Directory *directory;
3209 DPRINTF(("DirectoryFileModified(%s, %s, %s)\n",
3210 host_name, directory_name, file_name));
3212 /* Find the directory set entry. */
3213 directory = FindDirectory(host_name, directory_name);
3214 if (directory != NULL)
3216 /* see if the file is already on the list */
3217 for( i = 0; i < directory->modified_count; ++i )
3218 if( strcmp( directory->modified_list[i], file_name ) == 0 )
3221 /* add the file to the modified_list */
3222 i = directory->modified_count++;
3223 directory->modified_list = (char **)
3224 XtRealloc((char *)directory->modified_list, (i + 1)*sizeof(char *));
3225 directory->modified_list[i] = XtNewString(file_name);
3230 /*--------------------------------------------------------------------
3231 * DirectoryEndModify
3232 * Start an update background process (will check all the files
3233 * on the modfied list and update the corresponding FileData).
3234 *------------------------------------------------------------------*/
3239 char *directory_name)
3241 Directory *directory;
3243 DPRINTF(("DirectoryEndModify(%s, %s)\n", host_name, directory_name));
3245 /* Find the directory set entry. */
3246 directory = FindDirectory(host_name, directory_name);
3248 /* arrange for an update background process to be scheduled */
3249 if (directory != NULL)
3251 directory->modify_begin--;
3252 DPRINTF((" modify_begin %d, up_to_date %d, modified_count %d\n",
3253 directory->modify_begin,
3254 directory->was_up_to_date,
3255 directory->modified_count));
3256 if (directory->modified_count > 0)
3259 char subdir_name[MAX_PATH + 1];
3264 * If any of the modifed files is a subdirectory that we have
3265 * cached, schedule an activity_checking_dir to make sure that
3266 * the subdirectory is still readable.
3268 strcpy(subdir_name, directory_name);
3269 p = subdir_name + strlen(subdir_name);
3273 for (i = 0; i < directory->modified_count; i++)
3275 strcpy(p, directory->modified_list[i]);
3276 subdir = FindDirectory(host_name, subdir_name);
3279 DPRINTF((" schedule check for subdir \"%s\"\n",
3280 directory->modified_list[i]));
3281 subdir->busy[activity_checking_dir] = True;
3282 ScheduleActivity(subdir);
3286 #ifdef SMART_DIR_UPDATE
3287 /* schedule a partial update of the modfied directory */
3288 if (directory->was_up_to_date)
3289 directory->busy[activity_update_some] = True;
3291 directory->busy[activity_update_all] = True;
3293 /* schedule a full update of the modfied directory */
3294 directory->busy[activity_update_all] = True;
3296 ScheduleActivity(directory);
3302 /*--------------------------------------------------------------------
3303 * UpdateDirectorySet
3304 * We call this when we do a database update. It loops through
3305 * the directory_set list and rereads each directory.
3306 *------------------------------------------------------------------*/
3309 UpdateDirectorySet( void )
3313 DPRINTF(("UpdateDirectorySet ...\n"));
3315 for (i = 0; i < directory_count; i++)
3316 if (!directory_set[i]->busy[activity_reading])
3317 ReadDirectoryFiles (NULL, directory_set[i]);
3322 /*--------------------------------------------------------------------
3323 * UpdateCachedDirectories
3324 * Update view list for all cached directories.
3325 * Throw out any directories that are no longer being viewed.
3326 *------------------------------------------------------------------*/
3329 UpdateCachedDirectories(
3333 DialogData * dialog_data;
3334 FileMgrData * file_mgr_data;
3336 Directory *directory;
3340 * clear the view list in all directory set entries
3342 for (i = 0; i < directory_count; i++)
3344 if( !(strcmp(directory_set[i]->directory_name, trash_dir) == 0) )
3346 XtFree ((char *) directory_set[i]->directoryView);
3347 directory_set[i]->numOfViews = 0;
3348 directory_set[i]->directoryView = NULL;
3349 directory_set[i]->viewed = False;
3356 * reconstruct view lists by adding each directory found in the view
3357 * set to the view list for the corresponding directory set entry
3359 for (j = 0; j < view_count; j++)
3361 dialog_data = (DialogData *) view_set[j]->dialog_data;
3362 file_mgr_data = (FileMgrData *) dialog_data->data;
3364 /* loop through all direcories in this view */
3365 for (k = 0; k < file_mgr_data->directory_count; k++)
3367 /* find the directory in the directory set */
3368 directory = FindDirectory(view_set[j]->host_name,
3369 file_mgr_data->directory_set[k]->name);
3371 /* we expect the directory to be found; if not, something is wrong */
3372 if (directory == NULL)
3374 fprintf(stderr, "Warning: %s:%s not found in directory set.\n",
3375 view_set[j]->host_name,
3376 file_mgr_data->directory_set[k]->name);
3380 /* add the directory to the view list */
3381 n = directory->numOfViews;
3382 directory->directoryView = (DirectoryView *)
3383 XtRealloc ((char *) directory->directoryView,
3384 sizeof(DirectoryView) * (n + 1));
3385 directory->directoryView[n].file_mgr_data = file_mgr_data;
3386 directory->directoryView[n].mapped = file_mgr_data->mapped;
3387 directory->numOfViews++;
3388 directory->viewed = True;
3395 * remove all directories that have empty view lists
3398 while (i < directory_count)
3400 if (directory_set[i]->numOfViews > 0 ||
3401 strcmp(directory_set[i]->directory_name, trash_dir) == 0)
3403 /* Keep this directory in the directory set. */
3408 /* Delete the file data and remove from the directory set. */
3410 DPRINTF(("UpdateCachedDirectories: removing %s:%s\n",
3411 directory_set[i]->host_name,
3412 directory_set[i]->directory_name));
3414 FreeDirectory(directory_set[i]);
3416 for (k = i; k < directory_count - 1; k++)
3417 directory_set[k] = directory_set[k + 1];
3423 /* Restart refresh timer, if necessary */
3424 if (timer_suspended && SomeWindowMapped())
3426 XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
3427 timer_suspended = False;
3432 /*====================================================================
3434 * Routines that return directory data
3436 *==================================================================*/
3438 /*--------------------------------------------------------------------
3440 * Return a string that contains file information similar to "ls -l",
3441 * including: permissions, owner, modified time, link (if any).
3442 * Used for "view by attributes"
3445 * -rw-r--r-- dld staff 108314 Jul 26 15:16:36 1993 Directory.c
3447 *------------------------------------------------------------------*/
3451 FileData *file_data )
3455 char time_string[100];
3459 char link_path[MAX_PATH + 5];
3460 static gid_t group_id = (gid_t)-1;
3461 static uid_t user_id = (uid_t)-1;
3462 struct group * group_data;
3463 struct passwd * user_data;
3464 static char group_name[20];
3465 static char user_name[20];
3467 time_t long_modify_time;
3469 char usr_read_priv, usr_write_priv, usr_exec_priv;
3470 char grp_read_priv, grp_write_priv, grp_exec_priv;
3471 char oth_read_priv, oth_write_priv, oth_exec_priv;
3473 /* Generate the long list name. */
3474 long_name = (char *) XtMalloc(sizeof(char) * (MAX_PATH * 3));
3477 /* Initially, assume their is not a soft link */
3478 link_path[0] = '\0';
3480 if (file_data->errnum == 0)
3482 if (file_data->stat.st_gid != group_id)
3484 group_id = file_data->stat.st_gid;
3485 group_data = getgrgid (file_data->stat.st_gid);
3488 strcpy (group_name, group_data->gr_name);
3489 if (strlen (group_name) == 0)
3490 strcpy (group_name, "root");
3493 strcpy (group_name, "root");
3496 if (file_data->stat.st_uid != user_id)
3498 user_id = file_data->stat.st_uid;
3499 user_data = getpwuid (file_data->stat.st_uid);
3500 /* Initially, assume their is not a user name */
3501 user_name[0] = '\0';
3503 strcpy (user_name, user_data->pw_name);
3505 sprintf(user_name,"%ld",(long)user_id);
3510 char error_msg[1024];
3513 /* determine how much space we have for an error message */
3514 long_modify_time = 747616435;
3515 /* just needed to determine the length of a date */
3517 tms = localtime(&long_modify_time);
3518 strftime( time_string, 100,
3519 GetSharedMessage(DIRECTORY_DATE_FORMAT),
3522 time_string = ctime ((time_t *)&long_modify_time);
3523 time_string[strlen(time_string)-1] = 0x0;
3526 msg_len = 10 + 3 + 9 + 1 + 9 + 1 + 9 + 1 + strlen(time_string);
3528 /* generate the error message */
3529 strcpy(error_msg, "(");
3530 strncpy(error_msg + 1, strerror(file_data->errnum), msg_len - 2);
3531 error_msg[msg_len - 1] = '\0';
3532 strcat(error_msg, ")");
3534 sprintf( long_name, "%-28.28s %s %9d %s",
3535 file_data->file_name,
3543 /* Build the permission string */
3544 switch( file_data->stat.st_mode & S_IFMT )
3559 permission = OPTION_OFF;
3563 if (file_data->stat.st_mode & S_IRUSR) usr_read_priv = READ_PRIV;
3564 else usr_read_priv = OPTION_OFF;
3566 if (file_data->stat.st_mode & S_IWUSR) usr_write_priv = WRITE_PRIV;
3567 else usr_write_priv = OPTION_OFF;
3569 if (file_data->stat.st_mode & S_IXUSR) usr_exec_priv = EXEC_PRIV;
3570 else usr_exec_priv = OPTION_OFF;
3573 if (file_data->stat.st_mode & S_IRGRP) grp_read_priv = READ_PRIV;
3574 else grp_read_priv = OPTION_OFF;
3576 if (file_data->stat.st_mode & S_IWGRP) grp_write_priv = WRITE_PRIV;
3577 else grp_write_priv = OPTION_OFF;
3579 if (file_data->stat.st_mode & S_IXGRP) grp_exec_priv = EXEC_PRIV;
3580 else grp_exec_priv = OPTION_OFF;
3583 if (file_data->stat.st_mode & S_IROTH) oth_read_priv = READ_PRIV;
3584 else oth_read_priv = OPTION_OFF;
3586 if (file_data->stat.st_mode & S_IWOTH) oth_write_priv = WRITE_PRIV;
3587 else oth_write_priv = OPTION_OFF;
3589 if (file_data->stat.st_mode & S_IXOTH) oth_exec_priv = EXEC_PRIV;
3590 else oth_exec_priv = OPTION_OFF;
3593 long_modify_time = file_data->stat.st_mtime;
3595 tms = localtime(&long_modify_time);
3596 strftime( time_string, 100,
3597 GetSharedMessage(DIRECTORY_DATE_FORMAT),
3600 time_string = ctime ((time_t *)&long_modify_time);
3601 time_string[strlen(time_string)-1] = 0x0;
3605 /* Fill in the name of where the link goes */
3606 if (file_data->link)
3608 strcpy( link_path, " -> " );
3609 strcpy( link_path + 4, file_data->link );
3613 #define ELLIPSIS " (...) "
3614 #define NAME_PRECISION 28
3617 int len = strlen( file_data->file_name );
3618 if( len > NAME_PRECISION )
3621 char name[NAME_PRECISION];
3622 sprintf( name, "%-20.20s%s", file_data->file_name, ELLIPSIS);
3624 sprintf( long_name, "%-28.28s %s %9ld %c%c%c%c%c%c%c%c%c%c %-9s %-9s %s",
3627 (long)file_data->stat.st_size,
3629 usr_read_priv, usr_write_priv, usr_exec_priv,
3630 grp_read_priv, grp_write_priv, grp_exec_priv,
3631 oth_read_priv, oth_write_priv, oth_exec_priv,
3632 user_name, group_name,
3637 sprintf( long_name, "%-28.28s %s %9ld %c%c%c%c%c%c%c%c%c%c %-9s %-9s %s",
3638 file_data->file_name,
3640 (long)file_data->stat.st_size,
3642 usr_read_priv, usr_write_priv, usr_exec_priv,
3643 grp_read_priv, grp_write_priv, grp_exec_priv,
3644 oth_read_priv, oth_write_priv, oth_exec_priv,
3645 user_name, group_name,
3651 * sprintf() counts width in bytes (not characters), moreover,
3652 * it fails (returns -1 and produces no output) if input string is not
3653 * a valid multibyte string (at least the glibc version), but we can't fail
3654 * to display a file because it's name has some invalid characters). So it looks
3655 * that instead of using sprintf() we have to format the file name part manually.
3657 int len = DtCharCount( file_data->file_name );
3658 int copy_len = len > NAME_PRECISION ? NAME_PRECISION - sizeof(ELLIPSIS) + 1 : len;
3663 /* properly copy copy_len characters of the multibyte string
3664 replacing invalid chars with '?' */
3666 (count < copy_len) && *(file_data->file_name + byte_len);
3669 int chr_bytes = mblen(file_data->file_name + byte_len, MB_CUR_MAX);
3672 strncpy(long_name + byte_len, file_data->file_name + byte_len, chr_bytes);
3674 else if (chr_bytes < 0)
3675 { /* invalid char */
3677 long_name[byte_len]='?';
3681 /* null-wide character, won't really happen */
3684 byte_len+=chr_bytes;
3688 /* truncated name, add ellipsis */
3689 strncpy(long_name + byte_len, ELLIPSIS, sizeof(ELLIPSIS) - 1);
3690 byte_len+= sizeof(ELLIPSIS) - 1;
3694 /* full name, pad it with spaces up to the proper length */
3695 for (; count < NAME_PRECISION ; count++)
3697 long_name[byte_len++]=' ';
3700 sprintf( long_name + byte_len, " %s %9ld %c%c%c%c%c%c%c%c%c%c %-9s %-9s %s",
3702 (long)file_data->stat.st_size,
3704 usr_read_priv, usr_write_priv, usr_exec_priv,
3705 grp_read_priv, grp_write_priv, grp_exec_priv,
3706 oth_read_priv, oth_write_priv, oth_exec_priv,
3707 user_name, group_name,
3709 } /* is_multibyte */
3717 /*--------------------------------------------------------------------
3719 * See if path has a directory view of it or if any sub-directories
3720 * of it are viewed. The path parameter is of the for /foo/bar
3721 *------------------------------------------------------------------*/
3727 FileMgrData * file_mgr_data;
3728 FileViewData * sub_root;
3730 int len = strlen(path);
3732 for (i = 0; i < directory_count; i++)
3734 /* check if this directory is equal to 'path' or a subdir of 'path' */
3735 if (directory_set[i]->viewed &&
3736 (strcmp (directory_set[i]->path_name, path) == 0 ||
3737 strncmp (directory_set[i]->path_name, path,len) == 0 &&
3738 directory_set[i]->path_name[len] == '/'
3740 directory_set[i]->tt_path_name != NULL &&
3741 (strcmp (directory_set[i]->tt_path_name, path) == 0 ||
3742 strncmp (directory_set[i]->tt_path_name, path,len) == 0 &&
3743 directory_set[i]->tt_path_name[len] == '/')))
3745 /* check the views in the view list */
3746 for (j = 0; j < directory_set[i]->numOfViews; j++)
3748 file_mgr_data = directory_set[i]->directoryView[j].file_mgr_data;
3750 /* find the dir in the directory set for this view */
3751 for (k = 0; k < file_mgr_data->directory_count; k++)
3752 if (strcmp(file_mgr_data->directory_set[k]->name,
3753 directory_set[i]->directory_name) == 0)
3757 if (k == file_mgr_data->directory_count)
3758 continue; /* not found ... something must be wrong! */
3761 * Check if this directory is acutally visible.
3762 * If the directory is in a tree branch that is not currently
3763 * expanded, it is not visible and would not be considered busy.
3766 /* the tree root is always considered busy */
3770 /* a subdir is considered busy if it is visible and at least
3771 * partially expanded */
3772 sub_root = file_mgr_data->directory_set[k]->sub_root;
3773 if (sub_root->displayed &&
3774 (sub_root->ts == tsDirs && sub_root->ndir > 0 ||
3775 sub_root->ts == tsAll &&
3776 sub_root->ndir + sub_root->nfile > 0))
3788 /*--------------------------------------------------------------------
3789 * GetDirectoryLogicalType
3790 * Get logical type for the iconic path.
3791 *------------------------------------------------------------------*/
3794 GetDirectoryLogicalType(
3795 FileMgrData *file_mgr_data,
3800 Directory *directory;
3803 /* 'path' must be a prefix of the current directory */
3805 if (strncmp(file_mgr_data->current_directory, path, len) != 0 ||
3807 file_mgr_data->current_directory[len] != '/' &&
3808 file_mgr_data->current_directory[len] != '\0'))
3810 DPRINTF(("GetDirectoryLogicalType(%s): len %d, cur_dir %s\n",
3811 path, len, file_mgr_data->current_directory));
3815 /* Find the directory set entry. */
3816 directory = FindDirectory(file_mgr_data->host,
3817 file_mgr_data->current_directory);
3818 if ((directory != NULL) &&
3819 (strcmp(file_mgr_data->current_directory,
3820 directory->directory_name) == 0))
3822 /* if we don't have path_logical_types yet, we don't know */
3823 if (directory->path_logical_types == NULL)
3826 /* count the number of components in path */
3827 if (strcmp(path, "/") == 0)
3833 while ((ptr = DtStrchr(ptr, '/')) != NULL)
3843 DPRINTF2(("GetDirectoryLogicalType(%s): n %d, type %s\n",
3844 path, n, directory->path_logical_types[n]));
3846 /* return type form path_logical_types array */
3847 return directory->path_logical_types[n];
3850 /* directory not found in directory_set */
3856 /*====================================================================
3858 * Routines for accessing position information
3860 *==================================================================*/
3862 /*--------------------------------------------------------------------
3863 * GetDirectoryPositionInfo
3864 * Get cached position info
3865 *------------------------------------------------------------------*/
3868 GetDirectoryPositionInfo(
3870 char *directory_name,
3871 PositionInfo **position_info)
3873 Directory *directory;
3875 directory = FindDirectory(host_name, directory_name);
3876 if (directory == NULL)
3879 *position_info = directory->position_info;
3881 return directory->position_count;
3885 /*--------------------------------------------------------------------
3886 * WritePosInfoProcess
3887 * Main routine of the background process that writes the
3888 * postion information file.
3889 *------------------------------------------------------------------*/
3892 WritePosInfoProcess(
3894 Directory *directory,
3895 ActivityStatus activity)
3898 int position_count = directory->position_count;
3899 PositionInfo *position_info = directory->position_info;
3902 Tt_status tt_status;
3904 /* construct the full file name */
3905 fileName = ResolveLocalPathName( directory->host_name,
3906 directory->directory_name, positionFileName,
3907 home_host_name, &tt_status );
3908 /* Don't have to check for tt_status
3909 directory->host_name is home_host_name and ResolveLocalPathName will
3910 always return a good path
3912 DPRINTF(("WritePosInfoProcess: count %d, file %s\n",
3913 position_count, fileName));
3915 /* Remove old files, if no position information for this view */
3916 if (position_count <= 0)
3917 rc = unlink(fileName);
3920 /* open the file for writing */
3921 f = fopen(fileName, "w");
3925 /* Assume read-only directory, if we can't open the file */
3930 chmod(fileName, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
3932 fprintf(f, "%d\n", position_count);
3933 for (i = 0; i < position_count; i++)
3935 fprintf(f, "%s\n%d %d %d\n",
3936 position_info[i].name,
3939 position_info[i].stacking_order);
3947 /* send result back thorugh the pipe */
3948 DPRINTF(("WritePosInfoProcess: done (rc %d)\n", rc));
3949 write(pipe_fd, &rc, sizeof(int));
3955 /*--------------------------------------------------------------------
3956 * WritePosInfoPipeCallback
3957 * Callback routine that reads the return code sent through the
3958 * pipe from the WritePosInfoProcess background process.
3959 *------------------------------------------------------------------*/
3962 WritePosInfoPipeCallback(
3963 XtPointer client_data,
3967 PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
3968 Directory *directory = pipe_data->directory;
3972 /* get return code from the pipe */
3974 PipeRead(*fd, &rc, sizeof(int));
3976 /* close the pipe and cancel the callback */
3978 XtFree( client_data );
3981 /* verify that the directory still exists */
3982 if (DirectoryGone(directory))
3984 ScheduleActivity(NULL);
3988 DPRINTF(("WritePosInfoPipeCallback: rc %d\n", rc));
3990 /* reset the busy flag and schedule new work, if any */
3991 directory->busy[activity_writing_posinfo] = False;
3992 directory->activity = activity_idle;
3993 ScheduleActivity(directory);
3997 /*--------------------------------------------------------------------
3998 * SetDirectoryPositionInfo
3999 * Update cached position info. This routine schedules a
4000 * background process that writes the modified information
4001 * to the position info file.
4002 *------------------------------------------------------------------*/
4005 SetDirectoryPositionInfo(
4007 char *directory_name,
4009 PositionInfo *position_info)
4011 Directory *directory;
4015 /* find the directory */
4016 directory = FindDirectory(host_name, directory_name);
4017 if (directory == NULL)
4020 /* check if anything has changed */
4021 if (directory->position_count == position_count)
4024 for (i = 0; i < position_count && unchanged; i++)
4026 for (j = 0; j < position_count; j++)
4027 if (strcmp(position_info[i].name,
4028 directory->position_info[j].name) == 0)
4033 if (j == position_count ||
4034 position_info[i].x != directory->position_info[j].x ||
4035 position_info[i].y != directory->position_info[j].y ||
4036 position_info[i].stacking_order !=
4037 directory->position_info[j].stacking_order)
4043 /* if nothing changed, don't do anything */
4048 /* free old position info names*/
4049 for (i = 0; i < directory->position_count; i++)
4050 XtFree(directory->position_info[i].name);
4052 /* realloc array, if necessary */
4053 if (directory->position_count != position_count)
4055 directory->position_count = position_count;
4056 directory->position_info =
4057 (PositionInfo *) XtRealloc((char *)directory->position_info,
4058 position_count * sizeof(PositionInfo));
4061 /* replace old position info */
4062 for (i = 0; i < position_count; i++)
4064 directory->position_info[i].name = XtNewString(position_info[i].name);
4065 directory->position_info[i].x = position_info[i].x;
4066 directory->position_info[i].y = position_info[i].y;
4067 directory->position_info[i].stacking_order =
4068 position_info[i].stacking_order;
4071 /* make sure positionFileName is initialized */
4072 if (positionFileName == NULL)
4073 InitializePositionFileName();
4075 /* start background process that writes the position info file */
4076 directory->busy[activity_writing_posinfo] = True;
4077 ScheduleActivity(directory);
4083 /*====================================================================
4086 * These function are periodically called to scan all cached
4087 * directories to see if any have been modified since last read.
4088 * If any have, a background process is scheduled to re-read
4091 *==================================================================*/
4093 /*--------------------------------------------------------------------
4095 * Decide whether to skip an automatic re-read.
4096 * (We don't do re-reads on directories that aren't currently
4097 * being viewed and on the trash directory, if trash is currently
4099 *------------------------------------------------------------------*/
4103 Directory *directory)
4107 /* don't refresh while the directory is being modified */
4108 if (directory->modify_begin > 0)
4111 /* verify that the directory is still being viewed */
4112 if (!directory->viewed)
4115 for (i = 0; i < directory->numOfViews; i++)
4116 if (directory->directoryView[i].mapped)
4118 if (i == directory->numOfViews)
4121 /* if trash is being emptied and this is the trash dir, skip it */
4122 if (removingTrash && strcmp(directory->directory_name, trash_dir) == 0)
4129 /*--------------------------------------------------------------------
4131 * Main routine for the background process that checks directory
4132 * timestamps and the status of links.
4133 *------------------------------------------------------------------*/
4138 Directory *directory,
4139 ActivityStatus activity)
4141 struct stat stat_buf;
4143 Boolean link_changed;
4145 FileData *file_data;
4146 char full_name[MAX_PATH];
4155 * Do a stat on the directory to get its last-modified time.
4156 * Also check if we still have read and execute/search permisssion.
4159 * It is important to get the timstamp in exactly the same way that the
4160 * ReadDirectoryProcess does it; otherwise, we might get into a loop,
4161 * where TimerEventProcess detects that the directory has changed
4162 * and triggers ReadDirectoryProcess, but ReadDirectoryProcess won't
4163 * be able to get a new timestamp and update the directory structure,
4164 * so the next time TimerEventProcess runs it will trigger another
4165 * ReadDirectoryProcess, and so on ...
4167 if (CheckAccess(directory->path_name, R_OK | X_OK) != 0 ||
4168 stat(directory->path_name, &stat_buf) != 0)
4170 /* stat or access failed */
4176 /* stat succeeded and the directory is still readable */
4178 modify_time = stat_buf.st_mtime;
4182 * If requested, also check if any links broken.
4184 * Again: it is important that we determine the kind of link
4185 * (valid, recursive, or broken) in exactly the same way that
4186 * ReadDirectoryProcess does it (see comment above)!
4188 link_changed = False;
4189 if (rc == 0 && activity == activity_checking_links)
4191 strcpy(full_name, directory->path_name);
4192 namep = full_name + strlen(full_name);
4193 if (namep[-1] != '/')
4196 /* check all links */
4197 for (file_data = directory->file_data;
4198 file_data && !link_changed;
4199 file_data = file_data->next)
4201 /* Only worry about links */
4202 if (file_data->link == NULL)
4205 /* Check if the file is still a symbolic link */
4206 strcpy(namep, file_data->file_name);
4207 link_rc = lstat(full_name, &stat_buf);
4208 if (link_rc != 0 || (stat_buf.st_mode & S_IFMT) != S_IFLNK)
4210 /* no longer a link */
4211 link_changed = True;
4215 /* Check what kind of link this was the last time we looked:
4216 * a normal link (1), a recursive link (2), or an otherwise
4217 * broken link (3) */
4218 if (strcmp(file_data->logical_type, LT_BROKEN_LINK) == 0)
4220 else if (strcmp(file_data->logical_type, LT_RECURSIVE_LINK) == 0)
4225 /* Check what kind of link it is now */
4226 if (_DtFollowLink(full_name) == NULL)
4227 cur_link_kind = 2; /* recursive link */
4228 else if (stat(full_name, &stat_buf) != 0)
4229 cur_link_kind = 3; /* broken link */
4231 cur_link_kind = 1; /* a valid link */
4233 /* now we can tell if the link has changed */
4234 if (prev_link_kind != cur_link_kind)
4235 link_changed = True;
4239 /* send result back through the pipe */
4240 write(pipe_fd, &rc, sizeof(int));
4241 write(pipe_fd, &modify_time, sizeof(long));
4242 write(pipe_fd, &link_changed, sizeof(Boolean));
4247 /*--------------------------------------------------------------------
4249 * Mark sticky background process as idle. If there are too
4250 * may idle sticky procs, cause some of them to exit.
4251 *------------------------------------------------------------------*/
4255 ActivityStatus activity,
4256 StickyProcDesc *sticky_proc,
4259 StickyProcDesc *p, **lp;
4262 /* mark the process as idle */
4263 sticky_proc->idle = True;
4265 /* if there are too many idle procs, make some of them go away */
4267 lp = &ActivityTable[activity].sticky_procs;
4268 for (p = *lp; p; p = *lp)
4272 else if (n < max_procs)
4279 DPRINTF2(("StickyProcIdle: end sticky proc %ld\n", (long)p->child));
4280 PipeWriteString(p->pipe_m2s_fd, NULL);
4281 close(p->pipe_s2m_fd);
4282 close(p->pipe_m2s_fd);
4290 /*--------------------------------------------------------------------
4292 * Callback routine that reads information sent through the
4293 * pipe from the TimerEventProcess background process.
4294 *------------------------------------------------------------------*/
4298 XtPointer client_data,
4302 PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
4303 Directory *directory = pipe_data->directory;
4306 Boolean link_changed;
4309 /* get return code from the pipe */
4311 PipeRead(*fd, &rc, sizeof(int));
4312 PipeRead(*fd, &modify_time, sizeof(long));
4313 PipeRead(*fd, &link_changed, sizeof(Boolean));
4315 /* close the pipe and cancel the callback */
4316 if (pipe_data->sticky_proc)
4317 StickyProcIdle(pipe_data->activity, pipe_data->sticky_proc,
4318 maxRereadProcsPerTick);
4321 XtFree( client_data );
4324 /* verify that the directory still exists */
4325 if (DirectoryGone(directory))
4327 ScheduleActivity(NULL);
4332 "TimerPipeCallback: rc %d (was %d), time %ld (was %ld), link change %d\n",
4333 rc, directory->errnum, modify_time, (long)directory->modify_time, link_changed));
4335 /* reset the busy flag and schedule new work, if any */
4336 directory->busy[directory->activity] = False;
4337 directory->activity = activity_idle;
4338 ScheduleActivity(directory);
4340 /* if directory-read already in progress, nothing more to do here */
4341 if (directory->busy[activity_reading] ||
4342 directory->busy[activity_update_all])
4345 /* skip this directory if it is no longer being viewed */
4346 if (SkipRefresh(directory))
4349 /* if the directory was modified or links changed, re-read it */
4354 DPRINTF(("TimerPipeCallback: %s link changed\n",
4355 directory->directory_name));
4356 ReadDirectoryFiles(NULL, directory);
4358 else if (modify_time != directory->modify_time || directory->errnum != 0)
4360 DPRINTF(("TimerPipeCallback: %s modified\n",
4361 directory->directory_name));
4362 directory->busy[activity_update_all] = True;
4363 ScheduleActivity(directory);
4368 if (directory->errnum == 0)
4370 directory->errnum = rc;
4371 directory->errmsg_needed = True;
4372 ReadDirectoryFiles(NULL, directory);
4378 /*--------------------------------------------------------------------
4379 * CheckDesktopProcess
4380 * Main routine for the background process that checks each desktop
4381 * objects to see if the file that it refers to has disappeared
4382 * or has changed type.
4383 *------------------------------------------------------------------*/
4386 CheckDesktopProcess(
4388 Directory *directory,
4389 ActivityStatus activity)
4392 DesktopRec *desktopWindow;
4393 FileViewData *file_view_data;
4395 Tt_status tt_status;
4396 struct stat stat_buf;
4398 FileData2 file_data2;
4399 FileData *old_data, *new_data;
4401 for (i = 0; i < desktop_data->numIconsUsed; i++)
4403 desktopWindow = desktop_data->desktopWindows[i];
4404 file_view_data = desktopWindow->file_view_data;
4406 full_path = ResolveLocalPathName( desktopWindow->host,
4407 desktopWindow->dir_linked_to,
4408 desktopWindow->file_name,
4409 home_host_name, &tt_status);
4411 /* Check if the file still exists */
4413 if (lstat(full_path, &stat_buf) < 0)
4415 /* the real file no longer exists */
4417 "CheckDesktopProcess: sending PIPEMSG_DESKTOP_REMOVED for %s\n",
4419 pipe_msg = PIPEMSG_DESKTOP_REMOVED;
4420 write(pipe_fd, &pipe_msg, sizeof(short));
4421 PipeWriteString(pipe_fd, desktopWindow->host);
4422 PipeWriteString(pipe_fd, desktopWindow->dir_linked_to);
4423 PipeWriteString(pipe_fd, desktopWindow->file_name);
4428 /* See if the type has changed */
4429 old_data = file_view_data->file_data;
4431 if(directory->directoryView && directory->directoryView->file_mgr_data)
4432 IsToolBox = directory->directoryView->file_mgr_data->toolbox;
4436 ReadFileData2(&file_data2, full_path, NULL,IsToolBox);
4437 new_data = FileData2toFileData(&file_data2, &n);
4439 if (new_data->physical_type != old_data->physical_type ||
4440 strcmp(new_data->logical_type, old_data->logical_type) != 0)
4442 /* the type has changed */
4444 "CheckDesktopProcess: sending PIPEMSG_DESKTOP_CHANGED for %s\n",
4447 " old type %d %s, new type %d %s\n",
4448 old_data->physical_type, old_data->logical_type,
4449 new_data->physical_type, new_data->logical_type));
4451 pipe_msg = PIPEMSG_DESKTOP_CHANGED;
4452 write(pipe_fd, &pipe_msg, sizeof(short));
4453 PipeWriteString(pipe_fd, desktopWindow->host);
4454 PipeWriteString(pipe_fd, desktopWindow->dir_linked_to);
4455 PipeWriteString(pipe_fd, desktopWindow->file_name);
4457 PipeWriteFileData(pipe_fd, new_data);
4460 FreeFileData(new_data, True);
4466 /* send a 'done' msg through the pipe */
4467 DPRINTF2(("CheckDesktopProcess: sending DONE\n"));
4468 pipe_msg = PIPEMSG_DONE;
4469 write(pipe_fd, &pipe_msg, sizeof(short));
4474 /*--------------------------------------------------------------------
4475 * CheckDesktopPipeCallback
4476 * Callback routine that reads information sent through the
4477 * pipe from the CheckDesktopProcess background process.
4478 *------------------------------------------------------------------*/
4481 CheckDesktopPipeCallback(
4482 XtPointer client_data,
4486 PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
4487 Directory *directory = pipe_data->directory;
4489 char *host, *dir_linked_to, *file_name;
4490 FileData *new_data, *old_data;
4492 DesktopRec *desktopWindow;
4495 /* read the next msg from the pipe */
4497 n = PipeRead(*fd, &msg, sizeof(short));
4499 if (msg == PIPEMSG_DESKTOP_REMOVED ||
4500 msg == PIPEMSG_DESKTOP_CHANGED)
4502 /* get information from pipe */
4503 host = PipeReadString(*fd);
4504 dir_linked_to = PipeReadString(*fd);
4505 file_name = PipeReadString(*fd);
4506 if (msg == PIPEMSG_DESKTOP_CHANGED)
4507 new_data = PipeReadFileData(*fd);
4512 "CheckDesktopPipeCallback: msg %d: host %s, dir %s, name %s\n",
4513 msg, host, dir_linked_to, file_name));
4516 /* find the desktop object */
4518 for (i = 0; i < desktop_data->numIconsUsed; i++)
4520 desktopWindow = desktop_data->desktopWindows[i];
4522 if (strcmp(host, desktopWindow->host) == 0 &&
4523 strcmp(dir_linked_to, desktopWindow->dir_linked_to) == 0 &&
4524 strcmp(file_name, desktopWindow->file_name) == 0)
4531 /* remove or update the desktop object, if found */
4536 else if (msg == PIPEMSG_DESKTOP_REMOVED)
4538 /* remove the desktop object */
4539 DesktopObjectRemoved(desktopWindow);
4541 else /* msg == PIPEMSG_DESKTOP_CHANGED */
4543 /* replace file data */
4544 old_data = desktopWindow->file_view_data->file_data;
4545 FreeFileData(old_data, False);
4546 memcpy(old_data, new_data, sizeof(FileData));
4547 XtFree((char *)new_data);
4550 /* update the desktop object */
4551 DesktopObjectChanged(desktopWindow);
4556 XtFree(dir_linked_to);
4559 FreeFileData(new_data, True);
4562 else if (msg == PIPEMSG_DONE)
4564 /* close the pipe and cancel the callback */
4566 XtFree( client_data );
4569 /* reset the busy flag and schedule new work, if any */
4570 directory->busy[directory->activity] = False;
4571 directory->activity = activity_idle;
4572 ScheduleActivity(directory);
4577 /*--------------------------------------------------------------------
4580 * Arrange for a CheckDesktopProcess background process to be
4581 * started (checks each desktop objects to see if the file that
4582 * it refers to has disappeared or has changed type).
4584 *------------------------------------------------------------------*/
4587 CheckDesktop( void )
4589 dummy_directory->busy[activity_checking_desktop] = True;
4590 ScheduleActivity(dummy_directory);
4594 /*--------------------------------------------------------------------
4596 * This routine is called periodically. It schedules a
4597 * TimerEventProcess background process to be started for every
4598 * directory in the cache.
4599 *------------------------------------------------------------------*/
4601 /* comparison routine for qsort */
4607 return directory_set[*p1]->last_check - directory_set[*p2]->last_check;
4613 XtPointer client_data,
4616 static int *check_list = NULL;
4617 static int check_alloc = 0;
4621 DPRINTF2(("Directory::TimerEvent\n"));
4626 * Don't change any directories while a drag is active.
4628 * Reason: drag callbacks are called with a pointer to a FileViewData
4629 * structure; if a directory is reread while a drag is active,
4630 * the pointer would become invalid, causing unpredictable behavior.
4632 * Schedule the next TimerEvent in 1/2 second, so that check will
4633 * be done soon after the drag is finished.
4635 XtAppAddTimeOut (app_context, 500, TimerEvent, NULL);
4639 /* update tick count */
4642 /* determine if we should also check for broken links this time */
4643 if (checkBrokenLink > 0 &&
4644 tick_count >= lastLinkCheckTick + ticksBetweenLinkChecks)
4646 /* set link_check_needed flag on all directores */
4647 for (i = 0; i < directory_count; i++)
4649 /* skip this directory if no view is mapped */
4650 if (SkipRefresh(directory_set[i]))
4653 /* if a check is already in progress, don't start another one */
4654 if (directory_set[i]->busy[activity_checking_links])
4657 /* arrange for background process to be scheduled */
4658 directory_set[i]->link_check_needed = True;
4661 lastLinkCheckTick = tick_count;
4664 /* make sure check_list array is big enough */
4665 if (directory_count > check_alloc)
4667 check_alloc = directory_count + 5;
4669 (int *)XtRealloc((char *)check_list, check_alloc*sizeof(int));
4672 /* get a list of all directories that need to be checked */
4674 for (i = 0; i < directory_count; i++)
4676 /* skip this directory if no view is mapped */
4677 if (SkipRefresh(directory_set[i]))
4680 /* if a stat is already in progress, don't start another one */
4681 if (directory_set[i]->busy[activity_checking_dir] ||
4682 directory_set[i]->busy[activity_checking_links])
4685 /* add this directory to the check list */
4686 check_list[n++] = i;
4690 * Next we want to schedule a background process to be started
4691 * for each directory in the check_list. However, the variable
4692 * maxRereadProcsPerTick puts a limit on the number of such
4693 * background processes started per clock tick (i.e., per call
4694 * to this routine). Hence we sort check_list by last_check
4695 * (records the tick count when a directory was last read or
4696 * checked) and schedule backround processes on those dirs that
4697 * haven't been checked in the longest time.
4699 qsort(check_list, n, sizeof(int), (int (*)())CheckListCmp);
4701 /* arrange for background process to be started */
4702 for (j = 0; j < n && j < maxRereadProcsPerTick; j++)
4705 if (directory_set[i]->link_check_needed)
4707 directory_set[i]->link_check_needed = False;
4708 directory_set[i]->busy[activity_checking_links] = True;
4711 directory_set[i]->busy[activity_checking_dir] = True;
4712 ScheduleActivity(directory_set[i]);
4713 directory_set[i]->last_check = tick_count;
4716 /* Reset the timeout for the next interval. */
4717 if (SomeWindowMapped())
4718 XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
4720 timer_suspended = True;
4724 /*--------------------------------------------------------------------
4725 * TimerEventBrokenLinks
4726 * This routine is called periodically. It checks whether any
4727 * desktop object is broken (i.e., the object it refers to no
4729 *------------------------------------------------------------------*/
4732 TimerEventBrokenLinks(
4733 XtPointer client_data,
4738 DPRINTF2(("Directory::TimerEventBrokenLinks\n"));
4742 /* go check the desktop objects */
4743 if (desktop_data->numIconsUsed > 0)
4747 /* Reset the timeout for the next interval. */
4748 if (desktop_data->numIconsUsed > 0)
4750 checkBrokenLinkTimerId = XtAppAddTimeOut( app_context,
4751 checkBrokenLink * 1000,
4752 TimerEventBrokenLinks,
4757 checkBrokenLinkTimerId = None;
4762 /*====================================================================
4764 * Background process scheduler
4766 * The routines below schedule background activity, making sure
4767 * that there aren't too many processes running at the same time.
4769 *==================================================================*/
4771 /*--------------------------------------------------------------------
4772 * ScheduleDirectoryActivity
4773 * If there is any work to do for a directory, and if there is
4774 * no backgroud process currently running for that directory,
4775 * then fork a process to do the work.
4776 *------------------------------------------------------------------*/
4779 ScheduleDirectoryActivity(
4780 Directory *directory)
4782 static char *pname = "ScheduleActivity";
4783 ActivityStatus activity;
4784 PipeCallbackData *pipe_data;
4785 Boolean all_views_active;
4786 Boolean this_view_active;
4788 int n_active, n_checking;
4789 int save_last_check = 0;
4790 FileMgrData *file_mgr_data;
4793 int pipe_s2m_fd[2] = {-1, -1}; /* for msgs from backgroundnd proc (slave to master) */
4794 int pipe_m2s_fd[2] = {-1, -1}; /* for msgs to backgroundnd proc (master to slave) */
4799 /* If already active, don't start anything new. */
4800 if (directory->activity != activity_idle)
4803 /* Decide what to do next */
4804 for (activity = 0; activity < activity_idle; activity++)
4805 if (directory->busy[activity])
4808 /* If nothing to do, return */
4809 if (activity == activity_idle)
4812 DPRINTF2(("ScheduleActivity: activity %d, busy %c%c%c%c%c%c%c, dir %s\n",
4813 directory->activity,
4814 directory->busy[activity_writing_posinfo]? 'W': '-',
4815 directory->busy[activity_reading]? 'R': '-',
4816 directory->busy[activity_update_all]? 'A': '-',
4817 directory->busy[activity_update_some]? 'U': '-',
4818 directory->busy[activity_checking_links]? 'B': '-',
4819 directory->busy[activity_checking_desktop]? 'D': '-',
4820 directory->busy[activity_checking_dir]? 'C': '-',
4821 directory->directory_name));
4823 /* Don't start more than a certain number of background processed */
4826 for (j = 0; j < directory_count; j++)
4828 if (directory_set[j]->activity != activity_idle)
4830 if (directory_set[j]->activity == activity_checking_links ||
4831 directory_set[j]->activity == activity_checking_dir)
4834 if (dummy_directory->activity != activity_idle)
4839 if (n_active >= maxDirectoryProcesses ||
4840 n_checking >= maxRereadProcesses)
4842 DPRINTF2(("ScheduleActivity: too many processes\n"));
4847 * We don't want to start more than one background process per view.
4848 * In tree mode one view may show more than one directory.
4849 * Hence we go through the view list for this directory and for each
4850 * view, we check if the same view appears on the view list of some
4851 * other directory that currently has active background activity.
4852 * If all vies on this directory have other activity, then we won't
4853 * start anything new.
4855 if (directory->numOfViews > 0)
4857 all_views_active = True;
4858 for (i = 0; i < directory->numOfViews; i++)
4860 /* get file_mgr_data for this view */
4861 file_mgr_data = directory->directoryView[i].file_mgr_data;
4863 /* see if the same view appears in the view list of a non-idle dir */
4864 this_view_active = False;
4865 for (j = 0; j < directory_count && !this_view_active; j++)
4867 /* we are only interested in directories that are not idle */
4868 if (directory_set[j]->activity == activity_idle)
4871 /* see if the view appears in the view list */
4872 for (k = 0; k < directory_set[j]->numOfViews; k++)
4874 if (directory_set[j]->directoryView[k].file_mgr_data ==
4877 this_view_active = True;
4883 if (!this_view_active)
4885 all_views_active = False;
4890 if (all_views_active)
4892 DPRINTF2(("ScheduleActivity: all views busy\n"));
4897 /* now we are ready to start the next activity */
4898 directory->activity = activity;
4899 if (activity == activity_reading ||
4900 activity == activity_update_all ||
4901 activity == activity_checking_dir ||
4902 activity == activity_checking_links)
4904 save_last_check = directory->last_check;
4905 directory->last_check = tick_count;
4909 * Special optimization for periodic background processes
4910 * (currently only used for activity_checking_dir):
4911 * Since this is done frequently, we don't want to fork new process each
4912 * time. Hence, instead of exiting when it's done, the background process
4913 * is "sticky", i.e., it will stay around waiting for a message on stdin,
4914 * so it can be re-used the next time around. A linked list of sticky
4915 * procs that are currently active is maintained in the ActivityTable.
4917 sticky = ActivityTable[activity].sticky;
4920 /* see if we can find an idle sticky proc that can do the work */
4921 for (p = ActivityTable[activity].sticky_procs; p; p = p->next)
4930 /* We found an idle sticky proc that can be used */
4931 DPRINTF2(("ScheduleActivity: use sticky proc %ld\n", (long)p->child));
4933 /* Send the directory name to the sticky proc */
4935 if (PipeWriteString(p->pipe_m2s_fd, directory->path_name) < 0) {
4938 /* the pipe is broken, remove the old proc then start a new one */
4939 for (d = ActivityTable[activity].sticky_procs; d && p; d = d->next) {
4942 /* the proc listed 1st is dead, remove it */
4943 ActivityTable[activity].sticky_procs = p->next;
4947 else if (d->next == p)
4949 /* the process "p" is dead, remove it */
4958 pipe_s2m_fd[0] = p->pipe_s2m_fd;
4967 /* Need to fork a new background process */
4969 /* create a pipe for reading data from the background proc */
4972 /* creating a new sticky proc? */
4975 /* also need a pipe for sending msgs to the sticky proc */
4978 /* add entry to the list of sticky procs */
4979 p = (StickyProcDesc *) XtMalloc(sizeof(StickyProcDesc));
4980 p->next = ActivityTable[activity].sticky_procs;
4981 ActivityTable[activity].sticky_procs = p;
4983 p->pipe_s2m_fd = pipe_s2m_fd[0];
4984 p->pipe_m2s_fd = pipe_m2s_fd[1];
4988 /* fork a background process */
4993 DBGFORK(("%s: fork failed for activity %d: %s\n",
4994 pname, activity, strerror(errno)));
4997 "%s: fork failed, ppid %d, pid %d, activity %d: error %d=%s\n",
4998 pname, getppid(), getpid(), activity, errno, strerror(errno));
5000 directory->activity = activity_idle;
5001 directory->last_check = save_last_check;
5003 /* close unused pipe connections */
5004 close(pipe_s2m_fd[0]); /* child won't read from this pipe */
5005 close(pipe_s2m_fd[1]); /* parent won't write to this pipe */
5008 close(pipe_m2s_fd[1]); /* child won't write to this pipe */
5009 close(pipe_m2s_fd[0]); /* parent won't read from this pipe */
5021 DBGFORK(("%s: child activity %d, s2m %d, m2s %d\n",
5022 pname, activity, pipe_s2m_fd[1], pipe_m2s_fd[0]));
5024 /* close unused pipe connections */
5025 close(pipe_s2m_fd[0]); /* child won't read from this pipe */
5027 close(pipe_m2s_fd[1]); /* child won't write to this pipe */
5029 /* run main routine for this activity from ActivityTable */
5032 rc = (*ActivityTable[activity].main)(pipe_s2m_fd[1],
5033 directory, activity);
5034 if (!sticky || rc != 0)
5037 /* wait for a message in the pipe */
5038 s = PipeReadString(pipe_m2s_fd[0]);
5042 XtFree(directory->path_name);
5043 directory->path_name = s;
5045 DPRINTF2(("StickyActivity: activity %d, dir %s\n", activity, s));
5048 /* close pipes and end this process */
5049 close(pipe_s2m_fd[1]);
5051 close(pipe_m2s_fd[0]);
5053 DBGFORK(("%s: completed activity %d, (rc %d)\n",pname, activity, rc));
5058 DBGFORK(("%s: forked child<%d> for activity %d, s2m %d, m2s %d\n",
5059 pname, pid, activity, pipe_s2m_fd[0], pipe_m2s_fd[1]));
5061 /* parent process */
5066 * If a directory read or update was started:
5067 * clear the modifile_list, now that the
5068 * background process has it's own copy.
5070 if (activity == activity_reading ||
5071 activity == activity_update_all ||
5072 activity == activity_update_some)
5074 for (i = 0; i < directory->modified_count; i++)
5075 XtFree(directory->modified_list[i]);
5076 XtFree((char *)directory->modified_list);
5078 directory->modified_count = 0;
5079 directory->modified_list = NULL;
5082 /* close unused pipe connections */
5083 close(pipe_s2m_fd[1]); /* parent won't write to this pipe */
5085 close(pipe_m2s_fd[0]); /* parent won't read from this pipe */
5089 /* set up callback to get the pipe data */
5090 DPRINTF2(("ScheduleActivity: setting up pipe callback\n"));
5091 pipe_data = (PipeCallbackData *)XtMalloc(sizeof(PipeCallbackData));
5092 pipe_data->directory = directory;
5093 pipe_data->child = pid;
5094 pipe_data->sticky_proc = p;
5095 pipe_data->activity = activity;
5097 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
5098 pipe_s2m_fd[0], (XtPointer)XtInputReadMask,
5099 ActivityTable[activity].callback, (XtPointer)pipe_data);
5103 /*--------------------------------------------------------------------
5105 * See if any new background work should be started.
5106 *------------------------------------------------------------------*/
5110 Directory *directory)
5114 /* first try to schedule new activity for this directory */
5115 if (directory != NULL)
5116 ScheduleDirectoryActivity(directory);
5118 /* see if there is anything else we can schedule now */
5119 if (directory == NULL || directory->activity == activity_idle)
5121 for (i = 0; i < directory_count; i++)
5122 if (directory_set[i] != directory)
5123 ScheduleDirectoryActivity(directory_set[i]);
5125 ScheduleDirectoryActivity(dummy_directory);
5130 FileMgrData *file_mgr_data)
5132 DirectorySet *directory_data;
5133 FileViewData *file_view_data;
5136 directory_data = file_mgr_data->directory_set[0];
5138 for (j = 0; j < directory_data->file_count; j++)
5140 file_view_data = directory_data->file_view_data[j];
5142 if (file_view_data->filtered != True &&
5143 strcmp(file_mgr_data->desktop_file,
5144 file_view_data->file_data->file_name) == 0)
5146 SelectFile (file_mgr_data, file_view_data);
5150 ActivateSingleSelect(file_mgr_data->file_mgr_rec,
5151 file_mgr_data->selection_list[0]->file_data->logical_type);
5152 PositionFileView(file_view_data, file_mgr_data);