In ssplit(), assert() that the last substring already is nul-terminated
[privoxy.git] / ssplit.c
1 const char ssplit_rcs[] = "$Id: ssplit.c,v 1.18 2012/07/23 12:46:18 fabiankeil Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/ssplit.c,v $
5  *
6  * Purpose     :  A function to split a string at specified delimiters.
7  *
8  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
9  *                Privoxy team. http://www.privoxy.org/
10  *
11  *                Based on the Internet Junkbuster originally written
12  *                by and Copyright (C) 1997 Anonymous Coders and
13  *                Junkbusters Corporation.  http://www.junkbusters.com
14  *
15  *                This program is free software; you can redistribute it
16  *                and/or modify it under the terms of the GNU General
17  *                Public License as published by the Free Software
18  *                Foundation; either version 2 of the License, or (at
19  *                your option) any later version.
20  *
21  *                This program is distributed in the hope that it will
22  *                be useful, but WITHOUT ANY WARRANTY; without even the
23  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
24  *                PARTICULAR PURPOSE.  See the GNU General Public
25  *                License for more details.
26  *
27  *                The GNU General Public License should be included with
28  *                this file.  If not, you can view it at
29  *                http://www.gnu.org/copyleft/gpl.html
30  *                or write to the Free Software Foundation, Inc., 59
31  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
32  *
33  *********************************************************************/
34
35
36 #include "config.h"
37
38 #include <string.h>
39 #include <stdlib.h>
40 #include <assert.h>
41
42 #include "ssplit.h"
43 #include "miscutil.h"
44
45 const char ssplit_h_rcs[] = SSPLIT_H_VERSION;
46
47
48 /*********************************************************************
49  *
50  * Function    :  ssplit
51  *
52  * Description :  Split a string using delimiters in `delim'.  Results
53  *                go into `vec'.
54  *
55  * Parameters  :
56  *          1  :  str = string to split.  Will be split in place
57  *                (i.e. do not free until you've finished with vec,
58  *                previous contents will be trashed by the call).
59  *          2  :  delim = array of delimiters (if NULL, uses " \t").
60  *          3  :  vec[] = results vector (aka. array) [out]
61  *          4  :  vec_len = number of usable slots in the vector (aka. array size)
62  *
63  * Returns     :  -1 => Error: vec_len is too small to hold all the
64  *                      data, or str == NULL.
65  *                >=0 => the number of fields put in `vec'.
66  *                On error, vec and str may still have been overwritten.
67  *
68  *********************************************************************/
69 int ssplit(char *str, const char *delim, char *vec[], size_t vec_len)
70 {
71    unsigned char is_delim[256];
72    unsigned char char_type;
73    int vec_count = 0;
74    enum char_type {
75       WANTED     = 0,
76       SEPARATOR  = 1,
77       TERMINATOR = 2,
78    };
79
80
81    if (!str)
82    {
83       return(-1);
84    }
85
86
87    /* Build is_delim array */
88
89    memset(is_delim, '\0', sizeof(is_delim));
90
91    if (!delim)
92    {
93       delim = " \t";  /* default field separators */
94    }
95
96    while (*delim)
97    {
98       is_delim[(unsigned)(unsigned char)*delim++] = SEPARATOR;
99    }
100
101    is_delim[(unsigned)(unsigned char)'\0'] = TERMINATOR;
102    is_delim[(unsigned)(unsigned char)'\n'] = TERMINATOR;
103
104
105    /* Parse string */
106
107    /* Skip leading separators. XXX: Why do they matter? */
108    while (is_delim[(unsigned)(unsigned char)*str] == SEPARATOR)
109    {
110       str++;
111    }
112
113    /* The first pointer is the beginning of string */
114    if (is_delim[(unsigned)(unsigned char)*str] == WANTED)
115    {
116       /*
117        * The first character in this field is not a
118        * delimiter or the end of string, so save it.
119        */
120       if (vec_count >= vec_len)
121       {
122          return(-1); /* overflow */
123       }
124       vec[vec_count++] = str;
125    }
126
127    while ((char_type = is_delim[(unsigned)(unsigned char)*str]) != TERMINATOR)
128    {
129       if (char_type == SEPARATOR)
130       {
131          /* the char is a separator */
132
133          /* null terminate the substring */
134          *str++ = '\0';
135
136          /* Check if we want to save this field */
137          if (is_delim[(unsigned)(unsigned char)*str] == WANTED)
138          {
139             /*
140              * The first character in this field is not a
141              * delimiter or the end of string. So save it.
142              */
143             if (vec_count >= vec_len)
144             {
145                return(-1); /* overflow */
146             }
147             vec[vec_count++] = str;
148          }
149       }
150       else
151       {
152          str++;
153       }
154    }
155    /* null terminate the substring */
156    /* XXX: this shouldn't be necessary, so assert that it isn't. */
157    assert(*str == '\0');
158    *str = '\0';
159
160    return(vec_count);
161 }
162
163
164 /*
165   Local Variables:
166   tab-width: 3
167   end:
168 */