Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ if(TL_BUILD_TESTS)
tl_config_test
tl_debug_test
tl_error_test
tl_flag_test
tl_test_test
)

Expand Down
30 changes: 0 additions & 30 deletions include/tl_app.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
#ifndef TL_APP_H
#define TL_APP_H

#include <stdbool.h>

/**
* @brief Initializes the running app with the given command line arguments.
*
Expand All @@ -15,32 +13,4 @@
*/
void tl_init_app(int argc, char *argv[]);

/**
* @brief Parses the given command line arguments.
*
* @param argc The number of command line arguments.
* @param argv The command line arguments.
*
* @return void
*/
void tl_parse_args(int argc, char *argv[]);

/**
* @brief Looks up a specific flag.
*
* @param flag The flag to look up.
*
* @return true if the flag is found, false otherwise.
*/
bool tl_lookup_flag(const char *flag);

/**
* @brief Returns the value of a specific flag.
*
* @param flag The flag to get.
*
* @return The value of the flag, or NULL if not found.
*/
const char *tl_get_flag(const char *flag);

#endif // TL_APP_H
143 changes: 143 additions & 0 deletions include/tl_flag.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// See LICENSE.txt and CONTRIBUTING.md for details.

#ifndef TL_FLAG_H
#define TL_FLAG_H

#include <stdbool.h>
#include <stddef.h>

/**
* @brief Represents a parsed flag.
*
* `name` points into argv (or the tokenizer buffer) at the first '-' of
* the flag. `name_len` is the length up to '\0' or '='. For the '=' form
* `name` is NOT a NUL-terminated C string at name_len — name[name_len] is
* '=', so comparisons must use memcmp with name_len, never strcmp.
*
* argv entry: "--foo=bar"
* - - f o o = b a r \0
* ^ ^
* name value
* name_len = 5
* value = "bar"
*
* argv entry: "--foo" "bar"
* - - f o o \0 b a r \0
* ^ ^
* name value
* name_len = 5
* value = "bar"
*
* argv entry: "--foo" (boolean, no value)
* - - f o o \0
* ^
* name
* name_len = 5
* value = NULL
*/
typedef struct {
const char *name; // points at the first '-' of the flag in argv
size_t name_len; // length of the flag name up to '\0' or '='
const char *value; // value after first '=', or NULL if none
} tl_flag_t;

/**
* @brief Parses the given command line arguments.
*
* Parses argv into flags. A flag is anything starting with "--". It can carry
* a value written as --name=value, or as --name value in the next entry.
* A bare "--" ends flag parsing; everything after it is a positional, even if
* it starts with dashes. Any previously parsed state is thrown away first.
*
* @param argc The number of command line arguments.
* @param argv The command line arguments.
*
* @return void
*/
void tl_parse_args(int argc, char *argv[]);

/**
* @brief Parses a raw command line string.
*
* Splits the line into tokens. Double quotes group text with spaces into
* one token, and a backslash keeps the next character as-is. The first
* token is the program name and is skipped, like argv[0].
*
* @param line The command line string to parse.
*
* @return true on success, false otherwise.
*/
bool tl_parse_line(const char *line);

/**
* @brief Releases memory held by the argument parser.
*
* Safe to call when nothing has been parsed. Called implicitly by
* tl_parse_args and tl_parse_line.
*
* @return void
*/
void tl_free_args(void);

/**
* @brief Looks up a specific flag.
*
* @param flag The flag to look up.
*
* @return true if the flag is found, false otherwise.
*/
bool tl_lookup_flag(const char *flag);

/**
* @brief Returns the value of a specific flag.
*
* Returns the value of the first occurrence of flag. For repeated flags
* use tl_count_flag and tl_get_flag_at.
*
* @param flag The flag to get.
*
* @return The value of the flag, or NULL if not found or no value.
*/
const char *tl_get_flag(const char *flag);

/**
* @brief Returns the number of times a flag was given.
*
* @param flag The flag to count.
*
* @return The occurrence count (0 if not given).
*/
size_t tl_count_flag(const char *flag);

/**
* @brief Returns the value of a repeated flag at a given index.
*
* Occurrences are indexed in the order they appeared on the command line.
*
* @param flag The flag to get.
* @param index The occurrence index (0-based).
*
* @return The value, or NULL if out of range or no value at that index.
*/
const char *tl_get_flag_at(const char *flag, size_t index);

/**
* @brief Returns the number of positional arguments.
*
* Positionals are bare arguments (not starting with `--`) and everything
* after a bare `--` terminator, in the order they appeared.
*
* @return The positional argument count.
*/
size_t tl_count_positional(void);

/**
* @brief Returns the positional argument at the given index.
*
* @param index The positional index (0-based).
*
* @return The positional value, or NULL if out of range.
*/
const char *tl_get_positional(size_t index);

#endif // TL_FLAG_H
41 changes: 1 addition & 40 deletions src/tl_app.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,12 @@

#include "tl_app.h"
#include "tl_config.h"
#include <stdio.h>
#include "tl_flag.h"
#include <stdlib.h>
#include <string.h>

// Init vars
static int arg_count = 0;
static char **args = NULL;

void tl_init_app(int argc, char *argv[]) {
tl_parse_args(argc, argv);
if (tl_get_flag("--debug-level")) {
tl_set_debug_level((int)strtol(tl_get_flag("--debug-level"), NULL, 10));
}
}

void tl_parse_args(int argc, char *argv[]) {
arg_count = argc;
args = argv;
}

bool tl_lookup_flag(const char *flag) {
for (int i = 1; i < arg_count; i++) {
// If the argument starts with the flag and is followed by either '\0' or '=' then
if (strncmp(args[i], flag, strlen(flag)) == 0 &&
(args[i][strlen(flag)] == '\0' || args[i][strlen(flag)] == '=')) {
return true;
}
}
return false;
}

const char *tl_get_flag(const char *flag) {
size_t flag_len = strlen(flag);
for (int i = 1; i < arg_count; i++) {
if (strncmp(args[i], flag, flag_len) != 0) {
continue;
}
// If the argument is followed by '=' then return the value after '='
if (args[i][flag_len] == '=') {
return args[i] + flag_len + 1;
}
// If the argument is an exact match and the next argument exists then return it
if (args[i][flag_len] == '\0' && i + 1 < arg_count) {
return args[i + 1];
}
}
return NULL;
}
Loading