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