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