xref: /dragonfly/contrib/cvs-1.12/src/filesubr.c (revision d8082429)
1 /* filesubr.c --- subroutines for dealing with files
2    Jim Blandy <jimb@cyclic.com>
3 
4    This file is part of GNU CVS.
5 
6    GNU CVS is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 2, or (at your option) any
9    later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.  */
15 
16 /* These functions were moved out of subr.c because they need different
17    definitions under operating systems (like, say, Windows NT) with different
18    file system semantics.  */
19 
20 #include "cvs.h"
21 #include "lstat.h"
22 #include "save-cwd.h"
23 #include "xsize.h"
24 
25 static int deep_remove_dir (const char *path);
26 
27 /*
28  * Copies "from" to "to".
29  */
30 void
31 copy_file (const char *from, const char *to)
32 {
33     struct stat sb;
34     struct utimbuf t;
35     int fdin, fdout;
36     ssize_t rsize;
37 
38     TRACE (TRACE_FUNCTION, "copy(%s,%s)", from, to);
39 
40     if (noexec)
41 	return;
42 
43     /* If the file to be copied is a link or a device, then just create
44        the new link or device appropriately. */
45     if ((rsize = islink (from)) > 0)
46     {
47 	char *source = Xreadlink (from, rsize);
48 	symlink (source, to);
49 	free (source);
50 	return;
51     }
52 
53     if (isdevice (from))
54     {
55 #if defined(HAVE_MKNOD) && defined(HAVE_STRUCT_STAT_ST_RDEV)
56 	if (stat (from, &sb) < 0)
57 	    error (1, errno, "cannot stat %s", from);
58 	mknod (to, sb.st_mode, sb.st_rdev);
59 #else
60 	error (1, 0, "cannot copy device files on this system (%s)", from);
61 #endif
62     }
63     else
64     {
65 	/* Not a link or a device... probably a regular file. */
66 	if ((fdin = open (from, O_RDONLY)) < 0)
67 	    error (1, errno, "cannot open %s for copying", from);
68 	if (fstat (fdin, &sb) < 0)
69 	    error (1, errno, "cannot fstat %s", from);
70 	if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0)
71 	    error (1, errno, "cannot create %s for copying", to);
72 	if (sb.st_size > 0)
73 	{
74 	    char buf[BUFSIZ];
75 	    int n;
76 
77 	    for (;;)
78 	    {
79 		n = read (fdin, buf, sizeof(buf));
80 		if (n == -1)
81 		{
82 #ifdef EINTR
83 		    if (errno == EINTR)
84 			continue;
85 #endif
86 		    error (1, errno, "cannot read file %s for copying", from);
87 		}
88 		else if (n == 0)
89 		    break;
90 
91 		if (write(fdout, buf, n) != n) {
92 		    error (1, errno, "cannot write file %s for copying", to);
93 		}
94 	    }
95 	}
96 
97 	if (close (fdin) < 0)
98 	    error (0, errno, "cannot close %s", from);
99 	if (close (fdout) < 0)
100 	    error (1, errno, "cannot close %s", to);
101     }
102 
103     /* preserve last access & modification times */
104     memset ((char *) &t, 0, sizeof (t));
105     t.actime = sb.st_atime;
106     t.modtime = sb.st_mtime;
107     (void) utime (to, &t);
108 }
109 
110 
111 
112 /* FIXME-krp: these functions would benefit from caching the char * &
113    stat buf.  */
114 
115 /*
116  * Returns true if the argument file is a directory, or is a symbolic
117  * link which points to a directory.
118  */
119 bool
120 isdir (const char *file)
121 {
122     struct stat sb;
123 
124     if (stat (file, &sb) < 0)
125 	return false;
126     return S_ISDIR (sb.st_mode);
127 }
128 
129 
130 
131 /*
132  * Returns 0 if the argument file is not a symbolic link.
133  * Returns size of the link if it is a symbolic link.
134  */
135 ssize_t
136 islink (const char *file)
137 {
138     ssize_t retsize = 0;
139 #ifdef S_ISLNK
140     struct stat sb;
141 
142     if ((lstat (file, &sb) >= 0) && S_ISLNK (sb.st_mode))
143 	retsize = sb.st_size;
144 #endif
145     return retsize;
146 }
147 
148 
149 
150 /*
151  * Returns true if the argument file is a block or
152  * character special device.
153  */
154 bool
155 isdevice (const char *file)
156 {
157     struct stat sb;
158 
159     if (lstat (file, &sb) < 0)
160 	return false;
161 #ifdef S_ISBLK
162     if (S_ISBLK (sb.st_mode))
163 	return true;
164 #endif
165 #ifdef S_ISCHR
166     if (S_ISCHR (sb.st_mode))
167 	return true;
168 #endif
169     return false;
170 }
171 
172 
173 
174 /*
175  * Returns true if the argument file exists.
176  */
177 bool
178 isfile (const char *file)
179 {
180     return isaccessible (file, F_OK);
181 }
182 
183 
184 
185 /*
186  * Returns non-zero if the argument file is readable.
187  */
188 bool
189 isreadable (const char *file)
190 {
191     return isaccessible (file, R_OK);
192 }
193 
194 
195 
196 /*
197  * Returns non-zero if the argument file is writable.
198  */
199 bool
200 iswritable (const char *file)
201 {
202     return isaccessible (file, W_OK);
203 }
204 
205 
206 
207 /*
208  * Returns true if the argument file is accessable according to
209  * mode.  If compiled with SETXID_SUPPORT also works if cvs has setxid
210  * bits set.
211  */
212 bool
213 isaccessible (const char *file, const int mode)
214 {
215 #ifdef SETXID_SUPPORT
216     struct stat sb;
217     int umask = 0;
218     int gmask = 0;
219     int omask = 0;
220     int uid, mask;
221 
222     if (stat (file, &sb)== -1)
223 	return false;
224     if (mode == F_OK)
225 	return true;
226 
227     uid = geteuid();
228     if (uid == 0)		/* superuser */
229     {
230 	if (!(mode & X_OK) || (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
231 	    return true;
232 
233 	errno = EACCES;
234 	return false;
235     }
236 
237     if (mode & R_OK)
238     {
239 	umask |= S_IRUSR;
240 	gmask |= S_IRGRP;
241 	omask |= S_IROTH;
242     }
243     if (mode & W_OK)
244     {
245 	umask |= S_IWUSR;
246 	gmask |= S_IWGRP;
247 	omask |= S_IWOTH;
248     }
249     if (mode & X_OK)
250     {
251 	umask |= S_IXUSR;
252 	gmask |= S_IXGRP;
253 	omask |= S_IXOTH;
254     }
255 
256     mask = sb.st_uid == uid ? umask : sb.st_gid == getegid() ? gmask : omask;
257     if ((sb.st_mode & mask) == mask)
258 	return true;
259     errno = EACCES;
260     return false;
261 #else /* !SETXID_SUPPORT */
262     return access (file, mode) == 0;
263 #endif /* SETXID_SUPPORT */
264 }
265 
266 
267 
268 /*
269  * Make a directory and die if it fails
270  */
271 void
272 make_directory (const char *name)
273 {
274     struct stat sb;
275 
276     if (stat (name, &sb) == 0 && (!S_ISDIR (sb.st_mode)))
277 	    error (0, 0, "%s already exists but is not a directory", name);
278     if (!noexec && mkdir (name, 0777) < 0)
279 	error (1, errno, "cannot make directory %s", name);
280 }
281 
282 /*
283  * Make a path to the argument directory, printing a message if something
284  * goes wrong.
285  */
286 void
287 make_directories (const char *name)
288 {
289     char *cp;
290 
291     if (noexec)
292 	return;
293 
294     if (mkdir (name, 0777) == 0 || errno == EEXIST)
295 	return;
296     if (! existence_error (errno))
297     {
298 	error (0, errno, "cannot make path to %s", name);
299 	return;
300     }
301     if ((cp = strrchr (name, '/')) == NULL)
302 	return;
303     *cp = '\0';
304     make_directories (name);
305     *cp++ = '/';
306     if (*cp == '\0')
307 	return;
308     (void) mkdir (name, 0777);
309 }
310 
311 /* Create directory NAME if it does not already exist; fatal error for
312    other errors.  Returns 0 if directory was created; 1 if it already
313    existed.  */
314 int
315 mkdir_if_needed (const char *name)
316 {
317     if (mkdir (name, 0777) < 0)
318     {
319 	int save_errno = errno;
320 	if (save_errno != EEXIST && !isdir (name))
321 	    error (1, save_errno, "cannot make directory %s", name);
322 	return 1;
323     }
324     return 0;
325 }
326 
327 /*
328  * Change the mode of a file, either adding write permissions, or removing
329  * all write permissions.  Either change honors the current umask setting.
330  *
331  * Don't do anything if PreservePermissions is set to `yes'.  This may
332  * have unexpected consequences for some uses of xchmod.
333  */
334 void
335 xchmod (const char *fname, int writable)
336 {
337     struct stat sb;
338     mode_t mode, oumask;
339 
340 #ifdef PRESERVE_PERMISSIONS_SUPPORT
341     if (config->preserve_perms)
342 	return;
343 #endif /* PRESERVE_PERMISSIONS_SUPPORT */
344 
345     if (stat (fname, &sb) < 0)
346     {
347 	if (!noexec)
348 	    error (0, errno, "cannot stat %s", fname);
349 	return;
350     }
351     oumask = umask (0);
352     (void) umask (oumask);
353     if (writable)
354     {
355 	mode = sb.st_mode | (~oumask
356 			     & (((sb.st_mode & S_IRUSR) ? S_IWUSR : 0)
357 				| ((sb.st_mode & S_IRGRP) ? S_IWGRP : 0)
358 				| ((sb.st_mode & S_IROTH) ? S_IWOTH : 0)));
359     }
360     else
361     {
362 	mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH) & ~oumask;
363     }
364 
365     TRACE (TRACE_FUNCTION, "chmod(%s,%o)", fname, (unsigned int) mode);
366 
367     if (noexec)
368 	return;
369 
370     if (chmod (fname, mode) < 0)
371 	error (0, errno, "cannot change mode of file %s", fname);
372 }
373 
374 /*
375  * Rename a file and die if it fails
376  */
377 void
378 rename_file (const char *from, const char *to)
379 {
380     TRACE (TRACE_FUNCTION, "rename(%s,%s)", from, to);
381 
382     if (noexec)
383 	return;
384 
385     if (rename (from, to) < 0)
386 	error (1, errno, "cannot rename file %s to %s", from, to);
387 }
388 
389 /*
390  * unlink a file, if possible.
391  */
392 int
393 unlink_file (const char *f)
394 {
395     TRACE (TRACE_FUNCTION, "unlink_file(%s)", f);
396 
397     if (noexec)
398 	return (0);
399 
400     return (CVS_UNLINK (f));
401 }
402 
403 
404 
405 /*
406  * Unlink a file or dir, if possible.  If it is a directory do a deep
407  * removal of all of the files in the directory.  Return -1 on error
408  * (in which case errno is set).
409  */
410 int
411 unlink_file_dir (const char *f)
412 {
413     struct stat sb;
414 
415     /* This is called by the server parent process in contexts where
416        it is not OK to send output (e.g. after we sent "ok" to the
417        client).  */
418     if (!server_active)
419 	TRACE (TRACE_FUNCTION, "unlink_file_dir(%s)", f);
420 
421     if (noexec)
422 	return 0;
423 
424     /* For at least some unices, if root tries to unlink() a directory,
425        instead of doing something rational like returning EISDIR,
426        the system will gleefully go ahead and corrupt the filesystem.
427        So we first call stat() to see if it is OK to call unlink().  This
428        doesn't quite work--if someone creates a directory between the
429        call to stat() and the call to unlink(), we'll still corrupt
430        the filesystem.  Where is the Unix Haters Handbook when you need
431        it?  */
432     if (stat (f, &sb) < 0)
433     {
434 	if (existence_error (errno))
435 	{
436 	    /* The file or directory doesn't exist anyhow.  */
437 	    return -1;
438 	}
439     }
440     else if (S_ISDIR (sb.st_mode))
441 	return deep_remove_dir (f);
442 
443     return CVS_UNLINK (f);
444 }
445 
446 
447 
448 /* Remove a directory and everything it contains.  Returns 0 for
449  * success, -1 for failure (in which case errno is set).
450  */
451 
452 static int
453 deep_remove_dir (const char *path)
454 {
455     DIR		  *dirp;
456     struct dirent *dp;
457 
458     if (rmdir (path) != 0)
459     {
460 	if (errno == ENOTEMPTY
461 	    || errno == EEXIST
462 	    /* Ugly workaround for ugly AIX 4.1 (and 3.2) header bug
463 	       (it defines ENOTEMPTY and EEXIST to 17 but actually
464 	       returns 87).  */
465 	    || (ENOTEMPTY == 17 && EEXIST == 17 && errno == 87))
466 	{
467 	    if ((dirp = CVS_OPENDIR (path)) == NULL)
468 		/* If unable to open the directory return
469 		 * an error
470 		 */
471 		return -1;
472 
473 	    errno = 0;
474 	    while ((dp = CVS_READDIR (dirp)) != NULL)
475 	    {
476 		char *buf;
477 
478 		if (strcmp (dp->d_name, ".") == 0 ||
479 			    strcmp (dp->d_name, "..") == 0)
480 		    continue;
481 
482 		buf = Xasprintf ("%s/%s", path, dp->d_name);
483 
484 		/* See comment in unlink_file_dir explanation of why we use
485 		   isdir instead of just calling unlink and checking the
486 		   status.  */
487 		if (isdir (buf))
488 		{
489 		    if (deep_remove_dir (buf))
490 		    {
491 			CVS_CLOSEDIR (dirp);
492 			free (buf);
493 			return -1;
494 		    }
495 		}
496 		else
497 		{
498 		    if (CVS_UNLINK (buf) != 0)
499 		    {
500 			CVS_CLOSEDIR (dirp);
501 			free (buf);
502 			return -1;
503 		    }
504 		}
505 		free (buf);
506 
507 		errno = 0;
508 	    }
509 	    if (errno != 0)
510 	    {
511 		int save_errno = errno;
512 		CVS_CLOSEDIR (dirp);
513 		errno = save_errno;
514 		return -1;
515 	    }
516 	    CVS_CLOSEDIR (dirp);
517 	    return rmdir (path);
518 	}
519 	else
520 	    return -1;
521     }
522 
523     /* Was able to remove the directory return 0 */
524     return 0;
525 }
526 
527 
528 
529 /* Read NCHARS bytes from descriptor FD into BUF.
530    Return the number of characters successfully read.
531    The number returned is always NCHARS unless end-of-file or error.  */
532 static size_t
533 block_read (int fd, char *buf, size_t nchars)
534 {
535     char *bp = buf;
536     size_t nread;
537 
538     do
539     {
540 	nread = read (fd, bp, nchars);
541 	if (nread == (size_t)-1)
542 	{
543 #ifdef EINTR
544 	    if (errno == EINTR)
545 		continue;
546 #endif
547 	    return (size_t)-1;
548 	}
549 
550 	if (nread == 0)
551 	    break;
552 
553 	bp += nread;
554 	nchars -= nread;
555     } while (nchars != 0);
556 
557     return bp - buf;
558 }
559 
560 
561 /*
562  * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
563  * If FILE1 and FILE2 are special files, compare their salient characteristics
564  * (i.e. major/minor device numbers, links, etc.
565  */
566 int
567 xcmp (const char *file1, const char *file2)
568 {
569     char *buf1, *buf2;
570     struct stat sb1, sb2;
571     int fd1, fd2;
572     int ret;
573 
574     if (lstat (file1, &sb1) < 0)
575 	error (1, errno, "cannot lstat %s", file1);
576     if (lstat (file2, &sb2) < 0)
577 	error (1, errno, "cannot lstat %s", file2);
578 
579     /* If FILE1 and FILE2 are not the same file type, they are unequal. */
580     if ((sb1.st_mode & S_IFMT) != (sb2.st_mode & S_IFMT))
581 	return 1;
582 
583     /* If FILE1 and FILE2 are symlinks, they are equal if they point to
584        the same thing. */
585 #ifdef S_ISLNK
586     if (S_ISLNK (sb1.st_mode) && S_ISLNK (sb2.st_mode))
587     {
588 	int result;
589 	buf1 = Xreadlink (file1, sb1.st_size);
590 	buf2 = Xreadlink (file2, sb2.st_size);
591 	result = (strcmp (buf1, buf2) == 0);
592 	free (buf1);
593 	free (buf2);
594 	return result;
595     }
596 #endif
597 
598     /* If FILE1 and FILE2 are devices, they are equal if their device
599        numbers match. */
600     if (S_ISBLK (sb1.st_mode) || S_ISCHR (sb1.st_mode))
601     {
602 #ifdef HAVE_STRUCT_STAT_ST_RDEV
603 	if (sb1.st_rdev == sb2.st_rdev)
604 	    return 0;
605 	else
606 	    return 1;
607 #else
608 	error (1, 0, "cannot compare device files on this system (%s and %s)",
609 	       file1, file2);
610 #endif
611     }
612 
613     if ((fd1 = open (file1, O_RDONLY)) < 0)
614 	error (1, errno, "cannot open file %s for comparing", file1);
615     if ((fd2 = open (file2, O_RDONLY)) < 0)
616 	error (1, errno, "cannot open file %s for comparing", file2);
617 
618     /* A generic file compare routine might compare st_dev & st_ino here
619        to see if the two files being compared are actually the same file.
620        But that won't happen in CVS, so we won't bother. */
621 
622     if (sb1.st_size != sb2.st_size)
623 	ret = 1;
624     else if (sb1.st_size == 0)
625 	ret = 0;
626     else
627     {
628 	/* FIXME: compute the optimal buffer size by computing the least
629 	   common multiple of the files st_blocks field */
630 	size_t buf_size = 8 * 1024;
631 	size_t read1;
632 	size_t read2;
633 
634 	buf1 = xmalloc (buf_size);
635 	buf2 = xmalloc (buf_size);
636 
637 	do
638 	{
639 	    read1 = block_read (fd1, buf1, buf_size);
640 	    if (read1 == (size_t)-1) {
641 		error (1, errno, "cannot read file %s for comparing", file1);
642 		exit(1);	/* to suppress -Wstringop-overflow */
643 	    }
644 
645 	    read2 = block_read (fd2, buf2, buf_size);
646 	    if (read2 == (size_t)-1)
647 		error (1, errno, "cannot read file %s for comparing", file2);
648 
649 	    /* assert (read1 == read2); */
650 
651 	    ret = memcmp(buf1, buf2, read1);
652 	} while (ret == 0 && read1 == buf_size);
653 
654 	free (buf1);
655 	free (buf2);
656     }
657 
658     (void) close (fd1);
659     (void) close (fd2);
660     return (ret);
661 }
662 
663 /* Generate a unique temporary filename.  Returns a pointer to a newly
664  * malloc'd string containing the name.  Returns successfully or not at
665  * all.
666  *
667  *     THIS FUNCTION IS DEPRECATED!!!  USE cvs_temp_file INSTEAD!!!
668  *
669  * and yes, I know about the way the rcs commands use temp files.  I think
670  * they should be converted too but I don't have time to look into it right
671  * now.
672  */
673 char *
674 cvs_temp_name (void)
675 {
676     char *fn;
677     FILE *fp;
678 
679     fp = cvs_temp_file (&fn);
680     if (fp == NULL)
681 	error (1, errno, "Failed to create temporary file");
682     if (fclose (fp) == EOF)
683 	error (0, errno, "Failed to close temporary file %s", fn);
684     return fn;
685 }
686 
687 /* Generate a unique temporary filename and return an open file stream
688  * to the truncated file by that name
689  *
690  *  INPUTS
691  *	filename	where to place the pointer to the newly allocated file
692  *   			name string
693  *
694  *  OUTPUTS
695  *	filename	dereferenced, will point to the newly allocated file
696  *			name string.  This value is undefined if the function
697  *			returns an error.
698  *
699  *  RETURNS
700  *	An open file pointer to a read/write mode empty temporary file with the
701  *	unique file name or NULL on failure.
702  *
703  *  ERRORS
704  *	On error, errno will be set to some value either by CVS_FOPEN or
705  *	whatever system function is called to generate the temporary file name.
706  *	The value of filename is undefined on error.
707  */
708 FILE *
709 cvs_temp_file (char **filename)
710 {
711     char *fn;
712     FILE *fp;
713     int fd;
714 
715     /* FIXME - I'd like to be returning NULL here in noexec mode, but I think
716      * some of the rcs & diff functions which rely on a temp file run in
717      * noexec mode too.
718      */
719 
720     assert (filename != NULL);
721 
722     fn = Xasprintf ("%s/%s", get_cvs_tmp_dir (), "cvsXXXXXX");
723     fd = mkstemp (fn);
724 
725     /* a NULL return will be interpreted by callers as an error and
726      * errno should still be set
727      */
728     if (fd == -1)
729 	fp = NULL;
730     else if ((fp = CVS_FDOPEN (fd, "w+")) == NULL)
731     {
732 	/* Attempt to close and unlink the file since mkstemp returned
733 	 * sucessfully and we believe it's been created and opened.
734 	 */
735  	int save_errno = errno;
736 	if (close (fd))
737 	    error (0, errno, "Failed to close temporary file %s", fn);
738 	if (CVS_UNLINK (fn))
739 	    error (0, errno, "Failed to unlink temporary file %s", fn);
740 	errno = save_errno;
741     }
742 
743     if (fp == NULL)
744 	free (fn);
745 
746     /* mkstemp is defined to open mode 0600 using glibc 2.0.7+.  There used
747      * to be a complicated #ifdef checking the library versions here and then
748      * a chmod 0600 on the temp file for versions of glibc less than 2.1.  This
749      * is rather a special case, leaves a race condition open regardless, and
750      * one could hope that sysadmins have read the relevant security
751      * announcements and upgraded by now to a version with a fix committed in
752      * January of 1999.
753      *
754      * If it is decided at some point that old, buggy versions of glibc should
755      * still be catered to, a umask of 0600 should be set before file creation
756      * instead then reset after file creation since this would avoid the race
757      * condition that the chmod left open to exploitation.
758      */
759 
760     *filename = fn;
761     return fp;
762 }
763 
764 
765 
766 /* Return a pointer into PATH's last component.  */
767 const char *
768 last_component (const char *path)
769 {
770     const char *last = strrchr (path, '/');
771 
772     if (last && (last != path))
773         return last + 1;
774     else
775         return path;
776 }
777 
778 
779 
780 /* Return the home directory.  Returns a pointer to storage
781    managed by this function or its callees (currently getenv).
782    This function will return the same thing every time it is
783    called.  Returns NULL if there is no home directory.
784 
785    Note that for a pserver server, this may return root's home
786    directory.  What typically happens is that upon being started from
787    inetd, before switching users, the code in cvsrc.c calls
788    get_homedir which remembers root's home directory in the static
789    variable.  Then the switch happens and get_homedir might return a
790    directory that we don't even have read or execute permissions for
791    (which is bad, when various parts of CVS try to read there).  One
792    fix would be to make the value returned by get_homedir only good
793    until the next call (which would free the old value).  Another fix
794    would be to just always malloc our answer, and let the caller free
795    it (that is best, because some day we may need to be reentrant).
796 
797    The workaround is to put -f in inetd.conf which means that
798    get_homedir won't get called until after the switch in user ID.
799 
800    The whole concept of a "home directory" on the server is pretty
801    iffy, although I suppose some people probably are relying on it for
802    .cvsrc and such, in the cases where it works.  */
803 char *
804 get_homedir (void)
805 {
806     static char *home = NULL;
807     char *env;
808     struct passwd *pw;
809 
810     if (home != NULL)
811 	return home;
812 
813     if (!server_active && (env = getenv ("HOME")) != NULL)
814 	home = env;
815     else if ((pw = (struct passwd *) getpwuid (getuid ()))
816 	     && pw->pw_dir)
817 	home = xstrdup (pw->pw_dir);
818     else
819 	return 0;
820 
821     return home;
822 }
823 
824 /* Compose a path to a file in the home directory.  This is necessary because
825  * of different behavior on UNIX and VMS.  See the notes in vms/filesubr.c.
826  *
827  * A more clean solution would be something more along the lines of a
828  * "join a directory to a filename" kind of thing which was not specific to
829  * the homedir.  This should aid portability between UNIX, Mac, Windows, VMS,
830  * and possibly others.  This is already handled by Perl - it might be
831  * interesting to see how much of the code was written in C since Perl is under
832  * the GPL and the Artistic license - we might be able to use it.
833  */
834 char *
835 strcat_filename_onto_homedir (const char *dir, const char *file)
836 {
837     char *path = Xasprintf ("%s/%s", dir, file);
838     return path;
839 }
840 
841 /* See cvs.h for description.  On unix this does nothing, because the
842    shell expands the wildcards.  */
843 void
844 expand_wild (int argc, char **argv, int *pargc, char ***pargv)
845 {
846     int i;
847     if (size_overflow_p (xtimes (argc, sizeof (char *)))) {
848 	*pargc = 0;
849 	*pargv = NULL;
850 	error (0, 0, "expand_wild: too many arguments");
851 	return;
852     }
853     *pargc = argc;
854     *pargv = xnmalloc (argc, sizeof (char *));
855     for (i = 0; i < argc; ++i)
856 	(*pargv)[i] = xstrdup (argv[i]);
857 }
858 
859 
860 
861 static char *tmpdir_env;
862 
863 /* Return path to temp directory.
864  */
865 const char *
866 get_system_temp_dir (void)
867 {
868     if (!tmpdir_env) tmpdir_env = getenv (TMPDIR_ENV);
869     return tmpdir_env;
870 }
871 
872 
873 
874 void
875 push_env_temp_dir (void)
876 {
877     const char *tmpdir = get_cvs_tmp_dir ();
878     if (tmpdir_env && strcmp (tmpdir_env, tmpdir))
879 	setenv (TMPDIR_ENV, tmpdir, 1);
880 }
881