/* * Copyright (c) 2001 by The XFree86 Project, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of the XFree86 Project shall * not be used in advertising or otherwise to promote the sale, use or other * dealings in this Software without prior written authorization from the * XFree86 Project. * * Author: Paulo César Pereira de Andrade */ /* $XFree86: xc/programs/xedit/lisp/string.c,v 1.24tsi Exp $ */ #include "lisp/helper.h" #include "lisp/read.h" #include "lisp/string.h" #include "lisp/private.h" #include #define CHAR_LESS 1 #define CHAR_LESS_EQUAL 2 #define CHAR_EQUAL 3 #define CHAR_GREATER_EQUAL 4 #define CHAR_GREATER 5 #define CHAR_NOT_EQUAL 6 #define CHAR_ALPHAP 1 #define CHAR_DOWNCASE 2 #define CHAR_UPCASE 3 #define CHAR_INT 4 #define CHAR_BOTHP 5 #define CHAR_UPPERP 6 #define CHAR_LOWERP 7 #define CHAR_GRAPHICP 8 #ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif /* * Prototypes */ static LispObj *LispCharCompare(LispBuiltin*, int, int); static LispObj *LispStringCompare(LispBuiltin*, int, int); static LispObj *LispCharOp(LispBuiltin*, int); static LispObj *LispStringTrim(LispBuiltin*, int, int, int); static LispObj *LispStringUpcase(LispBuiltin*, int); static LispObj *LispStringDowncase(LispBuiltin*, int); static LispObj *LispStringCapitalize(LispBuiltin*, int); /* * Implementation */ static LispObj * LispCharCompare(LispBuiltin *builtin, int operation, int ignore_case) { LispObj *object; int cmp, value, next_value; LispObj *character, *more_characters; more_characters = ARGUMENT(1); character = ARGUMENT(0); CHECK_SCHAR(character); value = SCHAR_VALUE(character); if (ignore_case && islower(value)) value = toupper(value); if (!CONSP(more_characters)) return (T); /* First check if all parameters are characters */ for (object = more_characters; CONSP(object); object = CDR(object)) CHECK_SCHAR(CAR(object)); /* All characters in list must be different */ if (operation == CHAR_NOT_EQUAL) { /* Compare all characters */ do { for (object = more_characters; CONSP(object); object = CDR(object)) { character = CAR(object); next_value = SCHAR_VALUE(character); if (ignore_case && islower(next_value)) next_value = toupper(next_value); if (value == next_value) return (NIL); } value = SCHAR_VALUE(CAR(more_characters)); if (ignore_case && islower(value)) value = toupper(value); more_characters = CDR(more_characters); } while (CONSP(more_characters)); return (T); } /* Linearly compare characters */ for (; CONSP(more_characters); more_characters = CDR(more_characters)) { character = CAR(more_characters); next_value = SCHAR_VALUE(character); if (ignore_case && islower(next_value)) next_value = toupper(next_value); switch (operation) { case CHAR_LESS: cmp = value < next_value; break; case CHAR_LESS_EQUAL: cmp = value <= next_value; break; case CHAR_EQUAL: cmp = value == next_value; break; case CHAR_GREATER_EQUAL: cmp = value >= next_value; break; case CHAR_GREATER: cmp = value > next_value; break; default: cmp = 0; break; } if (!cmp) return (NIL); value = next_value; } return (T); } LispObj * Lisp_CharLess(LispBuiltin *builtin) /* char< character &rest more-characters */ { return (LispCharCompare(builtin, CHAR_LESS, 0)); } LispObj * Lisp_CharLessEqual(LispBuiltin *builtin) /* char<= character &rest more-characters */ { return (LispCharCompare(builtin, CHAR_LESS_EQUAL, 0)); } LispObj * Lisp_CharEqual_(LispBuiltin *builtin) /* char= character &rest more-characters */ { return (LispCharCompare(builtin, CHAR_EQUAL, 0)); } LispObj * Lisp_CharGreater(LispBuiltin *builtin) /* char> character &rest more-characters */ { return (LispCharCompare(builtin, CHAR_GREATER, 0)); } LispObj * Lisp_CharGreaterEqual(LispBuiltin *builtin) /* char>= character &rest more-characters */ { return (LispCharCompare(builtin, CHAR_GREATER_EQUAL, 0)); } LispObj * Lisp_CharNotEqual_(LispBuiltin *builtin) /* char/= character &rest more-characters */ { return (LispCharCompare(builtin, CHAR_NOT_EQUAL, 0)); } LispObj * Lisp_CharLessp(LispBuiltin *builtin) /* char-lessp character &rest more-characters */ { return (LispCharCompare(builtin, CHAR_LESS, 1)); } LispObj * Lisp_CharNotGreaterp(LispBuiltin *builtin) /* char-not-greaterp character &rest more-characters */ { return (LispCharCompare(builtin, CHAR_LESS_EQUAL, 1)); } LispObj * Lisp_CharEqual(LispBuiltin *builtin) /* char-equalp character &rest more-characters */ { return (LispCharCompare(builtin, CHAR_EQUAL, 1)); } LispObj * Lisp_CharGreaterp(LispBuiltin *builtin) /* char-greaterp character &rest more-characters */ { return (LispCharCompare(builtin, CHAR_GREATER, 1)); } LispObj * Lisp_CharNotLessp(LispBuiltin *builtin) /* char-not-lessp &rest more-characters */ { return (LispCharCompare(builtin, CHAR_GREATER_EQUAL, 1)); } LispObj * Lisp_CharNotEqual(LispBuiltin *builtin) /* char-not-equal character &rest more-characters */ { return (LispCharCompare(builtin, CHAR_NOT_EQUAL, 1)); } static LispObj * LispCharOp(LispBuiltin *builtin, int operation) { int value; LispObj *result, *character; character = ARGUMENT(0); CHECK_SCHAR(character); value = (int)SCHAR_VALUE(character); switch (operation) { case CHAR_ALPHAP: result = isalpha(value) ? T : NIL; break; case CHAR_DOWNCASE: result = SCHAR(tolower(value)); break; case CHAR_UPCASE: result = SCHAR(toupper(value)); break; case CHAR_INT: result = FIXNUM(value); break; case CHAR_BOTHP: result = isupper(value) || islower(value) ? T : NIL; break; case CHAR_UPPERP: result = isupper(value) ? T : NIL; break; case CHAR_LOWERP: result = islower(value) ? T : NIL; break; case CHAR_GRAPHICP: result = value == ' ' || isgraph(value) ? T : NIL; break; default: result = NIL; break; } return (result); } LispObj * Lisp_AlphaCharP(LispBuiltin *builtin) /* alpha-char-p char */ { return (LispCharOp(builtin, CHAR_ALPHAP)); } LispObj * Lisp_CharDowncase(LispBuiltin *builtin) /* char-downcase character */ { return (LispCharOp(builtin, CHAR_DOWNCASE)); } LispObj * Lisp_CharInt(LispBuiltin *builtin) /* char-int character char-code character */ { return (LispCharOp(builtin, CHAR_INT)); } LispObj * Lisp_CharUpcase(LispBuiltin *builtin) /* char-upcase character */ { return (LispCharOp(builtin, CHAR_UPCASE)); } LispObj * Lisp_BothCaseP(LispBuiltin *builtin) /* both-case-p character */ { return (LispCharOp(builtin, CHAR_BOTHP)); } LispObj * Lisp_UpperCaseP(LispBuiltin *builtin) /* upper-case-p character */ { return (LispCharOp(builtin, CHAR_UPPERP)); } LispObj * Lisp_LowerCaseP(LispBuiltin *builtin) /* upper-case-p character */ { return (LispCharOp(builtin, CHAR_LOWERP)); } LispObj * Lisp_GraphicCharP(LispBuiltin *builtin) /* graphic-char-p char */ { return (LispCharOp(builtin, CHAR_GRAPHICP)); } LispObj * Lisp_Char(LispBuiltin *builtin) /* char string index schar simple-string index */ { unsigned char *string; long offset, length; LispObj *ostring, *oindex; oindex = ARGUMENT(1); ostring = ARGUMENT(0); CHECK_STRING(ostring); CHECK_INDEX(oindex); offset = FIXNUM_VALUE(oindex); string = (unsigned char*)THESTR(ostring); length = STRLEN(ostring); if (offset >= length) LispDestroy("%s: index %ld too large for string length %ld", STRFUN(builtin), offset, length); return (SCHAR(string[offset])); } /* helper function for setf * DONT explicitly call. Non standard function */ LispObj * Lisp_XeditCharStore(LispBuiltin *builtin) /* xedit::char-store string index value */ { int character; long offset, length; LispObj *ostring, *oindex, *ovalue; ovalue = ARGUMENT(2); oindex = ARGUMENT(1); ostring = ARGUMENT(0); CHECK_STRING(ostring); CHECK_INDEX(oindex); length = STRLEN(ostring); offset = FIXNUM_VALUE(oindex); if (offset >= length) LispDestroy("%s: index %ld too large for string length %ld", STRFUN(builtin), offset, length); CHECK_SCHAR(ovalue); CHECK_STRING_WRITABLE(ostring); character = SCHAR_VALUE(ovalue); if (character < 0 || character > 255) LispDestroy("%s: cannot represent character %d", STRFUN(builtin), character); THESTR(ostring)[offset] = character; return (ovalue); } LispObj * Lisp_Character(LispBuiltin *builtin) /* character object */ { LispObj *object; object = ARGUMENT(0); return (LispCharacterCoerce(builtin, object)); } LispObj * Lisp_Characterp(LispBuiltin *builtin) /* characterp object */ { LispObj *object; object = ARGUMENT(0); return (SCHARP(object) ? T : NIL); } LispObj * Lisp_DigitChar(LispBuiltin *builtin) /* digit-char weight &optional radix */ { long radix = 10, weight; LispObj *oweight, *oradix, *result = NIL; oradix = ARGUMENT(1); oweight = ARGUMENT(0); CHECK_FIXNUM(oweight); weight = FIXNUM_VALUE(oweight); if (oradix != UNSPEC) { CHECK_INDEX(oradix); radix = FIXNUM_VALUE(oradix); } if (radix < 2 || radix > 36) LispDestroy("%s: radix must be >= 2 and <= 36, not %ld", STRFUN(builtin), radix); if (weight >= 0 && weight < radix) { if (weight < 9) weight += '0'; else weight += 'A' - 10; result = SCHAR(weight); } return (result); } LispObj * Lisp_DigitCharP(LispBuiltin *builtin) /* digit-char-p character &optional radix */ { long radix = 10, character; LispObj *ochar, *oradix, *result = NIL; oradix = ARGUMENT(1); ochar = ARGUMENT(0); CHECK_SCHAR(ochar); character = SCHAR_VALUE(ochar); if (oradix != UNSPEC) { CHECK_INDEX(oradix); radix = FIXNUM_VALUE(oradix); } if (radix < 2 || radix > 36) LispDestroy("%s: radix must be >= 2 and <= 36, not %ld", STRFUN(builtin), radix); if (character >= '0' && character <= '9') character -= '0'; else if (character >= 'A' && character <= 'Z') character -= 'A' - 10; else if (character >= 'a' && character <= 'z') character -= 'a' - 10; if (character < radix) result = FIXNUM(character); return (result); } LispObj * Lisp_IntChar(LispBuiltin *builtin) /* int-char integer code-char integer */ { long character = 0; LispObj *integer; integer = ARGUMENT(0); CHECK_FIXNUM(integer); character = FIXNUM_VALUE(integer); return (character >= 0 && character < 0xff ? SCHAR(character) : NIL); } /* XXX ignoring element-type */ LispObj * Lisp_MakeString(LispBuiltin *builtin) /* make-string size &key initial-element element-type */ { long length; char *string, initial; LispObj *size, *initial_element; initial_element = ARGUMENT(1); size = ARGUMENT(0); CHECK_INDEX(size); length = FIXNUM_VALUE(size); if (initial_element != UNSPEC) { CHECK_SCHAR(initial_element); initial = SCHAR_VALUE(initial_element); } else initial = 0; string = LispMalloc(length + 1); memset(string, initial, length); string[length] = '\0'; return (LSTRING2(string, length)); } LispObj * Lisp_ParseInteger(LispBuiltin *builtin) /* parse-integer string &key start end radix junk-allowed */ { GC_ENTER(); char *ptr, *string; int character, junk, sign, overflow; long i, start, end, radix, length, integer, check; LispObj *result; LispObj *ostring, *ostart, *oend, *oradix, *junk_allowed; junk_allowed = ARGUMENT(4); oradix = ARGUMENT(3); oend = ARGUMENT(2); ostart = ARGUMENT(1); ostring = ARGUMENT(0); start = end = radix = 0; result = NIL; CHECK_STRING(ostring); LispCheckSequenceStartEnd(builtin, ostring, ostart, oend, &start, &end, &length); string = THESTR(ostring); if (oradix == UNSPEC) radix = 10; else { CHECK_INDEX(oradix); radix = FIXNUM_VALUE(oradix); } if (radix < 2 || radix > 36) LispDestroy("%s: :RADIX %ld must be in the range 2 to 36", STRFUN(builtin), radix); integer = check = 0; ptr = string + start; sign = overflow = 0; /* Skip leading white spaces */ for (i = start; i < end && *ptr && isspace(*ptr); ptr++, i++) ; /* Check for sign specification */ if (i < end && (*ptr == '-' || *ptr == '+')) { sign = *ptr == '-'; ++ptr; ++i; } for (junk = 0; i < end; i++, ptr++) { character = *ptr; if (islower(character)) character = toupper(character); if (character >= '0' && character <= '9') { if (character - '0' >= radix) junk = 1; else { check = integer; integer = integer * radix + character - '0'; } } else if (character >= 'A' && character <= 'Z') { if (character - 'A' + 10 >= radix) junk = 1; else { check = integer; integer = integer * radix + character - 'A' + 10; } } else { if (isspace(character)) break; junk = 1; } if (junk) break; if (!overflow && check > integer) overflow = 1; /* keep looping just to count read bytes */ } if (!junk) /* Skip white spaces */ for (; i < end && *ptr && isspace(*ptr); ptr++, i++) ; if ((junk || ptr == string) && (junk_allowed == UNSPEC || junk_allowed == NIL)) LispDestroy("%s: %s has a bad integer representation", STRFUN(builtin), STROBJ(ostring)); else if (ptr == string) result = NIL; else if (overflow) { mpi *bigi = LispMalloc(sizeof(mpi)); char *str; length = end - start + sign; str = LispMalloc(length + 1); strncpy(str, string - sign, length + sign); str[length + sign] = '\0'; mpi_init(bigi); mpi_setstr(bigi, str, radix); LispFree(str); result = BIGNUM(bigi); } else result = INTEGER(sign ? -integer : integer); GC_PROTECT(result); RETURN(0) = FIXNUM(i); RETURN_COUNT = 1; GC_LEAVE(); return (result); } LispObj * Lisp_String(LispBuiltin *builtin) /* string object */ { LispObj *object; object = ARGUMENT(0); return (LispStringCoerce(builtin, object)); } LispObj * Lisp_Stringp(LispBuiltin *builtin) /* stringp object */ { LispObj *object; object = ARGUMENT(0); return (STRINGP(object) ? T : NIL); } /* XXX preserve-whitespace is being ignored */ LispObj * Lisp_ReadFromString(LispBuiltin *builtin) /* read-from-string string &optional eof-error-p eof-value &key start end preserve-whitespace */ { GC_ENTER(); char *string; LispObj *stream, *result; long length, start, end, bytes_read; LispObj *ostring, *eof_error_p, *eof_value, *ostart, *oend; oend = ARGUMENT(4); ostart = ARGUMENT(3); eof_value = ARGUMENT(2); eof_error_p = ARGUMENT(1); ostring = ARGUMENT(0); CHECK_STRING(ostring); string = THESTR(ostring); LispCheckSequenceStartEnd(builtin, ostring, ostart, oend, &start, &end, &length); if (start > 0 || end < length) length = end - start; stream = LSTRINGSTREAM(string + start, STREAM_READ, length); if (eof_value == UNSPEC) eof_value = NIL; LispPushInput(stream); result = LispRead(); /* stream->data.stream.source.string->input is * the offset of the last byte read in string */ bytes_read = stream->data.stream.source.string->input; LispPopInput(stream); if (result == NULL) { if (eof_error_p == NIL) result = eof_value; else LispDestroy("%s: unexpected end of input", STRFUN(builtin)); } GC_PROTECT(result); RETURN(0) = FIXNUM(start + bytes_read); RETURN_COUNT = 1; GC_LEAVE(); return (result); } static LispObj * LispStringTrim(LispBuiltin *builtin, int left, int right, int inplace) /* string-{,left-,right-}trim character-bag string */ { unsigned char *string; long start, end, length; LispObj *ochars, *ostring; ostring = ARGUMENT(1); ochars = ARGUMENT(0); if (!POINTERP(ochars) || !(XSTRINGP(ochars) || XCONSP(ochars))) { if (ARRAYP(ochars) && ochars->data.array.rank == 1) ochars = ochars->data.array.list; else LispDestroy("%s: %s is not a sequence", STRFUN(builtin), STROBJ(ochars)); } CHECK_STRING(ostring); string = (unsigned char*)THESTR(ostring); length = STRLEN(ostring); start = 0; end = length; if (XSTRINGP(ochars)) { unsigned char *chars = (unsigned char*)THESTR(ochars); long i, clength = STRLEN(ochars); if (left) { for (; start < end; start++) { for (i = 0; i < clength; i++) if (string[start] == chars[i]) break; if (i >= clength) break; } } if (right) { for (--end; end >= 0; end--) { for (i = 0; i < clength; i++) if (string[end] == chars[i]) break; if (i >= clength) break; } ++end; } } else { LispObj *ochar, *list; if (left) { for (; start < end; start++) { for (list = ochars; CONSP(list); list = CDR(list)) { ochar = CAR(list); if (SCHARP(ochar) && string[start] == SCHAR_VALUE(ochar)) break; } if (!CONSP(list)) break; } } if (right) { for (--end; end >= 0; end--) { for (list = ochars; CONSP(list); list = CDR(list)) { ochar = CAR(list); if (SCHARP(ochar) && string[end] == SCHAR_VALUE(ochar)) break; } if (!CONSP(list)) break; } ++end; } } if (start == 0 && end == length) return (ostring); length = end - start; if (inplace) { CHECK_STRING_WRITABLE(ostring); memmove(string, string + start, length); string[length] = '\0'; STRLEN(ostring) = length; } else { string = LispMalloc(length + 1); memcpy(string, THESTR(ostring) + start, length); string[length] = '\0'; ostring = LSTRING2((char*)string, length); } return (ostring); } LispObj * Lisp_StringTrim(LispBuiltin *builtin) /* string-trim character-bag string */ { return (LispStringTrim(builtin, 1, 1, 0)); } LispObj * Lisp_NstringTrim(LispBuiltin *builtin) /* ext::nstring-trim character-bag string */ { return (LispStringTrim(builtin, 1, 1, 1)); } LispObj * Lisp_StringLeftTrim(LispBuiltin *builtin) /* string-left-trim character-bag string */ { return (LispStringTrim(builtin, 1, 0, 0)); } LispObj * Lisp_NstringLeftTrim(LispBuiltin *builtin) /* ext::nstring-left-trim character-bag string */ { return (LispStringTrim(builtin, 1, 0, 1)); } LispObj * Lisp_StringRightTrim(LispBuiltin *builtin) /* string-right-trim character-bag string */ { return (LispStringTrim(builtin, 0, 1, 0)); } LispObj * Lisp_NstringRightTrim(LispBuiltin *builtin) /* ext::nstring-right-trim character-bag string */ { return (LispStringTrim(builtin, 0, 1, 1)); } static LispObj * LispStringCompare(LispBuiltin *builtin, int function, int ignore_case) { int cmp1, cmp2; LispObj *fixnum; unsigned char *string1, *string2; long start1, end1, start2, end2, offset, length; LispGetStringArgs(builtin, (char**)&string1, (char**)&string2, &start1, &end1, &start2, &end2); string1 += start1; string2 += start2; if (function == CHAR_EQUAL) { length = end1 - start1; if (length != (end2 - start2)) return (NIL); if (!ignore_case) return (memcmp(string1, string2, length) ? NIL : T); for (; length; length--, string1++, string2++) if (toupper(*string1) != toupper(*string2)) return (NIL); return (T); } end1 -= start1; end2 -= start2; length = MIN(end1, end2); for (offset = 0; offset < length; string1++, string2++, offset++, start1++, start2++) { cmp1 = *string1; cmp2 = *string2; if (ignore_case) { cmp1 = toupper(cmp1); cmp2 = toupper(cmp2); } if (cmp1 != cmp2) { fixnum = FIXNUM(start1); switch (function) { case CHAR_LESS: return ((cmp1 < cmp2) ? fixnum : NIL); case CHAR_LESS_EQUAL: return ((cmp1 <= cmp2) ? fixnum : NIL); case CHAR_NOT_EQUAL: return (fixnum); case CHAR_GREATER_EQUAL: return ((cmp1 >= cmp2) ? fixnum : NIL); case CHAR_GREATER: return ((cmp1 > cmp2) ? fixnum : NIL); } } } fixnum = FIXNUM(start1); switch (function) { case CHAR_LESS: return (start1 >= end1 && start2 < end2 ? fixnum : NIL); case CHAR_LESS_EQUAL: return (start1 >= end1 ? fixnum : NIL); case CHAR_NOT_EQUAL: return (start1 >= end1 && start2 >= end2 ? NIL : fixnum); case CHAR_GREATER_EQUAL: return (start2 >= end2 ? fixnum : NIL); case CHAR_GREATER: return (start2 >= end2 && start1 < end1 ? fixnum : NIL); } return (NIL); } LispObj * Lisp_StringEqual_(LispBuiltin *builtin) /* string= string1 string2 &key start1 end1 start2 end2 */ { return (LispStringCompare(builtin, CHAR_EQUAL, 0)); } LispObj * Lisp_StringLess(LispBuiltin *builtin) /* string< string1 string2 &key start1 end1 start2 end2 */ { return (LispStringCompare(builtin, CHAR_LESS, 0)); } LispObj * Lisp_StringGreater(LispBuiltin *builtin) /* string> string1 string2 &key start1 end1 start2 end2 */ { return (LispStringCompare(builtin, CHAR_GREATER, 0)); } LispObj * Lisp_StringLessEqual(LispBuiltin *builtin) /* string<= string1 string2 &key start1 end1 start2 end2 */ { return (LispStringCompare(builtin, CHAR_LESS_EQUAL, 0)); } LispObj * Lisp_StringGreaterEqual(LispBuiltin *builtin) /* string>= string1 string2 &key start1 end1 start2 end2 */ { return (LispStringCompare(builtin, CHAR_GREATER_EQUAL, 0)); } LispObj * Lisp_StringNotEqual_(LispBuiltin *builtin) /* string/= string1 string2 &key start1 end1 start2 end2 */ { return (LispStringCompare(builtin, CHAR_NOT_EQUAL, 0)); } LispObj * Lisp_StringEqual(LispBuiltin *builtin) /* string-equal string1 string2 &key start1 end1 start2 end2 */ { return (LispStringCompare(builtin, CHAR_EQUAL, 1)); } LispObj * Lisp_StringLessp(LispBuiltin *builtin) /* string-lessp string1 string2 &key start1 end1 start2 end2 */ { return (LispStringCompare(builtin, CHAR_LESS, 1)); } LispObj * Lisp_StringGreaterp(LispBuiltin *builtin) /* string-greaterp string1 string2 &key start1 end1 start2 end2 */ { return (LispStringCompare(builtin, CHAR_GREATER, 1)); } LispObj * Lisp_StringNotGreaterp(LispBuiltin *builtin) /* string-not-greaterp string1 string2 &key start1 end1 start2 end2 */ { return (LispStringCompare(builtin, CHAR_LESS_EQUAL, 1)); } LispObj * Lisp_StringNotLessp(LispBuiltin *builtin) /* string-not-lessp string1 string2 &key start1 end1 start2 end2 */ { return (LispStringCompare(builtin, CHAR_GREATER_EQUAL, 1)); } LispObj * Lisp_StringNotEqual(LispBuiltin *builtin) /* string-not-equal string1 string2 &key start1 end1 start2 end2 */ { return (LispStringCompare(builtin, CHAR_NOT_EQUAL, 1)); } LispObj * LispStringUpcase(LispBuiltin *builtin, int inplace) /* string-upcase string &key start end nstring-upcase string &key start end */ { LispObj *result; char *string, *newstring; long start, end, length, offset; LispObj *ostring, *ostart, *oend; oend = ARGUMENT(2); ostart = ARGUMENT(1); ostring = ARGUMENT(0); CHECK_STRING(ostring); LispCheckSequenceStartEnd(builtin, ostring, ostart, oend, &start, &end, &offset); result = ostring; string = THESTR(ostring); length = STRLEN(ostring); /* first check if something need to be done */ for (offset = start; offset < end; offset++) if (string[offset] != toupper(string[offset])) break; if (offset >= end) return (result); if (inplace) { CHECK_STRING_WRITABLE(ostring); newstring = string; } else { /* upcase a copy of argument */ newstring = LispMalloc(length + 1); if (offset) memcpy(newstring, string, offset); if (length > end) memcpy(newstring + end, string + end, length - end); newstring[length] = '\0'; } for (; offset < end; offset++) newstring[offset] = toupper(string[offset]); if (!inplace) result = LSTRING2(newstring, length); return (result); } LispObj * Lisp_StringUpcase(LispBuiltin *builtin) /* string-upcase string &key start end */ { return (LispStringUpcase(builtin, 0)); } LispObj * Lisp_NstringUpcase(LispBuiltin *builtin) /* nstring-upcase string &key start end */ { return (LispStringUpcase(builtin, 1)); } LispObj * LispStringDowncase(LispBuiltin *builtin, int inplace) /* string-downcase string &key start end nstring-downcase string &key start end */ { LispObj *result; char *string, *newstring; long start, end, length, offset; LispObj *ostring, *ostart, *oend; oend = ARGUMENT(2); ostart = ARGUMENT(1); ostring = ARGUMENT(0); CHECK_STRING(ostring); LispCheckSequenceStartEnd(builtin, ostring, ostart, oend, &start, &end, &offset); result = ostring; string = THESTR(ostring); length = STRLEN(ostring); /* first check if something need to be done */ for (offset = start; offset < end; offset++) if (string[offset] != tolower(string[offset])) break; if (offset >= end) return (result); if (inplace) { CHECK_STRING_WRITABLE(ostring); newstring = string; } else { /* downcase a copy of argument */ newstring = LispMalloc(length + 1); if (offset) memcpy(newstring, string, offset); if (length > end) memcpy(newstring + end, string + end, length - end); newstring[length] = '\0'; } for (; offset < end; offset++) newstring[offset] = tolower(string[offset]); if (!inplace) result = LSTRING2(newstring, length); return (result); } LispObj * Lisp_StringDowncase(LispBuiltin *builtin) /* string-downcase string &key start end */ { return (LispStringDowncase(builtin, 0)); } LispObj * Lisp_NstringDowncase(LispBuiltin *builtin) /* nstring-downcase string &key start end */ { return (LispStringDowncase(builtin, 1)); } LispObj * LispStringCapitalize(LispBuiltin *builtin, int inplace) /* string-capitalize string &key start end nstring-capitalize string &key start end */ { LispObj *result; char *string, *newstring; long start, end, length, offset, upcase; LispObj *ostring, *ostart, *oend; oend = ARGUMENT(2); ostart = ARGUMENT(1); ostring = ARGUMENT(0); CHECK_STRING(ostring); LispCheckSequenceStartEnd(builtin, ostring, ostart, oend, &start, &end, &offset); result = ostring; string = THESTR(ostring); length = STRLEN(ostring); /* first check if something need to be done */ for (upcase = 1, offset = start; offset < end; offset++) { if (upcase) { if (!isalnum(string[offset])) continue; if (string[offset] != toupper(string[offset])) break; upcase = 0; } else { if (isalnum(string[offset])) { if (string[offset] != tolower(string[offset])) break; } else upcase = 1; } } if (offset >= end) return (result); if (inplace) { CHECK_STRING_WRITABLE(ostring); newstring = string; } else { /* capitalize a copy of argument */ newstring = LispMalloc(length + 1); memcpy(newstring, string, length); newstring[length] = '\0'; } for (; offset < end; offset++) { if (upcase) { if (!isalnum(string[offset])) continue; newstring[offset] = toupper(string[offset]); upcase = 0; } else { if (isalnum(newstring[offset])) newstring[offset] = tolower(string[offset]); else upcase = 1; } } if (!inplace) result = LSTRING2(newstring, length); return (result); } LispObj * Lisp_StringCapitalize(LispBuiltin *builtin) /* string-capitalize string &key start end */ { return (LispStringCapitalize(builtin, 0)); } LispObj * Lisp_NstringCapitalize(LispBuiltin *builtin) /* nstring-capitalize string &key start end */ { return (LispStringCapitalize(builtin, 1)); } LispObj * Lisp_StringConcat(LispBuiltin *builtin) /* string-concat &rest strings */ { char *buffer; long size, length; LispObj *object, *string; LispObj *strings; strings = ARGUMENT(0); if (strings == NIL) return (STRING("")); for (length = 1, object = strings; CONSP(object); object = CDR(object)) { string = CAR(object); CHECK_STRING(string); length += STRLEN(string); } buffer = LispMalloc(length); for (length = 0, object = strings; CONSP(object); object = CDR(object)) { string = CAR(object); size = STRLEN(string); memcpy(buffer + length, THESTR(string), size); length += size; } buffer[length] = '\0'; object = LSTRING2(buffer, length); return (object); }