Convert uses of XKeycodeToKeysym (deprecated) to XkbKeycodeToKeysym
[oweals/cde.git] / cde / programs / dtfile / Trash.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
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)
10  * any later version.
11  *
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
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: Trash.c /main/12 1999/12/09 13:07:25 mgreess $ */
24 /************************************<+>*************************************
25  ****************************************************************************
26  *
27  *   FILE:           Trash.c
28  *
29  *   COMPONENT_NAME: Desktop File Manager (dtfile)
30  *
31  *   Description:    Source file for the trash code.
32  *
33  *   FUNCTIONS: AddToDeleteList
34  *              CheckDeletePermission
35  *              CheckDeletePermissionRecur
36  *              CloseTrash
37  *              ConfirmCancel
38  *              ConfirmOk
39  *              ConfirmRemove
40  *              CreateTrashFilename
41  *              CreateTrashMenu
42  *              DropOnTrashCan
43  *              EmptyTrash
44  *              EmptyTrashPipeCB
45  *              EmptyTrashProcess
46  *              EraseDir
47  *              EraseObject
48  *              FileFromTrash
49  *              InitializeTrash
50  *              MatchesSacredDirectory
51  *              MessageToFileList
52  *              MessagesToFileList
53  *              MoveOutOfTrashCan
54  *              MoveToTrash
55  *              MoveToTrashPipeCB
56  *              MoveToTrashProcess
57  *              Noop
58  *              ReadTrashList
59  *              Remove
60  *              RemoveCancelCB
61  *              RemoveOkCB
62  *              Restore
63  *              RestoreFromTrash
64  *              RestorePipeCB
65  *              RestoreProcess
66  *              Select_All
67  *              SensitizeTrashBtns
68  *              TrashCreateDialog
69  *              TrashDisplayHandler
70  *              TrashEmpty
71  *              TrashEmptyHandler
72  *              TrashIsInitialized
73  *              TrashRemoveHandler
74  *              TrashRemoveNoConfirmHandler
75  *              TrashRestoreHandler
76  *              Unselect_All
77  *              UpdateDirectoryOf
78  *              VerifyCancel
79  *              VerifyCleanup
80  *              VerifyOk
81  *              WriteEntry
82  *              WriteTrashEntries
83  *
84  *   (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
85  *   (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
86  *   (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
87  *   (c) Copyright 1993, 1994, 1995 Novell, Inc.
88  *
89  ****************************************************************************
90  ************************************<+>*************************************/
91
92 #include <EUSCompat.h>
93 #include <stdio.h>
94 #include <sys/types.h>
95 #include <sys/stat.h>
96 #include <fcntl.h>
97 #include <pwd.h>
98 #include <errno.h>
99 #include <limits.h>
100 #include <dirent.h>
101 #include <unistd.h>
102 #include <string.h>
103 #include <stdint.h>
104 #if defined(CSRG_BASED)
105 #include <sys/param.h>
106 #include <sys/mount.h>
107 #else
108 #if defined(__linux__)
109 #include <sys/vfs.h>
110 #include <linux/magic.h>
111 #else
112 #include <ustat.h>
113 #endif
114 #endif
115
116 #include <Xm/RowColumn.h>
117 #include <Xm/CascadeB.h>
118 #include <Xm/List.h>
119 #include <Xm/Frame.h>
120 #include <Xm/MainW.h>
121 #include <Xm/PushBG.h>
122 #include <Xm/SeparatoG.h>
123 #include <Xm/MessageB.h>
124 #include <Xm/MwmUtil.h>
125 #include <Xm/XmP.h>
126 #include <X11/Shell.h>
127 #include <X11/Xatom.h>
128 #include <Xm/Protocols.h>
129
130 #include <Dt/Action.h>
131 #include <Dt/DtP.h>
132 #include <Dt/FileM.h>
133 #include <Dt/DtNlUtils.h>
134 #include <Dt/Connect.h>
135 #include <Dt/Indicator.h>
136 #include <Dt/EnvControlP.h>
137 #include <Dt/Wsm.h>
138 #include <Dt/Dnd.h>
139 #include <Dt/SharedProcs.h>
140 #include "DtSvcInternal.h" /* _DtGetMask */
141
142 #include <Tt/tttk.h>
143
144 #include "Encaps.h"
145 #include "SharedProcs.h"
146 #include "MultiView.h"
147 #include "Help.h"
148 #include "FileMgr.h"
149 #include "Desktop.h"
150 #include "Main.h"
151 #include "SharedMsgs.h"
152 #include "dtcopy/fsrtns.h"
153
154 #define AdditionalHeader (GETMESSAGE(27,98, "(Plus %d additional object(s))"))
155
156 /* Trash file errors */
157 #define NO_FILE_ERROR         0
158 #define BAD_FILE_ERROR       -1
159 #define VERIFY_DIR           -2
160 #define VERIFY_FILE          -3
161 #define BAD_TRASH_DIRECTORY  -4
162 #define BAD_TRASH_FILE       -5
163 #define NO_TRASH_FILE        -6
164 #define SKIP_FILE            -7
165 #define BAD_TRASH            -8
166 #define BAD_FILE_SACRED      -9
167
168 /* types of messages sent through the pipe */
169 #define PIPEMSG_FILEOP_ERROR  1
170 #define PIPEMSG_OTHER_ERROR   3
171 #define PIPEMSG_DONE          7
172 #define PIPEMSG_TARGET_TIME   5
173 #define PIPEMSG_FILE_MODIFIED 6
174
175 /*
176  * Structure describing each file in the trash can.
177  * Includes the external host:/name format, the original internal
178  * /nfs/host/name format, and the internal /nfs/host/new_name describing
179  * the temporary location of the file.  The `problem' flag is used to
180  * indicate if a problem occurred when the file was physically being
181  * removed.
182  */
183
184 typedef struct {
185    Boolean problem;
186    String  intNew;
187    String  intOrig;
188    String  external;
189    String  filename;
190 } TrashEntry;
191
192 /* callback data MoveToTrash */
193 typedef struct
194 {
195    char **file_list;
196    int file_count;
197    int done_count;
198    char **path;
199    char **to;
200    int *rc;
201    Tt_message msg;
202 #ifdef SUN_PERF
203    Tt_message *msg_list ;
204    int msg_cnt ;
205 #endif /* SUN_PERF */
206    int child;
207 } MoveToTrashCBData;
208
209 #ifdef SUN_PERF
210 typedef struct
211 {
212    Tt_message *msg_list ;
213    int msg_cnt ;
214 } Tt_msg_cache ;
215 #endif /* SUN_PERF */
216
217 /* callback data RestoreFromTrash */
218 typedef struct
219 {
220    char **file_list;
221    int file_count;
222    char *target_host;
223    char *target_dir;
224    int *rc;
225    Tt_message msg;
226    int child;
227 } RestoreFromTrashCBData;
228
229 /* callback data EmptyTrash */
230 typedef struct
231 {
232    char *trash;
233    char *orig;
234 } DeleteList;
235
236 typedef struct
237 {
238    DeleteList *del_list;
239    int del_count;
240    int removeType;
241    int *rc;
242    Tt_message msg;
243    int child;
244 } EmptyTrashCBData;
245
246
247 DialogData *trashDialogData;          /* fm data associated with trash dialog */
248 FileMgrData *trashFileMgrData = NULL;
249
250 Boolean removingTrash = False;
251 Widget trashShell;
252 DialogData * primaryTrashHelpDialog = NULL;
253 DialogData ** secondaryTrashHelpDialogList = NULL;
254 int secondaryTrashHelpDialogCount = 0;
255
256 /* Forward prototype */
257 int EraseDir(char *dir_name);
258 /* From dtcopy/fsrtns.c */
259 extern int EmptyDir(char *sourceP, int rm, int force);
260
261                                             /* 'defines' for trash files */
262 static char * TRASH_DIR = ".dt/Trash";
263 static char * TRASH_INFO_FILE = ".dt/Trash/.trashinfo";
264 static char * NEW_TRASH_INFO_FILE = ".dt/.tmptrashinfo";
265
266 static char * RM = "/bin/rm";
267 static char * RM_ARGS = "-rf";
268
269 static Widget * selectAllBtn = NULL;
270 static Widget * restoreBtn = NULL;
271 static Widget * removeBtn = NULL;
272
273 static int trashMenuItemCount = 22;         /* trash menu items */
274 static MenuDesc * trashMenu = NULL;
275
276 static TrashEntry * trashCan = NULL;
277 static int trashListSize = 0;
278 static int numTrashItems = 0;
279
280 static Boolean trashDialogPosted;
281
282 static char * TrashInfoFileName = NULL;
283 static char * NewTrashInfoFileName = NULL;
284
285 static char ** sacred_dir_list = NULL;   /* list of directories in trash path */
286 static int sacred_dir_list_size = 0;
287 static int sacred_dir_count = 0;
288
289 static Boolean verifyPromptsEnabled;     /* do we prompt the user? */
290
291 static Tt_message global;
292 #ifdef SUN_PERF
293 static Tt_message *global_msg_list ;
294 static int global_msg_cnt = 0 ;
295 #endif /*  SUN_PERF */
296 static Boolean TrashInitialized = False;
297
298 /********    Static Function Declarations    ********/
299
300 static String CreateTrashFilename(
301                         String baseName,
302                         Boolean uniqueTest);
303 static void MessageToFileList(
304                         Tt_message msg,
305                         char ***file_list,
306                         int *file_count);
307 #ifdef SUN_PERF
308 static void MessagesToFileList(
309                         Tt_message *msg_list,
310                         int msg_cnt,
311                         char ***file_list,
312                         int *file_count);
313 #endif /* SUN_PERF */
314 static int WriteEntry(
315                         FILE *id,
316                         String external,
317                         String internal) ;
318 static Boolean MatchesSacredDirectory(
319                         String file) ;
320 static void VerifyCleanup(
321                         Widget mbox,
322                         Boolean completeDelete) ;
323 static void VerifyCancel(
324                         Widget w,
325                         XtPointer client_data,
326                         XtPointer call_data) ;
327 static void VerifyOk(
328                         Widget w,
329                         XtPointer client_data,
330                         XtPointer call_data) ;
331 static Boolean WriteTrashEntries( void ) ;
332 static void Select_All(
333                         Widget w,
334                         XtPointer client_data,
335                         XtPointer call_data) ;
336 static void Unselect_All(
337                         Widget w,
338                         XtPointer client_data,
339                         XtPointer call_data) ;
340 void CloseTrash(
341                         Widget w,
342                         XtPointer client_data,
343                         XtPointer call_data) ;
344 static String GetBasePath(
345                         String fullPath) ;
346 static Boolean ReadTrashList( void ) ;
347 static void RemoveOkCB(
348                         Widget w,
349                         XtPointer client_data,
350                         XtPointer call_data ) ;
351 static void RemoveCancelCB(
352                         Widget w,
353                         XtPointer client_data,
354                         XtPointer call_data ) ;
355 static void ConfirmOk(
356                         Widget w,
357                         XtPointer client_data,
358                         XtPointer call_data ) ;
359 static void ConfirmCancel(
360                         Widget w,
361                         XtPointer client_data,
362                         XtPointer call_data ) ;
363 static void Noop (
364                         Widget w,
365                         XtPointer clientData,
366                         XtPointer callData) ;
367 static void AddToDeleteList(
368                         DeleteList *deleteList,
369                         int i,
370                         char *filename) ;
371 static void MoveToTrash(
372                         char **file_list,
373                         int file_count,
374                         Boolean do_verify_checks,
375                         Tt_message msg) ;
376 static void RestoreFromTrash(
377                         char **file_list,
378                         int file_count,
379                         char *target_host,
380                         char *target_dir,
381                         Tt_message msg,
382                         Boolean CheckedAlready) ;
383 static void EmptyTrash(
384                         DeleteList *del_list,
385                         int del_count,
386                         int removeType,
387                         Tt_message msg) ;
388 static int CheckDeletePermissionRecur(
389                         char *dir);
390 static void RestoreVerifyOk(
391                         Widget w,
392                         XtPointer client_data,
393                         XtPointer call_data ) ;
394 static void RestoreVerifyCancel(
395                         Widget w,
396                         XtPointer client_data,
397                         XtPointer call_data ) ;
398 static int RestoreObject(
399                         Widget w,
400                         int mode,
401                         char *source,
402                         char *target,
403                         Boolean  isContainer,
404                         void (*errorHandler)(),
405                         Boolean checkForBusyDir,
406                         int type ,
407                         Boolean CheckedAlready);
408 static void CreateRestoreDialog(
409                         char  *source,
410                         char *target);
411
412 /********    End Static Function Declarations    ********/
413
414 Boolean
415 TrashIsInitialized( void )
416 {
417   return( TrashInitialized );
418 }
419
420 Boolean
421 InitializeTrash( Boolean enableVerifyPrompt )
422 {
423   char * ptr;
424   struct stat statInfo;
425
426   /* Build the 'TRASH' directory */
427   trash_dir = XtMalloc(strlen(users_home_dir) +
428                        strlen(TRASH_DIR) +
429                        1);
430   sprintf(trash_dir, "%s%s", users_home_dir, TRASH_DIR);
431   if (stat(trash_dir, &statInfo) < 0)
432     mkdir(trash_dir, S_IRUSR | S_IWUSR | S_IXUSR |
433           S_IRGRP | S_IWGRP | S_IXGRP |
434           S_IROTH | S_IWOTH | S_IXOTH);
435
436   /* build path to .trashinfo file */
437   TrashInfoFileName = XtMalloc(strlen(users_home_dir) + strlen(TRASH_INFO_FILE) + 1);
438   sprintf(TrashInfoFileName, "%s%s", users_home_dir, TRASH_INFO_FILE);
439
440   /* */
441   NewTrashInfoFileName = XtMalloc(strlen(users_home_dir) +
442                                   strlen(NEW_TRASH_INFO_FILE)
443                                   + 1);
444   sprintf(NewTrashInfoFileName, "%s%s", users_home_dir, NEW_TRASH_INFO_FILE);
445
446   /* Keep track of whether to prompt for user verification */
447   verifyPromptsEnabled =  enableVerifyPrompt;
448
449   /*
450    * Create an array of paths and filenames which we will not allow the
451    * user to delete, because deleting any of them will cause trash to
452    * stop working.
453    */
454   sacred_dir_list_size = 5;
455   sacred_dir_list = (char **)XtMalloc(sizeof(char *) * sacred_dir_list_size);
456
457   sacred_dir_list[sacred_dir_count++] = XtNewString("/");
458   sacred_dir_list[sacred_dir_count++] = XtNewString(TrashInfoFileName);
459
460   ptr = TrashInfoFileName + 1;
461   while(ptr = DtStrchr(ptr, '/'))
462   {
463     /* All parent components of the user's home dir cannot be deleted */
464     *ptr = '\0';
465     if (sacred_dir_count >= sacred_dir_list_size)
466     {
467          /* Grow the list */
468       sacred_dir_list_size += 5;
469       sacred_dir_list = (char **)XtRealloc((char *)sacred_dir_list,
470                                            sizeof(char *) * sacred_dir_list_size);
471     }
472     sacred_dir_list[sacred_dir_count++] = XtNewString(TrashInfoFileName);
473     *ptr = '/';
474     ptr++;
475   }
476
477   /* load and verify existence for files previously left in the trash can */
478   TrashInitialized = ReadTrashList( );
479   return( TrashInitialized );
480 }
481
482 /************************************************************************
483  *
484  * TrashCreateDialog
485  *   This function must be called before any other interatctions with the
486  *   trash can.  It creates the trash dialog, opens the trash file, and
487  *   loads information about any files previously left in the trash can.
488  *
489  *   This function is called from main().
490  *
491  ************************************************************************/
492
493 void
494 TrashCreateDialog(
495         Display *display )
496 {
497    char * tmpStr;
498    char * title;
499    DialogData * dialog_data;
500    FileMgrData * file_mgr_data;
501
502    if( ! TrashInitialized )
503      return;
504
505    /* Create the trash dialog window */
506    /* load trash title */
507    tmpStr = GETMESSAGE(27, 9, "Trash Can");
508    title = XtNewString(tmpStr);
509
510    /* Initially, no items can be selected */
511    if (removeBtn != NULL)
512       XtSetSensitive(*removeBtn, False);
513    if (restoreBtn != NULL)
514       XtSetSensitive(*restoreBtn, False);
515
516    /* retrieve the default data for a file manager dialog -- */
517    /* the TrashView flag affects this data                   */
518    TrashView = True;
519    dialog_data = _DtGetDefaultDialogData (file_mgr_dialog);
520    file_mgr_data = (FileMgrData *) dialog_data->data;
521    trashFileMgrData = file_mgr_data;
522    trashDialogData = dialog_data;
523
524    /* build up directory set for trash directory */
525    FileMgrBuildDirectories (file_mgr_data, home_host_name, trash_dir);
526
527    /* initialize trash data */
528    file_mgr_data->restricted_directory =
529                           XtNewString(file_mgr_data->current_directory);
530    file_mgr_data->title = XtNewString(title);
531    file_mgr_data->toolbox = False;
532    file_mgr_data->width = 300;
533    file_mgr_data->height = 300;
534
535    /* load any positioning information */
536    LoadPositionInfo(file_mgr_data);
537
538    /* build the trash dialog */
539    _DtBuildDialog (NULL, NULL, NULL, dialog_data, NULL, NULL, NULL,
540                 NULL, NULL, False, special_view, title, NULL);
541    TrashView = False;
542
543    XtFree(title);
544 }
545
546
547
548 /************************************************************************
549  *
550  * ReadTrashList
551  *   At startup time, we need to read in the trash information file, to
552  *   find out what, if any, files were left in the trash the last time
553  *   this client was run.  Each entry must be read in and then verified
554  *   that it still exists; if it no longer exists, then someone has been
555  *   mucking with our trash files, so we will remove it from our knowledge
556  *   base.  Otherwise, the file is added to the trash list.
557  *
558  ************************************************************************/
559
560 static Boolean
561 ReadTrashList( void )
562 {
563    int intSize, extSize, bufSize;
564    FILE * trashInfoFileId;
565    String external, intName, internal;
566    char * trashEntry;
567    struct stat statbuf;
568
569    trashInfoFileId = fopen(TrashInfoFileName, "a+");
570    if (trashInfoFileId == 0)
571    {
572      char * msg, * title;
573
574      title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
575
576      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.") )
577                      + strlen( TrashInfoFileName )
578                      + 1 );
579
580      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 );
581
582      _DtMessage(toplevel, title, msg, NULL, HelpRequestCB);
583      XtFree(title);
584      XtFree(msg);
585      return( False );
586    }
587
588    chmod(TrashInfoFileName, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
589
590    bufSize = MAX_PATH * 2;
591    trashEntry = (char *)XtCalloc( 1, bufSize );
592
593    /* Extract the size of the two path strings */
594    while( fgets( trashEntry, bufSize, trashInfoFileId ) != NULL )
595    {
596      int len = strlen( trashEntry );
597
598      if( sscanf( trashEntry, "%d %d", &extSize, &intSize ) == 2 )
599      {
600        external = (char *)XtCalloc( 1, extSize + 1 );
601        intName = (char *)XtCalloc( 1, intSize + 1 );
602
603        if( len > extSize + intSize + 3 )
604        /* extSize + intSize + 3 is a rough estimate of a trash entry
605           a trash entry looks something like the following:
606
607           8 4 /tmp/abc abcd
608
609           extSize is the size of the string '/tmp/abc'
610           intSize is the size of the string 'abcd'
611
612           so the len (the string length of the trash entry has AT LEAST to be
613           bigger than extSize + intSize + 3 separator spaces
614        */
615        {
616          char * tmpPtr;
617
618          /* sscanf can be used to reduce the code.
619             I'm not using it here because I don't have time to research.
620             I just want it to work.
621          */
622          tmpPtr = DtStrchr( trashEntry, ' ' ) + 1;
623          tmpPtr = DtStrchr( tmpPtr, ' ' ) + 1;
624
625          /* Extract the full external and partial internal file names */
626          memcpy( external, tmpPtr, extSize );
627          memcpy( intName, tmpPtr + extSize + 1, intSize );
628
629          /* Create internal/trash name */
630          internal = (char *)XtMalloc(strlen(users_home_dir)
631                                      + strlen(TRASH_DIR)
632                                      + strlen(intName) + 2);
633          sprintf(internal, "%s%s/%s", users_home_dir, TRASH_DIR, intName);
634
635          /* Make sure the file still exists */
636          if (lstat(internal, &statbuf) < 0)
637          {
638            /* File no longer exists; ignore it */
639            XtFree(internal);
640            XtFree(external);
641            XtFree(intName);
642          }
643          else
644          {
645            /* Add to trash list */
646            if (numTrashItems >= trashListSize)
647            {
648              trashListSize += 10;
649              trashCan = (TrashEntry *)XtRealloc((char *) trashCan,
650                                                 sizeof(TrashEntry) * trashListSize);
651            }
652
653            trashCan[numTrashItems].intNew = internal;
654            trashCan[numTrashItems].intOrig = XtNewString(external);
655            trashCan[numTrashItems].external = external;
656            trashCan[numTrashItems].filename = intName;
657            numTrashItems++;
658          } /* end if file exists */
659        }
660        else
661        {
662          XtFree(external);
663          XtFree(intName);
664        }
665      }
666    } /* end while */
667    fclose(trashInfoFileId);
668
669    return( WriteTrashEntries() );
670 }
671
672
673
674 /************************************************************************
675  *
676  * WriteTrashEntries
677  *   This function is responsible for creating a new copy of the trash
678  *   information file.  It will open a new, temporary copy, and will
679  *   copy in the contents of the trash can.  It will then remove the
680  *   old trash information file, and will rename the new one, opening
681  *   a handle to it for later use.
682  *
683  ************************************************************************/
684
685 static Boolean
686 WriteTrashEntries( void )
687 {
688    static String path = NULL;
689    FILE * newFile;
690    int i;
691
692    newFile = fopen(NewTrashInfoFileName, "w+");
693    if (newFile)
694    {
695       chmod(NewTrashInfoFileName,
696             S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
697
698       /* Write all remaining entries */
699       for (i = 0; i < numTrashItems; i++)
700       {
701          if( WriteEntry(newFile, trashCan[i].external, trashCan[i].filename) < 0 )
702          {
703            fclose(newFile);
704            remove(NewTrashInfoFileName);
705            return( False );
706          }
707       }
708
709       /* Remove the original information file, and move the new one */
710       (void) fclose(newFile);
711       if(-1 == chown(NewTrashInfoFileName, getuid(), getgid())) {
712           return( False );      
713       }
714       (void) remove(TrashInfoFileName);
715       (void) rename(NewTrashInfoFileName, TrashInfoFileName);
716       if(-1 == chown(TrashInfoFileName, getuid(), getgid())) {
717           return( False );      
718       }
719       return( True );
720    }
721    else
722      return( False );
723 }
724
725
726
727 /************************************************************************
728  *
729  * WriteEntry
730  *   This function writes an entry to the trash information file.
731  *   It expects the full path name for the original file [external]
732  *   and only the new file name (without path information) [internal].
733  *   If the file ends in '/', then it adds a '.' to the end; this is
734  *   a special case, so that when we parse this information later, we
735  *   are guaranteed to have a directory and a file name.
736  *
737  ************************************************************************/
738
739 static int
740 WriteEntry(
741         FILE *id,
742         String external,
743         String internal )
744 {
745   return( fprintf(id, "%ld %ld %s %s\n",
746                   (long)strlen(external), (long)strlen(internal),
747                   external, internal) );
748 }
749
750
751
752 /************************************************************************
753  *
754  * CreateTrashMenu
755  *
756  ************************************************************************/
757
758 Widget
759 CreateTrashMenu(
760         Widget mainw,
761         FileMgrRec *file_mgr_rec )
762 {
763    int j, i;
764    Widget menu_bar;
765    Arg args[2];
766    static Widget * directoryBarBtn;
767
768    /* Create the menubar hierarchy */
769    trashMenu = (MenuDesc *)XtMalloc(sizeof(MenuDesc) * trashMenuItemCount);
770    j = 0;
771
772    for (i = 0; i < trashMenuItemCount; i++)
773    {
774       trashMenu[i].helpCallback = TrashHelpRequestCB;
775       trashMenu[i].helpData = NULL;
776       trashMenu[i].activateCallback = NULL;
777       trashMenu[i].activateData = NULL;
778       trashMenu[i].maskBit = 0;
779       trashMenu[i].isHelpBtn = False;
780       trashMenu[i].label = NULL;
781       trashMenu[i].mnemonic = NULL;
782    }
783
784
785    /*************************************/
786    /* Create the 'File' menu components */
787    /*************************************/
788
789    directoryBarBtn = &(trashMenu[j].widget);
790    trashMenu[j].type = MENU_PULLDOWN_BUTTON;
791    trashMenu[j].label = GETMESSAGE(20,1, "File");
792    trashMenu[j].label = XtNewString(trashMenu[j].label);
793    trashMenu[j].mnemonic = GETMESSAGE(20,2, "F");
794    trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
795    trashMenu[j].name = "file_trash";
796    trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
797
798    trashMenu[j].type = SHARED_MENU_PANE;
799    trashMenu[j].name = "fileMenuTrash";
800    trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
801
802    selectAllBtn = &(trashMenu[j].widget);
803    trashMenu[j].type = MENU_BUTTON;
804    trashMenu[j].label = GETMESSAGE(20,48, "Select All");
805    trashMenu[j].label = XtNewString(trashMenu[j].label);
806    trashMenu[j].mnemonic = GETMESSAGE(20,51, "S");
807    trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
808    trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
809    trashMenu[j].name = "selectAll";
810    trashMenu[j++].activateCallback = Select_All;
811
812    trashMenu[j].type = MENU_BUTTON;
813    trashMenu[j].label = GETMESSAGE(20,52, "Deselect All");
814    trashMenu[j].label = XtNewString(trashMenu[j].label);
815    trashMenu[j].mnemonic = GETMESSAGE(20,55, "D");
816    trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
817    trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
818    trashMenu[j].name = "deselectAll";
819    trashMenu[j++].activateCallback = Unselect_All;
820
821    trashMenu[j].type = MENU_SEPARATOR;
822    trashMenu[j].name = "separator";
823    trashMenu[j++].helpCallback = NULL;
824
825    restoreBtn = &(trashMenu[j].widget);
826    trashMenu[j].type = MENU_BUTTON;
827    trashMenu[j].label = GETMESSAGE(27,24, "Put back");
828    trashMenu[j].label = XtNewString(trashMenu[j].label);
829    trashMenu[j].mnemonic = GETMESSAGE(27,26, "P");
830    trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
831    trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
832    trashMenu[j].name = "putBack";
833    trashMenu[j++].activateCallback = Restore;
834
835    removeBtn = &(trashMenu[j].widget);
836    trashMenu[j].type = MENU_BUTTON;
837    trashMenu[j].label = GETMESSAGE(27,28, "Shred");
838    trashMenu[j].label = XtNewString(trashMenu[j].label);
839    trashMenu[j].mnemonic = GETMESSAGE(27,30, "h");
840    trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
841    trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
842    trashMenu[j].name = "shred";
843    trashMenu[j++].activateCallback = ConfirmRemove;
844
845    trashMenu[j].type = MENU_SEPARATOR;
846    trashMenu[j].name = "separator";
847    trashMenu[j++].helpCallback = NULL;
848
849    trashMenu[j].type = MENU_BUTTON;
850    trashMenu[j].maskBit = PREFERENCES;
851    trashMenu[j].label = GETMESSAGE(20,141, "Set View Options ...");
852    trashMenu[j].label = XtNewString(trashMenu[j].label);
853    trashMenu[j].mnemonic = GETMESSAGE(20,6, "V");
854    trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
855    trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
856    trashMenu[j].name = "setPreferences";
857    trashMenu[j++].activateCallback = ShowPreferencesDialog;
858
859    trashMenu[j].type = MENU_SEPARATOR;
860    trashMenu[j].name = "separator";
861    trashMenu[j++].helpCallback = NULL;
862
863    trashMenu[j].type = MENU_BUTTON;
864    trashMenu[j].label = GETMESSAGE(20,117, "Close");
865    trashMenu[j].label = XtNewString(trashMenu[j].label);
866    trashMenu[j].mnemonic = GETMESSAGE(20,118, "C");
867    trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
868    trashMenu[j].helpData = HELP_TRASH_DIALOG_STR;
869    trashMenu[j].name = "close";
870    trashMenu[j++].activateCallback = CloseTrash;
871
872
873    /*************************************/
874    /* Create the 'Help' menu components */
875    /*************************************/
876
877    trashMenu[j].type = MENU_PULLDOWN_BUTTON;
878    trashMenu[j].label = GETMESSAGE(20,123, "Help");
879    trashMenu[j].label = XtNewString(trashMenu[j].label);
880    trashMenu[j].mnemonic = GETMESSAGE(20,9, "H");
881    trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
882    trashMenu[j].isHelpBtn = True;
883    trashMenu[j].name = "help_trash";
884    trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
885
886    trashMenu[j].type = SHARED_MENU_PANE;
887    trashMenu[j].name = "help_pane_trash";
888    trashMenu[j++].helpData = HELP_TRASH_DIALOG_STR;
889
890    trashMenu[j].type = MENU_BUTTON;
891    trashMenu[j].label = GETMESSAGE(20,105, "Overview");
892    trashMenu[j].label = XtNewString(trashMenu[j].label);
893    trashMenu[j].mnemonic = GETMESSAGE(20,106, "v");
894    trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
895    trashMenu[j].helpData = HELP_HELP_MENU_STR;
896    trashMenu[j].activateData = HELP_TRASH_OVERVIEW_TOPIC_STR;
897    trashMenu[j].name = "introduction";
898    trashMenu[j++].activateCallback = TrashHelpRequestCB;
899
900    trashMenu[j].type = MENU_SEPARATOR;
901    trashMenu[j].name = "separator";
902    trashMenu[j++].helpCallback = NULL;
903
904    trashMenu[j].type = MENU_BUTTON;
905    trashMenu[j].label = GETMESSAGE(20,107, "Tasks");
906    trashMenu[j].label = XtNewString(trashMenu[j].label);
907    trashMenu[j].mnemonic = GETMESSAGE(20,108, "T");
908    trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
909    trashMenu[j].helpData = HELP_HELP_MENU_STR;
910    trashMenu[j].activateData = HELP_TRASH_TASKS_TOPIC_STR;
911    trashMenu[j].name = "tasks";
912    trashMenu[j++].activateCallback = TrashHelpRequestCB;
913
914    trashMenu[j].type = MENU_BUTTON;
915    trashMenu[j].label = GETMESSAGE(20,109, "Reference");
916    trashMenu[j].label = XtNewString(trashMenu[j].label);
917    trashMenu[j].mnemonic = GETMESSAGE(20,110, "R");
918    trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
919    trashMenu[j].helpData = HELP_HELP_MENU_STR;
920    trashMenu[j].activateData = HELP_TRASH_DIALOG_STR;
921    trashMenu[j].name = "reference";
922    trashMenu[j++].activateCallback = TrashHelpRequestCB;
923
924    trashMenu[j].type = MENU_BUTTON;
925    trashMenu[j].label = GETMESSAGE(20,111, "On Item");
926    trashMenu[j].label = XtNewString(trashMenu[j].label);
927    trashMenu[j].mnemonic = GETMESSAGE(20,112, "O");
928    trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
929    trashMenu[j].helpData = HELP_HELP_MENU_STR;
930    trashMenu[j].activateData = HELP_HELP_MODE_STR;
931    trashMenu[j].name = "onItem";
932    trashMenu[j++].activateCallback = TrashHelpRequestCB;
933
934    trashMenu[j].type = MENU_SEPARATOR;
935    trashMenu[j].name = "separator";
936    trashMenu[j++].helpCallback = NULL;
937
938    usingHelpTrash = &(trashMenu[j].widget);
939    trashMenu[j].type = MENU_BUTTON;
940    trashMenu[j].label = GETMESSAGE(20,113, "Using Help");
941    trashMenu[j].label = XtNewString(trashMenu[j].label);
942    trashMenu[j].mnemonic = GETMESSAGE(20,114, "U");
943    trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
944    trashMenu[j].helpData = HELP_HELP_MENU_STR;
945    trashMenu[j].activateData = HELP_HOME_TOPIC;
946    trashMenu[j].name = "usingHelp";
947    trashMenu[j++].activateCallback = TrashHelpRequestCB;
948
949    trashMenu[j].type = MENU_SEPARATOR;
950    trashMenu[j].name = "separator";
951    trashMenu[j++].helpCallback = NULL;
952
953    trashMenu[j].type = MENU_BUTTON;
954    trashMenu[j].label = GETMESSAGE(27,106, "About Trash Can");
955    trashMenu[j].label = XtNewString(trashMenu[j].label);
956    trashMenu[j].mnemonic = GETMESSAGE(20,116, "A");
957    trashMenu[j].mnemonic = XtNewString(trashMenu[j].mnemonic);
958    trashMenu[j].helpData = HELP_HELP_MENU_STR;
959    trashMenu[j].activateData = HELP_ABOUT_STR;
960    trashMenu[j].name = "version";
961    trashMenu[j++].activateCallback = TrashHelpRequestCB;
962
963    menu_bar = _DtCreateMenuSystem(mainw, "menu_bar_trash", TrashHelpRequestCB,
964                                HELP_TRASH_DIALOG_STR, True,
965                                trashMenu, trashMenuItemCount, NULL,
966                                Noop, Noop);
967
968    /* Fine tune the menubar */
969    XtSetArg(args[0], XmNmarginWidth, 2);
970    XtSetArg(args[1], XmNmarginHeight, 2);
971    XtSetValues(menu_bar, args, 2);
972
973    file_mgr_rec->actions = NULL;
974    file_mgr_rec->action_pane = NULL;
975    file_mgr_rec->directoryBarBtn = *directoryBarBtn;
976
977    return(menu_bar);
978 }
979
980
981
982 /************************************************************************
983  *
984  * TrashDisplayHandler
985  *   This is the ICCCM message handler for the message used to display
986  *   the trash can window.  If the window is already displayed, then
987  *   this call is a no-op.
988  *
989  *   This handler is triggered by the front panel trash icon and by the
990  *   File pulldown 'Show Trash' option.
991  *
992  ************************************************************************/
993
994 void
995 TrashDisplayHandler(
996    Tt_message msg)
997 {
998    Atom   current_ws;
999    Window root;
1000
1001    if (msg != NULL) {
1002       tt_message_reply( msg );
1003       tttk_message_destroy( msg );
1004    }
1005
1006    if (!TrashInitialized)
1007    {
1008      char *tmpStr, *tmpTitle, *tmpMsg;
1009
1010      tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1011      tmpTitle = XtNewString(tmpStr);
1012      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.");
1013      tmpMsg = XtNewString(tmpStr);
1014
1015      _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1016
1017      XtFree(tmpTitle);
1018      XtFree(tmpMsg);
1019      return;
1020    }
1021
1022 #ifdef DEFER_TRASH_CREATION
1023    if( trashFileMgrData == NULL )
1024      TrashCreateDialog (XtDisplay(toplevel));
1025 #endif
1026
1027    /* the encapsulation functions do not set file_mgr_rec until a dialog */
1028    /* is actually displayed                                              */
1029    if (trashFileMgrData->file_mgr_rec == 0)
1030    {
1031       static Pixmap trash_icon = XmUNSPECIFIED_PIXMAP;
1032       static Pixmap trash_mask = XmUNSPECIFIED_PIXMAP;
1033
1034       Pixel background, foreground, top_shadow, bottom_shadow, select;
1035       Colormap colormap;
1036       XClassHint classHints;
1037       FileMgrRec * file_mgr_rec;
1038       unsigned int width;
1039       unsigned int height;
1040       Pixmap pixmap;
1041       Arg args[3];
1042
1043       classHints.res_name = trashFileMgrData->title;
1044       classHints.res_class = DTFILE_CLASS_NAME;
1045
1046       TrashView = True;
1047       _DtShowBuiltDialog(NULL,NULL, trashDialogData, NULL, False, &classHints);
1048       TrashView = False;
1049
1050       file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
1051
1052       trashShell = file_mgr_rec->shell;
1053
1054       if (trash_icon == XmUNSPECIFIED_PIXMAP)
1055       {
1056          XtSetArg (args[0], XmNbackground, &background);
1057          XtSetArg (args[1], XmNcolormap,  &colormap);
1058          XtGetValues (file_mgr_rec->main, args, 2);
1059
1060          XmGetColors (XtScreen (file_mgr_rec->main), colormap, background,
1061                       &foreground, &top_shadow, &bottom_shadow, &select);
1062
1063          pixmap = XmGetPixmap (XtScreen (file_mgr_rec->main), trashIcon,
1064                                         foreground, background);
1065          if( pixmap != XmUNSPECIFIED_PIXMAP)
1066             trash_icon = pixmap;
1067
1068          /* now let's get the mask for the File Manager */
1069          pixmap = _DtGetMask (XtScreen (file_mgr_rec->main), trashIcon);
1070          if( pixmap != XmUNSPECIFIED_PIXMAP)
1071             trash_mask = pixmap;
1072
1073          if (trash_icon != XmUNSPECIFIED_PIXMAP)
1074          {
1075             XtSetArg (args[0], XmNiconPixmap, trash_icon);
1076             if(trash_mask != XmUNSPECIFIED_PIXMAP)
1077             {
1078                XtSetArg (args[1], XmNiconMask, trash_mask);
1079                XtSetValues (trashShell, args, 2);
1080             }
1081             else
1082                XtSetValues (trashShell, args, 1);
1083          }
1084       }
1085    }
1086    else
1087    {
1088       XWMHints *wmhints;
1089
1090       root = RootWindowOfScreen (XtScreen (trashShell));
1091
1092       /*  Change the hints to reflect the current workspace  */
1093       /*  and raise the window                               */
1094
1095       if (DtWsmGetCurrentWorkspace (XtDisplay (trashShell),
1096                                  root, &current_ws) == Success)
1097          DtWsmSetWorkspacesOccupied (XtDisplay (trashShell),
1098                               XtWindow (trashShell), &current_ws, 1);
1099
1100       /* Set the iconify state */
1101       /* Remove the iconify hint from the current shell */
1102       wmhints = XGetWMHints(XtDisplay(trashShell), XtWindow(trashShell));
1103       wmhints->flags |= IconWindowHint;
1104       wmhints->initial_state = NormalState;
1105       XSetWMHints(XtDisplay(trashShell), XtWindow(trashShell), wmhints);
1106       XFree(wmhints);
1107
1108
1109       XtPopup (trashShell, XtGrabNone);
1110       XSync(XtDisplay(trashShell), False);
1111       XRaiseWindow (XtDisplay (trashShell), XtWindow (trashShell));
1112       XMapWindow( XtDisplay (trashShell), XtWindow (trashShell) );
1113       {
1114         Tt_message msg;
1115         msg = tt_pnotice_create(TT_SESSION, "DtActivity_Began");
1116         tt_message_send(msg);
1117         tttk_message_destroy(msg);
1118       }
1119    }
1120
1121    trashDialogPosted = True;
1122 }
1123
1124
1125 /************************************************************************
1126  *
1127  *  ConfirmEmptyCancel
1128  *
1129  ************************************************************************/
1130 static XtPointer trash_popup_client_data;
1131 static void
1132 ConfirmEmptyCancel(
1133         Widget w,
1134         XtPointer client_data,
1135         XtPointer call_data )
1136 {
1137   XtUnmanageChild((Widget)client_data);
1138   XmUpdateDisplay((Widget)client_data);
1139   XtDestroyWidget((Widget)client_data);
1140 }
1141
1142 /************************************************************************
1143  *
1144  *  ConfirmEmptyOk
1145  *
1146  ************************************************************************/
1147
1148 static void
1149 ConfirmEmptyOk(
1150         Widget w,
1151         XtPointer client_data,
1152         XtPointer call_data )
1153 {
1154   /* destroy dialog */
1155   XtUnmanageChild((Widget)client_data);
1156   XmUpdateDisplay((Widget)client_data);
1157   XtDestroyWidget((Widget)client_data);
1158
1159   TrashEmpty();
1160 }
1161
1162 /************************************************************************
1163  *
1164  * TrashEmptyHandler
1165  *   This is a message interface used to really remove all of the
1166  *   trash files.
1167  *
1168  *   This handler is only called from an empty trash action.
1169  *
1170  ************************************************************************/
1171
1172 void
1173 TrashEmptyHandler(
1174    Tt_message msg)
1175 {
1176    if (msg != 0) {
1177      tt_message_reply( msg );
1178      tttk_message_destroy( msg );
1179    }
1180
1181    if(!TrashInitialized)
1182    {
1183      char *tmpStr, *tmpTitle, *tmpMsg;
1184
1185      tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1186      tmpTitle = XtNewString(tmpStr);
1187      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.");
1188      tmpMsg = XtNewString(tmpStr);
1189
1190      _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1191
1192      XtFree(tmpTitle);
1193      XtFree(tmpMsg);
1194      return;
1195    }
1196
1197    {
1198       char *tmpStr, *title, *msg;
1199
1200       tmpStr = GETMESSAGE(27,73, "Shred File(s)");
1201       title = XtNewString(tmpStr);
1202       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");
1203       msg = XtNewString(tmpStr);
1204
1205       _DtMessageDialog(toplevel, title, msg, NULL, TRUE, ConfirmEmptyCancel,
1206               ConfirmEmptyOk, NULL, HelpRequestCB, False, QUESTION_DIALOG);
1207
1208       XtFree(title);
1209       XtFree(msg);
1210    }
1211 }
1212
1213
1214
1215 /************************************************************************
1216  *
1217  * TrashEmpty
1218  *   This is a functional interface used to really remove all of the
1219  *   trash files.
1220  *
1221  ************************************************************************/
1222
1223 void
1224 TrashEmpty(void)
1225 {
1226    if (trashFileMgrData)
1227    {
1228 /*
1229       Select_All(*selectAllBtn, NULL, NULL);
1230       Remove(*removeBtn, NULL, NULL);
1231 */
1232       EmptyDir(trashFileMgrData->current_directory,0,0);
1233    }
1234 }
1235
1236
1237
1238 /************************************************************************
1239  *
1240  * Select_All
1241  *   This is the callback attached to the 'Select All' menu item.  It
1242  *   will select all of the files currently resident in the trash can.
1243  *   It will also sensitize the 'Restore' and 'Remove' buttons, as long
1244  *   as a remove request is not currently in the middle of being processed.
1245  *
1246  ************************************************************************/
1247
1248 static void
1249 Select_All(
1250         Widget w,
1251         XtPointer client_data,
1252         XtPointer call_data )
1253 {
1254    XmUpdateDisplay(w);
1255    SelectAllFiles(trashFileMgrData);
1256    SensitizeTrashBtns();
1257 }
1258
1259
1260
1261 /************************************************************************
1262  *
1263  * Unselect_All
1264  * This is the callback attached to the 'Unselect All' menu item.  It
1265  * will unselect all of the items in the trash can, and will clear
1266  * out our selection array.  It also desensitizes the 'Restore' and
1267  * 'Remove' buttons.
1268  *
1269  ************************************************************************/
1270
1271 static void
1272 Unselect_All(
1273         Widget w,
1274         XtPointer client_data,
1275         XtPointer call_data )
1276 {
1277    XmUpdateDisplay(w);
1278    DeselectAllFiles(trashFileMgrData);
1279    SensitizeTrashBtns();
1280 }
1281
1282
1283
1284 /************************************************************************
1285  *
1286  * SensitizeTrashBtns
1287  *
1288  ************************************************************************/
1289
1290 void
1291 SensitizeTrashBtns(void)
1292 {
1293 #ifdef DEFER_TRASH_CREATION
1294   if( trashFileMgrData
1295       && removeBtn
1296       && restoreBtn )
1297 #endif
1298   {
1299     if (trashFileMgrData->selected_file_count > 0)
1300     {
1301       XtSetSensitive(*removeBtn, True);
1302       XtSetSensitive(*restoreBtn, True);
1303     }
1304     else
1305     {
1306       XtSetSensitive(*removeBtn, False);
1307       XtSetSensitive(*restoreBtn, False);
1308     }
1309   }
1310 }
1311
1312
1313
1314 /************************************************************************
1315  *
1316  * TrashRemoveHandler
1317  *   This is the message handler for the message requesting that
1318  *   a set of files be added to the trashcan.  Each file name comes in
1319  *   in /dir/name format; they are all in a single string, separated
1320  *   by spaces.
1321  *
1322  *   Check for spaces in filenames and destop files.  If we pass these
1323  *   cases, call TrashRemover where the following occurs:
1324  *
1325  *   All of the files will be moved to the trash can directory; any
1326  *   problems encountered will be reported in an error dialog.  Each file
1327  *   which is successfully moved, will be added to the trash information file.
1328  *
1329  ************************************************************************/
1330
1331 void
1332 TrashRemoveHandler(
1333    Tt_message msg)
1334 {
1335    char *ptr;
1336    char *str;
1337    int number;
1338    char **file_list;
1339    int file_count;
1340 #ifdef SUN_PERF
1341    int time_out = 0 ;
1342    int msglistsize = 0 ;
1343 #endif /* SUN_PERF */
1344
1345    /*
1346     * Note: There used to be a call to tt_message_reply() right here at the
1347     *  beginning of the routine.  However, the mailer wants to trash files
1348     *  but wants the notification after the trash has been removed.
1349     *  Therefore, we now put this off until the work is done or an error
1350     *  is detected.
1351     */
1352
1353    if( !TrashInitialized )
1354    {
1355      char *tmpStr, *tmpTitle, *tmpMsg;
1356
1357      tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1358      tmpTitle = XtNewString(tmpStr);
1359      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.");
1360      tmpMsg = XtNewString(tmpStr);
1361
1362      _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1363
1364      XtFree(tmpTitle);
1365      XtFree(tmpMsg);
1366
1367      tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
1368 #ifdef SUN_PERF
1369      global_msg_cnt = 0 ;
1370 #endif /* SUN_PERF */
1371      return;
1372    }
1373 #ifndef SUN_PERF
1374    MessageToFileList(msg, &file_list, &file_count);
1375 #else
1376    /*
1377     * When the Dttrash_Remove handler "TrashRemoveHandler" is invoked for
1378     * the first time it caches the TT request and then invokes
1379     * tttk_block_while() with timeout = 0. This in effect causes the next
1380     * TT request to be processed before tttk_block_while() returns. Now if
1381     * TT msgs are coming in faster than dtfile is handling them, then the
1382     * next TT msg. should be another Dttrash_Remove msg. which will cause
1383     * re-entrance into TrashRemoveHandler. This will continue until the last
1384     * Dttrash_Remove msg. has been processed (and the recursive
1385     * TrashRemoveHandler starts unwinding) after which we check if the
1386     * number of TT msgs are > 0 in which case we fire off the
1387     * process to move the list of files to Trash.
1388     */
1389    global_msg_cnt++ ;
1390    msglistsize =  sizeof(Tt_message) * global_msg_cnt ;
1391    global_msg_list = (Tt_message *)XtRealloc((char *)global_msg_list, msglistsize);
1392    memcpy((char *)&(global_msg_list[global_msg_cnt - 1]),
1393                         (char *)&msg, sizeof(Tt_message)) ;
1394    tttk_block_while(0, 0, time_out) ;
1395
1396    /* If we get to this pt. then either :
1397     * 1. No more Dttrash_Remove msgs. left to process. Thus we should
1398     * process the TT msg list  and move files to Trash.
1399     * OR
1400     * 2. We got a msg. different from Dttrash_Remove. In this case
1401     * we process the msgs. we have so far. The remaining files to
1402     * be trashed will be buffered in another list.
1403     */
1404
1405    if (global_msg_cnt > 0) {
1406    MessagesToFileList(global_msg_list, global_msg_cnt,
1407                 &file_list, &file_count);
1408 #endif /* SUN_PERF */
1409
1410    if (file_count == 0)
1411    {
1412       tt_message_reply( msg );
1413       tttk_message_destroy( msg );
1414 #ifdef SUN_PERF
1415       global_msg_cnt = 0 ;
1416 #endif /* SUN_PERF */
1417       return;
1418    }
1419
1420    /* return if from the trash dir */
1421    if (strncmp(file_list[0], trash_dir, strlen(trash_dir)) == 0)
1422    {
1423       int i;
1424
1425       for (i = 0; i < file_count; i++) {
1426          XtFree( file_list[i] );
1427       }
1428       XtFree( (char *)file_list );
1429       tt_message_status_set( msg, TT_DESKTOP_EALREADY );
1430       tt_message_reply( msg );
1431       tttk_message_destroy( msg );
1432 #ifdef SUN_PERF
1433       global_msg_cnt = 0 ;
1434 #endif /* SUN_PERF */
1435       return;
1436    }
1437
1438    /* post message if file(s) from desktop */
1439    {
1440       Boolean IsToolBox;
1441
1442       str=(char *)IsAFileOnDesktop2(file_list, file_count, &number,&IsToolBox);
1443       /* IsToolBox is unused here, but is required to satisfy the prototype */
1444    }
1445    if (number == 0)
1446    {
1447       MoveToTrash(file_list, file_count, verifyPromptsEnabled, msg);
1448    }
1449    else
1450    {
1451        char * tmpStr;
1452        char *title;
1453        char * template;
1454        char *message;
1455        int i;
1456
1457        /* List will be regenerated by callbacks */
1458        for (i = 0; i < file_count; i++) {
1459           XtFree( file_list[i] );
1460        }
1461        XtFree( (char *)file_list );
1462         file_list  = NULL;
1463        tmpStr = (GETMESSAGE(27,73, "Shred File(s)"));
1464        title = XtNewString(tmpStr);
1465        if (number == 1)
1466        {
1467           if (widget_dragged)
1468              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."));
1469           else
1470              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."));
1471           message = (char *)XtMalloc(strlen(template) + strlen(str) + 1);
1472           sprintf(message, template, str);
1473        }
1474        else
1475        {
1476           if (widget_dragged)
1477              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."));
1478           else
1479              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."));
1480           message = (char *)XtMalloc(strlen(template) + strlen(str) + 1);
1481           sprintf(message, template, str);
1482        }
1483
1484        /* Really ought to pass to OK and Cancel CBs via client_data */
1485 #ifndef SUN_PERF
1486        global = msg;
1487         _DtMessageDialog(toplevel, title, message, NULL, TRUE,
1488                 RemoveCancelCB, RemoveOkCB, NULL, HelpRequestCB, False,
1489                 QUESTION_DIALOG);
1490 #else
1491         {
1492                 Widget dlog ;
1493                 Arg   args[2] ;
1494                 Tt_msg_cache *msg_cache ;
1495
1496                 msg_cache = (Tt_msg_cache *)XtMalloc(sizeof(Tt_msg_cache)) ;
1497                 msg_cache->msg_cnt = global_msg_cnt ;
1498
1499                 msg_cache->msg_list = (Tt_message *)XtMalloc(sizeof(Tt_message) *
1500                                         msg_cache->msg_cnt );
1501                 msg_cache->msg_list[msg_cache->msg_cnt] = NULL ;
1502                 memcpy((char *)msg_cache->msg_list, (char *)global_msg_list,
1503                                 sizeof(Tt_message) * msg_cache->msg_cnt ) ;
1504                 dlog =  (Widget)_DtMessageDialog(toplevel, title, message, NULL, TRUE,
1505                         RemoveCancelCB, RemoveOkCB, NULL, HelpRequestCB, False,
1506                         QUESTION_DIALOG);
1507                 XtSetArg ( args[0], XmNuserData, msg_cache );
1508                 XtSetValues ( dlog, args, 1);
1509         }
1510 #endif /* SUN_PERF */
1511
1512                 XtFree (message);
1513                 XtFree (title);
1514                 XtFree (str);
1515    }
1516 #ifdef SUN_PERF
1517   global_msg_cnt = 0 ;
1518   }
1519 #endif /* SUN_PERF */
1520 }
1521
1522
1523
1524 /************************************************************************
1525  *
1526  *  RemoveOkCB
1527  *      Cleanup and unmanage the remove from DT dialog
1528  *
1529  ************************************************************************/
1530
1531 static void
1532 RemoveOkCB(
1533         Widget w,
1534         XtPointer client_data,
1535         XtPointer call_data )
1536 {
1537    char **file_list;
1538    int file_count;
1539 #ifdef SUN_PERF
1540    Arg args[1] ;
1541    Tt_msg_cache *current_msg_cache ;
1542    int i = 0 ;
1543
1544    XtSetArg( args[0], XmNuserData, &current_msg_cache );
1545    XtGetValues( (Widget)client_data, args, 1 );
1546 #endif /* SUN_PERF */
1547
1548    /* destroy dialog */
1549    XtUnmanageChild((Widget)client_data);
1550    XmUpdateDisplay((Widget)client_data);
1551    XtDestroyWidget((Widget)client_data);
1552
1553    /* remove files */
1554 #ifndef SUN_PERF
1555    MessageToFileList(global, &file_list, &file_count);
1556 #else
1557    MessagesToFileList( current_msg_cache->msg_list, current_msg_cache->msg_cnt,
1558                                 &file_list, &file_count);
1559 #endif /* SUN_PERF */
1560
1561    MoveToTrash(file_list, file_count, verifyPromptsEnabled, NULL);
1562
1563    /* reread desktop files */
1564    CheckDesktop();
1565
1566 #ifndef SUN_PERF
1567    tt_message_reply( global );
1568    tttk_message_destroy( global );
1569    global = 0;
1570 #else
1571    for (i = 0 ; i < current_msg_cache->msg_cnt ; i++) {
1572         tt_message_reply( current_msg_cache->msg_list[i] );
1573         tttk_message_destroy( current_msg_cache->msg_list[i] );
1574    }
1575    XtFree ((char *)current_msg_cache->msg_list);
1576    XtFree ((char *)current_msg_cache);
1577 #endif /* SUN_PERF */
1578 }
1579
1580
1581
1582 /************************************************************************
1583  *
1584  *  RemoveCancelCB
1585  *      Cleanup and unmanage the remove from DT dialog
1586  *
1587  ************************************************************************/
1588
1589 static void
1590 RemoveCancelCB(
1591         Widget w,
1592         XtPointer client_data,
1593         XtPointer call_data )
1594 {
1595 #ifdef SUN_PERF
1596    Arg args[1] ;
1597    Tt_msg_cache *current_msg_cache ;
1598    int i = 0 ;
1599
1600    XtSetArg( args[0], XmNuserData, &current_msg_cache );
1601    XtGetValues( (Widget)client_data, args, 1 );
1602 #endif /* SUN_PERF */
1603
1604     /* destroy dialog */
1605     XtUnmanageChild((Widget)client_data);
1606     XmUpdateDisplay((Widget)client_data);
1607     XtDestroyWidget((Widget)client_data);
1608
1609 #ifndef SUN_PERF
1610     tttk_message_fail( global, TT_DESKTOP_ECANCELED, 0, 1 );
1611     global = 0;
1612 #else
1613    for (i = 0 ; i < current_msg_cache->msg_cnt ; i++)
1614         tttk_message_fail( current_msg_cache->msg_list[i], TT_DESKTOP_ECANCELED, 0, 1 );
1615    XtFree ((char *)current_msg_cache->msg_list);
1616    XtFree ((char *)current_msg_cache);
1617 #endif /* SUN_PERF */
1618 }
1619
1620
1621
1622 /************************************************************************
1623  *
1624  * TrashRemoveNoConfirmHandler
1625  *   This function is a message handler.  It will place the specified set
1626  *   of files into the trash can, without requesting confirmation if, for
1627  *   instance, a file does not have write permission, or a directory is
1628  *   not empty.
1629  *
1630  ************************************************************************/
1631
1632 void
1633 TrashRemoveNoConfirmHandler(
1634    Tt_message msg)
1635 {
1636    char **file_list;
1637    int file_count;
1638
1639    if( !TrashInitialized )
1640    {
1641      char *tmpStr, *tmpTitle, *tmpMsg;
1642
1643      tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
1644      tmpTitle = XtNewString(tmpStr);
1645      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.");
1646      tmpMsg = XtNewString(tmpStr);
1647
1648      _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
1649
1650      XtFree(tmpTitle);
1651      XtFree(tmpMsg);
1652
1653      if (msg != 0)
1654        tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
1655      return;
1656    }
1657
1658    MessageToFileList(msg, &file_list, &file_count);
1659    MoveToTrash(file_list, file_count, False, msg);
1660 }
1661
1662
1663
1664 /************************************************************************
1665  *
1666  * MessageToFileList
1667  *
1668  *   Parses a list of file names from a message
1669  *   and returns an array of strings.
1670  *
1671  *   if (no message arguments) {
1672  *      if (no file attribute) {
1673  *         return length==0 list
1674  *      } else {
1675  *         return length==1 list containing file attribute
1676  *      }
1677  *   } else {
1678  *      if (file attribute) {
1679  *         file attribute is a directory, prepended onto subsequent filenames
1680  *      }
1681  *      if (arg 0 is non-null) {
1682  *         arg 0 interpreted as a space-separated list of filenames
1683  *      }
1684  *      for (args > 0) {
1685  *         arg interpreted as a filename possibly containing spaces
1686  *      }
1687  *   }
1688  *
1689  ************************************************************************/
1690
1691 static void
1692 MessageToFileList(
1693         Tt_message msg,
1694         char ***file_list,
1695         int *file_count)
1696 {
1697    char *dir;
1698    int args;
1699    int arg;
1700    int dirlen;
1701
1702    *file_count = 0;
1703    *file_list = 0;
1704
1705    args = tt_message_args_count( msg );
1706    if (tt_is_err(tt_int_error( args ))) {
1707            args = 0;
1708    }
1709
1710    dir = tt_message_file( msg );
1711    if ((tt_is_err(tt_ptr_error( dir ))) || (dir == 0)) {
1712            dir = "";
1713    } else {
1714      if (args <= 0) {
1715         /* No args means use tt_message_file() as is */
1716        *file_list = (char **)
1717          XtRealloc((char *)(*file_list),
1718                    (*file_count + 1)*sizeof(char *));
1719        (*file_list)[*file_count] = XtNewString(dir);
1720        (*file_count)++;
1721        tt_free(dir);
1722        return;
1723      }
1724      if (dir[strlen(dir)] != '/') {
1725        char *newdir;
1726        if (strcmp(dir, "/") == 0)
1727        {
1728          newdir = (char *)tt_malloc( strlen(dir)+1 );
1729          strcpy( newdir, dir );
1730        }
1731        else
1732        {
1733          newdir = (char *)tt_malloc( strlen(dir)+2 );
1734          strcpy( newdir, dir );
1735          strcat( newdir, "/" );
1736        }
1737        tt_free( dir );
1738        dir = newdir;
1739      }
1740    }
1741    /* Invariant: dir can now be safely concatenated to form a valid path */
1742    dirlen = strlen(dir);
1743    for (arg = 0; arg < args; arg++) {
1744      char *val;
1745      char *val2free;
1746      char *white;
1747      String file;
1748      val = tt_message_arg_val( msg, arg );
1749      if ((tt_is_err(tt_ptr_error( val ))) || (val == 0)) {
1750        continue;
1751      }
1752      val2free = val;
1753      white = "";
1754      if (arg == 0) {
1755        /* Arg 0 is a space-separated list */
1756        white = " ";
1757      }
1758      for (val = DtStrtok(val, white); val; val = DtStrtok(0, white))
1759      {
1760        if (strcmp(val, "/") == 0)
1761        {
1762          file = (char *)XtMalloc( dirlen + 1 );
1763          strcpy( file, dir );
1764        }
1765        else
1766        {
1767          file = (char *)XtMalloc( dirlen + strlen(val) + 1 );
1768          strcpy( file, dir );
1769          strcat( file, val );
1770        }
1771        *file_list = (char **)
1772          XtRealloc((char *)(*file_list),
1773                    (*file_count + 1)*sizeof(char *));
1774        (*file_list)[*file_count] = DtEliminateDots(file);
1775        (*file_count)++;
1776      }
1777      tt_free( val2free );
1778    }
1779    tt_free( dir );
1780 }
1781
1782 #ifdef SUN_PERF
1783 static void
1784 MessagesToFileList(
1785         Tt_message *msg_list,
1786         int msg_cnt,
1787         char ***file_list,
1788         int *file_count)
1789 {
1790    char *dir;
1791    int args;
1792    int arg;
1793    int dirlen;
1794    int num_msgs = 0 ;
1795    Tt_message msg;
1796
1797    *file_count = 0;
1798    *file_list = 0;
1799
1800  for (num_msgs = 0 ; num_msgs < msg_cnt ; num_msgs++)
1801  {
1802    msg = msg_list[num_msgs] ;
1803
1804    args = tt_message_args_count( msg );
1805    if (tt_is_err(tt_int_error( args ))) {
1806            args = 0;
1807    }
1808
1809    dir = tt_message_file( msg );
1810    if ((tt_is_err(tt_ptr_error( dir ))) || (dir == 0)) {
1811            dir = "";
1812    } else {
1813      if (args <= 0) {
1814         /* No args means use tt_message_file() as is */
1815        *file_list = (char **)
1816          XtRealloc((char *)(*file_list),
1817                    (*file_count + 1)*sizeof(char *));
1818        (*file_list)[*file_count] = XtNewString(dir);
1819        (*file_count)++;
1820        tt_free(dir);
1821        return;
1822      }
1823      if (dir[strlen(dir)] != '/') {
1824        char *newdir;
1825        if (strcmp(dir, "/") == 0)
1826        {
1827          newdir = (char *)tt_malloc( strlen(dir)+1 );
1828          strcpy( newdir, dir );
1829        }
1830        else
1831        {
1832          newdir = (char *)tt_malloc( strlen(dir)+2 );
1833          strcpy( newdir, dir );
1834          strcat( newdir, "/" );
1835        }
1836        tt_free( dir );
1837        dir = newdir;
1838      }
1839    }
1840    /* Invariant: dir can now be safely concatenated to form a valid path */
1841    dirlen = strlen(dir);
1842    for (arg = 0; arg < args; arg++) {
1843      char *val;
1844      char *val2free;
1845      char *white;
1846      String file;
1847      val = tt_message_arg_val( msg, arg );
1848      if ((tt_is_err(tt_ptr_error( val ))) || (val == 0)) {
1849        continue;
1850      }
1851      val2free = val;
1852      white = "";
1853      if (arg == 0) {
1854        /* Arg 0 is a space-separated list */
1855        white = " ";
1856      }
1857      for (val = DtStrtok(val, white); val; val = DtStrtok(0, white))
1858      {
1859        if (strcmp(val, "/") == 0)
1860        {
1861          file = (char *)XtMalloc( dirlen + 1 );
1862          strcpy( file, dir );
1863        }
1864        else if (strcmp(val, "/") == 0 && dirlen == 0)
1865        {
1866          file = (char *)XtMalloc( strlen(val) + 1 );
1867          strcpy( file, val );
1868        }
1869        else
1870        {
1871          file = (char *)XtMalloc( dirlen + strlen(val) + 1 );
1872          strcpy( file, dir );
1873          strcat( file, val );
1874        }
1875        *file_list = (char **)
1876          XtRealloc((char *)(*file_list),
1877                    (*file_count + 1)*sizeof(char *));
1878        (*file_list)[*file_count] = DtEliminateDots(file);
1879        (*file_count)++;
1880      }
1881      tt_free( val2free );
1882    }
1883    tt_free( dir );
1884  }
1885 }
1886 #endif /* SUN_PERF */
1887
1888
1889 /************************************************************************
1890  *
1891  * CreateTrashFilename
1892  *   Create trash directory name.
1893  *
1894  ************************************************************************/
1895
1896 static String
1897 CreateTrashFilename(
1898         String baseName,
1899         Boolean uniqueTest )
1900 {
1901    String trashName;
1902    char * extension = NULL;
1903    struct stat statInfo;
1904
1905    /* Create trash path */
1906    /* Give that name a little extra cushion, just in case */
1907    trashName = (char *)XtMalloc(strlen(users_home_dir) + strlen(TRASH_DIR) +
1908                                                          strlen(baseName) + 15);
1909    sprintf(trashName, "%s%s/%s", users_home_dir, TRASH_DIR, baseName);
1910
1911    /* Want to find the extension so the new file name created will preserve
1912       its original datatype.
1913    */
1914    extension = strrchr( baseName, '.' );
1915    if( extension )
1916    {
1917      *extension = 0x0;
1918      ++extension;
1919    }
1920
1921    /* If a file by the trash name already exists, keep building new names */
1922    /* until one by that name doesn't exist and then use it                */
1923    if (uniqueTest && (lstat(trashName, &statInfo) == 0))
1924    {
1925       int i;
1926
1927       for (i = 1; True ; i++)
1928       {
1929          /* Make a duplicate file name */
1930          if( extension )
1931             sprintf(trashName, "%s%s/%s_%d.%s", users_home_dir, TRASH_DIR,
1932                     baseName, i, extension);
1933          else
1934             sprintf(trashName, "%s%s/%s_%d", users_home_dir, TRASH_DIR,
1935                     baseName, i);
1936
1937          /* Is the filename taken? */
1938          if (lstat(trashName, &statInfo) != 0)
1939             break;   /* Nope */
1940       }
1941
1942    } /* end if not unique name */
1943
1944    if( extension )
1945      *(extension-1) = '.';
1946
1947    return(trashName);
1948
1949 }
1950
1951
1952
1953 /************************************************************************
1954  *
1955  * MatchesSacredDirectory
1956  *   Checks if the specify filename matches one of the sacred,
1957  *   non-deleteable files.
1958  *
1959  ************************************************************************/
1960
1961 static Boolean
1962 MatchesSacredDirectory(
1963         String file )
1964 {
1965    int i;
1966
1967    /* Don't allow the user to delete any of the sacred directories */
1968    for (i = 0; i < sacred_dir_count; i++)
1969    {
1970       if (!strcmp(file, sacred_dir_list[i]))
1971          return(True);
1972    }
1973
1974    /* Compare against special desktop directories */
1975    /* remote_sys_dir is currently NULL */
1976    if (strcmp(file, remote_sys_dir) == 0)
1977       return(True);
1978
1979    return(False);
1980 }
1981
1982
1983
1984 /************************************************************************
1985  *
1986  * VerifyOk
1987  *   Callback for 'Delete To Trash' verify dialog Ok push button.
1988  *
1989  ************************************************************************/
1990
1991 static void
1992 VerifyOk(
1993         Widget w,
1994         XtPointer client_data,
1995         XtPointer call_data )
1996 {
1997    VerifyCleanup ((Widget)client_data, True);
1998 }
1999
2000
2001
2002 /************************************************************************
2003  *
2004  * VerifyCancel
2005  *   Callback for 'Delete To Trash' verify dialog Cancel push button.
2006  *
2007  ************************************************************************/
2008
2009 static void
2010 VerifyCancel(
2011         Widget w,
2012         XtPointer client_data,
2013         XtPointer call_data )
2014
2015 {
2016    VerifyCleanup ((Widget)client_data, False);
2017 }
2018
2019
2020
2021 /************************************************************************
2022  *
2023  * VerifyCleanup
2024  *   Callback for 'Delete To Trash' verify dialog push buttons.
2025  *
2026  *   Called from VerifyOk and VerifyCancel.
2027  *
2028  ************************************************************************/
2029
2030 static void
2031 VerifyCleanup(
2032         Widget mbox,
2033         Boolean completeDelete )
2034 {
2035    Arg args[2];
2036    char ** verifylist;
2037    int fileCount;
2038    int i;
2039
2040    /* Unpost dialog, and retrieve the list of verified files */
2041    XtUnmanageChild(mbox);
2042    XmUpdateDisplay(mbox);
2043    XtSetArg(args[0], XmNuserData, &verifylist);
2044    XtGetValues(mbox, args, 1);
2045
2046    /* count the number of files in the list */
2047    for (fileCount = 0; verifylist[fileCount]; fileCount++)
2048       ;
2049
2050    /*
2051     * Start the background process that moves the files into the trash,
2052     * if so instructed
2053     */
2054    if (completeDelete)
2055       MoveToTrash(verifylist, fileCount, False, NULL);
2056
2057    else
2058    {
2059       /* Free up the storage we allocated */
2060       for (i = fileCount; i > 0; i--)
2061          XtFree(verifylist[i]);
2062       XtFree((char *)verifylist);
2063    }
2064
2065    XtDestroyWidget(mbox);
2066 }
2067
2068
2069
2070 /************************************************************************
2071  *
2072  *  ConfirmCancel
2073  *
2074  ************************************************************************/
2075 static XtPointer trash_popup_client_data;
2076 static void
2077 ConfirmCancel(
2078         Widget w,
2079         XtPointer client_data,
2080         XtPointer call_data )
2081 {
2082   XtUnmanageChild((Widget)client_data);
2083   XmUpdateDisplay((Widget)client_data);
2084   XtDestroyWidget((Widget)client_data);
2085
2086   XtSetSensitive(*removeBtn, True);
2087   XtSetSensitive(*restoreBtn, True);
2088   XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
2089   XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
2090   trash_popup_client_data = NULL;
2091 }
2092
2093
2094
2095 /************************************************************************
2096  *
2097  *  ConfirmOk
2098  *
2099  ************************************************************************/
2100
2101 static void
2102 ConfirmOk(
2103         Widget w,
2104         XtPointer client_data,
2105         XtPointer call_data )
2106 {
2107   FileViewData *file_view_data;
2108
2109   /* destroy dialog */
2110   XtUnmanageChild((Widget)client_data);
2111   XmUpdateDisplay((Widget)client_data);
2112   XtDestroyWidget((Widget)client_data);
2113
2114   file_view_data = trashFileMgrData->popup_menu_icon;
2115   if(!file_view_data && trashFileMgrData->selected_file_count)
2116       file_view_data = trashFileMgrData->selection_list[0];
2117   trashFileMgrData->popup_menu_icon = NULL;
2118
2119   if(file_view_data != NULL)
2120       Remove(*removeBtn, file_view_data, NULL);
2121 }
2122
2123
2124
2125 /************************************************************************
2126  *
2127  * ConfirmRemove
2128  *   This is the callback attached to the 'Remove' menu item.
2129  *
2130  ************************************************************************/
2131
2132 void
2133 ConfirmRemove(
2134         Widget w,
2135         XtPointer client_data,
2136         XtPointer call_data )
2137 {
2138   char *tmpStr, *title, *msg;
2139
2140   /* Desensitize remove & restore buttons, until we're done */
2141   XtSetSensitive(*removeBtn, False);
2142   XtSetSensitive(*restoreBtn, False);
2143   XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], False);
2144   XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], False);
2145   XmUpdateDisplay(w);
2146
2147   trash_popup_client_data = client_data;
2148
2149   tmpStr = GETMESSAGE(27,73, "Shred File(s)");
2150   title = XtNewString(tmpStr);
2151   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");
2152   msg = XtNewString(tmpStr);
2153
2154   _DtMessageDialog(toplevel, title, msg, NULL, TRUE, ConfirmCancel, ConfirmOk,
2155                    NULL, HelpRequestCB, False, QUESTION_DIALOG);
2156
2157   XtFree(title);
2158   XtFree(msg);
2159 }
2160
2161
2162
2163 /************************************************************************
2164  *
2165  * Remove
2166  *   While we wait for the background process to complete, we will
2167  *   desensitize the 'Remove' and 'Restore' menu items, since we don't
2168  *   handle multiple requests.
2169  *
2170  ************************************************************************/
2171
2172 void
2173 Remove(
2174         Widget w,
2175         XtPointer client_data,
2176         XtPointer call_data )
2177 {
2178    FileViewData *file_view_data;
2179    Boolean match = False;
2180    DeleteList *deleteList = NULL;
2181    int deleteCount;
2182    int removeType;
2183    int i;
2184
2185
2186    /* Remove may be called to remove a file or to remove a trash file;  */
2187    /* if w is NULL, this is a call to remove a file */
2188    if (w != NULL)
2189    {
2190       removeType = TRASH_FILE;
2191
2192       /* Count number of items to 'Delete To Trash' */
2193       if (client_data != NULL)
2194       {
2195          /* This was called by trash popup */
2196          /* need to check to see if it is selected, if it is restore all
2197             the selected files, else just restore this file */
2198
2199          file_view_data = (FileViewData *)client_data;
2200          for (i = 0;  i < trashFileMgrData->selected_file_count; i++)
2201          {
2202             if (strcmp(file_view_data->file_data->file_name,
2203               trashFileMgrData->selection_list[i]->file_data->file_name) == 0)
2204             {
2205                match = True;
2206             }
2207          }
2208       }
2209       else
2210       {
2211          if (trashFileMgrData->selected_file_count == 0)
2212          {
2213            XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
2214            XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
2215            return;
2216          }
2217
2218          match = True;
2219       }
2220
2221       /* Desensitize remove & restore buttons, until we're done */
2222       XtSetSensitive(*removeBtn, False);
2223       XtSetSensitive(*restoreBtn, False);
2224       XmUpdateDisplay(w);
2225
2226       if (match)
2227          deleteCount = trashFileMgrData->selected_file_count;
2228       else
2229          deleteCount = 1;
2230
2231       /* Create the list of things being deleted */
2232       deleteList = (DeleteList *)XtMalloc(deleteCount * sizeof(DeleteList));
2233
2234       if (match)
2235       {
2236          for (i = 0; i < deleteCount; i++)
2237          {
2238             AddToDeleteList(deleteList, i,
2239                    trashFileMgrData->selection_list[i]->file_data->file_name);
2240          }
2241       }
2242       else
2243          AddToDeleteList(deleteList, 0, file_view_data->file_data->file_name);
2244
2245    }
2246    else
2247    {
2248       removeType = REMOVE_FILE;
2249       deleteList = (DeleteList *)XtMalloc(sizeof(DeleteList));
2250       deleteCount = 1;
2251       deleteList[0].trash = XtNewString(client_data);
2252       deleteList[0].orig = NULL;
2253    }
2254
2255    EmptyTrash(deleteList, deleteCount, removeType, NULL);
2256 }
2257
2258
2259
2260 /************************************************************************
2261  *
2262  * AddToDeleteList
2263  *   Locate a file in the trash list and add it to the delete list.
2264  *
2265  ************************************************************************/
2266
2267 static void
2268 AddToDeleteList(
2269    DeleteList *deleteList,
2270    int i,
2271    char *filename)
2272 {
2273    int j;
2274
2275    /* Locate file in trash list, add entry to delete list */
2276    for (j = 0; j < numTrashItems; j++)
2277    {
2278       if (strcmp(filename, trashCan[j].filename) == 0)
2279       {
2280          /* file found in trash list */
2281          deleteList[i].trash = XtNewString(trashCan[j].intNew);
2282          deleteList[i].orig = XtNewString(trashCan[j].intOrig);
2283          return;
2284          break;
2285       }
2286    }
2287
2288    /* file not found in trash list */
2289    deleteList[i].trash = CreateTrashFilename(filename, FALSE);
2290    deleteList[i].orig = NULL;
2291 }
2292
2293
2294
2295 /************************************************************************
2296  *
2297  * TrashRestoreHandler
2298  *   This function is a message handler.  It will restore the specified set
2299  *   of files from the trash can.
2300  *
2301  ************************************************************************/
2302
2303 void
2304 TrashRestoreHandler(
2305    Tt_message msg)
2306 {
2307    char **file_list;
2308    int file_count;
2309
2310    if( !TrashInitialized )
2311    {
2312      char *tmpStr, *tmpTitle, *tmpMsg;
2313
2314      tmpStr = GetSharedMessage(TRASH_ERROR_TITLE);
2315      tmpTitle = XtNewString(tmpStr);
2316      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.");
2317      tmpMsg = XtNewString(tmpStr);
2318
2319      _DtMessage(toplevel, tmpTitle, tmpMsg, NULL, HelpRequestCB);
2320
2321      XtFree(tmpTitle);
2322      XtFree(tmpMsg);
2323
2324      if (msg != 0) {
2325        tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
2326      }
2327      return;
2328    }
2329
2330    /* Desensitize remove & restore buttons, until we're done */
2331    XtSetSensitive(*removeBtn, False);
2332    XtSetSensitive(*restoreBtn, False);
2333
2334    MessageToFileList(msg, &file_list, &file_count);
2335    RestoreFromTrash(file_list, file_count, NULL, NULL, msg,False);
2336 }
2337
2338
2339
2340 /************************************************************************
2341  *
2342  * Restore
2343  *   This is the callback attached to the 'Restore' menu item.  It will
2344  *   remove from the trash all of the selected files, and will restore
2345  *   them to their original location.  If any problems occur while trying
2346  *   to restore any of the files, then an error dialog will be posted.
2347  *
2348  ************************************************************************/
2349
2350 void
2351 Restore(
2352         Widget w,
2353         XtPointer client_data,
2354         XtPointer call_data )
2355 {
2356    FileViewData *file_view_data;
2357    Boolean match = False;
2358    char **file_list;
2359    int file_count;
2360    int i;
2361
2362    if (client_data != NULL)
2363    {
2364       /* This was called by trash popup */
2365       /* need to check to see if it is selected, if it is restore all
2366          the selected files, else just restore this file */
2367
2368        file_view_data = trashFileMgrData->popup_menu_icon;
2369
2370        /* The object would have been deleted if the following condn is true */
2371        if(!file_view_data && !trashFileMgrData->selected_file_count)
2372            return;
2373        trashFileMgrData->popup_menu_icon = NULL;
2374
2375       for (i = 0;  i < trashFileMgrData->selected_file_count; i++)
2376       {
2377          if (strcmp(file_view_data->file_data->file_name,
2378               trashFileMgrData->selection_list[i]->file_data->file_name) == 0)
2379          {
2380             match = True;
2381          }
2382       }
2383    }
2384    else
2385       match = True;
2386
2387    /* Desensitize remove & restore buttons, until we're done */
2388    XtSetSensitive(*removeBtn, False);
2389    XtSetSensitive(*restoreBtn, False);
2390    XmUpdateDisplay(w);
2391
2392    if (match)
2393    {
2394       file_count = trashFileMgrData->selected_file_count;
2395       file_list = (char **)XtMalloc(file_count * sizeof(char *));
2396       for (i = 0; i < file_count; i++)
2397       {
2398          file_view_data = trashFileMgrData->selection_list[file_count - 1 - i];
2399          file_list[i] = XtNewString(file_view_data->file_data->file_name);
2400       }
2401    }
2402    else
2403    {
2404       file_count = 1;
2405       file_list = (char **)XtMalloc(sizeof(char *));
2406       file_list[0] = XtNewString(file_view_data->file_data->file_name);
2407    }
2408
2409    /* Start the background process that will do the restore */
2410    RestoreFromTrash(file_list, file_count, NULL, NULL, NULL,False);
2411 }
2412
2413
2414
2415 /************************************************************************
2416  *
2417  * CloseTrash
2418  * This is the callback attached to the 'Close' menu item.  It will
2419  * unpost the trash can window.
2420  *
2421  ************************************************************************/
2422
2423 void
2424 CloseTrash(
2425         Widget w,
2426         XtPointer client_data,
2427         XtPointer call_data )
2428 {
2429    int i;
2430
2431    if (trashDialogPosted)
2432    {
2433       XWithdrawWindow(XtDisplay(trashShell), XtWindow(trashShell),
2434                             XDefaultScreen(XtDisplay(trashShell)));
2435       XtPopdown(trashShell);
2436    }
2437
2438
2439    for (i = 0; i < secondaryTrashHelpDialogCount; i++)
2440    {
2441       if (_DtIsDialogShowing(secondaryTrashHelpDialogList[i]))
2442          _DtHideDialog(secondaryTrashHelpDialogList[i], False);
2443       _DtFreeDialogData(secondaryTrashHelpDialogList[i]);
2444    }
2445    XtFree((char *)secondaryTrashHelpDialogList);
2446    secondaryTrashHelpDialogList = NULL;
2447    secondaryTrashHelpDialogCount = 0;
2448
2449    if (primaryTrashHelpDialog)
2450    {
2451       if (_DtIsDialogShowing(primaryTrashHelpDialog))
2452          _DtHideDialog(primaryTrashHelpDialog, False);
2453       _DtFreeDialogData(primaryTrashHelpDialog);
2454    }
2455    primaryTrashHelpDialog = NULL;
2456
2457    if (PositionFlagSet(trashFileMgrData))
2458        SavePositionInfo(trashFileMgrData);
2459
2460    {
2461      FileMgrRec * file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
2462
2463      if( (file_mgr_rec->menuStates & PREFERENCES) == 0 )
2464        file_mgr_rec->menuStates |= PREFERENCES;
2465    }
2466
2467    trashDialogPosted = False;
2468 }
2469
2470
2471
2472 /************************************************************************
2473  *
2474  * DropOnTrashCan
2475  *
2476  ************************************************************************/
2477
2478 void
2479 DropOnTrashCan(
2480         int file_count,
2481         char **host_set,
2482         char **file_set,
2483         DtDndDropCallbackStruct *drop_parameters)
2484 {
2485    int arg_count = 0;
2486    DtActionArg * action_args;
2487    FileMgrRec * file_mgr_rec = (FileMgrRec *)trashFileMgrData->file_mgr_rec;
2488    Position drop_x = drop_parameters->x;
2489    Position drop_y = drop_parameters->y;
2490
2491    _DtBuildActionArgsWithDroppedFiles(NULL, drop_parameters,
2492                                       &action_args, &arg_count);
2493
2494    DtActionInvoke(file_mgr_rec->shell, TRASH_ACTION,
2495                   action_args, arg_count, NULL, NULL,
2496                   trashFileMgrData->current_directory, True, NULL, NULL);
2497
2498    _DtFreeActionArgs(action_args, arg_count);
2499
2500    RepositionIcons(trashFileMgrData, file_set, file_count, drop_x,
2501                                                           drop_y, True);
2502 }
2503
2504
2505
2506 /************************************************************************
2507  *
2508  * MoveOutOfTrashCan
2509  *
2510  ************************************************************************/
2511
2512 void
2513 MoveOutOfTrashCan(
2514         FileMgrData *file_mgr_data,
2515         FileMgrRec *file_mgr_rec,
2516         Window w,
2517         int file_count,
2518         char **host_set,
2519         char **file_set,
2520         Position drop_x,
2521         Position drop_y)
2522 {
2523    int i;
2524    char *target_host;
2525    char *target_dir;
2526    char **file_list;
2527
2528    /*
2529     * Get target host and directory
2530     */
2531    target_dir = XtNewString(file_mgr_data->current_directory);
2532    target_host = XtNewString(file_mgr_data->host);
2533
2534    /*
2535     * Create file list and call RestoreFromTrash
2536     */
2537    file_list = (char **)XtMalloc(file_count * sizeof(char *));
2538
2539    for (i = 0;  i < file_count; i++)
2540       file_list[i] = XtNewString(file_set[i]);
2541
2542    RestoreFromTrash(file_list, file_count, target_host, target_dir, NULL,False);
2543 }
2544
2545
2546
2547 /************************************************************************
2548  *
2549  * FileFromTrash
2550  *   Locate file in trash list based on new internal name.  Return file name.
2551  *
2552  ************************************************************************/
2553
2554 Boolean
2555 FileFromTrash(
2556    char *filename)
2557 {
2558    Boolean IsTrash = False;
2559
2560    if (strncmp(users_home_dir, filename, strlen(users_home_dir)) == 0)
2561    {
2562       if (strncmp
2563          ((filename + strlen(users_home_dir)), TRASH_DIR, strlen(TRASH_DIR))
2564          == 0)
2565         IsTrash = True;
2566    }
2567
2568    return(IsTrash);
2569 }
2570
2571
2572
2573 /************************************************************************
2574  *
2575  * Noop
2576  *   Since the trash does not use shared menupanes, there is no work to
2577  *   be done during the popup and popdown callbacks; therefore, we use
2578  *   and empty function.
2579  *
2580  ************************************************************************/
2581
2582 static void
2583 Noop (
2584    Widget w,
2585    XtPointer clientData,
2586    XtPointer callData )
2587
2588 {
2589    ;
2590 }
2591
2592
2593 /*--------------------------------------------------------------------
2594  * UpdateDirectoryOf:
2595  *   Arrange for the directory containing a file to be updated
2596  *------------------------------------------------------------------*/
2597
2598 static void
2599 UpdateDirectoryOf(
2600    char *path)
2601 {
2602    char *ptr;
2603    char host[256];
2604
2605    /* remove last component from path to get the directory */
2606    ptr = strrchr(path, '/');
2607    if (ptr > path)
2608       *ptr = '\0';
2609
2610    strcpy(host, home_host_name);
2611
2612    /* now arrange for the directory to be updated */
2613    UpdateDirectory(NULL, host, path);
2614
2615    /* restore the path */
2616    if (ptr > path)
2617       *ptr = '/';
2618 }
2619
2620
2621 /*--------------------------------------------------------------------
2622  * EraseObject, EraseDir
2623  *   Routines for recursively deleting files and directories
2624  *------------------------------------------------------------------*/
2625
2626 int
2627 EraseObject(char *file_name)
2628 {
2629    struct stat stat_buf;
2630
2631    if (lstat(file_name, &stat_buf) < 0)
2632       return errno;
2633
2634    else if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
2635       return EraseDir(file_name);
2636
2637    else if (remove(file_name) < 0)
2638       return errno;
2639
2640    else
2641       return 0;
2642 }
2643
2644
2645 int
2646 EraseDir(char *dir_name)
2647 {
2648    DIR *dir;                      /* open directory */
2649    struct dirent *entry;          /* directory entry */
2650    char srcname[MAX_PATH];
2651    int srclen;
2652    int rc;
2653
2654    /* open source directory */
2655    dir = opendir(dir_name);
2656    if (dir == NULL)
2657      return errno;
2658
2659    /* prepare source name */
2660    strcpy(srcname, dir_name);
2661    srclen = strlen(srcname);
2662    if (srcname[srclen - 1] != '/')
2663      srcname[srclen++] = '/';
2664
2665    rc = 0;
2666    while (rc == 0 && (entry = readdir(dir)) != NULL)
2667    {
2668       if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
2669          continue;
2670       strcpy(srcname + srclen, entry->d_name);
2671       rc = EraseObject(srcname);
2672    }
2673
2674    closedir(dir);
2675
2676    if (rc == 0)
2677    {
2678      if (rmdir(dir_name) < 0)
2679      {
2680        if (unlink(dir_name) < 0 )
2681        {
2682          perror(dir_name);
2683          rc = errno;
2684        }
2685      }
2686    }
2687
2688    return rc;
2689 }
2690
2691
2692 /*====================================================================
2693  *
2694  * MoveToTrashProcess
2695  *      Run a background process to move files to the trash can.
2696  *
2697  *==================================================================*/
2698
2699 /*--------------------------------------------------------------------
2700  * MoveToTrashProcess
2701  *   Main routine of background process for MoveToTrash
2702  *------------------------------------------------------------------*/
2703
2704 static void
2705 MoveToTrashProcess(
2706         int pipe_fd,
2707         char **file_list,
2708         int file_count,
2709         Boolean do_verify_checks)
2710 {
2711    char *baseName;
2712    char *path;
2713    char *to;
2714    short pipe_msg;
2715    struct stat statInfo;
2716    DIR * dirp;
2717    struct dirent * entry;
2718    Boolean success;
2719    int i, rc, rv;
2720    char savechar;
2721
2722    for (i = 0; i < file_count; i++)
2723    {
2724       /* get base name and full path */
2725       path = XtNewString(file_list[i]);
2726       baseName = strrchr(file_list[i], '/');
2727       if (baseName == NULL || path == NULL)
2728       {
2729          /* Invalid filename */
2730          XtFree(path);
2731          pipe_msg = PIPEMSG_OTHER_ERROR;
2732          rc = BAD_FILE_ERROR;
2733          DPRINTF(("MoveToTrashProcess: sending BAD_FILE_ERROR\n"));
2734          rv = write(pipe_fd, &pipe_msg, sizeof(short));
2735          rv = write(pipe_fd, &rc, sizeof(int));
2736          continue;
2737       }
2738       if (path && MatchesSacredDirectory(path))
2739       {
2740          /* Invalid filename */
2741          XtFree(path);
2742          pipe_msg = PIPEMSG_OTHER_ERROR;
2743          rc = BAD_FILE_SACRED;
2744          DPRINTF(("MoveToTrashProcess: sending BAD_FILE_SACRED\n"));
2745          rv = write(pipe_fd, &pipe_msg, sizeof(short));
2746          rv = write(pipe_fd, &rc, sizeof(int));
2747          continue;
2748       }
2749
2750       baseName++;
2751       if (do_verify_checks)
2752       {
2753          char *tmpstr = (file_list[i] == (baseName - 1))?"/":file_list[i];
2754          struct stat s1;
2755          Boolean TrashError = False;
2756
2757          /* check write permissions for the file */
2758          if (lstat(path,&s1) < 0)
2759          {
2760             rc = NO_TRASH_FILE;
2761             TrashError = True;
2762          }
2763          else
2764          {
2765            savechar = *(baseName-1);
2766            *(baseName-1) = '\0';
2767            if(CheckDeletePermission(tmpstr, path) != 0)
2768            {
2769               /* No write access; display error message */
2770
2771              if (S_ISDIR(s1.st_mode))
2772                rc = BAD_TRASH_DIRECTORY;
2773              else
2774                rc = BAD_TRASH_FILE;
2775              TrashError = True;
2776            }
2777            *(baseName-1) = savechar;
2778          }
2779          if(TrashError)
2780          {
2781            XtFree(path);
2782            pipe_msg = PIPEMSG_OTHER_ERROR;
2783            DPRINTF(("MoveToTrashProcess: sending BAD_TRASH message\n"));
2784            rv = write(pipe_fd, &pipe_msg, sizeof(short));
2785            rv = write(pipe_fd, &rc, sizeof(int));
2786            continue;
2787          }
2788          else if (CheckAccess(path, W_OK) != 0 && !S_ISLNK(s1.st_mode))
2789          {
2790             /* No write access; ask user for verification */
2791             XtFree(path);
2792             pipe_msg = PIPEMSG_OTHER_ERROR;
2793             rc = VERIFY_FILE;
2794             DPRINTF(("MoveToTrashProcess: sending VERIFY_FILE\n"));
2795             rv = write(pipe_fd, &pipe_msg, sizeof(short));
2796             rv = write(pipe_fd, &rc, sizeof(int));
2797             continue;
2798          }
2799
2800          /*
2801           * If this is a directory, make sure it's empty, i.e.,
2802           * contains only ".", "..", and ".!" or ".~" files.
2803           */
2804          if (lstat(path, &statInfo) == 0 &&
2805              (statInfo.st_mode & S_IFMT) == S_IFDIR &&
2806              (dirp = opendir(path)) != NULL)
2807          {
2808             /* read the directory */
2809             while ((entry = readdir(dirp)) != NULL)
2810             {
2811                if ( !(strcmp(entry->d_name, ".") == 0 ||
2812                       strcmp(entry->d_name, "..") == 0 ||
2813                       strncmp(entry->d_name, ".!", 2) == 0 ||
2814                       strncmp(entry->d_name, ".~", 2) == 0) )
2815                {
2816                   /* found a real file: directory not empty */
2817                   break;
2818                }
2819             }
2820
2821             closedir(dirp);
2822
2823             if (entry != NULL)
2824             {
2825                /* Directory is not empty */
2826                XtFree(path);
2827                pipe_msg = PIPEMSG_OTHER_ERROR;
2828                rc = VERIFY_DIR;
2829                DPRINTF(("MoveToTrashProcess: sending VERIFY_DIR\n"));
2830                rv = write(pipe_fd, &pipe_msg, sizeof(short));
2831                rv = write(pipe_fd, &rc, sizeof(int));
2832                continue;
2833             }
2834          }
2835       } /* end if do_verify_checks */
2836
2837
2838       to = CreateTrashFilename(baseName, TRUE);
2839
2840       /* move file to the trash directory */
2841       success = FileManip((Widget) (intptr_t) pipe_fd, MOVE_FILE, path, to, TRUE,
2842                           FileOpError, True, TRASH_DIRECTORY);
2843       if (success)
2844       {
2845          pipe_msg = PIPEMSG_DONE;
2846          DPRINTF(("MoveToTrashProcess: sending DONE\n"));
2847          rv = write(pipe_fd, &pipe_msg, sizeof(short));
2848          PipeWriteString(pipe_fd, path);
2849          PipeWriteString(pipe_fd, to);
2850       }
2851
2852       XtFree(path);
2853       XtFree(to);
2854    }
2855 }
2856
2857
2858 /*--------------------------------------------------------------------
2859  * MoveToTrashPipeCB:
2860  *   Read and process data sent through the pipe.
2861  *------------------------------------------------------------------*/
2862
2863 static void
2864 MoveToTrashPipeCB(
2865    XtPointer client_data,
2866    int *fd,
2867    XtInputId *id)
2868 {
2869    MoveToTrashCBData *cb_data = (MoveToTrashCBData *)client_data;
2870    short pipe_msg;
2871    int i, j, n, rc;
2872    char *title, *err_msg, *err_arg;
2873    int badCount;
2874    String buf;
2875    int bufsize;
2876    int verifyCount;
2877    String verifybuf;
2878    int verifybufsize;
2879    Boolean vfiles;
2880    Boolean vdirs;
2881    char **verifylist;
2882    int fileCount;
2883    char *baseName;
2884    char *fileName;
2885    DesktopRec *desktopWin;
2886    char *dir_error=NULL,*file_error=NULL,*no_file_error=NULL,*sacred_error=NULL;
2887
2888    /* read the next msg from the pipe */
2889    pipe_msg = -1;
2890    n = PipeRead(*fd, &pipe_msg, sizeof(short));
2891    DPRINTF(("MoveToTrashPipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
2892
2893    switch (pipe_msg)
2894    {
2895       case PIPEMSG_FILEOP_ERROR:
2896          PipeRead(*fd, &rc, sizeof(int));
2897          err_msg = PipeReadString(*fd);
2898          err_arg = PipeReadString(*fd);
2899
2900          FileOperationError(toplevel, err_msg, err_arg);
2901
2902          XtFree(err_msg);
2903          XtFree(err_arg);
2904
2905          cb_data->rc[cb_data->done_count++] = BAD_FILE_ERROR;
2906          break;
2907
2908       case PIPEMSG_OTHER_ERROR:
2909          PipeRead(*fd, &rc, sizeof(int));
2910          cb_data->rc[cb_data->done_count++] = rc;
2911          break;
2912
2913       case PIPEMSG_TARGET_TIME:
2914          break;
2915
2916       case PIPEMSG_FILE_MODIFIED:
2917          break;
2918
2919       case PIPEMSG_DONE:
2920          i = cb_data->done_count++;
2921          cb_data->path[i] = PipeReadString(*fd);
2922          cb_data->to[i] = PipeReadString(*fd);
2923          cb_data->rc[i] = 0;
2924          break;
2925
2926       default:
2927         fprintf(stderr,
2928                 "Internal error in MoveToTrashPipeCB: bad pipe_msg %d\n",
2929                 pipe_msg);
2930         /* Don't know how it can get in here, but if it does, we'll try to
2931          * simulate an error condition.  Without it, the SGI hung up
2932          */
2933         while (cb_data->done_count < cb_data->file_count)
2934            cb_data->rc[cb_data->done_count++] = BAD_FILE_ERROR;
2935
2936    }
2937
2938
2939    /* if still more files to be processed, return now to wait for the rest */
2940    if (cb_data->done_count < cb_data->file_count)
2941       return;
2942
2943
2944    /* close the pipe and cancel the callback */
2945    DPRINTF(("MoveToTrashPipeCB: done\n"));
2946    close(*fd);
2947    XtRemoveInput(*id);
2948
2949    /* process the results */
2950    badCount = 0;
2951    buf = NULL;
2952    bufsize = 0;
2953    verifyCount = 0;
2954    verifybuf = NULL;
2955    verifybufsize = 0;
2956    vfiles = False;
2957    vdirs = False;
2958    verifylist = NULL;
2959    fileCount = 0;
2960
2961    for (i = 0; i < cb_data->file_count; i++)
2962    {
2963      if (cb_data->rc[i]==BAD_TRASH_DIRECTORY || cb_data->rc[i]==BAD_TRASH_FILE)
2964      {
2965         if (badCount < 8)
2966         {
2967            char *tmpmsg;
2968            Boolean errflg = False;
2969            if(cb_data->rc[i] == BAD_TRASH_DIRECTORY)
2970            {
2971               tmpmsg = GETMESSAGE(27, 113,
2972                  "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");
2973               if(dir_error)
2974               {
2975                  dir_error = (char *) XtRealloc(dir_error,strlen(dir_error)+
2976                                                 strlen(cb_data->file_list[i])+5);
2977                  strcat(dir_error,cb_data->file_list[i]);
2978                  strcat(dir_error,"\n");
2979               }
2980               else
2981               {
2982                  dir_error = XtMalloc(strlen(tmpmsg)+
2983                                       strlen(cb_data->file_list[i])+5);
2984                  sprintf(dir_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
2985               }
2986            }
2987            else
2988            {
2989               tmpmsg = GETMESSAGE(27, 114,
2990                                   "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");
2991               if(file_error)
2992               {
2993                  file_error = (char *) XtRealloc(file_error,strlen(file_error)
2994                         + strlen(cb_data->file_list[i])+5);
2995                  strcat(file_error,cb_data->file_list[i]);
2996                  strcat(file_error,"\n");
2997               }
2998               else
2999               {
3000                  file_error = XtMalloc(strlen(tmpmsg)+
3001                                        strlen(cb_data->file_list[i])+5);
3002                  sprintf(file_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
3003               }
3004            }
3005            XtFree(cb_data->file_list[i]);
3006            cb_data->file_list[i] = NULL;
3007         }
3008         badCount++;
3009      }
3010    else if (cb_data->rc[i] == BAD_FILE_SACRED)
3011    {
3012      if (badCount < 8)
3013      {
3014        char *tmpmsg = GETMESSAGE(27, 115,
3015          "The following object(s) cannot be deleted.\nThe desktop cannot function properly without these object(s).\n\n");
3016        if(sacred_error)
3017        {
3018          sacred_error = (char *) XtRealloc(sacred_error,strlen(sacred_error)+
3019                             strlen(cb_data->file_list[i])+5);
3020          strcat(sacred_error,cb_data->file_list[i]);
3021          strcat(sacred_error,"\n");
3022        }
3023        else
3024        {
3025          sacred_error=XtMalloc(strlen(tmpmsg)+strlen(cb_data->file_list[i])+5);
3026          sprintf(sacred_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
3027        }
3028        XtFree(cb_data->file_list[i]);
3029        cb_data->file_list[i] = NULL;
3030      }
3031      badCount++;
3032    }
3033    else if (cb_data->rc[i] == BAD_FILE_ERROR)
3034    {
3035        XtFree(cb_data->file_list[i]);
3036        cb_data->file_list[i] = NULL;
3037    }
3038    else if (cb_data->rc[i] == NO_TRASH_FILE)
3039    {
3040      char *tmpmsg;
3041      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");
3042
3043      if(badCount < 8)
3044      {
3045         if(no_file_error)
3046         {
3047           no_file_error = (char *)XtRealloc(no_file_error,strlen(no_file_error)+
3048                             strlen(cb_data->file_list[i])+5);
3049           strcat(no_file_error,cb_data->file_list[i]);
3050           strcat(no_file_error,"\n");
3051         }
3052         else
3053         {
3054           no_file_error = XtMalloc(strlen(tmpmsg)+
3055                   strlen(cb_data->file_list[i])+5);
3056           sprintf(no_file_error,"%s%s\n",tmpmsg,cb_data->file_list[i]);
3057         }
3058      }
3059      badCount++;
3060    }
3061    else if (cb_data->rc[i] == VERIFY_FILE ||
3062               cb_data->rc[i] == VERIFY_DIR)
3063    {
3064        if (verifyCount < 6)
3065          AddString(&verifybuf, &verifybufsize, cb_data->file_list[i], NULL);
3066        verifyCount++;
3067
3068        /*
3069         * Keep track of whether we have directories only, files only,
3070         * or a combination, so that we can display the appropriate
3071         * label in the dialog.
3072         */
3073        if (cb_data->rc[i] == VERIFY_FILE)
3074          vfiles = True;
3075        else
3076          vdirs = True;
3077
3078        /*
3079         * Add to array which is to be attached to the dialog;
3080         * the array will ultimately be NULL terminated.
3081         */
3082        verifylist = (char **)XtRealloc((char *)verifylist,
3083                                        sizeof(char *) * (verifyCount + 1));
3084        verifylist[verifyCount - 1] = cb_data->file_list[i];
3085      }
3086      else
3087      {
3088        /* Add file to trash list */
3089        if (numTrashItems >= trashListSize)
3090        {
3091          trashListSize += 10;
3092          trashCan = (TrashEntry *)
3093            XtRealloc((char *)trashCan, sizeof(TrashEntry) * trashListSize);
3094        }
3095
3096        trashCan[numTrashItems].problem = False;
3097        trashCan[numTrashItems].intNew = cb_data->to[i];
3098        trashCan[numTrashItems].intOrig = cb_data->path[i];
3099        trashCan[numTrashItems].external = cb_data->file_list[i];
3100
3101        /* extract base file name */
3102        baseName = strrchr(cb_data->to[i], '/');
3103        baseName++;
3104        if (*baseName == '\0')
3105          baseName = ".";
3106        trashCan[numTrashItems].filename = XtNewString(baseName);
3107
3108        numTrashItems++;
3109        fileCount++;
3110
3111        /* arrange for the source directory to be updated */
3112        UpdateDirectoryOf(cb_data->path[i]);
3113
3114        /*
3115         * If the source file was referenced by a desktop object,
3116         * we need to remove the destkop object
3117         */
3118        for (j = 0; j < desktop_data->numIconsUsed; j++)
3119        {
3120          Tt_status tt_status;
3121
3122          desktopWin = desktop_data->desktopWindows[j];
3123          fileName = ResolveLocalPathName( desktopWin->host,
3124                                           desktopWin->dir_linked_to,
3125                                           desktopWin->file_name,
3126                                           home_host_name, &tt_status);
3127
3128          if( TT_OK == tt_status )
3129          {
3130            if (strcmp(fileName, cb_data->path[i]) == 0)
3131 #ifndef SUN_PERF
3132              RemoveDT(desktopWin->shell, (XtPointer)desktopWin, NULL);
3133 #else
3134              RemoveMovedObjectFromDT(desktopWin->shell, (XtPointer)desktopWin,
3135                         cb_data->file_count, cb_data->file_list);
3136 #endif /* SUN_PERF */
3137
3138            XtFree(fileName);
3139          }
3140        }
3141      }
3142    }
3143    if(dir_error)
3144    {
3145      buf = XtMalloc(strlen(dir_error)+3);
3146      sprintf(buf,"%s\n",dir_error);
3147      XtFree(dir_error);
3148    }
3149    if(file_error)
3150    {
3151      if(!buf)
3152      {
3153        buf = XtMalloc(strlen(file_error)+3);
3154        sprintf(buf,"%s\n",file_error);
3155      }
3156      else
3157      {
3158        buf = XtRealloc(buf,strlen(buf)+strlen(file_error)+3);
3159        sprintf(buf,"%s%s\n",buf,file_error);
3160      }
3161      XtFree(file_error);
3162    }
3163    if(no_file_error)
3164    {
3165      if(!buf)
3166      {
3167        buf = XtMalloc(strlen(no_file_error)+3);
3168        sprintf(buf,"%s\n",no_file_error);
3169      }
3170      else
3171      {
3172        buf = XtRealloc(buf,strlen(buf)+strlen(no_file_error)+3);
3173        sprintf(buf,"%s%s\n",buf,no_file_error);
3174      }
3175      XtFree(no_file_error);
3176    }
3177    if(sacred_error)
3178    {
3179      if(!buf)
3180      {
3181        buf = XtMalloc(strlen(sacred_error)+3);
3182        sprintf(buf,"%s\n",sacred_error);
3183      }
3184      else
3185      {
3186        buf = XtRealloc(buf,strlen(buf)+strlen(sacred_error)+3);
3187        sprintf(buf,"%s%s\n",buf,sacred_error);
3188      }
3189      XtFree(sacred_error);
3190    }
3191    if(buf)
3192      bufsize = strlen(buf);
3193
3194    /* Update the trash information file, and the trash window */
3195    if (fileCount > 0)
3196    {
3197       FILE * trashInfoFileId = fopen(TrashInfoFileName, "a+");
3198       if( trashInfoFileId != NULL )
3199       {
3200         for (i = fileCount; i > 0; i--)
3201         {
3202           if( WriteEntry(trashInfoFileId,
3203                          trashCan[numTrashItems - i].external,
3204                          trashCan[numTrashItems - i].filename) < 0 )
3205             break;
3206         }
3207         fflush(trashInfoFileId);
3208         fclose(trashInfoFileId);
3209         if( trashFileMgrData )
3210           UpdateDirectory(NULL, trashFileMgrData->host,
3211                           trashFileMgrData->current_directory);
3212       }
3213    }
3214
3215    /* Check for any bad files; post an error dialog */
3216    if (buf)
3217    {
3218       /* send a reply to the message that triggered this operation, if any */
3219 #ifndef SUN_PERF
3220      if (cb_data->msg != 0) {
3221        /*
3222        Until Action is fixed.
3223        tttk_message_fail( cb_data->msg, TT_DESKTOP_EACCES, 0, 1 );
3224        */
3225        tt_message_reply( cb_data->msg );
3226        tttk_message_destroy( cb_data->msg );
3227 #else
3228        if (cb_data->msg_cnt > 0) {
3229        for (i = 0 ; i < cb_data->msg_cnt ; i++) {
3230            tt_message_reply( cb_data->msg_list[i] ) ;
3231            tttk_message_destroy( cb_data->msg_list[i] ) ;
3232         }
3233        XtFree((char *)cb_data->msg_list) ;
3234        cb_data->msg_cnt = 0 ;
3235 #endif /* SUN_PERF */
3236        cb_data->msg = 0;
3237      }
3238       /* If more items than can be displayed, let user know */
3239       if (badCount > 8)
3240       {
3241          char extraFiles[256];
3242
3243          (void) sprintf(extraFiles, AdditionalHeader, badCount - 8);
3244          AddString(&buf,
3245                    &bufsize,
3246                    extraFiles,
3247                    GETMESSAGE(27,97, "The following objects could not be placed in the trash can:     \n"));
3248       }
3249
3250       title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3251       _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3252       XtFree(title);
3253       XtFree ((char *) buf);
3254    } else {
3255       /* send a reply to the message that triggered this operation, if any */
3256 #ifndef SUN_PERF
3257       if (cb_data->msg != 0) {
3258          tt_message_reply( cb_data->msg );
3259          tttk_message_destroy( cb_data->msg );
3260 #else
3261         if (cb_data->msg_cnt > 0) {
3262         for (i = 0 ; i < cb_data->msg_cnt ; i++) {
3263            tt_message_reply( cb_data->msg_list[i] ) ;
3264            tttk_message_destroy( cb_data->msg_list[i] ) ;
3265         }
3266         XtFree((char *)cb_data->msg_list) ;
3267         cb_data->msg_cnt = 0 ;
3268 #endif /* SUN_PERF */
3269          cb_data->msg = 0;
3270       }
3271    }
3272
3273    /* Check for any files requiring user verification; post a prompt dialog */
3274    /* XXX Really should fail incoming ToolTalk request if user cancels any */
3275    if (verifybuf)
3276    {
3277       char * tmpbuf;
3278       char * header;
3279       Widget dlog;
3280       Arg args[1];
3281
3282       /* If more items than can be displayed, let user know */
3283       if (verifyCount > 6)
3284       {
3285          char extraFiles[256];
3286
3287          (void) sprintf(extraFiles, AdditionalHeader, verifyCount - 6);
3288          AddString(&verifybuf, &verifybufsize, extraFiles, NULL);
3289       }
3290
3291       /*
3292        * Depending upon what type of files are to be displayed in the
3293        * dialog, choose the appropriate dialog text.
3294        */
3295       if (vfiles && vdirs)
3296          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");
3297       else if (vfiles)
3298         header = GETMESSAGE(27,104, "You do not have write permission for the following file(s):\nDo you want to proceed?\n");
3299       else
3300          header = GETMESSAGE(27,100, "The following folder(s) are not empty.\nDo you want to proceed?\n");
3301
3302       tmpbuf = XtMalloc(strlen(header) + strlen(verifybuf) + 1);
3303       sprintf(tmpbuf, "%s%s", header, verifybuf);
3304       title = XtNewString(GETMESSAGE(27, 4, "Trash Can Warning"));
3305       dlog = (Widget)_DtMessageDialog(toplevel, title, tmpbuf, NULL, True,
3306                                   VerifyCancel, VerifyOk, NULL, HelpRequestCB,
3307                                   False, WARNING_DIALOG);
3308       XtFree(title);
3309       XtFree ((char *) verifybuf);
3310       XtFree (tmpbuf);
3311
3312       /*
3313        * Add array as userdata on the dialog.
3314        * NULL terminate the array.
3315        */
3316       verifylist[verifyCount] = NULL;
3317       XtSetArg(args[0], XmNuserData, verifylist);
3318       XtSetValues(dlog, args, 1);
3319    }
3320
3321    /* free the callback data */
3322    XtFree((char *)cb_data->file_list);
3323    XtFree((char *)cb_data->path);
3324    XtFree((char *)cb_data->to);
3325    XtFree((char *)cb_data->rc);
3326    XtFree((char *)cb_data);
3327
3328    CheckDesktop();
3329 }
3330
3331
3332 /*--------------------------------------------------------------------
3333  * MoveToTrash:
3334  *    Start the background process and set up callback for the pipe.
3335  *------------------------------------------------------------------*/
3336
3337 static void
3338 MoveToTrash(
3339         char **file_list,
3340         int file_count,
3341         Boolean do_verify_checks,
3342         Tt_message msg)
3343 {
3344    static char *pname = "MoveToTrash";
3345    MoveToTrashCBData *cb_data;
3346    int pipe_fd[2];
3347    int pid;
3348
3349    if( !TrashInitialized )
3350    {
3351      if (msg != 0) {
3352        tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
3353      }
3354      return;
3355    }
3356
3357    /* set up callback data */
3358    cb_data = XtNew(MoveToTrashCBData);
3359    cb_data->file_list = file_list;
3360    cb_data->file_count = file_count;
3361    cb_data->done_count = 0;
3362    cb_data->path = (char **)XtCalloc(file_count, sizeof(char *));
3363    cb_data->to = (char **)XtCalloc(file_count, sizeof(char *));
3364    cb_data->rc = (int *)XtCalloc(file_count, sizeof(int));
3365    cb_data->msg = msg;
3366 #ifdef SUN_PERF
3367    cb_data->msg_cnt = 0 ;
3368    if (global_msg_cnt > 0) {
3369    cb_data->msg_cnt = global_msg_cnt ;
3370    cb_data->msg_list = (Tt_message *)XtMalloc(sizeof(Tt_message) * cb_data->msg_cnt);
3371    memcpy((char *)cb_data->msg_list,(char *)global_msg_list, sizeof(Tt_message) *
3372         cb_data->msg_cnt) ;
3373    }
3374 #endif /* SUN_PERF */
3375
3376    /* create a pipe */
3377    if(-1 == pipe(pipe_fd)) {
3378        fprintf(stderr,
3379                 "%s:  pipe failed error %d=%s\n",
3380                 pname, errno, strerror(errno));
3381        return;
3382    }
3383
3384    /* fork the process that does the actual work */
3385    pid = fork();
3386    if (pid == -1)
3387    {
3388        fprintf(stderr,
3389                 "%s:  fork failed, ppid %d, pid %d: error %d=%s\n",
3390                 pname, getppid(), getpid(), errno, strerror(errno));
3391        return;
3392    }
3393
3394    if (pid == 0)
3395    {
3396       /* child process */
3397       DBGFORK(("%s:  child forked, pipe %d\n", pname, pipe_fd[1]));
3398
3399       close(pipe_fd[0]);  /* child won't read from the pipe */
3400
3401       MoveToTrashProcess(pipe_fd[1], file_list, file_count, do_verify_checks);
3402       close(pipe_fd[1]);
3403
3404       DBGFORK(("%s:  child exiting\n", pname));
3405
3406       exit(0);
3407    }
3408
3409    DBGFORK(("%s:  forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
3410
3411    /* parent: set up callback to get the pipe data */
3412    close(pipe_fd[1]);  /* parent won't write the pipe */
3413
3414    cb_data->child = pid;
3415
3416    XtAppAddInput(XtWidgetToApplicationContext(toplevel),
3417                  pipe_fd[0], (XtPointer)XtInputReadMask,
3418                  MoveToTrashPipeCB, (XtPointer)cb_data);
3419 }
3420
3421
3422 /*====================================================================
3423  *
3424  * RestoreFromTrash
3425  *
3426  *   Run a background process to restore files from the trash can.
3427  *
3428  *   These routines are used both for "normal restores", i.e.,
3429  *   restores initiated from the Trash menu or through an ICCCM
3430  *   message, as well as "drag&drop restores", i.e., restores done
3431  *   by dragging files from the trash can to some other directory.
3432  *   For normal resores, file_list contains only simple file names
3433  *   (no paths) and target_dir is NULL.  For drag&drop restores
3434  *   file_list contains complete path names and target_dir contains
3435  *   the name of the directoy to which the files should be moved.
3436  *
3437  *==================================================================*/
3438
3439 /*--------------------------------------------------------------------
3440  * RestoreProcess
3441  *   Main routine of background process for RestoreFromTrash
3442  *------------------------------------------------------------------*/
3443
3444 static void
3445 RestoreProcess(
3446         int pipe_fd,
3447         char **file_list,
3448         int file_count,
3449         char *target_host,
3450         char *target_dir,
3451         int *rc,
3452         Boolean CheckedAlready)
3453 {
3454    int i, j, rv;
3455    char *full_dirname;
3456    char *from, *to;
3457    char *ptr;
3458    char buf[MAX_PATH];
3459    short pipe_msg;
3460    int status;
3461    char **RestoreList= NULL;
3462
3463    /* get full path name of target directory */
3464    if (target_dir)
3465    {
3466       Tt_status tt_status;
3467       full_dirname = ResolveLocalPathName(target_host, target_dir,
3468                                           NULL, home_host_name, &tt_status);
3469       if( TT_OK != tt_status )
3470       {
3471         /* send return codes back trough the pipe */
3472         pipe_msg = PIPEMSG_DONE;
3473         rc[0] = -1;
3474         DPRINTF(("RestoreProcess: Unable to Resolve local path name\n"));
3475         rv = write(pipe_fd, &pipe_msg, sizeof(short));
3476         rv = write(pipe_fd, rc, sizeof(int));
3477         return;
3478       }
3479    }
3480    else
3481       full_dirname = NULL;
3482
3483    /* restore the files */
3484    for (i = 0; i < file_count; i++)
3485    {
3486       /* Locate file in trash list */
3487       for (j = 0; j < numTrashItems; j++)
3488       {
3489          /* file_list[i] may be a complete path or just a file name */
3490          if (strcmp(file_list[i], trashCan[j].filename) == 0 ||
3491              strcmp(file_list[i], trashCan[j].intNew) == 0)
3492          {
3493             break;
3494          }
3495       }
3496
3497       /* determine source and target for the move */
3498       if (target_dir == NULL)
3499       {
3500          /* this is a normal restore */
3501          if (j < numTrashItems)
3502          {
3503             from = trashCan[j].intNew;
3504             to = trashCan[j].intOrig;
3505          }
3506          else
3507          {
3508             /* can't do a restore if the file wasn't found in the trash list */
3509             from = to = NULL;
3510          }
3511       }
3512       else
3513       {
3514          /* this is a drag&drop from the trash can to target_dir */
3515          from = file_list[i];
3516          if (j < numTrashItems)
3517             ptr = strrchr(trashCan[j].intOrig, '/');
3518          else
3519             ptr = strrchr(file_list[i], '/');
3520          strcpy(buf, full_dirname);
3521          strcat(buf, ptr);
3522          to = buf;
3523       }
3524
3525       if (to != NULL)
3526       {
3527
3528          status = RestoreObject((Widget) (intptr_t) pipe_fd, MOVE_FILE, from,to,
3529                         TRUE, FileOpError, False, NOT_DESKTOP,CheckedAlready);
3530          /* restore was successful */
3531          if(status == (int) True)
3532            rc[i] = 0;
3533          else if(status == (int) False)
3534            rc[i] = -1;
3535          else
3536            rc[i] = SKIP_FILE;
3537       }
3538       else
3539          /* restore failed */
3540          rc[i] = -1;
3541    }
3542
3543    /* send return codes back trough the pipe */
3544    pipe_msg = PIPEMSG_DONE;
3545    DPRINTF(("RestoreProcess: sending DONE\n"));
3546    rv = write(pipe_fd, &pipe_msg, sizeof(short));
3547    rv = write(pipe_fd, rc, file_count * sizeof(int));
3548
3549    XtFree(full_dirname);
3550 }
3551
3552
3553 /*--------------------------------------------------------------------
3554  * RestorePipeCB:
3555  *   Read and process data sent through the pipe.
3556  *------------------------------------------------------------------*/
3557
3558 static void
3559 RestorePipeCB(
3560    XtPointer client_data,
3561    int *fd,
3562    XtInputId *id)
3563 {
3564    RestoreFromTrashCBData *cb_data = (RestoreFromTrashCBData *)client_data;
3565    short pipe_msg;
3566    int i, j, k, n, rc;
3567    char *title, *err_msg, *err_arg;
3568    String buf;
3569    int bufsize,index;
3570    int RestoreIndex=0;
3571    char **ToRestoreList=NULL;
3572    char **FromRestoreList=NULL;
3573    char *target_host,*target_dir;
3574    Tt_status msg;
3575
3576    /* read the next msg from the pipe */
3577    pipe_msg = -1;
3578    n = PipeRead(*fd, &pipe_msg, sizeof(short));
3579    DPRINTF(("RestorePipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
3580
3581    switch (pipe_msg)
3582    {
3583       case PIPEMSG_FILEOP_ERROR:
3584          PipeRead(*fd, &rc, sizeof(int));
3585          err_msg = PipeReadString(*fd);
3586          err_arg = PipeReadString(*fd);
3587             FileOperationError(toplevel, err_msg, err_arg);
3588          /* This call will popup an error dialog.
3589
3590             FileOperationError(toplevel, err_msg, err_arg);
3591
3592             It's not appropriate at all to popup an error dialog here.
3593             i.e. if there're 1000 files, and the file system is full,
3594                  and we're unable to move, would we want to popup 1000
3595                  error dialogs. NOT!
3596          */
3597          XtFree(err_msg);
3598          XtFree(err_arg);
3599          return;
3600       case PIPEMSG_DONE:
3601          PipeRead(*fd, cb_data->rc, cb_data->file_count * sizeof(int));
3602          break;
3603
3604       default:
3605         fprintf(stderr,
3606                 "Internal error in RestorePipeCB: bad pipe_msg %d\n",
3607                 pipe_msg);
3608    }
3609
3610    /* close the pipe and cancel the callback */
3611    DPRINTF(("RestorePipeCB: done\n"));
3612    close(*fd);
3613    XtRemoveInput(*id);
3614
3615    buf = NULL;
3616    bufsize = 0;
3617
3618    for (i = 0; i < cb_data->file_count; i++)
3619    {
3620       /* Locate file in trash list */
3621       for (j = 0; j < numTrashItems; j++)
3622       {
3623          /* file_list[i] may be a complete path or just a file name */
3624          if (strcmp(cb_data->file_list[i], trashCan[j].filename) == 0 ||
3625              strcmp(cb_data->file_list[i], trashCan[j].intNew) == 0)
3626          {
3627             break;
3628          }
3629       }
3630
3631       if (cb_data->rc[i] == SKIP_FILE)
3632       {
3633          ToRestoreList = (char **) XtRealloc((char *)ToRestoreList,sizeof(char *) *
3634                                     (++RestoreIndex));
3635          ToRestoreList[RestoreIndex-1] = XtNewString(trashCan[j].intOrig);
3636          FromRestoreList = (char **) XtRealloc((char *)FromRestoreList,sizeof(char *) *
3637                                     RestoreIndex);
3638          FromRestoreList[RestoreIndex-1] = XtNewString( trashCan[j].intNew );
3639          continue;
3640       }
3641       /* Check the return code from the restore */
3642       else if (cb_data->rc[i] == 0)
3643       {
3644          /* restore was successful: remove the file from the trash list */
3645          if (j < numTrashItems)
3646          {
3647             /* arrange for the source directory to be updated */
3648             UpdateDirectoryOf(trashCan[j].intOrig);
3649
3650             /* Remove this entry from the trash list */
3651             XtFree ((char *) trashCan[j].intNew);
3652             XtFree ((char *) trashCan[j].intOrig);
3653             XtFree ((char *) trashCan[j].external);
3654             XtFree ((char *) trashCan[j].filename);
3655             for (k = j; k < (numTrashItems - 1); k++)
3656                trashCan[k] = trashCan[k + 1];
3657
3658             numTrashItems--;
3659          }
3660       }
3661       else
3662       {
3663          char *restore_header,*tmpStr = GETMESSAGE(27,101,
3664                            "The following object(s) could not be put back:\n");
3665
3666          restore_header = XtNewString(tmpStr);
3667          if (j < numTrashItems && cb_data->target_dir == NULL)
3668             AddString(&buf, &bufsize, trashCan[j].external, restore_header);
3669          else
3670             AddString(&buf, &bufsize, cb_data->file_list[i], restore_header);
3671          XtFree(restore_header);
3672       }
3673    }
3674
3675    /* Update the trash information file */
3676    if( ! WriteTrashEntries() )
3677    {
3678      char * tmpStr, * title;
3679
3680      title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3681      tmpStr = XtNewString(GETMESSAGE(27, 88, "Cannot write to a temporary file.\nPerhaps your file system is full.\n"));
3682      _DtMessage(toplevel, title, tmpStr, NULL, HelpRequestCB);
3683      XtFree(title);
3684      XtFree(tmpStr);
3685    }
3686
3687    /* send a reply to the message that triggered this operation, if any */
3688    if (cb_data->msg != 0) {
3689       tt_message_reply( cb_data->msg );
3690       tttk_message_destroy( cb_data->msg );
3691       cb_data->msg = 0;
3692    }
3693
3694    /* Report any errors */
3695    if (buf)
3696    {
3697       title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3698       _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3699       XtFree(title);
3700       XtFree ((char *) buf);
3701    }
3702
3703    UpdateDirectory(NULL, trashFileMgrData->host,
3704                    trashFileMgrData->current_directory);
3705    if (cb_data->target_dir)
3706       UpdateDirectory(NULL, cb_data->target_host, cb_data->target_dir);
3707
3708    target_host = XtNewString(cb_data->target_host);
3709    target_dir  = XtNewString(cb_data->target_dir);
3710
3711    /* free the callback data */
3712    for (i = 0; i < cb_data->file_count; i++)
3713       XtFree(cb_data->file_list[i]);
3714    XtFree((char *)cb_data->file_list);
3715    XtFree((char *)cb_data->target_host);
3716    XtFree((char *)cb_data->target_dir);
3717    XtFree((char *)cb_data->rc);
3718    XtFree((char *)cb_data);
3719    if(ToRestoreList)
3720    {
3721      for(i=0;i<RestoreIndex;i++)
3722      {
3723        CreateRestoreDialog(FromRestoreList[i],ToRestoreList[i]);
3724        XtFree(FromRestoreList[i]);
3725        XtFree(ToRestoreList[i]);
3726      }
3727      XtFree((char *) FromRestoreList);
3728      XtFree((char *) ToRestoreList);
3729    }
3730    XtFree(target_host);
3731    XtFree(target_dir);
3732 }
3733
3734
3735 /*--------------------------------------------------------------------
3736  * RestoreFromTrash:
3737  *    Start the background process and set up callback for the pipe.
3738  *------------------------------------------------------------------*/
3739
3740 static void
3741 RestoreFromTrash(
3742         char **file_list,
3743         int file_count,
3744         char *target_host,
3745         char *target_dir,
3746         Tt_message msg,
3747         Boolean CheckedAlready)
3748 {
3749    static char *pname = "RestoreFromTrash";
3750    RestoreFromTrashCBData *cb_data;
3751    int pipe_fd[2];
3752    int pid;
3753
3754    if( !TrashInitialized )
3755    {
3756      if (msg != 0) {
3757        tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
3758      }
3759      return;
3760    }
3761
3762    /* set up callback data */
3763    cb_data = XtNew(RestoreFromTrashCBData);
3764    cb_data->file_list = file_list;
3765    cb_data->file_count = file_count;
3766    cb_data->target_host = target_host;
3767    cb_data->target_dir = target_dir;
3768    cb_data->rc = (int *)XtCalloc(file_count, sizeof(int));
3769    cb_data->msg = msg;
3770
3771    /* create a pipe */
3772    if(-1 == pipe(pipe_fd)) {
3773        fprintf(stderr,
3774                 "%s: pipe failed, error %d=%s\n",
3775                 pname, errno, strerror(errno));
3776        return;
3777    }
3778
3779    /* fork the process that does the actual work */
3780    pid = fork();
3781    if (pid == -1)
3782    {
3783        fprintf(stderr,
3784                 "%s:  fork failed, ppid %d, pid %d: error %d=%s\n",
3785                 pname, getppid(), getpid(), errno, strerror(errno));
3786        return;
3787    }
3788
3789    if (pid == 0)
3790    {
3791       /* child process */
3792       DBGFORK(("%s:  child forked, pipe %d\n", pname, pipe_fd[1]));
3793
3794       close(pipe_fd[0]);  /* child won't read from the pipe */
3795
3796       RestoreProcess(pipe_fd[1], file_list, file_count,
3797                      target_host, target_dir, cb_data->rc,CheckedAlready);
3798       close(pipe_fd[1]);
3799
3800       DBGFORK(("%s:  child exiting\n", pname));
3801
3802       exit(0);
3803    }
3804
3805    DBGFORK(("%s:  forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
3806
3807    /* parent: set up callback to get the pipe data */
3808    close(pipe_fd[1]);  /* parent won't write the pipe */
3809
3810    cb_data->child = pid;
3811
3812    XtAppAddInput(XtWidgetToApplicationContext(toplevel),
3813                  pipe_fd[0], (XtPointer)XtInputReadMask,
3814                  RestorePipeCB, (XtPointer)cb_data);
3815 }
3816
3817
3818
3819 /*====================================================================
3820  *
3821  * EmptyTrash
3822  *   Run a background process to remove files from the trash can.
3823  *
3824  *==================================================================*/
3825
3826 /*--------------------------------------------------------------------
3827  * EmptyTrashProcess
3828  *   Main routine of background process for EmptyTrash
3829  *------------------------------------------------------------------*/
3830
3831 static void
3832 EmptyTrashProcess(
3833         int pipe_fd,
3834         DeleteList * del_list,
3835         int del_count,
3836         int *rc)
3837 {
3838    int i, rv;
3839    short pipe_msg;
3840
3841    /*
3842     * Delete all files or directories in the list,
3843     * as well as any associated annotations.
3844     */
3845    for (i = 0; i < del_count; i++)
3846    {
3847       /* delete the file */
3848       rc[i] = EraseObject(del_list[i].trash);
3849    }
3850
3851    /* send return codes back trough the pipe */
3852    pipe_msg = PIPEMSG_DONE;
3853    DPRINTF(("EmptyTrashProcess: sending DONE\n"));
3854    rv = write(pipe_fd, &pipe_msg, sizeof(short));
3855    rv = write(pipe_fd, rc, del_count * sizeof(int));
3856 }
3857
3858
3859 /*--------------------------------------------------------------------
3860  * EmptyTrashPipeCB:
3861  *   Read and process data sent through the pipe.
3862  *------------------------------------------------------------------*/
3863
3864 static void
3865 EmptyTrashPipeCB(
3866    XtPointer client_data,
3867    int *fd,
3868    XtInputId *id)
3869 {
3870    EmptyTrashCBData *cb_data = (EmptyTrashCBData *)client_data;
3871    short pipe_msg;
3872    int i, j, k, n, rc;
3873    char *title, *err_msg, *err_arg;
3874    int problemCount, itemCount;
3875    String buf;
3876    int bufsize;
3877    char *file_name;
3878
3879    /* read the next msg from the pipe */
3880    pipe_msg = -1;
3881    n = PipeRead(*fd, &pipe_msg, sizeof(short));
3882    DPRINTF(("EmptyTrashPipeCB: n %d, pipe_msg %d\n", n, pipe_msg));
3883
3884    switch (pipe_msg)
3885    {
3886       case PIPEMSG_FILEOP_ERROR:
3887          PipeRead(*fd, &rc, sizeof(int));
3888          err_msg = PipeReadString(*fd);
3889          err_arg = PipeReadString(*fd);
3890          /* This call will popup an error dialog.
3891
3892             FileOperationError(toplevel, err_msg, err_arg);
3893
3894             It's not appropriate at all to popup an error dialog here.
3895             i.e. if there're 1000 files, and the file system is full,
3896                  and we're unable to move, would we want to popup 1000
3897                  error dialogs. NOT!
3898          */
3899          XtFree(err_msg);
3900          XtFree(err_arg);
3901          return;
3902
3903       case PIPEMSG_DONE:
3904          PipeRead(*fd, cb_data->rc, cb_data->del_count * sizeof(int));
3905          break;
3906
3907       default:
3908         fprintf(stderr,
3909                 "Internal error in EmptyTrashPipeCB: bad pipe_msg %d\n",
3910                 pipe_msg);
3911    }
3912
3913    /* close the pipe and cancel the callback */
3914    DPRINTF(("EmptyTrashPipeCB: done\n"));
3915    close(*fd);
3916    XtRemoveInput(*id);
3917
3918    buf = NULL;
3919    bufsize = 0;
3920    problemCount = 0;
3921    itemCount = 0;
3922
3923    if (cb_data->removeType == TRASH_FILE)
3924    {
3925       for (i = 0; i < cb_data->del_count; i++)
3926       {
3927          /* Locate file in trash list */
3928          for (j = 0; j < numTrashItems; j++)
3929             if (strcmp(cb_data->del_list[i].trash, trashCan[j].intNew) == 0)
3930                break;
3931
3932          /* Check the return code from the erase */
3933          if (cb_data->rc[i] == 0)
3934          {
3935             /* erase was successful: remove the file from the trash list */
3936             if (j < numTrashItems)
3937             {
3938                /* Remove this entry from the trash list */
3939                XtFree ((char *) trashCan[j].intNew);
3940                XtFree ((char *) trashCan[j].intOrig);
3941                XtFree ((char *) trashCan[j].external);
3942                XtFree ((char *) trashCan[j].filename);
3943                for (k = j; k < (numTrashItems - 1); k++)
3944                   trashCan[k] = trashCan[k + 1];
3945
3946                numTrashItems--;
3947             }
3948          }
3949          else
3950          {
3951             /* erase failed */
3952             problemCount++;
3953             if (itemCount < 8)
3954             {
3955                char * msg = XtNewString(GETMESSAGE(27,96, "The following objects could not be removed from the file system:   \n"));
3956
3957                if (j < numTrashItems)
3958                   file_name = trashCan[j].external;
3959                else
3960                   file_name = cb_data->del_list[i].trash;
3961                AddString(&buf, &bufsize, file_name, msg);
3962                XtFree(msg);
3963                itemCount++;
3964             }
3965          }
3966       }
3967
3968       /* Update the trash information file */
3969       if( ! WriteTrashEntries() )
3970       {
3971         char * tmpStr, * title;
3972
3973         title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3974         tmpStr = XtNewString(GETMESSAGE(27, 88, "Cannot write to a temporary file.\nPerhaps your file system is full.\n"));
3975         _DtMessage(toplevel, title, tmpStr, NULL, HelpRequestCB);
3976         XtFree(title);
3977         XtFree(tmpStr);
3978       }
3979
3980       /* Report any errors */
3981       if (problemCount)
3982       {
3983          /* If more items than can be displayed, let user know */
3984          if (itemCount < problemCount)
3985          {
3986             char extraFiles[256];
3987             sprintf(extraFiles, AdditionalHeader, problemCount - itemCount);
3988             AddString(&buf,
3989                       &bufsize,
3990                       extraFiles,
3991                       GETMESSAGE(27,97, "The following objects could not be placed in the trash can:     \n") );
3992          }
3993
3994          title = XtNewString(GetSharedMessage(TRASH_ERROR_TITLE));
3995          _DtMessage(toplevel, title, buf, NULL, HelpRequestCB);
3996          XtFree(title);
3997          XtFree ((char *) buf);
3998       }
3999
4000       if (trashFileMgrData->selected_file_count > 0)
4001       {
4002         XtSetSensitive(*removeBtn, True);
4003         XtSetSensitive(*restoreBtn, True);
4004       }
4005
4006       XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_REMOVE], True);
4007       XtSetSensitive(fileMgrPopup.trash_objPopup[BTN_RESTORE], True);
4008
4009       UpdateDirectory(NULL, trashFileMgrData->host,
4010                       trashFileMgrData->current_directory);
4011    }
4012    else
4013    {
4014       if (cb_data->rc[0] == 0)
4015       {
4016          /* arrange for the directory containing the file to be updated */
4017          UpdateDirectoryOf(cb_data->del_list[0].trash);
4018       }
4019       else
4020       {
4021          /*  put up an error message saying the file couldn't be removed */
4022          char *buffer;
4023          char *tmpStr;
4024
4025          tmpStr = GETMESSAGE(27, 81, "Shred File/Folder Error");
4026          title = XtNewString(tmpStr);
4027          tmpStr = GETMESSAGE(27, 82, "   The following file could not be removed from the file system:  \n");
4028          file_name = cb_data->del_list[0].trash;
4029          buffer = XtMalloc(strlen(tmpStr) + strlen(file_name) + 1);
4030          sprintf(buffer, "%s%s\n", tmpStr, file_name);
4031          _DtMessage(toplevel, title, buffer, NULL, HelpRequestCB);
4032          XtFree(title);
4033          XtFree(buffer);
4034       }
4035    }
4036
4037
4038    /* reset removingTrash flag */
4039    removingTrash = False;
4040
4041    /* send a reply to the message that triggered this operation, if any */
4042    if (cb_data->msg != 0) {
4043       tt_message_reply( cb_data->msg );
4044       tttk_message_destroy( cb_data->msg );
4045       cb_data->msg = 0;
4046    }
4047
4048    /* free the callback data */
4049    for (i = 0; i < cb_data->del_count; i++)
4050    {
4051       XtFree(cb_data->del_list[i].trash);
4052       XtFree(cb_data->del_list[i].orig);
4053    }
4054    XtFree((char *)cb_data->del_list);
4055    XtFree((char *)cb_data->rc);
4056    XtFree((char *)cb_data);
4057 }
4058
4059
4060 /*--------------------------------------------------------------------
4061  * EmptyTrash:
4062  *    Start the background process and set up callback for the pipe.
4063  *------------------------------------------------------------------*/
4064
4065 static void
4066 EmptyTrash(
4067         DeleteList *del_list,
4068         int del_count,
4069         int removeType,
4070         Tt_message msg)
4071 {
4072    static char *pname = "EmptyTrash";
4073    EmptyTrashCBData *cb_data;
4074    int pipe_fd[2];
4075    int pid;
4076
4077    if( !TrashInitialized )
4078    {
4079      if (msg != 0) {
4080        tttk_message_fail( msg, TT_DESKTOP_ENOSPC, 0, 1 );
4081      }
4082      return;
4083    }
4084
4085    /* set removingTrash flag */
4086    removingTrash = True;
4087
4088    /* set up callback data */
4089    cb_data = XtNew(EmptyTrashCBData);
4090    cb_data->del_list = del_list;
4091    cb_data->del_count = del_count;
4092    cb_data->removeType = removeType;
4093    cb_data->rc = (int *)XtCalloc(del_count, sizeof(int));
4094    cb_data->msg = msg;
4095
4096    /* create a pipe */
4097    if(-1 == pipe(pipe_fd)) {
4098        fprintf(stderr,
4099                 "%s: pipe failed, error %d=%s\n",
4100                 pname, errno, strerror(errno));
4101        return;
4102    }
4103
4104    /* fork the process that does the actual work */
4105    pid = fork();
4106    if (pid == -1)
4107    {
4108        fprintf(stderr,
4109                 "%s:  fork failed, ppid %d, pid %d: error %d=%s\n",
4110                 pname, getppid(), getpid(), errno, strerror(errno));
4111        return;
4112    }
4113
4114    if (pid == 0)
4115    {
4116       /* child process */
4117       DBGFORK(("%s:  child forked, pipe %d\n", pname, pipe_fd[1]));
4118
4119       close(pipe_fd[0]);  /* child won't read from the pipe */
4120
4121       EmptyTrashProcess(pipe_fd[1], del_list, del_count, cb_data->rc);
4122       close(pipe_fd[1]);
4123
4124       DBGFORK(("%s:  child exiting\n", pname));
4125
4126       exit(0);
4127    }
4128
4129    DBGFORK(("%s:  forked child<%d>, pipe %d\n", pname, pid, pipe_fd[0]));
4130
4131    /* parent: set up callback to get the pipe data */
4132    close(pipe_fd[1]);  /* parent won't write the pipe */
4133
4134    cb_data->child = pid;
4135
4136    XtAppAddInput(XtWidgetToApplicationContext(toplevel),
4137                  pipe_fd[0], (XtPointer)XtInputReadMask,
4138                  EmptyTrashPipeCB, (XtPointer)cb_data);
4139 }
4140
4141 int
4142 CheckDeletePermission(
4143   char *parentdir,
4144   char *destinationPath)
4145 {
4146 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__linux__)
4147   struct statfs statbuf;
4148 #elif defined(__NetBSD__)
4149   struct statvfs statbuf;
4150 #else
4151   struct stat statbuf;
4152 #endif
4153   char fname[PATH_MAX];
4154
4155 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__linux__)
4156   if (statfs(parentdir,&statbuf) < 0)  /* does not exist */
4157 #elif defined(__NetBSD__)
4158   if (statvfs(parentdir,&statbuf) < 0)  /* does not exist */
4159 #else
4160   if (lstat(parentdir,&statbuf) < 0)  /* does not exist */
4161 #endif
4162     return -1;
4163
4164   /* check if we are root */
4165   if (getuid() == 0)
4166   {
4167     /* if NFS, need to check if server trusts root */
4168 #if defined(CSRG_BASED)
4169     if (!strcmp(statbuf.f_fstypename, "nfs"))  /* Root user and nfs */
4170 #elif defined(__linux__)
4171     if (statbuf.f_type == NFS_SUPER_MAGIC)
4172 #else
4173     /* nothing - always check if root */
4174 #endif
4175     {
4176        int fd = -1;
4177        char *tmpfile;
4178        tmpfile = tempnam(parentdir,"dtfile");
4179        if (!tmpfile)
4180            return -1;
4181        if ((fd = creat(tmpfile,O_RDONLY)) < 0)  /* Create a temporary file */
4182        {
4183            free(tmpfile);
4184            return -1;
4185        }
4186        close(fd);
4187        if (remove(tmpfile) < 0)                /* Delete the created file */
4188        {
4189            free(tmpfile);
4190            return -1;
4191        }
4192        free(tmpfile);
4193     }
4194
4195     /* root user can delete anything */
4196     return 0;
4197   }
4198
4199   /* check for write and execute permisssion on parent dir */
4200   if (CheckAccess(parentdir, W_OK | X_OK) < 0)
4201       return -1;
4202
4203   /* copy destinationPath to tmp buffer */
4204   snprintf(fname, PATH_MAX, "%s", destinationPath);
4205
4206   return CheckDeletePermissionRecur(fname);
4207 }
4208
4209
4210 static int
4211 CheckDeletePermissionRecur(
4212   char *destinationPath)
4213 {
4214   struct stat statbuf;
4215   DIR *dirp;
4216   struct dirent * dp;
4217   Boolean first_file;
4218   char *fnamep;
4219
4220   DPRINTF(("CheckDeletePermissionRecur(\"%s\")\n", destinationPath));
4221
4222   if (lstat(destinationPath, &statbuf) < 0)
4223     return -1;  /* probably does not exist */
4224
4225   if (! S_ISDIR(statbuf.st_mode))
4226     return 0;   /* no need to check anything more */
4227
4228   dirp = opendir (destinationPath);
4229   if (dirp == NULL)
4230     return -1;  /* could not read directory */
4231
4232
4233   first_file = True;
4234
4235   while (dp = readdir (dirp))
4236   {
4237     if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
4238     {
4239       if (first_file)
4240       {
4241         /* check for write permission in this directory */
4242         if (CheckAccess(destinationPath, W_OK|X_OK) < 0) {
4243           closedir(dirp);
4244           return -1;
4245         }
4246
4247         /* append a '/' to the end of directory name */
4248         fnamep = destinationPath + strlen(destinationPath);
4249         *fnamep++ = '/';
4250
4251         first_file = False;
4252       }
4253
4254       /* append file name to end of directory name */
4255       strcpy(fnamep, dp->d_name);
4256
4257       /* recursively check permission on this file */
4258       if (CheckDeletePermissionRecur(destinationPath))
4259       {
4260           closedir(dirp);
4261           return -1;
4262       }
4263
4264     }
4265   }
4266
4267   closedir(dirp);
4268
4269   return 0;
4270 }
4271
4272 static int
4273 RestoreObject(
4274         Widget w,
4275         int mode,
4276         char *source,
4277         char *target,
4278         Boolean  isContainer,
4279         void (*errorHandler)(),
4280         Boolean checkForBusyDir,
4281         int type ,
4282         Boolean CheckedAlready)
4283 {
4284   struct stat statsrc,stattar;
4285   Boolean status;
4286   char *localdir = NULL,*chrptr;
4287   int rv;
4288
4289   if(!CheckedAlready)
4290   {
4291     if(stat(source,&statsrc) < 0)
4292       if(lstat(source,&statsrc) < 0)
4293         return 0;
4294
4295     localdir =  strdup(target);
4296
4297     chrptr = strrchr(localdir,'/');
4298     if(chrptr)
4299     {
4300       if(chrptr == localdir) /* Must be root folder */
4301          chrptr = "/";
4302       else
4303          *chrptr = '\0';
4304     }
4305     if(stat(target,&stattar) >= 0)  /* Target exists  */
4306     {
4307        if(CheckDeletePermission(localdir,target)) {
4308          free(localdir);
4309          return ((int)False);
4310        } else {
4311          free(localdir);
4312          return SKIP_FILE;
4313        }
4314     }
4315   }
4316
4317   free(localdir);
4318   return ((int )FileManip((Widget)w, MOVE_FILE, source, target, TRUE,
4319                           FileOpError, False, NOT_DESKTOP));
4320 }
4321 static void
4322 CreateRestoreDialog(
4323    char  *source,
4324    char *target)
4325 {
4326     char *tmpbuf,*title;
4327     Widget dw;
4328     Arg args[4];
4329     int n=0;
4330     char **dirs = (char **) malloc(sizeof(char *) * 2);
4331     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.");
4332
4333     dirs[0] = strdup(source);
4334     dirs[1] = strdup(target);
4335     tmpbuf = XtMalloc(strlen(header) + strlen(target) + 1);
4336     sprintf(tmpbuf, header, target);
4337     title = XtNewString(GETMESSAGE(27, 109, "Put Back Warning"));
4338     dw = (Widget)_DtMessageDialog(toplevel, title,tmpbuf,NULL,True,RestoreVerifyCancel,
4339           RestoreVerifyOk, NULL, HelpRequestCB, False, WARNING_DIALOG);
4340
4341     XtSetArg(args[n], XmNuserData,dirs); n++;
4342     XtSetValues(dw,args,n);
4343     XtFree(tmpbuf);
4344     XtFree(title);
4345 }
4346
4347 static void
4348 RestoreVerifyOk(
4349         Widget w,
4350         XtPointer client_data,
4351         XtPointer call_data )
4352 {
4353     char **dirs;
4354     Arg args[4];
4355     int n=0;
4356     Widget mbox = (Widget)client_data;
4357
4358     XtSetArg(args[n],XmNuserData,&dirs); n++;
4359     XtGetValues(mbox,args,n);
4360
4361     if(DirectoryBusy(dirs[1]))
4362     {
4363        char *msgbuf;
4364        char * tmpStr,*title;
4365
4366        title = XtNewString(GETMESSAGE(27,111,"Put Back Error"));
4367        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.");
4368        msgbuf = XtMalloc(strlen(tmpStr) + strlen(dirs[1])+1);
4369        sprintf (msgbuf, tmpStr, dirs[1]);
4370        _DtMessage(toplevel,title,msgbuf,NULL,HelpRequestCB);
4371        XtFree(title);
4372        XtFree(msgbuf);
4373     }
4374     else
4375     {
4376       int status,j,k;
4377       struct stat s1;
4378       char *realTarget,*tptr;
4379       Boolean moveop;
4380       char **FileList;
4381
4382       if(lstat(dirs[1],&s1) < 0)
4383          goto goback;
4384       fsErase(dirs[1],&status,0);
4385       FileList = (char **) XtMalloc(sizeof(char *));
4386       FileList[0] = XtNewString(dirs[0]);
4387       RestoreFromTrash(FileList, (int) 1, NULL, NULL, NULL,True);
4388     }
4389 goback:
4390     free(dirs[0]);
4391     free(dirs[1]);
4392     free(dirs);
4393     XtDestroyWidget(mbox);
4394 }
4395
4396 static void
4397 RestoreVerifyCancel(
4398         Widget w,
4399         XtPointer client_data,
4400         XtPointer call_data )
4401
4402 {
4403   Arg args[4];
4404   int n=0;
4405   char **dirs;
4406   Widget mbox = (Widget)client_data;
4407
4408   XtSetArg(args[n],XmNuserData,&dirs); n++;
4409   XtGetValues(mbox,args,n);
4410   free(dirs[0]);
4411   free(dirs[1]);
4412   free(dirs);
4413   XtDestroyWidget(mbox);
4414 }
4415