]>
Commit | Line | Data |
---|---|---|
997358a6 MW |
1 | /* lexer (lexical analyzer) for control files |
2 | * Copyright (C) 1998-2001 D. Hugh Redelmeier. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify it | |
5 | * under the terms of the GNU General Public License as published by the | |
6 | * Free Software Foundation; either version 2 of the License, or (at your | |
7 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, but | |
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
11 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
12 | * for more details. | |
13 | * | |
14 | * RCSID $Id: lex.c,v 1.1 2004/03/15 20:35:28 as Exp $ | |
15 | */ | |
16 | ||
17 | #include <stdio.h> | |
18 | #include <stdlib.h> | |
19 | #include <stddef.h> | |
20 | #include <string.h> | |
21 | #include <ctype.h> | |
22 | #include <unistd.h> | |
23 | #include <errno.h> | |
24 | ||
25 | #include <freeswan.h> | |
26 | ||
27 | #include "constants.h" | |
28 | #include "defs.h" | |
29 | #include "log.h" | |
30 | #include "whack.h" /* for RC_LOG_SERIOUS */ | |
31 | #include "lex.h" | |
32 | ||
33 | struct file_lex_position *flp = NULL; | |
34 | ||
35 | /* Open a file for lexical processing. | |
36 | * new_flp and name must point into storage with will live | |
37 | * at least until the file is closed. | |
38 | */ | |
39 | bool | |
40 | lexopen(struct file_lex_position *new_flp, const char *name, bool optional) | |
41 | { | |
42 | FILE *f = fopen(name, "r"); | |
43 | ||
44 | if (f == NULL) | |
45 | { | |
46 | if (!optional || errno != ENOENT) | |
47 | log_errno((e, "could not open \"%s\"", name)); | |
48 | return FALSE; | |
49 | } | |
50 | else | |
51 | { | |
52 | new_flp->previous = flp; | |
53 | flp = new_flp; | |
54 | flp->filename = name; | |
55 | flp->fp = f; | |
56 | flp->lino = 0; | |
57 | flp->bdry = B_none; | |
58 | ||
59 | flp->cur = flp->buffer; /* nothing loaded yet */ | |
60 | flp->under = *flp->cur = '\0'; | |
61 | ||
62 | (void) shift(); /* prime tok */ | |
63 | return TRUE; | |
64 | } | |
65 | } | |
66 | ||
67 | void | |
68 | lexclose(void) | |
69 | { | |
70 | fclose(flp->fp); | |
71 | flp = flp->previous; | |
72 | } | |
73 | ||
74 | /* Token decoding: shift() loads the next token into tok. | |
75 | * Iff a token starts at the left margin, it is considered | |
76 | * to be the first in a record. We create a special condition, | |
77 | * Record Boundary (analogous to EOF), just before such a token. | |
78 | * We are unwilling to shift through a record boundary: | |
79 | * it must be overridden first. | |
80 | * Returns FALSE iff Record Boundary or EOF (i.e. no token); | |
81 | * tok will then be NULL. | |
82 | */ | |
83 | ||
84 | char *tok; | |
85 | #define tokeq(s) (streq(tok, (s))) | |
86 | #define tokeqword(s) (strcasecmp(tok, (s)) == 0) | |
87 | ||
88 | bool | |
89 | shift(void) | |
90 | { | |
91 | char *p = flp->cur; | |
92 | char *sor = NULL; /* start of record for any new lines */ | |
93 | ||
94 | passert(flp->bdry == B_none); | |
95 | ||
96 | *p = flp->under; | |
97 | flp->under = '\0'; | |
98 | ||
99 | for (;;) | |
100 | { | |
101 | switch (*p) | |
102 | { | |
103 | case '\0': /* end of line */ | |
104 | case '#': /* comment to end of line: treat as end of line */ | |
105 | /* get the next line */ | |
106 | if (fgets(flp->buffer, sizeof(flp->buffer)-1, flp->fp) == NULL) | |
107 | { | |
108 | flp->bdry = B_file; | |
109 | tok = flp->cur = NULL; | |
110 | return FALSE; | |
111 | } | |
112 | else | |
113 | { | |
114 | /* strip trailing whitespace, including \n */ | |
115 | ||
116 | for (p = flp->buffer+strlen(flp->buffer)-1 | |
117 | ; p>flp->buffer && isspace(p[-1]); p--) | |
118 | ; | |
119 | *p = '\0'; | |
120 | ||
121 | flp->lino++; | |
122 | sor = p = flp->buffer; | |
123 | } | |
124 | break; /* try again for a token */ | |
125 | ||
126 | case ' ': /* whitespace */ | |
127 | case '\t': | |
128 | p++; | |
129 | break; /* try again for a token */ | |
130 | ||
131 | case '"': /* quoted token */ | |
132 | case '\'': | |
133 | if (p != sor) | |
134 | { | |
135 | /* we have a quoted token: note and advance to its end */ | |
136 | tok = p; | |
137 | p = strchr(p+1, *p); | |
138 | if (p == NULL) | |
139 | { | |
140 | loglog(RC_LOG_SERIOUS, "\"%s\" line %d: unterminated string" | |
141 | , flp->filename, flp->lino); | |
142 | p = tok + strlen(tok); | |
143 | } | |
144 | else | |
145 | { | |
146 | p++; /* include delimiter in token */ | |
147 | } | |
148 | ||
149 | /* remember token delimiter and replace with '\0' */ | |
150 | flp->under = *p; | |
151 | *p = '\0'; | |
152 | flp->cur = p; | |
153 | return TRUE; | |
154 | } | |
155 | /* FALL THROUGH */ | |
156 | default: | |
157 | if (p != sor) | |
158 | { | |
159 | /* we seem to have a token: note and advance to its end */ | |
160 | tok = p; | |
161 | ||
162 | if (p[0] == '0' && p[1] == 't') | |
163 | { | |
164 | /* 0t... token goes to end of line */ | |
165 | p += strlen(p); | |
166 | } | |
167 | else | |
168 | { | |
169 | /* "ordinary" token: up to whitespace or end of line */ | |
170 | do { | |
171 | p++; | |
172 | } while (*p != '\0' && !isspace(*p)) | |
173 | ; | |
174 | ||
175 | /* fudge to separate ':' from a preceding adjacent token */ | |
176 | if (p-1 > tok && p[-1] == ':') | |
177 | p--; | |
178 | } | |
179 | ||
180 | /* remember token delimiter and replace with '\0' */ | |
181 | flp->under = *p; | |
182 | *p = '\0'; | |
183 | flp->cur = p; | |
184 | return TRUE; | |
185 | } | |
186 | ||
187 | /* we have a start-of-record: return it, deferring "real" token */ | |
188 | flp->bdry = B_record; | |
189 | tok = NULL; | |
190 | flp->under = *p; | |
191 | flp->cur = p; | |
192 | return FALSE; | |
193 | } | |
194 | } | |
195 | } | |
196 | ||
197 | /* ensures we are at a Record (or File) boundary, optionally warning if not */ | |
198 | ||
199 | bool | |
200 | flushline(const char *m) | |
201 | { | |
202 | if (flp->bdry != B_none) | |
203 | { | |
204 | return TRUE; | |
205 | } | |
206 | else | |
207 | { | |
208 | if (m != NULL) | |
209 | loglog(RC_LOG_SERIOUS, "\"%s\" line %d: %s", flp->filename, flp->lino, m); | |
210 | do ; while (shift()); | |
211 | return FALSE; | |
212 | } | |
213 | } |