Open Chinese Convert  0.4.3
A project for conversion between Traditional and Simplified Chinese
 All Data Structures Files Functions Variables Groups Pages
config_reader.c
1 /*
2  * Open Chinese Convert
3  *
4  * Copyright 2010-2013 BYVoid <byvoid@byvoid.com>
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 #include "config_reader.h"
20 #include "dict_group.h"
21 #include "dict_chain.h"
22 
23 #define LINE_BUFFER_SIZE 8192
24 #define CONFIG_DICT_TYPE_OCD "OCD"
25 #define CONFIG_DICT_TYPE_TEXT "TEXT"
26 
27 static config_error errnum = CONFIG_ERROR_VOID;
28 
29 static int qsort_dictionary_buffer_cmp(const void* a, const void* b) {
30  if (((DictMeta*)a)->index < ((DictMeta*)b)->index) {
31  return -1;
32  }
33  if (((DictMeta*)a)->index > ((DictMeta*)b)->index) {
34  return 1;
35  }
36  return ((DictMeta*)a)->stamp < ((DictMeta*)b)->stamp ? -1 : 1;
37 }
38 
39 static int load_dictionary(Config* config) {
40  if (config->dicts_count == 0) {
41  return 0;
42  }
43  // Sort dictionaries
44  qsort(config->dicts,
45  config->dicts_count,
46  sizeof(config->dicts[0]),
47  qsort_dictionary_buffer_cmp);
48  DictGroup* group = dict_chain_add_group(config->dict_chain);
49  size_t last_index = 0;
50  size_t i;
51  for (i = 0; i < config->dicts_count; i++) {
52  if (config->dicts[i].index > last_index) {
53  last_index = config->dicts[i].index;
54  group = dict_chain_add_group(config->dict_chain);
55  }
56  dict_group_load(group,
57  config->dicts[i].file_name,
58  config->dicts[i].dict_type);
59  }
60  return 0;
61 }
62 
63 static int parse_add_dict(Config* config, size_t index, const char* dictstr) {
64  const char* pstr = dictstr;
65  while (*pstr != '\0' && *pstr != ' ') {
66  pstr++;
67  }
68  opencc_dictionary_type dict_type;
69  if (strncmp(dictstr, CONFIG_DICT_TYPE_OCD,
70  sizeof(CONFIG_DICT_TYPE_OCD) - 1) == 0) {
71  dict_type = OPENCC_DICTIONARY_TYPE_DATRIE;
72  } else if (strncmp(dictstr, CONFIG_DICT_TYPE_TEXT,
73  sizeof(CONFIG_DICT_TYPE_OCD) - 1) == 0) {
74  dict_type = OPENCC_DICTIONARY_TYPE_TEXT;
75  } else {
76  errnum = CONFIG_ERROR_INVALID_DICT_TYPE;
77  return -1;
78  }
79  while (*pstr != '\0' && (*pstr == ' ' || *pstr == '\t')) {
80  pstr++;
81  }
82  size_t i = config->dicts_count++;
83  config->dicts[i].dict_type = dict_type;
84  config->dicts[i].file_name = mstrcpy(pstr);
85  config->dicts[i].index = index;
86  config->dicts[i].stamp = config->stamp++;
87  return 0;
88 }
89 
90 static int parse_property(Config* config, const char* key, const char* value) {
91  if (strncmp(key, "dict", 4) == 0) {
92  int index = 0;
93  sscanf(key + 4, "%d", &index);
94  return parse_add_dict(config, index, value);
95  } else if (strcmp(key, "title") == 0) {
96  free(config->title);
97  config->title = mstrcpy(value);
98  return 0;
99  } else if (strcmp(key, "description") == 0) {
100  free(config->description);
101  config->description = mstrcpy(value);
102  return 0;
103  }
104  errnum = CONFIG_ERROR_NO_PROPERTY;
105  return -1;
106 }
107 
108 static int parse_line(const char* line, char** key, char** value) {
109  const char* line_begin = line;
110  while (*line != '\0' && (*line != ' ' && *line != '\t' && *line != '=')) {
111  line++;
112  }
113  size_t key_len = line - line_begin;
114  while (*line != '\0' && *line != '=') {
115  line++;
116  }
117  if (*line == '\0') {
118  return -1;
119  }
120  assert(*line == '=');
121  *key = mstrncpy(line_begin, key_len);
122  line++;
123  while (*line != '\0' && (*line == ' ' || *line == '\t')) {
124  line++;
125  }
126  if (*line == '\0') {
127  free(*key);
128  return -1;
129  }
130  *value = mstrcpy(line);
131  return 0;
132 }
133 
134 static char* parse_trim(char* str) {
135  for (; *str != '\0' && (*str == ' ' || *str == '\t'); str++) {}
136  register char* prs = str;
137  for (; *prs != '\0' && *prs != '\n' && *prs != '\r'; prs++) {}
138  for (prs--; prs > str && (*prs == ' ' || *prs == '\t'); prs--) {}
139  *(++prs) = '\0';
140  return str;
141 }
142 
143 static int parse(Config* config, const char* filename) {
144  char* path = try_open_file(filename);
145  if (path == NULL) {
146  errnum = CONFIG_ERROR_CANNOT_ACCESS_CONFIG_FILE;
147  return -1;
148  }
149  config->file_path = get_file_path(path);
150  FILE* fp = fopen(path, "r");
151  assert(fp != NULL);
152  free(path);
153  skip_utf8_bom(fp);
154  static char buff[LINE_BUFFER_SIZE];
155  while (fgets(buff, LINE_BUFFER_SIZE, fp) != NULL) {
156  char* trimed_buff = parse_trim(buff);
157  if ((*trimed_buff == ';') || (*trimed_buff == '#') ||
158  (*trimed_buff == '\0')) {
159  /* Comment Line or empty line */
160  continue;
161  }
162  char* key = NULL, * value = NULL;
163  if (parse_line(trimed_buff, &key, &value) == -1) {
164  free(key);
165  free(value);
166  fclose(fp);
167  errnum = CONFIG_ERROR_PARSE;
168  return -1;
169  }
170  if (parse_property(config, key, value) == -1) {
171  free(key);
172  free(value);
173  fclose(fp);
174  return -1;
175  }
176  free(key);
177  free(value);
178  }
179  fclose(fp);
180  return 0;
181 }
182 
183 DictChain* config_get_dict_chain(Config* config) {
184  if (config->dict_chain != NULL) {
185  dict_chain_delete(config->dict_chain);
186  }
187  config->dict_chain = dict_chain_new(config);
188  load_dictionary(config);
189  return config->dict_chain;
190 }
191 
192 config_error config_errno(void) {
193  return errnum;
194 }
195 
196 void config_perror(const char* spec) {
197  perr(spec);
198  perr("\n");
199  switch (errnum) {
200  case CONFIG_ERROR_VOID:
201  break;
202  case CONFIG_ERROR_CANNOT_ACCESS_CONFIG_FILE:
203  perror(_("Can not access configuration file"));
204  break;
205  case CONFIG_ERROR_PARSE:
206  perr(_("Configuration file parse error"));
207  break;
208  case CONFIG_ERROR_NO_PROPERTY:
209  perr(_("Invalid property"));
210  break;
211  case CONFIG_ERROR_INVALID_DICT_TYPE:
212  perr(_("Invalid dictionary type"));
213  break;
214  default:
215  perr(_("Unknown"));
216  }
217 }
218 
219 Config* config_open(const char* filename) {
220  Config* config = (Config*)malloc(sizeof(Config));
221  config->title = NULL;
222  config->description = NULL;
223  config->dicts_count = 0;
224  config->stamp = 0;
225  config->dict_chain = NULL;
226  config->file_path = NULL;
227  if (parse(config, filename) == -1) {
228  config_close((Config*)config);
229  return (Config*)-1;
230  }
231  return (Config*)config;
232 }
233 
234 void config_close(Config* config) {
235  size_t i;
236  for (i = 0; i < config->dicts_count; i++) {
237  free(config->dicts[i].file_name);
238  }
239  free(config->title);
240  free(config->description);
241  free(config->file_path);
242  free(config);
243 }