null+****@clear*****
null+****@clear*****
2012年 3月 14日 (水) 09:58:18 JST
Susumu Yata 2012-03-14 09:58:18 +0900 (Wed, 14 Mar 2012)
New Revision: c92de2e7b090d37aaa9a1585d4f34e5541a88c54
Log:
Change the way of parsing config file.
- Use fgetc() instead of fgets() so as to find '\0'.
- Ignore too long lines.
- Free memory spaces allocated for values.
Modified files:
src/groonga.c
Modified: src/groonga.c (+151 -47)
===================================================================
--- src/groonga.c 2012-03-13 19:52:10 +0900 (6c4134f)
+++ src/groonga.c 2012-03-14 09:58:18 +0900 (b862ce4)
@@ -2024,58 +2024,152 @@ get_core_number(void)
#endif /* WIN32 */
}
-static inline char *
-skipspace(char *str)
-{
- while (*str == ' ' || *str == '\t') { ++str; }
- return str;
+/*
+ * The length of each line, including an end-of-line, in config file should be
+ * shorter than (CONFIG_FILE_BUF_SIZE - 1) bytes. Too long lines are ignored.
+ * Note that both '\r' and '\n' are handled as end-of-lines.
+ *
+ * '#' and ';' are special symbols to start comments. A comment ends with an
+ * end-of-line.
+ *
+ * Format: name[=value]
+ * - Preceding/trailing white-spaces of each line are removed.
+ * - White-spaces aroung '=' are removed.
+ * - name does not allow white-spaces.
+ */
+#define CONFIG_FILE_BUF_SIZE 4096
+#define CONFIG_FILE_MAX_NAME_LENGTH 128
+#define CONFIG_FILE_MAX_VALUE_LENGTH 2048
+
+/*
+ * The node type of a linked list for storing values. Note that a value is
+ * stored in the extra space of an object.
+ */
+typedef struct _config_file_entry {
+ struct _config_file_entry *next;
+} config_file_entry;
+
+static config_file_entry *config_file_entry_head = NULL;
+
+static void
+config_file_clear(void) {
+ while (config_file_entry_head) {
+ config_file_entry *next = config_file_entry_head->next;
+ free(config_file_entry_head);
+ config_file_entry_head = next;
+ }
}
+/* TODO: Error messages should be printed. */
static int
-load_config_file(const char *path,
- const grn_str_getopt_opt *opts, int *flags)
+config_file_parse(const char *path, const grn_str_getopt_opt *opts,
+ int *flags, char *buf) {
+ char *ptr, *name, *value;
+ size_t name_length, value_length;
+
+ while (isspace(*buf)) { buf++; }
+
+ ptr = buf;
+ while (*ptr && *ptr != '#' && *ptr != ';') { ptr++; }
+ do { *ptr-- = '\0'; } while (ptr >= buf && isspace(*ptr));
+ if (!*buf) { return 0; }
+
+ name = ptr = buf;
+ while (*ptr && !isspace(*ptr) && *ptr != '=') { ptr++; }
+ while (isspace(*ptr)) { *ptr++ = '\0'; }
+
+ name_length = strlen(name);
+ if (name_length == 0) { return 0; }
+ else if (name_length > CONFIG_FILE_MAX_NAME_LENGTH) { return 0; }
+
+ if (*ptr == '=') {
+ *ptr++ = '\0';
+ while (isspace(*ptr)) { ptr++; }
+ value = ptr;
+ } else if (*ptr) {
+ /* Invalid format! */
+ return 0;
+ } else {
+ value = NULL;
+ }
+
+ value_length = value ? strlen(value) : 0;
+ if (value_length > CONFIG_FILE_MAX_VALUE_LENGTH) { return 0; }
+
+ {
+ char name_buf[CONFIG_FILE_MAX_NAME_LENGTH + 3];
+ config_file_entry *entry = NULL;
+ char *args[4];
+
+ name_buf[0] = name_buf[1] = '-';
+ strcpy(name_buf + 2, name);
+
+ if (value) {
+ const size_t entry_size = sizeof(config_file_entry) + value_length + 1;
+ entry = (config_file_entry *)malloc(entry_size);
+ if (!entry) {
+ fprintf(stderr, "memory allocation failed: %u bytes\n",
+ (unsigned int)entry_size);
+ return -1;
+ }
+ strcpy((char *)(entry + 1), value);
+ entry->next = config_file_entry_head;
+ if (!config_file_entry_head) {
+ atexit(config_file_clear);
+ }
+ config_file_entry_head = entry;
+ }
+
+ args[0] = (char *)path;
+ args[1] = name_buf;
+ args[2] = entry ? (char *)(entry + 1) : NULL;
+ args[3] = NULL;
+ grn_str_getopt(entry ? 3 : 2, args, opts, flags);
+ }
+
+ return 0;
+}
+
+static int
+config_file_load(const char *path, const grn_str_getopt_opt *opts, int *flags)
{
- int name_len, value_len;
- char buf[1024+2], *str, *name, *args[4];
- FILE *file;
-
- if (!(file = fopen(path, "r"))) return 0;
-
- args[0] = (char *)path;
- args[3] = NULL;
- while ((str = fgets(buf + 2, sizeof(buf) - 2, file))) {
- char *value = NULL;
- str = skipspace(str);
- switch (*str) {
- case '#': case ';': case '\0':
- continue;
- }
- name = str;
- while (*str && !isspace(*str) && *str != '=') { str++; }
- if ((name_len = (int)(str - name)) == 0) {
- continue;
- }
- value_len = 0;
- if (*str && (*str == '=' || *(str = skipspace(str)) == '=')) {
- str++;
- value = str = skipspace(str);
- while (*str && *str != '#' && *str != ';') {
- if (!isspace(*str)) {
- value_len = (int)(str - value) + 1;
+ int return_code = 0;
+ char buf[CONFIG_FILE_BUF_SIZE];
+ size_t length = 0;
+ FILE * const file = fopen(path, "rb");
+ if (!file) {
+ return -1;
+ }
+
+ for ( ; ; ) {
+ int c = fgetc(file);
+ if (c == '\r' || c == '\n' || c == EOF) {
+ if (length < sizeof(buf) - 1) {
+ buf[length] = '\0';
+ if (config_file_parse(path, opts, flags, buf)) {
+ return_code = -1;
+ break;
}
- str++;
}
- value[value_len] = '\0';
+ length = 0;
+ } else if (c == '\0') {
+ fprintf(stderr, "config file contains '\\0': %s", path);
+ return_code = -1;
+ break;
+ } else {
+ if (length < sizeof(buf) - 1) {
+ buf[length] = (char)c;
+ }
+ length++;
+ }
+
+ if (c == EOF) {
+ break;
}
- name[name_len] = '\0';
- memset(name -= 2, '-', 2);
- args[1] = name;
- args[2] = value ? strdup(value) : NULL;
- grn_str_getopt((value_len > 0) + 2, args, opts, flags);
}
- fclose(file);
- return 1;
+ fclose(file);
+ return return_code;
}
static const int default_port = DEFAULT_PORT;
@@ -2300,6 +2394,7 @@ main(int argc, char **argv)
const char *port_arg = NULL, *encoding_arg = NULL,
*max_num_threads_arg = NULL, *log_level_arg = NULL,
*bind_address_arg = NULL, *hostname_arg = NULL, *protocol_arg = NULL,
+ *log_path_arg = NULL, *query_log_path_arg = NULL,
*cache_limit_arg = NULL, *default_command_version_arg = NULL,
*default_match_escalation_threshold_arg = NULL;
const char *config_path = NULL;
@@ -2337,8 +2432,8 @@ main(int argc, char **argv)
opts[7].arg = &log_level_arg;
opts[8].arg = &hostname_arg;
opts[11].arg = &protocol_arg;
- opts[13].arg = &grn_log_path;
- opts[14].arg = &grn_qlog_path;
+ opts[13].arg = &log_path_arg;
+ opts[14].arg = &query_log_path_arg;
opts[15].arg = &pidfile_path;
opts[16].arg = &config_path;
opts[18].arg = &cache_limit_arg;
@@ -2350,6 +2445,7 @@ main(int argc, char **argv)
init_default_settings();
+ /* only for parsing --config-path. */
i = grn_str_getopt(argc, argv, opts, &mode);
if (i < 0) {
show_usage(stderr);
@@ -2357,13 +2453,13 @@ main(int argc, char **argv)
}
if (config_path) {
- if (!load_config_file(config_path, opts, &mode)) {
+ if (config_file_load(config_path, opts, &mode)) {
fprintf(stderr, "%s: can't open config file: %s (%s)\n",
argv[0], config_path, strerror(errno));
return EXIT_FAILURE;
}
} else if (*default_config_path) {
- load_config_file(default_config_path, opts, &mode);
+ config_file_load(default_config_path, opts, &mode);
}
/* ignore mode option in config file */
mode = (mode == mode_error) ? default_mode :
@@ -2464,6 +2560,14 @@ main(int argc, char **argv)
do_server = g_server;
}
+ if (log_path_arg) {
+ grn_log_path = log_path_arg;
+ }
+
+ if (query_log_path_arg) {
+ grn_qlog_path = query_log_path_arg;
+ }
+
if (max_num_threads_arg) {
const char * const end = max_num_threads_arg + strlen(max_num_threads_arg);
const char *rest = NULL;