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
3619 int len = strlen( file_data->file_name );
3620 if( len > NAME_PRECISION )
3623 char name[NAME_PRECISION];
3624 sprintf( name, "%-20.20s%s", file_data->file_name, ELLIPSIS);
3626 sprintf( long_name, "%-28.28s %s %9ld %c%c%c%c%c%c%c%c%c%c %-9s %-9s %s",
3629 (long)file_data->stat.st_size,
3631 usr_read_priv, usr_write_priv, usr_exec_priv,
3632 grp_read_priv, grp_write_priv, grp_exec_priv,
3633 oth_read_priv, oth_write_priv, oth_exec_priv,
3634 user_name, group_name,
3639 sprintf( long_name, "%-28.28s %s %9ld %c%c%c%c%c%c%c%c%c%c %-9s %-9s %s",
3640 file_data->file_name,
3642 (long)file_data->stat.st_size,
3644 usr_read_priv, usr_write_priv, usr_exec_priv,
3645 grp_read_priv, grp_write_priv, grp_exec_priv,
3646 oth_read_priv, oth_write_priv, oth_exec_priv,
3647 user_name, group_name,
3654 * sprintf() counts width in bytes (not characters), moreover,
3655 * it fails (returns -1 and produces no output) if input string is not
3656 * a valid multibyte string (at least the glibc version), but we can't fail
3657 * to display a file because it's name has some invalid characters). So it looks
3658 * that instead of using sprintf() we have to format the file name part manually.
3660 int len = DtCharCount( file_data->file_name );
3661 int copy_len = len > NAME_PRECISION ? NAME_PRECISION - sizeof(ELLIPSIS) + 1 : len;
3666 /* properly copy copy_len characters of the multibyte string
3667 replacing invalid chars with '?' */
3669 (count < copy_len) && *(file_data->file_name + byte_len);
3672 int chr_bytes = mblen(file_data->file_name + byte_len, MB_CUR_MAX);
3675 strncpy(long_name + byte_len, file_data->file_name + byte_len, chr_bytes);
3677 else if (chr_bytes < 0)
3678 { /* invalid char */
3680 long_name[byte_len]='?';
3684 /* null-wide character, won't really happen */
3687 byte_len+=chr_bytes;
3691 /* truncated name, add ellipsis */
3692 strncpy(long_name + byte_len, ELLIPSIS, sizeof(ELLIPSIS) - 1);
3693 byte_len+= sizeof(ELLIPSIS) - 1;
3697 /* full name, pad it with spaces up to the proper length */
3698 for (; count < NAME_PRECISION ; count++)
3700 long_name[byte_len++]=' ';
3703 sprintf( long_name + byte_len, " %s %9ld %c%c%c%c%c%c%c%c%c%c %-9s %-9s %s",
3705 (long)file_data->stat.st_size,
3707 usr_read_priv, usr_write_priv, usr_exec_priv,
3708 grp_read_priv, grp_write_priv, grp_exec_priv,
3709 oth_read_priv, oth_write_priv, oth_exec_priv,
3710 user_name, group_name,
3712 } /* is_multibyte */
3713 #endif /* MULTIBYTE */
3721 /*--------------------------------------------------------------------
3723 * See if path has a directory view of it or if any sub-directories
3724 * of it are viewed. The path parameter is of the for /foo/bar
3725 *------------------------------------------------------------------*/
3731 FileMgrData * file_mgr_data;
3732 FileViewData * sub_root;
3734 int len = strlen(path);
3736 for (i = 0; i < directory_count; i++)
3738 /* check if this directory is equal to 'path' or a subdir of 'path' */
3739 if (directory_set[i]->viewed &&
3740 (strcmp (directory_set[i]->path_name, path) == 0 ||
3741 strncmp (directory_set[i]->path_name, path,len) == 0 &&
3742 directory_set[i]->path_name[len] == '/'
3744 directory_set[i]->tt_path_name != NULL &&
3745 (strcmp (directory_set[i]->tt_path_name, path) == 0 ||
3746 strncmp (directory_set[i]->tt_path_name, path,len) == 0 &&
3747 directory_set[i]->tt_path_name[len] == '/')))
3749 /* check the views in the view list */
3750 for (j = 0; j < directory_set[i]->numOfViews; j++)
3752 file_mgr_data = directory_set[i]->directoryView[j].file_mgr_data;
3754 /* find the dir in the directory set for this view */
3755 for (k = 0; k < file_mgr_data->directory_count; k++)
3756 if (strcmp(file_mgr_data->directory_set[k]->name,
3757 directory_set[i]->directory_name) == 0)
3761 if (k == file_mgr_data->directory_count)
3762 continue; /* not found ... something must be wrong! */
3765 * Check if this directory is acutally visible.
3766 * If the directory is in a tree branch that is not currently
3767 * expanded, it is not visible and would not be considered busy.
3770 /* the tree root is always considered busy */
3774 /* a subdir is considered busy if it is visible and at least
3775 * partially expanded */
3776 sub_root = file_mgr_data->directory_set[k]->sub_root;
3777 if (sub_root->displayed &&
3778 (sub_root->ts == tsDirs && sub_root->ndir > 0 ||
3779 sub_root->ts == tsAll &&
3780 sub_root->ndir + sub_root->nfile > 0))
3792 /*--------------------------------------------------------------------
3793 * GetDirectoryLogicalType
3794 * Get logical type for the iconic path.
3795 *------------------------------------------------------------------*/
3798 GetDirectoryLogicalType(
3799 FileMgrData *file_mgr_data,
3804 Directory *directory;
3807 /* 'path' must be a prefix of the current directory */
3809 if (strncmp(file_mgr_data->current_directory, path, len) != 0 ||
3811 file_mgr_data->current_directory[len] != '/' &&
3812 file_mgr_data->current_directory[len] != '\0'))
3814 DPRINTF(("GetDirectoryLogicalType(%s): len %d, cur_dir %s\n",
3815 path, len, file_mgr_data->current_directory));
3819 /* Find the directory set entry. */
3820 directory = FindDirectory(file_mgr_data->host,
3821 file_mgr_data->current_directory);
3822 if ((directory != NULL) &&
3823 (strcmp(file_mgr_data->current_directory,
3824 directory->directory_name) == 0))
3826 /* if we don't have path_logical_types yet, we don't know */
3827 if (directory->path_logical_types == NULL)
3830 /* count the number of components in path */
3831 if (strcmp(path, "/") == 0)
3837 while ((ptr = DtStrchr(ptr, '/')) != NULL)
3847 DPRINTF2(("GetDirectoryLogicalType(%s): n %d, type %s\n",
3848 path, n, directory->path_logical_types[n]));
3850 /* return type form path_logical_types array */
3851 return directory->path_logical_types[n];
3854 /* directory not found in directory_set */
3860 /*====================================================================
3862 * Routines for accessing position information
3864 *==================================================================*/
3866 /*--------------------------------------------------------------------
3867 * GetDirectoryPositionInfo
3868 * Get cached position info
3869 *------------------------------------------------------------------*/
3872 GetDirectoryPositionInfo(
3874 char *directory_name,
3875 PositionInfo **position_info)
3877 Directory *directory;
3879 directory = FindDirectory(host_name, directory_name);
3880 if (directory == NULL)
3883 *position_info = directory->position_info;
3885 return directory->position_count;
3889 /*--------------------------------------------------------------------
3890 * WritePosInfoProcess
3891 * Main routine of the background process that writes the
3892 * postion information file.
3893 *------------------------------------------------------------------*/
3896 WritePosInfoProcess(
3898 Directory *directory,
3899 ActivityStatus activity)
3902 int position_count = directory->position_count;
3903 PositionInfo *position_info = directory->position_info;
3906 Tt_status tt_status;
3908 /* construct the full file name */
3909 fileName = ResolveLocalPathName( directory->host_name,
3910 directory->directory_name, positionFileName,
3911 home_host_name, &tt_status );
3912 /* Don't have to check for tt_status
3913 directory->host_name is home_host_name and ResolveLocalPathName will
3914 always return a good path
3916 DPRINTF(("WritePosInfoProcess: count %d, file %s\n",
3917 position_count, fileName));
3919 /* Remove old files, if no position information for this view */
3920 if (position_count <= 0)
3921 rc = unlink(fileName);
3924 /* open the file for writing */
3925 f = fopen(fileName, "w");
3929 /* Assume read-only directory, if we can't open the file */
3934 chmod(fileName, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
3936 fprintf(f, "%d\n", position_count);
3937 for (i = 0; i < position_count; i++)
3939 fprintf(f, "%s\n%d %d %d\n",
3940 position_info[i].name,
3943 position_info[i].stacking_order);
3951 /* send result back thorugh the pipe */
3952 DPRINTF(("WritePosInfoProcess: done (rc %d)\n", rc));
3953 write(pipe_fd, &rc, sizeof(int));
3959 /*--------------------------------------------------------------------
3960 * WritePosInfoPipeCallback
3961 * Callback routine that reads the return code sent through the
3962 * pipe from the WritePosInfoProcess background process.
3963 *------------------------------------------------------------------*/
3966 WritePosInfoPipeCallback(
3967 XtPointer client_data,
3971 PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
3972 Directory *directory = pipe_data->directory;
3976 /* get return code from the pipe */
3978 PipeRead(*fd, &rc, sizeof(int));
3980 /* close the pipe and cancel the callback */
3982 XtFree( client_data );
3985 /* verify that the directory still exists */
3986 if (DirectoryGone(directory))
3988 ScheduleActivity(NULL);
3992 DPRINTF(("WritePosInfoPipeCallback: rc %d\n", rc));
3994 /* reset the busy flag and schedule new work, if any */
3995 directory->busy[activity_writing_posinfo] = False;
3996 directory->activity = activity_idle;
3997 ScheduleActivity(directory);
4001 /*--------------------------------------------------------------------
4002 * SetDirectoryPositionInfo
4003 * Update cached position info. This routine schedules a
4004 * background process that writes the modified information
4005 * to the position info file.
4006 *------------------------------------------------------------------*/
4009 SetDirectoryPositionInfo(
4011 char *directory_name,
4013 PositionInfo *position_info)
4015 Directory *directory;
4019 /* find the directory */
4020 directory = FindDirectory(host_name, directory_name);
4021 if (directory == NULL)
4024 /* check if anything has changed */
4025 if (directory->position_count == position_count)
4028 for (i = 0; i < position_count && unchanged; i++)
4030 for (j = 0; j < position_count; j++)
4031 if (strcmp(position_info[i].name,
4032 directory->position_info[j].name) == 0)
4037 if (j == position_count ||
4038 position_info[i].x != directory->position_info[j].x ||
4039 position_info[i].y != directory->position_info[j].y ||
4040 position_info[i].stacking_order !=
4041 directory->position_info[j].stacking_order)
4047 /* if nothing changed, don't do anything */
4052 /* free old position info names*/
4053 for (i = 0; i < directory->position_count; i++)
4054 XtFree(directory->position_info[i].name);
4056 /* realloc array, if necessary */
4057 if (directory->position_count != position_count)
4059 directory->position_count = position_count;
4060 directory->position_info =
4061 (PositionInfo *) XtRealloc((char *)directory->position_info,
4062 position_count * sizeof(PositionInfo));
4065 /* replace old position info */
4066 for (i = 0; i < position_count; i++)
4068 directory->position_info[i].name = XtNewString(position_info[i].name);
4069 directory->position_info[i].x = position_info[i].x;
4070 directory->position_info[i].y = position_info[i].y;
4071 directory->position_info[i].stacking_order =
4072 position_info[i].stacking_order;
4075 /* make sure positionFileName is initialized */
4076 if (positionFileName == NULL)
4077 InitializePositionFileName();
4079 /* start background process that writes the position info file */
4080 directory->busy[activity_writing_posinfo] = True;
4081 ScheduleActivity(directory);
4087 /*====================================================================
4090 * These function are periodically called to scan all cached
4091 * directories to see if any have been modified since last read.
4092 * If any have, a background process is scheduled to re-read
4095 *==================================================================*/
4097 /*--------------------------------------------------------------------
4099 * Decide whether to skip an automatic re-read.
4100 * (We don't do re-reads on directories that aren't currently
4101 * being viewed and on the trash directory, if trash is currently
4103 *------------------------------------------------------------------*/
4107 Directory *directory)
4111 /* don't refresh while the directory is being modified */
4112 if (directory->modify_begin > 0)
4115 /* verify that the directory is still being viewed */
4116 if (!directory->viewed)
4119 for (i = 0; i < directory->numOfViews; i++)
4120 if (directory->directoryView[i].mapped)
4122 if (i == directory->numOfViews)
4125 /* if trash is being emptied and this is the trash dir, skip it */
4126 if (removingTrash && strcmp(directory->directory_name, trash_dir) == 0)
4133 /*--------------------------------------------------------------------
4135 * Main routine for the background process that checks directory
4136 * timestamps and the status of links.
4137 *------------------------------------------------------------------*/
4142 Directory *directory,
4143 ActivityStatus activity)
4145 struct stat stat_buf;
4147 Boolean link_changed;
4149 FileData *file_data;
4150 char full_name[MAX_PATH];
4159 * Do a stat on the directory to get its last-modified time.
4160 * Also check if we still have read and execute/search permisssion.
4163 * It is important to get the timstamp in exactly the same way that the
4164 * ReadDirectoryProcess does it; otherwise, we might get into a loop,
4165 * where TimerEventProcess detects that the directory has changed
4166 * and triggers ReadDirectoryProcess, but ReadDirectoryProcess won't
4167 * be able to get a new timestamp and update the directory structure,
4168 * so the next time TimerEventProcess runs it will trigger another
4169 * ReadDirectoryProcess, and so on ...
4171 if (CheckAccess(directory->path_name, R_OK | X_OK) != 0 ||
4172 stat(directory->path_name, &stat_buf) != 0)
4174 /* stat or access failed */
4180 /* stat succeeded and the directory is still readable */
4182 modify_time = stat_buf.st_mtime;
4186 * If requested, also check if any links broken.
4188 * Again: it is important that we determine the kind of link
4189 * (valid, recursive, or broken) in exactly the same way that
4190 * ReadDirectoryProcess does it (see comment above)!
4192 link_changed = False;
4193 if (rc == 0 && activity == activity_checking_links)
4195 strcpy(full_name, directory->path_name);
4196 namep = full_name + strlen(full_name);
4197 if (namep[-1] != '/')
4200 /* check all links */
4201 for (file_data = directory->file_data;
4202 file_data && !link_changed;
4203 file_data = file_data->next)
4205 /* Only worry about links */
4206 if (file_data->link == NULL)
4209 /* Check if the file is still a symbolic link */
4210 strcpy(namep, file_data->file_name);
4211 link_rc = lstat(full_name, &stat_buf);
4212 if (link_rc != 0 || (stat_buf.st_mode & S_IFMT) != S_IFLNK)
4214 /* no longer a link */
4215 link_changed = True;
4219 /* Check what kind of link this was the last time we looked:
4220 * a normal link (1), a recursive link (2), or an otherwise
4221 * broken link (3) */
4222 if (strcmp(file_data->logical_type, LT_BROKEN_LINK) == 0)
4224 else if (strcmp(file_data->logical_type, LT_RECURSIVE_LINK) == 0)
4229 /* Check what kind of link it is now */
4230 if (_DtFollowLink(full_name) == NULL)
4231 cur_link_kind = 2; /* recursive link */
4232 else if (stat(full_name, &stat_buf) != 0)
4233 cur_link_kind = 3; /* broken link */
4235 cur_link_kind = 1; /* a valid link */
4237 /* now we can tell if the link has changed */
4238 if (prev_link_kind != cur_link_kind)
4239 link_changed = True;
4243 /* send result back through the pipe */
4244 write(pipe_fd, &rc, sizeof(int));
4245 write(pipe_fd, &modify_time, sizeof(long));
4246 write(pipe_fd, &link_changed, sizeof(Boolean));
4251 /*--------------------------------------------------------------------
4253 * Mark sticky background process as idle. If there are too
4254 * may idle sticky procs, cause some of them to exit.
4255 *------------------------------------------------------------------*/
4259 ActivityStatus activity,
4260 StickyProcDesc *sticky_proc,
4263 StickyProcDesc *p, **lp;
4266 /* mark the process as idle */
4267 sticky_proc->idle = True;
4269 /* if there are too many idle procs, make some of them go away */
4271 lp = &ActivityTable[activity].sticky_procs;
4272 for (p = *lp; p; p = *lp)
4276 else if (n < max_procs)
4283 DPRINTF2(("StickyProcIdle: end sticky proc %ld\n", (long)p->child));
4284 PipeWriteString(p->pipe_m2s_fd, NULL);
4285 close(p->pipe_s2m_fd);
4286 close(p->pipe_m2s_fd);
4294 /*--------------------------------------------------------------------
4296 * Callback routine that reads information sent through the
4297 * pipe from the TimerEventProcess background process.
4298 *------------------------------------------------------------------*/
4302 XtPointer client_data,
4306 PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
4307 Directory *directory = pipe_data->directory;
4310 Boolean link_changed;
4313 /* get return code from the pipe */
4315 PipeRead(*fd, &rc, sizeof(int));
4316 PipeRead(*fd, &modify_time, sizeof(long));
4317 PipeRead(*fd, &link_changed, sizeof(Boolean));
4319 /* close the pipe and cancel the callback */
4320 if (pipe_data->sticky_proc)
4321 StickyProcIdle(pipe_data->activity, pipe_data->sticky_proc,
4322 maxRereadProcsPerTick);
4325 XtFree( client_data );
4328 /* verify that the directory still exists */
4329 if (DirectoryGone(directory))
4331 ScheduleActivity(NULL);
4336 "TimerPipeCallback: rc %d (was %d), time %ld (was %ld), link change %d\n",
4337 rc, directory->errnum, modify_time, (long)directory->modify_time, link_changed));
4339 /* reset the busy flag and schedule new work, if any */
4340 directory->busy[directory->activity] = False;
4341 directory->activity = activity_idle;
4342 ScheduleActivity(directory);
4344 /* if directory-read already in progress, nothing more to do here */
4345 if (directory->busy[activity_reading] ||
4346 directory->busy[activity_update_all])
4349 /* skip this directory if it is no longer being viewed */
4350 if (SkipRefresh(directory))
4353 /* if the directory was modified or links changed, re-read it */
4358 DPRINTF(("TimerPipeCallback: %s link changed\n",
4359 directory->directory_name));
4360 ReadDirectoryFiles(NULL, directory);
4362 else if (modify_time != directory->modify_time || directory->errnum != 0)
4364 DPRINTF(("TimerPipeCallback: %s modified\n",
4365 directory->directory_name));
4366 directory->busy[activity_update_all] = True;
4367 ScheduleActivity(directory);
4372 if (directory->errnum == 0)
4374 directory->errnum = rc;
4375 directory->errmsg_needed = True;
4376 ReadDirectoryFiles(NULL, directory);
4382 /*--------------------------------------------------------------------
4383 * CheckDesktopProcess
4384 * Main routine for the background process that checks each desktop
4385 * objects to see if the file that it refers to has disappeared
4386 * or has changed type.
4387 *------------------------------------------------------------------*/
4390 CheckDesktopProcess(
4392 Directory *directory,
4393 ActivityStatus activity)
4396 DesktopRec *desktopWindow;
4397 FileViewData *file_view_data;
4399 Tt_status tt_status;
4400 struct stat stat_buf;
4402 FileData2 file_data2;
4403 FileData *old_data, *new_data;
4405 for (i = 0; i < desktop_data->numIconsUsed; i++)
4407 desktopWindow = desktop_data->desktopWindows[i];
4408 file_view_data = desktopWindow->file_view_data;
4410 full_path = ResolveLocalPathName( desktopWindow->host,
4411 desktopWindow->dir_linked_to,
4412 desktopWindow->file_name,
4413 home_host_name, &tt_status);
4415 /* Check if the file still exists */
4417 if (lstat(full_path, &stat_buf) < 0)
4419 /* the real file no longer exists */
4421 "CheckDesktopProcess: sending PIPEMSG_DESKTOP_REMOVED for %s\n",
4423 pipe_msg = PIPEMSG_DESKTOP_REMOVED;
4424 write(pipe_fd, &pipe_msg, sizeof(short));
4425 PipeWriteString(pipe_fd, desktopWindow->host);
4426 PipeWriteString(pipe_fd, desktopWindow->dir_linked_to);
4427 PipeWriteString(pipe_fd, desktopWindow->file_name);
4432 /* See if the type has changed */
4433 old_data = file_view_data->file_data;
4435 if(directory->directoryView && directory->directoryView->file_mgr_data)
4436 IsToolBox = directory->directoryView->file_mgr_data->toolbox;
4440 ReadFileData2(&file_data2, full_path, NULL,IsToolBox);
4441 new_data = FileData2toFileData(&file_data2, &n);
4443 if (new_data->physical_type != old_data->physical_type ||
4444 strcmp(new_data->logical_type, old_data->logical_type) != 0)
4446 /* the type has changed */
4448 "CheckDesktopProcess: sending PIPEMSG_DESKTOP_CHANGED for %s\n",
4451 " old type %d %s, new type %d %s\n",
4452 old_data->physical_type, old_data->logical_type,
4453 new_data->physical_type, new_data->logical_type));
4455 pipe_msg = PIPEMSG_DESKTOP_CHANGED;
4456 write(pipe_fd, &pipe_msg, sizeof(short));
4457 PipeWriteString(pipe_fd, desktopWindow->host);
4458 PipeWriteString(pipe_fd, desktopWindow->dir_linked_to);
4459 PipeWriteString(pipe_fd, desktopWindow->file_name);
4461 PipeWriteFileData(pipe_fd, new_data);
4464 FreeFileData(new_data, True);
4470 /* send a 'done' msg through the pipe */
4471 DPRINTF2(("CheckDesktopProcess: sending DONE\n"));
4472 pipe_msg = PIPEMSG_DONE;
4473 write(pipe_fd, &pipe_msg, sizeof(short));
4478 /*--------------------------------------------------------------------
4479 * CheckDesktopPipeCallback
4480 * Callback routine that reads information sent through the
4481 * pipe from the CheckDesktopProcess background process.
4482 *------------------------------------------------------------------*/
4485 CheckDesktopPipeCallback(
4486 XtPointer client_data,
4490 PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
4491 Directory *directory = pipe_data->directory;
4493 char *host, *dir_linked_to, *file_name;
4494 FileData *new_data, *old_data;
4496 DesktopRec *desktopWindow;
4499 /* read the next msg from the pipe */
4501 n = PipeRead(*fd, &msg, sizeof(short));
4503 if (msg == PIPEMSG_DESKTOP_REMOVED ||
4504 msg == PIPEMSG_DESKTOP_CHANGED)
4506 /* get information from pipe */
4507 host = PipeReadString(*fd);
4508 dir_linked_to = PipeReadString(*fd);
4509 file_name = PipeReadString(*fd);
4510 if (msg == PIPEMSG_DESKTOP_CHANGED)
4511 new_data = PipeReadFileData(*fd);
4516 "CheckDesktopPipeCallback: msg %d: host %s, dir %s, name %s\n",
4517 msg, host, dir_linked_to, file_name));
4520 /* find the desktop object */
4522 for (i = 0; i < desktop_data->numIconsUsed; i++)
4524 desktopWindow = desktop_data->desktopWindows[i];
4526 if (strcmp(host, desktopWindow->host) == 0 &&
4527 strcmp(dir_linked_to, desktopWindow->dir_linked_to) == 0 &&
4528 strcmp(file_name, desktopWindow->file_name) == 0)
4535 /* remove or update the desktop object, if found */
4540 else if (msg == PIPEMSG_DESKTOP_REMOVED)
4542 /* remove the desktop object */
4543 DesktopObjectRemoved(desktopWindow);
4545 else /* msg == PIPEMSG_DESKTOP_CHANGED */
4547 /* replace file data */
4548 old_data = desktopWindow->file_view_data->file_data;
4549 FreeFileData(old_data, False);
4550 memcpy(old_data, new_data, sizeof(FileData));
4551 XtFree((char *)new_data);
4554 /* update the desktop object */
4555 DesktopObjectChanged(desktopWindow);
4560 XtFree(dir_linked_to);
4563 FreeFileData(new_data, True);
4566 else if (msg == PIPEMSG_DONE)
4568 /* close the pipe and cancel the callback */
4570 XtFree( client_data );
4573 /* reset the busy flag and schedule new work, if any */
4574 directory->busy[directory->activity] = False;
4575 directory->activity = activity_idle;
4576 ScheduleActivity(directory);
4581 /*--------------------------------------------------------------------
4584 * Arrange for a CheckDesktopProcess background process to be
4585 * started (checks each desktop objects to see if the file that
4586 * it refers to has disappeared or has changed type).
4588 *------------------------------------------------------------------*/
4591 CheckDesktop( void )
4593 dummy_directory->busy[activity_checking_desktop] = True;
4594 ScheduleActivity(dummy_directory);
4598 /*--------------------------------------------------------------------
4600 * This routine is called periodically. It schedules a
4601 * TimerEventProcess background process to be started for every
4602 * directory in the cache.
4603 *------------------------------------------------------------------*/
4605 /* comparison routine for qsort */
4611 return directory_set[*p1]->last_check - directory_set[*p2]->last_check;
4617 XtPointer client_data,
4620 static int *check_list = NULL;
4621 static int check_alloc = 0;
4625 DPRINTF2(("Directory::TimerEvent\n"));
4630 * Don't change any directories while a drag is active.
4632 * Reason: drag callbacks are called with a pointer to a FileViewData
4633 * structure; if a directory is reread while a drag is active,
4634 * the pointer would become invalid, causing unpredictable behavior.
4636 * Schedule the next TimerEvent in 1/2 second, so that check will
4637 * be done soon after the drag is finished.
4639 XtAppAddTimeOut (app_context, 500, TimerEvent, NULL);
4643 /* update tick count */
4646 /* determine if we should also check for broken links this time */
4647 if (checkBrokenLink > 0 &&
4648 tick_count >= lastLinkCheckTick + ticksBetweenLinkChecks)
4650 /* set link_check_needed flag on all directores */
4651 for (i = 0; i < directory_count; i++)
4653 /* skip this directory if no view is mapped */
4654 if (SkipRefresh(directory_set[i]))
4657 /* if a check is already in progress, don't start another one */
4658 if (directory_set[i]->busy[activity_checking_links])
4661 /* arrange for background process to be scheduled */
4662 directory_set[i]->link_check_needed = True;
4665 lastLinkCheckTick = tick_count;
4668 /* make sure check_list array is big enough */
4669 if (directory_count > check_alloc)
4671 check_alloc = directory_count + 5;
4673 (int *)XtRealloc((char *)check_list, check_alloc*sizeof(int));
4676 /* get a list of all directories that need to be checked */
4678 for (i = 0; i < directory_count; i++)
4680 /* skip this directory if no view is mapped */
4681 if (SkipRefresh(directory_set[i]))
4684 /* if a stat is already in progress, don't start another one */
4685 if (directory_set[i]->busy[activity_checking_dir] ||
4686 directory_set[i]->busy[activity_checking_links])
4689 /* add this directory to the check list */
4690 check_list[n++] = i;
4694 * Next we want to schedule a background process to be started
4695 * for each directory in the check_list. However, the variable
4696 * maxRereadProcsPerTick puts a limit on the number of such
4697 * background processes started per clock tick (i.e., per call
4698 * to this routine). Hence we sort check_list by last_check
4699 * (records the tick count when a directory was last read or
4700 * checked) and schedule backround processes on those dirs that
4701 * haven't been checked in the longest time.
4703 qsort(check_list, n, sizeof(int), (int (*)())CheckListCmp);
4705 /* arrange for background process to be started */
4706 for (j = 0; j < n && j < maxRereadProcsPerTick; j++)
4709 if (directory_set[i]->link_check_needed)
4711 directory_set[i]->link_check_needed = False;
4712 directory_set[i]->busy[activity_checking_links] = True;
4715 directory_set[i]->busy[activity_checking_dir] = True;
4716 ScheduleActivity(directory_set[i]);
4717 directory_set[i]->last_check = tick_count;
4720 /* Reset the timeout for the next interval. */
4721 if (SomeWindowMapped())
4722 XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
4724 timer_suspended = True;
4728 /*--------------------------------------------------------------------
4729 * TimerEventBrokenLinks
4730 * This routine is called periodically. It checks whether any
4731 * desktop object is broken (i.e., the object it refers to no
4733 *------------------------------------------------------------------*/
4736 TimerEventBrokenLinks(
4737 XtPointer client_data,
4742 DPRINTF2(("Directory::TimerEventBrokenLinks\n"));
4746 /* go check the desktop objects */
4747 if (desktop_data->numIconsUsed > 0)
4751 /* Reset the timeout for the next interval. */
4752 if (desktop_data->numIconsUsed > 0)
4754 checkBrokenLinkTimerId = XtAppAddTimeOut( app_context,
4755 checkBrokenLink * 1000,
4756 TimerEventBrokenLinks,
4761 checkBrokenLinkTimerId = None;
4766 /*====================================================================
4768 * Background process scheduler
4770 * The routines below schedule background activity, making sure
4771 * that there aren't too many processes running at the same time.
4773 *==================================================================*/
4775 /*--------------------------------------------------------------------
4776 * ScheduleDirectoryActivity
4777 * If there is any work to do for a directory, and if there is
4778 * no backgroud process currently running for that directory,
4779 * then fork a process to do the work.
4780 *------------------------------------------------------------------*/
4783 ScheduleDirectoryActivity(
4784 Directory *directory)
4786 static char *pname = "ScheduleActivity";
4787 ActivityStatus activity;
4788 PipeCallbackData *pipe_data;
4789 Boolean all_views_active;
4790 Boolean this_view_active;
4792 int n_active, n_checking;
4793 int save_last_check = 0;
4794 FileMgrData *file_mgr_data;
4797 int pipe_s2m_fd[2] = {-1, -1}; /* for msgs from backgroundnd proc (slave to master) */
4798 int pipe_m2s_fd[2] = {-1, -1}; /* for msgs to backgroundnd proc (master to slave) */
4803 /* If already active, don't start anything new. */
4804 if (directory->activity != activity_idle)
4807 /* Decide what to do next */
4808 for (activity = 0; activity < activity_idle; activity++)
4809 if (directory->busy[activity])
4812 /* If nothing to do, return */
4813 if (activity == activity_idle)
4816 DPRINTF2(("ScheduleActivity: activity %d, busy %c%c%c%c%c%c%c, dir %s\n",
4817 directory->activity,
4818 directory->busy[activity_writing_posinfo]? 'W': '-',
4819 directory->busy[activity_reading]? 'R': '-',
4820 directory->busy[activity_update_all]? 'A': '-',
4821 directory->busy[activity_update_some]? 'U': '-',
4822 directory->busy[activity_checking_links]? 'B': '-',
4823 directory->busy[activity_checking_desktop]? 'D': '-',
4824 directory->busy[activity_checking_dir]? 'C': '-',
4825 directory->directory_name));
4827 /* Don't start more than a certain number of background processed */
4830 for (j = 0; j < directory_count; j++)
4832 if (directory_set[j]->activity != activity_idle)
4834 if (directory_set[j]->activity == activity_checking_links ||
4835 directory_set[j]->activity == activity_checking_dir)
4838 if (dummy_directory->activity != activity_idle)
4843 if (n_active >= maxDirectoryProcesses ||
4844 n_checking >= maxRereadProcesses)
4846 DPRINTF2(("ScheduleActivity: too many processes\n"));
4851 * We don't want to start more than one background process per view.
4852 * In tree mode one view may show more than one directory.
4853 * Hence we go through the view list for this directory and for each
4854 * view, we check if the same view appears on the view list of some
4855 * other directory that currently has active background activity.
4856 * If all vies on this directory have other activity, then we won't
4857 * start anything new.
4859 if (directory->numOfViews > 0)
4861 all_views_active = True;
4862 for (i = 0; i < directory->numOfViews; i++)
4864 /* get file_mgr_data for this view */
4865 file_mgr_data = directory->directoryView[i].file_mgr_data;
4867 /* see if the same view appears in the view list of a non-idle dir */
4868 this_view_active = False;
4869 for (j = 0; j < directory_count && !this_view_active; j++)
4871 /* we are only interested in directories that are not idle */
4872 if (directory_set[j]->activity == activity_idle)
4875 /* see if the view appears in the view list */
4876 for (k = 0; k < directory_set[j]->numOfViews; k++)
4878 if (directory_set[j]->directoryView[k].file_mgr_data ==
4881 this_view_active = True;
4887 if (!this_view_active)
4889 all_views_active = False;
4894 if (all_views_active)
4896 DPRINTF2(("ScheduleActivity: all views busy\n"));
4901 /* now we are ready to start the next activity */
4902 directory->activity = activity;
4903 if (activity == activity_reading ||
4904 activity == activity_update_all ||
4905 activity == activity_checking_dir ||
4906 activity == activity_checking_links)
4908 save_last_check = directory->last_check;
4909 directory->last_check = tick_count;
4913 * Special optimization for periodic background processes
4914 * (currently only used for activity_checking_dir):
4915 * Since this is done frequently, we don't want to fork new process each
4916 * time. Hence, instead of exiting when it's done, the background process
4917 * is "sticky", i.e., it will stay around waiting for a message on stdin,
4918 * so it can be re-used the next time around. A linked list of sticky
4919 * procs that are currently active is maintained in the ActivityTable.
4921 sticky = ActivityTable[activity].sticky;
4924 /* see if we can find an idle sticky proc that can do the work */
4925 for (p = ActivityTable[activity].sticky_procs; p; p = p->next)
4934 /* We found an idle sticky proc that can be used */
4935 DPRINTF2(("ScheduleActivity: use sticky proc %ld\n", (long)p->child));
4937 /* Send the directory name to the sticky proc */
4939 if (PipeWriteString(p->pipe_m2s_fd, directory->path_name) < 0) {
4942 /* the pipe is broken, remove the old proc then start a new one */
4943 for (d = ActivityTable[activity].sticky_procs; d && p; d = d->next) {
4946 /* the proc listed 1st is dead, remove it */
4947 ActivityTable[activity].sticky_procs = p->next;
4951 else if (d->next == p)
4953 /* the process "p" is dead, remove it */
4962 pipe_s2m_fd[0] = p->pipe_s2m_fd;
4971 /* Need to fork a new background process */
4973 /* create a pipe for reading data from the background proc */
4976 /* creating a new sticky proc? */
4979 /* also need a pipe for sending msgs to the sticky proc */
4982 /* add entry to the list of sticky procs */
4983 p = (StickyProcDesc *) XtMalloc(sizeof(StickyProcDesc));
4984 p->next = ActivityTable[activity].sticky_procs;
4985 ActivityTable[activity].sticky_procs = p;
4987 p->pipe_s2m_fd = pipe_s2m_fd[0];
4988 p->pipe_m2s_fd = pipe_m2s_fd[1];
4992 /* fork a background process */
4997 DBGFORK(("%s: fork failed for activity %d: %s\n",
4998 pname, activity, strerror(errno)));
5001 "%s: fork failed, ppid %d, pid %d, activity %d: error %d=%s\n",
5002 pname, getppid(), getpid(), activity, errno, strerror(errno));
5004 directory->activity = activity_idle;
5005 directory->last_check = save_last_check;
5007 /* close unused pipe connections */
5008 close(pipe_s2m_fd[0]); /* child won't read from this pipe */
5009 close(pipe_s2m_fd[1]); /* parent won't write to this pipe */
5012 close(pipe_m2s_fd[1]); /* child won't write to this pipe */
5013 close(pipe_m2s_fd[0]); /* parent won't read from this pipe */
5025 DBGFORK(("%s: child activity %d, s2m %d, m2s %d\n",
5026 pname, activity, pipe_s2m_fd[1], pipe_m2s_fd[0]));
5028 /* close unused pipe connections */
5029 close(pipe_s2m_fd[0]); /* child won't read from this pipe */
5031 close(pipe_m2s_fd[1]); /* child won't write to this pipe */
5033 /* run main routine for this activity from ActivityTable */
5036 rc = (*ActivityTable[activity].main)(pipe_s2m_fd[1],
5037 directory, activity);
5038 if (!sticky || rc != 0)
5041 /* wait for a message in the pipe */
5042 s = PipeReadString(pipe_m2s_fd[0]);
5046 XtFree(directory->path_name);
5047 directory->path_name = s;
5049 DPRINTF2(("StickyActivity: activity %d, dir %s\n", activity, s));
5052 /* close pipes and end this process */
5053 close(pipe_s2m_fd[1]);
5055 close(pipe_m2s_fd[0]);
5057 DBGFORK(("%s: completed activity %d, (rc %d)\n",pname, activity, rc));
5062 DBGFORK(("%s: forked child<%d> for activity %d, s2m %d, m2s %d\n",
5063 pname, pid, activity, pipe_s2m_fd[0], pipe_m2s_fd[1]));
5065 /* parent process */
5070 * If a directory read or update was started:
5071 * clear the modifile_list, now that the
5072 * background process has it's own copy.
5074 if (activity == activity_reading ||
5075 activity == activity_update_all ||
5076 activity == activity_update_some)
5078 for (i = 0; i < directory->modified_count; i++)
5079 XtFree(directory->modified_list[i]);
5080 XtFree((char *)directory->modified_list);
5082 directory->modified_count = 0;
5083 directory->modified_list = NULL;
5086 /* close unused pipe connections */
5087 close(pipe_s2m_fd[1]); /* parent won't write to this pipe */
5089 close(pipe_m2s_fd[0]); /* parent won't read from this pipe */
5093 /* set up callback to get the pipe data */
5094 DPRINTF2(("ScheduleActivity: setting up pipe callback\n"));
5095 pipe_data = (PipeCallbackData *)XtMalloc(sizeof(PipeCallbackData));
5096 pipe_data->directory = directory;
5097 pipe_data->child = pid;
5098 pipe_data->sticky_proc = p;
5099 pipe_data->activity = activity;
5101 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
5102 pipe_s2m_fd[0], (XtPointer)XtInputReadMask,
5103 ActivityTable[activity].callback, (XtPointer)pipe_data);
5107 /*--------------------------------------------------------------------
5109 * See if any new background work should be started.
5110 *------------------------------------------------------------------*/
5114 Directory *directory)
5118 /* first try to schedule new activity for this directory */
5119 if (directory != NULL)
5120 ScheduleDirectoryActivity(directory);
5122 /* see if there is anything else we can schedule now */
5123 if (directory == NULL || directory->activity == activity_idle)
5125 for (i = 0; i < directory_count; i++)
5126 if (directory_set[i] != directory)
5127 ScheduleDirectoryActivity(directory_set[i]);
5129 ScheduleDirectoryActivity(dummy_directory);
5134 FileMgrData *file_mgr_data)
5136 DirectorySet *directory_data;
5137 FileViewData *file_view_data;
5140 directory_data = file_mgr_data->directory_set[0];
5142 for (j = 0; j < directory_data->file_count; j++)
5144 file_view_data = directory_data->file_view_data[j];
5146 if (file_view_data->filtered != True &&
5147 strcmp(file_mgr_data->desktop_file,
5148 file_view_data->file_data->file_name) == 0)
5150 SelectFile (file_mgr_data, file_view_data);
5154 ActivateSingleSelect(file_mgr_data->file_mgr_rec,
5155 file_mgr_data->selection_list[0]->file_data->logical_type);
5156 PositionFileView(file_view_data, file_mgr_data);