1 /*        $NetBSD: parse_units.c,v 1.2 2017/01/28 21:31:50 christos Exp $       */
2 
3 /*
4  * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <config.h>
37 
38 #include <stdio.h>
39 #include <ctype.h>
40 #include <string.h>
41 #include <krb5/roken.h>
42 #include <krb5/parse_units.h>
43 
44 /*
45  * Parse string in `s' according to `units' and return value.
46  * def_unit defines the default unit.
47  */
48 
49 static int
parse_something(const char * s,const struct units * units,const char * def_unit,int (* func)(int res,int val,unsigned mult),int init,int accept_no_val_p)50 parse_something (const char *s, const struct units *units,
51                      const char *def_unit,
52                      int (*func)(int res, int val, unsigned mult),
53                      int init,
54                      int accept_no_val_p)
55 {
56     const char *p;
57     int res = init;
58     unsigned def_mult = 1;
59 
60     if (def_unit != NULL) {
61           const struct units *u;
62 
63           for (u = units; u->name; ++u) {
64               if (strcasecmp (u->name, def_unit) == 0) {
65                     def_mult = u->mult;
66                     break;
67               }
68           }
69           if (u->name == NULL)
70               return -1;
71     }
72 
73     p = s;
74     while (*p) {
75           int val;
76           char *next;
77           const struct units *u, *partial_unit;
78           size_t u_len;
79           unsigned partial;
80           int no_val_p = 0;
81 
82           while(isspace((unsigned char)*p) || *p == ',')
83               ++p;
84 
85           val = strtol(p, &next, 0);
86           if (p == next) {
87               val = 0;
88               if(!accept_no_val_p)
89                     return -1;
90               no_val_p = 1;
91           }
92           p = next;
93           while (isspace((unsigned char)*p))
94               ++p;
95           if (*p == '\0') {
96               res = (*func)(res, val, def_mult);
97               if (res < 0)
98                     return res;
99               break;
100           } else if (*p == '+') {
101               ++p;
102               val = 1;
103           } else if (*p == '-') {
104               ++p;
105               val = -1;
106           }
107           if (no_val_p && val == 0)
108               val = 1;
109           u_len = strcspn (p, ", \t");
110           partial = 0;
111           partial_unit = NULL;
112           if (u_len > 1 && p[u_len - 1] == 's')
113               --u_len;
114           for (u = units; u->name; ++u) {
115               if (strncasecmp (p, u->name, u_len) == 0) {
116                     if (u_len == strlen (u->name)) {
117                         p += u_len;
118                         res = (*func)(res, val, u->mult);
119                         if (res < 0)
120                               return res;
121                         break;
122                     } else {
123                         ++partial;
124                         partial_unit = u;
125                     }
126               }
127           }
128           if (u->name == NULL) {
129               if (partial == 1) {
130                     p += u_len;
131                     res = (*func)(res, val, partial_unit->mult);
132                     if (res < 0)
133                         return res;
134               } else {
135                     return -1;
136               }
137           }
138           if (*p == 's')
139               ++p;
140     }
141     return res;
142 }
143 
144 /*
145  * The string consists of a sequence of `n unit'
146  */
147 
148 static int
acc_units(int res,int val,unsigned mult)149 acc_units(int res, int val, unsigned mult)
150 {
151     return res + val * mult;
152 }
153 
154 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
parse_units(const char * s,const struct units * units,const char * def_unit)155 parse_units (const char *s, const struct units *units,
156                const char *def_unit)
157 {
158     return parse_something (s, units, def_unit, acc_units, 0, 0);
159 }
160 
161 /*
162  * The string consists of a sequence of `[+-]flag'.  `orig' consists
163  * the original set of flags, those are then modified and returned as
164  * the function value.
165  */
166 
167 static int
acc_flags(int res,int val,unsigned mult)168 acc_flags(int res, int val, unsigned mult)
169 {
170     if(val == 1)
171           return res | mult;
172     else if(val == -1)
173           return res & ~mult;
174     else if (val == 0)
175           return mult;
176     else
177           return -1;
178 }
179 
180 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
parse_flags(const char * s,const struct units * units,int orig)181 parse_flags (const char *s, const struct units *units,
182                int orig)
183 {
184     return parse_something (s, units, NULL, acc_flags, orig, 1);
185 }
186 
187 /*
188  * Return a string representation according to `units' of `num' in `s'
189  * with maximum length `len'.  The actual length is the function value.
190  */
191 
192 static int
unparse_something(int num,const struct units * units,char * s,size_t len,int (* print)(char *,size_t,int,const char *,int),int (* update)(int,unsigned),const char * zero_string)193 unparse_something (int num, const struct units *units, char *s, size_t len,
194                        int (*print) (char *, size_t, int, const char *, int),
195                        int (*update) (int, unsigned),
196                        const char *zero_string)
197 {
198     const struct units *u;
199     int ret = 0, tmp;
200 
201     if (num == 0)
202           return snprintf (s, len, "%s", zero_string);
203 
204     for (u = units; num > 0 && u->name; ++u) {
205           int divisor;
206 
207           divisor = num / u->mult;
208           if (divisor) {
209               num = (*update) (num, u->mult);
210               tmp = (*print) (s, len, divisor, u->name, num);
211               if (tmp < 0)
212                     return tmp;
213               if (tmp > (int) len) {
214                     len = 0;
215                     s = NULL;
216               } else {
217                     len -= tmp;
218                     s += tmp;
219               }
220               ret += tmp;
221           }
222     }
223     return ret;
224 }
225 
226 static int
print_unit(char * s,size_t len,int divisor,const char * name,int rem)227 print_unit (char *s, size_t len, int divisor, const char *name, int rem)
228 {
229     return snprintf (s, len, "%u %s%s%s",
230                          divisor, name,
231                          divisor == 1 ? "" : "s",
232                          rem > 0 ? " " : "");
233 }
234 
235 static int
update_unit(int in,unsigned mult)236 update_unit (int in, unsigned mult)
237 {
238     return in % mult;
239 }
240 
241 static int
update_unit_approx(int in,unsigned mult)242 update_unit_approx (int in, unsigned mult)
243 {
244     if (in / mult > 0)
245           return 0;
246     else
247           return update_unit (in, mult);
248 }
249 
250 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
unparse_units(int num,const struct units * units,char * s,size_t len)251 unparse_units (int num, const struct units *units, char *s, size_t len)
252 {
253     return unparse_something (num, units, s, len,
254                                     print_unit,
255                                     update_unit,
256                                     "0");
257 }
258 
259 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
unparse_units_approx(int num,const struct units * units,char * s,size_t len)260 unparse_units_approx (int num, const struct units *units, char *s, size_t len)
261 {
262     return unparse_something (num, units, s, len,
263                                     print_unit,
264                                     update_unit_approx,
265                                     "0");
266 }
267 
268 ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
print_units_table(const struct units * units,FILE * f)269 print_units_table (const struct units *units, FILE *f)
270 {
271     const struct units *u, *u2;
272     size_t max_sz = 0;
273 
274     for (u = units; u->name; ++u) {
275           max_sz = max(max_sz, strlen(u->name));
276     }
277 
278     for (u = units; u->name;) {
279           char buf[1024];
280           const struct units *next;
281 
282           for (next = u + 1; next->name && next->mult == u->mult; ++next)
283               ;
284 
285           if (next->name) {
286               for (u2 = next;
287                      u2->name && u->mult % u2->mult != 0;
288                      ++u2)
289                     ;
290               if (u2->name == NULL)
291                     --u2;
292               unparse_units (u->mult, u2, buf, sizeof(buf));
293               fprintf (f, "1 %*s = %s\n", (int)max_sz, u->name, buf);
294           } else {
295               fprintf (f, "1 %s\n", u->name);
296           }
297           u = next;
298     }
299 }
300 
301 static int
print_flag(char * s,size_t len,int divisor,const char * name,int rem)302 print_flag (char *s, size_t len, int divisor, const char *name, int rem)
303 {
304     return snprintf (s, len, "%s%s", name, rem > 0 ? ", " : "");
305 }
306 
307 static int
update_flag(int in,unsigned mult)308 update_flag (int in, unsigned mult)
309 {
310     return in - mult;
311 }
312 
313 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
unparse_flags(int num,const struct units * units,char * s,size_t len)314 unparse_flags (int num, const struct units *units, char *s, size_t len)
315 {
316     return unparse_something (num, units, s, len,
317                                     print_flag,
318                                     update_flag,
319                                     "");
320 }
321 
322 ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
print_flags_table(const struct units * units,FILE * f)323 print_flags_table (const struct units *units, FILE *f)
324 {
325     const struct units *u;
326 
327     for(u = units; u->name; ++u)
328           fprintf(f, "%s%s", u->name, (u+1)->name ? ", " : "\n");
329 }
330