1 // ----------------------------------------------------------------------------
2 // Digital Modem Program for the Fast Light Toolkit
3 //
4 // Copyright 2006-2010, Dave Freese, W1HKJ
5 // Copyright 2007-2010, Stelios Bounanos, M0GLD
6 //
7 // This file is part of fldigi.
8 //
9 // Fldigi is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation, either version 3 of the License, or
12 // (at your option) any later version.
13 //
14 // Fldigi is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with fldigi.  If not, see <http://www.gnu.org/licenses/>.
21 //
22 // Please report all bugs and problems to fldigi-devel@lists.sourceforge.net.
23 // ----------------------------------------------------------------------------
24 
25 #include <config.h>
26 
27 //++++++++++++++++++
28 #include <FL/Fl_Scroll.H>
29 extern Fl_Scroll       *wefax_pic_rx_scroll;
30 
31 #include <iostream>
32 #include <iomanip>
33 #include <sstream>
34 #include <cstdlib>
35 #include <getopt.h>
36 #include <sys/types.h>
37 
38 #if !defined(__WOE32__) && !defined(__APPLE__)
39 #  include <sys/ipc.h>
40 #  include <sys/msg.h>
41 #endif
42 
43 #ifdef __MINGW32__
44 #  include "compat.h"
45 #endif
46 
47 #include <sys/stat.h>
48 
49 #if HAVE_SYS_UTSNAME_H
50 #  include <sys/utsname.h>
51 #endif
52 
53 #include <unistd.h>
54 
55 #include <exception>
56 #include <signal.h>
57 #include <locale.h>
58 
59 #include <FL/Fl.H>
60 #include <FL/Enumerations.H>
61 #include <FL/Fl_Window.H>
62 #include <FL/Fl_Shared_Image.H>
63 #include <FL/x.H>
64 #ifdef __MINGW32__
65 #  define dirent fl_dirent_no_thanks
66 #endif
67 #include <FL/filename.H>
68 
69 #ifdef __WOE32__
70 #	if FLDIGI_FLTK_API_MAJOR == 1 && FLDIGI_FLTK_API_MINOR < 3
71 #		undef dirent
72 #		include <dirent.h>
73 #	endif
74 #else
75 #	include <dirent.h>
76 #endif
77 
78 
79 #include "gettext.h"
80 #include "main.h"
81 #include "waterfall.h"
82 #include "trx.h"
83 #include "soundconf.h"
84 #include "fl_digi.h"
85 #include "rigio.h"
86 #include "globals.h"
87 #include "confdialog.h"
88 #include "rxmon.h"
89 #include "configuration.h"
90 #include "macros.h"
91 #include "status.h"
92 #include "fileselect.h"
93 #include "timeops.h"
94 #include "debug.h"
95 #include "pskrep.h"
96 #include "notify.h"
97 #include "logbook.h"
98 #include "dxcc.h"
99 #include "newinstall.h"
100 #include "Viewer.h"
101 #include "kmlserver.h"
102 #include "data_io.h"
103 #include "maclogger.h"
104 #include "psm/psm.h"
105 #include "fd_logger.h"
106 #include "n3fjp_logger.h"
107 #include "dx_cluster.h"
108 #include "dx_dialog.h"
109 #include "record_loader.h"
110 
111 #if USE_HAMLIB
112 	#include "rigclass.h"
113 #endif
114 #include "rigsupport.h"
115 
116 #include "log.h"
117 #include "qrunner.h"
118 #include "stacktrace.h"
119 #include "xmlrpc.h"
120 #include "icons.h"
121 #include "nullmodem.h"
122 #include "spectrum_viewer.h"
123 #include "contest.h"
124 #include "counties.h"
125 
126 #include "cmedia.h"
127 
128 #if BENCHMARK_MODE
129 	#include "benchmark.h"
130 #endif
131 
132 using namespace std;
133 
134 string appname;
135 
136 string scDevice[2];
137 
138 string BaseDir = "";
139 string HomeDir = "";
140 string RigsDir = "";
141 string ScriptsDir = "";
142 string PalettesDir = "";
143 string LogsDir = "";
144 string PicsDir = "";
145 string AvatarDir = "";
146 string HelpDir = "";
147 string MacrosDir = "";
148 string WrapDir = "";
149 string TalkDir = "";
150 string TempDir = "";
151 string DebugDir = "";
152 string LoTWDir = "";
153 string KmlDir = "";
154 string PskMailDir = "";
155 string AnalysisDir = "";
156 string FMTDir = "";
157 
158 string NBEMS_dir = "";
159 string NBEMS_dir_default = "";
160 string DATA_dir = "";
161 string ARQ_dir = "";
162 string ARQ_files_dir = "";
163 string ARQ_recv_dir = "";
164 string ARQ_send = "";
165 string WRAP_dir = "";
166 string WRAP_recv_dir = "";
167 string WRAP_send_dir = "";
168 string WRAP_auto_dir = "";
169 string ICS_dir = "";
170 string ICS_msg_dir = "";
171 string ICS_tmp_dir = "";
172 
173 string FLMSG_dir = "";
174 string FLMSG_dir_default = "";
175 string FLMSG_WRAP_dir = "";
176 string FLMSG_WRAP_recv_dir = "";
177 string FLMSG_WRAP_send_dir = "";
178 string FLMSG_WRAP_auto_dir = "";
179 string FLMSG_ICS_dir = "";
180 string FLMSG_ICS_msg_dir = "";
181 string FLMSG_ICS_tmp_dir = "";
182 
183 string PskMailFile;
184 string ArqFilename;
185 string xmlfname;
186 
187 PTT		*push2talk = (PTT *)0;
188 #if USE_HAMLIB
189 Rig		*xcvr = (Rig *)0;
190 #endif
191 
192 bool tlfio = false;
193 cLogfile	*logfile = 0;
194 cLogfile	*Maillogfile = (cLogfile *)0;
195 FILE	*server;
196 FILE	*client;
197 bool	mailserver = false, mailclient = false, arqmode = false;
198 static bool show_cpucheck = false;
199 static bool iconified = false;
200 
201 string option_help, version_text, build_text;
202 
203 qrunner *cbq[NUM_QRUNNER_THREADS];
204 
205 void arqchecks(void);
206 void generate_option_help(void);
207 int parse_args(int argc, char **argv, int& idx);
208 void generate_version_text(void);
209 void debug_exec(char** argv);
210 void set_platform_ui(void);
211 double speed_test(int converter, unsigned repeat);
212 static void setup_signal_handlers(void);
213 static void checkdirectories(void);
214 
215 static void arg_error(const char* name, const char* arg, bool missing);
216 static void fatal_error(string);
217 
218 /*
219 from: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx
220 
221 BOOL WINAPI CreateProcess(
222   _In_opt_    LPCTSTR               lpApplicationName,
223   _Inout_opt_ LPTSTR                lpCommandLine,
224   _In_opt_    LPSECURITY_ATTRIBUTES lpProcessAttributes,
225   _In_opt_    LPSECURITY_ATTRIBUTES lpThreadAttributes,
226   _In_        BOOL                  bInheritHandles,
227   _In_        DWORD                 dwCreationFlags,
228   _In_opt_    LPVOID                lpEnvironment,
229   _In_opt_    LPCTSTR               lpCurrentDirectory,
230   _In_        LPSTARTUPINFO         lpStartupInfo,
231   _Out_       LPPROCESS_INFORMATION lpProcessInformation
232 );
233 
234 Parameters
235 
236 lpApplicationName [in, optional]
237 
238   The name of the module to be executed. This module can be a Windows-based
239   application. It can be some other type of module (for example, MS-DOS or OS/2)
240   if the appropriate subsystem is available on the local computer.
241 
242   The string can specify the full path and file name of the module to execute
243   or it can specify a partial name. In the case of a partial name, the function
244   uses the current drive and current directory to complete the specification.
245   The function will not use the search path. This parameter must include the
246   file name extension; no default extension is assumed.
247 
248   The lpApplicationName parameter can be NULL. In that case, the module name must
249   be the first white space–delimited token in the lpCommandLine string. If you are
250   using a long file name that contains a space, use quoted strings to indicate where
251   the file name ends and the arguments begin; otherwise, the file name is ambiguous.
252   For example, consider the string "c:\program files\sub dir\program name".
253   This string can be interpreted in a number of ways. The system tries to interpret
254   the possibilities in the following order:
255 
256      c:\program.exe files\sub dir\program name
257      c:\program files\sub.exe dir\program name
258      c:\program files\sub dir\program.exe name
259      c:\program files\sub dir\program name.exe
260 
261   If the executable module is a 16-bit application, lpApplicationName should be NULL,
262   and the string pointed to by lpCommandLine should specify the executable module as
263   well as its arguments.
264 
265   To run a batch file, you must start the command interpreter; set lpApplicationName
266   to cmd.exe and set lpCommandLine to the following arguments: /c plus the name of the
267   batch file.
268 
269 lpCommandLine [in, out, optional]
270 
271   The command line to be executed. The maximum length of this string is 32,768
272   characters, including the Unicode terminating null character. If lpApplicationName
273   is NULL, the module name portion of lpCommandLine is limited to MAX_PATH characters.
274 
275   The Unicode version of this function, CreateProcessW, can modify the contents of this
276   string. Therefore, this parameter cannot be a pointer to read-only memory (such as a
277   const variable or a literal string). If this parameter is a constant string, the
278   function may cause an access violation.
279 
280   The lpCommandLine parameter can be NULL. In that case, the function uses the string
281   pointed to by lpApplicationName as the command line.
282 
283   If both lpApplicationName and lpCommandLine are non-NULL, the null-terminated string
284   pointed to by lpApplicationName specifies the module to execute, and the n
285   ull-terminated string pointed to by lpCommandLine specifies the command line. The
286   new process can use GetCommandLine to retrieve the entire command line. Console
287   processes written in C can use the argc and argv arguments to parse the command line.
288   Because argv[0] is the module name, C programmers generally repeat the module name
289   as the first token in the command line.
290 
291   If lpApplicationName is NULL, the first white space–delimited token of the command
292   line specifies the module name. If you are using a long file name that contains a
293   space, use quoted strings to indicate where the file name ends and the arguments
294   begin (see the explanation for the lpApplicationName parameter). If the file name
295   does not contain an extension, .exe is appended. Therefore, if the file name
296   extension is .com, this parameter must include the .com extension. If the file
297   name ends in a period (.) with no extension, or if the file name contains a path,
298   .exe is not appended. If the file name does not contain a directory path, the
299   system searches for the executable file in the following sequence:
300 
301     The directory from which the application loaded.
302     The current directory for the parent process.
303     The 32-bit Windows system directory. Use the GetSystemDirectory function to get
304     the path of this directory.
305     The 16-bit Windows system directory. There is no function that obtains the path
306     of this directory, but it is searched. The name of this directory is System.
307     The Windows directory. Use the GetWindowsDirectory function to get the path of
308     this directory.
309     The directories that are listed in the PATH environment variable. Note that this
310     function does not search the per-application path specified by the App Paths registry
311     key. To include this per-application path in the search sequence, use the ShellExecute
312     function.
313 
314   The system adds a terminating null character to the command-line string to separate
315   the file name from the arguments. This divides the original string into two strings
316   for internal processing.
317 lpProcessAttributes [in, optional]
318 
319   A pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned
320   handle to the new process object can be inherited by child processes. If
321   lpProcessAttributes is NULL, the handle cannot be inherited.
322 
323   The lpSecurityDescriptor member of the structure specifies a security descriptor for
324   the new process. If lpProcessAttributes is NULL or lpSecurityDescriptor is NULL, the
325   process gets a default security descriptor. The ACLs in the default security descriptor
326   for a process come from the primary token of the creator.
327 
328   Windows XP:  The ACLs in the default security descriptor for a process come from the
329   primary or impersonation token of the creator. This behavior changed with Windows XP
330   with SP2 and Windows Server 2003.
331 
332 lpThreadAttributes [in, optional]
333 
334   A pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned
335   handle to the new thread object can be inherited by child processes. If
336   lpThreadAttributes is NULL, the handle cannot be inherited.
337 
338   The lpSecurityDescriptor member of the structure specifies a security descriptor for
339   the main thread. If lpThreadAttributes is NULL or lpSecurityDescriptor is NULL, the
340   thread gets a default security descriptor. The ACLs in the default security descriptor
341   for a thread come from the process token.
342 
343   Windows XP:  The ACLs in the default security descriptor for a thread come from the
344   primary or impersonation token of the creator. This behavior changed with Windows XP
345   with SP2 and Windows Server 2003.
346 
347 bInheritHandles [in]
348 
349   If this parameter TRUE, each inheritable handle in the calling process is inherited by
350   the new process. If the parameter is FALSE, the handles are not inherited. Note that
351   inherited handles have the same value and access rights as the original handles.
352 
353 dwCreationFlags [in]
354 
355   The flags that control the priority class and the creation of the process. For a list
356   of values, see Process Creation Flags.
357 
358   This parameter also controls the new process's priority class, which is used to
359   determine the scheduling priorities of the process's threads. For a list of values,
360   see GetPriorityClass. If none of the priority class flags is specified, the priority
361   class defaults to NORMAL_PRIORITY_CLASS unless the priority class of the creating
362   process is IDLE_PRIORITY_CLASS or BELOW_NORMAL_PRIORITY_CLASS. In this case, the
363   child process receives the default priority class of the calling process.
364 
365 lpEnvironment [in, optional]
366 
367   A pointer to the environment block for the new process. If this parameter is NULL,
368   the new process uses the environment of the calling process.
369 
370   An environment block consists of a null-terminated block of null-terminated strings.
371   Each string is in the following form:
372 
373   name=value\0
374 
375   Because the equal sign is used as a separator, it must not be used in the name of
376   an environment variable.
377 
378   An environment block can contain either Unicode or ANSI characters. If the
379   environment block pointed to by lpEnvironment contains Unicode characters, be
380   sure that dwCreationFlags includes CREATE_UNICODE_ENVIRONMENT. If this parameter
381   is NULL and the environment block of the parent process contains Unicode characters,
382   you must also ensure that dwCreationFlags includes CREATE_UNICODE_ENVIRONMENT.
383 
384   The ANSI version of this function, CreateProcessA fails if the total size of the
385   environment block for the process exceeds 32,767 characters.
386 
387   Note that an ANSI environment block is terminated by two zero bytes: one for the
388   last string, one more to terminate the block. A Unicode environment block is
389   terminated by four zero bytes: two for the last string, two more to terminate
390   the block.
391 
392 lpCurrentDirectory [in, optional]
393 
394   The full path to the current directory for the process. The string can also
395   specify a UNC path.
396 
397   If this parameter is NULL, the new process will have the same current drive and
398   directory as the calling process. (This feature is provided primarily for shells
399   that need to start an application and specify its initial drive and working
400   directory.)
401 
402 lpStartupInfo [in]
403 
404   A pointer to a STARTUPINFO or STARTUPINFOEX structure.
405 
406   To set extended attributes, use a STARTUPINFOEX structure and specify
407   EXTENDED_STARTUPINFO_PRESENT in the dwCreationFlags parameter.
408 
409   Handles in STARTUPINFO or STARTUPINFOEX must be closed with CloseHandle when
410   they are no longer needed.
411   Important  The caller is responsible for ensuring that the standard handle
412   fields in STARTUPINFO contain valid handle values. These fields are copied
413   unchanged to the child process without validation, even when the dwFlags
414   member specifies STARTF_USESTDHANDLES. Incorrect values can cause the child
415   process to misbehave or crash. Use the Application Verifier runtime verification
416   tool to detect invalid handles.
417 
418 lpProcessInformation [out]
419 
420   A pointer to a PROCESS_INFORMATION structure that receives identification
421   information about the new process.
422 
423   Handles in PROCESS_INFORMATION must be closed with CloseHandle when they are
424   no longer needed.
425 
426 Return value
427 
428 If the function succeeds, the return value is nonzero.
429 
430 If the function fails, the return value is zero. To get extended error information,
431 call GetLastError.
432 
433 Note that the function returns before the process has finished initialization. If a
434 required DLL cannot be located or fails to initialize, the process is terminated. To
435 get the termination status of a process, call GetExitCodeProcess.
436 Remarks
437 
438 The process is assigned a process identifier. The identifier is valid until the
439 process terminates. It can be used to identify the process, or specified in the
440 OpenProcess function to open a handle to the process. The initial thread in the
441 process is also assigned a thread identifier. It can be specified in the OpenThread
442 function to open a handle to the thread. The identifier is valid until the thread
443 terminates and can be used to uniquely identify the thread within the system. These
444 identifiers are returned in the PROCESS_INFORMATION structure.
445 
446 The name of the executable in the command line that the operating system provides
447 to a process is not necessarily identical to that in the command line that the
448 calling process gives to the CreateProcess function. The operating system may
449 prepend a fully qualified path to an executable name that is provided without
450 a fully qualified path.
451 
452 The calling thread can use the WaitForInputIdle function to wait until the new
453 process has finished its initialization and is waiting for user input with no
454 input pending. This can be useful for synchronization between parent and child
455 processes, because CreateProcess returns without waiting for the new process to
456 finish its initialization. For example, the creating process would use
457 WaitForInputIdle before trying to find a window associated with the new process.
458 
459 The preferred way to shut down a process is by using the ExitProcess function,
460 because this function sends notification of approaching termination to all DLLs
461 attached to the process. Other means of shutting down a process do not notify
462 the attached DLLs. Note that when a thread calls ExitProcess, other threads of
463 the process are terminated without an opportunity to execute any additional code
464 (including the thread termination code of attached DLLs). For more information,
465 see Terminating a Process.
466 
467 A parent process can directly alter the environment variables of a child process
468 during process creation. This is the only situation when a process can directly
469 change the environment settings of another process. For more information, see
470 Changing Environment Variables.
471 
472 If an application provides an environment block, the current directory information
473 of the system drives is not automatically propagated to the new process. For
474 example, there is an environment variable named =C: whose value is the current
475 directory on drive C. An application must manually pass the current directory
476 information to the new process. To do so, the application must explicitly create
477 these environment variable strings, sort them alphabetically (because the system
478 uses a sorted environment), and put them into the environment block. Typically,
479 they will go at the front of the environment block, due to the environment block
480 sort order.
481 
482 One way to obtain the current directory information for a drive X is to make the
483 following call: GetFullPathName("X:", ...). That avoids an application having to
484 scan the environment block. If the full path returned is X:\, there is no need to
485 pass that value on as environment data, since the root directory is the default
486 current directory for drive X of a new process.
487 
488 When a process is created with CREATE_NEW_PROCESS_GROUP specified, an implicit
489 call to SetConsoleCtrlHandler(NULL,TRUE) is made on behalf of the new process;
490 this means that the new process has CTRL+C disabled. This lets shells handle
491 CTRL+C themselves, and selectively pass that signal on to sub-processes.
492 CTRL+BREAK is not disabled, and may be used to interrupt the process/process group.
493 
494 Security Remarks
495 
496 The first parameter, lpApplicationName, can be NULL, in which case the executable
497 name must be in the white space–delimited string pointed to by lpCommandLine. If
498 the executable or path name has a space in it, there is a risk that a different
499 executable could be run because of the way the function parses spaces. The following
500 example is dangerous because the function will attempt to run "Program.exe", if it
501 exists, instead of "MyApp.exe".
502 
503 	LPTSTR szCmdline = _tcsdup(TEXT("C:\\Program Files\\MyApp -L -S"));
504 	CreateProcess(NULL, szCmdline,  ... );
505 
506 If a malicious user were to create an application called "Program.exe" on a system,
507 any program that incorrectly calls CreateProcess using the Program Files directory
508 will run this application instead of the intended application.
509 
510 To avoid this problem, do not pass NULL for lpApplicationName. If you do pass NULL
511 for lpApplicationName, use quotation marks around the executable path in lpCommandLine.
512 */
513 
start_process(string executable)514 void start_process(string executable)
515 {
516 	while(executable[executable.length()-1] == ' ')
517 		executable.erase(executable.length()-1);
518 	if (!executable.empty()) {
519 #ifdef __MINGW32__
520 		static string cmdstr;
521 		cmdstr.assign(executable);
522 		STARTUPINFO si;
523 		PROCESS_INFORMATION pi;
524 		memset(&si, 0, sizeof(si));
525 		si.cb = sizeof(si);
526 		memset(&pi, 0, sizeof(pi));
527 		LOG_INFO("Starting external process: %s", cmdstr.c_str());
528 		if (!CreateProcess(	NULL, const_cast<char*>(cmdstr.c_str()),
529 							NULL, NULL,
530 							FALSE, CREATE_NO_WINDOW,
531 							NULL, NULL,
532 							&si, &pi))
533 			LOG_ERROR("CreateProcess failed with error code %ld", GetLastError());
534 		for (int i = 0; i < 5; i++) {
535 			MilliSleep(50);
536 			Fl::awake();
537 		}
538 //		MilliSleep(100);
539 		CloseHandle(pi.hProcess);
540 		CloseHandle(pi.hThread);
541 		LOG_INFO("Process handles closed");
542 #else
543 #ifdef __APPLE__
544 		string params = "";
545 		size_t p = executable.find(".app/Contents/MacOS");
546 
547 		if (p == string::npos) {
548 			p = executable.find(".app");
549 			if (p != string::npos) {
550 				params = executable.substr(p+4);
551 				executable.erase(p+4);
552 
553 				if (executable[0] == '"') executable.erase(0,1);
554 				while ( (executable[executable.length()-1] == '"') ||
555 					(executable[executable.length()-1] == ' ') )
556 					executable.erase(executable.length()-1, 1);
557 				if (params[0] == '"') params.erase(0,1);
558 				while (params[0] == ' ') params.erase(0,1);
559 
560 				DIR *dp = NULL;
561 				executable.append("/Contents/MacOS/");
562 					dp = opendir(executable.c_str());
563 				if (!dp) {
564 					fl_alert2("FOLDER NOT FOUND\n\n%s", executable.c_str());
565 					return;
566 				}
567 				struct dirent *sd = NULL;
568 				sd = readdir(dp);
569 				string sds = sd->d_name;
570 				while (sds == "." || sds == "..") { sd = readdir(dp); sds = sd->d_name; }
571 				closedir(dp);
572 
573 				executable.insert(0,"\"");
574 				executable.append(sds).append("\"");
575 
576 				if (!params.empty()) executable.append(" ").append(params);
577 			}
578 		}
579 #endif // __APPLE__
580 
581 		LOG_INFO("Start external process: %s", executable.c_str());
582 		switch (fork()) {
583 			case -1:
584 				LOG_PERROR("fork");
585 				// fall through
586 			default:
587 				break;
588 
589 			case 0:
590 				execl("/bin/sh", "sh", "-c", executable.c_str(), (char *)NULL);
591 				perror("execl");
592 				exit(EXIT_FAILURE);
593 		}
594 #endif
595 	}
596 }
597 
toggle_io_port_selection(int io_mode)598 void toggle_io_port_selection(int io_mode)
599 {
600 
601 	switch(io_mode) {
602 		case ARQ_IO:
603 			enable_arq();
604 			progdefaults.changed = false;
605 		break;
606 
607 		case KISS_IO:
608 
609 			enable_kiss();
610 
611 			if(progdefaults.tcp_udp_auto_connect) {
612 				btn_connect_kiss_io->value(1);
613 				btn_connect_kiss_io->do_callback();
614 			}
615 
616 			if(progdefaults.kpsql_enabled && progdefaults.show_psm_btn) {
617 				btnPSQL->value(progdefaults.kpsql_enabled);
618 				btnPSQL->do_callback();
619 			}
620 
621 			progdefaults.changed = false;
622 			break;
623 
624 		default:
625 			LOG_INFO("Unknown data io mode");
626 	}
627 }
628 
auto_start()629 static void auto_start()
630 {
631 	bool run_flamp = false;
632 
633 	// Make sure we are in ARQ_IO mode if executing FLAMP
634 	if (!progdefaults.auto_flamp_pathname.empty() &&
635 		 progdefaults.flamp_auto_enable) {
636 		 toggle_io_port_selection(ARQ_IO);
637 		 run_flamp = true;
638 	}
639 
640 	// A general wait to ensure FLDIGI initialization of
641 	// io ports. 1/4 to 3/4 second delay.
642 	int nloops = 0;
643 	while(nloops++ < 15) {// 3) {
644 		MilliSleep(50);
645 		Fl::awake();
646 //		MilliSleep(250);
647 		if(arq_state() && data_io_enabled == ARQ_IO)
648 			break; // Exit early if verified.
649 	}
650 
651 	if (!progdefaults.auto_flrig_pathname.empty() &&
652 		 progdefaults.flrig_auto_enable)
653 		start_process(progdefaults.auto_flrig_pathname);
654 
655 	if (run_flamp)
656 		start_process(progdefaults.auto_flamp_pathname);
657 
658 	if (!progdefaults.auto_fllog_pathname.empty() &&
659 		 progdefaults.fllog_auto_enable)
660 		start_process(progdefaults.auto_fllog_pathname);
661 
662 	if (!progdefaults.auto_flnet_pathname.empty() &&
663 		 progdefaults.flnet_auto_enable)
664 		start_process(progdefaults.auto_flnet_pathname);
665 
666 	if (!progdefaults.auto_prog1_pathname.empty() &&
667 		 progdefaults.prog1_auto_enable)
668 		start_process(progdefaults.auto_prog1_pathname);
669 
670 	if (!progdefaults.auto_prog2_pathname.empty() &&
671 		 progdefaults.prog2_auto_enable)
672 		start_process(progdefaults.auto_prog2_pathname);
673 
674 	if (!progdefaults.auto_prog3_pathname.empty() &&
675 		 progdefaults.prog3_auto_enable)
676 		start_process(progdefaults.auto_prog3_pathname);
677 }
678 
679 // reset those default values that have been overriden by a command line parameter
check_overrides()680 void check_overrides()
681 {
682 	if (xmlrpc_address_override_flag)
683 		progdefaults.xmlrpc_address = override_xmlrpc_address;
684 	if (xmlrpc_port_override_flag)
685 		progdefaults.xmlrpc_port = override_xmlrpc_port;
686 	if (arq_address_override_flag)
687 		progdefaults.arq_address = override_arq_address;
688 	if (arq_port_override_flag)
689 		progdefaults.arq_port = override_arq_port;
690 }
691 
692 // these functions are all started after Fl::run() is executing
delayed_startup(void *)693 void delayed_startup(void *)
694 {
695 	macros.loadDefault();
696 
697 	connect_to_log_server();
698 
699 #ifdef __WIN32__
700 	if (progdefaults.auto_talk) open_talker();
701 #else
702 	grpTalker->hide();
703 #endif
704 
705 	XML_RPC_Server::start(progdefaults.xmlrpc_address.c_str(), progdefaults.xmlrpc_port.c_str());
706 
707 	FLRIG_start_flrig_thread();
708 
709 	data_io_enabled = DISABLED_IO;
710 
711 	n3fjp_init();
712 	arq_init();
713 	FD_init();
714 	DXcluster_init();
715 
716 	start_psm_thread();
717 
718 	if (progdefaults.connect_to_maclogger) maclogger_init();
719 	data_io_enabled = progStatus.data_io_enabled;
720 
721 	toggle_io_port_selection(data_io_enabled);
722 	disable_config_p2p_io_widgets();
723 
724 	notify_start();
725 
726 	if (progdefaults.pskrep_autostart) {
727 		if (!pskrep_start()) {
728 			LOG_ERROR("Could not start PSK reporter: %s", pskrep_error());
729 			box_connected_to_pskrep->color(FL_WHITE);
730 		} else
731 			box_connected_to_pskrep->color(FL_GREEN);
732 	}
733 
734 	auto_start();
735 
736 	if (progStatus.WK_online) {
737 		if (progStatus.WKFSK_mode) {
738 			btn_WKFSK_connect->value(1);
739 			WKFSK_connect(1);
740 		} else {
741 			btn_WKCW_connect->value(1);
742 			WKCW_connect(1);
743 		}
744 	}
745 
746 	if (progStatus.Nav_online) {
747 		if (open_NavFSK()) btn_Nav_connect->value(1);
748 	}
749 
750 	if (progStatus.Nav_config_online) {
751 		if (open_NavConfig()) btn_Nav_config->value(1);
752 	}
753 
754 	if (progStatus.nanoCW_online) {
755 		if (open_nanoCW()) btn_nanoCW_connect->value(1);
756 	}
757 
758 	if (progStatus.nanoFSK_online) {
759 		if (open_nanoIO()) btn_nanoIO_connect->value(1);
760 	}
761 
762 	if (progStatus.useCW_KEYLINE) {
763 		if (!open_CW_KEYLINE())
764 			progStatus.useCW_KEYLINE = false;
765 	}
766 
767 	if (progdefaults.check_for_updates)
768 		cb_mnuCheckUpdate((Fl_Widget *)0, NULL);
769 
770 #if USE_PORTAUDIO
771 	try {
772 		audio_alert = 0;
773 		audio_alert = new Caudio_alert;
774 	} catch (...) {
775 		audio_alert = 0;
776 		LOG_ERROR("%s", "Failed to create audio alert object");
777 	}
778 
779 	if (audio_alert)
780 		LOG_INFO("%s", "Created audio alert object");
781 
782 	reset_audio_alerts();
783 #endif
784 
785 }
786 
787 std::string pname = "";
788 
main(int argc,char * argv[])789 int main (int argc, char *argv[])
790 {
791 	pname = argv[0];
792 	size_t pn = pname.rfind("/");
793 	if (pn != std::string::npos) pname.erase(0, pn + 1);
794 	pn = pname.rfind("\\");
795 	if (pn != std::string::npos) pname.erase(0, pn + 1);
796 	XmlRpc::set_pname(pname);
797 
798 	// for KISS_IO status information
799 	program_start_time = time(0);
800 
801 	active_modem = new NULLMODEM;
802 
803 	string appdir = appname = argv[0];
804 	string test_file_name;
805 
806 	BaseDir.clear();
807 	HomeDir.clear();
808 	NBEMS_dir.clear();
809 	FLMSG_dir.clear();
810 
811 #ifdef __WOE32__
812 	size_t p = appdir.rfind("fldigi.exe");
813 	appdir.erase(p);
814 	p = appdir.find("FL_APPS\\");
815 	if (p != string::npos) {
816 		BaseDir.assign(appdir.substr(0, p + 8));
817 		progdefaults.flmsg_pathname.assign(BaseDir).append("flmsg.exe");
818 	} else {
819 		BaseDir.clear();
820 		HomeDir.clear();
821 		NBEMS_dir.clear();
822 		FLMSG_dir.clear();
823 	}
824 #else
825 	char apptemp[FL_PATH_MAX + 1];
826 	fl_filename_absolute(apptemp, sizeof(apptemp), argv[0]);
827 	appdir.assign(apptemp);
828 	size_t p = appdir.rfind("fldigi");
829 	if (p != string::npos)
830 		appdir.erase(p);
831 	p = appdir.find("FL_APPS/");
832 	if (p != string::npos) {
833 		BaseDir.assign(appdir.substr(0, p + 8));
834 		progdefaults.flmsg_pathname.assign(BaseDir).append("flmsg");
835 		string test_dir;
836 		test_dir.assign(BaseDir).append("fldigi.files/");
837 		DIR *isdir = opendir(test_dir.c_str());
838 		if (isdir) {
839 			HomeDir = test_dir;
840 			closedir(isdir);
841 		} else {
842 			test_dir.assign(BaseDir).append(".fldigi/");
843 			isdir = opendir(test_dir.c_str());
844 			if (isdir) {
845 				HomeDir = test_dir;
846 			} else {
847 				HomeDir.clear();
848 			}
849 		}
850 		if (!HomeDir.empty()) {
851 			test_dir.assign(BaseDir).append("NBEMS.files/");
852 			isdir = opendir(test_dir.c_str());
853 			if (isdir) {
854 				NBEMS_dir = test_dir;
855 				FLMSG_dir = test_dir;
856 				closedir(isdir);
857 			} else {
858 				test_dir.assign(BaseDir).append(".nbems/");
859 				isdir = opendir(test_dir.c_str());
860 				if (isdir) {
861 					NBEMS_dir = test_dir;
862 					FLMSG_dir = test_dir;
863 				} else {
864 					NBEMS_dir.clear();
865 					FLMSG_dir.clear();
866 				}
867 			}
868 		}
869 	} else {
870 		BaseDir.clear();
871 		HomeDir.clear();
872 		NBEMS_dir.clear();
873 		FLMSG_dir.clear();
874 	}
875 #endif
876 
877 	debug_exec(argv);
878 	CREATE_THREAD_ID(); // only call this once
879 	SET_THREAD_ID(FLMAIN_TID);
880 
881 	for (int i = 0; i < NUM_QRUNNER_THREADS; i++) {
882 		cbq[i] = new qrunner;
883 		switch(i) {
884 			case TRX_TID:
885 				cbq[i]->attach(i, "TRX_TID");
886 				break;
887 
888 			case TOD_TID:
889 				cbq[i]->attach(i, "TOD_TID");
890 				break;
891 
892 			case QRZ_TID:
893 				cbq[i]->attach(i, "QRZ_TID");
894 				break;
895 
896 			case RIGCTL_TID:
897 				cbq[i]->attach(i, "RIGCTL_TID");
898 				break;
899 
900 			case NORIGCTL_TID:
901 				cbq[i]->attach(i, "NORIGCTL_TID");
902 				break;
903 
904 			case EQSL_TID:
905 				cbq[i]->attach(i, "EQSL_TID");
906 				break;
907 
908 			case ADIF_RW_TID:
909 				cbq[i]->attach(i, "ADIF_RW_TID");
910 				break;
911 
912 			case ADIF_MERGE_TID:
913 				cbq[i]->attach(i, "ADIF_MERGE_TID");
914 				break;
915 
916 			case XMLRPC_TID:
917 				cbq[i]->attach(i, "XMLRPC_TID");
918 				break;
919 
920 			case ARQ_TID:
921 				cbq[i]->attach(i, "ARQ_TID");
922 				break;
923 
924 			case ARQSOCKET_TID:
925 				cbq[i]->attach(i, "ARQSOCKET_TID");
926 				break;
927 
928 			case KISS_TID:
929 				cbq[i]->attach(i, "KISS_TID");
930 				break;
931 
932 			case KISSSOCKET_TID:
933 				cbq[i]->attach(i, "KISSSOCKET_TID");
934 				break;
935 
936 			case MACLOGGER_TID:
937 				cbq[i]->attach(i, "MACLOGGER_TID");
938 				break;
939 
940 			case PSM_TID:
941 				cbq[i]->attach(i, "PSM_TID");
942 				break;
943 
944 			case AUDIO_ALERT_TID:
945 				cbq[i]->attach(i, "AUDIO_ALERT_TID");
946 				break;
947 
948 			case FD_TID:
949 				cbq[i]->attach(i, "FD_TID");
950 				break;
951 
952 			case N3FJP_TID:
953 				cbq[i]->attach(i, "N3FJP_TID");
954 				break;
955 
956 			case DXCC_TID:
957 				cbq[i]->attach(i, "DXCC_TID");
958 				break;
959 
960 			case WKEY_TID:
961 				cbq[i]->attach(i, "WKEY_TID");
962 				break;
963 
964 			case FLMAIN_TID:
965 				cbq[i]->attach(i, "FLMAIN_TID");
966 				break;
967 
968 			default:
969 				break;
970 		}
971 	}
972 
973 	set_unexpected(handle_unexpected);
974 	set_terminate(diediedie);
975 	setup_signal_handlers();
976 
977 	setlocale(LC_ALL, "");
978 
979 #ifndef ENABLE_NLS
980 	setlocale(LC_TIME, "");
981 #endif
982 
983 	set_platform_ui();
984 
985 	generate_version_text();
986 	{
987 		char dirbuf[FL_PATH_MAX + 1];
988 #ifdef __WOE32__
989 		if (BaseDir.empty()) {
990 			fl_filename_expand(dirbuf, sizeof(dirbuf) -1, "$USERPROFILE/");
991 			BaseDir = dirbuf;
992 		}
993 #else
994 		if (BaseDir.empty()) {
995 			fl_filename_expand(dirbuf, sizeof(dirbuf) -1, "$HOME/");
996 			BaseDir = dirbuf;
997 		}
998 #endif
999 	}
1000 
1001 	generate_option_help();
1002 
1003 //	FL_NORMAL_SIZE = 14;
1004 
1005 	int arg_idx;
1006 	if (Fl::args(argc, argv, arg_idx, parse_args) != argc)
1007 		arg_error(argv[0], NULL, false);
1008 
1009 	if (argv_window_title.empty())
1010 		argv_window_title.assign(PACKAGE_TARNAME);
1011 
1012 #ifdef __WOE32__
1013 	if (HomeDir.empty()) HomeDir.assign(BaseDir).append("fldigi.files/");
1014 	if (PskMailDir.empty()) PskMailDir = BaseDir;
1015 	if (DATA_dir.empty()) DATA_dir.assign(BaseDir).append("DATA.files/");
1016 	if (NBEMS_dir.empty()) NBEMS_dir.assign(BaseDir).append("NBEMS.files/");
1017 	if (FLMSG_dir.empty()) FLMSG_dir = NBEMS_dir;
1018 #else
1019 	if (HomeDir.empty()) HomeDir.assign(BaseDir).append(".fldigi/");
1020 	if (PskMailDir.empty()) PskMailDir = BaseDir;
1021 	if (DATA_dir.empty()) DATA_dir.assign(BaseDir).append("DATA.files/");
1022 	if (NBEMS_dir.empty()) NBEMS_dir.assign(BaseDir).append(".nbems/");
1023 	if (FLMSG_dir.empty()) FLMSG_dir = NBEMS_dir;
1024 #endif
1025 
1026 	if (!FLMSG_dir_default.empty()) {
1027 		char dirbuf[FL_PATH_MAX + 1];
1028 		if (FLMSG_dir_default[FLMSG_dir_default.length()-1] != '/')
1029 			FLMSG_dir_default += '/';
1030 		fl_filename_expand(dirbuf, sizeof(dirbuf) - 1, FLMSG_dir_default.c_str());
1031 		FLMSG_dir = dirbuf;
1032 	}
1033 
1034 	if (!NBEMS_dir_default.empty()) {
1035 		char dirbuf[FL_PATH_MAX + 1];
1036 		if (NBEMS_dir_default[NBEMS_dir_default.length()-1] != '/')
1037 			NBEMS_dir_default += '/';
1038 		fl_filename_expand(dirbuf, sizeof(dirbuf) - 1, NBEMS_dir_default.c_str());
1039 		NBEMS_dir = dirbuf;
1040 	}
1041 
1042 	checkdirectories();
1043 	check_nbems_dirs();
1044 	check_data_dir();
1045 
1046 	try {
1047 		debug::start(string(DebugDir).append("status_log.txt").c_str());
1048 		time_t t = time(NULL);
1049 		LOG(debug::QUIET_LEVEL, debug::LOG_OTHER, _("%s log started on %s"), PACKAGE_STRING, ctime(&t));
1050 		LOG_THREAD_ID();
1051 	}
1052 	catch (const char* error) {
1053 		cerr << error << '\n';
1054 		debug::stop();
1055 	}
1056 	TOD_init(); // initialize time of day thread
1057 
1058 	LOG_INFO("appname: %s", appname.c_str());
1059 
1060 	LOG_INFO("%s", "Directories");
1061 	LOG_INFO("HomeDir: %s", HomeDir.c_str());
1062 	LOG_INFO("DATA_dir: %s", DATA_dir.c_str());
1063 	LOG_INFO("DebugDir: %s", DebugDir.c_str());
1064 	LOG_INFO("HelpDir: %s", HelpDir.c_str());
1065 	LOG_INFO("KmlDir: %s", KmlDir.c_str());
1066 	LOG_INFO("LogsDir: %s", LogsDir.c_str());
1067 	LOG_INFO("LoTWDir: %s", LoTWDir.c_str());
1068 	LOG_INFO("MacrosDir: %s", MacrosDir.c_str());
1069 	LOG_INFO("AnalysisDir: %s", AnalysisDir.c_str());
1070 	LOG_INFO("FMTDir: %s", FMTDir.c_str());
1071 	LOG_INFO("PalettesDir: %s", PalettesDir.c_str());
1072 	LOG_INFO("PicsDir: %s", PicsDir.c_str());
1073 	LOG_INFO("PskMailDir: %s", PskMailDir.c_str());
1074 	LOG_INFO("RigsDir: %s", RigsDir.c_str());
1075 	LOG_INFO("ScriptsDir: %s", ScriptsDir.c_str());
1076 	LOG_INFO("TalkDir: %s", TalkDir.c_str());
1077 	LOG_INFO("TempDir: %s", TempDir.c_str());
1078 	LOG_INFO("WrapDir: %s", WrapDir.c_str());
1079 
1080 	LOG_INFO("%s", "NBEMS directories");
1081 	LOG_INFO("NBEMS_dir: %s", NBEMS_dir.c_str());
1082 	LOG_INFO("ARQ_dir: %s", ARQ_dir.c_str());
1083 	LOG_INFO("ARQ_files_dir: %s", ARQ_files_dir.c_str());
1084 	LOG_INFO("ARQ_recv_dir: %s", ARQ_recv_dir.c_str());
1085 	LOG_INFO("ARQ_send: %s", ARQ_send.c_str());
1086 	LOG_INFO("WRAP_dir: %s", WRAP_dir.c_str());
1087 	LOG_INFO("WRAP_recv_dir: %s", WRAP_recv_dir.c_str());
1088 	LOG_INFO("WRAP_send_dir: %s", WRAP_send_dir.c_str());
1089 	LOG_INFO("WRAP_auto_dir: %s", WRAP_auto_dir.c_str());
1090 	LOG_INFO("ICS_dir: %s", ICS_dir.c_str());
1091 	LOG_INFO("ICS_msg_dir: %s", ICS_msg_dir.c_str());
1092 	LOG_INFO("ICS_tmp_dir: %s", ICS_tmp_dir.c_str());
1093 
1094 	LOG_INFO("%s", "FLMSG directories");
1095 	LOG_INFO("FLMSG_dir: %s", FLMSG_dir.c_str());
1096 	LOG_INFO("FLMSG_dir_default: %s", FLMSG_dir_default.c_str());
1097 	LOG_INFO("FLMSG_WRAP_dir: %s", FLMSG_WRAP_dir.c_str());
1098 	LOG_INFO("FLMSG_WRAP_recv_dir: %s", FLMSG_WRAP_recv_dir.c_str());
1099 	LOG_INFO("FLMSG_WRAP_send_dir: %s", FLMSG_WRAP_send_dir.c_str());
1100 	LOG_INFO("FLMSG_WRAP_auto_dir: %s", FLMSG_WRAP_auto_dir.c_str());
1101 	LOG_INFO("FLMSG_ICS_dir: %s", FLMSG_ICS_dir.c_str());
1102 	LOG_INFO("FLMSG_ICS_msg_dir: %s", FLMSG_ICS_msg_dir.c_str());
1103 	LOG_INFO("FLMSG_ICS_tmp_dir: %s", FLMSG_ICS_tmp_dir.c_str());
1104 
1105 	bool have_config = progdefaults.readDefaultsXML();
1106 	check_overrides();
1107 
1108 	xmlfname = HomeDir;
1109 	xmlfname.append(DEFAULT_RIGXML_FILENAME);
1110 
1111 	checkTLF();
1112 
1113 	Fl::lock();  // start the gui thread!!
1114 	Fl::visual(FL_RGB); // insure 24 bit color operation
1115 
1116 	fl_register_images();
1117 	Fl::set_fonts(0);
1118 
1119 	Fl::scheme(progdefaults.ui_scheme.c_str());
1120 	progdefaults.initFonts();
1121 
1122 	if (progdefaults.cty_dat_pathname.empty())
1123 		progdefaults.cty_dat_pathname = HomeDir;
1124 
1125 	dxcc_open(string(progdefaults.cty_dat_pathname).append("cty.dat").c_str());
1126 	qsl_open(string(progdefaults.cty_dat_pathname).append("lotw1.txt").c_str(), QSL_LOTW);
1127 	if (!qsl_open(string(progdefaults.cty_dat_pathname).append("eqsl.txt").c_str(), QSL_EQSL))
1128 		qsl_open(string(progdefaults.cty_dat_pathname).append("AGMemberList.txt").c_str(), QSL_EQSL);
1129 
1130 	progStatus.loadLastState();
1131 	create_fl_digi_main(argc, argv);
1132 
1133 	if (!have_config || show_cpucheck) {
1134 		double speed = speed_test(SRC_SINC_FASTEST, 8);
1135 
1136 		if (speed > 150.0) {      // fast
1137 			progdefaults.slowcpu = false;
1138 			progdefaults.sample_converter = SRC_SINC_BEST_QUALITY;
1139 		}
1140 		else if (speed > 60.0) {  // ok
1141 			progdefaults.slowcpu = false;
1142 			progdefaults.sample_converter = SRC_SINC_MEDIUM_QUALITY;
1143 		}
1144 		else if (speed > 15.0) { // slow
1145 			progdefaults.slowcpu = true;
1146 			progdefaults.sample_converter = SRC_SINC_FASTEST;
1147 		}
1148 		else {                   // recycle me
1149 			progdefaults.slowcpu = true;
1150 			progdefaults.sample_converter = SRC_LINEAR;
1151 		}
1152 
1153 		LOG_INFO("CPU speed factor=%f: setting slowcpu=%s, sample_converter=\"%s\"", speed,
1154 			 progdefaults.slowcpu ? "true" : "false",
1155 			 src_get_name(progdefaults.sample_converter));
1156 	}
1157 
1158 	if (progdefaults.XmlRigFilename.empty())
1159 		progdefaults.XmlRigFilename = xmlfname;
1160 
1161 #if BENCHMARK_MODE
1162 	return setup_benchmark();
1163 #endif
1164 
1165 	FSEL::create();
1166 
1167 #if FLDIGI_FLTK_API_MAJOR == 1 && FLDIGI_FLTK_API_MINOR < 3
1168 		listbox_charset_status->hide();
1169 #else
1170 		listbox_charset_status->show();
1171 #endif
1172 	populate_charset_listbox();
1173 	set_default_charset();
1174 	setTabColors();
1175 
1176 	progdefaults.testCommPorts();
1177 	//init_hids();
1178 
1179 #if USE_HAMLIB
1180 	xcvr = new Rig();
1181 #endif
1182 
1183 	push2talk = new PTT();
1184 
1185 	progdefaults.setDefaults();
1186 
1187 	atexit(sound_close);
1188 	sound_init();
1189 
1190 	progdefaults.initInterface();
1191 	trx_start();
1192 
1193 #if USE_PORTAUDIO
1194 	try {
1195 		audio_alert = 0;
1196 		audio_alert = new Caudio_alert;
1197 	} catch (...) {
1198 		audio_alert = 0;
1199 		LOG_ERROR("%s", "Failed to create audio alert object");
1200 	}
1201 
1202 	if (audio_alert)
1203 		LOG_INFO("%s", "Created audio alert object");
1204 #endif
1205 
1206 	if (!have_config) {
1207 		show_wizard(argc, argv);
1208 		Fl_Window* w;
1209 		while ((w = Fl::first_window()) && w->visible())
1210 			Fl::wait();
1211 	}
1212 
1213 	dlgViewer = createViewer();
1214 
1215 	dxcluster_viewer = dxc_window();
1216 	dxcluster_viewer->hide();
1217 
1218 	if (!dlgLogbook)
1219 		create_logbook_dialogs();
1220 
1221 	LOGBOOK_colors_font();
1222 
1223 	rxaudio_dialog = make_rxaudio_dialog();
1224 	rxaudio_dialog->hide();
1225 
1226 	if( progdefaults.kml_save_dir.empty() ) {
1227 		progdefaults.kml_save_dir = KmlDir ;
1228 	}
1229 	kml_init(true);
1230 
1231 	if (progdefaults.kml_purge_on_startup) {
1232 		KmlServer::GetInstance()->Reset();
1233 	}
1234 
1235 // OS X will prevent the main window from being resized if we change its
1236 // size *after* it has been shown. With some X11 window managers, OTOH,
1237 // the main window will not be restored at its exact saved position if
1238 // we move it *after* it has been shown.
1239 #ifndef __APPLE__
1240 	progStatus.initLastState();
1241 	fl_digi_main->show(argc, argv);
1242 #else
1243 #  if FLDIGI_FLTK_API_MAJOR == 1 && FLDIGI_FLTK_API_MINOR < 4
1244 	fl_digi_main->show(argc, argv);
1245 	progStatus.initLastState();
1246 #  else
1247 	progStatus.initLastState();
1248 	fl_digi_main->show(argc, argv);
1249 #  endif
1250 #endif
1251 
1252 	if (iconified)
1253 		for (Fl_Window* w = Fl::first_window(); w; w = Fl::next_window(w))
1254 			w->iconize();
1255 	update_main_title();
1256 
1257 	mode_browser = new Mode_Browser;
1258 
1259 	clearQSO();
1260 
1261 	Fl::add_timeout(.25, delayed_startup);
1262 
1263 	Fl::set_color(FL_SELECTION_COLOR, 0, 0, 128);
1264 
1265 	return  Fl::run();
1266 }
1267 
exit_process()1268 void exit_process() {
1269 
1270 LOG_INFO("Close Spectrum dialog");
1271 	close_spectrum_viewer();
1272 
1273 LOG_INFO("Stop KML server");
1274 	if (progdefaults.kml_enabled)
1275 		KmlServer::Exit();
1276 
1277 LOG_INFO("Stop PSM processing");
1278 	stop_psm_thread();
1279 LOG_INFO("Stop ARQ processing");
1280 	arq_close();
1281 LOG_INFO("Close FD dialog");
1282 	FD_close();
1283 LOG_INFO("Stop DX cluster i/o");
1284 	DXcluster_close();
1285 LOG_INFO("Stop KISS processing");
1286 	kiss_close(false);
1287 LOG_INFO("Stop MacLogger i/o");
1288 	maclogger_close();
1289 LOG_INFO("Send shutdown signal to flrig");
1290 	if (progdefaults.flrig_auto_shutdown)
1291 		xmlrpc_shutdown_flrig();
1292 LOG_INFO("Stop xmlrpc processing");
1293 	XML_RPC_Server::stop();
1294 LOG_INFO("Stop PSK reporter processing");
1295 	if (progdefaults.usepskrep)
1296 		pskrep_stop();
1297 
1298 LOG_INFO("Detach/delete qrunner threads");
1299 	for (int i = 0; i < NUM_QRUNNER_THREADS; i++) {
1300 LOG_INFO("thread %d", i);
1301 		cbq[i]->detach();
1302 		delete cbq[i];
1303 	}
1304 LOG_INFO("FSEL::destroy()");
1305 	FSEL::destroy();
1306 }
1307 
generate_option_help(void)1308 void generate_option_help(void) {
1309 	ostringstream help;
1310 	string disp_base_dir = BaseDir;
1311 #ifdef __WOE32__
1312 	size_t p = 0;
1313 	while ((p = disp_base_dir.find("/")) != string::npos)
1314 		disp_base_dir[p] = '\\';
1315 #endif
1316 
1317 	help << "Usage:\n"
1318 		 << "    " << PACKAGE_NAME << " [option...]\n\n";
1319 
1320 	help << PACKAGE_NAME << " options:\n\n"
1321 #if !defined(__WOE32__)
1322 		 << "  --home-dir DIRECTORY\n"
1323 		 << "    Set the home directory to full pathname of DIRECTORY\n"
1324 		 << "    fldigi will put the file stores\n"
1325 		 << "      .fldigi.files, and .nbems.files\n"
1326 		 << "    in this directory\n"
1327 		 << "    The default is: " << disp_base_dir << "\n\n"
1328 
1329 		 << "  --config-dir DIRECTORY\n"
1330 		 << "    Look for configuration files in DIRECTORY\n"
1331 		 << "    The default is: " << disp_base_dir << ".fldigi/\n\n"
1332 #else
1333 		 << "  --home-dir FOLDER\n"
1334 		 << "    Set the home folder to full pathname of FOLDER\n"
1335 		 << "    fldigi will put the file stores\n"
1336 		 << "       fldigi.files, and nbems.files\n"
1337 		 << "    in this folder\n"
1338 		 << "    The default is: " << disp_base_dir << "\n\n"
1339 
1340 		 << "  --config-dir FOLDER\n"
1341 		 << "    Look for configuration files in FOLDER\n"
1342 		 << "    The default is: " << disp_base_dir << "fldigi.files\\\n\n"
1343 #endif
1344 
1345 #if !defined(__WOE32__) && !defined(__APPLE__)
1346 		 << "  --rx-ipc-key KEY\n"
1347 		 << "    Set the receive message queue key\n"
1348 		 << "    May be given in hex if prefixed with \"0x\"\n"
1349 		 << "    The default is: " << progdefaults.rx_msgid
1350 		 << " or 0x" << hex << progdefaults.rx_msgid << dec << "\n\n"
1351 
1352 		 << "  --tx-ipc-key KEY\n"
1353 		 << "    Set the transmit message queue key\n"
1354 		 << "    May be given in hex if prefixed with \"0x\"\n"
1355 		 << "    The default is: " << progdefaults.tx_msgid
1356 		 << " or 0x" << hex << progdefaults.tx_msgid << dec << "\n\n"
1357 #endif
1358 
1359 		 << "  --enable-io-port <" << ARQ_IO << "|" << KISS_IO << "> ARQ=" << ARQ_IO << " KISS=" << KISS_IO << "\n"
1360 		 << "    Select the active IO Port\n"
1361 		 << "    The default is: " << progdefaults.data_io_enabled << "\n\n"
1362 
1363 		 << "  --kiss-server-address HOSTNAME\n"
1364 		 << "    Set the KISS TCP/UDP server address\n"
1365 		 << "    The default is: " << progdefaults.kiss_address << "\n\n"
1366 		 << "  --kiss-server-port-io I/O PORT\n"
1367 		 << "    Set the KISS TCP/UDP server I/O port\n"
1368 		 << "    The default is: " << progdefaults.kiss_io_port << "\n\n"
1369 		 << "  --kiss-server-port-o Output PORT\n"
1370 		 << "    Set the KISS UDP server output port\n"
1371 		 << "    The default is: " << progdefaults.kiss_out_port << "\n\n"
1372 		 << "  --kiss-server-dual-port Dual Port Use (0=disable / 1=enable)\n"
1373 		 << "    Set the KISS UDP server dual port flag\n"
1374 		 << "    The default is: " << progdefaults.kiss_dual_port_enabled << "\n\n"
1375 
1376 		 << "  --arq-server-address HOSTNAME\n"
1377 		 << "    Set the ARQ TCP server address\n"
1378 		 << "    The default is: " << progdefaults.arq_address << "\n\n"
1379 		 << "  --arq-server-port PORT\n"
1380 		 << "    Set the ARQ TCP server port\n"
1381 		 << "    The default is: " << progdefaults.arq_port << "\n\n"
1382 		 << "  --flmsg-dir DIRECTORY\n"
1383 		 << "    Look for flmsg files in DIRECTORY\n"
1384 		 << "    The default is " << FLMSG_dir_default << "\n\n"
1385 		 << "  --auto-dir DIRECTORY\n"
1386 		 << "    Look for auto-send files in DIRECTORY\n"
1387 		 << "    The default is " << HomeDir << "/autosend" << "\n\n"
1388 
1389 		 << "  --xmlrpc-server-address HOSTNAME\n"
1390 		 << "    Set the XML-RPC server address\n"
1391 		 << "    The default is: " << progdefaults.xmlrpc_address << "\n\n"
1392 		 << "  --xmlrpc-server-port PORT\n"
1393 		 << "    Set the XML-RPC server port\n"
1394 		 << "    The default is: " << progdefaults.xmlrpc_port << "\n\n"
1395 		 << "  --xmlrpc-allow REGEX\n"
1396 		 << "    Allow only the methods whose names match REGEX\n\n"
1397 		 << "  --xmlrpc-deny REGEX\n"
1398 		 << "    Allow only the methods whose names don't match REGEX\n\n"
1399 		 << "  --xmlrpc-list\n"
1400 		 << "    List all available methods\n\n"
1401 
1402 #if BENCHMARK_MODE
1403 		 << "  --benchmark-modem ID\n"
1404 		 << "    Specify the modem\n"
1405 		 << "    Default: " << mode_info[benchmark.modem].sname << "\n\n"
1406 		 << "  --benchmark-frequency FREQ\n"
1407 		 << "    Specify the modem frequency\n"
1408 		 << "    Default: " << benchmark.freq << "\n\n"
1409 		 << "  --benchmark-afc BOOLEAN\n"
1410 		 << "    Set modem AFC\n"
1411 		 << "    Default: " << benchmark.afc
1412 		 << " (" << boolalpha << benchmark.afc << noboolalpha << ")\n\n"
1413 		 << "  --benchmark-squelch BOOLEAN\n"
1414 		 << "    Set modem squelch\n"
1415 		 << "    Default: " << benchmark.sql
1416 		 << " (" << boolalpha << benchmark.sql << noboolalpha << ")\n\n"
1417 		 << "  --benchmark-squelch-level LEVEL\n"
1418 		 << "    Set modem squelch level\n"
1419 		 << "    Default: " << benchmark.sqlevel << " (%)\n\n"
1420 		 << "  --benchmark-input INPUT\n"
1421 		 << "    Specify the input\n"
1422 		 << "    Must be a positive integer indicating the number of samples\n"
1423 		"    of silence to generate as the input"
1424 		", or a filename containing\n"
1425 		"    non-digit characters"
1426 		"\n\n"
1427 
1428 		 << "  --benchmark-output FILE\n"
1429 		 << "    Specify the output data file\n"
1430 		 << "    Default: decoder output is discarded\n\n"
1431 		 << "  --benchmark-src-ratio RATIO\n"
1432 		 << "    Specify the sample rate conversion ratio\n"
1433 		 << "    Default: 1.0 (input is not resampled)\n\n"
1434 		 << "  --benchmark-src-type TYPE\n"
1435 		 << "    Specify the sample rate conversion type\n"
1436 		 << "    Default: " << benchmark.src_type << " (" << src_get_name(benchmark.src_type) << ")\n\n"
1437 #endif
1438 
1439 		 << "  --cpu-speed-test\n"
1440 		 << "    Perform the CPU speed test, show results in the event log\n"
1441 		 << "    and possibly change options.\n\n"
1442 
1443 		 << "  --wfall-only\n"
1444 		 << "    Hide all controls but the waterfall\n\n"
1445 
1446 		 << "  --debug-level LEVEL\n"
1447 		 << "    Set the event log verbosity\n\n"
1448 
1449 		 << "  --version\n"
1450 		 << "    Print version information\n\n"
1451 
1452 		 << "  --build-info\n"
1453 		 << "    Print build information\n\n"
1454 
1455 		 << "  --help\n"
1456 		 << "    Print this option help\n\n";
1457 
1458 // Fl::help looks ugly so we'll write our own
1459 
1460 	help << "Standard FLTK options:\n\n"
1461 
1462 		 << "   -bg COLOR, -background COLOR\n"
1463 		 << "    Set the background color\n"
1464 
1465 		 << "   -bg2 COLOR, -background2 COLOR\n"
1466 		 << "    Set the secondary (text) background color\n\n"
1467 
1468 		 << "   -di DISPLAY, -display DISPLAY\n"
1469 		 << "    Set the X display to use DISPLAY,\n"
1470 		 << "    format is ``host:n.n''\n\n"
1471 
1472 		 << "   -dn, -dnd or -nodn, -nodnd\n"
1473 		 << "    Enable or disable drag and drop copy and paste in text fields\n\n"
1474 
1475 		 << "   -fg COLOR, -foreground COLOR\n"
1476 		 << "    Set the foreground color\n\n"
1477 
1478 		 << "   -g GEOMETRY, -geometry GEOMETRY\n"
1479 		 << "    Set the initial window size and position\n"
1480 		 << "    GEOMETRY format is ``WxH+X+Y''\n"
1481 		 << "    ** " << PACKAGE_NAME << " may override this setting **\n\n"
1482 
1483 		 << "   -i, -iconic\n"
1484 		 << "    Start " << PACKAGE_NAME << " in iconified state\n\n"
1485 
1486 		 << "   -k, -kbd or -nok, -nokbd\n"
1487 		 << "    Enable or disable visible keyboard focus in non-text widgets\n\n"
1488 
1489 		 << "   -na CLASSNAME, -name CLASSNAME\n"
1490 		 << "    Set the window class to CLASSNAME\n\n"
1491 
1492 		 << "   -ti WINDOWTITLE, -title WINDOWTITLE\n"
1493 		 << "    Set the window title\n\n";
1494 
1495 	help << "Additional UI options:\n\n"
1496 
1497 		 << "  --font FONT[:SIZE]\n"
1498 		 << "    Set the widget font and (optionally) size\n"
1499 		 << "    The default is: " << Fl::get_font(FL_HELVETICA)
1500 		 << ':' << FL_NORMAL_SIZE << "\n\n"
1501 
1502 		;
1503 
1504 	option_help = help.str();
1505 }
1506 
exit_cb(void *)1507 void exit_cb(void*) { fl_digi_main->do_callback(); }
1508 
parse_args(int argc,char ** argv,int & idx)1509 int parse_args(int argc, char **argv, int& idx)
1510 {
1511 	// Only handle long options
1512 	if (!(strlen(argv[idx]) >= 2 && strncmp(argv[idx], "--", 2) == 0)) {
1513 		// Store the window title. We may need this early in the initialisation
1514 		// process, before FLTK uses it to set the main window title.
1515 		if (argv_window_title.empty() && argc > idx &&
1516 		    (!strcmp(argv[idx], "-ti") || !strcmp(argv[idx], "-title")))
1517 			argv_window_title = argv[idx + 1];
1518 		else if (!strcmp(argv[idx], "-i") || !strcmp(argv[idx], "-iconic"))
1519 			iconified = true;
1520 		return 0;
1521 	}
1522 
1523 		enum { OPT_ZERO,
1524 #ifndef __WOE32__
1525 		   OPT_RX_IPC_KEY, OPT_TX_IPC_KEY,
1526 #endif
1527 		   OPT_HOME_DIR,
1528 		   OPT_CONFIG_DIR,
1529 		   OPT_ARQ_ADDRESS, OPT_ARQ_PORT,
1530 		   OPT_SHOW_CPU_CHECK,
1531 		   OPT_FLMSG_DIR,
1532 		   OPT_NBEMS_DIR,
1533 		   OPT_AUTOSEND_DIR,
1534 
1535 		   OPT_CONFIG_XMLRPC_ADDRESS, OPT_CONFIG_XMLRPC_PORT,
1536 		   OPT_CONFIG_XMLRPC_ALLOW, OPT_CONFIG_XMLRPC_DENY, OPT_CONFIG_XMLRPC_LIST,
1537 		   OPT_CONFIG_KISS_ADDRESS, OPT_CONFIG_KISS_PORT_IO, OPT_CONFIG_KISS_PORT_O,
1538 		   OPT_CONFIG_KISS_DUAL_PORT, OPT_ENABLE_IO_PORT,
1539 
1540 #if BENCHMARK_MODE
1541 		   OPT_BENCHMARK_MODEM, OPT_BENCHMARK_AFC, OPT_BENCHMARK_SQL, OPT_BENCHMARK_SQLEVEL,
1542 		   OPT_BENCHMARK_FREQ, OPT_BENCHMARK_INPUT, OPT_BENCHMARK_OUTPUT,
1543 		   OPT_BENCHMARK_SRC_RATIO, OPT_BENCHMARK_SRC_TYPE,
1544 #endif
1545 
1546 			   OPT_FONT, OPT_WFALL_HEIGHT,
1547 			   OPT_WINDOW_WIDTH, OPT_WINDOW_HEIGHT, OPT_WFALL_ONLY,
1548 			   OPT_RX_ONLY,
1549 #if USE_PORTAUDIO
1550 			   OPT_FRAMES_PER_BUFFER,
1551 #endif
1552 		   OPT_DEBUG_LEVEL,
1553 			   OPT_EXIT_AFTER,
1554 			   OPT_DEPRECATED, OPT_HELP, OPT_VERSION, OPT_BUILD_INFO };
1555 
1556 	static const char shortopts[] = ":";
1557 	static const struct option longopts[] = {
1558 #ifndef __WOE32__
1559 		{ "rx-ipc-key",	   1, 0, OPT_RX_IPC_KEY },
1560 		{ "tx-ipc-key",	   1, 0, OPT_TX_IPC_KEY },
1561 #endif
1562 		{ "home-dir",	   1, 0, OPT_HOME_DIR },
1563 		{ "config-dir",	   1, 0, OPT_CONFIG_DIR },
1564 
1565 		{ "arq-server-address", 1, 0, OPT_ARQ_ADDRESS },
1566 		{ "arq-server-port",    1, 0, OPT_ARQ_PORT },
1567 		{ "flmsg-dir", 1, 0, OPT_FLMSG_DIR },
1568 		{ "nbems-dir", 1, 0, OPT_NBEMS_DIR },
1569 		{ "auto-dir", 1, 0, OPT_AUTOSEND_DIR },
1570 
1571 		{ "cpu-speed-test", 0, 0, OPT_SHOW_CPU_CHECK },
1572 
1573 		{ "enable-io-port",   1, 0, OPT_ENABLE_IO_PORT },
1574 
1575 		{ "kiss-server-address",   1, 0, OPT_CONFIG_KISS_ADDRESS },
1576 		{ "kiss-server-port-io",   1, 0, OPT_CONFIG_KISS_PORT_IO },
1577 		{ "kiss-server-port-o",    1, 0, OPT_CONFIG_KISS_PORT_O },
1578 		{ "kiss-server-dual-port", 1, 0, OPT_CONFIG_KISS_DUAL_PORT },
1579 
1580 		{ "xmlrpc-server-address", 1, 0, OPT_CONFIG_XMLRPC_ADDRESS },
1581 		{ "xmlrpc-server-port",    1, 0, OPT_CONFIG_XMLRPC_PORT },
1582 		{ "xmlrpc-allow",          1, 0, OPT_CONFIG_XMLRPC_ALLOW },
1583 		{ "xmlrpc-deny",           1, 0, OPT_CONFIG_XMLRPC_DENY },
1584 		{ "xmlrpc-list",           0, 0, OPT_CONFIG_XMLRPC_LIST },
1585 
1586 #if BENCHMARK_MODE
1587 		{ "benchmark-modem", 1, 0, OPT_BENCHMARK_MODEM },
1588 		{ "benchmark-frequency", 1, 0, OPT_BENCHMARK_FREQ },
1589 		{ "benchmark-afc", 1, 0, OPT_BENCHMARK_AFC },
1590 		{ "benchmark-squelch", 1, 0, OPT_BENCHMARK_SQL },
1591 		{ "benchmark-squelch-level", 1, 0, OPT_BENCHMARK_SQLEVEL },
1592 		{ "benchmark-input", 1, 0, OPT_BENCHMARK_INPUT },
1593 		{ "benchmark-output", 1, 0, OPT_BENCHMARK_OUTPUT },
1594 		{ "benchmark-src-ratio", 1, 0, OPT_BENCHMARK_SRC_RATIO },
1595 		{ "benchmark-src-type", 1, 0, OPT_BENCHMARK_SRC_TYPE },
1596 #endif
1597 
1598 		{ "font",	   1, 0, OPT_FONT },
1599 
1600 		{ "wfall-height",  1, 0, OPT_WFALL_HEIGHT },
1601 		{ "window-width",  1, 0, OPT_WINDOW_WIDTH },
1602 		{ "window-height", 1, 0, OPT_WINDOW_HEIGHT },
1603 		{ "wfall-only",    0, 0, OPT_WFALL_ONLY },
1604 		{ "wo",            0, 0, OPT_WFALL_ONLY },
1605 		{ "rx-only",       0, 0, OPT_RX_ONLY },
1606 		{ "ro",            0, 0, OPT_RX_ONLY },
1607 
1608 #if USE_PORTAUDIO
1609 		{ "frames-per-buffer",1, 0, OPT_FRAMES_PER_BUFFER },
1610 #endif
1611 		{ "exit-after",    1, 0, OPT_EXIT_AFTER },
1612 
1613 		{ "debug-level",   1, 0, OPT_DEBUG_LEVEL },
1614 
1615 		{ "help",	   0, 0, OPT_HELP },
1616 		{ "version",	   0, 0, OPT_VERSION },
1617 		{ "build-info",	   0, 0, OPT_BUILD_INFO },
1618 		{ 0 }
1619 	};
1620 
1621 	int longindex;
1622 	optind = idx;
1623 	opterr = 0;
1624 	int c = getopt_long(argc, argv, shortopts, longopts, &longindex);
1625 
1626 	switch (c) {
1627 		case -1:
1628 			return 0;
1629 		case 0:
1630 			// handle options with non-0 flag here
1631 			return 0;
1632 
1633 #if !defined(__WOE32__) && !defined(__APPLE__)
1634 		case OPT_RX_IPC_KEY: case OPT_TX_IPC_KEY:
1635 		{
1636 			errno = 0;
1637 			int key = strtol(optarg, NULL, (strncasecmp(optarg, "0x", 2) ? 10 : 16));
1638 			if (errno || key <= 0)
1639 				cerr << "Hmm, " << key << " doesn't look like a valid IPC key\n";
1640 			if (c == OPT_RX_IPC_KEY)
1641 				progdefaults.rx_msgid = key;
1642 			else
1643 				progdefaults.tx_msgid = key;
1644 		}
1645 			break;
1646 #endif
1647 
1648 		case OPT_HOME_DIR: {
1649 			char buf[FL_PATH_MAX + 1];
1650 			fl_filename_absolute(buf, sizeof(buf) - 1, optarg);
1651 			BaseDir = buf;
1652 		}
1653 			if (*BaseDir.rbegin() != '/')
1654 				   BaseDir += '/';
1655 			break;
1656 
1657 		case OPT_CONFIG_DIR: {
1658 			char buf[FL_PATH_MAX + 1];
1659 			fl_filename_absolute(buf, sizeof(buf) - 1, optarg);
1660 			HomeDir = buf;
1661 		}
1662 			if (*HomeDir.rbegin() != '/')
1663 				   HomeDir += '/';
1664 			break;
1665 
1666 		case OPT_ARQ_ADDRESS:
1667 			override_arq_address = optarg;
1668 			arq_address_override_flag = true;
1669 			break;
1670 		case OPT_ARQ_PORT:
1671 			override_arq_port = optarg;
1672 			arq_port_override_flag = true;
1673 			break;
1674 
1675 		case OPT_FLMSG_DIR:
1676 			FLMSG_dir_default = optarg;
1677 			break;
1678 
1679 		case OPT_NBEMS_DIR:
1680 			NBEMS_dir_default = optarg;
1681 			break;
1682 
1683 		case OPT_AUTOSEND_DIR:
1684 			FLMSG_WRAP_auto_dir = optarg;
1685 			break;
1686 
1687 		case OPT_ENABLE_IO_PORT:
1688 			if(optarg) {
1689 				switch(atoi(optarg)) {
1690 					case ARQ_IO:
1691 						progdefaults.data_io_enabled = ARQ_IO;
1692 						override_data_io_enabled = ARQ_IO;
1693 						arq_address_override_flag = true;
1694 						break;
1695 
1696 					case KISS_IO:
1697 						progdefaults.data_io_enabled = KISS_IO;
1698 						override_data_io_enabled = KISS_IO;
1699 						kiss_address_override_flag = true;
1700 						break;
1701 				}
1702 			}
1703 			break;
1704 
1705 		case OPT_CONFIG_KISS_ADDRESS:
1706 			progdefaults.kiss_address = optarg;
1707 			override_kiss_address = optarg;
1708 			kiss_address_override_flag = true;
1709 			break;
1710 		case OPT_CONFIG_KISS_PORT_IO:
1711 			progdefaults.kiss_io_port = optarg;
1712 			override_kiss_io_port = optarg;
1713 			kiss_address_override_flag = true;
1714 			break;
1715 		case OPT_CONFIG_KISS_PORT_O:
1716 			progdefaults.kiss_out_port = optarg;
1717 			override_kiss_out_port = optarg;
1718 			kiss_address_override_flag = true;
1719 			break;
1720 		case OPT_CONFIG_KISS_DUAL_PORT:
1721 			if((optarg) && atoi(optarg)) {
1722 				progdefaults.kiss_dual_port_enabled = true;
1723 				override_kiss_dual_port_enabled = true;
1724 				kiss_address_override_flag = true;
1725 			} else {
1726 				progdefaults.kiss_dual_port_enabled = false;
1727 				override_kiss_dual_port_enabled = false;
1728 				kiss_address_override_flag = true;
1729 			}
1730 			break;
1731 
1732 		case OPT_CONFIG_XMLRPC_ADDRESS:
1733 			override_xmlrpc_address = optarg;
1734 			xmlrpc_address_override_flag = true;
1735 			break;
1736 		case OPT_CONFIG_XMLRPC_PORT:
1737 			override_xmlrpc_port = optarg;
1738 			xmlrpc_port_override_flag = true;
1739 			break;
1740 		case OPT_CONFIG_XMLRPC_ALLOW:
1741 			progdefaults.xmlrpc_allow = optarg;
1742 			break;
1743 		case OPT_CONFIG_XMLRPC_DENY:
1744 			if (!progdefaults.xmlrpc_allow.empty())
1745 				cerr << "W: --" << longopts[longindex].name
1746 					 << " cannot be used together with --"
1747 					 << longopts[OPT_CONFIG_XMLRPC_ALLOW-1].name
1748 					 << " and will be ignored\n";
1749 			else
1750 				progdefaults.xmlrpc_deny = optarg;
1751 			break;
1752 		case OPT_CONFIG_XMLRPC_LIST:
1753 			XML_RPC_Server::list_methods(cout);
1754 			exit(EXIT_SUCCESS);
1755 
1756 #if BENCHMARK_MODE
1757 		case OPT_BENCHMARK_MODEM:
1758 			benchmark.modem = strtol(optarg, NULL, 10);
1759 			if (!(benchmark.modem >= 0 && benchmark.modem < NUM_MODES)) {
1760 				fatal_error(_("Bad modem id"));
1761 			}
1762 			break;
1763 
1764 		case OPT_BENCHMARK_FREQ:
1765 			benchmark.freq = strtol(optarg, NULL, 10);
1766 			if (benchmark.freq < 0) {
1767 				fatal_error(_("Bad frequency"));
1768 			}
1769 			break;
1770 
1771 		case OPT_BENCHMARK_AFC:
1772 			benchmark.afc = strtol(optarg, NULL, 10);
1773 			break;
1774 
1775 		case OPT_BENCHMARK_SQL:
1776 			benchmark.sql = strtol(optarg, NULL, 10);
1777 			break;
1778 
1779 		case OPT_BENCHMARK_SQLEVEL:
1780 			benchmark.sqlevel = strtod(optarg, NULL);
1781 			break;
1782 
1783 		case OPT_BENCHMARK_INPUT:
1784 			benchmark.input = optarg;
1785 			break;
1786 
1787 		case OPT_BENCHMARK_OUTPUT:
1788 			benchmark.output = optarg;
1789 			break;
1790 
1791 		case OPT_BENCHMARK_SRC_RATIO:
1792 			benchmark.src_ratio = strtod(optarg, NULL);
1793 			break;
1794 
1795 		case OPT_BENCHMARK_SRC_TYPE:
1796 			benchmark.src_type = strtol(optarg, NULL, 10);
1797 			break;
1798 #endif
1799 
1800 		case OPT_FONT:
1801 		{
1802 			char *p;
1803 			if ((p = strchr(optarg, ':'))) {
1804 				*p = '\0';
1805 				FL_NORMAL_SIZE = strtol(p + 1, 0, 10);
1806 			}
1807 		}
1808 			Fl::set_font(FL_HELVETICA, optarg);
1809 			break;
1810 
1811 //		case OPT_WFALL_HEIGHT:
1812 //			progdefaults.wfheight = strtol(optarg, NULL, 10);
1813 //			break;
1814 
1815 //		case OPT_WINDOW_WIDTH:
1816 //			WNOM = strtol(optarg, NULL, 10);
1817 //			break;
1818 
1819 //		case OPT_WINDOW_HEIGHT:
1820 //			HNOM = strtol(optarg, NULL, 10);
1821 //			break;
1822 
1823 #if USE_PORTAUDIO
1824 		case OPT_FRAMES_PER_BUFFER:
1825 			progdefaults.PortFramesPerBuffer = strtol(optarg, 0, 10);
1826 			break;
1827 #endif // USE_PORTAUDIO
1828 
1829 		case OPT_EXIT_AFTER:
1830 			Fl::add_timeout(strtod(optarg, 0), exit_cb);
1831 			break;
1832 
1833 		case OPT_WFALL_ONLY:
1834 			bWF_only = true;
1835 			break;
1836 
1837 		case OPT_RX_ONLY:
1838 			rx_only = true;
1839 			break;
1840 
1841 		case OPT_SHOW_CPU_CHECK:
1842 			show_cpucheck = true;
1843 			break;
1844 
1845 		case OPT_DEBUG_LEVEL:
1846 		{
1847 			int v = strtol(optarg, 0, 10);
1848 			debug::level = (debug::level_e)CLAMP(v, 0, debug::LOG_NLEVELS-1);
1849 		}
1850 			break;
1851 
1852 		case OPT_DEPRECATED:
1853 			cerr << "W: the --" << longopts[longindex].name
1854 				 << " option has been deprecated and will be removed in a future version\n";
1855 			break;
1856 
1857 		case OPT_HELP:
1858 			cout << option_help;
1859 			exit(EXIT_SUCCESS);
1860 
1861 		case OPT_VERSION:
1862 			cout << version_text;
1863 			exit(EXIT_SUCCESS);
1864 
1865 		case OPT_BUILD_INFO:
1866 			cout << build_text;
1867 			exit(EXIT_SUCCESS);
1868 
1869 		case '?': case ':': default:
1870 			arg_error(argv[0], argv[idx], (c == ':'));
1871 	}
1872 
1873 	// Increment idx by the number of args we used and return that number.
1874 	// We must check whether the option argument is in the same argv element
1875 	// as the option name itself, i.e., --opt=arg.
1876 		c = longopts[longindex].has_arg ? 2 : 1;
1877 		if (c == 2) {
1878 				string arg = argv[idx];
1879 				string::size_type p;
1880 				if ((p = arg.rfind(optarg)) != string::npos && arg[p-1] == '=')
1881 						c = 1;
1882 		}
1883 	idx += c;
1884 	return c;
1885 }
1886 
generate_version_text(void)1887 void generate_version_text(void)
1888 {
1889 	version_text.assign(PACKAGE_STRING "\nCopyright (C) 2007-2010 " PACKAGE_AUTHORS ".\n");
1890 	version_text.append(_("License GPLv3+: GNU GPL version 3 or later "
1891 				  "<http://www.gnu.org/licenses/gpl-3.0.html>\n"
1892 				  "This is free software: you are free to change and redistribute it.\n"
1893 				  "There is NO WARRANTY, to the extent permitted by law.\n"));
1894 
1895 	ostringstream s;
1896 	s << "Build information:\n";
1897 	s << "  built          : " << BUILD_DATE << " by " << BUILD_USER
1898 	  << '@' << BUILD_HOST << " on " << BUILD_BUILD_PLATFORM
1899 	  << " for " << BUILD_TARGET_PLATFORM << "\n\n"
1900 	  << "  configure flags: " << BUILD_CONFIGURE_ARGS << "\n\n"
1901 	  << "  compiler       : " << BUILD_COMPILER << "\n\n"
1902 	  << "  compiler flags : " << FLDIGI_BUILD_CXXFLAGS << "\n\n"
1903 	  << "  linker flags   : " << FLDIGI_BUILD_LDFLAGS << "\n\n"
1904 
1905 	  << "  libraries      : " "FLTK " FLTK_BUILD_VERSION "\n"
1906 	  << "                   " "libsamplerate " << SAMPLERATE_BUILD_VERSION "\n";
1907 	s << "                   " "libsndfile " << SNDFILE_BUILD_VERSION "\n";
1908 #if USE_PORTAUDIO
1909 	s << "                   " "PortAudio " << PORTAUDIO_BUILD_VERSION "\n";
1910 #endif
1911 #if USE_PULSEAUDIO
1912 	s << "                   " "PulseAudio " << PULSEAUDIO_BUILD_VERSION "\n";
1913 #endif
1914 #if USE_HAMLIB
1915 	s << "                   " "Hamlib " << HAMLIB_BUILD_VERSION "\n";
1916 #endif
1917 
1918 	s << "\nRuntime information:\n";
1919 		struct utsname u;
1920 		if (uname(&u) != -1) {
1921 		s << "  system         : " << u.sysname << ' ' << u.nodename
1922 		  << ' ' << u.release << ' ' << u.version << ' ' << u.machine << "\n\n";
1923 	}
1924 
1925 	s << "  libraries      : " << src_get_version() << '\n';
1926 	char sndfile_version[32];
1927 	sf_command(NULL, SFC_GET_LIB_VERSION, sndfile_version, sizeof(sndfile_version));
1928 	s << "                   " << sndfile_version << '\n';
1929 #if USE_PORTAUDIO
1930 	s << "                   " << Pa_GetVersionText() << ' ' << Pa_GetVersion() << '\n';
1931 #endif
1932 #if USE_PULSEAUDIO
1933 	s << "                   " << "Pulseaudio " << pa_get_library_version() << '\n';
1934 #endif
1935 #if USE_HAMLIB
1936 	s << "                   " << hamlib_version << '\n';
1937 #endif
1938 
1939 	build_text = s.str();
1940 }
1941 
1942 // When debugging is enabled, reexec with malloc debugging hooks enabled, unless
1943 // the env var FLDIGI_NO_EXEC is set, or our parent process is gdb.
debug_exec(char ** argv)1944 void debug_exec(char** argv)
1945 {
1946 #if !defined(NDEBUG) && defined(__GLIBC__)
1947 		if (getenv("FLDIGI_NO_EXEC"))
1948 				return;
1949 
1950 	char ppath[32], lname[32];
1951 	ssize_t n;
1952 	snprintf(ppath, sizeof(ppath), "/proc/%u/exe", getppid());
1953 	if ((n = readlink(ppath, lname, sizeof(lname))) > 0) {
1954 		lname[n] = '\0';
1955 		if (strstr(lname, "gdb")) {
1956 						cerr << "Not using malloc debugging hooks\n";
1957 						return;
1958 				}
1959 	}
1960 
1961 		setenv("FLDIGI_NO_EXEC", "1", 0);
1962 		setenv("MALLOC_CHECK_", "3", 0);
1963 		setenv("MALLOC_PERTURB_", "42", 0);
1964 		if (execvp(*argv, argv) == -1)
1965 				perror("execvp");
1966 #endif
1967 }
1968 
set_platform_ui(void)1969 void set_platform_ui(void)
1970 {
1971 #if defined(__APPLE__)
1972 	   FL_NORMAL_SIZE = 12;
1973 	   progdefaults.WaterfallFontsize = 12;
1974 	   progdefaults.RxFontsize = 12;
1975 	   progdefaults.TxFontsize = 12;
1976 #elif defined(__WOE32__)
1977 	   Fl::set_font(FL_HELVETICA, "Tahoma");
1978 	   FL_NORMAL_SIZE = 11;
1979 	   progdefaults.WaterfallFontnbr = FL_HELVETICA;
1980 	   progdefaults.WaterfallFontsize = 12;
1981 	   progdefaults.RxFontsize = 12;
1982 	   progdefaults.TxFontsize = 12;
1983 #else
1984 	   FL_NORMAL_SIZE = 12;
1985 #endif
1986 }
1987 
1988 // Convert 1 second of 1-channel silence from IN_RATE Hz to OUT_RATE Hz,
1989 // Repeat test "repeat" times. Return (repeat / elapsed_time),
1990 // the faster-than-realtime factor averaged over "repeat" runs.
1991 // Some figures for SRC_SINC_FASTEST:
1992 // Pentium 4 2.8GHz:     70
1993 // Pentium 3 550MHz:     13
1994 // UltraSparc II 270MHz: 3.5
1995 // Atom N280 1.66GHz:    17.7
1996 #define IN_RATE 48000
1997 #define OUT_RATE 8000
speed_test(int converter,unsigned repeat)1998 double speed_test(int converter, unsigned repeat)
1999 {
2000 	float input_frames[IN_RATE];
2001 	float output_frames[OUT_RATE];
2002 
2003 	SRC_DATA src;
2004 
2005 	src.src_ratio = (double)OUT_RATE / IN_RATE;
2006 	src.input_frames = IN_RATE;
2007 	src.output_frames = OUT_RATE;
2008 	src.data_in = &input_frames[0];
2009 	src.data_out = &output_frames[0];
2010 
2011 	memset(input_frames, 0, sizeof(input_frames));
2012 
2013 	// warm up
2014 	src_simple(&src, converter, 1);
2015 
2016 	struct timespec t0, t1;
2017 #ifdef _POSIX_MONOTONIC_CLOCK
2018 	clock_gettime(CLOCK_MONOTONIC, &t0);
2019 #else
2020 	clock_gettime(CLOCK_REALTIME, &t0);
2021 #endif
2022 	for (unsigned i = 0; i < repeat; i++)
2023 		src_simple(&src, converter, 1);
2024 #ifdef _POSIX_MONOTONIC_CLOCK
2025 	clock_gettime(CLOCK_MONOTONIC, &t1);
2026 #else
2027 	clock_gettime(CLOCK_REALTIME, &t1);
2028 #endif
2029 
2030 	t0 = t1 - t0;
2031 	return repeat / (t0.tv_sec + t0.tv_nsec/1e9);
2032 }
2033 
setup_signal_handlers(void)2034 static void setup_signal_handlers(void)
2035 {
2036 #ifndef __WOE32__
2037 	struct sigaction action;
2038 	memset(&action, 0, sizeof(struct sigaction));
2039 
2040 	// no child stopped notifications, no zombies
2041 	action.sa_handler = SIG_DFL;
2042 	action.sa_flags = SA_NOCLDSTOP;
2043 #ifdef SA_NOCLDWAIT
2044 	action.sa_flags |= SA_NOCLDWAIT;
2045 #endif
2046 	sigaction(SIGCHLD, &action, NULL);
2047 	action.sa_flags = 0;
2048 
2049 	action.sa_handler = handle_signal;
2050 	sigaction(SIGSEGV, &action, NULL);
2051 	sigaction(SIGILL, &action, NULL);
2052 	sigaction(SIGABRT, &action, NULL);
2053 	sigaction(SIGUSR2, &action, NULL);
2054 
2055 	action.sa_handler = SIG_IGN;
2056 	sigaction(SIGPIPE, &action, NULL);
2057 
2058 	sigemptyset(&action.sa_mask);
2059 	sigaddset(&action.sa_mask, SIGUSR2);
2060 	pthread_sigmask(SIG_BLOCK, &action.sa_mask, NULL);
2061 #else
2062 	signal(SIGSEGV, handle_signal);
2063 	signal(SIGILL, handle_signal);
2064 	signal(SIGABRT, handle_signal);
2065 #endif
2066 }
2067 
2068 // Show an error dialog and print to cerr if available.
2069 // On win32 Fl::fatal displays its own error window.
fatal_error(string sz_error)2070 static void fatal_error(string sz_error)
2071 {
2072 	string s = "Fatal error!\n";
2073 	s.append(sz_error).append("\n").append(strerror(errno));
2074 
2075 // Win32 will display a MessageBox error message
2076 #if !defined(__WOE32__)
2077 	fl_message_font(FL_HELVETICA, FL_NORMAL_SIZE);
2078 	fl_alert2("%s", s.c_str());
2079 #endif
2080 	Fl::fatal(s.c_str());
2081 }
2082 
checkdirectories(void)2083 static void checkdirectories(void)
2084 {
2085 	struct DIRS {
2086 		string& dir;
2087 		const char* suffix;
2088 		void (*new_dir_func)(void);
2089 	};
2090 	DIRS fldigi_dirs[] = {
2091 		{ HomeDir, 0, 0 },
2092 		{ RigsDir, "rigs", 0 },
2093 		{ ScriptsDir, "scripts", 0 },
2094 		{ PalettesDir, "palettes", create_new_palettes },
2095 		{ LogsDir, "logs", 0 },
2096 		{ PicsDir, "images", 0 },
2097 		{ AvatarDir, "avatars", 0},
2098 		{ HelpDir, "help", 0 },
2099 		{ MacrosDir, "macros", create_new_macros },
2100 		{ AnalysisDir, "analysis", 0 },
2101 		{ FMTDir, "fmt", 0 },
2102 		{ WrapDir, "wrap", 0 },
2103 		{ TalkDir, "talk", 0 },
2104 		{ TempDir, "temp", 0 },
2105 		{ LoTWDir, "LOTW", 0 },
2106 		{ KmlDir, "kml", 0 },
2107 		{ DATA_dir, "data", 0 },
2108 		{ DebugDir, "debug", 0 }
2109 	};
2110 
2111 	int r;
2112 	for (size_t i = 0; i < sizeof(fldigi_dirs)/sizeof(*fldigi_dirs); i++) {
2113 		if (fldigi_dirs[i].suffix)
2114 			fldigi_dirs[i].dir.assign(HomeDir).append(fldigi_dirs[i].suffix).append(PATH_SEP);
2115 		r  = mkdir(fldigi_dirs[i].dir.c_str(), 0777);
2116 		if (r == -1 && errno != EEXIST) {
2117 			string s = _("Could not make directory ");
2118 			s.append(fldigi_dirs[i].dir);
2119 			fatal_error(s);
2120 		}
2121 		else if (r == 0 && fldigi_dirs[i].new_dir_func)
2122 			fldigi_dirs[i].new_dir_func();
2123 	}
2124 
2125 }
2126 
2127 bool nbems_dirs_checked = false;
2128 
check_nbems_dirs(void)2129 void check_nbems_dirs(void)
2130 {
2131 	if (nbems_dirs_checked) return;
2132 
2133 	struct DIRS {
2134 		string& dir;
2135 		const char* suffix;
2136 		void (*new_dir_func)(void);
2137 	};
2138 	DIRS NBEMS_dirs[] = {
2139 		{ NBEMS_dir,     0, 0 },
2140 		{ ARQ_dir,       "ARQ", 0 },
2141 		{ ARQ_files_dir, "ARQ/files", 0 },
2142 		{ ARQ_recv_dir,  "ARQ/recv", 0 },
2143 		{ ARQ_send,      "ARQ/send", 0 },
2144 		{ WRAP_dir,      "WRAP", 0 },
2145 		{ WRAP_recv_dir, "WRAP/recv", 0 },
2146 		{ WRAP_send_dir, "WRAP/send", 0 },
2147 		{ WRAP_auto_dir, "WRAP/auto", 0 },
2148 		{ ICS_dir,       "ICS", 0 },
2149 		{ ICS_msg_dir,   "ICS/messages", 0 },
2150 		{ ICS_tmp_dir,   "ICS/templates", 0 },
2151 	};
2152 
2153 	int r;
2154 	for (size_t i = 0; i < sizeof(NBEMS_dirs)/sizeof(*NBEMS_dirs); i++) {
2155 		if (NBEMS_dirs[i].suffix)
2156 			NBEMS_dirs[i].dir.assign(NBEMS_dir).append(NBEMS_dirs[i].suffix).append(PATH_SEP);
2157 
2158 		if ((r = mkdir(NBEMS_dirs[i].dir.c_str(), 0777)) == -1 && errno != EEXIST) {
2159 			string s = _("Could not make directory ");
2160 			s.append(NBEMS_dirs[i].dir).append(", ").append(strerror(errno));
2161 			fatal_error(s);
2162 		}
2163 		else if (r == 0 && NBEMS_dirs[i].new_dir_func)
2164 			NBEMS_dirs[i].new_dir_func();
2165 	}
2166 
2167 	DIRS FLMSG_dirs[] = {
2168 		{ FLMSG_dir,               0, 0 },
2169 		{ FLMSG_WRAP_dir,          "WRAP", 0 },
2170 		{ FLMSG_WRAP_recv_dir,     "WRAP/recv", 0 },
2171 		{ FLMSG_WRAP_send_dir,     "WRAP/send", 0 },
2172 		{ FLMSG_WRAP_auto_dir,     "WRAP/auto", 0 },
2173 		{ FLMSG_ICS_dir,           "ICS", 0 },
2174 		{ FLMSG_ICS_msg_dir,       "ICS/messages", 0 },
2175 		{ FLMSG_ICS_tmp_dir,       "ICS/templates", 0 },
2176 	};
2177 
2178 	for (size_t i = 0; i < sizeof(FLMSG_dirs)/sizeof(*FLMSG_dirs); i++) {
2179 		if (FLMSG_dirs[i].dir.empty() && FLMSG_dirs[i].suffix)
2180 			FLMSG_dirs[i].dir.assign(FLMSG_dir).append(FLMSG_dirs[i].suffix).append("/");
2181 
2182 		if ((r = mkdir(FLMSG_dirs[i].dir.c_str(), 0777)) == -1 && errno != EEXIST) {
2183 			string s = _("Could not make directory ");
2184 			s.append(FLMSG_dirs[i].dir);
2185 			fatal_error(s);
2186 		}
2187 		else if (r == 0 && FLMSG_dirs[i].new_dir_func)
2188 			FLMSG_dirs[i].new_dir_func();
2189 	}
2190 
2191 	nbems_dirs_checked = true;
2192 }
2193 
check_data_dir(void)2194 void check_data_dir(void)
2195 {
2196 	if (mkdir(DATA_dir.c_str(), 0777) == -1 && errno != EEXIST) {
2197 		string s = _("Could not make directory ");
2198 		s.append(DATA_dir);
2199 		fatal_error(s);
2200 	}
2201 }
2202 
2203 // Print an error message and exit.
arg_error(const char * name,const char * arg,bool missing)2204 static void arg_error(const char* name, const char* arg, bool missing)
2205 {
2206 	ostringstream msg;
2207 	msg << name << ": ";
2208 	if (arg && *arg) {
2209 		if (missing)
2210 			msg << "option '" << arg << "' requires an argument\n";
2211 		else
2212 			msg << "unrecognized option '" << arg << "'\n";
2213 	}
2214 	else
2215 		msg << "error while parsing command line\n";
2216 
2217 	msg << "See command line help for more information.";
2218 
2219 	fatal_error(msg.str());
2220 }
2221 
2222 /// Sets or resets the KML parameters, and loads existing files.
kml_init(bool load_files)2223 void kml_init(bool load_files)
2224 {
2225 	if (progdefaults.kml_enabled == false) return; // disabled kml service
2226 
2227 	KmlServer::GetInstance()->InitParams(
2228 			progdefaults.kml_command,
2229 			progdefaults.kml_save_dir,
2230 			(double)progdefaults.kml_merge_distance,
2231 			progdefaults.kml_retention_time,
2232 			progdefaults.kml_refresh_interval,
2233 			progdefaults.kml_balloon_style);
2234 
2235 	if (load_files)
2236 		KmlServer::GetInstance()->ReloadKmlFiles();
2237 
2238 	/// TODO: Should do this only when the locator has changed.
2239 	try {
2240 		/// One special KML object for the user.
2241 		CoordinateT::Pair myCoo( progdefaults.myLocator );
2242 
2243 		/// TODO: Fix this: It does not seem to create a polyline when changing the locator.
2244 		KmlServer::CustomDataT custData ;
2245 		custData.Push( "QTH", progdefaults.myQth );
2246 		custData.Push( "Locator", progdefaults.myLocator );
2247 		custData.Push( "Antenna", progdefaults.myAntenna );
2248 		custData.Push( "Name", progdefaults.myName );
2249 
2250 		KmlServer::GetInstance()->Broadcast(
2251 			"User",
2252 			KmlServer::UniqueEvent,
2253 			myCoo,
2254 			0.0, // Altitude.
2255 			progdefaults.myCall,
2256 			progdefaults.myLocator,
2257 			progdefaults.myQth,
2258 			custData );
2259 	}
2260 	catch( const std::exception & exc ) {
2261 
2262 ;//		LOG_WARN("Cannot publish user position:%s", exc.what() );
2263 	}
2264 }
2265 
2266 /// Tests if a directory exists.
directory_is_created(const char * strdir)2267 int directory_is_created( const char * strdir )
2268 {
2269 	DIR *dir = opendir(strdir);
2270 	if (dir) {
2271 		closedir(dir);
2272 		return true;
2273 	}
2274 	return false;
2275 }
2276 
2277