CGI-скрипт, который считывает или записывает конфигурацию его роста и веса.

Есть два исходных файла 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;
}

Теперь, когда я рассмотрел код, я хочу указать на некоторые из моих взглядов на код.

  1. 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 Как работает приведенный выше код? Есть ли другие общепринятые методы решения таких проблем с обслуживанием? Подскажите пожалуйста поподробнее.

  1. 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 Это действительно хороший подход? Есть ли другие распространенные практики?

  1. andy_web.c не должен знать подробностей о файле конфигурации.

Что, если бы файлы конфигурации обрабатывались в виде двоичных файлов, а не текстовых файлов? Код придется перекомпилировать и перекомпилировать. К счастью, эту проблему можно решить, создав программу andy_cfg что описано в 1.1.

Я потратил на этот пост несколько часов. Пожалуйста, поделитесь своим комментарием любым способом! Спасибо.

0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *