tools/llvm: Do not build with symbols
[minix3.git] / external / bsd / kyua-testers / dist / cli.c
blob220d5956063595db939cb03ed53efd68efb044e3
1 // Copyright 2012 Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #if defined(HAVE_CONFIG_H)
30 #include "config.h"
31 #endif
33 #include "cli.h"
35 #include <assert.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <limits.h>
39 #include <stdbool.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
44 #include "defs.h"
45 #include "error.h"
46 #include "run.h"
48 #if !defined(GID_MAX)
49 # define GID_MAX INT_MAX
50 #endif
51 #if !defined(UID_MAX)
52 # define UID_MAX INT_MAX
53 #endif
55 #if defined(HAVE_GETOPT_GNU)
56 # define GETOPT_PLUS "+"
57 #else
58 # define GETOPT_PLUS
59 #endif
62 /// Terminates execution if the given error is set.
63 ///
64 /// \param error The error to validate.
65 ///
66 /// \post The program terminates if the given error is set.
67 static void
68 check_error(kyua_error_t error)
70 if (kyua_error_is_set(error)) {
71 const bool usage_error = kyua_error_is_type(error,
72 kyua_usage_error_type);
74 char buffer[1024];
75 kyua_error_format(error, buffer, sizeof(buffer));
76 kyua_error_free(error);
78 errx(usage_error ? EXIT_USAGE_ERROR : EXIT_INTERNAL_ERROR,
79 "%s", buffer);
84 /// Converts a string to an unsigned long.
85 ///
86 /// \param str The string to convert.
87 /// \param message Part of the error message to print if the string does not
88 /// represent a valid unsigned long number.
89 /// \param max Maximum accepted value.
90 ///
91 /// \return The converted numerical value.
92 ///
93 /// \post The program terminates if the value is invalid.
94 static unsigned long
95 parse_ulong(const char* str, const char* message, const unsigned long max)
97 char *endptr;
99 errno = 0;
100 const unsigned long value = strtoul(str, &endptr, 10);
101 if (str[0] == '\0' || *endptr != '\0')
102 errx(EXIT_USAGE_ERROR, "%s '%s' (not a number)", message, str);
103 else if (errno == ERANGE || value == LONG_MAX || value > max)
104 errx(EXIT_USAGE_ERROR, "%s '%s' (out of range)", message, str);
105 return value;
109 /// Clears getopt(3) state to allow calling the function again.
110 static void
111 reset_getopt(void)
113 opterr = 0;
114 optind = GETOPT_OPTIND_RESET_VALUE;
115 #if defined(HAVE_GETOPT_WITH_OPTRESET)
116 optreset = 1;
117 #endif
121 /// Prints the list of test cases and their metadata in a test program.
123 /// \param argc Number of arguments to the command, including the command name.
124 /// \param argv Arguments to the command, including the command name.
125 /// \param tester Description of the tester implemented by this binary.
126 /// \param run_params Execution parameters to configure the test process.
128 /// \return An exit status to indicate the success or failure of the listing.
130 /// \post Usage errors terminate the execution of the program right away.
131 static int
132 list_command(const int argc, char* const* const argv,
133 const kyua_cli_tester_t* tester,
134 const kyua_run_params_t* run_params)
136 if (argc < 2)
137 errx(EXIT_USAGE_ERROR, "No test program provided");
138 else if (argc > 2)
139 errx(EXIT_USAGE_ERROR, "Only one test program allowed");
140 const char* test_program = argv[1];
142 check_error(tester->list_test_cases(test_program, run_params));
143 return EXIT_SUCCESS;
147 /// Runs and cleans up a single test case.
149 /// \param argc Number of arguments to the command, including the command name.
150 /// \param argv Arguments to the command, including the command name.
151 /// \param tester Description of the tester implemented by this binary.
152 /// \param run_params Execution parameters to configure the test process.
154 /// \return An exit status to indicate the success or failure of the test case
155 /// execution.
157 /// \post Usage errors terminate the execution of the program right away.
158 static int
159 test_command(int argc, char* const* argv, const kyua_cli_tester_t* tester,
160 const kyua_run_params_t* run_params)
162 # define MAX_USER_VARIABLES 256
163 const char* user_variables[MAX_USER_VARIABLES];
165 const char** last_variable = user_variables;
166 int ch;
167 while ((ch = getopt(argc, argv, GETOPT_PLUS":v:")) != -1) {
168 switch (ch) {
169 case 'v':
170 *last_variable++ = optarg;
171 break;
173 case ':':
174 errx(EXIT_USAGE_ERROR, "%s's -%c requires an argument", argv[0],
175 optopt);
177 case '?':
178 errx(EXIT_USAGE_ERROR, "Unknown %s option -%c", argv[0], optopt);
180 default:
181 assert(false);
184 argc -= optind;
185 argv += optind;
186 *last_variable = NULL;
188 if (argc != 3)
189 errx(EXIT_USAGE_ERROR, "Must provide a test program, a test case name "
190 "and a result file");
191 const char* test_program = argv[0];
192 const char* test_case = argv[1];
193 const char* result_file = argv[2];
195 bool success;
196 check_error(tester->run_test_case(test_program, test_case, result_file,
197 user_variables, run_params, &success));
198 return success ? EXIT_SUCCESS : EXIT_FAILURE;
202 /// Generic entry point to the tester's command-line interface.
204 /// \param argc Verbatim argc passed to main().
205 /// \param argv Verbatim argv passed to main().
206 /// \param tester Description of the tester implemented by this binary.
208 /// \return An exit status.
210 /// \post Usage errors terminate the execution of the program right away.
212 kyua_cli_main(int argc, char* const* argv, const kyua_cli_tester_t* tester)
214 kyua_run_params_t run_params;
215 kyua_run_params_init(&run_params);
217 int ch;
218 while ((ch = getopt(argc, argv, GETOPT_PLUS":g:t:u:")) != -1) {
219 switch (ch) {
220 case 'g':
221 run_params.unprivileged_group = (uid_t)parse_ulong(
222 optarg, "Invalid GID", GID_MAX);
223 break;
225 case 't':
226 run_params.timeout_seconds = parse_ulong(
227 optarg, "Invalid timeout value", LONG_MAX);
228 break;
230 case 'u':
231 run_params.unprivileged_user = (uid_t)parse_ulong(
232 optarg, "Invalid UID", UID_MAX);
233 break;
235 case ':':
236 errx(EXIT_USAGE_ERROR, "-%c requires an argument", optopt);
238 case '?':
239 errx(EXIT_USAGE_ERROR, "Unknown option -%c", optopt);
241 default:
242 assert(false);
245 argc -= optind;
246 argv += optind;
247 reset_getopt();
249 if (argc == 0)
250 errx(EXIT_USAGE_ERROR, "Must provide a command");
251 const char* command = argv[0];
253 // Keep sorted by order of likelyhood (yeah, micro-optimization).
254 if (strcmp(command, "test") == 0)
255 return test_command(argc, argv, tester, &run_params);
256 else if (strcmp(command, "list") == 0)
257 return list_command(argc, argv, tester, &run_params);
258 else
259 errx(EXIT_USAGE_ERROR, "Unknown command '%s'", command);