| /* The library used by gdb. | 
 |    Copyright (C) 2014-2020 Free Software Foundation, Inc. | 
 |  | 
 | This file is part of GCC. | 
 |  | 
 | GCC is free software; you can redistribute it and/or modify it under | 
 | the terms of the GNU General Public License as published by the Free | 
 | Software Foundation; either version 3, or (at your option) any later | 
 | version. | 
 |  | 
 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | 
 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
 | FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License | 
 | for more details. | 
 |  | 
 | You should have received a copy of the GNU General Public License | 
 | along with GCC; see the file COPYING3.  If not see | 
 | <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | #include <cc1plugin-config.h> | 
 | #include <vector> | 
 | #include <string> | 
 | #include <sys/socket.h> | 
 | #include <sys/types.h> | 
 | #include <unistd.h> | 
 | #include <sys/wait.h> | 
 | #include <stdio.h> | 
 | #include <errno.h> | 
 | #include <sys/stat.h> | 
 | #include <stdlib.h> | 
 | #include <sstream> | 
 | #include "marshall-c.hh" | 
 | #include "rpc.hh" | 
 | #include "connection.hh" | 
 | #include "names.hh" | 
 | #include "callbacks.hh" | 
 | #include "libiberty.h" | 
 | #include "xregex.h" | 
 | #include "findcomp.hh" | 
 | #include "compiler-name.hh" | 
 | #include "intl.h" | 
 |  | 
 | struct libcc1; | 
 |  | 
 | class libcc1_connection; | 
 |  | 
 | // The C compiler context that we hand back to our caller. | 
 | struct libcc1 : public gcc_c_context | 
 | { | 
 |   libcc1 (const gcc_base_vtable *, const gcc_c_fe_vtable *); | 
 |   ~libcc1 (); | 
 |  | 
 |   // A convenience function to print something. | 
 |   void print (const char *str) | 
 |   { | 
 |     this->print_function (this->print_datum, str); | 
 |   } | 
 |  | 
 |   libcc1_connection *connection; | 
 |  | 
 |   gcc_c_oracle_function *binding_oracle; | 
 |   gcc_c_symbol_address_function *address_oracle; | 
 |   void *oracle_datum; | 
 |  | 
 |   void (*print_function) (void *datum, const char *message); | 
 |   void *print_datum; | 
 |  | 
 |   std::vector<std::string> args; | 
 |   std::string source_file; | 
 |  | 
 |   /* Non-zero as an equivalent to gcc driver option "-v".  */ | 
 |   bool verbose; | 
 |  | 
 |   /* Compiler to set by set_triplet_regexp or set_driver_filename.  */ | 
 |   class compiler | 
 |   { | 
 |   protected: | 
 |     libcc1 *self_; | 
 |   public: | 
 |     compiler (libcc1 *self) : self_ (self) | 
 |     { | 
 |     } | 
 |     virtual char *find (std::string &compiler) const; | 
 |     virtual ~compiler () | 
 |     { | 
 |     } | 
 |   } *compilerp; | 
 |  | 
 |   /* Compiler to set by set_triplet_regexp.  */ | 
 |   class compiler_triplet_regexp : public compiler | 
 |   { | 
 |   private: | 
 |     std::string triplet_regexp_; | 
 |   public: | 
 |     virtual char *find (std::string &compiler) const; | 
 |     compiler_triplet_regexp (libcc1 *self, std::string triplet_regexp) | 
 |       : compiler (self), triplet_regexp_ (triplet_regexp) | 
 |     { | 
 |     } | 
 |     virtual ~compiler_triplet_regexp () | 
 |     { | 
 |     } | 
 |   }; | 
 |  | 
 |   /* Compiler to set by set_driver_filename.  */ | 
 |   class compiler_driver_filename : public compiler | 
 |   { | 
 |   private: | 
 |     std::string driver_filename_; | 
 |   public: | 
 |     virtual char *find (std::string &compiler) const; | 
 |     compiler_driver_filename (libcc1 *self, std::string driver_filename) | 
 |       : compiler (self), driver_filename_ (driver_filename) | 
 |     { | 
 |     } | 
 |     virtual ~compiler_driver_filename () | 
 |     { | 
 |     } | 
 |   }; | 
 | }; | 
 |  | 
 | // A local subclass of connection that holds a back-pointer to the | 
 | // gcc_c_context object that we provide to our caller. | 
 | class libcc1_connection : public cc1_plugin::connection | 
 | { | 
 | public: | 
 |  | 
 |   libcc1_connection (int fd, int aux_fd, libcc1 *b) | 
 |     : connection (fd, aux_fd), | 
 |       back_ptr (b) | 
 |   { | 
 |   } | 
 |  | 
 |   virtual void print (const char *buf) | 
 |   { | 
 |     back_ptr->print (buf); | 
 |   } | 
 |  | 
 |   libcc1 *back_ptr; | 
 | }; | 
 |  | 
 | libcc1::libcc1 (const gcc_base_vtable *v, | 
 | 		const gcc_c_fe_vtable *cv) | 
 |   : connection (NULL), | 
 |     binding_oracle (NULL), | 
 |     address_oracle (NULL), | 
 |     oracle_datum (NULL), | 
 |     print_function (NULL), | 
 |     print_datum (NULL), | 
 |     args (), | 
 |     source_file (), | 
 |     verbose (false), | 
 |     compilerp (new libcc1::compiler (this)) | 
 | { | 
 |   base.ops = v; | 
 |   c_ops = cv; | 
 | } | 
 |  | 
 | libcc1::~libcc1 () | 
 | { | 
 |   delete connection; | 
 |   delete compilerp; | 
 | } | 
 |  | 
 |  | 
 |  | 
 | // Enclose these functions in an anonymous namespace because they | 
 | // shouldn't be exported, but they can't be static because they're | 
 | // used as template arguments. | 
 | namespace { | 
 |   // This is a wrapper function that is called by the RPC system and | 
 |   // that then forwards the call to the library user.  Note that the | 
 |   // return value is not used; the type cannot be 'void' due to | 
 |   // limitations in our simple RPC. | 
 |   int | 
 |   c_call_binding_oracle (cc1_plugin::connection *conn, | 
 | 			 enum gcc_c_oracle_request request, | 
 | 			 const char *identifier) | 
 |   { | 
 |     libcc1 *self = ((libcc1_connection *) conn)->back_ptr; | 
 |  | 
 |     self->binding_oracle (self->oracle_datum, self, request, identifier); | 
 |     return 1; | 
 |   } | 
 |  | 
 |   // This is a wrapper function that is called by the RPC system and | 
 |   // that then forwards the call to the library user. | 
 |   gcc_address | 
 |   c_call_symbol_address (cc1_plugin::connection *conn, const char *identifier) | 
 |   { | 
 |     libcc1 *self = ((libcc1_connection *) conn)->back_ptr; | 
 |  | 
 |     return self->address_oracle (self->oracle_datum, self, identifier); | 
 |   } | 
 | } /* anonymous namespace */ | 
 |  | 
 |  | 
 |  | 
 | static void | 
 | set_callbacks (struct gcc_c_context *s, | 
 | 	       gcc_c_oracle_function *binding_oracle, | 
 | 	       gcc_c_symbol_address_function *address_oracle, | 
 | 	       void *datum) | 
 | { | 
 |   libcc1 *self = (libcc1 *) s; | 
 |  | 
 |   self->binding_oracle = binding_oracle; | 
 |   self->address_oracle = address_oracle; | 
 |   self->oracle_datum = datum; | 
 | } | 
 |  | 
 | // Instances of these rpc<> template functions are installed into the | 
 | // "c_vtable".  These functions are parameterized by type and method | 
 | // name and forward the call via the connection. | 
 |  | 
 | template<typename R, const char *&NAME> | 
 | R rpc (struct gcc_c_context *s) | 
 | { | 
 |   libcc1 *self = (libcc1 *) s; | 
 |   R result; | 
 |  | 
 |   if (!cc1_plugin::call (self->connection, NAME, &result)) | 
 |     return 0; | 
 |   return result; | 
 | } | 
 |  | 
 | template<typename R, const char *&NAME, typename A> | 
 | R rpc (struct gcc_c_context *s, A arg) | 
 | { | 
 |   libcc1 *self = (libcc1 *) s; | 
 |   R result; | 
 |  | 
 |   if (!cc1_plugin::call (self->connection, NAME, &result, arg)) | 
 |     return 0; | 
 |   return result; | 
 | } | 
 |  | 
 | template<typename R, const char *&NAME, typename A1, typename A2> | 
 | R rpc (struct gcc_c_context *s, A1 arg1, A2 arg2) | 
 | { | 
 |   libcc1 *self = (libcc1 *) s; | 
 |   R result; | 
 |  | 
 |   if (!cc1_plugin::call (self->connection, NAME, &result, arg1, arg2)) | 
 |     return 0; | 
 |   return result; | 
 | } | 
 |  | 
 | template<typename R, const char *&NAME, typename A1, typename A2, typename A3> | 
 | R rpc (struct gcc_c_context *s, A1 arg1, A2 arg2, A3 arg3) | 
 | { | 
 |   libcc1 *self = (libcc1 *) s; | 
 |   R result; | 
 |  | 
 |   if (!cc1_plugin::call (self->connection, NAME, &result, arg1, arg2, arg3)) | 
 |     return 0; | 
 |   return result; | 
 | } | 
 |  | 
 | template<typename R, const char *&NAME, typename A1, typename A2, typename A3, | 
 | 	 typename A4> | 
 | R rpc (struct gcc_c_context *s, A1 arg1, A2 arg2, A3 arg3, A4 arg4) | 
 | { | 
 |   libcc1 *self = (libcc1 *) s; | 
 |   R result; | 
 |  | 
 |   if (!cc1_plugin::call (self->connection, NAME, &result, arg1, arg2, arg3, | 
 | 			 arg4)) | 
 |     return 0; | 
 |   return result; | 
 | } | 
 |  | 
 | template<typename R, const char *&NAME, typename A1, typename A2, typename A3, | 
 | 	 typename A4, typename A5> | 
 | R rpc (struct gcc_c_context *s, A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5) | 
 | { | 
 |   libcc1 *self = (libcc1 *) s; | 
 |   R result; | 
 |  | 
 |   if (!cc1_plugin::call (self->connection, NAME, &result, arg1, arg2, arg3, | 
 | 			 arg4, arg5)) | 
 |     return 0; | 
 |   return result; | 
 | } | 
 |  | 
 | template<typename R, const char *&NAME, typename A1, typename A2, typename A3, | 
 | 	 typename A4, typename A5, typename A6, typename A7> | 
 | R rpc (struct gcc_c_context *s, A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5, | 
 |        A6 arg6, A7 arg7) | 
 | { | 
 |   libcc1 *self = (libcc1 *) s; | 
 |   R result; | 
 |  | 
 |   if (!cc1_plugin::call (self->connection, NAME, &result, arg1, arg2, arg3, | 
 | 			 arg4, arg5, arg6, arg7)) | 
 |     return 0; | 
 |   return result; | 
 | } | 
 |  | 
 | static const struct gcc_c_fe_vtable c_vtable = | 
 | { | 
 |   GCC_C_FE_VERSION_0, | 
 |   set_callbacks, | 
 |  | 
 | #define GCC_METHOD0(R, N) \ | 
 |   rpc<R, cc1_plugin::c::N>, | 
 | #define GCC_METHOD1(R, N, A) \ | 
 |   rpc<R, cc1_plugin::c::N, A>, | 
 | #define GCC_METHOD2(R, N, A, B) \ | 
 |   rpc<R, cc1_plugin::c::N, A, B>, | 
 | #define GCC_METHOD3(R, N, A, B, C) \ | 
 |   rpc<R, cc1_plugin::c::N, A, B, C>, | 
 | #define GCC_METHOD4(R, N, A, B, C, D) \ | 
 |   rpc<R, cc1_plugin::c::N, A, B, C, D>, | 
 | #define GCC_METHOD5(R, N, A, B, C, D, E) \ | 
 |   rpc<R, cc1_plugin::c::N, A, B, C, D, E>, | 
 | #define GCC_METHOD7(R, N, A, B, C, D, E, F, G) \ | 
 |   rpc<R, cc1_plugin::c::N, A, B, C, D, E, F, G>, | 
 |  | 
 | #include "gcc-c-fe.def" | 
 |  | 
 | #undef GCC_METHOD0 | 
 | #undef GCC_METHOD1 | 
 | #undef GCC_METHOD2 | 
 | #undef GCC_METHOD3 | 
 | #undef GCC_METHOD4 | 
 | #undef GCC_METHOD5 | 
 | #undef GCC_METHOD7 | 
 | }; | 
 |  | 
 |  | 
 |  | 
 | // Construct an appropriate regexp to match the compiler name. | 
 | static std::string | 
 | make_regexp (const char *triplet_regexp, const char *compiler) | 
 | { | 
 |   std::stringstream buf; | 
 |  | 
 |   buf << "^" << triplet_regexp << "-"; | 
 |  | 
 |   // Quote the compiler name in case it has something funny in it. | 
 |   for (const char *p = compiler; *p; ++p) | 
 |     { | 
 |       switch (*p) | 
 | 	{ | 
 | 	case '.': | 
 | 	case '^': | 
 | 	case '$': | 
 | 	case '*': | 
 | 	case '+': | 
 | 	case '?': | 
 | 	case '(': | 
 | 	case ')': | 
 | 	case '[': | 
 | 	case '{': | 
 | 	case '\\': | 
 | 	case '|': | 
 | 	  buf << '\\'; | 
 | 	  break; | 
 | 	} | 
 |       buf << *p; | 
 |     } | 
 |   buf << "$"; | 
 |  | 
 |   return buf.str (); | 
 | } | 
 |  | 
 | static void | 
 | libcc1_set_verbose (struct gcc_base_context *s, int /* bool */ verbose) | 
 | { | 
 |   libcc1 *self = (libcc1 *) s; | 
 |  | 
 |   self->verbose = verbose != 0; | 
 | } | 
 |  | 
 | char * | 
 | libcc1::compiler::find (std::string &compiler ATTRIBUTE_UNUSED) const | 
 | { | 
 |   return xstrdup (_("Compiler has not been specified")); | 
 | } | 
 |  | 
 | char * | 
 | libcc1::compiler_triplet_regexp::find (std::string &compiler) const | 
 | { | 
 |   std::string rx = make_regexp (triplet_regexp_.c_str (), C_COMPILER_NAME); | 
 |   if (self_->verbose) | 
 |     fprintf (stderr, _("searching for compiler matching regex %s\n"), | 
 | 	     rx.c_str()); | 
 |   regex_t triplet; | 
 |   int code = regcomp (&triplet, rx.c_str (), REG_EXTENDED | REG_NOSUB); | 
 |   if (code != 0) | 
 |     { | 
 |       size_t len = regerror (code, &triplet, NULL, 0); | 
 |       char err[len]; | 
 |  | 
 |       regerror (code, &triplet, err, len); | 
 |  | 
 |       return concat ("Could not compile regexp \"", | 
 | 		     rx.c_str (), | 
 | 		     "\": ", | 
 | 		     err, | 
 | 		     (char *) NULL); | 
 |     } | 
 |  | 
 |   if (!find_compiler (triplet, &compiler)) | 
 |     { | 
 |       regfree (&triplet); | 
 |       return concat ("Could not find a compiler matching \"", | 
 | 		     rx.c_str (), | 
 | 		     "\"", | 
 | 		     (char *) NULL); | 
 |     } | 
 |   regfree (&triplet); | 
 |   if (self_->verbose) | 
 |     fprintf (stderr, _("found compiler %s\n"), compiler.c_str()); | 
 |   return NULL; | 
 | } | 
 |  | 
 | char * | 
 | libcc1::compiler_driver_filename::find (std::string &compiler) const | 
 | { | 
 |   // Simulate fnotice by fprintf. | 
 |   if (self_->verbose) | 
 |     fprintf (stderr, _("using explicit compiler filename %s\n"), | 
 | 	     driver_filename_.c_str()); | 
 |   compiler = driver_filename_; | 
 |   return NULL; | 
 | } | 
 |  | 
 | static char * | 
 | libcc1_set_arguments (struct gcc_base_context *s, | 
 | 		      int argc, char **argv) | 
 | { | 
 |   libcc1 *self = (libcc1 *) s; | 
 |  | 
 |   std::string compiler; | 
 |   char *errmsg = self->compilerp->find (compiler); | 
 |   if (errmsg != NULL) | 
 |     return errmsg; | 
 |  | 
 |   self->args.push_back (compiler); | 
 |  | 
 |   for (int i = 0; i < argc; ++i) | 
 |     self->args.push_back (argv[i]); | 
 |  | 
 |   return NULL; | 
 | } | 
 |  | 
 | static char * | 
 | libcc1_set_triplet_regexp (struct gcc_base_context *s, | 
 | 			   const char *triplet_regexp) | 
 | { | 
 |   libcc1 *self = (libcc1 *) s; | 
 |  | 
 |   delete self->compilerp; | 
 |   self->compilerp = new libcc1::compiler_triplet_regexp (self, triplet_regexp); | 
 |   return NULL; | 
 | } | 
 |  | 
 | static char * | 
 | libcc1_set_driver_filename (struct gcc_base_context *s, | 
 | 			    const char *driver_filename) | 
 | { | 
 |   libcc1 *self = (libcc1 *) s; | 
 |  | 
 |   delete self->compilerp; | 
 |   self->compilerp = new libcc1::compiler_driver_filename (self, | 
 | 							  driver_filename); | 
 |   return NULL; | 
 | } | 
 |  | 
 | static char * | 
 | libcc1_set_arguments_v0 (struct gcc_base_context *s, | 
 | 			 const char *triplet_regexp, | 
 | 			 int argc, char **argv) | 
 | { | 
 |   char *errmsg = libcc1_set_triplet_regexp (s, triplet_regexp); | 
 |   if (errmsg != NULL) | 
 |     return errmsg; | 
 |  | 
 |   return libcc1_set_arguments (s, argc, argv); | 
 | } | 
 |  | 
 | static void | 
 | libcc1_set_source_file (struct gcc_base_context *s, | 
 | 			const char *file) | 
 | { | 
 |   libcc1 *self = (libcc1 *) s; | 
 |  | 
 |   self->source_file = file; | 
 | } | 
 |  | 
 | static void | 
 | libcc1_set_print_callback (struct gcc_base_context *s, | 
 | 			   void (*print_function) (void *datum, | 
 | 						   const char *message), | 
 | 			   void *datum) | 
 | { | 
 |   libcc1 *self = (libcc1 *) s; | 
 |  | 
 |   self->print_function = print_function; | 
 |   self->print_datum = datum; | 
 | } | 
 |  | 
 | static int | 
 | fork_exec (libcc1 *self, char **argv, int spair_fds[2], int stderr_fds[2]) | 
 | { | 
 |   pid_t child_pid = fork (); | 
 |  | 
 |   if (child_pid == -1) | 
 |     { | 
 |       close (spair_fds[0]); | 
 |       close (spair_fds[1]); | 
 |       close (stderr_fds[0]); | 
 |       close (stderr_fds[1]); | 
 |       return 0; | 
 |     } | 
 |  | 
 |   if (child_pid == 0) | 
 |     { | 
 |       // Child. | 
 |       dup2 (stderr_fds[1], 1); | 
 |       dup2 (stderr_fds[1], 2); | 
 |       close (stderr_fds[0]); | 
 |       close (stderr_fds[1]); | 
 |       close (spair_fds[0]); | 
 |  | 
 |       execvp (argv[0], argv); | 
 |       _exit (127); | 
 |     } | 
 |   else | 
 |     { | 
 |       // Parent. | 
 |       close (spair_fds[1]); | 
 |       close (stderr_fds[1]); | 
 |  | 
 |       cc1_plugin::status result = cc1_plugin::FAIL; | 
 |       if (self->connection->send ('H') | 
 | 	  && ::cc1_plugin::marshall (self->connection, GCC_C_FE_VERSION_1)) | 
 | 	result = self->connection->wait_for_query (); | 
 |  | 
 |       close (spair_fds[0]); | 
 |       close (stderr_fds[0]); | 
 |  | 
 |       while (true) | 
 | 	{ | 
 | 	  int status; | 
 |  | 
 | 	  if (waitpid (child_pid, &status, 0) == -1) | 
 | 	    { | 
 | 	      if (errno != EINTR) | 
 | 		return 0; | 
 | 	    } | 
 |  | 
 | 	  if (!WIFEXITED (status) || WEXITSTATUS (status) != 0) | 
 | 	    return 0; | 
 | 	  break; | 
 | 	} | 
 |  | 
 |       if (!result) | 
 | 	return 0; | 
 |       return 1; | 
 |     } | 
 | } | 
 |  | 
 | static int | 
 | libcc1_compile (struct gcc_base_context *s, | 
 | 		const char *filename) | 
 | { | 
 |   libcc1 *self = (libcc1 *) s; | 
 |  | 
 |   int fds[2]; | 
 |   if (socketpair (AF_UNIX, SOCK_STREAM, 0, fds) != 0) | 
 |     { | 
 |       self->print ("could not create socketpair\n"); | 
 |       return 0; | 
 |     } | 
 |  | 
 |   int stderr_fds[2]; | 
 |   if (pipe (stderr_fds) != 0) | 
 |     { | 
 |       self->print ("could not create pipe\n"); | 
 |       close (fds[0]); | 
 |       close (fds[1]); | 
 |       return 0; | 
 |     } | 
 |  | 
 |   self->args.push_back ("-fplugin=libcc1plugin"); | 
 |   char buf[100]; | 
 |   if (snprintf (buf, sizeof (buf), "-fplugin-arg-libcc1plugin-fd=%d", fds[1]) | 
 |       >= (long) sizeof (buf)) | 
 |     abort (); | 
 |   self->args.push_back (buf); | 
 |  | 
 |   self->args.push_back (self->source_file); | 
 |   self->args.push_back ("-c"); | 
 |   self->args.push_back ("-o"); | 
 |   self->args.push_back (filename); | 
 |   if (self->verbose) | 
 |     self->args.push_back ("-v"); | 
 |  | 
 |   self->connection = new libcc1_connection (fds[0], stderr_fds[0], self); | 
 |  | 
 |   cc1_plugin::callback_ftype *fun | 
 |     = cc1_plugin::callback<int, | 
 | 			   enum gcc_c_oracle_request, | 
 | 			   const char *, | 
 | 			   c_call_binding_oracle>; | 
 |   self->connection->add_callback ("binding_oracle", fun); | 
 |  | 
 |   fun = cc1_plugin::callback<gcc_address, | 
 | 			     const char *, | 
 | 			     c_call_symbol_address>; | 
 |   self->connection->add_callback ("address_oracle", fun); | 
 |  | 
 |   char **argv = new (std::nothrow) char *[self->args.size () + 1]; | 
 |   if (argv == NULL) | 
 |     return 0; | 
 |  | 
 |   for (unsigned int i = 0; i < self->args.size (); ++i) | 
 |     argv[i] = const_cast<char *> (self->args[i].c_str ()); | 
 |   argv[self->args.size ()] = NULL; | 
 |  | 
 |   return fork_exec (self, argv, fds, stderr_fds); | 
 | } | 
 |  | 
 | static int | 
 | libcc1_compile_v0 (struct gcc_base_context *s, const char *filename, | 
 | 		   int verbose) | 
 | { | 
 |   libcc1_set_verbose (s, verbose); | 
 |   return libcc1_compile (s, filename); | 
 | } | 
 |  | 
 | static void | 
 | libcc1_destroy (struct gcc_base_context *s) | 
 | { | 
 |   libcc1 *self = (libcc1 *) s; | 
 |  | 
 |   delete self; | 
 | } | 
 |  | 
 | static const struct gcc_base_vtable vtable = | 
 | { | 
 |   GCC_FE_VERSION_1, | 
 |   libcc1_set_arguments_v0, | 
 |   libcc1_set_source_file, | 
 |   libcc1_set_print_callback, | 
 |   libcc1_compile_v0, | 
 |   libcc1_destroy, | 
 |   libcc1_set_verbose, | 
 |   libcc1_compile, | 
 |   libcc1_set_arguments, | 
 |   libcc1_set_triplet_regexp, | 
 |   libcc1_set_driver_filename, | 
 | }; | 
 |  | 
 | extern "C" gcc_c_fe_context_function gcc_c_fe_context; | 
 |  | 
 | #ifdef __GNUC__ | 
 | #pragma GCC visibility push(default) | 
 | #endif | 
 |  | 
 | extern "C" | 
 | struct gcc_c_context * | 
 | gcc_c_fe_context (enum gcc_base_api_version base_version, | 
 | 		  enum gcc_c_api_version c_version) | 
 | { | 
 |   if ((base_version != GCC_FE_VERSION_0 && base_version != GCC_FE_VERSION_1) | 
 |       || (c_version != GCC_C_FE_VERSION_0 && c_version != GCC_C_FE_VERSION_1)) | 
 |     return NULL; | 
 |  | 
 |   return new libcc1 (&vtable, &c_vtable); | 
 | } |