From 6c9c89e71d41a04e0eedce08a8e8e75a2a226d7b Mon Sep 17 00:00:00 2001 From: Zoltan Csahok Date: Wed, 8 Mar 2023 07:43:20 +0100 Subject: [PATCH] Add Python plugin support (#347) Some functions can be overridden by contest specific plugin methods. --------- Co-authored-by: Victor Denisov Co-authored-by: Thomas Beierlein Co-authored-by: Thomas Beierlein Co-authored-by: Jonathan Bastien-Filiatrault --- .github/workflows/ci-build-ubuntu-20.yml | 31 ++ .github/workflows/ci-build.yml | 6 +- configure.ac | 2 +- rules/cqp/cqp_ca | 63 ++++ rules/cqp/cqp_ca_mults | 76 +++++ rules/hadx/hadx | 61 ++++ rules/hadx/hadx.py | 22 ++ rules/mwc/mwc | 32 ++ rules/mwc/mwc.py | 19 ++ rules/tesla.py | 1 - share/cabrillo.fmt | 4 + src/Makefile.am | 4 +- src/addmult.c | 4 +- src/addpfx.c | 2 +- src/addspot.c | 2 +- src/bandmap.h | 1 - src/callinput.c | 79 +++-- src/changepars.c | 4 +- src/cleanup.c | 69 ++++- src/cleanup.h | 4 + src/deleteqso.c | 4 +- src/edit_last.c | 14 +- src/editlog.c | 2 +- src/genqtclist.c | 2 +- src/getexchange.c | 102 ++++-- src/gettxinfo.c | 28 ++ src/globalvars.h | 15 +- src/hamlib_keyer.c | 44 +-- src/hamlib_keyer.h | 2 - src/keyer.c | 56 +--- src/keystroke_names.h | 2 + src/last10.c | 4 +- src/log_to_disk.c | 5 +- src/main.c | 37 ++- src/muf.c | 3 +- src/note.c | 13 +- src/parse_logcfg.c | 154 +++++---- src/parse_logcfg.h | 10 +- src/plugin.c | 377 +++++++++++++++-------- src/plugin.h | 14 +- src/prevqso.c | 45 --- src/prevqso.h | 28 -- src/qtcwin.c | 9 +- src/readcabrillo.c | 6 +- src/readcalls.c | 35 ++- src/scroll_log.c | 4 +- src/searchlog.c | 2 +- src/sendbuf.c | 132 +++++--- src/sendbuf.h | 6 + src/sendqrg.c | 35 +++ src/sendqrg.h | 4 + src/setcontest.c | 4 +- src/setcontest.h | 3 +- src/showscore.c | 11 +- src/startmsg.c | 2 +- src/startmsg.h | 2 +- src/store_qso.c | 8 +- src/store_qso.h | 2 +- src/tlf.h | 4 +- src/writecabrillo.c | 5 +- test/data.c | 13 +- test/test_addcall.c | 2 +- test/test_adif.c | 1 + test/test_cabrillo.c | 4 +- test/test_getexchange.c | 21 +- test/test_parse_logcfg.c | 24 +- test/test_readcalls.c | 5 + test/test_searchlog.c | 23 +- test/test_sendbuf.c | 9 +- tlf.1.in | 309 ++++++++++++++++--- 70 files changed, 1492 insertions(+), 640 deletions(-) create mode 100644 .github/workflows/ci-build-ubuntu-20.yml create mode 100644 rules/cqp/cqp_ca create mode 100644 rules/cqp/cqp_ca_mults create mode 100644 rules/hadx/hadx create mode 100644 rules/hadx/hadx.py create mode 100644 rules/mwc/mwc create mode 100644 rules/mwc/mwc.py delete mode 100644 src/prevqso.c delete mode 100644 src/prevqso.h diff --git a/.github/workflows/ci-build-ubuntu-20.yml b/.github/workflows/ci-build-ubuntu-20.yml new file mode 100644 index 000000000..1b2332a89 --- /dev/null +++ b/.github/workflows/ci-build-ubuntu-20.yml @@ -0,0 +1,31 @@ +name: CI build for Ubuntu 20 + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-20.04 + timeout-minutes: 3 + env: + TERM: xterm + AM_COLOR_TESTS: always + + steps: + - uses: actions/checkout@v3 + - name: Show OS info + run: cat /etc/os-release + - name: Install dependencies + run: | + sudo apt-get -qq update + sudo apt-get install -y libhamlib-dev libxmlrpc-core-c3-dev libcmocka-dev + - name: Autoreconf and basic make + run: autoreconf -i && ./configure && make -j2 + - name: Configure with xmlrpc + run: make clean && ./configure --enable-fldigi-xmlrpc && make -j2 + - name: Run tests + run: make check; rc=$?; cat test/run_*.log; exit $rc diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 5e8fa55b5..aca5b0b68 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -16,16 +16,18 @@ jobs: AM_COLOR_TESTS: always steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Show OS info run: cat /etc/os-release - name: Install dependencies run: | sudo apt-get -qq update - sudo apt-get install -y libhamlib-dev libxmlrpc-core-c3-dev libcmocka-dev + sudo apt-get install -y libhamlib-dev libxmlrpc-core-c3-dev libcmocka-dev python3-dev - name: Autoreconf and basic make run: autoreconf -i && ./configure && make -j2 - name: Configure with xmlrpc run: make clean && ./configure --enable-fldigi-xmlrpc && make -j2 + - name: Configure with xmlrpc and Python plugin + run: make clean && ./configure --enable-fldigi-xmlrpc --enable-python-plugin && make -j2 - name: Run tests run: make check; rc=$?; cat test/run_*.log; exit $rc diff --git a/configure.ac b/configure.ac index 684597836..2d87b86f0 100644 --- a/configure.ac +++ b/configure.ac @@ -172,7 +172,7 @@ AS_IF([test "x$wantpythonplugin" = xtrue], [ dnl Look for Python AC_MSG_RESULT([yes]) - AM_PATH_PYTHON([3.0]) + AM_PATH_PYTHON([3.9]) python_main_ver=`$PYTHON -c "import sys; print(sys.version.split('.')[[0]])"` python_sub_ver=`$PYTHON -c "import sys; print(sys.version.split('.')[[1]])"` diff --git a/rules/cqp/cqp_ca b/rules/cqp/cqp_ca new file mode 100644 index 000000000..f1e12980b --- /dev/null +++ b/rules/cqp/cqp_ca @@ -0,0 +1,63 @@ +######################## +# Provided by: Victor Denisov N6DVS +# Last Verified: 2022-10-16 +######################## +# +CONTEST=California_QSO_Party +CONTEST_MODE +LOGFILE=cqp.log +CABRILLO=UNIVERSAL +CWPOINTS=3 +SSBPOINTS=2 +SERIAL+SECTION +MULT_LIST=cqp_mults +SECTION_MULT_ONCE +NO_RST +LEADING_ZEROS_SERIAL=OFF +# +################################## +# # +# Messages F1= to F12= # +# Message CQ_TU_MSG= # +# Message S&P_TU_MSG= # +# # +# % = call # +# @ = hiscall # +# # = serial # +# [ = RST # +# + = increase cw speed # +# - = decrease cw speed # +# # +################################## +# +F1=cq cqp % +F2=@ DE % +F3=@ # trin +F4=TU +F5=@ +F6=% +F7=@ SRI QSO B4 GL +F8=AGN +F9= ? +F10= QRZ? +F11= PSE K +F12=cq cqp % +# +CQ_TU_MSG=TU % +S&P_TU_MSG=TU # trin +# +#ALT_0= +ALT_1=QTH TRIN TRIN +ALT_2=QTH? +ALT_3=NR # # +ALT_4=NR? +#ALT_5= +#ALT_6= +#ALT_7= +#ALT_8= +#ALT_9= +# +#SEND_DE +# +CABRILLO-QSO-FORMAT=CQP +####### END ##################### diff --git a/rules/cqp/cqp_ca_mults b/rules/cqp/cqp_ca_mults new file mode 100644 index 000000000..5379009ed --- /dev/null +++ b/rules/cqp/cqp_ca_mults @@ -0,0 +1,76 @@ +# This mult file is suitable for a California station. +# Mults are states and counties are aliases for California. +# If you operate outside of California your mults will be California counties. +######################### +## California Counties ## +######################### +CA:ALAM,ALPI,AMAD,BUTT,CALA,COLU,CCOS,DELN,ELDO,FRES,GLEN,HUMB,IMPE,INYO,KERN,KING,LAKE,LASS,LANG,MADE,MARN,MARP,MEND,MERC,MODO,MONO,MONT,NAPA,NEVA,ORAN,PLAC,PLUM,RIVE,SACR,SBEN,SBER,SDIE,SFRA,SJOA,SLUI,SMAT,SBAR,SCLA,SCRU,SHAS,SIER,SISK,SOLA,SONO,STAN,SUTT,TEHA,TRIN,TULA,TUOL,VENT,YOLO,YUBA +################### +## United States ## +################### +AL +AK +AZ +AR +CO +CT +DE +FL +GA +HI +ID +IL +IN +IA +KS +KY +LA +ME +MD +MA +MI +MN +MS +MO +MT +NE +NV +NH +NJ +NM +NY +NC +ND +OH +OK +OR +PA +RI +SC +SD +TN +TX +UT +VT +VA +WA +WV +WI +WY +############ +## Canada ## +############ +MR:NB,NL,NS,PE +# Maritimes (NB, NL, NS, PE) +QC +ON +MB +SK +AB +BC +NT:NU,YT +# Northern Territories (NT, NU, YT) +######## +## DX ## +######## +# All other locations diff --git a/rules/hadx/hadx b/rules/hadx/hadx new file mode 100644 index 000000000..fcded4400 --- /dev/null +++ b/rules/hadx/hadx @@ -0,0 +1,61 @@ +########################## +# HA-DX contest # +########################## +# https://ha-dx.com/en/contest-rules +# + +LOGFILE=hadx.log +CONTEST_MODE + +CABRILLO-CONTEST= HA-DX +CABRILLO-CATEGORY-BAND= (SOSB, SOAB, SO3BAND, YOUTH6H, MS) +CABRILLO-CATEGORY-MODE= (CW, SSB, MIXED) +CABRILLO-CATEGORY-TRANSMITTER= (ONE, MULTI) + +CABRILLO-CATEGORY-STATION=- +CABRILLO-CATEGORY-OVERLAY=- +CABRILLO-CATEGORY-TIME=- +CABRILLO-LOCATION=- +CABRILLO-OFFTIME=- + +#================================ +# Select one of following blocks: +#-------------------------------- +# +# non-HA stations send serial number +# +CABRILLO-EXCHANGE=# +F3=@ ++5NN-- # +S&P_TU_MSG=TU ++5NN-- # +MY_COUNTRY_POINTS=2 +#-------------------------------- +# +# magyar állomások megyét adnak +# +##CABRILLO-EXCHANGE=XX +##F3=@ ++5NN-- XX +##S&P_TU_MSG=TU ++5NN-- XX +##MY_COUNTRY_POINTS=10 +#================================ + +INITIAL_EXCHANGE=hadx.txt +RECALL_MULTS + +# Scoring: +# Contact with a Hungarian station: 10 points +# Contact with a station on your continent: 2 points +# Contact with a station on another continent: 5 points +# +COUNTRY_LIST_POINTS=10 +COUNTRYLIST=HA + +MY_CONTINENT_POINTS=2 +DX_POINTS=5 + +# Multipliers: +# worked DXCC + WAE list countries (exception HA) and Hungarian counties +# per band, regardless of mode. +# (handled by hadx.py) + +GENERIC_MULT=BAND + diff --git a/rules/hadx/hadx.py b/rules/hadx/hadx.py new file mode 100644 index 000000000..935657c94 --- /dev/null +++ b/rules/hadx/hadx.py @@ -0,0 +1,22 @@ +""" +HA-DX contest +https://ha-dx.com/en/contest-rules +""" + +HA_COUNTIES = ['BN', 'BA', 'BE', 'BO', 'CS', 'FE', 'GY', 'HB', 'HE', + 'SZ', 'KO', 'NG', 'PE', 'SO', 'SA', 'TO', 'VA', 'VE', 'ZA', 'BP'] + +def check_exchange(qso): + dxcc = tlf.get_dxcc(qso.call) + + if dxcc.main_prefix == 'HA': + exchange = qso.exchange.strip() + if exchange in HA_COUNTIES: + mult = f'HA/{exchange}' + else: + mult = '' # invalid county + else: + mult = dxcc.main_prefix + + return {'mult1_value': mult} + diff --git a/rules/mwc/mwc b/rules/mwc/mwc new file mode 100644 index 000000000..1ee42a8c2 --- /dev/null +++ b/rules/mwc/mwc @@ -0,0 +1,32 @@ +# OK1WC Memorial (MWC) +# https://memorial-ok1wc.cz/index.php?page=rules2l + +CONTEST=mwc +LOGFILE=mwc.log + +CONTEST_MODE +SERIAL_EXCHANGE + +# scoring +ONE_POINT + +# mults handled by mwc.py +GENERIC_MULT = BAND + + +CABRILLO-CONTEST=MWC +CABRILLO-CATEGORY-MODE=CW +CABRILLO-CATEGORY-BAND=(80M,40M,ALL) +CABRILLO-CATEGORY-POWER=(LOW,QRP) + +# fixed Cabrillo fields +CABRILLO-CATEGORY-OPERATOR=SINGLE-OP +CABRILLO-CATEGORY-TRANSMITTER=ONE +CABRILLO-OPERATORS=- +CABRILLO-CATEGORY-STATION=- +CABRILLO-CATEGORY-TIME=- +CABRILLO-CATEGORY-OVERLAY=- +CABRILLO-OFFTIME=- +CABRILLO-LOCATION=- +CABRILLO-CLUB=- + diff --git a/rules/mwc/mwc.py b/rules/mwc/mwc.py new file mode 100644 index 000000000..b003f91f6 --- /dev/null +++ b/rules/mwc/mwc.py @@ -0,0 +1,19 @@ +""" +OK1WC Memorial (MWC) +https://memorial-ok1wc.cz/index.php?page=rules2l +""" + +import re + +# match trailing call modifier (e.g. /8, /P, /MM, /QRP, etc.) +MODIFIER_PATTERN = re.compile('/(\d|[A-Z]+)$') + +def check_exchange(qso): + call = MODIFIER_PATTERN.sub('', qso.call) # remove modifier + if len(call) > 1: + mult = call[-1] # last character of the call + else: + mult = '' + + return {'mult1_value': mult} + diff --git a/rules/tesla.py b/rules/tesla.py index 29fbbd3b5..dba01e13a 100644 --- a/rules/tesla.py +++ b/rules/tesla.py @@ -1,5 +1,4 @@ import re -import tlf # e.g. 023 KN03 XCHG_PATTERN = re.compile('\d+\s*(\w{2}\d{2})') diff --git a/share/cabrillo.fmt b/share/cabrillo.fmt index 9f88cd26c..aa9e704c1 100644 --- a/share/cabrillo.fmt +++ b/share/cabrillo.fmt @@ -48,3 +48,7 @@ QSO=FREQ,5;MODE,2;DATE,10;TIME,4;MYCALL,13;RST_S,3;EXC_S,9;HISCALL,13;RST_R,3;EX # space and slash are both separators EXCHANGE-SEPARATOR=\s/ QSO=FREQ,5;MODE,2;DATE,10;TIME,4;MYCALL,13;RST_S,3;EXC_S,9;HISCALL,13;RST_R,3;EXC1,4;EXC2,4;EXC3,4 + +# Califonia QSO Party +[CQP] +QSO=FREQ,5;MODE,2;DATE,10;TIME,4;MYCALL,13;EXC_S,14;HISCALL,13;EXCH,14 diff --git a/src/Makefile.am b/src/Makefile.am index c5f35584a..76552dce8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,7 +23,7 @@ tlf_SOURCES = \ logit.c logview.c \ main.c makelogline.c messagechange.c muf.c \ nicebox.c note.c netkeyer.c\ - paccdx.c parse_logcfg.c plugin.c prevqso.c printcall.c \ + paccdx.c parse_logcfg.c plugin.c printcall.c \ qrb.c qsonr_to_str.c qtc_log.c qtcwin.c qtcutil.c readcabrillo.c \ readcalls.c readqtccalls.c readctydata.c recall_exchange.c rules.c \ rtty.c \ @@ -60,7 +60,7 @@ noinst_HEADERS = \ log_to_disk.h logit.h logview.h \ makelogline.h messagechange.h muf.h \ nicebox.h note.h netkeyer.h\ - paccdx.h parse_logcfg.h plugin.h prevqso.h printcall.h \ + paccdx.h parse_logcfg.h plugin.h printcall.h \ qrb.h qsonr_to_str.h qtc_log.h qtcvars.h qtcwin.h qtcutil.h \ readcalls.h readqtccalls.h readctydata.h recall_exchange.h \ rules.h readcabrillo.h rtty.h \ diff --git a/src/addmult.c b/src/addmult.c index 76837e2e9..c471c917a 100644 --- a/src/addmult.c +++ b/src/addmult.c @@ -336,7 +336,7 @@ void add_mult_line(char *line) { int init_and_load_multipliers(void) { FILE *cfp; - char s_inputbuffer[186] = ""; + char s_inputbuffer[2000] = ""; char *mults_location; if (mults_possible) { @@ -364,7 +364,7 @@ int init_and_load_multipliers(void) { return 0; // couldn't open file } - while (fgets(s_inputbuffer, 85, cfp) != NULL) { + while (fgets(s_inputbuffer, sizeof(s_inputbuffer), cfp) != NULL) { /* strip leading and trailing whitespace */ g_strstrip(s_inputbuffer); diff --git a/src/addpfx.c b/src/addpfx.c index 80e2cc0af..39453d3d7 100644 --- a/src/addpfx.c +++ b/src/addpfx.c @@ -82,7 +82,7 @@ bool pfx_is_new_on(char *prefix, int bandindex) { int add_pfx(char *pxstr, unsigned int bandindex) { - extern int pfxmultab; + extern bool pfxmultab; int q = 0, found = 0, bandfound = 0; prefixes_worked[nr_of_px].pfx[0] = '\0'; diff --git a/src/addspot.c b/src/addspot.c index 9fbe001ff..ffc6a2d30 100644 --- a/src/addspot.c +++ b/src/addspot.c @@ -100,7 +100,7 @@ void add_local_spot(void) { /* find last qso record in qso_array, return NULL if no one found */ static struct qso_t *find_last_qso() { - int i = nr_qsos; + int i = NR_QSOS; while (i > 0) { struct qso_t *last_qso = g_ptr_array_index(qso_array, i-1); if (!last_qso->is_comment) { diff --git a/src/bandmap.h b/src/bandmap.h index 770510d93..eecae8ede 100644 --- a/src/bandmap.h +++ b/src/bandmap.h @@ -23,7 +23,6 @@ #include #include -#include "tlf.h" typedef struct { char *call; diff --git a/src/callinput.c b/src/callinput.c index 8e247758c..bb7f38313 100644 --- a/src/callinput.c +++ b/src/callinput.c @@ -58,7 +58,6 @@ #include "netkeyer.h" #include "nicebox.h" // Includes curses.h #include "note.h" -#include "prevqso.h" #include "printcall.h" #include "qtcvars.h" // Includes globalvars.h #include "qtcwin.h" @@ -174,7 +173,8 @@ int callinput(void) { show_rtty(); } - if (digikeyer == FLDIGI && fldigi_set_callfield == 1 && current_qso.call[0] != '\0') { + if (digikeyer == FLDIGI && fldigi_set_callfield == 1 + && current_qso.call[0] != '\0') { freqstore = freq; fldigi_set_callfield = 0; } @@ -196,7 +196,8 @@ int callinput(void) { /* if BMAUTOGRAB is active in S&P mode and input field is empty and a spot has * not already been grabbed here check if a spot is on freq * and pick it up if one found */ - if (bmautograb && cqmode == S_P && *current_qso.call == '\0' && grab.state == NONE) { + if (bmautograb && cqmode == S_P && *current_qso.call == '\0' + && grab.state == NONE) { get_spot_on_qrg(grab.call, freq); if (strlen(grab.call) >= 3) { g_strlcpy(current_qso.call, grab.call, CALL_SIZE); @@ -572,7 +573,11 @@ int callinput(void) { // Alt-0 to Alt-9 (M-0...M-9), send CW/Digimode messages 15-24. case 176 ... 185: { - send_standard_message(x - 162); /* alt-0 to alt-9 */ + if (*current_qso.call == '\0') { + send_standard_message_prev_qso(x - 162); // alt-0 to alt-9 + } else { + send_standard_message(x - 162); /* alt-0 to alt-9 */ + } break; } @@ -601,7 +606,12 @@ int callinput(void) { // F2-F11, send messages 2 through 11. case KEY_F(2) ... KEY_F(11): { - send_standard_message(x - KEY_F(1)); // F2...F11 - F1 = 1...10 + // F2...F11 - F1 = 1...10 + if (*current_qso.call == '\0') { + send_standard_message_prev_qso(x - KEY_F(1)); + } else { + send_standard_message(x - KEY_F(1)); + } break; } @@ -774,26 +784,55 @@ int callinput(void) { break; } + case CTRL_U: + /* wipe out or restore call input and comment field */ + if (current_qso.call[0] != '\0' || + current_qso.comment[0] != '\0') { + /* wipe out any content */ + cleanup_hiscall(); + cleanup_comment(); + rst_reset(); + + } else { + /* restore content */ + restore_hiscall(); + restore_comment(); + } + + clear_display(); + break; + + case CTRL_W: + /* wipe out or restore call input field */ + if (current_qso.call[0] != '\0') { + cleanup_hiscall(); + } else { + restore_hiscall(); + } + + break; + // , clear call input or stop sending. case ESCAPE: { - if (early_started == 0) { - /* if CW not started early drop call and start anew */ - cleanup(); - clear_display(); - } else { - /* otherwise just stop sending */ - stoptx(); - *hiscall_sent = '\0'; - early_started = 0; + if (!stop_tx_only) { + if (!early_started) { + /* if CW not started early drop call and start anew */ + cleanup(); + clear_display(); + } + freqstore = 0; } - freqstore = 0; break; } // Underscore, confirm last exchange. case '_': { - prev_qso(); + if (S_P == cqmode) { + send_standard_message_prev_qso(SP_TU_MSG); + } else { + send_standard_message_prev_qso(2); + } break; } @@ -1058,10 +1097,10 @@ int autosend() { int x; int char_sent; - early_started = 1; - sending_call = 1; + early_started = true; + sending_call = true; sendmessage(current_qso.call); - sending_call = 0; + sending_call = false; strcpy(hiscall_sent, current_qso.call); char_sent = 0; /* no char sent so far */ @@ -1103,7 +1142,7 @@ int autosend() { if (x == ESCAPE) { stoptx(); *hiscall_sent = '\0'; - early_started = 0; + early_started = false; continue; } diff --git a/src/changepars.c b/src/changepars.c index d94d6a47e..5b0b3329b 100644 --- a/src/changepars.c +++ b/src/changepars.c @@ -512,13 +512,13 @@ int changepars(void) { case 41: { /* SYNC */ if (strlen(synclogfile) > 0) synclog(synclogfile); - nr_qsos = log_read_n_score(); + log_read_n_score(); scroll_log(); clear_display(); break; } case 42: { /* RESCORE */ - nr_qsos = log_read_n_score(); + log_read_n_score(); clear_display(); scroll_log(); break; diff --git a/src/cleanup.c b/src/cleanup.c index f2a14798e..f8175702d 100644 --- a/src/cleanup.c +++ b/src/cleanup.c @@ -23,38 +23,75 @@ *--------------------------------------------------------------*/ +#include + +#include "getexchange.h" #include "globalvars.h" #include "change_rst.h" +#include "recall_exchange.h" #include "tlf_curses.h" #include "ui_utils.h" #include "write_keyer.h" -void cleanup_qso(void) { - current_qso.call[0] = '\0'; /* reset current call and comment */ +gchar *comment_backup = NULL; +gchar *call_backup = NULL; + +/* reset comment */ +void cleanup_comment(void) { + g_free(comment_backup); + comment_backup = g_strdup(current_qso.comment); + current_qso.comment[0] = '\0'; current_qso.normalized_comment[0] = '\0'; - proposed_exchange[0] = '\0'; - rst_reset();; /* reset to 599 */ - countrynr = 0; } -void cleanup(void) { - extern int defer_store; +/* restore comment */ +void restore_comment(void) { + if (comment_backup) { + g_strlcpy(current_qso.comment, comment_backup, sizeof(current_qso.comment)); + g_free(comment_backup); + comment_backup = NULL; + } + checkexchange(¤t_qso, true); +} - attron(modify_attr(COLOR_PAIR(NORMCOLOR))); - mvaddstr(12, 29, spaces(12)); +/* reset hiscall */ +void cleanup_hiscall(void) { + g_free(call_backup); + call_backup = g_strdup(current_qso.call); - attron(COLOR_PAIR(C_WINDOW)); - mvaddstr(12, 54, spaces(contest->exchange_width)); + current_qso.call[0] = '\0'; /* reset current call and comment */ + proposed_exchange[0] = '\0'; +} - attron(COLOR_PAIR(C_LOG | A_STANDOUT)); - for (int k = 1; k <= 5; k++) { - mvaddstr(k, 0, spaces(40)); +/* restore call */ +void restore_hiscall(void) { + if (call_backup) { + g_strlcpy(current_qso.call, call_backup, sizeof(current_qso.call)); + g_free(call_backup); + call_backup = NULL; } + get_proposed_exchange(); +} + + +void cleanup_qso(void) { + cleanup_hiscall(); + cleanup_comment(); + + g_free(comment_backup); + comment_backup = NULL; + + g_free(call_backup); + call_backup = NULL; + + rst_reset();; /* reset to 599 */ +} + +void cleanup(void) { + extern int defer_store; - refreshp(); cleanup_qso(); defer_store = 0; keyer_flush(); - } diff --git a/src/cleanup.h b/src/cleanup.h index 461960aa7..9c3a4cce0 100644 --- a/src/cleanup.h +++ b/src/cleanup.h @@ -21,6 +21,10 @@ #ifndef CLEANUP_H #define CLEANUP_H +void cleanup_comment(void); +void restore_comment(void); +void cleanup_hiscall(void); +void restore_hiscall(void); void cleanup_qso(void); void cleanup(void); diff --git a/src/deleteqso.c b/src/deleteqso.c index 964b58150..dd0f83489 100644 --- a/src/deleteqso.c +++ b/src/deleteqso.c @@ -99,7 +99,7 @@ void delete_last_qtcs(char *call, char *bandmode) { if ((int)qstatbuf.st_size > QTCSENTCALLPOS) { look = 1; qtclen = 0; - s = nr_qsos; + s = NR_QSOS; while (s >= 0 && qsoflags_for_qtc[s] != 1) { s--; } @@ -170,7 +170,7 @@ void delete_qso(void) { fsync(lfile); close(lfile); - nr_qsos = log_read_n_score(); + log_read_n_score(); } scroll_log(); } diff --git a/src/edit_last.c b/src/edit_last.c index 9ea22d9aa..1e078951a 100644 --- a/src/edit_last.c +++ b/src/edit_last.c @@ -116,7 +116,7 @@ static void flash_field(int row, int column, char *value) { /* get a copy of selected QSO into the buffer */ static void get_qso(int nr, char *buffer) { - assert(nr < nr_qsos); + assert(nr < NR_QSOS); strcpy(buffer, QSOS(nr)); assert(strlen(buffer) == (LOGLINELEN - 1)); } @@ -126,7 +126,7 @@ static void putback_qso(int nr, char *buffer) { FILE *fp; assert(strlen(buffer) == (LOGLINELEN - 1)); - assert(nr < nr_qsos); + assert(nr < NR_QSOS); if ((fp = fopen(logfile, "r+")) == NULL) { TLF_LOG_WARN("Can not open logfile..."); @@ -168,13 +168,13 @@ static void check_store_and_get_next_line(int direction) { } unhighlight_line(editline, editbuffer); if (changed) { - putback_qso(nr_qsos - (NR_LINES - editline), editbuffer); + putback_qso(NR_QSOS - (NR_LINES - editline), editbuffer); needs_rescore = true; changed = false; } if (direction != 0) { editline += direction; - get_qso(nr_qsos - (NR_LINES - editline), editbuffer); + get_qso(NR_QSOS - (NR_LINES - editline), editbuffer); } } @@ -183,12 +183,12 @@ void edit_last(void) { int j, b, k; - if (nr_qsos == 0) + if (NR_QSOS == 0) return; /* nothing to edit */ stop_background_process(); // note: this freezes nr_qsos, as network is paused - const int topline = MAX(NR_LINES - nr_qsos, 0); + const int topline = (NR_LINES > NR_QSOS ? NR_LINES - NR_QSOS : 0); // set current end of exchange field fields[FIELD_INDEX_EXCHANGE].end = fields[FIELD_INDEX_EXCHANGE].start + contest->exchange_width - 1; @@ -199,7 +199,7 @@ void edit_last(void) { /* start with last QSO */ editline = NR_LINES - 1; - get_qso(nr_qsos - (NR_LINES - editline), editbuffer); + get_qso(NR_QSOS - (NR_LINES - editline), editbuffer); changed = false; needs_rescore = false; diff --git a/src/editlog.c b/src/editlog.c index cc04c1500..8039c988d 100644 --- a/src/editlog.c +++ b/src/editlog.c @@ -76,7 +76,7 @@ void logedit(void) { edit(logfile); checklogfile(); - nr_qsos = log_read_n_score(); + log_read_n_score(); start_background_process(); diff --git a/src/genqtclist.c b/src/genqtclist.c index bda3baed1..4c0ee486c 100644 --- a/src/genqtclist.c +++ b/src/genqtclist.c @@ -63,7 +63,7 @@ int genqtclist(char *callsign, int nrofqtc) { s = next_qtc_qso; - while (qtclist.count < qtclistlen && s < nr_qsos) { + while (qtclist.count < qtclistlen && s < NR_QSOS) { if (strlen(callsign) == 0 || strncmp(QSOS(s) + 29, callsign, strlen(callsign)) != 0) { /* exclude current callsign */ diff --git a/src/getexchange.c b/src/getexchange.c index 6709f291c..41941ff37 100644 --- a/src/getexchange.c +++ b/src/getexchange.c @@ -34,6 +34,7 @@ #include "audio.h" #include "cw_utils.h" #include "change_rst.h" +#include "cleanup.h" #include "globalvars.h" #include "keyer.h" #include "keystroke_names.h" @@ -55,6 +56,7 @@ #include "tlf_curses.h" #include "ui_utils.h" #include "addmult.h" +#include "plugin.h" #include "getexchange.h" @@ -136,7 +138,7 @@ int getexchange(void) { if (call_update && strlen(current_qso.callupdate) >= 3) { strcpy(current_qso.call, current_qso.callupdate); - current_qso.callupdate[0] = 0; + current_qso.callupdate[0] = 0; printcall(); } @@ -193,16 +195,57 @@ int getexchange(void) { break; } + case CTRL_U: + /* wipe out or restore call input and comment field */ + if (current_qso.call[0] != '\0' || + current_qso.comment[0] != '\0') { + /* wipe out any content */ + cleanup_hiscall(); + cleanup_comment(); + rst_reset(); + + x = TAB; /* back to call input field */ + + } + + break; + + case CTRL_W: { + /* wipe out or restore exchange field */ + if (current_qso.comment[0] != '\0') { + cleanup_comment(); + i = 0; + } else { + restore_comment(); + i = strlen(current_qso.comment); + } + break; + } + + case ESCAPE: { // stoptx(); /* stop sending CW */ - if (current_qso.comment[0] != '\0') { /* if comment not empty */ - /* drop exchange so far */ - current_qso.comment[0] = '\0'; - i = 0; + if (!stop_tx_only) { + if (current_qso.comment[0] != '\0') { /* if comment not empty */ + /* drop exchange so far */ + cleanup_comment(); + i = 0; + } else { + /* back to callinput */ + x = TAB; // + } + } + break; + } + + // Underscore, confirm last exchange. + case '_': { + if (S_P == cqmode) { + send_standard_message_prev_qso(SP_TU_MSG); } else { - /* back to callinput */ - x = TAB; // + send_standard_message_prev_qso(2); } + break; } @@ -258,7 +301,11 @@ int getexchange(void) { case KEY_F(2) ... KEY_F(11): { /* F2...F11 - F1 = 1...10 */ - send_standard_message(x - KEY_F(1)); + if (*current_qso.call == '\0') { + send_standard_message_prev_qso(x - KEY_F(1)); + } else { + send_standard_message(x - KEY_F(1)); + } break; } @@ -537,7 +584,7 @@ static void checkexchange_cqww(struct qso_t *qso, bool interactive) { // get call fix index = g_match_info_fetch(match_info, 2); if (index != NULL) { - g_strlcpy(qso->callupdate, index, MAX_CALL_LENGTH + 1); + g_strlcpy(qso->callupdate, index, MAX_CALL_LENGTH + 1); } g_free(index); } @@ -600,7 +647,7 @@ static void checkexchange_arrlss(struct qso_t *qso, bool interactive) { // get call update index = g_match_info_fetch(match_info, 3); if (index != NULL && strchr("AKNWVC", index[0]) != NULL) { // US/CA only - g_strlcpy(qso->callupdate, index, MAX_CALL_LENGTH + 1); + g_strlcpy(qso->callupdate, index, MAX_CALL_LENGTH + 1); } g_free(index); @@ -632,7 +679,8 @@ static void checkexchange_arrlss(struct qso_t *qso, bool interactive) { OnLowerSearchPanel(8, buf); } - sprintf(qso->normalized_comment, "%s %s %s %s", serial, precedent, check, qso->section); + sprintf(qso->normalized_comment, "%s %s %s %s", serial, precedent, check, + qso->section); g_strlcpy(qso->mult1_value, qso->section, MULT_SIZE); // multiplier: section } @@ -679,19 +727,19 @@ static void checkexchange_serial_section(struct qso_t *qso, bool interactive) { // get call update index = g_match_info_fetch(match_info, 3); if (index != NULL) { - g_strlcpy(qso->callupdate, index, MAX_CALL_LENGTH + 1); + g_strlcpy(qso->callupdate, index, MAX_CALL_LENGTH + 1); } g_free(index); } g_match_info_free(match_info); if (serial_grid4_mult) { - if (!check_qra(qso->section)) { - qso->section[0] = 0; - } - if (strlen(qso->section) > 4) { - qso->section[4] = 0; // mult is the first 4 chars only - } + if (!check_qra(qso->section)) { + qso->section[0] = 0; + } + if (strlen(qso->section) > 4) { + qso->section[4] = 0; // mult is the first 4 chars only + } } if (interactive) { @@ -737,7 +785,7 @@ static void checkexchange_sectn_mult(struct qso_t *qso, bool interactive) { // get call update index = g_match_info_fetch(match_info, 2); if (index != NULL) { - g_strlcpy(qso->callupdate, index, MAX_CALL_LENGTH + 1); + g_strlcpy(qso->callupdate, index, MAX_CALL_LENGTH + 1); } g_free(index); } @@ -765,22 +813,27 @@ static void checkexchange_sectn_mult(struct qso_t *qso, bool interactive) { void checkexchange(struct qso_t *qso, bool interactive) { // create fields if (qso->callupdate == NULL) { - qso->callupdate = g_malloc0(MAX_CALL_LENGTH + 1); + qso->callupdate = g_malloc0(MAX_CALL_LENGTH + 1); } if (qso->normalized_comment == NULL) { - qso->normalized_comment = g_malloc0(COMMENT_SIZE); + qso->normalized_comment = g_malloc0(COMMENT_SIZE); } if (qso->section == NULL) { - qso->section = g_malloc0(MAX_SECTION_LENGTH + 1); + qso->section = g_malloc0(MAX_SECTION_LENGTH + 1); } if (qso->mult1_value == NULL) { - qso->mult1_value = g_malloc0(MULT_SIZE); + qso->mult1_value = g_malloc0(MULT_SIZE); } qso->callupdate[0] = 0; qso->normalized_comment[0] = 0; qso->mult1_value[0] = 0; + if (plugin_has_check_exchange()) { + plugin_check_exchange(qso); + return; + } + // ----------------------------cqww------------------------------ if (CONTEST_IS(CQWW)) { @@ -895,7 +948,8 @@ void exchange_edit(void) { if (strlen(current_qso.comment) < contest->exchange_width) { /* copy including trailing \0 */ - strncpy(comment2, current_qso.comment + b, strlen(current_qso.comment) - (b - 1)); + strncpy(comment2, current_qso.comment + b, + strlen(current_qso.comment) - (b - 1)); current_qso.comment[b] = i; current_qso.comment[b + 1] = '\0'; diff --git a/src/gettxinfo.c b/src/gettxinfo.c index b4dfb2bc1..0cc2d1934 100644 --- a/src/gettxinfo.c +++ b/src/gettxinfo.c @@ -115,7 +115,9 @@ void gettxinfo(void) { /* CAT PTT wanted, available, inactive, and PTT On requested */ if (rigptt == (CAT_PTT_USE | CAT_PTT_ON)) { + pthread_mutex_lock(&rig_lock); retval = rig_set_ptt(my_rig, RIG_VFO_CURR, RIG_PTT_ON); + pthread_mutex_unlock(&rig_lock); /* Set PTT active bit. */ rigptt |= CAT_PTT_ACTIVE; @@ -127,7 +129,9 @@ void gettxinfo(void) { /* CAT PTT wanted, available, active and PTT Off requested */ if (rigptt == (CAT_PTT_USE | CAT_PTT_ACTIVE | CAT_PTT_OFF)) { + pthread_mutex_lock(&rig_lock); retval = rig_set_ptt(my_rig, RIG_VFO_CURR, RIG_PTT_OFF); + pthread_mutex_unlock(&rig_lock); /* Clear PTT Off requested bit. */ rigptt &= ~CAT_PTT_OFF; @@ -148,12 +152,22 @@ void gettxinfo(void) { } last_freq_time = now; + pthread_mutex_lock(&rig_lock); retval = rig_get_vfo(my_rig, &vfo); /* initialize RIG_VFO_CURR */ + pthread_mutex_unlock(&rig_lock); + if (retval == RIG_OK || retval == -RIG_ENIMPL || retval == -RIG_ENAVAIL) { + pthread_mutex_lock(&rig_lock); retval = rig_get_freq(my_rig, RIG_VFO_CURR, &rigfreq); + pthread_mutex_unlock(&rig_lock); + if (trxmode == DIGIMODE && (digikeyer == GMFSK || digikeyer == FLDIGI) && retval == RIG_OK) { + + pthread_mutex_lock(&rig_lock); retvalmode = rig_get_mode(my_rig, RIG_VFO_CURR, &rigmode, &bwidth); + pthread_mutex_unlock(&rig_lock); + if (retvalmode != RIG_OK) { rigmode = RIG_MODE_NONE; } @@ -165,8 +179,10 @@ void gettxinfo(void) { if (rigmode == RIG_MODE_RTTY || rigmode == RIG_MODE_RTTYR) { fldigi_shift_freq = fldigi_get_shift_freq(); if (fldigi_shift_freq != 0) { + pthread_mutex_lock(&rig_lock); retval = rig_set_freq(my_rig, RIG_VFO_CURR, ((freq_t)rigfreq + (freq_t)fldigi_shift_freq)); + pthread_mutex_unlock(&rig_lock); } } } @@ -209,7 +225,9 @@ void gettxinfo(void) { } else if (reqf == SETCWMODE) { + pthread_mutex_lock(&rig_lock); retval = rig_set_mode(my_rig, RIG_VFO_CURR, RIG_MODE_CW, get_cw_bandwidth()); + pthread_mutex_unlock(&rig_lock); if (retval != RIG_OK) { TLF_LOG_WARN("Problem with rig link: %s", rigerror(retval)); @@ -217,8 +235,10 @@ void gettxinfo(void) { } else if (reqf == SETSSBMODE) { + pthread_mutex_lock(&rig_lock); retval = rig_set_mode(my_rig, RIG_VFO_CURR, get_ssb_mode(), TLF_DEFAULT_PASSBAND); + pthread_mutex_unlock(&rig_lock); if (retval != RIG_OK) { TLF_LOG_WARN("Problem with rig link: %s", rigerror(retval)); @@ -232,15 +252,19 @@ void gettxinfo(void) { else new_mode = RIG_MODE_LSB; } + pthread_mutex_lock(&rig_lock); retval = rig_set_mode(my_rig, RIG_VFO_CURR, new_mode, TLF_DEFAULT_PASSBAND); + pthread_mutex_unlock(&rig_lock); if (retval != RIG_OK) { TLF_LOG_WARN("Problem with rig link: %s", rigerror(retval)); } } else if (reqf == RESETRIT) { + pthread_mutex_lock(&rig_lock); retval = rig_set_rit(my_rig, RIG_VFO_CURR, 0); + pthread_mutex_unlock(&rig_lock); if (retval != RIG_OK) { TLF_LOG_WARN("Problem with rig link: %s", rigerror(retval)); @@ -249,7 +273,9 @@ void gettxinfo(void) { } else { // set rig frequency (or carrier) to `reqf' reqf -= fldigi_get_carrier(); + pthread_mutex_lock(&rig_lock); retval = rig_set_freq(my_rig, RIG_VFO_CURR, (freq_t) reqf); + pthread_mutex_unlock(&rig_lock); if (retval != RIG_OK) { TLF_LOG_WARN("Problem with rig link: set frequency: %s", rigerror(retval)); @@ -290,7 +316,9 @@ static void handle_trx_bandswitch(const freq_t freq) { return; // no change was requested } + pthread_mutex_lock(&rig_lock); int retval = rig_set_mode(my_rig, RIG_VFO_CURR, mode, width); + pthread_mutex_unlock(&rig_lock); if (retval != RIG_OK) { TLF_LOG_WARN("Problem with rig link: %s", rigerror(retval)); diff --git a/src/globalvars.h b/src/globalvars.h index 801c8f2fe..b033e5a33 100644 --- a/src/globalvars.h +++ b/src/globalvars.h @@ -14,8 +14,6 @@ extern mystation_t my; // all about my station extern char whichcontest[]; extern contest_config_t *contest; // contest configuration -extern int nr_qsos; // number of lines in qsos[] - extern GPtrArray *qso_array; // array of parsed QSOs // note that not every log line needs // to be a QSO, it could also be a @@ -106,6 +104,7 @@ extern int highqsonr; extern RIG *my_rig; +extern pthread_mutex_t rig_lock; extern cqmode_t cqmode; extern int trxmode; extern int myrig_model; @@ -116,7 +115,6 @@ extern bool cqwwm2; extern char lastcall[]; extern char recvd_rst[]; extern char sent_rst[]; -extern char last_rst[]; extern bool wazmult; extern int addcallarea; extern int new_cty; @@ -144,13 +142,14 @@ extern int xplanet; extern int cwkeyer; extern int digikeyer; extern int cwstart; -extern int early_started; +extern bool early_started; +extern bool stop_tx_only; extern int zonedisplay; extern int rigptt; extern int k_ptt; extern int k_pin14; extern int tune_seconds; -extern int sending_call; +extern bool sending_call; extern int exclude_multilist_type; extern bool partials; extern bool use_part; @@ -222,7 +221,6 @@ extern char exchange_list[40]; extern char rttyoutput[]; extern char spot_ptr[MAX_SPOTS][82]; extern char lastmsg[]; -extern char exchange[40]; #ifdef HAVE_LIBXMLRPC extern char fldigi_url[50]; #endif @@ -231,6 +229,9 @@ extern char *cabrillo; extern char *editor_cmd; extern char *rigportname; extern char *config_file; +#ifdef HAVE_PYTHON +extern char *plugin_config; +#endif extern int bandindexarray[]; extern int tlfcolors[8][2]; @@ -243,7 +244,7 @@ extern bool mult_side; extern bool countrylist_only; extern bool mixedmode; extern bool qso_once; -extern bool noleadingzeros; +extern bool leading_zeros_serial; extern bool ignoredupe; extern bool continentlist_only; extern bool debugflag; diff --git a/src/hamlib_keyer.c b/src/hamlib_keyer.c index 728235893..cdb79903d 100644 --- a/src/hamlib_keyer.c +++ b/src/hamlib_keyer.c @@ -21,45 +21,51 @@ #include #include "globalvars.h" +#include "sendqrg.h" #include "hamlib_keyer.h" -bool rig_has_send_morse() { - return (my_rig->caps->send_morse != NULL); -} - -bool rig_has_stop_morse() { -#if HAMLIB_VERSION >= 400 - return (my_rig->caps->stop_morse != NULL); -#else - return false; -#endif -} - int hamlib_keyer_set_speed(int cwspeed) { value_t spd; spd.i = cwspeed; - return rig_set_level(my_rig, RIG_VFO_CURR, RIG_LEVEL_KEYSPD, spd); + pthread_mutex_lock(&rig_lock); + int ret = rig_set_level(my_rig, RIG_VFO_CURR, RIG_LEVEL_KEYSPD, spd); + pthread_mutex_unlock(&rig_lock); + + return ret; } -int hamlib_keyer_get_speed( int *cwspeed) { +int hamlib_keyer_get_speed(int *cwspeed) { value_t value; - assert (cwspeed != NULL); + assert(cwspeed != NULL); + + pthread_mutex_lock(&rig_lock); int ret = rig_get_level(my_rig, RIG_VFO_CURR, RIG_LEVEL_KEYSPD, &value); + pthread_mutex_unlock(&rig_lock); + if (ret == RIG_OK) *cwspeed = value.i; return ret; } int hamlib_keyer_send(char *cwmessage) { - return rig_send_morse(my_rig, RIG_VFO_CURR, cwmessage); + pthread_mutex_lock(&rig_lock); + int ret = rig_send_morse(my_rig, RIG_VFO_CURR, cwmessage); + pthread_mutex_unlock(&rig_lock); + + return ret; } int hamlib_keyer_stop() { +#if HAMLIB_VERSION >= 400 if (rig_has_stop_morse()) { - return rig_stop_morse(my_rig, RIG_VFO_CURR); - } else { - return RIG_OK; + pthread_mutex_lock(&rig_lock); + int ret = rig_stop_morse(my_rig, RIG_VFO_CURR); + pthread_mutex_unlock(&rig_lock); + return ret; } +#endif + + return RIG_OK; } diff --git a/src/hamlib_keyer.h b/src/hamlib_keyer.h index 4eaef408d..2ae53be9a 100644 --- a/src/hamlib_keyer.h +++ b/src/hamlib_keyer.h @@ -17,8 +17,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -bool rig_has_send_morse(); -bool rig_has_stop_morse(); int hamlib_keyer_set_speed(int cwspeed); int hamlib_keyer_get_speed( int *cwspeed); int hamlib_keyer_send(char *cwmessage); diff --git a/src/keyer.c b/src/keyer.c index 1358e21ef..41a7f4a84 100644 --- a/src/keyer.c +++ b/src/keyer.c @@ -246,50 +246,26 @@ void keyer(void) { move(cury, curx); break; } - case KEY_F(2): { - send_keyer_message(1); /* F2 */ - break; - } - case KEY_F(3): { - send_keyer_message(2); /* F3 */ - break; - } - case KEY_F(4): { - send_keyer_message(3); /* F4 */ - break; - } - case KEY_F(5): { - send_keyer_message(4); /* F5 */ - break; - } - case KEY_F(6): { - send_keyer_message(5); /* F6 */ - break; - } - case KEY_F(7): { - send_keyer_message(6); /* F7 */ - break; - } - case KEY_F(8): { - send_keyer_message(7); /* F8 */ - break; - } - case KEY_F(9): { - send_keyer_message(8); /* F9 */ - break; - } - case KEY_F(10): { - send_keyer_message(9); /* F10 */ - break; - } - case KEY_F(11): { - send_keyer_message(10); /* F11 */ + case KEY_F(2) ... KEY_F(11): { + if (*current_qso.call == '\0') { + send_standard_message_prev_qso(x - KEY_F(1)); + } else { + send_standard_message(x - KEY_F(1)); + } break; } - case KEY_F(12): { - send_keyer_message(11); /* F12 */ + + // Underscore, confirm last exchange. + case '_': { + if (S_P == cqmode) { + send_standard_message_prev_qso(SP_TU_MSG); + } else { + send_standard_message_prev_qso(2); + } + break; } + case KEY_BACKSPACE: { keyer_append_char('\b'); /* ASCII BS */ break; diff --git a/src/keystroke_names.h b/src/keystroke_names.h index 23a2c9266..757213875 100644 --- a/src/keystroke_names.h +++ b/src/keystroke_names.h @@ -41,7 +41,9 @@ #define CTRL_R 18 #define CTRL_S 19 #define CTRL_T 20 +#define CTRL_U 21 #define CTRL_V 22 +#define CTRL_W 23 /* Keyboard characters */ #define ESCAPE 27 diff --git a/src/last10.c b/src/last10.c index c66322534..d481cbb54 100644 --- a/src/last10.c +++ b/src/last10.c @@ -38,13 +38,13 @@ int last10(void) { int thisband; struct qso_t *qso; - if (nr_qsos < 10) + if (NR_QSOS < 10) return (-1); thisband = atoi(band[bandinx]); /* look backwards in actual band for QSOs */ - for (index = nr_qsos - 1; index >= 0; index--) { + for (index = NR_QSOS - 1; index >= 0; index--) { qso = g_ptr_array_index(qso_array, index); if (thisband == qso->band) { diff --git a/src/log_to_disk.c b/src/log_to_disk.c index 9ac5e519c..774cb8ac8 100644 --- a/src/log_to_disk.c +++ b/src/log_to_disk.c @@ -75,7 +75,6 @@ void log_to_disk(int from_lan) { /* remember call and report for resend after qso (see callinput.c) */ strcpy(lastcall, current_qso.call); - strcpy(last_rst, sent_rst); // use normalized comment if available if (strlen(current_qso.normalized_comment) > 0) { @@ -91,7 +90,7 @@ void log_to_disk(int from_lan) { char *logline = makelogline(qso); qso->logline = logline; /* remember formatted line in qso entry */ - store_qso(logline); + store_qso(logfile, logline); //TODO: create a copy of current_qso g_ptr_array_add(qso_array, qso); @@ -122,7 +121,7 @@ void log_to_disk(int from_lan) { addcall2(); - store_qso(lan_logline); + store_qso(logfile, lan_logline); g_ptr_array_add(qso_array, qso); } diff --git a/src/main.c b/src/main.c index fa9a85b79..84ae17113 100644 --- a/src/main.c +++ b/src/main.c @@ -142,8 +142,8 @@ bool serial_section_mult = false; bool serial_or_section = false; /* exchange is serial OR section, like HA-DX */ bool serial_grid4_mult = false; bool qso_once = false; -bool noleadingzeros; -bool ctcomp = false; +bool leading_zeros_serial; +bool ctcomp; bool nob4 = false; // allow auto b4 bool ignoredupe = false; int dupe = 0; @@ -176,7 +176,6 @@ rmode_t rigmode = RIG_MODE_NONE; bool mixedmode = false; char sent_rst[4] = "599"; char recvd_rst[4] = "599"; -char last_rst[4] = "599"; /* Report for last QSO */ int shortqsonr = LONGCW; /* 1 = short cw char in exchange */ int cluster = NOCLUSTER; /* 0 = OFF, 1 = FOLLOW, 2 = spots 3 = all */ @@ -188,7 +187,6 @@ bool demode = false; /* send DE before s&p call */ int announcefilter = FILTER_ANN; /* filter cluster announcements */ bool showscore_flag = false; /* show score window */ -char exchange[40]; int defer_store = 0; mystation_t my; /* all info about me */ @@ -265,9 +263,11 @@ char hiscall_sent[20] = ""; /**< part which was sent during early int cwstart = 0; /**< number characters after which sending call started automatically, 0 - off, -1 - manual start */ -int sending_call = 0; -int early_started = 0; /**< 1 if sending call started early, +bool sending_call = false; +bool early_started = false; /**< 1 if sending call started early, strlen(hiscall)>cwstart or 'space' */ +bool stop_tx_only = false; /**< ESC should stop only tx */ + char lastcall[20]; char lastqsonr[5]; char qsonrstr[5] = "0001"; @@ -331,6 +331,7 @@ bool bmautograb = false; /*-------------------------------------rigctl-------------------------------*/ int myrig_model = 0; /* unset */ RIG *my_rig; /* handle to rig (instance) */ +pthread_mutex_t rig_lock = PTHREAD_MUTEX_INITIALIZER; rmode_t rmode; /* radio mode of operation */ pbwidth_t width; vfo_t vfo; /* vfo selection */ @@ -349,7 +350,6 @@ char fldigi_url[50] = "http://localhost:7362/RPC2"; /*----------------------------the parsed log lines-------------------------*/ // array of qso's GPtrArray *qso_array; -int nr_qsos = 0; /*------------------------------dupe array---------------------------------*/ int nr_worked = 0; /**< number of calls in worked[] */ @@ -421,7 +421,16 @@ static struct termios oldt, newt; const char *argp_program_version = "tlf-" VERSION; const char *argp_program_bug_address = ""; static const char program_description[] = - "tlf - contest logging program for amateur radio operators"; + "tlf - contest logging program for amateur radio operators" + "\v" // "post-doc" separator + "Features:" +#ifdef HAVE_LIBXMLRPC + " fldigi-xmlrpc" +#endif +#ifdef HAVE_PYTHON + " python-plugin" +#endif +; static const struct argp_option options[] = { { "config", 'f', "FILE", 0, @@ -628,6 +637,7 @@ static void init_variables() { unique_call_multi = MULT_NONE; generic_mult = MULT_NONE; + leading_zeros_serial = true; ctcomp = false; resend_call = RESEND_NOT_SET; @@ -649,7 +659,9 @@ static void init_variables() { } FREE_DYNAMIC_STRING(cabrillo); - +#ifdef HAVE_PYTHON + FREE_DYNAMIC_STRING(plugin_config); +#endif } /** load all databases @@ -736,8 +748,6 @@ static int databases_load() { static void hamlib_init() { - rig_set_debug(RIG_DEBUG_NONE); - if (no_trx_control) { trx_control = false; } @@ -957,6 +967,8 @@ static void tlf_cleanup() { } #endif + plugin_close(); + endwin(); tcsetattr(STDIN_FILENO, TCSANOW, &oldt); @@ -1020,6 +1032,7 @@ int main(int argc, char *argv[]) { showmsg(""); memset(&my, 0, sizeof(my)); + rig_set_debug(RIG_DEBUG_NONE); // disable Hamlib messages total = 0; if (databases_load() == EXIT_FAILURE) { @@ -1063,7 +1076,7 @@ int main(int argc, char *argv[]) { /* read the logfile and rebuild point and multiplier scoring */ /* see also log_read_n_score() for non-interactive variant */ - nr_qsos = readcalls(logfile, true); + readcalls(logfile, true); if (qtcdirection > 0) { readqtccalls(); } diff --git a/src/muf.c b/src/muf.c index 31bf9701f..fd5d2bb44 100644 --- a/src/muf.c +++ b/src/muf.c @@ -198,8 +198,7 @@ int t; double xn, xs, ls, h, ff, x, yn_, k, lm, u, a; -static double power(man, ex) -double man, ex; +static double power(double man, double ex) { return exp(ex * log(man)); } diff --git a/src/note.c b/src/note.c index cce0a76d2..58a24b376 100644 --- a/src/note.c +++ b/src/note.c @@ -31,6 +31,7 @@ #include "log_utils.h" #include "nicebox.h" // Includes curses.h #include "scroll_log.h" +#include "store_qso.h" void include_note(void) { @@ -41,7 +42,6 @@ void include_note(void) { char buffer2[LOGLINELEN + 1] = ""; int i; - FILE *fp; attron(A_STANDOUT); mvprintw(15, 1, @@ -65,19 +65,10 @@ void include_note(void) { (LOGLINELEN - 1) - strlen(buffer2)); /* fill spaces */ buffer2[LOGLINELEN - 1] = '\0'; - if ((fp = fopen(logfile, "a")) == NULL) { - endwin(); - fprintf(stdout, "\nnote.c: Error opening log file.\n"); - exit(1); - } - fputs(buffer2, fp); - fputs("\n", fp); - - fclose(fp); + store_qso(logfile, buffer2); struct qso_t *qso = parse_qso(buffer2); g_ptr_array_add(qso_array, qso); - nr_qsos++; scroll_log(); clear_display(); diff --git a/src/parse_logcfg.c b/src/parse_logcfg.c index ed05d6b7c..9481a01c6 100644 --- a/src/parse_logcfg.c +++ b/src/parse_logcfg.c @@ -141,6 +141,12 @@ int getidxbybandstr(char *confband) { return -1; } +static void str_toupper(char *str) { + for (char *p = str; *p; ++p) { + *p = g_ascii_toupper(*p); + } +} + //////////////////// // global variables for matcher functions: GMatchInfo *match_info; @@ -177,14 +183,43 @@ static int parse_int(const char *string, gint64 min, gint64 max, int *result) { return PARSE_OK; } -int cfg_bool_const(const cfg_arg_t arg) { - *arg.bool_p = arg.bool_value; +static int parse_bool(const char *string, bool *result) { + + gchar *str = NULL; + if (string != NULL) { + str = g_strdup(string); + g_strstrip(str); + str_toupper(str); // normalize to upper case + } + + bool value; + + if (str == NULL || str[0] == 0 // no or empty input is true + || strcmp(str, "TRUE") == 0 + || strcmp(str, "YES") == 0 + || strcmp(str, "ON") == 0) { + value = true; + } else if (strcmp(str, "FALSE") == 0 + || strcmp(str, "NO") == 0 + || strcmp(str, "OFF") == 0) { + value = false; + } else { + g_free(str); + return PARSE_WRONG_PARAMETER; + } + + g_free(str); + + *result = value; return PARSE_OK; } -int cfg_contest_bool_const(const cfg_arg_t arg) { - *(bool *)((char *)contest + arg.offset) = arg.bool_value; - return PARSE_OK; +int cfg_bool(const cfg_arg_t arg) { + return parse_bool(parameter, arg.bool_p); +} + +int cfg_contest_bool(const cfg_arg_t arg) { + return parse_bool(parameter, (bool *)((char *)contest + arg.offset)); } int cfg_int_const(const cfg_arg_t arg) { @@ -298,9 +333,7 @@ static int cfg_call(const cfg_arg_t arg) { return PARSE_WRONG_PARAMETER; } - for (char *p = my.call; *p; ++p) { - *p = g_ascii_toupper(*p); - } + str_toupper(my.call); return PARSE_OK; } @@ -1094,57 +1127,59 @@ static int cfg_resend_call(const cfg_arg_t arg) { } static config_t logcfg_configs[] = { - {"CONTEST_MODE", CFG_BOOL_TRUE(iscontest)}, - {"MIXED", CFG_BOOL_TRUE(mixedmode)}, - {"IGNOREDUPE", CFG_BOOL_TRUE(ignoredupe)}, - {"USE_CONTINENTLIST_ONLY", CFG_BOOL_TRUE(continentlist_only)}, - {"RADIO_CONTROL", CFG_BOOL_TRUE(trx_control)}, - {"PORTABLE_MULT_2", CFG_BOOL_TRUE(portable_x2)}, - - {"USEPARTIALS", CFG_BOOL_TRUE(use_part)}, - {"PARTIALS", CFG_BOOL_TRUE(partials)}, - {"RECALL_MULTS", CFG_CONTEST_BOOL_TRUE(recall_mult)}, - {"WYSIWYG_MULTIBAND", CFG_BOOL_TRUE(wysiwyg_multi)}, - {"WYSIWYG_ONCE", CFG_BOOL_TRUE(wysiwyg_once)}, - {"RIT_CLEAR", CFG_BOOL_TRUE(rit)}, + {"CONTEST_MODE", CFG_BOOL(iscontest)}, + {"MIXED", CFG_BOOL(mixedmode)}, + {"IGNOREDUPE", CFG_BOOL(ignoredupe)}, + {"USE_CONTINENTLIST_ONLY", CFG_BOOL(continentlist_only)}, + {"RADIO_CONTROL", CFG_BOOL(trx_control)}, + {"PORTABLE_MULT_2", CFG_BOOL(portable_x2)}, + + {"USEPARTIALS", CFG_BOOL(use_part)}, + {"PARTIALS", CFG_BOOL(partials)}, + {"RECALL_MULTS", CFG_CONTEST_BOOL(recall_mult)}, + {"WYSIWYG_MULTIBAND", CFG_BOOL(wysiwyg_multi)}, + {"WYSIWYG_ONCE", CFG_BOOL(wysiwyg_once)}, + {"RIT_CLEAR", CFG_BOOL(rit)}, {"SHORT_SERIAL", CFG_INT_ONE(shortqsonr)}, - {"SCOREWINDOW", CFG_BOOL_TRUE(showscore_flag)}, - {"CHECKWINDOW", CFG_BOOL_TRUE(searchflg)}, - {"SEND_DE", CFG_BOOL_TRUE(demode)}, - {"SERIAL_EXCHANGE", CFG_CONTEST_BOOL_TRUE(exchange_serial)}, - {"COUNTRY_MULT", CFG_BOOL_TRUE(country_mult)}, - {"CQWW_M2", CFG_BOOL_TRUE(cqwwm2)}, - {"LAN_DEBUG", CFG_BOOL_TRUE(landebug)}, - {"CALLUPDATE", CFG_BOOL_TRUE(call_update)}, - {"TIME_MASTER", CFG_BOOL_TRUE(time_master)}, - {"CTCOMPATIBLE", CFG_BOOL_TRUE(ctcomp)}, - {"SERIAL\\+SECTION", CFG_BOOL_TRUE(serial_section_mult)}, - {"SECTION_MULT", CFG_BOOL_TRUE(sectn_mult)}, - {"NOB4", CFG_BOOL_TRUE(nob4)}, - {"SHOW_TIME", CFG_BOOL_TRUE(show_time)}, - {"RXVT", CFG_BOOL_TRUE(use_rxvt)}, - {"WAZMULT", CFG_BOOL_TRUE(wazmult)}, - {"ITUMULT", CFG_BOOL_TRUE(itumult)}, - {"CONTINENT_EXCHANGE", CFG_BOOL_TRUE(exc_cont)}, - {"NOAUTOCQ", CFG_BOOL_TRUE(noautocq)}, - {"NO_BANDSWITCH_ARROWKEYS", CFG_BOOL_TRUE(no_arrows)}, - {"SOUNDCARD", CFG_BOOL_TRUE(sc_sidetone)}, - {"LOWBAND_DOUBLE", CFG_BOOL_TRUE(lowband_point_mult)}, - {"CLUSTER_LOG", CFG_BOOL_TRUE(clusterlog)}, - {"SERIAL\\+GRID4", CFG_BOOL_TRUE(serial_grid4_mult)}, - {"LOGFREQUENCY", CFG_BOOL_TRUE(logfrequency)}, - {"NO_RST", CFG_BOOL_TRUE(no_rst)}, - {"SERIAL_OR_SECTION", CFG_BOOL_TRUE(serial_or_section)}, - {"PFX_MULT", CFG_BOOL_TRUE(pfxmult)}, - {"PFX_MULT_MULTIBAND", CFG_BOOL_TRUE(pfxmultab)}, - {"QTCREC_RECORD", CFG_BOOL_TRUE(qtcrec_record)}, - {"QTC_AUTO_FILLTIME", CFG_BOOL_TRUE(qtc_auto_filltime)}, - {"QTC_RECV_LAZY", CFG_BOOL_TRUE(qtc_recv_lazy)}, - {"BMAUTOGRAB", CFG_BOOL_TRUE(bmautograb)}, - {"BMAUTOADD", CFG_BOOL_TRUE(bmautoadd)}, - {"SPRINTMODE", CFG_BOOL_TRUE(sprint_mode)}, - {"KEYER_BACKSPACE", CFG_BOOL_TRUE(keyer_backspace)}, - {"SECTION_MULT_ONCE", CFG_BOOL_TRUE(sectn_mult_once)}, + {"LEADING_ZEROS_SERIAL", CFG_BOOL(leading_zeros_serial)}, + {"SCOREWINDOW", CFG_BOOL(showscore_flag)}, + {"CHECKWINDOW", CFG_BOOL(searchflg)}, + {"SEND_DE", CFG_BOOL(demode)}, + {"SERIAL_EXCHANGE", CFG_CONTEST_BOOL(exchange_serial)}, + {"COUNTRY_MULT", CFG_BOOL(country_mult)}, + {"CQWW_M2", CFG_BOOL(cqwwm2)}, + {"LAN_DEBUG", CFG_BOOL(landebug)}, + {"CALLUPDATE", CFG_BOOL(call_update)}, + {"TIME_MASTER", CFG_BOOL(time_master)}, + {"CTCOMPATIBLE", CFG_BOOL(ctcomp)}, + {"SERIAL\\+SECTION", CFG_BOOL(serial_section_mult)}, + {"SECTION_MULT", CFG_BOOL(sectn_mult)}, + {"NOB4", CFG_BOOL(nob4)}, + {"SHOW_TIME", CFG_BOOL(show_time)}, + {"RXVT", CFG_BOOL(use_rxvt)}, + {"WAZMULT", CFG_BOOL(wazmult)}, + {"ITUMULT", CFG_BOOL(itumult)}, + {"CONTINENT_EXCHANGE", CFG_BOOL(exc_cont)}, + {"NOAUTOCQ", CFG_BOOL(noautocq)}, + {"NO_BANDSWITCH_ARROWKEYS", CFG_BOOL(no_arrows)}, + {"SOUNDCARD", CFG_BOOL(sc_sidetone)}, + {"LOWBAND_DOUBLE", CFG_BOOL(lowband_point_mult)}, + {"CLUSTER_LOG", CFG_BOOL(clusterlog)}, + {"SERIAL\\+GRID4", CFG_BOOL(serial_grid4_mult)}, + {"LOGFREQUENCY", CFG_BOOL(logfrequency)}, + {"NO_RST", CFG_BOOL(no_rst)}, + {"SERIAL_OR_SECTION", CFG_BOOL(serial_or_section)}, + {"PFX_MULT", CFG_BOOL(pfxmult)}, + {"PFX_MULT_MULTIBAND", CFG_BOOL(pfxmultab)}, + {"QTCREC_RECORD", CFG_BOOL(qtcrec_record)}, + {"QTC_AUTO_FILLTIME", CFG_BOOL(qtc_auto_filltime)}, + {"QTC_RECV_LAZY", CFG_BOOL(qtc_recv_lazy)}, + {"BMAUTOGRAB", CFG_BOOL(bmautograb)}, + {"BMAUTOADD", CFG_BOOL(bmautoadd)}, + {"SPRINTMODE", CFG_BOOL(sprint_mode)}, + {"KEYER_BACKSPACE", CFG_BOOL(keyer_backspace)}, + {"SECTION_MULT_ONCE", CFG_BOOL(sectn_mult_once)}, + {"ESC_STOPS_TX_ONLY", CFG_BOOL(stop_tx_only)}, {"F([1-9]|1[0-2])", CFG_MESSAGE(message, -1)}, // index is 1-based {"S&P_TU_MSG", CFG_MESSAGE(message, SP_TU_MSG)}, @@ -1220,6 +1255,9 @@ static config_t logcfg_configs[] = { {"SOUNDLOG_PLAY_COMMAND", CFG_STRING(soundlog_play_cmd)}, {"SOUNDLOG_RECORD_COMMAND", CFG_STRING(soundlog_record_cmd)}, {"SOUNDLOG_DIRECTORY", CFG_STRING(soundlog_dir)}, +#ifdef HAVE_PYTHON + {"PLUGIN_CONFIG", CFG_STRING(plugin_config)}, +#endif {"RIGPORT", CFG_STRING_NOCHOMP(rigportname)}, {"CLUSTERLOGIN", CFG_STRING_STATIC_NOCHOMP(clusterlogin, 80)}, diff --git a/src/parse_logcfg.h b/src/parse_logcfg.h index 3afc30e95..50dd2d82d 100644 --- a/src/parse_logcfg.h +++ b/src/parse_logcfg.h @@ -67,7 +67,6 @@ typedef struct { }; union { // extra info int int_value; - bool bool_value; struct { int string_type; int base, size; @@ -86,12 +85,11 @@ typedef struct { cfg_arg_t arg; } config_t; -#define CFG_BOOL_TRUE(var) NO_PARAM, cfg_bool_const, \ - (cfg_arg_t){.bool_p=&var, .bool_value=true} +#define CFG_BOOL(var) OPTIONAL_PARAM, cfg_bool, \ + (cfg_arg_t){.bool_p=&var} -#define CFG_CONTEST_BOOL_TRUE(var) NO_PARAM, cfg_contest_bool_const, \ - (cfg_arg_t){.offset=offsetof(contest_config_t, var), \ - .bool_value=true} +#define CFG_CONTEST_BOOL(var) OPTIONAL_PARAM, cfg_contest_bool, \ + (cfg_arg_t){.offset=offsetof(contest_config_t, var)} #define CFG_INT_CONST(var,n) NO_PARAM, cfg_int_const, \ (cfg_arg_t){.int_p=&var, .int_value=n} diff --git a/src/plugin.c b/src/plugin.c index 40cc179e2..17681f31b 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -15,13 +15,17 @@ #include "startmsg.h" #include "utils.h" #include "qrb.h" +#include "getctydata.h" -// hacks: -#define PARSE_OK 0 -#define PARSE_ERROR 1 -#define BANDINDEX_ANY (-1) +#include "parse_logcfg.h" // for PARSE_OK/PARSE_ERROR -// python3-dev +/* +TODO: + - add mutex to plugin_* functions + - test plugin execution +*/ + +// needs python3-dev #ifdef HAVE_PYTHON static PyObject *pModule, *pDict, *pTlf; @@ -33,30 +37,32 @@ static PyObject *pModule, *pDict, *pTlf; bool plugin_has_##name() { return (pf_##name != NULL); } //== +PLUGIN_FUNC(init) PLUGIN_FUNC(setup) PLUGIN_FUNC(score) - -PLUGIN_FUNC(is_multi) -PLUGIN_FUNC(nr_of_mults) -PLUGIN_FUNC(get_multi) +PLUGIN_FUNC(check_exchange) #ifdef HAVE_PYTHON + +char *plugin_config = NULL; + static PyStructSequence_Desc qso_descr = { - .name = "qso", + .name = "Qso", .doc = "QSO data", .fields = (PyStructSequence_Field[]) { {.name = "band"}, {.name = "call"}, {.name = "mode"}, {.name = "exchange"}, - // utc, ... + {.name = "utc"}, + // ... {.name = NULL} // guard }, .n_in_sequence = 4 }; static PyStructSequence_Desc qrb_descr = { - .name = "qrb", + .name = "Qrb", .doc = "QRB data", .fields = (PyStructSequence_Field[]) { {.name = "distance"}, @@ -66,8 +72,37 @@ static PyStructSequence_Desc qrb_descr = { .n_in_sequence = 2 }; +static PyStructSequence_Desc dxcc_descr = { + .name = "Dxcc", + .doc = "DXCC data", + .fields = (PyStructSequence_Field[]) { + // dxcc_data fields: + {.name = "country_name"}, + {.name = "main_prefix"}, + {.name = "main_cq_zone"}, + {.name = "main_itu_zone"}, + {.name = "main_latitude"}, + {.name = "main_longitude"}, + {.name = "main_continent"}, + {.name = "main_timezone"}, + {.name = "starred"}, + // prefix_data fields: + {.name = "prefix"}, + {.name = "cq_zone"}, + {.name = "itu_zone"}, + {.name = "latitude"}, + {.name = "longitude"}, + {.name = "continent"}, + {.name = "timezone"}, + {.name = "exact"}, + {.name = NULL} // guard + }, + .n_in_sequence = 9 + 8 +}; + static PyTypeObject *qso_type; static PyTypeObject *qrb_type; +static PyTypeObject *dxcc_type; static PyObject *py_get_qrb_for_locator(PyObject *self, PyObject *args) { const char *locator; @@ -82,15 +117,47 @@ static PyObject *py_get_qrb_for_locator(PyObject *self, PyObject *args) { Py_RETURN_NONE; } - //return Py_BuildValue("{s:d,s:d}", "distance", distance, "bearing", bearing); PyObject *py_qrb = PyStructSequence_New(qrb_type); PyStructSequence_SetItem(py_qrb, 0, Py_BuildValue("d", distance)); PyStructSequence_SetItem(py_qrb, 1, Py_BuildValue("d", bearing)); return py_qrb; } +static PyObject *py_get_dxcc(PyObject *self, PyObject *args) { + char *call; + + if (!PyArg_ParseTuple(args, "s", &call)) + return NULL; + + prefix_data *prefix = getctyinfo(call); + dxcc_data *dxcc = dxcc_by_index(prefix->dxcc_ctynr); + + PyObject *py_dxcc = PyStructSequence_New(dxcc_type); + PyStructSequence_SetItem(py_dxcc, 0, Py_BuildValue("s", dxcc->countryname)); + PyStructSequence_SetItem(py_dxcc, 1, Py_BuildValue("s", dxcc->pfx)); + PyStructSequence_SetItem(py_dxcc, 2, Py_BuildValue("i", dxcc->cq)); + PyStructSequence_SetItem(py_dxcc, 3, Py_BuildValue("i", dxcc->itu)); + PyStructSequence_SetItem(py_dxcc, 4, Py_BuildValue("d", dxcc->lat)); + PyStructSequence_SetItem(py_dxcc, 5, Py_BuildValue("d", dxcc->lon)); + PyStructSequence_SetItem(py_dxcc, 6, Py_BuildValue("s", dxcc->continent)); + PyStructSequence_SetItem(py_dxcc, 7, Py_BuildValue("d", dxcc->timezone)); + PyStructSequence_SetItem(py_dxcc, 8, Py_BuildValue("N", + PyBool_FromLong(dxcc->starred))); + PyStructSequence_SetItem(py_dxcc, 9, Py_BuildValue("s", prefix->pfx)); + PyStructSequence_SetItem(py_dxcc, 10, Py_BuildValue("i", prefix->cq)); + PyStructSequence_SetItem(py_dxcc, 11, Py_BuildValue("i", prefix->itu)); + PyStructSequence_SetItem(py_dxcc, 12, Py_BuildValue("d", prefix->lat)); + PyStructSequence_SetItem(py_dxcc, 13, Py_BuildValue("d", prefix->lon)); + PyStructSequence_SetItem(py_dxcc, 14, Py_BuildValue("s", prefix->continent)); + PyStructSequence_SetItem(py_dxcc, 15, Py_BuildValue("d", prefix->timezone)); + PyStructSequence_SetItem(py_dxcc, 16, Py_BuildValue("N", + PyBool_FromLong(prefix->exact))); + return py_dxcc; +} + static PyMethodDef tlf_methods[] = { { "get_qrb_for_locator", py_get_qrb_for_locator, METH_VARARGS, "Get QRB for given locator" }, + { "get_dxcc", py_get_dxcc, METH_VARARGS, "Get DXCC information for given call" }, { NULL, NULL, 0, NULL } }; @@ -101,20 +168,21 @@ static struct PyModuleDef tlf_module = { .m_methods = tlf_methods }; -PyMODINIT_FUNC PyModInit_tlf(void) { +PyMODINIT_FUNC pyModInit_tlf(void) { PyObject *tlf = PyModule_Create(&tlf_module); PyModule_AddIntMacro(tlf, CWMODE); PyModule_AddIntMacro(tlf, SSBMODE); - PyModule_AddIntConstant(tlf, "BAND_ANY", BANDINDEX_ANY); + PyModule_AddIntMacro(tlf, DIGIMODE); + PyModule_AddObject(tlf, "MY_CALL", PyUnicode_FromString(my.call)); PyModule_AddObject(tlf, "MY_LAT", PyFloat_FromDouble(my.Lat)); PyModule_AddObject(tlf, "MY_LONG", PyFloat_FromDouble(my.Long)); - //... + PyModule_AddType(tlf, qso_type); + PyModule_AddType(tlf, qrb_type); + PyModule_AddType(tlf, dxcc_type); return tlf; } -#endif -#ifdef HAVE_PYTHON static void lookup_function(const char *name, PyObject **pf_ptr) { // pf_ptr is a borrowed reference *pf_ptr = PyDict_GetItemString(pDict, name); @@ -123,6 +191,137 @@ static void lookup_function(const char *name, PyObject **pf_ptr) { *pf_ptr = NULL; } } + +// +// copy a string out of a Python dict: +// *dest = dict[name] +// +// if *dest is NULL then it is allocated accordingly +// and must be freed by the caller +// +static void copy_dict_string(PyObject *dict, const char *name, + char **dest, int size) { + // po is a borrowed reference + PyObject *po = PyDict_GetItemString(dict, name); + if (po == NULL) { + return; // no such entry + } + if (*dest == NULL) { + *dest = g_malloc0(size); + } + g_strlcpy(*dest, PyUnicode_AsUTF8(po), size); +} + +#endif + +int parse_version(const char *version, int *major, int *minor) { + char *v = g_strdup(version); + g_strstrip(v); + + if (v[0] == 0) { // version is empty + g_free(v); + *major = 0; + *minor = 0; + return PARSE_OK; + } + + int result = PARSE_ERROR; // default: failure + + // format: .[ignored_trailing_chars] + GRegex *regex = g_regex_new("^(\\d+)\\.(\\d+)", 0, 0, NULL); + GMatchInfo *match_info; + g_regex_match(regex, v, 0, &match_info); + if (g_match_info_matches(match_info)) { + char *major_str = g_match_info_fetch(match_info, 1); + char *minor_str = g_match_info_fetch(match_info, 2); + *major = atoi(major_str); + *minor = atoi(minor_str); + result = PARSE_OK; // success + g_free(major_str); + g_free(minor_str); + } + g_match_info_free(match_info); + g_regex_unref(regex); + g_free(v); + + return result; +} + + +#define ERROR_WRONG_VERSION1 10 +#define ERROR_WRONG_VERSION2 20 +// returns -1/0/+1, or one of the above error codes +int compare_versions(const char *version1, const char *version2) { + int v1_major, v1_minor; + int v2_major, v2_minor; + + if (parse_version(version1, &v1_major, &v1_minor) != PARSE_OK) { + return ERROR_WRONG_VERSION1; + } + if (parse_version(version2, &v2_major, &v2_minor) != PARSE_OK) { + return ERROR_WRONG_VERSION2; + } + + if (v1_major > v2_major) { + return 1; + } + if (v1_major < v2_major) { + return -1; + } + if (v1_minor > v2_minor) { + return 1; + } + if (v1_minor < v2_minor) { + return -1; + } + return 0; +} + +#ifdef HAVE_PYTHON +// call init if available +static int call_init() { + if (pf_init == NULL) { + return PARSE_OK; + } + + char *cfg = (plugin_config != NULL ? plugin_config : ""); + PyObject *args = Py_BuildValue("(s)", cfg); + PyObject *pValue = PyObject_CallObject(pf_init, args); + Py_DECREF(args); + + if (NULL != PyErr_Occurred()) { + showmsg("Error: "); + PyErr_Print(); + return PARSE_ERROR; + } + + if (pValue == NULL || pValue == Py_None) { + // no check needed + } else if (PyUnicode_Check(pValue)) { + const char *required_version = PyUnicode_AsUTF8(pValue); + int rc = compare_versions(VERSION, required_version); + if (rc == ERROR_WRONG_VERSION2) { + showstring("Error: Unparseable required version:", required_version); + return PARSE_ERROR; + } else if (rc == ERROR_WRONG_VERSION1) { // should not happen + showmsg("Error: Internal error parsing version: " VERSION); + return PARSE_ERROR; + } else if (rc < 0) { + char *msg = g_strdup_printf("Plugin requires version %s but you are running " + VERSION ". Please upgrade TLF.", + required_version); + showmsg(msg); + g_free(msg); + return PARSE_ERROR; + } + } else { + showmsg("Wrong return type for init()"); + return PARSE_ERROR; + } + + Py_XDECREF(pValue); + return PARSE_OK; +} #endif int plugin_init(const char *name) { @@ -150,9 +349,14 @@ int plugin_init(const char *name) { g_free(path); // Initialize the Python Interpreter - PyImport_AppendInittab("tlf", &PyModInit_tlf); // declare tlf module + PyImport_AppendInittab("tlf", &pyModInit_tlf); // declare tlf module Py_Initialize(); + // build interface types + qso_type = PyStructSequence_NewType(&qso_descr); + qrb_type = PyStructSequence_NewType(&qrb_descr); + dxcc_type = PyStructSequence_NewType(&dxcc_descr); + pTlf = PyImport_ImportModule("tlf"); if (pTlf == NULL) { PyErr_Print(); @@ -163,7 +367,7 @@ int plugin_init(const char *name) { PyRun_SimpleString(set_path); // set module search path g_free(set_path); - // Load the module object + // Load the plugin module object PyObject *pName = PyUnicode_FromString(name); pModule = PyImport_Import(pName); Py_DECREF(pName); @@ -173,57 +377,21 @@ int plugin_init(const char *name) { return PARSE_ERROR; } - PyModule_AddObject(pModule, "tlf", pTlf); + PyModule_AddObject(pModule, "tlf", pTlf); // for 'import tlf' // pDict is a borrowed reference pDict = PyModule_GetDict(pModule); - PyObject *pf_init; lookup_function("init", &pf_init); lookup_function("setup", &pf_setup); lookup_function("score", &pf_score); - lookup_function("is_multi", &pf_is_multi); - lookup_function("get_multi", &pf_get_multi); - - if (pf_setup == NULL) { - showmsg("ERROR: missing setup() in plugin"); - return PARSE_ERROR; - } + lookup_function("check_exchange", &pf_check_exchange); - // call init if available - if (pf_init != NULL) { - PyObject *pValue = PyObject_CallObject(pf_init, NULL); - // ...check exception.... - Py_XDECREF(pValue); + int rc = call_init(); + if (rc != PARSE_OK) { + return rc; } - - // check add_qso ? - - // build interface types - qso_type = PyStructSequence_NewType(&qso_descr); - qrb_type = PyStructSequence_NewType(&qrb_descr); - -#if 0 - plugin_setup(); - - plugin_add_qso(" 80CW 04-Jan-21 16:30 0001 OK1AY 599 599 003 0 3540.0"); - plugin_add_qso(" 80CW 04-Jan-21 16:30 0001 OK1Z 599 599 003 0 3540.0"); - plugin_add_qso(" 80CW 04-Jan-21 16:30 0001 HA8QSY 599 599 003 0 3540.0"); - - int n = plugin_nr_of_mults(BANDINDEX_ANY); - printf("n=%d\n", n); - - printf("S51Z multi: %d\n", plugin_is_multi(1, "S51Z", CWMODE)); - printf("9A1A multi: %d\n", plugin_is_multi(1, "9A1A", CWMODE)); - - char *m = plugin_get_multi("YT1W", CWMODE); - printf("multi: |%s|\n", m); - g_free(m); - - exit(0); -#endif - showmsg("Loaded plugin"); #endif @@ -232,24 +400,22 @@ int plugin_init(const char *name) { void plugin_close() { #ifdef HAVE_PYTHON - // Clean up - Py_DECREF(pModule); - //FIXME check other pointers - - // Finish the Python Interpreter - Py_Finalize(); + if (pModule != NULL) { + // Finish the Python Interpreter + Py_Finalize(); + } #endif } void plugin_setup() { #ifdef HAVE_PYTHON PyObject *pValue = PyObject_CallObject(pf_setup, NULL); - //printf("after pf_setup, pValue %s NULL\n", pValue == NULL ? "is" : "not"); Py_XDECREF(pValue); if (NULL != PyErr_Occurred()) { PyErr_Print(); - // FIXME: action? + sleep(2); + exit(1); } #endif } @@ -261,6 +427,7 @@ static PyObject *create_py_qso(struct qso_t *qso) { PyStructSequence_SetItem(py_qso, 1, Py_BuildValue("s", qso->call)); PyStructSequence_SetItem(py_qso, 2, Py_BuildValue("i", qso->mode)); PyStructSequence_SetItem(py_qso, 3, Py_BuildValue("s", qso->comment)); + PyStructSequence_SetItem(py_qso, 4, Py_BuildValue("i", qso->timestamp)); return py_qso; } #endif @@ -275,7 +442,7 @@ int plugin_score(struct qso_t *qso) { Py_DECREF(py_qso); if (pValue != NULL) { - result = PyLong_AsLong(pValue); + result = PyLong_AsLong(pValue); } Py_XDECREF(pValue); @@ -288,82 +455,26 @@ int plugin_score(struct qso_t *qso) { return result; } -bool plugin_is_multi(int band, char *call, int mode) { -#ifdef HAVE_PYTHON - // call is_multi - struct qso_t qso; - qso.band = band; - qso.call = call; - qso.mode = mode; - qso.comment = ""; - - PyObject *py_qso = create_py_qso(&qso); - PyObject *args = Py_BuildValue("(O)", py_qso); - PyObject *pValue = PyObject_CallObject(pf_is_multi, args); - Py_DECREF(args); - Py_DECREF(py_qso); - - bool result = false; - if (pValue != NULL) { - result = PyLong_AsLong(pValue); - } - Py_XDECREF(pValue); - - if (NULL != PyErr_Occurred()) { - PyErr_Print(); - sleep(2); - //exit(1); - } - return result; -#else - return false; -#endif -} - -// result has to be g_freed()'s -char *plugin_get_multi(struct qso_t *qso) { +void plugin_check_exchange(struct qso_t *qso) { #ifdef HAVE_PYTHON PyObject *py_qso = create_py_qso(qso); PyObject *args = Py_BuildValue("(O)", py_qso); - PyObject *pValue = PyObject_CallObject(pf_get_multi, args); + PyObject *pValue = PyObject_CallObject(pf_check_exchange, args); Py_DECREF(args); Py_DECREF(py_qso); - char *result = NULL; - if (pValue != NULL) { - result = g_strdup(PyUnicode_AsUTF8(pValue)); + if (pValue != NULL && PyDict_Check(pValue)) { + copy_dict_string(pValue, "mult1_value", &qso->mult1_value, MULT_SIZE); + copy_dict_string(pValue, "normalized_exchange", &qso->normalized_comment, + COMMENT_SIZE); } Py_XDECREF(pValue); if (NULL != PyErr_Occurred()) { PyErr_Print(); sleep(2); - //exit(1); + exit(1); } - return result; -#else - return NULL; -#endif -} - -int plugin_nr_of_mults(int band) { -#ifdef HAVE_PYTHON - PyObject *args = Py_BuildValue("(i)", band); - PyObject *pValue = PyObject_CallObject(pf_nr_of_mults, args); - Py_DECREF(args); - - int result = 0; - if (pValue != NULL) { - result = PyLong_AsLong(pValue); - } - Py_XDECREF(pValue); - - if (NULL != PyErr_Occurred()) { - PyErr_Print(); - } - return result; -#else - return 0; #endif } diff --git a/src/plugin.h b/src/plugin.h index 5e7998147..5b81166a3 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -4,6 +4,7 @@ #include "tlf.h" int plugin_init(const char *name); +void plugin_close(); bool plugin_has_setup(); void plugin_setup(); @@ -11,16 +12,7 @@ void plugin_setup(); bool plugin_has_score(); int plugin_score(struct qso_t *qso); -bool plugin_has_is_dupe(); -bool plugin_is_dupe(); - -bool plugin_has_is_multi(); -bool plugin_is_multi(int band, const char *call, int mode); - -bool plugin_has_nr_of_mults(); -int plugin_nr_of_mults(int band); - -bool plugin_has_get_multi(); -char *plugin_get_multi(struct qso_t *qso); +bool plugin_has_check_exchange(); +void plugin_check_exchange(struct qso_t *qso); #endif diff --git a/src/prevqso.c b/src/prevqso.c deleted file mode 100644 index f62658e9a..000000000 --- a/src/prevqso.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Tlf - contest logging program for amateur radio operators - * Copyright (C) 2001-2002-2003 Rein Couperus - * 2014, 2015 Thomas Beierlein - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -/* ------------------------------------------------------------ - * repeat previous_qsonr - * - *--------------------------------------------------------------*/ - - -#include - -#include - -#include "globalvars.h" -#include "sendbuf.h" - - -void prev_qso(void) { - - int i; - char *str; - - str = g_strdup_printf("%3s %03d ", last_rst, qsonum - 1); - for (i = 0; i < strlen(str); i++) { - str[i] = short_number(str[i]); - } - sendmessage(str); - g_free(str); -} diff --git a/src/prevqso.h b/src/prevqso.h deleted file mode 100644 index c5b2d8118..000000000 --- a/src/prevqso.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Tlf - contest logging program for amateur radio operators - * Copyright (C) 2001-2002-2003 Rein Couperus - * 2014, 2015 Thomas Beierlein - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#ifndef PREVQSO_H -#define PREVQSO_H - -void prev_qso(void); - - -#endif /* end of include guard: PREVQSO_H */ diff --git a/src/qtcwin.c b/src/qtcwin.c index 816b821fa..6d793e257 100644 --- a/src/qtcwin.c +++ b/src/qtcwin.c @@ -54,11 +54,11 @@ void init_qtc_panel(); -void draw_qtc_panel(); +void draw_qtc_panel(int direction); void start_qtc_recording(); void stop_qtc_recording(); void clear_help_block(); -void show_help_msg(); +void show_help_msg(int msgidx); void showfield(int fidx); void modify_field(int pressed); void delete_from_field(int dir); @@ -75,7 +75,6 @@ void fill_qtc_times(char *time); extern char lastcall[]; extern int trxmode; extern int digikeyer; -extern int nr_qsos; static int record_run = -1; /* was recording already started? */ @@ -660,7 +659,7 @@ void qtc_main_panel(int direction) { if (*qtccount > 0 && qtcreclist.confirmed == *qtccount) { if (qtcreclist.sentcfmall == 0) { qtcreclist.sentcfmall = 1; - log_recv_qtc_to_disk(nr_qsos); + log_recv_qtc_to_disk(NR_QSOS); if (record_run > -1) { stop_qtc_recording(); } @@ -785,7 +784,7 @@ void qtc_main_panel(int direction) { case CTRL_S: if (qtccurrdirection == SEND && *qtccount > 0 && qtclist.totalsent == *qtccount) { - log_sent_qtc_to_disk(nr_qsos); + log_sent_qtc_to_disk(NR_QSOS); wattrset(qtcwin, LINE_INVERTED); mvwaddstr(qtcwin, 2, 11, "QTCs have been saved!"); prevqtccall[0] = '\0'; diff --git a/src/readcabrillo.c b/src/readcabrillo.c index 4353e470e..2c603de4d 100644 --- a/src/readcabrillo.c +++ b/src/readcabrillo.c @@ -84,11 +84,11 @@ void write_log_fm_cabr(struct qso_t *qso) { score_qso(qso); char *logline = makelogline(qso); /* format logline */ qso->logline = logline; - store_qso(logline); + store_qso(logfile, logline); g_ptr_array_add(qso_array, qso); cleanup_qso(); - qsoflags_for_qtc[nr_qsos - 1] = 0; + qsoflags_for_qtc[NR_QSOS - 1] = 0; } /* write a new line to the qtc log */ @@ -127,7 +127,7 @@ void write_qtclog_fm_cabr(char *qtcrcall, struct read_qtc_t qtc_line) { } // look until not found and we're in list - while (found_call == 0 && qtc_curr_call_nr < nr_qsos) { + while (found_call == 0 && qtc_curr_call_nr < NR_QSOS) { strncpy(thiscall, QSOS(qtc_curr_call_nr) + 29, 14); g_strchomp(thiscall); strncpy(ttime, QSOS(qtc_curr_call_nr) + 17, 2); diff --git a/src/readcalls.c b/src/readcalls.c index 68c29d6d4..f953a8eb6 100644 --- a/src/readcalls.c +++ b/src/readcalls.c @@ -53,6 +53,24 @@ #include "ui_utils.h" +/* Backup original logfile and write a new one from internal database */ +void do_backup(const char *logfile, bool interactive) { + // save a backup + char prefix[40]; + format_time(prefix, sizeof(prefix), "%Y%m%d_%H%M%S"); + char *backup = g_strdup_printf("%s_%s", prefix, logfile); + rename(logfile, backup); + // rewrite log + for (int i = 0 ; i < NR_QSOS; i++) { + store_qso(logfile, QSOS(i)); + } + if (interactive) { + showstring("Log has been backed up as", backup); + sleep(1); + } + g_free(backup); +} + void init_scoring(void) { /* reset counter and score anew */ total = 0; @@ -183,27 +201,14 @@ int readcalls(const char *logfile, bool interactive) { } if (ok) { - // save a backup - char prefix[40]; - format_time(prefix, sizeof(prefix), "%Y%m%d_%H%M%S"); - char *backup = g_strdup_printf("%s_%s", prefix, logfile); - rename(logfile, backup); - // rewrite log - nr_qsos = 0; // FIXME store_qso increments nr_qsos - for (int i = 0 ; i < linenr; i++) { - store_qso(QSOS(i)); - } - if (interactive) { - showstring("Log has been backed up as", backup); - sleep(1); - } - g_free(backup); + do_backup(logfile, interactive); } } return linenr; // nr of lines in log } + int log_read_n_score() { int nr_qsolines = readcalls(logfile, false); diff --git a/src/scroll_log.c b/src/scroll_log.c index c05cf6aaa..924c01723 100644 --- a/src/scroll_log.c +++ b/src/scroll_log.c @@ -104,10 +104,10 @@ void get_next_serial(void) { void scroll_log(void) { for (int i = 5; i > 0; i--) { - if (nr_qsos < i) { + if (NR_QSOS < i) { g_strlcpy(logline_edit[5 - i], spaces(80), LINELEN + 1); } else { - g_strlcpy(logline_edit[5- i], QSOS(nr_qsos - i), LINELEN + 1); + g_strlcpy(logline_edit[5- i], QSOS(NR_QSOS - i), LINELEN + 1); } } diff --git a/src/searchlog.c b/src/searchlog.c index b12dce9d4..c0be4657a 100644 --- a/src/searchlog.c +++ b/src/searchlog.c @@ -392,7 +392,7 @@ void filterLog(const char *call) { srch_index = 0; - for (int qso_index = 0; qso_index < nr_qsos; qso_index++) { + for (int qso_index = 0; qso_index < NR_QSOS; qso_index++) { struct qso_t *qso = g_ptr_array_index(qso_array, qso_index); if (qso->is_comment) { diff --git a/src/sendbuf.c b/src/sendbuf.c index a73455fda..f294808e5 100644 --- a/src/sendbuf.c +++ b/src/sendbuf.c @@ -42,6 +42,7 @@ #include "tlf_curses.h" #include "write_keyer.h" +#include "sendbuf.h" #define BUFSIZE 81 char buffer[BUFSIZE]; @@ -165,22 +166,47 @@ void replace_all(char *buf, int size, const char *what, const char *rep) { replace_n(buf, size, what, rep, 999); } -void ExpandMacro(void) { - - int i; +void ExpandQsoNumber(char *qsonr) { static char qsonroutput[5] = ""; + int leading_zeros = 0; + bool lead = true; + for (int i = 0; i <= 4; i++) { + if (lead && qsonr[i] == '0') { + ++leading_zeros; + } else { + lead = false; + } + qsonroutput[i] = short_number(qsonr[i]); + } + qsonroutput[4] = '\0'; + + if (leading_zeros_serial && leading_zeros > 1) { + leading_zeros = 1; + } + + replace_all(buffer, BUFSIZE, "#", + qsonroutput + leading_zeros); /* serial nr */ +} + +void ExpandRst(char rst[4]) { static char rst_out[4] = ""; + rst_out[0] = rst[0]; + rst_out[1] = short_number(rst[1]); + rst_out[2] = short_number(rst[2]); + rst_out[3] = '\0'; + + replace_all(buffer, BUFSIZE, "[", rst_out); /* his RST */ +} +void ExpandMacro_CurrentQso(void) { replace_all(buffer, BUFSIZE, "%", my.call); /* mycall */ - if (NULL != strstr(buffer, "@")) { char *p = current_qso.call + strlen(hiscall_sent); if (strlen(hiscall_sent) != 0) { hiscall_sent[0] = '\0'; - early_started = 0; -// sending_call = 0; + early_started = false; } if (cqmode == CQ && resend_call != RESEND_NOT_SET) { strcpy(sentcall, current_qso.call); @@ -190,34 +216,10 @@ void ExpandMacro(void) { current_qso.call); /* his call, further occurrences */ } - - rst_out[0] = sent_rst[0]; - rst_out[1] = short_number(sent_rst[1]); - rst_out[2] = short_number(sent_rst[2]); - rst_out[3] = '\0'; - - replace_all(buffer, BUFSIZE, "[", rst_out); /* his RST */ - + ExpandRst(sent_rst); if (NULL != strstr(buffer, "#")) { - int leading_zeros = 0; - bool lead = true; - for (i = 0; i <= 4; i++) { - if (lead && qsonrstr[i] == '0') { - ++leading_zeros; - } else { - lead = false; - } - qsonroutput[i] = short_number(qsonrstr[i]); - } - qsonroutput[4] = '\0'; - - if (!noleadingzeros && leading_zeros > 1) { - leading_zeros = 1; - } - - replace_all(buffer, BUFSIZE, "#", - qsonroutput + leading_zeros); /* serial nr */ + ExpandQsoNumber(qsonrstr); if (lan_active && contest->exchange_serial) { strncpy(lastqsonr, qsonrstr, 5); @@ -225,7 +227,6 @@ void ExpandMacro(void) { } } - replace_all(buffer, BUFSIZE, "!", current_qso.comment); if (trxmode == DIGIMODE) @@ -234,16 +235,52 @@ void ExpandMacro(void) { replace_all(buffer, BUFSIZE, "|", ""); /* drop it */ } +struct qso_t *get_previous_qso() { + static struct qso_t empty_qso = { + .call = "", + .qso_nr = 0 + }; + + // TODO:lan for networked mode it will be incorrect. Previous qso may not be the last one in the log. + for(int i=NR_QSOS-1; i >= 0; i--) { + struct qso_t *out_qso = g_ptr_array_index(qso_array, i); + if(!out_qso->is_comment) { + return out_qso; + } + } + return &empty_qso; +} + +void ExpandMacro_PreviousQso(void) { + replace_all(buffer, BUFSIZE, "%", my.call); /* mycall */ + + struct qso_t *prev_qso = get_previous_qso(); + + if (NULL != strstr(buffer, "@")) { + replace_all(buffer, BUFSIZE, "@", prev_qso->call); + } + + char *prevrst = g_strdup_printf("%03d", prev_qso->rst_s); + ExpandRst(prevrst); + g_free(prevrst); + + if (NULL != strstr(buffer, "#")) { + char *prevnr = g_strdup_printf("%04d", prev_qso->qso_nr); + ExpandQsoNumber(prevnr); + g_free(prevnr); + } +} + -void sendbuf(void) { +void sendbuf(ExpandMacro_t expandMacro) { if ((trxmode == CWMODE && cwkeyer != NO_KEYER) || (trxmode == DIGIMODE && digikeyer != NO_KEYER)) { - ExpandMacro(); + expandMacro(); if (!simulator) { - if (sending_call == 0) + if (!sending_call) add_to_keyer_terminal(buffer); } @@ -288,20 +325,25 @@ void sendbuf(void) { * Send the message via CW or DIGI mode, but only if not empty * \param msg message to send */ -void sendmessage(const char *msg) { +void sendmessage_with_macro_expand(const char *msg, ExpandMacro_t expandMacro) { if (strlen(msg) != 0) { g_strlcpy(buffer, msg, sizeof(buffer)); - sendbuf(); + sendbuf(expandMacro); } } -void send_standard_message(int msg) { +void sendmessage(const char *msg) { + sendmessage_with_macro_expand(msg, ExpandMacro_CurrentQso); +} + +void send_standard_message_with_macro_expand(int msg, + ExpandMacro_t expandMacro) { switch (trxmode) { case CWMODE: - sendmessage(message[msg]); + sendmessage_with_macro_expand(message[msg], expandMacro); break; case DIGIMODE: - sendmessage(digi_message[msg]); + sendmessage_with_macro_expand(digi_message[msg], expandMacro); break; default: if (msg < 14) @@ -310,6 +352,14 @@ void send_standard_message(int msg) { } } +void send_standard_message(int msg) { + send_standard_message_with_macro_expand(msg, ExpandMacro_CurrentQso); +} + +void send_standard_message_prev_qso(int msg) { + send_standard_message_with_macro_expand(msg, ExpandMacro_PreviousQso); +} + void send_keyer_message(int msg) { switch (trxmode) { case DIGIMODE: diff --git a/src/sendbuf.h b/src/sendbuf.h index 021d5627e..6c1fd88c1 100644 --- a/src/sendbuf.h +++ b/src/sendbuf.h @@ -22,9 +22,15 @@ #ifndef SENDBUF_H #define SENDBUF_H +typedef void (*ExpandMacro_t)(void); + +void ExpandMacro_CurrentQso(void); +void ExpandMacro_PreviousQso(void); + char short_number(char c); void sendmessage(const char *msg); void send_standard_message(int msg); +void send_standard_message_prev_qso(int msg); void send_keyer_message(int msg); void replace_n(char *buf, int size, const char *what, const char *rep, diff --git a/src/sendqrg.c b/src/sendqrg.c index 378014823..69685941d 100644 --- a/src/sendqrg.c +++ b/src/sendqrg.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "bands.h" #include "cw_utils.h" @@ -32,6 +33,19 @@ #include "bands.h" #include "globalvars.h" +static bool init_called = false; +static bool can_send_morse = false; +static bool can_stop_morse = false; + +bool rig_has_send_morse() { + assert(init_called); + return can_send_morse; +} + +bool rig_has_stop_morse() { + assert(init_called); + return can_stop_morse; +} void send_bandswitch(freq_t trxqrg); @@ -95,6 +109,13 @@ int init_tlf_rig(void) { caps = my_rig->caps; + can_send_morse = caps->send_morse != NULL; +#if HAMLIB_VERSION >= 400 + can_stop_morse = caps->stop_morse != NULL; +#else + can_stop_morse = false; // rig_stop_morse was introduced in Hamlib 4.0 +#endif + /* If CAT PTT is wanted, test for CAT capability of rig backend. */ if (rigptt & CAT_PTT_WANTED) { if (caps->ptt_type == RIG_PTT_RIG) { @@ -169,13 +190,17 @@ int init_tlf_rig(void) { break; } + init_called = true; + return 0; } void close_tlf_rig(RIG *my_rig) { + pthread_mutex_lock(&rig_lock); rig_close(my_rig); /* close port */ rig_cleanup(my_rig); /* if you care about memory */ + pthread_mutex_unlock(&rig_lock); printf("Rig port %s closed\n", rigportname); } @@ -202,9 +227,13 @@ static int parse_rigconf() { } if (rigconf[i] == ',') rigconf[i] = '\0'; + + pthread_mutex_lock(&rig_lock); retcode = rig_set_conf(my_rig, rig_token_lookup(my_rig, cnfparm), cnfval); + pthread_mutex_unlock(&rig_lock); + if (retcode != RIG_OK) { showmsg("rig_set_conf: error "); return -1; @@ -225,7 +254,9 @@ static void debug_tlf_rig() { sleep(10); + pthread_mutex_lock(&rig_lock); retcode = rig_get_freq(my_rig, RIG_VFO_CURR, &rigfreq); + pthread_mutex_unlock(&rig_lock); if (retcode != RIG_OK) { TLF_LOG_WARN("Problem with rig get freq: %s", rigerror(retcode)); @@ -236,7 +267,9 @@ static void debug_tlf_rig() { const freq_t testfreq = 14000000; // test set frequency + pthread_mutex_lock(&rig_lock); retcode = rig_set_freq(my_rig, RIG_VFO_CURR, testfreq); + pthread_mutex_unlock(&rig_lock); if (retcode != RIG_OK) { TLF_LOG_WARN("Problem with rig set freq: %s", rigerror(retcode)); @@ -244,7 +277,9 @@ static void debug_tlf_rig() { showmsg("Rig set freq ok!"); } + pthread_mutex_lock(&rig_lock); retcode = rig_get_freq(my_rig, RIG_VFO_CURR, &rigfreq); // read qrg + pthread_mutex_unlock(&rig_lock); if (retcode != RIG_OK) { TLF_LOG_WARN("Problem with rig get freq: %s", rigerror(retcode)); diff --git a/src/sendqrg.h b/src/sendqrg.h index 6544433a0..e718315b4 100644 --- a/src/sendqrg.h +++ b/src/sendqrg.h @@ -22,6 +22,7 @@ #define SENDQRG_H #include +#include #ifdef HAMLIB_FILPATHLEN #define TLFFILPATHLEN HAMLIB_FILPATHLEN @@ -33,6 +34,9 @@ #endif #endif +bool rig_has_send_morse(); +bool rig_has_stop_morse(); + int init_tlf_rig(void); void close_tlf_rig(RIG *my_rig); diff --git a/src/setcontest.c b/src/setcontest.c index 92c3c8d50..c06a7257c 100644 --- a/src/setcontest.c +++ b/src/setcontest.c @@ -219,7 +219,6 @@ contest_config_t config_arrldx_dx = { contest_config_t config_arrl_ss = { .id = ARRL_SS, .name = "ARRL_SS", - .exchange_serial = true, .points = { .type = FIXED, .point = 2, @@ -340,7 +339,6 @@ void setcontest(char *name) { showscore_flag = true; searchflg = true; sectn_mult = false; - noleadingzeros = false; w_cty = getctynr(wcall); ve_cty = getctynr(vecall); @@ -360,7 +358,7 @@ void setcontest(char *name) { qso_once = true; multlist = 1; // sectn_mult = true; - noleadingzeros = true; + leading_zeros_serial = false; } if (CONTEST_IS(PACC_PA)) { diff --git a/src/setcontest.h b/src/setcontest.h index 0fe9a2472..6a6bcb3c1 100644 --- a/src/setcontest.h +++ b/src/setcontest.h @@ -21,6 +21,7 @@ #ifndef SETCONTEST_H #define SETCONTEST_H +#include "bandmap.h" #include "globalvars.h" #define CONTEST_IS(cid) (contest->id == cid) @@ -28,7 +29,7 @@ extern contest_config_t config_qso; -bool general_ismulti(); +bool general_ismulti(spot *data); contest_config_t *lookup_contest(char *name); void list_contests(); void setcontest(char *name); diff --git a/src/showscore.c b/src/showscore.c index 0a8a344c9..6a14c7663 100644 --- a/src/showscore.c +++ b/src/showscore.c @@ -32,7 +32,6 @@ #include "globalvars.h" // Includes tlf.h #include "last10.h" #include "nicebox.h" // Includes curses.h -#include "plugin.h" #include "printcall.h" #include "bands.h" #include "setcontest.h" @@ -113,15 +112,7 @@ void display_header(int *bi) { printfield(2, band_cols[i], qsos_per_band[bi[i]]); } - if (plugin_has_nr_of_mults()) { - mvaddstr(3, START_COL, "Mults "); - for (i = 0; i < 6; i++) { - printfield(3, band_cols[i], plugin_nr_of_mults(bi[i])); - } - } else { - mvaddstr(3, START_COL, spaces(80 - START_COL)); - } - + mvaddstr(3, START_COL, spaces(80 - START_COL)); mvaddstr(4, START_COL, spaces(80 - START_COL)); mvaddstr(5, START_COL, spaces(80 - START_COL)); diff --git a/src/startmsg.c b/src/startmsg.c index 5f774a102..4be22cc9d 100644 --- a/src/startmsg.c +++ b/src/startmsg.c @@ -75,7 +75,7 @@ void shownr(char *message, int nr) { } //---------------------------------------------------------------- -void showstring(char *message1, char *message2) { +void showstring(const char *message1, const char *message2) { if (!has_room_for_message()) clearmsg_wait(); mvprintw(linectr, 0, "%s %s", message1, message2); diff --git a/src/startmsg.h b/src/startmsg.h index ec2e3dca2..21ee985f6 100644 --- a/src/startmsg.h +++ b/src/startmsg.h @@ -26,7 +26,7 @@ void clearmsg(void); void clearmsg_wait(void); void showmsg(char *message); // output text void shownr(char *message, int nr); // output text + number -void showstring(char *message1, char *message2); // output 2 strings +void showstring(const char *message1, const char *message2); // output 2 strings #endif /* end of include guard: STARTMSG_H */ diff --git a/src/store_qso.c b/src/store_qso.c index d905a339f..b63dfef0a 100644 --- a/src/store_qso.c +++ b/src/store_qso.c @@ -25,21 +25,21 @@ #include #include #include +#include #include "globalvars.h" // Includes glib.h and tlf.h #include "tlf_curses.h" -#include "plugin.h" -void store_qso(char *loglineptr) { +void store_qso(const char *file, char *loglineptr) { FILE *fp; - if ((fp = fopen(logfile, "a")) == NULL) { + if ((fp = fopen(file, "a")) == NULL) { fprintf(stdout, "store_qso.c: Error opening file.\n"); + sleep(1); endwin(); exit(1); } - nr_qsos++; fputs(loglineptr, fp); fputc('\n', fp); diff --git a/src/store_qso.h b/src/store_qso.h index da56d0ec9..f7ff23849 100644 --- a/src/store_qso.h +++ b/src/store_qso.h @@ -21,6 +21,6 @@ #ifndef STORE_QSO_H #define STORE_QSO_H -void store_qso(char *loglineptr); +void store_qso(const char *file, char *loglineptr); #endif /* STORE_QSO_H */ diff --git a/src/tlf.h b/src/tlf.h index 88f7144ec..6038335b1 100644 --- a/src/tlf.h +++ b/src/tlf.h @@ -25,6 +25,7 @@ #include #include "hamlib/rig.h" +#include "bandmap.h" #include "dxcc.h" enum { @@ -266,7 +267,7 @@ typedef struct { int (*fn)(struct qso_t *); }; } points; - bool (*is_multi)(); + bool (*is_multi)(spot *data); } contest_config_t; @@ -306,6 +307,7 @@ enum { #define LEN(array) (sizeof(array) / sizeof(array[0])) #define QSOS(n) (((struct qso_t*)g_ptr_array_index(qso_array, n))->logline) +#define NR_QSOS (qso_array->len) #endif /* TLF_H */ diff --git a/src/writecabrillo.c b/src/writecabrillo.c index d6ff2a214..b3d4c0586 100644 --- a/src/writecabrillo.c +++ b/src/writecabrillo.c @@ -43,6 +43,8 @@ #include "sendbuf.h" #include "bands.h" +char exchange[40]; // format of sent exchange + struct linedata_t *get_next_record(FILE *fp); struct linedata_t *get_next_qtc_record(FILE *fp, int qtcdirection); void free_linedata(struct linedata_t *ptr); @@ -453,9 +455,6 @@ void prepare_line(struct linedata_t *qso, struct cabrillo_desc *desc, } static void set_exchange_format() { - if (strlen(exchange) > 0) { - return; // it was set explicitly, use it - } if (contest->exchange_serial) { strcpy(exchange, "#"); // contest is using serial number return; diff --git a/test/data.c b/test/data.c index 45051b6d3..28a0c9761 100644 --- a/test/data.c +++ b/test/data.c @@ -103,7 +103,7 @@ bool serial_section_mult = false; bool serial_or_section = false; /* exchange is serial OR section, like HA-DX */ bool serial_grid4_mult = false; bool qso_once = false; -bool noleadingzeros; +bool leading_zeros_serial; bool ctcomp = false; int isdupe = 0; // 0 if nodupe -- for auto qso b4 (LZ3NY) bool nob4 = false; // allow auto b4 @@ -140,7 +140,6 @@ rmode_t digi_mode = 0; bool mixedmode = false; char sent_rst[4] = "599"; char recvd_rst[4] = "599"; -char last_rst[4] = "599"; /* Report for last QSO */ int shortqsonr = LONGCW; /* 1 = short cw char in exchange */ int cluster = NOCLUSTER; /* 0 = OFF, 1 = FOLLOW, 2 = spots 3 = all */ bool clusterlog = false; /* clusterlog on/off */ @@ -151,7 +150,6 @@ bool demode = false; /* send DE before s&p call */ int announcefilter = FILTER_ANN; /* filter cluster announcements */ bool showscore_flag = false; /* show score window */ int change_rst = 0; -char exchange[40]; int defer_store = 0; mystation_t my; char logfile[120] = "general.log"; @@ -234,9 +232,11 @@ char hiscall_sent[20] = ""; /**< part which was sent during early int cwstart = 0; /**< number characters after which sending call started automatically, 0 - off, -1 - manual start */ -int sending_call = 0; -int early_started = 0; /**< 1 if sending call started early, +bool sending_call = false; +bool early_started = false; /**< 1 if sending call started early, strlen(hiscall)>cwstart or 'space' */ +bool stop_tx_only = false; /**< ESC should stop only tx */ + char lastcall[20]; char qsonrstr[5] = "0001"; char band[NBANDS][4] = @@ -327,7 +327,6 @@ int rigptt = 0; /*----------------------------the parsed log lines-------------------------*/ // array of qso's GPtrArray *qso_array; -int nr_qsos = 0; /*------------------------------dupe array---------------------------------*/ int nr_worked = 0; /*< number of calls in worked[] */ @@ -420,8 +419,6 @@ int wattr_on(WINDOW *win, attr_t attrs, void *opts) { return 0; } -void displayit() { -} int netkeyer(int cw_op, char *cwmessage) { return 0; diff --git a/test/test_addcall.c b/test/test_addcall.c index eb68248f4..7187dc31d 100644 --- a/test/test_addcall.c +++ b/test/test_addcall.c @@ -27,7 +27,7 @@ // OBJECT ../src/utils.o // OBJECT ../src/zone_nr.o -void addmult(struct qso_t qso) {} +int addmult(struct qso_t *qso) { return -1; } void addmult_lan() {} void checkexchange(struct qso_t *qso, bool interactive) {} int check_mult(struct qso_t *qso) { return -1; } diff --git a/test/test_adif.c b/test/test_adif.c index 7d1c49c1e..1a2d1d709 100644 --- a/test/test_adif.c +++ b/test/test_adif.c @@ -86,6 +86,7 @@ int setup_default(void **state) { void test_keep_old_format(void **state) { char buffer[181]; + extern char exchange[40]; // defined in writecabrillo.c strcpy(exchange, "14"); struct linedata_t *qso; diff --git a/test/test_cabrillo.c b/test/test_cabrillo.c index 27b785f2e..84dcd0ba1 100644 --- a/test/test_cabrillo.c +++ b/test/test_cabrillo.c @@ -24,7 +24,7 @@ char section[8] = ""; // defined in getexchange.c int qsoflags_for_qtc[MAX_QSOS]; void addcall(struct qso_t *qso) { } -void store_qso(char *logline) { nr_qsos++; } +void store_qso(const char *file, char *logline) { } void cleanup_qso() { } void make_qtc_logline(struct read_qtc_t qtc_line, char *fname) { } char *getgrid(char *comment) { return comment; } @@ -91,10 +91,10 @@ char formatfile[] = TOP_SRCDIR "/share/cabrillo.fmt" ; int setup_default(void **state) { strcpy(my.call, "A1BCD"); + extern char exchange[40]; // defined in writecabrillo.c strcpy(exchange, "#"); init_qso_array(); - nr_qsos = 0; return 0; } diff --git a/test/test_getexchange.c b/test/test_getexchange.c index cfb44bd6f..cd5d0eebd 100644 --- a/test/test_getexchange.c +++ b/test/test_getexchange.c @@ -28,12 +28,17 @@ bool lan_active = false; /* dummies */ +void cleanup_comment() {} +void restore_comment() {} +void cleanup_hiscall() {} void refresh_comment(void) {} +void rst_reset() {} void time_update(void) {} void show_rtty(void) {} void OnLowerSearchPanel(int x, char *str) {} void sendmessage(const char *msg) {} void send_standard_message(int msg) {} +void send_standard_message_prev_qso(int msg) {} void add_local_spot(void) {} void keyer(void) {} void qtc_main_panel(int direction) {} @@ -179,7 +184,7 @@ void test_getexchange_arrlss(void **state) { init_and_load_multipliers(); for (int i = 0; i < LEN(getex_arrlss); ++i) { - struct qso_t *qso = g_malloc0(sizeof(struct qso_t)); + struct qso_t *qso = g_malloc0(sizeof(struct qso_t)); qso->comment = g_strdup_printf("%-20s", getex_arrlss[i].input); checkexchange(qso, false); @@ -187,7 +192,7 @@ void test_getexchange_arrlss(void **state) { assert_string_equal(qso->normalized_comment, getex_arrlss[i].expected_normalized_comment); assert_string_equal(qso->mult1_value, getex_arrlss[i].expected_mult1_value); - assert_string_equal(qso->callupdate, getex_arrlss[i].expected_callupdate); + assert_string_equal(qso->callupdate, getex_arrlss[i].expected_callupdate); free_qso(qso); } @@ -251,7 +256,7 @@ void test_getexchange_cqww(void **state) { contest = lookup_contest("CQWW"); for (int i = 0; i < LEN(getex_cqww); ++i) { - struct qso_t *qso = g_malloc0(sizeof(struct qso_t)); + struct qso_t *qso = g_malloc0(sizeof(struct qso_t)); qso->comment = g_strdup_printf("%-20s", getex_cqww[i].input); checkexchange(qso, false); @@ -329,15 +334,17 @@ void test_getexchange_serial_section(void **state) { add_mult_line("50ABCD"); for (int i = 0; i < LEN(getex_serial_section); ++i) { - struct qso_t *qso = g_malloc0(sizeof(struct qso_t)); + struct qso_t *qso = g_malloc0(sizeof(struct qso_t)); qso->comment = g_strdup_printf("%-20s", getex_serial_section[i].input); checkexchange(qso, false); assert_string_equal(qso->normalized_comment, getex_serial_section[i].expected_normalized_comment); - assert_string_equal(qso->mult1_value, getex_serial_section[i].expected_mult1_value); - assert_string_equal(qso->callupdate, getex_serial_section[i].expected_callupdate); + assert_string_equal(qso->mult1_value, + getex_serial_section[i].expected_mult1_value); + assert_string_equal(qso->callupdate, + getex_serial_section[i].expected_callupdate); free_qso(qso); } @@ -400,7 +407,7 @@ void test_getexchange_sectn_mult(void **state) { add_mult_line("50ABCD"); for (int i = 0; i < LEN(getex_sectn_mult); ++i) { - struct qso_t *qso = g_malloc0(sizeof(struct qso_t)); + struct qso_t *qso = g_malloc0(sizeof(struct qso_t)); qso->comment = g_strdup_printf("%-20s", getex_sectn_mult[i].input); checkexchange(qso, false); diff --git a/test/test_parse_logcfg.c b/test/test_parse_logcfg.c index 80a165cf6..871bb66b7 100644 --- a/test/test_parse_logcfg.c +++ b/test/test_parse_logcfg.c @@ -61,6 +61,7 @@ t_qtc_ry_line qtc_ry_lines[QTC_RY_LINE_NR]; void checkexchange(struct qso_t *qso, bool interactive) {} int check_mult(struct qso_t *qso) { return -1; } +dxcc_data *dxcc_by_index(unsigned int index) { return NULL; } contest_config_t config_focm; @@ -377,11 +378,26 @@ void test_usepartials(void **state) { assert_int_equal(use_part, 1); } -void test_usepartials_with_arg(void **state) { - int rc = call_parse_logcfg("USEPARTIALS=no\n"); +void test_usepartials_no(void **state) { + use_part = true; + int rc = call_parse_logcfg("USEPARTIALS = no\n"); // space around = + assert_int_equal(rc, PARSE_OK); + assert_int_equal(use_part, false); +} + +void test_usepartials_yes(void **state) { + use_part = false; + int rc = call_parse_logcfg("USEPARTIALS=yes\n"); + assert_int_equal(rc, PARSE_OK); + assert_int_equal(use_part, true); +} + +void test_usepartials_wrong_arg(void **state) { + int rc = call_parse_logcfg("USEPARTIALS=abc\n"); assert_int_equal(rc, PARSE_ERROR); assert_string_equal(showmsg_spy, - "Keyword 'USEPARTIALS' can't have a parameter. See man page.\n"); + "Wrong parameter format for keyword 'USEPARTIALS'. See man page.\n"); + } typedef struct { @@ -435,6 +451,8 @@ static bool_true_t bool_trues[] = { {"QTCREC_RECORD", &qtcrec_record}, {"QTC_AUTO_FILLTIME", &qtc_auto_filltime}, {"QTC_RECV_LAZY", &qtc_recv_lazy}, + {"LEADING_ZEROS_SERIAL", &leading_zeros_serial}, + {"ESC_STOPS_TX_ONLY", &stop_tx_only}, }; void test_bool_trues(void **state) { diff --git a/test/test_readcalls.c b/test/test_readcalls.c index 3c5623c11..e933d7f0b 100644 --- a/test/test_readcalls.c +++ b/test/test_readcalls.c @@ -42,6 +42,10 @@ char thisnode = 'A'; bool lan_active = false; // dummy functions +void cleanup_comment() {} +void restore_comment() {} +void cleanup_hiscall() {} +void rst_reset() {} void readqtccalls() {} void shownr(char *msg, int x) {} @@ -56,6 +60,7 @@ void time_update() {} void show_rtty() {} void keyer() {} void send_standard_message(int msg) {} +void send_standard_message_prev_qso(int msg) {} void stoptx() {} void qtc_main_panel(int direction) {} void add_local_spot() {} diff --git a/test/test_searchlog.c b/test/test_searchlog.c index 1105cdf90..3184a3497 100644 --- a/test/test_searchlog.c +++ b/test/test_searchlog.c @@ -108,10 +108,10 @@ contest_config_t config_focm; #define QSO5 " 80CW 12-Jan-18 16:34 0009 UA9LM 599 599 17 UA9 17 3 " #define QSO6 " 80CW 12-Jan-18 16:36 0010 AA3BP 599 599 05 K 05 3 " -/* helper to add string to pos n in qsos array, parse the string as qso +/* helper to add string in qsos array, parse the string as qso * and add it to qso_array */ -void add_log(int n, char *string) { +void add_log(char *string) { struct qso_t *qso; char *line; @@ -124,14 +124,12 @@ void add_log(int n, char *string) { static void write_qsos() { init_qso_array(); - add_log(0, QSO1); - add_log(1, QSO2); - add_log(2, QSO3); - add_log(3, QSO4); - add_log(4, QSO5); - add_log(5, QSO6); - - nr_qsos = 6; + add_log(QSO1); + add_log(QSO2); + add_log(QSO3); + add_log(QSO4); + add_log(QSO5); + add_log(QSO6); } int setup_default(void **state) { @@ -190,6 +188,7 @@ int teardown_default(void **state) { return 0; } + //void test_callmaster_no_file(void **state) { // remove_callmaster(); // int n = load_callmaster(); @@ -370,10 +369,8 @@ void test_displayPartials(void **state) { char *line = g_strdup_printf(" 80CW 12-Jan-18 16:34 0009 UA9%cAA 599 599 17 UA9 17 3 ", 'A' + i); - add_log(6 + i, line); + add_log(line); g_free(line); - - nr_qsos++; } // callmaster has also some UAs diff --git a/test/test_sendbuf.c b/test/test_sendbuf.c index 143812fbd..2528fcd40 100644 --- a/test/test_sendbuf.c +++ b/test/test_sendbuf.c @@ -23,7 +23,6 @@ bool simulator = false; char test_msg[1024]; /* export internal function for test */ -void ExpandMacro(); char *PrepareSPcall(); void replace_all(char *buf, int size, const char *what, const char *rep); @@ -74,7 +73,7 @@ void check_replace_all(char *input, const char *what, char *rep, void check_ExpandMacro(const char *input, const char *exp) { strcpy(buffer, input); - ExpandMacro(); + ExpandMacro_CurrentQso(); assert_string_equal(buffer, exp); } @@ -86,7 +85,7 @@ int setup_default(void **state) { wkeyerbuffer[0] = '\0'; data_ready = 0; simulator = false; - sending_call = 0; + sending_call = false; trxmode = CWMODE; cwkeyer = 1; digikeyer = 1; @@ -188,7 +187,7 @@ void test_expandQsoNrshort(void **state) { } void test_expandQsoNr_leadingzeros(void **state) { - noleadingzeros = false; + leading_zeros_serial = true; strcpy(qsonrstr, "0007"); check_ExpandMacro("nr #", "nr 007"); strcpy(qsonrstr, "0073"); @@ -200,7 +199,7 @@ void test_expandQsoNr_leadingzeros(void **state) { } void test_expandQsoNr_noleadingzeros(void **state) { - noleadingzeros = true; + leading_zeros_serial = false; strcpy(qsonrstr, "0007"); check_ExpandMacro("nr #", "nr 7"); strcpy(qsonrstr, "0073"); diff --git a/tlf.1.in b/tlf.1.in index 2070b8a6a..4986a3445 100644 --- a/tlf.1.in +++ b/tlf.1.in @@ -935,6 +935,11 @@ The third press of .B Escape will clear the call input field. . +.IP +You can disable the above described UNDO functionality by setting +.BR ESC_STOPS_TX_ONLY +(see below). +. .TP .BR \(<-\ (Left-Arrow) Call input field is empty: change to next band lower in frequency or wrap to @@ -975,7 +980,8 @@ Restore previous CQ frequency from MEM and send message . .TP .B F2-F11 -Send CW, RTTY or VOICE messages 2 through 11. +Send CW, RTTY or VOICE messages 2 through 11. If the callsign field is empty +the messages will be sent with the preceding qso data. . .TP .B F12 @@ -1249,12 +1255,29 @@ Show talk messages. In the QTC window shows RTTY lines. . .TP +.B Ctrl-U +Wipe out whole input line (call input and exchange), clear all +information about current contact. +. +If entered by accident a second +.BR Ctrl-U +restores the line. +. +.TP .B Ctrl-V Toggle grabbing direction in which .B Ctrl-G looks for a spot. . .TP +.B Ctrl-W +Wipe actual field the cursor is in (call input OR exchange). +. +If entered by accident a second +.BR Ctrl-W +restores the field. +. +.TP .B Ctrl-Z Suspend @PACKAGE_NAME@ returning to shell prompt. . @@ -1263,7 +1286,8 @@ equivalent command for your shell. . .TP .BR Alt-0 ... Alt-9 -Send alternate CW (Morse code) messages. +Send alternate CW (Morse code) messages. If the callsign field is empty the +messages will be sent with the preceding qso data. . .TP .B Alt-A @@ -1507,6 +1531,13 @@ Make sure you edit the .I logcfg.dat file for at least your callsign and your preferred system configuration. . +.P +Parameters requiring a boolean value (like \fItrue\fR/\fIfalse\fR) +can have an optional case-insensitive argument. +Alternatively, both \fIyes\fR and \fIon\fR are treated as \fItrue\fR. +Similarly instead of \fIfalse\fR also \fIno\fR or \fIoff\fR can be used. +Without the argument the default assigned value is \fIyes\fR. +. . .SH logcfg.dat STATEMENTS . @@ -1534,7 +1565,7 @@ File on remote host you want to synchronize with (use syntax). . .TP -.B CTCOMPATIBLE +\fBCTCOMPATIBLE\fR[=<\fION\fR|\fIOFF\fR>] Do not use the TR-Log QSO sequence, but use .BR + , .B Insert @@ -1599,6 +1630,10 @@ to the same colour pallet as the Linux console. Most other terminals have their own way of setting the colour pallet. . .TP +\fBRXVT\fR[=<\fION\fR|\fIOFF\fR>] +Use \fBrxvt\fR's colours. +. +.TP \fBEDITOR\fR=\fInano\fR | \fIvi\fR[\fIm\fR] | \fI\fR Editor used to modify the QSO log or logcfg.dat. . @@ -1630,7 +1665,7 @@ Normally 0. Range: 0\(en23. . .TP -.B TIME_MASTER +\fBTIME_MASTER\fR[=<\fION\fR|\fIOFF\fR>] This node transmits the time over the network (only one master allowed!). . .TP @@ -1772,13 +1807,13 @@ Delay activation of the Push To Talk pin. Range: 0\(en50. . .TP -.B KEYER_BACKSPACE +\fBKEYER_BACKSPACE\fR[=<\fION\fR|\fIOFF\fR>] Support backspace key in keyer window. . .SS Sound Commands . .TP -.B SOUNDCARD +\fBSOUNDCARD\fR[=<\fION\fR|\fIOFF\fR>] Use soundcard for CW sidetone output. . .TP @@ -1878,7 +1913,7 @@ for 20, 15 and 10. This comes in handy when you have 3 dipoles and a 3-band beam (...). . .TP -.B NO_BANDSWITCH_ARROWKEYS +\fBNO_BANDSWITCH_ARROWKEYS\fR[=<\fION\fR|\fIOFF\fR>] This will prevent unwanted band switching when you are not using rig control. . .IP @@ -1904,7 +1939,7 @@ DX Clusters often use a non-standard port for Telnet, e.g. 8000. Automatic login for the telnet client. . .TP -.B CLUSTER_LOG +\fBCLUSTER_LOG\fR[=<\fION\fR|\fIOFF\fR>] Write clusterlog to disk. . .TP @@ -1931,7 +1966,7 @@ Anything you dump into this FIFO will be displayed by the packet interface. .SS Radio Control Commands . .TP -.B RADIO_CONTROL +\fBRADIO_CONTROL\fR[=<\fION\fR|\fIOFF\fR>] Switches the radio interface on. . The rig interface makes use of the @@ -1975,7 +2010,7 @@ Send rig configuration parameters to Hamlib. e.g. \fBRIGCONF\fR=\fIcivaddr=0x40,retry=3,rig_pathname=/dev/ttyS0\fR . .TP -.B RIT_CLEAR +\fBRIT_CLEAR\fR[=<\fION\fR|\fIOFF\fR>] Clears the RIT after logging the qso. . .IP @@ -2051,15 +2086,15 @@ lifetime for new spots in seconds (number >= .IR 30 ) . .TP -.B SCOREWINDOW +\fBSCOREWINDOW\fR[=<\fION\fR|\fIOFF\fR>] Show the score window (same as \fBAlt-R\fR). . .TP -.B CHECKWINDOW +\fBCHECKWINDOW\fR[=<\fION\fR|\fIOFF\fR>] Show the country/call check window. . .TP -.B PARTIALS +\fBPARTIALS\fR[=<\fION\fR|\fIOFF\fR>] Show a list of possible contest calls. . .IP @@ -2070,7 +2105,7 @@ in the section below for partials file specification. . .TP -.B USEPARTIALS +\fBUSEPARTIALS\fR[=<\fION\fR|\fIOFF\fR>] Use the auto-complete utility (takes some practice...). . .IP @@ -2085,7 +2120,7 @@ Sometimes you must edit the call because it has locked on a unique call. Try it, and switch it off when you don't like it. . .TP -.B LOGFREQUENCY +\fBLOGFREQUENCY\fR[=<\fION\fR|\fIOFF\fR>] Put frequency (kHz) into QSO number to enable logging of frequency (only in .B QSO or @@ -2093,7 +2128,7 @@ or modes). . .TP -.B IGNOREDUPE +\fBIGNOREDUPE\fR[=<\fION\fR|\fIOFF\fR>] Enables logging multiple QSOs in a contest with the same station (considered a good idea these days as contest bots that parse the submitted Cabrillo file will determine such dupes (duplicate contacts)). @@ -2146,14 +2181,14 @@ Providing a value for T is optional but will be ignored. In CW and DIGI modes T will always be 9. . .TP -.B NOB4 +\fBNOB4\fR[=<\fION\fR|\fIOFF\fR>] Do not send automatic \(lqQSO B4\(rq message. . .IP Default is to send such messages. . .TP -.B NOAUTOCQ +\fBNOAUTOCQ\fR[=<\fION\fR|\fIOFF\fR>] No automatic CQ when pressing .B Enter or @@ -2166,7 +2201,12 @@ in the section above). . .TP -.B SEND_DE +\fBESC_STOPS_TX_ONLY\fR[=<\fION\fR|\fIOFF\fR>] +Pressing ESC key does only stop sending CW or voice macros. No further action +is taken. +. +.TP +\fBSEND_DE\fR[=<\fION\fR|\fIOFF\fR>] Sends a \(lqDE\(rq word before your callsign, as in \(lqDE W1AW\(rq. . .IP @@ -2190,9 +2230,13 @@ has to be sent. Eg.: if the received call was but later it is corrected to .B AB1CB, then -.B CB. +.B CB is sent. . +.TP +\fBSHOW_TIME\fR[=<\fION\fR|\fIOFF\fR>] +Show last worked time instead of QSO number in the worked window. +. .SH RULES . The contest rules can be put into separate files. @@ -2267,7 +2311,7 @@ For further Cabrillo related information see below. . .TP -.B CONTEST_MODE +\fBCONTEST_MODE\fR[=<\fION\fR|\fIOFF\fR>] Sets @PACKAGE_NAME@ into contest mode. . .IP @@ -2282,7 +2326,12 @@ Uses short form for serial number (9=N, 0=T). Uses long form for serial number (default). . .TP -.B NO_RST +\fBLEADING_ZEROS_SERIAL\fR[=<\fION\fR|\fIOFF\fR>] +Controls sending leading zeros in serial number. By default serial numbers +are sent with (up to 2) leading zeros. +. +.TP +\fBNO_RST\fR[=<\fION\fR|\fIOFF\fR>] Do not use RST in contest, such as for CW Open, ARRL Sweepstakes, ARRL Field Day, or various QSO parties and such events. . @@ -2291,7 +2340,7 @@ In such a case when writing a Cabrillo log format template you must provide a conforming format definition without RST values. . .TP -.B MIXED +\fBMIXED\fR[=<\fION\fR|\fIOFF\fR>] Stations can be worked in any mode (CW, SSB and DIG) on a given band without being counted as a DUPE. . @@ -2374,7 +2423,7 @@ section above and the section below for obtaining Supercheck Partials updates. . .TP -.B BMAUTOGRAB +\fBBMAUTOGRAB\fR[=<\fION\fR|\fIOFF\fR>] If set along with the .B RADIO_CONTROL and @@ -2385,7 +2434,7 @@ bandmap frequency. . .\" FIXME: To what is the call added? .TP -.B BMAUTOADD +\fBBMAUTOADD\fR[=<\fION\fR|\fIOFF\fR>] If set along with the .B RADIO_CONTROL and @@ -2398,7 +2447,7 @@ Use \(lqS\(rqkip dupes in BANDMAP settings to control if it should also grab dupes. . .TP -.B SPRINTMODE +\fBSPRINTMODE\fR[=<\fION\fR|\fIOFF\fR>] If set, @PACKAGE_NAME@ will automatically switch its mode between LOG and S&P after every QSO. . @@ -2633,7 +2682,7 @@ instead. Points for countries in country list. . .TP -.B USE_COUNTRYLIST_ONLY +\fBUSE_COUNTRYLIST_ONLY\fR[=<\fION\fR|\fIOFF\fR>] Score zero points for countries not in the list. . .TP @@ -2651,11 +2700,11 @@ E.g. Scandinavia:SM,LA,OZ,OH. Not to be confused with so-called "country" files maintained by AD1C. . .TP -.B PORTABLE_MULT_2 +\fBPORTABLE_MULT_2\fR[=<\fION\fR|\fIOFF\fR>] Multiply points x2 for portable stations (e.g. R1 field day). . .TP -.B LOWBAND_DOUBLE +\fBLOWBAND_DOUBLE\fR[=<\fION\fR|\fIOFF\fR>] Double all points for lowband (40, 80, and 160m) QSOs (can be combined with any other value). . @@ -2681,29 +2730,29 @@ function, which gives the largest integer value that is not greater than multiplied score. . .TP -.B WYSIWYG_MULTIBAND +\fBWYSIWYG_MULTIBAND\fR[=<\fION\fR|\fIOFF\fR>] Exchange is the multiplier, per band, whatever you enter. . .IP @PACKAGE_NAME@ builds its own list of multipliers as QSOs are logged. . .TP -.B WYSIWYG_ONCE +\fBWYSIWYG_ONCE\fR[=<\fION\fR|\fIOFF\fR>] Exchange is multiplier, whatever you enter. . .IP Counts once per contest, not per band. . .TP -.B WAZMULT +\fBWAZMULT\fR[=<\fION\fR|\fIOFF\fR>] Multiplier is the CQ zone (per band). . .TP -.B ITUMULT +\fBITUMULT\fR[=<\fION\fR|\fIOFF\fR>] Multiplier is the ITU zone (per band). . .TP -.B PFX_MULT +\fBPFX_MULT\fR[=<\fION\fR|\fIOFF\fR>] Multiplier is prefix (PA0, DA2, VE7, etc.). . .IP @@ -2712,7 +2761,7 @@ Counts once per contest, not per band. .\" FIXME: Reference CQ WPX handling elsewhere. CQ-WW-WPX is confusing as it .\" is not a parameter keyword and only appears here in the repository. .TP -.B PFX_MULT_MULTIBAND +\fBPFX_MULT_MULTIBAND\fR[=<\fION\fR|\fIOFF\fR>] Same as WPX, but the WPX only used CQ-WW-WPX, and there a single prefix multiplier only once, not all band. . @@ -2722,7 +2771,7 @@ With this option, the PFX counts as multiplier on all band. This usable in the All Asia DX contest (AA-DX). . .TP -.B COUNTRY_MULT +\fBCOUNTRY_MULT\fR[=<\fION\fR|\fIOFF\fR>] Multiplier is the DXCC entity (per band). . .IP @@ -2763,14 +2812,14 @@ operators exchange county abbreviations rather than the state but the state can only count once (Kansas QSO Party is once such example). . .TP -.B SECTION_MULT +\fBSECTION_MULT\fR[=<\fION\fR|\fIOFF\fR>] Multiplier is section from multipliers file. . .IP Counts per band. . .TP -.B SECTION_MULT_ONCE +\fBSECTION_MULT_ONCE\fR[=<\fION\fR|\fIOFF\fR>] Multiplier is section from multipliers file. . .IP @@ -2912,13 +2961,19 @@ Use generic multiplier determined by the contest plugin. .IP The argument specifies the multiplier counting rule (see above). Default value is NONE (disabled). -.br +. +.TP +\fBPLUGIN_CONFIG\fR +Specifies the argument to the contest plugin's \fBinit\fR function. +See the +.B PYTHON PLUGIN +section below. . .SS Exchange The following parameters configure @PACKAGE_NAME@'s handling of the exchange. . .TP -.B SERIAL+SECTION +\fBSERIAL+SECTION\fR[=<\fION\fR|\fIOFF\fR>] Exchange is serial number and section, multiplier is section from multiplier file. . @@ -2926,7 +2981,7 @@ file. Counts per band. . .TP -.B SERIAL_OR_SECTION +\fBSERIAL_OR_SECTION\fR[=<\fION\fR|\fIOFF\fR>] Exchange is serial number or section. . .IP @@ -2937,7 +2992,7 @@ This option was introduced for HA-DX, where HA stations give the shortest form of their county, other stations give serial. . .TP -.B SERIAL+GRID4 +\fBSERIAL+GRID4\fR[=<\fION\fR|\fIOFF\fR>] Exchange is serial number and grid (e.g. 001 JO21QI), multiplier is 4-character grid (JO21). . @@ -2981,12 +3036,15 @@ Marathon. The module also recognises embedded calls (e.g. CT3/PA0R/QRP). . .TP -.B CONTINENT_EXCHANGE +\fBCONTINENT_EXCHANGE\fR[=<\fION\fR|\fIOFF\fR>] Exchange is a continent (NA, SA, EU, AS, AF, OC). . .TP .B SERIAL_EXCHANGE -Exchange is a serial number (formats exchange field). +Exchange is a serial number (formats received exchange +and configures Cabrillo exchange). +Set this only if \fIboth\fR sent and received exchanges +are plain serial numbers. . .TP .B MYQRA @@ -3029,7 +3087,7 @@ Put the callsigns of such stations in a file in the working directory, one callsign per line, and give its name as an argument to this parameter. . .TP -.B QTC_AUTO_FILLTIME +\fBQTC_AUTO_FILLTIME\fR[=<\fION\fR|\fIOFF\fR>] If you use the QTC feature and you are an EU station in CW or SSB modes, then you can only RECEIVE the QTCs. . @@ -3047,7 +3105,7 @@ If you change the hour (e.g. if there is a time of 2059 and the next one is 2100), then all next time fields will be changed. . .TP -.B QTC_RECV_LAZY +\fBQTC_RECV_LAZY\fR[=<\fION\fR|\fIOFF\fR>] If you use the QTC feature, and you are an EU station in CW or SSB modes, then you can use this feature. . @@ -3287,6 +3345,165 @@ It can be disabled but not set.) .TQ .B CABRILLO\-SOAPBOX(3) . +.SH PYTHON PLUGIN +. +@PACKAGE_NAME@ uses Python plugins to customize or extend functions beyond +the built-in features. Two notable use cases are adding a specific +scoring logic and determining multipliers. +.P +. +The Python plugin file is loaded the same way as the RULES +file but the file must have \fIpy\fR extension. +. +. +.SS tlf Python module +. +@PACKAGE_NAME@ core functionality and constants can be accessed via +the pre-imported \fItlf\fR module. +. +.TP +.B Constants +.TQ + \fBCWMODE\fR: \fIint\fR +.TQ + \fBSSBMODE\fR: \fIint\fR +.TQ + \fBDIGIMODE\fR: \fIint\fR +.TQ + \fBMY_CALL\fR: \fIstr\fR +.TQ + \fBMY_LAT\fR: \fIfloat\fR +.TQ + \fBMY_LONG\fR: \fIfloat\fR +. +.P +.TP +.B Functions +.TQ + \fBdef get_qrb_for_locator(locator: str) -> Qrb:\fR +Returns the distance (in km) and bearing (in degrees) +to the specified locator from the current location. +.TQ + \fBdef get_dxcc(call: str) -> Dxcc:\fR +Returns the DXCC information for the given call using the \fIcty.dat\fR file. +. +.SS Data structures +. +.TP 20 +.B Qso +.TQ + \fBcall\fR: \fIstr\fR +callsign +.TQ + \fBexchange\fR: \fIstr\fR +received exchange +.TQ + \fBband\fR: \fIint\fR +band in meters +.TQ + \fBmode\fR: \fIint\fR +one of \fItlf.CWMODE\fR, \fItlf.SSBMODE\fR or \fItlf.DIGIMODE\fR +.TQ + \fButc\fR: \fIint\fR +time in integer seconds since epoch +.TP 20 +.B Qrb +.TQ + \fBdistance\fR: \fIfloat\fR +distance in km +.TQ + \fBbearing\fR: \fIfloat\fR +bearing in degrees +.TP 20 +.B Dxcc +.TQ + \fBcountry_name\fR: \fIstr\fR +.TQ + \fBmain_prefix\fR: \fIstr\fR +.TQ + \fBmain_cq_zone\fR: \fIint\fR +.TQ + \fBmain_itu_zone\fR: \fIint\fR +.TQ + \fBmain_latitude\fR: \fIfloat\fR +.TQ + \fBmain_longitude\fR: \fIfloat\fR +.TQ + \fBmain_continent\fR: \fIstr\fR +.TQ + \fBmain_timezone\fR: \fIfloat\fR +.TQ + \fBstarred\fR: \fIbool\fR +.TQ + \fBprefix\fR: \fIstr\fR +.TQ + \fBcq_zone\fR: \fIint\fR +.TQ + \fBitu_zone\fR: \fIint\fR +.TQ + \fBlatitude\fR: \fIfloat\fR +.TQ + \fBlongitude\fR: \fIfloat\fR +.TQ + \fBcontinent\fR: \fIstr\fR +.TQ + \fBtimezone\fR: \fIfloat\fR +.TQ + \fBexact\fR: \fIbool\fR +. +.SS Functions +. +All plugin functions are optional. +. +.TP +\fBdef init(cfg: str) -> str:\fR +. +Called once on @PACKAGE_NAME@ start up. It shall initialize the internal +state of the plugin. +. +.IP +The parameter \fIcfg\fR contains the value provided to +PLUGIN_CONFIG. It can be used to control the internal +logic of the plugin. If no PLUGIN_CONFIG is specified then +an empty string is passed. +. +.IP +The return value is a string specifying the minimal required +@PACKAGE_NAME@ version (e.g. '1.5'). In case of no version dependency +\fINone\fR shall be returned. +. +.TP +\fBdef setup() -> None:\fR +. +Called whenever the QSO list is initialized (e.g. on inital loading of +the log file or before rescoring it). It shall reset any internal +structures related to QSOs. +. +.TP +\fBdef score(qso: Qso) -> int:\fR +. +Called before actually logging a QSO. +It shall return a non-negative score value. +. +.TP +\fBdef check_exchange(qso: Qso) -> Dict[str, str]:\fR +. +Called when exchange is received or updated. +From the returned dictionary these (optional) entries are evaluated: +.TQ + \fBmult1_value\fR: \fIstr\fR +. +.TQ + \fBnormalized_exchange\fR: \fIstr\fR +. +.IP +\fBmult1_value\fR is used as a multiplier without further conversion. +The handling of this multiplier is controlled by the GENERIC_MULT +parameter. +.IP +\fBnormalized_exchange\fR replaces the received exchange +in the finally logged QSO. +. .SH FILES . .I @prefix@/share/@PACKAGE@/logcfg.dat