After replacing Boehm with the new GC, I just removed the the final non-standard dependency in chibi-scheme, C string streams, which are used to implement Scheme's string ports. Now if your libc doesn't have C streams, Chibi will provide its own simplistic alternative. The allows Chibi to build and run on Plan 9 (though there are still some bugs to be worked out), and should port fairly easily to Windows.

Although string ports are only a semi-standard extension to Scheme, they are fairly widely implemented, and are needed for some of the string conversion routines in Chibi. Also, they're incredibly useful. Whenever you have a non-trivial utility for processing textual data, such as a parser, it can be useful to apply the utility to either in-memory strings or to file streams. Providing both interfaces doubles the API,[1] and causes corresponding code duplication.[2]

[1]Unless you have functions which take multiple textual parameters and want to provide all variations, in which case you get a combinatorial explosion in the size of the API.

[2]With Lisp macros you could abstract this out and remove at least the code duplication, if not the API duplication, but C macros aren't powerful enough.

To alleviate this problem, there are various extensions to libc allowing you to treat a string as a stream. You can then write the API only worrying about streams, and let the user convert to and from strings as necessary.[3] The POSIX [ fmemopen(3)] extension lets you open a string as an input port, while [ open_memstream(3)] can be used to accumulate output to a port into a string. In addition to API simplification, these functions help reduce errors in constructing strings by preventing buffer overflows. BSD systems provide the [ funopen(3)] function, which accepts callback functions for reading, writing, seeking and closing in a virtual stream.[4] Rather than a string, these callbacks are passed an opaque void* cookie, allowing you to base your streams on whatever you want.

[3]The opposite approach is to only support strings in your API, and then let the user convert file descriptors to strings via anonymous mmap.

[4]GNU libc provides identical functionality with the [ fopencookie(3)] utility, using superficially different syntax. Why they didn't copy BSD is beyond me.

While it's trivial to implement fmemopen(3) with funopen(3), the only truly portable implementation is to write to and read from temporary files, which is silly. So I was held up in porting to Plan 9 and Windows, and I certainly didn't want to duplicate the sexp reader for both strings and streams. Now, Plan 9 is a very minimal OS, and traditionally it focuses on separate programs rather than libraries, and simple line-oriented formats that are easy to pass through a pipeline, so it has little use for parsers, which explains why Plan 9 doesn't have the equivalent functionality. But why Windows should have no equivalent, despite the usefulness and immunity to buffer overflows at first surprised me, until I remembered Windows is fundamentally a C++ OS. The C++ equivalent is [ stringstreams], or more generally to simply provide your own class which overloads the << and >> iostream operators. funopen(3) with its callback functions and opaque cookie is just the C way of rolling your own object system.

In the end, I fell back on the old adage that you can solve any problem with an extra level of indirection. So when neither fmemopen(3) nor funopen(3) are available, chibi will implement string ports by maintaining its own buffer in the port structure, which otherwise contains the C stream. It works, but it would be nice if more systems provided string streams by default.