config: deindent all help texts
[oweals/busybox.git] / console-tools / loadfont.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * loadfont.c - Eugene Crosser & Andries Brouwer
4  *
5  * Version 0.96bb
6  *
7  * Loads the console font, and possibly the corresponding screen map(s).
8  * (Adapted for busybox by Matej Vela.)
9  *
10  * Licensed under GPLv2, see file LICENSE in this source tree.
11  */
12 //config:config LOADFONT
13 //config:       bool "loadfont (5.4 kb)"
14 //config:       default y
15 //config:       select PLATFORM_LINUX
16 //config:       help
17 //config:       This program loads a console font from standard input.
18 //config:
19 //config:config SETFONT
20 //config:       bool "setfont (26 kb)"
21 //config:       default y
22 //config:       select PLATFORM_LINUX
23 //config:       help
24 //config:       Allows to load console screen map. Useful for i18n.
25 //config:
26 //config:config FEATURE_SETFONT_TEXTUAL_MAP
27 //config:       bool "Support reading textual screen maps"
28 //config:       default y
29 //config:       depends on SETFONT
30 //config:       help
31 //config:       Support reading textual screen maps.
32 //config:
33 //config:config DEFAULT_SETFONT_DIR
34 //config:       string "Default directory for console-tools files"
35 //config:       default ""
36 //config:       depends on SETFONT
37 //config:       help
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).
40 //config:
41 //config:comment "Common options for loadfont and setfont"
42 //config:       depends on LOADFONT || SETFONT
43 //config:
44 //config:config FEATURE_LOADFONT_PSF2
45 //config:       bool "Support PSF2 console fonts"
46 //config:       default y
47 //config:       depends on LOADFONT || SETFONT
48 //config:
49 //config:config FEATURE_LOADFONT_RAW
50 //config:       bool "Support old (raw) console fonts"
51 //config:       default y
52 //config:       depends on LOADFONT || SETFONT
53
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))
56
57 //kbuild:lib-$(CONFIG_LOADFONT) += loadfont.o
58 //kbuild:lib-$(CONFIG_SETFONT) += loadfont.o
59
60 //usage:#define loadfont_trivial_usage
61 //usage:       "< font"
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" */
65 //usage:
66 //usage:#define loadfont_example_usage
67 //usage:       "$ loadfont < /etc/i18n/fontname\n"
68 //usage:
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"
75 //usage:
76 //usage:#define setfont_example_usage
77 //usage:       "$ setfont -m koi8-r /etc/i18n/fontname\n"
78
79 #include "libbb.h"
80 #include <sys/kd.h>
81
82 #ifndef KDFONTOP
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;
88         unsigned charcount;
89         unsigned char *data;    /* font data with height fixed to 32 */
90 };
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) */
98 #endif /* KDFONTOP */
99
100
101 enum {
102         PSF1_MAGIC0 = 0x36,
103         PSF1_MAGIC1 = 0x04,
104         PSF1_MODE512 = 0x01,
105         PSF1_MODEHASTAB = 0x02,
106         PSF1_MODEHASSEQ = 0x04,
107         PSF1_MAXMODE = 0x05,
108         PSF1_STARTSEQ = 0xfffe,
109         PSF1_SEPARATOR = 0xffff,
110 };
111
112 struct psf1_header {
113         unsigned char magic[2];         /* Magic number */
114         unsigned char mode;             /* PSF font mode */
115         unsigned char charsize;         /* Character size */
116 };
117
118 #define psf1h(x) ((struct psf1_header*)(x))
119
120 #define PSF1_MAGIC_OK(x) ( \
121      (x)->magic[0] == PSF1_MAGIC0 \
122   && (x)->magic[1] == PSF1_MAGIC1 \
123 )
124
125 #if ENABLE_FEATURE_LOADFONT_PSF2
126 enum {
127         PSF2_MAGIC0 = 0x72,
128         PSF2_MAGIC1 = 0xb5,
129         PSF2_MAGIC2 = 0x4a,
130         PSF2_MAGIC3 = 0x86,
131         PSF2_HAS_UNICODE_TABLE = 0x01,
132         PSF2_MAXVERSION = 0,
133         PSF2_STARTSEQ = 0xfe,
134         PSF2_SEPARATOR = 0xff
135 };
136
137 struct psf2_header {
138         unsigned char magic[4];
139         unsigned int version;
140         unsigned int headersize;    /* offset of bitmaps in file */
141         unsigned int flags;
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) */
146 };
147
148 #define psf2h(x) ((struct psf2_header*)(x))
149
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 \
155 )
156 #endif /* ENABLE_FEATURE_LOADFONT_PSF2 */
157
158
159 static void do_loadfont(int fd, unsigned char *inbuf, int height, int width, int charsize, int fontsize)
160 {
161         unsigned char *buf;
162         int charwidth = 32 * ((width+7)/8);
163         int i;
164
165         if (height < 1 || height > 32 || width < 1 || width > 32)
166                 bb_error_msg_and_die("bad character size %dx%d", height, width);
167
168         buf = xzalloc(charwidth * ((fontsize < 128) ? 128 : fontsize));
169         for (i = 0; i < fontsize; i++)
170                 memcpy(buf + (i*charwidth), inbuf + (i*charsize), charsize);
171
172         { /* KDFONTOP */
173                 struct console_font_op cfo;
174                 cfo.op = KD_FONT_OP_SET;
175                 cfo.flags = 0;
176                 cfo.width = width;
177                 cfo.height = height;
178                 cfo.charcount = fontsize;
179                 cfo.data = buf;
180                 xioctl(fd, KDFONTOP, &cfo);
181         }
182
183         free(buf);
184 }
185
186 /*
187  * Format of the Unicode information:
188  *
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.
195  *
196  * Semantics:
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.
202  *
203  * Example:
204  * At the font position for a capital A-ring glyph, we
205  * may have:
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.
209  */
210 #if !ENABLE_FEATURE_LOADFONT_PSF2
211 #define do_loadtable(fd, inbuf, tailsz, fontsize, psf2) \
212         do_loadtable(fd, inbuf, tailsz, fontsize)
213 #endif
214 static void do_loadtable(int fd, unsigned char *inbuf, int tailsz, int fontsize, int psf2)
215 {
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 */
221 #endif
222         struct unimapinit advice;
223         struct unimapdesc ud;
224         struct unipair *up;
225         int ct = 0, maxct;
226         int glyph;
227         uint16_t unicode;
228
229         maxct = tailsz; /* more than enough */
230         up = xmalloc(maxct * sizeof(*up));
231
232         for (glyph = 0; glyph < fontsize; glyph++) {
233                 while (tailsz > 0) {
234                         if (!psf2) { /* PSF1 */
235                                 unicode = (((uint16_t) inbuf[1]) << 8) + inbuf[0];
236                                 tailsz -= 2;
237                                 inbuf += 2;
238                                 if (unicode == PSF1_SEPARATOR)
239                                         break;
240                         } else { /* PSF2 */
241 #if ENABLE_FEATURE_LOADFONT_PSF2
242                                 --tailsz;
243                                 unicode = *inbuf++;
244                                 if (unicode == PSF2_SEPARATOR) {
245                                         break;
246                                 } else if (unicode == PSF2_STARTSEQ) {
247                                         bb_error_msg_and_die("unicode sequences not implemented");
248                                 } else if (unicode >= 0xC0) {
249                                         if (unicode >= 0xFC)
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;
257                                         else
258                                                 unicode &= 0x1F, maxct = 1;
259                                         do {
260                                                 if (tailsz <= 0 || *inbuf < 0x80 || *inbuf > 0xBF)
261                                                         bb_error_msg_and_die("illegal UTF-8 character");
262                                                 --tailsz;
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");
267                                 }
268 #else
269                                 return;
270 #endif
271                         }
272                         up[ct].unicode = unicode;
273                         up[ct].fontpos = glyph;
274                         ct++;
275                 }
276         }
277
278         /* Note: after PIO_UNIMAPCLR and before PIO_UNIMAP
279          * this printf did not work on many kernels */
280
281         advice.advised_hashsize = 0;
282         advice.advised_hashstep = 0;
283         advice.advised_hashlevel = 0;
284         xioctl(fd, PIO_UNIMAPCLR, &advice);
285         ud.entry_ct = ct;
286         ud.entries = up;
287         xioctl(fd, PIO_UNIMAP, &ud);
288 #undef psf2
289 }
290
291 static void do_load(int fd, unsigned char *buffer, size_t len)
292 {
293         int height;
294         int width = 8;
295         int charsize;
296         int fontsize = 256;
297         int has_table = 0;
298         unsigned char *font = buffer;
299         unsigned char *table;
300
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)
305                         fontsize = 512;
306                 if (psf1h(buffer)->mode & PSF1_MODEHASTAB)
307                         has_table = 1;
308                 height = charsize = psf1h(buffer)->charsize;
309                 font += sizeof(struct psf1_header);
310         } else
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)
317                         has_table = 2;
318                 charsize = psf2h(buffer)->charsize;
319                 height = psf2h(buffer)->height;
320                 width = psf2h(buffer)->width;
321                 font += psf2h(buffer)->headersize;
322         } else
323 #endif
324 #if ENABLE_FEATURE_LOADFONT_RAW
325         if (len == 9780) {  /* file with three code pages? */
326                 charsize = height = 16;
327                 font += 40;
328         } else if ((len & 0377) == 0) {  /* bare font */
329                 charsize = height = len / 256;
330         } else
331 #endif
332         {
333                 bb_error_msg_and_die("input file: bad length or unsupported font type");
334         }
335
336 #if !defined(PIO_FONTX) || defined(__sparc__)
337         if (fontsize != 256)
338                 bb_error_msg_and_die("only fontsize 256 supported");
339 #endif
340
341         table = font + fontsize * charsize;
342         buffer += len;
343
344         if (table > buffer || (!has_table && table != buffer))
345                 bb_error_msg_and_die("input file: bad length");
346
347         do_loadfont(fd, font, height, width, charsize, fontsize);
348
349         if (has_table)
350                 do_loadtable(fd, table, buffer - table, fontsize, has_table - 1);
351 }
352
353
354 #if ENABLE_LOADFONT
355 int loadfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
356 int loadfont_main(int argc UNUSED_PARAM, char **argv)
357 {
358         size_t len;
359         unsigned char *buffer;
360
361         // no arguments allowed!
362         opt_complementary = "=0";
363         getopt32(argv, "");
364
365         /*
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).
371          */
372         len = 128*1024;
373         buffer = xmalloc_read(STDIN_FILENO, &len);
374         // xmalloc_open_zipped_read_close(filename, &len);
375         if (!buffer)
376                 bb_perror_msg_and_die("error reading input font");
377         do_load(get_console_fd_or_die(), buffer, len);
378
379         return EXIT_SUCCESS;
380 }
381 #endif
382
383 #if ENABLE_SETFONT
384
385 /*
386 kbd-1.12:
387
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]
390 [-hNN] [-v] [-V]
391
392 -h NN  Override font height
393 -o file
394        Save previous font in file
395 -O file
396        Save previous font and Unicode map in file
397 -om file
398        Store console map in file
399 -ou file
400        Save previous Unicode map in file
401 -m file
402        Load console map or Unicode console map from file
403 -u file
404        Load Unicode table describing the font from file
405        Example:
406        # cp866
407        0x00-0x7f       idem
408        #
409        0x80    U+0410  # CYRILLIC CAPITAL LETTER A
410        0x81    U+0411  # CYRILLIC CAPITAL LETTER BE
411        0x82    U+0412  # CYRILLIC CAPITAL LETTER VE
412 -C console
413        Set the font for the indicated console
414 -v     Verbose
415 -V     Version
416 */
417
418 #if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
419 static int ctoi(char *s)
420 {
421         if (s[0] == '\'' && s[1] != '\0' && s[2] == '\'' && s[3] == '\0')
422                 return s[1];
423         // U+ means 0x
424         if (s[0] == 'U' && s[1] == '+') {
425                 s[0] = '0';
426                 s[1] = 'x';
427         }
428         if (!isdigit(s[0]))
429                 return -1;
430         return xstrtoul(s, 0);
431 }
432 #endif
433
434 int setfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
435 int setfont_main(int argc UNUSED_PARAM, char **argv)
436 {
437         size_t len;
438         unsigned opts;
439         int fd;
440         unsigned char *buffer;
441         char *mapfilename;
442         const char *tty_name = CURRENT_TTY;
443
444         opt_complementary = "=1";
445         opts = getopt32(argv, "m:C:", &mapfilename, &tty_name);
446         argv += optind;
447
448         fd = xopen_nonblocking(tty_name);
449
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");
454                 }
455         }
456         // load font
457         len = 128*1024;
458         buffer = xmalloc_open_zipped_read_close(*argv, &len);
459         if (!buffer)
460                 bb_simple_perror_msg_and_die(*argv);
461         do_load(fd, buffer, len);
462
463         // load the screen map, if any
464         if (opts & 1) { // -m
465                 unsigned mode = PIO_SCRNMAP;
466                 void *map;
467
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");
472                         }
473                 }
474                 // fetch keymap
475                 map = xmalloc_open_zipped_read_close(mapfilename, &len);
476                 if (!map)
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;
482                 }
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)
489                 else {
490                         int i;
491                         char *token[2];
492                         parser_t *parser;
493
494                         if (ENABLE_FEATURE_CLEAN_UP)
495                                 free(map);
496                         map = xmalloc(E_TABSZ * sizeof(unsigned short));
497
498 #define unicodes ((unsigned short *)map)
499                         // fill vanilla map
500                         for (i = 0; i < E_TABSZ; i++)
501                                 unicodes[i] = 0xf000 + i;
502
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
510                                 ) {
511                                         bb_error_msg_and_die("map format");
512                                 }
513                                 // patch map
514                                 unicodes[a] = b;
515                                 // unicode character is met?
516                                 if (b > 255)
517                                         mode = PIO_UNISCRNMAP;
518                         }
519                         if (ENABLE_FEATURE_CLEAN_UP)
520                                 config_close(parser);
521
522                         if (mode != PIO_UNISCRNMAP) {
523 #define asciis ((unsigned char *)map)
524                                 for (i = 0; i < E_TABSZ; i++)
525                                         asciis[i] = unicodes[i];
526 #undef asciis
527                         }
528 #undef unicodes
529                 }
530 #endif // ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
531
532                 // do set screen map
533                 xioctl(fd, mode, map);
534
535                 if (ENABLE_FEATURE_CLEAN_UP)
536                         free(map);
537         }
538
539         return EXIT_SUCCESS;
540 }
541 #endif