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