util.c revision d2f28e1b
143f32c10Smrg/*
243f32c10SmrgCopyright (c) 2002-2003 by Juliusz Chroboczek
343f32c10Smrg
443f32c10SmrgPermission is hereby granted, free of charge, to any person obtaining a copy
543f32c10Smrgof this software and associated documentation files (the "Software"), to deal
643f32c10Smrgin the Software without restriction, including without limitation the rights
743f32c10Smrgto use, copy, modify, merge, publish, distribute, sublicense, and/or sell
843f32c10Smrgcopies of the Software, and to permit persons to whom the Software is
943f32c10Smrgfurnished to do so, subject to the following conditions:
1043f32c10Smrg
1143f32c10SmrgThe above copyright notice and this permission notice shall be included in
1243f32c10Smrgall copies or substantial portions of the Software.
1343f32c10Smrg
1443f32c10SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1543f32c10SmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1643f32c10SmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
1743f32c10SmrgAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1843f32c10SmrgLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1943f32c10SmrgOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2043f32c10SmrgTHE SOFTWARE.
2143f32c10Smrg*/
2243f32c10Smrg/* $XdotOrg: xc/programs/fonttosfnt/util.c,v 1.11 2003/12/19 02:16:36 dawes Exp $ */
2343f32c10Smrg/* $XFree86: xc/programs/fonttosfnt/util.c,v 1.10 2003/12/19 02:05:39 dawes Exp $ */
2443f32c10Smrg
2543f32c10Smrg#include <time.h>
2643f32c10Smrg#include <string.h>
2743f32c10Smrg#include <errno.h>
2843f32c10Smrg#include <stdlib.h>
2943f32c10Smrg#ifndef __UNIXOS2__
3043f32c10Smrg# include <math.h>
3143f32c10Smrg#else
3243f32c10Smrg# include <float.h>
3343f32c10Smrg#endif
3443f32c10Smrg#include <stdarg.h>
3543f32c10Smrg
3643f32c10Smrg#include <ft2build.h>
3743f32c10Smrg#include FT_FREETYPE_H
3843f32c10Smrg#include FT_BDF_H
3943f32c10Smrg#include "X11/Xos.h"
40ea148d1dSmrg#include "X11/Xfuncproto.h" /* for _X_ATTRIBUTE_PRINTF */
4143f32c10Smrg#include "fonttosfnt.h"
4243f32c10Smrg
4343f32c10Smrg#ifdef NEED_SNPRINTF
4443f32c10Smrg#undef SCOPE
4543f32c10Smrg#define SCOPE static
4643f32c10Smrg#include "snprintf.c"
4743f32c10Smrg#endif
4843f32c10Smrg
4943f32c10Smrg#ifdef __GLIBC__
5043f32c10Smrg#define HAVE_TIMEGM
5143f32c10Smrg#define HAVE_TM_GMTOFF
5243f32c10Smrg#endif
5343f32c10Smrg
5443f32c10Smrg#ifdef BSD
5543f32c10Smrg#define HAVE_TM_GMTOFF
5643f32c10Smrg#define GMTOFFMEMBER tm_gmtoff
5743f32c10Smrg#endif
5843f32c10Smrg
5943f32c10Smrg#ifdef __SCO__
6043f32c10Smrg#define HAVE_TM_GMTOFF
6143f32c10Smrg#define GMTOFFMEMBER tm_tzadj
6243f32c10Smrg#endif
6343f32c10Smrg
6443f32c10Smrg/* That's in POSIX */
6543f32c10Smrg#define HAVE_TZSET
6643f32c10Smrg
6743f32c10Smrg#ifdef NEED_SETENV
6843f32c10Smrgextern int setenv(const char *name, const char *value, int overwrite);
6943f32c10Smrgextern void unsetenv(const char *name);
7043f32c10Smrg#endif
7143f32c10Smrg
7243f32c10Smrgchar*
736ef05171Smrgsprintf_alloc(const char *f, ...)
7443f32c10Smrg{
7543f32c10Smrg    char *s;
7643f32c10Smrg    va_list args;
7743f32c10Smrg    va_start(args, f);
7843f32c10Smrg    s = vsprintf_alloc(f, args);
7943f32c10Smrg    va_end(args);
8043f32c10Smrg    return s;
8143f32c10Smrg}
8243f32c10Smrg
8343f32c10Smrg#if HAVE_VASPRINTF
84ea148d1dSmrg_X_ATTRIBUTE_PRINTF(1, 0)
8543f32c10Smrgchar*
866ef05171Smrgvsprintf_alloc(const char *f, va_list args)
8743f32c10Smrg{
8843f32c10Smrg    char *r;
8943f32c10Smrg    int rc;
9043f32c10Smrg
9143f32c10Smrg    rc = vasprintf(&r, f, args);
9243f32c10Smrg    if(rc < 0)
9343f32c10Smrg        return NULL;
9443f32c10Smrg    return r;
9543f32c10Smrg}
9643f32c10Smrg#else
97ea148d1dSmrg_X_ATTRIBUTE_PRINTF(1, 0)
9843f32c10Smrgchar*
996ef05171Smrgvsprintf_alloc(const char *f, va_list args)
10043f32c10Smrg{
10143f32c10Smrg    int n, size = 12;
10243f32c10Smrg    char *string;
10343f32c10Smrg    va_list args_copy;
10443f32c10Smrg
10543f32c10Smrg    while(1) {
10643f32c10Smrg        if(size > 4096)
10743f32c10Smrg            return NULL;
10843f32c10Smrg        string = malloc(size);
10943f32c10Smrg        if(!string)
11043f32c10Smrg            return NULL;
11143f32c10Smrg
11243f32c10Smrg#if HAVE_DECL_VA_COPY
11343f32c10Smrg        va_copy(args_copy, args);
11443f32c10Smrg        n = vsnprintf(string, size, f, args_copy);
11543f32c10Smrg#else
11643f32c10Smrg        n = vsnprintf(string, size, f, args);
11743f32c10Smrg#endif
11843f32c10Smrg        if(n >= 0 && n < size)
11943f32c10Smrg            return string;
12043f32c10Smrg        else if(n >= size)
12143f32c10Smrg            size = n + 1;
12243f32c10Smrg        else
12343f32c10Smrg            size = size * 3 / 2 + 1;
12443f32c10Smrg        free(string);
12543f32c10Smrg    }
12643f32c10Smrg    /* NOTREACHED */
12743f32c10Smrg}
12843f32c10Smrg#endif
12943f32c10Smrg
130ea148d1dSmrg/* Build a UTF-16 string from a Latin-1 string.
13143f32c10Smrg   Result is not NUL-terminated. */
13243f32c10Smrgchar *
1336ef05171SmrgmakeUTF16(const char *string)
13443f32c10Smrg{
13543f32c10Smrg    int i;
13643f32c10Smrg    int n = strlen(string);
13743f32c10Smrg    char *value = malloc(2 * n);
13843f32c10Smrg    if(!value)
13943f32c10Smrg        return NULL;
14043f32c10Smrg    for(i = 0; i < n; i++) {
14143f32c10Smrg        value[2 * i] = '\0';
14243f32c10Smrg        value[2 * i + 1] = string[i];
14343f32c10Smrg    }
14443f32c10Smrg    return value;
14543f32c10Smrg}
14643f32c10Smrg
14743f32c10Smrgunsigned
1486ef05171SmrgmakeName(const char *s)
14943f32c10Smrg{
15043f32c10Smrg    return s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3];
15143f32c10Smrg}
15243f32c10Smrg
15343f32c10Smrg/* Like mktime(3), but UTC rather than local time */
15443f32c10Smrg#if defined(HAVE_TIMEGM)
15543f32c10Smrgstatic time_t
15643f32c10Smrgmktime_gmt(struct tm *tm)
15743f32c10Smrg{
15843f32c10Smrg    return timegm(tm);
15943f32c10Smrg}
16043f32c10Smrg#elif defined(HAVE_TM_GMTOFF)
16143f32c10Smrgstatic time_t
16243f32c10Smrgmktime_gmt(struct tm *tm)
16343f32c10Smrg{
16443f32c10Smrg    time_t t;
16543f32c10Smrg    struct tm *ltm;
16643f32c10Smrg
16743f32c10Smrg    t = mktime(tm);
16843f32c10Smrg    if(t < 0)
16943f32c10Smrg        return -1;
17043f32c10Smrg    ltm = localtime(&t);
17143f32c10Smrg    if(ltm == NULL)
17243f32c10Smrg        return -1;
17343f32c10Smrg    return t + ltm->GMTOFFMEMBER;
17443f32c10Smrg}
17543f32c10Smrg#elif defined(HAVE_TZSET)
17643f32c10Smrg/* Taken from the Linux timegm(3) man page */
17743f32c10Smrgstatic time_t
17843f32c10Smrgmktime_gmt(struct tm *tm)
17943f32c10Smrg{
18043f32c10Smrg    time_t t;
18143f32c10Smrg    char *tz;
18243f32c10Smrg
18343f32c10Smrg    tz = getenv("TZ");
18443f32c10Smrg    setenv("TZ", "", 1);
18543f32c10Smrg    tzset();
18643f32c10Smrg    t = mktime(tm);
18743f32c10Smrg    if(tz)
18843f32c10Smrg        setenv("TZ", tz, 1);
18943f32c10Smrg    else
19043f32c10Smrg        unsetenv("TZ");
19143f32c10Smrg    tzset();
19243f32c10Smrg    return t;
19343f32c10Smrg}
19443f32c10Smrg#else
19543f32c10Smrg#error no mktime_gmt implementation on this platform
19643f32c10Smrg#endif
19743f32c10Smrg
19843f32c10Smrg/* Return the current time as a signed 64-bit number of seconds since
19943f32c10Smrg   midnight, 1 January 1904.  This is apparently when the Macintosh
20043f32c10Smrg   was designed. */
20143f32c10Smrgint
20243f32c10SmrgmacTime(int *hi, unsigned *lo)
20343f32c10Smrg{
20443f32c10Smrg    unsigned long diff;		/* Not time_t */
20543f32c10Smrg    time_t macEpoch, current;
20643f32c10Smrg    struct tm tm;
20743f32c10Smrg    tm.tm_sec = 0;
20843f32c10Smrg    tm.tm_min = 0;
20943f32c10Smrg    tm.tm_hour = 0;
21043f32c10Smrg    tm.tm_mday = 1;
21143f32c10Smrg    tm.tm_mon = 1;
21243f32c10Smrg    tm.tm_year = 4;
21343f32c10Smrg    tm.tm_isdst = -1;
21443f32c10Smrg
21543f32c10Smrg    macEpoch = mktime_gmt(&tm);
216c813b494Smrg    if(macEpoch == -1) return -1;
21743f32c10Smrg
21843f32c10Smrg    current = time(NULL);
219c813b494Smrg    if(current == -1)
22043f32c10Smrg        return -1;
22143f32c10Smrg
22243f32c10Smrg    if(current < macEpoch) {
22343f32c10Smrg        errno = EINVAL;
22443f32c10Smrg        return -1;
22543f32c10Smrg    }
22643f32c10Smrg
22743f32c10Smrg    diff = current - macEpoch;
22843f32c10Smrg#if INT_MAX == LONG_MAX
22943f32c10Smrg    *hi = 0;
23043f32c10Smrg#else
23143f32c10Smrg    *hi = diff >> 32;
23243f32c10Smrg#endif
23343f32c10Smrg    *lo = diff & 0xFFFFFFFF;
23443f32c10Smrg    return 0;
23543f32c10Smrg}
23643f32c10Smrg
23743f32c10Smrgunsigned
23843f32c10SmrgfaceFoundry(FT_Face face)
23943f32c10Smrg{
24043f32c10Smrg    int rc;
24143f32c10Smrg    BDF_PropertyRec prop;
24243f32c10Smrg
24343f32c10Smrg    rc = FT_Get_BDF_Property(face, "FOUNDRY", &prop);
244ea148d1dSmrg    if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM && prop.u.atom) {
24543f32c10Smrg        if(strcasecmp(prop.u.atom, "adobe") == 0)
24643f32c10Smrg            return makeName("ADBE");
24743f32c10Smrg        else if(strcasecmp(prop.u.atom, "agfa") == 0)
24843f32c10Smrg            return makeName("AGFA");
24943f32c10Smrg        else if(strcasecmp(prop.u.atom, "altsys") == 0)
25043f32c10Smrg            return makeName("ALTS");
25143f32c10Smrg        else if(strcasecmp(prop.u.atom, "apple") == 0)
25243f32c10Smrg            return makeName("APPL");
25343f32c10Smrg        else if(strcasecmp(prop.u.atom, "arphic") == 0)
25443f32c10Smrg            return makeName("ARPH");
25543f32c10Smrg        else if(strcasecmp(prop.u.atom, "alltype") == 0)
25643f32c10Smrg            return makeName("ATEC");
25743f32c10Smrg        else if(strcasecmp(prop.u.atom, "b&h") == 0)
25843f32c10Smrg            return makeName("B&H ");
25943f32c10Smrg        else if(strcasecmp(prop.u.atom, "bitstream") == 0)
26043f32c10Smrg            return makeName("BITS");
26143f32c10Smrg        else if(strcasecmp(prop.u.atom, "dynalab") == 0)
26243f32c10Smrg            return makeName("DYNA");
26343f32c10Smrg        else if(strcasecmp(prop.u.atom, "ibm") == 0)
26443f32c10Smrg            return makeName("IBM ");
26543f32c10Smrg        else if(strcasecmp(prop.u.atom, "itc") == 0)
26643f32c10Smrg            return makeName("ITC ");
26743f32c10Smrg        else if(strcasecmp(prop.u.atom, "interleaf") == 0)
26843f32c10Smrg            return makeName("LEAF");
26943f32c10Smrg        else if(strcasecmp(prop.u.atom, "impress") == 0)
27043f32c10Smrg            return makeName("IMPR");
27143f32c10Smrg        else if(strcasecmp(prop.u.atom, "larabiefonts") == 0)
27243f32c10Smrg            return makeName("LARA");
27343f32c10Smrg        else if(strcasecmp(prop.u.atom, "linotype") == 0)
27443f32c10Smrg            return makeName("LINO");
27543f32c10Smrg        else if(strcasecmp(prop.u.atom, "monotype") == 0)
27643f32c10Smrg            return makeName("MT  ");
27743f32c10Smrg        else if(strcasecmp(prop.u.atom, "microsoft") == 0)
27843f32c10Smrg            return makeName("MS  ");
27943f32c10Smrg        else if(strcasecmp(prop.u.atom, "urw") == 0)
28043f32c10Smrg            return makeName("URW ");
28143f32c10Smrg        else if(strcasecmp(prop.u.atom, "y&y") == 0)
28243f32c10Smrg            return makeName("Y&Y ");
283c813b494Smrg        else {
284c813b494Smrg	    char buf[5];
285c813b494Smrg	    snprintf(buf, sizeof(buf), "%-4s", prop.u.atom);
286c813b494Smrg            return makeName(buf);
287c813b494Smrg	}
28843f32c10Smrg    }
28943f32c10Smrg    /* For now */
29043f32c10Smrg    return makeName("UNKN");
29143f32c10Smrg}
292ea148d1dSmrg
29343f32c10Smrg
29443f32c10Smrgint
29543f32c10SmrgfaceWeight(FT_Face face)
29643f32c10Smrg{
29743f32c10Smrg    int rc;
29843f32c10Smrg    BDF_PropertyRec prop;
29943f32c10Smrg    rc = FT_Get_BDF_Property(face, "WEIGHT_NAME", &prop);
300ea148d1dSmrg    if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM && prop.u.atom) {
30143f32c10Smrg        if(strcasecmp(prop.u.atom, "thin") == 0)
30243f32c10Smrg            return 100;
30343f32c10Smrg        else if(strcasecmp(prop.u.atom, "extralight") == 0)
30443f32c10Smrg            return 200;
30543f32c10Smrg        else if(strcasecmp(prop.u.atom, "light") == 0)
30643f32c10Smrg            return 300;
30743f32c10Smrg        else if(strcasecmp(prop.u.atom, "medium") == 0)
308c813b494Smrg            return 400;
30943f32c10Smrg        else if(strcasecmp(prop.u.atom, "semibold") == 0)
31043f32c10Smrg            return 600;
31143f32c10Smrg        else if(strcasecmp(prop.u.atom, "bold") == 0)
31243f32c10Smrg            return 700;
31343f32c10Smrg        else if(strcasecmp(prop.u.atom, "extrabold") == 0)
31443f32c10Smrg            return 800;
31543f32c10Smrg        else if(strcasecmp(prop.u.atom, "black") == 0)
31643f32c10Smrg            return 900;
31743f32c10Smrg        else
318c813b494Smrg            return 400;
31943f32c10Smrg    } else
320c813b494Smrg        return 400;             /* for now */
32143f32c10Smrg}
32243f32c10Smrg
32343f32c10Smrgint
32443f32c10SmrgfaceWidth(FT_Face face)
32543f32c10Smrg{
32643f32c10Smrg    int rc;
32743f32c10Smrg    BDF_PropertyRec prop;
32843f32c10Smrg    rc = FT_Get_BDF_Property(face, "SETWIDTH_NAME", &prop);
329ea148d1dSmrg    if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM && prop.u.atom) {
33043f32c10Smrg        if(strcasecmp(prop.u.atom, "ultracondensed") == 0)
33143f32c10Smrg            return 1;
33243f32c10Smrg        else if(strcasecmp(prop.u.atom, "extracondensed") == 0)
33343f32c10Smrg            return 2;
33443f32c10Smrg        else if(strcasecmp(prop.u.atom, "condensed") == 0)
33543f32c10Smrg            return 3;
33643f32c10Smrg        else if(strcasecmp(prop.u.atom, "semicondensed") == 0)
33743f32c10Smrg            return 4;
33843f32c10Smrg        else if(strcasecmp(prop.u.atom, "normal") == 0)
33943f32c10Smrg            return 5;
34043f32c10Smrg        else if(strcasecmp(prop.u.atom, "semiexpanded") == 0)
34143f32c10Smrg            return 6;
34243f32c10Smrg        else if(strcasecmp(prop.u.atom, "expanded") == 0)
34343f32c10Smrg            return 7;
34443f32c10Smrg        else if(strcasecmp(prop.u.atom, "extraexpanded") == 0)
34543f32c10Smrg            return 8;
34643f32c10Smrg        else if(strcasecmp(prop.u.atom, "ultraexpanded") == 0)
34743f32c10Smrg            return 9;
34843f32c10Smrg        else
34943f32c10Smrg            return 5;
35043f32c10Smrg    } else
35143f32c10Smrg        return 5;               /* for now */
35243f32c10Smrg}
35343f32c10Smrg
35443f32c10Smrgint
35543f32c10SmrgfaceItalicAngle(FT_Face face)
35643f32c10Smrg{
35743f32c10Smrg    int rc;
35843f32c10Smrg    BDF_PropertyRec prop;
35943f32c10Smrg
36043f32c10Smrg    rc = FT_Get_BDF_Property(face, "ITALIC_ANGLE", &prop);
36143f32c10Smrg    if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_INTEGER) {
36243f32c10Smrg        return (prop.u.integer - 64 * 90) * (TWO_SIXTEENTH / 64);
36343f32c10Smrg    }
36443f32c10Smrg
36543f32c10Smrg    rc = FT_Get_BDF_Property(face, "SLANT", &prop);
366ea148d1dSmrg    if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM && prop.u.atom) {
36743f32c10Smrg        if(strcasecmp(prop.u.atom, "i") == 0 ||
36843f32c10Smrg           strcasecmp(prop.u.atom, "s") == 0)
36943f32c10Smrg            return -30 * TWO_SIXTEENTH;
37043f32c10Smrg        else
37143f32c10Smrg            return 0;
37243f32c10Smrg    } else
37343f32c10Smrg        return 0;               /* for now */
37443f32c10Smrg}
37543f32c10Smrg
37643f32c10Smrgint
37743f32c10SmrgfaceFlags(FT_Face face)
37843f32c10Smrg{
37943f32c10Smrg    int flags = 0;
38043f32c10Smrg    BDF_PropertyRec prop;
38143f32c10Smrg    int rc;
38243f32c10Smrg
38343f32c10Smrg    if(faceWeight(face) >= 650)
38443f32c10Smrg        flags |= FACE_BOLD;
38543f32c10Smrg    rc = FT_Get_BDF_Property(face, "SLANT", &prop);
386ea148d1dSmrg    if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM && prop.u.atom) {
38743f32c10Smrg        if(strcasecmp(prop.u.atom, "i") == 0 ||
38843f32c10Smrg           strcasecmp(prop.u.atom, "s") == 0)
38943f32c10Smrg            flags |= FACE_ITALIC;
39043f32c10Smrg    }
39143f32c10Smrg    return flags;
39243f32c10Smrg}
39343f32c10Smrg
394c813b494Smrgint
395c813b494SmrgfaceIntProp(FT_Face face, const char *name)
396c813b494Smrg{
397c813b494Smrg    int rc;
398c813b494Smrg    BDF_PropertyRec prop;
399c813b494Smrg
400c813b494Smrg    rc = FT_Get_BDF_Property(face, name, &prop);
401c813b494Smrg    if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_INTEGER)
402c813b494Smrg	return prop.u.integer;
403c813b494Smrg    else
404c813b494Smrg	return UNDEF;
405c813b494Smrg}
406c813b494Smrg
407d2f28e1bSmrgchar *
408d2f28e1bSmrgfaceStringProp(FT_Face face, const char *name)
409d2f28e1bSmrg{
410d2f28e1bSmrg    int rc;
411d2f28e1bSmrg    BDF_PropertyRec prop;
412d2f28e1bSmrg    char *buf = NULL;
413d2f28e1bSmrg
414d2f28e1bSmrg    rc = FT_Get_BDF_Property(face, name, &prop);
415d2f28e1bSmrg    if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM) {
416d2f28e1bSmrg	buf = sprintf_alloc("%s", prop.u.atom ? prop.u.atom : "");
417d2f28e1bSmrg	if(buf == NULL) {
418d2f28e1bSmrg	    perror("sprintf_alloc failed");
419d2f28e1bSmrg	    exit(1);
420d2f28e1bSmrg	}
421d2f28e1bSmrg    }
422d2f28e1bSmrg    return buf;
423d2f28e1bSmrg}
424d2f28e1bSmrg
42543f32c10Smrgchar *
42643f32c10SmrgfaceEncoding(FT_Face face)
42743f32c10Smrg{
42843f32c10Smrg    BDF_PropertyRec p1, p2;
42943f32c10Smrg    int rc;
43043f32c10Smrg
43143f32c10Smrg    rc = FT_Get_BDF_Property(face, "CHARSET_REGISTRY", &p1);
43243f32c10Smrg    if(rc != 0 || p1.type != BDF_PROPERTY_TYPE_ATOM)
43343f32c10Smrg        return NULL;
43443f32c10Smrg    rc = FT_Get_BDF_Property(face, "CHARSET_ENCODING", &p2);
43543f32c10Smrg    if(rc != 0 || p2.type != BDF_PROPERTY_TYPE_ATOM)
43643f32c10Smrg        return NULL;
437ea148d1dSmrg    if(!(p1.u.atom && p2.u.atom))
438ea148d1dSmrg        return NULL;
43943f32c10Smrg
44043f32c10Smrg    return sprintf_alloc("%s-%s", p1.u.atom, p2.u.atom);
44143f32c10Smrg}
442ea148d1dSmrg
44343f32c10Smrgint
44443f32c10SmrgdegreesToFraction(int deg, int *num, int *den)
44543f32c10Smrg{
44643f32c10Smrg    double n, d;
44743f32c10Smrg    double rad, val;
44843f32c10Smrg    int i;
44943f32c10Smrg
45043f32c10Smrg    if(deg <= -(60 * TWO_SIXTEENTH) || deg >= (60 * TWO_SIXTEENTH))
45143f32c10Smrg        goto fail;
45243f32c10Smrg
45343f32c10Smrg    rad = (((double)deg) / TWO_SIXTEENTH) / 180.0 * M_PI;
45443f32c10Smrg
45543f32c10Smrg    n = sin(-rad);
45643f32c10Smrg    d = cos(rad);
45743f32c10Smrg
45843f32c10Smrg    if(d < 0.001)
45943f32c10Smrg        goto fail;
46043f32c10Smrg
46143f32c10Smrg    val = atan2(n, d);
46243f32c10Smrg    /* There must be a cleaner way */
46343f32c10Smrg    for(i = 1; i < 10000; i++) {
46443f32c10Smrg        if((int)(d * i) != 0.0 &&
46543f32c10Smrg           fabs(atan2(ROUND(n * i), ROUND(d * i)) - val) < 0.05) {
46643f32c10Smrg            *num = (int)ROUND(n * i);
46743f32c10Smrg            *den = (int)ROUND(d * i);
46843f32c10Smrg            return 0;
46943f32c10Smrg        }
47043f32c10Smrg    }
47143f32c10Smrg
47243f32c10Smrg fail:
47343f32c10Smrg    *den = 1;
47443f32c10Smrg    *num = 0;
47543f32c10Smrg    return -1;
47643f32c10Smrg}
47743f32c10Smrg
478