pathname.c revision 5dfecf96
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], *namestr, *typestr, *send; 488 long start, end, length, alength, namelen, typelen; 489 490 LispCheckSequenceStartEnd(builtin, object, ostart, oend, 491 &start, &end, &length); 492 alength = end - start; 493 494 if (alength > sizeof(data) - 1) 495 LispDestroy("%s: string %s too large", 496 STRFUN(builtin), STROBJ(object)); 497 memcpy(data, name + start, alength); 498#ifndef KEEP_EXTRA_PATH_SEP 499 ptr = data; 500 send = ptr + alength; 501 while (ptr < send) { 502 if (*ptr++ == PATH_SEP) { 503 for (str = ptr; str < send && *str == PATH_SEP; str++) 504 ; 505 if (str - ptr) { 506 memmove(ptr, str, alength - (str - data)); 507 alength -= str - ptr; 508 send -= str - ptr; 509 } 510 } 511 } 512#endif 513 data[alength] = '\0'; 514 memcpy(string, data, alength + 1); 515 516 if (PATHNAMEP(defaults)) 517 defaults = defaults->data.pathname; 518 519 /* string name */ 520 result = cons = CONS(NIL, NIL); 521 GC_PROTECT(result); 522 523 /* host */ 524 if (defaults != NIL) 525 defaults = CDR(defaults); 526 cdr = defaults == NIL ? NIL : CAR(defaults); 527 RPLACD(cons, CONS(cdr, NIL)); 528 cons = CDR(cons); 529 530 /* device */ 531 if (defaults != NIL) 532 defaults = CDR(defaults); 533 cdr = defaults == NIL ? NIL : CAR(defaults); 534 RPLACD(cons, CONS(cdr, NIL)); 535 cons = CDR(cons); 536 537 /* directory */ 538 if (defaults != NIL) 539 defaults = CDR(defaults); 540 if (*data == PATH_SEP) 541 cdr = CONS(Kabsolute, NIL); 542 else 543 cdr = CONS(Krelative, NIL); 544 RPLACD(cons, CONS(cdr, NIL)); 545 cons = CDR(cons); 546 /* directory components */ 547 ptr = data; 548 send = data + alength; 549 if (*ptr == PATH_SEP) 550 ++ptr; 551 for (str = ptr; str < send; str++) { 552 if (*str == PATH_SEP) 553 break; 554 } 555 while (str < send) { 556 *str++ = '\0'; 557 if (str - ptr > NAME_MAX) 558 LispDestroy("%s: directory name too long %s", 559 STRFUN(builtin), ptr); 560 RPLACD(cdr, CONS(LSTRING(ptr, str - ptr - 1), NIL)); 561 cdr = CDR(cdr); 562 for (ptr = str; str < send; str++) { 563 if (*str == PATH_SEP) 564 break; 565 } 566 } 567 if (str - ptr > NAME_MAX) 568 LispDestroy("%s: file name too long %s", STRFUN(builtin), ptr); 569 if (CAAR(cons) == Krelative && 570 defaults != NIL && CAAR(defaults) == Kabsolute) { 571 /* defaults specify directory and pathname doesn't */ 572 char *tstring; 573 long dlength, tlength; 574 LispObj *dir = CDAR(defaults); 575 576 for (dlength = 1; CONSP(dir); dir = CDR(dir)) 577 dlength += STRLEN(CAR(dir)) + 1; 578 if (alength + dlength < PATH_MAX) { 579 memmove(data + dlength, data, alength + 1); 580 memmove(string + dlength, string, alength + 1); 581 alength += dlength; 582 ptr += dlength; 583 send += dlength; 584 CAAR(cons) = Kabsolute; 585 for (dir = CDAR(defaults), cdr = CAR(cons); 586 CONSP(dir); 587 dir = CDR(dir)) { 588 RPLACD(cdr, CONS(CAR(dir), CDR(cdr))); 589 cdr = CDR(cdr); 590 } 591 dir = CDAR(defaults); 592 data[0] = string[0] = PATH_SEP; 593 for (dlength = 1; CONSP(dir); dir = CDR(dir)) { 594 tstring = THESTR(CAR(dir)); 595 tlength = STRLEN(CAR(dir)); 596 memcpy(data + dlength, tstring, tlength); 597 memcpy(string + dlength, tstring, tlength); 598 dlength += tlength; 599 data[dlength] = string[dlength] = PATH_SEP; 600 ++dlength; 601 } 602 } 603 } 604 605 /* name */ 606 if (defaults != NIL) 607 defaults = CDR(defaults); 608 cdr = defaults == NIL ? NIL : CAR(defaults); 609 for (typelen = 0, str = ptr; str < send; str++) { 610 if (*str == PATH_TYPESEP) { 611 typelen = 1; 612 break; 613 } 614 } 615 if (*ptr) 616 cdr = LSTRING(ptr, str - ptr); 617 if (STRINGP(cdr)) { 618 namestr = THESTR(cdr); 619 namelen = STRLEN(cdr); 620 } 621 else { 622 namestr = ""; 623 namelen = 0; 624 } 625 RPLACD(cons, CONS(cdr, NIL)); 626 cons = CDR(cons); 627 628 /* type */ 629 if (defaults != NIL) 630 defaults = CDR(defaults); 631 cdr = defaults == NIL ? NIL : CAR(defaults); 632 ptr = str + typelen; 633 if (*ptr) 634 cdr = LSTRING(ptr, send - ptr); 635 if (STRINGP(cdr)) { 636 typestr = THESTR(cdr); 637 typelen = STRLEN(cdr); 638 } 639 else { 640 typestr = ""; 641 typelen = 0; 642 } 643 RPLACD(cons, CONS(cdr, NIL)); 644 cons = CDR(cons); 645 646 /* version */ 647 if (defaults != NIL) 648 defaults = CDR(defaults); 649 cdr = defaults == NIL ? NIL : CAR(defaults); 650 RPLACD(cons, CONS(cdr, NIL)); 651 652 /* string representation, must be done here to use defaults */ 653 for (ptr = string + alength; ptr >= string; ptr--) { 654 if (*ptr == PATH_SEP) 655 break; 656 } 657 if (ptr >= string) 658 ++ptr; 659 else 660 ptr = string; 661 *ptr = '\0'; 662 663 length = ptr - string; 664 665 alength = namelen; 666 if (alength) { 667 if (length + alength + 2 > sizeof(string)) 668 alength = sizeof(string) - length - 2; 669 memcpy(string + length, namestr, alength); 670 length += alength; 671 } 672 673 alength = typelen; 674 if (alength) { 675 if (length + 2 < sizeof(string)) 676 string[length++] = PATH_TYPESEP; 677 if (length + alength + 2 > sizeof(string)) 678 alength = sizeof(string) - length - 2; 679 memcpy(string + length, typestr, alength); 680 length += alength; 681 } 682 string[length] = '\0'; 683 684 RPLACA(result, LSTRING(string, length)); 685 RETURN(0) = FIXNUM(end); 686 687 result = PATHNAME(result); 688 } 689 else if (junk_allowed == UNSPEC || junk_allowed == NIL) 690 LispDestroy("%s: bad argument %s", STRFUN(builtin), STROBJ(object)); 691 else 692 RETURN(0) = NIL; 693 694 GC_LEAVE(); 695 696 return (result); 697} 698 699LispObj * 700Lisp_MakePathname(LispBuiltin *builtin) 701/* 702 make-pathname &key host device directory name type version defaults 703 */ 704{ 705 GC_ENTER(); 706 int length, alength; 707 char *string, pathname[PATH_MAX + 1]; 708 LispObj *result, *cdr, *cons; 709 710 LispObj *host, *device, *directory, *name, *type, *version, *defaults; 711 712 defaults = ARGUMENT(6); 713 version = ARGUMENT(5); 714 type = ARGUMENT(4); 715 name = ARGUMENT(3); 716 directory = ARGUMENT(2); 717 device = ARGUMENT(1); 718 host = ARGUMENT(0); 719 720 if (host != UNSPEC) { 721 CHECK_STRING(host); 722 } 723 if (device != UNSPEC) { 724 CHECK_STRING(device); 725 } 726 727 if (directory != UNSPEC) { 728 LispObj *dir; 729 730 CHECK_CONS(directory); 731 dir = CAR(directory); 732 CHECK_KEYWORD(dir); 733 if (dir != Kabsolute && dir != Krelative) 734 LispDestroy("%s: directory type %s unknown", 735 STRFUN(builtin), STROBJ(dir)); 736 } 737 738 if (name != UNSPEC) { 739 CHECK_STRING(name); 740 } 741 if (type != UNSPEC) { 742 CHECK_STRING(type); 743 } 744 745 if (version != UNSPEC && version != NIL) { 746 switch (OBJECT_TYPE(version)) { 747 case LispFixnum_t: 748 if (FIXNUM_VALUE(version) >= 0) 749 goto version_ok; 750 case LispInteger_t: 751 if (INT_VALUE(version) >= 0) 752 goto version_ok; 753 break; 754 case LispDFloat_t: 755 if (DFLOAT_VALUE(version) >= 0.0) 756 goto version_ok; 757 break; 758 default: 759 break; 760 } 761 LispDestroy("%s: %s is not a positive real number", 762 STRFUN(builtin), STROBJ(version)); 763 } 764version_ok: 765 766 if (defaults != UNSPEC && !PATHNAMEP(defaults) && 767 (host == UNSPEC || device == UNSPEC || directory == UNSPEC || 768 name == UNSPEC || type == UNSPEC || version == UNSPEC)) { 769 defaults = APPLY1(Oparse_namestring, defaults); 770 GC_PROTECT(defaults); 771 } 772 773 if (defaults != UNSPEC) { 774 defaults = defaults->data.pathname; 775 defaults = CDR(defaults); /* host */ 776 if (host == UNSPEC) 777 host = CAR(defaults); 778 defaults = CDR(defaults); /* device */ 779 if (device == UNSPEC) 780 device = CAR(defaults); 781 defaults = CDR(defaults); /* directory */ 782 if (directory == UNSPEC) 783 directory = CAR(defaults); 784 defaults = CDR(defaults); /* name */ 785 if (name == UNSPEC) 786 name = CAR(defaults); 787 defaults = CDR(defaults); /* type */ 788 if (type == UNSPEC) 789 type = CAR(defaults); 790 defaults = CDR(defaults); /* version */ 791 if (version == UNSPEC) 792 version = CAR(defaults); 793 } 794 795 /* string representation */ 796 length = 0; 797 if (CONSP(directory)) { 798 if (CAR(directory) == Kabsolute) 799 pathname[length++] = PATH_SEP; 800 801 for (cdr = CDR(directory); CONSP(cdr); cdr = CDR(cdr)) { 802 CHECK_STRING(CAR(cdr)); 803 string = THESTR(CAR(cdr)); 804 alength = STRLEN(CAR(cdr)); 805 if (alength > NAME_MAX) 806 LispDestroy("%s: directory name too long %s", 807 STRFUN(builtin), string); 808 if (length + alength + 2 > sizeof(pathname)) 809 alength = sizeof(pathname) - length - 2; 810 memcpy(pathname + length, string, alength); 811 length += alength; 812 pathname[length++] = PATH_SEP; 813 } 814 } 815 if (STRINGP(name)) { 816 int xlength = 0; 817 818 if (STRINGP(type)) 819 xlength = STRLEN(type) + 1; 820 821 string = THESTR(name); 822 alength = STRLEN(name); 823 if (alength + xlength > NAME_MAX) 824 LispDestroy("%s: file name too long %s", 825 STRFUN(builtin), string); 826 if (length + alength + 2 > sizeof(pathname)) 827 alength = sizeof(pathname) - length - 2; 828 memcpy(pathname + length, string, alength); 829 length += alength; 830 } 831 if (STRINGP(type)) { 832 if (length + 2 < sizeof(pathname)) 833 pathname[length++] = PATH_TYPESEP; 834 string = THESTR(type); 835 alength = STRLEN(type); 836 if (length + alength + 2 > sizeof(pathname)) 837 alength = sizeof(pathname) - length - 2; 838 memcpy(pathname + length, string, alength); 839 length += alength; 840 } 841 pathname[length] = '\0'; 842 result = cons = CONS(LSTRING(pathname, length), NIL); 843 GC_PROTECT(result); 844 845 /* host */ 846 RPLACD(cons, CONS(host == UNSPEC ? NIL : host, NIL)); 847 cons = CDR(cons); 848 849 /* device */ 850 RPLACD(cons, CONS(device == UNSPEC ? NIL : device, NIL)); 851 cons = CDR(cons); 852 853 /* directory */ 854 if (directory == UNSPEC) 855 cdr = CONS(Krelative, NIL); 856 else 857 cdr = directory; 858 RPLACD(cons, CONS(cdr, NIL)); 859 cons = CDR(cons); 860 861 /* name */ 862 RPLACD(cons, CONS(name == UNSPEC ? NIL : name, NIL)); 863 cons = CDR(cons); 864 865 /* type */ 866 RPLACD(cons, CONS(type == UNSPEC ? NIL : type, NIL)); 867 cons = CDR(cons); 868 869 /* version */ 870 RPLACD(cons, CONS(version == UNSPEC ? NIL : version, NIL)); 871 872 GC_LEAVE(); 873 874 return (PATHNAME(result)); 875} 876 877LispObj * 878Lisp_PathnameHost(LispBuiltin *builtin) 879/* 880 pathname-host pathname 881 */ 882{ 883 return (LispPathnameField(PATH_HOST, 0)); 884} 885 886LispObj * 887Lisp_PathnameDevice(LispBuiltin *builtin) 888/* 889 pathname-device pathname 890 */ 891{ 892 return (LispPathnameField(PATH_DEVICE, 0)); 893} 894 895LispObj * 896Lisp_PathnameDirectory(LispBuiltin *builtin) 897/* 898 pathname-device pathname 899 */ 900{ 901 return (LispPathnameField(PATH_DIRECTORY, 0)); 902} 903 904LispObj * 905Lisp_PathnameName(LispBuiltin *builtin) 906/* 907 pathname-name pathname 908 */ 909{ 910 return (LispPathnameField(PATH_NAME, 0)); 911} 912 913LispObj * 914Lisp_PathnameType(LispBuiltin *builtin) 915/* 916 pathname-type pathname 917 */ 918{ 919 return (LispPathnameField(PATH_TYPE, 0)); 920} 921 922LispObj * 923Lisp_PathnameVersion(LispBuiltin *builtin) 924/* 925 pathname-version pathname 926 */ 927{ 928 return (LispPathnameField(PATH_VERSION, 0)); 929} 930 931LispObj * 932Lisp_FileNamestring(LispBuiltin *builtin) 933/* 934 file-namestring pathname 935 */ 936{ 937 return (LispPathnameField(PATH_NAME, 1)); 938} 939 940LispObj * 941Lisp_DirectoryNamestring(LispBuiltin *builtin) 942/* 943 directory-namestring pathname 944 */ 945{ 946 return (LispPathnameField(PATH_DIRECTORY, 1)); 947} 948 949LispObj * 950Lisp_EnoughNamestring(LispBuiltin *builtin) 951/* 952 enough-pathname pathname &optional defaults 953 */ 954{ 955 LispObj *pathname, *defaults; 956 957 defaults = ARGUMENT(1); 958 pathname = ARGUMENT(0); 959 960 if (defaults != UNSPEC && defaults != NIL) { 961 char *ppathname, *pdefaults, *pp, *pd; 962 963 if (!STRINGP(pathname)) { 964 if (PATHNAMEP(pathname)) 965 pathname = CAR(pathname->data.pathname); 966 else if (STREAMP(pathname) && 967 pathname->data.stream.type == LispStreamFile) 968 pathname = CAR(pathname->data.stream.pathname->data.pathname); 969 else 970 LispDestroy("%s: bad PATHNAME %s", 971 STRFUN(builtin), STROBJ(pathname)); 972 } 973 974 if (!STRINGP(defaults)) { 975 if (PATHNAMEP(defaults)) 976 defaults = CAR(defaults->data.pathname); 977 else if (STREAMP(defaults) && 978 defaults->data.stream.type == LispStreamFile) 979 defaults = CAR(defaults->data.stream.pathname->data.pathname); 980 else 981 LispDestroy("%s: bad DEFAULTS %s", 982 STRFUN(builtin), STROBJ(defaults)); 983 } 984 985 ppathname = pp = THESTR(pathname); 986 pdefaults = pd = THESTR(defaults); 987 while (*ppathname && *pdefaults && *ppathname == *pdefaults) { 988 ppathname++; 989 pdefaults++; 990 } 991 if (*pdefaults == '\0' && pdefaults > pd) 992 --pdefaults; 993 if (*ppathname && *pdefaults && *pdefaults != PATH_SEP) { 994 --ppathname; 995 while (*ppathname != PATH_SEP && ppathname > pp) 996 --ppathname; 997 if (*ppathname == PATH_SEP) 998 ++ppathname; 999 } 1000 1001 return (STRING(ppathname)); 1002 } 1003 else { 1004 if (STRINGP(pathname)) 1005 return (pathname); 1006 else if (PATHNAMEP(pathname)) 1007 return (CAR(pathname->data.pathname)); 1008 else if (STREAMP(pathname)) { 1009 if (pathname->data.stream.type == LispStreamFile) 1010 return (CAR(pathname->data.stream.pathname->data.pathname)); 1011 } 1012 } 1013 LispDestroy("%s: bad PATHNAME %s", STRFUN(builtin), STROBJ(pathname)); 1014 1015 return (NIL); 1016} 1017 1018LispObj * 1019Lisp_Namestring(LispBuiltin *builtin) 1020/* 1021 namestring pathname 1022 */ 1023{ 1024 return (LispPathnameField(PATH_STRING, 1)); 1025} 1026 1027LispObj * 1028Lisp_HostNamestring(LispBuiltin *builtin) 1029/* 1030 host-namestring pathname 1031 */ 1032{ 1033 return (LispPathnameField(PATH_HOST, 1)); 1034} 1035 1036LispObj * 1037Lisp_Pathnamep(LispBuiltin *builtin) 1038/* 1039 pathnamep object 1040 */ 1041{ 1042 LispObj *object; 1043 1044 object = ARGUMENT(0); 1045 1046 return (PATHNAMEP(object) ? T : NIL); 1047} 1048 1049/* XXX only checks if host is a string and only checks the HOME enviroment 1050 * variable */ 1051LispObj * 1052Lisp_UserHomedirPathname(LispBuiltin *builtin) 1053/* 1054 user-homedir-pathname &optional host 1055 */ 1056{ 1057 GC_ENTER(); 1058 int length; 1059 char *home = getenv("HOME"), data[PATH_MAX + 1]; 1060 LispObj *result; 1061 1062 LispObj *host; 1063 1064 host = ARGUMENT(0); 1065 1066 if (host != UNSPEC && !STRINGP(host)) 1067 LispDestroy("%s: bad hostname %s", STRFUN(builtin), STROBJ(host)); 1068 1069 length = 0; 1070 if (home) { 1071 length = strlen(home); 1072 strncpy(data, home, length); 1073 if (length && home[length - 1] != PATH_SEP) 1074 data[length++] = PATH_SEP; 1075 } 1076 data[length] = '\0'; 1077 1078 result = LSTRING(data, length); 1079 GC_PROTECT(result); 1080 result = APPLY1(Oparse_namestring, result); 1081 GC_LEAVE(); 1082 1083 return (result); 1084} 1085 1086LispObj * 1087Lisp_Truename(LispBuiltin *builtin) 1088{ 1089 return (LispProbeFile(builtin, 0)); 1090} 1091 1092LispObj * 1093Lisp_ProbeFile(LispBuiltin *builtin) 1094{ 1095 return (LispProbeFile(builtin, 1)); 1096} 1097