1 /**
2 * @file m_jconf.c
3 *
4 * <JA>
5 * @brief ����ե�������ɤ߹���.
6 *
7 * ���ץ�������Ҥ��� jconf ����ե�������ɤ߹��ߤޤ�.
8 * jconf ����ե�������Ǥϡ����֥륯�����ơ������ˤ��ʸ�����
9 * ���ꡤ�Хå�����å���ˤ��ʸ���Υ��������פ��Ǥ��ޤ�.
10 * �ޤ����ƹԤˤ����� '#' �ʹߤϥ����åפ���ޤ�.
11 *
12 * jconf ����ե�������Ǥϡ����Ƥ����Хѥ��ϡ����ץꥱ��������
13 * �����ȥǥ��쥯�ȥ�ǤϤʤ������� jconf ��¸�ߤ���ǥ��쥯�ȥ꤫���
14 * ���Хѥ��Ȥ��Ʋ�ᤵ��ޤ�.
15 *
16 * �ޤ���$HOME, ${HOME}, $(HOME), �η��ǻ��ꤵ�줿��ʬ�ˤĤ���
17 * �Ķ��ѿ���Ÿ���Ǥ��ޤ�.
18 *
19 * </JA>
20 *
21 * <EN>
22 * @brief Read a configuration file.
23 *
24 * These functions are for reading jconf configuration file and set the
25 * parameters into jconf structure. String bracing by double quotation,
26 * and escaping character with backslash are supproted.
27 * Characters after '#' at each line will be ignored.
28 *
29 * Note that all relative paths in jconf file are treated as relative
30 * to the jconf file, not the run-time current directory.
31 *
32 * You can expand environment variables in a format of $HOME, ${HOME} or
33 * $(HOME) in jconf file.
34 *
35 * </EN>
36 *
37 * @author Akinobu Lee
38 * @date Thu May 12 14:16:18 2005
39 *
40 * $Revision: 1.5 $
41 *
42 */
43 /*
44 * Copyright (c) 1991-2007 Kawahara Lab., Kyoto University
45 * Copyright (c) 2000-2005 Shikano Lab., Nara Institute of Science and Technology
46 * Copyright (c) 2005-2007 Julius project team, Nagoya Institute of Technology
47 * All rights reserved
48 */
49
50 #include <julius/julius.h>
51
52 #if defined(_WIN32) && !defined(__CYGWIN32__)
53 #include <mbstring.h>
54 #endif
55
56 #define ISTOKEN(A) (A == ' ' || A == '\t' || A == '\n') ///< Determine token characters
57
58 /**
59 * <JA>
60 * @brief jconf �Ѥι��ɤ߹��ߥ롼����
61 *
62 * �Хå�����å���ˤ�륨����������������� Mac/Win �β��ԥ����ɤ�
63 * �б�����. ���Ԥϥ����åפ��졤���ԥ����ɤϾä����.
64 *
65 * @param buf [out] �ɤ߹����1��ʬ�Υƥ����Ȥ��Ǽ����Хåե�
66 * @param size [in] @a buf ���礭���ʥХ��ȿ���
67 * @param fp [in] �ե�����ݥ���
68 *
69 * @return @a buf ���֤�. EOF �Ǥ���ʾ����Ϥ��ʤ���� NULL ���֤�.
70 * </JA>
71 * <EN>
72 * @brief line reading function for jconf file.
73 *
74 * This function has capability of character escaping and newline codes
75 * on Win/Mac. Blank line will be skipped and newline characters will be
76 * stripped.
77 *
78 * @param buf [out] buffer to store the read text per line
79 * @param size [in] size of @a buf in bytes
80 * @param fp [in] file pointer
81 *
82 * @return @a buf on success, or NULL when encountered EOF and no further input.
83 * </EN>
84 */
85 /* added by H.Banno for Windows & Mac */
86 static char *
fgets_jconf(char * buf,int size,FILE * fp)87 fgets_jconf(char *buf, int size, FILE *fp)
88 {
89 int c, prev_c;
90 int pos;
91
92 if (fp == NULL) return NULL;
93
94 pos = 0;
95 c = '\0';
96 prev_c = '\0';
97 while (1) {
98 if (pos >= size) {
99 pos--;
100 break;
101 }
102
103 c = fgetc(fp);
104 if (c == EOF) {
105 buf[pos] = '\0';
106 if (pos <= 0) {
107 return NULL;
108 } else {
109 return buf;
110 }
111 } else if (c == '\n' || c == '\r') {
112 if (c == '\r' && (c = fgetc(fp)) != '\n') { /* for Mac */
113 ungetc(c, fp);
114 }
115 if (prev_c == '\\') {
116 pos--;
117 } else {
118 break;
119 }
120 } else {
121 buf[pos] = c;
122 pos++;
123
124 #if defined(_WIN32) && !defined(__CYGWIN32__)
125 if (c == '\\' && (_ismbblead(prev_c) && _ismbbtrail(c))) {
126 c = '\0';
127 }
128 #endif
129 }
130 prev_c = c;
131 }
132 buf[pos] = '\0';
133
134 return buf;
135 }
136
137 /**
138 * <JA>
139 * @brief �ե�����Υѥ�̾����ǥ��쥯�ȥ�̾��ȴ���Ф�.
140 *
141 * �Ǹ�� '/' �ϻĤ����.
142 *
143 * @param path [i/o] �ե�����Υѥ�̾�ʴؿ�����ѹ�������
144 * </JA>
145 * <EN>
146 * @brief Get directory name from a path name of a file.
147 *
148 * The trailing slash will be left, and the given buffer will be modified.
149 *
150 * @param path [i/o] file path name, will be modified to directory name
151 * </EN>
152 */
153 void
get_dirname(char * path)154 get_dirname(char *path)
155 {
156 char *p;
157 /* /path/file -> /path/ */
158 /* path/file -> path/ */
159 /* /file -> / */
160 /* file -> */
161 /* ../file -> ../ */
162 p = path + strlen(path) - 1;
163 while (*p != '/'
164 #if defined(_WIN32) && !defined(__CYGWIN32__)
165 && *p != '\\'
166 #endif
167 && p != path) p--;
168 if (p == path && *p != '/') *p = '\0';
169 else *(p+1) = '\0';
170 }
171
172 /**
173 * <JA>
174 * @brief �Ķ��ѿ���Ÿ��
175 *
176 * �Ķ��ѿ���Ÿ������. $HOME �η���ʸ�����Ķ��ѿ��Ȥߤʤ��������ͤ�
177 * �ִ�����. �ִ��������ä��ݤˤϡ�Ϳ����줿ʸ����Хåե���������
178 * �����������餿�˳���դ���줿�Хåե����֤�.
179 *
180 * �ѿ��λ���� $HOME, ${HOME}, $(HOME), �η��ǻ���Ǥ���.
181 * $ ��Ÿ���������ʤ����ϥХå�����å��� "\" �ǥ��������פǤ���.
182 * �ޤ����륯������ "'" �dz��줿�ϰϤ�Ÿ����Ԥ�ʤ�.
183 *
184 * @param str [in] �о�ʸ�����Ÿ��ȯ������������ free �����Τ���ա�
185 *
186 * @return Ÿ�����٤��оݤ��ʤ��ä���硤str �����Τޤ��֤����. Ÿ�����Ԥ�줿��硤���餿�˳���դ���줿Ÿ�����ʸ�����ޤ�Хåե����֤����.
187 * </JA>
188 * <EN>
189 * @brief Envronment valuable expansion for a string
190 *
191 * This function expands environment valuable in a string. When an
192 * expantion occurs, the given buffer will be released inside this
193 * function and newly allocated buffer that holds the resulting string
194 * will be returned.
195 *
196 * Environment valuables should be in a form of $HOME, ${HOME} or $(HOME).
197 * '$' can be escaped by back slash, and strings enbraced by single quote
198 * will be treated as is (no expansion).
199 *
200 * @param str [in] target string
201 *
202 * @return the str itself when no expansion performed, or newly
203 * allocated buffer if expansion occurs.
204 * </EN>
205 */
206 static char *
expand_env(char * str)207 expand_env(char *str)
208 {
209 char *p, *q;
210 char *bgn;
211 char eb;
212 char *target;
213 char *envval;
214 int target_malloclen;
215 int len, n;
216 boolean inbrace;
217 char env[256];
218
219 /* check if string contains '$' and return immediately if not */
220 /* '$' = 36, '\'' = 39 */
221 p = str;
222 inbrace = FALSE;
223 while (*p != '\0') {
224 if (*p == 39) {
225 if (inbrace == FALSE) {
226 inbrace = TRUE;
227 } else {
228 inbrace = FALSE;
229 }
230 p++;
231 continue;
232 }
233 if (! inbrace) {
234 if (*p == '\\') {
235 p++;
236 if (*p == '\0') break;
237 } else {
238 if (*p == 36) break;
239 }
240 }
241 p++;
242 }
243 if (*p == '\0') return str;
244
245 /* prepare result buffer */
246 target_malloclen = strlen(str) * 2;
247 target = (char *)mymalloc(target_malloclen);
248
249 p = str;
250 q = target;
251
252 /* parsing */
253 inbrace = FALSE;
254 while (*p != '\0') {
255
256 /* look for next '$' */
257 while (*p != '\0') {
258 if (*p == 39) {
259 if (inbrace == FALSE) {
260 inbrace = TRUE;
261 } else {
262 inbrace = FALSE;
263 }
264 p++;
265 continue;
266 }
267 if (! inbrace) {
268 if (*p == '\\') {
269 p++;
270 if (*p == '\0') break;
271 } else {
272 if (*p == 36) break;
273 }
274 }
275 *q = *p;
276 p++;
277 q++;
278 n = q - target;
279 if (n >= target_malloclen) {
280 target_malloclen *= 2;
281 target = myrealloc(target, target_malloclen);
282 q = target + n;
283 }
284 }
285 if (*p == '\0') { /* reached end of string */
286 *q = '\0';
287 break;
288 }
289
290 /* move to next */
291 p++;
292
293 /* check for brace */
294 eb = 0;
295 if (*p == '(') {
296 eb = ')';
297 } else if (*p == '{') {
298 eb = '}';
299 }
300
301 /* proceed to find env end point and set the env string to env[] */
302 if (eb != 0) {
303 p++;
304 bgn = p;
305 while (*p != '\0' && *p != eb) p++;
306 if (*p == '\0') {
307 jlog("ERROR: failed to expand variable: no end brace: \"%s\"\n", str);
308 free(target);
309 return str;
310 }
311 } else {
312 bgn = p;
313 while (*p == '_'
314 || (*p >= '0' && *p <= '9')
315 || (*p >= 'a' && *p <= 'z')
316 || (*p >= 'A' && *p <= 'Z')) p++;
317 }
318 len = p - bgn;
319 if (len >= 256 - 1) {
320 jlog("ERROR: failed to expand variable: too long env name: \"%s\"\n", str);
321 free(target);
322 return str;
323 }
324 strncpy(env, bgn, len);
325 env[len] = '\0';
326
327 /* get value */
328 if ((envval = getenv(env)) == NULL) {
329 jlog("ERROR: failed to expand variable: no such variable \"%s\"\n", env);
330 free(target);
331 return str;
332 }
333
334 if (debug2_flag) { /* for debug */
335 jlog("DEBUG: expand $%s to %s\n", env, envval);
336 }
337
338 /* paste value to target */
339 while(*envval != '\0') {
340 *q = *envval;
341 q++;
342 envval++;
343 n = q - target;
344 if (n >= target_malloclen) {
345 target_malloclen *= 2;
346 target = myrealloc(target, target_malloclen);
347 q = target + n;
348 }
349 }
350
351 /* go on to next */
352 if (eb != 0) p++;
353 }
354
355 free(str);
356 return target;
357 }
358
359 /* read-in and parse jconf file and process those using m_options */
360 /**
361 * <JA>
362 * jconf ����ե�������ɤ߹���Dz��Ϥ����б����륪�ץ��������ꤹ��.
363 *
364 * @param conffile [in] jconf �ե�����Υѥ�̾
365 * @param jconf [out] �ͤåȤ��� jconf ����ǡ���
366 * </JA>
367 * <EN>
368 * Read and parse a jconf file, and set the specified option values.
369 *
370 * @param conffile [in] jconf file path name
371 * @param jconf [out] global configuration data to be written.
372 * </EN>
373 *
374 * @callgraph
375 * @callergraph
376 */
377 boolean
config_file_parse(char * conffile,Jconf * jconf)378 config_file_parse(char *conffile, Jconf *jconf)
379 {
380 int c_argc;
381 char **c_argv;
382 FILE *fp;
383 int maxnum, step;
384 static const int len = 512;
385 char buf[len], cpy[len];
386 char *p, *dst, *dst_from;
387 char *cdir;
388 int i;
389 boolean ret;
390
391 jlog("STAT: include config: %s\n", conffile);
392
393 /* set the content of jconf file into argument list c_argv[1..c_argc-1] */
394 /* c_argv[0] will be the original conffile name */
395 /* inside jconf file, quoting by ", ' and escape by '\' is supported */
396 if ((fp = fopen(conffile, "r")) == NULL) {
397 jlog("ERROR: m_jconf: failed to open jconf file: %s\n", conffile);
398 return FALSE;
399 }
400 step = 20;
401 maxnum = step;
402 c_argv = (char **)mymalloc(sizeof(char *) * maxnum);
403 c_argv[0] = strcpy((char *)mymalloc(strlen(conffile)+1), conffile);
404 c_argc = 1;
405 while (fgets_jconf(buf, len, fp) != NULL) {
406 if (buf[0] == '\0') continue;
407 p = buf; dst = cpy;
408 while (1) {
409 while (*p != '\0' && ISTOKEN(*p)) p++;
410 if (*p == '\0') break;
411
412 dst_from = dst;
413
414 while (*p != '\0' && (!ISTOKEN(*p))) {
415 if (*p == '\\') { /* escape by '\' */
416 if (*(++p) == '\0') break;
417 *(dst++) = *(p++);
418 } else {
419 if (*p == '"') { /* quote by "" */
420 p++;
421 while (*p != '\0' && *p != '"') *(dst++) = *(p++);
422 if (*p == '\0') break;
423 p++;
424 } else if (*p == '\'') { /* quote by '' */
425 p++;
426 while (*p != '\0' && *p != '\'') *(dst++) = *(p++);
427 if (*p == '\0') break;
428 p++;
429 } else if (*p == '#') { /* comment out by '#' */
430 *p = '\0';
431 break;
432 } else { /* other */
433 *(dst++) = *(p++);
434 }
435 }
436 }
437 if (dst != dst_from) {
438 *dst = '\0'; dst++;
439 if (c_argc >= maxnum) {
440 maxnum += step;
441 c_argv = (char **)myrealloc(c_argv, sizeof(char *) * maxnum);
442 }
443 c_argv[c_argc++] = strcpy((char*)mymalloc(strlen(dst_from)+1), dst_from);
444 }
445 }
446 }
447 if (fclose(fp) == -1) {
448 jlog("ERROR: m_jconf: cannot close jconf file\n");
449 return FALSE;
450 }
451
452 /* env expansion */
453 for (i=1;i<c_argc;i++) {
454 c_argv[i] = expand_env(c_argv[i]);
455 }
456
457 if (debug2_flag) { /* for debug */
458 jlog("DEBUG: args:");
459 for (i=1;i<c_argc;i++) jlog(" %s",c_argv[i]);
460 jlog("\n");
461 }
462
463 /* now that options are in c_argv[][], call opt_parse() to process them */
464 /* relative paths in jconf file are relative to the jconf file (not current) */
465 cdir = strcpy((char *)mymalloc(strlen(conffile)+1), conffile);
466 get_dirname(cdir);
467 ret = opt_parse(c_argc, c_argv, (cdir[0] == '\0') ? NULL : cdir, jconf);
468 free(cdir);
469
470 /* free arguments */
471 while (c_argc-- > 0) {
472 free(c_argv[c_argc]);
473 }
474 free(c_argv);
475
476 return(ret);
477 }
478
479 /* end of file */
480