chiark / gitweb /
importd: add new bus calls for importing local tar and raw images
[elogind.git] / src / libsystemd-terminal / unifont.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 /*
23  * Unifont
24  * This implements the unifont glyph-array parser and provides it via a simple
25  * API to the caller. No heavy transformations are performed so glyph-lookups
26  * stay as fast as possible.
27  */
28
29 #include <endian.h>
30 #include <fcntl.h>
31 #include <stdint.h>
32 #include <stdlib.h>
33 #include <sys/mman.h>
34 #include <sys/stat.h>
35 #include "macro.h"
36 #include "unifont-def.h"
37 #include "unifont.h"
38 #include "util.h"
39
40 struct unifont {
41         unsigned long ref;
42
43         int fd;
44         const uint8_t *map;
45         size_t size;
46
47         unifont_header header;
48         const void *glyphs;     /* unaligned! */
49         size_t n_glyphs;
50         size_t glyphsize;
51 };
52
53 static int unifont_fetch_header(unifont *u) {
54         unifont_header h = { };
55         uint64_t glyphsize;
56
57         if (u->size < UNIFONT_HEADER_SIZE_MIN)
58                 return -EBFONT;
59
60         assert_cc(sizeof(h) >= UNIFONT_HEADER_SIZE_MIN);
61         memcpy(&h, u->map, UNIFONT_HEADER_SIZE_MIN);
62
63         h.compatible_flags = le32toh(h.compatible_flags);
64         h.incompatible_flags = le32toh(h.incompatible_flags);
65         h.header_size = le32toh(h.header_size);
66         h.glyph_header_size = le16toh(h.glyph_header_size);
67         h.glyph_stride = le16toh(h.glyph_stride);
68         h.glyph_body_size = le64toh(h.glyph_body_size);
69
70         if (memcmp(h.signature, "DVDHRMUF", 8))
71                 return -EBFONT;
72         if (h.incompatible_flags != 0)
73                 return -EBFONT;
74         if (h.header_size < UNIFONT_HEADER_SIZE_MIN || h.header_size > u->size)
75                 return -EBFONT;
76         if (h.glyph_header_size + h.glyph_body_size < h.glyph_header_size)
77                 return -EBFONT;
78         if (h.glyph_stride * 16ULL > h.glyph_body_size)
79                 return -EBFONT;
80
81         glyphsize = h.glyph_header_size + h.glyph_body_size;
82
83         if (glyphsize == 0 || glyphsize > u->size - h.header_size) {
84                 u->n_glyphs = 0;
85         } else {
86                 u->glyphs = u->map + h.header_size;
87                 u->n_glyphs = (u->size - h.header_size) / glyphsize;
88                 u->glyphsize = glyphsize;
89         }
90
91         memcpy(&u->header, &h, sizeof(h));
92         return 0;
93 }
94
95 static int unifont_fetch_glyph(unifont *u, unifont_glyph_header *out_header, const void **out_body, uint32_t ucs4) {
96         unifont_glyph_header glyph_header = { };
97         const void *glyph_body = NULL;
98         const uint8_t *p;
99
100         if (ucs4 >= u->n_glyphs)
101                 return -ENOENT;
102
103         p = u->glyphs;
104
105         /* copy glyph-header data */
106         p += ucs4 * u->glyphsize;
107         memcpy(&glyph_header, p, MIN(sizeof(glyph_header), u->header.glyph_header_size));
108
109         /* copy glyph-body pointer */
110         p += u->header.glyph_header_size;
111         glyph_body = p;
112
113         if (glyph_header.width < 1)
114                 return -ENOENT;
115         if (glyph_header.width > u->header.glyph_stride)
116                 return -EBFONT;
117
118         memcpy(out_header, &glyph_header, sizeof(glyph_header));
119         *out_body = glyph_body;
120         return 0;
121 }
122
123 int unifont_new(unifont **out) {
124         _cleanup_(unifont_unrefp) unifont *u = NULL;
125         struct stat st;
126         int r;
127
128         assert_return(out, -EINVAL);
129
130         u = new0(unifont, 1);
131         if (!u)
132                 return -ENOMEM;
133
134         u->ref = 1;
135         u->fd = -1;
136         u->map = MAP_FAILED;
137
138         u->fd = open(UNIFONT_PATH, O_RDONLY | O_CLOEXEC | O_NOCTTY);
139         if (u->fd < 0)
140                 return -errno;
141
142         r = fstat(u->fd, &st);
143         if (r < 0)
144                 return -errno;
145
146         u->size = st.st_size;
147         u->map = mmap(NULL, u->size, PROT_READ, MAP_PRIVATE, u->fd, 0);
148         if (u->map == MAP_FAILED)
149                 return -errno;
150
151         r = unifont_fetch_header(u);
152         if (r < 0)
153                 return r;
154
155         *out = u;
156         u = NULL;
157         return 0;
158 }
159
160 unifont *unifont_ref(unifont *u) {
161         if (!u || !u->ref)
162                 return NULL;
163
164         ++u->ref;
165
166         return u;
167 }
168
169 unifont *unifont_unref(unifont *u) {
170         if (!u || !u->ref || --u->ref)
171                 return NULL;
172
173         if (u->map != MAP_FAILED)
174                 munmap((void*)u->map, u->size);
175         u->fd = safe_close(u->fd);
176         free(u);
177
178         return NULL;
179 }
180
181 unsigned int unifont_get_width(unifont *u) {
182         assert(u);
183
184         return 8U;
185 }
186
187 unsigned int unifont_get_height(unifont *u) {
188         assert(u);
189
190         return 16U;
191 }
192
193 unsigned int unifont_get_stride(unifont *u) {
194         assert(u);
195
196         return u->header.glyph_stride;
197 }
198
199 int unifont_lookup(unifont *u, unifont_glyph *out, uint32_t ucs4) {
200         unifont_glyph_header h = { };
201         const void *b = NULL;
202         unifont_glyph g = { };
203         int r;
204
205         assert_return(u, -EINVAL);
206
207         r = unifont_fetch_glyph(u, &h, &b, ucs4);
208         if (r < 0)
209                 return r;
210
211         g.width = h.width * 8U;
212         g.height = 16U;
213         g.stride = u->header.glyph_stride;
214         g.cwidth = h.width;
215         g.data = b;
216
217         if (out)
218                 memcpy(out, &g, sizeof(g));
219         return 0;
220 }
221
222 void unifont_fallback(unifont_glyph *out) {
223         static const uint8_t fallback_data[] = {
224                 /* unifont 0xfffd '�' (unicode replacement character) */
225                 0x00, 0x00, 0x00, 0x7e,
226                 0x66, 0x5a, 0x5a, 0x7a,
227                 0x76, 0x76, 0x7e, 0x76,
228                 0x76, 0x7e, 0x00, 0x00,
229         };
230
231         assert(out);
232
233         out->width = 8;
234         out->height = 16;
235         out->stride = 1;
236         out->cwidth = 1;
237         out->data = fallback_data;
238 }