/*****************************************************************************/

/*
 *      freqdb.c  --  Station/Frequency "Data Base".
 *
 *      Copyright (C) 2000, 2001
 *        Thomas Sailer (t.sailer@alumni.ethz.ch)
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*****************************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "trxapp.h"

#ifdef WIN32

#include <windows.h>

#else

/* libxml includes */
#include <tree.h>
#include <parser.h>

#endif

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

#include "sysdeps.h"

/* ---------------------------------------------------------------------- */

#define MAXSTATIONS 256

static struct {
	unsigned int nrstn;
	struct stndb_entry stn[MAXSTATIONS];
} stns;

/* ---------------------------------------------------------------------- */

#define DIGI(rx,tx,nm,md,br) { nm, rx*1e6, tx*1e6, md, br }
#define RELAIS(rx,tx,nm) { nm, rx*1e6, tx*1e6, trxapi_baycomusb_mode_audio, 9600 }

static const struct stndb_entry default_stations[] = {
	DIGI(430.600, 438.200, "HB9AJ-8   Biberstein        JN47AJ  DIEBOX", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(430.600, 438.200, "HB9IAP    Onex/GE           JN36BE  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(430.600, 438.200, "HB9IAC-11 Beauregard/NE     JN37IB  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(430.625, 438.225, "HB9PD     Ahorn, Eriswil    JN37WB  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(430.650, 438.250, "HB9EAS    Stierenberg       JN37SH  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(430.650, 438.250, "HB9IAP    Onex/GE           JN36BE  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(430.650, 438.250, "HB9GR-7   Weisshorn GR      JN46TT  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	//DIGI(430.675, 438.275, "HB9P/E    Bütschelegg/BE    JN36RU  Echo", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(430.675, 438.275, "HB9P/E    Buetschelegg/BE   JN36RU  Echo", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(430.725, 430.725, "HB9C-8    Bern              JN36QV  TCP/IP", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(430.725, 430.725, "HB9C-10   Bern              JN36RW  TCP/IP", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(430.750, 430.750, "HB9IG     Gurten            JN36RW  XNET", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(430.750, 430.750, "HB9CC-11  St. Gallen        JN47OK  JNOS", trxapi_baycomusb_mode_fsk, 9600),
	DIGI(430.750, 430.750, "HB9CC     Eggersriet        JN47RK  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(430.800, 430.800, "HB9EI     Motto Rotondo     JN46KC  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(430.800, 438.400, "HB9IAP    Onex/GE           JN36BE  RMNC", trxapi_baycomusb_mode_fsk, 9600),
	DIGI(430.875, 430.875, "HB9RF     Rigi Staffel      JN47FB  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(430.875, 430.875, "HB9CC-9   Berg TG           JN47NN  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(430.975, 438.575, "HB9PD-7   Bern Spiegel      JN36RW  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(433.625, 433.625, "HB9EA     Corvatsch GR      JN46VK  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	//DIGI(433.625, 433.625, "HB9IAP-10 Mont Pélerin VD   JN36JL  THENET", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(433.625, 433.625, "HB9IAP-10 Mont Pelerin VD   JN36JL  THENET", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(433.675, 433.675, "HB9PD-7   Bern Spiegel      JN36RW  RMNC", trxapi_baycomusb_mode_fsk, 9600),
	DIGI(433.725, 433.725, "HB9PB-7   Montoz            JN37OF  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	//DIGI(433.725, 433.725, "HB9X      Moléson           JN36MN  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(433.725, 433.725, "HB9X      Moleson           JN36MN  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(433.725, 433.725, "HB9Y-8    Brig              JN36XH  F6FBB", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(433.725, 433.725, "HB9Y-10   Moosalpe          JN36WG  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	//DIGI(433.725, 433.725, "HB9GL     Näfels            JN47MC  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(433.725, 433.725, "HB9GL     Naefels           JN47MC  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(433.750, 433.750, "HB9FS     Bad Ramsach BL    JN37WJ  RMNC", trxapi_baycomusb_mode_fsk, 9600),
	DIGI(433.775, 433.775, "HB9N      Niederhorn        JN36VR  XNET", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.025, 438.025, "HB9H      Bellinz./Artore   JN46ME  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.050, 438.050, "HB9EAS-7  Basel/Bruderholz  JN37SM  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.050, 438.050, "HB9OS-7   Siebnen SZ        JN47KE  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.050, 438.050, "HB9C-8    Bern              JN36QV  TCP/IP", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.050, 438.050, "HB9F      Ulmizberg         JN36RV  TNOS", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.075, 438.075, "HB9LU     Luzern            JN47CA  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.075, 438.075, "HB9HAI    Weissfluhgipfel   JN46VU  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.075, 438.075, "HB9HB     Biel              JN37OD  TNOS", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.075, 438.075, "HB9HB     Biel              JN37OD  TNOS", trxapi_baycomusb_mode_fsk, 9600),
	DIGI(438.100, 438.100, "HB9FS     Bad Ramsach BL    JN37WJ  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.100, 438.100, "HB9IAC    La Barillette     JN36BK  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.125, 438.125, "HB9BI     Lueg              JN37UB  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.125, 438.125, "HB9CGB-8  Basel/Klybeck     JN37TN  DX-Clu", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.125, 438.125, "HB9AU     Schaffhausen      JN47HR  TNN", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.125, 438.125, "HB9AU     Schaffhausen      JN47HR  TNN", trxapi_baycomusb_mode_fsk, 9600),
	DIGI(438.150, 438.150, "HB9FG-8   Fribourg          JN36NT  DIEBOX", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.150, 438.150, "HB9EAS-12 Basel/Bruderholz  JN37SM  RMNC", trxapi_baycomusb_mode_fsk, 9600),
	DIGI(438.175, 438.175, "HB9CGB    Basel/Rosental    JN37TN  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.175, 438.175, "HB9N-7    Niederhorn        JN36VR  RMNC", trxapi_baycomusb_mode_fsk, 9600),
	DIGI(438.225, 430.625, "HB9OS-7   Siebnen SZ        JN47KE  RMNC", trxapi_baycomusb_mode_fsk, 9600),
	DIGI(438.275, 430.675, "HB9VC     Kloten            JN47HL  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.325, 430.725, "HB9F      Ulmizberg         JN36RV  TNOS", trxapi_baycomusb_mode_fsk, 9600),
	DIGI(438.375, 430.775, "HB9RF-7   Zugerberg         JN47GD  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.375, 430.775, "HB9EH     Burgdorf          JN37TB  TCP/IP", trxapi_baycomusb_mode_afsk, 1200),
	//DIGI(438.375, 430.775, "HB9X      Moléson           JN36MN  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.375, 430.775, "HB9X      Moleson           JN36MN  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.400, 430.800, "HB9LU     Luzern            JN47CA  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.400, 430.800, "HB9GR     Buchserberg       JN47RD  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.400, 430.800, "HB9W-14   Winterthur        JN47IL  RMNC", trxapi_baycomusb_mode_fsk, 9600),
	//DIGI(438.425, 430.825, "HB9OS     Chäserrugg        JN47PD  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.425, 430.825, "HB9OS     Chaeserrugg       JN47PD  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	//DIGI(438.425, 430.825, "HB9DA     Nütziweid         JN47BD  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.425, 430.825, "HB9DA     Nuetziweid        JN47BD  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.425, 430.825, "HB9CGB    Basel/Rosental    JN37TN  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	//DIGI(438.425, 430.825, "HB9IAP-13 Tête de Ran       JN37KA  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.425, 430.825, "HB9IAP-13 Tete de Ran       JN37KA  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.450, 430.850, "HB9IAC    La Barillette     JN36BK  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.450, 430.850, "HB9OK     Alpe Foppa        JN46KC  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.450, 430.850, "HB9OK     Alpe Foppa        JN46KC  RMNC", trxapi_baycomusb_mode_fsk, 9600),
	DIGI(438.475, 430.875, "HB9OK     Alpe Foppa        JN46KC  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.550, 430.950, "HB9ZRH    Uetliberg         JN47FI  XNET", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.550, 430.950, "HB9ZRH    Uetliberg         JN47FI  XNET", trxapi_baycomusb_mode_fsk, 9600),
	DIGI(438.575, 430.975, "HB9CF     Rigi Scheidegg    JN47GA  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	//DIGI(438.600, 431.000, "HB9AK     Hörnli            JN47LI  XNET", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.600, 431.000, "HB9AK     Hoernli           JN47LI  XNET", trxapi_baycomusb_mode_afsk, 1200),
	//DIGI(438.600, 431.000, "HB9AK     Hörnli            JN47LI  XNET", trxapi_baycomusb_mode_fsk, 9600),
	DIGI(438.600, 431.000, "HB9AK     Hoernli           JN47LI  XNET", trxapi_baycomusb_mode_fsk, 9600),
	//DIGI(438.625, 431.025, "HB9W      Brütten ZH        JN47IL  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(438.625, 431.025, "HB9W      Bruetten ZH        JN47IL  RMNC", trxapi_baycomusb_mode_afsk, 1200),
	DIGI(439.925, 432.325, "HB9IAP/E  La Praz           JN36FQ  Echo", trxapi_baycomusb_mode_afsk, 1200),
	RELAIS(438.650, 431.050, "HB9UF   Uetliberg        JN47GI  870m ID \"U\" (UTO)"),
	RELAIS(438.650, 431.050, "HB9RW   Chur             JN46SU  590m"),
	RELAIS(438.650, 431.050, "HB9Y    Roc Blanc        JN36NI 1704m"),
	RELAIS(438.675, 431.075, "HB9RF   Zug              JN47FE  510m"),
	RELAIS(438.675, 431.075, "HB9BS   Muttenz Basel    JN37TM  285m"),
	RELAIS(438.675, 431.075, "HB9EI   Monte Bar Lugano JN46MC 1816m <> Corvatsch /95"),
	RELAIS(438.675, 431.075, "HB9HAI  Weissfluhgipfel  JN46VT 2843m"),
	RELAIS(438.675, 431.075, "HB9Y    Moosalpe         JN36XH 1998m"),
	RELAIS(438.700, 431.100, "HB9BA   Weissenstein     JN37SG 1280m"),
	RELAIS(438.725, 431.125, "HB9XC   Chasseral        JN37MD 1607m"),
	//RELAIS(438.750, 431.150, "HB9UF   Säntis           JN47QG 2502m <> Corvatsch"),
	RELAIS(438.750, 431.150, "HB9UF   Saentis           JN47QG 2502m <> Corvatsch"),
	RELAIS(438.775, 431.175, "HB9FS   Sissacherflue    JN37VL  701m"),
	RELAIS(438.800, 431.200, "HB9UF   Pilatus          JN46DX 2120m ID \"P\""),
	RELAIS(438.825, 431.225, "HB9CF   Fronalpstock SZ  JN46HX 1900m"),
	RELAIS(438.850, 431.250, "HB9MM   La Praz Lausanne JN36FQ  890m"),
	RELAIS(438.925, 431.325, "HB9F    Bern             JN36RW  540m"),
	//RELAIS(438.925, 431.325, "HB9HD   Zürich Stadt                m Projekt 7.1.96"),
	RELAIS(438.925, 431.325, "HB9HD   Zuerich Stadt                m Projekt 7.1.96"),
	RELAIS(438.925, 431.325, "HB9HD   Kyburg           JN47IL  625m <> Frauenfeld"),
	RELAIS(438.950, 431.350, "HB9AG   Strihen          JN47AK  850m"),
	RELAIS(438.975, 431.375, "HB9F    Interlaken       JN36WQ  593m"),
	RELAIS(438.975, 431.375, "HB9GL   Mollis Fronalp   JN47NB 1390m ID \"G\""),
	RELAIS(439.000, 431.400, "HB9FG   Le Gibloux       JN36MQ 1250m"),
	RELAIS(439.025, 431.425, "HB0FL   Buchserberg      JN47RD 1411m"),
	RELAIS(439.050, 431.450, "HB9F    Niesen           JN36TP 2362m"),
	RELAIS(439.050, 431.450, "HB9OK   San Salvatore    JN45LX  912m <> Monti Malmera"),
	RELAIS(439.050, 431.450, "HB9F    Schilthorn       JN36VN 2970m"),
	RELAIS(439.100, 431.500, "HB9G    Petit Lancy      JN36BE  450m"),
	RELAIS(439.100, 431.500, "HB9KF   Basel            JN37TN  270m"),
	RELAIS(439.150, 431.550, "HB9W    Winterthur       JN47IL  502m ID \"W\""),
	RELAIS(439.150, 431.550, "HB9F    Burgdorf         JN37TB  691m <> Ulmizberg"),
	RELAIS(439.225, 431.625, "HB9ZO   Uster            JN47II  460m"),
	RELAIS(439.375, 431.775, "HB9UF   Corvatsch        JN46VK 3295m <> HB9UF Säntis"),
	RELAIS(439.375, 431.775, "HB9XC   Loveresse        JN37OG 1224m")
};

#undef DIGI
#undef RELAIS

/* ---------------------------------------------------------------------- */

static int updatelist(void);

#ifdef WIN32

#define REGISTRYPATH "SOFTWARE\\FlexNet\\T7F"
#define REGISTRYSTNPATH "Stations"
#define REGISTRYKEY  HKEY_CURRENT_USER


static int deletekeyx(HKEY key)
{
        char name[128];
        LONG err;
        DWORD len;
        HKEY key2;
        int ret = 0;
        
        for (;;) {
                len = sizeof(name);
                if ((RegEnumKeyEx(key, 0, name, &len, NULL, NULL, NULL, NULL)) != ERROR_SUCCESS)
                        return ret;
                if ((err = RegOpenKeyEx(key, name, 0, KEY_ALL_ACCESS, &key2)) != ERROR_SUCCESS) {
                        g_printerr("RegOpenKeyEx(%s) returned 0x%lx\n", name, err);
                        return -1;
                }
                ret |= deletekeyx(key2);
                RegCloseKey(key2);
                RegDeleteKey(key, name);
        }
        return ret;
}

static int deletekey(const char *name)
{
        HKEY key;
        int ret;
        DWORD err;
        
        if ((err = RegOpenKeyEx(REGISTRYKEY, name, 0, KEY_ALL_ACCESS, &key)) != ERROR_SUCCESS) {
                g_printerr("RegOpenKeyEx(%s) returned 0x%lx\n", name, err);
                return -1;
        }
        ret = deletekeyx(key);
        RegCloseKey(key);
        RegDeleteKey(REGISTRYKEY, name);
        return ret;
}

static int deletestn(const char *newname)
{
        char name[256];

        snprintf(name, sizeof(name), "%s\\%s\\%s", REGISTRYPATH, REGISTRYSTNPATH, newname);
        return deletekey(name);
}

static int writestn(const struct stndb_entry *ent)
{
        char name[256];
        HKEY key;
        DWORD err, val;
        
	snprintf(name, sizeof(name), "%s\\%s\\%s", REGISTRYPATH, REGISTRYSTNPATH, ent->name);
        if ((err = RegCreateKeyEx(REGISTRYKEY, name, 0, "", REG_OPTION_NON_VOLATILE,
				  KEY_WRITE, NULL, &key, NULL)) != ERROR_SUCCESS) {
                g_printerr("RegCreateKeyEx(%s) returned 0x%lx\n", name, err);
                return -1;
        }
	val = ent->rx;
        err = RegSetValueEx(key, "RX", 0, REG_DWORD, &val, sizeof(DWORD));
        if (err != ERROR_SUCCESS) {
		RegCloseKey(key);
                 g_printerr("RegSetValueEx(RX) returned 0x%lx\n", err);
                return -1;
        }
	val = ent->tx;
        err = RegSetValueEx(key, "TX", 0, REG_DWORD, &val, sizeof(DWORD));
        if (err != ERROR_SUCCESS) {
		RegCloseKey(key);
                g_printerr("RegSetValueEx(TX) returned 0x%lx\n", err);
                return -1;
        }
	val = ent->mode;
        err = RegSetValueEx(key, "Mode", 0, REG_DWORD, &val, sizeof(DWORD));
        if (err != ERROR_SUCCESS) {
		RegCloseKey(key);
                g_printerr("RegSetValueEx(Mode) returned 0x%lx\n", err);
                return -1;
        }
	val = ent->bitraterx;
        err = RegSetValueEx(key, "BitrateRx", 0, REG_DWORD, &val, sizeof(DWORD));
        if (err != ERROR_SUCCESS) {
		RegCloseKey(key);
                g_printerr("RegSetValueEx(BitrateRx) returned 0x%lx\n", err);
                return -1;
        }
	val = ent->bitratetx;
        err = RegSetValueEx(key, "BitrateTx", 0, REG_DWORD, &val, sizeof(DWORD));
        if (err != ERROR_SUCCESS) {
		RegCloseKey(key);
                g_printerr("RegSetValueEx(BitrateTx) returned 0x%lx\n", err);
                return -1;
        }
        RegCloseKey(key);
        return 0;
}

static int readstns(void)
{
        char name[256];
        HKEY regkey, regkey2;
        LONG err;
        DWORD regtype, regval, len;
        DWORD index = 0;
        
	stns.nrstn = 0;
        snprintf(name, sizeof(name), "%s\\%s", REGISTRYPATH, REGISTRYSTNPATH);
        if ((err = RegOpenKeyEx(REGISTRYKEY, name, 0, KEY_READ, &regkey)) != ERROR_SUCCESS) {
                g_printerr("RegOpenKeyEx(%s) returned 0x%lx\n", name, err);
                return -1;
        }
        while (stns.nrstn < MAXSTATIONS) {
                len = sizeof(stns.stn[stns.nrstn].name);
                if ((RegEnumKeyEx(regkey, index, stns.stn[stns.nrstn].name, &len,
                                  NULL, NULL, NULL, NULL)) != ERROR_SUCCESS)
                        break;
                index++;
		/* read RX and TX frequency */
		if ((err = RegOpenKeyEx(regkey, stns.stn[stns.nrstn].name, 0, KEY_READ, &regkey2)) != ERROR_SUCCESS) {
			g_printerr("RegOpenKeyEx(%s) returned 0x%lx\n", stns.stn[stns.nrstn].name, err);
			continue;
		}
		len = sizeof(regval);
		if (RegQueryValueEx(regkey2, "RX", NULL, &regtype, (void *)&regval, &len) != ERROR_SUCCESS || regtype != REG_DWORD) {
			g_printerr("RegQueryValueEx(RX) error\n");
			regval = 435000;
		}
		stns.stn[stns.nrstn].rx = regval;
		len = sizeof(regval);
		if (RegQueryValueEx(regkey2, "TX", NULL, &regtype, (void *)&regval, &len) != ERROR_SUCCESS || regtype != REG_DWORD) {
			g_printerr("RegQueryValueEx(TX) error\n");
			regval = 435000;
		}
		stns.stn[stns.nrstn].tx = regval;
		len = sizeof(regval);
		if (RegQueryValueEx(regkey2, "Mode", NULL, &regtype, (void *)&regval, &len) != ERROR_SUCCESS || regtype != REG_DWORD) {
			g_printerr("RegQueryValueEx(Mode) error\n");
			regval = -1;
		}
		stns.stn[stns.nrstn].mode = regval;
		len = sizeof(regval);
		if (RegQueryValueEx(regkey2, "BitrateRx", NULL, &regtype, (void *)&regval, &len) != ERROR_SUCCESS || regtype != REG_DWORD) {
			g_printerr("RegQueryValueEx(BitrateRx) error\n");
			regval = 9600;
		}
		stns.stn[stns.nrstn].bitraterx = regval;
		len = sizeof(regval);
		if (RegQueryValueEx(regkey2, "BitrateTx", NULL, &regtype, (void *)&regval, &len) != ERROR_SUCCESS || regtype != REG_DWORD) {
			g_printerr("RegQueryValueEx(BitrateTx) error\n");
			regval = 9600;
		}
		stns.stn[stns.nrstn].bitratetx = regval;
		RegCloseKey(regkey2);
		stns.nrstn++;
        }
        RegCloseKey(regkey);
	return 0;
}

void stndb_open(void)
{
	unsigned int i;

	if (updatelist()) {
		for (i = 0; i < sizeof(default_stations)/sizeof(default_stations[0]); i++)
			writestn(&default_stations[i]);
	}
	updatelist();
}

void stndb_close(void)
{
}

#else /* WIN32 */

static xmlDocPtr doc = NULL;
static char cfgfile[512] = "/tmp/t7fstndb";

static int deletestn(const char *newname)
{
	xmlNodePtr node;
	const char *name;

	for (node = doc->root->childs; node; node = node->next) {
                if (!node->name || strcmp(node->name, "station"))
                        continue;
                name = xmlGetProp(node, "name");
                if (!name)
			continue;
		if (!strcmp(name, newname)) {
			xmlUnlinkNode(node);
			xmlFreeNode(node);
			return 0;
		}
	}
	return -1;
}

static int writestn(const struct stndb_entry *ent)
{
	xmlNodePtr node;
	char buf[32];

	deletestn(ent->name);
	node = xmlNewChild(doc->root, NULL, "station", NULL);
	if (!node)
		return -1;
	xmlSetProp(node, "name", ent->name);
	snprintf(buf, sizeof(buf), "%lu", (unsigned long)ent->rx);
	xmlSetProp(node, "rx", buf);
	snprintf(buf, sizeof(buf), "%lu", (unsigned long)ent->tx);
	xmlSetProp(node, "tx", buf);
	snprintf(buf, sizeof(buf), "%d", ent->mode);
	xmlSetProp(node, "mode", buf);
	snprintf(buf, sizeof(buf), "%lu", ent->bitraterx);
	xmlSetProp(node, "bitraterx", buf);
	snprintf(buf, sizeof(buf), "%lu", ent->bitratetx);
	xmlSetProp(node, "bitratetx", buf);
        return 0;
}

static int readstns(void)
{
	xmlNodePtr node;
	const char *name;

	stns.nrstn = 0;
	for (node = doc->root->childs; node; node = node->next) {
                if (!node->name || strcmp(node->name, "station"))
                        continue;
                name = xmlGetProp(node, "name");
                if (!name)
			continue;
		strncpy(stns.stn[stns.nrstn].name, name, sizeof(stns.stn[stns.nrstn].name));
		stns.stn[stns.nrstn].rx = stns.stn[stns.nrstn].tx = 435000000;
		stns.stn[stns.nrstn].mode = -1;
		stns.stn[stns.nrstn].bitraterx = stns.stn[stns.nrstn].bitratetx = 9600;
		if ((name = xmlGetProp(node, "rx")))
			stns.stn[stns.nrstn].rx = strtoul(name, NULL, 0);
		if ((name = xmlGetProp(node, "tx")))
			stns.stn[stns.nrstn].tx = strtoul(name, NULL, 0);
		if ((name = xmlGetProp(node, "mode")))
			stns.stn[stns.nrstn].mode = strtol(name, NULL, 0);
		if ((name = xmlGetProp(node, "bitraterx")))
			stns.stn[stns.nrstn].bitraterx = strtoul(name, NULL, 0);
		if ((name = xmlGetProp(node, "bitratetx")))
			stns.stn[stns.nrstn].bitratetx = strtoul(name, NULL, 0);
		stns.nrstn++;
        }
	return 0;
}

void stndb_open(void)
{
	unsigned int i;
	char *home;

	if ((home = getenv("HOME")))
		snprintf(cfgfile, sizeof(cfgfile), "%s/.t7fstndb", home);
        doc = xmlParseFile(cfgfile);
	if (doc && (!doc->root || !doc->root->name || strcmp(doc->root->name, "t7fstations"))) {
		g_printerr("t7f: Invalid configuration file %s\n", cfgfile);
		xmlFreeDoc(doc);
		doc = NULL;
	}
	if (!doc && (doc = xmlNewDoc("1.0"))) {
		doc->root = xmlNewDocNode(doc, NULL, "t7fstations", NULL);
		for (i = 0; i < sizeof(default_stations)/sizeof(default_stations[0]); i++)
			writestn(&default_stations[i]);
	}
	if (!doc || !doc->root) {
		g_printerr("t7f: out of memory\n");
		exit(1);
	}
	updatelist();
}

void stndb_close(void)
{
	if (!xmlSaveFile(cfgfile, doc)) 
		g_printerr("baycomusbtrx: error saving configuration file %s\n", cfgfile);
        xmlFreeDoc(doc);
	doc = NULL;
}

#endif /* WIN32 */

/* ---------------------------------------------------------------------- */

int stndb_delete(const char *name)
{
	char n[MAXSTNNAME];
	int ret;

	if (!name)
		return -1;
	strncpy(n, name, sizeof(n));
	ret = deletestn(n);
	updatelist();
	return ret;
}

int stndb_write(const struct stndb_entry *entry)
{
	int ret;

	if (!entry->name[0])
		return -1;
	ret = writestn(entry);
	updatelist();
	return ret;
}

static int stncompare(struct stndb_entry *i, struct stndb_entry *j)
{
        return strcmp(i->name, j->name);
}

static int updatelist(void)
{
	int r;

	stns.nrstn = 0;
	r = readstns();
	if (r)
		return r;
	if (!stns.nrstn)
		return 0;
        qsort(&stns.stn[0], stns.nrstn, sizeof(stns.stn[0]), stncompare);
	return 0;
}

GList *stndb_list(GList *list)
{
	unsigned int i;

	for (i = 0; i < stns.nrstn; i++)
		list = g_list_append(list, stns.stn[i].name);
	return list;
}

void stndb_clist(GtkCList *clist)
{
	unsigned int i;
	char buf[4][MAXSTNNAME];
	gchar *xbuf[4];

	gtk_clist_freeze(clist);
	gtk_clist_clear(clist);
	xbuf[0] = buf[0];
	xbuf[1] = buf[1];
	xbuf[2] = buf[2];
	xbuf[3] = buf[3];
	for (i = 0; i < stns.nrstn; i++) {
		strncpy(buf[0], stns.stn[i].name, sizeof(buf[0]));
		snprintf(buf[1], sizeof(buf[1]), "%9.4f", stns.stn[i].rx * (1.0 / 1000000.0));
		snprintf(buf[2], sizeof(buf[2]), "%9.4f", stns.stn[i].tx * (1.0 / 1000000.0));
		switch (stns.stn[i].mode) {
		case trxapi_baycomusb_mode_fsk:
			snprintf(buf[3], sizeof(buf[3]), "fsk %lu/%lu", stns.stn[i].bitraterx, stns.stn[i].bitratetx);
			break;

		case trxapi_baycomusb_mode_external:
			snprintf(buf[3], sizeof(buf[3]), "external");
			break;

		case trxapi_baycomusb_mode_afsk:
			snprintf(buf[3], sizeof(buf[3]), "afsk");
			break;

		case trxapi_baycomusb_mode_audio:
			snprintf(buf[3], sizeof(buf[3]), "audio");
			break;

		default:
			snprintf(buf[3], sizeof(buf[3]), "-");
			break;
		}
		gtk_clist_append(clist, xbuf);
	}
	gtk_clist_thaw(clist);
}

int stndb_retrieve(struct stndb_entry *entry)
{
	unsigned int i;
	
	for (i = 0; i < stns.nrstn; i++) {
#if 0
		printf("Station %s RX %llu %llu TX %llu %llu\n", stns.stn[i].name, stns.stn[i].rx, rx, stns.stn[i].tx, tx);
#endif
		if (entry->name[0] && strcmp(entry->name, stns.stn[i].name))
			continue;
		if (entry->rx && entry->rx != stns.stn[i].rx)
			continue;
		if (entry->tx && entry->tx != stns.stn[i].tx)
			continue;
		if (entry->mode != -1 && stns.stn[i].mode != -1) {
		        if (entry->mode != stns.stn[i].mode)
		                continue;
			if (entry->mode == trxapi_baycomusb_mode_fsk) {
			        if (entry->bitraterx && abs(entry->bitraterx - stns.stn[i].bitraterx) > 100)
		                        continue;
			        if (entry->bitratetx && abs(entry->bitratetx - stns.stn[i].bitratetx) > 100)
		                        continue;
			}
		}
		*entry = stns.stn[i];
		return 0;
	}
	return -1;
}
