unitparse.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  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 (BUG(!cp)) {
  122. // cp should always be non-NULL if the parse operation succeeds.
  123. // LCOV_EXCL_START
  124. *ok = 1;
  125. v = use_float ? ((uint64_t)d) : v;
  126. goto done;
  127. // LCOV_EXCL_STOP
  128. }
  129. cp = (char*) eat_whitespace(cp);
  130. for ( ;u->unit;++u) {
  131. if (!strcasecmp(u->unit, cp)) {
  132. if (use_float)
  133. v = (uint64_t)(u->multiplier * d);
  134. else
  135. v *= u->multiplier;
  136. *ok = 1;
  137. goto done;
  138. }
  139. }
  140. log_warn(LD_CONFIG, "Unknown unit '%s'.", cp);
  141. *ok = 0;
  142. done:
  143. if (*ok)
  144. return v;
  145. else
  146. return 0;
  147. }
  148. /** Parse a string in the format "number unit", where unit is a unit of
  149. * information (byte, KB, M, etc). On success, set *<b>ok</b> to true
  150. * and return the number of bytes specified. Otherwise, set
  151. * *<b>ok</b> to false and return 0. */
  152. uint64_t
  153. config_parse_memunit(const char *s, int *ok)
  154. {
  155. uint64_t u = config_parse_units(s, memory_units, ok);
  156. return u;
  157. }
  158. /** Parse a string in the format "number unit", where unit is a unit of
  159. * time in milliseconds. On success, set *<b>ok</b> to true and return
  160. * the number of milliseconds in the provided interval. Otherwise, set
  161. * *<b>ok</b> to 0 and return -1. */
  162. int
  163. config_parse_msec_interval(const char *s, int *ok)
  164. {
  165. uint64_t r;
  166. r = config_parse_units(s, time_msec_units, ok);
  167. if (r > INT_MAX) {
  168. log_warn(LD_CONFIG, "Msec interval '%s' is too long", s);
  169. *ok = 0;
  170. return -1;
  171. }
  172. return (int)r;
  173. }
  174. /** Parse a string in the format "number unit", where unit is a unit of time.
  175. * On success, set *<b>ok</b> to true and return the number of seconds in
  176. * the provided interval. Otherwise, set *<b>ok</b> to 0 and return -1.
  177. */
  178. int
  179. config_parse_interval(const char *s, int *ok)
  180. {
  181. uint64_t r;
  182. r = config_parse_units(s, time_units, ok);
  183. if (r > INT_MAX) {
  184. log_warn(LD_CONFIG, "Interval '%s' is too long", s);
  185. *ok = 0;
  186. return -1;
  187. }
  188. return (int)r;
  189. }