2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these libraries and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $TOG: Trash.c /main/12 1999/12/09 13:07:25 mgreess $ */
24 /************************************<+>*************************************
25 ****************************************************************************
29 * COMPONENT_NAME: Desktop File Manager (dtfile)
31 * Description: Source file for the trash code.
33 * FUNCTIONS: AddToDeleteList
34 * CheckDeletePermission
35 * CheckDeletePermissionRecur
50 * MatchesSacredDirectory
74 * TrashRemoveNoConfirmHandler
84 * (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
85 * (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
86 * (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
87 * (c) Copyright 1993, 1994, 1995 Novell, Inc.
89 ****************************************************************************
90 ************************************<+>*************************************/
92 #include <EUSCompat.h>
94 #include <sys/types.h>
104 #if defined(CSRG_BASED)
105 #include <sys/param.h>
106 #include <sys/mount.h>
108 #if defined(__linux__)
110 #include <linux/magic.h>
116 #include <Xm/RowColumn.h>
117 #include <Xm/CascadeB.h>
119 #include <Xm/Frame.h>
120 #include <Xm/MainW.h>
121 #include <Xm/PushBG.h>
122 #include <Xm/SeparatoG.h>
123 #include <Xm/MessageB.h>
124 #include <Xm/MwmUtil.h>
126 #include <X11/Shell.h>
127 #include <X11/Xatom.h>
128 #include <Xm/Protocols.h>
130 #include <Dt/Action.h>
132 #include <Dt/FileM.h>
133 #include <Dt/DtNlUtils.h>
134 #include <Dt/Connect.h>
135 #include <Dt/Indicator.h>
136 #include <Dt/EnvControlP.h>
139 #include <Dt/SharedProcs.h>
140 #include "DtSvcInternal.h" /* _DtGetMask */
145 #include "SharedProcs.h"
146 #include "MultiView.h"
151 #include "SharedMsgs.h"
152 #include "dtcopy/fsrtns.h"
154 #define AdditionalHeader (GETMESSAGE(27,98, "(Plus %d additional object(s))"))
156 /* Trash file errors */
157 #define NO_FILE_ERROR 0
158 #define BAD_FILE_ERROR -1
159 #define VERIFY_DIR -2
160 #define VERIFY_FILE -3
161 #define BAD_TRASH_DIRECTORY -4
162 #define BAD_TRASH_FILE -5
163 #define NO_TRASH_FILE -6
166 #define BAD_FILE_SACRED -9
168 /* types of messages sent through the pipe */
169 #define PIPEMSG_FILEOP_ERROR 1
170 #define PIPEMSG_OTHER_ERROR 3
171 #define PIPEMSG_DONE 7
172 #define PIPEMSG_TARGET_TIME 5
173 #define PIPEMSG_FILE_MODIFIED 6
176 * Structure describing each file in the trash can.
177 * Includes the external host:/name format, the original internal
178 * /nfs/host/name format, and the internal /nfs/host/new_name describing
179 * the temporary location of the file. The `problem' flag is used to
180 * indicate if a problem occurred when the file was physically being
192 /* callback data MoveToTrash */
203 Tt_message *msg_list ;
205 #endif /* SUN_PERF */
212 Tt_message *msg_list ;
215 #endif /* SUN_PERF */
217 /* callback data RestoreFromTrash */
227 } RestoreFromTrashCBData;
229 /* callback data EmptyTrash */
238 DeleteList *del_list;
247 DialogData *trashDialogData; /* fm data associated with trash dialog */
248 FileMgrData *trashFileMgrData = NULL;
250 Boolean removingTrash = False;
252 DialogData * primaryTrashHelpDialog = NULL;
253 DialogData ** secondaryTrashHelpDialogList = NULL;
254 int secondaryTrashHelpDialogCount = 0;
256 /* Forward prototype */
257 int EraseDir(char *dir_name);
258 /* From dtcopy/fsrtns.c */
259 extern int EmptyDir(char *sourceP, int rm, int force);
261 /* 'defines' for trash files */
262 static char * TRASH_DIR = ".dt/Trash";
263 static char * TRASH_INFO_FILE = ".dt/Trash/.trashinfo";
264 static char * NEW_TRASH_INFO_FILE = ".dt/.tmptrashinfo";
266 static char * RM = "/bin/rm";
267 static char * RM_ARGS = "-rf";
269 static Widget * selectAllBtn = NULL;
270 static Widget * restoreBtn = NULL;
271 static Widget * removeBtn = NULL;
273 static int trashMenuItemCount = 22; /* trash menu items */
274 static MenuDesc * trashMenu = NULL;
276 static TrashEntry * trashCan = NULL;
277 static int trashListSize = 0;
278 static int numTrashItems = 0;
280 static Boolean trashDialogPosted;
282 static char * TrashInfoFileName = NULL;
283 static char * NewTrashInfoFileName = NULL;
285 static char ** sacred_dir_list = NULL; /* list of directories in trash path */
286 static int sacred_dir_list_size = 0;
287 static int sacred_dir_count = 0;
289 static Boolean verifyPromptsEnabled; /* do we prompt the user? */
291 static Tt_message global;
293 static Tt_message *global_msg_list ;
294 static int global_msg_cnt = 0 ;
295 #endif /* SUN_PERF */
296 static Boolean TrashInitialized = False;
298 /******** Static Function Declarations ********/
300 static String CreateTrashFilename(
303 static void MessageToFileList(
308 static void MessagesToFileList(
309 Tt_message *msg_list,
313 #endif /* SUN_PERF */
314 static int WriteEntry(
318 static Boolean MatchesSacredDirectory(
320 static void VerifyCleanup(
322 Boolean completeDelete) ;
323 static void VerifyCancel(
325 XtPointer client_data,
326 XtPointer call_data) ;
327 static void VerifyOk(
329 XtPointer client_data,
330 XtPointer call_data) ;
331 static Boolean WriteTrashEntries( void ) ;
332 static void Select_All(
334 XtPointer client_data,
335 XtPointer call_data) ;
336 static void Unselect_All(
338 XtPointer client_data,
339 XtPointer call_data) ;
342 XtPointer client_data,
343 XtPointer call_data) ;
344 static String GetBasePath(
346 static Boolean ReadTrashList( void ) ;
347 static void RemoveOkCB(
349 XtPointer client_data,
350 XtPointer call_data ) ;
351 static void RemoveCancelCB(
353 XtPointer client_data,
354 XtPointer call_data ) ;
355 static void ConfirmOk(
357 XtPointer client_data,
358 XtPointer call_data ) ;
359 static void ConfirmCancel(
361 XtPointer client_data,
362 XtPointer call_data ) ;
365 XtPointer clientData,
366 XtPointer callData) ;
367 static void AddToDeleteList(
368 DeleteList *deleteList,
371 static void MoveToTrash(
374 Boolean do_verify_checks,
376 static void RestoreFromTrash(
382 Boolean CheckedAlready) ;
383 static void EmptyTrash(
384 DeleteList *del_list,
388 static int CheckDeletePermissionRecur(
390 static void RestoreVerifyOk(
392 XtPointer client_data,
393 XtPointer call_data ) ;
394 static void RestoreVerifyCancel(
396 XtPointer client_data,
397 XtPointer call_data ) ;
398 static int RestoreObject(
404 void (*errorHandler)(),
405 Boolean checkForBusyDir,
407 Boolean CheckedAlready);
408 static void CreateRestoreDialog(
412 /******** End Static Function Declarations ********/
415 TrashIsInitialized( void )
417 return( TrashInitialized );
421 InitializeTrash( Boolean enableVerifyPrompt )
424 struct stat statInfo;
426 /* Build the 'TRASH' directory */
427 trash_dir = XtMalloc(strlen(users_home_dir) +
430 sprintf(trash_dir, "%s%s", users_home_dir, TRASH_DIR);
431 if (stat(trash_dir, &statInfo) < 0)
432 mkdir(trash_dir, S_IRUSR | S_IWUSR | S_IXUSR |
433 S_IRGRP | S_IWGRP | S_IXGRP |
434 S_IROTH | S_IWOTH | S_IXOTH);
436 /* build path to .trashinfo file */
437 TrashInfoFileName = XtMalloc(strlen(users_home_dir) + strlen(TRASH_INFO_FILE) + 1);
438 sprintf(TrashInfoFileName, "%s%s", users_home_dir, TRASH_INFO_FILE);
441 NewTrashInfoFileName = XtMalloc(strlen(users_home_dir) +
442 strlen(NEW_TRASH_INFO_FILE)
444 sprintf(NewTrashInfoFileName, "%s%s", users_home_dir, NEW_TRASH_INFO_FILE);
446 /* Keep track of whether to prompt for user verification */
447 verifyPromptsEnabled = enableVerifyPrompt;
450 * Create an array of paths and filenames which we will not allow the
451 * user to delete, because deleting any of them will cause trash to
454 sacred_dir_list_size = 5;
455 sacred_dir_list = (char **)XtMalloc(sizeof(char *) * sacred_dir_list_size);
457 sacred_dir_list[sacred_dir_count++] = XtNewString("/");
458 sacred_dir_list[sacred_dir_count++] = XtNewString(TrashInfoFileName);
460 ptr = TrashInfoFileName + 1;
461 while(ptr = DtStrchr(ptr, '/'))
463 /* All parent components of the user's home dir cannot be deleted */
465 if (sacred_dir_count >= sacred_dir_list_size)
468 sacred_dir_list_size += 5;
469 sacred_dir_list = (char **)XtRealloc((char *)sacred_dir_list,
470 sizeof(char *) * sacred_dir_list_size);
472 sacred_dir_list[sacred_dir_count++] = XtNewString(TrashInfoFileName);
477 /* load and verify existence for files previously left in the trash can */
478 TrashInitialized = ReadTrashList( );
479 return( TrashInitialized );
482 /************************************************************************
485 * This function must be called before any other interatctions with the
486 * trash can. It creates the trash dialog, opens the trash file, and
487 * loads information about any files previously left in the trash can.
489 * This function is called from main().
491 ************************************************************************/
499 DialogData * dialog_data;
500 FileMgrData * file_mgr_data;
502 if( ! TrashInitialized )
505 /* Create the trash dialog window */
506 /* load trash title */
507 tmpStr = GETMESSAGE(27, 9, "Trash Can");
508 title = XtNewString(tmpStr);
510 /* Initially, no items can be selected */
511 if (removeBtn != NULL)
512 XtSetSensitive(*removeBtn, False);
513 if (restoreBtn != NULL)
514 XtSetSensitive(*restoreBtn, False);
516 /* retrieve the default data for a file manager dialog -- */
517 /* the TrashView flag affects this data */
519 dialog_data = _DtGetDefaultDialogData (file_mgr_dialog);
520 file_mgr_data = (FileMgrData *) dialog_data->data;
521 trashFileMgrData = file_mgr_data;
522 trashDialogData = dialog_data;
524 /* build up directory set for trash directory */
525 FileMgrBuildDirectories (file_mgr_data, home_host_name, trash_dir);
527 /* initialize trash data */
528 file_mgr_data->restricted_directory =
529 XtNewString(file_mgr_data->current_directory);
530 file_mgr_data->title = XtNewString(title);
531 file_mgr_data->toolbox = False;
532 file_mgr_data->width = 300;
533 file_mgr_data->height = 300;
535 /* load any positioning information */
536 LoadPositionInfo(file_mgr_data);
538 /* build the trash dialog */
539 _DtBuildDialog (NULL, NULL, NULL, dialog_data, NULL, NULL, NULL,
540 NULL, NULL, False, special_view, title, NULL);
548 /************************************************************************
551 * At startup time, we need to read in the trash information file, to
552 * find out what, if any, files were left in the trash the last time
553 * this client was run. Each entry must be read in and then verified
554 * that it still exists; if it no longer exists, then someone has been
555 * mucking with our trash files, so we will remove it from our knowledge
556 * base. Otherwise, the file is added to the trash list.
558 ************************************************************************/
561 ReadTrashList( void )
563 int intSize, extSize, bufSize;
564 FILE * trashInfoFileId;
565 String external, intName, internal;
569 trashInfoFileId = fopen(TrashInfoFileName, "a+");
570 if (trashInfoFileId == 0)
574 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
576 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.") )
577 + strlen( TrashInfoFileName )
580 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 );
582 _DtMessage(toplevel, title, msg, NULL, HelpRequestCB);
588 chmod(TrashInfoFileName, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
590 bufSize = MAX_PATH * 2;
591 trashEntry = (char *)XtCalloc( 1, bufSize );
593 /* Extract the size of the two path strings */
594 while( fgets( trashEntry, bufSize, trashInfoFileId ) != NULL )
596 int len = strlen( trashEntry );
598 if( sscanf( trashEntry, "%d %d", &extSize, &intSize ) == 2 )
600 external = (char *)XtCalloc( 1, extSize + 1 );
601 intName = (char *)XtCalloc( 1, intSize + 1 );
603 if( len > extSize + intSize + 3 )
604 /* extSize + intSize + 3 is a rough estimate of a trash entry
605 a trash entry looks something like the following:
609 extSize is the size of the string '/tmp/abc'
610 intSize is the size of the string 'abcd'
612 so the len (the string length of the trash entry has AT LEAST to be
613 bigger than extSize + intSize + 3 separator spaces
618 /* sscanf can be used to reduce the code.
619 I'm not using it here because I don't have time to research.
620 I just want it to work.
622 tmpPtr = DtStrchr( trashEntry, ' ' ) + 1;
623 tmpPtr = DtStrchr( tmpPtr, ' ' ) + 1;
625 /* Extract the full external and partial internal file names */
626 memcpy( external, tmpPtr, extSize );
627 memcpy( intName, tmpPtr + extSize + 1, intSize );
629 /* Create internal/trash name */
630 internal = (char *)XtMalloc(strlen(users_home_dir)
632 + strlen(intName) + 2);
633 sprintf(internal, "%s%s/%s", users_home_dir, TRASH_DIR, intName);
635 /* Make sure the file still exists */
636 if (lstat(internal, &statbuf) < 0)
638 /* File no longer exists; ignore it */
645 /* Add to trash list */
646 if (numTrashItems >= trashListSize)
649 trashCan = (TrashEntry *)XtRealloc((char *) trashCan,
650 sizeof(TrashEntry) * trashListSize);
653 trashCan[numTrashItems].intNew = internal;
654 trashCan[numTrashItems].intOrig = XtNewString(external);
655 trashCan[numTrashItems].external = external;
656 trashCan[numTrashItems].filename = intName;
658 } /* end if file exists */
667 fclose(trashInfoFileId);
669 return( WriteTrashEntries() );
674 /************************************************************************
677 * This function is responsible for creating a new copy of the trash
678 * information file. It will open a new, temporary copy, and will
679 * copy in the contents of the trash can. It will then remove the
680 * old trash information file, and will rename the new one, opening
681 * a handle to it for later use.
683 ************************************************************************/
686 WriteTrashEntries( void )
688 static String path = NULL;
692 newFile = fopen(NewTrashInfoFileName, "w+");
695 chmod(NewTrashInfoFileName,
696 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
698 /* Write all remaining entries */
699 for (i = 0; i < numTrashItems; i++)
701 if( WriteEntry(newFile, trashCan[i].external, trashCan[i].filename) < 0 )
704 remove(NewTrashInfoFileName);
709 /* Remove the original information file, and move the new one */
710 (void) fclose(newFile);
711 if(-1 == chown(NewTrashInfoFileName, getuid(), getgid())) {
714 (void) remove(TrashInfoFileName);
715 (void) rename(NewTrashInfoFileName, TrashInfoFileName);
716 if(-1 == chown(TrashInfoFileName, getuid(), getgid())) {
727 /************************************************************************
730 * This function writes an entry to the trash information file.
731 * It expects the full path name for the original file [external]
732 * and only the new file name (without path information) [internal].
733 * If the file ends in '/', then it adds a '.' to the end; this is
734 * a special case, so that when we parse this information later, we
735 * are guaranteed to have a directory and a file name.
737 ************************************************************************/
745 return( fprintf(id, "%ld %ld %s %s\n",
746 (long)strlen(external), (long)strlen(internal),
747 external, internal) );
752 /************************************************************************
756 ************************************************************************/
761 FileMgrRec *file_mgr_rec )
766 static Widget * directoryBarBtn;
768 /* Create the menubar hierarchy */
769 trashMenu = (MenuDesc *)XtMalloc(sizeof(MenuDesc) * trashMenuItemCount);
772 for (i = 0; i < trashMenuItemCount; i++)
774 trashMenu[i].helpCallback = TrashHelpRequestCB;
775 trashMenu[i].helpData = NULL;
776 trashMenu[i].activateCallback = NULL;
777 trashMenu[i].activateData = NULL;
778 trashMenu[i].maskBit = 0;
779 trashMenu[i].isHelpBtn = False;
780 trashMenu[i].label = NULL;
781 trashMenu[i].mnemonic = NULL;
785 /*************************************/
786 /* Create the 'File' menu components */
787 /*************************************/
789 directoryBarBtn = &(trashMenu[j].widget);
790 trashMenu[j].type = MENU_PULLDOWN_BUTTON;
791 trashMenu[j].label = GETMESSAGE(20,1, "File");
792 trashMenu[j].label = XtNewString(trashMenu[j].label);
793 trashMenu[j].mnemonic = GETMESSAGE(20,2, "F");
794 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
795 trashMenu[j].name = "file_trash";
796 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
798 trashMenu[j].type = SHARED_MENU_PANE;
799 trashMenu[j].name = "fileMenuTrash";
800 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
802 selectAllBtn = &(trashMenu[j].widget);
803 trashMenu[j].type = MENU_BUTTON;
804 trashMenu[j].label = GETMESSAGE(20,48, "Select All");
805 trashMenu[j].label = XtNewString(trashMenu[j].label);
806 trashMenu[j].mnemonic = GETMESSAGE(20,51, "S");
807 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
808 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
809 trashMenu[j].name = "selectAll";
810 trashMenu[j++].activateCallback = Select_All;
812 trashMenu[j].type = MENU_BUTTON;
813 trashMenu[j].label = GETMESSAGE(20,52, "Deselect All");
814 trashMenu[j].label = XtNewString(trashMenu[j].label);
815 trashMenu[j].mnemonic = GETMESSAGE(20,55, "D");
816 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
817 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
818 trashMenu[j].name = "deselectAll";
819 trashMenu[j++].activateCallback = Unselect_All;
821 trashMenu[j].type = MENU_SEPARATOR;
822 trashMenu[j].name = "separator";
823 trashMenu[j++].helpCallback = NULL;
825 restoreBtn = &(trashMenu[j].widget);
826 trashMenu[j].type = MENU_BUTTON;
827 trashMenu[j].label = GETMESSAGE(27,24, "Put back");
828 trashMenu[j].label = XtNewString(trashMenu[j].label);
829 trashMenu[j].mnemonic = GETMESSAGE(27,26, "P");
830 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
831 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
832 trashMenu[j].name = "putBack";
833 trashMenu[j++].activateCallback = Restore;
835 removeBtn = &(trashMenu[j].widget);
836 trashMenu[j].type = MENU_BUTTON;
837 trashMenu[j].label = GETMESSAGE(27,28, "Shred");
838 trashMenu[j].label = XtNewString(trashMenu[j].label);
839 trashMenu[j].mnemonic = GETMESSAGE(27,30, "h");
840 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
841 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
842 trashMenu[j].name = "shred";
843 trashMenu[j++].activateCallback = ConfirmRemove;
845 trashMenu[j].type = MENU_SEPARATOR;
846 trashMenu[j].name = "separator";
847 trashMenu[j++].helpCallback = NULL;
849 trashMenu[j].type = MENU_BUTTON;
850 trashMenu[j].maskBit = PREFERENCES;
851 trashMenu[j].label = GETMESSAGE(20,141, "Set View Options ...");
852 trashMenu[j].label = XtNewString(trashMenu[j].label);
853 trashMenu[j].mnemonic = GETMESSAGE(20,6, "V");
854 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
855 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
856 trashMenu[j].name = "setPreferences";
857 trashMenu[j++].activateCallback = ShowPreferencesDialog;
859 trashMenu[j].type = MENU_SEPARATOR;
860 trashMenu[j].name = "separator";
861 trashMenu[j++].helpCallback = NULL;
863 trashMenu[j].type = MENU_BUTTON;
864 trashMenu[j].label = GETMESSAGE(20,117, "Close");
865 trashMenu[j].label = XtNewString(trashMenu[j].label);
866 trashMenu[j].mnemonic = GETMESSAGE(20,118, "C");
867 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
868 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
869 trashMenu[j].name = "close";
870 trashMenu[j++].activateCallback = CloseTrash;
873 /*************************************/
874 /* Create the 'Help' menu components */
875 /*************************************/
877 trashMenu[j].type = MENU_PULLDOWN_BUTTON;
878 trashMenu[j].label = GETMESSAGE(20,123, "Help");
879 trashMenu[j].label = XtNewString(trashMenu[j].label);
880 trashMenu[j].mnemonic = GETMESSAGE(20,9, "H");
881 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
882 trashMenu[j].isHelpBtn = True;
883 trashMenu[j].name = "help_trash";
884 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
886 trashMenu[j].type = SHARED_MENU_PANE;
887 trashMenu[j].name = "help_pane_trash";
888 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
890 trashMenu[j].type = MENU_BUTTON;
891 trashMenu[j].label = GETMESSAGE(20,105, "Overview");
892 trashMenu[j].label = XtNewString(trashMenu[j].label);
893 trashMenu[j].mnemonic = GETMESSAGE(20,106, "v");
894 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
895 trashMenu[j].helpData = HELP_HELP_MENU_STR;
896 trashMenu[j].activateData = HELP_TRASH_OVERVIEW_TOPIC_STR;
897 trashMenu[j].name = "introduction";
898 trashMenu[j++].activateCallback = TrashHelpRequestCB;
900 trashMenu[j].type = MENU_SEPARATOR;
901 trashMenu[j].name = "separator";
902 trashMenu[j++].helpCallback = NULL;
904 trashMenu[j].type = MENU_BUTTON;
905 trashMenu[j].label = GETMESSAGE(20,107, "Tasks");
906 trashMenu[j].label = XtNewString(trashMenu[j].label);
907 trashMenu[j].mnemonic = GETMESSAGE(20,108, "T");
908 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
909 trashMenu[j].helpData = HELP_HELP_MENU_STR;
910 trashMenu[j].activateData = HELP_TRASH_TASKS_TOPIC_STR;
911 trashMenu[j].name = "tasks";
912 trashMenu[j++].activateCallback = TrashHelpRequestCB;
914 trashMenu[j].type = MENU_BUTTON;
915 trashMenu[j].label = GETMESSAGE(20,109, "Reference");
916 trashMenu[j].label = XtNewString(trashMenu[j].label);
917 trashMenu[j].mnemonic = GETMESSAGE(20,110, "R");
918 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
919 trashMenu[j].helpData = HELP_HELP_MENU_STR;
920 trashMenu[j].activateData = HELP_TRASH_DIALOG_STR;
921 trashMenu[j].name = "reference";
922 trashMenu[j++].activateCallback = TrashHelpRequestCB;
924 trashMenu[j].type = MENU_BUTTON;
925 trashMenu[j].label = GETMESSAGE(20,111, "On Item");
926 trashMenu[j].label = XtNewString(trashMenu[j].label);
927 trashMenu[j].mnemonic = GETMESSAGE(20,112, "O");
928 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
929 trashMenu[j].helpData = HELP_HELP_MENU_STR;
930 trashMenu[j].activateData = HELP_HELP_MODE_STR;
931 trashMenu[j].name = "onItem";
932 trashMenu[j++].activateCallback = TrashHelpRequestCB;
934 trashMenu[j].type = MENU_SEPARATOR;
935 trashMenu[j].name = "separator";
936 trashMenu[j++].helpCallback = NULL;
938 usingHelpTrash = &(trashMenu[j].widget);
939 trashMenu[j].type = MENU_BUTTON;
940 trashMenu[j].label = GETMESSAGE(20,113, "Using Help");
941 trashMenu[j].label = XtNewString(trashMenu[j].label);
942 trashMenu[j].mnemonic = GETMESSAGE(20,114, "U");
943 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
944 trashMenu[j].helpData = HELP_HELP_MENU_STR;
945 trashMenu[j].activateData = HELP_HOME_TOPIC;
946 trashMenu[j].name = "usingHelp";
947 trashMenu[j++].activateCallback = TrashHelpRequestCB;
949 trashMenu[j].type = MENU_SEPARATOR;
950 trashMenu[j].name = "separator";
951 trashMenu[j++].helpCallback = NULL;
953 trashMenu[j].type = MENU_BUTTON;
954 trashMenu[j].label = GETMESSAGE(27,106, "About Trash Can");
955 trashMenu[j].label = XtNewString(trashMenu[j].label);
956 trashMenu[j].mnemonic = GETMESSAGE(20,116, "A");
957 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
958 trashMenu[j].helpData = HELP_HELP_MENU_STR;
959 trashMenu[j].activateData = HELP_ABOUT_STR;
960 trashMenu[j].name = "version";
961 trashMenu[j++].activateCallback = TrashHelpRequestCB;
963 menu_bar = _DtCreateMenuSystem(mainw, "menu_bar_trash", TrashHelpRequestCB,
964 HELP_TRASH_DIALOG_STR, True,
965 trashMenu, trashMenuItemCount, NULL,
968 /* Fine tune the menubar */
969 XtSetArg(args[0], XmNmarginWidth, 2);
970 XtSetArg(args[1], XmNmarginHeight, 2);
971 XtSetValues(menu_bar, args, 2);
973 file_mgr_rec->actions = NULL;
974 file_mgr_rec->action_pane = NULL;
975 file_mgr_rec->directoryBarBtn = *directoryBarBtn;
982 /************************************************************************
984 * TrashDisplayHandler
985 * This is the ICCCM message handler for the message used to display
986 * the trash can window. If the window is already displayed, then
987 * this call is a no-op.
989 * This handler is triggered by the front panel trash icon and by the
990 * File pulldown 'Show Trash' option.
992 ************************************************************************/
1002 tt_message_reply( msg );
1003 tttk_message_destroy( msg );
1006 if (!TrashInitialized)
1008 char *tmpStr, *tmpTitle, *tmpMsg;
1010 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1011 tmpTitle = XtNewString(tmpStr);
1012 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.");
1013 tmpMsg = XtNewString(tmpStr);
1015 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1022 #ifdef DEFER_TRASH_CREATION
1023 if( trashFileMgrData == NULL )
1024 TrashCreateDialog (XtDisplay(toplevel));
1027 /* the encapsulation functions do not set file_mgr_rec until a dialog */
1028 /* is actually displayed */
1029 if (trashFileMgrData->file_mgr_rec == 0)
1031 static Pixmap trash_icon = XmUNSPECIFIED_PIXMAP;
1032 static Pixmap trash_mask = XmUNSPECIFIED_PIXMAP;
1034 Pixel background, foreground, top_shadow, bottom_shadow, select;
1036 XClassHint classHints;
1037 FileMgrRec * file_mgr_rec;
1039 unsigned int height;
1043 classHints.res_name = trashFileMgrData->title;
1044 classHints.res_class = DTFILE_CLASS_NAME;
1047 _DtShowBuiltDialog(NULL,NULL, trashDialogData, NULL, False, &classHints);
1050 file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
1052 trashShell = file_mgr_rec->shell;
1054 if (trash_icon == XmUNSPECIFIED_PIXMAP)
1056 XtSetArg (args[0], XmNbackground, &background);
1057 XtSetArg (args[1], XmNcolormap, &colormap);
1058 XtGetValues (file_mgr_rec->main, args, 2);
1060 XmGetColors (XtScreen (file_mgr_rec->main), colormap, background,
1061 &foreground, &top_shadow, &bottom_shadow, &select);
1063 pixmap = XmGetPixmap (XtScreen (file_mgr_rec->main), trashIcon,
1064 foreground, background);
1065 if( pixmap != XmUNSPECIFIED_PIXMAP)
1066 trash_icon = pixmap;
1068 /* now let's get the mask for the File Manager */
1069 pixmap = _DtGetMask (XtScreen (file_mgr_rec->main), trashIcon);
1070 if( pixmap != XmUNSPECIFIED_PIXMAP)
1071 trash_mask = pixmap;
1073 if (trash_icon != XmUNSPECIFIED_PIXMAP)
1075 XtSetArg (args[0], XmNiconPixmap, trash_icon);
1076 if(trash_mask != XmUNSPECIFIED_PIXMAP)
1078 XtSetArg (args[1], XmNiconMask, trash_mask);
1079 XtSetValues (trashShell, args, 2);
1082 XtSetValues (trashShell, args, 1);
1090 root = RootWindowOfScreen (XtScreen (trashShell));
1092 /* Change the hints to reflect the current workspace */
1093 /* and raise the window */
1095 if (DtWsmGetCurrentWorkspace (XtDisplay (trashShell),
1096 root, ¤t_ws) == Success)
1097 DtWsmSetWorkspacesOccupied (XtDisplay (trashShell),
1098 XtWindow (trashShell), ¤t_ws, 1);
1100 /* Set the iconify state */
1101 /* Remove the iconify hint from the current shell */
1102 wmhints = XGetWMHints(XtDisplay(trashShell), XtWindow(trashShell));
1103 wmhints->flags |= IconWindowHint;
1104 wmhints->initial_state = NormalState;
1105 XSetWMHints(XtDisplay(trashShell), XtWindow(trashShell), wmhints);
1109 XtPopup (trashShell, XtGrabNone);
1110 XSync(XtDisplay(trashShell), False);
1111 XRaiseWindow (XtDisplay (trashShell), XtWindow (trashShell));
1112 XMapWindow( XtDisplay (trashShell), XtWindow (trashShell) );
1115 msg = tt_pnotice_create(TT_SESSION, "DtActivity_Began");
1116 tt_message_send(msg);
1117 tttk_message_destroy(msg);
1121 trashDialogPosted = True;
1125 /************************************************************************
1127 * ConfirmEmptyCancel
1129 ************************************************************************/
1130 static XtPointer trash_popup_client_data;
1134 XtPointer client_data,
1135 XtPointer call_data )
1137 XtUnmanageChild((Widget)client_data);
1138 XmUpdateDisplay((Widget)client_data);
1139 XtDestroyWidget((Widget)client_data);
1142 /************************************************************************
1146 ************************************************************************/
1151 XtPointer client_data,
1152 XtPointer call_data )
1154 /* destroy dialog */
1155 XtUnmanageChild((Widget)client_data);
1156 XmUpdateDisplay((Widget)client_data);
1157 XtDestroyWidget((Widget)client_data);
1162 /************************************************************************
1165 * This is a message interface used to really remove all of the
1168 * This handler is only called from an empty trash action.
1170 ************************************************************************/
1177 tt_message_reply( msg );
1178 tttk_message_destroy( msg );
1181 if(!TrashInitialized)
1183 char *tmpStr, *tmpTitle, *tmpMsg;
1185 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1186 tmpTitle = XtNewString(tmpStr);
1187 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.");
1188 tmpMsg = XtNewString(tmpStr);
1190 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1198 char *tmpStr, *title, *msg;
1200 tmpStr = GETMESSAGE(27,73, "Shred File(s)");
1201 title = XtNewString(tmpStr);
1202 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");
1203 msg = XtNewString(tmpStr);
1205 _DtMessageDialog(toplevel, title, msg, NULL, TRUE, ConfirmEmptyCancel,
1206 ConfirmEmptyOk, NULL, HelpRequestCB, False, QUESTION_DIALOG);
1215 /************************************************************************
1218 * This is a functional interface used to really remove all of the
1221 ************************************************************************/
1226 if (trashFileMgrData)
1229 Select_All(*selectAllBtn, NULL, NULL);
1230 Remove(*removeBtn, NULL, NULL);
1232 EmptyDir(trashFileMgrData->current_directory,0,0);
1238 /************************************************************************
1241 * This is the callback attached to the 'Select All' menu item. It
1242 * will select all of the files currently resident in the trash can.
1243 * It will also sensitize the 'Restore' and 'Remove' buttons, as long
1244 * as a remove request is not currently in the middle of being processed.
1246 ************************************************************************/
1251 XtPointer client_data,
1252 XtPointer call_data )
1255 SelectAllFiles(trashFileMgrData);
1256 SensitizeTrashBtns();
1261 /************************************************************************
1264 * This is the callback attached to the 'Unselect All' menu item. It
1265 * will unselect all of the items in the trash can, and will clear
1266 * out our selection array. It also desensitizes the 'Restore' and
1269 ************************************************************************/
1274 XtPointer client_data,
1275 XtPointer call_data )
1278 DeselectAllFiles(trashFileMgrData);
1279 SensitizeTrashBtns();
1284 /************************************************************************
1286 * SensitizeTrashBtns
1288 ************************************************************************/
1291 SensitizeTrashBtns(void)
1293 #ifdef DEFER_TRASH_CREATION
1294 if( trashFileMgrData
1299 if (trashFileMgrData->selected_file_count > 0)
1301 XtSetSensitive(*removeBtn, True);
1302 XtSetSensitive(*restoreBtn, True);
1306 XtSetSensitive(*removeBtn, False);
1307 XtSetSensitive(*restoreBtn, False);
1314 /************************************************************************
1316 * TrashRemoveHandler
1317 * This is the message handler for the message requesting that
1318 * a set of files be added to the trashcan. Each file name comes in
1319 * in /dir/name format; they are all in a single string, separated
1322 * Check for spaces in filenames and destop files. If we pass these
1323 * cases, call TrashRemover where the following occurs:
1325 * All of the files will be moved to the trash can directory; any
1326 * problems encountered will be reported in an error dialog. Each file
1327 * which is successfully moved, will be added to the trash information file.
1329 ************************************************************************/
1342 int msglistsize = 0 ;
1343 #endif /* SUN_PERF */
1346 * Note: There used to be a call to tt_message_reply() right here at the
1347 * beginning of the routine. However, the mailer wants to trash files
1348 * but wants the notification after the trash has been removed.
1349 * Therefore, we now put this off until the work is done or an error
1353 if( !TrashInitialized )
1355 char *tmpStr, *tmpTitle, *tmpMsg;
1357 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1358 tmpTitle = XtNewString(tmpStr);
1359 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.");
1360 tmpMsg = XtNewString(tmpStr);
1362 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1367 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
1369 global_msg_cnt = 0 ;
1370 #endif /* SUN_PERF */
1374 MessageToFileList(msg, &file_list, &file_count);
1377 * When the Dttrash_Remove handler "TrashRemoveHandler" is invoked for
1378 * the first time it caches the TT request and then invokes
1379 * tttk_block_while() with timeout = 0. This in effect causes the next
1380 * TT request to be processed before tttk_block_while() returns. Now if
1381 * TT msgs are coming in faster than dtfile is handling them, then the
1382 * next TT msg. should be another Dttrash_Remove msg. which will cause
1383 * re-entrance into TrashRemoveHandler. This will continue until the last
1384 * Dttrash_Remove msg. has been processed (and the recursive
1385 * TrashRemoveHandler starts unwinding) after which we check if the
1386 * number of TT msgs are > 0 in which case we fire off the
1387 * process to move the list of files to Trash.
1390 msglistsize = sizeof(Tt_message) * global_msg_cnt ;
1391 global_msg_list = (Tt_message *)XtRealloc((char *)global_msg_list, msglistsize);
1392 memcpy((char *)&(global_msg_list[global_msg_cnt - 1]),
1393 (char *)&msg, sizeof(Tt_message)) ;
1394 tttk_block_while(0, 0, time_out) ;
1396 /* If we get to this pt. then either :
1397 * 1. No more Dttrash_Remove msgs. left to process. Thus we should
1398 * process the TT msg list and move files to Trash.
1400 * 2. We got a msg. different from Dttrash_Remove. In this case
1401 * we process the msgs. we have so far. The remaining files to
1402 * be trashed will be buffered in another list.
1405 if (global_msg_cnt > 0) {
1406 MessagesToFileList(global_msg_list, global_msg_cnt,
1407 &file_list, &file_count);
1408 #endif /* SUN_PERF */
1410 if (file_count == 0)
1412 tt_message_reply( msg );
1413 tttk_message_destroy( msg );
1415 global_msg_cnt = 0 ;
1416 #endif /* SUN_PERF */
1420 /* return if from the trash dir */
1421 if (strncmp(file_list[0], trash_dir, strlen(trash_dir)) == 0)
1425 for (i = 0; i < file_count; i++) {
1426 XtFree( file_list[i] );
1428 XtFree( (char *)file_list );
1429 tt_message_status_set( msg, TT_DESKTOP_EALREADY );
1430 tt_message_reply( msg );
1431 tttk_message_destroy( msg );
1433 global_msg_cnt = 0 ;
1434 #endif /* SUN_PERF */
1438 /* post message if file(s) from desktop */
1442 str=(char *)IsAFileOnDesktop2(file_list, file_count, &number,&IsToolBox);
1443 /* IsToolBox is unused here, but is required to satisfy the prototype */
1447 MoveToTrash(file_list, file_count, verifyPromptsEnabled, msg);
1457 /* List will be regenerated by callbacks */
1458 for (i = 0; i < file_count; i++) {
1459 XtFree( file_list[i] );
1461 XtFree( (char *)file_list );
1463 tmpStr = (GETMESSAGE(27,73, "Shred File(s)"));
1464 title = XtNewString(tmpStr);
1468 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."));
1470 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."));
1471 message = (char *)XtMalloc(strlen(template) + strlen(str) + 1);
1472 sprintf(message, template, str);
1477 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."));
1479 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."));
1480 message = (char *)XtMalloc(strlen(template) + strlen(str) + 1);
1481 sprintf(message, template, str);
1484 /* Really ought to pass to OK and Cancel CBs via client_data */
1487 _DtMessageDialog(toplevel, title, message, NULL, TRUE,
1488 RemoveCancelCB, RemoveOkCB, NULL, HelpRequestCB, False,
1494 Tt_msg_cache *msg_cache ;
1496 msg_cache = (Tt_msg_cache *)XtMalloc(sizeof(Tt_msg_cache)) ;
1497 msg_cache->msg_cnt = global_msg_cnt ;
1499 msg_cache->msg_list = (Tt_message *)XtMalloc(sizeof(Tt_message) *
1500 msg_cache->msg_cnt );
1501 msg_cache->msg_list[msg_cache->msg_cnt] = NULL ;
1502 memcpy((char *)msg_cache->msg_list, (char *)global_msg_list,
1503 sizeof(Tt_message) * msg_cache->msg_cnt ) ;
1504 dlog = (Widget)_DtMessageDialog(toplevel, title, message, NULL, TRUE,
1505 RemoveCancelCB, RemoveOkCB, NULL, HelpRequestCB, False,
1507 XtSetArg ( args[0], XmNuserData, msg_cache );
1508 XtSetValues ( dlog, args, 1);
1510 #endif /* SUN_PERF */
1517 global_msg_cnt = 0 ;
1519 #endif /* SUN_PERF */
1524 /************************************************************************
1527 * Cleanup and unmanage the remove from DT dialog
1529 ************************************************************************/
1534 XtPointer client_data,
1535 XtPointer call_data )
1541 Tt_msg_cache *current_msg_cache ;
1544 XtSetArg( args[0], XmNuserData, ¤t_msg_cache );
1545 XtGetValues( (Widget)client_data, args, 1 );
1546 #endif /* SUN_PERF */
1548 /* destroy dialog */
1549 XtUnmanageChild((Widget)client_data);
1550 XmUpdateDisplay((Widget)client_data);
1551 XtDestroyWidget((Widget)client_data);
1555 MessageToFileList(global, &file_list, &file_count);
1557 MessagesToFileList( current_msg_cache->msg_list, current_msg_cache->msg_cnt,
1558 &file_list, &file_count);
1559 #endif /* SUN_PERF */
1561 MoveToTrash(file_list, file_count, verifyPromptsEnabled, NULL);
1563 /* reread desktop files */
1567 tt_message_reply( global );
1568 tttk_message_destroy( global );
1571 for (i = 0 ; i < current_msg_cache->msg_cnt ; i++) {
1572 tt_message_reply( current_msg_cache->msg_list[i] );
1573 tttk_message_destroy( current_msg_cache->msg_list[i] );
1575 XtFree ((char *)current_msg_cache->msg_list);
1576 XtFree ((char *)current_msg_cache);
1577 #endif /* SUN_PERF */
1582 /************************************************************************
1585 * Cleanup and unmanage the remove from DT dialog
1587 ************************************************************************/
1592 XtPointer client_data,
1593 XtPointer call_data )
1597 Tt_msg_cache *current_msg_cache ;
1600 XtSetArg( args[0], XmNuserData, ¤t_msg_cache );
1601 XtGetValues( (Widget)client_data, args, 1 );
1602 #endif /* SUN_PERF */
1604 /* destroy dialog */
1605 XtUnmanageChild((Widget)client_data);
1606 XmUpdateDisplay((Widget)client_data);
1607 XtDestroyWidget((Widget)client_data);
1610 tttk_message_fail( global, TT_DESKTOP_ECANCELED, 0, 1 );
1613 for (i = 0 ; i < current_msg_cache->msg_cnt ; i++)
1614 tttk_message_fail( current_msg_cache->msg_list[i], TT_DESKTOP_ECANCELED, 0, 1 );
1615 XtFree ((char *)current_msg_cache->msg_list);
1616 XtFree ((char *)current_msg_cache);
1617 #endif /* SUN_PERF */
1622 /************************************************************************
1624 * TrashRemoveNoConfirmHandler
1625 * This function is a message handler. It will place the specified set
1626 * of files into the trash can, without requesting confirmation if, for
1627 * instance, a file does not have write permission, or a directory is
1630 ************************************************************************/
1633 TrashRemoveNoConfirmHandler(
1639 if( !TrashInitialized )
1641 char *tmpStr, *tmpTitle, *tmpMsg;
1643 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1644 tmpTitle = XtNewString(tmpStr);
1645 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.");
1646 tmpMsg = XtNewString(tmpStr);
1648 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1654 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
1658 MessageToFileList(msg, &file_list, &file_count);
1659 MoveToTrash(file_list, file_count, False, msg);
1664 /************************************************************************
1668 * Parses a list of file names from a message
1669 * and returns an array of strings.
1671 * if (no message arguments) {
1672 * if (no file attribute) {
1673 * return length==0 list
1675 * return length==1 list containing file attribute
1678 * if (file attribute) {
1679 * file attribute is a directory, prepended onto subsequent filenames
1681 * if (arg 0 is non-null) {
1682 * arg 0 interpreted as a space-separated list of filenames
1685 * arg interpreted as a filename possibly containing spaces
1689 ************************************************************************/
1705 args = tt_message_args_count( msg );
1706 if (tt_is_err(tt_int_error( args ))) {
1710 dir = tt_message_file( msg );
1711 if ((tt_is_err(tt_ptr_error( dir ))) || (dir == 0)) {
1715 /* No args means use tt_message_file() as is */
1716 *file_list = (char **)
1717 XtRealloc((char *)(*file_list),
1718 (*file_count + 1)*sizeof(char *));
1719 (*file_list)[*file_count] = XtNewString(dir);
1724 if (dir[strlen(dir)] != '/') {
1726 if (strcmp(dir, "/") == 0)
1728 newdir = (char *)tt_malloc( strlen(dir)+1 );
1729 strcpy( newdir, dir );
1733 newdir = (char *)tt_malloc( strlen(dir)+2 );
1734 strcpy( newdir, dir );
1735 strcat( newdir, "/" );
1741 /* Invariant: dir can now be safely concatenated to form a valid path */
1742 dirlen = strlen(dir);
1743 for (arg = 0; arg < args; arg++) {
1748 val = tt_message_arg_val( msg, arg );
1749 if ((tt_is_err(tt_ptr_error( val ))) || (val == 0)) {
1755 /* Arg 0 is a space-separated list */
1758 for (val = DtStrtok(val, white); val; val = DtStrtok(0, white))
1760 if (strcmp(val, "/") == 0)
1762 file = (char *)XtMalloc( dirlen + 1 );
1763 strcpy( file, dir );
1767 file = (char *)XtMalloc( dirlen + strlen(val) + 1 );
1768 strcpy( file, dir );
1769 strcat( file, val );
1771 *file_list = (char **)
1772 XtRealloc((char *)(*file_list),
1773 (*file_count + 1)*sizeof(char *));
1774 (*file_list)[*file_count] = DtEliminateDots(file);
1777 tt_free( val2free );
1785 Tt_message *msg_list,
1800 for (num_msgs = 0 ; num_msgs < msg_cnt ; num_msgs++)
1802 msg = msg_list[num_msgs] ;
1804 args = tt_message_args_count( msg );
1805 if (tt_is_err(tt_int_error( args ))) {
1809 dir = tt_message_file( msg );
1810 if ((tt_is_err(tt_ptr_error( dir ))) || (dir == 0)) {
1814 /* No args means use tt_message_file() as is */
1815 *file_list = (char **)
1816 XtRealloc((char *)(*file_list),
1817 (*file_count + 1)*sizeof(char *));
1818 (*file_list)[*file_count] = XtNewString(dir);
1823 if (dir[strlen(dir)] != '/') {
1825 if (strcmp(dir, "/") == 0)
1827 newdir = (char *)tt_malloc( strlen(dir)+1 );
1828 strcpy( newdir, dir );
1832 newdir = (char *)tt_malloc( strlen(dir)+2 );
1833 strcpy( newdir, dir );
1834 strcat( newdir, "/" );
1840 /* Invariant: dir can now be safely concatenated to form a valid path */
1841 dirlen = strlen(dir);
1842 for (arg = 0; arg < args; arg++) {
1847 val = tt_message_arg_val( msg, arg );
1848 if ((tt_is_err(tt_ptr_error( val ))) || (val == 0)) {
1854 /* Arg 0 is a space-separated list */
1857 for (val = DtStrtok(val, white); val; val = DtStrtok(0, white))
1859 if (strcmp(val, "/") == 0)
1861 file = (char *)XtMalloc( dirlen + 1 );
1862 strcpy( file, dir );
1864 else if (strcmp(val, "/") == 0 && dirlen == 0)
1866 file = (char *)XtMalloc( strlen(val) + 1 );
1867 strcpy( file, val );
1871 file = (char *)XtMalloc( dirlen + strlen(val) + 1 );
1872 strcpy( file, dir );
1873 strcat( file, val );
1875 *file_list = (char **)
1876 XtRealloc((char *)(*file_list),
1877 (*file_count + 1)*sizeof(char *));
1878 (*file_list)[*file_count] = DtEliminateDots(file);
1881 tt_free( val2free );
1886 #endif /* SUN_PERF */
1889 /************************************************************************
1891 * CreateTrashFilename
1892 * Create trash directory name.
1894 ************************************************************************/
1897 CreateTrashFilename(
1899 Boolean uniqueTest )
1902 char * extension = NULL;
1903 struct stat statInfo;
1905 /* Create trash path */
1906 /* Give that name a little extra cushion, just in case */
1907 trashName = (char *)XtMalloc(strlen(users_home_dir) + strlen(TRASH_DIR) +
1908 strlen(baseName) + 15);
1909 sprintf(trashName, "%s%s/%s", users_home_dir, TRASH_DIR, baseName);
1911 /* Want to find the extension so the new file name created will preserve
1912 its original datatype.
1914 extension = strrchr( baseName, '.' );
1921 /* If a file by the trash name already exists, keep building new names */
1922 /* until one by that name doesn't exist and then use it */
1923 if (uniqueTest && (lstat(trashName, &statInfo) == 0))
1927 for (i = 1; True ; i++)
1929 /* Make a duplicate file name */
1931 sprintf(trashName, "%s%s/%s_%d.%s", users_home_dir, TRASH_DIR,
1932 baseName, i, extension);
1934 sprintf(trashName, "%s%s/%s_%d", users_home_dir, TRASH_DIR,
1937 /* Is the filename taken? */
1938 if (lstat(trashName, &statInfo) != 0)
1942 } /* end if not unique name */
1945 *(extension-1) = '.';
1953 /************************************************************************
1955 * MatchesSacredDirectory
1956 * Checks if the specify filename matches one of the sacred,
1957 * non-deleteable files.
1959 ************************************************************************/
1962 MatchesSacredDirectory(
1967 /* Don't allow the user to delete any of the sacred directories */
1968 for (i = 0; i < sacred_dir_count; i++)
1970 if (!strcmp(file, sacred_dir_list[i]))
1974 /* Compare against special desktop directories */
1975 /* remote_sys_dir is currently NULL */
1976 if (strcmp(file, remote_sys_dir) == 0)
1984 /************************************************************************
1987 * Callback for 'Delete To Trash' verify dialog Ok push button.
1989 ************************************************************************/
1994 XtPointer client_data,
1995 XtPointer call_data )
1997 VerifyCleanup ((Widget)client_data, True);
2002 /************************************************************************
2005 * Callback for 'Delete To Trash' verify dialog Cancel push button.
2007 ************************************************************************/
2012 XtPointer client_data,
2013 XtPointer call_data )
2016 VerifyCleanup ((Widget)client_data, False);
2021 /************************************************************************
2024 * Callback for 'Delete To Trash' verify dialog push buttons.
2026 * Called from VerifyOk and VerifyCancel.
2028 ************************************************************************/
2033 Boolean completeDelete )
2040 /* Unpost dialog, and retrieve the list of verified files */
2041 XtUnmanageChild(mbox);
2042 XmUpdateDisplay(mbox);
2043 XtSetArg(args[0], XmNuserData, &verifylist);
2044 XtGetValues(mbox, args, 1);
2046 /* count the number of files in the list */
2047 for (fileCount = 0; verifylist[fileCount]; fileCount++)
2051 * Start the background process that moves the files into the trash,
2055 MoveToTrash(verifylist, fileCount, False, NULL);
2059 /* Free up the storage we allocated */
2060 for (i = fileCount; i > 0; i--)
2061 XtFree(verifylist[i]);
2062 XtFree((char *)verifylist);
2065 XtDestroyWidget(mbox);
2070 /************************************************************************
2074 ************************************************************************/
2075 static XtPointer trash_popup_client_data;
2079 XtPointer client_data,
2080 XtPointer call_data )
2082 XtUnmanageChild((Widget)client_data);
2083 XmUpdateDisplay((Widget)client_data);
2084 XtDestroyWidget((Widget)client_data);
2086 XtSetSensitive(*removeBtn, True);
2087 XtSetSensitive(*restoreBtn, True);
2088 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
2089 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
2090 trash_popup_client_data = NULL;
2095 /************************************************************************
2099 ************************************************************************/
2104 XtPointer client_data,
2105 XtPointer call_data )
2107 FileViewData *file_view_data;
2109 /* destroy dialog */
2110 XtUnmanageChild((Widget)client_data);
2111 XmUpdateDisplay((Widget)client_data);
2112 XtDestroyWidget((Widget)client_data);
2114 file_view_data = trashFileMgrData->popup_menu_icon;
2115 if(!file_view_data && trashFileMgrData->selected_file_count)
2116 file_view_data = trashFileMgrData->selection_list[0];
2117 trashFileMgrData->popup_menu_icon = NULL;
2119 if(file_view_data != NULL)
2120 Remove(*removeBtn, file_view_data, NULL);
2125 /************************************************************************
2128 * This is the callback attached to the 'Remove' menu item.
2130 ************************************************************************/
2135 XtPointer client_data,
2136 XtPointer call_data )
2138 char *tmpStr, *title, *msg;
2140 /* Desensitize remove & restore buttons, until we're done */
2141 XtSetSensitive(*removeBtn, False);
2142 XtSetSensitive(*restoreBtn, False);
2143 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], False);
2144 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], False);
2147 trash_popup_client_data = client_data;
2149 tmpStr = GETMESSAGE(27,73, "Shred File(s)");
2150 title = XtNewString(tmpStr);
2151 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");
2152 msg = XtNewString(tmpStr);
2154 _DtMessageDialog(toplevel, title, msg, NULL, TRUE, ConfirmCancel, ConfirmOk,
2155 NULL, HelpRequestCB, False, QUESTION_DIALOG);
2163 /************************************************************************
2166 * While we wait for the background process to complete, we will
2167 * desensitize the 'Remove' and 'Restore' menu items, since we don't
2168 * handle multiple requests.
2170 ************************************************************************/
2175 XtPointer client_data,
2176 XtPointer call_data )
2178 FileViewData *file_view_data;
2179 Boolean match = False;
2180 DeleteList *deleteList = NULL;
2186 /* Remove may be called to remove a file or to remove a trash file; */
2187 /* if w is NULL, this is a call to remove a file */
2190 removeType = TRASH_FILE;
2192 /* Count number of items to 'Delete To Trash' */
2193 if (client_data != NULL)
2195 /* This was called by trash popup */
2196 /* need to check to see if it is selected, if it is restore all
2197 the selected files, else just restore this file */
2199 file_view_data = (FileViewData *)client_data;
2200 for (i = 0; i < trashFileMgrData->selected_file_count; i++)
2202 if (strcmp(file_view_data->file_data->file_name,
2203 trashFileMgrData->selection_list[i]->file_data->file_name) == 0)
2211 if (trashFileMgrData->selected_file_count == 0)
2213 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
2214 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
2221 /* Desensitize remove & restore buttons, until we're done */
2222 XtSetSensitive(*removeBtn, False);
2223 XtSetSensitive(*restoreBtn, False);
2227 deleteCount = trashFileMgrData->selected_file_count;
2231 /* Create the list of things being deleted */
2232 deleteList = (DeleteList *)XtMalloc(deleteCount * sizeof(DeleteList));
2236 for (i = 0; i < deleteCount; i++)
2238 AddToDeleteList(deleteList, i,
2239 trashFileMgrData->selection_list[i]->file_data->file_name);
2243 AddToDeleteList(deleteList, 0, file_view_data->file_data->file_name);
2248 removeType = REMOVE_FILE;
2249 deleteList = (DeleteList *)XtMalloc(sizeof(DeleteList));
2251 deleteList[0].trash = XtNewString(client_data);
2252 deleteList[0].orig = NULL;
2255 EmptyTrash(deleteList, deleteCount, removeType, NULL);
2260 /************************************************************************
2263 * Locate a file in the trash list and add it to the delete list.
2265 ************************************************************************/
2269 DeleteList *deleteList,
2275 /* Locate file in trash list, add entry to delete list */
2276 for (j = 0; j < numTrashItems; j++)
2278 if (strcmp(filename, trashCan[j].filename) == 0)
2280 /* file found in trash list */
2281 deleteList[i].trash = XtNewString(trashCan[j].intNew);
2282 deleteList[i].orig = XtNewString(trashCan[j].intOrig);
2288 /* file not found in trash list */
2289 deleteList[i].trash = CreateTrashFilename(filename, FALSE);
2290 deleteList[i].orig = NULL;
2295 /************************************************************************
2297 * TrashRestoreHandler
2298 * This function is a message handler. It will restore the specified set
2299 * of files from the trash can.
2301 ************************************************************************/
2304 TrashRestoreHandler(
2310 if( !TrashInitialized )
2312 char *tmpStr, *tmpTitle, *tmpMsg;
2314 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
2315 tmpTitle = XtNewString(tmpStr);
2316 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.");
2317 tmpMsg = XtNewString(tmpStr);
2319 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
2325 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
2330 /* Desensitize remove & restore buttons, until we're done */
2331 XtSetSensitive(*removeBtn, False);
2332 XtSetSensitive(*restoreBtn, False);
2334 MessageToFileList(msg, &file_list, &file_count);
2335 RestoreFromTrash(file_list, file_count, NULL, NULL, msg,False);
2340 /************************************************************************
2343 * This is the callback attached to the 'Restore' menu item. It will
2344 * remove from the trash all of the selected files, and will restore
2345 * them to their original location. If any problems occur while trying
2346 * to restore any of the files, then an error dialog will be posted.
2348 ************************************************************************/
2353 XtPointer client_data,
2354 XtPointer call_data )
2356 FileViewData *file_view_data;
2357 Boolean match = False;
2362 if (client_data != NULL)
2364 /* This was called by trash popup */
2365 /* need to check to see if it is selected, if it is restore all
2366 the selected files, else just restore this file */
2368 file_view_data = trashFileMgrData->popup_menu_icon;
2370 /* The object would have been deleted if the following condn is true */
2371 if(!file_view_data && !trashFileMgrData->selected_file_count)
2373 trashFileMgrData->popup_menu_icon = NULL;
2375 for (i = 0; i < trashFileMgrData->selected_file_count; i++)
2377 if (strcmp(file_view_data->file_data->file_name,
2378 trashFileMgrData->selection_list[i]->file_data->file_name) == 0)
2387 /* Desensitize remove & restore buttons, until we're done */
2388 XtSetSensitive(*removeBtn, False);
2389 XtSetSensitive(*restoreBtn, False);
2394 file_count = trashFileMgrData->selected_file_count;
2395 file_list = (char **)XtMalloc(file_count * sizeof(char *));
2396 for (i = 0; i < file_count; i++)
2398 file_view_data = trashFileMgrData->selection_list[file_count - 1 - i];
2399 file_list[i] = XtNewString(file_view_data->file_data->file_name);
2405 file_list = (char **)XtMalloc(sizeof(char *));
2406 file_list[0] = XtNewString(file_view_data->file_data->file_name);
2409 /* Start the background process that will do the restore */
2410 RestoreFromTrash(file_list, file_count, NULL, NULL, NULL,False);
2415 /************************************************************************
2418 * This is the callback attached to the 'Close' menu item. It will
2419 * unpost the trash can window.
2421 ************************************************************************/
2426 XtPointer client_data,
2427 XtPointer call_data )
2431 if (trashDialogPosted)
2433 XWithdrawWindow(XtDisplay(trashShell), XtWindow(trashShell),
2434 XDefaultScreen(XtDisplay(trashShell)));
2435 XtPopdown(trashShell);
2439 for (i = 0; i < secondaryTrashHelpDialogCount; i++)
2441 if (_DtIsDialogShowing(secondaryTrashHelpDialogList[i]))
2442 _DtHideDialog(secondaryTrashHelpDialogList[i], False);
2443 _DtFreeDialogData(secondaryTrashHelpDialogList[i]);
2445 XtFree((char *)secondaryTrashHelpDialogList);
2446 secondaryTrashHelpDialogList = NULL;
2447 secondaryTrashHelpDialogCount = 0;
2449 if (primaryTrashHelpDialog)
2451 if (_DtIsDialogShowing(primaryTrashHelpDialog))
2452 _DtHideDialog(primaryTrashHelpDialog, False);
2453 _DtFreeDialogData(primaryTrashHelpDialog);
2455 primaryTrashHelpDialog = NULL;
2457 if (PositionFlagSet(trashFileMgrData))
2458 SavePositionInfo(trashFileMgrData);
2461 FileMgrRec * file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
2463 if( (file_mgr_rec->menuStates & PREFERENCES) == 0 )
2464 file_mgr_rec->menuStates |= PREFERENCES;
2467 trashDialogPosted = False;
2472 /************************************************************************
2476 ************************************************************************/
2483 DtDndDropCallbackStruct *drop_parameters)
2486 DtActionArg * action_args;
2487 FileMgrRec * file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
2488 Position drop_x = drop_parameters->x;
2489 Position drop_y = drop_parameters->y;
2491 _DtBuildActionArgsWithDroppedFiles(NULL, drop_parameters,
2492 &action_args, &arg_count);
2494 DtActionInvoke(file_mgr_rec->shell, TRASH_ACTION,
2495 action_args, arg_count, NULL, NULL,
2496 trashFileMgrData->current_directory, True, NULL, NULL);
2498 _DtFreeActionArgs(action_args, arg_count);
2500 RepositionIcons(trashFileMgrData, file_set, file_count, drop_x,
2506 /************************************************************************
2510 ************************************************************************/
2514 FileMgrData *file_mgr_data,
2515 FileMgrRec *file_mgr_rec,
2529 * Get target host and directory
2531 target_dir = XtNewString(file_mgr_data->current_directory);
2532 target_host = XtNewString(file_mgr_data->host);
2535 * Create file list and call RestoreFromTrash
2537 file_list = (char **)XtMalloc(file_count * sizeof(char *));
2539 for (i = 0; i < file_count; i++)
2540 file_list[i] = XtNewString(file_set[i]);
2542 RestoreFromTrash(file_list, file_count, target_host, target_dir, NULL,False);
2547 /************************************************************************
2550 * Locate file in trash list based on new internal name. Return file name.
2552 ************************************************************************/
2558 Boolean IsTrash = False;
2560 if (strncmp(users_home_dir, filename, strlen(users_home_dir)) == 0)
2563 ((filename + strlen(users_home_dir)), TRASH_DIR, strlen(TRASH_DIR))
2573 /************************************************************************
2576 * Since the trash does not use shared menupanes, there is no work to
2577 * be done during the popup and popdown callbacks; therefore, we use
2578 * and empty function.
2580 ************************************************************************/
2585 XtPointer clientData,
2586 XtPointer callData )
2593 /*--------------------------------------------------------------------
2594 * UpdateDirectoryOf:
2595 * Arrange for the directory containing a file to be updated
2596 *------------------------------------------------------------------*/
2605 /* remove last component from path to get the directory */
2606 ptr = strrchr(path, '/');
2610 strcpy(host, home_host_name);
2612 /* now arrange for the directory to be updated */
2613 UpdateDirectory(NULL, host, path);
2615 /* restore the path */
2621 /*--------------------------------------------------------------------
2622 * EraseObject, EraseDir
2623 * Routines for recursively deleting files and directories
2624 *------------------------------------------------------------------*/
2627 EraseObject(char *file_name)
2629 struct stat stat_buf;
2631 if (lstat(file_name, &stat_buf) < 0)
2634 else if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
2635 return EraseDir(file_name);
2637 else if (remove(file_name) < 0)
2646 EraseDir(char *dir_name)
2648 DIR *dir; /* open directory */
2649 struct dirent *entry; /* directory entry */
2650 char srcname[MAX_PATH];
2654 /* open source directory */
2655 dir = opendir(dir_name);
2659 /* prepare source name */
2660 strcpy(srcname, dir_name);
2661 srclen = strlen(srcname);
2662 if (srcname[srclen - 1] != '/')
2663 srcname[srclen++] = '/';
2666 while (rc == 0 && (entry = readdir(dir)) != NULL)
2668 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
2670 strcpy(srcname + srclen, entry->d_name);
2671 rc = EraseObject(srcname);
2678 if (rmdir(dir_name) < 0)
2680 if (unlink(dir_name) < 0 )
2692 /*====================================================================
2694 * MoveToTrashProcess
2695 * Run a background process to move files to the trash can.
2697 *==================================================================*/
2699 /*--------------------------------------------------------------------
2700 * MoveToTrashProcess
2701 * Main routine of background process for MoveToTrash
2702 *------------------------------------------------------------------*/
2709 Boolean do_verify_checks)
2715 struct stat statInfo;
2717 struct dirent * entry;
2722 for (i = 0; i < file_count; i++)
2724 /* get base name and full path */
2725 path = XtNewString(file_list[i]);
2726 baseName = strrchr(file_list[i], '/');
2727 if (baseName == NULL || path == NULL)
2729 /* Invalid filename */
2731 pipe_msg = PIPEMSG_OTHER_ERROR;
2732 rc = BAD_FILE_ERROR;
2733 DPRINTF(("MoveToTrashProcess: sending BAD_FILE_ERROR\n"));
2734 rv = write(pipe_fd, &pipe_msg, sizeof(short));
2735 rv = write(pipe_fd, &rc, sizeof(int));
2738 if (path && MatchesSacredDirectory(path))
2740 /* Invalid filename */
2742 pipe_msg = PIPEMSG_OTHER_ERROR;
2743 rc = BAD_FILE_SACRED;
2744 DPRINTF(("MoveToTrashProcess: sending BAD_FILE_SACRED\n"));
2745 rv = write(pipe_fd, &pipe_msg, sizeof(short));
2746 rv = write(pipe_fd, &rc, sizeof(int));
2751 if (do_verify_checks)
2753 char *tmpstr = (file_list[i] == (baseName - 1))?"/":file_list[i];
2755 Boolean TrashError = False;
2757 /* check write permissions for the file */
2758 if (lstat(path,&s1) < 0)
2765 savechar = *(baseName-1);
2766 *(baseName-1) = '\0';
2767 if(CheckDeletePermission(tmpstr, path) != 0)
2769 /* No write access; display error message */
2771 if (S_ISDIR(s1.st_mode))
2772 rc = BAD_TRASH_DIRECTORY;
2774 rc = BAD_TRASH_FILE;
2777 *(baseName-1) = savechar;
2782 pipe_msg = PIPEMSG_OTHER_ERROR;
2783 DPRINTF(("MoveToTrashProcess: sending BAD_TRASH message\n"));
2784 rv = write(pipe_fd, &pipe_msg, sizeof(short));
2785 rv = write(pipe_fd, &rc, sizeof(int));
2788 else if (CheckAccess(path, W_OK) != 0 && !S_ISLNK(s1.st_mode))
2790 /* No write access; ask user for verification */
2792 pipe_msg = PIPEMSG_OTHER_ERROR;
2794 DPRINTF(("MoveToTrashProcess: sending VERIFY_FILE\n"));
2795 rv = write(pipe_fd, &pipe_msg, sizeof(short));
2796 rv = write(pipe_fd, &rc, sizeof(int));
2801 * If this is a directory, make sure it's empty, i.e.,
2802 * contains only ".", "..", and ".!" or ".~" files.
2804 if (lstat(path, &statInfo) == 0 &&
2805 (statInfo.st_mode & S_IFMT) == S_IFDIR &&
2806 (dirp = opendir(path)) != NULL)
2808 /* read the directory */
2809 while ((entry = readdir(dirp)) != NULL)
2811 if ( !(strcmp(entry->d_name, ".") == 0 ||
2812 strcmp(entry->d_name, "..") == 0 ||
2813 strncmp(entry->d_name, ".!", 2) == 0 ||
2814 strncmp(entry->d_name, ".~", 2) == 0) )
2816 /* found a real file: directory not empty */
2825 /* Directory is not empty */
2827 pipe_msg = PIPEMSG_OTHER_ERROR;
2829 DPRINTF(("MoveToTrashProcess: sending VERIFY_DIR\n"));
2830 rv = write(pipe_fd, &pipe_msg, sizeof(short));
2831 rv = write(pipe_fd, &rc, sizeof(int));
2835 } /* end if do_verify_checks */
2838 to = CreateTrashFilename(baseName, TRUE);
2840 /* move file to the trash directory */
2841 success = FileManip((Widget) (intptr_t) pipe_fd, MOVE_FILE, path, to, TRUE,
2842 FileOpError, True, TRASH_DIRECTORY);
2845 pipe_msg = PIPEMSG_DONE;
2846 DPRINTF(("MoveToTrashProcess: sending DONE\n"));
2847 rv = write(pipe_fd, &pipe_msg, sizeof(short));
2848 PipeWriteString(pipe_fd, path);
2849 PipeWriteString(pipe_fd, to);
2858 /*--------------------------------------------------------------------
2859 * MoveToTrashPipeCB:
2860 * Read and process data sent through the pipe.
2861 *------------------------------------------------------------------*/
2865 XtPointer client_data,
2869 MoveToTrashCBData *cb_data = (MoveToTrashCBData *)client_data;
2872 char *title, *err_msg, *err_arg;
2885 DesktopRec *desktopWin;
2886 char *dir_error=NULL,*file_error=NULL,*no_file_error=NULL,*sacred_error=NULL;
2888 /* read the next msg from the pipe */
2890 n = PipeRead(*fd, &pipe_msg, sizeof(short));
2891 DPRINTF(("MoveToTrashPipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
2895 case PIPEMSG_FILEOP_ERROR:
2896 PipeRead(*fd, &rc, sizeof(int));
2897 err_msg = PipeReadString(*fd);
2898 err_arg = PipeReadString(*fd);
2900 FileOperationError(toplevel, err_msg, err_arg);
2905 cb_data->rc[cb_data->done_count++] = BAD_FILE_ERROR;
2908 case PIPEMSG_OTHER_ERROR:
2909 PipeRead(*fd, &rc, sizeof(int));
2910 cb_data->rc[cb_data->done_count++] = rc;
2913 case PIPEMSG_TARGET_TIME:
2916 case PIPEMSG_FILE_MODIFIED:
2920 i = cb_data->done_count++;
2921 cb_data->path[i] = PipeReadString(*fd);
2922 cb_data->to[i] = PipeReadString(*fd);
2928 "Internal error in MoveToTrashPipeCB: bad pipe_msg %d\n",
2930 /* Don't know how it can get in here, but if it does, we'll try to
2931 * simulate an error condition. Without it, the SGI hung up
2933 while (cb_data->done_count < cb_data->file_count)
2934 cb_data->rc[cb_data->done_count++] = BAD_FILE_ERROR;
2939 /* if still more files to be processed, return now to wait for the rest */
2940 if (cb_data->done_count < cb_data->file_count)
2944 /* close the pipe and cancel the callback */
2945 DPRINTF(("MoveToTrashPipeCB: done\n"));
2949 /* process the results */
2961 for (i = 0; i < cb_data->file_count; i++)
2963 if (cb_data->rc[i]==BAD_TRASH_DIRECTORY || cb_data->rc[i]==BAD_TRASH_FILE)
2968 Boolean errflg = False;
2969 if(cb_data->rc[i] == BAD_TRASH_DIRECTORY)
2971 tmpmsg = GETMESSAGE(27, 113,
2972 "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");
2975 dir_error = (char *) XtRealloc(dir_error,strlen(dir_error)+
2976 strlen(cb_data->file_list[i])+5);
2977 strcat(dir_error,cb_data->file_list[i]);
2978 strcat(dir_error,"\n");
2982 dir_error = XtMalloc(strlen(tmpmsg)+
2983 strlen(cb_data->file_list[i])+5);
2984 sprintf(dir_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
2989 tmpmsg = GETMESSAGE(27, 114,
2990 "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");
2993 file_error = (char *) XtRealloc(file_error,strlen(file_error)
2994 + strlen(cb_data->file_list[i])+5);
2995 strcat(file_error,cb_data->file_list[i]);
2996 strcat(file_error,"\n");
3000 file_error = XtMalloc(strlen(tmpmsg)+
3001 strlen(cb_data->file_list[i])+5);
3002 sprintf(file_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
3005 XtFree(cb_data->file_list[i]);
3006 cb_data->file_list[i] = NULL;
3010 else if (cb_data->rc[i] == BAD_FILE_SACRED)
3014 char *tmpmsg = GETMESSAGE(27, 115,
3015 "The following object(s) cannot be deleted.\nThe desktop cannot function properly without these object(s).\n\n");
3018 sacred_error = (char *) XtRealloc(sacred_error,strlen(sacred_error)+
3019 strlen(cb_data->file_list[i])+5);
3020 strcat(sacred_error,cb_data->file_list[i]);
3021 strcat(sacred_error,"\n");
3025 sacred_error=XtMalloc(strlen(tmpmsg)+strlen(cb_data->file_list[i])+5);
3026 sprintf(sacred_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
3028 XtFree(cb_data->file_list[i]);
3029 cb_data->file_list[i] = NULL;
3033 else if (cb_data->rc[i] == BAD_FILE_ERROR)
3035 XtFree(cb_data->file_list[i]);
3036 cb_data->file_list[i] = NULL;
3038 else if (cb_data->rc[i] == NO_TRASH_FILE)
3041 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");
3047 no_file_error = (char *)XtRealloc(no_file_error,strlen(no_file_error)+
3048 strlen(cb_data->file_list[i])+5);
3049 strcat(no_file_error,cb_data->file_list[i]);
3050 strcat(no_file_error,"\n");
3054 no_file_error = XtMalloc(strlen(tmpmsg)+
3055 strlen(cb_data->file_list[i])+5);
3056 sprintf(no_file_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
3061 else if (cb_data->rc[i] == VERIFY_FILE ||
3062 cb_data->rc[i] == VERIFY_DIR)
3064 if (verifyCount < 6)
3065 AddString(&verifybuf, &verifybufsize, cb_data->file_list[i], NULL);
3069 * Keep track of whether we have directories only, files only,
3070 * or a combination, so that we can display the appropriate
3071 * label in the dialog.
3073 if (cb_data->rc[i] == VERIFY_FILE)
3079 * Add to array which is to be attached to the dialog;
3080 * the array will ultimately be NULL terminated.
3082 verifylist = (char **)XtRealloc((char *)verifylist,
3083 sizeof(char *) * (verifyCount + 1));
3084 verifylist[verifyCount - 1] = cb_data->file_list[i];
3088 /* Add file to trash list */
3089 if (numTrashItems >= trashListSize)
3091 trashListSize += 10;
3092 trashCan = (TrashEntry *)
3093 XtRealloc((char *)trashCan, sizeof(TrashEntry) * trashListSize);
3096 trashCan[numTrashItems].problem = False;
3097 trashCan[numTrashItems].intNew = cb_data->to[i];
3098 trashCan[numTrashItems].intOrig = cb_data->path[i];
3099 trashCan[numTrashItems].external = cb_data->file_list[i];
3101 /* extract base file name */
3102 baseName = strrchr(cb_data->to[i], '/');
3104 if (*baseName == '\0')
3106 trashCan[numTrashItems].filename = XtNewString(baseName);
3111 /* arrange for the source directory to be updated */
3112 UpdateDirectoryOf(cb_data->path[i]);
3115 * If the source file was referenced by a desktop object,
3116 * we need to remove the destkop object
3118 for (j = 0; j < desktop_data->numIconsUsed; j++)
3120 Tt_status tt_status;
3122 desktopWin = desktop_data->desktopWindows[j];
3123 fileName = ResolveLocalPathName( desktopWin->host,
3124 desktopWin->dir_linked_to,
3125 desktopWin->file_name,
3126 home_host_name, &tt_status);
3128 if( TT_OK == tt_status )
3130 if (strcmp(fileName, cb_data->path[i]) == 0)
3132 RemoveDT(desktopWin->shell, (XtPointer)desktopWin, NULL);
3134 RemoveMovedObjectFromDT(desktopWin->shell, (XtPointer)desktopWin,
3135 cb_data->file_count, cb_data->file_list);
3136 #endif /* SUN_PERF */
3145 buf = XtMalloc(strlen(dir_error)+3);
3146 sprintf(buf,"%s\n",dir_error);
3153 buf = XtMalloc(strlen(file_error)+3);
3154 sprintf(buf,"%s\n",file_error);
3158 buf = XtRealloc(buf,strlen(buf)+strlen(file_error)+3);
3159 sprintf(buf,"%s%s\n",buf,file_error);
3167 buf = XtMalloc(strlen(no_file_error)+3);
3168 sprintf(buf,"%s\n",no_file_error);
3172 buf = XtRealloc(buf,strlen(buf)+strlen(no_file_error)+3);
3173 sprintf(buf,"%s%s\n",buf,no_file_error);
3175 XtFree(no_file_error);
3181 buf = XtMalloc(strlen(sacred_error)+3);
3182 sprintf(buf,"%s\n",sacred_error);
3186 buf = XtRealloc(buf,strlen(buf)+strlen(sacred_error)+3);
3187 sprintf(buf,"%s%s\n",buf,sacred_error);
3189 XtFree(sacred_error);
3192 bufsize = strlen(buf);
3194 /* Update the trash information file, and the trash window */
3197 FILE * trashInfoFileId = fopen(TrashInfoFileName, "a+");
3198 if( trashInfoFileId != NULL )
3200 for (i = fileCount; i > 0; i--)
3202 if( WriteEntry(trashInfoFileId,
3203 trashCan[numTrashItems - i].external,
3204 trashCan[numTrashItems - i].filename) < 0 )
3207 fflush(trashInfoFileId);
3208 fclose(trashInfoFileId);
3209 if( trashFileMgrData )
3210 UpdateDirectory(NULL, trashFileMgrData->host,
3211 trashFileMgrData->current_directory);
3215 /* Check for any bad files; post an error dialog */
3218 /* send a reply to the message that triggered this operation, if any */
3220 if (cb_data->msg != 0) {
3222 Until Action is fixed.
3223 tttk_message_fail( cb_data->msg, TT_DESKTOP_EACCES, 0, 1 );
3225 tt_message_reply( cb_data->msg );
3226 tttk_message_destroy( cb_data->msg );
3228 if (cb_data->msg_cnt > 0) {
3229 for (i = 0 ; i < cb_data->msg_cnt ; i++) {
3230 tt_message_reply( cb_data->msg_list[i] ) ;
3231 tttk_message_destroy( cb_data->msg_list[i] ) ;
3233 XtFree((char *)cb_data->msg_list) ;
3234 cb_data->msg_cnt = 0 ;
3235 #endif /* SUN_PERF */
3238 /* If more items than can be displayed, let user know */
3241 char extraFiles[256];
3243 (void) sprintf(extraFiles, AdditionalHeader, badCount - 8);
3247 GETMESSAGE(27,97, "The following objects could not be placed in the trash can: \n"));
3250 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3251 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3253 XtFree ((char *) buf);
3255 /* send a reply to the message that triggered this operation, if any */
3257 if (cb_data->msg != 0) {
3258 tt_message_reply( cb_data->msg );
3259 tttk_message_destroy( cb_data->msg );
3261 if (cb_data->msg_cnt > 0) {
3262 for (i = 0 ; i < cb_data->msg_cnt ; i++) {
3263 tt_message_reply( cb_data->msg_list[i] ) ;
3264 tttk_message_destroy( cb_data->msg_list[i] ) ;
3266 XtFree((char *)cb_data->msg_list) ;
3267 cb_data->msg_cnt = 0 ;
3268 #endif /* SUN_PERF */
3273 /* Check for any files requiring user verification; post a prompt dialog */
3274 /* XXX Really should fail incoming ToolTalk request if user cancels any */
3282 /* If more items than can be displayed, let user know */
3283 if (verifyCount > 6)
3285 char extraFiles[256];
3287 (void) sprintf(extraFiles, AdditionalHeader, verifyCount - 6);
3288 AddString(&verifybuf, &verifybufsize, extraFiles, NULL);
3292 * Depending upon what type of files are to be displayed in the
3293 * dialog, choose the appropriate dialog text.
3295 if (vfiles && vdirs)
3296 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");
3298 header = GETMESSAGE(27,104, "You do not have write permission for the following file(s):\nDo you want to proceed?\n");
3300 header = GETMESSAGE(27,100, "The following folder(s) are not empty.\nDo you want to proceed?\n");
3302 tmpbuf = XtMalloc(strlen(header) + strlen(verifybuf) + 1);
3303 sprintf(tmpbuf, "%s%s", header, verifybuf);
3304 title = XtNewString(GETMESSAGE(27, 4, "Trash Can Warning"));
3305 dlog = (Widget)_DtMessageDialog(toplevel, title, tmpbuf, NULL, True,
3306 VerifyCancel, VerifyOk, NULL, HelpRequestCB,
3307 False, WARNING_DIALOG);
3309 XtFree ((char *) verifybuf);
3313 * Add array as userdata on the dialog.
3314 * NULL terminate the array.
3316 verifylist[verifyCount] = NULL;
3317 XtSetArg(args[0], XmNuserData, verifylist);
3318 XtSetValues(dlog, args, 1);
3321 /* free the callback data */
3322 XtFree((char *)cb_data->file_list);
3323 XtFree((char *)cb_data->path);
3324 XtFree((char *)cb_data->to);
3325 XtFree((char *)cb_data->rc);
3326 XtFree((char *)cb_data);
3332 /*--------------------------------------------------------------------
3334 * Start the background process and set up callback for the pipe.
3335 *------------------------------------------------------------------*/
3341 Boolean do_verify_checks,
3344 static char *pname = "MoveToTrash";
3345 MoveToTrashCBData *cb_data;
3349 if( !TrashInitialized )
3352 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
3357 /* set up callback data */
3358 cb_data = XtNew(MoveToTrashCBData);
3359 cb_data->file_list = file_list;
3360 cb_data->file_count = file_count;
3361 cb_data->done_count = 0;
3362 cb_data->path = (char **)XtCalloc(file_count, sizeof(char *));
3363 cb_data->to = (char **)XtCalloc(file_count, sizeof(char *));
3364 cb_data->rc = (int *)XtCalloc(file_count, sizeof(int));
3367 cb_data->msg_cnt = 0 ;
3368 if (global_msg_cnt > 0) {
3369 cb_data->msg_cnt = global_msg_cnt ;
3370 cb_data->msg_list = (Tt_message *)XtMalloc(sizeof(Tt_message) * cb_data->msg_cnt);
3371 memcpy((char *)cb_data->msg_list,(char *)global_msg_list, sizeof(Tt_message) *
3374 #endif /* SUN_PERF */
3377 if(-1 == pipe(pipe_fd)) {
3379 "%s: pipe failed error %d=%s\n",
3380 pname, errno, strerror(errno));
3384 /* fork the process that does the actual work */
3389 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
3390 pname, getppid(), getpid(), errno, strerror(errno));
3397 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
3399 close(pipe_fd[0]); /* child won't read from the pipe */
3401 MoveToTrashProcess(pipe_fd[1], file_list, file_count, do_verify_checks);
3404 DBGFORK(("%s: child exiting\n", pname));
3409 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
3411 /* parent: set up callback to get the pipe data */
3412 close(pipe_fd[1]); /* parent won't write the pipe */
3414 cb_data->child = pid;
3416 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
3417 pipe_fd[0], (XtPointer)XtInputReadMask,
3418 MoveToTrashPipeCB, (XtPointer)cb_data);
3422 /*====================================================================
3426 * Run a background process to restore files from the trash can.
3428 * These routines are used both for "normal restores", i.e.,
3429 * restores initiated from the Trash menu or through an ICCCM
3430 * message, as well as "drag&drop restores", i.e., restores done
3431 * by dragging files from the trash can to some other directory.
3432 * For normal resores, file_list contains only simple file names
3433 * (no paths) and target_dir is NULL. For drag&drop restores
3434 * file_list contains complete path names and target_dir contains
3435 * the name of the directoy to which the files should be moved.
3437 *==================================================================*/
3439 /*--------------------------------------------------------------------
3441 * Main routine of background process for RestoreFromTrash
3442 *------------------------------------------------------------------*/
3452 Boolean CheckedAlready)
3461 char **RestoreList= NULL;
3463 /* get full path name of target directory */
3466 Tt_status tt_status;
3467 full_dirname = ResolveLocalPathName(target_host, target_dir,
3468 NULL, home_host_name, &tt_status);
3469 if( TT_OK != tt_status )
3471 /* send return codes back trough the pipe */
3472 pipe_msg = PIPEMSG_DONE;
3474 DPRINTF(("RestoreProcess: Unable to Resolve local path name\n"));
3475 rv = write(pipe_fd, &pipe_msg, sizeof(short));
3476 rv = write(pipe_fd, rc, sizeof(int));
3481 full_dirname = NULL;
3483 /* restore the files */
3484 for (i = 0; i < file_count; i++)
3486 /* Locate file in trash list */
3487 for (j = 0; j < numTrashItems; j++)
3489 /* file_list[i] may be a complete path or just a file name */
3490 if (strcmp(file_list[i], trashCan[j].filename) == 0 ||
3491 strcmp(file_list[i], trashCan[j].intNew) == 0)
3497 /* determine source and target for the move */
3498 if (target_dir == NULL)
3500 /* this is a normal restore */
3501 if (j < numTrashItems)
3503 from = trashCan[j].intNew;
3504 to = trashCan[j].intOrig;
3508 /* can't do a restore if the file wasn't found in the trash list */
3514 /* this is a drag&drop from the trash can to target_dir */
3515 from = file_list[i];
3516 if (j < numTrashItems)
3517 ptr = strrchr(trashCan[j].intOrig, '/');
3519 ptr = strrchr(file_list[i], '/');
3520 strcpy(buf, full_dirname);
3528 status = RestoreObject((Widget) (intptr_t) pipe_fd, MOVE_FILE, from,to,
3529 TRUE, FileOpError, False, NOT_DESKTOP,CheckedAlready);
3530 /* restore was successful */
3531 if(status == (int) True)
3533 else if(status == (int) False)
3539 /* restore failed */
3543 /* send return codes back trough the pipe */
3544 pipe_msg = PIPEMSG_DONE;
3545 DPRINTF(("RestoreProcess: sending DONE\n"));
3546 rv = write(pipe_fd, &pipe_msg, sizeof(short));
3547 rv = write(pipe_fd, rc, file_count * sizeof(int));
3549 XtFree(full_dirname);
3553 /*--------------------------------------------------------------------
3555 * Read and process data sent through the pipe.
3556 *------------------------------------------------------------------*/
3560 XtPointer client_data,
3564 RestoreFromTrashCBData *cb_data = (RestoreFromTrashCBData *)client_data;
3567 char *title, *err_msg, *err_arg;
3571 char **ToRestoreList=NULL;
3572 char **FromRestoreList=NULL;
3573 char *target_host,*target_dir;
3576 /* read the next msg from the pipe */
3578 n = PipeRead(*fd, &pipe_msg, sizeof(short));
3579 DPRINTF(("RestorePipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
3583 case PIPEMSG_FILEOP_ERROR:
3584 PipeRead(*fd, &rc, sizeof(int));
3585 err_msg = PipeReadString(*fd);
3586 err_arg = PipeReadString(*fd);
3587 FileOperationError(toplevel, err_msg, err_arg);
3588 /* This call will popup an error dialog.
3590 FileOperationError(toplevel, err_msg, err_arg);
3592 It's not appropriate at all to popup an error dialog here.
3593 i.e. if there're 1000 files, and the file system is full,
3594 and we're unable to move, would we want to popup 1000
3601 PipeRead(*fd, cb_data->rc, cb_data->file_count * sizeof(int));
3606 "Internal error in RestorePipeCB: bad pipe_msg %d\n",
3610 /* close the pipe and cancel the callback */
3611 DPRINTF(("RestorePipeCB: done\n"));
3618 for (i = 0; i < cb_data->file_count; i++)
3620 /* Locate file in trash list */
3621 for (j = 0; j < numTrashItems; j++)
3623 /* file_list[i] may be a complete path or just a file name */
3624 if (strcmp(cb_data->file_list[i], trashCan[j].filename) == 0 ||
3625 strcmp(cb_data->file_list[i], trashCan[j].intNew) == 0)
3631 if (cb_data->rc[i] == SKIP_FILE)
3633 ToRestoreList = (char **) XtRealloc((char *)ToRestoreList,sizeof(char *) *
3635 ToRestoreList[RestoreIndex-1] = XtNewString(trashCan[j].intOrig);
3636 FromRestoreList = (char **) XtRealloc((char *)FromRestoreList,sizeof(char *) *
3638 FromRestoreList[RestoreIndex-1] = XtNewString( trashCan[j].intNew );
3641 /* Check the return code from the restore */
3642 else if (cb_data->rc[i] == 0)
3644 /* restore was successful: remove the file from the trash list */
3645 if (j < numTrashItems)
3647 /* arrange for the source directory to be updated */
3648 UpdateDirectoryOf(trashCan[j].intOrig);
3650 /* Remove this entry from the trash list */
3651 XtFree ((char *) trashCan[j].intNew);
3652 XtFree ((char *) trashCan[j].intOrig);
3653 XtFree ((char *) trashCan[j].external);
3654 XtFree ((char *) trashCan[j].filename);
3655 for (k = j; k < (numTrashItems - 1); k++)
3656 trashCan[k] = trashCan[k + 1];
3663 char *restore_header,*tmpStr = GETMESSAGE(27,101,
3664 "The following object(s) could not be put back:\n");
3666 restore_header = XtNewString(tmpStr);
3667 if (j < numTrashItems && cb_data->target_dir == NULL)
3668 AddString(&buf, &bufsize, trashCan[j].external, restore_header);
3670 AddString(&buf, &bufsize, cb_data->file_list[i], restore_header);
3671 XtFree(restore_header);
3675 /* Update the trash information file */
3676 if( ! WriteTrashEntries() )
3678 char * tmpStr, * title;
3680 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3681 tmpStr = XtNewString(GETMESSAGE(27, 88, "Cannot write to a temporary file.\nPerhaps your file system is full.\n"));
3682 _DtMessage(toplevel, title, tmpStr, NULL, HelpRequestCB);
3687 /* send a reply to the message that triggered this operation, if any */
3688 if (cb_data->msg != 0) {
3689 tt_message_reply( cb_data->msg );
3690 tttk_message_destroy( cb_data->msg );
3694 /* Report any errors */
3697 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3698 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3700 XtFree ((char *) buf);
3703 UpdateDirectory(NULL, trashFileMgrData->host,
3704 trashFileMgrData->current_directory);
3705 if (cb_data->target_dir)
3706 UpdateDirectory(NULL, cb_data->target_host, cb_data->target_dir);
3708 target_host = XtNewString(cb_data->target_host);
3709 target_dir = XtNewString(cb_data->target_dir);
3711 /* free the callback data */
3712 for (i = 0; i < cb_data->file_count; i++)
3713 XtFree(cb_data->file_list[i]);
3714 XtFree((char *)cb_data->file_list);
3715 XtFree((char *)cb_data->target_host);
3716 XtFree((char *)cb_data->target_dir);
3717 XtFree((char *)cb_data->rc);
3718 XtFree((char *)cb_data);
3721 for(i=0;i<RestoreIndex;i++)
3723 CreateRestoreDialog(FromRestoreList[i],ToRestoreList[i]);
3724 XtFree(FromRestoreList[i]);
3725 XtFree(ToRestoreList[i]);
3727 XtFree((char *) FromRestoreList);
3728 XtFree((char *) ToRestoreList);
3730 XtFree(target_host);
3735 /*--------------------------------------------------------------------
3737 * Start the background process and set up callback for the pipe.
3738 *------------------------------------------------------------------*/
3747 Boolean CheckedAlready)
3749 static char *pname = "RestoreFromTrash";
3750 RestoreFromTrashCBData *cb_data;
3754 if( !TrashInitialized )
3757 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
3762 /* set up callback data */
3763 cb_data = XtNew(RestoreFromTrashCBData);
3764 cb_data->file_list = file_list;
3765 cb_data->file_count = file_count;
3766 cb_data->target_host = target_host;
3767 cb_data->target_dir = target_dir;
3768 cb_data->rc = (int *)XtCalloc(file_count, sizeof(int));
3772 if(-1 == pipe(pipe_fd)) {
3774 "%s: pipe failed, error %d=%s\n",
3775 pname, errno, strerror(errno));
3779 /* fork the process that does the actual work */
3784 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
3785 pname, getppid(), getpid(), errno, strerror(errno));
3792 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
3794 close(pipe_fd[0]); /* child won't read from the pipe */
3796 RestoreProcess(pipe_fd[1], file_list, file_count,
3797 target_host, target_dir, cb_data->rc,CheckedAlready);
3800 DBGFORK(("%s: child exiting\n", pname));
3805 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
3807 /* parent: set up callback to get the pipe data */
3808 close(pipe_fd[1]); /* parent won't write the pipe */
3810 cb_data->child = pid;
3812 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
3813 pipe_fd[0], (XtPointer)XtInputReadMask,
3814 RestorePipeCB, (XtPointer)cb_data);
3819 /*====================================================================
3822 * Run a background process to remove files from the trash can.
3824 *==================================================================*/
3826 /*--------------------------------------------------------------------
3828 * Main routine of background process for EmptyTrash
3829 *------------------------------------------------------------------*/
3834 DeleteList * del_list,
3842 * Delete all files or directories in the list,
3843 * as well as any associated annotations.
3845 for (i = 0; i < del_count; i++)
3847 /* delete the file */
3848 rc[i] = EraseObject(del_list[i].trash);
3851 /* send return codes back trough the pipe */
3852 pipe_msg = PIPEMSG_DONE;
3853 DPRINTF(("EmptyTrashProcess: sending DONE\n"));
3854 rv = write(pipe_fd, &pipe_msg, sizeof(short));
3855 rv = write(pipe_fd, rc, del_count * sizeof(int));
3859 /*--------------------------------------------------------------------
3861 * Read and process data sent through the pipe.
3862 *------------------------------------------------------------------*/
3866 XtPointer client_data,
3870 EmptyTrashCBData *cb_data = (EmptyTrashCBData *)client_data;
3873 char *title, *err_msg, *err_arg;
3874 int problemCount, itemCount;
3879 /* read the next msg from the pipe */
3881 n = PipeRead(*fd, &pipe_msg, sizeof(short));
3882 DPRINTF(("EmptyTrashPipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
3886 case PIPEMSG_FILEOP_ERROR:
3887 PipeRead(*fd, &rc, sizeof(int));
3888 err_msg = PipeReadString(*fd);
3889 err_arg = PipeReadString(*fd);
3890 /* This call will popup an error dialog.
3892 FileOperationError(toplevel, err_msg, err_arg);
3894 It's not appropriate at all to popup an error dialog here.
3895 i.e. if there're 1000 files, and the file system is full,
3896 and we're unable to move, would we want to popup 1000
3904 PipeRead(*fd, cb_data->rc, cb_data->del_count * sizeof(int));
3909 "Internal error in EmptyTrashPipeCB: bad pipe_msg %d\n",
3913 /* close the pipe and cancel the callback */
3914 DPRINTF(("EmptyTrashPipeCB: done\n"));
3923 if (cb_data->removeType == TRASH_FILE)
3925 for (i = 0; i < cb_data->del_count; i++)
3927 /* Locate file in trash list */
3928 for (j = 0; j < numTrashItems; j++)
3929 if (strcmp(cb_data->del_list[i].trash, trashCan[j].intNew) == 0)
3932 /* Check the return code from the erase */
3933 if (cb_data->rc[i] == 0)
3935 /* erase was successful: remove the file from the trash list */
3936 if (j < numTrashItems)
3938 /* Remove this entry from the trash list */
3939 XtFree ((char *) trashCan[j].intNew);
3940 XtFree ((char *) trashCan[j].intOrig);
3941 XtFree ((char *) trashCan[j].external);
3942 XtFree ((char *) trashCan[j].filename);
3943 for (k = j; k < (numTrashItems - 1); k++)
3944 trashCan[k] = trashCan[k + 1];
3955 char * msg = XtNewString(GETMESSAGE(27,96, "The following objects could not be removed from the file system: \n"));
3957 if (j < numTrashItems)
3958 file_name = trashCan[j].external;
3960 file_name = cb_data->del_list[i].trash;
3961 AddString(&buf, &bufsize, file_name, msg);
3968 /* Update the trash information file */
3969 if( ! WriteTrashEntries() )
3971 char * tmpStr, * title;
3973 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3974 tmpStr = XtNewString(GETMESSAGE(27, 88, "Cannot write to a temporary file.\nPerhaps your file system is full.\n"));
3975 _DtMessage(toplevel, title, tmpStr, NULL, HelpRequestCB);
3980 /* Report any errors */
3983 /* If more items than can be displayed, let user know */
3984 if (itemCount < problemCount)
3986 char extraFiles[256];
3987 sprintf(extraFiles, AdditionalHeader, problemCount - itemCount);
3991 GETMESSAGE(27,97, "The following objects could not be placed in the trash can: \n") );
3994 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3995 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3997 XtFree ((char *) buf);
4000 if (trashFileMgrData->selected_file_count > 0)
4002 XtSetSensitive(*removeBtn, True);
4003 XtSetSensitive(*restoreBtn, True);
4006 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
4007 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
4009 UpdateDirectory(NULL, trashFileMgrData->host,
4010 trashFileMgrData->current_directory);
4014 if (cb_data->rc[0] == 0)
4016 /* arrange for the directory containing the file to be updated */
4017 UpdateDirectoryOf(cb_data->del_list[0].trash);
4021 /* put up an error message saying the file couldn't be removed */
4025 tmpStr = GETMESSAGE(27, 81, "Shred File/Folder Error");
4026 title = XtNewString(tmpStr);
4027 tmpStr = GETMESSAGE(27, 82, " The following file could not be removed from the file system: \n");
4028 file_name = cb_data->del_list[0].trash;
4029 buffer = XtMalloc(strlen(tmpStr) + strlen(file_name) + 1);
4030 sprintf(buffer, "%s%s\n", tmpStr, file_name);
4031 _DtMessage(toplevel, title, buffer, NULL, HelpRequestCB);
4038 /* reset removingTrash flag */
4039 removingTrash = False;
4041 /* send a reply to the message that triggered this operation, if any */
4042 if (cb_data->msg != 0) {
4043 tt_message_reply( cb_data->msg );
4044 tttk_message_destroy( cb_data->msg );
4048 /* free the callback data */
4049 for (i = 0; i < cb_data->del_count; i++)
4051 XtFree(cb_data->del_list[i].trash);
4052 XtFree(cb_data->del_list[i].orig);
4054 XtFree((char *)cb_data->del_list);
4055 XtFree((char *)cb_data->rc);
4056 XtFree((char *)cb_data);
4060 /*--------------------------------------------------------------------
4062 * Start the background process and set up callback for the pipe.
4063 *------------------------------------------------------------------*/
4067 DeleteList *del_list,
4072 static char *pname = "EmptyTrash";
4073 EmptyTrashCBData *cb_data;
4077 if( !TrashInitialized )
4080 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
4085 /* set removingTrash flag */
4086 removingTrash = True;
4088 /* set up callback data */
4089 cb_data = XtNew(EmptyTrashCBData);
4090 cb_data->del_list = del_list;
4091 cb_data->del_count = del_count;
4092 cb_data->removeType = removeType;
4093 cb_data->rc = (int *)XtCalloc(del_count, sizeof(int));
4097 if(-1 == pipe(pipe_fd)) {
4099 "%s: pipe failed, error %d=%s\n",
4100 pname, errno, strerror(errno));
4104 /* fork the process that does the actual work */
4109 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
4110 pname, getppid(), getpid(), errno, strerror(errno));
4117 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
4119 close(pipe_fd[0]); /* child won't read from the pipe */
4121 EmptyTrashProcess(pipe_fd[1], del_list, del_count, cb_data->rc);
4124 DBGFORK(("%s: child exiting\n", pname));
4129 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
4131 /* parent: set up callback to get the pipe data */
4132 close(pipe_fd[1]); /* parent won't write the pipe */
4134 cb_data->child = pid;
4136 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
4137 pipe_fd[0], (XtPointer)XtInputReadMask,
4138 EmptyTrashPipeCB, (XtPointer)cb_data);
4142 CheckDeletePermission(
4144 char *destinationPath)
4146 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__linux__)
4147 struct statfs statbuf;
4148 #elif defined(__NetBSD__)
4149 struct statvfs statbuf;
4151 struct stat statbuf;
4153 char fname[PATH_MAX];
4155 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__linux__)
4156 if (statfs(parentdir,&statbuf) < 0) /* does not exist */
4157 #elif defined(__NetBSD__)
4158 if (statvfs(parentdir,&statbuf) < 0) /* does not exist */
4160 if (lstat(parentdir,&statbuf) < 0) /* does not exist */
4164 /* check if we are root */
4167 /* if NFS, need to check if server trusts root */
4168 #if defined(CSRG_BASED)
4169 if (!strcmp(statbuf.f_fstypename, "nfs")) /* Root user and nfs */
4170 #elif defined(__linux__)
4171 if (statbuf.f_type == NFS_SUPER_MAGIC)
4173 /* nothing - always check if root */
4178 tmpfile = tempnam(parentdir,"dtfile");
4181 if ((fd = creat(tmpfile,O_RDONLY)) < 0) /* Create a temporary file */
4187 if (remove(tmpfile) < 0) /* Delete the created file */
4195 /* root user can delete anything */
4199 /* check for write and execute permisssion on parent dir */
4200 if (CheckAccess(parentdir, W_OK | X_OK) < 0)
4203 /* copy destinationPath to tmp buffer */
4204 snprintf(fname, PATH_MAX, "%s", destinationPath);
4206 return CheckDeletePermissionRecur(fname);
4211 CheckDeletePermissionRecur(
4212 char *destinationPath)
4214 struct stat statbuf;
4220 DPRINTF(("CheckDeletePermissionRecur(\"%s\")\n", destinationPath));
4222 if (lstat(destinationPath, &statbuf) < 0)
4223 return -1; /* probably does not exist */
4225 if (! S_ISDIR(statbuf.st_mode))
4226 return 0; /* no need to check anything more */
4228 dirp = opendir (destinationPath);
4230 return -1; /* could not read directory */
4235 while (dp = readdir (dirp))
4237 if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
4241 /* check for write permission in this directory */
4242 if (CheckAccess(destinationPath, W_OK|X_OK) < 0) {
4247 /* append a '/' to the end of directory name */
4248 fnamep = destinationPath + strlen(destinationPath);
4254 /* append file name to end of directory name */
4255 strcpy(fnamep, dp->d_name);
4257 /* recursively check permission on this file */
4258 if (CheckDeletePermissionRecur(destinationPath))
4278 Boolean isContainer,
4279 void (*errorHandler)(),
4280 Boolean checkForBusyDir,
4282 Boolean CheckedAlready)
4284 struct stat statsrc,stattar;
4286 char *localdir = NULL,*chrptr;
4291 if(stat(source,&statsrc) < 0)
4292 if(lstat(source,&statsrc) < 0)
4295 localdir = strdup(target);
4297 chrptr = strrchr(localdir,'/');
4300 if(chrptr == localdir) /* Must be root folder */
4305 if(stat(target,&stattar) >= 0) /* Target exists */
4307 if(CheckDeletePermission(localdir,target)) {
4309 return ((int)False);
4318 return ((int )FileManip((Widget)w, MOVE_FILE, source, target, TRUE,
4319 FileOpError, False, NOT_DESKTOP));
4322 CreateRestoreDialog(
4326 char *tmpbuf,*title;
4330 char **dirs = (char **) malloc(sizeof(char *) * 2);
4331 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.");
4333 dirs[0] = strdup(source);
4334 dirs[1] = strdup(target);
4335 tmpbuf = XtMalloc(strlen(header) + strlen(target) + 1);
4336 sprintf(tmpbuf, header, target);
4337 title = XtNewString(GETMESSAGE(27, 109, "Put Back Warning"));
4338 dw = (Widget)_DtMessageDialog(toplevel, title,tmpbuf,NULL,True,RestoreVerifyCancel,
4339 RestoreVerifyOk, NULL, HelpRequestCB, False, WARNING_DIALOG);
4341 XtSetArg(args[n], XmNuserData,dirs); n++;
4342 XtSetValues(dw,args,n);
4350 XtPointer client_data,
4351 XtPointer call_data )
4356 Widget mbox = (Widget)client_data;
4358 XtSetArg(args[n],XmNuserData,&dirs); n++;
4359 XtGetValues(mbox,args,n);
4361 if(DirectoryBusy(dirs[1]))
4364 char * tmpStr,*title;
4366 title = XtNewString(GETMESSAGE(27,111,"Put Back Error"));
4367 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.");
4368 msgbuf = XtMalloc(strlen(tmpStr) + strlen(dirs[1])+1);
4369 sprintf (msgbuf, tmpStr, dirs[1]);
4370 _DtMessage(toplevel,title,msgbuf,NULL,HelpRequestCB);
4378 char *realTarget,*tptr;
4382 if(lstat(dirs[1],&s1) < 0)
4384 fsErase(dirs[1],&status,0);
4385 FileList = (char **) XtMalloc(sizeof(char *));
4386 FileList[0] = XtNewString(dirs[0]);
4387 RestoreFromTrash(FileList, (int) 1, NULL, NULL, NULL,True);
4393 XtDestroyWidget(mbox);
4397 RestoreVerifyCancel(
4399 XtPointer client_data,
4400 XtPointer call_data )
4406 Widget mbox = (Widget)client_data;
4408 XtSetArg(args[n],XmNuserData,&dirs); n++;
4409 XtGetValues(mbox,args,n);
4413 XtDestroyWidget(mbox);