Whamcloud - gitweb
97b5f7f9dfb1098bb2271127ac051825d7b2757a
[fs/lustre-release.git] / libcfs / libcfs / util / string.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Copyright (c) 2012, 2014, Intel Corporation.
27  */
28 /*
29  * This file is part of Lustre, http://www.lustre.org/
30  * Lustre is a trademark of Sun Microsystems, Inc.
31  *
32  * String manipulation functions.
33  *
34  * libcfs/libcfs/util/string.c
35  *
36  * Author: Nathan Rutman <nathan.rutman@sun.com>
37  */
38 #include <ctype.h>
39 #include <errno.h>
40 #include <stdbool.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <libcfs/util/string.h>
45
46 /*
47  * According manual of strlcpy() and strlcat() the functions should return
48  * the total length of the string they tried to create. For strlcpy() that
49  * means the length of src. For strlcat() that means the initial length of
50  * dst plus the length of src. So, the function strnlen() cannot be used
51  * otherwise the return value will be wrong.
52  */
53 #ifndef HAVE_STRLCPY /* not in glibc for RHEL 5.x, remove when obsolete */
54 size_t strlcpy(char *dst, const char *src, size_t size)
55 {
56         size_t ret = strlen(src);
57
58         if (size) {
59                 size_t len = (ret >= size) ? size - 1 : ret;
60                 memcpy(dst, src, len);
61                 dst[len] = '\0';
62         }
63         return ret;
64 }
65 #endif
66
67 #ifndef HAVE_STRLCAT /* not in glibc for RHEL 5.x, remove when obsolete */
68 size_t strlcat(char *dst, const char *src, size_t size)
69 {
70         size_t dsize = strlen(dst);
71         size_t len = strlen(src);
72         size_t ret = dsize + len;
73
74         dst  += dsize;
75         size -= dsize;
76         if (len >= size)
77                 len = size-1;
78         memcpy(dst, src, len);
79         dst[len] = '\0';
80         return ret;
81 }
82 #endif
83
84 /**
85  * Extracts tokens from strings.
86  *
87  * Looks for \a delim in string \a next, sets \a res to point to
88  * substring before the delimiter, sets \a next right after the found
89  * delimiter.
90  *
91  * \retval 1 if \a res points to a string of non-whitespace characters
92  * \retval 0 otherwise
93  */
94 int
95 cfs_gettok(struct cfs_lstr *next, char delim, struct cfs_lstr *res)
96 {
97         char *end;
98
99         if (next->ls_str == NULL)
100                 return 0;
101
102         /* skip leading white spaces */
103         while (next->ls_len) {
104                 if (!isspace(*next->ls_str))
105                         break;
106                 next->ls_str++;
107                 next->ls_len--;
108         }
109
110         if (next->ls_len == 0) /* whitespaces only */
111                 return 0;
112
113         if (*next->ls_str == delim) {
114                 /* first non-writespace is the delimiter */
115                 return 0;
116         }
117
118         res->ls_str = next->ls_str;
119         end = memchr(next->ls_str, delim, next->ls_len);
120         if (end == NULL) {
121                 /* there is no the delimeter in the string */
122                 end = next->ls_str + next->ls_len;
123                 next->ls_str = NULL;
124         } else {
125                 next->ls_str = end + 1;
126                 next->ls_len -= (end - res->ls_str + 1);
127         }
128
129         /* skip ending whitespaces */
130         while (--end != res->ls_str) {
131                 if (!isspace(*end))
132                         break;
133         }
134
135         res->ls_len = end - res->ls_str + 1;
136         return 1;
137 }
138
139 /**
140  * Converts string to integer.
141  *
142  * Accepts decimal and hexadecimal number recordings.
143  *
144  * \retval 1 if first \a nob chars of \a str convert to decimal or
145  * hexadecimal integer in the range [\a min, \a max]
146  * \retval 0 otherwise
147  */
148 int
149 cfs_str2num_check(char *str, int nob, unsigned *num,
150                   unsigned min, unsigned max)
151 {
152         char    *endp;
153
154         *num = strtoul(str, &endp, 0);
155         if (endp == str)
156                 return 0;
157
158         for (; endp < str + nob; endp++) {
159                 if (!isspace(*endp))
160                         return 0;
161         }
162
163         return (*num >= min && *num <= max);
164 }
165
166 /**
167  * Parses \<range_expr\> token of the syntax. If \a bracketed is false,
168  * \a src should only have a single token which can be \<number\> or  \*
169  *
170  * \retval pointer to allocated range_expr and initialized
171  * range_expr::re_lo, range_expr::re_hi and range_expr:re_stride if \a
172  * src parses to
173  * \<number\> |
174  * \<number\> '-' \<number\> |
175  * \<number\> '-' \<number\> '/' \<number\>
176  * \retval 0 will be returned if it can be parsed, otherwise -EINVAL or
177  * -ENOMEM will be returned.
178  */
179 static int
180 cfs_range_expr_parse(struct cfs_lstr *src, unsigned min, unsigned max,
181                      int bracketed, struct cfs_range_expr **expr)
182 {
183         struct cfs_range_expr   *re;
184         struct cfs_lstr         tok;
185
186         re = calloc(1, sizeof(*re));
187         if (re == NULL)
188                 return -ENOMEM;
189
190         if (src->ls_len == 1 && src->ls_str[0] == '*') {
191                 re->re_lo = min;
192                 re->re_hi = max;
193                 re->re_stride = 1;
194                 goto out;
195         }
196
197         if (cfs_str2num_check(src->ls_str, src->ls_len,
198                               &re->re_lo, min, max)) {
199                 /* <number> is parsed */
200                 re->re_hi = re->re_lo;
201                 re->re_stride = 1;
202                 goto out;
203         }
204
205         if (!bracketed || !cfs_gettok(src, '-', &tok))
206                 goto failed;
207
208         if (!cfs_str2num_check(tok.ls_str, tok.ls_len,
209                                &re->re_lo, min, max))
210                 goto failed;
211
212         /* <number> - */
213         if (cfs_str2num_check(src->ls_str, src->ls_len,
214                               &re->re_hi, min, max)) {
215                 /* <number> - <number> is parsed */
216                 re->re_stride = 1;
217                 goto out;
218         }
219
220         /* go to check <number> '-' <number> '/' <number> */
221         if (cfs_gettok(src, '/', &tok)) {
222                 if (!cfs_str2num_check(tok.ls_str, tok.ls_len,
223                                        &re->re_hi, min, max))
224                         goto failed;
225
226                 /* <number> - <number> / ... */
227                 if (cfs_str2num_check(src->ls_str, src->ls_len,
228                                       &re->re_stride, min, max)) {
229                         /* <number> - <number> / <number> is parsed */
230                         goto out;
231                 }
232         }
233
234  out:
235         *expr = re;
236         return 0;
237
238  failed:
239         free(re);
240         return -EINVAL;
241 }
242
243 /**
244  * Print the range expression \a re into specified \a buffer.
245  * If \a bracketed is true, expression does not need additional
246  * brackets.
247  *
248  * \retval number of characters written
249  */
250 static int
251 cfs_range_expr_print(char *buffer, int count, struct cfs_range_expr *expr,
252                      bool bracketed)
253 {
254         int i;
255         char s[] = "[";
256         char e[] = "]";
257
258         if (bracketed)
259                 s[0] = e[0] = '\0';
260
261         if (expr->re_lo == expr->re_hi)
262                 i = snprintf(buffer, count, "%u", expr->re_lo);
263         else if (expr->re_stride == 1)
264                 i = snprintf(buffer, count, "%s%u-%u%s",
265                                   s, expr->re_lo, expr->re_hi, e);
266         else
267                 i = snprintf(buffer, count, "%s%u-%u/%u%s",
268                                   s, expr->re_lo, expr->re_hi,
269                                   expr->re_stride, e);
270         return i;
271 }
272
273 /**
274  * Print a list of range expressions (\a expr_list) into specified \a buffer.
275  * If the list contains several expressions, separate them with comma
276  * and surround the list with brackets.
277  *
278  * \retval number of characters written
279  */
280 int
281 cfs_expr_list_print(char *buffer, int count, struct cfs_expr_list *expr_list)
282 {
283         struct cfs_range_expr *expr;
284         int i = 0, j = 0;
285         int numexprs = 0;
286
287         if (count <= 0)
288                 return 0;
289
290         list_for_each_entry(expr, &expr_list->el_exprs, re_link)
291                 numexprs++;
292
293         if (numexprs > 1)
294                 i += snprintf(buffer + i, count - i, "[");
295
296         list_for_each_entry(expr, &expr_list->el_exprs, re_link) {
297                 if (j++ != 0)
298                         i += snprintf(buffer + i, count - i, ",");
299                 i += cfs_range_expr_print(buffer + i, count - i, expr,
300                                           numexprs > 1);
301         }
302
303         if (numexprs > 1)
304                 i += snprintf(buffer + i, count - i, "]");
305
306         return i;
307 }
308
309 /**
310  * Matches value (\a value) against ranges expression list \a expr_list.
311  *
312  * \retval 1 if \a value matches
313  * \retval 0 otherwise
314  */
315 int
316 cfs_expr_list_match(__u32 value, struct cfs_expr_list *expr_list)
317 {
318         struct cfs_range_expr   *expr;
319
320         list_for_each_entry(expr, &expr_list->el_exprs, re_link) {
321                 if (value >= expr->re_lo && value <= expr->re_hi &&
322                     ((value - expr->re_lo) % expr->re_stride) == 0)
323                         return 1;
324         }
325
326         return 0;
327 }
328
329 /**
330  * Frees cfs_range_expr structures of \a expr_list.
331  *
332  * \retval none
333  */
334 static void
335 cfs_expr_list_free(struct cfs_expr_list *expr_list)
336 {
337         while (!list_empty(&expr_list->el_exprs)) {
338                 struct cfs_range_expr *expr;
339
340                 expr = list_entry(expr_list->el_exprs.next,
341                                   struct cfs_range_expr, re_link);
342                 list_del(&expr->re_link);
343                 free(expr);
344         }
345
346         free(expr_list);
347 }
348
349 /**
350  * Parses \<cfs_expr_list\> token of the syntax.
351  *
352  * \retval 0 if \a str parses to \<number\> | \<expr_list\>
353  * \retval -errno otherwise
354  */
355 int
356 cfs_expr_list_parse(char *str, int len, unsigned min, unsigned max,
357                     struct cfs_expr_list **elpp)
358 {
359         struct cfs_expr_list    *expr_list;
360         struct cfs_range_expr   *expr;
361         struct cfs_lstr         src;
362         int                     rc;
363
364         expr_list = calloc(1, sizeof(*expr_list));
365         if (expr_list == NULL)
366                 return -ENOMEM;
367
368         src.ls_str = str;
369         src.ls_len = len;
370
371         INIT_LIST_HEAD(&expr_list->el_exprs);
372
373         if (src.ls_str[0] == '[' &&
374             src.ls_str[src.ls_len - 1] == ']') {
375                 src.ls_str++;
376                 src.ls_len -= 2;
377
378                 rc = -EINVAL;
379                 while (src.ls_str != NULL) {
380                         struct cfs_lstr tok;
381
382                         if (!cfs_gettok(&src, ',', &tok)) {
383                                 rc = -EINVAL;
384                                 break;
385                         }
386
387                         rc = cfs_range_expr_parse(&tok, min, max, 1, &expr);
388                         if (rc != 0)
389                                 break;
390
391                         list_add_tail(&expr->re_link,
392                                           &expr_list->el_exprs);
393                 }
394         } else {
395                 rc = cfs_range_expr_parse(&src, min, max, 0, &expr);
396                 if (rc == 0) {
397                         list_add_tail(&expr->re_link,
398                                           &expr_list->el_exprs);
399                 }
400         }
401
402         if (rc != 0)
403                 cfs_expr_list_free(expr_list);
404         else
405                 *elpp = expr_list;
406
407         return rc;
408 }
409
410 /**
411  * Frees cfs_expr_list structures of \a list.
412  *
413  * For each struct cfs_expr_list structure found on \a list it frees
414  * range_expr list attached to it and frees the cfs_expr_list itself.
415  *
416  * \retval none
417  */
418 void
419 cfs_expr_list_free_list(struct list_head *list)
420 {
421         struct cfs_expr_list *el;
422
423         while (!list_empty(list)) {
424                 el = list_entry(list->next,
425                                     struct cfs_expr_list, el_link);
426                 list_del(&el->el_link);
427                 cfs_expr_list_free(el);
428         }
429 }