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
51 * MatchesSacredDirectory
75 * TrashRemoveNoConfirmHandler
85 * (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
86 * (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
87 * (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
88 * (c) Copyright 1993, 1994, 1995 Novell, Inc.
90 ****************************************************************************
91 ************************************<+>*************************************/
93 #include <EUSCompat.h>
95 #include <sys/types.h>
105 #if defined(CSRG_BASED)
106 #include <sys/param.h>
107 #include <sys/mount.h>
112 #include <Xm/RowColumn.h>
113 #include <Xm/CascadeB.h>
115 #include <Xm/Frame.h>
116 #include <Xm/MainW.h>
117 #include <Xm/PushBG.h>
118 #include <Xm/SeparatoG.h>
119 #include <Xm/MessageB.h>
120 #include <Xm/MwmUtil.h>
122 #include <X11/Shell.h>
123 #include <X11/Xatom.h>
124 #include <Xm/Protocols.h>
126 #include <Dt/Action.h>
128 #include <Dt/FileM.h>
129 #include <Dt/DtNlUtils.h>
130 #include <Dt/Connect.h>
131 #include <Dt/Indicator.h>
132 #include <Dt/EnvControlP.h>
135 #include <Dt/SharedProcs.h>
136 #include "DtSvcInternal.h" /* _DtGetMask */
141 #include "SharedProcs.h"
142 #include "MultiView.h"
147 #include "SharedMsgs.h"
148 #include "dtcopy/fsrtns.h"
150 #define AdditionalHeader (GETMESSAGE(27,98, "(Plus %d additional object(s))"))
152 /* Trash file errors */
153 #define NO_FILE_ERROR 0
154 #define BAD_FILE_ERROR -1
155 #define VERIFY_DIR -2
156 #define VERIFY_FILE -3
157 #define BAD_TRASH_DIRECTORY -4
158 #define BAD_TRASH_FILE -5
159 #define NO_TRASH_FILE -6
162 #define BAD_FILE_SACRED -9
164 /* types of messages sent through the pipe */
165 #define PIPEMSG_FILEOP_ERROR 1
166 #define PIPEMSG_OTHER_ERROR 3
167 #define PIPEMSG_DONE 7
168 #define PIPEMSG_TARGET_TIME 5
169 #define PIPEMSG_FILE_MODIFIED 6
172 * Structure describing each file in the trash can.
173 * Includes the external host:/name format, the original internal
174 * /nfs/host/name format, and the internal /nfs/host/new_name describing
175 * the temporary location of the file. The `problem' flag is used to
176 * indicate if a problem occurred when the file was physically being
188 /* callback data MoveToTrash */
199 Tt_message *msg_list ;
201 #endif /* SUN_PERF */
208 Tt_message *msg_list ;
211 #endif /* SUN_PERF */
213 /* callback data RestoreFromTrash */
223 } RestoreFromTrashCBData;
225 /* callback data EmptyTrash */
234 DeleteList *del_list;
243 DialogData *trashDialogData; /* fm data associated with trash dialog */
244 FileMgrData *trashFileMgrData = NULL;
246 Boolean removingTrash = False;
248 DialogData * primaryTrashHelpDialog = NULL;
249 DialogData ** secondaryTrashHelpDialogList = NULL;
250 int secondaryTrashHelpDialogCount = 0;
252 /* Forward prototype */
253 int EraseDir(char *dir_name);
254 /* From dtcopy/fsrtns.c */
255 extern int EmptyDir(char *sourceP, int rm, int force);
257 /* 'defines' for trash files */
258 static char * TRASH_DIR = ".dt/Trash";
259 static char * TRASH_INFO_FILE = ".dt/Trash/.trashinfo";
260 static char * NEW_TRASH_INFO_FILE = ".dt/.tmptrashinfo";
262 static char * RM = "/bin/rm";
263 static char * RM_ARGS = "-rf";
265 static Widget * selectAllBtn = NULL;
266 static Widget * restoreBtn = NULL;
267 static Widget * removeBtn = NULL;
269 static int trashMenuItemCount = 22; /* trash menu items */
270 static MenuDesc * trashMenu = NULL;
272 static TrashEntry * trashCan = NULL;
273 static int trashListSize = 0;
274 static int numTrashItems = 0;
276 static Boolean trashDialogPosted;
278 static char * TrashInfoFileName = NULL;
279 static char * NewTrashInfoFileName = NULL;
281 static char ** sacred_dir_list = NULL; /* list of directories in trash path */
282 static int sacred_dir_list_size = 0;
283 static int sacred_dir_count = 0;
285 static Boolean verifyPromptsEnabled; /* do we prompt the user? */
287 static Tt_message global;
289 static Tt_message *global_msg_list ;
290 static int global_msg_cnt = 0 ;
291 #endif /* SUN_PERF */
292 static Boolean TrashInitialized = False;
294 /******** Static Function Declarations ********/
296 static String CreateTrashFilename(
299 static void MessageToFileList(
304 static void MessagesToFileList(
305 Tt_message *msg_list,
309 #endif /* SUN_PERF */
310 static int WriteEntry(
314 static Boolean MatchesSacredDirectory(
316 static void VerifyCleanup(
318 Boolean completeDelete) ;
319 static void VerifyCancel(
321 XtPointer client_data,
322 XtPointer call_data) ;
323 static void VerifyOk(
325 XtPointer client_data,
326 XtPointer call_data) ;
327 static Boolean WriteTrashEntries( void ) ;
328 static void Select_All(
330 XtPointer client_data,
331 XtPointer call_data) ;
332 static void Unselect_All(
334 XtPointer client_data,
335 XtPointer call_data) ;
338 XtPointer client_data,
339 XtPointer call_data) ;
340 static String GetBasePath(
342 static Boolean ReadTrashList( void ) ;
343 static void RemoveOkCB(
345 XtPointer client_data,
346 XtPointer call_data ) ;
347 static void RemoveCancelCB(
349 XtPointer client_data,
350 XtPointer call_data ) ;
351 static void ConfirmOk(
353 XtPointer client_data,
354 XtPointer call_data ) ;
355 static void ConfirmCancel(
357 XtPointer client_data,
358 XtPointer call_data ) ;
361 XtPointer clientData,
362 XtPointer callData) ;
363 static void AddToDeleteList(
364 DeleteList *deleteList,
367 static void MoveToTrash(
370 Boolean do_verify_checks,
372 static void RestoreFromTrash(
378 Boolean CheckedAlready) ;
379 static void EmptyTrash(
380 DeleteList *del_list,
384 static int CheckDeletePermissionRecur(
386 static int FileSysType(int dev);
387 static void RestoreVerifyOk(
389 XtPointer client_data,
390 XtPointer call_data ) ;
391 static void RestoreVerifyCancel(
393 XtPointer client_data,
394 XtPointer call_data ) ;
395 static int RestoreObject(
401 void (*errorHandler)(),
402 Boolean checkForBusyDir,
404 Boolean CheckedAlready);
405 static void CreateRestoreDialog(
409 /******** End Static Function Declarations ********/
412 TrashIsInitialized( void )
414 return( TrashInitialized );
418 InitializeTrash( Boolean enableVerifyPrompt )
421 struct stat statInfo;
423 /* Build the 'TRASH' directory */
424 trash_dir = XtMalloc(strlen(users_home_dir) +
427 sprintf(trash_dir, "%s%s", users_home_dir, TRASH_DIR);
428 if (stat(trash_dir, &statInfo) < 0)
429 mkdir(trash_dir, S_IRUSR | S_IWUSR | S_IXUSR |
430 S_IRGRP | S_IWGRP | S_IXGRP |
431 S_IROTH | S_IWOTH | S_IXOTH);
433 /* build path to .trashinfo file */
434 TrashInfoFileName = XtMalloc(strlen(users_home_dir) + strlen(TRASH_INFO_FILE) + 1);
435 sprintf(TrashInfoFileName, "%s%s", users_home_dir, TRASH_INFO_FILE);
438 NewTrashInfoFileName = XtMalloc(strlen(users_home_dir) +
439 strlen(NEW_TRASH_INFO_FILE)
441 sprintf(NewTrashInfoFileName, "%s%s", users_home_dir, NEW_TRASH_INFO_FILE);
443 /* Keep track of whether to prompt for user verification */
444 verifyPromptsEnabled = enableVerifyPrompt;
447 * Create an array of paths and filenames which we will not allow the
448 * user to delete, because deleting any of them will cause trash to
451 sacred_dir_list_size = 5;
452 sacred_dir_list = (char **)XtMalloc(sizeof(char *) * sacred_dir_list_size);
454 sacred_dir_list[sacred_dir_count++] = XtNewString("/");
455 sacred_dir_list[sacred_dir_count++] = XtNewString(TrashInfoFileName);
457 ptr = TrashInfoFileName + 1;
458 while(ptr = DtStrchr(ptr, '/'))
460 /* All parent components of the user's home dir cannot be deleted */
462 if (sacred_dir_count >= sacred_dir_list_size)
465 sacred_dir_list_size += 5;
466 sacred_dir_list = (char **)XtRealloc((char *)sacred_dir_list,
467 sizeof(char *) * sacred_dir_list_size);
469 sacred_dir_list[sacred_dir_count++] = XtNewString(TrashInfoFileName);
474 /* load and verify existence for files previously left in the trash can */
475 TrashInitialized = ReadTrashList( );
476 return( TrashInitialized );
479 /************************************************************************
482 * This function must be called before any other interatctions with the
483 * trash can. It creates the trash dialog, opens the trash file, and
484 * loads information about any files previously left in the trash can.
486 * This function is called from main().
488 ************************************************************************/
496 DialogData * dialog_data;
497 FileMgrData * file_mgr_data;
499 if( ! TrashInitialized )
502 /* Create the trash dialog window */
503 /* load trash title */
504 tmpStr = GETMESSAGE(27, 9, "Trash Can");
505 title = XtNewString(tmpStr);
507 /* Initially, no items can be selected */
508 if (removeBtn != NULL)
509 XtSetSensitive(*removeBtn, False);
510 if (restoreBtn != NULL)
511 XtSetSensitive(*restoreBtn, False);
513 /* retrieve the default data for a file manager dialog -- */
514 /* the TrashView flag affects this data */
516 dialog_data = _DtGetDefaultDialogData (file_mgr_dialog);
517 file_mgr_data = (FileMgrData *) dialog_data->data;
518 trashFileMgrData = file_mgr_data;
519 trashDialogData = dialog_data;
521 /* build up directory set for trash directory */
522 FileMgrBuildDirectories (file_mgr_data, home_host_name, trash_dir);
524 /* initialize trash data */
525 file_mgr_data->restricted_directory =
526 XtNewString(file_mgr_data->current_directory);
527 file_mgr_data->title = XtNewString(title);
528 file_mgr_data->toolbox = False;
529 file_mgr_data->width = 300;
530 file_mgr_data->height = 300;
532 /* load any positioning information */
533 LoadPositionInfo(file_mgr_data);
535 /* build the trash dialog */
536 _DtBuildDialog (NULL, NULL, NULL, dialog_data, NULL, NULL, NULL,
537 NULL, NULL, False, special_view, title, NULL);
545 /************************************************************************
548 * At startup time, we need to read in the trash information file, to
549 * find out what, if any, files were left in the trash the last time
550 * this client was run. Each entry must be read in and then verified
551 * that it still exists; if it no longer exists, then someone has been
552 * mucking with our trash files, so we will remove it from our knowledge
553 * base. Otherwise, the file is added to the trash list.
555 ************************************************************************/
558 ReadTrashList( void )
560 int intSize, extSize, bufSize;
561 FILE * trashInfoFileId;
562 String external, intName, internal;
566 trashInfoFileId = fopen(TrashInfoFileName, "a+");
567 if (trashInfoFileId == 0)
571 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
573 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.") )
574 + strlen( TrashInfoFileName )
577 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 );
579 _DtMessage(toplevel, title, msg, NULL, HelpRequestCB);
585 chmod(TrashInfoFileName, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
587 bufSize = MAX_PATH * 2;
588 trashEntry = (char *)XtCalloc( 1, bufSize );
590 /* Extract the size of the two path strings */
591 while( fgets( trashEntry, bufSize, trashInfoFileId ) != NULL )
593 int len = strlen( trashEntry );
595 if( sscanf( trashEntry, "%d %d", &extSize, &intSize ) == 2 )
597 external = (char *)XtCalloc( 1, extSize + 1 );
598 intName = (char *)XtCalloc( 1, intSize + 1 );
600 if( len > extSize + intSize + 3 )
601 /* extSize + intSize + 3 is a rough estimate of a trash entry
602 a trash entry looks something like the following:
606 extSize is the size of the string '/tmp/abc'
607 intSize is the size of the string 'abcd'
609 so the len (the string length of the trash entry has AT LEAST to be
610 bigger than extSize + intSize + 3 separator spaces
615 /* sscanf can be used to reduce the code.
616 I'm not using it here because I don't have time to research.
617 I just want it to work.
619 tmpPtr = DtStrchr( trashEntry, ' ' ) + 1;
620 tmpPtr = DtStrchr( tmpPtr, ' ' ) + 1;
622 /* Extract the full external and partial internal file names */
623 memcpy( external, tmpPtr, extSize );
624 memcpy( intName, tmpPtr + extSize + 1, intSize );
626 /* Create internal/trash name */
627 internal = (char *)XtMalloc(strlen(users_home_dir)
629 + strlen(intName) + 2);
630 sprintf(internal, "%s%s/%s", users_home_dir, TRASH_DIR, intName);
632 /* Make sure the file still exists */
633 if (lstat(internal, &statbuf) < 0)
635 /* File no longer exists; ignore it */
642 /* Add to trash list */
643 if (numTrashItems >= trashListSize)
646 trashCan = (TrashEntry *)XtRealloc((char *) trashCan,
647 sizeof(TrashEntry) * trashListSize);
650 trashCan[numTrashItems].intNew = internal;
651 trashCan[numTrashItems].intOrig = XtNewString(external);
652 trashCan[numTrashItems].external = external;
653 trashCan[numTrashItems].filename = intName;
655 } /* end if file exists */
664 fclose(trashInfoFileId);
666 return( WriteTrashEntries() );
671 /************************************************************************
674 * This function is responsible for creating a new copy of the trash
675 * information file. It will open a new, temporary copy, and will
676 * copy in the contents of the trash can. It will then remove the
677 * old trash information file, and will rename the new one, opening
678 * a handle to it for later use.
680 ************************************************************************/
683 WriteTrashEntries( void )
685 static String path = NULL;
689 newFile = fopen(NewTrashInfoFileName, "w+");
692 chmod(NewTrashInfoFileName,
693 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
695 /* Write all remaining entries */
696 for (i = 0; i < numTrashItems; i++)
698 if( WriteEntry(newFile, trashCan[i].external, trashCan[i].filename) < 0 )
701 remove(NewTrashInfoFileName);
706 /* Remove the original information file, and move the new one */
707 (void) fclose(newFile);
708 if(-1 == chown(NewTrashInfoFileName, getuid(), getgid())) {
711 (void) remove(TrashInfoFileName);
712 (void) rename(NewTrashInfoFileName, TrashInfoFileName);
713 if(-1 == chown(TrashInfoFileName, getuid(), getgid())) {
724 /************************************************************************
727 * This function writes an entry to the trash information file.
728 * It expects the full path name for the original file [external]
729 * and only the new file name (without path information) [internal].
730 * If the file ends in '/', then it adds a '.' to the end; this is
731 * a special case, so that when we parse this information later, we
732 * are guaranteed to have a directory and a file name.
734 ************************************************************************/
742 return( fprintf(id, "%ld %ld %s %s\n",
743 (long)strlen(external), (long)strlen(internal),
744 external, internal) );
749 /************************************************************************
753 ************************************************************************/
758 FileMgrRec *file_mgr_rec )
763 static Widget * directoryBarBtn;
765 /* Create the menubar hierarchy */
766 trashMenu = (MenuDesc *)XtMalloc(sizeof(MenuDesc) * trashMenuItemCount);
769 for (i = 0; i < trashMenuItemCount; i++)
771 trashMenu[i].helpCallback = TrashHelpRequestCB;
772 trashMenu[i].helpData = NULL;
773 trashMenu[i].activateCallback = NULL;
774 trashMenu[i].activateData = NULL;
775 trashMenu[i].maskBit = 0;
776 trashMenu[i].isHelpBtn = False;
777 trashMenu[i].label = NULL;
778 trashMenu[i].mnemonic = NULL;
782 /*************************************/
783 /* Create the 'File' menu components */
784 /*************************************/
786 directoryBarBtn = &(trashMenu[j].widget);
787 trashMenu[j].type = MENU_PULLDOWN_BUTTON;
788 trashMenu[j].label = GETMESSAGE(20,1, "File");
789 trashMenu[j].label = XtNewString(trashMenu[j].label);
790 trashMenu[j].mnemonic = GETMESSAGE(20,2, "F");
791 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
792 trashMenu[j].name = "file_trash";
793 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
795 trashMenu[j].type = SHARED_MENU_PANE;
796 trashMenu[j].name = "fileMenuTrash";
797 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
799 selectAllBtn = &(trashMenu[j].widget);
800 trashMenu[j].type = MENU_BUTTON;
801 trashMenu[j].label = GETMESSAGE(20,48, "Select All");
802 trashMenu[j].label = XtNewString(trashMenu[j].label);
803 trashMenu[j].mnemonic = GETMESSAGE(20,51, "S");
804 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
805 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
806 trashMenu[j].name = "selectAll";
807 trashMenu[j++].activateCallback = Select_All;
809 trashMenu[j].type = MENU_BUTTON;
810 trashMenu[j].label = GETMESSAGE(20,52, "Deselect All");
811 trashMenu[j].label = XtNewString(trashMenu[j].label);
812 trashMenu[j].mnemonic = GETMESSAGE(20,55, "D");
813 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
814 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
815 trashMenu[j].name = "deselectAll";
816 trashMenu[j++].activateCallback = Unselect_All;
818 trashMenu[j].type = MENU_SEPARATOR;
819 trashMenu[j].name = "separator";
820 trashMenu[j++].helpCallback = NULL;
822 restoreBtn = &(trashMenu[j].widget);
823 trashMenu[j].type = MENU_BUTTON;
824 trashMenu[j].label = GETMESSAGE(27,24, "Put back");
825 trashMenu[j].label = XtNewString(trashMenu[j].label);
826 trashMenu[j].mnemonic = GETMESSAGE(27,26, "P");
827 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
828 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
829 trashMenu[j].name = "putBack";
830 trashMenu[j++].activateCallback = Restore;
832 removeBtn = &(trashMenu[j].widget);
833 trashMenu[j].type = MENU_BUTTON;
834 trashMenu[j].label = GETMESSAGE(27,28, "Shred");
835 trashMenu[j].label = XtNewString(trashMenu[j].label);
836 trashMenu[j].mnemonic = GETMESSAGE(27,30, "h");
837 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
838 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
839 trashMenu[j].name = "shred";
840 trashMenu[j++].activateCallback = ConfirmRemove;
842 trashMenu[j].type = MENU_SEPARATOR;
843 trashMenu[j].name = "separator";
844 trashMenu[j++].helpCallback = NULL;
846 trashMenu[j].type = MENU_BUTTON;
847 trashMenu[j].maskBit = PREFERENCES;
848 trashMenu[j].label = GETMESSAGE(20,141, "Set View Options ...");
849 trashMenu[j].label = XtNewString(trashMenu[j].label);
850 trashMenu[j].mnemonic = GETMESSAGE(20,6, "V");
851 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
852 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
853 trashMenu[j].name = "setPreferences";
854 trashMenu[j++].activateCallback = ShowPreferencesDialog;
856 trashMenu[j].type = MENU_SEPARATOR;
857 trashMenu[j].name = "separator";
858 trashMenu[j++].helpCallback = NULL;
860 trashMenu[j].type = MENU_BUTTON;
861 trashMenu[j].label = GETMESSAGE(20,117, "Close");
862 trashMenu[j].label = XtNewString(trashMenu[j].label);
863 trashMenu[j].mnemonic = GETMESSAGE(20,118, "C");
864 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
865 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
866 trashMenu[j].name = "close";
867 trashMenu[j++].activateCallback = CloseTrash;
870 /*************************************/
871 /* Create the 'Help' menu components */
872 /*************************************/
874 trashMenu[j].type = MENU_PULLDOWN_BUTTON;
875 trashMenu[j].label = GETMESSAGE(20,123, "Help");
876 trashMenu[j].label = XtNewString(trashMenu[j].label);
877 trashMenu[j].mnemonic = GETMESSAGE(20,9, "H");
878 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
879 trashMenu[j].isHelpBtn = True;
880 trashMenu[j].name = "help_trash";
881 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
883 trashMenu[j].type = SHARED_MENU_PANE;
884 trashMenu[j].name = "help_pane_trash";
885 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
887 trashMenu[j].type = MENU_BUTTON;
888 trashMenu[j].label = GETMESSAGE(20,105, "Overview");
889 trashMenu[j].label = XtNewString(trashMenu[j].label);
890 trashMenu[j].mnemonic = GETMESSAGE(20,106, "v");
891 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
892 trashMenu[j].helpData = HELP_HELP_MENU_STR;
893 trashMenu[j].activateData = HELP_TRASH_OVERVIEW_TOPIC_STR;
894 trashMenu[j].name = "introduction";
895 trashMenu[j++].activateCallback = TrashHelpRequestCB;
897 trashMenu[j].type = MENU_SEPARATOR;
898 trashMenu[j].name = "separator";
899 trashMenu[j++].helpCallback = NULL;
901 trashMenu[j].type = MENU_BUTTON;
902 trashMenu[j].label = GETMESSAGE(20,107, "Tasks");
903 trashMenu[j].label = XtNewString(trashMenu[j].label);
904 trashMenu[j].mnemonic = GETMESSAGE(20,108, "T");
905 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
906 trashMenu[j].helpData = HELP_HELP_MENU_STR;
907 trashMenu[j].activateData = HELP_TRASH_TASKS_TOPIC_STR;
908 trashMenu[j].name = "tasks";
909 trashMenu[j++].activateCallback = TrashHelpRequestCB;
911 trashMenu[j].type = MENU_BUTTON;
912 trashMenu[j].label = GETMESSAGE(20,109, "Reference");
913 trashMenu[j].label = XtNewString(trashMenu[j].label);
914 trashMenu[j].mnemonic = GETMESSAGE(20,110, "R");
915 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
916 trashMenu[j].helpData = HELP_HELP_MENU_STR;
917 trashMenu[j].activateData = HELP_TRASH_DIALOG_STR;
918 trashMenu[j].name = "reference";
919 trashMenu[j++].activateCallback = TrashHelpRequestCB;
921 trashMenu[j].type = MENU_BUTTON;
922 trashMenu[j].label = GETMESSAGE(20,111, "On Item");
923 trashMenu[j].label = XtNewString(trashMenu[j].label);
924 trashMenu[j].mnemonic = GETMESSAGE(20,112, "O");
925 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
926 trashMenu[j].helpData = HELP_HELP_MENU_STR;
927 trashMenu[j].activateData = HELP_HELP_MODE_STR;
928 trashMenu[j].name = "onItem";
929 trashMenu[j++].activateCallback = TrashHelpRequestCB;
931 trashMenu[j].type = MENU_SEPARATOR;
932 trashMenu[j].name = "separator";
933 trashMenu[j++].helpCallback = NULL;
935 usingHelpTrash = &(trashMenu[j].widget);
936 trashMenu[j].type = MENU_BUTTON;
937 trashMenu[j].label = GETMESSAGE(20,113, "Using Help");
938 trashMenu[j].label = XtNewString(trashMenu[j].label);
939 trashMenu[j].mnemonic = GETMESSAGE(20,114, "U");
940 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
941 trashMenu[j].helpData = HELP_HELP_MENU_STR;
942 trashMenu[j].activateData = HELP_HOME_TOPIC;
943 trashMenu[j].name = "usingHelp";
944 trashMenu[j++].activateCallback = TrashHelpRequestCB;
946 trashMenu[j].type = MENU_SEPARATOR;
947 trashMenu[j].name = "separator";
948 trashMenu[j++].helpCallback = NULL;
950 trashMenu[j].type = MENU_BUTTON;
951 trashMenu[j].label = GETMESSAGE(27,106, "About Trash Can");
952 trashMenu[j].label = XtNewString(trashMenu[j].label);
953 trashMenu[j].mnemonic = GETMESSAGE(20,116, "A");
954 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
955 trashMenu[j].helpData = HELP_HELP_MENU_STR;
956 trashMenu[j].activateData = HELP_ABOUT_STR;
957 trashMenu[j].name = "version";
958 trashMenu[j++].activateCallback = TrashHelpRequestCB;
960 menu_bar = _DtCreateMenuSystem(mainw, "menu_bar_trash", TrashHelpRequestCB,
961 HELP_TRASH_DIALOG_STR, True,
962 trashMenu, trashMenuItemCount, NULL,
965 /* Fine tune the menubar */
966 XtSetArg(args[0], XmNmarginWidth, 2);
967 XtSetArg(args[1], XmNmarginHeight, 2);
968 XtSetValues(menu_bar, args, 2);
970 file_mgr_rec->actions = NULL;
971 file_mgr_rec->action_pane = NULL;
972 file_mgr_rec->directoryBarBtn = *directoryBarBtn;
979 /************************************************************************
981 * TrashDisplayHandler
982 * This is the ICCCM message handler for the message used to display
983 * the trash can window. If the window is already displayed, then
984 * this call is a no-op.
986 * This handler is triggered by the front panel trash icon and by the
987 * File pulldown 'Show Trash' option.
989 ************************************************************************/
999 tt_message_reply( msg );
1000 tttk_message_destroy( msg );
1003 if (!TrashInitialized)
1005 char *tmpStr, *tmpTitle, *tmpMsg;
1007 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1008 tmpTitle = XtNewString(tmpStr);
1009 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.");
1010 tmpMsg = XtNewString(tmpStr);
1012 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1019 #ifdef DEFER_TRASH_CREATION
1020 if( trashFileMgrData == NULL )
1021 TrashCreateDialog (XtDisplay(toplevel));
1024 /* the encapsulation functions do not set file_mgr_rec until a dialog */
1025 /* is actually displayed */
1026 if (trashFileMgrData->file_mgr_rec == 0)
1028 static Pixmap trash_icon = XmUNSPECIFIED_PIXMAP;
1029 static Pixmap trash_mask = XmUNSPECIFIED_PIXMAP;
1031 Pixel background, foreground, top_shadow, bottom_shadow, select;
1033 XClassHint classHints;
1034 FileMgrRec * file_mgr_rec;
1036 unsigned int height;
1040 classHints.res_name = trashFileMgrData->title;
1041 classHints.res_class = DTFILE_CLASS_NAME;
1044 _DtShowBuiltDialog(NULL,NULL, trashDialogData, NULL, False, &classHints);
1047 file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
1049 trashShell = file_mgr_rec->shell;
1051 if (trash_icon == XmUNSPECIFIED_PIXMAP)
1053 XtSetArg (args[0], XmNbackground, &background);
1054 XtSetArg (args[1], XmNcolormap, &colormap);
1055 XtGetValues (file_mgr_rec->main, args, 2);
1057 XmGetColors (XtScreen (file_mgr_rec->main), colormap, background,
1058 &foreground, &top_shadow, &bottom_shadow, &select);
1060 pixmap = XmGetPixmap (XtScreen (file_mgr_rec->main), trashIcon,
1061 foreground, background);
1062 if( pixmap != XmUNSPECIFIED_PIXMAP)
1063 trash_icon = pixmap;
1065 /* now let's get the mask for the File Manager */
1066 pixmap = _DtGetMask (XtScreen (file_mgr_rec->main), trashIcon);
1067 if( pixmap != XmUNSPECIFIED_PIXMAP)
1068 trash_mask = pixmap;
1070 if (trash_icon != XmUNSPECIFIED_PIXMAP)
1072 XtSetArg (args[0], XmNiconPixmap, trash_icon);
1073 if(trash_mask != XmUNSPECIFIED_PIXMAP)
1075 XtSetArg (args[1], XmNiconMask, trash_mask);
1076 XtSetValues (trashShell, args, 2);
1079 XtSetValues (trashShell, args, 1);
1087 root = RootWindowOfScreen (XtScreen (trashShell));
1089 /* Change the hints to reflect the current workspace */
1090 /* and raise the window */
1092 if (DtWsmGetCurrentWorkspace (XtDisplay (trashShell),
1093 root, ¤t_ws) == Success)
1094 DtWsmSetWorkspacesOccupied (XtDisplay (trashShell),
1095 XtWindow (trashShell), ¤t_ws, 1);
1097 /* Set the iconify state */
1098 /* Remove the iconify hint from the current shell */
1099 wmhints = XGetWMHints(XtDisplay(trashShell), XtWindow(trashShell));
1100 wmhints->flags |= IconWindowHint;
1101 wmhints->initial_state = NormalState;
1102 XSetWMHints(XtDisplay(trashShell), XtWindow(trashShell), wmhints);
1106 XtPopup (trashShell, XtGrabNone);
1107 XSync(XtDisplay(trashShell), False);
1108 XRaiseWindow (XtDisplay (trashShell), XtWindow (trashShell));
1109 XMapWindow( XtDisplay (trashShell), XtWindow (trashShell) );
1112 msg = tt_pnotice_create(TT_SESSION, "DtActivity_Began");
1113 tt_message_send(msg);
1114 tttk_message_destroy(msg);
1118 trashDialogPosted = True;
1122 /************************************************************************
1124 * ConfirmEmptyCancel
1126 ************************************************************************/
1127 static XtPointer trash_popup_client_data;
1131 XtPointer client_data,
1132 XtPointer call_data )
1134 XtUnmanageChild((Widget)client_data);
1135 XmUpdateDisplay((Widget)client_data);
1136 XtDestroyWidget((Widget)client_data);
1139 /************************************************************************
1143 ************************************************************************/
1148 XtPointer client_data,
1149 XtPointer call_data )
1151 /* destroy dialog */
1152 XtUnmanageChild((Widget)client_data);
1153 XmUpdateDisplay((Widget)client_data);
1154 XtDestroyWidget((Widget)client_data);
1159 /************************************************************************
1162 * This is a message interface used to really remove all of the
1165 * This handler is only called from an empty trash action.
1167 ************************************************************************/
1174 tt_message_reply( msg );
1175 tttk_message_destroy( msg );
1178 if(!TrashInitialized)
1180 char *tmpStr, *tmpTitle, *tmpMsg;
1182 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1183 tmpTitle = XtNewString(tmpStr);
1184 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.");
1185 tmpMsg = XtNewString(tmpStr);
1187 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1195 char *tmpStr, *title, *msg;
1197 tmpStr = GETMESSAGE(27,73, "Shred File(s)");
1198 title = XtNewString(tmpStr);
1199 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");
1200 msg = XtNewString(tmpStr);
1202 _DtMessageDialog(toplevel, title, msg, NULL, TRUE, ConfirmEmptyCancel,
1203 ConfirmEmptyOk, NULL, HelpRequestCB, False, QUESTION_DIALOG);
1212 /************************************************************************
1215 * This is a functional interface used to really remove all of the
1218 ************************************************************************/
1223 if (trashFileMgrData)
1226 Select_All(*selectAllBtn, NULL, NULL);
1227 Remove(*removeBtn, NULL, NULL);
1229 EmptyDir(trashFileMgrData->current_directory,0,0);
1235 /************************************************************************
1238 * This is the callback attached to the 'Select All' menu item. It
1239 * will select all of the files currently resident in the trash can.
1240 * It will also sensitize the 'Restore' and 'Remove' buttons, as long
1241 * as a remove request is not currently in the middle of being processed.
1243 ************************************************************************/
1248 XtPointer client_data,
1249 XtPointer call_data )
1252 SelectAllFiles(trashFileMgrData);
1253 SensitizeTrashBtns();
1258 /************************************************************************
1261 * This is the callback attached to the 'Unselect All' menu item. It
1262 * will unselect all of the items in the trash can, and will clear
1263 * out our selection array. It also desensitizes the 'Restore' and
1266 ************************************************************************/
1271 XtPointer client_data,
1272 XtPointer call_data )
1275 DeselectAllFiles(trashFileMgrData);
1276 SensitizeTrashBtns();
1281 /************************************************************************
1283 * SensitizeTrashBtns
1285 ************************************************************************/
1288 SensitizeTrashBtns(void)
1290 #ifdef DEFER_TRASH_CREATION
1291 if( trashFileMgrData
1296 if (trashFileMgrData->selected_file_count > 0)
1298 XtSetSensitive(*removeBtn, True);
1299 XtSetSensitive(*restoreBtn, True);
1303 XtSetSensitive(*removeBtn, False);
1304 XtSetSensitive(*restoreBtn, False);
1311 /************************************************************************
1313 * TrashRemoveHandler
1314 * This is the message handler for the message requesting that
1315 * a set of files be added to the trashcan. Each file name comes in
1316 * in /dir/name format; they are all in a single string, separated
1319 * Check for spaces in filenames and destop files. If we pass these
1320 * cases, call TrashRemover where the following occurs:
1322 * All of the files will be moved to the trash can directory; any
1323 * problems encountered will be reported in an error dialog. Each file
1324 * which is successfully moved, will be added to the trash information file.
1326 ************************************************************************/
1339 int msglistsize = 0 ;
1340 #endif /* SUN_PERF */
1343 * Note: There used to be a call to tt_message_reply() right here at the
1344 * beginning of the routine. However, the mailer wants to trash files
1345 * but wants the notification after the trash has been removed.
1346 * Therefore, we now put this off until the work is done or an error
1350 if( !TrashInitialized )
1352 char *tmpStr, *tmpTitle, *tmpMsg;
1354 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1355 tmpTitle = XtNewString(tmpStr);
1356 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.");
1357 tmpMsg = XtNewString(tmpStr);
1359 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1364 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
1366 global_msg_cnt = 0 ;
1367 #endif /* SUN_PERF */
1371 MessageToFileList(msg, &file_list, &file_count);
1374 * When the Dttrash_Remove handler "TrashRemoveHandler" is invoked for
1375 * the first time it caches the TT request and then invokes
1376 * tttk_block_while() with timeout = 0. This in effect causes the next
1377 * TT request to be processed before tttk_block_while() returns. Now if
1378 * TT msgs are coming in faster than dtfile is handling them, then the
1379 * next TT msg. should be another Dttrash_Remove msg. which will cause
1380 * re-entrance into TrashRemoveHandler. This will continue until the last
1381 * Dttrash_Remove msg. has been processed (and the recursive
1382 * TrashRemoveHandler starts unwinding) after which we check if the
1383 * number of TT msgs are > 0 in which case we fire off the
1384 * process to move the list of files to Trash.
1387 msglistsize = sizeof(Tt_message) * global_msg_cnt ;
1388 global_msg_list = (Tt_message *)XtRealloc((char *)global_msg_list, msglistsize);
1389 memcpy((char *)&(global_msg_list[global_msg_cnt - 1]),
1390 (char *)&msg, sizeof(Tt_message)) ;
1391 tttk_block_while(0, 0, time_out) ;
1393 /* If we get to this pt. then either :
1394 * 1. No more Dttrash_Remove msgs. left to process. Thus we should
1395 * process the TT msg list and move files to Trash.
1397 * 2. We got a msg. different from Dttrash_Remove. In this case
1398 * we process the msgs. we have so far. The remaining files to
1399 * be trashed will be buffered in another list.
1402 if (global_msg_cnt > 0) {
1403 MessagesToFileList(global_msg_list, global_msg_cnt,
1404 &file_list, &file_count);
1405 #endif /* SUN_PERF */
1407 if (file_count == 0)
1409 tt_message_reply( msg );
1410 tttk_message_destroy( msg );
1412 global_msg_cnt = 0 ;
1413 #endif /* SUN_PERF */
1417 /* return if from the trash dir */
1418 if (strncmp(file_list[0], trash_dir, strlen(trash_dir)) == 0)
1422 for (i = 0; i < file_count; i++) {
1423 XtFree( file_list[i] );
1425 XtFree( (char *)file_list );
1426 tt_message_status_set( msg, TT_DESKTOP_EALREADY );
1427 tt_message_reply( msg );
1428 tttk_message_destroy( msg );
1430 global_msg_cnt = 0 ;
1431 #endif /* SUN_PERF */
1435 /* post message if file(s) from desktop */
1439 str=(char *)IsAFileOnDesktop2(file_list, file_count, &number,&IsToolBox);
1440 /* IsToolBox is unused here, but is required to satisfy the prototype */
1444 MoveToTrash(file_list, file_count, verifyPromptsEnabled, msg);
1454 /* List will be regenerated by callbacks */
1455 for (i = 0; i < file_count; i++) {
1456 XtFree( file_list[i] );
1458 XtFree( (char *)file_list );
1460 tmpStr = (GETMESSAGE(27,73, "Shred File(s)"));
1461 title = XtNewString(tmpStr);
1465 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."));
1467 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."));
1468 message = (char *)XtMalloc(strlen(template) + strlen(str) + 1);
1469 sprintf(message, template, str);
1474 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."));
1476 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."));
1477 message = (char *)XtMalloc(strlen(template) + strlen(str) + 1);
1478 sprintf(message, template, str);
1481 /* Really ought to pass to OK and Cancel CBs via client_data */
1484 _DtMessageDialog(toplevel, title, message, NULL, TRUE,
1485 RemoveCancelCB, RemoveOkCB, NULL, HelpRequestCB, False,
1491 Tt_msg_cache *msg_cache ;
1493 msg_cache = (Tt_msg_cache *)XtMalloc(sizeof(Tt_msg_cache)) ;
1494 msg_cache->msg_cnt = global_msg_cnt ;
1496 msg_cache->msg_list = (Tt_message *)XtMalloc(sizeof(Tt_message) *
1497 msg_cache->msg_cnt );
1498 msg_cache->msg_list[msg_cache->msg_cnt] = NULL ;
1499 memcpy((char *)msg_cache->msg_list, (char *)global_msg_list,
1500 sizeof(Tt_message) * msg_cache->msg_cnt ) ;
1501 dlog = (Widget)_DtMessageDialog(toplevel, title, message, NULL, TRUE,
1502 RemoveCancelCB, RemoveOkCB, NULL, HelpRequestCB, False,
1504 XtSetArg ( args[0], XmNuserData, msg_cache );
1505 XtSetValues ( dlog, args, 1);
1507 #endif /* SUN_PERF */
1514 global_msg_cnt = 0 ;
1516 #endif /* SUN_PERF */
1521 /************************************************************************
1524 * Cleanup and unmanage the remove from DT dialog
1526 ************************************************************************/
1531 XtPointer client_data,
1532 XtPointer call_data )
1538 Tt_msg_cache *current_msg_cache ;
1541 XtSetArg( args[0], XmNuserData, ¤t_msg_cache );
1542 XtGetValues( (Widget)client_data, args, 1 );
1543 #endif /* SUN_PERF */
1545 /* destroy dialog */
1546 XtUnmanageChild((Widget)client_data);
1547 XmUpdateDisplay((Widget)client_data);
1548 XtDestroyWidget((Widget)client_data);
1552 MessageToFileList(global, &file_list, &file_count);
1554 MessagesToFileList( current_msg_cache->msg_list, current_msg_cache->msg_cnt,
1555 &file_list, &file_count);
1556 #endif /* SUN_PERF */
1558 MoveToTrash(file_list, file_count, verifyPromptsEnabled, NULL);
1560 /* reread desktop files */
1564 tt_message_reply( global );
1565 tttk_message_destroy( global );
1568 for (i = 0 ; i < current_msg_cache->msg_cnt ; i++) {
1569 tt_message_reply( current_msg_cache->msg_list[i] );
1570 tttk_message_destroy( current_msg_cache->msg_list[i] );
1572 XtFree ((char *)current_msg_cache->msg_list);
1573 XtFree ((char *)current_msg_cache);
1574 #endif /* SUN_PERF */
1579 /************************************************************************
1582 * Cleanup and unmanage the remove from DT dialog
1584 ************************************************************************/
1589 XtPointer client_data,
1590 XtPointer call_data )
1594 Tt_msg_cache *current_msg_cache ;
1597 XtSetArg( args[0], XmNuserData, ¤t_msg_cache );
1598 XtGetValues( (Widget)client_data, args, 1 );
1599 #endif /* SUN_PERF */
1601 /* destroy dialog */
1602 XtUnmanageChild((Widget)client_data);
1603 XmUpdateDisplay((Widget)client_data);
1604 XtDestroyWidget((Widget)client_data);
1607 tttk_message_fail( global, TT_DESKTOP_ECANCELED, 0, 1 );
1610 for (i = 0 ; i < current_msg_cache->msg_cnt ; i++)
1611 tttk_message_fail( current_msg_cache->msg_list[i], TT_DESKTOP_ECANCELED, 0, 1 );
1612 XtFree ((char *)current_msg_cache->msg_list);
1613 XtFree ((char *)current_msg_cache);
1614 #endif /* SUN_PERF */
1619 /************************************************************************
1621 * TrashRemoveNoConfirmHandler
1622 * This function is a message handler. It will place the specified set
1623 * of files into the trash can, without requesting confirmation if, for
1624 * instance, a file does not have write permission, or a directory is
1627 ************************************************************************/
1630 TrashRemoveNoConfirmHandler(
1636 if( !TrashInitialized )
1638 char *tmpStr, *tmpTitle, *tmpMsg;
1640 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1641 tmpTitle = XtNewString(tmpStr);
1642 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.");
1643 tmpMsg = XtNewString(tmpStr);
1645 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1651 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
1655 MessageToFileList(msg, &file_list, &file_count);
1656 MoveToTrash(file_list, file_count, False, msg);
1661 /************************************************************************
1665 * Parses a list of file names from a message
1666 * and returns an array of strings.
1668 * if (no message arguments) {
1669 * if (no file attribute) {
1670 * return length==0 list
1672 * return length==1 list containing file attribute
1675 * if (file attribute) {
1676 * file attribute is a directory, prepended onto subsequent filenames
1678 * if (arg 0 is non-null) {
1679 * arg 0 interpreted as a space-separated list of filenames
1682 * arg interpreted as a filename possibly containing spaces
1686 ************************************************************************/
1702 args = tt_message_args_count( msg );
1703 if (tt_is_err(tt_int_error( args ))) {
1707 dir = tt_message_file( msg );
1708 if ((tt_is_err(tt_ptr_error( dir ))) || (dir == 0)) {
1712 /* No args means use tt_message_file() as is */
1713 *file_list = (char **)
1714 XtRealloc((char *)(*file_list),
1715 (*file_count + 1)*sizeof(char *));
1716 (*file_list)[*file_count] = XtNewString(dir);
1721 if (dir[strlen(dir)] != '/') {
1723 if (strcmp(dir, "/") == 0)
1725 newdir = (char *)tt_malloc( strlen(dir)+1 );
1726 strcpy( newdir, dir );
1730 newdir = (char *)tt_malloc( strlen(dir)+2 );
1731 strcpy( newdir, dir );
1732 strcat( newdir, "/" );
1738 /* Invariant: dir can now be safely concatenated to form a valid path */
1739 dirlen = strlen(dir);
1740 for (arg = 0; arg < args; arg++) {
1745 val = tt_message_arg_val( msg, arg );
1746 if ((tt_is_err(tt_ptr_error( val ))) || (val == 0)) {
1752 /* Arg 0 is a space-separated list */
1755 for (val = DtStrtok(val, white); val; val = DtStrtok(0, white))
1757 if (strcmp(val, "/") == 0)
1759 file = (char *)XtMalloc( dirlen + 1 );
1760 strcpy( file, dir );
1764 file = (char *)XtMalloc( dirlen + strlen(val) + 1 );
1765 strcpy( file, dir );
1766 strcat( file, val );
1768 *file_list = (char **)
1769 XtRealloc((char *)(*file_list),
1770 (*file_count + 1)*sizeof(char *));
1771 (*file_list)[*file_count] = DtEliminateDots(file);
1774 tt_free( val2free );
1782 Tt_message *msg_list,
1797 for (num_msgs = 0 ; num_msgs < msg_cnt ; num_msgs++)
1799 msg = msg_list[num_msgs] ;
1801 args = tt_message_args_count( msg );
1802 if (tt_is_err(tt_int_error( args ))) {
1806 dir = tt_message_file( msg );
1807 if ((tt_is_err(tt_ptr_error( dir ))) || (dir == 0)) {
1811 /* No args means use tt_message_file() as is */
1812 *file_list = (char **)
1813 XtRealloc((char *)(*file_list),
1814 (*file_count + 1)*sizeof(char *));
1815 (*file_list)[*file_count] = XtNewString(dir);
1820 if (dir[strlen(dir)] != '/') {
1822 if (strcmp(dir, "/") == 0)
1824 newdir = (char *)tt_malloc( strlen(dir)+1 );
1825 strcpy( newdir, dir );
1829 newdir = (char *)tt_malloc( strlen(dir)+2 );
1830 strcpy( newdir, dir );
1831 strcat( newdir, "/" );
1837 /* Invariant: dir can now be safely concatenated to form a valid path */
1838 dirlen = strlen(dir);
1839 for (arg = 0; arg < args; arg++) {
1844 val = tt_message_arg_val( msg, arg );
1845 if ((tt_is_err(tt_ptr_error( val ))) || (val == 0)) {
1851 /* Arg 0 is a space-separated list */
1854 for (val = DtStrtok(val, white); val; val = DtStrtok(0, white))
1856 if (strcmp(val, "/") == 0)
1858 file = (char *)XtMalloc( dirlen + 1 );
1859 strcpy( file, dir );
1861 else if (strcmp(val, "/") == 0 && dirlen == 0)
1863 file = (char *)XtMalloc( strlen(val) + 1 );
1864 strcpy( file, val );
1868 file = (char *)XtMalloc( dirlen + strlen(val) + 1 );
1869 strcpy( file, dir );
1870 strcat( file, val );
1872 *file_list = (char **)
1873 XtRealloc((char *)(*file_list),
1874 (*file_count + 1)*sizeof(char *));
1875 (*file_list)[*file_count] = DtEliminateDots(file);
1878 tt_free( val2free );
1883 #endif /* SUN_PERF */
1886 /************************************************************************
1888 * CreateTrashFilename
1889 * Create trash directory name.
1891 ************************************************************************/
1894 CreateTrashFilename(
1896 Boolean uniqueTest )
1899 char * extension = NULL;
1900 struct stat statInfo;
1902 /* Create trash path */
1903 /* Give that name a little extra cushion, just in case */
1904 trashName = (char *)XtMalloc(strlen(users_home_dir) + strlen(TRASH_DIR) +
1905 strlen(baseName) + 15);
1906 sprintf(trashName, "%s%s/%s", users_home_dir, TRASH_DIR, baseName);
1908 /* Want to find the extension so the new file name created will preserve
1909 its original datatype.
1911 extension = strrchr( baseName, '.' );
1918 /* If a file by the trash name already exists, keep building new names */
1919 /* until one by that name doesn't exist and then use it */
1920 if (uniqueTest && (lstat(trashName, &statInfo) == 0))
1924 for (i = 1; True ; i++)
1926 /* Make a duplicate file name */
1928 sprintf(trashName, "%s%s/%s_%d.%s", users_home_dir, TRASH_DIR,
1929 baseName, i, extension);
1931 sprintf(trashName, "%s%s/%s_%d", users_home_dir, TRASH_DIR,
1934 /* Is the filename taken? */
1935 if (lstat(trashName, &statInfo) != 0)
1939 } /* end if not unique name */
1942 *(extension-1) = '.';
1950 /************************************************************************
1952 * MatchesSacredDirectory
1953 * Checks if the specify filename matches one of the sacred,
1954 * non-deleteable files.
1956 ************************************************************************/
1959 MatchesSacredDirectory(
1964 /* Don't allow the user to delete any of the sacred directories */
1965 for (i = 0; i < sacred_dir_count; i++)
1967 if (!strcmp(file, sacred_dir_list[i]))
1971 /* Compare against special desktop directories */
1972 /* remote_sys_dir is currently NULL */
1973 if (strcmp(file, remote_sys_dir) == 0)
1981 /************************************************************************
1984 * Callback for 'Delete To Trash' verify dialog Ok push button.
1986 ************************************************************************/
1991 XtPointer client_data,
1992 XtPointer call_data )
1994 VerifyCleanup ((Widget)client_data, True);
1999 /************************************************************************
2002 * Callback for 'Delete To Trash' verify dialog Cancel push button.
2004 ************************************************************************/
2009 XtPointer client_data,
2010 XtPointer call_data )
2013 VerifyCleanup ((Widget)client_data, False);
2018 /************************************************************************
2021 * Callback for 'Delete To Trash' verify dialog push buttons.
2023 * Called from VerifyOk and VerifyCancel.
2025 ************************************************************************/
2030 Boolean completeDelete )
2037 /* Unpost dialog, and retrieve the list of verified files */
2038 XtUnmanageChild(mbox);
2039 XmUpdateDisplay(mbox);
2040 XtSetArg(args[0], XmNuserData, &verifylist);
2041 XtGetValues(mbox, args, 1);
2043 /* count the number of files in the list */
2044 for (fileCount = 0; verifylist[fileCount]; fileCount++)
2048 * Start the background process that moves the files into the trash,
2052 MoveToTrash(verifylist, fileCount, False, NULL);
2056 /* Free up the storage we allocated */
2057 for (i = fileCount; i > 0; i--)
2058 XtFree(verifylist[i]);
2059 XtFree((char *)verifylist);
2062 XtDestroyWidget(mbox);
2067 /************************************************************************
2071 ************************************************************************/
2072 static XtPointer trash_popup_client_data;
2076 XtPointer client_data,
2077 XtPointer call_data )
2079 XtUnmanageChild((Widget)client_data);
2080 XmUpdateDisplay((Widget)client_data);
2081 XtDestroyWidget((Widget)client_data);
2083 XtSetSensitive(*removeBtn, True);
2084 XtSetSensitive(*restoreBtn, True);
2085 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
2086 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
2087 trash_popup_client_data = NULL;
2092 /************************************************************************
2096 ************************************************************************/
2101 XtPointer client_data,
2102 XtPointer call_data )
2104 FileViewData *file_view_data;
2106 /* destroy dialog */
2107 XtUnmanageChild((Widget)client_data);
2108 XmUpdateDisplay((Widget)client_data);
2109 XtDestroyWidget((Widget)client_data);
2111 file_view_data = trashFileMgrData->popup_menu_icon;
2112 if(!file_view_data && trashFileMgrData->selected_file_count)
2113 file_view_data = trashFileMgrData->selection_list[0];
2114 trashFileMgrData->popup_menu_icon = NULL;
2116 if(file_view_data != NULL)
2117 Remove(*removeBtn, file_view_data, NULL);
2122 /************************************************************************
2125 * This is the callback attached to the 'Remove' menu item.
2127 ************************************************************************/
2132 XtPointer client_data,
2133 XtPointer call_data )
2135 char *tmpStr, *title, *msg;
2137 /* Desensitize remove & restore buttons, until we're done */
2138 XtSetSensitive(*removeBtn, False);
2139 XtSetSensitive(*restoreBtn, False);
2140 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], False);
2141 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], False);
2144 trash_popup_client_data = client_data;
2146 tmpStr = GETMESSAGE(27,73, "Shred File(s)");
2147 title = XtNewString(tmpStr);
2148 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");
2149 msg = XtNewString(tmpStr);
2151 _DtMessageDialog(toplevel, title, msg, NULL, TRUE, ConfirmCancel, ConfirmOk,
2152 NULL, HelpRequestCB, False, QUESTION_DIALOG);
2160 /************************************************************************
2163 * While we wait for the background process to complete, we will
2164 * desensitize the 'Remove' and 'Restore' menu items, since we don't
2165 * handle multiple requests.
2167 ************************************************************************/
2172 XtPointer client_data,
2173 XtPointer call_data )
2175 FileViewData *file_view_data;
2176 Boolean match = False;
2177 DeleteList *deleteList = NULL;
2183 /* Remove may be called to remove a file or to remove a trash file; */
2184 /* if w is NULL, this is a call to remove a file */
2187 removeType = TRASH_FILE;
2189 /* Count number of items to 'Delete To Trash' */
2190 if (client_data != NULL)
2192 /* This was called by trash popup */
2193 /* need to check to see if it is selected, if it is restore all
2194 the selected files, else just restore this file */
2196 file_view_data = (FileViewData *)client_data;
2197 for (i = 0; i < trashFileMgrData->selected_file_count; i++)
2199 if (strcmp(file_view_data->file_data->file_name,
2200 trashFileMgrData->selection_list[i]->file_data->file_name) == 0)
2208 if (trashFileMgrData->selected_file_count == 0)
2210 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
2211 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
2218 /* Desensitize remove & restore buttons, until we're done */
2219 XtSetSensitive(*removeBtn, False);
2220 XtSetSensitive(*restoreBtn, False);
2224 deleteCount = trashFileMgrData->selected_file_count;
2228 /* Create the list of things being deleted */
2229 deleteList = (DeleteList *)XtMalloc(deleteCount * sizeof(DeleteList));
2233 for (i = 0; i < deleteCount; i++)
2235 AddToDeleteList(deleteList, i,
2236 trashFileMgrData->selection_list[i]->file_data->file_name);
2240 AddToDeleteList(deleteList, 0, file_view_data->file_data->file_name);
2245 removeType = REMOVE_FILE;
2246 deleteList = (DeleteList *)XtMalloc(sizeof(DeleteList));
2248 deleteList[0].trash = XtNewString(client_data);
2249 deleteList[0].orig = NULL;
2252 EmptyTrash(deleteList, deleteCount, removeType, NULL);
2257 /************************************************************************
2260 * Locate a file in the trash list and add it to the delete list.
2262 ************************************************************************/
2266 DeleteList *deleteList,
2272 /* Locate file in trash list, add entry to delete list */
2273 for (j = 0; j < numTrashItems; j++)
2275 if (strcmp(filename, trashCan[j].filename) == 0)
2277 /* file found in trash list */
2278 deleteList[i].trash = XtNewString(trashCan[j].intNew);
2279 deleteList[i].orig = XtNewString(trashCan[j].intOrig);
2285 /* file not found in trash list */
2286 deleteList[i].trash = CreateTrashFilename(filename, FALSE);
2287 deleteList[i].orig = NULL;
2292 /************************************************************************
2294 * TrashRestoreHandler
2295 * This function is a message handler. It will restore the specified set
2296 * of files from the trash can.
2298 ************************************************************************/
2301 TrashRestoreHandler(
2307 if( !TrashInitialized )
2309 char *tmpStr, *tmpTitle, *tmpMsg;
2311 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
2312 tmpTitle = XtNewString(tmpStr);
2313 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.");
2314 tmpMsg = XtNewString(tmpStr);
2316 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
2322 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
2327 /* Desensitize remove & restore buttons, until we're done */
2328 XtSetSensitive(*removeBtn, False);
2329 XtSetSensitive(*restoreBtn, False);
2331 MessageToFileList(msg, &file_list, &file_count);
2332 RestoreFromTrash(file_list, file_count, NULL, NULL, msg,False);
2337 /************************************************************************
2340 * This is the callback attached to the 'Restore' menu item. It will
2341 * remove from the trash all of the selected files, and will restore
2342 * them to their original location. If any problems occur while trying
2343 * to restore any of the files, then an error dialog will be posted.
2345 ************************************************************************/
2350 XtPointer client_data,
2351 XtPointer call_data )
2353 FileViewData *file_view_data;
2354 Boolean match = False;
2359 if (client_data != NULL)
2361 /* This was called by trash popup */
2362 /* need to check to see if it is selected, if it is restore all
2363 the selected files, else just restore this file */
2365 file_view_data = trashFileMgrData->popup_menu_icon;
2367 /* The object would have been deleted if the following condn is true */
2368 if(!file_view_data && !trashFileMgrData->selected_file_count)
2370 trashFileMgrData->popup_menu_icon = NULL;
2372 for (i = 0; i < trashFileMgrData->selected_file_count; i++)
2374 if (strcmp(file_view_data->file_data->file_name,
2375 trashFileMgrData->selection_list[i]->file_data->file_name) == 0)
2384 /* Desensitize remove & restore buttons, until we're done */
2385 XtSetSensitive(*removeBtn, False);
2386 XtSetSensitive(*restoreBtn, False);
2391 file_count = trashFileMgrData->selected_file_count;
2392 file_list = (char **)XtMalloc(file_count * sizeof(char *));
2393 for (i = 0; i < file_count; i++)
2395 file_view_data = trashFileMgrData->selection_list[file_count - 1 - i];
2396 file_list[i] = XtNewString(file_view_data->file_data->file_name);
2402 file_list = (char **)XtMalloc(sizeof(char *));
2403 file_list[0] = XtNewString(file_view_data->file_data->file_name);
2406 /* Start the background process that will do the restore */
2407 RestoreFromTrash(file_list, file_count, NULL, NULL, NULL,False);
2412 /************************************************************************
2415 * This is the callback attached to the 'Close' menu item. It will
2416 * unpost the trash can window.
2418 ************************************************************************/
2423 XtPointer client_data,
2424 XtPointer call_data )
2428 if (trashDialogPosted)
2430 XWithdrawWindow(XtDisplay(trashShell), XtWindow(trashShell),
2431 XDefaultScreen(XtDisplay(trashShell)));
2432 XtPopdown(trashShell);
2436 for (i = 0; i < secondaryTrashHelpDialogCount; i++)
2438 if (_DtIsDialogShowing(secondaryTrashHelpDialogList[i]))
2439 _DtHideDialog(secondaryTrashHelpDialogList[i], False);
2440 _DtFreeDialogData(secondaryTrashHelpDialogList[i]);
2442 XtFree((char *)secondaryTrashHelpDialogList);
2443 secondaryTrashHelpDialogList = NULL;
2444 secondaryTrashHelpDialogCount = 0;
2446 if (primaryTrashHelpDialog)
2448 if (_DtIsDialogShowing(primaryTrashHelpDialog))
2449 _DtHideDialog(primaryTrashHelpDialog, False);
2450 _DtFreeDialogData(primaryTrashHelpDialog);
2452 primaryTrashHelpDialog = NULL;
2454 if (PositionFlagSet(trashFileMgrData))
2455 SavePositionInfo(trashFileMgrData);
2458 FileMgrRec * file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
2460 if( (file_mgr_rec->menuStates & PREFERENCES) == 0 )
2461 file_mgr_rec->menuStates |= PREFERENCES;
2464 trashDialogPosted = False;
2469 /************************************************************************
2473 ************************************************************************/
2480 DtDndDropCallbackStruct *drop_parameters)
2483 DtActionArg * action_args;
2484 FileMgrRec * file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
2485 Position drop_x = drop_parameters->x;
2486 Position drop_y = drop_parameters->y;
2488 _DtBuildActionArgsWithDroppedFiles(NULL, drop_parameters,
2489 &action_args, &arg_count);
2491 DtActionInvoke(file_mgr_rec->shell, TRASH_ACTION,
2492 action_args, arg_count, NULL, NULL,
2493 trashFileMgrData->current_directory, True, NULL, NULL);
2495 _DtFreeActionArgs(action_args, arg_count);
2497 RepositionIcons(trashFileMgrData, file_set, file_count, drop_x,
2503 /************************************************************************
2507 ************************************************************************/
2511 FileMgrData *file_mgr_data,
2512 FileMgrRec *file_mgr_rec,
2526 * Get target host and directory
2528 target_dir = XtNewString(file_mgr_data->current_directory);
2529 target_host = XtNewString(file_mgr_data->host);
2532 * Create file list and call RestoreFromTrash
2534 file_list = (char **)XtMalloc(file_count * sizeof(char *));
2536 for (i = 0; i < file_count; i++)
2537 file_list[i] = XtNewString(file_set[i]);
2539 RestoreFromTrash(file_list, file_count, target_host, target_dir, NULL,False);
2544 /************************************************************************
2547 * Locate file in trash list based on new internal name. Return file name.
2549 ************************************************************************/
2555 Boolean IsTrash = False;
2557 if (strncmp(users_home_dir, filename, strlen(users_home_dir)) == 0)
2560 ((filename + strlen(users_home_dir)), TRASH_DIR, strlen(TRASH_DIR))
2570 /************************************************************************
2573 * Since the trash does not use shared menupanes, there is no work to
2574 * be done during the popup and popdown callbacks; therefore, we use
2575 * and empty function.
2577 ************************************************************************/
2582 XtPointer clientData,
2583 XtPointer callData )
2590 /*--------------------------------------------------------------------
2591 * UpdateDirectoryOf:
2592 * Arrange for the directory containing a file to be updated
2593 *------------------------------------------------------------------*/
2602 /* remove last component from path to get the directory */
2603 ptr = strrchr(path, '/');
2607 strcpy(host, home_host_name);
2609 /* now arrange for the directory to be updated */
2610 UpdateDirectory(NULL, host, path);
2612 /* restore the path */
2618 /*--------------------------------------------------------------------
2619 * EraseObject, EraseDir
2620 * Routines for recursively deleting files and directories
2621 *------------------------------------------------------------------*/
2624 EraseObject(char *file_name)
2626 struct stat stat_buf;
2628 if (lstat(file_name, &stat_buf) < 0)
2631 else if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
2632 return EraseDir(file_name);
2634 else if (remove(file_name) < 0)
2643 EraseDir(char *dir_name)
2645 DIR *dir; /* open directory */
2646 struct dirent *entry; /* directory entry */
2647 char srcname[MAX_PATH];
2651 /* open source directory */
2652 dir = opendir(dir_name);
2656 /* prepare source name */
2657 strcpy(srcname, dir_name);
2658 srclen = strlen(srcname);
2659 if (srcname[srclen - 1] != '/')
2660 srcname[srclen++] = '/';
2663 while (rc == 0 && (entry = readdir(dir)) != NULL)
2665 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
2667 strcpy(srcname + srclen, entry->d_name);
2668 rc = EraseObject(srcname);
2675 if (rmdir(dir_name) < 0)
2677 if (unlink(dir_name) < 0 )
2689 /*====================================================================
2691 * MoveToTrashProcess
2692 * Run a background process to move files to the trash can.
2694 *==================================================================*/
2696 /*--------------------------------------------------------------------
2697 * MoveToTrashProcess
2698 * Main routine of background process for MoveToTrash
2699 *------------------------------------------------------------------*/
2706 Boolean do_verify_checks)
2712 struct stat statInfo;
2714 struct dirent * entry;
2719 for (i = 0; i < file_count; i++)
2721 /* get base name and full path */
2722 path = XtNewString(file_list[i]);
2723 baseName = strrchr(file_list[i], '/');
2724 if (baseName == NULL || path == NULL)
2726 /* Invalid filename */
2728 pipe_msg = PIPEMSG_OTHER_ERROR;
2729 rc = BAD_FILE_ERROR;
2730 DPRINTF(("MoveToTrashProcess: sending BAD_FILE_ERROR\n"));
2731 rv = write(pipe_fd, &pipe_msg, sizeof(short));
2732 rv = write(pipe_fd, &rc, sizeof(int));
2735 if (path && MatchesSacredDirectory(path))
2737 /* Invalid filename */
2739 pipe_msg = PIPEMSG_OTHER_ERROR;
2740 rc = BAD_FILE_SACRED;
2741 DPRINTF(("MoveToTrashProcess: sending BAD_FILE_SACRED\n"));
2742 rv = write(pipe_fd, &pipe_msg, sizeof(short));
2743 rv = write(pipe_fd, &rc, sizeof(int));
2748 if (do_verify_checks)
2750 char *tmpstr = (file_list[i] == (baseName - 1))?"/":file_list[i];
2752 Boolean TrashError = False;
2754 /* check write permissions for the file */
2755 if (lstat(path,&s1) < 0)
2762 savechar = *(baseName-1);
2763 *(baseName-1) = '\0';
2764 if(CheckDeletePermission(tmpstr, path) != 0)
2766 /* No write access; display error message */
2768 if (S_ISDIR(s1.st_mode))
2769 rc = BAD_TRASH_DIRECTORY;
2771 rc = BAD_TRASH_FILE;
2774 *(baseName-1) = savechar;
2779 pipe_msg = PIPEMSG_OTHER_ERROR;
2780 DPRINTF(("MoveToTrashProcess: sending BAD_TRASH message\n"));
2781 rv = write(pipe_fd, &pipe_msg, sizeof(short));
2782 rv = write(pipe_fd, &rc, sizeof(int));
2785 else if (CheckAccess(path, W_OK) != 0 && !S_ISLNK(s1.st_mode))
2787 /* No write access; ask user for verification */
2789 pipe_msg = PIPEMSG_OTHER_ERROR;
2791 DPRINTF(("MoveToTrashProcess: sending VERIFY_FILE\n"));
2792 rv = write(pipe_fd, &pipe_msg, sizeof(short));
2793 rv = write(pipe_fd, &rc, sizeof(int));
2798 * If this is a directory, make sure it's empty, i.e.,
2799 * contains only ".", "..", and ".!" or ".~" files.
2801 if (lstat(path, &statInfo) == 0 &&
2802 (statInfo.st_mode & S_IFMT) == S_IFDIR &&
2803 (dirp = opendir(path)) != NULL)
2805 /* read the directory */
2806 while ((entry = readdir(dirp)) != NULL)
2808 if ( !(strcmp(entry->d_name, ".") == 0 ||
2809 strcmp(entry->d_name, "..") == 0 ||
2810 strncmp(entry->d_name, ".!", 2) == 0 ||
2811 strncmp(entry->d_name, ".~", 2) == 0) )
2813 /* found a real file: directory not empty */
2822 /* Directory is not empty */
2824 pipe_msg = PIPEMSG_OTHER_ERROR;
2826 DPRINTF(("MoveToTrashProcess: sending VERIFY_DIR\n"));
2827 rv = write(pipe_fd, &pipe_msg, sizeof(short));
2828 rv = write(pipe_fd, &rc, sizeof(int));
2832 } /* end if do_verify_checks */
2835 to = CreateTrashFilename(baseName, TRUE);
2837 /* move file to the trash directory */
2838 success = FileManip((Widget) (intptr_t) pipe_fd, MOVE_FILE, path, to, TRUE,
2839 FileOpError, True, TRASH_DIRECTORY);
2842 pipe_msg = PIPEMSG_DONE;
2843 DPRINTF(("MoveToTrashProcess: sending DONE\n"));
2844 rv = write(pipe_fd, &pipe_msg, sizeof(short));
2845 PipeWriteString(pipe_fd, path);
2846 PipeWriteString(pipe_fd, to);
2855 /*--------------------------------------------------------------------
2856 * MoveToTrashPipeCB:
2857 * Read and process data sent through the pipe.
2858 *------------------------------------------------------------------*/
2862 XtPointer client_data,
2866 MoveToTrashCBData *cb_data = (MoveToTrashCBData *)client_data;
2869 char *title, *err_msg, *err_arg;
2882 DesktopRec *desktopWin;
2883 char *dir_error=NULL,*file_error=NULL,*no_file_error=NULL,*sacred_error=NULL;
2885 /* read the next msg from the pipe */
2887 n = PipeRead(*fd, &pipe_msg, sizeof(short));
2888 DPRINTF(("MoveToTrashPipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
2892 case PIPEMSG_FILEOP_ERROR:
2893 PipeRead(*fd, &rc, sizeof(int));
2894 err_msg = PipeReadString(*fd);
2895 err_arg = PipeReadString(*fd);
2897 FileOperationError(toplevel, err_msg, err_arg);
2902 cb_data->rc[cb_data->done_count++] = BAD_FILE_ERROR;
2905 case PIPEMSG_OTHER_ERROR:
2906 PipeRead(*fd, &rc, sizeof(int));
2907 cb_data->rc[cb_data->done_count++] = rc;
2910 case PIPEMSG_TARGET_TIME:
2913 case PIPEMSG_FILE_MODIFIED:
2917 i = cb_data->done_count++;
2918 cb_data->path[i] = PipeReadString(*fd);
2919 cb_data->to[i] = PipeReadString(*fd);
2925 "Internal error in MoveToTrashPipeCB: bad pipe_msg %d\n",
2927 /* Don't know how it can get in here, but if it does, we'll try to
2928 * simulate an error condition. Without it, the SGI hung up
2930 while (cb_data->done_count < cb_data->file_count)
2931 cb_data->rc[cb_data->done_count++] = BAD_FILE_ERROR;
2936 /* if still more files to be processed, return now to wait for the rest */
2937 if (cb_data->done_count < cb_data->file_count)
2941 /* close the pipe and cancel the callback */
2942 DPRINTF(("MoveToTrashPipeCB: done\n"));
2946 /* process the results */
2958 for (i = 0; i < cb_data->file_count; i++)
2960 if (cb_data->rc[i]==BAD_TRASH_DIRECTORY || cb_data->rc[i]==BAD_TRASH_FILE)
2965 Boolean errflg = False;
2966 if(cb_data->rc[i] == BAD_TRASH_DIRECTORY)
2968 tmpmsg = GETMESSAGE(27, 113,
2969 "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");
2972 dir_error = (char *) XtRealloc(dir_error,strlen(dir_error)+
2973 strlen(cb_data->file_list[i])+5);
2974 strcat(dir_error,cb_data->file_list[i]);
2975 strcat(dir_error,"\n");
2979 dir_error = XtMalloc(strlen(tmpmsg)+
2980 strlen(cb_data->file_list[i])+5);
2981 sprintf(dir_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
2986 tmpmsg = GETMESSAGE(27, 114,
2987 "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");
2990 file_error = (char *) XtRealloc(file_error,strlen(file_error)
2991 + strlen(cb_data->file_list[i])+5);
2992 strcat(file_error,cb_data->file_list[i]);
2993 strcat(file_error,"\n");
2997 file_error = XtMalloc(strlen(tmpmsg)+
2998 strlen(cb_data->file_list[i])+5);
2999 sprintf(file_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
3002 XtFree(cb_data->file_list[i]);
3003 cb_data->file_list[i] = NULL;
3007 else if (cb_data->rc[i] == BAD_FILE_SACRED)
3011 char *tmpmsg = GETMESSAGE(27, 115,
3012 "The following object(s) cannot be deleted.\nThe desktop cannot function properly without these object(s).\n\n");
3015 sacred_error = (char *) XtRealloc(sacred_error,strlen(sacred_error)+
3016 strlen(cb_data->file_list[i])+5);
3017 strcat(sacred_error,cb_data->file_list[i]);
3018 strcat(sacred_error,"\n");
3022 sacred_error=XtMalloc(strlen(tmpmsg)+strlen(cb_data->file_list[i])+5);
3023 sprintf(sacred_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
3025 XtFree(cb_data->file_list[i]);
3026 cb_data->file_list[i] = NULL;
3030 else if (cb_data->rc[i] == BAD_FILE_ERROR)
3032 XtFree(cb_data->file_list[i]);
3033 cb_data->file_list[i] = NULL;
3035 else if (cb_data->rc[i] == NO_TRASH_FILE)
3038 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");
3044 no_file_error = (char *)XtRealloc(no_file_error,strlen(no_file_error)+
3045 strlen(cb_data->file_list[i])+5);
3046 strcat(no_file_error,cb_data->file_list[i]);
3047 strcat(no_file_error,"\n");
3051 no_file_error = XtMalloc(strlen(tmpmsg)+
3052 strlen(cb_data->file_list[i])+5);
3053 sprintf(no_file_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
3058 else if (cb_data->rc[i] == VERIFY_FILE ||
3059 cb_data->rc[i] == VERIFY_DIR)
3061 if (verifyCount < 6)
3062 AddString(&verifybuf, &verifybufsize, cb_data->file_list[i], NULL);
3066 * Keep track of whether we have directories only, files only,
3067 * or a combination, so that we can display the appropriate
3068 * label in the dialog.
3070 if (cb_data->rc[i] == VERIFY_FILE)
3076 * Add to array which is to be attached to the dialog;
3077 * the array will ultimately be NULL terminated.
3079 verifylist = (char **)XtRealloc((char *)verifylist,
3080 sizeof(char *) * (verifyCount + 1));
3081 verifylist[verifyCount - 1] = cb_data->file_list[i];
3085 /* Add file to trash list */
3086 if (numTrashItems >= trashListSize)
3088 trashListSize += 10;
3089 trashCan = (TrashEntry *)
3090 XtRealloc((char *)trashCan, sizeof(TrashEntry) * trashListSize);
3093 trashCan[numTrashItems].problem = False;
3094 trashCan[numTrashItems].intNew = cb_data->to[i];
3095 trashCan[numTrashItems].intOrig = cb_data->path[i];
3096 trashCan[numTrashItems].external = cb_data->file_list[i];
3098 /* extract base file name */
3099 baseName = strrchr(cb_data->to[i], '/');
3101 if (*baseName == '\0')
3103 trashCan[numTrashItems].filename = XtNewString(baseName);
3108 /* arrange for the source directory to be updated */
3109 UpdateDirectoryOf(cb_data->path[i]);
3112 * If the source file was referenced by a desktop object,
3113 * we need to remove the destkop object
3115 for (j = 0; j < desktop_data->numIconsUsed; j++)
3117 Tt_status tt_status;
3119 desktopWin = desktop_data->desktopWindows[j];
3120 fileName = ResolveLocalPathName( desktopWin->host,
3121 desktopWin->dir_linked_to,
3122 desktopWin->file_name,
3123 home_host_name, &tt_status);
3125 if( TT_OK == tt_status )
3127 if (strcmp(fileName, cb_data->path[i]) == 0)
3129 RemoveDT(desktopWin->shell, (XtPointer)desktopWin, NULL);
3131 RemoveMovedObjectFromDT(desktopWin->shell, (XtPointer)desktopWin,
3132 cb_data->file_count, cb_data->file_list);
3133 #endif /* SUN_PERF */
3142 buf = XtMalloc(strlen(dir_error)+3);
3143 sprintf(buf,"%s\n",dir_error);
3150 buf = XtMalloc(strlen(file_error)+3);
3151 sprintf(buf,"%s\n",file_error);
3155 buf = XtRealloc(buf,strlen(buf)+strlen(file_error)+3);
3156 sprintf(buf,"%s%s\n",buf,file_error);
3164 buf = XtMalloc(strlen(no_file_error)+3);
3165 sprintf(buf,"%s\n",no_file_error);
3169 buf = XtRealloc(buf,strlen(buf)+strlen(no_file_error)+3);
3170 sprintf(buf,"%s%s\n",buf,no_file_error);
3172 XtFree(no_file_error);
3178 buf = XtMalloc(strlen(sacred_error)+3);
3179 sprintf(buf,"%s\n",sacred_error);
3183 buf = XtRealloc(buf,strlen(buf)+strlen(sacred_error)+3);
3184 sprintf(buf,"%s%s\n",buf,sacred_error);
3186 XtFree(sacred_error);
3189 bufsize = strlen(buf);
3191 /* Update the trash information file, and the trash window */
3194 FILE * trashInfoFileId = fopen(TrashInfoFileName, "a+");
3195 if( trashInfoFileId != NULL )
3197 for (i = fileCount; i > 0; i--)
3199 if( WriteEntry(trashInfoFileId,
3200 trashCan[numTrashItems - i].external,
3201 trashCan[numTrashItems - i].filename) < 0 )
3204 fflush(trashInfoFileId);
3205 fclose(trashInfoFileId);
3206 if( trashFileMgrData )
3207 UpdateDirectory(NULL, trashFileMgrData->host,
3208 trashFileMgrData->current_directory);
3212 /* Check for any bad files; post an error dialog */
3215 /* send a reply to the message that triggered this operation, if any */
3217 if (cb_data->msg != 0) {
3219 Until Action is fixed.
3220 tttk_message_fail( cb_data->msg, TT_DESKTOP_EACCES, 0, 1 );
3222 tt_message_reply( cb_data->msg );
3223 tttk_message_destroy( cb_data->msg );
3225 if (cb_data->msg_cnt > 0) {
3226 for (i = 0 ; i < cb_data->msg_cnt ; i++) {
3227 tt_message_reply( cb_data->msg_list[i] ) ;
3228 tttk_message_destroy( cb_data->msg_list[i] ) ;
3230 XtFree((char *)cb_data->msg_list) ;
3231 cb_data->msg_cnt = 0 ;
3232 #endif /* SUN_PERF */
3235 /* If more items than can be displayed, let user know */
3238 char extraFiles[256];
3240 (void) sprintf(extraFiles, AdditionalHeader, badCount - 8);
3244 GETMESSAGE(27,97, "The following objects could not be placed in the trash can: \n"));
3247 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3248 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3250 XtFree ((char *) buf);
3252 /* send a reply to the message that triggered this operation, if any */
3254 if (cb_data->msg != 0) {
3255 tt_message_reply( cb_data->msg );
3256 tttk_message_destroy( cb_data->msg );
3258 if (cb_data->msg_cnt > 0) {
3259 for (i = 0 ; i < cb_data->msg_cnt ; i++) {
3260 tt_message_reply( cb_data->msg_list[i] ) ;
3261 tttk_message_destroy( cb_data->msg_list[i] ) ;
3263 XtFree((char *)cb_data->msg_list) ;
3264 cb_data->msg_cnt = 0 ;
3265 #endif /* SUN_PERF */
3270 /* Check for any files requiring user verification; post a prompt dialog */
3271 /* XXX Really should fail incoming ToolTalk request if user cancels any */
3279 /* If more items than can be displayed, let user know */
3280 if (verifyCount > 6)
3282 char extraFiles[256];
3284 (void) sprintf(extraFiles, AdditionalHeader, verifyCount - 6);
3285 AddString(&verifybuf, &verifybufsize, extraFiles, NULL);
3289 * Depending upon what type of files are to be displayed in the
3290 * dialog, choose the appropriate dialog text.
3292 if (vfiles && vdirs)
3293 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");
3295 header = GETMESSAGE(27,104, "You do not have write permission for the following file(s):\nDo you want to proceed?\n");
3297 header = GETMESSAGE(27,100, "The following folder(s) are not empty.\nDo you want to proceed?\n");
3299 tmpbuf = XtMalloc(strlen(header) + strlen(verifybuf) + 1);
3300 sprintf(tmpbuf, "%s%s", header, verifybuf);
3301 title = XtNewString(GETMESSAGE(27, 4, "Trash Can Warning"));
3302 dlog = (Widget)_DtMessageDialog(toplevel, title, tmpbuf, NULL, True,
3303 VerifyCancel, VerifyOk, NULL, HelpRequestCB,
3304 False, WARNING_DIALOG);
3306 XtFree ((char *) verifybuf);
3310 * Add array as userdata on the dialog.
3311 * NULL terminate the array.
3313 verifylist[verifyCount] = NULL;
3314 XtSetArg(args[0], XmNuserData, verifylist);
3315 XtSetValues(dlog, args, 1);
3318 /* free the callback data */
3319 XtFree((char *)cb_data->file_list);
3320 XtFree((char *)cb_data->path);
3321 XtFree((char *)cb_data->to);
3322 XtFree((char *)cb_data->rc);
3323 XtFree((char *)cb_data);
3329 /*--------------------------------------------------------------------
3331 * Start the background process and set up callback for the pipe.
3332 *------------------------------------------------------------------*/
3338 Boolean do_verify_checks,
3341 static char *pname = "MoveToTrash";
3342 MoveToTrashCBData *cb_data;
3346 if( !TrashInitialized )
3349 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
3354 /* set up callback data */
3355 cb_data = XtNew(MoveToTrashCBData);
3356 cb_data->file_list = file_list;
3357 cb_data->file_count = file_count;
3358 cb_data->done_count = 0;
3359 cb_data->path = (char **)XtCalloc(file_count, sizeof(char *));
3360 cb_data->to = (char **)XtCalloc(file_count, sizeof(char *));
3361 cb_data->rc = (int *)XtCalloc(file_count, sizeof(int));
3364 cb_data->msg_cnt = 0 ;
3365 if (global_msg_cnt > 0) {
3366 cb_data->msg_cnt = global_msg_cnt ;
3367 cb_data->msg_list = (Tt_message *)XtMalloc(sizeof(Tt_message) * cb_data->msg_cnt);
3368 memcpy((char *)cb_data->msg_list,(char *)global_msg_list, sizeof(Tt_message) *
3371 #endif /* SUN_PERF */
3374 if(-1 == pipe(pipe_fd)) {
3376 "%s: pipe failed error %d=%s\n",
3377 pname, errno, strerror(errno));
3381 /* fork the process that does the actual work */
3386 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
3387 pname, getppid(), getpid(), errno, strerror(errno));
3394 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
3396 close(pipe_fd[0]); /* child won't read from the pipe */
3398 MoveToTrashProcess(pipe_fd[1], file_list, file_count, do_verify_checks);
3401 DBGFORK(("%s: child exiting\n", pname));
3406 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
3408 /* parent: set up callback to get the pipe data */
3409 close(pipe_fd[1]); /* parent won't write the pipe */
3411 cb_data->child = pid;
3413 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
3414 pipe_fd[0], (XtPointer)XtInputReadMask,
3415 MoveToTrashPipeCB, (XtPointer)cb_data);
3419 /*====================================================================
3423 * Run a background process to restore files from the trash can.
3425 * These routines are used both for "normal restores", i.e.,
3426 * restores initiated from the Trash menu or through an ICCCM
3427 * message, as well as "drag&drop restores", i.e., restores done
3428 * by dragging files from the trash can to some other directory.
3429 * For normal resores, file_list contains only simple file names
3430 * (no paths) and target_dir is NULL. For drag&drop restores
3431 * file_list contains complete path names and target_dir contains
3432 * the name of the directoy to which the files should be moved.
3434 *==================================================================*/
3436 /*--------------------------------------------------------------------
3438 * Main routine of background process for RestoreFromTrash
3439 *------------------------------------------------------------------*/
3449 Boolean CheckedAlready)
3458 char **RestoreList= NULL;
3460 /* get full path name of target directory */
3463 Tt_status tt_status;
3464 full_dirname = ResolveLocalPathName(target_host, target_dir,
3465 NULL, home_host_name, &tt_status);
3466 if( TT_OK != tt_status )
3468 /* send return codes back trough the pipe */
3469 pipe_msg = PIPEMSG_DONE;
3471 DPRINTF(("RestoreProcess: Unable to Resolve local path name\n"));
3472 rv = write(pipe_fd, &pipe_msg, sizeof(short));
3473 rv = write(pipe_fd, rc, sizeof(int));
3478 full_dirname = NULL;
3480 /* restore the files */
3481 for (i = 0; i < file_count; i++)
3483 /* Locate file in trash list */
3484 for (j = 0; j < numTrashItems; j++)
3486 /* file_list[i] may be a complete path or just a file name */
3487 if (strcmp(file_list[i], trashCan[j].filename) == 0 ||
3488 strcmp(file_list[i], trashCan[j].intNew) == 0)
3494 /* determine source and target for the move */
3495 if (target_dir == NULL)
3497 /* this is a normal restore */
3498 if (j < numTrashItems)
3500 from = trashCan[j].intNew;
3501 to = trashCan[j].intOrig;
3505 /* can't do a restore if the file wasn't found in the trash list */
3511 /* this is a drag&drop from the trash can to target_dir */
3512 from = file_list[i];
3513 if (j < numTrashItems)
3514 ptr = strrchr(trashCan[j].intOrig, '/');
3516 ptr = strrchr(file_list[i], '/');
3517 strcpy(buf, full_dirname);
3525 status = RestoreObject((Widget) (intptr_t) pipe_fd, MOVE_FILE, from,to,
3526 TRUE, FileOpError, False, NOT_DESKTOP,CheckedAlready);
3527 /* restore was successful */
3528 if(status == (int) True)
3530 else if(status == (int) False)
3536 /* restore failed */
3540 /* send return codes back trough the pipe */
3541 pipe_msg = PIPEMSG_DONE;
3542 DPRINTF(("RestoreProcess: sending DONE\n"));
3543 rv = write(pipe_fd, &pipe_msg, sizeof(short));
3544 rv = write(pipe_fd, rc, file_count * sizeof(int));
3546 XtFree(full_dirname);
3550 /*--------------------------------------------------------------------
3552 * Read and process data sent through the pipe.
3553 *------------------------------------------------------------------*/
3557 XtPointer client_data,
3561 RestoreFromTrashCBData *cb_data = (RestoreFromTrashCBData *)client_data;
3564 char *title, *err_msg, *err_arg;
3568 char **ToRestoreList=NULL;
3569 char **FromRestoreList=NULL;
3570 char *target_host,*target_dir;
3573 /* read the next msg from the pipe */
3575 n = PipeRead(*fd, &pipe_msg, sizeof(short));
3576 DPRINTF(("RestorePipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
3580 case PIPEMSG_FILEOP_ERROR:
3581 PipeRead(*fd, &rc, sizeof(int));
3582 err_msg = PipeReadString(*fd);
3583 err_arg = PipeReadString(*fd);
3584 FileOperationError(toplevel, err_msg, err_arg);
3585 /* This call will popup an error dialog.
3587 FileOperationError(toplevel, err_msg, err_arg);
3589 It's not appropriate at all to popup an error dialog here.
3590 i.e. if there're 1000 files, and the file system is full,
3591 and we're unable to move, would we want to popup 1000
3598 PipeRead(*fd, cb_data->rc, cb_data->file_count * sizeof(int));
3603 "Internal error in RestorePipeCB: bad pipe_msg %d\n",
3607 /* close the pipe and cancel the callback */
3608 DPRINTF(("RestorePipeCB: done\n"));
3615 for (i = 0; i < cb_data->file_count; i++)
3617 /* Locate file in trash list */
3618 for (j = 0; j < numTrashItems; j++)
3620 /* file_list[i] may be a complete path or just a file name */
3621 if (strcmp(cb_data->file_list[i], trashCan[j].filename) == 0 ||
3622 strcmp(cb_data->file_list[i], trashCan[j].intNew) == 0)
3628 if (cb_data->rc[i] == SKIP_FILE)
3630 ToRestoreList = (char **) XtRealloc((char *)ToRestoreList,sizeof(char *) *
3632 ToRestoreList[RestoreIndex-1] = XtNewString(trashCan[j].intOrig);
3633 FromRestoreList = (char **) XtRealloc((char *)FromRestoreList,sizeof(char *) *
3635 FromRestoreList[RestoreIndex-1] = XtNewString( trashCan[j].intNew );
3638 /* Check the return code from the restore */
3639 else if (cb_data->rc[i] == 0)
3641 /* restore was successful: remove the file from the trash list */
3642 if (j < numTrashItems)
3644 /* arrange for the source directory to be updated */
3645 UpdateDirectoryOf(trashCan[j].intOrig);
3647 /* Remove this entry from the trash list */
3648 XtFree ((char *) trashCan[j].intNew);
3649 XtFree ((char *) trashCan[j].intOrig);
3650 XtFree ((char *) trashCan[j].external);
3651 XtFree ((char *) trashCan[j].filename);
3652 for (k = j; k < (numTrashItems - 1); k++)
3653 trashCan[k] = trashCan[k + 1];
3660 char *restore_header,*tmpStr = GETMESSAGE(27,101,
3661 "The following object(s) could not be put back:\n");
3663 restore_header = XtNewString(tmpStr);
3664 if (j < numTrashItems && cb_data->target_dir == NULL)
3665 AddString(&buf, &bufsize, trashCan[j].external, restore_header);
3667 AddString(&buf, &bufsize, cb_data->file_list[i], restore_header);
3668 XtFree(restore_header);
3672 /* Update the trash information file */
3673 if( ! WriteTrashEntries() )
3675 char * tmpStr, * title;
3677 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3678 tmpStr = XtNewString(GETMESSAGE(27, 88, "Cannot write to a temporary file.\nPerhaps your file system is full.\n"));
3679 _DtMessage(toplevel, title, tmpStr, NULL, HelpRequestCB);
3684 /* send a reply to the message that triggered this operation, if any */
3685 if (cb_data->msg != 0) {
3686 tt_message_reply( cb_data->msg );
3687 tttk_message_destroy( cb_data->msg );
3691 /* Report any errors */
3694 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3695 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3697 XtFree ((char *) buf);
3700 UpdateDirectory(NULL, trashFileMgrData->host,
3701 trashFileMgrData->current_directory);
3702 if (cb_data->target_dir)
3703 UpdateDirectory(NULL, cb_data->target_host, cb_data->target_dir);
3705 target_host = XtNewString(cb_data->target_host);
3706 target_dir = XtNewString(cb_data->target_dir);
3708 /* free the callback data */
3709 for (i = 0; i < cb_data->file_count; i++)
3710 XtFree(cb_data->file_list[i]);
3711 XtFree((char *)cb_data->file_list);
3712 XtFree((char *)cb_data->target_host);
3713 XtFree((char *)cb_data->target_dir);
3714 XtFree((char *)cb_data->rc);
3715 XtFree((char *)cb_data);
3718 for(i=0;i<RestoreIndex;i++)
3720 CreateRestoreDialog(FromRestoreList[i],ToRestoreList[i]);
3721 XtFree(FromRestoreList[i]);
3722 XtFree(ToRestoreList[i]);
3724 XtFree((char *) FromRestoreList);
3725 XtFree((char *) ToRestoreList);
3727 XtFree(target_host);
3732 /*--------------------------------------------------------------------
3734 * Start the background process and set up callback for the pipe.
3735 *------------------------------------------------------------------*/
3744 Boolean CheckedAlready)
3746 static char *pname = "RestoreFromTrash";
3747 RestoreFromTrashCBData *cb_data;
3751 if( !TrashInitialized )
3754 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
3759 /* set up callback data */
3760 cb_data = XtNew(RestoreFromTrashCBData);
3761 cb_data->file_list = file_list;
3762 cb_data->file_count = file_count;
3763 cb_data->target_host = target_host;
3764 cb_data->target_dir = target_dir;
3765 cb_data->rc = (int *)XtCalloc(file_count, sizeof(int));
3769 if(-1 == pipe(pipe_fd)) {
3771 "%s: pipe failed, error %d=%s\n",
3772 pname, errno, strerror(errno));
3776 /* fork the process that does the actual work */
3781 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
3782 pname, getppid(), getpid(), errno, strerror(errno));
3789 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
3791 close(pipe_fd[0]); /* child won't read from the pipe */
3793 RestoreProcess(pipe_fd[1], file_list, file_count,
3794 target_host, target_dir, cb_data->rc,CheckedAlready);
3797 DBGFORK(("%s: child exiting\n", pname));
3802 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
3804 /* parent: set up callback to get the pipe data */
3805 close(pipe_fd[1]); /* parent won't write the pipe */
3807 cb_data->child = pid;
3809 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
3810 pipe_fd[0], (XtPointer)XtInputReadMask,
3811 RestorePipeCB, (XtPointer)cb_data);
3816 /*====================================================================
3819 * Run a background process to remove files from the trash can.
3821 *==================================================================*/
3823 /*--------------------------------------------------------------------
3825 * Main routine of background process for EmptyTrash
3826 *------------------------------------------------------------------*/
3831 DeleteList * del_list,
3839 * Delete all files or directories in the list,
3840 * as well as any associated annotations.
3842 for (i = 0; i < del_count; i++)
3844 /* delete the file */
3845 rc[i] = EraseObject(del_list[i].trash);
3848 /* send return codes back trough the pipe */
3849 pipe_msg = PIPEMSG_DONE;
3850 DPRINTF(("EmptyTrashProcess: sending DONE\n"));
3851 rv = write(pipe_fd, &pipe_msg, sizeof(short));
3852 rv = write(pipe_fd, rc, del_count * sizeof(int));
3856 /*--------------------------------------------------------------------
3858 * Read and process data sent through the pipe.
3859 *------------------------------------------------------------------*/
3863 XtPointer client_data,
3867 EmptyTrashCBData *cb_data = (EmptyTrashCBData *)client_data;
3870 char *title, *err_msg, *err_arg;
3871 int problemCount, itemCount;
3876 /* read the next msg from the pipe */
3878 n = PipeRead(*fd, &pipe_msg, sizeof(short));
3879 DPRINTF(("EmptyTrashPipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
3883 case PIPEMSG_FILEOP_ERROR:
3884 PipeRead(*fd, &rc, sizeof(int));
3885 err_msg = PipeReadString(*fd);
3886 err_arg = PipeReadString(*fd);
3887 /* This call will popup an error dialog.
3889 FileOperationError(toplevel, err_msg, err_arg);
3891 It's not appropriate at all to popup an error dialog here.
3892 i.e. if there're 1000 files, and the file system is full,
3893 and we're unable to move, would we want to popup 1000
3901 PipeRead(*fd, cb_data->rc, cb_data->del_count * sizeof(int));
3906 "Internal error in EmptyTrashPipeCB: bad pipe_msg %d\n",
3910 /* close the pipe and cancel the callback */
3911 DPRINTF(("EmptyTrashPipeCB: done\n"));
3920 if (cb_data->removeType == TRASH_FILE)
3922 for (i = 0; i < cb_data->del_count; i++)
3924 /* Locate file in trash list */
3925 for (j = 0; j < numTrashItems; j++)
3926 if (strcmp(cb_data->del_list[i].trash, trashCan[j].intNew) == 0)
3929 /* Check the return code from the erase */
3930 if (cb_data->rc[i] == 0)
3932 /* erase was successful: remove the file from the trash list */
3933 if (j < numTrashItems)
3935 /* Remove this entry from the trash list */
3936 XtFree ((char *) trashCan[j].intNew);
3937 XtFree ((char *) trashCan[j].intOrig);
3938 XtFree ((char *) trashCan[j].external);
3939 XtFree ((char *) trashCan[j].filename);
3940 for (k = j; k < (numTrashItems - 1); k++)
3941 trashCan[k] = trashCan[k + 1];
3952 char * msg = XtNewString(GETMESSAGE(27,96, "The following objects could not be removed from the file system: \n"));
3954 if (j < numTrashItems)
3955 file_name = trashCan[j].external;
3957 file_name = cb_data->del_list[i].trash;
3958 AddString(&buf, &bufsize, file_name, msg);
3965 /* Update the trash information file */
3966 if( ! WriteTrashEntries() )
3968 char * tmpStr, * title;
3970 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3971 tmpStr = XtNewString(GETMESSAGE(27, 88, "Cannot write to a temporary file.\nPerhaps your file system is full.\n"));
3972 _DtMessage(toplevel, title, tmpStr, NULL, HelpRequestCB);
3977 /* Report any errors */
3980 /* If more items than can be displayed, let user know */
3981 if (itemCount < problemCount)
3983 char extraFiles[256];
3984 sprintf(extraFiles, AdditionalHeader, problemCount - itemCount);
3988 GETMESSAGE(27,97, "The following objects could not be placed in the trash can: \n") );
3991 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3992 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3994 XtFree ((char *) buf);
3997 if (trashFileMgrData->selected_file_count > 0)
3999 XtSetSensitive(*removeBtn, True);
4000 XtSetSensitive(*restoreBtn, True);
4003 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
4004 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
4006 UpdateDirectory(NULL, trashFileMgrData->host,
4007 trashFileMgrData->current_directory);
4011 if (cb_data->rc[0] == 0)
4013 /* arrange for the directory containing the file to be updated */
4014 UpdateDirectoryOf(cb_data->del_list[0].trash);
4018 /* put up an error message saying the file couldn't be removed */
4022 tmpStr = GETMESSAGE(27, 81, "Shred File/Folder Error");
4023 title = XtNewString(tmpStr);
4024 tmpStr = GETMESSAGE(27, 82, " The following file could not be removed from the file system: \n");
4025 file_name = cb_data->del_list[0].trash;
4026 buffer = XtMalloc(strlen(tmpStr) + strlen(file_name) + 1);
4027 sprintf(buffer, "%s%s\n", tmpStr, file_name);
4028 _DtMessage(toplevel, title, buffer, NULL, HelpRequestCB);
4035 /* reset removingTrash flag */
4036 removingTrash = False;
4038 /* send a reply to the message that triggered this operation, if any */
4039 if (cb_data->msg != 0) {
4040 tt_message_reply( cb_data->msg );
4041 tttk_message_destroy( cb_data->msg );
4045 /* free the callback data */
4046 for (i = 0; i < cb_data->del_count; i++)
4048 XtFree(cb_data->del_list[i].trash);
4049 XtFree(cb_data->del_list[i].orig);
4051 XtFree((char *)cb_data->del_list);
4052 XtFree((char *)cb_data->rc);
4053 XtFree((char *)cb_data);
4057 /*--------------------------------------------------------------------
4059 * Start the background process and set up callback for the pipe.
4060 *------------------------------------------------------------------*/
4064 DeleteList *del_list,
4069 static char *pname = "EmptyTrash";
4070 EmptyTrashCBData *cb_data;
4074 if( !TrashInitialized )
4077 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
4082 /* set removingTrash flag */
4083 removingTrash = True;
4085 /* set up callback data */
4086 cb_data = XtNew(EmptyTrashCBData);
4087 cb_data->del_list = del_list;
4088 cb_data->del_count = del_count;
4089 cb_data->removeType = removeType;
4090 cb_data->rc = (int *)XtCalloc(del_count, sizeof(int));
4094 if(-1 == pipe(pipe_fd)) {
4096 "%s: pipe failed, error %d=%s\n",
4097 pname, errno, strerror(errno));
4101 /* fork the process that does the actual work */
4106 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
4107 pname, getppid(), getpid(), errno, strerror(errno));
4114 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
4116 close(pipe_fd[0]); /* child won't read from the pipe */
4118 EmptyTrashProcess(pipe_fd[1], del_list, del_count, cb_data->rc);
4121 DBGFORK(("%s: child exiting\n", pname));
4126 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
4128 /* parent: set up callback to get the pipe data */
4129 close(pipe_fd[1]); /* parent won't write the pipe */
4131 cb_data->child = pid;
4133 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
4134 pipe_fd[0], (XtPointer)XtInputReadMask,
4135 EmptyTrashPipeCB, (XtPointer)cb_data);
4139 CheckDeletePermission(
4141 char *destinationPath)
4143 #if defined(__FreeBSD__) || defined(__OpenBSD__)
4144 struct statfs statbuf;
4145 #elif defined(__NetBSD__)
4146 struct statvfs statbuf;
4148 struct stat statbuf;
4152 #if defined(__FreeBSD__) || defined(__OpenBSD__)
4153 if (statfs(parentdir,&statbuf) < 0) /* does not exist */
4154 #elif defined(__NetBSD__)
4155 if (statvfs(parentdir,&statbuf) < 0) /* does not exist */
4157 if (lstat(parentdir,&statbuf) < 0) /* does not exist */
4161 /* check if we are root */
4164 /* if NFS, need to check if server trusts root */
4165 #if defined(CSRG_BASED)
4166 if (!strcmp(statbuf.f_fstypename, "nfs")) /* Root user and nfs */
4168 if (FileSysType(statbuf.st_dev) < 0) /* Root user and nfs */
4173 tmpfile = tempnam(parentdir,"dtfile");
4176 if ((fd = creat(tmpfile,O_RDONLY)) < 0) /* Create a temporary file */
4182 if (remove(tmpfile) < 0) /* Delete the created file */
4190 /* root user can delete anything */
4194 /* check for write and execute permisssion on parent dir */
4195 if (CheckAccess(parentdir, W_OK | X_OK) < 0)
4198 /* copy destinationPath to tmp buffer */
4199 strcpy(fname, destinationPath);
4201 return CheckDeletePermissionRecur(fname);
4206 CheckDeletePermissionRecur(
4207 char *destinationPath)
4209 struct stat statbuf;
4215 DPRINTF(("CheckDeletePermissionRecur(\"%s\")\n", destinationPath));
4217 if (lstat(destinationPath, &statbuf) < 0)
4218 return -1; /* probably does not exist */
4220 if (! S_ISDIR(statbuf.st_mode))
4221 return 0; /* no need to check anything more */
4223 dirp = opendir (destinationPath);
4225 return -1; /* could not read directory */
4230 while (dp = readdir (dirp))
4232 if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
4236 /* check for write permission in this directory */
4237 if (CheckAccess(destinationPath, W_OK|X_OK) < 0) {
4242 /* append a '/' to the end of directory name */
4243 fnamep = destinationPath + strlen(destinationPath);
4249 /* append file name to end of directory name */
4250 strcpy(fnamep, dp->d_name);
4252 /* recursively check permission on this file */
4253 if (CheckDeletePermissionRecur(destinationPath))
4267 #if !defined(CSRG_BASED)
4273 if(ustat(dev,&u1) < 0)
4285 Boolean isContainer,
4286 void (*errorHandler)(),
4287 Boolean checkForBusyDir,
4289 Boolean CheckedAlready)
4291 struct stat statsrc,stattar;
4293 char *localdir = NULL,*chrptr;
4298 if(stat(source,&statsrc) < 0)
4299 if(lstat(source,&statsrc) < 0)
4302 localdir = strdup(target);
4304 chrptr = strrchr(localdir,'/');
4307 if(chrptr == localdir) /* Must be root folder */
4312 if(stat(target,&stattar) >= 0) /* Target exists */
4314 if(CheckDeletePermission(localdir,target)) {
4316 return ((int)False);
4325 return ((int )FileManip((Widget)w, MOVE_FILE, source, target, TRUE,
4326 FileOpError, False, NOT_DESKTOP));
4329 CreateRestoreDialog(
4333 char *tmpbuf,*title;
4337 char **dirs = (char **) malloc(sizeof(char *) * 2);
4338 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.");
4340 dirs[0] = strdup(source);
4341 dirs[1] = strdup(target);
4342 tmpbuf = XtMalloc(strlen(header) + strlen(target) + 1);
4343 sprintf(tmpbuf, header, target);
4344 title = XtNewString(GETMESSAGE(27, 109, "Put Back Warning"));
4345 dw = (Widget)_DtMessageDialog(toplevel, title,tmpbuf,NULL,True,RestoreVerifyCancel,
4346 RestoreVerifyOk, NULL, HelpRequestCB, False, WARNING_DIALOG);
4348 XtSetArg(args[n], XmNuserData,dirs); n++;
4349 XtSetValues(dw,args,n);
4357 XtPointer client_data,
4358 XtPointer call_data )
4363 Widget mbox = (Widget)client_data;
4365 XtSetArg(args[n],XmNuserData,&dirs); n++;
4366 XtGetValues(mbox,args,n);
4368 if(DirectoryBusy(dirs[1]))
4371 char * tmpStr,*title;
4373 title = XtNewString(GETMESSAGE(27,111,"Put Back Error"));
4374 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.");
4375 msgbuf = XtMalloc(strlen(tmpStr) + strlen(dirs[1])+1);
4376 sprintf (msgbuf, tmpStr, dirs[1]);
4377 _DtMessage(toplevel,title,msgbuf,NULL,HelpRequestCB);
4385 char *realTarget,*tptr;
4389 if(lstat(dirs[1],&s1) < 0)
4391 fsErase(dirs[1],&status,0);
4392 FileList = (char **) XtMalloc(sizeof(char *));
4393 FileList[0] = XtNewString(dirs[0]);
4394 RestoreFromTrash(FileList, (int) 1, NULL, NULL, NULL,True);
4400 XtDestroyWidget(mbox);
4404 RestoreVerifyCancel(
4406 XtPointer client_data,
4407 XtPointer call_data )
4413 Widget mbox = (Widget)client_data;
4415 XtSetArg(args[n],XmNuserData,&dirs); n++;
4416 XtGetValues(mbox,args,n);
4420 XtDestroyWidget(mbox);