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