1 /* vi: set sw=4 ts=4: */
3 * loadfont.c - Eugene Crosser & Andries Brouwer
7 * Loads the console font, and possibly the corresponding screen map(s).
8 * (Adapted for busybox by Matej Vela.)
10 * Licensed under GPLv2, see file LICENSE in this source tree.
12 //config:config LOADFONT
13 //config: bool "loadfont"
15 //config: select PLATFORM_LINUX
17 //config: This program loads a console font from standard input.
19 //config:config SETFONT
20 //config: bool "setfont"
22 //config: select PLATFORM_LINUX
24 //config: Allows to load console screen map. Useful for i18n.
26 //config:config FEATURE_SETFONT_TEXTUAL_MAP
27 //config: bool "Support reading textual screen maps"
29 //config: depends on SETFONT
31 //config: Support reading textual screen maps.
33 //config:config DEFAULT_SETFONT_DIR
34 //config: string "Default directory for console-tools files"
36 //config: depends on SETFONT
38 //config: Directory to use if setfont's params are simple filenames
39 //config: (not /path/to/file or ./file). Default is "" (no default directory).
41 //config:comment "Common options for loadfont and setfont"
42 //config: depends on LOADFONT || SETFONT
44 //config:config FEATURE_LOADFONT_PSF2
45 //config: bool "Support PSF2 console fonts"
47 //config: depends on LOADFONT || SETFONT
49 //config:config FEATURE_LOADFONT_RAW
50 //config: bool "Support old (raw) console fonts"
52 //config: depends on LOADFONT || SETFONT
54 //applet:IF_LOADFONT(APPLET(loadfont, BB_DIR_USR_SBIN, BB_SUID_DROP))
55 //applet:IF_SETFONT(APPLET(setfont, BB_DIR_USR_SBIN, BB_SUID_DROP))
57 //kbuild:lib-$(CONFIG_LOADFONT) += loadfont.o
58 //kbuild:lib-$(CONFIG_SETFONT) += loadfont.o
60 //usage:#define loadfont_trivial_usage
62 //usage:#define loadfont_full_usage "\n\n"
63 //usage: "Load a console font from stdin"
64 /* //usage: "\n -C TTY Affect TTY instead of /dev/tty" */
66 //usage:#define loadfont_example_usage
67 //usage: "$ loadfont < /etc/i18n/fontname\n"
69 //usage:#define setfont_trivial_usage
70 //usage: "FONT [-m MAPFILE] [-C TTY]"
71 //usage:#define setfont_full_usage "\n\n"
72 //usage: "Load a console font\n"
73 //usage: "\n -m MAPFILE Load console screen map"
74 //usage: "\n -C TTY Affect TTY instead of /dev/tty"
76 //usage:#define setfont_example_usage
77 //usage: "$ setfont -m koi8-r /etc/i18n/fontname\n"
83 # define KDFONTOP 0x4B72
84 struct console_font_op {
85 unsigned op; /* KD_FONT_OP_* */
86 unsigned flags; /* KD_FONT_FLAG_* */
87 unsigned width, height;
89 unsigned char *data; /* font data with height fixed to 32 */
91 # define KD_FONT_OP_SET 0 /* Set font */
92 # define KD_FONT_OP_GET 1 /* Get font */
93 # define KD_FONT_OP_SET_DEFAULT 2 /* Set font to default, data points to name / NULL */
94 # define KD_FONT_OP_COPY 3 /* Copy from another console */
95 # define KD_FONT_FLAG_OLD 0x80000000 /* Invoked via old interface */
96 # define KD_FONT_FLAG_DONT_RECALC 1 /* Don't call adjust_height() */
97 /* (Used internally for PIO_FONT support) */
105 PSF1_MODEHASTAB = 0x02,
106 PSF1_MODEHASSEQ = 0x04,
108 PSF1_STARTSEQ = 0xfffe,
109 PSF1_SEPARATOR = 0xffff,
113 unsigned char magic[2]; /* Magic number */
114 unsigned char mode; /* PSF font mode */
115 unsigned char charsize; /* Character size */
118 #define psf1h(x) ((struct psf1_header*)(x))
120 #define PSF1_MAGIC_OK(x) ( \
121 (x)->magic[0] == PSF1_MAGIC0 \
122 && (x)->magic[1] == PSF1_MAGIC1 \
125 #if ENABLE_FEATURE_LOADFONT_PSF2
131 PSF2_HAS_UNICODE_TABLE = 0x01,
133 PSF2_STARTSEQ = 0xfe,
134 PSF2_SEPARATOR = 0xff
138 unsigned char magic[4];
139 unsigned int version;
140 unsigned int headersize; /* offset of bitmaps in file */
142 unsigned int length; /* number of glyphs */
143 unsigned int charsize; /* number of bytes for each character */
144 unsigned int height; /* max dimensions of glyphs */
145 unsigned int width; /* charsize = height * ((width + 7) / 8) */
148 #define psf2h(x) ((struct psf2_header*)(x))
150 #define PSF2_MAGIC_OK(x) ( \
151 (x)->magic[0] == PSF2_MAGIC0 \
152 && (x)->magic[1] == PSF2_MAGIC1 \
153 && (x)->magic[2] == PSF2_MAGIC2 \
154 && (x)->magic[3] == PSF2_MAGIC3 \
156 #endif /* ENABLE_FEATURE_LOADFONT_PSF2 */
159 static void do_loadfont(int fd, unsigned char *inbuf, int height, int width, int charsize, int fontsize)
162 int charwidth = 32 * ((width+7)/8);
165 if (height < 1 || height > 32 || width < 1 || width > 32)
166 bb_error_msg_and_die("bad character size %dx%d", height, width);
168 buf = xzalloc(charwidth * ((fontsize < 128) ? 128 : fontsize));
169 for (i = 0; i < fontsize; i++)
170 memcpy(buf + (i*charwidth), inbuf + (i*charsize), charsize);
173 struct console_font_op cfo;
174 cfo.op = KD_FONT_OP_SET;
178 cfo.charcount = fontsize;
180 xioctl(fd, KDFONTOP, &cfo);
187 * Format of the Unicode information:
189 * For each font position <uc>*<seq>*<term>
190 * where <uc> is a 2-byte little endian Unicode value (PSF1)
191 * or an UTF-8 coded value (PSF2),
192 * <seq> = <ss><uc><uc>*, <ss> = psf1 ? 0xFFFE : 0xFE,
193 * <term> = psf1 ? 0xFFFF : 0xFF.
194 * and * denotes zero or more occurrences of the preceding item.
197 * The leading <uc>* part gives Unicode symbols that are all
198 * represented by this font position. The following sequences
199 * are sequences of Unicode symbols - probably a symbol
200 * together with combining accents - also represented by
201 * this font position.
204 * At the font position for a capital A-ring glyph, we
206 * 00C5,212B,FFFE,0041,030A,FFFF
207 * Some font positions may be described by sequences only,
208 * namely when there is no precomposed Unicode value for the glyph.
210 #if !ENABLE_FEATURE_LOADFONT_PSF2
211 #define do_loadtable(fd, inbuf, tailsz, fontsize, psf2) \
212 do_loadtable(fd, inbuf, tailsz, fontsize)
214 static void do_loadtable(int fd, unsigned char *inbuf, int tailsz, int fontsize, int psf2)
216 #if !ENABLE_FEATURE_LOADFONT_PSF2
217 /* gcc 4.3.1 code size: */
218 # define psf2 0 /* +0 bytes */
219 // const int psf2 = 0; /* +8 bytes */
220 // enum { psf2 = 0 }; /* +13 bytes */
222 struct unimapinit advice;
223 struct unimapdesc ud;
229 maxct = tailsz; /* more than enough */
230 up = xmalloc(maxct * sizeof(*up));
232 for (glyph = 0; glyph < fontsize; glyph++) {
234 if (!psf2) { /* PSF1 */
235 unicode = (((uint16_t) inbuf[1]) << 8) + inbuf[0];
238 if (unicode == PSF1_SEPARATOR)
241 #if ENABLE_FEATURE_LOADFONT_PSF2
244 if (unicode == PSF2_SEPARATOR) {
246 } else if (unicode == PSF2_STARTSEQ) {
247 bb_error_msg_and_die("unicode sequences not implemented");
248 } else if (unicode >= 0xC0) {
250 unicode &= 0x01, maxct = 5;
251 else if (unicode >= 0xF8)
252 unicode &= 0x03, maxct = 4;
253 else if (unicode >= 0xF0)
254 unicode &= 0x07, maxct = 3;
255 else if (unicode >= 0xE0)
256 unicode &= 0x0F, maxct = 2;
258 unicode &= 0x1F, maxct = 1;
260 if (tailsz <= 0 || *inbuf < 0x80 || *inbuf > 0xBF)
261 bb_error_msg_and_die("illegal UTF-8 character");
263 unicode = (unicode << 6) + (*inbuf++ & 0x3F);
264 } while (--maxct > 0);
265 } else if (unicode >= 0x80) {
266 bb_error_msg_and_die("illegal UTF-8 character");
272 up[ct].unicode = unicode;
273 up[ct].fontpos = glyph;
278 /* Note: after PIO_UNIMAPCLR and before PIO_UNIMAP
279 * this printf did not work on many kernels */
281 advice.advised_hashsize = 0;
282 advice.advised_hashstep = 0;
283 advice.advised_hashlevel = 0;
284 xioctl(fd, PIO_UNIMAPCLR, &advice);
287 xioctl(fd, PIO_UNIMAP, &ud);
291 static void do_load(int fd, unsigned char *buffer, size_t len)
298 unsigned char *font = buffer;
299 unsigned char *table;
301 if (len >= sizeof(struct psf1_header) && PSF1_MAGIC_OK(psf1h(buffer))) {
302 if (psf1h(buffer)->mode > PSF1_MAXMODE)
303 bb_error_msg_and_die("unsupported psf file mode");
304 if (psf1h(buffer)->mode & PSF1_MODE512)
306 if (psf1h(buffer)->mode & PSF1_MODEHASTAB)
308 height = charsize = psf1h(buffer)->charsize;
309 font += sizeof(struct psf1_header);
311 #if ENABLE_FEATURE_LOADFONT_PSF2
312 if (len >= sizeof(struct psf2_header) && PSF2_MAGIC_OK(psf2h(buffer))) {
313 if (psf2h(buffer)->version > PSF2_MAXVERSION)
314 bb_error_msg_and_die("unsupported psf file version");
315 fontsize = psf2h(buffer)->length;
316 if (psf2h(buffer)->flags & PSF2_HAS_UNICODE_TABLE)
318 charsize = psf2h(buffer)->charsize;
319 height = psf2h(buffer)->height;
320 width = psf2h(buffer)->width;
321 font += psf2h(buffer)->headersize;
324 #if ENABLE_FEATURE_LOADFONT_RAW
325 if (len == 9780) { /* file with three code pages? */
326 charsize = height = 16;
328 } else if ((len & 0377) == 0) { /* bare font */
329 charsize = height = len / 256;
333 bb_error_msg_and_die("input file: bad length or unsupported font type");
336 #if !defined(PIO_FONTX) || defined(__sparc__)
338 bb_error_msg_and_die("only fontsize 256 supported");
341 table = font + fontsize * charsize;
344 if (table > buffer || (!has_table && table != buffer))
345 bb_error_msg_and_die("input file: bad length");
347 do_loadfont(fd, font, height, width, charsize, fontsize);
350 do_loadtable(fd, table, buffer - table, fontsize, has_table - 1);
355 int loadfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
356 int loadfont_main(int argc UNUSED_PARAM, char **argv)
359 unsigned char *buffer;
361 // no arguments allowed!
362 opt_complementary = "=0";
366 * We used to look at the length of the input file
367 * with stat(); now that we accept compressed files,
368 * just read the entire file.
369 * Len was 32k, but latarcyrheb-sun32.psfu is 34377 bytes
370 * (it has largish Unicode map).
373 buffer = xmalloc_read(STDIN_FILENO, &len);
374 // xmalloc_open_zipped_read_close(filename, &len);
376 bb_perror_msg_and_die("error reading input font");
377 do_load(get_console_fd_or_die(), buffer, len);
388 setfont [-O font+umap.orig] [-o font.orig] [-om cmap.orig]
389 [-ou umap.orig] [-N] [font.new ...] [-m cmap] [-u umap] [-C console]
392 -h NN Override font height
394 Save previous font in file
396 Save previous font and Unicode map in file
398 Store console map in file
400 Save previous Unicode map in file
402 Load console map or Unicode console map from file
404 Load Unicode table describing the font from file
409 0x80 U+0410 # CYRILLIC CAPITAL LETTER A
410 0x81 U+0411 # CYRILLIC CAPITAL LETTER BE
411 0x82 U+0412 # CYRILLIC CAPITAL LETTER VE
413 Set the font for the indicated console
418 #if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
419 static int ctoi(char *s)
421 if (s[0] == '\'' && s[1] != '\0' && s[2] == '\'' && s[3] == '\0')
424 if (s[0] == 'U' && s[1] == '+') {
430 return xstrtoul(s, 0);
434 int setfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
435 int setfont_main(int argc UNUSED_PARAM, char **argv)
440 unsigned char *buffer;
442 const char *tty_name = CURRENT_TTY;
444 opt_complementary = "=1";
445 opts = getopt32(argv, "m:C:", &mapfilename, &tty_name);
448 fd = xopen_nonblocking(tty_name);
450 if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not ""
451 if (*argv[0] != '/') {
452 // goto default fonts location. don't die if doesn't exist
453 chdir(CONFIG_DEFAULT_SETFONT_DIR "/consolefonts");
458 buffer = xmalloc_open_zipped_read_close(*argv, &len);
460 bb_simple_perror_msg_and_die(*argv);
461 do_load(fd, buffer, len);
463 // load the screen map, if any
464 if (opts & 1) { // -m
465 unsigned mode = PIO_SCRNMAP;
468 if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not ""
469 if (mapfilename[0] != '/') {
470 // goto default keymaps location
471 chdir(CONFIG_DEFAULT_SETFONT_DIR "/consoletrans");
475 map = xmalloc_open_zipped_read_close(mapfilename, &len);
477 bb_simple_perror_msg_and_die(mapfilename);
478 // file size is 256 or 512 bytes? -> assume binary map
479 if (len == E_TABSZ || len == 2*E_TABSZ) {
480 if (len == 2*E_TABSZ)
481 mode = PIO_UNISCRNMAP;
483 #if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
484 // assume textual Unicode console maps:
485 // 0x00 U+0000 # NULL (NUL)
486 // 0x01 U+0001 # START OF HEADING (SOH)
487 // 0x02 U+0002 # START OF TEXT (STX)
488 // 0x03 U+0003 # END OF TEXT (ETX)
494 if (ENABLE_FEATURE_CLEAN_UP)
496 map = xmalloc(E_TABSZ * sizeof(unsigned short));
498 #define unicodes ((unsigned short *)map)
500 for (i = 0; i < E_TABSZ; i++)
501 unicodes[i] = 0xf000 + i;
503 parser = config_open(mapfilename);
504 while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL | PARSE_MIN_DIE)) {
505 // parse code/value pair
506 int a = ctoi(token[0]);
507 int b = ctoi(token[1]);
508 if (a < 0 || a >= E_TABSZ
509 || b < 0 || b > 65535
511 bb_error_msg_and_die("map format");
515 // unicode character is met?
517 mode = PIO_UNISCRNMAP;
519 if (ENABLE_FEATURE_CLEAN_UP)
520 config_close(parser);
522 if (mode != PIO_UNISCRNMAP) {
523 #define asciis ((unsigned char *)map)
524 for (i = 0; i < E_TABSZ; i++)
525 asciis[i] = unicodes[i];
530 #endif // ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
533 xioctl(fd, mode, map);
535 if (ENABLE_FEATURE_CLEAN_UP)