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