C プログラミングでの 設定ファイル読み込み
Windows だと iniファイルを GetPrivateProfileString などで読めるが、 C 単体だとそんなのない。 C で ini ファイルのパーサを作るか、何かライブラリを入れるしかない。そこで、 ini ファイルの変わりにシェルスクリプトと環境変数を使うことを考える。
設定ファイル setting.sh
#!/bin/sh # comment1 export APL_STR_VAR='abc #' # comment2 export APL_LONG_VAR=123 # comment3 export APL_DOUBLE_VAR=123.45 export APL_IP_ADDR_VAR=127.0.0.1 export APL_MAC_ADDR_VAR=01:02:03:04:05:06
起動スクリプト start_apl.sh
#!/bin/sh source setting.sh ./a.out
設定ファイル setting.sh を起動スクリプト tart_apl.sh で読み取ってから、C プログラムを起動する。C プログラムでは、getenvで文字列を取得する。一旦起動スクリプトをはさんでいるのは、シェルプロンプトの環境変数を汚したくないから。設定値が少量であれば、シェルプロンプトで環境変数を定義してもよい。
設定ファイルをシェルスクリプトにすることによって、
- シェル展開(`cmd`)、コメントアウトやシングル・ダブルクォーテーションが使える
- 自分でパーサを書く必要がない
- 設定値を簡単にファイル分割できる
この方法の問題点は、
- 設定項目が大量にある場合、環境変数も大量になるので問題になるかも(メモリを食う)
- 設定値が非常に長い場合も同様に問題になるかも
- ini ファイルにおけるセクションの概念がない。ファイル分割しても、環境変数名で区別する必要がある。
#if 0 gcc -g $0 -o a.out && ./a.out test.ini; exit; #endif #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <errno.h> #define TRUE 1 #define FALSE 0 #define MAC_ADDR_LEN 6 #define NELEMS(array) (sizeof(array) / sizeof(array[0])) typedef enum EnvType { STRING, LONG, DOUBLE, IP_ADDR, MAC_ADDR } EnvType; typedef struct Env { EnvType type; char *name; void *data; int len; } Env; char g_string[256]; long g_long; double g_double; u_int g_ip_addr; u_char g_mac_addr[MAC_ADDR_LEN]; Env g_env_table[] = { {STRING, "APL_STR_VAR", (void *)g_string, sizeof(g_string)}, {LONG, "APL_LONG_VAR", (void *)&g_long, sizeof(long)}, {DOUBLE, "APL_DOUBLE_VAR", (void *)&g_double, sizeof(double)}, {IP_ADDR, "APL_IP_ADDR_VAR", (void *)&g_ip_addr, sizeof(u_int)}, {MAC_ADDR, "APL_MAC_ADDR_VAR", (void *)g_mac_addr, MAC_ADDR_LEN}, }; int convert_mac_addr(char *data, u_char *mac_addr); char *print_mac_addr(u_char *mac_addr, char *buf); int convert_ip_addr(char *data, u_int *ip_addr); char *print_ip_addr(u_int ip_addr, char *buf); int convert_long(char *data, long *num); int convert_double(char *data, double *num); int main(int argc, char *argv[]) { int i; char *p; char buf[256]; for (i = 0; i < NELEMS(g_env_table); i++) { p = getenv(g_env_table[i].name); if (NULL == p) { fprintf(stderr, "Not found %s\n", g_env_table[i].name); } else { switch (g_env_table[i].type) { case STRING: strncpy((char *)g_env_table[i].data, p, g_env_table[i].len); printf("%s=%s\n", g_env_table[i].name, (char *)g_env_table[i].data); break; case LONG: convert_long(p, (long *)g_env_table[i].data); printf("%s=%ld\n", g_env_table[i].name, *(long *)g_env_table[i].data); break; case DOUBLE: convert_double(p, (double *)g_env_table[i].data); printf("%s=%lf\n", g_env_table[i].name, *(double *)g_env_table[i].data); break; case IP_ADDR: convert_ip_addr(p, (u_int *)g_env_table[i].data); printf("%s=%s\n", g_env_table[i].name, print_ip_addr(*(u_int *)g_env_table[i].data, buf)); break; case MAC_ADDR: convert_mac_addr(p, (u_char *)g_env_table[i].data); printf("%s=%s\n", g_env_table[i].name, print_mac_addr((u_char *)g_env_table[i].data, buf)); break; default: fprintf(stderr, "Unknown parameter type : %d\n", g_env_table[i].type); break; } } } return 0; } int convert_long(char *data, long *num) { long ret; char *e; ret = strtol(data, &e, 10); if(ERANGE != errno) { *num = ret; return TRUE; } else { return FALSE; } } int convert_double(char *data, double *num) { double ret; char *e; ret = strtod(data, &e); if(ERANGE != errno) { *num = ret; return TRUE; } else { return FALSE; } } int convert_mac_addr(char *data, u_char *mac_addr) { int ret; int i; u_int check_mac_addr[MAC_ADDR_LEN]; ret = sscanf(data, "%02X:%02X:%02X:%02X:%02X:%02X", &(check_mac_addr[0]), &(check_mac_addr[1]), &(check_mac_addr[2]), &(check_mac_addr[3]), &(check_mac_addr[4]), &(check_mac_addr[5])); if(MAC_ADDR_LEN == ret) { for (i = 0; i < MAC_ADDR_LEN; i++) { mac_addr[i] = check_mac_addr[i]; } return TRUE; } return FALSE; } char *print_mac_addr(u_char *mac_addr, char *buf) { sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); return buf; } int convert_ip_addr(char *data, u_int *ip_addr) { int ret; struct in_addr in_addr; ret = inet_pton(AF_INET, data, &in_addr); if (0 != ret) { *ip_addr = in_addr.s_addr; return TRUE; } return FALSE; } char *print_ip_addr(u_int ip_addr, char *buf) { struct in_addr in_addr; char dest[INET_ADDRSTRLEN]; memset(&in_addr, 0x00, sizeof(in_addr)); in_addr.s_addr = ip_addr; if (NULL != inet_ntop(AF_INET, (void *)&in_addr, dest, INET_ADDRSTRLEN)) { sprintf(buf, "%s", dest); } else { buf = NULL; } return buf; }
結果
$ sh start_apl.sh
APL_STR_VAR=abc #
APL_LONG_VAR=123
APL_DOUBLE_VAR=123.450000
APL_IP_ADDR_VAR=127.0.0.1
APL_MAC_ADDR_VAR=01:02:03:04:05:06