]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/vtfontcvt/vtfontcvt.c
sysctl(9): Fix a few mandoc related issues
[FreeBSD/FreeBSD.git] / usr.bin / vtfontcvt / vtfontcvt.c
1 /*-
2  * Copyright (c) 2009, 2014 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Ed Schouten under sponsorship from the
6  * FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/types.h>
34 #include <sys/fnv_hash.h>
35 #include <sys/endian.h>
36 #include <sys/param.h>
37 #include <sys/queue.h>
38 #include <sys/font.h>
39
40 #include <assert.h>
41 #include <err.h>
42 #include <stdint.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <stdbool.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <lz4.h>
49
50 #define VFNT_MAXGLYPHS 131072
51 #define VFNT_MAXDIMENSION 128
52
53 static unsigned int width = 8, wbytes, height = 16;
54
55 struct glyph {
56         TAILQ_ENTRY(glyph)       g_list;
57         SLIST_ENTRY(glyph)       g_hash;
58         uint8_t                 *g_data;
59         unsigned int             g_index;
60 };
61
62 #define FONTCVT_NHASH 4096
63 TAILQ_HEAD(glyph_list, glyph);
64 static SLIST_HEAD(, glyph) glyph_hash[FONTCVT_NHASH];
65 static struct glyph_list glyphs[VFNT_MAPS] = {
66         TAILQ_HEAD_INITIALIZER(glyphs[0]),
67         TAILQ_HEAD_INITIALIZER(glyphs[1]),
68         TAILQ_HEAD_INITIALIZER(glyphs[2]),
69         TAILQ_HEAD_INITIALIZER(glyphs[3]),
70 };
71 static unsigned int glyph_total, glyph_count[4], glyph_unique, glyph_dupe;
72
73 struct mapping {
74         TAILQ_ENTRY(mapping)     m_list;
75         unsigned int             m_char;
76         unsigned int             m_length;
77         struct glyph            *m_glyph;
78 };
79
80 TAILQ_HEAD(mapping_list, mapping);
81 static struct mapping_list maps[VFNT_MAPS] = {
82         TAILQ_HEAD_INITIALIZER(maps[0]),
83         TAILQ_HEAD_INITIALIZER(maps[1]),
84         TAILQ_HEAD_INITIALIZER(maps[2]),
85         TAILQ_HEAD_INITIALIZER(maps[3]),
86 };
87 static unsigned int mapping_total, map_count[4], map_folded_count[4],
88     mapping_unique, mapping_dupe;
89
90 enum output_format {
91         VT_FONT,                /* default */
92         VT_C_SOURCE,            /* C source for built in fonts */
93         VT_C_COMPRESSED         /* C source with compressed font data */
94 };
95
96 struct whitelist {
97         uint32_t c;
98         uint32_t len;
99 };
100
101 /*
102  * Compressed font glyph list. To be used with boot loader, we need to have
103  * ascii set and box drawing chars.
104  */
105 static struct whitelist c_list[] = {
106         { .c = 0, .len = 0 },           /* deault char */
107         { .c = 0x20, .len = 0x5f },
108         { .c = 0x2500, .len = 0 },      /* single frame */
109         { .c = 0x2502, .len = 0 },
110         { .c = 0x250c, .len = 0 },
111         { .c = 0x2510, .len = 0 },
112         { .c = 0x2514, .len = 0 },
113         { .c = 0x2518, .len = 0 },
114         { .c = 0x2550, .len = 1 },      /* double frame */
115         { .c = 0x2554, .len = 0 },
116         { .c = 0x2557, .len = 0 },
117         { .c = 0x255a, .len = 0 },
118         { .c = 0x255d, .len = 0 },
119 };
120
121 /*
122  * Uncompressed source. For x86 we need cp437 so the vga text mode
123  * can program font into the vga card.
124  */
125 static struct whitelist s_list[] = {
126         { .c = 0, .len = 0 },           /* deault char */
127         { .c = 0x20, .len = 0x5f },     /* ascii set */
128         { .c = 0xA0, .len = 0x5f },     /* latin 1 */
129         { .c = 0x0192, .len = 0 },
130         { .c = 0x0332, .len = 0 },      /* composing lower line */
131         { .c = 0x0393, .len = 0 },
132         { .c = 0x0398, .len = 0 },
133         { .c = 0x03A3, .len = 0 },
134         { .c = 0x03A6, .len = 0 },
135         { .c = 0x03A9, .len = 0 },
136         { .c = 0x03B1, .len = 1 },
137         { .c = 0x03B4, .len = 0 },
138         { .c = 0x03C0, .len = 0 },
139         { .c = 0x03C3, .len = 0 },
140         { .c = 0x03C4, .len = 0 },
141         { .c = 0x207F, .len = 0 },
142         { .c = 0x20A7, .len = 0 },
143         { .c = 0x2205, .len = 0 },
144         { .c = 0x220A, .len = 0 },
145         { .c = 0x2219, .len = 1 },
146         { .c = 0x221E, .len = 0 },
147         { .c = 0x2229, .len = 0 },
148         { .c = 0x2248, .len = 0 },
149         { .c = 0x2261, .len = 0 },
150         { .c = 0x2264, .len = 1 },
151         { .c = 0x2310, .len = 0 },
152         { .c = 0x2320, .len = 1 },
153         { .c = 0x2500, .len = 0 },
154         { .c = 0x2502, .len = 0 },
155         { .c = 0x250C, .len = 0 },
156         { .c = 0x2510, .len = 0 },
157         { .c = 0x2514, .len = 0 },
158         { .c = 0x2518, .len = 0 },
159         { .c = 0x251C, .len = 0 },
160         { .c = 0x2524, .len = 0 },
161         { .c = 0x252C, .len = 0 },
162         { .c = 0x2534, .len = 0 },
163         { .c = 0x253C, .len = 0 },
164         { .c = 0x2550, .len = 0x1c },
165         { .c = 0x2580, .len = 0 },
166         { .c = 0x2584, .len = 0 },
167         { .c = 0x2588, .len = 0 },
168         { .c = 0x258C, .len = 0 },
169         { .c = 0x2590, .len = 3 },
170         { .c = 0x25A0, .len = 0 },
171 };
172
173 static bool filter = true;
174 static enum output_format format = VT_FONT;
175 /* Type for write callback. */
176 typedef size_t (*vt_write)(const void *, size_t, size_t, FILE *);
177 static uint8_t *uncompressed;
178
179 static void
180 usage(void)
181 {
182
183         (void)fprintf(stderr, "usage: vtfontcvt "
184             "[-n] [-f font|source|compressed-source] [-w width] "
185             "[-h height]\n\t[-v] normal.bdf [bold.bdf] out.fnt\n");
186         exit(1);
187 }
188
189 static void *
190 xmalloc(size_t size)
191 {
192         void *m;
193
194         if ((m = calloc(1, size)) == NULL)
195                 errx(1, "memory allocation failure");
196         return (m);
197 }
198
199 static int
200 add_mapping(struct glyph *gl, unsigned int c, unsigned int map_idx)
201 {
202         struct mapping *mp, *mp_temp;
203         struct mapping_list *ml;
204
205         mapping_total++;
206
207         mp = xmalloc(sizeof *mp);
208         mp->m_char = c;
209         mp->m_glyph = gl;
210         mp->m_length = 0;
211
212         ml = &maps[map_idx];
213         if (TAILQ_LAST(ml, mapping_list) == NULL ||
214             TAILQ_LAST(ml, mapping_list)->m_char < c) {
215                 /* Common case: empty list or new char at end of list. */
216                 TAILQ_INSERT_TAIL(ml, mp, m_list);
217         } else {
218                 /* Find insertion point for char; cannot be at end. */
219                 TAILQ_FOREACH(mp_temp, ml, m_list) {
220                         if (mp_temp->m_char >= c) {
221                                 TAILQ_INSERT_BEFORE(mp_temp, mp, m_list);
222                                 break;
223                         }
224                 }
225         }
226
227         map_count[map_idx]++;
228         mapping_unique++;
229
230         return (0);
231 }
232
233 static int
234 dedup_mapping(unsigned int map_idx)
235 {
236         struct mapping *mp_bold, *mp_normal, *mp_temp;
237         unsigned normal_map_idx = map_idx - VFNT_MAP_BOLD;
238
239         assert(map_idx == VFNT_MAP_BOLD || map_idx == VFNT_MAP_BOLD_RIGHT);
240         mp_normal = TAILQ_FIRST(&maps[normal_map_idx]);
241         TAILQ_FOREACH_SAFE(mp_bold, &maps[map_idx], m_list, mp_temp) {
242                 while (mp_normal->m_char < mp_bold->m_char)
243                         mp_normal = TAILQ_NEXT(mp_normal, m_list);
244                 if (mp_bold->m_char != mp_normal->m_char)
245                         errx(1, "Character %u not in normal font!",
246                             mp_bold->m_char);
247                 if (mp_bold->m_glyph != mp_normal->m_glyph)
248                         continue;
249
250                 /* No mapping is needed if it's equal to the normal mapping. */
251                 TAILQ_REMOVE(&maps[map_idx], mp_bold, m_list);
252                 free(mp_bold);
253                 mapping_dupe++;
254         }
255         return (0);
256 }
257
258 static struct glyph *
259 add_glyph(const uint8_t *bytes, unsigned int map_idx, int fallback)
260 {
261         struct glyph *gl;
262         int hash;
263
264         glyph_total++;
265         glyph_count[map_idx]++;
266
267         /* Return existing glyph if we have an identical one. */
268         hash = fnv_32_buf(bytes, wbytes * height, FNV1_32_INIT) % FONTCVT_NHASH;
269         SLIST_FOREACH(gl, &glyph_hash[hash], g_hash) {
270                 if (memcmp(gl->g_data, bytes, wbytes * height) == 0) {
271                         glyph_dupe++;
272                         return (gl);
273                 }
274         }
275
276         /* Allocate new glyph. */
277         gl = xmalloc(sizeof *gl);
278         gl->g_data = xmalloc(wbytes * height);
279         memcpy(gl->g_data, bytes, wbytes * height);
280         if (fallback)
281                 TAILQ_INSERT_HEAD(&glyphs[map_idx], gl, g_list);
282         else
283                 TAILQ_INSERT_TAIL(&glyphs[map_idx], gl, g_list);
284         SLIST_INSERT_HEAD(&glyph_hash[hash], gl, g_hash);
285
286         glyph_unique++;
287         if (glyph_unique > VFNT_MAXGLYPHS)
288                 errx(1, "too many glyphs (%u)", glyph_unique);
289         return (gl);
290 }
291
292 static bool
293 check_whitelist(unsigned c)
294 {
295         struct whitelist *w = NULL;
296         int i, n = 0;
297
298         if (filter == false)
299                 return (true);
300
301         if (format == VT_C_SOURCE) {
302                 w = s_list;
303                 n = sizeof (s_list) / sizeof (s_list[0]);
304         }
305         if (format == VT_C_COMPRESSED) {
306                 w = c_list;
307                 n = sizeof (c_list) / sizeof (c_list[0]);
308         }
309         if (w == NULL)
310                 return (true);
311         for (i = 0; i < n; i++) {
312                 if (c >= w[i].c && c <= w[i].c + w[i].len)
313                         return (true);
314         }
315         return (false);
316 }
317
318 static int
319 add_char(unsigned curchar, unsigned map_idx, uint8_t *bytes, uint8_t *bytes_r)
320 {
321         struct glyph *gl;
322
323         /* Prevent adding two glyphs for 0xFFFD */
324         if (curchar == 0xFFFD) {
325                 if (map_idx < VFNT_MAP_BOLD)
326                         gl = add_glyph(bytes, 0, 1);
327         } else if (filter == false || curchar >= 0x20) {
328                 gl = add_glyph(bytes, map_idx, 0);
329                 if (add_mapping(gl, curchar, map_idx) != 0)
330                         return (1);
331                 if (bytes_r != NULL) {
332                         gl = add_glyph(bytes_r, map_idx + 1, 0);
333                         if (add_mapping(gl, curchar, map_idx + 1) != 0)
334                                 return (1);
335                 }
336         }
337         return (0);
338 }
339
340 /*
341  * Right-shift glyph row.
342  */
343 static void
344 rshift_row(uint8_t *buf, size_t len, size_t shift)
345 {
346         ssize_t i, off_byte = shift / 8;
347         size_t off_bit = shift % 8;
348
349         if (shift == 0)
350                 return;
351         for (i = len - 1; i >= 0; i--)
352                 buf[i] = (i >= off_byte ? buf[i - off_byte] >> off_bit : 0) |
353                     (i > off_byte ? buf[i - off_byte - 1] << (8 - off_bit) : 0);
354 }
355
356 /*
357  * Split double-width characters into left and right half. Single-width
358  * characters in _left_ only.
359  */
360 static int
361 split_row(uint8_t *left, uint8_t *right, uint8_t *line, size_t w)
362 {
363         size_t s, i;
364
365         s = wbytes * 8 - width;
366
367         memcpy(left, line, wbytes);
368         *(left + wbytes - 1) &= 0xFF << s;
369
370         if (w > width) { /* Double-width character. */
371                 uint8_t t;
372
373                 for (i = 0; i < wbytes; i++) {
374                         t = *(line + wbytes + i - 1);
375                         t <<= 8 - s;
376                         t |= *(line + wbytes + i) >> s;
377                         *(right + i) = t;
378                 }
379                 *(right + wbytes - 1) &= 0xFF << s;
380         }
381         return (0);
382 }
383
384 static void
385 set_height(int h)
386 {
387         if (h <= 0 || h > VFNT_MAXDIMENSION)
388                 errx(1, "invalid height %d", h);
389         height = h;
390 }
391
392 static void
393 set_width(int w)
394 {
395         if (w <= 0 || w > VFNT_MAXDIMENSION)
396                 errx(1, "invalid width %d", w);
397         width = w;
398         wbytes = howmany(width, 8);
399 }
400
401 static int
402 parse_bdf(FILE *fp, unsigned int map_idx)
403 {
404         char *ln, *p;
405         size_t length;
406         uint8_t *line, *bytes, *bytes_r;
407         unsigned int curchar = 0, i, j, linenum = 0, bbwbytes;
408         int bbw, bbh, bbox, bboy;               /* Glyph bounding box. */
409         int fbbw = 0, fbbh, fbbox, fbboy;       /* Font bounding box. */
410         int dwidth = 0, dwy = 0;
411         int rv = -1;
412         char spc = '\0';
413
414         /*
415          * Step 1: Parse FONT logical font descriptor and FONTBOUNDINGBOX
416          * bounding box.
417          */
418         while ((ln = fgetln(fp, &length)) != NULL) {
419                 linenum++;
420                 ln[length - 1] = '\0';
421
422                 if (strncmp(ln, "FONT ", 5) == 0) {
423                         p = ln + 5;
424                         i = 0;
425                         while ((p = strchr(p, '-')) != NULL) {
426                                 p++;
427                                 i++;
428                                 if (i == 11) {
429                                         spc = *p;
430                                         break;
431                                 }
432                         }
433                 } else if (strncmp(ln, "FONTBOUNDINGBOX ", 16) == 0) {
434                         if (sscanf(ln + 16, "%d %d %d %d", &fbbw, &fbbh, &fbbox,
435                             &fbboy) != 4)
436                                 errx(1, "invalid FONTBOUNDINGBOX at line %u",
437                                     linenum);
438                         set_width(fbbw);
439                         set_height(fbbh);
440                         break;
441                 }
442         }
443         if (fbbw == 0)
444                 errx(1, "broken font header");
445         if (spc != 'c' && spc != 'C')
446                 errx(1, "font spacing \"C\" (character cell) required");
447
448         /* Step 2: Validate DWIDTH (Device Width) of all glyphs. */
449         while ((ln = fgetln(fp, &length)) != NULL) {
450                 linenum++;
451                 ln[length - 1] = '\0';
452
453                 if (strncmp(ln, "DWIDTH ", 7) == 0) {
454                         if (sscanf(ln + 7, "%d %d", &dwidth, &dwy) != 2)
455                                 errx(1, "invalid DWIDTH at line %u", linenum);
456                         if (dwy != 0 || (dwidth != fbbw && dwidth * 2 != fbbw))
457                                 errx(1, "bitmap with unsupported DWIDTH %d %d at line %u",
458                                     dwidth, dwy, linenum);
459                         if (dwidth < fbbw)
460                                 set_width(dwidth);
461                 }
462         }
463
464         /* Step 3: Restart at the beginning of the file and read glyph data. */
465         dwidth = bbw = bbh = 0;
466         rewind(fp);
467         linenum = 0;
468         bbwbytes = 0; /* GCC 4.2.1 "may be used uninitialized" workaround. */
469         bytes = xmalloc(wbytes * height);
470         bytes_r = xmalloc(wbytes * height);
471         line = xmalloc(wbytes * 2);
472         while ((ln = fgetln(fp, &length)) != NULL) {
473                 linenum++;
474                 ln[length - 1] = '\0';
475
476                 if (strncmp(ln, "ENCODING ", 9) == 0) {
477                         curchar = atoi(ln + 9);
478                 } else if (strncmp(ln, "DWIDTH ", 7) == 0) {
479                         dwidth = atoi(ln + 7);
480                 } else if (strncmp(ln, "BBX ", 4) == 0) {
481                         if (sscanf(ln + 4, "%d %d %d %d", &bbw, &bbh, &bbox,
482                              &bboy) != 4)
483                                 errx(1, "invalid BBX at line %u", linenum);
484                         if (bbw < 1 || bbh < 1 || bbw > fbbw || bbh > fbbh ||
485                             bbox < fbbox || bboy < fbboy ||
486                             bbh + bboy > fbbh + fbboy)
487                                 errx(1, "broken bitmap with BBX %d %d %d %d at line %u",
488                                     bbw, bbh, bbox, bboy, linenum);
489                         bbwbytes = howmany(bbw, 8);
490                 } else if (strncmp(ln, "BITMAP", 6) == 0 &&
491                     (ln[6] == ' ' || ln[6] == '\0')) {
492                         if (dwidth == 0 || bbw == 0 || bbh == 0)
493                                 errx(1, "broken char header at line %u!",
494                                     linenum);
495                         memset(bytes, 0, wbytes * height);
496                         memset(bytes_r, 0, wbytes * height);
497
498                         /*
499                          * Assume that the next _bbh_ lines are bitmap data.
500                          * ENDCHAR is allowed to terminate the bitmap
501                          * early but is not otherwise checked; any extra data
502                          * is ignored.
503                          */
504                         for (i = (fbbh + fbboy) - (bbh + bboy);
505                             i < (unsigned int)((fbbh + fbboy) - bboy); i++) {
506                                 if ((ln = fgetln(fp, &length)) == NULL)
507                                         errx(1, "unexpected EOF");
508                                 linenum++;
509                                 ln[length - 1] = '\0';
510                                 if (strcmp(ln, "ENDCHAR") == 0)
511                                         break;
512                                 if (strlen(ln) < bbwbytes * 2)
513                                         errx(1, "broken bitmap at line %u",
514                                             linenum);
515                                 memset(line, 0, wbytes * 2);
516                                 for (j = 0; j < bbwbytes; j++) {
517                                         unsigned int val;
518                                         if (sscanf(ln + j * 2, "%2x", &val) ==
519                                             0)
520                                                 break;
521                                         *(line + j) = (uint8_t)val;
522                                 }
523
524                                 rshift_row(line, wbytes * 2, bbox - fbbox);
525                                 rv = split_row(bytes + i * wbytes,
526                                      bytes_r + i * wbytes, line, dwidth);
527                                 if (rv != 0)
528                                         goto out;
529                         }
530
531                         if (check_whitelist(curchar) == true) {
532                                 rv = add_char(curchar, map_idx, bytes,
533                                     dwidth > (int)width ? bytes_r : NULL);
534                                 if (rv != 0)
535                                         goto out;
536                         }
537
538                         dwidth = bbw = bbh = 0;
539                 }
540         }
541
542 out:
543         free(bytes);
544         free(bytes_r);
545         free(line);
546         return (rv);
547 }
548
549 static int
550 parse_hex(FILE *fp, unsigned int map_idx)
551 {
552         char *ln, *p;
553         size_t length;
554         uint8_t *bytes = NULL, *bytes_r = NULL, *line = NULL;
555         unsigned curchar = 0, gwidth, gwbytes, i, j, chars_per_row;
556         int rv = 0;
557
558         while ((ln = fgetln(fp, &length)) != NULL) {
559                 ln[length - 1] = '\0';
560
561                 if (strncmp(ln, "# Height: ", 10) == 0) {
562                         if (bytes != NULL)
563                                 errx(1, "malformed input: Height tag after font data");
564                         set_height(atoi(ln + 10));
565                 } else if (strncmp(ln, "# Width: ", 9) == 0) {
566                         if (bytes != NULL)
567                                 errx(1, "malformed input: Width tag after font data");
568                         set_width(atoi(ln + 9));
569                 } else if (sscanf(ln, "%6x:", &curchar)) {
570                         if (bytes == NULL) {
571                                 bytes = xmalloc(wbytes * height);
572                                 bytes_r = xmalloc(wbytes * height);
573                                 line = xmalloc(wbytes * 2);
574                         }
575                         /* ln is guaranteed to have a colon here. */
576                         p = strchr(ln, ':') + 1;
577                         chars_per_row = strlen(p) / height;
578                         if (chars_per_row < wbytes * 2)
579                                 errx(1,
580                                     "malformed input: broken bitmap, character %06x",
581                                     curchar);
582                         gwidth = width * 2;
583                         gwbytes = howmany(gwidth, 8);
584                         if (chars_per_row < gwbytes * 2 || gwidth <= 8) {
585                                 gwidth = width; /* Single-width character. */
586                                 gwbytes = wbytes;
587                         }
588
589                         for (i = 0; i < height; i++) {
590                                 for (j = 0; j < gwbytes; j++) {
591                                         unsigned int val;
592                                         if (sscanf(p + j * 2, "%2x", &val) == 0)
593                                                 break;
594                                         *(line + j) = (uint8_t)val;
595                                 }
596                                 rv = split_row(bytes + i * wbytes,
597                                     bytes_r + i * wbytes, line, gwidth);
598                                 if (rv != 0)
599                                         goto out;
600                                 p += gwbytes * 2;
601                         }
602
603                         if (check_whitelist(curchar) == true) {
604                                 rv = add_char(curchar, map_idx, bytes,
605                                     gwidth != width ? bytes_r : NULL);
606                                 if (rv != 0)
607                                         goto out;
608                         }
609                 }
610         }
611 out:
612         free(bytes);
613         free(bytes_r);
614         free(line);
615         return (rv);
616 }
617
618 static int
619 parse_file(const char *filename, unsigned int map_idx)
620 {
621         FILE *fp;
622         size_t len;
623         int rv;
624
625         fp = fopen(filename, "r");
626         if (fp == NULL) {
627                 perror(filename);
628                 return (1);
629         }
630         len = strlen(filename);
631         if (len > 4 && strcasecmp(filename + len - 4, ".hex") == 0)
632                 rv = parse_hex(fp, map_idx);
633         else
634                 rv = parse_bdf(fp, map_idx);
635         fclose(fp);
636         return (rv);
637 }
638
639 static void
640 number_glyphs(void)
641 {
642         struct glyph *gl;
643         unsigned int i, idx = 0;
644
645         for (i = 0; i < VFNT_MAPS; i++)
646                 TAILQ_FOREACH(gl, &glyphs[i], g_list)
647                         gl->g_index = idx++;
648 }
649
650 /* Note we only deal with byte stream here. */
651 static size_t
652 write_glyph_source(const void *ptr, size_t size, size_t nitems, FILE *stream)
653 {
654         const uint8_t *data = ptr;
655         size_t i;
656
657         size *= nitems;
658         for (i = 0; i < size; i++) {
659                 if ((i % wbytes) == 0) {
660                         if (fprintf(stream, "\n") < 0)
661                                 return (0);
662                 }
663                 if (fprintf(stream, "0x%02x, ", data[i]) < 0)
664                         return (0);
665         }
666         if (fprintf(stream, "\n") < 0)
667                 nitems = 0;
668
669         return (nitems);
670 }
671
672 /* Write to buffer */
673 static size_t
674 write_glyph_buf(const void *ptr, size_t size, size_t nitems,
675     FILE *stream __unused)
676 {
677         static size_t index = 0;
678
679         size *= nitems;
680         (void) memmove(uncompressed + index, ptr, size);
681         index += size;
682
683         return (nitems);
684 }
685
686 static int
687 write_glyphs(FILE *fp, vt_write cb)
688 {
689         struct glyph *gl;
690         unsigned int i;
691
692         for (i = 0; i < VFNT_MAPS; i++) {
693                 TAILQ_FOREACH(gl, &glyphs[i], g_list)
694                         if (cb(gl->g_data, wbytes * height, 1, fp) != 1)
695                                 return (1);
696         }
697         return (0);
698 }
699
700 static void
701 fold_mappings(unsigned int map_idx)
702 {
703         struct mapping_list *ml = &maps[map_idx];
704         struct mapping *mn, *mp, *mbase;
705
706         mp = mbase = TAILQ_FIRST(ml);
707         for (mp = mbase = TAILQ_FIRST(ml); mp != NULL; mp = mn) {
708                 mn = TAILQ_NEXT(mp, m_list);
709                 if (mn != NULL && mn->m_char == mp->m_char + 1 &&
710                     mn->m_glyph->g_index == mp->m_glyph->g_index + 1)
711                         continue;
712                 mbase->m_length = mp->m_char - mbase->m_char + 1;
713                 mbase = mp = mn;
714                 map_folded_count[map_idx]++;
715         }
716 }
717
718 static int
719 write_mappings(FILE *fp, unsigned int map_idx)
720 {
721         struct mapping_list *ml = &maps[map_idx];
722         struct mapping *mp;
723         vfnt_map_t fm;
724         unsigned int i = 0, j = 0;
725
726         TAILQ_FOREACH(mp, ml, m_list) {
727                 j++;
728                 if (mp->m_length > 0) {
729                         i += mp->m_length;
730                         fm.vfm_src = htobe32(mp->m_char);
731                         fm.vfm_dst = htobe16(mp->m_glyph->g_index);
732                         fm.vfm_len = htobe16(mp->m_length - 1);
733                         if (fwrite(&fm, sizeof fm, 1, fp) != 1)
734                                 return (1);
735                 }
736         }
737         assert(i == j);
738         return (0);
739 }
740
741 static int
742 write_source_mappings(FILE *fp, unsigned int map_idx)
743 {
744         struct mapping_list *ml = &maps[map_idx];
745         struct mapping *mp;
746         unsigned int i = 0, j = 0;
747
748         TAILQ_FOREACH(mp, ml, m_list) {
749                 j++;
750                 if (mp->m_length > 0) {
751                         i += mp->m_length;
752                         if (fprintf(fp, "\t{ 0x%08x, 0x%04x, 0x%04x },\n",
753                             mp->m_char, mp->m_glyph->g_index,
754                             mp->m_length - 1) < 0)
755                                 return (1);
756                 }
757         }
758         assert(i == j);
759         return (0);
760 }
761
762 static int
763 write_fnt(const char *filename)
764 {
765         FILE *fp;
766         struct font_header fh = {
767                 .fh_magic = FONT_HEADER_MAGIC,
768         };
769
770         fp = fopen(filename, "wb");
771         if (fp == NULL) {
772                 perror(filename);
773                 return (1);
774         }
775
776         fh.fh_width = width;
777         fh.fh_height = height;
778         fh.fh_glyph_count = htobe32(glyph_unique);
779         fh.fh_map_count[0] = htobe32(map_folded_count[0]);
780         fh.fh_map_count[1] = htobe32(map_folded_count[1]);
781         fh.fh_map_count[2] = htobe32(map_folded_count[2]);
782         fh.fh_map_count[3] = htobe32(map_folded_count[3]);
783         if (fwrite(&fh, sizeof(fh), 1, fp) != 1) {
784                 perror(filename);
785                 fclose(fp);
786                 return (1);
787         }
788
789         if (write_glyphs(fp, &fwrite) != 0 ||
790             write_mappings(fp, VFNT_MAP_NORMAL) != 0 ||
791             write_mappings(fp, VFNT_MAP_NORMAL_RIGHT) != 0 ||
792             write_mappings(fp, VFNT_MAP_BOLD) != 0 ||
793             write_mappings(fp, VFNT_MAP_BOLD_RIGHT) != 0) {
794                 perror(filename);
795                 fclose(fp);
796                 return (1);
797         }
798
799         fclose(fp);
800         return (0);
801 }
802
803 static int
804 write_fnt_source(bool lz4, const char *filename)
805 {
806         FILE *fp;
807         int rv = 1;
808         size_t uncompressed_size = wbytes * height * glyph_unique;
809         size_t compressed_size = uncompressed_size;
810         uint8_t *compressed = NULL;
811
812         fp = fopen(filename, "w");
813         if (fp == NULL) {
814                 perror(filename);
815                 return (1);
816         }
817
818         if (lz4 == true) {
819                 uncompressed = xmalloc(uncompressed_size);
820                 compressed = xmalloc(uncompressed_size);
821         }
822         if (fprintf(fp, "/* Generated %ux%u console font source. */\n\n",
823             width, height) < 0)
824                 goto done;
825         if (fprintf(fp, "#include <sys/types.h>\n") < 0)
826                 goto done;
827         if (fprintf(fp, "#include <sys/param.h>\n") < 0)
828                 goto done;
829         if (fprintf(fp, "#include <sys/font.h>\n\n") < 0)
830                 goto done;
831
832         /* Write font bytes. */
833         if (fprintf(fp, "static uint8_t FONTDATA_%ux%u[] = {\n",
834             width, height) < 0)
835                 goto done;
836         if (lz4 == true) {
837                 if (write_glyphs(fp, &write_glyph_buf) != 0)
838                         goto done;
839                 compressed_size = lz4_compress(uncompressed, compressed,
840                     uncompressed_size, compressed_size, 0);
841                 if (write_glyph_source(compressed, compressed_size, 1, fp) != 1)
842                         goto done;
843                 free(uncompressed);
844                 free(compressed);
845         } else {
846                 if (write_glyphs(fp, &write_glyph_source) != 0)
847                         goto done;
848         }
849         if (fprintf(fp, "};\n\n") < 0)
850         goto done;
851
852         /* Write font maps. */
853         if (!TAILQ_EMPTY(&maps[VFNT_MAP_NORMAL])) {
854                 if (fprintf(fp, "static vfnt_map_t "
855                     "FONTMAP_NORMAL_%ux%u[] = {\n", width, height) < 0)
856                         goto done;
857                 if (write_source_mappings(fp, VFNT_MAP_NORMAL) != 0)
858                         goto done;
859                 if (fprintf(fp, "};\n\n") < 0)
860                         goto done;
861         }
862         if (!TAILQ_EMPTY(&maps[VFNT_MAP_NORMAL_RIGHT])) {
863                 if (fprintf(fp, "static vfnt_map_t "
864                     "FONTMAP_NORMAL_RH_%ux%u[] = {\n", width, height) < 0)
865                         goto done;
866                 if (write_source_mappings(fp, VFNT_MAP_NORMAL_RIGHT) != 0)
867                         goto done;
868                 if (fprintf(fp, "};\n\n") < 0)
869                         goto done;
870         }
871         if (!TAILQ_EMPTY(&maps[VFNT_MAP_BOLD])) {
872                 if (fprintf(fp, "static vfnt_map_t "
873                     "FONTMAP_BOLD_%ux%u[] = {\n", width, height) < 0)
874                         goto done;
875                 if (write_source_mappings(fp, VFNT_MAP_BOLD) != 0)
876                         goto done;
877                 if (fprintf(fp, "};\n\n") < 0)
878                         goto done;
879         }
880         if (!TAILQ_EMPTY(&maps[VFNT_MAP_BOLD_RIGHT])) {
881                 if (fprintf(fp, "static vfnt_map_t "
882                     "FONTMAP_BOLD_RH_%ux%u[] = {\n", width, height) < 0)
883                         goto done;
884                 if (write_source_mappings(fp, VFNT_MAP_BOLD_RIGHT) != 0)
885                         goto done;
886                 if (fprintf(fp, "};\n\n") < 0)
887                         goto done;
888         }
889
890         /* Write struct font. */
891         if (fprintf(fp, "struct vt_font font_%ux%u = {\n",
892             width, height) < 0)
893                 goto done;
894         if (fprintf(fp, "\t.vf_map\t= {\n") < 0)
895                 goto done;
896         if (TAILQ_EMPTY(&maps[VFNT_MAP_NORMAL])) {
897                 if (fprintf(fp, "\t\t\tNULL,\n") < 0)
898                         goto done;
899         } else {
900                 if (fprintf(fp, "\t\t\tFONTMAP_NORMAL_%ux%u,\n",
901                     width, height) < 0)
902                         goto done;
903         }
904         if (TAILQ_EMPTY(&maps[VFNT_MAP_NORMAL_RIGHT])) {
905                 if (fprintf(fp, "\t\t\tNULL,\n") < 0)
906                         goto done;
907         } else {
908                 if (fprintf(fp, "\t\t\tFONTMAP_NORMAL_RH_%ux%u,\n",
909                     width, height) < 0)
910                         goto done;
911         }
912         if (TAILQ_EMPTY(&maps[VFNT_MAP_BOLD])) {
913                 if (fprintf(fp, "\t\t\tNULL,\n") < 0)
914                         goto done;
915         } else {
916                 if (fprintf(fp, "\t\t\tFONTMAP_BOLD_%ux%u,\n",
917                     width, height) < 0)
918                         goto done;
919         }
920         if (TAILQ_EMPTY(&maps[VFNT_MAP_BOLD_RIGHT])) {
921                 if (fprintf(fp, "\t\t\tNULL\n") < 0)
922                         goto done;
923         } else {
924                 if (fprintf(fp, "\t\t\tFONTMAP_BOLD_RH_%ux%u\n",
925                     width, height) < 0)
926                         goto done;
927         }
928         if (fprintf(fp, "\t\t},\n") < 0)
929                 goto done;
930         if (lz4 == true) {
931                 if (fprintf(fp, "\t.vf_bytes\t= NULL,\n") < 0)
932                         goto done;
933         } else {
934                 if (fprintf(fp, "\t.vf_bytes\t= FONTDATA_%ux%u,\n",
935                     width, height) < 0) {
936                         goto done;
937                 }
938         }
939         if (fprintf(fp, "\t.vf_width\t= %u,\n", width) < 0)
940                 goto done;
941         if (fprintf(fp, "\t.vf_height\t= %u,\n", height) < 0)
942                 goto done;
943         if (fprintf(fp, "\t.vf_map_count\t= { %u, %u, %u, %u }\n",
944             map_folded_count[0], map_folded_count[1], map_folded_count[2],
945             map_folded_count[3]) < 0) {
946                 goto done;
947         }
948         if (fprintf(fp, "};\n\n") < 0)
949                 goto done;
950
951         /* Write bitmap data. */
952         if (fprintf(fp, "vt_font_bitmap_data_t font_data_%ux%u = {\n",
953             width, height) < 0)
954                 goto done;
955         if (fprintf(fp, "\t.vfbd_width\t= %u,\n", width) < 0)
956                 goto done;
957         if (fprintf(fp, "\t.vfbd_height\t= %u,\n", height) < 0)
958                 goto done;
959         if (lz4 == true) {
960                 if (fprintf(fp, "\t.vfbd_compressed_size\t= %zu,\n",
961                     compressed_size) < 0) {
962                         goto done;
963                 }
964                 if (fprintf(fp, "\t.vfbd_uncompressed_size\t= %zu,\n",
965                     uncompressed_size) < 0) {
966                         goto done;
967                 }
968                 if (fprintf(fp, "\t.vfbd_compressed_data\t= FONTDATA_%ux%u,\n",
969                     width, height) < 0) {
970                         goto done;
971                 }
972         } else {
973                 if (fprintf(fp, "\t.vfbd_compressed_size\t= 0,\n") < 0)
974                         goto done;
975                 if (fprintf(fp, "\t.vfbd_uncompressed_size\t= %zu,\n",
976                     uncompressed_size) < 0) {
977                         goto done;
978                 }
979                 if (fprintf(fp, "\t.vfbd_compressed_data\t= NULL,\n") < 0)
980                         goto done;
981         }
982         if (fprintf(fp, "\t.vfbd_font = &font_%ux%u\n", width, height) < 0)
983                 goto done;
984         if (fprintf(fp, "};\n") < 0)
985                 goto done;
986
987         rv = 0;
988 done:
989         if (rv != 0)
990                 perror(filename);
991         fclose(fp);
992         return (0);
993 }
994
995 static void
996 print_font_info(void)
997 {
998         printf(
999 "Statistics:\n"
1000 "- width:                       %6u\n"
1001 "- height:                      %6u\n"
1002 "- glyph_total:                 %6u\n"
1003 "- glyph_normal:                %6u\n"
1004 "- glyph_normal_right:          %6u\n"
1005 "- glyph_bold:                  %6u\n"
1006 "- glyph_bold_right:            %6u\n"
1007 "- glyph_unique:                %6u\n"
1008 "- glyph_dupe:                  %6u\n"
1009 "- mapping_total:               %6u\n"
1010 "- mapping_normal:              %6u\n"
1011 "- mapping_normal_folded:       %6u\n"
1012 "- mapping_normal_right:        %6u\n"
1013 "- mapping_normal_right_folded: %6u\n"
1014 "- mapping_bold:                %6u\n"
1015 "- mapping_bold_folded:         %6u\n"
1016 "- mapping_bold_right:          %6u\n"
1017 "- mapping_bold_right_folded:   %6u\n"
1018 "- mapping_unique:              %6u\n"
1019 "- mapping_dupe:                %6u\n",
1020             width, height,
1021             glyph_total,
1022             glyph_count[0],
1023             glyph_count[1],
1024             glyph_count[2],
1025             glyph_count[3],
1026             glyph_unique, glyph_dupe,
1027             mapping_total,
1028             map_count[0], map_folded_count[0],
1029             map_count[1], map_folded_count[1],
1030             map_count[2], map_folded_count[2],
1031             map_count[3], map_folded_count[3],
1032             mapping_unique, mapping_dupe);
1033 }
1034
1035 int
1036 main(int argc, char *argv[])
1037 {
1038         int ch, verbose = 0, rv = 0;
1039         char *outfile = NULL;
1040
1041         assert(sizeof(struct font_header) == 32);
1042         assert(sizeof(vfnt_map_t) == 8);
1043
1044         while ((ch = getopt(argc, argv, "nf:h:vw:o:")) != -1) {
1045                 switch (ch) {
1046                 case 'f':
1047                         if (strcmp(optarg, "font") == 0)
1048                                 format = VT_FONT;
1049                         else if (strcmp(optarg, "source") == 0)
1050                                 format = VT_C_SOURCE;
1051                         else if (strcmp(optarg, "compressed-source") == 0)
1052                                 format = VT_C_COMPRESSED;
1053                         else
1054                                 errx(1, "Invalid format: %s", optarg);
1055                         break;
1056                 case 'h':
1057                         height = atoi(optarg);
1058                         break;
1059                 case 'n':
1060                         filter = false;
1061                         break;
1062                 case 'o':
1063                         outfile = optarg;
1064                         break;
1065                 case 'v':
1066                         verbose = 1;
1067                         break;
1068                 case 'w':
1069                         width = atoi(optarg);
1070                         break;
1071                 case '?':
1072                 default:
1073                         usage();
1074                 }
1075         }
1076         argc -= optind;
1077         argv += optind;
1078
1079         if (outfile == NULL && (argc < 2 || argc > 3))
1080                 usage();
1081
1082         if (outfile == NULL) {
1083                 outfile = argv[argc - 1];
1084                 argc--;
1085         }
1086
1087         set_width(width);
1088         set_height(height);
1089
1090         if (parse_file(argv[0], VFNT_MAP_NORMAL) != 0)
1091                 return (1);
1092         argc--;
1093         argv++;
1094         if (argc == 1) {
1095                 if (parse_file(argv[0], VFNT_MAP_BOLD) != 0)
1096                         return (1);
1097                 argc--;
1098                 argv++;
1099         }
1100         number_glyphs();
1101         dedup_mapping(VFNT_MAP_BOLD);
1102         dedup_mapping(VFNT_MAP_BOLD_RIGHT);
1103         fold_mappings(0);
1104         fold_mappings(1);
1105         fold_mappings(2);
1106         fold_mappings(3);
1107
1108         switch (format) {
1109         case VT_FONT:
1110                 rv = write_fnt(outfile);
1111                 break;
1112         case VT_C_SOURCE:
1113                 rv = write_fnt_source(false, outfile);
1114                 break;
1115         case VT_C_COMPRESSED:
1116                 rv = write_fnt_source(true, outfile);
1117                 break;
1118         }
1119
1120         if (verbose)
1121                 print_font_info();
1122
1123         return (rv);
1124 }