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