chiark / gitweb /
bus: don't skip interfaces in bus_message_map_properties_changed()
[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-internal.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_stride(unifont *u) {
185         assert(u);
186
187         return u->header.glyph_stride;
188 }
189
190 int unifont_lookup(unifont *u, unifont_glyph *out, uint32_t ucs4) {
191         unifont_glyph_header h = { };
192         const void *b = NULL;
193         unifont_glyph g = { };
194         int r;
195
196         assert_return(u, -EINVAL);
197
198         r = unifont_fetch_glyph(u, &h, &b, ucs4);
199         if (r < 0)
200                 return r;
201
202         g.width = h.width * 8U;
203         g.height = 16U;
204         g.stride = u->header.glyph_stride;
205         g.cwidth = h.width;
206         g.data = b;
207
208         if (out)
209                 memcpy(out, &g, sizeof(g));
210         return 0;
211 }