/ src / IniParser.cpp
IniParser.cpp
  1  #include "IniParser.h"
  2  
  3  #include <SDCardManager.h>
  4  
  5  #include <cctype>
  6  #include <cstring>
  7  
  8  bool IniParser::parseFile(const char* path, Callback callback) {
  9    FsFile file = SdMan.open(path, O_RDONLY);
 10    if (!file) {
 11      return false;
 12    }
 13  
 14    char currentSection[64] = "";
 15    char line[256];
 16  
 17    while (file.available()) {
 18      // Read line
 19      size_t len = 0;
 20      while (file.available() && len < sizeof(line) - 1) {
 21        int c = file.read();
 22        if (c < 0 || c == '\n') break;
 23        if (c != '\r') {
 24          line[len++] = static_cast<char>(c);
 25        }
 26      }
 27      line[len] = '\0';
 28  
 29      // Discard remainder of long lines
 30      if (len == sizeof(line) - 1) {
 31        while (file.available()) {
 32          int c = file.read();
 33          if (c < 0 || c == '\n') break;
 34        }
 35      }
 36  
 37      // Check if this is a section header
 38      trimWhitespace(line);
 39      if (line[0] == '[') {
 40        char* end = strchr(line, ']');
 41        if (end) {
 42          *end = '\0';
 43          strncpy(currentSection, line + 1, sizeof(currentSection) - 1);
 44          currentSection[sizeof(currentSection) - 1] = '\0';
 45        }
 46        continue;
 47      }
 48  
 49      // Parse key=value
 50      if (!parseLine(line, currentSection, callback)) {
 51        file.close();
 52        return true;  // Callback requested stop
 53      }
 54    }
 55  
 56    file.close();
 57    return true;
 58  }
 59  
 60  bool IniParser::parseString(const char* content, Callback callback) {
 61    if (!content) return false;
 62  
 63    char currentSection[64] = "";
 64    char line[256];
 65    const char* ptr = content;
 66  
 67    while (*ptr) {
 68      // Read line
 69      size_t len = 0;
 70      while (*ptr && *ptr != '\n' && len < sizeof(line) - 1) {
 71        if (*ptr != '\r') {
 72          line[len++] = *ptr;
 73        }
 74        ptr++;
 75      }
 76      line[len] = '\0';
 77  
 78      // Discard remainder of long lines
 79      if (len == sizeof(line) - 1) {
 80        while (*ptr && *ptr != '\n') ptr++;
 81      }
 82  
 83      if (*ptr == '\n') ptr++;
 84  
 85      // Check if this is a section header
 86      trimWhitespace(line);
 87      if (line[0] == '[') {
 88        char* end = strchr(line, ']');
 89        if (end) {
 90          *end = '\0';
 91          strncpy(currentSection, line + 1, sizeof(currentSection) - 1);
 92          currentSection[sizeof(currentSection) - 1] = '\0';
 93        }
 94        continue;
 95      }
 96  
 97      // Parse key=value
 98      if (!parseLine(line, currentSection, callback)) {
 99        return true;  // Callback requested stop
100      }
101    }
102  
103    return true;
104  }
105  
106  bool IniParser::parseLine(char* line, const char* currentSection, const Callback& callback) {
107    // Skip comments and empty lines
108    if (line[0] == '#' || line[0] == ';' || line[0] == '\0') {
109      return true;
110    }
111  
112    // Find '='
113    char* eq = strchr(line, '=');
114    if (!eq) return true;
115  
116    *eq = '\0';
117    char* key = line;
118    char* value = eq + 1;
119  
120    trimWhitespace(key);
121    trimWhitespace(value);
122  
123    if (key[0] == '\0') return true;
124  
125    return callback(currentSection, key, value);
126  }
127  
128  void IniParser::trimWhitespace(char* str) {
129    if (!str || !*str) return;
130  
131    // Trim leading
132    char* start = str;
133    while (isspace((unsigned char)*start)) start++;
134  
135    // Handle all-whitespace string
136    if (!*start) {
137      *str = '\0';
138      return;
139    }
140  
141    // Trim trailing
142    char* end = start + strlen(start) - 1;
143    while (end > start && isspace((unsigned char)*end)) end--;
144    *(end + 1) = '\0';
145  
146    // Move to beginning if needed
147    if (start != str) {
148      memmove(str, start, strlen(start) + 1);
149    }
150  }
151  
152  bool IniParser::parseBool(const char* value, bool defaultValue) {
153    if (!value || !*value) return defaultValue;
154  
155    // Check true values
156    if (strcasecmp(value, "true") == 0 || strcasecmp(value, "yes") == 0 || strcasecmp(value, "on") == 0 ||
157        strcmp(value, "1") == 0) {
158      return true;
159    }
160  
161    // Check false values
162    if (strcasecmp(value, "false") == 0 || strcasecmp(value, "no") == 0 || strcasecmp(value, "off") == 0 ||
163        strcmp(value, "0") == 0) {
164      return false;
165    }
166  
167    return defaultValue;
168  }
169  
170  int IniParser::parseInt(const char* value, int defaultValue) {
171    if (!value || !*value) return defaultValue;
172  
173    char* end;
174    long result = strtol(value, &end, 10);
175    if (end == value) return defaultValue;  // No conversion
176  
177    return static_cast<int>(result);
178  }
179  
180  uint8_t IniParser::parseColor(const char* value, uint8_t defaultValue) {
181    if (!value || !*value) return defaultValue;
182  
183    if (strcasecmp(value, "black") == 0) return 0x00;
184    if (strcasecmp(value, "white") == 0) return 0xFF;
185  
186    // Try numeric
187    int num = parseInt(value, -1);
188    if (num >= 0 && num <= 255) {
189      return static_cast<uint8_t>(num);
190    }
191  
192    return defaultValue;
193  }