1 /* filesubr.c --- subroutines for dealing with files
2 Gratuitously adapted toward VMS quirks.
4 Jim Blandy <jimb@cyclic.com>
5 Benjamin J. Lee <benjamin@cyclic.com>
7 This file is part of GNU CVS.
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
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. */
22 static int deep_remove_dir( const char *path );
25 * Copies "from" to "to".
28 copy_file (from_file, to_file)
29 const char *from_file;
32 char from[PATH_MAX], to[PATH_MAX];
37 /* Prefer local relative paths to files at expense of logical name
40 if (isabsolute(from_file))
41 strcpy(from, from_file);
43 sprintf(from, "./%s", from_file);
45 if (isabsolute(to_file))
48 sprintf(to, "./%s", to_file);
52 (void) fprintf (stderr, "%c-> copy(%s,%s)\n",
53 (server_active) ? 'S' : ' ', from, to);
55 (void) fprintf (stderr, "-> copy(%s,%s)\n", from, to);
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);
73 n = read (fdin, buf, sizeof(buf));
80 error (1, errno, "cannot read file %s for copying", from);
85 if (write(fdout, buf, n) != n) {
86 error (1, errno, "cannot write file %s for copying", to);
92 error (1, errno, "cannot fsync file %s after copying", to);
97 error (0, errno, "cannot close %s", from);
98 if (close (fdout) < 0)
99 error (1, errno, "cannot close %s", to);
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);
108 /* FIXME-krp: these functions would benefit from caching the char * &
112 * Returns non-zero if the argument file is a directory, or is a symbolic
113 * link which points to a directory.
121 if (stat (file, &sb) < 0)
123 return (S_ISDIR (sb.st_mode));
127 * Returns non-zero if the argument file is a symbolic link.
136 if (lstat (file, &sb) < 0)
138 return (S_ISLNK (sb.st_mode));
145 * Returns non-zero if the argument file exists.
151 return isaccessible(file, F_OK);
155 * Returns non-zero if the argument file is readable.
161 return isaccessible(file, R_OK);
165 * Returns non-zero if the argument file is writable.
171 return isaccessible(file, W_OK);
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
180 isaccessible (file, mode)
184 #ifdef SETXID_SUPPORT
191 if (stat(file, &sb) == -1)
197 if (uid == 0) /* superuser */
200 return sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH);
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;
229 return (sb.st_mode & omask) == omask;
231 return access(file, mode) == 0;
238 * Make a directory and die if it fails
241 make_directory (name)
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);
253 * Make a path to the argument directory, printing a message if something
257 make_directories (name)
265 if (mkdir (name, 0777) == 0 || errno == EEXIST)
267 if (! existence_error (errno))
269 error (0, errno, "cannot make path to %s", name);
272 if ((cp = strrchr (name, '/')) == NULL)
275 make_directories (name);
279 (void) mkdir (name, 0777);
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
286 mkdir_if_needed (name)
289 if (mkdir (name, 0777) < 0)
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. */
298 error (1, errno, "cannot make directory %s", name);
305 * Change the mode of a file, either adding write permissions, or removing
306 * all write permissions. Either change honors the current umask setting.
309 xchmod (fname_file, writable)
313 char fname[PATH_MAX];
317 /* Prefer local relative paths to files at expense of logical name
320 if (isabsolute(fname_file))
321 strcpy(fname, fname_file);
323 sprintf(fname, "./%s", fname_file);
325 if (stat (fname, &sb) < 0)
328 error (0, errno, "cannot stat %s", fname);
332 (void) umask (oumask);
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)));
342 mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH) & ~oumask;
346 #ifdef SERVER_SUPPORT
347 (void) fprintf (stderr, "%c-> chmod(%s,%o)\n",
348 (server_active) ? 'S' : ' ', fname, mode);
350 (void) fprintf (stderr, "-> chmod(%s,%o)\n", fname, mode);
355 if (chmod (fname, mode) < 0)
356 error (0, errno, "cannot change mode of file %s", fname);
360 * Rename a file and die if it fails
363 rename_file (from_file, to_file)
364 const char *from_file;
367 char from[PATH_MAX], to[PATH_MAX];
369 /* Prefer local relative paths to files at expense of logical name
372 if (isabsolute(from_file))
373 strcpy(from, from_file);
375 sprintf(from, "./%s", from_file);
377 if (isabsolute(to_file))
380 sprintf(to, "./%s", to_file);
383 #ifdef SERVER_SUPPORT
384 (void) fprintf (stderr, "%c-> rename(%s,%s)\n",
385 (server_active) ? 'S' : ' ', from, to);
387 (void) fprintf (stderr, "-> rename(%s,%s)\n", from, to);
392 if (rename (from, to) < 0)
393 error (1, errno, "cannot rename file %s to %s", from, to);
397 * unlink a file, if possible.
405 /* Prefer local relative paths to files at expense of logical name
408 if (isabsolute(f_file))
411 sprintf(f, "./%s", f_file);
414 #ifdef SERVER_SUPPORT
415 (void) fprintf (stderr, "%c-> unlink(%s)\n",
416 (server_active) ? 'S' : ' ', f);
418 (void) fprintf (stderr, "-> unlink(%s)\n", f);
423 return (vms_unlink (f));
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).
432 unlink_file_dir (f_file)
437 /* Prefer local relative paths to files at expense of logical name
440 if (isabsolute(f_file))
443 sprintf(f, "./%s", f_file);
446 #ifdef SERVER_SUPPORT
447 (void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n",
448 (server_active) ? 'S' : ' ', f);
450 (void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
455 if (vms_unlink (f) != 0)
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.
464 if (errno == EISDIR || errno == EPERM)
465 return deep_remove_dir (f);
467 /* The file wasn't a directory and some other
472 /* We were able to remove the file from the disk */
476 /* Remove a directory and everything it contains. Returns 0 for
477 * success, -1 for failure (in which case errno is set).
481 deep_remove_dir (path)
488 if (rmdir (path) != 0 && (errno == ENOTEMPTY || errno == EEXIST))
490 if ((dirp = CVS_OPENDIR (path)) == NULL)
491 /* If unable to open the directory return
496 while ((dp = CVS_READDIR (dirp)) != NULL)
498 if (strcmp (dp->d_name, ".") == 0 ||
499 strcmp (dp->d_name, "..") == 0)
502 sprintf (buf, "%s/%s", path, dp->d_name);
504 if (vms_unlink (buf) != 0 )
506 if (errno == EISDIR || errno == EPERM)
508 if (deep_remove_dir (buf))
516 /* buf isn't a directory, or there are
517 * some sort of permision problems
528 /* Was able to remove the directory return 0 */
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. */
536 block_read (fd, buf, nchars)
546 nread = read (fd, bp, nchars);
547 if (nread == (size_t)-1)
561 } while (nchars != 0);
568 * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
571 xcmp (file1_file, file2_file)
572 const char *file1_file;
573 const char *file2_file;
575 char file1[PATH_MAX], file2[PATH_MAX];
577 struct stat sb1, sb2;
581 /* Prefer local relative paths to files at expense of logical name
584 if (isabsolute(file1_file))
585 strcpy(file1, file1_file);
587 sprintf(file1, "./%s", file1_file);
589 if (isabsolute(file2_file))
590 strcpy(file2, file2_file);
592 sprintf(file2, "./%s", file2_file);
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);
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. */
607 if (sb1.st_size != sb2.st_size)
609 else if (sb1.st_size == 0)
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;
619 buf1 = xmalloc (buf_size);
620 buf2 = xmalloc (buf_size);
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);
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);
632 assert (read1 == read2);
634 ret = memcmp(buf1, buf2, read1);
635 } while (ret == 0 && read1 == buf_size);
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). */
653 fncmp (const char *n1, const char *n2)
656 && (VMS_filename_classes[(unsigned char) *n1]
657 == VMS_filename_classes[(unsigned char) *n2]))
659 return (VMS_filename_classes[(unsigned char) *n1]
660 - VMS_filename_classes[(unsigned char) *n2]);
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".
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). */
679 fnfold (char *filename)
683 *filename = FOLD_FN_CHAR (*filename);
688 /* Generate a unique temporary filename. Returns a pointer to a newly
689 * malloc'd string containing the name. Returns successfully or not at
692 * THIS FUNCTION IS DEPRECATED!!! USE cvs_temp_file INSTEAD!!!
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
704 fp = cvs_temp_file (&fn);
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);
712 /* Generate a unique temporary filename and return an open file stream
713 * to the truncated file by that name
716 * filename where to place the pointer to the newly allocated file
720 * filename dereferenced, will point to the newly allocated file
721 * name string. This value is undefined if the function
725 * An open file pointer to a read/write mode empty temporary file with the
726 * unique file name or NULL on failure.
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
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.
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
743 FILE *cvs_temp_file (filename)
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
754 assert (filename != NULL);
761 fn = xmalloc (strlen (Tmpdir) + 11);
762 sprintf (fn, "%s/%s", Tmpdir, "cvsXXXXXX" );
765 /* a NULL return will be interpreted by callers as an error and
766 * errno should still be set
768 if (fd == -1) fp = NULL;
769 else if ((fp = CVS_FDOPEN (fd, "w+")) == NULL)
771 /* attempt to close and unlink the file since mkstemp returned sucessfully and
772 * we believe it's been created and opened
774 int save_errno = errno;
776 error (0, errno, "Failed to close temporary file %s", fn);
778 error (0, errno, "Failed to unlink temporary file %s", fn);
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+
787 else chmod (fn, 0600);
793 /* tempnam has been deprecated due to under-specification */
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);
800 /* tempnam returns a pointer to a newly malloc'd string, so there's
801 * no need for a xstrdup
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.
814 ifn = xmalloc (strlen (Tmpdir) + 11);
815 sprintf (ifn, "%s/%s", Tmpdir, "cvsXXXXXX" );
818 if (fn == NULL) fp = NULL;
819 else fp = CVS_FOPEN (fn, "w+");
821 if (fp == NULL) free (ifn);
822 else chmod (fn, 0600);
826 #else /* use tmpnam if all else fails */
828 /* tmpnam is deprecated */
831 char ifn[L_tmpnam + 1];
835 if (fn == NULL) fp = NULL;
836 else if ((fp = CVS_FOPEN (ifn, "w+")) != NULL)
853 * xresolvepath ( const char *path )
855 * Like xreadlink(), but resolve all links in a path.
858 * path The original path.
861 * The path with any symbolic links expanded.
864 * This function exits with a fatal error if it fails to read the link for
868 xresolvepath ( path )
874 assert ( isdir ( path ) );
876 /* FIXME - If HAVE_READLINK is defined, we should probably walk the path
877 * bit by bit calling xreadlink().
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 );
891 /* Return a pointer into PATH's last component. */
893 last_component (path)
896 char *last = strrchr (path, '/');
898 if (last && (last != path))
904 /* Return the home directory. Returns a pointer to storage
905 managed by this function or its callees (currently getenv). */
909 return getenv ("HOME");
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.
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.
924 strcat_filename_onto_homedir (dir, file)
928 char *path = xmalloc (strlen (dir) + strlen(file) + 1);
929 sprintf (path, "%s%s", dir, file);
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. */
944 expand_wild (argc, argv, pargc, pargv)
952 *pargv = (char **) xmalloc (argc * sizeof (char *));
953 for (i = 0; i < argc; ++i)
954 (*pargv)[i] = xstrdup (argv[i]);
957 #else /* __VMS_VER >= 70200000 && __DECC_VER >= 50700000 */
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
964 static char CurWorkingDir[PATH_MAX+1];
965 static char **ArgvList;
969 static int ew_no_op (char *fname) {
970 (void) fname; /* Shut the compiler up */
971 return 1; /* Continue */
974 static int ew_add_file (char *fname) {
975 char *lastslash, *firstper;
978 if (strncmp(fname,CurWorkingDir,strlen(CurWorkingDir)) == 0) {
979 fname += strlen(CurWorkingDir);
981 lastslash = strrchr(fname,'/');
985 if ((firstper=strchr(lastslash,'.')) != strrchr(lastslash,'.')) {
986 /* We have two periods -- one is to separate the version off */
987 *strrchr(fname,'.') = '\0';
989 if (firstper && firstper[1]=='\0') {
992 /* The following code is to insure that no duplicates appear,
993 * because most of the time it will just be a different version
995 for (i=0; i<CurArg && strcmp(ArgvList[i],fname)!=0; ++i) {
998 if (i==CurArg && CurArg<MaxArgs) {
999 ArgvList[CurArg++] = xstrdup(fname);
1001 return ArgvList[CurArg-1] != 0; /* Stop if we couldn't dup the string */
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.
1010 static void wait_and_protect_globs (void) {
1014 static void release_globs (void) {
1018 /*pf---------------------------------------------------------------- expand_wild
1020 * New Argument List - (SDS)
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.
1030 * argc - The integer argc passed into main
1031 * argv - The pointer to the array of char*'s passed into main
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
1041 * This routine will normally modify the global statics CurArg, MaxArg,
1042 * ArgvList, and CurWorkingDir.
1045 * It is ok for &argc == pargc and &argv == pargv.
1047 *------------------------------------------------------------------------------
1049 void expand_wild (int argc, char **argv, int *pargc, char ***pargv) {
1050 int totfiles, filesgotten;
1055 /* This first loop is to find out AT MOST how big to make the
1058 for (totfiles=0,i=0; i<argc; ++i) {
1059 char *arg = argv[i];
1061 if (arg != 0 && ( strchr(arg,' ') != 0
1062 || strcmp(arg,".") == 0
1063 || strcmp(arg,"..") == 0) ) {
1065 }else if (arg != 0) {
1068 /* Handle comma-separated filelists */
1069 while ( (p=strchr(p,',')) != 0) {
1071 num = decc$from_vms (arg, ew_no_op, 1);
1072 totfiles += num>0 ? num : 1;
1077 num = decc$from_vms (arg, ew_no_op, 1);
1078 totfiles += num>0 ? num : 1;
1084 largv = xmalloc (sizeof*largv * (totfiles + 1));
1089 /* All bits set to zero may not be a NULL ptr */
1090 for (i=totfiles; --i>=0; ) {
1093 largv[totfiles] = 0;
1095 wait_and_protect_globs ();
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, "/");
1106 MaxArgs = totfiles + 1;
1108 for (i=0; i<argc; ++i) {
1109 char *arg = argv[i];
1111 if (arg != 0 && ( strchr(arg,' ') != 0
1112 || strcmp(arg,".") == 0
1113 || strcmp(arg,"..") == 0) ) {
1114 if (CurArg < MaxArgs) {
1115 ArgvList[CurArg++] = xstrdup(arg);
1118 }else if (arg != 0) {
1121 /* Handle comma-separated filelists */
1122 while ( (p=strchr(p,',')) != 0) {
1124 num = decc$from_vms (arg, ew_add_file, 1);
1125 if (num <= 0 && CurArg < MaxArgs) {
1126 ArgvList[CurArg++] = xstrdup(arg);
1128 filesgotten += num>0 ? num : 1;
1133 num = decc$from_vms (arg, ew_add_file, 1);
1134 if (num <= 0 && CurArg < MaxArgs) {
1135 ArgvList[CurArg++] = xstrdup(arg);
1137 filesgotten += num>0 ? num : 1;
1141 if (filesgotten != totfiles) {
1142 /*--- Files must have been created/deleted here ---*/;
1144 filesgotten = CurArg;
1149 (*pargv) = xmalloc (sizeof(char *));
1150 if ((*pargv) != 0) {
1156 (*pargc) = largv ? filesgotten : 0;
1161 #endif /* __VMS_VER >= 70200000 && __DECC_VER >= 50700000 */