Whamcloud - gitweb
LU-7734 lnet: configure local NI from DLC
[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  * Convert express list (\a expr_list) to an array of all matched values
331  *
332  * \retval N N is total number of all matched values
333  * \retval 0 if expression list is empty
334  * \retval < 0 for failure
335  */
336 int
337 cfs_expr_list_values(struct cfs_expr_list *expr_list, int max, __u32 **valpp)
338 {
339         struct cfs_range_expr   *expr;
340         __u32                   *val;
341         int                     count = 0;
342         int                     i;
343
344         list_for_each_entry(expr, &expr_list->el_exprs, re_link) {
345                 for (i = expr->re_lo; i <= expr->re_hi; i++) {
346                         if (((i - expr->re_lo) % expr->re_stride) == 0)
347                                 count++;
348                 }
349         }
350
351         if (count == 0) /* empty expression list */
352                 return 0;
353
354         if (count > max)
355                 return -EINVAL;
356
357         val = calloc(sizeof(val[0]), count);
358         if (val == NULL)
359                 return -ENOMEM;
360
361         count = 0;
362         list_for_each_entry(expr, &expr_list->el_exprs, re_link) {
363                 for (i = expr->re_lo; i <= expr->re_hi; i++) {
364                         if (((i - expr->re_lo) % expr->re_stride) == 0)
365                                 val[count++] = i;
366                 }
367         }
368
369         *valpp = val;
370         return count;
371 }
372
373 void
374 cfs_expr_list_values_free(__u32 *values, int num)
375 {
376         /* This array is allocated by LIBCFS_ALLOC(), so it shouldn't be freed
377          * by OBD_FREE() if it's called by module other than libcfs & LNet,
378          * otherwise we will see fake memory leak */
379         free(values);
380 }
381
382 /**
383  * Frees cfs_range_expr structures of \a expr_list.
384  *
385  * \retval none
386  */
387 void
388 cfs_expr_list_free(struct cfs_expr_list *expr_list)
389 {
390         while (!list_empty(&expr_list->el_exprs)) {
391                 struct cfs_range_expr *expr;
392
393                 expr = list_entry(expr_list->el_exprs.next,
394                                   struct cfs_range_expr, re_link);
395                 list_del(&expr->re_link);
396                 free(expr);
397         }
398
399         free(expr_list);
400 }
401
402 /**
403  * Parses \<cfs_expr_list\> token of the syntax.
404  *
405  * \retval 0 if \a str parses to \<number\> | \<expr_list\>
406  * \retval -errno otherwise
407  */
408 int
409 cfs_expr_list_parse(char *str, int len, unsigned min, unsigned max,
410                     struct cfs_expr_list **elpp)
411 {
412         struct cfs_expr_list    *expr_list;
413         struct cfs_range_expr   *expr;
414         struct cfs_lstr         src;
415         int                     rc;
416
417         expr_list = calloc(1, sizeof(*expr_list));
418         if (expr_list == NULL)
419                 return -ENOMEM;
420
421         src.ls_str = str;
422         src.ls_len = len;
423
424         INIT_LIST_HEAD(&expr_list->el_exprs);
425
426         if (src.ls_str[0] == '[' &&
427             src.ls_str[src.ls_len - 1] == ']') {
428                 src.ls_str++;
429                 src.ls_len -= 2;
430
431                 rc = -EINVAL;
432                 while (src.ls_str != NULL) {
433                         struct cfs_lstr tok;
434
435                         if (!cfs_gettok(&src, ',', &tok)) {
436                                 rc = -EINVAL;
437                                 break;
438                         }
439
440                         rc = cfs_range_expr_parse(&tok, min, max, 1, &expr);
441                         if (rc != 0)
442                                 break;
443
444                         list_add_tail(&expr->re_link,
445                                           &expr_list->el_exprs);
446                 }
447         } else {
448                 rc = cfs_range_expr_parse(&src, min, max, 0, &expr);
449                 if (rc == 0) {
450                         list_add_tail(&expr->re_link,
451                                           &expr_list->el_exprs);
452                 }
453         }
454
455         if (rc != 0)
456                 cfs_expr_list_free(expr_list);
457         else
458                 *elpp = expr_list;
459
460         return rc;
461 }
462
463 /**
464  * Frees cfs_expr_list structures of \a list.
465  *
466  * For each struct cfs_expr_list structure found on \a list it frees
467  * range_expr list attached to it and frees the cfs_expr_list itself.
468  *
469  * \retval none
470  */
471 void
472 cfs_expr_list_free_list(struct list_head *list)
473 {
474         struct cfs_expr_list *el;
475
476         while (!list_empty(list)) {
477                 el = list_entry(list->next,
478                                     struct cfs_expr_list, el_link);
479                 list_del(&el->el_link);
480                 cfs_expr_list_free(el);
481         }
482 }