Skip to content

Commit

Permalink
Merge pull request #14 from Christer-Ekholm/add_support_to_systems_wi…
Browse files Browse the repository at this point in the history
…thout_FILE_and_fprintf

Add support to systems without file and fprintf
  • Loading branch information
likle authored Jan 18, 2024
2 parents 82b51a3 + 953c9b0 commit 50e713b
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 26 deletions.
42 changes: 42 additions & 0 deletions include/cargs.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ typedef struct cag_option_context
char *value;
} cag_option_context;

/**
* Prototype for printer used in cag_option_printer. For example fprintf have same prototype
*/
typedef int (*cag_printer)(void *ctx, const char *fmt, ...);

/**
* This is just a small macro which calculates the size of an array.
*/
Expand Down Expand Up @@ -196,8 +201,26 @@ CAG_PUBLIC char cag_option_get_error_letter(const cag_option_context *context);
* @param context Pointer to the context from which the option was fetched.
* @param destination Pointer to the file stream where the error information will be printed.
*/
#ifndef CAG_NO_FILE
CAG_PUBLIC void cag_option_print_error(const cag_option_context *context,
FILE *destination);
#endif

/**
* @brief Prints the error associated with the invalid option using user
* callback.
*
* This function prints information about the error associated with the invalid
* option using user callback. Callback prototype is same with fprintf. It helps
* in displaying the error of the current context.
*
* @param context Pointer to the context from which the option was fetched.
* @param printer The printer callback function. For example fprintf.
* @param printer_ctx The parameter for printer callback. For example fprintf
* could use parameter stderr.
*/
CAG_PUBLIC void cag_option_printer_error(const cag_option_context *context,
cag_printer printer, void *printer_ctx);

/**
* @brief Prints all options to the terminal.
Expand All @@ -209,8 +232,27 @@ CAG_PUBLIC void cag_option_print_error(const cag_option_context *context,
* @param option_count The option count which will be printed.
* @param destination The destination where the output will be printed.
*/
#ifndef CAG_NO_FILE
CAG_PUBLIC void cag_option_print(const cag_option *options, size_t option_count,
FILE *destination);
#endif

/**
* @brief Prints all options using user callback.
*
* This function prints all options using user callback. This can be used to
* generate the output for a "--help" option.
* Using user callback is useful in tiny system without FILE support
*
* @param options The options which will be printed.
* @param option_count The option count which will be printed.
* @param destination The destination where the output will be printed.
* @param printer The printer callback function. For example fprintf.
* @param printer_ctx The parameter for printer callback. For example fprintf
* could use parameter stderr.
*/
CAG_PUBLIC void cag_option_printer(const cag_option *options,
size_t option_count, cag_printer printer, void *printer_ctx);

CAG_DEPRECATED(
"cag_option_prepare has been deprecated. Use cag_option_init instead.")
Expand Down
61 changes: 38 additions & 23 deletions src/cargs.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,39 @@
#define CAG_OPTION_PRINT_MIN_INDENTION 20

static void cag_option_print_value(const cag_option *option,
size_t *accessor_length, FILE *destination)
size_t *accessor_length, cag_printer printer, void *printer_ctx)
{
if (option->value_name != NULL) {
*accessor_length += fprintf(destination, "=%s", option->value_name);
*accessor_length += printer(printer_ctx, "=%s", option->value_name);
}
}

static void cag_option_print_letters(const cag_option *option, bool *first,
size_t *accessor_length, FILE *destination)
size_t *accessor_length, cag_printer printer, void *printer_ctx)
{
const char *access_letter;
access_letter = option->access_letters;
if (access_letter != NULL) {
while (*access_letter) {
if (*first) {
*accessor_length += fprintf(destination, "-%c", *access_letter);
*accessor_length += printer(printer_ctx, "-%c", *access_letter);
*first = false;
} else {
*accessor_length += fprintf(destination, ", -%c", *access_letter);
*accessor_length += printer(printer_ctx, ", -%c", *access_letter);
}
++access_letter;
}
}
}

static void cag_option_print_name(const cag_option *option, bool *first,
size_t *accessor_length, FILE *destination)
size_t *accessor_length, cag_printer printer, void *printer_ctx)
{
if (option->access_name != NULL) {
if (*first) {
*accessor_length += fprintf(destination, "--%s", option->access_name);
*accessor_length += printer(printer_ctx, "--%s", option->access_name);
} else {
*accessor_length += fprintf(destination, ", --%s", option->access_name);
*accessor_length += printer(printer_ctx, ", --%s", option->access_name);
}
}
}
Expand Down Expand Up @@ -443,8 +443,8 @@ CAG_PUBLIC char cag_option_get_error_letter(const cag_option_context *context)
return context->error_letter;
}

CAG_PUBLIC void cag_option_print_error(const cag_option_context *context,
FILE *destination)
CAG_PUBLIC void cag_option_printer_error(const cag_option_context *context,
cag_printer printer, void *printer_ctx)
{
int error_index;
char error_letter;
Expand All @@ -456,15 +456,31 @@ CAG_PUBLIC void cag_option_print_error(const cag_option_context *context,

error_letter = cag_option_get_error_letter(context);
if (error_letter) {
fprintf(destination, "Unknown option '%c' in '%s'.\n", error_letter,
printer(printer_ctx, "Unknown option '%c' in '%s'.\n", error_letter,
context->argv[error_index]);
} else {
fprintf(destination, "Unknown option '%s'.\n", context->argv[error_index]);
printer(printer_ctx, "Unknown option '%s'.\n", context->argv[error_index]);
}
}

void cag_option_print(const cag_option *options, size_t option_count,
FILE *destination)
#ifndef CAG_NO_FILE
CAG_PUBLIC void cag_option_print_error(const cag_option_context* context,
FILE* destination)
{
cag_option_printer_error(context, (cag_printer)fprintf, destination);
}
#endif

#ifndef CAG_NO_FILE
void cag_option_print(const cag_option* options, size_t option_count,
FILE* destination)
{
cag_option_printer(options, option_count, (cag_printer)fprintf, destination);
}
#endif

CAG_PUBLIC void cag_option_printer(const cag_option *options,
size_t option_count, cag_printer printer, void *printer_ctx)
{
size_t option_index, indention, i, accessor_length;
const cag_option *option;
Expand All @@ -477,20 +493,19 @@ void cag_option_print(const cag_option *options, size_t option_count,
accessor_length = 0;
first = true;

fputs(" ", destination);
printer(printer_ctx, " ");

cag_option_print_letters(option, &first, &accessor_length, destination);
cag_option_print_name(option, &first, &accessor_length, destination);
cag_option_print_value(option, &accessor_length, destination);
cag_option_print_letters(option, &first, &accessor_length, printer,
printer_ctx);
cag_option_print_name(option, &first, &accessor_length, printer,
printer_ctx);
cag_option_print_value(option, &accessor_length, printer, printer_ctx);

for (i = accessor_length; i < indention; ++i) {
fputs(" ", destination);
printer(printer_ctx, " ");
}

fputs(" ", destination);
fputs(option->description, destination);

fprintf(destination, "\n");
printer(printer_ctx, " %s\n", option->description);
}
}

Expand Down
65 changes: 62 additions & 3 deletions test/option_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,7 @@ int option_boundaries_mix(void)

int option_print(void)
{
#ifndef CAG_NO_FILE
char buf[255];
const char *expected;
FILE *test_file;
Expand Down Expand Up @@ -824,14 +825,67 @@ int option_print(void)
fclose(test_file);
err_open:
return EXIT_FAILURE;

#else // #ifndef CAG_NO_FILE
return EXIT_SUCCESS;
#endif
}

#ifdef CAG_NO_FILE
typedef void FILE_CTX;
#else
typedef FILE FILE_CTX;
#endif

int option_printer(void)
{
char buf[255];
const char *expected;
FILE_CTX *test_file;

expected = " -s Simple flag\n"
" -a Another simple flag\n"
" -m, -M, -o, -O Multiple access letters\n"
" --long Long parameter name\n"
" -k, --key=VALUE Parameter value\n";

test_file = tmpfile();
if (test_file == NULL) {
goto err_open;
}

cag_option_printer(options, CAG_ARRAY_SIZE(options), (cag_printer)fprintf,
test_file);
if (fseek(test_file, 0, SEEK_SET) != 0) {
goto err_seek;
}

if (fread(buf, sizeof(buf), 1, test_file) != 1 && feof(test_file) == 0) {
goto err_read;
}

if (memcmp(buf, expected, strlen(expected)) != 0) {
goto err_test;
}

fclose(test_file);
return EXIT_SUCCESS;

err_test:
err_read:
err_seek:
fclose(test_file);
err_open:
return EXIT_FAILURE;

}

int option_error_print_short(void)
{
int status;
char buf[255];
const char *expected;
FILE *test_file;
FILE_CTX *test_file;

expected = "Unknown option 'u' in '-abu'.\n";

Expand All @@ -850,7 +904,12 @@ int option_error_print_short(void)
context.error_index = 1;
context.error_letter = 'u';

#ifdef CAG_NO_FILE
cag_option_printer_error(&context, (cag_printer)fprintf, test_file);
#else
cag_option_print_error(&context, test_file);
#endif

if (fseek(test_file, 0, SEEK_SET) != 0) {
goto err_seek;
}
Expand Down Expand Up @@ -882,7 +941,7 @@ int option_error_print_long(void)
int status;
char buf[255];
const char *expected;
FILE *test_file;
FILE_CTX *test_file;

expected = "Unknown option '--unknown'.\n";

Expand All @@ -901,7 +960,7 @@ int option_error_print_long(void)
context.error_index = 1;
context.error_letter = 0;

cag_option_print_error(&context, test_file);
cag_option_printer_error(&context, (cag_printer)fprintf, test_file);
if (fseek(test_file, 0, SEEK_SET) != 0) {
goto err_seek;
}
Expand Down

0 comments on commit 50e713b

Please sign in to comment.