1 /*
2 * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of version 2 of the GNU General Public License as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it would be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * Further, this software is distributed without any warranty that it is
13 * free of the rightful claim of any third person regarding infringement
14 * or the like. Any license provided herein, whether implied or
15 * otherwise, applies only to this software file. Patent licenses, if
16 * any, provided herein do not apply to combinations of this program with
17 * other software, or any other product whatsoever.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 *
23 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24 * Mountain View, CA 94043, or:
25 *
26 * http://www.sgi.com
27 *
28 * For further information regarding this notice, see:
29 *
30 * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
31 *
32 */
33 /* $Id: splitstr.c,v 1.2 2000/09/21 20:42:31 nstraz Exp $ */
34 /*
35 * Synopsis
36 *
37 * const char **splitstr(const char *str, const char *separator, int *argcount)
38 *
39 * Description
40 * This function splits a string (str) into components that are separated by
41 * one or more of the characters in the (separator) string. An array of
42 * strings is returned, along with argcount being set to the number of strings
43 * found. Argcount can be NULL. There will always be a NULL element in the
44 * array after the last valid element. If an error occurs, NULL will be
45 * returned and argcount will be set to zero.
46 *
47 * To rid yourself of the memory allocated for splitstr(), pass the return
48 * value from splitstr() unmodified to splitstr_free():
49 *
50 * void splitstr_free( const char ** return_from_splitstr );
51 *
52 */
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h> /* for string functions */
56 #ifdef UNIT_TEST
57 #include <assert.h>
58 #endif /* UNIT_TEST */
59 #include "splitstr.h"
60
splitstr(const char * str,const char * separator,int * argcount)61 const char **splitstr(const char *str, const char *separator, int *argcount)
62 {
63 char *arg_string = NULL, **arg_array = NULL, *cur_tok = NULL;
64
65 int num_toks = 0, max_toks = 20, i;
66
67 /*
68 * In most recoverable errors, if argcount is not NULL,
69 * set argcount to 0. Then return NULL.
70 */
71 if (str == NULL) {
72 if (argcount != NULL)
73 *argcount = 0;
74 return (NULL);
75 }
76
77 /*
78 * set aside temporary space to work on the string.
79 */
80 arg_string = strdup(str);
81
82 if (arg_string == NULL) {
83 if (argcount != NULL)
84 *argcount = 0;
85 return (NULL);
86 }
87
88 /*
89 * set aside an initial char ** array for string array.
90 */
91 arg_array = malloc(sizeof(char *) * max_toks);
92
93 if (arg_array == NULL) {
94 if (argcount != NULL)
95 *argcount = 0;
96 free(arg_string);
97 return (NULL);
98 }
99
100 if (separator == NULL)
101 separator = " \t";
102
103 /*
104 * Use strtok() to parse 'arg_string', placing pointers to the
105 * individual tokens into the elements of 'arg_array'. Expand
106 * 'arg_array' if necessary.
107 */
108 cur_tok = strtok(arg_string, separator);
109 while (cur_tok != NULL) {
110 arg_array[num_toks++] = cur_tok;
111 cur_tok = strtok(NULL, separator);
112 if (num_toks == max_toks) {
113 max_toks += 20;
114 arg_array =
115 (char **)realloc((void *)arg_array,
116 sizeof(char *) * max_toks);
117 if (arg_array == NULL) {
118 fprintf(stderr, "realloc: New memory allocation failed \n");
119 free(arg_string);
120 exit(1);
121 }
122 }
123 }
124 arg_array[num_toks] = NULL;
125
126 /*
127 * If there are any spaces left in our array, make them NULL
128 */
129 for (i = num_toks + 1; i < max_toks; i++)
130 arg_array[i] = NULL;
131
132 /* This seems nice, but since memory is allocated on a page basis, this
133 * isn't really helpful:
134 * arg_array = (char **)realloc((void *)arg_array, sizeof(char *)*num_toks+1 );*/
135
136 if (argcount != NULL)
137 *argcount = num_toks;
138
139 /*
140 * Return the argument array.
141 */
142 return ((const char **)arg_array);
143 }
144
145 /*
146 * splitster_free( const char ** )
147 *
148 * This takes the return value from splitster() and free()s memory
149 * allocated by splitster. Assuming: ret=splitster(...), this
150 * requires that ret and *ret returned from splitster() have not
151 * been modified.
152 */
splitstr_free(const char ** p_return)153 void splitstr_free(const char **p_return)
154 {
155 if (*p_return != NULL)
156 free((char *)*p_return);
157 if (p_return != NULL)
158 free((char **)p_return);
159 }
160
161 #ifdef UNIT_TEST
162
main()163 int main()
164 {
165 int i, y, test_size = 1000, size_ret;
166 char test_str[32768];
167 char buf[16];
168 char *test_str_array[test_size];
169 const char **ret;
170
171 for (i = 0; i < test_size; i++) {
172 snprintf(buf, 16, "arg%d", i);
173 test_str_array[i] = strdup(buf);
174 }
175
176 for (i = 0; i < test_size; i++) {
177 test_str[0] = '\0';
178 for (y = 0; y < i; y++) {
179 snprintf(buf, 16, "arg%d ", y);
180 strncat(test_str, buf, 16);
181 }
182 ret = splitstr(test_str, NULL, &size_ret);
183 assert(size_ret == i);
184 for (y = 0; y < i; y++)
185 assert(strcmp(ret[y], test_str_array[y]) == 0);
186
187 splitstr_free(ret);
188 }
189 return 0;
190 }
191
192 #endif
193