/src/p11-kit/common/lexer.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2005 Stefan Walter |
3 | | * Copyright (c) 2011 Collabora Ltd. |
4 | | * Copyright (c) 2013 Red Hat Inc. |
5 | | * |
6 | | * Redistribution and use in source and binary forms, with or without |
7 | | * modification, are permitted provided that the following conditions |
8 | | * are met: |
9 | | * |
10 | | * * Redistributions of source code must retain the above |
11 | | * copyright notice, this list of conditions and the |
12 | | * following disclaimer. |
13 | | * * Redistributions in binary form must reproduce the |
14 | | * above copyright notice, this list of conditions and |
15 | | * the following disclaimer in the documentation and/or |
16 | | * other materials provided with the distribution. |
17 | | * * The names of contributors to this software may not be |
18 | | * used to endorse or promote products derived from this |
19 | | * software without specific prior written permission. |
20 | | * |
21 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
22 | | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
23 | | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
24 | | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
25 | | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
26 | | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
27 | | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
28 | | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
29 | | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
30 | | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
31 | | * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
32 | | * DAMAGE. |
33 | | * |
34 | | * |
35 | | * CONTRIBUTORS |
36 | | * Stef Walter <stefw@redhat.com> |
37 | | */ |
38 | | |
39 | | #include "config.h" |
40 | | |
41 | | #define P11_DEBUG_FLAG P11_DEBUG_CONF |
42 | | #include "debug.h" |
43 | | #include "lexer.h" |
44 | | #include "message.h" |
45 | | |
46 | | #include <assert.h> |
47 | | #include <ctype.h> |
48 | | #include <errno.h> |
49 | | #include <stdio.h> |
50 | | #include <stdlib.h> |
51 | | #include <string.h> |
52 | | |
53 | | /* Only use on uninitialized lexer or after p11_lexer_done() |
54 | | */ |
55 | | bool |
56 | | p11_lexer_init (p11_lexer *lexer, |
57 | | const char *filename, |
58 | | const char *data, |
59 | | size_t length) |
60 | 2.24k | { |
61 | 2.24k | return_val_if_fail (lexer != NULL, false); |
62 | | |
63 | 2.24k | memset (lexer, 0, sizeof (p11_lexer)); |
64 | 2.24k | lexer->at = data; |
65 | 2.24k | lexer->remaining = length; |
66 | | |
67 | 2.24k | return_val_if_fail (filename != NULL, false); |
68 | 2.24k | lexer->filename = strdup (filename); |
69 | 2.24k | return_val_if_fail (lexer->filename != NULL, false); |
70 | 2.24k | return true; |
71 | 2.24k | } |
72 | | |
73 | | static void |
74 | | clear_state (p11_lexer *lexer) |
75 | 192k | { |
76 | 192k | switch (lexer->tok_type) { |
77 | 12.3k | case TOK_FIELD: |
78 | 12.3k | free (lexer->tok.field.name); |
79 | 12.3k | free (lexer->tok.field.value); |
80 | 12.3k | break; |
81 | 172k | case TOK_SECTION: |
82 | 172k | free (lexer->tok.section.name); |
83 | 172k | break; |
84 | 3.27k | case TOK_PEM: |
85 | 7.10k | case TOK_EOF: |
86 | 7.10k | break; |
87 | 192k | } |
88 | | |
89 | 192k | memset (&lexer->tok, 0, sizeof (lexer->tok)); |
90 | 192k | lexer->tok_type = TOK_EOF; |
91 | 192k | lexer->complained = false; |
92 | 192k | } |
93 | | |
94 | | bool |
95 | | p11_lexer_next (p11_lexer *lexer, |
96 | | bool *failed) |
97 | 190k | { |
98 | 190k | const char *colon; |
99 | 190k | const char *value; |
100 | 190k | const char *line; |
101 | 190k | const char *end; |
102 | 190k | const char *pos; |
103 | | |
104 | 190k | return_val_if_fail (lexer != NULL, false); |
105 | | |
106 | 190k | clear_state (lexer); |
107 | 190k | if (failed) |
108 | 190k | *failed = false; |
109 | | |
110 | | /* Go through lines and process them */ |
111 | 221k | while (lexer->remaining != 0) { |
112 | 220k | assert (lexer->remaining > 0); |
113 | | |
114 | | /* Is this line the start of a PEM block? */ |
115 | 220k | if (lexer->remaining >= 11 && strncmp (lexer->at, "-----BEGIN ", 11) == 0) { |
116 | 3.30k | pos = strnstr (lexer->at, "\n-----END ", lexer->remaining); |
117 | 3.30k | if (pos == NULL) { |
118 | 25 | p11_lexer_msg (lexer, "invalid pem block: no ending line"); |
119 | 25 | if (failed) |
120 | 25 | *failed = true; |
121 | 25 | return false; |
122 | 25 | } |
123 | | |
124 | 3.27k | end = memchr (pos + 1, '\n', lexer->remaining - (pos - lexer->at) - 1); |
125 | 3.27k | end = end ? end + 1 : lexer->at + lexer->remaining; |
126 | | /* Count newlines in the PEM block */ |
127 | 3.27k | pos = lexer->at; |
128 | 53.0k | while (pos < end) { |
129 | 50.1k | pos = memchr (pos, '\n', end - pos); |
130 | 50.1k | if (!pos) |
131 | 328 | break; |
132 | 49.8k | lexer->line++; |
133 | 49.8k | pos++; |
134 | 49.8k | } |
135 | 3.27k | lexer->tok_type = TOK_PEM; |
136 | 3.27k | lexer->tok.pem.begin = lexer->at; |
137 | 3.27k | lexer->tok.pem.length = end - lexer->at; |
138 | 3.27k | assert (end - lexer->at <= lexer->remaining); |
139 | 3.27k | lexer->remaining -= (end - lexer->at); |
140 | 3.27k | lexer->at = end; |
141 | 3.27k | return true; |
142 | 3.27k | } |
143 | | |
144 | 217k | line = lexer->at; |
145 | 217k | end = memchr (lexer->at, '\n', lexer->remaining); |
146 | 217k | if (end == NULL) { |
147 | 1.72k | end = lexer->at + lexer->remaining; |
148 | 1.72k | lexer->remaining = 0; |
149 | 1.72k | lexer->at = end; |
150 | 215k | } else { |
151 | 215k | assert ((end - lexer->at) + 1 <= lexer->remaining); |
152 | 215k | lexer->line++; |
153 | 215k | lexer->remaining -= (end - lexer->at) + 1; |
154 | 215k | lexer->at = end + 1; |
155 | 215k | } |
156 | | |
157 | | /* Strip whitespace from line */ |
158 | 217k | while (line != end && isspace (line[0])) |
159 | 293 | ++line; |
160 | 217k | while (line != end && isspace (*(end - 1))) |
161 | 719 | --end; |
162 | | |
163 | | /* Empty lines / comments at start */ |
164 | 217k | if (line == end || line[0] == '#') |
165 | 31.9k | continue; |
166 | | |
167 | | /* Is it a section ? */ |
168 | 185k | if (line[0] == '[') { |
169 | 172k | if (*(end - 1) != ']') { |
170 | 35 | p11_lexer_msg (lexer, "invalid section header: missing braces"); |
171 | 35 | if (failed) |
172 | 35 | *failed = true; |
173 | 35 | return false; |
174 | 35 | } |
175 | | |
176 | 172k | lexer->tok.section.name = strndup (line + 1, (end - line) - 2); |
177 | 172k | return_val_if_fail (lexer->tok.section.name != NULL, false); |
178 | 172k | lexer->tok_type = TOK_SECTION; |
179 | 172k | return true; |
180 | 172k | } |
181 | | |
182 | | /* Look for the break between name: value on the same line */ |
183 | 12.4k | colon = memchr (line, ':', end - line); |
184 | 12.4k | if (colon == NULL) { |
185 | 139 | p11_lexer_msg (lexer, "invalid field line: no colon"); |
186 | 139 | if (failed) |
187 | 139 | *failed = true; |
188 | 139 | return false; |
189 | 139 | } |
190 | | |
191 | | /* Strip whitespace from name and value */ |
192 | 12.3k | value = colon + 1; |
193 | 12.8k | while (value != end && isspace (value[0])) |
194 | 554 | ++value; |
195 | 12.5k | while (line != colon && isspace (*(colon - 1))) |
196 | 237 | --colon; |
197 | | |
198 | 12.3k | lexer->tok.field.name = strndup (line, colon - line); |
199 | 12.3k | return_val_if_fail (lexer->tok.field.name != NULL, false); |
200 | 12.3k | lexer->tok.field.value = strndup (value, end - value); |
201 | 12.3k | if (lexer->tok.field.value == NULL) { |
202 | 0 | free (lexer->tok.field.name); |
203 | 0 | return_val_if_reached (false); |
204 | 0 | } |
205 | 12.3k | lexer->tok_type = TOK_FIELD; |
206 | 12.3k | return true; |
207 | 12.3k | } |
208 | | |
209 | 1.38k | return false; |
210 | 190k | } |
211 | | |
212 | | void |
213 | | p11_lexer_done (p11_lexer *lexer) |
214 | 2.24k | { |
215 | 2.24k | return_if_fail (lexer != NULL); |
216 | 2.24k | clear_state (lexer); |
217 | 2.24k | free (lexer->filename); |
218 | 2.24k | memset (lexer, 0, sizeof (p11_lexer)); |
219 | 2.24k | } |
220 | | |
221 | | void |
222 | | p11_lexer_msg (p11_lexer *lexer, |
223 | | const char *msg) |
224 | 82.1k | { |
225 | 82.1k | return_if_fail (lexer != NULL); |
226 | | |
227 | 82.1k | if (lexer->complained) |
228 | 69.3k | return; |
229 | | |
230 | 12.8k | switch (lexer->tok_type) { |
231 | 304 | case TOK_FIELD: |
232 | 304 | p11_message ("%s:%zu: %s: %s", lexer->filename, lexer->line, |
233 | 304 | lexer->tok.field.name, msg); |
234 | 304 | break; |
235 | 11.9k | case TOK_SECTION: |
236 | 11.9k | p11_message ("%s:%zu: [%s]: %s", lexer->filename, lexer->line, |
237 | 11.9k | lexer->tok.section.name, msg); |
238 | 11.9k | break; |
239 | 357 | case TOK_PEM: |
240 | 357 | p11_message ("%s:%zu: BEGIN ...: %s", lexer->filename, |
241 | 357 | lexer->line, msg); |
242 | 357 | break; |
243 | 199 | default: |
244 | 199 | p11_message ("%s:%zu: %s", lexer->filename, lexer->line, msg); |
245 | 199 | break; |
246 | 12.8k | } |
247 | | |
248 | 12.8k | lexer->complained = true; |
249 | 12.8k | } |