Bug Summary

File:dmake/make.c
Location:line 1726, column 7
Description:Access to field 'string' results in a dereference of a null pointer (loaded from variable 'key')

Annotated Source Code

1/*
2--
3-- SYNOPSIS
4-- Perform the update of all outdated targets.
5--
6-- DESCRIPTION
7-- This is where we traverse the make graph looking for targets that
8-- are out of date, and we try to infer how to make them if we can.
9-- The usual make macros are understood, as well as some new ones:
10--
11-- $$ - expands to $
12-- $@ - full target name
13-- $* - target name with no suffix, same as $(@:db)
14-- or, the value of % in % meta rule recipes
15-- $? - list of out of date prerequisites
16-- $< - all prerequisites associated with rules line
17-- $& - all prerequisites associated with target
18-- $> - library name for target (if any)
19-- $^ - out of date prerequisites taken from value of $<
20--
21-- AUTHOR
22-- Dennis Vadura, dvadura@dmake.wticorp.com
23--
24-- WWW
25-- http://dmake.wticorp.com/
26--
27-- COPYRIGHT
28-- Copyright (c) 1996,1997 by WTI Corp. All rights reserved.
29--
30-- This program is NOT free software; you can redistribute it and/or
31-- modify it under the terms of the Software License Agreement Provided
32-- in the file <distribution-root>/readme/license.txt.
33--
34-- LOG
35-- Use cvs log to obtain detailed change logs.
36*/
37
38#include "extern.h"
39#include "sysintf.h"
40
41typedef struct cell {
42 char *datum;
43 struct cell *next;
44 size_t len;
45} LISTCELL, *LISTCELLPTR;
46
47typedef struct {
48 LISTCELLPTR first;
49 LISTCELLPTR last;
50 size_t len;
51} LISTSTRING, *LISTSTRINGPTR;
52
53
54static void _drop_mac ANSI((HASHPTR))(HASHPTR);
55static void _set_recipe ANSI((char*, int))(char*, int);
56static void _set_tmd ANSI(())();
57static void _append_file ANSI((STRINGPTR, FILE*, char*, int))(STRINGPTR, FILE*, char*, int);
58static LINKPTR _dup_prq ANSI((LINKPTR))(LINKPTR);
59static LINKPTR _expand_dynamic_prq ANSI(( LINKPTR, LINKPTR, char * ))( LINKPTR, LINKPTR, char * );
60static char* _prefix ANSI((char *, char *))(char *, char *);
61static char* _pool_lookup ANSI((char *))(char *);
62static int _explode_graph ANSI((CELLPTR, LINKPTR, CELLPTR))(CELLPTR, LINKPTR, CELLPTR);
63
64
65#define RP_GPPROLOG0 0
66#define RP_RECIPE1 1
67#define RP_GPEPILOG2 2
68#define NUM_RECIPES3 3
69
70static STRINGPTR _recipes[NUM_RECIPES3];
71static LISTCELLPTR _freelist=NULL((void*)0);
72
73static LISTCELLPTR
74get_cell()
75{
76 LISTCELLPTR cell;
77
78 if (!_freelist) {
79 if ((cell=MALLOC(1,LISTCELL)(LISTCELL*) malloc((unsigned int)(1)*(size_t)sizeof(LISTCELL)
)
) == NULL((void*)0))
80 No_ram();
81 }
82 else {
83 cell = _freelist;
84 _freelist = cell->next;
85 }
86
87 return(cell);
88}
89
90
91static void
92free_cell(LISTCELLPTR cell)
93{
94 cell->next = _freelist;
95 _freelist = cell;
96}
97
98
99static void
100free_list(LISTCELLPTR c)
101{
102 if(c) {
103 free_list(c->next);
104 free_cell(c);
105 }
106}
107
108
109static void
110list_init(LISTSTRINGPTR s)
111{
112 s->first = NULL((void*)0);
113 s->last = NULL((void*)0);
114 s->len = 0;
115}
116
117
118static void
119list_add(LISTSTRINGPTR s, char *str)
120{
121 LISTCELLPTR p;
122 int l;
123
124 if ((l = strlen(str)) == 0)
125 return;
126
127 p = get_cell();
128 p->datum = str;
129 p->next = NULL((void*)0);
130 p->len = l;
131
132 if(s->first == NULL((void*)0))
133 s->first = p;
134 else
135 s->last->next = p;
136
137 s->last = p;
138 s->len += l+1;
139}
140
141
142static char *
143gen_path_list_string(LISTSTRINGPTR s)/*
144=======================================
145 Take a list of filepaths and create a string from it separating
146 the filenames by a space.
147 This function honors the cygwin specific .WINPATH attribute. */
148{
149 LISTCELLPTR next, cell;
150 int len;
151 int slen, slen_rest;
152 char *result;
153 char *p, *tpath;
154
155 if( (slen_rest = slen = s->len) == 0)
156 return(NIL(char)((char*)((void*)0)));
157
158 /* reserve enough space to hold the concated original filenames. */
159 if((p = result = MALLOC(slen, char)(char*) malloc((unsigned int)(slen)*(size_t)sizeof(char))) == NULL((void*)0)) No_ram();
160
161 for (cell=s->first; cell; cell=next) {
162#if !defined(__CYGWIN__)
163 tpath = cell->datum;
164 len=cell->len;
165#else
166 /* For cygwin with .WINPATH set the lenght of the converted
167 * filepaths might be longer. Extra checking is needed ... */
168 tpath = DO_WINPATH(cell->datum)cell->datum;
169 if( tpath == cell->datum ) {
170 len=cell->len;
171 }
172 else {
173 /* ... but only if DO_WINPATH() did something. */
174 len = strlen(tpath);
175 }
176 if( len >= slen_rest ) {
177 /* We need more memory. As DOS paths are usually shorter than the
178 * original cygwin POSIX paths (exception mounted paths) this should
179 * rarely happen. */
180 int p_offset = p - result;
181 /* Get more than needed. */
182 slen = slen + len - slen_rest + 128;
183 if((result = realloc( result, slen ) ) == NULL((void*)0))
184 No_ram();
185 p = result + p_offset;
186 }
187#endif
188
189 memcpy((void *)p, (void *)tpath, len);
190 p += len;
191 *p++ = ' ';
192
193#if defined(__CYGWIN__)
194 /* slen_rest is only used in the cygwin / .WINPATH case. */
195 slen_rest = slen - (p - result);
196#endif
197
198 next = cell->next;
199 free_cell(cell);
200 }
201
202 *--p = '\0';
203 list_init(s);
204
205 return(result);
206}
207
208
209PUBLIC int
210Make_targets()/*
211================
212 Actually go and make the targets on the target list */
213{
214 LINKPTR lp;
215 int done = 0;
216
217 DB_ENTER( "Make_targets" );
218
219 Read_state();
220 _set_recipe( ".GROUPPROLOG", RP_GPPROLOG0 );
221 _set_recipe( ".GROUPEPILOG", RP_GPEPILOG2 );
222
223 /* Prevent recipe inference for .ROOT */
224 if ( Root->ce_recipe == NIL(STRING)((STRING*)((void*)0)) ) {
225 TALLOC( Root->ce_recipe, 1, STRING )if ((Root->ce_recipe = (STRING*) calloc((unsigned int)(1),
(size_t)sizeof(STRING))) == (STRING*)0) {No_ram();}
;
226 Root->ce_recipe->st_string = "";
227 }
228
229 /* Prevent recipe inference for .TARGETS */
230 if ( Targets->ce_recipe == NIL(STRING)((STRING*)((void*)0)) ) {
231 TALLOC( Targets->ce_recipe, 1, STRING )if ((Targets->ce_recipe = (STRING*) calloc((unsigned int)(
1), (size_t)sizeof(STRING))) == (STRING*)0) {No_ram();}
;
232 Targets->ce_recipe->st_string = "";
233 }
234
235 /* Make sure that user defined targets are marked as root targets */
236 for( lp = Targets->ce_prq; lp != NIL(LINK)((LINK*)((void*)0)); lp = lp->cl_next )
237 lp->cl_prq->ce_attr |= A_ROOT0x01000000;
238
239 while( !done ) {
240 int rval;
241
242 if( (rval = Make(Root, NIL(CELL)((CELL*)((void*)0)))) == -1 )
243 DB_RETURN(1)return (1);
244 else
245 done = Root->ce_flag & F_MADE0x8000;
246
247 if( !rval && !done ) Wait_for_child( FALSE0, -1 );
248 }
249
250 for( lp = Targets->ce_prq; lp != NIL(LINK)((LINK*)((void*)0)); lp = lp->cl_next ) {
251 CELLPTR tgt = lp->cl_prq;
252 if( !(tgt->ce_attr & A_UPDATED0x800000)
253 && (Verbose & V_MAKE0x10) )
254 printf( "`%s' is up to date\n", tgt->CE_NAMEce_name->ht_name );
255 }
256
257 DB_RETURN( 0 )return (0);
258}
259
260
261
262PUBLIC int
263Make( cp, setdirroot )/*
264========================
265 Make target cp. Make() is also called on prerequisites that have no rule
266 associated (F_TARGET is not set) to verify that they exist. */
267CELLPTR cp;
268CELLPTR setdirroot;
269{
270 register LINKPTR dp, prev,next;
271 register CELLPTR tcp;
272 CELLPTR nsetdirroot;
273 char *name, *lib;
274 HASHPTR m_at, m_q, m_b, m_g, m_l, m_bb, m_up;
275 LISTSTRING all_list, imm_list, outall_list, inf_list;
276 char *all = NIL(char)((char*)((void*)0));
277 char *inf = NIL(char)((char*)((void*)0));
278 char *outall = NIL(char)((char*)((void*)0));
279 char *imm = NIL(char)((char*)((void*)0));
280 int rval = 0; /* 0==ready, 1==target still running, -1==error */
281 int push = 0;
282 int made = F_MADE0x8000;
283 int ignore;
284 time_t otime = (time_t) 1L; /* Hold time of newest prerequisite. */
285 int mark_made = FALSE0;
286
287#if defined(__CYGWIN__)
288 /* static variable to hold .WINPATH status of previously made target.
289 * 0, 1 are .WINPATH states, -1 indicates the first target. */
290 static int prev_winpath_attr = -1;
291#endif
292
293 DB_ENTER( "Make" );
294 DB_PRINT( "mem", ("%s:-> mem %ld", cp->CE_NAME, (long) coreleft()) );
295
296 /* Initialize the various temporary storage */
297 m_q = m_b = m_g = m_l = m_bb = m_up = NIL(HASH)((HASH*)((void*)0));
298 list_init(&all_list);
299 list_init(&imm_list);
300 list_init(&outall_list);
301 list_init(&inf_list);
302
303 if (cp->ce_set && cp->ce_set != cp) {
304 if( Verbose & V_MAKE0x10 )
305 printf( "%s: Building .UPDATEALL representative [%s]\n", Pname,
306 cp->ce_set->CE_NAMEce_name->ht_name );
307 cp = cp->ce_set;
308 }
309
310 /* If we are supposed to change directories for this target then do so.
311 * If we do change dir, then modify the setdirroot variable to reflect
312 * that fact for all of the prerequisites that we will be making. */
313
314 nsetdirroot = setdirroot;
315 ignore = (((cp->ce_attr|Glob_attr)&A_IGNORE0x00020) != 0);
316
317 /* Set the UseWinpath variable to reflect the (global/local) .WINPATH
318 * attribute. The variable is used by DO_WINPATH() and in some other
319 * places. */
320 UseWinpath = (((cp->ce_attr|Glob_attr)&A_WINPATH0x01000) != 0);
321
322 /* m_at needs to be defined before going to a "stop_making_it" where
323 * a _drop_mac( m_at ) would try to free it. */
324 /* FIXME: m_at can most probably not be changed before the next
325 * Def_macro("@", ...) command. Check if both this and the next
326 * call are needed. */
327 m_at = Def_macro("@", DO_WINPATH(cp->ce_fname)cp->ce_fname, M_MULTI0x0004);
328
329 if( cp->ce_attr & A_SETDIR0x00400 ) {
330 /* Change directory only if the previous .SETDIR is a different
331 * directory from the current one. ie. all cells with the same .SETDIR
332 * attribute are assumed to come from the same directory. */
333
334 if( (setdirroot == NIL(CELL)((CELL*)((void*)0)) || setdirroot->ce_dir != cp->ce_dir) &&
335 (push = Push_dir(cp->ce_dir,cp->CE_NAMEce_name->ht_name,ignore)) != 0 )
336 setdirroot = cp;
337 }
338
339 DB_PRINT( "mem", ("%s:-A mem %ld", cp->CE_NAME, (long) coreleft()) );
340
341 /* FIXME: F_MULTI targets don't have cp->ce_recipe set but the recipes
342 * are known nevertheless. It is not necessary to infer them.
343 * If (cp->ce_flag & F_MULTI) is true the recipes of the corresponding
344 * subtargets can be used. */
345 if( cp->ce_recipe == NIL(STRING)((STRING*)((void*)0)) ) {
346 char *dir = cp->ce_dir;
347
348 if( Verbose & V_MAKE0x10 )
349 printf( "%s: Infering prerequisite(s) and recipe for [%s]\n", Pname,
350 cp->CE_NAMEce_name->ht_name );
351
352 Infer_recipe( cp, setdirroot );
353
354 /* See if the directory has changed, if it has then make sure we
355 * push it. */
356 if( dir != cp->ce_dir ) {
357 if( push ) Pop_dir(FALSE0);
358 push = Push_dir( cp->ce_dir, cp->CE_NAMEce_name->ht_name, ignore );
359 setdirroot = cp;
360 }
361 }
362
363 for(dp=CeMeToo(cp)&((cp)->ce_all); dp; dp=dp->cl_next) {
364 tcp = dp->cl_prq;
365 if( push ) {
366 /* If we changed the directory because of .SETDIR write Pwd into
367 * tcp->ce_dir so that it holds an absolute path. */
368 if( !(tcp->ce_attr & A_POOL0x08000000) && tcp->ce_dir ) FREE( tcp->ce_dir )free((char*)(tcp->ce_dir));
369 tcp->ce_dir = _pool_lookup(Pwd);
370 tcp->ce_attr |= A_SETDIR0x00400|A_POOL0x08000000;
371 }
372 tcp->ce_setdir = nsetdirroot;
373 }
374
375 DB_PRINT( "mem", ("%s:-A mem %ld", cp->CE_NAME, (long) coreleft()) );
376 /* If we have not yet statted the target then do so. */
377 if( !(cp->ce_flag & F_STAT0x0040) && !(cp->ce_attr&A_PHONY0x04000) ) {
378 if (cp->ce_parent && (cp->ce_parent->ce_flag & F_MULTI0x0002)) {
379 /* Inherit the stat info from the F_MULTI parent. */
380 cp->ce_time = cp->ce_parent->ce_time;
381 cp->ce_flag |= F_STAT0x0040;
382 /* Propagate the A_PRECIOUS attribute from the parent. */
383 cp->ce_attr |= cp->ce_parent->ce_attr & A_PRECIOUS0x00001;
384 }
385 else {
386 for(dp=CeMeToo(cp)&((cp)->ce_all); dp; dp=dp->cl_next) {
387 tcp = dp->cl_prq;
388 /* Check if target already exists. */
389 Stat_target( tcp, 1, FALSE0 );
390
391 if( tcp->ce_time != (time_t)0L ) {
392 /* File exists so don't remove it later. */
393 tcp->ce_attr |= A_PRECIOUS0x00001;
394 }
395
396 if( Verbose & V_MAKE0x10 )
397 printf("%s: Time stamp of [%s] is %ld\n",Pname,tcp->CE_NAMEce_name->ht_name,
398 tcp->ce_time);
399 }
400 }
401 }
402
403 DB_PRINT( "make", ("(%s, %ld, 0x%08x, 0x%04x)", cp->CE_NAME,
404 cp->ce_time, cp->ce_attr, cp->ce_flag) );
405
406 /* Handle targets without rule and without existing file. */
407 if( !(cp->ce_flag & F_TARGET0x0008) && (cp->ce_time == (time_t) 0L) ) {
408 if( Makemkf ) {
409 rval = -1;
410 goto stop_making_it;
411 }
412 else if( cp->ce_prq != NIL(LINK)((LINK*)((void*)0))
413 || (STOBOOL(Augmake)(Augmake && ((*Augmake | 0x20) == 'y')) && (cp->ce_flag&F_EXPLICIT0x0400)))
414 /* Assume an empty recipe for a target that we have run inference on
415 * but do not have a set of rules for but for which we have inferred
416 * a list of prerequisites. */
417 cp->ce_flag |= F_RULES0x0010;
418 else
419 Fatal( "`%s' not found, and can't be made", cp->CE_NAMEce_name->ht_name );
420 }
421
422 DB_PRINT( "mem", ("%s:-A mem %ld", cp->CE_NAME, (long) coreleft()) );
423
424 /* set value of $* if we have not infered a recipe, in this case $* is
425 * the same as $(@:db), this allows us to be compatible with BSD make */
426 if( cp->ce_per == NIL(char)((char*)((void*)0)) ) cp->ce_per = "$(@:db)";
427
428 /* Search the prerequisite list for dynamic prerequisites and if we find
429 * them copy the list of prerequisites for potential later re-use. */
430 if ( cp->ce_prqorg == NIL(LINK)((LINK*)((void*)0)) ) {
431 for( dp = cp->ce_prq; dp != NIL(LINK)((LINK*)((void*)0)); dp = dp->cl_next )
432 if ( strchr(dp->cl_prq->CE_NAMEce_name->ht_name, '$') != NULL((void*)0) )
433 break;
434
435 if (dp != NIL(LINK)((LINK*)((void*)0))) {
436 cp->ce_prqorg = _dup_prq(cp->ce_prq);
437 }
438 }
439
440 /* Define $@ macro. The only reason for defining it here (that I see ATM)
441 * is that $@ is already defined in conditional macros. */
442 /* FIXME: check if both this and the previous Def_macro("@", ...) call
443 * are needed. */
444 m_at = Def_macro("@", DO_WINPATH(cp->ce_fname)cp->ce_fname, M_MULTI0x0004);
445
446 /* Define conditional macros if any, note this is done BEFORE we process
447 * prerequisites for the current target. Thus the making of a prerequisite
448 * is done using the current value of the conditional macro. */
449 for(dp=CeMeToo(cp)&((cp)->ce_all); dp; dp=dp->cl_next) {
450 tcp=dp->cl_prq;
451 if (tcp->ce_cond != NIL(STRING)((STRING*)((void*)0))) {
452 STRINGPTR sp;
453
454 tcp->ce_pushed = NIL(HASH)((HASH*)((void*)0));
455 for(sp=tcp->ce_cond; sp; sp=sp->st_next) {
456 if(Parse_macro(sp->st_string,M_MULTI0x0004|M_PUSH0x0100)) {
457 HASHPTR hp;
458
459 hp = GET_MACRO(LastMacName)Get_name(LastMacName, Macs, 0);
460 hp->ht_link = tcp->ce_pushed;
461 tcp->ce_pushed = hp;
462 }
463 else {
464 Error("Invalid conditional macro expression [%s]",sp->st_string);
465 }
466 }
467 }
468 }
469
470 /* First round, will be repeated a second time below. */
471 for( prev=NULL((void*)0),dp=cp->ce_prq; dp != NIL(LINK)((LINK*)((void*)0)); prev=dp, dp=next ) {
472 int seq;
473
474 /* This loop executes Make() to build prerequisites if needed.
475 * The only macro that needs to be reset after a Make() was executed
476 * is $@ as it might be used when expanding potential dynamic
477 * prerequisites. As UseWinpath is a global variable we also
478 * need to restore it. */
479 if (m_at->ht_value == NIL(char)((char*)((void*)0))) {
480 /* This check effectively tests if Make() was run before because
481 * Make() frees all dynamic macro values at the end. */
482 UseWinpath = (((cp->ce_attr|Glob_attr)&A_WINPATH0x01000) != 0);
483 m_at = Def_macro("@", DO_WINPATH(cp->ce_fname)cp->ce_fname, M_MULTI0x0004);
484 }
485
486 /* Make the prerequisite, note that if the current target has the
487 * .LIBRARY attribute set we pass on to the prerequisite the .LIBRARYM
488 * attribute and pass on the name of the current target as the library
489 * name, and we take it away when we are done. */
490 next = dp->cl_next;
491
492 tcp = dp->cl_prq;
493 if( Verbose & V_MAKE0x10 )
494 printf("Checking prerequisite [%s]\n", tcp->CE_NAMEce_name->ht_name);
495
496 seq = (((cp->ce_attr | Glob_attr) & A_SEQ0x00200) != 0);
497
498 /* This checks if this prerequisite is still in the making, if yes
499 * come back later. */
500 if( tcp->ce_flag & F_VISITED0x0080 ) {
501 /* Check if this currently or fully made target has the same
502 * .SETDIR setting. If yes, continue if it was made or come
503 * back later otherwise. */
504 if( _explode_graph(tcp, dp, setdirroot) == 0 ) {
505 /* didn't blow it up so see if we need to wait for it. */
506 if( tcp->ce_flag & F_MADE0x8000 ) {
507 /* Target was made. */
508 continue;
509 }
510 else
511 /* Target is still in the making ... */
512 goto stop_making_it;
513 }
514 else
515 /* Use the new prerequisite with the new .SETDIR value. */
516 tcp = dp->cl_prq;
517 }
518
519 /* If the previous target (prereq) is not yet ready return if
520 * seq is TRUE. */
521 if( seq && !made ) goto stop_making_it;
522
523 /* Expand dynamic prerequisites. The F_MARK flag is guarging against
524 * possible double expandion of dynamic prerequisites containing more
525 * than one prerequisite. */
526 /* A new A_DYNAMIC attribute could save a lot of strchr( ,'$') calls. */
527 if ( tcp && !(tcp->ce_flag & F_MARK0x0001) && strchr(tcp->CE_NAMEce_name->ht_name, '$') ) {
528 /* Replace this dynamic prerequisite with the the real prerequisite,
529 * and add the additional prerequisites if there are more than one.*/
530
531 name = Expand( tcp->CE_NAMEce_name->ht_name );
532 if( strcmp(name,cp->CE_NAMEce_name->ht_name) == 0 )
533 Fatal("Detected circular dynamic dependency; generated '%s'",name);
534
535 /* Call helper for dynamic prerequisite expansion to replace the
536 * prerequisite with the expanded version and add the new
537 * prerequisites, if the macro expanded to more than one, after
538 * the current list element. */
539 dp = _expand_dynamic_prq( cp->ce_prq, dp, name );
540 FREE( name )free((char*)(name));
541
542 /* _expand_dynamic_prq() probably changed dp->cl_prq. */
543 tcp = dp->cl_prq;
544 if ( tcp ) {
545 next = dp->cl_next;
546 }
547 }
548
549 /* Dynamic expansion results in a NULL cell only when the the new
550 * prerequisite is already in the prerequisite list or empty. In this
551 * case delete the cell and continue. */
552 if ( tcp == NIL(CELL)((CELL*)((void*)0)) ) {
553 FREE(dp)free((char*)(dp));
554 if ( prev == NIL(LINK)((LINK*)((void*)0)) ) {
555 cp->ce_prq = next;
556 dp = NULL((void*)0); /* dp will be the new value of prev. */
557 }
558 else {
559 prev->cl_next = next;
560 dp = prev;
561 }
562 continue;
563 }
564
565 /* Clear F_MARK flag that could have been set by _expand_dynamic_prq(). */
566 tcp->ce_flag &= ~(F_MARK0x0001);
567
568 if( cp->ce_attr & A_LIBRARY0x00004 ) {
569 tcp->ce_attr |= A_LIBRARYM0x80000;
570 tcp->ce_lib = cp->ce_fname;
571 }
572
573 /* Propagate the parent's F_REMOVE and F_INFER flags to the
574 * prerequisites. */
575 tcp->ce_flag |= cp->ce_flag & (F_REMOVE0x1000|F_INFER0x4000);
576
577 /* Propagate parents A_ROOT attribute to a child if the parent is a
578 * F_MULTI target. */
579 if( (cp->ce_flag & F_MULTI0x0002) && (cp->ce_attr & A_ROOT0x01000000) )
580 tcp->ce_attr |= A_ROOT0x01000000;
581
582 tcp->ce_parent = cp;
583 rval |= Make(tcp, setdirroot);
584
585 if( cp->ce_attr & A_LIBRARY0x00004 )
586 tcp->ce_attr ^= A_LIBRARYM0x80000;
587
588 /* Return on error or if Make() is still running and A_SEQ is set.
589 * (All F_MULTI targets have the A_SEQ attribute.) */
590 if( rval == -1 || (seq && (rval==1)) )
591 goto stop_making_it;
592
593 /* If tcp is ready, set made = F_MADE. */
594 made &= tcp->ce_flag & F_MADE0x8000;
595 }
596
597
598 /* Do the loop again. We are most definitely going to make the current
599 * cell now. NOTE: doing this loop here also results in a reduction
600 * in peak memory usage by the algorithm. */
601
602 for( dp = cp->ce_prq; dp != NIL(LINK)((LINK*)((void*)0)); dp = dp->cl_next ) {
603 int tgflg;
604 tcp = dp->cl_prq;
605 if( tcp == NIL(CELL)((CELL*)((void*)0)) )
606 Fatal("Internal Error: Found prerequisite list cell without prerequisite!");
607
608 name = tcp->ce_fname;
609
610 /* make certain that all prerequisites are made prior to advancing. */
611 if( !(tcp->ce_flag & F_MADE0x8000) ) goto stop_making_it;
612
613 /* If the target is a library, then check to make certain that a member
614 * is newer than an object file sitting on disk. If the disk version
615 * is newer then set the time stamps so that the archived member is
616 * replaced. */
617 if( cp->ce_attr & A_LIBRARY0x00004 )
618 if( tcp->ce_time <= cp->ce_time ) {
619 time_t mtime = Do_stat( name, tcp->ce_lib, NIL(char *)((char **)((void*)0)), FALSE0 );
620 if( mtime < tcp->ce_time ) tcp->ce_time = cp->ce_time+1L;
621 }
622
623 /* Set otime to the newest time stamp of all prereqs or 1 if there
624 * are no prerequisites. */
625 if( tcp->ce_time > otime ) otime = tcp->ce_time;
626
627 list_add(&all_list, name);
628 if( (tgflg = (dp->cl_flag & F_TARGET0x0008)) != 0 )
629 list_add(&inf_list, name);
630
631 if((cp->ce_time<tcp->ce_time) || ((tcp->ce_flag & F_TARGET0x0008) && Force)) {
632 list_add(&outall_list, name);
633 if( tgflg )
634 list_add(&imm_list, name);
635 }
636 }
637
638 /* If we are building a F_MULTI target inherit the time from
639 * its children. */
640 if( (cp->ce_flag & F_MULTI0x0002) )
641 cp->ce_time = otime;
642
643 /* All prerequisites are made, now make the current target. */
644
645 /* Restore UseWinpath and $@ if needed, see above for an explanation. */
646 if (m_at->ht_value == NIL(char)((char*)((void*)0))) {
647 /* This check effectively tests if Make() was run before because
648 * Make() frees all dynamic macro values at the end. */
649 UseWinpath = (((cp->ce_attr|Glob_attr)&A_WINPATH0x01000) != 0);
650 m_at = Def_macro("@", DO_WINPATH(cp->ce_fname)cp->ce_fname, M_MULTI0x0004);
651 }
652
653 /* Create a string with all concatenate filenames. The function
654 * respects .WINPATH. Note that gen_path_list_string empties its
655 * parameter :( */
656 all = gen_path_list_string(&all_list);
657 imm = gen_path_list_string(&imm_list);
658 outall = gen_path_list_string(&outall_list);
659 inf = gen_path_list_string(&inf_list);
660
661 DB_PRINT( "mem", ("%s:-C mem %ld", cp->CE_NAME, (long) coreleft()) );
662 DB_PRINT( "make", ("I make '%s' if %ld > %ld", cp->CE_NAME, otime,
663 cp->ce_time) );
664
665 if( Verbose & V_MAKE0x10 ) {
666 printf( "%s: >>>> Making ", Pname );
667 /* Also print the F_MULTI master target. */
668 if( cp->ce_flag & F_MULTI0x0002 )
669 printf( "(::-\"master\" target) " );
670 if( cp->ce_count != 0 )
671 printf( "[%s::{%d}]\n", cp->CE_NAMEce_name->ht_name, cp->ce_count );
672 else
673 printf( "[%s]\n", cp->CE_NAMEce_name->ht_name );
674 }
675
676
677 /* Only PWD, TMD, MAKEDIR and the dynamic macros are affected by
678 * .WINPATH. $@ is handled earlier, do the rest now. */
679#if defined(__CYGWIN__)
680 /* This is only relevant for cygwin. */
681 if( UseWinpath != prev_winpath_attr ) {
682 Def_macro( "MAKEDIR", Makedir, M_FORCE0x0080 | M_EXPANDED0x0008 );
683 /* If push is TRUE (Push_dir() was used) PWD and TMD are already
684 * set. */
685 if( !push ) {
686 Def_macro( "PWD", Pwd, M_FORCE0x0080 | M_EXPANDED0x0008 );
687 _set_tmd();
688 }
689 }
690 prev_winpath_attr = UseWinpath;
691#endif
692
693 /* Set the remaining dynamic macros $*, $>, $?, $<, $& and $^. */
694
695 /* $* is either expanded as the result of a % inference or defined to
696 * $(@:db) and hence unexpanded otherwise. The latter doesn't start
697 * with / and will therefore not be touched by DO_WINPATH(). */
698 m_bb = Def_macro( "*", DO_WINPATH(cp->ce_per)cp->ce_per, M_MULTI0x0004 );
699
700 /* This is expanded. */
701 m_g = Def_macro( ">", DO_WINPATH(cp->ce_lib)cp->ce_lib, M_MULTI0x0004|M_EXPANDED0x0008 );
702 /* These strings are generated with gen_path_list_string() and honor
703 * .WINPATH */
704 m_q = Def_macro( "?", outall, M_MULTI0x0004|M_EXPANDED0x0008 );
705 m_b = Def_macro( "<", inf, M_MULTI0x0004|M_EXPANDED0x0008 );
706 m_l = Def_macro( "&", all, M_MULTI0x0004|M_EXPANDED0x0008 );
707 m_up = Def_macro( "^", imm, M_MULTI0x0004|M_EXPANDED0x0008 );
708
709 _recipes[ RP_RECIPE1 ] = cp->ce_recipe;
710
711 /* We attempt to make the target if
712 * 1. it has a newer prerequisite
713 * 2. It is a target and Force is set
714 * 3. It's set of recipe lines has changed.
715 */
716 if( Check_state(cp, _recipes, NUM_RECIPES3 )
717 || (cp->ce_time < otime)
718 || ((cp->ce_flag & F_TARGET0x0008) && Force)
719 ) {
720
721 if( Measure & M_TARGET0x01 )
722 Do_profile_output( "s", M_TARGET0x01, cp );
723
724 /* Only checking so stop as soon as we determine we will make
725 * something */
726 if( Check ) {
727 rval = -1;
728 goto stop_making_it;
729 }
730
731 if( Verbose & V_MAKE0x10 )
732 printf( "%s: Updating [%s], (%ld > %ld)\n", Pname,
733 cp->CE_NAMEce_name->ht_name, otime, cp->ce_time );
734
735 /* In order to check if a targets time stamp was properly updated
736 * after the target was made and to keep the dependency chain valid
737 * for targets without recipes we store the minimum required file
738 * time. If the target time stamp is older than the newest
739 * prerequisite use that time, otherwise the current time. (This
740 * avoids the call to Do_time() for every target, still checks
741 * if the target time is new enough for the given prerequisite and
742 * mintime is also the newest time of the given prerequisites and
743 * can be used for targets without recipes.)
744 * We reuse the ce_time member to store this minimum time until
745 * the target is finished by Update_time_stamp(). This function
746 * checks if the file time was updated properly and warns if it was
747 * not. (While making a target this value does not change.) */
748 cp->ce_time = ( cp->ce_time < otime ? otime : Do_time() );
749 DB_PRINT( "make", ("Set ce_time (mintime) to: %ld", cp->ce_time) );
750
751 if( Touch ) {
752 name = cp->ce_fname;
753 lib = cp->ce_lib;
754
755 if( (!(Glob_attr & A_SILENT0x00002) || !Trace) && !(cp->ce_attr & A_PHONY0x04000) ) {
756 if( lib == NIL(char)((char*)((void*)0)) )
757 printf("touch(%s)", name );
758 else if( cp->ce_attr & A_SYMBOL0x00040 )
759 printf("touch(%s((%s)))", lib, name );
760 else
761 printf("touch(%s(%s))", lib, name );
762 }
763
764 if( !Trace && !(cp->ce_attr & A_PHONY0x04000) )
765 if( Do_touch( name, lib,
766 (cp->ce_attr & A_SYMBOL0x00040) ? &name : NIL(char *)((char **)((void*)0)) ) != 0 )
767 printf( " not touched - non-existant" );
768
769 if( (!(Glob_attr & A_SILENT0x00002) || !Trace) && !(cp->ce_attr & A_PHONY0x04000) )
770 printf( "\n" );
771
772 Update_time_stamp( cp );
773 }
774 else if( cp->ce_recipe != NIL(STRING)((STRING*)((void*)0)) ) {
775 /* If a recipe is found use it. Note this misses F_MULTI targets. */
776 if( !(cp->ce_flag & F_SINGLE0x0004) ) /* Execute the recipes once ... */
777 rval = Exec_commands( cp );
778 /* Update_time_stamp() is called inside Exec_commands() after the
779 * last recipe line is finished. (In _finished_child()) */
780 else { /* or for every out of date dependency
781 * if the ruleop ! was used. */
782 TKSTR tk;
783
784 /* We will redefine $? to be the prerequisite that the recipes
785 * are currently evaluated for. */
786 _drop_mac( m_q );
787
788 /* Execute recipes for each out out of date prerequisites.
789 * WARNING! If no prerequisite is given the recipes are not
790 * executed at all! */
791 if( outall && *outall ) {
792 /* Wait for each prerequisite to finish, save the status
793 * of Wait_for_completion. */
794 int wait_for_completion_status = Wait_for_completion;
795 Wait_for_completion = TRUE1;
796
797 SET_TOKEN( &tk, outall )(&tk)->tk_str = (outall); (&tk)->tk_cchar = *(outall
); (&tk)->tk_quote = 1;
;
798
799 /* No need to update the target timestamp/removing temporary
800 * prerequisites (Update_time_stamp() in _finished_child())
801 * until all prerequisites are done. */
802 Doing_bang = TRUE1;
803 name = Get_token( &tk, "", FALSE0 );
804 /* This loop might fail if outall contains filenames with
805 * spaces. */
806 do {
807 /* Set $? to current prerequisite. */
808 m_q->ht_value = name;
809
810 rval = Exec_commands( cp );
811 /* Thanks to Wait_for_completion = TRUE we are allowed
812 * to remove the temp files here. */
813 Unlink_temp_files(cp);
814 }
815 while( *(name = Get_token( &tk, "", FALSE0 )) != '\0' );
816 Wait_for_completion = wait_for_completion_status;
817 Doing_bang = FALSE0;
818 }
819
820 Update_time_stamp( cp );
821 /* Erase $? again. Don't free the pointer, it was part of outall. */
822 m_q->ht_value = NIL(char)((char*)((void*)0));
823 }
824 }
825 else if( !(cp->ce_flag & F_RULES0x0010) && !(cp->ce_flag & F_STAT0x0040) &&
826 (!(cp->ce_attr & A_ROOT0x01000000) || !(cp->ce_flag & F_EXPLICIT0x0400)) &&
827 !(cp->ce_count) )
828 /* F_MULTI subtargets should evaluate its parents F_RULES value
829 * but _make_multi always sets the F_RULES value of the master
830 * target. Assume F_RULES is set for subtargets. This might not
831 * be true if there are no prerequisites and no recipes in any
832 * of the subtargets. (FIXME) */
833 Fatal( "Don't know how to make `%s'",cp->CE_NAMEce_name->ht_name );
834 else {
835 /* Empty recipe, set the flag as MADE and update the time stamp */
836 /* This might be a the master cell of a F_MULTI target. */
837 Update_time_stamp( cp );
838 }
839 }
840 else {
841 if( Verbose & V_MAKE0x10 )
842 printf( "%s: Up to date [%s], prq time = %ld , target time = %ld)\n", Pname,
843 cp->CE_NAMEce_name->ht_name, otime, cp->ce_time );
844 mark_made = TRUE1;
845 }
846
847 /* If mark_made == TRUE the target is up-to-date otherwise it is
848 * currently in the making. */
849
850 /* Update all targets in .UPDATEALL rule / only target cp. */
851 for(dp=CeMeToo(cp)&((cp)->ce_all); dp; dp=dp->cl_next) {
852 tcp=dp->cl_prq;
853
854 /* Set the time stamp of those prerequisites without rule to the current
855 * time if Force is TRUE to make sure that targets depending on those
856 * prerequisites get remade. */
857 if( !(tcp->ce_flag & F_TARGET0x0008) && Force ) tcp->ce_time = Do_time();
858 if( mark_made ) {
859 tcp->ce_flag |= F_MADE0x8000;
860 if( tcp->ce_flag & F_MULTI0x0002 ) {
861 LINKPTR tdp;
862 for( tdp = tcp->ce_prq; tdp != NIL(LINK)((LINK*)((void*)0)); tdp = tdp->cl_next )
863 tcp->ce_attr |= tdp->cl_prq->ce_attr & A_UPDATED0x800000;
864 }
865 }
866
867 /* Note that the target is in the making. */
868 tcp->ce_flag |= F_VISITED0x0080;
869
870 /* Note: If the prerequisite was made using a .SETDIR= attribute
871 * directory then we will include the directory in the fname
872 * of the target. */
873 if( push ) {
874 char *dir = nsetdirroot ? nsetdirroot->ce_dir : Makedir;
875 /* get relative path from current SETDIR to new SETDIR. */
876 /* Attention, even with .WINPATH set this has to be a POSIX
877 * path as ce_fname neeed to be POSIX. */
878 char *pref = _prefix( dir, tcp->ce_dir );
879 char *nname = Build_path(pref, tcp->ce_fname);
880
881 FREE(pref)free((char*)(pref));
882 if( (tcp->ce_attr & A_FFNAME0x400000) && (tcp->ce_fname != NIL(char)((char*)((void*)0))) )
883 FREE( tcp->ce_fname )free((char*)(tcp->ce_fname));
884
885 tcp->ce_fname = DmStrDup(nname);
886 tcp->ce_attr |= A_FFNAME0x400000;
887 }
888 }
889
890stop_making_it:
891 _drop_mac( m_g );
892 _drop_mac( m_q );
893 _drop_mac( m_b );
894 _drop_mac( m_l );
895 _drop_mac( m_bb );
896 _drop_mac( m_up );
897 _drop_mac( m_at );
898
899 /* undefine conditional macros if any */
900 for(dp=CeMeToo(cp)&((cp)->ce_all); dp; dp=dp->cl_next) {
901 tcp=dp->cl_prq;
902
903 while (tcp->ce_pushed != NIL(HASH)((HASH*)((void*)0))) {
904 HASHPTR cur = tcp->ce_pushed;
905 tcp->ce_pushed = cur->ht_link;
906
907 Pop_macro(cur);
908 FREE(cur->ht_name)free((char*)(cur->ht_name));
909 if(cur->ht_value)
910 FREE(cur->ht_value)free((char*)(cur->ht_value));
911 FREE(cur)free((char*)(cur));
912 }
913 }
914
915 if( push )
916 Pop_dir(FALSE0);
917
918 /* Undefine the strings that we used for constructing inferred
919 * prerequisites. */
920 if( inf != NIL(char)((char*)((void*)0)) ) FREE( inf )free((char*)(inf));
921 if( all != NIL(char)((char*)((void*)0)) ) FREE( all )free((char*)(all));
922 if( imm != NIL(char)((char*)((void*)0)) ) FREE( imm )free((char*)(imm));
923 if( outall != NIL(char)((char*)((void*)0)) ) FREE( outall )free((char*)(outall));
924 free_list(all_list.first);
925 free_list(imm_list.first);
926 free_list(outall_list.first);
927 free_list(inf_list.first);
928
929 DB_PRINT( "mem", ("%s:-< mem %ld", cp->CE_NAME, (long) coreleft()) );
930 DB_RETURN(rval)return (rval);
931}
932
933
934static char *
935_prefix( pfx, pat )/*
936=====================
937 Return the relative path from pfx to pat. Both paths have to be absolute
938 paths. If the paths are on different resources or drives (if applicable)
939 or only share a relative path going up to the root dir and down again
940 return pat. */
941char *pfx;
942char *pat;
943{
944 char *cmp1=pfx;
945 char *cmp2=pat;
946 char *tpat=pat; /* Keep pointer to original pat. */
947 char *result;
948 char *up;
949 int first = 1;
950 int samerootdir = 1; /* Marks special treatment for the root dir. */
951#ifdef HAVE_DRIVE_LETTERS
952 int pfxdl = 0;
953 int patdl = 0;
954#endif
955
956 /* Micro optimization return immediately if pfx and pat are equal. */
957 if( strcmp(pfx, pat) == 0 )
958 return(DmStrDup(""));
959
960#ifdef HAVE_DRIVE_LETTERS
961 /* remove the drive letters to avoid getting them into the relative
962 * path later. */
963 if( *pfx && pfx[1] == ':' && isalpha(*pfx)((*__ctype_b_loc ())[(int) ((*pfx))] & (unsigned short int
) _ISalpha)
) {
964 pfxdl = 1;
965 cmp1 = DmStrSpn(pfx+2, DirBrkStr);
966 }
967 if( *pat && pat[1] == ':' && isalpha(*pat)((*__ctype_b_loc ())[(int) ((*pat))] & (unsigned short int
) _ISalpha)
) {
968 patdl = 1;
969 cmp2 = DmStrSpn(pat+2, DirBrkStr);
970 }
971 /* If the drive letters are different use the abs. path. */
972 if( pfxdl && patdl && (tolower(*pfx) != tolower(*pat)) )
973 return(DmStrDup(pat));
974
975 /* If only one has a drive letter also use the abs. path. */
976 if( pfxdl != patdl )
977 return(DmStrDup(pat));
978 else if( pfxdl )
979 /* If both are the same drive letter, disable the special top
980 * dir treatment. */
981 samerootdir = 0;
982
983 /* Continue without the drive letters. (Either none was present,
984 * or both were the same. This also solves the problem that the
985 * case of the drive letters sometimes depends on the shell.
986 * (cmd.exe vs. cygwin bash) */
987 pfx = cmp1;
988 pat = cmp2;
989#endif
990
991 /* Cut off equal leading parts of pfx, pat. Both have to be abs. paths. */
992 while(*pfx && *pat) {
993 /* skip leading dir. separators. */
994 pfx = DmStrSpn(cmp1, DirBrkStr);
995 pat = DmStrSpn(cmp2, DirBrkStr);
996
997 /* Only check in the first run of the loop. Leading slashes can only
998 * mean POSIX paths or Windows resources (two) slashes. Drive letters
999 * have no leading slash. In any case, if the number of slashes are
1000 * not equal there can be no relative path from one two the other.
1001 * In this case return the absolute path. */
1002 if( first ) {
1003 if( cmp1-pfx != cmp2-pat ) {
1004 return(DmStrDup(tpat));
1005 }
1006 first = 0;
1007 }
1008
1009 /* find next dir. separator (or ""). */
1010 cmp1 = DmStrPbrk(pfx, DirBrkStr);
1011 cmp2 = DmStrPbrk(pat, DirBrkStr);
1012
1013 /* if length of directory name is equal compare the strings. If equal
1014 * go into next loop. If not equal and directory names in the root
1015 * dir are compared return the absolut path otherwise break the loop
1016 * and construct the relative path from pfx to pat. */
1017 if ( (cmp1-pfx) != (cmp2-pat) || strncmp(pfx,pat,cmp1-pfx) != 0 ) {
1018 if( samerootdir ) {
1019 return(DmStrDup(tpat));
1020 }
1021 break;
1022 }
1023
1024 if( samerootdir ) {
1025#if __CYGWIN__
1026 /* If the toplevel directory is /cygdrive (or the equivalent prefix)
1027 * treat the following level also as rootdir. If we are here cmp1-pfx
1028 * cannot be zero so we won't compare with an empty cygdrive prefix. */
1029 if ( (cmp1-pfx) == CygDrvPreLen && strncmp(pfx,CygDrvPre,CygDrvPreLen) == 0 )
1030 samerootdir = 1;
1031 else
1032#endif
1033 samerootdir = 0;
1034 }
1035 }
1036
1037 result = DmStrDup("");
1038 up = DmStrJoin("..",DirSepStr,-1,FALSE0);
1039 cmp1 = pfx;
1040 /* Add "../" for each directory in pfx */
1041 while ( *(pfx=DmStrSpn(cmp1,DirBrkStr)) != '\0' ) {
1042 cmp1 = DmStrPbrk(pfx,DirBrkStr);
1043 result = DmStrJoin(result,up,-1,TRUE1);
1044 }
1045 FREE(up)free((char*)(up));
1046
1047 pat = DmStrSpn(pat,DirBrkStr);
1048 /* Append pat to result. */
1049 if( *pat != '\0' ) {
1050 cmp2 = DmStrDup(Build_path(result, pat));
1051 FREE(result)free((char*)(result));
1052 result = cmp2;
1053 } else {
1054 /* if pat is empty and result exists remove the trailing slash
1055 * from the last "../". */
1056 if( *result ) {
1057 result[strlen(result)-1] = '\0';
1058 }
1059 }
1060
1061 return(result);
1062}
1063
1064
1065static LINKPTR
1066_dup_prq( lp )
1067LINKPTR lp;
1068{
1069 LINKPTR tlp;
1070
1071 if( lp == NIL(LINK)((LINK*)((void*)0)) ) return(lp);
1072
1073 TALLOC(tlp, 1, LINK)if ((tlp = (LINK*) calloc((unsigned int)(1), (size_t)sizeof(LINK
))) == (LINK*)0) {No_ram();}
;
1074 tlp->cl_prq = lp->cl_prq;
1075 tlp->cl_flag = lp->cl_flag;
1076 tlp->cl_next = _dup_prq( lp->cl_next );
1077
1078 return(tlp);
1079}
1080
1081
1082static LINKPTR
1083_expand_dynamic_prq( head, lp, name )/*
1084=======================================
1085 The string name can contain one or more target names. Check if these are
1086 already a prerequisite for the current target. If not add them to the list
1087 of prerequisites. If no prerequisites were added set lp->cl_prq to NULL.
1088 Set the F_MARK flag to indicate that the prerequisite was expanded.
1089 Use cl_flag instead?? */
1090LINKPTR head;
1091LINKPTR lp;
1092char *name;
1093{
1094 CELLPTR cur = lp->cl_prq;
1095
1096 if( !(*name) ) {
1097 /* If name is empty this leaves lp->cl_prq unchanged -> No prerequisite added. */
1098 ;
1099 }
1100 else if ( strchr(name, ' ') == NIL(char)((char*)((void*)0)) ) {
1101 /* If condition above is true, no space is found. */
1102 CELLPTR prq = Def_cell(name);
1103 LINKPTR tmp;
1104
1105 /* Check if prq already exists. */
1106 for(tmp=head;tmp != NIL(LINK)((LINK*)((void*)0)) && tmp->cl_prq != prq;tmp=tmp->cl_next);
1107
1108 /* If tmp is NULL then the prerequisite is new and is added to the list. */
1109 if ( !tmp ) {
1110 /* replace the prerequisite with the expanded version. */
1111 lp->cl_prq = prq;
1112 lp->cl_prq->ce_flag |= F_MARK0x0001;
1113 }
1114 }
1115 else {
1116 LINKPTR tlp = lp;
1117 LINKPTR next = lp->cl_next;
1118 TKSTR token;
1119 char *p;
1120 int first=TRUE1;
1121
1122 /* Handle more than one prerequisite. */
1123 SET_TOKEN(&token, name)(&token)->tk_str = (name); (&token)->tk_cchar =
*(name); (&token)->tk_quote = 1;
;
1124 while (*(p=Get_token(&token, "", FALSE0)) != '\0') {
1125 CELLPTR prq = Def_cell(p);
1126 LINKPTR tmp;
1127
1128 for(tmp=head;tmp != NIL(LINK)((LINK*)((void*)0)) && tmp->cl_prq != prq;tmp=tmp->cl_next);
1129
1130 /* If tmp is not NULL the prerequisite already exists. */
1131 if ( tmp ) continue;
1132
1133 /* Add list elements behind the first if more then one new
1134 * prerequisite is found. */
1135 if ( first ) {
1136 first = FALSE0;
1137 }
1138 else {
1139 TALLOC(tlp->cl_next,1,LINK)if ((tlp->cl_next = (LINK*) calloc((unsigned int)(1), (size_t
)sizeof(LINK))) == (LINK*)0) {No_ram();}
;
1140 tlp = tlp->cl_next;
1141 tlp->cl_flag |= F_TARGET0x0008;
1142 tlp->cl_next = next;
1143 }
1144
1145 tlp->cl_prq = prq;
1146 tlp->cl_prq->ce_flag |= F_MARK0x0001;
1147 }
1148 CLEAR_TOKEN( &token )*(&token)->tk_str = (&token)->tk_cchar;
1149 }
1150
1151 /* If the condition is true no new prerequisits were found. */
1152 if ( lp->cl_prq == cur ) {
1153 lp->cl_prq = NIL(CELL)((CELL*)((void*)0));
1154 lp->cl_flag = 0;
1155 }
1156
1157 /* Is returned unchanged. */
1158 return(lp);
1159}
1160
1161
1162static void
1163_drop_mac( hp )/*
1164================ set a macro value to zero. */
1165HASHPTR hp;
1166{
1167 if( hp && hp->ht_value != NIL(char)((char*)((void*)0)) ) {
1168 FREE( hp->ht_value )free((char*)(hp->ht_value));
1169 hp->ht_value = NIL(char)((char*)((void*)0));
1170 }
1171}
1172
1173
1174
1175static int
1176_explode_graph( cp, parent, setdirroot )/*
1177==========================================
1178 Check to see if we have made the node already. If so then don't do
1179 it again, except if the cell's ce_setdir field is set to something other
1180 than the value of setdirroot. If they differ then, and we have made it
1181 already, then make it again and set the cell's stat bit to off so that
1182 we do the stat again. */
1183CELLPTR cp;
1184LINKPTR parent;
1185CELLPTR setdirroot;
1186{
1187 static CELLPTR removecell = NIL(CELL)((CELL*)((void*)0));
1188
1189 if ( removecell == NIL(CELL)((CELL*)((void*)0)) )
1190 removecell = Def_cell(".REMOVE");
1191
1192 /* we may return if we made it already from the same setdir location,
1193 * or if it is not a library member whose lib field is non NULL. (if
1194 * it is such a member then we have a line of the form:
1195 * lib1 lib2 .LIBRARY : member_list...
1196 * and we have to make sure all members are up to date in both libs. */
1197
1198 if ( setdirroot == removecell )
1199 return( 0 );
1200
1201 if( cp->ce_setdir == setdirroot &&
1202 !((cp->ce_attr & A_LIBRARYM0x80000) && (cp->ce_lib != NIL(char)((char*)((void*)0)))) )
1203 return( 0 );
1204
1205 /* We check to make sure that we are comming from a truly different
1206 * directory, ie. ".SETDIR=joe : a.c b.c d.c" are all assumed to come
1207 * from the same directory, even though setdirroot is different when
1208 * making dependents of each of these targets. */
1209
1210 if( cp->ce_setdir != NIL(CELL)((CELL*)((void*)0)) &&
1211 setdirroot != NIL(CELL)((CELL*)((void*)0)) &&
1212 cp->ce_dir &&
1213 setdirroot->ce_dir &&
1214 !strcmp(cp->ce_dir, setdirroot->ce_dir) )
1215 return( 0 );
1216
1217 if( Max_proc > 1 ) {
1218 LINKPTR dp;
1219
1220 TALLOC(parent->cl_prq, 1, CELL)if ((parent->cl_prq = (CELL*) calloc((unsigned int)(1), (size_t
)sizeof(CELL))) == (CELL*)0) {No_ram();}
;
1221 *parent->cl_prq = *cp;
1222 cp = parent->cl_prq;
1223 cp->ce_prq = _dup_prq(cp->ce_prqorg);
1224 cp->ce_all.cl_prq = cp;
1225 CeNotMe(cp)(cp)->ce_all.cl_next = _dup_prq(CeNotMe(cp)(cp)->ce_all.cl_next);
1226
1227 for(dp=CeNotMe(cp)(cp)->ce_all.cl_next;dp;dp=dp->cl_next) {
1228 CELLPTR tcp = dp->cl_prq;
1229 TALLOC(dp->cl_prq,1,CELL)if ((dp->cl_prq = (CELL*) calloc((unsigned int)(1), (size_t
)sizeof(CELL))) == (CELL*)0) {No_ram();}
;
1230 *dp->cl_prq = *tcp;
1231 dp->cl_prq->ce_flag &= ~(F_STAT0x0040|F_VISITED0x0080|F_MADE0x8000);
1232 dp->cl_prq->ce_set = cp;
1233 }
1234 }
1235 cp->ce_flag &= ~(F_STAT0x0040|F_VISITED0x0080|F_MADE0x8000);
1236
1237 /* Indicate that we exploded the graph and that the current node should
1238 * be made. */
1239 return(1);
1240}
1241
1242
1243
1244PUBLIC int
1245Exec_commands( cp )/*
1246=====================
1247 Execute the commands one at a time that are pointed to by the rules pointer
1248 of the target cp if normal (non-group) recipes are defined. If a group recipe
1249 is found all commands are written into a temporary file first and this
1250 (group-) shell script is executed all at once.
1251 If a group is indicated, then the ce_attr determines .IGNORE and .SILENT
1252 treatment for the group.
1253
1254 The function returns 0, if the command is executed and has successfully
1255 returned, and it returns 1 if the command is executing but has not yet
1256 returned or -1 if an error occurred (Return value from Do_cmnd()).
1257
1258 Macros that are found in recipe lines are expanded in this function, in
1259 parallel builds this can mean they are expanded before the previous recipe
1260 lines are finished. (Exception: $(shell ..) waits until all previous recipe
1261 lines are done.)
1262
1263 The F_MADE bit in the cell is guaranteed set when the command has
1264 successfully completed. */
1265CELLPTR cp;
1266{
1267 static HASHPTR useshell = NIL(HASH)((HASH*)((void*)0));
1268 static HASHPTR command = NIL(HASH)((HASH*)((void*)0));
1269 static int read_cmnd = 0;
1270 register STRINGPTR rp;
1271 STRINGPTR orp;
1272 char *cmnd;
1273 char *groupfile;
1274 FILE *tmpfile = 0;
1275 int do_it;
1276 t_attr attr;
1277 int group;
1278 int trace;
1279 int rval = 0;
1280
1281 DB_ENTER( "Exec_commands" );
1282
1283 if( cp->ce_recipe == NIL(STRING)((STRING*)((void*)0)) )
1284 Fatal("Internal Error: No recipe found!");
1285
1286 attr = Glob_attr | cp->ce_attr;
1287 trace = Trace || !(attr & A_SILENT0x00002);
1288 group = cp->ce_flag & F_GROUP0x0020;
1289
1290 /* Do it again here for those that call us from places other than Make()
1291 * above. */
1292 orp = _recipes[ RP_RECIPE1 ];
1293 _recipes[ RP_RECIPE1 ] = cp->ce_recipe;
1294
1295 if( group ) {
1296 /* Leave this assignment of Current_target here. It is needed just
1297 * incase the user hits ^C after the tempfile for the group recipe
1298 * has been opened. */
1299 Current_target = cp;
1300 trace = Trace || !(attr & A_SILENT0x00002);
1301
1302 if( !Trace ) tmpfile = Start_temp( Grp_suff, cp, &groupfile );
1303 if( trace ) fputs( "[\n", stdoutstdout );
1304
1305 /* Emit group prolog */
1306 if( attr & A_PROLOG0x00010 )
1307 _append_file( _recipes[RP_GPPROLOG0], tmpfile, cp->CE_NAMEce_name->ht_name, trace );
1308 }
1309
1310 if( !useshell )
1311 useshell=Def_macro("USESHELL",NIL(char)((char*)((void*)0)),M_MULTI0x0004|M_EXPANDED0x0008);
1312
1313 if( !read_cmnd ) {
1314 command = GET_MACRO("COMMAND")Get_name("COMMAND", Macs, 0);
1315 read_cmnd = 1;
1316 }
1317
1318 /* Process commands in recipe. If in group, merely append to file.
1319 * Otherwise, run them. */
1320 for( rp=_recipes[RP_RECIPE1]; rp != NIL(STRING)((STRING*)((void*)0)); rp=rp->st_next) {
1321 t_attr a_attr = A_DEFAULT0x00000;
1322 t_attr l_attr;
1323 char *p;
1324 int new_attr = FALSE0;
1325 int shell; /* True if the recipe shall run in shell. */
1326
1327 /* Reset it for each recipe line otherwise tempfiles don't get removed.
1328 * Since processing of $(mktmp ...) depends on Current_target being
1329 * correctly set. */
1330 Current_target = cp;
1331
1332 /* Only check for +,-,%,@ if the recipe line begins with a '$' macro
1333 * expansion. Otherwise there is no way it is going to find these
1334 * now. */
1335 if( *rp->st_string == '$' && !group ) {
1336 t_attr s_attr = Glob_attr;
1337 Glob_attr |= A_SILENT0x00002;
1338 Suppress_temp_file = TRUE1;
1339 cmnd = Expand(rp->st_string);
1340 Suppress_temp_file = FALSE0;
1341 a_attr |= Rcp_attribute(cmnd);
1342 FREE(cmnd)free((char*)(cmnd));
1343 ++new_attr;
1344 Glob_attr = s_attr;
1345 }
1346
1347 l_attr = attr|a_attr|rp->st_attr;
1348 shell = ((l_attr & A_SHELL0x00800) != 0);
1349 useshell->ht_value = (group||shell)?"yes":"no";
1350
1351 /* All macros are expanded before putting them in the "process queue".
1352 * Nothing in Expand() should be able to change dynamic macros. */
1353 cmnd = Expand( rp->st_string );
1354
1355 if( new_attr && (p = DmStrSpn(cmnd," \t\n+-@%")) != cmnd ) {
1356 size_t len = strlen(p)+1;
1357 memmove(cmnd,p,len);
1358 }
1359
1360 /* COMMAND macro is set to "$(CMNDNAME) $(CMNDARGS)" by default, it is
1361 * possible for the user to reset it to, for example
1362 * COMMAND = $(CMNDNAME) @$(mktmp $(CMNDARGS))
1363 * in order to get a different interface for his command execution. */
1364 if( command != NIL(HASH)((HASH*)((void*)0)) && !group ) {
1365 char *cname = cmnd;
1366 char cmndbuf[30];
1367
1368 if ( *(p=DmStrPbrk(cmnd," \t\n")) != '\0' ) {
1369 *p = '\0';
1370 (void)Def_macro("CMNDARGS",DmStrSpn(p+1," \t\n"),M_MULTI0x0004|M_EXPANDED0x0008);
1371 }
1372 else
1373 (void) Def_macro("CMNDARGS","",M_MULTI0x0004|M_EXPANDED0x0008);
1374
1375 (void) Def_macro("CMNDNAME",cname,M_MULTI0x0004|M_EXPANDED0x0008);
1376
1377 strcpy(cmndbuf, "$(COMMAND)");
1378 cmnd = Expand(cmndbuf);
1379 FREE(cname)free((char*)(cname)); /* cname == cmnd at this point. */
1380
1381 /* Collect up any new attributes */
1382 l_attr |= Rcp_attribute(cmnd);
1383 shell = ((l_attr & A_SHELL0x00800) != 0);
1384
1385 /* clean up the attributes that we may have just added. */
1386 if( (p = DmStrSpn(cmnd," \t\n+-@%")) != cmnd ) {
1387 size_t len = strlen(p)+1;
1388 memmove(cmnd,p,len);
1389 }
1390 }
1391
1392#if defined(MSDOS)
1393 Swap_on_exec = ((l_attr & A_SWAP0x01000) != 0); /* Swapping for DOS only */
1394#endif
1395 do_it = !Trace;
1396
1397 /* We force execution of the recipe if we are tracing and the .EXECUTE
1398 * attribute was given or if the it is not a group recipe and the
1399 * recipe line contains the string $(MAKE). Wait_for_completion might
1400 * be changed gobaly but this is without consequences as we wait for
1401 * every recipe with .EXECUTE and don't start anything else. */
1402 if( Trace
1403 && ((l_attr & A_EXECUTE0x20000)||(!group && DmStrStr(rp->st_string,"$(MAKE)")))
1404 ) {
1405 Wait_for_completion |= Trace;
1406 do_it = TRUE1;
1407 }
1408
1409 if( group )
1410 /* Append_line() calls Print_cmnd(). */
1411 Append_line( cmnd, TRUE1, tmpfile, cp->CE_NAMEce_name->ht_name, trace, 0 );
1412 else {
1413 /* Don't print empty recipe lines. .ROOT and .TARGETS
1414 * deliberately might have empty "" recipes and we don't want
1415 * to output empty recipe lines for them. */
1416 if ( *cmnd ) {
1417 /* Print command and remove continuation sequence from cmnd. */
1418 Print_cmnd(cmnd, !(do_it && (l_attr & A_SILENT0x00002)), 0);
1419 }
1420 rval=Do_cmnd(&cmnd,FALSE0,do_it,cp,l_attr,
1421 rp->st_next == NIL(STRING)((STRING*)((void*)0)) );
1422 }
1423
1424 FREE(cmnd)free((char*)(cmnd));
1425 }
1426
1427 /* If it is a group then output the EPILOG if required and possibly
1428 * execute the command */
1429 if( group && !(cp->ce_attr & A_ERROR0x10000000) ) {
1430 if( attr & A_EPILOG0x00008 ) /* emit epilog */
1431 _append_file( _recipes[RP_GPEPILOG2], tmpfile, cp->CE_NAMEce_name->ht_name, trace );
1432
1433 if( trace ) fputs("]\n", stdoutstdout);
1434
1435 do_it = !Trace;
1436 if( do_it )
1437 {
1438 Close_temp( cp, tmpfile );
1439#if defined(UNIX)
1440
1441 chmod(groupfile,0700);
1442#endif
1443 }
1444 rval = Do_cmnd(&groupfile, TRUE1, do_it, cp, attr | A_SHELL0x00800, TRUE1);
1445 }
1446
1447 _recipes[ RP_RECIPE1 ] = orp;
1448 cp->ce_attr &= ~A_ERROR0x10000000;
1449 DB_RETURN( rval )return (rval);
1450}
1451
1452
1453PUBLIC void
1454Print_cmnd( cmnd, echo, map )/*
1455================================
1456 This routine is called to print out the command to stdout. If echo is
1457 false the printing to stdout is supressed.
1458 The routine is also used to remove the line continuation sequence
1459 \<nl> from the command string and convert escape sequences if the
1460 map flag is set.
1461 The changed string is used later to actually to execute the command. */
1462char *cmnd;
1463int echo;
1464int map;
1465{
1466 register char *p;
1467 register char *n;
1468 char tmp[3];
1469
1470 DB_ENTER( "Print_cmnd" );
1471
1472 if( echo ) {
1473 printf( "%s\n", cmnd );
1474 fflush(stdoutstdout);
1475 }
1476
1477 tmp[0] = ESCAPE_CHAR*Escape_char;
1478 tmp[1] = CONTINUATION_CHAR'\\';
1479 tmp[2] = '\0';
1480
1481 for( p=cmnd; *(n = DmStrPbrk(p,tmp)) != '\0'; )
1482 /* Remove the \<nl> sequences. */
1483 if(*n == CONTINUATION_CHAR'\\' && n[1] == '\n') {
1484 size_t len = strlen(n+2)+1;
1485 DB_PRINT( "make", ("fixing [%s]", p) );
1486 memmove( n, n+2, len );
1487 p = n;
1488 }
1489 /* Look for an escape sequence and replace it by it's corresponding
1490 * character value. */
1491 else {
1492 if( *n == ESCAPE_CHAR*Escape_char && map ) Map_esc( n );
1493 p = n+1;
1494 }
1495
1496 DB_VOID_RETURNreturn;
1497}
1498
1499
1500
1501/* These routines are used to maintain a stack of directories when making
1502 * the targets. If a target cd's to the directory then it is assumed that
1503 * it will undo it when it is finished making itself. */
1504
1505static STRINGPTR dir_stack = NIL(STRING)((STRING*)((void*)0));
1506
1507PUBLIC int
1508Push_dir( dir, name, ignore )/*
1509===============================
1510 Change the current working directory to dir and save the current
1511 working directory on the stack so that we can come back.
1512
1513 If ignore is TRUE then do not complain about _ch_dir if not possible.
1514
1515 Return 1 if the directory change was successfull and 0 otherwise. */
1516char *dir;
1517char *name;
1518int ignore;
1519{
1520 STRINGPTR new_dir;
1521
1522 DB_ENTER( "Push_dir" );
1523
1524 if( dir == NIL(char)((char*)((void*)0)) || *dir == '\0' ) dir = Pwd;
1525 if( *dir == '\'' && dir[strlen(dir)-1] == '\'' ) {
1526 dir = DmStrDup(dir+1);
1527 dir[strlen(dir)-1]='\0';
1528 }
1529 else if (strchr(dir,'$') != NIL(char)((char*)((void*)0)))
1530 dir = Expand(dir);
1531 else
1532 dir = DmStrDup(dir);
1533
1534 if( Set_dir(dir) ) {
1535 if( !ignore )
1536 Fatal( "Unable to change to directory `%s', target is [%s]",
1537 dir, name );
1538 FREE(dir)free((char*)(dir));
1539 DB_RETURN( 0 )return (0);
1540 }
1541
1542 DB_PRINT( "dir", ("Push: [%s]", dir) );
1543 if( Verbose & V_DIR_SET0x02 )
1544 printf( "%s: Changed to directory [%s]\n", Pname, dir );
1545
1546 FREE( dir )free((char*)(dir));
1547 TALLOC( new_dir, 1, STRING )if ((new_dir = (STRING*) calloc((unsigned int)(1), (size_t)sizeof
(STRING))) == (STRING*)0) {No_ram();}
;
1548 new_dir->st_next = dir_stack;
1549 dir_stack = new_dir;
1550 new_dir->st_string = DmStrDup( Pwd );
1551
1552 Def_macro( "PWD", Get_current_dir(), M_FORCE0x0080 | M_EXPANDED0x0008 );
1553 _set_tmd();
1554
1555 DB_RETURN( 1 )return (1);
1556}
1557
1558
1559
1560PUBLIC void
1561Pop_dir(ignore)/*
1562=================
1563 Change the current working directory to the previous saved dir. */
1564int ignore;
1565{
1566 STRINGPTR old_dir;
1567 char *dir;
1568
1569 DB_ENTER( "Pop_dir" );
1570
1571 if( dir_stack == NIL(STRING)((STRING*)((void*)0)) ) {
1572 if( ignore ) {
1573 DB_VOID_RETURNreturn;
1574 }
1575 else
1576 Error( "Directory stack empty for return from .SETDIR" );
1577 }
1578
1579 if( Set_dir(dir = dir_stack->st_string) )
1580 Fatal( "Could not change to directory `%s'", dir );
1581
1582 Def_macro( "PWD", dir, M_FORCE0x0080 | M_EXPANDED0x0008 );
1583 DB_PRINT( "dir", ("Pop: [%s]", dir) );
1584 if( Verbose & V_DIR_SET0x02 )
1585 printf( "%s: Changed back to directory [%s]\n", Pname, dir);
1586
1587 old_dir = dir_stack;
1588 dir_stack = dir_stack->st_next;
1589
1590 FREE( old_dir->st_string )free((char*)(old_dir->st_string));
1591 FREE( old_dir )free((char*)(old_dir));
1592 _set_tmd();
1593
1594 DB_VOID_RETURNreturn;
1595}
1596
1597
1598
1599static void
1600_set_tmd()/*
1601============
1602 Set the TMD Macro and the Tmd global variable. TMD stands for "To MakeDir"
1603 and is the path from the present directory (value of $(PWD)) to the directory
1604 dmake was started up in (value of $(MAKEDIR)). As _prefix() can return absolute
1605 paths some special .WINPATH treatment is needed.
1606*/
1607{
1608 char *tmd;
1609
1610 if( Tmd )
1611 FREE(Tmd)free((char*)(Tmd));
1612
1613 tmd = _prefix(Pwd, Makedir);
1614 if( *tmd ) {
1615 Def_macro( "TMD", DO_WINPATH(tmd)tmd, M_FORCE0x0080 | M_EXPANDED0x0008 );
1616 Tmd = DmStrDup(tmd);
1617 } else {
1618 Def_macro( "TMD", ".", M_FORCE0x0080 | M_EXPANDED0x0008 );
1619 Tmd = DmStrDup(".");
1620 }
1621 FREE( tmd )free((char*)(tmd));
1622}
1623
1624
1625static void
1626_set_recipe( target, ind )/*
1627============================
1628 Set up the _recipes static variable so that the slot passed in points
1629 at the rules corresponding to the target supplied. */
1630char *target;
1631int ind;
1632{
1633 CELLPTR cp;
1634 HASHPTR hp;
1635
1636 if( (hp = Get_name(target, Defs, FALSE0)) != NIL(HASH)((HASH*)((void*)0)) ) {
1637 cp = hp->CP_OWNRvar.val.ht.ht_owner;
1638 _recipes[ ind ] = cp->ce_recipe;
1639 }
1640 else
1641 _recipes[ ind ] = NIL(STRING)((STRING*)((void*)0));
1642}
1643
1644
1645
1646PUBLIC void
1647Append_line( cmnd, newline, tmpfile, name, printit, map )
1648char *cmnd;
1649int newline;
1650FILE *tmpfile;
1651char *name;
1652int printit;
1653int map;
1654{
1655 Print_cmnd( cmnd, printit, map );
1656
1657 if( Trace ) return;
1658
1659 fputs(cmnd, tmpfile);
1660 if( newline ) fputc('\n', tmpfile);
1661 fflush(tmpfile);
1662
1663 if( ferror(tmpfile) )
1664 Fatal("Write error on temporary file, while processing `%s'", name);
1665}
1666
1667
1668
1669static void
1670_append_file( rp, tmpfile, name, printit )
1671register STRINGPTR rp;
1672FILE *tmpfile;
1673char *name;
1674int printit;
1675{
1676 char *cmnd;
1677
1678 while( rp != NIL(STRING)((STRING*)((void*)0)) ) {
1679 Append_line(cmnd = Expand(rp->st_string), TRUE1, tmpfile, name, printit,0);
1680 FREE(cmnd)free((char*)(cmnd));
1681 rp = rp->st_next;
1682 }
1683}
1684
1685
1686#define NUM_BUCKETS20 20
1687
1688typedef struct strpool {
1689 char *string; /* a pointer to the string value */
1690 uint32 keyval; /* the strings hash value */
1691 struct strpool *next; /* hash table link pointer */
1692} POOL, *POOLPTR;
1693
1694static POOLPTR strings[ NUM_BUCKETS20 ];
1695
1696static char *
1697_pool_lookup( str )/*
1698=====================
1699 Scan down the list of chained strings and see if one of them matches
1700 the string we are looking for. */
1701char *str;
1702{
1703 register POOLPTR key;
1704 uint32 keyval;
1705 uint16 hv;
1706 uint16 keyindex;
1707 char *string;
1708
1709 DB_ENTER( "_pool_lookup" );
1710
1711 if( str == NIL(char)((char*)((void*)0)) ) DB_RETURN("")return ("");
1
Assuming 'str' is not equal to null
2
Taking false branch
1712
1713 hv = Hash(str, &keyval);
1714 key = strings[ keyindex = (hv % NUM_BUCKETS20) ];
1715
1716 while( key != NIL(POOL)((POOL*)((void*)0)) )
3
Assuming 'key' is equal to null
4
Loop condition is false. Execution continues on line 1722
1717 if( (key->keyval != keyval) || strcmp(str, key->string) )
1718 key = key->next;
1719 else
1720 break;
1721
1722 if( key == NIL(POOL)((POOL*)((void*)0)) ) {
5
Taking true branch
1723 DB_PRINT( "pool", ("Adding string [%s]", str) );
1724 TALLOC( key, 1, POOL )if ((key = (POOL*) calloc((unsigned int)(1), (size_t)sizeof(POOL
))) == (POOL*)0) {No_ram();}
; /* not found so add string */
6
Within the expansion of the macro 'TALLOC':
a
Value assigned to 'key'
b
Assuming pointer value is null
1725
1726 key->string = string = DmStrDup(str);
7
Access to field 'string' results in a dereference of a null pointer (loaded from variable 'key')
1727 key->keyval = keyval;
1728
1729 key->next = strings[ keyindex ];
1730 strings[ keyindex ] = key;
1731 }
1732 else {
1733 DB_PRINT( "pool", ("Found string [%s], key->string") );
1734 string = key->string;
1735 }
1736
1737 DB_RETURN( string )return (string);
1738}
1739
1740
1741void
1742Unmake( cp )/*
1743==============
1744 Remove flags indicating that a target was previously made. This
1745 is used for infered makefiles. */
1746CELLPTR cp;
1747{
1748 LINKPTR dp, ep;
1749 CELLPTR tcp, pcp;
1750
1751 DB_ENTER( "Unmake" );
1752
1753 for(dp=CeMeToo(cp)&((cp)->ce_all); dp; dp=dp->cl_next) {
1754 tcp = dp->cl_prq;
1755
1756 /* Unmake the prerequisites. */
1757 for( ep = tcp->ce_prq; ep != NIL(LINK)((LINK*)((void*)0)); ep = ep->cl_next ) {
1758 pcp = ep->cl_prq;
1759
1760 Unmake(pcp);
1761 }
1762 DB_PRINT( "unmake", ("Unmake [%s]", tcp->CE_NAME) );
1763
1764 tcp->ce_flag &= ~(F_MADE0x8000|F_VISITED0x0080|F_STAT0x0040);
1765 tcp->ce_time = (time_t)0L;
1766 }
1767
1768 DB_VOID_RETURNreturn;
1769}