   else return nil;
 }
 
+// Characters
+
+object *fn_char (object *args, object *env) {
+  (void) env;
+  char c = nthchar(first(args), integer(second(args)));
+  if (c == 0) error(PSTR("'char' index out of range"));
+  return character(c);
+}
+
+object *fn_charcode (object *args, object *env) {
+  (void) env;
+  return number(fromchar(first(args)));
+}
+
+object *fn_codechar (object *args, object *env) {
+  (void) env;
+  return character(integer(first(args)));
+}
+
+object *fn_characterp (object *args, object *env) {
+  (void) env;
+  return characterp(first(args)) ? tee : nil;
+}
+
 // Strings
 
 object *fn_stringp (object *args, object *env) {
   (void) env;
+  return stringp(first(args)) ? tee : nil;
+}
+
+bool stringcompare (object *args, bool lt, bool gt, bool eq) {
   object *arg1 = first(args);
-  return stringp(arg1) ? tee : nil;
-}
-
-object *fn_stringeq (object *args, object *env) {
-  (void) env;
-  object *arg1 = first(args);
-  if (!stringp(arg1)) error(F("'string=' first argument is not a string"));
   object *arg2 = second(args);
-  if (!stringp(arg2)) error(F("'string=' second argument is not a string"));
+  if (!stringp(arg1) || !stringp(arg2)) error(PSTR("String compare argument is not a string"));
   arg1 = cdr(arg1);
   arg2 = cdr(arg2);
   while ((arg1 != NULL) || (arg2 != NULL)) {
-    if ((arg1 == NULL) || (arg2 == NULL) || (arg1->integer != arg2->integer)) return nil;
+    if (arg1 == NULL) return lt;
+    if (arg2 == NULL) return gt;
+    if (arg1->integer < arg2->integer) return lt;
+    if (arg1->integer > arg2->integer) return gt;
     arg1 = car(arg1);
     arg2 = car(arg2);
   }
-  return tee;
+  return eq;
+}
+
+object *fn_stringeq (object *args, object *env) {
+  (void) env;
+  return stringcompare(args, false, false, true) ? tee : nil;
+}
+
+object *fn_stringless (object *args, object *env) {
+  (void) env;
+  return stringcompare(args, true, false, false) ? tee : nil;
+}
+
+object *fn_stringgreater (object *args, object *env) {
+  (void) env;
+  return stringcompare(args, false, true, false) ? tee : nil;
+}
+
+object *fn_sort (object *args, object *env) {
+  if (first(args) == NULL) return nil;
+  object *list = cons(nil,first(args));
+  push(list,GCStack);
+  object *predicate = second(args);
+  object *compare = cons(NULL,cons(NULL,NULL));
+  object *ptr = cdr(list);
+  while (cdr(ptr) != NULL) {
+    object *go = list;
+    while (go != ptr) {
+      car(compare) = car(cdr(ptr));
+      car(cdr(compare)) = car(cdr(go));
+      if (apply(predicate, compare, &env)) break;
+      go = cdr(go);
+    }
+    if (go != ptr) {
+      object *obj = cdr(ptr);
+      cdr(ptr) = cdr(obj);
+      cdr(obj) = cdr(go);
+      cdr(go) = obj;
+    } else ptr = cdr(ptr);
+  }
+  pop(GCStack);
+  return cdr(list);
+}
+
+object *fn_stringfn (object *args, object *env) {
+  (void) env;
+  object *arg = first(args);
+  int type = arg->type;
+  if (type == STRING) return arg;
+  object *obj = myalloc();
+  obj->type = STRING;
+  if (type == CHARACTER) {
+    object *cell = myalloc();
+    cell->car = NULL;
+    cell->integer = fromchar(arg)<<8;
+    obj->cdr = cell;
+  } else if (type == SYMBOL) {
+    char *s = name(arg);
+    char ch = *s++;
+    object *head = NULL;
+    int chars = 0;
+    while (ch) {
+      if (ch == '\\') ch = *s++;
+      buildstring(ch, &chars, &head);
+      ch = *s++;
+    }
+    obj->cdr = head;
+  } else error(PSTR("Cannot convert to string"));
+  return obj;
 }
 
 object *fn_concatenate (object *args, object *env) {
   (void) env;
   object *arg = first(args);
   symbol_t name = arg->name;
-  if (name != STRINGFN) error(F("Only string result supported"));
+  if (name != STRINGFN) error(PSTR("Only string result supported"));
   args = cdr(args);
   object *result = myalloc();
   result->type = STRING;
   object *head = NULL;
-  object *tail = NULL;
   int chars = 0;
   while (args != NULL) {
     object *obj = first(args);
-    if (obj->type != STRING) error2(obj, F("not a string"));
+    if (obj->type != STRING) error2(obj, PSTR("not a string"));
     obj = cdr(obj);
     while (obj != NULL) {
       int pair = obj->integer;
        while (pair != 0) {
          char ch = pair>>8 & 0xFF;
-         buildstring(ch, &chars, &head, &tail);
+         buildstring(ch, &chars, &head);
          pair = pair<<8;
       }
       obj = car(obj);

/* uLisp ATmega Version 1.8 - www.ulisp.com
   David Johnson-Davies - www.technoblogy.com - 15th April 2017

   Licensed under the MIT license: https://opensource.org/licenses/MIT
*/

    if (TraceFn[i] == name) { TraceFn[i] = 0; return; }
    i++;
  }
  error(PSTR("It wasn't being traced"));
}

// Helper functions
