summaryrefslogtreecommitdiffstats
path: root/_posts/2018-05-29-Embedding-files-in-C.md
blob: 897cc87887da2efa5a823921e0a1352837654819 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
---
layout: post
title: Embedding files in C programs with koio
tags: [C, announcement]
---

Quick blog post today to introduce a new tool I wrote:
[koio](https://git.sr.ht/~sircmpwn/koio). This is a small tool which takes a
list of files and embeds them in a C file. A library provides an fopen shim
which checks the list of embedded files before resorting to the real filesystem.

I made this tool for [chopsui](https://github.com/SirCmpwn/chopsui), where I
eventually want to be able to bundle up sui markup, stylesheets, images, and so
on in a statically linked chopsui program. Many projects have small tools which
serve a similar purpose, but it was simple enough and useful enough that I chose
to make something generic so it could be used on several projects.

The usage is pretty simple. I can embed `ko_fopen.c` in a C file with this
command:

```sh
$ koio -o bundle.c ko_fopen.c://ko_fopen.c
```

I can compile and link with `bundle.c` and do something like this:

```c
#include <koio.h>

void koio_load_assets(void);
void koio_unload_assets(void);

int main(int argc, char **argv) {
    koio_load_assets();
    FILE *src = ko_fopen("//ko_fopen.c", "r");
    int c;
    while ((c = fgetc(src)) != EOF) {
        putchar(c);
    }
    fclose(src);
    koio_unload_assets();
    return 0;
}
```

The generated `bundle.c` looks like this:

```c
#include <koio.h>

static struct {
	const char *path;
	size_t len;
	char *data;
} files[] = {
	{
		.path = "//ko_fopen.c",
		.len = 408,
		.data =
"#define _POSIX_C_SOURCE 200809L\n#include <errno.h>\n#include <stdlib.h>\n#inc"
"lude <stdio.h>\n#include \"koio_private.h\"\n\nFILE *ko_fopen(const char *path"
", const char *mode) {\n\tstruct file_entry *entry = hashtable_get(&koio_vfs, p"
"ath);\n\tif (entry) {\n\t\tif (mode[0] != 'r' || mode[1] != '\\0') {\n\t\t\ter"
"rno = ENOTSUP;\n\t\t\treturn NULL;\n\t\t}\n\t\treturn fmemopen(entry->data, en"
"try->len, \"r\");\n\t}\n\treturn fopen(path, mode);\n}\n",
	},
};

void koio_load_assets(void) {
	ko_add_file(files[0].path, files[0].data, files[0].len);
}

void koio_unload_assets(void) {
	ko_del_file(files[0].path);
}
```

A very simple tool, but one that I hope people will find useful. It's very
lightweight:

- 312 lines of C
- /bin/koio is ~40 KiB statically linked to musl
- libkoio.a is ~18 KiB
- Only mandatory dependencies are POSIX 2008 and a C99 compiler
- Only optional dependency is [scdoc](https://git.sr.ht/~sircmpwn/scdoc) for the
  manual, which is similarly lightweight

Enjoy!