text: /* ----------------------------------------------------------------------- *
   *
   *   Copyright 1996-2017 The NASM Authors - All Rights Reserved
   *   See the file AUTHORS included with the NASM distribution for
   *   the specific copyright holders.
   *
   *   Redistribution and use in source and binary forms, with or without
   *   modification, are permitted provided that the following
   *   conditions are met:
   *
   *   * Redistributions of source code must retain the above copyright
   *     notice, this list of conditions and the following disclaimer.
   *   * Redistributions in binary form must reproduce the above
   *     copyright notice, this list of conditions and the following
   *     disclaimer in the documentation and/or other materials provided
   *     with the distribution.
   *
   *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
   *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
   *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
   *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
   *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
   *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   * ----------------------------------------------------------------------- */
  
  /*
   * Parse and handle assembler directives
   */
  
  #include "compiler.h"
  
  #include <stdlib.h>
  #include <string.h>
  #include <ctype.h>
  #include <limits.h>
  
  #include "nasm.h"
  #include "nasmlib.h"
  #include "error.h"
  #include "float.h"
  #include "stdscan.h"
  #include "preproc.h"
  #include "eval.h"
  #include "assemble.h"
  #include "outform.h"
  #include "listing.h"
  #include "labels.h"
  #include "iflag.h"
  
  static iflag_t get_cpu(char *value)
  {
      iflag_t r;
  
      iflag_clear_all(&r);
  
      if (!strcmp(value, "8086"))
          iflag_set(&r, IF_8086);
      else if (!strcmp(value, "186"))
          iflag_set(&r, IF_186);
      else if (!strcmp(value, "286"))
          iflag_set(&r, IF_286);
      else if (!strcmp(value, "386"))
          iflag_set(&r, IF_386);
      else if (!strcmp(value, "486"))
          iflag_set(&r, IF_486);
      else if (!strcmp(value, "586") ||
               !nasm_stricmp(value, "pentium"))
          iflag_set(&r, IF_PENT);
      else if (!strcmp(value, "686")              ||
               !nasm_stricmp(value, "ppro")       ||
               !nasm_stricmp(value, "pentiumpro") ||
               !nasm_stricmp(value, "p2"))
          iflag_set(&r, IF_P6);
      else if (!nasm_stricmp(value, "p3") ||
               !nasm_stricmp(value, "katmai"))
          iflag_set(&r, IF_KATMAI);
      else if (!nasm_stricmp(value, "p4") ||   /* is this right? -- jrc */
               !nasm_stricmp(value, "willamette"))
          iflag_set(&r, IF_WILLAMETTE);
      else if (!nasm_stricmp(value, "prescott"))
          iflag_set(&r, IF_PRESCOTT);
      else if (!nasm_stricmp(value, "x64") ||
               !nasm_stricmp(value, "x86-64"))
          iflag_set(&r, IF_X86_64);
      else if (!nasm_stricmp(value, "ia64")   ||
               !nasm_stricmp(value, "ia-64")  ||
               !nasm_stricmp(value, "itanium")||
               !nasm_stricmp(value, "itanic") ||
               !nasm_stricmp(value, "merced"))
          iflag_set(&r, IF_IA64);
      else {
          iflag_set(&r, IF_PLEVEL);
          nasm_error(pass0 < 2 ? ERR_NONFATAL : ERR_FATAL,
                     "unknown 'cpu' type");
      }
      return r;
  }
  
  static int get_bits(char *value)
  {
      int i;
  
      if ((i = atoi(value)) == 16)
          return i;               /* set for a 16-bit segment */
      else if (i == 32) {
          if (iflag_ffs(&cpu) < IF_386) {
              nasm_error(ERR_NONFATAL,
                           "cannot specify 32-bit segment on processor below a 386");
              i = 16;
          }
      } else if (i == 64) {
          if (iflag_ffs(&cpu) < IF_X86_64) {
              nasm_error(ERR_NONFATAL,
                           "cannot specify 64-bit segment on processor below an x86-64");
              i = 16;
          }
      } else {
          nasm_error(pass0 < 2 ? ERR_NONFATAL : ERR_FATAL,
                       "`%s' is not a valid segment size; must be 16, 32 or 64",
                       value);
          i = 16;
      }
      return i;
  }
  
  static enum directive parse_directive_line(char **directive, char **value)
  {
      char *p, *q, *buf;
  
      buf = nasm_skip_spaces(*directive);
  
      /*
       * It should be enclosed in [ ].
       * XXX: we don't check there is nothing else on the remainder of the
       * line, except a possible comment.
       */
      if (*buf != '[')
          return D_none;
      q = strchr(buf, ']');
      if (!q)
          return D_corrupt;
  
      /*
       * Strip off the comments.  XXX: this doesn't account for quoted
       * strings inside a directive.  We should really strip the
       * comments in generic code, not here.  While we're at it, it
       * would be better to pass the backend a series of tokens instead
       * of a raw string, and actually process quoted strings for it,
       * like of like argv is handled in C.
       */
      p = strchr(buf, ';');
      if (p) {
          if (p < q) /* ouch! somewhere inside */
              return D_corrupt;
          *p = '\0';
      }
  
      /* no brace, no trailing spaces */
      *q = '\0';
      nasm_zap_spaces_rev(--q);
  
      /* directive */
      p = nasm_skip_spaces(++buf);
      q = nasm_skip_word(p);
      if (!q)
          return D_corrupt; /* sigh... no value there */
      *q = '\0';
      *directive = p;
  
      /* and value finally */
      p = nasm_skip_spaces(++q);
      *value = p;
  
      return directive_find(*directive);
  }
  
  /*
   * Process a line from the assembler and try to handle it if it
   * is a directive.  Return true if the line was handled (including
   * if it was an error), false otherwise.
   */
  bool process_directives(char *directive)
  {
      enum directive d;
      char *value, *p, *q, *special;
      struct tokenval tokval;
      bool bad_param = false;
      int pass2 = passn > 1 ? 2 : 1;
  
      d = parse_directive_line(&directive, &value);
  
      switch (d) {
      case D_none:
          return D_none;      /* Not a directive */
  
      case D_corrupt:
  	nasm_error(ERR_NONFATAL, "invalid directive line");
  	break;
  
      default:			/* It's a backend-specific directive */
          switch (ofmt->directive(d, value, pass2)) {
          case DIRR_UNKNOWN:
              goto unknown;
          case DIRR_OK:
          case DIRR_ERROR:
              break;
          case DIRR_BADPARAM:
              bad_param = true;
              break;
          default:
              panic();
          }
          break;
  
      case D_unknown:
      unknown:
          nasm_error(pass0 < 2 ? ERR_NONFATAL : ERR_PANIC,
                     "unrecognised directive [%s]", directive);
          break;
  
      case D_SEGMENT:         /* [SEGMENT n] */
      case D_SECTION:
      {
  	int sb = globalbits;
          int32_t seg = ofmt->section(value, pass2, &sb);
  
          if (seg == NO_SEG) {
              nasm_error(pass0 < 2 ? ERR_NONFATAL : ERR_PANIC,
                         "segment name `%s' not recognized", value);
          } else {
              in_absolute = false;
              location.segment = seg;
              globalbits = sb;
          }
          break;
      }
  
      case D_SECTALIGN:       /* [SECTALIGN n] */
      {
  	expr *e;
  
          if (*value) {
              stdscan_reset();
              stdscan_set(value);
              tokval.t_type = TOKEN_INVALID;
              e = evaluate(stdscan, NULL, &tokval, NULL, pass2, NULL);
              if (e) {
                  uint64_t align = e->value;
  
  		if (!is_power2(e->value)) {
                      nasm_error(ERR_NONFATAL,
                                 "segment alignment `%s' is not power of two",
                                 value);
  		} else if (align > UINT64_C(0x7fffffff)) {
                      /*
                       * FIXME: Please make some sane message here
                       * ofmt should have some 'check' method which
                       * would report segment alignment bounds.
                       */
  		    nasm_error(ERR_NONFATAL,
  			       "absurdly large segment alignment `%s' (2^%d)",
  			       value, ilog2_64(align));
                  }
  
                  /* callee should be able to handle all details */
                  if (location.segment != NO_SEG)
                      ofmt->sectalign(location.segment, align);
              }
          }
          break;
      }
  
      case D_EXTERN:          /* [EXTERN label:special] */
          if (*value == '$')
              value++;        /* skip initial $ if present */
          if (pass0 == 2) {
              q = value;
              while (*q && *q != ':')
                  q++;
              if (*q == ':') {
                  *q++ = '\0';
                  ofmt->symdef(value, 0L, 0L, 3, q);
              }
          } else if (passn == 1) {
              bool validid = true;
              q = value;
              if (!isidstart(*q))
                  validid = false;
              while (*q && *q != ':') {
                  if (!isidchar(*q))
                      validid = false;
                  q++;
              }
              if (!validid) {
                  nasm_error(ERR_NONFATAL, "identifier expected after EXTERN");
                  break;
              }
              if (*q == ':') {
                  *q++ = '\0';
                  special = q;
              } else
                  special = NULL;
              if (!is_extern(value)) {        /* allow re-EXTERN to be ignored */
                  int temp = pass0;
                  pass0 = 1;  /* fake pass 1 in labels.c */
                  declare_as_global(value, special);
                  define_label(value, seg_alloc(), 0L, NULL,
                               false, true);
                  pass0 = temp;
              }
          }           /* else  pass0 == 1 */
          break;
  
      case D_BITS:            /* [BITS bits] */
          globalbits = get_bits(value);
          break;
  
      case D_GLOBAL:          /* [GLOBAL symbol:special] */
          if (*value == '$')
              value++;        /* skip initial $ if present */
          if (pass0 == 2) {   /* pass 2 */
              q = value;
              while (*q && *q != ':')
                  q++;
              if (*q == ':') {
                  *q++ = '\0';
                  ofmt->symdef(value, 0L, 0L, 3, q);
              }
          } else if (pass2 == 1) {    /* pass == 1 */
              bool validid = true;
  
              q = value;
              if (!isidstart(*q))
                  validid = false;
              while (*q && *q != ':') {
                  if (!isidchar(*q))
                      validid = false;
                  q++;
              }
              if (!validid) {
                  nasm_error(ERR_NONFATAL,
                             "identifier expected after GLOBAL");
                  break;
              }
              if (*q == ':') {
                  *q++ = '\0';
                  special = q;
              } else
                  special = NULL;
              declare_as_global(value, special);
          }           /* pass == 1 */
          break;
  
      case D_COMMON:          /* [COMMON symbol size:special] */
      {
          int64_t size;
  	bool rn_error;
  	bool validid;
  
          if (*value == '$')
              value++;        /* skip initial $ if present */
          p = value;
          validid = true;
          if (!isidstart(*p))
              validid = false;
          while (*p && !nasm_isspace(*p)) {
              if (!isidchar(*p))
                  validid = false;
              p++;
          }
          if (!validid) {
              nasm_error(ERR_NONFATAL, "identifier expected after COMMON");
              break;
          }
          if (*p) {
              p = nasm_zap_spaces_fwd(p);
              q = p;
              while (*q && *q != ':')
                  q++;
              if (*q == ':') {
                  *q++ = '\0';
                  special = q;
              } else {
                  special = NULL;
              }
              size = readnum(p, &rn_error);
              if (rn_error) {
                  nasm_error(ERR_NONFATAL,
                             "invalid size specified"
                             " in COMMON declaration");
                  break;
              }
          } else {
              nasm_error(ERR_NONFATAL,
                         "no size specified in"
                         " COMMON declaration");
              break;
          }
  
          if (pass0 < 2) {
              define_common(value, seg_alloc(), size, special);
          } else if (pass0 == 2) {
              if (special)
                  ofmt->symdef(value, 0L, 0L, 3, special);
          }
          break;
      }
  
      case D_ABSOLUTE:        /* [ABSOLUTE address] */
      {
  	expr *e;
  
          stdscan_reset();
          stdscan_set(value);
          tokval.t_type = TOKEN_INVALID;
          e = evaluate(stdscan, NULL, &tokval, NULL, pass2, NULL);
          if (e) {
              if (!is_reloc(e))
                  nasm_error(pass0 ==
                             1 ? ERR_NONFATAL : ERR_PANIC,
                             "cannot use non-relocatable expression as "
                             "ABSOLUTE address");
              else {
                  absolute.segment = reloc_seg(e);
                  absolute.offset = reloc_value(e);
              }
          } else if (passn == 1)
              absolute.offset = 0x100;     /* don't go near zero in case of / */
          else
              nasm_panic(0, "invalid ABSOLUTE address "
                         "in pass two");
          in_absolute = true;
          location.segment = NO_SEG;
          break;
      }
  
      case D_DEBUG:           /* [DEBUG] */
      {
          bool badid, overlong;
  	char debugid[128];
  
          p = value;
          q = debugid;
          badid = overlong = false;
          if (!isidstart(*p)) {
              badid = true;
          } else {
              while (*p && !nasm_isspace(*p)) {
                  if (q >= debugid + sizeof debugid - 1) {
                      overlong = true;
                      break;
                  }
                  if (!isidchar(*p))
                      badid = true;
                  *q++ = *p++;
              }
              *q = 0;
          }
          if (badid) {
              nasm_error(passn == 1 ? ERR_NONFATAL : ERR_PANIC,
                         "identifier expected after DEBUG");
              break;
          }
          if (overlong) {
              nasm_error(passn == 1 ? ERR_NONFATAL : ERR_PANIC,
                         "DEBUG identifier too long");
              break;
          }
          p = nasm_skip_spaces(p);
          if (pass0 == 2)
              dfmt->debug_directive(debugid, p);
          break;
      }
  
      case D_WARNING:         /* [WARNING {+|-|*}warn-name] */
          if (!set_warning_status(value)) {
              nasm_error(ERR_WARNING|ERR_WARN_UNK_WARNING,
                         "unknown warning option: %s", value);
          }
          break;
  
      case D_CPU:         /* [CPU] */
          cpu = get_cpu(value);
          break;
  
      case D_LIST:        /* [LIST {+|-}] */
          value = nasm_skip_spaces(value);
          if (*value == '+') {
              user_nolist = false;
          } else {
              if (*value == '-') {
                  user_nolist = true;
              } else {
                  bad_param = true;
              }
          }
          break;
  
      case D_DEFAULT:         /* [DEFAULT] */
          stdscan_reset();
          stdscan_set(value);
          tokval.t_type = TOKEN_INVALID;
          if (stdscan(NULL, &tokval) != TOKEN_INVALID) {
              switch (tokval.t_integer) {
              case S_REL:
                  globalrel = 1;
                  break;
              case S_ABS:
                  globalrel = 0;
                  break;
              case P_BND:
                  globalbnd = 1;
                  break;
              case P_NOBND:
                  globalbnd = 0;
                  break;
              default:
                  bad_param = true;
                  break;
              }
          } else {
              bad_param = true;
          }
          break;
  
      case D_FLOAT:
          if (float_option(value)) {
              nasm_error(pass0 < 2 ? ERR_NONFATAL : ERR_PANIC,
                         "unknown 'float' directive: %s", value);
          }
          break;
  
      case D_PRAGMA:
          process_pragma(value);
          break;
      }
  
  
      /* A common error message */
      if (bad_param) {
          nasm_error(ERR_NONFATAL, "invalid parameter to [%s] directive",
                     directive);
      }
  
      return d != D_none;
  }
  