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