diff options
| author | Filip Wandzio <contact@philw.dev> | 2026-03-01 17:45:00 +0100 |
|---|---|---|
| committer | Filip Wandzio <contact@philw.dev> | 2026-03-01 17:45:00 +0100 |
| commit | 9e9c1b21569faeabd33716e4153a881e2eed7134 (patch) | |
| tree | f3a7ad21aed4b1c4f51c3ee308ffef88430deafc /src/utils.c | |
| parent | 57b077a4788b7fb5ed6add1df4ba3f15c9e6349b (diff) | |
| download | ysnp-master.tar.gz ysnp-master.zip | |
Signed-off-by: Filip Wandzio <contact@philw.dev>
Diffstat (limited to '')
| -rw-r--r-- | src/utils.c | 125 |
1 files changed, 104 insertions, 21 deletions
diff --git a/src/utils.c b/src/utils.c index c139929..2e26e8e 100644 --- a/src/utils.c +++ b/src/utils.c | |||
| @@ -1,34 +1,117 @@ | |||
| 1 | #include "utils.h" | 1 | #include "utils.h" |
| 2 | #include <stdbool.h> | ||
| 2 | #include <stdio.h> | 3 | #include <stdio.h> |
| 3 | #include <stdlib.h> | 4 | #include <stdlib.h> |
| 4 | #include <time.h> | 5 | #include <time.h> |
| 5 | 6 | ||
| 6 | void print_line(void) { | 7 | #define SEPARATOR_CHAR '-' |
| 7 | printf("--------------------------------------------------\n"); | 8 | #define SEPARATOR_WIDTH 50 |
| 9 | #define NEWLINE_CHAR '\n' | ||
| 10 | #define INPUT_BUFFER_SIZE 16 | ||
| 11 | #define TIME_FORMAT_STRING "%Y-%m-%d %H:%M:%S" | ||
| 12 | #define ASK_YES_NO_PROMPT "Please enter 'y' or 'n': " | ||
| 13 | |||
| 14 | /** Clear leftover input from stdin */ | ||
| 15 | static void flush_stdin(void) | ||
| 16 | { | ||
| 17 | int input_char; | ||
| 18 | do | ||
| 19 | input_char = getchar(); | ||
| 20 | while (input_char != NEWLINE_CHAR && input_char != EOF); | ||
| 21 | } | ||
| 22 | |||
| 23 | /** Print a horizontal separator line for the quiz UI */ | ||
| 24 | static void print_separator_line(void) | ||
| 25 | { | ||
| 26 | for (size_t current_column = 0; current_column < SEPARATOR_WIDTH; | ||
| 27 | ++current_column) | ||
| 28 | putchar(SEPARATOR_CHAR); | ||
| 29 | |||
| 30 | putchar(NEWLINE_CHAR); | ||
| 8 | } | 31 | } |
| 9 | 32 | ||
| 10 | void wait_enter(const char *msg) { | 33 | /** Print a horizontal separator line */ |
| 11 | if (msg) | 34 | void print_line(void) |
| 12 | printf("%s", msg); | 35 | { |
| 13 | int c; | 36 | print_separator_line(); |
| 14 | while ((c = getchar()) != '\n' && c != EOF) | ||
| 15 | ; | ||
| 16 | } | 37 | } |
| 17 | 38 | ||
| 18 | char ask_yes_no(const char *msg) { | 39 | /** |
| 19 | char c = 'n'; | 40 | * Pause until the user presses ENTER |
| 20 | printf("%s", msg); | 41 | * |
| 21 | if (scanf(" %c", &c) != 1) | 42 | * @param prompt Optional message to display before waiting |
| 22 | c = 'n'; | 43 | */ |
| 23 | int flush; | 44 | void wait_for_enter(const char* prompt) |
| 24 | while ((flush = getchar()) != '\n' && flush != EOF) | 45 | { |
| 25 | ; | 46 | if (prompt != NULL) |
| 26 | return c; | 47 | fputs(prompt, stdout); |
| 48 | |||
| 49 | flush_stdin(); | ||
| 50 | } | ||
| 51 | |||
| 52 | /** | ||
| 53 | * Ask a yes/no question and return a boolean answer | ||
| 54 | * | ||
| 55 | * @param prompt Question message to display | ||
| 56 | * @return true if user answered 'y' or 'Y', false if 'n', default false | ||
| 57 | */ | ||
| 58 | bool ask_yes_no(const char* prompt) | ||
| 59 | { | ||
| 60 | char input_line[INPUT_BUFFER_SIZE]; | ||
| 61 | |||
| 62 | while (1) { | ||
| 63 | if (prompt != NULL) | ||
| 64 | fputs(prompt, stdout); | ||
| 65 | |||
| 66 | if (fgets(input_line, sizeof(input_line), stdin) == NULL) | ||
| 67 | return false; | ||
| 68 | |||
| 69 | const char first_char = input_line[0]; | ||
| 70 | |||
| 71 | if (first_char == 'y' || first_char == 'Y') | ||
| 72 | return true; | ||
| 73 | |||
| 74 | if (first_char == 'n' || first_char == 'N') | ||
| 75 | return false; | ||
| 76 | |||
| 77 | fputs(ASK_YES_NO_PROMPT, stdout); | ||
| 78 | } | ||
| 27 | } | 79 | } |
| 28 | 80 | ||
| 29 | void now_str(char *buf, size_t size) { | 81 | /** |
| 30 | time_t t = time(NULL); | 82 | * Get the current timestamp for recording when a question was answered |
| 31 | strftime(buf, size, "%Y-%m-%d %H:%M:%S", localtime(&t)); | 83 | * |
| 84 | * @param answer_time_buffer Buffer to store the formatted timestamp | ||
| 85 | * @param buffer_capacity Size of the buffer in bytes | ||
| 86 | */ | ||
| 87 | void get_answer_timestamp(char* answer_time_buffer, | ||
| 88 | const size_t buffer_capacity) | ||
| 89 | { | ||
| 90 | if (answer_time_buffer == NULL || buffer_capacity == 0) | ||
| 91 | return; | ||
| 92 | |||
| 93 | const time_t current_time = time(NULL); | ||
| 94 | const struct tm* local_time = localtime(¤t_time); | ||
| 95 | if (local_time != NULL) | ||
| 96 | strftime(answer_time_buffer, | ||
| 97 | buffer_capacity, | ||
| 98 | TIME_FORMAT_STRING, | ||
| 99 | local_time); | ||
| 32 | } | 100 | } |
| 33 | 101 | ||
| 34 | size_t rand_index(size_t max) { return max ? (size_t)(rand() % max) : 0; } | 102 | /** |
| 103 | * Get a random question index in the quiz (thread-safe) | ||
| 104 | * | ||
| 105 | * @param seed Pointer to an unsigned int seed (per-thread) | ||
| 106 | * @param total_questions Number of questions in the current quiz session | ||
| 107 | * @return Random index in the range [0, total_questions-1] | ||
| 108 | */ | ||
| 109 | size_t get_random_question_index(unsigned int* seed, | ||
| 110 | const size_t total_questions) | ||
| 111 | { | ||
| 112 | if (total_questions == 0 || seed == NULL) | ||
| 113 | return 0; | ||
| 114 | |||
| 115 | const unsigned long random_value = (unsigned long)rand_r(seed); | ||
| 116 | return random_value % total_questions; | ||
| 117 | } | ||
