/* Interface to LUCID Cadillac system for GNU compiler.
   Copyright (C) 1988, 1992, 1993 Free Software Foundation, Inc.

This file is part of GNU CC.

GNU CC 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 2, or (at your option)
any later version.

GNU CC 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 GNU CC; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "config.h"

#include "tree.h"
#include "flags.h"
#include <stdio.h>
#include "cp-tree.h"
#include "obstack.h"

#ifdef CADILLAC
#include <compilerreq.h>
#include <compilerconn.h>
#include <sys/time.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/file.h>

#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free free

void init_cadillac ();

extern char *input_filename;
extern int lineno;

/* Put random information we might want to get back from
   Cadillac here.  */
typedef struct
{
  /* The connection to the Cadillac kernel.  */
  Connection *conn;

  /* Input and output file descriptors for Cadillac.  */
  short fd_input, fd_output;

  /* #include nesting of current file.  */
  short depth;

  /* State variables for the connection.  */
  char messages;
  char conversion;
  char emission;
  char process_until;

  /* #if level of current file.  */
  int iflevel;

  /* Line number that starts current source file.  */
  int lineno;

  /* Name of current file.  */
  char *filename;

  /* Where to stop processing (if process_until is set).  */
  char *end_filename;
  int end_position;

} cadillac_struct;
static cadillac_struct cadillacObj;

/* Nonzero if in the process of exiting.  */
static int exiting;

void cadillac_note_source ();
static void CWriteLanguageDecl ();
static void CWriteLanguageType ();
static void CWriteTopLevel ();
static void cadillac_note_filepos ();
static void cadillac_process_request (), cadillac_process_requests ();
static void cadillac_switch_source ();
static void exit_cadillac ();

/* Blocking test.  */
static int
readable_p (fd)
     int fd;
{
  fd_set f;

  FD_ZERO (&f);
  FD_SET (fd, &f);

  return select (32, &f, NULL, NULL, 0) == 1;
}

static CObjectType *tree_to_cadillac_map;
struct obstack cadillac_obstack;


#include "stack.h"

struct context_level
{
  struct stack_level base;

  tree context;
};

/* Stack for maintaining contexts (in case functions or types are nested).
   When defining a struct type, the `context' field is the RECORD_TYPE.
   When defining a function, the `context' field is the FUNCTION_DECL.  */

static struct context_level *context_stack;

static struct context_level *
push_context_level (stack, obstack)
     struct stack_level *stack;
     struct obstack *obstack;
{
  struct context_level tem;

  tem.base.prev = stack;
  return (struct context_level *)push_stack_level (obstack, &tem, sizeof (tem));
}

/* Discard a level of search allocation.  */

static struct context_level *
pop_context_level (stack)
     struct context_level *stack;
{
  stack = (struct context_level *)pop_stack_level (stack);
  return stack;
}

void
init_cadillac ()
{
  extern FILE *finput;
  extern int errno;
  CCompilerMessage* req;
  cadillac_struct *cp = &cadillacObj;
  int i;

  if (! flag_cadillac)
    return;

  tree_to_cadillac_map = (CObjectType*) xmalloc (sizeof (CObjectType) * LAST_CPLUS_TREE_CODE);
  for (i = 0; i < LAST_CPLUS_TREE_CODE; i++)
    tree_to_cadillac_map[i] = MiscOType;
  tree_to_cadillac_map[RECORD_TYPE] = StructOType;
  tree_to_cadillac_map[UNION_TYPE] = UnionOType;
  tree_to_cadillac_map[ENUMERAL_TYPE] = EnumTypeOType;
  tree_to_cadillac_map[TYPE_DECL] = TypedefOType;
  tree_to_cadillac_map[VAR_DECL] = VariableOType;
  tree_to_cadillac_map[CONST_DECL] = EnumConstantOType;
  tree_to_cadillac_map[FUNCTION_DECL] = FunctionOType;
  tree_to_cadillac_map[FIELD_DECL] = FieldOType;

#ifdef sun
  on_exit (&exit_cadillac, 0);
#endif

  gcc_obstack_init (&cadillac_obstack);

  /* Yow!  This is the way Cadillac was designed to deal with
     Oregon C++ compiler!  */
  cp->fd_input = flag_cadillac;
  cp->fd_output = flag_cadillac;

  /* Start in "turned-on" state.  */
  cp->messages = 1;
  cp->conversion = 1;
  cp->emission = 1;

  /* Establish a connection with Cadillac here.  */
  cp->conn = NewConnection (cp, cp->fd_input, cp->fd_output);

  CWriteHeader (cp->conn, WaitingMType, 0);
  CWriteRequestBuffer (cp->conn);

  if (!readable_p (cp->fd_input))
    ;

  req = CReadCompilerMessage (cp->conn);

  if (!req)
    switch (errno)
      {
      case EWOULDBLOCK:
	sleep (5);
	return;
      
      case 0:
	fatal ("init_cadillac: EOF on connection to kernel, exiting\n");
	break;

      default:
	perror ("Editor to kernel connection");
	exit (0);
      }
}

static void
cadillac_process_requests (conn)
     Connection *conn;
{
  CCompilerMessage *req;
  while (req = (CCompilerMessage*) CPeekNextRequest (conn))
    {
      req = CReadCompilerMessage (conn);
      cadillac_process_request (&cadillacObj, req);
    }
}

static void
cadillac_process_request (cp, req)
     cadillac_struct *cp;
     CCompilerMessage *req;
{
  if (! req)
    return;

  switch (req->reqType)
    {
    case ProcessUntilMType:
      if (cp->process_until)
	my_friendly_abort (23);
      cp->process_until = 1;
      /* This is not really right.  */
      cp->end_position = ((CCompilerCommand*)req)->processuntil.position;
#if 0
      cp->end_filename = req->processuntil.filename;
#endif
      break;

    case CommandMType:
      switch (req->header.data)
	{
	case MessagesOnCType:
	  cp->messages = 1;
	  break;
	case MessagesOffCType:
	  cp->messages = 0;
	  break;
	case ConversionOnCType:
	  cp->conversion = 1;
	  break;
	case ConversionOffCType:
	  cp->conversion = 0;
	  break;
	case EmissionOnCType:
	  cp->emission = 1;
	  break;
	case EmissionOffCType:
	  cp->emission = 0;
	  break;

	case FinishAnalysisCType:
	  return;

	case PuntAnalysisCType:
	case ContinueAnalysisCType:
	case GotoFileposCType:
	case OpenSucceededCType:
	case OpenFailedCType:
	  fprintf (stderr, "request type %d not implemented\n", req->reqType);
	  return;

	case DieCType:
	  if (! exiting)
	    my_friendly_abort (24);
	  return;

	}
      break;

    default:
      fatal ("unknown request type %d", req->reqType);
    }
}

void
cadillac_start ()
{
  Connection *conn = cadillacObj.conn;
  CCompilerMessage *req;

  /* Let Cadillac know that we start in C++ language scope.  */
  CWriteHeader (conn, ForeignLinkageMType, LinkCPlus);
  CWriteLength (conn);
  CWriteRequestBuffer (conn);

  cadillac_process_requests (conn);
}

static void
cadillac_printf (msg, name)
{
  if (cadillacObj.messages)
    printf ("[%s,%4d] %s `%s'\n", input_filename, lineno, msg, name);
}

void
cadillac_start_decl (decl)
     tree decl;
{
  Connection *conn = cadillacObj.conn;
  CObjectType object_type = tree_to_cadillac_map [TREE_CODE (decl)];

  if (context_stack)
    switch (TREE_CODE (context_stack->context))
      {
      case FUNCTION_DECL:
	/* Currently, cadillac only implements top-level forms.  */
	return;
      case RECORD_TYPE:
      case UNION_TYPE:
	cadillac_printf ("start class-level decl", IDENTIFIER_POINTER (DECL_NAME (decl)));
	break;
      default:
	my_friendly_abort (25);
      }
  else
    {
      cadillac_printf ("start top-level decl", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)));
      CWriteTopLevel (conn, StartMType);
    }

  CWriteLanguageDecl (conn, decl, tree_to_cadillac_map[TREE_CODE (decl)]);
  CWriteRequestBuffer (conn);
  cadillac_process_requests (conn);
}

void
cadillac_finish_decl (decl)
     tree decl;
{
  Connection *conn = cadillacObj.conn;

  if (context_stack)
    switch (TREE_CODE (context_stack->context))
      {
      case FUNCTION_DECL:
	return;
      case RECORD_TYPE:
      case UNION_TYPE:
	cadillac_printf ("end class-level decl", IDENTIFIER_POINTER (DECL_NAME (decl)));
	CWriteHeader (conn, EndDefMType, 0);
	CWriteLength (conn);
	break;
      default:
	my_friendly_abort (26);
      }
  else
    {
      cadillac_printf ("end top-level decl", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)));
      CWriteHeader (conn, EndDefMType, 0);
      CWriteLength (conn);
      CWriteTopLevel (conn, StopMType);
    }

  CWriteRequestBuffer (conn);
  cadillac_process_requests (conn);
}

void
cadillac_start_function (fndecl)
     tree fndecl;
{
  Connection *conn = cadillacObj.conn;

  if (context_stack)
    /* nested functions not yet handled.  */
    my_friendly_abort (27);

  cadillac_printf ("start top-level function", lang_printable_name (fndecl));
  context_stack = push_context_level (context_stack, &cadillac_obstack);
  context_stack->context = fndecl;

  CWriteTopLevel (conn, StartMType);
  my_friendly_assert (TREE_CODE (fndecl) == FUNCTION_DECL, 202);
  CWriteLanguageDecl (conn, fndecl,
		      (TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE
		       ? MemberFnOType : FunctionOType));
  CWriteRequestBuffer (conn);
  cadillac_process_requests (conn);
}

void
cadillac_finish_function (fndecl)
     tree fndecl;
{
  Connection *conn = cadillacObj.conn;

  cadillac_printf ("end top-level function", lang_printable_name (fndecl));
  context_stack = pop_context_level (context_stack);

  if (context_stack)
    /* nested functions not yet implemented.  */
    my_friendly_abort (28);

  CWriteHeader (conn, EndDefMType, 0);
  CWriteLength (conn);
  CWriteTopLevel (conn, StopMType);
  CWriteRequestBuffer (conn);
  cadillac_process_requests (conn);
}

void
cadillac_finish_anon_union (decl)
     tree decl;
{
  Connection *conn = cadillacObj.conn;

  if (! global_bindings_p ())
    return;
  cadillac_printf ("finish top-level anon union", "");
  CWriteHeader (conn, EndDefMType, 0);
  CWriteLength (conn);
  CWriteTopLevel (conn, StopMType);
  CWriteRequestBuffer (conn);
  cadillac_process_requests (conn);
}

void
cadillac_start_enum (type)
     tree type;
{
  Connection *conn = cadillacObj.conn;

  tree name = TYPE_NAME (type);

  if (TREE_CODE (name) == TYPE_DECL)
    name = DECL_NAME (name);

  if (context_stack)
    switch (TREE_CODE (context_stack->context))
      {
      case FUNCTION_DECL:
	return;
      case RECORD_TYPE:
      case UNION_TYPE:
	break;
      default:
	my_friendly_abort (29);
      }
  else
    {
      cadillac_printf ("start top-level enum", IDENTIFIER_POINTER (name));
      CWriteTopLevel (conn, StartMType);
    }

  CWriteLanguageType (conn, type, tree_to_cadillac_map[ENUMERAL_TYPE]);
}

void
cadillac_finish_enum (type)
     tree type;
{
  Connection *conn = cadillacObj.conn;
  tree name = TYPE_NAME (type);

  if (TREE_CODE (name) == TYPE_DECL)
    name = DECL_NAME (name);

  if (context_stack)
    switch (TREE_CODE (context_stack->context))
      {
      case FUNCTION_DECL:
	return;
      case RECORD_TYPE:
      case UNION_TYPE:
	CWriteHeader (conn, EndDefMType, 0);
	CWriteLength (conn);
	break;
      default:
	my_friendly_abort (30);
      }
  else
    {
      CWriteHeader (conn, EndDefMType, 0);
      CWriteLength (conn);
      cadillac_printf ("finish top-level enum", IDENTIFIER_POINTER (name));
      CWriteTopLevel (conn, StopMType);
    }

  CWriteRequestBuffer (conn);
  cadillac_process_requests (conn);
}

void
cadillac_start_struct (type)
     tree type;
{
  Connection *conn = cadillacObj.conn;
  tree name = TYPE_NAME (type);

  if (TREE_CODE (name) == TYPE_DECL)
    name = DECL_NAME (name);

  if (context_stack)
    switch (TREE_CODE (context_stack->context))
      {
      case FUNCTION_DECL:
	return;
      case RECORD_TYPE:
      case UNION_TYPE:
	return;
      default:
	my_friendly_abort (31);
      }
  else
    {
      cadillac_printf ("start struct", IDENTIFIER_POINTER (name));
      CWriteTopLevel (conn, StartMType);
    }

  context_stack = push_context_level (context_stack, &cadillac_obstack);
  context_stack->context = type;

  CWriteLanguageType (conn, type,
		      TYPE_LANG_SPECIFIC (type) && CLASSTYPE_DECLARED_CLASS (type) ? ClassOType : tree_to_cadillac_map[TREE_CODE (type)]);
}

void
cadillac_finish_struct (type)
     tree type;
{
  Connection *conn = cadillacObj.conn;
  tree name = TYPE_NAME (type);

  if (TREE_CODE (name) == TYPE_DECL)
    name = DECL_NAME (name);

  context_stack = pop_context_level (context_stack);
  if (context_stack)
    return;

  cadillac_printf ("finish struct", IDENTIFIER_POINTER (name));
  CWriteHeader (conn, EndDefMType, 0);
  CWriteLength (conn);
  CWriteTopLevel (conn, StopMType);
  CWriteRequestBuffer (conn);
  cadillac_process_requests (conn);
}

void
cadillac_finish_exception (type)
     tree type;
{
  Connection *conn = cadillacObj.conn;

  fatal ("cadillac_finish_exception");
  CWriteHeader (conn, EndDefMType, 0);
  CWriteLength (conn);
  CWriteTopLevel (conn, StopMType);
  CWriteRequestBuffer (conn);
  cadillac_process_requests (conn);
}

void
cadillac_push_class (type)
     tree type;
{
}

void
cadillac_pop_class ()
{
}

void
cadillac_push_lang (name)
     tree name;
{
  Connection *conn = cadillacObj.conn;
  CLinkLanguageType m;

  if (name == lang_name_cplusplus)
    m = LinkCPlus;
  else if (name == lang_name_c)
    m = LinkC;
  else
    my_friendly_abort (32);
  CWriteHeader (conn, ForeignLinkageMType, m);
  CWriteRequestBuffer (conn);
  cadillac_process_requests (conn);
}

void
cadillac_pop_lang ()
{
  Connection *conn = cadillacObj.conn;

  CWriteHeader (conn, ForeignLinkageMType, LinkPop);
  CWriteRequestBuffer (conn);
  cadillac_process_requests (conn);
}

void
cadillac_finish_stmt ()
{
}

void
cadillac_note_source ()
{
  cadillacObj.lineno = lineno;
  cadillacObj.filename = input_filename;
}

static void
CWriteTopLevel (conn, m)
     Connection *conn;
     CMessageSubType m;
{
  static context_id = 0;
  CWriteHeader (conn, TopLevelFormMType, m);
  cadillac_note_filepos ();

  /* Eventually, this will point somewhere into the digest file.  */
  context_id += 1;
  CWriteSomething (conn, &context_id, sizeof (BITS32));

  CWriteSomething (conn, &cadillacObj.iflevel, sizeof (BITS32));
  CWriteLength (conn);
}

static void
cadillac_note_filepos ()
{
  extern FILE *finput;
  int pos = ftell (finput);
  CWriteSomething (cadillacObj.conn, &pos, sizeof (BITS32));
}

void
cadillac_switch_source (startflag)
     int startflag;
{
  Connection *conn = cadillacObj.conn;
  /* Send out the name of the source file being compiled.  */

  CWriteHeader (conn, SourceFileMType, startflag ? StartMType : StopMType);
  CWriteSomething (conn, &cadillacObj.depth, sizeof (BITS16));
  CWriteVstring0 (conn, input_filename);
  CWriteLength (conn);
  CWriteRequestBuffer (conn);
  cadillac_process_requests (conn);
}

void
cadillac_push_source ()
{
  cadillacObj.depth += 1;
  cadillac_switch_source (1);
}

void
cadillac_pop_source ()
{
  cadillacObj.depth -= 1;
  cadillac_switch_source (0);
}

struct cadillac_mdep
{
  short object_type;
  char linkage;
  char access;
  short length;
};

static void
CWriteLanguageElem (conn, p, name)
     Connection *conn;
     struct cadillac_mdep *p;
     char *name;
{
  CWriteSomething (conn, &p->object_type, sizeof (BITS16));
  CWriteSomething (conn, &p->linkage, sizeof (BITS8));
  CWriteSomething (conn, &p->access, sizeof (BITS8));
  CWriteSomething (conn, &p->length, sizeof (BITS16));
  CWriteVstring0 (conn, name);

#if 0
  /* Don't write date_type.  */
  CWriteVstring0 (conn, "");
#endif
  CWriteLength (conn);
}

static void
CWriteLanguageDecl (conn, decl, object_type)
     Connection *conn;
     tree decl;
     CObjectType object_type;
{
  struct cadillac_mdep foo;
  tree name;

  CWriteHeader (conn, LanguageElementMType, StartDefineMType);
  foo.object_type = object_type;
  if (decl_type_context (decl))
    {
      foo.linkage = ParentLinkage;
      if (TREE_PRIVATE (decl))
	foo.access = PrivateAccess;
      else if (TREE_PROTECTED (decl))
	foo.access = ProtectedAccess;
      else
	foo.access = PublicAccess;
    }
  else
    {
      if (TREE_PUBLIC (decl))
	foo.linkage = GlobalLinkage;
      else
	foo.linkage = FileLinkage;
      foo.access = PublicAccess;
    }
  name = DECL_NAME (decl);
  foo.length = IDENTIFIER_LENGTH (name);

  CWriteLanguageElem (conn, &foo, IDENTIFIER_POINTER (name));
  CWriteRequestBuffer (conn);
  cadillac_process_requests (conn);
}

static void
CWriteLanguageType (conn, type, object_type)
     Connection *conn;
     tree type;
     CObjectType object_type;
{
  struct cadillac_mdep foo;
  tree name = TYPE_NAME (type);

  CWriteHeader (conn, LanguageElementMType, StartDefineMType);
  foo.object_type = object_type;
  if (current_class_type)
    {
      foo.linkage = ParentLinkage;
      if (TREE_PRIVATE (type))
	foo.access = PrivateAccess;
      else if (TREE_PROTECTED (type))
	foo.access = ProtectedAccess;
      else
	foo.access = PublicAccess;
    }
  else
    {
      foo.linkage = NoLinkage;
      foo.access = PublicAccess;
    }
  if (TREE_CODE (name) == TYPE_DECL)
    name = DECL_NAME (name);

  foo.length = IDENTIFIER_LENGTH (name);

  CWriteLanguageElem (conn, &foo, IDENTIFIER_POINTER (name));
  CWriteRequestBuffer (conn);
  cadillac_process_requests (conn);
}

static void
CWriteUseObject (conn, type, object_type, use)
     Connection *conn;
     tree type;
     CObjectType object_type;
     CMessageSubType use;
{
  struct cadillac_mdep foo;
  tree name = NULL_TREE;

  CWriteHeader (conn, LanguageElementMType, use);
  foo.object_type = object_type;
  if (current_class_type)
    {
      foo.linkage = ParentLinkage;
      if (TREE_PRIVATE (type))
	foo.access = PrivateAccess;
      else if (TREE_PROTECTED (type))
	foo.access = ProtectedAccess;
      else
	foo.access = PublicAccess;
    }
  else
    {
      foo.linkage = NoLinkage;
      foo.access = PublicAccess;
    }
  switch (TREE_CODE (type))
    {
    case VAR_DECL:
    case FIELD_DECL:
    case TYPE_DECL:
    case CONST_DECL:
    case FUNCTION_DECL:
      name = DECL_NAME (type);
      break;

    default:
      my_friendly_abort (33);
  }

  foo.length = IDENTIFIER_LENGTH (name);

  CWriteLanguageElem (conn, &foo, IDENTIFIER_POINTER (name));
  CWriteRequestBuffer (conn);
  cadillac_process_requests (conn);
}

/* Here's how we exit under cadillac.  */

static void
exit_cadillac ()
{
  extern int errorcount;

  Connection *conn = cadillacObj.conn;

  if (flag_cadillac)
    {
      CCompilerMessage *req;

      CWriteHeader (conn, FinishedMType,
		    errorcount ? 0 : CsObjectWritten | CsComplete);
      /* Bye, bye!  */
      CWriteRequestBuffer (conn);

      /* Block on read.  */
      while (! readable_p (cadillacObj.fd_input))
	{
	  if (exiting)
	    my_friendly_abort (34);
	  exiting = 1;
	}
      exiting = 1;

      req = CReadCompilerMessage (conn);
      cadillac_process_request (&cadillacObj, req);
    }
}

#else
/* Stubs.  */
void init_cadillac () {}
void cadillac_start () {}
void cadillac_start_decl (decl)
     tree decl;
{}
void
cadillac_finish_decl (decl)
     tree decl;
{}
void
cadillac_start_function (fndecl)
     tree fndecl;
{}
void
cadillac_finish_function (fndecl)
     tree fndecl;
{}
void
cadillac_finish_anon_union (decl)
     tree decl;
{}
void
cadillac_start_enum (type)
     tree type;
{}
void
cadillac_finish_enum (type)
     tree type;
{}
void
cadillac_start_struct (type)
     tree type;
{}
void
cadillac_finish_struct (type)
     tree type;
{}
void
cadillac_finish_exception (type)
     tree type;
{}
void
cadillac_push_class (type)
     tree type;
{}
void
cadillac_pop_class ()
{}
void
cadillac_push_lang (name)
     tree name;
{}
void
cadillac_pop_lang ()
{}
void
cadillac_note_source ()
{}
void
cadillac_finish_stmt ()
{}
void
cadillac_switch_source ()
{}
void
cadillac_push_source ()
{}
void
cadillac_pop_source ()
{}
#endif
