xref: /openbsd/gnu/usr.bin/cvs/emx/filesubr.c (revision b6f6614e)
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 <sys/param.h>
22 
23 /*
24  * I don't know of a convenient way to test this at configure time, or else
25  * I'd certainly do it there.
26  */
27 #if defined(NeXT)
28 #define LOSING_TMPNAM_FUNCTION
29 #endif
30 
31 static int deep_remove_dir PROTO((const char *path));
32 
33 /*
34  * Copies "from" to "to".
35  */
36 void
copy_file(from,to)37 copy_file (from, to)
38     const char *from;
39     const char *to;
40 {
41     struct stat sb;
42     struct utimbuf t;
43     int fdin, fdout;
44 
45     if (trace)
46 #ifdef SERVER_SUPPORT
47 	(void) fprintf (stderr, "%c-> copy(%s,%s)\n",
48 			(server_active) ? 'S' : ' ', from, to);
49 #else
50 	(void) fprintf (stderr, "-> copy(%s,%s)\n", from, to);
51 #endif
52     if (noexec)
53 	return;
54 
55     if ((fdin = open (from, O_RDONLY | O_BINARY)) < 0)
56 	error (1, errno, "cannot open %s for copying", from);
57     if (fstat (fdin, &sb) < 0)
58 	error (1, errno, "cannot fstat %s", from);
59     if ((fdout = open (to, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY,
60 					   (int) sb.st_mode & 07777)) < 0)
61 	error (1, errno, "cannot create %s for copying", to);
62     if (sb.st_size > 0)
63     {
64 	char buf[BUFSIZ];
65 	int n;
66 
67 	for (;;)
68 	{
69 	    n = read (fdin, buf, sizeof(buf));
70 	    if (n == -1)
71 	    {
72 #ifdef EINTR
73 		if (errno == EINTR)
74 		    continue;
75 #endif
76 		error (1, errno, "cannot read file %s for copying", from);
77 	    }
78             else if (n == 0)
79 		break;
80 
81 	    if (write(fdout, buf, n) != n) {
82 		error (1, errno, "cannot write file %s for copying", to);
83 	    }
84 	}
85 
86 #ifdef HAVE_FSYNC
87 	if (fsync (fdout))
88 	    error (1, errno, "cannot fsync file %s after copying", to);
89 #endif
90     }
91 
92     if (close (fdin) < 0)
93 	error (0, errno, "cannot close %s", from);
94     if (close (fdout) < 0)
95 	error (1, errno, "cannot close %s", to);
96 
97     /* now, set the times for the copied file to match those of the original */
98     memset ((char *) &t, 0, sizeof (t));
99     t.actime = sb.st_atime;
100     t.modtime = sb.st_mtime;
101     (void) utime (to, &t);
102 }
103 
104 /* FIXME-krp: these functions would benefit from caching the char * &
105    stat buf.  */
106 
107 /*
108  * Returns non-zero if the argument file is a directory, or is a symbolic
109  * link which points to a directory.
110  */
111 int
isdir(file)112 isdir (file)
113     const char *file;
114 {
115     struct stat sb;
116 
117     if (stat (file, &sb) < 0)
118 	return (0);
119     return (S_ISDIR (sb.st_mode));
120 }
121 
122 /*
123  * Returns non-zero if the argument file is a symbolic link.
124  */
125 int
islink(file)126 islink (file)
127     const char *file;
128 {
129 #ifdef S_ISLNK
130     struct stat sb;
131 
132     if (lstat (file, &sb) < 0)
133 	return (0);
134     return (S_ISLNK (sb.st_mode));
135 #else
136     return (0);
137 #endif
138 }
139 
140 /*
141  * Returns non-zero if the argument file exists.
142  */
143 int
isfile(file)144 isfile (file)
145     const char *file;
146 {
147     return isaccessible(file, F_OK);
148 }
149 
150 /*
151  * Returns non-zero if the argument file is readable.
152  */
153 int
isreadable(file)154 isreadable (file)
155     const char *file;
156 {
157     return isaccessible(file, R_OK);
158 }
159 
160 /*
161  * Returns non-zero if the argument file is writable.
162  */
163 int
iswritable(file)164 iswritable (file)
165     const char *file;
166 {
167     return isaccessible(file, W_OK);
168 }
169 
170 /*
171  * Returns non-zero if the argument file is accessable according to
172  * mode.  If compiled with SETXID_SUPPORT also works if cvs has setxid
173  * bits set.
174  */
175 int
isaccessible(file,mode)176 isaccessible (file, mode)
177     const char *file;
178     const int mode;
179 {
180 #ifdef SETXID_SUPPORT
181     struct stat sb;
182     int umask = 0;
183     int gmask = 0;
184     int omask = 0;
185     int uid;
186 
187     if (stat(file, &sb) == -1)
188 	return 0;
189     if (mode == F_OK)
190 	return 1;
191 
192     uid = geteuid();
193     if (uid == 0)		/* superuser */
194     {
195 	if (mode & X_OK)
196 	    return sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH);
197 	else
198 	    return 1;
199     }
200 
201     if (mode & R_OK)
202     {
203 	umask |= S_IRUSR;
204 	gmask |= S_IRGRP;
205 	omask |= S_IROTH;
206     }
207     if (mode & W_OK)
208     {
209 	umask |= S_IWUSR;
210 	gmask |= S_IWGRP;
211 	omask |= S_IWOTH;
212     }
213     if (mode & X_OK)
214     {
215 	umask |= S_IXUSR;
216 	gmask |= S_IXGRP;
217 	omask |= S_IXOTH;
218     }
219 
220     if (sb.st_uid == uid)
221 	return (sb.st_mode & umask) == umask;
222     else if (sb.st_gid == getegid())
223 	return (sb.st_mode & gmask) == gmask;
224     else
225 	return (sb.st_mode & omask) == omask;
226 #else
227     return access(file, mode) == 0;
228 #endif
229 }
230 
231 /*
232  * Open a file and die if it fails
233  */
234 FILE *
open_file(name,mode)235 open_file (name, mode)
236     const char *name;
237     const char *mode;
238 {
239     FILE *fp;
240 
241     if ((fp = fopen (name, mode)) == NULL)
242 	error (1, errno, "cannot open %s", name);
243     return (fp);
244 }
245 
246 /*
247  * Make a directory and die if it fails
248  */
249 void
make_directory(name)250 make_directory (name)
251     const char *name;
252 {
253     struct stat sb;
254 
255     if (stat (name, &sb) == 0 && (!S_ISDIR (sb.st_mode)))
256 	    error (0, 0, "%s already exists but is not a directory", name);
257     if (!noexec && mkdir (name, 0777) < 0)
258 	error (1, errno, "cannot make directory %s", name);
259 }
260 
261 /*
262  * Make a path to the argument directory, printing a message if something
263  * goes wrong.
264  */
265 void
make_directories(name)266 make_directories (name)
267     const char *name;
268 {
269     char *cp;
270 
271     if (noexec)
272 	return;
273 
274     if (mkdir (name, 0777) == 0 || errno == EEXIST)
275 	return;
276     if (! existence_error (errno))
277     {
278 	error (0, errno, "cannot make path to %s", name);
279 	return;
280     }
281     if ((cp = strrchr (name, '/')) == NULL)
282 	return;
283     *cp = '\0';
284     make_directories (name);
285     *cp++ = '/';
286     if (*cp == '\0')
287 	return;
288     (void) mkdir (name, 0777);
289 }
290 
291 /* Create directory NAME if it does not already exist; fatal error for
292    other errors.  Returns 0 if directory was created; 1 if it already
293    existed.  */
294 int
mkdir_if_needed(name)295 mkdir_if_needed (name)
296     char *name;
297 {
298     if (mkdir (name, 0777) < 0)
299     {
300 	if (errno != EEXIST)
301 	    error (1, errno, "cannot make directory %s", name);
302 	return 1;
303     }
304     return 0;
305 }
306 
307 /*
308  * Change the mode of a file, either adding write permissions, or removing
309  * all write permissions.  Either change honors the current umask setting.
310  * The EMX doc (0.9c, emxlib.doc) says that chmod sets/clears the readonly
311  * bit.  But it always seemed to be a noop when I tried it.  Therefore,
312  * I've copied over the "attrib" code from os2/filesubr.c.
313  */
314 void
xchmod(fname,writable)315 xchmod (fname, writable)
316     char *fname;
317     int writable;
318 {
319     char *attrib_cmd;
320     char *attrib_option;
321     char *whole_cmd;
322     char *p;
323     char *q;
324 
325     if (!isfile (fname))
326     {
327 	error (0, 0, "cannot change mode of file %s; it does not exist",
328 	       fname);
329 	return;
330     }
331 
332     attrib_cmd = "attrib "; /* No, really? */
333 
334     if (writable)
335         attrib_option = "-r ";  /* make writeable */
336     else
337         attrib_option = "+r ";  /* make read-only */
338 
339     whole_cmd = xmalloc (strlen (attrib_cmd)
340                          + strlen (attrib_option)
341                          + strlen (fname)
342                          + 1);
343 
344     strcpy (whole_cmd, attrib_cmd);
345     strcat (whole_cmd, attrib_option);
346 
347     /* Copy fname to the end of whole_cmd, translating / to \.
348 	   Attrib doesn't take / but many parts of CVS rely
349        on being able to use it.  */
350     p = whole_cmd + strlen (whole_cmd);
351     q = fname;
352     while (*q)
353     {
354 	if (*q == '/')
355 	    *p++ = '\\';
356 	else
357 	    *p++ = *q;
358 	++q;
359     }
360     *p = '\0';
361 
362     system (whole_cmd);
363     free (whole_cmd);
364 }
365 
366 /*
367  * Rename a file and die if it fails
368  */
369 void
rename_file(from,to)370 rename_file (from, to)
371     const char *from;
372     const char *to;
373 {
374     if (trace)
375 #ifdef SERVER_SUPPORT
376 	(void) fprintf (stderr, "%c-> rename(%s,%s)\n",
377 			(server_active) ? 'S' : ' ', from, to);
378 #else
379 	(void) fprintf (stderr, "-> rename(%s,%s)\n", from, to);
380 #endif
381     if (noexec)
382 	return;
383 
384     unlink_file (to);
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
unlink_file(f)393 unlink_file (f)
394     const char *f;
395 {
396     if (trace)
397 #ifdef SERVER_SUPPORT
398 	(void) fprintf (stderr, "%c-> unlink(%s)\n",
399 			(server_active) ? 'S' : ' ', f);
400 #else
401 	(void) fprintf (stderr, "-> unlink(%s)\n", f);
402 #endif
403     if (noexec)
404 	return (0);
405 
406     if (isfile (f))
407 	xchmod ((char *)f, 1);
408     return (unlink (f));
409 }
410 
411 /*
412  * Unlink a file or dir, if possible.  If it is a directory do a deep
413  * removal of all of the files in the directory.  Return -1 on error
414  * (in which case errno is set).
415  */
416 int
unlink_file_dir(f)417 unlink_file_dir (f)
418     const char *f;
419 {
420     if (trace)
421 #ifdef SERVER_SUPPORT
422 	(void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n",
423 			(server_active) ? 'S' : ' ', f);
424 #else
425 	(void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
426 #endif
427     if (noexec)
428 	return (0);
429 
430     /* For at least some unices, if root tries to unlink() a directory,
431        instead of doing something rational like returning EISDIR,
432        the system will gleefully go ahead and corrupt the filesystem.
433        So we first call isdir() to see if it is OK to call unlink().  This
434        doesn't quite work--if someone creates a directory between the
435        call to isdir() and the call to unlink(), we'll still corrupt
436        the filesystem.  Where is the Unix Haters Handbook when you need
437        it?  */
438     if (isdir(f))
439 	return deep_remove_dir(f);
440     else
441     {
442 	if (unlink (f) != 0)
443 	    return -1;
444     }
445     /* We were able to remove the file from the disk */
446     return 0;
447 }
448 
449 /* Remove a directory and everything it contains.  Returns 0 for
450  * success, -1 for failure (in which case errno is set).
451  */
452 
453 static int
deep_remove_dir(path)454 deep_remove_dir (path)
455     const char *path;
456 {
457     DIR		  *dirp;
458     struct dirent *dp;
459     char	   buf[PATH_MAX];
460 
461     if (rmdir (path) != 0)
462     {
463 	if (errno == ENOTEMPTY
464 	    || errno == EEXIST
465 	    /* Ugly workaround for ugly AIX 4.1 (and 3.2) header bug
466 	       (it defines ENOTEMPTY and EEXIST to 17 but actually
467 	       returns 87).  */
468 	    || (ENOTEMPTY == 17 && EEXIST == 17 && errno == 87))
469 	{
470 	    if ((dirp = opendir (path)) == NULL)
471 		/* If unable to open the directory return
472 		 * an error
473 		 */
474 		return -1;
475 
476 	    while ((dp = readdir (dirp)) != NULL)
477 	    {
478 		if (strcmp (dp->d_name, ".") == 0 ||
479 			    strcmp (dp->d_name, "..") == 0)
480 		    continue;
481 
482 		sprintf (buf, "%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 			closedir(dirp);
492 			return -1;
493 		    }
494 		}
495 		else
496 		{
497 		    if (unlink (buf) != 0)
498 		    {
499 			closedir(dirp);
500 			return -1;
501 		    }
502 		}
503 	    }
504 	    closedir (dirp);
505 	    return rmdir (path);
506 	}
507 	else
508 	    return -1;
509     }
510 
511     /* Was able to remove the directory return 0 */
512     return 0;
513 }
514 
515 /* Read NCHARS bytes from descriptor FD into BUF.
516    Return the number of characters successfully read.
517    The number returned is always NCHARS unless end-of-file or error.  */
518 static size_t
block_read(fd,buf,nchars)519 block_read (fd, buf, nchars)
520     int fd;
521     char *buf;
522     size_t nchars;
523 {
524     char *bp = buf;
525     size_t nread;
526 
527     do
528     {
529 	nread = read (fd, bp, nchars);
530 	if (nread == (size_t)-1)
531 	{
532 #ifdef EINTR
533 	    if (errno == EINTR)
534 		continue;
535 #endif
536 	    return (size_t)-1;
537 	}
538 
539 	if (nread == 0)
540 	    break;
541 
542 	bp += nread;
543 	nchars -= nread;
544     } while (nchars != 0);
545 
546     return bp - buf;
547 }
548 
549 
550 /*
551  * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
552  */
553 int
xcmp(file1,file2)554 xcmp (file1, file2)
555     const char *file1;
556     const char *file2;
557 {
558     char *buf1, *buf2;
559     struct stat sb1, sb2;
560     int fd1, fd2;
561     int ret;
562 
563     if ((fd1 = open (file1, O_RDONLY | O_BINARY)) < 0)
564 	error (1, errno, "cannot open file %s for comparing", file1);
565     if ((fd2 = open (file2, O_RDONLY | O_BINARY)) < 0)
566 	error (1, errno, "cannot open file %s for comparing", file2);
567     if (fstat (fd1, &sb1) < 0)
568 	error (1, errno, "cannot fstat %s", file1);
569     if (fstat (fd2, &sb2) < 0)
570 	error (1, errno, "cannot fstat %s", file2);
571 
572     /* A generic file compare routine might compare st_dev & st_ino here
573        to see if the two files being compared are actually the same file.
574        But that won't happen in CVS, so we won't bother. */
575 
576     if (sb1.st_size != sb2.st_size)
577 	ret = 1;
578     else if (sb1.st_size == 0)
579 	ret = 0;
580     else
581     {
582 	/* FIXME: compute the optimal buffer size by computing the least
583 	   common multiple of the files st_blocks field */
584 	size_t buf_size = 8 * 1024;
585 	size_t read1;
586 	size_t read2;
587 
588 	buf1 = xmalloc (buf_size);
589 	buf2 = xmalloc (buf_size);
590 
591 	do
592 	{
593 	    read1 = block_read (fd1, buf1, buf_size);
594 	    if (read1 == (size_t)-1)
595 		error (1, errno, "cannot read file %s for comparing", file1);
596 
597 	    read2 = block_read (fd2, buf2, buf_size);
598 	    if (read2 == (size_t)-1)
599 		error (1, errno, "cannot read file %s for comparing", file2);
600 
601 	    /* assert (read1 == read2); */
602 
603 	    ret = memcmp(buf1, buf2, read1);
604 	} while (ret == 0 && read1 == buf_size);
605 
606 	free (buf1);
607 	free (buf2);
608     }
609 
610     (void) close (fd1);
611     (void) close (fd2);
612     return (ret);
613 }
614 
615 
616 /* Just in case this implementation does not define this.  */
617 #ifndef L_tmpnam
618 #define	L_tmpnam 50
619 #endif
620 
621 
622 #ifdef LOSING_TMPNAM_FUNCTION
623 char *
cvs_temp_name()624 cvs_temp_name ()
625 {
626     char value[L_tmpnam + 1];
627 
628     /* FIXME: Should be using TMPDIR.  */
629     strcpy (value, "/tmp/cvsXXXXXX");
630     mktemp (value);
631     return xstrdup (value);
632 }
633 #else
634 /* Generate a unique temporary filename.  Returns a pointer to a newly
635    malloc'd string containing the name.  Returns successfully or not at
636    all.  */
637 char *
cvs_temp_name()638 cvs_temp_name ()
639 {
640     char value[L_tmpnam + 1];
641     char *retval;
642 
643     /* FIXME: should be using TMPDIR, perhaps by using tempnam on systems
644        which have it.  */
645     retval = tmpnam (value);
646     if (retval == NULL)
647 	error (1, errno, "cannot generate temporary filename");
648     return xstrdup (retval);
649 }
650 #endif
651 
652 
653 /* Return non-zero iff FILENAME is absolute.
654    Trivial under Unix, but more complicated under other systems.
655    Under EMX let _fnisabs do all this work. */
656 int
isabsolute(filename)657 isabsolute (filename)
658     const char *filename;
659 {
660     return _fnisabs(filename);
661 }
662 
663 
664 /* Return a pointer into PATH's last component.  */
665 char *
last_component(path)666 last_component (path)
667     char *path;
668 {
669     char *last;
670 
671     /* We can't be sure here if 'path' is already slashified. */
672     _fnslashify (path);
673 
674     last = strrchr (path, '/');
675 
676     if (last && (last != path))
677         return last + 1;
678     else
679         return path;
680 }
681 
682 /* Return the home directory.  Returns a pointer to storage
683    managed by this function or its callees (currently getenv).
684    This function will return the same thing every time it is
685    called.  */
686 char *
get_homedir()687 get_homedir ()
688 {
689     static char *home = NULL;
690     char *env = getenv ("HOME");
691     struct passwd *pw;
692 
693     if (home != NULL)
694 	return home;
695 
696     if (env)
697 	home = env;
698     else if ((pw = (struct passwd *) getpwuid (getuid ()))
699 	     && pw->pw_dir)
700 	home = xstrdup (pw->pw_dir);
701     else
702 	return 0;
703 
704     return home;
705 }
706 
707 /* See cvs.h for description.  On unix this does nothing, because the
708    shell expands the wildcards.  Under EMX, use _fnexplode to get the
709    expanded filenames */
710 void
expand_wild(argc,argv,pargc,pargv)711 expand_wild (argc, argv, pargc, pargv)
712     int argc;
713     char **argv;
714     int *pargc;
715     char ***pargv;
716 {
717     int i;
718     *pargc = argc;
719     *pargv = (char **) xmalloc (argc * sizeof (char *));
720     for (i = 0; i < argc; ++i)
721 	(*pargv)[i] = xstrdup (argv[i]);
722 }
723 
724 unsigned char
725 OS2_filename_classes[] =
726 {
727     0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,
728     0x08,0x09,0x0a,0x0b, 0x0c,0x0d,0x0e,0x0f,
729     0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17,
730     0x18,0x19,0x1a,0x1b, 0x1c,0x1d,0x1e,0x1f,
731     0x20,0x21,0x22,0x23, 0x24,0x25,0x26,0x27,
732     0x28,0x29,0x2a,0x2b, 0x2c,0x2d,0x2e,0x2f,
733     0x30,0x31,0x32,0x33, 0x34,0x35,0x36,0x37,
734     0x38,0x39,0x3a,0x3b, 0x3c,0x3d,0x3e,0x3f,
735     0x40,0x61,0x62,0x63, 0x64,0x65,0x66,0x67,
736     0x68,0x69,0x6a,0x6b, 0x6c,0x6d,0x6e,0x6f,
737     0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77,
738     0x78,0x79,0x7a,0x5b, 0x2f,0x5d,0x5e,0x5f,
739     0x60,0x61,0x62,0x63, 0x64,0x65,0x66,0x67,
740     0x68,0x69,0x6a,0x6b, 0x6c,0x6d,0x6e,0x6f,
741     0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77,
742     0x78,0x79,0x7a,0x7b, 0x7c,0x7d,0x7e,0x7f,
743     0x80,0x81,0x82,0x83, 0x84,0x85,0x86,0x87,
744     0x88,0x89,0x8a,0x8b, 0x8c,0x8d,0x8e,0x8f,
745     0x90,0x91,0x92,0x93, 0x94,0x95,0x96,0x97,
746     0x98,0x99,0x9a,0x9b, 0x9c,0x9d,0x9e,0x9f,
747     0xa0,0xa1,0xa2,0xa3, 0xa4,0xa5,0xa6,0xa7,
748     0xa8,0xa9,0xaa,0xab, 0xac,0xad,0xae,0xaf,
749     0xb0,0xb1,0xb2,0xb3, 0xb4,0xb5,0xb6,0xb7,
750     0xb8,0xb9,0xba,0xbb, 0xbc,0xbd,0xbe,0xbf,
751     0xc0,0xc1,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7,
752     0xc8,0xc9,0xca,0xcb, 0xcc,0xcd,0xce,0xcf,
753     0xd0,0xd1,0xd2,0xd3, 0xd4,0xd5,0xd6,0xd7,
754     0xd8,0xd9,0xda,0xdb, 0xdc,0xdd,0xde,0xdf,
755     0xe0,0xe1,0xe2,0xe3, 0xe4,0xe5,0xe6,0xe7,
756     0xe8,0xe9,0xea,0xeb, 0xec,0xed,0xee,0xef,
757     0xf0,0xf1,0xf2,0xf3, 0xf4,0xf5,0xf6,0xf7,
758     0xf8,0xf9,0xfa,0xfb, 0xfc,0xfd,0xfe,0xff,
759 };
760 
761 
762 /* Like strcmp, but with the appropriate tweaks for file names.
763    Under OS/2, filenames are case-insensitive but case-preserving, and
764    both \ and / are path element separators.  */
765 int
fncmp(const char * n1,const char * n2)766 fncmp (const char *n1, const char *n2)
767 {
768     char fn1[MAXNAMLEN], fn2[MAXNAMLEN];
769 
770     strcpy (fn1, n1); _fnslashify(fn1);
771     strcpy (fn2, n2); _fnslashify(fn2);
772 
773     return _fncmp ((unsigned char *) fn1, (unsigned char *) fn2);
774 }
775 
776 
777 /* Fold characters in FILENAME to their canonical forms.
778    If FOLD_FN_CHAR is not #defined, the system provides a default
779    definition for this.  */
780 void
fnfold(char * filename)781 fnfold (char *filename)
782 {
783     while (*filename)
784     {
785         *filename = FOLD_FN_CHAR (*filename);
786 	filename++;
787     }
788 }
789