[Openmcl-devel] Bidirectional streams

Gary Byers gb at clozure.com
Thu Jan 8 04:36:37 PST 2009

On Wed, 7 Jan 2009, Daniel Herring wrote:

> Hi,
> A current cll thread with the same subject has been exploring the behavior
> of read-char and unread-char on bidirectional streams.
> A recent post provided the following test.
> (with-open-file (s "foo.txt" :direction :io :if-exists :supersede)
>   (write-line (print "abcdefghijk") s)
>   (file-position s 0)
>   (let (a)
>     (print (list (file-position s)
>                  (setf a (read-char s))
>                  (file-position s)
>                  (unread-char a s)
>                  (file-position s)
>                  (prog1 (write-char #\x s) (force-output s))
>                  (file-position s)
>                  (read-char s)
>                  (file-position s)
>                  (read-char s))))
>   (file-position s 0)
>   (print (read-line s)))
> This fails to run on clozure...
> Welcome to Clozure Common Lisp Version 1.3-dev-r11532M-trunk (LinuxX8664)!
> ...
> "abcdefghijk"
>> Error: Unexpected end of file on #<BASIC-FILE-CHARACTER-IO-STREAM ("foo.txt"/4 ISO-8859-1) #x3000416F06BD>, near position 12
>> While executing: CCL::READ-CHAR-INTERNAL, in process listener(1).
> Is this poorly written code, or does it expose a bug in clozure?


A clear example of the bug (that causes the spurious EOF) is:

(with-open-file (f "junk.txt" :direction :output :if-does-not-exist :supersede)
   (write-line "abcdefg" f)
   (file-position f 0)
   (force-output f)
   (eql (file-position f) 0))

(I'm not checking to make sure that the FILE-POSITION calls return non-NIL;
they should.  I am trying to make sure that the call to FORCE-OUTPUT has
no effect on the file position; it shouldn't, but does.  AFAIK, that bug's
been there for a long time; apparently, people don't really write code
like this ...)

As far as the code goes ...  the spec allows us to assume (if we again
ignore the fact that FILE-POSITION is allowed to return NIL) that:

(let* ((p (file-position f)))
   (write-char c f) ; or READ-CHAR
   (let* ((q (file-position f)))
     (> q p)))

is true (the value returned by FILE-POSITION increases as a result of
reading or writing characters), but we can't generally assume that

   (= (- q p) 1)

is true.  (It's very likely to be true in practice, but whether it is
or not ultimately depends on the external format of the file and
on the values of the characters being read/written.)

We might also assume (as the code above seems to) that in:

(let* ((c (read-char f))
        (p (file-position f)))
   (unread-char c f)
   (let* ((q (file-position f)))
     (< q p)))

is true.  However reasonable that assumption might be (and however
true it might be in practice), the spec doesn't seem to say anything
about whether or how UNREAD-CHAR affects the value returned by
FILE-POSITION.  (A plausible way to implement UNREAD-CHAR on a file
stream might be to effectively decrement the stream's FILE-POSITION by
one or more units, but I don't think that there's any requirement
that UNREAD-CHAR be implemented in a way that matches that effect.)
Some implementations might follow that model, others don't, and 
the value of the example code is probably pretty limited.  (If someone
doesn't understand the concept of "undefined, implementation dependent
behavior", it's a good way of illustrating it.)

That said, even if there's no formal basis for a model that says that
"UNREAD-CHAR has effects similar to those that'd be obtained by
decrementing the file position by 1 or more units, depending on its
argument and the stream's external format", there's something to be
said for it in that it can yield more predictable implementation-
dependendent behavior (and that kind of behavior can be useful.)
There are enough other ways to lose when trying to update a text
file in-place that there are likely limits on how useful that'd
be, but it'd be nice not to have to read or write an explanation
like this again ...

> Thanks,
> Daniel
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel

More information about the Openmcl-devel mailing list