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