Use case-insensitive variant of `strcmp`.

pull/9/head
Joshua Potter 2023-11-25 15:47:47 -07:00
parent 6f77c400e9
commit 0fc476ceed
7 changed files with 70 additions and 6 deletions

View File

@ -5,9 +5,7 @@ CLI utility for defining custom project initialization scripts.
TODO: TODO:
- [ ] Add evaluator tests. - [ ] Add evaluator tests.
- [ ] Color output to console. - [ ] Color output to console.
- [ ] string -> line, case insensitive
- [ ] Ensure keys are alphanumeric, underscore - [ ] Ensure keys are alphanumeric, underscore
- [ ] sophie
## Overview ## Overview

View File

@ -23,4 +23,23 @@ This function assumes a forward slash path separator (i.e. `/`).
*/ */
char *join(size_t n, const char *segments[static n], char sep); char *join(size_t n, const char *segments[static n], char sep);
/**
@brief Perform case insensitive string comparison.
This function operates like `strcmp` except comparison ignores case.
@param s1
C string to be compared.
@param s2
C string to be compared.
@return
An integral value indicating the relationship between the strings:
- `< 0`: the first character (converted uppercase) that does not match has a
lower value in @p s1 than in @p s2.
- `= 0`: the contents of both strings are equal.
- `> 0`: the first character (converted uppercase) that does not match has a
greater value in @p s1 than in @p s2.
*/
int strcmp_ci(const char *s1, const char *s2);
#endif /* _BOOTSTRAP_STRING_UTILS_H */ #endif /* _BOOTSTRAP_STRING_UTILS_H */

View File

@ -1,6 +1,8 @@
#include "string_utils.h" #include "string_utils.h"
#include <assert.h> #include <assert.h>
#include <ctype.h>
#include <math.h>
#include <string.h> #include <string.h>
char *join(size_t n, const char *segments[static n], char sep) { char *join(size_t n, const char *segments[static n], char sep) {
@ -26,3 +28,20 @@ char *join(size_t n, const char *segments[static n], char sep) {
return joined; return joined;
} }
int strcmp_ci(const char *s1, const char *s2) {
int i = 0;
size_t s1_len = strlen(s1);
size_t s2_len = strlen(s2);
for (; i < (s1_len < s2_len ? s1_len : s2_len); ++i) {
if (toupper(s1[i]) < toupper(s2[i])) {
return -1;
} else if (toupper(s1[i]) > toupper(s2[i])) {
return 1;
}
}
if (s1_len == s2_len) {
return 0;
}
return s1_len < s2_len ? -1 : 1;
}

View File

@ -1,6 +1,6 @@
#include "validator.h" #include "validator.h"
#include <string.h> #include "string_utils.h"
static struct Error *read_field(const cJSON *const field, struct Field **out) { static struct Error *read_field(const cJSON *const field, struct Field **out) {
if (!cJSON_IsObject(field)) { if (!cJSON_IsObject(field)) {
@ -27,7 +27,7 @@ static struct Error *read_field(const cJSON *const field, struct Field **out) {
goto cleanup; goto cleanup;
} }
if (strcmp(type->valuestring, "STRING") == 0) { if (strcmp_ci(type->valuestring, "text") == 0) {
(*out)->type = FT_TEXT; (*out)->type = FT_TEXT;
} else { } else {
error = ERROR_NEW( error = ERROR_NEW(

View File

@ -26,6 +26,7 @@ int main(int argc, char *argv[]) {
sput_enter_suite("string_utils"); sput_enter_suite("string_utils");
sput_run_test(test_join_single); sput_run_test(test_join_single);
sput_run_test(test_join_multiple); sput_run_test(test_join_multiple);
sput_run_test(test_strcmp_ci);
sput_enter_suite("parser"); sput_enter_suite("parser");
sput_run_test(test_parser_missing); sput_run_test(test_parser_missing);
@ -37,6 +38,7 @@ int main(int argc, char *argv[]) {
sput_run_test(test_validator_field_not_object); sput_run_test(test_validator_field_not_object);
sput_run_test(test_validator_field_type_invalid); sput_run_test(test_validator_field_type_invalid);
sput_run_test(test_validator_field_type_unknown); sput_run_test(test_validator_field_type_unknown);
sput_run_test(test_validator_valid_type_ci);
sput_run_test(test_validator_field_prompt_invalid); sput_run_test(test_validator_field_prompt_invalid);
sput_run_test(test_validator_valid); sput_run_test(test_validator_valid);

View File

@ -18,4 +18,14 @@ static void test_join_multiple() {
free(joined); free(joined);
} }
static void test_strcmp_ci() {
const char *a1 = "aBcD";
const char *a2 = "AbCd";
sput_fail_unless(strcmp_ci(a1, a2) == 0, "strcmp_ci == 0");
const char *b1 = "aBcDe";
const char *b2 = "AbCd";
sput_fail_unless(strcmp_ci(b1, b2) > 0, "strcmp_ci > 0");
sput_fail_unless(strcmp_ci(b2, b1) < 0, "strcmp_ci < 0");
}
#endif /* _BOOTSTRAP_TEST_STRING_UTILS */ #endif /* _BOOTSTRAP_TEST_STRING_UTILS */

View File

@ -88,11 +88,27 @@ static void test_validator_field_type_unknown() {
test_validator_teardown(fixture); test_validator_teardown(fixture);
} }
static void test_validator_valid_type_ci() {
struct TestValidatorFixture *fixture = test_validator_setup(
"{"
" \"key\": {"
" \"type\": \"tExT\","
" \"prompt\": \"What value for key?\""
" }"
"}"
);
struct Error *error = validate_spec_json(fixture->parsed, &fixture->prompts);
sput_fail_unless(error == 0, "valid");
test_validator_teardown(fixture);
}
static void test_validator_field_prompt_invalid() { static void test_validator_field_prompt_invalid() {
struct TestValidatorFixture *fixture = test_validator_setup( struct TestValidatorFixture *fixture = test_validator_setup(
"{" "{"
" \"key\": {" " \"key\": {"
" \"type\": \"STRING\"," " \"type\": \"text\","
" \"prompt\": 2" " \"prompt\": 2"
" }" " }"
"}" "}"
@ -111,7 +127,7 @@ static void test_validator_valid() {
struct TestValidatorFixture *fixture = test_validator_setup( struct TestValidatorFixture *fixture = test_validator_setup(
"{" "{"
" \"key\": {" " \"key\": {"
" \"type\": \"STRING\"," " \"type\": \"text\","
" \"prompt\": \"What value for key?\"" " \"prompt\": \"What value for key?\""
" }" " }"
"}" "}"