ident.c revision eaa89f16
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 (n > 4 && 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 = strdup(strings + props[i].value);
321    if(s == NULL)
322        goto fail;
323    *name = s;
324    free(strings);
325    free(props);
326    fontFileClose(f);
327    return 1;
328
329 fail:
330    if(strings) free(strings);
331    if(props) free(props);
332    fontFileClose(f);
333    return 0;
334}
335
336#define NKEY 20
337
338static char*
339getKeyword(fontFile *f, int *eol)
340{
341    static char keyword[NKEY + 1];
342    int c, i;
343    i = 0;
344    while(i < NKEY) {
345        c = fontFileGetc(f);
346        if(c == ' ' || c == '\n') {
347            if(i <= 0)
348                return NULL;
349            if(eol)
350                *eol = (c == '\n');
351            keyword[i] = '\0';
352            return keyword;
353        }
354        if(c < 'A' || c > 'Z')
355            return NULL;
356        keyword[i++] = c;
357    }
358    return NULL;
359}
360
361static int
362bdfskip(fontFile *f)
363{
364    int c;
365    do {
366        c = fontFileGetc(f);
367    } while(c >= 0 && c != '\n');
368    if(c < 0)
369        return -1;
370    return 1;
371}
372
373static char *
374bdfend(fontFile *f)
375{
376    int c;
377    char *buf = NULL;
378    int bufsize = 0;
379    int i = 0;
380
381    do {
382        c = fontFileGetc(f);
383    } while (c == ' ');
384
385    while(i < 1000) {
386        if(c < 0 || (c == '\n' && i == 0)) {
387            goto fail;
388        }
389        if(bufsize < i + 1) {
390            char *newbuf;
391            if(bufsize == 0) {
392                bufsize = 20;
393                newbuf = malloc(bufsize);
394            } else {
395                bufsize = 2 * bufsize;
396                newbuf = realloc(buf, bufsize);
397            }
398            if(newbuf == NULL)
399                goto fail;
400            buf = newbuf;
401        }
402        if(c == '\n') {
403            buf[i] = '\0';
404            return buf;
405        }
406        buf[i++] = c;
407        c = fontFileGetc(f);
408    }
409
410 fail:
411    if(buf)
412        free(buf);
413    return NULL;
414}
415
416static int
417bdfIdentify(fontFile *f, char **name)
418{
419    char *k;
420    int rc;
421    int eol;
422    /* bitmapIdentify already read "STAR", so we need to check for
423       "TFONT" */
424    k = getKeyword(f, &eol);
425    if(k == NULL || eol)
426        goto fail;
427    if(strcmp(k, "TFONT") != 0)
428        goto fail;
429    while(1) {
430        if(!eol) {
431            rc = bdfskip(f);
432            if(rc < 0)
433                goto fail;
434        }
435        k = getKeyword(f, &eol);
436        if(k == NULL)
437            goto fail;
438        else if(strcmp(k, "FONT") == 0) {
439            if(eol)
440                goto fail;
441            k = bdfend(f);
442            if(k == NULL)
443                goto fail;
444            *name = k;
445            fontFileClose(f);
446            return 1;
447        } else if(strcmp(k, "CHARS") == 0)
448            goto fail;
449    }
450 fail:
451    fontFileClose(f);
452    return 0;
453}
454