Link with C++ linker
[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)
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,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] = NULL;
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            (((s3.st_mode & S_IFMT) == S_IFDIR)          /* if is a directory */
999            || ((s3.st_mode & S_IFMT) == S_IFSOCK)) )    /* or a net special */
1000 #else
1001 #if defined(SVR4) || defined(_AIX) || defined(sco)
1002            ((s3.st_mode & S_IFMT) == S_IFDIR) )         /* if is a directory */
1003 #else  /* (__hpux) */
1004            (((s3.st_mode & S_IFMT) == S_IFDIR)          /* if is a directory */
1005            || ((s3.st_mode & S_IFMT) == S_IFNWK)) )     /* or a net special */
1006 #endif
1007 #endif
1008       {                                            /* then get file name */
1009             (void) strcpy (filename, to);
1010
1011             DtLastChar(to, &cptr, &len);
1012             if ((len != 1) || (*cptr != '/'))
1013                 (void) strcat (filename, "/");
1014
1015             (void) strcat (filename, DName (from));
1016
1017             to = filename;
1018       }
1019
1020       if (lstat (to, &s2) >= 0)                    /* reverify <to> exists */
1021       {
1022          if ((stat (to, &s3) >= 0) &&
1023              ((s3.st_mode & S_IFMT) == S_IFDIR))   /* if is a directory */
1024          {
1025             if (errorHandler)
1026             {
1027                char * tmpStr;
1028
1029                tmpStr = GetSharedMessage(CANT_OVERWRITE_ERROR);
1030                msg = XtNewString(tmpStr);
1031                (*errorHandler) (w, msg, to);
1032                XtFree(msg);
1033             }
1034             return (False);
1035          }
1036
1037
1038          /*  <from> = <to> NOOP.  */
1039
1040          if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino)
1041             return (True);
1042
1043
1044          /*  no write permission  */
1045          if (CheckAccess(to, W_OK) == -1)
1046          {
1047             char *link_path;
1048
1049             link_path = _DtFollowLink(to);
1050             if(strcmp(to,link_path) == 0)
1051             {
1052                if (errorHandler)
1053                {
1054                   char * tmpStr;
1055
1056                   tmpStr = GetSharedMessage(CANT_OVERWRITE_ERROR);
1057                   msg = XtNewString(tmpStr);
1058                   (*errorHandler) (w, msg, to);
1059                   XtFree(msg);
1060                }
1061                return (False);
1062             }
1063          }
1064
1065
1066          /*  unlink failed  */
1067
1068          if (unlink (to) < 0)
1069          {
1070             if (errorHandler)
1071             {
1072                char * tmpStr;
1073
1074                tmpStr = GetSharedMessage(CANT_OVERWRITE_ERROR);
1075                msg = XtNewString(tmpStr);
1076                (*errorHandler) (w, msg, to);
1077                XtFree(msg);
1078             }
1079             return (False);
1080          }
1081          fileExists = True;
1082       }
1083    }
1084
1085
1086    /* Here <from> is a file and <to> doesn't exist.
1087     *
1088     * If not a copy, link the files.  If the link succeeds, unlink
1089     *   <from> and return.
1090     * Copy <from> to <to>.
1091     * If the copy cmd is not specified, unlink <from>.
1092     */
1093
1094    if (mode != COPY_FILE)
1095    {
1096       /* Try to maintain symbolic links, except when we're doing a link! */
1097       if (((s1.st_mode & S_IFMT) == S_IFLNK) && (mode != LINK_FILE))
1098       {
1099          char link_path[MAX_PATH + 1];
1100          int link_len;
1101
1102          if ((link_len = readlink(from, link_path, MAX_PATH)) > 0)
1103          {
1104             link_path[link_len] = '\0';
1105             link_result = symlink(link_path, to);
1106          }
1107          else
1108          {
1109             /* Fail-safe; do it the hard way */
1110             if (mode == MOVE_FILE)
1111                if (RunFileCommand(MOVE_CMD, from, to, NULL) == 0)
1112                   return(True);
1113             else
1114                if (RunFileCommand(LINK_CMD, "-s", from, to) == 0)
1115                   return(True);
1116
1117             link_result = (-1);
1118          }
1119       }
1120       else
1121       {
1122          if (mode == LINK_FILE)
1123             link_result = symlink(from, to);
1124          else
1125          {
1126             char *link_path;
1127
1128             link_path = _DtFollowLink(from);
1129
1130             if(strcmp(link_path, from) != 0)
1131                  link_result = symlink(link_path, to);
1132             else
1133                  link_result = link (from, to);
1134          }
1135       }
1136
1137       /* If this was a link, then time to bail out */
1138       if (mode == LINK_FILE)
1139       {
1140          if ((link_result != 0) && errorHandler)
1141          {
1142             char * tmpStr;
1143
1144             tmpStr = GetSharedMessage(CANT_CREATE_ERROR);
1145             msg = XtNewString(tmpStr);
1146             (*errorHandler) (w, msg, to);
1147             XtFree(msg);
1148          }
1149
1150          return(link_result == 0 ? True : False);
1151       }
1152
1153       /* Unlink source only if this was a move request */
1154       if ((mode == MOVE_FILE) && (link_result >= 0))
1155       {
1156          if (unlink (from) < 0)
1157          {
1158             if (errorHandler)
1159             {
1160                char * tmpStr;
1161
1162                tmpStr = GetSharedMessage(CANT_DELETE_ERROR);
1163                msg = XtNewString(tmpStr);
1164                (*errorHandler) (w, msg, from);
1165                XtFree(msg);
1166             }
1167             (void) unlink (to);
1168             return (False);
1169          }
1170
1171          return (True);
1172       }
1173    }
1174
1175    /*  unable to read <from>  */
1176
1177    if ((fold = open (from, O_RDONLY)) < 0)
1178    {
1179       if (errorHandler)
1180       {
1181          char * tmpStr;
1182
1183          tmpStr = GetSharedMessage(CANT_READ_ERROR);
1184          msg = XtNewString(tmpStr);
1185          (*errorHandler) (w, msg, from);
1186          XtFree(msg);
1187       }
1188       return (False);
1189    }
1190
1191
1192    /*  unable create <to>  */
1193
1194
1195    /* We use the stat buffer info not lstat info */
1196    (void) stat (from, &s4);
1197
1198    if ((fnew = creat (to, (int) s4.st_mode)) < 0)
1199    {
1200       if (errorHandler)
1201       {
1202          char * tmpStr;
1203
1204          tmpStr = GetSharedMessage(CANT_CREATE_ERROR);
1205          msg = XtNewString(tmpStr);
1206          (*errorHandler) (w, msg, to);
1207          XtFree(msg);
1208       }
1209       (void) close (fold);
1210       return (False);
1211    }
1212
1213    /*  do the copy  */
1214
1215
1216    while (n = read (fold, buf, BLOCK_SIZE))
1217    {
1218      int result;
1219
1220      if (n < 0)
1221      {
1222        if (errorHandler)
1223        {
1224          tmpStr = (GETMESSAGE(11,31, "Error while reading %s"));
1225          msg = XtNewString(tmpStr);
1226          (*errorHandler) (w, msg, to);
1227          XtFree(msg);
1228        }
1229        (void) close (fold);
1230        (void) close (fnew);
1231        return (False);
1232      }
1233
1234      errno = 0;
1235      result = write(fnew, buf, n);
1236      if (result != n)
1237      {
1238        (void) close (fold);
1239        (void) close (fnew);
1240        if(errno)
1241        {
1242          char * strerrormsg = NULL;
1243          char * catmsg = NULL;
1244          char * samsg = NULL;
1245          char errnoMsg[25];
1246          Boolean unknown = False;
1247          int bufLen;
1248
1249          switch (errno)
1250          {
1251 #ifdef EDQUOT
1252              case EDQUOT:
1253              {
1254                  if(mode == COPY_FILE)
1255                    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."));
1256                  else
1257                    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."));
1258                  catmsg = XtNewString(tmpStr);
1259                  break;
1260              }
1261 #endif
1262              case ENOSPC:
1263                  {
1264                  if(mode == COPY_FILE)
1265                    tmpStr = (GETMESSAGE(11,42, "No space available on the\ndevice you are copying to.\n\n"));
1266                  else
1267                    tmpStr = (GETMESSAGE(11,43, "No space available on the\ndevice you are moving to.\n"));
1268                  catmsg = XtNewString(tmpStr);
1269                  break;
1270                  }
1271              case EAGAIN:
1272                  sprintf(errnoMsg, "EAGAIN:  ");
1273                  strerrormsg = strerror(errno);
1274                  break;
1275              case EBADF:
1276                  sprintf(errnoMsg, "EBADF:  ");
1277                  strerrormsg = strerror(errno);
1278                  break;
1279              case EDEADLK:
1280                  sprintf(errnoMsg, "EDEADLK:  ");
1281                  strerrormsg = strerror(errno);
1282                  break;
1283              case EINTR:
1284                  sprintf(errnoMsg, "EINTR:  ");
1285                  strerrormsg = strerror(errno);
1286                  break;
1287              case EIO:
1288                  sprintf(errnoMsg, "EIO:   ");
1289                  strerrormsg = strerror(errno);
1290                  break;
1291              case ENOLCK:
1292                  sprintf(errnoMsg, "ENOLCK:  ");
1293                  strerrormsg = strerror(errno);
1294                  break;
1295              case EPIPE:
1296                  sprintf(errnoMsg, "EPIPE:  ");
1297                  strerrormsg = strerror(errno);
1298                  break;
1299              default:
1300                  unknown = True;
1301                  sprintf(errnoMsg, GETMESSAGE(11,56, "(Unknown):"));
1302                  strerrormsg = strerror(errno);
1303                  break;
1304          }
1305
1306          /* If catmsg is NULL then one of the miscellanous error's occured.
1307           * Set up a generic error message which will output the internal
1308           * error message.
1309           */
1310          if(catmsg == NULL)
1311          {
1312            if(mode == COPY_FILE)
1313              tmpStr = (GETMESSAGE(11,53, "The copy of the file/folder failed\ndue to some internal error. The internal\nerror given is:"));
1314            else
1315              tmpStr = (GETMESSAGE(11,54, "The move of the file/folder failed\ndue to some internal error. The internal\nerror given is:"));
1316            catmsg = XtNewString(tmpStr);
1317            tmpStr = (GETMESSAGE(11,55, "Please see your System Adminstrator"));
1318            samsg = XtNewString(tmpStr);
1319          }
1320
1321          /* Build a concatination of the possible message parts */
1322          bufLen = (strerrormsg ? strlen(strerrormsg) +
1323                    strlen(errnoMsg) + strlen(samsg) : 0) +
1324                    strlen(catmsg) + 10;
1325          msg = XtMalloc(bufLen);
1326          strcpy (msg, catmsg);
1327          if (strerrormsg)
1328          {
1329            strcat (msg, "\n\n");
1330            strcat (msg, errnoMsg);
1331            if(unknown)
1332              strcat (msg, "  ");
1333            strcat (msg, strerrormsg);
1334            strcat (msg, "\n\n");
1335            strcat (msg, samsg);
1336          }
1337
1338          (*errorHandler) (w, msg, to);
1339          XtFree(msg);
1340          XtFree(catmsg);
1341        }
1342        unlink(to);
1343        return (False);
1344      }
1345    }
1346
1347    (void) close (fold);
1348    (void) close (fnew);
1349
1350
1351    /*  unlink <from> if not copy  */
1352
1353    if (mode == MOVE_FILE)
1354    {
1355       if (unlink (from) < 0)
1356       {
1357          if (errorHandler)
1358          {
1359             char *tmpStr, *ptr;
1360
1361
1362             ptr = strrchr(from, '/') + 1;
1363             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."));
1364             msg = XtNewString(tmpStr);
1365             (*errorHandler) (w, msg, ptr);
1366             XtFree(msg);
1367             unlink(to);
1368          }
1369          return (False);
1370       }
1371    }
1372
1373
1374    /*
1375     * WARNING: this is different from how the shell behaves.  If you use
1376     *          a shell to copy over an existing file, the file keeps its
1377     *          original owner and group; for some historical reason,
1378     *          dtfile does it differently.
1379     *   UPDATE:  This is no longer the case as of 10/31/94, we change the
1380     *            file to the original owner and group now. This is to fix
1381     *            a bug.
1382     *          Also, this call originally occurred after we opened/created
1383     *          the file, but before we closed it.  This caused problems
1384     *          if the user was running as root, and was copying across
1385     *          an nfs link, since root access is not typically carried
1386     *          across an nfs mount.  The result was that we were able to
1387     *          create the file, copy to it, but when we tried to close it,
1388     *          because the file was now owned by root on the other system,
1389     *          we could not close the file; thus, the file ended up empty!
1390     */
1391
1392    if (mode == COPY_FILE && fileExists)
1393    {
1394       /*  set for user  */
1395       (void) chmod (to, s3.st_mode);
1396       (void) chown (to, s3.st_uid, s3.st_gid);
1397    }
1398    else
1399    {
1400       /*  set for user  */
1401       (void) chown (to, getuid(), getgid());
1402    }
1403
1404    return (True);
1405 }