bring cvs_1.12.13+real.orig.tar.gz into the game, with -I ! -I CVS -kb
[alioth/cvs.git] / vms / filesubr.c
1 /* filesubr.c --- subroutines for dealing with files
2    Gratuitously adapted toward VMS quirks.
3
4    Jim Blandy <jimb@cyclic.com>
5    Benjamin J. Lee <benjamin@cyclic.com>
6
7    This file is part of GNU CVS.
8
9    GNU CVS is free software; you can redistribute it and/or modify it
10    under the terms of the GNU General Public License as published by the
11    Free Software Foundation; either version 2, or (at your option) any
12    later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.  */
18
19 #include "cvs.h"
20 #include <assert.h>
21
22 static int deep_remove_dir( const char *path );
23
24 /*
25  * Copies "from" to "to".
26  */
27 void
28 copy_file (from_file, to_file)
29     const char *from_file;
30     const char *to_file;
31 {
32     char from[PATH_MAX], to[PATH_MAX];
33     struct stat sb;
34     struct utimbuf t;
35     int fdin, fdout;
36
37     /* Prefer local relative paths to files at expense of logical name
38        access to files. */
39
40     if (isabsolute(from_file))
41       strcpy(from, from_file);
42     else
43       sprintf(from, "./%s", from_file);
44
45     if (isabsolute(to_file))
46       strcpy(to, to_file);
47     else
48       sprintf(to, "./%s", to_file);
49
50     if (trace)
51 #ifdef SERVER_SUPPORT
52         (void) fprintf (stderr, "%c-> copy(%s,%s)\n",
53                         (server_active) ? 'S' : ' ', from, to);
54 #else
55         (void) fprintf (stderr, "-> copy(%s,%s)\n", from, to);
56 #endif
57     if (noexec)
58         return;
59
60     if ((fdin = open (from, O_RDONLY)) < 0)
61         error (1, errno, "cannot open %s for copying", from);
62     if (fstat (fdin, &sb) < 0)
63         error (1, errno, "cannot fstat %s", from);
64     if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0)
65         error (1, errno, "cannot create %s for copying", to);
66     if (sb.st_size > 0)
67     {
68         char buf[BUFSIZ];
69         int n;
70
71         for (;;) 
72         {
73             n = read (fdin, buf, sizeof(buf));
74             if (n == -1)
75             {
76 #ifdef EINTR
77                 if (errno == EINTR)
78                     continue;
79 #endif
80                 error (1, errno, "cannot read file %s for copying", from);
81             }
82             else if (n == 0) 
83                 break;
84   
85             if (write(fdout, buf, n) != n) {
86                 error (1, errno, "cannot write file %s for copying", to);
87             }
88         }
89
90 #ifdef HAVE_FSYNC
91         if (fsync (fdout)) 
92             error (1, errno, "cannot fsync file %s after copying", to);
93 #endif
94     }
95
96     if (close (fdin) < 0) 
97         error (0, errno, "cannot close %s", from);
98     if (close (fdout) < 0)
99         error (1, errno, "cannot close %s", to);
100
101     /* now, set the times for the copied file to match those of the original */
102     memset ((char *) &t, 0, sizeof (t));
103     t.actime = sb.st_atime;
104     t.modtime = sb.st_mtime;
105     (void) utime (to, &t);
106 }
107
108 /* FIXME-krp: these functions would benefit from caching the char * &
109    stat buf.  */
110
111 /*
112  * Returns non-zero if the argument file is a directory, or is a symbolic
113  * link which points to a directory.
114  */
115 int
116 isdir (file)
117     const char *file;
118 {
119     struct stat sb;
120
121     if (stat (file, &sb) < 0)
122         return (0);
123     return (S_ISDIR (sb.st_mode));
124 }
125
126 /*
127  * Returns non-zero if the argument file is a symbolic link.
128  */
129 int
130 islink (file)
131     const char *file;
132 {
133 #ifdef S_ISLNK
134     struct stat sb;
135
136     if (lstat (file, &sb) < 0)
137         return (0);
138     return (S_ISLNK (sb.st_mode));
139 #else
140     return (0);
141 #endif
142 }
143
144 /*
145  * Returns non-zero if the argument file exists.
146  */
147 int
148 isfile (file)
149     const char *file;
150 {
151     return isaccessible(file, F_OK);
152 }
153
154 /*
155  * Returns non-zero if the argument file is readable.
156  */
157 int
158 isreadable (file)
159     const char *file;
160 {
161     return isaccessible(file, R_OK);
162 }
163
164 /*
165  * Returns non-zero if the argument file is writable.
166  */
167 int
168 iswritable (file)
169     const char *file;
170 {
171     return isaccessible(file, W_OK);
172 }
173
174 /*
175  * Returns non-zero if the argument file is accessable according to
176  * mode.  If compiled with SETXID_SUPPORT also works if cvs has setxid
177  * bits set.
178  */
179 int
180 isaccessible (file, mode)
181     const char *file;
182     const int mode;
183 {
184 #ifdef SETXID_SUPPORT
185     struct stat sb;
186     int umask = 0;
187     int gmask = 0;
188     int omask = 0;
189     int uid;
190     
191     if (stat(file, &sb) == -1)
192         return 0;
193     if (mode == F_OK)
194         return 1;
195
196     uid = geteuid();
197     if (uid == 0)               /* superuser */
198     {
199         if (mode & X_OK)
200             return sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH);
201         else
202             return 1;
203     }
204         
205     if (mode & R_OK)
206     {
207         umask |= S_IRUSR;
208         gmask |= S_IRGRP;
209         omask |= S_IROTH;
210     }
211     if (mode & W_OK)
212     {
213         umask |= S_IWUSR;
214         gmask |= S_IWGRP;
215         omask |= S_IWOTH;
216     }
217     if (mode & X_OK)
218     {
219         umask |= S_IXUSR;
220         gmask |= S_IXGRP;
221         omask |= S_IXOTH;
222     }
223
224     if (sb.st_uid == uid)
225         return (sb.st_mode & umask) == umask;
226     else if (sb.st_gid == getegid())
227         return (sb.st_mode & gmask) == gmask;
228     else
229         return (sb.st_mode & omask) == omask;
230 #else
231     return access(file, mode) == 0;
232 #endif
233 }
234
235
236
237 /*
238  * Make a directory and die if it fails
239  */
240 void
241 make_directory (name)
242     const char *name;
243 {
244     struct stat sb;
245
246     if (stat (name, &sb) == 0 && (!S_ISDIR (sb.st_mode)))
247             error (0, 0, "%s already exists but is not a directory", name);
248     if (!noexec && mkdir (name, 0777) < 0)
249         error (1, errno, "cannot make directory %s", name);
250 }
251
252 /*
253  * Make a path to the argument directory, printing a message if something
254  * goes wrong.
255  */
256 void
257 make_directories (name)
258     const char *name;
259 {
260     char *cp;
261
262     if (noexec)
263         return;
264
265     if (mkdir (name, 0777) == 0 || errno == EEXIST)
266         return;
267     if (! existence_error (errno))
268     {
269         error (0, errno, "cannot make path to %s", name);
270         return;
271     }
272     if ((cp = strrchr (name, '/')) == NULL)
273         return;
274     *cp = '\0';
275     make_directories (name);
276     *cp++ = '/';
277     if (*cp == '\0')
278         return;
279     (void) mkdir (name, 0777);
280 }
281
282 /* Create directory NAME if it does not already exist; fatal error for
283    other errors.  Returns 0 if directory was created; 1 if it already
284    existed.  */
285 int
286 mkdir_if_needed (name)
287     char *name;
288 {
289     if (mkdir (name, 0777) < 0)
290     {
291         if (errno != EEXIST
292 #ifdef EACCESS
293             /* This was copied over from the OS/2 code; I would guess it
294                isn't needed here but that has not been verified.  */
295             && errno != EACCESS
296 #endif
297             )
298             error (1, errno, "cannot make directory %s", name);
299         return 1;
300     }
301     return 0;
302 }
303
304 /*
305  * Change the mode of a file, either adding write permissions, or removing
306  * all write permissions.  Either change honors the current umask setting.
307  */
308 void
309 xchmod (fname_file, writable)
310     char *fname_file;
311     int writable;
312 {
313     char fname[PATH_MAX];
314     struct stat sb;
315     mode_t mode, oumask;
316
317     /* Prefer local relative paths to files at expense of logical name
318        access to files. */
319
320     if (isabsolute(fname_file))
321       strcpy(fname, fname_file);
322     else
323       sprintf(fname, "./%s", fname_file);
324
325     if (stat (fname, &sb) < 0)
326     {
327         if (!noexec)
328             error (0, errno, "cannot stat %s", fname);
329         return;
330     }
331     oumask = umask (0);
332     (void) umask (oumask);
333     if (writable)
334     {
335         mode = sb.st_mode | (~oumask
336                              & (((sb.st_mode & S_IRUSR) ? S_IWUSR : 0)
337                                 | ((sb.st_mode & S_IRGRP) ? S_IWGRP : 0)
338                                 | ((sb.st_mode & S_IROTH) ? S_IWOTH : 0)));
339     }
340     else
341     {
342         mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH) & ~oumask;
343     }
344
345     if (trace)
346 #ifdef SERVER_SUPPORT
347         (void) fprintf (stderr, "%c-> chmod(%s,%o)\n",
348                         (server_active) ? 'S' : ' ', fname, mode);
349 #else
350         (void) fprintf (stderr, "-> chmod(%s,%o)\n", fname, mode);
351 #endif
352     if (noexec)
353         return;
354
355     if (chmod (fname, mode) < 0)
356         error (0, errno, "cannot change mode of file %s", fname);
357 }
358
359 /*
360  * Rename a file and die if it fails
361  */
362 void
363 rename_file (from_file, to_file)
364     const char *from_file;
365     const char *to_file;
366 {
367     char from[PATH_MAX], to[PATH_MAX];
368
369     /* Prefer local relative paths to files at expense of logical name
370        access to files. */
371
372     if (isabsolute(from_file))
373       strcpy(from, from_file);
374     else
375       sprintf(from, "./%s", from_file);
376
377     if (isabsolute(to_file))
378       strcpy(to, to_file);
379     else
380       sprintf(to, "./%s", to_file);
381
382     if (trace)
383 #ifdef SERVER_SUPPORT
384         (void) fprintf (stderr, "%c-> rename(%s,%s)\n",
385                         (server_active) ? 'S' : ' ', from, to);
386 #else
387         (void) fprintf (stderr, "-> rename(%s,%s)\n", from, to);
388 #endif
389     if (noexec)
390         return;
391
392     if (rename (from, to) < 0)
393         error (1, errno, "cannot rename file %s to %s", from, to);
394 }
395
396 /*
397  * unlink a file, if possible.
398  */
399 int
400 unlink_file (f_file)
401     const char *f_file;
402 {
403     char f[PATH_MAX];
404
405     /* Prefer local relative paths to files at expense of logical name
406        access to files. */
407
408     if (isabsolute(f_file))
409       strcpy(f, f_file);
410     else
411       sprintf(f, "./%s", f_file);
412
413     if (trace)
414 #ifdef SERVER_SUPPORT
415         (void) fprintf (stderr, "%c-> unlink(%s)\n",
416                         (server_active) ? 'S' : ' ', f);
417 #else
418         (void) fprintf (stderr, "-> unlink(%s)\n", f);
419 #endif
420     if (noexec)
421         return (0);
422
423     return (vms_unlink (f));
424 }
425
426 /*
427  * Unlink a file or dir, if possible.  If it is a directory do a deep
428  * removal of all of the files in the directory.  Return -1 on error
429  * (in which case errno is set).
430  */
431 int
432 unlink_file_dir (f_file)
433     const char *f_file;
434 {
435     char f[PATH_MAX];
436
437     /* Prefer local relative paths to files at expense of logical name
438        access to files. */
439
440     if (isabsolute(f_file))
441       strcpy(f, f_file);
442     else
443       sprintf(f, "./%s", f_file);
444
445     if (trace)
446 #ifdef SERVER_SUPPORT
447         (void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n",
448                         (server_active) ? 'S' : ' ', f);
449 #else
450         (void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
451 #endif
452     if (noexec)
453         return (0);
454
455     if (vms_unlink (f) != 0)
456     {
457         /* under NEXTSTEP errno is set to return EPERM if
458          * the file is a directory,or if the user is not
459          * allowed to read or write to the file.
460          * [This is probably a bug in the O/S]
461          * other systems will return EISDIR to indicate
462          * that the path is a directory.
463          */
464         if (errno == EISDIR || errno == EPERM)
465                 return deep_remove_dir (f);
466         else
467                 /* The file wasn't a directory and some other
468                  * error occured
469                  */
470                 return -1;
471     }
472     /* We were able to remove the file from the disk */
473     return 0;
474 }
475
476 /* Remove a directory and everything it contains.  Returns 0 for
477  * success, -1 for failure (in which case errno is set).
478  */
479
480 static int
481 deep_remove_dir (path)
482     const char *path;
483 {
484     DIR           *dirp;
485     struct dirent *dp;
486     char           buf[PATH_MAX];
487
488     if (rmdir (path) != 0 && (errno == ENOTEMPTY || errno == EEXIST)) 
489     {
490         if ((dirp = CVS_OPENDIR (path)) == NULL)
491             /* If unable to open the directory return
492              * an error
493              */
494             return -1;
495
496         while ((dp = CVS_READDIR (dirp)) != NULL)
497         {
498             if (strcmp (dp->d_name, ".") == 0 ||
499                         strcmp (dp->d_name, "..") == 0)
500                 continue;
501
502             sprintf (buf, "%s/%s", path, dp->d_name);
503
504             if (vms_unlink (buf) != 0 )
505             {
506                 if (errno == EISDIR || errno == EPERM)
507                 {
508                     if (deep_remove_dir (buf))
509                     {
510                         CVS_CLOSEDIR (dirp);
511                         return -1;
512                     }
513                 }
514                 else
515                 {
516                     /* buf isn't a directory, or there are
517                      * some sort of permision problems
518                      */
519                     CVS_CLOSEDIR (dirp);
520                     return -1;
521                 }
522             }
523         }
524         CVS_CLOSEDIR (dirp);
525         return rmdir (path);
526         }
527
528     /* Was able to remove the directory return 0 */
529     return 0;
530 }
531
532 /* Read NCHARS bytes from descriptor FD into BUF.
533    Return the number of characters successfully read.
534    The number returned is always NCHARS unless end-of-file or error.  */
535 static size_t
536 block_read (fd, buf, nchars)
537     int fd;
538     char *buf;
539     size_t nchars;
540 {
541     char *bp = buf;
542     size_t nread;
543
544     do 
545     {
546         nread = read (fd, bp, nchars);
547         if (nread == (size_t)-1)
548         {
549 #ifdef EINTR
550             if (errno == EINTR)
551                 continue;
552 #endif
553             return (size_t)-1;
554         }
555
556         if (nread == 0)
557             break; 
558
559         bp += nread;
560         nchars -= nread;
561     } while (nchars != 0);
562
563     return bp - buf;
564
565
566     
567 /*
568  * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
569  */
570 int
571 xcmp (file1_file, file2_file)
572     const char *file1_file;
573     const char *file2_file;
574 {
575     char file1[PATH_MAX], file2[PATH_MAX];
576     char *buf1, *buf2;
577     struct stat sb1, sb2;
578     int fd1, fd2;
579     int ret;
580
581     /* Prefer local relative paths to files at expense of logical name
582        access to files. */
583
584     if (isabsolute(file1_file))
585       strcpy(file1, file1_file);
586     else
587       sprintf(file1, "./%s", file1_file);
588
589     if (isabsolute(file2_file))
590       strcpy(file2, file2_file);
591     else
592       sprintf(file2, "./%s", file2_file);
593
594     if ((fd1 = open (file1, O_RDONLY)) < 0)
595         error (1, errno, "cannot open file %s for comparing", file1);
596     if ((fd2 = open (file2, O_RDONLY)) < 0)
597         error (1, errno, "cannot open file %s for comparing", file2);
598     if (fstat (fd1, &sb1) < 0)
599         error (1, errno, "cannot fstat %s", file1);
600     if (fstat (fd2, &sb2) < 0)
601         error (1, errno, "cannot fstat %s", file2);
602
603     /* A generic file compare routine might compare st_dev & st_ino here 
604        to see if the two files being compared are actually the same file.
605        But that won't happen in CVS, so we won't bother. */
606
607     if (sb1.st_size != sb2.st_size)
608         ret = 1;
609     else if (sb1.st_size == 0)
610         ret = 0;
611     else
612     {
613         /* FIXME: compute the optimal buffer size by computing the least
614            common multiple of the files st_blocks field */
615         size_t buf_size = 8 * 1024;
616         size_t read1;
617         size_t read2;
618
619         buf1 = xmalloc (buf_size);
620         buf2 = xmalloc (buf_size);
621
622         do 
623         {
624             read1 = block_read (fd1, buf1, buf_size);
625             if (read1 == (size_t)-1)
626                 error (1, errno, "cannot read file %s for comparing", file1);
627
628             read2 = block_read (fd2, buf2, buf_size);
629             if (read2 == (size_t)-1)
630                 error (1, errno, "cannot read file %s for comparing", file2);
631
632             assert (read1 == read2);
633
634             ret = memcmp(buf1, buf2, read1);
635         } while (ret == 0 && read1 == buf_size);
636
637         free (buf1);
638         free (buf2);
639     }
640         
641     (void) close (fd1);
642     (void) close (fd2);
643     return (ret);
644 }
645
646
647
648 /* Like strcmp, but with the appropriate tweaks for file names.
649    Under VMS, filenames are case-insensitive but case-preserving.
650    FIXME: this should compare y.tab.c equal with y_tab.c, at least
651    if fnfold is modified (see below).  */
652 int
653 fncmp (const char *n1, const char *n2)
654 {
655     while (*n1 && *n2
656            && (VMS_filename_classes[(unsigned char) *n1]
657                == VMS_filename_classes[(unsigned char) *n2]))
658         n1++, n2++;
659     return (VMS_filename_classes[(unsigned char) *n1]
660             - VMS_filename_classes[(unsigned char) *n2]);
661 }
662
663 /* Fold characters in FILENAME to their canonical forms.  FIXME: this
664    probably should be mapping y.tab.c to y_tab.c but first we have to
665    figure out whether fnfold is the right hook for that functionality
666    (probable answer: yes, but it should not fold case on OS/2, VMS, or
667    NT.  You see, fnfold isn't called anywhere, so we can define it to
668    mean whatever makes sense.  Of course to solve the VMS y.tab.c
669    problem we'd need to call it where appropriate.  It would need to
670    be redocumented as "fold to a form we can create in the filesystem"
671    rather than "canonical form").  The idea is that files we create
672    would get thusly munged, but CVS can cope with their names being
673    different the same way that the NT port copes with it if the user
674    renames a file from "foo" to "FOO".
675
676    Alternately, this kind of handling could/should go into CVS_FOPEN
677    and friends (if we want to do it like the Mac port, anyway).  */
678 void
679 fnfold (char *filename)
680 {
681     while (*filename)
682     {
683         *filename = FOLD_FN_CHAR (*filename);
684         filename++;
685     }
686 }
687 \f
688 /* Generate a unique temporary filename.  Returns a pointer to a newly
689  * malloc'd string containing the name.  Returns successfully or not at
690  * all.
691  *
692  *     THIS FUNCTION IS DEPRECATED!!!  USE cvs_temp_file INSTEAD!!!
693  *
694  * and yes, I know about the way the rcs commands use temp files.  I think
695  * they should be converted too but I don't have time to look into it right
696  * now.
697  */
698 char *
699 cvs_temp_name ()
700 {
701     char *fn;
702     FILE *fp;
703
704     fp = cvs_temp_file (&fn);
705     if (fp == NULL)
706         error (1, errno, "Failed to create temporary file");
707     if (fclose (fp) == EOF)
708         error (0, errno, "Failed to close temporary file %s", fn);
709     return fn;
710 }
711
712 /* Generate a unique temporary filename and return an open file stream
713  * to the truncated file by that name
714  *
715  *  INPUTS
716  *      filename        where to place the pointer to the newly allocated file
717  *                      name string
718  *
719  *  OUTPUTS
720  *      filename        dereferenced, will point to the newly allocated file
721  *                      name string.  This value is undefined if the function
722  *                      returns an error.
723  *
724  *  RETURNS
725  *      An open file pointer to a read/write mode empty temporary file with the
726  *      unique file name or NULL on failure.
727  *
728  *  ERRORS
729  *      on error, errno will be set to some value either by CVS_FOPEN or
730  *      whatever system function is called to generate the temporary file name
731  */
732 /* There are at least four functions for generating temporary
733  * filenames.  We use mkstemp (BSD 4.3) if possible, else tempnam (SVID 3),
734  * else mktemp (BSD 4.3), and as last resort tmpnam (POSIX).  Reason is that
735  * mkstemp, tempnam, and mktemp both allow to specify the directory in which
736  * the temporary file will be created.
737  *
738  * And the _correct_ way to use the deprecated functions probably involves
739  * opening file descriptors using O_EXCL & O_CREAT and even doing the annoying
740  * NFS locking thing, but until I hear of more problems, I'm not going to
741  * bother.
742  */
743 FILE *cvs_temp_file (filename)
744     char **filename;
745 {
746     char *fn;
747     FILE *fp;
748
749     /* FIXME - I'd like to be returning NULL here in noexec mode, but I think
750      * some of the rcs & diff functions which rely on a temp file run in
751      * noexec mode too.
752      */
753
754     assert (filename != NULL);
755
756 #ifdef HAVE_MKSTEMP
757
758     {
759     int fd;
760
761     fn = xmalloc (strlen (Tmpdir) + 11);
762     sprintf (fn, "%s/%s", Tmpdir, "cvsXXXXXX" );
763     fd = mkstemp (fn);
764
765     /* a NULL return will be interpreted by callers as an error and
766      * errno should still be set
767      */
768     if (fd == -1) fp = NULL;
769     else if ((fp = CVS_FDOPEN (fd, "w+")) == NULL)
770     {
771         /* attempt to close and unlink the file since mkstemp returned sucessfully and
772          * we believe it's been created and opened
773          */
774         int save_errno = errno;
775         if (close (fd))
776             error (0, errno, "Failed to close temporary file %s", fn);
777         if (CVS_UNLINK (fn))
778             error (0, errno, "Failed to unlink temporary file %s", fn);
779         errno = save_errno;
780     }
781
782     if (fp == NULL) free (fn);
783     /* mkstemp is defined to open mode 0600 using glibc 2.0.7+ */
784     /* FIXME - configure can probably tell us which version of glibc we are
785      * linking to and not chmod for 2.0.7+
786      */
787     else chmod (fn, 0600);
788
789     }
790
791 #elif HAVE_TEMPNAM
792
793     /* tempnam has been deprecated due to under-specification */
794
795     fn = tempnam (Tmpdir, "cvs");
796     if (fn == NULL) fp = NULL;
797     else if ((fp = CVS_FOPEN (fn, "w+")) == NULL) free (fn);
798     else chmod (fn, 0600);
799
800     /* tempnam returns a pointer to a newly malloc'd string, so there's
801      * no need for a xstrdup
802      */
803
804 #elif HAVE_MKTEMP
805
806     /* mktemp has been deprecated due to the BSD 4.3 specification specifying
807      * that XXXXXX will be replaced by a PID and a letter, creating only 26
808      * possibilities, a security risk, and a race condition.
809      */
810
811     {
812     char *ifn;
813
814     ifn = xmalloc (strlen (Tmpdir) + 11);
815     sprintf (ifn, "%s/%s", Tmpdir, "cvsXXXXXX" );
816     fn = mktemp (ifn);
817
818     if (fn == NULL) fp = NULL;
819     else fp = CVS_FOPEN (fn, "w+");
820
821     if (fp == NULL) free (ifn);
822     else chmod (fn, 0600);
823
824     }
825
826 #else   /* use tmpnam if all else fails */
827
828     /* tmpnam is deprecated */
829
830     {
831     char ifn[L_tmpnam + 1];
832
833     fn = tmpnam (ifn);
834
835     if (fn == NULL) fp = NULL;
836     else if ((fp = CVS_FOPEN (ifn, "w+")) != NULL)
837     {
838         fn = xstrdup (ifn);
839         chmod (fn, 0600);
840     }
841
842     }
843
844 #endif
845
846     *filename = fn;
847     return fp;
848 }
849
850
851
852 /* char *
853  * xresolvepath ( const char *path )
854  *
855  * Like xreadlink(), but resolve all links in a path.
856  *
857  * INPUTS
858  *  path        The original path.
859  *
860  * RETURNS
861  *  The path with any symbolic links expanded.
862  *
863  * ERRORS
864  *  This function exits with a fatal error if it fails to read the link for
865  *  any reason.
866  */
867 char *
868 xresolvepath ( path )
869     const char *path;
870 {
871     char *hardpath;
872     char *owd;
873
874     assert ( isdir ( path ) );
875
876     /* FIXME - If HAVE_READLINK is defined, we should probably walk the path
877      * bit by bit calling xreadlink().
878      */
879
880     owd = xgetwd();
881     if ( CVS_CHDIR ( path ) < 0)
882         error ( 1, errno, "cannot chdir to %s", path );
883     if ( ( hardpath = xgetwd() ) == NULL )
884         error (1, errno, "cannot readlink %s", hardpath);
885     if ( CVS_CHDIR ( owd ) < 0)
886         error ( 1, errno, "cannot chdir to %s", owd );
887     free (owd);
888     return hardpath;
889 }
890
891 /* Return a pointer into PATH's last component.  */
892 char *
893 last_component (path)
894     char *path;
895 {
896     char *last = strrchr (path, '/');
897
898     if (last && (last != path))
899         return last + 1;
900     else
901         return path;
902 }
903
904 /* Return the home directory.  Returns a pointer to storage
905    managed by this function or its callees (currently getenv).  */
906 char *
907 get_homedir ()
908 {
909     return getenv ("HOME");
910 }
911
912 /* Compose a path to a file in the home directory.  This is different than
913  * the UNIX version since, on VMS, foo:[bar]/.cvspass is not
914  * a legal filename but foo:[bar].cvspass is.
915  *
916  * A more clean solution would be something more along the lines of a
917  * "join a directory to a filename" kind of thing which was not specific to
918  * the homedir.  This should aid portability between UNIX, Mac, Windows, VMS,
919  * and possibly others.  This is already handled by Perl - it might be
920  * interesting to see how much of the code was written in C since Perl is under
921  * the GPL and the Artistic license - we might be able to use it.
922  */
923 char *
924 strcat_filename_onto_homedir (dir, file)
925     const char *dir;
926     const char *file;
927 {
928     char *path = xmalloc (strlen (dir) + strlen(file) + 1);
929     sprintf (path, "%s%s", dir, file);
930     return path;
931 }
932
933 #ifndef __VMS_VER
934 #define __VMS_VER 0
935 #endif
936 #ifndef __DECC_VER
937 #define __DECC_VER 0
938 #endif
939
940 #if __VMS_VER < 70200000 || __DECC_VER < 50700000
941 /* See cvs.h for description.  On VMS this currently does nothing, although
942    I think we should be expanding wildcards here.  */
943 void
944 expand_wild (argc, argv, pargc, pargv)
945     int argc;
946     char **argv;
947     int *pargc;
948     char ***pargv;
949 {
950     int i;
951     *pargc = argc;
952     *pargv = (char **) xmalloc (argc * sizeof (char *));
953     for (i = 0; i < argc; ++i)
954         (*pargv)[i] = xstrdup (argv[i]);
955 }
956
957 #else  /*  __VMS_VER >= 70200000 && __DECC_VER >= 50700000  */
958
959 /* These global variables are necessary to pass information from the
960  * routine that calls decc$from_vms into the callback routine.  In a
961  * multi-threaded environment, access to these variables MUST be
962  * serialized.
963  */
964 static char CurWorkingDir[PATH_MAX+1];
965 static char **ArgvList;
966 static int  CurArg;
967 static int  MaxArgs;
968
969 static int ew_no_op (char *fname) {
970     (void) fname;   /* Shut the compiler up */
971     return 1;       /* Continue */
972 }
973
974 static int ew_add_file (char *fname) {
975     char *lastslash, *firstper;
976     int i;
977
978     if (strncmp(fname,CurWorkingDir,strlen(CurWorkingDir)) == 0) {
979         fname += strlen(CurWorkingDir);
980     }
981     lastslash = strrchr(fname,'/');
982     if (!lastslash) {
983         lastslash = fname;
984     }
985     if ((firstper=strchr(lastslash,'.')) != strrchr(lastslash,'.')) {
986         /* We have two periods -- one is to separate the version off */
987         *strrchr(fname,'.') = '\0';
988     }
989     if (firstper && firstper[1]=='\0') {
990         *firstper = '\0';
991     }
992     /* The following code is to insure that no duplicates appear,
993      * because most of the time it will just be a different version
994      */
995     for (i=0;  i<CurArg && strcmp(ArgvList[i],fname)!=0;  ++i) {
996         ;
997     }
998     if (i==CurArg && CurArg<MaxArgs) {
999         ArgvList[CurArg++] = xstrdup(fname);
1000     }
1001     return ArgvList[CurArg-1] != 0; /* Stop if we couldn't dup the string */
1002 }
1003
1004 /* The following two routines are meant to allow future versions of new_arglist
1005  * routine to be multi-thread-safe.  It will be necessary in that environment
1006  * to serialize access to CurWorkingDir, ArgvList, MaxArg, and CurArg.  We
1007  * currently don't do any multi-threaded programming, so right now these
1008  * routines are no-ops.
1009  */
1010 static void wait_and_protect_globs (void) {
1011     return;
1012 }
1013
1014 static void release_globs (void) {
1015     return;
1016 }
1017
1018 /*pf---------------------------------------------------------------- expand_wild
1019  *
1020  *  New Argument List - (SDS)
1021  *
1022  *  DESCRIPTION:
1023  *      This routine takes the argc, argv passed in from main() and returns a
1024  *  new argc, argv list, which simulates (to an extent) Unix-Style filename
1025  *  globbing with VMS wildcards.  The key difference is that it will return
1026  *  Unix-style filenames, i.e., no VMS file version numbers.  The complexity
1027  *  comes from the desire to not simply allocate 10000 argv entries.
1028  *
1029  *  INPUTS:
1030  *      argc - The integer argc passed into main
1031  *      argv - The pointer to the array of char*'s passed into main
1032  *
1033  *  OUTPUTS:
1034  *      pargv - A pointer to a (char **) to hold the new argv list
1035  *      pargc - A pointer to an int to hold the new argc
1036  *
1037  *  RETURNS:
1038  *      NONE
1039  *
1040  *  SIDE EFFECTS:
1041  *      This routine will normally modify the global statics CurArg, MaxArg,
1042  *  ArgvList, and CurWorkingDir.
1043  *
1044  *  NOTES:
1045  *      It is ok for &argc == pargc and &argv == pargv.
1046  *
1047  *------------------------------------------------------------------------------
1048  */
1049 void expand_wild (int argc, char **argv, int *pargc, char ***pargv) {
1050     int totfiles, filesgotten;
1051     int i;
1052     int largc;
1053     char **largv;
1054
1055     /* This first loop is to find out AT MOST how big to make the
1056      * pargv array.
1057      */
1058     for (totfiles=0,i=0;  i<argc;  ++i) {
1059         char *arg = argv[i];
1060
1061         if (arg != 0 && (   strchr(arg,' ') != 0
1062                          || strcmp(arg,".") == 0
1063                          || strcmp(arg,"..") == 0) ) {
1064             ++totfiles;
1065         }else if (arg != 0) {
1066             int num;
1067             char *p = arg;
1068             /* Handle comma-separated filelists */
1069             while ( (p=strchr(p,',')) != 0) {
1070                 *p = '\0';
1071                 num = decc$from_vms (arg, ew_no_op, 1);
1072                 totfiles += num>0 ? num : 1;
1073                 *p++ = ',';
1074                 arg = p;
1075             }
1076             if (*arg != '\0') {
1077                 num = decc$from_vms (arg, ew_no_op, 1);
1078                 totfiles += num>0 ? num : 1;
1079             }
1080         }
1081     }
1082     largv = 0;
1083     if (totfiles) {
1084         largv = xmalloc (sizeof*largv * (totfiles + 1));
1085     }
1086     filesgotten = 0;
1087     if (largv != 0) {
1088         int len;
1089         /* All bits set to zero may not be a NULL ptr */
1090         for (i=totfiles;  --i>=0;  ) {
1091             largv[i] = 0;
1092         }
1093         largv[totfiles] = 0;
1094
1095         wait_and_protect_globs ();
1096
1097         /*--- getcwd has an OpenVMS extension that allows us to ---*/
1098         /*--- get back Unix-style path names ---*/
1099         (void) getcwd (CurWorkingDir, sizeof CurWorkingDir - 1, 0);
1100         len = strlen (CurWorkingDir);
1101         if (   len > 0 && CurWorkingDir[len-1] != '/') {
1102             (void) strcat (CurWorkingDir, "/");
1103         }
1104         CurArg = 0;
1105         ArgvList = largv;
1106         MaxArgs = totfiles + 1;
1107
1108         for (i=0;  i<argc;  ++i) {
1109             char *arg = argv[i];
1110
1111             if (arg != 0 && (   strchr(arg,' ') != 0
1112                              || strcmp(arg,".") == 0
1113                              || strcmp(arg,"..") == 0) ) {
1114                 if (CurArg < MaxArgs) {
1115                     ArgvList[CurArg++] = xstrdup(arg);
1116                 }
1117                 ++filesgotten;
1118             }else if (arg != 0) {
1119                 char *p = arg;
1120                 int num;
1121                 /* Handle comma-separated filelists */
1122                 while ( (p=strchr(p,',')) != 0) {
1123                     *p = '\0';
1124                     num = decc$from_vms (arg, ew_add_file, 1);
1125                     if (num <= 0 && CurArg < MaxArgs) {
1126                         ArgvList[CurArg++] = xstrdup(arg);
1127                     }
1128                     filesgotten += num>0 ? num : 1;
1129                     *p++ = ',';
1130                     arg = p;
1131                 }
1132                 if (*arg != '\0') {
1133                     num = decc$from_vms (arg, ew_add_file, 1);
1134                     if (num <= 0 && CurArg < MaxArgs) {
1135                         ArgvList[CurArg++] = xstrdup(arg);
1136                     }
1137                     filesgotten += num>0 ? num : 1;
1138                 }
1139             }
1140         }
1141         if (filesgotten != totfiles) {
1142             /*--- Files must have been created/deleted here ---*/;
1143         }
1144         filesgotten = CurArg;
1145
1146         release_globs();
1147     }
1148     if (!largv) {
1149         (*pargv) = xmalloc (sizeof(char *));
1150         if ((*pargv) != 0) {
1151             *(*pargv) = 0;
1152         }
1153     }else {
1154         (*pargv) = largv;
1155     }
1156     (*pargc) = largv ? filesgotten : 0;
1157
1158     return;
1159 }
1160
1161 #endif  /*  __VMS_VER >= 70200000 && __DECC_VER >= 50700000  */