nuttx-apps/system/settings/storage_text.c
2024-03-03 02:36:56 +08:00

426 lines
10 KiB
C

/****************************************************************************
* apps/system/settings/storage_text.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include "system/settings.h"
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <nuttx/config.h>
#include <sys/types.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define BUFFER_SIZE 256 /* Note alignment for Flash writes! */
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: getsetting
*
* Description:
* Gets the setting information from a given key.
*
* Input Parameters:
* key - key of the required setting
*
* Returned Value:
* The setting
*
****************************************************************************/
FAR static setting_t *getsetting(char *key);
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Public Data
****************************************************************************/
extern setting_t map[CONFIG_SYSTEM_SETTINGS_MAP_SIZE];
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: getsetting
*
* Description:
* Gets the setting information from a given key.
*
* Input Parameters:
* key - key of the required setting
*
* Returned Value:
* The setting
*
****************************************************************************/
FAR setting_t *getsetting(char *key)
{
int i;
FAR setting_t *setting;
if (strlen(key) >= CONFIG_SYSTEM_SETTINGS_KEY_SIZE)
{
return NULL;
}
for (i = 0; i < CONFIG_SYSTEM_SETTINGS_MAP_SIZE; i++)
{
setting = &map[i];
if (strcmp(key, setting->key) == 0)
{
return setting;
}
if (setting->type == SETTING_EMPTY)
{
strncpy(setting->key, key, CONFIG_SYSTEM_SETTINGS_KEY_SIZE);
setting->key[CONFIG_SYSTEM_SETTINGS_KEY_SIZE - 1] = '\0';
return setting;
}
}
return NULL;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: load_text
*
* Description:
* Loads text from a storage file.
*
* Input Parameters:
* file - the filename of the storage to use
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int load_text(FAR char *file)
{
int ret = OK;
FAR FILE *f;
FAR char *eq;
FAR char *key;
FAR char *val;
FAR char *backup_file;
FAR char *buffer;
FAR setting_t *setting;
/* Check that the file exists */
if (access(file, F_OK) != 0)
{
/* If not, try the backup file */
backup_file = malloc(strlen(file) + 2);
if (backup_file == NULL)
{
return -ENODEV;
}
strcpy(backup_file, file);
strcat(backup_file, "~");
if (access(backup_file, F_OK) == 0)
{
/* There is a backup file
* Restore this as the new settings file
*/
rename(backup_file, file);
}
free(backup_file);
}
f = fopen(file, "r");
if (f == NULL)
{
return -ENOENT;
}
buffer = malloc(BUFFER_SIZE);
if (buffer == NULL)
{
ret = -ENOMEM;
goto abort;
}
while (fgets(buffer, BUFFER_SIZE, f))
{
int i;
/* Remove any line terminators */
for (i = ((int)strlen(buffer) - 1); i > 0; i--)
{
if (buffer[i] == ';' || buffer[i] == '\n' || buffer[i] == '\r')
{
buffer[i] = '\0';
}
else
{
break;
}
}
/* Separate the key / value pair */
eq = strchr(buffer, '=');
if (eq == NULL)
{
continue;
}
key = buffer;
val = eq + 1;
*eq = '\0';
/* Check if the key is valid */
if (!isalpha(key[0]))
{
continue;
}
/* Get the setting slot */
setting = getsetting(key);
if (setting == NULL)
{
continue;
}
/* Parse the value */
if (isalpha(val[0]))
{
if (strcasecmp(val, "true") == 0)
{
/* It's a boolean */
setting->type = SETTING_BOOL;
setting->val.i = 1;
}
else if (strcasecmp(val, "false") == 0)
{
/* It's a boolean */
setting->type = SETTING_BOOL;
setting->val.i = 0;
}
else
{
/* It's a string */
if (strlen(val) >= CONFIG_SYSTEM_SETTINGS_VALUE_SIZE)
{
continue;
}
setting->type = SETTING_STRING;
strncpy(setting->val.s, val,
CONFIG_SYSTEM_SETTINGS_VALUE_SIZE);
setting->val.s[CONFIG_SYSTEM_SETTINGS_VALUE_SIZE - 1] = '\0';
}
}
else
{
if (strchr(val, '.') != NULL)
{
FAR char *s = val;
i = 0;
while (s[i]) s[i] == '.' ? i++ : *s++;
if (i == 1)
{
/* It's a float */
double d = 0;
if (sscanf(val, "%lf", &d) == 1)
{
setting->type = SETTING_FLOAT;
setting->val.f = d;
}
}
else if (i == 3)
{
/* It's an IP address */
setting->type = SETTING_IP_ADDR;
inet_pton(AF_INET, val, &setting->val.ip);
}
}
else
{
/* It's an integer */
i = 0;
if (sscanf(val, "%d", &i) == 1)
{
setting->type = SETTING_INT;
setting->val.i = i;
}
}
}
/* Handle parse errors */
if (setting->type == SETTING_EMPTY)
{
memset(setting->key, 0, CONFIG_SYSTEM_SETTINGS_KEY_SIZE);
}
}
free(buffer);
abort:
fclose(f);
return ret;
}
/****************************************************************************
* Name: save_text
*
* Description:
* Saves text to a storage file.
*
* Input Parameters:
* file - the filename of the storage to use
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int save_text(FAR char *file)
{
int ret = OK;
FAR char *backup_file = malloc(strlen(file) + 2);
FAR FILE *f;
int i;
if (backup_file == NULL)
{
return -ENODEV;
}
strcpy(backup_file, file);
strcat(backup_file, "~");
f = fopen(backup_file, "w");
if (f == NULL)
{
ret = -ENODEV;
goto abort;
}
for (i = 0; i < CONFIG_SYSTEM_SETTINGS_MAP_SIZE; i++)
{
if (map[i].type == SETTING_EMPTY)
{
break;
}
switch (map[i].type)
{
case SETTING_STRING:
{
fprintf(f, "%s=%s\n", map[i].key,
map[i].val.s);
}
break;
case SETTING_INT:
{
fprintf(f, "%s=%d\n", map[i].key,
map[i].val.i);
}
break;
case SETTING_BOOL:
{
fprintf(f, "%s=%s\n", map[i].key,
map[i].val.i ?
"true" : "false");
}
break;
case SETTING_FLOAT:
{
fprintf(f, "%s=%.06f\n", map[i].key,
map[i].val.f);
}
break;
case SETTING_IP_ADDR:
{
char buffer[20];
inet_ntop(AF_INET, &map[i].val.ip, buffer, 20);
fprintf(f, "%s=%s\n", map[i].key, buffer);
}
break;
default:
{
return -EINVAL;
}
break;
}
}
fclose(f);
remove(file);
rename(backup_file, file);
abort:
free(backup_file);
return ret;
}