The requirements above of not burdening applications that do not need stdio with required I/O support, and at the same time not burdening the programmer who chooses to use it with nasty porting issues, has led to a modular design. The uCR implementation of stdio barely even requires uCR!
The stdio support exists in its own in a library, libstdio. This library includes all the code to implement the stdio functionality, except the actual I/O to devices. The <stdio.h> header file describes the functions and definitions available in this library.
The library contains all the code to do buffering of input and output, and all the code to interface to a subprogram that uses stdio, but does not do any I/O. Instead, the library invokes methods of a device driver interface to actually manipulate devices. The <StdioDevice.h> header file describes the interfaces that the library uses to communicate with the I/O subsystem of the application.
Logically, the StdioDevice interface provides a way for the application to give names to devices, and to attach a common interface to those devices, for the purpose of communicating with the stdio library. The programmer then passes those names to fopen to open a stream attached to those devices. In paractice, the device driver interface allows the programmer to connect streams to any medium, physical or not.
The names understood by the library are broken into two parts: the device type and the instance name. The parts are seperated by a single colon (``:'') character. If no instance name is required, then the colon may be left out. Examples of valid names are
The first and second names are identical, all the others are unique. The meanings of the device types depend entirely on the devices made available by the application, and the meanings of the instance parts depend entirely on the device type associated with them. Use these names like this:
FILE*stream = fopen("uart16550:a", "w");
The fopen will parse the name, locate the StdioDeviceType that corresponds, and pass the instance part to StdioDeviceType::open to get a device. It saves that device until the fclose(stream) causes stdio to get rid of the object with the StdioDeviceType::close method.