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