/* cleaner.c: -*- C -*-  Cleanup excess whitespace before point. */

/*  Copyright (c) 1996 Brian J. Fox
    Author: Brian J. Fox (bfox@ai.mit.edu) Tue Apr 15 13:01:17 1997.

    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"

static void pf_cleanup_whitespace (PFunArgs);
static void pf_cleanup_beautify (PFunArgs);

static PFunDesc functab[] = {
  /*   tag		complex? debug_level	   code    */
  { "CLEANUP-WHITESPACE", 0,	 0,		pf_cleanup_whitespace },
  { "CLEANUP-BEAUTIFY",	  0,	 0,		pf_cleanup_beautify },
  { (char *)NULL,	0,	 0,		(PFunHandler *)NULL }
};

PACKAGE_INITIALIZER(initialize_cleaner_funcs)

void
initialize_cleaner_funcs (Package *pack)
{
  register int i;
  Symbol *sym;

  /* Install the names and pointers. */
  for (i = 0; functab[i].tag != (char *)NULL; i++)
    {
      sym = symbol_intern_in_package (mhtml_function_package, functab[i].tag);
      sym->type = symtype_FUNCTION;
      sym->values = (char **)(&functab[i]);
    }
}

#define newline(x) ((x) == '\r' || (x) == '\n')
static char *
cleanup_html_whitespace (char *string, int len, int beautify_p)
{
  register int src, dst;
  char *result = (char *)xmalloc (1 + len);
  int quoted = 0;
  int brace_level = 0;
  int in_pre = 0;
  int in_textarea = 0;
  int accum = 0, newlines = 0;

  src = dst = 0;

  while (src < len)
    {
      if (!quoted && !in_pre && !in_textarea)
	{
	  if (newline (string[src]))
	    newlines++;
	  else if (whitespace (string[src]))
	    accum++;
	  else
	    {
	      if (newlines)
		{
		  if (string[src] != '>')
		    {
		      result[dst++] = '\n';
		      if (beautify_p && newlines > 1)
			result[dst++] = '\n';
		    }
		  newlines = 0;
		  accum = 0;
		}
	      else if (accum)
		{
		  accum = 0;
		  if (string[src] != '>')
		    result[dst++] = ' ';
		}
	      result[dst++] = string[src];
	    }
	}
      else
	{
	  result[dst++] = string[src];
	}

      if (string[src] == '"')
	quoted = !quoted;

      if (!quoted)
	{
	  if (string[src] == '<')
	    {
	      brace_level++;
	      /* Check for <TEXTAREA ....
		 Do NOT check for "<TEXTAREA>" as that is handled below. */
	      if ((string[src + 1] == 'T' || string[src + 1] == 't') &&
		  (string[src + 2] == 'E' || string[src + 2] == 'e') &&
		  (string[src + 3] == 'X' || string[src + 3] == 'x') &&
		  (string[src + 4] == 'T' || string[src + 4] == 't') &&
		  (string[src + 5] == 'A' || string[src + 5] == 'a') &&
		  (string[src + 6] == 'R' || string[src + 6] == 'r') &&
		  (string[src + 7] == 'E' || string[src + 7] == 'e') &&
		  (string[src + 8] == 'A' || string[src + 8] == 'a') &&
		  (whitespace (string[src + 9]) ||
		   (newline (string[src + 9]))))
		in_textarea++;
	    }
	  else if (string[src] == '>')
	    {
	      brace_level--;
	      if (src > 3)
		{
		  /* Check explicitly for <pre> and </pre> */
		  if ((string[src - 1] == 'E' || string[src - 1] == 'e') &&
		      (string[src - 2] == 'R' || string[src - 2] == 'r') &&
		      (string[src - 3] == 'P' || string[src - 3] == 'p'))
		    {
		      if (string[src - 4] == '<')
			in_pre++;
		      else if (in_pre &&
			       (string[src - 4] == '/' &&
				string[src - 5] == '<'))
			in_pre--;
		    }

		  if (src > 8)
		    {
		      /* Check explicitly for <textarea> and </textarea>. */
		      if ((string[src - 1] == 'A' || string[src - 1] == 'a') &&
			  (string[src - 2] == 'E' || string[src - 2] == 'e') &&
			  (string[src - 3] == 'R' || string[src - 3] == 'r') &&
			  (string[src - 4] == 'A' || string[src - 4] == 'a') &&
			  (string[src - 5] == 'T' || string[src - 5] == 't') &&
			  (string[src - 6] == 'X' || string[src - 6] == 'x') &&
			  (string[src - 7] == 'E' || string[src - 7] == 'e') &&
			  (string[src - 8] == 'T' || string[src - 8] == 't'))
			{
			  if (string[src - 9] == '<')
			    in_textarea++;
			  else if (in_textarea &&
				   (string[src - 9] == '/' &&
				    string[src - 10] == '<'))
			    in_textarea--;
			}
		    }
		}
	    }
	}

      src++;
    }

  result[dst] = '\0';
  return (result);
}
	    
static void
pf_cleanup_whitespace (PFunArgs)
{
  char *varname = mhtml_evaluate_string (get_positional_arg (vars, 0));
  char *cleaned = (char *)NULL;

  if (varname != (char *)NULL)
    {
      char *temp = pagefunc_get_variable (varname);

      if (temp != (char *)NULL)
	{
	  cleaned = cleanup_html_whitespace (temp, strlen (temp), 0);
	  pagefunc_set_variable (varname, cleaned);
	  xfree (cleaned);
	}
    }
  else
    {
      cleaned = cleanup_html_whitespace (page->buffer, start, 0);
      bprintf_delete_range (page, 0, start);
      bprintf_insert_text (page, 0, cleaned);
      *newstart = strlen (cleaned);
      free (cleaned);
    }
}

static void
pf_cleanup_beautify (PFunArgs)
{
  char *cleaned = cleanup_html_whitespace (page->buffer, start, 1);
  bprintf_delete_range (page, 0, start);
  bprintf_insert_text (page, 0, cleaned);
  free (cleaned);
  *newstart = strlen (cleaned);
}
