Home | History | Annotate | Line # | Download | only in lisp
      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 Csar Pereira de Andrade
     28  */
     29 
     30 /* $XdotOrg: xc/programs/xedit/lisp/string.c,v 1.2 2004/04/23 19:54:44 eich 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  */
     62 static LispObj *LispCharCompare(LispBuiltin*, int, int);
     63 static LispObj *LispStringCompare(LispBuiltin*, int, int);
     64 static LispObj *LispCharOp(LispBuiltin*, int);
     65 static LispObj *LispStringTrim(LispBuiltin*, int, int, int);
     66 static LispObj *LispStringUpcase(LispBuiltin*, int);
     67 static LispObj *LispStringDowncase(LispBuiltin*, int);
     68 static LispObj *LispStringCapitalize(LispBuiltin*, int);
     69 
     70 /*
     71  * Implementation
     72  */
     73 static LispObj *
     74 LispCharCompare(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 
    141 LispObj *
    142 Lisp_CharLess(LispBuiltin *builtin)
    143 /*
    144  char< character &rest more-characters
    145  */
    146 {
    147     return (LispCharCompare(builtin, CHAR_LESS, 0));
    148 }
    149 
    150 LispObj *
    151 Lisp_CharLessEqual(LispBuiltin *builtin)
    152 /*
    153  char<= character &rest more-characters
    154  */
    155 {
    156     return (LispCharCompare(builtin, CHAR_LESS_EQUAL, 0));
    157 }
    158 
    159 LispObj *
    160 Lisp_CharEqual_(LispBuiltin *builtin)
    161 /*
    162  char= character &rest more-characters
    163  */
    164 {
    165     return (LispCharCompare(builtin, CHAR_EQUAL, 0));
    166 }
    167 
    168 LispObj *
    169 Lisp_CharGreater(LispBuiltin *builtin)
    170 /*
    171  char> character &rest more-characters
    172  */
    173 {
    174     return (LispCharCompare(builtin, CHAR_GREATER, 0));
    175 }
    176 
    177 LispObj *
    178 Lisp_CharGreaterEqual(LispBuiltin *builtin)
    179 /*
    180  char>= character &rest more-characters
    181  */
    182 {
    183     return (LispCharCompare(builtin, CHAR_GREATER_EQUAL, 0));
    184 }
    185 
    186 LispObj *
    187 Lisp_CharNotEqual_(LispBuiltin *builtin)
    188 /*
    189  char/= character &rest more-characters
    190  */
    191 {
    192     return (LispCharCompare(builtin, CHAR_NOT_EQUAL, 0));
    193 }
    194 
    195 LispObj *
    196 Lisp_CharLessp(LispBuiltin *builtin)
    197 /*
    198  char-lessp character &rest more-characters
    199  */
    200 {
    201     return (LispCharCompare(builtin, CHAR_LESS, 1));
    202 }
    203 
    204 LispObj *
    205 Lisp_CharNotGreaterp(LispBuiltin *builtin)
    206 /*
    207  char-not-greaterp character &rest more-characters
    208  */
    209 {
    210     return (LispCharCompare(builtin, CHAR_LESS_EQUAL, 1));
    211 }
    212 
    213 LispObj *
    214 Lisp_CharEqual(LispBuiltin *builtin)
    215 /*
    216  char-equalp character &rest more-characters
    217  */
    218 {
    219     return (LispCharCompare(builtin, CHAR_EQUAL, 1));
    220 }
    221 
    222 LispObj *
    223 Lisp_CharGreaterp(LispBuiltin *builtin)
    224 /*
    225  char-greaterp character &rest more-characters
    226  */
    227 {
    228     return (LispCharCompare(builtin, CHAR_GREATER, 1));
    229 }
    230 
    231 LispObj *
    232 Lisp_CharNotLessp(LispBuiltin *builtin)
    233 /*
    234  char-not-lessp &rest more-characters
    235  */
    236 {
    237     return (LispCharCompare(builtin, CHAR_GREATER_EQUAL, 1));
    238 }
    239 
    240 LispObj *
    241 Lisp_CharNotEqual(LispBuiltin *builtin)
    242 /*
    243  char-not-equal character &rest more-characters
    244  */
    245 {
    246     return (LispCharCompare(builtin, CHAR_NOT_EQUAL, 1));
    247 }
    248 
    249 static LispObj *
    250 LispCharOp(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 
    292 LispObj *
    293 Lisp_AlphaCharP(LispBuiltin *builtin)
    294 /*
    295  alpha-char-p char
    296  */
    297 {
    298     return (LispCharOp(builtin, CHAR_ALPHAP));
    299 }
    300 
    301 LispObj *
    302 Lisp_CharDowncase(LispBuiltin *builtin)
    303 /*
    304  char-downcase character
    305  */
    306 {
    307     return (LispCharOp(builtin, CHAR_DOWNCASE));
    308 }
    309 
    310 LispObj *
    311 Lisp_CharInt(LispBuiltin *builtin)
    312 /*
    313  char-int character
    314  char-code character
    315  */
    316 {
    317     return (LispCharOp(builtin, CHAR_INT));
    318 }
    319 
    320 LispObj *
    321 Lisp_CharUpcase(LispBuiltin *builtin)
    322 /*
    323  char-upcase character
    324  */
    325 {
    326     return (LispCharOp(builtin, CHAR_UPCASE));
    327 }
    328 
    329 LispObj *
    330 Lisp_BothCaseP(LispBuiltin *builtin)
    331 /*
    332  both-case-p character
    333  */
    334 {
    335     return (LispCharOp(builtin, CHAR_BOTHP));
    336 }
    337 
    338 LispObj *
    339 Lisp_UpperCaseP(LispBuiltin *builtin)
    340 /*
    341  upper-case-p character
    342  */
    343 {
    344     return (LispCharOp(builtin, CHAR_UPPERP));
    345 }
    346 
    347 LispObj *
    348 Lisp_LowerCaseP(LispBuiltin *builtin)
    349 /*
    350  upper-case-p character
    351  */
    352 {
    353     return (LispCharOp(builtin, CHAR_LOWERP));
    354 }
    355 
    356 LispObj *
    357 Lisp_GraphicCharP(LispBuiltin *builtin)
    358 /*
    359  graphic-char-p char
    360  */
    361 {
    362     return (LispCharOp(builtin, CHAR_GRAPHICP));
    363 }
    364 
    365 LispObj *
    366 Lisp_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  */
    396 LispObj *
    397 Lisp_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 
    431 LispObj *
    432 Lisp_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 
    444 LispObj *
    445 Lisp_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 
    457 LispObj *
    458 Lisp_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 
    491 LispObj *
    492 Lisp_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 
    525 LispObj *
    526 Lisp_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 */
    544 LispObj *
    545 Lisp_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 
    574 LispObj *
    575 Lisp_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 
    696 LispObj *
    697 Lisp_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 
    709 LispObj *
    710 Lisp_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 */
    723 LispObj *
    724 Lisp_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 
    776 static LispObj *
    777 LispStringTrim(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 
    878 LispObj *
    879 Lisp_StringTrim(LispBuiltin *builtin)
    880 /*
    881  string-trim character-bag string
    882  */
    883 {
    884     return (LispStringTrim(builtin, 1, 1, 0));
    885 }
    886 
    887 LispObj *
    888 Lisp_NstringTrim(LispBuiltin *builtin)
    889 /*
    890  ext::nstring-trim character-bag string
    891  */
    892 {
    893     return (LispStringTrim(builtin, 1, 1, 1));
    894 }
    895 
    896 LispObj *
    897 Lisp_StringLeftTrim(LispBuiltin *builtin)
    898 /*
    899  string-left-trim character-bag string
    900  */
    901 {
    902     return (LispStringTrim(builtin, 1, 0, 0));
    903 }
    904 
    905 LispObj *
    906 Lisp_NstringLeftTrim(LispBuiltin *builtin)
    907 /*
    908  ext::nstring-left-trim character-bag string
    909  */
    910 {
    911     return (LispStringTrim(builtin, 1, 0, 1));
    912 }
    913 
    914 LispObj *
    915 Lisp_StringRightTrim(LispBuiltin *builtin)
    916 /*
    917  string-right-trim character-bag string
    918  */
    919 {
    920     return (LispStringTrim(builtin, 0, 1, 0));
    921 }
    922 
    923 LispObj *
    924 Lisp_NstringRightTrim(LispBuiltin *builtin)
    925 /*
    926  ext::nstring-right-trim character-bag string
    927  */
    928 {
    929     return (LispStringTrim(builtin, 0, 1, 1));
    930 }
    931 
    932 static LispObj *
    933 LispStringCompare(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 
   1007 LispObj *
   1008 Lisp_StringEqual_(LispBuiltin *builtin)
   1009 /*
   1010  string= string1 string2 &key start1 end1 start2 end2
   1011  */
   1012 {
   1013     return (LispStringCompare(builtin, CHAR_EQUAL, 0));
   1014 }
   1015 
   1016 LispObj *
   1017 Lisp_StringLess(LispBuiltin *builtin)
   1018 /*
   1019  string< string1 string2 &key start1 end1 start2 end2
   1020  */
   1021 {
   1022     return (LispStringCompare(builtin, CHAR_LESS, 0));
   1023 }
   1024 
   1025 LispObj *
   1026 Lisp_StringGreater(LispBuiltin *builtin)
   1027 /*
   1028  string> string1 string2 &key start1 end1 start2 end2
   1029  */
   1030 {
   1031     return (LispStringCompare(builtin, CHAR_GREATER, 0));
   1032 }
   1033 
   1034 LispObj *
   1035 Lisp_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 
   1043 LispObj *
   1044 Lisp_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 
   1052 LispObj *
   1053 Lisp_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 
   1061 LispObj *
   1062 Lisp_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 
   1070 LispObj *
   1071 Lisp_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 
   1079 LispObj *
   1080 Lisp_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 
   1088 LispObj *
   1089 Lisp_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 
   1097 LispObj *
   1098 Lisp_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 
   1106 LispObj *
   1107 Lisp_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 
   1115 LispObj *
   1116 LispStringUpcase(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 
   1169 LispObj *
   1170 Lisp_StringUpcase(LispBuiltin *builtin)
   1171 /*
   1172  string-upcase string &key start end
   1173  */
   1174 {
   1175     return (LispStringUpcase(builtin, 0));
   1176 }
   1177 
   1178 LispObj *
   1179 Lisp_NstringUpcase(LispBuiltin *builtin)
   1180 /*
   1181  nstring-upcase string &key start end
   1182  */
   1183 {
   1184     return (LispStringUpcase(builtin, 1));
   1185 }
   1186 
   1187 LispObj *
   1188 LispStringDowncase(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 
   1240 LispObj *
   1241 Lisp_StringDowncase(LispBuiltin *builtin)
   1242 /*
   1243  string-downcase string &key start end
   1244  */
   1245 {
   1246     return (LispStringDowncase(builtin, 0));
   1247 }
   1248 
   1249 LispObj *
   1250 Lisp_NstringDowncase(LispBuiltin *builtin)
   1251 /*
   1252  nstring-downcase string &key start end
   1253  */
   1254 {
   1255     return (LispStringDowncase(builtin, 1));
   1256 }
   1257 
   1258 LispObj *
   1259 LispStringCapitalize(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 
   1334 LispObj *
   1335 Lisp_StringCapitalize(LispBuiltin *builtin)
   1336 /*
   1337  string-capitalize string &key start end
   1338  */
   1339 {
   1340     return (LispStringCapitalize(builtin, 0));
   1341 }
   1342 
   1343 LispObj *
   1344 Lisp_NstringCapitalize(LispBuiltin *builtin)
   1345 /*
   1346  nstring-capitalize string &key start end
   1347  */
   1348 {
   1349     return (LispStringCapitalize(builtin, 1));
   1350 }
   1351 
   1352 LispObj *
   1353 Lisp_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