chiark / gitweb /
switch_root: do not fail, if base_filesystem_create() failed
[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 <stdbool.h>
32 #include <stdint.h>
33 #include <stdlib.h>
34 #include <sys/mman.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <unistd.h>
38 #include "macro.h"
39 #include "unifont-def.h"
40 #include "unifont.h"
41 #include "util.h"
42
43 struct unifont {
44         unsigned long ref;
45
46         int fd;
47         const uint8_t *map;
48         size_t size;
49
50         unifont_header header;
51         const void *glyphs;     /* unaligned! */
52         size_t n_glyphs;
53         size_t glyphsize;
54 };
55
56 static int unifont_fetch_header(unifont *u) {
57         unifont_header h = { };
58         uint64_t glyphsize;
59
60         if (u->size < UNIFONT_HEADER_SIZE_MIN)
61                 return -EBFONT;
62
63         assert_cc(sizeof(h) >= UNIFONT_HEADER_SIZE_MIN);
64         memcpy(&h, u->map, UNIFONT_HEADER_SIZE_MIN);
65
66         h.compatible_flags = le32toh(h.compatible_flags);
67         h.incompatible_flags = le32toh(h.incompatible_flags);
68         h.header_size = le32toh(h.header_size);
69         h.glyph_header_size = le16toh(h.glyph_header_size);
70         h.glyph_stride = le16toh(h.glyph_stride);
71         h.glyph_body_size = le64toh(h.glyph_body_size);
72
73         if (memcmp(h.signature, "DVDHRMUF", 8))
74                 return -EBFONT;
75         if (h.incompatible_flags != 0)
76                 return -EBFONT;
77         if (h.header_size < UNIFONT_HEADER_SIZE_MIN || h.header_size > u->size)
78                 return -EBFONT;
79         if (h.glyph_header_size + h.glyph_body_size < h.glyph_header_size)
80                 return -EBFONT;
81         if (h.glyph_stride * 16ULL > h.glyph_body_size)
82                 return -EBFONT;
83
84         glyphsize = h.glyph_header_size + h.glyph_body_size;
85
86         if (glyphsize == 0 || glyphsize > u->size - h.header_size) {
87                 u->n_glyphs = 0;
88         } else {
89                 u->glyphs = u->map + h.header_size;
90                 u->n_glyphs = (u->size - h.header_size) / glyphsize;
91                 u->glyphsize = glyphsize;
92         }
93
94         memcpy(&u->header, &h, sizeof(h));
95         return 0;
96 }
97
98 static int unifont_fetch_glyph(unifont *u, unifont_glyph_header *out_header, const void **out_body, uint32_t ucs4) {
99         unifont_glyph_header glyph_header = { };
100         const void *glyph_body = NULL;
101         const uint8_t *p;
102
103         if (ucs4 >= u->n_glyphs)
104                 return -ENOENT;
105
106         p = u->glyphs;
107
108         /* copy glyph-header data */
109         p += ucs4 * u->glyphsize;
110         memcpy(&glyph_header, p, MIN(sizeof(glyph_header), u->header.glyph_header_size));
111
112         /* copy glyph-body pointer */
113         p += u->header.glyph_header_size;
114         glyph_body = p;
115
116         if (glyph_header.width < 1)
117                 return -ENOENT;
118         if (glyph_header.width > u->header.glyph_stride)
119                 return -EBFONT;
120
121         memcpy(out_header, &glyph_header, sizeof(glyph_header));
122         *out_body = glyph_body;
123         return 0;
124 }
125
126 int unifont_new(unifont **out) {
127         _cleanup_(unifont_unrefp) unifont *u = NULL;
128         struct stat st;
129         int r;
130
131         assert_return(out, -EINVAL);
132
133         u = new0(unifont, 1);
134         if (!u)
135                 return -ENOMEM;
136
137         u->ref = 1;
138         u->fd = -1;
139         u->map = MAP_FAILED;
140
141         u->fd = open(UNIFONT_PATH, O_RDONLY | O_CLOEXEC | O_NOCTTY);
142         if (u->fd < 0)
143                 return -errno;
144
145         r = fstat(u->fd, &st);
146         if (r < 0)
147                 return -errno;
148
149         u->size = st.st_size;
150         u->map = mmap(NULL, u->size, PROT_READ, MAP_PRIVATE, u->fd, 0);
151         if (u->map == MAP_FAILED)
152                 return -errno;
153
154         r = unifont_fetch_header(u);
155         if (r < 0)
156                 return r;
157
158         *out = u;
159         u = NULL;
160         return 0;
161 }
162
163 unifont *unifont_ref(unifont *u) {
164         if (!u || !u->ref)
165                 return NULL;
166
167         ++u->ref;
168
169         return u;
170 }
171
172 unifont *unifont_unref(unifont *u) {
173         if (!u || !u->ref || --u->ref)
174                 return NULL;
175
176         if (u->map != MAP_FAILED)
177                 munmap((void*)u->map, u->size);
178         u->fd = safe_close(u->fd);
179         free(u);
180
181         return NULL;
182 }
183
184 unsigned int unifont_get_width(unifont *u) {
185         assert(u);
186
187         return 8U;
188 }
189
190 unsigned int unifont_get_height(unifont *u) {
191         assert(u);
192
193         return 16U;
194 }
195
196 unsigned int unifont_get_stride(unifont *u) {
197         assert(u);
198
199         return u->header.glyph_stride;
200 }
201
202 int unifont_lookup(unifont *u, unifont_glyph *out, uint32_t ucs4) {
203         unifont_glyph_header h = { };
204         const void *b = NULL;
205         unifont_glyph g = { };
206         int r;
207
208         assert_return(u, -EINVAL);
209
210         r = unifont_fetch_glyph(u, &h, &b, ucs4);
211         if (r < 0)
212                 return r;
213
214         g.width = h.width * 8U;
215         g.height = 16U;
216         g.stride = u->header.glyph_stride;
217         g.cwidth = h.width;
218         g.data = b;
219
220         if (out)
221                 memcpy(out, &g, sizeof(g));
222         return 0;
223 }
224
225 void unifont_fallback(unifont_glyph *out) {
226         static const uint8_t fallback_data[] = {
227                 /* unifont 0xfffd '�' (unicode replacement character) */
228                 0x00, 0x00, 0x00, 0x7e,
229                 0x66, 0x5a, 0x5a, 0x7a,
230                 0x76, 0x76, 0x7e, 0x76,
231                 0x76, 0x7e, 0x00, 0x00,
232         };
233
234         assert(out);
235
236         out->width = 8;
237         out->height = 16;
238         out->stride = 1;
239         out->cwidth = 1;
240         out->data = fallback_data;
241 }