Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtfile / FileManip.c
1 /* $TOG: FileManip.c /main/10 1999/12/09 13:06:10 mgreess $ */
2 /************************************<+>*************************************
3  ****************************************************************************
4  *
5  *   FILE:           FileManip.c
6  *
7  *   COMPONENT_NAME: Desktop File Manager (dtfile)
8  *
9  *   Description:    This module does the copy, move and rename commands.
10  *                   Included are functions to control command execution,
11  *                   move a directory, get the directory portion of a pathname,
12  *                   get the file name portion of a pathname, and check if a
13  *                   folder is to be moved within itself.
14  *
15  *   FUNCTIONS: Check
16  *              CheckAccess
17  *              CopyDir
18  *              DName
19  *              FileManip
20  *              FileOperationError
21  *              MoveDir
22  *              defined
23  *
24  *   (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
25  *   (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
26  *   (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
27  *   (c) Copyright 1993, 1994, 1995 Novell, Inc.
28  *
29  ****************************************************************************
30  ************************************<+>*************************************/
31
32 #if defined(SVR4) || defined(sco)
33 #  if defined(USL) || defined(sco) || defined(__uxp__)
34 #    include <sys/param.h>
35 #    include <sys/types.h>
36 #  endif
37 #  ifdef sco
38 #    include <sys/fs/s5param.h>
39 #    define ROOTINO S5ROOTINO
40 #  else
41 #    include <sys/fs/ufs_fs.h>
42 #    define ROOTINO UFSROOTINO
43 #  endif        /* sco */
44 #else
45 #  if defined(linux)
46 #    define ROOTINO 2
47 #  endif
48 #  include <sys/param.h>
49 #endif  /* SVR4 || sco */
50
51 #include <sys/types.h>
52
53 #include <sys/stat.h>
54 #include <stdio.h>
55 #include <signal.h>
56 #include <errno.h>
57 #include <fcntl.h>
58
59 /** The following ifdefs need to be straightened out
60  ** They are a mess.
61  **/
62 #ifdef sun
63 #include <unistd.h>
64 #include <limits.h>
65 #ifndef SVR4
66 #include <ufs/fs.h>
67 #endif
68 #else
69 #ifdef __hp_osf
70 #include <unistd.h>
71 #include <limits.h>
72 #include <ufs/fs.h>
73 #include <sys/access.h>
74 #else
75 #ifdef __ultrix
76 #include <unistd.h>
77 #include <limits.h>
78 #include <ufs/fs.h>
79 #else
80 #include <unistd.h>
81 #include <limits.h>
82 #ifdef __hpux
83 #include <unistd.h>
84 #endif
85 #endif /* __ultrix */
86 #endif /* __hp_osf */
87 #endif /* sun */
88
89 #ifdef __osf__
90 #include <ufs/fs.h>
91 #endif
92
93 #include <Xm/Xm.h>
94
95 #include <Xm/MwmUtil.h>
96
97 #include <Dt/DtP.h>                     /* required for DtDirPaths type */
98 #include <Dt/Connect.h>
99 #include <Dt/DtNlUtils.h>
100
101 #include "Encaps.h"
102 #include "SharedProcs.h"
103 #include "FileMgr.h"
104 #include "Desktop.h"
105 #include "Main.h"
106 #include "Help.h"
107 #include "SharedMsgs.h"
108
109 #ifndef CDE_INSTALLATION_TOP
110 #define CDE_INSTALLATION_TOP "/usr/dt"
111 #endif
112
113 /*  Local Function Definitions  */
114 static char * MOVE_CMD = "/bin/mv";
115 static char * LINK_CMD = "/bin/ln";
116 static char * REMOVE_CMD = "/bin/rm";
117 static char * DTCOPY = CDE_INSTALLATION_TOP "/bin/dtfile_copy";
118
119
120 /************************************************************************
121  *
122  *  CheckAccess
123  *
124  ************************************************************************/
125 int
126 CheckAccess(
127         char *fname,
128         int what)
129 {
130     int access_priv;
131     uid_t save_ruid;
132     gid_t save_rgid;
133
134 #if defined(__hp_osf) || defined(__ultrix)
135 /*--------------------------------------------------------------------
136  * access code for __hp_osf, __ultrix
137  *------------------------------------------------------------------*/
138
139       setreuid(geteuid(),-1);
140       return access (fname, what);
141
142 #else
143 #ifdef BLS
144 /*--------------------------------------------------------------------
145  * access code for BLS
146  *------------------------------------------------------------------*/
147
148       setresuid(geteuid(),-1,-1);
149       return access (fname, what);
150
151 #else /* the rest of the OS's */
152
153    save_ruid = getuid();
154
155 #ifdef _AIX
156    setreuid(geteuid(),-1);
157 #else
158    setuid(geteuid());
159 #endif /* _AIX */
160
161    save_rgid = getgid();
162
163 #ifdef _AIX
164    setregid(getegid(),-1);
165 #else
166    setgid(getegid());
167 #endif /* _AIX */
168
169    access_priv = access (fname, what);
170
171 #ifdef _AIX
172    setreuid(save_ruid,-1);
173    setregid(save_rgid,-1);
174 #else
175    setuid(save_ruid);
176    setgid(save_rgid);
177 #endif /* _AIX */
178
179    return access_priv;
180 #endif /* BLS */
181 #endif /* Apollo & OSF */
182 }
183
184
185 /************************************************************************
186  *
187  *  FileOperationError
188  *      Display an error message.
189  *
190  ************************************************************************/
191 void
192 FileOperationError(
193         Widget w,
194         char *message1,
195         char *message2 )
196 {
197    char *message_buf;
198    char * title;
199    char * tmpStr;
200
201   if (message2 != NULL)
202    {
203      message_buf = XtMalloc(strlen(message1) + strlen(message2) + 1);
204      (void) sprintf(message_buf,message1, message2);
205    }
206    else
207    {
208      message_buf = XtMalloc(strlen(message1) + 1);
209      (void) sprintf(message_buf,message1);
210    }
211
212    /*  Display an error dialog  */
213    tmpStr = GetSharedMessage(FILE_MANIPULATION_ERROR_TITLE);
214    title = XtNewString(tmpStr);
215    _DtMessage (w, title, message_buf, NULL, HelpRequestCB);
216    XtFree(title);
217    XtFree(message_buf);
218 }
219
220
221 /************************************************************************
222  *
223  *  DName
224  *      Returns the file name of its argument.
225  *      Keep looking thru the string until a "/" is found which still
226  *      has some characters after it.
227  *
228  ************************************************************************/
229 char *
230 DName(
231         register char *name )
232 {
233    char * p;
234    char * q;
235
236    p = q = name;
237
238    while (1)
239    {
240       q = DtStrchr(q, '/');
241       if ((q) && *(q+1))
242          p = q + 1;
243
244       if (q == NULL)
245          break;
246
247       q++;
248    }
249
250    return(p);
251 }
252
253
254 /************************************************************************
255  *
256  *  Check
257  *
258  ************************************************************************/
259 static int
260 Check(
261         Widget w,
262         register char *spth,
263         register ino_t dinode,
264         int mode,
265         void (*errorHandler)() )
266 {
267    struct stat sbuf;
268    char filename [MAX_PATH];
269    char * msg;
270    char * tmpStr;
271
272    sbuf.st_ino = 0;
273
274    (void) strcpy (filename, spth);
275
276    while (sbuf.st_ino != ROOTINO)
277    {
278       if (lstat (filename, &sbuf) < 0)
279       {
280          if (errorHandler)
281          {
282             tmpStr = (GETMESSAGE(11,33, "Cannot open %s"));
283             msg = XtNewString(tmpStr);
284             (*errorHandler) (w, msg, filename);
285             XtFree(msg);
286          }
287          return (False);
288       }
289
290       if (sbuf.st_ino == dinode)
291       {
292          if (errorHandler)
293          {
294             if (mode == COPY_FILE)
295               tmpStr = GETMESSAGE(11,35, "Cannot copy a folder into itself.");
296             else
297               tmpStr = GETMESSAGE(11,16, "A folder cannot be moved into itself.\n%s");
298             msg = XtNewString(tmpStr);
299             if (mode == COPY_FILE)
300               (*errorHandler) (w, msg, NULL);
301             else
302               (*errorHandler) (w, msg, filename);
303             XtFree(msg);
304          }
305          return(1);
306       }
307
308       (void) strcat (filename, "/..");
309    }
310
311    return(0);
312 }
313
314
315 /************************************************************************
316  *
317  *  MoveDir
318  *
319  ************************************************************************/
320
321 static Boolean
322 MoveDir(
323         Widget w,
324         register char *source,
325         register char *target,
326         struct stat *sourceStatInfo,
327         void (*errorHandler)(),
328         char ** targetRtn ,
329         int type )
330 {
331    static char *pname = "MoveDir";
332    register char *p;
333
334    char * targetDir;            /* original target dir path */
335    char *link_path;
336    int link_result;
337
338    struct stat  s1;             /* status of from file */
339    struct stat  s2;             /* status of to file   */
340    char * cptr;
341    int len, val, val1;
342
343    static char buf [BUF_SIZE];  /* generic buffer */
344    char filename [MAX_PATH];    /* buffer to hold the full file name */
345    char * msg;
346    char * tmpStr;
347    int child_pid, rc;
348
349    /* Copy target so we have it for an error dialog if we need it */
350    targetDir = XtNewString(target);
351    *targetRtn = NULL;
352
353    if ((val = stat (target, &s2)) < 0)
354       val = lstat(target, &s2);
355
356    /* Check if move to itself */
357    if( sourceStatInfo->st_dev == s2.st_dev)
358    {
359      if (Check (w, target, sourceStatInfo->st_ino, MOVE_FILE, NULL ))
360        return (False);
361    }
362
363    if (val >= 0) /* target exists */
364    {
365       if ((s2.st_mode & S_IFMT) != S_IFDIR) /* target not directory */
366       {
367          if (errorHandler)
368          {
369             char * tmpStr;
370             tmpStr = GetSharedMessage(CANT_OVERWRITE_ERROR);
371             msg = XtNewString(tmpStr);
372             (*errorHandler) (w, msg, target);
373             XtFree(msg);
374          }
375          XtFree(targetDir);
376          return (False);
377       }
378
379       (void) strcpy (buf, target);
380       target = buf;
381       *targetRtn = buf;
382
383       DtLastChar(buf, &cptr, &len);
384       if ((len != 1) || (*cptr != '/'))
385          (void) strcat (buf, "/");
386       (void) strcat (buf, DName (source));
387
388       if (lstat (target, &s2) >= 0) /* new target exists */
389       {
390          if (errorHandler)
391          {
392             char * tmpStr;
393
394             tmpStr = GetSharedMessage(CANT_OVERWRITE_ERROR);
395             msg = XtNewString(tmpStr);
396             (*errorHandler) (w, msg, target);
397             XtFree(msg);
398          }
399          XtFree(targetDir);
400          return (False);
401       }
402    }
403
404
405    p = DName (source);
406
407    /*  don't rename these  */
408    DtLastChar(p, &cptr, &len);
409
410    if (!strcmp (p, ".") || !strcmp (p, "..") ||
411        !strcmp (p, "")  || ((len == 1) && (*cptr == '/')))
412    {
413       if (errorHandler)
414       {
415          char * tmpStr;
416
417          tmpStr = (GETMESSAGE(11,32, "Cannot rename %s"));
418          msg = XtNewString(tmpStr);
419          (*errorHandler) (w, msg, p);
420          XtFree(msg);
421       }
422       XtFree(targetDir);
423       return (False);
424    }
425
426
427    /*  parent doesn't exist  */
428    if((val = stat (_DtPName (source), &s1)) < 0)
429       val = lstat (_DtPName (source), &s1);
430
431    if((val1 = stat (_DtPName (target), &s2)) < 0)
432       val1 = lstat (_DtPName (target), &s2);
433
434    if (val < 0 || val1 < 0)
435    {
436       if (errorHandler)
437       {
438          tmpStr = GETMESSAGE(11, 14, "Cannot find the folders location.");
439          msg = XtNewString(tmpStr);
440          (*errorHandler) (w, msg, NULL);
441          XtFree(msg);
442       }
443       XtFree(targetDir);
444       return (False);
445    }
446
447
448    /*  check for target parent not writeable  */
449    if (CheckAccess(_DtPName (target), W_OK) == -1)
450    {
451       if (errorHandler)
452       {
453          char * tmpStr;
454
455          tmpStr = GetSharedMessage(CANT_WRITE_ERROR);
456          msg = XtNewString(tmpStr);
457          (*errorHandler) (w, msg, targetDir);
458          XtFree(msg);
459       }
460       XtFree(targetDir);
461       return (False);
462    }
463
464
465    /* check for source parent not writeable */
466    if (CheckAccess(_DtPName (source), W_OK) == -1)
467    {
468       if (errorHandler)
469       {
470          char * tmpStr;
471
472          tmpStr = GetSharedMessage(CANT_WRITE_ERROR);
473          msg = XtNewString(tmpStr);
474          (*errorHandler) (w, msg, source);
475          XtFree(msg);
476       }
477       XtFree(targetDir);
478       return (False);
479    }
480    /*
481      if (((sourceStatInfo->st_mode & S_IFMT) == S_IFDIR) &&
482      (CheckAccess(source, W_OK) != 0))
483      {
484      if (errorHandler)
485      {
486      char * tmpStr;
487
488      tmpStr=GETMESSAGE(11, 57,"You do not have permission to move the folder\n%s\nWrite permission is required.");
489      msg = XtMalloc(strlen(tmpStr) + strlen(source) + 2);
490      sprintf(msg, tmpStr, source);
491      (*errorHandler) (w, msg, source);
492      XtFree(msg);
493      }
494      XtFree(targetDir);
495      return (False);
496      }
497      */
498    /*  if parents are not on the same device, do a copy & delete */
499    if (s1.st_dev != s2.st_dev)
500    {
501       /* Determine correct Geometry Placement fo Move Dialog */
502       /* @@@ ... to be added */
503
504       child_pid = fork();
505       if (child_pid == -1)
506       {
507          if (errorHandler)
508          {
509             tmpStr = GETMESSAGE(11, 39, "Cannot create child process.\nThe maximum number of processes for this system has been reached.\nStop some of the processes or programs that are currently\nrunning and then retry this function.");
510             msg = XtNewString(tmpStr);
511             (*errorHandler) (w, msg, NULL);
512             XtFree(msg);
513          }
514          XtFree(targetDir);
515          return False;
516       }
517
518       if (child_pid == 0)
519       {
520          DBGFORK(("%s:  child forked\n", pname));
521
522          /* pass in geometry, and other command lines params when available */
523          if(type == TRASH_DIRECTORY)
524            rc = execlp(DTCOPY, "dtfile_copy", "-move", "-confirmReplace",
525                  "-confirmErrors", "-popDown","-checkPerms", source, target, 0);
526          else
527            rc = execlp(DTCOPY, "dtfile_copy", "-move", "-confirmReplace",
528                  "-confirmErrors", "-popDown", source, target, 0);
529
530          /* call errorhandler */
531          perror ("Could not exec child process \"dtfile_copy\"");
532
533          DBGFORK(("%s:  child exiting\n", pname));
534
535          exit (1);
536       }
537
538       DBGFORK(("%s:  forked child<%d>\n", pname, child_pid));
539
540
541       XtFree(targetDir);
542       return (True);
543    }
544
545    link_path = _DtFollowLink(source);
546
547
548    if (s1.st_ino != s2.st_ino)
549    { /*  different parent inodes  */
550      (void) lstat (source, &s1); /* get source dir ino */
551
552
553      if (Check (w, _DtPName (target), s1.st_ino, MOVE_FILE, errorHandler))
554      { /* move into self */
555        XtFree(targetDir);
556        return(False);
557      }
558    }
559
560 /*   This part of code was implemented with the idea that the links
561      to be treated differently.  So, it has to be uncommented whenever
562      links are handled differently (i.e., moving a link shall move the
563      absolute object.
564
565    if(strcmp(link_path, source) != 0)
566    {
567      if (RunFileCommand (MOVE_CMD, link_path, target, NULL) == 0)
568      {
569        XtFree(targetDir);
570        return (True);
571      }
572    }
573    else
574 */
575    {
576      if (RunFileCommand (MOVE_CMD, source, target, NULL) == 0)
577      {
578        XtFree(targetDir);
579        return (True);
580      }
581    }
582
583    XtFree(targetDir);
584    return (False);
585 }
586
587
588 /************************************************************************
589  *
590  *  CopyDir
591  *
592  ************************************************************************/
593 static Boolean
594 CopyDir(
595         Widget w,
596         int mode,
597         register char *from,
598         register char *to,
599         Boolean  isContainer,
600         struct stat *s1,
601         void (*errorHandler)(),
602         Boolean checkForBusyDir,
603         int type )
604 {
605    static char *pname = "CopyDir";
606    char * cptr;
607    int len;
608    char target [MAX_PATH];      /* buffer to hold the full file name */
609    char target_dir [MAX_PATH], target_file [MAX_PATH];
610    struct stat  s2;             /* status of to file   */
611    int child_pid, rc, target_rc;
612    char *msg, *tmpStr;
613
614    /* Check if source is readable */
615    if (CheckAccess(from, R_OK) == -1)
616    {
617       if (errorHandler)
618       {
619          tmpStr = GetSharedMessage(CANT_READ_ERROR);
620          msg = XtNewString(tmpStr);
621          (*errorHandler) (w, msg, from);
622          XtFree(msg);
623       }
624       return (False);
625    }
626
627    /* generate target name */
628    /* "to" can be something to copy the source into (isContainer=TRUE)     */
629    /*    or it can be the full path of the destination (isContainer=FALSE) */
630    /* the former case is probably more common (e.g. a drag&drop copy)      */
631    /*    whereas the second case occurs when, for example, the menu is     */
632    /*    to copy directory /u/joe/a to /u/joe/b (not doable with d&d)      */
633    (void) strcpy (target, to);
634    if (isContainer)
635    {
636       DtLastChar(to, &cptr, &len);
637       if ((len != 1) || (*cptr != '/'))
638           (void) strcat (target, "/");
639       (void) strcat (target, DName (from));
640    }
641    split_path(target, target_dir, target_file);
642
643    /* Check if target directory exists */
644    if ((target_rc = stat (target, &s2)) < 0)
645       target_rc = lstat(target, &s2);
646
647    if (target_rc >= 0)
648    {
649       /* target exists:
650        * make sure it's a directory */
651
652       if ((s2.st_mode & S_IFMT) != S_IFDIR)     /* target not directory */
653       {
654          if (errorHandler)
655          {
656             tmpStr = GetSharedMessage(CANT_OVERWRITE_ERROR);
657             msg = XtNewString(tmpStr);
658             (*errorHandler) (w, msg, target);
659             XtFree(msg);
660          }
661          return (False);
662       }
663    }
664    else
665    {
666      /* target does not exist:
667       * make sure the "to" directory exists and is writable */
668      if ((rc = stat (target_dir, &s2)) < 0)
669         rc = lstat(target_dir, &s2);
670
671      if (rc < 0 || (s2.st_mode & S_IFMT) != S_IFDIR)
672      {
673         if (errorHandler)
674         {
675            tmpStr = GETMESSAGE(11, 14, "Cannot find the folders location.");
676            msg = XtNewString(tmpStr);
677            (*errorHandler) (w, msg, NULL);
678            XtFree(msg);
679         }
680         return (False);
681      }
682
683      if (CheckAccess(target_dir, W_OK) == -1)
684      {
685         if (errorHandler)
686         {
687            tmpStr = GetSharedMessage(CANT_WRITE_ERROR);
688            msg = XtNewString(tmpStr);
689            (*errorHandler) (w, msg, to);
690            XtFree(msg);
691         }
692         return (False);
693      }
694    }
695
696    /* Determine if we are attempting a copy into self */
697    if (s1->st_dev == s2.st_dev)
698    {
699      if (target_rc >= 0)
700      {
701         if (Check (w, to, s1->st_ino, COPY_FILE, errorHandler))
702            return False;
703      }
704      else  /* destination dir does not exist, look at its proposed parent */
705      {
706         if (Check (w, target_dir, s1->st_ino, COPY_FILE, errorHandler))
707            return False;
708      }
709    }
710
711    /* Determine correct Geometry Placement fo Copy Dialog */
712    /* @@@ ... to be added */
713
714    /* If all the above checks have passed, then fork off the copy dialog */
715
716    child_pid = fork();
717    if (child_pid == -1)
718    {
719       if (errorHandler)
720       {
721          tmpStr = GETMESSAGE(11, 39, "Cannot create child process.\nThe maximum number of processes for this system has been reached.\nStop some of the processes or programs that are currently\nrunning and then retry this function.");
722          msg = XtNewString(tmpStr);
723          (*errorHandler) (w, msg, NULL);
724          XtFree(msg);
725       }
726       return False;
727    }
728
729    if (child_pid == 0)
730    {
731       DBGFORK(("%s:  child forked\n", pname));
732
733       /* pass in geometry, and other command lines params when available */
734       if (mode == MERGE_DIR)
735         /* merge source & target directories */
736         rc = execlp(DTCOPY, "dtfile_copy",
737                      "-dontDelete", "-forceCopies", "-copyTop",
738                      "-confirmReplace", "-confirmErrors", "-popDown",
739                      from, target, 0);
740       else
741          /* replace target dir */
742          rc = execlp(DTCOPY, "dtfile_copy",
743                      "-forceCopies", "-copyTop",
744                      "-confirmErrors", "-popDown",
745                      from, target, 0);
746
747       /* call errorhandler */
748       perror ("Could not exec child process \"dtfile_copy\"");
749
750       DBGFORK(("%s:  child exiting\n", pname));
751
752       exit (1);
753    }
754
755    DBGFORK(("%s:  forked child<%d>\n", pname, child_pid));
756
757
758    return TRUE;
759 }
760
761
762 /************************************************************************
763  *
764  *  FileManip
765  *
766  ************************************************************************/
767 Boolean
768 FileManip(
769         Widget w,
770         int mode,
771         register char *from,
772         register char *to,
773         Boolean  isContainer,          /* described in function CopyDir */
774         void (*errorHandler)(),
775         Boolean checkForBusyDir,
776         int type )
777 {
778    register int fold;
779    register int fnew;
780    register int n;
781    Boolean copy_dir_return;
782    Boolean move_dir_return;
783    void (*oldInt)();
784    void (*oldQuit)();
785    void (*oldPipe)();
786    void (*oldTerm)();
787    char * cptr;
788    int len;
789    Boolean restricted = False;
790    Boolean fileExists = False;
791
792    struct stat s1;      /* status of from file e.g. lstat info */
793    struct stat s4;      /* status of from file e.g. stat info  */
794    struct stat s2;      /* status of to file e.g.   stat info  */
795    struct stat s3;      /* status of to file e.g.   lstat info */
796
797    char buf [BLOCK_SIZE];               /* generic buffer */
798    char filename [MAX_PATH];            /* buffer to hold the full file name */
799    char * msg;
800    int link_result;
801    char * realTarget;
802    char * tmpStr;
803
804
805    /* Check the <from> part of the command:
806     *
807     * Report error if <from> doesn't exist.
808     * Else error if <from> hasn't read access.
809     */
810
811    DPRINTF(("FileManip: mode %d type %d from \"%s\" to \"%s\"\n", mode, type, from, to));
812    if (stat (from, &s1) < 0)
813    {
814       if (lstat (from, &s1) < 0)
815       {
816          if (errorHandler)
817          {
818             tmpStr = (GETMESSAGE(11,28, "%s cannot be found."));
819             msg = XtNewString(tmpStr);
820             (*errorHandler) (w, msg, from);
821             XtFree(msg);
822          }
823          return (False);
824       }
825    }
826
827    /* We will check if we need to initiate a copy of a directory */
828    if ((s1.st_mode & S_IFMT) == S_IFDIR)        /* from is a directory */
829    {
830       if (mode == COPY_FILE  ||  mode == MERGE_DIR)
831       {
832          oldInt = signal (SIGINT, SIG_IGN);
833          oldQuit = signal (SIGQUIT, SIG_IGN);
834          oldPipe = signal (SIGPIPE, SIG_IGN);
835          oldTerm = signal (SIGTERM, SIG_IGN);
836
837          copy_dir_return = CopyDir(w, mode, from, to, isContainer, &s1,
838                                    errorHandler, checkForBusyDir, type);
839
840          (void) signal (SIGINT, oldInt);
841          (void) signal (SIGQUIT, oldQuit);
842          (void) signal (SIGPIPE, oldPipe);
843          (void) signal (SIGTERM, oldTerm);
844
845          return (copy_dir_return);
846       }
847
848
849       /*  If the directory has more than one open view or open views  */
850       /*  of sub directories.  Then do not allow the move or rename.  */
851
852       if (mode == MOVE_FILE)
853       {
854          if (checkForBusyDir && DirectoryBusy (from))
855          {
856             if (errorHandler)
857             {
858                char message_buf[512];
859                char * tmpStr;
860
861                message_buf[0] = NULL;
862                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."));
863                sprintf (message_buf, tmpStr, from);
864                (*errorHandler) (w, message_buf, NULL);
865             }
866             return (False);
867          }
868
869
870          oldInt = signal (SIGINT, SIG_IGN);
871          oldQuit = signal (SIGQUIT, SIG_IGN);
872          oldPipe = signal (SIGPIPE, SIG_IGN);
873          oldTerm = signal (SIGTERM, SIG_IGN);
874
875          move_dir_return = MoveDir (w, from, to, &s1, errorHandler, &realTarget,type);
876
877          (void) signal (SIGINT, oldInt);
878          (void) signal (SIGQUIT, oldQuit);
879          (void) signal (SIGPIPE, oldPipe);
880          (void) signal (SIGTERM, oldTerm);
881
882          return (move_dir_return);
883       }
884
885       if (mode == LINK_FILE)
886       {
887          /* Need to append the directory name on */
888          (void) strcpy(filename, to);
889          if(type == DESKTOP)
890          {
891              char *tmp, *ptr;
892
893              tmp = (char *)XtMalloc(strlen(filename) + 1);
894              strcpy(tmp, filename);
895              /* get the workspace number first */
896              ptr = strrchr(tmp, '/');
897              *ptr = '\0';
898              /* now get the Desktop */
899              ptr = strrchr(tmp, '/');
900
901              /* if we don't get "/Desktop" then there is another filename
902                 attached to the end of the to name passed in */
903              if(strcmp(ptr, "/Desktop") != 0)
904                 restricted = True;
905              XtFree(tmp);
906          }
907
908          if( (!restricted && type != TRASH_DIRECTORY) && isContainer)
909          {
910             DtLastChar(to, &cptr, &len);
911             if ((len != 1) || (*cptr != '/'))
912                (void) strcat(filename, "/");
913          }
914
915          if(strcmp(from, "/.") == 0 || strcmp(from, "/") == 0)
916          {
917             (void) strcat(filename, home_host_name);
918             (void) strcat(filename, ":");
919             (void) strcat(filename, root_title);
920          }
921          else if ( (!restricted && type != TRASH_DIRECTORY) && isContainer)
922            (void) strcat(filename, DName(from));
923
924          to = filename;
925
926          if (((link_result = symlink(from, to)) != 0) && errorHandler)
927          {
928             if(type == NOT_DESKTOP || errno != EEXIST )
929             {
930                char * tmpStr;
931
932                tmpStr = GetSharedMessage(CANT_CREATE_ERROR);
933                msg = XtNewString(tmpStr);
934                (*errorHandler) (w, msg, to);
935                XtFree(msg);
936             }
937          }
938
939          return(link_result == 0 ? True : False);
940       }
941    }
942
943    if (CheckAccess(from, R_OK) == -1)
944    {
945       /*
946        * A move operation does not require read permission, but a copy does.
947        */
948       if (mode == COPY_FILE)
949       {
950          if (errorHandler)
951          {
952             char * tmpStr;
953
954             tmpStr = GetSharedMessage(CANT_READ_ERROR);
955             msg = XtNewString(tmpStr);
956             (*errorHandler) (w, msg, from);
957             XtFree(msg);
958          }
959          return (False);
960       }
961    }
962
963
964    /* Here <from> is a file (not a directory).
965     * Check the <to> part of the command:
966     *
967     * <To> can either be an existing file,
968     *                    an existing directory,
969     *                    or a new file in an existing directory.
970     */
971
972    if (lstat (to, &s2) >= 0)                       /* <to> exists */
973    {
974       if ((stat (to, &s3) >= 0) &&
975 #if defined(__hp_osf) || (__ultrix) || defined(__osf__) || defined(linux)
976            (((s3.st_mode & S_IFMT) == S_IFDIR)          /* if is a directory */
977            || ((s3.st_mode & S_IFMT) == S_IFSOCK)) )    /* or a net special */
978 #else
979 #if defined(SVR4) || defined(_AIX) || defined(sco)
980            ((s3.st_mode & S_IFMT) == S_IFDIR) )         /* if is a directory */
981 #else  /* (__hpux) */
982            (((s3.st_mode & S_IFMT) == S_IFDIR)          /* if is a directory */
983            || ((s3.st_mode & S_IFMT) == S_IFNWK)) )     /* or a net special */
984 #endif
985 #endif
986       {                                            /* then get file name */
987             (void) strcpy (filename, to);
988
989             DtLastChar(to, &cptr, &len);
990             if ((len != 1) || (*cptr != '/'))
991                 (void) strcat (filename, "/");
992
993             (void) strcat (filename, DName (from));
994
995             to = filename;
996       }
997
998       if (lstat (to, &s2) >= 0)                    /* reverify <to> exists */
999       {
1000          if ((stat (to, &s3) >= 0) &&
1001              ((s3.st_mode & S_IFMT) == S_IFDIR))   /* if is a directory */
1002          {
1003             if (errorHandler)
1004             {
1005                char * tmpStr;
1006
1007                tmpStr = GetSharedMessage(CANT_OVERWRITE_ERROR);
1008                msg = XtNewString(tmpStr);
1009                (*errorHandler) (w, msg, to);
1010                XtFree(msg);
1011             }
1012             return (False);
1013          }
1014
1015
1016          /*  <from> = <to> NOOP.  */
1017
1018          if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino)
1019             return (True);
1020
1021
1022          /*  no write permission  */
1023          if (CheckAccess(to, W_OK) == -1)
1024          {
1025             char *link_path;
1026
1027             link_path = _DtFollowLink(to);
1028             if(strcmp(to,link_path) == 0)
1029             {
1030                if (errorHandler)
1031                {
1032                   char * tmpStr;
1033
1034                   tmpStr = GetSharedMessage(CANT_OVERWRITE_ERROR);
1035                   msg = XtNewString(tmpStr);
1036                   (*errorHandler) (w, msg, to);
1037                   XtFree(msg);
1038                }
1039                return (False);
1040             }
1041          }
1042
1043
1044          /*  unlink failed  */
1045
1046          if (unlink (to) < 0)
1047          {
1048             if (errorHandler)
1049             {
1050                char * tmpStr;
1051
1052                tmpStr = GetSharedMessage(CANT_OVERWRITE_ERROR);
1053                msg = XtNewString(tmpStr);
1054                (*errorHandler) (w, msg, to);
1055                XtFree(msg);
1056             }
1057             return (False);
1058          }
1059          fileExists = True;
1060       }
1061    }
1062
1063
1064    /* Here <from> is a file and <to> doesn't exist.
1065     *
1066     * If not a copy, link the files.  If the link succeeds, unlink
1067     *   <from> and return.
1068     * Copy <from> to <to>.
1069     * If the copy cmd is not specified, unlink <from>.
1070     */
1071
1072    if (mode != COPY_FILE)
1073    {
1074       /* Try to maintain symbolic links, except when we're doing a link! */
1075       if (((s1.st_mode & S_IFMT) == S_IFLNK) && (mode != LINK_FILE))
1076       {
1077          char link_path[MAX_PATH + 1];
1078          int link_len;
1079
1080          if ((link_len = readlink(from, link_path, MAX_PATH)) > 0)
1081          {
1082             link_path[link_len] = '\0';
1083             link_result = symlink(link_path, to);
1084          }
1085          else
1086          {
1087             /* Fail-safe; do it the hard way */
1088             if (mode == MOVE_FILE)
1089                if (RunFileCommand(MOVE_CMD, from, to, NULL) == 0)
1090                   return(True);
1091             else
1092                if (RunFileCommand(LINK_CMD, "-s", from, to) == 0)
1093                   return(True);
1094
1095             link_result = (-1);
1096          }
1097       }
1098       else
1099       {
1100          if (mode == LINK_FILE)
1101             link_result = symlink(from, to);
1102          else
1103          {
1104             char *link_path;
1105
1106             link_path = _DtFollowLink(from);
1107
1108             if(strcmp(link_path, from) != 0)
1109                  link_result = symlink(link_path, to);
1110             else
1111                  link_result = link (from, to);
1112          }
1113       }
1114
1115       /* If this was a link, then time to bail out */
1116       if (mode == LINK_FILE)
1117       {
1118          if ((link_result != 0) && errorHandler)
1119          {
1120             char * tmpStr;
1121
1122             tmpStr = GetSharedMessage(CANT_CREATE_ERROR);
1123             msg = XtNewString(tmpStr);
1124             (*errorHandler) (w, msg, to);
1125             XtFree(msg);
1126          }
1127
1128          return(link_result == 0 ? True : False);
1129       }
1130
1131       /* Unlink source only if this was a move request */
1132       if ((mode == MOVE_FILE) && (link_result >= 0))
1133       {
1134          if (unlink (from) < 0)
1135          {
1136             if (errorHandler)
1137             {
1138                char * tmpStr;
1139
1140                tmpStr = GetSharedMessage(CANT_DELETE_ERROR);
1141                msg = XtNewString(tmpStr);
1142                (*errorHandler) (w, msg, from);
1143                XtFree(msg);
1144             }
1145             (void) unlink (to);
1146             return (False);
1147          }
1148
1149          return (True);
1150       }
1151    }
1152
1153    /*  unable to read <from>  */
1154
1155    if ((fold = open (from, O_RDONLY)) < 0)
1156    {
1157       if (errorHandler)
1158       {
1159          char * tmpStr;
1160
1161          tmpStr = GetSharedMessage(CANT_READ_ERROR);
1162          msg = XtNewString(tmpStr);
1163          (*errorHandler) (w, msg, from);
1164          XtFree(msg);
1165       }
1166       return (False);
1167    }
1168
1169
1170    /*  unable create <to>  */
1171
1172
1173    /* We use the stat buffer info not lstat info */
1174    (void) stat (from, &s4);
1175
1176    if ((fnew = creat (to, (int) s4.st_mode)) < 0)
1177    {
1178       if (errorHandler)
1179       {
1180          char * tmpStr;
1181
1182          tmpStr = GetSharedMessage(CANT_CREATE_ERROR);
1183          msg = XtNewString(tmpStr);
1184          (*errorHandler) (w, msg, to);
1185          XtFree(msg);
1186       }
1187       (void) close (fold);
1188       return (False);
1189    }
1190
1191    /*  do the copy  */
1192
1193
1194    while (n = read (fold, buf, BLOCK_SIZE))
1195    {
1196      int result;
1197
1198      if (n < 0)
1199      {
1200        if (errorHandler)
1201        {
1202          tmpStr = (GETMESSAGE(11,31, "Error while reading %s"));
1203          msg = XtNewString(tmpStr);
1204          (*errorHandler) (w, msg, to);
1205          XtFree(msg);
1206        }
1207        (void) close (fold);
1208        (void) close (fnew);
1209        return (False);
1210      }
1211
1212      errno = 0;
1213      result = write(fnew, buf, n);
1214      if (result != n)
1215      {
1216        (void) close (fold);
1217        (void) close (fnew);
1218        if(errno)
1219        {
1220          char * strerrormsg = NULL;
1221          char * catmsg = NULL;
1222          char * samsg = NULL;
1223          char errnoMsg[25];
1224          Boolean unknown = False;
1225          int bufLen;
1226
1227          switch (errno)
1228          {
1229 #ifdef EDQUOT
1230              case EDQUOT:
1231              {
1232                  if(mode == COPY_FILE)
1233                    tmpStr = (GETMESSAGE(11,51, "Unable to copy the file/folder because\nthe disk quota will be exceeded on the disk\nyou are copying it to."));
1234                  else
1235                    tmpStr = (GETMESSAGE(11,52, "Unable to move the file/folder because\nthe disk quota will be exceeded on the disk\nyou are moving it to."));
1236                  catmsg = XtNewString(tmpStr);
1237                  break;
1238              }
1239 #endif
1240              case ENOSPC:
1241                  {
1242                  if(mode == COPY_FILE)
1243                    tmpStr = (GETMESSAGE(11,42, "No space available on the\ndevice you are copying to.\n\n"));
1244                  else
1245                    tmpStr = (GETMESSAGE(11,43, "No space available on the\ndevice you are moving to.\n"));
1246                  catmsg = XtNewString(tmpStr);
1247                  break;
1248                  }
1249              case EAGAIN:
1250                  sprintf(errnoMsg, "EAGAIN:  ");
1251                  strerrormsg = strerror(errno);
1252                  break;
1253              case EBADF:
1254                  sprintf(errnoMsg, "EBADF:  ");
1255                  strerrormsg = strerror(errno);
1256                  break;
1257              case EDEADLK:
1258                  sprintf(errnoMsg, "EDEADLK:  ");
1259                  strerrormsg = strerror(errno);
1260                  break;
1261              case EINTR:
1262                  sprintf(errnoMsg, "EINTR:  ");
1263                  strerrormsg = strerror(errno);
1264                  break;
1265              case EIO:
1266                  sprintf(errnoMsg, "EIO:   ");
1267                  strerrormsg = strerror(errno);
1268                  break;
1269              case ENOLCK:
1270                  sprintf(errnoMsg, "ENOLCK:  ");
1271                  strerrormsg = strerror(errno);
1272                  break;
1273              case EPIPE:
1274                  sprintf(errnoMsg, "EPIPE:  ");
1275                  strerrormsg = strerror(errno);
1276                  break;
1277              default:
1278                  unknown = True;
1279                  sprintf(errnoMsg, GETMESSAGE(11,56, "(Unknown):"));
1280                  strerrormsg = strerror(errno);
1281                  break;
1282          }
1283
1284          /* If catmsg is NULL then one of the miscellanous error's occured.
1285           * Set up a generic error message which will output the internal
1286           * error message.
1287           */
1288          if(catmsg == NULL)
1289          {
1290            if(mode == COPY_FILE)
1291              tmpStr = (GETMESSAGE(11,53, "The copy of the file/folder failed\ndue to some internal error. The internal\nerror given is:"));
1292            else
1293              tmpStr = (GETMESSAGE(11,54, "The move of the file/folder failed\ndue to some internal error. The internal\nerror given is:"));
1294            catmsg = XtNewString(tmpStr);
1295            tmpStr = (GETMESSAGE(11,55, "Please see your System Adminstrator"));
1296            samsg = XtNewString(tmpStr);
1297          }
1298
1299          /* Build a concatination of the possible message parts */
1300          bufLen = (strerrormsg ? strlen(strerrormsg) +
1301                    strlen(errnoMsg) + strlen(samsg) : 0) +
1302                    strlen(catmsg) + 10;
1303          msg = XtMalloc(bufLen);
1304          strcpy (msg, catmsg);
1305          if (strerrormsg)
1306          {
1307            strcat (msg, "\n\n");
1308            strcat (msg, errnoMsg);
1309            if(unknown)
1310              strcat (msg, "  ");
1311            strcat (msg, strerrormsg);
1312            strcat (msg, "\n\n");
1313            strcat (msg, samsg);
1314          }
1315
1316          (*errorHandler) (w, msg, to);
1317          XtFree(msg);
1318          XtFree(catmsg);
1319        }
1320        unlink(to);
1321        return (False);
1322      }
1323    }
1324
1325    (void) close (fold);
1326    (void) close (fnew);
1327
1328
1329    /*  unlink <from> if not copy  */
1330
1331    if (mode == MOVE_FILE)
1332    {
1333       if (unlink (from) < 0)
1334       {
1335          if (errorHandler)
1336          {
1337             char *tmpStr, *ptr;
1338
1339
1340             ptr = strrchr(from, '/') + 1;
1341             tmpStr = (GETMESSAGE(11,38, "You do not have permission to move %s\nHowever, you can copy the object.\nTo copy an object:\n  - press and hold the <Ctrl> key, and\n  - drag the object with your mouse.\nOr\n  - use 'Copy To' in the 'Selected' menu popup of the menu bar."));
1342             msg = XtNewString(tmpStr);
1343             (*errorHandler) (w, msg, ptr);
1344             XtFree(msg);
1345             unlink(to);
1346          }
1347          return (False);
1348       }
1349    }
1350
1351
1352    /*
1353     * WARNING: this is different from how the shell behaves.  If you use
1354     *          a shell to copy over an existing file, the file keeps its
1355     *          original owner and group; for some historical reason,
1356     *          dtfile does it differently.
1357     *   UPDATE:  This is no longer the case as of 10/31/94, we change the
1358     *            file to the original owner and group now. This is to fix
1359     *            a bug.
1360     *          Also, this call originally occurred after we opened/created
1361     *          the file, but before we closed it.  This caused problems
1362     *          if the user was running as root, and was copying across
1363     *          an nfs link, since root access is not typically carried
1364     *          across an nfs mount.  The result was that we were able to
1365     *          create the file, copy to it, but when we tried to close it,
1366     *          because the file was now owned by root on the other system,
1367     *          we could not close the file; thus, the file ended up empty!
1368     */
1369
1370    if (mode == COPY_FILE && fileExists)
1371    {
1372       /*  set for user  */
1373       (void) chmod (to, s3.st_mode);
1374       (void) chown (to, s3.st_uid, s3.st_gid);
1375    }
1376    else
1377    {
1378       /*  set for user  */
1379       (void) chown (to, getuid(), getgid());
1380    }
1381
1382    return (True);
1383 }