package.c revision 5dfecf96
1/* 2 * Copyright (c) 2002 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/package.c,v 1.20tsi Exp $ */ 31 32#include "lisp/package.h" 33#include "lisp/private.h" 34 35/* 36 * Prototypes 37 */ 38static int LispDoSymbol(LispObj*, LispAtom*, int, int); 39static LispObj *LispReallyDoSymbols(LispBuiltin*, int, int); 40static LispObj *LispDoSymbols(LispBuiltin*, int, int); 41static LispObj *LispFindSymbol(LispBuiltin*, int); 42static LispObj *LispFindPackageOrDie(LispBuiltin*, LispObj*); 43static void LispDoExport(LispBuiltin*, LispObj*, LispObj*, int); 44static void LispDoImport(LispBuiltin*, LispObj*); 45 46/* 47 * Initialization 48 */ 49extern LispProperty *NOPROPERTY; 50static LispObj *Kinternal, *Kexternal, *Kinherited; 51 52/* 53 * Implementation 54 */ 55void 56LispPackageInit(void) 57{ 58 Kinternal = KEYWORD("INTERNAL"); 59 Kexternal = KEYWORD("EXTERNAL"); 60 Kinherited = KEYWORD("INHERITED"); 61} 62 63LispObj * 64LispFindPackageFromString(char *string) 65{ 66 LispObj *list, *package, *nick; 67 68 for (list = PACK; CONSP(list); list = CDR(list)) { 69 package = CAR(list); 70 if (strcmp(THESTR(package->data.package.name), string) == 0) 71 return (package); 72 for (nick = package->data.package.nicknames; 73 CONSP(nick); nick = CDR(nick)) 74 if (strcmp(THESTR(CAR(nick)), string) == 0) 75 return (package); 76 } 77 78 return (NIL); 79} 80 81LispObj * 82LispFindPackage(LispObj *name) 83{ 84 char *string = NULL; 85 86 if (PACKAGEP(name)) 87 return (name); 88 89 if (SYMBOLP(name)) 90 string = ATOMID(name); 91 else if (STRINGP(name)) 92 string = THESTR(name); 93 else 94 LispDestroy("FIND-PACKAGE: %s is not a string or symbol", STROBJ(name)); 95 96 return (LispFindPackageFromString(string)); 97} 98 99int 100LispCheckAtomString(char *string) 101{ 102 char *ptr; 103 104 if (*string == '\0') 105 return (0); 106 107 for (ptr = string; *ptr; ptr++) { 108 if (islower(*ptr) || strchr("\"\\;#()`'|:", *ptr) || 109 ((ptr == string || ptr[1] == '\0') && strchr(".,@", *ptr))) 110 return (0); 111 } 112 113 return (1); 114} 115 116/* This function is used to avoid some namespace polution caused by the 117 * way builtin functions are created, all function name arguments enter 118 * the current package, but most of them do not have a property */ 119static int 120LispDoSymbol(LispObj *package, LispAtom *atom, int if_extern, int all_packages) 121{ 122 int dosymbol; 123 124 /* condition 1: atom package is current package */ 125 dosymbol = !all_packages || atom->package == package; 126 if (dosymbol) { 127 /* condition 2: intern and extern symbols or symbol is extern */ 128 dosymbol = !if_extern || atom->ext; 129 if (dosymbol) { 130 /* condition 3: atom has properties or is in 131 * the current package */ 132 dosymbol = atom->property != NOPROPERTY || 133 package == lisp__data.keyword || 134 package == PACKAGE; 135 } 136 } 137 138 return (dosymbol); 139} 140 141static LispObj * 142LispFindPackageOrDie(LispBuiltin *builtin, LispObj *name) 143{ 144 LispObj *package; 145 146 package = LispFindPackage(name); 147 148 if (package == NIL) 149 LispDestroy("%s: package %s is not available", 150 STRFUN(builtin), STROBJ(name)); 151 152 return (package); 153} 154 155/* package must be of type LispPackage_t, symbol type is checked 156 bypass lisp.c:LispExportSymbol() */ 157static void 158LispDoExport(LispBuiltin *builtin, 159 LispObj *package, LispObj *symbol, int export) 160{ 161 CHECK_SYMBOL(symbol); 162 if (!export) { 163 if (package == lisp__data.keyword || 164 symbol->data.atom->package == lisp__data.keyword) 165 LispDestroy("%s: symbol %s cannot be unexported", 166 STRFUN(builtin), STROBJ(symbol)); 167 } 168 169 if (package == PACKAGE) 170 symbol->data.atom->ext = export ? 1 : 0; 171 else { 172 int i; 173 char *string; 174 LispAtom *atom; 175 LispPackage *pack; 176 177 string = ATOMID(symbol); 178 pack = package->data.package.package; 179 i = STRHASH(string); 180 atom = pack->atoms[i]; 181 while (atom) { 182 if (strcmp(atom->string, string) == 0) { 183 atom->ext = export ? 1 : 0; 184 return; 185 } 186 187 atom = atom->next; 188 } 189 190 LispDestroy("%s: the symbol %s is not available in package %s", 191 STRFUN(builtin), STROBJ(symbol), 192 THESTR(package->data.package.name)); 193 } 194} 195 196static void 197LispDoImport(LispBuiltin *builtin, LispObj *symbol) 198{ 199 CHECK_SYMBOL(symbol); 200 LispImportSymbol(symbol); 201} 202 203static LispObj * 204LispReallyDoSymbols(LispBuiltin *builtin, int only_externs, int all_symbols) 205{ 206 int i, head = lisp__data.env.length; 207 LispPackage *pack = NULL; 208 LispAtom *atom, *next_atom; 209 LispObj *variable, *package = NULL, *list, *code, *result_form; 210 211 LispObj *init, *body; 212 213 body = ARGUMENT(1); 214 init = ARGUMENT(0); 215 216 /* Prepare for loop */ 217 CHECK_CONS(init); 218 variable = CAR(init); 219 CHECK_SYMBOL(variable); 220 221 if (!all_symbols) { 222 /* if all_symbols, a package name is not specified in the init form */ 223 224 init = CDR(init); 225 if (!CONSP(init)) 226 LispDestroy("%s: missing package name", STRFUN(builtin)); 227 228 /* Evaluate package specification */ 229 package = EVAL(CAR(init)); 230 if (!PACKAGEP(package)) 231 package = LispFindPackageOrDie(builtin, package); 232 233 pack = package->data.package.package; 234 } 235 236 result_form = NIL; 237 238 init = CDR(init); 239 if (CONSP(init)) 240 result_form = init; 241 242 /* Initialize iteration variable */ 243 CHECK_CONSTANT(variable); 244 LispAddVar(variable, NIL); 245 ++lisp__data.env.head; 246 247 for (list = PACK; CONSP(list); list = CDR(list)) { 248 if (all_symbols) { 249 package = CAR(list); 250 pack = package->data.package.package; 251 } 252 253 /* Traverse the symbol list, executing body */ 254 for (i = 0; i < STRTBLSZ; i++) { 255 atom = pack->atoms[i]; 256 while (atom) { 257 /* Save pointer to next atom. If variable is removed, 258 * predicatable result is only guaranteed if the bound 259 * variable is removed. */ 260 next_atom = atom->next; 261 262 if (LispDoSymbol(package, atom, only_externs, all_symbols)) { 263 LispSetVar(variable, atom->object); 264 for (code = body; CONSP(code); code = CDR(code)) 265 EVAL(CAR(code)); 266 } 267 268 atom = next_atom; 269 } 270 } 271 272 if (!all_symbols) 273 break; 274 } 275 276 /* Variable is still bound */ 277 for (code = result_form; CONSP(code); code = CDR(code)) 278 EVAL(CAR(code)); 279 280 lisp__data.env.head = lisp__data.env.length = head; 281 282 return (NIL); 283} 284 285static LispObj * 286LispDoSymbols(LispBuiltin *builtin, int only_externs, int all_symbols) 287{ 288 int did_jump, *pdid_jump = &did_jump; 289 LispObj *result, **presult = &result; 290 LispBlock *block; 291 292 *presult = NIL; 293 *pdid_jump = 1; 294 block = LispBeginBlock(NIL, LispBlockTag); 295 if (setjmp(block->jmp) == 0) { 296 *presult = LispReallyDoSymbols(builtin, only_externs, all_symbols); 297 *pdid_jump = 0; 298 } 299 LispEndBlock(block); 300 if (*pdid_jump) 301 *presult = lisp__data.block.block_ret; 302 303 return (*presult); 304} 305 306LispObj * 307LispFindSymbol(LispBuiltin *builtin, int intern) 308{ 309 int i; 310 char *ptr; 311 LispAtom *atom; 312 LispObj *symbol; 313 LispPackage *pack; 314 315 LispObj *string, *package; 316 317 package = ARGUMENT(1); 318 string = ARGUMENT(0); 319 320 CHECK_STRING(string); 321 if (package != UNSPEC) 322 package = LispFindPackageOrDie(builtin, package); 323 else 324 package = PACKAGE; 325 326 /* If got here, package is a LispPackage_t */ 327 pack = package->data.package.package; 328 329 /* Search symbol in specified package */ 330 ptr = THESTR(string); 331 332 RETURN_COUNT = 1; 333 334 symbol = NULL; 335 /* Fix for current behaviour where NIL and T aren't symbols... */ 336 if (STRLEN(string) == 3 && memcmp(ptr, "NIL", 3) == 0) 337 symbol = NIL; 338 else if (STRLEN(string) == 1 && ptr[0] == 'T') 339 symbol = T; 340 if (symbol) { 341 RETURN(0) = NIL; 342 return (symbol); 343 } 344 345 i = STRHASH(ptr); 346 atom = pack->atoms[i]; 347 while (atom) { 348 if (strcmp(atom->string, ptr) == 0) { 349 symbol = atom->object; 350 break; 351 } 352 atom = atom->next; 353 } 354 355 if (symbol == NULL || symbol->data.atom->package == NULL) { 356 RETURN(0) = NIL; 357 if (intern) { 358 /* symbol does not exist in the specified package, create a new 359 * internal symbol */ 360 361 if (package == PACKAGE) 362 symbol = ATOM(ptr); 363 else { 364 LispPackage *savepack; 365 LispObj *savepackage; 366 367 /* Save package environment */ 368 savepackage = PACKAGE; 369 savepack = lisp__data.pack; 370 371 /* Change package environment */ 372 PACKAGE = package; 373 lisp__data.pack = package->data.package.package; 374 375 symbol = ATOM(ptr); 376 377 /* Restore package environment */ 378 PACKAGE = savepackage; 379 lisp__data.pack = savepack; 380 } 381 382 symbol->data.atom->unreadable = !LispCheckAtomString(ptr); 383 /* If symbol being create in the keyword package, make it external */ 384 if (package == lisp__data.keyword) 385 symbol->data.atom->ext = symbol->data.atom->constant = 1; 386 } 387 else 388 symbol = NIL; 389 } 390 else { 391 if (symbol->data.atom->package == package) 392 RETURN(0) = symbol->data.atom->ext ? Kexternal : Kinternal; 393 else 394 RETURN(0) = Kinherited; 395 } 396 397 return (symbol); 398} 399 400 401LispObj * 402Lisp_DoAllSymbols(LispBuiltin *builtin) 403/* 404 do-all-symbols init &rest body 405 */ 406{ 407 return (LispDoSymbols(builtin, 0, 1)); 408} 409 410LispObj * 411Lisp_DoExternalSymbols(LispBuiltin *builtin) 412/* 413 do-external-symbols init &rest body 414 */ 415{ 416 return (LispDoSymbols(builtin, 1, 0)); 417} 418 419LispObj * 420Lisp_DoSymbols(LispBuiltin *builtin) 421/* 422 do-symbols init &rest body 423 */ 424{ 425 return (LispDoSymbols(builtin, 0, 0)); 426} 427 428LispObj * 429Lisp_FindAllSymbols(LispBuiltin *builtin) 430/* 431 find-all-symbols string-or-symbol 432 */ 433{ 434 GC_ENTER(); 435 char *string = NULL; 436 LispAtom *atom; 437 LispPackage *pack; 438 LispObj *list, *package, *result; 439 int i; 440 441 LispObj *string_or_symbol; 442 443 string_or_symbol = ARGUMENT(0); 444 445 if (STRINGP(string_or_symbol)) 446 string = THESTR(string_or_symbol); 447 else if (SYMBOLP(string_or_symbol)) 448 string = ATOMID(string_or_symbol); 449 else 450 LispDestroy("%s: %s is not a string or symbol", 451 STRFUN(builtin), STROBJ(string_or_symbol)); 452 453 result = NIL; 454 i = STRHASH(string); 455 456 /* Traverse all packages, searching for symbols matching specified string */ 457 for (list = PACK; CONSP(list); list = CDR(list)) { 458 package = CAR(list); 459 pack = package->data.package.package; 460 461 atom = pack->atoms[i]; 462 while (atom) { 463 if (strcmp(atom->string, string) == 0 && 464 LispDoSymbol(package, atom, 0, 1)) { 465 /* Return only one pointer to a matching symbol */ 466 467 if (result == NIL) { 468 result = CONS(atom->object, NIL); 469 GC_PROTECT(result); 470 } 471 else { 472 /* Put symbols defined first in the 473 * beginning of the result list */ 474 RPLACD(result, CONS(CAR(result), CDR(result))); 475 RPLACA(result, atom->object); 476 } 477 } 478 atom = atom->next; 479 } 480 } 481 GC_LEAVE(); 482 483 return (result); 484} 485 486LispObj * 487Lisp_FindSymbol(LispBuiltin *builtin) 488/* 489 find-symbol string &optional package 490 */ 491{ 492 return (LispFindSymbol(builtin, 0)); 493} 494 495LispObj * 496Lisp_FindPackage(LispBuiltin *builtin) 497/* 498 find-package name 499 */ 500{ 501 LispObj *name; 502 503 name = ARGUMENT(0); 504 505 return (LispFindPackage(name)); 506} 507 508LispObj * 509Lisp_Export(LispBuiltin *builtin) 510/* 511 export symbols &optional package 512 */ 513{ 514 LispObj *list; 515 516 LispObj *symbols, *package; 517 518 package = ARGUMENT(1); 519 symbols = ARGUMENT(0); 520 521 /* If specified, make sure package is available */ 522 if (package != UNSPEC) 523 package = LispFindPackageOrDie(builtin, package); 524 else 525 package = PACKAGE; 526 527 /* Export symbols */ 528 if (CONSP(symbols)) { 529 for (list = symbols; CONSP(list); list = CDR(list)) 530 LispDoExport(builtin, package, CAR(list), 1); 531 } 532 else 533 LispDoExport(builtin, package, symbols, 1); 534 535 return (T); 536} 537 538LispObj * 539Lisp_Import(LispBuiltin *builtin) 540/* 541 import symbols &optional package 542 */ 543{ 544 int restore_package; 545 LispPackage *savepack = NULL; 546 LispObj *list, *savepackage = NULL; 547 548 LispObj *symbols, *package; 549 550 package = ARGUMENT(1); 551 symbols = ARGUMENT(0); 552 553 /* If specified, make sure package is available */ 554 if (package != UNSPEC) 555 package = LispFindPackageOrDie(builtin, package); 556 else 557 package = PACKAGE; 558 559 restore_package = package != PACKAGE; 560 if (restore_package) { 561 /* Save package environment */ 562 savepackage = PACKAGE; 563 savepack = lisp__data.pack; 564 565 /* Change package environment */ 566 PACKAGE = package; 567 lisp__data.pack = package->data.package.package; 568 } 569 570 /* Export symbols */ 571 if (CONSP(symbols)) { 572 for (list = symbols; CONSP(list); list = CDR(list)) 573 LispDoImport(builtin, CAR(list)); 574 } 575 else 576 LispDoImport(builtin, symbols); 577 578 if (restore_package) { 579 /* Restore package environment */ 580 PACKAGE = savepackage; 581 lisp__data.pack = savepack; 582 } 583 584 return (T); 585} 586 587LispObj * 588Lisp_InPackage(LispBuiltin *builtin) 589/* 590 in-package name 591 */ 592{ 593 LispObj *package; 594 595 LispObj *name; 596 597 name = ARGUMENT(0); 598 599 package = LispFindPackageOrDie(builtin, name); 600 601 /* Update pointer to package symbol table */ 602 lisp__data.pack = package->data.package.package; 603 PACKAGE = package; 604 605 return (package); 606} 607 608LispObj * 609Lisp_Intern(LispBuiltin *builtin) 610/* 611 intern string &optional package 612 */ 613{ 614 return (LispFindSymbol(builtin, 1)); 615} 616 617LispObj * 618Lisp_ListAllPackages(LispBuiltin *builtin) 619/* 620 list-all-packages 621 */ 622{ 623 /* Maybe this should be read-only or a copy of the package list. 624 * But, if properly implemented, it should be possible to (rplaca) 625 * this variable from lisp code with no problems. Don't do it at home. */ 626 627 return (PACK); 628} 629 630LispObj * 631Lisp_MakePackage(LispBuiltin *builtin) 632/* 633 make-package package-name &key nicknames use 634 */ 635{ 636 GC_ENTER(); 637 LispObj *list, *package, *nicks, *cons, *savepackage; 638 639 LispObj *package_name, *nicknames, *use; 640 641 use = ARGUMENT(2); 642 nicknames = ARGUMENT(1); 643 package_name = ARGUMENT(0); 644 645 /* Check if package already exists */ 646 package = LispFindPackage(package_name); 647 if (package != NIL) 648 /* FIXME: this should be a correctable error */ 649 LispDestroy("%s: package %s already defined", 650 STRFUN(builtin), STROBJ(package_name)); 651 652 /* Error checks done, package_name is either a symbol or string */ 653 if (!XSTRINGP(package_name)) 654 package_name = STRING(ATOMID(package_name)); 655 656 GC_PROTECT(package_name); 657 658 /* Check nicknames */ 659 nicks = cons = NIL; 660 for (list = nicknames; CONSP(list); list = CDR(list)) { 661 package = LispFindPackage(CAR(list)); 662 if (package != NIL) 663 /* FIXME: this should be a correctable error */ 664 LispDestroy("%s: nickname %s matches package %s", 665 STRFUN(builtin), STROBJ(CAR(list)), 666 THESTR(package->data.package.name)); 667 /* Store all nicknames as strings */ 668 package = CAR(list); 669 if (!XSTRINGP(package)) 670 package = STRING(ATOMID(package)); 671 if (nicks == NIL) { 672 nicks = cons = CONS(package, NIL); 673 GC_PROTECT(nicks); 674 } 675 else { 676 RPLACD(cons, CONS(package, NIL)); 677 cons = CDR(cons); 678 } 679 } 680 681 /* Check use list */ 682 for (list = use; CONSP(list); list = CDR(list)) 683 (void)LispFindPackageOrDie(builtin, CAR(list)); 684 685 /* No errors, create new package */ 686 package = LispNewPackage(package_name, nicks); 687 688 /* Update list of packages */ 689 PACK = CONS(package, PACK); 690 691 /* No need for gc protection anymore */ 692 GC_LEAVE(); 693 694 /* Import symbols from use list */ 695 savepackage = PACKAGE; 696 697 /* Update pointer to package symbol table */ 698 lisp__data.pack = package->data.package.package; 699 PACKAGE = package; 700 701 if (use != UNSPEC) { 702 for (list = use; CONSP(list); list = CDR(list)) 703 LispUsePackage(LispFindPackage(CAR(list))); 704 } 705 else 706 LispUsePackage(lisp__data.lisp); 707 708 /* Restore pointer to package symbol table */ 709 lisp__data.pack = savepackage->data.package.package; 710 PACKAGE = savepackage; 711 712 return (package); 713} 714 715LispObj * 716Lisp_Packagep(LispBuiltin *builtin) 717/* 718 packagep object 719 */ 720{ 721 LispObj *object; 722 723 object = ARGUMENT(0); 724 725 return (PACKAGEP(object) ? T : NIL); 726} 727 728LispObj * 729Lisp_PackageName(LispBuiltin *builtin) 730/* 731 package-name package 732 */ 733{ 734 LispObj *package; 735 736 package = ARGUMENT(0); 737 738 package = LispFindPackageOrDie(builtin, package); 739 740 return (package->data.package.name); 741} 742 743LispObj * 744Lisp_PackageNicknames(LispBuiltin *builtin) 745/* 746 package-nicknames package 747 */ 748{ 749 LispObj *package; 750 751 package = ARGUMENT(0); 752 753 package = LispFindPackageOrDie(builtin, package); 754 755 return (package->data.package.nicknames); 756} 757 758LispObj * 759Lisp_PackageUseList(LispBuiltin *builtin) 760/* 761 package-use-list package 762 */ 763{ 764 /* If the variable returned by this function is expected to be changeable, 765 * need to change the layout of the LispPackage structure. */ 766 767 LispPackage *pack; 768 LispObj *package, *use, *cons; 769 770 package = ARGUMENT(0); 771 772 package = LispFindPackageOrDie(builtin, package); 773 774 use = cons = NIL; 775 pack = package->data.package.package; 776 777 if (pack->use.length) { 778 GC_ENTER(); 779 int i = pack->use.length - 1; 780 781 use = cons = CONS(pack->use.pairs[i], NIL); 782 GC_PROTECT(use); 783 for (--i; i >= 0; i--) { 784 RPLACD(cons, CONS(pack->use.pairs[i], NIL)); 785 cons = CDR(cons); 786 } 787 GC_LEAVE(); 788 } 789 790 return (use); 791} 792 793LispObj * 794Lisp_PackageUsedByList(LispBuiltin *builtin) 795/* 796 package-used-by-list package 797 */ 798{ 799 GC_ENTER(); 800 int i; 801 LispPackage *pack; 802 LispObj *package, *other, *used, *cons, *list; 803 804 package = ARGUMENT(0); 805 806 package = LispFindPackageOrDie(builtin, package); 807 808 used = cons = NIL; 809 810 for (list = PACK; CONSP(list); list = CDR(list)) { 811 other = CAR(list); 812 if (package == other) 813 /* Surely package uses itself */ 814 continue; 815 816 pack = other->data.package.package; 817 818 for (i = 0; i < pack->use.length; i++) { 819 if (pack->use.pairs[i] == package) { 820 if (used == NIL) { 821 used = cons = CONS(other, NIL); 822 GC_PROTECT(used); 823 } 824 else { 825 RPLACD(cons, CONS(other, NIL)); 826 cons = CDR(cons); 827 } 828 } 829 } 830 } 831 832 GC_LEAVE(); 833 834 return (used); 835} 836 837LispObj * 838Lisp_Unexport(LispBuiltin *builtin) 839/* 840 unexport symbols &optional package 841 */ 842{ 843 LispObj *list; 844 845 LispObj *symbols, *package; 846 847 package = ARGUMENT(1); 848 symbols = ARGUMENT(0); 849 850 /* If specified, make sure package is available */ 851 if (package != UNSPEC) 852 package = LispFindPackageOrDie(builtin, package); 853 else 854 package = PACKAGE; 855 856 /* Export symbols */ 857 if (CONSP(symbols)) { 858 for (list = symbols; CONSP(list); list = CDR(list)) 859 LispDoExport(builtin, package, CAR(list), 0); 860 } 861 else 862 LispDoExport(builtin, package, symbols, 0); 863 864 return (T); 865} 866