unitparse.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. /* Copyright (c) 2001 Matej Pfajfar.
  2. * Copyright (c) 2001-2004, Roger Dingledine.
  3. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
  4. * Copyright (c) 2007-2019, The Tor Project, Inc. */
  5. /* See LICENSE for licensing information */
  6. /**
  7. * @file unitparse.c
  8. * @brief Functions for parsing values with units from a configuration file.
  9. **/
  10. #include "orconfig.h"
  11. #include "lib/confmgt/unitparse.h"
  12. #include "lib/log/log.h"
  13. #include "lib/log/util_bug.h"
  14. #include "lib/string/parse_int.h"
  15. #include "lib/string/util_string.h"
  16. #include <string.h>
  17. /** Table to map the names of memory units to the number of bytes they
  18. * contain. */
  19. const struct unit_table_t memory_units[] = {
  20. { "", 1 },
  21. { "b", 1<< 0 },
  22. { "byte", 1<< 0 },
  23. { "bytes", 1<< 0 },
  24. { "kb", 1<<10 },
  25. { "kbyte", 1<<10 },
  26. { "kbytes", 1<<10 },
  27. { "kilobyte", 1<<10 },
  28. { "kilobytes", 1<<10 },
  29. { "kilobits", 1<<7 },
  30. { "kilobit", 1<<7 },
  31. { "kbits", 1<<7 },
  32. { "kbit", 1<<7 },
  33. { "m", 1<<20 },
  34. { "mb", 1<<20 },
  35. { "mbyte", 1<<20 },
  36. { "mbytes", 1<<20 },
  37. { "megabyte", 1<<20 },
  38. { "megabytes", 1<<20 },
  39. { "megabits", 1<<17 },
  40. { "megabit", 1<<17 },
  41. { "mbits", 1<<17 },
  42. { "mbit", 1<<17 },
  43. { "gb", 1<<30 },
  44. { "gbyte", 1<<30 },
  45. { "gbytes", 1<<30 },
  46. { "gigabyte", 1<<30 },
  47. { "gigabytes", 1<<30 },
  48. { "gigabits", 1<<27 },
  49. { "gigabit", 1<<27 },
  50. { "gbits", 1<<27 },
  51. { "gbit", 1<<27 },
  52. { "tb", UINT64_C(1)<<40 },
  53. { "tbyte", UINT64_C(1)<<40 },
  54. { "tbytes", UINT64_C(1)<<40 },
  55. { "terabyte", UINT64_C(1)<<40 },
  56. { "terabytes", UINT64_C(1)<<40 },
  57. { "terabits", UINT64_C(1)<<37 },
  58. { "terabit", UINT64_C(1)<<37 },
  59. { "tbits", UINT64_C(1)<<37 },
  60. { "tbit", UINT64_C(1)<<37 },
  61. { NULL, 0 },
  62. };
  63. /** Table to map the names of time units to the number of seconds they
  64. * contain. */
  65. const struct unit_table_t time_units[] = {
  66. { "", 1 },
  67. { "second", 1 },
  68. { "seconds", 1 },
  69. { "minute", 60 },
  70. { "minutes", 60 },
  71. { "hour", 60*60 },
  72. { "hours", 60*60 },
  73. { "day", 24*60*60 },
  74. { "days", 24*60*60 },
  75. { "week", 7*24*60*60 },
  76. { "weeks", 7*24*60*60 },
  77. { "month", 2629728, }, /* about 30.437 days */
  78. { "months", 2629728, },
  79. { NULL, 0 },
  80. };
  81. /** Table to map the names of time units to the number of milliseconds
  82. * they contain. */
  83. const struct unit_table_t time_msec_units[] = {
  84. { "", 1 },
  85. { "msec", 1 },
  86. { "millisecond", 1 },
  87. { "milliseconds", 1 },
  88. { "second", 1000 },
  89. { "seconds", 1000 },
  90. { "minute", 60*1000 },
  91. { "minutes", 60*1000 },
  92. { "hour", 60*60*1000 },
  93. { "hours", 60*60*1000 },
  94. { "day", 24*60*60*1000 },
  95. { "days", 24*60*60*1000 },
  96. { "week", 7*24*60*60*1000 },
  97. { "weeks", 7*24*60*60*1000 },
  98. { NULL, 0 },
  99. };
  100. /** Parse a string <b>val</b> containing a number, zero or more
  101. * spaces, and an optional unit string. If the unit appears in the
  102. * table <b>u</b>, then multiply the number by the unit multiplier.
  103. * On success, set *<b>ok</b> to 1 and return this product.
  104. * Otherwise, set *<b>ok</b> to 0.
  105. */
  106. uint64_t
  107. config_parse_units(const char *val, const unit_table_t *u, int *ok)
  108. {
  109. uint64_t v = 0;
  110. double d = 0;
  111. int use_float = 0;
  112. char *cp;
  113. tor_assert(ok);
  114. v = tor_parse_uint64(val, 10, 0, UINT64_MAX, ok, &cp);
  115. if (!*ok || (cp && *cp == '.')) {
  116. d = tor_parse_double(val, 0, (double)UINT64_MAX, ok, &cp);
  117. if (!*ok)
  118. goto done;
  119. use_float = 1;
  120. }
  121. if (!cp) {
  122. *ok = 1;
  123. v = use_float ? ((uint64_t)d) : v;
  124. goto done;
  125. }
  126. cp = (char*) eat_whitespace(cp);
  127. for ( ;u->unit;++u) {
  128. if (!strcasecmp(u->unit, cp)) {
  129. if (use_float)
  130. v = (uint64_t)(u->multiplier * d);
  131. else
  132. v *= u->multiplier;
  133. *ok = 1;
  134. goto done;
  135. }
  136. }
  137. log_warn(LD_CONFIG, "Unknown unit '%s'.", cp);
  138. *ok = 0;
  139. done:
  140. if (*ok)
  141. return v;
  142. else
  143. return 0;
  144. }
  145. /** Parse a string in the format "number unit", where unit is a unit of
  146. * information (byte, KB, M, etc). On success, set *<b>ok</b> to true
  147. * and return the number of bytes specified. Otherwise, set
  148. * *<b>ok</b> to false and return 0. */
  149. uint64_t
  150. config_parse_memunit(const char *s, int *ok)
  151. {
  152. uint64_t u = config_parse_units(s, memory_units, ok);
  153. return u;
  154. }
  155. /** Parse a string in the format "number unit", where unit is a unit of
  156. * time in milliseconds. On success, set *<b>ok</b> to true and return
  157. * the number of milliseconds in the provided interval. Otherwise, set
  158. * *<b>ok</b> to 0 and return -1. */
  159. int
  160. config_parse_msec_interval(const char *s, int *ok)
  161. {
  162. uint64_t r;
  163. r = config_parse_units(s, time_msec_units, ok);
  164. if (r > INT_MAX) {
  165. log_warn(LD_CONFIG, "Msec interval '%s' is too long", s);
  166. *ok = 0;
  167. return -1;
  168. }
  169. return (int)r;
  170. }
  171. /** Parse a string in the format "number unit", where unit is a unit of time.
  172. * On success, set *<b>ok</b> to true and return the number of seconds in
  173. * the provided interval. Otherwise, set *<b>ok</b> to 0 and return -1.
  174. */
  175. int
  176. config_parse_interval(const char *s, int *ok)
  177. {
  178. uint64_t r;
  179. r = config_parse_units(s, time_units, ok);
  180. if (r > INT_MAX) {
  181. log_warn(LD_CONFIG, "Interval '%s' is too long", s);
  182. *ok = 0;
  183. return -1;
  184. }
  185. return (int)r;
  186. }