2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $TOG: Directory.c /main/18 1999/12/09 13:05:34 mgreess $ */
24 /************************************<+>*************************************
25 ****************************************************************************
29 * COMPONENT_NAME: Desktop File Manager (dtfile)
31 * Description: Directory processing functions used by the File Browser.
33 * FUNCTIONS: CheckDesktop
34 * CheckDesktopPipeCallback
37 * DirectoryBeginModify
40 * DirectoryFileModified
48 * GetDirectoryLogicalType
49 * GetDirectoryPositionInfo
51 * InitializeDirectoryRead
52 * InitializePositionFileName
54 * PipeReadPositionInfo
56 * PipeWritePositionInfo
60 * ReadDirectoryProcess
66 * ScheduleDirectoryActivity
67 * SetDirectoryPositionInfo
72 * TimerEventBrokenLinks
76 * UpdateCachedDirectories
80 * WritePosInfoPipeCallback
85 * (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
86 * (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
87 * (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
88 * (c) Copyright 1993, 1994, 1995 Novell, Inc.
90 ****************************************************************************
91 ************************************<+>*************************************/
94 #include <sys/types.h>
110 #include <Dt/Connect.h>
111 #include <Dt/DtNlUtils.h>
120 #include "SharedMsgs.h"
123 extern Boolean removingTrash;
126 /*--------------------------------------------------------------------
127 * Constants and Types
128 *------------------------------------------------------------------*/
131 #define OPTION_OFF '-'
132 #define WRITE_PRIV 'w'
133 #define READ_PRIV 'r'
134 #define EXEC_PRIV 'x'
136 /* prefix for the name of the position info file */
137 #define POSITION_FILE_PREFIX ".!dt"
139 /* kinds of messages sent through the pipe */
140 #define PIPEMSG_ERROR 1
141 #define PIPEMSG_FILEDATA 2
142 #define PIPEMSG_DONE 3
143 #define PIPEMSG_PATH_LOGICAL_TYPES 4
144 #define PIPEMSG_POSITION_INFO 5
145 #define PIPEMSG_FILEDATA2 6
146 #define PIPEMSG_FILEDATA3 7
147 #define PIPEMSG_DESKTOP_REMOVED 8
148 #define PIPEMSG_DESKTOP_CHANGED 9
151 #define FILEDATABUF 50
152 #endif /* FILEDATABUF */
157 * Background activities, ordered by priority:
158 * (activity_idle must be the last one in the list!)
162 activity_writing_posinfo, /* writing position information file */
163 activity_reading, /* reading the directory */
164 activity_update_all, /* updating the directory */
165 activity_update_some, /* updating selected files */
166 activity_checking_links, /* checking for broken links */
167 activity_checking_desktop, /* checking desktop objects */
168 activity_checking_dir, /* checking if the directory has changed */
169 activity_idle /* no background activity */
172 /* The internal directory structure and directory set list */
176 FileMgrData * file_mgr_data;
183 char * directory_name;
187 ActivityStatus activity;
188 Boolean busy[activity_idle];
189 Boolean errmsg_needed;
193 Boolean link_check_needed;
195 FileData * file_data;
199 char ** path_logical_types;
201 PositionInfo * position_info;
203 Boolean was_up_to_date;
205 char ** modified_list;
207 DirectoryView * directoryView;
211 /* data for keeping track of sticky background procs */
221 /* data for callback routines that handle background processes */
224 Directory *directory;
226 StickyProcDesc *sticky_proc;
227 ActivityStatus activity;
231 /* background procedure */
232 typedef int (*DirBackgroundProc)(int, Directory *, ActivityStatus);
235 /*--------------------------------------------------------------------
236 * Static Function Declarations
237 *------------------------------------------------------------------*/
239 static void TimerEvent(
240 XtPointer client_data,
242 static void ScheduleActivity(
243 Directory *directory);
244 static int WritePosInfoProcess(
246 Directory *directory,
247 ActivityStatus activity);
248 static int ReadDirectoryProcess(
250 Directory *directory,
251 ActivityStatus activity);
252 static int UpdateAllProcess(
254 Directory *directory,
255 ActivityStatus activity);
256 static int UpdateSomeProcess(
258 Directory *directory,
259 ActivityStatus activity);
260 static int TimerEventProcess(
262 Directory *directory,
263 ActivityStatus activity);
264 static int CheckDesktopProcess(
266 Directory *directory,
267 ActivityStatus activity);
268 static void WritePosInfoPipeCallback(
269 XtPointer client_data,
272 static void ReaddirPipeCallback(
273 XtPointer client_data,
276 static void TimerPipeCallback(
277 XtPointer client_data,
280 static void CheckDesktopPipeCallback(
281 XtPointer client_data,
284 static void SelectDesktopFile(FileMgrData *fmd);
288 /*--------------------------------------------------------------------
290 *------------------------------------------------------------------*/
292 int maxDirectoryProcesses = 10;
293 int maxRereadProcesses = 5;
294 int maxRereadProcsPerTick = 1;
296 XtIntervalId checkBrokenLinkTimerId = NULL;
298 static Directory ** directory_set = NULL;
299 static int directory_count = 0;
300 static int directory_set_size = 0;
301 static char * positionFileName = NULL;
302 static XtAppContext app_context = None;
303 static int tickTime = 0;
304 static int ticksBetweenLinkChecks = 0;
305 static Boolean timer_suspended = False;
306 static int tick_count = 0;
307 static int lastLinkCheckTick = 0;
308 static Directory dummy_dir_struct =
317 static Directory *dummy_directory = &dummy_dir_struct;
321 DirBackgroundProc main;
322 XtInputCallbackProc callback;
324 StickyProcDesc *sticky_procs;
327 { WritePosInfoProcess, WritePosInfoPipeCallback, False,NULL },/* writing_posinfo*/
328 { ReadDirectoryProcess, ReaddirPipeCallback, False,NULL }, /* reading */
329 { UpdateAllProcess, ReaddirPipeCallback, False,NULL }, /* update_all */
330 { UpdateSomeProcess, ReaddirPipeCallback, False,NULL }, /* update_some */
331 { TimerEventProcess, TimerPipeCallback, False,NULL }, /* checking_links */
332 { CheckDesktopProcess, CheckDesktopPipeCallback, False,NULL },/* checking_desktop */
333 { TimerEventProcess, TimerPipeCallback, True, NULL }, /* checking_dir */
334 { NULL, NULL, False, NULL } /* idle */
338 /*====================================================================
340 * Initialization routines
342 *==================================================================*/
344 /*--------------------------------------------------------------------
345 * InitializePositionFileName
346 * Initialize the name under which the position info is stored.
347 *------------------------------------------------------------------*/
350 InitializePositionFileName(void)
352 struct passwd * pwInfo;
354 /* Determine the name under which the position info is stored */
355 if (positionFileName == NULL)
358 pwInfo = getpwuid(getuid());
359 positionFileName = XtMalloc(strlen(pwInfo->pw_name) +
360 strlen(POSITION_FILE_PREFIX) + 1);
361 sprintf(positionFileName, "%s%s", POSITION_FILE_PREFIX, pwInfo->pw_name);
366 /*--------------------------------------------------------------------
367 * InitializeDirectoryRead
368 * Set up a timer used to automatically check the read in
369 * directories to see if they have been modified.
370 *------------------------------------------------------------------*/
373 InitializeDirectoryRead(
377 /* remeber application context */
378 app_context = XtWidgetToApplicationContext(widget);
380 /* start timer to check for modified directories and broken links */
381 tick_count = lastLinkCheckTick = 0;
385 tickTime = rereadTime;
386 ticksBetweenLinkChecks = checkBrokenLink / rereadTime;
388 else if (checkBrokenLink != 0)
390 tickTime = checkBrokenLink;
391 ticksBetweenLinkChecks = 1;
397 XtAppAddTimeOut (app_context, tickTime * 1000, TimerEvent, NULL);
399 /* start timer to check for broken desktop objects */
400 if( desktop_data->numIconsUsed > 0
401 && checkBrokenLink != 0
404 checkBrokenLinkTimerId = XtAppAddTimeOut( app_context,
405 checkBrokenLink * 1000,
406 TimerEventBrokenLinks,
411 checkBrokenLinkTimerId = NULL;
416 /*====================================================================
420 *==================================================================*/
422 /*--------------------------------------------------------------------
424 * Given a host & directory name, find the directory in our cache.
425 *------------------------------------------------------------------*/
430 char *directory_name)
434 /* See if the directory is in the directory set. First, compare */
435 /* the names from the directory entries ONLY. There will be one */
436 /* directory entry for every directory of a different name. This */
437 /* may mean that there is more than one directory entry for a */
438 /* single directory (ie. if there is a directory that is a link or */
439 /* a mount point in the system). */
440 /* If this doesn't succeed, we may be getting a ToolTalk resolved */
441 /* name. So run the comparison again but this time compare the */
442 /* incoming name to the tt_path_name in the directory entries. */
443 /* This algorithm has a limitation in that if ToolTalk has resolved */
444 /* a path name, the match will occur for the first entry in the */
445 /* directory set whose tt_path_name matches our name, this MAY NOT */
446 /* be the directory where the activity originated. The user should */
447 /* only notice in the case where automatic refresh is turned off. */
448 for (i = 0; i < directory_count; i++)
450 if (strcmp (host_name, directory_set[i]->host_name) == 0 &&
451 strcmp (directory_name, directory_set[i]->directory_name) == 0)
453 return directory_set[i];
457 for (i = 0; i < directory_count; i++)
459 if (directory_set[i]->tt_path_name != NULL &&
460 strcmp (host_name, home_host_name) == 0 &&
461 strcmp (directory_name, directory_set[i]->tt_path_name) == 0)
463 return directory_set[i];
472 /*--------------------------------------------------------------------
474 * Check if a directory has been removed from the cache.
475 *------------------------------------------------------------------*/
479 Directory *directory)
483 for (i = 0; i < directory_count; i++)
484 if (directory_set[i] == directory)
491 /*--------------------------------------------------------------------
493 * Free FileData structure.
494 *------------------------------------------------------------------*/
501 XtFree(file_data->file_name);
502 file_data->file_name = NULL;
504 XtFree(file_data->action_name);
505 file_data->action_name = NULL;
507 DtDtsFreeDataType(file_data->logical_type);
508 file_data->logical_type = NULL;
510 if ( file_data->final_link != NULL &&
511 file_data->final_link != file_data->link)
513 XtFree(file_data->final_link);
514 file_data->final_link = NULL;
517 if (file_data->link != NULL )
519 XtFree(file_data->link);
520 file_data->link = NULL;
524 XtFree((char *)file_data);
528 /*--------------------------------------------------------------------
530 * Free Directory structure.
531 *------------------------------------------------------------------*/
535 Directory *directory)
538 FileData *file_data, *next_file_data;
540 if( directory == NULL )
543 XtFree (directory->host_name);
544 directory->host_name = NULL;
546 XtFree (directory->directory_name);
547 directory->directory_name = NULL;
549 XtFree (directory->path_name);
550 directory->path_name = NULL;
552 XtFree (directory->tt_path_name);
553 directory->tt_path_name = NULL;
555 for (i=0; i < directory->path_count; i++)
556 DtDtsFreeDataType(directory->path_logical_types[i]);
557 XtFree ((char *) directory->path_logical_types);
558 directory->path_logical_types = NULL;
560 for (i = 0; i < directory->position_count; i++)
561 XtFree(directory->position_info[i].name);
562 XtFree ((char *) directory->position_info);
563 directory->position_info = NULL;
565 for (i = 0; i < directory->modified_count; i++)
566 XtFree(directory->modified_list[i]);
567 XtFree ((char *) directory->modified_list);
568 directory->modified_list = NULL;
570 XtFree ((char *) directory->directoryView);
571 directory->directoryView = NULL;
573 if (directory->dir_data)
575 FreeFileData(directory->dir_data, True);
576 directory->dir_data = NULL;
579 file_data = directory->file_data;
580 while (file_data != NULL)
582 next_file_data = file_data->next;
583 FreeFileData(file_data, True);
584 file_data = next_file_data;
586 directory->file_data = NULL;
588 XtFree((char *) directory);
592 /*--------------------------------------------------------------------
594 * Check if any cached directory is currently being viewed in
595 * a window that is mapped (not iconified). (If there is none,
596 * we won't need to set a refresh timer).
597 *------------------------------------------------------------------*/
600 SomeWindowMapped(void)
605 for (i = 0; i < directory_count; i++)
606 for (j = 0; j < directory_set[i]->numOfViews; j++)
607 if (directory_set[i]->directoryView[j].mapped)
614 /*====================================================================
616 * Routines for reading a directory
618 *==================================================================*/
620 /*--------------------------------------------------------------------
623 * Directories are read in a background process that is connected
624 * to the main dtfile process by a pipe.
625 * The routines below are used to send directory entry information
626 * through the pipe from the background to the main process.
627 *------------------------------------------------------------------*/
629 /* write FileData to the pipe */
635 write(fd, file_data, sizeof(FileData));
636 PipeWriteString(fd, file_data->file_name);
637 PipeWriteString(fd, file_data->action_name); /* @@@ ??? */
638 PipeWriteString(fd, file_data->logical_type);
640 PipeWriteString(fd, file_data->link);
641 if (file_data->final_link)
642 PipeWriteString(fd, file_data->final_link);
646 /* read FileData from the pipe */
654 file_data = (FileData *)XtCalloc(1,sizeof(FileData));
655 n = PipeRead(fd, file_data, sizeof(FileData));
656 if (n < sizeof(FileData))
658 fprintf(stderr, "PipeReadFileData: n = %d, expected %ld\n",
659 n, (long)sizeof(FileData));
661 file_data->file_name = PipeReadString(fd);
662 file_data->action_name = PipeReadString(fd);
663 file_data->logical_type = PipeReadString(fd);
665 file_data->link = PipeReadString(fd);
666 if (file_data->final_link)
667 file_data->final_link = PipeReadString(fd);
669 /* return the file data */
675 FileData2 *file_data2,
680 char file_name_buf[MAXPATHLEN];
681 char action_name_buf[MAXPATHLEN];
682 char logical_type_buf[MAXPATHLEN];
683 char link_buf[MAXPATHLEN];
684 char final_link_buf[MAXPATHLEN];
685 char *textptr = file_data2->text;
687 file_data = (FileData *)XtCalloc(1,sizeof(FileData));
689 strncpy(file_name_buf, textptr, n = file_data2->file_name);
690 file_name_buf[n] = NILL;
693 strncpy(action_name_buf, textptr, n = file_data2->action_name);
694 action_name_buf[n] = NILL;
697 strncpy(logical_type_buf, textptr, n = file_data2->logical_type);
698 logical_type_buf[n] = NILL;
701 strncpy(link_buf, textptr, n = file_data2->link);
705 strncpy(final_link_buf, textptr, n = file_data2->final_link);
706 final_link_buf[n] = NILL;
709 file_data->next = NULL;
710 file_data->file_name = XtNewString(file_name_buf);
711 file_data->action_name = file_data2->action_name
712 ? XtNewString(action_name_buf)
714 file_data->physical_type = file_data2->physical_type;
715 file_data->logical_type = XtNewString(logical_type_buf);
716 file_data->errnum = file_data2->errnum;
717 file_data->stat = file_data2->stat;
719 file_data->link = file_data2->link
720 ? XtNewString(link_buf)
723 file_data->final_link = file_data2->final_link
724 ? XtNewString(final_link_buf)
726 file_data->is_subdir = file_data2->is_subdir;
727 file_data->is_broken = file_data2->is_broken;
729 *l = sizeof(*file_data2) - sizeof(file_data2->text)
730 + file_data2->file_name + file_data2->action_name
731 + file_data2->logical_type + file_data2->link
732 + file_data2->final_link;
734 *l = (*l + sizeof(char *) - 1) & ~(sizeof(char *) - 1);
736 /* return the file data */
740 /* write PositionInfo to the pipe */
742 PipeWritePositionInfo(
744 PositionInfo *position_info)
746 PipeWriteString(fd, position_info->name);
747 write(fd, &position_info->x, sizeof(Position));
748 write(fd, &position_info->y, sizeof(Position));
749 write(fd, &position_info->stacking_order, sizeof(int));
753 /* read PositionInfo from the pipe */
755 PipeReadPositionInfo(
757 PositionInfo *position_info)
759 position_info->name = PipeReadString(fd);
760 PipeRead(fd, &position_info->x, sizeof(Position));
761 PipeRead(fd, &position_info->y, sizeof(Position));
762 PipeRead(fd, &position_info->stacking_order, sizeof(int));
765 /*--------------------------------------------------------------------
767 * Given a path name, return FileData for a file.
768 *------------------------------------------------------------------*/
772 char *full_directory_name,
776 char full_file_name[MAX_PATH];
777 char link_file_name[MAX_PATH];
778 char link_path[MAX_PATH];
784 Boolean recursive_link_found;
785 struct stat stat_buf;
786 struct stat stat_buf2;
791 /* Allocate a new file structure. */
792 file_data = (FileData *)XtMalloc(sizeof(FileData));
794 /* get the full name of the file */
795 strcpy (full_file_name, full_directory_name);
798 /* append file name to the directory */
799 if (strcmp(full_directory_name,"/") != 0)
800 strcat (full_file_name, "/");
801 strcat (full_file_name, file_name);
805 /* no file name passed: use last component of directory */
806 file_name = strrchr(full_file_name, '/');
807 if (file_name > full_file_name)
813 /* Follow symbolic links to their ultimate destination */
816 recursive_link_found = False;
817 strcpy(link_file_name, full_file_name);
819 stat_result = lstat (link_file_name, &stat_buf);
820 if (stat_result == 0 && (stat_buf.st_mode & S_IFMT) == S_IFLNK)
822 while ((link_len = readlink(link_file_name, link_path, MAX_PATH)) > 0)
824 link_path[link_len] = '\0';
825 link_list = (char **)XtRealloc((char *)link_list, sizeof(char *) *
828 /* Force the link to be an absolute path, if necessary */
829 if (link_path[0] != '/')
831 /* Relative paths are relative to the current directory */
832 end = strrchr(link_file_name, '/') + 1;
834 strcat(link_file_name, link_path);
837 strcpy(link_file_name, link_path);
839 /* Check for a recursive loop; abort if found */
840 for (i = 0; i < link_count; i++)
842 if (strcmp(link_file_name, link_list[i]) == 0)
844 /* Back up to last non-recursive portion */
845 strcpy(link_file_name, link_list[link_count - 1]);
846 recursive_link_found = True;
851 if (recursive_link_found)
854 link_list[link_count++] = XtNewString(link_file_name);
855 link_list[link_count] = NULL;
858 /* try to stat the file that the link points to */
859 if (stat (link_file_name, &stat_buf2) == 0)
861 /* replace lstat result with the stat */
862 memcpy(&stat_buf, &stat_buf2, sizeof(struct stat));
867 /* fill in the FileData structure with the information we found */
868 file_data->next = NULL;
869 file_data->file_name = XtNewString(file_name? file_name: ".");
870 file_data->logical_type = NULL;
871 file_data->is_subdir = False;
872 file_data->action_name = NULL;
876 file_data->link = XtNewString( link_list[0] );
877 file_data->final_link = XtNewString( link_list[link_count - 1] );
878 for (i = 0; i < link_count; i++)
879 XtFree(link_list[i]);
880 XtFree((char *)link_list);
882 file_data->link = file_data->final_link = NULL;
884 if (stat_result == 0)
886 file_data->errnum = 0;
887 file_data->stat = stat_buf;
889 /* Find and set the physical type of the file */
891 if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
893 file_data->physical_type = DtDIRECTORY;
894 if (file_name == NULL ||
895 strcmp(file_name, ".") != 0 && strcmp(file_name, "..") != 0)
897 file_data->is_subdir = True;
900 else if ((stat_buf.st_mode & S_IFMT) == S_IFREG)
902 if ((stat_buf.st_mode & S_IXUSR) ||
903 (stat_buf.st_mode & S_IXGRP) ||
904 (stat_buf.st_mode & S_IXOTH))
905 file_data->physical_type = DtEXECUTABLE;
907 file_data->physical_type = DtDATA;
910 file_data->physical_type = DtDATA;
912 /* Find and set the logical type of the file */
913 if ((stat_buf.st_mode & S_IFMT) == S_IFLNK)
915 file_data->is_broken = True;
916 if (recursive_link_found)
917 file_data->logical_type = XtNewString(LT_RECURSIVE_LINK);
919 file_data->logical_type = XtNewString(LT_BROKEN_LINK);
923 file_data->is_broken = False;
925 file_data->logical_type = (char *) DtDtsDataToDataType(
928 file_data->final_link, NULL,
931 file_data->logical_type = (char *) DtDtsDataToDataType(
936 #if defined( DATATYPE_IS_FIXED )
938 /* The problem here is there isn't a way for user to mask
939 only the OWNER READ bit in the MODE field of dtfile.dt file.
940 If the MODE field set to d&!r Then all READ permission
941 (S_IRUSR, S_IRGRP and S_IROTH)
942 bits has to be off in order for the above data typing to work.
943 Also data typing is unable to detect when the directory is not
944 the owners and only has execute permission by that owner.
945 The work around is manually checking it ourselves.
946 When the data typing code is fixed, please remove this check.
948 if( S_ISDIR( stat_buf.st_mode ) &&
949 (strcmp (file_data->logical_type, LT_DIRECTORY) == 0))
951 if( strcmp( file_name, ".." ) != 0
952 && strcmp( file_name, "." ) != 0 )
956 if( file_data->link )
957 fullPathName = file_data->link;
959 fullPathName = full_file_name;
961 if( access( fullPathName, R_OK ) != 0 )
963 XtFree( file_data->logical_type );
964 file_data->logical_type = XtNewString( LT_FOLDER_LOCK );
966 else if( access( fullPathName, W_OK ) != 0 )
968 XtFree( file_data->logical_type );
969 file_data->logical_type = XtNewString( LT_NON_WRITABLE_FOLDER );
976 if(DtActionExists(file_data->logical_type))
978 file_data->action_name = (char *)DtActionLabel(file_data->file_name);
982 char *ptr = DtDtsDataTypeToAttributeValue(file_data->logical_type,
987 file_data->action_name = XtNewString(ptr);
988 DtDtsFreeAttributeValue(ptr);
994 /* couldn't stat the file */
995 file_data->errnum = stat_errno;
996 memset(&file_data->stat, 0, sizeof(file_data->stat));
997 file_data->physical_type = DtUNKNOWN;
998 file_data->is_broken = True;
999 file_data->logical_type = XtNewString(DtDEFAULT_DATA_FT_NAME);
1006 /*--------------------------------------------------------------------
1008 * Given a path name, return FileData for a file.
1009 *------------------------------------------------------------------*/
1013 FileData2 *file_data2,
1014 char *full_directory_name,
1018 char full_file_name[MAX_PATH];
1019 char link_file_name[MAX_PATH];
1020 char link_path[MAX_PATH];
1021 char file_name_buf[MAXPATHLEN];
1022 char action_name_buf[MAXPATHLEN];
1023 char logical_type_buf[MAXPATHLEN];
1024 char link_buf[MAXPATHLEN];
1025 char final_link_buf[MAXPATHLEN];
1031 Boolean recursive_link_found;
1032 struct stat stat_buf;
1037 /* Allocate a new file structure. */
1039 /* get the full name of the file */
1040 strcpy (full_file_name, full_directory_name);
1044 /* append file name to the directory */
1045 if (strcmp(full_directory_name,"/") != 0)
1046 strcat (full_file_name, "/");
1047 strcat (full_file_name, file_name);
1051 /* no file name passed: use last component of directory */
1052 file_name = strrchr(full_file_name, '/');
1053 if (file_name > full_file_name)
1059 /* Follow symbolic links to their ultimate destination */
1062 recursive_link_found = False;
1063 strcpy(link_file_name, full_file_name);
1065 stat_result = lstat (link_file_name, &stat_buf);
1066 if ((stat_buf.st_mode & S_IFMT) == S_IFLNK)
1068 while ((link_len = readlink(link_file_name, link_path, MAX_PATH)) > 0)
1070 link_path[link_len] = NILL;
1071 link_list = (char **)XtRealloc((char *)link_list, sizeof(char *) *
1074 /* Force the link to be an absolute path, if necessary */
1075 if (link_path[0] != '/')
1077 /* Relative paths are relative to the current directory */
1078 end = strrchr(link_file_name, '/') + 1;
1080 strcat(link_file_name, link_path);
1083 strcpy(link_file_name, link_path);
1085 /* Check for a recursive loop; abort if found */
1086 for (i = 0; i < link_count; i++)
1088 if (strcmp(link_file_name, link_list[i]) == 0)
1090 /* Back up to last non-recursive portion */
1091 strcpy(link_file_name, link_list[link_count - 1]);
1092 recursive_link_found = True;
1097 if (recursive_link_found)
1100 link_list[link_count++] = XtNewString(link_file_name);
1101 link_list[link_count] = NULL;
1104 if ((stat_result = stat (link_file_name, &stat_buf)) != 0)
1106 /* probably a broken link; try lstat */
1107 stat_result = lstat (full_file_name, &stat_buf);
1108 strcpy(link_file_name, full_file_name);
1113 /* fill in the FileData2 structure with the information we found */
1114 file_data2->next = NULL;
1115 strcpy(file_name_buf, (file_name ? file_name : "."));
1116 logical_type_buf[0] = NILL;
1117 file_data2->is_subdir = False;
1118 action_name_buf[0] = NILL;
1122 strcpy(link_buf, link_list[0]);
1123 strcpy(final_link_buf, link_list[link_count - 1]);
1124 for (i = 0; i < link_count; i++) {
1125 XtFree(link_list[i]);
1127 XtFree((char *)link_list);
1129 final_link_buf[0] = NILL;
1133 if (stat_result == 0)
1135 file_data2->errnum = 0;
1136 file_data2->stat = stat_buf;
1138 /* Find and set the physical type of the file */
1140 if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
1142 file_data2->physical_type = DtDIRECTORY;
1143 if (file_name == NULL ||
1144 strcmp(file_name, ".") != 0 && strcmp(file_name, "..") != 0)
1146 file_data2->is_subdir = True;
1149 else if ((stat_buf.st_mode & S_IFMT) == S_IFREG)
1151 if ((stat_buf.st_mode & S_IXUSR) ||
1152 (stat_buf.st_mode & S_IXGRP) ||
1153 (stat_buf.st_mode & S_IXOTH))
1154 file_data2->physical_type = DtEXECUTABLE;
1156 file_data2->physical_type = DtDATA;
1159 file_data2->physical_type = DtDATA;
1161 /* Find and set the logical type of the file */
1162 if ((stat_buf.st_mode & S_IFMT) == S_IFLNK)
1164 file_data2->is_broken = True;
1165 if (recursive_link_found)
1166 strcpy(logical_type_buf, LT_RECURSIVE_LINK);
1168 strcpy(logical_type_buf, LT_BROKEN_LINK);
1174 file_data2->is_broken = False;
1175 if (link_buf[0] == NILL)
1176 ptr = (char *) DtDtsDataToDataType( full_file_name,
1181 ptr = (char *) DtDtsDataToDataType( link_buf,
1183 final_link_buf, NULL,
1186 #if defined( DATATYPE_IS_FIXED )
1187 strcpy(logical_type_buf, ptr);
1190 /* The problem here is there isn't a way for user to mask
1191 only the OWNER READ bit in the MODE field of dtfile.dt file.
1192 If the MODE field set to d&!r Then all READ permission
1193 (S_IRUSR, S_IRGRP and S_IROTH)
1194 bits has to be off in order for the above data typing to work.
1195 Also data typing is unable to detect when the directory is not
1196 the owners and only has execute permission by that owner.
1197 The work around is manually checking it ourselves.
1198 When the data typing code is fixed, please remove this check.
1200 if( !IsToolBox && S_ISDIR( stat_buf.st_mode ) &&
1201 (strcmp (ptr, LT_DIRECTORY) == 0))
1203 if( strcmp( file_name, ".." ) != 0
1204 && strcmp( file_name, "." ) != 0 )
1206 char * fullPathName;
1208 if( link_buf[0] == NILL )
1209 fullPathName = full_file_name;
1211 fullPathName = link_buf;
1213 if( access( fullPathName, R_OK ) != 0 )
1215 free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
1216 strcpy( logical_type_buf, LT_FOLDER_LOCK );
1218 else if( access( fullPathName, W_OK ) != 0 )
1220 free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
1221 strcpy( logical_type_buf, LT_NON_WRITABLE_FOLDER );
1225 strcpy( logical_type_buf, ptr );
1226 free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
1231 strcpy( logical_type_buf, ptr );
1232 free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
1237 strcpy( logical_type_buf, ptr );
1238 free( ptr ); /* Don't use XtFree. This pointer is being kept by tooltalk */
1243 if( DtActionExists(logical_type_buf) )
1245 char *ptr = (char *)DtActionLabel(file_name_buf);
1246 strcpy(action_name_buf, ptr);
1251 char *ptr = DtDtsDataTypeToAttributeValue(logical_type_buf,
1256 strcpy(action_name_buf, ptr);
1257 DtDtsFreeAttributeValue(ptr);
1263 /* couldn't stat the file */
1264 file_data2->errnum = stat_errno;
1265 memset(&file_data2->stat, 0, sizeof(file_data2->stat));
1266 file_data2->physical_type = DtUNKNOWN;
1267 file_data2->is_broken = True;
1268 strcpy(logical_type_buf, DtDEFAULT_DATA_FT_NAME);
1271 strcpy(file_data2->text, file_name_buf);
1272 file_data2->file_name = strlen(file_name_buf);
1274 strcat(file_data2->text, action_name_buf);
1275 file_data2->action_name = strlen(action_name_buf);
1277 strcat(file_data2->text, logical_type_buf);
1278 file_data2->logical_type = strlen(logical_type_buf);
1280 strcat(file_data2->text, link_buf);
1281 file_data2->link = strlen(link_buf);
1283 strcat(file_data2->text, final_link_buf);
1284 file_data2->final_link = strlen(final_link_buf);
1286 i = sizeof(*file_data2) - sizeof(file_data2->text)
1287 + file_data2->file_name + file_data2->action_name
1288 + file_data2->logical_type + file_data2->link
1289 + file_data2->final_link;
1291 i = (i + sizeof(char *) - 1) & ~(sizeof(char *) - 1);
1294 * This data marshalling operation relies on char[BUFSIZ]
1295 * being large enough for all the text pieces. However,
1296 * BUFSIZ has nothing to do with the above operations so
1297 * we'll do this assert for now.
1299 assert( (i <= sizeof(FileData2)) );
1304 /*--------------------------------------------------------------------
1306 * Resolves the links in the path.
1307 *------------------------------------------------------------------*/
1310 GetTTPath(char *path)
1312 Tt_message dummy_msg;
1313 char *tmp, *tt_path;
1314 Tt_status tt_status;
1316 dummy_msg = tt_message_create();
1317 tt_status = tt_message_file_set(dummy_msg, path);
1318 tmp = tt_message_file(dummy_msg);
1320 tt_path = XtNewString(tmp);
1323 tt_message_destroy(dummy_msg);
1330 ReadDirectoryProcess(
1332 Directory *directory,
1333 ActivityStatus activity)
1335 #ifdef DT_PERFORMANCE
1336 struct timeval update_time_s;
1337 struct timeval update_time_f;
1339 char *host_name = directory->host_name;
1340 char *directory_name = directory->directory_name;
1341 struct stat stat_buf;
1344 char **path_logical_types;
1345 char *full_directory_name;
1350 FileData *file_data;
1351 FileData2 file_data2;
1354 short file_data_count = 0;
1358 char file_name[MAX_PATH];
1361 int x, y, stacking_order;
1364 char file_data_buffer[FILEDATABUF * sizeof(FileData2)];
1365 char *file_data_buf_ptr = file_data_buffer;
1366 char *file_data_count_ptr;
1367 struct timeval time1, time2;
1371 DPRINTF(("ReadDirectoryProcess(%d, \"%s\", \"%s\")\n",
1372 pipe_fd, host_name, directory_name));
1375 * Get the logical data type of all components of the path;
1376 * We need only the last path component for (1) the tree root icon,
1377 * and (2) the current directory icon; the other path components
1378 * are needed for the iconic path icons.
1381 path_logical_types = NULL;
1383 /* Don't muck with original string */
1384 ptrOrig = ptr = XtNewString(directory_name);
1388 Tt_status tt_status;
1393 if (ptrOrig[0] == '\0')
1398 /* get logical type of next path component */
1399 full_directory_name = ResolveLocalPathName( host_name,
1404 if( TT_OK != tt_status )
1407 DtEliminateDots (full_directory_name);
1408 path_logical_types = (char **) XtRealloc((char *)path_logical_types,
1409 (path_count + 1)*sizeof(char *));
1410 path_logical_types[path_count] =
1411 (char *) DtDtsDataToDataType(full_directory_name, NULL, 0, NULL, NULL,
1413 #if defined( DATATYPE_IS_FIXED )
1416 if( stat( full_directory_name, &stat_buf ) == 0 )
1418 if( S_ISDIR( stat_buf.st_mode ) &&
1419 (strcmp (path_logical_types[path_count], LT_DIRECTORY) == 0))
1421 if( access( full_directory_name, R_OK ) != 0 )
1423 XtFree( path_logical_types[path_count] );
1424 path_logical_types[path_count] = XtNewString( LT_FOLDER_LOCK );
1426 else if( access( full_directory_name, W_OK ) != 0 )
1428 XtFree( path_logical_types[path_count] );
1429 path_logical_types[path_count] = XtNewString( LT_NON_WRITABLE_FOLDER );
1435 DPRINTF2(("ReadDirectoryProcess: path '%s', fullname '%s', type %s\n",
1436 namePtr, full_directory_name, path_logical_types[path_count]));
1438 XtFree( full_directory_name );
1447 /* find next component */
1448 if (strcmp(ptr, "/") == 0)
1450 ptr = DtStrchr(ptr + 1, '/');
1454 /* get the full name of the current directory */
1456 Tt_status tt_status;
1457 full_directory_name = ResolveLocalPathName( host_name,
1462 /* It's ok not to check for tt_status.
1463 The code below will handle it properly.
1467 /* send the path_logical_types back through the pipe */
1468 pipe_msg = PIPEMSG_PATH_LOGICAL_TYPES;
1469 DPRINTF(("ReadDirectoryProcess: sending %d path_logical_types\n",
1471 write(pipe_fd, &pipe_msg, sizeof(short));
1472 write(pipe_fd, &path_count, sizeof(int));
1473 for(i = 0; i < path_count; i++)
1475 PipeWriteString(pipe_fd, path_logical_types[i]);
1476 XtFree((char *) path_logical_types[i]);
1478 XtFree((char *) path_logical_types);
1480 /* send the tt_path */
1481 tt_path = GetTTPath(full_directory_name);
1482 PipeWriteString(pipe_fd, tt_path);
1486 * Stat the directory to get its timestamp.
1487 * Also check if we have read and execute/search permisssion.
1489 if (CheckAccess(full_directory_name, R_OK | X_OK) != 0 ||
1490 stat(full_directory_name, &stat_buf) != 0)
1492 /* send an error code back through the pipe */
1493 pipe_msg = PIPEMSG_ERROR;
1496 DPRINTF(("ReadDirectoryProcess: sending errno %d (stat failed)\n", rc));
1497 write(pipe_fd, &pipe_msg, sizeof(short));
1498 write(pipe_fd, &rc, sizeof(int));
1499 write(pipe_fd, &modify_time, sizeof(long));
1503 modify_time = stat_buf.st_mtime;
1506 * We never want to display the '~/.dt/Desktop' directory, so when we
1507 * are working with the .dt directory, add a special check for a
1508 * directory named 'Desktop'.
1510 if ((ptr = strrchr(full_directory_name, '/')) &&
1511 (strcmp(ptr, "/.dt") == 0))
1516 /* try to open the directory */
1517 dirp = opendir (full_directory_name);
1520 /* send an error code back through the pipe */
1521 pipe_msg = PIPEMSG_ERROR;
1523 DPRINTF(("ReadDirectoryProcess: sending errno %d (opendir failed)\n",
1525 write(pipe_fd, &pipe_msg, sizeof(short));
1526 write(pipe_fd, &rc, sizeof(int));
1527 write(pipe_fd, &modify_time, sizeof(long));
1528 XtFree( full_directory_name );
1532 /* Loop through the directory entries and update the file list */
1534 #ifdef DT_PERFORMANCE
1535 printf(" begin reading directory: %s\n", full_directory_name);
1536 gettimeofday(&update_time_s, NULL);
1540 * FILEDATA3 creates a buffer of static FileData2 structures,
1541 * then sends FILEDATABUF worth of FileData2 structs to the parent.
1542 * FILEDATABUF appears to work the best when set to 50.
1544 * We send data to the parent at least every half seconds, even if
1545 * less than FILEDATABUF worth of FileData2 structs have been read.
1546 * This is to ensure that the file count in the status line gets
1547 * updated every half seconds, even if the file system is slow.
1550 /* initialize pointer into file data buffer */
1551 # define PIPEMSG_HDR_LEN (2*sizeof(short) + sizeof(int))
1552 file_data_buf_ptr = file_data_buffer + PIPEMSG_HDR_LEN;
1554 /* get current time */
1555 gettimeofday(&time1, NULL);
1562 if ((dp = readdir (dirp)) != NULL)
1565 /* if Desktop skip */
1566 if (inDtDir && (strcmp(dp->d_name, "Desktop") == 0))
1570 if(directory->directoryView && directory->directoryView->file_mgr_data)
1571 IsToolBox = directory->directoryView->file_mgr_data->toolbox;
1574 len = ReadFileData2((FileData2 *)file_data_buf_ptr,
1575 full_directory_name, dp->d_name,IsToolBox);
1576 file_data_buf_ptr += len;
1582 /* check if 0.4 seconds have passed since the last status line update */
1583 gettimeofday(&time2, NULL);
1584 diff = 1024*(time2.tv_sec - time1.tv_sec);
1585 diff += time2.tv_usec/1024;
1586 diff -= time1.tv_usec/1024;
1587 update_due = (diff >= 400);
1589 /* check if we need to send the buffered data now */
1590 if (file_data_count == FILEDATABUF ||
1591 file_data_count > 0 && (done || update_due))
1594 file_data_count |= 0x8000;
1595 len = file_data_buf_ptr - (file_data_buffer + PIPEMSG_HDR_LEN);
1597 /* now send the file data through the pipe */
1598 *(short *)file_data_buffer = PIPEMSG_FILEDATA3;
1599 *(short *)(file_data_buffer + sizeof(short)) = file_data_count;
1600 *(int *)(file_data_buffer + 2*sizeof(short)) = len;
1601 write(pipe_fd, file_data_buffer,
1602 file_data_buf_ptr - file_data_buffer);
1604 /* reset pointer to file data buffer, file count and time stamp */
1605 file_data_buf_ptr = file_data_buffer + PIPEMSG_HDR_LEN;
1606 file_data_count = 0;
1612 #ifdef DT_PERFORMANCE
1613 gettimeofday(&update_time_f, NULL);
1614 if (update_time_s.tv_usec > update_time_f.tv_usec) {
1615 update_time_f.tv_usec += 1000000;
1616 update_time_f.tv_sec--;
1618 printf(" finished reading: %s, time: %ld.%ld\n\n", full_directory_name, update_time_f.tv_sec - update_time_s.tv_sec, update_time_f.tv_usec - update_time_s.tv_usec);
1622 /* load position info, if available */
1624 /* construct full name of the position info file */
1625 if (strcmp(full_directory_name,"/") != 0)
1626 sprintf( file_name, "%s/%s", full_directory_name, positionFileName );
1628 sprintf( file_name, "%s%s", full_directory_name, positionFileName );
1630 /* read the count from the position info file */
1632 if ((fptr = fopen(file_name, "r")) != NULL)
1634 PositionInfo * position_info;
1635 fscanf(fptr, "%d\n", &position_count);
1637 if (position_count > 0)
1639 /* allocate position info array */
1640 position_info = (PositionInfo *)
1641 XtMalloc(position_count * sizeof(PositionInfo));
1643 /* read the position info from the file */
1645 while (i < position_count)
1647 if( fgets( file_name, MAX_PATH, fptr ) != NULL &&
1648 fscanf(fptr, "%d %d %d\n", &x, &y, &stacking_order ) == 3 )
1650 int len = strlen(file_name);
1651 file_name[len-1] = 0x0;
1652 position_info[i].name = XtNewString(file_name);
1653 position_info[i].x = x,
1654 position_info[i].y = y,
1655 position_info[i].stacking_order = stacking_order;
1666 /* send the position info back through the pipe */
1667 pipe_msg = PIPEMSG_POSITION_INFO;
1668 DPRINTF(("ReadDirectoryProcess: sending %d position_info\n",
1670 write(pipe_fd, &pipe_msg, sizeof(short));
1671 write(pipe_fd, &position_count, sizeof(int));
1672 for (i = 0; i < position_count; i++)
1674 PipeWriteString( pipe_fd, position_info[i].name );
1675 XtFree( position_info[i].name );
1676 write( pipe_fd, &(position_info[i].x), sizeof(Position));
1677 write( pipe_fd, &(position_info[i].y), sizeof(Position));
1678 write( pipe_fd, &(position_info[i].stacking_order), sizeof(int));
1681 XtFree( (char *)position_info );
1685 XtFree(full_directory_name);
1688 /* send a 'done' msg through the pipe */
1689 DPRINTF(("ReadDirectoryProcess: sending DONE\n"));
1690 pipe_msg = PIPEMSG_DONE;
1691 write(pipe_fd, &pipe_msg, sizeof(short));
1692 write(pipe_fd, &modify_time, sizeof(long));
1697 /*--------------------------------------------------------------------
1699 * Main routine of the background process that checks the directory
1700 * for new files or files that have disapeared.
1701 *------------------------------------------------------------------*/
1706 Directory *directory,
1707 ActivityStatus activity)
1709 char *host_name = directory->host_name;
1710 char *directory_name = directory->directory_name;
1711 char *full_directory_name;
1712 struct stat stat_buf;
1717 FileData *file_data;
1718 FileData *old_data, **old_pp;
1719 FileData2 file_data2;
1723 Tt_status tt_status;
1724 char **path_logical_types;
1728 DPRINTF(("UpdateAllProcess(%d, \"%s\", \"%s\")\n",
1729 pipe_fd, host_name, directory_name));
1731 /* if modified list contains "." or ".." arrange for the path_logical_types
1733 if(directory->modified_count == 0 &&
1734 FindDirectory(directory->host_name, directory_name))
1740 for (i = 0; i < directory->modified_count; i++)
1742 if (strcmp(directory->modified_list[i],".") == 0 ||
1743 strcmp(directory->modified_list[i],"..") == 0 )
1755 path_logical_types = NULL;
1756 ptrOrig = ptr = XtNewString(directory_name);
1759 Tt_status tt_status;
1765 if (ptrOrig[0] == '\0')
1770 /* get logical type of next path component */
1771 full_directory_name = ResolveLocalPathName( host_name,
1776 if( TT_OK != tt_status )
1779 DtEliminateDots (full_directory_name);
1780 path_logical_types = (char **) XtRealloc((char *)path_logical_types,
1781 (path_count + 1)*sizeof(char *));
1782 path_logical_types[path_count] =
1783 (char *)DtDtsDataToDataType(full_directory_name, NULL, 0, NULL, NULL,
1785 #if defined( DATATYPE_IS_FIXED )
1788 if( stat( full_directory_name, &stat_buf ) == 0 )
1790 if( S_ISDIR( stat_buf.st_mode ) &&
1791 (strcmp (path_logical_types[path_count], LT_DIRECTORY) == 0))
1793 if( access( full_directory_name, R_OK ) != 0 )
1795 XtFree( path_logical_types[path_count] );
1796 path_logical_types[path_count] = XtNewString( LT_FOLDER_LOCK );
1798 else if( access( full_directory_name, W_OK ) != 0 )
1800 XtFree( path_logical_types[path_count] );
1801 path_logical_types[path_count] = XtNewString( LT_NON_WRITABLE_FOLDER );
1808 DPRINTF2(("ReadDirectoryProcess: path '%s', fullname '%s', type %s\n",
1809 namePtr, full_directory_name, path_logical_types[path_count]));
1811 XtFree(full_directory_name);
1820 /* find next component */
1821 if (strcmp(ptr, "/") == 0)
1823 ptr = DtStrchr(ptr + 1, '/');
1827 /* get the full name of the current directory */
1828 full_directory_name = ResolveLocalPathName( host_name,
1833 /* It's ok not to check for tt_status.
1834 The code below will handle it properly.
1837 /* send the path_logical_types back through the pipe */
1838 pipe_msg = PIPEMSG_PATH_LOGICAL_TYPES;
1839 DPRINTF(("ReadDirectoryProcess: sending %d path_logical_types\n",
1841 write(pipe_fd, &pipe_msg, sizeof(short));
1842 write(pipe_fd, &path_count, sizeof(int));
1843 for(i = 0; i < path_count; i++)
1845 PipeWriteString(pipe_fd, path_logical_types[i]);
1846 XtFree((char *) path_logical_types[i]);
1848 XtFree((char *) path_logical_types);
1850 /* send the tt_path */
1851 tt_path = GetTTPath(full_directory_name);
1852 PipeWriteString(pipe_fd, tt_path);
1857 full_directory_name = ResolveLocalPathName( host_name,
1864 if( TT_OK != tt_status )
1866 pipe_msg = PIPEMSG_ERROR;
1869 DPRINTF(("UpdateAllProcess: sending errno %d (tooltalk failed)\n", rc));
1870 write(pipe_fd, &pipe_msg, sizeof(short));
1871 write(pipe_fd, &rc, sizeof(int));
1872 write(pipe_fd, &modify_time, sizeof(long));
1875 (void) DtEliminateDots (full_directory_name);
1878 * Stat the directory to get its timestamp.
1879 * Also check if we still have read and execute/search permisssion.
1881 if (CheckAccess(full_directory_name, R_OK | X_OK) != 0 ||
1882 stat(full_directory_name, &stat_buf) != 0)
1884 /* send an error code back through the pipe */
1885 pipe_msg = PIPEMSG_ERROR;
1888 DPRINTF(("UpdateAllProcess: sending errno %d (stat failed)\n", rc));
1889 write(pipe_fd, &pipe_msg, sizeof(short));
1890 write(pipe_fd, &rc, sizeof(int));
1891 write(pipe_fd, &modify_time, sizeof(long));
1892 XtFree( full_directory_name );
1895 modify_time = stat_buf.st_mtime;
1897 /* check if we are in the .dt directory */
1898 if ((ptr = strrchr(full_directory_name, '/')) &&
1899 (strcmp(ptr, "/.dt") == 0))
1906 /* try to open the directory */
1907 dirp = opendir (full_directory_name);
1910 /* send an error code back through the pipe */
1911 pipe_msg = PIPEMSG_ERROR;
1913 DPRINTF(("UpdateAllProcess: sending errno %d (opendir failed)\n", rc));
1914 write(pipe_fd, &pipe_msg, sizeof(short));
1915 write(pipe_fd, &rc, sizeof(int));
1916 write(pipe_fd, &modify_time, sizeof(long));
1917 XtFree( full_directory_name );
1921 /* Loop through the directory entries and update the file list */
1922 while (dp = readdir (dirp))
1924 /* if Desktop skip */
1925 if (inDtDir && (strcmp(dp->d_name, "Desktop") == 0))
1928 /* check if we already know this file */
1929 for (old_pp = &directory->file_data;
1930 (old_data = *old_pp) != NULL;
1931 old_pp = &old_data->next)
1933 if (strcmp(dp->d_name, old_data->file_name) == 0)
1938 /* check modified times */
1939 tname = XtMalloc(strlen(full_directory_name)+strlen(dp->d_name)+2);
1940 sprintf(tname,"%s/%s",full_directory_name,dp->d_name);
1941 if((lstat(tname,&sbuf)>=0)&&sbuf.st_mtime!=old_data->stat.st_mtime)
1944 if(old_data == NULL)
1947 /* check if this file appears on the modified list */
1948 for (i = 0; i < directory->modified_count; i++)
1949 if (strcmp(dp->d_name, directory->modified_list[i]) == 0)
1952 * This file is on the modified list.
1953 * Pretend we didn't find it to force a refresh of this file.
1962 /* If this is a known file, remember we saw it and continue. */
1963 if (old_data != NULL)
1966 * We remove the file from the old file list; thus, when we are done,
1967 * the files on the old file list will be onew that no longer exist.
1969 *old_pp = old_data->next;
1973 /* this is a new file */
1974 DPRINTF(("UpdateAllProcess: found new file \"%s\"\n", dp->d_name));
1976 /* Fix for incorrect icons in App Manager */
1980 if(directory->directoryView && directory->directoryView->file_mgr_data)
1981 IsToolBox = directory->directoryView->file_mgr_data->toolbox;
1984 ReadFileData2(&file_data2, full_directory_name, dp->d_name,IsToolBox);
1986 file_data = FileData2toFileData(&file_data2, &n);
1988 /* now send the file data through the pipe */
1989 pipe_msg = PIPEMSG_FILEDATA;
1990 write(pipe_fd, &pipe_msg, sizeof(short));
1991 PipeWriteFileData(pipe_fd, file_data);
1993 FreeFileData(file_data, True);
1996 /* all files left in the old file list no longer exist */
1997 for (old_data = directory->file_data;
1999 old_data = old_data->next)
2001 DPRINTF(("UpdateAllProcess: file gone \"%s\"\n", old_data->file_name));
2002 old_data->errnum = ENOENT;
2003 pipe_msg = PIPEMSG_FILEDATA;
2004 write(pipe_fd, &pipe_msg, sizeof(short));
2005 PipeWriteFileData(pipe_fd, old_data);
2009 XtFree(full_directory_name);
2011 /* send a 'done' msg through the pipe */
2012 DPRINTF(("UpdateAllProcess: sending DONE\n"));
2013 pipe_msg = PIPEMSG_DONE;
2014 write(pipe_fd, &pipe_msg, sizeof(short));
2015 write(pipe_fd, &modify_time, sizeof(long));
2020 /*--------------------------------------------------------------------
2022 * Main routine of the background process that updates a selected
2023 * list of directory entries.
2024 *------------------------------------------------------------------*/
2029 Directory *directory,
2030 ActivityStatus activity)
2032 char *host_name = directory->host_name;
2033 char *directory_name = directory->directory_name;
2034 char *full_directory_name;
2035 struct stat stat_buf;
2037 FileData *file_data;
2038 FileData2 file_data2;
2044 DPRINTF(("UpdateSomeProcess(%d, \"%s\", \"%s\")\n",
2045 pipe_fd, host_name, directory_name));
2048 /* get the full name of the current directory */
2050 Tt_status tt_status;
2052 full_directory_name = ResolveLocalPathName( host_name,
2057 if( TT_OK != tt_status )
2059 pipe_msg = PIPEMSG_ERROR;
2062 DPRINTF(("UpdateSomeProcess: sending errno %d (stat failed)\n", rc));
2063 write(pipe_fd, &pipe_msg, sizeof(short));
2064 write(pipe_fd, &rc, sizeof(int));
2065 write(pipe_fd, &modify_time, sizeof(long));
2069 (void) DtEliminateDots (full_directory_name);
2071 /* stat the directory to get the timestamp */
2072 if (stat(full_directory_name, &stat_buf) < 0 ||
2073 ! (stat_buf.st_mode & S_IXUSR) )
2075 /* send an error code back through the pipe */
2076 pipe_msg = PIPEMSG_ERROR;
2079 DPRINTF(("UpdateSomeProcess: sending errno %d (stat failed)\n", rc));
2080 write(pipe_fd, &pipe_msg, sizeof(short));
2081 write(pipe_fd, &rc, sizeof(int));
2082 write(pipe_fd, &modify_time, sizeof(long));
2083 XtFree( full_directory_name );
2086 modify_time = stat_buf.st_mtime;
2088 /* Loop through the list of modified files */
2089 for (i = 0; i < directory->modified_count; i++)
2093 if(directory->directoryView && directory->directoryView->file_mgr_data)
2094 IsToolBox = directory->directoryView->file_mgr_data->toolbox;
2098 ReadFileData2(&file_data2, full_directory_name,
2099 directory->modified_list[i],IsToolBox);
2101 /* now send the file data through the pipe */
2102 pipe_msg = PIPEMSG_FILEDATA2;
2103 write(pipe_fd, &pipe_msg, sizeof(short));
2104 write(pipe_fd, &file_data2, sizeof(FileData2));
2107 XtFree(full_directory_name);
2109 /* send a 'done' msg through the pipe */
2110 DPRINTF(("UpdateSomeProcess: sending DONE\n"));
2111 pipe_msg = PIPEMSG_DONE;
2112 write(pipe_fd, &pipe_msg, sizeof(short));
2113 write(pipe_fd, &modify_time, sizeof(long));
2118 /*--------------------------------------------------------------------
2119 * ReaddirPipeCallback
2120 * Callback routine that reads directory entry information sent
2121 * through the pipe from the background process.
2122 *------------------------------------------------------------------*/
2125 ReaddirPipeCallback(
2126 XtPointer client_data,
2130 static whined_fd = 0;
2131 PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
2132 Directory *directory = pipe_data->directory;
2133 FileMgrData *file_mgr_data;
2134 FileMgrRec *file_mgr_rec;
2135 ActivityStatus activity;
2139 FileData *new_data, **new_nextp;
2140 FileData *old_data, **old_nextp;
2146 char dirname[MAX_PATH];
2147 short file_data_count;
2150 /* verify that the directory still exists */
2151 if (DirectoryGone(directory))
2154 * The directory is no longer present:
2155 * close the pipe and kill the reader.
2159 kill(pipe_data->child, SIGKILL);
2160 XtFree( client_data );
2161 ScheduleActivity(NULL);
2165 /* read the next msg from the pipe */
2167 n = PipeRead(*fd, &msg, sizeof(short));
2168 activity = directory->activity;
2173 case PIPEMSG_PATH_LOGICAL_TYPES:
2174 /* get the number of path components */
2175 PipeRead(*fd, &n, sizeof(int));
2177 /* free logical types */
2178 for (i = 0; i < directory->path_count; i++)
2179 XtFree(directory->path_logical_types[i]);
2181 /* allocate array of the right size */
2182 if (directory->path_count != n)
2184 directory->path_count = n;
2185 directory->path_logical_types = (char **)
2186 XtRealloc((char *)directory->path_logical_types,
2190 /* get new logical types */
2191 for (i = 0; i < directory->path_count; i++)
2192 directory->path_logical_types[i] = PipeReadString(*fd);
2194 /* get the tt_path */
2195 XtFree(directory->tt_path_name);
2196 directory->tt_path_name = PipeReadString(*fd);
2198 /* update all views */
2199 for (i = 0; i < directory->numOfViews; i++)
2201 file_mgr_data = directory->directoryView[i].file_mgr_data;
2202 file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2203 UpdateHeaders(file_mgr_rec, file_mgr_data, True);
2204 XmUpdateDisplay(file_mgr_rec->file_window);
2206 XSync(XtDisplay(toplevel), False);
2209 case PIPEMSG_FILEDATA:
2210 case PIPEMSG_FILEDATA2:
2211 case PIPEMSG_FILEDATA3:
2213 if (msg == PIPEMSG_FILEDATA)
2215 new_data = PipeReadFileData(*fd);
2217 else if (msg == PIPEMSG_FILEDATA2)
2219 FileData2 file_data2;
2222 n = PipeRead(*fd, &file_data2, sizeof(FileData2));
2223 if (n < sizeof(FileData2)) {
2225 fprintf(stderr, "PipeReadFileData2: n = %d, expected %ld\n",
2226 n, (long)sizeof(FileData2));
2229 new_data = FileData2toFileData(&file_data2, &n);
2232 if (msg == PIPEMSG_FILEDATA3)
2234 int file_data_length;
2236 char file_data_buffer[FILEDATABUF * sizeof(FileData2)];
2237 char *file_data_buf_ptr;
2240 n = PipeRead(*fd, &file_data_count, sizeof(short));
2241 n = PipeRead(*fd, &file_data_length, sizeof(int));
2243 if (file_data_count & 0x8000)
2245 file_data_count &= 0x7fff;
2251 for (new_nextp = &directory->new_data;
2253 new_nextp = &(*new_nextp)->next)
2256 n = PipeRead(*fd, file_data_buffer, file_data_length);
2257 file_data_buf_ptr = file_data_buffer;
2259 for (i = 0; i < file_data_count; i++)
2261 /* get next FileData out of buffer */
2263 FileData2toFileData((FileData2 *)file_data_buf_ptr, &n);
2264 file_data_buf_ptr += n;
2266 /* append new_data to end of list */
2267 *new_nextp = new_data;
2268 new_data->next = NULL;
2269 new_nextp = &new_data->next;
2274 /* append new_data to end of list */
2275 file_data_count = 1;
2277 for (new_nextp = &directory->new_data;
2279 new_nextp = &(*new_nextp)->next)
2281 *new_nextp = new_data;
2282 new_data->next = NULL;
2285 if (activity == activity_reading)
2287 /* update file counts in all views */
2288 for (i = 0; i < directory->numOfViews; i++)
2290 file_mgr_data = directory->directoryView[i].file_mgr_data;
2291 file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2292 file_mgr_data->busy_detail += file_data_count;
2294 (file_mgr_data->busy_status == initiating_readdir ||
2295 file_mgr_data->busy_status == busy_readdir) &&
2296 file_mgr_data->busy_detail > 2)
2298 if (file_mgr_data->show_status_line)
2301 XmString label_string;
2304 GetStatusMsg(file_mgr_data, buf);
2306 XmStringCreateLocalized(buf);
2307 XtSetArg (args[0], XmNlabelString, label_string);
2308 XtSetValues(file_mgr_rec->status_line, args, 1);
2309 XmStringFree(label_string);
2311 else if (file_mgr_data->show_iconic_path)
2313 DtUpdateIconicPath(file_mgr_rec, file_mgr_data, False);
2315 else if (file_mgr_data->show_current_dir)
2317 DrawCurrentDirectory(file_mgr_rec->current_directory,
2318 file_mgr_rec, file_mgr_data);
2325 case PIPEMSG_POSITION_INFO:
2326 /* free old position info names */
2327 for (i = 0; i < directory->position_count; i++)
2328 XtFree(directory->position_info[i].name);
2330 /* get number of positions and realloc array, if necessary */
2331 PipeRead(*fd, &n, sizeof(int));
2332 if (directory->position_count != n)
2334 directory->position_count = n;
2335 directory->position_info = (PositionInfo *) XtRealloc(
2336 (char *)directory->position_info, n*sizeof(PositionInfo));
2339 /* read new position info */
2340 for (i = 0; i < n; i++)
2341 PipeReadPositionInfo(*fd, &directory->position_info[i]);
2345 PipeRead(*fd, &modify_time, sizeof(long));
2346 directory->errnum = 0;
2347 directory->errmsg_needed = False;
2352 PipeRead(*fd, &rc, sizeof(int));
2353 PipeRead(*fd, &modify_time, sizeof(long));
2354 if (rc != directory->errnum)
2356 directory->errnum = rc;
2357 directory->errmsg_needed = True;
2363 if (whined_fd != *fd)
2367 "ReaddirPipeCallback: badmsg, ppid=%d pid=%d fd=%d activ'y=%d\n",
2368 msg, getppid(), getpid(), *fd, activity);
2370 directory->errnum = -1;
2371 directory->errmsg_needed = False;
2375 /* check if we are done */
2378 #ifdef DT_PERFORMANCE
2379 /* Aloke Gupta: As suggested by Dana Dao */
2380 _DtPerfChkpntMsgSend("Done Read Directory");
2382 DPRINTF(("ReaddirPipeCallback: done, errno %d, time %ld\n",
2383 directory->errnum, modify_time));
2385 /* close the pipe and cancel the callback */
2390 * @@@ what if a drag is active ???
2394 * For files in the old list that still exist in the new
2395 * one we need to re-use the old FileData structures.
2396 * Reason: the code in GetFileData relies on this to
2397 * preserve the position_info and selection list.
2398 * The following loops through the new list of files
2399 * and replaces entries that also exist in the old list.
2401 for (new_nextp = &directory->new_data;
2402 (new_data = *new_nextp) != NULL;
2403 new_nextp = &new_data->next)
2405 for (old_nextp = &directory->file_data;
2406 (old_data = *old_nextp) != NULL;
2407 old_nextp = &old_data->next)
2409 if( strcmp(old_data->file_name, new_data->file_name) == 0 )
2411 *old_nextp = old_data->next;
2413 FreeFileData(old_data, False);
2414 memcpy(old_data, new_data, sizeof(FileData));
2416 XtFree((char *)new_data);
2417 *new_nextp = new_data = old_data;
2425 * If this was a complete re-read, we free all FileData still left
2426 * in the old list. Otherwise, if this was just a partial update,
2427 * we append the old data that's still left to the end of the
2430 if (activity == activity_reading)
2432 /* This was a complete re-read: free all old data still left. */
2433 while (directory->file_data)
2435 old_data = directory->file_data;
2436 directory->file_data = old_data->next;
2437 FreeFileData(old_data, True);
2440 /* replace the old list by the new list */
2441 directory->file_data = directory->new_data;
2442 directory->new_data = NULL;
2446 FileData * tmp_ptr = NULL;
2448 /* remove any directory entries that no longer exist
2451 new_nextp = &directory->new_data;
2452 while ((new_data = *new_nextp) != NULL)
2454 if (new_data->errnum == ENOENT)
2456 *new_nextp = new_data->next;
2457 FreeFileData(new_data, True);
2461 tmp_ptr = *new_nextp;
2462 new_nextp = &new_data->next;
2466 /* remove any directory entries that no longer exist
2469 old_nextp = &directory->file_data;
2470 while ((old_data = *old_nextp) != NULL)
2472 if (old_data->errnum == ENOENT)
2474 *old_nextp = old_data->next;
2475 FreeFileData(old_data, True);
2478 old_nextp = &old_data->next;
2481 /* Append the old list to the end of the new list
2482 Replace the old list pointer with the new list pointer
2484 if( tmp_ptr != NULL )
2486 tmp_ptr->next = directory->file_data;
2487 directory->file_data = directory->new_data;
2488 directory->new_data = NULL;
2492 /* update the file count */
2493 directory->file_count = 0;
2494 for (new_data = directory->file_data; new_data; new_data = new_data->next)
2495 directory->file_count++;
2497 /* update directory timestamp */
2498 if (activity == activity_reading ||
2499 activity == activity_update_all ||
2500 directory->was_up_to_date)
2502 if (modify_time != 0)
2503 directory->modify_time = modify_time;
2506 /* flush the icon cache */ /* @@@ Why? What does this do? @@@ */
2507 strcpy (dirname, directory->path_name);
2508 DtEliminateDots(dirname);
2509 /* We will not attempt to flush the icon cache until this */
2510 /* function has been fixed. */
2512 _DtFlushIconFileCache(dirname);
2514 /* reset busy flags */
2515 directory->busy[activity] = False;
2516 directory->activity = activity_idle;
2517 directory->was_up_to_date = True;
2518 directory->link_check_needed = False;
2522 * Fill dir_data field with information on the directory itself.
2523 * This data will be read when querying this view's top directory,
2524 * if the parent directory isn't already cached (tree mode)
2526 for (new_data = directory->file_data;
2528 new_data = new_data->next)
2529 if (strcmp(new_data->file_name, ".") == 0)
2533 * Found current directory information, now we make
2534 * dir_data info from "." info
2537 /* If we already have allocated space for dir_data free it */
2538 if ( directory->dir_data != NULL )
2539 FreeFileData(directory->dir_data, True);
2541 directory->dir_data = (FileData *)XtMalloc(sizeof(FileData));
2543 memcpy(directory->dir_data, new_data, sizeof(FileData));
2546 * Doctor up some of the information fields so that this doesn't
2547 * seem to be a "." entry
2549 directory->dir_data->next = NULL;
2550 directory->dir_data->file_name =
2551 XtNewString(DName(directory->directory_name));
2552 directory->dir_data->action_name = NULL;
2553 if (directory->path_count > 0)
2555 directory->dir_data->logical_type = XtNewString(
2556 directory->path_logical_types[directory->path_count - 1]);
2559 directory->dir_data->logical_type = NULL;
2560 directory->dir_data->link = NULL;
2561 directory->dir_data->final_link = NULL;
2562 directory->dir_data->is_subdir = True;
2567 /* cause all views on this directory to be redrawn */
2568 for (i = 0; i < directory->numOfViews; i++)
2570 file_mgr_data = directory->directoryView[i].file_mgr_data;
2571 file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2572 FileMgrRedisplayFiles(file_mgr_rec, file_mgr_data, False);
2573 if(file_mgr_data->desktop_file)
2575 SelectDesktopFile(file_mgr_data);
2576 XtFree(file_mgr_data->desktop_file);
2577 file_mgr_data->desktop_file = NULL;
2580 XtFree(client_data);
2582 /* schedule the next background activity */
2583 ScheduleActivity(directory);
2588 /*--------------------------------------------------------------------
2589 * ReadDirectoryFiles
2590 * This routine is called to read a directory if the directory
2591 * wasn't found in the cached, or if the directory has to be
2592 * re-read because it changed. This routine schedules a background
2593 * process to be started that will do the actual work.
2594 *------------------------------------------------------------------*/
2599 Directory *directory)
2601 FileMgrData *file_mgr_data;
2602 FileMgrRec *file_mgr_rec;
2605 #ifdef DT_PERFORMANCE
2607 _DtPerfChkpntMsgSend("Begin Read Directory");
2610 /* make sure positionFileName is initialized */
2611 if (positionFileName == NULL)
2612 InitializePositionFileName();
2614 /* mark the directory busy reading */
2615 directory->busy[activity_reading] = True;
2617 /* arrange for background process to be started */
2618 ScheduleActivity(directory);
2620 /* make sure all views on this directory are marked busy */
2621 for (i = 0; i < directory->numOfViews; i++)
2623 file_mgr_data = directory->directoryView[i].file_mgr_data;
2624 if (file_mgr_data->busy_status == not_busy)
2626 file_mgr_data->busy_status = busy_readdir;
2627 file_mgr_data->busy_detail = 0;
2628 file_mgr_rec = (FileMgrRec *)file_mgr_data->file_mgr_rec;
2629 FileMgrRedisplayFiles(file_mgr_rec, file_mgr_data, False);
2631 file_mgr_data->busy_status = busy_readdir;
2637 /*--------------------------------------------------------------------
2639 * Given a directory name, see if the directory is already cached.
2640 * If so, return the file data list, otherwise, read the directory.
2641 *------------------------------------------------------------------*/
2647 char *directory_name,
2648 FileData **file_data,
2650 FileMgrData *file_mgr_data)
2652 Directory *directory;
2656 /* initialize return values */
2657 if (file_data != NULL)
2663 /* see if the directory is already in the cache */
2664 directory = FindDirectory(host_name, directory_name);
2666 if ((directory != NULL) &&
2667 (strcmp(directory_name, directory->directory_name) == 0))
2669 /* The directory is already in the cache. */
2670 directory->viewed = True;
2672 /* Look for the view in the view list */
2673 for (i = 0; i < directory->numOfViews; i++)
2674 if (directory->directoryView[i].file_mgr_data == file_mgr_data)
2677 /* If view not found, add to the view list */
2678 if (i == directory->numOfViews)
2680 directory->directoryView = (DirectoryView *)
2681 XtRealloc ((char *) directory->directoryView,
2682 sizeof(DirectoryView) * (i + 1));
2683 directory->numOfViews++;
2684 directory->directoryView[i].file_mgr_data = file_mgr_data;
2687 /* set mapped flag for the view */
2688 directory->directoryView[i].mapped = file_mgr_data->mapped;
2690 /* check if we need to popup an error message */
2691 if (directory->errmsg_needed &&
2692 !directory->busy[activity_reading] &&
2695 err_msg = XtNewString(GetSharedMessage(CANNOT_READ_DIRECTORY_ERROR));
2696 FileOperationError (w, err_msg, directory_name);
2698 directory->errmsg_needed = False;
2701 DPRINTF2(("ReadDirectory(\"%s\", \"%s\") returns cached\n",
2702 host_name, directory_name));
2707 Tt_status tt_status;
2709 /* The directory is not yet in the cache. */
2711 /* Expand the directory set array, if necessary. */
2712 if (directory_count == directory_set_size)
2714 directory_set_size += 10;
2715 directory_set = (Directory **) XtRealloc((char *)directory_set,
2716 sizeof(Directory **) * directory_set_size);
2720 /* Create and initialize a new directory entry */
2721 directory_set[directory_count] = directory =
2722 (Directory *) XtMalloc (sizeof (Directory));
2725 directory->host_name = XtNewString (host_name);
2726 directory->directory_name = XtNewString (directory_name);
2727 directory->path_name = ResolveLocalPathName (host_name,
2732 if (directory->path_name == NULL)
2734 directory->path_name = (char *) XtMalloc(sizeof(char));
2735 directory->path_name[0]='\0';
2737 directory->tt_path_name = NULL;
2738 directory->viewed = True;
2739 directory->file_count = 0;
2740 directory->numOfViews = 1;
2741 directory->errnum = 0;
2742 directory->errmsg_needed = False;
2743 directory->last_check = 0;
2744 directory->link_check_needed = False;
2745 directory->file_count = 0;
2746 directory->file_data = NULL;
2747 directory->new_data = NULL;
2748 directory->dir_data = NULL;
2749 directory->path_count = 0;
2750 directory->path_logical_types = NULL;
2751 directory->position_count = 0;
2752 directory->position_info = NULL;
2753 directory->modify_begin = 0;
2754 directory->modified_count = 0;
2755 directory->was_up_to_date = True;
2756 directory->modified_list = NULL;
2757 directory->activity = activity_idle;
2758 for (i = 0; i < activity_idle; i++)
2759 directory->busy[i] = False;
2761 directory->directoryView = (DirectoryView *)
2762 XtMalloc (sizeof(DirectoryView));
2763 directory->directoryView[0].file_mgr_data = file_mgr_data;
2764 directory->directoryView[0].mapped = file_mgr_data->mapped;
2766 /* Open the directory for reading and read the files. */
2767 ReadDirectoryFiles (w, directory);
2770 /* Restart refresh timer, if necessary */
2771 if (file_mgr_data->mapped && timer_suspended)
2773 XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
2774 timer_suspended = False;
2777 /* return the file data */
2778 if (file_data != NULL && !directory->busy[activity_reading])
2780 *file_count = directory->file_count;
2781 *file_data = directory->file_data;
2784 return directory->busy[activity_reading];
2788 /*--------------------------------------------------------------------
2790 * Internal routine that recursively read a directory plus
2791 * subdirectories down to a depth given by read_level.
2792 *------------------------------------------------------------------*/
2797 FileMgrData *file_mgr_data,
2799 char *directory_name,
2800 FileViewData *dp, /* directory info */
2801 int level, /* tree level of this directory */
2802 int read_level, /* deepest level to be read */
2803 char **branch_list) /* list of tree branches to expand */
2805 * Recursively read a directory plus subdirectories down to a depth
2806 * given by read_level.
2809 char subdir_name[MAX_PATH];
2810 FileData *fp, *file_data;
2811 FileViewData **lp, *ip;
2814 Boolean busy_reading;
2816 DPRINTF2(("_ReadDir(\"%s\", \"%s\"): level %d, read_level %d\n",
2817 host_name, directory_name, level, read_level));
2819 /* initialize list of descendents and counts */
2823 dp->ndir = dp->nfile = 0;
2828 /* Read the directory content */
2829 busy_reading = ReadDirectory(w, host_name, directory_name,
2830 &file_data, &n, file_mgr_data);
2833 file_mgr_data->busy_status = busy_readdir;
2842 for (i = 0, fp = file_data; i < n && fp; i++, fp = fp->next) {
2844 /* initialize new dir entry */
2847 ip = (FileViewData *)XtMalloc(sizeof(FileViewData));
2848 memset(ip, 0, sizeof(FileViewData));
2854 /* read subdirectory */
2857 /* construct sub directory name */
2858 strcpy(subdir_name, directory_name);
2859 if (subdir_name[strlen(subdir_name) - 1] != '/')
2860 strcat(subdir_name, "/");
2861 strcat(subdir_name, fp->file_name);
2863 /* see if we know this entry from branch_list */
2864 if (!QueryBranchList(file_mgr_data, branch_list, subdir_name, &ts))
2865 /* not known: assume we shouldn't read this subdir */
2868 if (level < read_level || ts != tsNotRead) {
2870 rc = _ReadDir(w, file_mgr_data, host_name, subdir_name, ip,
2871 level, read_level, branch_list);
2876 else if (ts >= tsReading)
2878 else if (level >= file_mgr_data->tree_show_level)
2880 else if (file_mgr_data->tree_files == TREE_FILES_ALWAYS)
2887 /* add new entry to linked list */
2899 /*--------------------------------------------------------------------
2901 * This is the main external entry point for reading directories.
2902 *------------------------------------------------------------------*/
2907 FileMgrData *file_mgr_data,
2909 char *directory_name,
2910 FileViewData *dp, /* directory info */
2911 int level, /* tree level of this directory */
2912 int read_level, /* deepest level to be read */
2913 char **branch_list) /* list of tree branches to expand */
2915 /* initially assume we are not busy */
2916 if (file_mgr_data->busy_status == not_busy)
2917 file_mgr_data->busy_detail = 0;
2919 file_mgr_data->busy_status = initiating_readdir;
2921 /* first pass: just check if any directory we need is busy */
2922 _ReadDir(w, file_mgr_data, host_name, directory_name, NULL, level,
2923 read_level, branch_list);
2925 /* if a directory we need is busy, return now */
2926 if (file_mgr_data->busy_status == busy_readdir)
2930 * All directories wee need are available.
2931 * Make a second pass for real.
2933 file_mgr_data->busy_status = not_busy;
2934 return _ReadDir(w, file_mgr_data, host_name, directory_name, dp, level,
2935 read_level, branch_list);
2939 /*====================================================================
2941 * Routines that update the directory cache
2943 *==================================================================*/
2945 /*--------------------------------------------------------------------
2946 * FileWindowMapUnmap
2947 * Update mapped flag in view lists.
2948 *------------------------------------------------------------------*/
2952 FileMgrData *file_mgr_data)
2957 for (i = 0; i < directory_count; i++)
2959 for (j = 0; j < directory_set[i]->numOfViews; j++)
2961 if (file_mgr_data == directory_set[i]->directoryView[j].file_mgr_data)
2963 directory_set[i]->directoryView[j].mapped = file_mgr_data->mapped;
2969 if (file_mgr_data->mapped && timer_suspended)
2971 XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
2972 timer_suspended = False;
2977 /*--------------------------------------------------------------------
2979 * Read a directory already cached and update its contents.
2980 *------------------------------------------------------------------*/
2986 char *directory_name )
2988 Directory *directory;
2990 DPRINTF(("RereadDirectory(%s, %s)\n", host_name, directory_name));
2992 /* Find the directory set entry. */
2993 directory = FindDirectory(host_name, directory_name);
2994 if (directory != NULL)
2996 /* reset errnum to make sure we'll get an error message */
2997 directory->errnum = 0;
2999 /* Read the directory. */
3000 if (!directory->busy[activity_reading])
3001 ReadDirectoryFiles(w, directory);
3006 /*--------------------------------------------------------------------
3008 * Check if any files were added or deleted in a directory
3009 * and update the directory contents accordingly.
3010 *------------------------------------------------------------------*/
3016 char *directory_name )
3018 Directory *directory;
3020 DPRINTF(("UpdateDirectory(%s, %s)\n", host_name, directory_name));
3022 /* Find the directory set entry. */
3023 directory = FindDirectory(host_name, directory_name);
3024 if (directory != NULL)
3026 /* arrange for directory contents to be checked */
3027 if (!directory->busy[activity_update_all])
3029 directory->busy[activity_update_all] = True;
3030 ScheduleActivity(directory);
3036 /*====================================================================
3038 * Directory modification routines:
3040 * The following routines are provided to avoid unnecessary
3041 * re-reads of whole directories. For example, if the user
3042 * renames a file, it's only necessary to remove the old file
3043 * from the directory and add it back under its new name; there
3044 * is no need to read the whole directory again. Similarly,
3045 * when a file is dropped on a directory, it's only necessary
3046 * to add the one new file to the directory.
3048 * To accomplish this, the routines that rename or copy files
3049 * make the following calls:
3051 * DirectoryBeginModify(): called before doing the operation
3052 * DirectoryFileModified(): called once for each affected file
3053 * DirectoryEndModify(): called when the operation is completed
3055 * The routines remember which files were modified, and when
3056 * DirectoryEndModify is called, a background process is started,
3057 * that re-stats and types just those files.
3059 * A complication arises from automatic re-reads triggered by
3060 * a periodic timer (routine TimerEvent). Since renaming or
3061 * copying files changes the timestamp on the directory, the
3062 * automatic re-read would re-read the whole directory soon after
3063 * the operation is done, nullifying our efforts to avoid
3064 * unnecessary re-reads. Therefore:
3066 * - We don't do any automatic re-reads between calls to
3067 * DirectoryBeginModify and DirectoryEndModify.
3069 * - If the directory timestamp hadn't changed at the time
3070 * of the DirectoryBeginModify, then when the directory
3071 * update triggered by DirectoryEndModify finishes, we
3072 * set the modify_time in the directory_set to the current
3073 * timestamp of the directory. This means that the next
3074 * automatic re-read won't be triggered unless the directory
3075 * is modified again after the DirectoryEndModify.
3077 *==================================================================*/
3079 /*--------------------------------------------------------------------
3080 * DirectoryAbortModify
3081 * Decrement the modify_begin counter.
3082 *------------------------------------------------------------------*/
3085 DirectoryAbortModify(
3087 char *directory_name)
3089 Directory *directory;
3091 DPRINTF(("DirectoryAbortModify(%s, %s)\n", host_name, directory_name));
3093 /* Find the directory set entry. */
3094 directory = FindDirectory(host_name, directory_name);
3095 if (directory != NULL)
3097 directory->modify_begin--;
3099 if (directory->modify_begin == 0)
3100 directory->was_up_to_date = True;
3102 DPRINTF((" modify_begin %d, up_to_date %d\n",
3103 directory->modify_begin, directory->was_up_to_date));
3108 /*--------------------------------------------------------------------
3109 * DirectoryBeginModify
3110 * Increment the modify_begin counter to suspend automatic
3111 * re-reads until DirectoryEndModify is called.
3112 *------------------------------------------------------------------*/
3115 DirectoryBeginModify(
3117 char *directory_name)
3119 Directory *directory;
3121 DPRINTF(("DirectoryBeginModify(%s, %s)\n", host_name, directory_name));
3123 /* Find the directory set entry. */
3124 directory = FindDirectory(host_name, directory_name);
3125 if (directory != NULL)
3127 if (directory->modify_begin == 0)
3128 /* until we know better, assume the directory changed */
3129 directory->was_up_to_date = False;
3131 /* increment the modify_begin counter */
3132 directory->modify_begin++;
3134 DPRINTF((" modify_begin %d, up_to_date %d\n",
3135 directory->modify_begin, directory->was_up_to_date));
3140 /*--------------------------------------------------------------------
3141 * DirectoryModifyTime
3142 * This routine should be called after DirectoryBeginModify and
3143 * before doing any operation on the directory. The parameter
3144 * modify_time should be the current timestamp of the directory.
3145 * By comparing the value to the modify_time stored in the
3146 * directory set we decide whether the directory had already
3147 * changed before the update operation began.
3148 * Note: the reason for supplying a separate call for this check,
3149 * instead of doing it inside DirectoryBeginModify(), is that we
3150 * want to do the stat call that determines the current timestamp
3151 * of the directory in a background process. The background
3152 * process that we start fo do the actual update is a convenient
3154 *------------------------------------------------------------------*/
3157 DirectoryModifyTime(
3159 char *directory_name,
3162 Directory *directory;
3164 DPRINTF(("DirectoryModifyTime(%s, %s)\n", host_name, directory_name));
3166 #ifdef SMART_DIR_UPDATE
3167 /* Find the directory set entry. */
3168 directory = FindDirectory(host_name, directory_name);
3170 if (directory != NULL)
3172 /* mark directory up-to-date if unchanged since last read */
3173 if (modify_time <= directory->modify_time)
3174 directory->was_up_to_date = True;
3175 DPRINTF((" modify_begin %d, up_to_date %d\n",
3176 directory->modify_begin, directory->was_up_to_date));
3182 /*--------------------------------------------------------------------
3183 * DirectoryFileModified
3184 * This routine is called when we know that a file in a directory
3185 * has been modified, added or removed. The file name is added
3186 * to the list of modified files. The next time an update
3187 * background process is started, it will check all the files
3188 * on the modfied list and update the corresponding FileData.
3189 *------------------------------------------------------------------*/
3192 DirectoryFileModified(
3194 char *directory_name,
3197 Directory *directory;
3200 DPRINTF(("DirectoryFileModified(%s, %s, %s)\n",
3201 host_name, directory_name, file_name));
3203 /* Find the directory set entry. */
3204 directory = FindDirectory(host_name, directory_name);
3205 if (directory != NULL)
3207 /* see if the file is already on the list */
3208 for( i = 0; i < directory->modified_count; ++i )
3209 if( strcmp( directory->modified_list[i], file_name ) == 0 )
3212 /* add the file to the modified_list */
3213 i = directory->modified_count++;
3214 directory->modified_list = (char **)
3215 XtRealloc((char *)directory->modified_list, (i + 1)*sizeof(char *));
3216 directory->modified_list[i] = XtNewString(file_name);
3221 /*--------------------------------------------------------------------
3222 * DirectoryEndModify
3223 * Start an update background process (will check all the files
3224 * on the modfied list and update the corresponding FileData).
3225 *------------------------------------------------------------------*/
3230 char *directory_name)
3232 Directory *directory;
3234 DPRINTF(("DirectoryEndModify(%s, %s)\n", host_name, directory_name));
3236 /* Find the directory set entry. */
3237 directory = FindDirectory(host_name, directory_name);
3239 /* arrange for an update background process to be scheduled */
3240 if (directory != NULL)
3242 directory->modify_begin--;
3243 DPRINTF((" modify_begin %d, up_to_date %d, modified_count %d\n",
3244 directory->modify_begin,
3245 directory->was_up_to_date,
3246 directory->modified_count));
3247 if (directory->modified_count > 0)
3250 char subdir_name[MAX_PATH + 1];
3255 * If any of the modifed files is a subdirectory that we have
3256 * cached, schedule an activity_checking_dir to make sure that
3257 * the subdirectory is still readable.
3259 strcpy(subdir_name, directory_name);
3260 p = subdir_name + strlen(subdir_name);
3264 for (i = 0; i < directory->modified_count; i++)
3266 strcpy(p, directory->modified_list[i]);
3267 subdir = FindDirectory(host_name, subdir_name);
3270 DPRINTF((" schedule check for subdir \"%s\"\n",
3271 directory->modified_list[i]));
3272 subdir->busy[activity_checking_dir] = True;
3273 ScheduleActivity(subdir);
3277 #ifdef SMART_DIR_UPDATE
3278 /* schedule a partial update of the modfied directory */
3279 if (directory->was_up_to_date)
3280 directory->busy[activity_update_some] = True;
3282 directory->busy[activity_update_all] = True;
3284 /* schedule a full update of the modfied directory */
3285 directory->busy[activity_update_all] = True;
3287 ScheduleActivity(directory);
3293 /*--------------------------------------------------------------------
3294 * UpdateDirectorySet
3295 * We call this when we do a database update. It loops through
3296 * the directory_set list and rereads each directory.
3297 *------------------------------------------------------------------*/
3300 UpdateDirectorySet( void )
3304 DPRINTF(("UpdateDirectorySet ...\n"));
3306 for (i = 0; i < directory_count; i++)
3307 if (!directory_set[i]->busy[activity_reading])
3308 ReadDirectoryFiles (NULL, directory_set[i]);
3313 /*--------------------------------------------------------------------
3314 * UpdateCachedDirectories
3315 * Update view list for all cached directories.
3316 * Throw out any directories that are no longer being viewed.
3317 *------------------------------------------------------------------*/
3320 UpdateCachedDirectories(
3324 DialogData * dialog_data;
3325 FileMgrData * file_mgr_data;
3327 Directory *directory;
3331 * clear the view list in all directory set entries
3333 for (i = 0; i < directory_count; i++)
3335 if( !(strcmp(directory_set[i]->directory_name, trash_dir) == 0) )
3337 XtFree ((char *) directory_set[i]->directoryView);
3338 directory_set[i]->numOfViews = 0;
3339 directory_set[i]->directoryView = NULL;
3340 directory_set[i]->viewed = False;
3347 * reconstruct view lists by adding each directory found in the view
3348 * set to the view list for the corresponding directory set entry
3350 for (j = 0; j < view_count; j++)
3352 dialog_data = (DialogData *) view_set[j]->dialog_data;
3353 file_mgr_data = (FileMgrData *) dialog_data->data;
3355 /* loop through all direcories in this view */
3356 for (k = 0; k < file_mgr_data->directory_count; k++)
3358 /* find the directory in the directory set */
3359 directory = FindDirectory(view_set[j]->host_name,
3360 file_mgr_data->directory_set[k]->name);
3362 /* we expect the directory to be found; if not, something is wrong */
3363 if (directory == NULL)
3365 fprintf(stderr, "Warning: %s:%s not found in directory set.\n",
3366 view_set[j]->host_name,
3367 file_mgr_data->directory_set[k]->name);
3371 /* add the directory to the view list */
3372 n = directory->numOfViews;
3373 directory->directoryView = (DirectoryView *)
3374 XtRealloc ((char *) directory->directoryView,
3375 sizeof(DirectoryView) * (n + 1));
3376 directory->directoryView[n].file_mgr_data = file_mgr_data;
3377 directory->directoryView[n].mapped = file_mgr_data->mapped;
3378 directory->numOfViews++;
3379 directory->viewed = True;
3386 * remove all directories that have empty view lists
3389 while (i < directory_count)
3391 if (directory_set[i]->numOfViews > 0 ||
3392 strcmp(directory_set[i]->directory_name, trash_dir) == 0)
3394 /* Keep this directory in the directory set. */
3399 /* Delete the file data and remove from the directory set. */
3401 DPRINTF(("UpdateCachedDirectories: removing %s:%s\n",
3402 directory_set[i]->host_name,
3403 directory_set[i]->directory_name));
3405 FreeDirectory(directory_set[i]);
3407 for (k = i; k < directory_count - 1; k++)
3408 directory_set[k] = directory_set[k + 1];
3414 /* Restart refresh timer, if necessary */
3415 if (timer_suspended && SomeWindowMapped())
3417 XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
3418 timer_suspended = False;
3423 /*====================================================================
3425 * Routines that return directory data
3427 *==================================================================*/
3429 /*--------------------------------------------------------------------
3431 * Return a string that contains file information similar to "ls -l",
3432 * including: permissions, owner, modified time, link (if any).
3433 * Used for "view by attributes"
3436 * -rw-r--r-- dld staff 108314 Jul 26 15:16:36 1993 Directory.c
3438 *------------------------------------------------------------------*/
3442 FileData *file_data )
3446 char time_string[100];
3450 char link_path[MAX_PATH + 5];
3451 static gid_t group_id = (gid_t)-1;
3452 static uid_t user_id = (uid_t)-1;
3453 struct group * group_data;
3454 struct passwd * user_data;
3455 static char group_name[20];
3456 static char user_name[20];
3458 time_t long_modify_time;
3460 char usr_read_priv, usr_write_priv, usr_exec_priv;
3461 char grp_read_priv, grp_write_priv, grp_exec_priv;
3462 char oth_read_priv, oth_write_priv, oth_exec_priv;
3464 /* Generate the long list name. */
3465 long_name = (char *) XtMalloc(sizeof(char) * (MAX_PATH * 3));
3468 /* Initially, assume their is not a soft link */
3469 link_path[0] = '\0';
3471 if (file_data->errnum == 0)
3473 if (file_data->stat.st_gid != group_id)
3475 group_id = file_data->stat.st_gid;
3476 group_data = getgrgid (file_data->stat.st_gid);
3479 strcpy (group_name, group_data->gr_name);
3480 if (strlen (group_name) == 0)
3481 strcpy (group_name, "root");
3484 strcpy (group_name, "root");
3487 if (file_data->stat.st_uid != user_id)
3489 user_id = file_data->stat.st_uid;
3490 user_data = getpwuid (file_data->stat.st_uid);
3491 /* Initially, assume their is not a user name */
3492 user_name[0] = '\0';
3494 strcpy (user_name, user_data->pw_name);
3496 sprintf(user_name,"%ld",(long)user_id);
3501 char error_msg[1024];
3504 /* determine how much space we have for an error message */
3505 long_modify_time = 747616435;
3506 /* just needed to determine the length of a date */
3508 tms = localtime(&long_modify_time);
3509 strftime( time_string, 100,
3510 GetSharedMessage(DIRECTORY_DATE_FORMAT),
3513 time_string = ctime ((time_t *)&long_modify_time);
3514 time_string[strlen(time_string)-1] = 0x0;
3517 msg_len = 10 + 3 + 9 + 1 + 9 + 1 + 9 + 1 + strlen(time_string);
3519 /* generate the error message */
3520 strcpy(error_msg, "(");
3521 strncpy(error_msg + 1, strerror(file_data->errnum), msg_len - 2);
3522 error_msg[msg_len - 1] = '\0';
3523 strcat(error_msg, ")");
3525 sprintf( long_name, "%-28.28s %s %9d %s",
3526 file_data->file_name,
3534 /* Build the permission string */
3535 switch( file_data->stat.st_mode & S_IFMT )
3550 permission = OPTION_OFF;
3554 if (file_data->stat.st_mode & S_IRUSR) usr_read_priv = READ_PRIV;
3555 else usr_read_priv = OPTION_OFF;
3557 if (file_data->stat.st_mode & S_IWUSR) usr_write_priv = WRITE_PRIV;
3558 else usr_write_priv = OPTION_OFF;
3560 if (file_data->stat.st_mode & S_IXUSR) usr_exec_priv = EXEC_PRIV;
3561 else usr_exec_priv = OPTION_OFF;
3564 if (file_data->stat.st_mode & S_IRGRP) grp_read_priv = READ_PRIV;
3565 else grp_read_priv = OPTION_OFF;
3567 if (file_data->stat.st_mode & S_IWGRP) grp_write_priv = WRITE_PRIV;
3568 else grp_write_priv = OPTION_OFF;
3570 if (file_data->stat.st_mode & S_IXGRP) grp_exec_priv = EXEC_PRIV;
3571 else grp_exec_priv = OPTION_OFF;
3574 if (file_data->stat.st_mode & S_IROTH) oth_read_priv = READ_PRIV;
3575 else oth_read_priv = OPTION_OFF;
3577 if (file_data->stat.st_mode & S_IWOTH) oth_write_priv = WRITE_PRIV;
3578 else oth_write_priv = OPTION_OFF;
3580 if (file_data->stat.st_mode & S_IXOTH) oth_exec_priv = EXEC_PRIV;
3581 else oth_exec_priv = OPTION_OFF;
3584 long_modify_time = file_data->stat.st_mtime;
3586 tms = localtime(&long_modify_time);
3587 strftime( time_string, 100,
3588 GetSharedMessage(DIRECTORY_DATE_FORMAT),
3591 time_string = ctime ((time_t *)&long_modify_time);
3592 time_string[strlen(time_string)-1] = 0x0;
3596 /* Fill in the name of where the link goes */
3597 if (file_data->link)
3599 strcpy( link_path, " -> " );
3600 strcpy( link_path + 4, file_data->link );
3604 #define NAME_PRECISION 28
3605 int len = strlen( file_data->file_name );
3606 if( len > NAME_PRECISION )
3609 char name[NAME_PRECISION];
3610 sprintf( name, "%-20.20s (...) ", file_data->file_name );
3612 sprintf( long_name, "%-28.28s %s %9ld %c%c%c%c%c%c%c%c%c%c %-9s %-9s %s",
3615 (long)file_data->stat.st_size,
3617 usr_read_priv, usr_write_priv, usr_exec_priv,
3618 grp_read_priv, grp_write_priv, grp_exec_priv,
3619 oth_read_priv, oth_write_priv, oth_exec_priv,
3620 user_name, group_name,
3625 sprintf( long_name, "%-28.28s %s %9ld %c%c%c%c%c%c%c%c%c%c %-9s %-9s %s",
3626 file_data->file_name,
3628 (long)file_data->stat.st_size,
3630 usr_read_priv, usr_write_priv, usr_exec_priv,
3631 grp_read_priv, grp_write_priv, grp_exec_priv,
3632 oth_read_priv, oth_write_priv, oth_exec_priv,
3633 user_name, group_name,
3645 /*--------------------------------------------------------------------
3647 * See if path has a directory view of it or if any sub-directories
3648 * of it are viewed. The path parameter is of the for /foo/bar
3649 *------------------------------------------------------------------*/
3655 FileMgrData * file_mgr_data;
3656 FileViewData * sub_root;
3658 int len = strlen(path);
3660 for (i = 0; i < directory_count; i++)
3662 /* check if this directory is equal to 'path' or a subdir of 'path' */
3663 if (directory_set[i]->viewed &&
3664 (strcmp (directory_set[i]->path_name, path) == 0 ||
3665 strncmp (directory_set[i]->path_name, path,len) == 0 &&
3666 directory_set[i]->path_name[len] == '/'
3668 directory_set[i]->tt_path_name != NULL &&
3669 (strcmp (directory_set[i]->tt_path_name, path) == 0 ||
3670 strncmp (directory_set[i]->tt_path_name, path,len) == 0 &&
3671 directory_set[i]->tt_path_name[len] == '/')))
3673 /* check the views in the view list */
3674 for (j = 0; j < directory_set[i]->numOfViews; j++)
3676 file_mgr_data = directory_set[i]->directoryView[j].file_mgr_data;
3678 /* find the dir in the directory set for this view */
3679 for (k = 0; k < file_mgr_data->directory_count; k++)
3680 if (strcmp(file_mgr_data->directory_set[k]->name,
3681 directory_set[i]->directory_name) == 0)
3685 if (k == file_mgr_data->directory_count)
3686 continue; /* not found ... something must be wrong! */
3689 * Check if this directory is acutally visible.
3690 * If the directory is in a tree branch that is not currently
3691 * expanded, it is not visible and would not be considered busy.
3694 /* the tree root is always considered busy */
3698 /* a subdir is considered busy if it is visible and at least
3699 * partially expanded */
3700 sub_root = file_mgr_data->directory_set[k]->sub_root;
3701 if (sub_root->displayed &&
3702 (sub_root->ts == tsDirs && sub_root->ndir > 0 ||
3703 sub_root->ts == tsAll &&
3704 sub_root->ndir + sub_root->nfile > 0))
3716 /*--------------------------------------------------------------------
3717 * GetDirectoryLogicalType
3718 * Get logical type for the iconic path.
3719 *------------------------------------------------------------------*/
3722 GetDirectoryLogicalType(
3723 FileMgrData *file_mgr_data,
3728 Directory *directory;
3731 /* 'path' must be a prefix of the current directory */
3733 if (strncmp(file_mgr_data->current_directory, path, len) != 0 ||
3735 file_mgr_data->current_directory[len] != '/' &&
3736 file_mgr_data->current_directory[len] != '\0'))
3738 DPRINTF(("GetDirectoryLogicalType(%s): len %d, cur_dir %s\n",
3739 path, len, file_mgr_data->current_directory));
3743 /* Find the directory set entry. */
3744 directory = FindDirectory(file_mgr_data->host,
3745 file_mgr_data->current_directory);
3746 if ((directory != NULL) &&
3747 (strcmp(file_mgr_data->current_directory,
3748 directory->directory_name) == 0))
3750 /* if we don't have path_logical_types yet, we don't know */
3751 if (directory->path_logical_types == NULL)
3754 /* count the number of components in path */
3755 if (strcmp(path, "/") == 0)
3761 while ((ptr = DtStrchr(ptr, '/')) != NULL)
3771 DPRINTF2(("GetDirectoryLogicalType(%s): n %d, type %s\n",
3772 path, n, directory->path_logical_types[n]));
3774 /* return type form path_logical_types array */
3775 return directory->path_logical_types[n];
3778 /* directory not found in directory_set */
3784 /*====================================================================
3786 * Routines for accessing position information
3788 *==================================================================*/
3790 /*--------------------------------------------------------------------
3791 * GetDirectoryPositionInfo
3792 * Get cached position info
3793 *------------------------------------------------------------------*/
3796 GetDirectoryPositionInfo(
3798 char *directory_name,
3799 PositionInfo **position_info)
3801 Directory *directory;
3803 directory = FindDirectory(host_name, directory_name);
3804 if (directory == NULL)
3807 *position_info = directory->position_info;
3809 return directory->position_count;
3813 /*--------------------------------------------------------------------
3814 * WritePosInfoProcess
3815 * Main routine of the background process that writes the
3816 * postion information file.
3817 *------------------------------------------------------------------*/
3820 WritePosInfoProcess(
3822 Directory *directory,
3823 ActivityStatus activity)
3826 int position_count = directory->position_count;
3827 PositionInfo *position_info = directory->position_info;
3830 Tt_status tt_status;
3832 /* construct the full file name */
3833 fileName = ResolveLocalPathName( directory->host_name,
3834 directory->directory_name, positionFileName,
3835 home_host_name, &tt_status );
3836 /* Don't have to check for tt_status
3837 directory->host_name is home_host_name and ResolveLocalPathName will
3838 always return a good path
3840 DPRINTF(("WritePosInfoProcess: count %d, file %s\n",
3841 position_count, fileName));
3843 /* Remove old files, if no position information for this view */
3844 if (position_count <= 0)
3845 rc = unlink(fileName);
3848 /* open the file for writing */
3849 f = fopen(fileName, "w");
3853 /* Assume read-only directory, if we can't open the file */
3858 chmod(fileName, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
3860 fprintf(f, "%d\n", position_count);
3861 for (i = 0; i < position_count; i++)
3863 fprintf(f, "%s\n%d %d %d\n",
3864 position_info[i].name,
3867 position_info[i].stacking_order);
3875 /* send result back thorugh the pipe */
3876 DPRINTF(("WritePosInfoProcess: done (rc %d)\n", rc));
3877 write(pipe_fd, &rc, sizeof(int));
3883 /*--------------------------------------------------------------------
3884 * WritePosInfoPipeCallback
3885 * Callback routine that reads the return code sent through the
3886 * pipe from the WritePosInfoProcess background process.
3887 *------------------------------------------------------------------*/
3890 WritePosInfoPipeCallback(
3891 XtPointer client_data,
3895 PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
3896 Directory *directory = pipe_data->directory;
3900 /* get return code from the pipe */
3902 PipeRead(*fd, &rc, sizeof(int));
3904 /* close the pipe and cancel the callback */
3906 XtFree( client_data );
3909 /* verify that the directory still exists */
3910 if (DirectoryGone(directory))
3912 ScheduleActivity(NULL);
3916 DPRINTF(("WritePosInfoPipeCallback: rc %d\n", rc));
3918 /* reset the busy flag and schedule new work, if any */
3919 directory->busy[activity_writing_posinfo] = False;
3920 directory->activity = activity_idle;
3921 ScheduleActivity(directory);
3925 /*--------------------------------------------------------------------
3926 * SetDirectoryPositionInfo
3927 * Update cached position info. This routine schedules a
3928 * background process that writes the modified information
3929 * to the position info file.
3930 *------------------------------------------------------------------*/
3933 SetDirectoryPositionInfo(
3935 char *directory_name,
3937 PositionInfo *position_info)
3939 Directory *directory;
3943 /* find the directory */
3944 directory = FindDirectory(host_name, directory_name);
3945 if (directory == NULL)
3948 /* check if anything has changed */
3949 if (directory->position_count == position_count)
3952 for (i = 0; i < position_count && unchanged; i++)
3954 for (j = 0; j < position_count; j++)
3955 if (strcmp(position_info[i].name,
3956 directory->position_info[j].name) == 0)
3961 if (j == position_count ||
3962 position_info[i].x != directory->position_info[j].x ||
3963 position_info[i].y != directory->position_info[j].y ||
3964 position_info[i].stacking_order !=
3965 directory->position_info[j].stacking_order)
3971 /* if nothing changed, don't do anything */
3976 /* free old position info names*/
3977 for (i = 0; i < directory->position_count; i++)
3978 XtFree(directory->position_info[i].name);
3980 /* realloc array, if necessary */
3981 if (directory->position_count != position_count)
3983 directory->position_count = position_count;
3984 directory->position_info =
3985 (PositionInfo *) XtRealloc((char *)directory->position_info,
3986 position_count * sizeof(PositionInfo));
3989 /* replace old position info */
3990 for (i = 0; i < position_count; i++)
3992 directory->position_info[i].name = XtNewString(position_info[i].name);
3993 directory->position_info[i].x = position_info[i].x;
3994 directory->position_info[i].y = position_info[i].y;
3995 directory->position_info[i].stacking_order =
3996 position_info[i].stacking_order;
3999 /* make sure positionFileName is initialized */
4000 if (positionFileName == NULL)
4001 InitializePositionFileName();
4003 /* start background process that writes the position info file */
4004 directory->busy[activity_writing_posinfo] = True;
4005 ScheduleActivity(directory);
4011 /*====================================================================
4014 * These function are periodically called to scan all cached
4015 * directories to see if any have been modified since last read.
4016 * If any have, a background process is scheduled to re-read
4019 *==================================================================*/
4021 /*--------------------------------------------------------------------
4023 * Decide whether to skip an automatic re-read.
4024 * (We don't do re-reads on directories that aren't currently
4025 * being viewed and on the trash directory, if trash is currently
4027 *------------------------------------------------------------------*/
4031 Directory *directory)
4035 /* don't refresh while the directory is being modified */
4036 if (directory->modify_begin > 0)
4039 /* verify that the directory is still being viewed */
4040 if (!directory->viewed)
4043 for (i = 0; i < directory->numOfViews; i++)
4044 if (directory->directoryView[i].mapped)
4046 if (i == directory->numOfViews)
4049 /* if trash is being emptied and this is the trash dir, skip it */
4050 if (removingTrash && strcmp(directory->directory_name, trash_dir) == 0)
4057 /*--------------------------------------------------------------------
4059 * Main routine for the background process that checks directory
4060 * timestamps and the status of links.
4061 *------------------------------------------------------------------*/
4066 Directory *directory,
4067 ActivityStatus activity)
4069 struct stat stat_buf;
4071 Boolean link_changed;
4073 FileData *file_data;
4074 char full_name[MAX_PATH];
4083 * Do a stat on the directory to get its last-modified time.
4084 * Also check if we still have read and execute/search permisssion.
4087 * It is important to get the timstamp in exactly the same way that the
4088 * ReadDirectoryProcess does it; otherwise, we might get into a loop,
4089 * where TimerEventProcess detects that the directory has changed
4090 * and triggers ReadDirectoryProcess, but ReadDirectoryProcess won't
4091 * be able to get a new timestamp and update the directory structure,
4092 * so the next time TimerEventProcess runs it will trigger another
4093 * ReadDirectoryProcess, and so on ...
4095 if (CheckAccess(directory->path_name, R_OK | X_OK) != 0 ||
4096 stat(directory->path_name, &stat_buf) != 0)
4098 /* stat or access failed */
4104 /* stat succeeded and the directory is still readable */
4106 modify_time = stat_buf.st_mtime;
4110 * If requested, also check if any links broken.
4112 * Again: it is important that we determine the kind of link
4113 * (valid, recursive, or broken) in exactly the same way that
4114 * ReadDirectoryProcess does it (see comment above)!
4116 link_changed = False;
4117 if (rc == 0 && activity == activity_checking_links)
4119 strcpy(full_name, directory->path_name);
4120 namep = full_name + strlen(full_name);
4121 if (namep[-1] != '/')
4124 /* check all links */
4125 for (file_data = directory->file_data;
4126 file_data && !link_changed;
4127 file_data = file_data->next)
4129 /* Only worry about links */
4130 if (file_data->link == NULL)
4133 /* Check if the file is still a symbolic link */
4134 strcpy(namep, file_data->file_name);
4135 link_rc = lstat(full_name, &stat_buf);
4136 if (link_rc != 0 || (stat_buf.st_mode & S_IFMT) != S_IFLNK)
4138 /* no longer a link */
4139 link_changed = True;
4143 /* Check what kind of link this was the last time we looked:
4144 * a normal link (1), a recursive link (2), or an otherwise
4145 * broken link (3) */
4146 if (strcmp(file_data->logical_type, LT_BROKEN_LINK) == 0)
4148 else if (strcmp(file_data->logical_type, LT_RECURSIVE_LINK) == 0)
4153 /* Check what kind of link it is now */
4154 if (_DtFollowLink(full_name) == NULL)
4155 cur_link_kind = 2; /* recursive link */
4156 else if (stat(full_name, &stat_buf) != 0)
4157 cur_link_kind = 3; /* broken link */
4159 cur_link_kind = 1; /* a valid link */
4161 /* now we can tell if the link has changed */
4162 if (prev_link_kind != cur_link_kind)
4163 link_changed = True;
4167 /* send result back through the pipe */
4168 write(pipe_fd, &rc, sizeof(int));
4169 write(pipe_fd, &modify_time, sizeof(long));
4170 write(pipe_fd, &link_changed, sizeof(Boolean));
4175 /*--------------------------------------------------------------------
4177 * Mark sticky background process as idle. If there are too
4178 * may idle sticky procs, cause some of them to exit.
4179 *------------------------------------------------------------------*/
4183 ActivityStatus activity,
4184 StickyProcDesc *sticky_proc,
4187 StickyProcDesc *p, **lp;
4190 /* mark the process as idle */
4191 sticky_proc->idle = True;
4193 /* if there are too many idle procs, make some of them go away */
4195 lp = &ActivityTable[activity].sticky_procs;
4196 for (p = *lp; p; p = *lp)
4200 else if (n < max_procs)
4207 DPRINTF2(("StickyProcIdle: end sticky proc %ld\n", (long)p->child));
4208 PipeWriteString(p->pipe_m2s_fd, NULL);
4209 close(p->pipe_s2m_fd);
4210 close(p->pipe_m2s_fd);
4218 /*--------------------------------------------------------------------
4220 * Callback routine that reads information sent through the
4221 * pipe from the TimerEventProcess background process.
4222 *------------------------------------------------------------------*/
4226 XtPointer client_data,
4230 PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
4231 Directory *directory = pipe_data->directory;
4234 Boolean link_changed;
4237 /* get return code from the pipe */
4239 PipeRead(*fd, &rc, sizeof(int));
4240 PipeRead(*fd, &modify_time, sizeof(long));
4241 PipeRead(*fd, &link_changed, sizeof(Boolean));
4243 /* close the pipe and cancel the callback */
4244 if (pipe_data->sticky_proc)
4245 StickyProcIdle(pipe_data->activity, pipe_data->sticky_proc,
4246 maxRereadProcsPerTick);
4249 XtFree( client_data );
4252 /* verify that the directory still exists */
4253 if (DirectoryGone(directory))
4255 ScheduleActivity(NULL);
4260 "TimerPipeCallback: rc %d (was %d), time %ld (was %ld), link change %d\n",
4261 rc, directory->errnum, modify_time, (long)directory->modify_time, link_changed));
4263 /* reset the busy flag and schedule new work, if any */
4264 directory->busy[directory->activity] = False;
4265 directory->activity = activity_idle;
4266 ScheduleActivity(directory);
4268 /* if directory-read already in progress, nothing more to do here */
4269 if (directory->busy[activity_reading] ||
4270 directory->busy[activity_update_all])
4273 /* skip this directory if it is no longer being viewed */
4274 if (SkipRefresh(directory))
4277 /* if the directory was modified or links changed, re-read it */
4282 DPRINTF(("TimerPipeCallback: %s link changed\n",
4283 directory->directory_name));
4284 ReadDirectoryFiles(NULL, directory);
4286 else if (modify_time != directory->modify_time || directory->errnum != 0)
4288 DPRINTF(("TimerPipeCallback: %s modified\n",
4289 directory->directory_name));
4290 directory->busy[activity_update_all] = True;
4291 ScheduleActivity(directory);
4296 if (directory->errnum == 0)
4298 directory->errnum = rc;
4299 directory->errmsg_needed = True;
4300 ReadDirectoryFiles(NULL, directory);
4306 /*--------------------------------------------------------------------
4307 * CheckDesktopProcess
4308 * Main routine for the background process that checks each desktop
4309 * objects to see if the file that it refers to has disappeared
4310 * or has changed type.
4311 *------------------------------------------------------------------*/
4314 CheckDesktopProcess(
4316 Directory *directory,
4317 ActivityStatus activity)
4320 DesktopRec *desktopWindow;
4321 FileViewData *file_view_data;
4323 Tt_status tt_status;
4324 struct stat stat_buf;
4326 FileData2 file_data2;
4327 FileData *old_data, *new_data;
4329 for (i = 0; i < desktop_data->numIconsUsed; i++)
4331 desktopWindow = desktop_data->desktopWindows[i];
4332 file_view_data = desktopWindow->file_view_data;
4334 full_path = ResolveLocalPathName( desktopWindow->host,
4335 desktopWindow->dir_linked_to,
4336 desktopWindow->file_name,
4337 home_host_name, &tt_status);
4339 /* Check if the file still exists */
4341 if (lstat(full_path, &stat_buf) < 0)
4343 /* the real file no longer exists */
4345 "CheckDesktopProcess: sending PIPEMSG_DESKTOP_REMOVED for %s\n",
4347 pipe_msg = PIPEMSG_DESKTOP_REMOVED;
4348 write(pipe_fd, &pipe_msg, sizeof(short));
4349 PipeWriteString(pipe_fd, desktopWindow->host);
4350 PipeWriteString(pipe_fd, desktopWindow->dir_linked_to);
4351 PipeWriteString(pipe_fd, desktopWindow->file_name);
4356 /* See if the type has changed */
4357 old_data = file_view_data->file_data;
4359 if(directory->directoryView && directory->directoryView->file_mgr_data)
4360 IsToolBox = directory->directoryView->file_mgr_data->toolbox;
4364 ReadFileData2(&file_data2, full_path, NULL,IsToolBox);
4365 new_data = FileData2toFileData(&file_data2, &n);
4367 if (new_data->physical_type != old_data->physical_type ||
4368 strcmp(new_data->logical_type, old_data->logical_type) != 0)
4370 /* the type has changed */
4372 "CheckDesktopProcess: sending PIPEMSG_DESKTOP_CHANGED for %s\n",
4375 " old type %d %s, new type %d %s\n",
4376 old_data->physical_type, old_data->logical_type,
4377 new_data->physical_type, new_data->logical_type));
4379 pipe_msg = PIPEMSG_DESKTOP_CHANGED;
4380 write(pipe_fd, &pipe_msg, sizeof(short));
4381 PipeWriteString(pipe_fd, desktopWindow->host);
4382 PipeWriteString(pipe_fd, desktopWindow->dir_linked_to);
4383 PipeWriteString(pipe_fd, desktopWindow->file_name);
4385 PipeWriteFileData(pipe_fd, new_data);
4388 FreeFileData(new_data, True);
4394 /* send a 'done' msg through the pipe */
4395 DPRINTF2(("CheckDesktopProcess: sending DONE\n"));
4396 pipe_msg = PIPEMSG_DONE;
4397 write(pipe_fd, &pipe_msg, sizeof(short));
4402 /*--------------------------------------------------------------------
4403 * CheckDesktopPipeCallback
4404 * Callback routine that reads information sent through the
4405 * pipe from the CheckDesktopProcess background process.
4406 *------------------------------------------------------------------*/
4409 CheckDesktopPipeCallback(
4410 XtPointer client_data,
4414 PipeCallbackData *pipe_data = (PipeCallbackData *)client_data;
4415 Directory *directory = pipe_data->directory;
4417 char *host, *dir_linked_to, *file_name;
4418 FileData *new_data, *old_data;
4420 DesktopRec *desktopWindow;
4423 /* read the next msg from the pipe */
4425 n = PipeRead(*fd, &msg, sizeof(short));
4427 if (msg == PIPEMSG_DESKTOP_REMOVED ||
4428 msg == PIPEMSG_DESKTOP_CHANGED)
4430 /* get information from pipe */
4431 host = PipeReadString(*fd);
4432 dir_linked_to = PipeReadString(*fd);
4433 file_name = PipeReadString(*fd);
4434 if (msg == PIPEMSG_DESKTOP_CHANGED)
4435 new_data = PipeReadFileData(*fd);
4440 "CheckDesktopPipeCallback: msg %d: host %s, dir %s, name %s\n",
4441 msg, host, dir_linked_to, file_name));
4444 /* find the desktop object */
4446 for (i = 0; i < desktop_data->numIconsUsed; i++)
4448 desktopWindow = desktop_data->desktopWindows[i];
4450 if (strcmp(host, desktopWindow->host) == 0 &&
4451 strcmp(dir_linked_to, desktopWindow->dir_linked_to) == 0 &&
4452 strcmp(file_name, desktopWindow->file_name) == 0)
4459 /* remove or update the desktop object, if found */
4464 else if (msg == PIPEMSG_DESKTOP_REMOVED)
4466 /* remove the desktop object */
4467 DesktopObjectRemoved(desktopWindow);
4469 else /* msg == PIPEMSG_DESKTOP_CHANGED */
4471 /* replace file data */
4472 old_data = desktopWindow->file_view_data->file_data;
4473 FreeFileData(old_data, False);
4474 memcpy(old_data, new_data, sizeof(FileData));
4475 XtFree((char *)new_data);
4478 /* update the desktop object */
4479 DesktopObjectChanged(desktopWindow);
4484 XtFree(dir_linked_to);
4487 FreeFileData(new_data, True);
4490 else if (msg == PIPEMSG_DONE)
4492 /* close the pipe and cancel the callback */
4494 XtFree( client_data );
4497 /* reset the busy flag and schedule new work, if any */
4498 directory->busy[directory->activity] = False;
4499 directory->activity = activity_idle;
4500 ScheduleActivity(directory);
4505 /*--------------------------------------------------------------------
4508 * Arrange for a CheckDesktopProcess background process to be
4509 * started (checks each desktop objects to see if the file that
4510 * it refers to has disappeared or has changed type).
4512 *------------------------------------------------------------------*/
4515 CheckDesktop( void )
4517 dummy_directory->busy[activity_checking_desktop] = True;
4518 ScheduleActivity(dummy_directory);
4522 /*--------------------------------------------------------------------
4524 * This routine is called periodically. It schedules a
4525 * TimerEventProcess background process to be started for every
4526 * directory in the cache.
4527 *------------------------------------------------------------------*/
4529 /* comparison routine for qsort */
4535 return directory_set[*p1]->last_check - directory_set[*p2]->last_check;
4541 XtPointer client_data,
4544 static int *check_list = NULL;
4545 static int check_alloc = 0;
4549 DPRINTF2(("Directory::TimerEvent\n"));
4554 * Don't change any directories while a drag is active.
4556 * Reason: drag callbacks are called with a pointer to a FileViewData
4557 * structure; if a directory is reread while a drag is active,
4558 * the pointer would become invalid, causing unpredictable behavior.
4560 * Schedule the next TimerEvent in 1/2 second, so that check will
4561 * be done soon after the drag is finished.
4563 XtAppAddTimeOut (app_context, 500, TimerEvent, NULL);
4567 /* update tick count */
4570 /* determine if we should also check for broken links this time */
4571 if (checkBrokenLink > 0 &&
4572 tick_count >= lastLinkCheckTick + ticksBetweenLinkChecks)
4574 /* set link_check_needed flag on all directores */
4575 for (i = 0; i < directory_count; i++)
4577 /* skip this directory if no view is mapped */
4578 if (SkipRefresh(directory_set[i]))
4581 /* if a check is already in progress, don't start another one */
4582 if (directory_set[i]->busy[activity_checking_links])
4585 /* arrange for background process to be scheduled */
4586 directory_set[i]->link_check_needed = True;
4589 lastLinkCheckTick = tick_count;
4592 /* make sure check_list array is big enough */
4593 if (directory_count > check_alloc)
4595 check_alloc = directory_count + 5;
4597 (int *)XtRealloc((char *)check_list, check_alloc*sizeof(int));
4600 /* get a list of all directories that need to be checked */
4602 for (i = 0; i < directory_count; i++)
4604 /* skip this directory if no view is mapped */
4605 if (SkipRefresh(directory_set[i]))
4608 /* if a stat is already in progress, don't start another one */
4609 if (directory_set[i]->busy[activity_checking_dir] ||
4610 directory_set[i]->busy[activity_checking_links])
4613 /* add this directory to the check list */
4614 check_list[n++] = i;
4618 * Next we want to schedule a background process to be started
4619 * for each directory in the check_list. However, the variable
4620 * maxRereadProcsPerTick puts a limit on the number of such
4621 * background processes started per clock tick (i.e., per call
4622 * to this routine). Hence we sort check_list by last_check
4623 * (records the tick count when a directory was last read or
4624 * checked) and schedule backround processes on those dirs that
4625 * haven't been checked in the longest time.
4627 qsort(check_list, n, sizeof(int), (int (*)())CheckListCmp);
4629 /* arrange for background process to be started */
4630 for (j = 0; j < n && j < maxRereadProcsPerTick; j++)
4633 if (directory_set[i]->link_check_needed)
4635 directory_set[i]->link_check_needed = False;
4636 directory_set[i]->busy[activity_checking_links] = True;
4639 directory_set[i]->busy[activity_checking_dir] = True;
4640 ScheduleActivity(directory_set[i]);
4641 directory_set[i]->last_check = tick_count;
4644 /* Reset the timeout for the next interval. */
4645 if (SomeWindowMapped())
4646 XtAppAddTimeOut(app_context, tickTime * 1000, TimerEvent, NULL);
4648 timer_suspended = True;
4652 /*--------------------------------------------------------------------
4653 * TimerEventBrokenLinks
4654 * This routine is called periodically. It checks whether any
4655 * desktop object is broken (i.e., the object it refers to no
4657 *------------------------------------------------------------------*/
4660 TimerEventBrokenLinks(
4661 XtPointer client_data,
4666 DPRINTF2(("Directory::TimerEventBrokenLinks\n"));
4670 /* go check the desktop objects */
4671 if (desktop_data->numIconsUsed > 0)
4675 /* Reset the timeout for the next interval. */
4676 if (desktop_data->numIconsUsed > 0)
4678 checkBrokenLinkTimerId = XtAppAddTimeOut( app_context,
4679 checkBrokenLink * 1000,
4680 TimerEventBrokenLinks,
4685 checkBrokenLinkTimerId = NULL;
4690 /*====================================================================
4692 * Background process scheduler
4694 * The routines below schedule background activity, making sure
4695 * that there aren't too many processes running at the same time.
4697 *==================================================================*/
4699 /*--------------------------------------------------------------------
4700 * ScheduleDirectoryActivity
4701 * If there is any work to do for a directory, and if there is
4702 * no backgroud process currently running for that directory,
4703 * then fork a process to do the work.
4704 *------------------------------------------------------------------*/
4707 ScheduleDirectoryActivity(
4708 Directory *directory)
4710 static char *pname = "ScheduleActivity";
4711 ActivityStatus activity;
4712 PipeCallbackData *pipe_data;
4713 Boolean all_views_active;
4714 Boolean this_view_active;
4716 int n_active, n_checking;
4717 int save_last_check;
4718 FileMgrData *file_mgr_data;
4721 int pipe_s2m_fd[2]; /* for msgs from backgroundnd proc (slave to master) */
4722 int pipe_m2s_fd[2]; /* for msgs to backgroundnd proc (master to slave) */
4727 /* If already active, don't start anything new. */
4728 if (directory->activity != activity_idle)
4731 /* Decide what to do next */
4732 for (activity = 0; activity < activity_idle; activity++)
4733 if (directory->busy[activity])
4736 /* If nothing to do, return */
4737 if (activity == activity_idle)
4740 DPRINTF2(("ScheduleActivity: activity %d, busy %c%c%c%c%c%c%c, dir %s\n",
4741 directory->activity,
4742 directory->busy[activity_writing_posinfo]? 'W': '-',
4743 directory->busy[activity_reading]? 'R': '-',
4744 directory->busy[activity_update_all]? 'A': '-',
4745 directory->busy[activity_update_some]? 'U': '-',
4746 directory->busy[activity_checking_links]? 'B': '-',
4747 directory->busy[activity_checking_desktop]? 'D': '-',
4748 directory->busy[activity_checking_dir]? 'C': '-',
4749 directory->directory_name));
4751 /* Don't start more than a certain number of background processed */
4754 for (j = 0; j < directory_count; j++)
4756 if (directory_set[j]->activity != activity_idle)
4758 if (directory_set[j]->activity == activity_checking_links ||
4759 directory_set[j]->activity == activity_checking_dir)
4762 if (dummy_directory->activity != activity_idle)
4767 if (n_active >= maxDirectoryProcesses ||
4768 n_checking >= maxRereadProcesses)
4770 DPRINTF2(("ScheduleActivity: too many processes\n"));
4775 * We don't want to start more than one background process per view.
4776 * In tree mode one view may show more than one directory.
4777 * Hence we go through the view list for this directory and for each
4778 * view, we check if the same view appears on the view list of some
4779 * other directory that currently has active background activity.
4780 * If all vies on this directory have other activity, then we won't
4781 * start anything new.
4783 if (directory->numOfViews > 0)
4785 all_views_active = True;
4786 for (i = 0; i < directory->numOfViews; i++)
4788 /* get file_mgr_data for this view */
4789 file_mgr_data = directory->directoryView[i].file_mgr_data;
4791 /* see if the same view appears in the view list of a non-idle dir */
4792 this_view_active = False;
4793 for (j = 0; j < directory_count && !this_view_active; j++)
4795 /* we are only interested in directories that are not idle */
4796 if (directory_set[j]->activity == activity_idle)
4799 /* see if the view appears in the view list */
4800 for (k = 0; k < directory_set[j]->numOfViews; k++)
4802 if (directory_set[j]->directoryView[k].file_mgr_data ==
4805 this_view_active = True;
4811 if (!this_view_active)
4813 all_views_active = False;
4818 if (all_views_active)
4820 DPRINTF2(("ScheduleActivity: all views busy\n"));
4825 /* now we are ready to start the next activity */
4826 directory->activity = activity;
4827 if (activity == activity_reading ||
4828 activity == activity_update_all ||
4829 activity == activity_checking_dir ||
4830 activity == activity_checking_links)
4832 save_last_check = directory->last_check;
4833 directory->last_check = tick_count;
4837 * Special optimization for periodic background processes
4838 * (currently only used for activity_checking_dir):
4839 * Since this is done frequently, we don't want to fork new process each
4840 * time. Hence, instead of exiting when it's done, the background process
4841 * is "sticky", i.e., it will stay around waiting for a message on stdin,
4842 * so it can be re-used the next time around. A linked list of sticky
4843 * procs that are currently active is maintained in the ActivityTable.
4845 sticky = ActivityTable[activity].sticky;
4848 /* see if we can find an idle sticky proc that can do the work */
4849 for (p = ActivityTable[activity].sticky_procs; p; p = p->next)
4858 /* We found an idle sticky proc that can be used */
4859 DPRINTF2(("ScheduleActivity: use sticky proc %ld\n", (long)p->child));
4861 /* Send the directory name to the sticky proc */
4863 if (PipeWriteString(p->pipe_m2s_fd, directory->path_name) < 0) {
4866 /* the pipe is broken, remove the old proc then start a new one */
4867 for (d = ActivityTable[activity].sticky_procs; d && p; d = d->next) {
4870 /* the proc listed 1st is dead, remove it */
4871 ActivityTable[activity].sticky_procs = p->next;
4875 else if (d->next == p)
4877 /* the process "p" is dead, remove it */
4886 pipe_s2m_fd[0] = p->pipe_s2m_fd;
4895 /* Need to fork a new background process */
4897 /* create a pipe for reading data from the background proc */
4900 /* creating a new sticky proc? */
4903 /* also need a pipe for sending msgs to the sticky proc */
4906 /* add entry to the list of sticky procs */
4907 p = (StickyProcDesc *) XtMalloc(sizeof(StickyProcDesc));
4908 p->next = ActivityTable[activity].sticky_procs;
4909 ActivityTable[activity].sticky_procs = p;
4911 p->pipe_s2m_fd = pipe_s2m_fd[0];
4912 p->pipe_m2s_fd = pipe_m2s_fd[1];
4916 /* fork a background process */
4921 DBGFORK(("%s: fork failed for activity %d: %s\n",
4922 pname, activity, strerror(errno)));
4925 "%s: fork failed, ppid %d, pid %d, activity %d: error %d=%s\n",
4926 pname, getppid(), getpid(), activity, errno, strerror(errno));
4928 directory->activity = activity_idle;
4929 directory->last_check = save_last_check;
4931 /* close unused pipe connections */
4932 close(pipe_s2m_fd[0]); /* child won't read from this pipe */
4933 close(pipe_s2m_fd[1]); /* parent won't write to this pipe */
4936 close(pipe_m2s_fd[1]); /* child won't write to this pipe */
4937 close(pipe_m2s_fd[0]); /* parent won't read from this pipe */
4949 DBGFORK(("%s: child activity %d, s2m %d, m2s %d\n",
4950 pname, activity, pipe_s2m_fd[1], pipe_m2s_fd[0]));
4952 /* close unused pipe connections */
4953 close(pipe_s2m_fd[0]); /* child won't read from this pipe */
4955 close(pipe_m2s_fd[1]); /* child won't write to this pipe */
4957 /* run main routine for this activity from ActivityTable */
4960 rc = (*ActivityTable[activity].main)(pipe_s2m_fd[1],
4961 directory, activity);
4962 if (!sticky || rc != 0)
4965 /* wait for a message in the pipe */
4966 s = PipeReadString(pipe_m2s_fd[0]);
4970 XtFree(directory->path_name);
4971 directory->path_name = s;
4973 DPRINTF2(("StickyActivity: activity %d, dir %s\n", activity, s));
4976 /* close pipes and end this process */
4977 close(pipe_s2m_fd[1]);
4979 close(pipe_m2s_fd[0]);
4981 DBGFORK(("%s: completed activity %d, (rc %d)\n",pname, activity, rc));
4986 DBGFORK(("%s: forked child<%d> for activity %d, s2m %d, m2s %d\n",
4987 pname, pid, activity, pipe_s2m_fd[0], pipe_m2s_fd[1]));
4989 /* parent process */
4994 * If a directory read or update was started:
4995 * clear the modifile_list, now that the
4996 * background process has it's own copy.
4998 if (activity == activity_reading ||
4999 activity == activity_update_all ||
5000 activity == activity_update_some)
5002 for (i = 0; i < directory->modified_count; i++)
5003 XtFree(directory->modified_list[i]);
5004 XtFree((char *)directory->modified_list);
5006 directory->modified_count = 0;
5007 directory->modified_list = NULL;
5010 /* close unused pipe connections */
5011 close(pipe_s2m_fd[1]); /* parent won't write to this pipe */
5013 close(pipe_m2s_fd[0]); /* parent won't read from this pipe */
5017 /* set up callback to get the pipe data */
5018 DPRINTF2(("ScheduleActivity: setting up pipe callback\n"));
5019 pipe_data = (PipeCallbackData *)XtMalloc(sizeof(PipeCallbackData));
5020 pipe_data->directory = directory;
5021 pipe_data->child = pid;
5022 pipe_data->sticky_proc = p;
5023 pipe_data->activity = activity;
5025 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
5026 pipe_s2m_fd[0], (XtPointer)XtInputReadMask,
5027 ActivityTable[activity].callback, (XtPointer)pipe_data);
5031 /*--------------------------------------------------------------------
5033 * See if any new background work should be started.
5034 *------------------------------------------------------------------*/
5038 Directory *directory)
5042 /* first try to schedule new activity for this directory */
5043 if (directory != NULL)
5044 ScheduleDirectoryActivity(directory);
5046 /* see if there is anything else we can schedule now */
5047 if (directory == NULL || directory->activity == activity_idle)
5049 for (i = 0; i < directory_count; i++)
5050 if (directory_set[i] != directory)
5051 ScheduleDirectoryActivity(directory_set[i]);
5053 ScheduleDirectoryActivity(dummy_directory);
5058 FileMgrData *file_mgr_data)
5060 DirectorySet *directory_data;
5061 FileViewData *file_view_data;
5064 directory_data = file_mgr_data->directory_set[0];
5066 for (j = 0; j < directory_data->file_count; j++)
5068 file_view_data = directory_data->file_view_data[j];
5070 if (file_view_data->filtered != True &&
5071 strcmp(file_mgr_data->desktop_file,
5072 file_view_data->file_data->file_name) == 0)
5074 SelectFile (file_mgr_data, file_view_data);
5078 ActivateSingleSelect(file_mgr_data->file_mgr_rec,
5079 file_mgr_data->selection_list[0]->file_data->logical_type);
5080 PositionFileView(file_view_data, file_mgr_data);