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
copy_file(from,to)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
isdir(file)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
islink(file)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
isfile(file)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
isreadable(file)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
iswritable(file)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
isaccessible(file,mode)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 *
open_file(name,mode)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
make_directory(name)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
make_directories(name)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
mkdir_if_needed(name)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
xchmod(fname,writable)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
readlink(char * path,char * buf,int buf_size)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
unlink_file(f)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
unlink_file_dir(f)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
deep_remove_dir(path)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
rename_file(from,to)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
block_read(fd,buf,nchars)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
xcmp(file1,file2)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
fncmp(const char * n1,const char * n2)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
fnfold(char * filename)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 *
cvs_temp_name()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
isabsolute(filename)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 *
last_component(char * path)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 *
get_homedir()740 get_homedir ()
741 {
742 return getenv ("HOME");
743 }
744
745 /* See cvs.h for description. */
746 void
expand_wild(argc,argv,pargc,pargv)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
os2_chdir(const char * Dir)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