1 /* $TOG: Trash.c /main/12 1999/12/09 13:07:25 mgreess $ */
2 /************************************<+>*************************************
3 ****************************************************************************
7 * COMPONENT_NAME: Desktop File Manager (dtfile)
9 * Description: Source file for the trash code.
11 * FUNCTIONS: AddToDeleteList
12 * CheckDeletePermission
13 * CheckDeletePermissionRecur
29 * MatchesSacredDirectory
53 * TrashRemoveNoConfirmHandler
63 * (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
64 * (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
65 * (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
66 * (c) Copyright 1993, 1994, 1995 Novell, Inc.
68 ****************************************************************************
69 ************************************<+>*************************************/
71 #include <EUSCompat.h>
73 #include <sys/types.h>
84 #include <Xm/RowColumn.h>
85 #include <Xm/CascadeB.h>
89 #include <Xm/PushBG.h>
90 #include <Xm/SeparatoG.h>
91 #include <Xm/MessageB.h>
92 #include <Xm/MwmUtil.h>
94 #include <X11/Shell.h>
95 #include <X11/Xatom.h>
96 #include <Xm/Protocols.h>
98 #include <Dt/Action.h>
100 #include <Dt/FileM.h>
101 #include <Dt/DtNlUtils.h>
102 #include <Dt/Connect.h>
103 #include <Dt/Indicator.h>
104 #include <Dt/EnvControlP.h>
111 #include "SharedProcs.h"
112 #include "MultiView.h"
117 #include "SharedMsgs.h"
119 #define AdditionalHeader (GETMESSAGE(27,98, "(Plus %d additional object(s))"))
121 /* Trash file errors */
122 #define NO_FILE_ERROR 0
123 #define BAD_FILE_ERROR -1
124 #define VERIFY_DIR -2
125 #define VERIFY_FILE -3
126 #define BAD_TRASH_DIRECTORY -4
127 #define BAD_TRASH_FILE -5
128 #define NO_TRASH_FILE -6
131 #define BAD_FILE_SACRED -9
133 /* types of messages sent through the pipe */
134 #define PIPEMSG_FILEOP_ERROR 1
135 #define PIPEMSG_OTHER_ERROR 3
136 #define PIPEMSG_DONE 7
137 #define PIPEMSG_TARGET_TIME 5
138 #define PIPEMSG_FILE_MODIFIED 6
141 * Structure describing each file in the trash can.
142 * Includes the external host:/name format, the original internal
143 * /nfs/host/name format, and the internal /nfs/host/new_name describing
144 * the temporary location of the file. The `problem' flag is used to
145 * indicate if a problem occurred when the file was physically being
157 /* callback data MoveToTrash */
168 Tt_message *msg_list ;
170 #endif /* SUN_PERF */
177 Tt_message *msg_list ;
180 #endif /* SUN_PERF */
182 /* callback data RestoreFromTrash */
192 } RestoreFromTrashCBData;
194 /* callback data EmptyTrash */
203 DeleteList *del_list;
212 DialogData *trashDialogData; /* fm data associated with trash dialog */
213 FileMgrData *trashFileMgrData = NULL;
215 Boolean removingTrash = False;
217 DialogData * primaryTrashHelpDialog = NULL;
218 DialogData ** secondaryTrashHelpDialogList = NULL;
219 int secondaryTrashHelpDialogCount = 0;
220 /* 'defines' for trash files */
221 static char * TRASH_DIR = ".dt/Trash";
222 static char * TRASH_INFO_FILE = ".dt/Trash/.trashinfo";
223 static char * NEW_TRASH_INFO_FILE = ".dt/.tmptrashinfo";
225 static char * RM = "/bin/rm";
226 static char * RM_ARGS = "-rf";
228 static Widget * selectAllBtn = NULL;
229 static Widget * restoreBtn = NULL;
230 static Widget * removeBtn = NULL;
232 static int trashMenuItemCount = 22; /* trash menu items */
233 static MenuDesc * trashMenu = NULL;
235 static TrashEntry * trashCan = NULL;
236 static int trashListSize = 0;
237 static int numTrashItems = 0;
239 static Boolean trashDialogPosted;
241 static char * TrashInfoFileName = NULL;
242 static char * NewTrashInfoFileName = NULL;
244 static char ** sacred_dir_list = NULL; /* list of directories in trash path */
245 static int sacred_dir_list_size = 0;
246 static int sacred_dir_count = 0;
248 static Boolean verifyPromptsEnabled; /* do we prompt the user? */
250 static Tt_message global;
252 static Tt_message *global_msg_list ;
253 static int global_msg_cnt = 0 ;
254 #endif /* SUN_PERF */
255 static Boolean TrashInitialized = False;
257 /******** Static Function Declarations ********/
259 static String CreateTrashFilename(
262 static void MessageToFileList(
267 static void MessagesToFileList(
268 Tt_message *msg_list,
272 #endif /* SUN_PERF */
273 static int WriteEntry(
277 static Boolean MatchesSacredDirectory(
279 static void VerifyCleanup(
281 Boolean completeDelete) ;
282 static void VerifyCancel(
284 XtPointer client_data,
285 XtPointer call_data) ;
286 static void VerifyOk(
288 XtPointer client_data,
289 XtPointer call_data) ;
290 static Boolean WriteTrashEntries( void ) ;
291 static void Select_All(
293 XtPointer client_data,
294 XtPointer call_data) ;
295 static void Unselect_All(
297 XtPointer client_data,
298 XtPointer call_data) ;
301 XtPointer client_data,
302 XtPointer call_data) ;
303 static String GetBasePath(
305 static Boolean ReadTrashList( void ) ;
306 static void RemoveOkCB(
308 XtPointer client_data,
309 XtPointer call_data ) ;
310 static void RemoveCancelCB(
312 XtPointer client_data,
313 XtPointer call_data ) ;
314 static void ConfirmOk(
316 XtPointer client_data,
317 XtPointer call_data ) ;
318 static void ConfirmCancel(
320 XtPointer client_data,
321 XtPointer call_data ) ;
324 XtPointer clientData,
325 XtPointer callData) ;
326 static void AddToDeleteList(
327 DeleteList *deleteList,
330 static void MoveToTrash(
333 Boolean do_verify_checks,
335 static void RestoreFromTrash(
341 Boolean CheckedAlready) ;
342 static void EmptyTrash(
343 DeleteList *del_list,
347 static int CheckDeletePermissionRecur(
349 static int FileSysType(int dev);
350 static void RestoreVerifyOk(
352 XtPointer client_data,
353 XtPointer call_data ) ;
354 static void RestoreVerifyCancel(
356 XtPointer client_data,
357 XtPointer call_data ) ;
358 static int RestoreObject(
361 register char *source,
362 register char *target,
364 void (*errorHandler)(),
365 Boolean checkForBusyDir,
367 Boolean CheckedAlready);
368 static void CreateRestoreDialog(
372 /******** End Static Function Declarations ********/
375 TrashIsInitialized( void )
377 return( TrashInitialized );
381 InitializeTrash( Boolean enableVerifyPrompt )
384 struct stat statInfo;
386 /* Build the 'TRASH' directory */
387 trash_dir = XtMalloc(strlen(users_home_dir) +
390 sprintf(trash_dir, "%s%s", users_home_dir, TRASH_DIR);
391 if (stat(trash_dir, &statInfo) < 0)
392 mkdir(trash_dir, S_IRUSR | S_IWUSR | S_IXUSR |
393 S_IRGRP | S_IWGRP | S_IXGRP |
394 S_IROTH | S_IWOTH | S_IXOTH);
396 /* build path to .trashinfo file */
397 TrashInfoFileName = XtMalloc(strlen(users_home_dir) + strlen(TRASH_INFO_FILE) + 1);
398 sprintf(TrashInfoFileName, "%s%s", users_home_dir, TRASH_INFO_FILE);
401 NewTrashInfoFileName = XtMalloc(strlen(users_home_dir) +
402 strlen(NEW_TRASH_INFO_FILE)
404 sprintf(NewTrashInfoFileName, "%s%s", users_home_dir, NEW_TRASH_INFO_FILE);
406 /* Keep track of whether to prompt for user verification */
407 verifyPromptsEnabled = enableVerifyPrompt;
410 * Create an array of paths and filenames which we will not allow the
411 * user to delete, because deleting any of them will cause trash to
414 sacred_dir_list_size = 5;
415 sacred_dir_list = (char **)XtMalloc(sizeof(char *) * sacred_dir_list_size);
417 sacred_dir_list[sacred_dir_count++] = XtNewString("/");
418 sacred_dir_list[sacred_dir_count++] = XtNewString(TrashInfoFileName);
420 ptr = TrashInfoFileName + 1;
421 while(ptr = DtStrchr(ptr, '/'))
423 /* All parent components of the user's home dir cannot be deleted */
425 if (sacred_dir_count >= sacred_dir_list_size)
428 sacred_dir_list_size += 5;
429 sacred_dir_list = (char **)XtRealloc((char *)sacred_dir_list,
430 sizeof(char *) * sacred_dir_list_size);
432 sacred_dir_list[sacred_dir_count++] = XtNewString(TrashInfoFileName);
437 /* load and verify existence for files previously left in the trash can */
438 TrashInitialized = ReadTrashList( );
439 return( TrashInitialized );
442 /************************************************************************
445 * This function must be called before any other interatctions with the
446 * trash can. It creates the trash dialog, opens the trash file, and
447 * loads information about any files previously left in the trash can.
449 * This function is called from main().
451 ************************************************************************/
459 DialogData * dialog_data;
460 FileMgrData * file_mgr_data;
462 if( ! TrashInitialized )
465 /* Create the trash dialog window */
466 /* load trash title */
467 tmpStr = GETMESSAGE(27, 9, "Trash Can");
468 title = XtNewString(tmpStr);
470 /* Initially, no items can be selected */
471 if (removeBtn != NULL)
472 XtSetSensitive(*removeBtn, False);
473 if (restoreBtn != NULL)
474 XtSetSensitive(*restoreBtn, False);
476 /* retrieve the default data for a file manager dialog -- */
477 /* the TrashView flag affects this data */
479 dialog_data = _DtGetDefaultDialogData (file_mgr_dialog);
480 file_mgr_data = (FileMgrData *) dialog_data->data;
481 trashFileMgrData = file_mgr_data;
482 trashDialogData = dialog_data;
484 /* build up directory set for trash directory */
485 FileMgrBuildDirectories (file_mgr_data, home_host_name, trash_dir);
487 /* initialize trash data */
488 file_mgr_data->restricted_directory =
489 XtNewString(file_mgr_data->current_directory);
490 file_mgr_data->title = XtNewString(title);
491 file_mgr_data->toolbox = False;
492 file_mgr_data->width = 300;
493 file_mgr_data->height = 300;
495 /* load any positioning information */
496 LoadPositionInfo(file_mgr_data);
498 /* build the trash dialog */
499 _DtBuildDialog (NULL, NULL, NULL, dialog_data, NULL, NULL, NULL,
500 NULL, NULL, False, special_view, title, NULL);
508 /************************************************************************
511 * At startup time, we need to read in the trash information file, to
512 * find out what, if any, files were left in the trash the last time
513 * this client was run. Each entry must be read in and then verified
514 * that it still exists; if it no longer exists, then someone has been
515 * mucking with our trash files, so we will remove it from our knowledge
516 * base. Otherwise, the file is added to the trash list.
518 ************************************************************************/
521 ReadTrashList( void )
523 int intSize, extSize, bufSize;
524 FILE * trashInfoFileId;
525 String external, intName, internal;
529 trashInfoFileId = fopen(TrashInfoFileName, "a+");
530 if (trashInfoFileId == 0)
534 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
536 msg = XtMalloc( strlen( GETMESSAGE(27,93, "Unable to access this trash information file:\n %s\n All trash operations will not be performed.\n The most common causes are:\n - Network authentication.\n - Insufficient disk space.\n - Wrong permissions $HOME/.dt/Trash.") )
537 + strlen( TrashInfoFileName )
540 sprintf( msg, GETMESSAGE(27, 93, "Unable to access this trash information file:\n %s\n All trash operations will not be performed.\n The most common causes are:\n - Network authentication.\n - Insufficient disk space.\n - Wrong permissions $HOME/.dt/Trash."), TrashInfoFileName );
542 _DtMessage(toplevel, title, msg, NULL, HelpRequestCB);
548 chmod(TrashInfoFileName, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
550 bufSize = MAX_PATH * 2;
551 trashEntry = (char *)XtCalloc( 1, bufSize );
553 /* Extract the size of the two path strings */
554 while( fgets( trashEntry, bufSize, trashInfoFileId ) != NULL )
556 int len = strlen( trashEntry );
558 if( sscanf( trashEntry, "%d %d", &extSize, &intSize ) == 2 )
560 external = (char *)XtCalloc( 1, extSize + 1 );
561 intName = (char *)XtCalloc( 1, intSize + 1 );
563 if( len > extSize + intSize + 3 )
564 /* extSize + intSize + 3 is a rough estimate of a trash entry
565 a trash entry looks something like the following:
569 extSize is the size of the string '/tmp/abc'
570 intSize is the size of the string 'abcd'
572 so the len (the string length of the trash entry has AT LEAST to be
573 bigger than extSize + intSize + 3 separator spaces
578 /* sscanf can be used to reduce the code.
579 I'm not using it here because I don't have time to research.
580 I just want it to work.
582 tmpPtr = DtStrchr( trashEntry, ' ' ) + 1;
583 tmpPtr = DtStrchr( tmpPtr, ' ' ) + 1;
585 /* Extract the full external and partial internal file names */
586 memcpy( external, tmpPtr, extSize );
587 memcpy( intName, tmpPtr + extSize + 1, intSize );
589 /* Create internal/trash name */
590 internal = (char *)XtMalloc(strlen(users_home_dir)
592 + strlen(intName) + 2);
593 sprintf(internal, "%s%s/%s", users_home_dir, TRASH_DIR, intName);
595 /* Make sure the file still exists */
596 if (lstat(internal, &statbuf) < 0)
598 /* File no longer exists; ignore it */
605 /* Add to trash list */
606 if (numTrashItems >= trashListSize)
609 trashCan = (TrashEntry *)XtRealloc((char *) trashCan,
610 sizeof(TrashEntry) * trashListSize);
613 trashCan[numTrashItems].intNew = internal;
614 trashCan[numTrashItems].intOrig = XtNewString(external);
615 trashCan[numTrashItems].external = external;
616 trashCan[numTrashItems].filename = intName;
618 } /* end if file exists */
627 fclose(trashInfoFileId);
629 return( WriteTrashEntries() );
634 /************************************************************************
637 * This function is responsible for creating a new copy of the trash
638 * information file. It will open a new, temporary copy, and will
639 * copy in the contents of the trash can. It will then remove the
640 * old trash information file, and will rename the new one, opening
641 * a handle to it for later use.
643 ************************************************************************/
646 WriteTrashEntries( void )
648 static String path = NULL;
652 newFile = fopen(NewTrashInfoFileName, "w+");
655 chmod(NewTrashInfoFileName,
656 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
658 /* Write all remaining entries */
659 for (i = 0; i < numTrashItems; i++)
661 if( WriteEntry(newFile, trashCan[i].external, trashCan[i].filename) < 0 )
664 remove(NewTrashInfoFileName);
669 /* Remove the original information file, and move the new one */
670 (void) fclose(newFile);
671 (void) chown(NewTrashInfoFileName, getuid(), getgid());
672 (void) remove(TrashInfoFileName);
673 (void) rename(NewTrashInfoFileName, TrashInfoFileName);
674 (void) chown(TrashInfoFileName, getuid(), getgid());
683 /************************************************************************
686 * This function writes an entry to the trash information file.
687 * It expects the full path name for the original file [external]
688 * and only the new file name (without path information) [internal].
689 * If the file ends in '/', then it adds a '.' to the end; this is
690 * a special case, so that when we parse this information later, we
691 * are guaranteed to have a directory and a file name.
693 ************************************************************************/
701 return( fprintf(id, "%ld %ld %s %s\n",
702 (long)strlen(external), (long)strlen(internal),
703 external, internal) );
708 /************************************************************************
712 ************************************************************************/
717 FileMgrRec *file_mgr_rec )
722 static Widget * directoryBarBtn;
724 /* Create the menubar hierarchy */
725 trashMenu = (MenuDesc *)XtMalloc(sizeof(MenuDesc) * trashMenuItemCount);
728 for (i = 0; i < trashMenuItemCount; i++)
730 trashMenu[i].helpCallback = TrashHelpRequestCB;
731 trashMenu[i].helpData = NULL;
732 trashMenu[i].activateCallback = NULL;
733 trashMenu[i].activateData = NULL;
734 trashMenu[i].maskBit = NULL;
735 trashMenu[i].isHelpBtn = False;
736 trashMenu[i].label = NULL;
737 trashMenu[i].mnemonic = NULL;
741 /*************************************/
742 /* Create the 'File' menu components */
743 /*************************************/
745 directoryBarBtn = &(trashMenu[j].widget);
746 trashMenu[j].type = MENU_PULLDOWN_BUTTON;
747 trashMenu[j].label = GETMESSAGE(20,1, "File");
748 trashMenu[j].label = XtNewString(trashMenu[j].label);
749 trashMenu[j].mnemonic = GETMESSAGE(20,2, "F");
750 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
751 trashMenu[j].name = "file_trash";
752 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
754 trashMenu[j].type = SHARED_MENU_PANE;
755 trashMenu[j].name = "fileMenuTrash";
756 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
758 selectAllBtn = &(trashMenu[j].widget);
759 trashMenu[j].type = MENU_BUTTON;
760 trashMenu[j].label = GETMESSAGE(20,48, "Select All");
761 trashMenu[j].label = XtNewString(trashMenu[j].label);
762 trashMenu[j].mnemonic = GETMESSAGE(20,51, "S");
763 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
764 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
765 trashMenu[j].name = "selectAll";
766 trashMenu[j++].activateCallback = Select_All;
768 trashMenu[j].type = MENU_BUTTON;
769 trashMenu[j].label = GETMESSAGE(20,52, "Deselect All");
770 trashMenu[j].label = XtNewString(trashMenu[j].label);
771 trashMenu[j].mnemonic = GETMESSAGE(20,55, "D");
772 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
773 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
774 trashMenu[j].name = "deselectAll";
775 trashMenu[j++].activateCallback = Unselect_All;
777 trashMenu[j].type = MENU_SEPARATOR;
778 trashMenu[j].name = "separator";
779 trashMenu[j++].helpCallback = NULL;
781 restoreBtn = &(trashMenu[j].widget);
782 trashMenu[j].type = MENU_BUTTON;
783 trashMenu[j].label = GETMESSAGE(27,24, "Put back");
784 trashMenu[j].label = XtNewString(trashMenu[j].label);
785 trashMenu[j].mnemonic = GETMESSAGE(27,26, "P");
786 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
787 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
788 trashMenu[j].name = "putBack";
789 trashMenu[j++].activateCallback = Restore;
791 removeBtn = &(trashMenu[j].widget);
792 trashMenu[j].type = MENU_BUTTON;
793 trashMenu[j].label = GETMESSAGE(27,28, "Shred");
794 trashMenu[j].label = XtNewString(trashMenu[j].label);
795 trashMenu[j].mnemonic = GETMESSAGE(27,30, "h");
796 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
797 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
798 trashMenu[j].name = "shred";
799 trashMenu[j++].activateCallback = ConfirmRemove;
801 trashMenu[j].type = MENU_SEPARATOR;
802 trashMenu[j].name = "separator";
803 trashMenu[j++].helpCallback = NULL;
805 trashMenu[j].type = MENU_BUTTON;
806 trashMenu[j].maskBit = PREFERENCES;
807 trashMenu[j].label = GETMESSAGE(20,141, "Set View Options ...");
808 trashMenu[j].label = XtNewString(trashMenu[j].label);
809 trashMenu[j].mnemonic = GETMESSAGE(20,6, "V");
810 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
811 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
812 trashMenu[j].name = "setPreferences";
813 trashMenu[j++].activateCallback = ShowPreferencesDialog;
815 trashMenu[j].type = MENU_SEPARATOR;
816 trashMenu[j].name = "separator";
817 trashMenu[j++].helpCallback = NULL;
819 trashMenu[j].type = MENU_BUTTON;
820 trashMenu[j].label = GETMESSAGE(20,117, "Close");
821 trashMenu[j].label = XtNewString(trashMenu[j].label);
822 trashMenu[j].mnemonic = GETMESSAGE(20,118, "C");
823 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
824 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
825 trashMenu[j].name = "close";
826 trashMenu[j++].activateCallback = CloseTrash;
829 /*************************************/
830 /* Create the 'Help' menu components */
831 /*************************************/
833 trashMenu[j].type = MENU_PULLDOWN_BUTTON;
834 trashMenu[j].label = GETMESSAGE(20,123, "Help");
835 trashMenu[j].label = XtNewString(trashMenu[j].label);
836 trashMenu[j].mnemonic = GETMESSAGE(20,9, "H");
837 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
838 trashMenu[j].isHelpBtn = True;
839 trashMenu[j].name = "help_trash";
840 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
842 trashMenu[j].type = SHARED_MENU_PANE;
843 trashMenu[j].name = "help_pane_trash";
844 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
846 trashMenu[j].type = MENU_BUTTON;
847 trashMenu[j].label = GETMESSAGE(20,105, "Overview");
848 trashMenu[j].label = XtNewString(trashMenu[j].label);
849 trashMenu[j].mnemonic = GETMESSAGE(20,106, "v");
850 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
851 trashMenu[j].helpData = HELP_HELP_MENU_STR;
852 trashMenu[j].activateData = HELP_TRASH_OVERVIEW_TOPIC_STR;
853 trashMenu[j].name = "introduction";
854 trashMenu[j++].activateCallback = TrashHelpRequestCB;
856 trashMenu[j].type = MENU_SEPARATOR;
857 trashMenu[j].name = "separator";
858 trashMenu[j++].helpCallback = NULL;
860 trashMenu[j].type = MENU_BUTTON;
861 trashMenu[j].label = GETMESSAGE(20,107, "Tasks");
862 trashMenu[j].label = XtNewString(trashMenu[j].label);
863 trashMenu[j].mnemonic = GETMESSAGE(20,108, "T");
864 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
865 trashMenu[j].helpData = HELP_HELP_MENU_STR;
866 trashMenu[j].activateData = HELP_TRASH_TASKS_TOPIC_STR;
867 trashMenu[j].name = "tasks";
868 trashMenu[j++].activateCallback = TrashHelpRequestCB;
870 trashMenu[j].type = MENU_BUTTON;
871 trashMenu[j].label = GETMESSAGE(20,109, "Reference");
872 trashMenu[j].label = XtNewString(trashMenu[j].label);
873 trashMenu[j].mnemonic = GETMESSAGE(20,110, "R");
874 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
875 trashMenu[j].helpData = HELP_HELP_MENU_STR;
876 trashMenu[j].activateData = HELP_TRASH_DIALOG_STR;
877 trashMenu[j].name = "reference";
878 trashMenu[j++].activateCallback = TrashHelpRequestCB;
880 trashMenu[j].type = MENU_BUTTON;
881 trashMenu[j].label = GETMESSAGE(20,111, "On Item");
882 trashMenu[j].label = XtNewString(trashMenu[j].label);
883 trashMenu[j].mnemonic = GETMESSAGE(20,112, "O");
884 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
885 trashMenu[j].helpData = HELP_HELP_MENU_STR;
886 trashMenu[j].activateData = HELP_HELP_MODE_STR;
887 trashMenu[j].name = "onItem";
888 trashMenu[j++].activateCallback = TrashHelpRequestCB;
890 trashMenu[j].type = MENU_SEPARATOR;
891 trashMenu[j].name = "separator";
892 trashMenu[j++].helpCallback = NULL;
894 usingHelpTrash = &(trashMenu[j].widget);
895 trashMenu[j].type = MENU_BUTTON;
896 trashMenu[j].label = GETMESSAGE(20,113, "Using Help");
897 trashMenu[j].label = XtNewString(trashMenu[j].label);
898 trashMenu[j].mnemonic = GETMESSAGE(20,114, "U");
899 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
900 trashMenu[j].helpData = HELP_HELP_MENU_STR;
901 trashMenu[j].activateData = HELP_HOME_TOPIC;
902 trashMenu[j].name = "usingHelp";
903 trashMenu[j++].activateCallback = TrashHelpRequestCB;
905 trashMenu[j].type = MENU_SEPARATOR;
906 trashMenu[j].name = "separator";
907 trashMenu[j++].helpCallback = NULL;
909 trashMenu[j].type = MENU_BUTTON;
910 trashMenu[j].label = GETMESSAGE(27,106, "About Trash Can");
911 trashMenu[j].label = XtNewString(trashMenu[j].label);
912 trashMenu[j].mnemonic = GETMESSAGE(20,116, "A");
913 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
914 trashMenu[j].helpData = HELP_HELP_MENU_STR;
915 trashMenu[j].activateData = HELP_ABOUT_STR;
916 trashMenu[j].name = "version";
917 trashMenu[j++].activateCallback = TrashHelpRequestCB;
919 menu_bar = _DtCreateMenuSystem(mainw, "menu_bar_trash", TrashHelpRequestCB,
920 HELP_TRASH_DIALOG_STR, True,
921 trashMenu, trashMenuItemCount, NULL,
924 /* Fine tune the menubar */
925 XtSetArg(args[0], XmNmarginWidth, 2);
926 XtSetArg(args[1], XmNmarginHeight, 2);
927 XtSetValues(menu_bar, args, 2);
929 file_mgr_rec->actions = NULL;
930 file_mgr_rec->action_pane = NULL;
931 file_mgr_rec->directoryBarBtn = *directoryBarBtn;
938 /************************************************************************
940 * TrashDisplayHandler
941 * This is the ICCCM message handler for the message used to display
942 * the trash can window. If the window is already displayed, then
943 * this call is a no-op.
945 * This handler is triggered by the front panel trash icon and by the
946 * File pulldown 'Show Trash' option.
948 ************************************************************************/
958 tt_message_reply( msg );
959 tttk_message_destroy( msg );
962 if (!TrashInitialized)
964 char *tmpStr, *tmpTitle, *tmpMsg;
966 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
967 tmpTitle = XtNewString(tmpStr);
968 tmpStr = GETMESSAGE(27, 90, "The 'Trash Can' cannot be initialized.\nPlease check:\n 1)$HOME/.dt/Trash for permissions.\n 2)Available disk space.\n 3)Authentication.");
969 tmpMsg = XtNewString(tmpStr);
971 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
978 #ifdef DEFER_TRASH_CREATION
979 if( trashFileMgrData == NULL )
980 TrashCreateDialog (XtDisplay(toplevel));
983 /* the encapsulation functions do not set file_mgr_rec until a dialog */
984 /* is actually displayed */
985 if (trashFileMgrData->file_mgr_rec == 0)
987 static Pixmap trash_icon = XmUNSPECIFIED_PIXMAP;
988 static Pixmap trash_mask = XmUNSPECIFIED_PIXMAP;
990 Pixel background, foreground, top_shadow, bottom_shadow, select;
992 XClassHint classHints;
993 FileMgrRec * file_mgr_rec;
999 classHints.res_name = trashFileMgrData->title;
1000 classHints.res_class = DTFILE_CLASS_NAME;
1003 _DtShowBuiltDialog(NULL,NULL, trashDialogData, NULL, False, &classHints);
1006 file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
1008 trashShell = file_mgr_rec->shell;
1010 if (trash_icon == XmUNSPECIFIED_PIXMAP)
1012 XtSetArg (args[0], XmNbackground, &background);
1013 XtSetArg (args[1], XmNcolormap, &colormap);
1014 XtGetValues (file_mgr_rec->main, args, 2);
1016 XmGetColors (XtScreen (file_mgr_rec->main), colormap, background,
1017 &foreground, &top_shadow, &bottom_shadow, &select);
1019 pixmap = XmGetPixmap (XtScreen (file_mgr_rec->main), trashIcon,
1020 foreground, background);
1021 if( pixmap != XmUNSPECIFIED_PIXMAP)
1022 trash_icon = pixmap;
1024 /* now let's get the mask for the File Manager */
1025 pixmap = _DtGetMask (XtScreen (file_mgr_rec->main), trashIcon);
1026 if( pixmap != XmUNSPECIFIED_PIXMAP)
1027 trash_mask = pixmap;
1029 if (trash_icon != XmUNSPECIFIED_PIXMAP)
1031 XtSetArg (args[0], XmNiconPixmap, trash_icon);
1032 if(trash_mask != XmUNSPECIFIED_PIXMAP)
1034 XtSetArg (args[1], XmNiconMask, trash_mask);
1035 XtSetValues (trashShell, args, 2);
1038 XtSetValues (trashShell, args, 1);
1046 root = RootWindowOfScreen (XtScreen (trashShell));
1048 /* Change the hints to reflect the current workspace */
1049 /* and raise the window */
1051 if (DtWsmGetCurrentWorkspace (XtDisplay (trashShell),
1052 root, ¤t_ws) == Success)
1053 DtWsmSetWorkspacesOccupied (XtDisplay (trashShell),
1054 XtWindow (trashShell), ¤t_ws, 1);
1056 /* Set the iconify state */
1057 /* Remove the iconify hint from the current shell */
1058 wmhints = XGetWMHints(XtDisplay(trashShell), XtWindow(trashShell));
1059 wmhints->flags |= IconWindowHint;
1060 wmhints->initial_state = NormalState;
1061 XSetWMHints(XtDisplay(trashShell), XtWindow(trashShell), wmhints);
1065 XtPopup (trashShell, XtGrabNone);
1066 XSync(XtDisplay(trashShell), False);
1067 XRaiseWindow (XtDisplay (trashShell), XtWindow (trashShell));
1068 XMapWindow( XtDisplay (trashShell), XtWindow (trashShell) );
1071 msg = tt_pnotice_create(TT_SESSION, "DtActivity_Began");
1072 tt_message_send(msg);
1073 tttk_message_destroy(msg);
1077 trashDialogPosted = True;
1081 /************************************************************************
1083 * ConfirmEmptyCancel
1085 ************************************************************************/
1086 static XtPointer trash_popup_client_data;
1090 XtPointer client_data,
1091 XtPointer call_data )
1093 XtUnmanageChild((Widget)client_data);
1094 XmUpdateDisplay((Widget)client_data);
1095 XtDestroyWidget((Widget)client_data);
1098 /************************************************************************
1102 ************************************************************************/
1107 XtPointer client_data,
1108 XtPointer call_data )
1110 /* destroy dialog */
1111 XtUnmanageChild((Widget)client_data);
1112 XmUpdateDisplay((Widget)client_data);
1113 XtDestroyWidget((Widget)client_data);
1118 /************************************************************************
1121 * This is a message interface used to really remove all of the
1124 * This handler is only called from an empty trash action.
1126 ************************************************************************/
1133 tt_message_reply( msg );
1134 tttk_message_destroy( msg );
1137 if(!TrashInitialized)
1139 char *tmpStr, *tmpTitle, *tmpMsg;
1141 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1142 tmpTitle = XtNewString(tmpStr);
1143 tmpStr = GETMESSAGE(27, 90, "The 'Trash Can' cannot be initialized.\nPlease check:\n 1)$HOME/.dt/Trash for permissions.\n 2)Available disk space.\n 3)Authentication.");
1144 tmpMsg = XtNewString(tmpStr);
1146 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1154 char *tmpStr, *title, *msg;
1156 tmpStr = GETMESSAGE(27,73, "Shred File(s)");
1157 title = XtNewString(tmpStr);
1158 tmpStr = GETMESSAGE(27,84, "When trash objects are shredded, they are permanently\nremoved from your system.\n\nAre you sure you want to shred?\n");
1159 msg = XtNewString(tmpStr);
1161 _DtMessageDialog(toplevel, title, msg, NULL, TRUE, ConfirmEmptyCancel,
1162 ConfirmEmptyOk, NULL, HelpRequestCB, False, QUESTION_DIALOG);
1171 /************************************************************************
1174 * This is a functional interface used to really remove all of the
1177 ************************************************************************/
1182 if (trashFileMgrData)
1185 Select_All(*selectAllBtn, NULL, NULL);
1186 Remove(*removeBtn, NULL, NULL);
1188 EmptyDir(trashFileMgrData->current_directory,0);
1194 /************************************************************************
1197 * This is the callback attached to the 'Select All' menu item. It
1198 * will select all of the files currently resident in the trash can.
1199 * It will also sensitize the 'Restore' and 'Remove' buttons, as long
1200 * as a remove request is not currently in the middle of being processed.
1202 ************************************************************************/
1207 XtPointer client_data,
1208 XtPointer call_data )
1211 SelectAllFiles(trashFileMgrData);
1212 SensitizeTrashBtns();
1217 /************************************************************************
1220 * This is the callback attached to the 'Unselect All' menu item. It
1221 * will unselect all of the items in the trash can, and will clear
1222 * out our selection array. It also desensitizes the 'Restore' and
1225 ************************************************************************/
1230 XtPointer client_data,
1231 XtPointer call_data )
1234 DeselectAllFiles(trashFileMgrData);
1235 SensitizeTrashBtns();
1240 /************************************************************************
1242 * SensitizeTrashBtns
1244 ************************************************************************/
1247 SensitizeTrashBtns(void)
1249 #ifdef DEFER_TRASH_CREATION
1250 if( trashFileMgrData
1255 if (trashFileMgrData->selected_file_count > 0)
1257 XtSetSensitive(*removeBtn, True);
1258 XtSetSensitive(*restoreBtn, True);
1262 XtSetSensitive(*removeBtn, False);
1263 XtSetSensitive(*restoreBtn, False);
1270 /************************************************************************
1272 * TrashRemoveHandler
1273 * This is the message handler for the message requesting that
1274 * a set of files be added to the trashcan. Each file name comes in
1275 * in /dir/name format; they are all in a single string, separated
1278 * Check for spaces in filenames and destop files. If we pass these
1279 * cases, call TrashRemover where the following occurs:
1281 * All of the files will be moved to the trash can directory; any
1282 * problems encountered will be reported in an error dialog. Each file
1283 * which is successfully moved, will be added to the trash information file.
1285 ************************************************************************/
1298 int msglistsize = 0 ;
1299 #endif /* SUN_PERF */
1302 * Note: There used to be a call to tt_message_reply() right here at the
1303 * beginning of the routine. However, the mailer wants to trash files
1304 * but wants the notification after the trash has been removed.
1305 * Therefore, we now put this off until the work is done or an error
1309 if( !TrashInitialized )
1311 char *tmpStr, *tmpTitle, *tmpMsg;
1313 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1314 tmpTitle = XtNewString(tmpStr);
1315 tmpStr = GETMESSAGE(27, 90, "The 'Trash Can' cannot be initialized.\nPlease check:\n 1)$HOME/.dt/Trash for permissions.\n 2)Available disk space.\n 3)Authentication.");
1316 tmpMsg = XtNewString(tmpStr);
1318 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1323 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
1325 global_msg_cnt = 0 ;
1326 #endif /* SUN_PERF */
1330 MessageToFileList(msg, &file_list, &file_count);
1333 * When the Dttrash_Remove handler "TrashRemoveHandler" is invoked for
1334 * the first time it caches the TT request and then invokes
1335 * tttk_block_while() with timeout = 0. This in effect causes the next
1336 * TT request to be processed before tttk_block_while() returns. Now if
1337 * TT msgs are coming in faster than dtfile is handling them, then the
1338 * next TT msg. should be another Dttrash_Remove msg. which will cause
1339 * re-entrance into TrashRemoveHandler. This will continue until the last
1340 * Dttrash_Remove msg. has been processed (and the recursive
1341 * TrashRemoveHandler starts unwinding) after which we check if the
1342 * number of TT msgs are > 0 in which case we fire off the
1343 * process to move the list of files to Trash.
1346 msglistsize = sizeof(Tt_message) * global_msg_cnt ;
1347 global_msg_list = (Tt_message *)XtRealloc((char *)global_msg_list, msglistsize);
1348 memcpy((char *)&(global_msg_list[global_msg_cnt - 1]),
1349 (char *)&msg, sizeof(Tt_message)) ;
1350 tttk_block_while(0, 0, time_out) ;
1352 /* If we get to this pt. then either :
1353 * 1. No more Dttrash_Remove msgs. left to process. Thus we should
1354 * process the TT msg list and move files to Trash.
1356 * 2. We got a msg. different from Dttrash_Remove. In this case
1357 * we process the msgs. we have so far. The remaining files to
1358 * be trashed will be buffered in another list.
1361 if (global_msg_cnt > 0) {
1362 MessagesToFileList(global_msg_list, global_msg_cnt,
1363 &file_list, &file_count);
1364 #endif /* SUN_PERF */
1366 if (file_count == 0)
1368 tt_message_reply( msg );
1369 tttk_message_destroy( msg );
1371 global_msg_cnt = 0 ;
1372 #endif /* SUN_PERF */
1376 /* return if from the trash dir */
1377 if (strncmp(file_list[0], trash_dir, strlen(trash_dir)) == 0)
1381 for (i = 0; i < file_count; i++) {
1382 XtFree( file_list[i] );
1384 XtFree( (char *)file_list );
1385 tt_message_status_set( msg, TT_DESKTOP_EALREADY );
1386 tt_message_reply( msg );
1387 tttk_message_destroy( msg );
1389 global_msg_cnt = 0 ;
1390 #endif /* SUN_PERF */
1394 /* post message if file(s) from desktop */
1398 str=(char *)IsAFileOnDesktop2(file_list, file_count, &number,&IsToolBox);
1399 /* IsToolBox is unused here, but is required to satisfy the prototype */
1403 MoveToTrash(file_list, file_count, verifyPromptsEnabled, msg);
1413 /* List will be regenerated by callbacks */
1414 for (i = 0; i < file_count; i++) {
1415 XtFree( file_list[i] );
1417 XtFree( (char *)file_list );
1419 tmpStr = (GETMESSAGE(27,73, "Shred File(s)"));
1420 title = XtNewString(tmpStr);
1424 template = (GETMESSAGE(27,85, "The Workspace object you are deleting is a linked copy of\n an object inside the File Manager at\n %s\n If you click OK, both the linked copy and the original object\n will be deleted.\n If you just want to delete the Workspace object,\n 1) click Cancel on this dialog\n 2) then select 'Remove From Workspace'\n from the Workspace object's popup menu."));
1426 template = (GETMESSAGE(27,102, "A linked copy of the following object is on\nthe Workspace backdrop:\n%s\nMoving the object to the trash will remove it from\nboth the File Manager and Workspace backdrop."));
1427 message = (char *)XtMalloc(strlen(template) + strlen(str) + 1);
1428 sprintf(message, template, str);
1433 template = (GETMESSAGE(27,86, "The Workspace objects you are deleting are linked copies of\n objects inside the File Manager at\n%s\n If you click OK, both the linked copies and the original objects\n will be deleted.\n If you just want to delete the Workspace objects,\n 1) click Cancel on this dialog\n 2) then select 'Remove From Workspace'\n from the Workspace objects' popup menu."));
1435 template = (GETMESSAGE(27,103, "A linked copy of the following objects are also on\nthe Workspace backdrop:\n%s\nMoving the objects to the trash will remove them from\nboth the File Manager and Workspace backdrop."));
1436 message = (char *)XtMalloc(strlen(template) + strlen(str) + 1);
1437 sprintf(message, template, str);
1440 /* Really ought to pass to OK and Cancel CBs via client_data */
1443 _DtMessageDialog(toplevel, title, message, NULL, TRUE,
1444 RemoveCancelCB, RemoveOkCB, NULL, HelpRequestCB, False,
1450 Tt_msg_cache *msg_cache ;
1452 msg_cache = (Tt_msg_cache *)XtMalloc(sizeof(Tt_msg_cache)) ;
1453 msg_cache->msg_cnt = global_msg_cnt ;
1455 msg_cache->msg_list = (Tt_message *)XtMalloc(sizeof(Tt_message) *
1456 msg_cache->msg_cnt );
1457 msg_cache->msg_list[msg_cache->msg_cnt] = NULL ;
1458 memcpy((char *)msg_cache->msg_list, (char *)global_msg_list,
1459 sizeof(Tt_message) * msg_cache->msg_cnt ) ;
1460 dlog = (Widget)_DtMessageDialog(toplevel, title, message, NULL, TRUE,
1461 RemoveCancelCB, RemoveOkCB, NULL, HelpRequestCB, False,
1463 XtSetArg ( args[0], XmNuserData, msg_cache );
1464 XtSetValues ( dlog, args, 1);
1466 #endif /* SUN_PERF */
1473 global_msg_cnt = 0 ;
1475 #endif /* SUN_PERF */
1480 /************************************************************************
1483 * Cleanup and unmanage the remove from DT dialog
1485 ************************************************************************/
1490 XtPointer client_data,
1491 XtPointer call_data )
1497 Tt_msg_cache *current_msg_cache ;
1500 XtSetArg( args[0], XmNuserData, ¤t_msg_cache );
1501 XtGetValues( (Widget)client_data, args, 1 );
1502 #endif /* SUN_PERF */
1504 /* destroy dialog */
1505 XtUnmanageChild((Widget)client_data);
1506 XmUpdateDisplay((Widget)client_data);
1507 XtDestroyWidget((Widget)client_data);
1511 MessageToFileList(global, &file_list, &file_count);
1513 MessagesToFileList( current_msg_cache->msg_list, current_msg_cache->msg_cnt,
1514 &file_list, &file_count);
1515 #endif /* SUN_PERF */
1517 MoveToTrash(file_list, file_count, verifyPromptsEnabled, NULL);
1519 /* reread desktop files */
1523 tt_message_reply( global );
1524 tttk_message_destroy( global );
1527 for (i = 0 ; i < current_msg_cache->msg_cnt ; i++) {
1528 tt_message_reply( current_msg_cache->msg_list[i] );
1529 tttk_message_destroy( current_msg_cache->msg_list[i] );
1531 XtFree ((char *)current_msg_cache->msg_list);
1532 XtFree ((char *)current_msg_cache);
1533 #endif /* SUN_PERF */
1538 /************************************************************************
1541 * Cleanup and unmanage the remove from DT dialog
1543 ************************************************************************/
1548 XtPointer client_data,
1549 XtPointer call_data )
1553 Tt_msg_cache *current_msg_cache ;
1556 XtSetArg( args[0], XmNuserData, ¤t_msg_cache );
1557 XtGetValues( (Widget)client_data, args, 1 );
1558 #endif /* SUN_PERF */
1560 /* destroy dialog */
1561 XtUnmanageChild((Widget)client_data);
1562 XmUpdateDisplay((Widget)client_data);
1563 XtDestroyWidget((Widget)client_data);
1566 tttk_message_fail( global, TT_DESKTOP_ECANCELED, 0, 1 );
1569 for (i = 0 ; i < current_msg_cache->msg_cnt ; i++)
1570 tttk_message_fail( current_msg_cache->msg_list[i], TT_DESKTOP_ECANCELED, 0, 1 );
1571 XtFree ((char *)current_msg_cache->msg_list);
1572 XtFree ((char *)current_msg_cache);
1573 #endif /* SUN_PERF */
1578 /************************************************************************
1580 * TrashRemoveNoConfirmHandler
1581 * This function is a message handler. It will place the specified set
1582 * of files into the trash can, without requesting confirmation if, for
1583 * instance, a file does not have write permission, or a directory is
1586 ************************************************************************/
1589 TrashRemoveNoConfirmHandler(
1595 if( !TrashInitialized )
1597 char *tmpStr, *tmpTitle, *tmpMsg;
1599 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1600 tmpTitle = XtNewString(tmpStr);
1601 tmpStr = GETMESSAGE(27, 90, "The 'Trash Can' cannot be initialized.\nPlease check:\n 1)$HOME/.dt/Trash for permissions.\n 2)Available disk space.\n 3)Authentication.");
1602 tmpMsg = XtNewString(tmpStr);
1604 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1610 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
1614 MessageToFileList(msg, &file_list, &file_count);
1615 MoveToTrash(file_list, file_count, False, msg);
1620 /************************************************************************
1624 * Parses a list of file names from a message
1625 * and returns an array of strings.
1627 * if (no message arguments) {
1628 * if (no file attribute) {
1629 * return length==0 list
1631 * return length==1 list containing file attribute
1634 * if (file attribute) {
1635 * file attribute is a directory, prepended onto subsequent filenames
1637 * if (arg 0 is non-null) {
1638 * arg 0 interpreted as a space-separated list of filenames
1641 * arg interpreted as a filename possibly containing spaces
1645 ************************************************************************/
1661 args = tt_message_args_count( msg );
1662 if (tt_is_err(tt_int_error( args ))) {
1666 dir = tt_message_file( msg );
1667 if ((tt_is_err(tt_ptr_error( dir ))) || (dir == 0)) {
1671 /* No args means use tt_message_file() as is */
1672 *file_list = (char **)
1673 XtRealloc((char *)(*file_list),
1674 (*file_count + 1)*sizeof(char *));
1675 (*file_list)[*file_count] = XtNewString(dir);
1680 if (dir[strlen(dir)] != '/') {
1682 if (strcmp(dir, "/") == 0)
1684 newdir = (char *)tt_malloc( strlen(dir)+1 );
1685 strcpy( newdir, dir );
1689 newdir = (char *)tt_malloc( strlen(dir)+2 );
1690 strcpy( newdir, dir );
1691 strcat( newdir, "/" );
1697 /* Invariant: dir can now be safely concatenated to form a valid path */
1698 dirlen = strlen(dir);
1699 for (arg = 0; arg < args; arg++) {
1704 val = tt_message_arg_val( msg, arg );
1705 if ((tt_is_err(tt_ptr_error( val ))) || (val == 0)) {
1711 /* Arg 0 is a space-separated list */
1714 for (val = DtStrtok(val, white); val; val = DtStrtok(0, white))
1716 if (strcmp(val, "/") == 0)
1718 file = (char *)XtMalloc( dirlen + 1 );
1719 strcpy( file, dir );
1723 file = (char *)XtMalloc( dirlen + strlen(val) + 1 );
1724 strcpy( file, dir );
1725 strcat( file, val );
1727 *file_list = (char **)
1728 XtRealloc((char *)(*file_list),
1729 (*file_count + 1)*sizeof(char *));
1730 (*file_list)[*file_count] = DtEliminateDots(file);
1733 tt_free( val2free );
1741 Tt_message *msg_list,
1756 for (num_msgs = 0 ; num_msgs < msg_cnt ; num_msgs++)
1758 msg = msg_list[num_msgs] ;
1760 args = tt_message_args_count( msg );
1761 if (tt_is_err(tt_int_error( args ))) {
1765 dir = tt_message_file( msg );
1766 if ((tt_is_err(tt_ptr_error( dir ))) || (dir == 0)) {
1770 /* No args means use tt_message_file() as is */
1771 *file_list = (char **)
1772 XtRealloc((char *)(*file_list),
1773 (*file_count + 1)*sizeof(char *));
1774 (*file_list)[*file_count] = XtNewString(dir);
1779 if (dir[strlen(dir)] != '/') {
1781 if (strcmp(dir, "/") == 0)
1783 newdir = (char *)tt_malloc( strlen(dir)+1 );
1784 strcpy( newdir, dir );
1788 newdir = (char *)tt_malloc( strlen(dir)+2 );
1789 strcpy( newdir, dir );
1790 strcat( newdir, "/" );
1796 /* Invariant: dir can now be safely concatenated to form a valid path */
1797 dirlen = strlen(dir);
1798 for (arg = 0; arg < args; arg++) {
1803 val = tt_message_arg_val( msg, arg );
1804 if ((tt_is_err(tt_ptr_error( val ))) || (val == 0)) {
1810 /* Arg 0 is a space-separated list */
1813 for (val = DtStrtok(val, white); val; val = DtStrtok(0, white))
1815 if (strcmp(val, "/") == 0)
1817 file = (char *)XtMalloc( dirlen + 1 );
1818 strcpy( file, dir );
1820 else if (strcmp(val, "/") == 0 && dirlen == 0)
1822 file = (char *)XtMalloc( strlen(val) + 1 );
1823 strcpy( file, val );
1827 file = (char *)XtMalloc( dirlen + strlen(val) + 1 );
1828 strcpy( file, dir );
1829 strcat( file, val );
1831 *file_list = (char **)
1832 XtRealloc((char *)(*file_list),
1833 (*file_count + 1)*sizeof(char *));
1834 (*file_list)[*file_count] = DtEliminateDots(file);
1837 tt_free( val2free );
1842 #endif /* SUN_PERF */
1845 /************************************************************************
1847 * CreateTrashFilename
1848 * Create trash directory name.
1850 ************************************************************************/
1853 CreateTrashFilename(
1855 Boolean uniqueTest )
1858 char * extension = NULL;
1859 struct stat statInfo;
1861 /* Create trash path */
1862 /* Give that name a little extra cushion, just in case */
1863 trashName = (char *)XtMalloc(strlen(users_home_dir) + strlen(TRASH_DIR) +
1864 strlen(baseName) + 15);
1865 sprintf(trashName, "%s%s/%s", users_home_dir, TRASH_DIR, baseName);
1867 /* Want to find the extension so the new file name created will preserve
1868 its original datatype.
1870 extension = strrchr( baseName, '.' );
1877 /* If a file by the trash name already exists, keep building new names */
1878 /* until one by that name doesn't exist and then use it */
1879 if (uniqueTest && (lstat(trashName, &statInfo) == 0))
1883 for (i = 1; True ; i++)
1885 /* Make a duplicate file name */
1887 sprintf(trashName, "%s%s/%s_%d.%s", users_home_dir, TRASH_DIR,
1888 baseName, i, extension);
1890 sprintf(trashName, "%s%s/%s_%d", users_home_dir, TRASH_DIR,
1893 /* Is the filename taken? */
1894 if (lstat(trashName, &statInfo) != 0)
1898 } /* end if not unique name */
1901 *(extension-1) = '.';
1909 /************************************************************************
1911 * MatchesSacredDirectory
1912 * Checks if the specify filename matches one of the sacred,
1913 * non-deleteable files.
1915 ************************************************************************/
1918 MatchesSacredDirectory(
1923 /* Don't allow the user to delete any of the sacred directories */
1924 for (i = 0; i < sacred_dir_count; i++)
1926 if (!strcmp(file, sacred_dir_list[i]))
1930 /* Compare against special desktop directories */
1931 /* remote_sys_dir is currently NULL */
1932 if (strcmp(file, remote_sys_dir) == 0)
1940 /************************************************************************
1943 * Callback for 'Delete To Trash' verify dialog Ok push button.
1945 ************************************************************************/
1950 XtPointer client_data,
1951 XtPointer call_data )
1953 VerifyCleanup ((Widget)client_data, True);
1958 /************************************************************************
1961 * Callback for 'Delete To Trash' verify dialog Cancel push button.
1963 ************************************************************************/
1968 XtPointer client_data,
1969 XtPointer call_data )
1972 VerifyCleanup ((Widget)client_data, False);
1977 /************************************************************************
1980 * Callback for 'Delete To Trash' verify dialog push buttons.
1982 * Called from VerifyOk and VerifyCancel.
1984 ************************************************************************/
1989 Boolean completeDelete )
1996 /* Unpost dialog, and retrieve the list of verified files */
1997 XtUnmanageChild(mbox);
1998 XmUpdateDisplay(mbox);
1999 XtSetArg(args[0], XmNuserData, &verifylist);
2000 XtGetValues(mbox, args, 1);
2002 /* count the number of files in the list */
2003 for (fileCount = 0; verifylist[fileCount]; fileCount++)
2007 * Start the background process that moves the files into the trash,
2011 MoveToTrash(verifylist, fileCount, False, NULL);
2015 /* Free up the storage we allocated */
2016 for (i = fileCount; i > 0; i--)
2017 XtFree(verifylist[i]);
2018 XtFree((char *)verifylist);
2021 XtDestroyWidget(mbox);
2026 /************************************************************************
2030 ************************************************************************/
2031 static XtPointer trash_popup_client_data;
2035 XtPointer client_data,
2036 XtPointer call_data )
2038 XtUnmanageChild((Widget)client_data);
2039 XmUpdateDisplay((Widget)client_data);
2040 XtDestroyWidget((Widget)client_data);
2042 XtSetSensitive(*removeBtn, True);
2043 XtSetSensitive(*restoreBtn, True);
2044 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
2045 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
2046 trash_popup_client_data = NULL;
2051 /************************************************************************
2055 ************************************************************************/
2060 XtPointer client_data,
2061 XtPointer call_data )
2063 FileViewData *file_view_data;
2065 /* destroy dialog */
2066 XtUnmanageChild((Widget)client_data);
2067 XmUpdateDisplay((Widget)client_data);
2068 XtDestroyWidget((Widget)client_data);
2070 file_view_data = trashFileMgrData->popup_menu_icon;
2071 if(!file_view_data && trashFileMgrData->selected_file_count)
2072 file_view_data = trashFileMgrData->selection_list[0];
2073 trashFileMgrData->popup_menu_icon = NULL;
2075 if(file_view_data != NULL)
2076 Remove(*removeBtn, file_view_data, NULL);
2081 /************************************************************************
2084 * This is the callback attached to the 'Remove' menu item.
2086 ************************************************************************/
2091 XtPointer client_data,
2092 XtPointer call_data )
2094 char *tmpStr, *title, *msg;
2096 /* Desensitize remove & restore buttons, until we're done */
2097 XtSetSensitive(*removeBtn, False);
2098 XtSetSensitive(*restoreBtn, False);
2099 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], False);
2100 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], False);
2103 trash_popup_client_data = client_data;
2105 tmpStr = GETMESSAGE(27,73, "Shred File(s)");
2106 title = XtNewString(tmpStr);
2107 tmpStr = GETMESSAGE(27,84, "When trash objects are shredded, they are permanently\nremoved from your system.\n\nAre you sure you want to shred?\n");
2108 msg = XtNewString(tmpStr);
2110 _DtMessageDialog(toplevel, title, msg, NULL, TRUE, ConfirmCancel, ConfirmOk,
2111 NULL, HelpRequestCB, False, QUESTION_DIALOG);
2119 /************************************************************************
2122 * While we wait for the background process to complete, we will
2123 * desensitize the 'Remove' and 'Restore' menu items, since we don't
2124 * handle multiple requests.
2126 ************************************************************************/
2131 XtPointer client_data,
2132 XtPointer call_data )
2134 FileViewData *file_view_data;
2135 Boolean match = False;
2136 DeleteList *deleteList = NULL;
2142 /* Remove may be called to remove a file or to remove a trash file; */
2143 /* if w is NULL, this is a call to remove a file */
2146 removeType = TRASH_FILE;
2148 /* Count number of items to 'Delete To Trash' */
2149 if (client_data != NULL)
2151 /* This was called by trash popup */
2152 /* need to check to see if it is selected, if it is restore all
2153 the selected files, else just restore this file */
2155 file_view_data = (FileViewData *)client_data;
2156 for (i = 0; i < trashFileMgrData->selected_file_count; i++)
2158 if (strcmp(file_view_data->file_data->file_name,
2159 trashFileMgrData->selection_list[i]->file_data->file_name) == 0)
2167 if (trashFileMgrData->selected_file_count == 0)
2169 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
2170 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
2177 /* Desensitize remove & restore buttons, until we're done */
2178 XtSetSensitive(*removeBtn, False);
2179 XtSetSensitive(*restoreBtn, False);
2183 deleteCount = trashFileMgrData->selected_file_count;
2187 /* Create the list of things being deleted */
2188 deleteList = (DeleteList *)XtMalloc(deleteCount * sizeof(DeleteList));
2192 for (i = 0; i < deleteCount; i++)
2194 AddToDeleteList(deleteList, i,
2195 trashFileMgrData->selection_list[i]->file_data->file_name);
2199 AddToDeleteList(deleteList, 0, file_view_data->file_data->file_name);
2204 removeType = REMOVE_FILE;
2205 deleteList = (DeleteList *)XtMalloc(sizeof(DeleteList));
2207 deleteList[0].trash = XtNewString(client_data);
2208 deleteList[0].orig = NULL;
2211 EmptyTrash(deleteList, deleteCount, removeType, NULL);
2216 /************************************************************************
2219 * Locate a file in the trash list and add it to the delete list.
2221 ************************************************************************/
2225 DeleteList *deleteList,
2231 /* Locate file in trash list, add entry to delete list */
2232 for (j = 0; j < numTrashItems; j++)
2234 if (strcmp(filename, trashCan[j].filename) == 0)
2236 /* file found in trash list */
2237 deleteList[i].trash = XtNewString(trashCan[j].intNew);
2238 deleteList[i].orig = XtNewString(trashCan[j].intOrig);
2244 /* file not found in trash list */
2245 deleteList[i].trash = CreateTrashFilename(filename, FALSE);
2246 deleteList[i].orig = NULL;
2251 /************************************************************************
2253 * TrashRestoreHandler
2254 * This function is a message handler. It will restore the specified set
2255 * of files from the trash can.
2257 ************************************************************************/
2260 TrashRestoreHandler(
2266 if( !TrashInitialized )
2268 char *tmpStr, *tmpTitle, *tmpMsg;
2270 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
2271 tmpTitle = XtNewString(tmpStr);
2272 tmpStr = GETMESSAGE(27, 90, "The 'Trash Can' cannot be initialized.\nPlease check:\n 1)$HOME/.dt/Trash for permissions.\n 2)Available disk space.\n 3)Authentication.");
2273 tmpMsg = XtNewString(tmpStr);
2275 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
2281 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
2286 /* Desensitize remove & restore buttons, until we're done */
2287 XtSetSensitive(*removeBtn, False);
2288 XtSetSensitive(*restoreBtn, False);
2290 MessageToFileList(msg, &file_list, &file_count);
2291 RestoreFromTrash(file_list, file_count, NULL, NULL, msg,False);
2296 /************************************************************************
2299 * This is the callback attached to the 'Restore' menu item. It will
2300 * remove from the trash all of the selected files, and will restore
2301 * them to their original location. If any problems occur while trying
2302 * to restore any of the files, then an error dialog will be posted.
2304 ************************************************************************/
2309 XtPointer client_data,
2310 XtPointer call_data )
2312 FileViewData *file_view_data;
2313 Boolean match = False;
2318 if (client_data != NULL)
2320 /* This was called by trash popup */
2321 /* need to check to see if it is selected, if it is restore all
2322 the selected files, else just restore this file */
2324 file_view_data = trashFileMgrData->popup_menu_icon;
2326 /* The object would have been deleted if the following condn is true */
2327 if(!file_view_data && !trashFileMgrData->selected_file_count)
2329 trashFileMgrData->popup_menu_icon = NULL;
2331 for (i = 0; i < trashFileMgrData->selected_file_count; i++)
2333 if (strcmp(file_view_data->file_data->file_name,
2334 trashFileMgrData->selection_list[i]->file_data->file_name) == 0)
2343 /* Desensitize remove & restore buttons, until we're done */
2344 XtSetSensitive(*removeBtn, False);
2345 XtSetSensitive(*restoreBtn, False);
2350 file_count = trashFileMgrData->selected_file_count;
2351 file_list = (char **)XtMalloc(file_count * sizeof(char *));
2352 for (i = 0; i < file_count; i++)
2354 file_view_data = trashFileMgrData->selection_list[file_count - 1 - i];
2355 file_list[i] = XtNewString(file_view_data->file_data->file_name);
2361 file_list = (char **)XtMalloc(sizeof(char *));
2362 file_list[0] = XtNewString(file_view_data->file_data->file_name);
2365 /* Start the background process that will do the restore */
2366 RestoreFromTrash(file_list, file_count, NULL, NULL, NULL,False);
2371 /************************************************************************
2374 * This is the callback attached to the 'Close' menu item. It will
2375 * unpost the trash can window.
2377 ************************************************************************/
2382 XtPointer client_data,
2383 XtPointer call_data )
2387 if (trashDialogPosted)
2389 XWithdrawWindow(XtDisplay(trashShell), XtWindow(trashShell),
2390 XDefaultScreen(XtDisplay(trashShell)));
2391 XtPopdown(trashShell);
2395 for (i = 0; i < secondaryTrashHelpDialogCount; i++)
2397 if (_DtIsDialogShowing(secondaryTrashHelpDialogList[i]))
2398 _DtHideDialog(secondaryTrashHelpDialogList[i], False);
2399 _DtFreeDialogData(secondaryTrashHelpDialogList[i]);
2401 XtFree((char *)secondaryTrashHelpDialogList);
2402 secondaryTrashHelpDialogList = NULL;
2403 secondaryTrashHelpDialogCount = 0;
2405 if (primaryTrashHelpDialog)
2407 if (_DtIsDialogShowing(primaryTrashHelpDialog))
2408 _DtHideDialog(primaryTrashHelpDialog, False);
2409 _DtFreeDialogData(primaryTrashHelpDialog);
2411 primaryTrashHelpDialog = NULL;
2413 if (PositionFlagSet(trashFileMgrData))
2414 SavePositionInfo(trashFileMgrData);
2417 FileMgrRec * file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
2419 if( (file_mgr_rec->menuStates & PREFERENCES) == 0 )
2420 file_mgr_rec->menuStates |= PREFERENCES;
2423 trashDialogPosted = False;
2428 /************************************************************************
2432 ************************************************************************/
2439 DtDndDropCallbackStruct *drop_parameters)
2442 DtActionArg * action_args;
2443 FileMgrRec * file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
2444 Position drop_x = drop_parameters->x;
2445 Position drop_y = drop_parameters->y;
2447 _DtBuildActionArgsWithDroppedFiles(NULL, drop_parameters,
2448 &action_args, &arg_count);
2450 DtActionInvoke(file_mgr_rec->shell, TRASH_ACTION,
2451 action_args, arg_count, NULL, NULL,
2452 trashFileMgrData->current_directory, True, NULL, NULL);
2454 _DtFreeActionArgs(action_args, arg_count);
2456 RepositionIcons(trashFileMgrData, file_set, file_count, drop_x,
2462 /************************************************************************
2466 ************************************************************************/
2470 FileMgrData *file_mgr_data,
2471 FileMgrRec *file_mgr_rec,
2485 * Get target host and directory
2487 target_dir = XtNewString(file_mgr_data->current_directory);
2488 target_host = XtNewString(file_mgr_data->host);
2491 * Create file list and call RestoreFromTrash
2493 file_list = (char **)XtMalloc(file_count * sizeof(char *));
2495 for (i = 0; i < file_count; i++)
2496 file_list[i] = XtNewString(file_set[i]);
2498 RestoreFromTrash(file_list, file_count, target_host, target_dir, NULL,False);
2503 /************************************************************************
2506 * Locate file in trash list based on new internal name. Return file name.
2508 ************************************************************************/
2514 Boolean IsTrash = False;
2516 if (strncmp(users_home_dir, filename, strlen(users_home_dir)) == 0)
2519 ((filename + strlen(users_home_dir)), TRASH_DIR, strlen(TRASH_DIR))
2529 /************************************************************************
2532 * Since the trash does not use shared menupanes, there is no work to
2533 * be done during the popup and popdown callbacks; therefore, we use
2534 * and empty function.
2536 ************************************************************************/
2541 XtPointer clientData,
2542 XtPointer callData )
2549 /*--------------------------------------------------------------------
2550 * UpdateDirectoryOf:
2551 * Arrange for the directory containing a file to be updated
2552 *------------------------------------------------------------------*/
2561 /* remove last component from path to get the directory */
2562 ptr = strrchr(path, '/');
2566 strcpy(host, home_host_name);
2568 /* now arrange for the directory to be updated */
2569 UpdateDirectory(NULL, host, path);
2571 /* restore the path */
2577 /*--------------------------------------------------------------------
2578 * EraseObject, EraseDir
2579 * Routines for recursively deleting files and directories
2580 *------------------------------------------------------------------*/
2583 EraseObject(char *file_name)
2585 struct stat stat_buf;
2587 if (lstat(file_name, &stat_buf) < 0)
2590 else if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
2591 return EraseDir(file_name);
2593 else if (remove(file_name) < 0)
2602 EraseDir(char *dir_name)
2604 DIR *dir; /* open directory */
2605 struct dirent *entry; /* directory entry */
2606 char srcname[MAX_PATH];
2610 /* open source directory */
2611 dir = opendir(dir_name);
2615 /* prepare source name */
2616 strcpy(srcname, dir_name);
2617 srclen = strlen(srcname);
2618 if (srcname[srclen - 1] != '/')
2619 srcname[srclen++] = '/';
2622 while (rc == 0 && (entry = readdir(dir)) != NULL)
2624 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
2626 strcpy(srcname + srclen, entry->d_name);
2627 rc = EraseObject(srcname);
2634 if (rmdir(dir_name) < 0)
2636 if (unlink(dir_name) < 0 )
2648 /*====================================================================
2650 * MoveToTrashProcess
2651 * Run a background process to move files to the trash can.
2653 *==================================================================*/
2655 /*--------------------------------------------------------------------
2656 * MoveToTrashProcess
2657 * Main routine of background process for MoveToTrash
2658 *------------------------------------------------------------------*/
2665 Boolean do_verify_checks)
2671 struct stat statInfo;
2673 struct dirent * entry;
2678 for (i = 0; i < file_count; i++)
2680 /* get base name and full path */
2681 path = XtNewString(file_list[i]);
2682 baseName = strrchr(file_list[i], '/');
2683 if (baseName == NULL || path == NULL)
2685 /* Invalid filename */
2687 pipe_msg = PIPEMSG_OTHER_ERROR;
2688 rc = BAD_FILE_ERROR;
2689 DPRINTF(("MoveToTrashProcess: sending BAD_FILE_ERROR\n"));
2690 write(pipe_fd, &pipe_msg, sizeof(short));
2691 write(pipe_fd, &rc, sizeof(int));
2694 if (path && MatchesSacredDirectory(path))
2696 /* Invalid filename */
2698 pipe_msg = PIPEMSG_OTHER_ERROR;
2699 rc = BAD_FILE_SACRED;
2700 DPRINTF(("MoveToTrashProcess: sending BAD_FILE_SACRED\n"));
2701 write(pipe_fd, &pipe_msg, sizeof(short));
2702 write(pipe_fd, &rc, sizeof(int));
2707 if (do_verify_checks)
2709 char *tmpstr = (file_list[i] == (baseName - 1))?"/":file_list[i];
2711 Boolean TrashError = False;
2713 /* check write permissions for the file */
2714 if (lstat(path,&s1) < 0)
2721 savechar = *(baseName-1);
2722 *(baseName-1) = '\0';
2723 if(CheckDeletePermission(tmpstr, path) != 0)
2725 /* No write access; display error message */
2727 if (S_ISDIR(s1.st_mode))
2728 rc = BAD_TRASH_DIRECTORY;
2730 rc = BAD_TRASH_FILE;
2733 *(baseName-1) = savechar;
2738 pipe_msg = PIPEMSG_OTHER_ERROR;
2739 DPRINTF(("MoveToTrashProcess: sending BAD_TRASH message\n"));
2740 write(pipe_fd, &pipe_msg, sizeof(short));
2741 write(pipe_fd, &rc, sizeof(int));
2744 else if (CheckAccess(path, W_OK) != 0 && !S_ISLNK(s1.st_mode))
2746 /* No write access; ask user for verification */
2748 pipe_msg = PIPEMSG_OTHER_ERROR;
2750 DPRINTF(("MoveToTrashProcess: sending VERIFY_FILE\n"));
2751 write(pipe_fd, &pipe_msg, sizeof(short));
2752 write(pipe_fd, &rc, sizeof(int));
2757 * If this is a directory, make sure it's empty, i.e.,
2758 * contains only ".", "..", and ".!" or ".~" files.
2760 if (lstat(path, &statInfo) == 0 &&
2761 (statInfo.st_mode & S_IFMT) == S_IFDIR &&
2762 (dirp = opendir(path)) != NULL)
2764 /* read the directory */
2765 while ((entry = readdir(dirp)) != NULL)
2767 if ( !(strcmp(entry->d_name, ".") == 0 ||
2768 strcmp(entry->d_name, "..") == 0 ||
2769 strncmp(entry->d_name, ".!", 2) == 0 ||
2770 strncmp(entry->d_name, ".~", 2) == 0) )
2772 /* found a real file: directory not empty */
2781 /* Directory is not empty */
2783 pipe_msg = PIPEMSG_OTHER_ERROR;
2785 DPRINTF(("MoveToTrashProcess: sending VERIFY_DIR\n"));
2786 write(pipe_fd, &pipe_msg, sizeof(short));
2787 write(pipe_fd, &rc, sizeof(int));
2791 } /* end if do_verify_checks */
2794 to = CreateTrashFilename(baseName, TRUE);
2796 /* move file to the trash directory */
2797 success = FileManip((Widget)pipe_fd, MOVE_FILE, path, to, TRUE,
2798 FileOpError, True, TRASH_DIRECTORY);
2801 pipe_msg = PIPEMSG_DONE;
2802 DPRINTF(("MoveToTrashProcess: sending DONE\n"));
2803 write(pipe_fd, &pipe_msg, sizeof(short));
2804 PipeWriteString(pipe_fd, path);
2805 PipeWriteString(pipe_fd, to);
2814 /*--------------------------------------------------------------------
2815 * MoveToTrashPipeCB:
2816 * Read and process data sent through the pipe.
2817 *------------------------------------------------------------------*/
2821 XtPointer client_data,
2825 MoveToTrashCBData *cb_data = (MoveToTrashCBData *)client_data;
2828 char *title, *err_msg, *err_arg;
2841 DesktopRec *desktopWin;
2842 char *dir_error=NULL,*file_error=NULL,*no_file_error=NULL,*sacred_error=NULL;
2844 /* read the next msg from the pipe */
2846 n = PipeRead(*fd, &pipe_msg, sizeof(short));
2847 DPRINTF(("MoveToTrashPipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
2851 case PIPEMSG_FILEOP_ERROR:
2852 PipeRead(*fd, &rc, sizeof(int));
2853 err_msg = PipeReadString(*fd);
2854 err_arg = PipeReadString(*fd);
2856 FileOperationError(toplevel, err_msg, err_arg);
2861 cb_data->rc[cb_data->done_count++] = BAD_FILE_ERROR;
2864 case PIPEMSG_OTHER_ERROR:
2865 PipeRead(*fd, &rc, sizeof(int));
2866 cb_data->rc[cb_data->done_count++] = rc;
2869 case PIPEMSG_TARGET_TIME:
2872 case PIPEMSG_FILE_MODIFIED:
2876 i = cb_data->done_count++;
2877 cb_data->path[i] = PipeReadString(*fd);
2878 cb_data->to[i] = PipeReadString(*fd);
2884 "Internal error in MoveToTrashPipeCB: bad pipe_msg %d\n",
2886 /* Don't know how it can get in here, but if it does, we'll try to
2887 * simulate an error condition. Without it, the SGI hung up
2889 while (cb_data->done_count < cb_data->file_count)
2890 cb_data->rc[cb_data->done_count++] = BAD_FILE_ERROR;
2895 /* if still more files to be processed, return now to wait for the rest */
2896 if (cb_data->done_count < cb_data->file_count)
2900 /* close the pipe and cancel the callback */
2901 DPRINTF(("MoveToTrashPipeCB: done\n"));
2905 /* process the results */
2917 for (i = 0; i < cb_data->file_count; i++)
2919 if (cb_data->rc[i]==BAD_TRASH_DIRECTORY || cb_data->rc[i]==BAD_TRASH_FILE)
2924 Boolean errflg = False;
2925 if(cb_data->rc[i] == BAD_TRASH_DIRECTORY)
2927 tmpmsg = GETMESSAGE(27, 113,
2928 "At least one object in the following Folder(s) or \nthe folder itself cannot be deleted.\nThe most likely cause is that you do not have permissions\nto delete that object.\n\n");
2931 dir_error = (char *) XtRealloc(dir_error,strlen(dir_error)+
2932 strlen(cb_data->file_list[i])+5);
2933 strcat(dir_error,cb_data->file_list[i]);
2934 strcat(dir_error,"\n");
2938 dir_error = XtMalloc(strlen(tmpmsg)+
2939 strlen(cb_data->file_list[i])+5);
2940 sprintf(dir_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
2945 tmpmsg = GETMESSAGE(27, 114,
2946 "The following file(s) cannot be deleted. The most likely cause \nis that you do not have permissions to delete the file(s).\n\n");
2949 file_error = (char *) XtRealloc(file_error,strlen(file_error)
2950 + strlen(cb_data->file_list[i])+5);
2951 strcat(file_error,cb_data->file_list[i]);
2952 strcat(file_error,"\n");
2956 file_error = XtMalloc(strlen(tmpmsg)+
2957 strlen(cb_data->file_list[i])+5);
2958 sprintf(file_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
2961 XtFree(cb_data->file_list[i]);
2962 cb_data->file_list[i] = NULL;
2966 else if (cb_data->rc[i] == BAD_FILE_SACRED)
2970 char *tmpmsg = GETMESSAGE(27, 115,
2971 "The following object(s) cannot be deleted.\nThe desktop cannot function properly without these object(s).\n\n");
2974 sacred_error = (char *) XtRealloc(sacred_error,strlen(sacred_error)+
2975 strlen(cb_data->file_list[i])+5);
2976 strcat(sacred_error,cb_data->file_list[i]);
2977 strcat(sacred_error,"\n");
2981 sacred_error=XtMalloc(strlen(tmpmsg)+strlen(cb_data->file_list[i])+5);
2982 sprintf(sacred_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
2984 XtFree(cb_data->file_list[i]);
2985 cb_data->file_list[i] = NULL;
2989 else if (cb_data->rc[i] == BAD_FILE_ERROR)
2991 XtFree(cb_data->file_list[i]);
2992 cb_data->file_list[i] = NULL;
2994 else if (cb_data->rc[i] == NO_TRASH_FILE)
2997 tmpmsg = GETMESSAGE(27, 112, "The following object(s) cannot be deleted.\nThe most likely cause is that these object(s)\nhave already been deleted.\n\n");
3003 no_file_error = (char *)XtRealloc(no_file_error,strlen(no_file_error)+
3004 strlen(cb_data->file_list[i])+5);
3005 strcat(no_file_error,cb_data->file_list[i]);
3006 strcat(no_file_error,"\n");
3010 no_file_error = XtMalloc(strlen(tmpmsg)+
3011 strlen(cb_data->file_list[i])+5);
3012 sprintf(no_file_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
3017 else if (cb_data->rc[i] == VERIFY_FILE ||
3018 cb_data->rc[i] == VERIFY_DIR)
3020 if (verifyCount < 6)
3021 AddString(&verifybuf, &verifybufsize, cb_data->file_list[i], NULL);
3025 * Keep track of whether we have directories only, files only,
3026 * or a combination, so that we can display the appropriate
3027 * label in the dialog.
3029 if (cb_data->rc[i] == VERIFY_FILE)
3035 * Add to array which is to be attached to the dialog;
3036 * the array will ultimately be NULL terminated.
3038 verifylist = (char **)XtRealloc((char *)verifylist,
3039 sizeof(char *) * (verifyCount + 1));
3040 verifylist[verifyCount - 1] = cb_data->file_list[i];
3044 /* Add file to trash list */
3045 if (numTrashItems >= trashListSize)
3047 trashListSize += 10;
3048 trashCan = (TrashEntry *)
3049 XtRealloc((char *)trashCan, sizeof(TrashEntry) * trashListSize);
3052 trashCan[numTrashItems].problem = False;
3053 trashCan[numTrashItems].intNew = cb_data->to[i];
3054 trashCan[numTrashItems].intOrig = cb_data->path[i];
3055 trashCan[numTrashItems].external = cb_data->file_list[i];
3057 /* extract base file name */
3058 baseName = strrchr(cb_data->to[i], '/');
3060 if (*baseName == '\0')
3062 trashCan[numTrashItems].filename = XtNewString(baseName);
3067 /* arrange for the source directory to be updated */
3068 UpdateDirectoryOf(cb_data->path[i]);
3071 * If the source file was referenced by a desktop object,
3072 * we need to remove the destkop object
3074 for (j = 0; j < desktop_data->numIconsUsed; j++)
3076 Tt_status tt_status;
3078 desktopWin = desktop_data->desktopWindows[j];
3079 fileName = ResolveLocalPathName( desktopWin->host,
3080 desktopWin->dir_linked_to,
3081 desktopWin->file_name,
3082 home_host_name, &tt_status);
3084 if( TT_OK == tt_status )
3086 if (strcmp(fileName, cb_data->path[i]) == 0)
3088 RemoveDT(desktopWin->shell, (XtPointer)desktopWin, NULL);
3090 RemoveMovedObjectFromDT(desktopWin->shell, (XtPointer)desktopWin,
3091 cb_data->file_count, cb_data->file_list);
3092 #endif /* SUN_PERF */
3101 buf = XtMalloc(strlen(dir_error)+3);
3102 sprintf(buf,"%s\n",dir_error);
3109 buf = XtMalloc(strlen(file_error)+3);
3110 sprintf(buf,"%s\n",file_error);
3114 buf = XtRealloc(buf,strlen(buf)+strlen(file_error)+3);
3115 sprintf(buf,"%s%s\n",buf,file_error);
3123 buf = XtMalloc(strlen(no_file_error)+3);
3124 sprintf(buf,"%s\n",no_file_error);
3128 buf = XtRealloc(buf,strlen(buf)+strlen(no_file_error)+3);
3129 sprintf(buf,"%s%s\n",buf,no_file_error);
3131 XtFree(no_file_error);
3137 buf = XtMalloc(strlen(sacred_error)+3);
3138 sprintf(buf,"%s\n",sacred_error);
3142 buf = XtRealloc(buf,strlen(buf)+strlen(sacred_error)+3);
3143 sprintf(buf,"%s%s\n",buf,sacred_error);
3145 XtFree(sacred_error);
3148 bufsize = strlen(buf);
3150 /* Update the trash information file, and the trash window */
3153 FILE * trashInfoFileId = fopen(TrashInfoFileName, "a+");
3154 if( trashInfoFileId != NULL )
3156 for (i = fileCount; i > 0; i--)
3158 if( WriteEntry(trashInfoFileId,
3159 trashCan[numTrashItems - i].external,
3160 trashCan[numTrashItems - i].filename) < 0 )
3163 fflush(trashInfoFileId);
3164 fclose(trashInfoFileId);
3165 if( trashFileMgrData )
3166 UpdateDirectory(NULL, trashFileMgrData->host,
3167 trashFileMgrData->current_directory);
3171 /* Check for any bad files; post an error dialog */
3174 /* send a reply to the message that triggered this operation, if any */
3176 if (cb_data->msg != 0) {
3178 Until Action is fixed.
3179 tttk_message_fail( cb_data->msg, TT_DESKTOP_EACCES, 0, 1 );
3181 tt_message_reply( cb_data->msg );
3182 tttk_message_destroy( cb_data->msg );
3184 if (cb_data->msg_cnt > 0) {
3185 for (i = 0 ; i < cb_data->msg_cnt ; i++) {
3186 tt_message_reply( cb_data->msg_list[i] ) ;
3187 tttk_message_destroy( cb_data->msg_list[i] ) ;
3189 XtFree((char *)cb_data->msg_list) ;
3190 cb_data->msg_cnt = 0 ;
3191 #endif /* SUN_PERF */
3194 /* If more items than can be displayed, let user know */
3197 char extraFiles[256];
3199 (void) sprintf(extraFiles, AdditionalHeader, badCount - 8);
3203 GETMESSAGE(27,97, "The following objects could not be placed in the trash can: \n"));
3206 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3207 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3209 XtFree ((char *) buf);
3211 /* send a reply to the message that triggered this operation, if any */
3213 if (cb_data->msg != 0) {
3214 tt_message_reply( cb_data->msg );
3215 tttk_message_destroy( cb_data->msg );
3217 if (cb_data->msg_cnt > 0) {
3218 for (i = 0 ; i < cb_data->msg_cnt ; i++) {
3219 tt_message_reply( cb_data->msg_list[i] ) ;
3220 tttk_message_destroy( cb_data->msg_list[i] ) ;
3222 XtFree((char *)cb_data->msg_list) ;
3223 cb_data->msg_cnt = 0 ;
3224 #endif /* SUN_PERF */
3229 /* Check for any files requiring user verification; post a prompt dialog */
3230 /* XXX Really should fail incoming ToolTalk request if user cancels any */
3238 /* If more items than can be displayed, let user know */
3239 if (verifyCount > 6)
3241 char extraFiles[256];
3243 (void) sprintf(extraFiles, AdditionalHeader, verifyCount - 6);
3244 AddString(&verifybuf, &verifybufsize, extraFiles, NULL);
3248 * Depending upon what type of files are to be displayed in the
3249 * dialog, choose the appropriate dialog text.
3251 if (vfiles && vdirs)
3252 header = GETMESSAGE(27, 6, " Each of the following is either a non-empty folder, \n or a file for which you do not have write permission. \n Do you want to proceed?\n");
3254 header = GETMESSAGE(27,104, "You do not have write permission for the following file(s):\nDo you want to proceed?\n");
3256 header = GETMESSAGE(27,100, "The following folder(s) are not empty.\nDo you want to proceed?\n");
3258 tmpbuf = XtMalloc(strlen(header) + strlen(verifybuf) + 1);
3259 sprintf(tmpbuf, "%s%s", header, verifybuf);
3260 title = XtNewString(GETMESSAGE(27, 4, "Trash Can Warning"));
3261 dlog = (Widget)_DtMessageDialog(toplevel, title, tmpbuf, NULL, True,
3262 VerifyCancel, VerifyOk, NULL, HelpRequestCB,
3263 False, WARNING_DIALOG);
3265 XtFree ((char *) verifybuf);
3269 * Add array as userdata on the dialog.
3270 * NULL terminate the array.
3272 verifylist[verifyCount] = NULL;
3273 XtSetArg(args[0], XmNuserData, verifylist);
3274 XtSetValues(dlog, args, 1);
3277 /* free the callback data */
3278 XtFree((char *)cb_data->file_list);
3279 XtFree((char *)cb_data->path);
3280 XtFree((char *)cb_data->to);
3281 XtFree((char *)cb_data->rc);
3282 XtFree((char *)cb_data);
3288 /*--------------------------------------------------------------------
3290 * Start the background process and set up callback for the pipe.
3291 *------------------------------------------------------------------*/
3297 Boolean do_verify_checks,
3300 static char *pname = "MoveToTrash";
3301 MoveToTrashCBData *cb_data;
3305 if( !TrashInitialized )
3308 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
3313 /* set up callback data */
3314 cb_data = XtNew(MoveToTrashCBData);
3315 cb_data->file_list = file_list;
3316 cb_data->file_count = file_count;
3317 cb_data->done_count = 0;
3318 cb_data->path = (char **)XtCalloc(file_count, sizeof(char *));
3319 cb_data->to = (char **)XtCalloc(file_count, sizeof(char *));
3320 cb_data->rc = (int *)XtCalloc(file_count, sizeof(int));
3323 cb_data->msg_cnt = 0 ;
3324 if (global_msg_cnt > 0) {
3325 cb_data->msg_cnt = global_msg_cnt ;
3326 cb_data->msg_list = (Tt_message *)XtMalloc(sizeof(Tt_message) * cb_data->msg_cnt);
3327 memcpy((char *)cb_data->msg_list,(char *)global_msg_list, sizeof(Tt_message) *
3330 #endif /* SUN_PERF */
3335 /* fork the process that does the actual work */
3340 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
3341 pname, getppid(), getpid(), errno, strerror(errno));
3348 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
3350 close(pipe_fd[0]); /* child won't read from the pipe */
3352 MoveToTrashProcess(pipe_fd[1], file_list, file_count, do_verify_checks);
3355 DBGFORK(("%s: child exiting\n", pname));
3360 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
3362 /* parent: set up callback to get the pipe data */
3363 close(pipe_fd[1]); /* parent won't write the pipe */
3365 cb_data->child = pid;
3367 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
3368 pipe_fd[0], (XtPointer)XtInputReadMask,
3369 MoveToTrashPipeCB, (XtPointer)cb_data);
3373 /*====================================================================
3377 * Run a background process to restore files from the trash can.
3379 * These routines are used both for "normal restores", i.e.,
3380 * restores initiated from the Trash menu or through an ICCCM
3381 * message, as well as "drag&drop restores", i.e., restores done
3382 * by dragging files from the trash can to some other directory.
3383 * For normal resores, file_list contains only simple file names
3384 * (no paths) and target_dir is NULL. For drag&drop restores
3385 * file_list contains complete path names and target_dir contains
3386 * the name of the directoy to which the files should be moved.
3388 *==================================================================*/
3390 /*--------------------------------------------------------------------
3392 * Main routine of background process for RestoreFromTrash
3393 *------------------------------------------------------------------*/
3403 Boolean CheckedAlready)
3412 char **RestoreList= NULL;
3414 /* get full path name of target directory */
3417 Tt_status tt_status;
3418 full_dirname = ResolveLocalPathName(target_host, target_dir,
3419 NULL, home_host_name, &tt_status);
3420 if( TT_OK != tt_status )
3422 /* send return codes back trough the pipe */
3423 pipe_msg = PIPEMSG_DONE;
3425 DPRINTF(("RestoreProcess: Unable to Resolve local path name\n"));
3426 write(pipe_fd, &pipe_msg, sizeof(short));
3427 write(pipe_fd, rc, sizeof(int));
3432 full_dirname = NULL;
3434 /* restore the files */
3435 for (i = 0; i < file_count; i++)
3437 /* Locate file in trash list */
3438 for (j = 0; j < numTrashItems; j++)
3440 /* file_list[i] may be a complete path or just a file name */
3441 if (strcmp(file_list[i], trashCan[j].filename) == 0 ||
3442 strcmp(file_list[i], trashCan[j].intNew) == 0)
3448 /* determine source and target for the move */
3449 if (target_dir == NULL)
3451 /* this is a normal restore */
3452 if (j < numTrashItems)
3454 from = trashCan[j].intNew;
3455 to = trashCan[j].intOrig;
3459 /* can't do a restore if the file wasn't found in the trash list */
3465 /* this is a drag&drop from the trash can to target_dir */
3466 from = file_list[i];
3467 if (j < numTrashItems)
3468 ptr = strrchr(trashCan[j].intOrig, '/');
3470 ptr = strrchr(file_list[i], '/');
3471 strcpy(buf, full_dirname);
3479 status = RestoreObject((Widget)pipe_fd, MOVE_FILE, from,to,
3480 TRUE, FileOpError, False, NOT_DESKTOP,CheckedAlready);
3481 /* restore was successful */
3482 if(status == (int) True)
3484 else if(status == (int) False)
3490 /* restore failed */
3494 /* send return codes back trough the pipe */
3495 pipe_msg = PIPEMSG_DONE;
3496 DPRINTF(("RestoreProcess: sending DONE\n"));
3497 write(pipe_fd, &pipe_msg, sizeof(short));
3498 write(pipe_fd, rc, file_count * sizeof(int));
3500 XtFree(full_dirname);
3504 /*--------------------------------------------------------------------
3506 * Read and process data sent through the pipe.
3507 *------------------------------------------------------------------*/
3511 XtPointer client_data,
3515 RestoreFromTrashCBData *cb_data = (RestoreFromTrashCBData *)client_data;
3518 char *title, *err_msg, *err_arg;
3522 char **ToRestoreList=NULL;
3523 char **FromRestoreList=NULL;
3524 char *target_host,*target_dir;
3527 /* read the next msg from the pipe */
3529 n = PipeRead(*fd, &pipe_msg, sizeof(short));
3530 DPRINTF(("RestorePipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
3534 case PIPEMSG_FILEOP_ERROR:
3535 PipeRead(*fd, &rc, sizeof(int));
3536 err_msg = PipeReadString(*fd);
3537 err_arg = PipeReadString(*fd);
3538 FileOperationError(toplevel, err_msg, err_arg);
3539 /* This call will popup an error dialog.
3541 FileOperationError(toplevel, err_msg, err_arg);
3543 It's not appropriate at all to popup an error dialog here.
3544 i.e. if there're 1000 files, and the file system is full,
3545 and we're unable to move, would we want to popup 1000
3552 PipeRead(*fd, cb_data->rc, cb_data->file_count * sizeof(int));
3557 "Internal error in RestorePipeCB: bad pipe_msg %d\n",
3561 /* close the pipe and cancel the callback */
3562 DPRINTF(("RestorePipeCB: done\n"));
3569 for (i = 0; i < cb_data->file_count; i++)
3571 /* Locate file in trash list */
3572 for (j = 0; j < numTrashItems; j++)
3574 /* file_list[i] may be a complete path or just a file name */
3575 if (strcmp(cb_data->file_list[i], trashCan[j].filename) == 0 ||
3576 strcmp(cb_data->file_list[i], trashCan[j].intNew) == 0)
3582 if (cb_data->rc[i] == SKIP_FILE)
3584 ToRestoreList = (char **) XtRealloc((char *)ToRestoreList,sizeof(char *) *
3586 ToRestoreList[RestoreIndex-1] = XtNewString(trashCan[j].intOrig);
3587 FromRestoreList = (char **) XtRealloc((char *)FromRestoreList,sizeof(char *) *
3589 FromRestoreList[RestoreIndex-1] = XtNewString( trashCan[j].intNew );
3592 /* Check the return code from the restore */
3593 else if (cb_data->rc[i] == 0)
3595 /* restore was successful: remove the file from the trash list */
3596 if (j < numTrashItems)
3598 /* arrange for the source directory to be updated */
3599 UpdateDirectoryOf(trashCan[j].intOrig);
3601 /* Remove this entry from the trash list */
3602 XtFree ((char *) trashCan[j].intNew);
3603 XtFree ((char *) trashCan[j].intOrig);
3604 XtFree ((char *) trashCan[j].external);
3605 XtFree ((char *) trashCan[j].filename);
3606 for (k = j; k < (numTrashItems - 1); k++)
3607 trashCan[k] = trashCan[k + 1];
3614 char *restore_header,*tmpStr = GETMESSAGE(27,101,
3615 "The following object(s) could not be put back:\n");
3617 restore_header = XtNewString(tmpStr);
3618 if (j < numTrashItems && cb_data->target_dir == NULL)
3619 AddString(&buf, &bufsize, trashCan[j].external, restore_header);
3621 AddString(&buf, &bufsize, cb_data->file_list[i], restore_header);
3622 XtFree(restore_header);
3626 /* Update the trash information file */
3627 if( ! WriteTrashEntries() )
3629 char * tmpStr, * title;
3631 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3632 tmpStr = XtNewString(GETMESSAGE(27, 88, "Cannot write to a temporary file.\nPerhaps your file system is full.\n"));
3633 _DtMessage(toplevel, title, tmpStr, NULL, HelpRequestCB);
3638 /* send a reply to the message that triggered this operation, if any */
3639 if (cb_data->msg != 0) {
3640 tt_message_reply( cb_data->msg );
3641 tttk_message_destroy( cb_data->msg );
3645 /* Report any errors */
3648 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3649 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3651 XtFree ((char *) buf);
3654 UpdateDirectory(NULL, trashFileMgrData->host,
3655 trashFileMgrData->current_directory);
3656 if (cb_data->target_dir)
3657 UpdateDirectory(NULL, cb_data->target_host, cb_data->target_dir);
3659 target_host = XtNewString(cb_data->target_host);
3660 target_dir = XtNewString(cb_data->target_dir);
3662 /* free the callback data */
3663 for (i = 0; i < cb_data->file_count; i++)
3664 XtFree(cb_data->file_list[i]);
3665 XtFree((char *)cb_data->file_list);
3666 XtFree((char *)cb_data->target_host);
3667 XtFree((char *)cb_data->target_dir);
3668 XtFree((char *)cb_data->rc);
3669 XtFree((char *)cb_data);
3672 for(i=0;i<RestoreIndex;i++)
3674 CreateRestoreDialog(FromRestoreList[i],ToRestoreList[i]);
3675 XtFree(FromRestoreList[i]);
3676 XtFree(ToRestoreList[i]);
3678 XtFree((char *) FromRestoreList);
3679 XtFree((char *) ToRestoreList);
3681 XtFree(target_host);
3686 /*--------------------------------------------------------------------
3688 * Start the background process and set up callback for the pipe.
3689 *------------------------------------------------------------------*/
3698 Boolean CheckedAlready)
3700 static char *pname = "RestoreFromTrash";
3701 RestoreFromTrashCBData *cb_data;
3705 if( !TrashInitialized )
3708 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
3713 /* set up callback data */
3714 cb_data = XtNew(RestoreFromTrashCBData);
3715 cb_data->file_list = file_list;
3716 cb_data->file_count = file_count;
3717 cb_data->target_host = target_host;
3718 cb_data->target_dir = target_dir;
3719 cb_data->rc = (int *)XtCalloc(file_count, sizeof(int));
3725 /* fork the process that does the actual work */
3730 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
3731 pname, getppid(), getpid(), errno, strerror(errno));
3738 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
3740 close(pipe_fd[0]); /* child won't read from the pipe */
3742 RestoreProcess(pipe_fd[1], file_list, file_count,
3743 target_host, target_dir, cb_data->rc,CheckedAlready);
3746 DBGFORK(("%s: child exiting\n", pname));
3751 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
3753 /* parent: set up callback to get the pipe data */
3754 close(pipe_fd[1]); /* parent won't write the pipe */
3756 cb_data->child = pid;
3758 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
3759 pipe_fd[0], (XtPointer)XtInputReadMask,
3760 RestorePipeCB, (XtPointer)cb_data);
3765 /*====================================================================
3768 * Run a background process to remove files from the trash can.
3770 *==================================================================*/
3772 /*--------------------------------------------------------------------
3774 * Main routine of background process for EmptyTrash
3775 *------------------------------------------------------------------*/
3780 DeleteList * del_list,
3788 * Delete all files or directories in the list,
3789 * as well as any associated annotations.
3791 for (i = 0; i < del_count; i++)
3793 /* delete the file */
3794 rc[i] = EraseObject(del_list[i].trash);
3797 /* send return codes back trough the pipe */
3798 pipe_msg = PIPEMSG_DONE;
3799 DPRINTF(("EmptyTrashProcess: sending DONE\n"));
3800 write(pipe_fd, &pipe_msg, sizeof(short));
3801 write(pipe_fd, rc, del_count * sizeof(int));
3805 /*--------------------------------------------------------------------
3807 * Read and process data sent through the pipe.
3808 *------------------------------------------------------------------*/
3812 XtPointer client_data,
3816 EmptyTrashCBData *cb_data = (EmptyTrashCBData *)client_data;
3819 char *title, *err_msg, *err_arg;
3820 int problemCount, itemCount;
3825 /* read the next msg from the pipe */
3827 n = PipeRead(*fd, &pipe_msg, sizeof(short));
3828 DPRINTF(("EmptyTrashPipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
3832 case PIPEMSG_FILEOP_ERROR:
3833 PipeRead(*fd, &rc, sizeof(int));
3834 err_msg = PipeReadString(*fd);
3835 err_arg = PipeReadString(*fd);
3836 /* This call will popup an error dialog.
3838 FileOperationError(toplevel, err_msg, err_arg);
3840 It's not appropriate at all to popup an error dialog here.
3841 i.e. if there're 1000 files, and the file system is full,
3842 and we're unable to move, would we want to popup 1000
3850 PipeRead(*fd, cb_data->rc, cb_data->del_count * sizeof(int));
3855 "Internal error in EmptyTrashPipeCB: bad pipe_msg %d\n",
3859 /* close the pipe and cancel the callback */
3860 DPRINTF(("EmptyTrashPipeCB: done\n"));
3869 if (cb_data->removeType == TRASH_FILE)
3871 for (i = 0; i < cb_data->del_count; i++)
3873 /* Locate file in trash list */
3874 for (j = 0; j < numTrashItems; j++)
3875 if (strcmp(cb_data->del_list[i].trash, trashCan[j].intNew) == 0)
3878 /* Check the return code from the erase */
3879 if (cb_data->rc[i] == 0)
3881 /* erase was successful: remove the file from the trash list */
3882 if (j < numTrashItems)
3884 /* Remove this entry from the trash list */
3885 XtFree ((char *) trashCan[j].intNew);
3886 XtFree ((char *) trashCan[j].intOrig);
3887 XtFree ((char *) trashCan[j].external);
3888 XtFree ((char *) trashCan[j].filename);
3889 for (k = j; k < (numTrashItems - 1); k++)
3890 trashCan[k] = trashCan[k + 1];
3901 char * msg = XtNewString(GETMESSAGE(27,96, "The following objects could not be removed from the file system: \n"));
3903 if (j < numTrashItems)
3904 file_name = trashCan[j].external;
3906 file_name = cb_data->del_list[i].trash;
3907 AddString(&buf, &bufsize, file_name, msg);
3914 /* Update the trash information file */
3915 if( ! WriteTrashEntries() )
3917 char * tmpStr, * title;
3919 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3920 tmpStr = XtNewString(GETMESSAGE(27, 88, "Cannot write to a temporary file.\nPerhaps your file system is full.\n"));
3921 _DtMessage(toplevel, title, tmpStr, NULL, HelpRequestCB);
3926 /* Report any errors */
3929 /* If more items than can be displayed, let user know */
3930 if (itemCount < problemCount)
3932 char extraFiles[256];
3933 sprintf(extraFiles, AdditionalHeader, problemCount - itemCount);
3937 GETMESSAGE(27,97, "The following objects could not be placed in the trash can: \n") );
3940 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3941 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3943 XtFree ((char *) buf);
3946 if (trashFileMgrData->selected_file_count > 0)
3948 XtSetSensitive(*removeBtn, True);
3949 XtSetSensitive(*restoreBtn, True);
3952 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
3953 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
3955 UpdateDirectory(NULL, trashFileMgrData->host,
3956 trashFileMgrData->current_directory);
3960 if (cb_data->rc[0] == 0)
3962 /* arrange for the directory containing the file to be updated */
3963 UpdateDirectoryOf(cb_data->del_list[0].trash);
3967 /* put up an error message saying the file couldn't be removed */
3971 tmpStr = GETMESSAGE(27, 81, "Shred File/Folder Error");
3972 title = XtNewString(tmpStr);
3973 tmpStr = GETMESSAGE(27, 82, " The following file could not be removed from the file system: \n");
3974 file_name = cb_data->del_list[0].trash;
3975 buffer = XtMalloc(strlen(tmpStr) + strlen(file_name) + 1);
3976 sprintf(buffer, "%s%s\n", tmpStr, file_name);
3977 _DtMessage(toplevel, title, buffer, NULL, HelpRequestCB);
3984 /* reset removingTrash flag */
3985 removingTrash = False;
3987 /* send a reply to the message that triggered this operation, if any */
3988 if (cb_data->msg != 0) {
3989 tt_message_reply( cb_data->msg );
3990 tttk_message_destroy( cb_data->msg );
3994 /* free the callback data */
3995 for (i = 0; i < cb_data->del_count; i++)
3997 XtFree(cb_data->del_list[i].trash);
3998 XtFree(cb_data->del_list[i].orig);
4000 XtFree((char *)cb_data->del_list);
4001 XtFree((char *)cb_data->rc);
4002 XtFree((char *)cb_data);
4006 /*--------------------------------------------------------------------
4008 * Start the background process and set up callback for the pipe.
4009 *------------------------------------------------------------------*/
4013 DeleteList *del_list,
4018 static char *pname = "EmptyTrash";
4019 EmptyTrashCBData *cb_data;
4023 if( !TrashInitialized )
4026 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
4031 /* set removingTrash flag */
4032 removingTrash = True;
4034 /* set up callback data */
4035 cb_data = XtNew(EmptyTrashCBData);
4036 cb_data->del_list = del_list;
4037 cb_data->del_count = del_count;
4038 cb_data->removeType = removeType;
4039 cb_data->rc = (int *)XtCalloc(del_count, sizeof(int));
4045 /* fork the process that does the actual work */
4050 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
4051 pname, getppid(), getpid(), errno, strerror(errno));
4058 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
4060 close(pipe_fd[0]); /* child won't read from the pipe */
4062 EmptyTrashProcess(pipe_fd[1], del_list, del_count, cb_data->rc);
4065 DBGFORK(("%s: child exiting\n", pname));
4070 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
4072 /* parent: set up callback to get the pipe data */
4073 close(pipe_fd[1]); /* parent won't write the pipe */
4075 cb_data->child = pid;
4077 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
4078 pipe_fd[0], (XtPointer)XtInputReadMask,
4079 EmptyTrashPipeCB, (XtPointer)cb_data);
4083 CheckDeletePermission(
4085 char *destinationPath)
4087 struct stat statbuf;
4090 if (lstat(parentdir,&statbuf) < 0) /* does not exist */
4093 /* check if we are root */
4096 /* if NFS, need to check if server trusts root */
4097 if (FileSysType(statbuf.st_dev) < 0) /* Root user and nfs */
4100 tmpfile = tempnam(parentdir,"quang");
4101 if (creat(tmpfile,O_RDONLY)< 0) /* Create a temporary file */
4103 if (remove(tmpfile) < 0) /* Delete the created file */
4107 /* root user can delete anything */
4111 /* check for write and execute permisssion on parent dir */
4112 if (CheckAccess(parentdir, W_OK | X_OK) < 0)
4115 /* copy destinationPath to tmp buffer */
4116 strcpy(fname, destinationPath);
4118 return CheckDeletePermissionRecur(fname);
4123 CheckDeletePermissionRecur(
4124 char *destinationPath)
4126 struct stat statbuf;
4132 DPRINTF(("CheckDeletePermissionRecur(\"%s\")\n", destinationPath));
4134 if (lstat(destinationPath, &statbuf) < 0)
4135 return -1; /* probably does not exist */
4137 if (! S_ISDIR(statbuf.st_mode))
4138 return 0; /* no need to check anything more */
4140 dirp = opendir (destinationPath);
4142 return -1; /* could not read directory */
4147 while (dp = readdir (dirp))
4149 if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
4153 /* check for write permission in this directory */
4154 if (CheckAccess(destinationPath, W_OK|X_OK) < 0)
4157 /* append a '/' to the end of directory name */
4158 fnamep = destinationPath + strlen(destinationPath);
4164 /* append file name to end of directory name */
4165 strcpy(fnamep, dp->d_name);
4167 /* recursively check permission on this file */
4168 if (CheckDeletePermissionRecur(destinationPath))
4181 if(ustat(dev,&u1) < 0)
4190 register char *source,
4191 register char *target,
4192 Boolean isContainer,
4193 void (*errorHandler)(),
4194 Boolean checkForBusyDir,
4196 Boolean CheckedAlready)
4198 struct stat statsrc,stattar;
4200 char *localdir,*chrptr;
4204 if(stat(source,&statsrc) < 0)
4205 if(lstat(source,&statsrc) < 0)
4208 localdir = strdup(target);
4210 chrptr = strrchr(localdir,'/');
4213 if(chrptr == localdir) /* Must be root folder */
4218 if(stat(target,&stattar) >= 0) /* Target exists */
4220 if(CheckDeletePermission(localdir,target))
4221 return ((int)False);
4226 return ((int )FileManip((Widget)w, MOVE_FILE, source, target, TRUE,
4227 FileOpError, False, NOT_DESKTOP));
4230 CreateRestoreDialog(
4234 char *tmpbuf,*title;
4238 char **dirs = (char **) malloc(sizeof(char *) * 2);
4239 char *header = GETMESSAGE(27,110,"There is already an object named\n\n%s\n\nSelect Ok if you want to replace the object\nwith the object from the Trash.\n\nSelect Cancel if you want to keep the object.\nIf you then rename the object, you will be able\nto repeat the Put Back and keep both objects.");
4241 dirs[0] = strdup(source);
4242 dirs[1] = strdup(target);
4243 tmpbuf = XtMalloc(strlen(header) + strlen(target) + 1);
4244 sprintf(tmpbuf, header, target);
4245 title = XtNewString(GETMESSAGE(27, 109, "Put Back Warning"));
4246 dw = (Widget)_DtMessageDialog(toplevel, title,tmpbuf,NULL,True,RestoreVerifyCancel,
4247 RestoreVerifyOk, NULL, HelpRequestCB, False, WARNING_DIALOG);
4249 XtSetArg(args[n], XmNuserData,dirs); n++;
4250 XtSetValues(dw,args,n);
4258 XtPointer client_data,
4259 XtPointer call_data )
4264 Widget mbox = (Widget)client_data;
4266 XtSetArg(args[n],XmNuserData,&dirs); n++;
4267 XtGetValues(mbox,args,n);
4269 if(DirectoryBusy(dirs[1]))
4272 char * tmpStr,*title;
4274 title = XtNewString(GETMESSAGE(27,111,"Put Back Error"));
4275 tmpStr = GETMESSAGE(11,30, "Cannot move or rename the folder %s.\nAll File Manager views displayed for a folder or its sub-folders\nmust be closed before a folder can be moved or renamed.");
4276 msgbuf = XtMalloc(strlen(tmpStr) + strlen(dirs[1])+1);
4277 sprintf (msgbuf, tmpStr, dirs[1]);
4278 _DtMessage(toplevel,title,msgbuf,NULL,HelpRequestCB);
4286 char *realTarget,*tptr;
4290 if(lstat(dirs[1],&s1) < 0)
4292 fsErase(dirs[1],&status);
4293 FileList = (char **) XtMalloc(sizeof(char *));
4294 FileList[0] = XtNewString(dirs[0]);
4295 RestoreFromTrash(FileList, (int) 1, NULL, NULL, NULL,True);
4301 XtDestroyWidget(mbox);
4305 RestoreVerifyCancel(
4307 XtPointer client_data,
4308 XtPointer call_data )
4314 Widget mbox = (Widget)client_data;
4316 XtSetArg(args[n],XmNuserData,&dirs); n++;
4317 XtGetValues(mbox,args,n);
4321 XtDestroyWidget(mbox);