Bug Summary

File:solenv/bin/concat-deps.c
Location:line 1072, column 12
Description:Memory is never released; potential leak of memory pointed to by 'in_list_base'

Annotated Source Code

1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * Copyright (C) 2011 Norbert Thiebaud
4 * License: GPLv3
5 */
6
7/* define to activate stats reporting on hash usage*/
8/* #define HASH_STAT */
9
10/* ===============================================
11 * Set-up: defines to identify the system and system related properties
12 * ===============================================
13 */
14
15#ifdef __APPLE__
16#ifdef __x86_64__
17#define CORE_BIG_ENDIAN0 0
18#define CORE_LITTLE_ENDIAN1 1
19#define USE_MEMORY_ALIGNMENT64 64 /* big value -> no alignment */
20#else
21#define CORE_BIG_ENDIAN0 1
22#define CORE_LITTLE_ENDIAN1 0
23#define USE_MEMORY_ALIGNMENT64 4
24#endif
25
26#endif
27#ifdef _AIX
28#define CORE_BIG_ENDIAN0 1
29#define CORE_LITTLE_ENDIAN1 0
30#define USE_MEMORY_ALIGNMENT64 4
31#endif /* Def _AIX */
32
33#ifdef __CYGWIN__
34#define __windows
35#define CORE_BIG_ENDIAN0 0
36#define CORE_LITTLE_ENDIAN1 1
37#define USE_MEMORY_ALIGNMENT64 64 /* big value -> no alignment */
38#endif /* Def __CYGWIN__ */
39
40#if defined(__linux1) || defined(__OpenBSD__) || \
41 defined(__FreeBSD__) || defined(__NetBSD__) || \
42 defined(__DragonFly__) || defined(__FreeBSD_kernel__)
43#if __BYTE_ORDER1234 == __LITTLE_ENDIAN1234
44#define CORE_BIG_ENDIAN0 0
45#define CORE_LITTLE_ENDIAN1 1
46#if defined(__x86_64) || defined(__i3861)
47#define USE_MEMORY_ALIGNMENT64 64
48#else
49#define USE_MEMORY_ALIGNMENT64 4
50#endif
51#else /* !(__BYTE_ORDER == __LITTLE_ENDIAN) */
52#if __BYTE_ORDER1234 == __BIG_ENDIAN4321
53#define CORE_BIG_ENDIAN0 1
54#define CORE_LITTLE_ENDIAN1 0
55#define USE_MEMORY_ALIGNMENT64 4
56#endif /* __BYTE_ORDER == __BIG_ENDIAN */
57#endif /* !(__BYTE_ORDER == __LITTLE_ENDIAN) */
58#endif /* Def __linux || Def *BSD */
59
60#ifdef __sun
61#ifdef __sparc
62#define CORE_BIG_ENDIAN0 1
63#define CORE_LITTLE_ENDIAN1 0
64#define USE_MEMORY_ALIGNMENT64 4
65#else /* Ndef __sparc */
66#define CORE_BIG_ENDIAN0 0
67#define CORE_LITTLE_ENDIAN1 1
68#define USE_MEMORY_ALIGNMENT64 4
69#endif /* Ndef __sparc */
70#endif /* Def __sun */
71
72/* Note USE_MEMORY_ALIGNMENT is 4 for platform that allow short non-aligned but required int access to be aligned (e.g sparc, ppc, zos..)
73 * USE_MEMORY_ALIGNMENT is 2 for platform that require short and int access to be aligned (e.g hppa )
74 * if the platform does not have alignment requirement (x86/amd64) use a big value (i.e > 16)
75 */
76#ifndef USE_MEMORY_ALIGNMENT64
77#error "USE_MEMORY_ALIGNMENT must be defined to the proper alignment value for the platform"
78#endif
79
80#include <assert.h>
81#include <stdio.h>
82#include <stdlib.h>
83#include <sys/types.h>
84#include <sys/stat.h>
85#include <errno(*__errno_location ()).h>
86#include <fcntl.h>
87#include <string.h>
88#include <ctype.h>
89
90#ifdef __windows
91#include <io.h>
92#else
93#include <unistd.h>
94#endif
95
96/* modes */
97#ifdef __windows
98#define FILE_O_RDONLY00 _O_RDONLY
99#define FILE_O_BINARY0 _O_BINARY
100#else /* not windaube */
101#define FILE_O_RDONLY00 O_RDONLY00
102#define FILE_O_BINARY0 0
103#endif /* not windaube */
104
105#ifndef TRUE1
106#define TRUE1 1
107#endif
108#ifndef FALSE0
109#define FALSE0 0
110#endif
111
112int internal_boost = 0;
113static char* base_dir;
114static char* out_dir;
115
116#ifdef __GNUC__4
117#define clz__builtin_clz __builtin_clz
118#else
119static inline int clz__builtin_clz(unsigned int value)
120{
121int result = 32;
122
123 while(value)
124 {
125 value >>= 1;
126 result -= 1;
127 }
128 return result;
129}
130#endif
131
132#if (USE_MEMORY_ALIGNMENT64 > 4)
133#define get_unaligned_uint(str)(*(unsigned int*)(str)) (*(unsigned int*)(str))
134#else
135static inline unsigned int get_unaligned_uint(const unsigned char* cursor)(*(unsigned int*)(const unsigned char* cursor))
136{
137unsigned int result;
138
139 memcpy(&result, cursor, sizeof(unsigned int));
140 return result;
141}
142#endif
143
144/* ===============================================
145 * memory pool for fast fix-size allocation (non-tread-safe)
146 * ===============================================
147 */
148struct pool
149{
150 void* head_free; /**< head of a linked list of freed element */
151 char* fresh; /**< top of a memory block to dig new element */
152 char* tail; /**< to detect end of extent... when fresh pass tail */
153 void* extent; /**< pointer to the primary extent block */
154 int size_elem; /**< size of an element. */
155 int primary; /**< primary allocation in bytes */
156 int secondary; /**< secondary allocation in bytes */
157};
158#define POOL_ALIGN_INCREMENT8 8 /**< Alignement, must be a power of 2 and of size > to sizeof(void*) */
159
160
161static void* pool_take_extent(struct pool* pool, int allocate)
162{
163unsigned int size = 0;
164void* extent;
165void* data = NULL((void*)0);
166
167 if(pool->extent)
168 {
169 /* we already have an extent, so this is a secondary */
170 if(pool->secondary)
171 {
172 size = pool->secondary;
173 }
174 }
175 else
176 {
177 assert(pool->primary)((pool->primary) ? (void) (0) : __assert_fail ("pool->primary"
, "/usr/local/src/libreoffice/solenv/bin/concat-deps.c", 177,
__PRETTY_FUNCTION__))
;
178 size = pool->primary;
179 }
180 if(size)
181 {
182 extent = malloc(size);
183 if(extent)
184 {
185 *(void**)extent = pool->extent;
186 pool->extent = extent;
187 if(allocate)
188 {
189 data = ((char*)extent) + POOL_ALIGN_INCREMENT8;
190 pool->fresh = ((char*)data) + pool->size_elem;
191 pool->tail = pool->fresh + (size - pool->size_elem);
192 }
193 else
194 {
195 pool->fresh = ((char*)extent) + POOL_ALIGN_INCREMENT8;
196 pool->tail = pool->fresh + (size - pool->size_elem);
197 }
198 }
199 }
200 return data;
201}
202
203/* Create a memory pool for fix size objects
204 * this is a simplified implementation that
205 * is _not_ thread safe.
206 */
207struct pool* pool_create(int size_elem, int flags, int primary, int secondary)
208{
209struct pool* pool;
210
211 assert(primary > 0)((primary > 0) ? (void) (0) : __assert_fail ("primary > 0"
, "/usr/local/src/libreoffice/solenv/bin/concat-deps.c", 211,
__PRETTY_FUNCTION__))
;
212 assert(secondary >= 0)((secondary >= 0) ? (void) (0) : __assert_fail ("secondary >= 0"
, "/usr/local/src/libreoffice/solenv/bin/concat-deps.c", 212,
__PRETTY_FUNCTION__))
;
213 assert(size_elem > 0)((size_elem > 0) ? (void) (0) : __assert_fail ("size_elem > 0"
, "/usr/local/src/libreoffice/solenv/bin/concat-deps.c", 213,
__PRETTY_FUNCTION__))
;
214
215 pool = (struct pool*)calloc(1, sizeof(struct pool));
216 if(!pool) return NULL((void*)0);
217 /* Adjust the element size so that it be aligned, and so that an element could
218 * at least contain a void*
219 */
220 pool->size_elem = size_elem = (size_elem + POOL_ALIGN_INCREMENT8 - 1) & ~(POOL_ALIGN_INCREMENT8 - 1);
221
222 pool->primary = (size_elem * primary) + POOL_ALIGN_INCREMENT8;
223 pool->secondary = secondary > 0 ? (size_elem * secondary) + POOL_ALIGN_INCREMENT8 : 0;
224 pool_take_extent(pool, FALSE0);
225
226 return pool;
227
228}
229
230void pool_destroy(struct pool* pool)
231{
232void* extent;
233void* next;
234
235 if(pool != NULL((void*)0))
236 {
237 extent = pool->extent;
238 while(extent)
239 {
240 next = *(void**)extent;
241 free(extent);
242 extent = next;
243 }
244 free(pool);
245 }
246}
247
248static inline void* pool_alloc(struct pool* pool)
249{
250void* data;
251
252 data = pool->head_free;
253 if(data == NULL((void*)0))
254 {
255 /* we have no old-freed elem */
256 if(pool->fresh <= pool->tail)
257 {
258 /* pick a slice of the current extent */
259 data = (void*)pool->fresh;
260 pool->fresh += pool->size_elem;
261 }
262 else
263 {
264 /* allocate a new extent */
265 data = pool_take_extent(pool, TRUE1);
266 }
267 }
268 else
269 {
270 /* re-used old freed element by chopipng the head of the free list */
271 pool->head_free = *(void**)data;
272 }
273
274 return data;
275}
276
277
278static inline void pool_free(struct pool* pool, void* data)
279{
280 assert(pool && data)((pool && data) ? (void) (0) : __assert_fail ("pool && data"
, "/usr/local/src/libreoffice/solenv/bin/concat-deps.c", 280,
__PRETTY_FUNCTION__))
;
281
282 /* stack on top of the free list */
283 *(void**)data = pool->head_free;
284 pool->head_free = data;
285}
286
287
288/* ===============================================
289 * Hash implementation custumized to be just tracking
290 * a unique list of string (i.e no data associated
291 * with the key, no need for retrieval, etc..
292 *
293 * This is tuned for the particular use-case we have here
294 * measures in tail_build showed that
295 * we can get north of 4000 distinct values stored in a hash
296 * the collision rate is at worse around 2%
297 * the collision needing an expensive memcmp to resolve
298 * have a rate typically at 1 per 1000
299 * for tail_build we register 37229 unique key
300 * with a total of 377 extra memcmp needed
301 * which is completely negligible compared to the
302 * number of memcmp required to eliminate duplicate
303 * entry (north of 2.5 millions for tail_build)
304 * ===============================================
305 */
306
307struct hash_elem
308{
309 struct hash_elem* next;
310 const char* key;
311 int key_len;
312};
313
314struct hash
315{
316 struct hash_elem** array;
317 struct pool* elems_pool;
318 int flags;
319 unsigned int used;
320 unsigned int size;
321 unsigned int load_limit;
322#ifdef HASH_STAT
323 int stored;
324 int collisions;
325 int cost;
326 int memcmp;
327#endif
328};
329#define HASH_F_NO_RESIZE(1<<0) (1<<0)
330
331/* The following hash_compute function was adapted from :
332 * lookup3.c, by Bob Jenkins, May 2006, Public Domain.
333 *
334 * The changes from the original are mostly cosmetic
335 */
336#define hashsize(n)(1<<(n)) (1<<(n))
337#define hashmask(n)((1<<(n))-1) (hashsize(n)(1<<(n))-1)
338#define rot(x,k)(((x)<<(k)) | ((x)>>(32 -(k)))) (((x)<<(k)) | ((x)>>(32-(k))))
339
340
341#if CORE_BIG_ENDIAN0
342#define MASK_C10xFFFFFF 0xFFFFFF00
343#define MASK_C20xFFFF 0xFFFF0000
344#define MASK_C30xFF 0xFF000000
345#else
346#if CORE_LITTLE_ENDIAN1
347#define MASK_C10xFFFFFF 0xFFFFFF
348#define MASK_C20xFFFF 0xFFFF
349#define MASK_C30xFF 0xFF
350#else
351#error "Missing Endianness definition"
352#endif
353#endif
354
355
356#define mix(a,b,c){ a -= c; a ^= (((c)<<(4)) | ((c)>>(32 -(4)))); c
+= b; b -= a; b ^= (((a)<<(6)) | ((a)>>(32 -(6))
)); a += c; c -= b; c ^= (((b)<<(8)) | ((b)>>(32 -
(8)))); b += a; a -= c; a ^= (((c)<<(16)) | ((c)>>
(32 -(16)))); c += b; b -= a; b ^= (((a)<<(19)) | ((a)>>
(32 -(19)))); a += c; c -= b; c ^= (((b)<<(4)) | ((b)>>
(32 -(4)))); b += a; }
\
357{ \
358 a -= c; a ^= rot(c, 4)(((c)<<(4)) | ((c)>>(32 -(4)))); c += b; \
359 b -= a; b ^= rot(a, 6)(((a)<<(6)) | ((a)>>(32 -(6)))); a += c; \
360 c -= b; c ^= rot(b, 8)(((b)<<(8)) | ((b)>>(32 -(8)))); b += a; \
361 a -= c; a ^= rot(c,16)(((c)<<(16)) | ((c)>>(32 -(16)))); c += b; \
362 b -= a; b ^= rot(a,19)(((a)<<(19)) | ((a)>>(32 -(19)))); a += c; \
363 c -= b; c ^= rot(b, 4)(((b)<<(4)) | ((b)>>(32 -(4)))); b += a; \
364}
365#define final(a,b,c){ c ^= b; c -= (((b)<<(14)) | ((b)>>(32 -(14))));
a ^= c; a -= (((c)<<(11)) | ((c)>>(32 -(11)))); b
^= a; b -= (((a)<<(25)) | ((a)>>(32 -(25)))); c ^=
b; c -= (((b)<<(16)) | ((b)>>(32 -(16)))); a ^= c
; a -= (((c)<<(4)) | ((c)>>(32 -(4)))); b ^= a; b
-= (((a)<<(14)) | ((a)>>(32 -(14)))); c ^= b; c -=
(((b)<<(24)) | ((b)>>(32 -(24)))); }
\
366{ \
367 c ^= b; c -= rot(b,14)(((b)<<(14)) | ((b)>>(32 -(14)))); \
368 a ^= c; a -= rot(c,11)(((c)<<(11)) | ((c)>>(32 -(11)))); \
369 b ^= a; b -= rot(a,25)(((a)<<(25)) | ((a)>>(32 -(25)))); \
370 c ^= b; c -= rot(b,16)(((b)<<(16)) | ((b)>>(32 -(16)))); \
371 a ^= c; a -= rot(c,4)(((c)<<(4)) | ((c)>>(32 -(4)))); \
372 b ^= a; b -= rot(a,14)(((a)<<(14)) | ((a)>>(32 -(14)))); \
373 c ^= b; c -= rot(b,24)(((b)<<(24)) | ((b)>>(32 -(24)))); \
374}
375
376static unsigned int hash_compute( struct hash* hash, const char* key, int length)
377{
378 unsigned int a;
379 unsigned int b;
380 unsigned int c; /* internal state */
381 const unsigned char* uk = (const unsigned char*)key;
382
383 /* Set up the internal state */
384 a = b = c = 0xdeadbeef + (length << 2);
385
386 /* we use this to 'hash' full path with mostly a common root
387 * let's now waste too much cycles hashing mostly constant stuff
388 */
389 if(length > 36)
390 {
391 uk += length - 36;
392 length = 36;
393 }
394 /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
395 while (length > 12)
396 {
397 a += get_unaligned_uint(uk)(*(unsigned int*)(uk));
398 b += get_unaligned_uint(uk+4)(*(unsigned int*)(uk+4));
399 c += get_unaligned_uint(uk+8)(*(unsigned int*)(uk+8));
400 mix(a,b,c){ a -= c; a ^= (((c)<<(4)) | ((c)>>(32 -(4)))); c
+= b; b -= a; b ^= (((a)<<(6)) | ((a)>>(32 -(6))
)); a += c; c -= b; c ^= (((b)<<(8)) | ((b)>>(32 -
(8)))); b += a; a -= c; a ^= (((c)<<(16)) | ((c)>>
(32 -(16)))); c += b; b -= a; b ^= (((a)<<(19)) | ((a)>>
(32 -(19)))); a += c; c -= b; c ^= (((b)<<(4)) | ((b)>>
(32 -(4)))); b += a; }
;
401 length -= 12;
402 uk += 12;
403 }
404
405 /*----------------------------- handle the last (probably partial) block */
406 /* Note: we possibly over-read, which would trigger complaint from VALGRIND
407 * but we mask the undefined stuff if any, so we are still good, thanks
408 * to alignment of memory allocation and tail-memory managment overhead
409 * we always can read 3 bytes past the official end without triggering
410 * a segfault -- if you find a platform/compiler couple for which that postulat
411 * is false, then you just need to over-allocate by 2 more bytes in file_load()
412 * file_load already over-allocate by 1 to sitck a \0 at the end of the buffer.
413 */
414 switch(length)
415 {
416 case 12: c+=get_unaligned_uint(uk+8)(*(unsigned int*)(uk+8)); b+=get_unaligned_uint(uk+4)(*(unsigned int*)(uk+4)); a+=get_unaligned_uint(uk)(*(unsigned int*)(uk)); break;
417 case 11: c+=get_unaligned_uint(uk+8)(*(unsigned int*)(uk+8)) & MASK_C10xFFFFFF; b+=get_unaligned_uint(uk+4)(*(unsigned int*)(uk+4)); a+=get_unaligned_uint(uk)(*(unsigned int*)(uk)); break;
418 case 10: c+=get_unaligned_uint(uk+8)(*(unsigned int*)(uk+8)) & MASK_C20xFFFF; b+=get_unaligned_uint(uk+4)(*(unsigned int*)(uk+4)); a+=get_unaligned_uint(uk)(*(unsigned int*)(uk)); break;
419 case 9 : c+=get_unaligned_uint(uk+8)(*(unsigned int*)(uk+8)) & MASK_C30xFF; b+=get_unaligned_uint(uk+4)(*(unsigned int*)(uk+4)); a+=get_unaligned_uint(uk)(*(unsigned int*)(uk)); break;
420 case 8 : b+=get_unaligned_uint(uk+4)(*(unsigned int*)(uk+4)); a+=get_unaligned_uint(uk)(*(unsigned int*)(uk)); break;
421 case 7 : b+=get_unaligned_uint(uk+4)(*(unsigned int*)(uk+4)) & MASK_C10xFFFFFF; a+=get_unaligned_uint(uk)(*(unsigned int*)(uk)); break;
422 case 6 : b+=get_unaligned_uint(uk+4)(*(unsigned int*)(uk+4)) & MASK_C20xFFFF; a+=get_unaligned_uint(uk)(*(unsigned int*)(uk)); break;
423 case 5 : b+=get_unaligned_uint(uk+4)(*(unsigned int*)(uk+4)) & MASK_C30xFF; a+=get_unaligned_uint(uk)(*(unsigned int*)(uk)); break;
424 case 4 : a+=get_unaligned_uint(uk)(*(unsigned int*)(uk)); break;
425 case 3 : a+=get_unaligned_uint(uk)(*(unsigned int*)(uk)) & MASK_C10xFFFFFF; break;
426 case 2 : a+=get_unaligned_uint(uk)(*(unsigned int*)(uk)) & MASK_C20xFFFF; break;
427 case 1 : a+=get_unaligned_uint(uk)(*(unsigned int*)(uk)) & MASK_C30xFF; break;
428 case 0 : return c & hash->size; /* zero length strings require no mixing */
429 }
430
431 final(a,b,c){ c ^= b; c -= (((b)<<(14)) | ((b)>>(32 -(14))));
a ^= c; a -= (((c)<<(11)) | ((c)>>(32 -(11)))); b
^= a; b -= (((a)<<(25)) | ((a)>>(32 -(25)))); c ^=
b; c -= (((b)<<(16)) | ((b)>>(32 -(16)))); a ^= c
; a -= (((c)<<(4)) | ((c)>>(32 -(4)))); b ^= a; b
-= (((a)<<(14)) | ((a)>>(32 -(14)))); c ^= b; c -=
(((b)<<(24)) | ((b)>>(32 -(24)))); }
;
432 return c & hash->size;
433}
434
435static void hash_destroy(struct hash* hash)
436{
437 if(hash)
438 {
439 if(hash->array)
440 {
441 free(hash->array);
442 }
443 if(hash->elems_pool)
444 {
445 pool_destroy(hash->elems_pool);
446 }
447 free(hash);
448 }
449}
450
451static struct hash* hash_create(unsigned int size)
452{
453struct hash* hash;
454
455 assert(size > 0)((size > 0) ? (void) (0) : __assert_fail ("size > 0", "/usr/local/src/libreoffice/solenv/bin/concat-deps.c"
, 455, __PRETTY_FUNCTION__))
;
456 hash = calloc(1, sizeof(struct hash));
457 if(hash)
458 {
459 size += (size >> 2) + 1; /* ~ 75% load factor */
460 if(size >= 15)
461 {
462 hash->size = (((unsigned int)0xFFFFFFFF) >> clz__builtin_clz((unsigned int)size));
463 }
464 else
465 {
466 hash->size = size = 15;
467 }
468 hash->load_limit = hash->size - (hash->size >> 2);
469 hash->used = 0;
470 hash->array = (struct hash_elem**)calloc(hash->size + 1, sizeof(struct hash_elem*));
471 if(hash->array == NULL((void*)0))
472 {
473 hash_destroy(hash);
474 hash = NULL((void*)0);
475 }
476 }
477 if(hash)
478 {
479 hash->elems_pool = pool_create(sizeof(struct hash_elem),
480 0, size, size << 1);
481 if(!hash->elems_pool)
482 {
483 hash_destroy(hash);
484 hash = NULL((void*)0);
485 }
486 }
487 return hash;
488}
489
490static void hash_resize(struct hash* hash)
491{
492unsigned int old_size = hash->size;
493unsigned int hashed;
494struct hash_elem* hash_elem;
495struct hash_elem* next;
496struct hash_elem** array;
497int i;
498
499 hash->size = (old_size << 1) + 1;
500 /* we really should avoid to get there... so print a message to alert of the condition */
501 fprintf(stderrstderr, "resize hash %d -> %d\n", old_size, hash->size);
502 if(hash->size == old_size)
503 {
504 hash->flags |= HASH_F_NO_RESIZE(1<<0);
505 return;
506 }
507 array = calloc(hash->size + 1, sizeof(struct hash_elem*));
508 if(array)
509 {
510 hash->load_limit = hash->size - (hash->size >> 2);
511 for(i=0; i <= old_size; i++)
512 {
513 hash_elem = (struct hash_elem*)hash->array[i];
514 while(hash_elem)
515 {
516 next = hash_elem->next;
517
518 hashed = hash_compute(hash, hash_elem->key, hash_elem->key_len);
519 hash_elem->next = array[hashed];
520 array[hashed] = hash_elem;
521 hash_elem = next;
522 }
523 }
524 free(hash->array);
525 hash->array = (struct hash_elem**)array;
526 }
527 else
528 {
529 hash->size = old_size;
530 hash->flags |= HASH_F_NO_RESIZE(1<<0);
531 }
532}
533
534#ifdef HASH_STAT
535static inline int compare_key(struct hash* hash, const char* a, const char* b, int len, int* cost)memcmp(const char* a,const char* b,int len)
536{
537 *cost += 1;
538 hash->memcmp += 1;
539 return memcmp(a,b, len);
540}
541#else
542#define compare_key(h,a,b,l,c)memcmp(a,b,l) memcmp(a,b,l)
543#endif
544
545/* a customized hash_store function that just store the key and return
546 * TRUE if the key was effectively stored, or FALSE if the key was already there
547 */
548static int hash_store(struct hash* hash, const char* key, int key_len)
549{
550unsigned int hashed;
551struct hash_elem* hash_elem;
552int cost = 0;
553
554 hashed = hash_compute(hash, key, key_len);
555#ifdef HASH_STAT
556 hash->stored += 1;
557#endif
558 hash_elem = (struct hash_elem*)hash->array[hashed];
559 while(hash_elem && (hash_elem->key_len != key_len || compare_key(hash, hash_elem->key, key, key_len, &cost)memcmp(hash_elem->key,key,key_len)))
560 {
561 hash_elem = hash_elem->next;
562 }
563
564 if(!hash_elem)
565 {
566 hash_elem = pool_alloc(hash->elems_pool);
567 if(hash_elem)
568 {
569 hash_elem->key = key;
570 hash_elem->key_len = key_len;
571 hash_elem->next = hash->array[hashed];
572
573#ifdef HASH_STAT
574 if(hash_elem->next)
575 {
576 hash->collisions += 1;
577 hash->cost += cost;
578 }
579#endif
580 hash->array[hashed] = hash_elem;
581 hash->used += 1;
582 if(hash->used > hash->load_limit)
583 {
584 hash_resize(hash);
585 }
586 }
587 return TRUE1;
588 }
589 return FALSE0;
590}
591
592static int file_stat(const char* name, struct stat* buffer_stat, int* rc)
593{
594int rc_local = 0;
595
596 rc_local = stat(name, buffer_stat);
597 if (rc_local < 0)
598 {
599 *rc = errno(*__errno_location ());
600 }
601 return rc_local;
602}
603
604static off_t file_get_size(const char* name, int* rc)
605{
606struct stat buffer_stat;
607off_t size = -1;
608
609 if (!file_stat(name, &buffer_stat, rc))
610 {
611 if(S_ISREG(buffer_stat.st_mode)((((buffer_stat.st_mode)) & 0170000) == (0100000)))
612 {
613 size = buffer_stat.st_size;
614 }
615 else
616 {
617 *rc = EINVAL22;
618 }
619 }
620 return size;
621}
622
623static char* file_load(const char* name, off_t* size, int* return_rc)
624{
625off_t local_size = 0;
626int rc = 0;
627char* buffer = NULL((void*)0);
628int fd;
629
630 assert(name != NULL)((name != ((void*)0)) ? (void) (0) : __assert_fail ("name != ((void*)0)"
, "/usr/local/src/libreoffice/solenv/bin/concat-deps.c", 630,
__PRETTY_FUNCTION__))
;
631
632 if(!size)
6
Taking false branch
633 {
634 size = &local_size;
635 }
636 *size = file_get_size(name, &rc);
637 if (!rc)
7
Taking true branch
638 {
639 fd = open(name, FILE_O_RDONLY00 | FILE_O_BINARY0);
640 if (!(fd == -1))
8
Taking true branch
641 {
642 buffer = malloc((size_t)(*size + 1));
9
Memory is allocated
643 if (buffer == NULL((void*)0))
10
Assuming 'buffer' is not equal to null
11
Taking false branch
644 {
645 rc = ENOMEM12;
646 }
647 else
648 {
649 ssize_t i;
650
651 REDO:
652 i = read(fd, buffer, (size_t)(*size));
653 if(i == -1)
12
Taking false branch
654 {
655 if(errno(*__errno_location ()) == EINTR4)
656 {
657 goto REDO;
658 }
659 else
660 {
661 rc = errno(*__errno_location ());
662 }
663 }
664 else
665 {
666 if (i != *size)
13
Taking false branch
667 {
668 rc = EIO5;
669 }
670 }
671 close(fd);
672 buffer[*size] = 0;
673 }
674 }
675 }
676
677 if(rc && buffer)
678 {
679 free(buffer);
680 buffer = NULL((void*)0);
681 }
682 if(return_rc)
14
Taking true branch
683 {
684 *return_rc = rc;
685 }
686 return buffer;
687}
688
689static void _cancel_relative(char* base, char** ref_cursor, char** ref_cursor_out, char* end)
690{
691 char* cursor = *ref_cursor;
692 char* cursor_out = *ref_cursor_out;
693
694 do
695 {
696 cursor += 3;
697 while(cursor_out > base && cursor_out[-1] == '/')
698 cursor_out--;
699 while(cursor_out > base && *--cursor_out != '/');
700 }
701 while(cursor + 3 < end && !memcmp(cursor, "/../", 4));
702 *ref_cursor = cursor;
703 *ref_cursor_out = cursor_out;
704}
705
706static inline void eat_space(char ** token)
707{
708 while ((' ' == **token) || ('\t' == **token)) {
709 ++(*token);
710 }
711}
712
713/*
714 * Prune LibreOffice specific duplicate dependencies to improve
715 * gnumake startup time, and shrink the disk-space footprint.
716 */
717static inline int
718elide_dependency(const char* key, int key_len,
719 int *boost_count, const char **unpacked_end)
720{
721#if 0
722 {
723 int i;
724 fprintf (stderrstderr, "elide?%d!: '", internal_boost);
725 for (i = 0; i < key_len; i++) {
726 fprintf (stderrstderr, "%c", key[i]);
727 }
728 fprintf (stderrstderr, "'\n");
729 }
730#endif
731
732 /* .hdl files are always matched by .hpp */
733 if (key_len > 4 && !strncmp(key + key_len - 4, ".hdl", 4))
734 return 1;
735
736 /* boost brings a plague of header files */
737 int i;
738 int boost = 0;
739 int unpacked = 0;
740 /* walk down path elements */
741 for (i = 0; i < key_len - 1; i++)
742 {
743 if (key[i] == '/')
744 {
745 if (internal_boost)
746 {
747 if (0 == boost)
748 {
749 if (!strncmp(key + i + 1, "solver/", 7))
750 {
751 boost++;
752 continue;
753 }
754 }
755 else if (!strncmp(key + i + 1, "inc/external/boost/", 19))
756 {
757 if (boost_count)
758 (*boost_count)++;
759 return 1;
760 }
761 }
762 if (0 == unpacked)
763 {
764 if (!strncmp(key + i + 1, "workdir/", 8))
765 {
766 unpacked = 1;
767 continue;
768 }
769 }
770 else
771 {
772 if (!strncmp(key + i + 1, "UnpackedTarball/", 16))
773 {
774 if (unpacked_end)
775 *unpacked_end = strchr(key + i + 17, '/');
776 return 1;
777 }
778 }
779 }
780 }
781
782 return 0;
783}
784
785/*
786 * We collapse tens of internal boost headers to a single one, such
787 * that you can re-compile / install boost and all is well.
788 */
789static void emit_single_boost_header(void)
790{
791#define BOOST_HEADER"/inc/external/boost/bind.hpp" "/inc/external/boost/bind.hpp"
792 fprintf(stdoutstdout, "%s" BOOST_HEADER"/inc/external/boost/bind.hpp" " ", out_dir);
793}
794
795static void emit_unpacked_target(char const*const token, char const*const end)
796{
797 /* is there some obvious way to printf N characters that i'm missing? */
798 size_t size = end - token + 1;
799 char tmp[size];
800 snprintf(tmp, size, "%s", token);
801 fputs(tmp, stdoutstdout);
802 fputs(".done ", stdoutstdout);
803}
804
805/* prefix paths to absolute */
806static inline void print_fullpaths(char* line)
807{
808 char* token;
809 char* end;
810 int boost_count = 0;
811 const char * unpacked_end = 0; /* end of UnpackedTarget match (if any) */
812 int first = 1; /* for UnpackedTarget the first (target) is GenCxxObject! */
813
814 token = line;
815 eat_space(&token);
816 while (*token)
817 {
818 end = token;
819 while (*end && (' ' != *end) && ('\t' != *end)) {
820 ++end;
821 }
822 int token_len = end - token;
823 if (!first &&
824 elide_dependency(token, token_len, &boost_count, &unpacked_end))
825 {
826 if (unpacked_end)
827 {
828 emit_unpacked_target(token, unpacked_end);
829 unpacked_end = 0;
830 }
831 else if (boost_count == 1)
832 emit_single_boost_header();
833 else
834 {
835 /* don't output, and swallow trailing \\\n if any */
836 token = end;
837 eat_space(&token);
838 if (token[0] == '\\' && token[1] == '\n')
839 end = token + 2;
840 }
841 }
842 else if(*token == ':' || *token == '\\' || *token == '/' ||
843 *token == '$' || ':' == token[1])
844 {
845 if(fwrite(token, token_len, 1, stdoutstdout) != 1)
846 abort();
847 fputc(' ', stdoutstdout);
848 }
849 else
850 {
851 if(fwrite(token, end - token, 1, stdoutstdout) != 1)
852 abort();
853 fputc(' ', stdoutstdout);
854 }
855 first = 0;
856 token = end;
857 eat_space(&token);
858 }
859}
860
861static inline char * eat_space_at_end(char * end)
862{
863 assert('\0' == *end)(('\0' == *end) ? (void) (0) : __assert_fail ("'\\0' == *end"
, "/usr/local/src/libreoffice/solenv/bin/concat-deps.c", 863,
__PRETTY_FUNCTION__))
;
864 char * real_end = end - 1;
865 while (' ' == *real_end || '\t' == *real_end || '\n' == *real_end
866 || ':' == *real_end)
867 { /* eat colon and whitespace at end */
868 --real_end;
869 }
870 return real_end;
871}
872
873static int _process(struct hash* dep_hash, char* fn)
874{
875int rc;
876char* buffer;
877char* end;
878char* cursor;
879char* cursor_out;
880char* base;
881int continuation = 0;
882char last_ns = 0;
883off_t size;
884
885 buffer = file_load(fn, &size, &rc);
886 /* Note: yes we are going to leak 'buffer'
887 * this is on purpose, to avoid cloning the 'key' out of it
888 * and our special 'hash' just store the pointer to the key
889 * inside of buffer, hence it need to remain allocated
890 */
891 if(!rc)
892 {
893 base = cursor_out = cursor = end = buffer;
894 end += size;
895 while(cursor < end)
896 {
897 if(*cursor == '\\')
898 {
899 continuation = 1;
900 *cursor_out++ = *cursor++;
901 }
902 else if(*cursor == '/')
903 {
904 if(cursor + 3 < end)
905 {
906 if(!memcmp(cursor, "/../", 4))
907 {
908 _cancel_relative(base, &cursor, &cursor_out, end);
909 }
910 }
911 *cursor_out++ = *cursor++;
912 }
913 else if(*cursor == '\n')
914 {
915 if(!continuation)
916 {
917 *cursor_out = 0;
918 if(base < cursor)
919 {
920 /* here we have a complete rule */
921 if(last_ns == ':')
922 {
923 /* if the rule ended in ':' that is a no-dep rule
924 * these are the one for which we want to filter
925 * duplicate out
926 */
927 int key_len = eat_space_at_end(cursor_out) - base;
928 if (!elide_dependency(base,key_len + 1, NULL((void*)0), NULL((void*)0))
929 && hash_store(dep_hash, base, key_len))
930 {
931 /* DO NOT modify base after it has been added
932 as key by hash_store */
933 print_fullpaths(base);
934 putc('\n', stdout)_IO_putc ('\n', stdout);
935 }
936 }
937 else
938 {
939 /* rule with dep, just write it */
940 print_fullpaths(base);
941 putc('\n', stdout)_IO_putc ('\n', stdout);
942 }
943 last_ns = ' '; // cannot hurt to reset it
944 }
945 cursor += 1;
946 base = cursor_out = cursor;
947 }
948 else
949 {
950 /* here we have a '\' followed by \n this is a continuation
951 * i.e not a complete rule yet
952 */
953 *cursor_out++ = *cursor++;
954 continuation = 0; // cancel current one (empty lines!)
955 }
956 }
957 else
958 {
959 continuation = 0;
960 /* not using isspace() here save 25% of I refs and 75% of D refs based on cachegrind */
961 if(*cursor != ' ' && *cursor != '\n' && *cursor != '\t' )
962 {
963 last_ns = *cursor;
964 }
965 *cursor_out++ = *cursor++;
966 }
967 }
968 /* just in case the file did not end with a \n, there may be a pending rule */
969 if(base < cursor_out)
970 {
971 if(last_ns == ':')
972 {
973 int key_len = eat_space_at_end(cursor_out) - base;
974 if (!elide_dependency(base,key_len + 1, NULL((void*)0), NULL((void*)0)) &&
975 hash_store(dep_hash, base, key_len))
976 {
977 puts(base);
978 putc('\n', stdout)_IO_putc ('\n', stdout);
979 }
980 }
981 else
982 {
983 puts(base);
984 putc('\n', stdout)_IO_putc ('\n', stdout);
985 }
986 }
987 }
988 return rc;
989}
990
991static void _usage(void)
992{
993 fputs("Usage: concat-deps <file that contains dep_files>\n", stderrstderr);
994}
995
996#define kDEFAULT_HASH_SIZE4096 4096
997
998static int get_var(char **var, const char *name)
999{
1000 *var = (char *)getenv(name);
1001 if(!*var)
1002 {
1003 fprintf(stderrstderr,"Error: %s is missing in the environement\n", name);
1004 return 1;
1005 }
1006 return 0;
1007}
1008
1009int main(int argc, char** argv)
1010{
1011int rc = 0;
1012off_t in_list_size = 0;
1013char* in_list;
1014char* in_list_cursor;
1015char* in_list_base;
1016struct hash* dep_hash;
1017const char *env_str;
1018
1019 if(argc < 2)
1
Assuming 'argc' is >= 2
2
Taking false branch
1020 {
1021 _usage();
1022 return 1;
1023 }
1024 if(get_var(&base_dir, "SRCDIR") || get_var(&out_dir, "OUTDIR"))
3
Taking false branch
1025 return 1;
1026
1027 env_str = getenv("SYSTEM_BOOST");
1028 internal_boost = !env_str || strcmp(env_str,"TRUE");
4
Assuming 'env_str' is non-null
1029
1030 in_list = file_load(argv[1], &in_list_size, &rc);
5
Calling 'file_load'
15
Returned allocated memory
1031 if(!rc)
16
Taking true branch
1032 {
1033 dep_hash = hash_create( kDEFAULT_HASH_SIZE4096);
1034 in_list_base = in_list_cursor = in_list;
1035
1036 /* extract filename of dep file from a 'space' separated list */
1037 while(*in_list_cursor)
17
Loop condition is true. Entering loop body
19
Loop condition is true. Entering loop body
21
Loop condition is true. Entering loop body
23
Loop condition is true. Entering loop body
1038 {
1039 if(*in_list_cursor == ' ' || *in_list_cursor == '\n')
18
Taking false branch
20
Taking false branch
22
Taking false branch
24
Taking true branch
1040 {
1041 *in_list_cursor = 0;
1042 if(in_list_base < in_list_cursor)
25
Taking true branch
1043 {
1044 rc = _process(dep_hash, in_list_base);
1045 if(rc)
26
Taking true branch
1046 {
1047 break;
27
Execution continues on line 1058
1048 }
1049 }
1050 in_list_cursor += 1;
1051 in_list_base = in_list_cursor;
1052 }
1053 else
1054 {
1055 in_list_cursor += 1;
1056 }
1057 }
1058 if(!rc)
28
Taking false branch
1059 {
1060 /* catch the last entry in case the input did not terminate with a 'space' */
1061 if(in_list_base < in_list_cursor)
1062 {
1063 rc = _process(dep_hash, in_list_base);
1064 }
1065 }
1066#ifdef HASH_STAT
1067 fprintf(stderrstderr, "stats: u:%d s:%d l:%d t:%d c:%d m:%d $:%d\n",
1068 dep_hash->used, dep_hash->size, dep_hash->load_limit, dep_hash->stored,
1069 dep_hash->collisions, dep_hash->memcmp, dep_hash->cost);
1070#endif
1071 }
1072 return rc;
29
Memory is never released; potential leak of memory pointed to by 'in_list_base'
1073}
1074
1075/* vim:set shiftwidth=4 softtabstop=4 expandtab: */