resetting manifest requested domain to floor
[platform/upstream/ccache.git] / hashutil.c
1 /*
2  * Copyright (C) 2009-2010 Joel Rosdahl
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 Free
6  * Software Foundation; either version 3 of the License, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program; if not, write to the Free Software Foundation, Inc., 51
16  * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18
19 #include "ccache.h"
20 #include "hashutil.h"
21 #include "murmurhashneutral2.h"
22
23 unsigned
24 hash_from_string(void *str)
25 {
26         return murmurhashneutral2(str, strlen((const char *)str), 0);
27 }
28
29 unsigned
30 hash_from_int(int i)
31 {
32         return murmurhashneutral2(&i, sizeof(int), 0);
33 }
34
35 int
36 strings_equal(void *str1, void *str2)
37 {
38         return str_eq((const char *)str1, (const char *)str2);
39 }
40
41 int
42 file_hashes_equal(struct file_hash *fh1, struct file_hash *fh2)
43 {
44         return memcmp(fh1->hash, fh2->hash, 16) == 0
45                 && fh1->size == fh2->size;
46 }
47
48 #define HASH(ch) \
49         do {\
50                 hashbuf[hashbuflen] = ch; \
51                 hashbuflen++; \
52                 if (hashbuflen == sizeof(hashbuf)) {\
53                         hash_buffer(hash, hashbuf, sizeof(hashbuf)); \
54                         hashbuflen = 0; \
55                 } \
56         } while (0)
57
58 /*
59  * Hash a string ignoring comments. Returns a bitmask of HASH_SOURCE_CODE_*
60  * results.
61  */
62 int
63 hash_source_code_string(
64         struct mdfour *hash, const char *str, size_t len, const char *path)
65 {
66         const char *p;
67         const char *end;
68         char hashbuf[64];
69         size_t hashbuflen = 0;
70         int result = HASH_SOURCE_CODE_OK;
71         extern unsigned sloppiness;
72
73         p = str;
74         end = str + len;
75         while (1) {
76                 if (p >= end) {
77                         goto end;
78                 }
79                 switch (*p) {
80                 /* Potential start of comment. */
81                 case '/':
82                         if (p+1 == end) {
83                                 break;
84                         }
85                         switch (*(p+1)) {
86                         case '*':
87                                 HASH(' '); /* Don't paste tokens together when removing the comment. */
88                                 p += 2;
89                                 while (p+1 < end
90                                        && (*p != '*' || *(p+1) != '/')) {
91                                         if (*p == '\n') {
92                                                 /* Keep line numbers. */
93                                                 HASH('\n');
94                                         }
95                                         p++;
96                                 }
97                                 if (p+1 == end) {
98                                         goto end;
99                                 }
100                                 p += 2;
101                                 continue;
102
103                         case '/':
104                                 p += 2;
105                                 while (p < end
106                                        && (*p != '\n' || *(p-1) == '\\')) {
107                                         p++;
108                                 }
109                                 continue;
110
111                         default:
112                                 break;
113                         }
114                         break;
115
116                 /* Start of string. */
117                 case '"':
118                         HASH(*p);
119                         p++;
120                         while (p < end && (*p != '"' || *(p-1) == '\\')) {
121                                 HASH(*p);
122                                 p++;
123                         }
124                         if (p == end) {
125                                 goto end;
126                         }
127                         break;
128
129                 /* Potential start of volatile macro. */
130                 case '_':
131                         if (p + 7 < end
132                             && p[1] == '_' && p[5] == 'E'
133                             && p[6] == '_' && p[7] == '_') {
134                                 if (p[2] == 'D' && p[3] == 'A'
135                                     && p[4] == 'T') {
136                                         result |= HASH_SOURCE_CODE_FOUND_DATE;
137                                 } else if (p[2] == 'T' && p[3] == 'I'
138                                            && p[4] == 'M') {
139                                         result |= HASH_SOURCE_CODE_FOUND_TIME;
140                                 }
141                                 /*
142                                  * Of course, we can't be sure that we have found a __{DATE,TIME}__
143                                  * that's actually used, but better safe than sorry. And if you do
144                                  * something like
145                                  *
146                                  * #define TIME __TI ## ME__
147                                  *
148                                  * in your code, you deserve to get a false cache hit.
149                                  */
150                         }
151                         break;
152
153                 default:
154                         break;
155                 }
156
157                 HASH(*p);
158                 p++;
159         }
160
161 end:
162         hash_buffer(hash, hashbuf, hashbuflen);
163
164         if (sloppiness & SLOPPY_TIME_MACROS) {
165                 return 0;
166         }
167         if (result & HASH_SOURCE_CODE_FOUND_DATE) {
168                 /*
169                  * Make sure that the hash sum changes if the (potential) expansion of
170                  * __DATE__ changes.
171                  */
172                 time_t t = time(NULL);
173                 struct tm *now = localtime(&t);
174                 cc_log("Found __DATE__ in %s", path);
175                 hash_delimiter(hash, "date");
176                 hash_buffer(hash, &now->tm_year, sizeof(now->tm_year));
177                 hash_buffer(hash, &now->tm_mon, sizeof(now->tm_mon));
178                 hash_buffer(hash, &now->tm_mday, sizeof(now->tm_mday));
179         }
180         if (result & HASH_SOURCE_CODE_FOUND_TIME) {
181                 /*
182                  * We don't know for sure that the program actually uses the __TIME__
183                  * macro, but we have to assume it anyway and hash the time stamp. However,
184                  * that's not very useful since the chance that we get a cache hit later
185                  * the same second should be quite slim... So, just signal back to the
186                  * caller that __TIME__ has been found so that the direct mode can be
187                  * disabled.
188                  */
189                 cc_log("Found __TIME__ in %s", path);
190         }
191
192         return result;
193 }
194
195 /*
196  * Hash a file ignoring comments. Returns a bitmask of HASH_SOURCE_CODE_*
197  * results.
198  */
199 int
200 hash_source_code_file(struct mdfour *hash, const char *path)
201 {
202         char *data;
203         size_t size;
204         int result;
205
206         if (is_precompiled_header(path)) {
207                 if (hash_file(hash, path)) {
208                         return HASH_SOURCE_CODE_OK;
209                 } else {
210                         return HASH_SOURCE_CODE_ERROR;
211                 }
212         } else {
213                 if (!read_file(path, 0, &data, &size)) {
214                         return HASH_SOURCE_CODE_ERROR;
215                 }
216                 result = hash_source_code_string(hash, data, size, path);
217                 free(data);
218                 return result;
219         }
220 }
221
222 bool
223 hash_command_output(struct mdfour *hash, const char *command,
224                     const char *compiler)
225 {
226         pid_t pid;
227         int pipefd[2];
228
229         struct args *args = args_init_from_string(command);
230         int i;
231         for (i = 0; i < args->argc; i++) {
232                 if (str_eq(args->argv[i], "%compiler%")) {
233                         args_set(args, i, compiler);
234                 }
235         }
236         cc_log_argv("Executing compiler check command ", args->argv);
237
238         if (pipe(pipefd) == -1) {
239                 fatal("pipe failed");
240         }
241         pid = fork();
242         if (pid == -1) {
243                 fatal("fork failed");
244         }
245
246         if (pid == 0) {
247                 /* Child. */
248                 close(pipefd[0]);
249                 close(0);
250                 dup2(pipefd[1], 1);
251                 dup2(pipefd[1], 2);
252                 _exit(execvp(args->argv[0], args->argv));
253                 return false; /* Never reached. */
254         } else {
255                 /* Parent. */
256                 int status;
257                 bool ok;
258                 args_free(args);
259                 close(pipefd[1]);
260                 ok = hash_fd(hash, pipefd[0]);
261                 if (!ok) {
262                         cc_log("Error hashing compiler check command output: %s", strerror(errno));
263                         stats_update(STATS_COMPCHECK);
264                 }
265                 close(pipefd[0]);
266                 if (waitpid(pid, &status, 0) != pid) {
267                         cc_log("waitpid failed");
268                         return false;
269                 }
270                 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
271                         cc_log("Compiler check command returned %d", WEXITSTATUS(status));
272                         stats_update(STATS_COMPCHECK);
273                         return false;
274                 }
275                 return ok;
276         }
277 }
278
279 bool
280 hash_multicommand_output(struct mdfour *hash, const char *commands,
281                          const char *compiler)
282 {
283         char *command_string, *command, *p, *saveptr = NULL;
284         bool ok = true;
285
286         command_string = x_strdup(commands);
287         p = command_string;
288         while ((command = strtok_r(p, ";", &saveptr))) {
289                 if (!hash_command_output(hash, command, compiler)) {
290                         ok = false;
291                 }
292                 p = NULL;
293         }
294         free(command_string);
295         return ok;
296 }