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