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 /* 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 * Open a file and die if it fails 186 */ 187 FILE * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 * 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 * 740 get_homedir () 741 { 742 return getenv ("HOME"); 743 } 744 745 /* See cvs.h for description. */ 746 void 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 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