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