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