/* prims.c: -*- C -*-  Primitive internal functions. */

/* Author: Brian J. Fox (bfox@ai.mit.edu) Sat Jul 20 17:22:47 1996.

   This file is part of <Meta-HTML>(tm), a system for the rapid deployment
   of Internet and Intranet applications via the use of the Meta-HTML
   language.

   Copyright (c) 1995, 1996, Brian J. Fox (bfox@ai.mit.edu).
   Copyright (c) 1996, Universal Access Inc. (http://www.ua.com).

   Meta-HTML is free software; you can redistribute it and/or modify
   it under the terms of the UAI Free Software License as published
   by Universal Access Inc.; either version 1, or (at your option) any
   later version.

   This program 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
   UAI Free Software License for more details.

   You should have received a copy of the UAI Free Software License
   along with this program; if you have not, you may obtain one by
   writing to:

   Universal Access Inc.
   129 El Paseo Court
   Santa Barbara, CA
   93101  */

#include "language.h"
#include "symdump.h"

static void pf_eval (PFunArgs);
static void pf_read (PFunArgs);
static void pf_write_package_file (PFunArgs);
static void pf_read_package_file (PFunArgs);
static void pf_switch_user (PFunArgs);
static void pf_the_page (PFunArgs);
static void pf_point (PFunArgs);

PFunDesc prims_table[] = {
  { "%%EVAL",			0, 0, pf_eval },
  { "%%READ",			0, 0, pf_read },
  { "%%WRITE-PACKAGE-FILE",	0, 0, pf_write_package_file },
  { "%%READ-PACKAGE-FILE",	0, 0, pf_read_package_file },
  { "%%SWITCH-USER",		0, 0, pf_switch_user },
  { "%%THE-PAGE",		0, 0, pf_the_page },
  { "%%POINT",			0, 0, pf_point },
  { (char *)NULL,		0, 0, (PFunHandler *)NULL }
};

PACKAGE_INITIALIZER (initialize_primitive_functions)
void
initialize_primitive_functions (Package *package)
{
  register int i;
  Symbol *sym;

  for (i = 0; prims_table[i].tag != (char *)NULL; i++)
    {
      sym = symbol_intern_in_package (package, prims_table[i].tag);
      sym->type = symtype_FUNCTION;
      sym->values = (char **)(&prims_table[i]);
    }
}

static void
pf_eval (PFunArgs)
{
  char *result = body ? body->buffer : (char *)NULL;

  if (result != (char *)NULL)
    {
      char *expr = mhtml_evaluate_string (result);
      result = mhtml_evaluate_string (expr);

      if (!empty_string_p (result))
	{
	  bprintf_insert (page, start, "%s", result);
	  *newstart = start + strlen (result);
	}

      if (expr != (char *)NULL) free (expr);
      if (result != (char *)NULL) free (result);
    }
}

static void
pf_read (PFunArgs)
{
  char *result = body ? body->buffer : (char *)NULL;

  if (result != (char *)NULL)
    {
      int point = 0;
      char *sexp = read_sexp_1 (result, &point, 0, 1);

      if (!empty_string_p (sexp))
	{
	  bprintf_insert (page, start, "%s", sexp);
	  *newstart = start + strlen (sexp);
	}

      if (sexp != (char *)NULL)
	free (sexp);
    }
}

static void
pf_write_package_file (PFunArgs)
{
  char *filename = mhtml_evaluate_string (get_positional_arg (vars, 0));
  int packages_written = 0;

  if (!empty_string_p (filename))
    {
      int fd = os_open (filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);

      if (fd > -1)
	{
	  register int i = 1;
	  char *arg;

	  while ((arg = get_positional_arg (vars, i)) != (char *)NULL)
	    {
	      char *packname = mhtml_evaluate_string (arg);

	      if (!empty_string_p (packname))
		{
		  Package *pack = symbol_lookup_package (packname);

		  if (pack != (Package *)NULL)
		    {
		      symbol_dump_package (fd, pack);
		      packages_written++;
		    }
		}

	      if (packname) free (packname);
	      i++;
	    }
	  close (fd);
	}
    }

  if (filename) free (filename);

  if (packages_written)
    bprintf_insert (page, start, "%d", packages_written);
}

static void
pf_read_package_file (PFunArgs)
{
  char *filename = mhtml_evaluate_string (get_positional_arg (vars, 0));
  int packages_read = 0;

  if (!empty_string_p (filename))
    {
      int fd;

      /* If the filename specification doesn't start with a slash, then
	 add the current directory and relative path. */
      if (*filename != '/')
	{
	  BPRINTF_BUFFER *temp = bprintf_create_buffer ();
	  char *pre = pagefunc_get_variable ("mhtml::include-prefix");
	  char *rel = pagefunc_get_variable ("mhtml::relative-prefix");

	  if (pre)
	    {
	      if (!rel) rel = "";
	      bprintf (temp, "%s%s/%s", pre, rel, filename);
	      free (filename);
	      filename = temp->buffer;
	      free (temp);
	    }
	}

      fd = os_open (filename, O_RDONLY, 0666);

      if (fd > -1)
	{
	  Package *pack;

	  while ((pack = symbol_load_package (fd)) != (Package *)NULL)
	    packages_read++;

	  close (fd);
	}
    }

  if (filename) free (filename);

  if (packages_read)
    {
      bprintf_insert (page, start, "%d", packages_read);

      if (mhtml_user_keywords == (Package *)NULL)
	mhtml_user_keywords = symbol_lookup_package ("*user-functions*");
    }
}

static void
pf_switch_user (PFunArgs)
{
  char *username = mhtml_evaluate_string (get_positional_arg (vars, 0));
  char *result = (char *)NULL;

#if defined (HAVE_GETPWNAM)
  if (!empty_string_p (username))
    {
      struct passwd *entry = (struct passwd *)getpwnam (username);

      if (entry != (struct passwd *)NULL)
	{
	  uid_t uid = (uid_t)entry->pw_uid;

	  if (setuid (uid) != -1)
	    result = "true";
	}
    }
#endif /* HAVE_GETPWNAM */

  if (username != (char *)NULL) free (username);

  if (result != (char *)NULL)
    {
      bprintf_insert (page, start, "%s", result);
      *newstart += strlen (result);
    }
}

/* <%%the-page [varname]>
   Places the current page into VARNAME as a binary variable, or,
   returns the current page as text if VARNAME is not supplied. */
static void
pf_the_page (PFunArgs)
{
  char *varname = mhtml_evaluate_string (get_positional_arg (vars, 0));
  PAGE *the_page = parser_top_page ();

  if (empty_string_p (varname))
    {
      PAGE *contents = page_copy_page (the_page);
      bprintf_insert (page, start, "%s", contents->buffer);
      *newstart += contents->bindex;
      page_free_page (contents);
    }
  else
    {
      Symbol *sym = symbol_remove (varname);
      Datablock *block;
      symbol_free (sym);

      sym = symbol_intern (varname);
      block = datablock_create (the_page->buffer, the_page->bindex);
      sym->type = symtype_BINARY;
      sym->values = (char **)block;
    }

  xfree (varname);
}

/* <%%point> --> Returns the current parser marker in the page.
   CAVEAT!  Currently, can only be called at top level. */
static void
pf_point (PFunArgs)
{
  bprintf_insert (page, start, "%d", start);
}
