INPUT /  OUTPUT /  LANGUAGE





Appendix: The libiol C Library

Throughout this guide, your programs have been started by the iol command, which opens a console and connects your program to it. Programs written in C or C++ have another option: libiol, a small library that lets a program open ioL consoles itself, without the iol command.

Why would you want to? Perhaps your program wants to open several consoles at once, or to keep its own terminal free for something else. Or perhaps it's a launcher that starts other programs in consoles of their own. libiol gives you that control, at a lower level than the iol command.

This appendix assumes you already know C. Everything in it works the same way from C++.


Getting started

Include the library's header, and link with -liol:

#include <iol.h>
gcc -o clicks clicks.c -liol

The library gives you three ways to work with a console:

Opening a console

The first two ways both start with iol_open_terminal:

iol_terminal iol_open_terminal(
    const char* rescom, const char* title,
    const char* containerKey, short errRedirect,
    int (*child_main)(void*), void* child_arg
);
ParameterWhat it's for
rescomthe console's console.rescom, as set by iol -r
titlethe console's starting title, as set by iol -t; NULL for none
containerKeythe key of a clientFrame to open the console inside, as with iol -k; NULL for a console of its own
errRedirect0 to have the console's error messages appear on your program's standard error; 1 to read them yourself, from errin
child_main, child_arga function to run as the console's program, and the argument to pass it; NULL to talk to the console directly

It returns an iol_terminal, which describes the new console:

FieldHolds
pidthe console's process ID. A negative value means the console couldn't be opened.
ina file descriptor to read the console's input from: whatever putLn sends
outa file descriptor to send markup to
errinif errRedirect was set, a file descriptor to read the console's error messages from, including those from putError; otherwise -1
erroutthe standard error of a function started with iol_deploy. It's 2, so the function's error messages go to your program's standard error. To send them somewhere else, put another file descriptor here before calling iol_deploy.
datafor the library's own use. Don't read or change it.

Talking to a console directly

Pass NULL for child_main, and your program talks to the console itself, through in and out. Everything works as it would for a program run by iol: you write markup, and read lines of input. Here's a button that counts its clicks:

#include <iol.h>
#include <stdio.h>

int main(void)
{
    iol_terminal t = iol_open_terminal("clicks", "Click Counter", NULL, 0, NULL, NULL);
    if (t.pid < 0) {
        fprintf(stderr, "Couldn't open an ioL console.\n");
        return 1;
    }

    FILE *out = fdopen(t.out, "w");
    FILE *in = fdopen(t.in, "r");

    fprintf(out, "<button {Click me} onClick=<putLn {click}>> <count:span {No clicks yet.}>\n");
    fflush(out);

    char line[100];
    int clicks = 0;
    while (fgets(line, sizeof line, in)) {
        clicks++;
        fprintf(out, "<count {Clicks so far: %d}>\n", clicks);
        fflush(out);
    }
    return 0;
}

Wrapping the descriptors with fdopen lets you use the familiar fprintf and fgets. Notice the fflush after each piece of markup: out isn't your program's standard output, so it's up to you to make sure the markup is sent straight away.

The console closes when your program closes its connection to it, which happens when your program ends.

A program working this way isn't limited to one console. Call iol_open_terminal again, and you have a second console, with its own in and out.

Giving the console a function to run

Alternatively, pass a function as child_main, then call iol_deploy to start it. The function becomes the console's program: its standard input and output are connected to the console, so it prints markup and reads input exactly as a program started by iol would. child_arg is passed to it:

#include <iol.h>
#include <stdio.h>

static int greeter(void *arg)
{
    const char *name = arg;

    printf("<box {Hello, %s!}>\n", name);
    printf("<button {Done} onClick=<putLn {done}>>\n");
    fflush(stdout);

    char line[100];
    fgets(line, sizeof line, stdin);
    return 0;
}

int main(void)
{
    iol_terminal t = iol_open_terminal("greeter", "Greeter", NULL, 0, greeter, "world");
    if (t.pid < 0) {
        fprintf(stderr, "Couldn't open an ioL console.\n");
        return 1;
    }
    iol_deploy(&t);
    return 0;
}

A few things to know about the function:

Once you've called iol_deploy, the in and out descriptors in the iol_terminal are no longer valid: the console belongs to the function now. If you want to talk to the console yourself, don't call iol_deploy.

Becoming a console

iol_exec_terminal works the other way round. It turns the process that calls it into an ioL console. The console's program is whatever is connected to that process's standard input and output, just as with iol -:

int iol_exec_terminal(const char* rescom, const char* title, const char* containerKey);

The parameters mean the same as for iol_open_terminal. If the console starts, the function never returns. If it does return, the console couldn't be started.

A common way to use it is to fork, connect pipes to the new process's standard input and output, and have it call iol_exec_terminal. Your program then talks to the console through the other ends of the pipes:

#include <iol.h>
#include <stdio.h>
#include <unistd.h>

int main(void)
{
    int to_console[2], from_console[2];
    pipe(to_console);
    pipe(from_console);

    if (fork() == 0) {
        /* The new process becomes the console. */
        dup2(to_console[0], 0);
        dup2(from_console[1], 1);
        close(to_console[1]);
        close(from_console[0]);
        iol_exec_terminal("forked", "Forked Console", NULL);
        _exit(1);   /* only reached if the console couldn't start */
    }

    close(to_console[0]);
    close(from_console[1]);
    FILE *out = fdopen(to_console[1], "w");
    FILE *in = fdopen(from_console[0], "r");

    fprintf(out, "<button {Ping} onClick=<putLn {ping}>> <reply:span>\n");
    fflush(out);

    char line[100];
    while (fgets(line, sizeof line, in)) {
        fprintf(out, "<reply {pong}>\n");
        fflush(out);
    }
    return 0;
}

Other functions

These functions change how the next console is opened, or how your program behaves. Call them before opening the console they're meant to affect.

FunctionDoes
iol_unbuffer_this_process()Turns off standard i/o buffering in your program, and in programs it later runs with the exec family of functions, so their output is sent as soon as it's printed.
iol_disable_tc()Turns off Turing completeness in the next console to be opened, like iol -s. Use it for content you don't trust, such as a document or a simple form. The chapter on hosting other programs lists what's disabled.
iol_unset_quiet()Has the welcome message printed when a new console is opened. Consoles opened by the library are quiet by default, as with iol -q.
iol_set_wrapper_flag()Tells ioL, when a console is opened, that your program is the leader, owner and wrapping container of the programs and consoles it starts. TODO: describe the effects.