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