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