1*2ed2be6eSchl /* $OpenBSD: split.c,v 1.5 2007/09/09 23:25:12 chl Exp $ */
2df930be7Sderaadt /* $NetBSD: split.c,v 1.2 1995/04/20 22:39:57 cgd Exp $ */
3df930be7Sderaadt
4df930be7Sderaadt #include <stdio.h>
5df930be7Sderaadt #include <string.h>
6df930be7Sderaadt
7db3296cfSderaadt int split(char *string, char *fields[], int nfields, char *sep);
8db3296cfSderaadt
9df930be7Sderaadt /*
10df930be7Sderaadt - split - divide a string into fields, like awk split()
11df930be7Sderaadt = int split(char *string, char *fields[], int nfields, char *sep);
12df930be7Sderaadt */
13df930be7Sderaadt int /* number of fields, including overflow */
split(char * string,char * fields[],int nfields,char * sep)14db3296cfSderaadt split(char *string, char *fields[], int nfields, char *sep)
15df930be7Sderaadt {
16df930be7Sderaadt register char *p = string;
17df930be7Sderaadt register char c; /* latest character */
18df930be7Sderaadt register char sepc = sep[0];
19df930be7Sderaadt register char sepc2;
20df930be7Sderaadt register int fn;
21df930be7Sderaadt register char **fp = fields;
22df930be7Sderaadt register char *sepp;
23df930be7Sderaadt register int trimtrail;
24df930be7Sderaadt
25df930be7Sderaadt /* white space */
26df930be7Sderaadt if (sepc == '\0') {
27df930be7Sderaadt while ((c = *p++) == ' ' || c == '\t')
28df930be7Sderaadt continue;
29df930be7Sderaadt p--;
30df930be7Sderaadt trimtrail = 1;
31df930be7Sderaadt sep = " \t"; /* note, code below knows this is 2 long */
32df930be7Sderaadt sepc = ' ';
33df930be7Sderaadt } else
34df930be7Sderaadt trimtrail = 0;
35df930be7Sderaadt sepc2 = sep[1]; /* now we can safely pick this up */
36df930be7Sderaadt
37df930be7Sderaadt /* catch empties */
38df930be7Sderaadt if (*p == '\0')
39df930be7Sderaadt return(0);
40df930be7Sderaadt
41df930be7Sderaadt /* single separator */
42df930be7Sderaadt if (sepc2 == '\0') {
43df930be7Sderaadt fn = nfields;
44df930be7Sderaadt for (;;) {
45df930be7Sderaadt *fp++ = p;
46df930be7Sderaadt fn--;
47df930be7Sderaadt if (fn == 0)
48df930be7Sderaadt break;
49df930be7Sderaadt while ((c = *p++) != sepc)
50df930be7Sderaadt if (c == '\0')
51df930be7Sderaadt return(nfields - fn);
52df930be7Sderaadt *(p-1) = '\0';
53df930be7Sderaadt }
54df930be7Sderaadt /* we have overflowed the fields vector -- just count them */
55df930be7Sderaadt fn = nfields;
56df930be7Sderaadt for (;;) {
57df930be7Sderaadt while ((c = *p++) != sepc)
58df930be7Sderaadt if (c == '\0')
59df930be7Sderaadt return(fn);
60df930be7Sderaadt fn++;
61df930be7Sderaadt }
62df930be7Sderaadt /* not reached */
63df930be7Sderaadt }
64df930be7Sderaadt
65df930be7Sderaadt /* two separators */
66df930be7Sderaadt if (sep[2] == '\0') {
67df930be7Sderaadt fn = nfields;
68df930be7Sderaadt for (;;) {
69df930be7Sderaadt *fp++ = p;
70df930be7Sderaadt fn--;
71df930be7Sderaadt while ((c = *p++) != sepc && c != sepc2)
72df930be7Sderaadt if (c == '\0') {
73df930be7Sderaadt if (trimtrail && **(fp-1) == '\0')
74df930be7Sderaadt fn++;
75df930be7Sderaadt return(nfields - fn);
76df930be7Sderaadt }
77df930be7Sderaadt if (fn == 0)
78df930be7Sderaadt break;
79df930be7Sderaadt *(p-1) = '\0';
80df930be7Sderaadt while ((c = *p++) == sepc || c == sepc2)
81df930be7Sderaadt continue;
82df930be7Sderaadt p--;
83df930be7Sderaadt }
84df930be7Sderaadt /* we have overflowed the fields vector -- just count them */
85df930be7Sderaadt fn = nfields;
86df930be7Sderaadt while (c != '\0') {
87df930be7Sderaadt while ((c = *p++) == sepc || c == sepc2)
88df930be7Sderaadt continue;
89df930be7Sderaadt p--;
90df930be7Sderaadt fn++;
91df930be7Sderaadt while ((c = *p++) != '\0' && c != sepc && c != sepc2)
92df930be7Sderaadt continue;
93df930be7Sderaadt }
94df930be7Sderaadt /* might have to trim trailing white space */
95df930be7Sderaadt if (trimtrail) {
96df930be7Sderaadt p--;
97df930be7Sderaadt while ((c = *--p) == sepc || c == sepc2)
98df930be7Sderaadt continue;
99df930be7Sderaadt p++;
100df930be7Sderaadt if (*p != '\0') {
101df930be7Sderaadt if (fn == nfields+1)
102df930be7Sderaadt *p = '\0';
103df930be7Sderaadt fn--;
104df930be7Sderaadt }
105df930be7Sderaadt }
106df930be7Sderaadt return(fn);
107df930be7Sderaadt }
108df930be7Sderaadt
109df930be7Sderaadt /* n separators */
110df930be7Sderaadt fn = 0;
111df930be7Sderaadt for (;;) {
112df930be7Sderaadt if (fn < nfields)
113df930be7Sderaadt *fp++ = p;
114df930be7Sderaadt fn++;
115df930be7Sderaadt for (;;) {
116df930be7Sderaadt c = *p++;
117df930be7Sderaadt if (c == '\0')
118df930be7Sderaadt return(fn);
119df930be7Sderaadt sepp = sep;
120df930be7Sderaadt while ((sepc = *sepp++) != '\0' && sepc != c)
121df930be7Sderaadt continue;
122df930be7Sderaadt if (sepc != '\0') /* it was a separator */
123df930be7Sderaadt break;
124df930be7Sderaadt }
125df930be7Sderaadt if (fn < nfields)
126df930be7Sderaadt *(p-1) = '\0';
127df930be7Sderaadt for (;;) {
128df930be7Sderaadt c = *p++;
129df930be7Sderaadt sepp = sep;
130df930be7Sderaadt while ((sepc = *sepp++) != '\0' && sepc != c)
131df930be7Sderaadt continue;
132df930be7Sderaadt if (sepc == '\0') /* it wasn't a separator */
133df930be7Sderaadt break;
134df930be7Sderaadt }
135df930be7Sderaadt p--;
136df930be7Sderaadt }
137df930be7Sderaadt
138df930be7Sderaadt /* not reached */
139df930be7Sderaadt }
140df930be7Sderaadt
141df930be7Sderaadt #ifdef TEST_SPLIT
142df930be7Sderaadt
143df930be7Sderaadt
144df930be7Sderaadt /*
145df930be7Sderaadt * test program
146df930be7Sderaadt * pgm runs regression
147df930be7Sderaadt * pgm sep splits stdin lines by sep
148df930be7Sderaadt * pgm str sep splits str by sep
149df930be7Sderaadt * pgm str sep n splits str by sep n times
150df930be7Sderaadt */
151df930be7Sderaadt int
main(argc,argv)152df930be7Sderaadt main(argc, argv)
153df930be7Sderaadt int argc;
154df930be7Sderaadt char *argv[];
155df930be7Sderaadt {
156df930be7Sderaadt char buf[512];
157df930be7Sderaadt register int n;
158df930be7Sderaadt # define MNF 10
159df930be7Sderaadt char *fields[MNF];
160df930be7Sderaadt
161df930be7Sderaadt if (argc > 4)
162df930be7Sderaadt for (n = atoi(argv[3]); n > 0; n--) {
163112e1910Sderaadt (void) strlcpy(buf, argv[1], sizeof buf);
164df930be7Sderaadt }
165df930be7Sderaadt else if (argc > 3)
166df930be7Sderaadt for (n = atoi(argv[3]); n > 0; n--) {
167112e1910Sderaadt (void) strlcpy(buf, argv[1], sizeof buf);
168df930be7Sderaadt (void) split(buf, fields, MNF, argv[2]);
169df930be7Sderaadt }
170df930be7Sderaadt else if (argc > 2)
171df930be7Sderaadt dosplit(argv[1], argv[2]);
172df930be7Sderaadt else if (argc > 1)
173df930be7Sderaadt while (fgets(buf, sizeof(buf), stdin) != NULL) {
174*2ed2be6eSchl buf[strcspn(buf, "\n")] = '\0'; /* stomp newline */
175df930be7Sderaadt dosplit(buf, argv[1]);
176df930be7Sderaadt }
177df930be7Sderaadt else
178df930be7Sderaadt regress();
179df930be7Sderaadt
180df930be7Sderaadt exit(0);
181df930be7Sderaadt }
182df930be7Sderaadt
dosplit(string,seps)183df930be7Sderaadt dosplit(string, seps)
184df930be7Sderaadt char *string;
185df930be7Sderaadt char *seps;
186df930be7Sderaadt {
187df930be7Sderaadt # define NF 5
188df930be7Sderaadt char *fields[NF];
189df930be7Sderaadt register int nf;
190df930be7Sderaadt
191df930be7Sderaadt nf = split(string, fields, NF, seps);
192df930be7Sderaadt print(nf, NF, fields);
193df930be7Sderaadt }
194df930be7Sderaadt
print(nf,nfp,fields)195df930be7Sderaadt print(nf, nfp, fields)
196df930be7Sderaadt int nf;
197df930be7Sderaadt int nfp;
198df930be7Sderaadt char *fields[];
199df930be7Sderaadt {
200df930be7Sderaadt register int fn;
201df930be7Sderaadt register int bound;
202df930be7Sderaadt
203df930be7Sderaadt bound = (nf > nfp) ? nfp : nf;
204df930be7Sderaadt printf("%d:\t", nf);
205df930be7Sderaadt for (fn = 0; fn < bound; fn++)
206df930be7Sderaadt printf("\"%s\"%s", fields[fn], (fn+1 < nf) ? ", " : "\n");
207df930be7Sderaadt }
208df930be7Sderaadt
209df930be7Sderaadt #define RNF 5 /* some table entries know this */
210df930be7Sderaadt struct {
211df930be7Sderaadt char *str;
212df930be7Sderaadt char *seps;
213df930be7Sderaadt int nf;
214df930be7Sderaadt char *fi[RNF];
215df930be7Sderaadt } tests[] = {
216df930be7Sderaadt "", " ", 0, { "" },
217df930be7Sderaadt " ", " ", 2, { "", "" },
218df930be7Sderaadt "x", " ", 1, { "x" },
219df930be7Sderaadt "xy", " ", 1, { "xy" },
220df930be7Sderaadt "x y", " ", 2, { "x", "y" },
221df930be7Sderaadt "abc def g ", " ", 5, { "abc", "def", "", "g", "" },
222df930be7Sderaadt " a bcd", " ", 4, { "", "", "a", "bcd" },
223df930be7Sderaadt "a b c d e f", " ", 6, { "a", "b", "c", "d", "e f" },
224df930be7Sderaadt " a b c d ", " ", 6, { "", "a", "b", "c", "d " },
225df930be7Sderaadt
226df930be7Sderaadt "", " _", 0, { "" },
227df930be7Sderaadt " ", " _", 2, { "", "" },
228df930be7Sderaadt "x", " _", 1, { "x" },
229df930be7Sderaadt "x y", " _", 2, { "x", "y" },
230df930be7Sderaadt "ab _ cd", " _", 2, { "ab", "cd" },
231df930be7Sderaadt " a_b c ", " _", 5, { "", "a", "b", "c", "" },
232df930be7Sderaadt "a b c_d e f", " _", 6, { "a", "b", "c", "d", "e f" },
233df930be7Sderaadt " a b c d ", " _", 6, { "", "a", "b", "c", "d " },
234df930be7Sderaadt
235df930be7Sderaadt "", " _~", 0, { "" },
236df930be7Sderaadt " ", " _~", 2, { "", "" },
237df930be7Sderaadt "x", " _~", 1, { "x" },
238df930be7Sderaadt "x y", " _~", 2, { "x", "y" },
239df930be7Sderaadt "ab _~ cd", " _~", 2, { "ab", "cd" },
240df930be7Sderaadt " a_b c~", " _~", 5, { "", "a", "b", "c", "" },
241df930be7Sderaadt "a b_c d~e f", " _~", 6, { "a", "b", "c", "d", "e f" },
242df930be7Sderaadt "~a b c d ", " _~", 6, { "", "a", "b", "c", "d " },
243df930be7Sderaadt
244df930be7Sderaadt "", " _~-", 0, { "" },
245df930be7Sderaadt " ", " _~-", 2, { "", "" },
246df930be7Sderaadt "x", " _~-", 1, { "x" },
247df930be7Sderaadt "x y", " _~-", 2, { "x", "y" },
248df930be7Sderaadt "ab _~- cd", " _~-", 2, { "ab", "cd" },
249df930be7Sderaadt " a_b c~", " _~-", 5, { "", "a", "b", "c", "" },
250df930be7Sderaadt "a b_c-d~e f", " _~-", 6, { "a", "b", "c", "d", "e f" },
251df930be7Sderaadt "~a-b c d ", " _~-", 6, { "", "a", "b", "c", "d " },
252df930be7Sderaadt
253df930be7Sderaadt "", " ", 0, { "" },
254df930be7Sderaadt " ", " ", 2, { "", "" },
255df930be7Sderaadt "x", " ", 1, { "x" },
256df930be7Sderaadt "xy", " ", 1, { "xy" },
257df930be7Sderaadt "x y", " ", 2, { "x", "y" },
258df930be7Sderaadt "abc def g ", " ", 4, { "abc", "def", "g", "" },
259df930be7Sderaadt " a bcd", " ", 3, { "", "a", "bcd" },
260df930be7Sderaadt "a b c d e f", " ", 6, { "a", "b", "c", "d", "e f" },
261df930be7Sderaadt " a b c d ", " ", 6, { "", "a", "b", "c", "d " },
262df930be7Sderaadt
263df930be7Sderaadt "", "", 0, { "" },
264df930be7Sderaadt " ", "", 0, { "" },
265df930be7Sderaadt "x", "", 1, { "x" },
266df930be7Sderaadt "xy", "", 1, { "xy" },
267df930be7Sderaadt "x y", "", 2, { "x", "y" },
268df930be7Sderaadt "abc def g ", "", 3, { "abc", "def", "g" },
269df930be7Sderaadt "\t a bcd", "", 2, { "a", "bcd" },
270df930be7Sderaadt " a \tb\t c ", "", 3, { "a", "b", "c" },
271df930be7Sderaadt "a b c d e ", "", 5, { "a", "b", "c", "d", "e" },
272df930be7Sderaadt "a b\tc d e f", "", 6, { "a", "b", "c", "d", "e f" },
273df930be7Sderaadt " a b c d e f ", "", 6, { "a", "b", "c", "d", "e f " },
274df930be7Sderaadt
275df930be7Sderaadt NULL, NULL, 0, { NULL },
276df930be7Sderaadt };
277df930be7Sderaadt
regress()278df930be7Sderaadt regress()
279df930be7Sderaadt {
280df930be7Sderaadt char buf[512];
281df930be7Sderaadt register int n;
282df930be7Sderaadt char *fields[RNF+1];
283df930be7Sderaadt register int nf;
284df930be7Sderaadt register int i;
285df930be7Sderaadt register int printit;
286df930be7Sderaadt register char *f;
287df930be7Sderaadt
288df930be7Sderaadt for (n = 0; tests[n].str != NULL; n++) {
289112e1910Sderaadt (void) strlcpy(buf, tests[n].str, sizeof buf);
290df930be7Sderaadt fields[RNF] = NULL;
291df930be7Sderaadt nf = split(buf, fields, RNF, tests[n].seps);
292df930be7Sderaadt printit = 0;
293df930be7Sderaadt if (nf != tests[n].nf) {
294df930be7Sderaadt printf("split `%s' by `%s' gave %d fields, not %d\n",
295df930be7Sderaadt tests[n].str, tests[n].seps, nf, tests[n].nf);
296df930be7Sderaadt printit = 1;
297df930be7Sderaadt } else if (fields[RNF] != NULL) {
298df930be7Sderaadt printf("split() went beyond array end\n");
299df930be7Sderaadt printit = 1;
300df930be7Sderaadt } else {
301df930be7Sderaadt for (i = 0; i < nf && i < RNF; i++) {
302df930be7Sderaadt f = fields[i];
303df930be7Sderaadt if (f == NULL)
304df930be7Sderaadt f = "(NULL)";
305df930be7Sderaadt if (strcmp(f, tests[n].fi[i]) != 0) {
306df930be7Sderaadt printf("split `%s' by `%s', field %d is `%s', not `%s'\n",
307df930be7Sderaadt tests[n].str, tests[n].seps,
308df930be7Sderaadt i, fields[i], tests[n].fi[i]);
309df930be7Sderaadt printit = 1;
310df930be7Sderaadt }
311df930be7Sderaadt }
312df930be7Sderaadt }
313df930be7Sderaadt if (printit)
314df930be7Sderaadt print(nf, RNF, fields);
315df930be7Sderaadt }
316df930be7Sderaadt }
317df930be7Sderaadt #endif
318