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