]> mj.ucw.cz Git - libucw.git/blob - ucw/conf-parse.c
Config: Added support for terabyte values, for example "123T".
[libucw.git] / ucw / conf-parse.c
1 /*
2  *      UCW Library -- Configuration files: parsers for basic types
3  *
4  *      (c) 2001--2006 Robert Spalek <robert@ucw.cz>
5  *      (c) 2003--2006 Martin Mares <mj@ucw.cz>
6  *      (c) 2019 Pavel Charvat <pchar@ucw.cz>
7  *
8  *      This software may be freely distributed and used according to the terms
9  *      of the GNU Lesser General Public License.
10  */
11
12 #include <ucw/lib.h>
13 #include <ucw/conf.h>
14 #include <ucw/chartype.h>
15
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <errno.h>
19
20 struct unit {
21   uint name;                    // one-letter name of the unit
22   uint den;                     // fraction
23   u64 num;
24 };
25
26 static const struct unit units[] = {
27   { 'd', 1, 86400 },
28   { 'h', 1, 3600 },
29   { 'k', 1, 1000 },
30   { 'm', 1, 1000000 },
31   { 'g', 1, 1000000000 },
32   { 't', 1, 1000000000000LLU },
33   { 'K', 1, 1<<10 },
34   { 'M', 1, 1<<20 },
35   { 'G', 1, 1<<30 },
36   { 'T', 1, 1LLU<<40 },
37   { '%', 100, 1 },
38   { 0, 0, 0 }
39 };
40
41 static const struct unit *
42 lookup_unit(const char *value, const char *end, char **msg)
43 {
44   if (end && *end) {
45     if (end == value || end[1] || *end >= '0' && *end <= '9')
46       *msg = "Invalid number";
47     else {
48       for (const struct unit *u=units; u->name; u++)
49         if ((char)u->name == *end)
50           return u;
51       *msg = "Invalid unit";
52     }
53   }
54   return NULL;
55 }
56
57 static char cf_rngerr[] = "Number out of range";
58
59 char *
60 cf_parse_int(const char *str, int *ptr)
61 {
62   char *msg = NULL;
63   if (!*str)
64     msg = "Missing number";
65   else {
66     const struct unit *u;
67     char *end;
68     errno = 0;
69     uint x = strtoul(str, &end, 0);
70     if (errno == ERANGE)
71       msg = cf_rngerr;
72     else if (u = lookup_unit(str, end, &msg)) {
73       u64 y = (u64)x * u->num;
74       if (y % u->den)
75         msg = "Number is not an integer";
76       else {
77         y /= u->den;
78         if (y > 0xffffffff)
79           msg = cf_rngerr;
80         *ptr = y;
81       }
82     } else
83       *ptr = x;
84   }
85   return msg;
86 }
87
88 char *
89 cf_parse_u64(const char *str, u64 *ptr)
90 {
91   char *msg = NULL;
92   if (!*str)
93     msg = "Missing number";
94   else {
95     const struct unit *u;
96     char *end;
97     errno = 0;
98     u64 x = strtoull(str, &end, 0);
99     if (errno == ERANGE)
100       msg = cf_rngerr;
101     else if (u = lookup_unit(str, end, &msg)) {
102       if (x > ~(u64)0 / u->num)
103         msg = "Number out of range";
104       else {
105         x *= u->num;
106         if (x % u->den)
107           msg = "Number is not an integer";
108         else
109           *ptr = x / u->den;
110       }
111     } else
112       *ptr = x;
113   }
114   return msg;
115 }
116
117 char *
118 cf_parse_double(const char *str, double *ptr)
119 {
120   char *msg = NULL;
121   if (!*str)
122     msg = "Missing number";
123   else {
124     const struct unit *u;
125     double x;
126     uint read_chars;
127     if (sscanf(str, "%lf%n", &x, &read_chars) != 1)
128       msg = "Invalid number";
129     else if (u = lookup_unit(str, str + read_chars, &msg))
130       *ptr = x * u->num / u->den;
131     else
132       *ptr = x;
133   }
134   return msg;
135 }
136
137 char *
138 cf_parse_ip(const char *p, u32 *varp)
139 {
140   if (!*p)
141     return "Missing IP address";
142   uint x = 0;
143   char *p2;
144   if (*p == '0' && (p[1] | 32) == 'x' && Cxdigit(p[2])) {
145     errno = 0;
146     x = strtoul(p, &p2, 16);
147     if (errno == ERANGE || x > 0xffffffff)
148       goto error;
149     p = p2;
150   }
151   else
152     for (uint i = 0; i < 4; i++) {
153       if (i) {
154         if (*p++ != '.')
155           goto error;
156       }
157       if (!Cdigit(*p))
158         goto error;
159       errno = 0;
160       uint y = strtoul(p, &p2, 10);
161       if (errno == ERANGE || p2 == (char*) p || y > 255)
162         goto error;
163       p = p2;
164       x = (x << 8) + y;
165     }
166   *varp = x;
167   return *p ? "Trailing characters" : NULL;
168 error:
169   return "Invalid IP address";
170 }
171