static char *fgetkeys(char *str, int str_size, const char *key, FILE *fd) { char *sp, *ep, *s; int len; char buf[256]; assert(fd); assert(key); len = strlen(key); do { if (fgets(buf, 256, fd) == NULL) return NULL; sp = skipleading(buf); //if (*sp == ';' || *sp == '#') break; ep = strchr(sp, '='); } while (ep == NULL || ((int)(skiptrailing(ep, sp) - sp) != len || strncasecmp(sp, key, len) != 0)); assert(ep); assert(*ep == '='); sp = skipleading(ep + 1); // Array if (*sp == '{') { s = str; sp = skipleading(sp + 1); do { ep = strchr(sp, '}'); if (ep) { ep = skiptrailing(ep, sp); len = min(str_size, ep - sp); strncpy(s, sp, len); s += len; break; } else { ep = skiptrailing(strchr(sp, '\0'), sp); if (ep > sp) { len = min(str_size, ep - sp); strncpy(s, sp, len); s += len; str_size -= len; } } if (fgets(buf, 256, fd) == NULL) break; sp = skipleading(buf); } while (1); *s = '\0'; } else { ep = striptrailing(sp); if (ep > sp) strncpy(str, sp, str_size); } return str; }
static TCHAR *striptrailing(TCHAR *str) { TCHAR *ptr = skiptrailing(_tcschr(str, '\0'), str); assert(ptr != NULL); *ptr = '\0'; return str; }
static char *striptrailing(char *str) { char *ptr = skiptrailing(_tcschr(str, '\0'), str); assert(ptr != NULL); *ptr='\0'; return str; }
// ======================== Implementation ===================================== bool iniReadString(const char *ASection, const char *AKey, const char *AFileName, char *AOutput, uint32_t AMaxLen) { FRESULT rslt; // Open file rslt = f_open(&SD.File, AFileName, FA_READ+FA_OPEN_EXISTING); if (rslt != FR_OK) { Uart.Printf(AFileName); if (rslt == FR_NO_FILE) Uart.Printf(": file not found\r"); else Uart.Printf(": openFile error: %u", rslt); return false; } // Check if zero file if (SD.File.fsize == 0) { f_close(&SD.File); Uart.Printf("Empty file\r"); return false; } // Move through file one line at a time until a section is matched or EOF. char *StartP, *EndP; uint32_t len = strlen(ASection); do { if (!f_gets(IBuf, INI_BUF_SIZE, &SD.File)) { f_close(&SD.File); return false; } StartP = skipleading(IBuf); EndP = strchr(StartP, ']'); } while ((*StartP != '[') or (EndP == NULL) or ((uint32_t)(EndP-StartP-1) != len or strnicmp(StartP+1, ASection, len) != 0)); // Section found, find the key len = strlen(AKey); do { if (!f_gets(IBuf, INI_BUF_SIZE, &SD.File) or *(StartP = skipleading(IBuf)) == '[') { f_close(&SD.File); return false; } StartP = skipleading(IBuf); EndP = strchr(StartP, '='); /* Parse out the equal sign */ } while ((*StartP == ';') or (*StartP == '#') or (EndP == NULL) or ((uint32_t)(skiptrailing(EndP, StartP)-StartP) != len or strnicmp(StartP, AKey, len) != 0)); // Copy up to ALength chars to AOutput StartP = skipleading(EndP + 1); // Remove a trailing comment uint8_t isstring = 0; for (EndP = StartP; (*EndP != '\0') and (((*EndP != ';') and (*EndP != '#')) or isstring) and ((uint32_t)(EndP - StartP) < AMaxLen); EndP++) { if (*EndP == '"') { if (*(EndP + 1) == '"') EndP++; // skip "" (both quotes) else isstring = !isstring; // single quote, toggle isstring } else if (*EndP == '\\' && *(EndP + 1) == '"') EndP++; // skip \" (both quotes) } // for *EndP = '\0'; // Terminate at a comment striptrailing(StartP); strcpy(AOutput, StartP); f_close(&SD.File); return &AOutput[EndP-StartP-2]; // Pointer to last '\0' }
static char *striptrailing(char *str) { char *ptr = skiptrailing(strchr(str, '\0'), str); assert(ptr); *ptr = '\0'; return ptr; }
/** ini_puts() * \param Section the name of the section to write the string in * \param Key the name of the entry to write, or NULL to erase all keys in the section * \param Value a pointer to the buffer the string, or NULL to erase the key * \param Filename the name and full path of the .ini file to write to * * \return 1 if successful, otherwise 0 */ int ini_puts(const TCHAR *Section, const TCHAR *Key, const TCHAR *Value, const TCHAR *Filename) { INI_FILETYPE rfp; INI_FILETYPE wfp; INI_FILEPOS mark; TCHAR *sp, *ep; TCHAR LocalBuffer[INI_BUFFERSIZE]; int len, match, flag, cachelen; assert(Filename != NULL); if (!ini_openread(Filename, &rfp)) { /* If the .ini file doesn't exist, make a new file */ if (Key != NULL && Value != NULL) { if (!ini_openwrite(Filename, &wfp)) return 0; writesection(LocalBuffer, Section, &wfp); writekey(LocalBuffer, Key, Value, &wfp); (void)ini_close(&wfp); } /* if */ return 1; } /* if */ /* If parameters Key and Value are valid (so this is not an "erase" request) * and the setting already exists and it already has the correct value, do * nothing. This early bail-out avoids rewriting the INI file for no reason. */ if (Key != NULL && Value != NULL) { (void)ini_tell(&rfp, &mark); match = getkeystring(&rfp, Section, Key, -1, -1, LocalBuffer, sizearray(LocalBuffer)); if (match && _tcscmp(LocalBuffer,Value) == 0) { (void)ini_close(&rfp); return 1; } /* if */ /* key not found, or different value -> proceed (but rewind the input file first) */ (void)ini_seek(&rfp, &mark); } /* if */ /* Get a temporary file name to copy to. Use the existing name, but with * the last character set to a '~'. */ ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE); if (!ini_openwrite(LocalBuffer, &wfp)) { (void)ini_close(&rfp); return 0; } /* if */ (void)ini_tell(&rfp, &mark); cachelen = 0; /* Move through the file one line at a time until a section is * matched or until EOF. Copy to temp file as it is read. */ len = (Section != NULL) ? _tcslen(Section) : 0; if (len > 0) { do { if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { /* Failed to find section, so add one to the end */ flag = cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); if (Key!=NULL && Value!=NULL) { if (!flag) (void)ini_write(INI_LINETERM, &wfp); /* force a new line behind the last line of the INI file */ writesection(LocalBuffer, Section, &wfp); writekey(LocalBuffer, Key, Value, &wfp); } /* if */ return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ } /* if */ /* Copy the line from source to dest, but not if this is the section that * we are looking for and this section must be removed */ sp = skipleading(LocalBuffer); ep = _tcschr(sp, ']'); match = (*sp == '[' && ep != NULL && (int)(ep-sp-1) == len && _tcsnicmp(sp + 1,Section,len) == 0); if (!match || Key != NULL) { if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); } /* if */ } /* if */ } while (!match); } /* if */ cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); /* when deleting a section, the section head that was just found has not been * copied to the output file, but because this line was not "accumulated" in * the cache, the position in the input file was reset to the point just * before the section; this must now be skipped (again) */ if (Key == NULL) { (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); (void)ini_tell(&rfp, &mark); } /* if */ /* Now that the section has been found, find the entry. Stop searching * upon leaving the section's area. Copy the file as it is read * and create an entry if one is not found. */ len = (Key!=NULL) ? _tcslen(Key) : 0; for( ;; ) { if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { /* EOF without an entry so make one */ flag = cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); if (Key!=NULL && Value!=NULL) { if (!flag) (void)ini_write(INI_LINETERM, &wfp); /* force a new line behind the last line of the INI file */ writekey(LocalBuffer, Key, Value, &wfp); } /* if */ return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ } /* if */ sp = skipleading(LocalBuffer); ep = _tcschr(sp, '='); /* Parse out the equal sign */ if (ep == NULL) ep = _tcschr(sp, ':'); match = (ep != NULL && (int)(skiptrailing(ep,sp)-sp) == len && _tcsnicmp(sp,Key,len) == 0); if ((Key != NULL && match) || *sp == '[') break; /* found the key, or found a new section */ /* copy other keys in the section */ if (Key == NULL) { (void)ini_tell(&rfp, &mark); /* we are deleting the entire section, so update the read position */ } else { if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); } /* if */ } /* if */ } /* for */ /* the key was found, or we just dropped on the next section (meaning that it * wasn't found); in both cases we need to write the key, but in the latter * case, we also need to write the line starting the new section after writing * the key */ flag = (*sp == '['); cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); if (Key != NULL && Value != NULL) writekey(LocalBuffer, Key, Value, &wfp); /* cache_flush() reset the "read pointer" to the start of the line with the * previous key or the new section; read it again (because writekey() destroyed * the buffer) */ (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); if (flag) { /* the new section heading needs to be copied to the output file */ cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); } else { /* forget the old key line */ (void)ini_tell(&rfp, &mark); } /* if */ /* Copy the rest of the INI file */ while (ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); } /* if */ } /* while */ cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ }
static int getkeystring(INI_FILETYPE *fp, const TCHAR *Section, const TCHAR *Key, int idxSection, int idxKey, TCHAR *Buffer, int BufferSize) { TCHAR *sp, *ep; int len, idx; enum quote_option quotes; TCHAR LocalBuffer[INI_BUFFERSIZE]; assert(fp != NULL); /* Move through file 1 line at a time until a section is matched or EOF. If * parameter Section is NULL, only look at keys above the first section. If * idxSection is postive, copy the relevant section name. */ len = (Section != NULL) ? _tcslen(Section) : 0; if (len > 0 || idxSection >= 0) { idx = -1; do { if (!ini_read(LocalBuffer, INI_BUFFERSIZE, fp)) return 0; sp = skipleading(LocalBuffer); ep = _tcschr(sp, ']'); } while (*sp != '[' || ep == NULL || (((int)(ep-sp-1) != len || _tcsnicmp(sp+1,Section,len) != 0) && ++idx != idxSection)); if (idxSection >= 0) { if (idx == idxSection) { assert(ep != NULL); assert(*ep == ']'); *ep = '\0'; save_strncpy(Buffer, sp + 1, BufferSize, QUOTE_NONE); return 1; } /* if */ return 0; /* no more section found */ } /* if */ } /* if */ /* Now that the section has been found, find the entry. * Stop searching upon leaving the section's area. */ assert(Key != NULL || idxKey >= 0); len = (Key != NULL) ? (int)_tcslen(Key) : 0; idx = -1; do { if (!ini_read(LocalBuffer,INI_BUFFERSIZE,fp) || *(sp = skipleading(LocalBuffer)) == '[') return 0; sp = skipleading(LocalBuffer); ep = _tcschr(sp, '='); /* Parse out the equal sign */ if (ep == NULL) ep = _tcschr(sp, ':'); } while (*sp == ';' || *sp == '#' || ep == NULL || (((int)(skiptrailing(ep,sp)-sp) != len || _tcsnicmp(sp,Key,len) != 0) && ++idx != idxKey)); if (idxKey >= 0) { if (idx == idxKey) { assert(ep != NULL); assert(*ep == '=' || *ep == ':'); *ep = '\0'; striptrailing(sp); save_strncpy(Buffer, sp, BufferSize, QUOTE_NONE); return 1; } /* if */ return 0; /* no more key found (in this section) */ } /* if */ /* Copy up to BufferSize chars to buffer */ assert(ep != NULL); assert(*ep == '=' || *ep == ':'); sp = skipleading(ep + 1); sp = cleanstring(sp, "es); /* Remove a trailing comment */ save_strncpy(Buffer, sp, BufferSize, quotes); return 1; }
static int getkeystring(INI_FILETYPE *fp, const TCHAR *Section, const TCHAR *Key, int idxSection, int idxKey, TCHAR *Buffer, int BufferSize) { TCHAR *sp, *ep; int len, idx, isstring; enum quote_option quotes; TCHAR LocalBuffer[INI_BUFFERSIZE]; assert(fp != NULL); /* Move through file 1 line at a time until a section is matched or EOF. If * parameter Section is NULL, only look at keys above the first section. If * idxSection is postive, copy the relevant section name. */ len = (Section != NULL) ? _tcslen(Section) : 0; if (len > 0 || idxSection >= 0) { idx = -1; do { if (!ini_read(LocalBuffer, INI_BUFFERSIZE, fp)) return 0; sp = skipleading(LocalBuffer); ep = _tcschr(sp, ']'); } while (*sp != '[' || ep == NULL || (((int)(ep-sp-1) != len || _tcsnicmp(sp+1,Section,len) != 0) && ++idx != idxSection)); if (idxSection >= 0) { if (idx == idxSection) { assert(ep != NULL); assert(*ep == ']'); *ep = '\0'; save_strncpy(Buffer, sp + 1, BufferSize, QUOTE_NONE); return 1; } /* if */ return 0; /* no more section found */ } /* if */ } /* if */ /* Now that the section has been found, find the entry. * Stop searching upon leaving the section's area. */ assert(Key != NULL || idxKey >= 0); len = (Key != NULL) ? (int)_tcslen(Key) : 0; idx = -1; do { if (!ini_read(LocalBuffer,INI_BUFFERSIZE,fp) || *(sp = skipleading(LocalBuffer)) == '[') return 0; sp = skipleading(LocalBuffer); ep = _tcschr(sp, '='); /* Parse out the equal sign */ if (ep == NULL) ep = _tcschr(sp, ':'); } while (*sp == ';' || *sp == '#' || ep == NULL || (((int)(skiptrailing(ep,sp)-sp) != len || _tcsnicmp(sp,Key,len) != 0) && ++idx != idxKey)); if (idxKey >= 0) { if (idx == idxKey) { assert(ep != NULL); assert(*ep == '=' || *ep == ':'); *ep = '\0'; striptrailing(sp); save_strncpy(Buffer, sp, BufferSize, QUOTE_NONE); return 1; } /* if */ return 0; /* no more key found (in this section) */ } /* if */ /* Copy up to BufferSize chars to buffer */ assert(ep != NULL); assert(*ep == '=' || *ep == ':'); sp = skipleading(ep + 1); /* Remove a trailing comment */ isstring = 0; for (ep = sp; *ep != '\0' && ((*ep != ';' && *ep != '#') || isstring); ep++) { if (*ep == '"') { if (*(ep + 1) == '"') ep++; /* skip "" (both quotes) */ else isstring = !isstring; /* single quote, toggle isstring */ } else if (*ep == '\\' && *(ep + 1) == '"') { ep++; /* skip \" (both quotes */ } /* if */ } /* for */ assert(ep != NULL && (*ep == '\0' || *ep == ';' || *ep == '#')); *ep = '\0'; /* terminate at a comment */ striptrailing(sp); /* Remove double quotes surrounding a value */ quotes = QUOTE_NONE; if (*sp == '"' && (ep = _tcschr(sp, '\0')) != NULL && *(ep - 1) == '"') { sp++; *--ep = '\0'; quotes = QUOTE_DEQUOTE; /* this is a string, so remove escaped characters */ } /* if */ save_strncpy(Buffer, sp, BufferSize, quotes); return 1; }
static char* striptrailing(char *S) { char *ptr = skiptrailing(strchr(S, '\0'), S); *ptr='\0'; return (char*)S; }
/** ini_puts() * \param Section the name of the section to write the string in * \param Key the name of the entry to write, or NULL to erase all keys in the section * \param Value a pointer to the buffer the string, or NULL to erase the key * \param Filename the name and full path of the .ini file to write to * * \return 1 if successful, otherwise 0 */ int ini_puts(const TCHAR *Section, const TCHAR *Key, const TCHAR *Value, const TCHAR *Filename) { INI_FILETYPE rfp; INI_FILETYPE wfp; TCHAR *sp, *ep; TCHAR LocalBuffer[INI_BUFFERSIZE]; int len, match, count; assert(Filename!=NULL); if (!ini_openread(Filename, &rfp)) { /* If the .ini file doesn't exist, make a new file */ if (Key!=NULL && Value!=NULL) { if (!ini_openwrite(Filename, &wfp)) return 0; writesection(LocalBuffer, Section, &wfp); writekey(LocalBuffer, Key, Value, &wfp); ini_close(&wfp); } /* if */ return 1; } /* if */ /* If parameters Key and Value are valid (so this is not an "erase" request) * and the setting already exists and it already has the correct value, do * nothing. This early bail-out avoids rewriting the INI file for no reason. */ if (Key!=NULL && Value!=NULL) { match = getkeystring(&rfp, Section, Key, -1, -1, LocalBuffer, sizearray(LocalBuffer)); if (match && _tcscmp(LocalBuffer,Value)==0) { ini_close(&rfp); return 1; } /* if */ /* key not found, or different value -> proceed (but rewind the input file first) */ ini_rewind(&rfp); } /* if */ /* Get a temporary file name to copy to. Use the existing name, but with * the last character set to a '~'. */ ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE); if (!ini_openwrite(LocalBuffer, &wfp)) { ini_close(&rfp); return 0; } /* if */ /* Move through the file one line at a time until a section is * matched or until EOF. Copy to temp file as it is read. */ count = 0; len = (Section != NULL) ? _tcslen(Section) : 0; if (len > 0) { do { if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { /* Failed to find section, so add one to the end */ if (Key!=NULL && Value!=NULL) { ini_write(INI_LINETERM, &wfp); /* force a new line (there may not have been one) behind the last line of the INI file */ writesection(LocalBuffer, Section, &wfp); writekey(LocalBuffer, Key, Value, &wfp); } /* if */ /* Clean up and rename */ ini_close(&rfp); ini_close(&wfp); ini_remove(Filename); ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE); ini_rename(LocalBuffer, Filename); return 1; } /* if */ /* Copy the line from source to dest, but not if this is the section that * we are looking for and this section must be removed */ sp = skipleading(LocalBuffer); ep = _tcschr(sp, ']'); match = (*sp == '[' && ep != NULL && (int)(ep-sp-1) == len && _tcsnicmp(sp + 1,Section,len) == 0); if (!match || Key!=NULL) { /* Remove blank lines, but insert a blank line (possibly one that was * removed on the previous iteration) before a new section. This creates * "neat" INI files. */ if (_tcslen(sp) > 0) { if (*sp == '[' && count > 0) ini_write(INI_LINETERM, &wfp); ini_write(sp, &wfp); count++; } /* if */ } /* if */ } while (!match); } /* if */ /* Now that the section has been found, find the entry. Stop searching * upon leaving the section's area. Copy the file as it is read * and create an entry if one is not found. */ len = (Key!=NULL) ? _tcslen(Key) : 0; for( ;; ) { if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { /* EOF without an entry so make one */ if (Key!=NULL && Value!=NULL) { ini_write(INI_LINETERM, &wfp); /* force a new line (there may not have been one) behind the last line of the INI file */ writekey(LocalBuffer, Key, Value, &wfp); } /* if */ /* Clean up and rename */ ini_close(&rfp); ini_close(&wfp); ini_remove(Filename); ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE); ini_rename(LocalBuffer, Filename); return 1; } /* if */ sp = skipleading(LocalBuffer); ep = _tcschr(sp, '='); /* Parse out the equal sign */ if (ep == NULL) ep = _tcschr(sp, ':'); match = (ep != NULL && (int)(skiptrailing(ep,sp)-sp) == len && _tcsnicmp(sp,Key,len) == 0); if ((Key!=NULL && match) || *sp == '[') break; /* found the key, or found a new section */ /* in the section that we re-write, do not copy empty lines */ if (Key!=NULL && _tcslen(sp) > 0) ini_write(sp, &wfp); } /* for */ if (*sp == '[') { /* found start of new section, the key was not in the specified * section, so we add it just before the new section */ if (Key!=NULL && Value!=NULL) { /* We cannot use "writekey()" here, because we need to preserve the * contents of LocalBuffer. */ ini_write(Key, &wfp); ini_write("=", &wfp); write_quoted(Value, &wfp); ini_write(INI_LINETERM INI_LINETERM, &wfp); /* put a blank line between the current and the next section */ } /* if */ /* write the new section header that we read previously */ ini_write(sp, &wfp); } else { /* We found the key; ignore the line just read (with the key and * the current value) and write the key with the new value. */ if (Key!=NULL && Value!=NULL) writekey(LocalBuffer, Key, Value, &wfp); } /* if */ /* Copy the rest of the INI file (removing empty lines, except before a section) */ while (ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { sp = skipleading(LocalBuffer); if (_tcslen(sp) > 0) { if (*sp == '[') ini_write(INI_LINETERM, &wfp); ini_write(sp, &wfp); } /* if */ } /* while */ /* Clean up and rename */ ini_close(&rfp); ini_close(&wfp); ini_remove(Filename); ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE); ini_rename(LocalBuffer, Filename); return 1; }