Logo Search packages:      
Sourcecode: cableswig version File versions  Download package

cpp.c

/* -----------------------------------------------------------------------------
 * cpp.c
 *
 *     An implementation of a C preprocessor plus some support for additional
 *     SWIG directives.
 *
 *         - SWIG directives such as %include, %extern, and %import are handled
 *         - A new macro %define ... %enddef can be used for multiline macros
 *         - No preprocessing is performed in %{ ... %} blocks
 *         - Lines beginning with %# are stripped down to #... and passed through.
 *
 * Author(s) : David Beazley (beazley@cs.uchicago.edu)
 *
 * Copyright (C) 1999-2000.  The University of Chicago
 * See the file LICENSE for information on usage and redistribution.
 * ----------------------------------------------------------------------------- */

char cvsroot_cpp_c[] = "/cvsroot/SWIG/Source/Preprocessor/cpp.c,v 1.52 2004/01/15 22:46:06 cheetah Exp";

#include "swig.h"
#include "preprocessor.h"
#include <ctype.h>

static Hash     *cpp = 0;                /* C preprocessor data */
static int       include_all = 0;        /* Follow all includes */
static int       ignore_missing = 0; 
static int       import_all = 0;         /* Follow all includes, but as %import statements */
static int       single_include = 1;     /* Only include each file once */
static Hash     *included_files = 0;
static List     *dependencies = 0;

/* Test a character to see if it starts an identifier */
static int
isidentifier(int c) {
  if ((isalpha(c)) || (c == '_') || (c == '$')) return 1;
  else return 0;
}

/* Test a character to see if it valid in an identifier (after the first letter) */
static int
isidchar(int c) {
  if ((isalnum(c)) || (c == '_') || (c == '$')) return 1;
  else return 0;
}

/* Skip whitespace */
static void
skip_whitespace(File *s, File *out) {
  int c;
  while ((c = Getc(s)) != EOF) {
    if (!isspace(c)) {
      Ungetc(c,s);
      break;
    } else if (out) Putc(c,out);
  }
}

/* Skip to a specified character taking line breaks into account */
static int
skip_tochar(File *s, int ch, File *out) {
  int c;
  while ((c = Getc(s)) != EOF) {
    if (out) Putc(c,out);
    if (c == ch) break;
    if (c == '\\') {
      c = Getc(s);
      if ((c != EOF) && (out)) Putc(c,out);
    }
  }
  if (c == EOF) return -1;
  return 0;
}

static void
copy_location(DOH *s1, DOH *s2) {
  Setfile(s2,Getfile(s1));
  Setline(s2,Getline(s1));
}

static String *cpp_include(String_or_char *fn) {
  String *s;
  s = Swig_include(fn);
  if (s && single_include) {
    String *file = Getfile(s);
    if (Getattr(included_files,file)) {
      Delete(s);
      return 0;
    }
    Setattr(included_files,file,file);
  }
  if (!s) {
    Seek(fn,0,SEEK_SET);
    if (ignore_missing) {
      Swig_warning(WARN_PP_MISSING_FILE,Getfile(fn),Getline(fn),"Unable to find '%s'\n", fn);
    } else {
      Swig_error(Getfile(fn),Getline(fn),"Unable to find '%s'\n", fn);
    }
  } else {
    Seek(s,0,SEEK_SET);
    if (!dependencies) {
      dependencies = NewList();
    }
    Append(dependencies, Copy(Swig_last_file()));
  }
  return s;
}

List *Preprocessor_depend(void) {
  return dependencies;
}

/* -----------------------------------------------------------------------------
 * void Preprocessor_cpp_init() - Initialize the preprocessor
 * ----------------------------------------------------------------------------- */
void Preprocessor_init(void) {
  Hash *s;
  cpp = NewHash();
  s =   NewHash();
  Setattr(cpp,"symbols",s);
  Preprocessor_expr_init();            /* Initialize the expression evaluator */
  included_files = NewHash();
}
/* -----------------------------------------------------------------------------
 * void Preprocessor_include_all() - Instruct preprocessor to include all files
 * ----------------------------------------------------------------------------- */
void Preprocessor_include_all(int a) {
  include_all = a;
}

void Preprocessor_import_all(int a) {
  import_all = a;
}

void Preprocessor_ignore_missing(int a) {
  ignore_missing = a;
}


/* -----------------------------------------------------------------------------
 * Preprocessor_define()
 *
 * Defines a new C preprocessor symbol.   swigmacro specifies whether or not the macro has
 * SWIG macro semantics.
 * ----------------------------------------------------------------------------- */

 
String_or_char *Macro_vararg_name(String_or_char *str,
                                  String_or_char *line)
{
  String_or_char *argname, *varargname;
  char *s, *dots;

  argname = Copy(str);
  s = Char(argname);
  dots = strchr(s, '.');
  if (!dots) return NULL;
  if (strcmp(dots, "...") != 0) {
    Swig_error(Getfile(line), Getline(line),
               "Illegal macro argument name '%s'\n", str);  
    return NULL;
  }
  if (dots == s) {
      varargname = NewString("__VA_ARGS__");
  } else {
    *dots = '\0';
    varargname = NewStringf(argname);
  }
  Delete(argname);
  return varargname;
}

Hash *Preprocessor_define(const String_or_char *_str, int swigmacro)
{
  String *macroname = 0, *argstr = 0, *macrovalue = 0, *file = 0, *s = 0;
  Hash   *macro = 0, *symbols = 0, *m1;
  List   *arglist = 0;
  int c, line;
  int    varargs = 0;
  String_or_char *str = (String_or_char *) _str;

  assert(cpp);
  assert(str);

  /* First make sure that string is actually a string */
  if (DohCheck(str)) {
    s = NewString(str);
    copy_location(str,s);
    str = s;
  } else {
    str = NewString((char *) str);
  }
  Seek(str,0,SEEK_SET);
  line = Getline(str);
  file = Getfile(str);

  /* Skip over any leading whitespace */
  skip_whitespace(str,0);

  /* Now look for a macro name */
  macroname = NewString("");
  while ((c = Getc(str)) != EOF) {
    if (c == '(') {
      argstr = NewString("");
      copy_location(str,argstr);
      /* It is a macro.  Go extract it's argument string */
      while ((c = Getc(str)) != EOF) {
      if (c == ')') break;
      else Putc(c,argstr);
      }
      if (c != ')') {
      Swig_error(Getfile(str),Getline(str), "Missing \')\' in macro parameters\n");
      goto macro_error;
      }
      break;
    } else if (isidchar(c) || (c == '%')) {
      Putc(c,macroname);
    } else if (isspace(c)) {
      break;
    } else if (c == '\\') {
      c = Getc(str);
      if (c != '\n') {
      Ungetc(c,str);
      Ungetc('\\',str);
      break;
      }
    } else {
      /*Swig_error(Getfile(str),Getline(str),"Illegal character in macro name\n");
      goto macro_error; */
      Ungetc(c,str);
      break;
    }
  }
  if (!swigmacro)
    skip_whitespace(str,0);
  macrovalue = NewString("");
  while ((c = Getc(str)) != EOF) {
    Putc(c,macrovalue);
  }

  /* If there are any macro arguments, convert into a list */
  if (argstr) {
    String *argname, *varargname;
    arglist = NewList();
    Seek(argstr,0,SEEK_SET);
    argname = NewString("");
    while ((c = Getc(argstr)) != EOF) {
      if (c == ',') {
      varargname = Macro_vararg_name(argname, str);
      if (varargname) {
          Swig_error(Getfile(str),Getline(str),"Variable-length macro argument must be last parameter\n");    
      } else {
        Append(arglist,argname);
      }
      Delete(argname);
      argname = NewString("");
      } else if (isidchar(c) || (c == '.')) {
      Putc(c,argname);
      } else if (!(isspace(c) || (c == '\\'))) {
      Swig_error(Getfile(str),Getline(str),"Illegal character in macro argument name\n");
      goto macro_error;
      }
    }
    if (Len(argname)) {
      /* Check for varargs */
      varargname = Macro_vararg_name(argname, str);
      if (varargname) {
      Append(arglist,varargname);
      Delete(varargname);
      varargs = 1;
      } else {
      Append(arglist,argname);
      }
      Delete(argname);
    }
  }

  if (!swigmacro) {
    Replace(macrovalue,"\\\n"," ", DOH_REPLACE_NOQUOTE);
  }

  /* Look for special # substitutions.   We only consider # that appears
     outside of quotes and comments */

  {
    int state = 0;
    char *cc = Char(macrovalue);
    while (*cc) {
      switch(state) {
      case 0:
      if (*cc == '#') *cc = '\001';
      else if (*cc == '/') state = 10;
      else if (*cc == '\'') state = 20;
      else if (*cc == '\"') state = 30;
      break;
      case 10:
      if (*cc == '*') state = 11;
      else if (*cc == '/') state = 15;
      else {
        state = 0;
        cc--;
      }
      break;
      case 11:
      if (*cc == '*') state = 12;
      break;
      case 12:
      if (*cc == '/') state = 0;
      else if (*cc != '*') state = 11;
      break;
      case 15:
      if (*cc == '\n') state = 0;
      break;
      case 20:
      if (*cc == '\'') state = 0;
      if (*cc == '\\') state = 21;
      break;
      case 21:
      state = 20;
      break;
      case 30:
      if (*cc == '\"') state = 0;
      if (*cc == '\\') state = 31;
      break;
      case 31:
      state = 30;
      break;
      default:
      break;
      }
      cc++;
    }
  }

  /* Get rid of whitespace surrounding # */
  /*  Replace(macrovalue,"#","\001",DOH_REPLACE_NOQUOTE); */
  while(strstr(Char(macrovalue),"\001 ")) {
    Replace(macrovalue,"\001 ","\001", DOH_REPLACE_ANY);
  }
  while(strstr(Char(macrovalue)," \001")) {
    Replace(macrovalue," \001","\001", DOH_REPLACE_ANY);
  }
  /* Replace '##' with a special token */
  Replace(macrovalue,"\001\001","\002", DOH_REPLACE_ANY);

  /* Go create the macro */
  macro = NewHash();
  Setattr(macro,"name", macroname);
  if (arglist) {
    Setattr(macro,"args",arglist);
    Delete(arglist);
    if (varargs) {
      Setattr(macro,"varargs","1");
    }
  }
  Setattr(macro,"value",macrovalue);
  Delete(macrovalue);
  Setline(macro,line);
  Setfile(macro,file);
  if (swigmacro) {
    Setattr(macro,"swigmacro","1");
  }
  symbols = Getattr(cpp,"symbols");
  if ((m1 = Getattr(symbols,macroname))) {
    if (Cmp(Getattr(m1,"value"),macrovalue)) {
      Swig_error(Getfile(str),Getline(str),"Macro '%s' redefined,\n",macroname);    
      Swig_error(Getfile(m1),Getline(m1),"previous definition of '%s'.\n",macroname);
      goto macro_error;
    }
  }
  Setattr(symbols,macroname,macro);
  
  Delete(str);
  Delete(argstr);
  Delete(macroname);
  return macro;

 macro_error:
  Delete(str);
  Delete(argstr);
  Delete(macroname);
  return 0;
}

/* -----------------------------------------------------------------------------
 * Preprocessor_undef()
 *
 * Undefines a macro.
 * ----------------------------------------------------------------------------- */
void Preprocessor_undef(const String_or_char *str)
{
  Hash *symbols;
  assert(cpp);
  symbols = Getattr(cpp,"symbols");
  Delattr(symbols,str);
}

/* -----------------------------------------------------------------------------
 * find_args()
 *
 * Isolates macro arguments and returns them in a list.   For each argument,
 * leading and trailing whitespace is stripped (ala K&R, pg. 230).
 * ----------------------------------------------------------------------------- */
static List *
find_args(String *s)
{
  List   *args;
  String *str;
  int   c, level;
  long  pos;

  /* Create a new list */
  args = NewList();
  copy_location(s,args);

  /* First look for a '(' */
  pos = Tell(s);
  skip_whitespace(s,0);

  /* Now see if the next character is a '(' */
  c = Getc(s);
  if (c != '(') {
    /* Not a macro, bail out now! */
    Seek(s,pos, SEEK_SET);
    Delete(args);
    return 0;
  }
  c = Getc(s);
  /* Okay.  This appears to be a macro so we will start isolating arguments */
  while (c != EOF) {
    if (isspace(c)) {
      skip_whitespace(s,0);                    /* Skip leading whitespace */
      c = Getc(s);
    }
    str = NewString("");
    copy_location(s,str);
    level = 0;
    while (c != EOF) {
      if (c == '\"') {
      Putc(c,str);
      skip_tochar(s,'\"',str);
      c = Getc(s);
      continue;
      } else if (c == '\'') {
      Putc(c,str);
      skip_tochar(s,'\'',str);
      c = Getc(s);
      continue;
      }
      if ((c == ',') && (level == 0)) break;
      if ((c == ')') && (level == 0)) break;
      Putc(c,str);
      if (c == '(') level++;
      if (c == ')') level--;
      c = Getc(s);
    }
    if (level > 0) {
      goto unterm;
    }
    Chop(str);
    if (Len(args) || Len(str))
      Append(args,str);
    Delete(str);

    /*    if (Len(str) && (c != ')'))
        Append(args,str); */

    if (c == ')') return args;
    c = Getc(s);
  }
 unterm:
  Swig_error(Getfile(args),Getline(args),"Unterminated macro call.\n");
  return args;
}

/* -----------------------------------------------------------------------------
 * DOH *get_filename(DOH *str)
 *
 * Read a filename from str.   A filename can be enclose in quotes, angle brackets,
 * or bare.
 * ----------------------------------------------------------------------------- */

static String *
get_filename(String *str) {
  String *fn;
  int  c;

  skip_whitespace(str,0);
  fn = NewString("");
  copy_location(str,fn);
  c = Getc(str);
  if (c == '\"') {
    while (((c = Getc(str)) != EOF) && (c != '\"')) Putc(c,fn);
  } else if (c == '<') {
    while (((c = Getc(str)) != EOF) && (c != '>'))  Putc(c,fn);
  } else {
    Putc(c,fn);
    while (((c = Getc(str)) != EOF) && (!isspace(c))) Putc(c,fn);
    if (isspace(c)) Ungetc(c,str);
  }
#if defined(_WIN32) || defined(MACSWIG)
  /* accept Unix path separator on non-Unix systems */
  Replaceall(fn, "/", SWIG_FILE_DELIMETER);
#endif
#if defined(__CYGWIN__)
  /* accept Windows path separator in addition to Unix path separator */
  Replaceall(fn, "\\", SWIG_FILE_DELIMETER);
#endif
  Seek(fn,0,SEEK_SET);
  return fn;
}

static String *
get_options(String *str) {

  int  c;
  skip_whitespace(str,0);
  c = Getc(str);
  if (c == '(') {
    String *opt;
    int     level = 1;
    opt = NewString("(");
    while (((c = Getc(str)) != EOF)) {
      Putc(c,opt);
      if (c == ')') {
      level--;
      if (!level) return opt;
      }
      if (c == '(') level++;
    }
    Delete(opt);
    return 0;
  } else {
    Ungetc(c,str);
    return 0;
  }
}
/* -----------------------------------------------------------------------------
 * expand_macro()
 *
 * Perform macro expansion and return a new string.  Returns NULL if some sort
 * of error occurred.
 * ----------------------------------------------------------------------------- */

static String *
expand_macro(String_or_char *name, List *args)
{
  DOH *symbols, *ns, *macro, *margs, *mvalue, *temp, *tempa, *e;
  DOH *Preprocessor_replace(DOH *);
  int i, l;
  int isvarargs = 0;

  symbols = Getattr(cpp,"symbols");
  if (!symbols) return 0;

  /* See if the name is actually defined */
  macro = Getattr(symbols,name);
  if (!macro) return 0;
  if (Getattr(macro,"*expanded*")) {
    ns = NewString("");
    Printf(ns,"%s",name);
    if (args) {
      if (Len(args))
      Putc('(',ns);
      for (i = 0; i < Len(args); i++) {
      Printf(ns,"%s",Getitem(args,i));
      if (i < (Len(args) -1)) Putc(',',ns);
      }
      if (i)
      Putc(')',ns);
    }
    return ns;
  }

  /* Get macro arguments and value */
  mvalue = Getattr(macro,"value");
  assert(mvalue);
  margs = Getattr(macro,"args");

  if (args && Getattr(macro,"varargs")) {
    isvarargs = 1;
    /* Variable length argument macro.  We need to collect all of the extra arguments into a single argument */
    if (Len(args) >= (Len(margs)-1)) {
      int i;
      int vi, na;
      String *vararg = NewString("");
      vi = Len(margs)-1;
      na = Len(args);
      for (i = vi; i < na; i++) {
      Append(vararg,Getitem(args,i));
      if ((i+1) < na) {
        Append(vararg,",");
      }
      }
      /* Remove arguments */
      for (i = vi; i < na; i++) {
      Delitem(args,vi);
      }
      Append(args,vararg);
      Delete(vararg);
    }
  }
  /* If there are arguments, see if they match what we were given */
  if (args && (margs) && (Len(margs) != Len(args))) {
    if (Len(margs) > (1+isvarargs))
      Swig_error(Getfile(args),Getline(args),"Macro '%s' expects %d arguments\n", name, Len(margs)-isvarargs);
    else if (Len(margs) == (1+isvarargs))
      Swig_error(Getfile(args),Getline(args),"Macro '%s' expects 1 argument\n", name);
    else
      Swig_error(Getfile(args),Getline(args),"Macro '%s' expects no arguments\n", name);
    return 0;
  }

  /* If the macro expects arguments, but none were supplied, we leave it in place */
  if (!args && (margs)) {
    return NewString(name);
  }

  /* Copy the macro value */
  ns = Copy(mvalue);
  copy_location(mvalue,ns);

  /* Tag the macro as being expanded.   This is to avoid recursion in
     macro expansion */

  temp = NewString("");
  tempa = NewString("");
  if (args && margs) {
    l = Len(margs);
    for (i = 0; i < l; i++) {
      DOH *arg, *aname;
      String *reparg;
      arg = Getitem(args,i);           /* Get an argument value */
      reparg = Preprocessor_replace(arg);
      aname = Getitem(margs,i);        /* Get macro argument name */
      if (strstr(Char(ns),"\001")) {
      /* Try to replace a quoted version of the argument */
      Clear(temp);
      Clear(tempa);
      Printf(temp,"\001%s", aname);
      Printf(tempa,"\"%s\"",arg);
      Replace(ns, temp, tempa, DOH_REPLACE_ID_END);
      }
      if (strstr(Char(ns),"\002")) {
      /* Look for concatenation tokens */
      Clear(temp);
      Clear(tempa);
      Printf(temp,"\002%s",aname);
      Append(tempa,"\002\003");
      Replace(ns, temp, tempa, DOH_REPLACE_ID_END);
      Clear(temp);
      Clear(tempa);
      Printf(temp,"%s\002",aname);
      Append(tempa,"\003\002");
      Replace(ns,temp,tempa, DOH_REPLACE_ID_BEGIN);
      }

      /* Non-standard macro expansion.   The value `x` is replaced by a quoted
       version of the argument except that if the argument is already quoted
       nothing happens */

      if (strstr(Char(ns),"`")) {
      String *rep;
      char *c;
      Clear(temp);
      Printf(temp,"`%s`",aname);
      c = Char(arg);
      if (*c == '\"') {
        rep = arg;
      } else {
        Clear(tempa);
        Printf(tempa,"\"%s\"",arg);
        rep = tempa;
      }
      Replace(ns,temp,rep, DOH_REPLACE_ANY);
      }
      if (isvarargs && i == l-1 && Len(arg) == 0) {
      /* Zero length varargs macro argument.   We search for commas that might appear before and nuke them */
      char *a, *s, *t, *name;
        int namelen;
      s = Char(ns);
        name = Char(aname);
        namelen = Len(aname);
      a = strstr(s,name);
      while (a) {
          if (!isidchar(a[namelen+1])) {
          /* Matched the entire vararg name, not just a prefix */
          t = a-1;
          if (*t == '\002') {
            t--;
            while (t >= s) {
            if (isspace((int) *t)) t--;
            else if (*t == ',') {
              *t = ' ';
            } else break;
            }
          }
        }
        a = strstr(a+namelen,name);
      }
      }
      /*      Replace(ns, aname, arg, DOH_REPLACE_ID); */
      Replace(ns, aname, reparg, DOH_REPLACE_ID);   /* Replace expanded args */
      Replace(ns, "\003", arg, DOH_REPLACE_ANY);    /* Replace unexpanded arg */
      Delete(reparg);
    }
  }
  Replace(ns,"\002","",DOH_REPLACE_ANY);    /* Get rid of concatenation tokens */
  Replace(ns,"\001","#",DOH_REPLACE_ANY);   /* Put # back (non-standard C) */

  /* Expand this macro even further */
  Setattr(macro,"*expanded*","1"); 

  e = Preprocessor_replace(ns);

  Delattr(macro,"*expanded*");
  Delete(ns);

  if (Getattr(macro,"swigmacro")) {
    String *g;
    String *f = NewString("");
    Seek(e,0,SEEK_SET);
    copy_location(macro,e);
    g = Preprocessor_parse(e);

    /* Drop the macro in place, but with a marker around it */
    Printf(f,"/*@%s,%d,%s@*/%s/*@@*/", Getfile(macro), Getline(macro), name, g);

    /*    Printf(f," }\n"); */
    Delete(g);
    Delete(e);
    e = f;
  }
  Delete(temp);
  Delete(tempa);
  return e;
}

/* -----------------------------------------------------------------------------
 * evaluate_args()
 *
 * Evaluate the arguments of a macro 
 * ----------------------------------------------------------------------------- */

List *evaluate_args(List *x) {
  Iterator i;
  String *Preprocessor_replace(String *);
  List *nl = NewList();

  for (i = First(x); i.item; i = Next(i)) {
    Append(nl,Preprocessor_replace(i.item));
  }
  return nl;
}

/* -----------------------------------------------------------------------------
 * DOH *Preprocessor_replace(DOH *s)
 *
 * Performs a macro substitution on a string s.  Returns a new string with
 * substitutions applied.   This function works by walking down s and looking
 * for identifiers.   When found, a check is made to see if they are macros
 * which are then expanded.
 * ----------------------------------------------------------------------------- */

DOH *
Preprocessor_replace(DOH *s)
{
  DOH   *ns, *id, *symbols, *m;
  int   c, i, state = 0;

  assert(cpp);
  symbols = Getattr(cpp,"symbols");

  ns = NewString("");
  copy_location(s,ns);
  Seek(s,0,SEEK_SET);
  id = NewString("");

  /* Try to locate identifiers in s and replace them with macro replacements */
  while ((c = Getc(s)) != EOF) {
    switch (state) {
    case 0:
      if (isidentifier(c) || (c == '%')) {
      Clear(id);
      copy_location(s,id);
      Putc(c,id);
      state = 1;
      } else if (c == '\"') {
      Putc(c,ns);
      skip_tochar(s,'\"',ns);
      } else if (c == '\'') {
      Putc(c,ns);
      skip_tochar(s,'\'',ns);
      } else if (c == '/') {
      Putc(c,ns);
      state = 10;
      } else {
      Putc(c,ns);
      }
      break;
    case 1:  /* An identifier */
      if (isidchar(c)) {
      Putc(c,id);
      state = 1;
      } else {
      /* We found the end of a valid identifier */
      Ungetc(c,s);
      /* See if this is the special "defined" macro */
      if (Cmp(id,"defined") == 0) {
        DOH *args;
        /* See whether or not a paranthesis has been used */
        skip_whitespace(s,0);
        c = Getc(s);
        if (c == '(') {
          Seek(s,-1,SEEK_CUR);
          args = find_args(s);
        } else {
          DOH *arg = 0;
          args = NewList();
          arg = NewString("");
          if (isidchar(c)) Putc(c,arg);
          while ((c = Getc(s)) != EOF) {
            if (!isidchar(c)) {
            Seek(s,-1,SEEK_CUR);
            break;
            }
            Putc(c,arg);
          }
          Append(args,arg);
          Delete(arg);
        }
        if ((!args) || (!Len(args))) {
          Swig_error(Getfile(id),Getline(id),"No arguments given to defined()\n");
          state = 0;
          break;
        }
        for (i = 0; i < Len(args); i++) {
          DOH *o = Getitem(args,i);
          if (!Getattr(symbols,o)) {
            break;
          }
        }
        if (i < Len(args)) Putc('0',ns);
        else Putc('1',ns);
        Delete(args);
        state = 0;
        break;
      }
      if (Cmp(id,"__LINE__") == 0) {
        Printf(ns,"%d",Getline(s));
        state = 0;
        break;
      }
      if (Cmp(id,"__FILE__") == 0) {
        String *fn = Copy(Getfile(s));
        Replaceall(fn,"\\","\\\\");
        Printf(ns,"\"%s\"",fn);
        Delete(fn);
        state = 0;
        break;
      }
      /* See if the macro is defined in the preprocessor symbol table */
      if ((m = Getattr(symbols,id))) {
        DOH *args = 0;
        DOH *e;
        /* See if the macro expects arguments */
        if (Getattr(m,"args")) {
          /* Yep.  We need to go find the arguments and do a substitution */
          args = find_args(s);
          if (!Len(args)) {
            Delete(args);
            args = 0;
          }
        } else {
          args = 0;
        }
        e = expand_macro(id,args);
        if (e) {
          Printf(ns,"%s",e);
        }
        Delete(e);
        Delete(args);
      } else {
        Printf(ns,"%s",id);
      }
      state = 0;
      }
      break;
    case 10:
      if (c == '/') state = 11;
      else if (c == '*') state = 12;
      else {
      Ungetc(c,s);
      state = 0;
      break;
      }
      Putc(c,ns);
      break;
    case 11:
      Putc(c,ns);
      if (c == '\n') state = 0;
      break;
    case 12:
      Putc(c,ns);
      if (c == '*') state = 13;
      break;
    case 13:
      Putc(c,ns);
      if (c == '/') state = 0;
      else if (c != '*') state = 12;
      break;
    default:
      state = 0;
      break;
    }
  }

  /* Identifier at the end */
  if (state == 1) {
    /* See if this is the special "defined" macro */
    if (Cmp(id,"defined") == 0) {
      Swig_error(Getfile(id),Getline(id),"No arguments given to defined()\n");
    } else if ((m = Getattr(symbols,id))) {
      DOH *e;
      /* Yes.  There is a macro here */
      /* See if the macro expects arguments */
      /*    if (Getattr(m,"args")) {
        Swig_error(Getfile(id),Getline(id),"Macro arguments expected.\n");
        } */
      e = expand_macro(id,0);
      Printf(ns,"%s",e);
      Delete(e);
    } else {
      Printf(ns,"%s",id);
    }
  }
  Delete(id);
  return ns;
}


/* -----------------------------------------------------------------------------
 * int check_id(DOH *s)
 *
 * Checks the string s to see if it contains any unresolved identifiers.  This
 * function contains the heuristic that determines whether or not a macro
 * definition passes through the preprocessor as a constant declaration.
 * ----------------------------------------------------------------------------- */
static int
check_id(DOH *s)
{
  static SwigScanner *scan = 0;
  int c;
  int hastok = 0;

  Seek(s,0,SEEK_SET);

  if (!scan) {
    scan = NewSwigScanner();
  }

  SwigScanner_clear(scan);
  s = Copy(s);
  Seek(s,SEEK_SET,0);
  SwigScanner_push(scan,s);
  while ((c = SwigScanner_token(scan))) {
    hastok = 1;
    if ((c == SWIG_TOKEN_ID) || (c == SWIG_TOKEN_LBRACE) || (c == SWIG_TOKEN_RBRACE)) return 1;
  }
  if (!hastok) return 1;
  return 0;
}

/* addline().  Utility function for adding lines to a chunk */
static void
addline(DOH *s1, DOH *s2, int allow)
{
  if (allow) {
    Append(s1,s2);
  } else {
    char *c = Char(s2);
    while (*c) {
      if (*c == '\n') Putc('\n',s1);
      c++;
    }
  }
}

static void add_chunk(DOH *ns, DOH *chunk, int allow) {
  DOH *echunk;
  Seek(chunk,0,SEEK_SET);
  if (allow) {
    echunk = Preprocessor_replace(chunk);
    addline(ns,echunk,allow);
    Delete(echunk);
  } else {
    addline(ns,chunk,0);
  }
  Clear(chunk);
}

/* -----------------------------------------------------------------------------
 * Preprocessor_parse()
 *
 * Parses the string s.  Returns a new string containing the preprocessed version.
 *
 * Parsing rules :
 *       1.  Lines starting with # are C preprocessor directives
 *       2.  Macro expansion inside strings is not allowed
 *       3.  All code inside false conditionals is changed to blank lines
 *       4.  Code in %{, %} is not parsed because it may need to be
 *           included inline (with all preprocessor directives included).
 * ----------------------------------------------------------------------------- */

String *
Preprocessor_parse(String *s)
{
  String  *ns;             /* New string containing the preprocessed text */
  String  *chunk, *sval, *decl;
  Hash    *symbols;
  String  *id = 0, *value = 0, *comment = 0;
  int    i, state, val, e, c;
  int    start_line = 0;
  int    allow = 1;
  int    level = 0;
  int    mask = 0;
  int    start_level = 0;
  int    cpp_lines = 0;
  int    cond_lines[256];

  /* Blow away all carriage returns */
  Replace(s,"\015","",DOH_REPLACE_ANY); 

  ns = NewString("");        /* Return result */

  decl = NewString("");
  id = NewString("");
  value = NewString("");
  comment = NewString("");
  chunk = NewString("");
  copy_location(s,chunk);
  copy_location(s,ns);
  symbols = Getattr(cpp,"symbols");

  state = 0;
  while ((c = Getc(s)) != EOF) {
    switch(state) {
    case 0:        /* Initial state - in first column */
      /* Look for C preprocessor directives.   Otherwise, go directly to state 1 */
      if (c == '#') {
      add_chunk(ns,chunk,allow);
      copy_location(s,chunk);
      cpp_lines = 1;
      state = 40;
      } else if (isspace(c)) {
      Putc(c,chunk);
      skip_whitespace(s,chunk);
      } else {
      state = 1;
      Ungetc(c,s);
      }
      break;
    case 1:       /* Non-preprocessor directive */
      /* Look for SWIG directives */
      if (c == '%') {
      state = 100;
      break;
      }
      Putc(c,chunk);
      if (c == '\n') state = 0;
      else if (c == '\"') {
      start_line = Getline(s);
      if (skip_tochar(s,'\"',chunk) < 0) {
        Swig_error(Getfile(s),-1,"Unterminated string constant starting at line %d\n",start_line);
      }
      } else if (c == '\'') {
      start_line = Getline(s);
      if (skip_tochar(s,'\'',chunk) < 0) {
        Swig_error(Getfile(s),-1,"Unterminated character constant starting at line %d\n",start_line);
      }
      }
      else if (c == '/') state = 30;  /* Comment */
      break;

    case 30:      /* Possibly a comment string of some sort */
      start_line = Getline(s);
      Putc(c,chunk);
      if (c == '/') state = 31;
      else if (c == '*') state = 32;
      else state = 1;
      break;
    case 31:
      Putc(c,chunk);
      if (c == '\n') state = 0;
      break;
    case 32:
      Putc(c,chunk);
      if (c == '*') state = 33;
      break;
    case 33:
      Putc(c,chunk);
      if (c == '/') state = 1;
      else if (c != '*') state = 32;
      break;

    case 40:   /* Start of a C preprocessor directive */
      if (c == '\n') {
      Putc('\n',chunk);
      state = 0;
      } else if (isspace(c)) {
      state = 40;
      } else {
      /* Got the start of a preprocessor directive */
      Ungetc(c,s);
      Clear(id);
      copy_location(s,id);
      state = 41;
      }
      break;

    case 41:  /* Build up the name of the preprocessor directive */
      if ((isspace(c) || (!isalpha(c)))) {
      Clear(value);
      Clear(comment);
      if (c == '\n') {
        Ungetc(c,s);
        state = 50;
      } else {
        state = 42;
        if (!isspace(c)) {
          Ungetc(c,s);
        }
      }

      copy_location(s,value);
      break;
      }
      Putc(c,id);
      break;

    case 42: /* Strip any leading space before preprocessor value */
      if (isspace(c)) {
      if (c == '\n') {
        Ungetc(c,s);
        state = 50;
      }
      break;
      }
      state = 43;
      /* no break intended here */

    case 43:
      /* Get preprocessor value */
      if (c == '\n') {
      Ungetc(c,s);
      state = 50;
      } else if (c == '/') {
      state = 45;
      } else if (c == '\"') {
      Putc(c,value);
      skip_tochar(s,'\"',value);
      } else if (c == '\'') {
      Putc(c,value);
      skip_tochar(s,'\'',value);
      } else {
      Putc(c,value);
      if (c == '\\') state = 44;
      }
      break;

    case 44:
      if (c == '\n') {
      Putc(c,value);
      cpp_lines++;
      } else {
      Ungetc(c,s);
      }
      state = 43;
      break;

      /* States 45-48 are used to remove, but retain comments from macro values.  The comments
         will be placed in the output in an alternative form */

    case 45:
      if (c == '/') state = 46;
      else if (c == '*') state = 47;
      else if (c == '\n') {
      Putc('/',value);
      Ungetc(c,s);
      cpp_lines++;
      state = 50;
      } else {
      Putc('/',value);
      Putc(c,value);
      state = 43;
      }
      break;
    case 46:
      if (c == '\n') {
      Ungetc(c,s);
      cpp_lines++;
      state = 50;
      } else Putc(c,comment);
      break;
    case 47:
      if (c == '*') state = 48;
      else Putc(c,comment);
      break;
    case 48:
      if (c == '/') state = 43;
      else if (c == '*') Putc(c,comment);
      else {
      Putc('*',comment);
      Putc(c,comment);
      state = 47;
      }
      break;
    case 50:
      /* Check for various preprocessor directives */
      Chop(value);
      if (Cmp(id,"define") == 0) {
      if (allow) {
        DOH *m, *v, *v1;
        Seek(value,0,SEEK_SET);
        m = Preprocessor_define(value,0);
        if ((m) && !(Getattr(m,"args"))) {
          v = Copy(Getattr(m,"value"));
          if (Len(v)) {
            Swig_error_silent(1);
            v1 = Preprocessor_replace(v);
            Swig_error_silent(0);
            /*          Printf(stdout,"checking '%s'\n", v1); */
            if (!check_id(v1)) {
            if (Len(comment) == 0)
              Printf(ns,"%%constant %s = %s;\n", Getattr(m,"name"), v1);
            else
              Printf(ns,"%%constant %s = %s; /*%s*/\n", Getattr(m,"name"),v1,comment);
            cpp_lines--;
            }
            Delete(v1);
          }
          Delete(v);
        }
        Delete(m);
      }
      } else if (Cmp(id,"undef") == 0) {
      if (allow) Preprocessor_undef(value);
      } else if (Cmp(id,"ifdef") == 0) {
      cond_lines[level] = Getline(id);
      level++;
      if (allow) {
        start_level = level;
        /* See if the identifier is in the hash table */
        if (!Getattr(symbols,value)) allow = 0;
        mask = 1;
      }
      } else if (Cmp(id,"ifndef") == 0) {
      cond_lines[level] = Getline(id);
      level++;
      if (allow) {
        start_level = level;
        /* See if the identifier is in the hash table */
        if (Getattr(symbols,value)) allow = 0;
        mask = 1;
      }
      } else if (Cmp(id,"else") == 0) {
      if (level <= 0) {
        Swig_error(Getfile(s),Getline(id),"Misplaced #else.\n");
      } else {
        cond_lines[level-1] = Getline(id);
        if (allow) {
          allow = 0;
          mask = 0;
        } else if (level == start_level) {
          allow = 1*mask;
        }
      }
      } else if (Cmp(id,"endif") == 0) {
      level--;
      if (level < 0) {
        Swig_error(Getfile(id),Getline(id),"Extraneous #endif.\n");
        level = 0;
      } else {
        if (level < start_level) {
          allow = 1;
          start_level--;
        }
      }
      } else if (Cmp(id,"if") == 0) {
      cond_lines[level] = Getline(id);
      level++;
      if (allow) {
        start_level = level;
        sval = Preprocessor_replace(value);
        Seek(sval,0,SEEK_SET);
        /*    Printf(stdout,"Evaluating '%s'\n", sval); */
        val = Preprocessor_expr(sval,&e);
        if (e) {
          Seek(value,0,SEEK_SET);
          Swig_warning(WARN_PP_EVALUATION,Getfile(value),Getline(value),"Could not evaluate '%s'\n", value);
          allow = 0;
        } else {
          if (val == 0)
            allow = 0;
        }
        mask = 1;
      }
      } else if (Cmp(id,"elif") == 0) {
      if (level == 0) {
        Swig_error(Getfile(s),Getline(id),"Misplaced #elif.\n");
      } else {
        cond_lines[level-1] = Getline(id);
        if (allow) {
          allow = 0;
          mask = 0;
        } else if (level == start_level) {
          sval = Preprocessor_replace(value);
          Seek(sval,0,SEEK_SET);
          val = Preprocessor_expr(sval,&e);
          if (e) {
            Seek(value,0,SEEK_SET);
            Swig_warning(WARN_PP_EVALUATION,Getfile(value),Getline(value),"Could not evaluate '%s'\n", value);
            allow = 0;
          } else {
            if (val)
            allow = 1*mask;
            else
            allow = 0;
          }
        }
      }
      } else if (Cmp(id,"line") == 0) {
      } else if (Cmp(id,"include") == 0) {
      if (((include_all) || (import_all)) && (allow)) {
        String *s1, *s2, *fn;
        char *dirname;
        Seek(value,0,SEEK_SET);
        fn = get_filename(value);
        s1 = cpp_include(fn);
        if (s1) {
          if (include_all) 
            Printf(ns,"%%includefile \"%s\" [\n", Swig_last_file());
          else if (import_all) 
            Printf(ns,"%%importfile \"%s\" [\n", Swig_last_file());

          /* See if the filename has a directory component */
          dirname = Swig_file_dirname(Swig_last_file());
          if (!strlen(dirname)) dirname = 0;
          if (dirname) {
            dirname[strlen(dirname)-1] = 0;   /* Kill trailing directory delimeter */
            Swig_push_directory(dirname);
          }
          s2 = Preprocessor_parse(s1);
          addline(ns,s2,allow);
          Printf(ns,"\n]");
          if (dirname) {
            Swig_pop_directory();
          }
          Delete(s2);
        }
        Delete(s1);
        Delete(fn);
      }
      } else if (Cmp(id,"pragma") == 0) {
      if (Strncmp(value,"SWIG ",5) == 0) {
        char *c = Char(value)+5;
        while (*c && (isspace((int)*c))) c++;
        if (*c) {
          if (Strncmp(c,"nowarn=",7) == 0) {
            Swig_warnfilter(c+7,1);
          }
        }
      }
      } else if (Cmp(id,"level") == 0) {
      Swig_error(Getfile(s),Getline(id),"cpp debug: level = %d, startlevel = %d\n", level, start_level);
      }
      for (i = 0; i < cpp_lines; i++)
      Putc('\n',ns);
      state = 0;
      break;

        /* Swig directives  */
    case 100:
      /* %{,%} block  */
      if (c == '{') {
      start_line = Getline(s);
      add_chunk(ns,chunk,allow);
      copy_location(s,chunk);
      Putc('%',chunk);
      Putc(c,chunk);
      state = 105;
      }
      /* %#cpp -  an embedded C preprocessor directive (we strip off the %)  */
      else if (c == '#') {
      add_chunk(ns,chunk,allow);
      Putc(c,chunk);
      state = 107;
      } else if (isidentifier(c)) {
      Clear(decl);
      Putc('%',decl);
      Putc(c,decl);
      state = 110;
      } else {
      Putc('%',chunk);
      Putc(c,chunk);
      state = 1;
      }
      break;

    case 105:
      Putc(c,chunk);
      if (c == '%')
      state = 106;
      break;

    case 106:
      Putc(c,chunk);
      if (c == '}') {
      state = 1;
      addline(ns,chunk,allow);
      Clear(chunk);
      copy_location(s,chunk);
      } else {
      state = 105;
      }
      break;
      
    case 107:
      Putc(c,chunk);
      if (c == '\n') {
      addline(ns,chunk,allow);
      Clear(chunk);
      state = 0;
      } else if (c == '\\') {
      state = 108;
      }
      break;

    case 108:
      Putc(c,chunk);
      state = 107;
      break;

    case 110:
      if (!isidchar(c)) {
      Ungetc(c,s);
      /* Look for common Swig directives  */
      if ((Cmp(decl,"%include") == 0) || (Cmp(decl,"%import") == 0) || (Cmp(decl,"%extern") == 0)) {
        /* Got some kind of file inclusion directive  */
        if (allow) {
          DOH *s1, *s2, *fn, *opt;

          if (Cmp(decl,"%extern") == 0) {
            Swig_warning(WARN_DEPRECATED_EXTERN, Getfile(s),Getline(s),"%%extern is deprecated. Use %%import instead.\n");
            Clear(decl);
            Printf(decl,"%%import");
          }
          opt = get_options(s);
          fn = get_filename(s);
          s1 = cpp_include(fn);
          if (s1) {
            char *dirname;
            add_chunk(ns,chunk,allow);
            copy_location(s,chunk);
            Printf(ns,"%sfile%s \"%s\" [\n", decl, opt, Swig_last_file());
            if ((Cmp(decl,"%import") == 0) || (Cmp(decl,"%extern") == 0)) {
            Preprocessor_define("WRAPEXTERN 1", 0);
            Preprocessor_define("SWIGIMPORT 1", 0);
            }
            dirname = Swig_file_dirname(Swig_last_file());
            if (!strlen(dirname)) dirname = 0;
            if (dirname) {
            dirname[strlen(dirname)-1] = 0;   /* Kill trailing directory delimeter */
            Swig_push_directory(dirname);
            }
            s2 = Preprocessor_parse(s1);
            if (dirname) {
            Swig_pop_directory();
            }
            if ((Cmp(decl,"%import") == 0) || (Cmp(decl,"%extern") == 0)) {
            Preprocessor_undef("SWIGIMPORT");
            Preprocessor_undef("WRAPEXTERN");
            }
            addline(ns,s2,allow);
            Printf(ns,"\n]");
            Delete(s2);
            Delete(s1);
          }
          Delete(fn);
        }
        state = 1;
      } else if (Cmp(decl,"%line") == 0) {
        /* Got a line directive  */
        state = 1;
      } else if (Cmp(decl,"%define") == 0) {
        /* Got a define directive  */
        add_chunk(ns,chunk,allow);
        copy_location(s,chunk);
        Clear(value);
        copy_location(s,value);
        state = 150;
      } else {
        Printf(chunk,"%s", decl);
        state = 1;
      }
      } else {
      Putc(c,decl);
      }
      break;

        /* Searching for the end of a %define statement  */
    case 150:
      Putc(c,value);
      if (c == '%') {
      int i = 0;
      char *d = "enddef";
      for (i = 0; i < 6; i++) {
        c = Getc(s);
        Putc(c,value);
        if (c != d[i]) break;
      }
      c = Getc(s);
      Ungetc(c,s);
      if ((i == 6) && (isspace(c))) {
        /* Got the macro  */
        for (i = 0; i < 7; i++) {
          Delitem(value,DOH_END);
        }
        if (allow) {
          Seek(value,0,SEEK_SET);
          Preprocessor_define(value,1);
        }
        Putc('\n',ns);
        addline(ns,value,0);
        state = 0;
      }
      }
      break;
    default :
      Printf(stderr,"cpp: Invalid parser state %d\n", state);
      abort();
      break;
    }
  }
  while (level > 0) {
    Swig_error(Getfile(s),-1,"Missing #endif for conditional starting on line %d\n", cond_lines[level-1]);
    level--;
  }
  if (state == 150) {
    Seek(value,0,SEEK_SET);
    Swig_error(Getfile(s),-1,"Missing %%enddef for macro starting on line %d\n",Getline(value));
  }
  if ((state >= 105) && (state < 107)) {
    Swig_error(Getfile(s),-1,"Unterminated %%{ ... %%} block starting on line %d\n", start_line);
  }
  if ((state >= 30) && (state < 40)) {
    Swig_error(Getfile(s),-1,"Unterminated comment starting on line %d\n", start_line);
  }
  add_chunk(ns,chunk,allow);
  copy_location(s,chunk);

  /*  DelScope(scp); */
  Delete(decl);
  Delete(id);
  Delete(value);
  Delete(comment);
  Delete(chunk);

  /*  fprintf(stderr,"cpp: %d\n", Len(Getattr(cpp,"symbols"))); */
  return ns;
}





Generated by  Doxygen 1.6.0   Back to index