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>
104 #if defined(CSRG_BASED)
105 #include <sys/param.h>
106 #include <sys/mount.h>
111 #include <Xm/RowColumn.h>
112 #include <Xm/CascadeB.h>
114 #include <Xm/Frame.h>
115 #include <Xm/MainW.h>
116 #include <Xm/PushBG.h>
117 #include <Xm/SeparatoG.h>
118 #include <Xm/MessageB.h>
119 #include <Xm/MwmUtil.h>
121 #include <X11/Shell.h>
122 #include <X11/Xatom.h>
123 #include <Xm/Protocols.h>
125 #include <Dt/Action.h>
127 #include <Dt/FileM.h>
128 #include <Dt/DtNlUtils.h>
129 #include <Dt/Connect.h>
130 #include <Dt/Indicator.h>
131 #include <Dt/EnvControlP.h>
134 #include <Dt/SharedProcs.h>
135 #include "DtSvcInternal.h" /* _DtGetMask */
140 #include "SharedProcs.h"
141 #include "MultiView.h"
146 #include "SharedMsgs.h"
147 #include "dtcopy/fsrtns.h"
149 #define AdditionalHeader (GETMESSAGE(27,98, "(Plus %d additional object(s))"))
151 /* Trash file errors */
152 #define NO_FILE_ERROR 0
153 #define BAD_FILE_ERROR -1
154 #define VERIFY_DIR -2
155 #define VERIFY_FILE -3
156 #define BAD_TRASH_DIRECTORY -4
157 #define BAD_TRASH_FILE -5
158 #define NO_TRASH_FILE -6
161 #define BAD_FILE_SACRED -9
163 /* types of messages sent through the pipe */
164 #define PIPEMSG_FILEOP_ERROR 1
165 #define PIPEMSG_OTHER_ERROR 3
166 #define PIPEMSG_DONE 7
167 #define PIPEMSG_TARGET_TIME 5
168 #define PIPEMSG_FILE_MODIFIED 6
171 * Structure describing each file in the trash can.
172 * Includes the external host:/name format, the original internal
173 * /nfs/host/name format, and the internal /nfs/host/new_name describing
174 * the temporary location of the file. The `problem' flag is used to
175 * indicate if a problem occurred when the file was physically being
187 /* callback data MoveToTrash */
198 Tt_message *msg_list ;
200 #endif /* SUN_PERF */
207 Tt_message *msg_list ;
210 #endif /* SUN_PERF */
212 /* callback data RestoreFromTrash */
222 } RestoreFromTrashCBData;
224 /* callback data EmptyTrash */
233 DeleteList *del_list;
242 DialogData *trashDialogData; /* fm data associated with trash dialog */
243 FileMgrData *trashFileMgrData = NULL;
245 Boolean removingTrash = False;
247 DialogData * primaryTrashHelpDialog = NULL;
248 DialogData ** secondaryTrashHelpDialogList = NULL;
249 int secondaryTrashHelpDialogCount = 0;
251 /* Forward prototype */
252 int EraseDir(char *dir_name);
253 /* From dtcopy/fsrtns.c */
254 extern int EmptyDir(char *sourceP, int rm, int force);
256 /* 'defines' for trash files */
257 static char * TRASH_DIR = ".dt/Trash";
258 static char * TRASH_INFO_FILE = ".dt/Trash/.trashinfo";
259 static char * NEW_TRASH_INFO_FILE = ".dt/.tmptrashinfo";
261 static char * RM = "/bin/rm";
262 static char * RM_ARGS = "-rf";
264 static Widget * selectAllBtn = NULL;
265 static Widget * restoreBtn = NULL;
266 static Widget * removeBtn = NULL;
268 static int trashMenuItemCount = 22; /* trash menu items */
269 static MenuDesc * trashMenu = NULL;
271 static TrashEntry * trashCan = NULL;
272 static int trashListSize = 0;
273 static int numTrashItems = 0;
275 static Boolean trashDialogPosted;
277 static char * TrashInfoFileName = NULL;
278 static char * NewTrashInfoFileName = NULL;
280 static char ** sacred_dir_list = NULL; /* list of directories in trash path */
281 static int sacred_dir_list_size = 0;
282 static int sacred_dir_count = 0;
284 static Boolean verifyPromptsEnabled; /* do we prompt the user? */
286 static Tt_message global;
288 static Tt_message *global_msg_list ;
289 static int global_msg_cnt = 0 ;
290 #endif /* SUN_PERF */
291 static Boolean TrashInitialized = False;
293 /******** Static Function Declarations ********/
295 static String CreateTrashFilename(
298 static void MessageToFileList(
303 static void MessagesToFileList(
304 Tt_message *msg_list,
308 #endif /* SUN_PERF */
309 static int WriteEntry(
313 static Boolean MatchesSacredDirectory(
315 static void VerifyCleanup(
317 Boolean completeDelete) ;
318 static void VerifyCancel(
320 XtPointer client_data,
321 XtPointer call_data) ;
322 static void VerifyOk(
324 XtPointer client_data,
325 XtPointer call_data) ;
326 static Boolean WriteTrashEntries( void ) ;
327 static void Select_All(
329 XtPointer client_data,
330 XtPointer call_data) ;
331 static void Unselect_All(
333 XtPointer client_data,
334 XtPointer call_data) ;
337 XtPointer client_data,
338 XtPointer call_data) ;
339 static String GetBasePath(
341 static Boolean ReadTrashList( void ) ;
342 static void RemoveOkCB(
344 XtPointer client_data,
345 XtPointer call_data ) ;
346 static void RemoveCancelCB(
348 XtPointer client_data,
349 XtPointer call_data ) ;
350 static void ConfirmOk(
352 XtPointer client_data,
353 XtPointer call_data ) ;
354 static void ConfirmCancel(
356 XtPointer client_data,
357 XtPointer call_data ) ;
360 XtPointer clientData,
361 XtPointer callData) ;
362 static void AddToDeleteList(
363 DeleteList *deleteList,
366 static void MoveToTrash(
369 Boolean do_verify_checks,
371 static void RestoreFromTrash(
377 Boolean CheckedAlready) ;
378 static void EmptyTrash(
379 DeleteList *del_list,
383 static int CheckDeletePermissionRecur(
385 static int FileSysType(int dev);
386 static void RestoreVerifyOk(
388 XtPointer client_data,
389 XtPointer call_data ) ;
390 static void RestoreVerifyCancel(
392 XtPointer client_data,
393 XtPointer call_data ) ;
394 static int RestoreObject(
397 register char *source,
398 register char *target,
400 void (*errorHandler)(),
401 Boolean checkForBusyDir,
403 Boolean CheckedAlready);
404 static void CreateRestoreDialog(
408 /******** End Static Function Declarations ********/
411 TrashIsInitialized( void )
413 return( TrashInitialized );
417 InitializeTrash( Boolean enableVerifyPrompt )
420 struct stat statInfo;
422 /* Build the 'TRASH' directory */
423 trash_dir = XtMalloc(strlen(users_home_dir) +
426 sprintf(trash_dir, "%s%s", users_home_dir, TRASH_DIR);
427 if (stat(trash_dir, &statInfo) < 0)
428 mkdir(trash_dir, S_IRUSR | S_IWUSR | S_IXUSR |
429 S_IRGRP | S_IWGRP | S_IXGRP |
430 S_IROTH | S_IWOTH | S_IXOTH);
432 /* build path to .trashinfo file */
433 TrashInfoFileName = XtMalloc(strlen(users_home_dir) + strlen(TRASH_INFO_FILE) + 1);
434 sprintf(TrashInfoFileName, "%s%s", users_home_dir, TRASH_INFO_FILE);
437 NewTrashInfoFileName = XtMalloc(strlen(users_home_dir) +
438 strlen(NEW_TRASH_INFO_FILE)
440 sprintf(NewTrashInfoFileName, "%s%s", users_home_dir, NEW_TRASH_INFO_FILE);
442 /* Keep track of whether to prompt for user verification */
443 verifyPromptsEnabled = enableVerifyPrompt;
446 * Create an array of paths and filenames which we will not allow the
447 * user to delete, because deleting any of them will cause trash to
450 sacred_dir_list_size = 5;
451 sacred_dir_list = (char **)XtMalloc(sizeof(char *) * sacred_dir_list_size);
453 sacred_dir_list[sacred_dir_count++] = XtNewString("/");
454 sacred_dir_list[sacred_dir_count++] = XtNewString(TrashInfoFileName);
456 ptr = TrashInfoFileName + 1;
457 while(ptr = DtStrchr(ptr, '/'))
459 /* All parent components of the user's home dir cannot be deleted */
461 if (sacred_dir_count >= sacred_dir_list_size)
464 sacred_dir_list_size += 5;
465 sacred_dir_list = (char **)XtRealloc((char *)sacred_dir_list,
466 sizeof(char *) * sacred_dir_list_size);
468 sacred_dir_list[sacred_dir_count++] = XtNewString(TrashInfoFileName);
473 /* load and verify existence for files previously left in the trash can */
474 TrashInitialized = ReadTrashList( );
475 return( TrashInitialized );
478 /************************************************************************
481 * This function must be called before any other interatctions with the
482 * trash can. It creates the trash dialog, opens the trash file, and
483 * loads information about any files previously left in the trash can.
485 * This function is called from main().
487 ************************************************************************/
495 DialogData * dialog_data;
496 FileMgrData * file_mgr_data;
498 if( ! TrashInitialized )
501 /* Create the trash dialog window */
502 /* load trash title */
503 tmpStr = GETMESSAGE(27, 9, "Trash Can");
504 title = XtNewString(tmpStr);
506 /* Initially, no items can be selected */
507 if (removeBtn != NULL)
508 XtSetSensitive(*removeBtn, False);
509 if (restoreBtn != NULL)
510 XtSetSensitive(*restoreBtn, False);
512 /* retrieve the default data for a file manager dialog -- */
513 /* the TrashView flag affects this data */
515 dialog_data = _DtGetDefaultDialogData (file_mgr_dialog);
516 file_mgr_data = (FileMgrData *) dialog_data->data;
517 trashFileMgrData = file_mgr_data;
518 trashDialogData = dialog_data;
520 /* build up directory set for trash directory */
521 FileMgrBuildDirectories (file_mgr_data, home_host_name, trash_dir);
523 /* initialize trash data */
524 file_mgr_data->restricted_directory =
525 XtNewString(file_mgr_data->current_directory);
526 file_mgr_data->title = XtNewString(title);
527 file_mgr_data->toolbox = False;
528 file_mgr_data->width = 300;
529 file_mgr_data->height = 300;
531 /* load any positioning information */
532 LoadPositionInfo(file_mgr_data);
534 /* build the trash dialog */
535 _DtBuildDialog (NULL, NULL, NULL, dialog_data, NULL, NULL, NULL,
536 NULL, NULL, False, special_view, title, NULL);
544 /************************************************************************
547 * At startup time, we need to read in the trash information file, to
548 * find out what, if any, files were left in the trash the last time
549 * this client was run. Each entry must be read in and then verified
550 * that it still exists; if it no longer exists, then someone has been
551 * mucking with our trash files, so we will remove it from our knowledge
552 * base. Otherwise, the file is added to the trash list.
554 ************************************************************************/
557 ReadTrashList( void )
559 int intSize, extSize, bufSize;
560 FILE * trashInfoFileId;
561 String external, intName, internal;
565 trashInfoFileId = fopen(TrashInfoFileName, "a+");
566 if (trashInfoFileId == 0)
570 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
572 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.") )
573 + strlen( TrashInfoFileName )
576 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 );
578 _DtMessage(toplevel, title, msg, NULL, HelpRequestCB);
584 chmod(TrashInfoFileName, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
586 bufSize = MAX_PATH * 2;
587 trashEntry = (char *)XtCalloc( 1, bufSize );
589 /* Extract the size of the two path strings */
590 while( fgets( trashEntry, bufSize, trashInfoFileId ) != NULL )
592 int len = strlen( trashEntry );
594 if( sscanf( trashEntry, "%d %d", &extSize, &intSize ) == 2 )
596 external = (char *)XtCalloc( 1, extSize + 1 );
597 intName = (char *)XtCalloc( 1, intSize + 1 );
599 if( len > extSize + intSize + 3 )
600 /* extSize + intSize + 3 is a rough estimate of a trash entry
601 a trash entry looks something like the following:
605 extSize is the size of the string '/tmp/abc'
606 intSize is the size of the string 'abcd'
608 so the len (the string length of the trash entry has AT LEAST to be
609 bigger than extSize + intSize + 3 separator spaces
614 /* sscanf can be used to reduce the code.
615 I'm not using it here because I don't have time to research.
616 I just want it to work.
618 tmpPtr = DtStrchr( trashEntry, ' ' ) + 1;
619 tmpPtr = DtStrchr( tmpPtr, ' ' ) + 1;
621 /* Extract the full external and partial internal file names */
622 memcpy( external, tmpPtr, extSize );
623 memcpy( intName, tmpPtr + extSize + 1, intSize );
625 /* Create internal/trash name */
626 internal = (char *)XtMalloc(strlen(users_home_dir)
628 + strlen(intName) + 2);
629 sprintf(internal, "%s%s/%s", users_home_dir, TRASH_DIR, intName);
631 /* Make sure the file still exists */
632 if (lstat(internal, &statbuf) < 0)
634 /* File no longer exists; ignore it */
641 /* Add to trash list */
642 if (numTrashItems >= trashListSize)
645 trashCan = (TrashEntry *)XtRealloc((char *) trashCan,
646 sizeof(TrashEntry) * trashListSize);
649 trashCan[numTrashItems].intNew = internal;
650 trashCan[numTrashItems].intOrig = XtNewString(external);
651 trashCan[numTrashItems].external = external;
652 trashCan[numTrashItems].filename = intName;
654 } /* end if file exists */
663 fclose(trashInfoFileId);
665 return( WriteTrashEntries() );
670 /************************************************************************
673 * This function is responsible for creating a new copy of the trash
674 * information file. It will open a new, temporary copy, and will
675 * copy in the contents of the trash can. It will then remove the
676 * old trash information file, and will rename the new one, opening
677 * a handle to it for later use.
679 ************************************************************************/
682 WriteTrashEntries( void )
684 static String path = NULL;
688 newFile = fopen(NewTrashInfoFileName, "w+");
691 chmod(NewTrashInfoFileName,
692 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
694 /* Write all remaining entries */
695 for (i = 0; i < numTrashItems; i++)
697 if( WriteEntry(newFile, trashCan[i].external, trashCan[i].filename) < 0 )
700 remove(NewTrashInfoFileName);
705 /* Remove the original information file, and move the new one */
706 (void) fclose(newFile);
707 (void) chown(NewTrashInfoFileName, getuid(), getgid());
708 (void) remove(TrashInfoFileName);
709 (void) rename(NewTrashInfoFileName, TrashInfoFileName);
710 (void) chown(TrashInfoFileName, getuid(), getgid());
719 /************************************************************************
722 * This function writes an entry to the trash information file.
723 * It expects the full path name for the original file [external]
724 * and only the new file name (without path information) [internal].
725 * If the file ends in '/', then it adds a '.' to the end; this is
726 * a special case, so that when we parse this information later, we
727 * are guaranteed to have a directory and a file name.
729 ************************************************************************/
737 return( fprintf(id, "%ld %ld %s %s\n",
738 (long)strlen(external), (long)strlen(internal),
739 external, internal) );
744 /************************************************************************
748 ************************************************************************/
753 FileMgrRec *file_mgr_rec )
758 static Widget * directoryBarBtn;
760 /* Create the menubar hierarchy */
761 trashMenu = (MenuDesc *)XtMalloc(sizeof(MenuDesc) * trashMenuItemCount);
764 for (i = 0; i < trashMenuItemCount; i++)
766 trashMenu[i].helpCallback = TrashHelpRequestCB;
767 trashMenu[i].helpData = NULL;
768 trashMenu[i].activateCallback = NULL;
769 trashMenu[i].activateData = NULL;
770 trashMenu[i].maskBit = 0;
771 trashMenu[i].isHelpBtn = False;
772 trashMenu[i].label = NULL;
773 trashMenu[i].mnemonic = NULL;
777 /*************************************/
778 /* Create the 'File' menu components */
779 /*************************************/
781 directoryBarBtn = &(trashMenu[j].widget);
782 trashMenu[j].type = MENU_PULLDOWN_BUTTON;
783 trashMenu[j].label = GETMESSAGE(20,1, "File");
784 trashMenu[j].label = XtNewString(trashMenu[j].label);
785 trashMenu[j].mnemonic = GETMESSAGE(20,2, "F");
786 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
787 trashMenu[j].name = "file_trash";
788 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
790 trashMenu[j].type = SHARED_MENU_PANE;
791 trashMenu[j].name = "fileMenuTrash";
792 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
794 selectAllBtn = &(trashMenu[j].widget);
795 trashMenu[j].type = MENU_BUTTON;
796 trashMenu[j].label = GETMESSAGE(20,48, "Select All");
797 trashMenu[j].label = XtNewString(trashMenu[j].label);
798 trashMenu[j].mnemonic = GETMESSAGE(20,51, "S");
799 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
800 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
801 trashMenu[j].name = "selectAll";
802 trashMenu[j++].activateCallback = Select_All;
804 trashMenu[j].type = MENU_BUTTON;
805 trashMenu[j].label = GETMESSAGE(20,52, "Deselect All");
806 trashMenu[j].label = XtNewString(trashMenu[j].label);
807 trashMenu[j].mnemonic = GETMESSAGE(20,55, "D");
808 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
809 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
810 trashMenu[j].name = "deselectAll";
811 trashMenu[j++].activateCallback = Unselect_All;
813 trashMenu[j].type = MENU_SEPARATOR;
814 trashMenu[j].name = "separator";
815 trashMenu[j++].helpCallback = NULL;
817 restoreBtn = &(trashMenu[j].widget);
818 trashMenu[j].type = MENU_BUTTON;
819 trashMenu[j].label = GETMESSAGE(27,24, "Put back");
820 trashMenu[j].label = XtNewString(trashMenu[j].label);
821 trashMenu[j].mnemonic = GETMESSAGE(27,26, "P");
822 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
823 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
824 trashMenu[j].name = "putBack";
825 trashMenu[j++].activateCallback = Restore;
827 removeBtn = &(trashMenu[j].widget);
828 trashMenu[j].type = MENU_BUTTON;
829 trashMenu[j].label = GETMESSAGE(27,28, "Shred");
830 trashMenu[j].label = XtNewString(trashMenu[j].label);
831 trashMenu[j].mnemonic = GETMESSAGE(27,30, "h");
832 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
833 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
834 trashMenu[j].name = "shred";
835 trashMenu[j++].activateCallback = ConfirmRemove;
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].maskBit = PREFERENCES;
843 trashMenu[j].label = GETMESSAGE(20,141, "Set View Options ...");
844 trashMenu[j].label = XtNewString(trashMenu[j].label);
845 trashMenu[j].mnemonic = GETMESSAGE(20,6, "V");
846 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
847 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
848 trashMenu[j].name = "setPreferences";
849 trashMenu[j++].activateCallback = ShowPreferencesDialog;
851 trashMenu[j].type = MENU_SEPARATOR;
852 trashMenu[j].name = "separator";
853 trashMenu[j++].helpCallback = NULL;
855 trashMenu[j].type = MENU_BUTTON;
856 trashMenu[j].label = GETMESSAGE(20,117, "Close");
857 trashMenu[j].label = XtNewString(trashMenu[j].label);
858 trashMenu[j].mnemonic = GETMESSAGE(20,118, "C");
859 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
860 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
861 trashMenu[j].name = "close";
862 trashMenu[j++].activateCallback = CloseTrash;
865 /*************************************/
866 /* Create the 'Help' menu components */
867 /*************************************/
869 trashMenu[j].type = MENU_PULLDOWN_BUTTON;
870 trashMenu[j].label = GETMESSAGE(20,123, "Help");
871 trashMenu[j].label = XtNewString(trashMenu[j].label);
872 trashMenu[j].mnemonic = GETMESSAGE(20,9, "H");
873 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
874 trashMenu[j].isHelpBtn = True;
875 trashMenu[j].name = "help_trash";
876 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
878 trashMenu[j].type = SHARED_MENU_PANE;
879 trashMenu[j].name = "help_pane_trash";
880 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
882 trashMenu[j].type = MENU_BUTTON;
883 trashMenu[j].label = GETMESSAGE(20,105, "Overview");
884 trashMenu[j].label = XtNewString(trashMenu[j].label);
885 trashMenu[j].mnemonic = GETMESSAGE(20,106, "v");
886 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
887 trashMenu[j].helpData = HELP_HELP_MENU_STR;
888 trashMenu[j].activateData = HELP_TRASH_OVERVIEW_TOPIC_STR;
889 trashMenu[j].name = "introduction";
890 trashMenu[j++].activateCallback = TrashHelpRequestCB;
892 trashMenu[j].type = MENU_SEPARATOR;
893 trashMenu[j].name = "separator";
894 trashMenu[j++].helpCallback = NULL;
896 trashMenu[j].type = MENU_BUTTON;
897 trashMenu[j].label = GETMESSAGE(20,107, "Tasks");
898 trashMenu[j].label = XtNewString(trashMenu[j].label);
899 trashMenu[j].mnemonic = GETMESSAGE(20,108, "T");
900 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
901 trashMenu[j].helpData = HELP_HELP_MENU_STR;
902 trashMenu[j].activateData = HELP_TRASH_TASKS_TOPIC_STR;
903 trashMenu[j].name = "tasks";
904 trashMenu[j++].activateCallback = TrashHelpRequestCB;
906 trashMenu[j].type = MENU_BUTTON;
907 trashMenu[j].label = GETMESSAGE(20,109, "Reference");
908 trashMenu[j].label = XtNewString(trashMenu[j].label);
909 trashMenu[j].mnemonic = GETMESSAGE(20,110, "R");
910 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
911 trashMenu[j].helpData = HELP_HELP_MENU_STR;
912 trashMenu[j].activateData = HELP_TRASH_DIALOG_STR;
913 trashMenu[j].name = "reference";
914 trashMenu[j++].activateCallback = TrashHelpRequestCB;
916 trashMenu[j].type = MENU_BUTTON;
917 trashMenu[j].label = GETMESSAGE(20,111, "On Item");
918 trashMenu[j].label = XtNewString(trashMenu[j].label);
919 trashMenu[j].mnemonic = GETMESSAGE(20,112, "O");
920 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
921 trashMenu[j].helpData = HELP_HELP_MENU_STR;
922 trashMenu[j].activateData = HELP_HELP_MODE_STR;
923 trashMenu[j].name = "onItem";
924 trashMenu[j++].activateCallback = TrashHelpRequestCB;
926 trashMenu[j].type = MENU_SEPARATOR;
927 trashMenu[j].name = "separator";
928 trashMenu[j++].helpCallback = NULL;
930 usingHelpTrash = &(trashMenu[j].widget);
931 trashMenu[j].type = MENU_BUTTON;
932 trashMenu[j].label = GETMESSAGE(20,113, "Using Help");
933 trashMenu[j].label = XtNewString(trashMenu[j].label);
934 trashMenu[j].mnemonic = GETMESSAGE(20,114, "U");
935 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
936 trashMenu[j].helpData = HELP_HELP_MENU_STR;
937 trashMenu[j].activateData = HELP_HOME_TOPIC;
938 trashMenu[j].name = "usingHelp";
939 trashMenu[j++].activateCallback = TrashHelpRequestCB;
941 trashMenu[j].type = MENU_SEPARATOR;
942 trashMenu[j].name = "separator";
943 trashMenu[j++].helpCallback = NULL;
945 trashMenu[j].type = MENU_BUTTON;
946 trashMenu[j].label = GETMESSAGE(27,106, "About Trash Can");
947 trashMenu[j].label = XtNewString(trashMenu[j].label);
948 trashMenu[j].mnemonic = GETMESSAGE(20,116, "A");
949 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
950 trashMenu[j].helpData = HELP_HELP_MENU_STR;
951 trashMenu[j].activateData = HELP_ABOUT_STR;
952 trashMenu[j].name = "version";
953 trashMenu[j++].activateCallback = TrashHelpRequestCB;
955 menu_bar = _DtCreateMenuSystem(mainw, "menu_bar_trash", TrashHelpRequestCB,
956 HELP_TRASH_DIALOG_STR, True,
957 trashMenu, trashMenuItemCount, NULL,
960 /* Fine tune the menubar */
961 XtSetArg(args[0], XmNmarginWidth, 2);
962 XtSetArg(args[1], XmNmarginHeight, 2);
963 XtSetValues(menu_bar, args, 2);
965 file_mgr_rec->actions = NULL;
966 file_mgr_rec->action_pane = NULL;
967 file_mgr_rec->directoryBarBtn = *directoryBarBtn;
974 /************************************************************************
976 * TrashDisplayHandler
977 * This is the ICCCM message handler for the message used to display
978 * the trash can window. If the window is already displayed, then
979 * this call is a no-op.
981 * This handler is triggered by the front panel trash icon and by the
982 * File pulldown 'Show Trash' option.
984 ************************************************************************/
994 tt_message_reply( msg );
995 tttk_message_destroy( msg );
998 if (!TrashInitialized)
1000 char *tmpStr, *tmpTitle, *tmpMsg;
1002 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1003 tmpTitle = XtNewString(tmpStr);
1004 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.");
1005 tmpMsg = XtNewString(tmpStr);
1007 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1014 #ifdef DEFER_TRASH_CREATION
1015 if( trashFileMgrData == NULL )
1016 TrashCreateDialog (XtDisplay(toplevel));
1019 /* the encapsulation functions do not set file_mgr_rec until a dialog */
1020 /* is actually displayed */
1021 if (trashFileMgrData->file_mgr_rec == 0)
1023 static Pixmap trash_icon = XmUNSPECIFIED_PIXMAP;
1024 static Pixmap trash_mask = XmUNSPECIFIED_PIXMAP;
1026 Pixel background, foreground, top_shadow, bottom_shadow, select;
1028 XClassHint classHints;
1029 FileMgrRec * file_mgr_rec;
1031 unsigned int height;
1035 classHints.res_name = trashFileMgrData->title;
1036 classHints.res_class = DTFILE_CLASS_NAME;
1039 _DtShowBuiltDialog(NULL,NULL, trashDialogData, NULL, False, &classHints);
1042 file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
1044 trashShell = file_mgr_rec->shell;
1046 if (trash_icon == XmUNSPECIFIED_PIXMAP)
1048 XtSetArg (args[0], XmNbackground, &background);
1049 XtSetArg (args[1], XmNcolormap, &colormap);
1050 XtGetValues (file_mgr_rec->main, args, 2);
1052 XmGetColors (XtScreen (file_mgr_rec->main), colormap, background,
1053 &foreground, &top_shadow, &bottom_shadow, &select);
1055 pixmap = XmGetPixmap (XtScreen (file_mgr_rec->main), trashIcon,
1056 foreground, background);
1057 if( pixmap != XmUNSPECIFIED_PIXMAP)
1058 trash_icon = pixmap;
1060 /* now let's get the mask for the File Manager */
1061 pixmap = _DtGetMask (XtScreen (file_mgr_rec->main), trashIcon);
1062 if( pixmap != XmUNSPECIFIED_PIXMAP)
1063 trash_mask = pixmap;
1065 if (trash_icon != XmUNSPECIFIED_PIXMAP)
1067 XtSetArg (args[0], XmNiconPixmap, trash_icon);
1068 if(trash_mask != XmUNSPECIFIED_PIXMAP)
1070 XtSetArg (args[1], XmNiconMask, trash_mask);
1071 XtSetValues (trashShell, args, 2);
1074 XtSetValues (trashShell, args, 1);
1082 root = RootWindowOfScreen (XtScreen (trashShell));
1084 /* Change the hints to reflect the current workspace */
1085 /* and raise the window */
1087 if (DtWsmGetCurrentWorkspace (XtDisplay (trashShell),
1088 root, ¤t_ws) == Success)
1089 DtWsmSetWorkspacesOccupied (XtDisplay (trashShell),
1090 XtWindow (trashShell), ¤t_ws, 1);
1092 /* Set the iconify state */
1093 /* Remove the iconify hint from the current shell */
1094 wmhints = XGetWMHints(XtDisplay(trashShell), XtWindow(trashShell));
1095 wmhints->flags |= IconWindowHint;
1096 wmhints->initial_state = NormalState;
1097 XSetWMHints(XtDisplay(trashShell), XtWindow(trashShell), wmhints);
1101 XtPopup (trashShell, XtGrabNone);
1102 XSync(XtDisplay(trashShell), False);
1103 XRaiseWindow (XtDisplay (trashShell), XtWindow (trashShell));
1104 XMapWindow( XtDisplay (trashShell), XtWindow (trashShell) );
1107 msg = tt_pnotice_create(TT_SESSION, "DtActivity_Began");
1108 tt_message_send(msg);
1109 tttk_message_destroy(msg);
1113 trashDialogPosted = True;
1117 /************************************************************************
1119 * ConfirmEmptyCancel
1121 ************************************************************************/
1122 static XtPointer trash_popup_client_data;
1126 XtPointer client_data,
1127 XtPointer call_data )
1129 XtUnmanageChild((Widget)client_data);
1130 XmUpdateDisplay((Widget)client_data);
1131 XtDestroyWidget((Widget)client_data);
1134 /************************************************************************
1138 ************************************************************************/
1143 XtPointer client_data,
1144 XtPointer call_data )
1146 /* destroy dialog */
1147 XtUnmanageChild((Widget)client_data);
1148 XmUpdateDisplay((Widget)client_data);
1149 XtDestroyWidget((Widget)client_data);
1154 /************************************************************************
1157 * This is a message interface used to really remove all of the
1160 * This handler is only called from an empty trash action.
1162 ************************************************************************/
1169 tt_message_reply( msg );
1170 tttk_message_destroy( msg );
1173 if(!TrashInitialized)
1175 char *tmpStr, *tmpTitle, *tmpMsg;
1177 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1178 tmpTitle = XtNewString(tmpStr);
1179 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.");
1180 tmpMsg = XtNewString(tmpStr);
1182 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1190 char *tmpStr, *title, *msg;
1192 tmpStr = GETMESSAGE(27,73, "Shred File(s)");
1193 title = XtNewString(tmpStr);
1194 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");
1195 msg = XtNewString(tmpStr);
1197 _DtMessageDialog(toplevel, title, msg, NULL, TRUE, ConfirmEmptyCancel,
1198 ConfirmEmptyOk, NULL, HelpRequestCB, False, QUESTION_DIALOG);
1207 /************************************************************************
1210 * This is a functional interface used to really remove all of the
1213 ************************************************************************/
1218 if (trashFileMgrData)
1221 Select_All(*selectAllBtn, NULL, NULL);
1222 Remove(*removeBtn, NULL, NULL);
1224 EmptyDir(trashFileMgrData->current_directory,0,0);
1230 /************************************************************************
1233 * This is the callback attached to the 'Select All' menu item. It
1234 * will select all of the files currently resident in the trash can.
1235 * It will also sensitize the 'Restore' and 'Remove' buttons, as long
1236 * as a remove request is not currently in the middle of being processed.
1238 ************************************************************************/
1243 XtPointer client_data,
1244 XtPointer call_data )
1247 SelectAllFiles(trashFileMgrData);
1248 SensitizeTrashBtns();
1253 /************************************************************************
1256 * This is the callback attached to the 'Unselect All' menu item. It
1257 * will unselect all of the items in the trash can, and will clear
1258 * out our selection array. It also desensitizes the 'Restore' and
1261 ************************************************************************/
1266 XtPointer client_data,
1267 XtPointer call_data )
1270 DeselectAllFiles(trashFileMgrData);
1271 SensitizeTrashBtns();
1276 /************************************************************************
1278 * SensitizeTrashBtns
1280 ************************************************************************/
1283 SensitizeTrashBtns(void)
1285 #ifdef DEFER_TRASH_CREATION
1286 if( trashFileMgrData
1291 if (trashFileMgrData->selected_file_count > 0)
1293 XtSetSensitive(*removeBtn, True);
1294 XtSetSensitive(*restoreBtn, True);
1298 XtSetSensitive(*removeBtn, False);
1299 XtSetSensitive(*restoreBtn, False);
1306 /************************************************************************
1308 * TrashRemoveHandler
1309 * This is the message handler for the message requesting that
1310 * a set of files be added to the trashcan. Each file name comes in
1311 * in /dir/name format; they are all in a single string, separated
1314 * Check for spaces in filenames and destop files. If we pass these
1315 * cases, call TrashRemover where the following occurs:
1317 * All of the files will be moved to the trash can directory; any
1318 * problems encountered will be reported in an error dialog. Each file
1319 * which is successfully moved, will be added to the trash information file.
1321 ************************************************************************/
1334 int msglistsize = 0 ;
1335 #endif /* SUN_PERF */
1338 * Note: There used to be a call to tt_message_reply() right here at the
1339 * beginning of the routine. However, the mailer wants to trash files
1340 * but wants the notification after the trash has been removed.
1341 * Therefore, we now put this off until the work is done or an error
1345 if( !TrashInitialized )
1347 char *tmpStr, *tmpTitle, *tmpMsg;
1349 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1350 tmpTitle = XtNewString(tmpStr);
1351 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.");
1352 tmpMsg = XtNewString(tmpStr);
1354 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1359 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
1361 global_msg_cnt = 0 ;
1362 #endif /* SUN_PERF */
1366 MessageToFileList(msg, &file_list, &file_count);
1369 * When the Dttrash_Remove handler "TrashRemoveHandler" is invoked for
1370 * the first time it caches the TT request and then invokes
1371 * tttk_block_while() with timeout = 0. This in effect causes the next
1372 * TT request to be processed before tttk_block_while() returns. Now if
1373 * TT msgs are coming in faster than dtfile is handling them, then the
1374 * next TT msg. should be another Dttrash_Remove msg. which will cause
1375 * re-entrance into TrashRemoveHandler. This will continue until the last
1376 * Dttrash_Remove msg. has been processed (and the recursive
1377 * TrashRemoveHandler starts unwinding) after which we check if the
1378 * number of TT msgs are > 0 in which case we fire off the
1379 * process to move the list of files to Trash.
1382 msglistsize = sizeof(Tt_message) * global_msg_cnt ;
1383 global_msg_list = (Tt_message *)XtRealloc((char *)global_msg_list, msglistsize);
1384 memcpy((char *)&(global_msg_list[global_msg_cnt - 1]),
1385 (char *)&msg, sizeof(Tt_message)) ;
1386 tttk_block_while(0, 0, time_out) ;
1388 /* If we get to this pt. then either :
1389 * 1. No more Dttrash_Remove msgs. left to process. Thus we should
1390 * process the TT msg list and move files to Trash.
1392 * 2. We got a msg. different from Dttrash_Remove. In this case
1393 * we process the msgs. we have so far. The remaining files to
1394 * be trashed will be buffered in another list.
1397 if (global_msg_cnt > 0) {
1398 MessagesToFileList(global_msg_list, global_msg_cnt,
1399 &file_list, &file_count);
1400 #endif /* SUN_PERF */
1402 if (file_count == 0)
1404 tt_message_reply( msg );
1405 tttk_message_destroy( msg );
1407 global_msg_cnt = 0 ;
1408 #endif /* SUN_PERF */
1412 /* return if from the trash dir */
1413 if (strncmp(file_list[0], trash_dir, strlen(trash_dir)) == 0)
1417 for (i = 0; i < file_count; i++) {
1418 XtFree( file_list[i] );
1420 XtFree( (char *)file_list );
1421 tt_message_status_set( msg, TT_DESKTOP_EALREADY );
1422 tt_message_reply( msg );
1423 tttk_message_destroy( msg );
1425 global_msg_cnt = 0 ;
1426 #endif /* SUN_PERF */
1430 /* post message if file(s) from desktop */
1434 str=(char *)IsAFileOnDesktop2(file_list, file_count, &number,&IsToolBox);
1435 /* IsToolBox is unused here, but is required to satisfy the prototype */
1439 MoveToTrash(file_list, file_count, verifyPromptsEnabled, msg);
1449 /* List will be regenerated by callbacks */
1450 for (i = 0; i < file_count; i++) {
1451 XtFree( file_list[i] );
1453 XtFree( (char *)file_list );
1455 tmpStr = (GETMESSAGE(27,73, "Shred File(s)"));
1456 title = XtNewString(tmpStr);
1460 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."));
1462 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."));
1463 message = (char *)XtMalloc(strlen(template) + strlen(str) + 1);
1464 sprintf(message, template, str);
1469 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."));
1471 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."));
1472 message = (char *)XtMalloc(strlen(template) + strlen(str) + 1);
1473 sprintf(message, template, str);
1476 /* Really ought to pass to OK and Cancel CBs via client_data */
1479 _DtMessageDialog(toplevel, title, message, NULL, TRUE,
1480 RemoveCancelCB, RemoveOkCB, NULL, HelpRequestCB, False,
1486 Tt_msg_cache *msg_cache ;
1488 msg_cache = (Tt_msg_cache *)XtMalloc(sizeof(Tt_msg_cache)) ;
1489 msg_cache->msg_cnt = global_msg_cnt ;
1491 msg_cache->msg_list = (Tt_message *)XtMalloc(sizeof(Tt_message) *
1492 msg_cache->msg_cnt );
1493 msg_cache->msg_list[msg_cache->msg_cnt] = NULL ;
1494 memcpy((char *)msg_cache->msg_list, (char *)global_msg_list,
1495 sizeof(Tt_message) * msg_cache->msg_cnt ) ;
1496 dlog = (Widget)_DtMessageDialog(toplevel, title, message, NULL, TRUE,
1497 RemoveCancelCB, RemoveOkCB, NULL, HelpRequestCB, False,
1499 XtSetArg ( args[0], XmNuserData, msg_cache );
1500 XtSetValues ( dlog, args, 1);
1502 #endif /* SUN_PERF */
1509 global_msg_cnt = 0 ;
1511 #endif /* SUN_PERF */
1516 /************************************************************************
1519 * Cleanup and unmanage the remove from DT dialog
1521 ************************************************************************/
1526 XtPointer client_data,
1527 XtPointer call_data )
1533 Tt_msg_cache *current_msg_cache ;
1536 XtSetArg( args[0], XmNuserData, ¤t_msg_cache );
1537 XtGetValues( (Widget)client_data, args, 1 );
1538 #endif /* SUN_PERF */
1540 /* destroy dialog */
1541 XtUnmanageChild((Widget)client_data);
1542 XmUpdateDisplay((Widget)client_data);
1543 XtDestroyWidget((Widget)client_data);
1547 MessageToFileList(global, &file_list, &file_count);
1549 MessagesToFileList( current_msg_cache->msg_list, current_msg_cache->msg_cnt,
1550 &file_list, &file_count);
1551 #endif /* SUN_PERF */
1553 MoveToTrash(file_list, file_count, verifyPromptsEnabled, NULL);
1555 /* reread desktop files */
1559 tt_message_reply( global );
1560 tttk_message_destroy( global );
1563 for (i = 0 ; i < current_msg_cache->msg_cnt ; i++) {
1564 tt_message_reply( current_msg_cache->msg_list[i] );
1565 tttk_message_destroy( current_msg_cache->msg_list[i] );
1567 XtFree ((char *)current_msg_cache->msg_list);
1568 XtFree ((char *)current_msg_cache);
1569 #endif /* SUN_PERF */
1574 /************************************************************************
1577 * Cleanup and unmanage the remove from DT dialog
1579 ************************************************************************/
1584 XtPointer client_data,
1585 XtPointer call_data )
1589 Tt_msg_cache *current_msg_cache ;
1592 XtSetArg( args[0], XmNuserData, ¤t_msg_cache );
1593 XtGetValues( (Widget)client_data, args, 1 );
1594 #endif /* SUN_PERF */
1596 /* destroy dialog */
1597 XtUnmanageChild((Widget)client_data);
1598 XmUpdateDisplay((Widget)client_data);
1599 XtDestroyWidget((Widget)client_data);
1602 tttk_message_fail( global, TT_DESKTOP_ECANCELED, 0, 1 );
1605 for (i = 0 ; i < current_msg_cache->msg_cnt ; i++)
1606 tttk_message_fail( current_msg_cache->msg_list[i], TT_DESKTOP_ECANCELED, 0, 1 );
1607 XtFree ((char *)current_msg_cache->msg_list);
1608 XtFree ((char *)current_msg_cache);
1609 #endif /* SUN_PERF */
1614 /************************************************************************
1616 * TrashRemoveNoConfirmHandler
1617 * This function is a message handler. It will place the specified set
1618 * of files into the trash can, without requesting confirmation if, for
1619 * instance, a file does not have write permission, or a directory is
1622 ************************************************************************/
1625 TrashRemoveNoConfirmHandler(
1631 if( !TrashInitialized )
1633 char *tmpStr, *tmpTitle, *tmpMsg;
1635 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1636 tmpTitle = XtNewString(tmpStr);
1637 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.");
1638 tmpMsg = XtNewString(tmpStr);
1640 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1646 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
1650 MessageToFileList(msg, &file_list, &file_count);
1651 MoveToTrash(file_list, file_count, False, msg);
1656 /************************************************************************
1660 * Parses a list of file names from a message
1661 * and returns an array of strings.
1663 * if (no message arguments) {
1664 * if (no file attribute) {
1665 * return length==0 list
1667 * return length==1 list containing file attribute
1670 * if (file attribute) {
1671 * file attribute is a directory, prepended onto subsequent filenames
1673 * if (arg 0 is non-null) {
1674 * arg 0 interpreted as a space-separated list of filenames
1677 * arg interpreted as a filename possibly containing spaces
1681 ************************************************************************/
1697 args = tt_message_args_count( msg );
1698 if (tt_is_err(tt_int_error( args ))) {
1702 dir = tt_message_file( msg );
1703 if ((tt_is_err(tt_ptr_error( dir ))) || (dir == 0)) {
1707 /* No args means use tt_message_file() as is */
1708 *file_list = (char **)
1709 XtRealloc((char *)(*file_list),
1710 (*file_count + 1)*sizeof(char *));
1711 (*file_list)[*file_count] = XtNewString(dir);
1716 if (dir[strlen(dir)] != '/') {
1718 if (strcmp(dir, "/") == 0)
1720 newdir = (char *)tt_malloc( strlen(dir)+1 );
1721 strcpy( newdir, dir );
1725 newdir = (char *)tt_malloc( strlen(dir)+2 );
1726 strcpy( newdir, dir );
1727 strcat( newdir, "/" );
1733 /* Invariant: dir can now be safely concatenated to form a valid path */
1734 dirlen = strlen(dir);
1735 for (arg = 0; arg < args; arg++) {
1740 val = tt_message_arg_val( msg, arg );
1741 if ((tt_is_err(tt_ptr_error( val ))) || (val == 0)) {
1747 /* Arg 0 is a space-separated list */
1750 for (val = DtStrtok(val, white); val; val = DtStrtok(0, white))
1752 if (strcmp(val, "/") == 0)
1754 file = (char *)XtMalloc( dirlen + 1 );
1755 strcpy( file, dir );
1759 file = (char *)XtMalloc( dirlen + strlen(val) + 1 );
1760 strcpy( file, dir );
1761 strcat( file, val );
1763 *file_list = (char **)
1764 XtRealloc((char *)(*file_list),
1765 (*file_count + 1)*sizeof(char *));
1766 (*file_list)[*file_count] = DtEliminateDots(file);
1769 tt_free( val2free );
1777 Tt_message *msg_list,
1792 for (num_msgs = 0 ; num_msgs < msg_cnt ; num_msgs++)
1794 msg = msg_list[num_msgs] ;
1796 args = tt_message_args_count( msg );
1797 if (tt_is_err(tt_int_error( args ))) {
1801 dir = tt_message_file( msg );
1802 if ((tt_is_err(tt_ptr_error( dir ))) || (dir == 0)) {
1806 /* No args means use tt_message_file() as is */
1807 *file_list = (char **)
1808 XtRealloc((char *)(*file_list),
1809 (*file_count + 1)*sizeof(char *));
1810 (*file_list)[*file_count] = XtNewString(dir);
1815 if (dir[strlen(dir)] != '/') {
1817 if (strcmp(dir, "/") == 0)
1819 newdir = (char *)tt_malloc( strlen(dir)+1 );
1820 strcpy( newdir, dir );
1824 newdir = (char *)tt_malloc( strlen(dir)+2 );
1825 strcpy( newdir, dir );
1826 strcat( newdir, "/" );
1832 /* Invariant: dir can now be safely concatenated to form a valid path */
1833 dirlen = strlen(dir);
1834 for (arg = 0; arg < args; arg++) {
1839 val = tt_message_arg_val( msg, arg );
1840 if ((tt_is_err(tt_ptr_error( val ))) || (val == 0)) {
1846 /* Arg 0 is a space-separated list */
1849 for (val = DtStrtok(val, white); val; val = DtStrtok(0, white))
1851 if (strcmp(val, "/") == 0)
1853 file = (char *)XtMalloc( dirlen + 1 );
1854 strcpy( file, dir );
1856 else if (strcmp(val, "/") == 0 && dirlen == 0)
1858 file = (char *)XtMalloc( strlen(val) + 1 );
1859 strcpy( file, val );
1863 file = (char *)XtMalloc( dirlen + strlen(val) + 1 );
1864 strcpy( file, dir );
1865 strcat( file, val );
1867 *file_list = (char **)
1868 XtRealloc((char *)(*file_list),
1869 (*file_count + 1)*sizeof(char *));
1870 (*file_list)[*file_count] = DtEliminateDots(file);
1873 tt_free( val2free );
1878 #endif /* SUN_PERF */
1881 /************************************************************************
1883 * CreateTrashFilename
1884 * Create trash directory name.
1886 ************************************************************************/
1889 CreateTrashFilename(
1891 Boolean uniqueTest )
1894 char * extension = NULL;
1895 struct stat statInfo;
1897 /* Create trash path */
1898 /* Give that name a little extra cushion, just in case */
1899 trashName = (char *)XtMalloc(strlen(users_home_dir) + strlen(TRASH_DIR) +
1900 strlen(baseName) + 15);
1901 sprintf(trashName, "%s%s/%s", users_home_dir, TRASH_DIR, baseName);
1903 /* Want to find the extension so the new file name created will preserve
1904 its original datatype.
1906 extension = strrchr( baseName, '.' );
1913 /* If a file by the trash name already exists, keep building new names */
1914 /* until one by that name doesn't exist and then use it */
1915 if (uniqueTest && (lstat(trashName, &statInfo) == 0))
1919 for (i = 1; True ; i++)
1921 /* Make a duplicate file name */
1923 sprintf(trashName, "%s%s/%s_%d.%s", users_home_dir, TRASH_DIR,
1924 baseName, i, extension);
1926 sprintf(trashName, "%s%s/%s_%d", users_home_dir, TRASH_DIR,
1929 /* Is the filename taken? */
1930 if (lstat(trashName, &statInfo) != 0)
1934 } /* end if not unique name */
1937 *(extension-1) = '.';
1945 /************************************************************************
1947 * MatchesSacredDirectory
1948 * Checks if the specify filename matches one of the sacred,
1949 * non-deleteable files.
1951 ************************************************************************/
1954 MatchesSacredDirectory(
1959 /* Don't allow the user to delete any of the sacred directories */
1960 for (i = 0; i < sacred_dir_count; i++)
1962 if (!strcmp(file, sacred_dir_list[i]))
1966 /* Compare against special desktop directories */
1967 /* remote_sys_dir is currently NULL */
1968 if (strcmp(file, remote_sys_dir) == 0)
1976 /************************************************************************
1979 * Callback for 'Delete To Trash' verify dialog Ok push button.
1981 ************************************************************************/
1986 XtPointer client_data,
1987 XtPointer call_data )
1989 VerifyCleanup ((Widget)client_data, True);
1994 /************************************************************************
1997 * Callback for 'Delete To Trash' verify dialog Cancel push button.
1999 ************************************************************************/
2004 XtPointer client_data,
2005 XtPointer call_data )
2008 VerifyCleanup ((Widget)client_data, False);
2013 /************************************************************************
2016 * Callback for 'Delete To Trash' verify dialog push buttons.
2018 * Called from VerifyOk and VerifyCancel.
2020 ************************************************************************/
2025 Boolean completeDelete )
2032 /* Unpost dialog, and retrieve the list of verified files */
2033 XtUnmanageChild(mbox);
2034 XmUpdateDisplay(mbox);
2035 XtSetArg(args[0], XmNuserData, &verifylist);
2036 XtGetValues(mbox, args, 1);
2038 /* count the number of files in the list */
2039 for (fileCount = 0; verifylist[fileCount]; fileCount++)
2043 * Start the background process that moves the files into the trash,
2047 MoveToTrash(verifylist, fileCount, False, NULL);
2051 /* Free up the storage we allocated */
2052 for (i = fileCount; i > 0; i--)
2053 XtFree(verifylist[i]);
2054 XtFree((char *)verifylist);
2057 XtDestroyWidget(mbox);
2062 /************************************************************************
2066 ************************************************************************/
2067 static XtPointer trash_popup_client_data;
2071 XtPointer client_data,
2072 XtPointer call_data )
2074 XtUnmanageChild((Widget)client_data);
2075 XmUpdateDisplay((Widget)client_data);
2076 XtDestroyWidget((Widget)client_data);
2078 XtSetSensitive(*removeBtn, True);
2079 XtSetSensitive(*restoreBtn, True);
2080 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
2081 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
2082 trash_popup_client_data = NULL;
2087 /************************************************************************
2091 ************************************************************************/
2096 XtPointer client_data,
2097 XtPointer call_data )
2099 FileViewData *file_view_data;
2101 /* destroy dialog */
2102 XtUnmanageChild((Widget)client_data);
2103 XmUpdateDisplay((Widget)client_data);
2104 XtDestroyWidget((Widget)client_data);
2106 file_view_data = trashFileMgrData->popup_menu_icon;
2107 if(!file_view_data && trashFileMgrData->selected_file_count)
2108 file_view_data = trashFileMgrData->selection_list[0];
2109 trashFileMgrData->popup_menu_icon = NULL;
2111 if(file_view_data != NULL)
2112 Remove(*removeBtn, file_view_data, NULL);
2117 /************************************************************************
2120 * This is the callback attached to the 'Remove' menu item.
2122 ************************************************************************/
2127 XtPointer client_data,
2128 XtPointer call_data )
2130 char *tmpStr, *title, *msg;
2132 /* Desensitize remove & restore buttons, until we're done */
2133 XtSetSensitive(*removeBtn, False);
2134 XtSetSensitive(*restoreBtn, False);
2135 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], False);
2136 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], False);
2139 trash_popup_client_data = client_data;
2141 tmpStr = GETMESSAGE(27,73, "Shred File(s)");
2142 title = XtNewString(tmpStr);
2143 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");
2144 msg = XtNewString(tmpStr);
2146 _DtMessageDialog(toplevel, title, msg, NULL, TRUE, ConfirmCancel, ConfirmOk,
2147 NULL, HelpRequestCB, False, QUESTION_DIALOG);
2155 /************************************************************************
2158 * While we wait for the background process to complete, we will
2159 * desensitize the 'Remove' and 'Restore' menu items, since we don't
2160 * handle multiple requests.
2162 ************************************************************************/
2167 XtPointer client_data,
2168 XtPointer call_data )
2170 FileViewData *file_view_data;
2171 Boolean match = False;
2172 DeleteList *deleteList = NULL;
2178 /* Remove may be called to remove a file or to remove a trash file; */
2179 /* if w is NULL, this is a call to remove a file */
2182 removeType = TRASH_FILE;
2184 /* Count number of items to 'Delete To Trash' */
2185 if (client_data != NULL)
2187 /* This was called by trash popup */
2188 /* need to check to see if it is selected, if it is restore all
2189 the selected files, else just restore this file */
2191 file_view_data = (FileViewData *)client_data;
2192 for (i = 0; i < trashFileMgrData->selected_file_count; i++)
2194 if (strcmp(file_view_data->file_data->file_name,
2195 trashFileMgrData->selection_list[i]->file_data->file_name) == 0)
2203 if (trashFileMgrData->selected_file_count == 0)
2205 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
2206 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
2213 /* Desensitize remove & restore buttons, until we're done */
2214 XtSetSensitive(*removeBtn, False);
2215 XtSetSensitive(*restoreBtn, False);
2219 deleteCount = trashFileMgrData->selected_file_count;
2223 /* Create the list of things being deleted */
2224 deleteList = (DeleteList *)XtMalloc(deleteCount * sizeof(DeleteList));
2228 for (i = 0; i < deleteCount; i++)
2230 AddToDeleteList(deleteList, i,
2231 trashFileMgrData->selection_list[i]->file_data->file_name);
2235 AddToDeleteList(deleteList, 0, file_view_data->file_data->file_name);
2240 removeType = REMOVE_FILE;
2241 deleteList = (DeleteList *)XtMalloc(sizeof(DeleteList));
2243 deleteList[0].trash = XtNewString(client_data);
2244 deleteList[0].orig = NULL;
2247 EmptyTrash(deleteList, deleteCount, removeType, NULL);
2252 /************************************************************************
2255 * Locate a file in the trash list and add it to the delete list.
2257 ************************************************************************/
2261 DeleteList *deleteList,
2267 /* Locate file in trash list, add entry to delete list */
2268 for (j = 0; j < numTrashItems; j++)
2270 if (strcmp(filename, trashCan[j].filename) == 0)
2272 /* file found in trash list */
2273 deleteList[i].trash = XtNewString(trashCan[j].intNew);
2274 deleteList[i].orig = XtNewString(trashCan[j].intOrig);
2280 /* file not found in trash list */
2281 deleteList[i].trash = CreateTrashFilename(filename, FALSE);
2282 deleteList[i].orig = NULL;
2287 /************************************************************************
2289 * TrashRestoreHandler
2290 * This function is a message handler. It will restore the specified set
2291 * of files from the trash can.
2293 ************************************************************************/
2296 TrashRestoreHandler(
2302 if( !TrashInitialized )
2304 char *tmpStr, *tmpTitle, *tmpMsg;
2306 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
2307 tmpTitle = XtNewString(tmpStr);
2308 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.");
2309 tmpMsg = XtNewString(tmpStr);
2311 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
2317 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
2322 /* Desensitize remove & restore buttons, until we're done */
2323 XtSetSensitive(*removeBtn, False);
2324 XtSetSensitive(*restoreBtn, False);
2326 MessageToFileList(msg, &file_list, &file_count);
2327 RestoreFromTrash(file_list, file_count, NULL, NULL, msg,False);
2332 /************************************************************************
2335 * This is the callback attached to the 'Restore' menu item. It will
2336 * remove from the trash all of the selected files, and will restore
2337 * them to their original location. If any problems occur while trying
2338 * to restore any of the files, then an error dialog will be posted.
2340 ************************************************************************/
2345 XtPointer client_data,
2346 XtPointer call_data )
2348 FileViewData *file_view_data;
2349 Boolean match = False;
2354 if (client_data != NULL)
2356 /* This was called by trash popup */
2357 /* need to check to see if it is selected, if it is restore all
2358 the selected files, else just restore this file */
2360 file_view_data = trashFileMgrData->popup_menu_icon;
2362 /* The object would have been deleted if the following condn is true */
2363 if(!file_view_data && !trashFileMgrData->selected_file_count)
2365 trashFileMgrData->popup_menu_icon = NULL;
2367 for (i = 0; i < trashFileMgrData->selected_file_count; i++)
2369 if (strcmp(file_view_data->file_data->file_name,
2370 trashFileMgrData->selection_list[i]->file_data->file_name) == 0)
2379 /* Desensitize remove & restore buttons, until we're done */
2380 XtSetSensitive(*removeBtn, False);
2381 XtSetSensitive(*restoreBtn, False);
2386 file_count = trashFileMgrData->selected_file_count;
2387 file_list = (char **)XtMalloc(file_count * sizeof(char *));
2388 for (i = 0; i < file_count; i++)
2390 file_view_data = trashFileMgrData->selection_list[file_count - 1 - i];
2391 file_list[i] = XtNewString(file_view_data->file_data->file_name);
2397 file_list = (char **)XtMalloc(sizeof(char *));
2398 file_list[0] = XtNewString(file_view_data->file_data->file_name);
2401 /* Start the background process that will do the restore */
2402 RestoreFromTrash(file_list, file_count, NULL, NULL, NULL,False);
2407 /************************************************************************
2410 * This is the callback attached to the 'Close' menu item. It will
2411 * unpost the trash can window.
2413 ************************************************************************/
2418 XtPointer client_data,
2419 XtPointer call_data )
2423 if (trashDialogPosted)
2425 XWithdrawWindow(XtDisplay(trashShell), XtWindow(trashShell),
2426 XDefaultScreen(XtDisplay(trashShell)));
2427 XtPopdown(trashShell);
2431 for (i = 0; i < secondaryTrashHelpDialogCount; i++)
2433 if (_DtIsDialogShowing(secondaryTrashHelpDialogList[i]))
2434 _DtHideDialog(secondaryTrashHelpDialogList[i], False);
2435 _DtFreeDialogData(secondaryTrashHelpDialogList[i]);
2437 XtFree((char *)secondaryTrashHelpDialogList);
2438 secondaryTrashHelpDialogList = NULL;
2439 secondaryTrashHelpDialogCount = 0;
2441 if (primaryTrashHelpDialog)
2443 if (_DtIsDialogShowing(primaryTrashHelpDialog))
2444 _DtHideDialog(primaryTrashHelpDialog, False);
2445 _DtFreeDialogData(primaryTrashHelpDialog);
2447 primaryTrashHelpDialog = NULL;
2449 if (PositionFlagSet(trashFileMgrData))
2450 SavePositionInfo(trashFileMgrData);
2453 FileMgrRec * file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
2455 if( (file_mgr_rec->menuStates & PREFERENCES) == 0 )
2456 file_mgr_rec->menuStates |= PREFERENCES;
2459 trashDialogPosted = False;
2464 /************************************************************************
2468 ************************************************************************/
2475 DtDndDropCallbackStruct *drop_parameters)
2478 DtActionArg * action_args;
2479 FileMgrRec * file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
2480 Position drop_x = drop_parameters->x;
2481 Position drop_y = drop_parameters->y;
2483 _DtBuildActionArgsWithDroppedFiles(NULL, drop_parameters,
2484 &action_args, &arg_count);
2486 DtActionInvoke(file_mgr_rec->shell, TRASH_ACTION,
2487 action_args, arg_count, NULL, NULL,
2488 trashFileMgrData->current_directory, True, NULL, NULL);
2490 _DtFreeActionArgs(action_args, arg_count);
2492 RepositionIcons(trashFileMgrData, file_set, file_count, drop_x,
2498 /************************************************************************
2502 ************************************************************************/
2506 FileMgrData *file_mgr_data,
2507 FileMgrRec *file_mgr_rec,
2521 * Get target host and directory
2523 target_dir = XtNewString(file_mgr_data->current_directory);
2524 target_host = XtNewString(file_mgr_data->host);
2527 * Create file list and call RestoreFromTrash
2529 file_list = (char **)XtMalloc(file_count * sizeof(char *));
2531 for (i = 0; i < file_count; i++)
2532 file_list[i] = XtNewString(file_set[i]);
2534 RestoreFromTrash(file_list, file_count, target_host, target_dir, NULL,False);
2539 /************************************************************************
2542 * Locate file in trash list based on new internal name. Return file name.
2544 ************************************************************************/
2550 Boolean IsTrash = False;
2552 if (strncmp(users_home_dir, filename, strlen(users_home_dir)) == 0)
2555 ((filename + strlen(users_home_dir)), TRASH_DIR, strlen(TRASH_DIR))
2565 /************************************************************************
2568 * Since the trash does not use shared menupanes, there is no work to
2569 * be done during the popup and popdown callbacks; therefore, we use
2570 * and empty function.
2572 ************************************************************************/
2577 XtPointer clientData,
2578 XtPointer callData )
2585 /*--------------------------------------------------------------------
2586 * UpdateDirectoryOf:
2587 * Arrange for the directory containing a file to be updated
2588 *------------------------------------------------------------------*/
2597 /* remove last component from path to get the directory */
2598 ptr = strrchr(path, '/');
2602 strcpy(host, home_host_name);
2604 /* now arrange for the directory to be updated */
2605 UpdateDirectory(NULL, host, path);
2607 /* restore the path */
2613 /*--------------------------------------------------------------------
2614 * EraseObject, EraseDir
2615 * Routines for recursively deleting files and directories
2616 *------------------------------------------------------------------*/
2619 EraseObject(char *file_name)
2621 struct stat stat_buf;
2623 if (lstat(file_name, &stat_buf) < 0)
2626 else if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
2627 return EraseDir(file_name);
2629 else if (remove(file_name) < 0)
2638 EraseDir(char *dir_name)
2640 DIR *dir; /* open directory */
2641 struct dirent *entry; /* directory entry */
2642 char srcname[MAX_PATH];
2646 /* open source directory */
2647 dir = opendir(dir_name);
2651 /* prepare source name */
2652 strcpy(srcname, dir_name);
2653 srclen = strlen(srcname);
2654 if (srcname[srclen - 1] != '/')
2655 srcname[srclen++] = '/';
2658 while (rc == 0 && (entry = readdir(dir)) != NULL)
2660 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
2662 strcpy(srcname + srclen, entry->d_name);
2663 rc = EraseObject(srcname);
2670 if (rmdir(dir_name) < 0)
2672 if (unlink(dir_name) < 0 )
2684 /*====================================================================
2686 * MoveToTrashProcess
2687 * Run a background process to move files to the trash can.
2689 *==================================================================*/
2691 /*--------------------------------------------------------------------
2692 * MoveToTrashProcess
2693 * Main routine of background process for MoveToTrash
2694 *------------------------------------------------------------------*/
2701 Boolean do_verify_checks)
2707 struct stat statInfo;
2709 struct dirent * entry;
2714 for (i = 0; i < file_count; i++)
2716 /* get base name and full path */
2717 path = XtNewString(file_list[i]);
2718 baseName = strrchr(file_list[i], '/');
2719 if (baseName == NULL || path == NULL)
2721 /* Invalid filename */
2723 pipe_msg = PIPEMSG_OTHER_ERROR;
2724 rc = BAD_FILE_ERROR;
2725 DPRINTF(("MoveToTrashProcess: sending BAD_FILE_ERROR\n"));
2726 write(pipe_fd, &pipe_msg, sizeof(short));
2727 write(pipe_fd, &rc, sizeof(int));
2730 if (path && MatchesSacredDirectory(path))
2732 /* Invalid filename */
2734 pipe_msg = PIPEMSG_OTHER_ERROR;
2735 rc = BAD_FILE_SACRED;
2736 DPRINTF(("MoveToTrashProcess: sending BAD_FILE_SACRED\n"));
2737 write(pipe_fd, &pipe_msg, sizeof(short));
2738 write(pipe_fd, &rc, sizeof(int));
2743 if (do_verify_checks)
2745 char *tmpstr = (file_list[i] == (baseName - 1))?"/":file_list[i];
2747 Boolean TrashError = False;
2749 /* check write permissions for the file */
2750 if (lstat(path,&s1) < 0)
2757 savechar = *(baseName-1);
2758 *(baseName-1) = '\0';
2759 if(CheckDeletePermission(tmpstr, path) != 0)
2761 /* No write access; display error message */
2763 if (S_ISDIR(s1.st_mode))
2764 rc = BAD_TRASH_DIRECTORY;
2766 rc = BAD_TRASH_FILE;
2769 *(baseName-1) = savechar;
2774 pipe_msg = PIPEMSG_OTHER_ERROR;
2775 DPRINTF(("MoveToTrashProcess: sending BAD_TRASH message\n"));
2776 write(pipe_fd, &pipe_msg, sizeof(short));
2777 write(pipe_fd, &rc, sizeof(int));
2780 else if (CheckAccess(path, W_OK) != 0 && !S_ISLNK(s1.st_mode))
2782 /* No write access; ask user for verification */
2784 pipe_msg = PIPEMSG_OTHER_ERROR;
2786 DPRINTF(("MoveToTrashProcess: sending VERIFY_FILE\n"));
2787 write(pipe_fd, &pipe_msg, sizeof(short));
2788 write(pipe_fd, &rc, sizeof(int));
2793 * If this is a directory, make sure it's empty, i.e.,
2794 * contains only ".", "..", and ".!" or ".~" files.
2796 if (lstat(path, &statInfo) == 0 &&
2797 (statInfo.st_mode & S_IFMT) == S_IFDIR &&
2798 (dirp = opendir(path)) != NULL)
2800 /* read the directory */
2801 while ((entry = readdir(dirp)) != NULL)
2803 if ( !(strcmp(entry->d_name, ".") == 0 ||
2804 strcmp(entry->d_name, "..") == 0 ||
2805 strncmp(entry->d_name, ".!", 2) == 0 ||
2806 strncmp(entry->d_name, ".~", 2) == 0) )
2808 /* found a real file: directory not empty */
2817 /* Directory is not empty */
2819 pipe_msg = PIPEMSG_OTHER_ERROR;
2821 DPRINTF(("MoveToTrashProcess: sending VERIFY_DIR\n"));
2822 write(pipe_fd, &pipe_msg, sizeof(short));
2823 write(pipe_fd, &rc, sizeof(int));
2827 } /* end if do_verify_checks */
2830 to = CreateTrashFilename(baseName, TRUE);
2832 /* move file to the trash directory */
2833 success = FileManip((Widget)pipe_fd, MOVE_FILE, path, to, TRUE,
2834 FileOpError, True, TRASH_DIRECTORY);
2837 pipe_msg = PIPEMSG_DONE;
2838 DPRINTF(("MoveToTrashProcess: sending DONE\n"));
2839 write(pipe_fd, &pipe_msg, sizeof(short));
2840 PipeWriteString(pipe_fd, path);
2841 PipeWriteString(pipe_fd, to);
2850 /*--------------------------------------------------------------------
2851 * MoveToTrashPipeCB:
2852 * Read and process data sent through the pipe.
2853 *------------------------------------------------------------------*/
2857 XtPointer client_data,
2861 MoveToTrashCBData *cb_data = (MoveToTrashCBData *)client_data;
2864 char *title, *err_msg, *err_arg;
2877 DesktopRec *desktopWin;
2878 char *dir_error=NULL,*file_error=NULL,*no_file_error=NULL,*sacred_error=NULL;
2880 /* read the next msg from the pipe */
2882 n = PipeRead(*fd, &pipe_msg, sizeof(short));
2883 DPRINTF(("MoveToTrashPipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
2887 case PIPEMSG_FILEOP_ERROR:
2888 PipeRead(*fd, &rc, sizeof(int));
2889 err_msg = PipeReadString(*fd);
2890 err_arg = PipeReadString(*fd);
2892 FileOperationError(toplevel, err_msg, err_arg);
2897 cb_data->rc[cb_data->done_count++] = BAD_FILE_ERROR;
2900 case PIPEMSG_OTHER_ERROR:
2901 PipeRead(*fd, &rc, sizeof(int));
2902 cb_data->rc[cb_data->done_count++] = rc;
2905 case PIPEMSG_TARGET_TIME:
2908 case PIPEMSG_FILE_MODIFIED:
2912 i = cb_data->done_count++;
2913 cb_data->path[i] = PipeReadString(*fd);
2914 cb_data->to[i] = PipeReadString(*fd);
2920 "Internal error in MoveToTrashPipeCB: bad pipe_msg %d\n",
2922 /* Don't know how it can get in here, but if it does, we'll try to
2923 * simulate an error condition. Without it, the SGI hung up
2925 while (cb_data->done_count < cb_data->file_count)
2926 cb_data->rc[cb_data->done_count++] = BAD_FILE_ERROR;
2931 /* if still more files to be processed, return now to wait for the rest */
2932 if (cb_data->done_count < cb_data->file_count)
2936 /* close the pipe and cancel the callback */
2937 DPRINTF(("MoveToTrashPipeCB: done\n"));
2941 /* process the results */
2953 for (i = 0; i < cb_data->file_count; i++)
2955 if (cb_data->rc[i]==BAD_TRASH_DIRECTORY || cb_data->rc[i]==BAD_TRASH_FILE)
2960 Boolean errflg = False;
2961 if(cb_data->rc[i] == BAD_TRASH_DIRECTORY)
2963 tmpmsg = GETMESSAGE(27, 113,
2964 "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");
2967 dir_error = (char *) XtRealloc(dir_error,strlen(dir_error)+
2968 strlen(cb_data->file_list[i])+5);
2969 strcat(dir_error,cb_data->file_list[i]);
2970 strcat(dir_error,"\n");
2974 dir_error = XtMalloc(strlen(tmpmsg)+
2975 strlen(cb_data->file_list[i])+5);
2976 sprintf(dir_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
2981 tmpmsg = GETMESSAGE(27, 114,
2982 "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");
2985 file_error = (char *) XtRealloc(file_error,strlen(file_error)
2986 + strlen(cb_data->file_list[i])+5);
2987 strcat(file_error,cb_data->file_list[i]);
2988 strcat(file_error,"\n");
2992 file_error = XtMalloc(strlen(tmpmsg)+
2993 strlen(cb_data->file_list[i])+5);
2994 sprintf(file_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
2997 XtFree(cb_data->file_list[i]);
2998 cb_data->file_list[i] = NULL;
3002 else if (cb_data->rc[i] == BAD_FILE_SACRED)
3006 char *tmpmsg = GETMESSAGE(27, 115,
3007 "The following object(s) cannot be deleted.\nThe desktop cannot function properly without these object(s).\n\n");
3010 sacred_error = (char *) XtRealloc(sacred_error,strlen(sacred_error)+
3011 strlen(cb_data->file_list[i])+5);
3012 strcat(sacred_error,cb_data->file_list[i]);
3013 strcat(sacred_error,"\n");
3017 sacred_error=XtMalloc(strlen(tmpmsg)+strlen(cb_data->file_list[i])+5);
3018 sprintf(sacred_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
3020 XtFree(cb_data->file_list[i]);
3021 cb_data->file_list[i] = NULL;
3025 else if (cb_data->rc[i] == BAD_FILE_ERROR)
3027 XtFree(cb_data->file_list[i]);
3028 cb_data->file_list[i] = NULL;
3030 else if (cb_data->rc[i] == NO_TRASH_FILE)
3033 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");
3039 no_file_error = (char *)XtRealloc(no_file_error,strlen(no_file_error)+
3040 strlen(cb_data->file_list[i])+5);
3041 strcat(no_file_error,cb_data->file_list[i]);
3042 strcat(no_file_error,"\n");
3046 no_file_error = XtMalloc(strlen(tmpmsg)+
3047 strlen(cb_data->file_list[i])+5);
3048 sprintf(no_file_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
3053 else if (cb_data->rc[i] == VERIFY_FILE ||
3054 cb_data->rc[i] == VERIFY_DIR)
3056 if (verifyCount < 6)
3057 AddString(&verifybuf, &verifybufsize, cb_data->file_list[i], NULL);
3061 * Keep track of whether we have directories only, files only,
3062 * or a combination, so that we can display the appropriate
3063 * label in the dialog.
3065 if (cb_data->rc[i] == VERIFY_FILE)
3071 * Add to array which is to be attached to the dialog;
3072 * the array will ultimately be NULL terminated.
3074 verifylist = (char **)XtRealloc((char *)verifylist,
3075 sizeof(char *) * (verifyCount + 1));
3076 verifylist[verifyCount - 1] = cb_data->file_list[i];
3080 /* Add file to trash list */
3081 if (numTrashItems >= trashListSize)
3083 trashListSize += 10;
3084 trashCan = (TrashEntry *)
3085 XtRealloc((char *)trashCan, sizeof(TrashEntry) * trashListSize);
3088 trashCan[numTrashItems].problem = False;
3089 trashCan[numTrashItems].intNew = cb_data->to[i];
3090 trashCan[numTrashItems].intOrig = cb_data->path[i];
3091 trashCan[numTrashItems].external = cb_data->file_list[i];
3093 /* extract base file name */
3094 baseName = strrchr(cb_data->to[i], '/');
3096 if (*baseName == '\0')
3098 trashCan[numTrashItems].filename = XtNewString(baseName);
3103 /* arrange for the source directory to be updated */
3104 UpdateDirectoryOf(cb_data->path[i]);
3107 * If the source file was referenced by a desktop object,
3108 * we need to remove the destkop object
3110 for (j = 0; j < desktop_data->numIconsUsed; j++)
3112 Tt_status tt_status;
3114 desktopWin = desktop_data->desktopWindows[j];
3115 fileName = ResolveLocalPathName( desktopWin->host,
3116 desktopWin->dir_linked_to,
3117 desktopWin->file_name,
3118 home_host_name, &tt_status);
3120 if( TT_OK == tt_status )
3122 if (strcmp(fileName, cb_data->path[i]) == 0)
3124 RemoveDT(desktopWin->shell, (XtPointer)desktopWin, NULL);
3126 RemoveMovedObjectFromDT(desktopWin->shell, (XtPointer)desktopWin,
3127 cb_data->file_count, cb_data->file_list);
3128 #endif /* SUN_PERF */
3137 buf = XtMalloc(strlen(dir_error)+3);
3138 sprintf(buf,"%s\n",dir_error);
3145 buf = XtMalloc(strlen(file_error)+3);
3146 sprintf(buf,"%s\n",file_error);
3150 buf = XtRealloc(buf,strlen(buf)+strlen(file_error)+3);
3151 sprintf(buf,"%s%s\n",buf,file_error);
3159 buf = XtMalloc(strlen(no_file_error)+3);
3160 sprintf(buf,"%s\n",no_file_error);
3164 buf = XtRealloc(buf,strlen(buf)+strlen(no_file_error)+3);
3165 sprintf(buf,"%s%s\n",buf,no_file_error);
3167 XtFree(no_file_error);
3173 buf = XtMalloc(strlen(sacred_error)+3);
3174 sprintf(buf,"%s\n",sacred_error);
3178 buf = XtRealloc(buf,strlen(buf)+strlen(sacred_error)+3);
3179 sprintf(buf,"%s%s\n",buf,sacred_error);
3181 XtFree(sacred_error);
3184 bufsize = strlen(buf);
3186 /* Update the trash information file, and the trash window */
3189 FILE * trashInfoFileId = fopen(TrashInfoFileName, "a+");
3190 if( trashInfoFileId != NULL )
3192 for (i = fileCount; i > 0; i--)
3194 if( WriteEntry(trashInfoFileId,
3195 trashCan[numTrashItems - i].external,
3196 trashCan[numTrashItems - i].filename) < 0 )
3199 fflush(trashInfoFileId);
3200 fclose(trashInfoFileId);
3201 if( trashFileMgrData )
3202 UpdateDirectory(NULL, trashFileMgrData->host,
3203 trashFileMgrData->current_directory);
3207 /* Check for any bad files; post an error dialog */
3210 /* send a reply to the message that triggered this operation, if any */
3212 if (cb_data->msg != 0) {
3214 Until Action is fixed.
3215 tttk_message_fail( cb_data->msg, TT_DESKTOP_EACCES, 0, 1 );
3217 tt_message_reply( cb_data->msg );
3218 tttk_message_destroy( cb_data->msg );
3220 if (cb_data->msg_cnt > 0) {
3221 for (i = 0 ; i < cb_data->msg_cnt ; i++) {
3222 tt_message_reply( cb_data->msg_list[i] ) ;
3223 tttk_message_destroy( cb_data->msg_list[i] ) ;
3225 XtFree((char *)cb_data->msg_list) ;
3226 cb_data->msg_cnt = 0 ;
3227 #endif /* SUN_PERF */
3230 /* If more items than can be displayed, let user know */
3233 char extraFiles[256];
3235 (void) sprintf(extraFiles, AdditionalHeader, badCount - 8);
3239 GETMESSAGE(27,97, "The following objects could not be placed in the trash can: \n"));
3242 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3243 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3245 XtFree ((char *) buf);
3247 /* send a reply to the message that triggered this operation, if any */
3249 if (cb_data->msg != 0) {
3250 tt_message_reply( cb_data->msg );
3251 tttk_message_destroy( cb_data->msg );
3253 if (cb_data->msg_cnt > 0) {
3254 for (i = 0 ; i < cb_data->msg_cnt ; i++) {
3255 tt_message_reply( cb_data->msg_list[i] ) ;
3256 tttk_message_destroy( cb_data->msg_list[i] ) ;
3258 XtFree((char *)cb_data->msg_list) ;
3259 cb_data->msg_cnt = 0 ;
3260 #endif /* SUN_PERF */
3265 /* Check for any files requiring user verification; post a prompt dialog */
3266 /* XXX Really should fail incoming ToolTalk request if user cancels any */
3274 /* If more items than can be displayed, let user know */
3275 if (verifyCount > 6)
3277 char extraFiles[256];
3279 (void) sprintf(extraFiles, AdditionalHeader, verifyCount - 6);
3280 AddString(&verifybuf, &verifybufsize, extraFiles, NULL);
3284 * Depending upon what type of files are to be displayed in the
3285 * dialog, choose the appropriate dialog text.
3287 if (vfiles && vdirs)
3288 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");
3290 header = GETMESSAGE(27,104, "You do not have write permission for the following file(s):\nDo you want to proceed?\n");
3292 header = GETMESSAGE(27,100, "The following folder(s) are not empty.\nDo you want to proceed?\n");
3294 tmpbuf = XtMalloc(strlen(header) + strlen(verifybuf) + 1);
3295 sprintf(tmpbuf, "%s%s", header, verifybuf);
3296 title = XtNewString(GETMESSAGE(27, 4, "Trash Can Warning"));
3297 dlog = (Widget)_DtMessageDialog(toplevel, title, tmpbuf, NULL, True,
3298 VerifyCancel, VerifyOk, NULL, HelpRequestCB,
3299 False, WARNING_DIALOG);
3301 XtFree ((char *) verifybuf);
3305 * Add array as userdata on the dialog.
3306 * NULL terminate the array.
3308 verifylist[verifyCount] = NULL;
3309 XtSetArg(args[0], XmNuserData, verifylist);
3310 XtSetValues(dlog, args, 1);
3313 /* free the callback data */
3314 XtFree((char *)cb_data->file_list);
3315 XtFree((char *)cb_data->path);
3316 XtFree((char *)cb_data->to);
3317 XtFree((char *)cb_data->rc);
3318 XtFree((char *)cb_data);
3324 /*--------------------------------------------------------------------
3326 * Start the background process and set up callback for the pipe.
3327 *------------------------------------------------------------------*/
3333 Boolean do_verify_checks,
3336 static char *pname = "MoveToTrash";
3337 MoveToTrashCBData *cb_data;
3341 if( !TrashInitialized )
3344 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
3349 /* set up callback data */
3350 cb_data = XtNew(MoveToTrashCBData);
3351 cb_data->file_list = file_list;
3352 cb_data->file_count = file_count;
3353 cb_data->done_count = 0;
3354 cb_data->path = (char **)XtCalloc(file_count, sizeof(char *));
3355 cb_data->to = (char **)XtCalloc(file_count, sizeof(char *));
3356 cb_data->rc = (int *)XtCalloc(file_count, sizeof(int));
3359 cb_data->msg_cnt = 0 ;
3360 if (global_msg_cnt > 0) {
3361 cb_data->msg_cnt = global_msg_cnt ;
3362 cb_data->msg_list = (Tt_message *)XtMalloc(sizeof(Tt_message) * cb_data->msg_cnt);
3363 memcpy((char *)cb_data->msg_list,(char *)global_msg_list, sizeof(Tt_message) *
3366 #endif /* SUN_PERF */
3371 /* fork the process that does the actual work */
3376 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
3377 pname, getppid(), getpid(), errno, strerror(errno));
3384 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
3386 close(pipe_fd[0]); /* child won't read from the pipe */
3388 MoveToTrashProcess(pipe_fd[1], file_list, file_count, do_verify_checks);
3391 DBGFORK(("%s: child exiting\n", pname));
3396 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
3398 /* parent: set up callback to get the pipe data */
3399 close(pipe_fd[1]); /* parent won't write the pipe */
3401 cb_data->child = pid;
3403 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
3404 pipe_fd[0], (XtPointer)XtInputReadMask,
3405 MoveToTrashPipeCB, (XtPointer)cb_data);
3409 /*====================================================================
3413 * Run a background process to restore files from the trash can.
3415 * These routines are used both for "normal restores", i.e.,
3416 * restores initiated from the Trash menu or through an ICCCM
3417 * message, as well as "drag&drop restores", i.e., restores done
3418 * by dragging files from the trash can to some other directory.
3419 * For normal resores, file_list contains only simple file names
3420 * (no paths) and target_dir is NULL. For drag&drop restores
3421 * file_list contains complete path names and target_dir contains
3422 * the name of the directoy to which the files should be moved.
3424 *==================================================================*/
3426 /*--------------------------------------------------------------------
3428 * Main routine of background process for RestoreFromTrash
3429 *------------------------------------------------------------------*/
3439 Boolean CheckedAlready)
3448 char **RestoreList= NULL;
3450 /* get full path name of target directory */
3453 Tt_status tt_status;
3454 full_dirname = ResolveLocalPathName(target_host, target_dir,
3455 NULL, home_host_name, &tt_status);
3456 if( TT_OK != tt_status )
3458 /* send return codes back trough the pipe */
3459 pipe_msg = PIPEMSG_DONE;
3461 DPRINTF(("RestoreProcess: Unable to Resolve local path name\n"));
3462 write(pipe_fd, &pipe_msg, sizeof(short));
3463 write(pipe_fd, rc, sizeof(int));
3468 full_dirname = NULL;
3470 /* restore the files */
3471 for (i = 0; i < file_count; i++)
3473 /* Locate file in trash list */
3474 for (j = 0; j < numTrashItems; j++)
3476 /* file_list[i] may be a complete path or just a file name */
3477 if (strcmp(file_list[i], trashCan[j].filename) == 0 ||
3478 strcmp(file_list[i], trashCan[j].intNew) == 0)
3484 /* determine source and target for the move */
3485 if (target_dir == NULL)
3487 /* this is a normal restore */
3488 if (j < numTrashItems)
3490 from = trashCan[j].intNew;
3491 to = trashCan[j].intOrig;
3495 /* can't do a restore if the file wasn't found in the trash list */
3501 /* this is a drag&drop from the trash can to target_dir */
3502 from = file_list[i];
3503 if (j < numTrashItems)
3504 ptr = strrchr(trashCan[j].intOrig, '/');
3506 ptr = strrchr(file_list[i], '/');
3507 strcpy(buf, full_dirname);
3515 status = RestoreObject((Widget)pipe_fd, MOVE_FILE, from,to,
3516 TRUE, FileOpError, False, NOT_DESKTOP,CheckedAlready);
3517 /* restore was successful */
3518 if(status == (int) True)
3520 else if(status == (int) False)
3526 /* restore failed */
3530 /* send return codes back trough the pipe */
3531 pipe_msg = PIPEMSG_DONE;
3532 DPRINTF(("RestoreProcess: sending DONE\n"));
3533 write(pipe_fd, &pipe_msg, sizeof(short));
3534 write(pipe_fd, rc, file_count * sizeof(int));
3536 XtFree(full_dirname);
3540 /*--------------------------------------------------------------------
3542 * Read and process data sent through the pipe.
3543 *------------------------------------------------------------------*/
3547 XtPointer client_data,
3551 RestoreFromTrashCBData *cb_data = (RestoreFromTrashCBData *)client_data;
3554 char *title, *err_msg, *err_arg;
3558 char **ToRestoreList=NULL;
3559 char **FromRestoreList=NULL;
3560 char *target_host,*target_dir;
3563 /* read the next msg from the pipe */
3565 n = PipeRead(*fd, &pipe_msg, sizeof(short));
3566 DPRINTF(("RestorePipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
3570 case PIPEMSG_FILEOP_ERROR:
3571 PipeRead(*fd, &rc, sizeof(int));
3572 err_msg = PipeReadString(*fd);
3573 err_arg = PipeReadString(*fd);
3574 FileOperationError(toplevel, err_msg, err_arg);
3575 /* This call will popup an error dialog.
3577 FileOperationError(toplevel, err_msg, err_arg);
3579 It's not appropriate at all to popup an error dialog here.
3580 i.e. if there're 1000 files, and the file system is full,
3581 and we're unable to move, would we want to popup 1000
3588 PipeRead(*fd, cb_data->rc, cb_data->file_count * sizeof(int));
3593 "Internal error in RestorePipeCB: bad pipe_msg %d\n",
3597 /* close the pipe and cancel the callback */
3598 DPRINTF(("RestorePipeCB: done\n"));
3605 for (i = 0; i < cb_data->file_count; i++)
3607 /* Locate file in trash list */
3608 for (j = 0; j < numTrashItems; j++)
3610 /* file_list[i] may be a complete path or just a file name */
3611 if (strcmp(cb_data->file_list[i], trashCan[j].filename) == 0 ||
3612 strcmp(cb_data->file_list[i], trashCan[j].intNew) == 0)
3618 if (cb_data->rc[i] == SKIP_FILE)
3620 ToRestoreList = (char **) XtRealloc((char *)ToRestoreList,sizeof(char *) *
3622 ToRestoreList[RestoreIndex-1] = XtNewString(trashCan[j].intOrig);
3623 FromRestoreList = (char **) XtRealloc((char *)FromRestoreList,sizeof(char *) *
3625 FromRestoreList[RestoreIndex-1] = XtNewString( trashCan[j].intNew );
3628 /* Check the return code from the restore */
3629 else if (cb_data->rc[i] == 0)
3631 /* restore was successful: remove the file from the trash list */
3632 if (j < numTrashItems)
3634 /* arrange for the source directory to be updated */
3635 UpdateDirectoryOf(trashCan[j].intOrig);
3637 /* Remove this entry from the trash list */
3638 XtFree ((char *) trashCan[j].intNew);
3639 XtFree ((char *) trashCan[j].intOrig);
3640 XtFree ((char *) trashCan[j].external);
3641 XtFree ((char *) trashCan[j].filename);
3642 for (k = j; k < (numTrashItems - 1); k++)
3643 trashCan[k] = trashCan[k + 1];
3650 char *restore_header,*tmpStr = GETMESSAGE(27,101,
3651 "The following object(s) could not be put back:\n");
3653 restore_header = XtNewString(tmpStr);
3654 if (j < numTrashItems && cb_data->target_dir == NULL)
3655 AddString(&buf, &bufsize, trashCan[j].external, restore_header);
3657 AddString(&buf, &bufsize, cb_data->file_list[i], restore_header);
3658 XtFree(restore_header);
3662 /* Update the trash information file */
3663 if( ! WriteTrashEntries() )
3665 char * tmpStr, * title;
3667 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3668 tmpStr = XtNewString(GETMESSAGE(27, 88, "Cannot write to a temporary file.\nPerhaps your file system is full.\n"));
3669 _DtMessage(toplevel, title, tmpStr, NULL, HelpRequestCB);
3674 /* send a reply to the message that triggered this operation, if any */
3675 if (cb_data->msg != 0) {
3676 tt_message_reply( cb_data->msg );
3677 tttk_message_destroy( cb_data->msg );
3681 /* Report any errors */
3684 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3685 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3687 XtFree ((char *) buf);
3690 UpdateDirectory(NULL, trashFileMgrData->host,
3691 trashFileMgrData->current_directory);
3692 if (cb_data->target_dir)
3693 UpdateDirectory(NULL, cb_data->target_host, cb_data->target_dir);
3695 target_host = XtNewString(cb_data->target_host);
3696 target_dir = XtNewString(cb_data->target_dir);
3698 /* free the callback data */
3699 for (i = 0; i < cb_data->file_count; i++)
3700 XtFree(cb_data->file_list[i]);
3701 XtFree((char *)cb_data->file_list);
3702 XtFree((char *)cb_data->target_host);
3703 XtFree((char *)cb_data->target_dir);
3704 XtFree((char *)cb_data->rc);
3705 XtFree((char *)cb_data);
3708 for(i=0;i<RestoreIndex;i++)
3710 CreateRestoreDialog(FromRestoreList[i],ToRestoreList[i]);
3711 XtFree(FromRestoreList[i]);
3712 XtFree(ToRestoreList[i]);
3714 XtFree((char *) FromRestoreList);
3715 XtFree((char *) ToRestoreList);
3717 XtFree(target_host);
3722 /*--------------------------------------------------------------------
3724 * Start the background process and set up callback for the pipe.
3725 *------------------------------------------------------------------*/
3734 Boolean CheckedAlready)
3736 static char *pname = "RestoreFromTrash";
3737 RestoreFromTrashCBData *cb_data;
3741 if( !TrashInitialized )
3744 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
3749 /* set up callback data */
3750 cb_data = XtNew(RestoreFromTrashCBData);
3751 cb_data->file_list = file_list;
3752 cb_data->file_count = file_count;
3753 cb_data->target_host = target_host;
3754 cb_data->target_dir = target_dir;
3755 cb_data->rc = (int *)XtCalloc(file_count, sizeof(int));
3761 /* fork the process that does the actual work */
3766 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
3767 pname, getppid(), getpid(), errno, strerror(errno));
3774 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
3776 close(pipe_fd[0]); /* child won't read from the pipe */
3778 RestoreProcess(pipe_fd[1], file_list, file_count,
3779 target_host, target_dir, cb_data->rc,CheckedAlready);
3782 DBGFORK(("%s: child exiting\n", pname));
3787 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
3789 /* parent: set up callback to get the pipe data */
3790 close(pipe_fd[1]); /* parent won't write the pipe */
3792 cb_data->child = pid;
3794 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
3795 pipe_fd[0], (XtPointer)XtInputReadMask,
3796 RestorePipeCB, (XtPointer)cb_data);
3801 /*====================================================================
3804 * Run a background process to remove files from the trash can.
3806 *==================================================================*/
3808 /*--------------------------------------------------------------------
3810 * Main routine of background process for EmptyTrash
3811 *------------------------------------------------------------------*/
3816 DeleteList * del_list,
3824 * Delete all files or directories in the list,
3825 * as well as any associated annotations.
3827 for (i = 0; i < del_count; i++)
3829 /* delete the file */
3830 rc[i] = EraseObject(del_list[i].trash);
3833 /* send return codes back trough the pipe */
3834 pipe_msg = PIPEMSG_DONE;
3835 DPRINTF(("EmptyTrashProcess: sending DONE\n"));
3836 write(pipe_fd, &pipe_msg, sizeof(short));
3837 write(pipe_fd, rc, del_count * sizeof(int));
3841 /*--------------------------------------------------------------------
3843 * Read and process data sent through the pipe.
3844 *------------------------------------------------------------------*/
3848 XtPointer client_data,
3852 EmptyTrashCBData *cb_data = (EmptyTrashCBData *)client_data;
3855 char *title, *err_msg, *err_arg;
3856 int problemCount, itemCount;
3861 /* read the next msg from the pipe */
3863 n = PipeRead(*fd, &pipe_msg, sizeof(short));
3864 DPRINTF(("EmptyTrashPipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
3868 case PIPEMSG_FILEOP_ERROR:
3869 PipeRead(*fd, &rc, sizeof(int));
3870 err_msg = PipeReadString(*fd);
3871 err_arg = PipeReadString(*fd);
3872 /* This call will popup an error dialog.
3874 FileOperationError(toplevel, err_msg, err_arg);
3876 It's not appropriate at all to popup an error dialog here.
3877 i.e. if there're 1000 files, and the file system is full,
3878 and we're unable to move, would we want to popup 1000
3886 PipeRead(*fd, cb_data->rc, cb_data->del_count * sizeof(int));
3891 "Internal error in EmptyTrashPipeCB: bad pipe_msg %d\n",
3895 /* close the pipe and cancel the callback */
3896 DPRINTF(("EmptyTrashPipeCB: done\n"));
3905 if (cb_data->removeType == TRASH_FILE)
3907 for (i = 0; i < cb_data->del_count; i++)
3909 /* Locate file in trash list */
3910 for (j = 0; j < numTrashItems; j++)
3911 if (strcmp(cb_data->del_list[i].trash, trashCan[j].intNew) == 0)
3914 /* Check the return code from the erase */
3915 if (cb_data->rc[i] == 0)
3917 /* erase was successful: remove the file from the trash list */
3918 if (j < numTrashItems)
3920 /* Remove this entry from the trash list */
3921 XtFree ((char *) trashCan[j].intNew);
3922 XtFree ((char *) trashCan[j].intOrig);
3923 XtFree ((char *) trashCan[j].external);
3924 XtFree ((char *) trashCan[j].filename);
3925 for (k = j; k < (numTrashItems - 1); k++)
3926 trashCan[k] = trashCan[k + 1];
3937 char * msg = XtNewString(GETMESSAGE(27,96, "The following objects could not be removed from the file system: \n"));
3939 if (j < numTrashItems)
3940 file_name = trashCan[j].external;
3942 file_name = cb_data->del_list[i].trash;
3943 AddString(&buf, &bufsize, file_name, msg);
3950 /* Update the trash information file */
3951 if( ! WriteTrashEntries() )
3953 char * tmpStr, * title;
3955 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3956 tmpStr = XtNewString(GETMESSAGE(27, 88, "Cannot write to a temporary file.\nPerhaps your file system is full.\n"));
3957 _DtMessage(toplevel, title, tmpStr, NULL, HelpRequestCB);
3962 /* Report any errors */
3965 /* If more items than can be displayed, let user know */
3966 if (itemCount < problemCount)
3968 char extraFiles[256];
3969 sprintf(extraFiles, AdditionalHeader, problemCount - itemCount);
3973 GETMESSAGE(27,97, "The following objects could not be placed in the trash can: \n") );
3976 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3977 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3979 XtFree ((char *) buf);
3982 if (trashFileMgrData->selected_file_count > 0)
3984 XtSetSensitive(*removeBtn, True);
3985 XtSetSensitive(*restoreBtn, True);
3988 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
3989 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
3991 UpdateDirectory(NULL, trashFileMgrData->host,
3992 trashFileMgrData->current_directory);
3996 if (cb_data->rc[0] == 0)
3998 /* arrange for the directory containing the file to be updated */
3999 UpdateDirectoryOf(cb_data->del_list[0].trash);
4003 /* put up an error message saying the file couldn't be removed */
4007 tmpStr = GETMESSAGE(27, 81, "Shred File/Folder Error");
4008 title = XtNewString(tmpStr);
4009 tmpStr = GETMESSAGE(27, 82, " The following file could not be removed from the file system: \n");
4010 file_name = cb_data->del_list[0].trash;
4011 buffer = XtMalloc(strlen(tmpStr) + strlen(file_name) + 1);
4012 sprintf(buffer, "%s%s\n", tmpStr, file_name);
4013 _DtMessage(toplevel, title, buffer, NULL, HelpRequestCB);
4020 /* reset removingTrash flag */
4021 removingTrash = False;
4023 /* send a reply to the message that triggered this operation, if any */
4024 if (cb_data->msg != 0) {
4025 tt_message_reply( cb_data->msg );
4026 tttk_message_destroy( cb_data->msg );
4030 /* free the callback data */
4031 for (i = 0; i < cb_data->del_count; i++)
4033 XtFree(cb_data->del_list[i].trash);
4034 XtFree(cb_data->del_list[i].orig);
4036 XtFree((char *)cb_data->del_list);
4037 XtFree((char *)cb_data->rc);
4038 XtFree((char *)cb_data);
4042 /*--------------------------------------------------------------------
4044 * Start the background process and set up callback for the pipe.
4045 *------------------------------------------------------------------*/
4049 DeleteList *del_list,
4054 static char *pname = "EmptyTrash";
4055 EmptyTrashCBData *cb_data;
4059 if( !TrashInitialized )
4062 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
4067 /* set removingTrash flag */
4068 removingTrash = True;
4070 /* set up callback data */
4071 cb_data = XtNew(EmptyTrashCBData);
4072 cb_data->del_list = del_list;
4073 cb_data->del_count = del_count;
4074 cb_data->removeType = removeType;
4075 cb_data->rc = (int *)XtCalloc(del_count, sizeof(int));
4081 /* fork the process that does the actual work */
4086 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
4087 pname, getppid(), getpid(), errno, strerror(errno));
4094 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
4096 close(pipe_fd[0]); /* child won't read from the pipe */
4098 EmptyTrashProcess(pipe_fd[1], del_list, del_count, cb_data->rc);
4101 DBGFORK(("%s: child exiting\n", pname));
4106 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
4108 /* parent: set up callback to get the pipe data */
4109 close(pipe_fd[1]); /* parent won't write the pipe */
4111 cb_data->child = pid;
4113 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
4114 pipe_fd[0], (XtPointer)XtInputReadMask,
4115 EmptyTrashPipeCB, (XtPointer)cb_data);
4119 CheckDeletePermission(
4121 char *destinationPath)
4123 #if defined(CSRG_BASED)
4124 struct statfs statbuf;
4126 struct stat statbuf;
4130 #if defined(CSRG_BASED)
4131 if (statfs(parentdir,&statbuf) < 0) /* does not exist */
4133 if (lstat(parentdir,&statbuf) < 0) /* does not exist */
4137 /* check if we are root */
4140 /* if NFS, need to check if server trusts root */
4141 #if defined(CSRG_BASED)
4142 if (!strcmp(statbuf.f_fstypename, "nfs")) /* Root user and nfs */
4144 if (FileSysType(statbuf.st_dev) < 0) /* Root user and nfs */
4148 tmpfile = tempnam(parentdir,"quang");
4149 if (creat(tmpfile,O_RDONLY)< 0) /* Create a temporary file */
4151 if (remove(tmpfile) < 0) /* Delete the created file */
4155 /* root user can delete anything */
4159 /* check for write and execute permisssion on parent dir */
4160 if (CheckAccess(parentdir, W_OK | X_OK) < 0)
4163 /* copy destinationPath to tmp buffer */
4164 strcpy(fname, destinationPath);
4166 return CheckDeletePermissionRecur(fname);
4171 CheckDeletePermissionRecur(
4172 char *destinationPath)
4174 struct stat statbuf;
4180 DPRINTF(("CheckDeletePermissionRecur(\"%s\")\n", destinationPath));
4182 if (lstat(destinationPath, &statbuf) < 0)
4183 return -1; /* probably does not exist */
4185 if (! S_ISDIR(statbuf.st_mode))
4186 return 0; /* no need to check anything more */
4188 dirp = opendir (destinationPath);
4190 return -1; /* could not read directory */
4195 while (dp = readdir (dirp))
4197 if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
4201 /* check for write permission in this directory */
4202 if (CheckAccess(destinationPath, W_OK|X_OK) < 0)
4205 /* append a '/' to the end of directory name */
4206 fnamep = destinationPath + strlen(destinationPath);
4212 /* append file name to end of directory name */
4213 strcpy(fnamep, dp->d_name);
4215 /* recursively check permission on this file */
4216 if (CheckDeletePermissionRecur(destinationPath))
4224 #if !defined(CSRG_BASED)
4230 if(ustat(dev,&u1) < 0)
4240 register char *source,
4241 register char *target,
4242 Boolean isContainer,
4243 void (*errorHandler)(),
4244 Boolean checkForBusyDir,
4246 Boolean CheckedAlready)
4248 struct stat statsrc,stattar;
4250 char *localdir,*chrptr;
4254 if(stat(source,&statsrc) < 0)
4255 if(lstat(source,&statsrc) < 0)
4258 localdir = strdup(target);
4260 chrptr = strrchr(localdir,'/');
4263 if(chrptr == localdir) /* Must be root folder */
4268 if(stat(target,&stattar) >= 0) /* Target exists */
4270 if(CheckDeletePermission(localdir,target))
4271 return ((int)False);
4276 return ((int )FileManip((Widget)w, MOVE_FILE, source, target, TRUE,
4277 FileOpError, False, NOT_DESKTOP));
4280 CreateRestoreDialog(
4284 char *tmpbuf,*title;
4288 char **dirs = (char **) malloc(sizeof(char *) * 2);
4289 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.");
4291 dirs[0] = strdup(source);
4292 dirs[1] = strdup(target);
4293 tmpbuf = XtMalloc(strlen(header) + strlen(target) + 1);
4294 sprintf(tmpbuf, header, target);
4295 title = XtNewString(GETMESSAGE(27, 109, "Put Back Warning"));
4296 dw = (Widget)_DtMessageDialog(toplevel, title,tmpbuf,NULL,True,RestoreVerifyCancel,
4297 RestoreVerifyOk, NULL, HelpRequestCB, False, WARNING_DIALOG);
4299 XtSetArg(args[n], XmNuserData,dirs); n++;
4300 XtSetValues(dw,args,n);
4308 XtPointer client_data,
4309 XtPointer call_data )
4314 Widget mbox = (Widget)client_data;
4316 XtSetArg(args[n],XmNuserData,&dirs); n++;
4317 XtGetValues(mbox,args,n);
4319 if(DirectoryBusy(dirs[1]))
4322 char * tmpStr,*title;
4324 title = XtNewString(GETMESSAGE(27,111,"Put Back Error"));
4325 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.");
4326 msgbuf = XtMalloc(strlen(tmpStr) + strlen(dirs[1])+1);
4327 sprintf (msgbuf, tmpStr, dirs[1]);
4328 _DtMessage(toplevel,title,msgbuf,NULL,HelpRequestCB);
4336 char *realTarget,*tptr;
4340 if(lstat(dirs[1],&s1) < 0)
4342 fsErase(dirs[1],&status,0);
4343 FileList = (char **) XtMalloc(sizeof(char *));
4344 FileList[0] = XtNewString(dirs[0]);
4345 RestoreFromTrash(FileList, (int) 1, NULL, NULL, NULL,True);
4351 XtDestroyWidget(mbox);
4355 RestoreVerifyCancel(
4357 XtPointer client_data,
4358 XtPointer call_data )
4364 Widget mbox = (Widget)client_data;
4366 XtSetArg(args[n],XmNuserData,&dirs); n++;
4367 XtGetValues(mbox,args,n);
4371 XtDestroyWidget(mbox);