1 /* Copyright (C) 2018 by John Schember <john@nachtimwald.com>
2  *
3  * Permission to use, copy, modify, and distribute this
4  * software and its documentation for any purpose and without
5  * fee is hereby granted, provided that the above copyright
6  * notice appear in all copies and that both that copyright
7  * notice and this permission notice appear in supporting
8  * documentation, and that the name of M.I.T. not be used in
9  * advertising or publicity pertaining to distribution of the
10  * software without specific, written prior permission.
11  * M.I.T. makes no representations about the suitability of
12  * this software for any purpose.  It is provided "as is"
13  * without express or implied warranty.
14  */
15 
16 #include "ares_setup.h"
17 #include "ares_strsplit.h"
18 #include "ares.h"
19 #include "ares_private.h"
20 
list_contains(char * const * list,size_t num_elem,const char * str,int insensitive)21 static int list_contains(char * const *list, size_t num_elem, const char *str, int insensitive)
22 {
23   size_t len;
24   size_t i;
25 
26   len = strlen(str);
27   for (i=0; i<num_elem; i++)
28   {
29     if (insensitive)
30     {
31 #ifdef WIN32
32       if (strnicmp(list[i], str, len) == 0)
33 #else
34       if (strncasecmp(list[i], str, len) == 0)
35 #endif
36         return 1;
37     }
38     else
39     {
40       if (strncmp(list[i], str, len) == 0)
41         return 1;
42     }
43   }
44 
45   return 0;
46 }
47 
is_delim(char c,const char * delims,size_t num_delims)48 static int is_delim(char c, const char *delims, size_t num_delims)
49 {
50   size_t i;
51 
52   for (i=0; i<num_delims; i++)
53   {
54     if (c == delims[i])
55       return 1;
56   }
57   return 0;
58 }
59 
60 
ares_strsplit_free(char ** elms,size_t num_elm)61 void ares_strsplit_free(char **elms, size_t num_elm)
62 {
63   size_t i;
64 
65   if (elms == NULL)
66     return;
67 
68   for (i=0; i<num_elm; i++)
69     ares_free(elms[i]);
70   ares_free(elms);
71 }
72 
73 
ares_strsplit(const char * in,const char * delms,int make_set,size_t * num_elm)74 char **ares_strsplit(const char *in, const char *delms, int make_set, size_t *num_elm)
75 {
76   char *parsestr;
77   char **temp;
78   char **out;
79   size_t cnt;
80   size_t nelms;
81   size_t in_len;
82   size_t num_delims;
83   size_t i;
84 
85   if (in == NULL || delms == NULL || num_elm == NULL)
86     return NULL;
87 
88   *num_elm = 0;
89 
90   in_len = strlen(in);
91   num_delims = strlen(delms);
92 
93   /* Figure out how many elements. */
94   nelms = 1;
95   for (i=0; i<in_len; i++)
96   {
97     if (is_delim(in[i], delms, num_delims))
98     {
99       nelms++;
100     }
101   }
102 
103   /* Copy of input so we can cut it up. */
104   parsestr = ares_strdup(in);
105   if (parsestr == NULL)
106     return NULL;
107 
108   /* Temporary array to store locations of start of each element
109    * within parsestr. */
110   temp = ares_malloc(nelms * sizeof(*temp));
111   if (temp == NULL)
112   {
113     ares_free(parsestr);
114     return NULL;
115   }
116   temp[0] = parsestr;
117   cnt = 1;
118   for (i=0; i<in_len && cnt<nelms; i++)
119   {
120     if (!is_delim(parsestr[i], delms, num_delims))
121       continue;
122 
123     /* Replace sep with NULL. */
124     parsestr[i] = '\0';
125     /* Add the pointer to the array of elements */
126     temp[cnt] = parsestr+i+1;
127     cnt++;
128   }
129 
130   /* Copy each element to our output array. */
131   out = ares_malloc(nelms * sizeof(*out));
132   if (out == NULL)
133   {
134     ares_free(parsestr);
135     ares_free(temp);
136     return NULL;
137   }
138 
139   nelms = 0;
140   for (i=0; i<cnt; i++)
141   {
142     if (temp[i][0] == '\0')
143       continue;
144 
145     if (make_set && list_contains(out, nelms, temp[i], 1))
146       continue;
147 
148     out[nelms] = ares_strdup(temp[i]);
149     if (out[nelms] == NULL)
150     {
151       ares_strsplit_free(out, nelms);
152       ares_free(parsestr);
153       ares_free(temp);
154       return NULL;
155     }
156     nelms++;
157   }
158 
159 
160   /* If there are no elements don't return an empty allocated
161    * array. */
162   if (nelms == 0)
163   {
164     ares_strsplit_free(out, nelms);
165     out = NULL;
166   }
167 
168   /* Get the true number of elements (recalculated because of make_set) */
169   *num_elm = nelms;
170 
171   ares_free(parsestr);
172   ares_free(temp);
173   return out;
174 }
175