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) 745 return last + 1; 746 else 747 return path; 748 } 749 750 751 /* Read data from INFILE, and copy it to OUTFILE. 752 Open INFILE using INFLAGS, and OUTFILE using OUTFLAGS. 753 This is useful for converting between CRLF and LF line formats. */ 754 void 755 convert_file (char *infile, int inflags, 756 char *outfile, int outflags) 757 { 758 int infd, outfd; 759 char buf[8192]; 760 int len; 761 762 if ((infd = open (infile, inflags, S_IREAD | S_IWRITE)) < 0) 763 error (1, errno, "couldn't read %s", infile); 764 if ((outfd = open (outfile, outflags, S_IREAD | S_IWRITE)) < 0) 765 error (1, errno, "couldn't write %s", outfile); 766 767 while ((len = read (infd, buf, sizeof (buf))) > 0) 768 if (write (outfd, buf, len) < 0) 769 error (1, errno, "error writing %s", outfile); 770 if (len < 0) 771 error (1, errno, "error reading %s", infile); 772 773 if (close (outfd) < 0) 774 error (0, errno, "warning: couldn't close %s", outfile); 775 if (close (infd) < 0) 776 error (0, errno, "warning: couldn't close %s", infile); 777 } 778 779 /* Return the home directory. Returns a pointer to storage 780 managed by this function or its callees (currently getenv). */ 781 char * 782 get_homedir () 783 { 784 return getenv ("HOME"); 785 } 786 787 /* See cvs.h for description. */ 788 void 789 expand_wild (argc, argv, pargc, pargv) 790 int argc; 791 char **argv; 792 int *pargc; 793 char ***pargv; 794 { 795 int i; 796 int new_argc; 797 char **new_argv; 798 /* Allocated size of new_argv. We arrange it so there is always room for 799 one more element. */ 800 int max_new_argc; 801 802 new_argc = 0; 803 /* Add one so this is never zero. */ 804 max_new_argc = argc + 1; 805 new_argv = (char **) xmalloc (max_new_argc * sizeof (char *)); 806 for (i = 0; i < argc; ++i) 807 { 808 HDIR FindHandle = 0x0001; 809 FILEFINDBUF3 FindBuffer; 810 ULONG FindCount = 1; 811 APIRET rc; /* Return code */ 812 #define ALL_FILES (FILE_ARCHIVED|FILE_DIRECTORY|FILE_SYSTEM|FILE_HIDDEN|FILE_READONLY) 813 814 /* DosFindFirst, called with a string like 'dir/file' will return 815 * *only* the file part. So what we have to do here is to save the 816 * directory part, and add it later to the returned filename. 817 */ 818 819 /* Path + name */ 820 char *PathName = argv [i]; 821 822 /* Path only, including slash */ 823 char *Path = NULL; 824 825 /* Name without path */ 826 char *Name = last_component (PathName); 827 828 if (Name > PathName) 829 { 830 /* We have a path component, save it */ 831 Path = xmalloc (Name - PathName + 1); 832 memcpy (Path, PathName, Name - PathName); 833 Path [Name - PathName] = '\0'; 834 } 835 836 rc = DosFindFirst(PathName, /* File pattern */ 837 &FindHandle, /* Directory search handle */ 838 ALL_FILES, /* Search attribute */ 839 (PVOID) &FindBuffer, /* Result buffer */ 840 sizeof(FindBuffer), /* Result buffer length */ 841 &FindCount, /* Number of entries to find */ 842 FIL_STANDARD); /* Return level 1 file info */ 843 844 if (rc != 0) 845 { 846 if (rc == ERROR_NO_MORE_FILES) 847 { 848 /* No match. The file specified didn't contain a wildcard (in which case 849 we clearly should return it unchanged), or it contained a wildcard which 850 didn't match (in which case it might be better for it to be an error, 851 but we don't try to do that). */ 852 new_argv [new_argc++] = xstrdup (argv[i]); 853 if (new_argc == max_new_argc) 854 { 855 max_new_argc *= 2; 856 new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *)); 857 } 858 } 859 else 860 { 861 error (1, rc, "cannot find %s", PathName); 862 } 863 } 864 else 865 { 866 while (1) 867 { 868 /* 869 * Don't match ".", "..", and files starting with '.' 870 * (unless pattern also starts with '.'). This is 871 * (more or less) what standard Unix globbing does. 872 */ 873 if ((strcmp(FindBuffer.achName, ".") != 0) && 874 (strcmp(FindBuffer.achName, "..") != 0) && 875 ((argv[i][0] == '.') || (FindBuffer.achName[0] != '.'))) 876 { 877 /* Be sure to add the path if needed */ 878 char *NewArg; 879 if (Path) 880 { 881 unsigned Len = 882 strlen (Path) + strlen (FindBuffer.achName) + 1; 883 NewArg = xmalloc (Len); 884 strcpy (NewArg, Path); 885 strcat (NewArg, FindBuffer.achName); 886 } 887 else 888 { 889 NewArg = xstrdup (FindBuffer.achName); 890 } 891 new_argv [new_argc++] = NewArg; 892 if (new_argc == max_new_argc) 893 { 894 max_new_argc *= 2; 895 new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *)); 896 } 897 } 898 899 rc = DosFindNext (FindHandle, 900 (PVOID) &FindBuffer, 901 sizeof(FindBuffer), 902 &FindCount); 903 if (rc == ERROR_NO_MORE_FILES) 904 break; 905 else if (rc != NO_ERROR) 906 error (1, rc, "cannot find %s", argv[i]); 907 } 908 rc = DosFindClose(FindHandle); 909 if (rc != 0) 910 error (1, rc, "cannot close %s", argv[i]); 911 } 912 if (Path != NULL) 913 free (Path); 914 } 915 *pargc = new_argc; 916 *pargv = new_argv; 917 } 918 919 int 920 os2_chdir (const char *Dir) 921 /* Change drive and directory to the path given in Dir */ 922 { 923 /* If the path includes a drive, change the current drive to the one given */ 924 if (strlen (Dir) >= 2 && Dir [1] == ':') 925 { 926 /* A drive is given in Dir. Extract the drive from the string, then 927 * remove the drive from Dir by incrementing it. 928 */ 929 int Drive = Dir [0]; 930 Dir += 2; 931 932 /* Check if the given drive is valid, convert to a drive number 933 * (A: == 1, B: == 2, etc.). The compare below assumes ascii, but 934 * that is not a problem with OS/2. 935 */ 936 if (Drive >= 'a' && Drive <= 'z') 937 { 938 Drive -= 'a' - 1; 939 } 940 else if (Drive >= 'A' && Drive <= 'Z') 941 { 942 Drive -= 'A' - 1; 943 } 944 else 945 { 946 /* An invalid drive letter. Set errno and return an error */ 947 errno = EACCES; 948 return -1; 949 } 950 951 /* We have a valid drive given, so change the drive now */ 952 if (DosSetDefaultDisk (Drive) != 0) 953 { 954 /* We had an error. Assume that the drive does not exist */ 955 errno = ENODEV; 956 return -1; 957 } 958 959 } 960 961 /* Now we have a path without a drive left. Make it the current dir */ 962 return chdir (Dir); 963 } 964 965 966 967