#!/usr/bin/perl -w 
###############################################################################
#                                                                             
# Merge and convert ADIF files.
#
# Copyright (C) 2008 - 2025 Jaakko Koivuniemi.
#
# 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.
#
#############################################################################

use strict;
use POSIX;
use Getopt::Std;

use subs qw/AddLoc AddRec AdifHead AdifLine AdifRecTo2 b2cabf b2sota Band2freq CalcDist CheckBand CheckCQZ CheckDate CheckGrid CheckIota CheckITUZ CheckLoc CheckMode CheckMorsekey CheckPmode CheckPota CheckPwr CheckQslr CheckQsls CheckQso CheckRig CheckRst CheckSat CheckSatMode CheckSota CheckSubmode CheckUtc CheckWwff CheckVia CompareRec date2csv DelRec EmptyRec f2cabf FixRec Freq2band Grid2Loc jday Loc2Rad m2cab MapRec MergeRecs Modes2Submodes MRules MyNotes2Adif Notes2Adif PrintAdifHead PrintAdifRec PrintAdxHead PrintAdxRec PrintCabHead PrintCabRec PrintCsvRec PrintInfo PrintQsls PrintQso PrintQsos PrintTxtHead PrintTxtRec PrintTxtRecL printusage printversion QslDate QslVerified2Credit QslViaRemoveM Qso2Array Qso2Hash Qso2HashT Qso2Hash2s ReadAdifLog ReadAdifRec ReadCabLog ReadCsvLog ReadTxtLog SearchMatch StatQso time2csv Userdef2Standard/;

my $verno="20250112";
my $averno="3.1.5";

# Jim Reisert, AD1C, http://www.country-files.com/
my $ctyplist = "/usr/share/dxcc/cty.plist"; 

# general debugging switch
my $dbug = 0;

# header hash
my %hed = ("ADIF_VER" => "",
           "CREATED_TIMESTAMP" => "",
           "PROGRAMID" => "",
           "PROGRAMVERSION" => "");


# global two component array of record hash
my @rec = ({"ADDRESS" => "",
           "ADDRESS_INTL" => "",
           "AGE" => 0,
           "ALTITUDE" => 0,
           "A_INDEX" => 0,
           "ANT_AZ" => 0,
           "ANT_EL" => 0,
           "ANT_PATH" => "",
           "ARRL_SECT" => "",
           "AWARD_SUBMITTED" => "",
           "AWARD_GRANTED" => "",
           "BAND" => "",
           "BAND_RX" => "",
           "CALL" => "",
           "CHECK" => "",
           "CLASS" => "",
           "CLUBLOG_QSO_UPLOAD_DATE" => "",
           "CLUBLOG_QSO_UPLOAD_STATUS" => "",
           "CNTY" => "",
           "CNTY_ALT" => "",
           "COMMENT" => "",
           "COMMENT_INTL" => "",
           "CONT" => "",
           "CONTACTED_OP" => "",
           "CONTEST_ID" => "",
           "COUNTRY" => "",
           "COUNTRY_INTL" => "",
           "CQZ" => 0,
           "CREDIT_GRANTED" => "",
           "CREDIT_SUBMITTED" => "",
           "DARC_DOK" => "",
           "DCL_QSLRDATE" => "",
           "DCL_QSLSDATE" => "",
           "DCL_QSL_RCVD" => "",
           "DCL_QSL_SENT" => "",
           "DISTANCE" => 0,
           "DXCC" => 0,
           "EMAIL" => "",
           "EQ_CALL" => "",
           "EQSL_QSLRDATE" => "",
           "EQSL_QSLSDATE" => "",
           "EQSL_QSL_RCVD" => "",
           "EQSL_QSL_SENT" => "", 
           "FISTS" => "",
           "FISTS_CC" => "", 	
           "FORCE_INIT" => "",
           "FREQ" => 0,
           "FREQ_RX" => 0,
           "GRIDSQUARE" => "",
           "GRIDSQUARE_EXT" => "",
           "GUEST_OP" => "",
           "HAMLOGEU_QSO_UPLOAD_DATE" => "",
           "HAMLOGEU_QSO_UPLOAD_STATUS" => "",
           "HAMQTH_QSO_UPLOAD_DATE" => "",
           "HAMQTH_QSO_UPLOAD_STATUS" => "",
           "HRDLOG_QSO_UPLOAD_DATE" => "",
           "HRDLOG_QSO_UPLOAD_STATUS" => "",
	   "IOTA" => "",
           "IOTA_ISLAND_ID" => "",
           "ITUZ" => 0,
           "K_INDEX" => 0,
           "LAT" => "",
           "LON" => "",
           "LOTW_QSLRDATE" => "",
           "LOTW_QSLSDATE" => "",
           "LOTW_QSL_RCVD" => "",
           "LOTW_QSL_SENT" => "",
           "MAX_BURSTS" => 0,
           "MODE" => "",
           "MORSE_KEY_INFO" => "",
           "MORSE_KEY_TYPE" => "",
           "MS_SHOWER" => "",
           "MY_ALTITUDE" => 0,
           "MY_ANTENNA" => "",
           "MY_ANTENNA_INTL" => "",
           "MY_ARRL_SECT" => "",
           "MY_CITY" => "",
           "MY_CITY_INTL" => "",
           "MY_CNTY" => "",
           "MY_CNTY_ALT" => "",
           "MY_COUNTRY" => "",
           "MY_COUNTRY_INTL" => "",
           "MY_CQ_ZONE" => 0,
           "MY_DARC_DOK" => "",
           "MY_DXCC" => 0,
           "MY_FISTS" => "",
           "MY_GRIDSQUARE" => "",
           "MY_GRIDSQUARE_EXT" => "",
           "MY_IOTA" => "",
           "MY_IOTA_ISLAND_ID" => "",
           "MY_ITU_ZONE" => "",
           "MY_LAT" => "",
           "MY_LON" => "",
           "MY_MORSE_KEY_INFO" => "",
           "MY_MORSE_KEY_TYPE" => "",
           "MY_NAME" => "",
           "MY_NAME_INTL" => "",
           "MY_POSTAL_CODE" => "",
           "MY_POSTAL_CODE_INTL" => "",
           "MY_POTA_REF" => "",
           "MY_RIG" => "",
           "MY_RIG_INTL" => "",
           "MY_SIG" => "",
           "MY_SIG_INTL" => "",
           "MY_SIG_INFO" => "",
           "MY_SIG_INFO_INTL" => "",
           "MY_SOTA_REF" => "",
           "MY_STATE" => "",
           "MY_STREET" => "",
           "MY_STREET_INTL" => "",
           "MY_USACA_COUNTIES" => "",
           "MY_VUCC_GRIDS" => "",
           "MY_WWFF_REF" => "",
           "NAME" => "",
           "NAME_INTL" => "",
           "NOTES" => "",
           "NOTES_INTL" => "",
           "NR_BURSTS" => "",
           "NR_PINGS" => "",
           "OPERATOR" => "",
           "OWNER_CALLSIGN" => "",
           "PFX" => "",
           "POTA_REF" => "",
           "PRECEDENCE" => "",
           "PROP_MODE" => "",
           "PUBLIC_KEY" => "",
           "QRZCOM_QSO_DOWNLOAD_DATE" => "",
           "QRZCOM_QSO_DOWNLOAD_STATUS" => "",
           "QRZCOM_QSO_UPLOAD_DATE" => "",
           "QRZCOM_QSO_UPLOAD_STATUS" => "",
           "QSLMSG" => "",
           "QSLMSG_INTL" => "",
           "QSLMSG_RCVD" => "",
           "QSLRDATE" => "",
           "QSLSDATE" => "",
           "QSL_RCVD" => "",
           "QSL_RCVD_VIA" => "",
           "QSL_SENT" => "",
           "QSL_SENT_VIA" => "",
           "QSL_VIA" => "",
           "QSO_COMPLETE" => "",
           "QSO_DATE" => "",
           "QSO_DATE_OFF" => "",
           "QSO_RANDOM" => "",
           "QTH" => "",
           "QTH_INTL" => "",
           "REGION" => "",
           "RIG" => "",
           "RIG_INTL" => "",
           "RST_RCVD" => "",
           "RST_SENT" => "",
           "RX_PWR" => 0,
           "SAT_MODE" => "",
           "SAT_NAME" => "",
           "SFI" => "",
           "SIG" => "",
           "SIG_INTL" => "",
           "SIG_INFO" => "",
           "SIG_INFO_INTL" => "",
           "SILENT_KEY" => "",
           "SKCC" => "",
           "SOTA_REF" => "",
           "SRX" => 0,
           "SRX_STRING" => "",
           "STATE" => "",
           "STATION_CALLSIGN" => "",
           "STX" => 0,
           "STX_STRING" => "",
           "SUBMODE" => "",
           "SWL" => "",
           "TEN_TEN" => 0,
           "TIME_OFF" => "",
           "TIME_ON" => "",
           "TX_PWR" => 0,
           "UKSMG" => 0,
           "USACA_COUNTIES" => "",
           "VE_PROV" => "",
           "VUCC_GRIDS" => "",
           "WEB" => "",
           "WWFF_REF" => ""},
          {"ADDRESS" => "",
           "ADDRESS_INTL" => "",
           "AGE" => 0,
	   "ALTITUDE" => 0,
           "A_INDEX" => 0,
           "ANT_AZ" => 0,
           "ANT_EL" => 0,
           "ANT_PATH" => "",
           "ARRL_SECT" => "",
           "AWARD_SUBMITTED" => "",
           "AWARD_GRANTED" => "",
           "BAND" => "",
           "BAND_RX" => "",
           "CALL" => "",
           "CHECK" => "",
           "CLASS" => "",
           "CLUBLOG_QSO_UPLOAD_DATE" => "",
           "CLUBLOG_QSO_UPLOAD_STATUS" => "",
           "CNTY" => "",
           "CNTY_ALT" => "",
           "COMMENT" => "",
           "COMMENT_INTL" => "",
           "CONT" => "",
           "CONTACTED_OP" => "",
           "CONTEST_ID" => "",
           "COUNTRY" => "",
           "COUNTRY_INTL" => "",
           "CQZ" => 0,
           "CREDIT_GRANTED" => "",
           "CREDIT_SUBMITTED" => "",
           "DARC_DOK" => "",
           "DCL_QSLRDATE" => "",
           "DCL_QSLSDATE" => "",
           "DCL_QSL_RCVD" => "",
           "DCL_QSL_SENT" => "",
           "DISTANCE" => 0,
	   "DXCC" => 0,
	   "EMAIL" => "",
	   "EQ_CALL" => "",
	   "EQSL_QSLRDATE" => "",
	   "EQSL_QSLSDATE" => "",
	   "EQSL_QSL_RCVD" => "",
	   "EQSL_QSL_SENT" => "", 
           "FISTS" => "",
           "FISTS_CC" => "", 	
           "FORCE_INIT" => "",
           "FREQ" => 0,
           "FREQ_RX" => 0,
           "GRIDSQUARE" => "",
           "GRIDSQUARE_EXT" => "",
           "GUEST_OP" => "",
           "HAMLOGEU_QSO_UPLOAD_DATE" => "",
           "HAMLOGEU_QSO_UPLOAD_STATUS" => "",
           "HAMQTH_QSO_UPLOAD_DATE" => "",
           "HAMQTH_QSO_UPLOAD_STATUS" => "",
           "HRDLOG_QSO_UPLOAD_DATE" => "",
           "HRDLOG_QSO_UPLOAD_STATUS" => "",
           "IOTA" => "",
           "IOTA_ISLAND_ID" => "",
	   "ITUZ" => 0,
	   "K_INDEX" => 0,
	   "LAT" => "",
	   "LON" => "",
	   "LOTW_QSLRDATE" => "",
           "LOTW_QSLSDATE" => "",
           "LOTW_QSL_RCVD" => "",
           "LOTW_QSL_SENT" => "",
           "MAX_BURSTS" => 0,
           "MODE" => "",
           "MORSE_KEY_INFO" => "",
           "MORSE_KEY_TYPE" => "",
           "MS_SHOWER" => "",
           "MY_ALTITUDE" => 0,
           "MY_ANTENNA" => "",
           "MY_ANTENNA_INTL" => "",
           "MY_ARRL_SECT" => "",
           "MY_CITY" => "",
           "MY_CITY_INTL" => "",
	   "MY_CNTY" => "",
           "MY_CNTY_ALT" => "",
           "MY_COUNTRY" => "",
           "MY_COUNTRY_INTL" => "",
           "MY_CQ_ZONE" => 0,
           "MY_DARC_DOK" => "",
           "MY_DXCC" => 0,
           "MY_FISTS" => "",
           "MY_GRIDSQUARE" => "",
           "MY_GRIDSQUARE_EXT" => "",
           "MY_IOTA" => "",
           "MY_IOTA_ISLAND_ID" => "",
           "MY_ITU_ZONE" => "",
           "MY_LAT" => "",
           "MY_LON" => "",
           "MY_MORSE_KEY_INFO" => "",
           "MY_MORSE_KEY_TYPE" => "",
           "MY_NAME" => "",
           "MY_NAME_INTL" => "",
           "MY_POSTAL_CODE" => "",
           "MY_POSTAL_CODE_INTL" => "",
           "MY_POTA_REF" => "",
           "MY_RIG" => "",
           "MY_RIG_INTL" => "",
           "MY_SIG" => "",
           "MY_SIG_INTL" => "",
           "MY_SIG_INFO" => "",
           "MY_SIG_INFO_INTL" => "",
           "MY_SOTA_REF" => "",
           "MY_STATE" => "",
           "MY_STREET" => "",
           "MY_STREET_INTL" => "",
           "MY_USACA_COUNTIES" => "",
           "MY_VUCC_GRIDS" => "",
           "MY_WWFF_REF" => "",
           "NAME" => "",
           "NAME_INTL" => "",
           "NOTES" => "",
           "NOTES_INTL" => "",
           "NR_BURSTS" => "",
           "NR_PINGS" => "",
           "OPERATOR" => "",
           "OWNER_CALLSIGN" => "",
           "PFX" => "",
           "POTA_REF" => "",
           "PRECEDENCE" => "",
           "PROP_MODE" => "",
           "PUBLIC_KEY" => "",
           "QRZCOM_QSO_DOWNLOAD_DATE" => "",
           "QRZCOM_QSO_DOWNLOAD_STATUS" => "",
           "QRZCOM_QSO_UPLOAD_DATE" => "",
           "QRZCOM_QSO_UPLOAD_STATUS" => "",
           "QSLMSG" => "",
           "QSLMSG_INTL" => "",
           "QSLMSG_RCVD" => "",
           "QSLRDATE" => "",
           "QSLSDATE" => "",
           "QSL_RCVD" => "",
           "QSL_RCVD_VIA" => "",
           "QSL_SENT" => "",
           "QSL_SENT_VIA" => "",
           "QSL_VIA" => "",
           "QSO_COMPLETE" => "",
           "QSO_DATE" => "",
           "QSO_DATE_OFF" => "",
           "QSO_RANDOM" => "",
           "QTH" => "",
           "QTH_INTL" => "",
           "REGION" => "",
           "RIG" => "",
           "RIG_INTL" => "",
           "RST_RCVD" => "",
           "RST_SENT" => "",
           "RX_PWR" => 0,
           "SAT_MODE" => "",
           "SAT_NAME" => "",
           "SFI" => "",
           "SIG" => "",
           "SIG_INTL" => "",
           "SIG_INFO" => "",
           "SIG_INFO_INTL" => "",
           "SILENT_KEY" => "",
           "SKCC" => "",
	   "SOTA_REF" => "",
           "SRX" => 0,
           "SRX_STRING" => "",
           "STATE" => "",
           "STATION_CALLSIGN" => "",
           "STX" => 0,
           "STX_STRING" => "",
           "SUBMODE" => "",
           "SWL" => "",
           "TEN_TEN" => 0,
           "TIME_OFF" => "",
           "TIME_ON" => "",
           "TX_PWR" => 0,
           "UKSMG" => 0,
           "USACA_COUNTIES" => "",
           "VE_PROV" => "",
           "VUCC_GRIDS" => "",
           "WEB" => "",
           "WWFF_REF" => ""});

# data types: D date, T time, M multiline, G international characters and
# line breaks, S character/string, I international string, N number,
# L location, B boolean, E enumeration, A award list
my %typ = ("ADDRESS" => "M",
           "ADDRESS_INTL" => "G",
           "AGE" => "N",
           "ALTITUDE" => "N",
	   "A_INDEX" => "N",
           "ANT_AZ" => "N",
           "ANT_EL" => "N",
           "ANT_PATH" => "E",
           "ARRL_SECT" => "E",
           "AWARD_SUBMITTED" => "",
           "AWARD_GRANTED" => "",
           "BAND" => "E",
           "BAND_RX" => "E",
           "CALL" => "S",
           "CHECK" => "S",
           "CLASS" => "S",
           "CLUBLOG_QSO_UPLOAD_DATE" => "D",
           "CLUBLOG_QSO_UPLOAD_STATUS" => "E",
           "CNTY" => "E",
           "CNTY_ALT" => "E",
           "COMMENT" => "S",
           "COMMENT_INTL" => "I",
	   "CONT" => "E",
	   "CONTACTED_OP" => "S",
	   "CONTEST_ID" => "S",
	   "COUNTRY" => "S",
           "COUNTRY_INTL" => "I",
           "CQZ" => "N",
           "CREDIT_GRANTED" => "A", # can be also CreditList 
	   "CREDIT_SUBMITTED" => "A", # can be also CreditList 
           "DARC_DOK" => "E",
           "DCL_QSLRDATE" => "D",
           "DCL_QSLSDATE" => "D",
           "DCL_QSL_RCVD" => "E",
           "DCL_QSL_SENT" => "E",
           "DISTANCE" => "N",
	   "DXCC" => "E",
	   "EMAIL" => "S",
           "EQ_CALL" => "S",
           "EQSL_QSLRDATE" => "D",
           "EQSL_QSLSDATE" => "D",
           "EQSL_QSL_RCVD" => "E",
           "EQSL_QSL_SENT" => "E",
           "FISTS" => "N",
           "FISTS_CC" => "N",    
           "FORCE_INIT" => "B",
           "FREQ" => "N",
           "FREQ_RX" => "N",
           "GRIDSQUARE" => "S",
           "GUEST_OP" => "S",
           "HAMLOGEU_QSO_UPLOAD_DATE" => "D",
           "HAMLOGEU_QSO_UPLOAD_STATUS" => "E",
           "HAMQTH_QSO_UPLOAD_DATE" => "D",
           "HAMQTH_QSO_UPLOAD_STATUS" => "E",
           "HRDLOG_QSO_UPLOAD_DATE" => "D",
           "HRDLOG_QSO_UPLOAD_STATUS" => "E",
	   "IOTA" => "S",
	   "IOTA_ISLAND_ID" => "N",
	   "ITUZ" => "N",
	   "K_INDEX" => "N",
           "LAT" => "L",
           "LON" => "L",
	   "LOTW_QSLRDATE" => "D",
	   "LOTW_QSLSDATE" => "D",
	   "LOTW_QSL_RCVD" => "E",
	   "LOTW_QSL_SENT" => "E",
	   "MAX_BURSTS" => "N",
	   "MODE" => "E",
           "MORSE_KEY_INFO" => "S",
           "MORSE_KEY_TYPE" => "E",
           "MS_SHOWER" => "S",
           "MY_ALTITUDE" => "N",       
           "MY_ANTENNA" => "S",
           "MY_ANTENNA_INTL" => "I",
           "MY_ARRL_SECT" => "E",
           "MY_CITY" => "S",
           "MY_CITY_INTL" => "I",
	   "MY_CNTY" => "E",
           "MY_CNTY_ALT" => "E",
           "MY_COUNTRY" => "S",
           "MY_COUNTRY_INTL" => "I",
	   "MY_CQ_ZONE" => "N",
           "MY_DARC_DOK" => "E",
           "MY_DXCC" => "E",
           "MY_FISTS" => "N",
	   "MY_GRIDSQUARE" => "S",
           "MY_GRIDSQUARE_EXT" => "S",
           "MY_IOTA" => "S",
           "MY_IOTA_ISLAND_ID" => "N",
           "MY_ITU_ZONE" => "N",
           "MY_LAT" => "L",
           "MY_LON" => "L",
           "MY_MORSE_KEY_INFO" => "S",
           "MY_MORSE_KEY_TYPE" => "E",
	   "MY_NAME" => "S",
           "MY_NAME_INTL" => "I",
	   "MY_POSTAL_CODE" => "S",
           "MY_POSTAL_CODE_INTL" => "I",
           "MY_POTA_REF" => "S",
	   "MY_RIG" => "S",
           "MY_RIG_INTL" => "I",
	   "MY_SIG" => "S",
           "MY_SIG_INTL" => "I",
	   "MY_SIG_INFO" => "S",
           "MY_SIG_INFO_INTL" => "I",
	   "MY_SOTA_REF" => "S",
	   "MY_STATE" => "E",
	   "MY_STREET" => "S",
           "MY_STREET_INTL" => "I",
           "MY_USACA_COUNTIES" => "S",
           "MY_VUCC_GRIDS" => "S",
           "MY_WWFF_REF" => "S",
           "NAME" => "S",
           "NAME_INTL" => "I",
	   "NOTES" => "M",
           "NOTES_INTL" => "G",
	   "NR_BURSTS" => "N",
	   "NR_PINGS" => "N",
	   "OPERATOR" => "S",
	   "OWNER_CALLSIGN" => "S",
	   "PFX" => "S",
           "POTA_REF" => "S",
	   "PRECEDENCE" => "S",
	   "PROP_MODE" => "E",
	   "PUBLIC_KEY" => "S",
           "QRZCOM_QSO_DOWNLOAD_DATE" => "D",
           "QRZCOM_QSO_DOWNLOAD_STATUS" => "E",
           "QRZCOM_QSO_UPLOAD_DATE" => "D",
           "QRZCOM_QSO_UPLOAD_STATUS" => "E",
           "QSLMSG" => "M",
           "QSLMSG_INTL" => "G",
           "QSLMSG_RCVD" => "M",
           "QSLRDATE" => "D",
           "QSLSDATE" => "D",
	   "QSL_RCVD" => "E",
	   "QSL_RCVD_VIA" => "E",
	   "QSL_SENT" => "E",
	   "QSL_SENT_VIA" => "E",
           "QSL_VIA" => "S",
           "QSO_COMPLETE" => "E",
           "QSO_DATE" => "D",
           "QSO_DATE_OFF" => "D",
           "QSO_RANDOM" => "B",
           "QTH" => "S",
           "QTH_INTL" => "I",
           "REGION" => "E",
           "RIG" => "M",
           "RIG_INTL" => "G",
	   "RST_RCVD" => "S",
	   "RST_SENT" => "S",
           "RX_PWR" => "N",
           "SAT_MODE" => "S",
           "SAT_NAME" => "S",
           "SFI" => "N",
           "SIG" => "S",
           "SIG_INTL" => "I",
           "SIG_INFO" => "S",
           "SIG_INFO_INTL" => "I",
           "SILENT_KEY" => "B",
           "SKCC" => "S",
	   "SOTA_REF" => "S",
           "SRX" => "N",
           "SRX_STRING" => "S",
           "STATE" => "E",
	   "STATION_CALLSIGN" => "S",
           "STX" => "N",
           "STX_STRING" => "S",
           "SUBMODE" => "S",
           "SWL" => "B",
           "TEN_TEN" => "N",
           "TIME_OFF" => "T",
           "TIME_ON" => "T",
           "TX_PWR" => "N",
           "UKSMG" => "N",
           "USACA_COUNTIES" => "S",
           "VE_PROV" => "S",
           "VUCC_GRIDS" => "S",
           "WEB" => "S",
           "WWFF_REF" => "S");


# csv out separator
my $csv_out_separator = ",";

# csv record convertion hash
my %cconv = ("Band" => "BAND",
	     "Full reference" => "SOTA_REF",
	     "Station worked" => "CALL",
             "OtherCallsign" => "CALL",
	     "Notes" => "COMMENT",
             "Comments" => "COMMENT",
	     "Mode" => "MODE",
	     "Date" => "QSO_DATE",
	     "Call used" => "STATION_CALLSIGN",
             "YourCallsign" => "STATION_CALLSIGN",
	     "Time" => "TIME_ON",
	     "End" => "TIME_OFF",
	     "GridLoc" => "GRIDSQUARE",
	     "RSTs" => "RST_SENT",
	     "RSTr" => "RST_RCVD",
	     "SerRx" => "SRX_STRING",
	     "SerTx" => "STX_STRING",
	     "QSLr" => "QSL_RCVD",
	     "QSLrv" => "QSL_RCVD_VIA",
	     "QSLs" => "QSL_SENT",
	     "QSLsv" => "QSL_SENT_VIA",
	     "QSLvia" => "QSL_VIA",
             "Call (activator)" => "STATION_CALLSIGN",
             "Full Reference (activator)" => "MY_SOTA_REF",
             '"Your Summit"' => "MY_SOTA_REF",
	     "Call (chaser)" => "CALL",
             "Full Reference (chaser)" => "SOTA_REF",
             "OtherSummit" => "SOTA_REF",
	     "Call (my call)" => "STATION_CALLSIGN",
             "Full Reference (my summit)" => "MY_SOTA_REF",
             "Call (his call)" => "CALL",
             "Full Reference (his summit)" => "SOTA_REF");

# modes for adif 3.1.5, import only modes at end
my @modes=("AM", "ARDOP", "ATV", "CHIP", "CLO", "CONTESTI", "CW", "DIGITALVOICE", "DOMINO", "DYNAMIC", "FAX", "FM", "FSK441", "FT8", "HELL", "ISCAT", "JT4", "JT6M", "JT9", "JT44", "JT65", "MFSK", "MSK144", "MT63", "OLIVIA", "OPERA", "PAC", "PAX", "PKT", "PSK", "PSK2K", "Q15", "QRA64", "ROS", "RTTY", "RTTYM", "SSB", "SSTV", "T10", "THOR", "THRB", "TOR", "V4", "VOI", "WINMOR", "WSPR", "AMTORFEC", "ASCI", "C4FM", "CHIP64", "CHIP128", "DOMINOF", "DSTAR", "FMHELL", "FSK31", "GTOR", "HELL80", "HFSK", "JT4A", "JT4B", "JT4C", "JT4D", "JT4E", "JT4F", "JT4G", "JT65A", "JT65B", "JT65C", "MFSK8", "MFSK16", "PAC2", "PAC3", "PAX2", "PCW", "PSK10", "PSK31", "PSK63", "PSK63F", "PSK125", "PSKAM10", "PSKAM31", "PSKAM50", "PSKFEC31", "PSKHELL", "QPSK31", "QPSK63", "QPSK125", "THRBX");
 
 
# import-only modes for adif 3.1.5
my @modesimport=("AMTORFEC", "ASCI", "C4FM", "CHIP64", "CHIP128", "DOMINOF", "DSTAR", "FMHELL", "FSK31", "GTOR", "HELL80", "HFSK", "JT4A", "JT4B", "JT4C", "JT4D", "JT4E", "JT4F", "JT4G", "JT65A", "JT65B", "JT65C", "MFSK8", "MFSK16", "PAC2", "PAC3", "PAX2", "PCW", "PSK10", "PSK31", "PSK63", "PSK63F", "PSK125", "PSKAM10", "PSKAM31", "PSKAM50", "PSKFEC31", "PSKHELL", "QPSK31", "QPSK63", "QPSK125", "THRBX");

# submode hash to mode for adif 3.1.4
my %submodes = ("8PSK125" => "PSK",
                "8PSK125F" => "PSK",
                "8PSK125FL" => "PSK",
                "8PSK250" => "PSK",
                "8PSK250F" => "PSK",
                "8PSK250FL" => "PSK",
                "8PSK500" => "PSK",
                "8PSK500F" => "PSK",
                "8PSK1000" => "PSK",
                "8PSK1000F" => "PSK",
                "8PSK1200F" => "PSK",
                "AMTORFEC" => "TOR",
                "ASCI" => "RTTY",
                "C4FM" => "DIGITALVOICE",
                "CHIP64" => "CHIP",
                "CHIP128" => "CHIP",
                "DMR" => "DIGITALVOICE",
                "DOM-M" => "DOMINO",
                "DOM4" => "DOMINO",
                "DOM5" => "DOMINO",
                "DOM8" => "DOMINO",
                "DOM11" => "DOMINO",
                "DOM16" => "DOMINO",
                "DOM22" => "DOMINO",
                "DOM44" => "DOMINO",
                "DOM88" => "DOMINO",
		"DOMINOEX" => "DOMINO",
                "DOMINOF" => "DOMINO",
                "DSTAR" => "DIGITALVOICE",
                "FMHELL" => "HELL",
                "FREEDV" => "DIGITALVOICE",
                "FSK31" => "PSK",
                "FSKH105" => "HELL",
                "FSKH245" => "HELL",
                "FSKHELL" => "HELL",
                "FSQCALL" => "MFSK",
                "FST4" => "MFSK",
                "FST4W" => "MFSK",
                "FT4" => "MFSK",
                "GTOR" => "TOR",
                "HELL80" => "HELL",
                "HELLX5" => "HELL",
                "HELLX9" => "HELL",
                "HFSK" => "HELL",
                "ISCAT-A" => "ISCAT",
                "ISCAT-B" => "ISCAT",
                "JS8" => "MFSK",
		"JT4A" => "JT4",
                "JT4B" => "JT4",
                "JT4C" => "JT4",
                "JT4D" => "JT4",
                "JT4E" => "JT4",
                "JT4F" => "JT4",
                "JT4G" => "JT4",
                "JT9-1" => "JT9",
                "JT9-2" => "JT9",
                "JT9-5" => "JT9",
                "JT9-10" => "JT9",
                "JT9-30" => "JT9",
                "JT9A" => "JT9",
                "JT9B" => "JT9",
                "JT9C" => "JT9",
                "JT9D" => "JT9",
                "JT9E" => "JT9",
                "JT9E FAST" => "JT9",
                "JT9F" => "JT9",
                "JT9F FAST" => "JT9",
                "JT9G" => "JT9",
                "JT9G FAST" => "JT9",
                "JT9H" => "JT9",
                "JT9H FAST" => "JT9",
                "JT65A" => "JT65",
                "JT65B" => "JT65",
                "JT65B2" => "JT65",
                "JT65C" => "JT65",
                "JT65C2" => "JT65",
                "JTMS" => "MFSK",
		"LSB" => "SSB",
                "M17" => "DGITALVOICE",
                "MFSK4" => "MFSK",
                "MFSK8" => "MFSK",
                "MFSK11" => "MFSK",
                "MFSK16" => "MFSK",
                "MFSK22" => "MFSK",
                "MFSK31" => "MFSK",
                "MFSK32" => "MFSK",
                "MFSK64" => "MFSK",
                "MFSK64L" => "MFSK",
                "MFSK128" => "MFSK",
                "MFSK128L" => "MFSK",
                "NAVTEX" => "TOR",
                "OLIVIA 4/125" => "OLIVIA", 
                "OLIVIA 4/250" => "OLIVIA", 
                "OLIVIA 8/250" => "OLIVIA", 
                "OLIVIA 8/500" => "OLIVIA", 
                "OLIVIA 16/500" => "OLIVIA", 
                "OLIVIA 16/1000" => "OLIVIA", 
                "OLIVIA 32/1000" => "OLIVIA", 
                "OPERA-BEACON" => "OPERA",
                "OPERA-QSO" => "OPERA",
                "PAC2" => "PAC",
                "PAC3" => "PAC",
                "PAC4" => "PAC",
                "PAX2" => "PAX",
                "PCW" => "CW",
                "PSK10" => "PSK",
                "PSK31" => "PSK",
                "PSK63" => "PSK",
                "PSK63F" => "PSK",
                "PSK63RC10" => "PSK",
                "PSK63RC20" => "PSK",
                "PSK63RC32" => "PSK",
                "PSK63RC4" => "PSK",
                "PSK63RC5" => "PSK",
                "PSK125" => "PSK",
                "PSK125RC10" => "PSK",
                "PSK125RC12" => "PSK",
                "PSK125RC16" => "PSK",
                "PSK125RC4" => "PSK",
                "PSK125RC5" => "PSK",
                "PSK250" => "PSK",
                "PSK250RC2" => "PSK",
                "PSK250RC3" => "PSK",
                "PSK250RC5" => "PSK",
                "PSK250RC6" => "PSK",
                "PSK250RC7" => "PSK",
                "PSK500" => "PSK",
                "PSK500RC2" => "PSK",
                "PSK500RC3" => "PSK",
                "PSK500RC4" => "PSK",
                "PSK800RC2" => "PSK",
                "PSK1000" => "PSK",
                "PSK1000RC2" => "PSK",
                "PSKAM10" => "PSK",
                "PSKAM31" => "PSK",
                "PSKAM50" => "PSK",
                "PSKFEC31" => "PSK",
                "PSKHELL" => "HELL",
                "QPSK31" => "PSK",
                "Q65" => "MFSK",
		"QPSK63" => "PSK",
                "QPSK125" => "PSK",
                "QPSK250" => "PSK",
                "QPSK500" => "PSK",
                "QRA64A" => "QRA64",
                "QRA64B" => "QRA64",
                "QRA64C" => "QRA64",
                "QRA64D" => "QRA64",
                "QRA64E" => "QRA64",
                "ROS-EME" => "ROS",
                "ROS-HF" => "ROS",
                "ROS-MF" => "ROS",
		"SIM31" => "PSK",
                "SITORB" => "TOR",
                "SLOWHELL" => "HELL",
                "THOR-M" => "THOR",
                "THOR4" => "THOR",
                "THOR5" => "THOR",
                "THOR8" => "THOR",
                "THOR11" => "THOR",
                "THOR16" => "THOR",
                "THOR22" => "THOR",
                "THOR25X4" => "THOR",
                "THOR50X1" => "THOR",
                "THOR50X2" => "THOR",
                "THOR100" => "THOR",
                "THRBX" => "THRB",
                "THRBX1" => "THRB",
                "THRBX2" => "THRB",
                "THRBX4" => "THRB",
                "THROB1" => "THRB",
                "THROB2" => "THRB",
                "THROB4" => "THRB",
		"USB" => "SSB",
                "VARA HF" => "DYNAMIC",
                "VARA SATELLITE" => "DYNAMIC",
                "VARA FM 1200" => "DYNAMIC",
                "VARA FM 9600" => "DYNAMIC"); 


# propagation modes
my @pmodes=("AS", "AUE", "AUR", "BS", "ECH", "EME", "ES", "F2", "FAI", "GWAVE", "INTERNET", "ION", "IRL", "LOS", "MS", "RPT", "RS", "SAT", "TEP", "TR");

# bands for adif 3.1.5
my @bands=("2190M", "630M", "560M", "160M", "80M", "60M", "40M", "30M", "20M", "17M", "15M", "12M", "10M", "8M", "6M", "5M", "4M", "2M", "1.25M", "70CM", "33CM", "23CM", "13CM", "9CM", "6CM", "3CM", "1.25CM", "6MM", "4MM", "2.5MM", "2MM", "1MM", "SUBMM");

my @bandl=(0.1357, 0.472, 0.501, 1.8, 3.5, 5.06, 7.0, 10.1, 14.0, 18.068, 21.0, 24.89, 28.0, 40, 50, 54.000001, 70, 144, 222, 420, 902, 1240, 2300, 3300, 5650, 10000, 24000, 47000, 75500, 119980, 142000, 241000, 300000);

my @bandu=(0.1378, 0.479, 0.504, 2.0, 4.0, 5.45, 7.3, 10.15, 14.35, 18.168, 21.45, 24.99, 29.7, 45, 54, 69.9, 71, 148, 225, 450, 928, 1300, 2450, 3500, 5925, 10500, 24250, 47200, 81000, 120020, 149000, 250000, 7500000);

my @bandf=(0.137, 0.475, 0.5, 1.8, 3.5, 5, 7, 10.1, 14, 18.068, 21, 24.89, 28, 50, 60, 70, 144, 222, 432, 902, 1240, 2300, 3300, 5650, 10000, 24000, 47000, 75500, 119980, 142000, 241000);

# satellites for LoTW
my @sats=("AISAT1", "AO-10", "AO-13", "AO-16", "AO-21", "AO-24", "AO-27", "AO-3", "AO-4", "AO-40", "AO-51", "AO-6", "AO-7", "AO-73", "AO-8", "AO-85", "AO-91", "AO-92", "AO-109", "ARISS", "BO-102", "BY70-1", "CAS-3H", "CAS-4A", "CAS-4B", "DO-64", "EO-79", "EO-88", "ESEO", "FO-12", "FO-20", "FO-29", "FO-99", "FS-3", "HO-68", "HO-107", "IO-86", "JO-97", "KEDR", "LO-19", "LO-78", "LO-87", "LO-90", "MIREX", "NO-44", "NO-83", "NO-84", "NO-103", "NO-104", "PO-101", "QO-100", "RS-1", "RS-10", "RS-11", "RS-12", "RS-13", "RS-15", "RS-2", "RS-44", "RS-5", "RS-6", "RS-7", "RS-8", "SAREX", "SO-35", "SO-41", "SO-50", "SO-67", "TAURUS", "UKUBE1", "TO-108", "UO-14", "UVSQ", "VO-52", "XW-2A", "XW-2B", "XW-2C", "XW-2D", "XW-2E", "XW-2F");

# typical rigs
my @rigs=("ATS3B","ATS3B.1","ATS4","FT100","FT1802","FT1900","FT1000","FT2000","FT250","FT270","FT2800","FT450","FTDX5000","FT60R","FT736","FT747","FT757","FT757GX","FT767","FT817","FT817ND","FT818ND","FT840","FT847","FT857","FT8800","FT8900","FTM10R","FTM350R","FT890","FT897","FT900","FT920","FT950","FT990","FTDX9000","VX110","VX120","VX127","VX150","VX170","VX3R","VX6R","VX7R","VX8R","VX8RD","FT50R","FT7800","IC271","IC275","IC471","IC475","IC7000","IC703","IC706","IC706MkII","IC707","IC718","IC7200","IC725","IC726","IC728","IC735","IC736","IC737","IC746","IC751","IC756","IC761","IC765","IC775","IC78","IC7800","IC781","IC820","IC821","IC910","IC970","ICR10","ICR20","ICR7000","ICR71","ICR7100","ICR72","ICR75","ICR8500","ICR9000","K1","KX1","K2","K3","R5000","R8A","R8B","THD7A","THF7E","THG71","TMD700","TMV7","TS140","TS2000","TS440","TS450","TS480","TS50S","TS570","TS570","TS680","TS690","TS711","TS790","TS811","TS850","TS870","TS930","TS940","TS950","VR5000");

# morse key types Straight Key, Sideswiper, Mechanical semi-automatic keyer
# or Bug, Mechanical fully-automatic keyer or Bug, Single Paddle, Dual Paddle,
# Computer Driven
my @morsekeytypes = ("SK", "SS", "BUG", "FAB", "SP", "DP", "CPU");

# /usr/(local/)share/dxcc/cty.plist for country, CQ, ITU and continent if
# available
my %ctylist;

# options: -h help, -V version, -v verbose, -f adif file, -l list qsos etc
our ($opt_h,$opt_V,$opt_v,$opt_f,$opt_m,$opt_M,$opt_s,$opt_l,$opt_o,$opt_i,$opt_b,$opt_L,$opt_t,$opt_C,$opt_p,$opt_e,$opt_A,$opt_D,$opt_S,$opt_x,$opt_F,$opt_d,$opt_c,$opt_Q,$opt_q,$opt_a,$opt_u,$opt_R,$opt_2,$opt_X,$opt_T,$opt_N);

$opt_h=0;$opt_V=0;$opt_v=0;$opt_f="";$opt_m="";$opt_M="";$opt_s=0;$opt_l=0;
$opt_o=0;$opt_i=0;$opt_b=0;$opt_L=0;$opt_t=5;$opt_C="";$opt_p="";
$opt_e=0;$opt_A="";$opt_D="";$opt_S="";$opt_x=0;$opt_F="";$opt_d=0;
$opt_c=0;$opt_Q="";$opt_q="";$opt_a="";$opt_u=0;$opt_R="";$opt_2=0;$opt_X=0;
$opt_T="";$opt_N=0;

# main program
my $nqso=0; # number of QSOs
my $fdate="21000101"; # first date in file
my $ldate="11000101"; # last date in file
my $ftime="235959"; # first time in file
my $ltime="000000"; # last time in file
my $nerr=0; # number of errors

my $nqsls=0; # number of sent QSLs
my $nqslr=0; # number of received QSLs
my $neqsls=0; # number of sent eQSLs
my $neqslr=0; # number of received eQSLs
my $nlqsls=0; # number of sent LoTW QSLs
my $nlqslr=0; # number of received LoTW QSLs
my $lstqsl="11000101"; # most recent received QSL date
my $lsteqsl="11000101"; # most recent received eQSL date
my $lstlqsl="11000101"; # most recent received LoTW QSL date
my $sqsos=0; # number of shown QSOs
my %bndstat; # hash for band statistics
my %modstat; # hash for mode statistics
my %submodstat; # hash for submode statistics
my %dxstat; # hash for dxcc statistics
my %ctrystat; # hash for country statistics
my %dxstatc; # hash for confirmed dxcc statistics
my %cqzstat; # hash for CQ zone statistics
my %ituzstat; # hash for ITU zone statistics
my %contstat; # hash for continent statistics
my %gridstat; # grid square statistics
my $gridlen=4; # length of grid square in statistics
my %lonlatstat; # longitude/latitude statistics 
my %iotastat; # IOTA statistics
my %sotastat; # SOTA statistics
my %potastat; # POTA statistics
my %wwffstat; # WWFF statistics
my %cntystat; # county statistics
my %statestat; # state statistics

my %stationstat; # station statistics
my %propstat; # propagation statistics
my $dloss=0; # possible data loss during merging

my @qsos; # array of parsed adif lines with time in beginning for sorting
my $qi=0; # qso index

my %qsl; # hash for printing QSL labels

my %qso2d; # date hash to check duplicate QSOs
my %qso2t; # time hash to check duplicate QSOs

my $mycall=""; # my call sign in log
my $myiota=""; # my iota in log
my $mysota=""; # my sota in log
my $myloc=""; # my locator in log

my @tfile; # template file for output with ADIF tags, e.g. _CALL
my $ntf=0; # number of lines in template file

getopts('2hVvf:m:M:lsoibLt:C:A:D:p:eS:xq:F:dcQ:a:uR:XT:N');

printversion() if($opt_V); 
printusage() if($opt_h||(!$opt_f)); 

setvariables();

$opt_t=24*60 if((!$opt_t)&&($opt_2));

$gridlen=6 if($opt_q =~ /grid6/);
$gridlen=8 if($opt_q =~ /grid8/);
$gridlen=10 if($opt_q =~ /grid10/);


# remove USERDEF fields
if($opt_u)
{
    delete $hed{USERDEF1};
    delete $hed{USERDEF2};
    delete $rec[0]{MY_SOTA} if( $rec[0]{MY_SOTA} );
    delete $rec[1]{MY_SOTA} if( $rec[1]{MY_SOTA} );
    delete $rec[0]{SOTA} if( $rec[0]{SOTA} );
    delete $rec[1]{SOTA} if( $rec[1]{SOTA} );
    delete $typ{MY_SOTA} if( $typ{MY_SOTA} );
    delete $typ{SOTA} if( $typ{SOTA} );
    delete $cconv{"Full reference"};
}

$opt_Q = uc (substr $opt_Q, 0, 1) if( $opt_Q );

# check if point calculation script can be called
`$opt_p`|| die("Script $opt_p not found\n") if($opt_p);

my $file1=""; # main file
my $file2=""; # optional file for merging

$file1=$opt_f;
if(($file1 ne '-')&&($file1 ne '-csv')&&($file1 ne '-txt')&&($file1 ne '-cbr'))
{
    ((-e $file1)&&(-r $file1)) || die("Can not read $file1.\n");
}

$file2=$opt_m if($opt_m);
$file2=$opt_M if($opt_M);
if($file2)
{
    ((-e $file2)&&(-r $file2)) || die("Can not read $file2.\n");
}

# print header for ADIF file unless reading ADIF-file, in that case print
# only after reading the header first 
PrintAdifHead() if(($opt_o)&&($file1 !~ /\w+\.adi$/i)&&($file1 !~ /\w+\.adif$/i)&&($file1 ne "-")); 

PrintTxtHead() if((($opt_l)||($opt_e))&&($opt_v));

my $tpnts=0; # claimed points
my $cabcall=PrintCabHead() if($opt_C); # print header for Cabrillo file

# check that valid record name is given for addition/deletion
my ($recn, $recn2);
my @arecs;
if( $opt_A )
{
    @arecs = split /,/, $opt_A;
    foreach( @arecs )
    {
        ($recn,) = split /=/, $_;
        die("Unknown record name $recn, exit now.\n") if( !exists( $typ{ $recn } ) );
    }
}

my @drecs;
if( $opt_D )
{
    @drecs = split /,/, $opt_D;
    foreach( @drecs )
    {
        warn("Unknown record name $_.\n") if( !exists( $typ{ $_ } ) );
    }
}

my $srecn = ""; 
my $srecv = "";
my $srecu = "";
if( $opt_S )
{
    ($srecn, $srecv, $srecu) = split /[=,]/, $opt_S;
    $gridlen = 6 if( ($srecn eq "GRIDSQUARE") && ( length($srecv) == 6 ) );
    $gridlen = 8 if( ($srecn eq "GRIDSQUARE") && ( length($srecv) == 8 ) );
    $gridlen = 10 if( ($srecn eq "GRIDSQUARE") && ( length($srecv) == 10 ) );

    die("Unknown record name $srecn, exit now.\n") if( !exists( $typ{ $srecn } ) );
}

my @frecs;
if( $opt_F )
{
    @frecs = split /,/, $opt_F;
    foreach( @frecs )
    {
        ($recn,,) = split /=/, $_;
        die("Unknown record name $recn, exit now.\n") if( !exists( $typ{ $recn } ) );
    }
}

my @mrecs;
if( $opt_R )
{
    @mrecs = split /,/, $opt_R;
    foreach( @mrecs )
    {
	($recn, $recn2) = split /=/, $_;
        die("Unknown record name $recn, exit now.\n") if( !exists( $typ{ $recn } ) );
        die("Unknown record name $recn2, exit now.\n") if( !exists( $typ{ $recn2 } ) );
    }
}

# read -T template file to an array
if($opt_T)
{
    my $TEMPFILE;
    if(open($TEMPFILE,"<",$opt_T))
    {
	my $line=<$TEMPFILE>;
	$ntf=0;
	while($line)
	{
	    $tfile[$ntf]=$line;
	    $ntf++;
	    $line=<$TEMPFILE>;
	}
    }
    else
    {
	print "Could not open $opt_T\n";
	exit;
    }
}

# check if /usr/(local/)share/dxcc/cty.plist or dxcc can be used to check 
# the call sign
if( $opt_c )
{
    if( -r $ctyplist )
    {
        ReadCtyPlist( $ctyplist );
    }
    else
    { 
        `dxcc`|| die("dxcc not found\n");
    }
}    


if( $opt_f ) 
{
    if( $file1 )
    {
        # adif file
        if( $file1 =~ /\w+\.adi$/i || $file1 =~ /\w+\.adif$/i || $file1 eq "-" )
        {
            $nqso = ReadAdifLog($file1, $file2);
            PrintInfo( $nqso ) if( $opt_i );
        }
        # csv file
        elsif( $file1 =~ /\w+\.csv$/i || $file1 eq "-csv" )
        {
            $nqso = ReadCsvLog( $file1 );
            PrintInfo( $nqso ) if( $opt_i );
        }
        # text file
        elsif( $file1 =~ /\w+\.txt$/i || $file1 eq "-txt" )
        {
            $nqso = ReadTxtLog( $file1 );
            PrintInfo( $nqso ) if( $opt_i );
        }
        # cabrillo file
        elsif( $file1 =~ /\w+\.cbr$/i || $file1 eq "-cbr" )
        {
            $nqso = ReadCabLog( $file1 );
            PrintInfo( $nqso ) if( $opt_i );
        }
        else
        {
            print "$file1 unknown suffix\n";
        }
    }
}

#  print sorted adif log to simple ascending time order
#  for descending time order use 'sort {$b <=> $a} (@qsos)';
if( $opt_s )
{
    @qsos = sort( @qsos );
    PrintAdifHead();
    PrintQsos();
}

# last lines for Cabrillo file
if($opt_C)
{
    print "END-OF-LOG:\n"; 
    if($opt_p)
    {
	print (sprintf "Total points: %-10.2f\n", $tpnts);
	print "Move to CLAIMED-SCORE in Cabrillo template file and run again without '-p'\n";
    }
} 

# print QSL labels unless switch -o
PrintQsls() if(($opt_Q)&&(!$opt_o));

# print results from simple query
PrintInfo($nqso) if($opt_q);

print "Warning: data changed $dloss times during merging\n" if($dloss>0);

# set variables from command line, e.g. 'adifmerg -f file.adi -x csv_out_separator=$'\t' '
sub setvariables
{
    for(my $i = 0; $i < scalar( @ARGV ); $i++)
    {
       $dbug = 1 if( $ARGV[ $i ] =~ /debug/i );
       print "-- match ", $ARGV[ $i ], "\n" if( $dbug );
       $csv_out_separator = $1 if( $ARGV[ $i ] =~ "csv_out_separator=([,;:\|\ \t\-\n@#\$\/%_])" );
       print "-- CSV out separator = ", $csv_out_separator, "\n" if( $dbug );
    }
}

# read in QSO data from ADIF file
sub ReadAdifLog
{
    my $file1 = shift;
    my $file2 = shift;
    my $LOGFILE;
    my $LOGFILE2;
    my $l = 0;
    my $l2 = 0;
    my $line = "";
    my $line2 = "";
    my $ok = 0;
    my $ne = 0;
    my $serr = ""; # type of error
    my $res = "";
    my $res2 = "";
    my $c;
    my $c2;
    my $rcc1 = 0; # record 0 complete
    my $rcc2 = 0; # record 1 complete

    if( open( $LOGFILE, "<", $file1 ) || $file1 eq "-" )
    {
        # parse header if first character is not '<'
        if( $file1 ne "-" )
        {
            $line = <$LOGFILE>;
        }
        else 
        {
            $line = <>;
        }

        $c = substr $line, 0, 1 if( $line );
        if( $c && ($c ne "<") )
        {
            while( $line && $res !~ /^<eoh>.*/i )
            {
                $res = AdifHead( $line );
                if( $file1 ne "-" )
                {
                    $line = <$LOGFILE>;
                }
                else 
                {
                    $line = <>;
                }
            }
            print "--end of header\n" if( $dbug );
            $res = substr $res,5,( length( $res ) - 5 ) if( length( $res ) >= 5 );
            print "--rest of line: $res\n" if( $dbug );
        }

        # second file for merging
        if( $file2 )
        {
            if( open( $LOGFILE2, "<", $file2 ) )
            {
                # parse header if first character is not '<'
                $line2 = <$LOGFILE2>;
                $c2 = substr $line2, 0, 1;
                if( $c2 ne "<" )
                {
                    while( $line2 && $res2 !~ /^<eoh>.*/i )
                    {
                        $res2 = AdifHead( $line2 );
                        $line2 = <$LOGFILE2>;
                    }
                    print "--end of header2\n" if( $dbug );
                    $res2 = substr $res2, 5, ( length( $res2 ) - 5 );
                    print "--rest of line: $res2\n" if( $dbug );
                }
            }
            else
            {
                print "Could not open $file2\n";
                exit;
            }
        }

        PrintAdifHead() if( $opt_o );

        # fill the first two records from (two) files
        $line = $res . $line if( $line && $res );
        while( $line && !$rcc1 )
        {
            ($res, $rcc1, $l) = ReadAdifRec( $line, $l, 0);
            if( $file1 ne "-" )
            {
                $line = <$LOGFILE>;
            }
            else 
            {
                $line = <>;
            }
        }
        $l++ if( $rcc1 );
	    
        $line2 = $res2 . $line2;
        while( $line2 && !$rcc2 )
        {
            ($res2, $rcc2, $l2) = ReadAdifRec( $line2, $l2, 1 );
            $line2 = <$LOGFILE2>;
        }
        $l2++ if( $rcc2 );

        print "--rec1 status $rcc1 rec2 status $rcc2\n" if( $dbug );
        $ok = 1;
        while( $ok )
        {
            $ok = 0 if( !$rcc1 && !$rcc2 ); # no record full

            # record 1 complete, second file already empty
            if( $rcc1 &&  !$rcc2 ) 
            {
                print "--rec1 complete\n" if( $dbug );
                $rcc1 = 0;
                $nerr += PrintQso( 0 );
                EmptyRec( 0 );
  
                # fill new record
                $line = $res . $line if( $line );
                while( $line && !$rcc1 )
                {
                    ($res, $rcc1, $l) = ReadAdifRec( $line, $l, 0 );
                    if( $file1 ne "-" )
                    {
                        $line = <$LOGFILE>;
                    }
                    else 
                    {
                        $line = <>;
                    }
                }
                $l++ if( $rcc1 );
            }

            # record 2 complete, first file already empty
            if( $rcc2 && !$rcc1 ) 
            {
                print "--rec2 complete\n" if( $dbug );
                $rcc2 = 0;
                $nerr += PrintQso( 1 ) if( !$opt_M );
                SearchMatch() if( $opt_M );
                EmptyRec( 1 );

                # fill new record
                $line2 = $res2 . $line2 if( $line2 );
                while( $line2 && !$rcc2 )
                {
                    ($res2, $rcc2, $l2) = ReadAdifRec( $line2, $l2, 1 );
                    $line2 = <$LOGFILE2>;
                }
                $l2++ if( $rcc2 );
            }

            # both records complete, compare and print first in time
            if( $rcc1 && $rcc2 )
            {
                print "--rec1 and rec2 complete\n" if( $dbug );
                # compare and print out
                if( CompareRec() == 1 )
                {
                    # first record earlier
                    $rcc1 = 0;
                    $nerr += PrintQso( 0 );		    
                    EmptyRec( 0 );

                    # fill new record
                    $line = $res . $line if( $line );
                    while( $line && !$rcc1 )
                    {
                        ($res, $rcc1, $l) = ReadAdifRec( $line, $l, 0 );
                        if( $file1 ne "-" )
                        {
                            $line = <$LOGFILE>;
                        }
                        else 
                        {
                            $line = <>;
                        }
                    }
                    $l++ if( $rcc1 );
                }
                else
                {
                    if( CompareRec() != -1 )
                    {
                        # second record earlier
                        $rcc2 = 0;
                        $nerr += PrintQso( 1 ) if( !$opt_M );
                        SearchMatch() if( $opt_M );
                        EmptyRec( 1 );

                        # fill new record
                        $line2 = $res2 . $line2 if( $line2 );
                        while( $line2 && !$rcc2 )
                        {
                            ($res2, $rcc2, $l2) = ReadAdifRec( $line2, $l2, 1 );
                            $line2 = <$LOGFILE2>;
                        }
                        $l2++ if( $rcc2 );
                    }
                    else
                    {
                        # two records are probably same QSO
                        MergeRecs();

                        $rcc1 = 0;
                        $nerr += PrintQso( 0 );		    
                        EmptyRec( 0 );

                        # fill new record1
                        $line = $res . $line if( $line );
                        while( $line && !$rcc1 )
                        {
                            ($res, $rcc1, $l) = ReadAdifRec( $line, $l, 0 );
                            if( $file1 ne "-" )
                            {
                                $line = <$LOGFILE>;
                            }
                            else 
                            {
                                $line = <>;
                            }
                        }

                        $l++ if( $rcc1 );

                        $rcc2 = 0;
                        EmptyRec( 1 );

                        # fill new record
                        $line2 = $res2 . $line2 if( $line2 );
                        while( $line2 && !$rcc2 )
                        {
                            ($res2, $rcc2, $l2) = ReadAdifRec( $line2, $l2, 1 );
                            $line2 = <$LOGFILE2>;
                        }
                        $l2++ if( $rcc2 );
                    }
                }
            }
        }
    }

    print "$nerr errors found\n" if( $nerr > 0 && !$opt_q ); 
    return ( $l + $l2 );
}

# search for a QSO from first log if was not found in merging with -M
sub SearchMatch
{
    my $sqsos;
    my @lqsos;
    my $q;
    my $dt;
    my $x="";

    print "M? ";
    PrintTxtRec($x,1,$x);

    if($opt_v)
    {
	$sqsos=`adifmerg -f $file1 -S CALL=$rec[1]{CALL} -l`;
	@lqsos = split /\n/, $sqsos;
	foreach(@lqsos)
	{
	    $q=$_;
	    $q =~ /([1-2]\d\d\d[0-1]\d[0-3]\d)\s([0-2][0-9][0-5][0-9]).*/;
	    $dt=abs(jday($1,$2)-jday($rec[0]{QSO_DATE},$rec[0]{TIME_ON}));
	    print "C: $q\n" if($dt<=($opt_t/(24*60)));
	}
    }

}

# print QSO data as adif, text or csv and check validity of QSO records
sub PrintQso
{
    my $recno = shift;
    my $serr;
    my $ne = 0;
    my $prout = 0;
    my @awards;
    my $awstatus = "";

    # convert deprecated records
    AdifRecTo2( $recno );

    # convert previous modes to submodes if applicaple
    Modes2Submodes( $recno );

    # add CREDIT_GRANTED field if QSL received "V"
    QslVerified2Credit( $recno );

    # remove "M" (manager) from QSL via
    QslViaRemoveM( $recno );

    # map userdefined fields to standard if available
    Userdef2Standard( $recno );

    # optionally add/delete/fix records
    AddRec( $recno ) if( $opt_A );
    DelRec( $recno ) if( $opt_D );
    FixRec( $recno ) if( $opt_F );
    MapRec( $recno ) if( $opt_R );

    # add location from grid square 
    AddLoc( $recno ) if( $opt_d );
    # calculate distance
    CalcDist( $recno ) if( $opt_d );

    # optionally add band info from frequency
    $rec[ $recno ]{BAND} = Freq2band( $rec[ $recno ]{FREQ} )
       if( $opt_b && !$rec[ $recno ]{BAND} );

    # check QSO data
    if( CheckQso( $recno ) )
    {
        ($ne, $serr ) = CheckQso( $recno );
    }
    else
    {
        $serr = "";
    }
    
    # print out
    $prout = 0;
    $prout = 1 if( !$opt_S );
    if( !$srecu && $opt_S && (exists $rec[$recno]{$srecn}) )
    {
        # exact match 'REC=VAL'
        if( $srecv && $srecv ne "!" )
        {
            if( $srecn ne "GRIDSQUARE" )
            {
                $prout = 1 if( $rec[ $recno ]{$srecn} eq $srecv && $srecv );
            }
            else
            {
                $prout = 1 if(((substr $rec[$recno]{$srecn},0,$gridlen) eq $srecv)&&($srecv));
            }
        }
        else
        {
            # any empty/zero value 'REC=!'
            if( $srecv eq "!" )
            {
                $prout = 1 if( !$rec[ $recno ]{$srecn} );
            }
            else
            {
                # any value 'REC='
                $prout = 1 if( $rec[ $recno ]{$srecn} );
            }
        } 
    }

    if( $srecu && $srecv && $opt_S )
    {
        $prout = 1 if( $rec[$recno]{$srecn} ge $srecv && $rec[$recno]{$srecn} le $srecu );
    }

    $awstatus = "";
    if( $opt_a )
    {
        if( $rec[$recno]{CREDIT_GRANTED} || $rec[$recno]{CREDIT_SUBMITTED} )
        {
            @awards = split /,/, $rec[ $recno ]{CREDIT_SUBMITTED}; 
            foreach( @awards )
            {
                $awstatus = "S" if( $opt_a eq $_ );
            }

            @awards = split /,/, $rec[ $recno ]{CREDIT_GRANTED}; 
            foreach( @awards )
            {
                $awstatus = $awstatus . "G" if( $opt_a eq $_ );
            }
        }
        $prout = 0 if( $prout && !$awstatus );
    }

    if( !$opt_N )
    {
        $prout = 0 if( $rec[$recno]{QSO_COMPLETE} =~ /N/i && $srecn ne "QSO_COMPLETE" );
    }

    if( $prout )
    {
        if(((uc $opt_Q) eq 'A')&&(((uc $rec[$recno]{QSL_SENT}) eq 'N')||((uc $rec[$recno]{QSL_SENT}) eq 'R')))
        {
            if( $opt_T )
            {
                Qso2HashT();
            }
            else
            {
                Qso2Hash();
            }
        }
        if(((uc $opt_Q) eq 'R')&&((uc $rec[$recno]{QSL_SENT}) eq 'R'))
        {
            if( $opt_T )
            {
                Qso2HashT();
            }
            else
            {
                Qso2Hash();
            }
        }
        PrintAdifRec( $recno ) if( $opt_o );
        PrintTxtRec( $serr, $recno, $awstatus ) if( $opt_l );
        PrintTxtRec( $serr, $recno, $awstatus ) if( $opt_e && $serr );
        PrintTxtRecL( $serr, $recno, " " ) if( $opt_L );
        PrintCabRec( $recno ) if( $opt_C );
        PrintTxtRecL( $serr, $recno, $csv_out_separator ) if( $opt_x );
        PrintCsvRec( $recno, $csv_out_separator ) if( $opt_X );
        PrintTempRec( $recno ) if( $opt_T && !$opt_Q );
        Qso2Array() if( $opt_s );
        Qso2Hash2s() if( $opt_2 );
	
	$sqsos++ if( $opt_o || $opt_l || ( $opt_e && $serr ) || $opt_L || $opt_C || $opt_x || $opt_s || $opt_X);
        StatQso($recno);
    }

    return $ne;
}

# calculate QSO statistics
sub StatQso
{
    my $recno = shift;
    my $lonlat = "";
    my $key = "";

    # check the first and last times and dates
    if($rec[$recno]{QSO_DATE})
    {
	if($rec[$recno]{QSO_DATE} lt $fdate)
	{
	    $fdate=$rec[$recno]{QSO_DATE};
	    $ftime=$rec[$recno]{TIME_ON};
	    $ftime=$ftime."00" if(length($ftime)==4);
	}
	if($rec[$recno]{QSO_DATE} eq $fdate)
	{
	    if($rec[$recno]{TIME_ON} lt $ftime)
	    {
		$ftime=$rec[$recno]{TIME_ON};
		$ftime=$ftime."00" if(length($ftime)==4);
	    }
	}
	if($rec[$recno]{QSO_DATE} gt $ldate)
	{
	    $ldate=$rec[$recno]{QSO_DATE};
	    $ltime=$rec[$recno]{TIME_ON};
	    $ltime=$ltime."00" if(length($ltime)==4);
	}
	else
	{
	    if($rec[$recno]{QSO_DATE} eq $ldate)
	    {
		if($rec[$recno]{TIME_ON} gt $ltime)
		{
		    $ltime=$rec[$recno]{TIME_ON};
		    $ltime=$ltime."00" if(length($ltime)==4);
		}
	    }
	}
    }
    
    # count bands, modes and QSLs
    if(exists $bndstat{uc $rec[$recno]{BAND}})
    {
	$bndstat{uc $rec[$recno]{BAND}}++;
    }
    else
    {
	$bndstat{uc $rec[$recno]{BAND}}=1;
    }
    if(exists $modstat{uc $rec[$recno]{MODE}})
    {
	$modstat{uc $rec[$recno]{MODE}}++;
    }
    else
    {
	$modstat{uc $rec[$recno]{MODE}}=1;
    }
    if( $rec[$recno]{SUBMODE} )
    {
        if(exists $submodstat{uc $rec[$recno]{SUBMODE}})
        {
	    $submodstat{uc $rec[$recno]{SUBMODE}}++;
        }
        else
        {
	    $submodstat{uc $rec[$recno]{SUBMODE}}=1;
        }
    }
    if(exists $propstat{uc $rec[$recno]{PROP_MODE}})
    {
	$propstat{uc $rec[$recno]{PROP_MODE}}++;
    }
    else
    {
	$propstat{uc $rec[$recno]{PROP_MODE}}=1 if($rec[$recno]{PROP_MODE});
    }
    $nqsls++ if($rec[$recno]{QSL_SENT} eq "Y");
    $nqslr++ if($rec[$recno]{QSL_RCVD} eq "Y");
    $neqsls++ if($rec[$recno]{EQSL_QSL_SENT} eq "Y");
    $neqslr++ if($rec[$recno]{EQSL_QSL_RCVD} eq "Y");
    $nlqsls++ if($rec[$recno]{LOTW_QSL_SENT} eq "Y");
    $nlqslr++ if($rec[$recno]{LOTW_QSL_RCVD} eq "Y");

    if( $rec[ $recno ]{ DXCC } )
    {
        $dxstat{ $rec[ $recno ]{ DXCC } } = 1 
            if( !exists $dxstat{ $rec[ $recno ]{ DXCC } } );
        $dxstatc{ $rec[ $recno ]{ DXCC } } = 1 
            if( ( !exists $dxstatc{ $rec[ $recno ]{ DXCC }} ) && ( ( $rec[ $recno ]{ QSL_RCVD } eq "Y") || ( $rec[ $recno ]{ LOTW_QSL_RCVD } eq "Y" ) || ( $rec[ $recno ]{ EQSL_QSL_RCVD } eq "Y" ) ) );

	$key = "";
    	$key = $rec[ $recno ]{ CNTY } . ( sprintf "%4d", scalar( $rec[ $recno ]{ DXCC } ) ) if( $rec[ $recno ]{ CNTY } );
        if( $key )
	{
             $cntystat{ $key } = $rec[ $recno ]{ DXCC } if( !exists $cntystat{ $key } );
        }

	$key = "";
    	$key = $rec[ $recno ]{ STATE } . ( sprintf "%4d", scalar( $rec[ $recno ]{ DXCC } ) ) if( $rec[ $recno ]{ STATE } );
        if( $key )
	{
            $statestat{ $key } = $rec[ $recno ]{ DXCC } if( !exists $statestat{ $key } );
        }
    }

    if($rec[$recno]{CQZ})
    {
	$cqzstat{$rec[$recno]{CQZ}}=1 
	    if(!exists $cqzstat{$rec[$recno]{CQZ}});
    }
    if($rec[$recno]{ITUZ})
    {
	$ituzstat{$rec[$recno]{ITUZ}}=1 
	    if(!exists $ituzstat{$rec[$recno]{ITUZ}});
    }
    if(exists $ctrystat{uc $rec[$recno]{COUNTRY}})
    {
	$ctrystat{uc $rec[$recno]{COUNTRY}}++;
    }
    else
    {
	$ctrystat{uc $rec[$recno]{COUNTRY}}=1;
    }
    if($rec[$recno]{CONT})
    {
	$contstat{$rec[$recno]{CONT}}=1 
	    if(!exists $contstat{$rec[$recno]{CONT}});
    }
    if($rec[$recno]{IOTA})
    {
	$iotastat{$rec[$recno]{IOTA}}=1 
	    if(!exists $iotastat{$rec[$recno]{IOTA}});
    }
    if( $rec[ $recno ]{ POTA_REF } )
    {
	$potastat{ $rec[ $recno ]{ POTA_REF } } = 1 
            if( !exists $potastat{ $rec[ $recno ]{ POTA_REF } } );
    }
    if( $rec[ $recno ]{ WWFF_REF } )
    {
	$wwffstat{ $rec[ $recno ]{ WWFF_REF } } = 1 
            if( !exists $wwffstat{ $rec[ $recno ]{ WWFF_REF } } );
    }
    if($rec[$recno]{STATION_CALLSIGN})
    {
	$stationstat{uc $rec[$recno]{STATION_CALLSIGN}}=1 
	    if(!exists $stationstat{uc $rec[$recno]{STATION_CALLSIGN}});
    }
    if(exists $rec[$recno]{SOTA_REF})
    {
	if($rec[$recno]{SOTA_REF})
	{
	    $sotastat{$rec[$recno]{SOTA_REF}}=1 
		if(!exists $sotastat{$rec[$recno]{SOTA_REF}});
	}
    }
    if($rec[$recno]{GRIDSQUARE})
    {
	if(length($rec[$recno]{GRIDSQUARE})>=$gridlen)
	{
	    $gridstat{(substr $rec[$recno]{GRIDSQUARE},0,$gridlen)}=1
		if(!exists $gridstat{(substr $rec[$recno]{GRIDSQUARE},0,$gridlen)});
	    
	}
    }
    if(($rec[$recno]{LON})&&($rec[$recno]{LAT}))
    {
	$lonlat=$rec[$recno]{LON}." ".$rec[$recno]{LAT};
	$lonlatstat{$lonlat}=1 if(!exists $lonlatstat{$lonlat});
    }
    if($rec[$recno]{QSLRDATE})
    {
	$lstqsl=($rec[$recno]{QSLRDATE}) 
	    if($lstqsl lt ($rec[$recno]{QSLRDATE}));
    }
    if($rec[$recno]{EQSL_QSLRDATE})
    {
	$lsteqsl=($rec[$recno]{EQSL_QSLRDATE}) 
	    if($lsteqsl lt ($rec[$recno]{EQSL_QSLRDATE}));
    }
    if($rec[$recno]{LOTW_QSLRDATE})
    {
	$lstlqsl=($rec[$recno]{LOTW_QSLRDATE}) 
	    if($lstlqsl lt ($rec[$recno]{LOTW_QSLRDATE}));
    }
}



# parse adif header and fill header hash, return rest of line
sub AdifHead
{
    my $line = shift;
    my $parm;
    my $dta;
    my $pos;
    my $len;
    my $c;

    # while loop to discard characters before '<'
    $c = substr $line, 0, 1;
    while( ($c ne "<") && (length($line)>0) )
    {
        $line = substr $line, 1, (length($line)-1);
        $c=substr $line, 0, 1;
    }

    return "" if( length($line) == 0 );

#    print "--A hed\{ADIF_VER\} = $hed{ADIF_VER}\n";

    while( $line =~ /^<(\w+):(\d+)(:[ABNSDTML])?>.*/i )
    {
        $parm = uc $1;
        $len = $2;
        $pos = length($parm) + length($len) + 3;
        $pos += 2 if($3);
        $dta = substr $line, $pos, $len;
        $line = substr $line, ($pos+$len);
        
        if( exists $hed{$parm} )
        {
             $hed{$parm} = $dta;
#             print "--B hed\{$parm\} = $hed{$parm}\n";
        }
        else 
	{
	    if( $parm =~ /^USERDEF\d{1,2}/i )
	    {
		if(!$opt_u)
		{
		    $hed{$parm} = $dta;
		
		    # now $dta can be for example 'ShoeSize,{5:20}' or 
		    # 'SweaterSize,{S,M,L}'
		    # 1. add the new field to rec hash with default 'S' type
		    # if the type is not given
		    # 2. set the value to "" or 0 is number
		    if( $dta =~ /^(\w+)(\,\{\w+\})?/ )
		    {
			$rec[0]{$1} = "";
			$rec[1]{$1} = "";
			$typ{$1} = "S";
		    }
		}
	    }
	    else
	    {
		if( $parm !~ /^APP_\w+_\w+/i )
		{
		    print "Unknown parameter $parm = $dta\n";
		}
	    }
	}

        $c = substr $line, 0, 1;
        while( ($c ne "<") && (length($line)>0) )
        {
            $line = substr $line, 1, (length($line)-1);
            $c = substr $line, 0, 1;
        }
    }

    return $line;

}

# read adif record and fill hash
sub ReadAdifRec
{
    my $line=shift;
    my $l=shift;
    my $recno=shift;
    my $recc=0;
    my $res="";

    print "--[$recno] QSO $l: $line" if($dbug);

    $res=AdifLine($line,$recno);
    if($res =~ /^<eor>/i)
    {
	if($dbug)
	{
	    print "--record complete\n";
	    print "call $rec[$recno]{CALL}\n";
	    print "time $rec[$recno]{TIME_ON}\n";
	    print "date $rec[$recno]{QSO_DATE}\n";
	    print "rst $rec[$recno]{RST_SENT}\n";
	    print "mode $rec[$recno]{MODE}\n";
	    print "band $rec[$recno]{BAND}\n";
	    print "frequency $rec[$recno]{FREQ}\n";
	}
	
	$res="";
	$recc=1; # record completed
    }

    return ($res,$recc,$l);
}


# convert deprecated adif 1 records to version 2 
# GUEST_OP -> OPERATOR
# VE_PROV -> STATE
sub AdifRecTo2
{
    my $recno=shift;

    if($rec[$recno]{GUEST_OP})
    {
	$rec[$recno]{OPERATOR}=$rec[$recno]{GUEST_OP};
	$rec[$recno]{GUEST_OP}="";
    }

    if($rec[$recno]{VE_PROV})
    {
	$rec[$recno]{STATE}=$rec[$recno]{VE_PROV};
	$rec[$recno]{VE_PROV}="";
    }

}

# convert modes to modes and submodes if this can be done
# without overwriting data in SUBMODE field 
sub Modes2Submodes 
{
    my $recno=shift;

    if( exists $submodes{$rec[$recno]{MODE}} && !$rec[$recno]{SUBMODE} )
    {
	$rec[$recno]{SUBMODE}=$rec[$recno]{MODE};
	$rec[$recno]{MODE}=$submodes{$rec[$recno]{SUBMODE}};
    }
}

# add CREDIT_GRANTED field if QSL received "V" and change flag to "Y"
sub QslVerified2Credit
{
    my $recno = shift;

    if( $rec[$recno]{EQSL_QSL_RCVD} )
    {
        if( $rec[$recno]{EQSL_QSL_RCVD} =~ /V/i )
        { 
            $rec[$recno]{EQSL_QSL_RCVD} = "Y";
            if( $rec[$recno]{CREDIT_GRANTED} )
            {
                $rec[$recno]{CREDIT_GRANTED} =  $rec[$recno]{CREDIT_GRANTED} . ",AWARD:eqsl"; 
            }
            else
            {
                $rec[$recno]{CREDIT_GRANTED} =  "AWARD:eqsl"; 
            }
        }
    }

    if( $rec[$recno]{LOTW_QSL_RCVD} )
    {
        if( $rec[$recno]{LOTW_QSL_RCVD} =~ /V/i )
        { 
            $rec[$recno]{LOTW_QSL_RCVD} = "Y";
            if( $rec[$recno]{CREDIT_GRANTED} )
            {
                $rec[$recno]{CREDIT_GRANTED} =  $rec[$recno]{CREDIT_GRANTED} . ",AWARD:lotw"; 
            }
            else
            {
                $rec[$recno]{CREDIT_GRANTED} =  "AWARD:lotw"; 
            }
        }
    }

    if( $rec[$recno]{QSL_RCVD} )
    {
        if( $rec[$recno]{QSL_RCVD} =~ /V/i )
        { 
            $rec[$recno]{QSL_RCVD} = "Y";
            if( $rec[$recno]{CREDIT_GRANTED} )
            {
                $rec[$recno]{CREDIT_GRANTED} =  $rec[$recno]{CREDIT_GRANTED} . ",AWARD:card"; 
            }
            else
            {
                $rec[$recno]{CREDIT_GRANTED} =  "AWARD:card"; 
            }
        }
    }

}

# remove "M" (manager) from QSL via if applicable
sub QslViaRemoveM
{
    my $recno = shift;

    if( $rec[$recno]{QSL_RCVD_VIA} )
    {
        $rec[$recno]{QSL_RCVD_VIA} = "" if( $rec[$recno]{QSL_RCVD_VIA} =~ /M/i );
    }

    if( $rec[$recno]{QSL_SENT_VIA} )
    {
        $rec[$recno]{QSL_SENT_VIA} = "" if( $rec[$recno]{QSL_SENT_VIA} =~ /M/i );
    }

}

# map user defined field to new ADIF standard field if available
# do not overwrite data in existing fields
sub Userdef2Standard
{
    my $recno = shift;

    if( $rec[$recno]{SOTA} && !$rec[$recno]{SOTA_REF} )
    {
        $rec[$recno]{SOTA_REF} = $rec[$recno]{SOTA};
        delete $rec[$recno]{SOTA};
    }

    if( $rec[$recno]{MY_SOTA} && !$rec[$recno]{MY_SOTA_REF} )
    {
        $rec[$recno]{MY_SOTA_REF} = $rec[$recno]{MY_SOTA};
        delete $rec[$recno]{MY_SOTA};
    }
}


# print beginning of Cabrillo file, the values are read from template file
# $opt_C
sub PrintCabHead
{
    my $CABHEAD;
    my $line;
    my $callsign="";

    if(open($CABHEAD,"<",$opt_C))
    {
	print "START-OF-LOG: 3.0\n";
        $line=<$CABHEAD>;
	while($line)
        {
	    print $line if($line =~ /^\w{4,9}(-\w{2,11})?:\s+.*/);
	    if($line =~ /^CALLSIGN:\s(\w{3,13})/i)
	    {
		$callsign=$1;
	    }

#	    print $line if($line =~ /^\w{4,20}:\s+\w+/);
	    $line=<$CABHEAD>;
	}
    }

    return $callsign;
}

# print Cabrillo QSO
sub PrintCabRec
{
    my $recno=shift;
    my $oline="QSO: ";
    my $pnts=0;
    my $calc="";

    if($rec[$recno]{FREQ})
    {
	if(($rec[$recno]{FREQ}>=1.8)&&($rec[$recno]{FREQ}<=29.7))
	{
	    $oline=$oline.sprintf "%5d ", (int 1000*$rec[$recno]{FREQ});
	}
	else
	{
	    $oline=$oline.f2cabf($rec[$recno]{FREQ})." ";
	}
    }
    else
    {
	$oline=$oline.b2cabf(uc $rec[$recno]{BAND})." ";
    }

    $oline=$oline.m2cab($rec[$recno]{MODE})." ";
    $oline=$oline.(substr $rec[$recno]{QSO_DATE},0,4)."-";
    $oline=$oline.(substr $rec[$recno]{QSO_DATE},4,2)."-";
    $oline=$oline.(substr $rec[$recno]{QSO_DATE},6,2)." ";
    $oline=$oline.(substr $rec[$recno]{TIME_ON},0,4)." ";

    # use OPERATOR if STATION_CALLSIGN empty or CALLSIGN: from template
    if($rec[$recno]{STATION_CALLSIGN})
    {
	$oline=$oline.(sprintf "%-13.13s ",$rec[$recno]{STATION_CALLSIGN});
    }
    else
    {
	if($rec[$recno]{OPERATOR})
	{
	    $oline=$oline.(sprintf "%-13.13s ",$rec[$recno]{OPERATOR});
	}
	else
	{
	    $oline=$oline.(sprintf "%-13.13s ",$cabcall);
	}
    }
    $oline=$oline.$rec[$recno]{STX_STRING}." ";
    $oline=$oline.(sprintf "%-13.13s ",$rec[$recno]{CALL});
    $oline=$oline.$rec[$recno]{SRX_STRING};

    # add calculated points
    if($opt_p)
    {
	$calc=$opt_p." ".$rec[$recno]{CALL}." ".$rec[$recno]{BAND};
	$calc=$calc." ".$rec[$recno]{STX_STRING}." ".$rec[$recno]{SRX_STRING};
	$pnts=`$calc`;
	$oline=$oline.(sprintf " %-5.2f", $pnts);
	$tpnts+=$pnts;
    }

    print "$oline\n";
}

# frequency to Cabrillo MHz for > 29.7 MHz
sub f2cabf()
{
    my $freq=shift;
    my @frqs=("   50","  144","  222","  432","  902"," 1.2G"," 2.3G"," 3.4G"," 5.7G","  10G","  24G","  47G","  75G"," 119G"," 142G"," 241G");
    my @bandl=(50,144,222,420,902,1240,2300,3300,5650,10000,24000,47000,75500,119980,142000,241000);
    my @bandu=(54,148,225,450,928,1300,2450,3500,5925,10500,24250,47200,81000,120020,149000,250000);

    my $frq="";
    my $i;

    for($i=0;$i<16;$i++)
    {
	$frq=$frqs[$i] if(($freq>=$bandl[$i])&&($freq<=$bandu[$i]));
    }
    
    return $frq;

}

# ADIF band to Cabrillo frequency
sub b2cabf()
{
    my $bnd=shift;

    my %bands=("160M" => " 1800",
	       "80M"  => " 3500",
	       "40M"  => " 7000",
	       "20M"  => "14000",
	       "15M"  => "21000",
	       "10M"  => "28000",
	       "2M"   => "  144",
	       "70CM" => "  432",
	       "33CM" => "  920",
	       "23CM" => " 1.2G",
	       "13CM" => " 2.3G",
	       "9CM"  => " 3.4G",
	       "6CM"  => " 5.7G",
	       "3CM"  => "  10G",
	       "1.25CM" => "  24G",
	       "6MM"  => "  47G",
	       "4MM"  => "  75G",
	       "2.5MM" => " 119G",
	       "2MM"  => " 142G",
	       "1MM"  => " 300G");

    return $bands{$bnd};
    
}

# ADIF to Cabrillo modes: CW, PH, FM or RY
sub m2cab()
{
    my $m=shift;
    my $modc="";

    if(($m eq "CW")||($m eq "FM"))
    {
	$modc=$m;
    }
    else
    {
	if(($m eq "AM")||($m eq "SSB"))
	{
	    $modc="PH";
	}
	else
	{
	    $modc="RY";
	}
    }
    return $modc;
} 

# read Cabrillo log
sub ReadCabLog
{
    my $file=shift;
    my $line;
    my $LOGFILE;
    my @d;
    my $contest="";
    my $cpos;
    my $nex;
    my $nqso=0;
    my $nerr;
    my $i;
    my $txexc;
    my $rxexc;

    if(open($LOGFILE,"<",$file)||($file eq "-cbr"))
    {
	if($file ne "-cbr")
	{
	    $line=<$LOGFILE>;
	}
	else
	{
	    $line=<>;
	}

        while($line)
        {
	    print $line if($opt_v);
	    EmptyRec(0);
	    @d=split /\s+/, $line;

	    if($d[0]=~/^CONTEST:/)
	    {
		$contest=$d[1] if($d[1]);
		for($i=2;$i<@d;$i++)
		{
		    $contest=$contest." ".$d[$i];
		}
	    }
	    elsif($d[0]=~/^QSO:/)
	    {
		$rec[0]{CONTEST_ID}=$contest if($contest);
		$rec[0]{FREQ}=$d[1]/1000;
		$rec[0]{BAND}=Freq2band($d[1]/1000);
		$rec[0]{MODE}=cab2m($d[2]);
		$d[3]=~s/\-//g;
		$rec[0]{QSO_DATE}=$d[3];
		$rec[0]{TIME_ON}=$d[4];
		$rec[0]{STATION_CALLSIGN}=$d[5];

		# probable position of contacted call sign
		$cpos=int((@d-5)/2)+5;
		# number of exchange fields
		$nex=$cpos-6;
		$rec[0]{CALL}=$d[$cpos];
	
		# check if RST number in exchange, usually next after CALL
		if(CheckRst($d[6]))
		{
		    if(CheckRst($d[$cpos+1]))
		    {
			$rec[0]{RST_SENT}=$d[6];
			$rec[0]{RST_RCVD}=$d[$cpos+1];
			# in this case the next could be serial number
			if($nex>1)
			{
			    if($d[7]=~/\d{3,4}/)
			    {
				if($d[$cpos+2]=~/\d{3,4}/)
				{
				    $rec[0]{STX}=$d[7];
				    $rec[0]{SRX}=$d[$cpos+2];
				}
			    }
			}
		    }
		}
		# check if grid square
		elsif(CheckGrid($d[6]))
		{
		    if(CheckGrid($d[$cpos+1]))
		    {
			$rec[0]{MY_GRIDSQUARE}=$d[6];
			$rec[0]{GRIDSQUARE}=$d[$cpos+1];
		    }
		}
		# check if serial number
		elsif($d[6]=~/^\d{3,4}$/)
		{
		    if($d[$cpos+1]=~/^\d{3,4}$/)
		    {
			$rec[0]{STX}=$d[6];
			$rec[0]{SRX}=$d[$cpos+1];
		    }
		}
	
		# make transmitted string
		$txexc="";
		$txexc=$d[6] if($nex>0);
		for($i=7;$i<$nex+6;$i++)
		{
		    $txexc=$txexc." ".$d[$i];
		}
		$rec[0]{STX_STRING}=$txexc;

		# make received string
		$rxexc="";
		$rxexc=$d[$cpos+1] if($nex>0);
		for($i=$cpos+2;$i<$cpos+$nex+1;$i++)
		{
		    $rxexc=$rxexc." ".$d[$i];
		}
		$rec[0]{SRX_STRING}=$rxexc;

		$nqso++;

		# print qso with error message
		if($opt_l||$opt_o||$opt_L||$opt_x||$opt_X)
		{
		    $nerr+=PrintQso(0);
		}
	    }

	    if($file ne "-cbr")
	    {
		$line=<$LOGFILE>;
	    }
	    else
	    {
		$line=<>;
	    }
	}
    }
    return $nqso;
}

# Cabrillo modes: CW, PH, FM or RY to ADIF
sub cab2m()
{
    my $m=shift;
    my $moda="unknown";

    if(($m eq "CW")||($m eq "FM"))
    {
	$moda=$m;
    }
    elsif($m eq "PH")
    {
	$moda="SSB";
    }
    elsif($m eq "RY")
    {
	$moda="RTTY";
    }

    return $moda;
} 


# compare record 1 and 2 to see which is first in time
sub CompareRec
{
    my $erly=0; # 1=rec earlier that rec2, -1=probably same QSO w/ utc+-3min
    my $smcall=0; # 1=call signs look similar
    my $t1;
    my $t2;
    my $dt;
    
    $t1=$rec[0]{TIME_ON};
    $t1=$t1."00" if(length($t1)==4);
    $t2=$rec[1]{TIME_ON};
    $t2=$t2."00" if(length($t2)==4);
    $erly=1 if($rec[0]{QSO_DATE}<$rec[1]{QSO_DATE});
    
    if((uc $rec[0]{CALL}) eq (uc $rec[1]{CALL}))
    {
	$smcall=1;
    }
    else
    {
	if(length($rec[0]{CALL})>length($rec[1]{CALL}))
	{
	    $smcall=1 if($rec[0]{CALL} =~ m!$rec[1]{CALL}!);
	}
	if(length($rec[0]{CALL})<length($rec[1]{CALL}))
	{
	    $smcall=1 if($rec[1]{CALL} =~ m!$rec[0]{CALL}!);
	}
    }

    if(!$smcall)
    {
	$erly=1 if(($rec[0]{QSO_DATE}==$rec[1]{QSO_DATE})&&($t1<$t2));
    }
    else
    {
	# check here if it could be same QSO with time +-$opt_t min
	$dt=abs(jday($rec[0]{QSO_DATE},$rec[0]{TIME_ON})-
		jday($rec[1]{QSO_DATE},$rec[1]{TIME_ON}));

	if($dt<=($opt_t/(24*60)))
	{
	    if((!$rec[0]{BAND})||(!$rec[1]{BAND}))
	    {
		$erly=-1; 
	    }
	    else
	    {
		$erly=-1 if((uc $rec[0]{BAND}) eq (uc $rec[1]{BAND}));
	    }
	}
    }

    return $erly;
}


# merge two records for same QSO
# add records from $rec2 but do not overwrite records in $rec, exception
# when -M switch is used for QSL flags, also CREDIT_SUBMITTED and
# CREDIT_GRANTED are handeled as award lists
sub MergeRecs
{
    my $recn;
    my $wloss="";
    my @awards;
    my $equal;

    foreach $recn (keys %typ)
    {
	if( !$rec[0]{$recn} && $rec[1]{$recn} )
	{
	    $rec[0]{$recn} = $rec[1]{$recn};
	    $wloss = $wloss . $recn . ":+" . ($rec[0]{$recn}) . " " if( $opt_v );
	}
	else
	{
            $equal = 0;
            if( $rec[ 0 ]{ $recn } && $rec[ 1 ]{ $recn } )
            {
                $equal = 1 if( (uc $rec[ 0 ]{ $recn } ) eq (uc $rec[ 1 ]{ $recn } ) );
            }
            if( $recn ne 'TIME_ON' && $recn ne 'QSO_DATE' && !$equal && $rec[1]{$recn} )
	    {
		if( !( $opt_M && MRules($recn,$rec[0]{$recn},$rec[1]{$recn}) ) )
		{
		    $wloss = $wloss . $recn . ":+" . $rec[0]{$recn} . ",-" . $rec[1]{$recn} . " ";
		    $dloss++;
		}
	    } 
	}

	if( $opt_M && MRules($recn,$rec[0]{$recn},$rec[1]{$recn}) )
	{
	    $wloss = $wloss . $recn . ":" . $rec[0]{$recn} ."->". $rec[1]{$recn} ." ";
	    $rec[0]{$recn} = $rec[1]{$recn};
	    $dloss++;
	} 

	if( $recn eq "CREDIT_SUBMITTED" || $recn eq "CREDIT_GRANTED" )
	{
	    @awards = split /,/, $rec[ 1 ]{ $recn }; 
	    foreach( @awards )
	    {
		$rec[ 0 ]{ $recn } = $rec[ 0 ]{ $recn } . "," . $_ 
		    if( $_ && ($rec[0]{$recn} !~ /$_/i) );
	    }
	}

    }

    print "L $rec[ 0 ]{ QSO_DATE } $rec[ 0 ]{ TIME_ON } $rec[ 0 ]{ CALL } $wloss\n" 
	if( $opt_v && $wloss );
}

# rules for merging QSO records with '-M'
# QSL_RCVD allowed changes
# 1) N->{Y,R,I,V}
# 2) R->{Y,V}
# 3) I->{Y}
# 4) Y->{V}
# QSL_SENT allowed changes
# 1) N->{Y,R,Q,I}
# 2) R->{Q,Y}
# 3) Q->{Y}
sub MRules
{
    my $recn=shift;
    my $ra=uc shift;
    my $rb=uc shift;
    my $ok=0;

    if(($recn eq 'QSL_RCVD')||($recn eq 'EQSL_QSL_RCVD')||($recn eq 'LOTW_QSL_RCVD'))
    {
	$ok=1 if($ra eq 'N');
	$ok=1 if(($ra eq 'R')&&(($rb eq 'Y')||($rb eq 'V')));
	$ok=1 if(($ra eq 'I')&&($rb eq 'Y'));
	$ok=1 if(($ra eq 'Y')&&($rb eq 'V'));
    }

    if(($recn eq 'QSL_SENT')||($recn eq 'EQSL_QSL_SENT')||($recn eq 'LOTW_QSL_SENT'))
    {
	$ok=1 if($ra eq 'N');
	$ok=1 if(($ra eq 'R')&&(($rb eq 'Q')||($rb eq 'Y')));
	$ok=1 if(($ra eq 'Q')&&($rb eq 'Y'));
    }

    $ok=1 if(($recn eq 'QSL_RCVD_VIA')&&($ra ne 'B')&&($ra ne 'D'));
    $ok=1 if(($recn eq 'QSL_SENT_VIA')&&($ra ne 'B')&&($ra ne 'D'));
    $ok=1 if($recn eq 'QSLRDATE');
    $ok=1 if($recn eq 'QSLSDATE');
    $ok=1 if($recn eq 'EQSL_QSLRDATE');
    $ok=1 if($recn eq 'EQSL_QSLSDATE');

    $ok=0 if((!$ra)||(!$rb)||($ra eq $rb));

    return $ok;
}

# reset record to empty/zero values
# data types: D date, T time, M multiline, S character/string, N number,
# L location, B boolean, E enumeration, A award, I international string,
# G international characters and line breaks
sub EmptyRec
{
    my $recno=shift; 
    my $recn; # record name

    foreach $recn (keys %typ)
    {
	$rec[$recno]{$recn}="" if($typ{$recn} eq "D");
	$rec[$recno]{$recn}="" if($typ{$recn} eq "T");
	$rec[$recno]{$recn}="" if($typ{$recn} eq "M");
	$rec[$recno]{$recn}="" if($typ{$recn} eq "S");
	$rec[$recno]{$recn}="" if($typ{$recn} eq "I");
	$rec[$recno]{$recn}="" if($typ{$recn} eq "G");
	$rec[$recno]{$recn}=0 if($typ{$recn} eq "N");
	$rec[$recno]{$recn}="" if($typ{$recn} eq "L");
	$rec[$recno]{$recn}="" if($typ{$recn} eq "B");
	$rec[$recno]{$recn}="" if($typ{$recn} eq "E");
	$rec[$recno]{$recn}="" if($typ{$recn} eq "A");
    }

    print "--[$recno] cleaned\n" if($dbug);
}

# test record is valid
sub TestRecValid
{
    my $recn = shift;
    my $recv = shift;
    my $valid = 0;

    $recn = uc $recn if( $recn );

    if( $recn eq "RST_RCVD" || $recn eq "RST_SENT")
    {
        $valid = 1 if( $recv || $recv eq "0" );
    }
    else
    {
        $valid = 1 if( $recv );
    }

    return $valid;
}

# print adif record
sub PrintAdifRec
{
    my $recno = shift;
    my $oline = "";
    my $recn; # record name

    $oline = "";
    foreach $recn ( sort keys %typ )
    {
        $oline = $oline . "<" . $recn . ":" . length( $rec[ $recno ]{ $recn } ) . ">" . ( $rec[ $recno ]{ $recn } ) if( TestRecValid( $recn, $rec[ $recno ]{ $recn } ) );
    }
    $oline=$oline."<eor>\n";

    print $oline;

}

# print adx record
sub PrintAdxRec
{
    my $recno=shift;
    my $oline="";
    my $recn; # record name

    print "<RECORD>";
    $oline="";
    foreach $recn (sort keys %typ)
    {
	$oline=$oline."<".$recn.">".$rec[$recno]{$recn}."</".$recn.">" if($rec[$recno]{$recn});
    }

    print $oline;
    print "</RECORD>\n";
}


# fill array of adif lines for sorting, date, time and call in beginning 
# of each line
sub Qso2Array
{
    my $oline = "";
    my $recn; # record name

    $oline = $rec[0]{QSO_DATE} . $rec[0]{TIME_ON};
    $oline = $oline . "00" if( length( $rec[ 0 ]{ TIME_ON } ) == 4 );
    $oline = $oline . $rec[ 0 ]{ CALL } if( $rec[ 0 ]{ CALL } );

    foreach $recn (sort keys %typ)
    {
        $oline = $oline . "<" . $recn . ":" . length( $rec[ 0 ]{ $recn } ) . ">" . $rec[ 0 ]{ $recn } if( TestRecValid( $recn, $rec[ 0 ]{ $recn } ) );
    }
    $oline = $oline . "<eor>\n";

    $qsos[ $qi ] = $oline;
    $qi++;
}

# fill hash for duplicate QSOs, warn if duplicates found
# the hash key is made from call sign, band and mode and it is
# pointing to date and time values
sub Qso2Hash2s
{
    my $dt=0;
    my $c=uc $rec[0]{CALL};
    $c=$c." ".(uc $rec[0]{BAND});
    $c=$c." ".(uc $rec[0]{MODE});

    if(exists $qso2d{$c})
    {
	# check if time is close to QSO in hash
	$dt=abs(jday($qso2d{$c},$qso2t{$c})-jday($rec[0]{QSO_DATE},$rec[0]{TIME_ON}));
	if($dt<=($opt_t/(24*60)))
	{
	    print "1: ";
	    print $qso2d{$c};
	    print " ";
	    print $qso2t{$c};
	    print " $c\n";
	    print "2: ";
	    print $rec[0]{QSO_DATE};
	    print " ";
	    print $rec[0]{TIME_ON};
	    print " ";
	    print $rec[0]{CALL};
	    print " ";
	    print $rec[0]{BAND};
	    print " ";
	    print $rec[0]{MODE};
	    print "\n";
	}
    }
    else
    {
	$qso2d{$c}=$rec[0]{QSO_DATE};
	$qso2t{$c}=$rec[0]{TIME_ON};
    }

}


# fill hash for QSL labels
sub Qso2Hash
{
    my $l="";
    my $c;
    my $rn;
    my $a="";

    $c=$rec[0]{STATION_CALLSIGN};
    $c=$rec[0]{OWNER_CALLSIGN} if($rec[0]{OWNER_CALLSIGN});
 
    $rn=$c." ".$rec[0]{CALL};
    $rn=$rn." ".(uc $rec[0]{MY_GRIDSQUARE}) if($rec[0]{MY_GRIDSQUARE});
    if($rec[0]{CALL})
    {
	if(!exists $qsl{$rn})
	{
	    $l=$l."TO: ".($rec[0]{CALL})."  VIA ";
	    $l=$l.$rec[0]{QSL_VIA} if($rec[0]{QSL_VIA});
	    $l=$l."\n";
	    $l=$l."FROM: ".$c;
	    $l=$l." CQ ".$rec[0]{MY_CQ_ZONE} if($rec[0]{MY_CQ_ZONE});
	    $l=$l." ITU ".$rec[0]{MY_ITU_ZONE} if($rec[0]{MY_ITU_ZONE});
	    $l=$l." ".$rec[0]{MY_GRIDSQUARE} if($rec[0]{MY_GRIDSQUARE});
	    $l=$l." ".$rec[0]{MY_CITY} if($rec[0]{MY_CITY});
	    $l=$l." ".$rec[0]{MY_CNTY} if($rec[0]{MY_CNTY});
	    $l=$l." ".$rec[0]{MY_STATE} if($rec[0]{MY_STATE});
	    $l=$l." ".$rec[0]{MY_COUNTRY} if($rec[0]{MY_COUNTRY});
	    $l=$l." ".$rec[0]{MY_IOTA} if($rec[0]{MY_IOTA});
	    if(exists $rec[0]{MY_SOTA_REF})
	    {
	        $l=$l." ".$rec[0]{MY_SOTA_REF} if($rec[0]{MY_SOTA_REF});
	    }
	    if($rec[0]{SAT_NAME})
	    {
		$l=$l." ".$rec[0]{SAT_NAME};
		$l=$l." ".$rec[0]{SAT_MODE} if($rec[0]{SAT_MODE});
	    }
	    $l=$l." ".$rec[0]{TX_PWR}."W" if($rec[0]{TX_PWR});
	    $l=$l."\n";
	    $l=$l."Date          UTC      MHz     Mode     RST     QSL  PROP\n";
	    $qsl{$rn}=$l;
	}
	$l=QslDate($rec[0]{QSO_DATE});
	$l=$l."   ".(substr $rec[0]{TIME_ON},0,4);
	$l=$l."  ".(sprintf "%7.3f",$rec[0]{FREQ}) if($rec[0]{FREQ});
	$l=$l."  ".(sprintf "%7.3f",Band2freq($rec[0]{BAND})) if(!$rec[0]{FREQ});
	$l=$l."    ".(sprintf "%-8.8s",$rec[0]{MODE});
	$l=$l." ".(sprintf "%3d",$rec[0]{RST_SENT}) if($rec[0]{RST_SENT});
	$l=$l."    " if(!$rec[0]{RST_SENT});
	$a="         "; 
	$a="     PSE " if(uc $rec[0]{QSL_RCVD} eq 'R');
	$a="     TNX " if(uc $rec[0]{QSL_RCVD} eq 'Y');
	$l=$l.$a;
	$l=$l." ".($rec[0]{PROP_MODE}) if($rec[0]{PROP_MODE}); 
	$l=$l."\n";
	$qsl{$rn}=$qsl{$rn}.$l;
	$rec[0]{QSL_SENT}='Y';
	$rec[0]{QSL_SENT_VIA}='B';
    }
}


# fill hash for QSL labels using -T template file
sub Qso2HashT
{
    my $l="";
    my $c;
    my $rn;
    my $a="";

    $c=$rec[0]{STATION_CALLSIGN};
    $c=$rec[0]{OWNER_CALLSIGN} if($rec[0]{OWNER_CALLSIGN});
 
    $rn=$c." ".$rec[0]{CALL};
    $rn=$rn." ".(uc $rec[0]{MY_GRIDSQUARE}) if($rec[0]{MY_GRIDSQUARE});

    if(exists $rec[0]{MY_SOTA_REF})
    { 	
        $rn=$rn." ".(uc $rec[0]{MY_SOTA_REF}) if($rec[0]{MY_SOTA_REF});
    }	

    if($rec[0]{CALL})
    {
	if(!exists $qsl{$rn})
	{
	    $l=$l."CALL=".($rec[0]{CALL});
	    if($rec[0]{QSL_VIA})
	    {
		$l=$l." QSL_VIA=".$rec[0]{QSL_VIA};
	    }
	    else
	    {
		$l=$l." QSL_VIA=";
	    }
	    $l=$l."\n";
	    $l=$l."MYCALL=".$c;
	    $l=$l." MY_CQ_ZONE=".$rec[0]{MY_CQ_ZONE} if($rec[0]{MY_CQ_ZONE});
	    $l=$l." MY_ITU_ZONE=".$rec[0]{MY_ITU_ZONE} 
	    if($rec[0]{MY_ITU_ZONE});
	    $l=$l." MY_GRIDSQUARE=".$rec[0]{MY_GRIDSQUARE} 
	    if($rec[0]{MY_GRIDSQUARE});
	    $l=$l." MY_CITY=".spc($rec[0]{MY_CITY}) if($rec[0]{MY_CITY});
	    $l=$l." MY_CNTY=".spc($rec[0]{MY_CNTY}) if($rec[0]{MY_CNTY});
	    $l=$l." MY_STATE=".spc($rec[0]{MY_STATE}) if($rec[0]{MY_STATE});
	    $l=$l." MY_COUNTRY=".spc($rec[0]{MY_COUNTRY}) 
		if($rec[0]{MY_COUNTRY});
	    $l=$l." MY_IOTA=".$rec[0]{MY_IOTA} if($rec[0]{MY_IOTA});
	    if(exists $rec[0]{MY_SOTA_REF})
	    {
	        $l=$l." MY_SOTA_REF=".$rec[0]{MY_SOTA_REF} if($rec[0]{MY_SOTA_REF});
	    }
	    if($rec[0]{SAT_NAME})
	    {
		$l=$l." SAT_NAME=".$rec[0]{SAT_NAME};
		$l=$l." SAT_MODE=".$rec[0]{SAT_MODE} if($rec[0]{SAT_MODE});
	    }
	    $l=$l." TX_PWR=".$rec[0]{TX_PWR}."W" if($rec[0]{TX_PWR});
	    $l=$l."\n";
	    $qsl{$rn}=$l;
	}
	$l="DATE=".spc(QslDate($rec[0]{QSO_DATE}));
	$l=$l." TIME_ON=".(substr $rec[0]{TIME_ON},0,4);
	$l=$l." FREQ=".(sprintf "%-7.3f",$rec[0]{FREQ}) if($rec[0]{FREQ});
	$l=$l." BAND=".(sprintf "%-7.3f",Band2freq($rec[0]{BAND})) 
	    if(!$rec[0]{FREQ});
	$l=$l." MODE=".(sprintf "%-8.8s",$rec[0]{MODE});
	$l=$l." RST_SENT=".(sprintf "%-3d",$rec[0]{RST_SENT}) 
	    if($rec[0]{RST_SENT});
	if(uc $rec[0]{QSL_RCVD} eq 'R')
	{
	    $l=$l." QSL=PSE";
	}
	else
	{
	    if(uc $rec[0]{QSL_RCVD} eq 'Y')
	    {
		$l=$l." QSL=TNX";
	    }
	    else
	    {
		$l=$l." QSL=";
	    }
	}
	if($rec[0]{PROP_MODE})
	{
	    $l=$l." PROP_MODE=".($rec[0]{PROP_MODE});
	}
	else
	{
	    $l=$l." PROP_MODE=";
	}
	$l=$l."\n";
	$qsl{$rn}=$qsl{$rn}.$l;
    }
}



sub QslDate
{
    my @month=("JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC");
    my $d=shift;

    my $s=(substr $d,6,2)." ".$month[(substr $d,4,2)-1]." ".(substr $d,0,4);

    return $s;

}

# replace space with '_'
sub spc
{
    my $s=shift;
    $s =~ s/\s/_/g;

    return $s;
}

# print QSL labels from hash
# The hash %qsl has now lines
# 1. CALL QSL_VIA
# 2. MYCALL MY_CQ_ZONE...
# 3. DATE TIME_ON FREQ/BAND ...QSL
# 4. DATE TIME_ON FREQ/BAND ...QSL
# etc. 
# In addition to standard adif records also MYCALL, DATE and QSL are defined.
sub PrintQsls
{
    my $r;
    my $i;
    my $j;
    my $k;
    my $line;
    my @ls;
    my $pat;
    my $rep;
    my @q=("DATE","TIME_ON","BAND","MODE","RST_SENT","PROP_MODE","QSL");

    foreach $r (sort keys %qsl)
    {
#	if(0)
	if($opt_T)
	{
	    # split $qsl{$r} to lines, 3. first QSO, 4. second QSO...
	    @ls=split /\n/,$qsl{$r};
	    $k=2;

	    for($i=0;$i<@tfile;$i++)
	    {
		$line=$tfile[$i];
		
		# probably QSO line with date, time, band, mode, rst...
		if(($line =~ /DATE/)&&($line =~ /QSL/)&&($line =~ /MODE/))
		{
		    foreach $recn (@q)
		    {
			$pat="$recn=(\\S{1,30})?";

			# search the value from $ls[$k]
			if($k<@ls)
			{
			    if(($recn eq "BAND")&&($ls[$k] =~ /FREQ=(\d{1,5}\.\d{1,5})/))
			    {
				$pat="FREQ=(\\d{1,5}\\.\\d{1,5})";
			    }

			    if($ls[$k] =~ /$pat/m)
			    {
				$rep="";
				$rep=spa($1) if($1);
				$pat="__".$recn;
				$line =~ s/$pat/$rep/g;
			    }
			}
			else
			{
			    $rep="";
			    $pat="__".$recn;
			    $line =~ s/$pat/$rep/g;
			}
		    }
		    $k++;
		}
		else
		{
		    foreach $recn (keys %typ)
		    {
			$pat="$recn=(\\S{1,30})";
			
			# search the value from $qsl{$r}
			if($qsl{$r} =~ /$pat/m)
			{
			    $rep=spa($1);
			    $pat="__".$recn;
			    $line =~ s/$pat/$rep/g;
			}
		    }
		    $line =~ s/__QSL_VIA//;
		    if($qsl{$r} =~ /MYCALL=(\S{1,20})/m)
		    {
			$rep=$1;
			$pat="__MYCALL";
			$line =~ s/$pat/$rep/g;
		    }
		    if($qsl{$r} =~ /DATE=(\d{8})/m)
		    {
			$rep=QslDate($1);
			$pat="__DATE";
			$line =~ s/$pat/$rep/g;
		    }
		    if($qsl{$r} =~ /QSL=(\w{3})?/m)
		    {
			$rep="";
			$rep=$1 if($1);
			$pat="__QSL";
			$line =~ s/$pat/$rep/g;
		    }
		}
		print $line;
	    }
	}
	else
	{
	    print "$qsl{$r}\n";
	}
    }
}

# replace '_' with a space
sub spa
{
    my $s=shift;
    $s =~ s/_/\ /g;

    return $s;
}

# print qso array
sub PrintQsos
{
    my $i;
    my $l;
    
    for( $i = 0; $i < $qi; $i++)
    {
        $l = substr $qsos[ $i ], 14;
        while( ( substr $l, 0, 1 ) ne "<" )
        {
            $l = substr $l, 1, length($l) - 1;
        }
        print $l;
    }
}

# print short text file header
sub PrintTxtHead
{
    if( !$opt_d && !$opt_c && !$opt_a )
    {
	print "date     utc  band   mode   call        rsts rstr qsls/r eQ Lo prop comment\n";
    }
    if($opt_d)
    {
	print "date     utc  band   mode   call        rsts rstr qsls/r eQ Lo grid   distance\n";
    }
    if($opt_c)
    {
	print "date     utc  band   mode   call        rsts rstr qsls/r eQ Lo dxcc  cq itu\n";
    }
    if($opt_a)
    {
	print "date     utc  band   mode   call        rsts rstr qsls/r eQ Lo award\n";
    }

}

# print short text record
sub PrintTxtRec
{
    my $serr=shift;
    my $recno=shift;
    my $awstatus=shift;
    my $qsls="";
    my $oline="";
    my $newqth=0;

    # check if mycall or qth has changed
    $newqth=1 if($mycall ne (uc $rec[$recno]{STATION_CALLSIGN}));
    $newqth=1 if($myloc ne (uc $rec[$recno]{MY_GRIDSQUARE}));
    $newqth=1 if($myiota ne (uc $rec[$recno]{MY_IOTA}));

    $mycall=uc $rec[$recno]{STATION_CALLSIGN};
    $myloc=uc $rec[$recno]{MY_GRIDSQUARE};
    $myiota=uc $rec[$recno]{MY_IOTA};

    if(exists $rec[$recno]{MY_SOTA_REF})
    {
	$newqth=1 if($mysota ne (uc $rec[$recno]{MY_SOTA_REF}));
	$mysota=uc $rec[$recno]{MY_SOTA_REF};
    }
    if($newqth&&$opt_v)
    {
	print $mycall;
	print " $rec[$recno]{MY_CITY}" if($rec[$recno]{MY_CITY});
	print "  $mysota" if($mysota);
	print "  $myiota" if($myiota);
	print "  $myloc" if($myloc);
	print " $rec[$recno]{MY_RIG}" if($rec[$recno]{MY_RIG});
	print " $rec[$recno]{TX_PWR}W" if($rec[$recno]{TX_PWR});
	print "\n";
    }

    if($rec[$recno]{QSO_DATE})
    {
	$oline=$serr.$rec[$recno]{QSO_DATE};
    }
    else
    {
	$oline=$serr."        ";
    }
    if($rec[$recno]{TIME_ON})
    {
	$oline=$oline." ".(substr $rec[$recno]{TIME_ON},0,4);
    }
    else
    {
	$oline=$oline."     ";
    }
    if(($opt_v)&&($rec[$recno]{FREQ}))
    {
	$oline=$oline.(sprintf " %6.3f",$rec[$recno]{FREQ});
    }
    else
    {
	$oline=$oline.(sprintf " %-6.6s",$rec[$recno]{BAND});
    }
    if( $rec[$recno]{SUBMODE} )
    {
        $oline=$oline.(sprintf " %-6.6s",$rec[$recno]{SUBMODE});
    }
    else
    {
        $oline=$oline.(sprintf " %-6.6s",$rec[$recno]{MODE});
    }
    $oline=$oline.(sprintf " %-12.12s",$rec[$recno]{CALL});
    $oline = $oline . ( sprintf " %-3.3s", $rec[ $recno ]{ RST_SENT } ) 
        if( $rec[ $recno ]{ RST_SENT } || $rec[ $recno ]{ RST_SENT } eq "0" );
    $oline=$oline." -  " if(!$rec[$recno]{RST_SENT});
    $oline = $oline . ( sprintf " %-3.3s  ", $rec[ $recno ]{ RST_RCVD } ) 
        if( $rec[ $recno ]{ RST_RCVD } || $rec[ $recno ]{ RST_RCVD } eq "0" );
    $oline=$oline." -    " if( !$rec[$recno]{RST_RCVD} && $rec[ $recno ]{ RST_RCVD } ne "0" );
    $oline=$oline.($rec[$recno]{QSL_SENT}) 
	if($rec[$recno]{QSL_SENT});
    $oline=$oline."-" if(!$rec[$recno]{QSL_SENT});
    $oline=$oline.($rec[$recno]{QSL_SENT_VIA}) if($rec[$recno]{QSL_SENT_VIA});
    $oline=$oline."-" if(!$rec[$recno]{QSL_SENT_VIA});
    $oline=$oline.($rec[$recno]{QSL_RCVD}) if($rec[$recno]{QSL_RCVD});
    $oline=$oline."-" if(!$rec[$recno]{QSL_RCVD});
    $oline=$oline.($rec[$recno]{QSL_RCVD_VIA}) if($rec[$recno]{QSL_RCVD_VIA});
    $oline=$oline."-" if(!$rec[$recno]{QSL_RCVD_VIA});
    
    $oline=$oline." ".($rec[$recno]{EQSL_QSL_SENT}) 
	if($rec[$recno]{EQSL_QSL_SENT});
    $oline=$oline." -" if(!$rec[$recno]{EQSL_QSL_SENT});
    $oline=$oline.($rec[$recno]{EQSL_QSL_RCVD}) 
	if($rec[$recno]{EQSL_QSL_RCVD});
    $oline=$oline."-" if(!$rec[$recno]{EQSL_QSL_RCVD});
    
    $oline=$oline." ".($rec[$recno]{LOTW_QSL_SENT}) 
	if($rec[$recno]{LOTW_QSL_SENT});
    $oline=$oline." -" if(!$rec[$recno]{LOTW_QSL_SENT});
    $oline=$oline.($rec[$recno]{LOTW_QSL_RCVD}) 
	if($rec[$recno]{LOTW_QSL_RCVD});
    $oline=$oline."-" if(!$rec[$recno]{LOTW_QSL_RCVD});
    
    if( !$opt_d && !$opt_c && !$opt_a )
    {
	$oline=$oline." ".$rec[$recno]{PROP_MODE} if($rec[$recno]{PROP_MODE});
	if($opt_v)
	{
	    $oline=$oline." ".($rec[$recno]{GRIDSQUARE}) 
		if($rec[$recno]{GRIDSQUARE});
	    if(exists $rec[$recno]{SOTA_REF})
	    {
		$oline=$oline." ".($rec[$recno]{SOTA_REF}) if($rec[$recno]{SOTA_REF});
	    }
            $oline=$oline." ".($rec[$recno]{POTA_REF}) if($rec[$recno]{POTA_REF});
            $oline=$oline." ".($rec[$recno]{WWFF_REF}) if($rec[$recno]{WWFF_REF});

	    $oline=$oline." I".($rec[$recno]{ITUZ}) if($rec[$recno]{ITUZ});
            $oline=$oline." ".($rec[$recno]{CONT}) if($rec[$recno]{CONT});
	    $oline=$oline."-".($rec[$recno]{CQZ}) if($rec[$recno]{CQZ});
	    $oline=$oline." ".($rec[$recno]{RIG}) if($rec[$recno]{RIG});
	    $oline=$oline." ".($rec[$recno]{SAT_NAME}) 
		if($rec[$recno]{SAT_NAME});
	    $oline=$oline." ".($rec[$recno]{SAT_MODE}) 
		if($rec[$recno]{SAT_MODE});
	}
	$oline=$oline." ".$rec[$recno]{COMMENT} if($rec[$recno]{COMMENT});
	$oline=$oline." ".$rec[$recno]{NOTES} if($rec[$recno]{NOTES});
    }
    if($opt_d)
    {
	$oline=$oline.(sprintf " %-8.8s",$rec[$recno]{GRIDSQUARE}) 
	    if($rec[$recno]{GRIDSQUARE});
	$oline=$oline."        " if(!$rec[$recno]{GRIDSQUARE});
	$oline=$oline.(sprintf " %6.0f",$rec[$recno]{DISTANCE}) if($rec[$recno]{DISTANCE});
    }
    if( $opt_c )
    {
	$oline=$oline.(sprintf " %6d",$rec[$recno]{DXCC}) if($rec[$recno]{DXCC});
	$oline=$oline."       " if(!$rec[$recno]{DXCC});
	$oline=$oline.(sprintf "  %2d",$rec[$recno]{CQZ}) if($rec[$recno]{CQZ});
	$oline=$oline."    " if(!$rec[$recno]{CQZ});
	$oline=$oline.(sprintf " %2d",$rec[$recno]{ITUZ}) if($rec[$recno]{ITUZ});
	$oline=$oline."   " if(!$rec[$recno]{ITUZ});
    }
    if($opt_a)
    {
	$oline=$oline." ".$awstatus;
    }

    $oline="NIL:".$oline if((uc $rec[$recno]{QSO_COMPLETE} eq "NIL")||(uc $rec[$recno]{QSO_COMPLETE} eq "N"));

    print "$oline\n";

} 


# print full text record
sub PrintTxtRecL
{
    my $serr = shift;
    my $recno = shift;
    my $s = shift;
    my $oline = "";
    my $recn;

    if( $rec[ $recno ]{ QSO_DATE } )
    {
        if($s eq " ")
        {
            $oline = $serr . $rec[ $recno ]{ QSO_DATE };
        }
        else
        {
            $oline = $serr . date2csv( $rec[ $recno ]{ QSO_DATE } );
        }
    }
    else
    {
        $oline = $serr . "-";
    }
    if( $rec[ $recno ]{ TIME_ON } )
    {
        if( $s eq " " )
        {
            $oline = $oline . $s . (substr $rec[ $recno ]{ TIME_ON }, 0, 4 );
        }
        else
        {
            $oline = $oline . $s . time2csv( $rec[ $recno ]{ TIME_ON } );
        }
    }
    else
    {
        $oline = $oline . "-";
    }
    
    foreach $recn (sort keys %typ)
    {
        $oline = $oline . $s . $rec[ $recno ]{ $recn } if( TestRecValid( $recn, $rec[ $recno ]{ $recn } ) && $recn ne "QSO_DATE" && $recn ne "TIME_ON" );
    }

    $oline = "NIL:" . $oline if( (uc $rec[$recno]{QSO_COMPLETE} eq "NIL") || (uc $rec[$recno]{QSO_COMPLETE} eq "N") );

    print "$oline\n";

}

# print comma-seprated-value (csv) record truncated for SOTA database import
sub PrintCsvRec
{
    my $recno = shift;
    my $sep  = shift;
    $sep = "," if( $sep !~ /[,;:\|\ \t\-\n@#\$\/%_]/ );
    my $oline = "";
    my $recn;

    if( $rec[ $recno ]{ STATION_CALLSIGN } )
    {
	$oline = $rec[ $recno ]{ STATION_CALLSIGN } . $sep;
    }
    else
    {
	$oline = "-" . $sep;
    }
    
    if( $rec[ $recno ]{ QSO_DATE } )
    {
	$oline = $oline . date2csv( $rec[ $recno ]{ QSO_DATE } ) . $sep;
    }
    else
    {
	$oline = $oline . "-". $sep;
    }

    if( $rec[ $recno ]{ TIME_ON } )
    {
	$oline = $oline . ( substr $rec[ $recno ]{ TIME_ON }, 0, 4 ) . $sep;
    }
    else
    {
	$oline = $oline . "-". $sep;
    }

    if( $rec[ $recno ]{ MY_SOTA_REF } )
    {
	$oline = $oline . $rec[ $recno ]{ MY_SOTA_REF } . $sep;
    }
    else
    {
	if( $rec[ $recno ]{ SOTA_REF } )
	{
	    $oline = $oline . $rec[ $recno ]{ SOTA_REF } . $sep;
	}
	else
	{
	    $oline = $oline . "-" . $sep;
	}
    }

    if( $rec[ $recno ]{ BAND } )
    {
	$oline = $oline . b2sota ($rec[ $recno ]{ BAND } ) . $sep;
    }
    else
    {
	$oline = "-" . $sep;
    }
    
    if( $rec[ $recno ]{ SUBMODE } )
    {
        $oline = $oline . $rec[ $recno ]{ SUBMODE } . $sep;
    }
    elsif( $rec[ $recno ]{ MODE } )
    {
        $oline = $oline . $rec[ $recno ]{ MODE } . $sep;
    }
    else
    {
	$oline = "-" . $sep;
    }

    if( $rec[ $recno ]{ CALL } )
    {
	$oline = $oline . $rec[ $recno ]{ CALL } . $sep;
    }
    else
    {
	$oline = "-" . $sep;
    }

    if( $rec[ $recno ]{ RST_SENT } || $rec[ $recno ]{ RST_SENT } eq "0" )
    {
        $oline = $oline . $rec[ $recno ]{ RST_SENT };
    }
    $oline = $oline . "/";
    if( $rec[ $recno ]{ RST_RCVD } || $rec[ $recno ]{ RST_RCVD } eq "0" )
    {
        $oline = $oline . $rec[ $recno ]{ RST_RCVD };
    }

    print "$oline\n";

}


# band to SOTA frequency
sub b2sota
{
    my $bnd=shift;

    my %bands=("2190M" => "VLF",
	       "560M" => "VLF",
	       "160M" => "1.8MHz",
	       "80M"  => "3.5MHz",
	       "60M"  => "5MHz",
	       "40M"  => "7MHz",
	       "30M"  => "10MHz",
	       "20M"  => "14MHz",
	       "17M"  => "18MHz",
	       "15M"  => "21MHz",
	       "12M"  => "24MHz",
	       "10M"  => "28MHz",
	       "6M"   => "50MHz",
	       "4M"   => "70MHz",
	       "2M"   => "144MHz",
	       "1.25M" => "220MHz",
	       "70CM" => "433MHz",
	       "33CM" => "900MHz",
	       "23CM" => "1240MHz",
	       "13CM" => "2.3GHz",
	       "9CM"  => "3.4GHz",
	       "6CM"  => "5.6GHz",
	       "3CM"  => "10GHz",
	       "1.25CM" => "24GHz",
	       "6MM"  => "MICROWAVE",
	       "4MM"  => "MICROWAVE",
	       "2.5MM" => "MICROWAVE",
	       "2MM"  => "MICROWAVE",
	       "1MM"  => "MICROWAVE");

    return $bands{$bnd};

}

# print record by replacing adif tags in file given with -T
# example line: 'QSO with _CALL at _TIME_ON UTC on band _BAND m'
sub PrintTempRec
{
    my $recno=shift;
    my $recn;
    my $i=0;
    my $line;
    my $pat;
    my $rep;

    for( $i=0; $i<@tfile; $i++ )
    {
	$line = $tfile[$i];
	foreach $recn (keys %typ)
	{
            if( exists $rec[$recno]{$recn} )
	    {
                $rep = $rec[$recno]{$recn};
                # use SUBMODE instead of MODE if exists
                if( $recn =~ /MODE/i )
                {
                    $rep = $rec[$recno]{SUBMODE} if( exists $rec[$recno]{SUBMODE} );
                }
                $pat = "__".$recn;
                $line =~ s/$pat/$rep/g;
            }
        }
        print $line;
    }
}

# parse adif file line and fill record hash, return rest of line
sub AdifLine
{
    my $line=shift;
    my $recno=shift;
    my $parm;
    my $dta;
    my $pos;
    my $len;
    my $c;

    # while loop to discard characters before '<'
    $c=substr $line,0,1;
    while(($c ne "<")&&(length($line)>0))
    {
        $line=substr $line,1,(length($line)-1);
        $c=substr $line,0,1;
    }

    return "" if(length($line)==0);

    while($line =~ /^<(\w+):(\d+)(:[ABNSDTML])?>.*/i)
    {
        $parm=uc $1;
        $len=$2;
        $pos=length($parm)+length($len)+3;
        $pos+=2 if($3);
        $dta=substr $line,$pos,$len;
        $line=substr $line,($pos+$len);
        
        if(exists $rec[$recno]{$parm})
        {
             $rec[$recno]{$parm}=$dta;
         }
        else 
	{
	    if($parm !~ /^APP_\w+_\w+/i)
	    {
		print "Unknown parameter $parm = $dta\n";
		print "Header should not start with \"<\"\n"
		    if(exists $hed{$parm});
	    }
	}

        $c=substr $line,0,1;
        while(($c ne "<")&&(length($line)>0))
        {
            $line=substr $line,1,(length($line)-1);
            $c=substr $line,0,1;
        }
    }

    return $line;
}

# print header to ADIF file
sub PrintAdifHead
{
    my $oline="";
    my $recn; # record name

    $hed{ADIF_VER} = $averno;
    $hed{PROGRAMID} = "adifmerg";
    $hed{PROGRAMVERSION} = $verno;

    print "# http://www.adif.org\n";
    foreach $recn (sort keys %hed)
    {
        if( $hed{$recn} )
        {
            $oline = "<" . $recn . ":" . length($hed{$recn}) . ">" . ($hed{$recn});
	    print "$oline\n";
        }
    }
    print "<eoh>\n";
}

# print header to ADX file
sub PrintAdxHead
{
    my $oline="";
    my $recn; # record name

    $hed{PROGRAMID}="adifmerg";
    $hed{PROGRAMVERSION}=$verno;
    $hed{ADIF_VER}=$averno;

    print '<?xml version="1.0" encoding="UTF-8"?>\n';
    print "<ADX>\n";
    print "<HEADER>\n";
    print "<-!-- http://www.adif.org -->\n";
    foreach $recn (sort keys %hed)
    {
        if(($hed{$recn})&&($recn !~ /USERDEF/i))
        {
	   $oline="<".$recn.">".hed{$recn}."</".$recn.">"; 
	   print "$oline\n";
        }
        elsif($recn =~ /USERDEF(\d{1,2})/i)
        {
	   $oline='<USERDEF FIELDID="$1" TYPE="$typ{$recn}">$recn</USERDEF>';
           print "$oline\n"; 
        }
    }
    print "</HEADER>\n";
    print "</ADX>\n";
}

# read in CSV file with mycall,date,utc,sota,freq,mode,call,comment
# comment is matched for sent/received rst
# date, time and band are converted from SOTA database format
sub ReadCsvLog
{
    my $file = shift;
    my $line;
    my $LOGFILE;
    my $l = 0;
    my $ok = 0;
    my $ne = 0;
    my $serr = ""; # type of error
    my @csvhead; # csv header array
    my @csvrec; # csv record array
    my $i;
    my $j;
    my @d;
    my $comment = "";

    my %band = ("VLF"    => "2190M",
		"1.8MHZ" => "160M",
		"3.5MHZ" => "80M",
		"5MHZ"   => "60M",
		"7MHZ"   => "40M",
		"10MHZ"  => "30M",
		"14MHZ"  => "20M",
		"18MHZ"  => "17M",
		"21MHZ"  => "15M",
		"24MHZ"  => "12M",
		"28MHZ"  => "10M",
		"50MHZ"  => "6M",
		"70MHZ"  => "4M",
		"144MHZ" => "2M",
 		"220MHZ" => "1.25M", 
		"432MHZ" => "70CM",
		"900MHZ" => "33CM",
		"1240MHZ" => "23CM",
		"2.36GHZ" => "13CM",
		"3.4GHZ" => "9CM",
		"5.6GHZ" => "6CM",
		"10GHZ" => "3CM",
		"24GHZ" => "1.25CM",
		"MICROWAVE" => "6MM");


    if( open($LOGFILE, "<", $file ) || ( $file eq "-csv" ) )
    {
        # throw away first line
	if($file ne "-csv")
	{
	    $line = <$LOGFILE>; 
	}
	else
	{
	    $line = <>; 
	}
        @csvhead = split /,/, $line if( $line );

	print "-- header: $csvhead[0], $csvhead[1], ...\n" if( $dbug );

	die("Check CSV-file header for \%cconv hash, exit now.\n") if( !exists( $cconv{ $csvhead[ 0 ] } )  && !exists( $cconv{ $csvhead[ 1 ] } ) );

	if( $file ne "-csv" )
	{
	    $line = <$LOGFILE>; 
	}
	else
	{
	    $line = <>; 
	}

        $ok = 1;
	while( $line )
        {
	    EmptyRec( 0 ); # clean hash before adding new data
            print "$l:" if( $dbug );

            @csvrec = split /,/, $line if( $line );

	    for( $i = 0; $i < @csvhead; $i++ )
	    {

# remove extra hidden characters from SOTA chaser log header line end
                $csvhead[ $i ] = "Notes" if( $csvhead[ $i ] =~ /Notes/ );
                $csvhead[ $i ] = "Comments" if( $csvhead[ $i ] =~ /Comments/ );

                if( exists $cconv{ $csvhead[ $i ] } )
                {
                    if( $csvhead[ $i ] eq "Band" )
                    {
			if( CheckBand( $csvrec[ $i ] ) )
                        {
                            $rec[ 0 ]{ BAND } = $csvrec[ $i ];
			}
                        else
                        {
                            $rec[ 0 ]{ BAND } = $band{ uc $csvrec[ $i ] };
                        }			
                    }
                    else
                    {
                        if( $csvhead[ $i ] eq "Date" )
                        {
                            $csvrec[ $i ] =~ m!(\d\d)/(\d\d)/(\d\d\d\d)!;
                            $rec[ 0 ]{ QSO_DATE } = $3 . $2 . $1;  
                        }
                        else
                        {
                            if( $csvhead[ $i ] eq "Time" )
                            {
                                $csvrec[ $i ] =~ /^(\d?\d):(\d\d)/;
                                $rec[ 0 ]{ TIME_ON } = $1 . $2;
			        $rec[ 0 ]{ TIME_ON } = "0" . $rec[ 0 ]{ TIME_ON } if( ( length $rec[ 0 ]{ TIME_ON} ) == 3 );
			    }
                            else
                            {
                                if( $csvhead[$i] eq "Notes" || $csvhead[$i] eq "Comments" )
                                {
                                    $csvrec[ $i ] =~ s/"//g;
                                    $csvrec[ $i ] =~ m!([1-5][1-9][1-9]?)(QSB|QRM|QRN)?/([1-5][1-9][1-9]?)(.*)!;
                                    if( $1 )
                                    {
                                        $rec[ 0 ]{ RST_SENT } = $1 if( $1 );
                                        $rec[ 0 ]{ RST_RCVD } = $3 if( $3 );
                                        if( $2 )
                                        {
                                            $rec[ 0 ]{ COMMENT } = $2;
                                            $rec[ 0 ]{ COMMENT } = $rec[ 0 ]{ COMMENT } . " " . $4 if( $4 );
                                        }
                                        else
                                        {
                                            $rec[ 0 ]{ COMMENT } = $4 if( $4 );
                                        }
                                    }
                                    else
                                    {
                                        $rec[ 0 ]{ COMMENT } = $csvrec[ $i ];
                                    }

                                    @d = split /\s/, $rec[ 0 ]{ COMMENT };
                                    $comment = "";
                                    for( $j = 0; $j < @d; $j++ )
                                    {
                                        if( CheckPmode( $d[ $j ] ) )
                                        {
                                            $rec[ 0 ]{ PROP_MODE } = $d[ $j ];
					}
                                        elsif( CheckSat( $d[ $j ] ) )
                                        {
                                            $rec[ 0 ]{ SAT_NAME } = $d[ $j ];
                                        }
                                        elsif( CheckSatMode( $d[ $j ] ) )
                                        {
                                            $rec[ 0 ]{ SAT_MODE } = $d[ $j ];
					}
                                        elsif( ( CheckGrid( $d[ $j ] ) ) && ( length( $d[ $j ] ) >= 4 ) )
                                        {
                                            $rec[ 0 ]{ GRIDSQUARE } = $d[ $j ];
                                        }
                                        elsif( CheckIota( $d[ $j ] ) )
                                        {
                                            $rec[ 0 ]{ IOTA } = $d[ $j ];
                                        }
                                        elsif( CheckCQZ( $d[ $j ] ) )
                                        {
                                            $d[ $j ] =~ /^(NA|SA|EU|AF|OC|AS|AN)-(\d{1,2})/;
                                            $rec[ 0 ]{ CONT } = $1;
                                            $rec[ 0 ]{ CQZ } = $2;
					}
                                        elsif( CheckSota( $d[ $j ] ) )
                                        {
                                            $rec[ 0 ]{ SOTA_REF } = $d[ $j ] if( exists $rec[ 0 ]{ SOTA_REF } );
                                        }
                                        elsif( CheckPota( $d[ $j ] ) )
                                        {
                                            $rec[ 0 ]{ POTA_REF } = $d[ $j ];
					}
                                        elsif( CheckWwff( $d[ $j ] ) )
                                        {
                                            $rec[ 0 ]{ WWFF_REF } = $d[ $j ];
					}
					elsif( $comment )
                                        {
                                            $comment = $comment . " " . $d[ $j ];
                                        }
                                        else
                                        {
                                            $comment = $d[ $j ];
                                        }
                                    }
				    
                                    $rec[ 0 ]{ COMMENT } = $comment;

                                }
                                else
                                {
                                    $rec[ 0 ]{ $cconv{ $csvhead[ $i ] } } = $csvrec[ $i ];
                                }
                            }
                        }
                    }
                }
                else
                {
                    print "-- unknown CSV header column $csvhead[ $i ]\n" if( $dbug );
                }

	    }

            if( $dbug )
            {
		print "--record complete\n";
		print "call $rec[0]{CALL}\n";
		print "time $rec[0]{TIME_ON}\n";
		print "date $rec[0]{QSO_DATE}\n";
		print "rst $rec[0]{RST_SENT}\n";
		print "mode $rec[0]{MODE}\n";
		print "--\n";
	    }

	    # print qso with error message
            if( $opt_l || $opt_o || $opt_L || $opt_x )
	    {
               $nerr += PrintQso( 0 );
            }
	    
            $l++; 

            if( $file ne "-csv" )
	    {
                $line = <$LOGFILE>; 
            }
            else
            {
                $line = <>; 
           }
        }
    }

    print "$nerr errors found\n" if( $nerr > 0 ); 
    return $l;
}


# read text file with date, utc, band, mode, call, rsts, rstr, qsls/r, comment
sub ReadTxtLog
{
    my $file=shift;
    my $line;
    my $LOGFILE;
    my $l=0;
    my $ok=0;
    my $ne=0;
    my $serr=""; # type of error
    my @d;
    my $i;
    my $comment="";

    my $statcall=""; # station call sign
    my $mygrid=""; # my grid square
    my $mycq=""; # my CQ zone
    my $myitu=""; # my ITU zone
    my $myiota=""; # my IOTA reference
    my $mysota=""; # my SOTA reference
    my $myrig=""; # my rig
    my $txpwr=""; # tx power
    my $mycounty="";
    my $mystate="";
    my $mycity="";
    my $mycountry="";
    my $mypota = "";
    my $mywwff = "";

    if(open($LOGFILE,"<",$file)||($file eq "-txt"))
    {
	if($file ne "-txt")
	{
	    $line=<$LOGFILE>;
	}
	else
	{
	    $line=<>;
	}
        $ok=1;
        while($line)
        {
	    EmptyRec(0);

	    @d = split /\s+/, $line;

	    if($d[0]&&$d[1]&&$d[2]&&$d[3]&&$d[4]&&$d[5]&&$d[6]&&$d[7]&&$d[8]&&$d[9]&&CheckDate($d[0]))
	    {
		$rec[0]{QSO_DATE}=$d[0];
		$rec[0]{TIME_ON}=$d[1];
		if(CheckBand($d[2]))
		{
		    $rec[0]{BAND}=$d[2];
		}
		else
		{
		    $rec[0]{FREQ}=$d[2];
		    $rec[0]{BAND}=Freq2band($d[2]);
		}
		$rec[0]{MODE}=$d[3];
		$rec[0]{CALL}=$d[4];
		$rec[0]{RST_SENT}=$d[5] if($d[5] ne "-");
		$rec[0]{RST_RCVD}=$d[6] if($d[6] ne "-");
		$rec[0]{QSL_SENT}=substr $d[7],0,1 
		    if((substr $d[7],0,1) ne "-"); 
		$rec[0]{QSL_SENT_VIA}=substr $d[7],1,1 
		    if((substr $d[7],1,1) ne "-"); 

		$rec[0]{QSL_RCVD}=substr $d[7],2,1 
		    if((substr $d[7],2,1) ne "-"); 
		$rec[0]{QSL_RCVD_VIA}=substr $d[7],3,1 
		    if((substr $d[7],3,1) ne "-"); 

		$rec[0]{EQSL_QSL_SENT}=substr $d[8],0,1 
		    if((substr $d[8],0,1) ne "-");
		$rec[0]{EQSL_QSL_RCVD}=substr $d[8],1,1 
		    if((substr $d[8],1,1) ne "-");
		$rec[0]{LOTW_QSL_SENT}=substr $d[9],0,1 
		    if((substr $d[9],0,1) ne "-");
		$rec[0]{LOTW_QSL_RCVD}=substr $d[9],1,1 
		    if((substr $d[9],1,1) ne "-");

		# check if rest of the line has useful data like propagation 
		# mode, satellite name or locator - rest is added to comment 
		# record
		$comment = Notes2Adif( $line );

		$rec[0]{COMMENT} = $comment;
	    
		if($dbug)
		{
		    print "--record complete\n";
		    print "call $rec[0]{CALL}\n";
		    print "time $rec[0]{TIME_ON}\n";
		    print "date $rec[0]{QSO_DATE}\n";
		    print "rst $rec[0]{RST_SENT}\n";
		    print "mode $rec[0]{MODE}\n";
		    print "--\n";
		}

		# add my station info if available
		$rec[0]{STATION_CALLSIGN}=$statcall if($statcall);
		$rec[0]{MY_GRIDSQUARE}=$myloc if($myloc);
		$rec[0]{MY_CQ_ZONE}=$mycq if($mycq);
		$rec[0]{MY_ITU_ZONE}=$myitu if($myitu);
		$rec[0]{MY_RIG}=$myrig if($myrig);
		$rec[0]{TX_PWR}=$txpwr if($txpwr);
		$rec[0]{MY_CITY}=$mycity if($mycity);
		$rec[0]{MY_CNTY}=$mycounty if($mycounty);
		$rec[0]{MY_COUNTRY}=$mycountry if($mycountry);
		$rec[0]{MY_STATE}=$mystate if($mystate);
                $rec[0]{MY_POTA_REF}=$mypota if($mypota);
                $rec[0]{MY_WWFF_REF}=$mywwff if($mywwff);

		if(exists $rec[0]{MY_SOTA_REF})
		{
		    $rec[0]{MY_SOTA_REF}=$mysota if($mysota);
		}	

		# print qso with error message
		if($opt_l||$opt_o||$opt_L||$opt_x||$opt_X)
		{
		    $nerr+=PrintQso(0);
		}
	    }
	    else
	    {
		# check for my call, locator etc.
		if(($d[0])&&($d[0]!~/date/i)&&($d[0]!~/#/))
		{
		    $statcall=$d[0];
		    ($myloc,$mycq,$mysota,$myitu,$myrig,$txpwr,$mycounty,$mystate,$mycity,$mycountry,$mypota,$mywwff) = MyNotes2Adif( $line );
		}
	    }
	    $l++;

	    if($file ne "-txt")
	    {
		$line=<$LOGFILE>;
	    }
	    else
	    {
		$line=<>;
	    }
	}
    }
    
    print "$nerr errors found\n" if($nerr>0); 
    return $l;
}

# read file cty.plist by AD1C from http://www.country-files.com/ to hash %ctylist
sub ReadCtyPlist
{
    my $file = shift;
    my $line;
    my $CTYFILE;

    my $key = "";
    my $country = "";
    my $prefix = "";
    my $dxcc = 0;
    my $cqzone = 0;
    my $ituzone = 0;
    my $continent = "";
    my $exactCallsign = "";

    my $nkeys = 0;
    if( open( $CTYFILE, "<", $file ) )
    {
        $line = <$CTYFILE>;
	#	print $line, "\n";

	while( $line )
	{
            if( $line =~ /^<key>(\w+)<\/key>/i )
	    {
		    #		print $1, "\n";
                $key = $1;
                $nkeys++;

                $country = "";
                $prefix = "";
                $dxcc = 0;
                $cqzone = 0;
                $ituzone = 0;
                $continent = "";
                $exactCallsign = "";

                $line = <$CTYFILE>;
		#	        print $line, "\n";

		while( $line )
		{
		    if( $line =~ /<\/dict>/i )
		    {
                        $line = "";
                    }
		    else
		    {
                        if( $line =~ /<key>Country<\/key>/i )
		        {
                            $line = <$CTYFILE>;
                            if( $line =~ /<string>(.+)<\/string>/ )
			    {
                                $country = $1; 
                                $country = "Federal Republic of Germany" if( $country =~ /Fed. Rep. of Germany/i);
                                $country = "Democratic Republic of the Congo" if( $country =~ /Dem. Rep. of Congo/i);
                            }
		        }
                        elsif( $line =~ /<key>Prefix<\/key>/i )
		        {
                            $line = <$CTYFILE>;
                            $prefix = $1 if( $line =~ /<string>(\w+)<\/string>/ );
                        }
                        elsif( $line =~ /<key>ADIF<\/key>/i )
                        {
                            $line = <$CTYFILE>;
                            $dxcc = scalar( $1 ) if( $line =~ /<integer>(\w+)<\/integer>/ );
                        }
                        elsif( $line =~ /<key>CQZone<\/key>/i )
                        {
                            $line = <$CTYFILE>;
                            $cqzone = scalar( $1 ) if( $line =~ /<integer>(\w+)<\/integer>/ );
                        }
                        elsif( $line =~ /<key>ITUZone<\/key>/i )
                        {
                            $line = <$CTYFILE>;
                            $ituzone = scalar( $1 ) if( $line =~ /<integer>(\w+)<\/integer>/ );
                        }
                        elsif( $line =~ /<key>Continent<\/key>/i )
                        {
                            $line = <$CTYFILE>;
                            $continent = $1 if( $line =~ /<string>(\w+)<\/string>/ );
                        }
                        
			$line = <$CTYFILE>;
			#                        print $line, "\n";
                    }
                }

		print "$key $country $prefix $dxcc $cqzone $ituzone $continent\n" if( $dbug );

		$ctylist{ $key } = [$country, $cqzone, $ituzone, $continent, $dxcc];
		$line = <$CTYFILE> if( !$line );
            }
	    else
	    {
                $line = <$CTYFILE>;
            }
	}
    }
    else
    {
        print "Could not read file $file\n";
        return 0;
    }

    print "-- read $nkeys from cty.plist file\n" if( $dbug );

    return $nkeys;
}

# fill adif records from the notes 
sub Notes2Adif
{
    my $comment="";
    my $i;
    my $line=shift;
    my @d;

    @d = split /\s+/, $line;
    for($i=10;$i<@d;$i++)
    {
	if(CheckPmode($d[$i]))
	{
	    $rec[0]{PROP_MODE}=$d[$i];
	}
	elsif(CheckSat($d[$i]))
        {
           $rec[0]{SAT_NAME}=$d[$i];
        }
        elsif(CheckSatMode($d[$i]))
        {
           $rec[0]{SAT_MODE}=$d[$i];
        }
        elsif((CheckGrid($d[$i]))&&(length($d[$i])>=4))
        {
           $rec[0]{GRIDSQUARE}=$d[$i];
        }
        elsif(CheckIota($d[$i]))
        {
           $rec[0]{IOTA}=$d[$i];
        }
        elsif(CheckCQZ($d[$i]))
        {
           $d[$i] =~ /^(NA|SA|EU|AF|OC|AS|AN)-(\d{1,2})/;
           $rec[0]{CONT}=$1;
           $rec[0]{CQZ}=$2;
        }
        elsif( CheckPota( $d[ $i ] ) )
        {
           $rec[ 0 ]{ POTA_REF } = $d[ $i ];
	}
	elsif(CheckSota($d[$i]))
        {
           $rec[0]{SOTA_REF}=$d[$i] if(exists $rec[0]{SOTA_REF});
        }
        elsif( CheckWwff( $d[ $i ] ) )
        {
           $rec[ 0 ]{ WWFF_REF } = $d[ $i ];
	}
	elsif(CheckVia($d[$i]))
        {
           $rec[0]{QSL_VIA}=CheckVia($d[$i]);
        }
        elsif(CheckRig($d[$i]))
        {
           $rec[0]{RIG}=$d[$i];
        }
        elsif(CheckPwr($d[$i]))
        {
           $rec[0]{RX_PWR}=CheckPwr($d[$i]);
        }
        elsif($d[$i] =~ /I(\d{1,2})/i)
        {
           $rec[0]{ITUZ}=$1;
        }
        elsif($d[$i] =~ /QSL/i)
        {
           $rec[0]{QSL_SENT}="R";
        }
        elsif($d[$i] =~ /BURO/i)
        {
           $rec[0]{QSL_SENT_VIA}="B";
        }
        elsif($d[$i] =~ /NIL/i)
        {
           $rec[0]{QSO_COMPLETE}="NIL";
        }
        elsif($comment)
        {
           $comment=$comment." ".$d[$i];
        }
        else
        {
           $comment=$d[$i];
	}
    }

    return $comment;
}

# fill adif records from the my QTH and call notes 
sub MyNotes2Adif
{
    my $i;
    my @d;
    my $line = shift; 
    my $myloc = "";
    my $mycq = 0;
    my $mysota = "";
    my $myitu = 0;
    my $myrig = "";
    my $txpwr = 0;
    my $mycounty = "";
    my $mystate = "";
    my $mycity = "";
    my $mycountry = "";
    my $mypota = "";
    my $mywwff = "";

    @d = split /\s+/, $line;

    for( $i = 1; $i < scalar( @d ); $i++ )
    {
	if( (CheckGrid($d[$i])) && (length($d[$i])>=4) )
	{
	    $myloc = $d[$i];
	}
	elsif( CheckIota( $d[ $i ] ) )
        {
            $myiota = $d[ $i ];
        }
        elsif( CheckCQZ( $d[ $i ] ) )
        {
            $d[ $i ] =~ /^(NA|SA|EU|AF|OC|AS|AN)-(\d{1,2})/;
            #$mycont=$1;
            $mycq = $2;
        }
        elsif( CheckSota( $d[ $i ] ) )
        {
            $mysota = $d[ $i ];
        }
        elsif( CheckPota( $d[ $i ] ) )
        {
            $mypota = $d[ $i ];
	}
        elsif( CheckWwff( $d[ $i ] ) )
        {
            $mywwff = $d[ $i ];
	}
	elsif( CheckRig( $d[ $i ] ) )
        {
            $myrig = $d[ $i ];
        }
        elsif( CheckPwr( $d[ $i ] ) )
        {
            $txpwr = CheckPwr( $d[ $i ] );
        }
        elsif( $d[ $i ] =~ /I(\d{1,2})/i )
        {
            $myitu = $1;
        }
        elsif( $d[ $i ] =~ /ci([A-Z]\w+(-\w+)?)/ )
        {
            $mycity = $1;
            $mycity =~ s/_/\ /g;
        }
        elsif( $d[ $i ] =~ /st([A-Z]\w+(-\w+)?)/ )
        {
            $mystate = $1;
            $mystate =~ s/_/\ /g;
        }
        elsif( $d[ $i ] =~ /cy([A-Z]\w+(-\w+)?)/ )
        {
            $mycounty = $1;
            $mycounty =~ s/_/\ /g;
        }
        elsif( $d[ $i ] =~ /co([A-Z]\w+(-\w+)?)/ )
        {
            $mycountry = $1;
            $mycountry =~ s/_/\ /g;
        }
    }

    return ($myloc,$mycq,$mysota,$myitu,$myrig,$txpwr,$mycounty,$mystate,$mycity,$mycountry,$mypota,$mywwff);

}

# add new records but do not overwrite existing records
sub AddRec
{
    my $recno=shift;
    my ($recn,$recv);

    foreach(@arecs)
    {
	($recn,$recv)=split /=/, $_;
	if(exists $rec[$recno]{$recn})
	{
	    if(!$rec[$recno]{$recn})
	    {
		$rec[$recno]{$recn}=$recv;
	    }
	    else
	    {
		if($rec[$recno]{$recn} !~ /$recv/i)
		{
		    $rec[$recno]{$recn}=$rec[$recno]{$recn}.",".$recv
			if(($recn eq "CREDIT_SUBMITTED")||($recn eq "CREDIT_GRANTED"));
		}
	    }
	}
    }
}

# delete existing records
sub DelRec
{
    my $recno=shift;
    foreach(@drecs)
    {
	if(exists $rec[$recno]{$_})
	{
	    if($rec[$recno]{$_})
	    {
		if($typ{$_} eq "N")
		{
		    $rec[$recno]{$_}=0;
		}
		else
		{
		    $rec[$recno]{$_}="";
		}
	    }
	}
    }
}

# fix records with bad value
sub FixRec
{
    my $recno=shift;
    my ($recn,$rold,$rnew);

    foreach(@frecs)
    {
	($recn,$rold,$rnew)=split /=/, $_;
	if(exists $rec[$recno]{$recn})
	{
	    $rec[$recno]{$recn}=$rnew if($rec[$recno]{$recn} eq $rold);
	}
    }
}

# remap record to an other 
sub MapRec
{
    my $recno=shift;
    my ($recna,$recnb);

    foreach(@mrecs)
    {
	($recna,$recnb)=split /=/, $_;
	if((exists $rec[$recno]{$recna})&&(exists $rec[$recno]{$recnb}))
	{
	    if($rec[$recno]{$recnb})
	    {
		$rec[$recno]{$recna}=$rec[$recno]{$recnb};
		if($typ{$recnb} ne "N")
		{
		    $rec[$recno]{$recnb}="";
		}
		else
		{
		    $rec[$recno]{$recnb}=0;
		}
	    }
	}
    }
}


# add location if grid square is known
# precision depends on length of grid square 2, 4, 6 or 8
sub AddLoc
{
    my $recno = shift;
    if((!$rec[$recno]{LAT})&&(!$rec[$recno]{LON})&&($rec[$recno]{GRIDSQUARE}))
    {
	($rec[$recno]{LON},$rec[$recno]{LAT})=Grid2Loc($rec[$recno]{GRIDSQUARE}) 
	    if(CheckGrid($rec[$recno]{GRIDSQUARE}));
    }
    if((!$rec[$recno]{MY_LAT})&&(!$rec[$recno]{MY_LON})&&($rec[$recno]{MY_GRIDSQUARE}))
    {
	($rec[$recno]{MY_LON},$rec[$recno]{MY_LAT})=Grid2Loc($rec[$recno]{MY_GRIDSQUARE}) 
	    if(CheckGrid($rec[$recno]{MY_GRIDSQUARE}));
    }

}

# calculate distance if MY_LON, MY_LAT, LON and LAT are known
# from DXBearing.pm by Dirk Koopman G1TLH
sub CalcDist
{
    my $recno = shift;
    my $pi = 3.14159265358979;
    my $er = 6371; # earth radius
    my $d = 0;
    my $ok = 1;

    $ok = 0 if( !CheckLoc( $rec[ $recno ]{ LAT } ) );
    $ok = 0 if( !CheckLoc( $rec[ $recno ]{ LON } ) );
    $ok = 0 if( !CheckLoc( $rec[ $recno ]{ MY_LAT } ) );
    $ok = 0 if( !CheckLoc( $rec[ $recno ]{ MY_LON } ) );
    
    if( $ok && $rec[ $recno ]{ LAT } ne $rec[ $recno ]{ MY_LAT } && $rec[ $recno ]{ LON } ne $rec[ $recno ]{ MY_LON } )
    {
	my $hn = Loc2Rad( $rec[ $recno ]{ MY_LAT } );
	my $he = Loc2Rad( $rec[ $recno ]{ MY_LON } );
	my $n = Loc2Rad( $rec[ $recno ]{ LAT } );
	my $e = Loc2Rad( $rec[ $recno ]{ LON } );

	my $co = cos( $he - $e ) * cos( $hn ) * cos( $n ) + sin( $hn ) * sin( $n );
	my $ca = $co ? atan( abs( sqrt( 1 - $co * $co ) / $co ) ) : $pi;
	$ca = $pi - $ca if( $co < 0 );
	$d = $er * $ca;
	$rec[ $recno ]{ DISTANCE } = sprintf "%.3f", $d;
    }
}

# convert location to decimal radians
sub Loc2Rad
{
    my $pi = 3.14159265358979;
    my $l = shift;
    $l = uc $l;
    my $d = 0;

    if( $l =~ /^([EWNS])(\d\d\d)\s(\d\d\.\d\d\d)/ )
    {
	$d = $2 + $3 / 60;
	$d = -$d if( $1 eq 'W' || $1 eq 'S' );
    };

    $d *= $pi / 180;

    return $d;
}

#check if QSO data looks ok
sub CheckQso()
{
    my $recno = shift;
    my $serr = "";
    my $nerr = 0;
    my $cntry = "";
    my $cq = "";
    my $itu = "";
    my $cont = "";
    my $ctry = "";
    my $dxcc = "";
    my @entries;
    my $cntry2 = "";
    my $cq2 = "";
    my $itu2 = "";
    my $cont2 = "";
    my $ctry2 = "";
    my $dxcc2 = "";
    my @entries2;
    my $partial = "";
    my $found = 0;

    print "--[$recno] checking data\n" if( $dbug );

    if( !CheckDate( $rec[ $recno ]{QSO_DATE} ) )
    {
        $serr = $serr . "date?";
        $nerr++;
    }
    if( !CheckUtc( $rec[ $recno ]{TIME_ON} ) )
    {
        $serr = $serr . "utc?";
        $nerr++;
    }
    if( !CheckBand( $rec[ $recno ]{BAND} ) )
    {
        $serr = $serr . "band?";
        $nerr++;
    }
    if( !CheckMode( $rec[ $recno ]{MODE} ) )
    {
        $serr = $serr . "mode?";
        $nerr++;
    }
    if( $rec[ $recno ]{MORSE_KEY_TYPE} )
    {
        if( !CheckMorsekey( $rec[ $recno ]{MORSE_KEY_TYPE} ) )
        {
            $serr = $serr . "morse key?";
            $nerr++;
        }
    }
    if( $rec[ $recno ]{MY_MORSE_KEY_TYPE} )
    {
        if( !CheckMorsekey( $rec[ $recno ]{MY_MORSE_KEY_TYPE} ) )
        {
            $serr = $serr . "my morse key?";
            $nerr++;
        }
    }
    if( $rec[ $recno ]{SUBMODE} )
    {
        if( !CheckSubmode( $rec[ $recno ]{SUBMODE} ) )
        {
	    $serr = $serr . "submode?";
            $nerr++;
        }
    }
    if( $rec[ $recno ]{PROP_MODE} )
    {
	if( !CheckPmode( $rec[ $recno ]{PROP_MODE} ) )
        {
            $serr = $serr . "pmode?";
            $nerr++;
        }
    }
    if( $rec[ $recno ]{RST_SENT} )
    {
        if( !CheckRst( $rec[ $recno ]{RST_SENT} ) )
        {
            $serr = $serr . "rsts?";
            $nerr++;
        }
    }
    if( $rec[ $recno ]{RST_RCVD} )
    {
        if( !CheckRst( $rec[ $recno ]{RST_RCVD} ) )
        {
            $serr = $serr . "rstr?";
            $nerr++;
        }
    }
    if( $rec[ $recno ]{QSL_RCVD} )
    {
        if( !CheckQslr( $rec[ $recno ]{QSL_RCVD} ) )
        {
            $serr = $serr . "qslr?";
            $nerr++;
        }
    }
    if( $rec[ $recno ]{QSL_SENT} )
    {
        if( !CheckQsls( $rec[ $recno ]{QSL_SENT} ) )
        {
            $serr = $serr . "qsls?";
            $nerr++;
        }
    }
    if( $rec[ $recno ]{LAT} )
    {
        if( !CheckLoc( $rec[ $recno ]{LAT} ) )
        {
            $serr = $serr . "loc?";
            $nerr++;
        }
    }
    if( $rec[ $recno ]{LON} )
    {
        if( !CheckLoc( $rec[ $recno ]{LON} ) )
        {
            $serr = $serr . "loc?";
            $nerr++;
        }
    }
    if( $rec[ $recno ]{MY_LAT} )
    {
        if( !CheckLoc( $rec[ $recno ]{MY_LAT} ) )
        {
            $serr = $serr . "loc?";
            $nerr++;
        }
    }
    if( $rec[ $recno ]{MY_LON} )
    {
        if( !CheckLoc( $rec[$recno]{MY_LON} ) )
        {
            $serr = $serr . "loc?";
            $nerr++;
        }
    }
    if( $rec[ $recno ]{GRIDSQUARE} )
    {
        if( !CheckGrid( $rec[ $recno ]{GRIDSQUARE} ) )
        {
            $serr = $serr . "grid?";
            $nerr++;
        }
    }
    if( $rec[ $recno ]{MY_GRIDSQUARE} )
    {
        if( !CheckGrid( $rec[ $recno ]{MY_GRIDSQUARE} ) )
        {
            $serr = $serr . "grid?";
            $nerr++;
        }
    }
    if( $rec[ $recno ]{IOTA} )
    {
        if( !CheckIota( $rec[$recno]{IOTA} ) )
        {
            $serr = $serr . "iota?";
            $nerr++;
        }
    }
    if( $rec[ $recno ]{SAT_NAME} )
    {
        if( !CheckSat( $rec[$recno]{SAT_NAME} ) )
        {
            $serr = $serr . "sat?";
            $nerr++;
        }
    }
    if( $rec[ $recno ]{SAT_MODE} )
    {
        if( !CheckSatMode( $rec[ $recno ]{SAT_MODE} ) )
        {
            $serr = $serr . "smode?";
            $nerr++;
        }
    }
    if( exists $rec[ $recno ]{SOTA_REF} )
    {
        if( $rec[ $recno ]{SOTA_REF} )
        {
            if( !CheckSota( $rec[ $recno ]{SOTA_REF} ) )
            {
                $serr = $serr . "sota?";
                $nerr++;
            }
        }
    }
    if( exists $rec[ $recno ]{MY_SOTA_REF} )
    {
        if( $rec[ $recno ]{MY_SOTA_REF} )
        {
            if( !CheckSota( $rec[ $recno ]{MY_SOTA_REF} ) )
            {
                $serr = $serr . "sota?";
                $nerr++;
            }
        }
    }
    if( $rec[ $recno ]{POTA_REF} )
    {
        if( !CheckPota( $rec[ $recno ]{POTA_REF} ) )
        {
            $serr = $serr . "pota?";
            $nerr++;
        }
    }
    if( $rec[ $recno ]{MY_POTA_REF} )
    {
        if( !CheckPota( $rec[ $recno ]{MY_POTA_REF} ) )
        {
            $serr = $serr . "pota?";
            $nerr++;
        }
    }
    if( $rec[ $recno ]{WWFF_REF} )
    {
        if( !CheckWwff( $rec[ $recno ]{WWFF_REF} ) )
        {
            $serr = $serr . "wwff?";
            $nerr++;
        }
    }
    if( $rec[ $recno ]{MY_WWFF_REF} )
    {
        if( !CheckWwff( $rec[ $recno ]{MY_WWFF_REF} ) )
        {
            $serr = $serr . "wwff?";
            $nerr++;
        }
    }
    if( $opt_c )
    {
        if( %ctylist )
        {
            $found = 1;
            $partial = $rec[ $recno ]{CALL};

            print "-- try to find $partial from ctylist hash\n" if( $dbug );
            if( !exists $ctylist{ $rec[ $recno ]{CALL} } )
	    {
                $rec[ $recno ]{CALL} =~ /^(\w+)\/.+/;
		$partial = $1;

                $partial ="NoAncPfx" if( !$partial );

                print "-- try to find $partial from ctylist hash\n" if( $dbug );
                if( !exists $ctylist{ $partial } )
                {
                    $rec[ $recno ]{CALL} =~ /\/(\w+)$/;
		    $partial = $1;
		    if( $partial )
		    {
                        $partial = "Portable" if( ( uc $partial ) eq "P" );
                        $partial = "Mobile" if( ( uc $partial ) eq "M" );
                        $partial = "Maritime" if( ( uc $partial ) eq "MM" );
                        $partial = "Aeronautical" if( ( uc $partial ) eq "AM" );
                        $partial = "Alternative" if( ( uc $partial ) eq "A" );
                    }
		    else
		    {
                        $partial = "None";
                    }

                    if( !exists $ctylist{ $partial } )
		    {
                        $rec[ $recno ]{CALL} =~ /^(\w+\d\w)/;
                        $partial = $1;

                        print "-- try to find $partial from ctylist hash\n" if( $dbug );
                        if( !exists $ctylist{ $partial } )
                        {
                            $rec[ $recno ]{CALL} =~ /^(\w+\d).+/;
                            $partial = $1;

                            print "-- try to find $partial from ctylist hash\n" if( $dbug );
                            if( !exists $ctylist{ $partial } )
                            {
                                $rec[ $recno ]{CALL} =~ /^(\w+)\d.+/;
                                $partial = $1;

                                print "-- try to find $partial from ctylist hash\n" if( $dbug );
                                if( !exists $ctylist{ $partial } )
                                {
                                    $found = 0;	
                                }
                            }
                        }
                    }
                }
	    }

	    if( $found )
	    {
                $entries[ 3 ] = "Country Name: " . $ctylist{ $partial }[ 0 ];
                $entries[ 4 ] = "WAZ Zone: " . $ctylist{ $partial }[ 1 ];
                $entries[ 5 ] = "ITU Zone: " . $ctylist{ $partial }[ 2 ];
                $entries[ 6 ] = "Continent: " . $ctylist{ $partial }[ 3 ];
                $entries[ 10 ] = "Dxcc: " . $ctylist{ $partial }[ 4 ];
            }
	    else
	    {
                $entries[ 3 ] = "";
                $entries[ 4 ] = "";
                $entries[ 5 ] = "";
                $entries[ 6 ] = "";
                $entries[ 10 ] = "";
            }
        }
	else
	{
	    # Output format of dxcc command for each line:
            #
            #     ^ <- beginning position of the string
            #  0: Callsign: JJ1BDX
            #  1:
            #  2: Main Prefix:    JA
            #  3: Country Name:   Japan
            #  4: WAZ Zone:       25
            #  5: ITU Zone:       45
            #  6: Continent:      AS
            #  7: Latitude:       36.40
            #  8: Longitude:      -138.38
            #  9: UTC shift:      -9.0
            #
            #  I really hate positional parsing, but this is much better than
            #  parsing the entire output without setting line borders.

            $cntry = `dxcc $rec[$recno]{CALL}`;
            @entries = split('\n', $cntry);
            $entries[ 10 ] = "";
        }

        # maximum five (5) words in the country name
        if( $entries[3] =~ /^Country\sName:\s+([\w\.?\s]*)$/i )
        {
            $ctry = $1;
            if( ($ctry eq "Unknown") || ($ctry eq "") )
            {
                $serr = $serr . "call?";
                $nerr++;
            }
            else
            {
                if( $rec[ $recno ]{COUNTRY} )
                {
                    if( $rec[ $recno ]{COUNTRY} !~ /$ctry/i )
                    {
                        $serr = $serr . "cntry?";
                        $nerr++;
                    }
                }
                else
                {
                    $rec[$recno]{COUNTRY}=$ctry;
                }
                $cq = $1 if( $entries[4] =~ /^WAZ\sZone:\s+(\d{1,2})$/i );
                $cq = scalar( $cq );
                $itu = $1 if( $entries[5] =~ /^ITU\sZone:\s+(\d{1,2})$/i );
                $itu = scalar( $itu );
                $cont = $1 if( $entries[6] =~ /^Continent:\s+(\w\w)$/i );
                $dxcc = $1 if( $entries[ 10 ] =~ /Dxcc:\s+(\d{1,3})$/i );
                $dxcc = scalar( $dxcc );

                if( $rec[ $recno ]{CQZ} )
                {
                    if( $rec[ $recno ]{CQZ} != $cq )
                    {
                        $serr = $serr . "cq?";
                        $nerr++;
                    }
                }
                else
                {
                    $rec[ $recno ]{CQZ} = $cq;
                }

                if( $rec[ $recno ]{ITUZ} )
                {
                    if( $rec[ $recno ]{ITUZ} != $itu )
                    {
                        $serr = $serr . "itu?";
                        $nerr++;
                    }
                }
                else
                {
                    $rec[$recno]{ITUZ}=$itu;
                }

		if( $rec[ $recno ]{CONT} )
                {
                    if( $rec[ $recno ]{CONT} ne $cont )
                    {
                        $serr = $serr . "cont?";
                        $nerr++;
                    }
                }
                else
                {
                    $rec[ $recno ]{CONT} = $cont;
                }

                if( $rec[ $recno ]{DXCC} && $dxcc )
                {
                    if( $rec[ $recno ]{DXCC} != $dxcc )
                    {
                        $serr = $serr . "dxcc?";
                        $nerr++;
                    }
                }

            }

	    # check also STATION_CALLSIGN if exists
            if( $rec[ $recno ]{STATION_CALLSIGN} )
            {
                if( %ctylist )
                {
                    $found = 1;
                    $partial = $rec[ $recno ]{STATION_CALLSIGN};
                    print "-- try to find $partial from ctylist hash\n" if( $dbug );
                    if( !exists $ctylist{ $partial } )
                    {
                        $rec[ $recno ]{STATION_CALLSIGN} =~ /^(\w+)\/.+/;
                        $partial = $1;

                        $partial ="NoAncPfx" if( !$partial );
    
                        print "-- try to find $partial from ctylist hash\n" if( $dbug );
                        if( !exists $ctylist{ $partial } )
                        {
                            $rec[ $recno ]{STATION_CALLSIGN} =~ /\/(\w+)$/;
        		    $partial = $1;
        		    if( $partial )
        		    {
                                $partial = "Portable" if( ( uc $partial ) eq "P" );
                                $partial = "Mobile" if( ( uc $partial ) eq "M" );
                                $partial = "Maritime" if( ( uc $partial ) eq "MM" );
                                $partial = "Aeronautical" if( ( uc $partial ) eq "AM" );
                                $partial = "Alternative" if( ( uc $partial ) eq "A" );
                            }
        		    else
        		    {
                                $partial = "None";
                            }

			    if( !exists $ctylist{ $partial } )
			    {
                                $rec[ $recno ]{STATION_CALLSIGN} =~ /^(\w+\d\w)/;
                                $partial = $1;

                                print "-- try to find $partial from ctylist hash\n" if( $dbug );
                                if( !exists $ctylist{ $partial } )
                                {
                                    $rec[ $recno ]{STATION_CALLSIGN} =~ /^(\w+\d)/;
                                    $partial = $1;

                                    print "-- try to find $partial from ctylist hash\n" if( $dbug );
                                    if( !exists $ctylist{ $partial } )
                                    {
                                        $rec[ $recno ]{STATION_CALLSIGN} =~ /^(\w+)\d/;
                                        $partial = $1;

                                        print "-- try to find $partial from ctylist hash\n" if( $dbug );
                                        if( !exists $ctylist{ $partial } )
                                        {
                                            $found = 0;
                                        }
                                    }
                                }
                            }
                        }
        	    }

		    if( $found )
		    {
                        $entries2[ 3 ] = "Country Name: " . $ctylist{ $partial }[ 0 ];
                        $entries2[ 4 ] = "WAZ Zone: " . $ctylist{ $partial }[ 1 ];
                        $entries2[ 5 ] = "ITU Zone: " . $ctylist{ $partial }[ 2 ];
                        $entries2[ 6 ] = "Continent: " . $ctylist{ $partial }[ 3 ];
                        $entries[ 10 ] = "Dxcc: " . $ctylist{ $partial }[ 4 ];
                    }
		    else
		    {
                        $entries2[ 3 ] = "";
                        $entries2[ 4 ] = "";
                        $entries2[ 5 ] = "";
                        $entries2[ 6 ] = "";
                        $entries[ 10 ] = "";
                    }
                }
		else
		{
                    $cntry2 = `dxcc $rec[ $recno ]{STATION_CALLSIGN}`;
                    @entries2 = split('\n', $cntry2);
                    $entries[ 10 ] = "";
	        }

                # maximum five (5) words in the country name
                if( $entries2[3] =~ /^Country\sName:\s+([\w\.?\s]*)$/i )
                {
                    $ctry2 = $1;
                    if( ($ctry2 eq "Unknown") || ($ctry2 eq "") )
                    {
                        $serr = $serr . "mycall?";
                        $nerr++;
                    }
                    else
                    {
                        if( $rec[ $recno ]{MY_COUNTRY} )
                        {
                            if( $rec[ $recno ]{MY_COUNTRY} !~ /$ctry2/i )
                            {
                                $serr = $serr . "mycntry?";
                                $nerr++;
                            }
                        }
                        else
                        {
                            $rec[ $recno ]{MY_COUNTRY} = $ctry2;
                        }
                        $cq2 = $1 if( $entries2[4] =~ /^WAZ\sZone:\s+(\d{1,2})$/i );
                        $cq2 = scalar( $cq2 );
                        $itu2 = $1 if( $entries2[5] =~ /^ITU\sZone:\s+(\d{1,2})$/i );
                        $itu2 = scalar( $itu2 );
                        $cont2 = $1 if( $entries2[6] =~ /^Continent:\s+(\w\w)$/i );

                        $dxcc2 = $1 if( $entries[ 10 ] =~ /Dxcc:\s+(\d{1,3})$/i );
                        $dxcc2 = scalar( $dxcc2 );

			if( $rec[ $recno ]{MY_CQ_ZONE} )
                        {
                            if( $rec[ $recno ]{MY_CQ_ZONE} != $cq2 )
                            {
                                $serr = $serr . "mycq?";
                                $nerr++;
                            }
                        }
                        else
                        {
                            $rec[ $recno ]{MY_CQ_ZONE} = $cq2;
                        }
                        if( $rec[ $recno ]{MY_ITU_ZONE} )
                        {
                            if( $rec[ $recno ]{MY_ITU_ZONE} != $itu2 )
                            {
                                $serr = $serr . "myitu?";
                                $nerr++;
                            }
                        }
                        else
                        {
                            $rec[ $recno ]{MY_ITU_ZONE} = $itu2;
                        }

                        if( $rec[ $recno ]{MY_DXCC} && $dxcc2 )
                        {
                            if( $rec[ $recno ]{MY_DXCC} != $dxcc2 )
                            {
                                $serr = $serr . "mydxcc?";
                                $nerr++;
                            }
                        }

                    }
                }
            }
        }
    }

    return ( $nerr, $serr ); 

}


# calculate Julian day
# http://en.wikipedia.org/wiki/Julian_day
sub jday()
{
    my $qsod=shift; # QSO_DATE
    my $qsot=shift; # TIME_ON

    my $ye=substr $qsod,0,4;
    my $mo=substr $qsod,4,2;
    my $da=substr $qsod,6,2;
    my $hh=substr $qsot,0,2;
    my $mi=substr $qsot,2,2;
    my $se=0;

    $se=substr $qsot,4,2 if(length($qsot)==6);

    use integer;
    
    my $a=(14-$mo)/12;
    my $y=$ye+4800-$a;
    my $m=$mo+12*$a-3;

    my $jday=$da+(153*$m+2)/5+365*$y+$y/4-$y/100+$y/400-32045;
    
    no integer;
    
    $jday+=($hh-12.0)/24.0+$mi/1440.0+$se/86400.0;

    return $jday;
}

# test if valid date
sub CheckDate()
{
    my $date=shift;
    my $ok=0;
    my $year;
    my $month;
    my $day;

    if($date =~ /([1-2]\d\d\d)([0-1]\d)([0-3]\d)/)
    {
	$year=$1;
	$month=$2;
	$day=$3;

	if(($month==1)||($month==3)||($month==5)||($month==7)||($month==8)||($month==10)||($month==12))
	{
	    $ok=1 if(($day>=1)&&($day<=31));
	}
	if(($month==4)||($month==6)||($month==9)||($month==11))
	{
	    $ok=1 if(($day>=1)&&($day<=30));
	}
	if($month==2)
	{
	    $ok=1 if(($day>=1)&&($day<=29)&&($year%2==0));
	    $ok=1 if(($day>=1)&&($day<=28)&&($year%2!=0));
	}
	$ok=0 if(($year<1930)||($year>2025));
    }

    return $ok;
}

# test if valid UTC
sub CheckUtc()
{
    my $utc = shift;
    my $ok = 0;
    my $h = 0;
    my $m = 0;
    my $s = 0;

    if( $utc =~ /([0-2][0-9])([0-5][0-9])([0-5][0-9])?/ )
    {
        $h = $1;
        $m = $2;
        $s = $3 if( $3 );

        if( ( $h >= 0 ) && ( $h <= 23 ) && ( $m >= 0 ) && ( $m <= 59) )
        {
            if($s)
            {
                $ok = 1 if( ( $s >= 0 ) && ( $s <= 59 ) );
            }
            else
            {
                $ok = 1;
            }
        };
    }

    return $ok;
}

# test if valid RS(T)
sub CheckRst()
{
    my $rst = shift;
    my $ok = 0;

    $ok = 1 if( $rst =~ /^[1-5][1-9][1-9]?.*/ );
    $ok = 1 if( $rst =~ /[+-]?[0-9][0-9]?.*/ );

    return $ok;
}

# test if band looks ok
sub CheckBand()
{
    my $band=shift;
    my $ok=0;

    $band=uc $band;
    foreach(@bands) 
    {
	$ok=1 if($band eq $_); 	
    }

    return $ok;
}

# test if mode looks ok
sub CheckMode()
{
    my $mode=shift;
    my $ok=0;

    $mode=uc $mode;

    foreach(@modes) 
    {
	$ok=1 if($mode eq $_); 	
    }

    return $ok;
}

# test if morse key type looks ok
sub CheckMorsekey()
{
    my $morse = shift;
    my $ok = 0;

    $morse = uc $morse;

    foreach(@morsekeytypes)
    {
	$ok=1 if($morse eq $_);
    }

    return $ok;
}

# test if submode looks ok
sub CheckSubmode()
{
    my $submode=shift;
    my $ok=0;

    $submode=uc $submode;

    $ok=1 if( exists $submodes{$submode} );

    return $ok;
}



# test if propagation mode looks ok
sub CheckPmode()
{

    my $pmode=shift;
    my $ok=0;
    $pmode=uc $pmode;

    foreach(@pmodes) 
    {
	$ok=1 if($pmode eq $_); 	
    }

    return $ok;
}

# QSL_RCVD needs to be Y, N, R, I or V
sub CheckQslr()
{
    my $q=shift;
    my $ok=0;
    $q=uc $q;

    $ok=1 if(($q eq "Y")||($q eq "N")||($q eq "R")||($q eq "I")||($q eq "V"));

    return $ok;
} 

# QSL_SENT needs to be Y, N, R, Q or I
sub CheckQsls()
{
    my $q=shift;
    my $ok=0;
    $q=uc $q;

    $ok=1 if(($q eq "Y")||($q eq "N")||($q eq "R")||($q eq "Q")||($q eq "I"));

    return $ok;
} 

# check location XDDD MM.MMM
sub CheckLoc()
{
    my $l=shift;
    $l=uc $l;
    my $ok=0;

    if($l =~ /^([EWNS])(\d\d\d)\s(\d\d\.\d\d\d)/)
    {
	$ok=1 if(($2>=0)&&($2<=180)&&($3>=0)&&($3<=59.999));
    };

    return $ok;
}

# check IOTA CC-XXX
sub CheckIota()
{
    my $l = shift;
    $l = uc $l;
    my $ok = 0;

    $ok = 1 if( $l =~ /^(NA|SA|EU|AF|OC|AS|AN)-(\d\d\d)/ );

    return $ok;
}

# check POTA reference xxxx-nnnnn[@yyyyyy]
sub CheckPota()
{
   my $l = shift;
   $l = uc $l;
   my $ok = 0;

   if( $l =~ /^\w{1,4}-\d{4,5}(@(\w{2})-\w{2,3})?/ )
   {
	   #      print "match $1 and $2\n";
      if( $1 )
      {
         $ok = 1 if( $2 =~ /^(AD|AE|AF|AG|AI|AL|AM|AO|AQ|AR|AS|AT|AU|AW|AX|AZ|BA|BB|BD|BE|BF|BG|BH|BI|BJ|BL|BM|BN|BO|BQ|BR|BS|BT|BV|BW|BY|CA|CC|CD|CF|CG|CI|CK|CL|CM|CN|CO|CR|CU|CV|CW|CX|CY|CZ|DE|DJ|DK|DM|DO|DZ|EC|EE|EG|EH|ER|ES|ET|FI|FJ|FK|FO|FR|GA|GB|GD|GE|GF|GG|GH|GI|GL|GM|GN|GP|GQ|GR|GS|GT|GU|GW|GY|HK|HM|HN|HR|HT|HU|ID|IE|IL|IM|IN|IO|IQ|IR|IS|IT|JE|JM|JO|JP|KE|KG|KH|KI|KM|KN|KP|KR|KW|KY|KZ|LA|LB|LC|LI|LK|LR|LS|LT|LU|LV|LY|MA|MC|MD|ME|MF|MG|MH|MK|ML|MM|MN|MO|MP|MQ|MR|MS|MT|MU|MV|MW|MY|MZ|NA|NC|NE|NF|NG|NI|NL|NO|NP|NR|NU|NZ|OM|PA|PE|PF|PG|PH|PK|PL|PM|PN|PR|PS|PT|PW|PY|QA|RE|RO|RS|RU|RW|SA|SB|SC|SD|SE|SG|SH|SI|SJ|SK|SL|SM|SN|SO|SR|SS|ST|SV|SX|SZ|TC|TD|TF|TG|TH|TJ|TK|TL|TM|TN|TO|TR|TT|TV|TW|TZ|UA|UG|UM|US|UY|UZ|VA|VC|VE|VG|VI|VN|VU|WF|WS|YE|YT|ZA|ZM|ZW)/ );
      }
      else
      {
         $ok = 1 if( $l !~ /@/ )
      }
   }

   return $ok;
}

# check SOTA C/CC-XXX
sub CheckSota()
{
    my $l=shift;
    $l=uc $l;
    my $ok=0;

    $ok=1 if($l =~ /^(3Y|4O|4X|5B|8P|9A|9H|9M2|9M6|9V|A6|BV|C3|CT|CT3|CU|CX|DL|DM|DU2|DU3|DU4|DUC|E5|E7|EA1|EA2|EA3|EA4|EA5|EA6|EA7|EA8|EA9|EI|ES|F|FG|FH|FK|FL|FM|FP|FR|G|GD|GI|GJ|GM|GU|GW|HA|HB|HB0|HI|HL|HR|I|IA|IS0|JA|JA5|JA6|JA8|JW|JX|K0M|KH0|KH2|KH6|KLA|KLF|KLS|KP4|LA|LUD|LUH|LUM|LUP|LUQ|LUU|LUV|LUY|LX|LY|LZ|OD|OE|OH|OK|OM|ON|OY|OZ|PA|PP1|PP2|PP5|PP6|PP7|PQ2|PR8|PS7|PS8|PT2|PT7|PY1|PY2|PY3|PY4|PY5|PY6|PYF|PYT|R3|R9U|S5|S7|SM|SP|SV|TF|TI|TK|UT|V5|VE1|VE2|VE3|VE4|VE5|VE6|VE7|VE9|VK1|VK2|VK3|VK4|VK5|VK6|VK7|VK8|VK9|VKH|VKM|VO1|VO2|VP8|VR|VY1|VY2|W0C|W0D|W0I|W0M|W0N|W1|W2|W3|W4A|W4C|W4G|W4K|W4T|W4V|W5A|W5M|W5N|W5O|W5T|W6|W7A|W7I|W7M|W7N|W7O|W7U|W7W|W7Y|W8M|W8O|W8V|W9|XE1|XE2|XE3|XF4|YBE|YBJ|YL|YO|YU|Z3|ZB2|ZD|ZL1|ZL3|ZL7|ZL8|ZL9|ZS|ZS8)\/(\w\w)-(\d\d\d)/);

    return $ok;
}

# check WWFF reference
sub CheckWwff()
{
    my $l = shift;
    my $ok = 0;

    $ok = 1 if( $l =~ /^\w{1,4}FF-\d{4}/ );
    return $ok;
}

# check CQ-zone and continent CC-X or CC-XX
sub CheckCQZ()
{
    my $l=shift;
    $l=uc $l;
    my $ok=0;

    if($l =~ /^(NA|SA|EU|AF|OC|AS|AN)-(\d{1,2})/)
    {
	$ok=1 if(($2>=1)&&($2<=40));
    }

    return $ok;
}

# check ITU-zone
sub CheckITUZ()
{
    my $l=shift;
    my $ok=0;

    if($l =~ /^\d{1,2}$/)
    {
	$ok=1 if(($l>=1)&&($l<=75));
    }

    return $ok;
}

# check satellite name
sub CheckSat()
{

    my $sat=shift;
    my $ok=0;
    $sat=uc $sat;

    foreach(@sats) 
    {
	$ok=1 if($sat eq $_); 	
    }

    return $ok;
}

# check satellite mode U/V etc.
# 15m H, 10m T, 2m V, 70cm U, 24cm L, 13cm S, 6cm C, 3cm X, 1.5cm K
sub CheckSatMode()
{

    my $s=shift;
    my $ok=0;
    $s=uc $s;

    $ok=1 if($s =~ /^(H|T|V|U|L|S|C|X|K)\/(H|T|V|U|L|S|C|X|K)/);

    return $ok;
}

# check rig name
sub CheckRig()
{
    my $rig=shift;
    my $ok=0;

    $rig=uc $rig;
    foreach(@rigs) 
    {
	$ok=1 if($rig eq $_); 	
    }

    return $ok;
}

# check QSL via
sub CheckVia()
{
    my $qslvia=shift;
    my $via="";

    if($qslvia =~ /^via(.+)/i)
    {
	$via=$1;
    }

    return $via;
}

# check power
sub CheckPwr()
{
    my $pwrs=shift;
    my $pwr=0;

    if($pwrs =~ /^(\d{1,3}(\.\d{1,3})?)W/i)
    {
	$pwr=$1;
    }

    return $pwr;
}

# check Maidenhead grid square AA00BB11CC
# http://en.wikipedia.org/wiki/Maidenhead_Locator_System
sub CheckGrid
{
    my $g=shift;
    $g=uc $g;
    my $ok=0;

    if($g =~ /^[A-R][A-R](.*)/)
    {
	if($1)
	{
	    if($1 =~ /^\d\d(.*)/)
	    {
		if($1)
		{
		    if($1 =~ /^[A-X][A-X](.*)/)
		    {
			if($1)
			{
			    if($1 =~ /^\d\d(.*)/)
			    {
				if($1)
				{
				    $ok=1 if($1 =~ /^[A-X][A-X]/)
				}
				else
				{
				    $ok=1;
				}
			    } 
			}
			else
			{
			    $ok=1;
			}
		    }
		}
		else
		{
		    $ok=1;
		}		
	    }
	}
	else
	{
	    $ok=1;
	}
    }

    return $ok;
}

# convert grid square to location in the center of the square
# XDDD MM.MMM
sub Grid2Loc 
{
    my $g=shift;
    $g=uc $g;
    my $lon=0;
    my $lat=0;
    my $lons="W";
    my $lats="S";

    if($g =~ /([A-R])([A-R])(.*)/)
    {
	$lon=20*(ord($1)-ord('A'));
	$lat=10*(ord($2)-ord('A'));
	if($3)
	{
	    if($3 =~ /(\d)(\d)(.*)/)
	    {
		$lon+=2*$1;
		$lat+=$2;
		if($3)
		{
		    if($3 =~ /([A-X])([A-X])(.*)/)
		    {
			$lon+=((ord($1)-ord('A'))/12);
			$lat+=((ord($2)-ord('A'))/24);
			if($3 =~ /(\d)(\d)(.*)/)
			{
			    $lon+=$1/120;
			    $lat+=$2/240;
			    if($3 =~ /([A-X])([A-X])/)
			    {
				$lon+=(ord($1)-ord('A')+0.5)/(24*120);
				$lat+=(ord($2)-ord('A')+0.5)/(24*240);
			    }
			    else
			    {
				$lon+=0.5/120;
				$lat+=0.5/240;
			    }
			}
			else
			{
			    $lon+=0.5/12;
			    $lat+=0.5/24;
			}
		    }
		}
		else
		{
		    $lon+=1;
		    $lat+=0.5;
		}
	    }
	}
	else
	{
	    $lon+=10;
	    $lat+=5;
	}
    }
 
    $lon-=180;
    $lat-=90;
    $lons="E" if($lon>=0);
    $lats="N" if($lat>=0);
    $lon=abs $lon;
    $lat=abs $lat;
    $lons=$lons.sprintf "%03d ", (int $lon);
    $lats=$lats.sprintf "%03d ", (int $lat);
    $lons=$lons.sprintf "%06.3f", (60*($lon-(int $lon)));
    $lats=$lats.sprintf "%06.3f", (60*($lat-(int $lat)));

    return ($lons,$lats);
}

# convert frequency in MHz's to band
sub Freq2band()
{

    my $freq=shift;
    my $band="";
    my $i;

    for($i=0;$i<@bands;$i++)
    {
	$band=$bands[$i] if(($freq>=$bandl[$i])&&($freq<=$bandu[$i]));
    }
    
    return $band;
}

# convert band to frequency
sub Band2freq()
{
    my $b=uc shift;
    my $f="";
    my $i;

    for($i=0;$i<@bands;$i++)
    {
	$f=$bandf[$i] if($b eq $bands[$i]);
    }

    return $f;
}

# date to csv string
sub date2csv
{
    my $d=shift;
    my $s=(substr $d,6,2)."/".(substr $d,4,2)."/".(substr $d,0,4);

    return $s;
}

# time to csv string
sub time2csv
{
    my $t=shift;
    my $s=(substr $t,0,2).":".(substr $t,2,2);
    $s=$s.":".(substr $t,4,2) if(length($t)==6);

    return $s;
}


# print info on the log file
sub PrintInfo
{
    my $nqso = shift;
    my $bnd;
    my $mode;
    my $submode;
    my $ctry;
    my $dxx;
    my $lonlat;
    my $lond;
    my $latd;
    my $station;
    my $prop;
    my $cnty = "";
    my $state = "";
    my $ndx = 0;
    my $ndxc = 0;
    my $ncq = 0;
    my $nitu = 0;
    my $ncont = 0;
    my $ngrid = 0;
    my $niota = 0;
    my $nsota = 0;
    my $nlonlat = 0;
    my $nstation = 0;
    my $nsubmodes = 0;
    my $npota = 0;
    my $nwwff = 0;
    my $ncnty = 0;
    my $nstate = 0;

    print "STATION:\n" if( $opt_v );
    foreach $dxx (sort keys %stationstat)
    {
	print "$dxx " if( $opt_v || $opt_q eq 'stations' );
	$nstation++;
    }

    print "\nDXCC:\n" if( $opt_v );
    foreach $dxx (sort {$a <=> $b} keys %dxstat)
    {
	print "$dxx " if( $opt_v || $opt_q eq 'dxcc' );
	$ndx++;
    }

    foreach $dxx (keys %dxstatc)
    {
	$ndxc++;
    }
    print "\nCQ ZONE:\n" if( $opt_v );
    foreach $dxx (sort {$a <=> $b} keys %cqzstat)
    {
	print "$dxx " if( $opt_v || $opt_q eq 'cqz' );
	$ncq++;
    }
    print "\nITU ZONE:\n" if( $opt_v );
    foreach $dxx (sort {$a <=> $b} keys %ituzstat)
    {
	print "$dxx " if( $opt_v || $opt_q eq 'ituz' );
	$nitu++;
    }
    print "\nCONTINENTS:\n" if( $opt_v );
    foreach $dxx (sort keys %contstat)
    {
	print "$dxx " if( $opt_v || $opt_q eq 'cont' );
	$ncont++;
    }

    print "\nSTATES FOR DXCC:" if( $opt_v );
    foreach $dxx (sort {$a <=> $b} keys %dxstat)
    {
        print "\n$dxx: " if( $opt_v || $opt_q eq 'state' );
        foreach $state ( sort keys %statestat )
        {
            if( $statestat { $state } eq $dxx )
	    {
                if( $opt_v || $opt_q eq 'state' )
		{
                    print (substr $state, 0, -4);
                    print " ";
                }
                $nstate++; 
            }
        }
    }

    print "\nCOUNTIES FOR DXCC:" if( $opt_v );
    foreach $dxx (sort {$a <=> $b} keys %dxstat)
    {
        print "\n$dxx: " if( $opt_v || $opt_q eq 'county' );
        foreach $cnty ( sort keys %cntystat )
        {
            if( $cntystat{ $cnty } eq $dxx )
	    {
                if( $opt_v || $opt_q eq 'county' )
		{
                    print (substr $cnty, 0, -4);
                    print " ";
                }
                $ncnty++; 
            }
        }
    }

    print "\nIOTA:\n" if( $opt_v );
    foreach $dxx (sort keys %iotastat)
    {
	print "$dxx " if( $opt_v || $opt_q eq 'iota' );
	$niota++;
    }
    if(exists $rec[0]{SOTA_REF})
    {
	print "\nSOTA:\n" if( $opt_v );
	foreach $dxx (sort keys %sotastat)
	{
	    print "$dxx " if( $opt_v || $opt_q eq 'sota' );
	    $nsota++;
	}
    }
    print "\nPOTA:\n" if( $opt_v );
    foreach $dxx (sort keys %potastat)
    {
	print "$dxx " if( $opt_v || $opt_q eq 'pota' );
	$npota++;
    }
    print "\nWWFF:\n" if( $opt_v );
    foreach $dxx (sort keys %wwffstat)
    {
	print "$dxx " if( $opt_v || $opt_q eq 'wwff' );
	$nwwff++;
    }
    print "\nGRID SQUARES:\n" if($opt_v);
    foreach $dxx (sort keys %gridstat)
    {
	print "$dxx " if( $opt_v || $opt_q =~ /grid[s68]/ );
	$ngrid++;
    }
    print "\n" if( $opt_v );

    $nsubmodes = scalar keys %submodstat;

    if( !$opt_q )
    {
	print "$nqso QSOs $sqsos shown, $ndx DXCCs with $ndxc confirmed\n";
        print "$ncq CQ zones, $nitu ITU zones, $ncont continents, $niota IOTAs";
        print ", $ncnty counties, $nstate states";
        print ", $npota POTAs, $nwwff WWFFs";
        print ", $nsota SOTAs" if(exists $rec[0]{SOTA_REF});
	print " and $ngrid grid squares\n";
	print "$nqsls QSLs sent, $nqslr QSLs received, $neqsls eQSL sent, ";
	print "$neqslr received, $nlqsls LoTW QSL sent, $nlqslr received\n";
	print "dates ";
	print date2csv( $fdate );
	print " ";
	print time2csv( $ftime );
	print " - ";
	print date2csv( $ldate );
	print " ";
	print time2csv( $ltime );
	print "\n";
	if( $lstqsl ne "11000101" )
	{
	    print "last QSL ";
	    print date2csv( $lstqsl );
	    print "\n";
	}
	if( $lsteqsl ne "11000101" )
	{
	    print "last eQSL ";
	    print date2csv( $lsteqsl );
	    print "\n";
	}
	if( $lstlqsl ne "11000101" )
	{
	    print "last LoTW QSL ";
	    print date2csv( $lstlqsl );
	    print "\n";
	}
    }

    if( $opt_v && !$opt_q )
    {
	foreach $bnd ( @bands )
	{
	    print (sprintf "%-8.8s   %5d\n", $bnd, $bndstat{$bnd}) if(exists $bndstat{$bnd});
	}
        foreach $mode ( sort keys %modstat )
        {
            print (sprintf "%-8.8s   %5d\n", $mode, $modstat{$mode});
        }
        if( $nsubmodes > 0 )
        {
            print "$nsubmodes submodes\n";
            foreach $submode (sort keys %submodstat)
            {
                print (sprintf "%-8.8s   %5d\n", $submode, $submodstat{$submode});
            }
        }
        foreach $prop (sort keys %propstat)
	{
	    print (sprintf "%-8.8s   %5d\n", $prop, $propstat{$prop});
	}
    }

    if($opt_q eq 'bands')
    {
	foreach $bnd (@bands)
	{
	    print $bnd." ".$bndstat{$bnd}." " if(exists $bndstat{$bnd});
	}
	print "\n";
    }
    if($opt_q eq 'modes')
    {
	foreach $mode (sort keys %modstat)
	{
	    print $mode." ".$modstat{$mode}." ";
	}
	print "\n";
    }
    if($opt_q eq 'submodes')
    {
        foreach $submode (sort keys %submodstat)
	{
	    print $submode." ".$submodstat{$submode}." ";
	}
	print "\n";
    }
    if($opt_q eq 'props')
    {
	foreach $prop (sort keys %propstat)
	{
	    print $prop." ".$propstat{$prop}." ";
	}
	print "\n";
    }
    if($opt_q eq 'country')
    {
	foreach $ctry (sort keys %ctrystat)
	{
	    print $ctry." ".$ctrystat{$ctry}."\n";
	}
	print "\n";
    }
    if($opt_q eq 'lonlat')
    {
	foreach $lonlat (sort keys %lonlatstat)
	{
	    print "$lonlat\n";
	}
    }
    if($opt_q eq 'lonlatd')
    {
	foreach $lonlat (sort keys %lonlatstat)
	{
	    $lonlat = uc $lonlat;
	    $lonlat =~ /([EW])(\d\d\d)\s(\d\d)\.(\d\d\d)\s([SN])(\d\d\d)\s(\d\d)\.(\d\d\d)/;
	    $lond=$2+($3+$4/1000)/60;
	    $lond=-$lond if($1 eq 'W');
	    $latd=$6+($7+$8/1000)/60;
	    $latd=-$latd if($5 eq 'W');

	    print "$lond $latd\n";
	}
    }
    if($opt_q eq 'station')
    {
	foreach $station (sort keys %stationstat)
	{
	    print "$station ";
	}
	print "\n";
    }

# print query results
    if( $opt_q )
    {
	my $p;
	my $l;
	my @qpars = split /,/, $opt_q;

	foreach $p ( @qpars )
	{
	    print $fdate." ".$ftime." " if($p eq 'firstqso');
	    print $lstqsl." " if($p eq 'lastqsl');
	    print $lsteqsl." " if($p eq 'lasteqsl');
	    print $lstlqsl." " if($p eq 'lastlqsl');
	    print $ldate." ".$ltime." " if($p eq 'lastqso');
	    if($p eq 'llastqsl')
	    {
		$l=(substr $lstlqsl,0,4)."-".(substr $lstlqsl,4,2)."-";
		$l=$l.(substr $lstlqsl,6,2)." ";
		print $l;
	    }
	    if($p eq 'llastqso')
	    {
		$l=(substr $ldate,0,4)."-".(substr $ldate,4,2)."-";
		$l=$l.(substr $ldate,6,2)." ";
		print $l;
	    } 
	    if($p eq 'llastqsot')
	    {
		$l=(substr $ltime,0,2).":".(substr $ltime,2,2).":";
		$l=$l.(substr $ltime,4,2)." ";
		print $l;
	    } 
	    print "$ncont " if( $p eq 'ncont' );
	    print "$ncq " if( $p eq 'ncq' );
            print "$ndx " if( $p eq 'ndxcc' );
            print "$ncnty " if( $p eq 'ncnty' );
            print "$nstate " if( $p eq 'nstate' );
            print "$neqslr " if( $p eq 'neqslr' );
	    print "$neqsls " if( $p eq 'neqsls' );
	    print "$ngrid " if( $p eq 'ngrid' );
	    print "$nitu " if( $p eq 'nitu' );
	    print "$nlqslr " if( $p eq 'nlqslr' );
	    print "$nlqsls " if( $p eq 'nlqsls' );
	    print "$nqslr " if( $p eq 'nqslr' );
	    print "$nqsls " if( $p eq 'nqsls' );
	    print "$nqso " if( $p eq 'nqso' );
	    print "$nerr " if( $p eq 'nerr' );
	    print "$niota " if($p eq 'niota');
            print "$nsota " if( $p eq 'nsota' );
            print "$npota " if( $p eq 'npota' );
            print "$nwwff " if( $p eq 'nwwff' );
	    print "$nstation " if( $p eq 'nstation' );
	}
	print "\n";
    }
}


# print version
sub printversion 
{
    my $x;
    my $n;
    my $m;

    print "adifmerg version $verno, ";
    print "Jaakko Koivuniemi, OH7BF\n";
    if($opt_v)
    {
	print "Known records ADIF $averno\n";
	foreach $x (sort keys %typ)
	{
	    print $x." ";
	}
	print "\n";
        $n = scalar @modes;
        $m = scalar @modesimport;
        $n -= $m;
        print "$n known modes and $m import only modes\n";
	foreach $x (@modes)
	{
	    print $x." ";
	}
	print "\n";
        $n = scalar keys %submodes;
        print "$n known submodes\n";
	foreach $x (sort keys %submodes)
	{
	    print $x." ";
	}
	print "\n";
        $n=@pmodes;
	print "$n known propagation modes\n";
	foreach $x (@pmodes)
	{
	    print $x." ";
	}
	print "\n";
	$n=@sats;
	print "$n known satellites\n";
	foreach $x (@sats)
	{
	    print $x." ";
	}
	print "\n";
	$n=@bands;
	print "$n known bands\n";
	foreach $x (@bands)
	{
	    print $x." ";
	}
	print "\n";
	$n=@rigs;
	print "$n known rigs\n";
	foreach $x (sort @rigs)
	{
	    print $x." ";
	}
	print "\n";
        $n = scalar keys %cconv;
        print "$n known csv records\n";
	foreach $x (sort keys %cconv)
	{
	    print $x.",";
	}
	print "\n";

    }

    exit;
}

# print usage message
sub printusage 
{    
    print "usage: adifmerg -f file [-m file|-M file] [-b] [-d] [-c] [-i] [-u] [-a award] [-o|-s|-x|-X|-l|-L|-e|-C file|T -file] [-A rec] [-D rec] [-S rec] [-F rec] [-R rec] [-t min] [-p script] [-q query] [-Q all|requested] [-2] [-h] [-N] [-v] [-V]\n";
    exit;
}

