aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClecio Jung2023-03-18 20:43:43 -0300
committerClecio Jung2023-03-18 20:43:43 -0300
commit0113ec2c4358a5a6f4ad473372a63d2aa3c249c0 (patch)
tree18c1faa2ccebd38ca28511597cdbf031fc69e16b
parenta3942b3241189a003414874a96732e373e63ccae (diff)
downloadlibini-0113ec2c4358a5a6f4ad473372a63d2aa3c249c0.tar.xz
libini-0113ec2c4358a5a6f4ad473372a63d2aa3c249c0.tar.zst
Improve parsing
-rw-r--r--README.md12
-rw-r--r--examples/cfg.ini56
-rw-r--r--examples/incorrect.ini5
-rw-r--r--ini_file.c75
-rw-r--r--ini_file.h11
5 files changed, 117 insertions, 42 deletions
diff --git a/README.md b/README.md
index 76abf6d..220d1c1 100644
--- a/README.md
+++ b/README.md
@@ -31,18 +31,30 @@ This library provides a set of functions for reading and writing INI files. Here
int main(const int argc, const char **const argv) {
struct Ini_File *ini_file;
+
+ // Check that the user provided an argument specifying the INI file name
if (argc < 2) {
fprintf(stderr, "Usage: %s ini_file_name\n", argv[0]);
return EXIT_FAILURE;
}
+
+ // Parse the INI file and store the resulting data structure in a variable
ini_file = ini_file_parse(argv[1], NULL);
+
+ // Check that the INI file was parsed successfully
if (ini_file == NULL) {
fprintf(stderr, "Was not possible to parse the ini_file \"%s\"\n", argv[1]);
return EXIT_FAILURE;
}
+
+ // Print the properties from the INI file to the console
printf("\nThe properties retrieved from the the ini file \"%s\" are:\n\n", argv[1]);
ini_file_print_to(ini_file, stdout);
+
+ // Free the memory used by the INI file data structure
ini_file_free(ini_file);
+
+ // Return 0 to indicate success
return EXIT_SUCCESS;
}
```
diff --git a/examples/cfg.ini b/examples/cfg.ini
new file mode 100644
index 0000000..ae0ae42
--- /dev/null
+++ b/examples/cfg.ini
@@ -0,0 +1,56 @@
+[InitialConfiguration]
+;Optional, display language can only be one of the installed language
+Language=en-us
+;Optional, The name of a script that runs after setupComplete.cmd but before the initial configuration begins.
+;Optional
+Locale=en-us
+;Optional
+Country=US
+;Optional
+Keyboard=0409:00000409
+AcceptEula=true
+;This is only required on a server where an OEM EULA has been specified
+;by using the OOBE.xml file
+AcceptOEMEula=true
+;Optional. Example: My Company Name
+CompanyName=EnterCompanyName
+ServerName=EnterServerName
+; Example: CONTOSO
+NetbiosName=EnterNetbiosDomainName
+; Example: contoso.local
+DNSName=EnterDNSDomain
+; Used to set the user name for the domain admin
+UserName=EnterDomainAdminUserName
+;The password has to be strong and at least 8 characters
+PlainTextPassword=EnterAdminPassword
+;. Used to set the user name for the domain standard user account. Ignored in migration mode.
+StdUserName=EnterDomainStandardUserName
+;. The password for the domain standard user account has to be strong and at least 8 characters
+StdUserPlainTextPassword=EnterStandardUserPassword
+;Controls the Watson and automatic update settings
+Settings=All or Updates or None
+WebDomainName=www.abc.com
+TrustedCertFileName=c:\cert\a.pfx
+TrustedCertPassword=Enteryourpassword
+EnableVPN=true
+EnableRWA=true
+IPv4DNSForwarder=<IPV4Address,IPV4Address,¦>
+IPv6DNSForwarder=<IPV6Address,IPV6Address,¦>
+VpnIPv4StartAddress=<IPV4Address>
+VpnIPv4EndAddress=<IPV4Address>
+VpnBaseIPv6Address=<IPV6Address>
+VpnIPv6PrefixLength=<number>
+;All these section are optional.
+ [PostOSInstall]
+;Optional, The name of a script that runs after setupComplete.cmd but before the initial configuration begins.
+
+IsHosted=true
+StaticIPv4Address=<IPV4Address>
+StaticIPv4Gateway=<IPV4Address>
+StaticIPv4SubnetMask=<IPV4SubnetMask>
+StaticIPv6Address=<IPV6Address>
+StaticIPv6SubnetPrefixLength=<number>
+StaticIPv6Gateway=<IPV6Address>
+ClientBackupOn=true
+FileHistoryOn=true
+LaunchPadHiddenTasks=<Microsoft.LaunchPad.AdminDashboard,Microsoft.LaunchPad.Backup> \ No newline at end of file
diff --git a/examples/incorrect.ini b/examples/incorrect.ini
index 317ce8a..42dd5f2 100644
--- a/examples/incorrect.ini
+++ b/examples/incorrect.ini
@@ -10,6 +10,11 @@ a = 1
test=2
beta=96
+# We allow for spaces and special characters inside values, but not on keys
+erro r = 56
+correct = al bel gam
+
+# Section names can have special characters and spaces
[ section 2 ]
test2 = 65
a = ; 15
diff --git a/ini_file.c b/ini_file.c
index f844ec6..8d7126c 100644
--- a/ini_file.c
+++ b/ini_file.c
@@ -27,9 +27,9 @@ size_t get_file_size(FILE *const file) {
}
/* Remember to free the memory allocated for the returned string */
-char *get_content_from_file(const char *const file_name) {
+char *get_content_from_file(const char *const filename) {
char *buffer = NULL;
- FILE *const file = fopen(file_name, "rb");
+ FILE *const file = fopen(filename, "rb");
if (file != NULL) {
const size_t file_size = get_file_size(file);
/* Allocate memory to store the entire file */
@@ -101,12 +101,19 @@ void ini_section_print_to(const struct Ini_Section *const ini_section, FILE *con
}
void ini_file_print_to(const struct Ini_File *const ini_file, FILE *const sink) {
- size_t section_index;
+ size_t section_index, property_index;
if (ini_file == NULL) {
return;
}
for (section_index = 0; section_index < ini_file->sections_size; section_index++) {
- ini_section_print_to(&ini_file->sections[section_index], sink);
+ if (section_index != 0) {
+ fprintf(sink, "[%s]\n", ini_file->sections[section_index].name);
+ }
+ for (property_index = 0; property_index < ini_file->sections[section_index].properties_size; property_index++) {
+ fprintf(sink, "%s = %s\n",
+ ini_file->sections[section_index].properties[property_index].key,
+ ini_file->sections[section_index].properties[property_index].value);
+ }
putchar('\n');
}
}
@@ -119,7 +126,9 @@ char *ini_file_error_to_string(const enum Ini_File_Errors error) {
"Couldn't open file",
"Expected closing square bracket ']'",
"Expected equals sign '='",
- "Expected a value, but found a comment",
+ "A section name was not provided",
+ "A key was not provided",
+ "A value was not provided",
"Didn't found the requested section",
"Didn't found the requested property",
"The requested property is not a valid integer number",
@@ -166,10 +175,7 @@ static int ini_file_parse_handle_error(Ini_File_Error_Callback callback, const c
}
/* TODO: Check for repeated section and key names? */
-/* TODO: Check for spaces inside of section and key names? */
/* TODO: Sort the sections and keys? This would allow us to use binary search */
-/* TODO: we could allow keys and values to be strings delimited by "" or '',
- * which would allow us to use the characters "=,#,;" inside keys and values */
/* Remember to free the memory allocated for the returned ini file structure */
struct Ini_File *ini_file_parse(const char *const filename, Ini_File_Error_Callback callback) {
@@ -204,14 +210,14 @@ struct Ini_File *ini_file_parse(const char *const filename, Ini_File_Error_Callb
cursor++;
advance_white_spaces(&cursor);
name = cursor;
- advance_string_until(&cursor, "]#;\n");
+ advance_string_until(&cursor, "]#;\r\n");
if (*cursor != ']') {
if (ini_file_parse_handle_error(callback, filename, line_number, line, ini_expected_clocing_bracket) != 0) {
goto ini_file_parse_error;
}
continue;
}
- /* Compute length of the key string and remove trailing whitespaces */
+ /* Compute length of the name string and remove trailing whitespaces */
name_len = (size_t)(cursor - name);
while ((name_len > 0) && (isspace((unsigned char)name[name_len - 1]))) {
name_len--;
@@ -226,35 +232,20 @@ struct Ini_File *ini_file_parse(const char *const filename, Ini_File_Error_Callb
continue;
}
key = cursor;
- advance_string_until(&cursor, "=#;\n");
+ advance_string_until(&cursor, "=#; \t\r\n");
+ /* Compute length of the string name */
+ key_len = (size_t)(cursor - key);
+ advance_white_spaces(&cursor);
if (*cursor != '=') {
- /* Found an error, so we report it to the user using the callback provided.
- * In case of an error, if the callback returns an integer different from zero,
- * we end the parsing and return NULL. */
if (ini_file_parse_handle_error(callback, filename, line_number, line, ini_expected_equals) != 0) {
goto ini_file_parse_error;
}
continue;
}
- /* Compute length of the key string and remove trailing whitespaces */
- key_len = (size_t)(cursor - key);
- while ((key_len > 0) && (isspace((unsigned char)key[key_len - 1]))) {
- key_len--;
- }
cursor++;
advance_white_spaces(&cursor);
- /* If it is a commment, we found an error */
- if (strchr("#;", *cursor) != NULL) {
- /* Found an error, so we report it to the user using the callback provided.
- * In case of an error, if the callback returns an integer different from zero,
- * we end the parsing and return NULL. */
- if (ini_file_parse_handle_error(callback, filename, line_number, line, ini_expected_value_got_comment) != 0) {
- goto ini_file_parse_error;
- }
- continue;
- }
value = cursor;
- advance_string_until(&cursor, "#;\n");
+ advance_string_until(&cursor, "#;\r\n");
/* Compute length of the value string and remove trailing whitespaces */
value_len = (size_t)(cursor - value);
while ((value_len > 0) && (isspace((unsigned char)value[value_len - 1]))) {
@@ -277,11 +268,11 @@ ini_file_parse_error:
enum Ini_File_Errors ini_file_add_section_sized(struct Ini_File *const ini_file, const char *const name, const size_t name_len) {
struct Ini_Section *section;
- if ((ini_file == NULL) || (name == NULL)) {
+ if (ini_file == NULL) {
return ini_invalid_parameters;
}
- if (name_len == 0) {
- return ini_invalid_parameters;
+ if ((name == NULL) || (name_len == 0)) {
+ return ini_section_not_provided;
}
if ((ini_file->sections_size + 1) >= ini_file->sections_capacity) {
const size_t new_cap = 2 * ini_file->sections_capacity;
@@ -310,7 +301,7 @@ enum Ini_File_Errors ini_file_add_section_sized(struct Ini_File *const ini_file,
enum Ini_File_Errors ini_file_add_section(struct Ini_File *const ini_file, const char *const name) {
if (name == NULL) {
- return ini_invalid_parameters;
+ return ini_section_not_provided;
}
return ini_file_add_section_sized(ini_file, name, strlen(name));
}
@@ -318,11 +309,14 @@ enum Ini_File_Errors ini_file_add_section(struct Ini_File *const ini_file, const
enum Ini_File_Errors ini_file_add_property_sized(struct Ini_File *const ini_file, const char *const key, const size_t key_len, const char *const value, const size_t value_len) {
struct Ini_Section *section;
struct Key_Value_Pair *property;
- if ((ini_file == NULL) || (key == NULL) || (value == NULL)) {
+ if (ini_file == NULL) {
return ini_invalid_parameters;
}
- if ((key_len == 0) || (value_len == 0)) {
- return ini_invalid_parameters;
+ if ((key == NULL) || (key_len == 0)) {
+ return ini_key_not_provided;
+ }
+ if ((value == NULL) || (value_len == 0)) {
+ return ini_value_not_provided;
}
if (ini_file->sections_size == 0) {
return ini_allocation;
@@ -353,8 +347,11 @@ enum Ini_File_Errors ini_file_add_property_sized(struct Ini_File *const ini_file
}
enum Ini_File_Errors ini_file_add_property(struct Ini_File *const ini_file, const char *const key, const char *const value) {
- if ((key == NULL) || (value == NULL)) {
- return ini_invalid_parameters;
+ if (key == NULL) {
+ return ini_key_not_provided;
+ }
+ if (value == NULL) {
+ return ini_value_not_provided;
}
return ini_file_add_property_sized(ini_file, key, strlen(key), value, strlen(value));
}
diff --git a/ini_file.h b/ini_file.h
index 1790254..750f218 100644
--- a/ini_file.h
+++ b/ini_file.h
@@ -54,7 +54,9 @@ enum Ini_File_Errors {
ini_couldnt_open_file,
ini_expected_clocing_bracket,
ini_expected_equals,
- ini_expected_value_got_comment,
+ ini_section_not_provided,
+ ini_key_not_provided,
+ ini_value_not_provided,
ini_no_such_section,
ini_no_such_property,
ini_not_integer,
@@ -70,7 +72,7 @@ typedef int (*Ini_File_Error_Callback)(const char *const filename, const size_t
size_t get_file_size(FILE *const file);
/* Remember to free the memory allocated for the returned string */
-char *get_content_from_file(const char *const file_name);
+char *get_content_from_file(const char *const filename);
struct Ini_File *ini_file_new(void);
void ini_file_free(struct Ini_File *const ini_file);
@@ -81,12 +83,15 @@ char *ini_file_error_to_string(const enum Ini_File_Errors error);
/* Remember to free the memory allocated for the returned ini file structure */
struct Ini_File *ini_file_parse(const char *const filename, Ini_File_Error_Callback callback);
-/* These functions returns (ini_no_error = 0) if everything worked correctly */
+/* These functions returns ini_no_error = 0 if everything worked correctly */
enum Ini_File_Errors ini_file_add_section_sized(struct Ini_File *const ini_file, const char *const name, const size_t name_len);
enum Ini_File_Errors ini_file_add_section(struct Ini_File *const ini_file, const char *const name);
enum Ini_File_Errors ini_file_add_property_sized(struct Ini_File *const ini_file, const char *const key, const size_t key_len, const char *const value, const size_t value_len);
enum Ini_File_Errors ini_file_add_property(struct Ini_File *const ini_file, const char *const key, const char *const value);
enum Ini_File_Errors ini_file_save(const struct Ini_File *const ini_file, const char *const filename);
+
+/* These functions use sequential search algorithm to find the requested section and properties */
+/* These functions returns ini_no_error = 0 if everything worked correctly */
enum Ini_File_Errors ini_file_find_section(struct Ini_File *const ini_file, const char *const section, struct Ini_Section **ini_section);
enum Ini_File_Errors ini_file_find_property(struct Ini_File *const ini_file, const char *const section, const char *const key, char **value);
enum Ini_File_Errors ini_file_find_integer(struct Ini_File *const ini_file, const char *const section, const char *const key, long *integer);