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