Cthulhu  0.2.10
Cthulhu compiler collection
setup.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: LGPL-3.0-only
2 #include "setup/setup.h"
3 
4 #include "arena/arena.h"
5 #include "argparse/argparse.h"
6 #include "backtrace/backtrace.h"
7 #include "base/log.h"
8 #include "base/panic.h"
9 #include "base/util.h"
10 #include "config/config.h"
11 #include "core/macros.h"
12 #include "format/backtrace.h"
13 #include "format/colour.h"
14 #include "format/config.h"
15 #include "format/version.h"
16 #include "io/console.h"
17 #include "io/io.h"
18 #include "memory/memory.h"
19 #include "notify/notify.h"
20 #include "os/core.h"
21 #include "setup/memory.h"
22 #include "std/str.h"
23 #include "std/vector.h"
24 
25 #include <limits.h>
26 
28 
29 #define NAME_BUFFER_SIZE 256
30 #define PATH_BUFFER_SIZE 512
31 
33 #define INT_BIT (sizeof(int) * CHAR_BIT)
34 #define SHOULD_EXIT (1 << (INT_BIT - 1))
35 
36 static const cfg_info_t kGeneralGroupInfo = {
37  .name = "general",
38  .brief = "General options",
39 };
40 
41 static const cfg_arg_t kHelpArgs[] = { CT_ARG_SHORT("h"), CT_ARG_DOS("?"), CT_ARG_LONG("help") };
42 
43 static const cfg_info_t kHelpInfo = {
44  .name = "help",
45  .brief = "Print this help message and exit",
46 
47  .args = CT_ARGS(kHelpArgs),
48 };
49 
50 static const cfg_arg_t kVersionArgs[] = { CT_ARG_SHORT("V"), CT_ARG_LONG("version") };
51 
52 static const cfg_info_t kVersionInfo = {
53  .name = "version",
54  .brief = "Print version information and exit",
55 
56  .args = CT_ARGS(kVersionArgs),
57 };
58 
59 static const cfg_info_t kReportGroupInfo = {
60  .name = "report",
61  .brief = "Diangostic reporting options",
62 };
63 
64 static const cfg_choice_t kHeadingOptions[] = {
65  { .text = "generic", .value = eHeadingGeneric },
66  { .text = "microsoft", .value = eHeadingMicrosoft },
67 };
68 #define HEADING_OPTION_COUNT (sizeof(kHeadingOptions) / sizeof(cfg_choice_t))
69 
70 static const cfg_arg_t kHeadingArgs[] = { CT_ARG_SHORT("heading") };
71 
72 static const cfg_info_t kHeadingInfo = {
73  .name = "heading",
74  .brief = "Diagnostic heading style.",
75 
76  .args = CT_ARGS(kHeadingArgs),
77 };
78 
79 static const cfg_arg_t kColourArgs[] = { CT_ARG_SHORT("fcolour-diagnostics") };
80 
81 static const cfg_info_t kColourInfo = {
82  .name = "colour",
83  .brief = "Enable colour output",
84 
85  .args = CT_ARGS(kColourArgs),
86 };
87 
88 static const cfg_info_t kDebugGroupInfo = {
89  .name = "debug",
90  .brief = "Internal debugging options",
91 };
92 
93 static const cfg_arg_t kVerboseArgs[] = { CT_ARG_SHORT("v"), CT_ARG_LONG("verbose") };
94 
95 static const cfg_info_t kVerboseLoggingInfo = {
96  .name = "verbose",
97  .brief = "Enable verbose logging",
98 
99  .args = CT_ARGS(kVerboseArgs),
100 };
101 
103 
104 typedef struct error_context_t {
108 
109 static error_context_t gErrorContext;
110 
111 static void default_error_begin(size_t error, void *user)
112 {
113  error_context_t *context = user;
114  context->report = bt_report_new(get_global_arena());
115 
116  char buffer[1024];
117  size_t len = os_error_get_string(error, buffer, sizeof(buffer));
118  io_printf(context->io, "System error detected: %.*s\n", (int)len, buffer);
119 }
120 
121 static void default_error_next(bt_address_t frame, void *user)
122 {
123  error_context_t *context = user;
124 
125  bt_report_add(context->report, frame);
126 }
127 
128 static void default_error_end(void *user)
129 {
130  error_context_t *context = user;
131 
132  fmt_backtrace_t print = {
133  .options = {
134  .arena = get_global_arena(),
135  .io = context->io,
136  .pallete = &kColourDefault
137  },
138  .header = eHeadingGeneric,
139  .config = eBtZeroIndexedLines,
140  .project_source_path = CTU_SOURCE_ROOT,
141  };
142 
143  fmt_backtrace(print, context->report);
144 
146 }
147 
149 
150 static void pretty_panic_handler(source_info_t location, const char *fmt, va_list args)
151 {
152  arena_t *arena = get_global_arena();
153  bt_report_t *report = bt_report_collect(arena);
154  io_t *io = io_stderr();
155 
156  print_options_t print_options = {
157  .arena = arena,
158  .io = io,
159  .pallete = &kColourDefault, // TODO: some of these should be globals
160  };
161 
162  fmt_backtrace_t backtrace_config = {
163  .options = print_options,
164  .header = eHeadingGeneric,
165  .config = eBtZeroIndexedLines,
166  .project_source_path = CTU_SOURCE_ROOT,
167  };
168 
169  char *msg = str_vformat(arena, fmt, args);
170 
171  io_printf(io, "[panic][%s:%" CT_PRI_LINE "] => %s: %s\n", location.file, location.line, location.function, msg);
172 
173  fmt_backtrace(backtrace_config, report);
175 }
176 
178 
179 static void default_verbose(const char *fmt, va_list args)
180 {
181  io_t *io = io_stdout();
182  io_vprintf(io, fmt, args);
183  io_printf(io, "\n");
184 }
185 
187 
188 void setup_default(arena_t *arena)
189 {
190  arena_t *mem = arena ? arena : ctu_default_alloc();
191  bt_init();
192  os_init();
193 
194  init_global_arena(mem);
195  init_gmp_arena(mem);
196 
197  error_context_t context = {
198  .io = io_stderr(),
199  };
200 
201  gErrorContext = context;
202 
203  bt_error_t error = {
204  .begin = default_error_begin,
205  .end = default_error_end,
206  .next = default_error_next,
207  .user = &gErrorContext,
208  };
209 
210  gSystemError = error;
211  gPanicHandler = pretty_panic_handler;
212  gVerboseCallback = default_verbose;
213 }
214 
216 {
217  CTASSERT(root != NULL);
218 
219  // general options
220 
221  cfg_group_t *general = config_group(root, &kGeneralGroupInfo);
222 
223  cfg_field_t *help = config_bool(general, &kHelpInfo, false);
224 
225  cfg_field_t *version = config_bool(general, &kVersionInfo, false);
226 
227  // report options
228 
229  cfg_group_t *report = config_group(general, &kReportGroupInfo);
230 
231  cfg_enum_t heading_info = {
232  .options = kHeadingOptions,
233  .count = HEADING_OPTION_COUNT,
234  .initial = CT_DEFAULT_HEADER_STYLE,
235  };
236 
237  cfg_field_t *header = config_enum(report, &kHeadingInfo, heading_info);
238 
239  cfg_field_t *colour = config_bool(report, &kColourInfo, false);
240 
241  // debug options
242 
243  cfg_group_t *debug = config_group(root, &kDebugGroupInfo);
244 
245  cfg_field_t *verbose = config_bool(debug, &kVerboseLoggingInfo, false);
246 
247  // argparse setup
248 
249  ap_t *ap = ap_new(root, ctu_default_alloc());
250 
251  setup_options_t options = {
252  .version = info,
253  .root = root,
254  .ap = ap,
255 
256  .general = {
257  .group = general,
258  .help = help,
259  .version = version,
260  },
261  .report = {
262  .group = report,
263  .header = header,
264  .colour = colour,
265  },
266  .debug = {
267  .group = debug,
268  .verbose = verbose,
269  }
270  };
271 
272  return options;
273 }
274 
276 
277 static void report_ap_errors(io_t *io, format_context_t ctx, const vector_t *args)
278 {
279  size_t len = vector_len(args);
280  if (len == 0) return;
281 
282  char *err = colour_text(ctx, eColourRed, "error:");
283 
284  for (size_t i = 0; i < len; i++)
285  {
286  const char *arg = vector_get(args, i);
287  io_printf(io, "%s: %s\n", err, arg);
288  }
289 }
290 
291 static void report_ap_unknown(io_t *io, format_context_t ctx, vector_t *args)
292 {
293  size_t count = vector_len(args);
294  if (count == 0) return;
295 
296  char *unk = colour_text(ctx, eColourYellow, "unknown argument");
297 
298  for (size_t i = 0; i < count; i++)
299  {
300  const char *arg = vector_get(args, i);
301  io_printf(io, "%s: %s\n", unk, arg);
302  }
303 }
304 
305 static heading_style_t get_heading_style(const setup_options_t *setup)
306 {
307  CTASSERT(setup != NULL);
308 
309  size_t style = cfg_enum_value(setup->report.header);
310  return (style == eHeadingMicrosoft) ? eHeadingMicrosoft : eHeadingGeneric;
311 }
312 
313 static setup_init_t setup_exit(int code)
314 {
315  setup_init_t result = { 0 };
316  result.exitcode = code | SHOULD_EXIT;
317  return result;
318 }
319 
320 static setup_init_t setup_help(
321  const char *name,
322  const colour_pallete_t *pallete,
323  bool usage,
324  heading_style_t style,
325  const cfg_group_t *config)
326 {
327  print_options_t base = {
328  .arena = get_global_arena(),
329  .io = io_stdout(),
330  .pallete = pallete,
331  };
332 
333  print_config_t print = {
334  .options = base,
335  .print_usage = usage,
336  .win_style = (style == eHeadingMicrosoft),
337  .name = name,
338  };
339 
340  print_config(print, config);
341 
342  return setup_exit(CT_EXIT_OK);
343 }
344 
345 static setup_init_t setup_version(
346  const char *name,
347  const colour_pallete_t *pallete,
348  version_info_t version)
349 {
350  print_options_t base = {
351  .arena = get_global_arena(),
352  .io = io_stdout(),
353  .pallete = pallete,
354  };
355 
356  print_version_t print = {
357  .options = base,
358  };
359 
360  print_version(print, version, name);
361 
362  return setup_exit(CT_EXIT_OK);
363 }
364 
365 STA_DECL
366 setup_init_t setup_parse(int argc, const char **argv, setup_options_t setup)
367 {
368  CTASSERT(argc > 0);
369  CTASSERT(argv != NULL);
370 
371  arena_t *arena = get_global_arena();
372  io_t *io = io_stderr();
373 
374  ap_update(setup.ap);
375 
376  int err = ap_parse_args(setup.ap, argc, argv);
377 
378  vector_t *errors = ap_get_errors(setup.ap);
379  vector_t *unknown = ap_get_unknown(setup.ap);
380 
381  const colour_pallete_t *pallete = (cfg_bool_value(setup.report.colour) ? &kColourDefault : &kColourNone);
382  heading_style_t heading = get_heading_style(&setup);
383 
385  .pallete = pallete,
386  .arena = arena,
387  };
388 
389  report_ap_errors(io, fmt, errors);
390  report_ap_unknown(io, fmt, unknown);
391 
392  // if there was an error parsing the arguments, exit
393  // errors have already been printed
394  if (err != CT_EXIT_OK)
395  return setup_exit(err);
396 
397  vector_t *posargs = ap_get_posargs(setup.ap);
398  size_t pos = vector_len(posargs);
399  size_t unknowns = vector_len(unknown);
400  size_t params = ap_count_params(setup.ap);
401 
402  // no valid arguments were provided, print help and exit
403  if (pos == 0 && unknowns == 0 && params == 0)
404  {
405  io_printf(io, "%s: no arguments provided\n", argv[0]);
406  return setup_help(argv[0], pallete, false, heading, setup.root);
407  }
408 
409  // if the help flag was provided, print help and exit
410  bool help = cfg_bool_value(setup.general.help);
411  if (help)
412  return setup_help(argv[0], pallete, true, heading, setup.root);
413 
414  // if the version flag was provided, print version and exit
415  bool version = cfg_bool_value(setup.general.version);
416  if (version)
417  return setup_version(argv[0], pallete, setup.version);
418 
419  setup_init_t result = {
420  .argc = argc,
421  .argv = argv,
422  .exitcode = CT_EXIT_OK,
423  .posargs = posargs,
424  .pallete = pallete,
425  .heading = heading,
426  };
427 
428  return result;
429 }
430 
432 
433 STA_DECL
435 {
436  CTASSERT(init != NULL);
437 
438  return init->exitcode & SHOULD_EXIT;
439 }
440 
441 STA_DECL
443 {
445 
446  return init->exitcode & ~SHOULD_EXIT;
447 }
448 
449 STA_DECL
451 {
452  CTASSERT(!setup_should_exit(init));
453 
454  setup_help(init->argv[0], init->pallete, false, init->heading, setup.root);
455 
456  return CT_EXIT_OK;
457 }
CT_NODISCARD CT_OS_API size_t os_error_get_string(os_error_t error, STA_WRITES(size) char *buffer, size_t size)
convert an os error code to a string writes to a buffer rather than allocating. if size is 0,...
CT_NORETURN CT_OS_API os_exit(os_exitcode_t code)
exit the program
Definition: os.c:20
CT_OS_API void os_init(void)
initialize the os api
Definition: os.c:15
#define CT_DEFAULT_HEADER_STYLE
Definition: format.h:30
#define STA_DECL
sal2 annotation on function implementations to copy annotations from the declaration
CT_ARGPARSE_API size_t ap_count_params(ap_t *self)
get the number of processed arguments
Definition: argparse.c:181
CT_ARGPARSE_API vector_t * ap_get_unknown(ap_t *self)
get all unknown arguments
Definition: argparse.c:166
CT_ARGPARSE_API int ap_parse_args(ap_t *self, int argc, const char **argv)
parse a command line
Definition: parse.c:100
CT_ARGPARSE_API ap_t * ap_new(cfg_group_t *config, arena_t *arena)
create a new parser instance
Definition: argparse.c:97
CT_ARGPARSE_API void ap_update(ap_t *self)
update the parser with the latest config
Definition: argparse.c:128
CT_ARGPARSE_API vector_t * ap_get_posargs(ap_t *self)
get all positional arguments
Definition: argparse.c:158
CT_ARGPARSE_API vector_t * ap_get_errors(ap_t *self)
get all errors
Definition: argparse.c:174
CT_BACKTRACE_API void bt_init(void)
initialize the backtrace backend
Definition: dbghelp.c:139
uint_least64_t bt_address_t
an address of a symbol
Definition: backtrace.h:26
CT_BACKTRACE_API bt_error_t gSystemError
the global system error handler
Definition: common.c:11
CT_FORMAT_API const colour_pallete_t kColourNone
a colour pallete that applies no colours
Definition: colour.c:10
CT_FORMAT_API const colour_pallete_t kColourDefault
a colour pallete that applies ANSI VT100 colours
Definition: colour.c:24
STA_RET_STRING CT_FORMAT_API char * colour_text(format_context_t context, colour_t idx, const char *text)
add colour to a string
Definition: colour.c:56
@ eColourYellow
Definition: colour.h:26
@ eColourRed
Definition: colour.h:24
CT_CONFIG_API cfg_field_t * config_enum(cfg_group_t *group, const cfg_info_t *info, cfg_enum_t cfg)
add a new choice field to a configuration group
Definition: config.c:162
CT_CONFIG_API cfg_group_t * config_group(cfg_group_t *group, const cfg_info_t *info)
add a new configuration group to a configuration group
Definition: config.c:188
CT_CONFIG_API cfg_field_t * config_bool(cfg_group_t *group, const cfg_info_t *info, bool initial)
add a new yes/no field to a configuration group
Definition: config.c:126
CT_PUREFN CT_CONFIG_API bool cfg_bool_value(const cfg_field_t *field)
get the current boolean value of a configuration field
Definition: reflect.c:143
CT_PUREFN CT_CONFIG_API size_t cfg_enum_value(const cfg_field_t *field)
get the current enum value of a configuration field
Definition: reflect.c:167
#define CT_ARG_DOS(name)
Definition: config.h:56
#define CT_ARGS(it)
Definition: config.h:57
#define CT_ARG_LONG(name)
Definition: config.h:55
#define CT_ARG_SHORT(name)
Definition: config.h:54
#define CT_EXIT_OK
no user errors or internal errors
Definition: macros.h:91
#define CT_EXIT_INTERNAL
internal compiler errors have occurred
Definition: macros.h:94
CT_FORMAT_API bt_report_t * bt_report_collect(arena_t *arena)
collect a backtrace into a report this is equivalent to calling bt_report_new() and then bt_report_ad...
Definition: backtrace.c:504
CT_FORMAT_API bt_report_t * bt_report_new(arena_t *arena)
create a new backtrace report
Definition: backtrace.c:485
CT_FORMAT_API void bt_report_add(bt_report_t *report, bt_address_t frame)
add a frame to a backtrace report
Definition: backtrace.c:514
CT_FORMAT_API void fmt_backtrace(fmt_backtrace_t fmt, bt_report_t *report)
print a backtrace report
Definition: backtrace.c:426
CT_FORMAT_API void print_version(print_version_t config, version_info_t version, const char *name)
print the version of the program
Definition: version.c:9
fmt_heading_t
line heading style
Definition: format.h:18
CT_MEMORY_API void init_gmp_arena(arena_t *arena)
initialize gmp with a custom allocator
Definition: memory.c:52
CT_MEMORY_API void init_global_arena(arena_t *arena)
initialize the global memory arena
Definition: memory.c:21
CT_MEMORY_API arena_t * get_global_arena(void)
get the global memory arena
Definition: memory.c:16
CT_IO_API size_t io_vprintf(io_t *io, const char *fmt, va_list args)
vprintf to an io object
Definition: io.c:79
CT_IO_API io_t * io_stderr(void)
get the global stderr IO object
Definition: console.c:65
CT_IO_API io_t * io_stdout(void)
get the global stdout IO object
Definition: console.c:60
CT_IO_API size_t io_printf(io_t *io, STA_FORMAT_STRING const char *fmt,...)
printf to an io object
CT_BASE_API verbose_t gVerboseCallback
the global verbose logging callback
Definition: log.c:12
CT_BASE_API panic_handler_t gPanicHandler
the global panic handler.
Definition: panic.c:5
#define CTASSERT(expr)
assert a condition, prints the condition as a message
Definition: panic.h:130
STA_DECL setup_init_t setup_parse(int argc, const char **argv, setup_options_t setup)
parse the command line
Definition: setup.c:366
STA_DECL int setup_exit_help(setup_options_t setup, const setup_init_t *init)
print the help message and exit
Definition: setup.c:450
STA_DECL bool setup_should_exit(const setup_init_t *init)
public accessor api
Definition: setup.c:434
void setup_default(arena_t *arena)
public api
Definition: setup.c:188
CT_BEGIN_API CT_SETUP_API arena_t * ctu_default_alloc(void)
get the default allocator
Definition: memory.c:43
setup_options_t setup_options(version_info_t info, cfg_group_t *root)
setup default options
Definition: setup.c:215
STA_DECL int setup_exit_code(const setup_init_t *init)
get the exit code
Definition: setup.c:442
#define CT_PRI_LINE
format specifier for source_line_t
Definition: source_info.h:19
CT_NODISCARD STA_FORMAT_STRING const char * fmt
Definition: str.h:68
CT_NODISCARD STA_FORMAT_STRING const char CT_NODISCARD CT_STD_API char * str_vformat(arena_t *arena, const char *fmt, va_list args)
format a string
Definition: str.c:110
CT_NODISCARD CT_PUREFN CT_STD_API void * vector_get(const vector_t *vector, size_t index)
get a value from a vector
Definition: vector.c:134
CT_NODISCARD CT_PUREFN CT_STD_API size_t vector_len(const vector_t *vector)
get the length of a vector
Definition: vector.c:152
#define HEADING_OPTION_COUNT
Definition: setup.c:68
#define SHOULD_EXIT
Definition: setup.c:34
argparse instance
Definition: common.h:26
an allocator object
Definition: arena.h:86
system error handling callbacks
Definition: backtrace.h:89
bt_error_begin_t begin
called once when a system error occurs
Definition: backtrace.h:91
a backtrace report context
Definition: backtrace.c:29
a choice in a set of options
Definition: config.h:97
STA_FIELD_STRING const char * text
the name of this choice
Definition: config.h:99
a choice from a set of options
Definition: config.h:107
information about a configuration field
Definition: config.h:69
STA_FIELD_STRING const char * name
the name of this field
Definition: config.h:71
a colour pallete
Definition: colour.h:39
system error handler
Definition: setup.c:104
io_t * io
Definition: setup.c:105
bt_report_t * report
Definition: setup.c:106
printing options for a stacktrace
Definition: backtrace.h:36
print_options_t options
basic print options
Definition: backtrace.h:38
generic print options
Definition: format.h:35
arena_t * arena
temporary arena
Definition: format.h:37
a formatting context when using colours
Definition: colour.h:46
io object implementation
Definition: impl.h:122
config format options
Definition: config.h:19
print_options_t options
generic print options
Definition: config.h:21
version formatting config
Definition: version.h:21
print_options_t options
the options to use when printing
Definition: version.h:23
the result of parsing the command line
Definition: setup.h:69
const colour_pallete_t * pallete
the chosen colour pallete
Definition: setup.h:82
int exitcode
the exitcode
Definition: setup.h:76
const char ** argv
Definition: setup.h:72
heading_style_t heading
the chosen heading style
Definition: setup.h:85
int argc
the arguments
Definition: setup.h:71
default options shared by all tools
Definition: setup.h:31
cfg_field_t * help
print help and quit
Definition: setup.h:41
cfg_field_t * colour
enable colour output
Definition: setup.h:55
cfg_field_t * header
report header style
Definition: setup.h:52
version_info_t version
Definition: setup.h:32
struct setup_options_t::@170 report
diagnostic reporting options
struct setup_options_t::@169 general
general options
cfg_group_t * root
Definition: setup.h:33
ap_t * ap
Definition: setup.h:34
panic location information
Definition: source_info.h:26
source_line_t line
the line the panic occurred on
Definition: source_info.h:31
STA_FIELD_STRING const char * function
the function the panic occurred in
Definition: source_info.h:35
STA_FIELD_STRING const char * file
the file the panic occurred in
Definition: source_info.h:28
a generic vector of pointers
Definition: vector.c:16
version information for a driver/interface/plugin
Definition: version_def.h:48
CT_FORMAT_API void print_config(print_config_t print, const cfg_group_t *config)
print a configuration object
Definition: config.c:463