+;;;--------------------------------------------------------------------------
+;;; Conversion utilities.
+
+(defun string-to-symbol
+ (string &key (package *package*) (swap-case t) (swap-hyphen t))
+ "Convert STRING to a symbol in PACKAGE.
+
+ Parse off a `PACKAGE:' prefix from STRING if necessary, to identify the
+ package; PACKAGE is used if there isn't a prefix. A doubled colon allows
+ access to internal symbols, and will intern if necessary. Note that
+ escape characters are /not/ processed; don't put colons in package names
+ if you want to use them from SOD property sets.
+
+ The portions of the string are modified by `frob-identifier'; the
+ arguments SWAP-CASE and SWAP-HYPHEN are passed to `frob-identifier' to
+ control this process."
+
+ (let* ((length (length string))
+ (colon (position #\: string)))
+ (multiple-value-bind (start internalp)
+ (cond ((not colon) (values 0 t))
+ ((and (< (1+ colon) length)
+ (char= (char string (1+ colon)) #\:))
+ (values (+ colon 2) t))
+ (t
+ (values (1+ colon) nil)))
+ (when colon
+ (let* ((package-name (if (zerop colon) "KEYWORD"
+ (frob-identifier (subseq string 0 colon)
+ :swap-case swap-case
+ :swap-hyphen swap-hyphen)))
+ (found (find-package package-name)))
+ (unless found
+ (error "Unknown package `~A'" package-name))
+ (setf package found)))
+ (let ((name (frob-identifier (subseq string start)
+ :swap-case swap-case
+ :swap-hyphen swap-hyphen)))
+ (multiple-value-bind (symbol status)
+ (funcall (if internalp #'intern #'find-symbol) name package)
+ (cond ((or internalp (eq status :external))
+ symbol)
+ ((not status)
+ (error "Symbol `~A' not found in package `~A'"
+ name (package-name package)))
+ (t
+ (error "Symbol `~A' not external in package `~A'"
+ name (package-name package)))))))))
+
+(let ((truth-map (make-hash-table :test #'equalp)))
+ (dolist (string '("true" "t" "yes" "on" "yup" "verily"))
+ (setf (gethash string truth-map) t))
+ (dolist (string '("false" "nil" "no" "off" "nope" "nowise"))
+ (setf (gethash string truth-map) nil))
+ (defun truishp (string)
+ "Convert STRING to a boolean."
+ (multiple-value-bind (val foundp) (gethash string truth-map)
+ (if foundp val
+ (error "Unrecognized boolean value `~A'" string)))))
+