/****************************************************************************
 * 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 "mosaic.h"
#include "libhtmlw/HTML.h"

extern char *cached_url;
extern int binary_transfer;
extern char *startup_document, *home_document;
extern Display *dsp;
extern char reloading;

int loading_inlined_images = 0;
char *url_base_override = NULL;
int interrupted = 0;

extern char *mo_post_pull_er_over (char *url, char *content_type, 
                                   char *post_data, 
                                   char **texthead);

/****************************************************************************
 * name:    mo_snarf_scrollbar_values
 * purpose: Store current viewing state in the current node, in case
 *          we want to return to the same location later.
 * inputs:  
 *   - mo_window *win: Current window.
 * returns: 
 *   mo_succeed
 *   (mo_fail if no current node exists)
 * remarks: 
 *   Snarfs current docid position in the HTML widget.
 ****************************************************************************/
static mo_status mo_snarf_scrollbar_values (mo_window *win)
{
  /* Make sure we have a node. */
  if (!win->current_node)
    return mo_fail;

  win->current_node->docid = HTMLPositionToId(win->scrolled_win, 0, 3);

  /* Do the cached stuff thing. */
  win->current_node->cached_stuff = HTMLGetWidgetInfo (win->scrolled_win);

  return mo_succeed;
}


/* ---------------------- mo_reset_document_headers ----------------------- */

static mo_status mo_reset_document_headers (mo_window *win)
{
  if (win->current_node)
    {
      XmxTextSetString (win->title_text, win->current_node->title);
      XmxTextSetString (win->url_text, win->current_node->url);
    }

  return mo_succeed;
}

/* --------------------------- mo_back_possible --------------------------- */

/* This could be cached, but since it shouldn't take too long... */
static void mo_back_possible (mo_window *win)
{
  XmxSetSensitive (win->back_button, XmxSensitive);
  XmxRSetSensitive (win->menubar, mo_back, XmxSensitive);
  return;
}


/****************************************************************************
 * name:    mo_back_impossible
 * purpose: Can't go back (nothing in the history list).
 ****************************************************************************/
mo_status mo_back_impossible (mo_window *win)
{
  XmxSetSensitive (win->back_button, XmxNotSensitive);
  XmxRSetSensitive (win->menubar, mo_back, XmxNotSensitive);
  return mo_succeed;
}

static void mo_forward_possible (mo_window *win)
{
  XmxSetSensitive (win->forward_button, XmxSensitive);
  XmxRSetSensitive (win->menubar, mo_forward, XmxSensitive);
  return;
}


/****************************************************************************
 * name:    mo_forward_impossible
 * purpose: Can't go forward (nothing in the history list).
 ****************************************************************************/
mo_status mo_forward_impossible (mo_window *win)
{
  XmxSetSensitive (win->forward_button, XmxNotSensitive);
  XmxRSetSensitive (win->menubar, mo_forward, XmxNotSensitive);
  return mo_succeed;
}

/* ---------------------- mo_annotate_edit_possible ----------------------- */

static void mo_annotate_edit_possible (mo_window *win)
{
  XmxRSetSensitive (win->menubar, mo_annotate_edit, XmxSensitive);
  XmxRSetSensitive (win->menubar, mo_annotate_delete, XmxSensitive);
  return;
}

static void mo_annotate_edit_impossible (mo_window *win)
{
  XmxRSetSensitive (win->menubar, mo_annotate_edit, XmxNotSensitive);
  XmxRSetSensitive (win->menubar, mo_annotate_delete, XmxNotSensitive);
  return;
}


/* ------------------------------------------------------------------------ */

static void mo_set_text (Widget w, char *txt, char *ans, int id, 
                         char *target_anchor, void *cached_stuff)
{
  /* Any data transfer that takes place in here must be inlined
     image loading. */
  loading_inlined_images = 1;
  interrupted = 0;
  mo_set_image_cache_nuke_threshold ();
  if (Rdata.annotations_on_top)
    HTMLSetText (w, txt, ans ? ans : "\0", "\0", id, target_anchor, 
                 cached_stuff);
  else
    HTMLSetText (w, txt, "\0", ans ? ans : "\0", id, target_anchor, 
                 cached_stuff);
  loading_inlined_images = 0;
  interrupted = 0;
  mo_gui_done_with_icon ();
}


/****************************************************************************
 * name:    mo_do_window_text (PRIVATE)
 * purpose: Set a window's text and do lots of other housekeeping
 *          and GUI-maintenance things.
 * inputs:  
 *   - mo_window *win: The current window.
 *   - char      *url: The URL for the text; assumed to be canonicalized
 *                     and otherwise ready for inclusion in history lists,
 *                     the window's overhead URL display, etc.
 *   - char      *txt: The new text for the window.
 *   - char  *txthead: The start of the malloc'd block of text corresponding
 *                     to txt.
 *   - int register_visit: If TRUE, then this text should be registerd
 *                         as a new node in the history list.  If FALSE,
 *                         then we're just moving around in the history list.
 *   - char      *ref: Reference (possible title) for this text.
 * returns: 
 *   mo_succeed
 * remarks: 
 *   This is the mother of all functions in Mosaic.  Probably should be
 *   rethought and broken down.
 ****************************************************************************/
/*static */
mo_status mo_do_window_text (mo_window *win, char *url, char *txt,
                                    char *txthead,
                                    int register_visit, char *ref)
{
  char /**line,*/ *ans;

/************************************/
/* send document over cci if needed */
 if (txt != NULL)
 	MoCCISendBrowserViewOutput(url, "text/html", txt, strlen(txt));
/************************************/



  mo_set_current_cached_win (win);

  if (Rdata.track_pointer_motion)
    {
      XmString xmstr = XmStringCreateLtoR (" ", XmSTRING_DEFAULT_CHARSET);
      XtVaSetValues
        (win->tracker_label,
         XmNlabelString, (XtArgVal)xmstr,
         NULL);
      XmStringFree (xmstr);
    }
      
  /* If !register_visit, we're just screwing around with current_node
     already, so don't bother snarfing scrollbar values. */
  if (register_visit)
    mo_snarf_scrollbar_values (win);

  /* cached_url HAS to be set here, since Resolve counts on it. */
  cached_url = mo_url_canonicalize (url, "");
  win->cached_url = cached_url;

  mo_here_we_are_son (url);
  
  {
    /* Since mo_fetch_annotation_links uses the communications code,
       we need to play games with binary_transfer. */
    int tmp = binary_transfer;
    binary_transfer = 0;
    ans = mo_fetch_annotation_links (url,
                                     Rdata.annotations_on_top);
#ifdef L10N
    mo_ml_codeconv(win, ans);
#endif /* L10N */
    binary_transfer = tmp;
  }

  /* If there is a BASE tag in the document that contains a "real"
     URL, this will be non-NULL by the time we exit and base_callback
     will have been called. */
  url_base_override = NULL;

  {
    int id = 0, freeta = 0;
    void *cached_stuff = NULL;
    char *target_anchor = win->target_anchor;

    if ((!register_visit) && win->current_node)
      {
        id = win->current_node->docid;
        cached_stuff = win->current_node->cached_stuff;
      }

    /* If the window doesn't have a target anchor already,
       see if there's one in this node's URL. */
    if ((!target_anchor || !(*target_anchor)) && win->current_node)
      {
        target_anchor = mo_url_extract_anchor (win->current_node->url);
        freeta = 1;
      }

    if (!txt || !txthead)
      {
#if 0
        txt = strdup ("<h1>Uh oh, I'm stumped</h1> Mosaic has encountered a situation in which it doesn't know what to do.  Please back up and try again, or send a bug report including full details about what you're trying to do to <b>mosaic-x@ncsa.uiuc.edu</b>.  We thank you for your support.");
#endif
        /* Just make it look OK...  band-aid city. */
        txt = strdup ("\0");
        txthead = txt;
      }

    mo_set_text (win->scrolled_win, txt, ans, id, target_anchor,
                 cached_stuff);

    if (win->target_anchor)
      free (win->target_anchor);

    win->target_anchor = NULL;

    if (freeta)
      free (target_anchor);
  }

  if (url_base_override)
    {
      /* Get the override URL -- this should be all we need to do here. */
      url = url_base_override;
      mo_here_we_are_son (url);
    }

  /* Every time we view the document, we reset the search_start
     struct so searches will start at the beginning of the document. */
  ((ElementRef *)win->search_start)->id = 0;

  /* CURRENT_NODE ISN'T SET UNTIL HERE (assuming register_visit is 1). */
  /* Now that WbNtext has been set, we can pull out WbNtitle. */
  /* First, check and see if we have a URL.  If not, we probably
     are only jumping around inside a document. */
  if (url && *url)
    {
      if (register_visit)
        mo_record_visit (win, url, txt, txthead, ref);
      else
        {
          /* At the very least we want to pull out the new title,
             if one exists. */
          if (win->current_node)
            {
              if (win->current_node->title)
                free (win->current_node->title);
              win->current_node->title = mo_grok_title (win, url, ref);
            }
        }
    }
  
  mo_reset_document_headers (win);

  if (win->history_list && win->current_node)
    {
      XmListSelectPos (win->history_list, win->current_node->position, False);
      XmListSetBottomPos (win->history_list, win->current_node->position);
    }

  /* Update source text if necessary. */
  if (win->source_text && XtIsManaged(win->source_text) &&
      win->current_node)
    {
      XmxTextSetString (win->source_text, win->current_node->text);
      XmxTextSetString (win->source_url_text, win->current_node->url);
    }

  if (win->current_node && win->current_node->previous != NULL)
    mo_back_possible (win);
  else
    mo_back_impossible (win);
  
  if (win->current_node && win->current_node->next != NULL)
    mo_forward_possible (win);
  else
    mo_forward_impossible (win);

  if (win->current_node && 
      mo_is_editable_annotation (win, win->current_node->text))
    mo_annotate_edit_possible (win);
  else
    mo_annotate_edit_impossible (win);

  mo_not_busy ();

  return mo_succeed;
}


/****************************************************************************
 * name:    mo_set_win_current_node
 * purpose: Given a window and a node, set the window's current node.
 *          This assumes node is already all put together, in the history
 *          list for the window, etc.
 * inputs:  
 *   - mo_window *win: The current window.
 *   - mo_node  *node: The node to use.
 * returns: 
 *   Result of calling mo_do_window_text.
 * remarks: 
 *   This routine is meant to be used to move forward, backward,
 *   and to arbitrarily locations in the history list.
 ****************************************************************************/
mo_status mo_set_win_current_node (mo_window *win, mo_node *node)
{
  void *to_free = NULL;
  mo_status r;
  
  mo_snarf_scrollbar_values (win);

  if (win->current_node && win->reloading)
    {
      to_free = win->current_node->cached_stuff;

      win->current_node->cached_stuff = NULL;
    }

  win->current_node = node;

#ifdef L10N
  if ((Rdata.keep_document_charset) && (node->font_size != win->font_size)) {
      mo_set_fonts(win, node->font_size);
  }
#endif /* L10N */

  mo_busy ();
  mo_set_current_cached_win (win);

  /********* Send Anchor history to CCI if CCI wants it */
  MoCCISendAnchorToCCI(win->current_node->url, 0);
  /*****************************************************/

  r = mo_do_window_text (win, win->current_node->url, 
                         win->current_node->text, 
                         win->current_node->texthead,
                         FALSE, win->current_node->ref);

  if (win->reloading)
    {
      if (to_free)
        HTMLFreeWidgetInfo (to_free);
      
      win->reloading = 0;
    }

  return r;
}


/****************************************************************************
 * name:    mo_reload_window_text
 * purpose: Reload the current window's text by pulling it over the
 *          network again.
 * inputs:  
 *   - mo_window *win: The current window.
 * returns: 
 *   mo_succeed
 * remarks: 
 *   This frees the current window's texthead.  This calls mo_pull_er_over
 *   directly, and needs to be smarter about handling HDF, etc.
 ****************************************************************************/
mo_status mo_reload_window_text (mo_window *win, int reload_images_also)
{
  mo_busy ();

  mo_set_current_cached_win (win);

  /* Uh oh, this is trouble... */
  if (!win->current_node)
    return mo_load_window_text 
      (win, startup_document ? startup_document : home_document, NULL);

  /* Free all images in the current document. */
  if (Rdata.reload_reloads_images || reload_images_also)
    mo_zap_cached_images_here (win);

  /* Free the current document's text. */
  /* REALLY we shouldn't do this until we've verified we have new text that's
     actually good here -- for instance, if we have a document on display,
     then go to binary transfer mode, then do reload, we should pick up the
     access override here and keep the old copy up on screen. */
  if (win->current_node->texthead != NULL)
    {
      free (win->current_node->texthead);
      win->current_node->texthead = NULL;
    }

  /* Set binary_transfer as per current window. */
  binary_transfer = win->binary_transfer;
  mo_set_current_cached_win (win);
  interrupted = 0;

  if (Rdata.reload_pragma_no_cache) {
    reloading=1;
  }
  win->current_node->text = mo_pull_er_over (win->current_node->url, 
                                             &win->current_node->texthead);
  reloading=0;

  {
    /* Check use_this_url_instead from HTAccess.c. */
    /* IS THIS GOOD ENOUGH FOR THIS CASE??? */
    extern char *use_this_url_instead;
    if (use_this_url_instead)
      {
        win->current_node->url = use_this_url_instead;
      }
  }
#ifdef HAVE_HDF
  if (win->current_node->text && 
      strncmp (win->current_node->text, "<mosaic-internal-reference", 26) == 0)
    {
      char *text = mo_decode_internal_reference 
        (win->current_node->url, win->current_node->text,
         mo_url_extract_anchor (win->current_node->url));
      win->current_node->text = text;
      win->current_node->texthead = text;
    }
#endif 
  
  /* Clear out the cached stuff, if any exists. */
  win->reloading = 1;

#ifdef L10N
  /* do code conversion */
  mo_ml_codeconv(win, win->current_node->text);
  win->current_node->font_size = win->font_size;
#endif /* L10N */

  /*
   * Reload should force a cache refresh on a proxy
   *       -- Ari L. <luotonen@dxcern.cern.ch>
   *       T.Takinaka
   */
  if (Rdata.reload_pragma_no_cache) {
    reloading = 1;
  }
  mo_set_win_current_node (win, win->current_node);
  reloading = 0;

  win->reloading = 0;

  return mo_succeed;
}



/****************************************************************************
 * name:    mo_refresh_window_text
 * purpose: Reload the current window's text without pulling it over the net.
 * inputs:  
 *   - mo_window *win: The current window.
 * returns: 
 *   mo_succeed
 * remarks: 
 ****************************************************************************/
mo_status mo_refresh_window_text (mo_window *win)
{
  mo_busy ();

  mo_set_current_cached_win (win);

  if (!win->current_node)
    {
      mo_not_busy ();
      return mo_fail;
    }

  /* Clear out the cached stuff, if any exists. */
  win->reloading = 1;

  mo_set_win_current_node (win, win->current_node);

  mo_not_busy ();

  return mo_succeed;
}



/****************************************************************************
 * name:    mo_load_window_text
 * purpose: Given a window and a raw URL, load the window.  The window
 *          is assumed to already exist with a document inside, etc.
 * inputs:  
 *   - mo_window *win: The current window.
 *   - char      *url: The URL to load.
 *   - char      *ref: The reference ("parent") URL.
 *         NOTE: actually, the ref field is the citation hypertext - AMB
 * returns: 
 *   mo_succeed
 * remarks: 
 *   This is getting ugly.
 ****************************************************************************/
mo_status mo_load_window_text (mo_window *win, char *url, char *ref)
{
  char *newtext = NULL, *newtexthead = NULL;
  mo_busy ();

  win->target_anchor = mo_url_extract_anchor (url);

  /* If we're just referencing an anchor inside a document,
     do the right thing. */
  if (url && *url == '#')
    {
      /* Now we make a copy of the current text and make sure we ask
         for a new mo_node and entry in the history list. */
      /* IF we're not dealing with an internal reference. */
      if (strncmp (url, "#hdfref;", 8) &&
          strncmp (url, "#hdfdtm;", 8))
        {
          if (win->current_node)
            {
              newtext = strdup (win->current_node->text);
              newtexthead = newtext;
            }
          else
            {
              newtext = strdup ("lose");
              newtexthead = newtext;
            }
        }
      url = mo_url_canonicalize_keep_anchor 
        (url, win->current_node ? win->current_node->url : "");
      /********* Send Anchor history to CCI if CCI wants it */
      MoCCISendAnchorToCCI(url, 1);
      /*****************************************************/
    }
  else
    {
      /* Get a full address for this URL. */
      /* Under some circumstances we may not have a current node yet
         and may wish to just run with it... so check for that. */
      if (win->current_node && win->current_node->url)
        {
          url = mo_url_canonicalize_keep_anchor (url, win->current_node->url);
        }

      /* Set binary_transfer as per current window. */
      binary_transfer = win->binary_transfer;
      mo_set_current_cached_win (win);

      {
        char *canon = mo_url_canonicalize (url, "");
        interrupted = 0;

	/********* Send Anchor history to CCI if CCI wants it */
	MoCCISendAnchorToCCI(canon,1);
	/*****************************************************/
        newtext = mo_pull_er_over (canon, &newtexthead);
        free (canon);
      }

      {
        /* Check use_this_url_instead from HTAccess.c. */
        extern char *use_this_url_instead;
        if (use_this_url_instead)
          {
            mo_here_we_are_son (url);
            url = use_this_url_instead;
            
            /* Go get another target_anchor. */
            if (win->target_anchor)
              free (win->target_anchor);
            win->target_anchor = mo_url_extract_anchor (url);
          }
      }
    }

#ifdef HAVE_HDF
  /* If a target anchor exists, and if it's an HDF reference, then
     go decode the HDF reference and call mo_do_window_text on the
     resulting text. */
  if (win->target_anchor &&
      strncmp (win->target_anchor, "hdfref;", 7) == 0 &&
      strlen (win->target_anchor) > 8)
    {
      char *text;
      text = (char *)mo_decode_hdfref (url, win->target_anchor);
      {
        /* Check use_this_url_instead from HTAccess.c. */
        extern char *use_this_url_instead;
        if (use_this_url_instead)
          {
            mo_here_we_are_son (url);
            url = use_this_url_instead;
            mo_load_window_text(win, url, ref);
            return;
          }
      }
      mo_do_window_text (win, url, text, text, 1, ref);
    }
  /* An hdfdtm reference means that we should blast the referenced
     HDF data object over the output DTM port to Collage.  Currently
     this can only be an image; in the future we'll do SDS's, etc. */
  else if (win->target_anchor &&
           strncmp (win->target_anchor, "hdfdtm;", 7) == 0 &&
           strlen (win->target_anchor) > 8)
    {
#ifdef HAVE_DTM
      /* We specifically want to make sure that the anchor is allowed
         to stay in the URL, so we don't canonicalize to strip it out. */
      mo_do_hdf_dtm_thang (url, &(win->target_anchor[7]));
#endif

      if (win->target_anchor)
        free (win->target_anchor);
      win->target_anchor = NULL;

      mo_gui_done_with_icon ();
      mo_not_busy ();
    }
  /* Assuming we have HDF, the only thing mosaic-internal-reference
     currently can be is pointer to an HDF file. */
  else if (newtext && strncmp (newtext, "<mosaic-internal-reference", 26) == 0)
    {
      char *text;
      text = mo_decode_internal_reference (url, newtext, win->target_anchor);
      mo_do_window_text (win, url, text, text, 1, ref);
    }
  else
#endif
  /* Now, if it's a telnet session, there should be no need
     to do anything else.  Also check for override in text itself. */
  if (strncmp (url, "telnet:", 7) == 0 || strncmp (url, "tn3270:", 7) == 0 ||
      strncmp (url, "rlogin:", 7) == 0 ||
      (newtext && strncmp (newtext, "<mosaic-access-override>", 24) == 0))
    {
      /* We don't need this anymore. */
      free (newtext);

      /* We still want a global history entry but NOT a 
         window history entry. */
      mo_here_we_are_son (url);
      /* ... and we want to redisplay the current window to
         get the effect of the history entry today, not tomorrow. */
      mo_redisplay_window (win);
      /* We're not busy anymore... */
      mo_gui_done_with_icon ();
      mo_not_busy ();
    }
  else if (newtext)
    {
      /* Not a telnet session and not an override, but text present
         (the "usual" case): */

#ifdef L10N
      /* do code conversion */
      mo_ml_codeconv(win, newtext);
#endif /* L10N */
      /* Set the window text. */
      mo_do_window_text (win, url, newtext, newtexthead, 1, ref);
    }
  else
    {
      /* No text at all. */
      mo_gui_done_with_icon ();
      mo_not_busy ();
    }

/********* Send Anchor history to CCI if CCI wants it */
  MoCCISendAnchorToCCI(url,2);
/*********************************************/
/*
 outtahere:
*/
  return mo_succeed;
}


mo_status mo_post_load_window_text (mo_window *win, char *url, 
                                    char *content_type, char *post_data, 
                                    char *ref)
{
  char *newtext = NULL, *newtexthead = NULL;
  mo_busy ();

  win->target_anchor = mo_url_extract_anchor (url);

  /* If we're just referencing an anchor inside a document,
     do the right thing. */
  if (url && *url == '#')
    {
      /* Now we make a copy of the current text and make sure we ask
         for a new mo_node and entry in the history list. */
      /* IF we're not dealing with an internal reference. */
      if (strncmp (url, "#hdfref;", 8) &&
          strncmp (url, "#hdfdtm;", 8))
        {
          if (win->current_node)
            {
              newtext = strdup (win->current_node->text);
              newtexthead = newtext;
            }
          else
            {
              newtext = strdup ("lose");
              newtexthead = newtext;
            }
        }
      url = mo_url_canonicalize_keep_anchor 
        (url, win->current_node ? win->current_node->url : "");
    }
  else
    {
      /* Get a full address for this URL. */
      /* Under some circumstances we may not have a current node yet
         and may wish to just run with it... so check for that. */
      if (win->current_node && win->current_node->url)
        {
          url = mo_url_canonicalize_keep_anchor (url, win->current_node->url);
        }

      /* Set binary_transfer as per current window. */
      binary_transfer = win->binary_transfer;
      mo_set_current_cached_win (win);

      {
        char *canon = mo_url_canonicalize (url, "");
        interrupted = 0;

        newtext = mo_post_pull_er_over (canon, content_type, post_data, 
                                        &newtexthead);
        free (canon);
      }

      {
        /* Check use_this_url_instead from HTAccess.c. */
        extern char *use_this_url_instead;
        if (use_this_url_instead)
          {
            mo_here_we_are_son (url);
            url = use_this_url_instead;
          }
      }
    }

#ifdef HAVE_HDF
  /* If a target anchor exists, and if it's an HDF reference, then
     go decode the HDF reference and call mo_do_window_text on the
     resulting text. */
  if (win->target_anchor &&
      strncmp (win->target_anchor, "hdfref;", 7) == 0 &&
      strlen (win->target_anchor) > 8)
    {
      char *text;
      text = (char *)mo_decode_hdfref (url, win->target_anchor);
      {
        /* Check use_this_url_instead from HTAccess.c. */
        extern char *use_this_url_instead;
        if (use_this_url_instead)
          {
            mo_here_we_are_son (url);
            url = use_this_url_instead;
            mo_load_window_text(win, url, ref);
            return;
          }
      }
      mo_do_window_text (win, url, text, text, 1, ref);
    }
  /* An hdfdtm reference means that we should blast the referenced
     HDF data object over the output DTM port to Collage.  Currently
     this can only be an image; in the future we'll do SDS's, etc. */
  else if (win->target_anchor &&
           strncmp (win->target_anchor, "hdfdtm;", 7) == 0 &&
           strlen (win->target_anchor) > 8)
    {
#ifdef HAVE_DTM
      /* We specifically want to make sure that the anchor is allowed
         to stay in the URL, so we don't canonicalize to strip it out. */
      mo_do_hdf_dtm_thang (url, &(win->target_anchor[7]));
#endif

      if (win->target_anchor)
        free (win->target_anchor);
      win->target_anchor = NULL;

      mo_gui_done_with_icon ();
      mo_not_busy ();
    }
  /* Assuming we have HDF, the only thing mosaic-internal-reference
     currently can be is pointer to an HDF file. */
  else if (newtext && strncmp (newtext, "<mosaic-internal-reference", 26) == 0)
    {
      char *text;
      text = mo_decode_internal_reference (url, newtext, win->target_anchor);
      mo_do_window_text (win, url, text, text, 1, ref);
    }
  else
#endif
  /* Now, if it's a telnet session, there should be no need
     to do anything else.  Also check for override in text itself. */
  if (strncmp (url, "telnet:", 7) == 0 || strncmp (url, "tn3270:", 7) == 0 ||
      strncmp (url, "rlogin:", 7) == 0 ||
      (newtext && strncmp (newtext, "<mosaic-access-override>", 24) == 0))
    {
      /* We don't need this anymore. */
      free (newtext);

      /* We still want a global history entry but NOT a 
         window history entry. */
      mo_here_we_are_son (url);
      /* ... and we want to redisplay the current window to
         get the effect of the history entry today, not tomorrow. */
      mo_redisplay_window (win);
      /* We're not busy anymore... */
      mo_gui_done_with_icon ();
      mo_not_busy ();
    }
  else if (newtext)
    {
      /* Not a telnet session and not an override, but text present
         (the "usual" case): */

#ifdef L10N
      /* do code conversion */
      mo_ml_codeconv(win, newtext);
#endif /* L10N */
      /* Set the window text. */
      mo_do_window_text (win, url, newtext, newtexthead, 1, ref);
    }
  else
    {
      /* No text at all. */
      mo_gui_done_with_icon ();
      mo_not_busy ();
    }
/*
 outtahere:
*/
  return mo_succeed;
}





/****************************************************************************
 * name:    mo_duplicate_window_text
 * purpose: Given an old window and a new window, make a copy of the text
 *          in the old window and install it in the new window.
 * inputs:  
 *   - mo_window *oldw: The old window.
 *   - mo_window *neww: The new window.
 * returns: 
 *   mo_succeed
 * remarks: 
 *   This is how windows are cloned: a new window is created and this
 *   call sets up its contents.
 ****************************************************************************/
mo_status mo_duplicate_window_text (mo_window *oldw, mo_window *neww)
{
  /* We can get away with just cloning text here and forgetting
     about texthead, obviously, since we're making a new copy. */
  char *newtext;

  if (!oldw->current_node)
    return mo_fail;

  newtext = strdup (oldw->current_node->text);

  mo_do_window_text 
    (neww, strdup (oldw->current_node->url), 
     newtext, newtext, TRUE, 
     oldw->current_node->ref ? strdup (oldw->current_node->ref) : NULL);

  return mo_succeed;
}


/****************************************************************************
 * name:    mo_access_document
 * purpose: Given a URL, access the document by loading the current 
 *          window's text.
 * inputs:  
 *   - mo_window *win: The current window.
 *   - char      *url: URL to access.
 * returns: 
 *   mo_succeed
 * remarks: 
 *   This should be the standard call for accessing a document.
 ****************************************************************************/
mo_status mo_access_document (mo_window *win, char *url)
{
  mo_busy ();

  mo_set_current_cached_win (win);

  mo_load_window_text (win, url, NULL);

  return mo_succeed;
}

#ifdef L10N

/* --------------- multilingual code convert routine --------------- */

/* codeset status */
#define ASCII		0
#define MULTIBYTE	1

#define ESC		0x1b
#define SO		0x0e
#define SI		0x0f

#define IS_EUC(c)	((0xa1 <= (c)) && ((c) <= 0xfe))
#define IS_SJIS1(c)	(((0x81 <= (c)) && ((c) <= 0x9f)) || \
			 ((0xe0 <= (c)) && ((c) <= 0xfc)))
#define IS_SJIS2(c)	((0x40 <= (c)) && ((c) <= 0xfc) && ((c) != 0x7f))

#define E2E(i1, i2, o1, o2) {(o1) = (i1); (o2) = (i2);}
#define I2E(i1, i2, o1, o2) {(o1) = ((i1) | 0x80); (o2) = ((i2) | 0x80);}
#define S2E(i1, i2, o1, o2) {\
    if ((i1) >= 0xe0) { (i1) -= (0xe0 - 0xa0); }\
    if ((i2) >= 0x80) { (i2)--; }\
    if ((i2) < 0x40 + 94) {\
	(o1) = (((((i1) - 0x81) * 2) + 0x21) | 0x80);\
	(o2) = (((i2) - (0x40 - 0x21)) | 0x80);\
    } else {\
	(o1) = (((((i1) - 0x81) * 2) + 0x21 + 1) | 0x80);\
	(o2) = (((i2) - (0x9e - 0x21)) | 0x80);\
    }\
}

/*
 * code convertion main routine
 */
mo_ml_codeconv(win, str)
     mo_window *win;
     char *str;
{
    char *in = str;
    char *out = in;
    int codeset = ASCII;
    unsigned char c1, c2, c3, c4;
    static int sjiseuc=0;
    int eucflag,sjisflag;

    if (!in) return;
    while (c1 = *in++) {
	if (c1 == ESC) {
	    if (c2 = *in++) {
		if (c2 == '$') {
		    if (c3 = *in++) {
			if ((c3 == '@') || (c3 == 'B')) {
			    codeset = MULTIBYTE;
			    if (win->font_size != mo_charset_jis) {
				mo_set_fonts(win, mo_charset_jis); }
			} else if (c3 == ')') {
			    if (c4 = *in++) {
				if (c4 == 'A') {
				    mo_set_fonts(win, mo_charset_gb);
				} else if (c4 == 'B') {
				    mo_set_fonts(win, mo_charset_jis);
				} else if (c4 == 'C') {
				    mo_set_fonts(win, mo_charset_ksc);
				} else {
				    *out++ = c1; *out++ = c2;
				    *out++ = c3; *out++ = c4; }
			    } else {
				*out++ = c1; *out++ = c2; *out++ = c3; break; }
			} else {
			    *out++ = c1; *out++ = c2; *out++ = c3; }
		    } else {
			*out++ = c1; *out++ = c2; break; }
		} else if (c2 == '(') {
		    if (c3 = *in++) {
			if ((c3 == 'B') || (c3 == 'J')) {
			    codeset = ASCII;
			} else {
			    *out++ = c1; *out++ = c2; *out++ = c3; }
		    } else {
			*out++ = c1; *out++ = c2; break; }
		} else if (c2 == '-') {
		    if (c3 = *in++) {
			if (c3 == 'A') {
			    mo_set_fonts(win, mo_regular_fonts);
			} else if (c3 == 'B') {
			    mo_set_fonts(win, mo_charset_iso_8859_2);
			} else if (c3 == 'C') {
			    mo_set_fonts(win, mo_charset_iso_8859_3);
			} else if (c3 == 'D') {
			    mo_set_fonts(win, mo_charset_iso_8859_4);
			} else if (c3 == 'L') {
			    mo_set_fonts(win, mo_charset_iso_8859_5);
			} else if (c3 == 'F') {
			    mo_set_fonts(win, mo_charset_iso_8859_7);
			} else if (c3 == 'H') {
			    mo_set_fonts(win, mo_charset_iso_8859_8);
			} else if (c3 == 'M') {
			    mo_set_fonts(win, mo_charset_iso_8859_9);
			} else {
			    *out++ = c1; *out++ = c2; *out++ = c3; }
		    } else {
			*out++ = c1; *out++ = c2; break; }
		} else {
		    *out++ = c1; *out++ = c2; }
	    } else {
		*out++ = c1; break; }
	} else if ((c1 == '~') && (win->font_size == mo_charset_hz)) {
	    if (c2 = *in++) {
		if (c2 == '{') {
		    codeset = MULTIBYTE;
		} else if (c2 == '}') {
		    codeset = ASCII;
		} else if (c2 == '~') {
		    *out++ = '~';
		} else if (c2 == '\n') {
		    /* ignore newline */
		} else {
		    *out++ = c1; *out++ = c2; }
	    } else {
		*out++ = c1; break; }
	} else if (c1 == SO) {
	    codeset = MULTIBYTE;
	} else if (c1 == SI) {
	    codeset = ASCII;
	} else if (c2 = *in++) {
	    eucflag = (IS_EUC(c1) && IS_EUC(c2));
	    sjisflag = (IS_SJIS1(c1) && IS_SJIS2(c2));
	    if (win->font_size != mo_charset_jis) {
		*out++ = c1;
		in--;
	    } else if (eucflag && sjisflag) {
		if (sjiseuc) {
		    S2E(c1, c2, *out++, *out++);
		} else {
		    E2E(c1, c2, *out++, *out++); }
	    } else if (sjisflag) {
		S2E(c1, c2, *out++, *out++);
		sjiseuc = 1;
	    } else if (eucflag) {
		E2E(c1, c2, *out++, *out++);
		sjiseuc = 0;
	    } else if (codeset == MULTIBYTE && c1 > 0x20) {
		I2E(c1, c2, *out++, *out++);
	    } else {
		*out++ = c1 & 0x7f;
		in--; }
	} else {
	    *out++ = c1; break; }
    }
    *out = 0;
}

/*
 * code convertion for News
 */
mo_news_codeconv(str)
     char *str;
{
    char *in = str;
    char *out = in;
    int codeset = ASCII;
    unsigned char c1, c2, c3, c4;
    static int sjiseuc=0;
    int eucflag,sjisflag;

    if (!in) return;

    while (c1 = *in++) {
	if (c1 == ESC) {
	    if (c2 = *in++) {
		if (c2 == '$') {
		    if (c3 = *in++) {
			if ((c3 == '@') || (c3 == 'B')) {
			    codeset = MULTIBYTE;
			} else if (c3 == ')') {
			    if (c4 = *in++) {
				if (c4 == 'A') {
				    ;
				} else if (c4 == 'B') {
				    ;
				} else if (c4 == 'C') {
				    ;
				} else {
				    *out++ = c1; *out++ = c2;
				    *out++ = c3; *out++ = c4; }
			    } else {
				*out++ = c1; *out++ = c2; *out++ = c3; break; }
			} else {
			    *out++ = c1; *out++ = c2; *out++ = c3; }
		    } else {
			*out++ = c1; *out++ = c2; break; }
		} else if (c2 == '(') {
		    if (c3 = *in++) {
			if ((c3 == 'B') || (c3 == 'J')) {
			    codeset = ASCII;
			} else {
			    *out++ = c1; *out++ = c2; *out++ = c3; }
		    } else {
			*out++ = c1; *out++ = c2; break; }
		} else if (c2 == '-') {
		    if (c3 = *in++) {
			if (c3 == 'A') {
			    ;
			} else if (c3 == 'B') {
			    ;
			} else if (c3 == 'C') {
			    ;
			} else if (c3 == 'D') {
			    ;
			} else if (c3 == 'L') {
			    ;
			} else if (c3 == 'F') {
			    ;
			} else if (c3 == 'H') {
			    ;
			} else if (c3 == 'M') {
			    ;
			} else {
			    *out++ = c1; *out++ = c2; *out++ = c3; }
		    } else {
			*out++ = c1; *out++ = c2; break; }
		} else {
		    *out++ = c1; *out++ = c2; }
	    } else {
		*out++ = c1; break; }
	} else if ((c1 == '~')) {
	    if (c2 = *in++) {
		if (c2 == '{') {
		    codeset = MULTIBYTE;
		} else if (c2 == '}') {
		    codeset = ASCII;
		} else if (c2 == '~') {
		    *out++ = '~';
		} else if (c2 == '\n') {
		    /* ignore newline */
		} else {
		    *out++ = c1; *out++ = c2; }
	    } else {
		*out++ = c1; break; }
	} else if (c1 == SO) {
	    codeset = MULTIBYTE;
	} else if (c1 == SI) {
	    codeset = ASCII;
	} else if (c2 = *in++) {
	    eucflag = (IS_EUC(c1) && IS_EUC(c2));
	    sjisflag = (IS_SJIS1(c1) && IS_SJIS2(c2));
	    if (eucflag && sjisflag) {
		if (sjiseuc) {
		    S2E(c1, c2, *out++, *out++);
		} else {
		    E2E(c1, c2, *out++, *out++); }
	    } else if (sjisflag) {
		S2E(c1, c2, *out++, *out++);
		sjiseuc = 1;
	    } else if (eucflag) {
		E2E(c1, c2, *out++, *out++);
		sjiseuc = 0;
	    } else if (codeset == MULTIBYTE && c1 > 0x20) {
		I2E(c1, c2, *out++, *out++);
	    } else {
		*out++ = c1;
		in--; }
	} else {
	    *out++ = c1; break; }
    }
    *out = 0;
}

#endif /* L10N */

mo_status mo_post_access_document (mo_window *win, char *url,
                                   char *content_type, char *post_data)
{
  mo_busy ();

  mo_set_current_cached_win (win);

  mo_post_load_window_text (win, url, content_type, post_data, NULL);

  return mo_succeed;
}
