who: stop using static buffer, small size optimizations
[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 #include "busybox.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 loadnewfont(int fd);
32
33 int loadfont_main(int argc, char **argv);
34 int loadfont_main(int argc, char **argv)
35 {
36         int fd;
37
38         if (argc != 1)
39                 bb_show_usage();
40
41         fd = xopen(CURRENT_VC, O_RDWR);
42         loadnewfont(fd);
43
44         return EXIT_SUCCESS;
45 }
46
47 static void do_loadfont(int fd, unsigned char *inbuf, int unit, int fontsize)
48 {
49         char buf[16384];
50         int i;
51
52         memset(buf, 0, sizeof(buf));
53
54         if (unit < 1 || unit > 32)
55                 bb_error_msg_and_die("bad character size %d", unit);
56
57         for (i = 0; i < fontsize; i++)
58                 memcpy(buf + (32 * i), inbuf + (unit * i), unit);
59
60 #if defined( PIO_FONTX ) && !defined( __sparc__ )
61         {
62                 struct consolefontdesc cfd;
63
64                 cfd.charcount = fontsize;
65                 cfd.charheight = unit;
66                 cfd.chardata = buf;
67
68                 if (ioctl(fd, PIO_FONTX, &cfd) == 0)
69                         return;                         /* success */
70                 bb_perror_msg("PIO_FONTX ioctl error (trying PIO_FONT)");
71         }
72 #endif
73         if (ioctl(fd, PIO_FONT, buf))
74                 bb_perror_msg_and_die("PIO_FONT ioctl error");
75 }
76
77 static void
78 do_loadtable(int fd, unsigned char *inbuf, int tailsz, int fontsize)
79 {
80         struct unimapinit advice;
81         struct unimapdesc ud;
82         struct unipair *up;
83         int ct = 0, maxct;
84         int glyph;
85         uint16_t unicode;
86
87         maxct = tailsz;                         /* more than enough */
88         up = xmalloc(maxct * sizeof(struct unipair));
89
90         for (glyph = 0; glyph < fontsize; glyph++) {
91                 while (tailsz >= 2) {
92                         unicode = (((uint16_t) inbuf[1]) << 8) + inbuf[0];
93                         tailsz -= 2;
94                         inbuf += 2;
95                         if (unicode == PSF_SEPARATOR)
96                                 break;
97                         up[ct].unicode = unicode;
98                         up[ct].fontpos = glyph;
99                         ct++;
100                 }
101         }
102
103         /* Note: after PIO_UNIMAPCLR and before PIO_UNIMAP
104            this printf did not work on many kernels */
105
106         advice.advised_hashsize = 0;
107         advice.advised_hashstep = 0;
108         advice.advised_hashlevel = 0;
109         if (ioctl(fd, PIO_UNIMAPCLR, &advice)) {
110 #ifdef ENOIOCTLCMD
111                 if (errno == ENOIOCTLCMD) {
112                         bb_error_msg("it seems this kernel is older than 1.1.92");
113                         bb_error_msg_and_die("no Unicode mapping table loaded");
114                 } else
115 #endif
116                         bb_perror_msg_and_die("PIO_UNIMAPCLR");
117         }
118         ud.entry_ct = ct;
119         ud.entries = up;
120         if (ioctl(fd, PIO_UNIMAP, &ud)) {
121                 bb_perror_msg_and_die("PIO_UNIMAP");
122         }
123 }
124
125 static void loadnewfont(int fd)
126 {
127         int unit;
128         unsigned char inbuf[32768];                     /* primitive */
129         unsigned int inputlth, offset;
130
131         /*
132          * We used to look at the length of the input file
133          * with stat(); now that we accept compressed files,
134          * just read the entire file.
135          */
136         inputlth = fread(inbuf, 1, sizeof(inbuf), stdin);
137         if (ferror(stdin))
138                 bb_perror_msg_and_die("error reading input font");
139         /* use malloc/realloc in case of giant files;
140            maybe these do not occur: 16kB for the font,
141            and 16kB for the map leaves 32 unicode values
142            for each font position */
143         if (!feof(stdin))
144                 bb_perror_msg_and_die("font too large");
145
146         /* test for psf first */
147         {
148                 struct psf_header psfhdr;
149                 int fontsize;
150                 int hastable;
151                 unsigned int head0, head;
152
153                 if (inputlth < sizeof(struct psf_header))
154                         goto no_psf;
155
156                 psfhdr = *(struct psf_header *) &inbuf[0];
157
158                 if (!PSF_MAGIC_OK(psfhdr))
159                         goto no_psf;
160
161                 if (psfhdr.mode > PSF_MAXMODE)
162                         bb_error_msg_and_die("unsupported psf file mode");
163                 fontsize = ((psfhdr.mode & PSF_MODE512) ? 512 : 256);
164 #if !defined( PIO_FONTX ) || defined( __sparc__ )
165                 if (fontsize != 256)
166                         bb_error_msg_and_die("only fontsize 256 supported");
167 #endif
168                 hastable = (psfhdr.mode & PSF_MODEHASTAB);
169                 unit = psfhdr.charsize;
170                 head0 = sizeof(struct psf_header);
171
172                 head = head0 + fontsize * unit;
173                 if (head > inputlth || (!hastable && head != inputlth))
174                         bb_error_msg_and_die("input file: bad length");
175                 do_loadfont(fd, inbuf + head0, unit, fontsize);
176                 if (hastable)
177                         do_loadtable(fd, inbuf + head, inputlth - head, fontsize);
178                 return;
179         }
180   no_psf:
181
182         /* file with three code pages? */
183         if (inputlth == 9780) {
184                 offset = 40;
185                 unit = 16;
186         } else {
187                 /* bare font */
188                 if (inputlth & 0377)
189                         bb_error_msg_and_die("bad input file size");
190                 offset = 0;
191                 unit = inputlth / 256;
192         }
193         do_loadfont(fd, inbuf + offset, unit, 256);
194 }