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
25static int deep_remove_dir PROTO((const char *path));
26
27/*
28 * Copies "from" to "to".
29 */
30void
31copy_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 */
105int
106isdir (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 */
119int
120islink (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 */
137int
138isfile (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 */
152int
153isreadable (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 */
163int
164iswritable (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 */
175int
176isaccessible (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 */
187FILE *
188open_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 */
202void
203make_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 */
218void
219make_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.  */
247int
248mkdir_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 */
284void
285xchmod (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.  */
339int
340readlink (char *path, char *buf, int buf_size)
341{
342    errno = EINVAL;
343    return -1;
344}
345
346/*
347 * unlink a file, if possible.
348 */
349int
350unlink_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 */
380int
381unlink_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
414static int
415deep_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 */
469void
470rename_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.  */
493static size_t
494block_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 */
528int
529xcmp (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
601main ()
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
626unsigned char
627OS2_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.  */
666int
667fncmp (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.  */
680void
681fnfold (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.  */
694char *
695cvs_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.  */
709int
710isabsolute (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.  */
720char *
721last_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).  */
739char *
740get_homedir ()
741{
742    return getenv ("HOME");
743}
744
745/* See cvs.h for description.  */
746void
747expand_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
879int
880os2_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