| File: | dmake/rulparse.c |
| Location: | line 1489, column 8 |
| Description: | Access to field 'ce_link' results in a dereference of a null pointer (loaded from variable 'pq') |
| 1 | /* | |||
| 2 | -- | |||
| 3 | -- SYNOPSIS | |||
| 4 | -- Perform semantic analysis on input | |||
| 5 | -- | |||
| 6 | -- DESCRIPTION | |||
| 7 | -- This code performs semantic analysis on the input, and builds | |||
| 8 | -- the complex internal datastructure that is used to represent | |||
| 9 | -- the user makefile. | |||
| 10 | -- | |||
| 11 | -- AUTHOR | |||
| 12 | -- Dennis Vadura, dvadura@dmake.wticorp.com | |||
| 13 | -- | |||
| 14 | -- WWW | |||
| 15 | -- http://dmake.wticorp.com/ | |||
| 16 | -- | |||
| 17 | -- COPYRIGHT | |||
| 18 | -- Copyright (c) 1996,1997 by WTI Corp. All rights reserved. | |||
| 19 | -- | |||
| 20 | -- This program is NOT free software; you can redistribute it and/or | |||
| 21 | -- modify it under the terms of the Software License Agreement Provided | |||
| 22 | -- in the file <distribution-root>/readme/license.txt. | |||
| 23 | -- | |||
| 24 | -- LOG | |||
| 25 | -- Use cvs log to obtain detailed change logs. | |||
| 26 | */ | |||
| 27 | ||||
| 28 | #include "extern.h" | |||
| 29 | ||||
| 30 | /* prototypes for local functions */ | |||
| 31 | static void _add_indirect_prereq ANSI((CELLPTR))(CELLPTR); | |||
| 32 | static int _add_root ANSI((CELLPTR))(CELLPTR); | |||
| 33 | static CELLPTR _build_graph ANSI((int, CELLPTR, CELLPTR))(int, CELLPTR, CELLPTR); | |||
| 34 | static char* _build_meta ANSI((char*))(char*); | |||
| 35 | static int _do_magic ANSI((int, char*, CELLPTR, CELLPTR, t_attr, char*))(int, char*, CELLPTR, CELLPTR, t_attr, char*); | |||
| 36 | static void _do_special ANSI((int, int, t_attr,char*,CELLPTR,CELLPTR,int*))(int, int, t_attr,char*,CELLPTR,CELLPTR,int*); | |||
| 37 | static int _do_targets ANSI((int, t_attr, char*, CELLPTR, CELLPTR))(int, t_attr, char*, CELLPTR, CELLPTR); | |||
| 38 | static t_attr _is_attribute ANSI((char*))(char*); | |||
| 39 | static int _is_special ANSI((char*))(char*); | |||
| 40 | static char* _is_magic ANSI((char*))(char*); | |||
| 41 | static int _is_percent ANSI((char*))(char*); | |||
| 42 | static CELLPTR _make_multi ANSI((CELLPTR))(CELLPTR); | |||
| 43 | static CELLPTR _replace_cell ANSI((CELLPTR,CELLPTR,CELLPTR))(CELLPTR,CELLPTR,CELLPTR); | |||
| 44 | static void _set_attributes ANSI((t_attr, char*, CELLPTR ))(t_attr, char*, CELLPTR ); | |||
| 45 | static void _stick_at_head ANSI((CELLPTR, CELLPTR))(CELLPTR, CELLPTR); | |||
| 46 | static void _set_global_attr ANSI((t_attr))(t_attr); | |||
| 47 | ||||
| 48 | ||||
| 49 | /* static variables that must persist across invocation of Parse_rule_def */ | |||
| 50 | static CELLPTR _sv_targets = NIL(CELL)((CELL*)((void*)0)); | |||
| 51 | static STRINGPTR _sv_rules = NIL(STRING)((STRING*)((void*)0)); /* first recipe element. */ | |||
| 52 | static STRINGPTR _sv_crule = NIL(STRING)((STRING*)((void*)0)); /* current/last recipe element. */ | |||
| 53 | static CELLPTR _sv_edgel = NIL(CELL)((CELL*)((void*)0)); | |||
| 54 | static LINKPTR _sv_ind_prq = NIL(LINK)((LINK*)((void*)0)); /* indirect prerequisites for % cell */ | |||
| 55 | static int _sp_target = FALSE0; | |||
| 56 | static t_attr _sv_attr; | |||
| 57 | static int _sv_flag; | |||
| 58 | static int _sv_op; | |||
| 59 | static char *_sv_setdir; | |||
| 60 | static char _sv_globprq_only = 0; | |||
| 61 | ||||
| 62 | /* Define for global attribute mask (A_SWAP == A_WINPATH) */ | |||
| 63 | #define A_GLOB(0x00001 | 0x00002 | 0x00020 | 0x00008 | 0x01000 | 0x00800 | 0x00010 | 0x00080 | 0x00200 | 0x02000 ) (A_PRECIOUS0x00001 | A_SILENT0x00002 | A_IGNORE0x00020 | A_EPILOG0x00008 | A_SWAP0x01000 |\ | |||
| 64 | A_SHELL0x00800 | A_PROLOG0x00010 | A_NOINFER0x00080 | A_SEQ0x00200 | A_MKSARGS0x02000 ) | |||
| 65 | ||||
| 66 | ||||
| 67 | PUBLIC int | |||
| 68 | Parse_rule_def( state )/* | |||
| 69 | ========================= | |||
| 70 | Parse the rule definition contained in Buffer, and modify the state | |||
| 71 | if appropriate. The function returns 0, if the definition is found to | |||
| 72 | be an illegal rule definition, and it returns 1 if it is a rule definition. | |||
| 73 | */ | |||
| 74 | int *state; | |||
| 75 | { | |||
| 76 | TKSTR input; /* input string struct for token search */ | |||
| 77 | CELLPTR targets; /* list of targets if any */ | |||
| 78 | CELLPTR prereq; /* list of prereq if any */ | |||
| 79 | CELLPTR prereqtail; /* tail of prerequisite list */ | |||
| 80 | CELLPTR cp; /* temporary cell pointer for list making */ | |||
| 81 | char *result; /* temporary storage for result */ | |||
| 82 | char *tok; /* temporary pointer for tokens */ | |||
| 83 | char *set_dir; /* value of setdir attribute */ | |||
| 84 | char *brk; /* break char list for Get_token */ | |||
| 85 | char *firstrcp; /* first recipe line, from ; in rule line */ | |||
| 86 | t_attr attr; /* sum of attribute flags for current tgts*/ | |||
| 87 | t_attr at; /* temp place to keep an attribute code */ | |||
| 88 | int op; /* rule operator */ | |||
| 89 | int special; /* indicate special targets in rule */ | |||
| 90 | int augmeta; /* indicate .<suffix> like target */ | |||
| 91 | int percent; /* indicate percent rule target */ | |||
| 92 | int percent_prq; /* indicate mixed %-rule prereq possible */ | |||
| 93 | ||||
| 94 | DB_ENTER( "Parse_rule_def" ); | |||
| 95 | ||||
| 96 | op = 0; | |||
| 97 | attr = 0; | |||
| 98 | special = 0; | |||
| 99 | augmeta = 0; | |||
| 100 | percent = 0; | |||
| 101 | set_dir = NIL( char )((char*)((void*)0)); | |||
| 102 | targets = NIL(CELL)((CELL*)((void*)0)); | |||
| 103 | prereq = NIL(CELL)((CELL*)((void*)0)); | |||
| 104 | prereqtail = NIL(CELL)((CELL*)((void*)0)); | |||
| 105 | percent_prq = 0; | |||
| 106 | ||||
| 107 | /* Check to see if the line is of the form: | |||
| 108 | * targets : prerequisites; first recipe line | |||
| 109 | * If so remember the first_recipe part of the line. */ | |||
| 110 | ||||
| 111 | firstrcp = strchr( Buffer, ';' ); | |||
| 112 | if( firstrcp != NIL( char )((char*)((void*)0)) ) { | |||
| 113 | *firstrcp++ = 0; | |||
| 114 | firstrcp = DmStrSpn( firstrcp, " \t" ); | |||
| 115 | } | |||
| 116 | ||||
| 117 | result = Expand( Buffer ); | |||
| 118 | /* Remove CONTINUATION_CHAR, keep the <nl> */ | |||
| 119 | for( brk=strchr(result,CONTINUATION_CHAR'\\'); brk != NIL(char)((char*)((void*)0)); brk=strchr(brk,CONTINUATION_CHAR'\\') ) | |||
| 120 | if( brk[1] == '\n' ) | |||
| 121 | *brk = ' '; | |||
| 122 | else | |||
| 123 | brk++; | |||
| 124 | ||||
| 125 | DB_PRINT( "par", ("Scanning: [%s]", result) ); | |||
| 126 | ||||
| 127 | SET_TOKEN( &input, result )(&input)->tk_str = (result); (&input)->tk_cchar = *(result); (&input)->tk_quote = 1;; | |||
| 128 | brk = ":-^!|"; | |||
| 129 | Def_targets = TRUE1; | |||
| 130 | ||||
| 131 | /* Scan the input rule line collecting targets, the operator, and any | |||
| 132 | * prerequisites. Stop when we run out of targets and prerequisites. */ | |||
| 133 | ||||
| 134 | while( *(tok = Get_token( &input, brk, TRUE1 )) != '\0' ) | |||
| 135 | if( !op ) { | |||
| 136 | /* we are scanning targets and attributes | |||
| 137 | * check to see if token is an operator. */ | |||
| 138 | ||||
| 139 | op = Rule_op( tok ); | |||
| 140 | ||||
| 141 | if( !op ) { | |||
| 142 | /* Define a new cell, or get pointer to pre-existing cell. */ | |||
| 143 | /* Do we need cells for attributes? If not move the definition | |||
| 144 | * to the target part. */ | |||
| 145 | cp = Def_cell( tok ); | |||
| 146 | /* A $ character indicates either a literal $ in the pathname (this | |||
| 147 | * was broken before) or a dynamic macro (this is a syntax error). | |||
| 148 | * FIXME: Here would be the place to add a sanity check. */ | |||
| 149 | DB_PRINT( "par", ("tg_cell [%s]", tok) ); | |||
| 150 | ||||
| 151 | if( (at = _is_attribute(tok)) != 0 ) { | |||
| 152 | /* Ignore .SILENT when -vr is active. */ | |||
| 153 | if( (Verbose & V_FORCEECHO0x80) && (at == A_SILENT0x00002) ) | |||
| 154 | at = 0; | |||
| 155 | ||||
| 156 | /* Logically OR the attributes specified into one main | |||
| 157 | * ATTRIBUTE mask. */ | |||
| 158 | ||||
| 159 | if( at == A_SETDIR0x00400 ) { | |||
| 160 | if( set_dir != NIL( char )((char*)((void*)0)) ) | |||
| 161 | Warning( "Multiple .SETDIR attribute ignored" ); | |||
| 162 | else | |||
| 163 | set_dir = DmStrDup( tok ); | |||
| 164 | } | |||
| 165 | ||||
| 166 | attr |= at; | |||
| 167 | } | |||
| 168 | else { | |||
| 169 | /* Not an attribute, this must be a target. */ | |||
| 170 | int tmp; | |||
| 171 | ||||
| 172 | tmp = _is_special( tok ); | |||
| 173 | ||||
| 174 | if( _is_percent( tok ) ) { | |||
| 175 | /* First %-target checks if there were non-%-targets before. */ | |||
| 176 | if( !percent && targets != NIL(CELL)((CELL*)((void*)0)) ) | |||
| 177 | Fatal( "A %%-target must not be mixed with non-%%-targets, offending target [%s]", tok ); | |||
| 178 | ||||
| 179 | percent++; | |||
| 180 | cp->ce_flag |= F_PERCENT0x0800; | |||
| 181 | } else { | |||
| 182 | if( percent ) | |||
| 183 | Fatal( "A non-%%-target must not be mixed with %%-targets, offending target [%s]", tok ); | |||
| 184 | } | |||
| 185 | ||||
| 186 | if( _is_magic( tok ) ) { | |||
| 187 | /* Check that AUGMAKE targets are not mixed with other | |||
| 188 | * targets. The return value of _is_magic() is discarded and | |||
| 189 | * calculated again in _do_targets() if this rule definition | |||
| 190 | * really is a .<suffix> like target. | |||
| 191 | * If we would allow only one target per line we could easily | |||
| 192 | * store the result for later, but for multiple .<suffix> | |||
| 193 | * targets this creates too much overhead. | |||
| 194 | * These targets should be rare (obsolete?) anyway. */ | |||
| 195 | if( !augmeta && targets != NIL(CELL)((CELL*)((void*)0)) ) | |||
| 196 | Fatal( "An AUGMAKE meta target must not be mixed with non AUGMAKE meta targets, offending target [%s]", tok ); | |||
| 197 | ||||
| 198 | augmeta++; | |||
| 199 | cp->ce_flag |= F_MAGIC0x2000; /* do_magic will also add F_PERCENT later. */ | |||
| 200 | } else { | |||
| 201 | if( augmeta ) | |||
| 202 | Fatal( "A non AUGMAKE meta target must not be mixed with AUGMAKE meta targets, offending target [%s]", tok ); | |||
| 203 | } | |||
| 204 | ||||
| 205 | if( special ) | |||
| 206 | Fatal( "Special target must appear alone, found [%s]", tok ); | |||
| 207 | else if( !(cp->ce_flag & F_MARK0x0001) ) { | |||
| 208 | /* Targets are kept in this list in lexically sorted order. | |||
| 209 | * This allows for easy equality comparison of target | |||
| 210 | * sets.*/ | |||
| 211 | CELLPTR prev,cur; | |||
| 212 | for(prev=NIL(CELL)((CELL*)((void*)0)),cur=targets;cur;prev=cur,cur=cur->ce_link) | |||
| 213 | if(strcmp(cur->CE_NAMEce_name->ht_name,cp->CE_NAMEce_name->ht_name) > 0) | |||
| 214 | break; | |||
| 215 | ||||
| 216 | cp->ce_link = cur; | |||
| 217 | ||||
| 218 | if (!prev) | |||
| 219 | targets = cp; | |||
| 220 | else | |||
| 221 | prev->ce_link = cp; | |||
| 222 | ||||
| 223 | cp->ce_flag |= F_MARK0x0001 | F_EXPLICIT0x0400; | |||
| 224 | special = tmp; | |||
| 225 | } | |||
| 226 | else | |||
| 227 | Warning( "Duplicate target [%s]", cp->CE_NAMEce_name->ht_name ); | |||
| 228 | } | |||
| 229 | } | |||
| 230 | else { | |||
| 231 | /* found an operator so empty out break list and clear mark | |||
| 232 | * bits on target list, setting them all to F_VISITED*/ | |||
| 233 | ||||
| 234 | brk = ""; | |||
| 235 | for( cp=targets; cp != NIL(CELL)((CELL*)((void*)0)); cp=cp->ce_link ) { | |||
| 236 | cp->ce_flag ^= F_MARK0x0001; | |||
| 237 | cp->ce_flag |= F_VISITED0x0080; | |||
| 238 | } | |||
| 239 | ||||
| 240 | Def_targets = FALSE0; | |||
| 241 | } | |||
| 242 | } | |||
| 243 | else { | |||
| 244 | /* Scanning prerequisites so build the prerequisite list. We use | |||
| 245 | * F_MARK flag to make certain we have only a single copy of the | |||
| 246 | * prerequisite in the list */ | |||
| 247 | ||||
| 248 | cp = Def_cell( tok ); | |||
| 249 | ||||
| 250 | /* %-prerequisits require eiter a %-target or this might be a rule of | |||
| 251 | * the "ATTRIBUTE_LIST : targets" form. */ | |||
| 252 | if( _is_percent( tok ) ) { | |||
| 253 | if( percent || ((targets == NIL(CELL)((CELL*)((void*)0))) && attr) ) | |||
| 254 | percent_prq = 1; | |||
| 255 | else | |||
| 256 | Fatal( "Syntax error in %% rule, missing %% target"); | |||
| 257 | } | |||
| 258 | ||||
| 259 | if( cp->ce_flag & F_VISITED0x0080 ) { | |||
| 260 | if( cp->ce_attr & A_COMPOSITE0x200000 ) | |||
| 261 | continue; | |||
| 262 | else | |||
| 263 | Fatal( "Detected circular dependency in graph at [%s]", | |||
| 264 | cp->CE_NAMEce_name->ht_name ); | |||
| 265 | } | |||
| 266 | else if( !(cp->ce_flag & F_MARK0x0001) ) { | |||
| 267 | DB_PRINT( "par", ("pq_cell [%s]", tok) ); | |||
| 268 | cp->ce_flag |= F_MARK0x0001; | |||
| 269 | ||||
| 270 | if( prereqtail == NIL(CELL)((CELL*)((void*)0)) ) /* keep prereq's in order */ | |||
| 271 | prereq = cp; | |||
| 272 | else | |||
| 273 | prereqtail->ce_link = cp; | |||
| 274 | ||||
| 275 | prereqtail = cp; | |||
| 276 | cp->ce_link = NIL(CELL)((CELL*)((void*)0)); | |||
| 277 | } | |||
| 278 | else if( !(cp->ce_attr & A_LIBRARY0x00004) && (Verbose & V_WARNALL0x40)) | |||
| 279 | Warning("Duplicate entry [%s] in prerequisite list",cp->CE_NAMEce_name->ht_name); | |||
| 280 | } | |||
| 281 | ||||
| 282 | /* Check to see if we have a percent rule that has only global | |||
| 283 | * prerequisites, i.e. they are of the form: "%.a : foo". | |||
| 284 | * If so then set the flag so that later on, we don't issue | |||
| 285 | * an error if such targets supply an empty set of rules. */ | |||
| 286 | ||||
| 287 | if( percent && !percent_prq && (prereq != NIL(CELL)((CELL*)((void*)0))) ) | |||
| 288 | _sv_globprq_only = 1; | |||
| 289 | ||||
| 290 | /* It's ok to have targets with attributes, and no prerequisites, but it's | |||
| 291 | * not ok to have no targets and no attributes, or no operator */ | |||
| 292 | ||||
| 293 | CLEAR_TOKEN( &input )*(&input)->tk_str = (&input)->tk_cchar; FREE(result)free((char*)(result)); result = NIL(char)((char*)((void*)0)); | |||
| 294 | if( !op ) { | |||
| 295 | DB_PRINT( "par", ("Not a rule [%s]", Buffer) ); | |||
| 296 | DB_RETURN( 0 )return (0); | |||
| 297 | } | |||
| 298 | ||||
| 299 | /* More than one percent target didn't work with prior versions. */ | |||
| 300 | #if 0 | |||
| 301 | if( (percent > 1) && !(op & R_OP_OR32) ) | |||
| 302 | Warning( "Prior to dmake 4.5 only one\n" | |||
| 303 | "%%-target per target-definition worked reliably. Check your makefiles.\n" ); | |||
| 304 | #endif | |||
| 305 | ||||
| 306 | if( !attr && targets == NIL(CELL)((CELL*)((void*)0)) ) { | |||
| 307 | Fatal( "Missing targets or attributes in rule" ); | |||
| 308 | if( set_dir != NIL( char )((char*)((void*)0))) FREE( set_dir )free((char*)(set_dir)); | |||
| 309 | DB_RETURN( 0 )return (0); | |||
| 310 | } | |||
| 311 | ||||
| 312 | /* We have established we have a legal rules line, so we must process it. | |||
| 313 | * In doing so we must handle any special targets. Special targets must | |||
| 314 | * appear alone possibly accompanied by attributes. | |||
| 315 | * NOTE: special != 0 ==> targets != NIL(CELL) */ | |||
| 316 | ||||
| 317 | if( prereqtail != NIL(CELL)((CELL*)((void*)0)) ) prereqtail->ce_link = NIL(CELL)((CELL*)((void*)0)); | |||
| 318 | ||||
| 319 | /* Clear out MARK bits used in duplicate checking. I originally wanted | |||
| 320 | * to do this as the lists get processed but that got too error prone | |||
| 321 | * so I bit the bullit and added these two loops. */ | |||
| 322 | ||||
| 323 | for( cp=prereq; cp != NIL(CELL)((CELL*)((void*)0)); cp=cp->ce_link ) cp->ce_flag &= ~F_MARK0x0001; | |||
| 324 | for( cp=targets; cp != NIL(CELL)((CELL*)((void*)0)); cp=cp->ce_link ) cp->ce_flag &= ~F_VISITED0x0080; | |||
| 325 | ||||
| 326 | /* Check to see if the previous recipe was bound, if not the call | |||
| 327 | * Bind_rules_to_targets() to bind the recipe (_sv_rules) to the | |||
| 328 | * target(s) (_sv_targets). */ | |||
| 329 | /* was: if( _sv_rules != NIL(STRING) ) Bind_rules_to_targets( F_DEFAULT );*/ | |||
| 330 | /* Only Add_recipe_to_list() sets _sv_rules and Bind_rules_to_targets() | |||
| 331 | * clears the (static) variables again. Bind_rules_to_targets() is | |||
| 332 | * (should be) called after State is leaving RULE_SCAN in Parse(). | |||
| 333 | * Abort if there are unbound recipes. FIXME: Remove this paragraph | |||
| 334 | * if this never occurs. */ | |||
| 335 | if( _sv_rules != NIL(STRING)((STRING*)((void*)0)) ) | |||
| 336 | Fatal( "Internal Error: _sv_rules not empty." ); | |||
| 337 | ||||
| 338 | /* Add the first recipe line to the list */ | |||
| 339 | if( firstrcp != NIL( char )((char*)((void*)0)) ) | |||
| 340 | Add_recipe_to_list( firstrcp, TRUE1, FALSE0 ); | |||
| 341 | ||||
| 342 | /* Save these prior to calling _do_targets, since _build_graph needs the | |||
| 343 | * _sv_setdir value for matching edges. */ | |||
| 344 | _sv_op = op; | |||
| 345 | _sv_setdir = set_dir; | |||
| 346 | ||||
| 347 | if( special ) | |||
| 348 | /* _do_special() can alter *state */ | |||
| 349 | _do_special( special, op, attr, set_dir, targets, prereq, state ); | |||
| 350 | else | |||
| 351 | *state = _do_targets( op, attr, set_dir, targets, prereq ); | |||
| 352 | ||||
| 353 | if( (*state != RULE_SCAN1) && (_sv_rules != NIL(STRING)((STRING*)((void*)0))) ) | |||
| 354 | Fatal( "Unexpected recipe found." ); | |||
| 355 | ||||
| 356 | DB_RETURN( 1 )return (1); | |||
| 357 | } | |||
| 358 | ||||
| 359 | ||||
| 360 | PUBLIC int | |||
| 361 | Rule_op( op )/* | |||
| 362 | ================ | |||
| 363 | Check the passed in op string and map it to one of the rule operators */ | |||
| 364 | char *op; | |||
| 365 | { | |||
| 366 | int ret = 0; | |||
| 367 | ||||
| 368 | DB_ENTER( "rule_op" ); | |||
| 369 | ||||
| 370 | if( *op == TGT_DEP_SEP':' ) { | |||
| 371 | ret = R_OP_CL1; | |||
| 372 | op++; | |||
| 373 | ||||
| 374 | /* All rule operations begin with a :, but may include any one of the | |||
| 375 | * four modifiers. In order for the rule to be properly mapped we must | |||
| 376 | * check for each of the modifiers in turn, building up our return bit | |||
| 377 | * string. */ | |||
| 378 | ||||
| 379 | while( *op && ret ) | |||
| 380 | switch( *op ) { | |||
| 381 | case ':': ret |= R_OP_DCL2; op++; break; | |||
| 382 | case '!': ret |= R_OP_BG4; op++; break; | |||
| 383 | case '^': ret |= R_OP_UP8; op++; break; | |||
| 384 | case '-': ret |= R_OP_MI16; op++; break; | |||
| 385 | case '|': ret |= R_OP_OR32; op++; break; | |||
| 386 | ||||
| 387 | default : ret = 0; /* an invalid modifier, chuck whole string */ | |||
| 388 | } | |||
| 389 | ||||
| 390 | if( *op != '\0' ) ret = 0; | |||
| 391 | } | |||
| 392 | ||||
| 393 | DB_RETURN( ret )return (ret); | |||
| 394 | } | |||
| 395 | ||||
| 396 | ||||
| 397 | PUBLIC void | |||
| 398 | Add_recipe_to_list( rule, white_too, no_check )/* | |||
| 399 | ================================================= | |||
| 400 | Take the provided string and add it to the list of recipe lines | |||
| 401 | we are saving to be added to the list of targets we have built | |||
| 402 | previously. If white_too == TRUE add the rule EVEN IF it contains only | |||
| 403 | an empty string (whitespace is handled by Def_recipe()). */ | |||
| 404 | char *rule; | |||
| 405 | int white_too; | |||
| 406 | int no_check; | |||
| 407 | { | |||
| 408 | DB_ENTER( "Add_recipe_to_list" ); | |||
| 409 | ||||
| 410 | if( rule != NIL( char )((char*)((void*)0)) && (*rule != '\0' || white_too) ) { | |||
| 411 | DB_PRINT( "par", ("Adding recipe [%s]", rule) ); | |||
| 412 | _sv_crule = Def_recipe( rule, _sv_crule, white_too, no_check ); | |||
| 413 | ||||
| 414 | /* If _sv_rules is not yet set this must be the first recipe line, | |||
| 415 | * remember it. */ | |||
| 416 | if( _sv_rules == NIL(STRING)((STRING*)((void*)0)) ) | |||
| 417 | _sv_rules = _sv_crule; | |||
| 418 | } | |||
| 419 | ||||
| 420 | DB_VOID_RETURNreturn; | |||
| 421 | } | |||
| 422 | ||||
| 423 | ||||
| 424 | PUBLIC void | |||
| 425 | Bind_rules_to_targets( flag )/* | |||
| 426 | =============================== | |||
| 427 | Take the recipe lines we have defined and bind them with proper attributes | |||
| 428 | to the targets that were previously defined in the parse. The | |||
| 429 | attributes that get passed here are merged with those that are were | |||
| 430 | previously defined. (namely attribute F_SINGLE) */ | |||
| 431 | int flag; | |||
| 432 | { | |||
| 433 | CELLPTR tg; /* pointer to current target in list */ | |||
| 434 | LINKPTR lp; /* pointer to link cell */ | |||
| 435 | int magic; /* TRUE if target of % or .xxx.yyy form */ | |||
| 436 | int tflag; /* TRUE if we assigned targets here */ | |||
| 437 | ||||
| 438 | DB_ENTER( "Bind_rules_to_targets" ); | |||
| 439 | ||||
| 440 | /* This line is needed since Parse may call us twice when the last | |||
| 441 | * GROUP rule appears at the end of file. In this case the rules | |||
| 442 | * have already been bound and we want to ignore them. */ | |||
| 443 | ||||
| 444 | if( _sv_targets == NIL(CELL)((CELL*)((void*)0)) ) { DB_VOID_RETURNreturn; } | |||
| 445 | ||||
| 446 | tflag = FALSE0; | |||
| 447 | flag |= (_sv_flag & F_SINGLE0x0004); | |||
| 448 | flag |= ((_sv_attr & A_GROUP0x02000000) ? F_GROUP0x0020 : 0); | |||
| 449 | ||||
| 450 | for( tg = _sv_targets; tg != NIL(CELL)((CELL*)((void*)0)); tg = tg->ce_link ) { | |||
| 451 | DB_PRINT( "par", ("Binding to %s, %04x", tg->CE_NAME, tg->ce_flag) ); | |||
| 452 | magic = tg->ce_flag & F_PERCENT0x0800; | |||
| 453 | ||||
| 454 | ||||
| 455 | /* NOTE: For targets that are magic or special we ignore any | |||
| 456 | * previously defined rules, ie. We throw away the old definition | |||
| 457 | * and use the new, otherwise we complain. */ | |||
| 458 | if( !(tg->ce_flag & F_MULTI0x0002) && !magic && (tg->CE_RECIPEce_recipe != NIL(STRING)((STRING*)((void*)0))) | |||
| 459 | && !_sp_target && (_sv_rules != NIL(STRING)((STRING*)((void*)0))) ) | |||
| 460 | Fatal( "Multiply defined recipe for target %s", tg->CE_NAMEce_name->ht_name ); | |||
| 461 | ||||
| 462 | if( (magic || _sp_target) && (_sv_rules == NIL(STRING)((STRING*)((void*)0))) && | |||
| 463 | !(tg->ce_flag & F_SPECIAL0x0100) && !_sv_globprq_only ) | |||
| 464 | Warning( "Empty recipe for special or meta target %s", tg->CE_NAMEce_name->ht_name ); | |||
| 465 | ||||
| 466 | if( magic ) { | |||
| 467 | CELLPTR ep; | |||
| 468 | ||||
| 469 | for( ep=_sv_edgel; ep != NIL(CELL)((CELL*)((void*)0)); ep=ep->ce_link ) { | |||
| 470 | DB_PRINT( "par", ("ep address: %#x", ep) ); | |||
| 471 | /* %.xx :| '%.yy' abc xx '%.tt' ; touch $@ | |||
| 472 | * loops here ... */ | |||
| 473 | _set_attributes( _sv_attr, _sv_setdir, ep ); | |||
| 474 | ep->ce_flag |= (F_TARGET0x0008|flag); | |||
| 475 | ||||
| 476 | if( _sv_rules != NIL(STRING)((STRING*)((void*)0)) ) { | |||
| 477 | ep->ce_recipe = _sv_rules; | |||
| 478 | ep->ce_indprq = _sv_ind_prq; | |||
| 479 | } | |||
| 480 | } | |||
| 481 | } | |||
| 482 | else { | |||
| 483 | tg->ce_attr |= _sv_attr; | |||
| 484 | tg->ce_flag |= flag; | |||
| 485 | ||||
| 486 | if( _sv_rules != NIL(STRING)((STRING*)((void*)0)) ) { | |||
| 487 | tg->ce_recipe = _sv_rules; | |||
| 488 | tg->ce_flag |= F_RULES0x0010 | F_TARGET0x0008; | |||
| 489 | ||||
| 490 | /* Bind the current set of prerequisites as belonging to the | |||
| 491 | * original recipe given for the target */ | |||
| 492 | for( lp=tg->ce_prq; lp != NIL(LINK)((LINK*)((void*)0)); lp = lp->cl_next ) | |||
| 493 | if( !(lp->cl_flag & F_VISITED0x0080) ) lp->cl_flag |= F_TARGET0x0008; | |||
| 494 | } | |||
| 495 | else for( lp=tg->ce_prq; lp != NIL(LINK)((LINK*)((void*)0)); lp = lp->cl_next ) | |||
| 496 | lp->cl_flag |= F_VISITED0x0080; | |||
| 497 | } | |||
| 498 | ||||
| 499 | tflag |= _add_root(tg); | |||
| 500 | } | |||
| 501 | ||||
| 502 | if( tflag ) Target = TRUE1; | |||
| 503 | if( _sv_setdir ) FREE(_sv_setdir)free((char*)(_sv_setdir)); | |||
| 504 | _sv_rules = NIL(STRING)((STRING*)((void*)0)); | |||
| 505 | _sv_crule = NIL(STRING)((STRING*)((void*)0)); | |||
| 506 | _sv_targets = NIL(CELL)((CELL*)((void*)0)); | |||
| 507 | _sv_ind_prq = NIL(LINK)((LINK*)((void*)0)); | |||
| 508 | _sv_edgel = NIL(CELL)((CELL*)((void*)0)); | |||
| 509 | _sp_target = FALSE0; | |||
| 510 | _sv_globprq_only = 0; | |||
| 511 | ||||
| 512 | DB_VOID_RETURNreturn; | |||
| 513 | } | |||
| 514 | ||||
| 515 | ||||
| 516 | ||||
| 517 | PUBLIC int | |||
| 518 | Set_group_attributes( list )/* | |||
| 519 | ============================== | |||
| 520 | Scan list looking for the standard @,-,% and + (as in recipe line | |||
| 521 | defs) (+ is set but ignored for group recipes) | |||
| 522 | and set the flags accordingly so that they apply when we bind the | |||
| 523 | rules to the appropriate targets. | |||
| 524 | Return TRUE if group recipe start '[' was found, otherwise FALSE. */ | |||
| 525 | char *list; | |||
| 526 | { | |||
| 527 | int res = FALSE0; | |||
| 528 | char *s; | |||
| 529 | ||||
| 530 | if ( !((_sv_attr|Glob_attr)&A_IGNOREGROUP0x10000) ) { | |||
| 531 | s = DmStrSpn(list,"@-%+ \t"); | |||
| 532 | res = (*s == '['); | |||
| 533 | if( res ) { | |||
| 534 | /* Check for non-white space characters after the [. */ | |||
| 535 | for( s++; *s && iswhite(*s)((*s == ' ') || (*s == '\t')) ; s++ ) | |||
| 536 | ; | |||
| 537 | if( *s ) | |||
| 538 | Warning("Found non-white space character after '[' in [%s].", list); | |||
| 539 | ||||
| 540 | _sv_attr |= Rcp_attribute(list); | |||
| 541 | } | |||
| 542 | } | |||
| 543 | ||||
| 544 | return(res); | |||
| 545 | } | |||
| 546 | ||||
| 547 | ||||
| 548 | static void | |||
| 549 | _do_special( special, op, attr, set_dir, target, prereq, state )/* | |||
| 550 | ================================================================== | |||
| 551 | Process a special target (always only a single target). So far the only | |||
| 552 | special targets we have are those recognized by the _is_special function. | |||
| 553 | Some of the special targets can take recipes, they call _do_targets() | |||
| 554 | and (implicitly) set *state to to RULE_SCAN. Otherwise *state remains | |||
| 555 | unaffected, i.e. NORMAL_SCAN. | |||
| 556 | ||||
| 557 | target is always only a single special target. | |||
| 558 | ||||
| 559 | NOTE: For the cases of .IMPORT, and .INCLUDE, the cells created by the | |||
| 560 | parser are never freed. This is due to the fact that it is too much | |||
| 561 | trouble to get them out of the hash table once they are defined, and | |||
| 562 | if they are per chance used again it will be ok, anyway, since the | |||
| 563 | cell is not really used by the code below. */ | |||
| 564 | ||||
| 565 | int special; | |||
| 566 | int op; | |||
| 567 | t_attr attr; | |||
| 568 | char *set_dir; | |||
| 569 | CELLPTR target; | |||
| 570 | CELLPTR prereq; | |||
| 571 | int *state; | |||
| 572 | { | |||
| 573 | HASHPTR hp; /* pointer to macro def cell */ | |||
| 574 | CELLPTR cp; /* temporary pointer into cells list */ | |||
| 575 | CELLPTR dp; /* pointer to directory dir cell */ | |||
| 576 | LINKPTR lp; /* pointer at prerequisite list */ | |||
| 577 | char *dir; /* current dir to prepend */ | |||
| 578 | char *path; /* resulting path to try to read */ | |||
| 579 | char *name; /* File name for processing a .INCLUDE */ | |||
| 580 | char *tmp; /* temporary string pointer */ | |||
| 581 | FILE *fil; /* File descriptor returned by Openfile */ | |||
| 582 | ||||
| 583 | DB_ENTER( "_do_special" ); | |||
| 584 | ||||
| 585 | target->ce_flag = F_SPECIAL0x0100; /* mark the target as special */ | |||
| 586 | ||||
| 587 | switch( special ) { | |||
| 588 | case ST_EXPORT8: | |||
| 589 | for( ; prereq != NIL(CELL)((CELL*)((void*)0)); prereq = prereq->ce_link ) { | |||
| 590 | DB_PRINT( "par", ("Exporting [%s]", prereq->CE_NAME) ); | |||
| 591 | hp = GET_MACRO( prereq->CE_NAME )Get_name(prereq->ce_name->ht_name, Macs, 0); | |||
| 592 | ||||
| 593 | if( hp != NIL(HASH)((HASH*)((void*)0)) ) { | |||
| 594 | char *tmpstr = hp->ht_value; | |||
| 595 | ||||
| 596 | if( tmpstr == NIL(char)((char*)((void*)0)) ) tmpstr = ""; | |||
| 597 | ||||
| 598 | if( Write_env_string( prereq->CE_NAMEce_name->ht_name, tmpstr ) != 0 ) | |||
| 599 | Warning( "Could not export %s", prereq->CE_NAMEce_name->ht_name ); | |||
| 600 | } | |||
| 601 | } | |||
| 602 | break; | |||
| 603 | ||||
| 604 | /* Simply cause the parser to fail on the next input read */ | |||
| 605 | case ST_EXIT12: | |||
| 606 | Skip_to_eof = TRUE1; | |||
| 607 | break; | |||
| 608 | ||||
| 609 | case ST_IMPORT9: | |||
| 610 | for( ; prereq != NIL(CELL)((CELL*)((void*)0)); prereq = prereq->ce_link ) { | |||
| 611 | char *tmpstr; | |||
| 612 | ||||
| 613 | DB_PRINT( "par", ("Importing [%s]", prereq->CE_NAME) ); | |||
| 614 | ||||
| 615 | if( strcmp(prereq->CE_NAMEce_name->ht_name, ".EVERYTHING") == 0 ) { | |||
| 616 | t_attr sattr = Glob_attr; | |||
| 617 | Glob_attr |= A_SILENT0x00002; | |||
| 618 | ||||
| 619 | ReadEnvironment(); | |||
| 620 | ||||
| 621 | Glob_attr = sattr; | |||
| 622 | } | |||
| 623 | else { | |||
| 624 | tmpstr = Read_env_string( prereq->CE_NAMEce_name->ht_name ); | |||
| 625 | ||||
| 626 | if( tmpstr != NIL(char)((char*)((void*)0)) ) | |||
| 627 | Def_macro(prereq->CE_NAMEce_name->ht_name, tmpstr, M_EXPANDED0x0008|M_LITERAL0x0020); | |||
| 628 | else | |||
| 629 | if( !((Glob_attr | attr) & A_IGNORE0x00020) ) | |||
| 630 | Fatal("Imported macro `%s' not found",prereq->CE_NAMEce_name->ht_name); | |||
| 631 | } | |||
| 632 | } | |||
| 633 | ||||
| 634 | attr &= ~A_IGNORE0x00020; | |||
| 635 | break; | |||
| 636 | ||||
| 637 | case ST_INCLUDE5: | |||
| 638 | { | |||
| 639 | int pushed = FALSE0; | |||
| 640 | int first = (attr & A_FIRST0x20000000); | |||
| 641 | int ignore = (((Glob_attr | attr) & A_IGNORE0x00020) != 0); | |||
| 642 | int found = FALSE0; | |||
| 643 | int noinf = (attr & A_NOINFER0x00080); | |||
| 644 | LINKPTR prqlnk = NIL(LINK)((LINK*)((void*)0)); | |||
| 645 | LINKPTR prqlst = NIL(LINK)((LINK*)((void*)0)); | |||
| 646 | ||||
| 647 | if( prereq == NIL(CELL)((CELL*)((void*)0)) ) Fatal( "No .INCLUDE file(s) specified" ); | |||
| 648 | ||||
| 649 | dp = Def_cell( ".INCLUDEDIRS" ); | |||
| 650 | ||||
| 651 | if( (attr & A_SETDIR0x00400) && *(dir = strchr(set_dir, '=')+1) ) | |||
| 652 | pushed = Push_dir( dir, ".INCLUDE", ignore ); | |||
| 653 | ||||
| 654 | for( cp=prereq; cp != NIL(CELL)((CELL*)((void*)0)); cp = cp->ce_link ) { | |||
| 655 | LINKPTR ltmp; | |||
| 656 | TALLOC(ltmp, 1, LINK)if ((ltmp = (LINK*) calloc((unsigned int)(1), (size_t)sizeof( LINK))) == (LINK*)0) {No_ram();}; | |||
| 657 | ltmp->cl_prq = cp; | |||
| 658 | ||||
| 659 | if( prqlnk == NIL(LINK)((LINK*)((void*)0)) ) | |||
| 660 | prqlst = ltmp; | |||
| 661 | else | |||
| 662 | prqlnk->cl_next = ltmp; | |||
| 663 | ||||
| 664 | prqlnk = ltmp; | |||
| 665 | } | |||
| 666 | ||||
| 667 | for( ; prqlst != NIL(LINK)((LINK*)((void*)0)); FREE(prqlst)free((char*)(prqlst)), prqlst=prqlnk ) { | |||
| 668 | prqlnk = prqlst->cl_next; | |||
| 669 | cp = prqlst->cl_prq; | |||
| 670 | name = cp->CE_NAMEce_name->ht_name; | |||
| 671 | ||||
| 672 | /* Leave this here, it ensures that prqlst gets propely free'd */ | |||
| 673 | if ( first && found ) | |||
| 674 | continue; | |||
| 675 | ||||
| 676 | if( *name == '<' ) { | |||
| 677 | /* We have a file name enclosed in <....> | |||
| 678 | * so get rid of the <> arround the file name */ | |||
| 679 | ||||
| 680 | name++; | |||
| 681 | if( (tmp = strrchr( name, '>' )) != NIL( char )((char*)((void*)0)) ) | |||
| 682 | *tmp = 0; | |||
| 683 | ||||
| 684 | if( If_root_path( name ) ) | |||
| 685 | fil = Openfile( name, FALSE0, FALSE0 ); | |||
| 686 | else | |||
| 687 | fil = NIL(FILE)((FILE*)((void*)0)); | |||
| 688 | } | |||
| 689 | else | |||
| 690 | fil = Openfile( name, FALSE0, FALSE0 ); | |||
| 691 | ||||
| 692 | if( fil == NIL(FILE)((FILE*)((void*)0)) && !If_root_path( name ) ) { /*if true ==> not found in current dir*/ | |||
| 693 | ||||
| 694 | /* Now we must scan the list of prerequisites for .INCLUDEDIRS | |||
| 695 | * looking for the file in each of the specified directories. | |||
| 696 | * if we don't find it then we issue an error. The error | |||
| 697 | * message is suppressed if the .IGNORE attribute of attr is | |||
| 698 | * set. If a file is found we call Parse on the file to | |||
| 699 | * perform the parse and then continue on from where we left | |||
| 700 | * off. */ | |||
| 701 | ||||
| 702 | for(lp=dp->CE_PRQce_prq; lp && fil == NIL(FILE)((FILE*)((void*)0)); lp=lp->cl_next) { | |||
| 703 | dir = lp->cl_prq->CE_NAMEce_name->ht_name; | |||
| 704 | if( strchr(dir, '$') ) dir = Expand(dir); | |||
| 705 | path = Build_path( dir, name ); | |||
| 706 | ||||
| 707 | DB_PRINT( "par", ("Trying to include [%s]", path) ); | |||
| 708 | ||||
| 709 | fil = Openfile( path, FALSE0, FALSE0 ); | |||
| 710 | if( dir != lp->cl_prq->CE_NAMEce_name->ht_name ) FREE(dir)free((char*)(dir)); | |||
| 711 | } | |||
| 712 | } | |||
| 713 | ||||
| 714 | if (!noinf && fil == NIL(FILE)((FILE*)((void*)0))) { | |||
| 715 | t_attr glob = Glob_attr; | |||
| 716 | t_attr cattr = prqlst->cl_prq->ce_attr; | |||
| 717 | ||||
| 718 | prqlst->cl_next = NIL(LINK)((LINK*)((void*)0)); | |||
| 719 | Glob_attr |= (attr&A_IGNORE0x00020); | |||
| 720 | prqlst->cl_prq->ce_attr &= ~A_FRINGE0x100000; | |||
| 721 | ||||
| 722 | if( Verbose & V_FILE_IO0x20 ) | |||
| 723 | printf( "%s: Inferring include file [%s].\n", | |||
| 724 | Pname, name ); | |||
| 725 | fil = TryFiles(prqlst); | |||
| 726 | ||||
| 727 | Glob_attr = glob; | |||
| 728 | prqlst->cl_prq->ce_attr |= (cattr & A_FRINGE0x100000); | |||
| 729 | } | |||
| 730 | ||||
| 731 | if( fil != NIL(FILE)((FILE*)((void*)0)) ) { | |||
| 732 | if( Verbose & V_FILE_IO0x20 ) | |||
| 733 | printf( "%s: Parsing include file [%s].\n", | |||
| 734 | Pname, name ); | |||
| 735 | Parse( fil ); | |||
| 736 | found = TRUE1; | |||
| 737 | } | |||
| 738 | else if( !(ignore || first) ) | |||
| 739 | Fatal( "Include file %s, not found", name ); | |||
| 740 | else if( Verbose & V_FILE_IO0x20 ) | |||
| 741 | printf( "%s: Include file [%s] was not found.\n", | |||
| 742 | Pname, name ); | |||
| 743 | } | |||
| 744 | ||||
| 745 | if ( !ignore && first && !found ) | |||
| 746 | Fatal( "No include file was found" ); | |||
| 747 | ||||
| 748 | if( pushed ) Pop_dir(FALSE0); | |||
| 749 | attr &= ~(A_IGNORE0x00020|A_SETDIR0x00400|A_FIRST0x20000000|A_NOINFER0x00080); | |||
| 750 | } | |||
| 751 | break; | |||
| 752 | ||||
| 753 | case ST_SOURCE7: | |||
| 754 | if( prereq != NIL(CELL)((CELL*)((void*)0)) ) | |||
| 755 | _do_targets( op & (R_OP_CL1 | R_OP_MI16 | R_OP_UP8), attr, set_dir, | |||
| 756 | target, prereq ); | |||
| 757 | else { | |||
| 758 | /* The old semantics of .SOURCE were that an empty list of | |||
| 759 | * prerequisites clears the .SOURCE list. So we must implement | |||
| 760 | * that here as a clearout prerequisite operation. Since this is | |||
| 761 | * a standard operation with the :- opcode we can simply call the | |||
| 762 | * proper routine with the target cell and it should do the trick | |||
| 763 | */ | |||
| 764 | ||||
| 765 | if( op == R_OP_CL1 || (op & R_OP_MI16) ) | |||
| 766 | Clear_prerequisites( target ); | |||
| 767 | } | |||
| 768 | ||||
| 769 | op &= ~(R_OP_MI16 | R_OP_UP8); | |||
| 770 | break; | |||
| 771 | ||||
| 772 | case ST_KEEP11: | |||
| 773 | if( Keep_state != NIL(char)((char*)((void*)0)) ) break; | |||
| 774 | Def_macro( ".KEEP_STATE", "_state.mk", M_EXPANDED0x0008 ); | |||
| 775 | break; | |||
| 776 | ||||
| 777 | case ST_REST4: | |||
| 778 | /* The rest of the special targets can all take recipes, as such they | |||
| 779 | * must be able to affect the state of the parser. */ | |||
| 780 | ||||
| 781 | { | |||
| 782 | int s_targ = Target; | |||
| 783 | ||||
| 784 | Target = TRUE1; | |||
| 785 | _sp_target = TRUE1; | |||
| 786 | *state = _do_targets( op, attr, set_dir, target, prereq ); | |||
| 787 | Target = s_targ; | |||
| 788 | ||||
| 789 | target->ce_flag |= F_TARGET0x0008; | |||
| 790 | ||||
| 791 | attr = A_DEFAULT0x00000; | |||
| 792 | op = R_OP_CL1; | |||
| 793 | } | |||
| 794 | break; | |||
| 795 | ||||
| 796 | default:break; | |||
| 797 | } | |||
| 798 | ||||
| 799 | if( op != R_OP_CL1 ) Warning( "Modifier(s) for operator ignored" ); | |||
| 800 | if( attr != A_DEFAULT0x00000 ) Warning( "Extra attributes ignored" ); | |||
| 801 | ||||
| 802 | DB_VOID_RETURNreturn; | |||
| 803 | } | |||
| 804 | ||||
| 805 | ||||
| 806 | static int | |||
| 807 | _do_targets( op, attr, set_dir, targets, prereq )/* | |||
| 808 | =================================================== | |||
| 809 | Evaluate the values derived from the current target definition | |||
| 810 | line. Helper functions _build_graph(), _do_magic(), _make_multi(), | |||
| 811 | _add_root(), _replace_cell(), _set_attributes(), Clear_prerequisites() | |||
| 812 | _stick_at_head(), Add_prerequisite() and _set_global_attr() are used. | |||
| 813 | If successfull "_sv_targets" is set to "targets". | |||
| 814 | Return RULE_SCAN if a recipe is expected to follow, otherwise | |||
| 815 | NORMAL_SCAN. */ | |||
| 816 | int op; /* rule operator */ | |||
| 817 | t_attr attr; /* attribute flags for current targets */ | |||
| 818 | char *set_dir; /* value of setdir attribute */ | |||
| 819 | CELLPTR targets; /* list of targets (each cell maybe already | |||
| 820 | * defined by a previous target definition | |||
| 821 | * line. */ | |||
| 822 | CELLPTR prereq; /* list of prerequisites */ | |||
| 823 | { | |||
| 824 | CELLPTR tg1; /* temporary target pointer */ | |||
| 825 | CELLPTR tp1; /* temporary prerequisite pointer */ | |||
| 826 | LINKPTR prev_cell; /* pointer for .UPDATEALL processing */ | |||
| 827 | char *p; /* temporary char pointer */ | |||
| 828 | int tflag = FALSE0; /* set to TRUE if we add target to root */ | |||
| 829 | int ret_state = RULE_SCAN1; /* Return state */ | |||
| 830 | ||||
| 831 | DB_ENTER( "_do_targets" ); | |||
| 832 | ||||
| 833 | /* If .UPDATEALL is set sort the target list that was temporary linked | |||
| 834 | * with ce_link into a list using ce_link with ce_set pointing to the first | |||
| 835 | * element. */ | |||
| 836 | /* FIXME: Check that .UPDATEALL and %-targets on one line work together. */ | |||
| 837 | if( attr & A_UPDATEALL0x00100 ) { | |||
| ||||
| 838 | if( targets == NIL(CELL)((CELL*)((void*)0)) ) | |||
| 839 | Fatal( ".UPDATEALL attribute requires non-empty list of targets" ); | |||
| 840 | ||||
| 841 | if (targets->ce_set == NIL(CELL)((CELL*)((void*)0))) { | |||
| 842 | for( | |||
| 843 | prev_cell=CeMeToo(targets)&((targets)->ce_all),tg1=targets->ce_link; | |||
| 844 | tg1 != NIL(CELL)((CELL*)((void*)0)); | |||
| 845 | tg1=tg1->ce_link | |||
| 846 | ) { | |||
| 847 | if (tg1->ce_set) | |||
| 848 | Fatal( "Target [%s] appears on multiple .UPDATEALL lists", | |||
| 849 | tg1->CE_NAMEce_name->ht_name); | |||
| 850 | tg1->ce_set = targets; | |||
| 851 | TALLOC(prev_cell->cl_next, 1, LINK)if ((prev_cell->cl_next = (LINK*) calloc((unsigned int)(1) , (size_t)sizeof(LINK))) == (LINK*)0) {No_ram();}; | |||
| 852 | prev_cell = prev_cell->cl_next; | |||
| 853 | prev_cell->cl_prq = tg1; | |||
| 854 | } | |||
| 855 | targets->ce_set = targets; | |||
| 856 | } | |||
| 857 | else { | |||
| 858 | LINKPTR ap; | |||
| 859 | CELLPTR tp; | |||
| 860 | ||||
| 861 | tp = targets; | |||
| 862 | ap = CeMeToo(targets)&((targets)->ce_all); | |||
| 863 | while (ap && tp && ap->cl_prq == tp && tp->ce_set == targets) { | |||
| 864 | ap = ap->cl_next; | |||
| 865 | tp = tp->ce_link; | |||
| 866 | } | |||
| 867 | if (ap || tp) | |||
| 868 | Fatal("Inconsistent .UPDATEALL lists for target [%s]", | |||
| 869 | targets->CE_NAMEce_name->ht_name); | |||
| 870 | } | |||
| 871 | targets->ce_link = NIL(CELL)((CELL*)((void*)0)); | |||
| 872 | } | |||
| 873 | ||||
| 874 | for( tg1 = targets; tg1 != NIL(CELL)((CELL*)((void*)0)); tg1 = tg1->ce_link ) { | |||
| 875 | /* Check if tg1 is already marked as a %-target, but not a magic | |||
| 876 | * (.xxx.yyy) target. */ | |||
| 877 | int purepercent = (tg1->ce_flag & F_PERCENT0x0800) && !(tg1->ce_flag & F_MAGIC0x2000); | |||
| 878 | ||||
| 879 | /* Check each target. Check for inconsistencies between :: and : rule | |||
| 880 | * sets. :: may follow either : or :: but not the reverse. | |||
| 881 | * | |||
| 882 | * Any F_MULTI target (contains :: rules) is represented by a prerequisite | |||
| 883 | * list hanging off the main target cell where each of the prerequisites | |||
| 884 | * is a copy of the target cell but is not entered into the hash table. | |||
| 885 | */ | |||
| 886 | if( !(op & R_OP_DCL2 ) && (tg1->ce_flag & F_MULTI0x0002) && !purepercent ) | |||
| 887 | Fatal( "':' vs '::' inconsistency in rules for %s", tg1->CE_NAMEce_name->ht_name ); | |||
| 888 | ||||
| 889 | if( purepercent ) { | |||
| 890 | /* Handle %-targets. */ | |||
| 891 | CELLPTR cur; | |||
| 892 | CELLPTR tpq = NIL(CELL)((CELL*)((void*)0)); | |||
| 893 | CELLPTR nprq = NULL((void*)0); | |||
| 894 | ||||
| 895 | #ifdef DBUG | |||
| 896 | DB_PRINT( "%", ("Handling %%-target [%s : : <prerequisites follow, maybe empty>]", | |||
| 897 | tg1->CE_NAME) ); | |||
| 898 | for(cur=prereq;cur;cur=cur->ce_link) { | |||
| 899 | DB_PRINT( "%", (" %%-prerequisites : %s ", | |||
| 900 | cur->CE_NAME ? cur->CE_NAME : "<empty>") ); | |||
| 901 | } | |||
| 902 | #endif | |||
| 903 | ||||
| 904 | /* Handle indirect (global) prerequisites first. */ | |||
| 905 | for(cur=prereq;cur;cur=cur->ce_link) { | |||
| 906 | char *name = cur->CE_NAMEce_name->ht_name; | |||
| 907 | int len = strlen(name); | |||
| 908 | ||||
| 909 | if( *name == '\'' && name[len-1]=='\'' ){ | |||
| 910 | name[len-1] = '\0'; | |||
| 911 | len = strlen(name+1)+1; | |||
| 912 | memmove(name,name+1,len); | |||
| 913 | /* add indirect prerequisite */ | |||
| 914 | _add_indirect_prereq( cur ); | |||
| 915 | } | |||
| 916 | else { | |||
| 917 | /* Sort all "other" prerequisits into tpq, with nprq | |||
| 918 | * pointing to the first element. */ | |||
| 919 | if (tpq) | |||
| 920 | tpq->ce_link = cur; | |||
| 921 | else | |||
| 922 | nprq = cur; | |||
| 923 | tpq = cur; | |||
| 924 | } | |||
| 925 | } | |||
| 926 | /* Mark the last element of nprq. */ | |||
| 927 | if(tpq) | |||
| 928 | tpq->ce_link=NIL(CELL)((CELL*)((void*)0)); | |||
| 929 | else | |||
| 930 | nprq = NIL(CELL)((CELL*)((void*)0)); | |||
| 931 | ||||
| 932 | /* Handle "normal" prerequisites now. */ | |||
| 933 | ||||
| 934 | if ( op & R_OP_OR32 ) { | |||
| 935 | /* for op == ':|' transform: | |||
| 936 | * <%-target> :| <prereq_1> ... <prereq_n> ; <recipe> | |||
| 937 | * into: | |||
| 938 | * <%-target> : <prereq_1> ; <recipe> | |||
| 939 | * .. | |||
| 940 | * <%-target> : <prereq_n> ; <recipe> | |||
| 941 | */ | |||
| 942 | for(tp1=nprq; tp1; tp1=tp1->ce_link) { | |||
| 943 | CELLPTR tmpcell = tp1->ce_link; | |||
| 944 | tp1->ce_link = NIL(CELL)((CELL*)((void*)0)); | |||
| 945 | _build_graph(op,tg1,tp1); | |||
| 946 | tp1->ce_link = tmpcell; | |||
| 947 | } | |||
| 948 | } | |||
| 949 | else { | |||
| 950 | /* The inference mechanism for %-targets limits the number of | |||
| 951 | * (non-indirect) prerequisite to one, but an unlimited number | |||
| 952 | * of indirect prerequisites is possible. */ | |||
| 953 | if ( nprq && nprq->ce_link && !(op & R_OP_OR32)) | |||
| 954 | Warning("More than one prerequisite\n" | |||
| 955 | "for %%-target. Use :| ruleop or indirect prerequisites."); | |||
| 956 | ||||
| 957 | _build_graph(op,tg1,nprq); | |||
| 958 | } | |||
| 959 | } | |||
| 960 | else if( tg1->ce_flag & F_MAGIC0x2000 && | |||
| 961 | (p = _is_magic( tg1->CE_NAMEce_name->ht_name )) != NIL(char)((char*)((void*)0)) && | |||
| 962 | _do_magic( op, p, tg1, prereq, attr, set_dir ) ) | |||
| 963 | ; /* _do_magic() does all that is needed (if return value is TRUE). */ | |||
| 964 | else if( op & R_OP_DCL2 ) { /* op == :: */ | |||
| 965 | CELLPTR tmp_cell = _make_multi(tg1); | |||
| 966 | ||||
| 967 | /* Add the F_MULTI master to .TARGETS (If not set already). | |||
| 968 | * Do this here so that the member cell is not added instead | |||
| 969 | * when the recipies are bound in Bind_rules_to_targets(). */ | |||
| 970 | tflag |= _add_root(tg1); | |||
| 971 | ||||
| 972 | /* Replace the F_MULTI master with the member cell. */ | |||
| 973 | targets = _replace_cell( targets, tg1, tmp_cell ); | |||
| 974 | ||||
| 975 | /* We have to set (add) the attributes also for the F_MULTI master | |||
| 976 | * target cell. As there is no recipe the setdir value is not | |||
| 977 | * needed. _set_attributes() that follows in approx. 8 lines | |||
| 978 | * will set the attributes for the F_MULTI member cell. */ | |||
| 979 | tg1->ce_attr |= (attr & ~A_SETDIR0x00400); | |||
| 980 | ||||
| 981 | /* Now switch tg1 to the current (F_MULTI prereq.) target. | |||
| 982 | * All recipes have to be added to that cell and not to the | |||
| 983 | * F_MULTI master. */ | |||
| 984 | tg1 = tmp_cell; | |||
| 985 | } | |||
| 986 | ||||
| 987 | if( !purepercent ) _set_attributes( attr, set_dir, tg1 ); | |||
| 988 | ||||
| 989 | /* Build the proper prerequisite list of the target. If the `-', | |||
| 990 | * modifier was used clear the prerequisite list before adding any | |||
| 991 | * new prerequisites. Else add them to the head/tail as appropriate. | |||
| 992 | * | |||
| 993 | * If the target has F_PERCENT set then no prerequisites are used. */ | |||
| 994 | ||||
| 995 | if( !(tg1->ce_flag & F_PERCENT0x0800) ) { | |||
| 996 | if( op & R_OP_MI16 ) Clear_prerequisites( tg1 ); /* op == :- */ | |||
| 997 | ||||
| 998 | if( (op & R_OP_UP8) && (tg1->ce_prq != NIL(LINK)((LINK*)((void*)0))) ) /* op == :^ */ | |||
| 999 | _stick_at_head( tg1, prereq ); | |||
| 1000 | else for( tp1=prereq; tp1 != NIL(CELL)((CELL*)((void*)0)); tp1 = tp1->ce_link ) | |||
| 1001 | Add_prerequisite( tg1, tp1, FALSE0, FALSE0 ); | |||
| 1002 | } | |||
| 1003 | else if( op & (R_OP_MI16 | R_OP_UP8) ) | |||
| 1004 | Warning( "Modifier(s) `^-' for %-meta target ignored" ); | |||
| 1005 | } | |||
| 1006 | ||||
| 1007 | /* In case a F_MULTI member that was the first prerequisite of .TARGETS */ | |||
| 1008 | if(tflag) | |||
| 1009 | Target = TRUE1; | |||
| 1010 | ||||
| 1011 | /* Check to see if we have NO targets but some attributes, i.e. an | |||
| 1012 | * Attribute-Definition. If so then apply all of the attributes to the | |||
| 1013 | * complete list of prerequisites. No recipes are allowed to follow. */ | |||
| 1014 | ||||
| 1015 | if( (targets == NIL(CELL)((CELL*)((void*)0))) && attr ) { | |||
| 1016 | ret_state = NORMAL_SCAN0; | |||
| 1017 | if( prereq != NIL(CELL)((CELL*)((void*)0)) ) | |||
| 1018 | for( tp1=prereq; tp1 != NIL(CELL)((CELL*)((void*)0)); tp1 = tp1->ce_link ) | |||
| 1019 | _set_attributes( attr, set_dir, tp1 ); | |||
| 1020 | else | |||
| 1021 | _set_global_attr( attr ); | |||
| 1022 | } | |||
| 1023 | ||||
| 1024 | /* Now that we have built the lists of targets, the parser must parse the | |||
| 1025 | * recipes if there are any. However we must start the recipe list with the | |||
| 1026 | * recipe specified as via the ; kludge, if there is one */ | |||
| 1027 | _sv_targets = targets; | |||
| 1028 | _sv_attr = attr; | |||
| 1029 | _sv_flag = ((op & R_OP_BG4) ? F_SINGLE0x0004 : F_DEFAULT0x0000); | |||
| 1030 | ||||
| 1031 | DB_RETURN( ret_state )return (ret_state); | |||
| 1032 | } | |||
| 1033 | ||||
| 1034 | ||||
| 1035 | static int | |||
| 1036 | _do_magic( op, dot, target, prereq, attr, set_dir )/* | |||
| 1037 | ===================================================== | |||
| 1038 | This function investigates dot for being a magic target of the form | |||
| 1039 | .<chars>.<chars> or .<chars> and creates the appropriate % rules for | |||
| 1040 | that target. | |||
| 1041 | If the target is given with an undefined syntax, i.e. with prerequisites, | |||
| 1042 | then this function terminates early without creating % rules and | |||
| 1043 | returns 0. | |||
| 1044 | If successful the function returns 1. | |||
| 1045 | ||||
| 1046 | The function builds the % rule, `%.o : %.c' from .c.o, and | |||
| 1047 | `% : %.a' from .a */ | |||
| 1048 | ||||
| 1049 | int op; | |||
| 1050 | char *dot; | |||
| 1051 | CELLPTR target; | |||
| 1052 | CELLPTR prereq; | |||
| 1053 | t_attr attr; | |||
| 1054 | char *set_dir; | |||
| 1055 | { | |||
| 1056 | CELLPTR tg; | |||
| 1057 | CELLPTR prq; | |||
| 1058 | char *tmp, *tmp2; | |||
| 1059 | ||||
| 1060 | DB_ENTER( "_do_magic" ); | |||
| 1061 | ||||
| 1062 | DB_PRINT("%", ("Analysing magic target [%s]", target->CE_NAME)); | |||
| 1063 | ||||
| 1064 | if( prereq != NIL(CELL)((CELL*)((void*)0)) ) { | |||
| 1065 | Warning( "Ignoring AUGMAKE meta-target [%s] because prerequisites are present.", target->CE_NAMEce_name->ht_name ); | |||
| 1066 | DB_RETURN(0)return (0); | |||
| 1067 | } | |||
| 1068 | ||||
| 1069 | if( dot == target->CE_NAMEce_name->ht_name ) { /* its of the form .a */ | |||
| 1070 | tg = Def_cell( "%" ); | |||
| 1071 | tmp = _build_meta( target->CE_NAMEce_name->ht_name ); | |||
| 1072 | prq = Def_cell( tmp ); | |||
| 1073 | FREE( tmp )free((char*)(tmp)); | |||
| 1074 | ||||
| 1075 | _build_graph( op, tg, prq ); | |||
| 1076 | } | |||
| 1077 | else { | |||
| 1078 | tmp = _build_meta( dot ); | |||
| 1079 | tg = Def_cell( tmp ); | |||
| 1080 | FREE( tmp )free((char*)(tmp)); | |||
| 1081 | ||||
| 1082 | tmp = _build_meta( tmp2 = DmSubStr( target->CE_NAMEce_name->ht_name, dot ) ); | |||
| 1083 | prq = Def_cell( tmp ); | |||
| 1084 | FREE( tmp )free((char*)(tmp)); | |||
| 1085 | FREE( tmp2 )free((char*)(tmp2)); | |||
| 1086 | ||||
| 1087 | _build_graph( op, tg, prq ); | |||
| 1088 | } | |||
| 1089 | ||||
| 1090 | tg->ce_flag |= F_PERCENT0x0800; | |||
| 1091 | target->ce_flag |= (F_MAGIC0x2000|F_PERCENT0x0800); | |||
| 1092 | ||||
| 1093 | _set_attributes( attr, set_dir, tg ); | |||
| 1094 | ||||
| 1095 | DB_RETURN(1)return (1); | |||
| 1096 | } | |||
| 1097 | ||||
| 1098 | ||||
| 1099 | static CELLPTR | |||
| 1100 | _replace_cell( lst, cell, rep )/* | |||
| 1101 | ================================= | |||
| 1102 | Replace cell with rep in lst. Note if cell is not part of lst we are in | |||
| 1103 | real trouble. */ | |||
| 1104 | CELLPTR lst; | |||
| 1105 | CELLPTR cell; | |||
| 1106 | CELLPTR rep; | |||
| 1107 | { | |||
| 1108 | register CELLPTR tp; | |||
| 1109 | ||||
| 1110 | if( lst == cell ) { | |||
| 1111 | rep->ce_link = lst->ce_link; | |||
| 1112 | lst = rep; | |||
| 1113 | } | |||
| 1114 | else { | |||
| 1115 | for( tp=lst; tp->ce_link != cell && tp ; tp=tp->ce_link ); | |||
| 1116 | if( !tp ) | |||
| 1117 | Fatal( "Internal Error: cell not part of lst." ); | |||
| 1118 | rep->ce_link = tp->ce_link->ce_link; | |||
| 1119 | tp->ce_link = rep; | |||
| 1120 | } | |||
| 1121 | ||||
| 1122 | return(lst); | |||
| 1123 | } | |||
| 1124 | ||||
| 1125 | ||||
| 1126 | static char * | |||
| 1127 | _build_meta( name )/* | |||
| 1128 | ===================== | |||
| 1129 | Check to see if the name is of the form .c~ if so and if Augmake | |||
| 1130 | translation is enabled then return s.%.c, else return %.suff, where if the | |||
| 1131 | suffix ends in '~' then leave it be.*/ | |||
| 1132 | char *name; | |||
| 1133 | { | |||
| 1134 | char *tmp; | |||
| 1135 | int test = (STOBOOL(Augmake)(Augmake && ((*Augmake | 0x20) == 'y')) ? name[strlen(name)-1] == '~' : 0); | |||
| 1136 | ||||
| 1137 | tmp = DmStrJoin( test ? "s.%" : "%", name, -1, FALSE0); | |||
| 1138 | if( test ) tmp[ strlen(tmp)-1 ] = '\0'; | |||
| 1139 | ||||
| 1140 | return(tmp); | |||
| 1141 | } | |||
| 1142 | ||||
| 1143 | ||||
| 1144 | static CELLPTR | |||
| 1145 | _build_graph( op, target, prereq )/* | |||
| 1146 | ==================================== | |||
| 1147 | This function is called to build the graph for the % rule given by | |||
| 1148 | target : prereq cell combination. This function assumes that target | |||
| 1149 | is a % target and that prereq is one or multiple non-indirect prerequisite. | |||
| 1150 | It also assumes that target cell has F_PERCENT set already. | |||
| 1151 | ||||
| 1152 | NOTE: If more than one prerequisite is present this function handles them | |||
| 1153 | correctly but the lookup still only uses the first (BUG!). | |||
| 1154 | ||||
| 1155 | R_OP_CL (:) rules replace existing rules if any, %.o :: %.c is meaningless. | |||
| 1156 | ||||
| 1157 | The function always returns NIL(CELL). */ | |||
| 1158 | int op; | |||
| 1159 | CELLPTR target; | |||
| 1160 | CELLPTR prereq; | |||
| 1161 | { | |||
| 1162 | LINKPTR edl; | |||
| 1163 | CELLPTR edge = 0; | |||
| 1164 | CELLPTR tpq,cur; | |||
| 1165 | int match; | |||
| 1166 | ||||
| 1167 | #ifdef DBUG | |||
| 1168 | DB_ENTER( "_build_graph" ); | |||
| 1169 | DB_PRINT( "%", ("Building graph for [%s : <prerequisites follow, maybe empty>]", | |||
| 1170 | target->CE_NAME) ); | |||
| 1171 | for(tpq=prereq;tpq;tpq=tpq->ce_link) { | |||
| 1172 | DB_PRINT( "%", (" %%-prerequisites : %s ", | |||
| 1173 | tpq->CE_NAME ? tpq->CE_NAME : "<empty>") ); | |||
| 1174 | } | |||
| 1175 | #endif | |||
| 1176 | ||||
| 1177 | /* Currently multiple prerequisites are not (yet) handled correctly. | |||
| 1178 | * We already issue a warning in _do_targets(), don't issue it here | |||
| 1179 | * again. | |||
| 1180 | if ( prereq && prereq->ce_link ) | |||
| 1181 | Warning( "Internal Error: more than one prerequisite in _build_graph." ); | |||
| 1182 | */ | |||
| 1183 | ||||
| 1184 | /* There cannot be more than one target name ( linked with | |||
| 1185 | * (CeMeToo(target))->cl_next ) per %-target master. | |||
| 1186 | * FIXME: remove this check after verifying that it never triggers. */ | |||
| 1187 | if ( (CeMeToo(target)&((target)->ce_all))->cl_next ) | |||
| 1188 | Fatal( "Internal Error: more than one target name in _build_graph." ); | |||
| 1189 | ||||
| 1190 | /* Search the list of prerequisites for the current target and see if | |||
| 1191 | * any of them match the current %-meta's : prereq's pair. NOTE that | |||
| 1192 | * %-metas are built as if they were F_MULTI targets, i.e. the target | |||
| 1193 | * definitions for the %-target members are stored in the prerequisites | |||
| 1194 | * list of the master target. */ | |||
| 1195 | /* This relies on target->ce_prq being NULL if this is the first | |||
| 1196 | * occurrence of this %-target and therefore not yet having a %-target | |||
| 1197 | * master. */ | |||
| 1198 | match = FALSE0; | |||
| 1199 | for(edl=target->ce_prq; !match && edl != NIL(LINK)((LINK*)((void*)0)); edl=edl->cl_next) { | |||
| 1200 | LINKPTR l1,l2; | |||
| 1201 | edge = edl->cl_prq; | |||
| 1202 | ||||
| 1203 | DB_PRINT("%", ("Trying to match [%s]",edge?edge->CE_NAME:"(nil)")); | |||
| 1204 | ||||
| 1205 | /* First we match the target sets, if this fails then we don't have to | |||
| 1206 | * bother with the prerequisite sets. The targets sets are sorted. | |||
| 1207 | * this makes life very simple. */ | |||
| 1208 | /* ce_dir is handled per member target, no check needed for the | |||
| 1209 | * master target. */ | |||
| 1210 | ||||
| 1211 | /* FIXME: We already checked above that there is only one target | |||
| 1212 | * name. Remove the comparisons for following names. */ | |||
| 1213 | l1 = CeMeToo(target)&((target)->ce_all); /* Used by .UPDATEALL !!! */ | |||
| 1214 | l2 = CeMeToo(edge)&((edge)->ce_all); | |||
| 1215 | while(l1 && l2 && l1->cl_prq == l2->cl_prq) { | |||
| 1216 | l1=l1->cl_next; | |||
| 1217 | l2=l2->cl_next; | |||
| 1218 | } | |||
| 1219 | /* If both l1 and l2 are NULL we had a match. */ | |||
| 1220 | if (l1 || l2) | |||
| 1221 | continue; | |||
| 1222 | ||||
| 1223 | /* target sets match, so check prerequisites. */ | |||
| 1224 | if( (!edge->ce_prq && !prereq) /* matches both empty - separate this. */ | |||
| 1225 | || ( edge->ce_prq | |||
| 1226 | && ( edge->ce_dir == _sv_setdir | |||
| 1227 | || ( edge->ce_dir | |||
| 1228 | && _sv_setdir | |||
| 1229 | && !strcmp(edge->ce_dir,strchr(_sv_setdir,'=')+1) | |||
| 1230 | ) | |||
| 1231 | ) | |||
| 1232 | ) | |||
| 1233 | ) { | |||
| 1234 | LINKPTR prql; | |||
| 1235 | ||||
| 1236 | /* this is a really gross way to compare two sets, it's n^2 but | |||
| 1237 | * since the sets are assumed to always be tiny, it should be ok. */ | |||
| 1238 | for(tpq=prereq; tpq; tpq=tpq->ce_link) { | |||
| 1239 | for(prql=edge->ce_prq;prql;prql=prql->cl_next) | |||
| 1240 | if (prql->cl_prq == tpq) | |||
| 1241 | break; | |||
| 1242 | ||||
| 1243 | if(prql == NIL(LINK)((LINK*)((void*)0))) | |||
| 1244 | break; | |||
| 1245 | ||||
| 1246 | prql->cl_prq->ce_flag |= F_MARK0x0001; | |||
| 1247 | } | |||
| 1248 | ||||
| 1249 | if (tpq == NIL(CELL)((CELL*)((void*)0))) { | |||
| 1250 | for(prql=edge->ce_prq;prql;prql=prql->cl_next) | |||
| 1251 | if(!(prql->cl_prq->ce_flag & F_MARK0x0001)) | |||
| 1252 | break; | |||
| 1253 | ||||
| 1254 | if(prql == NIL(LINK)((LINK*)((void*)0))) | |||
| 1255 | match = TRUE1; | |||
| 1256 | } | |||
| 1257 | ||||
| 1258 | /* clean up the mark bits. */ | |||
| 1259 | for(prql=edge->ce_prq;prql;prql=prql->cl_next) | |||
| 1260 | prql->cl_prq->ce_flag &= ~F_MARK0x0001; | |||
| 1261 | } | |||
| 1262 | } | |||
| 1263 | ||||
| 1264 | if( match ) { | |||
| 1265 | /* match is TRUE hence, we found an edge joining the target and the | |||
| 1266 | * prerequisite so reset the new edge so that new values replace it. */ | |||
| 1267 | DB_PRINT( "%", ("It's an old edge") ); | |||
| 1268 | ||||
| 1269 | edge->ce_dir = NIL(char)((char*)((void*)0)); | |||
| 1270 | edge->ce_flag &= (F_PERCENT0x0800|F_MAGIC0x2000|F_DFA0x0200); | |||
| 1271 | edge->ce_attr &= A_NOINFER0x00080; | |||
| 1272 | } | |||
| 1273 | else { | |||
| 1274 | DB_PRINT( "%", ("Adding a new edge") ); | |||
| 1275 | ||||
| 1276 | edge = _make_multi(target); | |||
| 1277 | ||||
| 1278 | /* FIXME: There can be only one %-target. */ | |||
| 1279 | for(edl=CeMeToo(target)&((target)->ce_all);edl;edl=edl->cl_next) { | |||
| 1280 | if( !((tpq=edl->cl_prq)->ce_flag & F_DFA0x0200) ) { | |||
| 1281 | Add_nfa( tpq->CE_NAMEce_name->ht_name ); | |||
| 1282 | tpq->ce_flag |= F_DFA0x0200; | |||
| 1283 | } | |||
| 1284 | ||||
| 1285 | edl->cl_prq->ce_set = edge; | |||
| 1286 | } | |||
| 1287 | ||||
| 1288 | edge->ce_all = target->ce_all; | |||
| 1289 | target->ce_all.cl_next = NIL(LINK)((LINK*)((void*)0)); | |||
| 1290 | target->ce_set = NIL(CELL)((CELL*)((void*)0)); | |||
| 1291 | ||||
| 1292 | /* Add all prerequisites to edge. */ | |||
| 1293 | for(tpq=prereq; tpq; tpq=tpq->ce_link) | |||
| 1294 | Add_prerequisite(edge, tpq, FALSE0, TRUE1); | |||
| 1295 | } | |||
| 1296 | ||||
| 1297 | if( op & R_OP_DCL2 ) | |||
| 1298 | Warning("'::' operator for meta-target '%s' ignored, ':' operator assumed.", | |||
| 1299 | target->CE_NAMEce_name->ht_name ); | |||
| 1300 | ||||
| 1301 | /* If edge was already added we're in BIG trouble. */ | |||
| 1302 | /* Re-use cur as temporary variable. */ | |||
| 1303 | for( cur=_sv_edgel; cur != NIL(CELL)((CELL*)((void*)0)); cur=cur->ce_link ) { | |||
| 1304 | if( cur == edge ) | |||
| 1305 | Fatal( "Internal Error: edge already in _sv_edgel." ); | |||
| 1306 | } | |||
| 1307 | ||||
| 1308 | edge->ce_link = _sv_edgel; | |||
| 1309 | _sv_edgel = edge; | |||
| 1310 | _sv_globprq_only = 0; | |||
| 1311 | ||||
| 1312 | DB_RETURN(NIL(CELL))return (((CELL*)((void*)0))); | |||
| 1313 | } | |||
| 1314 | ||||
| 1315 | ||||
| 1316 | static CELLPTR | |||
| 1317 | _make_multi( tg )/* | |||
| 1318 | =================== | |||
| 1319 | This function is called to convert tg into an F_MULTI target. | |||
| 1320 | Return a pointer to the new member cell. | |||
| 1321 | I don't know what the author intended but the ce_index entry is only | |||
| 1322 | used in this function (set to 0 for added targets) and undefined otherwise! | |||
| 1323 | The undefined value is hopefully set to 0 by the C compiler as each added | |||
| 1324 | target sets its ce_count to ++ce_index (==1). (FIXME) */ | |||
| 1325 | CELLPTR tg; | |||
| 1326 | { | |||
| 1327 | CELLPTR cp; | |||
| 1328 | ||||
| 1329 | /* This creates a new master F_MULTI target if tg existed before as a normal | |||
| 1330 | * target with prerequisites or recipes. */ | |||
| 1331 | if( !(tg->ce_flag & F_MULTI0x0002) && (tg->ce_prq || tg->ce_recipe) ) { | |||
| 1332 | /* Allocate a new master cell. */ | |||
| 1333 | TALLOC(cp, 1, CELL)if ((cp = (CELL*) calloc((unsigned int)(1), (size_t)sizeof(CELL ))) == (CELL*)0) {No_ram();}; | |||
| 1334 | *cp = *tg; | |||
| 1335 | ||||
| 1336 | /* F_MULTI master */ | |||
| 1337 | tg->ce_prq = NIL(LINK)((LINK*)((void*)0)); | |||
| 1338 | tg->ce_flag |= F_RULES0x0010|F_MULTI0x0002|F_TARGET0x0008; | |||
| 1339 | tg->ce_attr |= A_SEQ0x00200; | |||
| 1340 | tg->ce_recipe = NIL(STRING)((STRING*)((void*)0)); | |||
| 1341 | tg->ce_dir = NIL(char)((char*)((void*)0)); | |||
| 1342 | ||||
| 1343 | /* F_MULTI member for preexisting elements */ | |||
| 1344 | cp->ce_count = ++tg->ce_index; | |||
| 1345 | cp->ce_cond = NIL(STRING)((STRING*)((void*)0)); | |||
| 1346 | cp->ce_set = NIL(CELL)((CELL*)((void*)0)); | |||
| 1347 | cp->ce_all.cl_prq = cp; | |||
| 1348 | CeNotMe(cp)(cp)->ce_all.cl_next = NIL(LINK)((LINK*)((void*)0)); | |||
| 1349 | ||||
| 1350 | Add_prerequisite(tg, cp, FALSE0, TRUE1); | |||
| 1351 | } | |||
| 1352 | ||||
| 1353 | /* Alocate memory for new member of F_MULTI target */ | |||
| 1354 | TALLOC(cp, 1, CELL)if ((cp = (CELL*) calloc((unsigned int)(1), (size_t)sizeof(CELL ))) == (CELL*)0) {No_ram();}; | |||
| 1355 | *cp = *tg; | |||
| 1356 | ||||
| 1357 | /* This is reached if the target already exists, but without having | |||
| 1358 | * prerequisites or recepies. Morph it into a F_MULTI master cell. */ | |||
| 1359 | if( !(tg->ce_flag & F_MULTI0x0002) ) { | |||
| 1360 | tg->ce_prq = NIL(LINK)((LINK*)((void*)0)); | |||
| 1361 | tg->ce_flag |= F_RULES0x0010|F_MULTI0x0002|F_TARGET0x0008; | |||
| 1362 | tg->ce_attr |= A_SEQ0x00200; | |||
| 1363 | tg->ce_recipe = NIL(STRING)((STRING*)((void*)0)); | |||
| 1364 | tg->ce_dir = NIL(char)((char*)((void*)0)); | |||
| 1365 | cp->ce_cond = NIL(STRING)((STRING*)((void*)0)); | |||
| 1366 | } | |||
| 1367 | /* This handles the case of adding an additional target as a | |||
| 1368 | * prerequisite to a F_MULTI target. */ | |||
| 1369 | else { | |||
| 1370 | cp->ce_flag &= ~(F_RULES0x0010|F_MULTI0x0002); | |||
| 1371 | cp->ce_attr &= ~A_SEQ0x00200; | |||
| 1372 | cp->ce_prq = NIL(LINK)((LINK*)((void*)0)); | |||
| 1373 | cp->ce_index = 0; | |||
| 1374 | cp->ce_cond = NIL(STRING)((STRING*)((void*)0)); | |||
| 1375 | } | |||
| 1376 | cp->ce_count = ++tg->ce_index; | |||
| 1377 | cp->ce_flag |= F_TARGET0x0008; | |||
| 1378 | cp->ce_set = NIL(CELL)((CELL*)((void*)0)); | |||
| 1379 | cp->ce_all.cl_prq = cp; | |||
| 1380 | CeNotMe(cp)(cp)->ce_all.cl_next = NIL(LINK)((LINK*)((void*)0)); | |||
| 1381 | ||||
| 1382 | Add_prerequisite(tg, cp, FALSE0, TRUE1); | |||
| 1383 | return(cp); | |||
| 1384 | } | |||
| 1385 | ||||
| 1386 | ||||
| 1387 | static void | |||
| 1388 | _add_indirect_prereq( pq )/* | |||
| 1389 | ========================== | |||
| 1390 | Prerequisite is an indirect prerequisite for a %-target, add it to | |||
| 1391 | the target's list of indirect prerequsites to add on match. */ | |||
| 1392 | CELLPTR pq; | |||
| 1393 | { | |||
| 1394 | register LINKPTR ln; | |||
| 1395 | ||||
| 1396 | /* Only add to list of indirect prerequsites if it is not in already. */ | |||
| 1397 | for(ln=_sv_ind_prq; ln; ln=ln->cl_next) | |||
| 1398 | if(strcmp(ln->cl_prq->CE_NAMEce_name->ht_name,pq->CE_NAMEce_name->ht_name) == 0) | |||
| 1399 | return; | |||
| 1400 | ||||
| 1401 | /* Not in, add it. */ | |||
| 1402 | TALLOC( ln, 1, LINK )if ((ln = (LINK*) calloc((unsigned int)(1), (size_t)sizeof(LINK ))) == (LINK*)0) {No_ram();}; | |||
| 1403 | ln->cl_next = _sv_ind_prq; | |||
| 1404 | ln->cl_prq = pq; | |||
| 1405 | _sv_ind_prq = ln; | |||
| 1406 | } | |||
| 1407 | ||||
| 1408 | ||||
| 1409 | ||||
| 1410 | static void | |||
| 1411 | _set_attributes( attr, set_dir, cp )/* | |||
| 1412 | ====================================== | |||
| 1413 | Set the appropriate attributes for a cell */ | |||
| 1414 | t_attr attr; | |||
| 1415 | char *set_dir; | |||
| 1416 | CELLPTR cp; | |||
| 1417 | { | |||
| 1418 | char *dir = 0; | |||
| 1419 | ||||
| 1420 | DB_ENTER( "_set_attributes" ); | |||
| 1421 | ||||
| 1422 | /* If .SETDIR attribute is set then we have at least .SETDIR= in the | |||
| 1423 | * set_dir string. So go and fishout what is at the end of the =. | |||
| 1424 | * If not set and not NULL then propagate it to the target cell. */ | |||
| 1425 | ||||
| 1426 | if( attr & A_SETDIR0x00400 ) { | |||
| 1427 | char *p; | |||
| 1428 | if( (p = strchr( set_dir, '=' )) != NULL((void*)0) ) | |||
| 1429 | dir = p + 1; | |||
| 1430 | ||||
| 1431 | if( cp->ce_dir ) | |||
| 1432 | Warning( "Multiple .SETDIR for %s ignored", cp->CE_NAMEce_name->ht_name ); | |||
| 1433 | else if( *dir ) | |||
| 1434 | cp->ce_dir = DmStrDup(dir); | |||
| 1435 | } | |||
| 1436 | cp->ce_attr |= attr; /* set rest of attributes for target */ | |||
| 1437 | ||||
| 1438 | DB_VOID_RETURNreturn; | |||
| 1439 | } | |||
| 1440 | ||||
| 1441 | ||||
| 1442 | ||||
| 1443 | static void | |||
| 1444 | _set_global_attr( attr )/* | |||
| 1445 | ========================== | |||
| 1446 | Handle the setting of the global attribute functions based on | |||
| 1447 | The attribute flags set in attr. */ | |||
| 1448 | t_attr attr; | |||
| 1449 | { | |||
| 1450 | t_attr flag; | |||
| 1451 | ||||
| 1452 | /* Some compilers can't handle a switch on a long, and t_attr is now a long | |||
| 1453 | * integer on some systems. foey! */ | |||
| 1454 | for( flag = MAX_ATTR0x40000; flag; flag >>= 1 ) | |||
| 1455 | if( flag & attr ) { | |||
| 1456 | if( flag == A_PRECIOUS0x00001) Def_macro(".PRECIOUS", "y", M_EXPANDED0x0008); | |||
| 1457 | else if( flag == A_SILENT0x00002) Def_macro(".SILENT", "y", M_EXPANDED0x0008); | |||
| 1458 | else if( flag == A_IGNORE0x00020 ) Def_macro(".IGNORE", "y", M_EXPANDED0x0008); | |||
| 1459 | else if( flag == A_EPILOG0x00008 ) Def_macro(".EPILOG", "y", M_EXPANDED0x0008); | |||
| 1460 | else if( flag == A_PROLOG0x00010 ) Def_macro(".PROLOG", "y", M_EXPANDED0x0008); | |||
| 1461 | else if( flag == A_NOINFER0x00080 ) Def_macro(".NOINFER", "y", M_EXPANDED0x0008); | |||
| 1462 | else if( flag == A_SEQ0x00200 ) Def_macro(".SEQUENTIAL","y", M_EXPANDED0x0008); | |||
| 1463 | else if( flag == A_SHELL0x00800 ) Def_macro(".USESHELL", "y", M_EXPANDED0x0008); | |||
| 1464 | else if( flag == A_MKSARGS0x02000 ) Def_macro(".MKSARGS", "y", M_EXPANDED0x0008); | |||
| 1465 | #if !defined(__CYGWIN__) | |||
| 1466 | else if( flag == A_SWAP0x01000 ) Def_macro(".SWAP", "y", M_EXPANDED0x0008); | |||
| 1467 | #else | |||
| 1468 | else if( flag == A_WINPATH0x01000 ) Def_macro(".WINPATH", "y", M_EXPANDED0x0008); | |||
| 1469 | #endif | |||
| 1470 | } | |||
| 1471 | ||||
| 1472 | attr &= ~A_GLOB(0x00001 | 0x00002 | 0x00020 | 0x00008 | 0x01000 | 0x00800 | 0x00010 | 0x00080 | 0x00200 | 0x02000 ); | |||
| 1473 | if( attr ) Warning( "Non global attribute(s) ignored" ); | |||
| 1474 | } | |||
| 1475 | ||||
| 1476 | ||||
| 1477 | ||||
| 1478 | static void | |||
| 1479 | _stick_at_head( cp, pq )/* | |||
| 1480 | ========================== | |||
| 1481 | Add the prerequisite list to the head of the existing prerequisite | |||
| 1482 | list */ | |||
| 1483 | ||||
| 1484 | CELLPTR cp; /* cell for target node */ | |||
| 1485 | CELLPTR pq; /* list of prerequisites to add */ | |||
| 1486 | { | |||
| 1487 | DB_ENTER( "_stick_at_head" ); | |||
| 1488 | ||||
| 1489 | if( pq->ce_link != NIL(CELL)((CELL*)((void*)0)) ) _stick_at_head( cp, pq->ce_link ); | |||
| ||||
| 1490 | Add_prerequisite( cp, pq, TRUE1, FALSE0 ); | |||
| 1491 | ||||
| 1492 | DB_VOID_RETURNreturn; | |||
| 1493 | } | |||
| 1494 | ||||
| 1495 | ||||
| 1496 | ||||
| 1497 | static t_attr | |||
| 1498 | _is_attribute( name )/* | |||
| 1499 | ======================= | |||
| 1500 | Check the passed name against the list of valid attributes and return the | |||
| 1501 | attribute index if it is, else return 0, indicating the name is not a valid | |||
| 1502 | attribute. The present attributes are defined in dmake.h as A_xxx #defines, | |||
| 1503 | with the corresponding makefile specification: (note they must be named | |||
| 1504 | exactly as defined below) | |||
| 1505 | ||||
| 1506 | Valid attributes are: .IGNORE, .SETDIR=, .SILENT, .PRECIOUS, .LIBRARY, | |||
| 1507 | .EPILOG, .PROLOG, .LIBRARYM, .SYMBOL, .UPDATEALL, | |||
| 1508 | .USESHELL, .NOINFER, .PHONY, .SWAP/.WINPATH, .SEQUENTIAL | |||
| 1509 | .NOSTATE, .MKSARGS, .IGNOREGROUP, .GROUP, .FIRST | |||
| 1510 | .EXECUTE, .ERRREMOVE | |||
| 1511 | ||||
| 1512 | NOTE: The strcmp's are OK since at most three are ever executed for any | |||
| 1513 | one attribute check, and that happens only when we can be fairly | |||
| 1514 | certain we have an attribute. */ | |||
| 1515 | char *name; | |||
| 1516 | { | |||
| 1517 | t_attr attr = 0; | |||
| 1518 | ||||
| 1519 | DB_ENTER( "_is_attribute" ); | |||
| 1520 | ||||
| 1521 | if( *name++ == '.' ) | |||
| 1522 | switch( *name ) | |||
| 1523 | { | |||
| 1524 | case 'E': | |||
| 1525 | if( !strcmp(name, "EPILOG") ) attr = A_EPILOG0x00008; | |||
| 1526 | else if( !strcmp(name, "EXECUTE")) attr = A_EXECUTE0x20000; | |||
| 1527 | else if( !strcmp(name, "ERRREMOVE")) attr = A_ERRREMOVE0x40000; | |||
| 1528 | else attr = 0; | |||
| 1529 | break; | |||
| 1530 | ||||
| 1531 | /* A_FIRST implies A_IGNORE, handled in ST_INCLUDE */ | |||
| 1532 | case 'F': | |||
| 1533 | attr = (strcmp(name, "FIRST")) ? 0 : A_FIRST0x20000000; | |||
| 1534 | break; | |||
| 1535 | ||||
| 1536 | case 'G': attr = (strcmp(name, "GROUP")) ? 0 : A_GROUP0x02000000; break; | |||
| 1537 | case 'L': attr = (strcmp(name, "LIBRARY")) ? 0 : A_LIBRARY0x00004; break; | |||
| 1538 | case 'M': attr = (strcmp(name, "MKSARGS")) ? 0 : A_MKSARGS0x02000; break; | |||
| 1539 | ||||
| 1540 | case 'I': | |||
| 1541 | if( !strcmp(name, "IGNORE") ) attr = A_IGNORE0x00020; | |||
| 1542 | else if( !strcmp(name, "IGNOREGROUP")) attr = A_IGNOREGROUP0x10000; | |||
| 1543 | else attr = 0; | |||
| 1544 | break; | |||
| 1545 | ||||
| 1546 | case 'N': | |||
| 1547 | if( !strcmp(name, "NOINFER") ) attr = A_NOINFER0x00080; | |||
| 1548 | else if( !strcmp(name, "NOSTATE")) attr = A_NOSTATE0x08000; | |||
| 1549 | else attr = 0; | |||
| 1550 | break; | |||
| 1551 | ||||
| 1552 | case 'U': | |||
| 1553 | if( !strcmp(name, "UPDATEALL") ) attr = A_UPDATEALL0x00100; | |||
| 1554 | else if( !strcmp(name, "USESHELL")) attr = A_SHELL0x00800; | |||
| 1555 | else attr = 0; | |||
| 1556 | break; | |||
| 1557 | ||||
| 1558 | case 'P': | |||
| 1559 | if( !strcmp(name, "PRECIOUS") ) attr = A_PRECIOUS0x00001; | |||
| 1560 | else if( !strcmp(name, "PROLOG") ) attr = A_PROLOG0x00010; | |||
| 1561 | else if( !strcmp(name, "PHONY") ) attr = A_PHONY0x04000; | |||
| 1562 | else attr = 0; | |||
| 1563 | break; | |||
| 1564 | ||||
| 1565 | case 'S': | |||
| 1566 | if( !strncmp(name, "SETDIR=", 7) ) attr = A_SETDIR0x00400; | |||
| 1567 | else if( !strcmp(name, "SILENT") ) attr = A_SILENT0x00002; | |||
| 1568 | else if( !strcmp(name, "SYMBOL") ) attr = A_SYMBOL0x00040; | |||
| 1569 | else if( !strcmp(name, "SEQUENTIAL")) attr = A_SEQ0x00200; | |||
| 1570 | /* A_SWAP has no meaning except for MSDOS. */ | |||
| 1571 | else if( !strcmp(name, "SWAP")) attr = A_SWAP0x01000; | |||
| 1572 | else attr = 0; | |||
| 1573 | break; | |||
| 1574 | ||||
| 1575 | case 'W': attr = (strcmp(name, "WINPATH")) ? 0 : A_WINPATH0x01000; break; | |||
| 1576 | } | |||
| 1577 | ||||
| 1578 | DB_RETURN( attr )return (attr); | |||
| 1579 | } | |||
| 1580 | ||||
| 1581 | ||||
| 1582 | ||||
| 1583 | static int | |||
| 1584 | _is_special( tg )/* | |||
| 1585 | =================== | |||
| 1586 | This function returns TRUE if the name passed in represents a special | |||
| 1587 | target, otherwise it returns false. A special target is one that has | |||
| 1588 | a special meaning to dmake, and may require processing at the time that | |||
| 1589 | it is parsed. | |||
| 1590 | ||||
| 1591 | Current Special targets are: | |||
| 1592 | .GROUPPROLOG .GROUPEPILOG .INCLUDE .IMPORT | |||
| 1593 | .EXPORT .SOURCE .SUFFIXES .ERROR .EXIT | |||
| 1594 | .INCLUDEDIRS .MAKEFILES .REMOVE .KEEP_STATE | |||
| 1595 | .TARGETS .ROOT | |||
| 1596 | */ | |||
| 1597 | char *tg; | |||
| 1598 | { | |||
| 1599 | DB_ENTER( "_is_special" ); | |||
| 1600 | ||||
| 1601 | if( *tg++ != '.' ) DB_RETURN( 0 )return (0); | |||
| 1602 | ||||
| 1603 | switch( *tg ) | |||
| 1604 | { | |||
| 1605 | case 'E': | |||
| 1606 | if( !strcmp( tg, "ERROR" ) ) DB_RETURN( ST_REST )return (4); | |||
| 1607 | else if( !strcmp( tg, "EXPORT" ) ) DB_RETURN( ST_EXPORT )return (8); | |||
| 1608 | else if( !strcmp( tg, "EXIT" ) ) DB_RETURN( ST_EXIT )return (12); | |||
| 1609 | break; | |||
| 1610 | ||||
| 1611 | case 'G': | |||
| 1612 | if( !strcmp( tg, "GROUPPROLOG" )) DB_RETURN( ST_REST )return (4); | |||
| 1613 | else if( !strcmp( tg, "GROUPEPILOG" )) DB_RETURN( ST_REST )return (4); | |||
| 1614 | break; | |||
| 1615 | ||||
| 1616 | case 'I': | |||
| 1617 | if( !strcmp( tg, "IMPORT" ) ) DB_RETURN( ST_IMPORT )return (9); | |||
| 1618 | else if( !strcmp( tg, "INCLUDE" ) ) DB_RETURN( ST_INCLUDE )return (5); | |||
| 1619 | else if( !strcmp( tg, "INCLUDEDIRS" )) DB_RETURN( ST_REST )return (4); | |||
| 1620 | break; | |||
| 1621 | ||||
| 1622 | case 'K': | |||
| 1623 | if( !strcmp( tg, "KEEP_STATE" ) ) DB_RETURN( ST_KEEP )return (11); | |||
| 1624 | break; | |||
| 1625 | ||||
| 1626 | case 'M': | |||
| 1627 | if( !strcmp( tg, "MAKEFILES" ) ) DB_RETURN( ST_REST )return (4); | |||
| 1628 | break; | |||
| 1629 | ||||
| 1630 | case 'R': | |||
| 1631 | if( !strcmp( tg, "REMOVE" ) ) DB_RETURN( ST_REST )return (4); | |||
| 1632 | else if( !strcmp( tg, "ROOT" ) ) DB_RETURN( ST_REST )return (4); | |||
| 1633 | break; | |||
| 1634 | ||||
| 1635 | case 'S': | |||
| 1636 | if( !strncmp( tg, "SOURCE", 6 ) ) DB_RETURN( ST_SOURCE )return (7); | |||
| 1637 | else if( !strncmp(tg, "SUFFIXES", 8 )) { | |||
| 1638 | if (Verbose & V_WARNALL0x40) | |||
| 1639 | Warning( "The .SUFFIXES target has no special meaning and is deprecated." ); | |||
| 1640 | DB_RETURN( ST_SOURCE )return (7); | |||
| 1641 | } | |||
| 1642 | break; | |||
| 1643 | ||||
| 1644 | case 'T': | |||
| 1645 | if( !strcmp( tg, "TARGETS" ) ) DB_RETURN( ST_REST )return (4); | |||
| 1646 | break; | |||
| 1647 | } | |||
| 1648 | ||||
| 1649 | DB_RETURN( 0 )return (0); | |||
| 1650 | } | |||
| 1651 | ||||
| 1652 | ||||
| 1653 | ||||
| 1654 | static int | |||
| 1655 | _is_percent( np )/* | |||
| 1656 | =================== | |||
| 1657 | return TRUE if np points at a string containing a % sign */ | |||
| 1658 | char *np; | |||
| 1659 | { | |||
| 1660 | return( (strchr(np,'%') && (*np != '\'' && np[strlen(np)-1] != '\'')) ? | |||
| 1661 | TRUE1 : FALSE0 ); | |||
| 1662 | } | |||
| 1663 | ||||
| 1664 | ||||
| 1665 | static char * | |||
| 1666 | _is_magic( np )/* | |||
| 1667 | ================= | |||
| 1668 | return NULL if np does not points at a string of the form | |||
| 1669 | .<chars>.<chars> or .<chars> | |||
| 1670 | where chars are "visible characters" for the current locale. If np is of the | |||
| 1671 | first form we return a pointer to the second '.' and for the second form we | |||
| 1672 | return a pointer to the '.'. | |||
| 1673 | ||||
| 1674 | NOTE: reject target if it contains / or begins with .. | |||
| 1675 | reject also .INIT and .DONE because they are mentioned in the | |||
| 1676 | man page. */ | |||
| 1677 | char *np; | |||
| 1678 | { | |||
| 1679 | register char *n; | |||
| 1680 | ||||
| 1681 | n = np; | |||
| 1682 | if( *n != '.' ) return( NIL(char)((char*)((void*)0)) ); | |||
| 1683 | if (strchr(DirBrkStr, *(n+1))!=NULL((void*)0) || *(n+1) == '.' ) | |||
| 1684 | return (NIL(char)((char*)((void*)0))); | |||
| 1685 | if( !strcmp( n+1, "INIT" ) || !strcmp( n+1, "DONE" ) ) | |||
| 1686 | return (NIL(char)((char*)((void*)0))); | |||
| 1687 | ||||
| 1688 | for( n++; isgraph(*n)((*__ctype_b_loc ())[(int) ((*n))] & (unsigned short int) _ISgraph) && (*n != '.'); n++ ); | |||
| 1689 | ||||
| 1690 | if( *n != '\0' ) { | |||
| 1691 | if( *n != '.' ) return( NIL(char)((char*)((void*)0)) ); | |||
| 1692 | for( np = n++; isgraph( *n )((*__ctype_b_loc ())[(int) ((*n))] & (unsigned short int) _ISgraph) && (*n != '.'); n++ ); | |||
| 1693 | if( *n != '\0' ) return( NIL(char)((char*)((void*)0)) ); | |||
| 1694 | } | |||
| 1695 | /* Until dmake 4.5 a .<suffix> target was ignored when AUGMAKE was | |||
| 1696 | * set and evaluated as a meta target if unset (also for -A). | |||
| 1697 | * To keep maximum compatibility accept this regardles of the AUGMAKE | |||
| 1698 | * status. */ | |||
| 1699 | ||||
| 1700 | /* np points at the second . of .<chars>.<chars> string. | |||
| 1701 | * if the special target is of the form .<chars> then np points at the | |||
| 1702 | * first . in the token. */ | |||
| 1703 | ||||
| 1704 | return( np ); | |||
| 1705 | } | |||
| 1706 | ||||
| 1707 | ||||
| 1708 | static int | |||
| 1709 | _add_root(tg)/* | |||
| 1710 | =============== | |||
| 1711 | Adds "tg" to the prerequisits list of "Targets" if "Target" is not TRUE, | |||
| 1712 | i.e. to the list of targets that are to be build. | |||
| 1713 | Instead io setting "Target" to TRUE, TRUE is returned as more targets | |||
| 1714 | might be defined in the current makefile line and they all have to be | |||
| 1715 | add to "Targets" in this case. */ | |||
| 1716 | ||||
| 1717 | CELLPTR tg; | |||
| 1718 | { | |||
| 1719 | int res = FALSE0; | |||
| 1720 | ||||
| 1721 | if(tg == Targets) | |||
| 1722 | return(TRUE1); | |||
| 1723 | ||||
| 1724 | if( !Target && !(tg->ce_flag & (F_SPECIAL0x0100|F_PERCENT0x0800)) ) { | |||
| 1725 | Add_prerequisite(Targets, tg, FALSE0, TRUE1); | |||
| 1726 | ||||
| 1727 | tg->ce_flag |= F_TARGET0x0008; | |||
| 1728 | tg->ce_attr |= A_FRINGE0x100000; | |||
| 1729 | res = TRUE1; | |||
| 1730 | } | |||
| 1731 | ||||
| 1732 | return(res); | |||
| 1733 | } |