Here's a set of trivial convenience functions which compile the other answers. Be aware that many older systems, such as Commodore 8-bits and 68K Apple Macintosh, used only Return (ASCII 13) for line endings. Thanks to @frank-shearar for mentioning the unexpected behavior of SBCL.
(defvar *crlf*
(concatenate 'string #(#\Return #\Linefeed))
"DOS line ending sequence.")
(defun dos-line (string)
"Append the DOS line ending sequence to STRING."
(concatenate 'string string *crlf*))
(defun dos-line-p (string)
"Does STRING have the DOS line ending sequence?"
(eql (- (length string) (length *crlf*))
;; SEARCH works for any sequence, but probably isn't the fastest
(search *crlf* string :from-end t)))
(defun ensure-dos-line (string)
"Ensure STRING ends with the DOS line ending sequence."
(if (dos-line-p string)
string
(dos-line string)))
;;; Tests
;;; Just load the file like this:
;;; sbcl --script dos-lines.lisp | hd
(let* ((s1 "Foo the bar!")
(s2 (ensure-dos-line s1)))
(assert (equal (format nil "~A~C~C" s1 (code-char 13) (code-char 10))
s2))
(princ s2))