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 (5.4 kb)"
15 //config: select PLATFORM_LINUX
17 //config: This program loads a console font from standard input.
19 //config:config SETFONT
20 //config: bool "setfont (26 kb)"
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_NOEXEC(loadfont, loadfont, BB_DIR_USR_SBIN, BB_SUID_DROP, loadfont))
55 //applet:IF_SETFONT(APPLET_NOEXEC(setfont, setfont, BB_DIR_USR_SBIN, BB_SUID_DROP, setfont))
57 //kbuild:lib-$(CONFIG_LOADFONT) += loadfont.o
58 //kbuild:lib-$(CONFIG_SETFONT) += loadfont.o
64 # define KDFONTOP 0x4B72
65 struct console_font_op {
66 unsigned op; /* KD_FONT_OP_* */
67 unsigned flags; /* KD_FONT_FLAG_* */
68 unsigned width, height;
70 unsigned char *data; /* font data with height fixed to 32 */
72 # define KD_FONT_OP_SET 0 /* Set font */
73 # define KD_FONT_OP_GET 1 /* Get font */
74 # define KD_FONT_OP_SET_DEFAULT 2 /* Set font to default, data points to name / NULL */
75 # define KD_FONT_OP_COPY 3 /* Copy from another console */
76 # define KD_FONT_FLAG_OLD 0x80000000 /* Invoked via old interface */
77 # define KD_FONT_FLAG_DONT_RECALC 1 /* Don't call adjust_height() */
78 /* (Used internally for PIO_FONT support) */
86 PSF1_MODEHASTAB = 0x02,
87 PSF1_MODEHASSEQ = 0x04,
89 PSF1_STARTSEQ = 0xfffe,
90 PSF1_SEPARATOR = 0xffff,
94 unsigned char magic[2]; /* Magic number */
95 unsigned char mode; /* PSF font mode */
96 unsigned char charsize; /* Character size */
99 #define psf1h(x) ((struct psf1_header*)(x))
101 #define PSF1_MAGIC_OK(x) ( \
102 (x)->magic[0] == PSF1_MAGIC0 \
103 && (x)->magic[1] == PSF1_MAGIC1 \
106 #if ENABLE_FEATURE_LOADFONT_PSF2
112 PSF2_HAS_UNICODE_TABLE = 0x01,
114 PSF2_STARTSEQ = 0xfe,
115 PSF2_SEPARATOR = 0xff
119 unsigned char magic[4];
120 unsigned int version;
121 unsigned int headersize; /* offset of bitmaps in file */
123 unsigned int length; /* number of glyphs */
124 unsigned int charsize; /* number of bytes for each character */
125 unsigned int height; /* max dimensions of glyphs */
126 unsigned int width; /* charsize = height * ((width + 7) / 8) */
129 #define psf2h(x) ((struct psf2_header*)(x))
131 #define PSF2_MAGIC_OK(x) ( \
132 (x)->magic[0] == PSF2_MAGIC0 \
133 && (x)->magic[1] == PSF2_MAGIC1 \
134 && (x)->magic[2] == PSF2_MAGIC2 \
135 && (x)->magic[3] == PSF2_MAGIC3 \
137 #endif /* ENABLE_FEATURE_LOADFONT_PSF2 */
140 static void do_loadfont(int fd, unsigned char *inbuf, int height, int width, int charsize, int fontsize)
143 int charwidth = 32 * ((width+7)/8);
146 if (height < 1 || height > 32 || width < 1 || width > 32)
147 bb_error_msg_and_die("bad character size %dx%d", height, width);
149 buf = xzalloc(charwidth * ((fontsize < 128) ? 128 : fontsize));
150 for (i = 0; i < fontsize; i++)
151 memcpy(buf + (i*charwidth), inbuf + (i*charsize), charsize);
154 struct console_font_op cfo;
155 cfo.op = KD_FONT_OP_SET;
159 cfo.charcount = fontsize;
161 xioctl(fd, KDFONTOP, &cfo);
168 * Format of the Unicode information:
170 * For each font position <uc>*<seq>*<term>
171 * where <uc> is a 2-byte little endian Unicode value (PSF1)
172 * or an UTF-8 coded value (PSF2),
173 * <seq> = <ss><uc><uc>*, <ss> = psf1 ? 0xFFFE : 0xFE,
174 * <term> = psf1 ? 0xFFFF : 0xFF.
175 * and * denotes zero or more occurrences of the preceding item.
178 * The leading <uc>* part gives Unicode symbols that are all
179 * represented by this font position. The following sequences
180 * are sequences of Unicode symbols - probably a symbol
181 * together with combining accents - also represented by
182 * this font position.
185 * At the font position for a capital A-ring glyph, we
187 * 00C5,212B,FFFE,0041,030A,FFFF
188 * Some font positions may be described by sequences only,
189 * namely when there is no precomposed Unicode value for the glyph.
191 #if !ENABLE_FEATURE_LOADFONT_PSF2
192 #define do_loadtable(fd, inbuf, tailsz, fontsize, psf2) \
193 do_loadtable(fd, inbuf, tailsz, fontsize)
195 static void do_loadtable(int fd, unsigned char *inbuf, int tailsz, int fontsize, int psf2)
197 #if !ENABLE_FEATURE_LOADFONT_PSF2
198 /* gcc 4.3.1 code size: */
199 # define psf2 0 /* +0 bytes */
200 // const int psf2 = 0; /* +8 bytes */
201 // enum { psf2 = 0 }; /* +13 bytes */
203 struct unimapinit advice;
204 struct unimapdesc ud;
210 maxct = tailsz; /* more than enough */
211 up = xmalloc(maxct * sizeof(*up));
213 for (glyph = 0; glyph < fontsize; glyph++) {
215 if (!psf2) { /* PSF1 */
216 unicode = (((uint16_t) inbuf[1]) << 8) + inbuf[0];
219 if (unicode == PSF1_SEPARATOR)
222 #if ENABLE_FEATURE_LOADFONT_PSF2
225 if (unicode == PSF2_SEPARATOR) {
227 } else if (unicode == PSF2_STARTSEQ) {
228 bb_error_msg_and_die("unicode sequences not implemented");
229 } else if (unicode >= 0xC0) {
231 unicode &= 0x01, maxct = 5;
232 else if (unicode >= 0xF8)
233 unicode &= 0x03, maxct = 4;
234 else if (unicode >= 0xF0)
235 unicode &= 0x07, maxct = 3;
236 else if (unicode >= 0xE0)
237 unicode &= 0x0F, maxct = 2;
239 unicode &= 0x1F, maxct = 1;
241 if (tailsz <= 0 || *inbuf < 0x80 || *inbuf > 0xBF)
242 bb_error_msg_and_die("illegal UTF-8 character");
244 unicode = (unicode << 6) + (*inbuf++ & 0x3F);
245 } while (--maxct > 0);
246 } else if (unicode >= 0x80) {
247 bb_error_msg_and_die("illegal UTF-8 character");
253 up[ct].unicode = unicode;
254 up[ct].fontpos = glyph;
259 /* Note: after PIO_UNIMAPCLR and before PIO_UNIMAP
260 * this printf did not work on many kernels */
262 advice.advised_hashsize = 0;
263 advice.advised_hashstep = 0;
264 advice.advised_hashlevel = 0;
265 xioctl(fd, PIO_UNIMAPCLR, &advice);
268 xioctl(fd, PIO_UNIMAP, &ud);
272 static void do_load(int fd, unsigned char *buffer, size_t len)
279 unsigned char *font = buffer;
280 unsigned char *table;
282 if (len >= sizeof(struct psf1_header) && PSF1_MAGIC_OK(psf1h(buffer))) {
283 if (psf1h(buffer)->mode > PSF1_MAXMODE)
284 bb_error_msg_and_die("unsupported psf file mode");
285 if (psf1h(buffer)->mode & PSF1_MODE512)
287 if (psf1h(buffer)->mode & PSF1_MODEHASTAB)
289 height = charsize = psf1h(buffer)->charsize;
290 font += sizeof(struct psf1_header);
292 #if ENABLE_FEATURE_LOADFONT_PSF2
293 if (len >= sizeof(struct psf2_header) && PSF2_MAGIC_OK(psf2h(buffer))) {
294 if (psf2h(buffer)->version > PSF2_MAXVERSION)
295 bb_error_msg_and_die("unsupported psf file version");
296 fontsize = psf2h(buffer)->length;
297 if (psf2h(buffer)->flags & PSF2_HAS_UNICODE_TABLE)
299 charsize = psf2h(buffer)->charsize;
300 height = psf2h(buffer)->height;
301 width = psf2h(buffer)->width;
302 font += psf2h(buffer)->headersize;
305 #if ENABLE_FEATURE_LOADFONT_RAW
306 if (len == 9780) { /* file with three code pages? */
307 charsize = height = 16;
309 } else if ((len & 0377) == 0) { /* bare font */
310 charsize = height = len / 256;
314 bb_error_msg_and_die("input file: bad length or unsupported font type");
317 #if !defined(PIO_FONTX) || defined(__sparc__)
319 bb_error_msg_and_die("only fontsize 256 supported");
322 table = font + fontsize * charsize;
325 if (table > buffer || (!has_table && table != buffer))
326 bb_error_msg_and_die("input file: bad length");
328 do_loadfont(fd, font, height, width, charsize, fontsize);
331 do_loadtable(fd, table, buffer - table, fontsize, has_table - 1);
336 //usage:#define loadfont_trivial_usage
338 //usage:#define loadfont_full_usage "\n\n"
339 //usage: "Load a console font from stdin"
340 /* //usage: "\n -C TTY Affect TTY instead of /dev/tty" */
342 //usage:#define loadfont_example_usage
343 //usage: "$ loadfont < /etc/i18n/fontname\n"
344 int loadfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
345 int loadfont_main(int argc UNUSED_PARAM, char **argv)
348 unsigned char *buffer;
350 // no arguments allowed!
351 opt_complementary = "=0";
355 * We used to look at the length of the input file
356 * with stat(); now that we accept compressed files,
357 * just read the entire file.
358 * Len was 32k, but latarcyrheb-sun32.psfu is 34377 bytes
359 * (it has largish Unicode map).
362 buffer = xmalloc_read(STDIN_FILENO, &len);
363 // xmalloc_open_zipped_read_close(filename, &len);
365 bb_perror_msg_and_die("error reading input font");
366 do_load(get_console_fd_or_die(), buffer, len);
375 setfont [-O font+umap.orig] [-o font.orig] [-om cmap.orig]
376 [-ou umap.orig] [-N] [font.new ...] [-m cmap] [-u umap] [-C console]
379 -h NN Override font height
381 Save previous font in file
383 Save previous font and Unicode map in file
385 Store console map in file
387 Save previous Unicode map in file
389 Load console map or Unicode console map from file
391 Load Unicode table describing the font from file
396 0x80 U+0410 # CYRILLIC CAPITAL LETTER A
397 0x81 U+0411 # CYRILLIC CAPITAL LETTER BE
398 0x82 U+0412 # CYRILLIC CAPITAL LETTER VE
400 Set the font for the indicated console
404 //usage:#define setfont_trivial_usage
405 //usage: "FONT [-m MAPFILE] [-C TTY]"
406 //usage:#define setfont_full_usage "\n\n"
407 //usage: "Load a console font\n"
408 //usage: "\n -m MAPFILE Load console screen map"
409 //usage: "\n -C TTY Affect TTY instead of /dev/tty"
411 //usage:#define setfont_example_usage
412 //usage: "$ setfont -m koi8-r /etc/i18n/fontname\n"
414 # if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
415 static int ctoi(char *s)
417 if (s[0] == '\'' && s[1] != '\0' && s[2] == '\'' && s[3] == '\0')
420 if (s[0] == 'U' && s[1] == '+') {
426 return xstrtoul(s, 0);
430 int setfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
431 int setfont_main(int argc UNUSED_PARAM, char **argv)
436 unsigned char *buffer;
438 const char *tty_name = CURRENT_TTY;
440 opt_complementary = "=1";
441 opts = getopt32(argv, "m:C:", &mapfilename, &tty_name);
444 fd = xopen_nonblocking(tty_name);
446 if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not ""
447 if (*argv[0] != '/') {
448 // goto default fonts location. don't die if doesn't exist
449 chdir(CONFIG_DEFAULT_SETFONT_DIR "/consolefonts");
454 buffer = xmalloc_open_zipped_read_close(*argv, &len);
456 bb_simple_perror_msg_and_die(*argv);
457 do_load(fd, buffer, len);
459 // load the screen map, if any
460 if (opts & 1) { // -m
461 unsigned mode = PIO_SCRNMAP;
464 if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not ""
465 if (mapfilename[0] != '/') {
466 // goto default keymaps location
467 chdir(CONFIG_DEFAULT_SETFONT_DIR "/consoletrans");
471 map = xmalloc_open_zipped_read_close(mapfilename, &len);
473 bb_simple_perror_msg_and_die(mapfilename);
474 // file size is 256 or 512 bytes? -> assume binary map
475 if (len == E_TABSZ || len == 2*E_TABSZ) {
476 if (len == 2*E_TABSZ)
477 mode = PIO_UNISCRNMAP;
479 # if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
480 // assume textual Unicode console maps:
481 // 0x00 U+0000 # NULL (NUL)
482 // 0x01 U+0001 # START OF HEADING (SOH)
483 // 0x02 U+0002 # START OF TEXT (STX)
484 // 0x03 U+0003 # END OF TEXT (ETX)
490 if (ENABLE_FEATURE_CLEAN_UP)
492 map = xmalloc(E_TABSZ * sizeof(unsigned short));
494 #define unicodes ((unsigned short *)map)
496 for (i = 0; i < E_TABSZ; i++)
497 unicodes[i] = 0xf000 + i;
499 parser = config_open(mapfilename);
500 while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL | PARSE_MIN_DIE)) {
501 // parse code/value pair
502 int a = ctoi(token[0]);
503 int b = ctoi(token[1]);
504 if (a < 0 || a >= E_TABSZ
505 || b < 0 || b > 65535
507 bb_error_msg_and_die("map format");
511 // unicode character is met?
513 mode = PIO_UNISCRNMAP;
515 if (ENABLE_FEATURE_CLEAN_UP)
516 config_close(parser);
518 if (mode != PIO_UNISCRNMAP) {
519 #define asciis ((unsigned char *)map)
520 for (i = 0; i < E_TABSZ; i++)
521 asciis[i] = unicodes[i];
526 # endif // ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
529 xioctl(fd, mode, map);
531 if (ENABLE_FEATURE_CLEAN_UP)