/****************************************************************************
 * NCSA Mosaic for the X Window System                                      *
 * Software Development Group                                               *
 * National Center for Supercomputing Applications                          *
 * University of Illinois at Urbana-Champaign                               *
 * 605 E. Springfield, Champaign IL 61820                                   *
 * mosaic@ncsa.uiuc.edu                                                     *
 *                                                                          *
 * Copyright (C) 1993, Board of Trustees of the University of Illinois      *
 *                                                                          *
 * NCSA Mosaic software, both binary and source (hereafter, Software) is    *
 * copyrighted by The Board of Trustees of the University of Illinois       *
 * (UI), and ownership remains with the UI.                                 *
 *                                                                          *
 * The UI grants you (hereafter, Licensee) a license to use the Software    *
 * for academic, research and internal business purposes only, without a    *
 * fee.  Licensee may distribute the binary and source code (if released)   *
 * to third parties provided that the copyright notice and this statement   *
 * appears on all copies and that no charge is associated with such         *
 * copies.                                                                  *
 *                                                                          *
 * Licensee may make derivative works.  However, if Licensee distributes    *
 * any derivative work based on or derived from the Software, then          *
 * Licensee will (1) notify NCSA regarding its distribution of the          *
 * derivative work, and (2) clearly notify users that such derivative       *
 * work is a modified version and not the original NCSA Mosaic              *
 * distributed by the UI.                                                   *
 *                                                                          *
 * Any Licensee wishing to make commercial use of the Software should       *
 * contact the UI, c/o NCSA, to negotiate an appropriate license for such   *
 * commercial use.  Commercial use includes (1) integration of all or       *
 * part of the source code into a product for sale or license by or on      *
 * behalf of Licensee to third parties, or (2) distribution of the binary   *
 * code or source code to third parties that need it to utilize a           *
 * commercial product sold or licensed by or on behalf of Licensee.         *
 *                                                                          *
 * UI MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR   *
 * ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED          *
 * WARRANTY.  THE UI SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY THE    *
 * USERS OF THIS SOFTWARE.                                                  *
 *                                                                          *
 * By using or copying this Software, Licensee agrees to abide by the       *
 * copyright law and all other applicable laws of the U.S. including, but   *
 * not limited to, export control laws, and the terms of this license.      *
 * UI shall have the right to terminate this license immediately by         *
 * written notice upon Licensee's breach of, or non-compliance with, any    *
 * of its terms.  Licensee may be held legally responsible for any          *
 * copyright infringement that is caused or encouraged by Licensee's        *
 * failure to abide by the terms of this license.                           *
 *                                                                          *
 * Comments and questions are welcome and can be sent to                    *
 * mosaic-x@ncsa.uiuc.edu.                                                  *
 ****************************************************************************/

#include <stdio.h>

#include "HTML.h"

/*
 * Code to manage a linked list of parsed HTML objects generated
 * from a raw text file.
 * Also code to manage a linked list of formatted elements that
 * make up a page of a formatted document.
 */

/*
 * Free up the passed linked list of parsed elements, freeing
 * all memory associates with each element.
 */
void
FreeObjList (List)
struct mark_up *List;
{
  struct mark_up *current;
  struct mark_up *mptr;

  current = List;
  while (current != NULL)
  {
    mptr = current;
    current = current->next;
    mptr->next = NULL;
    if (mptr->start != NULL)
    {
      XtFree((char *) mptr->start);
    }
    if (mptr->text != NULL)
    {
      XtFree((char *) mptr->text);
    }
    if (mptr->end != NULL)
    {
      XtFree((char *) mptr->end);
    }
    XtFree((char *) mptr);
  }
}


/*
 * Add an object to the parsed object list.
 * return a pointer to the current (end) position in the list.
 * If the object is a normal text object containing nothing but
 * white space, throw it out, unless we have been told to keep
 * white space.
 */
struct mark_up *
AddObj (listp, current, mark, keep_wsp)
struct mark_up **listp;
struct mark_up *current;
struct mark_up *mark;
int keep_wsp;
{
  if (mark == NULL)
  {
    return (current);
  }

  /*
   * Throw out normal text blocks that are only white space,
   * unless keep_wsp is set.
   */
  if ((mark->type == M_NONE) && (!keep_wsp))
  {
    char *ptr;

    ptr = mark->text;
    if (ptr == NULL)
    {
      XtFree((char *) mark);
      return (current);
    }

/*
 * No longer throw out whitespace, it is important to keep
 * white space between tags.
 while ((*ptr == ' ')||(*ptr == '\t')||(*ptr == '\n'))
 {
 ptr++;
 }
 *
 */

    if (*ptr == '\0')
    {
      XtFree(mark->text);
      XtFree((char *) mark);
      return (current);
    }
  }

  /*
   * Add object to either the head of the list for a new list,
   * or at the end after the current pointer.
   */
  if (*listp == NULL)
  {
    *listp = mark;
    current = *listp;
  }
  else
  {
    current->next = mark;
    current = current->next;
  }

  current->next = NULL;

  return (current);
}


#ifdef DEBUG

/*
 * Convert type number to a printed string for debug
 */
void
PrintType (type)
int type;
{
  switch (type)
  {
  case M_NONE:
    printf ("M_NONE");
    break;
  case M_TITLE:
    printf ("M_TITLE");
    break;
  case M_FIXED:
    printf ("M_FIXED");
    break;
  case M_BOLD:
    printf ("M_BOLD");
    break;
  case M_ITALIC:
    printf ("M_ITALIC");
    break;
  case M_EMPHASIZED:
    printf ("M_EMPHASIZED");
    break;
  case M_STRONG:
    printf ("M_STRONG");
    break;
  case M_CODE:
    printf ("M_CODE");
    break;
  case M_SAMPLE:
    printf ("M_SAMPLE");
    break;
  case M_KEYBOARD:
    printf ("M_KEYBOARD");
    break;
  case M_VARIABLE:
    printf ("M_VARIABLE");
    break;
  case M_CITATION:
    printf ("M_CITATION");
    break;
  case M_STRIKEOUT:
    printf ("M_STRIKEOUT");
    break;
  case M_HEADER_1:
    printf ("M_HEADER_1");
    break;
  case M_HEADER_2:
    printf ("M_HEADER_2");
    break;
  case M_HEADER_3:
    printf ("M_HEADER_3");
    break;
  case M_HEADER_4:
    printf ("M_HEADER_4");
    break;
  case M_HEADER_5:
    printf ("M_HEADER_5");
    break;
  case M_HEADER_6:
    printf ("M_HEADER_6");
    break;
  case M_ANCHOR:
    printf ("M_ANCHOR");
    break;
  case M_PARAGRAPH:
    printf ("M_PARAGRAPH");
    break;
  case M_ADDRESS:
    printf ("M_ADDRESS");
    break;
  case M_PLAIN_TEXT:
    printf ("M_PLAIN_TEXT");
    break;
  case M_LISTING_TEXT:
    printf ("M_LISTING_TEXT");
    break;
  case M_UNUM_LIST:
    printf ("M_UNUM_LIST");
    break;
  case M_NUM_LIST:
    printf ("M_NUM_LIST");
    break;
  case M_MENU:
    printf ("M_MENU");
    break;
  case M_DIRECTORY:
    printf ("M_DIRECTORY");
    break;
  case M_LIST_ITEM:
    printf ("M_LIST_ITEM");
    break;
  case M_DESC_LIST:
    printf ("M_DESC_LIST");
    break;
  case M_DESC_TITLE:
    printf ("M_DESC_TITLE");
    break;
  case M_DESC_TEXT:
    printf ("M_DESC_TEXT");
    break;
  case M_IMAGE:
    printf ("M_IMAGE");
    break;
  case M_SELECT:
    printf ("M_SELECT");
    break;
  case M_OPTION:
    printf ("M_OPTION");
    break;
  case M_INPUT:
    printf ("M_INPUT");
    break;
  case M_TEXTAREA:
    printf ("M_TEXTAREA");
    break;
  case M_FORM:
    printf ("M_FORM");
    break;
  case M_INDEX:
    printf ("M_INDEX");
    break;
  case M_HRULE:
    printf ("M_HRULE");
    break;
  case M_BASE:
    printf ("M_BASE");
    break;
  case M_LINEBREAK:
    printf ("M_LINEBREAK");
    break;
  case M_BLOCKQUOTE:
    printf ("M_BLOCKQUOTE");
    break;
  default:
    printf ("UNKNOWN %d", type);
    break;
  }
}


/*
 * Print the contents of a parsed object list, for debug
 */
void
PrintList (list)
struct mark_up *list;
{
  struct mark_up *mptr;

  mptr = list;
  while (mptr != NULL)
  {
    PrintType (mptr->type);
    if (mptr->is_end)
    {
      printf (" END");
    }
    else
    {
      printf (" START");
    }
    if (mptr->text != NULL)
    {
      printf ("\n{\n\t");
      printf ("%s", mptr->text);
      printf ("}\n");
    }
    else
    {
      printf ("\n");
    }
    mptr = mptr->next;
  }
}

#endif /* DEBUG */


/*
 * Used to find the longest line (in characters) in a collection
 * of text blocks.  cnt is the running count of characters, and
 * txt is the pointer to the current text block.  Since we are
 * finding line widths, a newline resets the width count.
 */
char *
MaxTextWidth (txt, cnt)
char *txt;
int *cnt;
{
  char *start;
  char *end;
  int width;

  if (txt == NULL)
  {
    return (NULL);
  }

  width = *cnt;
  start = txt;

  /*
   * If this blocks starts with a newline, reset the width
   * count, and skip the newline.
   */
  if (*start == '\n')
  {
    width = 0;
    start++;
  }

  end = start;

  /*
   * count characters, stoping either at a newline, or at the
   * end of this text block.  Expand tabs.
   */
  while ((*end != '\0') && (*end != '\n'))
  {
    if (*end == '\t')
    {
      width = ((width / 8) + 1) * 8;
    }
    else
    {
      width++;
    }
    end++;
  }

  *cnt = width;
  return (end);
}


/*
 * Free up the passed linked list of formatted elements, freeing
 * all memory associates with each element.
 */
void
FreeLineList (list)
struct ele_rec *list;
{
  struct ele_rec *current;
  struct ele_rec *eptr;

  current = list;
  while (current != NULL)
  {
    eptr = current;
    current = current->next;
    eptr->next = NULL;
    if (eptr->edata != NULL)
    {
      XtFree((char *) eptr->edata);
    }
    if (eptr->anchorHRef != NULL)
    {
      XtFree((char *) eptr->anchorHRef);
    }
    if (eptr->anchorName != NULL)
    {
      XtFree((char *) eptr->anchorName);
    }
    XtFree((char *) eptr);
  }
}


/*
 * Add an element to the linked list of formatted elements.
 * return a pointer to the current (end) position in the list.
 */
struct ele_rec *
AddEle (elistp, current, eptr)
struct ele_rec **elistp;
struct ele_rec *current;
struct ele_rec *eptr;
{
  if (eptr == NULL)
  {
    return (current);
  }

  /*
   * Add object to either the head of the list for a new list,
   * or at the end after the current pointer.
   */
  if (*elistp == NULL)
  {
    *elistp = eptr;
    (*elistp)->next = NULL;
    (*elistp)->prev = NULL;
    current = *elistp;
  }
  else
  {
    current->next = eptr;
    eptr->prev = current;
    current = current->next;
    current->next = NULL;
  }
  return (current);
}


/*
 * Contruct and return an array of pointers into the element list that
 * indexes the elements by line number.
 * Note, lines containing only while space will have NULL pointers
 * into the element list.
 */
struct ele_rec **
MakeLineList (elist, max_line)
struct ele_rec *elist;
int max_line;
{
  int i;
  struct ele_rec *eptr;
  struct ele_rec **ll;

  /*
   * malloc index array
   */
  ll = (struct ele_rec **)XtMalloc(sizeof (struct ele_rec *) * max_line);

  /*
   * zero the index array
   */
  for (i = 0; i < max_line; i++)
  {
    ll[i] = NULL;
  }

  /*
   * fill in pointers to beginning of the lines
   */
  eptr = elist;
  while (eptr != NULL)
  {
    if (eptr->line_number > max_line)
    {
      break;
    }

    if (ll[eptr->line_number - 1] == NULL)
    {
      ll[eptr->line_number - 1] = eptr;
    }

    eptr = eptr->next;
  }
  return (ll);
}


/*
 * Passed in 2 element pointers, and element positions.
 * Function should return 1 if if start occurs before end.
 * Otherwise return 0.
 */
int
ElementLessThan (start, end, start_pos, end_pos)
struct ele_rec *start;
struct ele_rec *end;
int start_pos, end_pos;
{
  struct ele_rec *current;

  /*
   * Deal with start or end being NULL
   */
  if ((start == NULL) && (end == NULL))
  {
    return (0);
  }
  else if ((start == NULL) && (end != NULL))
  {
    return (1);
  }
  else if ((start != NULL) && (end == NULL))
  {
    return (0);
  }

  /*
   * Deal with easy identical case
   */
  if (start == end)
  {
    if (start_pos < end_pos)
    {
      return (1);
    }
    else
    {
      return (0);
    }
  }

  /*
   * We know element Ids are always equal or increasing within a
   * list.
   */
  if (start->ele_id < end->ele_id)
  {
    return (1);
  }
  else if (start->ele_id == end->ele_id)
  {
    current = start;
    while (current != NULL)
    {
      if (current->ele_id != start->ele_id)
      {
	break;
      }
      else if (current == end)
      {
	break;
      }
      current = current->next;
    }
    if (current == end)
    {
      return (1);
    }
    else
    {
      return (0);
    }
  }
  else
  {
    return (0);
  }
}


/*
 * Passed in 2 element pointers, and element positions.
 * Function should return 1 if they need to be swapped in order for then
 * to proceed left to right and top to bottom in the text.
 * Otherwise return 0.
 */
int
SwapElements (start, end, start_pos, end_pos)
struct ele_rec *start;
struct ele_rec *end;
int start_pos, end_pos;
{
  struct ele_rec *current;

  /*
   * Deal with start or end being NULL
   */
  if ((start == NULL) && (end == NULL))
  {
    return (0);
  }
  else if ((start == NULL) && (end != NULL))
  {
    return (1);
  }
  else if ((start != NULL) && (end == NULL))
  {
    return (0);
  }

  /*
   * Deal with easy identical case
   */
  if (start == end)
  {
    if (start_pos > end_pos)
    {
      return (1);
    }
    else
    {
      return (0);
    }
  }

  /*
   * We know element Ids are always equal or increasing within a
   * list.
   */
  if (start->ele_id < end->ele_id)
  {
    return (0);
  }
  else if (start->ele_id == end->ele_id)
  {
    current = start;
    while (current != NULL)
    {
      if (current->ele_id != start->ele_id)
      {
	break;
      }
      else if (current == end)
      {
	break;
      }
      current = current->next;
    }
    if (current == end)
    {
      return (0);
    }
    else
    {
      return (1);
    }
  }
  else
  {
    return (1);
  }
}


/*
 * Free up the allocated list of internal hrefs.
 */
void
FreeHRefs (list)
struct ref_rec *list;
{
  struct ref_rec *hptr;
  struct ref_rec *tptr;

  hptr = list;
  while (hptr != NULL)
  {
    tptr = hptr;
    hptr = hptr->next;
    if (tptr->anchorHRef != NULL)
    {
      XtFree((char *) tptr->anchorHRef);
    }
    XtFree((char *) tptr);
  }
}


/*
 * Find an element in the linked list of Internal HREFS.
 * return a pointer to the element, or NULL if not found.
 */
struct ref_rec *
FindHRef (list, href)
struct ref_rec *list;
char *href;
{
  struct ref_rec *hptr;

  if (href == NULL)
  {
    return (NULL);
  }

  hptr = list;
  while (hptr != NULL)
  {
    if (strcmp (hptr->anchorHRef, href) == 0)
    {
      break;
    }
    hptr = hptr->next;
  }
  return (hptr);
}


/*
 * Add an element to the linked list of Internal HREFS we
 * have visited before.
 * return a pointer to the head of the new list.
 */
struct ref_rec *
AddHRef (list, href)
struct ref_rec *list;
char *href;
{
  struct ref_rec *hptr;

  if (href == NULL)
  {
    return (list);
  }

  hptr = FindHRef (list, href);

  if (hptr == NULL)
  {
    hptr = (struct ref_rec *)XtMalloc(sizeof(struct ref_rec));
    hptr->anchorHRef = XtNewString(href);
    hptr->next = list;
    list = hptr;
  }

  return (list);
}


/*
 * Free up the allocated list of visited delayed images
 */
void
FreeDelayedImages (list)
struct delay_rec *list;
{
  struct delay_rec *iptr;
  struct delay_rec *tptr;

  iptr = list;
  while (iptr != NULL)
  {
    tptr = iptr;
    iptr = iptr->next;
    if (tptr->src != NULL)
    {
      XtFree((char *) tptr->src);
    }
    XtFree((char *) tptr);
  }
}


/*
 * Find an element in the linked list of visited delayed images.
 * return a pointer to the element, or NULL if not found.
 */
struct delay_rec *
FindDelayedImage (list, src)
struct delay_rec *list;
char *src;
{
  struct delay_rec *iptr;

  if (src == NULL)
  {
    return (NULL);
  }

  iptr = list;
  while (iptr != NULL)
  {
    if (strcmp (iptr->src, src) == 0)
    {
      break;
    }
    iptr = iptr->next;
  }
  return (iptr);
}


/*
 * Add an element to the linked list of visited delayed images.
 * return a pointer to the head of the new list.
 */
struct delay_rec *
AddDelayedImage (list, src)
struct delay_rec *list;
char *src;
{
  struct delay_rec *iptr;

  if (src == NULL)
  {
    return (list);
  }

  iptr = FindDelayedImage (list, src);

  if (iptr == NULL)
  {
    iptr = (struct delay_rec *)XtMalloc(sizeof (struct delay_rec));
    iptr->src = XtNewString(src);
    iptr->next = list;
    list = iptr;
  }

  return (list);
}
