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>
138 #include "SharedProcs.h"
139 #include "MultiView.h"
144 #include "SharedMsgs.h"
146 #define AdditionalHeader (GETMESSAGE(27,98, "(Plus %d additional object(s))"))
148 /* Trash file errors */
149 #define NO_FILE_ERROR 0
150 #define BAD_FILE_ERROR -1
151 #define VERIFY_DIR -2
152 #define VERIFY_FILE -3
153 #define BAD_TRASH_DIRECTORY -4
154 #define BAD_TRASH_FILE -5
155 #define NO_TRASH_FILE -6
158 #define BAD_FILE_SACRED -9
160 /* types of messages sent through the pipe */
161 #define PIPEMSG_FILEOP_ERROR 1
162 #define PIPEMSG_OTHER_ERROR 3
163 #define PIPEMSG_DONE 7
164 #define PIPEMSG_TARGET_TIME 5
165 #define PIPEMSG_FILE_MODIFIED 6
168 * Structure describing each file in the trash can.
169 * Includes the external host:/name format, the original internal
170 * /nfs/host/name format, and the internal /nfs/host/new_name describing
171 * the temporary location of the file. The `problem' flag is used to
172 * indicate if a problem occurred when the file was physically being
184 /* callback data MoveToTrash */
195 Tt_message *msg_list ;
197 #endif /* SUN_PERF */
204 Tt_message *msg_list ;
207 #endif /* SUN_PERF */
209 /* callback data RestoreFromTrash */
219 } RestoreFromTrashCBData;
221 /* callback data EmptyTrash */
230 DeleteList *del_list;
239 DialogData *trashDialogData; /* fm data associated with trash dialog */
240 FileMgrData *trashFileMgrData = NULL;
242 Boolean removingTrash = False;
244 DialogData * primaryTrashHelpDialog = NULL;
245 DialogData ** secondaryTrashHelpDialogList = NULL;
246 int secondaryTrashHelpDialogCount = 0;
247 /* 'defines' for trash files */
248 static char * TRASH_DIR = ".dt/Trash";
249 static char * TRASH_INFO_FILE = ".dt/Trash/.trashinfo";
250 static char * NEW_TRASH_INFO_FILE = ".dt/.tmptrashinfo";
252 static char * RM = "/bin/rm";
253 static char * RM_ARGS = "-rf";
255 static Widget * selectAllBtn = NULL;
256 static Widget * restoreBtn = NULL;
257 static Widget * removeBtn = NULL;
259 static int trashMenuItemCount = 22; /* trash menu items */
260 static MenuDesc * trashMenu = NULL;
262 static TrashEntry * trashCan = NULL;
263 static int trashListSize = 0;
264 static int numTrashItems = 0;
266 static Boolean trashDialogPosted;
268 static char * TrashInfoFileName = NULL;
269 static char * NewTrashInfoFileName = NULL;
271 static char ** sacred_dir_list = NULL; /* list of directories in trash path */
272 static int sacred_dir_list_size = 0;
273 static int sacred_dir_count = 0;
275 static Boolean verifyPromptsEnabled; /* do we prompt the user? */
277 static Tt_message global;
279 static Tt_message *global_msg_list ;
280 static int global_msg_cnt = 0 ;
281 #endif /* SUN_PERF */
282 static Boolean TrashInitialized = False;
284 /******** Static Function Declarations ********/
286 static String CreateTrashFilename(
289 static void MessageToFileList(
294 static void MessagesToFileList(
295 Tt_message *msg_list,
299 #endif /* SUN_PERF */
300 static int WriteEntry(
304 static Boolean MatchesSacredDirectory(
306 static void VerifyCleanup(
308 Boolean completeDelete) ;
309 static void VerifyCancel(
311 XtPointer client_data,
312 XtPointer call_data) ;
313 static void VerifyOk(
315 XtPointer client_data,
316 XtPointer call_data) ;
317 static Boolean WriteTrashEntries( void ) ;
318 static void Select_All(
320 XtPointer client_data,
321 XtPointer call_data) ;
322 static void Unselect_All(
324 XtPointer client_data,
325 XtPointer call_data) ;
328 XtPointer client_data,
329 XtPointer call_data) ;
330 static String GetBasePath(
332 static Boolean ReadTrashList( void ) ;
333 static void RemoveOkCB(
335 XtPointer client_data,
336 XtPointer call_data ) ;
337 static void RemoveCancelCB(
339 XtPointer client_data,
340 XtPointer call_data ) ;
341 static void ConfirmOk(
343 XtPointer client_data,
344 XtPointer call_data ) ;
345 static void ConfirmCancel(
347 XtPointer client_data,
348 XtPointer call_data ) ;
351 XtPointer clientData,
352 XtPointer callData) ;
353 static void AddToDeleteList(
354 DeleteList *deleteList,
357 static void MoveToTrash(
360 Boolean do_verify_checks,
362 static void RestoreFromTrash(
368 Boolean CheckedAlready) ;
369 static void EmptyTrash(
370 DeleteList *del_list,
374 static int CheckDeletePermissionRecur(
376 static int FileSysType(int dev);
377 static void RestoreVerifyOk(
379 XtPointer client_data,
380 XtPointer call_data ) ;
381 static void RestoreVerifyCancel(
383 XtPointer client_data,
384 XtPointer call_data ) ;
385 static int RestoreObject(
388 register char *source,
389 register char *target,
391 void (*errorHandler)(),
392 Boolean checkForBusyDir,
394 Boolean CheckedAlready);
395 static void CreateRestoreDialog(
399 /******** End Static Function Declarations ********/
402 TrashIsInitialized( void )
404 return( TrashInitialized );
408 InitializeTrash( Boolean enableVerifyPrompt )
411 struct stat statInfo;
413 /* Build the 'TRASH' directory */
414 trash_dir = XtMalloc(strlen(users_home_dir) +
417 sprintf(trash_dir, "%s%s", users_home_dir, TRASH_DIR);
418 if (stat(trash_dir, &statInfo) < 0)
419 mkdir(trash_dir, S_IRUSR | S_IWUSR | S_IXUSR |
420 S_IRGRP | S_IWGRP | S_IXGRP |
421 S_IROTH | S_IWOTH | S_IXOTH);
423 /* build path to .trashinfo file */
424 TrashInfoFileName = XtMalloc(strlen(users_home_dir) + strlen(TRASH_INFO_FILE) + 1);
425 sprintf(TrashInfoFileName, "%s%s", users_home_dir, TRASH_INFO_FILE);
428 NewTrashInfoFileName = XtMalloc(strlen(users_home_dir) +
429 strlen(NEW_TRASH_INFO_FILE)
431 sprintf(NewTrashInfoFileName, "%s%s", users_home_dir, NEW_TRASH_INFO_FILE);
433 /* Keep track of whether to prompt for user verification */
434 verifyPromptsEnabled = enableVerifyPrompt;
437 * Create an array of paths and filenames which we will not allow the
438 * user to delete, because deleting any of them will cause trash to
441 sacred_dir_list_size = 5;
442 sacred_dir_list = (char **)XtMalloc(sizeof(char *) * sacred_dir_list_size);
444 sacred_dir_list[sacred_dir_count++] = XtNewString("/");
445 sacred_dir_list[sacred_dir_count++] = XtNewString(TrashInfoFileName);
447 ptr = TrashInfoFileName + 1;
448 while(ptr = DtStrchr(ptr, '/'))
450 /* All parent components of the user's home dir cannot be deleted */
452 if (sacred_dir_count >= sacred_dir_list_size)
455 sacred_dir_list_size += 5;
456 sacred_dir_list = (char **)XtRealloc((char *)sacred_dir_list,
457 sizeof(char *) * sacred_dir_list_size);
459 sacred_dir_list[sacred_dir_count++] = XtNewString(TrashInfoFileName);
464 /* load and verify existence for files previously left in the trash can */
465 TrashInitialized = ReadTrashList( );
466 return( TrashInitialized );
469 /************************************************************************
472 * This function must be called before any other interatctions with the
473 * trash can. It creates the trash dialog, opens the trash file, and
474 * loads information about any files previously left in the trash can.
476 * This function is called from main().
478 ************************************************************************/
486 DialogData * dialog_data;
487 FileMgrData * file_mgr_data;
489 if( ! TrashInitialized )
492 /* Create the trash dialog window */
493 /* load trash title */
494 tmpStr = GETMESSAGE(27, 9, "Trash Can");
495 title = XtNewString(tmpStr);
497 /* Initially, no items can be selected */
498 if (removeBtn != NULL)
499 XtSetSensitive(*removeBtn, False);
500 if (restoreBtn != NULL)
501 XtSetSensitive(*restoreBtn, False);
503 /* retrieve the default data for a file manager dialog -- */
504 /* the TrashView flag affects this data */
506 dialog_data = _DtGetDefaultDialogData (file_mgr_dialog);
507 file_mgr_data = (FileMgrData *) dialog_data->data;
508 trashFileMgrData = file_mgr_data;
509 trashDialogData = dialog_data;
511 /* build up directory set for trash directory */
512 FileMgrBuildDirectories (file_mgr_data, home_host_name, trash_dir);
514 /* initialize trash data */
515 file_mgr_data->restricted_directory =
516 XtNewString(file_mgr_data->current_directory);
517 file_mgr_data->title = XtNewString(title);
518 file_mgr_data->toolbox = False;
519 file_mgr_data->width = 300;
520 file_mgr_data->height = 300;
522 /* load any positioning information */
523 LoadPositionInfo(file_mgr_data);
525 /* build the trash dialog */
526 _DtBuildDialog (NULL, NULL, NULL, dialog_data, NULL, NULL, NULL,
527 NULL, NULL, False, special_view, title, NULL);
535 /************************************************************************
538 * At startup time, we need to read in the trash information file, to
539 * find out what, if any, files were left in the trash the last time
540 * this client was run. Each entry must be read in and then verified
541 * that it still exists; if it no longer exists, then someone has been
542 * mucking with our trash files, so we will remove it from our knowledge
543 * base. Otherwise, the file is added to the trash list.
545 ************************************************************************/
548 ReadTrashList( void )
550 int intSize, extSize, bufSize;
551 FILE * trashInfoFileId;
552 String external, intName, internal;
556 trashInfoFileId = fopen(TrashInfoFileName, "a+");
557 if (trashInfoFileId == 0)
561 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
563 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.") )
564 + strlen( TrashInfoFileName )
567 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 );
569 _DtMessage(toplevel, title, msg, NULL, HelpRequestCB);
575 chmod(TrashInfoFileName, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
577 bufSize = MAX_PATH * 2;
578 trashEntry = (char *)XtCalloc( 1, bufSize );
580 /* Extract the size of the two path strings */
581 while( fgets( trashEntry, bufSize, trashInfoFileId ) != NULL )
583 int len = strlen( trashEntry );
585 if( sscanf( trashEntry, "%d %d", &extSize, &intSize ) == 2 )
587 external = (char *)XtCalloc( 1, extSize + 1 );
588 intName = (char *)XtCalloc( 1, intSize + 1 );
590 if( len > extSize + intSize + 3 )
591 /* extSize + intSize + 3 is a rough estimate of a trash entry
592 a trash entry looks something like the following:
596 extSize is the size of the string '/tmp/abc'
597 intSize is the size of the string 'abcd'
599 so the len (the string length of the trash entry has AT LEAST to be
600 bigger than extSize + intSize + 3 separator spaces
605 /* sscanf can be used to reduce the code.
606 I'm not using it here because I don't have time to research.
607 I just want it to work.
609 tmpPtr = DtStrchr( trashEntry, ' ' ) + 1;
610 tmpPtr = DtStrchr( tmpPtr, ' ' ) + 1;
612 /* Extract the full external and partial internal file names */
613 memcpy( external, tmpPtr, extSize );
614 memcpy( intName, tmpPtr + extSize + 1, intSize );
616 /* Create internal/trash name */
617 internal = (char *)XtMalloc(strlen(users_home_dir)
619 + strlen(intName) + 2);
620 sprintf(internal, "%s%s/%s", users_home_dir, TRASH_DIR, intName);
622 /* Make sure the file still exists */
623 if (lstat(internal, &statbuf) < 0)
625 /* File no longer exists; ignore it */
632 /* Add to trash list */
633 if (numTrashItems >= trashListSize)
636 trashCan = (TrashEntry *)XtRealloc((char *) trashCan,
637 sizeof(TrashEntry) * trashListSize);
640 trashCan[numTrashItems].intNew = internal;
641 trashCan[numTrashItems].intOrig = XtNewString(external);
642 trashCan[numTrashItems].external = external;
643 trashCan[numTrashItems].filename = intName;
645 } /* end if file exists */
654 fclose(trashInfoFileId);
656 return( WriteTrashEntries() );
661 /************************************************************************
664 * This function is responsible for creating a new copy of the trash
665 * information file. It will open a new, temporary copy, and will
666 * copy in the contents of the trash can. It will then remove the
667 * old trash information file, and will rename the new one, opening
668 * a handle to it for later use.
670 ************************************************************************/
673 WriteTrashEntries( void )
675 static String path = NULL;
679 newFile = fopen(NewTrashInfoFileName, "w+");
682 chmod(NewTrashInfoFileName,
683 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
685 /* Write all remaining entries */
686 for (i = 0; i < numTrashItems; i++)
688 if( WriteEntry(newFile, trashCan[i].external, trashCan[i].filename) < 0 )
691 remove(NewTrashInfoFileName);
696 /* Remove the original information file, and move the new one */
697 (void) fclose(newFile);
698 (void) chown(NewTrashInfoFileName, getuid(), getgid());
699 (void) remove(TrashInfoFileName);
700 (void) rename(NewTrashInfoFileName, TrashInfoFileName);
701 (void) chown(TrashInfoFileName, getuid(), getgid());
710 /************************************************************************
713 * This function writes an entry to the trash information file.
714 * It expects the full path name for the original file [external]
715 * and only the new file name (without path information) [internal].
716 * If the file ends in '/', then it adds a '.' to the end; this is
717 * a special case, so that when we parse this information later, we
718 * are guaranteed to have a directory and a file name.
720 ************************************************************************/
728 return( fprintf(id, "%ld %ld %s %s\n",
729 (long)strlen(external), (long)strlen(internal),
730 external, internal) );
735 /************************************************************************
739 ************************************************************************/
744 FileMgrRec *file_mgr_rec )
749 static Widget * directoryBarBtn;
751 /* Create the menubar hierarchy */
752 trashMenu = (MenuDesc *)XtMalloc(sizeof(MenuDesc) * trashMenuItemCount);
755 for (i = 0; i < trashMenuItemCount; i++)
757 trashMenu[i].helpCallback = TrashHelpRequestCB;
758 trashMenu[i].helpData = NULL;
759 trashMenu[i].activateCallback = NULL;
760 trashMenu[i].activateData = NULL;
761 trashMenu[i].maskBit = 0;
762 trashMenu[i].isHelpBtn = False;
763 trashMenu[i].label = NULL;
764 trashMenu[i].mnemonic = NULL;
768 /*************************************/
769 /* Create the 'File' menu components */
770 /*************************************/
772 directoryBarBtn = &(trashMenu[j].widget);
773 trashMenu[j].type = MENU_PULLDOWN_BUTTON;
774 trashMenu[j].label = GETMESSAGE(20,1, "File");
775 trashMenu[j].label = XtNewString(trashMenu[j].label);
776 trashMenu[j].mnemonic = GETMESSAGE(20,2, "F");
777 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
778 trashMenu[j].name = "file_trash";
779 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
781 trashMenu[j].type = SHARED_MENU_PANE;
782 trashMenu[j].name = "fileMenuTrash";
783 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
785 selectAllBtn = &(trashMenu[j].widget);
786 trashMenu[j].type = MENU_BUTTON;
787 trashMenu[j].label = GETMESSAGE(20,48, "Select All");
788 trashMenu[j].label = XtNewString(trashMenu[j].label);
789 trashMenu[j].mnemonic = GETMESSAGE(20,51, "S");
790 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
791 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
792 trashMenu[j].name = "selectAll";
793 trashMenu[j++].activateCallback = Select_All;
795 trashMenu[j].type = MENU_BUTTON;
796 trashMenu[j].label = GETMESSAGE(20,52, "Deselect All");
797 trashMenu[j].label = XtNewString(trashMenu[j].label);
798 trashMenu[j].mnemonic = GETMESSAGE(20,55, "D");
799 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
800 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
801 trashMenu[j].name = "deselectAll";
802 trashMenu[j++].activateCallback = Unselect_All;
804 trashMenu[j].type = MENU_SEPARATOR;
805 trashMenu[j].name = "separator";
806 trashMenu[j++].helpCallback = NULL;
808 restoreBtn = &(trashMenu[j].widget);
809 trashMenu[j].type = MENU_BUTTON;
810 trashMenu[j].label = GETMESSAGE(27,24, "Put back");
811 trashMenu[j].label = XtNewString(trashMenu[j].label);
812 trashMenu[j].mnemonic = GETMESSAGE(27,26, "P");
813 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
814 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
815 trashMenu[j].name = "putBack";
816 trashMenu[j++].activateCallback = Restore;
818 removeBtn = &(trashMenu[j].widget);
819 trashMenu[j].type = MENU_BUTTON;
820 trashMenu[j].label = GETMESSAGE(27,28, "Shred");
821 trashMenu[j].label = XtNewString(trashMenu[j].label);
822 trashMenu[j].mnemonic = GETMESSAGE(27,30, "h");
823 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
824 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
825 trashMenu[j].name = "shred";
826 trashMenu[j++].activateCallback = ConfirmRemove;
828 trashMenu[j].type = MENU_SEPARATOR;
829 trashMenu[j].name = "separator";
830 trashMenu[j++].helpCallback = NULL;
832 trashMenu[j].type = MENU_BUTTON;
833 trashMenu[j].maskBit = PREFERENCES;
834 trashMenu[j].label = GETMESSAGE(20,141, "Set View Options ...");
835 trashMenu[j].label = XtNewString(trashMenu[j].label);
836 trashMenu[j].mnemonic = GETMESSAGE(20,6, "V");
837 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
838 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
839 trashMenu[j].name = "setPreferences";
840 trashMenu[j++].activateCallback = ShowPreferencesDialog;
842 trashMenu[j].type = MENU_SEPARATOR;
843 trashMenu[j].name = "separator";
844 trashMenu[j++].helpCallback = NULL;
846 trashMenu[j].type = MENU_BUTTON;
847 trashMenu[j].label = GETMESSAGE(20,117, "Close");
848 trashMenu[j].label = XtNewString(trashMenu[j].label);
849 trashMenu[j].mnemonic = GETMESSAGE(20,118, "C");
850 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
851 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
852 trashMenu[j].name = "close";
853 trashMenu[j++].activateCallback = CloseTrash;
856 /*************************************/
857 /* Create the 'Help' menu components */
858 /*************************************/
860 trashMenu[j].type = MENU_PULLDOWN_BUTTON;
861 trashMenu[j].label = GETMESSAGE(20,123, "Help");
862 trashMenu[j].label = XtNewString(trashMenu[j].label);
863 trashMenu[j].mnemonic = GETMESSAGE(20,9, "H");
864 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
865 trashMenu[j].isHelpBtn = True;
866 trashMenu[j].name = "help_trash";
867 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
869 trashMenu[j].type = SHARED_MENU_PANE;
870 trashMenu[j].name = "help_pane_trash";
871 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
873 trashMenu[j].type = MENU_BUTTON;
874 trashMenu[j].label = GETMESSAGE(20,105, "Overview");
875 trashMenu[j].label = XtNewString(trashMenu[j].label);
876 trashMenu[j].mnemonic = GETMESSAGE(20,106, "v");
877 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
878 trashMenu[j].helpData = HELP_HELP_MENU_STR;
879 trashMenu[j].activateData = HELP_TRASH_OVERVIEW_TOPIC_STR;
880 trashMenu[j].name = "introduction";
881 trashMenu[j++].activateCallback = TrashHelpRequestCB;
883 trashMenu[j].type = MENU_SEPARATOR;
884 trashMenu[j].name = "separator";
885 trashMenu[j++].helpCallback = NULL;
887 trashMenu[j].type = MENU_BUTTON;
888 trashMenu[j].label = GETMESSAGE(20,107, "Tasks");
889 trashMenu[j].label = XtNewString(trashMenu[j].label);
890 trashMenu[j].mnemonic = GETMESSAGE(20,108, "T");
891 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
892 trashMenu[j].helpData = HELP_HELP_MENU_STR;
893 trashMenu[j].activateData = HELP_TRASH_TASKS_TOPIC_STR;
894 trashMenu[j].name = "tasks";
895 trashMenu[j++].activateCallback = TrashHelpRequestCB;
897 trashMenu[j].type = MENU_BUTTON;
898 trashMenu[j].label = GETMESSAGE(20,109, "Reference");
899 trashMenu[j].label = XtNewString(trashMenu[j].label);
900 trashMenu[j].mnemonic = GETMESSAGE(20,110, "R");
901 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
902 trashMenu[j].helpData = HELP_HELP_MENU_STR;
903 trashMenu[j].activateData = HELP_TRASH_DIALOG_STR;
904 trashMenu[j].name = "reference";
905 trashMenu[j++].activateCallback = TrashHelpRequestCB;
907 trashMenu[j].type = MENU_BUTTON;
908 trashMenu[j].label = GETMESSAGE(20,111, "On Item");
909 trashMenu[j].label = XtNewString(trashMenu[j].label);
910 trashMenu[j].mnemonic = GETMESSAGE(20,112, "O");
911 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
912 trashMenu[j].helpData = HELP_HELP_MENU_STR;
913 trashMenu[j].activateData = HELP_HELP_MODE_STR;
914 trashMenu[j].name = "onItem";
915 trashMenu[j++].activateCallback = TrashHelpRequestCB;
917 trashMenu[j].type = MENU_SEPARATOR;
918 trashMenu[j].name = "separator";
919 trashMenu[j++].helpCallback = NULL;
921 usingHelpTrash = &(trashMenu[j].widget);
922 trashMenu[j].type = MENU_BUTTON;
923 trashMenu[j].label = GETMESSAGE(20,113, "Using Help");
924 trashMenu[j].label = XtNewString(trashMenu[j].label);
925 trashMenu[j].mnemonic = GETMESSAGE(20,114, "U");
926 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
927 trashMenu[j].helpData = HELP_HELP_MENU_STR;
928 trashMenu[j].activateData = HELP_HOME_TOPIC;
929 trashMenu[j].name = "usingHelp";
930 trashMenu[j++].activateCallback = TrashHelpRequestCB;
932 trashMenu[j].type = MENU_SEPARATOR;
933 trashMenu[j].name = "separator";
934 trashMenu[j++].helpCallback = NULL;
936 trashMenu[j].type = MENU_BUTTON;
937 trashMenu[j].label = GETMESSAGE(27,106, "About Trash Can");
938 trashMenu[j].label = XtNewString(trashMenu[j].label);
939 trashMenu[j].mnemonic = GETMESSAGE(20,116, "A");
940 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
941 trashMenu[j].helpData = HELP_HELP_MENU_STR;
942 trashMenu[j].activateData = HELP_ABOUT_STR;
943 trashMenu[j].name = "version";
944 trashMenu[j++].activateCallback = TrashHelpRequestCB;
946 menu_bar = _DtCreateMenuSystem(mainw, "menu_bar_trash", TrashHelpRequestCB,
947 HELP_TRASH_DIALOG_STR, True,
948 trashMenu, trashMenuItemCount, NULL,
951 /* Fine tune the menubar */
952 XtSetArg(args[0], XmNmarginWidth, 2);
953 XtSetArg(args[1], XmNmarginHeight, 2);
954 XtSetValues(menu_bar, args, 2);
956 file_mgr_rec->actions = NULL;
957 file_mgr_rec->action_pane = NULL;
958 file_mgr_rec->directoryBarBtn = *directoryBarBtn;
965 /************************************************************************
967 * TrashDisplayHandler
968 * This is the ICCCM message handler for the message used to display
969 * the trash can window. If the window is already displayed, then
970 * this call is a no-op.
972 * This handler is triggered by the front panel trash icon and by the
973 * File pulldown 'Show Trash' option.
975 ************************************************************************/
985 tt_message_reply( msg );
986 tttk_message_destroy( msg );
989 if (!TrashInitialized)
991 char *tmpStr, *tmpTitle, *tmpMsg;
993 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
994 tmpTitle = XtNewString(tmpStr);
995 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.");
996 tmpMsg = XtNewString(tmpStr);
998 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1005 #ifdef DEFER_TRASH_CREATION
1006 if( trashFileMgrData == NULL )
1007 TrashCreateDialog (XtDisplay(toplevel));
1010 /* the encapsulation functions do not set file_mgr_rec until a dialog */
1011 /* is actually displayed */
1012 if (trashFileMgrData->file_mgr_rec == 0)
1014 static Pixmap trash_icon = XmUNSPECIFIED_PIXMAP;
1015 static Pixmap trash_mask = XmUNSPECIFIED_PIXMAP;
1017 Pixel background, foreground, top_shadow, bottom_shadow, select;
1019 XClassHint classHints;
1020 FileMgrRec * file_mgr_rec;
1022 unsigned int height;
1026 classHints.res_name = trashFileMgrData->title;
1027 classHints.res_class = DTFILE_CLASS_NAME;
1030 _DtShowBuiltDialog(NULL,NULL, trashDialogData, NULL, False, &classHints);
1033 file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
1035 trashShell = file_mgr_rec->shell;
1037 if (trash_icon == XmUNSPECIFIED_PIXMAP)
1039 XtSetArg (args[0], XmNbackground, &background);
1040 XtSetArg (args[1], XmNcolormap, &colormap);
1041 XtGetValues (file_mgr_rec->main, args, 2);
1043 XmGetColors (XtScreen (file_mgr_rec->main), colormap, background,
1044 &foreground, &top_shadow, &bottom_shadow, &select);
1046 pixmap = XmGetPixmap (XtScreen (file_mgr_rec->main), trashIcon,
1047 foreground, background);
1048 if( pixmap != XmUNSPECIFIED_PIXMAP)
1049 trash_icon = pixmap;
1051 /* now let's get the mask for the File Manager */
1052 pixmap = _DtGetMask (XtScreen (file_mgr_rec->main), trashIcon);
1053 if( pixmap != XmUNSPECIFIED_PIXMAP)
1054 trash_mask = pixmap;
1056 if (trash_icon != XmUNSPECIFIED_PIXMAP)
1058 XtSetArg (args[0], XmNiconPixmap, trash_icon);
1059 if(trash_mask != XmUNSPECIFIED_PIXMAP)
1061 XtSetArg (args[1], XmNiconMask, trash_mask);
1062 XtSetValues (trashShell, args, 2);
1065 XtSetValues (trashShell, args, 1);
1073 root = RootWindowOfScreen (XtScreen (trashShell));
1075 /* Change the hints to reflect the current workspace */
1076 /* and raise the window */
1078 if (DtWsmGetCurrentWorkspace (XtDisplay (trashShell),
1079 root, ¤t_ws) == Success)
1080 DtWsmSetWorkspacesOccupied (XtDisplay (trashShell),
1081 XtWindow (trashShell), ¤t_ws, 1);
1083 /* Set the iconify state */
1084 /* Remove the iconify hint from the current shell */
1085 wmhints = XGetWMHints(XtDisplay(trashShell), XtWindow(trashShell));
1086 wmhints->flags |= IconWindowHint;
1087 wmhints->initial_state = NormalState;
1088 XSetWMHints(XtDisplay(trashShell), XtWindow(trashShell), wmhints);
1092 XtPopup (trashShell, XtGrabNone);
1093 XSync(XtDisplay(trashShell), False);
1094 XRaiseWindow (XtDisplay (trashShell), XtWindow (trashShell));
1095 XMapWindow( XtDisplay (trashShell), XtWindow (trashShell) );
1098 msg = tt_pnotice_create(TT_SESSION, "DtActivity_Began");
1099 tt_message_send(msg);
1100 tttk_message_destroy(msg);
1104 trashDialogPosted = True;
1108 /************************************************************************
1110 * ConfirmEmptyCancel
1112 ************************************************************************/
1113 static XtPointer trash_popup_client_data;
1117 XtPointer client_data,
1118 XtPointer call_data )
1120 XtUnmanageChild((Widget)client_data);
1121 XmUpdateDisplay((Widget)client_data);
1122 XtDestroyWidget((Widget)client_data);
1125 /************************************************************************
1129 ************************************************************************/
1134 XtPointer client_data,
1135 XtPointer call_data )
1137 /* destroy dialog */
1138 XtUnmanageChild((Widget)client_data);
1139 XmUpdateDisplay((Widget)client_data);
1140 XtDestroyWidget((Widget)client_data);
1145 /************************************************************************
1148 * This is a message interface used to really remove all of the
1151 * This handler is only called from an empty trash action.
1153 ************************************************************************/
1160 tt_message_reply( msg );
1161 tttk_message_destroy( msg );
1164 if(!TrashInitialized)
1166 char *tmpStr, *tmpTitle, *tmpMsg;
1168 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1169 tmpTitle = XtNewString(tmpStr);
1170 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.");
1171 tmpMsg = XtNewString(tmpStr);
1173 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1181 char *tmpStr, *title, *msg;
1183 tmpStr = GETMESSAGE(27,73, "Shred File(s)");
1184 title = XtNewString(tmpStr);
1185 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");
1186 msg = XtNewString(tmpStr);
1188 _DtMessageDialog(toplevel, title, msg, NULL, TRUE, ConfirmEmptyCancel,
1189 ConfirmEmptyOk, NULL, HelpRequestCB, False, QUESTION_DIALOG);
1198 /************************************************************************
1201 * This is a functional interface used to really remove all of the
1204 ************************************************************************/
1209 if (trashFileMgrData)
1212 Select_All(*selectAllBtn, NULL, NULL);
1213 Remove(*removeBtn, NULL, NULL);
1215 EmptyDir(trashFileMgrData->current_directory,0);
1221 /************************************************************************
1224 * This is the callback attached to the 'Select All' menu item. It
1225 * will select all of the files currently resident in the trash can.
1226 * It will also sensitize the 'Restore' and 'Remove' buttons, as long
1227 * as a remove request is not currently in the middle of being processed.
1229 ************************************************************************/
1234 XtPointer client_data,
1235 XtPointer call_data )
1238 SelectAllFiles(trashFileMgrData);
1239 SensitizeTrashBtns();
1244 /************************************************************************
1247 * This is the callback attached to the 'Unselect All' menu item. It
1248 * will unselect all of the items in the trash can, and will clear
1249 * out our selection array. It also desensitizes the 'Restore' and
1252 ************************************************************************/
1257 XtPointer client_data,
1258 XtPointer call_data )
1261 DeselectAllFiles(trashFileMgrData);
1262 SensitizeTrashBtns();
1267 /************************************************************************
1269 * SensitizeTrashBtns
1271 ************************************************************************/
1274 SensitizeTrashBtns(void)
1276 #ifdef DEFER_TRASH_CREATION
1277 if( trashFileMgrData
1282 if (trashFileMgrData->selected_file_count > 0)
1284 XtSetSensitive(*removeBtn, True);
1285 XtSetSensitive(*restoreBtn, True);
1289 XtSetSensitive(*removeBtn, False);
1290 XtSetSensitive(*restoreBtn, False);
1297 /************************************************************************
1299 * TrashRemoveHandler
1300 * This is the message handler for the message requesting that
1301 * a set of files be added to the trashcan. Each file name comes in
1302 * in /dir/name format; they are all in a single string, separated
1305 * Check for spaces in filenames and destop files. If we pass these
1306 * cases, call TrashRemover where the following occurs:
1308 * All of the files will be moved to the trash can directory; any
1309 * problems encountered will be reported in an error dialog. Each file
1310 * which is successfully moved, will be added to the trash information file.
1312 ************************************************************************/
1325 int msglistsize = 0 ;
1326 #endif /* SUN_PERF */
1329 * Note: There used to be a call to tt_message_reply() right here at the
1330 * beginning of the routine. However, the mailer wants to trash files
1331 * but wants the notification after the trash has been removed.
1332 * Therefore, we now put this off until the work is done or an error
1336 if( !TrashInitialized )
1338 char *tmpStr, *tmpTitle, *tmpMsg;
1340 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1341 tmpTitle = XtNewString(tmpStr);
1342 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.");
1343 tmpMsg = XtNewString(tmpStr);
1345 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1350 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
1352 global_msg_cnt = 0 ;
1353 #endif /* SUN_PERF */
1357 MessageToFileList(msg, &file_list, &file_count);
1360 * When the Dttrash_Remove handler "TrashRemoveHandler" is invoked for
1361 * the first time it caches the TT request and then invokes
1362 * tttk_block_while() with timeout = 0. This in effect causes the next
1363 * TT request to be processed before tttk_block_while() returns. Now if
1364 * TT msgs are coming in faster than dtfile is handling them, then the
1365 * next TT msg. should be another Dttrash_Remove msg. which will cause
1366 * re-entrance into TrashRemoveHandler. This will continue until the last
1367 * Dttrash_Remove msg. has been processed (and the recursive
1368 * TrashRemoveHandler starts unwinding) after which we check if the
1369 * number of TT msgs are > 0 in which case we fire off the
1370 * process to move the list of files to Trash.
1373 msglistsize = sizeof(Tt_message) * global_msg_cnt ;
1374 global_msg_list = (Tt_message *)XtRealloc((char *)global_msg_list, msglistsize);
1375 memcpy((char *)&(global_msg_list[global_msg_cnt - 1]),
1376 (char *)&msg, sizeof(Tt_message)) ;
1377 tttk_block_while(0, 0, time_out) ;
1379 /* If we get to this pt. then either :
1380 * 1. No more Dttrash_Remove msgs. left to process. Thus we should
1381 * process the TT msg list and move files to Trash.
1383 * 2. We got a msg. different from Dttrash_Remove. In this case
1384 * we process the msgs. we have so far. The remaining files to
1385 * be trashed will be buffered in another list.
1388 if (global_msg_cnt > 0) {
1389 MessagesToFileList(global_msg_list, global_msg_cnt,
1390 &file_list, &file_count);
1391 #endif /* SUN_PERF */
1393 if (file_count == 0)
1395 tt_message_reply( msg );
1396 tttk_message_destroy( msg );
1398 global_msg_cnt = 0 ;
1399 #endif /* SUN_PERF */
1403 /* return if from the trash dir */
1404 if (strncmp(file_list[0], trash_dir, strlen(trash_dir)) == 0)
1408 for (i = 0; i < file_count; i++) {
1409 XtFree( file_list[i] );
1411 XtFree( (char *)file_list );
1412 tt_message_status_set( msg, TT_DESKTOP_EALREADY );
1413 tt_message_reply( msg );
1414 tttk_message_destroy( msg );
1416 global_msg_cnt = 0 ;
1417 #endif /* SUN_PERF */
1421 /* post message if file(s) from desktop */
1425 str=(char *)IsAFileOnDesktop2(file_list, file_count, &number,&IsToolBox);
1426 /* IsToolBox is unused here, but is required to satisfy the prototype */
1430 MoveToTrash(file_list, file_count, verifyPromptsEnabled, msg);
1440 /* List will be regenerated by callbacks */
1441 for (i = 0; i < file_count; i++) {
1442 XtFree( file_list[i] );
1444 XtFree( (char *)file_list );
1446 tmpStr = (GETMESSAGE(27,73, "Shred File(s)"));
1447 title = XtNewString(tmpStr);
1451 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."));
1453 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."));
1454 message = (char *)XtMalloc(strlen(template) + strlen(str) + 1);
1455 sprintf(message, template, str);
1460 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."));
1462 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."));
1463 message = (char *)XtMalloc(strlen(template) + strlen(str) + 1);
1464 sprintf(message, template, str);
1467 /* Really ought to pass to OK and Cancel CBs via client_data */
1470 _DtMessageDialog(toplevel, title, message, NULL, TRUE,
1471 RemoveCancelCB, RemoveOkCB, NULL, HelpRequestCB, False,
1477 Tt_msg_cache *msg_cache ;
1479 msg_cache = (Tt_msg_cache *)XtMalloc(sizeof(Tt_msg_cache)) ;
1480 msg_cache->msg_cnt = global_msg_cnt ;
1482 msg_cache->msg_list = (Tt_message *)XtMalloc(sizeof(Tt_message) *
1483 msg_cache->msg_cnt );
1484 msg_cache->msg_list[msg_cache->msg_cnt] = NULL ;
1485 memcpy((char *)msg_cache->msg_list, (char *)global_msg_list,
1486 sizeof(Tt_message) * msg_cache->msg_cnt ) ;
1487 dlog = (Widget)_DtMessageDialog(toplevel, title, message, NULL, TRUE,
1488 RemoveCancelCB, RemoveOkCB, NULL, HelpRequestCB, False,
1490 XtSetArg ( args[0], XmNuserData, msg_cache );
1491 XtSetValues ( dlog, args, 1);
1493 #endif /* SUN_PERF */
1500 global_msg_cnt = 0 ;
1502 #endif /* SUN_PERF */
1507 /************************************************************************
1510 * Cleanup and unmanage the remove from DT dialog
1512 ************************************************************************/
1517 XtPointer client_data,
1518 XtPointer call_data )
1524 Tt_msg_cache *current_msg_cache ;
1527 XtSetArg( args[0], XmNuserData, ¤t_msg_cache );
1528 XtGetValues( (Widget)client_data, args, 1 );
1529 #endif /* SUN_PERF */
1531 /* destroy dialog */
1532 XtUnmanageChild((Widget)client_data);
1533 XmUpdateDisplay((Widget)client_data);
1534 XtDestroyWidget((Widget)client_data);
1538 MessageToFileList(global, &file_list, &file_count);
1540 MessagesToFileList( current_msg_cache->msg_list, current_msg_cache->msg_cnt,
1541 &file_list, &file_count);
1542 #endif /* SUN_PERF */
1544 MoveToTrash(file_list, file_count, verifyPromptsEnabled, NULL);
1546 /* reread desktop files */
1550 tt_message_reply( global );
1551 tttk_message_destroy( global );
1554 for (i = 0 ; i < current_msg_cache->msg_cnt ; i++) {
1555 tt_message_reply( current_msg_cache->msg_list[i] );
1556 tttk_message_destroy( current_msg_cache->msg_list[i] );
1558 XtFree ((char *)current_msg_cache->msg_list);
1559 XtFree ((char *)current_msg_cache);
1560 #endif /* SUN_PERF */
1565 /************************************************************************
1568 * Cleanup and unmanage the remove from DT dialog
1570 ************************************************************************/
1575 XtPointer client_data,
1576 XtPointer call_data )
1580 Tt_msg_cache *current_msg_cache ;
1583 XtSetArg( args[0], XmNuserData, ¤t_msg_cache );
1584 XtGetValues( (Widget)client_data, args, 1 );
1585 #endif /* SUN_PERF */
1587 /* destroy dialog */
1588 XtUnmanageChild((Widget)client_data);
1589 XmUpdateDisplay((Widget)client_data);
1590 XtDestroyWidget((Widget)client_data);
1593 tttk_message_fail( global, TT_DESKTOP_ECANCELED, 0, 1 );
1596 for (i = 0 ; i < current_msg_cache->msg_cnt ; i++)
1597 tttk_message_fail( current_msg_cache->msg_list[i], TT_DESKTOP_ECANCELED, 0, 1 );
1598 XtFree ((char *)current_msg_cache->msg_list);
1599 XtFree ((char *)current_msg_cache);
1600 #endif /* SUN_PERF */
1605 /************************************************************************
1607 * TrashRemoveNoConfirmHandler
1608 * This function is a message handler. It will place the specified set
1609 * of files into the trash can, without requesting confirmation if, for
1610 * instance, a file does not have write permission, or a directory is
1613 ************************************************************************/
1616 TrashRemoveNoConfirmHandler(
1622 if( !TrashInitialized )
1624 char *tmpStr, *tmpTitle, *tmpMsg;
1626 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1627 tmpTitle = XtNewString(tmpStr);
1628 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.");
1629 tmpMsg = XtNewString(tmpStr);
1631 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1637 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
1641 MessageToFileList(msg, &file_list, &file_count);
1642 MoveToTrash(file_list, file_count, False, msg);
1647 /************************************************************************
1651 * Parses a list of file names from a message
1652 * and returns an array of strings.
1654 * if (no message arguments) {
1655 * if (no file attribute) {
1656 * return length==0 list
1658 * return length==1 list containing file attribute
1661 * if (file attribute) {
1662 * file attribute is a directory, prepended onto subsequent filenames
1664 * if (arg 0 is non-null) {
1665 * arg 0 interpreted as a space-separated list of filenames
1668 * arg interpreted as a filename possibly containing spaces
1672 ************************************************************************/
1688 args = tt_message_args_count( msg );
1689 if (tt_is_err(tt_int_error( args ))) {
1693 dir = tt_message_file( msg );
1694 if ((tt_is_err(tt_ptr_error( dir ))) || (dir == 0)) {
1698 /* No args means use tt_message_file() as is */
1699 *file_list = (char **)
1700 XtRealloc((char *)(*file_list),
1701 (*file_count + 1)*sizeof(char *));
1702 (*file_list)[*file_count] = XtNewString(dir);
1707 if (dir[strlen(dir)] != '/') {
1709 if (strcmp(dir, "/") == 0)
1711 newdir = (char *)tt_malloc( strlen(dir)+1 );
1712 strcpy( newdir, dir );
1716 newdir = (char *)tt_malloc( strlen(dir)+2 );
1717 strcpy( newdir, dir );
1718 strcat( newdir, "/" );
1724 /* Invariant: dir can now be safely concatenated to form a valid path */
1725 dirlen = strlen(dir);
1726 for (arg = 0; arg < args; arg++) {
1731 val = tt_message_arg_val( msg, arg );
1732 if ((tt_is_err(tt_ptr_error( val ))) || (val == 0)) {
1738 /* Arg 0 is a space-separated list */
1741 for (val = DtStrtok(val, white); val; val = DtStrtok(0, white))
1743 if (strcmp(val, "/") == 0)
1745 file = (char *)XtMalloc( dirlen + 1 );
1746 strcpy( file, dir );
1750 file = (char *)XtMalloc( dirlen + strlen(val) + 1 );
1751 strcpy( file, dir );
1752 strcat( file, val );
1754 *file_list = (char **)
1755 XtRealloc((char *)(*file_list),
1756 (*file_count + 1)*sizeof(char *));
1757 (*file_list)[*file_count] = DtEliminateDots(file);
1760 tt_free( val2free );
1768 Tt_message *msg_list,
1783 for (num_msgs = 0 ; num_msgs < msg_cnt ; num_msgs++)
1785 msg = msg_list[num_msgs] ;
1787 args = tt_message_args_count( msg );
1788 if (tt_is_err(tt_int_error( args ))) {
1792 dir = tt_message_file( msg );
1793 if ((tt_is_err(tt_ptr_error( dir ))) || (dir == 0)) {
1797 /* No args means use tt_message_file() as is */
1798 *file_list = (char **)
1799 XtRealloc((char *)(*file_list),
1800 (*file_count + 1)*sizeof(char *));
1801 (*file_list)[*file_count] = XtNewString(dir);
1806 if (dir[strlen(dir)] != '/') {
1808 if (strcmp(dir, "/") == 0)
1810 newdir = (char *)tt_malloc( strlen(dir)+1 );
1811 strcpy( newdir, dir );
1815 newdir = (char *)tt_malloc( strlen(dir)+2 );
1816 strcpy( newdir, dir );
1817 strcat( newdir, "/" );
1823 /* Invariant: dir can now be safely concatenated to form a valid path */
1824 dirlen = strlen(dir);
1825 for (arg = 0; arg < args; arg++) {
1830 val = tt_message_arg_val( msg, arg );
1831 if ((tt_is_err(tt_ptr_error( val ))) || (val == 0)) {
1837 /* Arg 0 is a space-separated list */
1840 for (val = DtStrtok(val, white); val; val = DtStrtok(0, white))
1842 if (strcmp(val, "/") == 0)
1844 file = (char *)XtMalloc( dirlen + 1 );
1845 strcpy( file, dir );
1847 else if (strcmp(val, "/") == 0 && dirlen == 0)
1849 file = (char *)XtMalloc( strlen(val) + 1 );
1850 strcpy( file, val );
1854 file = (char *)XtMalloc( dirlen + strlen(val) + 1 );
1855 strcpy( file, dir );
1856 strcat( file, val );
1858 *file_list = (char **)
1859 XtRealloc((char *)(*file_list),
1860 (*file_count + 1)*sizeof(char *));
1861 (*file_list)[*file_count] = DtEliminateDots(file);
1864 tt_free( val2free );
1869 #endif /* SUN_PERF */
1872 /************************************************************************
1874 * CreateTrashFilename
1875 * Create trash directory name.
1877 ************************************************************************/
1880 CreateTrashFilename(
1882 Boolean uniqueTest )
1885 char * extension = NULL;
1886 struct stat statInfo;
1888 /* Create trash path */
1889 /* Give that name a little extra cushion, just in case */
1890 trashName = (char *)XtMalloc(strlen(users_home_dir) + strlen(TRASH_DIR) +
1891 strlen(baseName) + 15);
1892 sprintf(trashName, "%s%s/%s", users_home_dir, TRASH_DIR, baseName);
1894 /* Want to find the extension so the new file name created will preserve
1895 its original datatype.
1897 extension = strrchr( baseName, '.' );
1904 /* If a file by the trash name already exists, keep building new names */
1905 /* until one by that name doesn't exist and then use it */
1906 if (uniqueTest && (lstat(trashName, &statInfo) == 0))
1910 for (i = 1; True ; i++)
1912 /* Make a duplicate file name */
1914 sprintf(trashName, "%s%s/%s_%d.%s", users_home_dir, TRASH_DIR,
1915 baseName, i, extension);
1917 sprintf(trashName, "%s%s/%s_%d", users_home_dir, TRASH_DIR,
1920 /* Is the filename taken? */
1921 if (lstat(trashName, &statInfo) != 0)
1925 } /* end if not unique name */
1928 *(extension-1) = '.';
1936 /************************************************************************
1938 * MatchesSacredDirectory
1939 * Checks if the specify filename matches one of the sacred,
1940 * non-deleteable files.
1942 ************************************************************************/
1945 MatchesSacredDirectory(
1950 /* Don't allow the user to delete any of the sacred directories */
1951 for (i = 0; i < sacred_dir_count; i++)
1953 if (!strcmp(file, sacred_dir_list[i]))
1957 /* Compare against special desktop directories */
1958 /* remote_sys_dir is currently NULL */
1959 if (strcmp(file, remote_sys_dir) == 0)
1967 /************************************************************************
1970 * Callback for 'Delete To Trash' verify dialog Ok push button.
1972 ************************************************************************/
1977 XtPointer client_data,
1978 XtPointer call_data )
1980 VerifyCleanup ((Widget)client_data, True);
1985 /************************************************************************
1988 * Callback for 'Delete To Trash' verify dialog Cancel push button.
1990 ************************************************************************/
1995 XtPointer client_data,
1996 XtPointer call_data )
1999 VerifyCleanup ((Widget)client_data, False);
2004 /************************************************************************
2007 * Callback for 'Delete To Trash' verify dialog push buttons.
2009 * Called from VerifyOk and VerifyCancel.
2011 ************************************************************************/
2016 Boolean completeDelete )
2023 /* Unpost dialog, and retrieve the list of verified files */
2024 XtUnmanageChild(mbox);
2025 XmUpdateDisplay(mbox);
2026 XtSetArg(args[0], XmNuserData, &verifylist);
2027 XtGetValues(mbox, args, 1);
2029 /* count the number of files in the list */
2030 for (fileCount = 0; verifylist[fileCount]; fileCount++)
2034 * Start the background process that moves the files into the trash,
2038 MoveToTrash(verifylist, fileCount, False, NULL);
2042 /* Free up the storage we allocated */
2043 for (i = fileCount; i > 0; i--)
2044 XtFree(verifylist[i]);
2045 XtFree((char *)verifylist);
2048 XtDestroyWidget(mbox);
2053 /************************************************************************
2057 ************************************************************************/
2058 static XtPointer trash_popup_client_data;
2062 XtPointer client_data,
2063 XtPointer call_data )
2065 XtUnmanageChild((Widget)client_data);
2066 XmUpdateDisplay((Widget)client_data);
2067 XtDestroyWidget((Widget)client_data);
2069 XtSetSensitive(*removeBtn, True);
2070 XtSetSensitive(*restoreBtn, True);
2071 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
2072 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
2073 trash_popup_client_data = NULL;
2078 /************************************************************************
2082 ************************************************************************/
2087 XtPointer client_data,
2088 XtPointer call_data )
2090 FileViewData *file_view_data;
2092 /* destroy dialog */
2093 XtUnmanageChild((Widget)client_data);
2094 XmUpdateDisplay((Widget)client_data);
2095 XtDestroyWidget((Widget)client_data);
2097 file_view_data = trashFileMgrData->popup_menu_icon;
2098 if(!file_view_data && trashFileMgrData->selected_file_count)
2099 file_view_data = trashFileMgrData->selection_list[0];
2100 trashFileMgrData->popup_menu_icon = NULL;
2102 if(file_view_data != NULL)
2103 Remove(*removeBtn, file_view_data, NULL);
2108 /************************************************************************
2111 * This is the callback attached to the 'Remove' menu item.
2113 ************************************************************************/
2118 XtPointer client_data,
2119 XtPointer call_data )
2121 char *tmpStr, *title, *msg;
2123 /* Desensitize remove & restore buttons, until we're done */
2124 XtSetSensitive(*removeBtn, False);
2125 XtSetSensitive(*restoreBtn, False);
2126 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], False);
2127 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], False);
2130 trash_popup_client_data = client_data;
2132 tmpStr = GETMESSAGE(27,73, "Shred File(s)");
2133 title = XtNewString(tmpStr);
2134 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");
2135 msg = XtNewString(tmpStr);
2137 _DtMessageDialog(toplevel, title, msg, NULL, TRUE, ConfirmCancel, ConfirmOk,
2138 NULL, HelpRequestCB, False, QUESTION_DIALOG);
2146 /************************************************************************
2149 * While we wait for the background process to complete, we will
2150 * desensitize the 'Remove' and 'Restore' menu items, since we don't
2151 * handle multiple requests.
2153 ************************************************************************/
2158 XtPointer client_data,
2159 XtPointer call_data )
2161 FileViewData *file_view_data;
2162 Boolean match = False;
2163 DeleteList *deleteList = NULL;
2169 /* Remove may be called to remove a file or to remove a trash file; */
2170 /* if w is NULL, this is a call to remove a file */
2173 removeType = TRASH_FILE;
2175 /* Count number of items to 'Delete To Trash' */
2176 if (client_data != NULL)
2178 /* This was called by trash popup */
2179 /* need to check to see if it is selected, if it is restore all
2180 the selected files, else just restore this file */
2182 file_view_data = (FileViewData *)client_data;
2183 for (i = 0; i < trashFileMgrData->selected_file_count; i++)
2185 if (strcmp(file_view_data->file_data->file_name,
2186 trashFileMgrData->selection_list[i]->file_data->file_name) == 0)
2194 if (trashFileMgrData->selected_file_count == 0)
2196 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
2197 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
2204 /* Desensitize remove & restore buttons, until we're done */
2205 XtSetSensitive(*removeBtn, False);
2206 XtSetSensitive(*restoreBtn, False);
2210 deleteCount = trashFileMgrData->selected_file_count;
2214 /* Create the list of things being deleted */
2215 deleteList = (DeleteList *)XtMalloc(deleteCount * sizeof(DeleteList));
2219 for (i = 0; i < deleteCount; i++)
2221 AddToDeleteList(deleteList, i,
2222 trashFileMgrData->selection_list[i]->file_data->file_name);
2226 AddToDeleteList(deleteList, 0, file_view_data->file_data->file_name);
2231 removeType = REMOVE_FILE;
2232 deleteList = (DeleteList *)XtMalloc(sizeof(DeleteList));
2234 deleteList[0].trash = XtNewString(client_data);
2235 deleteList[0].orig = NULL;
2238 EmptyTrash(deleteList, deleteCount, removeType, NULL);
2243 /************************************************************************
2246 * Locate a file in the trash list and add it to the delete list.
2248 ************************************************************************/
2252 DeleteList *deleteList,
2258 /* Locate file in trash list, add entry to delete list */
2259 for (j = 0; j < numTrashItems; j++)
2261 if (strcmp(filename, trashCan[j].filename) == 0)
2263 /* file found in trash list */
2264 deleteList[i].trash = XtNewString(trashCan[j].intNew);
2265 deleteList[i].orig = XtNewString(trashCan[j].intOrig);
2271 /* file not found in trash list */
2272 deleteList[i].trash = CreateTrashFilename(filename, FALSE);
2273 deleteList[i].orig = NULL;
2278 /************************************************************************
2280 * TrashRestoreHandler
2281 * This function is a message handler. It will restore the specified set
2282 * of files from the trash can.
2284 ************************************************************************/
2287 TrashRestoreHandler(
2293 if( !TrashInitialized )
2295 char *tmpStr, *tmpTitle, *tmpMsg;
2297 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
2298 tmpTitle = XtNewString(tmpStr);
2299 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.");
2300 tmpMsg = XtNewString(tmpStr);
2302 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
2308 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
2313 /* Desensitize remove & restore buttons, until we're done */
2314 XtSetSensitive(*removeBtn, False);
2315 XtSetSensitive(*restoreBtn, False);
2317 MessageToFileList(msg, &file_list, &file_count);
2318 RestoreFromTrash(file_list, file_count, NULL, NULL, msg,False);
2323 /************************************************************************
2326 * This is the callback attached to the 'Restore' menu item. It will
2327 * remove from the trash all of the selected files, and will restore
2328 * them to their original location. If any problems occur while trying
2329 * to restore any of the files, then an error dialog will be posted.
2331 ************************************************************************/
2336 XtPointer client_data,
2337 XtPointer call_data )
2339 FileViewData *file_view_data;
2340 Boolean match = False;
2345 if (client_data != NULL)
2347 /* This was called by trash popup */
2348 /* need to check to see if it is selected, if it is restore all
2349 the selected files, else just restore this file */
2351 file_view_data = trashFileMgrData->popup_menu_icon;
2353 /* The object would have been deleted if the following condn is true */
2354 if(!file_view_data && !trashFileMgrData->selected_file_count)
2356 trashFileMgrData->popup_menu_icon = NULL;
2358 for (i = 0; i < trashFileMgrData->selected_file_count; i++)
2360 if (strcmp(file_view_data->file_data->file_name,
2361 trashFileMgrData->selection_list[i]->file_data->file_name) == 0)
2370 /* Desensitize remove & restore buttons, until we're done */
2371 XtSetSensitive(*removeBtn, False);
2372 XtSetSensitive(*restoreBtn, False);
2377 file_count = trashFileMgrData->selected_file_count;
2378 file_list = (char **)XtMalloc(file_count * sizeof(char *));
2379 for (i = 0; i < file_count; i++)
2381 file_view_data = trashFileMgrData->selection_list[file_count - 1 - i];
2382 file_list[i] = XtNewString(file_view_data->file_data->file_name);
2388 file_list = (char **)XtMalloc(sizeof(char *));
2389 file_list[0] = XtNewString(file_view_data->file_data->file_name);
2392 /* Start the background process that will do the restore */
2393 RestoreFromTrash(file_list, file_count, NULL, NULL, NULL,False);
2398 /************************************************************************
2401 * This is the callback attached to the 'Close' menu item. It will
2402 * unpost the trash can window.
2404 ************************************************************************/
2409 XtPointer client_data,
2410 XtPointer call_data )
2414 if (trashDialogPosted)
2416 XWithdrawWindow(XtDisplay(trashShell), XtWindow(trashShell),
2417 XDefaultScreen(XtDisplay(trashShell)));
2418 XtPopdown(trashShell);
2422 for (i = 0; i < secondaryTrashHelpDialogCount; i++)
2424 if (_DtIsDialogShowing(secondaryTrashHelpDialogList[i]))
2425 _DtHideDialog(secondaryTrashHelpDialogList[i], False);
2426 _DtFreeDialogData(secondaryTrashHelpDialogList[i]);
2428 XtFree((char *)secondaryTrashHelpDialogList);
2429 secondaryTrashHelpDialogList = NULL;
2430 secondaryTrashHelpDialogCount = 0;
2432 if (primaryTrashHelpDialog)
2434 if (_DtIsDialogShowing(primaryTrashHelpDialog))
2435 _DtHideDialog(primaryTrashHelpDialog, False);
2436 _DtFreeDialogData(primaryTrashHelpDialog);
2438 primaryTrashHelpDialog = NULL;
2440 if (PositionFlagSet(trashFileMgrData))
2441 SavePositionInfo(trashFileMgrData);
2444 FileMgrRec * file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
2446 if( (file_mgr_rec->menuStates & PREFERENCES) == 0 )
2447 file_mgr_rec->menuStates |= PREFERENCES;
2450 trashDialogPosted = False;
2455 /************************************************************************
2459 ************************************************************************/
2466 DtDndDropCallbackStruct *drop_parameters)
2469 DtActionArg * action_args;
2470 FileMgrRec * file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
2471 Position drop_x = drop_parameters->x;
2472 Position drop_y = drop_parameters->y;
2474 _DtBuildActionArgsWithDroppedFiles(NULL, drop_parameters,
2475 &action_args, &arg_count);
2477 DtActionInvoke(file_mgr_rec->shell, TRASH_ACTION,
2478 action_args, arg_count, NULL, NULL,
2479 trashFileMgrData->current_directory, True, NULL, NULL);
2481 _DtFreeActionArgs(action_args, arg_count);
2483 RepositionIcons(trashFileMgrData, file_set, file_count, drop_x,
2489 /************************************************************************
2493 ************************************************************************/
2497 FileMgrData *file_mgr_data,
2498 FileMgrRec *file_mgr_rec,
2512 * Get target host and directory
2514 target_dir = XtNewString(file_mgr_data->current_directory);
2515 target_host = XtNewString(file_mgr_data->host);
2518 * Create file list and call RestoreFromTrash
2520 file_list = (char **)XtMalloc(file_count * sizeof(char *));
2522 for (i = 0; i < file_count; i++)
2523 file_list[i] = XtNewString(file_set[i]);
2525 RestoreFromTrash(file_list, file_count, target_host, target_dir, NULL,False);
2530 /************************************************************************
2533 * Locate file in trash list based on new internal name. Return file name.
2535 ************************************************************************/
2541 Boolean IsTrash = False;
2543 if (strncmp(users_home_dir, filename, strlen(users_home_dir)) == 0)
2546 ((filename + strlen(users_home_dir)), TRASH_DIR, strlen(TRASH_DIR))
2556 /************************************************************************
2559 * Since the trash does not use shared menupanes, there is no work to
2560 * be done during the popup and popdown callbacks; therefore, we use
2561 * and empty function.
2563 ************************************************************************/
2568 XtPointer clientData,
2569 XtPointer callData )
2576 /*--------------------------------------------------------------------
2577 * UpdateDirectoryOf:
2578 * Arrange for the directory containing a file to be updated
2579 *------------------------------------------------------------------*/
2588 /* remove last component from path to get the directory */
2589 ptr = strrchr(path, '/');
2593 strcpy(host, home_host_name);
2595 /* now arrange for the directory to be updated */
2596 UpdateDirectory(NULL, host, path);
2598 /* restore the path */
2604 /*--------------------------------------------------------------------
2605 * EraseObject, EraseDir
2606 * Routines for recursively deleting files and directories
2607 *------------------------------------------------------------------*/
2610 EraseObject(char *file_name)
2612 struct stat stat_buf;
2614 if (lstat(file_name, &stat_buf) < 0)
2617 else if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
2618 return EraseDir(file_name);
2620 else if (remove(file_name) < 0)
2629 EraseDir(char *dir_name)
2631 DIR *dir; /* open directory */
2632 struct dirent *entry; /* directory entry */
2633 char srcname[MAX_PATH];
2637 /* open source directory */
2638 dir = opendir(dir_name);
2642 /* prepare source name */
2643 strcpy(srcname, dir_name);
2644 srclen = strlen(srcname);
2645 if (srcname[srclen - 1] != '/')
2646 srcname[srclen++] = '/';
2649 while (rc == 0 && (entry = readdir(dir)) != NULL)
2651 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
2653 strcpy(srcname + srclen, entry->d_name);
2654 rc = EraseObject(srcname);
2661 if (rmdir(dir_name) < 0)
2663 if (unlink(dir_name) < 0 )
2675 /*====================================================================
2677 * MoveToTrashProcess
2678 * Run a background process to move files to the trash can.
2680 *==================================================================*/
2682 /*--------------------------------------------------------------------
2683 * MoveToTrashProcess
2684 * Main routine of background process for MoveToTrash
2685 *------------------------------------------------------------------*/
2692 Boolean do_verify_checks)
2698 struct stat statInfo;
2700 struct dirent * entry;
2705 for (i = 0; i < file_count; i++)
2707 /* get base name and full path */
2708 path = XtNewString(file_list[i]);
2709 baseName = strrchr(file_list[i], '/');
2710 if (baseName == NULL || path == NULL)
2712 /* Invalid filename */
2714 pipe_msg = PIPEMSG_OTHER_ERROR;
2715 rc = BAD_FILE_ERROR;
2716 DPRINTF(("MoveToTrashProcess: sending BAD_FILE_ERROR\n"));
2717 write(pipe_fd, &pipe_msg, sizeof(short));
2718 write(pipe_fd, &rc, sizeof(int));
2721 if (path && MatchesSacredDirectory(path))
2723 /* Invalid filename */
2725 pipe_msg = PIPEMSG_OTHER_ERROR;
2726 rc = BAD_FILE_SACRED;
2727 DPRINTF(("MoveToTrashProcess: sending BAD_FILE_SACRED\n"));
2728 write(pipe_fd, &pipe_msg, sizeof(short));
2729 write(pipe_fd, &rc, sizeof(int));
2734 if (do_verify_checks)
2736 char *tmpstr = (file_list[i] == (baseName - 1))?"/":file_list[i];
2738 Boolean TrashError = False;
2740 /* check write permissions for the file */
2741 if (lstat(path,&s1) < 0)
2748 savechar = *(baseName-1);
2749 *(baseName-1) = '\0';
2750 if(CheckDeletePermission(tmpstr, path) != 0)
2752 /* No write access; display error message */
2754 if (S_ISDIR(s1.st_mode))
2755 rc = BAD_TRASH_DIRECTORY;
2757 rc = BAD_TRASH_FILE;
2760 *(baseName-1) = savechar;
2765 pipe_msg = PIPEMSG_OTHER_ERROR;
2766 DPRINTF(("MoveToTrashProcess: sending BAD_TRASH message\n"));
2767 write(pipe_fd, &pipe_msg, sizeof(short));
2768 write(pipe_fd, &rc, sizeof(int));
2771 else if (CheckAccess(path, W_OK) != 0 && !S_ISLNK(s1.st_mode))
2773 /* No write access; ask user for verification */
2775 pipe_msg = PIPEMSG_OTHER_ERROR;
2777 DPRINTF(("MoveToTrashProcess: sending VERIFY_FILE\n"));
2778 write(pipe_fd, &pipe_msg, sizeof(short));
2779 write(pipe_fd, &rc, sizeof(int));
2784 * If this is a directory, make sure it's empty, i.e.,
2785 * contains only ".", "..", and ".!" or ".~" files.
2787 if (lstat(path, &statInfo) == 0 &&
2788 (statInfo.st_mode & S_IFMT) == S_IFDIR &&
2789 (dirp = opendir(path)) != NULL)
2791 /* read the directory */
2792 while ((entry = readdir(dirp)) != NULL)
2794 if ( !(strcmp(entry->d_name, ".") == 0 ||
2795 strcmp(entry->d_name, "..") == 0 ||
2796 strncmp(entry->d_name, ".!", 2) == 0 ||
2797 strncmp(entry->d_name, ".~", 2) == 0) )
2799 /* found a real file: directory not empty */
2808 /* Directory is not empty */
2810 pipe_msg = PIPEMSG_OTHER_ERROR;
2812 DPRINTF(("MoveToTrashProcess: sending VERIFY_DIR\n"));
2813 write(pipe_fd, &pipe_msg, sizeof(short));
2814 write(pipe_fd, &rc, sizeof(int));
2818 } /* end if do_verify_checks */
2821 to = CreateTrashFilename(baseName, TRUE);
2823 /* move file to the trash directory */
2824 success = FileManip((Widget)pipe_fd, MOVE_FILE, path, to, TRUE,
2825 FileOpError, True, TRASH_DIRECTORY);
2828 pipe_msg = PIPEMSG_DONE;
2829 DPRINTF(("MoveToTrashProcess: sending DONE\n"));
2830 write(pipe_fd, &pipe_msg, sizeof(short));
2831 PipeWriteString(pipe_fd, path);
2832 PipeWriteString(pipe_fd, to);
2841 /*--------------------------------------------------------------------
2842 * MoveToTrashPipeCB:
2843 * Read and process data sent through the pipe.
2844 *------------------------------------------------------------------*/
2848 XtPointer client_data,
2852 MoveToTrashCBData *cb_data = (MoveToTrashCBData *)client_data;
2855 char *title, *err_msg, *err_arg;
2868 DesktopRec *desktopWin;
2869 char *dir_error=NULL,*file_error=NULL,*no_file_error=NULL,*sacred_error=NULL;
2871 /* read the next msg from the pipe */
2873 n = PipeRead(*fd, &pipe_msg, sizeof(short));
2874 DPRINTF(("MoveToTrashPipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
2878 case PIPEMSG_FILEOP_ERROR:
2879 PipeRead(*fd, &rc, sizeof(int));
2880 err_msg = PipeReadString(*fd);
2881 err_arg = PipeReadString(*fd);
2883 FileOperationError(toplevel, err_msg, err_arg);
2888 cb_data->rc[cb_data->done_count++] = BAD_FILE_ERROR;
2891 case PIPEMSG_OTHER_ERROR:
2892 PipeRead(*fd, &rc, sizeof(int));
2893 cb_data->rc[cb_data->done_count++] = rc;
2896 case PIPEMSG_TARGET_TIME:
2899 case PIPEMSG_FILE_MODIFIED:
2903 i = cb_data->done_count++;
2904 cb_data->path[i] = PipeReadString(*fd);
2905 cb_data->to[i] = PipeReadString(*fd);
2911 "Internal error in MoveToTrashPipeCB: bad pipe_msg %d\n",
2913 /* Don't know how it can get in here, but if it does, we'll try to
2914 * simulate an error condition. Without it, the SGI hung up
2916 while (cb_data->done_count < cb_data->file_count)
2917 cb_data->rc[cb_data->done_count++] = BAD_FILE_ERROR;
2922 /* if still more files to be processed, return now to wait for the rest */
2923 if (cb_data->done_count < cb_data->file_count)
2927 /* close the pipe and cancel the callback */
2928 DPRINTF(("MoveToTrashPipeCB: done\n"));
2932 /* process the results */
2944 for (i = 0; i < cb_data->file_count; i++)
2946 if (cb_data->rc[i]==BAD_TRASH_DIRECTORY || cb_data->rc[i]==BAD_TRASH_FILE)
2951 Boolean errflg = False;
2952 if(cb_data->rc[i] == BAD_TRASH_DIRECTORY)
2954 tmpmsg = GETMESSAGE(27, 113,
2955 "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");
2958 dir_error = (char *) XtRealloc(dir_error,strlen(dir_error)+
2959 strlen(cb_data->file_list[i])+5);
2960 strcat(dir_error,cb_data->file_list[i]);
2961 strcat(dir_error,"\n");
2965 dir_error = XtMalloc(strlen(tmpmsg)+
2966 strlen(cb_data->file_list[i])+5);
2967 sprintf(dir_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
2972 tmpmsg = GETMESSAGE(27, 114,
2973 "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");
2976 file_error = (char *) XtRealloc(file_error,strlen(file_error)
2977 + strlen(cb_data->file_list[i])+5);
2978 strcat(file_error,cb_data->file_list[i]);
2979 strcat(file_error,"\n");
2983 file_error = XtMalloc(strlen(tmpmsg)+
2984 strlen(cb_data->file_list[i])+5);
2985 sprintf(file_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
2988 XtFree(cb_data->file_list[i]);
2989 cb_data->file_list[i] = NULL;
2993 else if (cb_data->rc[i] == BAD_FILE_SACRED)
2997 char *tmpmsg = GETMESSAGE(27, 115,
2998 "The following object(s) cannot be deleted.\nThe desktop cannot function properly without these object(s).\n\n");
3001 sacred_error = (char *) XtRealloc(sacred_error,strlen(sacred_error)+
3002 strlen(cb_data->file_list[i])+5);
3003 strcat(sacred_error,cb_data->file_list[i]);
3004 strcat(sacred_error,"\n");
3008 sacred_error=XtMalloc(strlen(tmpmsg)+strlen(cb_data->file_list[i])+5);
3009 sprintf(sacred_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
3011 XtFree(cb_data->file_list[i]);
3012 cb_data->file_list[i] = NULL;
3016 else if (cb_data->rc[i] == BAD_FILE_ERROR)
3018 XtFree(cb_data->file_list[i]);
3019 cb_data->file_list[i] = NULL;
3021 else if (cb_data->rc[i] == NO_TRASH_FILE)
3024 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");
3030 no_file_error = (char *)XtRealloc(no_file_error,strlen(no_file_error)+
3031 strlen(cb_data->file_list[i])+5);
3032 strcat(no_file_error,cb_data->file_list[i]);
3033 strcat(no_file_error,"\n");
3037 no_file_error = XtMalloc(strlen(tmpmsg)+
3038 strlen(cb_data->file_list[i])+5);
3039 sprintf(no_file_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
3044 else if (cb_data->rc[i] == VERIFY_FILE ||
3045 cb_data->rc[i] == VERIFY_DIR)
3047 if (verifyCount < 6)
3048 AddString(&verifybuf, &verifybufsize, cb_data->file_list[i], NULL);
3052 * Keep track of whether we have directories only, files only,
3053 * or a combination, so that we can display the appropriate
3054 * label in the dialog.
3056 if (cb_data->rc[i] == VERIFY_FILE)
3062 * Add to array which is to be attached to the dialog;
3063 * the array will ultimately be NULL terminated.
3065 verifylist = (char **)XtRealloc((char *)verifylist,
3066 sizeof(char *) * (verifyCount + 1));
3067 verifylist[verifyCount - 1] = cb_data->file_list[i];
3071 /* Add file to trash list */
3072 if (numTrashItems >= trashListSize)
3074 trashListSize += 10;
3075 trashCan = (TrashEntry *)
3076 XtRealloc((char *)trashCan, sizeof(TrashEntry) * trashListSize);
3079 trashCan[numTrashItems].problem = False;
3080 trashCan[numTrashItems].intNew = cb_data->to[i];
3081 trashCan[numTrashItems].intOrig = cb_data->path[i];
3082 trashCan[numTrashItems].external = cb_data->file_list[i];
3084 /* extract base file name */
3085 baseName = strrchr(cb_data->to[i], '/');
3087 if (*baseName == '\0')
3089 trashCan[numTrashItems].filename = XtNewString(baseName);
3094 /* arrange for the source directory to be updated */
3095 UpdateDirectoryOf(cb_data->path[i]);
3098 * If the source file was referenced by a desktop object,
3099 * we need to remove the destkop object
3101 for (j = 0; j < desktop_data->numIconsUsed; j++)
3103 Tt_status tt_status;
3105 desktopWin = desktop_data->desktopWindows[j];
3106 fileName = ResolveLocalPathName( desktopWin->host,
3107 desktopWin->dir_linked_to,
3108 desktopWin->file_name,
3109 home_host_name, &tt_status);
3111 if( TT_OK == tt_status )
3113 if (strcmp(fileName, cb_data->path[i]) == 0)
3115 RemoveDT(desktopWin->shell, (XtPointer)desktopWin, NULL);
3117 RemoveMovedObjectFromDT(desktopWin->shell, (XtPointer)desktopWin,
3118 cb_data->file_count, cb_data->file_list);
3119 #endif /* SUN_PERF */
3128 buf = XtMalloc(strlen(dir_error)+3);
3129 sprintf(buf,"%s\n",dir_error);
3136 buf = XtMalloc(strlen(file_error)+3);
3137 sprintf(buf,"%s\n",file_error);
3141 buf = XtRealloc(buf,strlen(buf)+strlen(file_error)+3);
3142 sprintf(buf,"%s%s\n",buf,file_error);
3150 buf = XtMalloc(strlen(no_file_error)+3);
3151 sprintf(buf,"%s\n",no_file_error);
3155 buf = XtRealloc(buf,strlen(buf)+strlen(no_file_error)+3);
3156 sprintf(buf,"%s%s\n",buf,no_file_error);
3158 XtFree(no_file_error);
3164 buf = XtMalloc(strlen(sacred_error)+3);
3165 sprintf(buf,"%s\n",sacred_error);
3169 buf = XtRealloc(buf,strlen(buf)+strlen(sacred_error)+3);
3170 sprintf(buf,"%s%s\n",buf,sacred_error);
3172 XtFree(sacred_error);
3175 bufsize = strlen(buf);
3177 /* Update the trash information file, and the trash window */
3180 FILE * trashInfoFileId = fopen(TrashInfoFileName, "a+");
3181 if( trashInfoFileId != NULL )
3183 for (i = fileCount; i > 0; i--)
3185 if( WriteEntry(trashInfoFileId,
3186 trashCan[numTrashItems - i].external,
3187 trashCan[numTrashItems - i].filename) < 0 )
3190 fflush(trashInfoFileId);
3191 fclose(trashInfoFileId);
3192 if( trashFileMgrData )
3193 UpdateDirectory(NULL, trashFileMgrData->host,
3194 trashFileMgrData->current_directory);
3198 /* Check for any bad files; post an error dialog */
3201 /* send a reply to the message that triggered this operation, if any */
3203 if (cb_data->msg != 0) {
3205 Until Action is fixed.
3206 tttk_message_fail( cb_data->msg, TT_DESKTOP_EACCES, 0, 1 );
3208 tt_message_reply( cb_data->msg );
3209 tttk_message_destroy( cb_data->msg );
3211 if (cb_data->msg_cnt > 0) {
3212 for (i = 0 ; i < cb_data->msg_cnt ; i++) {
3213 tt_message_reply( cb_data->msg_list[i] ) ;
3214 tttk_message_destroy( cb_data->msg_list[i] ) ;
3216 XtFree((char *)cb_data->msg_list) ;
3217 cb_data->msg_cnt = 0 ;
3218 #endif /* SUN_PERF */
3221 /* If more items than can be displayed, let user know */
3224 char extraFiles[256];
3226 (void) sprintf(extraFiles, AdditionalHeader, badCount - 8);
3230 GETMESSAGE(27,97, "The following objects could not be placed in the trash can: \n"));
3233 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3234 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3236 XtFree ((char *) buf);
3238 /* send a reply to the message that triggered this operation, if any */
3240 if (cb_data->msg != 0) {
3241 tt_message_reply( cb_data->msg );
3242 tttk_message_destroy( cb_data->msg );
3244 if (cb_data->msg_cnt > 0) {
3245 for (i = 0 ; i < cb_data->msg_cnt ; i++) {
3246 tt_message_reply( cb_data->msg_list[i] ) ;
3247 tttk_message_destroy( cb_data->msg_list[i] ) ;
3249 XtFree((char *)cb_data->msg_list) ;
3250 cb_data->msg_cnt = 0 ;
3251 #endif /* SUN_PERF */
3256 /* Check for any files requiring user verification; post a prompt dialog */
3257 /* XXX Really should fail incoming ToolTalk request if user cancels any */
3265 /* If more items than can be displayed, let user know */
3266 if (verifyCount > 6)
3268 char extraFiles[256];
3270 (void) sprintf(extraFiles, AdditionalHeader, verifyCount - 6);
3271 AddString(&verifybuf, &verifybufsize, extraFiles, NULL);
3275 * Depending upon what type of files are to be displayed in the
3276 * dialog, choose the appropriate dialog text.
3278 if (vfiles && vdirs)
3279 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");
3281 header = GETMESSAGE(27,104, "You do not have write permission for the following file(s):\nDo you want to proceed?\n");
3283 header = GETMESSAGE(27,100, "The following folder(s) are not empty.\nDo you want to proceed?\n");
3285 tmpbuf = XtMalloc(strlen(header) + strlen(verifybuf) + 1);
3286 sprintf(tmpbuf, "%s%s", header, verifybuf);
3287 title = XtNewString(GETMESSAGE(27, 4, "Trash Can Warning"));
3288 dlog = (Widget)_DtMessageDialog(toplevel, title, tmpbuf, NULL, True,
3289 VerifyCancel, VerifyOk, NULL, HelpRequestCB,
3290 False, WARNING_DIALOG);
3292 XtFree ((char *) verifybuf);
3296 * Add array as userdata on the dialog.
3297 * NULL terminate the array.
3299 verifylist[verifyCount] = NULL;
3300 XtSetArg(args[0], XmNuserData, verifylist);
3301 XtSetValues(dlog, args, 1);
3304 /* free the callback data */
3305 XtFree((char *)cb_data->file_list);
3306 XtFree((char *)cb_data->path);
3307 XtFree((char *)cb_data->to);
3308 XtFree((char *)cb_data->rc);
3309 XtFree((char *)cb_data);
3315 /*--------------------------------------------------------------------
3317 * Start the background process and set up callback for the pipe.
3318 *------------------------------------------------------------------*/
3324 Boolean do_verify_checks,
3327 static char *pname = "MoveToTrash";
3328 MoveToTrashCBData *cb_data;
3332 if( !TrashInitialized )
3335 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
3340 /* set up callback data */
3341 cb_data = XtNew(MoveToTrashCBData);
3342 cb_data->file_list = file_list;
3343 cb_data->file_count = file_count;
3344 cb_data->done_count = 0;
3345 cb_data->path = (char **)XtCalloc(file_count, sizeof(char *));
3346 cb_data->to = (char **)XtCalloc(file_count, sizeof(char *));
3347 cb_data->rc = (int *)XtCalloc(file_count, sizeof(int));
3350 cb_data->msg_cnt = 0 ;
3351 if (global_msg_cnt > 0) {
3352 cb_data->msg_cnt = global_msg_cnt ;
3353 cb_data->msg_list = (Tt_message *)XtMalloc(sizeof(Tt_message) * cb_data->msg_cnt);
3354 memcpy((char *)cb_data->msg_list,(char *)global_msg_list, sizeof(Tt_message) *
3357 #endif /* SUN_PERF */
3362 /* fork the process that does the actual work */
3367 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
3368 pname, getppid(), getpid(), errno, strerror(errno));
3375 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
3377 close(pipe_fd[0]); /* child won't read from the pipe */
3379 MoveToTrashProcess(pipe_fd[1], file_list, file_count, do_verify_checks);
3382 DBGFORK(("%s: child exiting\n", pname));
3387 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
3389 /* parent: set up callback to get the pipe data */
3390 close(pipe_fd[1]); /* parent won't write the pipe */
3392 cb_data->child = pid;
3394 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
3395 pipe_fd[0], (XtPointer)XtInputReadMask,
3396 MoveToTrashPipeCB, (XtPointer)cb_data);
3400 /*====================================================================
3404 * Run a background process to restore files from the trash can.
3406 * These routines are used both for "normal restores", i.e.,
3407 * restores initiated from the Trash menu or through an ICCCM
3408 * message, as well as "drag&drop restores", i.e., restores done
3409 * by dragging files from the trash can to some other directory.
3410 * For normal resores, file_list contains only simple file names
3411 * (no paths) and target_dir is NULL. For drag&drop restores
3412 * file_list contains complete path names and target_dir contains
3413 * the name of the directoy to which the files should be moved.
3415 *==================================================================*/
3417 /*--------------------------------------------------------------------
3419 * Main routine of background process for RestoreFromTrash
3420 *------------------------------------------------------------------*/
3430 Boolean CheckedAlready)
3439 char **RestoreList= NULL;
3441 /* get full path name of target directory */
3444 Tt_status tt_status;
3445 full_dirname = ResolveLocalPathName(target_host, target_dir,
3446 NULL, home_host_name, &tt_status);
3447 if( TT_OK != tt_status )
3449 /* send return codes back trough the pipe */
3450 pipe_msg = PIPEMSG_DONE;
3452 DPRINTF(("RestoreProcess: Unable to Resolve local path name\n"));
3453 write(pipe_fd, &pipe_msg, sizeof(short));
3454 write(pipe_fd, rc, sizeof(int));
3459 full_dirname = NULL;
3461 /* restore the files */
3462 for (i = 0; i < file_count; i++)
3464 /* Locate file in trash list */
3465 for (j = 0; j < numTrashItems; j++)
3467 /* file_list[i] may be a complete path or just a file name */
3468 if (strcmp(file_list[i], trashCan[j].filename) == 0 ||
3469 strcmp(file_list[i], trashCan[j].intNew) == 0)
3475 /* determine source and target for the move */
3476 if (target_dir == NULL)
3478 /* this is a normal restore */
3479 if (j < numTrashItems)
3481 from = trashCan[j].intNew;
3482 to = trashCan[j].intOrig;
3486 /* can't do a restore if the file wasn't found in the trash list */
3492 /* this is a drag&drop from the trash can to target_dir */
3493 from = file_list[i];
3494 if (j < numTrashItems)
3495 ptr = strrchr(trashCan[j].intOrig, '/');
3497 ptr = strrchr(file_list[i], '/');
3498 strcpy(buf, full_dirname);
3506 status = RestoreObject((Widget)pipe_fd, MOVE_FILE, from,to,
3507 TRUE, FileOpError, False, NOT_DESKTOP,CheckedAlready);
3508 /* restore was successful */
3509 if(status == (int) True)
3511 else if(status == (int) False)
3517 /* restore failed */
3521 /* send return codes back trough the pipe */
3522 pipe_msg = PIPEMSG_DONE;
3523 DPRINTF(("RestoreProcess: sending DONE\n"));
3524 write(pipe_fd, &pipe_msg, sizeof(short));
3525 write(pipe_fd, rc, file_count * sizeof(int));
3527 XtFree(full_dirname);
3531 /*--------------------------------------------------------------------
3533 * Read and process data sent through the pipe.
3534 *------------------------------------------------------------------*/
3538 XtPointer client_data,
3542 RestoreFromTrashCBData *cb_data = (RestoreFromTrashCBData *)client_data;
3545 char *title, *err_msg, *err_arg;
3549 char **ToRestoreList=NULL;
3550 char **FromRestoreList=NULL;
3551 char *target_host,*target_dir;
3554 /* read the next msg from the pipe */
3556 n = PipeRead(*fd, &pipe_msg, sizeof(short));
3557 DPRINTF(("RestorePipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
3561 case PIPEMSG_FILEOP_ERROR:
3562 PipeRead(*fd, &rc, sizeof(int));
3563 err_msg = PipeReadString(*fd);
3564 err_arg = PipeReadString(*fd);
3565 FileOperationError(toplevel, err_msg, err_arg);
3566 /* This call will popup an error dialog.
3568 FileOperationError(toplevel, err_msg, err_arg);
3570 It's not appropriate at all to popup an error dialog here.
3571 i.e. if there're 1000 files, and the file system is full,
3572 and we're unable to move, would we want to popup 1000
3579 PipeRead(*fd, cb_data->rc, cb_data->file_count * sizeof(int));
3584 "Internal error in RestorePipeCB: bad pipe_msg %d\n",
3588 /* close the pipe and cancel the callback */
3589 DPRINTF(("RestorePipeCB: done\n"));
3596 for (i = 0; i < cb_data->file_count; i++)
3598 /* Locate file in trash list */
3599 for (j = 0; j < numTrashItems; j++)
3601 /* file_list[i] may be a complete path or just a file name */
3602 if (strcmp(cb_data->file_list[i], trashCan[j].filename) == 0 ||
3603 strcmp(cb_data->file_list[i], trashCan[j].intNew) == 0)
3609 if (cb_data->rc[i] == SKIP_FILE)
3611 ToRestoreList = (char **) XtRealloc((char *)ToRestoreList,sizeof(char *) *
3613 ToRestoreList[RestoreIndex-1] = XtNewString(trashCan[j].intOrig);
3614 FromRestoreList = (char **) XtRealloc((char *)FromRestoreList,sizeof(char *) *
3616 FromRestoreList[RestoreIndex-1] = XtNewString( trashCan[j].intNew );
3619 /* Check the return code from the restore */
3620 else if (cb_data->rc[i] == 0)
3622 /* restore was successful: remove the file from the trash list */
3623 if (j < numTrashItems)
3625 /* arrange for the source directory to be updated */
3626 UpdateDirectoryOf(trashCan[j].intOrig);
3628 /* Remove this entry from the trash list */
3629 XtFree ((char *) trashCan[j].intNew);
3630 XtFree ((char *) trashCan[j].intOrig);
3631 XtFree ((char *) trashCan[j].external);
3632 XtFree ((char *) trashCan[j].filename);
3633 for (k = j; k < (numTrashItems - 1); k++)
3634 trashCan[k] = trashCan[k + 1];
3641 char *restore_header,*tmpStr = GETMESSAGE(27,101,
3642 "The following object(s) could not be put back:\n");
3644 restore_header = XtNewString(tmpStr);
3645 if (j < numTrashItems && cb_data->target_dir == NULL)
3646 AddString(&buf, &bufsize, trashCan[j].external, restore_header);
3648 AddString(&buf, &bufsize, cb_data->file_list[i], restore_header);
3649 XtFree(restore_header);
3653 /* Update the trash information file */
3654 if( ! WriteTrashEntries() )
3656 char * tmpStr, * title;
3658 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3659 tmpStr = XtNewString(GETMESSAGE(27, 88, "Cannot write to a temporary file.\nPerhaps your file system is full.\n"));
3660 _DtMessage(toplevel, title, tmpStr, NULL, HelpRequestCB);
3665 /* send a reply to the message that triggered this operation, if any */
3666 if (cb_data->msg != 0) {
3667 tt_message_reply( cb_data->msg );
3668 tttk_message_destroy( cb_data->msg );
3672 /* Report any errors */
3675 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3676 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3678 XtFree ((char *) buf);
3681 UpdateDirectory(NULL, trashFileMgrData->host,
3682 trashFileMgrData->current_directory);
3683 if (cb_data->target_dir)
3684 UpdateDirectory(NULL, cb_data->target_host, cb_data->target_dir);
3686 target_host = XtNewString(cb_data->target_host);
3687 target_dir = XtNewString(cb_data->target_dir);
3689 /* free the callback data */
3690 for (i = 0; i < cb_data->file_count; i++)
3691 XtFree(cb_data->file_list[i]);
3692 XtFree((char *)cb_data->file_list);
3693 XtFree((char *)cb_data->target_host);
3694 XtFree((char *)cb_data->target_dir);
3695 XtFree((char *)cb_data->rc);
3696 XtFree((char *)cb_data);
3699 for(i=0;i<RestoreIndex;i++)
3701 CreateRestoreDialog(FromRestoreList[i],ToRestoreList[i]);
3702 XtFree(FromRestoreList[i]);
3703 XtFree(ToRestoreList[i]);
3705 XtFree((char *) FromRestoreList);
3706 XtFree((char *) ToRestoreList);
3708 XtFree(target_host);
3713 /*--------------------------------------------------------------------
3715 * Start the background process and set up callback for the pipe.
3716 *------------------------------------------------------------------*/
3725 Boolean CheckedAlready)
3727 static char *pname = "RestoreFromTrash";
3728 RestoreFromTrashCBData *cb_data;
3732 if( !TrashInitialized )
3735 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
3740 /* set up callback data */
3741 cb_data = XtNew(RestoreFromTrashCBData);
3742 cb_data->file_list = file_list;
3743 cb_data->file_count = file_count;
3744 cb_data->target_host = target_host;
3745 cb_data->target_dir = target_dir;
3746 cb_data->rc = (int *)XtCalloc(file_count, sizeof(int));
3752 /* fork the process that does the actual work */
3757 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
3758 pname, getppid(), getpid(), errno, strerror(errno));
3765 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
3767 close(pipe_fd[0]); /* child won't read from the pipe */
3769 RestoreProcess(pipe_fd[1], file_list, file_count,
3770 target_host, target_dir, cb_data->rc,CheckedAlready);
3773 DBGFORK(("%s: child exiting\n", pname));
3778 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
3780 /* parent: set up callback to get the pipe data */
3781 close(pipe_fd[1]); /* parent won't write the pipe */
3783 cb_data->child = pid;
3785 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
3786 pipe_fd[0], (XtPointer)XtInputReadMask,
3787 RestorePipeCB, (XtPointer)cb_data);
3792 /*====================================================================
3795 * Run a background process to remove files from the trash can.
3797 *==================================================================*/
3799 /*--------------------------------------------------------------------
3801 * Main routine of background process for EmptyTrash
3802 *------------------------------------------------------------------*/
3807 DeleteList * del_list,
3815 * Delete all files or directories in the list,
3816 * as well as any associated annotations.
3818 for (i = 0; i < del_count; i++)
3820 /* delete the file */
3821 rc[i] = EraseObject(del_list[i].trash);
3824 /* send return codes back trough the pipe */
3825 pipe_msg = PIPEMSG_DONE;
3826 DPRINTF(("EmptyTrashProcess: sending DONE\n"));
3827 write(pipe_fd, &pipe_msg, sizeof(short));
3828 write(pipe_fd, rc, del_count * sizeof(int));
3832 /*--------------------------------------------------------------------
3834 * Read and process data sent through the pipe.
3835 *------------------------------------------------------------------*/
3839 XtPointer client_data,
3843 EmptyTrashCBData *cb_data = (EmptyTrashCBData *)client_data;
3846 char *title, *err_msg, *err_arg;
3847 int problemCount, itemCount;
3852 /* read the next msg from the pipe */
3854 n = PipeRead(*fd, &pipe_msg, sizeof(short));
3855 DPRINTF(("EmptyTrashPipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
3859 case PIPEMSG_FILEOP_ERROR:
3860 PipeRead(*fd, &rc, sizeof(int));
3861 err_msg = PipeReadString(*fd);
3862 err_arg = PipeReadString(*fd);
3863 /* This call will popup an error dialog.
3865 FileOperationError(toplevel, err_msg, err_arg);
3867 It's not appropriate at all to popup an error dialog here.
3868 i.e. if there're 1000 files, and the file system is full,
3869 and we're unable to move, would we want to popup 1000
3877 PipeRead(*fd, cb_data->rc, cb_data->del_count * sizeof(int));
3882 "Internal error in EmptyTrashPipeCB: bad pipe_msg %d\n",
3886 /* close the pipe and cancel the callback */
3887 DPRINTF(("EmptyTrashPipeCB: done\n"));
3896 if (cb_data->removeType == TRASH_FILE)
3898 for (i = 0; i < cb_data->del_count; i++)
3900 /* Locate file in trash list */
3901 for (j = 0; j < numTrashItems; j++)
3902 if (strcmp(cb_data->del_list[i].trash, trashCan[j].intNew) == 0)
3905 /* Check the return code from the erase */
3906 if (cb_data->rc[i] == 0)
3908 /* erase was successful: remove the file from the trash list */
3909 if (j < numTrashItems)
3911 /* Remove this entry from the trash list */
3912 XtFree ((char *) trashCan[j].intNew);
3913 XtFree ((char *) trashCan[j].intOrig);
3914 XtFree ((char *) trashCan[j].external);
3915 XtFree ((char *) trashCan[j].filename);
3916 for (k = j; k < (numTrashItems - 1); k++)
3917 trashCan[k] = trashCan[k + 1];
3928 char * msg = XtNewString(GETMESSAGE(27,96, "The following objects could not be removed from the file system: \n"));
3930 if (j < numTrashItems)
3931 file_name = trashCan[j].external;
3933 file_name = cb_data->del_list[i].trash;
3934 AddString(&buf, &bufsize, file_name, msg);
3941 /* Update the trash information file */
3942 if( ! WriteTrashEntries() )
3944 char * tmpStr, * title;
3946 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3947 tmpStr = XtNewString(GETMESSAGE(27, 88, "Cannot write to a temporary file.\nPerhaps your file system is full.\n"));
3948 _DtMessage(toplevel, title, tmpStr, NULL, HelpRequestCB);
3953 /* Report any errors */
3956 /* If more items than can be displayed, let user know */
3957 if (itemCount < problemCount)
3959 char extraFiles[256];
3960 sprintf(extraFiles, AdditionalHeader, problemCount - itemCount);
3964 GETMESSAGE(27,97, "The following objects could not be placed in the trash can: \n") );
3967 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3968 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3970 XtFree ((char *) buf);
3973 if (trashFileMgrData->selected_file_count > 0)
3975 XtSetSensitive(*removeBtn, True);
3976 XtSetSensitive(*restoreBtn, True);
3979 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
3980 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
3982 UpdateDirectory(NULL, trashFileMgrData->host,
3983 trashFileMgrData->current_directory);
3987 if (cb_data->rc[0] == 0)
3989 /* arrange for the directory containing the file to be updated */
3990 UpdateDirectoryOf(cb_data->del_list[0].trash);
3994 /* put up an error message saying the file couldn't be removed */
3998 tmpStr = GETMESSAGE(27, 81, "Shred File/Folder Error");
3999 title = XtNewString(tmpStr);
4000 tmpStr = GETMESSAGE(27, 82, " The following file could not be removed from the file system: \n");
4001 file_name = cb_data->del_list[0].trash;
4002 buffer = XtMalloc(strlen(tmpStr) + strlen(file_name) + 1);
4003 sprintf(buffer, "%s%s\n", tmpStr, file_name);
4004 _DtMessage(toplevel, title, buffer, NULL, HelpRequestCB);
4011 /* reset removingTrash flag */
4012 removingTrash = False;
4014 /* send a reply to the message that triggered this operation, if any */
4015 if (cb_data->msg != 0) {
4016 tt_message_reply( cb_data->msg );
4017 tttk_message_destroy( cb_data->msg );
4021 /* free the callback data */
4022 for (i = 0; i < cb_data->del_count; i++)
4024 XtFree(cb_data->del_list[i].trash);
4025 XtFree(cb_data->del_list[i].orig);
4027 XtFree((char *)cb_data->del_list);
4028 XtFree((char *)cb_data->rc);
4029 XtFree((char *)cb_data);
4033 /*--------------------------------------------------------------------
4035 * Start the background process and set up callback for the pipe.
4036 *------------------------------------------------------------------*/
4040 DeleteList *del_list,
4045 static char *pname = "EmptyTrash";
4046 EmptyTrashCBData *cb_data;
4050 if( !TrashInitialized )
4053 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
4058 /* set removingTrash flag */
4059 removingTrash = True;
4061 /* set up callback data */
4062 cb_data = XtNew(EmptyTrashCBData);
4063 cb_data->del_list = del_list;
4064 cb_data->del_count = del_count;
4065 cb_data->removeType = removeType;
4066 cb_data->rc = (int *)XtCalloc(del_count, sizeof(int));
4072 /* fork the process that does the actual work */
4077 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
4078 pname, getppid(), getpid(), errno, strerror(errno));
4085 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
4087 close(pipe_fd[0]); /* child won't read from the pipe */
4089 EmptyTrashProcess(pipe_fd[1], del_list, del_count, cb_data->rc);
4092 DBGFORK(("%s: child exiting\n", pname));
4097 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
4099 /* parent: set up callback to get the pipe data */
4100 close(pipe_fd[1]); /* parent won't write the pipe */
4102 cb_data->child = pid;
4104 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
4105 pipe_fd[0], (XtPointer)XtInputReadMask,
4106 EmptyTrashPipeCB, (XtPointer)cb_data);
4110 CheckDeletePermission(
4112 char *destinationPath)
4114 #if defined(CSRG_BASED)
4115 struct statfs statbuf;
4117 struct stat statbuf;
4121 #if defined(CSRG_BASED)
4122 if (statfs(parentdir,&statbuf) < 0) /* does not exist */
4124 if (lstat(parentdir,&statbuf) < 0) /* does not exist */
4128 /* check if we are root */
4131 /* if NFS, need to check if server trusts root */
4132 #if defined(CSRG_BASED)
4133 if (!strcmp(statbuf.f_fstypename, "nfs")) /* Root user and nfs */
4135 if (FileSysType(statbuf.st_dev) < 0) /* Root user and nfs */
4139 tmpfile = tempnam(parentdir,"quang");
4140 if (creat(tmpfile,O_RDONLY)< 0) /* Create a temporary file */
4142 if (remove(tmpfile) < 0) /* Delete the created file */
4146 /* root user can delete anything */
4150 /* check for write and execute permisssion on parent dir */
4151 if (CheckAccess(parentdir, W_OK | X_OK) < 0)
4154 /* copy destinationPath to tmp buffer */
4155 strcpy(fname, destinationPath);
4157 return CheckDeletePermissionRecur(fname);
4162 CheckDeletePermissionRecur(
4163 char *destinationPath)
4165 struct stat statbuf;
4171 DPRINTF(("CheckDeletePermissionRecur(\"%s\")\n", destinationPath));
4173 if (lstat(destinationPath, &statbuf) < 0)
4174 return -1; /* probably does not exist */
4176 if (! S_ISDIR(statbuf.st_mode))
4177 return 0; /* no need to check anything more */
4179 dirp = opendir (destinationPath);
4181 return -1; /* could not read directory */
4186 while (dp = readdir (dirp))
4188 if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
4192 /* check for write permission in this directory */
4193 if (CheckAccess(destinationPath, W_OK|X_OK) < 0)
4196 /* append a '/' to the end of directory name */
4197 fnamep = destinationPath + strlen(destinationPath);
4203 /* append file name to end of directory name */
4204 strcpy(fnamep, dp->d_name);
4206 /* recursively check permission on this file */
4207 if (CheckDeletePermissionRecur(destinationPath))
4215 #if !defined(CSRG_BASED)
4221 if(ustat(dev,&u1) < 0)
4231 register char *source,
4232 register char *target,
4233 Boolean isContainer,
4234 void (*errorHandler)(),
4235 Boolean checkForBusyDir,
4237 Boolean CheckedAlready)
4239 struct stat statsrc,stattar;
4241 char *localdir,*chrptr;
4245 if(stat(source,&statsrc) < 0)
4246 if(lstat(source,&statsrc) < 0)
4249 localdir = strdup(target);
4251 chrptr = strrchr(localdir,'/');
4254 if(chrptr == localdir) /* Must be root folder */
4259 if(stat(target,&stattar) >= 0) /* Target exists */
4261 if(CheckDeletePermission(localdir,target))
4262 return ((int)False);
4267 return ((int )FileManip((Widget)w, MOVE_FILE, source, target, TRUE,
4268 FileOpError, False, NOT_DESKTOP));
4271 CreateRestoreDialog(
4275 char *tmpbuf,*title;
4279 char **dirs = (char **) malloc(sizeof(char *) * 2);
4280 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.");
4282 dirs[0] = strdup(source);
4283 dirs[1] = strdup(target);
4284 tmpbuf = XtMalloc(strlen(header) + strlen(target) + 1);
4285 sprintf(tmpbuf, header, target);
4286 title = XtNewString(GETMESSAGE(27, 109, "Put Back Warning"));
4287 dw = (Widget)_DtMessageDialog(toplevel, title,tmpbuf,NULL,True,RestoreVerifyCancel,
4288 RestoreVerifyOk, NULL, HelpRequestCB, False, WARNING_DIALOG);
4290 XtSetArg(args[n], XmNuserData,dirs); n++;
4291 XtSetValues(dw,args,n);
4299 XtPointer client_data,
4300 XtPointer call_data )
4305 Widget mbox = (Widget)client_data;
4307 XtSetArg(args[n],XmNuserData,&dirs); n++;
4308 XtGetValues(mbox,args,n);
4310 if(DirectoryBusy(dirs[1]))
4313 char * tmpStr,*title;
4315 title = XtNewString(GETMESSAGE(27,111,"Put Back Error"));
4316 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.");
4317 msgbuf = XtMalloc(strlen(tmpStr) + strlen(dirs[1])+1);
4318 sprintf (msgbuf, tmpStr, dirs[1]);
4319 _DtMessage(toplevel,title,msgbuf,NULL,HelpRequestCB);
4327 char *realTarget,*tptr;
4331 if(lstat(dirs[1],&s1) < 0)
4333 fsErase(dirs[1],&status);
4334 FileList = (char **) XtMalloc(sizeof(char *));
4335 FileList[0] = XtNewString(dirs[0]);
4336 RestoreFromTrash(FileList, (int) 1, NULL, NULL, NULL,True);
4342 XtDestroyWidget(mbox);
4346 RestoreVerifyCancel(
4348 XtPointer client_data,
4349 XtPointer call_data )
4355 Widget mbox = (Widget)client_data;
4357 XtSetArg(args[n],XmNuserData,&dirs); n++;
4358 XtGetValues(mbox,args,n);
4362 XtDestroyWidget(mbox);