Here's a macro definition which constructs a loop with the specified variable name bound to a new line in the specified file each time through the loop:
(defmacro with-string-from-file ((in-string filename
&key (if-does-not-exist :error))
&rest body)
(let ((working-stream (gensym)))
`(with-open-file (,working-stream
,filename
:direction :input
:external-format charset:iso-8859-1
:if-does-not-exist ,if-does-not-exist)
(let* ((,in-string))
(when ,working-stream
(loop while (setf ,in-string (read-line
,working-stream
nil))
do ,@body))))))
(princ "test 1:") (terpri)
(with-string-from-file (the-string "/etc/resolv.conf"
:if-does-not-exist nil)
(princ "one line is \"")
(princ the-string)
(princ "\"")
(terpri))
; Test 2 has not been introduced yet.
(princ "test 3:") (terpri)
(with-string-from-file (the-string "/etc/resolv.conf.nonexistent"
:if-does-not-exist nil)
(princ "one line is \"")
(princ the-string)
(princ "\"")
(terpri))
(princ "test 4:") (terpri)
(with-string-from-file (the-string "/etc/resolv.conf.nonexistent")
(princ "one line is \"")
(princ the-string)
(princ "\"")
(terpri))
The output:
test 1:
one line is "# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)"
one line is "# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN"
one line is "nameserver 192.168.6.1"
one line is "search x441afea5.org"
test 3:
test 4:
*** - OPEN: File #P"/etc/resolv.conf.nonexistent" does not exist
This works whether running the file directly, or compiling it first and then running the compiled file.
Now I attempt to add a feature to the macro so I can specify the input character set:
(defmacro with-string-from-file ((in-string filename
&key (if-does-not-exist :error)
(external-format charset:iso-8859-1))
&rest body)
(let ((working-stream (gensym)))
`(with-open-file (,working-stream
,filename
:direction :input
:external-format ,external-format
:if-does-not-exist ,if-does-not-exist)
(let* ((,in-string))
(when ,working-stream
(loop while (setf ,in-string (read-line
,working-stream
nil))
do ,@body))))))
(princ "test 1:") (terpri)
(with-string-from-file (the-string "/etc/resolv.conf"
:if-does-not-exist nil)
(princ "one line is \"")
(princ the-string)
(princ "\"")
(terpri))
(princ "test 2:") (terpri)
(with-string-from-file (the-string "/etc/resolv.conf"
:external-format charset:utf-8
:if-does-not-exist nil)
(princ "one line is \"")
(princ the-string)
(princ "\"")
(terpri))
(princ "test 3:") (terpri)
(with-string-from-file (the-string "/etc/resolv.conf.nonexistent"
:if-does-not-exist nil)
(princ "one line is \"")
(princ the-string)
(princ "\"")
(terpri))
(princ "test 4:") (terpri)
(with-string-from-file (the-string "/etc/resolv.conf.nonexistent")
(princ "one line is \"")
(princ the-string)
(princ "\"")
(terpri))
When I run the file directly it works:
test 1:
one line is "# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)"
one line is "# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN"
one line is "nameserver 192.168.6.1"
one line is "search x441afea5.org"
test 2:
one line is "# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)"
one line is "# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN"
one line is "nameserver 192.168.6.1"
one line is "search x441afea5.org"
test 3:
test 4:
*** - OPEN: File #P"/etc/resolv.conf.nonexistent" does not exist
But when I try to compile it, I get this error:
;; Compiling file /u/home/clisp/experiments/y1.lisp ...
*** - PRINT: Despite *PRINT-READABLY*, #<ENCODING CHARSET:ISO-8859-1 :UNIX>
cannot be printed readably.
How can I get this to compile? I'd like to specify a character set explicitly with charset:
, rather than potentially having those character set names conflict with symbols in my main namespace.