pathname.c revision f765521f
1/* 2 * Copyright (c) 2001 by The XFree86 Project, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 * SOFTWARE. 21 * 22 * Except as contained in this notice, the name of the XFree86 Project shall 23 * not be used in advertising or otherwise to promote the sale, use or other 24 * dealings in this Software without prior written authorization from the 25 * XFree86 Project. 26 * 27 * Author: Paulo César Pereira de Andrade 28 */ 29 30/* $XFree86: xc/programs/xedit/lisp/pathname.c,v 1.17tsi Exp $ */ 31 32#include <stdio.h> /* including dirent.h first may cause problems */ 33#include <sys/types.h> 34#include <dirent.h> 35#include <errno.h> 36#include <sys/stat.h> 37#include "lisp/pathname.h" 38#include "lisp/private.h" 39 40#define NOREAD_SKIP 0 41#define NOREAD_ERROR 1 42 43/* 44 * Initialization 45 */ 46LispObj *Oparse_namestring, *Kerror, *Kabsolute, *Krelative, *Kskip; 47 48/* 49 * Implementation 50 */ 51void 52LispPathnameInit(void) 53{ 54 Kerror = KEYWORD("ERROR"); 55 Oparse_namestring = STATIC_ATOM("PARSE-NAMESTRING"); 56 Kabsolute = KEYWORD("ABSOLUTE"); 57 Krelative = KEYWORD("RELATIVE"); 58} 59 60static int 61glob_match(char *cmp1, char *cmp2) 62/* 63 * Note: this code was written from scratch, and may generate incorrect 64 * results for very complex glob masks. 65 */ 66{ 67 for (;;) { 68 while (*cmp1 && *cmp1 == *cmp2) { 69 ++cmp1; 70 ++cmp2; 71 } 72 if (*cmp2) { 73 if (*cmp1 == '*') { 74 while (*cmp1 == '*') 75 ++cmp1; 76 if (*cmp1) { 77 int count = 0, settmp = 1; 78 char *tmp = cmp2, *sav2; 79 80 while (*cmp1 && *cmp1 == '?') { 81 ++cmp1; 82 ++count; 83 } 84 85 /* need to recurse here to make sure 86 * all cases are tested. 87 */ 88 while (*cmp2 && *cmp2 != *cmp1) 89 ++cmp2; 90 if (!*cmp1 && cmp2 - tmp < count) 91 return (0); 92 sav2 = cmp2; 93 94 /* if recursive calls fails, make sure all '?' 95 * following '*' are processed */ 96 while (*sav2 && sav2 - tmp < count) 97 ++sav2; 98 99 for (; *cmp2;) { 100 if (settmp) /* repeated letters: *?o? => boot, root */ 101 tmp = cmp2; 102 else 103 settmp = 1; 104 while (*cmp2 && *cmp2 != *cmp1) 105 ++cmp2; 106 if (cmp2 - tmp < count) { 107 if (*cmp2) 108 ++cmp2; 109 settmp = 0; 110 continue; 111 } 112 if (*cmp2) { 113 if (glob_match(cmp1, cmp2)) 114 return (1); 115 ++cmp2; 116 } 117 } 118 cmp2 = sav2; 119 } 120 else { 121 while (*cmp2) 122 ++cmp2; 123 break; 124 } 125 } 126 else if (*cmp1 == '?') { 127 while (*cmp1 == '?' && *cmp2) { 128 ++cmp1; 129 ++cmp2; 130 } 131 continue; 132 } 133 else 134 break; 135 } 136 else { 137 while (*cmp1 == '*') 138 ++cmp1; 139 break; 140 } 141 } 142 143 return (*cmp1 == '\0' && *cmp2 == '\0'); 144} 145 146/* 147 * Since directory is a function to be extended by the implementation, 148 * current extensions are: 149 * all => list files and directories 150 * it is an error to call 151 * (directory "<pathname-spec>/" :all t) 152 * if non nil, it is like the shell command 153 * echo <pathname-spec>, but normally, not in the 154 * same order, as the code does not sort the result. 155 * !=nil => list files and directories 156 * (default) nil => list only files, or only directories if 157 * <pathname-spec> ends with PATH_SEP char. 158 * if-cannot-read => if opendir fails on a directory 159 * :error => generate an error 160 * (default) :skip => skip search in this directory 161 */ 162LispObj * 163Lisp_Directory(LispBuiltin *builtin) 164/* 165 directory pathname &key all if-cannot-read 166 */ 167{ 168 GC_ENTER(); 169 DIR *dir; 170 struct stat st; 171 struct dirent *ent; 172 int length, listdirs, i, ndirs, nmatches; 173 char name[PATH_MAX + 1], path[PATH_MAX + 2], directory[PATH_MAX + 2]; 174 char *sep, *base, *ptr, **dirs, **matches, 175 dot[] = {'.', PATH_SEP, '\0'}, 176 dotdot[] = {'.', '.', PATH_SEP, '\0'}; 177 int cannot_read; 178 179 LispObj *pathname, *all, *if_cannot_read, *result, *cons, *object; 180 181 if_cannot_read = ARGUMENT(2); 182 all = ARGUMENT(1); 183 pathname = ARGUMENT(0); 184 result = NIL; 185 186 cons = NIL; 187 188 if (if_cannot_read != UNSPEC) { 189 if (!KEYWORDP(if_cannot_read) || 190 (if_cannot_read != Kskip && 191 if_cannot_read != Kerror)) 192 LispDestroy("%s: bad :IF-CANNOT-READ %s", 193 STRFUN(builtin), STROBJ(if_cannot_read)); 194 if (if_cannot_read != Kskip) 195 cannot_read = NOREAD_SKIP; 196 else 197 cannot_read = NOREAD_ERROR; 198 } 199 else 200 cannot_read = NOREAD_SKIP; 201 202 if (PATHNAMEP(pathname)) 203 pathname = CAR(pathname->data.pathname); 204 else if (STREAMP(pathname) && pathname->data.stream.type == LispStreamFile) 205 pathname = CAR(pathname->data.stream.pathname->data.pathname); 206 else if (!STRINGP(pathname)) 207 LispDestroy("%s: %s is not a pathname", 208 STRFUN(builtin), STROBJ(pathname)); 209 210 strncpy(name, THESTR(pathname), sizeof(name) - 1); 211 name[sizeof(name) - 1] = '\0'; 212 length = strlen(name); 213 if (length < STRLEN(pathname)) 214 LispDestroy("%s: pathname too long %s", 215 STRFUN(builtin), name); 216 217 if (length == 0) { 218 if (getcwd(path, sizeof(path) - 2) == NULL) 219 LispDestroy("%s: getcwd(): %s", STRFUN(builtin), strerror(errno)); 220 length = strlen(path); 221 if (!length || path[length - 1] != PATH_SEP) { 222 path[length++] = PATH_SEP; 223 path[length] = '\0'; 224 } 225 result = APPLY1(Oparse_namestring, LSTRING(path, length)); 226 GC_LEAVE(); 227 228 return (result); 229 } 230 231 if (name[length - 1] == PATH_SEP) { 232 listdirs = 1; 233 if (length > 1) { 234 --length; 235 name[length] = '\0'; 236 } 237 } 238 else 239 listdirs = 0; 240 241 if (name[0] != PATH_SEP) { 242 if (getcwd(path, sizeof(path) - 2) == NULL) 243 LispDestroy("%s: getcwd(): %s", STRFUN(builtin), strerror(errno)); 244 length = strlen(path); 245 if (!length || path[length - 1] != PATH_SEP) { 246 path[length++] = PATH_SEP; 247 path[length] = '\0'; 248 } 249 } 250 else 251 path[0] = '\0'; 252 253 result = NIL; 254 255 /* list intermediate directories */ 256 matches = NULL; 257 nmatches = 0; 258 dirs = LispMalloc(sizeof(char*)); 259 ndirs = 1; 260 if (snprintf(directory, sizeof(directory), "%s%s%c", 261 path, name, PATH_SEP) > PATH_MAX) 262 LispDestroy("%s: pathname too long %s", STRFUN(builtin), directory); 263 264 /* Remove ../ */ 265 sep = directory; 266 for (sep = strstr(sep, dotdot); sep; sep = strstr(sep, dotdot)) { 267 if (sep <= directory + 1) 268 strcpy(directory, sep + 2); 269 else if (sep[-1] == PATH_SEP) { 270 for (base = sep - 2; base > directory; base--) 271 if (*base == PATH_SEP) 272 break; 273 strcpy(base, sep + 2); 274 sep = base; 275 } 276 else 277 ++sep; 278 } 279 280 /* Remove "./" */ 281 sep = directory; 282 for (sep = strstr(sep, dot); sep; sep = strstr(sep, dot)) { 283 if (sep == directory || sep[-1] == PATH_SEP) 284 strcpy(sep, sep + 2); 285 else 286 ++sep; 287 } 288 289 /* This will happen when there are too many '../' in the path */ 290 if (directory[1] == '\0') { 291 directory[1] = PATH_SEP; 292 directory[2] = '\0'; 293 } 294 295 base = directory; 296 sep = strchr(base + 1, PATH_SEP); 297 dirs[0] = LispMalloc(2); 298 dirs[0][0] = PATH_SEP; 299 dirs[0][1] = '\0'; 300 301 for (base = directory + 1, sep = strchr(base, PATH_SEP); ; 302 base = sep + 1, sep = strchr(base, PATH_SEP)) { 303 *sep = '\0'; 304 if (sep[1] == '\0') 305 sep = NULL; 306 length = strlen(base); 307 if (length == 0) { 308 if (sep) 309 *sep = PATH_SEP; 310 else 311 break; 312 continue; 313 } 314 315 for (i = 0; i < ndirs; i++) { 316 length = strlen(dirs[i]); 317 if (length > 1) 318 dirs[i][length - 1] = '\0'; /* remove trailing / */ 319 if ((dir = opendir(dirs[i])) != NULL) { 320 (void)readdir(dir); /* "." */ 321 (void)readdir(dir); /* ".." */ 322 if (length > 1) 323 dirs[i][length - 1] = PATH_SEP; /* add trailing / again */ 324 325 snprintf(path, sizeof(path), "%s", dirs[i]); 326 length = strlen(path); 327 ptr = path + length; 328 329 while ((ent = readdir(dir)) != NULL) { 330 int isdir; 331 unsigned d_namlen = strlen(ent->d_name); 332 333 if (length + d_namlen + 2 < sizeof(path)) 334 strcpy(ptr, ent->d_name); 335 else { 336 closedir(dir); 337 LispDestroy("%s: pathname too long %s", 338 STRFUN(builtin), dirs[i]); 339 } 340 341 if (stat(path, &st) != 0) 342 isdir = 0; 343 else 344 isdir = S_ISDIR(st.st_mode); 345 346 if (all != UNSPEC || ((isdir && (listdirs || sep)) || 347 (!listdirs && !sep && !isdir))) { 348 if (glob_match(base, ent->d_name)) { 349 if (isdir) { 350 length = strlen(ptr); 351 ptr[length++] = PATH_SEP; 352 ptr[length] = '\0'; 353 } 354 /* XXX won't closedir on memory allocation failure! */ 355 matches = LispRealloc(matches, sizeof(char*) * 356 nmatches + 1); 357 matches[nmatches++] = LispStrdup(ptr); 358 } 359 } 360 } 361 closedir(dir); 362 363 if (nmatches == 0) { 364 if (sep || !listdirs || *base) { 365 LispFree(dirs[i]); 366 if (i + 1 < ndirs) 367 memmove(dirs + i, dirs + i + 1, 368 sizeof(char*) * (ndirs - (i + 1))); 369 --ndirs; 370 --i; /* XXX playing with for loop */ 371 } 372 } 373 else { 374 int j; 375 376 length = strlen(dirs[i]); 377 if (nmatches > 1) { 378 dirs = LispRealloc(dirs, sizeof(char*) * 379 (ndirs + nmatches)); 380 if (i + 1 < ndirs) 381 memmove(dirs + i + nmatches, dirs + i + 1, 382 sizeof(char*) * (ndirs - (i + 1))); 383 } 384 for (j = 1; j < nmatches; j++) { 385 dirs[i + j] = LispMalloc(length + 386 strlen(matches[j]) + 1); 387 sprintf(dirs[i + j], "%s%s", dirs[i], matches[j]); 388 } 389 dirs[i] = LispRealloc(dirs[i], 390 length + strlen(matches[0]) + 1); 391 strcpy(dirs[i] + length, matches[0]); 392 i += nmatches - 1; /* XXX playing with for loop */ 393 ndirs += nmatches - 1; 394 395 for (j = 0; j < nmatches; j++) 396 LispFree(matches[j]); 397 LispFree(matches); 398 matches = NULL; 399 nmatches = 0; 400 } 401 } 402 else { 403 if (cannot_read == NOREAD_ERROR) 404 LispDestroy("%s: opendir(%s): %s", 405 STRFUN(builtin), dirs[i], strerror(errno)); 406 else { 407 LispFree(dirs[i]); 408 if (i + 1 < ndirs) 409 memmove(dirs + i, dirs + i + 1, 410 sizeof(char*) * (ndirs - (i + 1))); 411 --ndirs; 412 --i; /* XXX playing with for loop */ 413 } 414 } 415 } 416 if (sep) 417 *sep = PATH_SEP; 418 else 419 break; 420 } 421 422 for (i = 0; i < ndirs; i++) { 423 object = APPLY1(Oparse_namestring, STRING2(dirs[i])); 424 if (result == NIL) { 425 result = cons = CONS(object, NIL); 426 GC_PROTECT(result); 427 } 428 else { 429 RPLACD(cons, CONS(object, NIL)); 430 cons = CDR(cons); 431 } 432 } 433 LispFree(dirs); 434 GC_LEAVE(); 435 436 return (result); 437} 438 439LispObj * 440Lisp_ParseNamestring(LispBuiltin *builtin) 441/* 442 parse-namestring object &optional host defaults &key start end junk-allowed 443 */ 444{ 445 GC_ENTER(); 446 LispObj *result; 447 448 LispObj *object, *host, *defaults, *ostart, *oend, *junk_allowed; 449 450 junk_allowed = ARGUMENT(5); 451 oend = ARGUMENT(4); 452 ostart = ARGUMENT(3); 453 defaults = ARGUMENT(2); 454 host = ARGUMENT(1); 455 object = ARGUMENT(0); 456 457 if (host == UNSPEC) 458 host = NIL; 459 if (defaults == UNSPEC) 460 defaults = NIL; 461 462 RETURN_COUNT = 1; 463 if (STREAMP(object)) { 464 if (object->data.stream.type == LispStreamFile) 465 object = object->data.stream.pathname; 466 /* else just check for JUNK-ALLOWED... */ 467 } 468 if (PATHNAMEP(object)) { 469 RETURN(0) = FIXNUM(0); 470 return (object); 471 } 472 473 if (host != NIL) { 474 CHECK_STRING(host); 475 } 476 if (defaults != NIL) { 477 if (!PATHNAMEP(defaults)) { 478 defaults = APPLY1(Oparse_namestring, defaults); 479 GC_PROTECT(defaults); 480 } 481 } 482 483 result = NIL; 484 if (STRINGP(object)) { 485 LispObj *cons, *cdr; 486 char *name = THESTR(object), *ptr, *str, data[PATH_MAX + 1], 487 string[PATH_MAX + 1], *send; 488 const char *namestr, *typestr; 489 long start, end, length, alength, namelen, typelen; 490 491 LispCheckSequenceStartEnd(builtin, object, ostart, oend, 492 &start, &end, &length); 493 alength = end - start; 494 495 if (alength > sizeof(data) - 1) 496 LispDestroy("%s: string %s too large", 497 STRFUN(builtin), STROBJ(object)); 498 memcpy(data, name + start, alength); 499#ifndef KEEP_EXTRA_PATH_SEP 500 ptr = data; 501 send = ptr + alength; 502 while (ptr < send) { 503 if (*ptr++ == PATH_SEP) { 504 for (str = ptr; str < send && *str == PATH_SEP; str++) 505 ; 506 if (str - ptr) { 507 memmove(ptr, str, alength - (str - data)); 508 alength -= str - ptr; 509 send -= str - ptr; 510 } 511 } 512 } 513#endif 514 data[alength] = '\0'; 515 memcpy(string, data, alength + 1); 516 517 if (PATHNAMEP(defaults)) 518 defaults = defaults->data.pathname; 519 520 /* string name */ 521 result = cons = CONS(NIL, NIL); 522 GC_PROTECT(result); 523 524 /* host */ 525 if (defaults != NIL) 526 defaults = CDR(defaults); 527 cdr = defaults == NIL ? NIL : CAR(defaults); 528 RPLACD(cons, CONS(cdr, NIL)); 529 cons = CDR(cons); 530 531 /* device */ 532 if (defaults != NIL) 533 defaults = CDR(defaults); 534 cdr = defaults == NIL ? NIL : CAR(defaults); 535 RPLACD(cons, CONS(cdr, NIL)); 536 cons = CDR(cons); 537 538 /* directory */ 539 if (defaults != NIL) 540 defaults = CDR(defaults); 541 if (*data == PATH_SEP) 542 cdr = CONS(Kabsolute, NIL); 543 else 544 cdr = CONS(Krelative, NIL); 545 RPLACD(cons, CONS(cdr, NIL)); 546 cons = CDR(cons); 547 /* directory components */ 548 ptr = data; 549 send = data + alength; 550 if (*ptr == PATH_SEP) 551 ++ptr; 552 for (str = ptr; str < send; str++) { 553 if (*str == PATH_SEP) 554 break; 555 } 556 while (str < send) { 557 *str++ = '\0'; 558 if (str - ptr > NAME_MAX) 559 LispDestroy("%s: directory name too long %s", 560 STRFUN(builtin), ptr); 561 RPLACD(cdr, CONS(LSTRING(ptr, str - ptr - 1), NIL)); 562 cdr = CDR(cdr); 563 for (ptr = str; str < send; str++) { 564 if (*str == PATH_SEP) 565 break; 566 } 567 } 568 if (str - ptr > NAME_MAX) 569 LispDestroy("%s: file name too long %s", STRFUN(builtin), ptr); 570 if (CAAR(cons) == Krelative && 571 defaults != NIL && CAAR(defaults) == Kabsolute) { 572 /* defaults specify directory and pathname doesn't */ 573 char *tstring; 574 long dlength, tlength; 575 LispObj *dir = CDAR(defaults); 576 577 for (dlength = 1; CONSP(dir); dir = CDR(dir)) 578 dlength += STRLEN(CAR(dir)) + 1; 579 if (alength + dlength < PATH_MAX) { 580 memmove(data + dlength, data, alength + 1); 581 memmove(string + dlength, string, alength + 1); 582 alength += dlength; 583 ptr += dlength; 584 send += dlength; 585 CAAR(cons) = Kabsolute; 586 for (dir = CDAR(defaults), cdr = CAR(cons); 587 CONSP(dir); 588 dir = CDR(dir)) { 589 RPLACD(cdr, CONS(CAR(dir), CDR(cdr))); 590 cdr = CDR(cdr); 591 } 592 dir = CDAR(defaults); 593 data[0] = string[0] = PATH_SEP; 594 for (dlength = 1; CONSP(dir); dir = CDR(dir)) { 595 tstring = THESTR(CAR(dir)); 596 tlength = STRLEN(CAR(dir)); 597 memcpy(data + dlength, tstring, tlength); 598 memcpy(string + dlength, tstring, tlength); 599 dlength += tlength; 600 data[dlength] = string[dlength] = PATH_SEP; 601 ++dlength; 602 } 603 } 604 } 605 606 /* name */ 607 if (defaults != NIL) 608 defaults = CDR(defaults); 609 cdr = defaults == NIL ? NIL : CAR(defaults); 610 for (typelen = 0, str = ptr; str < send; str++) { 611 if (*str == PATH_TYPESEP) { 612 typelen = 1; 613 break; 614 } 615 } 616 if (*ptr) 617 cdr = LSTRING(ptr, str - ptr); 618 if (STRINGP(cdr)) { 619 namestr = THESTR(cdr); 620 namelen = STRLEN(cdr); 621 } 622 else { 623 namestr = ""; 624 namelen = 0; 625 } 626 RPLACD(cons, CONS(cdr, NIL)); 627 cons = CDR(cons); 628 629 /* type */ 630 if (defaults != NIL) 631 defaults = CDR(defaults); 632 cdr = defaults == NIL ? NIL : CAR(defaults); 633 ptr = str + typelen; 634 if (*ptr) 635 cdr = LSTRING(ptr, send - ptr); 636 if (STRINGP(cdr)) { 637 typestr = THESTR(cdr); 638 typelen = STRLEN(cdr); 639 } 640 else { 641 typestr = ""; 642 typelen = 0; 643 } 644 RPLACD(cons, CONS(cdr, NIL)); 645 cons = CDR(cons); 646 647 /* version */ 648 if (defaults != NIL) 649 defaults = CDR(defaults); 650 cdr = defaults == NIL ? NIL : CAR(defaults); 651 RPLACD(cons, CONS(cdr, NIL)); 652 653 /* string representation, must be done here to use defaults */ 654 for (ptr = string + alength; ptr >= string; ptr--) { 655 if (*ptr == PATH_SEP) 656 break; 657 } 658 if (ptr >= string) 659 ++ptr; 660 else 661 ptr = string; 662 *ptr = '\0'; 663 664 length = ptr - string; 665 666 alength = namelen; 667 if (alength) { 668 if (length + alength + 2 > sizeof(string)) 669 alength = sizeof(string) - length - 2; 670 memcpy(string + length, namestr, alength); 671 length += alength; 672 } 673 674 alength = typelen; 675 if (alength) { 676 if (length + 2 < sizeof(string)) 677 string[length++] = PATH_TYPESEP; 678 if (length + alength + 2 > sizeof(string)) 679 alength = sizeof(string) - length - 2; 680 memcpy(string + length, typestr, alength); 681 length += alength; 682 } 683 string[length] = '\0'; 684 685 RPLACA(result, LSTRING(string, length)); 686 RETURN(0) = FIXNUM(end); 687 688 result = PATHNAME(result); 689 } 690 else if (junk_allowed == UNSPEC || junk_allowed == NIL) 691 LispDestroy("%s: bad argument %s", STRFUN(builtin), STROBJ(object)); 692 else 693 RETURN(0) = NIL; 694 695 GC_LEAVE(); 696 697 return (result); 698} 699 700LispObj * 701Lisp_MakePathname(LispBuiltin *builtin) 702/* 703 make-pathname &key host device directory name type version defaults 704 */ 705{ 706 GC_ENTER(); 707 int length, alength; 708 char *string, pathname[PATH_MAX + 1]; 709 LispObj *result, *cdr, *cons; 710 711 LispObj *host, *device, *directory, *name, *type, *version, *defaults; 712 713 defaults = ARGUMENT(6); 714 version = ARGUMENT(5); 715 type = ARGUMENT(4); 716 name = ARGUMENT(3); 717 directory = ARGUMENT(2); 718 device = ARGUMENT(1); 719 host = ARGUMENT(0); 720 721 if (host != UNSPEC) { 722 CHECK_STRING(host); 723 } 724 if (device != UNSPEC) { 725 CHECK_STRING(device); 726 } 727 728 if (directory != UNSPEC) { 729 LispObj *dir; 730 731 CHECK_CONS(directory); 732 dir = CAR(directory); 733 CHECK_KEYWORD(dir); 734 if (dir != Kabsolute && dir != Krelative) 735 LispDestroy("%s: directory type %s unknown", 736 STRFUN(builtin), STROBJ(dir)); 737 } 738 739 if (name != UNSPEC) { 740 CHECK_STRING(name); 741 } 742 if (type != UNSPEC) { 743 CHECK_STRING(type); 744 } 745 746 if (version != UNSPEC && version != NIL) { 747 switch (OBJECT_TYPE(version)) { 748 case LispFixnum_t: 749 if (FIXNUM_VALUE(version) >= 0) 750 goto version_ok; 751 case LispInteger_t: 752 if (INT_VALUE(version) >= 0) 753 goto version_ok; 754 break; 755 case LispDFloat_t: 756 if (DFLOAT_VALUE(version) >= 0.0) 757 goto version_ok; 758 break; 759 default: 760 break; 761 } 762 LispDestroy("%s: %s is not a positive real number", 763 STRFUN(builtin), STROBJ(version)); 764 } 765version_ok: 766 767 if (defaults != UNSPEC && !PATHNAMEP(defaults) && 768 (host == UNSPEC || device == UNSPEC || directory == UNSPEC || 769 name == UNSPEC || type == UNSPEC || version == UNSPEC)) { 770 defaults = APPLY1(Oparse_namestring, defaults); 771 GC_PROTECT(defaults); 772 } 773 774 if (defaults != UNSPEC) { 775 defaults = defaults->data.pathname; 776 defaults = CDR(defaults); /* host */ 777 if (host == UNSPEC) 778 host = CAR(defaults); 779 defaults = CDR(defaults); /* device */ 780 if (device == UNSPEC) 781 device = CAR(defaults); 782 defaults = CDR(defaults); /* directory */ 783 if (directory == UNSPEC) 784 directory = CAR(defaults); 785 defaults = CDR(defaults); /* name */ 786 if (name == UNSPEC) 787 name = CAR(defaults); 788 defaults = CDR(defaults); /* type */ 789 if (type == UNSPEC) 790 type = CAR(defaults); 791 defaults = CDR(defaults); /* version */ 792 if (version == UNSPEC) 793 version = CAR(defaults); 794 } 795 796 /* string representation */ 797 length = 0; 798 if (CONSP(directory)) { 799 if (CAR(directory) == Kabsolute) 800 pathname[length++] = PATH_SEP; 801 802 for (cdr = CDR(directory); CONSP(cdr); cdr = CDR(cdr)) { 803 CHECK_STRING(CAR(cdr)); 804 string = THESTR(CAR(cdr)); 805 alength = STRLEN(CAR(cdr)); 806 if (alength > NAME_MAX) 807 LispDestroy("%s: directory name too long %s", 808 STRFUN(builtin), string); 809 if (length + alength + 2 > sizeof(pathname)) 810 alength = sizeof(pathname) - length - 2; 811 memcpy(pathname + length, string, alength); 812 length += alength; 813 pathname[length++] = PATH_SEP; 814 } 815 } 816 if (STRINGP(name)) { 817 int xlength = 0; 818 819 if (STRINGP(type)) 820 xlength = STRLEN(type) + 1; 821 822 string = THESTR(name); 823 alength = STRLEN(name); 824 if (alength + xlength > NAME_MAX) 825 LispDestroy("%s: file name too long %s", 826 STRFUN(builtin), string); 827 if (length + alength + 2 > sizeof(pathname)) 828 alength = sizeof(pathname) - length - 2; 829 memcpy(pathname + length, string, alength); 830 length += alength; 831 } 832 if (STRINGP(type)) { 833 if (length + 2 < sizeof(pathname)) 834 pathname[length++] = PATH_TYPESEP; 835 string = THESTR(type); 836 alength = STRLEN(type); 837 if (length + alength + 2 > sizeof(pathname)) 838 alength = sizeof(pathname) - length - 2; 839 memcpy(pathname + length, string, alength); 840 length += alength; 841 } 842 pathname[length] = '\0'; 843 result = cons = CONS(LSTRING(pathname, length), NIL); 844 GC_PROTECT(result); 845 846 /* host */ 847 RPLACD(cons, CONS(host == UNSPEC ? NIL : host, NIL)); 848 cons = CDR(cons); 849 850 /* device */ 851 RPLACD(cons, CONS(device == UNSPEC ? NIL : device, NIL)); 852 cons = CDR(cons); 853 854 /* directory */ 855 if (directory == UNSPEC) 856 cdr = CONS(Krelative, NIL); 857 else 858 cdr = directory; 859 RPLACD(cons, CONS(cdr, NIL)); 860 cons = CDR(cons); 861 862 /* name */ 863 RPLACD(cons, CONS(name == UNSPEC ? NIL : name, NIL)); 864 cons = CDR(cons); 865 866 /* type */ 867 RPLACD(cons, CONS(type == UNSPEC ? NIL : type, NIL)); 868 cons = CDR(cons); 869 870 /* version */ 871 RPLACD(cons, CONS(version == UNSPEC ? NIL : version, NIL)); 872 873 GC_LEAVE(); 874 875 return (PATHNAME(result)); 876} 877 878LispObj * 879Lisp_PathnameHost(LispBuiltin *builtin) 880/* 881 pathname-host pathname 882 */ 883{ 884 return (LispPathnameField(PATH_HOST, 0)); 885} 886 887LispObj * 888Lisp_PathnameDevice(LispBuiltin *builtin) 889/* 890 pathname-device pathname 891 */ 892{ 893 return (LispPathnameField(PATH_DEVICE, 0)); 894} 895 896LispObj * 897Lisp_PathnameDirectory(LispBuiltin *builtin) 898/* 899 pathname-device pathname 900 */ 901{ 902 return (LispPathnameField(PATH_DIRECTORY, 0)); 903} 904 905LispObj * 906Lisp_PathnameName(LispBuiltin *builtin) 907/* 908 pathname-name pathname 909 */ 910{ 911 return (LispPathnameField(PATH_NAME, 0)); 912} 913 914LispObj * 915Lisp_PathnameType(LispBuiltin *builtin) 916/* 917 pathname-type pathname 918 */ 919{ 920 return (LispPathnameField(PATH_TYPE, 0)); 921} 922 923LispObj * 924Lisp_PathnameVersion(LispBuiltin *builtin) 925/* 926 pathname-version pathname 927 */ 928{ 929 return (LispPathnameField(PATH_VERSION, 0)); 930} 931 932LispObj * 933Lisp_FileNamestring(LispBuiltin *builtin) 934/* 935 file-namestring pathname 936 */ 937{ 938 return (LispPathnameField(PATH_NAME, 1)); 939} 940 941LispObj * 942Lisp_DirectoryNamestring(LispBuiltin *builtin) 943/* 944 directory-namestring pathname 945 */ 946{ 947 return (LispPathnameField(PATH_DIRECTORY, 1)); 948} 949 950LispObj * 951Lisp_EnoughNamestring(LispBuiltin *builtin) 952/* 953 enough-pathname pathname &optional defaults 954 */ 955{ 956 LispObj *pathname, *defaults; 957 958 defaults = ARGUMENT(1); 959 pathname = ARGUMENT(0); 960 961 if (defaults != UNSPEC && defaults != NIL) { 962 char *ppathname, *pdefaults, *pp, *pd; 963 964 if (!STRINGP(pathname)) { 965 if (PATHNAMEP(pathname)) 966 pathname = CAR(pathname->data.pathname); 967 else if (STREAMP(pathname) && 968 pathname->data.stream.type == LispStreamFile) 969 pathname = CAR(pathname->data.stream.pathname->data.pathname); 970 else 971 LispDestroy("%s: bad PATHNAME %s", 972 STRFUN(builtin), STROBJ(pathname)); 973 } 974 975 if (!STRINGP(defaults)) { 976 if (PATHNAMEP(defaults)) 977 defaults = CAR(defaults->data.pathname); 978 else if (STREAMP(defaults) && 979 defaults->data.stream.type == LispStreamFile) 980 defaults = CAR(defaults->data.stream.pathname->data.pathname); 981 else 982 LispDestroy("%s: bad DEFAULTS %s", 983 STRFUN(builtin), STROBJ(defaults)); 984 } 985 986 ppathname = pp = THESTR(pathname); 987 pdefaults = pd = THESTR(defaults); 988 while (*ppathname && *pdefaults && *ppathname == *pdefaults) { 989 ppathname++; 990 pdefaults++; 991 } 992 if (*pdefaults == '\0' && pdefaults > pd) 993 --pdefaults; 994 if (*ppathname && *pdefaults && *pdefaults != PATH_SEP) { 995 --ppathname; 996 while (*ppathname != PATH_SEP && ppathname > pp) 997 --ppathname; 998 if (*ppathname == PATH_SEP) 999 ++ppathname; 1000 } 1001 1002 return (STRING(ppathname)); 1003 } 1004 else { 1005 if (STRINGP(pathname)) 1006 return (pathname); 1007 else if (PATHNAMEP(pathname)) 1008 return (CAR(pathname->data.pathname)); 1009 else if (STREAMP(pathname)) { 1010 if (pathname->data.stream.type == LispStreamFile) 1011 return (CAR(pathname->data.stream.pathname->data.pathname)); 1012 } 1013 } 1014 LispDestroy("%s: bad PATHNAME %s", STRFUN(builtin), STROBJ(pathname)); 1015 1016 return (NIL); 1017} 1018 1019LispObj * 1020Lisp_Namestring(LispBuiltin *builtin) 1021/* 1022 namestring pathname 1023 */ 1024{ 1025 return (LispPathnameField(PATH_STRING, 1)); 1026} 1027 1028LispObj * 1029Lisp_HostNamestring(LispBuiltin *builtin) 1030/* 1031 host-namestring pathname 1032 */ 1033{ 1034 return (LispPathnameField(PATH_HOST, 1)); 1035} 1036 1037LispObj * 1038Lisp_Pathnamep(LispBuiltin *builtin) 1039/* 1040 pathnamep object 1041 */ 1042{ 1043 LispObj *object; 1044 1045 object = ARGUMENT(0); 1046 1047 return (PATHNAMEP(object) ? T : NIL); 1048} 1049 1050/* XXX only checks if host is a string and only checks the HOME enviroment 1051 * variable */ 1052LispObj * 1053Lisp_UserHomedirPathname(LispBuiltin *builtin) 1054/* 1055 user-homedir-pathname &optional host 1056 */ 1057{ 1058 GC_ENTER(); 1059 int length; 1060 char *home = getenv("HOME"), data[PATH_MAX + 1]; 1061 LispObj *result; 1062 1063 LispObj *host; 1064 1065 host = ARGUMENT(0); 1066 1067 if (host != UNSPEC && !STRINGP(host)) 1068 LispDestroy("%s: bad hostname %s", STRFUN(builtin), STROBJ(host)); 1069 1070 length = 0; 1071 if (home) { 1072 length = strlen(home); 1073 strncpy(data, home, length); 1074 if (length && home[length - 1] != PATH_SEP) 1075 data[length++] = PATH_SEP; 1076 } 1077 data[length] = '\0'; 1078 1079 result = LSTRING(data, length); 1080 GC_PROTECT(result); 1081 result = APPLY1(Oparse_namestring, result); 1082 GC_LEAVE(); 1083 1084 return (result); 1085} 1086 1087LispObj * 1088Lisp_Truename(LispBuiltin *builtin) 1089{ 1090 return (LispProbeFile(builtin, 0)); 1091} 1092 1093LispObj * 1094Lisp_ProbeFile(LispBuiltin *builtin) 1095{ 1096 return (LispProbeFile(builtin, 1)); 1097} 1098