xref: /openbsd-src/gnu/usr.bin/cvs/os2/filesubr.c (revision 13571821e83933f3c1d7fd1ab5ff9cd54f0eea7f)
1 /* filesubr.c --- subroutines for dealing with files under OS/2
2    Jim Blandy <jimb@cyclic.com> and Karl Fogel <kfogel@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    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
19 
20 /* These functions were moved out of subr.c because they need different
21    definitions under operating systems (like, say, Windows NT) with different
22    file system semantics.  */
23 
24 #include <io.h>
25 
26 #include "cvs.h"
27 
28 #ifndef lint
29 static const char rcsid[] = "$CVSid:$";
30 USE(rcsid);
31 #endif
32 
33 /*
34  * I don't know of a convenient way to test this at configure time, or else
35  * I'd certainly do it there.  -JimB
36  */
37 #if defined(NeXT)
38 #define LOSING_TMPNAM_FUNCTION
39 #endif
40 
41 static int deep_remove_dir PROTO((const char *path));
42 
43 /*
44  * Copies "from" to "to".
45  */
46 void
47 copy_file (from, to)
48     const char *from;
49     const char *to;
50 {
51     struct stat sb;
52     struct utimbuf t;
53     int fdin, fdout;
54 
55     if (trace)
56 #ifdef SERVER_SUPPORT
57 	(void) fprintf (stderr, "%c-> copy(%s,%s)\n",
58 			(server_active) ? 'S' : ' ', from, to);
59 #else
60 	(void) fprintf (stderr, "-> copy(%s,%s)\n", from, to);
61 #endif
62     if (noexec)
63 	return;
64 
65     if ((fdin = open (from, O_RDONLY | O_BINARY)) < 0)
66 	error (1, errno, "cannot open %s for copying", from);
67     if (fstat (fdin, &sb) < 0)
68 	error (1, errno, "cannot fstat %s", from);
69     if ((fdout = open (to, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY,
70 					   (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 #ifdef HAVE_FSYNC
97 	if (fsync (fdout))
98 	    error (1, errno, "cannot fsync file %s after copying", to);
99 #endif
100     }
101 
102     if (close (fdin) < 0)
103 	error (0, errno, "cannot close %s", from);
104     if (close (fdout) < 0)
105 	error (1, errno, "cannot close %s", to);
106 
107     /* now, set the times for the copied file to match those of the original */
108     memset ((char *) &t, 0, sizeof (t));
109     t.actime = sb.st_atime;
110     t.modtime = sb.st_mtime;
111     (void) utime (to, &t);
112 }
113 
114 /*
115  * link a file, if possible.  Warning: the Windows NT version of this
116  * function just copies the file, so only use this function in ways
117  * that can deal with either a link or a copy.
118  */
119 int
120 link_file (from, to)
121     const char *from;
122     const char *to;
123 {
124     copy_file (from, to);
125     return 0;
126 }
127 
128 /* FIXME-krp: these functions would benefit from caching the char * &
129    stat buf.  */
130 
131 /*
132  * Returns non-zero if the argument file is a directory, or is a symbolic
133  * link which points to a directory.
134  */
135 int
136 isdir (file)
137     const char *file;
138 {
139     struct stat sb;
140 
141     if (stat (file, &sb) < 0)
142 	return (0);
143     return (S_ISDIR (sb.st_mode));
144 }
145 
146 /*
147  * Returns non-zero if the argument file is a symbolic link.
148  */
149 int
150 islink (file)
151     const char *file;
152 {
153 #ifdef S_ISLNK
154     struct stat sb;
155 
156     if (lstat (file, &sb) < 0)
157 	return (0);
158     return (S_ISLNK (sb.st_mode));
159 #else
160     return (0);
161 #endif
162 }
163 
164 /*
165  * Returns non-zero if the argument file exists.
166  */
167 int
168 isfile (file)
169     const char *file;
170 {
171     struct stat sb;
172 
173     if (stat (file, &sb) < 0)
174 	return (0);
175     return (1);
176 }
177 
178 /*
179  * Returns non-zero if the argument file is readable.
180  * XXX - must be careful if "cvs" is ever made setuid!
181  */
182 int
183 isreadable (file)
184     const char *file;
185 {
186     return (access (file, R_OK) != -1);
187 }
188 
189 /*
190  * Returns non-zero if the argument file is writable
191  * XXX - muct be careful if "cvs" is ever made setuid!
192  */
193 int
194 iswritable (file)
195     const char *file;
196 {
197     return (access (file, W_OK) != -1);
198 }
199 
200 /*
201  * Returns non-zero if the argument file is accessable according to
202  * mode.  If compiled with SETXID_SUPPORT also works if cvs has setxid
203  * bits set.
204  */
205 int
206 isaccessible (file, mode)
207     const char *file;
208     const int mode;
209 {
210     return access(file, mode) == 0;
211 }
212 
213 
214 /*
215  * Open a file and die if it fails
216  */
217 FILE *
218 open_file (name, mode)
219     const char *name;
220     const char *mode;
221 {
222     FILE *fp;
223 
224     if ((fp = fopen (name, mode)) == NULL)
225 	error (1, errno, "cannot open %s", name);
226     return (fp);
227 }
228 
229 /*
230  * Make a directory and die if it fails
231  */
232 void
233 make_directory (name)
234     const char *name;
235 {
236     struct stat buf;
237 
238     if (stat (name, &buf) == 0 && (!S_ISDIR (buf.st_mode)))
239 	    error (0, 0, "%s already exists but is not a directory", name);
240     if (!noexec && mkdir (name) < 0)
241 	error (1, errno, "cannot make directory %s", name);
242 }
243 
244 /*
245  * Make a path to the argument directory, printing a message if something
246  * goes wrong.
247  */
248 void
249 make_directories (name)
250     const char *name;
251 {
252     char *cp;
253 
254     if (noexec)
255 	return;
256 
257     if (mkdir (name) == 0 || errno == EACCESS)
258 	return;
259     if (! existence_error (errno))
260     {
261 	error (0, errno, "cannot make path to %s", name);
262 	return;
263     }
264     if ((cp = strrchr (name, '/')) == NULL)
265 	return;
266     *cp = '\0';
267     make_directories (name);
268     *cp++ = '/';
269     if (*cp == '\0')
270 	return;
271     (void) mkdir (name);
272 }
273 
274 /*
275  * Change the mode of a file, either adding write permissions, or removing
276  * all write permissions.  Adding write permissions honors the current umask
277  * setting.
278  */
279 void
280 xchmod (fname, writable)
281     char *fname;
282     int writable;
283 {
284     char *attrib_cmd;
285     char *attrib_option;
286     char *whole_cmd;
287     char *p;
288     char *q;
289 
290     if (!isfile (fname))
291 	return ENOENT;
292 
293     attrib_cmd = "attrib "; /* No, really? */
294 
295     if (writable)
296         attrib_option = "-r ";  /* make writeable */
297     else
298         attrib_option = "+r ";  /* make read-only */
299 
300     whole_cmd = xmalloc (strlen (attrib_cmd)
301                          + strlen (attrib_option)
302                          + strlen (fname)
303                          + 1);
304 
305     strcpy (whole_cmd, attrib_cmd);
306     strcat (whole_cmd, attrib_option);
307 
308     /* Copy fname to the end of whole_cmd, translating / to \.
309 	   Attrib doesn't take / but many parts of CVS rely
310        on being able to use it.  */
311     p = whole_cmd + strlen (whole_cmd);
312     q = fname;
313     while (*q)
314     {
315 	if (*q == '/')
316 	    *p++ = '\\';
317 	else
318 	    *p++ = *q;
319 	++q;
320     }
321     *p = '\0';
322 
323     system (whole_cmd);
324     free (whole_cmd);
325 }
326 
327 
328 /* Read the value of a symbolic link.
329    Under OS/2, this function always returns EINVAL.  */
330 int
331 readlink (char *path, char *buf, int buf_size)
332 {
333     errno = EINVAL;
334     return -1;
335 }
336 
337 /*
338  * unlink a file, if possible.
339  */
340 int
341 unlink_file (f)
342     const char *f;
343 {
344     if (trace)
345 #ifdef SERVER_SUPPORT
346 	(void) fprintf (stderr, "%c-> unlink(%s)\n",
347 			(server_active) ? 'S' : ' ', f);
348 #else
349 	(void) fprintf (stderr, "-> unlink(%s)\n", f);
350 #endif
351     if (noexec)
352 	return (0);
353 
354    /* Win32 unlink is stupid - it fails if the file is read-only.
355     * OS/2 is similarly stupid.  It does have a remove() function,
356     * but the documentation does not make clear why remove() is or
357     * isn't preferable to unlink().  I'll use unlink() because the
358     * name is closer to our interface, what the heck.  Also, we know
359     * unlink()'s error code when trying to remove a directory.
360     */
361     xchmod (f, 1);
362     return (unlink (f));
363 }
364 
365 /*
366  * Unlink a file or dir, if possible.  If it is a directory do a deep
367  * removal of all of the files in the directory.  Return -1 on error
368  * (in which case errno is set).
369  */
370 int
371 unlink_file_dir (f)
372     const char *f;
373 {
374     if (trace)
375 #ifdef SERVER_SUPPORT
376 	(void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n",
377 			(server_active) ? 'S' : ' ', f);
378 #else
379 	(void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
380 #endif
381     if (noexec)
382 	return (0);
383 
384     if (unlink_file (f) != 0)
385     {
386 	/* under OS/2, unlink returns EACCESS if the path
387 	   is a directory.  */
388         if (errno == EACCESS)
389                 return deep_remove_dir (f);
390         else
391 		/* The file wasn't a directory and some other
392 		 * error occured
393 		 */
394                 return -1;
395     }
396     /* We were able to remove the file from the disk */
397     return 0;
398 }
399 
400 /* Remove a directory and everything it contains.  Returns 0 for
401  * success, -1 for failure (in which case errno is set).
402  */
403 
404 static int
405 deep_remove_dir (path)
406     const char *path;
407 {
408     DIR		  *dirp;
409     struct dirent *dp;
410     char	   buf[PATH_MAX];
411 
412     if ( rmdir (path) != 0 && errno == EACCESS )
413     {
414 	if ((dirp = opendir (path)) == NULL)
415 	    /* If unable to open the directory return
416 	     * an error
417 	     */
418 	    return -1;
419 
420 	while ((dp = readdir (dirp)) != NULL)
421 	{
422 	    if (strcmp (dp->d_name, ".") == 0 ||
423 			strcmp (dp->d_name, "..") == 0)
424 		continue;
425 
426 	    sprintf (buf, "%s/%s", path, dp->d_name);
427 
428 	    if (unlink_file (buf) != 0 )
429 	    {
430 		if (errno == EACCES)
431 		{
432 		    if (deep_remove_dir (buf))
433 		    {
434 			closedir (dirp);
435 			return -1;
436 		    }
437 		}
438 		else
439 		{
440 		    /* buf isn't a directory, or there are
441 		     * some sort of permision problems
442 		     */
443 		    closedir (dirp);
444 		    return -1;
445 		}
446 	    }
447 	}
448 	closedir (dirp);
449 	return rmdir (path);
450     }
451     /* Was able to remove the directory return 0 */
452     return 0;
453 }
454 
455 
456 /*
457  * Rename a file and die if it fails
458  */
459 void
460 rename_file (from, to)
461     const char *from;
462     const char *to;
463 {
464     if (trace)
465 #ifdef SERVER_SUPPORT
466 	(void) fprintf (stderr, "%c-> rename(%s,%s)\n",
467 			(server_active) ? 'S' : ' ', from, to);
468 #else
469 	(void) fprintf (stderr, "-> rename(%s,%s)\n", from, to);
470 #endif
471     if (noexec)
472 	return;
473 
474     unlink_file (to);
475     if (rename (from, to) != 0)
476 	error (1, errno, "cannot rename file %s to %s", from, to);
477 }
478 
479 
480 /* Read NCHARS bytes from descriptor FD into BUF.
481    Return the number of characters successfully read.
482    The number returned is always NCHARS unless end-of-file or error.  */
483 static size_t
484 block_read (fd, buf, nchars)
485     int fd;
486     char *buf;
487     size_t nchars;
488 {
489     char *bp = buf;
490     size_t nread;
491 
492     do
493     {
494 	nread = read (fd, bp, nchars);
495 	if (nread == (size_t)-1)
496 	{
497 #ifdef EINTR
498 	    if (errno == EINTR)
499 		continue;
500 #endif
501 	    return (size_t)-1;
502 	}
503 
504 	if (nread == 0)
505 	    break;
506 
507 	bp += nread;
508 	nchars -= nread;
509     } while (nchars != 0);
510 
511     return bp - buf;
512 }
513 
514 
515 /*
516  * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
517  */
518 int
519 xcmp (file1, file2)
520     const char *file1;
521     const char *file2;
522 {
523     char *buf1, *buf2;
524     struct stat sb1, sb2;
525     int fd1, fd2;
526     int ret;
527 
528     if ((fd1 = open (file1, O_RDONLY | O_BINARY)) < 0)
529 	error (1, errno, "cannot open file %s for comparing", file1);
530     if ((fd2 = open (file2, O_RDONLY | O_BINARY)) < 0)
531 	error (1, errno, "cannot open file %s for comparing", file2);
532     if (fstat (fd1, &sb1) < 0)
533 	error (1, errno, "cannot fstat %s", file1);
534     if (fstat (fd2, &sb2) < 0)
535 	error (1, errno, "cannot fstat %s", file2);
536 
537     /* A generic file compare routine might compare st_dev & st_ino here
538        to see if the two files being compared are actually the same file.
539        But that won't happen in CVS, so we won't bother. */
540 
541     if (sb1.st_size != sb2.st_size)
542 	ret = 1;
543     else if (sb1.st_size == 0)
544 	ret = 0;
545     else
546     {
547 	/* FIXME: compute the optimal buffer size by computing the least
548 	   common multiple of the files st_blocks field */
549 	size_t buf_size = 8 * 1024;
550 	size_t read1;
551 	size_t read2;
552 
553 	buf1 = xmalloc (buf_size);
554 	buf2 = xmalloc (buf_size);
555 
556 	do
557 	{
558 	    read1 = block_read (fd1, buf1, buf_size);
559 	    if (read1 == (size_t)-1)
560 		error (1, errno, "cannot read file %s for comparing", file1);
561 
562 	    read2 = block_read (fd2, buf2, buf_size);
563 	    if (read2 == (size_t)-1)
564 		error (1, errno, "cannot read file %s for comparing", file2);
565 
566 	    /* assert (read1 == read2); */
567 
568 	    ret = memcmp(buf1, buf2, read1);
569 	} while (ret == 0 && read1 == buf_size);
570 
571 	free (buf1);
572 	free (buf2);
573     }
574 
575     (void) close (fd1);
576     (void) close (fd2);
577     return (ret);
578 }
579 
580 
581 /* The equivalence class mapping for filenames.
582    OS/2 filenames are case-insensitive, but case-preserving.  Both /
583    and \ are path element separators.
584    Thus, this table maps both upper and lower case to lower case, and
585    both / and \ to /.
586 
587    Much thanks to Jim Blandy, who already invented this wheel in the
588    Windows NT port. */
589 
590 #if 0
591 main ()
592 {
593   int c;
594 
595   for (c = 0; c < 256; c++)
596     {
597       int t;
598 
599       if (c == '\\')
600         t = '/';
601       else
602         t = tolower (c);
603 
604       if ((c & 0x7) == 0x0)
605          printf ("    ");
606       printf ("0x%02x,", t);
607       if ((c & 0x7) == 0x7)
608          putchar ('\n');
609       else if ((c & 0x7) == 0x3)
610          putchar (' ');
611     }
612 }
613 #endif
614 
615 
616 unsigned char
617 OS2_filename_classes[] =
618 {
619     0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,
620     0x08,0x09,0x0a,0x0b, 0x0c,0x0d,0x0e,0x0f,
621     0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17,
622     0x18,0x19,0x1a,0x1b, 0x1c,0x1d,0x1e,0x1f,
623     0x20,0x21,0x22,0x23, 0x24,0x25,0x26,0x27,
624     0x28,0x29,0x2a,0x2b, 0x2c,0x2d,0x2e,0x2f,
625     0x30,0x31,0x32,0x33, 0x34,0x35,0x36,0x37,
626     0x38,0x39,0x3a,0x3b, 0x3c,0x3d,0x3e,0x3f,
627     0x40,0x61,0x62,0x63, 0x64,0x65,0x66,0x67,
628     0x68,0x69,0x6a,0x6b, 0x6c,0x6d,0x6e,0x6f,
629     0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77,
630     0x78,0x79,0x7a,0x5b, 0x2f,0x5d,0x5e,0x5f,
631     0x60,0x61,0x62,0x63, 0x64,0x65,0x66,0x67,
632     0x68,0x69,0x6a,0x6b, 0x6c,0x6d,0x6e,0x6f,
633     0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77,
634     0x78,0x79,0x7a,0x7b, 0x7c,0x7d,0x7e,0x7f,
635     0x80,0x81,0x82,0x83, 0x84,0x85,0x86,0x87,
636     0x88,0x89,0x8a,0x8b, 0x8c,0x8d,0x8e,0x8f,
637     0x90,0x91,0x92,0x93, 0x94,0x95,0x96,0x97,
638     0x98,0x99,0x9a,0x9b, 0x9c,0x9d,0x9e,0x9f,
639     0xa0,0xa1,0xa2,0xa3, 0xa4,0xa5,0xa6,0xa7,
640     0xa8,0xa9,0xaa,0xab, 0xac,0xad,0xae,0xaf,
641     0xb0,0xb1,0xb2,0xb3, 0xb4,0xb5,0xb6,0xb7,
642     0xb8,0xb9,0xba,0xbb, 0xbc,0xbd,0xbe,0xbf,
643     0xc0,0xc1,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7,
644     0xc8,0xc9,0xca,0xcb, 0xcc,0xcd,0xce,0xcf,
645     0xd0,0xd1,0xd2,0xd3, 0xd4,0xd5,0xd6,0xd7,
646     0xd8,0xd9,0xda,0xdb, 0xdc,0xdd,0xde,0xdf,
647     0xe0,0xe1,0xe2,0xe3, 0xe4,0xe5,0xe6,0xe7,
648     0xe8,0xe9,0xea,0xeb, 0xec,0xed,0xee,0xef,
649     0xf0,0xf1,0xf2,0xf3, 0xf4,0xf5,0xf6,0xf7,
650     0xf8,0xf9,0xfa,0xfb, 0xfc,0xfd,0xfe,0xff,
651 };
652 
653 /* Like strcmp, but with the appropriate tweaks for file names.
654    Under OS/2, filenames are case-insensitive but case-preserving, and
655    both \ and / are path element separators.  */
656 int
657 fncmp (const char *n1, const char *n2)
658 {
659     while (*n1 && *n2
660            && (OS2_filename_classes[(unsigned char) *n1]
661 	       == OS2_filename_classes[(unsigned char) *n2]))
662         n1++, n2++;
663     return (OS2_filename_classes[(unsigned char) *n1]
664             - OS2_filename_classes[(unsigned char) *n1]);
665 }
666 
667 /* Fold characters in FILENAME to their canonical forms.
668    If FOLD_FN_CHAR is not #defined, the system provides a default
669    definition for this.  */
670 void
671 fnfold (char *filename)
672 {
673     while (*filename)
674     {
675         *filename = FOLD_FN_CHAR (*filename);
676 	filename++;
677     }
678 }
679 
680 
681 /* Return non-zero iff FILENAME is absolute.
682    Trivial under Unix, but more complicated under other systems.  */
683 int
684 isabsolute (filename)
685     const char *filename;
686 {
687     return (ISDIRSEP (filename[0])
688             || (filename[0] != '\0'
689                 && filename[1] == ':'
690                 && ISDIRSEP (filename[2])));
691 }
692 
693 /* Return a pointer into PATH's last component.  */
694 char *
695 last_component (char *path)
696 {
697     char *scan;
698     char *last = 0;
699 
700     for (scan = path; *scan; scan++)
701         if (ISDIRSEP (*scan))
702 	    last = scan;
703 
704     if (last)
705         return last + 1;
706     else
707         return path;
708 }
709 
710 
711 /* Read data from INFILE, and copy it to OUTFILE.
712    Open INFILE using INFLAGS, and OUTFILE using OUTFLAGS.
713    This is useful for converting between CRLF and LF line formats.  */
714 void
715 convert_file (char *infile,  int inflags,
716 	      char *outfile, int outflags)
717 {
718     int infd, outfd;
719     char buf[8192];
720     int len;
721 
722     if ((infd = open (infile, inflags, S_IREAD | S_IWRITE)) < 0)
723         error (1, errno, "couldn't read %s", infile);
724     if ((outfd = open (outfile, outflags, S_IREAD | S_IWRITE)) < 0)
725         error (1, errno, "couldn't write %s", outfile);
726 
727     while ((len = read (infd, buf, sizeof (buf))) > 0)
728         if (write (outfd, buf, len) < 0)
729 	    error (1, errno, "error writing %s", outfile);
730     if (len < 0)
731         error (1, errno, "error reading %s", infile);
732 
733     if (close (outfd) < 0)
734         error (0, errno, "warning: couldn't close %s", outfile);
735     if (close (infd) < 0)
736         error (0, errno, "warning: couldn't close %s", infile);
737 }
738