setfont: support -m and -C, support -m TEXTUAL_MAP (by Vladimir)
[platform/upstream/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 #include "libbb.h"
11 #include <sys/kd.h>
12
13 enum {
14         PSF_MAGIC1 = 0x36,
15         PSF_MAGIC2 = 0x04,
16
17         PSF_MODE512 = 0x01,
18         PSF_MODEHASTAB = 0x02,
19         PSF_MAXMODE = 0x03,
20         PSF_SEPARATOR = 0xffff
21 };
22
23 struct psf_header {
24         unsigned char magic1, magic2;   /* Magic number */
25         unsigned char mode;             /* PSF font mode */
26         unsigned char charsize;         /* Character size */
27 };
28
29 #define PSF_MAGIC_OK(x) ((x)->magic1 == PSF_MAGIC1 && (x)->magic2 == PSF_MAGIC2)
30
31 static void do_loadfont(int fd, unsigned char *inbuf, int unit, int fontsize)
32 {
33         char *buf;
34         int i;
35
36         if (unit < 1 || unit > 32)
37                 bb_error_msg_and_die("bad character size %d", unit);
38
39         buf = xzalloc(16 * 1024);
40         for (i = 0; i < fontsize; i++)
41                 memcpy(buf + (32 * i), inbuf + (unit * i), unit);
42
43 #if defined(PIO_FONTX) && !defined(__sparc__)
44         {
45                 struct consolefontdesc cfd;
46
47                 cfd.charcount = fontsize;
48                 cfd.charheight = unit;
49                 cfd.chardata = buf;
50
51                 if (!ioctl_or_perror(fd, PIO_FONTX, &cfd, "PIO_FONTX ioctl failed (will try PIO_FONT)"))
52                         goto ret;                       /* success */
53         }
54 #endif
55         xioctl(fd, PIO_FONT, buf);
56  ret:
57         free(buf);
58 }
59
60 static void do_loadtable(int fd, unsigned char *inbuf, int tailsz, int fontsize)
61 {
62         struct unimapinit advice;
63         struct unimapdesc ud;
64         struct unipair *up;
65         int ct = 0, maxct;
66         int glyph;
67         uint16_t unicode;
68
69         maxct = tailsz; /* more than enough */
70         up = xmalloc(maxct * sizeof(struct unipair));
71
72         for (glyph = 0; glyph < fontsize; glyph++) {
73                 while (tailsz >= 2) {
74                         unicode = (((uint16_t) inbuf[1]) << 8) + inbuf[0];
75                         tailsz -= 2;
76                         inbuf += 2;
77                         if (unicode == PSF_SEPARATOR)
78                                 break;
79                         up[ct].unicode = unicode;
80                         up[ct].fontpos = glyph;
81                         ct++;
82                 }
83         }
84
85         /* Note: after PIO_UNIMAPCLR and before PIO_UNIMAP
86            this printf did not work on many kernels */
87
88         advice.advised_hashsize = 0;
89         advice.advised_hashstep = 0;
90         advice.advised_hashlevel = 0;
91         xioctl(fd, PIO_UNIMAPCLR, &advice);
92         ud.entry_ct = ct;
93         ud.entries = up;
94         xioctl(fd, PIO_UNIMAP, &ud);
95 }
96
97 static void do_load(int fd, struct psf_header *psfhdr, size_t len)
98 {
99         int unit;
100         int fontsize;
101         int hastable;
102         unsigned head0, head = head;
103
104         /* test for psf first */
105         if (len >= sizeof(struct psf_header) && PSF_MAGIC_OK(psfhdr)) {
106                 if (psfhdr->mode > PSF_MAXMODE)
107                         bb_error_msg_and_die("unsupported psf file mode");
108                 fontsize = ((psfhdr->mode & PSF_MODE512) ? 512 : 256);
109 #if !defined(PIO_FONTX) || defined(__sparc__)
110                 if (fontsize != 256)
111                         bb_error_msg_and_die("only fontsize 256 supported");
112 #endif
113                 hastable = (psfhdr->mode & PSF_MODEHASTAB);
114                 unit = psfhdr->charsize;
115                 head0 = sizeof(struct psf_header);
116
117                 head = head0 + fontsize * unit;
118                 if (head > len || (!hastable && head != len))
119                         bb_error_msg_and_die("input file: bad length");
120         } else {
121                 /* file with three code pages? */
122                 if (len == 9780) {
123                         head0 = 40;
124                         unit = 16;
125                 } else {
126                         /* bare font */
127                         if (len & 0377)
128                                 bb_error_msg_and_die("input file: bad length");
129                         head0 = 0;
130                         unit = len / 256;
131                 }
132                 fontsize = 256;
133                 hastable = 0;
134         }
135
136         do_loadfont(fd, (unsigned char *)psfhdr + head0, unit, fontsize);
137         if (hastable)
138                 do_loadtable(fd, (unsigned char *)psfhdr + head, len - head, fontsize);
139 }
140
141 #if ENABLE_LOADFONT
142 int loadfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
143 int loadfont_main(int argc UNUSED_PARAM, char **argv)
144 {
145         size_t len;
146         struct psf_header *psfhdr;
147
148         // no arguments allowed!
149         opt_complementary = "=0";
150         getopt32(argv, "");
151
152         /*
153          * We used to look at the length of the input file
154          * with stat(); now that we accept compressed files,
155          * just read the entire file.
156          */
157         len = 32*1024; // can't be larger
158         psfhdr = (struct psf_header *) xmalloc_read(STDIN_FILENO, &len);
159         // xmalloc_open_zipped_read_close(filename, &len);
160         if (!psfhdr)
161                 bb_perror_msg_and_die("error reading input font");
162         do_load(get_console_fd_or_die(), psfhdr, len);
163
164         return EXIT_SUCCESS;
165 }
166 #endif
167
168 #if ENABLE_SETFONT
169
170 /*
171 kbd-1.12:
172
173 setfont [-O font+umap.orig] [-o font.orig] [-om cmap.orig]
174 [-ou umap.orig] [-N] [font.new ...] [-m cmap] [-u umap] [-C console]
175 [-hNN] [-v] [-V]
176
177 -h NN  Override font height
178 -o file
179        Save previous font in file
180 -O file
181        Save previous font and Unicode map in file
182 -om file
183        Store console map in file
184 -ou file
185        Save previous Unicode map in file
186 -m file
187        Load console map or Unicode console map from file
188 -u file
189        Load Unicode table describing the font from file
190        Example:
191        # cp866
192        0x00-0x7f       idem
193        #
194        0x80    U+0410  # CYRILLIC CAPITAL LETTER A
195        0x81    U+0411  # CYRILLIC CAPITAL LETTER BE
196        0x82    U+0412  # CYRILLIC CAPITAL LETTER VE
197 -C console
198        Set the font for the indicated console
199 -v     Verbose
200 -V     Version
201 */
202
203 #if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
204 static int ctoi(char *s)
205 {
206         if (s[0] == '\'' && s[1] != '\0' && s[2] == '\'' && s[3] == '\0')
207                 return s[1];
208         // U+ means 0x
209         if (s[0] == 'U' && s[1] == '+') {
210                 s[0] = '0';
211                 s[1] = 'x';
212         }
213         if (!isdigit(s[0]))
214                 return -1;
215         return xstrtoul(s, 0);
216 }
217 #endif
218
219 int setfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
220 int setfont_main(int argc UNUSED_PARAM, char **argv)
221 {
222         size_t len;
223         unsigned opts;
224         int fd;
225         struct psf_header *psfhdr;
226         char *mapfilename;
227         const char *tty_name = CURRENT_TTY;
228
229         opt_complementary = "=1";
230         opts = getopt32(argv, "m:C:", &mapfilename, &tty_name);
231         argv += optind;
232
233         fd = xopen(tty_name, O_NONBLOCK);
234
235         if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not ""
236                 if (strchr(*argv, '/') != NULL) {
237                         // goto default fonts location. don't die if doesn't exist
238                         chdir(CONFIG_DEFAULT_SETFONT_DIR "/consolefonts");
239                         // buglet: we don't return to current dir...
240                         // affects "setfont FONT -m ./MAP" case
241                 }
242         }
243         // load font
244         len = 32*1024; // can't be larger
245         psfhdr = (struct psf_header *) xmalloc_open_zipped_read_close(*argv, &len);
246         do_load(fd, psfhdr, len);
247
248         // load the screen map, if any
249         if (opts & 1) { // -m
250                 unsigned mode = PIO_SCRNMAP;
251                 void *map;
252
253                 if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not ""
254                         if (strchr(mapfilename, '/') != NULL) {
255                                 // goto default keymaps location
256                                 chdir(CONFIG_DEFAULT_SETFONT_DIR "/consoletrans");
257                         }
258                 }
259                 // fetch keymap
260                 map = xmalloc_open_zipped_read_close(mapfilename, &len);
261                 if (!map)
262                         bb_simple_perror_msg_and_die(mapfilename);
263                 // file size is 256 or 512 bytes? -> assume binary map
264                 if (len == E_TABSZ || len == 2*E_TABSZ) {
265                         if (len == 2*E_TABSZ)
266                                 mode = PIO_UNISCRNMAP;
267                 }
268 #if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
269                 // assume textual Unicode console maps:
270                 // 0x00 U+0000  #  NULL (NUL)
271                 // 0x01 U+0001  #  START OF HEADING (SOH)
272                 // 0x02 U+0002  #  START OF TEXT (STX)
273                 // 0x03 U+0003  #  END OF TEXT (ETX)
274                 else {
275                         int i;
276                         char *token[2];
277                         parser_t *parser;
278
279                         if (ENABLE_FEATURE_CLEAN_UP)
280                                 free(map);
281                         map = xmalloc(E_TABSZ * sizeof(unsigned short));
282
283 #define unicodes ((unsigned short *)map)
284                         // fill vanilla map
285                         for (i = 0; i < E_TABSZ; i++)
286                                 unicodes[i] = 0xf000 + i;
287
288                         parser = config_open(mapfilename);
289                         while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL | PARSE_MIN_DIE)) {
290                                 // parse code/value pair
291                                 int a = ctoi(token[0]);
292                                 int b = ctoi(token[1]);
293                                 if (a < 0 || a >= E_TABSZ
294                                  || b < 0 || b > 65535
295                                 ) {
296                                         bb_error_msg_and_die("map format");
297                                 }
298                                 // patch map
299                                 unicodes[a] = b;
300                                 // unicode character is met?
301                                 if (b > 255)
302                                         mode = PIO_UNISCRNMAP;
303                         }
304                         if (ENABLE_FEATURE_CLEAN_UP)
305                                 config_close(parser);
306
307                         if (mode != PIO_UNISCRNMAP) {
308 #define asciis ((unsigned char *)map)
309                                 for (i = 0; i < E_TABSZ; i++)
310                                         asciis[i] = unicodes[i];
311 #undef asciis
312                         }
313 #undef unicodes
314                 }
315 #endif // ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
316
317                 // do set screen map
318                 xioctl(fd, mode, map);
319
320                 if (ENABLE_FEATURE_CLEAN_UP)
321                         free(map);
322         }
323
324         return EXIT_SUCCESS;
325 }
326 #endif