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 for PSF2 console fonts"
47 //config: depends on LOADFONT || SETFONT
49 //config: Support PSF2 console fonts.
51 //config:config FEATURE_LOADFONT_RAW
52 //config: bool "Support for old (raw) console fonts"
54 //config: depends on LOADFONT || SETFONT
56 //config: Support old (raw) console fonts.
58 //applet:IF_LOADFONT(APPLET(loadfont, BB_DIR_USR_SBIN, BB_SUID_DROP))
59 //applet:IF_SETFONT(APPLET(setfont, BB_DIR_USR_SBIN, BB_SUID_DROP))
61 //kbuild:lib-$(CONFIG_LOADFONT) += loadfont.o
62 //kbuild:lib-$(CONFIG_SETFONT) += loadfont.o
64 //usage:#define loadfont_trivial_usage
66 //usage:#define loadfont_full_usage "\n\n"
67 //usage: "Load a console font from stdin"
68 /* //usage: "\n -C TTY Affect TTY instead of /dev/tty" */
70 //usage:#define loadfont_example_usage
71 //usage: "$ loadfont < /etc/i18n/fontname\n"
73 //usage:#define setfont_trivial_usage
74 //usage: "FONT [-m MAPFILE] [-C TTY]"
75 //usage:#define setfont_full_usage "\n\n"
76 //usage: "Load a console font\n"
77 //usage: "\n -m MAPFILE Load console screen map"
78 //usage: "\n -C TTY Affect TTY instead of /dev/tty"
80 //usage:#define setfont_example_usage
81 //usage: "$ setfont -m koi8-r /etc/i18n/fontname\n"
87 # define KDFONTOP 0x4B72
88 struct console_font_op {
89 unsigned op; /* KD_FONT_OP_* */
90 unsigned flags; /* KD_FONT_FLAG_* */
91 unsigned width, height;
93 unsigned char *data; /* font data with height fixed to 32 */
95 # define KD_FONT_OP_SET 0 /* Set font */
96 # define KD_FONT_OP_GET 1 /* Get font */
97 # define KD_FONT_OP_SET_DEFAULT 2 /* Set font to default, data points to name / NULL */
98 # define KD_FONT_OP_COPY 3 /* Copy from another console */
99 # define KD_FONT_FLAG_OLD 0x80000000 /* Invoked via old interface */
100 # define KD_FONT_FLAG_DONT_RECALC 1 /* Don't call adjust_height() */
101 /* (Used internally for PIO_FONT support) */
102 #endif /* KDFONTOP */
109 PSF1_MODEHASTAB = 0x02,
110 PSF1_MODEHASSEQ = 0x04,
112 PSF1_STARTSEQ = 0xfffe,
113 PSF1_SEPARATOR = 0xffff,
117 unsigned char magic[2]; /* Magic number */
118 unsigned char mode; /* PSF font mode */
119 unsigned char charsize; /* Character size */
122 #define psf1h(x) ((struct psf1_header*)(x))
124 #define PSF1_MAGIC_OK(x) ( \
125 (x)->magic[0] == PSF1_MAGIC0 \
126 && (x)->magic[1] == PSF1_MAGIC1 \
129 #if ENABLE_FEATURE_LOADFONT_PSF2
135 PSF2_HAS_UNICODE_TABLE = 0x01,
137 PSF2_STARTSEQ = 0xfe,
138 PSF2_SEPARATOR = 0xff
142 unsigned char magic[4];
143 unsigned int version;
144 unsigned int headersize; /* offset of bitmaps in file */
146 unsigned int length; /* number of glyphs */
147 unsigned int charsize; /* number of bytes for each character */
148 unsigned int height; /* max dimensions of glyphs */
149 unsigned int width; /* charsize = height * ((width + 7) / 8) */
152 #define psf2h(x) ((struct psf2_header*)(x))
154 #define PSF2_MAGIC_OK(x) ( \
155 (x)->magic[0] == PSF2_MAGIC0 \
156 && (x)->magic[1] == PSF2_MAGIC1 \
157 && (x)->magic[2] == PSF2_MAGIC2 \
158 && (x)->magic[3] == PSF2_MAGIC3 \
160 #endif /* ENABLE_FEATURE_LOADFONT_PSF2 */
163 static void do_loadfont(int fd, unsigned char *inbuf, int height, int width, int charsize, int fontsize)
166 int charwidth = 32 * ((width+7)/8);
169 if (height < 1 || height > 32 || width < 1 || width > 32)
170 bb_error_msg_and_die("bad character size %dx%d", height, width);
172 buf = xzalloc(charwidth * ((fontsize < 128) ? 128 : fontsize));
173 for (i = 0; i < fontsize; i++)
174 memcpy(buf + (i*charwidth), inbuf + (i*charsize), charsize);
177 struct console_font_op cfo;
178 cfo.op = KD_FONT_OP_SET;
182 cfo.charcount = fontsize;
184 xioctl(fd, KDFONTOP, &cfo);
191 * Format of the Unicode information:
193 * For each font position <uc>*<seq>*<term>
194 * where <uc> is a 2-byte little endian Unicode value (PSF1)
195 * or an UTF-8 coded value (PSF2),
196 * <seq> = <ss><uc><uc>*, <ss> = psf1 ? 0xFFFE : 0xFE,
197 * <term> = psf1 ? 0xFFFF : 0xFF.
198 * and * denotes zero or more occurrences of the preceding item.
201 * The leading <uc>* part gives Unicode symbols that are all
202 * represented by this font position. The following sequences
203 * are sequences of Unicode symbols - probably a symbol
204 * together with combining accents - also represented by
205 * this font position.
208 * At the font position for a capital A-ring glyph, we
210 * 00C5,212B,FFFE,0041,030A,FFFF
211 * Some font positions may be described by sequences only,
212 * namely when there is no precomposed Unicode value for the glyph.
214 #if !ENABLE_FEATURE_LOADFONT_PSF2
215 #define do_loadtable(fd, inbuf, tailsz, fontsize, psf2) \
216 do_loadtable(fd, inbuf, tailsz, fontsize)
218 static void do_loadtable(int fd, unsigned char *inbuf, int tailsz, int fontsize, int psf2)
220 #if !ENABLE_FEATURE_LOADFONT_PSF2
221 /* gcc 4.3.1 code size: */
222 # define psf2 0 /* +0 bytes */
223 // const int psf2 = 0; /* +8 bytes */
224 // enum { psf2 = 0 }; /* +13 bytes */
226 struct unimapinit advice;
227 struct unimapdesc ud;
233 maxct = tailsz; /* more than enough */
234 up = xmalloc(maxct * sizeof(*up));
236 for (glyph = 0; glyph < fontsize; glyph++) {
238 if (!psf2) { /* PSF1 */
239 unicode = (((uint16_t) inbuf[1]) << 8) + inbuf[0];
242 if (unicode == PSF1_SEPARATOR)
245 #if ENABLE_FEATURE_LOADFONT_PSF2
248 if (unicode == PSF2_SEPARATOR) {
250 } else if (unicode == PSF2_STARTSEQ) {
251 bb_error_msg_and_die("unicode sequences not implemented");
252 } else if (unicode >= 0xC0) {
254 unicode &= 0x01, maxct = 5;
255 else if (unicode >= 0xF8)
256 unicode &= 0x03, maxct = 4;
257 else if (unicode >= 0xF0)
258 unicode &= 0x07, maxct = 3;
259 else if (unicode >= 0xE0)
260 unicode &= 0x0F, maxct = 2;
262 unicode &= 0x1F, maxct = 1;
264 if (tailsz <= 0 || *inbuf < 0x80 || *inbuf > 0xBF)
265 bb_error_msg_and_die("illegal UTF-8 character");
267 unicode = (unicode << 6) + (*inbuf++ & 0x3F);
268 } while (--maxct > 0);
269 } else if (unicode >= 0x80) {
270 bb_error_msg_and_die("illegal UTF-8 character");
276 up[ct].unicode = unicode;
277 up[ct].fontpos = glyph;
282 /* Note: after PIO_UNIMAPCLR and before PIO_UNIMAP
283 * this printf did not work on many kernels */
285 advice.advised_hashsize = 0;
286 advice.advised_hashstep = 0;
287 advice.advised_hashlevel = 0;
288 xioctl(fd, PIO_UNIMAPCLR, &advice);
291 xioctl(fd, PIO_UNIMAP, &ud);
295 static void do_load(int fd, unsigned char *buffer, size_t len)
302 unsigned char *font = buffer;
303 unsigned char *table;
305 if (len >= sizeof(struct psf1_header) && PSF1_MAGIC_OK(psf1h(buffer))) {
306 if (psf1h(buffer)->mode > PSF1_MAXMODE)
307 bb_error_msg_and_die("unsupported psf file mode");
308 if (psf1h(buffer)->mode & PSF1_MODE512)
310 if (psf1h(buffer)->mode & PSF1_MODEHASTAB)
312 height = charsize = psf1h(buffer)->charsize;
313 font += sizeof(struct psf1_header);
315 #if ENABLE_FEATURE_LOADFONT_PSF2
316 if (len >= sizeof(struct psf2_header) && PSF2_MAGIC_OK(psf2h(buffer))) {
317 if (psf2h(buffer)->version > PSF2_MAXVERSION)
318 bb_error_msg_and_die("unsupported psf file version");
319 fontsize = psf2h(buffer)->length;
320 if (psf2h(buffer)->flags & PSF2_HAS_UNICODE_TABLE)
322 charsize = psf2h(buffer)->charsize;
323 height = psf2h(buffer)->height;
324 width = psf2h(buffer)->width;
325 font += psf2h(buffer)->headersize;
328 #if ENABLE_FEATURE_LOADFONT_RAW
329 if (len == 9780) { /* file with three code pages? */
330 charsize = height = 16;
332 } else if ((len & 0377) == 0) { /* bare font */
333 charsize = height = len / 256;
337 bb_error_msg_and_die("input file: bad length or unsupported font type");
340 #if !defined(PIO_FONTX) || defined(__sparc__)
342 bb_error_msg_and_die("only fontsize 256 supported");
345 table = font + fontsize * charsize;
348 if (table > buffer || (!has_table && table != buffer))
349 bb_error_msg_and_die("input file: bad length");
351 do_loadfont(fd, font, height, width, charsize, fontsize);
354 do_loadtable(fd, table, buffer - table, fontsize, has_table - 1);
359 int loadfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
360 int loadfont_main(int argc UNUSED_PARAM, char **argv)
363 unsigned char *buffer;
365 // no arguments allowed!
366 opt_complementary = "=0";
370 * We used to look at the length of the input file
371 * with stat(); now that we accept compressed files,
372 * just read the entire file.
373 * Len was 32k, but latarcyrheb-sun32.psfu is 34377 bytes
374 * (it has largish Unicode map).
377 buffer = xmalloc_read(STDIN_FILENO, &len);
378 // xmalloc_open_zipped_read_close(filename, &len);
380 bb_perror_msg_and_die("error reading input font");
381 do_load(get_console_fd_or_die(), buffer, len);
392 setfont [-O font+umap.orig] [-o font.orig] [-om cmap.orig]
393 [-ou umap.orig] [-N] [font.new ...] [-m cmap] [-u umap] [-C console]
396 -h NN Override font height
398 Save previous font in file
400 Save previous font and Unicode map in file
402 Store console map in file
404 Save previous Unicode map in file
406 Load console map or Unicode console map from file
408 Load Unicode table describing the font from file
413 0x80 U+0410 # CYRILLIC CAPITAL LETTER A
414 0x81 U+0411 # CYRILLIC CAPITAL LETTER BE
415 0x82 U+0412 # CYRILLIC CAPITAL LETTER VE
417 Set the font for the indicated console
422 #if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
423 static int ctoi(char *s)
425 if (s[0] == '\'' && s[1] != '\0' && s[2] == '\'' && s[3] == '\0')
428 if (s[0] == 'U' && s[1] == '+') {
434 return xstrtoul(s, 0);
438 int setfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
439 int setfont_main(int argc UNUSED_PARAM, char **argv)
444 unsigned char *buffer;
446 const char *tty_name = CURRENT_TTY;
448 opt_complementary = "=1";
449 opts = getopt32(argv, "m:C:", &mapfilename, &tty_name);
452 fd = xopen_nonblocking(tty_name);
454 if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not ""
455 if (*argv[0] != '/') {
456 // goto default fonts location. don't die if doesn't exist
457 chdir(CONFIG_DEFAULT_SETFONT_DIR "/consolefonts");
462 buffer = xmalloc_open_zipped_read_close(*argv, &len);
464 bb_simple_perror_msg_and_die(*argv);
465 do_load(fd, buffer, len);
467 // load the screen map, if any
468 if (opts & 1) { // -m
469 unsigned mode = PIO_SCRNMAP;
472 if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not ""
473 if (mapfilename[0] != '/') {
474 // goto default keymaps location
475 chdir(CONFIG_DEFAULT_SETFONT_DIR "/consoletrans");
479 map = xmalloc_open_zipped_read_close(mapfilename, &len);
481 bb_simple_perror_msg_and_die(mapfilename);
482 // file size is 256 or 512 bytes? -> assume binary map
483 if (len == E_TABSZ || len == 2*E_TABSZ) {
484 if (len == 2*E_TABSZ)
485 mode = PIO_UNISCRNMAP;
487 #if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
488 // assume textual Unicode console maps:
489 // 0x00 U+0000 # NULL (NUL)
490 // 0x01 U+0001 # START OF HEADING (SOH)
491 // 0x02 U+0002 # START OF TEXT (STX)
492 // 0x03 U+0003 # END OF TEXT (ETX)
498 if (ENABLE_FEATURE_CLEAN_UP)
500 map = xmalloc(E_TABSZ * sizeof(unsigned short));
502 #define unicodes ((unsigned short *)map)
504 for (i = 0; i < E_TABSZ; i++)
505 unicodes[i] = 0xf000 + i;
507 parser = config_open(mapfilename);
508 while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL | PARSE_MIN_DIE)) {
509 // parse code/value pair
510 int a = ctoi(token[0]);
511 int b = ctoi(token[1]);
512 if (a < 0 || a >= E_TABSZ
513 || b < 0 || b > 65535
515 bb_error_msg_and_die("map format");
519 // unicode character is met?
521 mode = PIO_UNISCRNMAP;
523 if (ENABLE_FEATURE_CLEAN_UP)
524 config_close(parser);
526 if (mode != PIO_UNISCRNMAP) {
527 #define asciis ((unsigned char *)map)
528 for (i = 0; i < E_TABSZ; i++)
529 asciis[i] = unicodes[i];
534 #endif // ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
537 xioctl(fd, mode, map);
539 if (ENABLE_FEATURE_CLEAN_UP)