dl-fldigi/src/logbook/adif_io.cxx

361 wiersze
11 KiB
C++

#include <FL/Fl.H>
#include <FL/filename.H>
#include <FL/fl_ask.H>
#include <string>
#include <cstring>
#include <cstdlib>
#include <string>
#include "adif_io.h"
#include "config.h"
#include "configuration.h"
#include "lgbook.h"
#include "icons.h"
#include "gettext.h"
#include "debug.h"
#include "util.h"
#include "date.h"
#ifdef __WOE32__
static const char *szEOL = "\r\n";
#else
static const char *szEOL = "\n";
#endif
static const char *szEOR = "<EOR>";
// These ADIF fields define the QSO database
const char *fieldnames[] = {
"ADDRESS", "AGE", "ARRL_SECT", "BAND", "CALL", "CNTY", "COMMENT",
"CONT", "CONTEST_ID", "COUNTRY", "CQZ", "DXCC", "EXPORT", "FREQ",
"GRIDSQUARE", "IOTA", "ITUZ", "MODE", "STX_STRING",
"NAME", "NOTES", "OPERATOR", "PFX", "PROP_MODE",
"QSLRDATE", "QSLSDATE", "QSL_MSG", "QSL_RCVD", "QSL_SENT", "QSL_VIA", "QSO_DATE", "QSO_DATE_OFF",
"QTH",
"RST_RCVD", "RST_SENT", "RX_PWR",
"SAT_MODE", "SAT_NAME", "SRX",
"STATE", "STX", "TEN_TEN",
"TIME_OFF", "TIME_ON", "TX_PWR", "VE_PROV", "SRX_STRING"
};
FIELD fields[] = {
// TYPE, NAME, WIDGET
{ADDRESS, 0, NULL}, // contacted stations mailing address
{AGE, 0, NULL}, // contacted operators age in years
{ARRL_SECT, 0, NULL}, // contacted stations ARRL section
{BAND, 0, &btnSelectBand}, // QSO band
{CALL, 0, &btnSelectCall}, // contacted stations CALLSIGN
{CNTY, 0, &btnSelectCNTY}, // secondary political subdivision, ie: county
{COMMENT, 0, NULL}, // comment field for QSO
{CONT, 0, &btnSelectCONT}, // contacted stations continent
{CONTEST_ID, 0, NULL}, // QSO contest identifier
{COUNTRY, 0, &btnSelectCountry}, // contacted stations DXCC entity name
{CQZ, 0, &btnSelectCQZ}, // contacted stations CQ Zone
{DXCC, 0, &btnSelectDXCC}, // contacted stations Country Code
{EXPORT, 0, NULL}, // used to indicate record is to be exported
{FREQ, 0, &btnSelectFreq}, // QSO frequency in Mhz
{GRIDSQUARE, 0, &btnSelectLOC}, // contacted stations Maidenhead Grid Square
{IOTA, 0, &btnSelectIOTA}, // Islands on the air
{ITUZ, 0, &btnSelectITUZ}, // ITU zone
{MODE, 0, &btnSelectMode}, // QSO mode
{MYXCHG, 0, &btnSelectMyXchg}, // contest exchange sent
{NAME, 0, &btnSelectName}, // contacted operators NAME
{NOTES, 0, &btnSelectNotes}, // QSO notes
{OPERATOR, 0, NULL}, // Callsign of person logging the QSO
{PFX, 0, NULL}, // WPA prefix
{PROP_MODE, 0, NULL}, // propogation mode
{QSLRDATE, 0, &btnSelectQSLrcvd}, // QSL received date
{QSLSDATE, 0, &btnSelectQSLsent}, // QSL sent date
{QSL_MSG, 0, NULL}, // personal message to appear on qsl card
{QSL_RCVD, 0, NULL}, // QSL received status
{QSL_SENT, 0, NULL}, // QSL sent status
{QSL_VIA, 0, NULL},
{QSO_DATE, 0, &btnSelectQSOdateOn}, // QSO data
{QSO_DATE_OFF, 0, &btnSelectQSOdateOff},// QSO data OFF, according to ADIF 2.2.6
{QTH, 0, &btnSelectQth}, // contacted stations city
{RST_RCVD, 0, &btnSelectRSTrcvd}, // received signal report
{RST_SENT, 0, &btnSelectRSTsent}, // sent signal report
{RX_PWR, 0, NULL}, // power of other station in watts
{SAT_MODE, 0, NULL}, // satellite mode
{SAT_NAME, 0, NULL}, // satellite name
{SRX, 0, &btnSelectSerialIN}, // received serial number for a contest QSO
{STATE, 0, &btnSelectState}, // contacted stations STATE
{STX, 0, &btnSelectSerialOUT}, // QSO transmitted serial number
{TEN_TEN, 0, NULL}, // ten ten # of other station
{TIME_OFF, 0, &btnSelectTimeOFF}, // HHMM or HHMMSS in UTC
{TIME_ON, 0, &btnSelectTimeON}, // HHMM or HHMMSS in UTC
{TX_PWR, 0, &btnSelectTX_pwr}, // power transmitted by this station
{VE_PROV, 0, &btnSelectProvince}, // 2 letter abbreviation for Canadian Province
{XCHG1, 0, &btnSelectXchgIn} // contest exchange #1 / free1 in xlog
};
static void initfields()
{
for (int i = 0; i < NUMFIELDS; i++)
fields[i].name = new string(fieldnames[i]);
}
static int findfield( const char *p )
{
int m;
int test;
if (strncasecmp (p, "EOR>", 4) == 0)
return -1;
// following two tests are for backward compatibility with older
// fldigi fields
// changed to comply with ADIF 2.2.3
if (strncasecmp (p, "XCHG1>", 6) == 0)
return XCHG1;
if (strncasecmp (p, "MYXCHG>", 7) == 0)
return MYXCHG;
string tststr;
for (m = 0; m < NUMFIELDS; m++) {
tststr = *(fields[m].name);
tststr += ':';
if ( (test = strncasecmp( p, tststr.c_str(), tststr.length() )) == 0)
return fields[m].type;
}
return -2; //search key not found
}
cAdifIO::cAdifIO ()
{
initfields();
}
void cAdifIO::fillfield (int fieldnum, char *buff){
const char *p = buff;
int fldsize;
while (*p != ':' && *p != '>') p++;
if (*p == '>') return; // bad ADIF specifier ---> no ':' after field name
// found first ':'
p++;
fldsize = 0;
const char *p2 = strchr(buff,'>');
if (!p2) return;
while (p != p2) {
if (*p >= '0' && *p <= '9') {
fldsize = fldsize * 10 + *p - '0';
}
p++;
}
adifqso.putField (fieldnum, p2+1, fldsize);
}
void cAdifIO::readFile (const char *fname, cQsoDb *db) {
long filesize = 0;
char *buff;
int found;
int retval;
// open the adif file
adiFile = fopen (fname, "r");
if (!adiFile)
return;
// determine its size for buffer creation
fseek (adiFile, 0, SEEK_END);
filesize = ftell (adiFile);
if (filesize == 0) {
fl_alert2(_("Empty ADIF logbook file"));
return;
}
buff = new char[filesize + 1];
// read the entire file into the buffer
fseek (adiFile, 0, SEEK_SET);
retval = fread (buff, filesize, 1, adiFile);
fclose (adiFile);
// relaxed file integrity test to all importing from non conforming log programs
if ((strcasestr(buff, "<ADIF_VER:") != 0) &&
(strcasestr(buff, "<CALL:") == 0)) {
delete [] buff;
return;
}
if (strcasestr(buff, "<CALL:") == 0) {
fl_alert2(_("Not an ADIF file"));
delete [] buff;
return;
}
char *p1 = buff, *p2;
if (*p1 != '<') { // yes, skip over header to start of records
p1 = strchr(buff, '<');
while (strncasecmp (p1+1,"EOH>", 4) != 0) {
p1 = strchr(p1+1, '<'); // find next <> field
}
if (!p1) {
delete [] buff;
return; // must not be an ADIF compliant file
}
p1 += 1;
}
p2 = strchr(p1,'<'); // find first ADIF specifier
adifqso.clearRec();
while (p2) {
found = findfield(p2+1);
if (found > -1)
fillfield (found, p2+1);
else if (found == -1) { // <eor> reached; add this record to db
//update fields for older db
if (adifqso.getField(TIME_OFF)[0] == 0)
adifqso.putField(TIME_OFF, adifqso.getField(TIME_ON));
if (adifqso.getField(TIME_ON)[0] == 0)
adifqso.putField(TIME_ON, adifqso.getField(TIME_OFF));
if ((strlen(adifqso.getField(QSO_DATE)) > 7) &&
(adifqso.getField(QSO_DATE_OFF)[0] == 0)) {
char d_str[20];
int d, m, y, t_on, t_off;
strcpy(d_str, adifqso.getField(QSO_DATE));
d = atoi(&d_str[6]); d_str[6] = 0;
m = atoi(&d_str[4]); d_str[4] = 0;
y = atoi(d_str);
t_on = atoi(adifqso.getField(TIME_ON));
t_off = atoi(adifqso.getField(TIME_OFF));
Date dt(m, d, y);
if (t_off < t_on) dt++;
adifqso.putField(QSO_DATE_OFF, dt.szDate(2));
}
db->qsoNewRec (&adifqso);
adifqso.clearRec();
}
p1 = p2 + 1;
p2 = strchr(p1,'<');
}
db->SortByDate(progdefaults.sort_date_time_off);
delete [] buff;
}
static const char *adifmt = "<%s:%d>";
// write ALL or SELECTED records to the designated file
int cAdifIO::writeFile (const char *fname, cQsoDb *db)
{
string ADIFHEADER;
ADIFHEADER = "File: %s";
ADIFHEADER.append(szEOL);
ADIFHEADER.append("<ADIF_VER:%d>%s");
ADIFHEADER.append(szEOL);
ADIFHEADER.append("<PROGRAMID:%d>%s");
ADIFHEADER.append(szEOL);
ADIFHEADER.append("<PROGRAMVERSION:%d>%s");
ADIFHEADER.append(szEOL);
ADIFHEADER.append("<EOH>");
ADIFHEADER.append(szEOL);
// open the adif file
cQsoRec *rec;
string sFld;
adiFile = fopen (fname, "w");
if (!adiFile)
return 1;
fprintf (adiFile, ADIFHEADER.c_str(),
fl_filename_name(fname),
strlen(ADIF_VERS), ADIF_VERS,
strlen(PACKAGE_NAME), PACKAGE_NAME,
strlen(PACKAGE_VERSION), PACKAGE_VERSION);
for (int i = 0; i < db->nbrRecs(); i++) {
rec = db->getRec(i);
if (rec->getField(EXPORT)[0] == 'E') {
for (int j = 0; j < NUMFIELDS; j++) {
if (fields[j].btn != NULL)
if ((*fields[j].btn)->value()) {
sFld = rec->getField(fields[j].type);
if (!sFld.empty())
fprintf(adiFile, adifmt,
fields[j].name->c_str(),
sFld.length());
fprintf(adiFile, "%s", sFld.c_str());
}
}
rec->putField(EXPORT,"");
db->qsoUpdRec(i, rec);
fprintf(adiFile, "%s", szEOR);
fprintf(adiFile, "%s", szEOL);
}
}
fclose (adiFile);
return 0;
}
// write ALL records to the common log
int cAdifIO::writeLog (const char *fname, cQsoDb *db) {
string ADIFHEADER;
ADIFHEADER = "File: %s";
ADIFHEADER.append(szEOL);
ADIFHEADER.append("<ADIF_VER:%d>%s");
ADIFHEADER.append(szEOL);
ADIFHEADER.append("<PROGRAMID:%d>%s");
ADIFHEADER.append(szEOL);
ADIFHEADER.append("<PROGRAMVERSION:%d>%s");
ADIFHEADER.append(szEOL);
ADIFHEADER.append("<DATA CHECKSUM:%d>%s");
ADIFHEADER.append(szEOL);
ADIFHEADER.append("<EOH>");
ADIFHEADER.append(szEOL);
// open the adif file
string sFld;
cQsoRec *rec;
Ccrc16 checksum;
string s_checksum;
adiFile = fopen (fname, "w");
if (!adiFile) {
LOG_ERROR("Cannot write to %s", fname);
return 1;
}
string records;
string record;
char recfield[200];
records.clear();
for (int i = 0; i < db->nbrRecs(); i++) {
rec = db->getRec(i);
record.clear();
for (int j = 0; j < NUMFIELDS; j++) {
sFld = rec->getField(j);
if (!sFld.empty()) {
snprintf(recfield, sizeof(recfield), adifmt,
fields[j].name->c_str(), sFld.length());
record.append(recfield).append(sFld);
}
}
record.append(szEOR);
record.append(szEOL);
records.append(record);
db->qsoUpdRec(i, rec);
}
s_checksum = checksum.scrc16(records);
fprintf (adiFile, ADIFHEADER.c_str(),
fl_filename_name(fname),
strlen(ADIF_VERS), ADIF_VERS,
strlen(PACKAGE_NAME), PACKAGE_NAME,
strlen(PACKAGE_VERSION), PACKAGE_VERSION,
s_checksum.length(), s_checksum.c_str()
);
fprintf (adiFile, "%s", records.c_str());
fclose (adiFile);
return 0;
}