ident.c revision a5c37dfe
1/*
2  Copyright (c) 2003 by Juliusz Chroboczek
3
4  Permission is hereby granted, free of charge, to any person obtaining a copy
5  of this software and associated documentation files (the "Software"), to deal
6  in the Software without restriction, including without limitation the rights
7  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  copies of the Software, and to permit persons to whom the Software is
9  furnished to do so, subject to the following conditions:
10
11  The above copyright notice and this permission notice shall be included in
12  all copies or substantial portions of the Software.
13
14  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  THE SOFTWARE.
21*/
22/*
23 * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
24 *
25 * Permission is hereby granted, free of charge, to any person obtaining a
26 * copy of this software and associated documentation files (the "Software"),
27 * to deal in the Software without restriction, including without limitation
28 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
29 * and/or sell copies of the Software, and to permit persons to whom the
30 * Software is furnished to do so, subject to the following conditions:
31 *
32 * The above copyright notice and this permission notice (including the next
33 * paragraph) shall be included in all copies or substantial portions of the
34 * Software.
35 *
36 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
37 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
39 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
40 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
41 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
42 * DEALINGS IN THE SOFTWARE.
43 */
44
45/* The function identifyBitmap returns -1 if filename is definitively not
46   a font file, 1 if it is a single-face bitmap font with a XLFD name,
47   and 0 if it should be processed normally.  identifyBitmap is
48   much faster than parsing the whole font. */
49
50#ifdef HAVE_CONFIG_H
51#include "config.h"
52#endif
53
54#include <stdlib.h>
55#include <string.h>
56#include "zlib.h"
57#include "ident.h"
58
59#ifdef X_BZIP2_FONT_COMPRESSION
60# include <bzlib.h>
61#endif
62
63#define PCF_VERSION (('p'<<24)|('c'<<16)|('f'<<8)|1)
64#define PCF_PROPERTIES (1 << 0)
65
66typedef struct _Prop {
67    unsigned name;
68    int isString;
69    unsigned value;
70} PropRec, *PropPtr;
71
72#ifdef X_BZIP2_FONT_COMPRESSION
73typedef struct {
74    enum { gzFontFile, bz2FontFile } type;
75    union {
76	gzFile gz;
77	BZFILE *bz2;
78    } f;
79    unsigned pos;
80} fontFile;
81
82static inline void *
83fontFileOpen(fontFile *ff, const char *filename) {
84    int n = strlen(filename);
85
86    if (strcmp(filename + n - 4, ".bz2") == 0) {
87	ff->type = bz2FontFile;
88	ff->f.bz2 = BZ2_bzopen(filename, "rb");
89	ff->pos = 0;
90	return ff->f.bz2;
91    } else {
92	ff->type = gzFontFile;
93	ff->f.gz = gzopen(filename, "rb");
94	return ff->f.gz;
95    }
96}
97
98static inline int
99fontFileRead(fontFile *ff, void *buf, unsigned len)
100{
101    if (ff->type == gzFontFile) {
102	return gzread(ff->f.gz, buf, len);
103    } else {
104	int r = BZ2_bzread(ff->f.bz2, buf, len);
105	ff->pos += r;
106	return r;
107    }
108}
109
110static inline int
111fontFileGetc(fontFile *ff)
112{
113    if (ff->type == gzFontFile) {
114	return gzgetc(ff->f.gz);
115    } else {
116	char buf;
117	if (BZ2_bzread(ff->f.bz2, &buf, 1) != 1) {
118	    return -1;
119	} else {
120	    ff->pos += 1;
121	    return (int) buf;
122	}
123    }
124}
125
126static int
127fontFileSeek(fontFile *ff, z_off_t offset, int whence)
128{
129    if (ff->type == gzFontFile) {
130	return gzseek(ff->f.gz, offset, whence);
131    } else {
132	/* bzlib has no easy equivalent so we have to fake it,
133	 * fortunately, we only have to handle a couple of cases
134	 */
135	int n;
136	char buf[BUFSIZ];
137
138	switch (whence) {
139	  case SEEK_SET:
140	    n = offset - ff->pos;
141	    break;
142	  case SEEK_CUR:
143	    n = offset;
144	    break;
145	  default:
146	    return -1;
147	}
148
149	while (n > BUFSIZ) {
150	    if (BZ2_bzread(ff->f.bz2, buf, BUFSIZ) != BUFSIZ)
151		return -1;
152	    n -= BUFSIZ;
153	}
154	if (BZ2_bzread(ff->f.bz2, buf, n) != n)
155	    return -1;
156	ff->pos = offset;
157	return offset;
158    }
159}
160
161
162static inline int
163fontFileClose(fontFile *ff)
164{
165    if (ff->type == gzFontFile) {
166	return gzclose(ff->f.gz);
167    } else {
168	BZ2_bzclose(ff->f.bz2);
169	return 0;
170    }
171}
172
173#else /* no bzip2, only gzip */
174typedef gzFile fontFile;
175# define fontFileOpen(ff, filename)	(*(ff) = gzopen(filename, "rb"))
176# define fontFileRead(ff, buf, len)	gzread(*(ff), buf, len)
177# define fontFileGetc(ff)		gzgetc(*(ff))
178# define fontFileSeek(ff, off, whence)	gzseek(*(ff), off, whence)
179# define fontFileClose(ff)		gzclose(*(ff))
180#endif
181
182static int pcfIdentify(fontFile *f, char **name);
183static int bdfIdentify(fontFile *f, char **name);
184
185static int
186getLSB32(fontFile *f)
187{
188    int rc;
189    unsigned char c[4];
190
191    rc = fontFileRead(f, c, 4);
192    if(rc != 4)
193        return -1;
194    return (c[0]) | (c[1] << 8) | (c[2] << 16) | (c[3] << 24);
195}
196
197static int
198getInt8(fontFile *f, int format)
199{
200    unsigned char c;
201    int rc;
202
203    rc = fontFileRead(f, &c, 1);
204    if(rc != 1)
205        return -1;
206    return c;
207}
208
209static int
210getInt32(fontFile *f, int format)
211{
212    int rc;
213    unsigned char c[4];
214
215    rc = fontFileRead(f, c, 4);
216    if(rc != 4)
217        return -1;
218
219    if(format & (1 << 2)) {
220        return (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | (c[3]);
221    } else {
222        return (c[0]) | (c[1] << 8) | (c[2] << 16) | (c[3] << 24);
223    }
224}
225
226int
227bitmapIdentify(const char *filename, char **name)
228{
229    fontFile ff;
230    int magic;
231
232    if (fontFileOpen(&ff, filename) == NULL)
233	return -1;
234
235    magic = getLSB32(&ff);
236    if(magic == PCF_VERSION)
237        return pcfIdentify(&ff, name);
238    else if(magic == ('S' | ('T' << 8) | ('A' << 16) | ('R') << 24))
239        return bdfIdentify(&ff, name);
240
241    fontFileClose(&ff);
242    return 0;
243}
244
245static int
246pcfIdentify(fontFile *f, char **name)
247{
248    int prop_position;
249    PropPtr props = NULL;
250    int format, count, nprops, i, string_size, rc;
251    char *strings = NULL, *s;
252
253    count = getLSB32(f);
254    if(count <= 0)
255        goto fail;
256
257    prop_position = -1;
258    for(i = 0; i < count; i++) {
259        int type, offset;
260        type = getLSB32(f);
261        (void) getLSB32(f);
262        (void) getLSB32(f);
263        offset = getLSB32(f);
264        if(type == PCF_PROPERTIES) {
265            prop_position = offset;
266            break;
267        }
268    }
269    if(prop_position < 0)
270        goto fail;
271
272    rc = fontFileSeek(f, prop_position, SEEK_SET);
273    if(rc < 0)
274        goto fail;
275
276    format = getLSB32(f);
277    if((format & 0xFFFFFF00) != 0)
278        goto fail;
279    nprops = getInt32(f, format);
280    if(nprops <= 0 || nprops > 1000)
281        goto fail;
282    props = malloc(nprops * sizeof(PropRec));
283    if(props == NULL)
284        goto fail;
285
286    for(i = 0; i < nprops; i++) {
287        props[i].name = getInt32(f, format);
288        props[i].isString = getInt8(f, format);
289        props[i].value = getInt32(f, format);
290    }
291    if(nprops & 3) {
292	rc = fontFileSeek(f, 4 - (nprops & 3), SEEK_CUR);
293        if(rc < 0)
294            goto fail;
295    }
296
297    string_size = getInt32(f, format);
298    if(string_size < 0 || string_size > 100000)
299        goto fail;
300    strings = malloc(string_size);
301    if(!strings)
302        goto fail;
303
304    rc = fontFileRead(f, strings, string_size);
305    if(rc != string_size)
306        goto fail;
307
308    for(i = 0; i < nprops; i++) {
309        if(!props[i].isString ||
310           props[i].name >= string_size - 4 ||
311           props[i].value >= string_size)
312            continue;
313        if(strcmp(strings + props[i].name, "FONT") == 0)
314            break;
315    }
316
317    if(i >= nprops)
318        goto fail;
319
320    s = malloc(strlen(strings + props[i].value) + 1);
321    if(s == NULL)
322        goto fail;
323    strcpy(s, strings + props[i].value);
324    *name = s;
325    free(strings);
326    free(props);
327    fontFileClose(f);
328    return 1;
329
330 fail:
331    if(strings) free(strings);
332    if(props) free(props);
333    fontFileClose(f);
334    return 0;
335}
336
337#define NKEY 20
338
339static char*
340getKeyword(fontFile *f, int *eol)
341{
342    static char keyword[NKEY + 1];
343    int c, i;
344    i = 0;
345    while(i < NKEY) {
346        c = fontFileGetc(f);
347        if(c == ' ' || c == '\n') {
348            if(i <= 0)
349                return NULL;
350            if(eol)
351                *eol = (c == '\n');
352            keyword[i] = '\0';
353            return keyword;
354        }
355        if(c < 'A' || c > 'Z')
356            return NULL;
357        keyword[i++] = c;
358    }
359    return NULL;
360}
361
362static int
363bdfskip(fontFile *f)
364{
365    int c;
366    do {
367        c = fontFileGetc(f);
368    } while(c >= 0 && c != '\n');
369    if(c < 0)
370        return -1;
371    return 1;
372}
373
374static char *
375bdfend(fontFile *f)
376{
377    int c;
378    char *buf = NULL;
379    int bufsize = 0;
380    int i = 0;
381
382    do {
383        c = fontFileGetc(f);
384    } while (c == ' ');
385
386    while(i < 1000) {
387        if(c < 0 || (c == '\n' && i == 0)) {
388            goto fail;
389        }
390        if(bufsize < i + 1) {
391            char *newbuf;
392            if(bufsize == 0) {
393                bufsize = 20;
394                newbuf = malloc(bufsize);
395            } else {
396                bufsize = 2 * bufsize;
397                newbuf = realloc(buf, bufsize);
398            }
399            if(newbuf == NULL)
400                goto fail;
401            buf = newbuf;
402        }
403        if(c == '\n') {
404            buf[i] = '\0';
405            return buf;
406        }
407        buf[i++] = c;
408        c = fontFileGetc(f);
409    }
410
411 fail:
412    if(buf)
413        free(buf);
414    return NULL;
415}
416
417static int
418bdfIdentify(fontFile *f, char **name)
419{
420    char *k;
421    int rc;
422    int eol;
423    /* bitmapIdentify already read "STAR", so we need to check for
424       "TFONT" */
425    k = getKeyword(f, &eol);
426    if(k == NULL || eol)
427        goto fail;
428    if(strcmp(k, "TFONT") != 0)
429        goto fail;
430    while(1) {
431        if(!eol) {
432            rc = bdfskip(f);
433            if(rc < 0)
434                goto fail;
435        }
436        k = getKeyword(f, &eol);
437        if(k == NULL)
438            goto fail;
439        else if(strcmp(k, "FONT") == 0) {
440            if(eol)
441                goto fail;
442            k = bdfend(f);
443            if(k == NULL)
444                goto fail;
445            *name = k;
446            fontFileClose(f);
447            return 1;
448        } else if(strcmp(k, "CHARS") == 0)
449            goto fail;
450    }
451 fail:
452    fontFileClose(f);
453    return 0;
454}
455