2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $TOG: Trash.c /main/12 1999/12/09 13:07:25 mgreess $ */
24 /************************************<+>*************************************
25 ****************************************************************************
29 * COMPONENT_NAME: Desktop File Manager (dtfile)
31 * Description: Source file for the trash code.
33 * FUNCTIONS: AddToDeleteList
34 * CheckDeletePermission
35 * CheckDeletePermissionRecur
51 * MatchesSacredDirectory
75 * TrashRemoveNoConfirmHandler
85 * (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
86 * (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
87 * (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
88 * (c) Copyright 1993, 1994, 1995 Novell, Inc.
90 ****************************************************************************
91 ************************************<+>*************************************/
93 #include <EUSCompat.h>
95 #include <sys/types.h>
104 #if defined(CSRG_BASED)
105 #include <sys/param.h>
106 #include <sys/mount.h>
111 #include <Xm/RowColumn.h>
112 #include <Xm/CascadeB.h>
114 #include <Xm/Frame.h>
115 #include <Xm/MainW.h>
116 #include <Xm/PushBG.h>
117 #include <Xm/SeparatoG.h>
118 #include <Xm/MessageB.h>
119 #include <Xm/MwmUtil.h>
121 #include <X11/Shell.h>
122 #include <X11/Xatom.h>
123 #include <Xm/Protocols.h>
125 #include <Dt/Action.h>
127 #include <Dt/FileM.h>
128 #include <Dt/DtNlUtils.h>
129 #include <Dt/Connect.h>
130 #include <Dt/Indicator.h>
131 #include <Dt/EnvControlP.h>
134 #include <Dt/SharedProcs.h>
135 #include "DtSvcInternal.h" /* _DtGetMask */
140 #include "SharedProcs.h"
141 #include "MultiView.h"
146 #include "SharedMsgs.h"
147 #include "dtcopy/fsrtns.h"
149 #define AdditionalHeader (GETMESSAGE(27,98, "(Plus %d additional object(s))"))
151 /* Trash file errors */
152 #define NO_FILE_ERROR 0
153 #define BAD_FILE_ERROR -1
154 #define VERIFY_DIR -2
155 #define VERIFY_FILE -3
156 #define BAD_TRASH_DIRECTORY -4
157 #define BAD_TRASH_FILE -5
158 #define NO_TRASH_FILE -6
161 #define BAD_FILE_SACRED -9
163 /* types of messages sent through the pipe */
164 #define PIPEMSG_FILEOP_ERROR 1
165 #define PIPEMSG_OTHER_ERROR 3
166 #define PIPEMSG_DONE 7
167 #define PIPEMSG_TARGET_TIME 5
168 #define PIPEMSG_FILE_MODIFIED 6
171 * Structure describing each file in the trash can.
172 * Includes the external host:/name format, the original internal
173 * /nfs/host/name format, and the internal /nfs/host/new_name describing
174 * the temporary location of the file. The `problem' flag is used to
175 * indicate if a problem occurred when the file was physically being
187 /* callback data MoveToTrash */
198 Tt_message *msg_list ;
200 #endif /* SUN_PERF */
207 Tt_message *msg_list ;
210 #endif /* SUN_PERF */
212 /* callback data RestoreFromTrash */
222 } RestoreFromTrashCBData;
224 /* callback data EmptyTrash */
233 DeleteList *del_list;
242 DialogData *trashDialogData; /* fm data associated with trash dialog */
243 FileMgrData *trashFileMgrData = NULL;
245 Boolean removingTrash = False;
247 DialogData * primaryTrashHelpDialog = NULL;
248 DialogData ** secondaryTrashHelpDialogList = NULL;
249 int secondaryTrashHelpDialogCount = 0;
251 /* Forward prototype */
252 int EraseDir(char *dir_name);
253 /* From dtcopy/fsrtns.c */
254 extern int EmptyDir(char *sourceP, int rm, int force);
256 /* 'defines' for trash files */
257 static char * TRASH_DIR = ".dt/Trash";
258 static char * TRASH_INFO_FILE = ".dt/Trash/.trashinfo";
259 static char * NEW_TRASH_INFO_FILE = ".dt/.tmptrashinfo";
261 static char * RM = "/bin/rm";
262 static char * RM_ARGS = "-rf";
264 static Widget * selectAllBtn = NULL;
265 static Widget * restoreBtn = NULL;
266 static Widget * removeBtn = NULL;
268 static int trashMenuItemCount = 22; /* trash menu items */
269 static MenuDesc * trashMenu = NULL;
271 static TrashEntry * trashCan = NULL;
272 static int trashListSize = 0;
273 static int numTrashItems = 0;
275 static Boolean trashDialogPosted;
277 static char * TrashInfoFileName = NULL;
278 static char * NewTrashInfoFileName = NULL;
280 static char ** sacred_dir_list = NULL; /* list of directories in trash path */
281 static int sacred_dir_list_size = 0;
282 static int sacred_dir_count = 0;
284 static Boolean verifyPromptsEnabled; /* do we prompt the user? */
286 static Tt_message global;
288 static Tt_message *global_msg_list ;
289 static int global_msg_cnt = 0 ;
290 #endif /* SUN_PERF */
291 static Boolean TrashInitialized = False;
293 /******** Static Function Declarations ********/
295 static String CreateTrashFilename(
298 static void MessageToFileList(
303 static void MessagesToFileList(
304 Tt_message *msg_list,
308 #endif /* SUN_PERF */
309 static int WriteEntry(
313 static Boolean MatchesSacredDirectory(
315 static void VerifyCleanup(
317 Boolean completeDelete) ;
318 static void VerifyCancel(
320 XtPointer client_data,
321 XtPointer call_data) ;
322 static void VerifyOk(
324 XtPointer client_data,
325 XtPointer call_data) ;
326 static Boolean WriteTrashEntries( void ) ;
327 static void Select_All(
329 XtPointer client_data,
330 XtPointer call_data) ;
331 static void Unselect_All(
333 XtPointer client_data,
334 XtPointer call_data) ;
337 XtPointer client_data,
338 XtPointer call_data) ;
339 static String GetBasePath(
341 static Boolean ReadTrashList( void ) ;
342 static void RemoveOkCB(
344 XtPointer client_data,
345 XtPointer call_data ) ;
346 static void RemoveCancelCB(
348 XtPointer client_data,
349 XtPointer call_data ) ;
350 static void ConfirmOk(
352 XtPointer client_data,
353 XtPointer call_data ) ;
354 static void ConfirmCancel(
356 XtPointer client_data,
357 XtPointer call_data ) ;
360 XtPointer clientData,
361 XtPointer callData) ;
362 static void AddToDeleteList(
363 DeleteList *deleteList,
366 static void MoveToTrash(
369 Boolean do_verify_checks,
371 static void RestoreFromTrash(
377 Boolean CheckedAlready) ;
378 static void EmptyTrash(
379 DeleteList *del_list,
383 static int CheckDeletePermissionRecur(
385 static int FileSysType(int dev);
386 static void RestoreVerifyOk(
388 XtPointer client_data,
389 XtPointer call_data ) ;
390 static void RestoreVerifyCancel(
392 XtPointer client_data,
393 XtPointer call_data ) ;
394 static int RestoreObject(
397 register char *source,
398 register char *target,
400 void (*errorHandler)(),
401 Boolean checkForBusyDir,
403 Boolean CheckedAlready);
404 static void CreateRestoreDialog(
408 /******** End Static Function Declarations ********/
411 TrashIsInitialized( void )
413 return( TrashInitialized );
417 InitializeTrash( Boolean enableVerifyPrompt )
420 struct stat statInfo;
422 /* Build the 'TRASH' directory */
423 trash_dir = XtMalloc(strlen(users_home_dir) +
426 sprintf(trash_dir, "%s%s", users_home_dir, TRASH_DIR);
427 if (stat(trash_dir, &statInfo) < 0)
428 mkdir(trash_dir, S_IRUSR | S_IWUSR | S_IXUSR |
429 S_IRGRP | S_IWGRP | S_IXGRP |
430 S_IROTH | S_IWOTH | S_IXOTH);
432 /* build path to .trashinfo file */
433 TrashInfoFileName = XtMalloc(strlen(users_home_dir) + strlen(TRASH_INFO_FILE) + 1);
434 sprintf(TrashInfoFileName, "%s%s", users_home_dir, TRASH_INFO_FILE);
437 NewTrashInfoFileName = XtMalloc(strlen(users_home_dir) +
438 strlen(NEW_TRASH_INFO_FILE)
440 sprintf(NewTrashInfoFileName, "%s%s", users_home_dir, NEW_TRASH_INFO_FILE);
442 /* Keep track of whether to prompt for user verification */
443 verifyPromptsEnabled = enableVerifyPrompt;
446 * Create an array of paths and filenames which we will not allow the
447 * user to delete, because deleting any of them will cause trash to
450 sacred_dir_list_size = 5;
451 sacred_dir_list = (char **)XtMalloc(sizeof(char *) * sacred_dir_list_size);
453 sacred_dir_list[sacred_dir_count++] = XtNewString("/");
454 sacred_dir_list[sacred_dir_count++] = XtNewString(TrashInfoFileName);
456 ptr = TrashInfoFileName + 1;
457 while(ptr = DtStrchr(ptr, '/'))
459 /* All parent components of the user's home dir cannot be deleted */
461 if (sacred_dir_count >= sacred_dir_list_size)
464 sacred_dir_list_size += 5;
465 sacred_dir_list = (char **)XtRealloc((char *)sacred_dir_list,
466 sizeof(char *) * sacred_dir_list_size);
468 sacred_dir_list[sacred_dir_count++] = XtNewString(TrashInfoFileName);
473 /* load and verify existence for files previously left in the trash can */
474 TrashInitialized = ReadTrashList( );
475 return( TrashInitialized );
478 /************************************************************************
481 * This function must be called before any other interatctions with the
482 * trash can. It creates the trash dialog, opens the trash file, and
483 * loads information about any files previously left in the trash can.
485 * This function is called from main().
487 ************************************************************************/
495 DialogData * dialog_data;
496 FileMgrData * file_mgr_data;
498 if( ! TrashInitialized )
501 /* Create the trash dialog window */
502 /* load trash title */
503 tmpStr = GETMESSAGE(27, 9, "Trash Can");
504 title = XtNewString(tmpStr);
506 /* Initially, no items can be selected */
507 if (removeBtn != NULL)
508 XtSetSensitive(*removeBtn, False);
509 if (restoreBtn != NULL)
510 XtSetSensitive(*restoreBtn, False);
512 /* retrieve the default data for a file manager dialog -- */
513 /* the TrashView flag affects this data */
515 dialog_data = _DtGetDefaultDialogData (file_mgr_dialog);
516 file_mgr_data = (FileMgrData *) dialog_data->data;
517 trashFileMgrData = file_mgr_data;
518 trashDialogData = dialog_data;
520 /* build up directory set for trash directory */
521 FileMgrBuildDirectories (file_mgr_data, home_host_name, trash_dir);
523 /* initialize trash data */
524 file_mgr_data->restricted_directory =
525 XtNewString(file_mgr_data->current_directory);
526 file_mgr_data->title = XtNewString(title);
527 file_mgr_data->toolbox = False;
528 file_mgr_data->width = 300;
529 file_mgr_data->height = 300;
531 /* load any positioning information */
532 LoadPositionInfo(file_mgr_data);
534 /* build the trash dialog */
535 _DtBuildDialog (NULL, NULL, NULL, dialog_data, NULL, NULL, NULL,
536 NULL, NULL, False, special_view, title, NULL);
544 /************************************************************************
547 * At startup time, we need to read in the trash information file, to
548 * find out what, if any, files were left in the trash the last time
549 * this client was run. Each entry must be read in and then verified
550 * that it still exists; if it no longer exists, then someone has been
551 * mucking with our trash files, so we will remove it from our knowledge
552 * base. Otherwise, the file is added to the trash list.
554 ************************************************************************/
557 ReadTrashList( void )
559 int intSize, extSize, bufSize;
560 FILE * trashInfoFileId;
561 String external, intName, internal;
565 trashInfoFileId = fopen(TrashInfoFileName, "a+");
566 if (trashInfoFileId == 0)
570 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
572 msg = XtMalloc( strlen( GETMESSAGE(27,93, "Unable to access this trash information file:\n %s\n All trash operations will not be performed.\n The most common causes are:\n - Network authentication.\n - Insufficient disk space.\n - Wrong permissions $HOME/.dt/Trash.") )
573 + strlen( TrashInfoFileName )
576 sprintf( msg, GETMESSAGE(27, 93, "Unable to access this trash information file:\n %s\n All trash operations will not be performed.\n The most common causes are:\n - Network authentication.\n - Insufficient disk space.\n - Wrong permissions $HOME/.dt/Trash."), TrashInfoFileName );
578 _DtMessage(toplevel, title, msg, NULL, HelpRequestCB);
584 chmod(TrashInfoFileName, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
586 bufSize = MAX_PATH * 2;
587 trashEntry = (char *)XtCalloc( 1, bufSize );
589 /* Extract the size of the two path strings */
590 while( fgets( trashEntry, bufSize, trashInfoFileId ) != NULL )
592 int len = strlen( trashEntry );
594 if( sscanf( trashEntry, "%d %d", &extSize, &intSize ) == 2 )
596 external = (char *)XtCalloc( 1, extSize + 1 );
597 intName = (char *)XtCalloc( 1, intSize + 1 );
599 if( len > extSize + intSize + 3 )
600 /* extSize + intSize + 3 is a rough estimate of a trash entry
601 a trash entry looks something like the following:
605 extSize is the size of the string '/tmp/abc'
606 intSize is the size of the string 'abcd'
608 so the len (the string length of the trash entry has AT LEAST to be
609 bigger than extSize + intSize + 3 separator spaces
614 /* sscanf can be used to reduce the code.
615 I'm not using it here because I don't have time to research.
616 I just want it to work.
618 tmpPtr = DtStrchr( trashEntry, ' ' ) + 1;
619 tmpPtr = DtStrchr( tmpPtr, ' ' ) + 1;
621 /* Extract the full external and partial internal file names */
622 memcpy( external, tmpPtr, extSize );
623 memcpy( intName, tmpPtr + extSize + 1, intSize );
625 /* Create internal/trash name */
626 internal = (char *)XtMalloc(strlen(users_home_dir)
628 + strlen(intName) + 2);
629 sprintf(internal, "%s%s/%s", users_home_dir, TRASH_DIR, intName);
631 /* Make sure the file still exists */
632 if (lstat(internal, &statbuf) < 0)
634 /* File no longer exists; ignore it */
641 /* Add to trash list */
642 if (numTrashItems >= trashListSize)
645 trashCan = (TrashEntry *)XtRealloc((char *) trashCan,
646 sizeof(TrashEntry) * trashListSize);
649 trashCan[numTrashItems].intNew = internal;
650 trashCan[numTrashItems].intOrig = XtNewString(external);
651 trashCan[numTrashItems].external = external;
652 trashCan[numTrashItems].filename = intName;
654 } /* end if file exists */
663 fclose(trashInfoFileId);
665 return( WriteTrashEntries() );
670 /************************************************************************
673 * This function is responsible for creating a new copy of the trash
674 * information file. It will open a new, temporary copy, and will
675 * copy in the contents of the trash can. It will then remove the
676 * old trash information file, and will rename the new one, opening
677 * a handle to it for later use.
679 ************************************************************************/
682 WriteTrashEntries( void )
684 static String path = NULL;
688 newFile = fopen(NewTrashInfoFileName, "w+");
691 chmod(NewTrashInfoFileName,
692 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
694 /* Write all remaining entries */
695 for (i = 0; i < numTrashItems; i++)
697 if( WriteEntry(newFile, trashCan[i].external, trashCan[i].filename) < 0 )
700 remove(NewTrashInfoFileName);
705 /* Remove the original information file, and move the new one */
706 (void) fclose(newFile);
707 if(-1 == chown(NewTrashInfoFileName, getuid(), getgid())) {
710 (void) remove(TrashInfoFileName);
711 (void) rename(NewTrashInfoFileName, TrashInfoFileName);
712 if(-1 == chown(TrashInfoFileName, getuid(), getgid())) {
723 /************************************************************************
726 * This function writes an entry to the trash information file.
727 * It expects the full path name for the original file [external]
728 * and only the new file name (without path information) [internal].
729 * If the file ends in '/', then it adds a '.' to the end; this is
730 * a special case, so that when we parse this information later, we
731 * are guaranteed to have a directory and a file name.
733 ************************************************************************/
741 return( fprintf(id, "%ld %ld %s %s\n",
742 (long)strlen(external), (long)strlen(internal),
743 external, internal) );
748 /************************************************************************
752 ************************************************************************/
757 FileMgrRec *file_mgr_rec )
762 static Widget * directoryBarBtn;
764 /* Create the menubar hierarchy */
765 trashMenu = (MenuDesc *)XtMalloc(sizeof(MenuDesc) * trashMenuItemCount);
768 for (i = 0; i < trashMenuItemCount; i++)
770 trashMenu[i].helpCallback = TrashHelpRequestCB;
771 trashMenu[i].helpData = NULL;
772 trashMenu[i].activateCallback = NULL;
773 trashMenu[i].activateData = NULL;
774 trashMenu[i].maskBit = 0;
775 trashMenu[i].isHelpBtn = False;
776 trashMenu[i].label = NULL;
777 trashMenu[i].mnemonic = NULL;
781 /*************************************/
782 /* Create the 'File' menu components */
783 /*************************************/
785 directoryBarBtn = &(trashMenu[j].widget);
786 trashMenu[j].type = MENU_PULLDOWN_BUTTON;
787 trashMenu[j].label = GETMESSAGE(20,1, "File");
788 trashMenu[j].label = XtNewString(trashMenu[j].label);
789 trashMenu[j].mnemonic = GETMESSAGE(20,2, "F");
790 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
791 trashMenu[j].name = "file_trash";
792 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
794 trashMenu[j].type = SHARED_MENU_PANE;
795 trashMenu[j].name = "fileMenuTrash";
796 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
798 selectAllBtn = &(trashMenu[j].widget);
799 trashMenu[j].type = MENU_BUTTON;
800 trashMenu[j].label = GETMESSAGE(20,48, "Select All");
801 trashMenu[j].label = XtNewString(trashMenu[j].label);
802 trashMenu[j].mnemonic = GETMESSAGE(20,51, "S");
803 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
804 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
805 trashMenu[j].name = "selectAll";
806 trashMenu[j++].activateCallback = Select_All;
808 trashMenu[j].type = MENU_BUTTON;
809 trashMenu[j].label = GETMESSAGE(20,52, "Deselect All");
810 trashMenu[j].label = XtNewString(trashMenu[j].label);
811 trashMenu[j].mnemonic = GETMESSAGE(20,55, "D");
812 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
813 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
814 trashMenu[j].name = "deselectAll";
815 trashMenu[j++].activateCallback = Unselect_All;
817 trashMenu[j].type = MENU_SEPARATOR;
818 trashMenu[j].name = "separator";
819 trashMenu[j++].helpCallback = NULL;
821 restoreBtn = &(trashMenu[j].widget);
822 trashMenu[j].type = MENU_BUTTON;
823 trashMenu[j].label = GETMESSAGE(27,24, "Put back");
824 trashMenu[j].label = XtNewString(trashMenu[j].label);
825 trashMenu[j].mnemonic = GETMESSAGE(27,26, "P");
826 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
827 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
828 trashMenu[j].name = "putBack";
829 trashMenu[j++].activateCallback = Restore;
831 removeBtn = &(trashMenu[j].widget);
832 trashMenu[j].type = MENU_BUTTON;
833 trashMenu[j].label = GETMESSAGE(27,28, "Shred");
834 trashMenu[j].label = XtNewString(trashMenu[j].label);
835 trashMenu[j].mnemonic = GETMESSAGE(27,30, "h");
836 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
837 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
838 trashMenu[j].name = "shred";
839 trashMenu[j++].activateCallback = ConfirmRemove;
841 trashMenu[j].type = MENU_SEPARATOR;
842 trashMenu[j].name = "separator";
843 trashMenu[j++].helpCallback = NULL;
845 trashMenu[j].type = MENU_BUTTON;
846 trashMenu[j].maskBit = PREFERENCES;
847 trashMenu[j].label = GETMESSAGE(20,141, "Set View Options ...");
848 trashMenu[j].label = XtNewString(trashMenu[j].label);
849 trashMenu[j].mnemonic = GETMESSAGE(20,6, "V");
850 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
851 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
852 trashMenu[j].name = "setPreferences";
853 trashMenu[j++].activateCallback = ShowPreferencesDialog;
855 trashMenu[j].type = MENU_SEPARATOR;
856 trashMenu[j].name = "separator";
857 trashMenu[j++].helpCallback = NULL;
859 trashMenu[j].type = MENU_BUTTON;
860 trashMenu[j].label = GETMESSAGE(20,117, "Close");
861 trashMenu[j].label = XtNewString(trashMenu[j].label);
862 trashMenu[j].mnemonic = GETMESSAGE(20,118, "C");
863 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
864 trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
865 trashMenu[j].name = "close";
866 trashMenu[j++].activateCallback = CloseTrash;
869 /*************************************/
870 /* Create the 'Help' menu components */
871 /*************************************/
873 trashMenu[j].type = MENU_PULLDOWN_BUTTON;
874 trashMenu[j].label = GETMESSAGE(20,123, "Help");
875 trashMenu[j].label = XtNewString(trashMenu[j].label);
876 trashMenu[j].mnemonic = GETMESSAGE(20,9, "H");
877 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
878 trashMenu[j].isHelpBtn = True;
879 trashMenu[j].name = "help_trash";
880 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
882 trashMenu[j].type = SHARED_MENU_PANE;
883 trashMenu[j].name = "help_pane_trash";
884 trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
886 trashMenu[j].type = MENU_BUTTON;
887 trashMenu[j].label = GETMESSAGE(20,105, "Overview");
888 trashMenu[j].label = XtNewString(trashMenu[j].label);
889 trashMenu[j].mnemonic = GETMESSAGE(20,106, "v");
890 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
891 trashMenu[j].helpData = HELP_HELP_MENU_STR;
892 trashMenu[j].activateData = HELP_TRASH_OVERVIEW_TOPIC_STR;
893 trashMenu[j].name = "introduction";
894 trashMenu[j++].activateCallback = TrashHelpRequestCB;
896 trashMenu[j].type = MENU_SEPARATOR;
897 trashMenu[j].name = "separator";
898 trashMenu[j++].helpCallback = NULL;
900 trashMenu[j].type = MENU_BUTTON;
901 trashMenu[j].label = GETMESSAGE(20,107, "Tasks");
902 trashMenu[j].label = XtNewString(trashMenu[j].label);
903 trashMenu[j].mnemonic = GETMESSAGE(20,108, "T");
904 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
905 trashMenu[j].helpData = HELP_HELP_MENU_STR;
906 trashMenu[j].activateData = HELP_TRASH_TASKS_TOPIC_STR;
907 trashMenu[j].name = "tasks";
908 trashMenu[j++].activateCallback = TrashHelpRequestCB;
910 trashMenu[j].type = MENU_BUTTON;
911 trashMenu[j].label = GETMESSAGE(20,109, "Reference");
912 trashMenu[j].label = XtNewString(trashMenu[j].label);
913 trashMenu[j].mnemonic = GETMESSAGE(20,110, "R");
914 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
915 trashMenu[j].helpData = HELP_HELP_MENU_STR;
916 trashMenu[j].activateData = HELP_TRASH_DIALOG_STR;
917 trashMenu[j].name = "reference";
918 trashMenu[j++].activateCallback = TrashHelpRequestCB;
920 trashMenu[j].type = MENU_BUTTON;
921 trashMenu[j].label = GETMESSAGE(20,111, "On Item");
922 trashMenu[j].label = XtNewString(trashMenu[j].label);
923 trashMenu[j].mnemonic = GETMESSAGE(20,112, "O");
924 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
925 trashMenu[j].helpData = HELP_HELP_MENU_STR;
926 trashMenu[j].activateData = HELP_HELP_MODE_STR;
927 trashMenu[j].name = "onItem";
928 trashMenu[j++].activateCallback = TrashHelpRequestCB;
930 trashMenu[j].type = MENU_SEPARATOR;
931 trashMenu[j].name = "separator";
932 trashMenu[j++].helpCallback = NULL;
934 usingHelpTrash = &(trashMenu[j].widget);
935 trashMenu[j].type = MENU_BUTTON;
936 trashMenu[j].label = GETMESSAGE(20,113, "Using Help");
937 trashMenu[j].label = XtNewString(trashMenu[j].label);
938 trashMenu[j].mnemonic = GETMESSAGE(20,114, "U");
939 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
940 trashMenu[j].helpData = HELP_HELP_MENU_STR;
941 trashMenu[j].activateData = HELP_HOME_TOPIC;
942 trashMenu[j].name = "usingHelp";
943 trashMenu[j++].activateCallback = TrashHelpRequestCB;
945 trashMenu[j].type = MENU_SEPARATOR;
946 trashMenu[j].name = "separator";
947 trashMenu[j++].helpCallback = NULL;
949 trashMenu[j].type = MENU_BUTTON;
950 trashMenu[j].label = GETMESSAGE(27,106, "About Trash Can");
951 trashMenu[j].label = XtNewString(trashMenu[j].label);
952 trashMenu[j].mnemonic = GETMESSAGE(20,116, "A");
953 trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
954 trashMenu[j].helpData = HELP_HELP_MENU_STR;
955 trashMenu[j].activateData = HELP_ABOUT_STR;
956 trashMenu[j].name = "version";
957 trashMenu[j++].activateCallback = TrashHelpRequestCB;
959 menu_bar = _DtCreateMenuSystem(mainw, "menu_bar_trash", TrashHelpRequestCB,
960 HELP_TRASH_DIALOG_STR, True,
961 trashMenu, trashMenuItemCount, NULL,
964 /* Fine tune the menubar */
965 XtSetArg(args[0], XmNmarginWidth, 2);
966 XtSetArg(args[1], XmNmarginHeight, 2);
967 XtSetValues(menu_bar, args, 2);
969 file_mgr_rec->actions = NULL;
970 file_mgr_rec->action_pane = NULL;
971 file_mgr_rec->directoryBarBtn = *directoryBarBtn;
978 /************************************************************************
980 * TrashDisplayHandler
981 * This is the ICCCM message handler for the message used to display
982 * the trash can window. If the window is already displayed, then
983 * this call is a no-op.
985 * This handler is triggered by the front panel trash icon and by the
986 * File pulldown 'Show Trash' option.
988 ************************************************************************/
998 tt_message_reply( msg );
999 tttk_message_destroy( msg );
1002 if (!TrashInitialized)
1004 char *tmpStr, *tmpTitle, *tmpMsg;
1006 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1007 tmpTitle = XtNewString(tmpStr);
1008 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.");
1009 tmpMsg = XtNewString(tmpStr);
1011 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1018 #ifdef DEFER_TRASH_CREATION
1019 if( trashFileMgrData == NULL )
1020 TrashCreateDialog (XtDisplay(toplevel));
1023 /* the encapsulation functions do not set file_mgr_rec until a dialog */
1024 /* is actually displayed */
1025 if (trashFileMgrData->file_mgr_rec == 0)
1027 static Pixmap trash_icon = XmUNSPECIFIED_PIXMAP;
1028 static Pixmap trash_mask = XmUNSPECIFIED_PIXMAP;
1030 Pixel background, foreground, top_shadow, bottom_shadow, select;
1032 XClassHint classHints;
1033 FileMgrRec * file_mgr_rec;
1035 unsigned int height;
1039 classHints.res_name = trashFileMgrData->title;
1040 classHints.res_class = DTFILE_CLASS_NAME;
1043 _DtShowBuiltDialog(NULL,NULL, trashDialogData, NULL, False, &classHints);
1046 file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
1048 trashShell = file_mgr_rec->shell;
1050 if (trash_icon == XmUNSPECIFIED_PIXMAP)
1052 XtSetArg (args[0], XmNbackground, &background);
1053 XtSetArg (args[1], XmNcolormap, &colormap);
1054 XtGetValues (file_mgr_rec->main, args, 2);
1056 XmGetColors (XtScreen (file_mgr_rec->main), colormap, background,
1057 &foreground, &top_shadow, &bottom_shadow, &select);
1059 pixmap = XmGetPixmap (XtScreen (file_mgr_rec->main), trashIcon,
1060 foreground, background);
1061 if( pixmap != XmUNSPECIFIED_PIXMAP)
1062 trash_icon = pixmap;
1064 /* now let's get the mask for the File Manager */
1065 pixmap = _DtGetMask (XtScreen (file_mgr_rec->main), trashIcon);
1066 if( pixmap != XmUNSPECIFIED_PIXMAP)
1067 trash_mask = pixmap;
1069 if (trash_icon != XmUNSPECIFIED_PIXMAP)
1071 XtSetArg (args[0], XmNiconPixmap, trash_icon);
1072 if(trash_mask != XmUNSPECIFIED_PIXMAP)
1074 XtSetArg (args[1], XmNiconMask, trash_mask);
1075 XtSetValues (trashShell, args, 2);
1078 XtSetValues (trashShell, args, 1);
1086 root = RootWindowOfScreen (XtScreen (trashShell));
1088 /* Change the hints to reflect the current workspace */
1089 /* and raise the window */
1091 if (DtWsmGetCurrentWorkspace (XtDisplay (trashShell),
1092 root, ¤t_ws) == Success)
1093 DtWsmSetWorkspacesOccupied (XtDisplay (trashShell),
1094 XtWindow (trashShell), ¤t_ws, 1);
1096 /* Set the iconify state */
1097 /* Remove the iconify hint from the current shell */
1098 wmhints = XGetWMHints(XtDisplay(trashShell), XtWindow(trashShell));
1099 wmhints->flags |= IconWindowHint;
1100 wmhints->initial_state = NormalState;
1101 XSetWMHints(XtDisplay(trashShell), XtWindow(trashShell), wmhints);
1105 XtPopup (trashShell, XtGrabNone);
1106 XSync(XtDisplay(trashShell), False);
1107 XRaiseWindow (XtDisplay (trashShell), XtWindow (trashShell));
1108 XMapWindow( XtDisplay (trashShell), XtWindow (trashShell) );
1111 msg = tt_pnotice_create(TT_SESSION, "DtActivity_Began");
1112 tt_message_send(msg);
1113 tttk_message_destroy(msg);
1117 trashDialogPosted = True;
1121 /************************************************************************
1123 * ConfirmEmptyCancel
1125 ************************************************************************/
1126 static XtPointer trash_popup_client_data;
1130 XtPointer client_data,
1131 XtPointer call_data )
1133 XtUnmanageChild((Widget)client_data);
1134 XmUpdateDisplay((Widget)client_data);
1135 XtDestroyWidget((Widget)client_data);
1138 /************************************************************************
1142 ************************************************************************/
1147 XtPointer client_data,
1148 XtPointer call_data )
1150 /* destroy dialog */
1151 XtUnmanageChild((Widget)client_data);
1152 XmUpdateDisplay((Widget)client_data);
1153 XtDestroyWidget((Widget)client_data);
1158 /************************************************************************
1161 * This is a message interface used to really remove all of the
1164 * This handler is only called from an empty trash action.
1166 ************************************************************************/
1173 tt_message_reply( msg );
1174 tttk_message_destroy( msg );
1177 if(!TrashInitialized)
1179 char *tmpStr, *tmpTitle, *tmpMsg;
1181 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1182 tmpTitle = XtNewString(tmpStr);
1183 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.");
1184 tmpMsg = XtNewString(tmpStr);
1186 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1194 char *tmpStr, *title, *msg;
1196 tmpStr = GETMESSAGE(27,73, "Shred File(s)");
1197 title = XtNewString(tmpStr);
1198 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");
1199 msg = XtNewString(tmpStr);
1201 _DtMessageDialog(toplevel, title, msg, NULL, TRUE, ConfirmEmptyCancel,
1202 ConfirmEmptyOk, NULL, HelpRequestCB, False, QUESTION_DIALOG);
1211 /************************************************************************
1214 * This is a functional interface used to really remove all of the
1217 ************************************************************************/
1222 if (trashFileMgrData)
1225 Select_All(*selectAllBtn, NULL, NULL);
1226 Remove(*removeBtn, NULL, NULL);
1228 EmptyDir(trashFileMgrData->current_directory,0,0);
1234 /************************************************************************
1237 * This is the callback attached to the 'Select All' menu item. It
1238 * will select all of the files currently resident in the trash can.
1239 * It will also sensitize the 'Restore' and 'Remove' buttons, as long
1240 * as a remove request is not currently in the middle of being processed.
1242 ************************************************************************/
1247 XtPointer client_data,
1248 XtPointer call_data )
1251 SelectAllFiles(trashFileMgrData);
1252 SensitizeTrashBtns();
1257 /************************************************************************
1260 * This is the callback attached to the 'Unselect All' menu item. It
1261 * will unselect all of the items in the trash can, and will clear
1262 * out our selection array. It also desensitizes the 'Restore' and
1265 ************************************************************************/
1270 XtPointer client_data,
1271 XtPointer call_data )
1274 DeselectAllFiles(trashFileMgrData);
1275 SensitizeTrashBtns();
1280 /************************************************************************
1282 * SensitizeTrashBtns
1284 ************************************************************************/
1287 SensitizeTrashBtns(void)
1289 #ifdef DEFER_TRASH_CREATION
1290 if( trashFileMgrData
1295 if (trashFileMgrData->selected_file_count > 0)
1297 XtSetSensitive(*removeBtn, True);
1298 XtSetSensitive(*restoreBtn, True);
1302 XtSetSensitive(*removeBtn, False);
1303 XtSetSensitive(*restoreBtn, False);
1310 /************************************************************************
1312 * TrashRemoveHandler
1313 * This is the message handler for the message requesting that
1314 * a set of files be added to the trashcan. Each file name comes in
1315 * in /dir/name format; they are all in a single string, separated
1318 * Check for spaces in filenames and destop files. If we pass these
1319 * cases, call TrashRemover where the following occurs:
1321 * All of the files will be moved to the trash can directory; any
1322 * problems encountered will be reported in an error dialog. Each file
1323 * which is successfully moved, will be added to the trash information file.
1325 ************************************************************************/
1338 int msglistsize = 0 ;
1339 #endif /* SUN_PERF */
1342 * Note: There used to be a call to tt_message_reply() right here at the
1343 * beginning of the routine. However, the mailer wants to trash files
1344 * but wants the notification after the trash has been removed.
1345 * Therefore, we now put this off until the work is done or an error
1349 if( !TrashInitialized )
1351 char *tmpStr, *tmpTitle, *tmpMsg;
1353 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1354 tmpTitle = XtNewString(tmpStr);
1355 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.");
1356 tmpMsg = XtNewString(tmpStr);
1358 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1363 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
1365 global_msg_cnt = 0 ;
1366 #endif /* SUN_PERF */
1370 MessageToFileList(msg, &file_list, &file_count);
1373 * When the Dttrash_Remove handler "TrashRemoveHandler" is invoked for
1374 * the first time it caches the TT request and then invokes
1375 * tttk_block_while() with timeout = 0. This in effect causes the next
1376 * TT request to be processed before tttk_block_while() returns. Now if
1377 * TT msgs are coming in faster than dtfile is handling them, then the
1378 * next TT msg. should be another Dttrash_Remove msg. which will cause
1379 * re-entrance into TrashRemoveHandler. This will continue until the last
1380 * Dttrash_Remove msg. has been processed (and the recursive
1381 * TrashRemoveHandler starts unwinding) after which we check if the
1382 * number of TT msgs are > 0 in which case we fire off the
1383 * process to move the list of files to Trash.
1386 msglistsize = sizeof(Tt_message) * global_msg_cnt ;
1387 global_msg_list = (Tt_message *)XtRealloc((char *)global_msg_list, msglistsize);
1388 memcpy((char *)&(global_msg_list[global_msg_cnt - 1]),
1389 (char *)&msg, sizeof(Tt_message)) ;
1390 tttk_block_while(0, 0, time_out) ;
1392 /* If we get to this pt. then either :
1393 * 1. No more Dttrash_Remove msgs. left to process. Thus we should
1394 * process the TT msg list and move files to Trash.
1396 * 2. We got a msg. different from Dttrash_Remove. In this case
1397 * we process the msgs. we have so far. The remaining files to
1398 * be trashed will be buffered in another list.
1401 if (global_msg_cnt > 0) {
1402 MessagesToFileList(global_msg_list, global_msg_cnt,
1403 &file_list, &file_count);
1404 #endif /* SUN_PERF */
1406 if (file_count == 0)
1408 tt_message_reply( msg );
1409 tttk_message_destroy( msg );
1411 global_msg_cnt = 0 ;
1412 #endif /* SUN_PERF */
1416 /* return if from the trash dir */
1417 if (strncmp(file_list[0], trash_dir, strlen(trash_dir)) == 0)
1421 for (i = 0; i < file_count; i++) {
1422 XtFree( file_list[i] );
1424 XtFree( (char *)file_list );
1425 tt_message_status_set( msg, TT_DESKTOP_EALREADY );
1426 tt_message_reply( msg );
1427 tttk_message_destroy( msg );
1429 global_msg_cnt = 0 ;
1430 #endif /* SUN_PERF */
1434 /* post message if file(s) from desktop */
1438 str=(char *)IsAFileOnDesktop2(file_list, file_count, &number,&IsToolBox);
1439 /* IsToolBox is unused here, but is required to satisfy the prototype */
1443 MoveToTrash(file_list, file_count, verifyPromptsEnabled, msg);
1453 /* List will be regenerated by callbacks */
1454 for (i = 0; i < file_count; i++) {
1455 XtFree( file_list[i] );
1457 XtFree( (char *)file_list );
1459 tmpStr = (GETMESSAGE(27,73, "Shred File(s)"));
1460 title = XtNewString(tmpStr);
1464 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."));
1466 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."));
1467 message = (char *)XtMalloc(strlen(template) + strlen(str) + 1);
1468 sprintf(message, template, str);
1473 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."));
1475 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."));
1476 message = (char *)XtMalloc(strlen(template) + strlen(str) + 1);
1477 sprintf(message, template, str);
1480 /* Really ought to pass to OK and Cancel CBs via client_data */
1483 _DtMessageDialog(toplevel, title, message, NULL, TRUE,
1484 RemoveCancelCB, RemoveOkCB, NULL, HelpRequestCB, False,
1490 Tt_msg_cache *msg_cache ;
1492 msg_cache = (Tt_msg_cache *)XtMalloc(sizeof(Tt_msg_cache)) ;
1493 msg_cache->msg_cnt = global_msg_cnt ;
1495 msg_cache->msg_list = (Tt_message *)XtMalloc(sizeof(Tt_message) *
1496 msg_cache->msg_cnt );
1497 msg_cache->msg_list[msg_cache->msg_cnt] = NULL ;
1498 memcpy((char *)msg_cache->msg_list, (char *)global_msg_list,
1499 sizeof(Tt_message) * msg_cache->msg_cnt ) ;
1500 dlog = (Widget)_DtMessageDialog(toplevel, title, message, NULL, TRUE,
1501 RemoveCancelCB, RemoveOkCB, NULL, HelpRequestCB, False,
1503 XtSetArg ( args[0], XmNuserData, msg_cache );
1504 XtSetValues ( dlog, args, 1);
1506 #endif /* SUN_PERF */
1513 global_msg_cnt = 0 ;
1515 #endif /* SUN_PERF */
1520 /************************************************************************
1523 * Cleanup and unmanage the remove from DT dialog
1525 ************************************************************************/
1530 XtPointer client_data,
1531 XtPointer call_data )
1537 Tt_msg_cache *current_msg_cache ;
1540 XtSetArg( args[0], XmNuserData, ¤t_msg_cache );
1541 XtGetValues( (Widget)client_data, args, 1 );
1542 #endif /* SUN_PERF */
1544 /* destroy dialog */
1545 XtUnmanageChild((Widget)client_data);
1546 XmUpdateDisplay((Widget)client_data);
1547 XtDestroyWidget((Widget)client_data);
1551 MessageToFileList(global, &file_list, &file_count);
1553 MessagesToFileList( current_msg_cache->msg_list, current_msg_cache->msg_cnt,
1554 &file_list, &file_count);
1555 #endif /* SUN_PERF */
1557 MoveToTrash(file_list, file_count, verifyPromptsEnabled, NULL);
1559 /* reread desktop files */
1563 tt_message_reply( global );
1564 tttk_message_destroy( global );
1567 for (i = 0 ; i < current_msg_cache->msg_cnt ; i++) {
1568 tt_message_reply( current_msg_cache->msg_list[i] );
1569 tttk_message_destroy( current_msg_cache->msg_list[i] );
1571 XtFree ((char *)current_msg_cache->msg_list);
1572 XtFree ((char *)current_msg_cache);
1573 #endif /* SUN_PERF */
1578 /************************************************************************
1581 * Cleanup and unmanage the remove from DT dialog
1583 ************************************************************************/
1588 XtPointer client_data,
1589 XtPointer call_data )
1593 Tt_msg_cache *current_msg_cache ;
1596 XtSetArg( args[0], XmNuserData, ¤t_msg_cache );
1597 XtGetValues( (Widget)client_data, args, 1 );
1598 #endif /* SUN_PERF */
1600 /* destroy dialog */
1601 XtUnmanageChild((Widget)client_data);
1602 XmUpdateDisplay((Widget)client_data);
1603 XtDestroyWidget((Widget)client_data);
1606 tttk_message_fail( global, TT_DESKTOP_ECANCELED, 0, 1 );
1609 for (i = 0 ; i < current_msg_cache->msg_cnt ; i++)
1610 tttk_message_fail( current_msg_cache->msg_list[i], TT_DESKTOP_ECANCELED, 0, 1 );
1611 XtFree ((char *)current_msg_cache->msg_list);
1612 XtFree ((char *)current_msg_cache);
1613 #endif /* SUN_PERF */
1618 /************************************************************************
1620 * TrashRemoveNoConfirmHandler
1621 * This function is a message handler. It will place the specified set
1622 * of files into the trash can, without requesting confirmation if, for
1623 * instance, a file does not have write permission, or a directory is
1626 ************************************************************************/
1629 TrashRemoveNoConfirmHandler(
1635 if( !TrashInitialized )
1637 char *tmpStr, *tmpTitle, *tmpMsg;
1639 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1640 tmpTitle = XtNewString(tmpStr);
1641 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.");
1642 tmpMsg = XtNewString(tmpStr);
1644 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1650 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
1654 MessageToFileList(msg, &file_list, &file_count);
1655 MoveToTrash(file_list, file_count, False, msg);
1660 /************************************************************************
1664 * Parses a list of file names from a message
1665 * and returns an array of strings.
1667 * if (no message arguments) {
1668 * if (no file attribute) {
1669 * return length==0 list
1671 * return length==1 list containing file attribute
1674 * if (file attribute) {
1675 * file attribute is a directory, prepended onto subsequent filenames
1677 * if (arg 0 is non-null) {
1678 * arg 0 interpreted as a space-separated list of filenames
1681 * arg interpreted as a filename possibly containing spaces
1685 ************************************************************************/
1701 args = tt_message_args_count( msg );
1702 if (tt_is_err(tt_int_error( args ))) {
1706 dir = tt_message_file( msg );
1707 if ((tt_is_err(tt_ptr_error( dir ))) || (dir == 0)) {
1711 /* No args means use tt_message_file() as is */
1712 *file_list = (char **)
1713 XtRealloc((char *)(*file_list),
1714 (*file_count + 1)*sizeof(char *));
1715 (*file_list)[*file_count] = XtNewString(dir);
1720 if (dir[strlen(dir)] != '/') {
1722 if (strcmp(dir, "/") == 0)
1724 newdir = (char *)tt_malloc( strlen(dir)+1 );
1725 strcpy( newdir, dir );
1729 newdir = (char *)tt_malloc( strlen(dir)+2 );
1730 strcpy( newdir, dir );
1731 strcat( newdir, "/" );
1737 /* Invariant: dir can now be safely concatenated to form a valid path */
1738 dirlen = strlen(dir);
1739 for (arg = 0; arg < args; arg++) {
1744 val = tt_message_arg_val( msg, arg );
1745 if ((tt_is_err(tt_ptr_error( val ))) || (val == 0)) {
1751 /* Arg 0 is a space-separated list */
1754 for (val = DtStrtok(val, white); val; val = DtStrtok(0, white))
1756 if (strcmp(val, "/") == 0)
1758 file = (char *)XtMalloc( dirlen + 1 );
1759 strcpy( file, dir );
1763 file = (char *)XtMalloc( dirlen + strlen(val) + 1 );
1764 strcpy( file, dir );
1765 strcat( file, val );
1767 *file_list = (char **)
1768 XtRealloc((char *)(*file_list),
1769 (*file_count + 1)*sizeof(char *));
1770 (*file_list)[*file_count] = DtEliminateDots(file);
1773 tt_free( val2free );
1781 Tt_message *msg_list,
1796 for (num_msgs = 0 ; num_msgs < msg_cnt ; num_msgs++)
1798 msg = msg_list[num_msgs] ;
1800 args = tt_message_args_count( msg );
1801 if (tt_is_err(tt_int_error( args ))) {
1805 dir = tt_message_file( msg );
1806 if ((tt_is_err(tt_ptr_error( dir ))) || (dir == 0)) {
1810 /* No args means use tt_message_file() as is */
1811 *file_list = (char **)
1812 XtRealloc((char *)(*file_list),
1813 (*file_count + 1)*sizeof(char *));
1814 (*file_list)[*file_count] = XtNewString(dir);
1819 if (dir[strlen(dir)] != '/') {
1821 if (strcmp(dir, "/") == 0)
1823 newdir = (char *)tt_malloc( strlen(dir)+1 );
1824 strcpy( newdir, dir );
1828 newdir = (char *)tt_malloc( strlen(dir)+2 );
1829 strcpy( newdir, dir );
1830 strcat( newdir, "/" );
1836 /* Invariant: dir can now be safely concatenated to form a valid path */
1837 dirlen = strlen(dir);
1838 for (arg = 0; arg < args; arg++) {
1843 val = tt_message_arg_val( msg, arg );
1844 if ((tt_is_err(tt_ptr_error( val ))) || (val == 0)) {
1850 /* Arg 0 is a space-separated list */
1853 for (val = DtStrtok(val, white); val; val = DtStrtok(0, white))
1855 if (strcmp(val, "/") == 0)
1857 file = (char *)XtMalloc( dirlen + 1 );
1858 strcpy( file, dir );
1860 else if (strcmp(val, "/") == 0 && dirlen == 0)
1862 file = (char *)XtMalloc( strlen(val) + 1 );
1863 strcpy( file, val );
1867 file = (char *)XtMalloc( dirlen + strlen(val) + 1 );
1868 strcpy( file, dir );
1869 strcat( file, val );
1871 *file_list = (char **)
1872 XtRealloc((char *)(*file_list),
1873 (*file_count + 1)*sizeof(char *));
1874 (*file_list)[*file_count] = DtEliminateDots(file);
1877 tt_free( val2free );
1882 #endif /* SUN_PERF */
1885 /************************************************************************
1887 * CreateTrashFilename
1888 * Create trash directory name.
1890 ************************************************************************/
1893 CreateTrashFilename(
1895 Boolean uniqueTest )
1898 char * extension = NULL;
1899 struct stat statInfo;
1901 /* Create trash path */
1902 /* Give that name a little extra cushion, just in case */
1903 trashName = (char *)XtMalloc(strlen(users_home_dir) + strlen(TRASH_DIR) +
1904 strlen(baseName) + 15);
1905 sprintf(trashName, "%s%s/%s", users_home_dir, TRASH_DIR, baseName);
1907 /* Want to find the extension so the new file name created will preserve
1908 its original datatype.
1910 extension = strrchr( baseName, '.' );
1917 /* If a file by the trash name already exists, keep building new names */
1918 /* until one by that name doesn't exist and then use it */
1919 if (uniqueTest && (lstat(trashName, &statInfo) == 0))
1923 for (i = 1; True ; i++)
1925 /* Make a duplicate file name */
1927 sprintf(trashName, "%s%s/%s_%d.%s", users_home_dir, TRASH_DIR,
1928 baseName, i, extension);
1930 sprintf(trashName, "%s%s/%s_%d", users_home_dir, TRASH_DIR,
1933 /* Is the filename taken? */
1934 if (lstat(trashName, &statInfo) != 0)
1938 } /* end if not unique name */
1941 *(extension-1) = '.';
1949 /************************************************************************
1951 * MatchesSacredDirectory
1952 * Checks if the specify filename matches one of the sacred,
1953 * non-deleteable files.
1955 ************************************************************************/
1958 MatchesSacredDirectory(
1963 /* Don't allow the user to delete any of the sacred directories */
1964 for (i = 0; i < sacred_dir_count; i++)
1966 if (!strcmp(file, sacred_dir_list[i]))
1970 /* Compare against special desktop directories */
1971 /* remote_sys_dir is currently NULL */
1972 if (strcmp(file, remote_sys_dir) == 0)
1980 /************************************************************************
1983 * Callback for 'Delete To Trash' verify dialog Ok push button.
1985 ************************************************************************/
1990 XtPointer client_data,
1991 XtPointer call_data )
1993 VerifyCleanup ((Widget)client_data, True);
1998 /************************************************************************
2001 * Callback for 'Delete To Trash' verify dialog Cancel push button.
2003 ************************************************************************/
2008 XtPointer client_data,
2009 XtPointer call_data )
2012 VerifyCleanup ((Widget)client_data, False);
2017 /************************************************************************
2020 * Callback for 'Delete To Trash' verify dialog push buttons.
2022 * Called from VerifyOk and VerifyCancel.
2024 ************************************************************************/
2029 Boolean completeDelete )
2036 /* Unpost dialog, and retrieve the list of verified files */
2037 XtUnmanageChild(mbox);
2038 XmUpdateDisplay(mbox);
2039 XtSetArg(args[0], XmNuserData, &verifylist);
2040 XtGetValues(mbox, args, 1);
2042 /* count the number of files in the list */
2043 for (fileCount = 0; verifylist[fileCount]; fileCount++)
2047 * Start the background process that moves the files into the trash,
2051 MoveToTrash(verifylist, fileCount, False, NULL);
2055 /* Free up the storage we allocated */
2056 for (i = fileCount; i > 0; i--)
2057 XtFree(verifylist[i]);
2058 XtFree((char *)verifylist);
2061 XtDestroyWidget(mbox);
2066 /************************************************************************
2070 ************************************************************************/
2071 static XtPointer trash_popup_client_data;
2075 XtPointer client_data,
2076 XtPointer call_data )
2078 XtUnmanageChild((Widget)client_data);
2079 XmUpdateDisplay((Widget)client_data);
2080 XtDestroyWidget((Widget)client_data);
2082 XtSetSensitive(*removeBtn, True);
2083 XtSetSensitive(*restoreBtn, True);
2084 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
2085 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
2086 trash_popup_client_data = NULL;
2091 /************************************************************************
2095 ************************************************************************/
2100 XtPointer client_data,
2101 XtPointer call_data )
2103 FileViewData *file_view_data;
2105 /* destroy dialog */
2106 XtUnmanageChild((Widget)client_data);
2107 XmUpdateDisplay((Widget)client_data);
2108 XtDestroyWidget((Widget)client_data);
2110 file_view_data = trashFileMgrData->popup_menu_icon;
2111 if(!file_view_data && trashFileMgrData->selected_file_count)
2112 file_view_data = trashFileMgrData->selection_list[0];
2113 trashFileMgrData->popup_menu_icon = NULL;
2115 if(file_view_data != NULL)
2116 Remove(*removeBtn, file_view_data, NULL);
2121 /************************************************************************
2124 * This is the callback attached to the 'Remove' menu item.
2126 ************************************************************************/
2131 XtPointer client_data,
2132 XtPointer call_data )
2134 char *tmpStr, *title, *msg;
2136 /* Desensitize remove & restore buttons, until we're done */
2137 XtSetSensitive(*removeBtn, False);
2138 XtSetSensitive(*restoreBtn, False);
2139 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], False);
2140 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], False);
2143 trash_popup_client_data = client_data;
2145 tmpStr = GETMESSAGE(27,73, "Shred File(s)");
2146 title = XtNewString(tmpStr);
2147 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");
2148 msg = XtNewString(tmpStr);
2150 _DtMessageDialog(toplevel, title, msg, NULL, TRUE, ConfirmCancel, ConfirmOk,
2151 NULL, HelpRequestCB, False, QUESTION_DIALOG);
2159 /************************************************************************
2162 * While we wait for the background process to complete, we will
2163 * desensitize the 'Remove' and 'Restore' menu items, since we don't
2164 * handle multiple requests.
2166 ************************************************************************/
2171 XtPointer client_data,
2172 XtPointer call_data )
2174 FileViewData *file_view_data;
2175 Boolean match = False;
2176 DeleteList *deleteList = NULL;
2182 /* Remove may be called to remove a file or to remove a trash file; */
2183 /* if w is NULL, this is a call to remove a file */
2186 removeType = TRASH_FILE;
2188 /* Count number of items to 'Delete To Trash' */
2189 if (client_data != NULL)
2191 /* This was called by trash popup */
2192 /* need to check to see if it is selected, if it is restore all
2193 the selected files, else just restore this file */
2195 file_view_data = (FileViewData *)client_data;
2196 for (i = 0; i < trashFileMgrData->selected_file_count; i++)
2198 if (strcmp(file_view_data->file_data->file_name,
2199 trashFileMgrData->selection_list[i]->file_data->file_name) == 0)
2207 if (trashFileMgrData->selected_file_count == 0)
2209 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
2210 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
2217 /* Desensitize remove & restore buttons, until we're done */
2218 XtSetSensitive(*removeBtn, False);
2219 XtSetSensitive(*restoreBtn, False);
2223 deleteCount = trashFileMgrData->selected_file_count;
2227 /* Create the list of things being deleted */
2228 deleteList = (DeleteList *)XtMalloc(deleteCount * sizeof(DeleteList));
2232 for (i = 0; i < deleteCount; i++)
2234 AddToDeleteList(deleteList, i,
2235 trashFileMgrData->selection_list[i]->file_data->file_name);
2239 AddToDeleteList(deleteList, 0, file_view_data->file_data->file_name);
2244 removeType = REMOVE_FILE;
2245 deleteList = (DeleteList *)XtMalloc(sizeof(DeleteList));
2247 deleteList[0].trash = XtNewString(client_data);
2248 deleteList[0].orig = NULL;
2251 EmptyTrash(deleteList, deleteCount, removeType, NULL);
2256 /************************************************************************
2259 * Locate a file in the trash list and add it to the delete list.
2261 ************************************************************************/
2265 DeleteList *deleteList,
2271 /* Locate file in trash list, add entry to delete list */
2272 for (j = 0; j < numTrashItems; j++)
2274 if (strcmp(filename, trashCan[j].filename) == 0)
2276 /* file found in trash list */
2277 deleteList[i].trash = XtNewString(trashCan[j].intNew);
2278 deleteList[i].orig = XtNewString(trashCan[j].intOrig);
2284 /* file not found in trash list */
2285 deleteList[i].trash = CreateTrashFilename(filename, FALSE);
2286 deleteList[i].orig = NULL;
2291 /************************************************************************
2293 * TrashRestoreHandler
2294 * This function is a message handler. It will restore the specified set
2295 * of files from the trash can.
2297 ************************************************************************/
2300 TrashRestoreHandler(
2306 if( !TrashInitialized )
2308 char *tmpStr, *tmpTitle, *tmpMsg;
2310 tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
2311 tmpTitle = XtNewString(tmpStr);
2312 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.");
2313 tmpMsg = XtNewString(tmpStr);
2315 _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
2321 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
2326 /* Desensitize remove & restore buttons, until we're done */
2327 XtSetSensitive(*removeBtn, False);
2328 XtSetSensitive(*restoreBtn, False);
2330 MessageToFileList(msg, &file_list, &file_count);
2331 RestoreFromTrash(file_list, file_count, NULL, NULL, msg,False);
2336 /************************************************************************
2339 * This is the callback attached to the 'Restore' menu item. It will
2340 * remove from the trash all of the selected files, and will restore
2341 * them to their original location. If any problems occur while trying
2342 * to restore any of the files, then an error dialog will be posted.
2344 ************************************************************************/
2349 XtPointer client_data,
2350 XtPointer call_data )
2352 FileViewData *file_view_data;
2353 Boolean match = False;
2358 if (client_data != NULL)
2360 /* This was called by trash popup */
2361 /* need to check to see if it is selected, if it is restore all
2362 the selected files, else just restore this file */
2364 file_view_data = trashFileMgrData->popup_menu_icon;
2366 /* The object would have been deleted if the following condn is true */
2367 if(!file_view_data && !trashFileMgrData->selected_file_count)
2369 trashFileMgrData->popup_menu_icon = NULL;
2371 for (i = 0; i < trashFileMgrData->selected_file_count; i++)
2373 if (strcmp(file_view_data->file_data->file_name,
2374 trashFileMgrData->selection_list[i]->file_data->file_name) == 0)
2383 /* Desensitize remove & restore buttons, until we're done */
2384 XtSetSensitive(*removeBtn, False);
2385 XtSetSensitive(*restoreBtn, False);
2390 file_count = trashFileMgrData->selected_file_count;
2391 file_list = (char **)XtMalloc(file_count * sizeof(char *));
2392 for (i = 0; i < file_count; i++)
2394 file_view_data = trashFileMgrData->selection_list[file_count - 1 - i];
2395 file_list[i] = XtNewString(file_view_data->file_data->file_name);
2401 file_list = (char **)XtMalloc(sizeof(char *));
2402 file_list[0] = XtNewString(file_view_data->file_data->file_name);
2405 /* Start the background process that will do the restore */
2406 RestoreFromTrash(file_list, file_count, NULL, NULL, NULL,False);
2411 /************************************************************************
2414 * This is the callback attached to the 'Close' menu item. It will
2415 * unpost the trash can window.
2417 ************************************************************************/
2422 XtPointer client_data,
2423 XtPointer call_data )
2427 if (trashDialogPosted)
2429 XWithdrawWindow(XtDisplay(trashShell), XtWindow(trashShell),
2430 XDefaultScreen(XtDisplay(trashShell)));
2431 XtPopdown(trashShell);
2435 for (i = 0; i < secondaryTrashHelpDialogCount; i++)
2437 if (_DtIsDialogShowing(secondaryTrashHelpDialogList[i]))
2438 _DtHideDialog(secondaryTrashHelpDialogList[i], False);
2439 _DtFreeDialogData(secondaryTrashHelpDialogList[i]);
2441 XtFree((char *)secondaryTrashHelpDialogList);
2442 secondaryTrashHelpDialogList = NULL;
2443 secondaryTrashHelpDialogCount = 0;
2445 if (primaryTrashHelpDialog)
2447 if (_DtIsDialogShowing(primaryTrashHelpDialog))
2448 _DtHideDialog(primaryTrashHelpDialog, False);
2449 _DtFreeDialogData(primaryTrashHelpDialog);
2451 primaryTrashHelpDialog = NULL;
2453 if (PositionFlagSet(trashFileMgrData))
2454 SavePositionInfo(trashFileMgrData);
2457 FileMgrRec * file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
2459 if( (file_mgr_rec->menuStates & PREFERENCES) == 0 )
2460 file_mgr_rec->menuStates |= PREFERENCES;
2463 trashDialogPosted = False;
2468 /************************************************************************
2472 ************************************************************************/
2479 DtDndDropCallbackStruct *drop_parameters)
2482 DtActionArg * action_args;
2483 FileMgrRec * file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
2484 Position drop_x = drop_parameters->x;
2485 Position drop_y = drop_parameters->y;
2487 _DtBuildActionArgsWithDroppedFiles(NULL, drop_parameters,
2488 &action_args, &arg_count);
2490 DtActionInvoke(file_mgr_rec->shell, TRASH_ACTION,
2491 action_args, arg_count, NULL, NULL,
2492 trashFileMgrData->current_directory, True, NULL, NULL);
2494 _DtFreeActionArgs(action_args, arg_count);
2496 RepositionIcons(trashFileMgrData, file_set, file_count, drop_x,
2502 /************************************************************************
2506 ************************************************************************/
2510 FileMgrData *file_mgr_data,
2511 FileMgrRec *file_mgr_rec,
2525 * Get target host and directory
2527 target_dir = XtNewString(file_mgr_data->current_directory);
2528 target_host = XtNewString(file_mgr_data->host);
2531 * Create file list and call RestoreFromTrash
2533 file_list = (char **)XtMalloc(file_count * sizeof(char *));
2535 for (i = 0; i < file_count; i++)
2536 file_list[i] = XtNewString(file_set[i]);
2538 RestoreFromTrash(file_list, file_count, target_host, target_dir, NULL,False);
2543 /************************************************************************
2546 * Locate file in trash list based on new internal name. Return file name.
2548 ************************************************************************/
2554 Boolean IsTrash = False;
2556 if (strncmp(users_home_dir, filename, strlen(users_home_dir)) == 0)
2559 ((filename + strlen(users_home_dir)), TRASH_DIR, strlen(TRASH_DIR))
2569 /************************************************************************
2572 * Since the trash does not use shared menupanes, there is no work to
2573 * be done during the popup and popdown callbacks; therefore, we use
2574 * and empty function.
2576 ************************************************************************/
2581 XtPointer clientData,
2582 XtPointer callData )
2589 /*--------------------------------------------------------------------
2590 * UpdateDirectoryOf:
2591 * Arrange for the directory containing a file to be updated
2592 *------------------------------------------------------------------*/
2601 /* remove last component from path to get the directory */
2602 ptr = strrchr(path, '/');
2606 strcpy(host, home_host_name);
2608 /* now arrange for the directory to be updated */
2609 UpdateDirectory(NULL, host, path);
2611 /* restore the path */
2617 /*--------------------------------------------------------------------
2618 * EraseObject, EraseDir
2619 * Routines for recursively deleting files and directories
2620 *------------------------------------------------------------------*/
2623 EraseObject(char *file_name)
2625 struct stat stat_buf;
2627 if (lstat(file_name, &stat_buf) < 0)
2630 else if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
2631 return EraseDir(file_name);
2633 else if (remove(file_name) < 0)
2642 EraseDir(char *dir_name)
2644 DIR *dir; /* open directory */
2645 struct dirent *entry; /* directory entry */
2646 char srcname[MAX_PATH];
2650 /* open source directory */
2651 dir = opendir(dir_name);
2655 /* prepare source name */
2656 strcpy(srcname, dir_name);
2657 srclen = strlen(srcname);
2658 if (srcname[srclen - 1] != '/')
2659 srcname[srclen++] = '/';
2662 while (rc == 0 && (entry = readdir(dir)) != NULL)
2664 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
2666 strcpy(srcname + srclen, entry->d_name);
2667 rc = EraseObject(srcname);
2674 if (rmdir(dir_name) < 0)
2676 if (unlink(dir_name) < 0 )
2688 /*====================================================================
2690 * MoveToTrashProcess
2691 * Run a background process to move files to the trash can.
2693 *==================================================================*/
2695 /*--------------------------------------------------------------------
2696 * MoveToTrashProcess
2697 * Main routine of background process for MoveToTrash
2698 *------------------------------------------------------------------*/
2705 Boolean do_verify_checks)
2711 struct stat statInfo;
2713 struct dirent * entry;
2718 for (i = 0; i < file_count; i++)
2720 /* get base name and full path */
2721 path = XtNewString(file_list[i]);
2722 baseName = strrchr(file_list[i], '/');
2723 if (baseName == NULL || path == NULL)
2725 /* Invalid filename */
2727 pipe_msg = PIPEMSG_OTHER_ERROR;
2728 rc = BAD_FILE_ERROR;
2729 DPRINTF(("MoveToTrashProcess: sending BAD_FILE_ERROR\n"));
2730 write(pipe_fd, &pipe_msg, sizeof(short));
2731 write(pipe_fd, &rc, sizeof(int));
2734 if (path && MatchesSacredDirectory(path))
2736 /* Invalid filename */
2738 pipe_msg = PIPEMSG_OTHER_ERROR;
2739 rc = BAD_FILE_SACRED;
2740 DPRINTF(("MoveToTrashProcess: sending BAD_FILE_SACRED\n"));
2741 write(pipe_fd, &pipe_msg, sizeof(short));
2742 write(pipe_fd, &rc, sizeof(int));
2747 if (do_verify_checks)
2749 char *tmpstr = (file_list[i] == (baseName - 1))?"/":file_list[i];
2751 Boolean TrashError = False;
2753 /* check write permissions for the file */
2754 if (lstat(path,&s1) < 0)
2761 savechar = *(baseName-1);
2762 *(baseName-1) = '\0';
2763 if(CheckDeletePermission(tmpstr, path) != 0)
2765 /* No write access; display error message */
2767 if (S_ISDIR(s1.st_mode))
2768 rc = BAD_TRASH_DIRECTORY;
2770 rc = BAD_TRASH_FILE;
2773 *(baseName-1) = savechar;
2778 pipe_msg = PIPEMSG_OTHER_ERROR;
2779 DPRINTF(("MoveToTrashProcess: sending BAD_TRASH message\n"));
2780 write(pipe_fd, &pipe_msg, sizeof(short));
2781 write(pipe_fd, &rc, sizeof(int));
2784 else if (CheckAccess(path, W_OK) != 0 && !S_ISLNK(s1.st_mode))
2786 /* No write access; ask user for verification */
2788 pipe_msg = PIPEMSG_OTHER_ERROR;
2790 DPRINTF(("MoveToTrashProcess: sending VERIFY_FILE\n"));
2791 write(pipe_fd, &pipe_msg, sizeof(short));
2792 write(pipe_fd, &rc, sizeof(int));
2797 * If this is a directory, make sure it's empty, i.e.,
2798 * contains only ".", "..", and ".!" or ".~" files.
2800 if (lstat(path, &statInfo) == 0 &&
2801 (statInfo.st_mode & S_IFMT) == S_IFDIR &&
2802 (dirp = opendir(path)) != NULL)
2804 /* read the directory */
2805 while ((entry = readdir(dirp)) != NULL)
2807 if ( !(strcmp(entry->d_name, ".") == 0 ||
2808 strcmp(entry->d_name, "..") == 0 ||
2809 strncmp(entry->d_name, ".!", 2) == 0 ||
2810 strncmp(entry->d_name, ".~", 2) == 0) )
2812 /* found a real file: directory not empty */
2821 /* Directory is not empty */
2823 pipe_msg = PIPEMSG_OTHER_ERROR;
2825 DPRINTF(("MoveToTrashProcess: sending VERIFY_DIR\n"));
2826 write(pipe_fd, &pipe_msg, sizeof(short));
2827 write(pipe_fd, &rc, sizeof(int));
2831 } /* end if do_verify_checks */
2834 to = CreateTrashFilename(baseName, TRUE);
2836 /* move file to the trash directory */
2837 success = FileManip((Widget) (intptr_t) pipe_fd, MOVE_FILE, path, to, TRUE,
2838 FileOpError, True, TRASH_DIRECTORY);
2841 pipe_msg = PIPEMSG_DONE;
2842 DPRINTF(("MoveToTrashProcess: sending DONE\n"));
2843 write(pipe_fd, &pipe_msg, sizeof(short));
2844 PipeWriteString(pipe_fd, path);
2845 PipeWriteString(pipe_fd, to);
2854 /*--------------------------------------------------------------------
2855 * MoveToTrashPipeCB:
2856 * Read and process data sent through the pipe.
2857 *------------------------------------------------------------------*/
2861 XtPointer client_data,
2865 MoveToTrashCBData *cb_data = (MoveToTrashCBData *)client_data;
2868 char *title, *err_msg, *err_arg;
2881 DesktopRec *desktopWin;
2882 char *dir_error=NULL,*file_error=NULL,*no_file_error=NULL,*sacred_error=NULL;
2884 /* read the next msg from the pipe */
2886 n = PipeRead(*fd, &pipe_msg, sizeof(short));
2887 DPRINTF(("MoveToTrashPipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
2891 case PIPEMSG_FILEOP_ERROR:
2892 PipeRead(*fd, &rc, sizeof(int));
2893 err_msg = PipeReadString(*fd);
2894 err_arg = PipeReadString(*fd);
2896 FileOperationError(toplevel, err_msg, err_arg);
2901 cb_data->rc[cb_data->done_count++] = BAD_FILE_ERROR;
2904 case PIPEMSG_OTHER_ERROR:
2905 PipeRead(*fd, &rc, sizeof(int));
2906 cb_data->rc[cb_data->done_count++] = rc;
2909 case PIPEMSG_TARGET_TIME:
2912 case PIPEMSG_FILE_MODIFIED:
2916 i = cb_data->done_count++;
2917 cb_data->path[i] = PipeReadString(*fd);
2918 cb_data->to[i] = PipeReadString(*fd);
2924 "Internal error in MoveToTrashPipeCB: bad pipe_msg %d\n",
2926 /* Don't know how it can get in here, but if it does, we'll try to
2927 * simulate an error condition. Without it, the SGI hung up
2929 while (cb_data->done_count < cb_data->file_count)
2930 cb_data->rc[cb_data->done_count++] = BAD_FILE_ERROR;
2935 /* if still more files to be processed, return now to wait for the rest */
2936 if (cb_data->done_count < cb_data->file_count)
2940 /* close the pipe and cancel the callback */
2941 DPRINTF(("MoveToTrashPipeCB: done\n"));
2945 /* process the results */
2957 for (i = 0; i < cb_data->file_count; i++)
2959 if (cb_data->rc[i]==BAD_TRASH_DIRECTORY || cb_data->rc[i]==BAD_TRASH_FILE)
2964 Boolean errflg = False;
2965 if(cb_data->rc[i] == BAD_TRASH_DIRECTORY)
2967 tmpmsg = GETMESSAGE(27, 113,
2968 "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");
2971 dir_error = (char *) XtRealloc(dir_error,strlen(dir_error)+
2972 strlen(cb_data->file_list[i])+5);
2973 strcat(dir_error,cb_data->file_list[i]);
2974 strcat(dir_error,"\n");
2978 dir_error = XtMalloc(strlen(tmpmsg)+
2979 strlen(cb_data->file_list[i])+5);
2980 sprintf(dir_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
2985 tmpmsg = GETMESSAGE(27, 114,
2986 "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");
2989 file_error = (char *) XtRealloc(file_error,strlen(file_error)
2990 + strlen(cb_data->file_list[i])+5);
2991 strcat(file_error,cb_data->file_list[i]);
2992 strcat(file_error,"\n");
2996 file_error = XtMalloc(strlen(tmpmsg)+
2997 strlen(cb_data->file_list[i])+5);
2998 sprintf(file_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
3001 XtFree(cb_data->file_list[i]);
3002 cb_data->file_list[i] = NULL;
3006 else if (cb_data->rc[i] == BAD_FILE_SACRED)
3010 char *tmpmsg = GETMESSAGE(27, 115,
3011 "The following object(s) cannot be deleted.\nThe desktop cannot function properly without these object(s).\n\n");
3014 sacred_error = (char *) XtRealloc(sacred_error,strlen(sacred_error)+
3015 strlen(cb_data->file_list[i])+5);
3016 strcat(sacred_error,cb_data->file_list[i]);
3017 strcat(sacred_error,"\n");
3021 sacred_error=XtMalloc(strlen(tmpmsg)+strlen(cb_data->file_list[i])+5);
3022 sprintf(sacred_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
3024 XtFree(cb_data->file_list[i]);
3025 cb_data->file_list[i] = NULL;
3029 else if (cb_data->rc[i] == BAD_FILE_ERROR)
3031 XtFree(cb_data->file_list[i]);
3032 cb_data->file_list[i] = NULL;
3034 else if (cb_data->rc[i] == NO_TRASH_FILE)
3037 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");
3043 no_file_error = (char *)XtRealloc(no_file_error,strlen(no_file_error)+
3044 strlen(cb_data->file_list[i])+5);
3045 strcat(no_file_error,cb_data->file_list[i]);
3046 strcat(no_file_error,"\n");
3050 no_file_error = XtMalloc(strlen(tmpmsg)+
3051 strlen(cb_data->file_list[i])+5);
3052 sprintf(no_file_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
3057 else if (cb_data->rc[i] == VERIFY_FILE ||
3058 cb_data->rc[i] == VERIFY_DIR)
3060 if (verifyCount < 6)
3061 AddString(&verifybuf, &verifybufsize, cb_data->file_list[i], NULL);
3065 * Keep track of whether we have directories only, files only,
3066 * or a combination, so that we can display the appropriate
3067 * label in the dialog.
3069 if (cb_data->rc[i] == VERIFY_FILE)
3075 * Add to array which is to be attached to the dialog;
3076 * the array will ultimately be NULL terminated.
3078 verifylist = (char **)XtRealloc((char *)verifylist,
3079 sizeof(char *) * (verifyCount + 1));
3080 verifylist[verifyCount - 1] = cb_data->file_list[i];
3084 /* Add file to trash list */
3085 if (numTrashItems >= trashListSize)
3087 trashListSize += 10;
3088 trashCan = (TrashEntry *)
3089 XtRealloc((char *)trashCan, sizeof(TrashEntry) * trashListSize);
3092 trashCan[numTrashItems].problem = False;
3093 trashCan[numTrashItems].intNew = cb_data->to[i];
3094 trashCan[numTrashItems].intOrig = cb_data->path[i];
3095 trashCan[numTrashItems].external = cb_data->file_list[i];
3097 /* extract base file name */
3098 baseName = strrchr(cb_data->to[i], '/');
3100 if (*baseName == '\0')
3102 trashCan[numTrashItems].filename = XtNewString(baseName);
3107 /* arrange for the source directory to be updated */
3108 UpdateDirectoryOf(cb_data->path[i]);
3111 * If the source file was referenced by a desktop object,
3112 * we need to remove the destkop object
3114 for (j = 0; j < desktop_data->numIconsUsed; j++)
3116 Tt_status tt_status;
3118 desktopWin = desktop_data->desktopWindows[j];
3119 fileName = ResolveLocalPathName( desktopWin->host,
3120 desktopWin->dir_linked_to,
3121 desktopWin->file_name,
3122 home_host_name, &tt_status);
3124 if( TT_OK == tt_status )
3126 if (strcmp(fileName, cb_data->path[i]) == 0)
3128 RemoveDT(desktopWin->shell, (XtPointer)desktopWin, NULL);
3130 RemoveMovedObjectFromDT(desktopWin->shell, (XtPointer)desktopWin,
3131 cb_data->file_count, cb_data->file_list);
3132 #endif /* SUN_PERF */
3141 buf = XtMalloc(strlen(dir_error)+3);
3142 sprintf(buf,"%s\n",dir_error);
3149 buf = XtMalloc(strlen(file_error)+3);
3150 sprintf(buf,"%s\n",file_error);
3154 buf = XtRealloc(buf,strlen(buf)+strlen(file_error)+3);
3155 sprintf(buf,"%s%s\n",buf,file_error);
3163 buf = XtMalloc(strlen(no_file_error)+3);
3164 sprintf(buf,"%s\n",no_file_error);
3168 buf = XtRealloc(buf,strlen(buf)+strlen(no_file_error)+3);
3169 sprintf(buf,"%s%s\n",buf,no_file_error);
3171 XtFree(no_file_error);
3177 buf = XtMalloc(strlen(sacred_error)+3);
3178 sprintf(buf,"%s\n",sacred_error);
3182 buf = XtRealloc(buf,strlen(buf)+strlen(sacred_error)+3);
3183 sprintf(buf,"%s%s\n",buf,sacred_error);
3185 XtFree(sacred_error);
3188 bufsize = strlen(buf);
3190 /* Update the trash information file, and the trash window */
3193 FILE * trashInfoFileId = fopen(TrashInfoFileName, "a+");
3194 if( trashInfoFileId != NULL )
3196 for (i = fileCount; i > 0; i--)
3198 if( WriteEntry(trashInfoFileId,
3199 trashCan[numTrashItems - i].external,
3200 trashCan[numTrashItems - i].filename) < 0 )
3203 fflush(trashInfoFileId);
3204 fclose(trashInfoFileId);
3205 if( trashFileMgrData )
3206 UpdateDirectory(NULL, trashFileMgrData->host,
3207 trashFileMgrData->current_directory);
3211 /* Check for any bad files; post an error dialog */
3214 /* send a reply to the message that triggered this operation, if any */
3216 if (cb_data->msg != 0) {
3218 Until Action is fixed.
3219 tttk_message_fail( cb_data->msg, TT_DESKTOP_EACCES, 0, 1 );
3221 tt_message_reply( cb_data->msg );
3222 tttk_message_destroy( cb_data->msg );
3224 if (cb_data->msg_cnt > 0) {
3225 for (i = 0 ; i < cb_data->msg_cnt ; i++) {
3226 tt_message_reply( cb_data->msg_list[i] ) ;
3227 tttk_message_destroy( cb_data->msg_list[i] ) ;
3229 XtFree((char *)cb_data->msg_list) ;
3230 cb_data->msg_cnt = 0 ;
3231 #endif /* SUN_PERF */
3234 /* If more items than can be displayed, let user know */
3237 char extraFiles[256];
3239 (void) sprintf(extraFiles, AdditionalHeader, badCount - 8);
3243 GETMESSAGE(27,97, "The following objects could not be placed in the trash can: \n"));
3246 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3247 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3249 XtFree ((char *) buf);
3251 /* send a reply to the message that triggered this operation, if any */
3253 if (cb_data->msg != 0) {
3254 tt_message_reply( cb_data->msg );
3255 tttk_message_destroy( cb_data->msg );
3257 if (cb_data->msg_cnt > 0) {
3258 for (i = 0 ; i < cb_data->msg_cnt ; i++) {
3259 tt_message_reply( cb_data->msg_list[i] ) ;
3260 tttk_message_destroy( cb_data->msg_list[i] ) ;
3262 XtFree((char *)cb_data->msg_list) ;
3263 cb_data->msg_cnt = 0 ;
3264 #endif /* SUN_PERF */
3269 /* Check for any files requiring user verification; post a prompt dialog */
3270 /* XXX Really should fail incoming ToolTalk request if user cancels any */
3278 /* If more items than can be displayed, let user know */
3279 if (verifyCount > 6)
3281 char extraFiles[256];
3283 (void) sprintf(extraFiles, AdditionalHeader, verifyCount - 6);
3284 AddString(&verifybuf, &verifybufsize, extraFiles, NULL);
3288 * Depending upon what type of files are to be displayed in the
3289 * dialog, choose the appropriate dialog text.
3291 if (vfiles && vdirs)
3292 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");
3294 header = GETMESSAGE(27,104, "You do not have write permission for the following file(s):\nDo you want to proceed?\n");
3296 header = GETMESSAGE(27,100, "The following folder(s) are not empty.\nDo you want to proceed?\n");
3298 tmpbuf = XtMalloc(strlen(header) + strlen(verifybuf) + 1);
3299 sprintf(tmpbuf, "%s%s", header, verifybuf);
3300 title = XtNewString(GETMESSAGE(27, 4, "Trash Can Warning"));
3301 dlog = (Widget)_DtMessageDialog(toplevel, title, tmpbuf, NULL, True,
3302 VerifyCancel, VerifyOk, NULL, HelpRequestCB,
3303 False, WARNING_DIALOG);
3305 XtFree ((char *) verifybuf);
3309 * Add array as userdata on the dialog.
3310 * NULL terminate the array.
3312 verifylist[verifyCount] = NULL;
3313 XtSetArg(args[0], XmNuserData, verifylist);
3314 XtSetValues(dlog, args, 1);
3317 /* free the callback data */
3318 XtFree((char *)cb_data->file_list);
3319 XtFree((char *)cb_data->path);
3320 XtFree((char *)cb_data->to);
3321 XtFree((char *)cb_data->rc);
3322 XtFree((char *)cb_data);
3328 /*--------------------------------------------------------------------
3330 * Start the background process and set up callback for the pipe.
3331 *------------------------------------------------------------------*/
3337 Boolean do_verify_checks,
3340 static char *pname = "MoveToTrash";
3341 MoveToTrashCBData *cb_data;
3345 if( !TrashInitialized )
3348 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
3353 /* set up callback data */
3354 cb_data = XtNew(MoveToTrashCBData);
3355 cb_data->file_list = file_list;
3356 cb_data->file_count = file_count;
3357 cb_data->done_count = 0;
3358 cb_data->path = (char **)XtCalloc(file_count, sizeof(char *));
3359 cb_data->to = (char **)XtCalloc(file_count, sizeof(char *));
3360 cb_data->rc = (int *)XtCalloc(file_count, sizeof(int));
3363 cb_data->msg_cnt = 0 ;
3364 if (global_msg_cnt > 0) {
3365 cb_data->msg_cnt = global_msg_cnt ;
3366 cb_data->msg_list = (Tt_message *)XtMalloc(sizeof(Tt_message) * cb_data->msg_cnt);
3367 memcpy((char *)cb_data->msg_list,(char *)global_msg_list, sizeof(Tt_message) *
3370 #endif /* SUN_PERF */
3373 if(-1 == pipe(pipe_fd)) {
3375 "%s: pipe failed error %d=%s\n",
3376 pname, errno, strerror(errno));
3380 /* fork the process that does the actual work */
3385 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
3386 pname, getppid(), getpid(), errno, strerror(errno));
3393 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
3395 close(pipe_fd[0]); /* child won't read from the pipe */
3397 MoveToTrashProcess(pipe_fd[1], file_list, file_count, do_verify_checks);
3400 DBGFORK(("%s: child exiting\n", pname));
3405 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
3407 /* parent: set up callback to get the pipe data */
3408 close(pipe_fd[1]); /* parent won't write the pipe */
3410 cb_data->child = pid;
3412 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
3413 pipe_fd[0], (XtPointer)XtInputReadMask,
3414 MoveToTrashPipeCB, (XtPointer)cb_data);
3418 /*====================================================================
3422 * Run a background process to restore files from the trash can.
3424 * These routines are used both for "normal restores", i.e.,
3425 * restores initiated from the Trash menu or through an ICCCM
3426 * message, as well as "drag&drop restores", i.e., restores done
3427 * by dragging files from the trash can to some other directory.
3428 * For normal resores, file_list contains only simple file names
3429 * (no paths) and target_dir is NULL. For drag&drop restores
3430 * file_list contains complete path names and target_dir contains
3431 * the name of the directoy to which the files should be moved.
3433 *==================================================================*/
3435 /*--------------------------------------------------------------------
3437 * Main routine of background process for RestoreFromTrash
3438 *------------------------------------------------------------------*/
3448 Boolean CheckedAlready)
3457 char **RestoreList= NULL;
3459 /* get full path name of target directory */
3462 Tt_status tt_status;
3463 full_dirname = ResolveLocalPathName(target_host, target_dir,
3464 NULL, home_host_name, &tt_status);
3465 if( TT_OK != tt_status )
3467 /* send return codes back trough the pipe */
3468 pipe_msg = PIPEMSG_DONE;
3470 DPRINTF(("RestoreProcess: Unable to Resolve local path name\n"));
3471 write(pipe_fd, &pipe_msg, sizeof(short));
3472 write(pipe_fd, rc, sizeof(int));
3477 full_dirname = NULL;
3479 /* restore the files */
3480 for (i = 0; i < file_count; i++)
3482 /* Locate file in trash list */
3483 for (j = 0; j < numTrashItems; j++)
3485 /* file_list[i] may be a complete path or just a file name */
3486 if (strcmp(file_list[i], trashCan[j].filename) == 0 ||
3487 strcmp(file_list[i], trashCan[j].intNew) == 0)
3493 /* determine source and target for the move */
3494 if (target_dir == NULL)
3496 /* this is a normal restore */
3497 if (j < numTrashItems)
3499 from = trashCan[j].intNew;
3500 to = trashCan[j].intOrig;
3504 /* can't do a restore if the file wasn't found in the trash list */
3510 /* this is a drag&drop from the trash can to target_dir */
3511 from = file_list[i];
3512 if (j < numTrashItems)
3513 ptr = strrchr(trashCan[j].intOrig, '/');
3515 ptr = strrchr(file_list[i], '/');
3516 strcpy(buf, full_dirname);
3524 status = RestoreObject((Widget) (intptr_t) pipe_fd, MOVE_FILE, from,to,
3525 TRUE, FileOpError, False, NOT_DESKTOP,CheckedAlready);
3526 /* restore was successful */
3527 if(status == (int) True)
3529 else if(status == (int) False)
3535 /* restore failed */
3539 /* send return codes back trough the pipe */
3540 pipe_msg = PIPEMSG_DONE;
3541 DPRINTF(("RestoreProcess: sending DONE\n"));
3542 write(pipe_fd, &pipe_msg, sizeof(short));
3543 write(pipe_fd, rc, file_count * sizeof(int));
3545 XtFree(full_dirname);
3549 /*--------------------------------------------------------------------
3551 * Read and process data sent through the pipe.
3552 *------------------------------------------------------------------*/
3556 XtPointer client_data,
3560 RestoreFromTrashCBData *cb_data = (RestoreFromTrashCBData *)client_data;
3563 char *title, *err_msg, *err_arg;
3567 char **ToRestoreList=NULL;
3568 char **FromRestoreList=NULL;
3569 char *target_host,*target_dir;
3572 /* read the next msg from the pipe */
3574 n = PipeRead(*fd, &pipe_msg, sizeof(short));
3575 DPRINTF(("RestorePipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
3579 case PIPEMSG_FILEOP_ERROR:
3580 PipeRead(*fd, &rc, sizeof(int));
3581 err_msg = PipeReadString(*fd);
3582 err_arg = PipeReadString(*fd);
3583 FileOperationError(toplevel, err_msg, err_arg);
3584 /* This call will popup an error dialog.
3586 FileOperationError(toplevel, err_msg, err_arg);
3588 It's not appropriate at all to popup an error dialog here.
3589 i.e. if there're 1000 files, and the file system is full,
3590 and we're unable to move, would we want to popup 1000
3597 PipeRead(*fd, cb_data->rc, cb_data->file_count * sizeof(int));
3602 "Internal error in RestorePipeCB: bad pipe_msg %d\n",
3606 /* close the pipe and cancel the callback */
3607 DPRINTF(("RestorePipeCB: done\n"));
3614 for (i = 0; i < cb_data->file_count; i++)
3616 /* Locate file in trash list */
3617 for (j = 0; j < numTrashItems; j++)
3619 /* file_list[i] may be a complete path or just a file name */
3620 if (strcmp(cb_data->file_list[i], trashCan[j].filename) == 0 ||
3621 strcmp(cb_data->file_list[i], trashCan[j].intNew) == 0)
3627 if (cb_data->rc[i] == SKIP_FILE)
3629 ToRestoreList = (char **) XtRealloc((char *)ToRestoreList,sizeof(char *) *
3631 ToRestoreList[RestoreIndex-1] = XtNewString(trashCan[j].intOrig);
3632 FromRestoreList = (char **) XtRealloc((char *)FromRestoreList,sizeof(char *) *
3634 FromRestoreList[RestoreIndex-1] = XtNewString( trashCan[j].intNew );
3637 /* Check the return code from the restore */
3638 else if (cb_data->rc[i] == 0)
3640 /* restore was successful: remove the file from the trash list */
3641 if (j < numTrashItems)
3643 /* arrange for the source directory to be updated */
3644 UpdateDirectoryOf(trashCan[j].intOrig);
3646 /* Remove this entry from the trash list */
3647 XtFree ((char *) trashCan[j].intNew);
3648 XtFree ((char *) trashCan[j].intOrig);
3649 XtFree ((char *) trashCan[j].external);
3650 XtFree ((char *) trashCan[j].filename);
3651 for (k = j; k < (numTrashItems - 1); k++)
3652 trashCan[k] = trashCan[k + 1];
3659 char *restore_header,*tmpStr = GETMESSAGE(27,101,
3660 "The following object(s) could not be put back:\n");
3662 restore_header = XtNewString(tmpStr);
3663 if (j < numTrashItems && cb_data->target_dir == NULL)
3664 AddString(&buf, &bufsize, trashCan[j].external, restore_header);
3666 AddString(&buf, &bufsize, cb_data->file_list[i], restore_header);
3667 XtFree(restore_header);
3671 /* Update the trash information file */
3672 if( ! WriteTrashEntries() )
3674 char * tmpStr, * title;
3676 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3677 tmpStr = XtNewString(GETMESSAGE(27, 88, "Cannot write to a temporary file.\nPerhaps your file system is full.\n"));
3678 _DtMessage(toplevel, title, tmpStr, NULL, HelpRequestCB);
3683 /* send a reply to the message that triggered this operation, if any */
3684 if (cb_data->msg != 0) {
3685 tt_message_reply( cb_data->msg );
3686 tttk_message_destroy( cb_data->msg );
3690 /* Report any errors */
3693 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3694 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3696 XtFree ((char *) buf);
3699 UpdateDirectory(NULL, trashFileMgrData->host,
3700 trashFileMgrData->current_directory);
3701 if (cb_data->target_dir)
3702 UpdateDirectory(NULL, cb_data->target_host, cb_data->target_dir);
3704 target_host = XtNewString(cb_data->target_host);
3705 target_dir = XtNewString(cb_data->target_dir);
3707 /* free the callback data */
3708 for (i = 0; i < cb_data->file_count; i++)
3709 XtFree(cb_data->file_list[i]);
3710 XtFree((char *)cb_data->file_list);
3711 XtFree((char *)cb_data->target_host);
3712 XtFree((char *)cb_data->target_dir);
3713 XtFree((char *)cb_data->rc);
3714 XtFree((char *)cb_data);
3717 for(i=0;i<RestoreIndex;i++)
3719 CreateRestoreDialog(FromRestoreList[i],ToRestoreList[i]);
3720 XtFree(FromRestoreList[i]);
3721 XtFree(ToRestoreList[i]);
3723 XtFree((char *) FromRestoreList);
3724 XtFree((char *) ToRestoreList);
3726 XtFree(target_host);
3731 /*--------------------------------------------------------------------
3733 * Start the background process and set up callback for the pipe.
3734 *------------------------------------------------------------------*/
3743 Boolean CheckedAlready)
3745 static char *pname = "RestoreFromTrash";
3746 RestoreFromTrashCBData *cb_data;
3750 if( !TrashInitialized )
3753 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
3758 /* set up callback data */
3759 cb_data = XtNew(RestoreFromTrashCBData);
3760 cb_data->file_list = file_list;
3761 cb_data->file_count = file_count;
3762 cb_data->target_host = target_host;
3763 cb_data->target_dir = target_dir;
3764 cb_data->rc = (int *)XtCalloc(file_count, sizeof(int));
3768 if(-1 == pipe(pipe_fd)) {
3770 "%s: pipe failed, error %d=%s\n",
3771 pname, errno, strerror(errno));
3775 /* fork the process that does the actual work */
3780 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
3781 pname, getppid(), getpid(), errno, strerror(errno));
3788 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
3790 close(pipe_fd[0]); /* child won't read from the pipe */
3792 RestoreProcess(pipe_fd[1], file_list, file_count,
3793 target_host, target_dir, cb_data->rc,CheckedAlready);
3796 DBGFORK(("%s: child exiting\n", pname));
3801 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
3803 /* parent: set up callback to get the pipe data */
3804 close(pipe_fd[1]); /* parent won't write the pipe */
3806 cb_data->child = pid;
3808 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
3809 pipe_fd[0], (XtPointer)XtInputReadMask,
3810 RestorePipeCB, (XtPointer)cb_data);
3815 /*====================================================================
3818 * Run a background process to remove files from the trash can.
3820 *==================================================================*/
3822 /*--------------------------------------------------------------------
3824 * Main routine of background process for EmptyTrash
3825 *------------------------------------------------------------------*/
3830 DeleteList * del_list,
3838 * Delete all files or directories in the list,
3839 * as well as any associated annotations.
3841 for (i = 0; i < del_count; i++)
3843 /* delete the file */
3844 rc[i] = EraseObject(del_list[i].trash);
3847 /* send return codes back trough the pipe */
3848 pipe_msg = PIPEMSG_DONE;
3849 DPRINTF(("EmptyTrashProcess: sending DONE\n"));
3850 write(pipe_fd, &pipe_msg, sizeof(short));
3851 write(pipe_fd, rc, del_count * sizeof(int));
3855 /*--------------------------------------------------------------------
3857 * Read and process data sent through the pipe.
3858 *------------------------------------------------------------------*/
3862 XtPointer client_data,
3866 EmptyTrashCBData *cb_data = (EmptyTrashCBData *)client_data;
3869 char *title, *err_msg, *err_arg;
3870 int problemCount, itemCount;
3875 /* read the next msg from the pipe */
3877 n = PipeRead(*fd, &pipe_msg, sizeof(short));
3878 DPRINTF(("EmptyTrashPipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
3882 case PIPEMSG_FILEOP_ERROR:
3883 PipeRead(*fd, &rc, sizeof(int));
3884 err_msg = PipeReadString(*fd);
3885 err_arg = PipeReadString(*fd);
3886 /* This call will popup an error dialog.
3888 FileOperationError(toplevel, err_msg, err_arg);
3890 It's not appropriate at all to popup an error dialog here.
3891 i.e. if there're 1000 files, and the file system is full,
3892 and we're unable to move, would we want to popup 1000
3900 PipeRead(*fd, cb_data->rc, cb_data->del_count * sizeof(int));
3905 "Internal error in EmptyTrashPipeCB: bad pipe_msg %d\n",
3909 /* close the pipe and cancel the callback */
3910 DPRINTF(("EmptyTrashPipeCB: done\n"));
3919 if (cb_data->removeType == TRASH_FILE)
3921 for (i = 0; i < cb_data->del_count; i++)
3923 /* Locate file in trash list */
3924 for (j = 0; j < numTrashItems; j++)
3925 if (strcmp(cb_data->del_list[i].trash, trashCan[j].intNew) == 0)
3928 /* Check the return code from the erase */
3929 if (cb_data->rc[i] == 0)
3931 /* erase was successful: remove the file from the trash list */
3932 if (j < numTrashItems)
3934 /* Remove this entry from the trash list */
3935 XtFree ((char *) trashCan[j].intNew);
3936 XtFree ((char *) trashCan[j].intOrig);
3937 XtFree ((char *) trashCan[j].external);
3938 XtFree ((char *) trashCan[j].filename);
3939 for (k = j; k < (numTrashItems - 1); k++)
3940 trashCan[k] = trashCan[k + 1];
3951 char * msg = XtNewString(GETMESSAGE(27,96, "The following objects could not be removed from the file system: \n"));
3953 if (j < numTrashItems)
3954 file_name = trashCan[j].external;
3956 file_name = cb_data->del_list[i].trash;
3957 AddString(&buf, &bufsize, file_name, msg);
3964 /* Update the trash information file */
3965 if( ! WriteTrashEntries() )
3967 char * tmpStr, * title;
3969 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3970 tmpStr = XtNewString(GETMESSAGE(27, 88, "Cannot write to a temporary file.\nPerhaps your file system is full.\n"));
3971 _DtMessage(toplevel, title, tmpStr, NULL, HelpRequestCB);
3976 /* Report any errors */
3979 /* If more items than can be displayed, let user know */
3980 if (itemCount < problemCount)
3982 char extraFiles[256];
3983 sprintf(extraFiles, AdditionalHeader, problemCount - itemCount);
3987 GETMESSAGE(27,97, "The following objects could not be placed in the trash can: \n") );
3990 title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3991 _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3993 XtFree ((char *) buf);
3996 if (trashFileMgrData->selected_file_count > 0)
3998 XtSetSensitive(*removeBtn, True);
3999 XtSetSensitive(*restoreBtn, True);
4002 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
4003 XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
4005 UpdateDirectory(NULL, trashFileMgrData->host,
4006 trashFileMgrData->current_directory);
4010 if (cb_data->rc[0] == 0)
4012 /* arrange for the directory containing the file to be updated */
4013 UpdateDirectoryOf(cb_data->del_list[0].trash);
4017 /* put up an error message saying the file couldn't be removed */
4021 tmpStr = GETMESSAGE(27, 81, "Shred File/Folder Error");
4022 title = XtNewString(tmpStr);
4023 tmpStr = GETMESSAGE(27, 82, " The following file could not be removed from the file system: \n");
4024 file_name = cb_data->del_list[0].trash;
4025 buffer = XtMalloc(strlen(tmpStr) + strlen(file_name) + 1);
4026 sprintf(buffer, "%s%s\n", tmpStr, file_name);
4027 _DtMessage(toplevel, title, buffer, NULL, HelpRequestCB);
4034 /* reset removingTrash flag */
4035 removingTrash = False;
4037 /* send a reply to the message that triggered this operation, if any */
4038 if (cb_data->msg != 0) {
4039 tt_message_reply( cb_data->msg );
4040 tttk_message_destroy( cb_data->msg );
4044 /* free the callback data */
4045 for (i = 0; i < cb_data->del_count; i++)
4047 XtFree(cb_data->del_list[i].trash);
4048 XtFree(cb_data->del_list[i].orig);
4050 XtFree((char *)cb_data->del_list);
4051 XtFree((char *)cb_data->rc);
4052 XtFree((char *)cb_data);
4056 /*--------------------------------------------------------------------
4058 * Start the background process and set up callback for the pipe.
4059 *------------------------------------------------------------------*/
4063 DeleteList *del_list,
4068 static char *pname = "EmptyTrash";
4069 EmptyTrashCBData *cb_data;
4073 if( !TrashInitialized )
4076 tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
4081 /* set removingTrash flag */
4082 removingTrash = True;
4084 /* set up callback data */
4085 cb_data = XtNew(EmptyTrashCBData);
4086 cb_data->del_list = del_list;
4087 cb_data->del_count = del_count;
4088 cb_data->removeType = removeType;
4089 cb_data->rc = (int *)XtCalloc(del_count, sizeof(int));
4093 if(-1 == pipe(pipe_fd)) {
4095 "%s: pipe failed, error %d=%s\n",
4096 pname, errno, strerror(errno));
4100 /* fork the process that does the actual work */
4105 "%s: fork failed, ppid %d, pid %d: error %d=%s\n",
4106 pname, getppid(), getpid(), errno, strerror(errno));
4113 DBGFORK(("%s: child forked, pipe %d\n", pname, pipe_fd[1]));
4115 close(pipe_fd[0]); /* child won't read from the pipe */
4117 EmptyTrashProcess(pipe_fd[1], del_list, del_count, cb_data->rc);
4120 DBGFORK(("%s: child exiting\n", pname));
4125 DBGFORK(("%s: forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
4127 /* parent: set up callback to get the pipe data */
4128 close(pipe_fd[1]); /* parent won't write the pipe */
4130 cb_data->child = pid;
4132 XtAppAddInput(XtWidgetToApplicationContext(toplevel),
4133 pipe_fd[0], (XtPointer)XtInputReadMask,
4134 EmptyTrashPipeCB, (XtPointer)cb_data);
4138 CheckDeletePermission(
4140 char *destinationPath)
4142 #if defined(__FreeBSD__) || defined(__OpenBSD__)
4143 struct statfs statbuf;
4144 #elif defined(__NetBSD__)
4145 struct statvfs statbuf;
4147 struct stat statbuf;
4151 #if defined(__FreeBSD__) || defined(__OpenBSD__)
4152 if (statfs(parentdir,&statbuf) < 0) /* does not exist */
4153 #elif defined(__NetBSD__)
4154 if (statvfs(parentdir,&statbuf) < 0) /* does not exist */
4156 if (lstat(parentdir,&statbuf) < 0) /* does not exist */
4160 /* check if we are root */
4163 /* if NFS, need to check if server trusts root */
4164 #if defined(CSRG_BASED)
4165 if (!strcmp(statbuf.f_fstypename, "nfs")) /* Root user and nfs */
4167 if (FileSysType(statbuf.st_dev) < 0) /* Root user and nfs */
4171 tmpfile = tempnam(parentdir,"quang");
4172 if (creat(tmpfile,O_RDONLY)< 0) /* Create a temporary file */
4174 if (remove(tmpfile) < 0) /* Delete the created file */
4178 /* root user can delete anything */
4182 /* check for write and execute permisssion on parent dir */
4183 if (CheckAccess(parentdir, W_OK | X_OK) < 0)
4186 /* copy destinationPath to tmp buffer */
4187 strcpy(fname, destinationPath);
4189 return CheckDeletePermissionRecur(fname);
4194 CheckDeletePermissionRecur(
4195 char *destinationPath)
4197 struct stat statbuf;
4203 DPRINTF(("CheckDeletePermissionRecur(\"%s\")\n", destinationPath));
4205 if (lstat(destinationPath, &statbuf) < 0)
4206 return -1; /* probably does not exist */
4208 if (! S_ISDIR(statbuf.st_mode))
4209 return 0; /* no need to check anything more */
4211 dirp = opendir (destinationPath);
4213 return -1; /* could not read directory */
4218 while (dp = readdir (dirp))
4220 if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
4224 /* check for write permission in this directory */
4225 if (CheckAccess(destinationPath, W_OK|X_OK) < 0)
4228 /* append a '/' to the end of directory name */
4229 fnamep = destinationPath + strlen(destinationPath);
4235 /* append file name to end of directory name */
4236 strcpy(fnamep, dp->d_name);
4238 /* recursively check permission on this file */
4239 if (CheckDeletePermissionRecur(destinationPath))
4247 #if !defined(CSRG_BASED)
4253 if(ustat(dev,&u1) < 0)
4263 register char *source,
4264 register char *target,
4265 Boolean isContainer,
4266 void (*errorHandler)(),
4267 Boolean checkForBusyDir,
4269 Boolean CheckedAlready)
4271 struct stat statsrc,stattar;
4273 char *localdir,*chrptr;
4277 if(stat(source,&statsrc) < 0)
4278 if(lstat(source,&statsrc) < 0)
4281 localdir = strdup(target);
4283 chrptr = strrchr(localdir,'/');
4286 if(chrptr == localdir) /* Must be root folder */
4291 if(stat(target,&stattar) >= 0) /* Target exists */
4293 if(CheckDeletePermission(localdir,target))
4294 return ((int)False);
4299 return ((int )FileManip((Widget)w, MOVE_FILE, source, target, TRUE,
4300 FileOpError, False, NOT_DESKTOP));
4303 CreateRestoreDialog(
4307 char *tmpbuf,*title;
4311 char **dirs = (char **) malloc(sizeof(char *) * 2);
4312 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.");
4314 dirs[0] = strdup(source);
4315 dirs[1] = strdup(target);
4316 tmpbuf = XtMalloc(strlen(header) + strlen(target) + 1);
4317 sprintf(tmpbuf, header, target);
4318 title = XtNewString(GETMESSAGE(27, 109, "Put Back Warning"));
4319 dw = (Widget)_DtMessageDialog(toplevel, title,tmpbuf,NULL,True,RestoreVerifyCancel,
4320 RestoreVerifyOk, NULL, HelpRequestCB, False, WARNING_DIALOG);
4322 XtSetArg(args[n], XmNuserData,dirs); n++;
4323 XtSetValues(dw,args,n);
4331 XtPointer client_data,
4332 XtPointer call_data )
4337 Widget mbox = (Widget)client_data;
4339 XtSetArg(args[n],XmNuserData,&dirs); n++;
4340 XtGetValues(mbox,args,n);
4342 if(DirectoryBusy(dirs[1]))
4345 char * tmpStr,*title;
4347 title = XtNewString(GETMESSAGE(27,111,"Put Back Error"));
4348 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.");
4349 msgbuf = XtMalloc(strlen(tmpStr) + strlen(dirs[1])+1);
4350 sprintf (msgbuf, tmpStr, dirs[1]);
4351 _DtMessage(toplevel,title,msgbuf,NULL,HelpRequestCB);
4359 char *realTarget,*tptr;
4363 if(lstat(dirs[1],&s1) < 0)
4365 fsErase(dirs[1],&status,0);
4366 FileList = (char **) XtMalloc(sizeof(char *));
4367 FileList[0] = XtNewString(dirs[0]);
4368 RestoreFromTrash(FileList, (int) 1, NULL, NULL, NULL,True);
4374 XtDestroyWidget(mbox);
4378 RestoreVerifyCancel(
4380 XtPointer client_data,
4381 XtPointer call_data )
4387 Widget mbox = (Widget)client_data;
4389 XtSetArg(args[n],XmNuserData,&dirs); n++;
4390 XtGetValues(mbox,args,n);
4394 XtDestroyWidget(mbox);