1 /***************************************************************************
2 cabrillo.cpp - description
3 -------------------
4 begin : Thu Dec 5 2002
5 copyright : (C) 2002 by ARRL
6 author : Jon Bloom
7 email : jbloom@arrl.org
8 revision : $Id$
9 ***************************************************************************/
10
11
12 #define TQSLLIB_DEF
13
14 #include <ctype.h>
15 #include <errno.h>
16 #include <stdlib.h>
17 #include <cstdio>
18 #include <cstring>
19 #include "tqsllib.h"
20 #include "tqslerrno.h"
21
22 #include "winstrdefs.h"
23
24 #define TQSL_CABRILLO_MAX_RECORD_LENGTH 120
25
26 DLLEXPORTDATA TQSL_CABRILLO_ERROR_TYPE tQSL_Cabrillo_Error;
27
28 static char errmsgbuf[256];
29 static char errmsgdata[128];
30
31 struct TQSL_CABRILLO;
32
33 static int freq_to_band(TQSL_CABRILLO *cab, tqsl_cabrilloField *fp);
34 static int freq_to_mhz(TQSL_CABRILLO *cab, tqsl_cabrilloField *fp);
35 static int mode_xlat(TQSL_CABRILLO *cab, tqsl_cabrilloField *fp);
36 static int time_fixer(TQSL_CABRILLO *cab, tqsl_cabrilloField *fp);
37
38 struct cabrillo_field_def {
39 const char *name;
40 int loc;
41 int (*process)(TQSL_CABRILLO *cab, tqsl_cabrilloField *fp);
42 };
43
44 static cabrillo_field_def cabrillo_dummy[] = {
45 { "CALL", 6, 0 },
46 { "BAND", 0, freq_to_band },
47 { "MODE", 1, mode_xlat },
48 { "QSO_DATE", 2, 0 },
49 { "TIME_ON", 3, time_fixer },
50 { "FREQ", 0, freq_to_mhz },
51 { "MYCALL", 4, 0 },
52 };
53
54 /*
55
56 // Cabrillo QSO template specs
57
58 // Call in field 6
59 static cabrillo_field_def cabrillo_c6[] = {
60 { "BAND", 0, freq_to_band },
61 { "MODE", 1, mode_xlat },
62 { "QSO_DATE", 2, 0 },
63 { "TIME_ON", 3, time_fixer },
64 { "CALL", 6, 0 },
65 { "FREQ", 0, 0 },
66 { "MYCALL", 4, 0 },
67 };
68
69 // Call in field 7
70 static cabrillo_field_def cabrillo_c7[] = {
71 { "BAND", 0, freq_to_band },
72 { "MODE", 1, mode_xlat },
73 { "QSO_DATE", 2, 0 },
74 { "TIME_ON", 3, time_fixer },
75 { "CALL", 7, 0 },
76 { "FREQ", 0, 0 },
77 { "MYCALL", 4, 0 },
78 };
79
80 // Call in field 8
81 static cabrillo_field_def cabrillo_c8[] = {
82 { "BAND", 0, freq_to_band },
83 { "MODE", 1, mode_xlat },
84 { "QSO_DATE", 2, 0 },
85 { "TIME_ON", 3, time_fixer },
86 { "CALL", 8, 0 },
87 { "FREQ", 0, 0 },
88 { "MYCALL", 4, 0 },
89 };
90
91 // Call in field 9
92 static cabrillo_field_def cabrillo_c9[] = {
93 { "BAND", 0, freq_to_band },
94 { "MODE", 1, mode_xlat },
95 { "QSO_DATE", 2, 0 },
96 { "TIME_ON", 3, time_fixer },
97 { "CALL", 9, 0 },
98 { "FREQ", 0, 0 },
99 { "MYCALL", 4, 0 },
100 };
101
102 */
103
104 struct cabrillo_contest {
105 char *contest_name;
106 TQSL_CABRILLO_FREQ_TYPE type;
107 cabrillo_field_def *fields;
108 int nfields;
109 };
110
111 struct TQSL_CABRILLO {
112 int sentinel;
113 FILE *fp;
114 char *filename;
115 cabrillo_contest *contest;
116 int field_idx;
117 char rec[TQSL_CABRILLO_MAX_RECORD_LENGTH+1];
118 char *datap;
119 int line_no;
120 char *fields[TQSL_CABRILLO_MAX_FIELDS];
121 };
122
123 #define CAST_TQSL_CABRILLO(p) ((struct TQSL_CABRILLO *)p)
124
125 static TQSL_CABRILLO *
check_cabrillo(tQSL_Cabrillo cabp)126 check_cabrillo(tQSL_Cabrillo cabp) {
127 if (tqsl_init())
128 return 0;
129 if (cabp == 0) {
130 tQSL_Error = TQSL_ARGUMENT_ERROR;
131 return 0;
132 }
133 if (CAST_TQSL_CABRILLO(cabp)->sentinel != 0x2449)
134 return 0;
135 return CAST_TQSL_CABRILLO(cabp);
136 }
137
138 static char *
tqsl_parse_cabrillo_record(char * rec)139 tqsl_parse_cabrillo_record(char *rec) {
140 char *cp = strchr(rec, ':');
141 if (!cp)
142 return 0;
143 *cp++ = 0;
144 if (strlen(rec) > TQSL_CABRILLO_FIELD_NAME_LENGTH_MAX)
145 return 0;
146 while (isspace(*cp))
147 cp++;
148 char *sp;
149 if ((sp = strchr(cp, '\r')) != 0)
150 *sp = '\0';
151 if ((sp = strchr(cp, '\n')) != 0)
152 *sp = '\0';
153 for (sp = cp + strlen(cp); sp != cp; ) {
154 sp--;
155 if (isspace(*sp))
156 *sp = '\0';
157 else
158 break;
159 }
160 for (sp = rec; *sp; sp++)
161 *sp = toupper(*sp);
162 return cp;
163 }
164
165 static int
freq_to_band(TQSL_CABRILLO * cab,tqsl_cabrilloField * fp)166 freq_to_band(TQSL_CABRILLO *cab, tqsl_cabrilloField *fp) {
167 if (!strcasecmp(fp->value, "light")) {
168 strncpy(fp->value, "SUBMM", sizeof fp->value);
169 return 0;
170 }
171 int freq = strtol(fp->value, NULL, 10);
172 const char *band = 0;
173 if (freq < 30) {
174 // Handle known CT misbehavior
175 if (!strcmp(fp->value, "7"))
176 freq = 7000;
177 if (!strcmp(fp->value, "14"))
178 freq = 14000;
179 if (!strcmp(fp->value, "21"))
180 freq = 21000;
181 if (!strcmp(fp->value, "28"))
182 freq = 28000;
183 }
184 if (freq >= 1800 && freq <= 2000)
185 band = "160M";
186 else if (freq >= 3500 && freq <= 4000)
187 band = "80M";
188 else if (freq >= 7000 && freq <= 7300)
189 band = "40M";
190 else if (freq >= 10100 && freq <= 10150)
191 band = "30M";
192 else if (freq >= 14000 && freq <= 14350)
193 band = "20M";
194 else if (freq >= 18068 && freq <= 18168)
195 band = "17M";
196 else if (freq >= 21000 && freq <= 21450)
197 band = "15M";
198 else if (freq >= 24890 && freq <= 24990)
199 band = "12M";
200 else if (freq >= 28000 && freq <= 29700)
201 band = "10M";
202 else if (freq == 50)
203 band = "6M";
204 else if (freq == 70)
205 band = "4M";
206 else if (freq == 144)
207 band = "2M";
208 else if (freq == 222)
209 band = "1.25M";
210 else if (freq == 432)
211 band = "70CM";
212 else if (freq == 902 || freq == 903)
213 band = "33CM";
214 else if (!strcasecmp(fp->value, "1.2G") || !strcasecmp(fp->value, "1.2"))
215 band = "23CM";
216 else if (!strcasecmp(fp->value, "2.3G") || !strcasecmp(fp->value, "2.3"))
217 band = "13CM";
218 else if (!strcasecmp(fp->value, "3.4G") || !strcasecmp(fp->value, "3.4"))
219 band = "9CM";
220 else if (!strcasecmp(fp->value, "5.7G") || !strcasecmp(fp->value, "5.7"))
221 band = "6CM";
222 else if (!strcasecmp(fp->value, "10G") || !strcasecmp(fp->value, "10"))
223 band = "3CM";
224 else if (!strcasecmp(fp->value, "24G") || !strcasecmp(fp->value, "24"))
225 band = "1.25CM";
226 else if (!strcasecmp(fp->value, "47G") || !strcasecmp(fp->value, "47"))
227 band = "6MM";
228 else if (!strcasecmp(fp->value, "75G") || !strcasecmp(fp->value, "75") ||
229 !strcasecmp(fp->value, "76G") || !strcasecmp(fp->value, "76"))
230 band = "4MM";
231 else if (!strcasecmp(fp->value, "119G") || !strcasecmp(fp->value, "119"))
232 band = "2.5MM";
233 else if (!strcasecmp(fp->value, "142G") || !strcasecmp(fp->value, "142"))
234 band = "2MM";
235 else if (!strcasecmp(fp->value, "241G") || !strcasecmp(fp->value, "241")||
236 !strcasecmp(fp->value, "242G") || !strcasecmp(fp->value, "242"))
237 band = "1MM";
238 else if (!strcasecmp(fp->value, "300G") || !strcasecmp(fp->value, "300") || !strcasecmp(fp->value, "LIGHT"))
239 band = "SUBMM";
240
241 if (band && cab->contest->type == TQSL_CABRILLO_UNKNOWN) {
242 if (freq < 1000)
243 cab->contest->type = TQSL_CABRILLO_VHF;
244 else
245 cab->contest->type = TQSL_CABRILLO_HF;
246 }
247 if (band == 0)
248 return 1;
249 strncpy(fp->value, band, sizeof fp->value);
250 return 0;
251 }
252
253 static int
freq_to_mhz(TQSL_CABRILLO * cab,tqsl_cabrilloField * fp)254 freq_to_mhz(TQSL_CABRILLO *cab, tqsl_cabrilloField *fp) {
255 if (!strcasecmp(fp->value, "light")) {
256 return 0;
257 }
258 int freq = strtol(fp->value, NULL, 10);
259 double freqmhz = freq;
260 freqmhz /= 1000;
261
262 if (freq < 30) {
263 // Handle known CT misbehavior
264 if (freq == 7)
265 freqmhz = 7.0;
266 if (freq == 14)
267 freqmhz = 14.0;
268 if (freq == 21)
269 freqmhz = 21.0;
270 if (freq == 28)
271 freqmhz = 28.0;
272 }
273 // VHF+
274 if (!strcasecmp(fp->value, "50"))
275 freqmhz = 50.0;
276 else if (!strcasecmp(fp->value, "70"))
277 freqmhz = 70.0;
278 else if (!strcasecmp(fp->value, "144"))
279 freqmhz = 144.0;
280 else if (!strcasecmp(fp->value, "222"))
281 freqmhz = 222.0;
282 else if (!strcasecmp(fp->value, "432"))
283 freqmhz = 432.0;
284 else if (!strcasecmp(fp->value, "902") ||
285 !strcasecmp(fp->value, "903"))
286 freqmhz = 902.0;
287 else if (!strcasecmp(fp->value, "1.2G") || !strcasecmp(fp->value, "1.2"))
288 freqmhz = 1240.0;
289 else if (!strcasecmp(fp->value, "2.3G") || !strcasecmp(fp->value, "2.3"))
290 freqmhz = 2300.0;
291 else if (!strcasecmp(fp->value, "3.4G") || !strcasecmp(fp->value, "3.4"))
292 freqmhz = 3300.0;
293 else if (!strcasecmp(fp->value, "5.7G") || !strcasecmp(fp->value, "5.7"))
294 freqmhz = 5650.0;
295 else if (!strcasecmp(fp->value, "10G") || !strcasecmp(fp->value, "10"))
296 freqmhz = 10000.0;
297 else if (!strcasecmp(fp->value, "24G") || !strcasecmp(fp->value, "24"))
298 freqmhz = 24000.0;
299 else if (!strcasecmp(fp->value, "47G") || !strcasecmp(fp->value, "47"))
300 freqmhz = 47000.0;
301 else if (!strcasecmp(fp->value, "75G") || !strcasecmp(fp->value, "75") ||
302 !strcasecmp(fp->value, "76G") || !strcasecmp(fp->value, "76"))
303 freqmhz = 75500.0;
304 else if (!strcasecmp(fp->value, "119G") || !strcasecmp(fp->value, "119"))
305 freqmhz = 119980.0;
306 else if (!strcasecmp(fp->value, "142G") || !strcasecmp(fp->value, "142"))
307 freqmhz = 142000.0;
308 else if (!strcasecmp(fp->value, "241G") || !strcasecmp(fp->value, "241") ||
309 !strcasecmp(fp->value, "242G") || !strcasecmp(fp->value, "242"))
310 freqmhz = 241000.0;
311 else if (!strcasecmp(fp->value, "300G") || !strcasecmp(fp->value, "300"))
312 freqmhz = 300000.0;
313
314 if (freqmhz > 0 && cab->contest->type == TQSL_CABRILLO_UNKNOWN) {
315 if (freqmhz >= 50.0) // VHF
316 cab->contest->type = TQSL_CABRILLO_VHF;
317 else
318 cab->contest->type = TQSL_CABRILLO_HF;
319 }
320
321 snprintf(fp->value, sizeof fp->value, "%#f", freqmhz);
322 return 0;
323 }
324
325 static int
mode_xlat(TQSL_CABRILLO * cab,tqsl_cabrilloField * fp)326 mode_xlat(TQSL_CABRILLO *cab, tqsl_cabrilloField *fp) {
327 static struct {
328 const char *cmode;
329 const char *gmode;
330 } modes[] = {
331 { "CW", "CW"}, {"PH", "SSB"}, {"FM", "FM"}, {"RY", "RTTY" }
332 };
333 for (int i = 0; i < static_cast<int>(sizeof modes / sizeof modes[0]); i++) {
334 if (!strcasecmp(fp->value, modes[i].cmode)) {
335 strncpy(fp->value, modes[i].gmode, sizeof fp->value);
336 return 0;
337 }
338 }
339 return 1;
340 }
341
342 static int
time_fixer(TQSL_CABRILLO * cab,tqsl_cabrilloField * fp)343 time_fixer(TQSL_CABRILLO *cab, tqsl_cabrilloField *fp) {
344 if (strlen(fp->value) == 0)
345 return 0;
346 char *cp;
347 for (cp = fp->value; *cp; cp++)
348 if (!isdigit(*cp))
349 break;
350 if (*cp)
351 return 1;
352 snprintf(fp->value, sizeof fp->value, "%04d", static_cast<int>(strtol(fp->value, NULL, 10)));
353 return 0;
354 }
355
356 static void
tqsl_free_cabrillo_contest(struct cabrillo_contest * c)357 tqsl_free_cabrillo_contest(struct cabrillo_contest *c) {
358 if (c->contest_name)
359 free(c->contest_name);
360 if (c->fields)
361 free(c->fields);
362 free(c);
363 }
364
365 static struct cabrillo_contest *
tqsl_new_cabrillo_contest(const char * contest_name,int call_field,int contest_type)366 tqsl_new_cabrillo_contest(const char *contest_name, int call_field, int contest_type) {
367 cabrillo_contest *c = static_cast<cabrillo_contest *>(calloc(1, sizeof(struct cabrillo_contest)));
368 if (c == NULL)
369 return NULL;
370 if ((c->contest_name = strdup(contest_name)) == NULL) {
371 tqsl_free_cabrillo_contest(c);
372 return NULL;
373 }
374 c->type = (TQSL_CABRILLO_FREQ_TYPE)contest_type;
375 if ((c->fields = (struct cabrillo_field_def *)calloc(1, sizeof cabrillo_dummy)) == NULL) {
376 tqsl_free_cabrillo_contest(c);
377 return NULL;
378 }
379 memcpy(c->fields, cabrillo_dummy, sizeof cabrillo_dummy);
380 c->fields[0].loc = call_field-1;
381 c->nfields = sizeof cabrillo_dummy / sizeof cabrillo_dummy[0];
382 return c;
383 }
384
385 static void
tqsl_free_cab(struct TQSL_CABRILLO * cab)386 tqsl_free_cab(struct TQSL_CABRILLO *cab) {
387 if (!cab || cab->sentinel != 0x2449)
388 return;
389 cab->sentinel = 0;
390 if (cab->filename)
391 free(cab->filename);
392 if (cab->fp)
393 fclose(cab->fp);
394 if (cab->contest)
395 tqsl_free_cabrillo_contest(cab->contest);
396 free(cab);
397 }
398
399 DLLEXPORT int CALLCONVENTION
tqsl_beginCabrillo(tQSL_Cabrillo * cabp,const char * filename)400 tqsl_beginCabrillo(tQSL_Cabrillo *cabp, const char *filename) {
401 tqslTrace("tqsl_beginCabrillo", "cabp=0x%lx, filename=%s", cabp, filename);
402 TQSL_CABRILLO_ERROR_TYPE terrno;
403 if (filename == NULL) {
404 tQSL_Error = TQSL_ARGUMENT_ERROR;
405 return 1;
406 }
407 struct TQSL_CABRILLO *cab;
408 cab = (struct TQSL_CABRILLO *)calloc(1, sizeof(struct TQSL_CABRILLO));
409 if (cab == NULL) {
410 tQSL_Error = TQSL_ALLOC_ERROR;
411 goto err;
412 }
413 cab->sentinel = 0x2449;
414 cab->field_idx = -1;
415 #ifdef _WIN32
416 wchar_t * wfilename = utf8_to_wchar(filename);
417 if ((cab->fp = _wfopen(wfilename, L"rb, ccs=UTF-8")) == NULL) {
418 free_wchar(wfilename);
419 #else
420 if ((cab->fp = fopen(filename, "r")) == NULL) {
421 #endif
422 tQSL_Error = TQSL_SYSTEM_ERROR;
423 tQSL_Errno = errno;
424 tqslTrace("tqsl_beginCabrillo", "open error, errno=%d, error=%s", errno, strerror(errno));
425 goto err;
426 }
427 #ifdef _WIN32
428 free(wfilename);
429 #endif
430 char *cp;
431 terrno = TQSL_CABRILLO_NO_START_RECORD;
432 while ((cp = fgets(cab->rec, sizeof cab->rec, cab->fp)) != 0) {
433 cab->line_no++;
434 if (tqsl_parse_cabrillo_record(cab->rec) != 0
435 && !strcmp(cab->rec, "START-OF-LOG"))
436 break;
437 }
438 if (cp != 0) {
439 terrno = TQSL_CABRILLO_NO_CONTEST_RECORD;
440 while ((cp = fgets(cab->rec, sizeof cab->rec, cab->fp)) != 0) {
441 cab->line_no++;
442 char *vp;
443 if ((vp = tqsl_parse_cabrillo_record(cab->rec)) != 0
444 && !strcmp(cab->rec, "CONTEST")
445 && strtok(vp, " \t\r\n") != 0) {
446 terrno = TQSL_CABRILLO_UNKNOWN_CONTEST;
447 int callfield, contest_type;
448 if (tqsl_getCabrilloMapEntry(vp, &callfield, &contest_type)) {
449 // No defined contest with this name.
450 // callfield comes back as 0
451 contest_type = TQSL_CABRILLO_UNKNOWN;
452 }
453 cab->contest = tqsl_new_cabrillo_contest(vp, callfield, contest_type);
454 if (cab->contest == 0) {
455 strncpy(errmsgdata, vp, sizeof errmsgdata);
456 cp = 0;
457 }
458 break;
459 }
460 }
461 }
462 if (cp == 0) {
463 if (ferror(cab->fp)) {
464 tQSL_Error = TQSL_SYSTEM_ERROR;
465 tQSL_Errno = errno;
466 tqslTrace("tqsl_beginCabrillo", "read error, errno=%d, error=%s", errno, strerror(errno));
467 goto err;
468 }
469 tQSL_Cabrillo_Error = terrno;
470 tQSL_Error = TQSL_CABRILLO_ERROR;
471 goto err;
472 }
473 if ((cab->filename = strdup(filename)) == NULL) {
474 tQSL_Error = TQSL_ALLOC_ERROR;
475 goto err;
476 }
477 *((struct TQSL_CABRILLO **)cabp) = cab;
478 return 0;
479 err:
480 strncpy(tQSL_ErrorFile, filename, sizeof tQSL_ErrorFile);
481 tQSL_ErrorFile[sizeof tQSL_ErrorFile-1] = 0;
482 tqsl_free_cab(cab);
483 return 1;
484 }
485
486 DLLEXPORT int CALLCONVENTION
487 tqsl_endCabrillo(tQSL_Cabrillo *cabp) {
488 tqslTrace("tqsl_endCabrillo", "cabp=0x%lx", cabp);
489 TQSL_CABRILLO *cab;
490 if (cabp == 0)
491 return 0;
492 cab = CAST_TQSL_CABRILLO(*cabp);
493 if (!cab || cab->sentinel != 0x2449)
494 return 0;
495 tqsl_free_cab(cab);
496 *cabp = 0;
497 return 0;
498 }
499
500 DLLEXPORT const char* CALLCONVENTION
501 tqsl_cabrilloGetError(TQSL_CABRILLO_ERROR_TYPE err) {
502 const char *msg = 0;
503 switch (err) {
504 case TQSL_CABRILLO_NO_ERROR:
505 msg = "Cabrillo success";
506 break;
507 case TQSL_CABRILLO_EOF:
508 msg = "Cabrillo end-of-file";
509 break;
510 case TQSL_CABRILLO_EOR:
511 msg = "Cabrillo end-of-record";
512 break;
513 case TQSL_CABRILLO_NO_START_RECORD:
514 msg = "Cabrillo missing START-OF-LOG record";
515 break;
516 case TQSL_CABRILLO_NO_CONTEST_RECORD:
517 msg = "Cabrillo missing CONTEST record";
518 break;
519 case TQSL_CABRILLO_UNKNOWN_CONTEST:
520 snprintf(errmsgbuf, sizeof errmsgbuf, "Cabrillo unknown CONTEST: %s", errmsgdata);
521 msg = errmsgbuf;
522 break;
523 case TQSL_CABRILLO_BAD_FIELD_DATA:
524 snprintf(errmsgbuf, sizeof errmsgbuf, "Cabrillo field data error in %s field", errmsgdata);
525 msg = errmsgbuf;
526 break;
527 }
528 if (!msg) {
529 snprintf(errmsgbuf, sizeof errmsgbuf, "Cabrillo unknown error: %d", err);
530 if (errmsgdata[0] != '\0')
531 snprintf(errmsgbuf + strlen(errmsgbuf), sizeof errmsgbuf - strlen(errmsgbuf),
532 " (%s)", errmsgdata);
533 msg = errmsgbuf;
534 }
535 tqslTrace("tqsl_cabrilloGetError", "msg=%s", msg);
536 errmsgdata[0] = '\0';
537 return msg;
538 }
539
540 DLLEXPORT int CALLCONVENTION
541 tqsl_getCabrilloField(tQSL_Cabrillo cabp, tqsl_cabrilloField *field, TQSL_CABRILLO_ERROR_TYPE *error) {
542 TQSL_CABRILLO *cab;
543 cabrillo_field_def *fp;
544 const char *fielddat;
545
546 if ((cab = check_cabrillo(cabp)) == 0)
547 return 1;
548 if (field == 0 || error == 0) {
549 tQSL_Error = TQSL_ARGUMENT_ERROR;
550 return 1;
551 }
552 if (cab->datap == 0 || cab->field_idx < 0 || cab->field_idx >= cab->contest->nfields) {
553 char *cp;
554 while ((cp = fgets(cab->rec, sizeof cab->rec, cab->fp)) != 0) {
555 cab->line_no++;
556 cab->datap = tqsl_parse_cabrillo_record(cab->rec);
557 if (cab->datap != 0) {
558 if (!strcasecmp(cab->rec, "QSO")) {
559 cab->field_idx = 0;
560 char *fieldp = strtok(cab->datap, " \t\r\n");
561 memset(cab->fields, 0, sizeof cab->fields);
562 for (int i = 0; fieldp && i < static_cast<int>(sizeof cab->fields / sizeof cab->fields[0]); i++) {
563 cab->fields[i] = fieldp;
564 fieldp = strtok(0, " \t\r\n");
565 }
566 break;
567 } else if (!strcasecmp(cab->rec, "END-OF-LOG")) {
568 *error = TQSL_CABRILLO_EOF;
569 return 0;
570 }
571 }
572 }
573 if (cp == 0) {
574 if (ferror(cab->fp)) {
575 tQSL_Error = TQSL_SYSTEM_ERROR;
576 tQSL_Errno = errno;
577 goto err;
578 } else {
579 *error = TQSL_CABRILLO_EOF;
580 return 0;
581 }
582 }
583 }
584 // Record data is okay and field index is valid.
585
586 fp = cab->contest->fields + cab->field_idx;
587 strncpy(field->name, fp->name, sizeof field->name);
588 if (fp->loc < 0) { // New contest
589 // try to guess which field has the 'call-worked'
590 for (int i = 6; i < TQSL_CABRILLO_MAX_FIELDS && cab->fields[i]; i++) {
591 char *p = cab->fields[i];
592 // Simple parse: a callsign is at least 4 chars long
593 // and has at least one digit and at least one letter
594 // Nothing but alphnumeric and '/' allowed.
595
596
597 // First, eliminate grid squares
598 if (strlen(p) == 4) {
599 if (isalpha(p[0]) && isalpha(p[1]) &&
600 isdigit(p[2]) && isdigit(p[3]))
601 continue;
602 }
603 int nlet = 0, ndig = 0;
604 for (; *p; p++) {
605 if (isdigit(*p)) {
606 ndig++;
607 } else if (isalpha(*p)) {
608 nlet++;
609 } else if (*p != '/') {
610 ndig = 0;
611 nlet = 0;
612 break;
613 }
614 }
615 if (nlet > 0 && ndig > 0 && nlet+ndig > 3) {
616 // OK, looks like a callsign. Is it possibly a gridsquare?
617 if (strlen(p) == 6) {
618 if ((isalpha(p[0]) && toupper(p[0]) < 'S') &&
619 (isalpha(p[1]) && toupper(p[1]) < 'S') &&
620 (isdigit(p[2]) && isdigit(p[3])) &&
621 (isalpha(p[4]) && toupper(p[4]) < 'Y') &&
622 (isalpha(p[5]) && toupper(p[5]) < 'Y'))
623 continue; // Gridsquare. Don't use it.
624 }
625 if (fp->loc < 0) { // No callsign candidate yet
626 fp->loc = i;
627 } else {
628 tQSL_Cabrillo_Error = TQSL_CABRILLO_UNKNOWN_CONTEST;
629 tQSL_Error = TQSL_CABRILLO_ERROR;
630 snprintf(errmsgdata, sizeof errmsgdata, "%s\nUnable to find a unique call-worked field.\n"
631 "Please define a custom Cabrillo entry for this contest.\n", cab->contest->contest_name);
632 goto err;
633 }
634 }
635 }
636 if (fp->loc < 0) { // Still can't find a call. Have to bail.
637 tQSL_Cabrillo_Error = TQSL_CABRILLO_UNKNOWN_CONTEST;
638 tQSL_Error = TQSL_CABRILLO_ERROR;
639 snprintf(errmsgdata, sizeof errmsgdata, "%s\nUnable to find a valid call-worked field.\n"
640 "Please define a custom Cabrillo entry for this contest.\n", cab->contest->contest_name);
641 goto err;
642 }
643 }
644 fielddat = cab->fields[fp->loc];
645 if (fielddat == 0) {
646 tQSL_Cabrillo_Error = TQSL_CABRILLO_BAD_FIELD_DATA;
647 tQSL_Error = TQSL_CABRILLO_ERROR;
648 strncpy(errmsgdata, field->name, sizeof errmsgdata);
649 goto err;
650 }
651 strncpy(field->value, fielddat, sizeof field->value);
652
653 if (fp->process && fp->process(cab, field)) {
654 tQSL_Cabrillo_Error = TQSL_CABRILLO_BAD_FIELD_DATA;
655 tQSL_Error = TQSL_CABRILLO_ERROR;
656 strncpy(errmsgdata, field->name, sizeof errmsgdata);
657 goto err;
658 }
659 cab->field_idx++;
660 if (cab->field_idx >= cab->contest->nfields)
661 *error = TQSL_CABRILLO_EOR;
662 else
663 *error = TQSL_CABRILLO_NO_ERROR;
664 return 0;
665 err:
666 strncpy(tQSL_ErrorFile, cab->filename, sizeof tQSL_ErrorFile);
667 tQSL_ErrorFile[sizeof tQSL_ErrorFile-1] = 0;
668 return 1;
669 }
670
671 DLLEXPORT int CALLCONVENTION
672 tqsl_getCabrilloContest(tQSL_Cabrillo cabp, char *buf, int bufsiz) {
673 TQSL_CABRILLO *cab;
674 if ((cab = check_cabrillo(cabp)) == 0)
675 return 1;
676 if (buf == 0 || bufsiz <= 0) {
677 tQSL_Error = TQSL_ARGUMENT_ERROR;
678 return 1;
679 }
680 if (bufsiz < static_cast<int>(strlen(cab->contest->contest_name))+1) {
681 tQSL_Error = TQSL_BUFFER_ERROR;
682 return 1;
683 }
684 strncpy(buf, cab->contest->contest_name, bufsiz);
685 return 0;
686 }
687
688 DLLEXPORT int CALLCONVENTION
689 tqsl_getCabrilloFreqType(tQSL_Cabrillo cabp, TQSL_CABRILLO_FREQ_TYPE *type) {
690 TQSL_CABRILLO *cab;
691 if ((cab = check_cabrillo(cabp)) == 0)
692 return 1;
693 if (type == 0) {
694 tQSL_Error = TQSL_ARGUMENT_ERROR;
695 return 1;
696 }
697 *type = cab->contest->type;
698 return 0;
699 }
700
701 DLLEXPORT int CALLCONVENTION
702 tqsl_getCabrilloLine(tQSL_Cabrillo cabp, int *lineno) {
703 TQSL_CABRILLO *cab;
704 if ((cab = check_cabrillo(cabp)) == 0)
705 return 1;
706 if (lineno == 0) {
707 tQSL_Error = TQSL_ARGUMENT_ERROR;
708 return 1;
709 }
710 *lineno = cab->line_no;
711 return 0;
712 }
713
714 DLLEXPORT const char* CALLCONVENTION
715 tqsl_getCabrilloRecordText(tQSL_Cabrillo cabp) {
716 TQSL_CABRILLO *cab;
717 if ((cab = check_cabrillo(cabp)) == 0)
718 return 0;
719 return cab->rec;
720 }
721