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 getopt32(argv, "^" "" "\0" "=0");
354 * We used to look at the length of the input file
355 * with stat(); now that we accept compressed files,
356 * just read the entire file.
357 * Len was 32k, but latarcyrheb-sun32.psfu is 34377 bytes
358 * (it has largish Unicode map).
361 buffer = xmalloc_read(STDIN_FILENO, &len);
362 // xmalloc_open_zipped_read_close(filename, &len);
364 bb_perror_msg_and_die("error reading input font");
365 do_load(get_console_fd_or_die(), buffer, len);
374 setfont [-O font+umap.orig] [-o font.orig] [-om cmap.orig]
375 [-ou umap.orig] [-N] [font.new ...] [-m cmap] [-u umap] [-C console]
378 -h NN Override font height
380 Save previous font in file
382 Save previous font and Unicode map in file
384 Store console map in file
386 Save previous Unicode map in file
388 Load console map or Unicode console map from file
390 Load Unicode table describing the font from file
395 0x80 U+0410 # CYRILLIC CAPITAL LETTER A
396 0x81 U+0411 # CYRILLIC CAPITAL LETTER BE
397 0x82 U+0412 # CYRILLIC CAPITAL LETTER VE
399 Set the font for the indicated console
403 //usage:#define setfont_trivial_usage
404 //usage: "FONT [-m MAPFILE] [-C TTY]"
405 //usage:#define setfont_full_usage "\n\n"
406 //usage: "Load a console font\n"
407 //usage: "\n -m MAPFILE Load console screen map"
408 //usage: "\n -C TTY Affect TTY instead of /dev/tty"
410 //usage:#define setfont_example_usage
411 //usage: "$ setfont -m koi8-r /etc/i18n/fontname\n"
413 # if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
414 static int ctoi(char *s)
416 if (s[0] == '\'' && s[1] != '\0' && s[2] == '\'' && s[3] == '\0')
419 if (s[0] == 'U' && s[1] == '+') {
425 return xstrtoul(s, 0);
429 int setfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
430 int setfont_main(int argc UNUSED_PARAM, char **argv)
435 unsigned char *buffer;
437 const char *tty_name = CURRENT_TTY;
439 opts = getopt32(argv, "^" "m:C:" "\0" "=1", &mapfilename, &tty_name);
442 fd = xopen_nonblocking(tty_name);
444 if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not ""
445 if (*argv[0] != '/') {
446 // goto default fonts location. don't die if doesn't exist
447 chdir(CONFIG_DEFAULT_SETFONT_DIR "/consolefonts");
452 buffer = xmalloc_open_zipped_read_close(*argv, &len);
454 bb_simple_perror_msg_and_die(*argv);
455 do_load(fd, buffer, len);
457 // load the screen map, if any
458 if (opts & 1) { // -m
459 unsigned mode = PIO_SCRNMAP;
462 if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not ""
463 if (mapfilename[0] != '/') {
464 // goto default keymaps location
465 chdir(CONFIG_DEFAULT_SETFONT_DIR "/consoletrans");
469 map = xmalloc_open_zipped_read_close(mapfilename, &len);
471 bb_simple_perror_msg_and_die(mapfilename);
472 // file size is 256 or 512 bytes? -> assume binary map
473 if (len == E_TABSZ || len == 2*E_TABSZ) {
474 if (len == 2*E_TABSZ)
475 mode = PIO_UNISCRNMAP;
477 # if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
478 // assume textual Unicode console maps:
479 // 0x00 U+0000 # NULL (NUL)
480 // 0x01 U+0001 # START OF HEADING (SOH)
481 // 0x02 U+0002 # START OF TEXT (STX)
482 // 0x03 U+0003 # END OF TEXT (ETX)
488 if (ENABLE_FEATURE_CLEAN_UP)
490 map = xmalloc(E_TABSZ * sizeof(unsigned short));
492 #define unicodes ((unsigned short *)map)
494 for (i = 0; i < E_TABSZ; i++)
495 unicodes[i] = 0xf000 + i;
497 parser = config_open(mapfilename);
498 while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL | PARSE_MIN_DIE)) {
499 // parse code/value pair
500 int a = ctoi(token[0]);
501 int b = ctoi(token[1]);
502 if (a < 0 || a >= E_TABSZ
503 || b < 0 || b > 65535
505 bb_error_msg_and_die("map format");
509 // unicode character is met?
511 mode = PIO_UNISCRNMAP;
513 if (ENABLE_FEATURE_CLEAN_UP)
514 config_close(parser);
516 if (mode != PIO_UNISCRNMAP) {
517 #define asciis ((unsigned char *)map)
518 for (i = 0; i < E_TABSZ; i++)
519 asciis[i] = unicodes[i];
524 # endif // ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
527 xioctl(fd, mode, map);
529 if (ENABLE_FEATURE_CLEAN_UP)