Skip to content

Commit 5a59360

Browse files
upgrade parser to support new types and short arg names
1 parent 57e61df commit 5a59360

File tree

2 files changed

+156
-18
lines changed

2 files changed

+156
-18
lines changed

code/logic/fossil/io/parser.h

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ typedef enum {
3636
FOSSIL_IO_PARSER_BOOL, // Boolean (enable/disable)
3737
FOSSIL_IO_PARSER_STRING, // String argument
3838
FOSSIL_IO_PARSER_INT, // Integer argument
39+
FOSSIL_IO_PARSER_UINT, // Unsigned integer argument
3940
FOSSIL_IO_PARSER_FLOAT, // Floating-point argument
41+
FOSSIL_IO_PARSER_HEX, // Hexadecimal argument
42+
FOSSIL_IO_PARSER_OCT, // Octal argument
4043
FOSSIL_IO_PARSER_DATE, // Date argument
4144
FOSSIL_IO_PARSER_ARRAY, // Array of values
4245
FOSSIL_IO_PARSER_FEATURE, // Feature flag
@@ -46,6 +49,7 @@ typedef enum {
4649
// Structure to represent each argument in the command
4750
typedef struct fossil_io_parser_argument_s {
4851
char *name; // Argument name
52+
char *short_name; // Short argument name (e.g., -v for --verbose)
4953
fossil_io_parser_arg_type_t type; // Argument type
5054
char *value; // Parsed value
5155
char **combo_options; // Valid options for COMBO type
@@ -56,6 +60,7 @@ typedef struct fossil_io_parser_argument_s {
5660
// Structure for a command
5761
typedef struct fossil_io_parser_command_s {
5862
char *name; // Command name
63+
char *short_name; // Short command name
5964
char *description; // Command description
6065
fossil_io_parser_argument_t *arguments; // List of arguments
6166
struct fossil_io_parser_command_s *prev; // Previous command in the list
@@ -97,22 +102,24 @@ fossil_io_parser_palette_t *fossil_io_parser_create_palette(const char *name, co
97102
*
98103
* @param palette The parser palette to which the command will be added.
99104
* @param command_name The name of the command.
105+
* @param short_name The short name of the command (can be NULL).
100106
* @param description A description of the command.
101107
* @return A pointer to the newly added command.
102108
*/
103-
fossil_io_parser_command_t *fossil_io_parser_add_command(fossil_io_parser_palette_t *palette, const char *command_name, const char *description);
109+
fossil_io_parser_command_t *fossil_io_parser_add_command(fossil_io_parser_palette_t *palette, const char *command_name, const char *short_name, const char *description);
104110

105111
/**
106112
* @brief Adds an argument to a command.
107113
*
108114
* @param command The command to which the argument will be added.
109115
* @param arg_name The name of the argument.
116+
* @param short_name The short name of the argument (can be NULL).
110117
* @param arg_type The type of the argument.
111118
* @param combo_options (Optional) Array of valid options for COMBO type.
112119
* @param combo_count (Optional) Number of options for COMBO type.
113120
* @return A pointer to the newly added argument.
114121
*/
115-
fossil_io_parser_argument_t *fossil_io_parser_add_argument(fossil_io_parser_command_t *command, const char *arg_name, fossil_io_parser_arg_type_t arg_type, char **combo_options, int combo_count);
122+
fossil_io_parser_argument_t *fossil_io_parser_add_argument(fossil_io_parser_command_t *command, const char *arg_name, const char *short_name, fossil_io_parser_arg_type_t arg_type, char **combo_options, int combo_count);
116123

117124
/**
118125
* @brief Parses the command-line arguments using the parser palette.
@@ -132,6 +139,7 @@ void fossil_io_parser_free(fossil_io_parser_palette_t *palette);
132139

133140
#ifdef __cplusplus
134141
}
142+
#include <string>
135143

136144
/**
137145
* C++ wrapper for the Parser API.
@@ -163,25 +171,27 @@ namespace fossil {
163171
*
164172
* @param palette The parser palette to which the command will be added.
165173
* @param command_name The name of the command.
174+
* @param short_name The short name of the command (can be NULL).
166175
* @param description A description of the command.
167176
* @return A pointer to the newly added command.
168177
*/
169-
static fossil_io_parser_command_t *add_command(fossil_io_parser_palette_t *palette, const char *command_name, const char *description) {
170-
return fossil_io_parser_add_command(palette, command_name, description);
178+
static fossil_io_parser_command_t *add_command(fossil_io_parser_palette_t *palette, const std::string& command_name, const std::string& short_name, const std::string& description) {
179+
return fossil_io_parser_add_command(palette, command_name.c_str(), short_name.empty() ? nullptr : short_name.c_str(), description.c_str());
171180
}
172181

173182
/**
174183
* Adds an argument to a command.
175184
*
176185
* @param command The command to which the argument will be added.
177186
* @param arg_name The name of the argument.
187+
* @param short_name The short name of the argument (can be NULL).
178188
* @param arg_type The type of the argument.
179189
* @param combo_options (Optional) Array of valid options for COMBO type.
180190
* @param combo_count (Optional) Number of options for COMBO type.
181191
* @return A pointer to the newly added argument.
182192
*/
183-
static fossil_io_parser_argument_t *add_argument(fossil_io_parser_command_t *command, const char *arg_name, fossil_io_parser_arg_type_t arg_type, char **combo_options, int combo_count) {
184-
return fossil_io_parser_add_argument(command, arg_name, arg_type, combo_options, combo_count);
193+
static fossil_io_parser_argument_t *add_argument(fossil_io_parser_command_t *command, const std::string& arg_name, const std::string& short_name, fossil_io_parser_arg_type_t arg_type, char **combo_options, int combo_count) {
194+
return fossil_io_parser_add_argument(command, arg_name.c_str(), short_name.empty() ? nullptr : short_name.c_str(), arg_type, combo_options, combo_count);
185195
}
186196

187197
/**

code/logic/parser.c

Lines changed: 140 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,21 @@ void show_usage(const char *command_name, const fossil_io_parser_palette_t *pale
245245
case FOSSIL_IO_PARSER_INT:
246246
fossil_io_printf("{cyan}<int>{reset}");
247247
break;
248+
case FOSSIL_IO_PARSER_UINT:
249+
fossil_io_printf("{cyan}<uint>{reset}");
250+
break;
248251
case FOSSIL_IO_PARSER_BOOL:
249252
fossil_io_printf("{cyan}<true/false>{reset}");
250253
break;
251254
case FOSSIL_IO_PARSER_FLOAT:
252255
fossil_io_printf("{cyan}<float>{reset}");
253256
break;
257+
case FOSSIL_IO_PARSER_HEX:
258+
fossil_io_printf("{cyan}<0xHEX>{reset}");
259+
break;
260+
case FOSSIL_IO_PARSER_OCT:
261+
fossil_io_printf("{cyan}<0OCT>{reset}");
262+
break;
254263
case FOSSIL_IO_PARSER_DATE:
255264
fossil_io_printf("{cyan}<YYYY-MM-DD>{reset}");
256265
break;
@@ -310,7 +319,7 @@ fossil_io_parser_palette_t *fossil_io_parser_create_palette(const char *name, co
310319
return palette;
311320
}
312321

313-
fossil_io_parser_command_t *fossil_io_parser_add_command(fossil_io_parser_palette_t *palette, const char *command_name, const char *description) {
322+
fossil_io_parser_command_t *fossil_io_parser_add_command(fossil_io_parser_palette_t *palette, const char *command_name, const char *short_name, const char *description) {
314323
if (!palette || !command_name || !description) {
315324
fossil_io_fprintf(FOSSIL_STDERR, "{red}Error: Palette, command name, and description cannot be NULL.{reset}\n");
316325
return NULL;
@@ -321,13 +330,17 @@ fossil_io_parser_command_t *fossil_io_parser_add_command(fossil_io_parser_palett
321330
return NULL;
322331
}
323332

324-
// Check for duplicate command name
333+
// Check for duplicate command name or short name
325334
fossil_io_parser_command_t *current = palette->commands;
326335
while (current) {
327336
if (strcmp(current->name, command_name) == 0) {
328337
fossil_io_fprintf(FOSSIL_STDERR, "{red}Error: Command with name '%s' already exists.{reset}\n", command_name);
329338
return NULL;
330339
}
340+
if (short_name && current->short_name && strcmp(current->short_name, short_name) == 0) {
341+
fossil_io_fprintf(FOSSIL_STDERR, "{red}Error: Command with short name '%s' already exists.{reset}\n", short_name);
342+
return NULL;
343+
}
331344
current = current->next;
332345
}
333346

@@ -336,8 +349,10 @@ fossil_io_parser_command_t *fossil_io_parser_add_command(fossil_io_parser_palett
336349
fossil_io_fprintf(FOSSIL_STDERR, "{red}Error: Memory allocation failed for command.{reset}\n");
337350
return NULL;
338351
}
352+
339353
// Initialize all fields to zero/NULL
340354
command->name = NULL;
355+
command->short_name = NULL;
341356
command->description = NULL;
342357
command->arguments = NULL;
343358
command->prev = NULL;
@@ -350,10 +365,21 @@ fossil_io_parser_command_t *fossil_io_parser_add_command(fossil_io_parser_palett
350365
return NULL;
351366
}
352367

368+
if (short_name) {
369+
command->short_name = _custom_strdup(short_name);
370+
if (!command->short_name) {
371+
fossil_io_fprintf(FOSSIL_STDERR, "{red}Error: Memory allocation failed for command short name.{reset}\n");
372+
free(command->name);
373+
free(command);
374+
return NULL;
375+
}
376+
}
377+
353378
command->description = _custom_strdup(description);
354379
if (!command->description) {
355380
fossil_io_fprintf(FOSSIL_STDERR, "{red}Error: Memory allocation failed for command description.{reset}\n");
356381
free(command->name);
382+
if (command->short_name) free(command->short_name);
357383
free(command);
358384
return NULL;
359385
}
@@ -368,31 +394,71 @@ fossil_io_parser_command_t *fossil_io_parser_add_command(fossil_io_parser_palett
368394
return command;
369395
}
370396

371-
fossil_io_parser_argument_t *fossil_io_parser_add_argument(fossil_io_parser_command_t *command, const char *arg_name, fossil_io_parser_arg_type_t arg_type, char **combo_options, int combo_count) {
397+
fossil_io_parser_argument_t *fossil_io_parser_add_argument(fossil_io_parser_command_t *command, const char *arg_name, const char *short_name, fossil_io_parser_arg_type_t arg_type, char **combo_options, int combo_count) {
372398
if (!command || !arg_name) {
373399
fossil_io_fprintf(FOSSIL_STDERR, "{red}Error: Command and argument name cannot be NULL.{reset}\n");
374400
return NULL;
375401
}
376402

403+
if (strlen(arg_name) == 0) {
404+
fossil_io_fprintf(FOSSIL_STDERR, "{red}Error: Argument name cannot be empty.{reset}\n");
405+
return NULL;
406+
}
407+
408+
// Check for duplicate argument name or short name
409+
fossil_io_parser_argument_t *current = command->arguments;
410+
while (current) {
411+
if (strcmp(current->name, arg_name) == 0) {
412+
fossil_io_fprintf(FOSSIL_STDERR, "{red}Error: Argument with name '%s' already exists.{reset}\n", arg_name);
413+
return NULL;
414+
}
415+
if (short_name && current->short_name && strcmp(current->short_name, short_name) == 0) {
416+
fossil_io_fprintf(FOSSIL_STDERR, "{red}Error: Argument with short name '%s' already exists.{reset}\n", short_name);
417+
return NULL;
418+
}
419+
current = current->next;
420+
}
421+
377422
fossil_io_parser_argument_t *argument = malloc(sizeof(fossil_io_parser_argument_t));
378423
if (!argument) {
379424
fossil_io_fprintf(FOSSIL_STDERR, "{red}Error: Memory allocation failed for argument.{reset}\n");
380425
return NULL;
381426
}
382427

428+
// Initialize all fields to zero/NULL
429+
argument->name = NULL;
430+
argument->short_name = NULL;
431+
argument->type = FOSSIL_IO_PARSER_BOOL;
432+
argument->value = NULL;
433+
argument->combo_options = NULL;
434+
argument->combo_count = 0;
435+
argument->next = NULL;
436+
383437
argument->name = _custom_strdup(arg_name);
384438
if (!argument->name) {
385439
fossil_io_fprintf(FOSSIL_STDERR, "{red}Error: Memory allocation failed for argument name.{reset}\n");
386440
free(argument);
387441
return NULL;
388442
}
389443

390-
if (arg_type < FOSSIL_IO_PARSER_BOOL || arg_type > FOSSIL_IO_PARSER_FEATURE) {
444+
if (short_name) {
445+
argument->short_name = _custom_strdup(short_name);
446+
if (!argument->short_name) {
447+
fossil_io_fprintf(FOSSIL_STDERR, "{red}Error: Memory allocation failed for argument short name.{reset}\n");
448+
free(argument->name);
449+
free(argument);
450+
return NULL;
451+
}
452+
}
453+
454+
if (arg_type < FOSSIL_IO_PARSER_BOOL || arg_type >= FOSSIL_IO_PARSER_INVALID) {
391455
fossil_io_fprintf(FOSSIL_STDERR, "{red}Error: Invalid argument type for '%s'.{reset}\n", arg_name);
392456
free(argument->name);
457+
if (argument->short_name) free(argument->short_name);
393458
free(argument);
394459
return NULL;
395460
}
461+
396462
argument->type = arg_type;
397463
argument->value = NULL;
398464
argument->combo_options = combo_options;
@@ -576,6 +642,18 @@ void fossil_io_parser_parse(fossil_io_parser_palette_t *palette, int argc, char
576642
fossil_io_fprintf(FOSSIL_STDERR, "{red}Missing value for integer argument: %s{reset}\n", arg_value);
577643
}
578644
break;
645+
case FOSSIL_IO_PARSER_UINT:
646+
if (i + 1 < argc) {
647+
argument->value = malloc(sizeof(unsigned int));
648+
if (!argument->value) {
649+
fossil_io_fprintf(FOSSIL_STDERR, "{red}Error: Memory allocation failed for unsigned integer argument.{reset}\n");
650+
return;
651+
}
652+
*(unsigned int *)argument->value = (unsigned int)atol(argv[++i]);
653+
} else {
654+
fossil_io_fprintf(FOSSIL_STDERR, "{red}Missing value for unsigned integer argument: %s{reset}\n", arg_value);
655+
}
656+
break;
579657
case FOSSIL_IO_PARSER_FLOAT:
580658
if (i + 1 < argc) {
581659
argument->value = malloc(sizeof(float));
@@ -588,6 +666,30 @@ void fossil_io_parser_parse(fossil_io_parser_palette_t *palette, int argc, char
588666
fossil_io_fprintf(FOSSIL_STDERR, "{red}Missing value for float argument: %s{reset}\n", arg_value);
589667
}
590668
break;
669+
case FOSSIL_IO_PARSER_HEX:
670+
if (i + 1 < argc) {
671+
argument->value = malloc(sizeof(unsigned int));
672+
if (!argument->value) {
673+
fossil_io_fprintf(FOSSIL_STDERR, "{red}Error: Memory allocation failed for hex argument.{reset}\n");
674+
return;
675+
}
676+
*(unsigned int *)argument->value = (unsigned int)strtol(argv[++i], NULL, 16);
677+
} else {
678+
fossil_io_fprintf(FOSSIL_STDERR, "{red}Missing value for hex argument: %s{reset}\n", arg_value);
679+
}
680+
break;
681+
case FOSSIL_IO_PARSER_OCT:
682+
if (i + 1 < argc) {
683+
argument->value = malloc(sizeof(unsigned int));
684+
if (!argument->value) {
685+
fossil_io_fprintf(FOSSIL_STDERR, "{red}Error: Memory allocation failed for octal argument.{reset}\n");
686+
return;
687+
}
688+
*(unsigned int *)argument->value = (unsigned int)strtol(argv[++i], NULL, 8);
689+
} else {
690+
fossil_io_fprintf(FOSSIL_STDERR, "{red}Missing value for octal argument: %s{reset}\n", arg_value);
691+
}
692+
break;
591693
case FOSSIL_IO_PARSER_DATE:
592694
if (i + 1 < argc) {
593695
argument->value = _custom_strdup(argv[++i]);
@@ -670,21 +772,47 @@ void fossil_io_parser_parse(fossil_io_parser_palette_t *palette, int argc, char
670772
}
671773

672774
void fossil_io_parser_free(fossil_io_parser_palette_t *palette) {
775+
if (!palette) return;
776+
673777
fossil_io_parser_command_t *command = palette->commands;
674778
while (command) {
779+
fossil_io_parser_command_t *next_command = command->next;
780+
781+
// Free all arguments for this command
675782
fossil_io_parser_argument_t *argument = command->arguments;
676783
while (argument) {
677-
free(argument->name);
678-
if (argument->value && argument->value != (char *)argument->combo_options) {
784+
fossil_io_parser_argument_t *next_argument = argument->next;
785+
786+
if (argument->name) free(argument->name);
787+
if (argument->short_name) free(argument->short_name);
788+
789+
// Handle different value types
790+
if (argument->value) {
791+
if (argument->type == FOSSIL_IO_PARSER_ARRAY) {
792+
// For arrays, we need to free each string in the array
793+
char **array_values = (char **)argument->value;
794+
if (array_values) {
795+
for (int i = 0; array_values[i] != NULL; i++) {
796+
free(array_values[i]);
797+
}
798+
}
799+
}
679800
free(argument->value);
680801
}
681-
argument = argument->next;
802+
803+
free(argument);
804+
argument = next_argument;
682805
}
683-
free(command->name);
684-
free(command->description);
685-
command = command->next;
806+
807+
if (command->name) free(command->name);
808+
if (command->short_name) free(command->short_name);
809+
if (command->description) free(command->description);
810+
811+
free(command);
812+
command = next_command;
686813
}
687-
free(palette->name);
688-
free(palette->description);
814+
815+
if (palette->name) free(palette->name);
816+
if (palette->description) free(palette->description);
689817
free(palette);
690818
}

0 commit comments

Comments
 (0)