diff options
| author | foswret | 2026-08-15 12:05:08 -0500 |
|---|---|---|
| committer | foswret | 2026-08-15 12:05:08 -0500 |
| commit | 2fd20536872d24db4fe7507ed7da22db99bb7c14 (patch) | |
| tree | 336581f5a471d6d5897c093d985fcc768881a27a | |
| parent | 5ead4f6c3d36557c56e27cf52835409d81ae6ed8 (diff) | |
MagickWand too bloated for this application. Pull in ezini dependency,
for future .ini file parsing.
| -rw-r--r-- | README.md | 4 | ||||
| -rw-r--r-- | src/calp.c | 39 | ||||
| -rw-r--r-- | src/ezini.c | 1033 | ||||
| -rw-r--r-- | src/ezini.h | 121 |
4 files changed, 1182 insertions, 15 deletions
@@ -4,7 +4,6 @@ calp: generate reproduceable, printable calendars from the terminal. - gcc - pkgconf - pangocairo -- MagickWand # Install ```shell @@ -32,6 +31,3 @@ $ LANG=nl_NL.utf8 calp - Sunrise/sunset calcuations - Moon phase calculations - Switch between US day-ordering convention and ISO 8601 (first day = Monday) - -# Credits -- ezini library by Michael Dipperstein @@ -32,20 +32,30 @@ int main (void) { int num_of_months = 12; - char *months[num_of_months]; extern int days_in_month[12]; + // Locale Setting + // -------------- + char *week_names[7]; + char *months[num_of_months]; - //const nl_item nl_abmons[12] = {ABMON_1, ABMON_2, ABMON_3, ABMON_4, - // ABMON_5, ABMON_6, ABMON_7, ABMON_8, - // ABMON_9, ABMON_10, ABMON_11, ABMON_12}; - const nl_item locale_months[12] = {MON_1, MON_2, MON_3, MON_4, MON_5, MON_6, - MON_7, MON_8, MON_9, MON_10, MON_11, MON_12}; + nl_item locale_months[12] = - const nl_item locale_days[7] = {ABDAY_1, ABDAY_2, ABDAY_3, ABDAY_4, + nl_item locale_days[7] = {ABDAY_1, ABDAY_2, ABDAY_3, ABDAY_4, ABDAY_5, ABDAY_6, ABDAY_7}; + void generate_locale_months(nl_item *months[12]) { + locale_months = {MON_1, MON_2, MON_3, MON_4, MON_5, MON_6, + MON_7, MON_8, MON_9, MON_10, MON_11, MON_12}; + + setlocale(LC_ALL, ""); + + for (int i = 0; i < 12; i++) { + months[i] = nl_langinfo(locale_months[i]); + } + } + setlocale(LC_ALL, ""); for (int i = 0; i < 12; i++) { @@ -85,7 +95,6 @@ int main (void) { - // TBD: Add ability to choose filename // Defaults to the year being generated char filename[20]; snprintf(filename, sizeof(filename), "%d.pdf", year); @@ -98,6 +107,9 @@ int main (void) { + // Extent Declaration + // ------------------ + // Height and width of the paper // .x and .y are empty struct extents paper_extents = { @@ -131,6 +143,8 @@ int main (void) { }; + + // Position and width of the header // Height is set dynamically, based on the size of // the month grid @@ -142,12 +156,13 @@ int main (void) { // Program fails if the PDF backend isn't available - if (!CAIRO_HAS_PDF_SURFACE) { + if (CAIRO_HAS_PDF_SURFACE == false) { printf("Error: PDF backend not available.\n"); return 1; } - // Main surface and context are created + // Main surface and context are created, with the size being + // the paper cairo_surface_t *surface = cairo_pdf_surface_create( filename, @@ -183,12 +198,13 @@ int main (void) { // Month generation loop for (int i = 0; i < num_of_months; i++) { - // Initialize month struct + // Initialize month struct, setting values month.first_day = day_of_week(1, i + 1, year); month.num_of_days = days_in_month[i]; month.month_position = i; month.minimum_rows = calculate_minimum_rows(&month); + // Arbitrarily setting the distance between the month grid and the header double month_header_spacing = margin * 3.0; header_extents.height = paper_extents.height - month_header_spacing - ((month.minimum_rows + 1) * day_extents.height); @@ -207,6 +223,7 @@ int main (void) { + // Fill header with blue color for visibility set_color(ch, BLUE); fill_bg(ch); diff --git a/src/ezini.c b/src/ezini.c new file mode 100644 index 0000000..ee1f79e --- /dev/null +++ b/src/ezini.c @@ -0,0 +1,1033 @@ +/** + * \brief INI File handling library + * \file ezini.c + * \author Michael Dipperstein (mdipperstein@gmail.com) + * \date November 22, 2015 + * + * This file implements a set of library functions that maybe be used + * to create, update, and/or parse INI files. + * + * \copyright Copyright (C) 2015, 2019 by Michael Dipperstein + * (mdipperstein@gmail.com) + * + * \par + * This file is part of the ezini library. + * + * \license + * The ezini library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * \par + * The ezini library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * \par + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/** + * \defgroup library Library Code + * \brief This module contains the code for the ezini INI file handling + * library + * @{ + */ + +/*************************************************************************** +* INCLUDED FILES +***************************************************************************/ +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include "ezini.h" + +/*************************************************************************** +* TYPE DEFINITIONS +***************************************************************************/ + +/** + * \struct ini_key_list_t + * \brief A structure used for creating linked lists of key/value pairs + * for a each section. + */ + +/** + * \typedef struct ini_key_list_t + * \brief A shortcut for struct ini_key_list_t + */ + +typedef struct ini_key_list_t +{ + char *key; /*!< pointer to a a NULL terminated string + containing key name for this entry */ + char *value; /*!< pointer to a NULL terminated string + containing key value for this entry Use + ASCII strings to represent numbers */ + struct ini_key_list_t *next;/*!< pointer to the next key/value pair in + in this section */ + +} ini_key_list_t; + + +/** + * \struct ini_section_list_t + * \brief A structure used for creating linked lists of sections, each + * maintaining its own list of key/value pairs. + */ + +/** + * \typedef struct ini_section_list_t + * \brief A shortcut for struct ini_section_list_t + */ + +typedef struct ini_section_list_t +{ + char *section; /*!< pointer to a NULL terminated string + containing the section name */ + ini_key_list_t *members; /*!< pointer to the list of all key/value + pairs in this section */ + struct ini_section_list_t *next; /*!< pointer to the next section in + the list of entries */ + +} ini_section_list_t; + + +/*************************************************************************** +* PROTOTYPES +***************************************************************************/ + +/* allocate */ +static ini_key_list_t *NewKeyList(const char *key, const char *value); +static ini_section_list_t *NewSectionList(const char *section, const char *key, + const char *value); + +/* free */ +static void FreeKeyList(ini_key_list_t *list); +static void FreeEntry(ini_entry_t *entry); + +/* utilities */ +static char *SkipWS(const char *str); +static char *DupStr(const char *src); +static char *GetLine(FILE *fp); + +/*************************************************************************** +* FUNCTIONS +***************************************************************************/ + +/** + * \fn int AddEntryToList(ini_entry_list_t *list, const char *section, + * const char *key, const char *value) + * + * \brief This function adds a (section, key, value) entry to an entry list. + * + * \param list A pointer to an ini_entry_list_t that points to the + * head of entry list being modified. Pass a pointer to an ini_entry_list_t + * pointing to NULL if the list needs to be created. + * + * \param section A NULL terminated string containing the name of the + * section for the entry being added. + * + * \param key A NULL terminated string containing the name of the key for + * the entry being added. + * + * \param value A NULL terminated string containing the value of the key for + * the entry being added. All values must be represented as strings. They may + * be converted to/from strings by the calling program. + * + * \effects + * Information used to generate an entry structure containing copies of + * the (section, key, value) entry is added to the list passed as a parameter. + * Memory will be dynamically allocated as needed. + * + * \returns 0 for success, Non-zero on error. Error type is contained in + * errno. + * + * This function adds information used to create a (section, key, value) entry + * to an entry list. + * + * If an entry containing the same section name and key already exists, + * the new value will overwrite the old value. + * + * If the entry is for an existing section, it will be added to the end of the + * list for that section. + * + * If the entry is for a new section, a new section will be added to the list of + * sections, and the key/value pair will be the first entry of the section. + */ +int AddEntryToList(ini_entry_list_t *list, const char *section, + const char *key, const char *value) +{ + ini_section_list_t *next; + int result; + + /* handle empty list */ + if (NULL == *list) + { + /* add the first entry to the list */ + *list = NewSectionList(section, key, value); + + if (NULL == *list) + { + return -1; + } + + return 0; + } + + next = *list; + result = 1; + + while (1) + { + result = strcmp(section, next->section); + + if (0 == result) + { + break; /* match, insert here */ + } + + if (NULL == next->next) + { + break; /* no match, create new section here */ + } + + next = next->next; + } + + if (0 == result) + { + ini_key_list_t *member; + + member = next->members; + + while (1) + { + result = strcmp(key, member->key); + + if (0 == result) + { + break; /* match, insert here */ + } + + if (NULL == member->next) + { + break; /* no match, create new section here */ + } + + member = member->next; + } + + if (0 == result) + { + /* key exists, change value */ + free(member->value); + member->value = DupStr(value); + + if (NULL == member->value) + { + return -1; + } + } + else + { + /* new key, add to list */ + member->next = NewKeyList(key, value); + + if (NULL == member->next) + { + return -1; + } + } + } + else + { + /* add the section to the list with this key and value */ + next->next = NewSectionList(section, key, value); + } + + return 0; +} + + +/** + * \fn void FreeList(ini_entry_list_t list) + * + * \brief This function frees all of the members of an entry list. + * + * \param list A pointer to the head of an ini_entry_list_t + * + * \effects + * All of the memory allocated for all of the entries in an entry + * list will be freed. + * + * \returns Nothing + * + * This function uses recursion to step to the tail of the list and deletes + * section entries on the way back up. + */ +void FreeList(ini_entry_list_t list) +{ + /* recurse to the end of the list and free everything on the way back */ + if (list->next != NULL) + { + FreeList(list->next); + } + + if (list->section != NULL) + { + /* free the section name */ + free(list->section); + } + + if (list->members != NULL) + { + FreeKeyList(list->members); + } + + free(list); +} + + +/** + * \fn int MakeINIFile(const char *iniFile, const ini_entry_list_t list) + * + * \brief This function creates the specified INI file from the list of + * entries passed as an argument. + * + * \param iniFile The name of the INI file to be created. stdout will be + * used if iniFile is NULL. + * + * \param list A pointer to a list of that will be used to construct + * (section, key, value) entries. + * + * \effects + * The specified file is created and the (section, key, value) + * triples generated from the entry list are written to the file. If the + * specified file already exists, it will be overwritten. + * + * \returns 0 for success, Non-zero on error. Error type is contained in + * errno. + * + * This function creates the specified INI file from the list of entries + * passed as an argument. Any existing INI file with the same name in the + * same path will be overwritten. + */ +int MakeINIFile(const char *iniFile, const ini_entry_list_t list) +{ + ini_entry_list_t section; + ini_key_list_t *members; + FILE *fp; + + if (NULL == list) + { + errno = EINVAL; + return -1; + } + + if (NULL == iniFile) + { + fp = stdout; + } + else + { + + fp = fopen(iniFile, "w"); + + if (NULL == fp) + { + return -1; + } + } + + section = list; + + while (section != NULL) + { + fprintf(fp, "[%s]\n", section->section); + + members = section->members; + + while (members != NULL) + { + fprintf(fp, "%s = %s\n", members->key, members->value); + members = members->next; + } + + fprintf(fp, "\n"); + section = section->next; + } + + if (fp != stdout) + { + fclose(fp); + } + + return 0; +} + + +/** + * \fn int AddEntryToFile(const char *iniFile, const ini_entry_list_t list) + * + * \brief This function adds (section, key, value) entries in an entry list + * to an INI file. + * + * \param iniFile The name of the INI file to be modified. + * + * \param list A pointer to a list of entries to be added to the INI file. + * + * \effects + * The INI file will be re-written containing the results of adding + * the entries in the entry list to the entries already contained in the INI + * file. + * + * \returns 0 for success, Non-zero on error. Error type is contained in + * errno. + * + * This function adds (section, key, value) entries in an entry list to an + * INI file. Section order will be maintained with new sections added to the + * end of the INI file. If an entry containing the same section name and key + * already exists, the new value will overwrite the old value. + */ +int AddEntryToFile(const char *iniFile, const ini_entry_list_t list) +{ + ini_entry_t entry; + ini_entry_list_t merged; + ini_entry_list_t here; + int result; + FILE *fp; + + if (NULL == iniFile) + { + errno = EINVAL; + return -1; + } + + if (NULL == list) + { + errno = EINVAL; + return -1; + } + + fp = fopen(iniFile, "r"); + + if (NULL == fp) + { + return -1; + } + + merged = NULL; + entry.section = NULL; + entry.key = NULL; + entry.value = NULL; + + /* read ini file back into an entry list */ + while ((result = GetEntryFromFile(fp, &entry)) > 0) + { + AddEntryToList(&merged, entry.section, entry.key, entry.value); + } + + fclose(fp); + + if (result < 0) + { + FreeList(merged); + return -1; + } + + /* add entries passed into this function to entries from INI file */ + here = list; + + while (here != NULL) + { + ini_key_list_t *members; + + members = here->members; + + while (members != NULL) + { + AddEntryToList(&merged, here->section, members->key, + members->value); + members = members->next; + } + + here = here->next; + } + + /* re-write INI file from merged entry list */ + result = MakeINIFile(iniFile, merged); + FreeList(merged); + + return result; +} + +/** + * \fn int DeleteEntryFromFile(const char *iniFile, const char *section, + * const char *key) + * + * \brief This function deletes all entries from an INI file that match the + * section and key passed as an argument. + * + * \param iniFile The name of the INI file containing the entry to be + * deleted. + * + * \param section A pointer to a NULL terminated string containing the name + * of the section of the entry to be deleted. + * + * \param key A pointer to a NULL terminated string containing the name of + * the key of the entry to be deleted. + * + * \effects + * The INI file will be re-written without any entries that match + * the section and key to be deleted. Empty sections will not be deleted. + * + * \returns 0 for success, Non-zero on error. Error type is contained in + * errno. + * + * This function deletes all entries from an INI file that match the section + * and key passed as an argument. Empty sections will not be deleted. + * + * \note There will never be more than one matching entry in INI files + * created by this library. + */ +int DeleteEntryFromFile(const char *iniFile, const char *section, + const char *key) +{ + ini_entry_t entry; + ini_entry_list_t list; + int result; + FILE *fp; + + if (NULL == iniFile) + { + errno = EINVAL; + return -1; + } + + if (NULL == section) + { + errno = EINVAL; + return -1; + } + + if (NULL == key) + { + errno = EINVAL; + return -1; + } + + fp = fopen(iniFile, "r"); + + if (NULL == fp) + { + return -1; + } + + list = NULL; + entry.section = NULL; + entry.key = NULL; + entry.value = NULL; + + /* read ini file back into a structure */ + while ((result = GetEntryFromFile(fp, &entry)) > 0) + { + if (0 != strcmp(entry.section, section)) + { + /* this isn't one we're supposed to delete */ + AddEntryToList(&list, entry.section, entry.key, entry.value); + } + else if (0 != strcmp(entry.key, key)) + { + /* this isn't one we're supposed to delete */ + AddEntryToList(&list, entry.section, entry.key, entry.value); + } + } + + fclose(fp); + + if (result < 0) + { + FreeList(list); + return -1; + } + + result = MakeINIFile(iniFile, list); + FreeList(list); + + return result; +} + +/** + * \fn int GetEntryFromFile(FILE *iniFile, ini_entry_t *entry) + * + * \brief This function parses an INI file stream passed as an input, + * searching for the next (section, key, value) triple. + * + * \param iniFile A pointer to the INI file to be parsed. It must be + * opened for reading. + * + * \param entry A pointer to the entry structure used to store the discovered + * (section, key, value) triple. + * + * \effects + * The specified file is read until it discovers an entry. + * + * \returns 1 when an entry is found\n + * 0 when no more entries can be found\n + * -1 for an error. Error type is contained in errno. + * + * This function parses an INI file stream passed as an input, searching for + * the next (section, key, value) triple. The resulting triple will be used + * to populate the entry structure passed as a parameter. + */ +int GetEntryFromFile(FILE *iniFile, ini_entry_t *entry) +{ + char *line; + char *ptr; + + if (NULL == iniFile) + { + errno = EINVAL; + return -1; + } + + if (NULL == entry) + { + errno = EINVAL; + return -1; + } + + /* handle section names, comments, and blank lines */ + while ((line = GetLine(iniFile)) != NULL) + { + /* skip leading spaces and blank lines */ + ptr = SkipWS(line); + + /* skip blank lines and lines starting with ';' or '#' */ + if (*ptr == '\0' || *ptr == ';' || *ptr == '#') + { + free(line); + continue; + } + else if (*ptr == '[') + { + /* possible new section */ + char *end; + + end = strchr(ptr, ']'); + + if (NULL == end) + { + FreeEntry(entry); + errno = EILSEQ; + return -1; + } + + /* we have the full string for a new section, trim white space */ + ptr = SkipWS(ptr + 1); + + while (isspace(*(end - 1))) + { + end--; + } + + *end = '\0'; + + free(entry->section); + entry->section = DupStr(ptr); + free(line); + } + else + { + /* this line should be key = value */ + break; + } + } + + /* we either have a non-section line or nothing left to get */ + if (NULL == line) + { + /* nothing left to get */ + FreeEntry(entry); + return 0; + } + + /* the only other allowable lines are of the form key = value */ + free(entry->key); /* free old key */ + entry->key = ptr; + ptr++; + + while (*ptr != '=') + { + if (*ptr == '\0') + { + /* didn't find '=' */ + entry->key = NULL; + FreeEntry(entry); + free(line); + errno = EILSEQ; + return -1; + } + + ptr++; + } + + /* we found the '=' separating key and value trim white space */ + free(entry->value); /* free old value */ + entry->value = ptr + 1; + ptr--; + + while (isspace(*ptr)) + { + ptr--; + } + + *(ptr + 1) = '\0'; + entry->key = DupStr(entry->key); + + ptr = entry->value; + + /* now skip white space after '=' */ + while (*ptr == ' ' || *ptr =='\t') + { + if (*ptr == '\0') + { + FreeEntry(entry); + free(line); + errno = EILSEQ; + return -1; + } + + ptr++; + } + + /* we found the start of value, trim trailing white space */ + entry->value = ptr; + ptr = entry->value + strlen(entry->value) - 1; + + while (isspace(*ptr)) + { + ptr--; + } + + *(ptr + 1) = '\0'; + entry->value = DupStr(entry->value); + + free(line); + return 1; +} + + +/** + * \fn ini_key_list_t *NewKeyList(const char *key, const char *value) + * + * \brief This function allocates memory for a new ini_key_list_t type + * variable and populates it with the data passed as parameters + * + * \param key A pointer to a NULL terminated string containing the key name + * + * \param key A pointer to a NULL terminated string containing the value of + * this key. Use ASCII strings to represent numbers. + * + * \effects + * Memory will be allocated for a new ini_key_list_t and copies + * of the key and value strings passed as a parameter. key and value are + * copied into the appropriate fields and the next pointer is set to NULL. + * + * \returns A pointer to the ini_key_list_t item that was allocated. The + * pointer will be NULL if an error occurs. + * + * This function allocates memory for a new ini_key_list_t and copies + * of the key and value strings passed as a parameter. The next pointer + * will be set to NULL. + */ +static ini_key_list_t *NewKeyList(const char *key, const char *value) +{ + ini_key_list_t *item; + + item = (ini_key_list_t *)malloc(sizeof(ini_key_list_t)); + + if (NULL == item) + { + return NULL; + } + + /* allocation succeeded copy key and value */ + item->next = NULL; + + item->key = DupStr(key); + + if (NULL == item->key) + { + free(item); + return NULL; + } + + item->value = DupStr(value); + + if (NULL == item->value) + { + free(item->key); + free(item); + return NULL; + } + + return item; +} + + +/** + * \fn ini_section_list_t *NewSectionList(const char *section, const char *key, + * const char *value) + * + * \brief This function allocates memory for a new ini_section_list_t type + * variable and populates it with the data passed as parameters + * + * \param section A pointer to a NULL terminated string containing the section + * name + + * \param key A pointer to a NULL terminated string containing the key name + * + * \param key A pointer to a NULL terminated string containing the value of + * this key. Use ASCII strings to represent numbers. + * + * \effects + * Memory will be allocated for a new ini_section_list_t and copies + * of the section, key, value strings passed as a parameter. section is + * copied to the appropriate field and a new key list items is created fo + * the key and value strings. The next pointer will be set to NULL. + * + * \returns A pointer to the ini_section_list_t item that was allocated. The + * pointer will be NULL if an error occurs. + * + * This function allocates memory for a new ini_section_list_t and copies + * of the section, key, value strings passed as a parameter. A ini_key_list_t + * is allocated for the key and value strings. The next pointer is set to NULL. + */ +static ini_section_list_t *NewSectionList(const char *section, const char *key, + const char *value) +{ + ini_section_list_t *item; + + item = (ini_section_list_t *)malloc(sizeof(ini_section_list_t)); + + if (NULL == item) + { + return NULL; + } + + /* now populate item */ + item->next = NULL; + item->section = DupStr(section); + + if (NULL == item->section) + { + free(item); + return NULL; + } + + /* start a member list with the current key and value */ + item->members = NewKeyList(key, value); + + if (NULL == item->members) + { + free(item->section); + free(item); + return NULL; + } + + return item; +} + + +/** + * \fn void FreeKeyList(ini_key_list_t *list) + * + * \brief This function frees all of the members of a key list. + * + * \param list A pointer to the head of an ini_key_list_t + * + * \effects All of the memory allocated for all of the key/value pairs in + * a key list will be freed. + * + * \returns Nothing + * + * This function uses recursion to step to the tail of the list and deletes + * key/value entries on the way back up. + */ +static void FreeKeyList(ini_key_list_t *list) +{ + /* recurse to the end of the list and free everything on the way back */ + if (list->next != NULL) + { + FreeKeyList(list->next); + } + + free(list->key); + free(list->value); + free(list); +} + + +/** + * \fn static void FreeEntry(ini_entry_t *entry) + * + * \brief This function frees the allocated memory that is pointed to by the + * members of an ini_entry_t structure. + * + * \param entry A pointer to the entry structure containing the members + * pointing to the memory to be freed. + * + * \effects Dynamically allocated memory is freed and entry data is set + * to NULL. + * + * \returns Nothing + */ +static void FreeEntry(ini_entry_t *entry) +{ + free(entry->section); + free(entry->key); + free(entry->value); + + entry->section = NULL; + entry->key = NULL; + entry->value = NULL; +} + +/** + * \fn static char *SkipWS(const char *str) + * + * \brief This function returns a pointer to the first non-space in the + * string passed as a parameter. + * + * \param str A pointer to the string being searched. + * + * \effects None + * + * \returns A pointer to the first non-space in str. + */ +static char *SkipWS(const char *str) +{ + char *c; + + c = (char *)str; + while(isspace(*c)) + { + c++; + } + + return c; +} + +/** + * \fn static char *DupStr(const char *src) + * + * \brief This function returns a copy of the string passed as a parameter. + * + * \param str A pointer to the string being being copied. + * + * \effects Memory is dynamically allocated to hold a duplicate of the + * input string. + * + * \returns A copy of str in malloced memory is returned on success. NULL + * is returned on failure. + * + * This function returns a copy of the string passed as a parameter. The + * memory for the copy is allocated by malloc() and must be freed by the caller. + */ +static char *DupStr(const char *src) +{ + char *dest; + + if (NULL == src) + { + return NULL; + } + + dest = (char *)malloc(strlen(src) + 1); + + if (NULL != dest) + { + strcpy(dest, src); + } + + return dest; +} + +/** + * \fn static char *GetLine(FILE *fp) + * + * \brief This function returns a NULL terminated array of char containing the + * next line in the file passed as an argument. + * + * \param fp A pointer to the file being read. + * + * \effects One line is read from fp and copied into a dynamically allocated + * string. + * + * \returns A NULL terminated array of char containing the next line in fp + * is retured. The array must be free by the caller. NULL is returned at end + * of file. + * + * This function returns a NULL terminated array of char containing the next + * line in the file passed as an argument. The memory for the string returned + * is allocated by malloc() and must be freed by the caller. + */ +static char *GetLine(FILE *fp) +{ + char *line; /* string to read line into */ + char *next; /* where to write the next characters into */ + const size_t chunkSize = 32; + size_t lineSize; + + if ((NULL == fp) || feof(fp)) + { + return NULL; + } + + lineSize = chunkSize; + line = (char *)malloc(lineSize * sizeof(char)); + + if (NULL == line) + { + /* allocation failed */ + return NULL; + } + + line[0] = '\0'; + next = line; + + while (NULL != fgets(next, lineSize - strlen(line), fp)) + { + if ('\n' == line[strlen(line) - 1]) + { + /* we got to the EOL strip off the trailing '\n' and exit */ + line[strlen(line) - 1] = '\0'; + break; + } + else + { + /* there's still more on this line */ + lineSize += chunkSize; + line = (char *)realloc(line, lineSize); + + if (NULL == line) + { + return NULL; + } + + next = line + strlen(line); + } + } + + return line; +} + +/**@}*/ diff --git a/src/ezini.h b/src/ezini.h new file mode 100644 index 0000000..e563c7c --- /dev/null +++ b/src/ezini.h @@ -0,0 +1,121 @@ +/** + * \brief INI File handling library header + * \file ezini.h + * \author Michael Dipperstein (mdipperstein@gmail.com) + * \date November 22, 2015 + * + * This file implements a set of library functions that maybe be used + * to create, update, and/or parse INI files. + * + * \copyright Copyright (C) 2015, 2019 by Michael Dipperstein + * (mdipperstein@gmail.com) + * + * \par + * This file is part of the ezini library. + * + * \license The ezini library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * \par + * The ezini library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * \par + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +#ifndef __EZINI_H +#define __EZINI_H + +/** + * \mainpage EZIni - INI File handling library + * + * These pages provide documentation for EZIni, an INI File handling library. + * + * \copyright Copyright (C) 2015, 2019 by Michael Dipperstein + * (mdipperstein@gmail.com) + * + * \license + * The ezini library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * \par + * The ezini library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * \par + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/*************************************************************************** +* TYPE DEFINITIONS +***************************************************************************/ + +/** + * \struct ini_entry_t + * \brief A structure containing the section, key, and value of INI. + * file entry + */ +typedef struct +{ + char *section; /*!< pointer to a NULL terminated string containing the + section name */ + char *key; /*!< pointer to a a NULL terminated string containing + the key name */ + char *value; /*!< pointer to a NULL terminated string with entry + value. Use ASCII strings to represent numbers */ +} ini_entry_t; + +/** + * \typedef ini_entry_list_t + * \brief A shortcut for forward referenced struct ini_section_list_t* + */ +typedef struct ini_section_list_t* ini_entry_list_t; + +/*************************************************************************** +* PROTOTYPES +***************************************************************************/ + +/* add (section, key, value) to a list of INI entries */ +int AddEntryToList(ini_entry_list_t *list, const char *section, + const char *key, const char *value); + +/* free all of the entries in an entry list */ +void FreeList(ini_entry_list_t list); + +/* create/add entries to an INI file from a sorted entry list */ +int MakeINIFile(const char *iniFile, const ini_entry_list_t list); +int AddEntryToFile(const char *iniFile, const ini_entry_list_t list); + +/* remove a single entry from an INI file */ +int DeleteEntryFromFile(const char *iniFile, const char *section, + const char *key); + +/*************************************************************************** +* get the next entry in INI file. +* returns: 1 if an entry is found +* 0 if there are no more entries +* -1 on error +***************************************************************************/ +int GetEntryFromFile(FILE *iniFile, ini_entry_t *entry); + +#ifdef __cplusplus +} +#endif + +#endif /* ndef __EZINI_H */ |
