readconf  1.6
設定ファイル読み込みモジュール
 全て データ構造 ファイル 関数 変数 型定義 列挙型 列挙型の値 マクロ定義 グループ
readconf.c
説明を見る。
1 /**
2  * @file
3  * @brief 設定ファイル読み込み関数定義ファイル。
4  * @author tsntsumi
5  * @version 1.6
6  * @since v1.0 2013/12
7  */
8 #if defined(_MSC_VER)
9 #define _CRT_SECURE_NO_WARNINGS
10 #endif
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <stdarg.h>
14 #include <string.h>
15 #include <errno.h>
16 #include <ctype.h>
17 #include <math.h>
18 #if !defined(_MSC_VER)
19 #include <strings.h>
20 #endif
21 #include "readconf.h"
22 
23 #if defined(__GNUC_STDC_INLINE__)
24 #elif defined(__GNUC_GNU_INLINE__)
25 #define inline __inline__
26 #elif defined(_MSC_VER)
27 #define inline __inline
28 #else
29 #define inline
30 #endif
31 
32 #if defined(_MSC_VER)
33 #define strcasecmp _stricmp
34 #define vsnprintf(buf, len, ...) vsnprintf_s(buf, len, (len)-1, __VA_ARGS__)
35 #endif
36 
37 size_t RCNumRaisedErrors = 0;
39 size_t RCMaxNameLength = 64;
40 size_t RCMaxStringLength = 128;
41 
42 /**
43  * @brief 設定ファイルを読み込んでいる時の行番号。
44  */
45 static size_t RCReadConfLineNo = 0;
46 /**
47  * @brief 設定ファイル読み込み中に発生したエラーを格納する配列のサイズ。
48  */
49 static size_t RCRaisedErrorArrayCapacity = 0;
50 /**
51  * @brief エラーの原因の文字列の最大の長さ。
52  */
53 static const size_t RCMaxReasonLength = 256;
54 /**
55  * @brief デリミタ文字の集合。
56  */
57 static const char *const RCDelimiters = " \t\r";
58 /**
59  * @brief コメント開始文字。
60  */
61 static const char RCCommentChar = '#';
62 
63 /**
64  * @brief 指定された文字がデリミタかどうかを真偽値で返します。
65  */
66 #define IS_DELIMITER(c) (strchr(RCDelimiters, c) != NULL)
67 /**
68  * @brief 指定された文字がコメント開始文字かどうかを真偽値で返します。
69  */
70 #define IS_COMMENT(c) (c == RCCommentChar)
71 
72 static void readConf(RCConfItem *items, size_t numItems, FILE *conffp);
73 static void initialize(RCConfItem *items, size_t numItems);
74 static void readExpression(RCConfItem *items, size_t numItems, FILE *conffp);
75 static RCConfItem *findItem(RCConfItem *items, size_t numItems, char *name);
76 static char *readName(FILE *conffp);
77 static void readValue(RCConfItem *item, FILE *conffp);
78 static void readNL(FILE *conffp);
79 static void readString(RCConfItem *item, FILE *conffp);
80 static void readInteger(RCConfItem *item, FILE *conffp);
81 static void readReal(RCConfItem *item, FILE *conffp);
82 static void readBool(RCConfItem *item, FILE *conffp);
83 static bool convertStringToInteger(const char *name, const char *string,
84  RCConvertor convertor, RCConfValue *value);
85 static bool convertStringToReal(const char *name, const char *string,
86  RCConvertor convertor, RCConfValue *value);
87 static bool convertStringToBool(const char *name, const char *string,
88  RCConfValue *value);
89 static char *readQuotedString(FILE *conffp);
90 static char *readBareString(FILE *conffp);
91 static void skipToEndOfLine(FILE *conffp);
92 static void skipWhitespaces(FILE *conffp);
93 static void addError(RCErrorNo errorNo, char *format, ...);
94 
95 
96 bool readconf(RCConfItem *items, size_t numItems, const char *confPath)
97 {
98  FILE *conffp;
99 
100  if (numItems <= 0)
101  {
103  "no item was specified. numItems = %d", numItems);
104  return false;
105  }
106  if (items == NULL)
107  {
108  addError(RCErrorNullItem, "null items were specified.");
109  return false;
110  }
111 
112  initialize(items, numItems);
113 
114  conffp = fopen(confPath, "r");
115  if (conffp == NULL)
116  {
118  "cannot open conf file '%s'. %s",
119  confPath, strerror(errno));
120  return false;
121  }
122 
123  readConf(items, numItems, conffp);
124 
125  fclose(conffp);
126  return RCNumRaisedErrors == 0;
127 }
128 
129 void RCFreeStringValueInItems(RCConfItem *items, size_t numItems)
130 {
131  size_t i;
132 
133  for (i = 0; i < numItems; i ++)
134  {
135  if (items[i].type == RCValueTypeString)
136  {
137  free(items[i].value.string);
138  items[i].value.string = NULL;
139  free(items[i].defaultValue.string);
140  items[i].defaultValue.string = NULL;
141  }
142  }
143 }
144 
146 {
147  size_t i;
148 
149  for (i = 0; i < RCNumRaisedErrors; i ++)
150  {
151  free(RCRaisedErrors[i].reason);
152  }
153  free(RCRaisedErrors);
154  RCRaisedErrors = NULL;
155  RCNumRaisedErrors = 0;
157 }
158 
159 char *RCDuplicateString(const char *str)
160 {
161  size_t length;
162  char *dup;
163 
164  if (str == NULL)
165  {
166  return NULL;
167  }
168  length = strlen(str);
169  dup = (char*)calloc(1, length + 1);
170  if (dup != NULL)
171  {
172  strcpy(dup, str);
173  }
174  return dup;
175 }
176 
177 /**
178  * @brief 設定ファイルのストリームから設定を読み込み、指定の構造体に格納します。
179  * @param[inout] items 読み込むべき設定項目の名前とデフォルト値を設定した
180  * 構造体配列へのポインタ。
181  * 読み込んだ設定項目の値を格納するのにも使用されます。
182  * @param[in] numItems 構造体配列 items の項目数。
183  * @param[in] conffp 読み込む設定ファイルのストリーム。
184  */
185 static void readConf(RCConfItem *items, size_t numItems, FILE *conffp)
186 {
187  RCReadConfLineNo = 1;
188  while (!feof(conffp))
189  {
190  readExpression(items, numItems, conffp);
191  readNL(conffp);
192  }
193 }
194 
195 /**
196  * @brief 設定ファイルを読み込む前の初期化を行います。
197  * @details
198  * - 行番号を 0 に設定。
199  * - デフォルト値を初期値として設定。
200  * - 定義行番号を 0 に設定。
201  *
202  * @param[inout] items 初期化する設定項目の構造体配列。
203  * @param[in] numItems 設定項目の数。
204  */
205 static void initialize(RCConfItem *items, size_t numItems)
206 {
207  RCConfItem *item = items;
208  size_t i;
209 
210  RCReadConfLineNo = 0;
211  for (i = 0; i < numItems; i ++, item ++)
212  {
213  if (item->type == RCValueTypeString)
214  {
215  if (item->defaultAsString == NULL)
216  {
217  item->value.string = NULL;
218  }
219  else
220  {
222  if (item->defaultValue.string == NULL)
223  {
225  "cannot copy default value of %s. %s",
226  item->name,
227  strerror(errno));
228  }
230  if (item->value.string == NULL)
231  {
233  "cannot copy default value of %s. %s",
234  item->name,
235  strerror(errno));
236  }
237  }
238  }
239  else if (item->type == RCValueTypeInteger)
240  {
241  if (!convertStringToInteger(item->name,
242  item->defaultAsString,
243  item->convertor,
244  &item->defaultValue))
245  {
246  item->defaultValue.integer = 0;
247  }
248  item->value.integer = item->defaultValue.integer;
249  }
250  else if (item->type == RCValueTypeReal)
251  {
252  if (!convertStringToReal(item->name,
253  item->defaultAsString,
254  item->convertor,
255  &item->defaultValue))
256  {
257  item->defaultValue.real = 0.0;
258  }
259  item->value.real = item->defaultValue.real;
260  }
261  else if (item->type == RCValueTypeBool)
262  {
263  if (!convertStringToBool(item->name,
264  item->defaultAsString,
265  &item->defaultValue))
266  {
267  item->defaultValue.boolean = false;
268  }
269  item->value.boolean = item->defaultValue.boolean;
270  }
271  item->lineNo = 0;
272  }
273 }
274 
275 /**
276  * @brief 設定項目の式を読み込みます。
277  * @param[inout] items 読み込んだ式の値を格納する設定項目配列。
278  * @param[in] numItems 設定項目配列の要素数。
279  * @param[in] conffp 設定式を読み込むストリーム。
280  */
281 static void readExpression(RCConfItem *items, size_t numItems, FILE *conffp)
282 {
283  int c;
284  char *name;
285  RCConfItem *dest;
286 
287  skipWhitespaces(conffp);
288  c = fgetc(conffp);
289  ungetc(c, conffp);
290  if (c == EOF || c == '\n')
291  {
292  return;
293  }
294  name = readName(conffp);
295  if (name == NULL)
296  {
297  return;
298  }
299  skipWhitespaces(conffp);
300  c = fgetc(conffp);
301  if (c != '=')
302  {
303  addError(RCErrorSyntax, "syntax error. missing '='.");
304  free(name);
305  skipToEndOfLine(conffp);
306  return;
307  }
308  dest = findItem(items, numItems, name);
309  if (dest == NULL)
310  {
311  addError(RCErrorUnexpectedName, "unexpected name '%s' appeared.", name);
312  free(name);
313  skipToEndOfLine(conffp);
314  return;
315  }
316  if (dest->lineNo > 0)
317  {
319  "redefinition of '%s'. previous definition is at line %lu.",
320  name, dest->lineNo);
321  }
322  readValue(dest, conffp);
323  free(name);
324 }
325 
326 /**
327  * @brief 指定された名前の設定項目を検索します。
328  * @param[in] items 検索対象の設定項目配列。
329  * @param[in] numItems 検索対象の設定項目の数。
330  * @param[in] name 検索する名前。
331  * @return 見つかった設定項目。見つからなかったら NULL。
332  */
333 static RCConfItem *findItem(RCConfItem *items, size_t numItems, char *name)
334 {
335  size_t i;
336  RCConfItem *p = items;
337 
338  for (i = 0; i < numItems; i ++, p ++)
339  {
340  if (strcasecmp(p->name, name) == 0)
341  return p;
342  }
343  return NULL;
344 }
345 
346 /**
347  * @brief 文字列が格納されているメモリ領域を文字列の長さで切り詰めます。
348  * @param str 文字列が格納されているメモリ領域。
349  * @return 切り詰めた文字列。
350  */
351 static inline char *shrinkString(char *str)
352 {
353  char *shrinked = RCDuplicateString(str);
354  free(str);
355  return shrinked;
356 }
357 
358 /**
359  * @brief 設定ファイルから項目名を読み込みます。
360  * @param conffp 読み込む設定ファイルストリーム。
361  * @return 読み込んだ項目名。
362  */
363 static char *readName(FILE *conffp)
364 {
365  char *name = (char*)calloc(RCMaxNameLength, 1);
366  size_t i = 0;
367  int c;
368 
369  if (name == NULL)
370  {
371  addError(RCErrorNoMemory, "cannot allocate name. %s", strerror(errno));
372  return NULL;
373  }
374 
375  while ((c = fgetc(conffp)) != EOF)
376  {
377  if (c == '\n' || c == '=')
378  {
379  ungetc(c, conffp);
380  break;
381  }
382  if (IS_DELIMITER(c) || IS_COMMENT(c))
383  {
384  break;
385  }
386  name[i++] = (char)c;
387  if (i > RCMaxNameLength)
388  {
389  name[i-1] = '\0';
391  "name length is longer than %lu.", RCMaxNameLength);
392  skipToEndOfLine(conffp);
393  free(name);
394  return NULL;
395  }
396  }
397 
398  if (i == 0)
399  {
400  addError(RCErrorSyntax, "no name specified.");
401  free(name);
402  skipToEndOfLine(conffp);
403  return NULL;
404  }
405 
406  return shrinkString(name);
407 }
408 
409 /**
410  * @brief 指定された項目に値を設定します。
411  * @param item 値を設定する項目構造体。
412  * @param value 設定する値。
413  */
414 static inline void setValue(RCConfItem *item, RCConfValue value)
415 {
416  if (item->type == RCValueTypeString)
417  {
418  free(item->value.string);
419  item->value.string = RCDuplicateString(value.string);
420  }
421  else
422  {
423  item->value = value;
424  }
425 }
426 
427 /**
428  * @brief 設定ファイルの値を読み込みます。
429  * @param[inout] item 読み込んだ値を格納する設定項目構造体。
430  * @param[in] conffp 設定ファイルストリーム。
431  * @return 読み込んだ設定ファイルを格納した設定項目構造体へのポインタ。
432  */
433 static void readValue(RCConfItem *item, FILE *conffp)
434 {
435  switch (item->type)
436  {
437  case RCValueTypeString:
438  readString(item, conffp);
439  break;
440  case RCValueTypeInteger:
441  readInteger(item, conffp);
442  break;
443  case RCValueTypeReal:
444  readReal(item, conffp);
445  break;
446  case RCValueTypeBool:
447  readBool(item, conffp);
448  break;
449  default:
450  return;
451  }
452  if (item->validator == NULL)
453  {
454  return;
455  }
456  if (!item->validator(item->name, item->type, item->value))
457  {
458  addError(RCErrorValidationFailed, "item value validation failed.");
459  setValue(item, item->defaultValue);
460  }
461 }
462 
463 /**
464  * @brief 改行文字を読み込みます。
465  * @details 空白文字を読み飛ばした後に改行文字が現れなければ、
466  * エラーになります。
467  * @param conffp 設定ファイルストリーム。
468  */
469 static void readNL(FILE *conffp)
470 {
471  int c;
472 
473  skipWhitespaces(conffp);
474  c = fgetc(conffp);
475  if (c == EOF)
476  {
477  return;
478  }
479  if (c != '\n')
480  {
481  char *nextString;
482 
483  ungetc(c, conffp);
484  nextString = readBareString(conffp);
485  addError(RCErrorSyntax, "unexpected string '%s' appeared.", nextString);
486  free(nextString);
487  skipToEndOfLine(conffp);
488  return;
489  }
490  RCReadConfLineNo ++;
491 }
492 
493 /**
494  * @brief 文字列を読み込み、設定項目構造体に登録します。
495  * @details 読み込む文字列は、引用符で囲まれている場合、
496  * 両端の引用符を取り除きます。引用符で囲まれていない場合は、
497  * コメント開始文字か改行文字の直前までを読み込み、
498  * 末尾の空白文字を取り除きます。
499  * @param[out] item 読み込んだ文字列を登録する構造体。
500  * @param[in] conffp 設定ファイルストリーム。
501  */
502 static void readString(RCConfItem *item, FILE *conffp)
503 {
504  int c;
505  char *string = NULL;
506 
507  skipWhitespaces(conffp);
508  c = fgetc(conffp);
509  if (c == '"' || c == '\'')
510  {
511  ungetc(c, conffp);
512  string = readQuotedString(conffp);
513  }
514  else
515  {
516  ungetc(c, conffp);
517  string = readBareString(conffp);
518  }
519  if (string == NULL)
520  {
521  return;
522  }
523  free(item->value.string);
524  item->value.string = string;
525  item->lineNo = RCReadConfLineNo;
526 }
527 
528 /**
529  * @brief 整数値を読み込み、設定項目構造体に登録します。
530  * @details 整数を構成する文字以外を読み込んだ場合は、
531  * エラーにして登録は行いません。例えば「.」が含まれる場合は、
532  * 整数ではなく実数を指定したということでエラーになります。
533  *
534  * 整数値として「0」や「0x」を接頭辞とする数値を指定できます。
535  * 「0」で始まる場合は 8 進数です。そのため「8, 9」が続いた場合は、
536  * エラーになります。また、「0x」で始まる場合は 16 進数です。
537  * @param[out] item 読み込んだ整数値を登録する構造体。
538  * @param[in] conffp 設定ファイルストリーム。
539  */
540 static void readInteger(RCConfItem *item, FILE *conffp)
541 {
542  char *string;
543 
544  skipWhitespaces(conffp);
545  string = readBareString(conffp);
546  if (string == NULL)
547  {
548  return;
549  }
550  if (!convertStringToInteger(item->name, string, item->convertor, &item->value))
551  {
552  item->value.integer = item->defaultValue.integer;
553  }
554  item->lineNo = RCReadConfLineNo;
555  free(string);
556 }
557 
558 /**
559  * @brief 実数値を読み込み、設定項目構造体に登録します。
560  * @details 実数を構成する文字以外を読み込んだ場合は、
561  * エラーにして登録は行いません。
562  *
563  * 実数は次の正規表現で表すことが出来ます。
564  * - <code>[-+]?[0-9]*(\.[0-9]*([eE][-+]?[0-9]+)?)?</code>
565  *
566  * 例えば次のような文字列は実数値です。
567  * - <code>3.14</code>
568  * - <code>+0.</code>
569  * - <code>-.05</code>
570  * - <code>141421356e-8</code>
571  *
572  * @param[out] item 読み込んだ実数値を登録する構造体。
573  * @param[in] conffp 設定ファイルストリーム。
574  */
575 static void readReal(RCConfItem *item, FILE *conffp)
576 {
577  char *string;
578 
579  skipWhitespaces(conffp);
580  string = readBareString(conffp);
581  if (string == NULL)
582  {
583  return;
584  }
585  if (!convertStringToReal(item->name, string, item->convertor, &item->value))
586  {
587  item->value.real = item->defaultValue.real;
588  }
589  item->lineNo = RCReadConfLineNo;
590  free(string);
591 }
592 
593 /**
594  * @brief 真偽値を読み込み、設定項目構造体に登録します。
595  * @details 真偽値を構成する文字列以外を読み込んだ場合は、
596  * エラーにして登録は行いません。
597  *
598  * 例えば次のような文字列は真偽値です。大文字と小文字は同一視します。
599  * - <code>true, false</code>
600  * - <code>yes, no</code>
601  *
602  * @param[out] item 読み込んだ真偽値を登録する構造体。
603  * @param[in] conffp 設定ファイルストリーム。
604  */
605 static void readBool(RCConfItem *item, FILE *conffp)
606 {
607  char *string;
608 
609  skipWhitespaces(conffp);
610  string = readBareString(conffp);
611  if (string == NULL)
612  {
613  return;
614  }
615  if (!convertStringToBool(item->name, string, &item->value))
616  {
617  item->value.boolean = item->defaultValue.boolean;
618  }
619  item->lineNo = RCReadConfLineNo;
620  free(string);
621 }
622 
623 /**
624  * @brief 文字列を整数値に変換します。
625  *
626  * @param[in] name 変換する設定項目の名前。
627  * @param[in] string 変換する文字列。
628  * @param[in] convertor 変換関数。
629  * @param[out] value 変換した真偽値を格納する変数ポインタ。
630  * @return 変換に成功したら true 、変換できなかったら false 。
631  */
632 static bool convertStringToInteger(const char *name,
633  const char *string,
634  RCConvertor convertor,
635  RCConfValue *value)
636 {
637  if (convertor == NULL)
638  {
639  char *endptr = NULL;
640  long n;
641 
642  n = strtol(string, &endptr, 0);
643  if (*endptr != '\0')
644  {
646  "value of %s is not a number. '%c' in '%s'",
647  name, *endptr, string);
648  return false;
649  }
650  value->integer = n;
651  }
652  else if (!convertor(name, RCValueTypeInteger, string, value))
653  {
655  "cannot convert constant name '%s' of %s to integer",
656  string, name);
657  return false;
658  }
659  return true;
660 }
661 
662 /**
663  * @brief 文字列を実数値に変換します。
664  *
665  * @param[in] name 変換する設定項目の名前。
666  * @param[in] string 変換する文字列。
667  * @param[in] convertor 変換関数。
668  * @param[out] value 変換した真偽値を格納する変数ポインタ。
669  * @return 変換に成功したら true 、変換できなかったら false 。
670  */
671 static bool convertStringToReal(const char *name,
672  const char *string,
673  RCConvertor convertor,
674  RCConfValue *value)
675 {
676  if (convertor == NULL)
677  {
678  char *endptr = NULL;
679  double r = 0.0;
680 
681  r = strtod(string, &endptr);
682  if (*endptr != '\0')
683  {
685  "value of %s is not a number. '%c' in '%s'",
686  name, *endptr, string);
687  return false;
688  }
689  value->real = r;
690  }
691  else if (!convertor(name, RCValueTypeReal, string, value))
692  {
694  "cannot convert constant name '%s' of %s to real",
695  string, name);
696  return false;
697  }
698  return true;
699 }
700 
701 /**
702  * @brief 文字列を真偽値に変換します。
703  *
704  * @param[in] name 変換する設定項目の名前。
705  * @param[in] string 変換する文字列。
706  * @param[out] value 変換した真偽値を格納する変数へのポインタ。
707  * @return 変換に成功したら true 、変換できなかったら false 。
708  */
709  static bool convertStringToBool(const char *name,
710  const char *string,
711  RCConfValue *value)
712 {
713  size_t i;
714  static struct
715  {
716  char *name;
717  bool value;
718  } boolNames[] =
719  {
720  { "true", true },
721  { "false", false },
722  { "yes", true },
723  { "no", false },
724  };
725 
726  for (i = 0; i < ELEMENTSOF(boolNames); i ++)
727  {
728  if (strcasecmp(string, boolNames[i].name) == 0)
729  {
730  value->boolean = boolNames[i].value;
731  return true;
732  }
733  }
734  addError(RCErrorBoolValue, "not a bool value '%s' of %s.", string, name);
735  return false;
736 }
737 
738 /**
739  * @brief 引用符で囲まれた文字列を読み込みます。
740  * @details 両端の引用符は取り除きます。
741  *
742  * 読み込む文字列の最大長は、グローバル変数
743  * @link RCMaxStringLength @endlink に格納された値です。
744  * この値は終端文字 <code>\\0</code> を含む長さになります。
745  * 読み込む際に、この変数の長さのバッファを割り当てて読み込みに使用します。
746  * 読み込んだ後に、読み込んだ長さ分の文字列領域を割り当てて、
747  * バッファからコピーすることで文字列の使用メモリ容量を節約します。
748  *
749  * @param conffp 設定ファイルストリーム。
750  * @return 読み込んだ文字列。エラーが発生した場合は NULL。
751  */
752 static char *readQuotedString(FILE *conffp)
753 {
754  char *string = (char*)calloc(RCMaxStringLength, 1);
755  size_t i;
756  int quote;
757  int c;
758 
759  if (string == NULL)
760  {
761  addError(RCErrorNoMemory, "cannot allocate string. %s", strerror(errno));
762  return NULL;
763  }
764 
765  i = 0;
766  quote = fgetc(conffp);
767  while ((c = fgetc(conffp)) != quote)
768  {
769  if (c == EOF)
770  {
771  addError(RCErrorSyntax, "unterminated string without %c.", quote);
772  free(string);
773  return NULL;
774  }
775  if (c == '\n')
776  {
777  ungetc(c, conffp);
778  addError(RCErrorSyntax, "unterminated string without %c.", quote);
779  free(string);
780  return NULL;
781  }
782  string[i++] = (char)c;
783  if (i > RCMaxStringLength)
784  {
785  string[i-1] = '\0';
787  "stirng length longer than %lu.", RCMaxStringLength);
788  skipToEndOfLine(conffp);
789  free(string);
790  return NULL;
791  }
792  }
793 
794  return shrinkString(string);
795 }
796 
797 /**
798  * @brief コメント開始文字か改行文字が現れるまでを文字列として読み込みます。
799  * @details 読み込んだ文字列の末尾の空白文字は取り除きます。
800  *
801  * 読み込む文字列の最大長は、グローバル変数
802  * @link RCMaxStringLength @endlink に格納された値です。
803  * この値は終端文字 <code>\\0</code> を含む長さになります。
804  * 読み込む際に、この変数の長さのバッファを割り当てて読み込みに使用します。
805  * 読み込んだ後に、読み込んだ長さ分の文字列領域を割り当てて、
806  * バッファからコピーすることで文字列の使用メモリ容量を節約します。
807  *
808  * @param conffp 設定ファイルストリーム。
809  * @return 読み込んだ文字列。エラーが発生した場合は NULL。
810  */
811 static char *readBareString(FILE *conffp)
812 {
813  char *string = (char*)calloc(RCMaxStringLength, 1);
814  size_t i = 0;
815  int c;
816 
817  if (string == NULL)
818  {
819  addError(RCErrorNoMemory, "cannot allocate string. %s", strerror(errno));
820  return NULL;
821  }
822 
823  while ((c = fgetc(conffp)) != EOF)
824  {
825  if (c == '\n' || IS_COMMENT(c))
826  {
827  ungetc(c, conffp);
828  break;
829  }
830  string[i++] = (char)c;
831  if (i > RCMaxStringLength)
832  {
833  string[i-1] = '\0';
835  "string length is longer than %lu.", RCMaxStringLength);
836  skipToEndOfLine(conffp);
837  free(string);
838  return NULL;
839  }
840  }
841 
842  for (; i > 0; i --)
843  {
844  if (!IS_DELIMITER(string[i - 1]))
845  {
846  break;
847  }
848  }
849 
850  if (i == 0)
851  {
852  addError(RCErrorSyntax, "syntax error. unexpected NL.");
853  free(string);
854  return NULL;
855  }
856 
857  string[i] = '\0';
858 
859  return shrinkString(string);
860 }
861 
862 /**
863  * @brief 改行文字の直前までを読み捨てます。
864  * @param conffp 設定ファイルストリーム。
865  */
866 static void skipToEndOfLine(FILE* conffp)
867 {
868  int c;
869 
870  while ((c = fgetc(conffp)) != EOF)
871  {
872  if (c == '\n')
873  {
874  ungetc(c, conffp);
875  break;
876  }
877  }
878  return;
879 }
880 
881 /**
882  * @brief 連続する空白文字を読み捨てます。
883  * @details コメント開始文字が現れた場合は、
884  * 続くコメントを空白と見なして改行文字の前までを読み捨てます。
885  * @param conffp 設定ファイルストリーム。
886  */
887 static void skipWhitespaces(FILE *conffp)
888 {
889  int c;
890 
891  while ((c = fgetc(conffp)) != EOF)
892  {
893  if (IS_COMMENT(c))
894  {
895  skipToEndOfLine(conffp);
896  break;
897  }
898  if (IS_DELIMITER(c) == false)
899  {
900  ungetc(c, conffp);
901  break;
902  }
903  }
904 }
905 
906 /**
907  * @brief 指定されたエラー番号と文字列を、エラー情報構造体配列に追加します。
908  * @param errorNo エラー番号。
909  * @param format エラー理由書式文字列。
910  * @param ... format 引数
911  */
912 static void addError(RCErrorNo errorNo, char *format, ...)
913 {
914  RCError *errors;
915  char *reason;
916 
918  {
920  {
922  }
923  else
924  {
926  }
927  errors = (RCError*)realloc(RCRaisedErrors,
929  if (errors == NULL)
930  {
931  return;
932  }
933  RCRaisedErrors = errors;
934  }
935  RCRaisedErrors[RCNumRaisedErrors].errorNo = errorNo;
936  RCRaisedErrors[RCNumRaisedErrors].lineNo = RCReadConfLineNo;
937  RCRaisedErrors[RCNumRaisedErrors].reason = NULL;
938  reason = (char*)calloc(RCMaxReasonLength, 1);
939  if (reason != NULL)
940  {
941  va_list ap;
942  va_start(ap, format);
943  vsnprintf(reason, RCMaxReasonLength, format, ap);
944  va_end(ap);
945  RCRaisedErrors[RCNumRaisedErrors].reason = shrinkString(reason);
946  }
948 }
949 
950 /*
951  * Local variables: ***
952  * coding: utf-8-unix ***
953  * mode: C ***
954  * c-file-style: "gnu" ***
955  * tab-width: 8 ***
956  * indent-tabs-mode: nil ***
957  * End: ***
958  */
実数型(double)。
Definition: readconf.h:34
static void skipWhitespaces(FILE *conffp)
連続する空白文字を読み捨てます。
Definition: readconf.c:887
RCConvertor convertor
読み込んだ値文字列を数値に変換する関数。
Definition: readconf.h:89
static bool convertStringToReal(const char *name, const char *string, RCConvertor convertor, RCConfValue *value)
文字列を実数値に変換します。
Definition: readconf.c:671
static void readValue(RCConfItem *item, FILE *conffp)
設定ファイルの値を読み込みます。
Definition: readconf.c:433
static bool convertStringToInteger(const char *name, const char *string, RCConvertor convertor, RCConfValue *value)
文字列を整数値に変換します。
Definition: readconf.c:632
static void readExpression(RCConfItem *items, size_t numItems, FILE *conffp)
設定項目の式を読み込みます。
Definition: readconf.c:281
char * RCDuplicateString(const char *str)
指定された文字列を複製します。
Definition: readconf.c:159
設定項目の構造体。
Definition: readconf.h:78
bool(* RCConvertor)(const char *name, RCValueType type, const char *string, RCConfValue *value)
設定項目値文字列を数値に変換する関数。
Definition: readconf.h:64
static void readNL(FILE *conffp)
改行文字を読み込みます。
Definition: readconf.c:469
#define IS_COMMENT(c)
指定された文字がコメント開始文字かどうかを真偽値で返します。
Definition: readconf.c:70
static const size_t RCMaxReasonLength
エラーの原因の文字列の最大の長さ。
Definition: readconf.c:53
size_t RCMaxStringLength
設定ファイルから文字列を読み込む際の最大の長さ。
Definition: readconf.c:40
設定項目が再定義されました。
Definition: readconf.h:129
static char * readBareString(FILE *conffp)
コメント開始文字か改行文字が現れるまでを文字列として読み込みます。
Definition: readconf.c:811
RCValueType type
項目の型。
Definition: readconf.h:83
定数名を数値に変換できませんでした。
Definition: readconf.h:137
static bool convertStringToBool(const char *name, const char *string, RCConfValue *value)
文字列を真偽値に変換します。
Definition: readconf.c:709
long integer
Definition: readconf.h:46
size_t lineNo
発生した行番号。
Definition: readconf.h:148
文字列型。
Definition: readconf.h:30
設定ファイル読み込み関数用構造体等定義ファイル。
#define ELEMENTSOF(ARRAY)
Definition: readconf.h:250
static size_t RCRaisedErrorArrayCapacity
設定ファイル読み込み中に発生したエラーを格納する配列のサイズ。
Definition: readconf.c:49
RCErrorNo
エラー番号。
Definition: readconf.h:106
検証に失敗しました。
Definition: readconf.h:133
static void addError(RCErrorNo errorNo, char *format,...)
指定されたエラー番号と文字列を、エラー情報構造体配列に追加します。
Definition: readconf.c:912
真偽値の値が間違っています。
Definition: readconf.h:135
真偽値型(bool)。
Definition: readconf.h:36
#define IS_DELIMITER(c)
指定された文字がデリミタかどうかを真偽値で返します。
Definition: readconf.c:66
RCError * RCRaisedErrors
エラー情報格納配列。
Definition: readconf.c:38
char * string
Definition: readconf.h:45
RCConfValue value
読み込んだ値。
Definition: readconf.h:93
static void readReal(RCConfItem *item, FILE *conffp)
実数値を読み込み、設定項目構造体に登録します。
Definition: readconf.c:575
エラー情報格納構造体。
Definition: readconf.h:143
void RCFreeStringValueInItems(RCConfItem *items, size_t numItems)
指定された設定項目構造体配列の文字列を解放します。
Definition: readconf.c:129
予期しない設定項目名が現れました。
Definition: readconf.h:127
static void readConf(RCConfItem *items, size_t numItems, FILE *conffp)
設定ファイルのストリームから設定を読み込み、指定の構造体に格納します。
Definition: readconf.c:185
static const char RCCommentChar
コメント開始文字。
Definition: readconf.c:61
static void readInteger(RCConfItem *item, FILE *conffp)
整数値を読み込み、設定項目構造体に登録します。
Definition: readconf.c:540
RCErrorNo errorNo
エラー番号。
Definition: readconf.h:146
size_t lineNo
読み込んだ時の行番号。
Definition: readconf.h:95
static void setValue(RCConfItem *item, RCConfValue value)
指定された項目に値を設定します。
Definition: readconf.c:414
static void readBool(RCConfItem *item, FILE *conffp)
真偽値を読み込み、設定項目構造体に登録します。
Definition: readconf.c:605
読み込み先の設定項目構造体が指定されませんでした。
Definition: readconf.h:111
double real
Definition: readconf.h:47
static void readString(RCConfItem *item, FILE *conffp)
文字列を読み込み、設定項目構造体に登録します。
Definition: readconf.c:502
static const char *const RCDelimiters
デリミタ文字の集合。
Definition: readconf.c:57
static void initialize(RCConfItem *items, size_t numItems)
設定ファイルを読み込む前の初期化を行います。
Definition: readconf.c:205
static char * readQuotedString(FILE *conffp)
引用符で囲まれた文字列を読み込みます。
Definition: readconf.c:752
メモリを確保できませんでした。
Definition: readconf.h:117
読み込もうとした文字列が長すぎました。
Definition: readconf.h:119
char * name
項目名。
Definition: readconf.h:81
char * defaultAsString
デフォルト値の文字列表現。
Definition: readconf.h:85
設定項目値の共用体。
Definition: readconf.h:43
読み込もうとした名前が長すぎました。
Definition: readconf.h:121
RCValidator validator
読み込んだ値を検証する関数。
Definition: readconf.h:87
static char * shrinkString(char *str)
文字列が格納されているメモリ領域を文字列の長さで切り詰めます。
Definition: readconf.c:351
static size_t RCReadConfLineNo
設定ファイルを読み込んでいる時の行番号。
Definition: readconf.c:45
char * reason
発生した理由。
Definition: readconf.h:150
整数型(long)。
Definition: readconf.h:32
bool readconf(RCConfItem *items, size_t numItems, const char *confPath)
指定の設定ファイルから設定を読み込み、指定された構造体配列に格納します。
Definition: readconf.c:96
読み込み先の設定項目構造体がNULLでした。
Definition: readconf.h:113
static char * readName(FILE *conffp)
設定ファイルから項目名を読み込みます。
Definition: readconf.c:363
設定ファイルの構文誤りです。
Definition: readconf.h:123
RCConfValue defaultValue
デフォルト値。
Definition: readconf.h:91
static void skipToEndOfLine(FILE *conffp)
改行文字の直前までを読み捨てます。
Definition: readconf.c:866
void RCFreeRaisedErrors(void)
readconf() による読み込み処理で発生したエラー情報構造体配列を解放します。
Definition: readconf.c:145
設定ファイルをオープンできませんでした。
Definition: readconf.h:115
数値の形式が間違っています。
Definition: readconf.h:131
size_t RCNumRaisedErrors
エラー情報数。
Definition: readconf.c:37
static RCConfItem * findItem(RCConfItem *items, size_t numItems, char *name)
指定された名前の設定項目を検索します。
Definition: readconf.c:333
size_t RCMaxNameLength
設定ファイルから項目名を読み込む際の最大の長さ。
Definition: readconf.c:39
bool boolean
Definition: readconf.h:48