pdate addres for Free Software Foundation
[nfs-utils.git] / utils / mount / token.c
1 /*
2  * token.c -- tokenize strings, a la strtok(3)
3  *
4  * Copyright (C) 2007 Oracle.  All rights reserved.
5  * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public
18  * License along with this program; if not, write to the
19  * Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
21  *
22  */
23
24 /*
25  * We've constructed a simple string tokenizer that is better than
26  * strtok(3) in several ways:
27  *
28  * 1.  It doesn't interfere with ongoing tokenizations using strtok(3).
29  * 2.  It's re-entrant so we can nest tokenizations, if needed.
30  * 3.  It can handle double-quoted delimiters (needed for 'context="sd,fslj"').
31  * 4.  It doesn't alter the string we're tokenizing, so it can work
32  *     on write-protected strings as well as writable strings.
33  */
34
35
36 #include <ctype.h>
37 #include <unistd.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <stdlib.h>
41 #include <errno.h>
42
43 #include "token.h"
44
45
46 struct tokenizer_state {
47         char *pos;
48         char delimiter;
49         int error;
50 };
51
52 static void find_next_nondelimiter(struct tokenizer_state *tstate)
53 {
54         while (*tstate->pos != '\0' && *tstate->pos == tstate->delimiter)
55                 tstate->pos++;
56 }
57
58 static size_t find_next_delimiter(struct tokenizer_state *tstate)
59 {
60         size_t len = 0;
61         int quote_seen = 0;
62
63         while (*tstate->pos != '\0') {
64                 if (*tstate->pos == '"')
65                         quote_seen ^= 1;
66
67                 if (!quote_seen && *tstate->pos == tstate->delimiter)
68                         break;
69
70                 len++;
71                 tstate->pos++;
72         }
73
74         /* did the string terminate before the close quote? */
75         if (quote_seen) {
76                 tstate->error = EINVAL;
77                 return 0;
78         }
79
80         return len;
81 }
82
83 /**
84  * next_token - find the next token in a string and return it
85  * @tstate: pointer to tokenizer context object
86  *
87  * Returns the next token found in the current string.
88  * Returns NULL if there are no more tokens in the string,
89  * or if an error occurs.
90  *
91  * Side effect: tstate is updated
92  */
93 char *next_token(struct tokenizer_state *tstate)
94 {
95         char *token;
96         size_t len;
97
98         if (!tstate || !tstate->pos || tstate->error)
99                 return NULL;
100
101         find_next_nondelimiter(tstate);
102         if (*tstate->pos == '\0')
103                 goto fail;
104         token = tstate->pos;
105
106         len = find_next_delimiter(tstate);
107         if (len) {
108                 token = strndup(token, len);
109                 if (token)
110                         return token;
111                 tstate->error = ENOMEM;
112         }
113
114 fail:
115         tstate->pos = NULL;
116         return NULL;                    /* no tokens found in this string */
117 }
118
119 /**
120  * init_tokenizer - return an initialized tokenizer context object
121  * @string: pointer to C string
122  * @delimiter: single character that delimits tokens in @string
123  *
124  * Returns an initialized tokenizer context object
125  */
126 struct tokenizer_state *init_tokenizer(char *string, char delimiter)
127 {
128         struct tokenizer_state *tstate;
129
130         tstate = malloc(sizeof(*tstate));
131         if (tstate) {
132                 tstate->pos = string;
133                 tstate->delimiter = delimiter;
134                 tstate->error = 0;
135         }
136         return tstate;
137 }
138
139 /**
140  * tokenizer_error - digs error value out of tokenizer context
141  * @tstate: pointer to tokenizer context object
142  *
143  */
144 int tokenizer_error(struct tokenizer_state *tstate)
145 {
146         return tstate ? tstate->error : 0;
147 }
148
149 /**
150  * end_tokenizer - free a tokenizer context object
151  * @tstate: pointer to tokenizer context object
152  *
153  */
154 void end_tokenizer(struct tokenizer_state *tstate)
155 {
156         free(tstate);
157 }