root/branches/cu-security-branch/src/common/misc/str-utils.c @ 8397

Revision 8397, 20.8 KB (checked in by nlmills, 3 years ago)

initial merge with Orange-Branch. much will be broken

Line 
1/*
2 * (C) 2001 Clemson University and The University of Chicago
3 *
4 * See COPYING in top-level directory.
5 */
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <errno.h>
11#include <ctype.h>
12#include <assert.h>
13
14#include "str-utils.h"
15
16/* PINT_string_count_segments()
17 *
18 * Count number of segments in a path.
19 *
20 * Parameters:
21 * pathname   - pointer to string
22 *
23 * Returns number of segments in pathname; -1 if
24 * pathname is invalid or has no components
25 *
26 * Example inputs and return values:
27 *
28 * NULL              - returns -1
29 * /                 - returns -1
30 * filename          - returns  1
31 * /filename         - returns  1
32 * /filename/        - returns  1
33 * /filename//       - returns  1
34 * /dirname/filename - returns  2
35 * dirname/filename
36 *
37 */
38int PINT_string_count_segments(char *pathname)
39{
40    int count = 0;
41    char *segp = (char *)0;
42    void *segstate;
43
44    while(!PINT_string_next_segment(pathname,&segp,&segstate))
45    {
46        count++;
47    }
48    return count;
49}
50
51/* PINT_get_base_dir()
52 *
53 * Get base (parent) dir of a absolute path
54 *
55 * Parameters:
56 * pathname     - pointer to directory string
57 * out_base_dir - pointer to out base dir string
58 * max_out_len  - max length of out_base_dir buffer
59 *
60 * All incoming arguments must be valid and non-zero
61 *
62 * Returns 0 on success; -1 if args are invalid
63 *
64 * Example inputs and outputs/return values:
65 *
66 * pathname: /tmp         - out_base_dir: /         - returns  0
67 * pathname: /tmp/foo     - out_base_dir: /tmp      - returns  0
68 * pathname: /tmp/foo/bar - out_base_dir: /tmp/foo  - returns  0
69 *
70 *
71 * invalid pathname input examples:
72 * pathname: /            - out_base_dir: undefined - returns -1
73 * pathname: NULL         - out_base_dir: undefined - returns -1
74 * pathname: foo          - out_base_dir: undefined - returns -1
75 *
76 */
77int PINT_get_base_dir(char *pathname, char *out_base_dir, int out_max_len)
78{
79    int ret = -1, len = 0;
80    char *start, *end;
81
82    if (pathname && out_base_dir && out_max_len)
83    {
84        if ((strcmp(pathname,"/") == 0) || (pathname[0] != '/'))
85        {
86            return ret;
87        }
88
89        start = pathname;
90        end = (char *)(pathname + strlen(pathname));
91
92        while(end && (end > start) && (*(--end) != '/'));
93
94        /*
95          get rid of trailing slash unless we're handling
96          the case where parent is the root directory
97          (in root dir case, len == 1)
98        */
99        len = ++end - start;
100        if (len != 1)
101        {
102            len--;
103        }
104        if (len < out_max_len)
105        {
106            memcpy(out_base_dir,start,len);
107            out_base_dir[len] = '\0';
108            ret = 0;
109        }
110    }
111    return ret;
112}
113
114/* PINT_string_next_segment()
115 *
116 * Parameters:
117 * pathname   - pointer to string
118 * inout_segp - address of pointer; NULL to get first segment,
119 *              pointer to last segment to get next segment
120 * opaquep    - address of void *, used to maintain state outside
121 *              the function
122 *
123 * Returns 0 if segment is available, -1 on end of string.
124 *
125 * This approach is nice because it keeps all the necessary state
126 * outside the function and concisely stores it in two pointers.
127 *
128 * Internals:
129 * We're using opaquep to store the location of where we placed a
130 * '\0' separator to get a segment.  The value is undefined when
131 * called with *inout_segp == NULL.  Afterwards, if we place a '\0',
132 * *opaquep will point to where we placed it.  If we do not, then we
133 * know that we've hit the end of the path string before we even
134 * start processing.
135 *
136 * Note that it is possible that *opaquep != NULL and still there are
137 * no more segments; a trailing '/' could cause this, for example.
138 */
139int PINT_string_next_segment(char *pathname,
140                             char **inout_segp,
141                             void **opaquep)
142{
143    char *ptr = (char *)0;
144
145    /* initialize our starting position */
146    if (*inout_segp == NULL)
147    {
148        ptr = pathname;
149    }
150    else if (*opaquep != NULL)
151    {
152        /* replace the '/', point just past it */
153        ptr = (char *) *opaquep;
154        *ptr = '/';
155        ptr++;
156    }
157    else
158        return -1; /* NULL *opaquep indicates last segment returned last time */
159
160    /* at this point, the string is back in its original state */
161
162    /* jump past separators */
163    while ((*ptr != '\0') && (*ptr == '/'))
164        ptr++;
165    if (*ptr == '\0')
166        return -1; /* all that was left was trailing '/'s */
167
168    *inout_segp = ptr;
169
170    /* find next separator */
171    while ((*ptr != '\0') && (*ptr != '/'))
172        ptr++;
173    if (*ptr == '\0')
174        *opaquep = NULL; /* indicate last segment */
175    else
176    {
177        /* terminate segment and save position of terminator */
178        *ptr = '\0';
179        *opaquep = ptr;
180    }
181    return 0;
182}
183
184
185/*
186 * PINT_parse_handle_ranges:  the first time this is called, set 'status' to
187 * zero.  get back the first range in the string in the out_extent
188 * variable.  keep calling PINT_parse_handle_ranges until it returns zero
189 *
190 * range            :  string representing our ranges
191 * out_extent->first:  (output) beginning of range
192 * out_extent->last :  (output) end of range
193 * status           :  (opaque) how far we are in the range string
194 *
195 * returns:
196 *  0: no more ranges
197 *  1: found a range.  look at 'first' and 'last' for the values
198 *  -1: something bad happened, possibly invalid arguments
199 */
200int PINT_parse_handle_ranges(
201    char *range,
202    PVFS_handle_extent *out_extent,
203    int *status)
204{
205    char *p = NULL, *endchar = NULL;
206
207    if (!out_extent || !status)
208    {
209        return -1;
210    }
211
212    p = range + *status;
213
214    /* from strtol(3):
215       If endptr is not NULL, strtoul() stores the address of the
216       first invalid character in *endptr.  If there were no dig­
217       its at all, strtoul() stores the original value of nptr in
218       *endptr  (and  returns 0).  In particular, if *nptr is not
219       `\0' but **endptr is `\0' on return, the entire string  is
220       valid.  */
221    out_extent->first = out_extent->last =
222#ifdef HAVE_STRTOULL
223        (PVFS_handle)strtoull(p, &endchar, 0);
224#else
225        (PVFS_handle)strtoul(p, &endchar, 0);
226#endif
227    if ( p == endchar )  /* all done */
228        return 0;
229    /* strtoul eats leading space, but not trailing space.  take care of ws
230     * between number and delimiter (- or ,) */
231    while (isspace(*endchar)) endchar++;
232   
233    p = endchar+1; /* skip over the ',' or '-'*/
234
235    switch (*endchar) {
236        case '-': /* we got the first half of the range. grab 2nd half */
237#ifdef HAVE_STRTOULL
238            out_extent->last = (PVFS_handle)strtoull(p, &endchar, 0);
239#else
240            out_extent->last = (PVFS_handle)strtoul(p, &endchar, 0);
241#endif
242            /* again, skip trailing space ...*/
243            while (isspace(*endchar)) endchar++;
244            /* ... and the delimiter */
245            if (*endchar == ',') endchar ++;
246            /* 'status' tells us how far we are in the string */
247            *status = ( endchar - range);
248            break;
249        case ',': /* end of a range */
250        case '\0': /* end of the whole string */
251            *status = ( p - range );
252            break;
253        default:
254            return -1;
255    }
256    return 1;
257}
258
259/*
260 * PINT_get_path_element:  gets the specified segment in the
261 * provided path.
262 *
263 * pathname   :  string containing a valid pathname
264 * segment_num:  the desired segment number in the path
265 * out_segment:  where the segment will be stored on success
266 * out_max_len:  max num bytes to store in out_segment
267 *
268 * returns:
269 *  0 : if the segment was found and copied
270 *  -1: if an invalid segment was specified
271 *
272 * Example inputs and outputs/return values:
273 *
274 * pathname: /mnt/pvfs2/foo, segment_num: 0
275 *     out_segment: mnt, returns 0
276 * pathname: /mnt/pvfs2/foo, segment_num: 2
277 *     out_segment: foo, returns 0
278 * pathname: /mnt/pvfs2/foo, segment_num: 5
279 *     out_segment: undefined, returns -1
280 */
281int PINT_get_path_element(
282    char *pathname,
283    int segment_num,
284    char *out_segment,
285    int out_max_len)
286{
287    int count = -1;
288    char *segp = (char *)0;
289    void *segstate = NULL;
290    char local_pathname[PVFS_NAME_MAX] = {0};
291
292    strncpy(local_pathname,pathname,PVFS_NAME_MAX);
293
294    while(!PINT_string_next_segment(local_pathname,&segp,&segstate))
295    {
296        if (++count == segment_num)
297        {
298            strncpy(out_segment,segp,(size_t)out_max_len);
299            break;
300        }
301    }
302    return ((count == segment_num) ? 0 : -1);
303}
304
305/* PINT_get_next_path
306 *
307 * gets remaining path given number of path segments to skip
308 *
309 * returns 0 on success, -errno on failure
310 */
311int PINT_get_next_path(char *path, char **newpath, int skip)
312{
313    int pathlen=0, i=0, num_slashes_seen=0;
314    int delimiter1=0;
315
316    pathlen = strlen(path) + 1;
317
318    /* find our starting point in the old path, it could be past multiple
319     * segments*/
320    for(i =0; i < pathlen; i++)
321    {
322        if (path[i] == '/')
323        {
324                 num_slashes_seen++;
325            if (num_slashes_seen > skip)
326            {
327                break;
328            }
329        }
330    }
331
332    delimiter1 = i;
333    if (pathlen - delimiter1 < 1)
334    {
335        return (-PVFS_EINVAL);
336    }
337
338    *newpath = malloc(pathlen - delimiter1);
339    if (*newpath == NULL)
340    {
341        return (-PVFS_ENOMEM);
342    }
343    memcpy(*newpath, &path[delimiter1], pathlen - delimiter1 );
344    /* *newpath[pathlen - delimiter1 -1 ] = '\0';*/
345    return(0);
346}
347
348/*
349 * PINT_split_string_list()
350 *
351 * separates a comma delimited list of items into an array of strings
352 *
353 * returns the number of strings successfully parsed
354 */
355int PINT_split_string_list(char ***tokens, const char *comma_list)
356{
357
358    const char *holder = NULL;
359    const char *holder2 = NULL;
360    const char *end = NULL;
361    int tokencount = 1, retval;
362    int i = -1;
363
364    if (!comma_list || !tokens)
365    {
366        return (0);
367    }
368
369    /* count how many commas we have first */
370    holder = comma_list;
371    while ((holder = index(holder, ',')))
372    {
373        tokencount++;
374        holder++;
375    }
376
377    /* if we don't find any commas, just set the entire string to the first
378     *  token and return
379     */
380    if(0 == tokencount)
381    {
382        tokencount = 1;
383    }
384
385    retval = tokencount;
386    /* allocate pointers for each */
387    *tokens = (char **) malloc(sizeof(char *) * tokencount);
388    if (!(*tokens))
389    {
390        return 0;
391    }
392
393    if(1 == tokencount)
394    {
395        (*tokens)[0] = strdup(comma_list);
396        if(!(*tokens)[0])
397        {
398            tokencount = 0;
399            goto failure;
400        }
401        return tokencount;
402    }
403
404    /* copy out all of the tokenized strings */
405    holder = comma_list;
406    end = comma_list + strlen(comma_list);
407    for (i = 0; i < tokencount && holder; i++)
408    {
409        holder2 = index(holder, ',');
410        if (!holder2)
411        {
412            holder2 = end;
413        }
414        if (holder2 - holder == 0) {
415            retval--;
416            return (retval);
417        }
418        (*tokens)[i] = (char *) malloc((holder2 - holder) + 1);
419        if (!(*tokens)[i])
420        {
421            goto failure;
422        }
423        strncpy((*tokens)[i], holder, (holder2 - holder));
424        (*tokens)[i][(holder2 - holder)] = '\0';
425        assert(strlen((*tokens)[i]) != 0);
426        holder = holder2 + 1;
427    }
428
429    return (retval);
430
431failure:
432
433    /* free up any memory we allocated if we failed */
434    if (*tokens)
435    {
436        for (i = 0; i < tokencount; i++)
437        {
438            if ((*tokens)[i])
439            {
440                free((*tokens)[i]);
441            }
442        }
443        free(*tokens);
444    }
445    return (0);
446}
447
448/* PINT_free_string_list()
449 *
450 * Free the string list allocated by PINT_split_string_list()
451 */
452void PINT_free_string_list(char ** list, int len)
453{
454    int i = 0;
455
456    if(list)
457    {
458        for(; i < len; ++i)
459        {
460            if(list[i])
461            {
462                free(list[i]);
463            }
464        }
465        free(list);
466    }
467}
468
469/* PINT_remove_base_dir()
470 *
471 * Get absolute path minus the base dir
472 *
473 * Parameters:
474 * pathname     - pointer to directory string
475 * out_base_dir - pointer to out dir string
476 * max_out_len  - max length of out_base_dir buffer
477 *
478 * All incoming arguments must be valid and non-zero
479 *
480 * Returns 0 on success; -1 if args are invalid
481 *
482 * Example inputs and outputs/return values:
483 *
484 * pathname: /tmp/foo     - out_base_dir: foo       - returns  0
485 * pathname: /tmp/foo/bar - out_base_dir: bar       - returns  0
486 *
487 *
488 * invalid pathname input examples:
489 * pathname: /            - out_base_dir: undefined - returns -1
490 * pathname: NULL         - out_base_dir: undefined - returns -1
491 * pathname: foo          - out_base_dir: undefined - returns -1
492 *
493 */
494int PINT_remove_base_dir(
495    const char *pathname,
496    char *out_dir,
497    int out_max_len)
498{
499    int ret = -1, len = 0;
500    const char *start, *end, *end_ref;
501
502    if (pathname && out_dir && out_max_len)
503    {
504        if ((strcmp(pathname, "/") == 0) || (pathname[0] != '/'))
505        {
506            return -PVFS_ENOTDIR;
507        }
508
509        start = pathname;
510        end = pathname + strlen(pathname);
511        end_ref = end;
512
513        while (end && (end > start) && (*(--end) != '/'));
514
515        len = end_ref - ++end;
516        if (len < out_max_len)
517        {
518            memcpy(out_dir, end, len);
519            out_dir[len] = '\0';
520            ret = 0;
521        }
522    }
523    return ret;
524}
525
526/* PINT_remove_dir_prefix()
527 *
528 * Strips prefix directory out of the path, output includes beginning
529 * slash
530 *
531 * Parameters:
532 * pathname     - pointer to directory string (absolute)
533 * prefix       - pointer to prefix dir string (absolute)
534 * out_path     - pointer to output dir string
535 * max_out_len  - max length of out_base_dir buffer
536 *
537 * All incoming arguments must be valid and non-zero
538 *
539 * Returns 0 on success; -errno on failure
540 *
541 * Example inputs and outputs/return values:
542 *
543 * pathname: /mnt/pvfs2/foo, prefix: /mnt/pvfs2
544 *     out_path: /foo, returns 0
545 * pathname: /mnt/pvfs2/foo, prefix: /mnt/pvfs2/
546 *     out_path: /foo, returns 0
547 * pathname: /mnt/pvfs2/foo/bar, prefix: /mnt/pvfs2
548 *     out_path: /foo/bar, returns 0
549 * pathname: /mnt/pvfs2/foo/bar, prefix: /
550 *     out_path: /mnt/pvfs2/foo/bar, returns 0
551 *
552 * invalid pathname input examples:
553 * pathname: /mnt/foo/bar, prefix: /mnt/pvfs2
554 *     out_path: undefined, returns -PVFS_ENOENT
555 * pathname: /mnt/pvfs2fake/foo/bar, prefix: /mnt/pvfs2
556 *     out_path: undefined, returns -PVFS_ENOENT
557 * pathname: /mnt/foo/bar, prefix: mnt/pvfs2
558 *     out_path: undefined, returns -PVFS_EINVAL
559 * pathname: mnt/foo/bar, prefix: /mnt/pvfs2
560 *     out_path: undefined, returns -PVFS_EINVAL
561 * out_max_len not large enough for buffer, returns -PVFS_ENAMETOOLONG
562 */
563int PINT_remove_dir_prefix(
564    const char *pathname,
565    const char *prefix,
566    char *out_path,
567    int out_max_len)
568{
569    int ret = -PVFS_EINVAL;
570    int prefix_len, pathname_len;
571    int cut_index;
572
573    if (!pathname || !prefix || !out_path || !out_max_len)
574    {
575        return ret;
576    }
577
578    /* make sure we are given absolute paths */
579    if ((pathname[0] != '/') || (prefix[0] != '/'))
580    {
581        return ret;
582    }
583
584    while (pathname[1] == '/')
585        pathname++;
586
587    prefix_len = strlen(prefix);
588    pathname_len = strlen(pathname);
589
590    /* account for trailing slashes on prefix */
591    while (prefix[prefix_len - 1] == '/')
592    {
593        prefix_len--;
594    }
595
596    /* if prefix_len is now zero, then prefix must have been root
597     * directory; return copy of entire pathname
598     */
599    if (prefix_len == 0)
600    {
601        cut_index = 0;
602    }
603    else
604    {
605
606        /* make sure prefix would fit in pathname */
607        if (prefix_len > (pathname_len + 1))
608            return (-PVFS_ENOENT);
609
610        /* see if we can find prefix at beginning of path */
611        if (strncmp(prefix, pathname, prefix_len) == 0)
612        {
613            /* apparent match; see if next element is a slash */
614            if ((pathname[prefix_len] != '/') &&
615                (pathname[prefix_len] != '\0'))
616                return (-PVFS_ENOENT);
617
618            /* this was indeed a match */
619            /* in the case of no trailing slash cut_index will point to the end
620             * of "prefix" (NULL).   */
621            cut_index = prefix_len;
622        }
623        else
624        {
625            return (-PVFS_ENOENT);
626        }
627    }
628
629    /* if we hit this point, then we were successful */
630
631    /* is the buffer large enough? */
632    if ((1 + strlen(&(pathname[cut_index]))) > out_max_len)
633        return (-PVFS_ENAMETOOLONG);
634
635    /* try to handle the case of no trailing slash */
636    if (pathname[cut_index] == '\0')
637    {
638        out_path[0] = '/';
639        out_path[1] = '\0';
640    }
641    else
642        /* copy out appropriate part of pathname */
643        strcpy(out_path, &(pathname[cut_index]));
644
645    return (0);
646}
647
648char *PINT_merge_handle_range_strs(char *range1, char *range2)
649{
650    char *merged_range = NULL;
651
652    if (range1 && range2)
653    {
654        int rlen1 = strlen(range1) * sizeof(char) + 1;
655        int rlen2 = strlen(range2) * sizeof(char) + 1;
656        /*
657          2 bytes bigger since we need a tz null and space for the
658          additionally inserted comma
659        */
660        merged_range = (char *)malloc(rlen1 + rlen2);
661        snprintf(merged_range, rlen1 + rlen2, "%s,%s",
662                 range1,range2);
663    }
664    return merged_range;
665}
666
667#ifndef HAVE_STRNLEN
668/* a naive implementation of strnlen for systems w/o glibc */
669size_t strnlen(const char *s, size_t limit)
670{
671   size_t len = 0;
672   while ((len < limit) && (*s++))
673     len++;
674   return len;
675}
676#endif
677
678#ifndef HAVE_STRSTR
679/* a custom implementation of strstr for systems w/o it */
680char *strstr(const char *haystack, const char *needle)
681{
682    char *ptr = NULL;
683    int needle_len = 0;
684    int remaining_len = 0;
685
686    ptr = (char *)haystack;
687    if (haystack && needle)
688    {
689        needle_len = strlen(needle);
690        remaining_len = strlen(haystack);
691
692        while(ptr)
693        {
694            if (*ptr == *needle)
695            {
696                if (memcmp(ptr, needle, needle_len) == 0)
697                {
698                    break;
699                }
700            }
701            ptr++;
702            remaining_len--;
703
704            if ((remaining_len < needle_len) || (*ptr == '\0'))
705            {
706                ptr = NULL;
707                break;
708            }
709        }
710    }
711    return ptr;
712}
713#endif
714
715/*
716 * PINT_split_keyvals()
717 *
718 * Splits a given string into a number of key:val strings.
719 *
720 * Parameters:
721 * The given string must be comma separated, and each
722 * segment within the comma regions must be of of
723 * the form key:val.
724 * Return the number of such keyval pairs and a
725 * pointer to a double dimensional array of keys and values.
726 * In case of errors, a -ve PVFS error is returned.
727 *
728 * Example inputs and return values:
729 *
730 * NULL - return -PVFS_EINVAL
731 * ab:23 - return nkey as 1, pkey <"ab">, pval <"23">
732 * ab:23,bc:34 - returns nkey as 2, pkey <"ab", "bc">, pval<"23", "34">
733 *
734 */
735int PINT_split_keyvals(char *string, int *nkey,
736        char ***pkey, char ***pval)
737{
738    char **key, **val, *ptr, *params;
739    int nparams = 0, i;
740
741    if (string == NULL || nkey == NULL
742            || pkey == NULL || pval == NULL)
743    {
744      return -PVFS_EINVAL;
745    }
746    params = strdup(string);
747    if (params == NULL)
748    {
749      return -PVFS_ENOMEM;
750    }
751    ptr = params;
752    while (ptr)
753    {
754        if (*ptr != ',' || *ptr != '\0')
755                 nparams++;
756        ptr++;
757        ptr = strchr(ptr, ',');
758    }
759    if (nparams == 0)
760    {
761      free(params);
762      return -PVFS_EINVAL;
763    }
764    ptr = params;
765    key = (char **) calloc(nparams, sizeof(char *));
766    val = (char **) calloc(nparams, sizeof(char *));
767    if (key == NULL || val ==  NULL)
768    {
769      free(key);
770      free(val);
771      free(params);
772      return -PVFS_ENOMEM;
773    }
774    for (i = 0; i < nparams; i++)
775    {
776        char *ptr2;
777        if (i > 0 && ptr)
778        {
779            *ptr = '\0';
780            ptr++;
781        }
782        else if (ptr == NULL)
783        {
784            break;
785        }
786        ptr2 = strchr(ptr, ':');
787        if (ptr2 == NULL)
788        {
789            break;
790        }
791        key[i] = ptr;
792        ptr = strchr(ptr, ',');
793        if (ptr != NULL && ptr < ptr2)
794        {
795            break;
796        }
797        *ptr2 = '\0';
798        val[i] = ptr2 + 1;
799    }
800    if (i != nparams)
801    {
802      free(key);
803      free(val);
804      free(params);
805      return -PVFS_EINVAL;
806    }
807    else
808    {
809      for (i = 0; i < nparams; i++)
810      {
811          char *ptr1, *ptr2;
812          ptr1 = strdup(key[i]);
813          ptr2 = strdup(val[i]);
814          if (ptr1 == NULL || ptr2 == NULL)
815              break;
816          if (strchr(ptr1, ':') || strchr(ptr2, ':'))
817              break;
818          key[i] = ptr1;
819          val[i] = ptr2;
820      }
821      if (i != nparams)
822      {
823          int j;
824          for (j = 0; j < i; j++)
825          {
826              if (key[j]) free(key[j]);
827              if (val[j]) free(val[j]);
828          }
829          free(key);
830          free(val);
831          free(params);
832          return -PVFS_EINVAL;
833      }
834      free(params);
835      *nkey = nparams;
836      *pkey = key;
837      *pval = val;
838      return 0;
839    }
840}
841
842
843/*
844 * Local variables:
845 *  c-indent-level: 4
846 *  c-basic-offset: 4
847 * End:
848 *
849 * vim: ts=8 sts=4 sw=4 expandtab
850 */
851
852
Note: See TracBrowser for help on using the browser.