1 /* cond.c - conditional assembly pseudo-ops, and .include 2 Copyright 1990, 1991, 1992, 1993, 1995, 1997, 1998, 2000, 2001 3 Free Software Foundation, Inc. 4 5 This file is part of GAS, the GNU Assembler. 6 7 GAS is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2, or (at your option) 10 any later version. 11 12 GAS is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with GAS; see the file COPYING. If not, write to the Free 19 Software Foundation, 59 Temple Place - Suite 330, Boston, MA 20 02111-1307, USA. */ 21 22 #include "as.h" 23 #include "macro.h" 24 25 #include "obstack.h" 26 27 /* This is allocated to grow and shrink as .ifdef/.endif pairs are 28 scanned. */ 29 struct obstack cond_obstack; 30 31 struct file_line { 32 char *file; 33 unsigned int line; 34 }; 35 36 /* We push one of these structures for each .if, and pop it at the 37 .endif. */ 38 39 struct conditional_frame { 40 /* The source file & line number of the "if". */ 41 struct file_line if_file_line; 42 /* The source file & line of the "else". */ 43 struct file_line else_file_line; 44 /* The previous conditional. */ 45 struct conditional_frame *previous_cframe; 46 /* Have we seen an else yet? */ 47 int else_seen; 48 /* Whether we are currently ignoring input. */ 49 int ignoring; 50 /* Whether a conditional at a higher level is ignoring input. 51 Set also when a branch of an "if .. elseif .." tree has matched 52 to prevent further matches. */ 53 int dead_tree; 54 /* Macro nesting level at which this conditional was created. */ 55 int macro_nest; 56 }; 57 58 static void initialize_cframe (struct conditional_frame *cframe); 59 static char *get_mri_string (int, int *); 60 61 static struct conditional_frame *current_cframe = NULL; 62 63 /* Performs the .ifdef (test_defined == 1) and 64 the .ifndef (test_defined == 0) pseudo op. */ 65 66 void 67 s_ifdef (int test_defined) 68 { 69 /* Points to name of symbol. */ 70 char *name; 71 /* Points to symbol. */ 72 symbolS *symbolP; 73 struct conditional_frame cframe; 74 char c; 75 76 /* Leading whitespace is part of operand. */ 77 SKIP_WHITESPACE (); 78 name = input_line_pointer; 79 80 if (!is_name_beginner (*name)) 81 { 82 as_bad (_("invalid identifier for \".ifdef\"")); 83 obstack_1grow (&cond_obstack, 0); 84 ignore_rest_of_line (); 85 return; 86 } 87 88 c = get_symbol_end (); 89 symbolP = symbol_find (name); 90 *input_line_pointer = c; 91 92 initialize_cframe (&cframe); 93 94 if (cframe.dead_tree) 95 cframe.ignoring = 1; 96 else 97 { 98 int is_defined; 99 100 /* Use the same definition of 'defined' as .equiv so that a symbol 101 which has been referenced but not yet given a value/address is 102 considered to be undefined. */ 103 is_defined = 104 symbolP != NULL 105 && S_IS_DEFINED (symbolP) 106 && S_GET_SEGMENT (symbolP) != reg_section; 107 108 cframe.ignoring = ! (test_defined ^ is_defined); 109 } 110 111 current_cframe = ((struct conditional_frame *) 112 obstack_copy (&cond_obstack, &cframe, 113 sizeof (cframe))); 114 115 if (LISTING_SKIP_COND () 116 && cframe.ignoring 117 && (cframe.previous_cframe == NULL 118 || ! cframe.previous_cframe->ignoring)) 119 listing_list (2); 120 121 demand_empty_rest_of_line (); 122 } 123 124 void 125 s_if (int arg) 126 { 127 expressionS operand; 128 struct conditional_frame cframe; 129 int t; 130 char *stop = NULL; 131 char stopc; 132 133 if (flag_mri) 134 stop = mri_comment_field (&stopc); 135 136 /* Leading whitespace is part of operand. */ 137 SKIP_WHITESPACE (); 138 139 if (current_cframe != NULL && current_cframe->ignoring) 140 { 141 operand.X_add_number = 0; 142 while (! is_end_of_line[(unsigned char) *input_line_pointer]) 143 ++input_line_pointer; 144 } 145 else 146 { 147 expression (&operand); 148 if (operand.X_op != O_constant) 149 as_bad (_("non-constant expression in \".if\" statement")); 150 } 151 152 switch ((operatorT) arg) 153 { 154 case O_eq: t = operand.X_add_number == 0; break; 155 case O_ne: t = operand.X_add_number != 0; break; 156 case O_lt: t = operand.X_add_number < 0; break; 157 case O_le: t = operand.X_add_number <= 0; break; 158 case O_ge: t = operand.X_add_number >= 0; break; 159 case O_gt: t = operand.X_add_number > 0; break; 160 default: 161 abort (); 162 return; 163 } 164 165 /* If the above error is signaled, this will dispatch 166 using an undefined result. No big deal. */ 167 initialize_cframe (&cframe); 168 cframe.ignoring = cframe.dead_tree || ! t; 169 current_cframe = ((struct conditional_frame *) 170 obstack_copy (&cond_obstack, &cframe, sizeof (cframe))); 171 172 if (LISTING_SKIP_COND () 173 && cframe.ignoring 174 && (cframe.previous_cframe == NULL 175 || ! cframe.previous_cframe->ignoring)) 176 listing_list (2); 177 178 if (flag_mri) 179 mri_comment_end (stop, stopc); 180 181 demand_empty_rest_of_line (); 182 } 183 184 /* Get a string for the MRI IFC or IFNC pseudo-ops. */ 185 186 static char * 187 get_mri_string (int terminator, int *len) 188 { 189 char *ret; 190 char *s; 191 192 SKIP_WHITESPACE (); 193 s = ret = input_line_pointer; 194 if (*input_line_pointer == '\'') 195 { 196 ++s; 197 ++input_line_pointer; 198 while (! is_end_of_line[(unsigned char) *input_line_pointer]) 199 { 200 *s++ = *input_line_pointer++; 201 if (s[-1] == '\'') 202 { 203 if (*input_line_pointer != '\'') 204 break; 205 ++input_line_pointer; 206 } 207 } 208 SKIP_WHITESPACE (); 209 } 210 else 211 { 212 while (*input_line_pointer != terminator 213 && ! is_end_of_line[(unsigned char) *input_line_pointer]) 214 ++input_line_pointer; 215 s = input_line_pointer; 216 while (s > ret && (s[-1] == ' ' || s[-1] == '\t')) 217 --s; 218 } 219 220 *len = s - ret; 221 return ret; 222 } 223 224 /* The MRI IFC and IFNC pseudo-ops. */ 225 226 void 227 s_ifc (int arg) 228 { 229 char *stop = NULL; 230 char stopc; 231 char *s1, *s2; 232 int len1, len2; 233 int res; 234 struct conditional_frame cframe; 235 236 if (flag_mri) 237 stop = mri_comment_field (&stopc); 238 239 s1 = get_mri_string (',', &len1); 240 241 if (*input_line_pointer != ',') 242 as_bad (_("bad format for ifc or ifnc")); 243 else 244 ++input_line_pointer; 245 246 s2 = get_mri_string (';', &len2); 247 248 res = len1 == len2 && strncmp (s1, s2, len1) == 0; 249 250 initialize_cframe (&cframe); 251 cframe.ignoring = cframe.dead_tree || ! (res ^ arg); 252 current_cframe = ((struct conditional_frame *) 253 obstack_copy (&cond_obstack, &cframe, sizeof (cframe))); 254 255 if (LISTING_SKIP_COND () 256 && cframe.ignoring 257 && (cframe.previous_cframe == NULL 258 || ! cframe.previous_cframe->ignoring)) 259 listing_list (2); 260 261 if (flag_mri) 262 mri_comment_end (stop, stopc); 263 264 demand_empty_rest_of_line (); 265 } 266 267 void 268 s_elseif (int arg) 269 { 270 if (current_cframe == NULL) 271 { 272 as_bad (_("\".elseif\" without matching \".if\"")); 273 } 274 else if (current_cframe->else_seen) 275 { 276 as_bad (_("\".elseif\" after \".else\"")); 277 as_bad_where (current_cframe->else_file_line.file, 278 current_cframe->else_file_line.line, 279 _("here is the previous \"else\"")); 280 as_bad_where (current_cframe->if_file_line.file, 281 current_cframe->if_file_line.line, 282 _("here is the previous \"if\"")); 283 } 284 else 285 { 286 as_where (¤t_cframe->else_file_line.file, 287 ¤t_cframe->else_file_line.line); 288 289 current_cframe->dead_tree |= !current_cframe->ignoring; 290 current_cframe->ignoring = current_cframe->dead_tree; 291 } 292 293 if (current_cframe == NULL || current_cframe->ignoring) 294 { 295 while (! is_end_of_line[(unsigned char) *input_line_pointer]) 296 ++input_line_pointer; 297 298 if (current_cframe == NULL) 299 return; 300 } 301 else 302 { 303 expressionS operand; 304 int t; 305 306 /* Leading whitespace is part of operand. */ 307 SKIP_WHITESPACE (); 308 309 expression (&operand); 310 if (operand.X_op != O_constant) 311 as_bad (_("non-constant expression in \".elseif\" statement")); 312 313 switch ((operatorT) arg) 314 { 315 case O_eq: t = operand.X_add_number == 0; break; 316 case O_ne: t = operand.X_add_number != 0; break; 317 case O_lt: t = operand.X_add_number < 0; break; 318 case O_le: t = operand.X_add_number <= 0; break; 319 case O_ge: t = operand.X_add_number >= 0; break; 320 case O_gt: t = operand.X_add_number > 0; break; 321 default: 322 abort (); 323 return; 324 } 325 326 current_cframe->ignoring = current_cframe->dead_tree || ! t; 327 } 328 329 if (LISTING_SKIP_COND () 330 && (current_cframe->previous_cframe == NULL 331 || ! current_cframe->previous_cframe->ignoring)) 332 { 333 if (! current_cframe->ignoring) 334 listing_list (1); 335 else 336 listing_list (2); 337 } 338 339 demand_empty_rest_of_line (); 340 } 341 342 void 343 s_endif (int arg ATTRIBUTE_UNUSED) 344 { 345 struct conditional_frame *hold; 346 347 if (current_cframe == NULL) 348 { 349 as_bad (_("\".endif\" without \".if\"")); 350 } 351 else 352 { 353 if (LISTING_SKIP_COND () 354 && current_cframe->ignoring 355 && (current_cframe->previous_cframe == NULL 356 || ! current_cframe->previous_cframe->ignoring)) 357 listing_list (1); 358 359 hold = current_cframe; 360 current_cframe = current_cframe->previous_cframe; 361 obstack_free (&cond_obstack, hold); 362 } /* if one pop too many */ 363 364 if (flag_mri) 365 { 366 while (! is_end_of_line[(unsigned char) *input_line_pointer]) 367 ++input_line_pointer; 368 } 369 370 demand_empty_rest_of_line (); 371 } 372 373 void 374 s_else (int arg ATTRIBUTE_UNUSED) 375 { 376 if (current_cframe == NULL) 377 { 378 as_bad (_("\".else\" without matching \".if\"")); 379 } 380 else if (current_cframe->else_seen) 381 { 382 as_bad (_("duplicate \"else\"")); 383 as_bad_where (current_cframe->else_file_line.file, 384 current_cframe->else_file_line.line, 385 _("here is the previous \"else\"")); 386 as_bad_where (current_cframe->if_file_line.file, 387 current_cframe->if_file_line.line, 388 _("here is the previous \"if\"")); 389 } 390 else 391 { 392 as_where (¤t_cframe->else_file_line.file, 393 ¤t_cframe->else_file_line.line); 394 395 current_cframe->ignoring = 396 current_cframe->dead_tree | !current_cframe->ignoring; 397 398 if (LISTING_SKIP_COND () 399 && (current_cframe->previous_cframe == NULL 400 || ! current_cframe->previous_cframe->ignoring)) 401 { 402 if (! current_cframe->ignoring) 403 listing_list (1); 404 else 405 listing_list (2); 406 } 407 408 current_cframe->else_seen = 1; 409 } 410 411 if (flag_mri) 412 { 413 while (! is_end_of_line[(unsigned char) *input_line_pointer]) 414 ++input_line_pointer; 415 } 416 417 demand_empty_rest_of_line (); 418 } 419 420 void 421 s_ifeqs (int arg) 422 { 423 char *s1, *s2; 424 int len1, len2; 425 int res; 426 struct conditional_frame cframe; 427 428 s1 = demand_copy_C_string (&len1); 429 430 SKIP_WHITESPACE (); 431 if (*input_line_pointer != ',') 432 { 433 as_bad (_(".ifeqs syntax error")); 434 ignore_rest_of_line (); 435 return; 436 } 437 438 ++input_line_pointer; 439 440 s2 = demand_copy_C_string (&len2); 441 442 res = len1 == len2 && strncmp (s1, s2, len1) == 0; 443 444 initialize_cframe (&cframe); 445 cframe.ignoring = cframe.dead_tree || ! (res ^ arg); 446 current_cframe = ((struct conditional_frame *) 447 obstack_copy (&cond_obstack, &cframe, sizeof (cframe))); 448 449 if (LISTING_SKIP_COND () 450 && cframe.ignoring 451 && (cframe.previous_cframe == NULL 452 || ! cframe.previous_cframe->ignoring)) 453 listing_list (2); 454 455 demand_empty_rest_of_line (); 456 } 457 458 int 459 ignore_input (void) 460 { 461 char *s; 462 463 s = input_line_pointer; 464 465 if (NO_PSEUDO_DOT || flag_m68k_mri) 466 { 467 if (s[-1] != '.') 468 --s; 469 } 470 else 471 { 472 if (s[-1] != '.') 473 return (current_cframe != NULL) && (current_cframe->ignoring); 474 } 475 476 /* We cannot ignore certain pseudo ops. */ 477 if (((s[0] == 'i' 478 || s[0] == 'I') 479 && (!strncasecmp (s, "if", 2) 480 || !strncasecmp (s, "ifdef", 5) 481 || !strncasecmp (s, "ifndef", 6))) 482 || ((s[0] == 'e' 483 || s[0] == 'E') 484 && (!strncasecmp (s, "else", 4) 485 || !strncasecmp (s, "endif", 5) 486 || !strncasecmp (s, "endc", 4)))) 487 return 0; 488 489 return (current_cframe != NULL) && (current_cframe->ignoring); 490 } 491 492 static void 493 initialize_cframe (struct conditional_frame *cframe) 494 { 495 memset (cframe, 0, sizeof (*cframe)); 496 as_where (&cframe->if_file_line.file, 497 &cframe->if_file_line.line); 498 cframe->previous_cframe = current_cframe; 499 cframe->dead_tree = current_cframe != NULL && current_cframe->ignoring; 500 cframe->macro_nest = macro_nest; 501 } 502 503 /* Give an error if a conditional is unterminated inside a macro or 504 the assembly as a whole. If NEST is non negative, we are being 505 called because of the end of a macro expansion. If NEST is 506 negative, we are being called at the of the input files. */ 507 508 void 509 cond_finish_check (int nest) 510 { 511 if (current_cframe != NULL && current_cframe->macro_nest >= nest) 512 { 513 if (nest >= 0) 514 as_bad (_("end of macro inside conditional")); 515 else 516 as_bad (_("end of file inside conditional")); 517 as_bad_where (current_cframe->if_file_line.file, 518 current_cframe->if_file_line.line, 519 _("here is the start of the unterminated conditional")); 520 if (current_cframe->else_seen) 521 as_bad_where (current_cframe->else_file_line.file, 522 current_cframe->else_file_line.line, 523 _("here is the \"else\" of the unterminated conditional")); 524 } 525 } 526 527 /* This function is called when we exit out of a macro. We assume 528 that any conditionals which began within the macro are correctly 529 nested, and just pop them off the stack. */ 530 531 void 532 cond_exit_macro (int nest) 533 { 534 while (current_cframe != NULL && current_cframe->macro_nest >= nest) 535 { 536 struct conditional_frame *hold; 537 538 hold = current_cframe; 539 current_cframe = current_cframe->previous_cframe; 540 obstack_free (&cond_obstack, hold); 541 } 542 } 543