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