/* xlog - GTK+ logging program for amateur radio operators Copyright (C) 2001 - 2007 Joop Stakenborg This file is part of xlog. Xlog 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 3 of the License, or (at your option) any later version. Xlog 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 xlog. If not, see . */ /* * cabrillo.c - scanner for Cabrillo format - 2.0 * Reports to Stephane Fillod F8CFE * Specifications from http://www.kkn.net/~trey/cabrillo/ * * NB: Sweepstakes, NAQP and NA Sprint are only supported for import. */ #include #include #include #include #ifndef __USE_XOPEN #define __USE_XOPEN #endif #include #include #ifndef HAVE_STRPTIME #include "strptime.h" #define strptime(s,f,t) mystrptime(s,f,t) #endif #include "logfile.h" #include "preferences.h" #include "utils.h" #include "main.h" extern preferencestype preferences; extern programstatetype programstate; /* * fields to be stored in the cabrillo file */ static const gint cabrillo_fields[] = { BAND, MODE, DATE, GMT, RST, CALL, MYRST, REMARKS }; static const gint cabrillo_widths[] = { 5, 2, 10, 4, 10, 13, 10 }; static const gint cabrillo_ss_widths[] = { 5, 2, 10, 4, 13, 10, 13 }; static const gint cabrillo_na_widths[] = { 5, 2, 10, 4, 20, 10, 21 }; static const gint cabrillo_field_nr = 7; static gint cabrillo_open (LOGDB *); static void cabrillo_close (LOGDB *); static gint cabrillo_create (LOGDB *); static gint cabrillo_qso_append (LOGDB *, const qso_t *); static gint cabrillo_qso_foreach (LOGDB *, gint (*fn) (LOGDB *, qso_t *, gpointer arg), gpointer arg); const struct log_ops cabrillo_ops = { .open = cabrillo_open, .close = cabrillo_close, .create = cabrillo_create, .qso_append = cabrillo_qso_append, .qso_foreach = cabrillo_qso_foreach, .type = TYPE_CABRILLO, .name = "Cabrillo", .extension = ".cbr", }; /* * open for read */ gint cabrillo_open (LOGDB * handle) { FILE *fp; const gint xlog_fields [] = {DATE, GMT, CALL, BAND, MODE, RST, MYRST, REMARKS}; fp = fopen (handle->path, "r"); if (!fp) return -1; handle->priv = (gpointer) fp; /* set columns to be used in xlog */ handle->column_nr = 8; memcpy (handle->column_fields, xlog_fields, sizeof (xlog_fields)); /* TODO: set and use handle->column_widths */ return 0; } /* * open for write */ gint cabrillo_create (LOGDB * handle) { FILE *fp; fp = fopen (handle->path, "w"); if (!fp) return -1; handle->priv = (gpointer) fp; /* write header */ fprintf (fp, "START-OF-LOG: 2.0\n" "CREATED-BY: " PACKAGE " Version " VERSION "\n" "CONTEST: \n" "ARRL-SECTION: \n" "CALLSIGN: \n" "CATEGORY: \n" "CATEGORY-ASSISTED: \n" "CLAIMED-SCORE: \n" "CLUB: \n" "NAME: \n" "ADDRESS: \n" "ADDRESS: \n" "OPERATORS: \n" "SOAPBOX: \n" "SOAPBOX: \n"); return 0; } void cabrillo_close (LOGDB * handle) { FILE *fp = (FILE *) handle->priv; /* will fail silently if file was open read-only */ fprintf (fp, "END-OF-LOG:\n"); fclose (fp); } /* * append a qso. NOTE: preferences.callsign contains the operator's call. */ gint cabrillo_qso_append (LOGDB * handle, const qso_t * q) { FILE *fp = (FILE *) handle->priv; gchar rst[16], exch[16] = "", my_rst[16], my_exch[16] = "", date[16]; gchar freq[8]; const gchar *mode, *dot; gint rst_len, l; gchar *q_mode = q[MODE] ? q[MODE] : "SSB"; /* * there's no exchange fields in xlog. However, the exchange information * may be piggybacked by the rst field. eg. "599ON". */ if (!strcmp (q_mode, "SSB") || !strcmp (q_mode, "USB") || !strcmp (q_mode, "LSB") || !strcmp (q_mode, "FM")) rst_len = 2; else rst_len = 3; strncpy (rst, q[RST], rst_len); rst[rst_len] = '\0'; strncpy (my_rst, q[MYRST], rst_len); my_rst[rst_len] = '\0'; if (strlen (q[RST]) > rst_len) strcpy (exch, q[RST] + rst_len); if (strlen (q[MYRST]) > rst_len) strcpy (my_exch, q[MYRST] + rst_len); /* reformat or truncate frequency */ if ((dot = strstr (q[BAND], "."))) { l = dot - q[BAND]; q[BAND][l] = '\0'; sprintf (freq, "%s%s", q[BAND], q[BAND] + l + 1); /* we might have used the optionmenu */ if (!strcmp (freq, "18")) strcpy (freq, "1800"); } else strncpy (freq, q[BAND], 5); freq[5] = '\0'; /* we might have used the optionmenu */ if (strlen (freq) < 3) { if (!strcmp (freq, "35")) strcpy (freq, "3500"); else if (!strcmp (freq, "5")) strcpy (freq, "5300"); else if (!strcmp (freq, "7")) strcpy (freq, "7000"); else if (!strcmp (freq, "10")) strcpy (freq, "10100"); else if (!strcmp (freq, "14")) strcpy (freq, "14000"); else if (!strcmp (freq, "18")) strcpy (freq, "18068"); else if (!strcmp (freq, "21")) strcpy (freq, "21000"); else if (!strcmp (freq, "24")) strcpy (freq, "24890"); else if (!strcmp (freq, "28")) strcpy (freq, "28000"); else if (!strcmp (freq, "50")) strcpy (freq, "50000"); } /* reshape date */ if (strlen (q[DATE]) == 11 && q[DATE][2] == ' ' && q[DATE][6] == ' ') { sprintf (date, "%s-%02d-%c%c", q[DATE] + 7, scan_month (q[DATE] + 3), q[DATE][0], q[DATE][1]); } else { strcpy (date, q[DATE]); } /* translate mode, valid: PH, RY, FM, CW */ if (!strcmp (q_mode, "USB") || !strcmp (q_mode, "LSB") || !strcmp (q_mode, "SSB")) { mode = "PH"; } else if (!strcmp (q_mode, "RTTY")) { mode = "RY"; } else if (!strcmp (q_mode, "FM") || !strcmp (q_mode, "CW")) { mode = q_mode; } else mode = " "; fprintf (fp, "QSO: %5s %-3s%-11s%-5s%-14s%-4s%-7s%-14s%-4s%-7s\n", freq, mode, date, q[GMT], preferences.callsign, rst, exch, q[CALL], my_rst, my_exch); return 0; } #define MAXROWLEN 120 enum cbr_contest_type { CBR_OTHER, CBR_SWEEPSTAKES, CBR_NA }; gint cabrillo_qso_foreach (LOGDB * handle, gint (*fn) (LOGDB *, qso_t *, gpointer arg), gpointer arg) { FILE *fp = (FILE *) handle->priv; gint i, ret; qso_t q[QSO_FIELDS]; gchar *field, *end, buffer[MAXROWLEN], buf[20], *res = NULL; enum cbr_contest_type contest_type = CBR_OTHER; const gint *widths = cabrillo_widths; gint sent_call_len = 14; struct tm tm_cab; gboolean phone; while (!feof (fp)) { if (!fgets (buffer, MAXROWLEN - 1, fp)) break; phone = FALSE; /* valid record starts with "QSO: " */ if (strncmp (buffer, "QSO: ", 5)) { /* okay, this is not a QSO record. look at the Contest field * to deduce the log format */ if (!strncmp (buffer, "CONTEST: ", 9)) { if (!strncmp (buffer + 9, "NA", 2)) { contest_type = CBR_NA; widths = cabrillo_na_widths; sent_call_len = 11; } else if (!strncmp (buffer + 9, "ARRL-SS", 7)) { contest_type = CBR_SWEEPSTAKES; widths = cabrillo_ss_widths; sent_call_len = 11; } } else if (!strncmp (buffer, "END-OF-LOG:", 11)) { break; } continue; } memset (q, 0, sizeof (q)); field = buffer + 5; for (i = 0; i < cabrillo_field_nr; i++) { /* hop the fifth field, this is the operator call sign */ if (i == 4) field += sent_call_len; end = field + widths[i]; *end = '\0'; /* check the different mode strings and store mode */ if (i == 1) { if (!strcmp (field, "PH")) { phone = TRUE; strcpy (buf, "SSB"); } else if (!strcmp (field, "RY")) strcpy (buf, "RTTY"); else strcpy (buf, field); q[cabrillo_fields[i]] = g_strdup (buf); } /* reformat date 2007-01-23 -> 23 Jan 2007 */ else if (i == 2) { res = strptime (field, "%Y-%m-%d", &tm_cab); if (res != NULL) { setlocale (LC_TIME, ""); strftime (buf, 20, "%d %b %Y", &tm_cab); setlocale (LC_TIME, "C"); } else strcpy (buf, field); q[cabrillo_fields[i]] = g_strdup (buf); } else if (i == 4 || i == 6) {/* TODO: add prepending zero's if serial */ if (phone) { strcpy (buf, field + 2); g_strstrip (buf); field[2] = '\0'; } else { strcpy (buf, field + 3); g_strstrip (buf); field[3] = '\0'; } q[cabrillo_fields[i]] = g_strdup_printf ("%s%s", field, buf); /* add the remark field if present */ if ((i == 6) && (strlen(programstate.importremark) > 0)) { q[cabrillo_fields[i + 1]] = g_strdup (programstate.importremark); } } else q[cabrillo_fields[i]] = g_strdup (g_strstrip (field)); field = end + 1; } ret = (*fn) (handle, q, arg); if (ret) return ret; } return 0; }