2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $TOG: 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>
106 #include <Xm/RowColumn.h>
107 #include <Xm/CascadeB.h>
109 #include <Xm/Frame.h>
110 #include <Xm/MainW.h>
111 #include <Xm/PushBG.h>
112 #include <Xm/SeparatoG.h>
113 #include <Xm/MessageB.h>
114 #include <Xm/MwmUtil.h>
116 #include <X11/Shell.h>
117 #include <X11/Xatom.h>
118 #include <Xm/Protocols.h>
120 #include <Dt/Action.h>
122 #include <Dt/FileM.h>
123 #include <Dt/DtNlUtils.h>
124 #include <Dt/Connect.h>
125 #include <Dt/Indicator.h>
126 #include <Dt/EnvControlP.h>
133 #include "SharedProcs.h"
134 #include "MultiView.h"
139 #include "SharedMsgs.h"
141 #define AdditionalHeader (GETMESSAGE(27,98, "(Plus %d additional object(s))"))
143 /* Trash file errors */
144 #define NO_FILE_ERROR 0
145 #define BAD_FILE_ERROR -1
146 #define VERIFY_DIR -2
147 #define VERIFY_FILE -3
148 #define BAD_TRASH_DIRECTORY -4
149 #define BAD_TRASH_FILE -5
150 #define NO_TRASH_FILE -6
153 #define BAD_FILE_SACRED -9
155 /* types of messages sent through the pipe */
156 #define PIPEMSG_FILEOP_ERROR 1
157 #define PIPEMSG_OTHER_ERROR 3
158 #define PIPEMSG_DONE 7
159 #define PIPEMSG_TARGET_TIME 5
160 #define PIPEMSG_FILE_MODIFIED 6
163 * Structure describing each file in the trash can.
164 * Includes the external host:/name format, the original internal
165 * /nfs/host/name format, and the internal /nfs/host/new_name describing
166 * the temporary location of the file. The `problem' flag is used to
167 * indicate if a problem occurred when the file was physically being
179 /* callback data MoveToTrash */
190 Tt_message *msg_list ;
192 #endif /* SUN_PERF */
199 Tt_message *msg_list ;
202 #endif /* SUN_PERF */
204 /* callback data RestoreFromTrash */
214 } RestoreFromTrashCBData;
216 /* callback data EmptyTrash */
225 DeleteList *del_list;
234 DialogData *trashDialogData; /* fm data associated with trash dialog */
235 FileMgrData *trashFileMgrData = NULL;
237 Boolean removingTrash = False;
239 DialogData * primaryTrashHelpDialog = NULL;
240 DialogData ** secondaryTrashHelpDialogList = NULL;
241 int secondaryTrashHelpDialogCount = 0;
242 /* 'defines' for trash files */
243 static char * TRASH_DIR = ".dt/Trash";
244 static char * TRASH_INFO_FILE = ".dt/Trash/.trashinfo";
245 static char * NEW_TRASH_INFO_FILE = ".dt/.tmptrashinfo";
247 static char * RM = "/bin/rm";
248 static char * RM_ARGS = "-rf";
250 static Widget * selectAllBtn = NULL;
251 static Widget * restoreBtn = NULL;
252 static Widget * removeBtn = NULL;
254 static int trashMenuItemCount = 22; /* trash menu items */
255 static MenuDesc * trashMenu = NULL;
257 static TrashEntry * trashCan = NULL;
258 static int trashListSize = 0;
259 static int numTrashItems = 0;
261 static Boolean trashDialogPosted;
263 static char * TrashInfoFileName = NULL;
264 static char * NewTrashInfoFileName = NULL;
266 static char ** sacred_dir_list = NULL; /* list of directories in trash path */
267 static int sacred_dir_list_size = 0;
268 static int sacred_dir_count = 0;
270 static Boolean verifyPromptsEnabled; /* do we prompt the user? */
272 static Tt_message global;
274 static Tt_message *global_msg_list ;
275 static int global_msg_cnt = 0 ;
276 #endif /* SUN_PERF */
277 static Boolean TrashInitialized = False;
279 /******** Static Function Declarations ********/
281 static String CreateTrashFilename(
284 static void MessageToFileList(
289 static void MessagesToFileList(
290 Tt_message *msg_list,
294 #endif /* SUN_PERF */
295 static int WriteEntry(
299 static Boolean MatchesSacredDirectory(
301 static void VerifyCleanup(
303 Boolean completeDelete) ;
304 static void VerifyCancel(
306 XtPointer client_data,
307 XtPointer call_data) ;
308 static void VerifyOk(
310 XtPointer client_data,
311 XtPointer call_data) ;
312 static Boolean WriteTrashEntries( void ) ;
313 static void Select_All(
315 XtPointer client_data,
316 XtPointer call_data) ;
317 static void Unselect_All(
319 XtPointer client_data,
320 XtPointer call_data) ;
323 XtPointer client_data,
324 XtPointer call_data) ;
325 static String GetBasePath(
327 static Boolean ReadTrashList( void ) ;
328 static void RemoveOkCB(
330 XtPointer client_data,
331 XtPointer call_data ) ;
332 static void RemoveCancelCB(
334 XtPointer client_data,
335 XtPointer call_data ) ;
336 static void ConfirmOk(
338 XtPointer client_data,
339 XtPointer call_data ) ;
340 static void ConfirmCancel(
342 XtPointer client_data,
343 XtPointer call_data ) ;
346 XtPointer clientData,
347 XtPointer callData) ;
348 static void AddToDeleteList(
349 DeleteList *deleteList,
352 static void MoveToTrash(
355 Boolean do_verify_checks,
357 static void RestoreFromTrash(
363 Boolean CheckedAlready) ;
364 static void EmptyTrash(
365 DeleteList *del_list,
369 static int CheckDeletePermissionRecur(
371 static int FileSysType(int dev);
372 static void RestoreVerifyOk(
374 XtPointer client_data,
375 XtPointer call_data ) ;
376 static void RestoreVerifyCancel(
378 XtPointer client_data,
379 XtPointer call_data ) ;
380 static int RestoreObject(
383 register char *source,
384 register char *target,
386 void (*errorHandler)(),
387 Boolean checkForBusyDir,
389 Boolean CheckedAlready);
390 static void CreateRestoreDialog(
394 /******** End Static Function Declarations ********/
397 TrashIsInitialized( void )
399 return( TrashInitialized );
403 InitializeTrash( Boolean enableVerifyPrompt )
406 struct stat statInfo;
408 /* Build the 'TRASH' directory */
409 trash_dir = XtMalloc(strlen(users_home_dir) +
412 sprintf(trash_dir, "%s%s", users_home_dir, TRASH_DIR);
413 if (stat(trash_dir, &statInfo) < 0)
414 mkdir(trash_dir, S_IRUSR | S_IWUSR | S_IXUSR |
415 S_IRGRP | S_IWGRP | S_IXGRP |
416 S_IROTH | S_IWOTH | S_IXOTH);
418 /* build path to .trashinfo file */
419 TrashInfoFileName = XtMalloc(strlen(users_home_dir) + strlen(TRASH_INFO_FILE) + 1);
420 sprintf(TrashInfoFileName, "%s%s", users_home_dir, TRASH_INFO_FILE);
423 NewTrashInfoFileName = XtMalloc(strlen(users_home_dir) +
424 strlen(NEW_TRASH_INFO_FILE)
426 sprintf(NewTrashInfoFileName, "%s%s", users_home_dir, NEW_TRASH_INFO_FILE);
428 /* Keep track of whether to prompt for user verification */
429 verifyPromptsEnabled = enableVerifyPrompt;
432 * Create an array of paths and filenames which we will not allow the
433 * user to delete, because deleting any of them will cause trash to
436 sacred_dir_list_size = 5;
437 sacred_dir_list = (char **)XtMalloc(sizeof(char *) * sacred_dir_list_size);
439 sacred_dir_list[sacred_dir_count++] = XtNewString("/");
440 sacred_dir_list[sacred_dir_count++] = XtNewString(TrashInfoFileName);
442 ptr = TrashInfoFileName + 1;
443 while(ptr = DtStrchr(ptr, '/'))
445 /* All parent components of the user's home dir cannot be deleted */
447 if (sacred_dir_count >= sacred_dir_list_size)
450 sacred_dir_list_size += 5;
451 sacred_dir_list = (char **)XtRealloc((char *)sacred_dir_list,
452 sizeof(char *) * sacred_dir_list_size);
454 sacred_dir_list[sacred_dir_count++] = XtNewString(TrashInfoFileName);
459 /* load and verify existence for files previously left in the trash can */
460 TrashInitialized = ReadTrashList( );
461 return( TrashInitialized );
464 /************************************************************************
467 * This function must be called before any other interatctions with the
468 * trash can. It creates the trash dialog, opens the trash file, and
469 * loads information about any files previously left in the trash can.
471 * This function is called from main().
473 ************************************************************************/
481 DialogData * dialog_data;
482 FileMgrData * file_mgr_data;
484 if( ! TrashInitialized )
487 /* Create the trash dialog window */
488 /* load trash title */
489 tmpStr = GETMESSAGE(27, 9, "Trash Can");
490 title = XtNewString(tmpStr);
492 /* Initially, no items can be selected */
493 if (removeBtn != NULL)
494 XtSetSensitive(*removeBtn, False);
495 if (restoreBtn != NULL)
496 XtSetSensitive(*restoreBtn, False);
498 /* retrieve the default data for a file manager dialog -- */
499 /* the TrashView flag affects this data */
501 dialog_data = _DtGetDefaultDialogData (file_mgr_dialog);
502 file_mgr_data = (FileMgrData *) dialog_data->data;
503 trashFileMgrData = file_mgr_data;
504 trashDialogData = dialog_data;
506 /* build up directory set for trash directory */
507 FileMgrBuildDirectories (file_mgr_data, home_host_name, trash_dir);
509 /* initialize trash data */
510 file_mgr_data->restricted_directory =
511 XtNewString(file_mgr_data->current_directory);
512 file_mgr_data->title = XtNewString(title);
513 file_mgr_data->toolbox = False;
514 file_mgr_data->width = 300;
515 file_mgr_data->height = 300;
517 /* load any positioning information */
518 LoadPositionInfo(file_mgr_data);
520 /* build the trash dialog */
521 _DtBuildDialog (NULL, NULL, NULL, dialog_data, NULL, NULL, NULL,
522 NULL, NULL, False, special_view, title, NULL);
530 /************************************************************************
533 * At startup time, we need to read in the trash information file, to
534 * find out what, if any, files were left in the trash the last time
535 * this client was run. Each entry must be read in and then verified
536 * that it still exists; if it no longer exists, then someone has been
537 * mucking with our trash files, so we will remove it from our knowledge
538 * base. Otherwise, the file is added to the trash list.
540 ************************************************************************/
543 ReadTrashList( void )
545 int intSize, extSize, bufSize;
546 FILE * trashInfoFileId;
547 String external, intName, internal;
551 trashInfoFileId = fopen(TrashInfoFileName, "a+");
552 if (trashInfoFileId == 0)
556 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
558 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.") )
559 + strlen( TrashInfoFileName )
562 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 );
564 _DtMessage(toplevel, title, msg, NULL, HelpRequestCB);
570 chmod(TrashInfoFileName, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
572 bufSize = MAX_PATH * 2;
573 trashEntry = (char *)XtCalloc( 1, bufSize );
575 /* Extract the size of the two path strings */
576 while( fgets( trashEntry, bufSize, trashInfoFileId ) != NULL )
578 int len = strlen( trashEntry );
580 if( sscanf( trashEntry, "%d %d", &extSize, &intSize ) == 2 )
582 external = (char *)XtCalloc( 1, extSize + 1 );
583 intName = (char *)XtCalloc( 1, intSize + 1 );
585 if( len > extSize + intSize + 3 )
586 /* extSize + intSize + 3 is a rough estimate of a trash entry
587 a trash entry looks something like the following:
591 extSize is the size of the string '/tmp/abc'
592 intSize is the size of the string 'abcd'
594 so the len (the string length of the trash entry has AT LEAST to be
595 bigger than extSize + intSize + 3 separator spaces
600 /* sscanf can be used to reduce the code.
601 I'm not using it here because I don't have time to research.
602 I just want it to work.
604 tmpPtr = DtStrchr( trashEntry, ' ' ) + 1;
605 tmpPtr = DtStrchr( tmpPtr, ' ' ) + 1;
607 /* Extract the full external and partial internal file names */
608 memcpy( external, tmpPtr, extSize );
609 memcpy( intName, tmpPtr + extSize + 1, intSize );
611 /* Create internal/trash name */
612 internal = (char *)XtMalloc(strlen(users_home_dir)
614 + strlen(intName) + 2);
615 sprintf(internal, "%s%s/%s", users_home_dir, TRASH_DIR, intName);
617 /* Make sure the file still exists */
618 if (lstat(internal, &statbuf) < 0)
620 /* File no longer exists; ignore it */
627 /* Add to trash list */
628 if (numTrashItems >= trashListSize)
631 trashCan = (TrashEntry *)XtRealloc((char *) trashCan,
632 sizeof(TrashEntry) * trashListSize);
635 trashCan[numTrashItems].intNew = internal;
636 trashCan[numTrashItems].intOrig = XtNewString(external);
637 trashCan[numTrashItems].external = external;
638 trashCan[numTrashItems].filename = intName;
640 } /* end if file exists */
649 fclose(trashInfoFileId);
651 return( WriteTrashEntries() );
656 /************************************************************************
659 * This function is responsible for creating a new copy of the trash
660 * information file. It will open a new, temporary copy, and will
661 * copy in the contents of the trash can. It will then remove the
662 * old trash information file, and will rename the new one, opening
663 * a handle to it for later use.
665 ************************************************************************/
668 WriteTrashEntries( void )
670 static String path = NULL;
674 newFile = fopen(NewTrashInfoFileName, "w+");
677 chmod(NewTrashInfoFileName,
678 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
680 /* Write all remaining entries */
681 for (i = 0; i < numTrashItems; i++)
683 if( WriteEntry(newFile, trashCan[i].external, trashCan[i].filename) < 0 )
686 remove(NewTrashInfoFileName);
691 /* Remove the original information file, and move the new one */
692 (void) fclose(newFile);
693 (void) chown(NewTrashInfoFileName, getuid(), getgid());
694 (void) remove(TrashInfoFileName);
695 (void) rename(NewTrashInfoFileName, TrashInfoFileName);
696 (void) chown(TrashInfoFileName, getuid(), getgid());
705 /************************************************************************
708 * This function writes an entry to the trash information file.
709 * It expects the full path name for the original file [external]
710 * and only the new file name (without path information) [internal].
711 * If the file ends in '/', then it adds a '.' to the end; this is
712 * a special case, so that when we parse this information later, we
713 * are guaranteed to have a directory and a file name.
715 ************************************************************************/
723 return( fprintf(id, "%ld %ld %s %s\n",
724 (long)strlen(external), (long)strlen(internal),
725 external, internal) );
730 /************************************************************************
734 ************************************************************************/
739 FileMgrRec *file_mgr_rec )
744 static Widget * directoryBarBtn;
746 /* Create the menubar hierarchy */
747 trashMenu = (MenuDesc *)XtMalloc(sizeof(MenuDesc) * trashMenuItemCount);
750 for (i = 0; i < trashMenuItemCount; i++)
752 trashMenu[i].helpCallback = TrashHelpRequestCB;
753 trashMenu[i].helpData = NULL;
754 trashMenu[i].activateCallback = NULL;
755 trashMenu[i].activateData = NULL;
756 trashMenu[i].maskBit = NULL;
757 trashMenu[i].isHelpBtn = False;
758 trashMenu[i].label = NULL;
759 trashMenu[i].mnemonic = NULL;
763 /*************************************/
764 /* Create the 'File' menu components */
765 /*************************************/
767 directoryBarBtn = &(trashMenu[j].widget);
768 trashMenu[j].type = MENU_PULLDOWN_BUTTON;
769 trashMenu[j].label = GETMESSAGE(20,1, "File");
770 trashMenu[j].label = XtNewString(trashMenu[j].label);
771 trashMenu[j].mnemonic = GETMESSAGE(20,2, "F");
772 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
773 trashMenu[j].name = "file_trash";
774 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
776 trashMenu[j].type = SHARED_MENU_PANE;
777 trashMenu[j].name = "fileMenuTrash";
778 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
780 selectAllBtn = &(trashMenu[j].widget);
781 trashMenu[j].type = MENU_BUTTON;
782 trashMenu[j].label = GETMESSAGE(20,48, "Select All");
783 trashMenu[j].label = XtNewString(trashMenu[j].label);
784 trashMenu[j].mnemonic = GETMESSAGE(20,51, "S");
785 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
786 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
787 trashMenu[j].name = "selectAll";
788 trashMenu[j++].activateCallback = Select_All;
790 trashMenu[j].type = MENU_BUTTON;
791 trashMenu[j].label = GETMESSAGE(20,52, "Deselect All");
792 trashMenu[j].label = XtNewString(trashMenu[j].label);
793 trashMenu[j].mnemonic = GETMESSAGE(20,55, "D");
794 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
795 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
796 trashMenu[j].name = "deselectAll";
797 trashMenu[j++].activateCallback = Unselect_All;
799 trashMenu[j].type = MENU_SEPARATOR;
800 trashMenu[j].name = "separator";
801 trashMenu[j++].helpCallback = NULL;
803 restoreBtn = &(trashMenu[j].widget);
804 trashMenu[j].type = MENU_BUTTON;
805 trashMenu[j].label = GETMESSAGE(27,24, "Put back");
806 trashMenu[j].label = XtNewString(trashMenu[j].label);
807 trashMenu[j].mnemonic = GETMESSAGE(27,26, "P");
808 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
809 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
810 trashMenu[j].name = "putBack";
811 trashMenu[j++].activateCallback = Restore;
813 removeBtn = &(trashMenu[j].widget);
814 trashMenu[j].type = MENU_BUTTON;
815 trashMenu[j].label = GETMESSAGE(27,28, "Shred");
816 trashMenu[j].label = XtNewString(trashMenu[j].label);
817 trashMenu[j].mnemonic = GETMESSAGE(27,30, "h");
818 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
819 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
820 trashMenu[j].name = "shred";
821 trashMenu[j++].activateCallback = ConfirmRemove;
823 trashMenu[j].type = MENU_SEPARATOR;
824 trashMenu[j].name = "separator";
825 trashMenu[j++].helpCallback = NULL;
827 trashMenu[j].type = MENU_BUTTON;
828 trashMenu[j].maskBit = PREFERENCES;
829 trashMenu[j].label = GETMESSAGE(20,141, "Set View Options ...");
830 trashMenu[j].label = XtNewString(trashMenu[j].label);
831 trashMenu[j].mnemonic = GETMESSAGE(20,6, "V");
832 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
833 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
834 trashMenu[j].name = "setPreferences";
835 trashMenu[j++].activateCallback = ShowPreferencesDialog;
837 trashMenu[j].type = MENU_SEPARATOR;
838 trashMenu[j].name = "separator";
839 trashMenu[j++].helpCallback = NULL;
841 trashMenu[j].type = MENU_BUTTON;
842 trashMenu[j].label = GETMESSAGE(20,117, "Close");
843 trashMenu[j].label = XtNewString(trashMenu[j].label);
844 trashMenu[j].mnemonic = GETMESSAGE(20,118, "C");
845 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
846 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
847 trashMenu[j].name = "close";
848 trashMenu[j++].activateCallback = CloseTrash;
851 /*************************************/
852 /* Create the 'Help' menu components */
853 /*************************************/
855 trashMenu[j].type = MENU_PULLDOWN_BUTTON;
856 trashMenu[j].label = GETMESSAGE(20,123, "Help");
857 trashMenu[j].label = XtNewString(trashMenu[j].label);
858 trashMenu[j].mnemonic = GETMESSAGE(20,9, "H");
859 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
860 trashMenu[j].isHelpBtn = True;
861 trashMenu[j].name = "help_trash";
862 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
864 trashMenu[j].type = SHARED_MENU_PANE;
865 trashMenu[j].name = "help_pane_trash";
866 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
868 trashMenu[j].type = MENU_BUTTON;
869 trashMenu[j].label = GETMESSAGE(20,105, "Overview");
870 trashMenu[j].label = XtNewString(trashMenu[j].label);
871 trashMenu[j].mnemonic = GETMESSAGE(20,106, "v");
872 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
873 trashMenu[j].helpData = HELP_HELP_MENU_STR;
874 trashMenu[j].activateData = HELP_TRASH_OVERVIEW_TOPIC_STR;
875 trashMenu[j].name = "introduction";
876 trashMenu[j++].activateCallback = TrashHelpRequestCB;
878 trashMenu[j].type = MENU_SEPARATOR;
879 trashMenu[j].name = "separator";
880 trashMenu[j++].helpCallback = NULL;
882 trashMenu[j].type = MENU_BUTTON;
883 trashMenu[j].label = GETMESSAGE(20,107, "Tasks");
884 trashMenu[j].label = XtNewString(trashMenu[j].label);
885 trashMenu[j].mnemonic = GETMESSAGE(20,108, "T");
886 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
887 trashMenu[j].helpData = HELP_HELP_MENU_STR;
888 trashMenu[j].activateData = HELP_TRASH_TASKS_TOPIC_STR;
889 trashMenu[j].name = "tasks";
890 trashMenu[j++].activateCallback = TrashHelpRequestCB;
892 trashMenu[j].type = MENU_BUTTON;
893 trashMenu[j].label = GETMESSAGE(20,109, "Reference");
894 trashMenu[j].label = XtNewString(trashMenu[j].label);
895 trashMenu[j].mnemonic = GETMESSAGE(20,110, "R");
896 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
897 trashMenu[j].helpData = HELP_HELP_MENU_STR;
898 trashMenu[j].activateData = HELP_TRASH_DIALOG_STR;
899 trashMenu[j].name = "reference";
900 trashMenu[j++].activateCallback = TrashHelpRequestCB;
902 trashMenu[j].type = MENU_BUTTON;
903 trashMenu[j].label = GETMESSAGE(20,111, "On Item");
904 trashMenu[j].label = XtNewString(trashMenu[j].label);
905 trashMenu[j].mnemonic = GETMESSAGE(20,112, "O");
906 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
907 trashMenu[j].helpData = HELP_HELP_MENU_STR;
908 trashMenu[j].activateData = HELP_HELP_MODE_STR;
909 trashMenu[j].name = "onItem";
910 trashMenu[j++].activateCallback = TrashHelpRequestCB;
912 trashMenu[j].type = MENU_SEPARATOR;
913 trashMenu[j].name = "separator";
914 trashMenu[j++].helpCallback = NULL;
916 usingHelpTrash = &(trashMenu[j].widget);
917 trashMenu[j].type = MENU_BUTTON;
918 trashMenu[j].label = GETMESSAGE(20,113, "Using Help");
919 trashMenu[j].label = XtNewString(trashMenu[j].label);
920 trashMenu[j].mnemonic = GETMESSAGE(20,114, "U");
921 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
922 trashMenu[j].helpData = HELP_HELP_MENU_STR;
923 trashMenu[j].activateData = HELP_HOME_TOPIC;
924 trashMenu[j].name = "usingHelp";
925 trashMenu[j++].activateCallback = TrashHelpRequestCB;
927 trashMenu[j].type = MENU_SEPARATOR;
928 trashMenu[j].name = "separator";
929 trashMenu[j++].helpCallback = NULL;
931 trashMenu[j].type = MENU_BUTTON;
932 trashMenu[j].label = GETMESSAGE(27,106, "About Trash Can");
933 trashMenu[j].label = XtNewString(trashMenu[j].label);
934 trashMenu[j].mnemonic = GETMESSAGE(20,116, "A");
935 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
936 trashMenu[j].helpData = HELP_HELP_MENU_STR;
937 trashMenu[j].activateData = HELP_ABOUT_STR;
938 trashMenu[j].name = "version";
939 trashMenu[j++].activateCallback = TrashHelpRequestCB;
941 menu_bar = _DtCreateMenuSystem(mainw, "menu_bar_trash", TrashHelpRequestCB,
942 HELP_TRASH_DIALOG_STR, True,
943 trashMenu, trashMenuItemCount, NULL,
946 /* Fine tune the menubar */
947 XtSetArg(args[0], XmNmarginWidth, 2);
948 XtSetArg(args[1], XmNmarginHeight, 2);
949 XtSetValues(menu_bar, args, 2);
951 file_mgr_rec->actions = NULL;
952 file_mgr_rec->action_pane = NULL;
953 file_mgr_rec->directoryBarBtn = *directoryBarBtn;
960 /************************************************************************
962 * TrashDisplayHandler
963 * This is the ICCCM message handler for the message used to display
964 * the trash can window. If the window is already displayed, then
965 * this call is a no-op.
967 * This handler is triggered by the front panel trash icon and by the
968 * File pulldown 'Show Trash' option.
970 ************************************************************************/
980 tt_message_reply( msg );
981 tttk_message_destroy( msg );
984 if (!TrashInitialized)
986 char *tmpStr, *tmpTitle, *tmpMsg;
988 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
989 tmpTitle = XtNewString(tmpStr);
990 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.");
991 tmpMsg = XtNewString(tmpStr);
993 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1000 #ifdef DEFER_TRASH_CREATION
1001 if( trashFileMgrData == NULL )
1002 TrashCreateDialog (XtDisplay(toplevel));
1005 /* the encapsulation functions do not set file_mgr_rec until a dialog */
1006 /* is actually displayed */
1007 if (trashFileMgrData->file_mgr_rec == 0)
1009 static Pixmap trash_icon = XmUNSPECIFIED_PIXMAP;
1010 static Pixmap trash_mask = XmUNSPECIFIED_PIXMAP;
1012 Pixel background, foreground, top_shadow, bottom_shadow, select;
1014 XClassHint classHints;
1015 FileMgrRec * file_mgr_rec;
1017 unsigned int height;
1021 classHints.res_name = trashFileMgrData->title;
1022 classHints.res_class = DTFILE_CLASS_NAME;
1025 _DtShowBuiltDialog(NULL,NULL, trashDialogData, NULL, False, &classHints);
1028 file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
1030 trashShell = file_mgr_rec->shell;
1032 if (trash_icon == XmUNSPECIFIED_PIXMAP)
1034 XtSetArg (args[0], XmNbackground, &background);
1035 XtSetArg (args[1], XmNcolormap, &colormap);
1036 XtGetValues (file_mgr_rec->main, args, 2);
1038 XmGetColors (XtScreen (file_mgr_rec->main), colormap, background,
1039 &foreground, &top_shadow, &bottom_shadow, &select);
1041 pixmap = XmGetPixmap (XtScreen (file_mgr_rec->main), trashIcon,
1042 foreground, background);
1043 if( pixmap != XmUNSPECIFIED_PIXMAP)
1044 trash_icon = pixmap;
1046 /* now let's get the mask for the File Manager */
1047 pixmap = _DtGetMask (XtScreen (file_mgr_rec->main), trashIcon);
1048 if( pixmap != XmUNSPECIFIED_PIXMAP)
1049 trash_mask = pixmap;
1051 if (trash_icon != XmUNSPECIFIED_PIXMAP)
1053 XtSetArg (args[0], XmNiconPixmap, trash_icon);
1054 if(trash_mask != XmUNSPECIFIED_PIXMAP)
1056 XtSetArg (args[1], XmNiconMask, trash_mask);
1057 XtSetValues (trashShell, args, 2);
1060 XtSetValues (trashShell, args, 1);
1068 root = RootWindowOfScreen (XtScreen (trashShell));
1070 /* Change the hints to reflect the current workspace */
1071 /* and raise the window */
1073 if (DtWsmGetCurrentWorkspace (XtDisplay (trashShell),
1074 root, ¤t_ws) == Success)
1075 DtWsmSetWorkspacesOccupied (XtDisplay (trashShell),
1076 XtWindow (trashShell), ¤t_ws, 1);
1078 /* Set the iconify state */
1079 /* Remove the iconify hint from the current shell */
1080 wmhints = XGetWMHints(XtDisplay(trashShell), XtWindow(trashShell));
1081 wmhints->flags |= IconWindowHint;
1082 wmhints->initial_state = NormalState;
1083 XSetWMHints(XtDisplay(trashShell), XtWindow(trashShell), wmhints);
1087 XtPopup (trashShell, XtGrabNone);
1088 XSync(XtDisplay(trashShell), False);
1089 XRaiseWindow (XtDisplay (trashShell), XtWindow (trashShell));
1090 XMapWindow( XtDisplay (trashShell), XtWindow (trashShell) );
1093 msg = tt_pnotice_create(TT_SESSION, "DtActivity_Began");
1094 tt_message_send(msg);
1095 tttk_message_destroy(msg);
1099 trashDialogPosted = True;
1103 /************************************************************************
1105 * ConfirmEmptyCancel
1107 ************************************************************************/
1108 static XtPointer trash_popup_client_data;
1112 XtPointer client_data,
1113 XtPointer call_data )
1115 XtUnmanageChild((Widget)client_data);
1116 XmUpdateDisplay((Widget)client_data);
1117 XtDestroyWidget((Widget)client_data);
1120 /************************************************************************
1124 ************************************************************************/
1129 XtPointer client_data,
1130 XtPointer call_data )
1132 /* destroy dialog */
1133 XtUnmanageChild((Widget)client_data);
1134 XmUpdateDisplay((Widget)client_data);
1135 XtDestroyWidget((Widget)client_data);
1140 /************************************************************************
1143 * This is a message interface used to really remove all of the
1146 * This handler is only called from an empty trash action.
1148 ************************************************************************/
1155 tt_message_reply( msg );
1156 tttk_message_destroy( msg );
1159 if(!TrashInitialized)
1161 char *tmpStr, *tmpTitle, *tmpMsg;
1163 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1164 tmpTitle = XtNewString(tmpStr);
1165 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.");
1166 tmpMsg = XtNewString(tmpStr);
1168 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1176 char *tmpStr, *title, *msg;
1178 tmpStr = GETMESSAGE(27,73, "Shred File(s)");
1179 title = XtNewString(tmpStr);
1180 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");
1181 msg = XtNewString(tmpStr);
1183 _DtMessageDialog(toplevel, title, msg, NULL, TRUE, ConfirmEmptyCancel,
1184 ConfirmEmptyOk, NULL, HelpRequestCB, False, QUESTION_DIALOG);
1193 /************************************************************************
1196 * This is a functional interface used to really remove all of the
1199 ************************************************************************/
1204 if (trashFileMgrData)
1207 Select_All(*selectAllBtn, NULL, NULL);
1208 Remove(*removeBtn, NULL, NULL);
1210 EmptyDir(trashFileMgrData->current_directory,0);
1216 /************************************************************************
1219 * This is the callback attached to the 'Select All' menu item. It
1220 * will select all of the files currently resident in the trash can.
1221 * It will also sensitize the 'Restore' and 'Remove' buttons, as long
1222 * as a remove request is not currently in the middle of being processed.
1224 ************************************************************************/
1229 XtPointer client_data,
1230 XtPointer call_data )
1233 SelectAllFiles(trashFileMgrData);
1234 SensitizeTrashBtns();
1239 /************************************************************************
1242 * This is the callback attached to the 'Unselect All' menu item. It
1243 * will unselect all of the items in the trash can, and will clear
1244 * out our selection array. It also desensitizes the 'Restore' and
1247 ************************************************************************/
1252 XtPointer client_data,
1253 XtPointer call_data )
1256 DeselectAllFiles(trashFileMgrData);
1257 SensitizeTrashBtns();
1262 /************************************************************************
1264 * SensitizeTrashBtns
1266 ************************************************************************/
1269 SensitizeTrashBtns(void)
1271 #ifdef DEFER_TRASH_CREATION
1272 if( trashFileMgrData
1277 if (trashFileMgrData->selected_file_count > 0)
1279 XtSetSensitive(*removeBtn, True);
1280 XtSetSensitive(*restoreBtn, True);
1284 XtSetSensitive(*removeBtn, False);
1285 XtSetSensitive(*restoreBtn, False);
1292 /************************************************************************
1294 * TrashRemoveHandler
1295 * This is the message handler for the message requesting that
1296 * a set of files be added to the trashcan. Each file name comes in
1297 * in /dir/name format; they are all in a single string, separated
1300 * Check for spaces in filenames and destop files. If we pass these
1301 * cases, call TrashRemover where the following occurs:
1303 * All of the files will be moved to the trash can directory; any
1304 * problems encountered will be reported in an error dialog. Each file
1305 * which is successfully moved, will be added to the trash information file.
1307 ************************************************************************/
1320 int msglistsize = 0 ;
1321 #endif /* SUN_PERF */
1324 * Note: There used to be a call to tt_message_reply() right here at the
1325 * beginning of the routine. However, the mailer wants to trash files
1326 * but wants the notification after the trash has been removed.
1327 * Therefore, we now put this off until the work is done or an error
1331 if( !TrashInitialized )
1333 char *tmpStr, *tmpTitle, *tmpMsg;
1335 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1336 tmpTitle = XtNewString(tmpStr);
1337 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.");
1338 tmpMsg = XtNewString(tmpStr);
1340 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1345 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
1347 global_msg_cnt = 0 ;
1348 #endif /* SUN_PERF */
1352 MessageToFileList(msg, &file_list, &file_count);
1355 * When the Dttrash_Remove handler "TrashRemoveHandler" is invoked for
1356 * the first time it caches the TT request and then invokes
1357 * tttk_block_while() with timeout = 0. This in effect causes the next
1358 * TT request to be processed before tttk_block_while() returns. Now if
1359 * TT msgs are coming in faster than dtfile is handling them, then the
1360 * next TT msg. should be another Dttrash_Remove msg. which will cause
1361 * re-entrance into TrashRemoveHandler. This will continue until the last
1362 * Dttrash_Remove msg. has been processed (and the recursive
1363 * TrashRemoveHandler starts unwinding) after which we check if the
1364 * number of TT msgs are > 0 in which case we fire off the
1365 * process to move the list of files to Trash.
1368 msglistsize = sizeof(Tt_message) * global_msg_cnt ;
1369 global_msg_list = (Tt_message *)XtRealloc((char *)global_msg_list, msglistsize);
1370 memcpy((char *)&(global_msg_list[global_msg_cnt - 1]),
1371 (char *)&msg, sizeof(Tt_message)) ;
1372 tttk_block_while(0, 0, time_out) ;
1374 /* If we get to this pt. then either :
1375 * 1. No more Dttrash_Remove msgs. left to process. Thus we should
1376 * process the TT msg list and move files to Trash.
1378 * 2. We got a msg. different from Dttrash_Remove. In this case
1379 * we process the msgs. we have so far. The remaining files to
1380 * be trashed will be buffered in another list.
1383 if (global_msg_cnt > 0) {
1384 MessagesToFileList(global_msg_list, global_msg_cnt,
1385 &file_list, &file_count);
1386 #endif /* SUN_PERF */
1388 if (file_count == 0)
1390 tt_message_reply( msg );
1391 tttk_message_destroy( msg );
1393 global_msg_cnt = 0 ;
1394 #endif /* SUN_PERF */
1398 /* return if from the trash dir */
1399 if (strncmp(file_list[0], trash_dir, strlen(trash_dir)) == 0)
1403 for (i = 0; i < file_count; i++) {
1404 XtFree( file_list[i] );
1406 XtFree( (char *)file_list );
1407 tt_message_status_set( msg, TT_DESKTOP_EALREADY );
1408 tt_message_reply( msg );
1409 tttk_message_destroy( msg );
1411 global_msg_cnt = 0 ;
1412 #endif /* SUN_PERF */
1416 /* post message if file(s) from desktop */
1420 str=(char *)IsAFileOnDesktop2(file_list, file_count, &number,&IsToolBox);
1421 /* IsToolBox is unused here, but is required to satisfy the prototype */
1425 MoveToTrash(file_list, file_count, verifyPromptsEnabled, msg);
1435 /* List will be regenerated by callbacks */
1436 for (i = 0; i < file_count; i++) {
1437 XtFree( file_list[i] );
1439 XtFree( (char *)file_list );
1441 tmpStr = (GETMESSAGE(27,73, "Shred File(s)"));
1442 title = XtNewString(tmpStr);
1446 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."));
1448 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."));
1449 message = (char *)XtMalloc(strlen(template) + strlen(str) + 1);
1450 sprintf(message, template, str);
1455 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."));
1457 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."));
1458 message = (char *)XtMalloc(strlen(template) + strlen(str) + 1);
1459 sprintf(message, template, str);
1462 /* Really ought to pass to OK and Cancel CBs via client_data */
1465 _DtMessageDialog(toplevel, title, message, NULL, TRUE,
1466 RemoveCancelCB, RemoveOkCB, NULL, HelpRequestCB, False,
1472 Tt_msg_cache *msg_cache ;
1474 msg_cache = (Tt_msg_cache *)XtMalloc(sizeof(Tt_msg_cache)) ;
1475 msg_cache->msg_cnt = global_msg_cnt ;
1477 msg_cache->msg_list = (Tt_message *)XtMalloc(sizeof(Tt_message) *
1478 msg_cache->msg_cnt );
1479 msg_cache->msg_list[msg_cache->msg_cnt] = NULL ;
1480 memcpy((char *)msg_cache->msg_list, (char *)global_msg_list,
1481 sizeof(Tt_message) * msg_cache->msg_cnt ) ;
1482 dlog = (Widget)_DtMessageDialog(toplevel, title, message, NULL, TRUE,
1483 RemoveCancelCB, RemoveOkCB, NULL, HelpRequestCB, False,
1485 XtSetArg ( args[0], XmNuserData, msg_cache );
1486 XtSetValues ( dlog, args, 1);
1488 #endif /* SUN_PERF */
1495 global_msg_cnt = 0 ;
1497 #endif /* SUN_PERF */
1502 /************************************************************************
1505 * Cleanup and unmanage the remove from DT dialog
1507 ************************************************************************/
1512 XtPointer client_data,
1513 XtPointer call_data )
1519 Tt_msg_cache *current_msg_cache ;
1522 XtSetArg( args[0], XmNuserData, ¤t_msg_cache );
1523 XtGetValues( (Widget)client_data, args, 1 );
1524 #endif /* SUN_PERF */
1526 /* destroy dialog */
1527 XtUnmanageChild((Widget)client_data);
1528 XmUpdateDisplay((Widget)client_data);
1529 XtDestroyWidget((Widget)client_data);
1533 MessageToFileList(global, &file_list, &file_count);
1535 MessagesToFileList( current_msg_cache->msg_list, current_msg_cache->msg_cnt,
1536 &file_list, &file_count);
1537 #endif /* SUN_PERF */
1539 MoveToTrash(file_list, file_count, verifyPromptsEnabled, NULL);
1541 /* reread desktop files */
1545 tt_message_reply( global );
1546 tttk_message_destroy( global );
1549 for (i = 0 ; i < current_msg_cache->msg_cnt ; i++) {
1550 tt_message_reply( current_msg_cache->msg_list[i] );
1551 tttk_message_destroy( current_msg_cache->msg_list[i] );
1553 XtFree ((char *)current_msg_cache->msg_list);
1554 XtFree ((char *)current_msg_cache);
1555 #endif /* SUN_PERF */
1560 /************************************************************************
1563 * Cleanup and unmanage the remove from DT dialog
1565 ************************************************************************/
1570 XtPointer client_data,
1571 XtPointer call_data )
1575 Tt_msg_cache *current_msg_cache ;
1578 XtSetArg( args[0], XmNuserData, ¤t_msg_cache );
1579 XtGetValues( (Widget)client_data, args, 1 );
1580 #endif /* SUN_PERF */
1582 /* destroy dialog */
1583 XtUnmanageChild((Widget)client_data);
1584 XmUpdateDisplay((Widget)client_data);
1585 XtDestroyWidget((Widget)client_data);
1588 tttk_message_fail( global, TT_DESKTOP_ECANCELED, 0, 1 );
1591 for (i = 0 ; i < current_msg_cache->msg_cnt ; i++)
1592 tttk_message_fail( current_msg_cache->msg_list[i], TT_DESKTOP_ECANCELED, 0, 1 );
1593 XtFree ((char *)current_msg_cache->msg_list);
1594 XtFree ((char *)current_msg_cache);
1595 #endif /* SUN_PERF */
1600 /************************************************************************
1602 * TrashRemoveNoConfirmHandler
1603 * This function is a message handler. It will place the specified set
1604 * of files into the trash can, without requesting confirmation if, for
1605 * instance, a file does not have write permission, or a directory is
1608 ************************************************************************/
1611 TrashRemoveNoConfirmHandler(
1617 if( !TrashInitialized )
1619 char *tmpStr, *tmpTitle, *tmpMsg;
1621 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1622 tmpTitle = XtNewString(tmpStr);
1623 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.");
1624 tmpMsg = XtNewString(tmpStr);
1626 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1632 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
1636 MessageToFileList(msg, &file_list, &file_count);
1637 MoveToTrash(file_list, file_count, False, msg);
1642 /************************************************************************
1646 * Parses a list of file names from a message
1647 * and returns an array of strings.
1649 * if (no message arguments) {
1650 * if (no file attribute) {
1651 * return length==0 list
1653 * return length==1 list containing file attribute
1656 * if (file attribute) {
1657 * file attribute is a directory, prepended onto subsequent filenames
1659 * if (arg 0 is non-null) {
1660 * arg 0 interpreted as a space-separated list of filenames
1663 * arg interpreted as a filename possibly containing spaces
1667 ************************************************************************/
1683 args = tt_message_args_count( msg );
1684 if (tt_is_err(tt_int_error( args ))) {
1688 dir = tt_message_file( msg );
1689 if ((tt_is_err(tt_ptr_error( dir ))) || (dir == 0)) {
1693 /* No args means use tt_message_file() as is */
1694 *file_list = (char **)
1695 XtRealloc((char *)(*file_list),
1696 (*file_count + 1)*sizeof(char *));
1697 (*file_list)[*file_count] = XtNewString(dir);
1702 if (dir[strlen(dir)] != '/') {
1704 if (strcmp(dir, "/") == 0)
1706 newdir = (char *)tt_malloc( strlen(dir)+1 );
1707 strcpy( newdir, dir );
1711 newdir = (char *)tt_malloc( strlen(dir)+2 );
1712 strcpy( newdir, dir );
1713 strcat( newdir, "/" );
1719 /* Invariant: dir can now be safely concatenated to form a valid path */
1720 dirlen = strlen(dir);
1721 for (arg = 0; arg < args; arg++) {
1726 val = tt_message_arg_val( msg, arg );
1727 if ((tt_is_err(tt_ptr_error( val ))) || (val == 0)) {
1733 /* Arg 0 is a space-separated list */
1736 for (val = DtStrtok(val, white); val; val = DtStrtok(0, white))
1738 if (strcmp(val, "/") == 0)
1740 file = (char *)XtMalloc( dirlen + 1 );
1741 strcpy( file, dir );
1745 file = (char *)XtMalloc( dirlen + strlen(val) + 1 );
1746 strcpy( file, dir );
1747 strcat( file, val );
1749 *file_list = (char **)
1750 XtRealloc((char *)(*file_list),
1751 (*file_count + 1)*sizeof(char *));
1752 (*file_list)[*file_count] = DtEliminateDots(file);
1755 tt_free( val2free );
1763 Tt_message *msg_list,
1778 for (num_msgs = 0 ; num_msgs < msg_cnt ; num_msgs++)
1780 msg = msg_list[num_msgs] ;
1782 args = tt_message_args_count( msg );
1783 if (tt_is_err(tt_int_error( args ))) {
1787 dir = tt_message_file( msg );
1788 if ((tt_is_err(tt_ptr_error( dir ))) || (dir == 0)) {
1792 /* No args means use tt_message_file() as is */
1793 *file_list = (char **)
1794 XtRealloc((char *)(*file_list),
1795 (*file_count + 1)*sizeof(char *));
1796 (*file_list)[*file_count] = XtNewString(dir);
1801 if (dir[strlen(dir)] != '/') {
1803 if (strcmp(dir, "/") == 0)
1805 newdir = (char *)tt_malloc( strlen(dir)+1 );
1806 strcpy( newdir, dir );
1810 newdir = (char *)tt_malloc( strlen(dir)+2 );
1811 strcpy( newdir, dir );
1812 strcat( newdir, "/" );
1818 /* Invariant: dir can now be safely concatenated to form a valid path */
1819 dirlen = strlen(dir);
1820 for (arg = 0; arg < args; arg++) {
1825 val = tt_message_arg_val( msg, arg );
1826 if ((tt_is_err(tt_ptr_error( val ))) || (val == 0)) {
1832 /* Arg 0 is a space-separated list */
1835 for (val = DtStrtok(val, white); val; val = DtStrtok(0, white))
1837 if (strcmp(val, "/") == 0)
1839 file = (char *)XtMalloc( dirlen + 1 );
1840 strcpy( file, dir );
1842 else if (strcmp(val, "/") == 0 && dirlen == 0)
1844 file = (char *)XtMalloc( strlen(val) + 1 );
1845 strcpy( file, val );
1849 file = (char *)XtMalloc( dirlen + strlen(val) + 1 );
1850 strcpy( file, dir );
1851 strcat( file, val );
1853 *file_list = (char **)
1854 XtRealloc((char *)(*file_list),
1855 (*file_count + 1)*sizeof(char *));
1856 (*file_list)[*file_count] = DtEliminateDots(file);
1859 tt_free( val2free );
1864 #endif /* SUN_PERF */
1867 /************************************************************************
1869 * CreateTrashFilename
1870 * Create trash directory name.
1872 ************************************************************************/
1875 CreateTrashFilename(
1877 Boolean uniqueTest )
1880 char * extension = NULL;
1881 struct stat statInfo;
1883 /* Create trash path */
1884 /* Give that name a little extra cushion, just in case */
1885 trashName = (char *)XtMalloc(strlen(users_home_dir) + strlen(TRASH_DIR) +
1886 strlen(baseName) + 15);
1887 sprintf(trashName, "%s%s/%s", users_home_dir, TRASH_DIR, baseName);
1889 /* Want to find the extension so the new file name created will preserve
1890 its original datatype.
1892 extension = strrchr( baseName, '.' );
1899 /* If a file by the trash name already exists, keep building new names */
1900 /* until one by that name doesn't exist and then use it */
1901 if (uniqueTest && (lstat(trashName, &statInfo) == 0))
1905 for (i = 1; True ; i++)
1907 /* Make a duplicate file name */
1909 sprintf(trashName, "%s%s/%s_%d.%s", users_home_dir, TRASH_DIR,
1910 baseName, i, extension);
1912 sprintf(trashName, "%s%s/%s_%d", users_home_dir, TRASH_DIR,
1915 /* Is the filename taken? */
1916 if (lstat(trashName, &statInfo) != 0)
1920 } /* end if not unique name */
1923 *(extension-1) = '.';
1931 /************************************************************************
1933 * MatchesSacredDirectory
1934 * Checks if the specify filename matches one of the sacred,
1935 * non-deleteable files.
1937 ************************************************************************/
1940 MatchesSacredDirectory(
1945 /* Don't allow the user to delete any of the sacred directories */
1946 for (i = 0; i < sacred_dir_count; i++)
1948 if (!strcmp(file, sacred_dir_list[i]))
1952 /* Compare against special desktop directories */
1953 /* remote_sys_dir is currently NULL */
1954 if (strcmp(file, remote_sys_dir) == 0)
1962 /************************************************************************
1965 * Callback for 'Delete To Trash' verify dialog Ok push button.
1967 ************************************************************************/
1972 XtPointer client_data,
1973 XtPointer call_data )
1975 VerifyCleanup ((Widget)client_data, True);
1980 /************************************************************************
1983 * Callback for 'Delete To Trash' verify dialog Cancel push button.
1985 ************************************************************************/
1990 XtPointer client_data,
1991 XtPointer call_data )
1994 VerifyCleanup ((Widget)client_data, False);
1999 /************************************************************************
2002 * Callback for 'Delete To Trash' verify dialog push buttons.
2004 * Called from VerifyOk and VerifyCancel.
2006 ************************************************************************/
2011 Boolean completeDelete )
2018 /* Unpost dialog, and retrieve the list of verified files */
2019 XtUnmanageChild(mbox);
2020 XmUpdateDisplay(mbox);
2021 XtSetArg(args[0], XmNuserData, &verifylist);
2022 XtGetValues(mbox, args, 1);
2024 /* count the number of files in the list */
2025 for (fileCount = 0; verifylist[fileCount]; fileCount++)
2029 * Start the background process that moves the files into the trash,
2033 MoveToTrash(verifylist, fileCount, False, NULL);
2037 /* Free up the storage we allocated */
2038 for (i = fileCount; i > 0; i--)
2039 XtFree(verifylist[i]);
2040 XtFree((char *)verifylist);
2043 XtDestroyWidget(mbox);
2048 /************************************************************************
2052 ************************************************************************/
2053 static XtPointer trash_popup_client_data;
2057 XtPointer client_data,
2058 XtPointer call_data )
2060 XtUnmanageChild((Widget)client_data);
2061 XmUpdateDisplay((Widget)client_data);
2062 XtDestroyWidget((Widget)client_data);
2064 XtSetSensitive(*removeBtn, True);
2065 XtSetSensitive(*restoreBtn, True);
2066 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
2067 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
2068 trash_popup_client_data = NULL;
2073 /************************************************************************
2077 ************************************************************************/
2082 XtPointer client_data,
2083 XtPointer call_data )
2085 FileViewData *file_view_data;
2087 /* destroy dialog */
2088 XtUnmanageChild((Widget)client_data);
2089 XmUpdateDisplay((Widget)client_data);
2090 XtDestroyWidget((Widget)client_data);
2092 file_view_data = trashFileMgrData->popup_menu_icon;
2093 if(!file_view_data && trashFileMgrData->selected_file_count)
2094 file_view_data = trashFileMgrData->selection_list[0];
2095 trashFileMgrData->popup_menu_icon = NULL;
2097 if(file_view_data != NULL)
2098 Remove(*removeBtn, file_view_data, NULL);
2103 /************************************************************************
2106 * This is the callback attached to the 'Remove' menu item.
2108 ************************************************************************/
2113 XtPointer client_data,
2114 XtPointer call_data )
2116 char *tmpStr, *title, *msg;
2118 /* Desensitize remove & restore buttons, until we're done */
2119 XtSetSensitive(*removeBtn, False);
2120 XtSetSensitive(*restoreBtn, False);
2121 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], False);
2122 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], False);
2125 trash_popup_client_data = client_data;
2127 tmpStr = GETMESSAGE(27,73, "Shred File(s)");
2128 title = XtNewString(tmpStr);
2129 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");
2130 msg = XtNewString(tmpStr);
2132 _DtMessageDialog(toplevel, title, msg, NULL, TRUE, ConfirmCancel, ConfirmOk,
2133 NULL, HelpRequestCB, False, QUESTION_DIALOG);
2141 /************************************************************************
2144 * While we wait for the background process to complete, we will
2145 * desensitize the 'Remove' and 'Restore' menu items, since we don't
2146 * handle multiple requests.
2148 ************************************************************************/
2153 XtPointer client_data,
2154 XtPointer call_data )
2156 FileViewData *file_view_data;
2157 Boolean match = False;
2158 DeleteList *deleteList = NULL;
2164 /* Remove may be called to remove a file or to remove a trash file; */
2165 /* if w is NULL, this is a call to remove a file */
2168 removeType = TRASH_FILE;
2170 /* Count number of items to 'Delete To Trash' */
2171 if (client_data != NULL)
2173 /* This was called by trash popup */
2174 /* need to check to see if it is selected, if it is restore all
2175 the selected files, else just restore this file */
2177 file_view_data = (FileViewData *)client_data;
2178 for (i = 0; i < trashFileMgrData->selected_file_count; i++)
2180 if (strcmp(file_view_data->file_data->file_name,
2181 trashFileMgrData->selection_list[i]->file_data->file_name) == 0)
2189 if (trashFileMgrData->selected_file_count == 0)
2191 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
2192 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
2199 /* Desensitize remove & restore buttons, until we're done */
2200 XtSetSensitive(*removeBtn, False);
2201 XtSetSensitive(*restoreBtn, False);
2205 deleteCount = trashFileMgrData->selected_file_count;
2209 /* Create the list of things being deleted */
2210 deleteList = (DeleteList *)XtMalloc(deleteCount * sizeof(DeleteList));
2214 for (i = 0; i < deleteCount; i++)
2216 AddToDeleteList(deleteList, i,
2217 trashFileMgrData->selection_list[i]->file_data->file_name);
2221 AddToDeleteList(deleteList, 0, file_view_data->file_data->file_name);
2226 removeType = REMOVE_FILE;
2227 deleteList = (DeleteList *)XtMalloc(sizeof(DeleteList));
2229 deleteList[0].trash = XtNewString(client_data);
2230 deleteList[0].orig = NULL;
2233 EmptyTrash(deleteList, deleteCount, removeType, NULL);
2238 /************************************************************************
2241 * Locate a file in the trash list and add it to the delete list.
2243 ************************************************************************/
2247 DeleteList *deleteList,
2253 /* Locate file in trash list, add entry to delete list */
2254 for (j = 0; j < numTrashItems; j++)
2256 if (strcmp(filename, trashCan[j].filename) == 0)
2258 /* file found in trash list */
2259 deleteList[i].trash = XtNewString(trashCan[j].intNew);
2260 deleteList[i].orig = XtNewString(trashCan[j].intOrig);
2266 /* file not found in trash list */
2267 deleteList[i].trash = CreateTrashFilename(filename, FALSE);
2268 deleteList[i].orig = NULL;
2273 /************************************************************************
2275 * TrashRestoreHandler
2276 * This function is a message handler. It will restore the specified set
2277 * of files from the trash can.
2279 ************************************************************************/
2282 TrashRestoreHandler(
2288 if( !TrashInitialized )
2290 char *tmpStr, *tmpTitle, *tmpMsg;
2292 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
2293 tmpTitle = XtNewString(tmpStr);
2294 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.");
2295 tmpMsg = XtNewString(tmpStr);
2297 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
2303 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
2308 /* Desensitize remove & restore buttons, until we're done */
2309 XtSetSensitive(*removeBtn, False);
2310 XtSetSensitive(*restoreBtn, False);
2312 MessageToFileList(msg, &file_list, &file_count);
2313 RestoreFromTrash(file_list, file_count, NULL, NULL, msg,False);
2318 /************************************************************************
2321 * This is the callback attached to the 'Restore' menu item. It will
2322 * remove from the trash all of the selected files, and will restore
2323 * them to their original location. If any problems occur while trying
2324 * to restore any of the files, then an error dialog will be posted.
2326 ************************************************************************/
2331 XtPointer client_data,
2332 XtPointer call_data )
2334 FileViewData *file_view_data;
2335 Boolean match = False;
2340 if (client_data != NULL)
2342 /* This was called by trash popup */
2343 /* need to check to see if it is selected, if it is restore all
2344 the selected files, else just restore this file */
2346 file_view_data = trashFileMgrData->popup_menu_icon;
2348 /* The object would have been deleted if the following condn is true */
2349 if(!file_view_data && !trashFileMgrData->selected_file_count)
2351 trashFileMgrData->popup_menu_icon = NULL;
2353 for (i = 0; i < trashFileMgrData->selected_file_count; i++)
2355 if (strcmp(file_view_data->file_data->file_name,
2356 trashFileMgrData->selection_list[i]->file_data->file_name) == 0)
2365 /* Desensitize remove & restore buttons, until we're done */
2366 XtSetSensitive(*removeBtn, False);
2367 XtSetSensitive(*restoreBtn, False);
2372 file_count = trashFileMgrData->selected_file_count;
2373 file_list = (char **)XtMalloc(file_count * sizeof(char *));
2374 for (i = 0; i < file_count; i++)
2376 file_view_data = trashFileMgrData->selection_list[file_count - 1 - i];
2377 file_list[i] = XtNewString(file_view_data->file_data->file_name);
2383 file_list = (char **)XtMalloc(sizeof(char *));
2384 file_list[0] = XtNewString(file_view_data->file_data->file_name);
2387 /* Start the background process that will do the restore */
2388 RestoreFromTrash(file_list, file_count, NULL, NULL, NULL,False);
2393 /************************************************************************
2396 * This is the callback attached to the 'Close' menu item. It will
2397 * unpost the trash can window.
2399 ************************************************************************/
2404 XtPointer client_data,
2405 XtPointer call_data )
2409 if (trashDialogPosted)
2411 XWithdrawWindow(XtDisplay(trashShell), XtWindow(trashShell),
2412 XDefaultScreen(XtDisplay(trashShell)));
2413 XtPopdown(trashShell);
2417 for (i = 0; i < secondaryTrashHelpDialogCount; i++)
2419 if (_DtIsDialogShowing(secondaryTrashHelpDialogList[i]))
2420 _DtHideDialog(secondaryTrashHelpDialogList[i], False);
2421 _DtFreeDialogData(secondaryTrashHelpDialogList[i]);
2423 XtFree((char *)secondaryTrashHelpDialogList);
2424 secondaryTrashHelpDialogList = NULL;
2425 secondaryTrashHelpDialogCount = 0;
2427 if (primaryTrashHelpDialog)
2429 if (_DtIsDialogShowing(primaryTrashHelpDialog))
2430 _DtHideDialog(primaryTrashHelpDialog, False);
2431 _DtFreeDialogData(primaryTrashHelpDialog);
2433 primaryTrashHelpDialog = NULL;
2435 if (PositionFlagSet(trashFileMgrData))
2436 SavePositionInfo(trashFileMgrData);
2439 FileMgrRec * file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
2441 if( (file_mgr_rec->menuStates & PREFERENCES) == 0 )
2442 file_mgr_rec->menuStates |= PREFERENCES;
2445 trashDialogPosted = False;
2450 /************************************************************************
2454 ************************************************************************/
2461 DtDndDropCallbackStruct *drop_parameters)
2464 DtActionArg * action_args;
2465 FileMgrRec * file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
2466 Position drop_x = drop_parameters->x;
2467 Position drop_y = drop_parameters->y;
2469 _DtBuildActionArgsWithDroppedFiles(NULL, drop_parameters,
2470 &action_args, &arg_count);
2472 DtActionInvoke(file_mgr_rec->shell, TRASH_ACTION,
2473 action_args, arg_count, NULL, NULL,
2474 trashFileMgrData->current_directory, True, NULL, NULL);
2476 _DtFreeActionArgs(action_args, arg_count);
2478 RepositionIcons(trashFileMgrData, file_set, file_count, drop_x,
2484 /************************************************************************
2488 ************************************************************************/
2492 FileMgrData *file_mgr_data,
2493 FileMgrRec *file_mgr_rec,
2507 * Get target host and directory
2509 target_dir = XtNewString(file_mgr_data->current_directory);
2510 target_host = XtNewString(file_mgr_data->host);
2513 * Create file list and call RestoreFromTrash
2515 file_list = (char **)XtMalloc(file_count * sizeof(char *));
2517 for (i = 0; i < file_count; i++)
2518 file_list[i] = XtNewString(file_set[i]);
2520 RestoreFromTrash(file_list, file_count, target_host, target_dir, NULL,False);
2525 /************************************************************************
2528 * Locate file in trash list based on new internal name. Return file name.
2530 ************************************************************************/
2536 Boolean IsTrash = False;
2538 if (strncmp(users_home_dir, filename, strlen(users_home_dir)) == 0)
2541 ((filename + strlen(users_home_dir)), TRASH_DIR, strlen(TRASH_DIR))
2551 /************************************************************************
2554 * Since the trash does not use shared menupanes, there is no work to
2555 * be done during the popup and popdown callbacks; therefore, we use
2556 * and empty function.
2558 ************************************************************************/
2563 XtPointer clientData,
2564 XtPointer callData )
2571 /*--------------------------------------------------------------------
2572 * UpdateDirectoryOf:
2573 * Arrange for the directory containing a file to be updated
2574 *------------------------------------------------------------------*/
2583 /* remove last component from path to get the directory */
2584 ptr = strrchr(path, '/');
2588 strcpy(host, home_host_name);
2590 /* now arrange for the directory to be updated */
2591 UpdateDirectory(NULL, host, path);
2593 /* restore the path */
2599 /*--------------------------------------------------------------------
2600 * EraseObject, EraseDir
2601 * Routines for recursively deleting files and directories
2602 *------------------------------------------------------------------*/
2605 EraseObject(char *file_name)
2607 struct stat stat_buf;
2609 if (lstat(file_name, &stat_buf) < 0)
2612 else if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
2613 return EraseDir(file_name);
2615 else if (remove(file_name) < 0)
2624 EraseDir(char *dir_name)
2626 DIR *dir; /* open directory */
2627 struct dirent *entry; /* directory entry */
2628 char srcname[MAX_PATH];
2632 /* open source directory */
2633 dir = opendir(dir_name);
2637 /* prepare source name */
2638 strcpy(srcname, dir_name);
2639 srclen = strlen(srcname);
2640 if (srcname[srclen - 1] != '/')
2641 srcname[srclen++] = '/';
2644 while (rc == 0 && (entry = readdir(dir)) != NULL)
2646 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
2648 strcpy(srcname + srclen, entry->d_name);
2649 rc = EraseObject(srcname);
2656 if (rmdir(dir_name) < 0)
2658 if (unlink(dir_name) < 0 )
2670 /*====================================================================
2672 * MoveToTrashProcess
2673 * Run a background process to move files to the trash can.
2675 *==================================================================*/
2677 /*--------------------------------------------------------------------
2678 * MoveToTrashProcess
2679 * Main routine of background process for MoveToTrash
2680 *------------------------------------------------------------------*/
2687 Boolean do_verify_checks)
2693 struct stat statInfo;
2695 struct dirent * entry;
2700 for (i = 0; i < file_count; i++)
2702 /* get base name and full path */
2703 path = XtNewString(file_list[i]);
2704 baseName = strrchr(file_list[i], '/');
2705 if (baseName == NULL || path == NULL)
2707 /* Invalid filename */
2709 pipe_msg = PIPEMSG_OTHER_ERROR;
2710 rc = BAD_FILE_ERROR;
2711 DPRINTF(("MoveToTrashProcess: sending BAD_FILE_ERROR\n"));
2712 write(pipe_fd, &pipe_msg, sizeof(short));
2713 write(pipe_fd, &rc, sizeof(int));
2716 if (path && MatchesSacredDirectory(path))
2718 /* Invalid filename */
2720 pipe_msg = PIPEMSG_OTHER_ERROR;
2721 rc = BAD_FILE_SACRED;
2722 DPRINTF(("MoveToTrashProcess: sending BAD_FILE_SACRED\n"));
2723 write(pipe_fd, &pipe_msg, sizeof(short));
2724 write(pipe_fd, &rc, sizeof(int));
2729 if (do_verify_checks)
2731 char *tmpstr = (file_list[i] == (baseName - 1))?"/":file_list[i];
2733 Boolean TrashError = False;
2735 /* check write permissions for the file */
2736 if (lstat(path,&s1) < 0)
2743 savechar = *(baseName-1);
2744 *(baseName-1) = '\0';
2745 if(CheckDeletePermission(tmpstr, path) != 0)
2747 /* No write access; display error message */
2749 if (S_ISDIR(s1.st_mode))
2750 rc = BAD_TRASH_DIRECTORY;
2752 rc = BAD_TRASH_FILE;
2755 *(baseName-1) = savechar;
2760 pipe_msg = PIPEMSG_OTHER_ERROR;
2761 DPRINTF(("MoveToTrashProcess: sending BAD_TRASH message\n"));
2762 write(pipe_fd, &pipe_msg, sizeof(short));
2763 write(pipe_fd, &rc, sizeof(int));
2766 else if (CheckAccess(path, W_OK) != 0 && !S_ISLNK(s1.st_mode))
2768 /* No write access; ask user for verification */
2770 pipe_msg = PIPEMSG_OTHER_ERROR;
2772 DPRINTF(("MoveToTrashProcess: sending VERIFY_FILE\n"));
2773 write(pipe_fd, &pipe_msg, sizeof(short));
2774 write(pipe_fd, &rc, sizeof(int));
2779 * If this is a directory, make sure it's empty, i.e.,
2780 * contains only ".", "..", and ".!" or ".~" files.
2782 if (lstat(path, &statInfo) == 0 &&
2783 (statInfo.st_mode & S_IFMT) == S_IFDIR &&
2784 (dirp = opendir(path)) != NULL)
2786 /* read the directory */
2787 while ((entry = readdir(dirp)) != NULL)
2789 if ( !(strcmp(entry->d_name, ".") == 0 ||
2790 strcmp(entry->d_name, "..") == 0 ||
2791 strncmp(entry->d_name, ".!", 2) == 0 ||
2792 strncmp(entry->d_name, ".~", 2) == 0) )
2794 /* found a real file: directory not empty */
2803 /* Directory is not empty */
2805 pipe_msg = PIPEMSG_OTHER_ERROR;
2807 DPRINTF(("MoveToTrashProcess: sending VERIFY_DIR\n"));
2808 write(pipe_fd, &pipe_msg, sizeof(short));
2809 write(pipe_fd, &rc, sizeof(int));
2813 } /* end if do_verify_checks */
2816 to = CreateTrashFilename(baseName, TRUE);
2818 /* move file to the trash directory */
2819 success = FileManip((Widget)pipe_fd, MOVE_FILE, path, to, TRUE,
2820 FileOpError, True, TRASH_DIRECTORY);
2823 pipe_msg = PIPEMSG_DONE;
2824 DPRINTF(("MoveToTrashProcess: sending DONE\n"));
2825 write(pipe_fd, &pipe_msg, sizeof(short));
2826 PipeWriteString(pipe_fd, path);
2827 PipeWriteString(pipe_fd, to);
2836 /*--------------------------------------------------------------------
2837 * MoveToTrashPipeCB:
2838 * Read and process data sent through the pipe.
2839 *------------------------------------------------------------------*/
2843 XtPointer client_data,
2847 MoveToTrashCBData *cb_data = (MoveToTrashCBData *)client_data;
2850 char *title, *err_msg, *err_arg;
2863 DesktopRec *desktopWin;
2864 char *dir_error=NULL,*file_error=NULL,*no_file_error=NULL,*sacred_error=NULL;
2866 /* read the next msg from the pipe */
2868 n = PipeRead(*fd, &pipe_msg, sizeof(short));
2869 DPRINTF(("MoveToTrashPipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
2873 case PIPEMSG_FILEOP_ERROR:
2874 PipeRead(*fd, &rc, sizeof(int));
2875 err_msg = PipeReadString(*fd);
2876 err_arg = PipeReadString(*fd);
2878 FileOperationError(toplevel, err_msg, err_arg);
2883 cb_data->rc[cb_data->done_count++] = BAD_FILE_ERROR;
2886 case PIPEMSG_OTHER_ERROR:
2887 PipeRead(*fd, &rc, sizeof(int));
2888 cb_data->rc[cb_data->done_count++] = rc;
2891 case PIPEMSG_TARGET_TIME:
2894 case PIPEMSG_FILE_MODIFIED:
2898 i = cb_data->done_count++;
2899 cb_data->path[i] = PipeReadString(*fd);
2900 cb_data->to[i] = PipeReadString(*fd);
2906 "Internal error in MoveToTrashPipeCB: bad pipe_msg %d\n",
2908 /* Don't know how it can get in here, but if it does, we'll try to
2909 * simulate an error condition. Without it, the SGI hung up
2911 while (cb_data->done_count < cb_data->file_count)
2912 cb_data->rc[cb_data->done_count++] = BAD_FILE_ERROR;
2917 /* if still more files to be processed, return now to wait for the rest */
2918 if (cb_data->done_count < cb_data->file_count)
2922 /* close the pipe and cancel the callback */
2923 DPRINTF(("MoveToTrashPipeCB: done\n"));
2927 /* process the results */
2939 for (i = 0; i < cb_data->file_count; i++)
2941 if (cb_data->rc[i]==BAD_TRASH_DIRECTORY || cb_data->rc[i]==BAD_TRASH_FILE)
2946 Boolean errflg = False;
2947 if(cb_data->rc[i] == BAD_TRASH_DIRECTORY)
2949 tmpmsg = GETMESSAGE(27, 113,
2950 "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");
2953 dir_error = (char *) XtRealloc(dir_error,strlen(dir_error)+
2954 strlen(cb_data->file_list[i])+5);
2955 strcat(dir_error,cb_data->file_list[i]);
2956 strcat(dir_error,"\n");
2960 dir_error = XtMalloc(strlen(tmpmsg)+
2961 strlen(cb_data->file_list[i])+5);
2962 sprintf(dir_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
2967 tmpmsg = GETMESSAGE(27, 114,
2968 "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");
2971 file_error = (char *) XtRealloc(file_error,strlen(file_error)
2972 + strlen(cb_data->file_list[i])+5);
2973 strcat(file_error,cb_data->file_list[i]);
2974 strcat(file_error,"\n");
2978 file_error = XtMalloc(strlen(tmpmsg)+
2979 strlen(cb_data->file_list[i])+5);
2980 sprintf(file_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
2983 XtFree(cb_data->file_list[i]);
2984 cb_data->file_list[i] = NULL;
2988 else if (cb_data->rc[i] == BAD_FILE_SACRED)
2992 char *tmpmsg = GETMESSAGE(27, 115,
2993 "The following object(s) cannot be deleted.\nThe desktop cannot function properly without these object(s).\n\n");
2996 sacred_error = (char *) XtRealloc(sacred_error,strlen(sacred_error)+
2997 strlen(cb_data->file_list[i])+5);
2998 strcat(sacred_error,cb_data->file_list[i]);
2999 strcat(sacred_error,"\n");
3003 sacred_error=XtMalloc(strlen(tmpmsg)+strlen(cb_data->file_list[i])+5);
3004 sprintf(sacred_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
3006 XtFree(cb_data->file_list[i]);
3007 cb_data->file_list[i] = NULL;
3011 else if (cb_data->rc[i] == BAD_FILE_ERROR)
3013 XtFree(cb_data->file_list[i]);
3014 cb_data->file_list[i] = NULL;
3016 else if (cb_data->rc[i] == NO_TRASH_FILE)
3019 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");
3025 no_file_error = (char *)XtRealloc(no_file_error,strlen(no_file_error)+
3026 strlen(cb_data->file_list[i])+5);
3027 strcat(no_file_error,cb_data->file_list[i]);
3028 strcat(no_file_error,"\n");
3032 no_file_error = XtMalloc(strlen(tmpmsg)+
3033 strlen(cb_data->file_list[i])+5);
3034 sprintf(no_file_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
3039 else if (cb_data->rc[i] == VERIFY_FILE ||
3040 cb_data->rc[i] == VERIFY_DIR)
3042 if (verifyCount < 6)
3043 AddString(&verifybuf, &verifybufsize, cb_data->file_list[i], NULL);
3047 * Keep track of whether we have directories only, files only,
3048 * or a combination, so that we can display the appropriate
3049 * label in the dialog.
3051 if (cb_data->rc[i] == VERIFY_FILE)
3057 * Add to array which is to be attached to the dialog;
3058 * the array will ultimately be NULL terminated.
3060 verifylist = (char **)XtRealloc((char *)verifylist,
3061 sizeof(char *) * (verifyCount + 1));
3062 verifylist[verifyCount - 1] = cb_data->file_list[i];
3066 /* Add file to trash list */
3067 if (numTrashItems >= trashListSize)
3069 trashListSize += 10;
3070 trashCan = (TrashEntry *)
3071 XtRealloc((char *)trashCan, sizeof(TrashEntry) * trashListSize);
3074 trashCan[numTrashItems].problem = False;
3075 trashCan[numTrashItems].intNew = cb_data->to[i];
3076 trashCan[numTrashItems].intOrig = cb_data->path[i];
3077 trashCan[numTrashItems].external = cb_data->file_list[i];
3079 /* extract base file name */
3080 baseName = strrchr(cb_data->to[i], '/');
3082 if (*baseName == '\0')
3084 trashCan[numTrashItems].filename = XtNewString(baseName);
3089 /* arrange for the source directory to be updated */
3090 UpdateDirectoryOf(cb_data->path[i]);
3093 * If the source file was referenced by a desktop object,
3094 * we need to remove the destkop object
3096 for (j = 0; j < desktop_data->numIconsUsed; j++)
3098 Tt_status tt_status;
3100 desktopWin = desktop_data->desktopWindows[j];
3101 fileName = ResolveLocalPathName( desktopWin->host,
3102 desktopWin->dir_linked_to,
3103 desktopWin->file_name,
3104 home_host_name, &tt_status);
3106 if( TT_OK == tt_status )
3108 if (strcmp(fileName, cb_data->path[i]) == 0)
3110 RemoveDT(desktopWin->shell, (XtPointer)desktopWin, NULL);
3112 RemoveMovedObjectFromDT(desktopWin->shell, (XtPointer)desktopWin,
3113 cb_data->file_count, cb_data->file_list);
3114 #endif /* SUN_PERF */
3123 buf = XtMalloc(strlen(dir_error)+3);
3124 sprintf(buf,"%s\n",dir_error);
3131 buf = XtMalloc(strlen(file_error)+3);
3132 sprintf(buf,"%s\n",file_error);
3136 buf = XtRealloc(buf,strlen(buf)+strlen(file_error)+3);
3137 sprintf(buf,"%s%s\n",buf,file_error);
3145 buf = XtMalloc(strlen(no_file_error)+3);
3146 sprintf(buf,"%s\n",no_file_error);
3150 buf = XtRealloc(buf,strlen(buf)+strlen(no_file_error)+3);
3151 sprintf(buf,"%s%s\n",buf,no_file_error);
3153 XtFree(no_file_error);
3159 buf = XtMalloc(strlen(sacred_error)+3);
3160 sprintf(buf,"%s\n",sacred_error);
3164 buf = XtRealloc(buf,strlen(buf)+strlen(sacred_error)+3);
3165 sprintf(buf,"%s%s\n",buf,sacred_error);
3167 XtFree(sacred_error);
3170 bufsize = strlen(buf);
3172 /* Update the trash information file, and the trash window */
3175 FILE * trashInfoFileId = fopen(TrashInfoFileName, "a+");
3176 if( trashInfoFileId != NULL )
3178 for (i = fileCount; i > 0; i--)
3180 if( WriteEntry(trashInfoFileId,
3181 trashCan[numTrashItems - i].external,
3182 trashCan[numTrashItems - i].filename) < 0 )
3185 fflush(trashInfoFileId);
3186 fclose(trashInfoFileId);
3187 if( trashFileMgrData )
3188 UpdateDirectory(NULL, trashFileMgrData->host,
3189 trashFileMgrData->current_directory);
3193 /* Check for any bad files; post an error dialog */
3196 /* send a reply to the message that triggered this operation, if any */
3198 if (cb_data->msg != 0) {
3200 Until Action is fixed.
3201 tttk_message_fail( cb_data->msg, TT_DESKTOP_EACCES, 0, 1 );
3203 tt_message_reply( cb_data->msg );
3204 tttk_message_destroy( cb_data->msg );
3206 if (cb_data->msg_cnt > 0) {
3207 for (i = 0 ; i < cb_data->msg_cnt ; i++) {
3208 tt_message_reply( cb_data->msg_list[i] ) ;
3209 tttk_message_destroy( cb_data->msg_list[i] ) ;
3211 XtFree((char *)cb_data->msg_list) ;
3212 cb_data->msg_cnt = 0 ;
3213 #endif /* SUN_PERF */
3216 /* If more items than can be displayed, let user know */
3219 char extraFiles[256];
3221 (void) sprintf(extraFiles, AdditionalHeader, badCount - 8);
3225 GETMESSAGE(27,97, "The following objects could not be placed in the trash can: \n"));
3228 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3229 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3231 XtFree ((char *) buf);
3233 /* send a reply to the message that triggered this operation, if any */
3235 if (cb_data->msg != 0) {
3236 tt_message_reply( cb_data->msg );
3237 tttk_message_destroy( cb_data->msg );
3239 if (cb_data->msg_cnt > 0) {
3240 for (i = 0 ; i < cb_data->msg_cnt ; i++) {
3241 tt_message_reply( cb_data->msg_list[i] ) ;
3242 tttk_message_destroy( cb_data->msg_list[i] ) ;
3244 XtFree((char *)cb_data->msg_list) ;
3245 cb_data->msg_cnt = 0 ;
3246 #endif /* SUN_PERF */
3251 /* Check for any files requiring user verification; post a prompt dialog */
3252 /* XXX Really should fail incoming ToolTalk request if user cancels any */
3260 /* If more items than can be displayed, let user know */
3261 if (verifyCount > 6)
3263 char extraFiles[256];
3265 (void) sprintf(extraFiles, AdditionalHeader, verifyCount - 6);
3266 AddString(&verifybuf, &verifybufsize, extraFiles, NULL);
3270 * Depending upon what type of files are to be displayed in the
3271 * dialog, choose the appropriate dialog text.
3273 if (vfiles && vdirs)
3274 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");
3276 header = GETMESSAGE(27,104, "You do not have write permission for the following file(s):\nDo you want to proceed?\n");
3278 header = GETMESSAGE(27,100, "The following folder(s) are not empty.\nDo you want to proceed?\n");
3280 tmpbuf = XtMalloc(strlen(header) + strlen(verifybuf) + 1);
3281 sprintf(tmpbuf, "%s%s", header, verifybuf);
3282 title = XtNewString(GETMESSAGE(27, 4, "Trash Can Warning"));
3283 dlog = (Widget)_DtMessageDialog(toplevel, title, tmpbuf, NULL, True,
3284 VerifyCancel, VerifyOk, NULL, HelpRequestCB,
3285 False, WARNING_DIALOG);
3287 XtFree ((char *) verifybuf);
3291 * Add array as userdata on the dialog.
3292 * NULL terminate the array.
3294 verifylist[verifyCount] = NULL;
3295 XtSetArg(args[0], XmNuserData, verifylist);
3296 XtSetValues(dlog, args, 1);
3299 /* free the callback data */
3300 XtFree((char *)cb_data->file_list);
3301 XtFree((char *)cb_data->path);
3302 XtFree((char *)cb_data->to);
3303 XtFree((char *)cb_data->rc);
3304 XtFree((char *)cb_data);
3310 /*--------------------------------------------------------------------
3312 * Start the background process and set up callback for the pipe.
3313 *------------------------------------------------------------------*/
3319 Boolean do_verify_checks,
3322 static char *pname = "MoveToTrash";
3323 MoveToTrashCBData *cb_data;
3327 if( !TrashInitialized )
3330 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
3335 /* set up callback data */
3336 cb_data = XtNew(MoveToTrashCBData);
3337 cb_data->file_list = file_list;
3338 cb_data->file_count = file_count;
3339 cb_data->done_count = 0;
3340 cb_data->path = (char **)XtCalloc(file_count, sizeof(char *));
3341 cb_data->to = (char **)XtCalloc(file_count, sizeof(char *));
3342 cb_data->rc = (int *)XtCalloc(file_count, sizeof(int));
3345 cb_data->msg_cnt = 0 ;
3346 if (global_msg_cnt > 0) {
3347 cb_data->msg_cnt = global_msg_cnt ;
3348 cb_data->msg_list = (Tt_message *)XtMalloc(sizeof(Tt_message) * cb_data->msg_cnt);
3349 memcpy((char *)cb_data->msg_list,(char *)global_msg_list, sizeof(Tt_message) *
3352 #endif /* SUN_PERF */
3357 /* fork the process that does the actual work */
3362 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
3363 pname, getppid(), getpid(), errno, strerror(errno));
3370 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
3372 close(pipe_fd[0]); /* child won't read from the pipe */
3374 MoveToTrashProcess(pipe_fd[1], file_list, file_count, do_verify_checks);
3377 DBGFORK(("%s: child exiting\n", pname));
3382 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
3384 /* parent: set up callback to get the pipe data */
3385 close(pipe_fd[1]); /* parent won't write the pipe */
3387 cb_data->child = pid;
3389 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
3390 pipe_fd[0], (XtPointer)XtInputReadMask,
3391 MoveToTrashPipeCB, (XtPointer)cb_data);
3395 /*====================================================================
3399 * Run a background process to restore files from the trash can.
3401 * These routines are used both for "normal restores", i.e.,
3402 * restores initiated from the Trash menu or through an ICCCM
3403 * message, as well as "drag&drop restores", i.e., restores done
3404 * by dragging files from the trash can to some other directory.
3405 * For normal resores, file_list contains only simple file names
3406 * (no paths) and target_dir is NULL. For drag&drop restores
3407 * file_list contains complete path names and target_dir contains
3408 * the name of the directoy to which the files should be moved.
3410 *==================================================================*/
3412 /*--------------------------------------------------------------------
3414 * Main routine of background process for RestoreFromTrash
3415 *------------------------------------------------------------------*/
3425 Boolean CheckedAlready)
3434 char **RestoreList= NULL;
3436 /* get full path name of target directory */
3439 Tt_status tt_status;
3440 full_dirname = ResolveLocalPathName(target_host, target_dir,
3441 NULL, home_host_name, &tt_status);
3442 if( TT_OK != tt_status )
3444 /* send return codes back trough the pipe */
3445 pipe_msg = PIPEMSG_DONE;
3447 DPRINTF(("RestoreProcess: Unable to Resolve local path name\n"));
3448 write(pipe_fd, &pipe_msg, sizeof(short));
3449 write(pipe_fd, rc, sizeof(int));
3454 full_dirname = NULL;
3456 /* restore the files */
3457 for (i = 0; i < file_count; i++)
3459 /* Locate file in trash list */
3460 for (j = 0; j < numTrashItems; j++)
3462 /* file_list[i] may be a complete path or just a file name */
3463 if (strcmp(file_list[i], trashCan[j].filename) == 0 ||
3464 strcmp(file_list[i], trashCan[j].intNew) == 0)
3470 /* determine source and target for the move */
3471 if (target_dir == NULL)
3473 /* this is a normal restore */
3474 if (j < numTrashItems)
3476 from = trashCan[j].intNew;
3477 to = trashCan[j].intOrig;
3481 /* can't do a restore if the file wasn't found in the trash list */
3487 /* this is a drag&drop from the trash can to target_dir */
3488 from = file_list[i];
3489 if (j < numTrashItems)
3490 ptr = strrchr(trashCan[j].intOrig, '/');
3492 ptr = strrchr(file_list[i], '/');
3493 strcpy(buf, full_dirname);
3501 status = RestoreObject((Widget)pipe_fd, MOVE_FILE, from,to,
3502 TRUE, FileOpError, False, NOT_DESKTOP,CheckedAlready);
3503 /* restore was successful */
3504 if(status == (int) True)
3506 else if(status == (int) False)
3512 /* restore failed */
3516 /* send return codes back trough the pipe */
3517 pipe_msg = PIPEMSG_DONE;
3518 DPRINTF(("RestoreProcess: sending DONE\n"));
3519 write(pipe_fd, &pipe_msg, sizeof(short));
3520 write(pipe_fd, rc, file_count * sizeof(int));
3522 XtFree(full_dirname);
3526 /*--------------------------------------------------------------------
3528 * Read and process data sent through the pipe.
3529 *------------------------------------------------------------------*/
3533 XtPointer client_data,
3537 RestoreFromTrashCBData *cb_data = (RestoreFromTrashCBData *)client_data;
3540 char *title, *err_msg, *err_arg;
3544 char **ToRestoreList=NULL;
3545 char **FromRestoreList=NULL;
3546 char *target_host,*target_dir;
3549 /* read the next msg from the pipe */
3551 n = PipeRead(*fd, &pipe_msg, sizeof(short));
3552 DPRINTF(("RestorePipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
3556 case PIPEMSG_FILEOP_ERROR:
3557 PipeRead(*fd, &rc, sizeof(int));
3558 err_msg = PipeReadString(*fd);
3559 err_arg = PipeReadString(*fd);
3560 FileOperationError(toplevel, err_msg, err_arg);
3561 /* This call will popup an error dialog.
3563 FileOperationError(toplevel, err_msg, err_arg);
3565 It's not appropriate at all to popup an error dialog here.
3566 i.e. if there're 1000 files, and the file system is full,
3567 and we're unable to move, would we want to popup 1000
3574 PipeRead(*fd, cb_data->rc, cb_data->file_count * sizeof(int));
3579 "Internal error in RestorePipeCB: bad pipe_msg %d\n",
3583 /* close the pipe and cancel the callback */
3584 DPRINTF(("RestorePipeCB: done\n"));
3591 for (i = 0; i < cb_data->file_count; i++)
3593 /* Locate file in trash list */
3594 for (j = 0; j < numTrashItems; j++)
3596 /* file_list[i] may be a complete path or just a file name */
3597 if (strcmp(cb_data->file_list[i], trashCan[j].filename) == 0 ||
3598 strcmp(cb_data->file_list[i], trashCan[j].intNew) == 0)
3604 if (cb_data->rc[i] == SKIP_FILE)
3606 ToRestoreList = (char **) XtRealloc((char *)ToRestoreList,sizeof(char *) *
3608 ToRestoreList[RestoreIndex-1] = XtNewString(trashCan[j].intOrig);
3609 FromRestoreList = (char **) XtRealloc((char *)FromRestoreList,sizeof(char *) *
3611 FromRestoreList[RestoreIndex-1] = XtNewString( trashCan[j].intNew );
3614 /* Check the return code from the restore */
3615 else if (cb_data->rc[i] == 0)
3617 /* restore was successful: remove the file from the trash list */
3618 if (j < numTrashItems)
3620 /* arrange for the source directory to be updated */
3621 UpdateDirectoryOf(trashCan[j].intOrig);
3623 /* Remove this entry from the trash list */
3624 XtFree ((char *) trashCan[j].intNew);
3625 XtFree ((char *) trashCan[j].intOrig);
3626 XtFree ((char *) trashCan[j].external);
3627 XtFree ((char *) trashCan[j].filename);
3628 for (k = j; k < (numTrashItems - 1); k++)
3629 trashCan[k] = trashCan[k + 1];
3636 char *restore_header,*tmpStr = GETMESSAGE(27,101,
3637 "The following object(s) could not be put back:\n");
3639 restore_header = XtNewString(tmpStr);
3640 if (j < numTrashItems && cb_data->target_dir == NULL)
3641 AddString(&buf, &bufsize, trashCan[j].external, restore_header);
3643 AddString(&buf, &bufsize, cb_data->file_list[i], restore_header);
3644 XtFree(restore_header);
3648 /* Update the trash information file */
3649 if( ! WriteTrashEntries() )
3651 char * tmpStr, * title;
3653 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3654 tmpStr = XtNewString(GETMESSAGE(27, 88, "Cannot write to a temporary file.\nPerhaps your file system is full.\n"));
3655 _DtMessage(toplevel, title, tmpStr, NULL, HelpRequestCB);
3660 /* send a reply to the message that triggered this operation, if any */
3661 if (cb_data->msg != 0) {
3662 tt_message_reply( cb_data->msg );
3663 tttk_message_destroy( cb_data->msg );
3667 /* Report any errors */
3670 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3671 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3673 XtFree ((char *) buf);
3676 UpdateDirectory(NULL, trashFileMgrData->host,
3677 trashFileMgrData->current_directory);
3678 if (cb_data->target_dir)
3679 UpdateDirectory(NULL, cb_data->target_host, cb_data->target_dir);
3681 target_host = XtNewString(cb_data->target_host);
3682 target_dir = XtNewString(cb_data->target_dir);
3684 /* free the callback data */
3685 for (i = 0; i < cb_data->file_count; i++)
3686 XtFree(cb_data->file_list[i]);
3687 XtFree((char *)cb_data->file_list);
3688 XtFree((char *)cb_data->target_host);
3689 XtFree((char *)cb_data->target_dir);
3690 XtFree((char *)cb_data->rc);
3691 XtFree((char *)cb_data);
3694 for(i=0;i<RestoreIndex;i++)
3696 CreateRestoreDialog(FromRestoreList[i],ToRestoreList[i]);
3697 XtFree(FromRestoreList[i]);
3698 XtFree(ToRestoreList[i]);
3700 XtFree((char *) FromRestoreList);
3701 XtFree((char *) ToRestoreList);
3703 XtFree(target_host);
3708 /*--------------------------------------------------------------------
3710 * Start the background process and set up callback for the pipe.
3711 *------------------------------------------------------------------*/
3720 Boolean CheckedAlready)
3722 static char *pname = "RestoreFromTrash";
3723 RestoreFromTrashCBData *cb_data;
3727 if( !TrashInitialized )
3730 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
3735 /* set up callback data */
3736 cb_data = XtNew(RestoreFromTrashCBData);
3737 cb_data->file_list = file_list;
3738 cb_data->file_count = file_count;
3739 cb_data->target_host = target_host;
3740 cb_data->target_dir = target_dir;
3741 cb_data->rc = (int *)XtCalloc(file_count, sizeof(int));
3747 /* fork the process that does the actual work */
3752 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
3753 pname, getppid(), getpid(), errno, strerror(errno));
3760 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
3762 close(pipe_fd[0]); /* child won't read from the pipe */
3764 RestoreProcess(pipe_fd[1], file_list, file_count,
3765 target_host, target_dir, cb_data->rc,CheckedAlready);
3768 DBGFORK(("%s: child exiting\n", pname));
3773 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
3775 /* parent: set up callback to get the pipe data */
3776 close(pipe_fd[1]); /* parent won't write the pipe */
3778 cb_data->child = pid;
3780 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
3781 pipe_fd[0], (XtPointer)XtInputReadMask,
3782 RestorePipeCB, (XtPointer)cb_data);
3787 /*====================================================================
3790 * Run a background process to remove files from the trash can.
3792 *==================================================================*/
3794 /*--------------------------------------------------------------------
3796 * Main routine of background process for EmptyTrash
3797 *------------------------------------------------------------------*/
3802 DeleteList * del_list,
3810 * Delete all files or directories in the list,
3811 * as well as any associated annotations.
3813 for (i = 0; i < del_count; i++)
3815 /* delete the file */
3816 rc[i] = EraseObject(del_list[i].trash);
3819 /* send return codes back trough the pipe */
3820 pipe_msg = PIPEMSG_DONE;
3821 DPRINTF(("EmptyTrashProcess: sending DONE\n"));
3822 write(pipe_fd, &pipe_msg, sizeof(short));
3823 write(pipe_fd, rc, del_count * sizeof(int));
3827 /*--------------------------------------------------------------------
3829 * Read and process data sent through the pipe.
3830 *------------------------------------------------------------------*/
3834 XtPointer client_data,
3838 EmptyTrashCBData *cb_data = (EmptyTrashCBData *)client_data;
3841 char *title, *err_msg, *err_arg;
3842 int problemCount, itemCount;
3847 /* read the next msg from the pipe */
3849 n = PipeRead(*fd, &pipe_msg, sizeof(short));
3850 DPRINTF(("EmptyTrashPipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
3854 case PIPEMSG_FILEOP_ERROR:
3855 PipeRead(*fd, &rc, sizeof(int));
3856 err_msg = PipeReadString(*fd);
3857 err_arg = PipeReadString(*fd);
3858 /* This call will popup an error dialog.
3860 FileOperationError(toplevel, err_msg, err_arg);
3862 It's not appropriate at all to popup an error dialog here.
3863 i.e. if there're 1000 files, and the file system is full,
3864 and we're unable to move, would we want to popup 1000
3872 PipeRead(*fd, cb_data->rc, cb_data->del_count * sizeof(int));
3877 "Internal error in EmptyTrashPipeCB: bad pipe_msg %d\n",
3881 /* close the pipe and cancel the callback */
3882 DPRINTF(("EmptyTrashPipeCB: done\n"));
3891 if (cb_data->removeType == TRASH_FILE)
3893 for (i = 0; i < cb_data->del_count; i++)
3895 /* Locate file in trash list */
3896 for (j = 0; j < numTrashItems; j++)
3897 if (strcmp(cb_data->del_list[i].trash, trashCan[j].intNew) == 0)
3900 /* Check the return code from the erase */
3901 if (cb_data->rc[i] == 0)
3903 /* erase was successful: remove the file from the trash list */
3904 if (j < numTrashItems)
3906 /* Remove this entry from the trash list */
3907 XtFree ((char *) trashCan[j].intNew);
3908 XtFree ((char *) trashCan[j].intOrig);
3909 XtFree ((char *) trashCan[j].external);
3910 XtFree ((char *) trashCan[j].filename);
3911 for (k = j; k < (numTrashItems - 1); k++)
3912 trashCan[k] = trashCan[k + 1];
3923 char * msg = XtNewString(GETMESSAGE(27,96, "The following objects could not be removed from the file system: \n"));
3925 if (j < numTrashItems)
3926 file_name = trashCan[j].external;
3928 file_name = cb_data->del_list[i].trash;
3929 AddString(&buf, &bufsize, file_name, msg);
3936 /* Update the trash information file */
3937 if( ! WriteTrashEntries() )
3939 char * tmpStr, * title;
3941 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3942 tmpStr = XtNewString(GETMESSAGE(27, 88, "Cannot write to a temporary file.\nPerhaps your file system is full.\n"));
3943 _DtMessage(toplevel, title, tmpStr, NULL, HelpRequestCB);
3948 /* Report any errors */
3951 /* If more items than can be displayed, let user know */
3952 if (itemCount < problemCount)
3954 char extraFiles[256];
3955 sprintf(extraFiles, AdditionalHeader, problemCount - itemCount);
3959 GETMESSAGE(27,97, "The following objects could not be placed in the trash can: \n") );
3962 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3963 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3965 XtFree ((char *) buf);
3968 if (trashFileMgrData->selected_file_count > 0)
3970 XtSetSensitive(*removeBtn, True);
3971 XtSetSensitive(*restoreBtn, True);
3974 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
3975 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
3977 UpdateDirectory(NULL, trashFileMgrData->host,
3978 trashFileMgrData->current_directory);
3982 if (cb_data->rc[0] == 0)
3984 /* arrange for the directory containing the file to be updated */
3985 UpdateDirectoryOf(cb_data->del_list[0].trash);
3989 /* put up an error message saying the file couldn't be removed */
3993 tmpStr = GETMESSAGE(27, 81, "Shred File/Folder Error");
3994 title = XtNewString(tmpStr);
3995 tmpStr = GETMESSAGE(27, 82, " The following file could not be removed from the file system: \n");
3996 file_name = cb_data->del_list[0].trash;
3997 buffer = XtMalloc(strlen(tmpStr) + strlen(file_name) + 1);
3998 sprintf(buffer, "%s%s\n", tmpStr, file_name);
3999 _DtMessage(toplevel, title, buffer, NULL, HelpRequestCB);
4006 /* reset removingTrash flag */
4007 removingTrash = False;
4009 /* send a reply to the message that triggered this operation, if any */
4010 if (cb_data->msg != 0) {
4011 tt_message_reply( cb_data->msg );
4012 tttk_message_destroy( cb_data->msg );
4016 /* free the callback data */
4017 for (i = 0; i < cb_data->del_count; i++)
4019 XtFree(cb_data->del_list[i].trash);
4020 XtFree(cb_data->del_list[i].orig);
4022 XtFree((char *)cb_data->del_list);
4023 XtFree((char *)cb_data->rc);
4024 XtFree((char *)cb_data);
4028 /*--------------------------------------------------------------------
4030 * Start the background process and set up callback for the pipe.
4031 *------------------------------------------------------------------*/
4035 DeleteList *del_list,
4040 static char *pname = "EmptyTrash";
4041 EmptyTrashCBData *cb_data;
4045 if( !TrashInitialized )
4048 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
4053 /* set removingTrash flag */
4054 removingTrash = True;
4056 /* set up callback data */
4057 cb_data = XtNew(EmptyTrashCBData);
4058 cb_data->del_list = del_list;
4059 cb_data->del_count = del_count;
4060 cb_data->removeType = removeType;
4061 cb_data->rc = (int *)XtCalloc(del_count, sizeof(int));
4067 /* fork the process that does the actual work */
4072 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
4073 pname, getppid(), getpid(), errno, strerror(errno));
4080 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
4082 close(pipe_fd[0]); /* child won't read from the pipe */
4084 EmptyTrashProcess(pipe_fd[1], del_list, del_count, cb_data->rc);
4087 DBGFORK(("%s: child exiting\n", pname));
4092 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
4094 /* parent: set up callback to get the pipe data */
4095 close(pipe_fd[1]); /* parent won't write the pipe */
4097 cb_data->child = pid;
4099 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
4100 pipe_fd[0], (XtPointer)XtInputReadMask,
4101 EmptyTrashPipeCB, (XtPointer)cb_data);
4105 CheckDeletePermission(
4107 char *destinationPath)
4109 struct stat statbuf;
4112 if (lstat(parentdir,&statbuf) < 0) /* does not exist */
4115 /* check if we are root */
4118 /* if NFS, need to check if server trusts root */
4119 if (FileSysType(statbuf.st_dev) < 0) /* Root user and nfs */
4122 tmpfile = tempnam(parentdir,"quang");
4123 if (creat(tmpfile,O_RDONLY)< 0) /* Create a temporary file */
4125 if (remove(tmpfile) < 0) /* Delete the created file */
4129 /* root user can delete anything */
4133 /* check for write and execute permisssion on parent dir */
4134 if (CheckAccess(parentdir, W_OK | X_OK) < 0)
4137 /* copy destinationPath to tmp buffer */
4138 strcpy(fname, destinationPath);
4140 return CheckDeletePermissionRecur(fname);
4145 CheckDeletePermissionRecur(
4146 char *destinationPath)
4148 struct stat statbuf;
4154 DPRINTF(("CheckDeletePermissionRecur(\"%s\")\n", destinationPath));
4156 if (lstat(destinationPath, &statbuf) < 0)
4157 return -1; /* probably does not exist */
4159 if (! S_ISDIR(statbuf.st_mode))
4160 return 0; /* no need to check anything more */
4162 dirp = opendir (destinationPath);
4164 return -1; /* could not read directory */
4169 while (dp = readdir (dirp))
4171 if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
4175 /* check for write permission in this directory */
4176 if (CheckAccess(destinationPath, W_OK|X_OK) < 0)
4179 /* append a '/' to the end of directory name */
4180 fnamep = destinationPath + strlen(destinationPath);
4186 /* append file name to end of directory name */
4187 strcpy(fnamep, dp->d_name);
4189 /* recursively check permission on this file */
4190 if (CheckDeletePermissionRecur(destinationPath))
4203 if(ustat(dev,&u1) < 0)
4212 register char *source,
4213 register char *target,
4214 Boolean isContainer,
4215 void (*errorHandler)(),
4216 Boolean checkForBusyDir,
4218 Boolean CheckedAlready)
4220 struct stat statsrc,stattar;
4222 char *localdir,*chrptr;
4226 if(stat(source,&statsrc) < 0)
4227 if(lstat(source,&statsrc) < 0)
4230 localdir = strdup(target);
4232 chrptr = strrchr(localdir,'/');
4235 if(chrptr == localdir) /* Must be root folder */
4240 if(stat(target,&stattar) >= 0) /* Target exists */
4242 if(CheckDeletePermission(localdir,target))
4243 return ((int)False);
4248 return ((int )FileManip((Widget)w, MOVE_FILE, source, target, TRUE,
4249 FileOpError, False, NOT_DESKTOP));
4252 CreateRestoreDialog(
4256 char *tmpbuf,*title;
4260 char **dirs = (char **) malloc(sizeof(char *) * 2);
4261 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.");
4263 dirs[0] = strdup(source);
4264 dirs[1] = strdup(target);
4265 tmpbuf = XtMalloc(strlen(header) + strlen(target) + 1);
4266 sprintf(tmpbuf, header, target);
4267 title = XtNewString(GETMESSAGE(27, 109, "Put Back Warning"));
4268 dw = (Widget)_DtMessageDialog(toplevel, title,tmpbuf,NULL,True,RestoreVerifyCancel,
4269 RestoreVerifyOk, NULL, HelpRequestCB, False, WARNING_DIALOG);
4271 XtSetArg(args[n], XmNuserData,dirs); n++;
4272 XtSetValues(dw,args,n);
4280 XtPointer client_data,
4281 XtPointer call_data )
4286 Widget mbox = (Widget)client_data;
4288 XtSetArg(args[n],XmNuserData,&dirs); n++;
4289 XtGetValues(mbox,args,n);
4291 if(DirectoryBusy(dirs[1]))
4294 char * tmpStr,*title;
4296 title = XtNewString(GETMESSAGE(27,111,"Put Back Error"));
4297 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.");
4298 msgbuf = XtMalloc(strlen(tmpStr) + strlen(dirs[1])+1);
4299 sprintf (msgbuf, tmpStr, dirs[1]);
4300 _DtMessage(toplevel,title,msgbuf,NULL,HelpRequestCB);
4308 char *realTarget,*tptr;
4312 if(lstat(dirs[1],&s1) < 0)
4314 fsErase(dirs[1],&status);
4315 FileList = (char **) XtMalloc(sizeof(char *));
4316 FileList[0] = XtNewString(dirs[0]);
4317 RestoreFromTrash(FileList, (int) 1, NULL, NULL, NULL,True);
4323 XtDestroyWidget(mbox);
4327 RestoreVerifyCancel(
4329 XtPointer client_data,
4330 XtPointer call_data )
4336 Widget mbox = (Widget)client_data;
4338 XtSetArg(args[n],XmNuserData,&dirs); n++;
4339 XtGetValues(mbox,args,n);
4343 XtDestroyWidget(mbox);