VirtualBox

source: kBuild/trunk/src/kmk/incdep.c

Last change on this file was 3565, checked in by bird, 3 years ago

kmk/incdep.c: Fixed buffer/heap trashing in incdep_unescape_and_cache_filename when encountering non-escaping slashes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 68.7 KB
Line 
1#ifdef CONFIG_WITH_INCLUDEDEP
2/* $Id: incdep.c 3565 2022-05-24 20:40:24Z bird $ */
3/** @file
4 * incdep - Simple dependency files.
5 */
6
7/*
8 * Copyright (c) 2007-2010 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
9 *
10 * This file is part of kBuild.
11 *
12 * kBuild is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * kBuild is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
24 *
25 */
26
27/*******************************************************************************
28* Header Files *
29*******************************************************************************/
30#ifdef __OS2__
31# define INCL_BASE
32# define INCL_ERRORS
33#endif
34#ifdef KBUILD_OS_WINDOWS
35# ifdef KMK
36# define INCDEP_USE_KFSCACHE
37# endif
38#endif
39
40#include "makeint.h"
41
42#if !defined(WINDOWS32) && !defined(__OS2__)
43# define HAVE_PTHREAD
44#endif
45
46#include <assert.h>
47
48#include <glob.h>
49
50#include "filedef.h"
51#include "dep.h"
52#include "job.h"
53#include "commands.h"
54#include "variable.h"
55#include "rule.h"
56#include "debug.h"
57#include "strcache2.h"
58
59#ifdef HAVE_FCNTL_H
60# include <fcntl.h>
61#else
62# include <sys/file.h>
63#endif
64
65#ifdef WINDOWS32
66# include <io.h>
67# include <process.h>
68# include <Windows.h>
69# define PARSE_IN_WORKER
70#endif
71
72#ifdef INCDEP_USE_KFSCACHE
73# include "nt/kFsCache.h"
74extern PKFSCACHE g_pFsCache; /* dir-nt-bird.c for now */
75#endif
76
77#ifdef __OS2__
78# include <os2.h>
79# include <sys/fmutex.h>
80#endif
81
82#ifdef HAVE_PTHREAD
83# include <pthread.h>
84#endif
85
86#ifdef __APPLE__
87# include <malloc/malloc.h>
88# define PARSE_IN_WORKER
89#endif
90
91#if defined(__gnu_linux__) || defined(__linux__)
92# define PARSE_IN_WORKER
93#endif
94
95
96/*******************************************************************************
97* Structures and Typedefs *
98*******************************************************************************/
99struct incdep_variable_in_set
100{
101 struct incdep_variable_in_set *next;
102 /* the parameters */
103 struct strcache2_entry *name_entry; /* dep strcache - WRONG */
104 const char *value; /* xmalloc'ed */
105 unsigned int value_length;
106 int duplicate_value; /* 0 */
107 enum variable_origin origin;
108 int recursive;
109 struct variable_set *set;
110 const floc *flocp; /* NILF */
111};
112
113struct incdep_variable_def
114{
115 struct incdep_variable_def *next;
116 /* the parameters */
117 const floc *flocp; /* NILF */
118 struct strcache2_entry *name_entry; /* dep strcache - WRONG */
119 char *value; /* xmalloc'ed, free it */
120 unsigned int value_length;
121 enum variable_origin origin;
122 enum variable_flavor flavor;
123 int target_var;
124};
125
126struct incdep_recorded_file
127{
128 struct incdep_recorded_file *next;
129
130 /* the parameters */
131 struct strcache2_entry *filename_entry; /* dep strcache; converted to a nameseq record. */
132 struct dep *deps; /* All the names are dep strcache entries. */
133 const floc *flocp; /* NILF */
134};
135
136
137/* per dep file structure. */
138struct incdep
139{
140 struct incdep *next;
141 char *file_base;
142 char *file_end;
143
144 int worker_tid;
145#ifdef PARSE_IN_WORKER
146 unsigned int err_line_no;
147 const char *err_msg;
148
149 struct incdep_variable_in_set *recorded_variables_in_set_head;
150 struct incdep_variable_in_set *recorded_variables_in_set_tail;
151
152 struct incdep_variable_def *recorded_variable_defs_head;
153 struct incdep_variable_def *recorded_variable_defs_tail;
154
155 struct incdep_recorded_file *recorded_file_head;
156 struct incdep_recorded_file *recorded_file_tail;
157#endif
158#ifdef INCDEP_USE_KFSCACHE
159 /** Pointer to the fs cache object for this file (it exists and is a file). */
160 PKFSOBJ pFileObj;
161#else
162 char name[1];
163#endif
164};
165
166
167/*******************************************************************************
168* Global Variables *
169*******************************************************************************/
170
171/* mutex protecting the globals and an associated condition/event. */
172#ifdef HAVE_PTHREAD
173static pthread_mutex_t incdep_mtx;
174static pthread_cond_t incdep_cond_todo;
175static pthread_cond_t incdep_cond_done;
176
177#elif defined (WINDOWS32)
178static CRITICAL_SECTION incdep_mtx;
179static HANDLE incdep_hev_todo;
180static HANDLE incdep_hev_done;
181static int volatile incdep_hev_todo_waiters;
182static int volatile incdep_hev_done_waiters;
183
184#elif defined (__OS2__)
185static _fmutex incdep_mtx;
186static HEV incdep_hev_todo;
187static HEV incdep_hev_done;
188static int volatile incdep_hev_todo_waiters;
189static int volatile incdep_hev_done_waiters;
190#endif
191
192/* flag indicating whether the threads, lock and event/condvars has
193 been initialized or not. */
194static int incdep_initialized;
195
196/* the list of files that needs reading. */
197static struct incdep * volatile incdep_head_todo;
198static struct incdep * volatile incdep_tail_todo;
199
200/* the number of files that are currently being read. */
201static int volatile incdep_num_reading;
202
203/* the list of files that have been read. */
204static struct incdep * volatile incdep_head_done;
205static struct incdep * volatile incdep_tail_done;
206
207
208/* The handles to the worker threads. */
209#ifdef HAVE_PTHREAD
210# define INCDEP_MAX_THREADS 1
211static pthread_t incdep_threads[INCDEP_MAX_THREADS];
212
213#elif defined (WINDOWS32)
214# define INCDEP_MAX_THREADS 2
215static HANDLE incdep_threads[INCDEP_MAX_THREADS];
216
217#elif defined (__OS2__)
218# define INCDEP_MAX_THREADS 2
219static TID incdep_threads[INCDEP_MAX_THREADS];
220#endif
221
222static struct alloccache incdep_rec_caches[INCDEP_MAX_THREADS];
223static struct alloccache incdep_dep_caches[INCDEP_MAX_THREADS];
224static struct strcache2 incdep_dep_strcaches[INCDEP_MAX_THREADS];
225static struct strcache2 incdep_var_strcaches[INCDEP_MAX_THREADS];
226static unsigned incdep_num_threads;
227
228/* flag indicating whether the worker threads should terminate or not. */
229static int volatile incdep_terminate;
230
231#ifdef __APPLE__
232/* malloc zone for the incdep threads. */
233static malloc_zone_t *incdep_zone;
234#endif
235
236
237/*******************************************************************************
238* Internal Functions *
239*******************************************************************************/
240static void incdep_flush_it (floc *);
241static void eval_include_dep_file (struct incdep *, floc *);
242static void incdep_commit_recorded_file (const char *filename, struct dep *deps,
243 const floc *flocp);
244
245
246/* xmalloc wrapper.
247 For working around multithreaded performance problems found on Darwin,
248 Linux (glibc), and possibly other systems. */
249static void *
250incdep_xmalloc (struct incdep *cur, size_t size)
251{
252 void *ptr;
253
254#ifdef __APPLE__
255 if (cur && cur->worker_tid != -1)
256 {
257 ptr = malloc_zone_malloc (incdep_zone, size);
258 if (!ptr)
259 O (fatal, NILF, _("virtual memory exhausted"));
260 }
261 else
262 ptr = xmalloc (size);
263#else
264 ptr = xmalloc (size);
265#endif
266
267 (void)cur;
268 return ptr;
269}
270
271#if 0
272/* cmalloc wrapper */
273static void *
274incdep_xcalloc (struct incdep *cur, size_t size)
275{
276 void *ptr;
277
278#ifdef __APPLE__
279 if (cur && cur->worker_tid != -1)
280 ptr = malloc_zone_calloc (incdep_zone, size, 1);
281 else
282 ptr = calloc (size, 1);
283#else
284 ptr = calloc (size, 1);
285#endif
286 if (!ptr)
287 fatal (NILF, _("virtual memory exhausted"));
288
289 (void)cur;
290 return ptr;
291}
292#endif /* unused */
293
294/* free wrapper */
295static void
296incdep_xfree (struct incdep *cur, void *ptr)
297{
298 /* free() *must* work for the allocation hacks above because
299 of free_dep_chain. */
300 free (ptr);
301 (void)cur;
302}
303
304/* alloc a dep structure. These are allocated in bunches to save time. */
305struct dep *
306incdep_alloc_dep (struct incdep *cur)
307{
308 struct alloccache *cache;
309 if (cur->worker_tid != -1)
310 cache = &incdep_dep_caches[cur->worker_tid];
311 else
312 cache = &dep_cache;
313 return alloccache_calloc (cache);
314}
315
316/* duplicates the dependency list pointed to by srcdep. */
317static struct dep *
318incdep_dup_dep_list (struct incdep *cur, struct dep const *srcdep)
319{
320 struct alloccache *cache;
321 struct dep *retdep;
322 struct dep *dstdep;
323
324 if (cur->worker_tid != -1)
325 cache = &incdep_dep_caches[cur->worker_tid];
326 else
327 cache = &dep_cache;
328
329 if (srcdep)
330 {
331 retdep = dstdep = alloccache_alloc (cache);
332 for (;;)
333 {
334 dstdep->name = srcdep->name; /* string cached */
335 dstdep->includedep = srcdep->includedep;
336 srcdep = srcdep->next;
337 if (!srcdep)
338 {
339 dstdep->next = NULL;
340 break;
341 }
342 dstdep->next = alloccache_alloc (cache);
343 dstdep = dstdep->next;
344 }
345 }
346 else
347 retdep = NULL;
348 return retdep;
349}
350
351
352/* allocate a record. */
353static void *
354incdep_alloc_rec (struct incdep *cur)
355{
356 return alloccache_alloc (&incdep_rec_caches[cur->worker_tid]);
357}
358
359/* free a record. */
360static void
361incdep_free_rec (struct incdep *cur, void *rec)
362{
363 /*alloccache_free (&incdep_rec_caches[cur->worker_tid], rec); - doesn't work of course. */
364}
365
366
367/* grow a cache. */
368static void *
369incdep_cache_allocator (void *thrd, unsigned int size)
370{
371 (void)thrd;
372#ifdef __APPLE__
373 return malloc_zone_malloc (incdep_zone, size);
374#else
375 return xmalloc (size);
376#endif
377}
378
379/* term a cache. */
380static void
381incdep_cache_deallocator (void *thrd, void *ptr, unsigned int size)
382{
383 (void)thrd;
384 (void)size;
385 free (ptr);
386}
387
388/* acquires the lock */
389void
390incdep_lock(void)
391{
392#if defined (HAVE_PTHREAD) && !defined (CONFIG_WITHOUT_THREADS)
393 pthread_mutex_lock (&incdep_mtx);
394#elif defined (WINDOWS32)
395 EnterCriticalSection (&incdep_mtx);
396#elif defined (__OS2__)
397 _fmutex_request (&incdep_mtx, 0);
398#endif
399}
400
401/* releases the lock */
402void
403incdep_unlock(void)
404{
405#if defined (HAVE_PTHREAD) && !defined (CONFIG_WITHOUT_THREADS)
406 pthread_mutex_unlock (&incdep_mtx);
407#elif defined(WINDOWS32)
408 LeaveCriticalSection (&incdep_mtx);
409#elif defined(__OS2__)
410 _fmutex_release (&incdep_mtx);
411#endif
412}
413
414/* signals the main thread that there is stuff todo. caller owns the lock. */
415static void
416incdep_signal_done (void)
417{
418#if defined (HAVE_PTHREAD) && !defined (CONFIG_WITHOUT_THREADS)
419 pthread_cond_broadcast (&incdep_cond_done);
420#elif defined (WINDOWS32)
421 if (incdep_hev_done_waiters)
422 SetEvent (incdep_hev_done);
423#elif defined (__OS2__)
424 if (incdep_hev_done_waiters)
425 DosPostEventSem (incdep_hev_done);
426#endif
427}
428
429/* waits for a reader to finish reading. caller owns the lock. */
430static void
431incdep_wait_done (void)
432{
433#if defined (HAVE_PTHREAD) && !defined (CONFIG_WITHOUT_THREADS)
434 pthread_cond_wait (&incdep_cond_done, &incdep_mtx);
435
436#elif defined (WINDOWS32)
437 ResetEvent (incdep_hev_done);
438 incdep_hev_done_waiters++;
439 incdep_unlock ();
440 WaitForSingleObject (incdep_hev_done, INFINITE);
441 incdep_lock ();
442 incdep_hev_done_waiters--;
443
444#elif defined (__OS2__)
445 ULONG ulIgnore;
446 DosResetEventSem (incdep_hev_done, &ulIgnore);
447 incdep_hev_done_waiters++;
448 incdep_unlock ();
449 DosWaitEventSem (incdep_hev_done, SEM_INDEFINITE_WAIT);
450 incdep_lock ();
451 incdep_hev_done_waiters--;
452#endif
453}
454
455/* signals the worker threads. caller owns the lock. */
456static void
457incdep_signal_todo (void)
458{
459#if defined (HAVE_PTHREAD) && !defined (CONFIG_WITHOUT_THREADS)
460 pthread_cond_broadcast (&incdep_cond_todo);
461#elif defined(WINDOWS32)
462 if (incdep_hev_todo_waiters)
463 SetEvent (incdep_hev_todo);
464#elif defined(__OS2__)
465 if (incdep_hev_todo_waiters)
466 DosPostEventSem (incdep_hev_todo);
467#endif
468}
469
470/* waits for stuff to arrive in the todo list. caller owns the lock. */
471static void
472incdep_wait_todo (void)
473{
474#if defined (HAVE_PTHREAD) && !defined (CONFIG_WITHOUT_THREADS)
475 pthread_cond_wait (&incdep_cond_todo, &incdep_mtx);
476
477#elif defined (WINDOWS32)
478 ResetEvent (incdep_hev_todo);
479 incdep_hev_todo_waiters++;
480 incdep_unlock ();
481 WaitForSingleObject (incdep_hev_todo, INFINITE);
482 incdep_lock ();
483 incdep_hev_todo_waiters--;
484
485#elif defined (__OS2__)
486 ULONG ulIgnore;
487 DosResetEventSem (incdep_hev_todo, &ulIgnore);
488 incdep_hev_todo_waiters++;
489 incdep_unlock ();
490 DosWaitEventSem (incdep_hev_todo, SEM_INDEFINITE_WAIT);
491 incdep_lock ();
492 incdep_hev_todo_waiters--;
493#endif
494}
495
496/* Reads a dep file into memory. */
497static int
498incdep_read_file (struct incdep *cur, floc *f)
499{
500#ifdef INCDEP_USE_KFSCACHE
501 size_t const cbFile = (size_t)cur->pFileObj->Stats.st_size;
502
503 assert(cur->pFileObj->fHaveStats);
504 cur->file_base = incdep_xmalloc (cur, cbFile + 1);
505 if (cur->file_base)
506 {
507 if (kFsCacheFileSimpleOpenReadClose (g_pFsCache, cur->pFileObj, 0, cur->file_base, cbFile))
508 {
509 cur->file_end = cur->file_base + cbFile;
510 cur->file_base[cbFile] = '\0';
511 return 0;
512 }
513 incdep_xfree (cur, cur->file_base);
514 }
515 OSS (error, f, "%s/%s: error reading file", cur->pFileObj->pParent->Obj.pszName, cur->pFileObj->pszName);
516
517#else /* !INCDEP_USE_KFSCACHE */
518 int fd;
519 struct stat st;
520
521 errno = 0;
522# ifdef O_BINARY
523 fd = open (cur->name, O_RDONLY | O_BINARY, 0);
524# else
525 fd = open (cur->name, O_RDONLY, 0);
526# endif
527 if (fd < 0)
528 {
529 /* ignore non-existing dependency files. */
530 int err = errno;
531 if (err == ENOENT || stat (cur->name, &st) != 0)
532 return 1;
533 OSS (error, f, "%s: %s", cur->name, strerror (err));
534 return -1;
535 }
536# ifdef KBUILD_OS_WINDOWS /* fewer kernel calls */
537 if (!birdStatOnFdJustSize (fd, &st.st_size))
538# else
539 if (!fstat (fd, &st))
540# endif
541 {
542 cur->file_base = incdep_xmalloc (cur, st.st_size + 1);
543 if (read (fd, cur->file_base, st.st_size) == st.st_size)
544 {
545 close (fd);
546 cur->file_end = cur->file_base + st.st_size;
547 cur->file_base[st.st_size] = '\0';
548 return 0;
549 }
550
551 /* bail out */
552
553 OSS (error, f, "%s: read: %s", cur->name, strerror (errno));
554 incdep_xfree (cur, cur->file_base);
555 }
556 else
557 OSS (error, f, "%s: fstat: %s", cur->name, strerror (errno));
558
559 close (fd);
560#endif /* !INCDEP_USE_KFSCACHE */
561 cur->file_base = cur->file_end = NULL;
562 return -1;
563}
564
565/* Free the incdep structure. */
566static void
567incdep_freeit (struct incdep *cur)
568{
569#ifdef PARSE_IN_WORKER
570 assert (!cur->recorded_variables_in_set_head);
571 assert (!cur->recorded_variable_defs_head);
572 assert (!cur->recorded_file_head);
573#endif
574
575 incdep_xfree (cur, cur->file_base);
576#ifdef INCDEP_USE_KFSCACHE
577 /** @todo release object ref some day... */
578#endif
579 cur->next = NULL;
580 free (cur);
581}
582
583/* A worker thread. */
584void
585incdep_worker (int thrd)
586{
587 incdep_lock ();
588
589 while (!incdep_terminate)
590 {
591 /* get job from the todo list. */
592
593 struct incdep *cur = incdep_head_todo;
594 if (!cur)
595 {
596 incdep_wait_todo ();
597 continue;
598 }
599 if (cur->next)
600 incdep_head_todo = cur->next;
601 else
602 incdep_head_todo = incdep_tail_todo = NULL;
603 incdep_num_reading++;
604
605 /* read the file. */
606
607 incdep_unlock ();
608 cur->worker_tid = thrd;
609
610 incdep_read_file (cur, NILF);
611#ifdef PARSE_IN_WORKER
612 eval_include_dep_file (cur, NILF);
613#endif
614
615 cur->worker_tid = -1;
616 incdep_lock ();
617
618 /* insert finished job into the done list. */
619
620 incdep_num_reading--;
621 cur->next = NULL;
622 if (incdep_tail_done)
623 incdep_tail_done->next = cur;
624 else
625 incdep_head_done = cur;
626 incdep_tail_done = cur;
627
628 incdep_signal_done ();
629 }
630
631 incdep_unlock ();
632}
633
634/* Thread library specific thread functions wrapping incdep_wroker. */
635#ifdef HAVE_PTHREAD
636static void *
637incdep_worker_pthread (void *thrd)
638{
639 incdep_worker ((size_t)thrd);
640 return NULL;
641}
642
643#elif defined (WINDOWS32)
644static unsigned __stdcall
645incdep_worker_windows (void *thrd)
646{
647 incdep_worker ((size_t)thrd);
648 return 0;
649}
650
651#elif defined (__OS2__)
652static void
653incdep_worker_os2 (void *thrd)
654{
655 incdep_worker ((size_t)thrd);
656}
657#endif
658
659/* Checks if threads are enabled or not.
660
661 This is a special hack so that is possible to disable the threads when in a
662 debian fakeroot environment. Thus, in addition to the KMK_THREADS_DISABLED
663 and KMK_THREADS_ENABLED environment variable check we also check for signs
664 of fakeroot. */
665static int
666incdep_are_threads_enabled (void)
667{
668#if defined (CONFIG_WITHOUT_THREADS)
669 return 0;
670#endif
671
672 /* Generic overrides. */
673 if (getenv ("KMK_THREADS_DISABLED"))
674 {
675 O (message, 1, "Threads disabled (environment)");
676 return 0;
677 }
678 if (getenv ("KMK_THREADS_ENABLED"))
679 return 1;
680
681#if defined (__gnu_linux__) || defined (__linux__) || defined(__GLIBC__)
682 /* Try detect fakeroot. */
683 if (getenv ("FAKEROOTKEY")
684 || getenv ("FAKEROOTUID")
685 || getenv ("FAKEROOTGID")
686 || getenv ("FAKEROOTEUID")
687 || getenv ("FAKEROOTEGID")
688 || getenv ("FAKEROOTSUID")
689 || getenv ("FAKEROOTSGID")
690 || getenv ("FAKEROOTFUID")
691 || getenv ("FAKEROOTFGID")
692 || getenv ("FAKEROOTDONTTRYCHOWN")
693 || getenv ("FAKEROOT_FD_BASE")
694 || getenv ("FAKEROOT_DB_SEARCH_PATHS"))
695 {
696 O (message, 1, "Threads disabled (fakeroot)");
697 return 0;
698 }
699
700 /* LD_PRELOAD could indicate undetected debian fakeroot or some
701 other ingenius library which cannot deal correctly with threads. */
702 if (getenv ("LD_PRELOAD"))
703 {
704 O (message, 1, "Threads disabled (LD_PRELOAD)");
705 return 0;
706 }
707
708#elif defined(__APPLE__) \
709 || defined(__sun__) || defined(__SunOS__) || defined(__sun) || defined(__SunOS) \
710 || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) \
711 || defined(__HAIKU__)
712 /* No broken preload libraries known to be in common use on these platforms... */
713
714#elif defined(_MSC_VER) || defined(_WIN32) || defined(__OS2__)
715 /* No preload mess to care about. */
716
717#else
718# error "Add your self to the appropriate case above and send a patch to bird."
719#endif
720 return 1;
721}
722
723/* Creates the the worker threads. */
724static void
725incdep_init (floc *f)
726{
727 unsigned i;
728#if defined (HAVE_PTHREAD) && !defined (CONFIG_WITHOUT_THREADS)
729 int rc;
730 pthread_attr_t attr;
731
732#elif defined (WINDOWS32)
733 unsigned tid;
734 uintptr_t hThread;
735
736#elif defined (__OS2__)
737 int rc;
738 int tid;
739#endif
740 (void)f;
741
742 /* heap hacks */
743
744#ifdef __APPLE__
745 incdep_zone = malloc_create_zone (0, 0);
746 if (!incdep_zone)
747 incdep_zone = malloc_default_zone ();
748#endif
749
750
751 /* create the mutex and two condition variables / event objects. */
752
753#if defined (HAVE_PTHREAD) && !defined (CONFIG_WITHOUT_THREADS)
754 rc = pthread_mutex_init (&incdep_mtx, NULL);
755 if (rc)
756 ON (fatal, f, _("pthread_mutex_init failed: err=%d"), rc);
757 rc = pthread_cond_init (&incdep_cond_todo, NULL);
758 if (rc)
759 ON (fatal, f, _("pthread_cond_init failed: err=%d"), rc);
760 rc = pthread_cond_init (&incdep_cond_done, NULL);
761 if (rc)
762 ON (fatal, f, _("pthread_cond_init failed: err=%d"), rc);
763
764#elif defined (WINDOWS32)
765 InitializeCriticalSection (&incdep_mtx);
766 incdep_hev_todo = CreateEvent (NULL, TRUE /*bManualReset*/, FALSE /*bInitialState*/, NULL);
767 if (!incdep_hev_todo)
768 ON (fatal, f, _("CreateEvent failed: err=%d"), GetLastError());
769 incdep_hev_done = CreateEvent (NULL, TRUE /*bManualReset*/, FALSE /*bInitialState*/, NULL);
770 if (!incdep_hev_done)
771 ON (fatal, f, _("CreateEvent failed: err=%d"), GetLastError());
772 incdep_hev_todo_waiters = 0;
773 incdep_hev_done_waiters = 0;
774
775#elif defined (__OS2__)
776 _fmutex_create (&incdep_mtx, 0);
777 rc = DosCreateEventSem (NULL, &incdep_hev_todo, 0, FALSE);
778 if (rc)
779 ON (fatal, f, _("DosCreateEventSem failed: rc=%d"), rc);
780 rc = DosCreateEventSem (NULL, &incdep_hev_done, 0, FALSE);
781 if (rc)
782 ON (fatal, f, _("DosCreateEventSem failed: rc=%d"), rc);
783 incdep_hev_todo_waiters = 0;
784 incdep_hev_done_waiters = 0;
785#endif
786
787 /* create the worker threads and associated per thread data. */
788
789 incdep_terminate = 0;
790 if (incdep_are_threads_enabled())
791 {
792 incdep_num_threads = sizeof (incdep_threads) / sizeof (incdep_threads[0]);
793 if (incdep_num_threads + 1 > job_slots)
794 incdep_num_threads = job_slots <= 1 ? 1 : job_slots - 1;
795 for (i = 0; i < incdep_num_threads; i++)
796 {
797 /* init caches */
798 unsigned rec_size = sizeof (struct incdep_variable_in_set);
799 if (rec_size < sizeof (struct incdep_variable_def))
800 rec_size = sizeof (struct incdep_variable_def);
801 if (rec_size < sizeof (struct incdep_recorded_file))
802 rec_size = sizeof (struct incdep_recorded_file);
803 alloccache_init (&incdep_rec_caches[i], rec_size, "incdep rec",
804 incdep_cache_allocator, (void *)(size_t)i);
805 alloccache_init (&incdep_dep_caches[i], sizeof(struct dep), "incdep dep",
806 incdep_cache_allocator, (void *)(size_t)i);
807 strcache2_init (&incdep_dep_strcaches[i],
808 "incdep dep", /* name */
809 65536, /* hash size */
810 0, /* default segment size*/
811#ifdef HAVE_CASE_INSENSITIVE_FS
812 1, /* case insensitive */
813#else
814 0, /* case insensitive */
815#endif
816 0); /* thread safe */
817
818 strcache2_init (&incdep_var_strcaches[i],
819 "incdep var", /* name */
820 32768, /* hash size */
821 0, /* default segment size*/
822 0, /* case insensitive */
823 0); /* thread safe */
824
825 /* create the thread. */
826#if defined (HAVE_PTHREAD) && !defined (CONFIG_WITHOUT_THREADS)
827 rc = pthread_attr_init (&attr);
828 if (rc)
829 ON (fatal, f, _("pthread_attr_init failed: err=%d"), rc);
830 /*rc = pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE); */
831 rc = pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
832 if (rc)
833 ON (fatal, f, _("pthread_attr_setdetachstate failed: err=%d"), rc);
834 rc = pthread_create (&incdep_threads[i], &attr,
835 incdep_worker_pthread, (void *)(size_t)i);
836 if (rc)
837 ON (fatal, f, _("pthread_mutex_init failed: err=%d"), rc);
838 pthread_attr_destroy (&attr);
839
840#elif defined (WINDOWS32)
841 tid = 0;
842 hThread = _beginthreadex (NULL, 128*1024, incdep_worker_windows,
843 (void *)i, 0, &tid);
844 if (hThread == 0 || hThread == ~(uintptr_t)0)
845 ON (fatal, f, _("_beginthreadex failed: err=%d"), errno);
846 incdep_threads[i] = (HANDLE)hThread;
847
848#elif defined (__OS2__)
849 tid = _beginthread (incdep_worker_os2, NULL, 128*1024, (void *)i);
850 if (tid <= 0)
851 ON (fatal, f, _("_beginthread failed: err=%d"), errno);
852 incdep_threads[i] = tid;
853#endif
854 }
855 }
856 else
857 incdep_num_threads = 0;
858
859 incdep_initialized = 1;
860}
861
862/* Flushes outstanding work and terminates the worker threads.
863 This is called from snap_deps(). */
864void
865incdep_flush_and_term (void)
866{
867 unsigned i;
868
869 if (!incdep_initialized)
870 return;
871
872 /* flush any out standing work */
873
874 incdep_flush_it (NILF);
875
876 /* tell the threads to terminate */
877
878 incdep_lock ();
879 incdep_terminate = 1;
880 incdep_signal_todo ();
881 incdep_unlock ();
882
883 /* wait for the threads to quit */
884
885 for (i = 0; i < incdep_num_threads; i++)
886 {
887 /* more later? */
888
889 /* terminate or join up the allocation caches. */
890 alloccache_term (&incdep_rec_caches[i], incdep_cache_deallocator, (void *)(size_t)i);
891 alloccache_join (&dep_cache, &incdep_dep_caches[i]);
892 strcache2_term (&incdep_dep_strcaches[i]);
893 strcache2_term (&incdep_var_strcaches[i]);
894 }
895 incdep_num_threads = 0;
896
897 /* destroy the lock and condition variables / event objects. */
898
899 /* later */
900
901 incdep_initialized = 0;
902}
903
904#ifdef PARSE_IN_WORKER
905/* Flushes a strcache entry returning the actual string cache entry.
906 The input is freed! */
907static const char *
908incdep_flush_strcache_entry (struct strcache2_entry *entry)
909{
910 if (!entry->user)
911 entry->user = (void *) strcache2_add_hashed_file (&file_strcache,
912 (const char *)(entry + 1),
913 entry->length, entry->hash);
914 return (const char *)entry->user;
915}
916
917/* Flushes the recorded instructions. */
918static void
919incdep_flush_recorded_instructions (struct incdep *cur)
920{
921 struct incdep_variable_in_set *rec_vis;
922 struct incdep_variable_def *rec_vd;
923 struct incdep_recorded_file *rec_f;
924
925 /* Display saved error. */
926
927 if (cur->err_msg)
928#ifdef INCDEP_USE_KFSCACHE
929 OSSNS (error, NILF, "%s/%s(%d): %s", cur->pFileObj->pParent->Obj.pszName, cur->pFileObj->pszName,
930 cur->err_line_no, cur->err_msg);
931#else
932 OSNS (error,NILF, "%s(%d): %s", cur->name, cur->err_line_no, cur->err_msg);
933#endif
934
935
936 /* define_variable_in_set */
937
938 rec_vis = cur->recorded_variables_in_set_head;
939 cur->recorded_variables_in_set_head = cur->recorded_variables_in_set_tail = NULL;
940 if (rec_vis)
941 do
942 {
943 void *free_me = rec_vis;
944 unsigned int name_length = rec_vis->name_entry->length;
945 define_variable_in_set (incdep_flush_strcache_entry (rec_vis->name_entry),
946 name_length,
947 rec_vis->value,
948 rec_vis->value_length,
949 rec_vis->duplicate_value,
950 rec_vis->origin,
951 rec_vis->recursive,
952 rec_vis->set,
953 rec_vis->flocp);
954 rec_vis = rec_vis->next;
955 incdep_free_rec (cur, free_me);
956 }
957 while (rec_vis);
958
959 /* do_variable_definition */
960
961 rec_vd = cur->recorded_variable_defs_head;
962 cur->recorded_variable_defs_head = cur->recorded_variable_defs_tail = NULL;
963 if (rec_vd)
964 do
965 {
966 void *free_me = rec_vd;
967 do_variable_definition_2 (rec_vd->flocp,
968 incdep_flush_strcache_entry (rec_vd->name_entry),
969 rec_vd->value,
970 rec_vd->value_length,
971 0,
972 rec_vd->value,
973 rec_vd->origin,
974 rec_vd->flavor,
975 rec_vd->target_var);
976 rec_vd = rec_vd->next;
977 incdep_free_rec (cur, free_me);
978 }
979 while (rec_vd);
980
981 /* record_files */
982
983 rec_f = cur->recorded_file_head;
984 cur->recorded_file_head = cur->recorded_file_tail = NULL;
985 if (rec_f)
986 do
987 {
988 void *free_me = rec_f;
989 struct dep *dep;
990
991 for (dep = rec_f->deps; dep; dep = dep->next)
992 dep->name = incdep_flush_strcache_entry ((struct strcache2_entry *)dep->name);
993
994 incdep_commit_recorded_file (incdep_flush_strcache_entry (rec_f->filename_entry),
995 rec_f->deps,
996 rec_f->flocp);
997
998 rec_f = rec_f->next;
999 incdep_free_rec (cur, free_me);
1000 }
1001 while (rec_f);
1002}
1003#endif /* PARSE_IN_WORKER */
1004
1005/* Record / issue a warning about a misformed dep file. */
1006static void
1007incdep_warn (struct incdep *cur, unsigned int line_no, const char *msg)
1008{
1009 if (cur->worker_tid == -1)
1010#ifdef INCDEP_USE_KFSCACHE
1011 OSSNS (error,NILF, "%s/%s(%d): %s", cur->pFileObj->pParent->Obj.pszName, cur->pFileObj->pszName, line_no, msg);
1012#else
1013 OSNS (error, NILF, "%s(%d): %s", cur->name, line_no, msg);
1014#endif
1015#ifdef PARSE_IN_WORKER
1016 else
1017 {
1018 cur->err_line_no = line_no;
1019 cur->err_msg = msg;
1020 }
1021#endif
1022}
1023
1024/* Dependency or file strcache allocation / recording. */
1025static const char *
1026incdep_dep_strcache (struct incdep *cur, const char *str, int len)
1027{
1028 const char *ret;
1029 if (cur->worker_tid == -1)
1030 {
1031 /* Make sure the string is terminated before we hand it to
1032 strcache_add_len so it does have to make a temporary copy
1033 of it on the stack. */
1034 char ch = str[len];
1035 ((char *)str)[len] = '\0';
1036 ret = strcache_add_len (str, len);
1037 ((char *)str)[len] = ch;
1038 }
1039 else
1040 {
1041 /* Add it out the strcache of the thread. */
1042 ret = strcache2_add (&incdep_dep_strcaches[cur->worker_tid], str, len);
1043 ret = (const char *)strcache2_get_entry(&incdep_dep_strcaches[cur->worker_tid], ret);
1044 }
1045 return ret;
1046}
1047
1048/* Variable name allocation / recording. */
1049static const char *
1050incdep_var_strcache (struct incdep *cur, const char *str, int len)
1051{
1052 const char *ret;
1053 if (cur->worker_tid == -1)
1054 {
1055 /* XXX: we're leaking this memory now! This will be fixed later. */
1056 ret = xmalloc (len + 1);
1057 memcpy ((char *)ret, str, len);
1058 ((char *)ret)[len] = '\0';
1059 }
1060 else
1061 {
1062 /* Add it out the strcache of the thread. */
1063 ret = strcache2_add (&incdep_var_strcaches[cur->worker_tid], str, len);
1064 ret = (const char *)strcache2_get_entry(&incdep_var_strcaches[cur->worker_tid], ret);
1065 }
1066 return ret;
1067}
1068
1069/* Record / perform a variable definition in a set.
1070 The NAME is in the string cache.
1071 The VALUE is on the heap.
1072 The DUPLICATE_VALUE is always 0. */
1073static void
1074incdep_record_variable_in_set (struct incdep *cur,
1075 const char *name, unsigned int name_length,
1076 const char *value,
1077 unsigned int value_length,
1078 int duplicate_value,
1079 enum variable_origin origin,
1080 int recursive,
1081 struct variable_set *set,
1082 const floc *flocp)
1083{
1084 assert (!duplicate_value);
1085 if (cur->worker_tid == -1)
1086 define_variable_in_set (name, name_length, value, value_length,
1087 duplicate_value, origin, recursive, set, flocp);
1088#ifdef PARSE_IN_WORKER
1089 else
1090 {
1091 struct incdep_variable_in_set *rec =
1092 (struct incdep_variable_in_set *)incdep_alloc_rec (cur);
1093 rec->name_entry = (struct strcache2_entry *)name;
1094 rec->value = value;
1095 rec->value_length = value_length;
1096 rec->duplicate_value = duplicate_value;
1097 rec->origin = origin;
1098 rec->recursive = recursive;
1099 rec->set = set;
1100 rec->flocp = flocp;
1101
1102 rec->next = NULL;
1103 if (cur->recorded_variables_in_set_tail)
1104 cur->recorded_variables_in_set_tail->next = rec;
1105 else
1106 cur->recorded_variables_in_set_head = rec;
1107 cur->recorded_variables_in_set_tail = rec;
1108 }
1109#endif
1110}
1111
1112/* Record / perform a variable definition. The VALUE should be disposed of. */
1113static void
1114incdep_record_variable_def (struct incdep *cur,
1115 const floc *flocp,
1116 const char *name,
1117 unsigned int name_length,
1118 char *value,
1119 unsigned int value_length,
1120 enum variable_origin origin,
1121 enum variable_flavor flavor,
1122 int target_var)
1123{
1124 if (cur->worker_tid == -1)
1125 do_variable_definition_2 (flocp, name, value, value_length, 0, value,
1126 origin, flavor, target_var);
1127#ifdef PARSE_IN_WORKER
1128 else
1129 {
1130 struct incdep_variable_def *rec =
1131 (struct incdep_variable_def *)incdep_alloc_rec (cur);
1132 rec->flocp = flocp;
1133 rec->name_entry = (struct strcache2_entry *)name;
1134 rec->value = value;
1135 rec->value_length = value_length;
1136 rec->origin = origin;
1137 rec->flavor = flavor;
1138 rec->target_var = target_var;
1139
1140 rec->next = NULL;
1141 if (cur->recorded_variable_defs_tail)
1142 cur->recorded_variable_defs_tail->next = rec;
1143 else
1144 cur->recorded_variable_defs_head = rec;
1145 cur->recorded_variable_defs_tail = rec;
1146 }
1147#else
1148 (void)name_length;
1149#endif
1150}
1151
1152/* Similar to record_files in read.c, only much much simpler. */
1153static void
1154incdep_commit_recorded_file (const char *filename, struct dep *deps,
1155 const floc *flocp)
1156{
1157 struct file *f;
1158
1159 /* Perform some validations. */
1160 if (filename[0] == '.'
1161 && ( streq(filename, ".POSIX")
1162 || streq(filename, ".EXPORT_ALL_VARIABLES")
1163 || streq(filename, ".INTERMEDIATE")
1164 || streq(filename, ".LOW_RESOLUTION_TIME")
1165 || streq(filename, ".NOTPARALLEL")
1166 || streq(filename, ".ONESHELL")
1167 || streq(filename, ".PHONY")
1168 || streq(filename, ".PRECIOUS")
1169 || streq(filename, ".SECONDARY")
1170 || streq(filename, ".SECONDTARGETEXPANSION")
1171 || streq(filename, ".SILENT")
1172 || streq(filename, ".SHELLFLAGS")
1173 || streq(filename, ".SUFFIXES")
1174 )
1175 )
1176 {
1177 OS (error, flocp, _("reserved filename '%s' used in dependency file, ignored"), filename);
1178 return;
1179 }
1180
1181 /* Lookup or create an entry in the database. */
1182 f = enter_file (filename);
1183 if (f->double_colon)
1184 {
1185 OS (error, flocp, _("dependency file '%s' has a double colon entry already, ignoring"), filename);
1186 return;
1187 }
1188 f->is_target = 1;
1189
1190 /* Append dependencies. */
1191 deps = enter_prereqs (deps, NULL);
1192 if (deps)
1193 {
1194 struct dep *last = f->deps;
1195 if (!last)
1196 f->deps = deps;
1197 else
1198 {
1199 while (last->next)
1200 last = last->next;
1201 last->next = deps;
1202 }
1203 }
1204}
1205
1206/* Record a file.*/
1207static void
1208incdep_record_file (struct incdep *cur,
1209 const char *filename,
1210 struct dep *deps,
1211 const floc *flocp)
1212{
1213 if (cur->worker_tid == -1)
1214 incdep_commit_recorded_file (filename, deps, flocp);
1215#ifdef PARSE_IN_WORKER
1216 else
1217 {
1218 struct incdep_recorded_file *rec =
1219 (struct incdep_recorded_file *) incdep_alloc_rec (cur);
1220
1221 rec->filename_entry = (struct strcache2_entry *)filename;
1222 rec->deps = deps;
1223 rec->flocp = flocp;
1224
1225 rec->next = NULL;
1226 if (cur->recorded_file_tail)
1227 cur->recorded_file_tail->next = rec;
1228 else
1229 cur->recorded_file_head = rec;
1230 cur->recorded_file_tail = rec;
1231 }
1232#endif
1233}
1234
1235/* Counts slashes backwards from SLASH, stopping at START. */
1236static size_t incdep_count_slashes_backwards(const char *slash, const char *start)
1237{
1238 size_t slashes = 1;
1239 assert (*slash == '\\');
1240 while ((uintptr_t)slash > (uintptr_t)start && slash[0 - slashes] == '\\')
1241 slashes++;
1242 return slashes;
1243}
1244
1245/* Whitespace cannot be escaped at the end of a line, there has to be
1246 some stuff following it other than a line continuation slash.
1247
1248 So, we look ahead and makes sure that there is something non-whitespaced
1249 following this allegedly escaped whitespace.
1250
1251 This code ASSUMES the file content is zero terminated! */
1252static int incdep_verify_escaped_whitespace(const char *ws)
1253{
1254 char ch;
1255
1256 assert(ws[-1] == '\\');
1257 assert(ISBLANK((unsigned int)ws[0]));
1258
1259 /* If the character following the '\ ' sequence is not a whitespace,
1260 another escape character or null terminator, we're good. */
1261 ws += 2;
1262 ch = *ws;
1263 if (ch != '\\' && !ISSPACE((unsigned int)ch) && ch != '\0')
1264 return 1;
1265
1266 /* Otherwise we'll have to parse forward till we hit the end of the
1267 line/file or something. */
1268 while ((ch = *ws++) != '\0')
1269 {
1270 if (ch == '\\')
1271 {
1272 /* escaped newline? */
1273 ch = *ws;
1274 if (ch == '\n')
1275 ws++;
1276 else if (ch == '\r' && ws[1] == '\n')
1277 ws += 2;
1278 else
1279 return 1;
1280 }
1281 else if (ISBLANK((unsigned int)ch))
1282 { /* contine */ }
1283 else if (!ISSPACE((unsigned int)ch))
1284 return 1;
1285 else
1286 return 0; /* newline; all trailing whitespace will be ignored. */
1287 }
1288
1289 return 0;
1290}
1291
1292/* Unescapes the next filename and returns cached copy.
1293
1294 Modifies the input string that START points to.
1295
1296 When NEXTP is not NULL, ASSUME target filename and that END isn't entirely
1297 accurate in case the filename ends with a trailing backslash. There can be
1298 more than one filename in a this case. NEXTP will be set to the first
1299 character after then filename.
1300
1301 When NEXTP is NULL, ASSUME exactly one dependency filename and that END is
1302 accurately deliminating the string.
1303 */
1304static const char *
1305incdep_unescape_and_cache_filename(struct incdep *curdep, char *start, const char *end,
1306 int const is_dep, const char **nextp, unsigned int *linenop)
1307{
1308 unsigned const esc_mask = MAP_BLANK /* ' ' + '\t' */
1309 | MAP_COLON /* ':' */
1310 | MAP_COMMENT /* '#' */
1311 | MAP_EQUALS /* '=' */
1312 | MAP_SEMI /* ';' */
1313 | ( is_dep
1314 ? MAP_PIPE /* '|' */
1315 : MAP_PERCENT); /* '%' */
1316 unsigned const all_esc_mask = esc_mask | MAP_BLANK | MAP_NEWLINE;
1317 unsigned const stop_mask = nextp ? MAP_BLANK | MAP_NEWLINE | (!is_dep ? MAP_COLON : 0) : 0;
1318 char volatile *src;
1319 char volatile *dst;
1320
1321 /*
1322 * Skip forward to the first escaped character so we can avoid unnecessary shifting.
1323 */
1324#if 1
1325 src = start;
1326 dst = start;
1327#elif 1
1328 static const char s_szStop[] = "\n\r\t ";
1329
1330 src = memchr(start, '$', end - start);
1331 dst = memchr(start, '\\', end - start);
1332 if (src && ((uintptr_t)src < (uintptr_t)dst || dst == NULL))
1333 dst = src;
1334 else if (dst && ((uintptr_t)dst < (uintptr_t)src || src == NULL))
1335 src = dst;
1336 else
1337 {
1338 assert(src == NULL && dst == NULL);
1339 if (nextp)
1340 {
1341 int i = sizeof(s_szStop);
1342 while (i-- > 0)
1343 {
1344 char *stop = memchr(start, s_szStop[i], end - start);
1345 if (stop)
1346 end = stop;
1347 }
1348 *nextp = end;
1349 }
1350 return incdep_dep_strcache (curdep, start, end - start);
1351 }
1352 if (nextp)
1353 {
1354 char *stop = src;
1355 int i = sizeof(s_szStop);
1356 while (i-- > 0)
1357 {
1358 char *stop2 = memchr(start, s_szStop[i], stop - start);
1359 if (stop2)
1360 stop = stop2;
1361 }
1362 if (stop != src)
1363 {
1364 *nextp = stop;
1365 return incdep_dep_strcache (curdep, start, stop - start);
1366 }
1367 }
1368#endif
1369
1370 /*
1371 * Copy char-by-char, undoing escaping as we go along.
1372 */
1373 while ((uintptr_t)src < (uintptr_t)end)
1374 {
1375 const char ch = *src++;
1376 if (ch != '\\' && ch != '$')
1377 {
1378 if (!STOP_SET (ch, stop_mask))
1379 *dst++ = ch;
1380 else
1381 {
1382 src--;
1383 break;
1384 }
1385 }
1386 else
1387 {
1388 char ch2 = *src++; /* No bounds checking to handle "/dir/file\ : ..." when end points at " :". */
1389 if (ch == '$')
1390 {
1391 if (ch2 != '$') /* $$ -> $ - Ignores secondary expansion! */
1392 src--;
1393 *dst++ = ch;
1394 }
1395 else
1396 {
1397 unsigned int ch2_map;
1398
1399 /* Eat all the slashes and see what's at the end of them as that's all
1400 that's relevant. If there is an escapable char, we'll emit half of
1401 the slashes. */
1402 size_t const max_slashes = src - start - 1;
1403 size_t slashes = 1;
1404 while (ch2 == '\\')
1405 {
1406 slashes++;
1407 ch2 = *src++;
1408 }
1409
1410 /* Is it escapable? */
1411 ch2_map = stopchar_map[(unsigned char)ch2];
1412 if (ch2_map & all_esc_mask)
1413 {
1414 /* Non-whitespace is simple: Slash slashes, output or stop. */
1415 if (!(ch2_map & (MAP_BLANK | MAP_NEWLINE)))
1416 {
1417 assert(ch2_map & esc_mask);
1418 while (slashes >= 2)
1419 {
1420 *dst++ = '\\';
1421 slashes -= 2;
1422 }
1423 if (slashes || !(stop_mask & ch2_map))
1424 *dst++ = ch2;
1425 else
1426 {
1427 src--;
1428 break;
1429 }
1430 }
1431 /* Escaped blanks or newlines.
1432
1433 We have to pretent that we've already replaced any escaped newlines
1434 and associated whitespace with a single space here. We also have to
1435 pretend trailing whitespace doesn't exist when IS_DEP is non-zero.
1436 This makes for pretty interesting times... */
1437 else
1438 {
1439 char ch3;
1440
1441 /* An Escaped blank is interesting because it is striped unconditionally
1442 at the end of a line, regardless of how many escaped newlines may
1443 following it. We join the escaped newline handling if we fine one
1444 following us. */
1445 if (ch2_map & MAP_BLANK)
1446 {
1447 /* skip whitespace and check for escaped newline. */
1448 volatile char * const src_saved = src;
1449 while ((ch3 = *src) != '\0' && ISBLANK(ch3))
1450 src++;
1451 if (ch3 == '\\' && src[1] == '\n')
1452 src += 2; /* Escaped blank & newline joins into single space. */
1453 else if (ch3 == '\\' && src[1] == '\r' && src[2] == '\n')
1454 src += 3; /* -> Join the escaped newline code below on the next line. */
1455 else if (STOP_SET(ch3, stop_mask & MAP_NEWLINE))
1456 { /* last thing on the line, no blanks to escape. */
1457 while (slashes-- > 0)
1458 *dst++ = '\\';
1459 break;
1460 }
1461 else
1462 {
1463 src = src_saved;
1464 while (slashes >= 2)
1465 {
1466 *dst++ = '\\';
1467 slashes -= 2;
1468 }
1469 if (slashes)
1470 {
1471 *dst++ = ch2;
1472 continue;
1473 }
1474 assert (nextp || (uintptr_t)src >= (uintptr_t)end);
1475 break;
1476 }
1477 }
1478 /* Escaped newlines get special treatment as they an any adjacent whitespace
1479 gets reduced to a single space, including subsequent escaped newlines.
1480 In addition, if this is the final dependency/file and there is no
1481 significant new characters following this escaped newline, the replacement
1482 space will also be stripped and we won't have anything to escape, meaning
1483 that the slashes will remain as is. Finally, none of this space stuff can
1484 be stop characters, unless of course a newline isn't escaped. */
1485 else
1486 {
1487 assert (ch2_map & MAP_NEWLINE);
1488 if (ch2 == '\r' && *src == '\n')
1489 src++;
1490 }
1491
1492 /* common space/newline code */
1493 for (;;)
1494 {
1495 if (linenop)
1496 *linenop += 1;
1497 while ((uintptr_t)src < (uintptr_t)end && ISBLANK(*src))
1498 src++;
1499 if ((uintptr_t)src >= (uintptr_t)end)
1500 {
1501 ch3 = '\0';
1502 break;
1503 }
1504 ch3 = *src;
1505 if (ch3 != '\\')
1506 break;
1507 ch3 = src[1];
1508 if (ch3 == '\n')
1509 src += 2;
1510 else if (ch3 == '\r' && src[2] == '\n')
1511 src += 3;
1512 else
1513 break;
1514 }
1515
1516 if (is_dep && STOP_SET(ch3, stop_mask | MAP_NUL))
1517 { /* last thing on the line, no blanks to escape. */
1518 while (slashes-- > 0)
1519 *dst++ = '\\';
1520 break;
1521 }
1522 while (slashes >= 2)
1523 {
1524 *dst++ = '\\';
1525 slashes -= 2;
1526 }
1527 if (slashes)
1528 *dst++ = ' ';
1529 else
1530 {
1531 assert (nextp || (uintptr_t)src >= (uintptr_t)end);
1532 break;
1533 }
1534 }
1535 }
1536 /* Just output the slash if non-escapable character: */
1537 else
1538 {
1539 while (slashes-- > 0)
1540 *dst++ = '\\';
1541 src--;
1542 }
1543 }
1544 }
1545 }
1546
1547 if (nextp)
1548 *nextp = (const char *)src;
1549 return incdep_dep_strcache(curdep, start, dst - start);
1550}
1551
1552/* no nonsense dependency file including.
1553
1554 Because nobody wants bogus dependency files to break their incremental
1555 builds with hard to comprehend error messages, this function does not
1556 use the normal eval routine but does all the parsing itself. This isn't,
1557 as much work as it sounds, because the necessary feature set is very
1558 limited.
1559
1560 eval_include_dep_file groks:
1561
1562 define var
1563 endef
1564
1565 var [|:|?|>]= value [\]
1566
1567 [\]
1568 file: [deps] [\]
1569
1570 */
1571static void
1572eval_include_dep_file (struct incdep *curdep, floc *f)
1573{
1574 unsigned line_no = 1;
1575 const char *file_end = curdep->file_end;
1576 const char *cur = curdep->file_base;
1577 const char *endp;
1578
1579 /* if no file data, just return immediately. */
1580 if (!cur)
1581 return;
1582
1583 /* now parse the file. */
1584 while ((uintptr_t)cur < (uintptr_t)file_end)
1585 {
1586 /* skip empty lines */
1587 while ((uintptr_t)cur < (uintptr_t)file_end && ISSPACE (*cur) && *cur != '\n')
1588 ++cur;
1589 if ((uintptr_t)cur >= (uintptr_t)file_end)
1590 break;
1591 if (*cur == '#')
1592 {
1593 cur = memchr (cur, '\n', file_end - cur);
1594 if (!cur)
1595 break;
1596 }
1597 if (*cur == '\\')
1598 {
1599 unsigned eol_len = (file_end - cur > 1 && cur[1] == '\n') ? 2
1600 : (file_end - cur > 2 && cur[1] == '\r' && cur[2] == '\n') ? 3
1601 : (file_end - cur == 1) ? 1 : 0;
1602 if (eol_len)
1603 {
1604 cur += eol_len;
1605 line_no++;
1606 continue;
1607 }
1608 }
1609 if (*cur == '\n')
1610 {
1611 cur++;
1612 line_no++;
1613 continue;
1614 }
1615
1616 /* define var
1617 ...
1618 endef */
1619 if (strneq (cur, "define ", 7))
1620 {
1621 const char *var;
1622 unsigned var_len;
1623 const char *value_start;
1624 const char *value_end;
1625 char *value;
1626 unsigned value_len;
1627 int found_endef = 0;
1628
1629 /* extract the variable name. */
1630 cur += 7;
1631 while (ISBLANK (*cur))
1632 ++cur;
1633 value_start = endp = memchr (cur, '\n', file_end - cur);
1634 if (!endp)
1635 endp = cur;
1636 while (endp > cur && ISSPACE (endp[-1]))
1637 --endp;
1638 var_len = endp - cur;
1639 if (!var_len)
1640 {
1641 incdep_warn (curdep, line_no, "bogus define statement.");
1642 break;
1643 }
1644 var = incdep_var_strcache (curdep, cur, var_len);
1645
1646 /* find the end of the variable. */
1647 cur = value_end = value_start = value_start + 1;
1648 ++line_no;
1649 while ((uintptr_t)cur < (uintptr_t)file_end)
1650 {
1651 /* check for endef, don't bother with skipping leading spaces. */
1652 if ( file_end - cur >= 5
1653 && strneq (cur, "endef", 5))
1654 {
1655 endp = cur + 5;
1656 while ((uintptr_t)endp < (uintptr_t)file_end && ISSPACE (*endp) && *endp != '\n')
1657 endp++;
1658 if ((uintptr_t)endp >= (uintptr_t)file_end || *endp == '\n')
1659 {
1660 found_endef = 1;
1661 cur = (uintptr_t)endp >= (uintptr_t)file_end ? file_end : endp + 1;
1662 break;
1663 }
1664 }
1665
1666 /* skip a line ahead. */
1667 cur = value_end = memchr (cur, '\n', file_end - cur);
1668 if (cur != NULL)
1669 ++cur;
1670 else
1671 cur = value_end = file_end;
1672 ++line_no;
1673 }
1674
1675 if (!found_endef)
1676 {
1677 incdep_warn (curdep, line_no, "missing endef, dropping the rest of the file.");
1678 break;
1679 }
1680 value_len = value_end - value_start;
1681 if (memchr (value_start, '\0', value_len))
1682 {
1683 incdep_warn (curdep, line_no, "'\\0' in define, dropping the rest of the file.");
1684 break;
1685 }
1686
1687 /* make a copy of the value, converting \r\n to \n, and define it. */
1688 value = incdep_xmalloc (curdep, value_len + 1);
1689 endp = memchr (value_start, '\r', value_len);
1690 if (endp)
1691 {
1692 const char *src = value_start;
1693 char *dst = value;
1694 for (;;)
1695 {
1696 size_t len = endp - src;
1697 memcpy (dst, src, len);
1698 dst += len;
1699 src = endp;
1700 if ((uintptr_t)src + 1 < (uintptr_t)file_end && src[1] == '\n')
1701 src++; /* skip the '\r' */
1702 if (src >= value_end)
1703 break;
1704 endp = memchr (endp + 1, '\r', src - value_end);
1705 if (!endp)
1706 endp = value_end;
1707 }
1708 value_len = dst - value;
1709 }
1710 else
1711 memcpy (value, value_start, value_len);
1712 value [value_len] = '\0';
1713
1714 incdep_record_variable_in_set (curdep,
1715 var, var_len, value, value_len,
1716 0 /* don't duplicate */, o_file,
1717 0 /* defines are recursive but this is faster */,
1718 NULL /* global set */, f);
1719 }
1720
1721 /* file: deps
1722 OR
1723 variable [:]= value */
1724 else
1725 {
1726 const char *equalp;
1727 const char *eol;
1728
1729 /* Look for a colon or an equal sign. In the assignment case, we
1730 require it to be on the same line as the variable name to simplify
1731 the code. Because of clang, we cannot make the same assumptions
1732 with file dependencies. So, start with the equal. */
1733
1734 assert (*cur != '\n');
1735 eol = memchr (cur, '\n', file_end - cur);
1736 if (!eol)
1737 eol = file_end;
1738 equalp = memchr (cur, '=', eol - cur);
1739 if (equalp && equalp != cur && (ISSPACE(equalp[-1]) || equalp[-1] != '\\'))
1740 {
1741 /* An assignment of some sort. */
1742 const char *var;
1743 unsigned var_len;
1744 const char *value_start;
1745 const char *value_end;
1746 char *value;
1747 unsigned value_len;
1748 unsigned multi_line = 0;
1749 enum variable_flavor flavor;
1750
1751 /* figure the flavor first. */
1752 flavor = f_recursive;
1753 if (equalp > cur)
1754 {
1755 if (equalp[-1] == ':')
1756 flavor = f_simple;
1757 else if (equalp[-1] == '?')
1758 flavor = f_conditional;
1759 else if (equalp[-1] == '+')
1760 flavor = f_append;
1761 else if (equalp[-1] == '>')
1762 flavor = f_prepend;
1763 }
1764
1765 /* extract the variable name. */
1766 endp = flavor == f_recursive ? equalp : equalp - 1;
1767 while (endp > cur && ISBLANK (endp[-1]))
1768 --endp;
1769 var_len = endp - cur;
1770 if (!var_len)
1771 {
1772 incdep_warn (curdep, line_no, "empty variable. (includedep)");
1773 break;
1774 }
1775 if ( memchr (cur, '$', var_len)
1776 || memchr (cur, ' ', var_len)
1777 || memchr (cur, '\t', var_len))
1778 {
1779 incdep_warn (curdep, line_no, "fancy variable name. (includedep)");
1780 break;
1781 }
1782 var = incdep_var_strcache (curdep, cur, var_len);
1783
1784 /* find the start of the value. */
1785 cur = equalp + 1;
1786 while ((uintptr_t)cur < (uintptr_t)file_end && ISBLANK (*cur))
1787 cur++;
1788 value_start = cur;
1789
1790 /* find the end of the value / line (this isn't 101% correct). */
1791 value_end = cur;
1792 while ((uintptr_t)cur < (uintptr_t)file_end)
1793 {
1794 endp = value_end = memchr (cur, '\n', file_end - cur);
1795 if (!value_end)
1796 value_end = file_end;
1797 if (value_end - 1 >= cur && value_end[-1] == '\r')
1798 --value_end;
1799 if (value_end - 1 < cur || value_end[-1] != '\\')
1800 {
1801 cur = endp ? endp + 1 : file_end;
1802 break;
1803 }
1804 --value_end;
1805 if (value_end - 1 >= cur && value_end[-1] == '\\')
1806 {
1807 incdep_warn (curdep, line_no, "fancy escaping! (includedep)");
1808 cur = NULL;
1809 break;
1810 }
1811 if (!endp)
1812 {
1813 cur = file_end;
1814 break;
1815 }
1816
1817 cur = endp + 1;
1818 ++multi_line;
1819 ++line_no;
1820 }
1821 if (!cur)
1822 break;
1823 ++line_no;
1824
1825 /* make a copy of the value, converting \r\n to \n, and define it. */
1826 value_len = value_end - value_start;
1827 value = incdep_xmalloc (curdep, value_len + 1);
1828 if (!multi_line)
1829 memcpy (value, value_start, value_len);
1830 else
1831 {
1832 /* unescape it */
1833 const char *src = value_start;
1834 char *dst = value;
1835 while (src < value_end)
1836 {
1837 const char *nextp;
1838
1839 endp = memchr (src, '\n', value_end - src);
1840 if (!endp)
1841 nextp = endp = value_end;
1842 else
1843 nextp = endp + 1;
1844 if (endp > src && endp[-1] == '\r')
1845 --endp;
1846 if (endp > src && endp[-1] == '\\')
1847 --endp;
1848
1849 if (src != value_start)
1850 *dst++ = ' ';
1851 memcpy (dst, src, endp - src);
1852 dst += endp - src;
1853 src = nextp;
1854 }
1855 value_len = dst - value;
1856 }
1857 value [value_len] = '\0';
1858
1859 /* do the definition */
1860 if (flavor == f_recursive
1861 || ( flavor == f_simple
1862 && !memchr (value, '$', value_len)))
1863 incdep_record_variable_in_set (curdep,
1864 var, var_len, value, value_len,
1865 0 /* don't duplicate */, o_file,
1866 flavor == f_recursive /* recursive */,
1867 NULL /* global set */, f);
1868 else
1869 incdep_record_variable_def (curdep,
1870 f, var, var_len, value, value_len,
1871 o_file, flavor, 0 /* not target var */);
1872 }
1873 else
1874 {
1875 /* Expecting: file: dependencies */
1876
1877 int unescape_filename = 0;
1878 const char *filename;
1879 const char *fnnext;
1880 const char *fnend;
1881 const char *colonp;
1882 struct dep *deps = 0;
1883 struct dep **nextdep = &deps;
1884 struct dep *dep;
1885
1886
1887 /* Locate the next file colon. If it's not within the bounds of
1888 the current line, check that all new line chars are escaped. */
1889
1890 colonp = memchr (cur, ':', file_end - cur);
1891 while ( colonp
1892 && ( ( colonp != cur
1893 && colonp[-1] == '\\'
1894 && incdep_count_slashes_backwards (&colonp[-1], cur) & 1)
1895#ifdef HAVE_DOS_PATHS
1896 || ( colonp + 1 < file_end
1897 && (colonp[1] == '/' || colonp[1] == '\\')
1898 && colonp > cur
1899 && isalpha ((unsigned char)colonp[-1])
1900 && ( colonp == cur + 1
1901 || ISBLANK ((unsigned char)colonp[-2])))
1902#endif
1903 )
1904 )
1905 colonp = memchr (colonp + 1, ':', file_end - (colonp + 1));
1906 if (!colonp)
1907 {
1908 incdep_warn (curdep, line_no, "no colon.");
1909 break;
1910 }
1911
1912 if ((uintptr_t)colonp < (uintptr_t)eol)
1913 unescape_filename = memchr (cur, '\\', colonp - cur) != NULL
1914 || memchr (cur, '$', colonp - cur) != NULL;
1915 else if (memchr (eol, '=', colonp - eol))
1916 {
1917 incdep_warn (curdep, line_no, "multi line assignment / dependency confusion.");
1918 break;
1919 }
1920 else
1921 {
1922 const char *sol = cur;
1923 do
1924 {
1925 char *eol2 = (char *)eol - 1;
1926 if ((uintptr_t)eol2 >= (uintptr_t)sol && *eol2 == '\r') /* DOS line endings. */
1927 eol2--;
1928 if ((uintptr_t)eol2 < (uintptr_t)sol || *eol2 != '\\')
1929 incdep_warn (curdep, line_no, "no colon.");
1930 else if (eol2 != sol && eol2[-1] == '\\')
1931 incdep_warn (curdep, line_no, "fancy EOL escape. (includedep)");
1932 else
1933 {
1934 line_no++;
1935 sol = eol + 1;
1936 eol = memchr (sol, '\n', colonp - sol);
1937 continue;
1938 }
1939 sol = NULL;
1940 break;
1941 }
1942 while (eol != NULL);
1943 if (!sol)
1944 break;
1945 unescape_filename = 1;
1946 }
1947
1948 /* Extract the first filename after trimming and basic checks. */
1949 fnend = colonp;
1950 while ((uintptr_t)fnend > (uintptr_t)cur && ISBLANK (fnend[-1]))
1951 --fnend;
1952 if (cur == fnend)
1953 {
1954 incdep_warn (curdep, line_no, "empty filename.");
1955 break;
1956 }
1957 fnnext = cur;
1958 if (!unescape_filename)
1959 {
1960 while (fnnext != fnend && !ISBLANK (*fnnext))
1961 fnnext++;
1962 filename = incdep_dep_strcache (curdep, cur, fnnext - cur);
1963 }
1964 else
1965 filename = incdep_unescape_and_cache_filename (curdep, (char *)fnnext, fnend, 0, &fnnext, NULL);
1966
1967 /* parse any dependencies. */
1968 cur = colonp + 1;
1969 while ((uintptr_t)cur < (uintptr_t)file_end)
1970 {
1971 const char *dep_file;
1972
1973 /* skip blanks and count lines. */
1974 char ch = 0;
1975 while ((uintptr_t)cur < (uintptr_t)file_end && ISSPACE ((ch = *cur)) && ch != '\n')
1976 ++cur;
1977 if ((uintptr_t)cur >= (uintptr_t)file_end)
1978 break;
1979 if (ch == '\n')
1980 {
1981 cur++;
1982 line_no++;
1983 break;
1984 }
1985
1986 /* continuation + eol? */
1987 if (ch == '\\')
1988 {
1989 unsigned eol_len = (file_end - cur > 1 && cur[1] == '\n') ? 2
1990 : (file_end - cur > 2 && cur[1] == '\r' && cur[2] == '\n') ? 3
1991 : (file_end - cur == 1) ? 1 : 0;
1992 if (eol_len)
1993 {
1994 cur += eol_len;
1995 line_no++;
1996 continue;
1997 }
1998 }
1999
2000 /* find the end of the filename and cache it */
2001 dep_file = NULL;
2002 endp = cur;
2003 for (;;)
2004 if ((uintptr_t)endp < (uintptr_t)file_end)
2005 {
2006 ch = *endp;
2007 if (ch != '\\' && ch != '$' )
2008 {
2009 if (!ISSPACE (ch))
2010 endp++;
2011 else
2012 {
2013 dep_file = incdep_dep_strcache(curdep, cur, endp - cur);
2014 break;
2015 }
2016 }
2017 else
2018 {
2019 /* potential escape sequence, let the unescaper do the rest. */
2020 dep_file = incdep_unescape_and_cache_filename (curdep, (char *)cur, file_end, 1, &endp, &line_no);
2021 break;
2022 }
2023 }
2024 else
2025 {
2026 dep_file = incdep_dep_strcache(curdep, cur, endp - cur);
2027 break;
2028 }
2029
2030 /* add it to the list. */
2031 *nextdep = dep = incdep_alloc_dep (curdep);
2032 dep->includedep = 1;
2033 dep->name = dep_file;
2034 nextdep = &dep->next;
2035
2036 cur = endp;
2037 }
2038
2039 /* enter the file with its dependencies. */
2040 incdep_record_file (curdep, filename, deps, f);
2041
2042 /* More files? Record them with the same dependency list. */
2043 if ((uintptr_t)fnnext < (uintptr_t)fnend)
2044 for (;;)
2045 {
2046 const char *filename_prev = filename;
2047 while (fnnext != fnend && ISBLANK (*fnnext))
2048 fnnext++;
2049 if (fnnext == fnend)
2050 break;
2051 if (*fnnext == '\\')
2052 {
2053 if (fnnext[1] == '\n')
2054 {
2055 line_no++;
2056 fnnext += 2;
2057 continue;
2058 }
2059 if (fnnext[1] == '\r' && fnnext[2] == '\n')
2060 {
2061 line_no++;
2062 fnnext += 3;
2063 continue;
2064 }
2065 }
2066
2067 if (!unescape_filename)
2068 {
2069 const char *fnstart = fnnext;
2070 while (fnnext != fnend && !ISBLANK (*fnnext))
2071 fnnext++;
2072 filename = incdep_dep_strcache (curdep, fnstart, fnnext - fnstart);
2073 }
2074 else
2075 filename = incdep_unescape_and_cache_filename (curdep, (char *)fnnext, fnend, 0, &fnnext, NULL);
2076 if (filename != filename_prev) /* clang optimization. */
2077 incdep_record_file (curdep, filename, incdep_dup_dep_list (curdep, deps), f);
2078 }
2079 }
2080 }
2081 }
2082
2083 /* free the file data */
2084 incdep_xfree (curdep, curdep->file_base);
2085 curdep->file_base = curdep->file_end = NULL;
2086}
2087
2088/* Flushes the incdep todo and done lists. */
2089static void
2090incdep_flush_it (floc *f)
2091{
2092 incdep_lock ();
2093 for (;;)
2094 {
2095 struct incdep *cur = incdep_head_done;
2096
2097 /* if the done list is empty, grab a todo list entry. */
2098 if (!cur && incdep_head_todo)
2099 {
2100 cur = incdep_head_todo;
2101 if (cur->next)
2102 incdep_head_todo = cur->next;
2103 else
2104 incdep_head_todo = incdep_tail_todo = NULL;
2105 incdep_unlock ();
2106
2107 incdep_read_file (cur, f);
2108 eval_include_dep_file (cur, f);
2109 incdep_freeit (cur);
2110
2111 incdep_lock ();
2112 continue;
2113 }
2114
2115 /* if the todo list and done list are empty we're either done
2116 or will have to wait for the thread(s) to finish. */
2117 if (!cur && !incdep_num_reading)
2118 break; /* done */
2119 if (!cur)
2120 {
2121 while (!incdep_head_done)
2122 incdep_wait_done ();
2123 cur = incdep_head_done;
2124 }
2125
2126 /* we grab the entire done list and work thru it. */
2127 incdep_head_done = incdep_tail_done = NULL;
2128 incdep_unlock ();
2129
2130 while (cur)
2131 {
2132 struct incdep *next = cur->next;
2133#ifdef PARSE_IN_WORKER
2134 incdep_flush_recorded_instructions (cur);
2135#else
2136 eval_include_dep_file (cur, f);
2137#endif
2138 incdep_freeit (cur);
2139 cur = next;
2140 }
2141
2142 incdep_lock ();
2143 } /* outer loop */
2144 incdep_unlock ();
2145}
2146
2147
2148/* splits up a list of file names and feeds it to eval_include_dep_file,
2149 employing threads to try speed up the file reading. */
2150void
2151eval_include_dep (const char *names, floc *f, enum incdep_op op)
2152{
2153 struct incdep *head = 0;
2154 struct incdep *tail = 0;
2155 struct incdep *cur;
2156 const char *names_iterator = names;
2157 const char *name;
2158 unsigned int name_len;
2159
2160 /* loop through NAMES, creating a todo list out of them. */
2161
2162 while ((name = find_next_token (&names_iterator, &name_len)) != 0)
2163 {
2164#ifdef INCDEP_USE_KFSCACHE
2165 KFSLOOKUPERROR enmError;
2166 PKFSOBJ pFileObj = kFsCacheLookupWithLengthA (g_pFsCache, name, name_len, &enmError);
2167 if (!pFileObj)
2168 continue;
2169 if (pFileObj->bObjType != KFSOBJ_TYPE_FILE)
2170 {
2171 kFsCacheObjRelease (g_pFsCache, pFileObj);
2172 continue;
2173 }
2174
2175 cur = xmalloc (sizeof (*cur)); /* not incdep_xmalloc here */
2176 cur->pFileObj = pFileObj;
2177#else
2178 cur = xmalloc (sizeof (*cur) + name_len); /* not incdep_xmalloc here */
2179 memcpy (cur->name, name, name_len);
2180 cur->name[name_len] = '\0';
2181#endif
2182
2183 cur->file_base = cur->file_end = NULL;
2184 cur->worker_tid = -1;
2185#ifdef PARSE_IN_WORKER
2186 cur->err_line_no = 0;
2187 cur->err_msg = NULL;
2188 cur->recorded_variables_in_set_head = NULL;
2189 cur->recorded_variables_in_set_tail = NULL;
2190 cur->recorded_variable_defs_head = NULL;
2191 cur->recorded_variable_defs_tail = NULL;
2192 cur->recorded_file_head = NULL;
2193 cur->recorded_file_tail = NULL;
2194#endif
2195
2196 cur->next = NULL;
2197 if (tail)
2198 tail->next = cur;
2199 else
2200 head = cur;
2201 tail = cur;
2202 }
2203
2204#ifdef ELECTRIC_HEAP
2205 if (1)
2206#else
2207 if (op == incdep_read_it)
2208#endif
2209 {
2210 /* work our way thru the files directly */
2211
2212 cur = head;
2213 while (cur)
2214 {
2215 struct incdep *next = cur->next;
2216 incdep_read_file (cur, f);
2217 eval_include_dep_file (cur, f);
2218 incdep_freeit (cur);
2219 cur = next;
2220 }
2221 }
2222 else
2223 {
2224 /* initialize the worker threads and related stuff the first time around. */
2225
2226 if (!incdep_initialized)
2227 incdep_init (f);
2228
2229 /* queue the files and notify the worker threads. */
2230
2231 incdep_lock ();
2232
2233 if (incdep_tail_todo)
2234 incdep_tail_todo->next = head;
2235 else
2236 incdep_head_todo = head;
2237 incdep_tail_todo = tail;
2238
2239 incdep_signal_todo ();
2240 incdep_unlock ();
2241
2242 /* flush the todo queue if we're requested to do so. */
2243
2244 if (op == incdep_flush)
2245 incdep_flush_it (f);
2246 }
2247}
2248
2249#endif /* CONFIG_WITH_INCLUDEDEP */
2250
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette