Purpose
CPort stands for both C Portability and Character Ports.
As the overloaded name suggests, its goal is to provide an API to read and
write streams of Unicode code points on any platform that supports C,
even embedded and non-standard platforms that cannot use <stdio.h>
.
Concepts
Ports
A port provides a stream of characters. An input port provides an incoming stream for an application to read; an output port provides an outgoing stream to which an application may write.
From the application’s perspective, they use ports something like this:
C_Port *p = NULL;
C_Port_new(&p);
ASSERT(p != NULL);
C_Port_Status status =
C_Port_get(p, "/home/fmitchell/test.json");
// or C_Port_uri_get(p, "file:///home/fmitchell/test.json"));
if (status == CPORT_OK) {
wchar_t cp;
do {
status = C_Port_read_char(p, &cp);
if (status == CPORT_OK) {
/* do something with code point `cp` */
}
} while (cp >= 0 && status == CPORT_OK);
}
if (status != CPORT_OK) {
/* Handle error somewhow */
}
C_Port_release(&p); /* also closes the port if necessary */
As an abstraction layer over innumerable sources and sinks of text, ports provide no advanced functions to rewind or fast-forward along a file, interrogate the size or source of the stream, and so on.
Converter
To convert read octets (8-bit bytes) to UTF-8 or UTF-32, and to convert UTF-32 or UTF-8 back to arbitrary octets, C-Port will use a custom library called C-Converter to do the conversions.
Like the API below this avoids a direct dependency on libraries like iconv which may not exist – or be necessary – on all platforms.
Restricted Environments
Whike Unix Services lists the APIs used internally on Unix platforms, compiler switches will choose native APIs for non-Unix environments, including but not limited to Windows and embedded environments.
Internals are still a work in progress, but implementors need only implement the function prototypes below for their specific platform.
typedef enum _C_Port_Method {
MODE_CONNECT = 0,
MODE_DELETE,
MODE_GET,
MODE_PUT,
MODE_POST,
MODE_HEAD, /* RESERVED */
MODE_OPTIONS, /* RESERVED */
MODE_PATCH, /* RESERVED */
MODE_TRACE, /* RESERVED */
MODE_UNDEFINED
} C_Port_Method;
/*
* Callbacks to open/create an I/O stream.
* `uri` is the full URI given to a `C_Port_uri_<method>()` function.
* `method` denotes which `C_Port_uri_<method>()` function was called.
*
* If the resource at the `uri` may be read and/or written by `method`
* implementers should set `(*data)` with a non-NULL pointer
* to a data structure used to read and/or write to the resource
* and return CPORT_OK.
* Otherwise the implementer should return an informative error code.
*/
typedef C_Port_Status (*C_Port_Opener)(C_Port_Method method,
const char* uri,
void* (*data));
/*
* Callback to close/delete the I/O stream implementing `port`.
* `data` is a pointer to the structure created in the opener function.
*/
typedef C_Port_Status (*C_Port_Closer)(void* data);
/*
* Callback to read bytes and return a pointer to the bytes read,
* placing the number of bytes read in `*sptr`.
* `data` is a pointer to the structure created in the opener function.
*
* The implementer is responsible for creating and managing any buffers
* used in successive function calls, with the understanding that an
* application *could* be multi-threaded, but only one thread will access
* `data` at any given time.
*/
typedef const octet_t* (*C_Port_Reader)(void* data, size_t *sptr);
/*
* Callback to write up to `size` bytes from `buf`
* and return the number of bytes written.
* `data` is a pointer to the structure created in the opener function.
*/
typedef size_t (*C_Port_Writer)(void* data, size_t size, const octet_t* buf);
API
The full CPort API will look something like this.
Types
Data Types
/* defined in C99 */
#include <stdbool.h> /* defines bool */
#include <stddef.h> /* defines size_t */
#include <stdint.h> /* defines uint8_t */
#include <wchar.h> /* defines wchar_t */
typedef uint8_t octet_t;
typedef uint8_t utf8_t;
typedef struct _C_Port C_Port;
Status/Error Codes
typedef enum _C_Port_Status {
CPORT_OK = 0,
CPORT_OPEN_ERROR = 0x100,
CPORT_NAME_NOT_FOUND = 0x101,
CPORT_ALREADY_OPEN = 0x102,
/* to be continued ... */
CPORT_READ_ERROR = 0x200,
/* to be continued ... */
CPORT_WRITE_ERROR = 0x300,
/* to be continued ... */
CPORT_CONV_ERROR = 0x500,
/* to be continued ... */
CPORT_CLOSE_ERROR = 0x800,
CPORT_END_OF_STREAM = 0x801,
/* to be continued ... */
/* Uncategorized Error */
CPORT_UNKNOWN_ERROR = 0x7FFF
} C_Port_Status;
/* TODO: cross-reference with <errno.h> */
Error Handling
typedef void (*C_Port_Error_Handler)(C_Port* port, C_Port_Status code);
C_Port_Status C_Port_status(C_Port* port);
void C_Port_set_status(C_Port* port, C_Port_Status);
void C_Port_clear_status(C_Port* port);
Ports
Creating Ports
void C_Port_new(C_Port* *pptr);
Opening Ports
In the functions below:
-
port is a reference created by
C_Port_new()
. -
path is a platform specific path.
-
uri includes a Web protocol, host, and optionally port and resource path.
/** Open `path` for reading. */
C_Port_Status C_Port_get(C_Port* port, const char* path);
/**
* Open the `path` for reading or writing based on `mode`,
* which is the same as in Posix and Windows fopen(): "a", "r", or "w",
* with an optional "+".
* Windows may add a "t" or "b" for text or binary mode.
* Fully POSIX-compliant systems simply ignore "t" or "b".
*/
C_Port_Status C_Port_open(C_Port* port, const char* path, const char* mode);
/** Opens STDERR as a port. */
C_Port_Status C_Port_stderr(C_Port* port);
/** Opens STDIN as a port. */
C_Port_Status C_Port_stdin(C_Port* port);
/** Opens STDOUT as a port. */
C_Port_Status C_Port_stdout(C_Port* port);
/** Opens the resource at `uri` for reading and writing. */
C_Port_Status C_Port_uri_connect(C_Port* port, const char* uri);
/** Deletes the resource at `uri` without opening for reading or writing. */
C_Port_Status C_Port_uri_delete(C_Port* port, const char* uri);
/** Opens the resource at `uri` for reading. */
C_Port_Status C_Port_uri_get(C_Port* port, const char* uri);
/** Opens the resource at `uri` for writing THEN reading (e.g. HTTP POST). */
C_Port_Status C_Port_uri_post(C_Port* port, const char* uri);
/** Opens the resource at `uri` for writing. */
C_Port_Status C_Port_uri_put(C_Port* port, const char* uri);
C_Port_uri_<method>()
can open non-HTTP URIs including “file:” paths.
The
HTTP Method | Files | FTP | Other Network Protocols |
---|---|---|---|
post | POSIX fopen(path, "a+") |
read and write | |
delete | POSIX unlink(path) |
DELETE | |
get | POSIX fopen(path, "r") |
GET | read only |
post | POSIX fopen(path, "a") |
write THEN read | |
put | POSIX fopen(path, "w") |
PUT | write only |
Configuring Ports
The set_*
methods return boolean values to indicate whether
the command succeeded. Generally applications should configure a port
before reading or writing data.
const char* C_Port_attribute(C_Port* port, const char* name);
const char* C_Port_encoding(C_Port* port);
bool C_Port_is_binary(C_Port* port);
const char* C_Port_mime_type(C_Port* port);
bool C_Port_set_attribute(C_Port* port,
const char* name,
const char* value);
bool C_Port_set_binary(C_Port* port, bool bool);
bool C_Port_set_encoding(C_Port* port, const char* encoding);
bool C_Port_set_mime_type(C_Port* port, const char* type);
Reading From Input Ports
bool C_Port_is_end_of_stream(C_Port* port);
bool C_Port_is_input(C_Port* port);
bool C_Port_is_ready(C_Port* port);
C_Port_Status C_Port_peek_char(C_Port* port, wchar_t *cptr);
C_Port_Status C_Port_read_char(C_Port* port, wchar_t *cptr);
C_Port_Status C_Port_read_raw(C_Port* port,
size_t bufsiz,
size_t *szptr,
octet_t* buffer);
C_Port_Status C_Port_read_utf8(C_Port* port,
size_t bufsiz,
size_t *szptr,
utf8_t* buffer);
C_Port_Status C_Port_skip_char(C_Port* port);
Writing To Output Ports
size_t C_Port_capacity(C_Port* port, size_t *cptr);
bool C_Port_is_output(C_Port* port);
bool C_Port_is_writable(C_Port* port);
C_Port_Status C_Port_flush(C_Port* port);
C_Port_Status C_Port_write_char(C_Port* port, wchar_t char);
C_Port_Status C_Port_write_newline(C_Port* port);
C_Port_Status C_Port_write_raw(C_Port* port,
size_t bsz,
const octet_t* buf,
size_t *szptr);
C_Port_Status C_Port_write_utf8(C_Port* port,
size_t bsz,
const utf8_t* buf,
size_t *szptr);
/*
* for protocols like POST where the remote side will not respond
* until the other side finishes its response.
*/
C_Port_Status C_Port_write_end_of_stream(C_Port* port);
Closing Ports
/* closes and cleans up a Port */
void C_Port_close(C_Port* port);
bool C_Port_is_closed(C_Port* port);
Reference Counting Ports
/* adds reference */
C_Port* C_Port_retain(C_Port* port);
/* removes reference, closing if no longer needed */
void C_Port_release(C_Port* *pptr);
Unix Services
File I/O
#include <stdio.h>
- name
- Either a relative file name in the platform’s conventions or a File URL.
Open_File
: Port Open- Open a file for reading (GET), writing (PUT), appending (POST), or reading and appending (CONNECT). Does nothing and returns NULL on other methods. Uses character mode on platforms where that matters. Returns the file handle if successful.
Read_File
: Reader- Read bytes from the file if GET mode.
Write_File
: Writer- Writes bytes to the file if PUT or POST mode.
Delete_File
: Deleter- Deletes the named file.
Close_File
: Port Close- Closes the file handle.
Socket I/O
#include <sys/types.h>
#include <sys/socket.h>
- name
- Either “host
:
port” pair or a URL with a host and port. Open_Socket
: Port Open- Opens a client socket based on the hostname and port given in name. Uses full duplex if CONNECT, read-only if GET, write-only if PUT or POST. Does nothing and returns nothing on other methods. Returns the socket descriptor if successful.
Read_Socket
: Reader- Read bytes from the socket.
Write_Socket
: Writer- Write bytes to the socket.
Close_Socket
: Port Close- Close both sides of the socket.
Pipe I/O
#include <sys/types.h>
#include <sys/stat.h>
#indlude <fcntl.h>
- name
- The file name or other identifier for a named pipe.
Open_Pipe
: Port Open- Opens a named pipe based on name. Makes the pipe if it doesn’t exist. Opens for reading if GET, writing if PUT or POST, both if CONNECT. Does nothing and returns nothing on other methods. Returns the socket descriptor if successful.
Read_Pipe
: Reader- Read from the UNIX pipe.
Write_Pipe
: Writer- Write to the UNIX pipe.
Close_Pipe
: Port Close- Close the UNIX pipe.
CURL Services
#include <curl/curl.h>
The application will use the libcurl API to perform the requested operation to the requested URL, reading and writing from internal buffers rather than external files.
Unlike the other APIs, libcurl has been ported to Windows 64-bit and several other operating systems, and handles virtually every Internet protocol ever written.
TODO