1 /*
2  * Tlf - contest logging program for amateur radio operators
3  * Copyright (C) 2017 Ervin Hegedus <airween@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 
20 #ifndef _XOPEN_SOURCE
21 #define _XOPEN_SOURCE
22 #endif
23 #include <stdio.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include <time.h>
28 
29 #include "addcall.h"
30 #include "addmult.h"
31 #include "bands.h"
32 #include "cabrillo_utils.h"
33 #include "cleanup.h"
34 #include "getexchange.h"
35 #include "globalvars.h"
36 #include "makelogline.h"
37 #include "qtc_log.h"
38 #include "readcabrillo.h"
39 #include "startmsg.h"
40 #include "store_qso.h"
41 #include "tlf_curses.h"
42 
43 #define MAX_CABRILLO_LEN 255
44 
45 enum {
46     LOGPREF_NONE,
47     LOGPREF_QSO,
48     LOGPREF_XQSO,
49     LOGPREF_QTC
50 };
51 
52 static int cablinecnt = 0;
53 char qtcsend_logfile_import[] = "IMPORT_QTC_sent.log";
54 char qtcrecv_logfile_import[] = "IMPORT_QTC_recv.log";
55 extern char qsos[MAX_QSOS][LOGLINELEN +
56 			   1]; // array of log lines of QSOs so far;
57 extern int qsoflags_for_qtc[MAX_QSOS];	// array of flag to log lines of QSOs
58 extern int nr_qsos;
59 
60 
concat_comment(char * exchstr)61 void concat_comment(char *exchstr) {
62     if (strlen(comment) > 0) {
63 	strcat(comment, " ");
64     }
65     strcat(comment, exchstr);
66 }
67 
qtcs_allowed(struct cabrillo_desc * cabdesc)68 int qtcs_allowed(struct cabrillo_desc *cabdesc) {
69     return ((qtcdirection > 0) && (cabdesc->qtc_item_count > 0));
70 }
71 
72 /* check if line starts with 'start' */
starts_with(char * line,char * start)73 int starts_with(char *line, char *start) {
74     return (strncmp(line, start, strlen(start)) == 0);
75 }
76 
77 /* write a new line to the qso log */
write_log_fm_cabr()78 void write_log_fm_cabr() {
79     qsonum = cablinecnt;
80     sprintf(qsonrstr, "%04d", cablinecnt);
81 
82     if (serial_grid4_mult == 1) {
83 	strcpy(section, getgrid(comment));
84     }
85 
86     checkexchange(0);
87     addcall();		/* add call to dupe list */
88     makelogline();	/* format logline */
89     store_qso(logline4);
90     cleanup_qso();
91     qsoflags_for_qtc[nr_qsos - 1] = 0;
92 }
93 
94 /* write a new line to the qtc log */
write_qtclog_fm_cabr(char * qtcrcall,struct read_qtc_t qtc_line)95 void write_qtclog_fm_cabr(char *qtcrcall, struct read_qtc_t  qtc_line) {
96     extern char call[];
97 
98     static int qtc_curr_call_nr = 0;
99     static int qtc_last_call_nr = 0;
100     static int qtc_last_qtc_serial = 0;
101     static int qtc_last_qtc_count = 0;
102     static char qtc_last_qtc_rcall[15] = "";
103 
104     char thiscall[15] = "", ttime[5] = "";
105     int found_call = 0, found_empty = 0;
106 
107     if (strcmp(qtcrcall, call) == 0) {  // RECV
108 	qtc_line.direction = RECV;
109 	qtc_line.qsonr = cablinecnt;
110 	make_qtc_logline(qtc_line, qtcrecv_logfile_import);
111     } else { // SENT
112 
113 	qtc_line.direction = SEND;
114 	// search the sent callsign in list of QSO's
115 
116 	found_call = 0;	// indicates that the callsign found
117 	found_empty = 0;// indicates that there is the "hole" in the list
118 	// some reason, eg. own call
119 	// if new qtc block comes, go back to last empty
120 	if (qtc_last_qtc_count != qtc_line.qtchead_count &&
121 		qtc_last_qtc_serial != qtc_line.qtchead_serial &&
122 		strcmp(qtc_last_qtc_rcall, qtcrcall) != 0) {
123 	    // current nr	last stored nr - empty (see above) or after last
124 	    qtc_curr_call_nr = qtc_last_call_nr;
125 	    strcpy(qtc_last_qtc_rcall, qtcrcall);
126 	    qtc_last_qtc_serial = qtc_line.qtchead_serial;
127 	    qtc_last_qtc_count = qtc_line.qtchead_count;
128 	}
129 
130 	// look until not found and we're in list
131 	while (found_call == 0 && qtc_curr_call_nr < nr_qsos) {
132 	    strncpy(thiscall, qsos[qtc_curr_call_nr] + 29, 14);
133 	    g_strchomp(thiscall);
134 	    strncpy(ttime, qsos[qtc_curr_call_nr] + 17, 2);
135 	    strncpy(ttime + 2, qsos[qtc_curr_call_nr] + 20, 2);
136 	    ttime[4] = '\0';
137 	    // check the call was't sent, and call and time are equals
138 	    if (qsoflags_for_qtc[qtc_curr_call_nr] == 0 &&
139 		    (strcmp(thiscall, qtc_line.qtc_call) == 0) &&
140 		    (strcmp(ttime, qtc_line.qtc_time)) == 0) {
141 		found_call = qtc_curr_call_nr + 1;
142 		qsoflags_for_qtc[qtc_curr_call_nr] = 1;
143 	    } else {
144 		if (found_empty == 0) {
145 		    found_empty = 1;
146 		    qtc_last_call_nr = qtc_curr_call_nr;
147 		}
148 	    }
149 
150 	    // increment list pos.
151 	    qtc_curr_call_nr++;
152 
153 	}
154 	// end search
155 	if (found_empty == 0 && found_call > 0) {
156 	    qtc_last_call_nr = qtc_curr_call_nr;
157 	} else {
158 	    // TODO
159 	    // handling this issue
160 	    //if (found_call == 0) {
161 	    //syslog(LOG_DEBUG, "Found invalid QTC time / QTC call: '%s' '%s' - '%s' '%s' at QTC with %s [%d] (%d - %d)", qtc_line.qtc_time, qtc_line.qtc_call, ttime, thiscall, qtcrcall, qtc_curr_call_nr, found_empty, found_call);
162 	    //}
163 	    qtc_curr_call_nr = qtc_last_call_nr;
164 	}
165 	strcpy(qtc_line.call, qtcrcall);
166 	qtc_line.callpos = found_call;
167 	qtc_line.qsonr = cablinecnt;
168 	make_qtc_logline(qtc_line, qtcsend_logfile_import);
169     }
170 }
171 
172 /* cabrillo QSO to Tlf format
173  *
174  * walk through the lines which starts with QSO/X-QSO, and
175  * build a virtual QSO; then it calls the existing functions
176  * to add to the real log, used by the Cabrillo datas (eg. freq,
177  * date, time, band, ...) instead of the real
178  */
179 
180 struct read_qtc_t qtc_line;	/* make global for testability */
181 
cab_qso_to_tlf(char * line,struct cabrillo_desc * cabdesc)182 void cab_qso_to_tlf(char *line, struct cabrillo_desc *cabdesc) {
183 
184     extern freq_t freq;
185     extern struct tm time_ptr_cabrillo;
186     extern char call[];
187 
188 
189     int item_count;
190     GPtrArray *item_array;
191     struct line_item *item;
192 
193     int i;
194     int pos = 0;
195     char tempstr[80], *tempstrp, timestr[3];
196     int linetype = LOGPREF_NONE;
197     char qtcrcall[15], qtcscall[15];
198 
199     // [UNIVERSAL]
200     // QSO=FREQ,5;MODE,2;DATE,10;TIME,4;MYCALL,13;RST_S,3;EXC_S,6;HISCALL,13;RST_R,3;EXCH,6
201 
202     // [WAEDC]
203     // QSO=FREQ,5;MODE,2;DATE,10;TIME,4;MYCALL,13;RST_S,3;EXC_S,6;HISCALL,13;RST_R,3;EXCH,6
204     // QTC=FREQ,5;MODE,2;DATE,10;TIME,4;QTCRCALL,13;QTCHEAD,10;QTCSCALL,13;QTC,23
205     // QSO: 14043 CW 2016-08-13 0022 HA2OS         599 0004   KL7SB/VY2     599 025
206     // QSO:  7002 CW 2016-08-13 0033 HA2OS         599 0008   K6ND          599 044
207     //
208     // Tlf log:
209     //  20CW  13-Aug-16 00:22 0004  KL7SB/VY2      599  599  025           KL7      1  14043.5
210     //  40CW  13-Aug-16 00:33 0008  K6ND           599  599  044           K6       1   7002.8
211 
212     // QSO: 14084 RY 2016-11-12 1210 HA2OS         599 0013   K4GM          599 156
213     // QTC: 14084 RY 2016-11-12 1214 HA2OS          13/10     K4GM          0230 DL6UHD         074
214     //
215     //  20DIG 0013 12-Nov-16 12:14   K4GM           0013 0010 0230 DL6UHD          074    14084.0
216     //  BANDM QSO  DATE      TIME    CALL           SERIAL/NR TIME QTCCALL      QTCSER    FREQ
217     //
218     // QSO:  3593 RY 2016-11-12 2020 HA2OS         599 0110   RG9A          599 959
219     // QTC:  3593 RY 2016-11-12 2021 RG9A            2/10     HA2OS         1208 2M0WEV         018
220     //
221     //  80DIG 0110 0011 12-Nov-16 20:21   RG9A           0002 0010 1208 2M0WEV         018     3593.8
222     //  BANDM QSO  POS  DATE      TIME    CALL           SERIAL/NR TIME QTCCALL     QTCSER     FREQ
223 
224 
225     memset(&time_ptr_cabrillo, 0, sizeof(struct tm));
226     memset(&qtc_line, 0, sizeof(struct read_qtc_t));
227 
228     if (starts_with(line, "QSO")) {
229 	pos = 5;
230 	cablinecnt++;
231 	linetype = LOGPREF_QSO;
232 	item_count = cabdesc->item_count;
233 	item_array = cabdesc->item_array;
234     } else if (starts_with(line, "X-QSO")) {
235 	pos = 7;
236 	cablinecnt++;
237 	linetype = LOGPREF_XQSO;
238 	item_count = cabdesc->item_count;
239 	item_array = cabdesc->item_array;
240     } else if (qtcs_allowed(cabdesc) && (starts_with(line, "QTC"))) {
241 	pos = 5;
242 	linetype = LOGPREF_QTC;
243 	item_count = cabdesc->qtc_item_count;
244 	item_array = cabdesc->qtc_item_array;
245     } else {
246 	return;
247     }
248 
249     qtcrcall[0] = '\0';
250     qtcscall[0] = '\0';
251     for (i = 0; i < item_count; i++) {
252 	item = g_ptr_array_index(item_array, i);
253 	g_strlcpy(tempstr, line + pos, item->len + 1);
254 	g_strchomp(tempstr);
255 	pos += item->len;
256 	pos++;		// space between fields
257 	switch (item->tag) {
258 	    case FREQ:
259 		freq = atof(tempstr) * 1000.0;
260 		bandinx = freq2band(freq);
261 		strcpy(qtc_line.band, band[bandinx]);
262 		qtc_line.freq = freq;
263 		break;
264 	    case MODE:
265 		if (strcmp(tempstr, "CW") == 0) {
266 		    trxmode = CWMODE;
267 		    strcpy(qtc_line.mode, "CW ");
268 		} else if (strcmp(tempstr, "PH") == 0) {
269 		    trxmode = SSBMODE;
270 		    strcpy(qtc_line.mode, "PH ");
271 		} else {
272 		    trxmode = DIGIMODE;
273 		    strcpy(qtc_line.mode, "DIG");
274 		}
275 		break;
276 	    case DATE:
277 		strptime(tempstr, "%Y-%m-%d", &time_ptr_cabrillo);
278 		strftime(qtc_line.date, 60, "%d-%b-%y", &time_ptr_cabrillo);
279 		break;
280 	    case TIME:
281 		timestr[0] = tempstr[0];
282 		timestr[1] = tempstr[1];
283 		timestr[2] = '\0';
284 		time_ptr_cabrillo.tm_hour = atoi(timestr);
285 		timestr[0] = tempstr[2];
286 		timestr[1] = tempstr[3];
287 		timestr[2] = '\0';
288 		time_ptr_cabrillo.tm_min = atoi(timestr);
289 		sprintf(qtc_line.time, "%02d:%02d", time_ptr_cabrillo.tm_hour,
290 			time_ptr_cabrillo.tm_min);
291 		break;
292 	    case MYCALL:
293 		break;
294 	    case HISCALL:
295 		strcpy(hiscall, tempstr);
296 		break;
297 	    case RST_S:
298 		strcpy(my_rst, tempstr);
299 		break;
300 	    case RST_R:
301 		strcpy(his_rst, tempstr);
302 		break;
303 	    case EXCH:
304 		strcpy(comment, tempstr);
305 		break;
306 	    case EXC1:
307 		strcpy(comment, tempstr);
308 		break;
309 	    case EXC2:
310 		concat_comment(tempstr);
311 		break;
312 	    case EXC3:
313 		concat_comment(tempstr);
314 		break;
315 	    case EXC4:
316 		concat_comment(tempstr);
317 		break;
318 	    case EXC_S:
319 	    case TX:
320 	    case QTCRCALL:
321 		strcpy(qtcrcall, tempstr);
322 		strcpy(qtc_line.call, tempstr);
323 		break;
324 	    case QTCHEAD:
325 		strcpy(qtc_line.qtchead, tempstr);
326 		qtc_line.qtchead_serial = 0;
327 		if ((tempstrp = strtok(qtc_line.qtchead, "/")) != NULL)
328 		    qtc_line.qtchead_serial = atoi(tempstrp);
329 
330 		qtc_line.qtchead_count = 0;
331 		if ((tempstrp = strtok(NULL, " ")) != NULL)
332 		    qtc_line.qtchead_count = atoi(tempstrp);
333 
334 		break;
335 	    case QTCSCALL:
336 		strcpy(qtcscall, tempstr);
337 		strcpy(qtc_line.call, tempstr);
338 		break;
339 	    case QTC:
340 		strcpy(qtc_line.qtcstr, tempstr);
341 		if ((tempstrp = strtok(qtc_line.qtcstr, " ")) != NULL)
342 		    strcpy(qtc_line.qtc_time, tempstrp);
343 
344 		if ((tempstrp = strtok(NULL, " ")) != NULL) {
345 		    g_strchomp(tempstrp);
346 		    strcpy(qtc_line.qtc_call, tempstrp);
347 		}
348 
349 		qtc_line.qtc_serial = 0;
350 		if ((tempstrp = strtok(NULL, " ")) != NULL) {
351 		    g_strchomp(tempstrp);
352 		    qtc_line.qtc_serial = atoi(tempstrp);
353 		}
354 	    case NO_ITEM:
355 	    default:
356 		break;
357 	}
358 
359     }
360 
361 
362     if ((linetype == LOGPREF_QSO) || (linetype == LOGPREF_XQSO)) {
363 	write_log_fm_cabr();
364     } else if (linetype == LOGPREF_QTC) {
365 	write_qtclog_fm_cabr(qtcrcall, qtc_line);
366     }
367 
368 }
369 
show_readcab_msg(int mode,char * msg)370 void show_readcab_msg(int mode, char *msg) {
371 
372     if (mode == READCAB_MODE_CLI) {
373 	showmsg(msg);
374 	refreshp();
375     }
376 }
377 
378 /** readcabrillo
379  *
380  * Main routine to read the cabrillo lines, parses them, and
381  * creates a new Tlf compatible log.
382  *
383  */
384 
readcabrillo(int mode)385 int readcabrillo(int mode) {
386 
387     extern char *cabrillo;
388     extern char call[];
389 
390     char *cab_dfltfile;
391     struct cabrillo_desc *cabdesc;
392     char input_logfile[24];
393     char output_logfile[80], temp_logfile[80];
394     char logline[MAX_CABRILLO_LEN];
395     char *tempstrp;
396 
397     char t_qsonrstr[5];
398     int t_qsonum;
399     int t_bandinx;
400 
401     FILE *fp1, *fp2, *fpqtc;
402 
403     do_cabrillo = 1;
404 
405     if (cabrillo == NULL) {
406 	show_readcab_msg(mode, "Missing CABRILLO= keyword (see man page)");
407 	sleep(2);
408 	do_cabrillo = 0;
409 	return (1);
410     }
411 
412     /* Try to read cabrillo format first from local directory.
413      * Try also in default data dir if not found.
414      */
415     cabdesc = read_cabrillo_format("cabrillo.fmt", cabrillo);
416     if (!cabdesc) {
417 	cab_dfltfile = g_strconcat(PACKAGE_DATA_DIR, G_DIR_SEPARATOR_S,
418 				   "cabrillo.fmt", NULL);
419 	cabdesc = read_cabrillo_format(cab_dfltfile, cabrillo);
420 	g_free(cab_dfltfile);
421     }
422 
423     if (!cabdesc) {
424 	show_readcab_msg(mode, "Cabrillo format specification not found!");
425 	sleep(2);
426 	do_cabrillo = 0;
427 	return (2);
428     } else {
429 	tempstrp = g_strdup_printf("CABRILLO format: %s", cabrillo);
430 	show_readcab_msg(mode, tempstrp);
431 	g_free (tempstrp);
432 	sleep(1);
433     }
434 
435     strcpy(temp_logfile, logfile);
436 
437     strcpy(input_logfile, call);
438     g_strchomp(input_logfile); /* drop \n */
439     strcat(input_logfile, ".cbr");
440 
441     strcpy(output_logfile, "IMPORT_");
442     strcat(output_logfile, logfile);
443     strcpy(logfile, output_logfile);
444 
445     if ((fp2 = fopen(output_logfile, "w")) == NULL) {
446 	tempstrp = g_strdup_printf("Can't open output logfile: %s.",
447 							output_logfile);
448 	show_readcab_msg(mode, tempstrp);
449 	g_free (tempstrp);
450 	sleep(2);
451 	do_cabrillo = 0;
452 	free_cabfmt(cabdesc);
453 	return (1);
454     }
455     fclose(fp2);
456 
457     if ((fp1 = fopen(input_logfile, "r")) == NULL) {
458 	tempstrp = g_strdup_printf("Can't open input logfile: %s.",
459 							input_logfile);
460 	show_readcab_msg(mode, tempstrp);
461 	g_free (tempstrp);
462 	sleep(2);
463 	do_cabrillo = 0;
464 	free_cabfmt(cabdesc);
465 	return (1);
466     }
467 
468     if (cabdesc->qtc_item_count > 0) {
469 	if (qtcdirection & SEND) {
470 	    fpqtc = fopen(qtcsend_logfile_import, "w");
471 	    if (fpqtc) fclose(fpqtc);
472 	}
473 
474 	if (qtcdirection & RECV) {
475 	    fpqtc = fopen(qtcrecv_logfile_import, "w");
476 	    if (fpqtc) fclose(fpqtc);
477 	}
478     }
479 
480     strcpy(t_qsonrstr, qsonrstr);
481     t_qsonum = qsonum;
482     t_bandinx = bandinx;
483 
484     while (fgets(logline, MAX_CABRILLO_LEN, fp1) != NULL) {
485 	cab_qso_to_tlf(logline, cabdesc);
486     }
487 
488     strcpy(qsonrstr, t_qsonrstr);
489     qsonum = t_qsonum;
490     bandinx = t_bandinx;
491 
492     fclose(fp1);
493 
494     free_cabfmt(cabdesc);
495 
496     strcpy(logfile, temp_logfile);
497     do_cabrillo = 0;
498 
499     return 0;
500 }
501