1 //
2 // aegis - project change supervisor
3 // Copyright (C) 2009, 2010, 2012 Peter Miller
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or (at
8 // your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program. If not, see <http://www.gnu.org/licenses/>.
17 //
18
19 #include <common/ac/assert.h>
20 #include <common/ac/ctype.h>
21 #include <common/ac/string.h>
22
23 #include <common/nstring/accumulator.h>
24 #include <libaegis/change/file.h>
25 #include <libaegis/input/crlf.h>
26 #include <libaegis/input/file_text.h>
27 #include <libaegis/os.h>
28 #include <libaegis/sub.h>
29
30 #include <aede-policy/validation/files/reserved.h>
31
32
~validation_files_reserved()33 validation_files_reserved::~validation_files_reserved()
34 {
35 }
36
37
validation_files_reserved()38 validation_files_reserved::validation_files_reserved()
39 {
40 exceptions.push_back("__attribute__");
41 exceptions.push_back("__bool__");
42 exceptions.push_back("__bool");
43 exceptions.push_back("__DATE__");
44 exceptions.push_back("__FILE__");
45 exceptions.push_back("__func__");
46 exceptions.push_back("__FUNC__");
47 exceptions.push_back("__function__");
48 exceptions.push_back("__FUNCTION__");
49 exceptions.push_back("__inline__");
50 exceptions.push_back("__inline");
51 exceptions.push_back("__LINE__");
52 exceptions.push_back("__PRETTY_FUNCTION__");
53 exceptions.push_back("__TIME__");
54 exceptions.push_back("__TIMESTAMP__");
55 }
56
57
58 validation::pointer
create(void)59 validation_files_reserved::create(void)
60 {
61 return pointer(new validation_files_reserved());
62 }
63
64
65 bool
check_branches() const66 validation_files_reserved::check_branches()
67 const
68 {
69 return false;
70 }
71
72
73 bool
check_downloaded() const74 validation_files_reserved::check_downloaded()
75 const
76 {
77 return false;
78 }
79
80
81 bool
check_foreign_copyright() const82 validation_files_reserved::check_foreign_copyright()
83 const
84 {
85 return false;
86 }
87
88
89 bool
check_binaries() const90 validation_files_reserved::check_binaries()
91 const
92 {
93 return false;
94 }
95
96
97 bool
is_a_reserved_word(const nstring & name) const98 validation_files_reserved::is_a_reserved_word(const nstring &name)
99 const
100 {
101 #if 0
102 // These are just plain fugly.
103 if (name.size() >= 1 && (name[0] == '_' || name[name.size() - 1] == '_')
104 return true;
105 #endif
106
107 //
108 // The symbols defined by the standard (or in common use) are OK to
109 // be used. They aren't OK to be redefined, but that takes more
110 // semantic analysis, and this is just a heuristic.
111 //
112 if (exceptions.member(name))
113 return false;
114
115 //
116 // ANSI C++ Standard, Section 2.10, Paragraph 2:
117 // "Identifiers containing a double underscore (__) or beginning
118 // with an underscore and an upper-case letter are reserved for use
119 // by C++ implementations and standard libraries and shall not be
120 // used otherwise."
121 //
122 if (strstr(name.c_str(), "__"))
123 return true;
124 if (name.size() >= 2 && name[0] == '_' && isupper((unsigned char)name[1]))
125 return true;
126
127 //
128 // ANSI C Standard, Section 2.10, Paragraph 1:
129 // "In addition, some identifiers are reserved for use by C++
130 // implementations and standard libraries and shall not be used
131 // otherwise; no diagnostic is required."
132 //
133
134 //
135 // Can't find anything to complain about.
136 //
137 return false;
138 }
139
140
141 static nstring
get_character(input ip)142 get_character(input ip)
143 {
144 nstring_accumulator ac;
145 int c = ip->getch();
146 if (c < 0)
147 return ac.mkstr();
148 ac.push_back((char)c);
149 if (c != '\\')
150 return ac.mkstr();
151 c = ip->getch();
152 if (c < 0)
153 return ac.mkstr();
154 ac.push_back((char)c);
155 switch (c)
156 {
157 default:
158 return ac.mkstr();
159
160 case '0': case '1': case '2': case '3':
161 case '4': case '5': case '6': case '7':
162 for (;;)
163 {
164 c = ip->getch();
165 if (c < 0)
166 return ac.mkstr();
167 switch ((char)c)
168 {
169 case '0': case '1': case '2': case '3':
170 case '4': case '5': case '6': case '7':
171 continue;
172
173 default:
174 ip->ungetc((char)c);
175 return ac.mkstr();
176 }
177 }
178
179 case 'x':
180 case 'X':
181 for (;;)
182 {
183 c = ip->getch();
184 if (c < 0)
185 return ac.mkstr();
186 switch ((char)c)
187 {
188 case '0': case '1': case '2': case '3': case '4':
189 case '5': case '6': case '7': case '8': case '9':
190 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
191 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
192 continue;
193
194 default:
195 ip->ungetc((char)c);
196 return ac.mkstr();
197 }
198 }
199 }
200 }
201
202
203 bool
check(change::pointer cp,fstate_src_ty * src)204 validation_files_reserved::check(change::pointer cp, fstate_src_ty *src)
205 {
206 nstring path(cp->file_path(src));
207 assert(!path.empty());
208 if (path.empty())
209 return true;
210
211 //
212 // figure out what sort of file we are looking at
213 //
214 nstring base = path.basename();
215 nstring lc_base = base.downcase();
216 bool isa_c_file = base.ends_with(".c");
217 bool isa_cxx_file =
218 base.ends_with(".C") ||
219 lc_base.ends_with(".c++") ||
220 lc_base.ends_with(".cc") ||
221 lc_base.ends_with(".cpp") ||
222 base.ends_with(".H") ||
223 lc_base.ends_with(".h++") ||
224 lc_base.ends_with(".hh") ||
225 lc_base.ends_with(".hpp")
226 ;
227 bool isa_h_file = lc_base.ends_with(".h");
228 if (!isa_c_file && !isa_cxx_file && !isa_h_file)
229 return true;
230
231 os_become_orig();
232 bool ok = true;
233 input ip = input_file_text_open(path);
234 for (;;)
235 {
236 int ic = ip->getch();
237 if (ic < 0)
238 break;
239 unsigned char c = ic;
240 switch (c)
241 {
242 case ' ':
243 case '\b':
244 case '\f':
245 case '\n':
246 case '\r':
247 case '\t':
248 case '\v':
249 continue;
250
251 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
252 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
253 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
254 case 'V': case 'W': case 'X': case 'Y': case 'Z':
255 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
256 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
257 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
258 case 'v': case 'w': case 'x': case 'y': case 'z':
259 case '0': case '1': case '2': case '3': case '4':
260 case '5': case '6': case '7': case '8': case '9':
261 case '$': case '_':
262 // Identifier (or pre-processor phase number)
263 {
264 nstring_accumulator ac;
265 for (;;)
266 {
267 ac.push_back(c);
268 ic = ip->getch();
269 if (ic < 0)
270 break;
271 c = ic;
272 switch (c)
273 {
274 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
275 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
276 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
277 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
278 case 'Y': case 'Z':
279 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
280 case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
281 case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
282 case 's': case 't': case 'u': case 'v': case 'w': case 'x':
283 case 'y': case 'z':
284 case '0': case '1': case '2': case '3': case '4':
285 case '5': case '6': case '7': case '8': case '9':
286 case '$': case '_':
287 continue;
288
289 default:
290 ip->ungetc(c);
291 break;
292 }
293 break;
294 }
295 nstring name = ac.mkstr();
296 if (is_a_reserved_word(name))
297 {
298 string_ty *text =
299 str_from_c
300 (
301 "identifier \"$name\" is reserved by the "
302 "ANSI C Standard"
303 );
304 sub_context_ty sc;
305 sc.var_set_long("Name", name);
306 string_ty *msg = sc.substitute(cp, text);
307 str_free(text);
308 ip->error(msg->str_text);
309 str_free(msg);
310 ok = false;
311 }
312 }
313 break;
314
315 case '/':
316 ic = ip->getch();
317 if (ic < 0)
318 break;
319 c = ic;
320 switch (c)
321 {
322 default:
323 ip->ungetc(c);
324 break;
325
326 case '/':
327 // This is a C++ comment
328 for (;;)
329 {
330 ic = ip->getch();
331 if (ic < 0)
332 break;
333 if (c == '\n')
334 break;
335 }
336 break;
337
338 case '*':
339 // This is a C comment
340 for (;;)
341 {
342 ic = ip->getch();
343 if (ic < 0)
344 break;
345 c = ic;
346 if (c != '*')
347 continue;
348 for (;;)
349 {
350 ic = ip->getch();
351 if (ic < 0)
352 break;
353 c = ic;
354 if (c != '*')
355 break;
356 }
357 if (c != '/')
358 continue;
359 break;
360 }
361 break;
362 }
363 break;
364
365 case '\'':
366 // Character constant
367 for (;;)
368 {
369 nstring ss = get_character(ip);
370 if (ss == "'")
371 break;
372 }
373 break;
374
375 case '"':
376 // String constant
377 for (;;)
378 {
379 nstring ss = get_character(ip);
380 if (ss == "\"")
381 break;
382 }
383 break;
384 }
385 }
386
387 ip.close();
388 os_become_undo();
389 return ok;
390 }
391
392
393 // vim: set ts=8 sw=4 et :
394