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>
29d3173433Smrg#include <math.h>
3043f32c10Smrg#include <stdarg.h>
3143f32c10Smrg
3243f32c10Smrg#include <ft2build.h>
3343f32c10Smrg#include FT_FREETYPE_H
3443f32c10Smrg#include FT_BDF_H
3543f32c10Smrg#include "X11/Xos.h"
36ea148d1dSmrg#include "X11/Xfuncproto.h" /* for _X_ATTRIBUTE_PRINTF */
3743f32c10Smrg#include "fonttosfnt.h"
3843f32c10Smrg
3943f32c10Smrg#ifdef __GLIBC__
4043f32c10Smrg#define HAVE_TIMEGM
4143f32c10Smrg#define HAVE_TM_GMTOFF
4243f32c10Smrg#endif
4343f32c10Smrg
4443f32c10Smrg#ifdef BSD
4543f32c10Smrg#define HAVE_TM_GMTOFF
4643f32c10Smrg#define GMTOFFMEMBER tm_gmtoff
4743f32c10Smrg#endif
4843f32c10Smrg
4943f32c10Smrg/* That's in POSIX */
5043f32c10Smrg#define HAVE_TZSET
5143f32c10Smrg
5243f32c10Smrg#ifdef NEED_SETENV
5343f32c10Smrgextern int setenv(const char *name, const char *value, int overwrite);
5443f32c10Smrgextern void unsetenv(const char *name);
5543f32c10Smrg#endif
5643f32c10Smrg
5743f32c10Smrgchar*
586ef05171Smrgsprintf_alloc(const char *f, ...)
5943f32c10Smrg{
6043f32c10Smrg    char *s;
6143f32c10Smrg    va_list args;
6243f32c10Smrg    va_start(args, f);
6343f32c10Smrg    s = vsprintf_alloc(f, args);
6443f32c10Smrg    va_end(args);
6543f32c10Smrg    return s;
6643f32c10Smrg}
6743f32c10Smrg
6843f32c10Smrg#if HAVE_VASPRINTF
69ea148d1dSmrg_X_ATTRIBUTE_PRINTF(1, 0)
7043f32c10Smrgchar*
716ef05171Smrgvsprintf_alloc(const char *f, va_list args)
7243f32c10Smrg{
7343f32c10Smrg    char *r;
7443f32c10Smrg    int rc;
7543f32c10Smrg
7643f32c10Smrg    rc = vasprintf(&r, f, args);
7743f32c10Smrg    if(rc < 0)
7843f32c10Smrg        return NULL;
7943f32c10Smrg    return r;
8043f32c10Smrg}
8143f32c10Smrg#else
82ea148d1dSmrg_X_ATTRIBUTE_PRINTF(1, 0)
8343f32c10Smrgchar*
846ef05171Smrgvsprintf_alloc(const char *f, va_list args)
8543f32c10Smrg{
8643f32c10Smrg    int n, size = 12;
8743f32c10Smrg    char *string;
88fbfaf8f3Smrg#if HAVE_DECL_VA_COPY
8943f32c10Smrg    va_list args_copy;
90fbfaf8f3Smrg#endif
9143f32c10Smrg
9243f32c10Smrg    while(1) {
9343f32c10Smrg        if(size > 4096)
9443f32c10Smrg            return NULL;
9543f32c10Smrg        string = malloc(size);
9643f32c10Smrg        if(!string)
9743f32c10Smrg            return NULL;
9843f32c10Smrg
9943f32c10Smrg#if HAVE_DECL_VA_COPY
10043f32c10Smrg        va_copy(args_copy, args);
10143f32c10Smrg        n = vsnprintf(string, size, f, args_copy);
10243f32c10Smrg#else
10343f32c10Smrg        n = vsnprintf(string, size, f, args);
10443f32c10Smrg#endif
10543f32c10Smrg        if(n >= 0 && n < size)
10643f32c10Smrg            return string;
10743f32c10Smrg        else if(n >= size)
10843f32c10Smrg            size = n + 1;
10943f32c10Smrg        else
11043f32c10Smrg            size = size * 3 / 2 + 1;
11143f32c10Smrg        free(string);
11243f32c10Smrg    }
11343f32c10Smrg    /* NOTREACHED */
11443f32c10Smrg}
11543f32c10Smrg#endif
11643f32c10Smrg
117ea148d1dSmrg/* Build a UTF-16 string from a Latin-1 string.
11843f32c10Smrg   Result is not NUL-terminated. */
11943f32c10Smrgchar *
1206ef05171SmrgmakeUTF16(const char *string)
12143f32c10Smrg{
12243f32c10Smrg    int n = strlen(string);
12343f32c10Smrg    char *value = malloc(2 * n);
12443f32c10Smrg    if(!value)
12543f32c10Smrg        return NULL;
126fbfaf8f3Smrg    for(int i = 0; i < n; i++) {
12743f32c10Smrg        value[2 * i] = '\0';
12843f32c10Smrg        value[2 * i + 1] = string[i];
12943f32c10Smrg    }
13043f32c10Smrg    return value;
13143f32c10Smrg}
13243f32c10Smrg
13343f32c10Smrg/* Like mktime(3), but UTC rather than local time */
13443f32c10Smrg#if defined(HAVE_TIMEGM)
13543f32c10Smrgstatic time_t
13643f32c10Smrgmktime_gmt(struct tm *tm)
13743f32c10Smrg{
13843f32c10Smrg    return timegm(tm);
13943f32c10Smrg}
14043f32c10Smrg#elif defined(HAVE_TM_GMTOFF)
14143f32c10Smrgstatic time_t
14243f32c10Smrgmktime_gmt(struct tm *tm)
14343f32c10Smrg{
14443f32c10Smrg    time_t t;
14543f32c10Smrg    struct tm *ltm;
14643f32c10Smrg
14743f32c10Smrg    t = mktime(tm);
14843f32c10Smrg    if(t < 0)
14943f32c10Smrg        return -1;
15043f32c10Smrg    ltm = localtime(&t);
15143f32c10Smrg    if(ltm == NULL)
15243f32c10Smrg        return -1;
15343f32c10Smrg    return t + ltm->GMTOFFMEMBER;
15443f32c10Smrg}
15543f32c10Smrg#elif defined(HAVE_TZSET)
15643f32c10Smrg/* Taken from the Linux timegm(3) man page */
15743f32c10Smrgstatic time_t
15843f32c10Smrgmktime_gmt(struct tm *tm)
15943f32c10Smrg{
16043f32c10Smrg    time_t t;
16143f32c10Smrg    char *tz;
16243f32c10Smrg
16343f32c10Smrg    tz = getenv("TZ");
16443f32c10Smrg    setenv("TZ", "", 1);
16543f32c10Smrg    tzset();
16643f32c10Smrg    t = mktime(tm);
16743f32c10Smrg    if(tz)
16843f32c10Smrg        setenv("TZ", tz, 1);
16943f32c10Smrg    else
17043f32c10Smrg        unsetenv("TZ");
17143f32c10Smrg    tzset();
17243f32c10Smrg    return t;
17343f32c10Smrg}
17443f32c10Smrg#else
17543f32c10Smrg#error no mktime_gmt implementation on this platform
17643f32c10Smrg#endif
17743f32c10Smrg
17843f32c10Smrg/* Return the current time as a signed 64-bit number of seconds since
17943f32c10Smrg   midnight, 1 January 1904.  This is apparently when the Macintosh
18043f32c10Smrg   was designed. */
18143f32c10Smrgint
18243f32c10SmrgmacTime(int *hi, unsigned *lo)
18343f32c10Smrg{
184d3173433Smrg    unsigned long long diff;		/* Not time_t */
185d3173433Smrg    char *source_date_epoch;
18643f32c10Smrg    time_t macEpoch, current;
18743f32c10Smrg    struct tm tm;
18843f32c10Smrg    tm.tm_sec = 0;
18943f32c10Smrg    tm.tm_min = 0;
19043f32c10Smrg    tm.tm_hour = 0;
19143f32c10Smrg    tm.tm_mday = 1;
19243f32c10Smrg    tm.tm_mon = 1;
19343f32c10Smrg    tm.tm_year = 4;
19443f32c10Smrg    tm.tm_isdst = -1;
19543f32c10Smrg
19643f32c10Smrg    macEpoch = mktime_gmt(&tm);
197c813b494Smrg    if(macEpoch == -1) return -1;
19843f32c10Smrg
199d3173433Smrg    /* This assumes that the SOURCE_DATE_EPOCH environment variable will contain
200d3173433Smrg       a correct, positive integer in the time_t range */
201d3173433Smrg    if ((source_date_epoch = getenv("SOURCE_DATE_EPOCH")) == NULL ||
202d3173433Smrg        (current = (time_t)strtoll(source_date_epoch, NULL, 10)) <= 0)
203d3173433Smrg            current = time(NULL);
204c813b494Smrg    if(current == -1)
20543f32c10Smrg        return -1;
20643f32c10Smrg
20743f32c10Smrg    if(current < macEpoch) {
20843f32c10Smrg        errno = EINVAL;
20943f32c10Smrg        return -1;
21043f32c10Smrg    }
21143f32c10Smrg
21243f32c10Smrg    diff = current - macEpoch;
21343f32c10Smrg#if INT_MAX == LONG_MAX
21443f32c10Smrg    *hi = 0;
21543f32c10Smrg#else
21643f32c10Smrg    *hi = diff >> 32;
21743f32c10Smrg#endif
21843f32c10Smrg    *lo = diff & 0xFFFFFFFF;
21943f32c10Smrg    return 0;
22043f32c10Smrg}
22143f32c10Smrg
22243f32c10Smrgunsigned
22343f32c10SmrgfaceFoundry(FT_Face face)
22443f32c10Smrg{
22543f32c10Smrg    int rc;
22643f32c10Smrg    BDF_PropertyRec prop;
22743f32c10Smrg
22843f32c10Smrg    rc = FT_Get_BDF_Property(face, "FOUNDRY", &prop);
229ea148d1dSmrg    if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM && prop.u.atom) {
23043f32c10Smrg        if(strcasecmp(prop.u.atom, "adobe") == 0)
23143f32c10Smrg            return makeName("ADBE");
23243f32c10Smrg        else if(strcasecmp(prop.u.atom, "agfa") == 0)
23343f32c10Smrg            return makeName("AGFA");
23443f32c10Smrg        else if(strcasecmp(prop.u.atom, "altsys") == 0)
23543f32c10Smrg            return makeName("ALTS");
23643f32c10Smrg        else if(strcasecmp(prop.u.atom, "apple") == 0)
23743f32c10Smrg            return makeName("APPL");
23843f32c10Smrg        else if(strcasecmp(prop.u.atom, "arphic") == 0)
23943f32c10Smrg            return makeName("ARPH");
24043f32c10Smrg        else if(strcasecmp(prop.u.atom, "alltype") == 0)
24143f32c10Smrg            return makeName("ATEC");
24243f32c10Smrg        else if(strcasecmp(prop.u.atom, "b&h") == 0)
24343f32c10Smrg            return makeName("B&H ");
24443f32c10Smrg        else if(strcasecmp(prop.u.atom, "bitstream") == 0)
24543f32c10Smrg            return makeName("BITS");
24643f32c10Smrg        else if(strcasecmp(prop.u.atom, "dynalab") == 0)
24743f32c10Smrg            return makeName("DYNA");
24843f32c10Smrg        else if(strcasecmp(prop.u.atom, "ibm") == 0)
24943f32c10Smrg            return makeName("IBM ");
25043f32c10Smrg        else if(strcasecmp(prop.u.atom, "itc") == 0)
25143f32c10Smrg            return makeName("ITC ");
25243f32c10Smrg        else if(strcasecmp(prop.u.atom, "interleaf") == 0)
25343f32c10Smrg            return makeName("LEAF");
25443f32c10Smrg        else if(strcasecmp(prop.u.atom, "impress") == 0)
25543f32c10Smrg            return makeName("IMPR");
25643f32c10Smrg        else if(strcasecmp(prop.u.atom, "larabiefonts") == 0)
25743f32c10Smrg            return makeName("LARA");
25843f32c10Smrg        else if(strcasecmp(prop.u.atom, "linotype") == 0)
25943f32c10Smrg            return makeName("LINO");
26043f32c10Smrg        else if(strcasecmp(prop.u.atom, "monotype") == 0)
26143f32c10Smrg            return makeName("MT  ");
26243f32c10Smrg        else if(strcasecmp(prop.u.atom, "microsoft") == 0)
26343f32c10Smrg            return makeName("MS  ");
26443f32c10Smrg        else if(strcasecmp(prop.u.atom, "urw") == 0)
26543f32c10Smrg            return makeName("URW ");
26643f32c10Smrg        else if(strcasecmp(prop.u.atom, "y&y") == 0)
26743f32c10Smrg            return makeName("Y&Y ");
268c813b494Smrg        else {
269c813b494Smrg	    char buf[5];
270c813b494Smrg	    snprintf(buf, sizeof(buf), "%-4s", prop.u.atom);
271c813b494Smrg            return makeName(buf);
272c813b494Smrg	}
27343f32c10Smrg    }
27443f32c10Smrg    /* For now */
27543f32c10Smrg    return makeName("UNKN");
27643f32c10Smrg}
277ea148d1dSmrg
27843f32c10Smrg
27943f32c10Smrgint
28043f32c10SmrgfaceWeight(FT_Face face)
28143f32c10Smrg{
28243f32c10Smrg    int rc;
28343f32c10Smrg    BDF_PropertyRec prop;
28443f32c10Smrg    rc = FT_Get_BDF_Property(face, "WEIGHT_NAME", &prop);
285ea148d1dSmrg    if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM && prop.u.atom) {
28643f32c10Smrg        if(strcasecmp(prop.u.atom, "thin") == 0)
28743f32c10Smrg            return 100;
28843f32c10Smrg        else if(strcasecmp(prop.u.atom, "extralight") == 0)
28943f32c10Smrg            return 200;
29043f32c10Smrg        else if(strcasecmp(prop.u.atom, "light") == 0)
29143f32c10Smrg            return 300;
29243f32c10Smrg        else if(strcasecmp(prop.u.atom, "medium") == 0)
293c813b494Smrg            return 400;
29443f32c10Smrg        else if(strcasecmp(prop.u.atom, "semibold") == 0)
29543f32c10Smrg            return 600;
29643f32c10Smrg        else if(strcasecmp(prop.u.atom, "bold") == 0)
29743f32c10Smrg            return 700;
29843f32c10Smrg        else if(strcasecmp(prop.u.atom, "extrabold") == 0)
29943f32c10Smrg            return 800;
30043f32c10Smrg        else if(strcasecmp(prop.u.atom, "black") == 0)
30143f32c10Smrg            return 900;
30243f32c10Smrg        else
303c813b494Smrg            return 400;
30443f32c10Smrg    } else
305c813b494Smrg        return 400;             /* for now */
30643f32c10Smrg}
30743f32c10Smrg
30843f32c10Smrgint
30943f32c10SmrgfaceWidth(FT_Face face)
31043f32c10Smrg{
31143f32c10Smrg    int rc;
31243f32c10Smrg    BDF_PropertyRec prop;
31343f32c10Smrg    rc = FT_Get_BDF_Property(face, "SETWIDTH_NAME", &prop);
314ea148d1dSmrg    if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM && prop.u.atom) {
31543f32c10Smrg        if(strcasecmp(prop.u.atom, "ultracondensed") == 0)
31643f32c10Smrg            return 1;
31743f32c10Smrg        else if(strcasecmp(prop.u.atom, "extracondensed") == 0)
31843f32c10Smrg            return 2;
31943f32c10Smrg        else if(strcasecmp(prop.u.atom, "condensed") == 0)
32043f32c10Smrg            return 3;
32143f32c10Smrg        else if(strcasecmp(prop.u.atom, "semicondensed") == 0)
32243f32c10Smrg            return 4;
32343f32c10Smrg        else if(strcasecmp(prop.u.atom, "normal") == 0)
32443f32c10Smrg            return 5;
32543f32c10Smrg        else if(strcasecmp(prop.u.atom, "semiexpanded") == 0)
32643f32c10Smrg            return 6;
32743f32c10Smrg        else if(strcasecmp(prop.u.atom, "expanded") == 0)
32843f32c10Smrg            return 7;
32943f32c10Smrg        else if(strcasecmp(prop.u.atom, "extraexpanded") == 0)
33043f32c10Smrg            return 8;
33143f32c10Smrg        else if(strcasecmp(prop.u.atom, "ultraexpanded") == 0)
33243f32c10Smrg            return 9;
33343f32c10Smrg        else
33443f32c10Smrg            return 5;
33543f32c10Smrg    } else
33643f32c10Smrg        return 5;               /* for now */
33743f32c10Smrg}
33843f32c10Smrg
33943f32c10Smrgint
34043f32c10SmrgfaceItalicAngle(FT_Face face)
34143f32c10Smrg{
34243f32c10Smrg    int rc;
34343f32c10Smrg    BDF_PropertyRec prop;
34443f32c10Smrg
34543f32c10Smrg    rc = FT_Get_BDF_Property(face, "ITALIC_ANGLE", &prop);
34643f32c10Smrg    if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_INTEGER) {
34743f32c10Smrg        return (prop.u.integer - 64 * 90) * (TWO_SIXTEENTH / 64);
34843f32c10Smrg    }
34943f32c10Smrg
35043f32c10Smrg    rc = FT_Get_BDF_Property(face, "SLANT", &prop);
351ea148d1dSmrg    if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM && prop.u.atom) {
35243f32c10Smrg        if(strcasecmp(prop.u.atom, "i") == 0 ||
35343f32c10Smrg           strcasecmp(prop.u.atom, "s") == 0)
35443f32c10Smrg            return -30 * TWO_SIXTEENTH;
35543f32c10Smrg        else
35643f32c10Smrg            return 0;
35743f32c10Smrg    } else
35843f32c10Smrg        return 0;               /* for now */
35943f32c10Smrg}
36043f32c10Smrg
36143f32c10Smrgint
36243f32c10SmrgfaceFlags(FT_Face face)
36343f32c10Smrg{
36443f32c10Smrg    int flags = 0;
36543f32c10Smrg    BDF_PropertyRec prop;
36643f32c10Smrg    int rc;
36743f32c10Smrg
36843f32c10Smrg    if(faceWeight(face) >= 650)
36943f32c10Smrg        flags |= FACE_BOLD;
37043f32c10Smrg    rc = FT_Get_BDF_Property(face, "SLANT", &prop);
371ea148d1dSmrg    if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM && prop.u.atom) {
37243f32c10Smrg        if(strcasecmp(prop.u.atom, "i") == 0 ||
37343f32c10Smrg           strcasecmp(prop.u.atom, "s") == 0)
37443f32c10Smrg            flags |= FACE_ITALIC;
37543f32c10Smrg    }
37643f32c10Smrg    return flags;
37743f32c10Smrg}
37843f32c10Smrg
379c813b494Smrgint
380c813b494SmrgfaceIntProp(FT_Face face, const char *name)
381c813b494Smrg{
382c813b494Smrg    int rc;
383c813b494Smrg    BDF_PropertyRec prop;
384c813b494Smrg
385c813b494Smrg    rc = FT_Get_BDF_Property(face, name, &prop);
386c813b494Smrg    if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_INTEGER)
387c813b494Smrg	return prop.u.integer;
388c813b494Smrg    else
389c813b494Smrg	return UNDEF;
390c813b494Smrg}
391c813b494Smrg
392d2f28e1bSmrgchar *
393d2f28e1bSmrgfaceStringProp(FT_Face face, const char *name)
394d2f28e1bSmrg{
395d2f28e1bSmrg    int rc;
396d2f28e1bSmrg    BDF_PropertyRec prop;
397d2f28e1bSmrg    char *buf = NULL;
398d2f28e1bSmrg
399d2f28e1bSmrg    rc = FT_Get_BDF_Property(face, name, &prop);
400d2f28e1bSmrg    if(rc == 0 && prop.type == BDF_PROPERTY_TYPE_ATOM) {
401d2f28e1bSmrg	buf = sprintf_alloc("%s", prop.u.atom ? prop.u.atom : "");
402d2f28e1bSmrg	if(buf == NULL) {
403d2f28e1bSmrg	    perror("sprintf_alloc failed");
404d2f28e1bSmrg	    exit(1);
405d2f28e1bSmrg	}
406d2f28e1bSmrg    }
407d2f28e1bSmrg    return buf;
408d2f28e1bSmrg}
409d2f28e1bSmrg
41043f32c10Smrgchar *
41143f32c10SmrgfaceEncoding(FT_Face face)
41243f32c10Smrg{
41343f32c10Smrg    BDF_PropertyRec p1, p2;
41443f32c10Smrg    int rc;
41543f32c10Smrg
41643f32c10Smrg    rc = FT_Get_BDF_Property(face, "CHARSET_REGISTRY", &p1);
41743f32c10Smrg    if(rc != 0 || p1.type != BDF_PROPERTY_TYPE_ATOM)
41843f32c10Smrg        return NULL;
41943f32c10Smrg    rc = FT_Get_BDF_Property(face, "CHARSET_ENCODING", &p2);
42043f32c10Smrg    if(rc != 0 || p2.type != BDF_PROPERTY_TYPE_ATOM)
42143f32c10Smrg        return NULL;
422ea148d1dSmrg    if(!(p1.u.atom && p2.u.atom))
423ea148d1dSmrg        return NULL;
42443f32c10Smrg
42543f32c10Smrg    return sprintf_alloc("%s-%s", p1.u.atom, p2.u.atom);
42643f32c10Smrg}
427ea148d1dSmrg
42843f32c10Smrgint
42943f32c10SmrgdegreesToFraction(int deg, int *num, int *den)
43043f32c10Smrg{
43143f32c10Smrg    double n, d;
43243f32c10Smrg    double rad, val;
43343f32c10Smrg
43443f32c10Smrg    if(deg <= -(60 * TWO_SIXTEENTH) || deg >= (60 * TWO_SIXTEENTH))
43543f32c10Smrg        goto fail;
43643f32c10Smrg
43743f32c10Smrg    rad = (((double)deg) / TWO_SIXTEENTH) / 180.0 * M_PI;
43843f32c10Smrg
43943f32c10Smrg    n = sin(-rad);
44043f32c10Smrg    d = cos(rad);
44143f32c10Smrg
44243f32c10Smrg    if(d < 0.001)
44343f32c10Smrg        goto fail;
44443f32c10Smrg
44543f32c10Smrg    val = atan2(n, d);
44643f32c10Smrg    /* There must be a cleaner way */
447fbfaf8f3Smrg    for(int i = 1; i < 10000; i++) {
44843f32c10Smrg        if((int)(d * i) != 0.0 &&
44943f32c10Smrg           fabs(atan2(ROUND(n * i), ROUND(d * i)) - val) < 0.05) {
45043f32c10Smrg            *num = (int)ROUND(n * i);
45143f32c10Smrg            *den = (int)ROUND(d * i);
45243f32c10Smrg            return 0;
45343f32c10Smrg        }
45443f32c10Smrg    }
45543f32c10Smrg
45643f32c10Smrg fail:
45743f32c10Smrg    *den = 1;
45843f32c10Smrg    *num = 0;
45943f32c10Smrg    return -1;
46043f32c10Smrg}
46143f32c10Smrg
462