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 tarball for details.
16 # define KDFONTOP 0x4B72
17 struct console_font_op {
18 unsigned op; /* KD_FONT_OP_* */
19 unsigned flags; /* KD_FONT_FLAG_* */
20 unsigned width, height;
22 unsigned char *data; /* font data with height fixed to 32 */
24 # define KD_FONT_OP_SET 0 /* Set font */
25 # define KD_FONT_OP_GET 1 /* Get font */
26 # define KD_FONT_OP_SET_DEFAULT 2 /* Set font to default, data points to name / NULL */
27 # define KD_FONT_OP_COPY 3 /* Copy from another console */
28 # define KD_FONT_FLAG_OLD 0x80000000 /* Invoked via old interface */
29 # define KD_FONT_FLAG_DONT_RECALC 1 /* Don't call adjust_height() */
30 /* (Used internally for PIO_FONT support) */
38 PSF1_MODEHASTAB = 0x02,
39 PSF1_MODEHASSEQ = 0x04,
41 PSF1_STARTSEQ = 0xfffe,
42 PSF1_SEPARATOR = 0xffff,
46 unsigned char magic[2]; /* Magic number */
47 unsigned char mode; /* PSF font mode */
48 unsigned char charsize; /* Character size */
51 #define psf1h(x) ((struct psf1_header*)(x))
53 #define PSF1_MAGIC_OK(x) ( \
54 (x)->magic[0] == PSF1_MAGIC0 \
55 && (x)->magic[1] == PSF1_MAGIC1 \
58 #if ENABLE_FEATURE_LOADFONT_PSF2
64 PSF2_HAS_UNICODE_TABLE = 0x01,
71 unsigned char magic[4];
73 unsigned int headersize; /* offset of bitmaps in file */
75 unsigned int length; /* number of glyphs */
76 unsigned int charsize; /* number of bytes for each character */
77 unsigned int height; /* max dimensions of glyphs */
78 unsigned int width; /* charsize = height * ((width + 7) / 8) */
81 #define psf2h(x) ((struct psf2_header*)(x))
83 #define PSF2_MAGIC_OK(x) ( \
84 (x)->magic[0] == PSF2_MAGIC0 \
85 && (x)->magic[1] == PSF2_MAGIC1 \
86 && (x)->magic[2] == PSF2_MAGIC2 \
87 && (x)->magic[3] == PSF2_MAGIC3 \
89 #endif /* ENABLE_FEATURE_LOADFONT_PSF2 */
92 static void do_loadfont(int fd, unsigned char *inbuf, int height, int width, int charsize, int fontsize)
95 int charwidth = 32 * ((width+7)/8);
98 if (height < 1 || height > 32 || width < 1 || width > 32)
99 bb_error_msg_and_die("bad character size %dx%d", height, width);
101 buf = xzalloc(charwidth * ((fontsize < 128) ? 128 : fontsize));
102 for (i = 0; i < fontsize; i++)
103 memcpy(buf + (i*charwidth), inbuf + (i*charsize), charsize);
106 struct console_font_op cfo;
107 cfo.op = KD_FONT_OP_SET;
111 cfo.charcount = fontsize;
113 xioctl(fd, KDFONTOP, &cfo);
120 * Format of the Unicode information:
122 * For each font position <uc>*<seq>*<term>
123 * where <uc> is a 2-byte little endian Unicode value (PSF1)
124 * or an UTF-8 coded value (PSF2),
125 * <seq> = <ss><uc><uc>*, <ss> = psf1 ? 0xFFFE : 0xFE,
126 * <term> = psf1 ? 0xFFFF : 0xFF.
127 * and * denotes zero or more occurrences of the preceding item.
130 * The leading <uc>* part gives Unicode symbols that are all
131 * represented by this font position. The following sequences
132 * are sequences of Unicode symbols - probably a symbol
133 * together with combining accents - also represented by
134 * this font position.
137 * At the font position for a capital A-ring glyph, we
139 * 00C5,212B,FFFE,0041,030A,FFFF
140 * Some font positions may be described by sequences only,
141 * namely when there is no precomposed Unicode value for the glyph.
143 #if !ENABLE_FEATURE_LOADFONT_PSF2
144 #define do_loadtable(fd, inbuf, tailsz, fontsize, psf2) \
145 do_loadtable(fd, inbuf, tailsz, fontsize)
147 static void do_loadtable(int fd, unsigned char *inbuf, int tailsz, int fontsize, int psf2)
149 #if !ENABLE_FEATURE_LOADFONT_PSF2
150 /* gcc 4.3.1 code size: */
151 # define psf2 0 /* +0 bytes */
152 // const int psf2 = 0; /* +8 bytes */
153 // enum { psf2 = 0 }; /* +13 bytes */
155 struct unimapinit advice;
156 struct unimapdesc ud;
162 maxct = tailsz; /* more than enough */
163 up = xmalloc(maxct * sizeof(*up));
165 for (glyph = 0; glyph < fontsize; glyph++) {
167 if (!psf2) { /* PSF1 */
168 unicode = (((uint16_t) inbuf[1]) << 8) + inbuf[0];
171 if (unicode == PSF1_SEPARATOR)
174 #if ENABLE_FEATURE_LOADFONT_PSF2
177 if (unicode == PSF2_SEPARATOR) {
179 } else if (unicode == PSF2_STARTSEQ) {
180 bb_error_msg_and_die("unicode sequences not implemented");
181 } else if (unicode >= 0xC0) {
183 unicode &= 0x01, maxct = 5;
184 else if (unicode >= 0xF8)
185 unicode &= 0x03, maxct = 4;
186 else if (unicode >= 0xF0)
187 unicode &= 0x07, maxct = 3;
188 else if (unicode >= 0xE0)
189 unicode &= 0x0F, maxct = 2;
191 unicode &= 0x1F, maxct = 1;
193 if (tailsz <= 0 || *inbuf < 0x80 || *inbuf > 0xBF)
194 bb_error_msg_and_die("illegal UTF-8 character");
196 unicode = (unicode << 6) + (*inbuf++ & 0x3F);
197 } while (--maxct > 0);
198 } else if (unicode >= 0x80) {
199 bb_error_msg_and_die("illegal UTF-8 character");
205 up[ct].unicode = unicode;
206 up[ct].fontpos = glyph;
211 /* Note: after PIO_UNIMAPCLR and before PIO_UNIMAP
212 this printf did not work on many kernels */
214 advice.advised_hashsize = 0;
215 advice.advised_hashstep = 0;
216 advice.advised_hashlevel = 0;
217 xioctl(fd, PIO_UNIMAPCLR, &advice);
220 xioctl(fd, PIO_UNIMAP, &ud);
224 static void do_load(int fd, unsigned char *buffer, size_t len)
231 unsigned char *font = buffer;
232 unsigned char *table;
234 if (len >= sizeof(struct psf1_header) && PSF1_MAGIC_OK(psf1h(buffer))) {
235 if (psf1h(buffer)->mode > PSF1_MAXMODE)
236 bb_error_msg_and_die("unsupported psf file mode");
237 if (psf1h(buffer)->mode & PSF1_MODE512)
239 if (psf1h(buffer)->mode & PSF1_MODEHASTAB)
241 height = charsize = psf1h(buffer)->charsize;
242 font += sizeof(struct psf1_header);
244 #if ENABLE_FEATURE_LOADFONT_PSF2
245 if (len >= sizeof(struct psf2_header) && PSF2_MAGIC_OK(psf2h(buffer))) {
246 if (psf2h(buffer)->version > PSF2_MAXVERSION)
247 bb_error_msg_and_die("unsupported psf file version");
248 fontsize = psf2h(buffer)->length;
249 if (psf2h(buffer)->flags & PSF2_HAS_UNICODE_TABLE)
251 charsize = psf2h(buffer)->charsize;
252 height = psf2h(buffer)->height;
253 width = psf2h(buffer)->width;
254 font += psf2h(buffer)->headersize;
257 #if ENABLE_FEATURE_LOADFONT_RAW
258 if (len == 9780) { /* file with three code pages? */
259 charsize = height = 16;
261 } else if ((len & 0377) == 0) { /* bare font */
262 charsize = height = len / 256;
266 bb_error_msg_and_die("input file: bad length or unsupported font type");
269 #if !defined(PIO_FONTX) || defined(__sparc__)
271 bb_error_msg_and_die("only fontsize 256 supported");
274 table = font + fontsize * charsize;
277 if (table > buffer || (!has_table && table != buffer))
278 bb_error_msg_and_die("input file: bad length");
280 do_loadfont(fd, font, height, width, charsize, fontsize);
283 do_loadtable(fd, table, buffer - table, fontsize, has_table - 1);
288 int loadfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
289 int loadfont_main(int argc UNUSED_PARAM, char **argv)
292 unsigned char *buffer;
294 // no arguments allowed!
295 opt_complementary = "=0";
299 * We used to look at the length of the input file
300 * with stat(); now that we accept compressed files,
301 * just read the entire file.
303 len = 32*1024; // can't be larger
304 buffer = xmalloc_read(STDIN_FILENO, &len);
305 // xmalloc_open_zipped_read_close(filename, &len);
307 bb_perror_msg_and_die("error reading input font");
308 do_load(get_console_fd_or_die(), buffer, len);
319 setfont [-O font+umap.orig] [-o font.orig] [-om cmap.orig]
320 [-ou umap.orig] [-N] [font.new ...] [-m cmap] [-u umap] [-C console]
323 -h NN Override font height
325 Save previous font in file
327 Save previous font and Unicode map in file
329 Store console map in file
331 Save previous Unicode map in file
333 Load console map or Unicode console map from file
335 Load Unicode table describing the font from file
340 0x80 U+0410 # CYRILLIC CAPITAL LETTER A
341 0x81 U+0411 # CYRILLIC CAPITAL LETTER BE
342 0x82 U+0412 # CYRILLIC CAPITAL LETTER VE
344 Set the font for the indicated console
349 #if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
350 static int ctoi(char *s)
352 if (s[0] == '\'' && s[1] != '\0' && s[2] == '\'' && s[3] == '\0')
355 if (s[0] == 'U' && s[1] == '+') {
361 return xstrtoul(s, 0);
365 int setfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
366 int setfont_main(int argc UNUSED_PARAM, char **argv)
371 unsigned char *buffer;
373 const char *tty_name = CURRENT_TTY;
375 opt_complementary = "=1";
376 opts = getopt32(argv, "m:C:", &mapfilename, &tty_name);
379 fd = xopen_nonblocking(tty_name);
381 if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not ""
382 if (*argv[0] != '/') {
383 // goto default fonts location. don't die if doesn't exist
384 chdir(CONFIG_DEFAULT_SETFONT_DIR "/consolefonts");
388 len = 32*1024; // can't be larger
389 buffer = xmalloc_open_zipped_read_close(*argv, &len);
391 bb_simple_perror_msg_and_die(*argv);
392 do_load(fd, buffer, len);
394 // load the screen map, if any
395 if (opts & 1) { // -m
396 unsigned mode = PIO_SCRNMAP;
399 if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not ""
400 if (mapfilename[0] != '/') {
401 // goto default keymaps location
402 chdir(CONFIG_DEFAULT_SETFONT_DIR "/consoletrans");
406 map = xmalloc_open_zipped_read_close(mapfilename, &len);
408 bb_simple_perror_msg_and_die(mapfilename);
409 // file size is 256 or 512 bytes? -> assume binary map
410 if (len == E_TABSZ || len == 2*E_TABSZ) {
411 if (len == 2*E_TABSZ)
412 mode = PIO_UNISCRNMAP;
414 #if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
415 // assume textual Unicode console maps:
416 // 0x00 U+0000 # NULL (NUL)
417 // 0x01 U+0001 # START OF HEADING (SOH)
418 // 0x02 U+0002 # START OF TEXT (STX)
419 // 0x03 U+0003 # END OF TEXT (ETX)
425 if (ENABLE_FEATURE_CLEAN_UP)
427 map = xmalloc(E_TABSZ * sizeof(unsigned short));
429 #define unicodes ((unsigned short *)map)
431 for (i = 0; i < E_TABSZ; i++)
432 unicodes[i] = 0xf000 + i;
434 parser = config_open(mapfilename);
435 while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL | PARSE_MIN_DIE)) {
436 // parse code/value pair
437 int a = ctoi(token[0]);
438 int b = ctoi(token[1]);
439 if (a < 0 || a >= E_TABSZ
440 || b < 0 || b > 65535
442 bb_error_msg_and_die("map format");
446 // unicode character is met?
448 mode = PIO_UNISCRNMAP;
450 if (ENABLE_FEATURE_CLEAN_UP)
451 config_close(parser);
453 if (mode != PIO_UNISCRNMAP) {
454 #define asciis ((unsigned char *)map)
455 for (i = 0; i < E_TABSZ; i++)
456 asciis[i] = unicodes[i];
461 #endif // ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
464 xioctl(fd, mode, map);
466 if (ENABLE_FEATURE_CLEAN_UP)