1 /*	$NetBSD: parse_units.c,v 1.1.1.2 2014/04/24 12:45:52 pettai 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