xref: /openbsd/gnu/usr.bin/cvs/os2/filesubr.c (revision d89ec533)
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 /* 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 <io.h>
21 
22 #include "os2inc.h"
23 #include "cvs.h"
24 
25 static int deep_remove_dir PROTO((const char *path));
26 
27 /*
28  * Copies "from" to "to".
29  */
30 void
31 copy_file (from, to)
32     const char *from;
33     const char *to;
34 {
35     struct stat sb;
36     struct utimbuf t;
37     int fdin, fdout;
38 
39     if (trace)
40 #ifdef SERVER_SUPPORT
41 	(void) fprintf (stderr, "%c-> copy(%s,%s)\n",
42 			(server_active) ? 'S' : ' ', from, to);
43 #else
44 	(void) fprintf (stderr, "-> copy(%s,%s)\n", from, to);
45 #endif
46     if (noexec)
47 	return;
48 
49     if ((fdin = open (from, O_RDONLY | O_BINARY)) < 0)
50 	error (1, errno, "cannot open %s for copying", from);
51     if (fstat (fdin, &sb) < 0)
52 	error (1, errno, "cannot fstat %s", from);
53     if ((fdout = open (to, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY,
54 					   (int) sb.st_mode & 07777)) < 0)
55 	error (1, errno, "cannot create %s for copying", to);
56     if (sb.st_size > 0)
57     {
58 	char buf[BUFSIZ];
59 	int n;
60 
61 	for (;;)
62 	{
63 	    n = read (fdin, buf, sizeof(buf));
64 	    if (n == -1)
65 	    {
66 #ifdef EINTR
67 		if (errno == EINTR)
68 		    continue;
69 #endif
70 		error (1, errno, "cannot read file %s for copying", from);
71 	    }
72             else if (n == 0)
73 		break;
74 
75 	    if (write(fdout, buf, n) != n) {
76 		error (1, errno, "cannot write file %s for copying", to);
77 	    }
78 	}
79 
80 #ifdef HAVE_FSYNC
81 	if (fsync (fdout))
82 	    error (1, errno, "cannot fsync file %s after copying", to);
83 #endif
84     }
85 
86     if (close (fdin) < 0)
87 	error (0, errno, "cannot close %s", from);
88     if (close (fdout) < 0)
89 	error (1, errno, "cannot close %s", to);
90 
91     /* now, set the times for the copied file to match those of the original */
92     memset ((char *) &t, 0, sizeof (t));
93     t.actime = sb.st_atime;
94     t.modtime = sb.st_mtime;
95     (void) utime ((char *)to, &t);
96 }
97 
98 /* FIXME-krp: these functions would benefit from caching the char * &
99    stat buf.  */
100 
101 /*
102  * Returns non-zero if the argument file is a directory, or is a symbolic
103  * link which points to a directory.
104  */
105 int
106 isdir (file)
107     const char *file;
108 {
109     struct stat sb;
110 
111     if (stat (file, &sb) < 0)
112 	return (0);
113     return (S_ISDIR (sb.st_mode));
114 }
115 
116 /*
117  * Returns non-zero if the argument file is a symbolic link.
118  */
119 int
120 islink (file)
121     const char *file;
122 {
123 #ifdef S_ISLNK
124     struct stat sb;
125 
126     if (lstat (file, &sb) < 0)
127 	return (0);
128     return (S_ISLNK (sb.st_mode));
129 #else
130     return (0);
131 #endif
132 }
133 
134 /*
135  * Returns non-zero if the argument file exists.
136  */
137 int
138 isfile (file)
139     const char *file;
140 {
141     struct stat sb;
142 
143     if (stat (file, &sb) < 0)
144 	return (0);
145     return (1);
146 }
147 
148 /*
149  * Returns non-zero if the argument file is readable.
150  * XXX - must be careful if "cvs" is ever made setuid!
151  */
152 int
153 isreadable (file)
154     const char *file;
155 {
156     return (access (file, R_OK) != -1);
157 }
158 
159 /*
160  * Returns non-zero if the argument file is writable
161  * XXX - muct be careful if "cvs" is ever made setuid!
162  */
163 int
164 iswritable (file)
165     const char *file;
166 {
167     return (access (file, W_OK) != -1);
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
176 isaccessible (file, mode)
177     const char *file;
178     const int mode;
179 {
180     return access(file, mode) == 0;
181 }
182 
183 
184 /*
185  * Open a file and die if it fails
186  */
187 FILE *
188 open_file (name, mode)
189     const char *name;
190     const char *mode;
191 {
192     FILE *fp;
193 
194     if ((fp = fopen (name, mode)) == NULL)
195 	error (1, errno, "cannot open %s", name);
196     return (fp);
197 }
198 
199 /*
200  * Make a directory and die if it fails
201  */
202 void
203 make_directory (name)
204     const char *name;
205 {
206     struct stat buf;
207 
208     if (stat (name, &buf) == 0 && (!S_ISDIR (buf.st_mode)))
209 	    error (0, 0, "%s already exists but is not a directory", name);
210     if (!noexec && mkdir ((char *)name) < 0)
211 	error (1, errno, "cannot make directory %s", name);
212 }
213 
214 /*
215  * Make a path to the argument directory, printing a message if something
216  * goes wrong.
217  */
218 void
219 make_directories (name)
220     const char *name;
221 {
222     char *cp;
223 
224     if (noexec)
225 	return;
226 
227     if (mkdir ((char *)name) == 0 || errno == EACCES)
228 	return;
229     if (! existence_error (errno))
230     {
231 	error (0, errno, "cannot make path to %s", name);
232 	return;
233     }
234     if ((cp = strrchr (name, '/')) == NULL)
235 	return;
236     *cp = '\0';
237     make_directories (name);
238     *cp++ = '/';
239     if (*cp == '\0')
240 	return;
241     (void) mkdir ((char *)name);
242 }
243 
244 /* Create directory NAME if it does not already exist; fatal error for
245    other errors.  Returns 0 if directory was created; 1 if it already
246    existed.  */
247 int
248 mkdir_if_needed (name)
249     char *name;
250 {
251     if (mkdir (name) < 0)
252     {
253 	/* Now, let me get this straight.  In IBM C/C++
254 	   under OS/2, the error string for EEXIST is:
255 
256 	       "The file already exists",
257 
258            and the error string for EACCES is:
259 
260 	       "The file or directory specified is read-only".
261 
262            Nonetheless, mkdir() will set EACCES if the
263 	   directory *exists*, according both to the
264 	   documentation and its actual behavior.
265 
266 	   I'm sure that this made sense, to someone,
267 	   somewhere, sometime.  Just not me, here, now.  */
268 	if (errno != EEXIST
269 #ifdef EACCES
270             && errno != EACCES
271 #endif
272 	    )
273 	    error (1, errno, "cannot make directory %s", name);
274 	return 1;
275     }
276     return 0;
277 }
278 
279 /*
280  * Change the mode of a file, either adding write permissions, or removing
281  * all write permissions.  Adding write permissions honors the current umask
282  * setting.
283  */
284 void
285 xchmod (fname, writable)
286     char *fname;
287     int writable;
288 {
289     char *attrib_cmd;
290     char *attrib_option;
291     char *whole_cmd;
292     char *p;
293     char *q;
294 
295     if (!isfile (fname))
296     {
297 	error (0, 0, "cannot change mode of file %s; it does not exist",
298 	       fname);
299 	return;
300     }
301 
302     attrib_cmd = "attrib "; /* No, really? */
303 
304     if (writable)
305         attrib_option = "-r ";  /* make writeable */
306     else
307         attrib_option = "+r ";  /* make read-only */
308 
309     whole_cmd = xmalloc (strlen (attrib_cmd)
310                          + strlen (attrib_option)
311                          + strlen (fname)
312                          + 1);
313 
314     strcpy (whole_cmd, attrib_cmd);
315     strcat (whole_cmd, attrib_option);
316 
317     /* Copy fname to the end of whole_cmd, translating / to \.
318 	   Attrib doesn't take / but many parts of CVS rely
319        on being able to use it.  */
320     p = whole_cmd + strlen (whole_cmd);
321     q = fname;
322     while (*q)
323     {
324 	if (*q == '/')
325 	    *p++ = '\\';
326 	else
327 	    *p++ = *q;
328 	++q;
329     }
330     *p = '\0';
331 
332     system (whole_cmd);
333     free (whole_cmd);
334 }
335 
336 
337 /* Read the value of a symbolic link.
338    Under OS/2, this function always returns EINVAL.  */
339 int
340 readlink (char *path, char *buf, int buf_size)
341 {
342     errno = EINVAL;
343     return -1;
344 }
345 
346 /*
347  * unlink a file, if possible.
348  */
349 int
350 unlink_file (f)
351     const char *f;
352 {
353     if (trace)
354 #ifdef SERVER_SUPPORT
355 	(void) fprintf (stderr, "%c-> unlink(%s)\n",
356 			(server_active) ? 'S' : ' ', f);
357 #else
358 	(void) fprintf (stderr, "-> unlink(%s)\n", f);
359 #endif
360     if (noexec)
361 	return (0);
362 
363    /* Win32 unlink is stupid - it fails if the file is read-only.
364     * OS/2 is similarly stupid.  It does have a remove() function,
365     * but the documentation does not make clear why remove() is or
366     * isn't preferable to unlink().  I'll use unlink() because the
367     * name is closer to our interface, what the heck.  Also, we know
368     * unlink()'s error code when trying to remove a directory.
369     */
370     if (isfile (f))
371 	xchmod ((char *)f, 1);
372     return (unlink (f));
373 }
374 
375 /*
376  * Unlink a file or dir, if possible.  If it is a directory do a deep
377  * removal of all of the files in the directory.  Return -1 on error
378  * (in which case errno is set).
379  */
380 int
381 unlink_file_dir (f)
382     const char *f;
383 {
384     if (trace)
385 #ifdef SERVER_SUPPORT
386 	(void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n",
387 			(server_active) ? 'S' : ' ', f);
388 #else
389 	(void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
390 #endif
391     if (noexec)
392 	return (0);
393 
394     if (unlink_file (f) != 0)
395     {
396         /* under OS/2, unlink returns EACCES if the path
397 	   is a directory.  */
398         if (errno == EACCES)
399                 return deep_remove_dir (f);
400         else
401 		/* The file wasn't a directory and some other
402 		 * error occured
403 		 */
404                 return -1;
405     }
406     /* We were able to remove the file from the disk */
407     return 0;
408 }
409 
410 /* Remove a directory and everything it contains.  Returns 0 for
411  * success, -1 for failure (in which case errno is set).
412  */
413 
414 static int
415 deep_remove_dir (path)
416     const char *path;
417 {
418     DIR		  *dirp;
419     struct dirent *dp;
420     char	   buf[PATH_MAX];
421 
422     if (rmdir ((char *)path) != 0 && errno == EACCES)
423     {
424 	if ((dirp = opendir ((char *)path)) == NULL)
425 	    /* If unable to open the directory return
426 	     * an error
427 	     */
428 	    return -1;
429 
430 	while ((dp = readdir (dirp)) != NULL)
431 	{
432 	    if (strcmp (dp->d_name, ".") == 0 ||
433 			strcmp (dp->d_name, "..") == 0)
434 		continue;
435 
436 	    sprintf (buf, "%s/%s", path, dp->d_name);
437 
438 	    if (unlink_file (buf) != 0 )
439 	    {
440 		if (errno == EACCES)
441 		{
442 		    if (deep_remove_dir (buf))
443 		    {
444 			closedir (dirp);
445 			return -1;
446 		    }
447 		}
448 		else
449 		{
450 		    /* buf isn't a directory, or there are
451 		     * some sort of permision problems
452 		     */
453 		    closedir (dirp);
454 		    return -1;
455 		}
456 	    }
457 	}
458 	closedir (dirp);
459 	return rmdir ((char *)path);
460     }
461     /* Was able to remove the directory return 0 */
462     return 0;
463 }
464 
465 
466 /*
467  * Rename a file and die if it fails
468  */
469 void
470 rename_file (from, to)
471     const char *from;
472     const char *to;
473 {
474     if (trace)
475 #ifdef SERVER_SUPPORT
476 	(void) fprintf (stderr, "%c-> rename(%s,%s)\n",
477 			(server_active) ? 'S' : ' ', from, to);
478 #else
479 	(void) fprintf (stderr, "-> rename(%s,%s)\n", from, to);
480 #endif
481     if (noexec)
482 	return;
483 
484     unlink_file (to);
485     if (rename (from, to) != 0)
486 	error (1, errno, "cannot rename file %s to %s", from, to);
487 }
488 
489 
490 /* Read NCHARS bytes from descriptor FD into BUF.
491    Return the number of characters successfully read.
492    The number returned is always NCHARS unless end-of-file or error.  */
493 static size_t
494 block_read (fd, buf, nchars)
495     int fd;
496     char *buf;
497     size_t nchars;
498 {
499     char *bp = buf;
500     size_t nread;
501 
502     do
503     {
504 	nread = read (fd, bp, nchars);
505 	if (nread == (size_t)-1)
506 	{
507 #ifdef EINTR
508 	    if (errno == EINTR)
509 		continue;
510 #endif
511 	    return (size_t)-1;
512 	}
513 
514 	if (nread == 0)
515 	    break;
516 
517 	bp += nread;
518 	nchars -= nread;
519     } while (nchars != 0);
520 
521     return bp - buf;
522 }
523 
524 
525 /*
526  * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
527  */
528 int
529 xcmp (file1, file2)
530     const char *file1;
531     const char *file2;
532 {
533     char *buf1, *buf2;
534     struct stat sb1, sb2;
535     int fd1, fd2;
536     int ret;
537 
538     if ((fd1 = open (file1, O_RDONLY | O_BINARY)) < 0)
539 	error (1, errno, "cannot open file %s for comparing", file1);
540     if ((fd2 = open (file2, O_RDONLY | O_BINARY)) < 0)
541 	error (1, errno, "cannot open file %s for comparing", file2);
542     if (fstat (fd1, &sb1) < 0)
543 	error (1, errno, "cannot fstat %s", file1);
544     if (fstat (fd2, &sb2) < 0)
545 	error (1, errno, "cannot fstat %s", file2);
546 
547     /* A generic file compare routine might compare st_dev & st_ino here
548        to see if the two files being compared are actually the same file.
549        But that won't happen in CVS, so we won't bother. */
550 
551     if (sb1.st_size != sb2.st_size)
552 	ret = 1;
553     else if (sb1.st_size == 0)
554 	ret = 0;
555     else
556     {
557 	/* FIXME: compute the optimal buffer size by computing the least
558 	   common multiple of the files st_blocks field */
559 	size_t buf_size = 8 * 1024;
560 	size_t read1;
561 	size_t read2;
562 
563 	buf1 = xmalloc (buf_size);
564 	buf2 = xmalloc (buf_size);
565 
566 	do
567 	{
568 	    read1 = block_read (fd1, buf1, buf_size);
569 	    if (read1 == (size_t)-1)
570 		error (1, errno, "cannot read file %s for comparing", file1);
571 
572 	    read2 = block_read (fd2, buf2, buf_size);
573 	    if (read2 == (size_t)-1)
574 		error (1, errno, "cannot read file %s for comparing", file2);
575 
576 	    /* assert (read1 == read2); */
577 
578 	    ret = memcmp(buf1, buf2, read1);
579 	} while (ret == 0 && read1 == buf_size);
580 
581 	free (buf1);
582 	free (buf2);
583     }
584 
585     (void) close (fd1);
586     (void) close (fd2);
587     return (ret);
588 }
589 
590 
591 /* The equivalence class mapping for filenames.
592    OS/2 filenames are case-insensitive, but case-preserving.  Both /
593    and \ are path element separators.
594    Thus, this table maps both upper and lower case to lower case, and
595    both / and \ to /.
596 
597    Much thanks to Jim Blandy, who already invented this wheel in the
598    Windows NT port. */
599 
600 #if 0
601 main ()
602 {
603   int c;
604 
605   for (c = 0; c < 256; c++)
606     {
607       int t;
608 
609       if (c == '\\')
610         t = '/';
611       else
612         t = tolower (c);
613 
614       if ((c & 0x7) == 0x0)
615          printf ("    ");
616       printf ("0x%02x,", t);
617       if ((c & 0x7) == 0x7)
618          putchar ('\n');
619       else if ((c & 0x7) == 0x3)
620          putchar (' ');
621     }
622 }
623 #endif
624 
625 
626 unsigned char
627 OS2_filename_classes[] =
628 {
629     0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,
630     0x08,0x09,0x0a,0x0b, 0x0c,0x0d,0x0e,0x0f,
631     0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17,
632     0x18,0x19,0x1a,0x1b, 0x1c,0x1d,0x1e,0x1f,
633     0x20,0x21,0x22,0x23, 0x24,0x25,0x26,0x27,
634     0x28,0x29,0x2a,0x2b, 0x2c,0x2d,0x2e,0x2f,
635     0x30,0x31,0x32,0x33, 0x34,0x35,0x36,0x37,
636     0x38,0x39,0x3a,0x3b, 0x3c,0x3d,0x3e,0x3f,
637     0x40,0x61,0x62,0x63, 0x64,0x65,0x66,0x67,
638     0x68,0x69,0x6a,0x6b, 0x6c,0x6d,0x6e,0x6f,
639     0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77,
640     0x78,0x79,0x7a,0x5b, 0x2f,0x5d,0x5e,0x5f,
641     0x60,0x61,0x62,0x63, 0x64,0x65,0x66,0x67,
642     0x68,0x69,0x6a,0x6b, 0x6c,0x6d,0x6e,0x6f,
643     0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77,
644     0x78,0x79,0x7a,0x7b, 0x7c,0x7d,0x7e,0x7f,
645     0x80,0x81,0x82,0x83, 0x84,0x85,0x86,0x87,
646     0x88,0x89,0x8a,0x8b, 0x8c,0x8d,0x8e,0x8f,
647     0x90,0x91,0x92,0x93, 0x94,0x95,0x96,0x97,
648     0x98,0x99,0x9a,0x9b, 0x9c,0x9d,0x9e,0x9f,
649     0xa0,0xa1,0xa2,0xa3, 0xa4,0xa5,0xa6,0xa7,
650     0xa8,0xa9,0xaa,0xab, 0xac,0xad,0xae,0xaf,
651     0xb0,0xb1,0xb2,0xb3, 0xb4,0xb5,0xb6,0xb7,
652     0xb8,0xb9,0xba,0xbb, 0xbc,0xbd,0xbe,0xbf,
653     0xc0,0xc1,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7,
654     0xc8,0xc9,0xca,0xcb, 0xcc,0xcd,0xce,0xcf,
655     0xd0,0xd1,0xd2,0xd3, 0xd4,0xd5,0xd6,0xd7,
656     0xd8,0xd9,0xda,0xdb, 0xdc,0xdd,0xde,0xdf,
657     0xe0,0xe1,0xe2,0xe3, 0xe4,0xe5,0xe6,0xe7,
658     0xe8,0xe9,0xea,0xeb, 0xec,0xed,0xee,0xef,
659     0xf0,0xf1,0xf2,0xf3, 0xf4,0xf5,0xf6,0xf7,
660     0xf8,0xf9,0xfa,0xfb, 0xfc,0xfd,0xfe,0xff,
661 };
662 
663 /* Like strcmp, but with the appropriate tweaks for file names.
664    Under OS/2, filenames are case-insensitive but case-preserving, and
665    both \ and / are path element separators.  */
666 int
667 fncmp (const char *n1, const char *n2)
668 {
669     while (*n1 && *n2
670            && (OS2_filename_classes[(unsigned char) *n1]
671 	       == OS2_filename_classes[(unsigned char) *n2]))
672         n1++, n2++;
673     return (OS2_filename_classes[(unsigned char) *n1]
674             - OS2_filename_classes[(unsigned char) *n2]);
675 }
676 
677 /* Fold characters in FILENAME to their canonical forms.
678    If FOLD_FN_CHAR is not #defined, the system provides a default
679    definition for this.  */
680 void
681 fnfold (char *filename)
682 {
683     while (*filename)
684     {
685         *filename = FOLD_FN_CHAR (*filename);
686 	filename++;
687     }
688 }
689 
690 
691 /* Generate a unique temporary filename.  Returns a pointer to a newly
692    malloc'd string containing the name.  Returns successfully or not at
693    all.  */
694 char *
695 cvs_temp_name ()
696 {
697     char value[L_tmpnam + 1];
698     char *retval;
699 
700     /* FIXME: Does OS/2 have some equivalent to TMPDIR?  */
701     retval = tmpnam (value);
702     if (retval == NULL)
703 	error (1, errno, "cannot generate temporary filename");
704     return xstrdup (retval);
705 }
706 
707 /* Return non-zero iff FILENAME is absolute.
708    Trivial under Unix, but more complicated under other systems.  */
709 int
710 isabsolute (filename)
711     const char *filename;
712 {
713     return (ISDIRSEP (filename[0])
714             || (filename[0] != '\0'
715                 && filename[1] == ':'
716                 && ISDIRSEP (filename[2])));
717 }
718 
719 /* Return a pointer into PATH's last component.  */
720 char *
721 last_component (char *path)
722 {
723     char *scan;
724     char *last = 0;
725 
726     for (scan = path; *scan; scan++)
727         if (ISDIRSEP (*scan))
728 	    last = scan;
729 
730     if (last && (last != path))
731         return last + 1;
732     else
733         return path;
734 }
735 
736 
737 /* Return the home directory.  Returns a pointer to storage
738    managed by this function or its callees (currently getenv).  */
739 char *
740 get_homedir ()
741 {
742     return getenv ("HOME");
743 }
744 
745 /* See cvs.h for description.  */
746 void
747 expand_wild (argc, argv, pargc, pargv)
748     int argc;
749     char **argv;
750     int *pargc;
751     char ***pargv;
752 {
753     int i;
754     int new_argc;
755     char **new_argv;
756     /* Allocated size of new_argv.  We arrange it so there is always room for
757 	   one more element.  */
758     int max_new_argc;
759 
760     new_argc = 0;
761     /* Add one so this is never zero.  */
762     max_new_argc = argc + 1;
763     new_argv = (char **) xmalloc (max_new_argc * sizeof (char *));
764     for (i = 0; i < argc; ++i)
765     {
766 	HDIR          FindHandle = 0x0001;
767 	FILEFINDBUF3  FindBuffer;
768 	ULONG         FindCount = 1;
769 	APIRET        rc;          /* Return code */
770 #define ALL_FILES (FILE_ARCHIVED|FILE_DIRECTORY|FILE_SYSTEM|FILE_HIDDEN|FILE_READONLY)
771 
772 	/* DosFindFirst, called with a string like 'dir/file' will return
773 	 * *only* the file part. So what we have to do here is to save the
774 	 * directory part, and add it later to the returned filename.
775 	 */
776 
777 	/* Path + name */
778 	char *PathName = argv [i];
779 
780 	/* Path only, including slash */
781 	char *Path = NULL;
782 
783 	/* Name without path */
784 	char *Name = last_component (PathName);
785 
786 	if (Name > PathName)
787 	{
788 	    /* We have a path component, save it */
789 	    Path = xmalloc (Name - PathName + 1);
790 	    memcpy (Path, PathName, Name - PathName);
791 	    Path [Name - PathName] = '\0';
792 	}
793 
794 	rc = DosFindFirst(PathName,     	 /* File pattern */
795 			  &FindHandle,           /* Directory search handle */
796 			  ALL_FILES,             /* Search attribute */
797 			  (PVOID) &FindBuffer,   /* Result buffer */
798 			  sizeof(FindBuffer),    /* Result buffer length */
799 			  &FindCount,            /* Number of entries to find */
800 			  FIL_STANDARD);	 /* Return level 1 file info */
801 
802 	if (rc != 0)
803 	{
804 	    if (rc == ERROR_NO_MORE_FILES)
805 	    {
806 		/* No match.  The file specified didn't contain a wildcard (in which case
807 		   we clearly should return it unchanged), or it contained a wildcard which
808 		   didn't match (in which case it might be better for it to be an error,
809 		   but we don't try to do that).  */
810 		new_argv [new_argc++] = xstrdup (argv[i]);
811 		if (new_argc == max_new_argc)
812 		{
813 		    max_new_argc *= 2;
814 		    new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *));
815 		}
816 	    }
817 	    else
818 	    {
819                 error (1, rc, "cannot find %s", PathName);
820 	    }
821 	}
822 	else
823 	{
824 	    while (1)
825 	    {
826 		/*
827 		 * Don't match ".", "..", and files starting with '.'
828 		 * (unless pattern also starts with '.').  This is
829 		 * (more or less) what standard Unix globbing does.
830 		 */
831 		if ((strcmp(FindBuffer.achName, ".") != 0) &&
832 		    (strcmp(FindBuffer.achName, "..") != 0) &&
833 		    ((argv[i][0] == '.') || (FindBuffer.achName[0] != '.')))
834 		{
835 		    /* Be sure to add the path if needed */
836 		    char *NewArg;
837 		    if (Path)
838 		    {
839 			unsigned Len =
840 			    strlen (Path) + strlen (FindBuffer.achName) + 1;
841 			NewArg = xmalloc (Len);
842 			strcpy (NewArg, Path);
843 			strcat (NewArg, FindBuffer.achName);
844 		    }
845 		    else
846 		    {
847 			NewArg = xstrdup (FindBuffer.achName);
848 		    }
849 		    new_argv [new_argc++] = NewArg;
850 		    if (new_argc == max_new_argc)
851 		    {
852 			max_new_argc *= 2;
853 			new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *));
854 		    }
855 		}
856 
857 		rc = DosFindNext (FindHandle,
858 				  (PVOID) &FindBuffer,
859 				  sizeof(FindBuffer),
860 				  &FindCount);
861 		if (rc == ERROR_NO_MORE_FILES)
862 		    break;
863 		else if (rc != NO_ERROR)
864 		    error (1, rc, "cannot find %s", argv[i]);
865 	    }
866 	    rc = DosFindClose(FindHandle);
867 	    if (rc != 0)
868 		error (1, rc, "cannot close %s", argv[i]);
869 	}
870 	if (Path != NULL)
871 	    free (Path);
872     }
873     *pargc = new_argc;
874     *pargv = new_argv;
875 }
876 
877 /* Change drive and directory to path DIR.  */
878 
879 int
880 os2_chdir (const char *Dir)
881 {
882     /* If the path includes a drive, change the current drive to the one
883        given.  */
884     if (strlen (Dir) >= 2 && Dir [1] == ':')
885     {
886 	/* A drive is given in Dir. Extract the drive from the string, then
887 	 * remove the drive from Dir by incrementing it.
888 	 */
889 	int Drive = Dir [0];
890 	Dir += 2;
891 
892 	/* Check if the given drive is valid, convert to a drive number
893 	 * (A: == 1, B: == 2, etc.). The compare below assumes ascii, but
894 	 * that is not a problem with OS/2.
895 	 */
896 	if (Drive >= 'a' && Drive <= 'z')
897 	{
898 	    Drive -= 'a' - 1;
899 	}
900 	else if (Drive >= 'A' && Drive <= 'Z')
901 	{
902 	    Drive -= 'A' - 1;
903 	}
904 	else
905 	{
906 	    /* An invalid drive letter. Set errno and return an error */
907 	    errno = EACCES;
908 	    return -1;
909 	}
910 
911 	/* We have a valid drive given, so change the drive now */
912 	if (DosSetDefaultDisk (Drive) != 0)
913 	{
914 	    /* We had an error. Assume that the drive does not exist */
915 #ifdef ENODEV
916 	    errno = ENODEV;
917 #else
918 	    /* IBM C/C++ Tools 2.01 seems to lack ENODEV.  */
919 	    errno = ENOENT;
920 #endif
921 	    return -1;
922 	}
923 
924     }
925 
926     /* Now we have a path without a drive left. Make it the current dir */
927     return chdir (Dir);
928 }
929 
930 
931 
932