From 3b8100721032397f507b93c059d3dec0365c7eb0 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 19 May 2018 10:56:43 +0200 Subject: Refactoring command line option handling * Use own function to define and parse command line options * Command line options can be defined by config file also * --limesdr allows to auto-set required SDR option for LimeSDR --- .gitignore | 1 + configure.ac | 1 + docs/sdr.html | 14 +- src/Makefile.am | 1 + src/amps/Makefile.am | 1 + src/amps/amps_tacs_main.c | 277 +++++++++++++++++----------------- src/anetz/Makefile.am | 1 + src/anetz/main.c | 125 +++++++--------- src/bnetz/Makefile.am | 2 + src/bnetz/dialer.c | 122 +++++++-------- src/bnetz/main.c | 112 +++++++------- src/cnetz/Makefile.am | 1 + src/cnetz/main.c | 356 +++++++++++++++++++++----------------------- src/jolly/Makefile.am | 1 + src/jolly/main.c | 122 +++++++-------- src/jtacs/Makefile.am | 1 + src/libdebug/debug.c | 1 + src/libdebug/debug.h | 45 +++--- src/libmobile/main_mobile.c | 206 ++++++++++--------------- src/libmobile/main_mobile.h | 6 +- src/liboptions/Makefile.am | 7 + src/liboptions/options.c | 221 +++++++++++++++++++++++++++ src/liboptions/options.h | 6 + src/libsdr/sdr.c | 1 - src/libsdr/sdr_config.c | 110 ++++++-------- src/libsdr/sdr_config.h | 5 +- src/nmt/Makefile.am | 1 + src/nmt/main.c | 262 +++++++++++++++----------------- src/r2000/Makefile.am | 1 + src/r2000/main.c | 317 ++++++++++++++++++--------------------- src/radio/Makefile.am | 1 + src/radio/main.c | 277 +++++++++++++++------------------- src/tacs/Makefile.am | 1 + src/test/Makefile.am | 2 + src/test/test_dms.c | 2 +- src/tv/Makefile.am | 1 + src/tv/main.c | 251 +++++++++++++------------------ 37 files changed, 1426 insertions(+), 1436 deletions(-) create mode 100644 src/liboptions/Makefile.am create mode 100644 src/liboptions/options.c create mode 100644 src/liboptions/options.h diff --git a/.gitignore b/.gitignore index 574eb1f..a7bab40 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ compile .libs .dirstamp m4 +src/liboptions/liboptions.a src/libdebug/libdebug.a src/libmobile/libmobile.a src/libdisplay/libdisplay.a diff --git a/configure.ac b/configure.ac index aa34386..c93de4a 100644 --- a/configure.ac +++ b/configure.ac @@ -49,6 +49,7 @@ AS_IF([test "x$with_imagemagick" == "xyes"],[AC_MSG_NOTICE( Compiling with Image AS_IF([test "x$with_alsa" != "xyes" -a "x$with_sdr" != "xyes"],[AC_MSG_FAILURE( Without sound nor SDR support this project does not make sense. Please support sound card for analog transceivers or better SDR!" )],[]) AC_OUTPUT( + src/liboptions/Makefile src/libdebug/Makefile src/libmobile/Makefile src/libdisplay/Makefile diff --git a/docs/sdr.html b/docs/sdr.html index c4b3bf5..09bded1 100644 --- a/docs/sdr.html +++ b/docs/sdr.html @@ -67,7 +67,7 @@ LimeSDR

-If you have this device, you need to install the SoapySDR, then the LimeSuit and finally run configure with Osmocom Analog, compile and install. +If you have this device, you need to install the SoapySDR, then the LimeSuite and finally run configure with Osmocom Analog, compile and install. Run Osmocom Analog with --help again, and you should see a bunch of option for SDR. In case of B-Netz, I use the following parameters:

@@ -75,14 +75,21 @@ In case of B-Netz, I use the following parameters:
 
 # bnetz --sdr-soapy \
-        --sdr-tx-gain 50 \
+	--sdr-rx-antenna LNAL \
         --sdr-rx-gain 30 \
+        --sdr-tx-gain 30 \
         --sdr-samplerate 5000000 \
         -s 100000 \
         -k 17
 
 
+

+Be sure to select the right RX antenna input. +The frequencies we use require the low frequency filter network, so I suggest to connect your antenna to RX_1_L and select "--sdr-rx-antenna LNAL". +Different versions of LimeSuite have different default antenna inputs, so be sure to set your RX antenna. +

+

In order to change from analog sound card to SDR, you need --sdr-soapy option. In my setup I use antennas directly connected to the SDR. @@ -170,7 +177,8 @@ Because C-Netz uses only odd channel numbers for 10 KHz spacing, we use channel

 
 # cnetz --sdr-soapy \
-        --sdr-rx-gain 50 \
+	--sdr-rx-antenna LNAL \
+        --sdr-rx-gain 30 \
 	--sdr-tx-gain 30 \
         --sdr-samplerate 5000000 \
 	-s 100000 \
diff --git a/src/Makefile.am b/src/Makefile.am
index aa068e1..4f1751f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,7 @@
 AUTOMAKE_OPTIONS = foreign
 
 SUBDIRS = \
+	liboptions \
 	libdebug \
 	libmobile \
 	libdisplay \
diff --git a/src/amps/Makefile.am b/src/amps/Makefile.am
index 9bc972f..73118e2 100644
--- a/src/amps/Makefile.am
+++ b/src/amps/Makefile.am
@@ -26,6 +26,7 @@ amps_SOURCES = \
 amps_LDADD = \
 	$(COMMON_LA) \
 	libamps.a \
+	$(top_builddir)/src/liboptions/liboptions.a \
 	$(top_builddir)/src/libdebug/libdebug.a \
 	$(top_builddir)/src/libmobile/libmobile.a \
 	$(top_builddir)/src/libdisplay/libdisplay.a \
diff --git a/src/amps/amps_tacs_main.c b/src/amps/amps_tacs_main.c
index 9601b10..df1dca0 100644
--- a/src/amps/amps_tacs_main.c
+++ b/src/amps/amps_tacs_main.c
@@ -19,13 +19,14 @@
 
 #include 
 #include 
-#include 
 #include 
 #include 
+#include 
 #include "../libsample/sample.h"
 #include "../libmobile/main_mobile.h"
 #include "../libdebug/debug.h"
 #include "../libmobile/call.h"
+#include "../liboptions/options.h"
 #include "amps.h"
 #include "dsp.h"
 #include "frame.h"
@@ -103,152 +104,132 @@ void print_help(const char *arg0)
 	main_mobile_print_hotkeys();
 }
 
-static int handle_options(int argc, char **argv)
+static void add_options(void)
+{
+	main_mobile_add_options();
+	option_add('T', "channel-type", 1);
+	option_add('F', "flip-polarity", 1);
+	option_add('P', "ms-power", 1);
+	option_add('D', "dtx", 1);
+	option_add('S', "sysinfo", 1);
+	option_add('O', "tolerant", 0);
+}
+
+static int handle_options(int short_option, int argi, char **argv)
 {
 	const char *p;
-	int skip_args = 0;
 	int rc;
 
-	static struct option long_options_special[] = {
-		{"channel-type", 1, 0, 'T'},
-		{"flip-polarity", 1, 0, 'F'},
-		{"ms-power", 1, 0, 'P'},
-		{"dtx", 1, 0, 'D'},
-		{"sysinfo", 1, 0, 'S'},
-		{"tolerant", 0, 0, 'O'},
-		{0, 0, 0, 0}
-	};
-
-	main_mobile_set_options("T:F:P:D:S:O", long_options_special);
-
-	while (1) {
-		int option_index = 0, c;
-
-		c = getopt_long(argc, argv, optstring, long_options, &option_index);
-
-		if (c == -1)
-			break;
-
-		switch (c) {
-		case 'T':
-			if (!strcmp(optarg, "list")) {
-				amps_channel_list();
-				exit(0);
-			}
-			rc = amps_channel_by_short_name(optarg);
-			if (rc < 0) {
-				fprintf(stderr, "Error, channel type '%s' unknown. Please use '-t list' to get a list. I suggest to use the default.\n", optarg);
-				exit(0);
-			}
-			OPT_ARRAY(num_chan_type, chan_type, rc)
-			skip_args += 2;
-			break;
-		case 'F':
-			if (!strcasecmp(optarg, "no"))
-				flip_polarity = "no";
-			else if (!strcasecmp(optarg, "yes"))
-				flip_polarity = "yes";
-			else {
-				fprintf(stderr, "Given polarity '%s' is illegal, see help!\n", optarg);
-				exit(0);
-			}
-			skip_args += 2;
-			break;
-		case 'P':
-			ms_power = atoi(optarg);
-			if (ms_power > 7)
-				ms_power = 7;
-			if (ms_power < 0)
-				ms_power = 0;
-			skip_args += 2;
-			break;
-		case 'D':
-			dtx = atoi(optarg);
-			if (dtx > 3)
-				dtx = 3;
-			if (dtx < 0)
-				dtx = 0;
-			skip_args += 2;
-			break;
-		case 'S':
-			p = strchr(optarg, '=');
-			if (!p) {
-				fprintf(stderr, "Given sysinfo parameter '%s' requires '=' character to set value, see help!\n", optarg);
-				exit(0);
-			}
-			p++;
-			if (!strncasecmp(optarg, "sid=", p - optarg)
-			 || !strncasecmp(optarg, "aid=", p - optarg)) {
-				if (!strcasecmp(p, "list")) {
-					list_stations();
-					exit(0);
-				}
-				sid = atoi(p);
-				if (sid > 32767)
-					sid = 32767;
-				if (sid < 0)
-					sid = 0;
-			} else
-			if (!strncasecmp(optarg, "dcc=", p - optarg)) {
-				dcc = atoi(p);
-				if (dcc > 3)
-					dcc = 3;
-				if (dcc < 0)
-					dcc = 0;
-			} else
-			if (!strncasecmp(optarg, "scc=", p - optarg)) {
-				scc = atoi(p);
-				if (scc > 2)
-					scc = 2;
-				if (scc < 0)
-					scc = 0;
-			} else
-			if (!strncasecmp(optarg, "regincr=", p - optarg)) {
-				regincr = atoi(p);
-			} else
-			if (!strncasecmp(optarg, "pureg=", p - optarg)) {
-				pureg = atoi(p) & 1;
-			} else
-			if (!strncasecmp(optarg, "pdreg=", p - optarg)) {
-				pdreg = atoi(p) & 1;
-			} else
-			if (!strncasecmp(optarg, "locaid=", p - optarg)) {
-				locaid = atoi(p);
-				if (locaid > 4095)
-					locaid = 4095;
-			} else
-			if (!strncasecmp(optarg, "regh=", p - optarg)) {
-				regh = atoi(p) & 1;
-			} else
-			if (!strncasecmp(optarg, "regr=", p - optarg)) {
-				regr = atoi(p) & 1;
-			} else
-			if (!strncasecmp(optarg, "bis=", p - optarg)) {
-				bis = atoi(p) & 1;
-			} else {
-				fprintf(stderr, "Given sysinfo parameter '%s' unknown, see help!\n", optarg);
-				exit(0);
+	switch (short_option) {
+	case 'T':
+		if (!strcmp(argv[argi], "list")) {
+			amps_channel_list();
+			return 0;
+		}
+		rc = amps_channel_by_short_name(argv[argi]);
+		if (rc < 0) {
+			fprintf(stderr, "Error, channel type '%s' unknown. Please use '-t list' to get a list. I suggest to use the default.\n", argv[argi]);
+			return -EINVAL;
+		}
+		OPT_ARRAY(num_chan_type, chan_type, rc)
+		break;
+	case 'F':
+		if (!strcasecmp(argv[argi], "no"))
+			flip_polarity = "no";
+		else if (!strcasecmp(argv[argi], "yes"))
+			flip_polarity = "yes";
+		else {
+			fprintf(stderr, "Given polarity '%s' is illegal, use '-h' for help!\n", argv[argi]);
+			return -EINVAL;
+		}
+		break;
+	case 'P':
+		ms_power = atoi(argv[argi]);
+		if (ms_power > 7)
+			ms_power = 7;
+		if (ms_power < 0)
+			ms_power = 0;
+		break;
+	case 'D':
+		dtx = atoi(argv[argi]);
+		if (dtx > 3)
+			dtx = 3;
+		if (dtx < 0)
+			dtx = 0;
+		break;
+	case 'S':
+		p = strchr(argv[argi], '=');
+		if (!p) {
+			fprintf(stderr, "Given sysinfo parameter '%s' requires '=' character to set value, use '-h' for help!\n", argv[argi]);
+			return -EINVAL;
+		}
+		p++;
+		if (!strncasecmp(argv[argi], "sid=", p - argv[argi])
+		 || !strncasecmp(argv[argi], "aid=", p - argv[argi])) {
+			if (!strcasecmp(p, "list")) {
+				list_stations();
+				return 0;
 			}
-			skip_args += 2;
-			break;
-		case 'O':
-			tolerant = 1;
-			skip_args += 1;
-			break;
-		default:
-			main_mobile_opt_switch(c, argv[0], &skip_args);
+			sid = atoi(p);
+			if (sid > 32767)
+				sid = 32767;
+			if (sid < 0)
+				sid = 0;
+		} else
+		if (!strncasecmp(argv[argi], "dcc=", p - argv[argi])) {
+			dcc = atoi(p);
+			if (dcc > 3)
+				dcc = 3;
+			if (dcc < 0)
+				dcc = 0;
+		} else
+		if (!strncasecmp(argv[argi], "scc=", p - argv[argi])) {
+			scc = atoi(p);
+			if (scc > 2)
+				scc = 2;
+			if (scc < 0)
+				scc = 0;
+		} else
+		if (!strncasecmp(argv[argi], "regincr=", p - argv[argi])) {
+			regincr = atoi(p);
+		} else
+		if (!strncasecmp(argv[argi], "pureg=", p - argv[argi])) {
+			pureg = atoi(p) & 1;
+		} else
+		if (!strncasecmp(argv[argi], "pdreg=", p - argv[argi])) {
+			pdreg = atoi(p) & 1;
+		} else
+		if (!strncasecmp(argv[argi], "locaid=", p - argv[argi])) {
+			locaid = atoi(p);
+			if (locaid > 4095)
+				locaid = 4095;
+		} else
+		if (!strncasecmp(argv[argi], "regh=", p - argv[argi])) {
+			regh = atoi(p) & 1;
+		} else
+		if (!strncasecmp(argv[argi], "regr=", p - argv[argi])) {
+			regr = atoi(p) & 1;
+		} else
+		if (!strncasecmp(argv[argi], "bis=", p - argv[argi])) {
+			bis = atoi(p) & 1;
+		} else {
+			fprintf(stderr, "Given sysinfo parameter '%s' unknown, use '-h' for help!\n", argv[argi]);
+			return -EINVAL;
 		}
+		break;
+	case 'O':
+		tolerant = 1;
+		break;
+	default:
+		return main_mobile_handle_options(short_option, argi, argv);
 	}
 
-	free(long_options);
-
-	return skip_args;
+	return 1;
 }
 
 int main_amps_tacs(int argc, char *argv[])
 {
-	int rc;
-	int skip_args;
+	int rc, argi;
 	const char *station_id = "";
 	int polarity;
 	int i;
@@ -258,12 +239,24 @@ int main_amps_tacs(int argc, char *argv[])
 
 	main_mobile_init();
 
-	skip_args = handle_options(argc, argv);
-	argc -= skip_args;
-	argv += skip_args;
+	/* handle options / config file */
+	add_options();
+    if (!tacs) {
+	rc = options_config_file("~/.osmocom/analog/amps.conf", handle_options);
+    } else if (!jtacs) {
+	rc = options_config_file("~/.osmocom/analog/tacs.conf", handle_options);
+    } else {
+	rc = options_config_file("~/.osmocom/analog/jtacs.conf", handle_options);
+    }
+	if (rc < 0)
+		return 0;
+	argi = options_command_line(argc, argv, handle_options);
+	if (argi <= 0)
+		return argi;
+
 
-	if (argc > 1) {
-		station_id = argv[1];
+	if (argi < argc) {
+		station_id = argv[argi];
 		if (strlen(station_id) != 10) {
 			printf("Given station ID '%s' does not have 10 digits\n", station_id);
 			return 0;
@@ -272,7 +265,7 @@ int main_amps_tacs(int argc, char *argv[])
 
 	if (!num_kanal) {
 		printf("No channel (\"Kanal\") is specified, I suggest channel %d.\n\n", (!tacs) ? 334 : 323);
-		print_help(argv[-skip_args]);
+		print_help(argv[0]);
 		return 0;
 	}
 	if (use_sdr) {
@@ -380,7 +373,7 @@ int main_amps_tacs(int argc, char *argv[])
 	else if (use_sdr)
 		polarity = 1; /* SDR is always positive */
 	else {
-		fprintf(stderr, "You must define, if the the TX deviation polarity has to be flipped. (-F yes | no) See help.\n");
+		fprintf(stderr, "You must define, if the the TX deviation polarity has to be flipped. (-F yes | no) use '-h' for help.\n");
 		exit(0);
 	}
 
diff --git a/src/anetz/Makefile.am b/src/anetz/Makefile.am
index 175404d..eba47db 100644
--- a/src/anetz/Makefile.am
+++ b/src/anetz/Makefile.am
@@ -18,6 +18,7 @@ anetz_SOURCES = \
 anetz_LDADD = \
 	$(COMMON_LA) \
 	libgermanton.a \
+	$(top_builddir)/src/liboptions/liboptions.a \
 	$(top_builddir)/src/libdebug/libdebug.a \
 	$(top_builddir)/src/libmobile/libmobile.a \
 	$(top_builddir)/src/libdisplay/libdisplay.a \
diff --git a/src/anetz/main.c b/src/anetz/main.c
index 617bd88..35fc036 100644
--- a/src/anetz/main.c
+++ b/src/anetz/main.c
@@ -19,15 +19,16 @@
 
 #include 
 #include 
-#include 
 #include 
 #include 
+#include 
 #include 
 #include "../libsample/sample.h"
 #include "../libmobile/main_mobile.h"
 #include "../libdebug/debug.h"
 #include "../libtimer/timer.h"
 #include "../libmobile/call.h"
+#include "../liboptions/options.h"
 #include "freiton.h"
 #include "besetztton.h"
 #include "anetz.h"
@@ -69,79 +70,60 @@ void print_help(const char *arg0)
 	main_mobile_print_hotkeys();
 }
 
-static int handle_options(int argc, char **argv)
+static void add_options(void)
+{
+	main_mobile_add_options();
+	option_add('O', "operator", 1);
+	option_add('G', "geo", 1);
+	option_add('V', "page-gain", 1);
+	option_add('P', "page-sequence", 1);
+	option_add('S', "squelch", 1);
+}
+
+static int handle_options(int short_option, int argi, char **argv)
 {
-	int skip_args = 0;
 	char *p;
 	double gain_db;
 
-	static struct option long_options_special[] = {
-		{"operator", 1, 0, 'O'},
-		{"geo", 1, 0, 'G'},
-		{"page-gain", 1, 0, 'V'},
-		{"page-sequence", 1, 0, 'P'},
-		{"squelch", 1, 0, 'S'},
-		{0, 0, 0, 0}
-	};
-
-	main_mobile_set_options("O:G:V:P:S:", long_options_special);
-
-	while (1) {
-		int option_index = 0, c;
-
-		c = getopt_long(argc, argv, optstring, long_options, &option_index);
-
-		if (c == -1)
-			break;
-
-		switch (c) {
-		case 'O':
-			strncpy(operator, optarg, sizeof(operator) - 1);
-			operator[sizeof(operator) - 1] = '\0';
-			skip_args += 2;
-			break;
-		case 'G':
-			if (!strcasecmp(optarg, "list")) {
-				station_list();
-				exit(0);
-			}
-			if ((p = strchr(optarg, ','))) {
-				get_station_by_coordinates(atof(optarg), atof(p + 1));
-				exit(0);
-			}
-			fprintf(stderr, "Invalid geo parameter\n");
-			exit(0);
-			break;
-		case 'V':
-			gain_db = atof(optarg);
-			page_gain = pow(10, gain_db / 20.0);
-			skip_args += 2;
-			break;
-		case 'P':
-			page_sequence = atoi(optarg);
-			skip_args += 2;
-			break;
-		case 'S':
-			if (!strcasecmp(optarg, "auto"))
-				squelch_db = 0.0;
-			else
-				squelch_db = atof(optarg);
-			skip_args += 2;
-			break;
-		default:
-			main_mobile_opt_switch(c, argv[0], &skip_args);
+	switch (short_option) {
+	case 'O':
+		strncpy(operator, argv[argi], sizeof(operator) - 1);
+		operator[sizeof(operator) - 1] = '\0';
+		break;
+	case 'G':
+		if (!strcasecmp(argv[argi], "list")) {
+			station_list();
+			return 0;
 		}
+		if ((p = strchr(argv[argi], ','))) {
+			get_station_by_coordinates(atof(argv[argi]), atof(p + 1));
+			return 0;
+		}
+		fprintf(stderr, "Invalid geo parameter\n");
+		return -EINVAL;
+	case 'V':
+		gain_db = atof(argv[argi]);
+		page_gain = pow(10, gain_db / 20.0);
+		break;
+	case 'P':
+		page_sequence = atoi(argv[argi]);
+		break;
+	case 'S':
+		if (!strcasecmp(argv[argi], "auto"))
+			squelch_db = 0.0;
+		else
+			squelch_db = atof(argv[argi]);
+		break;
+	default:
+		return main_mobile_handle_options(short_option, argi, argv);
 	}
 
-	free(long_options);
-
-	return skip_args;
+	return 1;
 }
 
 int main(int argc, char *argv[])
 {
-	int rc;
-	int skip_args;
+	int rc, argi;
 	const char *station_id = "";
 	int i;
 
@@ -154,12 +136,17 @@ int main(int argc, char *argv[])
 
 	main_mobile_init();
 
-	skip_args = handle_options(argc, argv);
-	argc -= skip_args;
-	argv += skip_args;
+	/* handle options / config file */
+	add_options();
+	rc = options_config_file("~/.osmocom/analog/anetz.conf", handle_options);
+	if (rc < 0)
+		return 0;
+	argi = options_command_line(argc, argv, handle_options);
+	if (argi <= 0)
+		return argi;
 
-	if (argc > 1) {
-		station_id = argv[1];
+	if (argi < argc) {
+		station_id = argv[argi];
 		if (strlen(station_id) != 5 && strlen(station_id) != 7) {
 			printf("Given station ID '%s' does not have 7 or (the last) 5 digits\n", station_id);
 			return 0;
@@ -170,7 +157,7 @@ int main(int argc, char *argv[])
 
 	if (!num_kanal) {
 		printf("No channel (\"Kanal\") is specified, I suggest channel 30.\n\n");
-		print_help(argv[-skip_args]);
+		print_help(argv[0]);
 		return 0;
 	}
 	if (use_sdr) {
diff --git a/src/bnetz/Makefile.am b/src/bnetz/Makefile.am
index bc562e9..ecb611e 100644
--- a/src/bnetz/Makefile.am
+++ b/src/bnetz/Makefile.am
@@ -15,6 +15,7 @@ bnetz_SOURCES = \
 bnetz_LDADD = \
 	$(COMMON_LA) \
 	../anetz/libgermanton.a \
+	$(top_builddir)/src/liboptions/liboptions.a \
 	$(top_builddir)/src/libdebug/libdebug.a \
 	$(top_builddir)/src/libmobile/libmobile.a \
 	$(top_builddir)/src/libdisplay/libdisplay.a \
@@ -36,6 +37,7 @@ bnetz_dialer_SOURCES = \
 	dialer.c
 bnetz_dialer_LDADD = \
 	$(COMMON_LA) \
+	$(top_builddir)/src/liboptions/liboptions.a \
 	$(top_builddir)/src/libdebug/libdebug.a \
 	$(top_builddir)/src/libfsk/libfsk.a \
 	$(top_builddir)/src/libfm/libfm.a \
diff --git a/src/bnetz/dialer.c b/src/bnetz/dialer.c
index a3a292d..05195e3 100644
--- a/src/bnetz/dialer.c
+++ b/src/bnetz/dialer.c
@@ -19,10 +19,10 @@
 
 #include 
 #include 
-#include 
 #include 
 #include 
 #include 
+#include 
 #include "../libsample/sample.h"
 #include "../libfsk/fsk.h"
 #include "../libwave/wave.h"
@@ -30,6 +30,7 @@
 #ifdef HAVE_ALSA
 #include "../libsound/sound.h"
 #endif
+#include "../liboptions/options.h"
 #include "telegramm.h"
 
 #define MAX_PAUSE	0.5	/* pause before and after dialing sequence */
@@ -92,7 +93,6 @@ static void print_help(const char *arg0)
 	printf(" -a --audio-device hw:,\n");
 	printf("        Sound card and device number (default = '%s')\n", audiodev);
 #endif
-	printf("        Don't set it for SDR!\n");
 	printf(" -s --samplerate \n");
 	printf("        Sample rate of sound device (default = '%d')\n", samplerate);
 	printf(" -w --write-tx-wave \n");
@@ -106,70 +106,50 @@ static void print_help(const char *arg0)
 	printf("        Indicate to base station that we are a pay phone. ('Muenztelefon')\n");
 }
 
-static int handle_options(int argc, char **argv)
+static void add_options(void)
 {
-	const char *optstring;
-	int skip_args = 0;
-
-	static struct option long_options[] = {
-		{"help", 0, 0, 'h'},
-		{"station-id", 1, 0, 'i'},
-		{"audio-device", 1, 0, 'a'},
-		{"samplerate", 1, 0, 's'},
-		{"write-tx-wave", 1, 0, 'w'},
-		{"gebuehrenimpuls", 0, 0, 'g'},
-		{"metering", 0, 0, OPT_METERING},
-		{"muenztelefon", 0, 0, 'm'},
-		{"coin-box", 0, 0, OPT_COIN_BOX},
-		{0, 0, 0, 0},
-	};
-
-	optstring = "hi:a:s:w:gm";
-
-	while (1) {
-		int option_index = 0, c;
-
-		c = getopt_long(argc, argv, optstring, long_options, &option_index);
-
-		if (c == -1)
-			break;
+	option_add('h', "help", 0);
+	option_add('i', "station-id", 1);
+	option_add('a', "audio-device", 1);
+	option_add('s', "samplerate", 1);
+	option_add('w', "write-tx-wave", 1);
+	option_add('g', "gebuehrenimpuls", 0);
+	option_add(OPT_METERING, "metering", 0);
+	option_add('m', "muenztelefon", 0);
+	option_add(OPT_COIN_BOX, "coin-box", 0);
+}
 
-		switch (c) {
-		case 'h':
-			print_help(argv[0]);
-			exit(0);
-		case 'i':
-			station_id = strdup(optarg);
-			skip_args += 2;
-			break;
-		case 'a':
-			audiodev = strdup(optarg);
-			skip_args += 2;
-			break;
-		case 's':
-			samplerate = atoi(optarg);
-			skip_args += 2;
-			break;
-		case 'w':
-			write_tx_wave = strdup(optarg);
-			skip_args += 2;
-			break;
-		case 'g':
-		case OPT_METERING:
-			start_digit = 'S';
-			skip_args += 1;
-			break;
-		case 'm':
-		case OPT_COIN_BOX:
-			start_digit = 'M';
-			skip_args += 1;
-			break;
-		default:
-			break;
-		}
+static int handle_options(int short_option, int __attribute__((unused)) argi, char **argv)
+{
+	switch (short_option) {
+	case 'h':
+		print_help(argv[0]);
+		return 0;
+	case 'i':
+		station_id = strdup(argv[argi]);
+		break;
+	case 'a':
+		audiodev = strdup(argv[argi]);
+		break;
+	case 's':
+		samplerate = atoi(argv[argi]);
+		break;
+	case 'w':
+		write_tx_wave = strdup(argv[argi]);
+		break;
+	case 'g':
+	case OPT_METERING:
+		start_digit = 'S';
+		break;
+	case 'm':
+	case OPT_COIN_BOX:
+		start_digit = 'M';
+		break;
+	default:
+		return -EINVAL;
 	}
 
-	return skip_args;
+	return 1;
 }
 
 
@@ -301,10 +281,8 @@ static void process_signal(void)
 
 int main(int argc, char *argv[])
 {
-	const char *arg0 = argv[0];
-	int skip_args;
 	int i;
-	int rc;
+	int rc, argi;
 
 	/* init */
 	bnetz_init_telegramm();
@@ -313,13 +291,15 @@ int main(int argc, char *argv[])
 	/* latency of send buffer in samples */
 	latspl = samplerate * latency / 1000;
 
-	skip_args = handle_options(argc, argv);
-	argc -= skip_args;
-	argv += skip_args;
+	/* handle options / config file */
+	add_options();
+	argi = options_command_line(argc, argv, handle_options);
+	if (argi <= 0)
+		return argi;
 
-	if (argc <= 1) {
+	if (argi >= argc) {
 		printf("No phone number given!\n\n");
-		print_help(arg0);
+		print_help(argv[0]);
 		goto exit;
 	}
 
@@ -336,7 +316,7 @@ int main(int argc, char *argv[])
 	}
 
 	/* check for valid phone number */
-	dialing = argv[1];
+	dialing = argv[argi];
 	if (strlen(dialing) < 4) {
 		printf("Given phone number '%s' has too few digits! (less than minimum of 4 digits)\n", dialing);
 		goto exit;
diff --git a/src/bnetz/main.c b/src/bnetz/main.c
index d287e14..d60ba88 100644
--- a/src/bnetz/main.c
+++ b/src/bnetz/main.c
@@ -19,9 +19,9 @@
 
 #include 
 #include 
-#include 
 #include 
 #include 
+#include 
 #include 
 #include "../libsample/sample.h"
 #include "../libdebug/debug.h"
@@ -29,6 +29,7 @@
 #include "../libmobile/main_mobile.h"
 #include "../anetz/freiton.h"
 #include "../anetz/besetztton.h"
+#include "../liboptions/options.h"
 #include "bnetz.h"
 #include "dsp.h"
 #include "stations.h"
@@ -79,70 +80,54 @@ void print_help(const char *arg0)
 	main_mobile_print_hotkeys();
 }
 
-static int handle_options(int argc, char **argv)
+static void add_options(void)
+{
+	main_mobile_add_options();
+	option_add('G', "gfs", 1);
+	option_add('M', "gebuehrenimpuls", 1);
+	option_add('P', "paging", 1);
+	option_add('S', "squelch", 1);
+}
+
+static int handle_options(int short_option, int argi, char **argv)
 {
-	int skip_args = 0;
 	char *p;
 
-	static struct option long_options_special[] = {
-		{"gfs", 1, 0, 'G'},
-		{"gebuehrenimpuls", 1, 0, 'M'},
-		{"paging", 1, 0, 'P'},
-		{"squelch", 1, 0, 'S'},
-		{0, 0, 0, 0},
-	};
-
-	main_mobile_set_options("G:M:P:S:", long_options_special);
-
-	while (1) {
-		int option_index = 0, c;
-
-		c = getopt_long(argc, argv, optstring, long_options, &option_index);
-
-		if (c == -1)
-			break;
-
-		switch (c) {
-		case 'G':
-			if (!strcasecmp(optarg, "list")) {
-				station_list();
-				exit(0);
-			}
-			if ((p = strchr(optarg, ','))) {
-				gfs = get_station_by_coordinates(atof(optarg), atof(p + 1));
-				if (gfs == 0)
-					exit(0);
-			} else
-				gfs = atoi(optarg);
-			skip_args += 2;
-			break;
-		case 'M':
-			metering = atoi(optarg);
-			skip_args += 2;
-			break;
-		case 'P':
-			paging = strdup(optarg);
-			skip_args += 2;
-			break;
-		case 'S':
-			if (!strcasecmp(optarg, "auto"))
-				squelch_db = 0.0;
-			else
-				squelch_db = atof(optarg);
-			skip_args += 2;
-			break;
-		default:
-			main_mobile_opt_switch(c, argv[0], &skip_args);
+	switch (short_option) {
+	case 'G':
+		if (!strcasecmp(argv[argi], "list")) {
+			station_list();
+			return 0;
 		}
+		if ((p = strchr(argv[argi], ','))) {
+			gfs = get_station_by_coordinates(atof(argv[argi]), atof(p + 1));
+			if (gfs == 0)
+				return -EINVAL;
+		} else
+			gfs = atoi(argv[argi]);
+		break;
+	case 'M':
+		metering = atoi(argv[argi]);
+		break;
+	case 'P':
+		paging = strdup(argv[argi]);
+		break;
+	case 'S':
+		if (!strcasecmp(argv[argi], "auto"))
+			squelch_db = 0.0;
+		else
+			squelch_db = atof(argv[argi]);
+		break;
+	default:
+		return main_mobile_handle_options(short_option, argi, argv);
 	}
 
-	return skip_args;
+	return 1;
 }
 
 int main(int argc, char *argv[])
 {
-	int rc;
-	int skip_args;
+	int rc, argi;
 	const char *station_id = "";
 	int i;
 
@@ -153,12 +138,17 @@ int main(int argc, char *argv[])
 
 	main_mobile_init();
 
-	skip_args = handle_options(argc, argv);
-	argc -= skip_args;
-	argv += skip_args;
+	/* handle options / config file */
+	add_options();
+	rc = options_config_file("~/.osmocom/analog/bnetz.conf", handle_options);
+	if (rc < 0)
+		return 0;
+	argi = options_command_line(argc, argv, handle_options);
+	if (argi <= 0)
+		return argi;
 
-	if (argc > 1) {
-		station_id = argv[1];
+	if (argi < argc) {
+		station_id = argv[argi];
 		if (strlen(station_id) != 5) {
 			printf("Given station ID '%s' does not have 5 digits\n", station_id);
 			return 0;
@@ -167,7 +157,7 @@ int main(int argc, char *argv[])
 
 	if (!num_kanal) {
 		printf("No channel (\"Kanal\") is specified, I suggest channel 1 (sound card) or 17 (SDR).\n\n");
-		print_help(argv[-skip_args]);
+		print_help(argv[0]);
 		return 0;
 	}
 	if (use_sdr) {
diff --git a/src/cnetz/Makefile.am b/src/cnetz/Makefile.am
index ab7cf09..5823467 100644
--- a/src/cnetz/Makefile.am
+++ b/src/cnetz/Makefile.am
@@ -17,6 +17,7 @@ cnetz_SOURCES = \
 cnetz_LDADD = \
 	$(COMMON_LA) \
 	../anetz/libgermanton.a \
+	$(top_builddir)/src/liboptions/liboptions.a \
 	$(top_builddir)/src/libdebug/libdebug.a \
 	$(top_builddir)/src/libmobile/libmobile.a \
 	$(top_builddir)/src/libdisplay/libdisplay.a \
diff --git a/src/cnetz/main.c b/src/cnetz/main.c
index 8d31cd5..c5dd916 100644
--- a/src/cnetz/main.c
+++ b/src/cnetz/main.c
@@ -19,15 +19,16 @@
 
 #include 
 #include 
-#include 
 #include 
 #include 
+#include 
 #include "../libsample/sample.h"
 #include "../libmobile/main_mobile.h"
 #include "../libdebug/debug.h"
 #include "../libmobile/call.h"
 #include "../anetz/freiton.h"
 #include "../anetz/besetztton.h"
+#include "../liboptions/options.h"
 #include "cnetz.h"
 #include "database.h"
 #include "sysinfo.h"
@@ -216,198 +217,174 @@ static int atoi_limit(const char *p, int l1, int l2)
 
 #define OPT_WARTESCHLANGE	256
 
-static int handle_options(int argc, char **argv)
+static void add_options(void)
+{
+	main_mobile_add_options();
+	option_add('T', "channel-type", 1);
+	option_add('M', "measure-speed", 0);
+	option_add('C', "clock-speed", 1);
+	option_add('F', "flip-polarity", 1);
+	option_add('P', "ms-power", 1);
+	option_add('A', "authentication", 0);
+	option_add('Q', "queue", 1);
+	option_add(OPT_WARTESCHLANGE, "warteschlange", 1);
+	option_add('G', "gebuehren", 1);
+	option_add('S', "sysinfo", 1);
+	option_add('D', "demod", 1);
+}
+
+static int handle_options(int short_option, int argi, char **argv)
 {
-	int skip_args = 0;
 	int rc;
 	const char *p;
 
-	static struct option long_options_special[] = {
-		{"channel-type", 1, 0, 'T'},
-		{"measure-speed", 0, 0, 'M'},
-		{"clock-speed", 1, 0, 'C'},
-		{"flip-polarity", 1, 0, 'F'},
-		{"ms-power", 1, 0, 'P'},
-		{"authentication", 0, 0, 'A'},
-		{"queue", 1, 0, 'Q'},
-		{"warteschlange", 1, 0, OPT_WARTESCHLANGE},
-		{"gebuehren", 1, 0, 'G'},
-		{"sysinfo", 1, 0, 'S'},
-		{"demod", 1, 0, 'D'},
-		{0, 0, 0, 0}
-	};
-
-	main_mobile_set_options("T:MC:F:P:AQ:G:S:D:", long_options_special);
-
-	while (1) {
-		int option_index = 0, c;
-
-		c = getopt_long(argc, argv, optstring, long_options, &option_index);
-
-		if (c == -1)
-			break;
-
-		switch (c) {
-		case 'T':
-			if (!strcmp(optarg, "list")) {
-				cnetz_channel_list();
-				exit(0);
-			}
-			rc = cnetz_channel_by_short_name(optarg);
-			if (rc < 0) {
-				fprintf(stderr, "Error, channel type '%s' unknown. Please use '-t list' to get a list. I suggest to use the default.\n", optarg);
-				exit(0);
-			}
-			OPT_ARRAY(num_chan_type, chan_type, rc)
-			skip_args += 2;
-			break;
-		case 'M':
-			measure_speed = 1;
-			skip_args++;
-			break;
-		case 'C':
-			p = strchr(optarg, ',');
-			if (!p) {
-				fprintf(stderr, "Illegal clock speed, use two values, seperated by comma and no spaces!\n");
-				exit(0);
-			}
-			clock_speed[0] = strtold(optarg, NULL);
-			clock_speed[1] = strtold(p + 1, NULL);
-			set_clock_speed = 1;
-			skip_args += 2;
-			break;
-		case 'F':
-			if (!strcasecmp(optarg, "no"))
-				flip_polarity = "no";
-			else if (!strcasecmp(optarg, "yes"))
-				flip_polarity = "yes";
-			else if (!strcasecmp(optarg, "auto"))
-				flip_polarity = "auto";
-			else {
-				fprintf(stderr, "Given polarity '%s' is illegal, see help!\n", optarg);
-				exit(0);
-			}
-			skip_args += 2;
-			break;
-		case 'P':
-			ms_power = atoi_limit(optarg, 0, 3);
-			skip_args += 2;
-			break;
-		case 'A':
-			auth = 1;
-			skip_args += 1;
-			break;
-		case 'Q':
-		case OPT_WARTESCHLANGE:
-			warteschlange = atoi_limit(optarg, 0, 1);;
-			skip_args += 2;
-			break;
-		case 'G':
-			metering = atoi(optarg);
-			skip_args += 2;
-			break;
-		case 'S':
-			p = strchr(optarg, '=');
-			if (!p) {
-				fprintf(stderr, "Given sysinfo parameter '%s' requires '=' character to set value, see help!\n", optarg);
-				exit(0);
-			}
-			p++;
-			if (!strncasecmp(optarg, "fuz-nat=", p - optarg)) {
-				fuz_nat = atoi_limit(p, 0, 7);
-			} else
-			if (!strncasecmp(optarg, "fuz-fuvst=", p - optarg)) {
-				fuz_fuvst = atoi_limit(p, 0, 32);
-			} else
-			if (!strncasecmp(optarg, "fuz-rest=", p - optarg)) {
-				fuz_rest = atoi_limit(p, 0, 255);
-			} else
-			if (!strncasecmp(optarg, "kennung-fufst=", p - optarg)) {
-				kennung_fufst = atoi_limit(p, 0, 3);
-			} else
-			if (!strncasecmp(optarg, "ws-kennung=", p - optarg)) {
-				ws_kennung = atoi_limit(p, 0, 3);
-			} else
-			if (!strncasecmp(optarg, "fuvst-sperren=", p - optarg)) {
-				fuvst_sperren = atoi_limit(p, 0, 3);
-			} else
-			if (!strncasecmp(optarg, "grenz-einbuchen=", p - optarg)) {
-				grenz_einbuchen = atoi_limit(p, 0, 7);
-			} else
-			if (!strncasecmp(optarg, "grenz-umschalten=", p - optarg)) {
-				grenz_umschalten = atoi_limit(p, 0, 15);
-			} else
-			if (!strncasecmp(optarg, "grenz-ausloesen=", p - optarg)) {
-				grenz_ausloesen = atoi_limit(p, 0, 15);
-			} else
-			if (!strncasecmp(optarg, "mittel-umschalten=", p - optarg)) {
-				mittel_umschalten = atoi_limit(p, 0, 5);
-			} else
-			if (!strncasecmp(optarg, "mittel-ausloesen=", p - optarg)) {
-				mittel_ausloesen = atoi_limit(p, 0, 5);
-			} else
-			if (!strncasecmp(optarg, "genauigkeit=", p - optarg)) {
-				genauigkeit = atoi_limit(p, 0, 1);
-			} else
-			if (!strncasecmp(optarg, "bewertung=", p - optarg)) {
-				bewertung = atoi_limit(p, 0, 1);
-			} else
-			if (!strncasecmp(optarg, "entfernung=", p - optarg)) {
-				entfernung = atoi_limit(p, 0, 15);
-			} else
-			if (!strncasecmp(optarg, "nachbar-prio=", p - optarg)) {
-				nachbar_prio = atoi_limit(p, 0, 1);
-			} else
-			if (!strncasecmp(optarg, "futln-sperre=", p - optarg)) {
-				char value[128], *v, *q;
-				strncpy(value, p, sizeof(value) - 1);
-				value[sizeof(value) - 1] = '\0';
-				v = value;
-				q = strchr(value, '-');
-				if (q)
-					*q++ = '\0';
-				if (strlen(v) > 5)
-					v += strlen(v) - 5;
-				futln_sperre_start = atoi(v) & 0xf;
-				if (q) {
-					if (strlen(q) > 5)
-						q += strlen(q) - 5;
-					futln_sperre_end = atoi(q) & 0xf;
-				}
-			} else
-			{
-				fprintf(stderr, "Given sysinfo parameter '%s' unknown, see help!\n", optarg);
-				exit(0);
-			}
-			skip_args += 2;
-			break;
-		case 'D':
-			if (!strcasecmp(optarg, "auto"))
-				demod = FSK_DEMOD_AUTO;
-			else if (!strcasecmp(optarg, "slope"))
-				demod = FSK_DEMOD_SLOPE;
-			else if (!strcasecmp(optarg, "level"))
-				demod = FSK_DEMOD_LEVEL;
-			else {
-				fprintf(stderr, "Given demodulation type '%s' is illegal, see help!\n", optarg);
-				exit(0);
+	switch (short_option) {
+	case 'T':
+		if (!strcmp(argv[argi], "list")) {
+			cnetz_channel_list();
+			return 0;
+		}
+		rc = cnetz_channel_by_short_name(argv[argi]);
+		if (rc < 0) {
+			fprintf(stderr, "Error, channel type '%s' unknown. Please use '-t list' to get a list. I suggest to use the default.\n", argv[argi]);
+			return -EINVAL;
+		}
+		OPT_ARRAY(num_chan_type, chan_type, rc)
+		break;
+	case 'M':
+		measure_speed = 1;
+		break;
+	case 'C':
+		p = strchr(argv[argi], ',');
+		if (!p) {
+			fprintf(stderr, "Illegal clock speed, use two values, seperated by comma and no spaces!\n");
+			return -EINVAL;
+		}
+		clock_speed[0] = strtold(argv[argi], NULL);
+		clock_speed[1] = strtold(p + 1, NULL);
+		set_clock_speed = 1;
+		break;
+	case 'F':
+		if (!strcasecmp(argv[argi], "no"))
+			flip_polarity = "no";
+		else if (!strcasecmp(argv[argi], "yes"))
+			flip_polarity = "yes";
+		else if (!strcasecmp(argv[argi], "auto"))
+			flip_polarity = "auto";
+		else {
+			fprintf(stderr, "Given polarity '%s' is illegal, use '-h' for help!\n", argv[argi]);
+			return -EINVAL;
+		}
+		break;
+	case 'P':
+		ms_power = atoi_limit(argv[argi], 0, 3);
+		break;
+	case 'A':
+		auth = 1;
+		break;
+	case 'Q':
+	case OPT_WARTESCHLANGE:
+		warteschlange = atoi_limit(argv[argi], 0, 1);;
+		break;
+	case 'G':
+		metering = atoi(argv[argi]);
+		break;
+	case 'S':
+		p = strchr(argv[argi], '=');
+		if (!p) {
+			fprintf(stderr, "Given sysinfo parameter '%s' requires '=' character to set value, use '-h' for help!\n", argv[argi]);
+			return -EINVAL;
+		}
+		p++;
+		if (!strncasecmp(argv[argi], "fuz-nat=", p - argv[argi])) {
+			fuz_nat = atoi_limit(p, 0, 7);
+		} else
+		if (!strncasecmp(argv[argi], "fuz-fuvst=", p - argv[argi])) {
+			fuz_fuvst = atoi_limit(p, 0, 32);
+		} else
+		if (!strncasecmp(argv[argi], "fuz-rest=", p - argv[argi])) {
+			fuz_rest = atoi_limit(p, 0, 255);
+		} else
+		if (!strncasecmp(argv[argi], "kennung-fufst=", p - argv[argi])) {
+			kennung_fufst = atoi_limit(p, 0, 3);
+		} else
+		if (!strncasecmp(argv[argi], "ws-kennung=", p - argv[argi])) {
+			ws_kennung = atoi_limit(p, 0, 3);
+		} else
+		if (!strncasecmp(argv[argi], "fuvst-sperren=", p - argv[argi])) {
+			fuvst_sperren = atoi_limit(p, 0, 3);
+		} else
+		if (!strncasecmp(argv[argi], "grenz-einbuchen=", p - argv[argi])) {
+			grenz_einbuchen = atoi_limit(p, 0, 7);
+		} else
+		if (!strncasecmp(argv[argi], "grenz-umschalten=", p - argv[argi])) {
+			grenz_umschalten = atoi_limit(p, 0, 15);
+		} else
+		if (!strncasecmp(argv[argi], "grenz-ausloesen=", p - argv[argi])) {
+			grenz_ausloesen = atoi_limit(p, 0, 15);
+		} else
+		if (!strncasecmp(argv[argi], "mittel-umschalten=", p - argv[argi])) {
+			mittel_umschalten = atoi_limit(p, 0, 5);
+		} else
+		if (!strncasecmp(argv[argi], "mittel-ausloesen=", p - argv[argi])) {
+			mittel_ausloesen = atoi_limit(p, 0, 5);
+		} else
+		if (!strncasecmp(argv[argi], "genauigkeit=", p - argv[argi])) {
+			genauigkeit = atoi_limit(p, 0, 1);
+		} else
+		if (!strncasecmp(argv[argi], "bewertung=", p - argv[argi])) {
+			bewertung = atoi_limit(p, 0, 1);
+		} else
+		if (!strncasecmp(argv[argi], "entfernung=", p - argv[argi])) {
+			entfernung = atoi_limit(p, 0, 15);
+		} else
+		if (!strncasecmp(argv[argi], "nachbar-prio=", p - argv[argi])) {
+			nachbar_prio = atoi_limit(p, 0, 1);
+		} else
+		if (!strncasecmp(argv[argi], "futln-sperre=", p - argv[argi])) {
+			char value[128], *v, *q;
+			strncpy(value, p, sizeof(value) - 1);
+			value[sizeof(value) - 1] = '\0';
+			v = value;
+			q = strchr(value, '-');
+			if (q)
+				*q++ = '\0';
+			if (strlen(v) > 5)
+				v += strlen(v) - 5;
+			futln_sperre_start = atoi(v) & 0xf;
+			if (q) {
+				if (strlen(q) > 5)
+					q += strlen(q) - 5;
+				futln_sperre_end = atoi(q) & 0xf;
 			}
-			skip_args += 2;
-			break;
-		default:
-			main_mobile_opt_switch(c, argv[0], &skip_args);
+		} else
+		{
+			fprintf(stderr, "Given sysinfo parameter '%s' unknown, use '-h' for help!\n", argv[argi]);
+			return -EINVAL;
 		}
+		break;
+	case 'D':
+		if (!strcasecmp(argv[argi], "auto"))
+			demod = FSK_DEMOD_AUTO;
+		else if (!strcasecmp(argv[argi], "slope"))
+			demod = FSK_DEMOD_SLOPE;
+		else if (!strcasecmp(argv[argi], "level"))
+			demod = FSK_DEMOD_LEVEL;
+		else {
+			fprintf(stderr, "Given demodulation type '%s' is illegal, use '-h' for help!\n", argv[argi]);
+			return -EINVAL;
+		}
+		break;
+	default:
+		return main_mobile_handle_options(short_option, argi, argv);
 	}
 
-	free(long_options);
-
-	return skip_args;
+	return 1;
 }
 
 int main(int argc, char *argv[])
 {
-	int rc;
-	int skip_args;
+	int rc, argi;
 	const char *station_id = "";
 	int mandatory = 0;
 	int polarity;
@@ -422,12 +399,17 @@ int main(int argc, char *argv[])
 
 	main_mobile_init();
 
-	skip_args = handle_options(argc, argv);
-	argc -= skip_args;
-	argv += skip_args;
+	/* handle options / config file */
+	add_options();
+	rc = options_config_file("~/.osmocom/analog/cnetz.conf", handle_options);
+	if (rc < 0)
+		return 0;
+	argi = options_command_line(argc, argv, handle_options);
+	if (argi <= 0)
+		return argi;
 
-	if (argc > 1) {
-		station_id = argv[1];
+	if (argi < argc) {
+		station_id = argv[argi];
 		if (strlen(station_id) != 7) {
 			printf("Given station ID '%s' does not have 7 digits\n", station_id);
 			return 0;
@@ -470,7 +452,7 @@ int main(int argc, char *argv[])
 	}
 
 	if (mandatory) {
-		print_help(argv[-skip_args]);
+		print_help(argv[0]);
 		return 0;
 	}
 
diff --git a/src/jolly/Makefile.am b/src/jolly/Makefile.am
index e36515f..85d5fc5 100644
--- a/src/jolly/Makefile.am
+++ b/src/jolly/Makefile.am
@@ -11,6 +11,7 @@ jollycom_SOURCES = \
 jollycom_LDADD = \
 	$(COMMON_LA) \
 	../anetz/libgermanton.a \
+	$(top_builddir)/src/liboptions/liboptions.a \
 	$(top_builddir)/src/libdebug/libdebug.a \
 	$(top_builddir)/src/libmobile/libmobile.a \
 	$(top_builddir)/src/libdisplay/libdisplay.a \
diff --git a/src/jolly/main.c b/src/jolly/main.c
index 218ebfc..424b091 100644
--- a/src/jolly/main.c
+++ b/src/jolly/main.c
@@ -1,4 +1,4 @@
-/* main
+/* JollyCom main
  *
  * (C) 2017 by Andreas Eversberg 
  * All Rights Reserved
@@ -19,11 +19,11 @@
 
 #include 
 #include 
-#include 
 #include 
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -34,6 +34,7 @@
 #include "../libmncc/mncc_sock.h"
 #include "../anetz/freiton.h"
 #include "../anetz/besetztton.h"
+#include "../liboptions/options.h"
 #include "jolly.h"
 #include "dsp.h"
 #include "voice.h"
@@ -70,73 +71,55 @@ void print_help(const char *arg0)
 	main_mobile_print_hotkeys();
 }
 
-static int handle_options(int argc, char **argv)
+static void add_options(void)
 {
-	int skip_args = 0;
-
-	static struct option long_options_special[] = {
-		{"frequency", 1, 0, 'F'},
-		{"squelch", 1, 0, 'S'},
-		{"nbfm", 0, 0, 'N'},
-		{"repeater", 0, 0, 'R'},
-		{0, 0, 0, 0}
-	};
-
-	main_mobile_set_options("F:S:NR", long_options_special);
-
-	while (1) {
-		int option_index = 0, c;
-		char *string, *string_dl, *string_ul, *string_step;
-
-		c = getopt_long(argc, argv, optstring, long_options, &option_index);
-
-		if (c == -1)
-			break;
-
-		switch (c) {
-		case 'F':
-			string = strdup(optarg);
-			string_dl = strsep(&string, ",");
-			string_ul = strsep(&string, ",");
-			string_step = strsep(&string, ",");
-			if (!string_dl || !string_ul || !string_step) {
-				fprintf(stderr, "Please give 3 values for --frequency, seperated by comma and no space!\n");
-				exit(0);
-			}
-			dl_freq = atof(string_dl);
-			ul_freq = atof(string_ul);
-			step = atof(string_step);
-			skip_args += 2;
-			break;
-		case 'S':
-			if (!strcasecmp(optarg, "auto"))
-				squelch_db = 0.0;
-			else
-				squelch_db = atof(optarg);
-			skip_args += 2;
-			break;
-		case 'N':
-			nbfm = 1;
-			skip_args += 1;
-			break;
-		case 'R':
-			repeater = 1;
-			skip_args += 1;
-			break;
-		default:
-			main_mobile_opt_switch(c, argv[0], &skip_args);
+	main_mobile_add_options();
+	option_add('F', "frequency", 1);
+	option_add('S', "squelch", 1);
+	option_add('N', "nbfm", 0);
+	option_add('R', "repeater", 0);
+}
+
+static int handle_options(int short_option, int argi, char **argv)
+{
+	char *string, *string_dl, *string_ul, *string_step;
+
+	switch (short_option) {
+	case 'F':
+		string = strdup(argv[argi]);
+		string_dl = strsep(&string, ",");
+		string_ul = strsep(&string, ",");
+		string_step = strsep(&string, ",");
+		if (!string_dl || !string_ul || !string_step) {
+			fprintf(stderr, "Please give 3 values for --frequency, seperated by comma and no space!\n");
+			exit(0);
 		}
+		dl_freq = atof(string_dl);
+		ul_freq = atof(string_ul);
+		step = atof(string_step);
+		break;
+	case 'S':
+		if (!strcasecmp(argv[argi], "auto"))
+			squelch_db = 0.0;
+		else
+			squelch_db = atof(argv[argi]);
+		break;
+	case 'N':
+		nbfm = 1;
+		break;
+	case 'R':
+		repeater = 1;
+		break;
+	default:
+		return main_mobile_handle_options(short_option, argi, argv);
 	}
 
-	free(long_options);
-
-	return skip_args;
+	return 1;
 }
 
 int main(int argc, char *argv[])
 {
-	int rc;
-	int skip_args;
+	int rc, argi;
 	const char *station_id = "";
 	int mandatory = 0;
 	int i;
@@ -148,12 +131,17 @@ int main(int argc, char *argv[])
 
 	main_mobile_init();
 
-	skip_args = handle_options(argc, argv);
-	argc -= skip_args;
-	argv += skip_args;
+	/* handle options / config file */
+	add_options();
+	rc = options_config_file("~/.osmocom/analog/jollycom.conf", handle_options);
+	if (rc < 0)
+		return 0;
+	argi = options_command_line(argc, argv, handle_options);
+	if (argi <= 0)
+		return argi;
 
-	if (argc > 1) {
-		station_id = argv[1];
+	if (argi < argc) {
+		station_id = argv[argi];
 		if (strlen(station_id) != 4) {
 			printf("Given station ID '%s' does not have 4 digits\n", station_id);
 			return 0;
@@ -178,7 +166,7 @@ int main(int argc, char *argv[])
 	}
 
 	if (mandatory) {
-		print_help(argv[-skip_args]);
+		print_help(argv[0]);
 		return 0;
 	}
 
diff --git a/src/jtacs/Makefile.am b/src/jtacs/Makefile.am
index f2b051b..b5a8bb4 100644
--- a/src/jtacs/Makefile.am
+++ b/src/jtacs/Makefile.am
@@ -12,6 +12,7 @@ jtacs_SOURCES = \
 jtacs_LDADD = \
 	$(COMMON_LA) \
 	../amps/libamps.a \
+	$(top_builddir)/src/liboptions/liboptions.a \
 	$(top_builddir)/src/libdebug/libdebug.a \
 	$(top_builddir)/src/libmobile/libmobile.a \
 	$(top_builddir)/src/libdisplay/libdisplay.a \
diff --git a/src/libdebug/debug.c b/src/libdebug/debug.c
index fd891ec..934294b 100644
--- a/src/libdebug/debug.c
+++ b/src/libdebug/debug.c
@@ -41,6 +41,7 @@ struct debug_cat {
 	const char *name;
 	const char *color;
 } debug_cat[] = {
+	{ "options", "\033[1;37m" },
 	{ "sender", "\033[1;33m" },
 	{ "sound", "\033[0;35m" },
 	{ "dsp", "\033[0;31m" },
diff --git a/src/libdebug/debug.h b/src/libdebug/debug.h
index 8a76d45..cfc420a 100644
--- a/src/libdebug/debug.h
+++ b/src/libdebug/debug.h
@@ -4,28 +4,29 @@
 #define DEBUG_NOTICE	2 /* something unexpected happens */
 #define DEBUG_ERROR	3 /* there is an error with this software */
 
-#define DSENDER		0
-#define DSOUND		1
-#define DDSP		2
-#define DANETZ		3
-#define DBNETZ		4
-#define DCNETZ		5
-#define DNMT		6
-#define DAMPS		7
-#define DR2000		8
-#define DJOLLY		9
-#define DFRAME		10
-#define DCALL		11
-#define DMNCC		12
-#define DDB		13
-#define DTRANS		14
-#define DDMS		15
-#define DSMS		16
-#define DSDR		17
-#define DUHD		18
-#define DSOAPY		19
-#define DWAVE		20
-#define DRADIO		21
+#define DOPTIONS	0
+#define DSENDER		1
+#define DSOUND		2
+#define DDSP		3
+#define DANETZ		4
+#define DBNETZ		5
+#define DCNETZ		6
+#define DNMT		7
+#define DAMPS		8
+#define DR2000		9
+#define DJOLLY		10
+#define DFRAME		11
+#define DCALL		12
+#define DMNCC		13
+#define DDB		14
+#define DTRANS		15
+#define DDMS		16
+#define DSMS		17
+#define DSDR		18
+#define DUHD		19
+#define DSOAPY		20
+#define DWAVE		21
+#define DRADIO		22
 
 void get_win_size(int *w, int *h);
 
diff --git a/src/libmobile/main_mobile.c b/src/libmobile/main_mobile.c
index a66e038..3eee1d4 100644
--- a/src/libmobile/main_mobile.c
+++ b/src/libmobile/main_mobile.c
@@ -19,7 +19,6 @@
 
 #include 
 #include 
-#include 
 #include 
 #include 
 #include 
@@ -28,7 +27,6 @@
 #include 
 #include 
 #include 
-#include 
 #include "../libsample/sample.h"
 #include "main_mobile.h"
 #include "../libdebug/debug.h"
@@ -42,6 +40,7 @@
 #include "../libsdr/sdr.h"
 #include "../libsdr/sdr_config.h"
 #endif
+#include "../liboptions/options.h"
 
 #define DEFAULT_LO_OFFSET -1000000.0
 
@@ -153,6 +152,8 @@ void main_mobile_print_help(const char *arg0, const char *ext_usage)
 	printf("    --read-tx-wave \n");
 	printf("        Replace transmitted audio by given wave file.\n");
 #ifdef HAVE_SDR
+	printf("    --limesdr\n");
+	printf("        Auto-select several required options for LimeSDR\n");
 	sdr_config_print_help();
 #endif
 	printf("\nNetwork specific options:\n");
@@ -178,214 +179,167 @@ void main_mobile_print_hotkeys(void)
 #define	OPT_READ_TX_WAVE	1004
 #define	OPT_CALL_SAMPLERATE	1005
 #define	OPT_MNCC_NAME		1006
+#define	OPT_LIMESDR		1100
 
-static struct option main_mobile_long_options[] = {
-	{"help", 0, 0, 'h'},
-	{"debug", 1, 0, 'v'},
-	{"kanal", 1, 0, 'k'},
-	{"channel", 1, 0, OPT_CHANNEL},
-	{"audio-device", 1, 0, 'a'},
-	{"samplerate", 1, 0, 's'},
-	{"interval", 1, 0, 'i'},
-	{"buffer", 1, 0, 'b'},
-	{"pre-emphasis", 0, 0, 'p'},
-	{"de-emphasis", 0, 0, 'd'},
-	{"rx-gain", 1, 0, 'g'},
-	{"echo-test", 0, 0, 'e'},
-	{"mncc-cross", 0, 0, 'x'},
-	{"mncc-sock", 0, 0, 'm'},
-	{"mncc-name", 1, 0, OPT_MNCC_NAME},
-	{"call-device", 1, 0, 'c'},
-	{"call-samplerate", 1, 0, OPT_CALL_SAMPLERATE},
-	{"tones", 0, 0, 't'},
-	{"loopback", 1, 0, 'l'},
-	{"realtime", 1, 0, 'r'},
-	{"write-rx-wave", 1, 0, OPT_WRITE_RX_WAVE},
-	{"write-tx-wave", 1, 0, OPT_WRITE_TX_WAVE},
-	{"read-rx-wave", 1, 0, OPT_READ_RX_WAVE},
-	{"read-tx-wave", 1, 0, OPT_READ_TX_WAVE},
-	{0, 0, 0, 0}
-};
-
-static const char *main_mobile_optstring = "hv:k:a:s:i:b:pdg:exmc:t:l:r:";
-
-struct option *long_options;
-char *optstring;
-
-static void check_duplicate_option(int num, struct option *option)
-{
-	int i;
-
-	for (i = 0; i < num; i++) {
-		if (long_options[i].val == option->val) {
-			fprintf(stderr, "Duplicate option %d. Please fix!\n", option->val);
-			abort();
-		}
-	}
-}
-
-void main_mobile_set_options(const char *optstring_special, struct option *long_options_special)
+void main_mobile_add_options(void)
 {
-	int i = 0, j;
-
-	long_options = calloc(sizeof(*long_options), 256);
-	for (j = 0; main_mobile_long_options[j].name; i++, j++) {
-		check_duplicate_option(i, &main_mobile_long_options[j]);
-		memcpy(&long_options[i], &main_mobile_long_options[j], sizeof(*long_options));
-	}
+	option_add('h', "help", 0);
+	option_add('v', "debug", 1);
+	option_add('k', "kanal", 1);
+	option_add(OPT_CHANNEL, "channel", 1);
+	option_add('a', "audio-device", 1);
+	option_add('s', "samplerate", 1);
+	option_add('i', "interval", 1);
+	option_add('b', "buffer", 1);
+	option_add('p', "pre-emphasis", 0);
+	option_add('d', "de-emphasis", 0);
+	option_add('g', "rx-gain", 1);
+	option_add('e', "echo-test", 0);
+	option_add('x', "mncc-cross", 0);
+	option_add('m', "mncc-sock", 0);
+	option_add(OPT_MNCC_NAME, "mncc-name", 1);
+	option_add('c', "call-device", 1);
+	option_add(OPT_CALL_SAMPLERATE, "call-samplerate", 1);
+	option_add('t', "tones", 0);
+	option_add('l', "loopback", 1);
+	option_add('r', "realtime", 1);
+	option_add(OPT_WRITE_RX_WAVE, "write-rx-wave", 1);
+	option_add(OPT_WRITE_TX_WAVE, "write-tx-wave", 1);
+	option_add(OPT_READ_RX_WAVE, "read-rx-wave", 1);
+	option_add(OPT_READ_TX_WAVE, "read-tx-wave", 1);
 #ifdef HAVE_SDR
-	for (j = 0; sdr_config_long_options[j].name; i++, j++) {
-		check_duplicate_option(i, &sdr_config_long_options[j]);
-		memcpy(&long_options[i], &sdr_config_long_options[j], sizeof(*long_options));
-	}
+	option_add(OPT_LIMESDR, "limesdr", 0);
+	sdr_config_add_options();
 #endif
-	for (; long_options_special->name; i++) {
-		check_duplicate_option(i, long_options_special);
-		memcpy(&long_options[i], long_options_special++, sizeof(*long_options));
-	}
-	
-	optstring = calloc(256, 2);
-	strcpy(optstring, main_mobile_optstring);
-#ifdef HAVE_SDR
-	strcat(optstring, sdr_config_optstring);
-#endif
-	strcat(optstring, optstring_special);
-}
+};
 
 void print_help(const char *arg0);
 
-void main_mobile_opt_switch(int c, char *arg0, int *skip_args)
+int main_mobile_handle_options(int short_option, int argi, char **argv)
 {
 	double gain_db;
-#ifdef HAVE_SDR
 	int rc;
-#endif
 
-	switch (c) {
+	switch (short_option) {
 	case 'h':
-		print_help(arg0);
-		exit(0);
+		print_help(argv[0]);
+		return 0;
 	case 'v':
-		if (!strcasecmp(optarg, "list")) {
+		if (!strcasecmp(argv[argi], "list")) {
 	                debug_list_cat();
-			exit(0);
+			return 0;
 		}
-		if (parse_debug_opt(optarg)) {
+		rc = parse_debug_opt(argv[argi]);
+		if (rc < 0) {
 			fprintf(stderr, "Failed to parse debug option, please use -h for help.\n");
-			exit(0);
+			return rc;
 		}
-		*skip_args += 2;
 		break;
 	case 'k':
 	case OPT_CHANNEL:
-		OPT_ARRAY(num_kanal, kanal, atoi(optarg))
-		*skip_args += 2;
+		OPT_ARRAY(num_kanal, kanal, atoi(argv[argi]))
 		break;
 	case 'a':
-		OPT_ARRAY(num_audiodev, audiodev, strdup(optarg))
-		*skip_args += 2;
+		OPT_ARRAY(num_audiodev, audiodev, strdup(argv[argi]))
 		break;
 	case 's':
-		samplerate = atoi(optarg);
-		*skip_args += 2;
+		samplerate = atoi(argv[argi]);
 		break;
 	case 'i':
-		interval = atoi(optarg);
-		*skip_args += 2;
+		interval = atoi(argv[argi]);
 		if (interval < 1)
 			interval = 1;
 		if (interval > 25)
 			interval = 25;
 		break;
 	case 'b':
-		latency = atoi(optarg);
-		*skip_args += 2;
+		latency = atoi(argv[argi]);
 		break;
 	case 'p':
 		if (!uses_emphasis) {
 			no_emph:
 			fprintf(stderr, "This network does not use emphasis, please do not enable pre- or de-emphasis! Disable emphasis on transceiver, if possible.\n");
-			exit(0);
+			return -EINVAL;
 		}
 		do_pre_emphasis = 1;
-		*skip_args += 1;
 		break;
 	case 'd':
 		if (!uses_emphasis)
 			goto no_emph;
 		do_de_emphasis = 1;
-		*skip_args += 1;
 		break;
 	case 'g':
-		gain_db = atof(optarg);
+		gain_db = atof(argv[argi]);
 		if (gain_db < 0.0) {
 			fprintf(stderr, "Given gain is below 0. To reduce RX signal, use sound card's mixer (or resistor net)!\n");
-			exit(0);
+			return -EINVAL;
 		}
 		rx_gain = pow(10, gain_db / 20.0);
-		*skip_args += 2;
 		break;
 	case 'e':
 		echo_test = 1;
-		*skip_args += 1;
 		break;
 	case 'x':
 		use_mncc_cross = 1;
-		*skip_args += 1;
 		break;
 	case 'm':
 		use_mncc_sock = 1;
-		*skip_args += 1;
 		break;
 	case OPT_MNCC_NAME:
-		mncc_name = strdup(optarg);
-		*skip_args += 2;
+		mncc_name = strdup(argv[argi]);
 		break;
 	case 'c':
-		call_audiodev = strdup(optarg);
-		*skip_args += 2;
+		call_audiodev = strdup(argv[argi]);
 		break;
 	case OPT_CALL_SAMPLERATE:
-		call_samplerate = atoi(optarg);
-		*skip_args += 2;
+		call_samplerate = atoi(argv[argi]);
 		break;
 	case 't':
-		send_patterns = atoi(optarg);
-		*skip_args += 2;
+		send_patterns = atoi(argv[argi]);
 		break;
 	case 'l':
-		loopback = atoi(optarg);
-		*skip_args += 2;
+		loopback = atoi(argv[argi]);
 		break;
 	case 'r':
-		rt_prio = atoi(optarg);
-		*skip_args += 2;
+		rt_prio = atoi(argv[argi]);
 		break;
 	case OPT_WRITE_RX_WAVE:
-		write_rx_wave = strdup(optarg);
-		*skip_args += 2;
+		write_rx_wave = strdup(argv[argi]);
 		break;
 	case OPT_WRITE_TX_WAVE:
-		write_tx_wave = strdup(optarg);
-		*skip_args += 2;
+		write_tx_wave = strdup(argv[argi]);
 		break;
 	case OPT_READ_RX_WAVE:
-		read_rx_wave = strdup(optarg);
-		*skip_args += 2;
+		read_rx_wave = strdup(argv[argi]);
 		break;
 	case OPT_READ_TX_WAVE:
-		read_tx_wave = strdup(optarg);
-		*skip_args += 2;
+		read_tx_wave = strdup(argv[argi]);
 		break;
+#ifdef HAVE_SDR
+	case OPT_LIMESDR:
+		{
+			char *argv_lime[] = { argv[0],
+				"--sdr-soapy",
+				"--sdr-rx-antenna", "LNAL",
+				"--sdr-rx-gain", "30",
+				"--sdr-tx-gain", "30",
+				"--sdr-samplerate", "5000000",
+				"--sdr-bandwidth", "15000000",
+				"-s", "200000",
+			};
+			int argc_lime = sizeof(argv_lime) / sizeof (*argv_lime);
+			return options_command_line(argc_lime, argv_lime, main_mobile_handle_options);
+		}
+#endif
 	default:
 #ifdef HAVE_SDR
-		rc = sdr_config_opt_switch(c, skip_args);
-		if (rc < 0)
-			exit (0);
-		
+		return sdr_config_handle_options(short_option, argi, argv);
+#else
+		return -EINVAL;
 #endif
-		break;
 	}
+
+	return 1;
 }
 
 /* global variable to quit main loop */
diff --git a/src/libmobile/main_mobile.h b/src/libmobile/main_mobile.h
index e958f2c..4d5dffc 100644
--- a/src/libmobile/main_mobile.h
+++ b/src/libmobile/main_mobile.h
@@ -22,10 +22,8 @@ extern const char *read_tx_wave;
 void main_mobile_init(void);
 void main_mobile_print_help(const char *arg0, const char *ext_usage);
 void main_mobile_print_hotkeys(void);
-extern struct option *long_options;
-extern char *optstring;
-void main_mobile_set_options(const char *optstring_special, struct option *long_options_special);
-void main_mobile_opt_switch(int c, char *arg0, int *skip_args);
+void main_mobile_add_options(void);
+int main_mobile_handle_options(int short_option, int argi, char **argv);
 
 #define OPT_ARRAY(num_name, name, value) \
 { \
diff --git a/src/liboptions/Makefile.am b/src/liboptions/Makefile.am
new file mode 100644
index 0000000..0d10fa7
--- /dev/null
+++ b/src/liboptions/Makefile.am
@@ -0,0 +1,7 @@
+AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
+
+noinst_LIBRARIES = liboptions.a
+
+liboptions_a_SOURCES = \
+	options.c
+
diff --git a/src/liboptions/options.c b/src/liboptions/options.c
new file mode 100644
index 0000000..e9cc25b
--- /dev/null
+++ b/src/liboptions/options.c
@@ -0,0 +1,221 @@
+/* command line options and config file parsing
+ *
+ * (C) 2018 by Andreas Eversberg 
+ * All Rights Reserved
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see .
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "options.h"
+#include "../libdebug/debug.h"
+
+typedef struct option {
+	struct option *next;
+	int short_option;
+	const char *long_option;
+	int parameter_count;
+} option_t;
+
+static option_t *option_head = NULL;
+static option_t **option_tailp = &option_head;
+static int first_option = 1;
+
+void option_add(int short_option, const char *long_option, int parameter_count)
+{
+	option_t *option;
+
+	/* check if option already exists */
+	for (option = option_head; option; option = option->next) {
+		if (option->short_option == short_option
+		 || !strcmp(option->long_option, long_option)) {
+			PDEBUG(DOPTIONS, DEBUG_ERROR, "Option '%s' added twice, please fix!\n", option->long_option);
+			abort();
+		}
+	}
+
+	option = calloc(1, sizeof(*option));
+	if (!option) {
+		PDEBUG(DOPTIONS, DEBUG_ERROR, "No mem!\n");
+		abort();
+	}
+
+	option->short_option = short_option;
+	option->long_option = long_option;
+	option->parameter_count = parameter_count;
+	*option_tailp = option;
+	option_tailp = &(option->next);
+}
+
+// FIXME: support more than one option */
+int options_config_file(const char *config_file, int (*handle_options)(int short_option, int argi, char *argv[]))
+{
+	static const char *home;
+	char config[256];
+	FILE *fp;
+	char buffer[256], opt[256], param[256], *p, *argv[1];
+	int line;
+	int rc = 1;
+	int i;
+	option_t *option;
+
+	/* open config file */
+	home = getenv("HOME");
+        if (home == NULL)
+		return 1;
+	sprintf(config, "%s/%s", home, config_file + 2);
+		
+	fp = fopen(config, "r");
+	if (!fp) {
+		PDEBUG(DOPTIONS, DEBUG_INFO, "Config file '%s' seems not to exist, using command line options only.\n", config);
+		return 1;
+	}
+
+	/* parse config file */
+	line = 0;
+	while((fgets(buffer, sizeof(buffer), fp))) {
+		line++;
+		/* prevent buffer overflow */
+		buffer[sizeof(buffer) - 1] = '\0';
+		/* cut away new-line and white spaces */
+		while (buffer[0] && buffer[strlen(buffer) - 1] <= ' ')
+			 buffer[strlen(buffer) - 1] = '\0';
+		p = buffer;
+		/* remove white spaces in front of first keyword */
+		while (*p > '\0' && *p <= ' ')
+			p++;
+		/* ignore '#' lines */
+		if (*p == '#')
+			continue;
+		/* get option form line */
+		i = 0;
+		while (*p > ' ')
+			opt[i++] = *p++;
+		opt[i] = '\0';
+		if (opt[0] == '\0')
+			continue;
+		/* skip white spaces behind option */
+		while (*p > '\0' && *p <= ' ')
+			p++;
+		/* get param from line */
+		i = 0;
+		while (*p > ' ')
+			param[i++] = *p++;
+		param[i] = '\0';
+		/* search option */
+		for (option = option_head; option; option = option->next) {
+			if (opt[0] == option->short_option && opt[1] == '\0') {
+				PDEBUG(DOPTIONS, DEBUG_INFO, "Config file option '%s' ('%s'), parameter '%s'\n", opt, option->long_option, param);
+				break;
+			}
+			if (!strcmp(opt, option->long_option)) {
+				PDEBUG(DOPTIONS, DEBUG_INFO, "Config file option '%s', parameter '%s'\n", opt, param);
+				break;
+			}
+		}
+		if (!option) {
+			PDEBUG(DOPTIONS, DEBUG_ERROR, "Given option '%s' in config file '%s' at line %d is not a valid option, use '-h' for help!\n", opt, config_file, line);
+			rc = -EINVAL;
+			goto done;
+		}
+		if (option->parameter_count && !param[0]) {
+			PDEBUG(DOPTIONS, DEBUG_ERROR, "Given option '%s' in config file '%s' at line %d requires %d parameter(s), use '-h' for help!\n", opt, config_file, line,  option->parameter_count);
+			return -EINVAL;
+		}
+		argv[0] = param;
+		rc = handle_options(option->short_option, 0, argv);
+		if (rc <= 0)
+			goto done;
+		first_option = 0;
+	}
+
+done:
+	/* close config file */
+	fclose(fp);
+
+	return rc;
+}
+
+int options_command_line(int argc, char *argv[], int (*handle_options)(int short_option, int argi, char *argv[]))
+{
+	option_t *option;
+	int argi, i;
+	int rc;
+
+	for (argi = 1; argi < argc; argi++) {
+		if (argv[argi][0] == '-') {
+			if (argv[argi][1] != '-') {
+				if (strlen(argv[argi]) != 2) {
+					PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' exceeds one character, use '-h' for help!\n", argv[argi]);
+					return -EINVAL;
+				}
+				/* -x */
+				for (option = option_head; option; option = option->next) {
+					if (argv[argi][1] == option->short_option) {
+						if (option->parameter_count && argi + option->parameter_count < argc)
+							PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s' ('--%s'), parameter '%s'\n", argv[argi], option->long_option, argv[argi + 1]);
+						else
+							PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s' ('--%s')\n", argv[argi], option->long_option);
+						break;
+					}
+				}
+			} else {
+				/* --xxxxxx */
+				for (option = option_head; option; option = option->next) {
+					if (!strcmp(argv[argi] + 2, option->long_option)) {
+						if (option->parameter_count && argi + option->parameter_count < argc)
+							PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s', parameter '%s'\n", argv[argi], argv[argi + 1]);
+						else
+							PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s'\n", argv[argi]);
+						break;
+					}
+				}
+			}
+			if (!option) {
+				PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' is not a valid option, use '-h' for help!\n", argv[argi]);
+				return -EINVAL;
+			}
+			if (argi + option->parameter_count >= argc) {
+				PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' requires %d parameter(s), use '-h' for help!\n", argv[argi], option->parameter_count);
+				return -EINVAL;
+			}
+			rc = handle_options(option->short_option, argi + 1, argv);
+			if (rc <= 0)
+				return rc;
+			first_option = 0;
+			argi += option->parameter_count;
+		} else
+			break;
+	}
+
+	/* no more options, so we check if there is an option after a non-option parameter */
+	for (i = argi; i < argc; i++) {
+		if (argv[i][0] == '-') {
+			PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' behind command line parameter '%s' not allowed! Please put all command line options before command line parameter(s).\n", argv[i], argv[argi]);
+			return -EINVAL;
+		}
+	}
+
+	return argi;
+}
+
+int option_is_first(void)
+{
+	return first_option;
+}
+
diff --git a/src/liboptions/options.h b/src/liboptions/options.h
new file mode 100644
index 0000000..6f7e41a
--- /dev/null
+++ b/src/liboptions/options.h
@@ -0,0 +1,6 @@
+
+void option_add(int short_option, const char *long_option, int parameter_count);
+int options_config_file(const char *config_file, int (*handle_options)(int short_option, int argi, char *argv[]));
+int options_command_line(int argc, char *argv[], int (*handle_options)(int short_option, int argi, char *argv[]));
+int option_is_first(void);
+
diff --git a/src/libsdr/sdr.c b/src/libsdr/sdr.c
index dec6ab6..0bcdf9a 100644
--- a/src/libsdr/sdr.c
+++ b/src/libsdr/sdr.c
@@ -25,7 +25,6 @@ enum paging_signal;
 #include 
 #include 
 #include 
-#include 
 #define __USE_GNU
 #include 
 #include 
diff --git a/src/libsdr/sdr_config.c b/src/libsdr/sdr_config.c
index 8fbb87c..dfbada7 100644
--- a/src/libsdr/sdr_config.c
+++ b/src/libsdr/sdr_config.c
@@ -23,8 +23,9 @@ enum paging_signal;
 #include 
 #include 
 #include 
-#include 
+#include 
 #include "../libsample/sample.h"
+#include "../liboptions/options.h"
 #include "sdr.h"
 #include "sdr_config.h"
 
@@ -122,43 +123,40 @@ void sdr_config_print_hotkeys(void)
 #define	OPT_SDR_SWAP_LINKS	1517
 #define OPT_SDR_UHD_TX_TS	1518
 
-struct option sdr_config_long_options[] = {
-	{"sdr-uhd", 0, 0, OPT_SDR_UHD},
-	{"sdr-soapy", 0, 0, OPT_SDR_SOAPY},
-	{"sdr-channel", 1, 0, OPT_SDR_CHANNEL},
-	{"sdr-device-args", 1, 0, OPT_SDR_DEVICE_ARGS},
-	{"sdr-stream-args", 1, 0, OPT_SDR_STREAM_ARGS},
-	{"sdr-tune-args", 1, 0, OPT_SDR_TUNE_ARGS},
-	{"sdr-samplerate", 1, 0, OPT_SDR_SAMPLERATE},
-	{"sdr-lo-offset", 1, 0, OPT_SDR_LO_OFFSET},
-	{"sdr-bandwidth", 1, 0, OPT_SDR_BANDWIDTH},
-	{"sdr-rx-antenna", 1, 0, OPT_SDR_RX_ANTENNA},
-	{"sdr-tx-antenna", 1, 0, OPT_SDR_TX_ANTENNA},
-	{"sdr-rx-gain", 1, 0, OPT_SDR_RX_GAIN},
-	{"sdr-tx-gain", 1, 0, OPT_SDR_TX_GAIN},
-	{"write-iq-rx-wave", 1, 0, OPT_WRITE_IQ_RX_WAVE},
-	{"write-iq-tx-wave", 1, 0, OPT_WRITE_IQ_TX_WAVE},
-	{"read-iq-rx-wave", 1, 0, OPT_READ_IQ_RX_WAVE},
-	{"read-iq-tx-wave", 1, 0, OPT_READ_IQ_TX_WAVE},
-	{"sdr-swap-links", 0, 0, OPT_SDR_SWAP_LINKS},
-	{"sdr-uhd-tx-timestamps", 0, 0, OPT_SDR_UHD_TX_TS},
-	{0, 0, 0, 0}
-};
-
-const char *sdr_config_optstring = "";
+void sdr_config_add_options(void)
+{
+	option_add(OPT_SDR_UHD, "sdr-uhd", 0);
+	option_add(OPT_SDR_SOAPY, "sdr-soapy", 0);
+	option_add(OPT_SDR_CHANNEL, "sdr-channel", 1);
+	option_add(OPT_SDR_DEVICE_ARGS, "sdr-device-args", 1);
+	option_add(OPT_SDR_STREAM_ARGS, "sdr-stream-args", 1);
+	option_add(OPT_SDR_TUNE_ARGS, "sdr-tune-args", 1);
+	option_add(OPT_SDR_SAMPLERATE, "sdr-samplerate", 1);
+	option_add(OPT_SDR_LO_OFFSET, "sdr-lo-offset", 1);
+	option_add(OPT_SDR_BANDWIDTH, "sdr-bandwidth", 1);
+	option_add(OPT_SDR_RX_ANTENNA, "sdr-rx-antenna", 1);
+	option_add(OPT_SDR_TX_ANTENNA, "sdr-tx-antenna", 1);
+	option_add(OPT_SDR_RX_GAIN, "sdr-rx-gain", 1);
+	option_add(OPT_SDR_TX_GAIN, "sdr-tx-gain", 1);
+	option_add(OPT_WRITE_IQ_RX_WAVE, "write-iq-rx-wave", 1);
+	option_add(OPT_WRITE_IQ_TX_WAVE, "write-iq-tx-wave", 1);
+	option_add(OPT_READ_IQ_RX_WAVE, "read-iq-rx-wave", 1);
+	option_add(OPT_READ_IQ_TX_WAVE, "read-iq-tx-wave", 1);
+	option_add(OPT_SDR_SWAP_LINKS, "sdr-swap-links", 0);
+	option_add(OPT_SDR_UHD_TX_TS, "sdr-uhd-tx-timestamps", 0);
+}
 
-int sdr_config_opt_switch(int c, int *skip_args)
+int sdr_config_handle_options(int short_option, int argi, char **argv)
 {
-	switch (c) {
+	switch (short_option) {
 	case OPT_SDR_UHD:
 #ifdef HAVE_UHD
 		sdr_config->uhd = 1;
 		use_sdr = 1;
 #else
 		fprintf(stderr, "UHD SDR support not compiled in!\n");
-		exit(0);
+		return -EINVAL;
 #endif
-		*skip_args += 1;
 		break;
 	case OPT_SDR_SOAPY:
 #ifdef HAVE_SOAPY
@@ -166,83 +164,65 @@ int sdr_config_opt_switch(int c, int *skip_args)
 		use_sdr = 1;
 #else
 		fprintf(stderr, "SoapySDR support not compiled in!\n");
-		exit(0);
+		return -EINVAL;
 #endif
-		*skip_args += 1;
 		break;
 	case OPT_SDR_CHANNEL:
-		sdr_config->channel = atoi(optarg);
-		*skip_args += 2;
+		sdr_config->channel = atoi(argv[argi]);
 		break;
 	case OPT_SDR_DEVICE_ARGS:
-		sdr_config->device_args = strdup(optarg);
-		*skip_args += 2;
+		sdr_config->device_args = strdup(argv[argi]);
 		break;
 	case OPT_SDR_STREAM_ARGS:
-		sdr_config->stream_args = strdup(optarg);
-		*skip_args += 2;
+		sdr_config->stream_args = strdup(argv[argi]);
 		break;
 	case OPT_SDR_TUNE_ARGS:
-		sdr_config->tune_args = strdup(optarg);
-		*skip_args += 2;
+		sdr_config->tune_args = strdup(argv[argi]);
 		break;
 	case OPT_SDR_SAMPLERATE:
-		sdr_config->samplerate = atoi(optarg);
-		*skip_args += 2;
+		sdr_config->samplerate = atoi(argv[argi]);
 		break;
 	case OPT_SDR_LO_OFFSET:
-		sdr_config->lo_offset = atof(optarg);
-		*skip_args += 2;
+		sdr_config->lo_offset = atof(argv[argi]);
 		break;
 	case OPT_SDR_BANDWIDTH:
-		sdr_config->bandwidth = atof(optarg);
-		*skip_args += 2;
+		sdr_config->bandwidth = atof(argv[argi]);
 		break;
 	case OPT_SDR_RX_ANTENNA:
-		sdr_config->rx_antenna = strdup(optarg);
-		*skip_args += 2;
+		sdr_config->rx_antenna = strdup(argv[argi]);
 		break;
 	case OPT_SDR_TX_ANTENNA:
-		sdr_config->tx_antenna = strdup(optarg);
-		*skip_args += 2;
+		sdr_config->tx_antenna = strdup(argv[argi]);
 		break;
 	case OPT_SDR_RX_GAIN:
-		sdr_config->rx_gain = atof(optarg);
-		*skip_args += 2;
+		sdr_config->rx_gain = atof(argv[argi]);
 		break;
 	case OPT_SDR_TX_GAIN:
-		sdr_config->tx_gain = atof(optarg);
-		*skip_args += 2;
+		sdr_config->tx_gain = atof(argv[argi]);
 		break;
 	case OPT_WRITE_IQ_RX_WAVE:
-		sdr_config->write_iq_rx_wave = strdup(optarg);
-		*skip_args += 2;
+		sdr_config->write_iq_rx_wave = strdup(argv[argi]);
 		break;
 	case OPT_WRITE_IQ_TX_WAVE:
-		sdr_config->write_iq_tx_wave = strdup(optarg);
-		*skip_args += 2;
+		sdr_config->write_iq_tx_wave = strdup(argv[argi]);
 		break;
 	case OPT_READ_IQ_RX_WAVE:
-		sdr_config->read_iq_rx_wave = strdup(optarg);
-		*skip_args += 2;
+		sdr_config->read_iq_rx_wave = strdup(argv[argi]);
 		break;
 	case OPT_READ_IQ_TX_WAVE:
-		sdr_config->read_iq_tx_wave = strdup(optarg);
-		*skip_args += 2;
+		sdr_config->read_iq_tx_wave = strdup(argv[argi]);
 		break;
 	case OPT_SDR_SWAP_LINKS:
 		sdr_config->swap_links = 1;
-		*skip_args += 1;
 		break;
 	case OPT_SDR_UHD_TX_TS:
 		sdr_config->uhd_tx_timestamps = 1;
-		*skip_args += 1;
 		break;
 	default:
-		return -1;
+		return -EINVAL;
 	}
 
-	return 0;
+	return 1;
 }
 
 int sdr_configure(int samplerate)
diff --git a/src/libsdr/sdr_config.h b/src/libsdr/sdr_config.h
index 9bfba1f..1feb323 100644
--- a/src/libsdr/sdr_config.h
+++ b/src/libsdr/sdr_config.h
@@ -26,8 +26,7 @@ extern sdr_config_t *sdr_config;
 void sdr_config_init(double lo_offset);
 void sdr_config_print_help(void);
 void sdr_config_print_hotkeys(void);
-extern struct option sdr_config_long_options[];
-extern const char *sdr_config_optstring;
-int sdr_config_opt_switch(int c, int *skip_args);
+void sdr_config_add_options(void);
+int sdr_config_handle_options(int short_option, int argi, char **argv);
 int sdr_configure(int samplerate);
 
diff --git a/src/nmt/Makefile.am b/src/nmt/Makefile.am
index cee6953..5f15ff4 100644
--- a/src/nmt/Makefile.am
+++ b/src/nmt/Makefile.am
@@ -22,6 +22,7 @@ nmt_SOURCES = \
 nmt_LDADD = \
 	$(COMMON_LA) \
 	libdmssms.a \
+	$(top_builddir)/src/liboptions/liboptions.a \
 	$(top_builddir)/src/libdebug/libdebug.a \
 	$(top_builddir)/src/libmobile/libmobile.a \
 	$(top_builddir)/src/libdisplay/libdisplay.a \
diff --git a/src/nmt/main.c b/src/nmt/main.c
index ad8eb56..1d3eb2c 100644
--- a/src/nmt/main.c
+++ b/src/nmt/main.c
@@ -19,16 +19,17 @@
 
 #include 
 #include 
-#include 
 #include 
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include "../libsample/sample.h"
 #include "../libmobile/main_mobile.h"
 #include "../libdebug/debug.h"
+#include "../liboptions/options.h"
 #include "nmt.h"
 #include "frame.h"
 #include "dsp.h"
@@ -97,141 +98,118 @@ void print_help(const char *arg0)
 	main_mobile_print_hotkeys();
 }
 
-static int handle_options(int argc, char **argv)
+static void add_options(void)
 {
+	main_mobile_add_options();
+	option_add('N', "nmt-system", 1);
+	option_add('T', "channel-type", 1);
+	option_add('P', "ms-power", 1);
+	option_add('Y', "traffic-area", 1);
+	option_add('A', "area-number", 1);
+	option_add('C', "compandor", 1);
+	option_add('0', "supervisory", 1);
+	option_add('S', "smsc-number", 1);
+	option_add('I', "caller-id", 1);
+}
+
+static int handle_options(int short_option, int argi, char **argv)
+{
+	int rc;
 	char *p;
 	int super;
-	int skip_args = 0;
-
-	static struct option long_options_special[] = {
-		{"nmt-system", 1, 0, 'N'},
-		{"channel-type", 1, 0, 'T'},
-		{"ms-power", 1, 0, 'P'},
-		{"traffic-area", 1, 0, 'Y'},
-		{"area-number", 1, 0, 'A'},
-		{"compandor", 1, 0, 'C'},
-		{"supervisory", 1, 0, '0'},
-		{"smsc-number", 1, 0, 'S'},
-		{"caller-id", 1, 0, 'I'},
-		{0, 0, 0, 0}
-	};
-
-	main_mobile_set_options("N:T:P:Y:A:C:0:S:I:", long_options_special);
-
-	while (1) {
-		int option_index = 0, c, rc;
-		static int first_option = 1;
-
-		c = getopt_long(argc, argv, optstring, long_options, &option_index);
-
-		if (c == -1)
-			break;
-
-		switch (c) {
-		case 'N':
-			nmt_system = atoi(optarg);
-			if (nmt_system != 450 && nmt_system != 900) {
-				fprintf(stderr, "Error, NMT system type '%s' unknown. Please use '-N 450' for NMT-450 or '-N 900' for NMT-900.\n", optarg);
-				exit(0);
-			}
-			if (nmt_system == 900)
-				ms_power = 0;
-			if (!first_option) {
-				fprintf(stderr, "Please specify the NMT system (-N) as first command line option!\n");
-				exit(0);
-			}
-			skip_args += 2;
-			break;
-		case 'T':
-			if (!strcmp(optarg, "list")) {
-				nmt_channel_list(nmt_system);
-				exit(0);
-			}
-			rc = nmt_channel_by_short_name(nmt_system, optarg);
-			if (rc < 0) {
-				fprintf(stderr, "Error, channel type '%s' unknown. Please use '-t list' to get a list. I suggest to use the default.\n", optarg);
-				exit(0);
-			}
-			OPT_ARRAY(num_chan_type, chan_type, rc)
-			skip_args += 2;
-			break;
-		case 'P':
-			ms_power = atoi(optarg);
-			if (ms_power > 3)
-				ms_power = 3;
-			if (ms_power < 0)
-				ms_power = 0;
-			skip_args += 2;
-			break;
-		case 'Y':
-
-			if (!strcmp(optarg, "list")) {
-				nmt_country_list(nmt_system);
-				exit(0);
-			}
-			/* digits */
-			strncpy(country, optarg, sizeof(country) - 1);
-			country[sizeof(country) - 1] = '\0';
-			p = strchr(country, ',');
-			if (!p) {
-				fprintf(stderr, "Illegal traffic area '%s', see '-h' for help\n", optarg);
-				exit(0);
-			}
-			*p++ = '\0';
-			rc = nmt_country_by_short_name(nmt_system, country);
-			if (rc < 0) {
+
+	switch (short_option) {
+	case 'N':
+		nmt_system = atoi(argv[argi]);
+		if (nmt_system != 450 && nmt_system != 900) {
+			fprintf(stderr, "Error, NMT system type '%s' unknown. Please use '-N 450' for NMT-450 or '-N 900' for NMT-900.\n", argv[argi]);
+			return -EINVAL;
+		}
+		if (nmt_system == 900)
+			ms_power = 0;
+		if (!option_is_first()) {
+			fprintf(stderr, "Please specify the NMT system (-N) as first command line option!\n");
+			return -EINVAL;
+		}
+		break;
+	case 'T':
+		if (!strcmp(argv[argi], "list")) {
+			nmt_channel_list(nmt_system);
+			return 0;
+		}
+		rc = nmt_channel_by_short_name(nmt_system, argv[argi]);
+		if (rc < 0) {
+			fprintf(stderr, "Error, channel type '%s' unknown. Please use '-t list' to get a list. I suggest to use the default.\n", argv[argi]);
+			return -EINVAL;
+		}
+		OPT_ARRAY(num_chan_type, chan_type, rc)
+		break;
+	case 'P':
+		ms_power = atoi(argv[argi]);
+		if (ms_power > 3)
+			ms_power = 3;
+		if (ms_power < 0)
+			ms_power = 0;
+		break;
+	case 'Y':
+
+		if (!strcmp(argv[argi], "list")) {
+			nmt_country_list(nmt_system);
+			return 0;
+		}
+		/* digits */
+		strncpy(country, argv[argi], sizeof(country) - 1);
+		country[sizeof(country) - 1] = '\0';
+		p = strchr(country, ',');
+		if (!p) {
+			fprintf(stderr, "Illegal traffic area '%s', see '-h' for help\n", argv[argi]);
+			return -EINVAL;
+		}
+		*p++ = '\0';
+		rc = nmt_country_by_short_name(nmt_system, country);
+		if (rc < 0) {
 error_ta:
-				fprintf(stderr, "Invalid traffic area '%s', use '-Y list' for a list of valid areas\n", optarg);
-				exit(0);
-			}
-			traffic_area[0] = rc + '0';
-			if (p[strlen(p) - 1] != '!') {
-				rc = nmt_ta_by_short_name(nmt_system, country, atoi(p));
-				if (rc < 0)
-					goto error_ta;
-			}
-			nmt_value2digits(atoi(p), traffic_area + 1, 1);
-			traffic_area[2] = '\0';
-			skip_args += 2;
-			break;
-		case 'A':
-			area_no = optarg[0] - '0';
-			if (area_no > 4) {
-				fprintf(stderr, "Area number '%s' out of range, please use 1..4 or 0 for no area\n", optarg);
-				exit(0);
-			}
-			skip_args += 2;
-			break;
-		case 'C':
-			compandor = atoi(optarg);
-			skip_args += 2;
-			break;
-		case '0':
-			super = atoi(optarg);
-			if (super < 0 || super > 4) {
-				fprintf(stderr, "Given supervisory signal is wrong, use '-h' for help!\n");
-				exit(0);
-			}
-			OPT_ARRAY(num_supervisory, supervisory, super)
-			skip_args += 2;
-			break;
-		case 'S':
-			smsc_number = strdup(optarg);
-			skip_args += 2;
-			break;
-		case 'I':
-			send_callerid = atoi(optarg);
-			skip_args += 2;
-			break;
-		default:
-			main_mobile_opt_switch(c, argv[0], &skip_args);
+			fprintf(stderr, "Invalid traffic area '%s', use '-Y list' for a list of valid areas\n", argv[argi]);
+			return -EINVAL;
+		}
+		traffic_area[0] = rc + '0';
+		if (p[strlen(p) - 1] != '!') {
+			rc = nmt_ta_by_short_name(nmt_system, country, atoi(p));
+			if (rc < 0)
+				goto error_ta;
+		}
+		nmt_value2digits(atoi(p), traffic_area + 1, 1);
+		traffic_area[2] = '\0';
+		break;
+	case 'A':
+		area_no = argv[argi][0] - '0';
+		if (area_no > 4) {
+			fprintf(stderr, "Area number '%s' out of range, please use 1..4 or 0 for no area\n", argv[argi]);
+			return -EINVAL;
+		}
+		break;
+	case 'C':
+		compandor = atoi(argv[argi]);
+		break;
+	case '0':
+		super = atoi(argv[argi]);
+		if (super < 0 || super > 4) {
+			fprintf(stderr, "Given supervisory signal is wrong, use '-h' for help!\n");
+			return -EINVAL;
 		}
-		first_option = 0;
+		OPT_ARRAY(num_supervisory, supervisory, super)
+		break;
+	case 'S':
+		smsc_number = strdup(argv[argi]);
+		break;
+	case 'I':
+		send_callerid = atoi(argv[argi]);
+		break;
+	default:
+		return main_mobile_handle_options(short_option, argi, argv);
 	}
 
-	free(long_options);
-
-	return skip_args;
+	return 1;
 }
 
 static void myhandler(void)
@@ -280,8 +258,7 @@ int submit_sms(const char *sms)
 
 int main(int argc, char *argv[])
 {
-	int rc;
-	int skip_args;
+	int rc, argi;
 	const char *station_id = "";
 	int mandatory = 0;
 	int i;
@@ -292,12 +269,17 @@ int main(int argc, char *argv[])
 
 	main_mobile_init();
 
-	skip_args = handle_options(argc, argv);
-	argc -= skip_args;
-	argv += skip_args;
+	/* handle options / config file */
+	add_options();
+	rc = options_config_file("~/.osmocom/analog/nmt.conf", handle_options);
+	if (rc < 0)
+		return 0;
+	argi = options_command_line(argc, argv, handle_options);
+	if (argi <= 0)
+		return argi;
 
-	if (argc > 1) {
-		station_id = argv[1];
+	if (argi < argc) {
+		station_id = argv[argi];
 		if (strlen(station_id) != 7) {
 			printf("Given station ID '%s' does not have 7 digits\n", station_id);
 			return 0;
@@ -338,7 +320,7 @@ int main(int argc, char *argv[])
 		num_audiodev = 1; /* use default */
 	if (num_kanal != num_audiodev) {
 		fprintf(stderr, "You need to specify as many sound devices as you have channels.\n");
-		exit(0);
+		return -EINVAL;
 	}
 	if (num_kanal == 1 && num_chan_type == 0) {
 		num_chan_type = 1; /* use default */
@@ -347,14 +329,14 @@ int main(int argc, char *argv[])
 	}
 	if (num_kanal != num_chan_type) {
 		fprintf(stderr, "You need to specify as many channel types as you have channels.\n");
-		exit(0);
+		return -EINVAL;
 	}
 	if (num_kanal == 1 && num_supervisory == 0)
 		num_supervisory = 1; /* use default */
 	if (num_kanal != num_supervisory) {
 		fprintf(stderr, "You need to specify as many supervisory signals as you have channels.\n");
 		fprintf(stderr, "They shall be different at channels that are close to each other.\n");
-		exit(0);
+		return -EINVAL;
 	}
 	if (num_kanal) {
 		uint8_t super[5] = { 0, 0, 0, 0, 0 };
@@ -378,7 +360,7 @@ int main(int argc, char *argv[])
 	}
 
 	if (mandatory) {
-		print_help(argv[-skip_args]);
+		print_help(argv[0]);
 		return 0;
 	}
 
diff --git a/src/r2000/Makefile.am b/src/r2000/Makefile.am
index 9500a2c..120221b 100644
--- a/src/r2000/Makefile.am
+++ b/src/r2000/Makefile.am
@@ -12,6 +12,7 @@ radiocom2000_SOURCES = \
 	main.c
 radiocom2000_LDADD = \
 	$(COMMON_LA) \
+	$(top_builddir)/src/liboptions/liboptions.a \
 	$(top_builddir)/src/libdebug/libdebug.a \
 	$(top_builddir)/src/libmobile/libmobile.a \
 	$(top_builddir)/src/libdisplay/libdisplay.a \
diff --git a/src/r2000/main.c b/src/r2000/main.c
index c772d75..bf4e98f 100644
--- a/src/r2000/main.c
+++ b/src/r2000/main.c
@@ -19,16 +19,17 @@
 
 #include 
 #include 
-#include 
 #include 
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include "../libsample/sample.h"
 #include "../libmobile/main_mobile.h"
 #include "../libdebug/debug.h"
+#include "../liboptions/options.h"
 #include "r2000.h"
 #include "dsp.h"
 #include "frame.h"
@@ -102,179 +103,154 @@ void print_help(const char *arg0)
 #define OPT_TAXE	258
 #define OPT_DESTRUCTION	259
 
-static int handle_options(int argc, char **argv)
+static void add_options(void)
 {
-	int skip_args = 0;
-
-	static struct option long_options_special[] = {
-		{"band", 1, 0, 'B'},
-		{"bande", 1, 0, OPT_BANDE},
-		{"channel-type", 1, 0, 'T'},
-		{"relais", 1, 0, 'R'},
-		{"deport", 1, 0, OPT_DEPORT},
-		{"agi", 1, 0, 'I'},
-		{"sm-power", 1, 0, 'P'},
-		{"taxe", 1, 0, OPT_TAXE},
-		{"crins", 1, 0, 'C'},
-		{"destruction", 1, 0, OPT_DESTRUCTION},
-		{"nconv", 1, 0, 'N'},
-		{"recall", 1, 0, 'S'},
-		{0, 0, 0, 0}
-	};
-
-	main_mobile_set_options("B:T:R:I:P:C:N:S", long_options_special);
-
-	while (1) {
-		int option_index = 0, c, rc;
-
-		c = getopt_long(argc, argv, optstring, long_options, &option_index);
-
-		if (c == -1)
-			break;
+	main_mobile_add_options();
+	option_add('B', "band", 1);
+	option_add(OPT_BANDE, "bande", 1);
+	option_add('T', "channel-type", 1);
+	option_add('R', "relais", 1);
+	option_add(OPT_DEPORT, "deport", 1);
+	option_add('I', "agi", 1);
+	option_add('P', "sm-power", 1);
+	option_add(OPT_TAXE, "taxe", 1);
+	option_add('C', "crins", 1);
+	option_add(OPT_DESTRUCTION, "destruction", 1);
+	option_add('N', "nconv", 1);
+	option_add('S', "recall", 1);
+}
 
-		switch (c) {
-		case 'B':
-		case OPT_BANDE:
-			if (!strcmp(optarg, "list")) {
-				r2000_band_list();
-				exit(0);
-			}
-			band = atoi(optarg);
-			skip_args += 2;
-			break;
-		case 'T':
-			if (!strcmp(optarg, "list")) {
-				r2000_channel_list();
-				exit(0);
-			}
-			rc = r2000_channel_by_short_name(optarg);
-			if (rc < 0) {
-				fprintf(stderr, "Error, channel type '%s' unknown. Please use '-t list' to get a list. I suggest to use the default.\n", optarg);
-				exit(0);
-			}
-			OPT_ARRAY(num_chan_type, chan_type, rc)
-			skip_args += 2;
-			break;
-		case 'R':
-			relais = atoi(optarg);
-			if (relais > 511)
-				relais = 511;
-			if (relais < 1)
-				relais = 1;
-			skip_args += 2;
-			break;
-		case OPT_DEPORT:
-			deport = atoi(optarg);
-			if (deport > 7)
-				deport = 7;
-			if (deport < 0)
-				deport = 0;
-			skip_args += 2;
-			break;
-		case 'I':
-			if (!strcmp(optarg, "list")) {
-				int i;
+static int handle_options(int short_option, int argi, char **argv)
+{
+	int rc;
 
-				printf("\nList of possible AGI (inscription permission) codes:\n\n");
-				printf("Value\tDescription\n");
-				printf("------------------------------------------------------------------------\n");
-				for (i = 0; i < 8; i++)
-					printf("%d\t%s\n", i, param_agi(i));
-				exit(0);
-			}
-			agi = atoi(optarg);
-			if (agi < 0 || agi > 7) {
-				fprintf(stderr, "Error, given inscription permission (AGI) %d is invalid, use 'list' to get a list of values!\n", agi);
-				exit(0);
-			}
-			skip_args += 2;
-			break;
-		case 'P':
-			sm_power = atoi(optarg);
-			if (sm_power > 1)
-				sm_power = 1;
-			if (sm_power < 0)
-				sm_power = 0;
-			skip_args += 2;
-			break;
-		case OPT_TAXE:
-			taxe = atoi(optarg);
-			if (taxe > 1)
-				taxe = 1;
-			if (taxe < 0)
-				taxe = 0;
-			skip_args += 2;
-			break;
+	switch (short_option) {
+	case 'B':
+	case OPT_BANDE:
+		if (!strcmp(argv[argi], "list")) {
+			r2000_band_list();
+			return 0;
+		}
+		band = atoi(argv[argi]);
+		break;
+	case 'T':
+		if (!strcmp(argv[argi], "list")) {
+			r2000_channel_list();
+			return 0;
+		}
+		rc = r2000_channel_by_short_name(argv[argi]);
+		if (rc < 0) {
+			fprintf(stderr, "Error, channel type '%s' unknown. Please use '-t list' to get a list. I suggest to use the default.\n", argv[argi]);
+			return -EINVAL;
+		}
+		OPT_ARRAY(num_chan_type, chan_type, rc)
+		break;
+	case 'R':
+		relais = atoi(argv[argi]);
+		if (relais > 511)
+			relais = 511;
+		if (relais < 1)
+			relais = 1;
+		break;
+	case OPT_DEPORT:
+		deport = atoi(argv[argi]);
+		if (deport > 7)
+			deport = 7;
+		if (deport < 0)
+			deport = 0;
+		break;
+	case 'I':
+		if (!strcmp(argv[argi], "list")) {
+			int i;
+
+			printf("\nList of possible AGI (inscription permission) codes:\n\n");
+			printf("Value\tDescription\n");
+			printf("------------------------------------------------------------------------\n");
+			for (i = 0; i < 8; i++)
+				printf("%d\t%s\n", i, param_agi(i));
+			return 0;
+		}
+		agi = atoi(argv[argi]);
+		if (agi < 0 || agi > 7) {
+			fprintf(stderr, "Error, given inscription permission (AGI) %d is invalid, use 'list' to get a list of values!\n", agi);
+			return -EINVAL;
+		}
+		break;
+	case 'P':
+		sm_power = atoi(argv[argi]);
+		if (sm_power > 1)
+			sm_power = 1;
+		if (sm_power < 0)
+			sm_power = 0;
+		break;
+	case OPT_TAXE:
+		taxe = atoi(argv[argi]);
+		if (taxe > 1)
+			taxe = 1;
+		if (taxe < 0)
+			taxe = 0;
+		break;
 #if 0
-		case 'A':
-			if (!strcmp(optarg, "list")) {
-				int i;
-
-				printf("\nList of possible AGA (call permission) codes:\n\n");
-				printf("Value\tDescription\n");
-				printf("------------------------------------------------------------------------\n");
-				for (i = 0; i < 4; i++)
-					printf("%d\t%s\n", i, param_aga(i));
-				exit(0);
-			}
-			aga = atoi(optarg);
-			if (aga < 0 || aga > 3) {
-				fprintf(stderr, "Error, given call permission (AGA) %d is invalid, use 'list' to get a list of values!\n", aga);
-				exit(0);
-			}
-			skip_args += 2;
-			break;
+	case 'A':
+		if (!strcmp(argv[argi], "list")) {
+			int i;
+
+			printf("\nList of possible AGA (call permission) codes:\n\n");
+			printf("Value\tDescription\n");
+			printf("------------------------------------------------------------------------\n");
+			for (i = 0; i < 4; i++)
+				printf("%d\t%s\n", i, param_aga(i));
+			return 0;
+		}
+		aga = atoi(argv[argi]);
+		if (aga < 0 || aga > 3) {
+			fprintf(stderr, "Error, given call permission (AGA) %d is invalid, use 'list' to get a list of values!\n", aga);
+			return -EINVAL;
+		}
+		break;
 #endif
-		case 'C':
-			if (!strcmp(optarg, "list")) {
-				int i;
-
-				printf("\nList of possible CRINS (inscription response) codes:\n\n");
-				printf("Value\tDescription\n");
-				printf("------------------------------------------------------------------------\n");
-				for (i = 0; i < 8; i++)
-					printf("%d\t%s\n", i, param_crins(i));
-				exit(0);
-			}
-			crins = atoi(optarg);
-			if (crins < 0 || crins > 7) {
-				fprintf(stderr, "Error, given inscription response (CRINS) %d is invalid, use 'list' to get a list of values!\n", crins);
-				exit(0);
-			}
-			skip_args += 2;
-			break;
-		case OPT_DESTRUCTION:
-			if (!strcmp(optarg, "YES")) {
-				destruction = 2342;
-			}
-			skip_args += 2;
-			break;
-		case 'N':
-			nconv = atoi(optarg);
-			if (nconv > 7)
-				nconv = 7;
-			if (nconv < 0)
-				nconv = 0;
-			skip_args += 2;
-			break;
-		case 'S':
-			recall = 1;
-			skip_args += 1;
-			break;
-		default:
-			main_mobile_opt_switch(c, argv[0], &skip_args);
+	case 'C':
+		if (!strcmp(argv[argi], "list")) {
+			int i;
+
+			printf("\nList of possible CRINS (inscription response) codes:\n\n");
+			printf("Value\tDescription\n");
+			printf("------------------------------------------------------------------------\n");
+			for (i = 0; i < 8; i++)
+				printf("%d\t%s\n", i, param_crins(i));
+			return 0;
+		}
+		crins = atoi(argv[argi]);
+		if (crins < 0 || crins > 7) {
+			fprintf(stderr, "Error, given inscription response (CRINS) %d is invalid, use 'list' to get a list of values!\n", crins);
+			return -EINVAL;
+		}
+		break;
+	case OPT_DESTRUCTION:
+		if (!strcmp(argv[argi], "YES")) {
+			destruction = 2342;
 		}
+		break;
+	case 'N':
+		nconv = atoi(argv[argi]);
+		if (nconv > 7)
+			nconv = 7;
+		if (nconv < 0)
+			nconv = 0;
+		break;
+	case 'S':
+		recall = 1;
+		break;
+	default:
+		return main_mobile_handle_options(short_option, argi, argv);
 	}
 
-	free(long_options);
-
-	return skip_args;
+	return 1;
 }
 
 int main(int argc, char *argv[])
 {
-	int rc;
-	int skip_args;
+	int rc, argi;
 	const char *station_id = "";
 	int mandatory = 0;
 	int i;
@@ -284,12 +260,17 @@ int main(int argc, char *argv[])
 
 	main_mobile_init();
 
-	skip_args = handle_options(argc, argv);
-	argc -= skip_args;
-	argv += skip_args;
+	/* handle options / config file */
+	add_options();
+	rc = options_config_file("~/.osmocom/analog/radiocom2000.conf", handle_options);
+	if (rc < 0)
+		return 0;
+	argi = options_command_line(argc, argv, handle_options);
+	if (argi <= 0)
+		return argi;
 
-	if (argc > 1) {
-		station_id = argv[1];
+	if (argi < argc) {
+		station_id = argv[argi];
 		if (strlen(station_id) != 9) {
 			printf("Given station ID '%s' does not have 9 digits\n", station_id);
 			return 0;
@@ -328,7 +309,7 @@ int main(int argc, char *argv[])
 	}
 
 	if (mandatory) {
-		print_help(argv[-skip_args]);
+		print_help(argv[0]);
 		return 0;
 	}
 
diff --git a/src/radio/Makefile.am b/src/radio/Makefile.am
index 24c65da..a96e3bb 100644
--- a/src/radio/Makefile.am
+++ b/src/radio/Makefile.am
@@ -10,6 +10,7 @@ osmoradio_SOURCES = \
 	main.c
 osmoradio_LDADD = \
 	$(COMMON_LA) \
+	$(top_builddir)/src/liboptions/liboptions.a \
 	$(top_builddir)/src/libdebug/libdebug.a \
 	$(top_builddir)/src/libwave/libwave.a \
 	$(top_builddir)/src/libsample/libsample.a \
diff --git a/src/radio/main.c b/src/radio/main.c
index d576ffe..77c415b 100644
--- a/src/radio/main.c
+++ b/src/radio/main.c
@@ -1,4 +1,4 @@
-/* main function
+/* Radio main function
  *
  * (C) 2018 by Andreas Eversberg 
  * All Rights Reserved
@@ -24,8 +24,8 @@ enum paging_signal;
 #include 
 #include 
 #include 
-#include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -34,6 +34,7 @@ enum paging_signal;
 #include "../libsdr/sdr_config.h"
 #include "../libsdr/sdr.h"
 #include "../libdisplay/display.h"
+#include "../liboptions/options.h"
 #include "radio.h"
 
 #define DEFAULT_LO_OFFSET -1000000.0
@@ -137,170 +138,121 @@ void print_help(const char *arg0)
 	printf(" -S --stereo\n");
 	printf("        Enables stereo carrier for frequency modulated UHF broadcast.\n");
 	printf("        It uses the 'Pilot-tone' system.\n");
+	printf("    --limesdr\n");
+	printf("        Auto-select several required options for LimeSDR\n");
 	sdr_config_print_help();
 }
 
-static struct option long_options_common[] = {
-	{"help", 0, 0, 'h'},
-	{"frequency", 1, 0, 'f'},
-	{"samplerate", 1, 0, 's'},
-	{"tx-wave-file", 1, 0, 'r'},
-	{"rx-wave-file", 1, 0, 'w'},
-	{"audio-device", 1, 0, 'a'},
-	{"modulation", 1, 0, 'M'},
-	{"rx", 0, 0, 'R'},
-	{"tx", 0, 0, 'T'},
-	{"bandwidth", 1, 0, 'B'},
-	{"deviation", 1, 0, 'D'},
-	{"modulation-index", 1, 0, 'I'},
-	{"emphasis", 1, 0, 'E'},
-	{"stereo", 0, 0, 'S'},
-	{0, 0, 0, 0}
-};
-
-static const char *optstring_common = "hf:s:r:w:a:M:RTB:D:I:E:S";
-
-struct option *long_options;
-char *optstring;
-
-static void check_duplicate_option(int num, struct option *option)
-{
-	int i;
+#define OPT_LIMESDR		1100
 
-	for (i = 0; i < num; i++) {
-		if (long_options[i].val == option->val) {
-			fprintf(stderr, "Duplicate option %d. Please fix!\n", option->val);
-			abort();
-		}
-	}
-}
-
-void set_options_common(void)
+static void add_options(void)
 {
-	int i = 0, j;
-
-	long_options = calloc(sizeof(*long_options), 256);
-	for (j = 0; long_options_common[i].name; i++, j++) {
-		check_duplicate_option(i, &long_options_common[j]);
-		memcpy(&long_options[i], &long_options_common[j], sizeof(*long_options));
-	}
-	for (j = 0; sdr_config_long_options[j].name; i++, j++) {
-		check_duplicate_option(i, &sdr_config_long_options[j]);
-		memcpy(&long_options[i], &sdr_config_long_options[j], sizeof(*long_options));
-	}
-	
-	optstring = calloc(256, 2);
-	strcpy(optstring, optstring_common);
-	strcat(optstring, sdr_config_optstring);
+	option_add('h', "help", 0);
+	option_add('f', "frequency", 1);
+	option_add('s', "samplerate", 1);
+	option_add('r', "tx-wave-file", 1);
+	option_add('w', "rx-wave-file", 1);
+	option_add('a', "audio-device", 1);
+	option_add('M', "modulation", 1);
+	option_add('R', "rx", 0);
+	option_add('T', "tx", 0);
+	option_add('B', "bandwidth", 1);
+	option_add('D', "deviation", 1);
+	option_add('I', "modulation-index", 1);
+	option_add('E', "emphasis", 1);
+	option_add('S', "stereo", 0);
+	option_add(OPT_LIMESDR, "limesdr", 0);
+        sdr_config_add_options();
 }
 
-static int handle_options(int argc, char **argv)
+static int handle_options(int short_option, int argi, char **argv)
 {
-	int skip_args = 0;
-	int rc;
-
-	set_options_common();
-
-	while (1) {
-		int option_index = 0, c;
-
-		c = getopt_long(argc, argv, optstring, long_options, &option_index);
-
-		if (c == -1)
-			break;
-
-		switch (c) {
-		case 'h':
-			print_help(argv[0]);
-			exit(0);
-		case 'f':
-			frequency = atof(optarg);
-			skip_args += 2;
-			break;
-		case 's':
-			samplerate = atof(optarg);
-			skip_args += 2;
-			break;
-		case 'r':
-			tx_wave_file = strdup(optarg);
-			skip_args += 2;
-			break;
-		case 'w':
-			rx_wave_file = strdup(optarg);
-			skip_args += 2;
-			break;
-		case 'a':
-			tx_audiodev = strdup(optarg);
-			rx_audiodev = strdup(optarg);
-			skip_args += 2;
-			break;
-		case 'M':
-			if (!strcasecmp(optarg, "fm"))
-				modulation = MODULATION_FM;
-			else
-			if (!strcasecmp(optarg, "am"))
-				modulation = MODULATION_AM_DSB;
-			else
-			if (!strcasecmp(optarg, "usb"))
-				modulation = MODULATION_AM_USB;
-			else
-			if (!strcasecmp(optarg, "lsb"))
-				modulation = MODULATION_AM_LSB;
-			else
-			{
-				fprintf(stderr, "Invalid modulation option, see help!\n");
-				exit(0);
-			}
-			skip_args += 2;
-			break;
-		case 'R':
-			rx = 1;
-			skip_args += 1;
-			break;
-		case 'T':
-			tx = 1;
-			skip_args += 1;
-			break;
-		case 'B':
-			bandwidth = atof(optarg);
-			skip_args += 2;
-			break;
-		case 'D':
-			deviation = atof(optarg);
-			skip_args += 2;
-			break;
-		case 'I':
-			modulation_index = atof(optarg);
-			if (modulation_index < 0.0 || modulation_index > 1.0) {
-				fprintf(stderr, "Invalid modulation index, see help!\n");
-				exit(0);
-			}
-			skip_args += 2;
-			break;
-		case 'E':
-			time_constant_us = atof(optarg);
-			skip_args += 2;
-			break;
-		case 'S':
-			stereo = 1;
-			skip_args += 1;
-			break;
-		default:
-			rc = sdr_config_opt_switch(c, &skip_args);
-			if (rc < 0)
-				exit(0);
-			break;
+	switch (short_option) {
+	case 'h':
+		print_help(argv[0]);
+		return 0;
+	case 'f':
+		frequency = atof(argv[argi]);
+		break;
+	case 's':
+		samplerate = atof(argv[argi]);
+		break;
+	case 'r':
+		tx_wave_file = strdup(argv[argi]);
+		break;
+	case 'w':
+		rx_wave_file = strdup(argv[argi]);
+		break;
+	case 'a':
+		tx_audiodev = strdup(argv[argi]);
+		rx_audiodev = strdup(argv[argi]);
+		break;
+	case 'M':
+		if (!strcasecmp(argv[argi], "fm"))
+			modulation = MODULATION_FM;
+		else
+		if (!strcasecmp(argv[argi], "am"))
+			modulation = MODULATION_AM_DSB;
+		else
+		if (!strcasecmp(argv[argi], "usb"))
+			modulation = MODULATION_AM_USB;
+		else
+		if (!strcasecmp(argv[argi], "lsb"))
+			modulation = MODULATION_AM_LSB;
+		else
+		{
+			fprintf(stderr, "Invalid modulation option, use '-h' for help!\n");
+			return -EINVAL;
 		}
+		break;
+	case 'R':
+		rx = 1;
+		break;
+	case 'T':
+		tx = 1;
+		break;
+	case 'B':
+		bandwidth = atof(argv[argi]);
+		break;
+	case 'D':
+		deviation = atof(argv[argi]);
+		break;
+	case 'I':
+		modulation_index = atof(argv[argi]);
+		if (modulation_index < 0.0 || modulation_index > 1.0) {
+			fprintf(stderr, "Invalid modulation index, use '-h' for help!\n");
+			return -EINVAL;
+		}
+		break;
+	case 'E':
+		time_constant_us = atof(argv[argi]);
+		break;
+	case 'S':
+		stereo = 1;
+		break;
+	case OPT_LIMESDR:
+		{
+			char *argv_lime[] = { argv[0],
+				"--sdr-soapy",
+				"--sdr-rx-antenna", "LNAL",
+				"--sdr-rx-gain", "50",
+				"--sdr-tx-gain", "50",
+				"--sdr-samplerate", "5000000",
+				"--sdr-bandwidth", "15000000",
+			};
+			int argc_lime = sizeof(argv_lime) / sizeof (*argv_lime);
+			return options_command_line(argc_lime, argv_lime, handle_options);
+		}
+	default:
+		return sdr_config_handle_options(short_option, argi, argv);
 	}
 
-	return skip_args;
+	return 1;
 }
 
 int main(int argc, char *argv[])
 {
-	int skip_args;
-	int rc;
-	const char *arg0 = argv[0];
+	int rc, argi;
 	radio_t radio;
 	struct termios term, term_orig;
 	int c;
@@ -310,13 +262,18 @@ int main(int argc, char *argv[])
 
 	sdr_config_init(DEFAULT_LO_OFFSET);
 
-	skip_args = handle_options(argc, argv);
-	argc -= skip_args + 1;
-	argv += skip_args + 1;
+	/* handle options / config file */
+	add_options();
+	rc = options_config_file("~/.osmocom/analog/radio.conf", handle_options);
+	if (rc < 0)
+		return 0;
+	argi = options_command_line(argc, argv, handle_options);
+	if (argi <= 0)
+		return argi;
 
 	if (frequency == 0.0) {
 		printf("No frequency given, I suggest to use 100000000 (100 MHz) and FM\n\n");
-		print_help(arg0);
+		print_help(argv[0]);
 		exit(0);
 	}
 
@@ -324,12 +281,12 @@ int main(int argc, char *argv[])
 	if (rc < 0)
 		return rc;
 	if (rc == 0) {
-		fprintf(stderr, "Please select SDR, see help!\n");
+		fprintf(stderr, "Please select SDR, use '-h' for help!\n");
 		exit(0);
 	}
 
 	if (modulation == MODULATION_NONE) {
-		fprintf(stderr, "Please select modulation, see help!\n");
+		fprintf(stderr, "Please select modulation, use '-h' for help!\n");
 		exit(0);
 	}
 
@@ -341,18 +298,18 @@ int main(int argc, char *argv[])
 	}
 
 	if (stereo && modulation != MODULATION_FM) {
-		fprintf(stderr, "Stereo works with FM only, see help!\n");
+		fprintf(stderr, "Stereo works with FM only, use '-h' for help!\n");
 		exit(0);
 	}
 	if (!rx && !tx) {
-		fprintf(stderr, "You need to specify --rx (receiver) and/or --tx (transmitter), see help!\n");
+		fprintf(stderr, "You need to specify --rx (receiver) and/or --tx (transmitter), use '-h' for help!\n");
 		exit(0);
 	}
 	if (stereo && bandwidth != 15000.0) {
 		fprintf(stderr, "Warning: Stereo works with bandwidth of 15 KHz only, using this bandwidth!\n");
 	}
 	if (stereo && time_constant_us != 75.0 && time_constant_us != 50.0) {
-		fprintf(stderr, "Stereo works with time constant of 50 uS or 75 uS only, see help!\n");
+		fprintf(stderr, "Stereo works with time constant of 50 uS or 75 uS only, use '-h' for help!\n");
 		exit(0);
 	}
 
diff --git a/src/tacs/Makefile.am b/src/tacs/Makefile.am
index ec9b593..5a0a894 100644
--- a/src/tacs/Makefile.am
+++ b/src/tacs/Makefile.am
@@ -13,6 +13,7 @@ tacs_SOURCES = \
 tacs_LDADD = \
 	$(COMMON_LA) \
 	../amps/libamps.a \
+	$(top_builddir)/src/liboptions/liboptions.a \
 	$(top_builddir)/src/libdebug/libdebug.a \
 	$(top_builddir)/src/libmobile/libmobile.a \
 	$(top_builddir)/src/libdisplay/libdisplay.a \
diff --git a/src/test/Makefile.am b/src/test/Makefile.am
index 5f7a86e..141d88d 100644
--- a/src/test/Makefile.am
+++ b/src/test/Makefile.am
@@ -57,6 +57,7 @@ test_dms_LDADD = \
 	$(COMMON_LA) \
 	$(top_builddir)/src/libdebug/libdebug.a \
 	$(top_builddir)/src/libmobile/libmobile.a \
+	$(top_builddir)/src/liboptions/liboptions.a \
 	$(top_builddir)/src/libdisplay/libdisplay.a \
 	$(top_builddir)/src/nmt/libdmssms.a \
 	$(top_builddir)/src/libjitter/libjitter.a \
@@ -90,6 +91,7 @@ test_sms_LDADD = \
 	$(COMMON_LA) \
 	$(top_builddir)/src/libdebug/libdebug.a \
 	$(top_builddir)/src/libmobile/libmobile.a \
+	$(top_builddir)/src/liboptions/liboptions.a \
 	$(top_builddir)/src/libdisplay/libdisplay.a \
 	$(top_builddir)/src/nmt/libdmssms.a \
 	$(top_builddir)/src/libjitter/libjitter.a \
diff --git a/src/test/test_dms.c b/src/test/test_dms.c
index 1d3492f..5a29543 100644
--- a/src/test/test_dms.c
+++ b/src/test/test_dms.c
@@ -43,7 +43,7 @@ void dms_receive(nmt_t *nmt, const uint8_t *data, int length, int eight_bits)
 {
 	printf("(getting %d digits from DMS layer)\n", length);
 
-	assert(!memcmp((const char *)data, check_sequence, length), "Expecting received data to macht");
+	assert(!memcmp((const char *)data, check_sequence, length), "Expecting received data to match");
 
 	check_sequence += length;
 	check_length = length;
diff --git a/src/tv/Makefile.am b/src/tv/Makefile.am
index 733541a..d1fdb37 100644
--- a/src/tv/Makefile.am
+++ b/src/tv/Makefile.am
@@ -14,6 +14,7 @@ osmotv_SOURCES = \
 	main.c
 osmotv_LDADD = \
 	$(COMMON_LA) \
+	$(top_builddir)/src/liboptions/liboptions.a \
 	$(top_builddir)/src/libdebug/libdebug.a \
 	$(top_builddir)/src/libimage/libimage.a \
 	$(top_builddir)/src/libfm/libfm.a \
diff --git a/src/tv/main.c b/src/tv/main.c
index 01b27f0..7331c1d 100644
--- a/src/tv/main.c
+++ b/src/tv/main.c
@@ -1,4 +1,4 @@
-/* main function
+/* JollyTV main function
  *
  * (C) 2017 by Andreas Eversberg 
  * All Rights Reserved
@@ -24,8 +24,8 @@ enum paging_signal;
 #include 
 #include 
 #include 
-#include 
 #include 
+#include 
 #include 
 #include "../libsample/sample.h"
 #include "../libfilter/iir_filter.h"
@@ -37,6 +37,7 @@ enum paging_signal;
 #include "../libsdr/sdr_config.h"
 #include "../libsdr/sdr.h"
 #endif
+#include "../liboptions/options.h"
 #include "bas.h"
 #include "tv_modulate.h"
 #include "channels.h"
@@ -114,149 +115,104 @@ void print_help(const char *arg0)
 	printf("        Give exactly 12 characters to display as Station ID.\n");
 	printf("        (default = \"%s\")\n", station_id);
 #ifdef HAVE_SDR
+	printf("    --limesdr\n");
+	printf("        Auto-select several required options for LimeSDR\n");
 	sdr_config_print_help();
 #endif
 }
 
-static struct option long_options_common[] = {
-	{"help", 0, 0, 'h'},
-	{"frequency", 1, 0, 'f'},
-	{"channel", 1, 0, 'c'},
-	{"samplerate", 1, 0, 'r'},
-	{"wave-file", 1, 0, 'w'},
-	{"fbas", 1, 0, 'F'},
-	{"tone", 1, 0, 'T'},
-	{"circle-radius", 1, 0, 'R'},
-	{"color-bar", 1, 0, 'C'},
-	{"grid-only", 1, 0, 'G'},
-	{"station-id", 1, 0, 'I'},
-	{0, 0, 0, 0}
-};
-
-static const char *optstring_common = "hf:c:r:w:F:T:R:C:G:I:";
-
-struct option *long_options;
-char *optstring;
-
-static void check_duplicate_option(int num, struct option *option)
-{
-	int i;
+#define OPT_LIMESDR		1100
 
-	for (i = 0; i < num; i++) {
-		if (long_options[i].val == option->val) {
-			fprintf(stderr, "Duplicate option %d. Please fix!\n", option->val);
-			abort();
-		}
-	}
-}
-
-void set_options_common(void)
+static void add_options(void)
 {
-	int i = 0, j;
-
-	long_options = calloc(sizeof(*long_options), 256);
-	for (j = 0; long_options_common[i].name; i++, j++) {
-		check_duplicate_option(i, &long_options_common[j]);
-		memcpy(&long_options[i], &long_options_common[j], sizeof(*long_options));
-	}
-#ifdef HAVE_SDR
-	for (j = 0; sdr_config_long_options[j].name; i++, j++) {
-		check_duplicate_option(i, &sdr_config_long_options[j]);
-		memcpy(&long_options[i], &sdr_config_long_options[j], sizeof(*long_options));
-	}
-#endif
-	
-	optstring = calloc(256, 2);
-	strcpy(optstring, optstring_common);
+	option_add('h', "help", 0);
+	option_add('f', "frequency", 1);
+	option_add('c', "channel", 1);
+	option_add('r', "samplerate", 1);
+	option_add('w', "wave-file", 1);
+	option_add('F', "fbas", 1);
+	option_add('T', "tone", 1);
+	option_add('R', "circle-radius", 1);
+	option_add('C', "color-bar", 1);
+	option_add('G', "grid-only", 1);
+	option_add('I', "station-id", 1);
 #ifdef HAVE_SDR
-	strcat(optstring, sdr_config_optstring);
+	option_add(OPT_LIMESDR, "limesdr", 0);
+	sdr_config_add_options();
 #endif
 }
 
-static int handle_options(int argc, char **argv)
+static int handle_options(int short_option, int argi, char **argv)
 {
-	int skip_args = 0;
+	switch (short_option) {
+	case 'h':
+		print_help(argv[0]);
+		return 0;
+	case 'f':
+		frequency = atof(argv[argi]);
+		break;
+	case 'c':
+		if (!strcmp(argv[argi], "list")) {
+			list_tv_channels();
+			return 0;
+		}
+		frequency = get_tv_video_frequency(atoi(argv[argi]));
+		if (frequency == 0.0) {
+			fprintf(stderr, "Given channel number unknown, use \"-c list\" to get a list.\n");
+			return -EINVAL;
+		}
+		break;
+	case 'r':
+		samplerate = atof(argv[argi]);
+		break;
+	case 'w':
+		wave_file = strdup(argv[argi]);
+		break;
+	case 'F':
+		fbas = atoi(argv[argi]);
+		break;
+	case 'T':
+		tone = atoi(argv[argi]);
+		break;
+	case 'R':
+		circle_radius = atof(argv[argi]);
+		break;
+	case 'C':
+		color_bar = atoi(argv[argi]);
+		break;
+	case 'G':
+		grid_only = atoi(argv[argi]);
+		break;
+	case 'I':
+		station_id = strdup(argv[argi]);
+		if (strlen(station_id) != 12) {
+			fprintf(stderr, "Given station ID must be exactly 12 charaters long. (Use spaces to fill it.)\n");
+			return -EINVAL;
+		}
+		break;
 #ifdef HAVE_SDR
-	int rc;
+	case OPT_LIMESDR:
+		{
+			char *argv_lime[] = { argv[0],
+				"--sdr-soapy",
+				"--sdr-tx-gain", "50",
+				"--sdr-lo-offset", "-3000000",
+				"--sdr-bandwidth", "60000000",
+				"-r", "13750000",
+			};
+			int argc_lime = sizeof(argv_lime) / sizeof (*argv_lime);
+			return options_command_line(argc_lime, argv_lime, handle_options);
+		}
 #endif
-
-	set_options_common();
-
-	while (1) {
-		int option_index = 0, c;
-
-		c = getopt_long(argc, argv, optstring, long_options, &option_index);
-
-		if (c == -1)
-			break;
-
-		switch (c) {
-		case 'h':
-			print_help(argv[0]);
-			exit(0);
-		case 'f':
-			frequency = atof(optarg);
-			skip_args += 2;
-			break;
-		case 'c':
-			if (!strcmp(optarg, "list")) {
-				list_tv_channels();
-				exit(0);
-			}
-			frequency = get_tv_video_frequency(atoi(optarg));
-			if (frequency == 0.0) {
-				fprintf(stderr, "Given channel number unknown, use \"-c list\" to get a list.\n");
-				exit(0);
-			}
-			skip_args += 2;
-			break;
-		case 'r':
-			samplerate = atof(optarg);
-			skip_args += 2;
-			break;
-		case 'w':
-			wave_file = strdup(optarg);
-			skip_args += 2;
-			break;
-		case 'F':
-			fbas = atoi(optarg);
-			skip_args += 2;
-			break;
-		case 'T':
-			tone = atoi(optarg);
-			skip_args += 2;
-			break;
-		case 'R':
-			circle_radius = atof(optarg);
-			skip_args += 2;
-			break;
-		case 'C':
-			color_bar = atoi(optarg);
-			skip_args += 2;
-			break;
-		case 'G':
-			grid_only = atoi(optarg);
-			skip_args += 2;
-			break;
-		case 'I':
-			station_id = strdup(optarg);
-			if (strlen(station_id) != 12) {
-				fprintf(stderr, "Given station ID must be exactly 12 charaters long. (Use spaces to fill it.)\n");
-				exit(0);
-			}
-			skip_args += 2;
-			break;
-		default:
+	default:
 #ifdef HAVE_SDR
-			rc = sdr_config_opt_switch(c, &skip_args);
-			if (rc < 0)
-				exit(0);
+		return sdr_config_handle_options(short_option, argi, argv);
+#else
+		return -EINVAL;
 #endif
-			break;
-		}
 	}
 
-	return skip_args;
+	return 1;
 }
 
 static void tx_bas(sample_t *sample_bas, __attribute__((__unused__)) sample_t *sample_tone, __attribute__((__unused__)) uint8_t *power_tone, int samples)
@@ -448,9 +404,7 @@ error:
 
 int main(int argc, char *argv[])
 {
-	int skip_args;
-	int __attribute__((__unused__)) rc;
-	const char *arg0 = argv[0];
+	int __attribute__((__unused__)) rc, argi;
 
 	debuglevel = 0;
 
@@ -458,12 +412,17 @@ int main(int argc, char *argv[])
 	sdr_config_init(DEFAULT_LO_OFFSET);
 #endif
 
-	skip_args = handle_options(argc, argv);
-	argc -= skip_args + 1;
-	argv += skip_args + 1;
+	/* handle options / config file */
+	add_options();
+	rc = options_config_file("~/.osmocom/analog/osmotv.conf", handle_options);
+	if (rc < 0)
+		return 0;
+	argi = options_command_line(argc, argv, handle_options);
+	if (argi <= 0)
+		return argi;
 
 	if (frequency == 0.0 && !wave_file) {
-		print_help(arg0);
+		print_help(argv[0]);
 		exit(0);
 	}
 
@@ -475,22 +434,22 @@ int main(int argc, char *argv[])
 #endif
 	}
 
-	if (argc < 1) {
-		fprintf(stderr, "Expecting command, see help!\n");
+	if (argi >= argc) {
+		fprintf(stderr, "Expecting command, use '-h' for help!\n");
 		exit(0);
-	} else if (!strcmp(argv[0], "tx-fubk")) {
+	} else if (!strcmp(argv[argi], "tx-fubk")) {
 		tx_test_picture(BAS_FUBK);
-	} else if (!strcmp(argv[0], "tx-vcr")) {
+	} else if (!strcmp(argv[argi], "tx-vcr")) {
 		tx_test_picture(BAS_VCR);
-	} else if (!strcmp(argv[0], "tx-img")) {
-		if (argc < 2) {
-			fprintf(stderr, "Expecting image file, see help!\n");
-			exit(0);
+	} else if (!strcmp(argv[argi], "tx-img")) {
+		if (argi + 1 >= argc) {
+			fprintf(stderr, "Expecting image file, use '-h' for help!\n");
+			return -EINVAL;
 		}
-		tx_img(argv[1]);
+		tx_img(argv[argi + 1]);
 	} else {
-		fprintf(stderr, "Unknown command '%s', see help!\n", argv[0]);
-		exit(0);
+		fprintf(stderr, "Unknown command '%s', use '-h' for help!\n", argv[argi]);
+		return -EINVAL;
 	}
 
 	return 0;
-- 
cgit v1.2.3