Whamcloud - gitweb
LU-12861 libcfs: provide an scnprintf and start using it
[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, 2017, 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 <limits.h>
45 #include <unistd.h>
46 #include <libcfs/util/string.h>
47
48 /**
49  * Extracts tokens from strings.
50  *
51  * Looks for \a delim in string \a next, sets \a res to point to
52  * substring before the delimiter, sets \a next right after the found
53  * delimiter.
54  *
55  * \retval 1 if \a res points to a string of non-whitespace characters
56  * \retval 0 otherwise
57  */
58 int
59 cfs_gettok(struct cfs_lstr *next, char delim, struct cfs_lstr *res)
60 {
61         char *end;
62
63         if (next->ls_str == NULL)
64                 return 0;
65
66         /* skip leading white spaces */
67         while (next->ls_len) {
68                 if (!isspace(*next->ls_str))
69                         break;
70                 next->ls_str++;
71                 next->ls_len--;
72         }
73
74         if (next->ls_len == 0) /* whitespaces only */
75                 return 0;
76
77         if (*next->ls_str == delim) {
78                 /* first non-writespace is the delimiter */
79                 return 0;
80         }
81
82         res->ls_str = next->ls_str;
83         end = memchr(next->ls_str, delim, next->ls_len);
84         if (end == NULL) {
85                 /* there is no the delimeter in the string */
86                 end = next->ls_str + next->ls_len;
87                 next->ls_str = NULL;
88         } else {
89                 next->ls_str = end + 1;
90                 next->ls_len -= (end - res->ls_str + 1);
91         }
92
93         /* skip ending whitespaces */
94         while (--end != res->ls_str) {
95                 if (!isspace(*end))
96                         break;
97         }
98
99         res->ls_len = end - res->ls_str + 1;
100         return 1;
101 }
102
103 /**
104  * Converts string to integer.
105  *
106  * Accepts decimal and hexadecimal number recordings.
107  *
108  * \retval 1 if first \a nob chars of \a str convert to decimal or
109  * hexadecimal integer in the range [\a min, \a max]
110  * \retval 0 otherwise
111  */
112 int
113 cfs_str2num_check(char *str, int nob, unsigned *num,
114                   unsigned min, unsigned max)
115 {
116         char    *endp;
117
118         *num = strtoul(str, &endp, 0);
119         if (endp == str)
120                 return 0;
121
122         for (; endp < str + nob; endp++) {
123                 if (!isspace(*endp))
124                         return 0;
125         }
126
127         return (*num >= min && *num <= max);
128 }
129
130 /**
131  * Parses \<range_expr\> token of the syntax. If \a bracketed is false,
132  * \a src should only have a single token which can be \<number\> or  \*
133  *
134  * \retval pointer to allocated range_expr and initialized
135  * range_expr::re_lo, range_expr::re_hi and range_expr:re_stride if \a
136  * src parses to
137  * \<number\> |
138  * \<number\> '-' \<number\> |
139  * \<number\> '-' \<number\> '/' \<number\>
140  * \retval 0 will be returned if it can be parsed, otherwise -EINVAL or
141  * -ENOMEM will be returned.
142  */
143 static int
144 cfs_range_expr_parse(struct cfs_lstr *src, unsigned min, unsigned max,
145                      int bracketed, struct cfs_range_expr **expr)
146 {
147         struct cfs_range_expr   *re;
148         struct cfs_lstr         tok;
149
150         re = calloc(1, sizeof(*re));
151         if (re == NULL)
152                 return -ENOMEM;
153
154         if (src->ls_len == 1 && src->ls_str[0] == '*') {
155                 re->re_lo = min;
156                 re->re_hi = max;
157                 re->re_stride = 1;
158                 goto out;
159         }
160
161         if (cfs_str2num_check(src->ls_str, src->ls_len,
162                               &re->re_lo, min, max)) {
163                 /* <number> is parsed */
164                 re->re_hi = re->re_lo;
165                 re->re_stride = 1;
166                 goto out;
167         }
168
169         if (!bracketed || !cfs_gettok(src, '-', &tok))
170                 goto failed;
171
172         if (!cfs_str2num_check(tok.ls_str, tok.ls_len,
173                                &re->re_lo, min, max))
174                 goto failed;
175
176         /* <number> - */
177         if (cfs_str2num_check(src->ls_str, src->ls_len,
178                               &re->re_hi, min, max)) {
179                 /* <number> - <number> is parsed */
180                 re->re_stride = 1;
181                 goto out;
182         }
183
184         /* go to check <number> '-' <number> '/' <number> */
185         if (cfs_gettok(src, '/', &tok)) {
186                 if (!cfs_str2num_check(tok.ls_str, tok.ls_len,
187                                        &re->re_hi, min, max))
188                         goto failed;
189
190                 /* <number> - <number> / ... */
191                 if (cfs_str2num_check(src->ls_str, src->ls_len,
192                                       &re->re_stride, min, max)) {
193                         /* <number> - <number> / <number> is parsed */
194                         goto out;
195                 }
196         }
197
198  out:
199         *expr = re;
200         return 0;
201
202  failed:
203         free(re);
204         return -EINVAL;
205 }
206
207 /**
208  * Print the range expression \a re into specified \a buffer.
209  * If \a bracketed is true, expression does not need additional
210  * brackets.
211  *
212  * \retval number of characters written
213  */
214 static int
215 cfs_range_expr_print(char *buffer, int count, struct cfs_range_expr *expr,
216                      bool bracketed)
217 {
218         int i;
219         char s[] = "[";
220         char e[] = "]";
221
222         if (bracketed)
223                 s[0] = e[0] = '\0';
224
225         if (expr->re_lo == expr->re_hi)
226                 i = snprintf(buffer, count, "%u", expr->re_lo);
227         else if (expr->re_stride == 1)
228                 i = snprintf(buffer, count, "%s%u-%u%s",
229                                   s, expr->re_lo, expr->re_hi, e);
230         else
231                 i = snprintf(buffer, count, "%s%u-%u/%u%s",
232                                   s, expr->re_lo, expr->re_hi,
233                                   expr->re_stride, e);
234         return i;
235 }
236
237 /**
238  * Print a list of range expressions (\a expr_list) into specified \a buffer.
239  * If the list contains several expressions, separate them with comma
240  * and surround the list with brackets.
241  *
242  * \retval number of characters written
243  */
244 int
245 cfs_expr_list_print(char *buffer, int count, struct cfs_expr_list *expr_list)
246 {
247         struct cfs_range_expr *expr;
248         int i = 0, j = 0;
249         int numexprs = 0;
250
251         if (count <= 0)
252                 return 0;
253
254         list_for_each_entry(expr, &expr_list->el_exprs, re_link)
255                 numexprs++;
256
257         if (numexprs > 1)
258                 i += scnprintf(buffer + i, count - i, "[");
259
260         list_for_each_entry(expr, &expr_list->el_exprs, re_link) {
261                 if (j++ != 0)
262                         i += scnprintf(buffer + i, count - i, ",");
263                 i += cfs_range_expr_print(buffer + i, count - i, expr,
264                                           numexprs > 1);
265         }
266
267         if (numexprs > 1)
268                 i += scnprintf(buffer + i, count - i, "]");
269
270         return i;
271 }
272
273 /**
274  * Matches value (\a value) against ranges expression list \a expr_list.
275  *
276  * \retval 1 if \a value matches
277  * \retval 0 otherwise
278  */
279 int
280 cfs_expr_list_match(__u32 value, struct cfs_expr_list *expr_list)
281 {
282         struct cfs_range_expr   *expr;
283
284         list_for_each_entry(expr, &expr_list->el_exprs, re_link) {
285                 if (value >= expr->re_lo && value <= expr->re_hi &&
286                     ((value - expr->re_lo) % expr->re_stride) == 0)
287                         return 1;
288         }
289
290         return 0;
291 }
292
293 /**
294  * Convert express list (\a expr_list) to an array of all matched values
295  *
296  * \retval N N is total number of all matched values
297  * \retval 0 if expression list is empty
298  * \retval < 0 for failure
299  */
300 int
301 cfs_expr_list_values(struct cfs_expr_list *expr_list, int max, __u32 **valpp)
302 {
303         struct cfs_range_expr   *expr;
304         __u32                   *val;
305         int                     count = 0;
306         int                     i;
307
308         list_for_each_entry(expr, &expr_list->el_exprs, re_link) {
309                 for (i = expr->re_lo; i <= expr->re_hi; i++) {
310                         if (((i - expr->re_lo) % expr->re_stride) == 0)
311                                 count++;
312                 }
313         }
314
315         if (count == 0) /* empty expression list */
316                 return 0;
317
318         if (count > max)
319                 return -EINVAL;
320
321         val = calloc(sizeof(val[0]), count);
322         if (val == NULL)
323                 return -ENOMEM;
324
325         count = 0;
326         list_for_each_entry(expr, &expr_list->el_exprs, re_link) {
327                 for (i = expr->re_lo; i <= expr->re_hi; i++) {
328                         if (((i - expr->re_lo) % expr->re_stride) == 0)
329                                 val[count++] = i;
330                 }
331         }
332
333         *valpp = val;
334         return count;
335 }
336
337 void
338 cfs_expr_list_values_free(__u32 *values, int num)
339 {
340         /* This array is allocated by LIBCFS_ALLOC(), so it shouldn't be freed
341          * by OBD_FREE() if it's called by module other than libcfs & LNet,
342          * otherwise we will see fake memory leak */
343         free(values);
344 }
345
346 /**
347  * Frees cfs_range_expr structures of \a expr_list.
348  *
349  * \retval none
350  */
351 void
352 cfs_expr_list_free(struct cfs_expr_list *expr_list)
353 {
354         while (!list_empty(&expr_list->el_exprs)) {
355                 struct cfs_range_expr *expr;
356
357                 expr = list_entry(expr_list->el_exprs.next,
358                                   struct cfs_range_expr, re_link);
359                 list_del(&expr->re_link);
360                 free(expr);
361         }
362
363         free(expr_list);
364 }
365
366 /**
367  * Parses \<cfs_expr_list\> token of the syntax.
368  *
369  * \retval 0 if \a str parses to \<number\> | \<expr_list\>
370  * \retval -errno otherwise
371  */
372 int
373 cfs_expr_list_parse(char *str, int len, unsigned min, unsigned max,
374                     struct cfs_expr_list **elpp)
375 {
376         struct cfs_expr_list    *expr_list;
377         struct cfs_range_expr   *expr;
378         struct cfs_lstr         src;
379         int                     rc;
380
381         expr_list = calloc(1, sizeof(*expr_list));
382         if (expr_list == NULL)
383                 return -ENOMEM;
384
385         src.ls_str = str;
386         src.ls_len = len;
387
388         INIT_LIST_HEAD(&expr_list->el_exprs);
389
390         if (src.ls_str[0] == '[' &&
391             src.ls_str[src.ls_len - 1] == ']') {
392                 src.ls_str++;
393                 src.ls_len -= 2;
394
395                 rc = -EINVAL;
396                 while (src.ls_str != NULL) {
397                         struct cfs_lstr tok;
398
399                         if (!cfs_gettok(&src, ',', &tok)) {
400                                 rc = -EINVAL;
401                                 break;
402                         }
403
404                         rc = cfs_range_expr_parse(&tok, min, max, 1, &expr);
405                         if (rc != 0)
406                                 break;
407
408                         list_add_tail(&expr->re_link,
409                                           &expr_list->el_exprs);
410                 }
411         } else {
412                 rc = cfs_range_expr_parse(&src, min, max, 0, &expr);
413                 if (rc == 0) {
414                         list_add_tail(&expr->re_link,
415                                           &expr_list->el_exprs);
416                 }
417         }
418
419         if (rc != 0)
420                 cfs_expr_list_free(expr_list);
421         else
422                 *elpp = expr_list;
423
424         return rc;
425 }
426
427 /**
428  * Frees cfs_expr_list structures of \a list.
429  *
430  * For each struct cfs_expr_list structure found on \a list it frees
431  * range_expr list attached to it and frees the cfs_expr_list itself.
432  *
433  * \retval none
434  */
435 void
436 cfs_expr_list_free_list(struct list_head *list)
437 {
438         struct cfs_expr_list *el;
439
440         while (!list_empty(list)) {
441                 el = list_entry(list->next,
442                                     struct cfs_expr_list, el_link);
443                 list_del(&el->el_link);
444                 cfs_expr_list_free(el);
445         }
446 }
447
448 /**
449  * cfs_abs_path() - Get the absolute path of a relative path
450  * @request_path:       The relative path to be resolved
451  * @resolved_path:      Set to the resolved absolute path
452  *
453  * Returns the canonicalized absolute pathname.  This function is a wrapper to
454  * realpath, but will work even if the target file does not exist.  All
455  * directories in the path must exist.
456  *
457  * Return: On success, 0 is returned and resolved_path points to an allocated
458  * string containing the absolute pathname.  On error, errno is set
459  * appropriately, -errno is returned, and resolved_path points to NULL.
460  */
461 int cfs_abs_path(const char *request_path, char **resolved_path)
462 {
463         char  buf[PATH_MAX + 1] = "";
464         char *path;
465         char *ptr;
466         int len;
467         int rc = 0;
468         const char *fmt;
469
470         path = malloc(sizeof(buf));
471         if (path == NULL)
472                 return -ENOMEM;
473
474         if (request_path[0] != '/') {
475                 if (getcwd(path, sizeof(buf) - 1) == NULL) {
476                         rc = -errno;
477                         goto out;
478                 }
479                 len = snprintf(buf, sizeof(buf), "%s/%s", path, request_path);
480                 if (len >= sizeof(buf)) {
481                         rc = -ENAMETOOLONG;
482                         goto out;
483                 }
484         } else {
485                 /* skip duplicate leading '/' */
486                 len = snprintf(buf, sizeof(buf), "%s",
487                                request_path + strspn(request_path, "/") - 1);
488                 if (len >= sizeof(buf)) {
489                         rc = -ENAMETOOLONG;
490                         goto out;
491                 }
492         }
493
494         /* if filename not in root directory, call realpath for parent path */
495         ptr = strrchr(buf, '/');
496         if (ptr != buf) {
497                 *ptr = '\0';
498                 if (path != realpath(buf, path)) {
499                         rc = -errno;
500                         goto out;
501                 }
502                 /* add the filename back */
503                 len = strlen(path);
504                 fmt = (path[len - 1] == '/') ? "%s" : "/%s";
505                 len = snprintf(path + len, sizeof(buf) - len, fmt, ptr + 1);
506                 if (len >= sizeof(buf) - len) {
507                         rc = -ENAMETOOLONG;
508                         goto out;
509                 }
510         } else {
511                 len = snprintf(path, sizeof(buf), "%s", buf);
512                 if (len >= sizeof(buf)) {
513                         rc = -ENAMETOOLONG;
514                         goto out;
515                 }
516         }
517
518 out:
519         if (rc == 0) {
520                 *resolved_path = path;
521         } else {
522                 *resolved_path = NULL;
523                 free(path);
524         }
525         return rc;
526 }