DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download
Index: ┃ T s

⟦cb9138921⟧ TextFile

    Length: 27190 (0x6a36)
    Types: TextFile
    Names: »string.c«

Derivation

└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
    └─ ⟦this⟧ »EUUGD11/gnu-31mar87/scheme/microcode/string.c« 

TextFile

/*          Hey EMACS, this is -*- C -*- code!                 */

/****************************************************************
*                                                               *
*                         Copyright (c) 1984                    *
*               Massachusetts Institute of Technology           *
*                                                               *
* This material was developed by the Scheme project at the      *
* Massachusetts Institute of Technology, Department of          *
* Electrical Engineering and Computer Science.  Permission to   *
* copy this software, to redistribute it, and to use it for any *
* purpose is granted, subject to the following restrictions and *
* understandings.                                               *
*                                                               *
* 1. Any copy made of this software must include this copyright *
* notice in full.                                               *
*                                                               *
* 2. Users of this software agree to make their best efforts (a)*
* to return to the MIT Scheme project any improvements or       *
* extensions that they make, so that these may be included in   *
* future releases; and (b) to inform MIT of noteworthy uses of  *
* this software.                                                *
*                                                               *
* 3.  All materials developed as a consequence of the use of    *
* this software shall duly acknowledge such use, in accordance  *
* with the usual standards of acknowledging credit in academic  *
* research.                                                     *
*                                                               *
* 4. MIT has made no warrantee or representation that the       *
* operation of this software will be error-free, and MIT is     *
* under no obligation to provide any services, by way of        *
* maintenance, update, or otherwise.                            *
*                                                               *
* 5.  In conjunction with products arising from the use of this *
* material, there shall be no use of the name of the            *
* Massachusetts Institute of Technology nor of any adaptation   *
* thereof in any advertising, promotional, or sales literature  *
* without prior written consent from MIT in each case.          *
*                                                               *
****************************************************************/
\f


/* File: STRING.C
 *
 * Support for strings and conversion to and from lists of characters.
 */

#include "scheme.h"
#include "primitive.h"

                    /****************************/
                    /* Making Character Strings */
                    /****************************/

#define Empty_String()						\
  Pointer_Count = 2 + 1 + (Get_Integer(Arg1)/sizeof(Pointer));	\
  Primitive_GC_If_Needed(Free + Pointer_Count);			\
  Free[STRING_HEADER] =						\
    Make_Non_Pointer(TC_MANIFEST_NM_VECTOR, Pointer_Count-1);	\
  Free[STRING_LENGTH] = FIXNUM_0;				\
  /* put a null in first character position */			\
  (*((char *) (Free + STRING_CHARS))) = '\0';			\
  Free += Pointer_Count;

Built_In_Primitive(Prim_Make_Empty_String, 1, "MAKE-EMPTY-STRING")
{ long Pointer_Count;
  Primitive_1_Arg();
  Arg_1_Type(TC_FIXNUM);
  Empty_String();
  return Make_Pointer(TC_CHARACTER_STRING, Free-Pointer_Count);
}
\f

 
Built_In_Primitive(Prim_Make_Fld_String, 2, "MAKE-FILLED-STRING")
{ Pointer Result;
  long C, i, Pointer_Count, Count;
  char *P;
  Primitive_2_Args();
  Arg_1_Type(TC_FIXNUM);
  Arg_2_Type(TC_FIXNUM);
  Range_Check(C, Arg2, 0, 255, ERR_ARG_2_BAD_RANGE);
  Empty_String();
  Result = Make_Pointer(TC_CHARACTER_STRING, Free-Pointer_Count);
  P = (char *) Nth_Vector_Loc(Result, STRING_CHARS);
  Count = Get_Integer(Arg1);
  for (i=0; i < Count; i++) *P++ = C;
  *P++ = '\0';				       /* Add null */
  Vector_Set(Result, STRING_LENGTH, FIXNUM_0+Count);
  return Make_Pointer(TC_CHARACTER_STRING, Free-Pointer_Count);
}

/* Bit Blit.  Moves an arbitrary string of bits to an arbitrary place
   Given: Pointer to source string, Pointer to a pointer to
  destination string, Source offset, a pointer to destination offset and
  the number of bits to be moved.  Updates the destination pointer and
  offset.  
*/

#define Down_Shift(Word,Offset) \
          (((Word) >> Offset) & ((1 << (POINTER_LENGTH-Offset))-1))
#define Up_Shift(Word,Offset) \
          ((Word) << Offset)
\f


Shift_Bit_String(Source, Dest_Ptr, Source_Offset,
                 Dest_Offset_Ptr, Bit_Count)
long *Source, **Dest_Ptr, Source_Offset, *Dest_Offset_Ptr, Bit_Count;
{ long Offset, Count, Shift, Mask, i, Remainder_Bits,
       *Dest = *Dest_Ptr, D_Offset = *Dest_Offset_Ptr;
  Offset = Source_Offset-D_Offset;
  if (Offset == 0)
    if(Bit_Count+Source_Offset <= POINTER_LENGTH)
      { Mask=(1 << Bit_Count)-1;
        Mask=(Mask << Source_Offset);
        *Dest = (*Source & Mask) | (*Dest & ~Mask);
        if(Bit_Count+Source_Offset == POINTER_LENGTH) Dest++;
        *Dest_Offset_Ptr = (*Dest_Offset_Ptr+Bit_Count)%POINTER_LENGTH;
        *Dest_Ptr = Dest;
        return;
      }  /* Special Case */
    else
      { Mask = (1 << Source_Offset)-1, Remainder_Bits;
        *Dest = (*Source & ~Mask) | (*Dest & Mask); /* First Word */
        *Dest++; *Source++;
        Count = (Source_Offset+Bit_Count)/32-1;
        Remainder_Bits = (Source_Offset+Bit_Count)%POINTER_LENGTH;
        for(i=0; i<Count; i++) *Dest++ = *Source++;
        Mask = (1 << Remainder_Bits)-1;
        *Dest = (*Source & Mask) | (*Dest & ~Mask);
        *Dest_Offset_Ptr = (*Dest_Offset_Ptr+Bit_Count)%POINTER_LENGTH;
        *Dest_Ptr = Dest;
        return;
      }

/* Shift_Bit_String continues on the next page */
\f


/* Shift_Bit_String, continued */

  else
    switch(Offset > 0)
      {
        case 1: /* Source_Offset > D_Offset */
          if(D_Offset+Bit_Count <= POINTER_LENGTH)
            { long Temp;
              Mask=(1 << Bit_Count)-1;
              Mask = (Mask << D_Offset);
              Shift = POINTER_LENGTH-Offset;
              Temp=Down_Shift(*Source, Offset);
              if (Source_Offset+Bit_Count > POINTER_LENGTH)
                Temp |= Up_Shift(*++Source,Shift);
              *Dest=(Mask & Temp) | (*Dest & ~Mask);
              if(D_Offset+Bit_Count == POINTER_LENGTH) Dest++;
              D_Offset += Bit_Count%POINTER_LENGTH;
             *Dest_Offset_Ptr = (*Dest_Offset_Ptr+Bit_Count)%POINTER_LENGTH;
             *Dest_Ptr = Dest;
              return;
            }

          else
            { long Count_1, Count_2;
              Mask=(1 << D_Offset)-1;
              *Dest &= Mask;
              *Dest |= Down_Shift(*Source++,Offset) & ~Mask;
                  /* Take care of first word */

               Shift=POINTER_LENGTH-Offset;
               Mask=(1 << Shift)-1;
               Count_1=(Source_Offset+Bit_Count)/POINTER_LENGTH-1;
               Count_2=(D_Offset+Bit_Count)/POINTER_LENGTH-1;
               Remainder_Bits=(D_Offset+Bit_Count)%POINTER_LENGTH;
               if(Count_1 == Count_2)
                 { for(i=0; i<Count_1; i++) /* Middle words */
                     { *Dest++ |= Up_Shift(*Source,Shift);
                       *Dest = (*Source++ >> Offset) & Mask;
                     }
                   *Dest++ |= Up_Shift(*Source,Shift);  /* Last Word(s) */
                   Mask=(1 << Remainder_Bits)-1;
                   *Dest = (Down_Shift(*Source, Offset) & Mask) | 
                           (*Dest & ~Mask);
                 }

/* Shift_Bit_String continues on the next page */
\f


/* Shift_Bit_String, continued */

               else
                 { for(i=0; i<Count_2; i++) /* Middle words */
                     { *Dest++ |= Up_Shift(*Source,Shift);
                       *Dest = (*Source++ >> Offset) & Mask;
                     }

                   *Dest++ |= Up_Shift(*Source,Shift);  /* Last Word(s) */
                   Mask = (1 << Remainder_Bits)-1;
                   *Dest &= ~Mask;
                   *Dest |= Down_Shift(*Source++,Offset) & Mask;
                   *Dest |= Up_Shift(*Source,Shift) & Mask;
                 }
               *Dest_Offset_Ptr = (*Dest_Offset_Ptr+Bit_Count) %
                                   POINTER_LENGTH;
               *Dest_Ptr = Dest;
               return;
             }
        case 0: /* Source_Offset < D_Offset */
          Offset = -Offset;
          if(D_Offset+Bit_Count <= POINTER_LENGTH)
            { Mask=(1 << Bit_Count)-1;
              Mask=(Mask << D_Offset);
              *Dest = (*Dest & ~Mask) | (Mask & (*Source << Offset));
              if(D_Offset+Bit_Count == POINTER_LENGTH) Dest++;
              D_Offset += Bit_Count%POINTER_LENGTH;
              *Dest_Offset_Ptr = (*Dest_Offset_Ptr+Bit_Count)%POINTER_LENGTH;
              *Dest_Ptr = Dest;
              return;
            }

/* Shift_Bit_String continues on the next page */
\f


/* Shift_Bit_String, continued */

          else
            { Mask=(1 << D_Offset)-1;
              Shift=POINTER_LENGTH-Offset;
              Remainder_Bits=(D_Offset+Bit_Count)%POINTER_LENGTH;
              *Dest &= Mask;
              *Dest++ |= (*Source << Offset) & ~Mask;
                    /* First Word */
              Mask=(1 << Offset)-1;
              Count= (Bit_Count-(POINTER_LENGTH-D_Offset))/POINTER_LENGTH;
              for(i=0; i<Count; i++)  /* Middle Words */
                { *Dest = (*Source++ >> Shift) & Mask;
                  *Dest++ |= (*Source << Offset);
                }

              if(Remainder_Bits > 0)  /* Last Word patched up */
                { Mask=(1 << Remainder_Bits)-1;
                  *Dest = (Down_Shift(*Source++,Shift) & Mask) | 
                          (*Dest &= ~Mask);
                  *Dest |= Up_Shift(*Source,Offset) & Mask;
                  *Dest_Offset_Ptr = (*Dest_Offset_Ptr+Bit_Count)
                                     % POINTER_LENGTH;
                  *Dest_Ptr = Dest;
                  return;
                }
              else 
                *Dest_Offset_Ptr =
		  (*Dest_Offset_Ptr+Bit_Count)%POINTER_LENGTH;
                *Dest_Ptr = Dest;
                return;
            }
      }
}
\f


/* (BUILD_STRING_FROM_LIST LIST)
      [Primitive number 0x5F]
      Takes a list of the ASCII codes for characters and converts it
      into a string containing those characters.  For example, on
      input '(#/A #/B #/C) it returns "ABC".
*/
Built_In_Primitive(Prim_Build_String_From_List, 1, "BUILD-STRING-FROM-LIST")
{ Pointer Result;
  /* The work is done by Make_String, in file FASLOAD.C */
  Primitive_1_Arg();
  return Make_String(Arg1);
}
\f


/* (EQUAL_STRING_TO_LIST STRING LIST)
      [Primitive number 0x60]
      Compares characters from the string with ASCII character codes from
      the LIST.  Returns #!TRUE if the string and the list have the same
      number of characters and all the characters match.  Returns NIL
      otherwise.
*/
Built_In_Primitive(Prim_Equal_String_To_List, 2, "EQUAL-STRING-TO-LIST")
{ long Count, i;
  char *Next;
  Pointer Next_List;
  Primitive_2_Args();

  Arg_1_Type(TC_CHARACTER_STRING);
  Count = Get_Integer(Fast_Vector_Ref(Arg1, STRING_LENGTH));
  Next = (char *) Nth_Vector_Loc(Arg1, STRING_CHARS);
  Touch_In_Primitive(Arg2, Next_List);
  for (i=0; (i < Count) && (Type_Code(Next_List) == TC_LIST); i++)
  { fast Pointer Next_Val;
    Touch_In_Primitive(Vector_Ref(Next_List, CONS_CAR), Next_Val);
    if (Type_Code(Next_Val) != TC_CHARACTER)
      Primitive_Error(ERR_ARG_2_WRONG_TYPE);
    if (*Next++ != Get_Integer(Next_Val)) return NIL;
    Touch_In_Primitive(Vector_Ref(Next_List, CONS_CDR), Next_List);
  }
  if ((i==Count) && (Next_List==NIL)) return TRUTH;
  if ((Next_List != NIL) && (Type_Code(Next_List) != TC_LIST))
    Primitive_Error(ERR_ARG_2_WRONG_TYPE);
  return NIL;
}
\f


/* (INSERT_STRING ORIG-STRING N INSERTION)
      [Primitive number 0x1D]
      ORIG-STRING and INSERTION must be strings.  A new string is
      created which contains INSERTION between the (N-1)st and Nth
      characters of ORIG-STRING.  If N is 0, this is the concatenation
      of INSERTION followed by ORIG-STRING.  If N is (STRING_LENGTH
      ORIG_STRING) it is the concatenation of ORIG-STRING followed by
      INSERTION.  Both strings are copied in the process (i.e. the new
      string never shares characters with the original ones).
*/
Built_In_Primitive(Prim_Insert_String, 3, "INSERT-STRING")
{ char *To, *From_New, *From_Old; 
  long Index, Old_Length, New_Length, Length, i, Pointer_Count;
  Primitive_3_Args();
  Arg_1_Type(TC_CHARACTER_STRING);
  Arg_2_Type(TC_FIXNUM);
  Arg_3_Type(TC_CHARACTER_STRING);
  Old_Length = Get_Integer(Fast_Vector_Ref(Arg1, STRING_LENGTH));
  New_Length = Get_Integer(Fast_Vector_Ref(Arg3, STRING_LENGTH));
  Range_Check(Index, Arg2, 0, Old_Length, ERR_ARG_2_BAD_RANGE);
  From_Old = (char *) Nth_Vector_Loc(Arg1, STRING_CHARS);
  From_New = (char *) Nth_Vector_Loc(Arg3, STRING_CHARS);
  To = ((char *) (Free+2));
  if (Old_Length-Index >= New_Length) Length = Old_Length;
  else Length = Old_Length + New_Length;
  Pointer_Count=
    (sizeof(Pointer) + Length*sizeof(char))/ sizeof(Pointer);
  Primitive_GC_If_Needed(Free + Pointer_Count + 1);
  Free[NM_VECTOR_HEADER] =
    Make_Non_Pointer(TC_MANIFEST_NM_VECTOR, Pointer_Count+1);
  Free[NM_ENTRY_COUNT] = FIXNUM_0+Length;
  for (i=0; i < Index; i++) *To++ = *From_Old++;
  for (i=0; i < New_Length; i++) *To++ = *From_New++;
  for (i=Index; i < Old_Length; i++) *To++ = *From_Old++;
  *To++ = '\0';		 		 /* Add the null */
  Free += (Pointer_Count + 2);
  return Make_Pointer(TC_CHARACTER_STRING, Free-(Pointer_Count+2));
}
\f


/* (STRING_EQUAL STRING-1 STRING-2)
      [Primitive number 0x0E]
      Compare two strings for equality.  This comparison is
      case-sensitive.
*/
Built_In_Primitive(Prim_String_Equal, 2, "STRING-EQUAL")
{ char *Finger_1, *Finger_2; long Length_1, Length_2, i; 
  Primitive_2_Args();
  Arg_1_Type(TC_CHARACTER_STRING);
  Arg_2_Type(TC_CHARACTER_STRING);
  Length_1 = Get_Integer(Fast_Vector_Ref(Arg1, STRING_LENGTH));
  Length_2 = Get_Integer(Fast_Vector_Ref(Arg2, STRING_LENGTH));
  if (Length_1 != Length_2) return NIL;
  Finger_1 = (char *) Nth_Vector_Loc(Arg1, STRING_CHARS);
  Finger_2 = (char *) Nth_Vector_Loc(Arg2, STRING_CHARS);
  for (i=0; i < Length_1; i++)
    if (*Finger_1++ != *Finger_2++) return NIL;
  return TRUTH;
}
\f


/* (OVERWRITE_STRING ORIG-STRING POSITION NEW-STRING)
      [Primitive number 0x2B]
      Side-effects the ORIG-STRING by replacing characters starting at
      POSITION with characters from NEW-STRING.  If POSITION is 0, the
      replacement is at the start of the string, and so on.  It is not
      possible to extend the length of a string this way unless
      the string contains room for more characters than it currently 
      has, which should be priviledged information. The value returned
      is ORIG-STRING, which will have been modified.
*/
Built_In_Primitive(Prim_Overwrite_String, 3, "OVERWRITE-STRING")
{ char *To, *From;
  long Max_Length, Old_Length, Addition, New_Length, Index, i;
  Primitive_3_Args();
  Arg_1_Type(TC_CHARACTER_STRING);
  Arg_2_Type(TC_FIXNUM);
  Arg_3_Type(TC_CHARACTER_STRING);
  Max_Length = ((Get_Integer(Fast_Vector_Ref(Arg1, STRING_HEADER)) - 1) *
		(sizeof(Pointer))) - 1;
  Old_Length = Get_Integer(Fast_Vector_Ref(Arg1, STRING_LENGTH));
  Addition = Get_Integer(Fast_Vector_Ref(Arg3, STRING_LENGTH));
  Index = Get_Integer(Arg2);
  if (Index > Old_Length)
    Primitive_Error(ERR_ARG_2_BAD_RANGE);
  if ((New_Length = Index + Addition) > Max_Length)
    Primitive_Error(ERR_ARG_3_BAD_RANGE);
  From = (char *) Nth_Vector_Loc(Arg3, STRING_CHARS);
  To = (char *) Nth_Vector_Loc(Arg1, STRING_CHARS);
  To += Index;
  for (i=0; i < Addition; i++) *To++ = *From++;
  /* If we have gone past the bounds of the old string,
     add Null and adjust string length.
  */
  if (New_Length > Old_Length)
  { *To++ = '\0';
    Vector_Ref(Arg1, STRING_LENGTH) = FIXNUM_0 + New_Length;
  }
  for (i=0; i < New_Length; i++) *To++ = *From++;
  return Arg1;
}
\f


/* (SUBSTRING STRING FROM TO)
      [Primitive number 0x45]
      Extracts the substring of STRING beginning with the FROMth
      character and continuing to (but not including) the TOth
      character. Thus (SUBSTRING S 0 (STRING_LENGTH S)) returns a copy
      of all of S.
*/
Built_In_Primitive(Prim_Substring, 3, "SUBSTRING")
{ char *To, *From; long Low, High, Length,
       Elements, i, Pointer_Count;
  Primitive_3_Args();
  Arg_1_Type(TC_CHARACTER_STRING);
  Arg_2_Type(TC_FIXNUM);
  Arg_3_Type(TC_FIXNUM);
  Length = Get_Integer(Fast_Vector_Ref(Arg1, STRING_LENGTH));
  Range_Check(Low, Arg2, 0, Length, ERR_ARG_2_BAD_RANGE);
  Range_Check(High, Arg3, 0, Length, ERR_ARG_3_BAD_RANGE);
  Elements = High-Low;
  Pointer_Count=
    (sizeof(Pointer) + Elements*sizeof(char))/ sizeof(Pointer);
  Primitive_GC_If_Needed(Free + Pointer_Count + 1);
  Free[0]=Make_Non_Pointer(TC_MANIFEST_NM_VECTOR, Pointer_Count+1);
  Free[1]=FIXNUM_0+Elements;
  if (Low > High) Primitive_Error(ERR_ARG_3_BAD_RANGE);
  To = ((char *) (Free+2));
  From = (char *) Nth_Vector_Loc(Arg1, STRING_CHARS);
  From += Low;
  for (i=0; i < Elements; i++) *To++ = *From++;
  *To++ = '\0';			/* Add the null */
  Free += Pointer_Count + 2;
  return Make_Pointer(TC_CHARACTER_STRING, Free-(Pointer_Count+2));
}    
\f


/* (SUBSTRING_SEARCH STRING SUBSTRING)
      [Primitive number 0xB8]
      Returns the character position in STRING where the first
      occurrence of SUBSTRING begins.  The search is case-sensitive.
      Returns NIL if the SUBSTRING does not occur.
*/
Built_In_Primitive(Prim_Substring_Search, 2, "SUBSTRING-SEARCH")
{ char *String, *Sub;
  long String_Length, Sub_Length, Stop_At, i, j;
  Primitive_2_Args();
  Arg_1_Type(TC_CHARACTER_STRING);
  Arg_2_Type(TC_CHARACTER_STRING);
  String = (char *) Nth_Vector_Loc(Arg1, STRING_CHARS);
  Sub = (char *) Nth_Vector_Loc(Arg2, STRING_CHARS);
  String_Length = Get_Integer(Fast_Vector_Ref(Arg1, STRING_LENGTH));
  Sub_Length = Get_Integer(Fast_Vector_Ref(Arg2, STRING_LENGTH));
  Stop_At = String_Length-Sub_Length;
  for (i=0; i <= Stop_At; i++)
  { for (j=0; j < Sub_Length; j++)
      if (String[i+j] != Sub[j]) break;
    if (j == Sub_Length) return FIXNUM_0+i;
  }
  return NIL;
}
\f


/* (SUBSTRING_TO_LIST STRING FROM TO)
   [Primitive number 0x4A]
   The same as SUBSTRING, except the result is a list of ASCII
   character codes rather than a string.
   */    
Built_In_Primitive(Prim_Substring_To_List, 3, "SUBSTRING->LIST")
{ char *Finger;
  long Low, High, Length, Elements, i;
  Pointer Result;
  
  Primitive_3_Args();    
  Arg_1_Type(TC_CHARACTER_STRING);
  Arg_2_Type(TC_FIXNUM);
  Arg_3_Type(TC_FIXNUM);
  Length = Get_Integer(Fast_Vector_Ref(Arg1, STRING_LENGTH));
  Range_Check(Low, Arg2, 0, (Length == 0)? 0 : Length-1,
              ERR_ARG_2_BAD_RANGE);
  Range_Check(High, Arg3, Low, Length, ERR_ARG_3_BAD_RANGE);
  Elements = High-Low;
  Primitive_GC_If_Needed(Free +  2 * Elements);
  if (Elements==0) return NIL;
  Finger = (char *) Nth_Vector_Loc(Arg1, STRING_CHARS);
  Finger += Low;
  Result = Make_Pointer(TC_LIST, Free);
  for (i=0; i < Elements; i++, Free++)
  { *Free++ = FIXNUM_0+(MAX_CHAR & *Finger++);
    *Free = Make_Pointer(TC_LIST, Free+1);
  }
  Free[-1] = NIL;
  return Result;
}
\f


/* (RAISE_STRING STRING)
      [Primitive number 0xB3]
      Returns a copy of STRING with all lower-case letters changed to
      upper-case.
*/
Built_In_Primitive(Prim_Raise_String, 1, "RAISE-STRING")
{ long Length, Word_Length, i;
  char *From, *To;
  Primitive_1_Arg();
  Arg_1_Type(TC_CHARACTER_STRING);
  Word_Length =
    1 + Get_Integer(Fast_Vector_Ref(Arg1, STRING_HEADER));
  Primitive_GC_If_Needed(Free + Word_Length);
  Length = Get_Integer(Fast_Vector_Ref(Arg1, STRING_LENGTH));
  Free[STRING_HEADER] = Fast_Vector_Ref(Arg1, STRING_HEADER);
  Free[STRING_LENGTH] = FIXNUM_0+Length;
  From = (char *) Nth_Vector_Loc(Arg1, STRING_CHARS);
  To = (char *) &Free[STRING_CHARS];
  for (i=0; i < Length; i++, From++, To++)
  { fast char c = *From;
    if ((c >= 'a') && (c <= 'z'))
      *To = c-'a'+'A';
    else *To = c;
  }
  *To++ = '\0';			  /* Add the null */
  Free += Word_Length;
  return Make_Pointer(TC_CHARACTER_STRING, Free-Word_Length);
}

/* (RAISE_CHAR CHAR-CODE)
      [Primitive number 0x64]
      If CHAR-CODE is the ASCII code for a lower-case letter, returns
      the ASCII code for the upper-case letter.  Otherwise returns
      CHAR-CODE unchanged.
*/
Built_In_Primitive(Prim_Raise_Char, 1, "RAISE-CHAR")
{ long Value;
  Primitive_1_Arg();
  Arg_1_Type(TC_FIXNUM);
  Range_Check(Value, Arg1, 0, MAX_CHAR, ERR_ARG_1_BAD_RANGE);
  if (Value >= 'a' && Value <= 'z') Value += 'A'-'a';
    return FIXNUM_0+Value;
}
\f


/* (STRING_HASH STRING)
      [Primitive number 0x83]
      Return a hash value for a string.  This uses the hashing
      algorithm used for interning symbols.  It is intended for use by
      the reader in creating interned symbols.
*/
Built_In_Primitive(Prim_String_Hash, 1, "STRING-HASH")
{ Primitive_1_Arg();
  Arg_1_Type(TC_CHARACTER_STRING);
  /* The work is done by Hash, in file FASLOAD.C */
  return Hash(Arg1);
}

/* (STRING_LESS STRING-1 STRING-2)
      [Primitive number 0x59]
      Returns #!TRUE if STRING-1 < STRING-2 using the ASCII character
      code collating sequence.  The test is case-sensitive.
*/
Built_In_Primitive(Prim_String_Less, 2, "STRING-LESS")
{ char *Finger_1, *Finger_2; long Length_1, Length_2, i, Count;
  Primitive_2_Args();
  Arg_1_Type(TC_CHARACTER_STRING);
  Arg_2_Type(TC_CHARACTER_STRING);
  Length_1 = Get_Integer(Fast_Vector_Ref(Arg1, STRING_LENGTH));
  Length_2 = Get_Integer(Fast_Vector_Ref(Arg2, STRING_LENGTH));
  if (Length_1 < Length_2) Count=Length_1;
  else Count=Length_2;
  Finger_1 = (char *) Nth_Vector_Loc(Arg1, STRING_CHARS);
  Finger_2 = (char *) Nth_Vector_Loc(Arg2, STRING_CHARS);
  for (i=0; i<Count; i++, Finger_1++, Finger_2++)
    if (*Finger_1 == *Finger_2) continue;
    else return (*Finger_1 < *Finger_2) ? TRUTH : NIL;
  if (Length_1 < Length_2) return TRUTH;
  else return NIL;
}
\f

    
/* (STRING_POSITION STRING MASK CHARACTER)
      [Primitive number 0x58]
      Scans STRING one character at a time looking for a character
      which is the same as CHARACTER when both are compared after
      masking them with MASK.  If MASK=255 (the usual case) it will
      look for an exact match.  If MASK=255-32=223 then it "ignores"
      the bit which (in ASCII) differentiates upper and lower case
      letters, etc.  Returns the position of the match, or NIL if one
      isn't found.
*/
Built_In_Primitive(Prim_String_Position, 3, "STRING-POSITION")
{ char *Finger; long Length, Character, Mask, i;
  Primitive_3_Args();
  Arg_1_Type(TC_CHARACTER_STRING);
  Arg_2_Type(TC_FIXNUM);
  Arg_3_Type(TC_FIXNUM);
  Range_Check(Mask, Arg2, 0, MAX_CHAR, ERR_ARG_2_BAD_RANGE);
  Range_Check(Character, Arg3, 0, MAX_CHAR, ERR_ARG_3_BAD_RANGE);
  Length = Get_Integer(Fast_Vector_Ref(Arg1, STRING_LENGTH));
  Finger= (char *) Nth_Vector_Loc(Arg1, STRING_CHARS);
  /* The test is made by finding all bits where the character from the
     string and the argument character differ (using XOR, ^).  This is
     then masked and tested to see if the only differences are in
     bits that the mask ignores.
  */
  for (i=0; i<Length; i++)
    if ((Mask & (*Finger++ ^ Character)) == 0) return FIXNUM_0+i;
  return NIL;
}
\f


/* (TRUNCATE_STRING STRING INDEX)
      [Primitive number 0x44]
      A side-effecting string primitive. Lops off the characters in
      STRING from INDEX on, leaving the maximum size (i.e. the GC
      size) of STRING the same.  Returns STRING.
*/
Built_In_Primitive(Prim_Truncate_String, 2, "TRUNCATE-STRING")
{ long Length, Index, i;
  char *To;
  Primitive_2_Args();
  Arg_1_Type(TC_CHARACTER_STRING);
  Arg_2_Type(TC_FIXNUM);
  Length = Get_Integer(Fast_Vector_Ref(Arg1, STRING_LENGTH));
  Sign_Extend(Arg2, Index);
  if (Index < 0) Primitive_Error(ERR_ARG_2_BAD_RANGE);
  if (Index <= Length) Vector_Ref(Arg1,STRING_LENGTH) = FIXNUM_0+Index;
  /* Now add a null after the last valid positon */
  To = (char *) &Vector_Ref(Arg1, STRING_CHARS);
  for (i=0; i < Index; i++) To++;
  *To = '\0';
  return Arg1;
}
\f


/* (VECTOR_8B? OBJECT)
      [Primitive number 0xA4]
      Returns #!TRUE if OBJECT is an 8-bit vector (a string).
*/
Built_In_Primitive(Prim_Vector_8b, 1, "VECTOR-8B?")
{ Primitive_1_Arg();
  Touch_In_Primitive(Arg1, Arg1);
  return (Type_Code(Arg1) == TC_VECTOR_8B) ? TRUTH : NIL;
}

/* (VECTOR_8B_CONS LENGTH)
      [Primitive number 0xA3]
      Creates an uninitialized 8-bit vector (string) to hold LENGTH
      characters.
*/
Built_In_Primitive(Prim_Vector_8b_Cons, 1, "VECTOR-8B-CONS")
{ long Length;
  Primitive_1_Arg();
  Arg_1_Type(TC_FIXNUM);
  Length = NM_HEADER_LENGTH +
            (sizeof(char)*Get_Integer(Arg1)) /
            sizeof(Pointer) + 1;
  Primitive_GC_If_Needed(Free+Length);
  Free[NM_VECTOR_HEADER] = 
    Make_Non_Pointer(TC_MANIFEST_NM_VECTOR, Length-1);
  Free[NM_ENTRY_COUNT] = FIXNUM_0+Get_Integer(Arg1);
  Free += Length;
  return Make_Pointer(TC_VECTOR_8B, Free-Length);
}
\f


/* (VECTOR_8B_REF STRING OFFSET)
      [Primitive number 0xA5]
      Returns the OFFSETth entry (character) in the 8-bit vector
      (string).  Entries are numbered from 0.
*/
Built_In_Primitive(Prim_Vector_8b_Ref, 2, "VECTOR-8B-REF")
{ long Index;
  char *String_Ptr;
  Primitive_2_Args();
  Arg_1_Type(TC_VECTOR_8B);
  Arg_2_Type(TC_FIXNUM);
  Range_Check(Index, Arg2,
              0, Get_Integer(Fast_Vector_Ref(Arg1,STRING_LENGTH))-1,
              ERR_ARG_2_BAD_RANGE);
  String_Ptr = (char *) Nth_Vector_Loc(Arg1, STRING_CHARS);
  return Make_Non_Pointer(TC_CHARACTER,
			  (String_Ptr[Index]) & MAX_CHAR);
}

/* (VECTOR_8B_SET STRING OFFSET VALUE)
      [Primitive number 0xA6]
      Stores VALUE (which must be a fixnum between 0 and 255) as the
      OFFSETth entry (character) in the 8-bit vector (string).
      Returns (bad style to rely on this) the previous value of the
      entry.
*/
Built_In_Primitive(Prim_Vec_8b_Set, 3, "VECTOR-8B-SET!")
{ long Index, Value;
  char *String_Ptr;
  Primitive_3_Args();
  Arg_1_Type(TC_VECTOR_8B);
  Arg_2_Type(TC_FIXNUM);
  Range_Check(Index, Arg2,
              0, Get_Integer(Fast_Vector_Ref(Arg1,STRING_LENGTH))-1,
              ERR_ARG_2_BAD_RANGE);
  Arg_3_Type(TC_CHARACTER);
  Range_Check(Value, Arg3, 0, MAX_CHAR, ERR_ARG_3_BAD_RANGE);
  String_Ptr = (char *) Nth_Vector_Loc(Arg1, STRING_CHARS);
  Value = Make_Non_Pointer(TC_CHARACTER,
			   (String_Ptr[Index]) & MAX_CHAR);
  String_Ptr[Index] = (char) Get_Integer(Arg3);
  return Value;
}
\f


/* (VECTOR_8B_SIZE STRING)
      [Primitive number 0xAD]
      Returns the number of entries (characters) in the 8-bit vector
      (string).
*/
Built_In_Primitive(Prim_Vec_8b_Size, 1, "VECTOR-8B-SIZE")
{ Primitive_1_Arg();
  Arg_1_Type(TC_VECTOR_8B);
  return Fast_Vector_Ref(Arg1, NM_ENTRY_COUNT);
}