Есть два исходных файла andy_web.c
а также andy_cfg.c
.
andy_web.c
управляет вещами, связанными с веб-страницами, в виде программы, называемой andy_web.cgi
и вызывается lighttpd
.
Короче, если get=setting
получено, andy_web.cgi
будет брать высоту и вес из конфигурационного файла Энди, вызывая функции в andy_cfg.c
, и показать их на страницах; если post=setting
получено, andy_web.cgi
получит рост и вес от lighttpd
и установите их в файл конфигурации, также вызывая функции в andy_cfg.c
.
/*andy_web.c*/
#include "web_api.h"
#include "andy_cfg.h"
#define MIN_HEIGHT 150
#define MAX_HEIGHT 250
#define MIN_WEIGHT 50
#define MAX_WEIGHT 150
static andy_config_t* andy_config = NULL;
static web_t web = {0};
static void andy_web_page_show(void);
static int andy_web_page_setting_check_then_save(char Msg[], uint16_t MsgSize);
int main(void)
{
andy_config = andy_config_init(180, 80);
if (ANDY_CFG_FAILED == andy_config)
{
return -1;
}
web_get(&web);
const uint8_t METHOD = web.nv_ct - 1;
if (!strcmp(web.nameval[METHOD].name, "get"))
{
if (!strcmp(web.nameval[METHOD].value, "setting"))
{
andy_web_page_show();
}
goto DONE;
}
char Msg[100] = {0};
if (!strcmp(web.nameval[METHOD].name, "post"))
{
if (!strcmp(web.nameval[METHOD].value, "setting"))
{
if (andy_web_page_setting_check_then_save(Msg, sizeof(Msg)) == -1)
{
goto SETTING_CHECK_ERROR;
}
}
andy_web_page_show();
goto DONE;
}
SETTING_CHECK_ERROR:
printMsg(Msg);
DONE:
andy_config_close(andy_config);
return 0;
}
static void andy_web_page_show(void)
{
printf("Content-Type: text/htmlnn");
printf("<html>n");
printf("<head>n");
printf("<meta http-equiv="Content-Type" content="text/html;" />n");
printf("<title>andy Setting</title>n");
printf("</head>n");
printf("<body>n");
printf("<fieldset>n");
printf("<legend>andy Setting</legend>n");
printf("<form name="andyPage" method="post" action="/cgi-bin/andy_web.cgi" >n");
printf(" <table id="table1" border="0" cellpadding="1" cellspacing="1">n");
uint16_t height = 0;
if (andy_config_retrive_height(andy_config, &height) != -1)
{
printf(" <tr>n");
printf(" <td height="20" width="150" style="padding-left:10px">andy height</td>n");
printf(" <td height="20" ><input type="text" name="height" style="width: 100;" value="%d" />(%d~%d)</td>n", height, MIN_HEIGHT, MAX_HEIGHT);
printf(" </tr>n");
}
uint16_t weight = 0;
if (andy_config_retrive_weight(andy_config, &weight) != -1)
{
printf(" <tr>n");
printf(" <td height="20" width="150" style="padding-left:10px">andy weight</td>n");
printf(" <td height="20" ><input type="text" name="weight" style="width: 100;" value="%d" />(%d~%d)</td>n", weight, MIN_WEIGHT, MAX_WEIGHT);
printf(" </tr>n");
}
printf(" </table>n");
printf(" <p align="center">n");
printf(" <input type="submit" value=" Update " />n");
printf(" <input type="hidden" name="post" value="setting" />n");
printf(" </p>n");
printf("</form>n");
printf("</body>n");
printf("</html>n");
}
static int andy_web_page_setting_check_then_save(char Msg[], uint16_t MsgSize)
{
int count = 0;
for (; count < form.nv_ct-1; count++)
{
if (!strcmp(form.nameval[count].name, "height"))
{
uint16_t height = atoi(form.nameval[count].value);
if ((height < MIN_HEIGHT) || (height >= MAX_HEIGHT))
{
snprintf(Msg, msgSize, "height <%d> should be between %d and %d", height, MIN_HEIGHT, MAX_HEIGHT);
return -1;
}
andy_config_set_height(andy_config, height);
}
else if (!strcmp(form.nameval[count].name, "weight"))
{
uint16_t weight = atoi(form.nameval[count].value);
if ((weight < MIN_WEIGHT) || (weight >= MAX_WEIGHT))
{
snprintf(Msg, msgSize, "wieght <%d> should be between %d and %d", weight, MIN_WEIGHT, MAX_WEIGHT);
return -1;
}
andy_config_set_weight(andy_config, weight);
}
else
{
}
}
andy_config_save(andy_config);
return 0;
}
andy_cfg.c
управляет вещами, связанными с файлом конфигурации, в виде функций, которые вызываются andy_web.c
.
Имена функций начинаются с iniparser
, такой как iniparser_load
, предлагаются начальный анализатор который в основном анализирует файлы ini и предлагает возможность чтения / записи текстовых файлов на уровне C.
/*andy_cfg.h*/
#ifndef ANDY_CFG_H
#define ANDY_CFG_H
typedef struct andy_config_t andy_config_t;
#define ANDY_CFG_FAILED (andy_config_t*)NULL
andy_config_t* andy_config_init(uint16_t height, uint16_t weight);
void andy_config_close(andy_config_t* me);
void andy_config_save(andy_config_t* me);
void andy_config_set_height(andy_config_t* me, uint16_t height);
void andy_config_set_weight(andy_config_t* me, uint16_t weight);
int andy_config_retrive_height(andy_config_t* me, uint16_t* height);
int andy_config_retrive_weight(andy_config_t* me, uint16_t* weight);
#endif
/*andy_cfg.c*/
#include "iniparser.h"
#include "andy_cfg.h"
#define ANDY_STARTUP_DIR "/mnt/jffs2/cfg/"
#define ANDY_RUNNING_DIR "/var/"
#define ANDY_CFG_FILE "andy.ini"
andy_config_t* andy_config_init(uint16_t height, uint16_t weight)
{
if (!file_is_existing(ANDY_STARTUP_DIR""ANDY_CFG_FILE))
{
FILE* andy_startup_file = fopen(ANDY_STARTUP_DIR""ANDY_CFG_FILE, "w");
if (NULL == andy_startup_file)
{
return ANDY_CFG_FAILED;
}
fprintf(andy_startup_file,
"n"
"[Info]n"
"n"
"height = %d ; 150 ~ 250n"
"wieght = %d ; 50 ~ 150n"
"n", height, weight);
fclose(andy_startup_file);
}
dictionary* andy_running_config = iniparser_load(ANDY_STARTUP_DIR""ANDY_CFG_FILE);
if (NULL == andy_running_config)
{
return ANDY_CFG_FAILED;
}
return (andy_config_t*)andy_running_config;
}
void andy_config_close(andy_config_t* me)
{
if (NULL != me)
{
andy_config_save(me);
iniparser_freedict((dictionary*)me);
}
}
void andy_config_save(andy_config_t* me)
{
FILE* andy_startup_file = fopen(ANDY_STARTUP_DIR""ANDY_CFG_FILE, "w");
iniparser_dump_ini((dictionary*)me, andy_startup_file);
fclose(andy_startup_file);
}
void andy_config_set_height(andy_config_t* me, uint16_t height)
{
char str[10] = {0};
snprintf(str, sizeof(str), "%d", height);
iniparser_set((dictionary*)me, "Info:height", str);
}
void andy_config_set_weight(andy_config_t* me, uint16_t weight)
{
char str[10] = {0};
snprintf(str, sizeof(str), "%d", weight);
iniparser_set((dictionary*)me, "Info:weight", str);
}
int andy_config_retrive_height(andy_config_t* me, uint16_t* height)
{
int ret = iniparser_getint((dictionary*)me, "Info:height", -1);
if (ret == -1)
{
return -1;
}
*height = (uint16_t)ret;
return 0;
}
int andy_config_retrive_weight(andy_config_t* me, uint16_t* weight)
{
int ret = iniparser_getint((dictionary*)me, "Info:weight", -1);
if (ret == -1)
{
return -1;
}
*weight = (uint16_t)ret;
return 0;
}
Теперь, когда я рассмотрел код, я хочу указать на некоторые из моих взглядов на код.
andy_web.cgi
должен иметь дело и знать только о проблемах Интернета.
1.1. Будет ли это хороший выбор для andy_web.cgi
что создание программы andy_cfg
это касается всего о andy.ini
и получать / сохранять данные из / в него через IPC? Если да, то с какими соображениями вы воспользуетесь этим подходом?
Или можно создать программу cfg
который обрабатывает файлы конфигурации не только для Энди, но и для всех. Однако этот путь не удастся, потому что cfg
будет включать так много файлов заголовков, таких как andy_cfg.h
, amy_cfg.h
, benson_cfg.h
, и в конечном итоге возникнут проблемы с обслуживанием.
Я где-то видел код, который, кажется, предназначен для решения этих проблем с обслуживанием. По сути, есть одна функция обратного вызова, и внезапно кажется, что отношения между вызывающим и вызываемым перевернуты. Код может выглядеть так
/*cfg.c*/
typedef void(*register_function_t)(void);
cfg_register_set(char* function_name, register_function_t callback_function)
{
/*Object which wants to use config feature
will call this function to register this feature*/
}
void cfg_init(void)
{
/*After registration, all cfg files will be handled in some way.*/
}
1.2 Как работает приведенный выше код? Есть ли другие общепринятые методы решения таких проблем с обслуживанием? Подскажите пожалуйста поподробнее.
andy_cfg.c
кажется, нарушает DRY (не повторяйтесь).
Таким образом получится так много исходных файлов, как andy_cfg.c
, amy_cfg.c
, benson_cfg.c
и т. д. Все эти коды имеют все общее, за исключением имен функций, которые обеспечивают буквально одинаковую функциональность. Например
/*amy_cfg.h*/
#ifndef AMY_CFG_H
#define AMY_CFG_H
typedef struct amy_config_t amy_config_t;
#define AMY_CFG_FAILED (amy_config_t*)NULL
amy_config_t* amy_config_init(uint16_t height, uint16_t weight);
void amy_config_close(amy_config_t* me);
void amy_config_save(amy_config_t* me);
void amy_config_set_height(amy_config_t* me, uint16_t height);
void amy_config_set_weight(amy_config_t* me, uint16_t weight);
int amy_config_retrive_height(amy_config_t* me, uint16_t* height);
int amy_config_retrive_weight(amy_config_t* me, uint16_t* weight);
#endif
/*amy_cfg.c*/
#include "iniparser.h"
#include "amy_cfg.h"
#define AMY_STARTUP_DIR "/mnt/jffs2/cfg/"
#define AMY_RUNNING_DIR "/var/"
#define AMY_CFG_FILE "amy.ini"
amy_config_t* amy_config_init(uint16_t height, uint16_t weight)
{
if (!file_is_existing(AMY_STARTUP_DIR""AMY_CFG_FILE))
{
FILE* amy_startup_file = fopen(AMY_STARTUP_DIR""AMY_CFG_FILE, "w");
if (NULL == amy_startup_file)
{
return AMY_CFG_FAILED;
}
fprintf(amy_startup_file,
"n"
"[Info]n"
"n"
"height = %d ; 150 ~ 250n"
"wieght = %d ; 50 ~ 150n"
"n", height, weight);
fclose(amy_startup_file);
}
dictionary* amy_running_config = iniparser_load(AMY_STARTUP_DIR""AMY_CFG_FILE);
if (NULL == amy_running_config)
{
return AMY_CFG_FAILED;
}
return (amy_config_t*)amy_running_config;
}
void amy_config_close(amy_config_t* me)
{
if (NULL != me)
{
amy_config_save(me);
iniparser_freedict((dictionary*)me);
}
}
void amy_config_save(amy_config_t* me)
{
FILE* amy_startup_file = fopen(AMY_STARTUP_DIR""AMY_CFG_FILE, "w");
iniparser_dump_ini((dictionary*)me, amy_startup_file);
fclose(amy_startup_file);
}
void amy_config_set_height(amy_config_t* me, uint16_t height)
{
char str[10] = {0};
snprintf(str, sizeof(str), "%d", height);
iniparser_set((dictionary*)me, "Info:height", str);
}
void amy_config_set_weight(amy_config_t* me, uint16_t weight)
{
char str[10] = {0};
snprintf(str, sizeof(str), "%d", weight);
iniparser_set((dictionary*)me, "Info:weight", str);
}
int amy_config_retrive_height(amy_config_t* me, uint16_t* height)
{
int ret = iniparser_getint((dictionary*)me, "Info:height", -1);
if (ret == -1)
{
return -1;
}
*height = (uint16_t)ret;
return 0;
}
int amy_config_retrive_weight(amy_config_t* me, uint16_t* weight)
{
int ret = iniparser_getint((dictionary*)me, "Info:weight", -1);
if (ret == -1)
{
return -1;
}
*weight = (uint16_t)ret;
return 0;
}
Инстинкт подсказал мне, что я должен писать функции общего типа для iniparser
, чтобы я мог использовать его повторно. К сожалению, легче сказать, чем сделать.
2.1 Пишет функции общего типа для iniparser
, чтобы решить проблему СУХОЙ, хороший выбор в C? Есть ли другие распространенные практики?
Когда-то мне в голову приходил другой способ, но я не был уверен, что он осуществим.
Что, если я заверну iniparser
как программу и позволить другим программистам указывать аргументы в командной строке? Другими словами, можно создать файл конфигурации для всех, набрав iniparser someone.ini height=180 weight=80
в командной строке, не создавая someone_cfg.c
несколько раз.
Кроме того, для командной строки добавить новый атрибут намного проще, чем функциональный подход. Например, что, если появится атрибут возраста? Для программного подхода просто введите iniparser someone.ini height=180 weight=80 age=32
в командной строке; для функционального подхода, void someone_cfg_set_age(someone_cfg_t* me, uint8_t age);
добавлен, исходный файл перекомпилирован и перекомпонован. Все iniparser
просто помещает аргументы в командную строку в файл. Просто и понятно.
Однако этот подход меняет iniparser
кривая обучения от функционального уровня к программному уровню. А уж в самой программе могут быть ошибки! Более того, этот подход применим только к программистам, знающим скрипты.
2.2 Это действительно хороший подход? Есть ли другие распространенные практики?
andy_web.c
не должен знать подробностей о файле конфигурации.
Что, если бы файлы конфигурации обрабатывались в виде двоичных файлов, а не текстовых файлов? Код придется перекомпилировать и перекомпилировать. К счастью, эту проблему можно решить, создав программу andy_cfg
что описано в 1.1.
Я потратил на этот пост несколько часов. Пожалуйста, поделитесь своим комментарием любым способом! Спасибо.