xref: /dragonfly/contrib/cvs-1.12/src/filesubr.c (revision ed5d5720)
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 
643 	    read2 = block_read (fd2, buf2, buf_size);
644 	    if (read2 == (size_t)-1)
645 		error (1, errno, "cannot read file %s for comparing", file2);
646 
647 	    /* assert (read1 == read2); */
648 
649 	    ret = memcmp(buf1, buf2, read1);
650 	} while (ret == 0 && read1 == buf_size);
651 
652 	free (buf1);
653 	free (buf2);
654     }
655 
656     (void) close (fd1);
657     (void) close (fd2);
658     return (ret);
659 }
660 
661 /* Generate a unique temporary filename.  Returns a pointer to a newly
662  * malloc'd string containing the name.  Returns successfully or not at
663  * all.
664  *
665  *     THIS FUNCTION IS DEPRECATED!!!  USE cvs_temp_file INSTEAD!!!
666  *
667  * and yes, I know about the way the rcs commands use temp files.  I think
668  * they should be converted too but I don't have time to look into it right
669  * now.
670  */
671 char *
672 cvs_temp_name (void)
673 {
674     char *fn;
675     FILE *fp;
676 
677     fp = cvs_temp_file (&fn);
678     if (fp == NULL)
679 	error (1, errno, "Failed to create temporary file");
680     if (fclose (fp) == EOF)
681 	error (0, errno, "Failed to close temporary file %s", fn);
682     return fn;
683 }
684 
685 /* Generate a unique temporary filename and return an open file stream
686  * to the truncated file by that name
687  *
688  *  INPUTS
689  *	filename	where to place the pointer to the newly allocated file
690  *   			name string
691  *
692  *  OUTPUTS
693  *	filename	dereferenced, will point to the newly allocated file
694  *			name string.  This value is undefined if the function
695  *			returns an error.
696  *
697  *  RETURNS
698  *	An open file pointer to a read/write mode empty temporary file with the
699  *	unique file name or NULL on failure.
700  *
701  *  ERRORS
702  *	On error, errno will be set to some value either by CVS_FOPEN or
703  *	whatever system function is called to generate the temporary file name.
704  *	The value of filename is undefined on error.
705  */
706 FILE *
707 cvs_temp_file (char **filename)
708 {
709     char *fn;
710     FILE *fp;
711     int fd;
712 
713     /* FIXME - I'd like to be returning NULL here in noexec mode, but I think
714      * some of the rcs & diff functions which rely on a temp file run in
715      * noexec mode too.
716      */
717 
718     assert (filename != NULL);
719 
720     fn = Xasprintf ("%s/%s", get_cvs_tmp_dir (), "cvsXXXXXX");
721     fd = mkstemp (fn);
722 
723     /* a NULL return will be interpreted by callers as an error and
724      * errno should still be set
725      */
726     if (fd == -1)
727 	fp = NULL;
728     else if ((fp = CVS_FDOPEN (fd, "w+")) == NULL)
729     {
730 	/* Attempt to close and unlink the file since mkstemp returned
731 	 * sucessfully and we believe it's been created and opened.
732 	 */
733  	int save_errno = errno;
734 	if (close (fd))
735 	    error (0, errno, "Failed to close temporary file %s", fn);
736 	if (CVS_UNLINK (fn))
737 	    error (0, errno, "Failed to unlink temporary file %s", fn);
738 	errno = save_errno;
739     }
740 
741     if (fp == NULL)
742 	free (fn);
743 
744     /* mkstemp is defined to open mode 0600 using glibc 2.0.7+.  There used
745      * to be a complicated #ifdef checking the library versions here and then
746      * a chmod 0600 on the temp file for versions of glibc less than 2.1.  This
747      * is rather a special case, leaves a race condition open regardless, and
748      * one could hope that sysadmins have read the relevant security
749      * announcements and upgraded by now to a version with a fix committed in
750      * January of 1999.
751      *
752      * If it is decided at some point that old, buggy versions of glibc should
753      * still be catered to, a umask of 0600 should be set before file creation
754      * instead then reset after file creation since this would avoid the race
755      * condition that the chmod left open to exploitation.
756      */
757 
758     *filename = fn;
759     return fp;
760 }
761 
762 
763 
764 /* Return a pointer into PATH's last component.  */
765 const char *
766 last_component (const char *path)
767 {
768     const char *last = strrchr (path, '/');
769 
770     if (last && (last != path))
771         return last + 1;
772     else
773         return path;
774 }
775 
776 
777 
778 /* Return the home directory.  Returns a pointer to storage
779    managed by this function or its callees (currently getenv).
780    This function will return the same thing every time it is
781    called.  Returns NULL if there is no home directory.
782 
783    Note that for a pserver server, this may return root's home
784    directory.  What typically happens is that upon being started from
785    inetd, before switching users, the code in cvsrc.c calls
786    get_homedir which remembers root's home directory in the static
787    variable.  Then the switch happens and get_homedir might return a
788    directory that we don't even have read or execute permissions for
789    (which is bad, when various parts of CVS try to read there).  One
790    fix would be to make the value returned by get_homedir only good
791    until the next call (which would free the old value).  Another fix
792    would be to just always malloc our answer, and let the caller free
793    it (that is best, because some day we may need to be reentrant).
794 
795    The workaround is to put -f in inetd.conf which means that
796    get_homedir won't get called until after the switch in user ID.
797 
798    The whole concept of a "home directory" on the server is pretty
799    iffy, although I suppose some people probably are relying on it for
800    .cvsrc and such, in the cases where it works.  */
801 char *
802 get_homedir (void)
803 {
804     static char *home = NULL;
805     char *env;
806     struct passwd *pw;
807 
808     if (home != NULL)
809 	return home;
810 
811     if (!server_active && (env = getenv ("HOME")) != NULL)
812 	home = env;
813     else if ((pw = (struct passwd *) getpwuid (getuid ()))
814 	     && pw->pw_dir)
815 	home = xstrdup (pw->pw_dir);
816     else
817 	return 0;
818 
819     return home;
820 }
821 
822 /* Compose a path to a file in the home directory.  This is necessary because
823  * of different behavior on UNIX and VMS.  See the notes in vms/filesubr.c.
824  *
825  * A more clean solution would be something more along the lines of a
826  * "join a directory to a filename" kind of thing which was not specific to
827  * the homedir.  This should aid portability between UNIX, Mac, Windows, VMS,
828  * and possibly others.  This is already handled by Perl - it might be
829  * interesting to see how much of the code was written in C since Perl is under
830  * the GPL and the Artistic license - we might be able to use it.
831  */
832 char *
833 strcat_filename_onto_homedir (const char *dir, const char *file)
834 {
835     char *path = Xasprintf ("%s/%s", dir, file);
836     return path;
837 }
838 
839 /* See cvs.h for description.  On unix this does nothing, because the
840    shell expands the wildcards.  */
841 void
842 expand_wild (int argc, char **argv, int *pargc, char ***pargv)
843 {
844     int i;
845     if (size_overflow_p (xtimes (argc, sizeof (char *)))) {
846 	*pargc = 0;
847 	*pargv = NULL;
848 	error (0, 0, "expand_wild: too many arguments");
849 	return;
850     }
851     *pargc = argc;
852     *pargv = xnmalloc (argc, sizeof (char *));
853     for (i = 0; i < argc; ++i)
854 	(*pargv)[i] = xstrdup (argv[i]);
855 }
856 
857 
858 
859 static char *tmpdir_env;
860 
861 /* Return path to temp directory.
862  */
863 const char *
864 get_system_temp_dir (void)
865 {
866     if (!tmpdir_env) tmpdir_env = getenv (TMPDIR_ENV);
867     return tmpdir_env;
868 }
869 
870 
871 
872 void
873 push_env_temp_dir (void)
874 {
875     const char *tmpdir = get_cvs_tmp_dir ();
876     if (tmpdir_env && strcmp (tmpdir_env, tmpdir))
877 	setenv (TMPDIR_ENV, tmpdir, 1);
878 }
879