0eda1bdfc5cfdc377783c9a022aecfe8390f4520
[platform/upstream/kbd.git] / src / psfxtable.c
1 /*
2  * psfxtable.c
3  *
4  * Manipulate headers and Unicode tables for psf fonts
5  *
6  * Copyright (C) 1999 Andries E. Brouwer
7  *  derived from sources that were
8  * Copyright (C) 1994 H. Peter Anvin
9  *
10  * This program may be freely copied under the terms of the GNU
11  * General Public License (GPL), version 2, or at your option
12  * any later version.
13  */
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <ctype.h>
19 #include <sysexits.h>
20 #include "nls.h"
21 #include "version.h"
22 #include "psf.h"
23 #include "xmalloc.h"
24 #include "psffontop.h"
25
26 /*
27  * call: psfxtable -i infont -o outfont -it intable -ot outtable
28  *       psfaddtable infont intable [outfont]
29  *       psfgettable infont [outtable]
30  *       psfstriptable infont [outfont]
31  *
32  * When outfont is requested it will get psf1 header when
33  * infont had psf1 header and intable does not have sequences
34  * and psf2 header otherwise.
35  */
36
37 /*
38  * Parse humanly readable unicode table.
39  * Format: lines
40  *   <fontposition><tab><uc_defs>
41  * or
42  *   <fontrange><tab><uc_defs>
43  * or
44  *   <fontrange><tab><uc_range>
45  *  where
46  *   <uc_defs> :: <empty> | <uc_def><space><uc_defs>
47  *   <uc_def> :: <uc> | <uc>,<uc_def>
48  *   <uc> :: U+<h><h><h><h>
49  *   <h> :: <hexadecimal digit>
50  *   <range> :: <value>-<value>
51  *  Blank lines and lines starting with # are ignored.
52  */
53 struct unicode_list *uclistheads;
54
55 static void
56 addpair(int fontpos, unsigned int uc) {
57         struct unicode_list *ul;
58         struct unicode_seq *us;
59
60         ul = xmalloc(sizeof(struct unicode_list));
61         us = xmalloc(sizeof(struct unicode_seq));
62         us->uc = uc;
63         us->prev = us;
64         us->next = NULL;
65         ul->seq = us;
66         ul->prev = uclistheads[fontpos].prev;
67         ul->prev->next = ul;
68         ul->next = NULL;
69         uclistheads[fontpos].prev = ul;
70 }
71
72 static void
73 addseq(int fontpos, unsigned int uc) {
74         struct unicode_list *ul;
75         struct unicode_seq *us;
76
77         ul = uclistheads[fontpos].prev;
78         us = xmalloc(sizeof(struct unicode_seq));
79         us->uc = uc;
80         us->prev = ul->seq->prev;
81         us->prev->next = us;
82         us->next = NULL;
83         ul->seq->prev = us;
84 }
85
86 static int
87 getunicode(char **p0) {
88         char *p = *p0;
89
90         while (*p == ' ' || *p == '\t')
91                 p++;
92         if (*p != 'U' || p[1] != '+' ||
93             !isxdigit(p[2]) || !isxdigit(p[3]) || !isxdigit(p[4]) ||
94             !isxdigit(p[5]) || isxdigit(p[6]))
95                 return -1;
96         *p0 = p+6;
97         return strtol(p+2,0,16);
98 }
99
100 static void
101 parse_itab_line(char *buf, int fontlen){
102         char *p, *p1;
103         int i;
104         long fp0, fp1, un0, un1;
105
106         if ((p = strchr(buf, '\n')) != NULL)
107                 *p = 0;
108         else {
109                 char *u = _("%s: Warning: line too long\n");
110                 fprintf(stderr, u, progname);
111                 exit(EX_DATAERR);
112         }
113
114         p = buf;
115
116         while (*p == ' ' || *p == '\t')
117                 p++;
118         if (!*p || *p == '#')
119                 return;
120
121         fp0 = strtol(p, &p1, 0);
122         if (p1 == p) {
123                 char *u = _("%s: Bad input line: %s\n");
124                 fprintf(stderr, u, progname, buf);
125                 exit(EX_DATAERR);
126         }
127         p = p1;
128
129         if (*p == '-') {
130                 p++;
131                 fp1 = strtol(p, &p1, 0);
132                 if (p1 == p) {
133                         char *u = _("%s: Bad input line: %s\n");
134                         fprintf(stderr, u, progname, buf);
135                         exit(EX_DATAERR);
136                 }
137                 p = p1;
138         } else
139                 fp1 = 0;
140
141         if (fp0 < 0 || fp0 >= fontlen) {
142                 char *u = _("%s: Glyph number (0x%lx) past end of font\n");
143                 fprintf(stderr, u, progname, fp0);
144                 exit(EX_DATAERR);
145         }
146         if (fp1 && (fp1 < fp0 || fp1 >= fontlen)) {
147                 char *u = _("%s: Bad end of range (0x%lx)\n");
148                 fprintf(stderr, u, progname, fp1);
149                 exit(EX_DATAERR);
150         }
151
152         if (fp1) {
153                 /* we have a range; expect the word "idem"
154                    or a Unicode range of the same length */
155                 while (*p == ' ' || *p == '\t')
156                         p++;
157                 if (!strncmp(p, "idem", 4)) {
158                         for (i = fp0; i <= fp1; i++)
159                                 addpair(i,i);
160                         p += 4;
161                 } else {
162                         un0 = getunicode(&p);
163                         while (*p == ' ' || *p == '\t')
164                                 p++;
165                         if (*p != '-') {
166                                 char *u = _("%s: Corresponding to a range of "
167                                             "font positions, there should be "
168                                             "a Unicode range\n");
169                                 fprintf(stderr, u, progname);
170                                 exit(EX_DATAERR);
171                         }
172                         p++;
173                         un1 = getunicode(&p);
174                         if (un0 < 0 || un1 < 0) {
175                                 char *u = _("%s: Bad Unicode range "
176                                             "corresponding to font position "
177                                             "range 0x%x-0x%x\n");
178                                 fprintf(stderr, u, progname, fp0, fp1);
179                                 exit(EX_DATAERR);
180                         }
181                         if (un1 - un0 != fp1 - fp0) {
182                                 char *u = _("%s: Unicode range U+%x-U+%x not "
183                                             "of the same length as font "
184                                             "position range 0x%x-0x%x\n");
185                                 fprintf(stderr, u, progname,
186                                         un0, un1, fp0, fp1);
187                                 exit(EX_DATAERR);
188                         }
189                         for (i = fp0; i <= fp1; i++)
190                                 addpair(i, un0-fp0+i);
191                 } /* not idem */
192         } else {  /* no range */
193                 while ((un0 = getunicode(&p)) >= 0) {
194                         addpair(fp0, un0);
195                         while (*p++ == ',' && (un1 = getunicode(&p)) >= 0) {
196                                 addseq(fp0, un1);
197                         }
198                         p--;
199                 }
200                 while (*p == ' ' || *p == '\t')
201                         p++;
202                 if (*p && *p != '#') {
203                         char *u = _("%s: trailing junk (%s) ignored\n");
204                         fprintf(stderr, u, progname, p);
205                 }
206         }
207 }
208
209 static void
210 read_itable(FILE *itab, int fontlen, struct unicode_list **uclistheadsp) {
211         char buf[65536];
212         int i;
213
214         if (uclistheadsp) {
215                 *uclistheadsp = xrealloc(*uclistheadsp,
216                                          fontlen*sizeof(struct unicode_list));
217                 for (i=0; i<fontlen; i++) {
218                         struct unicode_list *up = &((*uclistheadsp)[i]);
219                         up->next = NULL;
220                         up->seq = NULL;
221                         up->prev = up;
222                 }
223                 while (fgets(buf, sizeof(buf), itab) != NULL)
224                         parse_itab_line(buf, fontlen);
225         }
226 }
227
228 int debug = 0;
229
230 int
231 main(int argc, char **argv) {
232         char *ifname, *ofname, *itname, *otname;
233         FILE *ifil, *ofil, *itab, *otab;
234         int psftype, fontlen, charsize, hastable, notable;
235         int i;
236         int width = 8, bytewidth, height;
237         char *inbuf, *fontbuf;
238         int inbuflth, fontbuflth;
239
240         set_progname(argv[0]);
241
242         setlocale(LC_ALL, "");
243         bindtextdomain(PACKAGE_NAME, LOCALEDIR);
244         textdomain(PACKAGE_NAME);
245
246         if (argc == 2 && !strcmp(argv[1], "-V"))
247                 print_version_and_exit();
248
249         ifil = ofil = itab = otab = NULL;
250         ifname = ofname = itname = otname = NULL;
251         fontbuf = NULL;
252         notable = 0;
253
254         if (!strcmp(progname, "psfaddtable")) {
255                 /* Do not send binary data to stdout without explicit "-" */
256                 if (argc != 4) {
257                         char *u = _("Usage:\n\t%s infont intable outfont\n");
258                         fprintf(stderr, u, progname);
259                         exit(EX_USAGE);
260                 }
261                 ifname = argv[1];
262                 itname = argv[2];
263                 ofname = argv[3];
264         } else if (!strcmp(progname, "psfgettable")) {
265                 if (argc < 2 || argc > 3) {
266                         char *u = _("Usage:\n\t%s infont [outtable]\n");
267                         fprintf(stderr, u, progname);
268                         exit(EX_USAGE);
269                 }
270                 ifname = argv[1];
271                 otname = (argc == 3) ? argv[2] : "-";
272         } else if (!strcmp(progname, "psfstriptable")) {
273                 /* Do not send binary data to stdout without explicit "-" */
274                 if (argc != 3) {
275                         char *u = _("Usage:\n\t%s infont outfont\n");
276                         fprintf(stderr, u, progname);
277                         exit(EX_USAGE);
278                 }
279                 ifname = argv[1];
280                 ofname = argv[2];
281                 notable = 1;
282         } else {
283                 for (i = 1; i < argc; i ++) {
284                         if ((!strcmp(argv[i], "-i") || !strcmp(argv[i], "-if"))
285                             && i < argc-1)
286                                 ifname = argv[++i];
287                         else if((!strcmp(argv[i],"-o")||!strcmp(argv[i],"-of"))
288                                 && i < argc-1)
289                                 ofname = argv[++i];
290                         else if(!strcmp(argv[i], "-it") && i < argc-1)
291                                 itname = argv[++i];
292                         else if(!strcmp(argv[i], "-ot") && i < argc-1)
293                                 otname = argv[++i];
294                         else if(!strcmp(argv[i], "-nt"))
295                                 notable = 1;
296                         else
297                                 break;
298                 }
299                 if (i < argc || argc <= 1) {
300                         char *u = _("Usage:\n\t%s [-i infont] [-o outfont] "
301                                     "[-it intable] [-ot outtable] [-nt]\n");
302                         fprintf(stderr, u, progname);
303                         exit(EX_USAGE);
304                 }
305         }
306
307         if (!ifname)
308                 ifname = "-";
309         if (!strcmp(ifname, "-"))
310                 ifil = stdin;
311         else {
312                 ifil = fopen(ifname, "r");
313                 if (!ifil) {
314                         perror(ifname);
315                         exit(EX_NOINPUT);
316                 }
317         }
318
319         if (!itname)
320                 /* nothing */;
321         else if (!strcmp(itname, "-"))
322                 itab = stdin;
323         else {
324                 itab = fopen(itname, "r");
325                 if (!itab) {
326                         perror(itname);
327                         exit(EX_NOINPUT);
328                 }
329         }
330
331         /* Refuse ifil == itab == stdin ? Perhaps not. */
332
333         if (!ofname)
334                 /* nothing */;
335         else if (!strcmp(ofname, "-"))
336                 ofil = stdout;
337         else {
338                 ofil = fopen(ofname, "w");
339                 if (!ofil) {
340                         perror(ofname);
341                         exit(EX_CANTCREAT);
342                 }
343         }
344
345         if (!otname)
346                 /* nothing */;
347         else if (!strcmp(otname, "-"))
348                 otab = stdout;
349         else {
350                 otab = fopen(otname, "w");
351                 if (!otab) {
352                         perror(otname);
353                         exit(EX_CANTCREAT);
354                 }
355         }
356
357         if (readpsffont(ifil, &inbuf, &inbuflth, &fontbuf, &fontbuflth,
358                         &width, &fontlen, 0,
359                         itab ? NULL : &uclistheads) == -1) {
360                 char *u = _("%s: Bad magic number on %s\n");
361                 fprintf(stderr, u, progname, ifname);
362                 exit(EX_DATAERR);
363         }
364         fclose(ifil);
365
366         charsize = fontbuflth/fontlen;
367         bytewidth = (width + 7)/8;
368         if (!bytewidth)
369                 bytewidth = 1;
370         height = charsize / bytewidth;
371
372         hastable = (uclistheads != NULL);
373
374         if (PSF1_MAGIC_OK((unsigned char *)inbuf)) {
375                 psftype = 1;
376         } else if (PSF2_MAGIC_OK((unsigned char *)inbuf)) {
377                 psftype = 2;
378         } else {
379                 char *u = _("%s: psf file with unknown magic\n");
380                 fprintf(stderr, u, progname);
381                 exit(EX_DATAERR);
382         }
383
384         if (itab) {
385                 read_itable(itab, fontlen, &uclistheads);
386                 fclose(itab);
387         }
388
389         if (otab) {
390                 struct unicode_list *ul;
391                 struct unicode_seq *us;
392                 char *sep;
393
394                 if (!hastable) {
395                         char *u = _("%s: input font does not have an index\n");
396                         fprintf(stderr, u, progname);
397                         exit(EX_DATAERR);
398                 }
399                 fprintf(otab,
400                         "#\n# Character table extracted from font %s\n#\n",
401                         ifname);
402                 for (i=0; i<fontlen; i++) {
403                         fprintf(otab, "0x%03x\t", i);
404                         sep = "";
405                         ul = uclistheads[i].next;
406                         while (ul) {
407                                 us = ul->seq;
408                                 while(us) {
409                                         fprintf(otab, "%sU+%04x", sep, us->uc);
410                                         us = us->next;
411                                         sep = ", ";
412                                 }
413                                 ul = ul->next;
414                                 sep = " ";
415                         }
416                         fprintf(otab, "\n");
417                 }
418                 fclose(otab);
419         }
420
421         if (ofil) {
422                 writepsffont(ofil, fontbuf, width, height, fontlen, psftype,
423                              notable ? NULL : uclistheads);
424                 fclose(ofil);
425         }
426
427         return EX_OK;
428 }