/* $XConsortium: cards.c /main/9 1996/10/19 18:15:32 kaleb $ */





/* $XFree86: xc/programs/Xserver/hw/xfree86/xf86config/cards.c,v 3.11 1996/12/23 07:04:43 dawes Exp $ */

/*
 *  Functions to manipulate card database.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "Card.h"

extern char *string(char **);
extern char *value(char *);

/*
 * Database format:
 *
 * NAME <name of card>
 * CHIPSET <chipset description>
 * SERVER <server name>
 *
 * Optional keywords:
 * RAMDAC <ramdac identifier>
 * CLOCKCHIP <clockchip identifier>
 * DACSPEED <dacspeed>
 * NOCLOCKPROBE
 * UNSUPPORTED
 * RAMPROBE
 * NEEDCHIPSET
 *
 * SEE <name of card> refers to another card definition; parameters that
 * are already defined are not overridden.
 *
 * <server name> is one of Mono, VGA16, SVGA, S3, Mach32, Mach8, 8514,
 * P9000, AGX, W32.
 *
 * A useful additional keywords may be CLOCKS.
 */



/* Database vars. */

int nrcards;
Card *card;

static void
strappend(char **destp, char *src)
{

    if (src) {
	if (*destp) {
	    *destp = (char*)realloc(*destp,  strlen(*destp) + strlen(src) + 1);
	    strcat(*destp, src);
	}
	else
	    *destp = strdup(src);
    }
}

int
lookupcard(char *name)
{
    int i;
    for (i = 0; i < nrcards; i++) {
	if (card[i].custom())
	    continue;
	if (strcmp(name, card[i].name) == 0)
	    return i;
    }
    return -1;
}

static int
compare(const void * v1, const void * v2)
{
    Card *c1 = (Card*)v1;
    Card *c2 = (Card*)v2;
    int rc = strcasecmp(c1->vendor(), c2->vendor());

    return (rc == 0) ? strcasecmp(c1->product(), c2->product()) : rc;
}

static char *s3_comment =
"# Use Option \"nolinear\" if the server doesn't start up correctly\n"
"# (this avoids the linear framebuffer probe). If that fails try\n"
"# option \"nomemaccess\".\n"
"#\n"
"# Refer to /usr/X11R6/lib/doc/README.S3, and the XF86_S3 man page.\n";

static char *cirrus_comment =
"# Use Option \"no_bitblt\" if you have graphics problems. If that fails\n"
"# try Option \"noaccel\".\n"
"# Refer to /usr/X11R6/lib/doc/README.cirrus.\n"
"# To allow linear addressing, uncomment the Option line and the\n"
"# address that the card maps the framebuffer to.\n";

#ifdef __EMX__
# define FN(x)	(char*) __XOS2RedirRoot(x)
#else
# define FN(x)	x
#endif

static char *company_names[] = {
    "chips & technologies",
    "creative labs",
    "cirrus logic",
    "number nine",
    "ark logic",
    "avance logic",
    "binar graphics",
    "california graphics",
    "2 the max",
};
#define NR_COMPANY_NAMES	(sizeof company_names / sizeof company_names[0])
#define COMPARE_TO(s,x)	strncasecmp(s,company_names[x],strlen(company_names[x]))

typedef int (fvv)(const void*, const void*);

int
Card::parse_database(messagefunc message)
{
    FILE *f;
    int cardalloc;

    if ( (f = fopen(FN(CARD_DATABASE_FILE), "r")) == 0)
	return 0;

    cardalloc = 100;
    card = (Card*)calloc(cardalloc, sizeof card[0]);

    nrcards = 0;

    do {
	if (card[nrcards].scan(f)) {
	    if (++nrcards >= cardalloc) {
		cardalloc += 100;
		card = (Card*)realloc(card, cardalloc*sizeof card[0]);
	    }
	}
    } while (!feof(f));
    fclose(f);

    /* Add general comments and build _vendor and _product fields */
    for (int i = 0; i < nrcards; i++) {
	int len, x;
	char *p;

	if (card[i].custom())
	    continue;

	if (card[i].server && strcmp(card[i].server, "S3") == 0)
	    strappend(&card[i].lines, s3_comment);
	if (card[i]._chipset && strncmp(card[i]._chipset, "CL-GD", 5) == 0)
	    strappend(&card[i].lines, cirrus_comment);

	if ( (p=strstr(card[i].name, "(generic)")) != 0) {
	    card[i]._vendor = "(choose chipset)";
	    *p = 0;
	    card[i]._product = card[i].name;
	}
	else {
	    for (len = x = 0; x < NR_COMPANY_NAMES; x++)
		if (COMPARE_TO(card[i].name, x) == 0) {
		    len = strlen(company_names[x]);
		    p = card[i].name + len;
		    break;
		}
	    if (len == 0) {
		/* Use the first word as the index.
		 */
		for (p = card[i].name; *p && !isspace(*p); p++)
		    ;
		len = p - card[i].name;
	    }
	    while (isspace(*p))
		++p;

	    if (*p) {
		card[i]._vendor = (char*)malloc(len+1);
		strncpy(card[i]._vendor, card[i].name, len);
		card[i]._vendor[len] = 0;
		card[i]._product = p;
	    }
	    else {
		card[i]._vendor = "(choose chipset)";
		card[i]._product = card[i].name;
	    }
	}
    }

    qsort(&card[1], nrcards-1, sizeof card[1], compare);
    return 0;
}

#define same(a,b)	(strcasecmp(a,b) == 0)
#define prefix(a,b)	(strncasecmp(a, b, sizeof(b)-1) == 0)

int
Card::scan(FILE *f)
{
    char buf[512];
    char *p, *q;
    int ok = 0;
    int first=1;

    Erase();
    while (fgets(buf, sizeof buf, f)) {
	for (p = buf; isspace(*p); ++p)
	    ;

	if (*p == 0) {	/* blank line terminates entry */
	    ok = !first;
	    break;
	}

	if (*p == '#')
	    continue;

	if (first && !prefix(p, "NAME"))
	    return 0;

	first = 0;

	if (prefix(p, "LINE")) {
	    strappend(&lines, p+5);
	    continue;
	}

	if ( (q = value(p)) == 0 || *q == 0)
	    continue;

	if (prefix(p, "NAME")) {
	    ok = 1;
	    name = strdup(q);
	}
	else if (prefix(p, "SEE")) {
	    int idx;

	    if ( (idx = lookupcard(q)) == 0) {
		ok = 0;
		break;
	    }
	    if (_usechip == NULL)
		_usechip = card[idx]._usechip;
	    if (_chipset == NULL)
		_chipset = card[idx]._chipset;
	    if (server == NULL)
		server = card[idx].server;
	    if (ramdac == NULL)
		ramdac = card[idx].ramdac;
	    if (clockchip == NULL)
		clockchip = card[idx].clockchip;
	    if (dacspeed == NULL)
		dacspeed = card[idx].dacspeed;
	    flags |= card[idx].flags;
	    strappend(&lines, card[idx].lines);
	    return 1;
	}
	else if (prefix(p, "USECHIP"))
	    /* The chipset that the server wants to use (grrr!) */
	    _usechip = strdup(q);
	else if (prefix(p, "CHIPSET"))
	    _chipset = strdup(q);
	else if (prefix(p, "SERVER"))
	    /* Server identifier. */
	    server = strdup(q);
	else if (prefix(buf, "RAMDAC"))
	    /* Ramdac indentifier. */
	    ramdac = strdup(q);
	else if (prefix(buf, "CLOCKCHIP") ) {
	    /* Clockchip indentifier. */
	    clockchip = strdup(q);
	    flags |= NOCLOCKPROBE;
	}
	else if (prefix(buf, "DACSPEED") )
	    /* Clockchip indentifier. */
	    dacspeed = strdup(q);
	else if (prefix(buf, "NOCLOCKPROBE") )
	    flags |= NOCLOCKPROBE;
	else if (prefix(buf, "UNSUPPORTED") )
	    flags |= UNSUPPORTED;
	else if (prefix(buf, "RAMPROBE") )
	    flags |= RAMPROBE;
	else if (prefix(buf, "NEEDCHIPSET") )
	    flags |= NEED_CHIPSET;
    }
#define FREE(x)	if (x) free(x)
    if (!ok) {
	FREE(name);
	FREE(_usechip);
	FREE(_chipset);
	FREE(server);
	FREE(ramdac);
	FREE(dacspeed);
	FREE(lines);
	return 0;
    }
    if (server == 0) server = "unknown";
    if (_chipset == 0) _chipset = "unknown";
    return 1;
} /* Card::scan() */
