1 |
|
|
/* $OpenBSD: parse.c,v 1.26 2017/02/16 00:24:43 krw Exp $ */ |
2 |
|
|
|
3 |
|
|
/* Common parser code for dhcpd and dhclient. */ |
4 |
|
|
|
5 |
|
|
/* |
6 |
|
|
* Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. |
7 |
|
|
* All rights reserved. |
8 |
|
|
* |
9 |
|
|
* Redistribution and use in source and binary forms, with or without |
10 |
|
|
* modification, are permitted provided that the following conditions |
11 |
|
|
* are met: |
12 |
|
|
* |
13 |
|
|
* 1. Redistributions of source code must retain the above copyright |
14 |
|
|
* notice, this list of conditions and the following disclaimer. |
15 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
16 |
|
|
* notice, this list of conditions and the following disclaimer in the |
17 |
|
|
* documentation and/or other materials provided with the distribution. |
18 |
|
|
* 3. Neither the name of The Internet Software Consortium nor the names |
19 |
|
|
* of its contributors may be used to endorse or promote products derived |
20 |
|
|
* from this software without specific prior written permission. |
21 |
|
|
* |
22 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND |
23 |
|
|
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, |
24 |
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
25 |
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
26 |
|
|
* DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR |
27 |
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
28 |
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
29 |
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
30 |
|
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
31 |
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
32 |
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
33 |
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
34 |
|
|
* SUCH DAMAGE. |
35 |
|
|
* |
36 |
|
|
* This software has been written for the Internet Software Consortium |
37 |
|
|
* by Ted Lemon <mellon@fugue.com> in cooperation with Vixie |
38 |
|
|
* Enterprises. To learn more about the Internet Software Consortium, |
39 |
|
|
* see ``http://www.vix.com/isc''. To learn more about Vixie |
40 |
|
|
* Enterprises, see ``http://www.vix.com''. |
41 |
|
|
*/ |
42 |
|
|
|
43 |
|
|
#include <sys/types.h> |
44 |
|
|
#include <sys/socket.h> |
45 |
|
|
|
46 |
|
|
#include <net/if.h> |
47 |
|
|
|
48 |
|
|
#include <netinet/in.h> |
49 |
|
|
|
50 |
|
|
#include <ctype.h> |
51 |
|
|
#include <errno.h> |
52 |
|
|
#include <stdarg.h> |
53 |
|
|
#include <stdint.h> |
54 |
|
|
#include <stdio.h> |
55 |
|
|
#include <stdlib.h> |
56 |
|
|
#include <string.h> |
57 |
|
|
#include <syslog.h> |
58 |
|
|
#include <time.h> |
59 |
|
|
#include <unistd.h> |
60 |
|
|
|
61 |
|
|
#include "dhcp.h" |
62 |
|
|
#include "tree.h" |
63 |
|
|
#include "dhcpd.h" |
64 |
|
|
#include "dhctoken.h" |
65 |
|
|
#include "log.h" |
66 |
|
|
|
67 |
|
|
/* |
68 |
|
|
* Skip to the semicolon ending the current statement. If we encounter |
69 |
|
|
* braces, the matching closing brace terminates the statement. If we |
70 |
|
|
* encounter a right brace but haven't encountered a left brace, return |
71 |
|
|
* leaving the brace in the token buffer for the caller. If we see a |
72 |
|
|
* semicolon and haven't seen a left brace, return. This lets us skip |
73 |
|
|
* over: |
74 |
|
|
* |
75 |
|
|
* statement; |
76 |
|
|
* statement foo bar { } |
77 |
|
|
* statement foo bar { statement { } } |
78 |
|
|
* statement} |
79 |
|
|
* |
80 |
|
|
* ...et cetera. |
81 |
|
|
*/ |
82 |
|
|
void |
83 |
|
|
skip_to_semi(FILE *cfile) |
84 |
|
|
{ |
85 |
|
|
int token; |
86 |
|
|
char *val; |
87 |
|
|
int brace_count = 0; |
88 |
|
|
|
89 |
|
|
do { |
90 |
|
|
token = peek_token(&val, cfile); |
91 |
|
|
if (token == '}') { |
92 |
|
|
if (brace_count) { |
93 |
|
|
token = next_token(&val, cfile); |
94 |
|
|
if (!--brace_count) |
95 |
|
|
return; |
96 |
|
|
} else |
97 |
|
|
return; |
98 |
|
|
} else if (token == '{') { |
99 |
|
|
brace_count++; |
100 |
|
|
} else if (token == ';' && !brace_count) { |
101 |
|
|
token = next_token(&val, cfile); |
102 |
|
|
return; |
103 |
|
|
} else if (token == '\n') { |
104 |
|
|
/* |
105 |
|
|
* EOL only happens when parsing |
106 |
|
|
* /etc/resolv.conf, and we treat it like a |
107 |
|
|
* semicolon because the resolv.conf file is |
108 |
|
|
* line-oriented. |
109 |
|
|
*/ |
110 |
|
|
token = next_token(&val, cfile); |
111 |
|
|
return; |
112 |
|
|
} |
113 |
|
|
token = next_token(&val, cfile); |
114 |
|
|
} while (token != EOF); |
115 |
|
|
} |
116 |
|
|
|
117 |
|
|
int |
118 |
|
|
parse_semi(FILE *cfile) |
119 |
|
|
{ |
120 |
|
|
int token; |
121 |
|
|
char *val; |
122 |
|
|
|
123 |
|
|
token = next_token(&val, cfile); |
124 |
|
|
if (token != ';') { |
125 |
|
|
parse_warn("semicolon expected."); |
126 |
|
|
skip_to_semi(cfile); |
127 |
|
|
return (0); |
128 |
|
|
} |
129 |
|
|
return (1); |
130 |
|
|
} |
131 |
|
|
|
132 |
|
|
/* |
133 |
|
|
* string-parameter :== STRING SEMI |
134 |
|
|
*/ |
135 |
|
|
char * |
136 |
|
|
parse_string(FILE *cfile) |
137 |
|
|
{ |
138 |
|
|
char *val, *s; |
139 |
|
|
int token; |
140 |
|
|
|
141 |
|
|
token = next_token(&val, cfile); |
142 |
|
|
if (token != TOK_STRING) { |
143 |
|
|
parse_warn("filename must be a string"); |
144 |
|
|
skip_to_semi(cfile); |
145 |
|
|
return (NULL); |
146 |
|
|
} |
147 |
|
|
s = strdup(val); |
148 |
|
|
if (s == NULL) |
149 |
|
|
fatalx("no memory for string %s.", val); |
150 |
|
|
|
151 |
|
|
if (!parse_semi(cfile)) { |
152 |
|
|
free(s); |
153 |
|
|
return (NULL); |
154 |
|
|
} |
155 |
|
|
return (s); |
156 |
|
|
} |
157 |
|
|
|
158 |
|
|
/* |
159 |
|
|
* hostname :== identifier | hostname DOT identifier |
160 |
|
|
*/ |
161 |
|
|
char * |
162 |
|
|
parse_host_name(FILE *cfile) |
163 |
|
|
{ |
164 |
|
|
char *val, *s, *t; |
165 |
|
|
int token, len = 0; |
166 |
|
|
pair c = NULL; |
167 |
|
|
|
168 |
|
|
/* Read a dotted hostname... */ |
169 |
|
|
do { |
170 |
|
|
/* Read a token, which should be an identifier. */ |
171 |
|
|
token = next_token(&val, cfile); |
172 |
|
|
if (!is_identifier(token)) { |
173 |
|
|
parse_warn("expecting an identifier in hostname"); |
174 |
|
|
skip_to_semi(cfile); |
175 |
|
|
return (NULL); |
176 |
|
|
} |
177 |
|
|
/* Store this identifier... */ |
178 |
|
|
s = strdup(val); |
179 |
|
|
if (s == NULL) |
180 |
|
|
fatalx("can't allocate temp space for hostname."); |
181 |
|
|
c = cons((caddr_t) s, c); |
182 |
|
|
len += strlen(s) + 1; |
183 |
|
|
/* |
184 |
|
|
* Look for a dot; if it's there, keep going, otherwise |
185 |
|
|
* we're done. |
186 |
|
|
*/ |
187 |
|
|
token = peek_token(&val, cfile); |
188 |
|
|
if (token == '.') |
189 |
|
|
token = next_token(&val, cfile); |
190 |
|
|
} while (token == '.'); |
191 |
|
|
|
192 |
|
|
/* Assemble the hostname together into a string. */ |
193 |
|
|
if (!(s = malloc(len))) |
194 |
|
|
fatalx("can't allocate space for hostname."); |
195 |
|
|
t = s + len; |
196 |
|
|
*--t = '\0'; |
197 |
|
|
while (c) { |
198 |
|
|
pair cdr = c->cdr; |
199 |
|
|
int l = strlen((char *)c->car); |
200 |
|
|
|
201 |
|
|
t -= l; |
202 |
|
|
memcpy(t, (char *)c->car, l); |
203 |
|
|
/* Free up temp space. */ |
204 |
|
|
free(c->car); |
205 |
|
|
free(c); |
206 |
|
|
c = cdr; |
207 |
|
|
if (t != s) |
208 |
|
|
*--t = '.'; |
209 |
|
|
} |
210 |
|
|
return (s); |
211 |
|
|
} |
212 |
|
|
|
213 |
|
|
/* |
214 |
|
|
* hardware-parameter :== HARDWARE ETHERNET csns SEMI |
215 |
|
|
* csns :== NUMBER | csns COLON NUMBER |
216 |
|
|
*/ |
217 |
|
|
void |
218 |
|
|
parse_hardware_param(FILE *cfile, struct hardware *hardware) |
219 |
|
|
{ |
220 |
|
|
char *val; |
221 |
|
|
int token, hlen; |
222 |
|
|
unsigned char *t; |
223 |
|
|
|
224 |
|
|
token = next_token(&val, cfile); |
225 |
|
|
switch (token) { |
226 |
|
|
case TOK_ETHERNET: |
227 |
|
|
hardware->htype = HTYPE_ETHER; |
228 |
|
|
break; |
229 |
|
|
case TOK_IPSEC_TUNNEL: |
230 |
|
|
hardware->htype = HTYPE_IPSEC_TUNNEL; |
231 |
|
|
break; |
232 |
|
|
default: |
233 |
|
|
parse_warn("expecting a network hardware type"); |
234 |
|
|
skip_to_semi(cfile); |
235 |
|
|
return; |
236 |
|
|
} |
237 |
|
|
|
238 |
|
|
/* |
239 |
|
|
* Parse the hardware address information. Technically, it |
240 |
|
|
* would make a lot of sense to restrict the length of the data |
241 |
|
|
* we'll accept here to the length of a particular hardware |
242 |
|
|
* address type. Unfortunately, there are some broken clients |
243 |
|
|
* out there that put bogus data in the chaddr buffer, and we |
244 |
|
|
* accept that data in the lease file rather than simply failing |
245 |
|
|
* on such clients. Yuck. |
246 |
|
|
*/ |
247 |
|
|
hlen = 0; |
248 |
|
|
t = parse_numeric_aggregate(cfile, NULL, &hlen, ':', 16, 8); |
249 |
|
|
if (!t) |
250 |
|
|
return; |
251 |
|
|
if (hlen > sizeof(hardware->haddr)) { |
252 |
|
|
free(t); |
253 |
|
|
parse_warn("hardware address too long"); |
254 |
|
|
} else { |
255 |
|
|
hardware->hlen = hlen; |
256 |
|
|
memcpy((unsigned char *)&hardware->haddr[0], t, |
257 |
|
|
hardware->hlen); |
258 |
|
|
if (hlen < sizeof(hardware->haddr)) |
259 |
|
|
memset(&hardware->haddr[hlen], 0, |
260 |
|
|
sizeof(hardware->haddr) - hlen); |
261 |
|
|
free(t); |
262 |
|
|
} |
263 |
|
|
|
264 |
|
|
token = next_token(&val, cfile); |
265 |
|
|
if (token != ';') { |
266 |
|
|
parse_warn("expecting semicolon."); |
267 |
|
|
skip_to_semi(cfile); |
268 |
|
|
} |
269 |
|
|
} |
270 |
|
|
|
271 |
|
|
/* |
272 |
|
|
* lease-time :== NUMBER SEMI |
273 |
|
|
*/ |
274 |
|
|
void |
275 |
|
|
parse_lease_time(FILE *cfile, time_t *timep) |
276 |
|
|
{ |
277 |
|
|
const char *errstr; |
278 |
|
|
char *val; |
279 |
|
|
uint32_t value; |
280 |
|
|
int token; |
281 |
|
|
|
282 |
|
|
token = next_token(&val, cfile); |
283 |
|
|
|
284 |
|
|
value = strtonum(val, 0, UINT32_MAX, &errstr); |
285 |
|
|
if (errstr) { |
286 |
|
|
parse_warn("lease time is %s: %s", errstr, val); |
287 |
|
|
skip_to_semi(cfile); |
288 |
|
|
return; |
289 |
|
|
} |
290 |
|
|
|
291 |
|
|
*timep = value; |
292 |
|
|
|
293 |
|
|
parse_semi(cfile); |
294 |
|
|
} |
295 |
|
|
|
296 |
|
|
/* |
297 |
|
|
* No BNF for numeric aggregates - that's defined by the caller. What |
298 |
|
|
* this function does is to parse a sequence of numbers separated by the |
299 |
|
|
* token specified in separator. If max is zero, any number of numbers |
300 |
|
|
* will be parsed; otherwise, exactly max numbers are expected. Base |
301 |
|
|
* and size tell us how to internalize the numbers once they've been |
302 |
|
|
* tokenized. |
303 |
|
|
*/ |
304 |
|
|
unsigned char * |
305 |
|
|
parse_numeric_aggregate(FILE *cfile, unsigned char *buf, int *max, |
306 |
|
|
int separator, int base, int size) |
307 |
|
|
{ |
308 |
|
|
char *val, *t; |
309 |
|
|
int token, count = 0; |
310 |
|
|
unsigned char *bufp = buf, *s = NULL; |
311 |
|
|
pair c = NULL; |
312 |
|
|
|
313 |
|
|
if (!bufp && *max) { |
314 |
|
|
bufp = malloc(*max * size / 8); |
315 |
|
|
if (!bufp) |
316 |
|
|
fatalx("can't allocate space for numeric aggregate"); |
317 |
|
|
} else |
318 |
|
|
s = bufp; |
319 |
|
|
|
320 |
|
|
do { |
321 |
|
|
if (count) { |
322 |
|
|
token = peek_token(&val, cfile); |
323 |
|
|
if (token != separator) { |
324 |
|
|
if (!*max) |
325 |
|
|
break; |
326 |
|
|
if (token != '{' && token != '}') |
327 |
|
|
token = next_token(&val, cfile); |
328 |
|
|
parse_warn("too few numbers."); |
329 |
|
|
if (token != ';') |
330 |
|
|
skip_to_semi(cfile); |
331 |
|
|
return (NULL); |
332 |
|
|
} |
333 |
|
|
token = next_token(&val, cfile); |
334 |
|
|
} |
335 |
|
|
token = next_token(&val, cfile); |
336 |
|
|
|
337 |
|
|
if (token == EOF) { |
338 |
|
|
parse_warn("unexpected end of file"); |
339 |
|
|
break; |
340 |
|
|
} |
341 |
|
|
if (token != TOK_NUMBER && token != TOK_NUMBER_OR_NAME) { |
342 |
|
|
parse_warn("expecting numeric value."); |
343 |
|
|
skip_to_semi(cfile); |
344 |
|
|
return (NULL); |
345 |
|
|
} |
346 |
|
|
/* |
347 |
|
|
* If we can, convert the number now; otherwise, build a |
348 |
|
|
* linked list of all the numbers. |
349 |
|
|
*/ |
350 |
|
|
if (s) { |
351 |
|
|
convert_num(s, val, base, size); |
352 |
|
|
s += size / 8; |
353 |
|
|
} else { |
354 |
|
|
t = strdup(val); |
355 |
|
|
if (t == NULL) |
356 |
|
|
fatalx("no temp space for number."); |
357 |
|
|
c = cons(t, c); |
358 |
|
|
} |
359 |
|
|
} while (++count != *max); |
360 |
|
|
|
361 |
|
|
/* If we had to cons up a list, convert it now. */ |
362 |
|
|
if (c) { |
363 |
|
|
bufp = malloc(count * size / 8); |
364 |
|
|
if (!bufp) |
365 |
|
|
fatalx("can't allocate space for numeric aggregate."); |
366 |
|
|
s = bufp + count - size / 8; |
367 |
|
|
*max = count; |
368 |
|
|
} |
369 |
|
|
while (c) { |
370 |
|
|
pair cdr = c->cdr; |
371 |
|
|
convert_num(s, (char *)c->car, base, size); |
372 |
|
|
s -= size / 8; |
373 |
|
|
/* Free up temp space. */ |
374 |
|
|
free(c->car); |
375 |
|
|
free(c); |
376 |
|
|
c = cdr; |
377 |
|
|
} |
378 |
|
|
return (bufp); |
379 |
|
|
} |
380 |
|
|
|
381 |
|
|
void |
382 |
|
|
convert_num(unsigned char *buf, char *str, int base, int size) |
383 |
|
|
{ |
384 |
|
|
int negative = 0, tval, max; |
385 |
|
|
u_int32_t val = 0; |
386 |
|
|
char *ptr = str; |
387 |
|
|
|
388 |
|
|
if (*ptr == '-') { |
389 |
|
|
negative = 1; |
390 |
|
|
ptr++; |
391 |
|
|
} |
392 |
|
|
|
393 |
|
|
/* If base wasn't specified, figure it out from the data. */ |
394 |
|
|
if (!base) { |
395 |
|
|
if (ptr[0] == '0') { |
396 |
|
|
if (ptr[1] == 'x') { |
397 |
|
|
base = 16; |
398 |
|
|
ptr += 2; |
399 |
|
|
} else if (isascii((unsigned char)ptr[1]) && |
400 |
|
|
isdigit((unsigned char)ptr[1])) { |
401 |
|
|
base = 8; |
402 |
|
|
ptr += 1; |
403 |
|
|
} else |
404 |
|
|
base = 10; |
405 |
|
|
} else |
406 |
|
|
base = 10; |
407 |
|
|
} |
408 |
|
|
|
409 |
|
|
do { |
410 |
|
|
tval = *ptr++; |
411 |
|
|
/* XXX assumes ASCII... */ |
412 |
|
|
if (tval >= 'a') |
413 |
|
|
tval = tval - 'a' + 10; |
414 |
|
|
else if (tval >= 'A') |
415 |
|
|
tval = tval - 'A' + 10; |
416 |
|
|
else if (tval >= '0') |
417 |
|
|
tval -= '0'; |
418 |
|
|
else { |
419 |
|
|
log_warnx("Bogus number: %s.", str); |
420 |
|
|
break; |
421 |
|
|
} |
422 |
|
|
if (tval >= base) { |
423 |
|
|
log_warnx("Bogus number: %s: digit %d not in base %d", |
424 |
|
|
str, tval, base); |
425 |
|
|
break; |
426 |
|
|
} |
427 |
|
|
val = val * base + tval; |
428 |
|
|
} while (*ptr); |
429 |
|
|
|
430 |
|
|
if (negative) |
431 |
|
|
max = (1 << (size - 1)); |
432 |
|
|
else |
433 |
|
|
max = (1 << (size - 1)) + ((1 << (size - 1)) - 1); |
434 |
|
|
if (val > max) { |
435 |
|
|
switch (base) { |
436 |
|
|
case 8: |
437 |
|
|
log_warnx("value %s%o exceeds max (%d) for precision.", |
438 |
|
|
negative ? "-" : "", val, max); |
439 |
|
|
break; |
440 |
|
|
case 16: |
441 |
|
|
log_warnx("value %s%x exceeds max (%d) for precision.", |
442 |
|
|
negative ? "-" : "", val, max); |
443 |
|
|
break; |
444 |
|
|
default: |
445 |
|
|
log_warnx("value %s%u exceeds max (%d) for precision.", |
446 |
|
|
negative ? "-" : "", val, max); |
447 |
|
|
break; |
448 |
|
|
} |
449 |
|
|
} |
450 |
|
|
|
451 |
|
|
if (negative) { |
452 |
|
|
switch (size) { |
453 |
|
|
case 8: |
454 |
|
|
*buf = -(unsigned long)val; |
455 |
|
|
break; |
456 |
|
|
case 16: |
457 |
|
|
putShort(buf, -(unsigned long)val); |
458 |
|
|
break; |
459 |
|
|
case 32: |
460 |
|
|
putLong(buf, -(unsigned long)val); |
461 |
|
|
break; |
462 |
|
|
default: |
463 |
|
|
log_warnx("Unexpected integer size: %d", size); |
464 |
|
|
break; |
465 |
|
|
} |
466 |
|
|
} else { |
467 |
|
|
switch (size) { |
468 |
|
|
case 8: |
469 |
|
|
*buf = (u_int8_t)val; |
470 |
|
|
break; |
471 |
|
|
case 16: |
472 |
|
|
putUShort(buf, (u_int16_t)val); |
473 |
|
|
break; |
474 |
|
|
case 32: |
475 |
|
|
putULong(buf, val); |
476 |
|
|
break; |
477 |
|
|
default: |
478 |
|
|
log_warnx("Unexpected integer size: %d", size); |
479 |
|
|
break; |
480 |
|
|
} |
481 |
|
|
} |
482 |
|
|
} |
483 |
|
|
|
484 |
|
|
/* |
485 |
|
|
* date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER |
486 |
|
|
* NUMBER COLON NUMBER COLON NUMBER UTC SEMI |
487 |
|
|
* |
488 |
|
|
* Dates are always in UTC; first number is day of week; next is |
489 |
|
|
* year/month/day; next is hours:minutes:seconds on a 24-hour |
490 |
|
|
* clock. |
491 |
|
|
*/ |
492 |
|
|
time_t |
493 |
|
|
parse_date(FILE *cfile) |
494 |
|
|
{ |
495 |
|
|
struct tm tm; |
496 |
|
|
char timestr[26]; /* "w yyyy/mm/dd hh:mm:ss UTC" */ |
497 |
|
|
char *val, *p; |
498 |
|
|
size_t n; |
499 |
|
|
time_t guess; |
500 |
|
|
int token; |
501 |
|
|
|
502 |
|
|
memset(timestr, 0, sizeof(timestr)); |
503 |
|
|
|
504 |
|
|
do { |
505 |
|
|
token = peek_token(NULL, cfile); |
506 |
|
|
switch (token) { |
507 |
|
|
case TOK_NAME: |
508 |
|
|
case TOK_NUMBER: |
509 |
|
|
case TOK_NUMBER_OR_NAME: |
510 |
|
|
case '/': |
511 |
|
|
case ':': |
512 |
|
|
token = next_token(&val, cfile); |
513 |
|
|
n = strlcat(timestr, val, sizeof(timestr)); |
514 |
|
|
if (n >= sizeof(timestr)) { |
515 |
|
|
/* XXX Will break after year 9999! */ |
516 |
|
|
parse_warn("time string too long"); |
517 |
|
|
skip_to_semi(cfile); |
518 |
|
|
return (0); |
519 |
|
|
} |
520 |
|
|
break; |
521 |
|
|
case';': |
522 |
|
|
break; |
523 |
|
|
default: |
524 |
|
|
parse_warn("invalid time string"); |
525 |
|
|
skip_to_semi(cfile); |
526 |
|
|
return (0); |
527 |
|
|
} |
528 |
|
|
} while (token != ';'); |
529 |
|
|
|
530 |
|
|
parse_semi(cfile); |
531 |
|
|
|
532 |
|
|
memset(&tm, 0, sizeof(tm)); /* 'cuz strptime ignores tm_isdt. */ |
533 |
|
|
p = strptime(timestr, DB_TIMEFMT, &tm); |
534 |
|
|
if (p == NULL || *p != '\0') { |
535 |
|
|
p = strptime(timestr, OLD_DB_TIMEFMT, &tm); |
536 |
|
|
if (p == NULL || *p != '\0') { |
537 |
|
|
parse_warn("unparseable time string"); |
538 |
|
|
return (0); |
539 |
|
|
} |
540 |
|
|
} |
541 |
|
|
|
542 |
|
|
guess = timegm(&tm); |
543 |
|
|
if (guess == -1) { |
544 |
|
|
parse_warn("time could not be represented"); |
545 |
|
|
return (0); |
546 |
|
|
} |
547 |
|
|
|
548 |
|
|
return (guess); |
549 |
|
|
} |
550 |
|
|
|
551 |
|
|
int warnings_occurred; |
552 |
|
|
|
553 |
|
|
int |
554 |
|
|
parse_warn(char *fmt, ...) |
555 |
|
|
{ |
556 |
|
|
static char fbuf[1024]; |
557 |
|
|
static char mbuf[1024]; |
558 |
|
|
static char spaces[81]; |
559 |
|
|
va_list list; |
560 |
|
|
int i; |
561 |
|
|
|
562 |
|
|
snprintf(fbuf, sizeof(fbuf), "%s line %d: %s", tlname, lexline, mbuf); |
563 |
|
|
va_start(list, fmt); |
564 |
|
|
vsnprintf(mbuf, sizeof(mbuf), fbuf, list); |
565 |
|
|
va_end(list); |
566 |
|
|
|
567 |
|
|
log_warnx("%s", mbuf); |
568 |
|
|
log_warnx("%s", token_line); |
569 |
|
|
if (lexchar < sizeof(spaces)) { |
570 |
|
|
memset(spaces, 0, sizeof(spaces)); |
571 |
|
|
for (i = 0; i < lexchar - 1; i++) { |
572 |
|
|
if (token_line[i] == '\t') |
573 |
|
|
spaces[i] = '\t'; |
574 |
|
|
else |
575 |
|
|
spaces[i] = ' '; |
576 |
|
|
} |
577 |
|
|
} |
578 |
|
|
log_warnx("%s^", spaces); |
579 |
|
|
|
580 |
|
|
warnings_occurred = 1; |
581 |
|
|
|
582 |
|
|
return (0); |
583 |
|
|
} |