1 /*
2  * Copyright (c) 2018 Oticon A/S
3  * Copyright (c) 2023 Nordic Semiconductor ASA
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <stdbool.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include "nsi_cmdline.h"
12 #include "nsi_cmdline_internal.h"
13 #include "nsi_tracing.h"
14 #include "nsi_timer_model.h"
15 #include "nsi_hw_scheduler.h"
16 #include "nsi_tasks.h"
17 
18 static int s_argc, test_argc;
19 static char **s_argv, **test_argv;
20 
21 /* Extra "command line options" provided programmatically: */
22 static int extra_argc;
23 static char **extra_argv;
24 
25 static struct args_struct_t *args_struct;
26 static int used_args;
27 static int args_aval;
28 #define ARGS_ALLOC_CHUNK_SIZE 20
29 
nsi_cleanup_cmd_line(void)30 static void nsi_cleanup_cmd_line(void)
31 {
32 	if (args_struct != NULL) { /* LCOV_EXCL_BR_LINE */
33 		free(args_struct);
34 		args_struct = NULL;
35 	}
36 }
37 
38 NSI_TASK(nsi_cleanup_cmd_line, ON_EXIT_POST, 0);
39 
40 /**
41  * Add a set of command line options to the program.
42  *
43  * Each option to be added is described in one entry of the input <args>
44  * This input must be terminated with an entry containing ARG_TABLE_ENDMARKER.
45  */
nsi_add_command_line_opts(struct args_struct_t * args)46 void nsi_add_command_line_opts(struct args_struct_t *args)
47 {
48 	int count = 0;
49 
50 	while (args[count].option != NULL) {
51 		count++;
52 	}
53 	count++; /*for the end marker*/
54 
55 	if (used_args + count >= args_aval) {
56 		int growby = count;
57 		/* reallocs are expensive let's do them only in big chunks */
58 		if (growby < ARGS_ALLOC_CHUNK_SIZE) {
59 			growby = ARGS_ALLOC_CHUNK_SIZE;
60 		}
61 
62 		struct args_struct_t *new_args_struct = realloc(args_struct,
63 				      (args_aval + growby)*
64 				      sizeof(struct args_struct_t));
65 		args_aval += growby;
66 		/* LCOV_EXCL_START */
67 		if (new_args_struct == NULL) {
68 			nsi_print_error_and_exit("Could not allocate memory");
69 		} else {
70 			args_struct = new_args_struct;
71 		}
72 		/* LCOV_EXCL_STOP */
73 	}
74 
75 	memcpy(&args_struct[used_args], args,
76 		count*sizeof(struct args_struct_t));
77 
78 	used_args += count - 1;
79 	/*
80 	 * -1 as the end marker should be overwritten next time something
81 	 * is added
82 	 */
83 }
84 
nsi_add_testargs_option(void)85 void nsi_add_testargs_option(void)
86 {
87 	static struct args_struct_t testargs_options[] = {
88 		{
89 		.manual = true,
90 		.option = "testargs",
91 		.name = "arg",
92 		.type = 'l',
93 		.descript = "Any argument that follows will be ignored by the top level, "
94 			    "and made available for possible tests"
95 		},
96 		ARG_TABLE_ENDMARKER
97 	};
98 
99 	nsi_add_command_line_opts(testargs_options);
100 }
101 
print_invalid_opt_error(char * argv)102 static void print_invalid_opt_error(char *argv)
103 {
104 	nsi_print_error_and_exit("Incorrect option '%s'. Did you misspell it?"
105 				 " Is that feature supported in this build?\n",
106 				 argv);
107 
108 }
109 
110 /**
111  * Handle possible command line arguments.
112  *
113  * We also store them for later use by possible test applications
114  */
nsi_handle_cmd_line(int argc,char * argv[])115 void nsi_handle_cmd_line(int argc, char *argv[])
116 {
117 	int i;
118 
119 	nsi_add_testargs_option();
120 
121 	s_argv = argv;
122 	s_argc = argc;
123 
124 	nsi_cmd_args_set_defaults(args_struct);
125 
126 	for (int i = 0; i < extra_argc; i++) {
127 		if (!nsi_cmd_parse_one_arg(extra_argv[i], args_struct)) {
128 			nsi_cmd_print_switches_help(args_struct);
129 			print_invalid_opt_error(extra_argv[i]);
130 		}
131 	}
132 
133 	for (i = 1; i < argc; i++) {
134 
135 		if ((nsi_cmd_is_option(argv[i], "testargs", 0))) {
136 			test_argc = argc - i - 1;
137 			test_argv = &argv[i+1];
138 			break;
139 		}
140 
141 		if (!nsi_cmd_parse_one_arg(argv[i], args_struct)) {
142 			nsi_cmd_print_switches_help(args_struct);
143 			print_invalid_opt_error(argv[i]);
144 		}
145 	}
146 }
147 
nsi_register_extra_args(int argc,char * argv[])148 void nsi_register_extra_args(int argc, char *argv[])
149 {
150 	int new_size = extra_argc + argc;
151 
152 	extra_argv = realloc(extra_argv, new_size*sizeof(char *));
153 	for (int i = 0; i < argc; i++) {
154 		memcpy(&extra_argv[extra_argc], argv, argc*sizeof(char *));
155 	}
156 	extra_argc += argc;
157 }
158 
clear_extra_args(void)159 static void clear_extra_args(void)
160 {
161 	free(extra_argv);
162 }
163 
164 NSI_TASK(clear_extra_args, ON_EXIT_PRE, 100);
165 
166 /**
167  * The application/test can use this function to inspect all the command line
168  * arguments
169  */
nsi_get_cmd_line_args(int * argc,char *** argv)170 void nsi_get_cmd_line_args(int *argc, char ***argv)
171 {
172 	*argc = s_argc;
173 	*argv = s_argv;
174 }
175 
176 /**
177  * The application/test can use this function to inspect the command line
178  * arguments received after --testargs
179  */
nsi_get_test_cmd_line_args(int * argc,char *** argv)180 void nsi_get_test_cmd_line_args(int *argc, char ***argv)
181 {
182 	*argc = test_argc;
183 	*argv = test_argv;
184 }
185