Allow passing in human-readable units for specifying amounts of space
[openafs.git] / src / util / volparse.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 #include <string.h>
14 #include <errno.h>
15 #ifdef HAVE_STDLIB_H
16 #include <stdlib.h>
17 #endif
18
19 #include "afsutil.h"
20
21 /* maximum number of partitions - must match vol/voldefs.h */
22 #define VOLMAXPARTS 255
23
24 /**
25  * map a partition id from any partition-style name.
26  *
27  * @param[in] aname  partition name string
28  *
29  * @return partition index number
30  *   @retval -1  invalid partition name
31  *
32  * @see volutil_PartitionName2_r
33  * @see volutil_PartitionName_r
34  * @see volutil_PartitionName
35  */
36 afs_int32
37 volutil_GetPartitionID(char *aname)
38 {
39     register char tc;
40     afs_int32 temp;
41     char ascii[3];
42
43     tc = *aname;
44     if (tc == 0)
45         return -1;              /* unknown */
46     /* numbers go straight through */
47     if (tc >= '0' && tc <= '9') {
48         temp = atoi(aname);
49         /* this next check is to make the syntax less ambiguous when discriminating
50          * between volume numbers and partition IDs.  This lets things like
51          * bos salvage do some reasonability checks on its input w/o checking
52          * to see if the partition is really on the server.
53          */
54         if (temp < 0 || temp >= VOLMAXPARTS)
55             return -1;
56         else
57             return temp;
58     }
59     /* otherwise check for vicepa or /vicepa, or just plain "a" */
60     ascii[2] = 0;
61     if (strlen(aname) <= 2) {
62         strcpy(ascii, aname);
63     } else if (!strncmp(aname, "/vicep", 6)) {
64         strncpy(ascii, aname + 6, 2);
65     } else if (!strncmp(aname, "vicep", 5)) {
66         strncpy(ascii, aname + 5, 2);
67     } else
68         return -1;              /* bad partition name */
69     /* now partitions are named /vicepa ... /vicepz, /vicepaa, /vicepab, .../vicepzz,
70      * and are numbered from 0.  Do the appropriate conversion */
71     if (ascii[1] == 0) {
72         /* one char name, 0..25 */
73         if (ascii[0] < 'a' || ascii[0] > 'z')
74             return -1;          /* wrongo */
75         return ascii[0] - 'a';
76     } else {
77         /* two char name, 26 .. <whatever> */
78         if (ascii[0] < 'a' || ascii[0] > 'z')
79             return -1;          /* wrongo */
80         if (ascii[1] < 'a' || ascii[1] > 'z')
81             return -1;          /* just as bad */
82         temp = (ascii[0] - 'a') * 26 + (ascii[1] - 'a') + 26;
83         return (temp >= VOLMAXPARTS ? -1 : temp);
84     }
85 }
86
87 /**
88  * convert a partition index number into a partition name string (/vicepXX).
89  *
90  * @param[in]  part     partition index number
91  * @param[out] tbuffer  buffer in which to store name
92  * @param[in]  buflen   length of tbuffer
93  *
94  * @return operation status
95  *   @retval 0   success
96  *   @retval -1  buffer too short
97  *   @retval -2  invalid partition id
98  *
99  * @see volutil_PartitionName_r
100  * @see volutil_PartitionName
101  * @see volutil_GetPartitionID
102  */
103 afs_int32
104 volutil_PartitionName2_r(afs_int32 part, char *tbuffer, size_t buflen)
105 {
106     char tempString[3];
107     register int i;
108
109     if (part < 0 || part >= VOLMAXPARTS) {
110         return -2;
111     }
112
113     tempString[1] = tempString[2] = 0;
114     strncpy(tbuffer, "/vicep", buflen);
115     if (part <= 25) {
116         tempString[0] = 'a' + part;
117     } else {
118         part -= 26;
119         i = (part / 26);
120         tempString[0] = i + 'a';
121         tempString[1] = (part % 26) + 'a';
122     }
123     if (strlcat(tbuffer, tempString, buflen) >= buflen) {
124         return -1;
125     }
126     return 0;
127 }
128
129 #define BAD_VID "BAD VOLUME ID"
130 #define BAD_VID_LEN (sizeof(BAD_VID))
131 /**
132  * convert a partition index number into a partition name string (/vicepXX).
133  *
134  * @param[in]  part     partition index number
135  * @param[out] tbuffer  buffer in which to store name
136  * @param[in]  buflen   length of tbuffer
137  *
138  * @return partition name string
139  *   @retval ""               buffer too short
140  *   @retval "SPC"            buffer too short
141  *   @retval "BAD VOLUME ID"  avalue contains an invalid partition index
142  *
143  * @note you may wish to consider using volutil_PartitionName2_r, as its
144  *       error handling is more standard
145  *
146  * @see volutil_PartitionName2_r
147  * @see volutil_PartitionName
148  * @see volutil_GetPartitionID
149  */
150 char *
151 volutil_PartitionName_r(int part, char *tbuffer, int buflen)
152 {
153     afs_int32 code;
154
155     if (buflen < BAD_VID_LEN) {
156         strlcpy(tbuffer, "SPC", buflen);
157         return tbuffer;
158     }
159
160     code = volutil_PartitionName2_r(part, tbuffer, buflen);
161
162     if (code == -2) {
163         strlcpy(tbuffer, BAD_VID, buflen);
164     }
165
166     return tbuffer;
167 }
168
169 /**
170  * convert a partition index number into a partition name string (/vicepXX).
171  *
172  * @param[in] avalue  partition index number
173  *
174  * @return partition name string
175  *   @retval "BAD VOLUME ID"  avalue contains an invalid partition index
176  *
177  * @warning this interface is not re-entrant
178  *
179  * @see volutil_PartitionName2_r
180  * @see volutil_PartitionName_r
181  * @see volutil_GetPartitionID
182  */
183 char *
184 volutil_PartitionName(int avalue)
185 {
186 #define VPN_TBUFLEN 64
187     static char tbuffer[VPN_TBUFLEN];
188     return volutil_PartitionName_r(avalue, tbuffer, VPN_TBUFLEN - 1);
189 }
190
191 /* is this a digit or a digit-like thing? */
192 static int
193 ismeta(register int ac, register int abase)
194 {
195 /*    if (ac == '-' || ac == 'x' || ac == 'X') return 1; */
196     if (ac >= '0' && ac <= '7')
197         return 1;
198     if (abase <= 8)
199         return 0;
200     if (ac >= '8' && ac <= '9')
201         return 1;
202     if (abase <= 10)
203         return 0;
204     if (ac >= 'a' && ac <= 'f')
205         return 1;
206     if (ac >= 'A' && ac <= 'F')
207         return 1;
208     return 0;
209 }
210
211 /* given that this is a digit or a digit-like thing, compute its value */
212 static int
213 getmeta(register int ac)
214 {
215     if (ac >= '0' && ac <= '9')
216         return ac - '0';
217     if (ac >= 'a' && ac <= 'f')
218         return ac - 'a' + 10;
219     if (ac >= 'A' && ac <= 'F')
220         return ac - 'A' + 10;
221     return 0;
222 }
223
224 afs_int32
225 util_GetInt32(register char *as, afs_int32 * aval)
226 {
227     register afs_int32 total;
228     register int tc;
229     int base;
230     int negative;
231
232     total = 0;                  /* initialize things */
233     negative = 0;
234
235     /* skip over leading spaces */
236     for (tc = *as; tc !='\0'; as++, tc = *as) {
237         if (tc != ' ' && tc != '\t')
238             break;
239     }
240
241     /* compute sign */
242     if (*as == '-') {
243         negative = 1;
244         as++;                   /* skip over character */
245     }
246
247     /* compute the base */
248     if (*as == '0') {
249         as++;
250         if (*as == 'x' || *as == 'X') {
251             base = 16;
252             as++;
253         } else
254             base = 8;
255     } else
256         base = 10;
257
258     /* compute the # itself */
259     for (tc = *as; tc !='\0'; as++, tc = *as) {
260         if (!ismeta(tc, base))
261             return -1;
262         total *= base;
263         total += getmeta(tc);
264     }
265
266     if (negative)
267         *aval = -total;
268     else
269         *aval = total;
270     return 0;
271 }
272
273 afs_uint32
274 util_GetUInt32(register char *as, afs_uint32 * aval)
275 {
276     register afs_uint32 total;
277     register int tc;
278     int base;
279
280     total = 0;                  /* initialize things */
281
282     /* skip over leading spaces */
283     for (tc = *as; tc !='\0'; as++, tc = *as) {
284         if (tc != ' ' && tc != '\t')
285             break;
286     }
287
288     /* compute the base */
289     if (*as == '0') {
290         as++;
291         if (*as == 'x' || *as == 'X') {
292             base = 16;
293             as++;
294         } else
295             base = 8;
296     } else
297         base = 10;
298
299     /* compute the # itself */
300     for (tc = *as; tc !='\0'; as++, tc = *as) {
301         if (!ismeta(tc, base))
302             return -1;
303         total *= base;
304         total += getmeta(tc);
305     }
306
307     *aval = total;
308     return 0;
309 }
310
311 static const char power_letter[] = {
312     'K',  /* kibi */
313     'M',  /* mebi */
314     'G',  /* gibi */
315     'T',  /* tebi */
316 };
317
318 afs_int32
319 util_GetHumanInt32(register char *as, afs_int32 * aval)
320 {
321     long value;
322     char * unit;
323     long mult = 1;
324     int exponent = 0;
325
326     errno = 0;
327     value = strtol(as, &unit, 0);
328     if (errno)
329         return -1;
330     if (unit[0] != 0) {
331         for (exponent = 0; exponent < sizeof(power_letter) && power_letter[exponent] != unit[0]; exponent++) {
332             mult *= 1024;
333         }
334         if (exponent == sizeof(power_letter))
335             return -1;
336     }
337     if (value > MAX_AFS_INT32 / mult || value < MIN_AFS_INT32 / mult)
338         return -1;
339
340     *aval = value * mult;
341
342     return 0;
343 }