string.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/* $XdotOrg: app/xedit/lisp/string.c,v 1.3 2004/12/04 00:43:13 kuhn Exp $ */ 31/* $XFree86: xc/programs/xedit/lisp/string.c,v 1.24tsi Exp $ */ 32 33#include "lisp/helper.h" 34#include "lisp/read.h" 35#include "lisp/string.h" 36#include "lisp/private.h" 37#include <ctype.h> 38 39#define CHAR_LESS 1 40#define CHAR_LESS_EQUAL 2 41#define CHAR_EQUAL 3 42#define CHAR_GREATER_EQUAL 4 43#define CHAR_GREATER 5 44#define CHAR_NOT_EQUAL 6 45 46#define CHAR_ALPHAP 1 47#define CHAR_DOWNCASE 2 48#define CHAR_UPCASE 3 49#define CHAR_INT 4 50#define CHAR_BOTHP 5 51#define CHAR_UPPERP 6 52#define CHAR_LOWERP 7 53#define CHAR_GRAPHICP 8 54 55#ifndef MIN 56#define MIN(a, b) ((a) < (b) ? (a) : (b)) 57#endif 58 59/* 60 * Prototypes 61 */ 62static LispObj *LispCharCompare(LispBuiltin*, int, int); 63static LispObj *LispStringCompare(LispBuiltin*, int, int); 64static LispObj *LispCharOp(LispBuiltin*, int); 65static LispObj *LispStringTrim(LispBuiltin*, int, int, int); 66static LispObj *LispStringUpcase(LispBuiltin*, int); 67static LispObj *LispStringDowncase(LispBuiltin*, int); 68static LispObj *LispStringCapitalize(LispBuiltin*, int); 69 70/* 71 * Implementation 72 */ 73static LispObj * 74LispCharCompare(LispBuiltin *builtin, int operation, int ignore_case) 75{ 76 LispObj *object; 77 int cmp, value, next_value; 78 79 LispObj *character, *more_characters; 80 81 more_characters = ARGUMENT(1); 82 character = ARGUMENT(0); 83 84 CHECK_SCHAR(character); 85 value = SCHAR_VALUE(character); 86 if (ignore_case && islower(value)) 87 value = toupper(value); 88 89 if (!CONSP(more_characters)) 90 return (T); 91 92 /* First check if all parameters are characters */ 93 for (object = more_characters; CONSP(object); object = CDR(object)) 94 CHECK_SCHAR(CAR(object)); 95 96 /* All characters in list must be different */ 97 if (operation == CHAR_NOT_EQUAL) { 98 /* Compare all characters */ 99 do { 100 for (object = more_characters; CONSP(object); object = CDR(object)) { 101 character = CAR(object); 102 next_value = SCHAR_VALUE(character); 103 if (ignore_case && islower(next_value)) 104 next_value = toupper(next_value); 105 if (value == next_value) 106 return (NIL); 107 } 108 value = SCHAR_VALUE(CAR(more_characters)); 109 if (ignore_case && islower(value)) 110 value = toupper(value); 111 more_characters = CDR(more_characters); 112 } while (CONSP(more_characters)); 113 114 return (T); 115 } 116 117 /* Linearly compare characters */ 118 for (; CONSP(more_characters); more_characters = CDR(more_characters)) { 119 character = CAR(more_characters); 120 next_value = SCHAR_VALUE(character); 121 if (ignore_case && islower(next_value)) 122 next_value = toupper(next_value); 123 124 switch (operation) { 125 case CHAR_LESS: cmp = value < next_value; break; 126 case CHAR_LESS_EQUAL: cmp = value <= next_value; break; 127 case CHAR_EQUAL: cmp = value == next_value; break; 128 case CHAR_GREATER_EQUAL: cmp = value >= next_value; break; 129 case CHAR_GREATER: cmp = value > next_value; break; 130 default: cmp = 0; break; 131 } 132 133 if (!cmp) 134 return (NIL); 135 value = next_value; 136 } 137 138 return (T); 139} 140 141LispObj * 142Lisp_CharLess(LispBuiltin *builtin) 143/* 144 char< character &rest more-characters 145 */ 146{ 147 return (LispCharCompare(builtin, CHAR_LESS, 0)); 148} 149 150LispObj * 151Lisp_CharLessEqual(LispBuiltin *builtin) 152/* 153 char<= character &rest more-characters 154 */ 155{ 156 return (LispCharCompare(builtin, CHAR_LESS_EQUAL, 0)); 157} 158 159LispObj * 160Lisp_CharEqual_(LispBuiltin *builtin) 161/* 162 char= character &rest more-characters 163 */ 164{ 165 return (LispCharCompare(builtin, CHAR_EQUAL, 0)); 166} 167 168LispObj * 169Lisp_CharGreater(LispBuiltin *builtin) 170/* 171 char> character &rest more-characters 172 */ 173{ 174 return (LispCharCompare(builtin, CHAR_GREATER, 0)); 175} 176 177LispObj * 178Lisp_CharGreaterEqual(LispBuiltin *builtin) 179/* 180 char>= character &rest more-characters 181 */ 182{ 183 return (LispCharCompare(builtin, CHAR_GREATER_EQUAL, 0)); 184} 185 186LispObj * 187Lisp_CharNotEqual_(LispBuiltin *builtin) 188/* 189 char/= character &rest more-characters 190 */ 191{ 192 return (LispCharCompare(builtin, CHAR_NOT_EQUAL, 0)); 193} 194 195LispObj * 196Lisp_CharLessp(LispBuiltin *builtin) 197/* 198 char-lessp character &rest more-characters 199 */ 200{ 201 return (LispCharCompare(builtin, CHAR_LESS, 1)); 202} 203 204LispObj * 205Lisp_CharNotGreaterp(LispBuiltin *builtin) 206/* 207 char-not-greaterp character &rest more-characters 208 */ 209{ 210 return (LispCharCompare(builtin, CHAR_LESS_EQUAL, 1)); 211} 212 213LispObj * 214Lisp_CharEqual(LispBuiltin *builtin) 215/* 216 char-equalp character &rest more-characters 217 */ 218{ 219 return (LispCharCompare(builtin, CHAR_EQUAL, 1)); 220} 221 222LispObj * 223Lisp_CharGreaterp(LispBuiltin *builtin) 224/* 225 char-greaterp character &rest more-characters 226 */ 227{ 228 return (LispCharCompare(builtin, CHAR_GREATER, 1)); 229} 230 231LispObj * 232Lisp_CharNotLessp(LispBuiltin *builtin) 233/* 234 char-not-lessp &rest more-characters 235 */ 236{ 237 return (LispCharCompare(builtin, CHAR_GREATER_EQUAL, 1)); 238} 239 240LispObj * 241Lisp_CharNotEqual(LispBuiltin *builtin) 242/* 243 char-not-equal character &rest more-characters 244 */ 245{ 246 return (LispCharCompare(builtin, CHAR_NOT_EQUAL, 1)); 247} 248 249static LispObj * 250LispCharOp(LispBuiltin *builtin, int operation) 251{ 252 int value; 253 LispObj *result, *character; 254 255 character = ARGUMENT(0); 256 CHECK_SCHAR(character); 257 value = (int)SCHAR_VALUE(character); 258 259 switch (operation) { 260 case CHAR_ALPHAP: 261 result = isalpha(value) ? T : NIL; 262 break; 263 case CHAR_DOWNCASE: 264 result = SCHAR(tolower(value)); 265 break; 266 case CHAR_UPCASE: 267 result = SCHAR(toupper(value)); 268 break; 269 case CHAR_INT: 270 result = FIXNUM(value); 271 break; 272 case CHAR_BOTHP: 273 result = isupper(value) || islower(value) ? T : NIL; 274 break; 275 case CHAR_UPPERP: 276 result = isupper(value) ? T : NIL; 277 break; 278 case CHAR_LOWERP: 279 result = islower(value) ? T : NIL; 280 break; 281 case CHAR_GRAPHICP: 282 result = value == ' ' || isgraph(value) ? T : NIL; 283 break; 284 default: 285 result = NIL; 286 break; 287 } 288 289 return (result); 290} 291 292LispObj * 293Lisp_AlphaCharP(LispBuiltin *builtin) 294/* 295 alpha-char-p char 296 */ 297{ 298 return (LispCharOp(builtin, CHAR_ALPHAP)); 299} 300 301LispObj * 302Lisp_CharDowncase(LispBuiltin *builtin) 303/* 304 char-downcase character 305 */ 306{ 307 return (LispCharOp(builtin, CHAR_DOWNCASE)); 308} 309 310LispObj * 311Lisp_CharInt(LispBuiltin *builtin) 312/* 313 char-int character 314 char-code character 315 */ 316{ 317 return (LispCharOp(builtin, CHAR_INT)); 318} 319 320LispObj * 321Lisp_CharUpcase(LispBuiltin *builtin) 322/* 323 char-upcase character 324 */ 325{ 326 return (LispCharOp(builtin, CHAR_UPCASE)); 327} 328 329LispObj * 330Lisp_BothCaseP(LispBuiltin *builtin) 331/* 332 both-case-p character 333 */ 334{ 335 return (LispCharOp(builtin, CHAR_BOTHP)); 336} 337 338LispObj * 339Lisp_UpperCaseP(LispBuiltin *builtin) 340/* 341 upper-case-p character 342 */ 343{ 344 return (LispCharOp(builtin, CHAR_UPPERP)); 345} 346 347LispObj * 348Lisp_LowerCaseP(LispBuiltin *builtin) 349/* 350 upper-case-p character 351 */ 352{ 353 return (LispCharOp(builtin, CHAR_LOWERP)); 354} 355 356LispObj * 357Lisp_GraphicCharP(LispBuiltin *builtin) 358/* 359 graphic-char-p char 360 */ 361{ 362 return (LispCharOp(builtin, CHAR_GRAPHICP)); 363} 364 365LispObj * 366Lisp_Char(LispBuiltin *builtin) 367/* 368 char string index 369 schar simple-string index 370 */ 371{ 372 unsigned char *string; 373 long offset, length; 374 375 LispObj *ostring, *oindex; 376 377 oindex = ARGUMENT(1); 378 ostring = ARGUMENT(0); 379 380 CHECK_STRING(ostring); 381 CHECK_INDEX(oindex); 382 offset = FIXNUM_VALUE(oindex); 383 string = (unsigned char*)THESTR(ostring); 384 length = STRLEN(ostring); 385 386 if (offset >= length) 387 LispDestroy("%s: index %ld too large for string length %ld", 388 STRFUN(builtin), offset, length); 389 390 return (SCHAR(string[offset])); 391} 392 393/* helper function for setf 394 * DONT explicitly call. Non standard function 395 */ 396LispObj * 397Lisp_XeditCharStore(LispBuiltin *builtin) 398/* 399 xedit::char-store string index value 400 */ 401{ 402 int character; 403 long offset, length; 404 LispObj *ostring, *oindex, *ovalue; 405 406 ovalue = ARGUMENT(2); 407 oindex = ARGUMENT(1); 408 ostring = ARGUMENT(0); 409 410 CHECK_STRING(ostring); 411 CHECK_INDEX(oindex); 412 length = STRLEN(ostring); 413 offset = FIXNUM_VALUE(oindex); 414 if (offset >= length) 415 LispDestroy("%s: index %ld too large for string length %ld", 416 STRFUN(builtin), offset, length); 417 CHECK_SCHAR(ovalue); 418 CHECK_STRING_WRITABLE(ostring); 419 420 character = SCHAR_VALUE(ovalue); 421 422 if (character < 0 || character > 255) 423 LispDestroy("%s: cannot represent character %d", 424 STRFUN(builtin), character); 425 426 THESTR(ostring)[offset] = character; 427 428 return (ovalue); 429} 430 431LispObj * 432Lisp_Character(LispBuiltin *builtin) 433/* 434 character object 435 */ 436{ 437 LispObj *object; 438 439 object = ARGUMENT(0); 440 441 return (LispCharacterCoerce(builtin, object)); 442} 443 444LispObj * 445Lisp_Characterp(LispBuiltin *builtin) 446/* 447 characterp object 448 */ 449{ 450 LispObj *object; 451 452 object = ARGUMENT(0); 453 454 return (SCHARP(object) ? T : NIL); 455} 456 457LispObj * 458Lisp_DigitChar(LispBuiltin *builtin) 459/* 460 digit-char weight &optional radix 461 */ 462{ 463 long radix = 10, weight; 464 LispObj *oweight, *oradix, *result = NIL; 465 466 oradix = ARGUMENT(1); 467 oweight = ARGUMENT(0); 468 469 CHECK_FIXNUM(oweight); 470 weight = FIXNUM_VALUE(oweight); 471 472 if (oradix != UNSPEC) { 473 CHECK_INDEX(oradix); 474 radix = FIXNUM_VALUE(oradix); 475 } 476 if (radix < 2 || radix > 36) 477 LispDestroy("%s: radix must be >= 2 and <= 36, not %ld", 478 STRFUN(builtin), radix); 479 480 if (weight >= 0 && weight < radix) { 481 if (weight < 9) 482 weight += '0'; 483 else 484 weight += 'A' - 10; 485 result = SCHAR(weight); 486 } 487 488 return (result); 489} 490 491LispObj * 492Lisp_DigitCharP(LispBuiltin *builtin) 493/* 494 digit-char-p character &optional radix 495 */ 496{ 497 long radix = 10, character; 498 LispObj *ochar, *oradix, *result = NIL; 499 500 oradix = ARGUMENT(1); 501 ochar = ARGUMENT(0); 502 503 CHECK_SCHAR(ochar); 504 character = SCHAR_VALUE(ochar); 505 if (oradix != UNSPEC) { 506 CHECK_INDEX(oradix); 507 radix = FIXNUM_VALUE(oradix); 508 } 509 if (radix < 2 || radix > 36) 510 LispDestroy("%s: radix must be >= 2 and <= 36, not %ld", 511 STRFUN(builtin), radix); 512 513 if (character >= '0' && character <= '9') 514 character -= '0'; 515 else if (character >= 'A' && character <= 'Z') 516 character -= 'A' - 10; 517 else if (character >= 'a' && character <= 'z') 518 character -= 'a' - 10; 519 if (character < radix) 520 result = FIXNUM(character); 521 522 return (result); 523} 524 525LispObj * 526Lisp_IntChar(LispBuiltin *builtin) 527/* 528 int-char integer 529 code-char integer 530 */ 531{ 532 long character = 0; 533 LispObj *integer; 534 535 integer = ARGUMENT(0); 536 537 CHECK_FIXNUM(integer); 538 character = FIXNUM_VALUE(integer); 539 540 return (character >= 0 && character < 0xff ? SCHAR(character) : NIL); 541} 542 543/* XXX ignoring element-type */ 544LispObj * 545Lisp_MakeString(LispBuiltin *builtin) 546/* 547 make-string size &key initial-element element-type 548 */ 549{ 550 long length; 551 char *string, initial; 552 553 LispObj *size, *initial_element; 554 555 initial_element = ARGUMENT(1); 556 size = ARGUMENT(0); 557 558 CHECK_INDEX(size); 559 length = FIXNUM_VALUE(size); 560 if (initial_element != UNSPEC) { 561 CHECK_SCHAR(initial_element); 562 initial = SCHAR_VALUE(initial_element); 563 } 564 else 565 initial = 0; 566 567 string = LispMalloc(length + 1); 568 memset(string, initial, length); 569 string[length] = '\0'; 570 571 return (LSTRING2(string, length)); 572} 573 574LispObj * 575Lisp_ParseInteger(LispBuiltin *builtin) 576/* 577 parse-integer string &key start end radix junk-allowed 578 */ 579{ 580 GC_ENTER(); 581 char *ptr, *string; 582 int character, junk, sign, overflow; 583 long i, start, end, radix, length, integer, check; 584 LispObj *result; 585 586 LispObj *ostring, *ostart, *oend, *oradix, *junk_allowed; 587 588 junk_allowed = ARGUMENT(4); 589 oradix = ARGUMENT(3); 590 oend = ARGUMENT(2); 591 ostart = ARGUMENT(1); 592 ostring = ARGUMENT(0); 593 594 start = end = radix = 0; 595 result = NIL; 596 597 CHECK_STRING(ostring); 598 LispCheckSequenceStartEnd(builtin, ostring, ostart, oend, 599 &start, &end, &length); 600 string = THESTR(ostring); 601 if (oradix == UNSPEC) 602 radix = 10; 603 else { 604 CHECK_INDEX(oradix); 605 radix = FIXNUM_VALUE(oradix); 606 } 607 if (radix < 2 || radix > 36) 608 LispDestroy("%s: :RADIX %ld must be in the range 2 to 36", 609 STRFUN(builtin), radix); 610 611 integer = check = 0; 612 ptr = string + start; 613 sign = overflow = 0; 614 615 /* Skip leading white spaces */ 616 for (i = start; i < end && *ptr && isspace(*ptr); ptr++, i++) 617 ; 618 619 /* Check for sign specification */ 620 if (i < end && (*ptr == '-' || *ptr == '+')) { 621 sign = *ptr == '-'; 622 ++ptr; 623 ++i; 624 } 625 626 for (junk = 0; i < end; i++, ptr++) { 627 character = *ptr; 628 if (islower(character)) 629 character = toupper(character); 630 if (character >= '0' && character <= '9') { 631 if (character - '0' >= radix) 632 junk = 1; 633 else { 634 check = integer; 635 integer = integer * radix + character - '0'; 636 } 637 } 638 else if (character >= 'A' && character <= 'Z') { 639 if (character - 'A' + 10 >= radix) 640 junk = 1; 641 else { 642 check = integer; 643 integer = integer * radix + character - 'A' + 10; 644 } 645 } 646 else { 647 if (isspace(character)) 648 break; 649 junk = 1; 650 } 651 652 if (junk) 653 break; 654 655 if (!overflow && check > integer) 656 overflow = 1; 657 /* keep looping just to count read bytes */ 658 } 659 660 if (!junk) 661 /* Skip white spaces */ 662 for (; i < end && *ptr && isspace(*ptr); ptr++, i++) 663 ; 664 665 if ((junk || ptr == string) && 666 (junk_allowed == UNSPEC || junk_allowed == NIL)) 667 LispDestroy("%s: %s has a bad integer representation", 668 STRFUN(builtin), STROBJ(ostring)); 669 else if (ptr == string) 670 result = NIL; 671 else if (overflow) { 672 mpi *bigi = LispMalloc(sizeof(mpi)); 673 char *str; 674 675 length = end - start + sign; 676 str = LispMalloc(length + 1); 677 678 strncpy(str, string - sign, length + sign); 679 str[length + sign] = '\0'; 680 mpi_init(bigi); 681 mpi_setstr(bigi, str, radix); 682 LispFree(str); 683 result = BIGNUM(bigi); 684 } 685 else 686 result = INTEGER(sign ? -integer : integer); 687 688 GC_PROTECT(result); 689 RETURN(0) = FIXNUM(i); 690 RETURN_COUNT = 1; 691 GC_LEAVE(); 692 693 return (result); 694} 695 696LispObj * 697Lisp_String(LispBuiltin *builtin) 698/* 699 string object 700 */ 701{ 702 LispObj *object; 703 704 object = ARGUMENT(0); 705 706 return (LispStringCoerce(builtin, object)); 707} 708 709LispObj * 710Lisp_Stringp(LispBuiltin *builtin) 711/* 712 stringp object 713 */ 714{ 715 LispObj *object; 716 717 object = ARGUMENT(0); 718 719 return (STRINGP(object) ? T : NIL); 720} 721 722/* XXX preserve-whitespace is being ignored */ 723LispObj * 724Lisp_ReadFromString(LispBuiltin *builtin) 725/* 726 read-from-string string &optional eof-error-p eof-value &key start end preserve-whitespace 727 */ 728{ 729 GC_ENTER(); 730 char *string; 731 LispObj *stream, *result; 732 long length, start, end, bytes_read; 733 734 LispObj *ostring, *eof_error_p, *eof_value, *ostart, *oend; 735 736 oend = ARGUMENT(4); 737 ostart = ARGUMENT(3); 738 eof_value = ARGUMENT(2); 739 eof_error_p = ARGUMENT(1); 740 ostring = ARGUMENT(0); 741 742 CHECK_STRING(ostring); 743 string = THESTR(ostring); 744 LispCheckSequenceStartEnd(builtin, ostring, ostart, oend, 745 &start, &end, &length); 746 747 if (start > 0 || end < length) 748 length = end - start; 749 stream = LSTRINGSTREAM(string + start, STREAM_READ, length); 750 751 if (eof_value == UNSPEC) 752 eof_value = NIL; 753 754 LispPushInput(stream); 755 result = LispRead(); 756 /* stream->data.stream.source.string->input is 757 * the offset of the last byte read in string */ 758 bytes_read = stream->data.stream.source.string->input; 759 LispPopInput(stream); 760 761 if (result == NULL) { 762 if (eof_error_p == NIL) 763 result = eof_value; 764 else 765 LispDestroy("%s: unexpected end of input", STRFUN(builtin)); 766 } 767 768 GC_PROTECT(result); 769 RETURN(0) = FIXNUM(start + bytes_read); 770 RETURN_COUNT = 1; 771 GC_LEAVE(); 772 773 return (result); 774} 775 776static LispObj * 777LispStringTrim(LispBuiltin *builtin, int left, int right, int inplace) 778/* 779 string-{,left-,right-}trim character-bag string 780*/ 781{ 782 unsigned char *string; 783 long start, end, length; 784 785 LispObj *ochars, *ostring; 786 787 ostring = ARGUMENT(1); 788 ochars = ARGUMENT(0); 789 790 if (!POINTERP(ochars) || !(XSTRINGP(ochars) || XCONSP(ochars))) { 791 if (ARRAYP(ochars) && ochars->data.array.rank == 1) 792 ochars = ochars->data.array.list; 793 else 794 LispDestroy("%s: %s is not a sequence", 795 STRFUN(builtin), STROBJ(ochars)); 796 } 797 CHECK_STRING(ostring); 798 799 string = (unsigned char*)THESTR(ostring); 800 length = STRLEN(ostring); 801 802 start = 0; 803 end = length; 804 805 if (XSTRINGP(ochars)) { 806 unsigned char *chars = (unsigned char*)THESTR(ochars); 807 long i, clength = STRLEN(ochars); 808 809 if (left) { 810 for (; start < end; start++) { 811 for (i = 0; i < clength; i++) 812 if (string[start] == chars[i]) 813 break; 814 if (i >= clength) 815 break; 816 } 817 } 818 if (right) { 819 for (--end; end >= 0; end--) { 820 for (i = 0; i < clength; i++) 821 if (string[end] == chars[i]) 822 break; 823 if (i >= clength) 824 break; 825 } 826 ++end; 827 } 828 } 829 else { 830 LispObj *ochar, *list; 831 832 if (left) { 833 for (; start < end; start++) { 834 for (list = ochars; CONSP(list); list = CDR(list)) { 835 ochar = CAR(list); 836 if (SCHARP(ochar) && string[start] == SCHAR_VALUE(ochar)) 837 break; 838 } 839 if (!CONSP(list)) 840 break; 841 } 842 } 843 if (right) { 844 for (--end; end >= 0; end--) { 845 for (list = ochars; CONSP(list); list = CDR(list)) { 846 ochar = CAR(list); 847 if (SCHARP(ochar) && string[end] == SCHAR_VALUE(ochar)) 848 break; 849 } 850 if (!CONSP(list)) 851 break; 852 } 853 ++end; 854 } 855 } 856 857 if (start == 0 && end == length) 858 return (ostring); 859 860 length = end - start; 861 862 if (inplace) { 863 CHECK_STRING_WRITABLE(ostring); 864 memmove(string, string + start, length); 865 string[length] = '\0'; 866 STRLEN(ostring) = length; 867 } 868 else { 869 string = LispMalloc(length + 1); 870 memcpy(string, THESTR(ostring) + start, length); 871 string[length] = '\0'; 872 ostring = LSTRING2((char*)string, length); 873 } 874 875 return (ostring); 876} 877 878LispObj * 879Lisp_StringTrim(LispBuiltin *builtin) 880/* 881 string-trim character-bag string 882 */ 883{ 884 return (LispStringTrim(builtin, 1, 1, 0)); 885} 886 887LispObj * 888Lisp_NstringTrim(LispBuiltin *builtin) 889/* 890 ext::nstring-trim character-bag string 891 */ 892{ 893 return (LispStringTrim(builtin, 1, 1, 1)); 894} 895 896LispObj * 897Lisp_StringLeftTrim(LispBuiltin *builtin) 898/* 899 string-left-trim character-bag string 900 */ 901{ 902 return (LispStringTrim(builtin, 1, 0, 0)); 903} 904 905LispObj * 906Lisp_NstringLeftTrim(LispBuiltin *builtin) 907/* 908 ext::nstring-left-trim character-bag string 909 */ 910{ 911 return (LispStringTrim(builtin, 1, 0, 1)); 912} 913 914LispObj * 915Lisp_StringRightTrim(LispBuiltin *builtin) 916/* 917 string-right-trim character-bag string 918 */ 919{ 920 return (LispStringTrim(builtin, 0, 1, 0)); 921} 922 923LispObj * 924Lisp_NstringRightTrim(LispBuiltin *builtin) 925/* 926 ext::nstring-right-trim character-bag string 927 */ 928{ 929 return (LispStringTrim(builtin, 0, 1, 1)); 930} 931 932static LispObj * 933LispStringCompare(LispBuiltin *builtin, int function, int ignore_case) 934{ 935 int cmp1, cmp2; 936 LispObj *fixnum; 937 unsigned char *string1, *string2; 938 long start1, end1, start2, end2, offset, length; 939 940 LispGetStringArgs(builtin, (char**)&string1, (char**)&string2, 941 &start1, &end1, &start2, &end2); 942 943 string1 += start1; 944 string2 += start2; 945 946 if (function == CHAR_EQUAL) { 947 length = end1 - start1; 948 949 if (length != (end2 - start2)) 950 return (NIL); 951 952 if (!ignore_case) 953 return (memcmp(string1, string2, length) ? NIL : T); 954 955 for (; length; length--, string1++, string2++) 956 if (toupper(*string1) != toupper(*string2)) 957 return (NIL); 958 return (T); 959 } 960 961 end1 -= start1; 962 end2 -= start2; 963 length = MIN(end1, end2); 964 for (offset = 0; 965 offset < length; 966 string1++, string2++, offset++, start1++, start2++) { 967 cmp1 = *string1; 968 cmp2 = *string2; 969 if (ignore_case) { 970 cmp1 = toupper(cmp1); 971 cmp2 = toupper(cmp2); 972 } 973 if (cmp1 != cmp2) { 974 fixnum = FIXNUM(start1); 975 switch (function) { 976 case CHAR_LESS: 977 return ((cmp1 < cmp2) ? fixnum : NIL); 978 case CHAR_LESS_EQUAL: 979 return ((cmp1 <= cmp2) ? fixnum : NIL); 980 case CHAR_NOT_EQUAL: 981 return (fixnum); 982 case CHAR_GREATER_EQUAL: 983 return ((cmp1 >= cmp2) ? fixnum : NIL); 984 case CHAR_GREATER: 985 return ((cmp1 > cmp2) ? fixnum : NIL); 986 } 987 } 988 } 989 990 fixnum = FIXNUM(start1); 991 switch (function) { 992 case CHAR_LESS: 993 return (start1 >= end1 && start2 < end2 ? fixnum : NIL); 994 case CHAR_LESS_EQUAL: 995 return (start1 >= end1 ? fixnum : NIL); 996 case CHAR_NOT_EQUAL: 997 return (start1 >= end1 && start2 >= end2 ? NIL : fixnum); 998 case CHAR_GREATER_EQUAL: 999 return (start2 >= end2 ? fixnum : NIL); 1000 case CHAR_GREATER: 1001 return (start2 >= end2 && start1 < end1 ? fixnum : NIL); 1002 } 1003 1004 return (NIL); 1005} 1006 1007LispObj * 1008Lisp_StringEqual_(LispBuiltin *builtin) 1009/* 1010 string= string1 string2 &key start1 end1 start2 end2 1011 */ 1012{ 1013 return (LispStringCompare(builtin, CHAR_EQUAL, 0)); 1014} 1015 1016LispObj * 1017Lisp_StringLess(LispBuiltin *builtin) 1018/* 1019 string< string1 string2 &key start1 end1 start2 end2 1020 */ 1021{ 1022 return (LispStringCompare(builtin, CHAR_LESS, 0)); 1023} 1024 1025LispObj * 1026Lisp_StringGreater(LispBuiltin *builtin) 1027/* 1028 string> string1 string2 &key start1 end1 start2 end2 1029 */ 1030{ 1031 return (LispStringCompare(builtin, CHAR_GREATER, 0)); 1032} 1033 1034LispObj * 1035Lisp_StringLessEqual(LispBuiltin *builtin) 1036/* 1037 string<= string1 string2 &key start1 end1 start2 end2 1038 */ 1039{ 1040 return (LispStringCompare(builtin, CHAR_LESS_EQUAL, 0)); 1041} 1042 1043LispObj * 1044Lisp_StringGreaterEqual(LispBuiltin *builtin) 1045/* 1046 string>= string1 string2 &key start1 end1 start2 end2 1047 */ 1048{ 1049 return (LispStringCompare(builtin, CHAR_GREATER_EQUAL, 0)); 1050} 1051 1052LispObj * 1053Lisp_StringNotEqual_(LispBuiltin *builtin) 1054/* 1055 string/= string1 string2 &key start1 end1 start2 end2 1056 */ 1057{ 1058 return (LispStringCompare(builtin, CHAR_NOT_EQUAL, 0)); 1059} 1060 1061LispObj * 1062Lisp_StringEqual(LispBuiltin *builtin) 1063/* 1064 string-equal string1 string2 &key start1 end1 start2 end2 1065 */ 1066{ 1067 return (LispStringCompare(builtin, CHAR_EQUAL, 1)); 1068} 1069 1070LispObj * 1071Lisp_StringLessp(LispBuiltin *builtin) 1072/* 1073 string-lessp string1 string2 &key start1 end1 start2 end2 1074 */ 1075{ 1076 return (LispStringCompare(builtin, CHAR_LESS, 1)); 1077} 1078 1079LispObj * 1080Lisp_StringGreaterp(LispBuiltin *builtin) 1081/* 1082 string-greaterp string1 string2 &key start1 end1 start2 end2 1083 */ 1084{ 1085 return (LispStringCompare(builtin, CHAR_GREATER, 1)); 1086} 1087 1088LispObj * 1089Lisp_StringNotGreaterp(LispBuiltin *builtin) 1090/* 1091 string-not-greaterp string1 string2 &key start1 end1 start2 end2 1092 */ 1093{ 1094 return (LispStringCompare(builtin, CHAR_LESS_EQUAL, 1)); 1095} 1096 1097LispObj * 1098Lisp_StringNotLessp(LispBuiltin *builtin) 1099/* 1100 string-not-lessp string1 string2 &key start1 end1 start2 end2 1101 */ 1102{ 1103 return (LispStringCompare(builtin, CHAR_GREATER_EQUAL, 1)); 1104} 1105 1106LispObj * 1107Lisp_StringNotEqual(LispBuiltin *builtin) 1108/* 1109 string-not-equal string1 string2 &key start1 end1 start2 end2 1110 */ 1111{ 1112 return (LispStringCompare(builtin, CHAR_NOT_EQUAL, 1)); 1113} 1114 1115LispObj * 1116LispStringUpcase(LispBuiltin *builtin, int inplace) 1117/* 1118 string-upcase string &key start end 1119 nstring-upcase string &key start end 1120 */ 1121{ 1122 LispObj *result; 1123 char *string, *newstring; 1124 long start, end, length, offset; 1125 1126 LispObj *ostring, *ostart, *oend; 1127 1128 oend = ARGUMENT(2); 1129 ostart = ARGUMENT(1); 1130 ostring = ARGUMENT(0); 1131 CHECK_STRING(ostring); 1132 LispCheckSequenceStartEnd(builtin, ostring, ostart, oend, 1133 &start, &end, &offset); 1134 result = ostring; 1135 string = THESTR(ostring); 1136 length = STRLEN(ostring); 1137 1138 /* first check if something need to be done */ 1139 for (offset = start; offset < end; offset++) 1140 if (string[offset] != toupper(string[offset])) 1141 break; 1142 1143 if (offset >= end) 1144 return (result); 1145 1146 if (inplace) { 1147 CHECK_STRING_WRITABLE(ostring); 1148 newstring = string; 1149 } 1150 else { 1151 /* upcase a copy of argument */ 1152 newstring = LispMalloc(length + 1); 1153 if (offset) 1154 memcpy(newstring, string, offset); 1155 if (length > end) 1156 memcpy(newstring + end, string + end, length - end); 1157 newstring[length] = '\0'; 1158 } 1159 1160 for (; offset < end; offset++) 1161 newstring[offset] = toupper(string[offset]); 1162 1163 if (!inplace) 1164 result = LSTRING2(newstring, length); 1165 1166 return (result); 1167} 1168 1169LispObj * 1170Lisp_StringUpcase(LispBuiltin *builtin) 1171/* 1172 string-upcase string &key start end 1173 */ 1174{ 1175 return (LispStringUpcase(builtin, 0)); 1176} 1177 1178LispObj * 1179Lisp_NstringUpcase(LispBuiltin *builtin) 1180/* 1181 nstring-upcase string &key start end 1182 */ 1183{ 1184 return (LispStringUpcase(builtin, 1)); 1185} 1186 1187LispObj * 1188LispStringDowncase(LispBuiltin *builtin, int inplace) 1189/* 1190 string-downcase string &key start end 1191 nstring-downcase string &key start end 1192 */ 1193{ 1194 LispObj *result; 1195 char *string, *newstring; 1196 long start, end, length, offset; 1197 1198 LispObj *ostring, *ostart, *oend; 1199 1200 oend = ARGUMENT(2); 1201 ostart = ARGUMENT(1); 1202 ostring = ARGUMENT(0); 1203 CHECK_STRING(ostring); 1204 LispCheckSequenceStartEnd(builtin, ostring, ostart, oend, 1205 &start, &end, &offset); 1206 result = ostring; 1207 string = THESTR(ostring); 1208 length = STRLEN(ostring); 1209 1210 /* first check if something need to be done */ 1211 for (offset = start; offset < end; offset++) 1212 if (string[offset] != tolower(string[offset])) 1213 break; 1214 1215 if (offset >= end) 1216 return (result); 1217 1218 if (inplace) { 1219 CHECK_STRING_WRITABLE(ostring); 1220 newstring = string; 1221 } 1222 else { 1223 /* downcase a copy of argument */ 1224 newstring = LispMalloc(length + 1); 1225 if (offset) 1226 memcpy(newstring, string, offset); 1227 if (length > end) 1228 memcpy(newstring + end, string + end, length - end); 1229 newstring[length] = '\0'; 1230 } 1231 for (; offset < end; offset++) 1232 newstring[offset] = tolower(string[offset]); 1233 1234 if (!inplace) 1235 result = LSTRING2(newstring, length); 1236 1237 return (result); 1238} 1239 1240LispObj * 1241Lisp_StringDowncase(LispBuiltin *builtin) 1242/* 1243 string-downcase string &key start end 1244 */ 1245{ 1246 return (LispStringDowncase(builtin, 0)); 1247} 1248 1249LispObj * 1250Lisp_NstringDowncase(LispBuiltin *builtin) 1251/* 1252 nstring-downcase string &key start end 1253 */ 1254{ 1255 return (LispStringDowncase(builtin, 1)); 1256} 1257 1258LispObj * 1259LispStringCapitalize(LispBuiltin *builtin, int inplace) 1260/* 1261 string-capitalize string &key start end 1262 nstring-capitalize string &key start end 1263 */ 1264{ 1265 LispObj *result; 1266 char *string, *newstring; 1267 long start, end, length, offset, upcase; 1268 1269 LispObj *ostring, *ostart, *oend; 1270 1271 oend = ARGUMENT(2); 1272 ostart = ARGUMENT(1); 1273 ostring = ARGUMENT(0); 1274 CHECK_STRING(ostring); 1275 LispCheckSequenceStartEnd(builtin, ostring, ostart, oend, 1276 &start, &end, &offset); 1277 result = ostring; 1278 string = THESTR(ostring); 1279 length = STRLEN(ostring); 1280 1281 /* first check if something need to be done */ 1282 for (upcase = 1, offset = start; offset < end; offset++) { 1283 if (upcase) { 1284 if (!isalnum(string[offset])) 1285 continue; 1286 if (string[offset] != toupper(string[offset])) 1287 break; 1288 upcase = 0; 1289 } 1290 else { 1291 if (isalnum(string[offset])) { 1292 if (string[offset] != tolower(string[offset])) 1293 break; 1294 } 1295 else 1296 upcase = 1; 1297 } 1298 } 1299 1300 if (offset >= end) 1301 return (result); 1302 1303 if (inplace) { 1304 CHECK_STRING_WRITABLE(ostring); 1305 newstring = string; 1306 } 1307 else { 1308 /* capitalize a copy of argument */ 1309 newstring = LispMalloc(length + 1); 1310 memcpy(newstring, string, length); 1311 newstring[length] = '\0'; 1312 } 1313 for (; offset < end; offset++) { 1314 if (upcase) { 1315 if (!isalnum(string[offset])) 1316 continue; 1317 newstring[offset] = toupper(string[offset]); 1318 upcase = 0; 1319 } 1320 else { 1321 if (isalnum(newstring[offset])) 1322 newstring[offset] = tolower(string[offset]); 1323 else 1324 upcase = 1; 1325 } 1326 } 1327 1328 if (!inplace) 1329 result = LSTRING2(newstring, length); 1330 1331 return (result); 1332} 1333 1334LispObj * 1335Lisp_StringCapitalize(LispBuiltin *builtin) 1336/* 1337 string-capitalize string &key start end 1338 */ 1339{ 1340 return (LispStringCapitalize(builtin, 0)); 1341} 1342 1343LispObj * 1344Lisp_NstringCapitalize(LispBuiltin *builtin) 1345/* 1346 nstring-capitalize string &key start end 1347 */ 1348{ 1349 return (LispStringCapitalize(builtin, 1)); 1350} 1351 1352LispObj * 1353Lisp_StringConcat(LispBuiltin *builtin) 1354/* 1355 string-concat &rest strings 1356 */ 1357{ 1358 char *buffer; 1359 long size, length; 1360 LispObj *object, *string; 1361 1362 LispObj *strings; 1363 1364 strings = ARGUMENT(0); 1365 1366 if (strings == NIL) 1367 return (STRING("")); 1368 1369 for (length = 1, object = strings; CONSP(object); object = CDR(object)) { 1370 string = CAR(object); 1371 CHECK_STRING(string); 1372 length += STRLEN(string); 1373 } 1374 1375 buffer = LispMalloc(length); 1376 1377 for (length = 0, object = strings; CONSP(object); object = CDR(object)) { 1378 string = CAR(object); 1379 size = STRLEN(string); 1380 memcpy(buffer + length, THESTR(string), size); 1381 length += size; 1382 } 1383 buffer[length] = '\0'; 1384 object = LSTRING2(buffer, length); 1385 1386 return (object); 1387} 1388