cond.c revision 77298
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. */ 29struct obstack cond_obstack; 30 31struct 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 39struct 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 58static void initialize_cframe PARAMS ((struct conditional_frame *cframe)); 59static char *get_mri_string PARAMS ((int, int *)); 60 61static struct conditional_frame *current_cframe = NULL; 62 63void 64s_ifdef (arg) 65 int arg; 66{ 67 /* Points to name of symbol. */ 68 register char *name; 69 /* Points to symbol. */ 70 register symbolS *symbolP; 71 struct conditional_frame cframe; 72 73 /* Leading whitespace is part of operand. */ 74 SKIP_WHITESPACE (); 75 name = input_line_pointer; 76 77 if (!is_name_beginner (*name)) 78 { 79 as_bad (_("invalid identifier for \".ifdef\"")); 80 obstack_1grow (&cond_obstack, 0); 81 ignore_rest_of_line (); 82 } 83 else 84 { 85 char c; 86 87 c = get_symbol_end (); 88 symbolP = symbol_find (name); 89 *input_line_pointer = c; 90 91 initialize_cframe (&cframe); 92 cframe.ignoring = cframe.dead_tree || !((symbolP != 0) ^ arg); 93 current_cframe = ((struct conditional_frame *) 94 obstack_copy (&cond_obstack, &cframe, 95 sizeof (cframe))); 96 97 if (LISTING_SKIP_COND () 98 && cframe.ignoring 99 && (cframe.previous_cframe == NULL 100 || ! cframe.previous_cframe->ignoring)) 101 listing_list (2); 102 103 demand_empty_rest_of_line (); 104 } /* if a valid identifyer name */ 105} 106 107void 108s_if (arg) 109 int arg; 110{ 111 expressionS operand; 112 struct conditional_frame cframe; 113 int t; 114 char *stop = NULL; 115 char stopc; 116 117 if (flag_mri) 118 stop = mri_comment_field (&stopc); 119 120 /* Leading whitespace is part of operand. */ 121 SKIP_WHITESPACE (); 122 123 if (current_cframe != NULL && current_cframe->ignoring) 124 { 125 operand.X_add_number = 0; 126 while (! is_end_of_line[(unsigned char) *input_line_pointer]) 127 ++input_line_pointer; 128 } 129 else 130 { 131 expression (&operand); 132 if (operand.X_op != O_constant) 133 as_bad (_("non-constant expression in \".if\" statement")); 134 } 135 136 switch ((operatorT) arg) 137 { 138 case O_eq: t = operand.X_add_number == 0; break; 139 case O_ne: t = operand.X_add_number != 0; break; 140 case O_lt: t = operand.X_add_number < 0; break; 141 case O_le: t = operand.X_add_number <= 0; break; 142 case O_ge: t = operand.X_add_number >= 0; break; 143 case O_gt: t = operand.X_add_number > 0; break; 144 default: 145 abort (); 146 return; 147 } 148 149 /* If the above error is signaled, this will dispatch 150 using an undefined result. No big deal. */ 151 initialize_cframe (&cframe); 152 cframe.ignoring = cframe.dead_tree || ! t; 153 current_cframe = ((struct conditional_frame *) 154 obstack_copy (&cond_obstack, &cframe, sizeof (cframe))); 155 156 if (LISTING_SKIP_COND () 157 && cframe.ignoring 158 && (cframe.previous_cframe == NULL 159 || ! cframe.previous_cframe->ignoring)) 160 listing_list (2); 161 162 if (flag_mri) 163 mri_comment_end (stop, stopc); 164 165 demand_empty_rest_of_line (); 166} 167 168/* Get a string for the MRI IFC or IFNC pseudo-ops. */ 169 170static char * 171get_mri_string (terminator, len) 172 int terminator; 173 int *len; 174{ 175 char *ret; 176 char *s; 177 178 SKIP_WHITESPACE (); 179 s = ret = input_line_pointer; 180 if (*input_line_pointer == '\'') 181 { 182 ++s; 183 ++input_line_pointer; 184 while (! is_end_of_line[(unsigned char) *input_line_pointer]) 185 { 186 *s++ = *input_line_pointer++; 187 if (s[-1] == '\'') 188 { 189 if (*input_line_pointer != '\'') 190 break; 191 ++input_line_pointer; 192 } 193 } 194 SKIP_WHITESPACE (); 195 } 196 else 197 { 198 while (*input_line_pointer != terminator 199 && ! is_end_of_line[(unsigned char) *input_line_pointer]) 200 ++input_line_pointer; 201 s = input_line_pointer; 202 while (s > ret && (s[-1] == ' ' || s[-1] == '\t')) 203 --s; 204 } 205 206 *len = s - ret; 207 return ret; 208} 209 210/* The MRI IFC and IFNC pseudo-ops. */ 211 212void 213s_ifc (arg) 214 int arg; 215{ 216 char *stop = NULL; 217 char stopc; 218 char *s1, *s2; 219 int len1, len2; 220 int res; 221 struct conditional_frame cframe; 222 223 if (flag_mri) 224 stop = mri_comment_field (&stopc); 225 226 s1 = get_mri_string (',', &len1); 227 228 if (*input_line_pointer != ',') 229 as_bad (_("bad format for ifc or ifnc")); 230 else 231 ++input_line_pointer; 232 233 s2 = get_mri_string (';', &len2); 234 235 res = len1 == len2 && strncmp (s1, s2, len1) == 0; 236 237 initialize_cframe (&cframe); 238 cframe.ignoring = cframe.dead_tree || ! (res ^ arg); 239 current_cframe = ((struct conditional_frame *) 240 obstack_copy (&cond_obstack, &cframe, sizeof (cframe))); 241 242 if (LISTING_SKIP_COND () 243 && cframe.ignoring 244 && (cframe.previous_cframe == NULL 245 || ! cframe.previous_cframe->ignoring)) 246 listing_list (2); 247 248 if (flag_mri) 249 mri_comment_end (stop, stopc); 250 251 demand_empty_rest_of_line (); 252} 253 254void 255s_elseif (arg) 256 int arg; 257{ 258 if (current_cframe == NULL) 259 { 260 as_bad (_("\".elseif\" without matching \".if\" - ignored")); 261 } 262 else if (current_cframe->else_seen) 263 { 264 as_bad (_("\".elseif\" after \".else\" - ignored")); 265 as_bad_where (current_cframe->else_file_line.file, 266 current_cframe->else_file_line.line, 267 _("here is the previous \"else\"")); 268 as_bad_where (current_cframe->if_file_line.file, 269 current_cframe->if_file_line.line, 270 _("here is the previous \"if\"")); 271 } 272 else 273 { 274 as_where (¤t_cframe->else_file_line.file, 275 ¤t_cframe->else_file_line.line); 276 277 current_cframe->dead_tree |= !current_cframe->ignoring; 278 current_cframe->ignoring = current_cframe->dead_tree; 279 } 280 281 if (current_cframe == NULL || current_cframe->ignoring) 282 { 283 while (! is_end_of_line[(unsigned char) *input_line_pointer]) 284 ++input_line_pointer; 285 286 if (current_cframe == NULL) 287 return; 288 } 289 else 290 { 291 expressionS operand; 292 int t; 293 294 /* Leading whitespace is part of operand. */ 295 SKIP_WHITESPACE (); 296 297 expression (&operand); 298 if (operand.X_op != O_constant) 299 as_bad (_("non-constant expression in \".elseif\" statement")); 300 301 switch ((operatorT) arg) 302 { 303 case O_eq: t = operand.X_add_number == 0; break; 304 case O_ne: t = operand.X_add_number != 0; break; 305 case O_lt: t = operand.X_add_number < 0; break; 306 case O_le: t = operand.X_add_number <= 0; break; 307 case O_ge: t = operand.X_add_number >= 0; break; 308 case O_gt: t = operand.X_add_number > 0; break; 309 default: 310 abort (); 311 return; 312 } 313 314 current_cframe->ignoring = current_cframe->dead_tree || ! t; 315 } 316 317 if (LISTING_SKIP_COND () 318 && (current_cframe->previous_cframe == NULL 319 || ! current_cframe->previous_cframe->ignoring)) 320 { 321 if (! current_cframe->ignoring) 322 listing_list (1); 323 else 324 listing_list (2); 325 } 326 327 demand_empty_rest_of_line (); 328} 329 330void 331s_endif (arg) 332 int arg ATTRIBUTE_UNUSED; 333{ 334 struct conditional_frame *hold; 335 336 if (current_cframe == NULL) 337 { 338 as_bad (_("\".endif\" without \".if\"")); 339 } 340 else 341 { 342 if (LISTING_SKIP_COND () 343 && current_cframe->ignoring 344 && (current_cframe->previous_cframe == NULL 345 || ! current_cframe->previous_cframe->ignoring)) 346 listing_list (1); 347 348 hold = current_cframe; 349 current_cframe = current_cframe->previous_cframe; 350 obstack_free (&cond_obstack, hold); 351 } /* if one pop too many */ 352 353 if (flag_mri) 354 { 355 while (! is_end_of_line[(unsigned char) *input_line_pointer]) 356 ++input_line_pointer; 357 } 358 359 demand_empty_rest_of_line (); 360} 361 362void 363s_else (arg) 364 int arg ATTRIBUTE_UNUSED; 365{ 366 if (current_cframe == NULL) 367 { 368 as_bad (_(".else without matching .if - ignored")); 369 } 370 else if (current_cframe->else_seen) 371 { 372 as_bad (_("duplicate \"else\" - ignored")); 373 as_bad_where (current_cframe->else_file_line.file, 374 current_cframe->else_file_line.line, 375 _("here is the previous \"else\"")); 376 as_bad_where (current_cframe->if_file_line.file, 377 current_cframe->if_file_line.line, 378 _("here is the previous \"if\"")); 379 } 380 else 381 { 382 as_where (¤t_cframe->else_file_line.file, 383 ¤t_cframe->else_file_line.line); 384 385 current_cframe->ignoring = 386 current_cframe->dead_tree | !current_cframe->ignoring; 387 388 if (LISTING_SKIP_COND () 389 && (current_cframe->previous_cframe == NULL 390 || ! current_cframe->previous_cframe->ignoring)) 391 { 392 if (! current_cframe->ignoring) 393 listing_list (1); 394 else 395 listing_list (2); 396 } 397 398 current_cframe->else_seen = 1; 399 } 400 401 if (flag_mri) 402 { 403 while (! is_end_of_line[(unsigned char) *input_line_pointer]) 404 ++input_line_pointer; 405 } 406 407 demand_empty_rest_of_line (); 408} 409 410void 411s_ifeqs (arg) 412 int arg; 413{ 414 char *s1, *s2; 415 int len1, len2; 416 int res; 417 struct conditional_frame cframe; 418 419 s1 = demand_copy_C_string (&len1); 420 421 SKIP_WHITESPACE (); 422 if (*input_line_pointer != ',') 423 { 424 as_bad (_(".ifeqs syntax error")); 425 ignore_rest_of_line (); 426 return; 427 } 428 429 ++input_line_pointer; 430 431 s2 = demand_copy_C_string (&len2); 432 433 res = len1 == len2 && strncmp (s1, s2, len1) == 0; 434 435 initialize_cframe (&cframe); 436 cframe.ignoring = cframe.dead_tree || ! (res ^ arg); 437 current_cframe = ((struct conditional_frame *) 438 obstack_copy (&cond_obstack, &cframe, sizeof (cframe))); 439 440 if (LISTING_SKIP_COND () 441 && cframe.ignoring 442 && (cframe.previous_cframe == NULL 443 || ! cframe.previous_cframe->ignoring)) 444 listing_list (2); 445 446 demand_empty_rest_of_line (); 447} 448 449int 450ignore_input () 451{ 452 char *s; 453 454 s = input_line_pointer; 455 456 if (NO_PSEUDO_DOT || flag_m68k_mri) 457 { 458 if (s[-1] != '.') 459 --s; 460 } 461 else 462 { 463 if (s[-1] != '.') 464 return (current_cframe != NULL) && (current_cframe->ignoring); 465 } 466 467 /* We cannot ignore certain pseudo ops. */ 468 if (((s[0] == 'i' 469 || s[0] == 'I') 470 && (!strncasecmp (s, "if", 2) 471 || !strncasecmp (s, "ifdef", 5) 472 || !strncasecmp (s, "ifndef", 6))) 473 || ((s[0] == 'e' 474 || s[0] == 'E') 475 && (!strncasecmp (s, "else", 4) 476 || !strncasecmp (s, "endif", 5) 477 || !strncasecmp (s, "endc", 4)))) 478 return 0; 479 480 return (current_cframe != NULL) && (current_cframe->ignoring); 481} 482 483static void 484initialize_cframe (cframe) 485 struct conditional_frame *cframe; 486{ 487 memset (cframe, 0, sizeof (*cframe)); 488 as_where (&cframe->if_file_line.file, 489 &cframe->if_file_line.line); 490 cframe->previous_cframe = current_cframe; 491 cframe->dead_tree = current_cframe != NULL && current_cframe->ignoring; 492 cframe->macro_nest = macro_nest; 493} 494 495/* Give an error if a conditional is unterminated inside a macro or 496 the assembly as a whole. If NEST is non negative, we are being 497 called because of the end of a macro expansion. If NEST is 498 negative, we are being called at the of the input files. */ 499 500void 501cond_finish_check (nest) 502 int nest; 503{ 504 if (current_cframe != NULL && current_cframe->macro_nest >= nest) 505 { 506 if (nest >= 0) 507 as_bad (_("end of macro inside conditional")); 508 else 509 as_bad (_("end of file inside conditional")); 510 as_bad_where (current_cframe->if_file_line.file, 511 current_cframe->if_file_line.line, 512 _("here is the start of the unterminated conditional")); 513 if (current_cframe->else_seen) 514 as_bad_where (current_cframe->else_file_line.file, 515 current_cframe->else_file_line.line, 516 _("here is the \"else\" of the unterminated conditional")); 517 } 518} 519 520/* This function is called when we exit out of a macro. We assume 521 that any conditionals which began within the macro are correctly 522 nested, and just pop them off the stack. */ 523 524void 525cond_exit_macro (nest) 526 int nest; 527{ 528 while (current_cframe != NULL && current_cframe->macro_nest >= nest) 529 { 530 struct conditional_frame *hold; 531 532 hold = current_cframe; 533 current_cframe = current_cframe->previous_cframe; 534 obstack_free (&cond_obstack, hold); 535 } 536} 537