Merge branch 'master' of https://git.maemo.org/projects/erwise master
authorToni Nikkanen <toni@tuug.fi>
Wed, 11 Mar 2009 16:21:53 +0000 (18:21 +0200)
committerToni Nikkanen <toni@tuug.fi>
Wed, 11 Mar 2009 16:21:53 +0000 (18:21 +0200)
122 files changed:
Cl/Cl.c [new file with mode: 0644]
Cl/Cl.h [new file with mode: 0644]
Cl/ClConnection.c [new file with mode: 0644]
Cl/ClFTP.c [new file with mode: 0644]
Cl/ClHTTP.c [new file with mode: 0644]
Cl/ClMisc.c [new file with mode: 0644]
Cl/Imakefile [new file with mode: 0644]
Cl/Makefile [new file with mode: 0644]
Cl/WWWLibrary/HTAccess.c [new file with mode: 0644]
Cl/WWWLibrary/HTAccess.h [new file with mode: 0644]
Cl/WWWLibrary/HTAnchor.c [new file with mode: 0644]
Cl/WWWLibrary/HTAnchor.h [new file with mode: 0644]
Cl/WWWLibrary/HTAtom.c [new file with mode: 0644]
Cl/WWWLibrary/HTAtom.h [new file with mode: 0644]
Cl/WWWLibrary/HTBrowse.h [new file with mode: 0644]
Cl/WWWLibrary/HTChunk.c [new file with mode: 0644]
Cl/WWWLibrary/HTChunk.h [new file with mode: 0644]
Cl/WWWLibrary/HTFTP.c [new file with mode: 0644]
Cl/WWWLibrary/HTFTP.h [new file with mode: 0644]
Cl/WWWLibrary/HTFile.c [new file with mode: 0644]
Cl/WWWLibrary/HTFile.h [new file with mode: 0644]
Cl/WWWLibrary/HTFont.h [new file with mode: 0644]
Cl/WWWLibrary/HTFormat.c [new file with mode: 0644]
Cl/WWWLibrary/HTFormat.h [new file with mode: 0644]
Cl/WWWLibrary/HTGopher.c [new file with mode: 0644]
Cl/WWWLibrary/HTGopher.h [new file with mode: 0644]
Cl/WWWLibrary/HTHistory.c [new file with mode: 0644]
Cl/WWWLibrary/HTHistory.h [new file with mode: 0644]
Cl/WWWLibrary/HTList.c [new file with mode: 0644]
Cl/WWWLibrary/HTList.h [new file with mode: 0644]
Cl/WWWLibrary/HTML.c [new file with mode: 0644]
Cl/WWWLibrary/HTML.h [new file with mode: 0644]
Cl/WWWLibrary/HTNews.c [new file with mode: 0644]
Cl/WWWLibrary/HTNews.h [new file with mode: 0644]
Cl/WWWLibrary/HTParse.c [new file with mode: 0644]
Cl/WWWLibrary/HTParse.h [new file with mode: 0644]
Cl/WWWLibrary/HTString.c [new file with mode: 0644]
Cl/WWWLibrary/HTString.h [new file with mode: 0644]
Cl/WWWLibrary/HTStyle.c [new file with mode: 0644]
Cl/WWWLibrary/HTStyle.h [new file with mode: 0644]
Cl/WWWLibrary/HTTCP.c [new file with mode: 0644]
Cl/WWWLibrary/HTTCP.h [new file with mode: 0644]
Cl/WWWLibrary/HTTP.c [new file with mode: 0644]
Cl/WWWLibrary/HTTP.h [new file with mode: 0644]
Cl/WWWLibrary/HTUtils.h [new file with mode: 0644]
Cl/WWWLibrary/HText.h [new file with mode: 0644]
Cl/WWWLibrary/Imakefile [new file with mode: 0644]
Cl/WWWLibrary/Makefile [new file with mode: 0644]
Cl/WWWLibrary/README [new file with mode: 0644]
Cl/WWWLibrary/SGML.c [new file with mode: 0644]
Cl/WWWLibrary/SGML.h [new file with mode: 0644]
Cl/WWWLibrary/WWW.h [new file with mode: 0644]
Cl/WWWLibrary/tcp.h [new file with mode: 0644]
HText/DefaultStyles.c [new file with mode: 0644]
HText/HText.c [new file with mode: 0644]
HText/HText.h [new file with mode: 0644]
HText/Imakefile [new file with mode: 0644]
HText/Makefile [new file with mode: 0644]
HText/dummy.c [new file with mode: 0644]
Imakefile [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
Ui/Imakefile [new file with mode: 0644]
Ui/Makefile [new file with mode: 0644]
Ui/RCS/.uidemo.c,v [new file with mode: 0644]
Ui/Ui.h [new file with mode: 0644]
Ui/UiConnections.c [new file with mode: 0644]
Ui/UiControlPanel.c [new file with mode: 0644]
Ui/UiDefaults.c [new file with mode: 0644]
Ui/UiDefs.h [new file with mode: 0644]
Ui/UiFileSelection.c [new file with mode: 0644]
Ui/UiIncludes.h [new file with mode: 0644]
Ui/UiInfo.c [new file with mode: 0644]
Ui/UiInit.c [new file with mode: 0644]
Ui/UiList.c [new file with mode: 0644]
Ui/UiMisc.c [new file with mode: 0644]
Ui/UiPage.c [new file with mode: 0644]
Ui/UiPageSettings.c [new file with mode: 0644]
Ui/UiPrint.c [new file with mode: 0644]
Ui/UiProtos.h [new file with mode: 0644]
Ui/UiRecall.c [new file with mode: 0644]
Ui/UiSearch.c [new file with mode: 0644]
Ui/UiSelectionBox.c [new file with mode: 0644]
Ui/UiTypes.h [new file with mode: 0644]
Ui/UiUtil.c [new file with mode: 0644]
Xl/Imakefile [new file with mode: 0644]
Xl/Makefile [new file with mode: 0644]
Xl/Xl.h [new file with mode: 0644]
Xl/XlConfig.h [new file with mode: 0644]
Xl/XlDefaultFonts.h [new file with mode: 0644]
Xl/XlFormatText.c [new file with mode: 0644]
Xl/XlFormatText.h [new file with mode: 0644]
Xl/XlSetup.c [new file with mode: 0644]
Xl/XlStyle.c [new file with mode: 0644]
Xl/XlStyle.h [new file with mode: 0644]
Xl/XlTypes.h [new file with mode: 0644]
Xl/XlUtil.c [new file with mode: 0644]
Xl/XlWindow.c [new file with mode: 0644]
debian/changelog [new file with mode: 0644]
debian/compat [new file with mode: 0644]
debian/control [new file with mode: 0644]
debian/copyright [new file with mode: 0644]
debian/dirs [new file with mode: 0644]
debian/erwise.desktop [new file with mode: 0644]
debian/rules [new file with mode: 0755]
erwise/Config.c [new file with mode: 0644]
erwise/Defs.h [new file with mode: 0644]
erwise/Help.c [new file with mode: 0644]
erwise/Imakefile [new file with mode: 0644]
erwise/Includes.h [new file with mode: 0644]
erwise/Makefile [new file with mode: 0644]
erwise/Misc.c [new file with mode: 0644]
erwise/Page.c [new file with mode: 0644]
erwise/Print.c [new file with mode: 0644]
erwise/Protos.h [new file with mode: 0644]
erwise/Setup.c [new file with mode: 0644]
erwise/TopLevel.c [new file with mode: 0644]
erwise/Types.h [new file with mode: 0644]
erwise/main.c [new file with mode: 0644]
lesstif-libs/libXm.so [new symlink]
lesstif-libs/libXm.so.2 [new symlink]
lesstif-libs/libXm.so.2.0.1 [new file with mode: 0755]

diff --git a/Cl/Cl.c b/Cl/Cl.c
new file mode 100644 (file)
index 0000000..77b80ce
--- /dev/null
+++ b/Cl/Cl.c
@@ -0,0 +1,413 @@
+/*
+ * Cl.c --
+ *
+ * Author: Teemu Rantanen <tvr@cs.hut.fi>
+ * Copyright (c) 1992 Teemu Rantanen
+ *                    All rights reserved
+ *
+ * Created: Thu Apr 16 22:08:27 1992 tvr
+ * Last modified: Wed May 13 13:27:58 1992 tvr
+ *
+ */
+
+#include <stdio.h>
+
+#include "Cl.h"
+
+#include "HTStyle.h"
+#include "HTParse.h"
+#include "HText.h"
+#include "tcp.h"
+
+#include "../HText/HText.h"
+
+extern HText_t *HtLocalText;
+
+int WWWErwiseStatus;
+ClConnection_t *WWWErwiseConnection;
+
+void cl_free_connection ();
+
+/*
+ * Where to load this file ?
+ */
+
+char *WWWErwiseFileLoadName = 0;
+
+/*
+ * Open connection. Allocate strutures.
+ */
+
+ClConnection_t *
+ClOpenConnection (address)
+     char *address;
+{
+  int status;
+  ClConnection_t *p = (ClConnection_t *) malloc (sizeof (*p));
+
+  /*
+   * Some validation checks
+   */
+
+  if (!p)
+    return p;
+
+  if (!address)
+    return NULL;
+
+  memset (p, 0, sizeof (*p));
+
+  p->address = (char *) strdup (address);
+
+  /*
+   * If we wish to load to file, open fd here
+   */
+
+  if (WWWErwiseFileLoadName)
+    {
+
+      p->load_to_file_fd =
+       open (WWWErwiseFileLoadName, O_WRONLY | O_CREAT, 0666);
+
+      free (WWWErwiseFileLoadName);
+
+      WWWErwiseFileLoadName = 0;
+
+      if (p->load_to_file_fd < 0)
+       {
+
+         printf ("ClOpenConnection: Cannot load to file '%s'\n",
+                 WWWErwiseFileLoadName);
+
+         ClCloseConnection (p);
+
+         return 0;
+       }
+
+      p->load_to_file = 1;
+    }
+
+
+  /*
+   * Kludge #1:
+   * Call common code to get socket fd. Also set state to some value so
+   * that ReadData calls some function that makes sense.
+   */
+
+  WWWErwiseConnection = p;
+
+  WWWErwiseStatus = CL_CONTINUES;
+
+  p->status = HTLoadAbsolute (address, 0);
+
+  if ((WWWErwiseStatus == CL_FAILED) ||
+      (WWWErwiseStatus == CL_ALREADY_LOADED))
+    {
+
+      ClCloseConnection (p);
+
+      return NULL;
+    }
+
+  return p;
+}
+
+
+/*
+ * Read data or poll connection opening
+ */
+struct HText *
+ClReadData (connection, how_done, fd)
+     ClConnection_t *connection;
+     int *how_done;
+     int *fd;
+{
+  int continues;
+
+  void (*tmpf) ();
+
+  WWWErwiseStatus = CL_CONTINUES;
+
+  WWWErwiseConnection = connection;
+
+  /*
+   * If loading was on non-blocking mode ?
+   */
+  if (!connection->function)
+    {
+
+      *fd = connection->fd;
+
+      if (connection->status)
+       {
+
+         *how_done = CL_COMPLETED;
+
+         ClCloseConnection (connection);
+
+         return HtLocalText;
+
+       }
+      else
+       {
+
+         *how_done = CL_FAILED;
+
+         ClCloseConnection (connection);
+
+         return 0;
+
+       }
+    }
+
+  /*
+   * Try max 3 pollings at one call. This makes loading faster but does not
+   * get too much cpu
+   */
+  for (continues = 3; continues > 0; continues--)
+    {
+
+      tmpf = *connection->function;
+
+      if (*connection->function)
+       (void) (*connection->function) ();
+
+      /*
+       * If mode is going to change to poll, set it now
+       */
+      if (*connection->function == WWWErwiseSetPoll)
+       (void) WWWErwiseSetPoll ();
+
+      /*
+       * Can we try next polling?
+       */
+      if ((*connection->function == tmpf) || (*how_done != CL_CONTINUES) ||
+         (connection->select_fd))
+       {
+
+         continues = 0;
+       }
+    }
+
+  *how_done = WWWErwiseStatus;
+
+  *fd = connection->select_fd;
+
+  if (*how_done == CL_FAILED)
+    {
+
+      ClCloseConnection (connection);
+
+      return NULL;
+    }
+
+  if (*how_done == CL_COMPLETED)
+    {
+
+      ClCloseConnection (connection);
+
+      return HtLocalText;      /* global variable because @#$#$ <censored> */
+    }
+
+  return NULL;
+}
+
+
+/*
+ * User wants to terminate a connection
+ */
+
+void
+ClCloseConnection (connection)
+     ClConnection_t *connection;
+{
+  if (connection->load_to_file)
+    {
+
+      close (connection->load_to_file_fd);
+    }
+
+  if (connection->fd)
+    {
+
+      shutdown (connection->fd, 2);
+
+      close (connection->fd);
+    }
+
+  if (connection->secondary_fd)
+    {
+
+      shutdown (connection->secondary_fd, 2);
+
+      close (connection->secondary_fd);
+    }
+
+  cl_free_connection (connection);
+}
+
+
+
+/*
+ * read data from local buffer. If no data on buffer, make normal read
+ */
+int
+cl_read_data (fd, data, length)
+     int fd;
+     char *data;
+     int length;
+{
+  ClConnection_t *p = WWWErwiseConnection;
+
+  cl_data_t *b = p->buffer_first;
+
+  int howmuch = 0;
+
+  if (p->buffer_last)
+    {
+      if (!b)
+       return 0;
+
+      if (length < b->size)
+       {
+
+         memcpy (data, b->data, length);
+
+         howmuch = length;
+
+         b->size -= length;
+
+         b->data += length;
+       }
+      else
+       {
+
+         memcpy (data, b->data, b->size);
+
+         howmuch = b->size;
+
+         free (b->freeptr);
+
+         if (b->next)
+           {
+             b->next->prev = 0;
+             p->buffer_first = b->next;
+           }
+         else
+           {
+             p->buffer_first = 0;
+           }
+         free (b);
+       }
+    }
+  else
+    {
+      return NETREAD (fd, data, length);
+    }
+  return howmuch;
+}
+
+
+
+/*
+ * Free everything connection structure has malloced (and not yet freed)
+ */
+
+void
+cl_free_connection (connection)
+     ClConnection_t *connection;
+{
+  if (connection->address)
+    free (connection->address);
+
+  if (connection->addr)
+    free (connection->addr);
+
+  if (connection->command)
+    free (connection->command);
+
+  if (connection->buffer_first)
+    {
+      cl_data_t *p = connection->buffer_first;
+
+      while (p)
+       {
+
+         cl_data_t *p2 = p;
+
+         free (p->freeptr);
+
+         free (p);
+
+         p = p2->next;
+       }
+    }
+
+  /*
+   * NOTE!
+   * anAnchor or diag should not be freed (at least they are not on
+   * original common code.
+   */
+}
+
+
+/*
+ * Return true if loading to file is supported with this address
+ */
+int
+ClCanLoadToFile (address)
+     char *address;
+{
+  char *access;
+
+  if (!address)
+    {
+      return 0;
+    }
+
+  access = HTParse (address, "", PARSE_ACCESS);
+
+  if (!strcmp (access, "html"))
+    {
+
+      free (access);
+
+      return 1;
+    }
+
+  if (!strcmp (access, "file"))
+    {
+
+      free (access);
+
+      return 1;
+    }
+
+  free (access);
+
+  return 0;
+}
+
+
+/*
+ * Are we loading this connection to file ?
+ */
+int
+ClConnectionOnLoadToFileMode (connection)
+     ClConnection_t *connection;
+{
+  if (!connection)
+    return 0;
+
+  return connection->load_to_file;
+}
+
+
+void
+ClSetFileNameForLoadingToFile (char *filename)
+{
+  if (filename)
+    WWWErwiseFileLoadName = (char *) strdup (filename);
+  else
+    WWWErwiseFileLoadName = NULL;
+}
diff --git a/Cl/Cl.h b/Cl/Cl.h
new file mode 100644 (file)
index 0000000..f502d13
--- /dev/null
+++ b/Cl/Cl.h
@@ -0,0 +1,180 @@
+/*
+ * Cl.h --
+ *
+ * Author: Teemu Rantanen <tvr@cs.hut.fi>
+ * Copyright (c) 1992 Teemu Rantanen
+ *                    All rights reserved
+ *
+ * Created: Thu Apr 16 21:40:39 1992 tvr
+ * Last modified: Wed May 13 00:11:13 1992 tvr
+ *
+ */
+
+#include "HTAnchor.h"
+#include "HTStyle.h"
+#include "HText.h"
+
+/*
+ * Data is store on linked list
+ */
+typedef struct cl_data_s
+{
+  struct cl_data_s *next;
+  struct cl_data_s *prev;
+  void *data;
+  void *freeptr;
+  int size;
+}         cl_data_t;
+
+
+/*
+ * This structure contains all information needed to accomplish this
+ * connection.
+ */
+typedef struct ClConnection_s
+{
+  /*
+   * address of this connection
+   */
+  char *address;
+
+  /*
+   * Fd of the connection. Also ftp connection data channel.
+   */
+  int fd;
+  int secondary_fd;
+
+  /*
+   * If this fd is not set connection has to be polled. If it is NOT
+   * set, connection has to be selected (must not poll!)
+   */
+  int select_fd;
+
+  /*
+   * Function of this state machine
+   */
+  void (**function) ();
+
+  /*
+   * Store junk here (address of data on connect() )
+   */
+  void *addr;
+  int addr_size;
+
+  /*
+   * Store command to write to the net here
+   */
+  char *command;
+
+  /*
+   * Data Buffer
+   */
+  cl_data_t *buffer_first;
+  cl_data_t *buffer_last;
+
+  /*
+   * stuff given to HTLoadHTTP()
+   */
+  int diag;
+  struct HTAnchor *anAnchor;
+
+  /*
+   * How happened with load if on nonblocking mode
+   */
+  int status;
+
+  /*
+   * Hostname on ftp connections
+   */
+  char *ftphost;
+
+  /*
+   * Data port on ftp connections
+   */
+  int port;
+
+
+  /*
+   * Load to file ?
+   */
+  int load_to_file;
+  int load_to_file_fd;
+
+}              ClConnection_t;
+
+
+/*
+ * More data is to be read
+ */
+#define CL_CONTINUES           0
+
+/*
+ * Connection completed
+ */
+#define CL_COMPLETED           1
+
+/*
+ * Connection failed
+ */
+#define CL_FAILED              2
+
+/*
+ * Document already loaded (error)
+ */
+#define CL_ALREADY_LOADED      3
+
+
+/*
+ * Prototypes
+ */
+ClConnection_t *ClOpenConnection (char *address);
+
+struct HText *ClReadData (ClConnection_t * connection, int *how_done, int *fd);
+
+void ClSetOptions (ClConnection_t * connection, int argc, char *argv[]);
+
+void ClCloseConnection (ClConnection_t * connection);
+
+int ClCanLoadToFile (char *address);
+
+void ClSetFileNameForLoadingToFile (char *filename);
+
+int ClConnectionOnLoadToFileMode (ClConnection_t * connection);
+
+/*
+ * Globals
+ */
+
+extern int WWWErwiseStatus;
+extern ClConnection_t *WWWErwiseConnection;
+
+void WWWErwiseConnect ();
+void WWWErwiseSendCommand ();
+void WWWErwiseReadData ();
+void WWWErwiseParse ();
+
+
+/*
+ * Internals
+ */
+
+void WWWErwiseFtpUser ();
+void WWWErwiseFtpPass ();
+void WWWErwiseFtpPassive ();
+void WWWErwiseFtpGetPassive ();
+void WWWErwiseFtpGetCommand ();
+void WWWErwiseFtpDataChannel ();
+void WWWErwiseFtpCheckForError ();
+void WWWErwiseFtpBinary ();
+void WWWErwiseCheckParse ();
+void WWWErwiseSetPoll ();
+void WWWErwiseSetSelect ();
+void WWWErwiseTerminateIfLoadToFile ();
+
+int cl_start_connection ();
+
+/*
+ * Debug ...
+ */
+
+#define CL_DEBUG(a)    printf a
diff --git a/Cl/ClConnection.c b/Cl/ClConnection.c
new file mode 100644 (file)
index 0000000..12e2688
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * ClConnection.c --
+ *
+ * Author: Teemu Rantanen <tvr@cs.hut.fi>
+ * Copyright (c) 1992 Teemu Rantanen
+ *                    All rights reserved
+ *
+ * Created: Mon Apr 20 19:29:14 1992 tvr
+ * Last modified: Wed May 13 00:53:24 1992 tvr
+ *
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#include "Cl.h"
+
+#include "HTParse.h"
+#include "HTFormat.h"
+#include "HTAnchor.h"
+#include "tcp.h"
+
+#include "../HText/HText.h"
+
+extern HText_t *HtLocalText;
+
+
+/*
+ * Poll connecting until connection completes.
+ */
+void
+WWWErwiseConnect ()
+{
+  int status;
+
+  CL_DEBUG (("Poll connect\n"));
+
+  status = connect (WWWErwiseConnection->fd,
+                   (struct sockaddr *) WWWErwiseConnection->addr,
+                   WWWErwiseConnection->addr_size);
+  if (status < 0 && (errno != EISCONN))
+    {
+      if ((errno == EALREADY) || (errno == EINPROGRESS))
+       {
+         /*
+           * Would block
+           */
+         return;
+       }
+
+      CL_DEBUG (("Cannot connect(%d)\n", errno));
+
+      /*
+       * Cannot connect
+       */
+      WWWErwiseStatus = CL_FAILED;
+
+      return;
+    }
+
+  /*
+   * Connected. Get next function from the list of things to do.
+   * Also, stop polling.
+   */
+
+  free (WWWErwiseConnection->addr);
+
+  WWWErwiseConnection->addr = NULL;
+
+  WWWErwiseConnection->function++;
+}
+
+
+
+/*
+ * Instead of connect, this function is called. Store needed data to
+ * poll connecting later.
+ */
+int
+erwise_connect (fd, addr, size)
+     int fd;
+     struct sockaddr *addr;
+     int size;
+{
+  int status;
+
+  CL_DEBUG (("Try to Connect\n"));
+
+  status = connect (fd, addr, size);
+
+  if (status < 0)
+    {
+      switch (errno)
+       {
+       case EINPROGRESS:
+       case EISCONN:
+       case EALREADY:
+         break;
+
+       default:
+         CL_DEBUG (("Cannot connect (first try %d)\n", errno));
+         return -1;
+       }
+    }
+
+  /*
+   * Duplicate what to connect
+   */
+  WWWErwiseConnection->addr = (void *) malloc (size);
+
+  memcpy (WWWErwiseConnection->addr, addr, size);
+
+  WWWErwiseConnection->addr_size = size;
+
+  /*
+   * OK
+   */
+  return 0;
+}
+
+
+
+
+
+/*
+ * Send command to net
+ */
+
+void
+WWWErwiseSendCommand ()
+{
+  int status;
+
+  CL_DEBUG (("Send Command\n"));
+
+  status = NETWRITE (WWWErwiseConnection->fd,
+                    WWWErwiseConnection->command,
+                    (int) strlen (WWWErwiseConnection->command));
+
+  if (status == strlen (WWWErwiseConnection->command))
+    {
+      /*
+       * Succeeded
+       */
+      free (WWWErwiseConnection->command);
+      WWWErwiseConnection->command = 0;
+
+      WWWErwiseConnection->function++;
+
+    }
+  else if (status < 0)
+    {
+      /*
+       * Failed
+       */
+      CL_DEBUG (("SendCommand failed\n"));
+
+      WWWErwiseStatus = CL_FAILED;
+      return;
+    }
+  else
+    {
+      /*
+       * Partial read
+       */
+      char *tmp = WWWErwiseConnection->command;
+
+      WWWErwiseConnection->command = (char *) strdup (tmp + status);
+      free (tmp);
+    }
+}
+
+
+#define ERWISE_BLOCK 8192
+
+/*
+ * Read data until all data is read
+ */
+
+void
+WWWErwiseReadData ()
+{
+  char tmp[ERWISE_BLOCK];
+
+  int i;
+
+  i = read (WWWErwiseConnection->fd,
+           tmp,
+           ERWISE_BLOCK);
+
+  CL_DEBUG (("got %d bytes\n", i));
+
+  /*
+   * Append data to (memory) buffer or to file.
+   */
+  if (i > 0)
+    {
+
+      /*
+       * Load directly to file ?
+       */
+
+      if (WWWErwiseConnection->load_to_file)
+       {
+         int st;
+
+         st = write (WWWErwiseConnection->load_to_file_fd,
+                     tmp,
+                     i);
+
+         if (st != i)
+           {
+             WWWErwiseStatus = CL_FAILED;
+           }
+
+         return;
+       }
+
+      if (!WWWErwiseConnection->buffer_first)
+       {
+
+         WWWErwiseConnection->buffer_first =
+           WWWErwiseConnection->buffer_last =
+           (cl_data_t *) malloc (sizeof (cl_data_t));
+
+         memset (WWWErwiseConnection->buffer_first, 0, sizeof (cl_data_t));
+
+         WWWErwiseConnection->buffer_first->data =
+           WWWErwiseConnection->buffer_first->freeptr =
+           (void *) malloc (i);
+
+         WWWErwiseConnection->buffer_first->size = i;
+
+         memcpy (WWWErwiseConnection->buffer_first->data, tmp, i);
+
+       }
+      else
+       {
+
+         cl_data_t *p = (cl_data_t *) malloc (sizeof (cl_data_t));
+
+         memset (p, 0, sizeof (cl_data_t));
+
+         p->data = p->freeptr = (void *) malloc (i);
+
+         p->size = i;
+
+         memcpy (p->data, tmp, i);
+
+         p->prev = WWWErwiseConnection->buffer_last;
+
+         WWWErwiseConnection->buffer_last->next = p;
+
+         WWWErwiseConnection->buffer_last = p;
+       }
+
+      return;
+    }
+
+  if (i < 0 && (errno != EWOULDBLOCK))
+    {
+      CL_DEBUG (("ReadData failed\n"));
+      WWWErwiseStatus = CL_FAILED;
+    }
+
+  if (i == 0)
+    {
+      WWWErwiseConnection->function++;
+      return;
+    }
+}
+
+
+
+/*
+ * Parse data that has been read
+ */
+
+void
+WWWErwiseParse ()
+{
+  /*
+   * XXXXXX If saving to file, don't parse
+   */
+
+  HTParseFormat (WWWErwiseConnection->diag ? WWW_PLAINTEXT : WWW_HTML,
+                WWWErwiseConnection->anAnchor,
+                WWWErwiseConnection->fd);
+
+  (void) HTClose (WWWErwiseConnection->fd);
+
+  /*
+   * XXXXX free connection structure
+   */
+  WWWErwiseStatus = CL_COMPLETED;
+  WWWErwiseConnection->function++;
+}
+
+
+
+/*
+ * If we are loading to file, nothing else needs to be done ...
+ */
+void
+WWWErwiseTerminateIfLoadToFile ()
+{
+  if (WWWErwiseConnection->load_to_file)
+    {
+      HtLocalText = 0;
+
+      while (*WWWErwiseConnection->function)
+       {
+         WWWErwiseConnection->function++;
+       }
+
+      WWWErwiseStatus = CL_COMPLETED;
+
+    }
+  else
+    {
+      WWWErwiseConnection->function++;
+    }
+}
diff --git a/Cl/ClFTP.c b/Cl/ClFTP.c
new file mode 100644 (file)
index 0000000..7b859c7
--- /dev/null
@@ -0,0 +1,496 @@
+/*
+ * ClFTP.c --
+ *
+ * Author: Teemu Rantanen <tvr@cs.hut.fi>
+ * Copyright (c) 1992 Teemu Rantanen
+ *                    All rights reserved
+ *
+ * Created: Sun Apr 19 22:42:51 1992 tvr
+ * Last modified: Mon May 11 23:30:15 1992 tvr
+ *
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "Cl.h"
+
+#include "HTParse.h"
+#include "HTUtils.h"
+#include "tcp.h"
+#include "HTAnchor.h"
+
+#define IPPORT_FTP 21
+
+/*
+ * Erwise's own ftp load function. Setup all things to be done
+ */
+
+PUBLIC int HTFTP_open_file_read
+ARGS2 (
+       char *, name,
+       HTParentAnchor *, anchor
+)
+{
+  int status;
+
+  /*
+   * Set up a list of things to do
+   */
+
+  {
+    static void (*functions[]) () =
+    {
+      WWWErwiseConnect,
+      WWWErwiseSetSelect,
+      WWWErwiseFtpGetCommand,  /* get junk */
+
+      WWWErwiseSetPoll,
+      WWWErwiseFtpUser,                /* Send user */
+      WWWErwiseSendCommand,
+      WWWErwiseSetSelect,
+      WWWErwiseFtpGetCommand,
+
+      WWWErwiseSetPoll,
+      WWWErwiseFtpPass,                /* send pass */
+      WWWErwiseSendCommand,
+      WWWErwiseSetSelect,
+      WWWErwiseFtpGetCommand,
+
+      WWWErwiseSetPoll,
+      WWWErwiseFtpBinary,      /* set binary mode */
+      WWWErwiseSendCommand,
+      WWWErwiseSetSelect,
+      WWWErwiseFtpGetCommand,
+
+      WWWErwiseSetPoll,
+      WWWErwiseFtpPassive,     /* set passive mode */
+      WWWErwiseSendCommand,
+      WWWErwiseSetSelect,
+
+      WWWErwiseFtpGetPassive,  /* Get reply and request a file */
+      WWWErwiseSetPoll,
+      WWWErwiseSendCommand,
+
+      WWWErwiseFtpDataChannel, /* make connection */
+      WWWErwiseConnect,
+
+      WWWErwiseFtpCheckForError,/* check for file not found etc ... */
+
+      WWWErwiseSetSelect,      /* read data */
+      WWWErwiseReadData,
+
+      WWWErwiseSetPoll,                /* parse stuff */
+      WWWErwiseTerminateIfLoadToFile,
+      WWWErwiseParse,
+      NULL,
+    };
+    WWWErwiseConnection->function = functions;
+  }
+
+  /*
+   * Set up information needed later
+   */
+
+  WWWErwiseConnection->ftphost = HTParse (name, "", PARSE_HOST);
+
+  WWWErwiseConnection->anAnchor = anchor;
+
+  /*
+   * Start connecting to data port
+   */
+
+  status = cl_start_connection (WWWErwiseConnection->ftphost,
+                               WWWErwiseConnection,
+                               IPPORT_FTP);
+
+  /*
+   * FTP mode on formatting is PLAINTEXT
+   */
+
+  WWWErwiseConnection->diag = 1;
+
+  return status;
+}
+
+
+/*
+ * Send misc commands to data flow
+ */
+
+void
+WWWErwiseFtpUser ()
+{
+  CL_DEBUG (("FTP: send user\n"));
+
+  WWWErwiseConnection->command = strdup ("USER ftp\r\n");
+
+  WWWErwiseConnection->function++;
+}
+
+void
+WWWErwiseFtpPass ()
+{
+  char tmp[8192], hostname[1024];
+
+  CL_DEBUG (("FTP: send pass\n"));
+
+  if (!gethostname (hostname, 1024))
+    {
+      strcpy (hostname, "noname.rupu");
+    }
+
+  sprintf (tmp, "PASS erwise@%s\r\n", hostname);
+
+  WWWErwiseConnection->command = strdup (tmp);
+
+  WWWErwiseConnection->function++;
+}
+
+void
+WWWErwiseFtpPassive ()
+{
+  CL_DEBUG (("FTP: send pasv\n"));
+
+  WWWErwiseConnection->command = strdup ("PASV\r\n");
+
+  WWWErwiseConnection->function++;
+}
+
+void
+WWWErwiseFtpBinary ()
+{
+  CL_DEBUG (("FTP: set binary\n"));
+
+  WWWErwiseConnection->command = strdup ("TYPE I\r\n");
+
+  WWWErwiseConnection->function++;
+}
+
+
+/*
+ * Get junk commands return when the are accomplished
+ */
+
+#define ERWISE_BL_SIZE   8192
+
+void
+WWWErwiseFtpGetCommand ()
+{
+  char tmp[ERWISE_BL_SIZE];
+
+  int i;
+
+  i = NETREAD (WWWErwiseConnection->fd, tmp, ERWISE_BL_SIZE);
+
+  if ((i == -1) && (errno == EWOULDBLOCK))
+    {
+      return;
+    }
+
+  CL_DEBUG (("FTP: got %s\n", tmp));
+
+  if ((tmp[0] < '2') || (tmp[0] > '3'))
+    {
+
+      WWWErwiseStatus = CL_FAILED;
+
+      return;
+
+    }
+
+  WWWErwiseConnection->function++;
+
+}
+/*
+ * Get return code from passive command
+ */
+
+void
+WWWErwiseFtpGetPassive ()
+{
+  char tmp[1024], *p;
+  int a1, a2, a3, a4;
+  int p1, p2, port;
+
+  int i;
+
+  i = NETREAD (WWWErwiseConnection->fd, tmp, 1024);
+
+  if ((i == -1) && (errno == EWOULDBLOCK))
+    {
+      return;
+    }
+
+  tmp[i] = 0;
+
+  CL_DEBUG (("FTP: got passive %s\n", tmp));
+
+  /*
+   * Get to the line which is reply from PASV command.
+   * Check for errors on the way.
+   */
+
+  p = tmp;
+
+  i = 1;
+
+  while (i)
+    {
+      if ((*p >= '0') && (*p <= '9'))
+       {
+         if (!strncmp ("227", p, 3))
+           {
+             i = 0;
+             continue;
+           }
+
+         if ((p[0] < '2') || (tmp[0] > '3'))
+           {
+             WWWErwiseStatus = CL_FAILED;
+             return;
+           }
+       }
+
+      /*
+       * Get next line
+       */
+      while (*p && (*p != '\n'))
+       p++;
+
+      p++;
+
+      if (!*p)
+       {
+         /*
+           * no line but no errors yet
+           */
+         return;
+       }
+    }
+
+  /*
+   * Correct line, get port
+   */
+
+  while (*p && (*p != '('))
+    p++;
+
+  if (!*p)
+    {
+      /*
+       * 227 but no address / port ??
+       */
+
+      WWWErwiseStatus = CL_FAILED;
+
+      return;
+    }
+
+  sscanf (p, "(%d,%d,%d,%d,%d,%d)", &a1, &a2, &a3, &a4, &p1, &p2);
+
+  port = p1 * 256 + p2;
+
+  printf ("ErwiseFTP: got connection to %d.%d.%d.%d on port %d\n",
+         a1, a2, a3, a4, port);
+
+  /*
+   * send receive command too
+   */
+
+  {
+    char *filename = HTParse (WWWErwiseConnection->address, "",
+                             PARSE_PATH + PARSE_PUNCTUATION);
+
+    sprintf (tmp, "RETR %s\r\n", filename);
+
+    WWWErwiseConnection->command = strdup (tmp);
+  }
+
+  WWWErwiseConnection->port = port;
+
+  WWWErwiseConnection->function++;
+}
+
+
+/*
+ * Make data channel connection
+ */
+void
+WWWErwiseFtpDataChannel ()
+{
+  /*
+   * Set data flow port to secondary so that it can be closed properly
+   */
+
+  WWWErwiseConnection->secondary_fd = WWWErwiseConnection->fd;
+
+  cl_start_connection (WWWErwiseConnection->ftphost,
+                      WWWErwiseConnection,
+                      WWWErwiseConnection->port);
+
+  WWWErwiseConnection->function++;
+}
+
+
+
+/*
+ * Start connection. Make sure it is nonblocking one.
+ */
+
+int
+cl_start_connection (host, connection, port)
+     char *host;
+     ClConnection_t *connection;
+     int port;
+{
+  /*
+   * Code partly from get_connection()
+   */
+
+  int s;
+
+  struct hostent *phost;       /* Pointer to host -- See netdb.h */
+  struct sockaddr_in soc_address;      /* Binary network address */
+  struct sockaddr_in *sin = &soc_address;
+
+/*  Set up defaults:
+*/
+  sin->sin_family = AF_INET;   /* Family, host order  */
+  sin->sin_port = htons (port);
+
+/* Get node name:
+*/
+  {
+    if (*host >= '0' && *host <= '9')
+      {                                /* Numeric node address: */
+       sin->sin_addr.s_addr = inet_addr (host);        /* See arpa/inet.h */
+
+      }
+    else
+      {                                /* Alphanumeric node name: */
+       phost = gethostbyname (host);   /* See netdb.h */
+       if (!phost)
+         {
+           if (TRACE)
+             printf (
+                      "FTP: Can't find internet node name `%s'.\n",
+                      host);
+
+           WWWErwiseStatus = CL_FAILED;
+
+           return -1;
+         }
+       memcpy (&sin->sin_addr, phost->h_addr, phost->h_length);
+      }
+
+    if (TRACE)
+      printf (
+              "FTP: Parsed remote address as port %d, inet %d.%d.%d.%d\n",
+              (unsigned int) ntohs (sin->sin_port),
+              (int) *((unsigned char *) (&sin->sin_addr) + 0),
+              (int) *((unsigned char *) (&sin->sin_addr) + 1),
+              (int) *((unsigned char *) (&sin->sin_addr) + 2),
+              (int) *((unsigned char *) (&sin->sin_addr) + 3));
+  }                            /* scope of p1 */
+
+
+  {
+    int status;
+
+    s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+    if (s < 0)
+      {
+       WWWErwiseStatus = CL_FAILED;
+
+       return -1;
+      }
+
+    connection->fd = s;
+
+    /*
+     * Must NOT select connection now. Must poll.
+     */
+    connection->select_fd = 0;
+
+    (void) fcntl (s, F_SETFL, O_NONBLOCK);
+    (void) fcntl (s, F_SETFL, FNDELAY);
+
+    status = erwise_connect (s,
+                            (struct sockaddr *) & soc_address,
+                            sizeof (soc_address));
+  }
+
+  /*
+   * everything done
+   */
+
+  return s;
+}
+
+
+
+/*
+ * Check for errors when retr is sent
+ */
+
+void
+WWWErwiseFtpCheckForError ()
+{
+  fd_set readfds;
+  fd_set exceptionfds;
+
+  struct timeval tv;
+
+  int fd, fd2;
+
+  fd = WWWErwiseConnection->fd;
+  fd2 = WWWErwiseConnection->secondary_fd;
+
+  tv.tv_sec = tv.tv_usec = 0;
+
+  FD_ZERO (&readfds);
+  FD_SET (fd, &readfds);
+  FD_SET (fd2, &readfds);
+
+  FD_ZERO (&exceptionfds);
+  FD_SET (fd, &exceptionfds);
+  FD_SET (fd2, &exceptionfds);
+
+  select ((fd > fd2 ? fd : fd2) + 1, &readfds, NULL, &exceptionfds, &tv);
+
+  /*
+   * Error on either data or command channel
+   */
+  if (FD_ISSET (fd, &exceptionfds) || FD_ISSET (fd2, &exceptionfds))
+    {
+
+      WWWErwiseStatus = CL_FAILED;
+
+      return;
+    }
+
+  /*
+   * Data is flowing (OK)
+   */
+  if (FD_ISSET (fd, &readfds))
+    {
+
+      WWWErwiseConnection->function++;
+
+      return;
+    }
+
+  /*
+   * Anything here is considered an error
+   */
+  if (FD_ISSET (fd2, &readfds))
+    {
+
+      WWWErwiseStatus = CL_FAILED;
+
+      return;
+    }
+}
diff --git a/Cl/ClHTTP.c b/Cl/ClHTTP.c
new file mode 100644 (file)
index 0000000..15c0dfe
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * ClHTTP.c --
+ *
+ * Author: Teemu Rantanen <tvr@cs.hut.fi>
+ * Copyright (c) 1992 Teemu Rantanen
+ *                    All rights reserved
+ *
+ * Created: Fri Apr 17 23:43:01 1992 tvr
+ * Last modified: Mon May 11 23:29:10 1992 tvr
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "Cl.h"
+
+#include "HTParse.h"
+#include "HTFormat.h"
+#include "HTAnchor.h"
+#include "tcp.h"
+
+
+
+/*
+ * We want that loading won't kill the whole f*king client.
+ *
+ * This is a kludge ;)
+ */
+
+PUBLIC int HTLoadHTTP
+ARGS4 (CONST char *, arg,
+       CONST char *, gate,
+       HTAnchor *, anAnchor,
+       int, diag)
+{
+  /*
+   * Most code from old loading function
+   */
+
+  int s;
+  int status;
+  char *command;
+
+  struct sockaddr_in soc_address;      /* Binary network address */
+  struct sockaddr_in *sin = &soc_address;
+
+  if (!arg)
+    return -3;                 /* Bad if no name sepcified     */
+  if (!*arg)
+    return -2;                 /* Bad if name had zero length  */
+
+/*  Set up defaults:
+*/
+  sin->sin_family = AF_INET;   /* Family, host order  */
+  sin->sin_port = htons (TCP_PORT);    /* Default: new port,    */
+
+  if (TRACE)
+    {
+      if (gate)
+       fprintf (stderr,
+                "HTTPAccess: Using gateway %s for %s\n", gate, arg);
+      else
+       fprintf (stderr, "HTTPAccess: Direct access for %s\n", arg);
+    }
+
+/* Get node name and optional port number:
+*/
+  {
+    char *p1 = HTParse (gate ? gate : arg, "", PARSE_HOST);
+    HTParseInet (sin, p1);
+    free (p1);
+  }
+
+
+/*     Now, let's get a socket set up from the server for the sgml data:
+*/
+  s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+  (void) fcntl (s, F_SETFL, O_NONBLOCK);
+  (void) fcntl (s, F_SETFL, FNDELAY);
+
+  /*
+   * Now. Do first attempt to connect.
+   */
+  status = erwise_connect (s,
+                          (struct sockaddr *) & soc_address,
+                          sizeof (soc_address));
+
+  if (s < 0)
+    {
+      /*
+       * Fail
+       */
+      /*free(command);*/
+      return -1;
+    }
+
+  if (TRACE)
+    printf ("HTTP connected, socket %d\n", s);
+
+/*     Ask that node for the document,
+**     omitting the host name & anchor if not gatewayed.
+*/
+  if (gate)
+    {
+      command = malloc (4 + strlen (arg) + 1 + 1);
+      strcpy (command, "GET ");
+      strcat (command, arg);
+    }
+  else
+    {                          /* not gatewayed */
+      char *p1 = HTParse (arg, "", PARSE_PATH | PARSE_PUNCTUATION);
+      command = malloc (4 + strlen (p1) + 1 + 1);
+      strcpy (command, "GET ");
+      strcat (command, p1);
+      free (p1);
+    }
+  strcat (command, "\r\n");    /* Include CR for telnet compat. */
+
+
+  if (TRACE)
+    printf ("HTTP writing command `%s' to socket %d\n", command, s);
+
+#ifdef NOT_ASCII
+  {
+    char *p;
+    for (p = command; *p; p++)
+      {
+       *p = TOASCII (*p);
+      }
+  }
+#endif
+
+  /*
+   * Ok. Everything is now set up. Set functionpointers so that
+   * the rest of loading works ok.
+   */
+
+  {
+    static void (*functions[]) () =
+    {
+      WWWErwiseConnect,
+      WWWErwiseSendCommand,
+      WWWErwiseSetSelect,
+      WWWErwiseReadData,
+      WWWErwiseSetPoll,
+      WWWErwiseTerminateIfLoadToFile,
+      WWWErwiseParse,
+      NULL,
+    };
+    WWWErwiseConnection->function = functions;
+  }
+
+  WWWErwiseConnection->command = command;
+
+  WWWErwiseConnection->diag = diag;
+
+  WWWErwiseConnection->anAnchor = anAnchor;
+
+  /*
+   * Cheat HTOpen()
+   */
+  return s;
+}
diff --git a/Cl/ClMisc.c b/Cl/ClMisc.c
new file mode 100644 (file)
index 0000000..932131f
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * ClMisc.c --
+ *
+ * Author: Teemu Rantanen <tvr@cs.hut.fi>
+ * Copyright (c) 1992 Teemu Rantanen
+ *                    All rights reserved
+ *
+ * Created: Mon Apr 20 19:27:04 1992 tvr
+ * Last modified: Mon Apr 20 19:48:47 1992 tvr
+ *
+ */
+
+#include "Cl.h"
+
+
+/*
+ * Set mode to poll or to select connection
+ */
+
+void
+WWWErwiseSetPoll ()
+{
+  CL_DEBUG (("SetPoll\n"));
+
+  WWWErwiseConnection->select_fd = 0;
+
+  WWWErwiseConnection->function++;
+}
+
+void
+WWWErwiseSetSelect ()
+{
+  CL_DEBUG (("SetSelect\n"));
+
+  WWWErwiseConnection->select_fd = WWWErwiseConnection->fd;
+
+  WWWErwiseConnection->function++;
+}
diff --git a/Cl/Imakefile b/Cl/Imakefile
new file mode 100644 (file)
index 0000000..9379405
--- /dev/null
@@ -0,0 +1,20 @@
+#
+#
+#
+
+#define IHavesubdirs
+#define PassCDebugFlags
+
+          OBJS = Cl.o ClHTTP.o ClFTP.o ClMisc.o ClConnection.o
+          SRCS = Cl.c ClHTTP.c ClFTP.c ClMisc.c ClConnection.c
+       SUBDIRS = WWWLibrary
+       DEFINES = -I$(SUBDIRS)
+
+NormalLibraryTarget(Cl, $(OBJS))
+
+MakeSubdirs($(SUBDIRS))
+MakefileSubdirs($(SUBDIRS))
+DependTarget()
+DependSubdirs($(SUBDIRS))
+
+
diff --git a/Cl/Makefile b/Cl/Makefile
new file mode 100644 (file)
index 0000000..631a7de
--- /dev/null
@@ -0,0 +1,405 @@
+# Makefile generated by imake - do not edit!
+# $XConsortium: imake.c,v 1.65 91/07/25 17:50:17 rws Exp $
+#
+# The cpp used on this machine replaces all newlines and multiple tabs and
+# spaces in a macro expansion with a single space.  Imake tries to compensate
+# for this, but is not always successful.
+#
+
+# -------------------------------------------------------------------------
+# Makefile generated from "Imake.tmpl" and </tmp/IIf.a10826>
+# $XConsortium: Imake.tmpl,v 1.139 91/09/16 08:52:48 rws Exp $
+#
+# Platform-specific parameters may be set in the appropriate <vendor>.cf
+# configuration files.  Site-specific parameters should be set in the file
+# site.def.  Full rebuilds are recommended if any parameters are changed.
+#
+# If your C preprocessor does not define any unique symbols, you will need
+# to set BOOTSTRAPCFLAGS when rebuilding imake (usually when doing
+# "make World" the first time).
+#
+
+# -------------------------------------------------------------------------
+# site-specific configuration parameters that need to come before
+# the platform-specific parameters - edit site.def to change
+
+# site:  $XConsortium: site.def,v 1.2 91/07/30 20:26:44 rws Exp $
+
+# -------------------------------------------------------------------------
+# platform-specific configuration parameters - edit sun.cf to change
+
+# platform:  $XConsortium: sun.cf,v 1.72.1.1 92/03/18 13:13:37 rws Exp $
+
+# operating system:  SunOS 4.1.2
+
+# $XConsortium: sunLib.rules,v 1.7 91/12/20 11:19:47 rws Exp $
+
+.c.o:
+       $(CC) -c $(CFLAGS) $*.c
+
+# -------------------------------------------------------------------------
+# site-specific configuration parameters that go after
+# the platform-specific parameters - edit site.def to change
+
+# site:  $XConsortium: site.def,v 1.2 91/07/30 20:26:44 rws Exp $
+
+            SHELL = /bin/sh
+
+              TOP = ../.
+      CURRENT_DIR = ./Cl
+
+               AR = ar clq
+  BOOTSTRAPCFLAGS =
+               CC = gcc2 -pipe -fstrength-reduce -fpcc-struct-return
+               AS = as
+
+         COMPRESS = compress
+              CPP = /lib/cpp $(STD_CPP_DEFINES)
+    PREPROCESSCMD = gcc2 -pipe -fstrength-reduce -fpcc-struct-return -E $(STD_CPP_DEFINES)
+          INSTALL = install
+               LD = ld
+             LINT = lint
+      LINTLIBFLAG = -C
+         LINTOPTS = -axz
+               LN = ln -s
+             MAKE = make
+               MV = mv
+               CP = cp
+
+           RANLIB = ranlib
+  RANLIBINSTFLAGS =
+
+               RM = rm -f
+            TROFF = psroff
+         MSMACROS = -ms
+              TBL = tbl
+              EQN = eqn
+     STD_INCLUDES =
+  STD_CPP_DEFINES =
+      STD_DEFINES =
+ EXTRA_LOAD_FLAGS = -B/usr/bin/
+  EXTRA_LIBRARIES =
+             TAGS = ctags
+
+    SHAREDCODEDEF = -DSHAREDCODE
+         SHLIBDEF = -DSUNSHLIB
+
+    PROTO_DEFINES =
+
+     INSTPGMFLAGS =
+
+     INSTBINFLAGS = -m 0755
+     INSTUIDFLAGS = -m 4755
+     INSTLIBFLAGS = -m 0644
+     INSTINCFLAGS = -m 0444
+     INSTMANFLAGS = -m 0444
+     INSTDATFLAGS = -m 0444
+    INSTKMEMFLAGS = -g kmem -m 2755
+
+      PROJECTROOT = /v/X11R5
+
+     TOP_INCLUDES = -I$(INCROOT)
+
+      CDEBUGFLAGS = -O
+        CCOPTIONS =
+
+      ALLINCLUDES = $(INCLUDES) $(EXTRA_INCLUDES) $(TOP_INCLUDES) $(STD_INCLUDES)
+       ALLDEFINES = $(ALLINCLUDES) $(STD_DEFINES) $(EXTRA_DEFINES) $(PROTO_DEFINES) $(DEFINES)
+           CFLAGS = $(CDEBUGFLAGS) $(CCOPTIONS) $(ALLDEFINES)
+        LINTFLAGS = $(LINTOPTS) -DLINT $(ALLDEFINES)
+
+           LDLIBS = $(SYS_LIBRARIES) $(EXTRA_LIBRARIES)
+
+        LDOPTIONS = $(CDEBUGFLAGS) $(CCOPTIONS) $(LOCAL_LDFLAGS) -L$(USRLIBDIR)
+
+   LDCOMBINEFLAGS = -X -r
+      DEPENDFLAGS =
+
+        MACROFILE = sun.cf
+           RM_CMD = $(RM) *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a .emacs_* tags TAGS make.log MakeOut
+
+    IMAKE_DEFINES =
+
+         IRULESRC = $(CONFIGDIR)
+        IMAKE_CMD = $(IMAKE) -DUseInstalled -I$(IRULESRC) $(IMAKE_DEFINES)
+
+     ICONFIGFILES = $(IRULESRC)/Imake.tmpl $(IRULESRC)/Imake.rules \
+                       $(IRULESRC)/Project.tmpl $(IRULESRC)/site.def \
+                       $(IRULESRC)/$(MACROFILE) $(EXTRA_ICONFIGFILES)
+
+# -------------------------------------------------------------------------
+# X Window System Build Parameters
+# $XConsortium: Project.tmpl,v 1.138 91/09/10 09:02:12 rws Exp $
+
+# -------------------------------------------------------------------------
+# X Window System make variables; this need to be coordinated with rules
+
+          PATHSEP = /
+        USRLIBDIR = /v/X11R5/lib
+           BINDIR = /v/X11R5/bin
+          INCROOT = /v/X11R5/include
+     BUILDINCROOT = $(TOP)
+      BUILDINCDIR = $(BUILDINCROOT)/X11
+      BUILDINCTOP = ..
+           INCDIR = $(INCROOT)/X11
+           ADMDIR = /usr/adm
+           LIBDIR = $(USRLIBDIR)/X11
+        CONFIGDIR = $(LIBDIR)/config
+       LINTLIBDIR = $(USRLIBDIR)/lint
+
+          FONTDIR = $(LIBDIR)/fonts
+         XINITDIR = $(LIBDIR)/xinit
+           XDMDIR = $(LIBDIR)/xdm
+           TWMDIR = $(LIBDIR)/twm
+          MANPATH = /v/X11R5/man
+    MANSOURCEPATH = $(MANPATH)/man
+        MANSUFFIX = n
+     LIBMANSUFFIX = 3
+           MANDIR = $(MANSOURCEPATH)$(MANSUFFIX)
+        LIBMANDIR = $(MANSOURCEPATH)$(LIBMANSUFFIX)
+           NLSDIR = $(LIBDIR)/nls
+        PEXAPIDIR = $(LIBDIR)/PEX
+      XAPPLOADDIR = $(LIBDIR)/app-defaults
+       FONTCFLAGS = -t
+
+     INSTAPPFLAGS = $(INSTDATFLAGS)
+
+            IMAKE = imake
+           DEPEND = makedepend
+              RGB = rgb
+
+            FONTC = bdftopcf
+
+        MKFONTDIR = mkfontdir
+        MKDIRHIER = /bin/sh $(BINDIR)/mkdirhier
+
+        CONFIGSRC = $(TOP)/config
+       DOCUTILSRC = $(TOP)/doc/util
+        CLIENTSRC = $(TOP)/clients
+          DEMOSRC = $(TOP)/demos
+           LIBSRC = $(TOP)/lib
+          FONTSRC = $(TOP)/fonts
+       INCLUDESRC = $(TOP)/X11
+        SERVERSRC = $(TOP)/server
+          UTILSRC = $(TOP)/util
+        SCRIPTSRC = $(UTILSRC)/scripts
+       EXAMPLESRC = $(TOP)/examples
+       CONTRIBSRC = $(TOP)/../contrib
+           DOCSRC = $(TOP)/doc
+           RGBSRC = $(TOP)/rgb
+        DEPENDSRC = $(UTILSRC)/makedepend
+         IMAKESRC = $(CONFIGSRC)
+         XAUTHSRC = $(LIBSRC)/Xau
+          XLIBSRC = $(LIBSRC)/X
+           XMUSRC = $(LIBSRC)/Xmu
+       TOOLKITSRC = $(LIBSRC)/Xt
+       AWIDGETSRC = $(LIBSRC)/Xaw
+       OLDXLIBSRC = $(LIBSRC)/oldX
+      XDMCPLIBSRC = $(LIBSRC)/Xdmcp
+      BDFTOSNFSRC = $(FONTSRC)/bdftosnf
+      BDFTOSNFSRC = $(FONTSRC)/clients/bdftosnf
+      BDFTOPCFSRC = $(FONTSRC)/clients/bdftopcf
+     MKFONTDIRSRC = $(FONTSRC)/clients/mkfontdir
+         FSLIBSRC = $(FONTSRC)/lib/fs
+    FONTSERVERSRC = $(FONTSRC)/server
+     EXTENSIONSRC = $(TOP)/extensions
+         XILIBSRC = $(EXTENSIONSRC)/lib/xinput
+      PHIGSLIBSRC = $(EXTENSIONSRC)/lib/PEX
+
+# $XConsortium: sunLib.tmpl,v 1.14.1.1 92/03/17 14:58:46 rws Exp $
+
+SHLIBLDFLAGS = -assert pure-text
+PICFLAGS = -fPIC
+
+  DEPEXTENSIONLIB =
+     EXTENSIONLIB = -lXext
+
+          DEPXLIB = $(DEPEXTENSIONLIB)
+             XLIB = $(EXTENSIONLIB) -lX11
+
+        DEPXMULIB = $(USRLIBDIR)/libXmu.sa.$(SOXMUREV)
+       XMULIBONLY = -lXmu
+           XMULIB = -lXmu
+
+       DEPOLDXLIB =
+          OLDXLIB = -loldX
+
+      DEPXTOOLLIB = $(USRLIBDIR)/libXt.sa.$(SOXTREV)
+         XTOOLLIB = -lXt
+
+        DEPXAWLIB = $(USRLIBDIR)/libXaw.sa.$(SOXAWREV)
+           XAWLIB = -lXaw
+
+        DEPXILIB =
+           XILIB = -lXi
+
+        SOXLIBREV = 4.10
+          SOXTREV = 4.10
+         SOXAWREV = 5.0
+        SOOLDXREV = 4.10
+         SOXMUREV = 4.10
+        SOXEXTREV = 4.10
+      SOXINPUTREV = 4.10
+
+      DEPXAUTHLIB = $(USRLIBDIR)/libXau.a
+         XAUTHLIB =  -lXau
+      DEPXDMCPLIB = $(USRLIBDIR)/libXdmcp.a
+         XDMCPLIB =  -lXdmcp
+
+        DEPPHIGSLIB = $(USRLIBDIR)/libphigs.a
+           PHIGSLIB =  -lphigs
+
+       DEPXBSDLIB = $(USRLIBDIR)/libXbsd.a
+          XBSDLIB =  -lXbsd
+
+ LINTEXTENSIONLIB = $(LINTLIBDIR)/llib-lXext.ln
+         LINTXLIB = $(LINTLIBDIR)/llib-lX11.ln
+          LINTXMU = $(LINTLIBDIR)/llib-lXmu.ln
+        LINTXTOOL = $(LINTLIBDIR)/llib-lXt.ln
+          LINTXAW = $(LINTLIBDIR)/llib-lXaw.ln
+           LINTXI = $(LINTLIBDIR)/llib-lXi.ln
+        LINTPHIGS = $(LINTLIBDIR)/llib-lphigs.ln
+
+          DEPLIBS = $(DEPXAWLIB) $(DEPXMULIB) $(DEPXTOOLLIB) $(DEPXLIB)
+
+         DEPLIBS1 = $(DEPLIBS)
+         DEPLIBS2 = $(DEPLIBS)
+         DEPLIBS3 = $(DEPLIBS)
+
+# -------------------------------------------------------------------------
+# Imake rules for building libraries, programs, scripts, and data files
+# rules:  $XConsortium: Imake.rules,v 1.123 91/09/16 20:12:16 rws Exp $
+
+# -------------------------------------------------------------------------
+# start of Imakefile
+
+#
+#
+#
+
+          OBJS = Cl.o ClHTTP.o ClFTP.o ClMisc.o ClConnection.o
+          SRCS = Cl.c ClHTTP.c ClFTP.c ClMisc.c ClConnection.c
+       SUBDIRS = WWWLibrary
+       DEFINES = -I$(SUBDIRS)
+
+all:: libCl.a
+
+libCl.a:  $(OBJS)
+       $(RM) $@
+       $(AR) $@  $(OBJS)
+       $(RANLIB) $@
+
+all::
+       @case '${MFLAGS}' in *[ik]*) set +e;; esac; \
+       for i in $(SUBDIRS) ;\
+       do \
+       (cd $$i ; echo "making" all "in $(CURRENT_DIR)/$$i..."; \
+       $(MAKE) $(MFLAGS)  all); \
+       done
+
+Makefiles::
+       @case '${MFLAGS}' in *[ik]*) set +e;; esac; \
+       for i in $(SUBDIRS) ;\
+       do \
+       echo "making Makefiles in $(CURRENT_DIR)/$$i..."; \
+       case "$$i" in \
+       ./?*/?*/?*/?*) newtop=../../../../ sub=subsubsubsub;; \
+       ./?*/?*/?*) newtop=../../../ sub=subsubsub;; \
+       ./?*/?*) newtop=../../ sub=subsub;; \
+       ./?*) newtop=../ sub=sub;; \
+       */?*/?*/?*) newtop=../../../../ sub=subsubsubsub;; \
+       */?*/?*) newtop=../../../ sub=subsubsub;; \
+       */?*) newtop=../../ sub=subsub;; \
+       *) newtop=../ sub=sub;; \
+       esac; \
+       case "$(TOP)" in \
+       /?*) newtop= upprefix= ;; \
+       *) upprefix=../ ;; \
+       esac; \
+       $(MAKE) $${sub}dirMakefiles UPPREFIX=$$upprefix NEWTOP=$$newtop \
+       MAKEFILE_SUBDIR=$$i NEW_CURRENT_DIR=$(CURRENT_DIR)/$$i;\
+       done
+
+subdirMakefiles:
+       $(RM) $(MAKEFILE_SUBDIR)/Makefile.bak
+       -@if [ -f $(MAKEFILE_SUBDIR)/Makefile ]; then set -x; \
+       $(MV) $(MAKEFILE_SUBDIR)/Makefile $(MAKEFILE_SUBDIR)/Makefile.bak; \
+       else exit 0; fi
+       cd $(MAKEFILE_SUBDIR); $(IMAKE_CMD) -DTOPDIR=$(UPPREFIX)$(TOP) -DCURDIR=$(NEW_CURRENT_DIR); \
+       $(MAKE) $(MFLAGS) Makefiles
+
+subsubdirMakefiles:
+       $(RM) $(MAKEFILE_SUBDIR)/Makefile.bak
+       -@if [ -f $(MAKEFILE_SUBDIR)/Makefile ]; then set -x; \
+       $(MV) $(MAKEFILE_SUBDIR)/Makefile $(MAKEFILE_SUBDIR)/Makefile.bak; \
+       else exit 0; fi
+       cd $(MAKEFILE_SUBDIR); $(IMAKE_CMD) -DTOPDIR=$(UPPREFIX)$(UPPREFIX)$(TOP) -DCURDIR=$(NEW_CURRENT_DIR); \
+       $(MAKE) $(MFLAGS) Makefiles
+
+subsubsubdirMakefiles:
+       $(RM) $(MAKEFILE_SUBDIR)/Makefile.bak
+       -@if [ -f $(MAKEFILE_SUBDIR)/Makefile ]; then set -x; \
+       $(MV) $(MAKEFILE_SUBDIR)/Makefile $(MAKEFILE_SUBDIR)/Makefile.bak; \
+       else exit 0; fi
+       cd $(MAKEFILE_SUBDIR); $(IMAKE_CMD) -DTOPDIR=$(UPPREFIX)$(UPPREFIX)$(UPPREFIX)$(TOP) -DCURDIR=$(NEW_CURRENT_DIR); \
+       $(MAKE) $(MFLAGS) Makefiles
+
+subsubsubsubdirMakefiles:
+       $(RM) $(MAKEFILE_SUBDIR)/Makefile.bak
+       -@if [ -f $(MAKEFILE_SUBDIR)/Makefile ]; then set -x; \
+       $(MV) $(MAKEFILE_SUBDIR)/Makefile $(MAKEFILE_SUBDIR)/Makefile.bak; \
+       else exit 0; fi
+       cd $(MAKEFILE_SUBDIR); $(IMAKE_CMD) -DTOPDIR=$(UPPREFIX)$(UPPREFIX)$(UPPREFIX)$(UPPREFIX)$(TOP) -DCURDIR=$(NEW_CURRENT_DIR); \
+       $(MAKE) $(MFLAGS) Makefiles
+
+depend::
+       $(DEPEND) $(DEPENDFLAGS) -s "# DO NOT DELETE" -- $(ALLDEFINES) -- $(SRCS)
+
+depend::
+       @case '${MFLAGS}' in *[ik]*) set +e;; esac; \
+       for i in $(SUBDIRS) ;\
+       do \
+       (cd $$i ; echo "depending" "in $(CURRENT_DIR)/$$i..."; \
+       $(MAKE) $(MFLAGS)  depend); \
+       done
+
+# -------------------------------------------------------------------------
+# common rules for all Makefiles - do not edit
+
+emptyrule::
+
+clean::
+       $(RM_CMD) "#"*
+
+Makefile::
+       -@if [ -f Makefile ]; then set -x; \
+       $(RM) Makefile.bak; $(MV) Makefile Makefile.bak; \
+       else exit 0; fi
+       $(IMAKE_CMD) -DTOPDIR=$(TOP) -DCURDIR=$(CURRENT_DIR)
+
+tags::
+       $(TAGS) -w *.[ch]
+       $(TAGS) -xw *.[ch] > TAGS
+
+saber:
+       # load $(ALLDEFINES) $(SRCS)
+
+osaber:
+       # load $(ALLDEFINES) $(OBJS)
+
+# -------------------------------------------------------------------------
+# empty rules for directories that do not have SUBDIRS - do not edit
+
+install::
+       @echo "install in $(CURRENT_DIR) done"
+
+install.man::
+       @echo "install.man in $(CURRENT_DIR) done"
+
+Makefiles::
+
+includes::
+
+# -------------------------------------------------------------------------
+# dependencies generated by makedepend
+
diff --git a/Cl/WWWLibrary/HTAccess.c b/Cl/WWWLibrary/HTAccess.c
new file mode 100644 (file)
index 0000000..3f7af2c
--- /dev/null
@@ -0,0 +1,520 @@
+/*             Access Manager                                  HTAccess.c
+**             ==============
+*/
+
+
+#include "HTParse.h"
+#include "HTUtils.h"
+#include "WWW.h"
+#include "HTAnchor.h"
+#include "HTFTP.h"
+#include "HTTP.h"
+#include "HTFile.h"
+#include <errno.h>
+#include <stdio.h>
+
+#include "tcp.h"
+#include "HText.h"
+#include "HTNews.h"
+#include "HTGopher.h"
+#include "HTBrowse.h"          /* Need global HTClientHost */
+
+#include "HTAccess.h"
+
+#ifdef ERWISE
+#include "Cl.h"
+#endif
+
+#define HT_NO_DATA -9999
+
+PUBLIC int HTDiag = 0;         /* Diagnostics: load source as text */
+
+/*     Telnet or "rlogin" access
+**     -------------------------
+*/
+PRIVATE int remote_session ARGS2(char *, access, char *, host)
+{
+       char * user = host;
+       char * hostname = strchr(host, '@');
+       char * port = strchr(host, ':');
+       char   command[256];
+       BOOL rlogin = strcmp(access, "rlogin");
+       
+       if (hostname) {
+           *hostname++ = 0;    /* Split */
+       } else {
+           hostname = host;
+           user = 0;           /* No user specified */
+       }
+       if (port) *port++ = 0;  /* Split */
+
+#ifdef unix
+       sprintf(command, "%s%s%s %s %s", access,
+               user ? " -l " : "",
+               user ? user : "",
+               hostname,
+               port ? port : "");
+       if (TRACE) fprintf(stderr, "HTaccess: Command is: %s\n", command);
+       system(command);
+       return HT_NO_DATA;              /* Ok - it was done but no data */
+#define TELNET_DONE
+#endif
+
+#ifdef MULTINET                                /* VMS varieties */
+       if (!rlogin) {                  /* telnet */
+           if (user) printf("When you are connected, log in as %s\n", user);
+           sprintf(command, "TELNET %s%s %s",
+               port ? "/PORT=" : "",
+               port ? port : "",
+               hostname);
+       } else {
+           sprintf(command, "RLOGIN%s%s%s%s %s", access,
+               user ? "/USERNAME=" : "",
+               user ? user : "",
+               port ? "/PORT=" : "",
+               port ? port : "",
+               hostname);
+       }
+       if (TRACE) fprintf(stderr, "HTaccess: Command is: %s\n", command);
+       system(command);
+       return HT_NO_DATA;              /* Ok - it was done but no data */
+#define TELNET_DONE
+#endif
+
+#ifdef UCX
+#define SIMPLE_TELNET
+#endif
+#ifdef VM
+#define SIMPLE_TELNET
+#endif
+#ifdef SIMPLE_TELNET
+       if (!rlogin) {                  /* telnet only */
+           if (user) printf("When you are connected, log in as %s\n", user);
+           sprintf(command, "TELNET  %s",      /* @@ Bug: port ignored */
+               hostname);
+           if (TRACE) fprintf(stderr, "HTaccess: Command is: %s\n", command);
+           system(command);
+           return HT_NO_DATA;          /* Ok - it was done but no data */
+       }
+#endif
+
+#ifndef TELNET_DONE
+       fprintf(stderr,
+       "Sorry, this browser was compiled without the %s access option.\n",
+               access);
+       fprintf(stderr,
+       "\nTo access the information you must %s to %s", access, hostname);
+       if (port) fprintf(stderr," (port %s)", port);
+       if (user) fprintf(stderr," logging in with username %s", user);
+       fprintf(stderr, ".\n");
+       return -1;
+#endif
+}
+
+/*     Open a file descriptor for a document
+**     -------------------------------------
+**
+** On entry,
+**     addr            must point to the fully qualified hypertext reference.
+**
+** On exit,
+**     returns         <0      Error has occured.
+**                     >=0     Value of file descriptor or socket to be used
+**                              to read data.
+**     *pFormat        Set to the format of the file, if known.
+**                     (See WWW.h)
+**
+*/
+PRIVATE int HTOpen ARGS3(
+       CONST char *,addr1,
+       HTFormat *,pFormat,
+       HTParentAnchor *,anchor)
+{
+    char * access=0;   /* Name of access method */
+    int status;
+    char * gateway;
+    char * gateway_parameter;
+    char * addr = (char *)malloc(strlen(addr1)+1);
+    
+    if (addr == NULL) outofmem(__FILE__, "HTOpen");
+    strcpy(addr, addr1);                       /* Copy to play with */
+    
+    access =  HTParse(addr, "file:", PARSE_ACCESS);
+    
+    gateway_parameter = (char *)malloc(strlen(access)+20);
+    if (gateway_parameter == NULL) outofmem(__FILE__, "HTOpen");
+    strcpy(gateway_parameter, "WWW_");
+    strcat(gateway_parameter, access);
+    strcat(gateway_parameter, "_GATEWAY");
+    gateway = getenv(gateway_parameter);
+    free(gateway_parameter);
+
+    if (gateway) {
+       status = HTLoadHTTP(addr, gateway, anchor, HTDiag);
+#ifndef CURSES
+       if (status<0) fprintf(stderr,   /* For simple users */
+           "Cannot connect to information gateway %s\n", gateway);
+#endif 
+    } else if (0==strcmp(access, "http")) {
+        status = HTLoadHTTP(addr, 0, anchor, HTDiag);
+#ifndef CURSES
+       if (status<0) fprintf(stderr,   /* For simple users */
+               "Cannot connect to information server.\n");
+#endif
+    } else if (0==strcmp(access, "file")) {
+        status = HTOpenFile(addr, pFormat, anchor);
+
+    } else if (0==strcmp(access, "news")) {
+        status = HTLoadNews(addr, anchor, HTDiag);
+       if (status>0) status = HT_LOADED;
+
+    } else if (0==strcmp(access, "gopher")) {
+        status = HTLoadGopher(addr, anchor, HTDiag);
+       if (status>0) status = HT_LOADED;
+
+    } else if (!strcmp(access, "telnet") ||            /* TELNET */
+       !strcmp(access, "rlogin")) {                    /* RLOGIN */
+        char * host = HTParse(addr, "", PARSE_HOST);
+       remote_session(access, host);
+       free(host);
+       
+    } else if (0==strcmp(access, "wais")) {
+        user_message(
+"HTAccess: For WAIS access set WWW_wais_GATEWAY to gateway address.\n");
+    } else {
+
+        user_message(
+       "HTAccess: name scheme `%s' unknown by this browser version.\n",
+                access);
+        status = -1;
+    }
+    free(access);
+    free(addr);
+    return status;
+}
+
+
+/*     Close socket opened for reading a file
+**     --------------------------------------
+**
+*/
+#ifdef ERWISE
+PUBLIC int HTClose ARGS1(int,soc)
+#else
+PRIVATE int HTClose ARGS1(int,soc)
+#endif
+{
+    return HTFTP_close_file(soc);
+}
+
+
+/*             Load a document
+**             ---------------
+**
+**    On Entry,
+**       anchor            is the node_anchor for the document
+**        full_address      The address of the file to be accessed.
+**
+**    On Exit,
+**        returns    YES     Success in opening file
+**                   NO      Failure 
+**
+*/
+
+PUBLIC BOOL HTLoadDocument ARGS3(HTParentAnchor *,anchor,
+       CONST char *,full_address,
+       BOOL,   filter)
+
+{
+    int                new_file_number;
+    HTFormat    format;
+    HText *    text;
+
+    if (text=(HText *)HTAnchor_document(anchor)) {     /* Already loaded */
+#ifdef ERWISE
+        /*
+         * do NOT do this
+         */
+        fprintf(stderr, "HTBrowse: Document already in memory.\n");
+
+        WWWErwiseStatus = CL_ALREADY_LOADED;
+
+        return YES;
+#else
+        if (TRACE) fprintf(stderr, "HTBrowse: Document already in memory.\n");
+        HText_select(text);
+       return YES;
+#endif
+    }
+    
+#ifdef CURSES
+      prompt_set("Retrieving document...");
+#endif
+    if (filter) {
+        new_file_number = 0;
+       format = WWW_HTML;
+    } else {
+       new_file_number = HTOpen(full_address, &format, anchor);
+    }
+/*     Log the access if necessary
+*/
+    if (logfile) {
+       time_t theTime;
+       time(&theTime);
+       fprintf(logfile, "%24.24s %s %s %s\n",
+           ctime(&theTime),
+           HTClientHost ? HTClientHost : "local",
+           new_file_number<0 ? "FAIL" : "GET",
+           full_address);
+       fflush(logfile);        /* Actually update it on disk */
+       if (TRACE) fprintf(stderr, "Log: %24.24s %s %s %s\n",
+           ctime(&theTime),
+           HTClientHost ? HTClientHost : "local",
+           new_file_number<0 ? "FAIL" : "GET",
+           full_address);
+    }
+    
+
+    if (new_file_number == HT_LOADED) {
+       if (TRACE) {
+           printf("HTAccess: `%s' has been accessed.\n",
+           full_address);
+       }
+#ifdef ERWISE
+        WWWErwiseStatus = CL_COMPLETED;
+        WWWErwiseConnection->fd = -1;
+#endif
+       return YES;
+    }
+    
+    if (new_file_number == HT_NO_DATA) {
+       if (TRACE) {
+           printf("HTAccess: `%s' has been accessed, No data left.\n",
+           full_address);
+       }
+#ifdef ERWISE
+        WWWErwiseStatus = CL_FAILED;
+#endif
+       return NO;
+    }
+    
+    if (new_file_number<0) {                 /* Failure in accessing a file */
+
+#ifdef CURSES
+        user_message("Can't access `%s'", full_address);
+#else
+       printf("\nWWW: Can't access `%s'\n", full_address);
+#endif
+#ifdef ERWISE
+        WWWErwiseStatus = CL_FAILED;
+        return NO;
+#endif
+       if (!HTMainText){
+            exit(2);                           /* Can't get first page */
+        } else {
+            return NO;
+        }
+    }
+    
+    if (TRACE) {
+       printf("WWW: Opened `%s' as fd %d\n",
+       full_address, new_file_number);
+    }
+
+#ifdef ERWISE
+    /*
+     * Do the rest elsewhere (if this connection can be loaded
+     * using non blocking transfer)
+     */
+
+    if(WWWErwiseConnection->function) {
+        WWWErwiseConnection->fd = new_file_number;
+
+        return YES;
+    }
+#endif
+
+    HTParseFormat(HTDiag ? WWW_PLAINTEXT : format, anchor, new_file_number);
+    
+    HTClose(new_file_number);
+    
+    return YES;
+    
+} /* HTLoadDocument */
+
+
+/*             Load a document from absolute name
+**             ---------------
+**
+**    On Entry,
+**        relative_name     The relative address of the file to be accessed.
+**
+**    On Exit,
+**        returns    YES     Success in opening file
+**                   NO      Failure 
+**
+**
+*/
+
+PUBLIC BOOL HTLoadAbsolute ARGS2(CONST char *,addr, BOOL, filter)
+{
+   return HTLoadDocument(
+                       HTAnchor_parent(HTAnchor_findAddress(addr)),
+                       addr, filter);
+}
+
+
+/*             Load a document from relative name
+**             ---------------
+**
+**    On Entry,
+**        relative_name     The relative address of the file to be accessed.
+**
+**    On Exit,
+**        returns    YES     Success in opening file
+**                   NO      Failure 
+**
+**
+*/
+
+PUBLIC BOOL HTLoadRelative ARGS1(CONST char *,relative_name)
+{
+    char *             full_address = 0;
+    BOOL                       result;
+    char *             mycopy = 0;
+    char *             stripped = 0;
+    char *             current_address =
+                               HTAnchor_address((HTAnchor*)HTMainAnchor);
+
+    StrAllocCopy(mycopy, relative_name);
+
+    stripped = HTStrip(mycopy);
+    full_address = HTParse(stripped,
+                  current_address,
+                  PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
+    result = HTLoadAbsolute(full_address, NO);
+    free(full_address);
+    free(current_address);
+    return result;
+}
+
+
+/*             Load if necessary, and select an anchor
+**             --------------------------------------
+**
+**    On Entry,
+**        destination              The child or parenet anchor to be loaded.
+**
+**    On Exit,
+**        returns    YES     Success
+**                   NO      Failure 
+**
+*/
+
+PUBLIC BOOL HTLoadAnchor ARGS1(HTAnchor *,destination)
+{
+    HTParentAnchor * parent;
+
+    if (!destination) return NO;       /* No link */
+    
+    parent  = HTAnchor_parent(destination);
+    
+    if ( /* HTAnchor_document (parent) == NULL) { */ parent != HTMainAnchor)   {       /* If not already loaded */
+        BOOL result;
+        char * address = HTAnchor_address((HTAnchor*) parent);
+       result = HTLoadDocument(parent, address, NO);
+       free(address);
+       if (!result) return NO;
+    }
+    
+    if (destination != (HTAnchor *)parent)     /* If child anchor */
+        HText_selectAnchor(HTMainText, 
+               (HTChildAnchor*)destination); /* Double display? @@ */
+               
+    return YES;
+       
+} /* HTLoadAnchor */
+
+
+#ifndef ERWISE
+/*             Search
+**             ------
+**  Performs a keyword search on word given by the user. Adds the keyword to 
+**  the end of the current address and attempts to open the new address.
+**
+**  On Entry,
+**       *keywords     space-separated keyword list or similar search list
+**     HTMainAnchor    global must be valid.
+*/
+
+PUBLIC BOOL HTSearch ARGS1(char *,keywords)
+
+{
+    char * p;            /* pointer to first non-blank */
+    char * q, *s;
+    char * address = HTAnchor_address((HTAnchor*)HTMainAnchor);
+    BOOL result;
+    
+    p = HTStrip(keywords);
+    for (q=p; *q; q++)
+        if (WHITE(*q)) {
+           *q = '+';
+       }
+
+    s=strchr(address, '?');            /* Find old search string */
+    if (s) *s = 0;                             /* Chop old search off */
+
+    StrAllocCat(address, "?");
+    StrAllocCat(address, p);
+
+    result = HTLoadRelative(address);
+    free(address);
+    return result;
+    
+}
+#else /* ERWISE */
+
+/*
+ * Why everything is so hardcoded ???? 
+ */
+
+PUBLIC char *HTSearchAddress ARGS1(char *,keywords)
+{
+    char * p;             /* pointer to first non-blank */
+    char * q, *s;
+    char * address = HTAnchor_address((HTAnchor*)HTMainAnchor);
+    char * current_address;
+    char * mycopy;
+    char * stripped;
+    char * full_address;
+
+    
+    p = HTStrip(keywords);
+    for (q=p; *q; q++)
+        if (WHITE(*q)) {
+            *q = '+';
+        }
+
+    s=strchr(address, '?');             /* Find old search string */
+    if (s) *s = 0;                              /* Chop old search off */
+
+    StrAllocCat(address, "?");
+    StrAllocCat(address, p);
+
+    StrAllocCopy(mycopy, address);
+    
+    current_address = HTAnchor_address((HTAnchor*)HTMainAnchor);
+
+    stripped = HTStrip(mycopy);
+
+    full_address = HTParse(stripped,
+                   current_address,
+                   PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
+
+    free(address);
+    
+    return full_address;
+}
+
+#endif /* ERWISE */
+
diff --git a/Cl/WWWLibrary/HTAccess.h b/Cl/WWWLibrary/HTAccess.h
new file mode 100644 (file)
index 0000000..ecbc165
--- /dev/null
@@ -0,0 +1,114 @@
+/*             Access Manager                                  HTAccess.h
+**             ==============
+*/
+
+#ifndef HTACCESS_H
+#define HTACCESS_H
+
+/*     Flag to allow source to be loaded as text
+*/
+extern int HTDiag;
+
+/*     Open a file descriptor for a document
+**     -------------------------------------
+**
+** On entry,
+**     addr            must point to the fully qualified hypertext reference.
+**
+** On exit,
+**     returns         <0      Error has occured.
+**                     >=0     Value of file descriptor or socket to be used
+**                              to read data.
+**     *pFormat        Set to the format of the file, if known.
+**                     (See WWW.h)
+**
+** No longer public -- only used internally.
+*/
+/* extern int HTOpen PARAMS((CONST char * addr, HTFormat * format)); */
+
+
+/*     Close socket opened for reading a file
+**     --------------------------------------
+**
+*/
+extern int HTClose PARAMS((int soc));
+
+
+/*             Load a document
+**             ---------------
+**
+**    On Entry,
+**       anchor            is the node_anchor for the document
+**        full_address      The address of the file to be accessed.
+**
+**    On Exit,
+**        returns    YES     Success in opening file
+**                   NO      Failure 
+**
+*/
+
+extern BOOL HTLoadDocument PARAMS((HTParentAnchor * anchor,
+       CONST char * full_address,
+       BOOL    filter));
+
+
+
+/*             Load a document from relative name
+**             ---------------
+**
+**    On Entry,
+**        relative_name     The relative address of the file to be accessed.
+**
+**    On Exit,
+**        returns    YES     Success in opening file
+**                   NO      Failure 
+**
+**
+*/
+
+extern  BOOL HTLoadRelative PARAMS((CONST char * relative_name));
+
+/*             Load a document from relative name
+**             ---------------
+**
+**    On Entry,
+**        relative_name     The relative address of the file to be accessed.
+**
+**    On Exit,
+**        returns    YES     Success in opening file
+**                   NO      Failure 
+**
+**
+*/
+
+extern BOOL HTLoadAbsolute PARAMS((CONST char * addr, BOOL filter));
+
+
+/*             Load if necessary, and select an anchor
+**             --------------------------------------
+**
+**    On Entry,
+**        destination              The child or parenet anchor to be loaded.
+**
+**    On Exit,
+**        returns    YES     Success
+**                   NO      Failure 
+**
+*/
+
+extern BOOL HTLoadAnchor PARAMS((HTAnchor * destination));
+
+/*             Search
+**             ------
+**  Performs a keyword search on word given by the user. Adds the keyword to 
+**  the end of the current address and attempts to open the new address.
+**
+**  On Entry,
+**       *keywords     space-separated keyword list or similar search list
+**     HTMainAnchor    global must be valid.
+*/
+
+extern BOOL HTSearch PARAMS((char * keywords));
+
+
+#endif /* HTACCESS_H */
diff --git a/Cl/WWWLibrary/HTAnchor.c b/Cl/WWWLibrary/HTAnchor.c
new file mode 100644 (file)
index 0000000..d5a0053
--- /dev/null
@@ -0,0 +1,487 @@
+/*     Hypertext "Anchor" Object                               HTAnchor.c
+**     ==========================
+**
+** An anchor represents a region of a hypertext document which is linked to
+** another anchor in the same or a different document.
+**
+** History
+**
+**         Nov 1990  Written in Objective-C for the NeXT browser (TBL)
+**     24-Oct-1991 (JFG), written in C, browser-independant 
+**     21-Nov-1991 (JFG), first complete version
+**
+**     (c) Copyright CERN 1991 - See Copyright.html
+*/
+
+#define HASH_SIZE 101          /* Arbitrary prime. Memory/speed tradeoff */
+
+#include <ctype.h>
+#include "tcp.h"
+#include "HTAnchor.h"
+#include "HTUtils.h"
+#include "HTParse.h"
+
+typedef struct _HyperDoc Hyperdoc;
+#ifdef vms
+struct _HyperDoc {
+       int junk;       /* VMS cannot handle pointers to undefined structs */
+};
+#endif
+
+PRIVATE HTList **adult_table=0;  /* Point to table of lists of all parents */
+
+/*                             Creation Methods
+**                             ================
+**
+**     Do not use "new" by itself outside this module. In order to enforce
+**     consistency, we insist that you furnish more information about the
+**     anchor you are creating : use newWithParent or newWithAddress.
+*/
+
+PRIVATE HTParentAnchor * HTParentAnchor_new
+  NOARGS
+{
+  HTParentAnchor *newAnchor = 
+    (HTParentAnchor *) calloc (1, sizeof (HTParentAnchor));  /* zero-filled */
+  newAnchor->parent = newAnchor;
+  return newAnchor;
+}
+
+PRIVATE HTChildAnchor * HTChildAnchor_new
+  NOARGS
+{
+  return (HTChildAnchor *) calloc (1, sizeof (HTChildAnchor));  /* zero-filled */
+}
+
+
+/*     Case insensitive string comparison
+**     ----------------------------------
+** On entry,
+**     s       Points to one string, null terminated
+**     t       points to the other.
+** On exit,
+**     returns YES if the strings are equivalent ignoring case
+**             NO if they differ in more than  their case.
+*/
+
+PRIVATE BOOL equivalent
+  ARGS2 (CONST char *,s, CONST char *,t)
+{
+  if (s && t) {  /* Make sure they point to something */
+    for ( ; *s && *t ; s++, t++) {
+        if (TOUPPER(*s) != TOUPPER(*t))
+         return NO;
+    }
+    return TOUPPER(*s) == TOUPPER(*t);
+  } else
+    return s == t;  /* Two NULLs are equivalent, aren't they ? */
+}
+
+
+/*     Create new or find old sub-anchor
+**     ---------------------------------
+**
+**     This one is for a new anchor being edited into an existing
+**     document. The parent anchor must already exist.
+*/
+
+PRIVATE HTChildAnchor * HTAnchor_findChild
+  ARGS2 (HTParentAnchor *,parent, CONST char *,tag)
+{
+  HTChildAnchor *child;
+  HTList *kids;
+
+  if (! parent) {
+    if (TRACE) printf ("HTAnchor_findChild called with NULL parent.\n");
+    return NULL;
+  }
+  if (kids = parent->children) {  /* parent has children : search them */
+    if (tag && *tag) {         /* TBL */
+       while (child = HTList_nextObject (kids)) {
+           if (equivalent(child->tag, tag)) { /* Case sensitive 920226 */
+               if (TRACE) printf (
+              "Child anchor %p of parent %p with name `%s' already exists.\n",
+                   child, parent, tag);
+               return child;
+           }
+       }
+     }  /*  end if tag is void */
+  } else  /* parent doesn't have any children yet : create family */
+    parent->children = HTList_new ();
+
+  child = HTChildAnchor_new ();
+  if (TRACE) printf("new Anchor %p named `%s' is child of %p\n",
+                  child, (int)tag ? tag : (CONST char *)"" , parent); /* int for apollo */
+  HTList_addObject (parent->children, child);
+  child->parent = parent;
+  StrAllocCopy(child->tag, tag);
+  return child;
+}
+
+
+/*     Create or find a child anchor with a possible link
+**     --------------------------------------------------
+**
+**     Create new anchor with a given parent and possibly
+**     a name, and possibly a link to a _relatively_ named anchor.
+**     (Code originally in ParseHTML.h)
+*/
+PUBLIC HTChildAnchor * HTAnchor_findChildAndLink
+  ARGS4(
+       HTParentAnchor *,parent,        /* May not be 0 */
+       CONST char *,tag,       /* May be "" or 0 */
+       CONST char *,href,      /* May be "" or 0 */
+       HTLinkType *,ltype      /* May be 0 */
+       )
+{
+  HTChildAnchor * child = HTAnchor_findChild(parent, tag);
+  if (href && *href) {
+    char * parsed_address;
+    HTAnchor * dest;
+    parsed_address = HTParse(href, HTAnchor_address((HTAnchor *) parent),
+                            PARSE_ALL);
+    dest = HTAnchor_findAddress(parsed_address);
+    HTAnchor_link((HTAnchor *) child, dest, ltype);
+    free(parsed_address);
+  }
+  return child;
+}
+
+
+/*     Create new or find old named anchor
+**     -----------------------------------
+**
+**     This one is for a reference which is found in a document, and might
+**     not be already loaded.
+**     Note: You are not guaranteed a new anchor -- you might get an old one,
+**     like with fonts.
+*/
+
+HTAnchor * HTAnchor_findAddress
+  ARGS1 (CONST char *,address)
+{
+  char *tag = HTParse (address, "", PARSE_ANCHOR);  /* Anchor tag specified ? */
+
+  /* If the address represents a sub-anchor, we recursively load its parent,
+     then we create a child anchor within that document. */
+  if (*tag) {
+    char *docAddress = HTParse(address, "", PARSE_ACCESS | PARSE_HOST |
+                                           PARSE_PATH | PARSE_PUNCTUATION);
+    HTParentAnchor * foundParent =
+      (HTParentAnchor *) HTAnchor_findAddress (docAddress);
+    HTChildAnchor * foundAnchor = HTAnchor_findChild (foundParent, tag);
+    free (docAddress);
+    free (tag);
+    return (HTAnchor *) foundAnchor;
+  }
+  
+  else { /* If the address has no anchor tag, check whether we have this node */
+    int hash;
+    CONST char *p;
+    HTList * adults;
+    HTList *grownups;
+    HTParentAnchor * foundAnchor;
+
+    free (tag);
+    
+    /* Select list from hash table */
+    for(p=address, hash=0; *p; p++) hash = (hash * 3 + *p) % HASH_SIZE;
+    if (!adult_table)
+        adult_table = (HTList**) calloc(HASH_SIZE, sizeof(HTList*));
+    if (!adult_table[hash]) adult_table[hash] = HTList_new();
+    adults = adult_table[hash];
+
+    /* Search list for anchor */
+    grownups = adults;
+    while (foundAnchor = HTList_nextObject (grownups)) {
+      if (equivalent(foundAnchor->address, address)) {
+       if (TRACE) printf("Anchor %p with address `%s' already exists.\n",
+                         foundAnchor, address);
+       return (HTAnchor *) foundAnchor;
+      }
+    }
+    
+    /* Node not found : create new anchor */
+    foundAnchor = HTParentAnchor_new ();
+    if (TRACE) printf("New anchor %p has hash %d and address `%s'\n",
+       foundAnchor, hash, address);
+    StrAllocCopy(foundAnchor->address, address);
+    HTList_addObject (adults, foundAnchor);
+    return (HTAnchor *) foundAnchor;
+  }
+}
+
+
+/*     Delete an anchor and possibly related things (auto garbage collection)
+**     --------------------------------------------
+**
+**     The anchor is only deleted if the corresponding document is not loaded.
+**     All outgoing links from parent and children are deleted, and this anchor
+**     is removed from the sources list of all its targets.
+**     We also try to delete the targets whose documents are not loaded.
+**     If this anchor's source list is empty, we delete it and its children.
+*/
+
+PRIVATE void deleteLinks
+  ARGS1 (HTAnchor *,this)
+{
+  if (! this)
+    return;
+
+  /* Recursively try to delete target anchors */
+  if (this->mainLink.dest) {
+    HTParentAnchor *parent = this->mainLink.dest->parent;
+    HTList_removeObject (parent->sources, this);
+    if (! parent->document)  /* Test here to avoid calling overhead */
+      HTAnchor_delete (parent);
+  }
+  if (this->links) {  /* Extra destinations */
+    HTLink *target;
+    while (target = HTList_removeLastObject (this->links)) {
+      HTParentAnchor *parent = target->dest->parent;
+      HTList_removeObject (parent->sources, this);
+      if (! parent->document)  /* Test here to avoid calling overhead */
+       HTAnchor_delete (parent);
+    }
+  }
+}
+
+PUBLIC BOOL HTAnchor_delete
+  ARGS1 (HTParentAnchor *,this)
+{
+  HTChildAnchor *child;
+
+  /* Don't delete if document is loaded */
+  if (this->document)
+    return NO;
+
+  /* Recursively try to delete target anchors */
+  deleteLinks ((HTAnchor *) this);
+
+  if (! HTList_isEmpty (this->sources)) {  /* There are still incoming links */
+    /* Delete all outgoing links from children, if any */
+    HTList *kids = this->children;
+    while (child = HTList_nextObject (kids))
+      deleteLinks ((HTAnchor *) child);
+    return NO;  /* Parent not deleted */
+  }
+
+  /* No more incoming links : kill everything */
+  /* First, recursively delete children */
+  while (child = HTList_removeLastObject (this->children)) {
+    deleteLinks ((HTAnchor *) child);
+    free (child->tag);
+    free (child);
+  }
+
+  /* Now kill myself */
+  HTList_delete (this->children);
+  HTList_delete (this->sources);
+  free (this->address);
+  /* Devise a way to clean out the HTFormat if no longer needed (ref count?) */
+  free (this);
+  return YES;  /* Parent deleted */
+}
+
+
+/*             Move an anchor to the head of the list of its siblings
+**             ------------------------------------------------------
+**
+**     This is to ensure that an anchor which might have already existed
+**     is put in the correct order as we load the document.
+*/
+
+void HTAnchor_makeLastChild
+  ARGS1(HTChildAnchor *,this)
+{
+  if (this->parent != (HTParentAnchor *) this) {  /* Make sure it's a child */
+    HTList * siblings = this->parent->children;
+    HTList_removeObject (siblings, this);
+    HTList_addObject (siblings, this);
+  }
+}
+
+/*     Data access functions
+**     ---------------------
+*/
+
+extern HTParentAnchor * HTAnchor_parent
+  ARGS1 (HTAnchor *,this)
+{
+  return this ? this->parent : NULL;
+}
+
+void HTAnchor_setDocument
+  ARGS2 (HTParentAnchor *,this, HyperDoc *,doc)
+{
+  if (this)
+    this->document = doc;
+}
+
+HyperDoc * HTAnchor_document
+  ARGS1 (HTParentAnchor *,this)
+{
+  return this ? this->document : NULL;
+}
+
+
+/* We don't want code to change an address after anchor creation... yet ?
+void HTAnchor_setAddress
+  ARGS2 (HTAnchor *,this, char *,addr)
+{
+  if (this)
+    StrAllocCopy (this->parent->address, addr);
+}
+*/
+
+char * HTAnchor_address
+  ARGS1 (HTAnchor *,this)
+{
+  char *addr = NULL;
+  if (this) {
+    if (((HTParentAnchor *) this == this->parent) ||
+       !((HTChildAnchor *) this)->tag) {  /* it's an adult or no tag */
+      StrAllocCopy (addr, this->parent->address);
+    }
+    else {  /* it's a named child */
+      addr = malloc (2 + strlen (this->parent->address)
+                    + strlen (((HTChildAnchor *) this)->tag));
+      if (addr == NULL) outofmem(__FILE__, "HTAnchor_address");
+      sprintf (addr, "%s#%s", this->parent->address,
+              ((HTChildAnchor *) this)->tag);
+    }
+  }
+  return addr;
+}
+
+
+
+void HTAnchor_setFormat
+  ARGS2 (HTParentAnchor *,this, HTFormat *,form)
+{
+  if (this)
+    this->format = form;
+}
+
+HTFormat * HTAnchor_format
+  ARGS1 (HTParentAnchor *,this)
+{
+  return this ? this->format : NULL;
+}
+
+
+
+void HTAnchor_setIndex
+  ARGS1 (HTParentAnchor *,this)
+{
+  if (this)
+    this->isIndex = YES;
+}
+
+BOOL HTAnchor_isIndex
+  ARGS1 (HTParentAnchor *,this)
+{
+  return this ? this->isIndex : NO;
+}
+
+
+
+BOOL HTAnchor_hasChildren
+  ARGS1 (HTParentAnchor *,this)
+{
+  return this ? ! HTList_isEmpty(this->children) : NO;
+}
+
+/*     Title handling
+*/
+CONST char * HTAnchor_title
+  ARGS1 (HTParentAnchor *,this)
+{
+  return this ? this->title : 0;
+}
+
+void HTAnchor_setTitle
+  ARGS2(HTParentAnchor *,this, CONST char *,title)
+{
+  StrAllocCopy(this->title, title);
+}
+
+void HTAnchor_appendTitle
+  ARGS2(HTParentAnchor *,this, CONST char *,title)
+{
+  StrAllocCat(this->title, title);
+}
+
+/*     Link this Anchor to another given one
+**     -------------------------------------
+*/
+
+BOOL HTAnchor_link
+  ARGS3(HTAnchor *,source, HTAnchor *,destination, HTLinkType *,type)
+{
+  if (! (source && destination))
+    return NO;  /* Can't link to/from non-existing anchor */
+  if (TRACE) printf ("Linking anchor %p to anchor %p\n", source, destination);
+  if (! source->mainLink.dest) {
+    source->mainLink.dest = destination;
+    source->mainLink.type = type;
+  } else {
+    HTLink * newLink = (HTLink *) malloc (sizeof (HTLink));
+    if (newLink == NULL) outofmem(__FILE__, "HTAnchor_link");
+    newLink->dest = destination;
+    newLink->type = type;
+    if (! source->links)
+      source->links = HTList_new ();
+    HTList_addObject (source->links, newLink);
+  }
+  if (!destination->parent->sources)
+    destination->parent->sources = HTList_new ();
+  HTList_addObject (destination->parent->sources, source);
+  return YES;  /* Success */
+}
+
+
+/*     Manipulation of links
+**     ---------------------
+*/
+
+HTAnchor * HTAnchor_followMainLink
+  ARGS1 (HTAnchor *,this)
+{
+  return this->mainLink.dest;
+}
+
+HTAnchor * HTAnchor_followTypedLink
+  ARGS2 (HTAnchor *,this, HTLinkType *,type)
+{
+  if (this->mainLink.type == type)
+    return this->mainLink.dest;
+  if (this->links) {
+    HTList *links = this->links;
+    HTLink *link;
+    while (link = HTList_nextObject (links))
+      if (link->type == type)
+       return link->dest;
+  }
+  return NULL;  /* No link of this type */
+}
+
+BOOL HTAnchor_makeMainLink
+  ARGS2 (HTAnchor *,this, HTLink *,movingLink)
+{
+  /* Check that everything's OK */
+  if (! (this && HTList_removeObject (this->links, movingLink)))
+    return NO;  /* link not found or NULL anchor */
+  else {
+    /* First push current main link onto top of links list */
+    HTLink *newLink = (HTLink*) malloc (sizeof (HTLink));
+    if (newLink == NULL) outofmem(__FILE__, "HTAnchor_makeMainLink");
+    memcpy (newLink, & this->mainLink, sizeof (HTLink));
+    HTList_addObject (this->links, newLink);
+
+    /* Now make movingLink the new main link, and free it */
+    memcpy (& this->mainLink, movingLink, sizeof (HTLink));
+    free (movingLink);
+    return YES;
+  }
+}
diff --git a/Cl/WWWLibrary/HTAnchor.h b/Cl/WWWLibrary/HTAnchor.h
new file mode 100644 (file)
index 0000000..63f2f97
--- /dev/null
@@ -0,0 +1,265 @@
+/*     Hypertext "Anchor" Object                                    HTAnchor.h
+**     ==========================
+**
+**     An anchor represents a region of a hypertext document which is linked
+**     to another anchor in the same or a different document.
+*/
+
+#ifndef HTANCHOR_H
+#define HTANCHOR_H
+
+/* Version 0 (TBL) written in Objective-C for the NeXT browser */
+/* Version 1 of 24-Oct-1991 (JFG), written in C, browser-independant */
+
+#include "HTList.h"
+#include "HTAtom.h"
+
+#ifdef SHORT_NAMES
+#define HTAnchor_findChild                     HTAnFiCh
+#define HTAnchor_findChildAndLink              HTAnFiLi
+#define HTAnchor_findAddress                   HTAnFiAd
+#define HTAnchor_delete                                HTAnDele
+#define HTAnchor_makeLastChild                 HTAnMaLa
+#define HTAnchor_parent                                HTAnPare
+#define HTAnchor_setDocument                   HTAnSeDo
+#define HTAnchor_document                      HTAnDocu
+#define HTAnchor_setFormat                     HTAnSeFo
+#define HTAnchor_format                                HTAnForm
+#define HTAnchor_setIndex                      HTAnSeIn
+#define HTAnchor_isIndex                       HTAnIsIn
+#define HTAnchor_address                       HTAnAddr
+#define HTAnchor_hasChildren                   HTAnHaCh
+#define HTAnchor_title                         HTAnTitl
+#define HTAnchor_setTitle                      HTAnSeTi
+#define HTAnchor_appendTitle                   HTAnApTi
+#define HTAnchor_link                          HTAnLink
+#define HTAnchor_followMainLink                        HTAnFoMa
+#define HTAnchor_followTypedLink               HTAnFoTy
+#define HTAnchor_makeMainLink                  HTAnMaMa
+#endif
+
+/*                     Main definition of anchor
+**                     =========================
+*/
+
+typedef struct _HyperDoc HyperDoc;  /* Ready for forward references */
+typedef struct _HTAnchor HTAnchor;
+typedef struct _HTParentAnchor HTParentAnchor;
+
+#include "HTFormat.h"
+
+typedef HTAtom HTLinkType;
+
+typedef struct {
+  HTAnchor *   dest;           /* The anchor to which this leads */
+  HTLinkType * type;           /* Semantics of this link */
+} HTLink;
+
+struct _HTAnchor {             /* Generic anchor : just links */
+  HTLink       mainLink;       /* Main (or default) destination of this */
+  HTList *     links;          /* List of extra links from this, if any */
+  /* We separate the first link from the others to avoid too many small mallocs
+     involved by a list creation. Most anchors only point to one place. */
+  HTParentAnchor * parent;     /* Parent of this anchor (self for adults) */
+};
+
+struct _HTParentAnchor {
+  /* Common part from the generic anchor structure */
+  HTLink       mainLink;       /* Main (or default) destination of this */
+  HTList *     links;          /* List of extra links from this, if any */
+  HTParentAnchor * parent;     /* Parent of this anchor (self) */
+
+  /* ParentAnchor-specific information */
+  HTList *     children;       /* Subanchors of this, if any */
+  HTList *     sources;        /* List of anchors pointing to this, if any */
+  HyperDoc *   document;       /* The document within which this is an anchor */
+  char *       address;        /* Absolute address of this node */
+  HTFormat *   format;         /* Pointer to node format descriptor */
+  BOOL         isIndex;        /* Acceptance of a keyword search */
+  char *       title;          /* Title of document */
+};
+
+typedef struct {
+  /* Common part from the generic anchor structure */
+  HTLink       mainLink;       /* Main (or default) destination of this */
+  HTList *     links;          /* List of extra links from this, if any */
+  HTParentAnchor * parent;     /* Parent of this anchor */
+
+  /* ChildAnchor-specific information */
+  char *       tag;            /* Address of this anchor relative to parent */
+} HTChildAnchor;
+
+
+/*     Create new or find old sub-anchor
+**     ---------------------------------
+**
+**     This one is for a new anchor being edited into an existing
+**     document. The parent anchor must already exist.
+*/
+
+extern HTChildAnchor * HTAnchor_findChild
+  PARAMS(
+     (HTParentAnchor *parent,
+      CONST char *tag)
+  );
+
+/*     Create or find a child anchor with a possible link
+**     --------------------------------------------------
+**
+**     Create new anchor with a given parent and possibly
+**     a name, and possibly a link to a _relatively_ named anchor.
+**     (Code originally in ParseHTML.h)
+*/
+extern HTChildAnchor * HTAnchor_findChildAndLink
+  PARAMS((
+      HTParentAnchor * parent, /* May not be 0 */
+      CONST char * tag,         /* May be "" or 0 */
+      CONST char * href,       /* May be "" or 0 */
+      HTLinkType * ltype       /* May be 0 */
+  ));
+
+
+/*     Create new or find old named anchor
+**     -----------------------------------
+**
+**     This one is for a reference which is found in a document, and might
+**     not be already loaded.
+**     Note: You are not guaranteed a new anchor -- you might get an old one,
+**     like with fonts.
+*/
+
+extern HTAnchor * HTAnchor_findAddress
+  PARAMS(
+     (CONST char * address)
+     );
+
+
+/*     Delete an anchor and possibly related things (auto garbage collection)
+**     --------------------------------------------
+**
+**     The anchor is only deleted if the corresponding document is not loaded.
+**     All outgoing links from parent and children are deleted, and this anchor
+**     is removed from the sources list of all its targets.
+**     We also try to delete the targets whose documents are not loaded.
+**     If this anchor's source list is empty, we delete it and its children.
+*/
+
+extern BOOL HTAnchor_delete
+  PARAMS(
+     (HTParentAnchor *this)
+     );
+
+
+/*             Move an anchor to the head of the list of its siblings
+**             ------------------------------------------------------
+**
+**     This is to ensure that an anchor which might have already existed
+**     is put in the correct order as we load the document.
+*/
+
+extern void HTAnchor_makeLastChild
+  PARAMS(
+     (HTChildAnchor *this)
+     );
+
+/*     Data access functions
+**     ---------------------
+*/
+
+extern HTParentAnchor * HTAnchor_parent
+  PARAMS(
+     (HTAnchor *this)
+     );
+
+extern void HTAnchor_setDocument
+  PARAMS(
+     (HTParentAnchor *this, HyperDoc *doc)
+     );
+
+extern HyperDoc * HTAnchor_document
+  PARAMS(
+     (HTParentAnchor *this)
+     );
+/* We don't want code to change an address after anchor creation... yet ?
+extern void HTAnchor_setAddress
+  PARAMS(
+     (HTAnchor *this, char *addr)
+     );
+*/
+
+extern char * HTAnchor_address
+  PARAMS(
+     (HTAnchor *this)
+     );
+
+extern void HTAnchor_setFormat
+  PARAMS(
+     (HTParentAnchor *this, HTFormat *form)
+     );
+
+extern HTFormat * HTAnchor_format
+  PARAMS(
+     (HTParentAnchor *this)
+     );
+
+extern void HTAnchor_setIndex
+  PARAMS(
+     (HTParentAnchor *this)
+     );
+
+extern BOOL HTAnchor_isIndex
+  PARAMS(
+     (HTParentAnchor *this)
+     );
+
+extern BOOL HTAnchor_hasChildren
+  PARAMS(
+     (HTParentAnchor *this)
+     );
+
+/*     Title handling
+*/
+extern CONST char * HTAnchor_title
+  PARAMS(
+     (HTParentAnchor *this)
+     );
+
+extern void HTAnchor_setTitle
+  PARAMS(
+     (HTParentAnchor *this, CONST char * title)
+     );
+
+extern void HTAnchor_appendTitle
+  PARAMS(
+     (HTParentAnchor *this, CONST char * title)
+     );
+
+/*     Link this Anchor to another given one
+**     -------------------------------------
+*/
+
+extern BOOL HTAnchor_link
+  PARAMS(
+     (HTAnchor *source, HTAnchor *destination, HTLinkType *type)
+     );
+
+/*     Manipulation of links
+**     ---------------------
+*/
+
+extern HTAnchor * HTAnchor_followMainLink
+  PARAMS(
+     (HTAnchor *this)
+     );
+
+extern HTAnchor * HTAnchor_followTypedLink
+  PARAMS(
+     (HTAnchor *this, HTLinkType *type)
+     );
+
+extern BOOL HTAnchor_makeMainLink
+  PARAMS(
+     (HTAnchor *this, HTLink *movingLink)
+     );
+
+#endif /* HTANCHOR_H */
diff --git a/Cl/WWWLibrary/HTAtom.c b/Cl/WWWLibrary/HTAtom.c
new file mode 100644 (file)
index 0000000..44a099f
--- /dev/null
@@ -0,0 +1,74 @@
+/*                     Atoms: Names to numbers                 HTAtom.c
+**                     =======================
+**
+**     Atoms are names which are given representative pointer values
+**     so that they can be stored more efficiently, and comparisons
+**     for equality done more efficiently.
+**
+**     Atoms are kept in a hash table consisting of an array of linked lists.
+**
+** Authors:
+**     TBL     Tim Berners-Lee, WorldWideWeb project, CERN
+**     (c) Copyright CERN 1991 - See Copyright.html
+**
+*/
+#define HASH_SIZE      101             /* Tunable */
+#include "HTUtils.h"
+#include "HTAtom.h"
+
+#ifdef ERWISE
+#include <stdio.h>
+#endif
+
+PRIVATE HTAtom * hash_table[HASH_SIZE];
+PRIVATE BOOL initialised = NO;
+
+#ifdef __STDC__
+PUBLIC HTAtom * HTAtom_for(const char * string)
+#else
+PUBLIC HTAtom * HTAtom_for(string)
+    char * string;
+#endif
+{
+    int hash;
+    CONST char * p;
+    HTAtom * a;
+    
+    /*         First time around, clear hash table
+    */
+    if (!initialised) {
+        int i;
+       for (i=0; i<HASH_SIZE; i++)
+           hash_table[i] = (HTAtom *) 0;
+       initialised = YES;
+    }
+    
+    /*         Generate hash function
+    */
+    for(p=string, hash=0; *p; p++) {
+        hash = (hash * 3 + *p) % HASH_SIZE;
+    }
+    
+    /*         Search for the string in the list
+    */
+    for (a=hash_table[hash]; a; a=a->next) {
+       if (0==strcmp(a->name, string)) {
+           if (TRACE) printf("HTAtom: Old atom %d for `&s'\n", a, string);
+           return a;                           /* Found: return it */
+       }
+    }
+    
+    /*         Generate a new entry
+    */
+    a = (HTAtom *)malloc(sizeof(*a));
+    if (a == NULL) outofmem(__FILE__, "HTAtom_for");
+    a->name = (char *)malloc(strlen(string)+1);
+    if (a->name == NULL) outofmem(__FILE__, "HTAtom_for");
+    strcpy(a->name, string);
+    a->next = hash_table[hash];                /* Put onto the head of list */
+    hash_table[hash] = a;
+    if (TRACE) printf("HTAtom: New atom %d for `&s'\n", a, string);
+    return a;
+}
+
+
diff --git a/Cl/WWWLibrary/HTAtom.h b/Cl/WWWLibrary/HTAtom.h
new file mode 100644 (file)
index 0000000..2ebef26
--- /dev/null
@@ -0,0 +1,36 @@
+/*                     Atoms: Names to numbers                 HTAtom.h
+**                     =======================
+**
+**     Atoms are names which are given representative pointer values
+**     so that they can be stored more efficiently, and compaisons
+**     for equality done more efficiently.
+**
+**     HTAtom_for(string) returns a representative value such that it
+**     will always (within one run of the program) return the same
+**     value for the same given string.
+**
+** Authors:
+**     TBL     Tim Berners-Lee, WorldWideWeb project, CERN
+**
+**     (c) Copyright CERN 1991 - See Copyright.html
+**
+*/
+
+#ifndef HTATOM_H
+#define HTATOM_H
+
+typedef struct _HTAtom HTAtom;
+struct _HTAtom {
+       HTAtom *        next;
+       char *          name;
+}; /* struct _HTAtom */
+
+#ifdef __STDC__
+extern HTAtom * HTAtom_for(const char * string);
+#else
+extern HTAtom * HTAtom_for();
+#endif
+
+#define HTAtom_name(a) ((a)->name)
+
+#endif /* HTATOM_H */
diff --git a/Cl/WWWLibrary/HTBrowse.h b/Cl/WWWLibrary/HTBrowse.h
new file mode 100644 (file)
index 0000000..781015e
--- /dev/null
@@ -0,0 +1,36 @@
+/*             Declarations of things available from HTBrowse.c
+**             ------------------------------------------------
+**
+**     HTBrowse.c, the main program of a line mode browser,  leaves various
+**     public variables atteh disposal of its submodules.
+*/
+
+#ifndef HTBROWSE_H
+#define HTBROWSE_H
+
+#include "tcp.h"
+
+#ifdef SHORT_NAMES
+#define HTScreenHeight                 HTScHeig
+#define HTScreenWidth                  HTScWidt
+#define HTClientHost                   HTClHost
+#define display_anchors                        HTDiAnch
+#define interactive                    HTIntera
+#define reference_mark                 HTReMark
+#endif
+
+extern  int  WWW_TraceFlag;            /* Off unless -v option given */
+extern  int  HTScreenWidth;            /* By default */
+extern  int  HTScreenHeight;           /* Undefined */
+extern BOOL  display_anchors;          /* anchor will be shown in text? */
+extern  BOOL interactive;              /*  e.g. shows prompts etc */
+
+                                          
+extern FILE * logfile;                 /* File to output one-liners to */
+
+extern char * HTClientHost;            /* Name or number of telnetting host */
+extern char * reference_mark;          /* Format string for  [1] &c */
+extern char * end_mark;                /* Format string for  [End] */
+
+#endif /* HTBROWSE_H */
diff --git a/Cl/WWWLibrary/HTChunk.c b/Cl/WWWLibrary/HTChunk.c
new file mode 100644 (file)
index 0000000..320ab9b
--- /dev/null
@@ -0,0 +1,84 @@
+/*             Chunk handling: Flexible arrays
+**             ===============================
+**
+*/
+
+#include "HTUtils.h"
+#include "HTChunk.h"
+#include <stdio.h>
+/*     Create a chunk with a certain allocation unit
+**     --------------
+*/
+PUBLIC HTChunk * HTChunkCreate ARGS1 (int,grow)
+{
+    HTChunk * ch = (HTChunk *) malloc(sizeof(HTChunk));
+    if (!ch) return 0;
+    ch->data = 0;
+    ch->growby = grow;
+    ch->size = 0;
+    ch->allocated = 0;
+    return ch;
+}
+
+
+/*     Clear a chunk of all data
+**     --------------------------
+*/
+PUBLIC void HTChunkClear ARGS1 (HTChunk *,ch)
+{
+    if (ch->data) {
+       free(ch->data);
+       ch->data = 0;
+    }
+    ch->size = 0;
+    ch->allocated = 0;
+}
+
+
+/*     Append a character
+**     ------------------
+*/
+PUBLIC void HTChunkPutc ARGS2 (HTChunk *,ch, char,c)
+{
+    if (ch->size >= ch->allocated) {
+       ch->allocated = ch->allocated + ch->growby;
+        ch->data = ch->data ? (char *)realloc(ch->data, ch->allocated)
+                           : (char *)malloc(ch->allocated);
+      if (!ch->data) outofmem(__FILE__, "HTChunkPutc");
+    }
+    ch->data[ch->size++] = c;
+}
+
+
+/*     Ensure a certain size
+**     ---------------------
+*/
+PUBLIC void HTChunkEnsure ARGS2 (HTChunk *,ch, int,needed)
+{
+    if (needed <= ch->allocated) return;
+    ch->allocated = needed-1 - ((needed-1) % ch->growby)
+                            + ch->growby; /* Round up */
+    ch->data = ch->data ? (char *)realloc(ch->data, ch->allocated)
+                       : (char *)malloc(ch->allocated);
+    if (ch->data == NULL) outofmem(__FILE__, "HTChunkEnsure");
+}
+
+
+/*     Terminate a chunk
+**     -----------------
+*/
+PUBLIC void HTChunkTerminate ARGS1 (HTChunk *,ch)
+{
+    HTChunkPutc(ch, (char)0);
+}
+
+
+/*     Append a string
+**     ---------------
+*/
+PUBLIC void HTChunkPuts ARGS2 (HTChunk *,ch, CONST char *,s)
+{
+    CONST char * p;
+    for (p=s; *p; p++)
+        HTChunkPutc(ch, *p);
+}
diff --git a/Cl/WWWLibrary/HTChunk.h b/Cl/WWWLibrary/HTChunk.h
new file mode 100644 (file)
index 0000000..a1da188
--- /dev/null
@@ -0,0 +1,97 @@
+/*             Chunk handling: Flexible arrays
+**             ===============================
+**
+** This module implements a flexible array. It is a general utility module.
+** A chunk is a structure which may be extended.  These routines create
+** and append data to chnuks, automatically reallocating them as necessary.
+**
+*/
+
+
+typedef struct {
+       int     size;           /* In bytes                     */
+       int     growby;         /* Allocation unit in bytes     */
+       int     allocated;      /* Current size of *data        */
+       char *  data;           /* Pointer to malloced area or 0 */
+} HTChunk;
+
+
+#ifdef SHORT_NAMES
+#define HTChunkClear           HTChClea
+#define HTChunkPutc            HTChPutc
+#define HTChunkPuts            HTChPuts
+#define HTChunkCreate          HTChCrea
+#define HTChunkTerminate       HTChTerm
+#define HTChunkEnsure          HtChEnsu
+#endif
+
+
+/*     Create new chunk
+**
+** On entry,
+**
+**     growby          The number of bytes to allocate at a time
+**                     when the chunk is later extended. Arbitrary but
+**                     normally a trade-off time vs. memory
+**
+** On exit,
+**     returns         A chunk pointer to the new chunk,
+*/
+extern HTChunk * HTChunkCreate PARAMS((int growby));
+
+
+/*     Clear a chunk
+**
+** On entry,
+**     ch      A valid chunk pointer made by HTChunkCreate()
+**
+** On exit,
+**     *ch     The size of the chunk is zero.
+*/
+extern void HTChunkClear PARAMS((HTChunk * ch));
+
+
+/*     Ensure a chunk has a certain space in
+**
+** On entry,
+**     ch      A valid chunk pointer made by HTChunkCreate()
+**     s       The size required
+**
+** On exit,
+**     *ch     Has size at least s
+*/
+extern void HTChunkEnsure PARAMS((HTChunk * ch, int s));
+
+
+/*     Append a character to a  chunk
+**
+** On entry,
+**     ch      A valid chunk pointer made by HTChunkCreate()
+**     c       The character to be appended
+** On exit,
+**     *ch     Is one character bigger
+*/
+
+extern void HTChunkPutc PARAMS((HTChunk * ch, char c));
+
+/*     Append a string to a  chunk
+**
+** On entry,
+**     ch      A valid chunk pointer made by HTChunkCreate()
+**     s       Tpoints to a zero-terminated string to be appended
+** On exit,
+**     *ch     Is bigger by strlen(s)
+*/
+
+extern void HTChunkPuts PARAMS((HTChunk * ch, const char *s));
+
+
+/*     Append a zero character to a  chunk
+**
+** On entry,
+**     ch      A valid chunk pointer made by HTChunkCreate()
+** On exit,
+**     *ch     Is one character bigger
+*/
+
+extern void HTChunkTerminate PARAMS((HTChunk * ch));
diff --git a/Cl/WWWLibrary/HTFTP.c b/Cl/WWWLibrary/HTFTP.c
new file mode 100644 (file)
index 0000000..7280bb4
--- /dev/null
@@ -0,0 +1,960 @@
+/*                     File Transfer Protocol (FTP) Client
+**                     for a WorldWideWeb browser
+**                     ===================================
+**
+**     A cache of control connections is kept.
+**
+** Note: Port allocation
+**
+**     It is essential that the port is allocated by the system, rather
+**     than chosen in rotation by us (POLL_PORTS), or the following
+**     problem occurs.
+**
+**     It seems that an attempt by the server to connect to a port which has
+**     been used recently by a listen on the same socket, or by another
+**     socket this or another process causes a hangup of (almost exactly)
+**     one minute. Therefore, we have to use a rotating port number.
+**     The problem remains that if the application is run twice in quick
+**     succession, it will hang for what remains of a minute.
+**
+** History:
+**      2 May 91       Written TBL, as a part of the WorldWideWeb project.
+**     15 Jan 92       Bug fix: close() was used for NETCLOSE for control soc
+**     10 Feb 92       Retry if cached connection times out or breaks
+**
+** Options:
+**     LISTEN          We listen, the other guy connects for data.
+**                     Otherwise, other way round, but problem finding our
+**                     internet address!
+*/
+
+#define LISTEN         /* @@@@ Test */
+
+/*
+BUGS:  @@@     Limit connection cache size!
+               Error reporting to user.
+               400 & 500 errors are acked by user with windows.
+               Use configuration file for user names
+               Prompt user for password
+               
+**             Note for portablility this version does not use select() and
+**             so does not watch the control and data channels at the
+**             same time.
+*/             
+
+#define REPEAT_PORT    /* Give the port number for each file */
+#define REPEAT_LISTEN  /* Close each listen socket and open a new one */
+
+/* define POLL_PORTS            If allocation does not work, poll ourselves.*/
+#define LISTEN_BACKLOG 2       /* Number of pending connect requests (TCP)*/
+
+#define FIRST_TCP_PORT 1024    /* Region to try for a listening port */
+#define LAST_TCP_PORT  5999    
+
+#define LINE_LENGTH 256
+#define COMMAND_LENGTH 256
+
+#include "HTParse.h"
+#include "HTUtils.h"
+#include "tcp.h"
+#include "HTTCP.h"
+#include "HTAnchor.h"
+#include "HText.h"
+#include "HTStyle.h"
+
+#include "HTFTP.h"
+
+#ifndef IPPORT_FTP
+#define IPPORT_FTP     21
+#endif
+
+extern HTStyleSheet * styleSheet;
+PRIVATE HTStyle *h1Style;                      /* For heading level 1 */
+PRIVATE HTStyle *h2Style;                      /* For heading level 2 */
+PRIVATE HTStyle *textStyle;                    /* Text style */
+PRIVATE HTStyle *dirStyle;                     /* Compact List style */
+
+#ifdef REMOVED_CODE
+extern char *malloc();
+extern void free();
+extern char *strncpy();
+#endif
+
+typedef struct _connection {
+    struct _connection *       next;   /* Link on list         */
+    u_long                     addr;   /* IP address           */
+    int                                socket; /* Socket number for communication */
+} connection;
+
+#ifndef NIL
+#define NIL 0
+#endif
+
+
+/*     Module-Wide Variables
+**     ---------------------
+*/
+PRIVATE connection * connections =0;   /* Linked list of connections */
+PRIVATE char    response_text[LINE_LENGTH+1];/* Last response from NewsHost */
+PRIVATE connection * control;          /* Current connection */
+PRIVATE int    data_soc = -1;          /* Socket for data transfer =invalid */
+
+#ifdef POLL_PORTS
+PRIVATE        unsigned short  port_number = FIRST_TCP_PORT;
+#endif
+
+#ifdef LISTEN
+PRIVATE int     master_socket = -1;    /* Listening socket = invalid   */
+PRIVATE char   port_command[255];      /* Command for setting the port */
+PRIVATE fd_set open_sockets;           /* Mask of active channels */
+PRIVATE int    num_sockets;            /* Number of sockets to scan */
+#else
+PRIVATE        unsigned short  passive_port;   /* Port server specified for data */
+#endif
+
+
+#define NEXT_CHAR HTGetChararcter()    /* Use function in HTFormat.c */
+
+#define DATA_BUFFER_SIZE 2048
+PRIVATE char data_buffer[DATA_BUFFER_SIZE];            /* Input data buffer */
+PRIVATE char * data_read_pointer;
+PRIVATE char * data_write_pointer;
+#define NEXT_DATA_CHAR next_data_char()
+
+
+/*     Procedure: Read a character from the data connection
+**     ----------------------------------------------------
+*/
+PRIVATE char next_data_char
+NOARGS
+{
+    int status;
+    if (data_read_pointer >= data_write_pointer) {
+       status = NETREAD(data_soc, data_buffer, DATA_BUFFER_SIZE);
+       /* Get some more data */
+       if (status <= 0) return (char)-1;
+       data_write_pointer = data_buffer + status;
+       data_read_pointer = data_buffer;
+    }
+#ifdef NOT_ASCII
+    {
+        char c = *data_read_pointer++;
+       return FROMASCII(c);
+    }
+#else
+    return *data_read_pointer++;
+#endif
+}
+
+
+/*     Close an individual connection
+**
+*/
+#ifdef __STDC__
+PRIVATE int close_connection(connection * con)
+#else
+PRIVATE int close_connection(con)
+    connection *con;
+#endif
+{
+    connection * scan;
+    int status = NETCLOSE(con->socket);
+    if (TRACE) printf("FTP: Closing control socket %d\n", con->socket);
+    if (connections==con) {
+        connections = con->next;
+       return status;
+    }
+    for(scan=connections; scan; scan=scan->next) {
+        if (scan->next == con) {
+           scan->next = con->next;     /* Unlink */
+           if (control==con) control = (connection*)0;
+           return status;
+       } /*if */
+    } /* for */
+    return -1;         /* very strange -- was not on list. */
+}
+
+
+/*     Execute Command and get Response
+**     --------------------------------
+**
+**     See the state machine illustrated in RFC959, p57. This implements
+**     one command/reply sequence.  It also interprets lines which are to
+**     be continued, which are marked with a "-" immediately after the
+**     status code.
+**
+** On entry,
+**     con     points to the connection which is established.
+**     cmd     points to a command, or is NIL to just get the response.
+**
+**     The command is terminated with the CRLF pair.
+**
+** On exit,
+**     returns:  The first digit of the reply type,
+**               or negative for communication failure.
+*/
+#ifdef __STDC__
+PRIVATE int response(char * cmd)
+#else
+PRIVATE int response(cmd)
+    char * cmd;
+#endif
+{
+    int result;                                /* Three-digit decimal code */
+    char       continuation;
+    int status;
+    
+    if (!control) {
+          if(TRACE) printf("FTP: No control connection set up!!\n");
+         return -99;
+    }
+    
+    if (cmd) {
+    
+       if (TRACE) printf("  Tx: %s", cmd);
+
+#ifdef NOT_ASCII
+       {
+           char * p;
+           for(p=cmd; *p; p++) {
+               *p = TOASCII(*p);
+           }
+       }
+#endif 
+       status = NETWRITE(control->socket, cmd, (int)strlen(cmd));
+       if (status<0) {
+           if (TRACE) printf(
+               "FTP: Error %d sending command: closing socket %d\n",
+               status, control->socket);
+           close_connection(control);
+           return status;
+       }
+    }
+
+    do {
+       char *p = response_text;
+       for(;;) {  
+           if (((*p++=NEXT_CHAR) == '\n')
+                       || (p == &response_text[LINE_LENGTH])) {
+               *p++=0;                 /* Terminate the string */
+               if (TRACE) printf("    Rx: %s", response_text);
+               sscanf(response_text, "%d%c", &result, &continuation);
+               break;      
+           } /* if end of line */
+           
+           if (*(p-1) < 0) {
+               if(TRACE) fprintf(stderr, "Error on rx: closing socket %d\n",
+                   control->socket);
+               strcpy(response_text, "000 *** TCP read error on response\n");
+               close_connection(control);
+               return -1;      /* End of file on response */
+           }
+       } /* Loop over characters */
+
+    } while (continuation == '-');
+    
+    if (result==421) {
+       if(TRACE) fprintf(stderr, "FTP: They close so we close socket %d\n",
+           control->socket);
+       close_connection(control);
+       return -1;
+    }
+    return result/100;
+}
+
+
+/*     Get a valid connection to the host
+**     ----------------------------------
+**
+** On entry,
+**     arg     points to the name of the host in a hypertext address
+** On exit,
+**     returns <0 if error
+**             socket number if success
+**
+**     This routine takes care of managing timed-out connections, and
+**     limiting the number of connections in use at any one time.
+**
+**     It ensures that all connections are logged in if they exist.
+**     It ensures they have the port number transferred.
+*/
+PRIVATE int get_connection ARGS1 (CONST char *,arg)
+{
+    struct hostent * phost;            /* Pointer to host -- See netdb.h */
+    struct sockaddr_in soc_address;    /* Binary network address */
+    struct sockaddr_in* sin = &soc_address;
+
+    char * username=0;
+    char * password=0;
+    
+    if (!arg) return -1;               /* Bad if no name sepcified     */
+    if (!*arg) return -1;              /* Bad if name had zero length  */
+
+/*  Set up defaults:
+*/
+    sin->sin_family = AF_INET;                 /* Family, host order  */
+    sin->sin_port = htons(IPPORT_FTP);         /* Well Known Number    */
+
+    if (TRACE) printf("FTP: Looking for %s\n", arg);
+
+/* Get node name:
+*/
+    {
+       char *p1 = HTParse(arg, "", PARSE_HOST);
+       char *p2 = strchr(p1, '@');     /* user? */
+       char * pw;
+       if (p2) {
+           username = p1;
+           *p2=0;                      /* terminate */
+           p1 = p2+1;                  /* point to host */
+           pw = strchr(username, ':');
+           if (pw) {
+               *pw++ = 0;
+               password = pw;
+           }
+       }
+       HTParseInet(sin, p1);
+        if (!username) free(p1);
+    } /* scope of p1 */
+
+        
+/*     Now we check whether we already have a connection to that port.
+*/
+
+    {
+       connection * scan;
+       for (scan=connections; scan; scan=scan->next) {
+           if (sin->sin_addr.s_addr == scan->addr) {
+               if (TRACE) printf(
+               "FTP: Already have connection for %d.%d.%d.%d.\n",
+                   (int)*((unsigned char *)(&scan->addr)+0),
+                   (int)*((unsigned char *)(&scan->addr)+1),
+                   (int)*((unsigned char *)(&scan->addr)+2),
+                   (int)*((unsigned char *)(&scan->addr)+3));
+               if (username) free(username);
+               return scan->socket;            /* Good return */
+           } else {
+               if (TRACE) printf(
+               "FTP: Existing connection is %d.%d.%d.%d\n",
+                   (int)*((unsigned char *)(&scan->addr)+0),
+                   (int)*((unsigned char *)(&scan->addr)+1),
+                   (int)*((unsigned char *)(&scan->addr)+2),
+                   (int)*((unsigned char *)(&scan->addr)+3));
+           }
+       }
+    }
+
+   
+/*     Now, let's get a socket set up from the server:
+*/      
+    {
+        int status;
+       connection * con = (connection *)malloc(sizeof(*con));
+       if (con == NULL) outofmem(__FILE__, "get_connection");
+       con->addr = sin->sin_addr.s_addr;       /* save it */
+       status = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+       if (status<0) {
+           (void) HTInetStatus("socket");
+           free(con);
+           if (username) free(username);
+           return status;
+       }
+       con->socket = status;
+
+       status = connect(con->socket, (struct sockaddr*)&soc_address,
+        sizeof(soc_address));
+        if (status<0){
+           (void) HTInetStatus("connect");
+           if (TRACE) printf(
+               "FTP: Unable to connect to remote host for `%s'.\n",
+               arg);
+           NETCLOSE(con->socket);
+           free(con);
+           if (username) free(username);
+           return status;                      /* Bad return */
+       }
+       
+       if (TRACE) printf("FTP connected, socket %d\n", con->socket);
+       control = con;                  /* Current control connection */
+       con->next = connections;        /* Link onto list of good ones */
+       connections = con;
+        HTInitInput(con->socket);/* Initialise buffering for contron connection */
+
+
+/*     Now we log in           Look up username, prompt for pw.
+*/
+       {
+           int status = response(NIL); /* Get greeting */
+           
+           if (status == 2) {          /* Send username */
+               char * command;
+               if (username) {
+                   command = (char*)malloc(10+strlen(username)+2+1);
+                   if (command == NULL) outofmem(__FILE__, "get_connection");
+                   sprintf(command, "USER %s\r\n", username);
+               } else {
+                   command = (char*)malloc(25);
+                   if (command == NULL) outofmem(__FILE__, "get_connection");
+                   sprintf(command, "USER anonymous\r\n");
+               }
+               status = response(command);
+               free(command);
+           }
+           if (status == 3) {          /* Send password */
+               char * command;
+               if (password) {
+                   command = (char*)malloc(10+strlen(password)+2+1);
+                   if (command == NULL) outofmem(__FILE__, "get_connection");
+                   sprintf(command, "PASS %s\r\n", password);
+               } else {
+                   command = (char*)malloc(10+strlen(HTHostName())+2+1);
+                   if (command == NULL) outofmem(__FILE__, "get_connection");
+                   sprintf(command,
+                   "PASS user@%s\r\n", HTHostName()); /*@@*/
+               }
+               status = response(command);
+               free(command);
+           }
+            if (username) free(username);
+
+           if (status == 3) status = response("ACCT noaccount\r\n");
+           
+           if (status !=2) {
+               if (TRACE) printf("FTP: Login fail: %s", response_text);
+               if (control) close_connection(control);
+               return -1;              /* Bad return */
+           }
+           if (TRACE) printf("FTP: Logged in.\n");
+       }
+
+/*     Now we inform the server of the port number we will listen on
+*/
+#ifndef REPEAT_PORT
+       {
+           int status = response(port_command);
+           if (status !=2) {
+               if (control) close_connection(control);
+               return -status;         /* Bad return */
+           }
+           if (TRACE) printf("FTP: Port defined.\n");
+       }
+#endif
+       return con->socket;                     /* Good return */
+    } /* Scope of con */
+}
+
+
+#ifdef LISTEN
+
+/*     Close Master (listening) socket
+**     -------------------------------
+**
+**
+*/
+#ifdef __STDC__
+PRIVATE int close_master_socket(void)
+#else
+PRIVATE int close_master_socket()
+#endif
+{
+    int status;
+    FD_CLR(master_socket, &open_sockets);
+    status = NETCLOSE(master_socket);
+    if (TRACE) printf("FTP: Closed master socket %d\n", master_socket);
+    master_socket = -1;
+    if (status<0) return HTInetStatus("close master socket");
+    else return status;
+}
+
+
+/*     Open a master socket for listening on
+**     -------------------------------------
+**
+**     When data is transferred, we open a port, and wait for the server to
+**     connect with the data.
+**
+** On entry,
+**     master_socket   Must be negative if not set up already.
+** On exit,
+**     Returns         socket number if good
+**                     less than zero if error.
+**     master_socket   is socket number if good, else negative.
+**     port_number     is valid if good.
+*/
+#ifdef __STDC__
+PRIVATE int get_listen_socket(void)
+#else
+PRIVATE int get_listen_socket()
+#endif
+{
+    struct sockaddr_in soc_address;    /* Binary network address */
+    struct sockaddr_in* sin = &soc_address;
+    int new_socket;                    /* Will be master_socket */
+    
+    
+    FD_ZERO(&open_sockets);    /* Clear our record of open sockets */
+    num_sockets = 0;
+    
+#ifndef REPEAT_LISTEN
+    if (master_socket>=0) return master_socket;  /* Done already */
+#endif
+
+/*  Create internet socket
+*/
+    new_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+       
+    if (new_socket<0)
+       return HTInetStatus("socket for master socket");
+    
+    if (TRACE) printf("FTP: Opened master socket number %d\n", new_socket);
+    
+/*  Search for a free port.
+*/
+    sin->sin_family = AF_INET;     /* Family = internet, host order  */
+    sin->sin_addr.s_addr = INADDR_ANY; /* Any peer address */
+#ifdef POLL_PORTS
+    {
+        unsigned short old_port_number = port_number;
+       for(port_number=old_port_number+1;;port_number++){ 
+           int status;
+           if (port_number > LAST_TCP_PORT)
+               port_number = FIRST_TCP_PORT;
+           if (port_number == old_port_number) {
+               return HTInetStatus("bind");
+           }
+           soc_address.sin_port = htons(port_number);
+           if ((status=bind(new_socket,
+                   (struct sockaddr*)&soc_address,
+                           /* Cast to generic sockaddr */
+                   sizeof(soc_address))) == 0)
+               break;
+           if (TRACE) printf(
+               "TCP bind attempt to port %d yields %d, errno=%d\n",
+               port_number, status, errno);
+       } /* for */
+    }
+#else
+    {
+        int status;
+       int address_length = sizeof(soc_address);
+       status = getsockname(control->socket,
+                       (struct sockaddr *)&soc_address,
+                        &address_length);
+       if (status<0) return HTInetStatus("getsockname");
+       CTRACE(tfp, "FTP: This host is %s\n",
+           HTInetString(sin));
+       
+       soc_address.sin_port = 0;       /* Unspecified: please allocate */
+       status=bind(new_socket,
+               (struct sockaddr*)&soc_address,
+                       /* Cast to generic sockaddr */
+               sizeof(soc_address));
+       if (status<0) return HTInetStatus("bind");
+       
+       address_length = sizeof(soc_address);
+       status = getsockname(new_socket,
+                       (struct sockaddr*)&soc_address,
+                       &address_length);
+       if (status<0) return HTInetStatus("getsockname");
+    }
+#endif    
+
+    CTRACE(tfp, "FTP: bound to port %d on %s\n",
+       (unsigned int)ntohs(sin->sin_port),
+       HTInetString(sin));
+
+#ifdef REPEAT_LISTEN
+    if (master_socket>=0)
+        (void) close_master_socket();
+#endif    
+    
+    master_socket = new_socket;
+    
+/*     Now we must find out who we are to tell the other guy
+*/
+    (void)HTHostName();        /* Make address valid - doesn't work*/
+    sprintf(port_command, "PORT %d,%d,%d,%d,%d,%d\r\n",
+                   (int)*((unsigned char *)(&sin->sin_addr)+0),
+                   (int)*((unsigned char *)(&sin->sin_addr)+1),
+                   (int)*((unsigned char *)(&sin->sin_addr)+2),
+                   (int)*((unsigned char *)(&sin->sin_addr)+3),
+                   (int)*((unsigned char *)(&sin->sin_port)+0),
+                   (int)*((unsigned char *)(&sin->sin_port)+1));
+
+
+/*     Inform TCP that we will accept connections
+*/
+    if (listen(master_socket, 1)<0) {
+       master_socket = -1;
+       return HTInetStatus("listen");
+    }
+    CTRACE(tfp, "TCP: Master socket(), bind() and listen() all OK\n");
+    FD_SET(master_socket, &open_sockets);
+    if ((master_socket+1) > num_sockets) num_sockets=master_socket+1;
+
+    return master_socket;              /* Good */
+
+} /* get_listen_socket */
+#endif
+
+
+/*     Get Styles from stylesheet
+**     --------------------------
+*/
+PRIVATE void get_styles NOARGS
+{
+    if (!h1Style) h1Style = HTStyleNamed(styleSheet, "Heading1");
+    if (!h2Style) h2Style = HTStyleNamed(styleSheet, "Heading2");
+    if (!textStyle) textStyle = HTStyleNamed(styleSheet, "Example");
+    if (!dirStyle) dirStyle = HTStyleNamed(styleSheet, "Dir");
+}
+
+
+/*     Read a directory into an hypertext object from the data socket
+**     --------------------------------------------------------------
+**
+** On entry,
+**     anchor          Parent anchor to link the HText to
+**     address         Address of the directory
+** On exit,
+**     returns         HT_LOADED if OK
+**                     <0 if error.
+*/
+PRIVATE int read_directory
+ARGS2 (
+  HTParentAnchor *,    parent,
+  CONST char *,                        address
+)
+{
+  HText * HT = HText_new (parent);
+  char *filename = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION);
+  HTChildAnchor * child;  /* corresponds to an entry of the directory */
+  char c = 0;
+#define LASTPATH_LENGTH 150  /* @@@@ Horrible limit on the entry size */
+  char lastpath[LASTPATH_LENGTH + 1];
+  char *p, *entry;
+
+  HText_beginAppend (HT);
+  get_styles();
+
+  HTAnchor_setTitle (parent, "FTP Directory of ");
+  HTAnchor_appendTitle (parent, address + 5);  /* +5 gets rid of "file:" */
+  HText_setStyle (HT, h1Style);
+  HText_appendText (HT, filename);
+
+  HText_setStyle (HT, dirStyle);
+  data_read_pointer = data_write_pointer = data_buffer;
+
+  if (*filename == 0)  /* Empty filename : use root */
+    strcpy (lastpath, "/");
+  else {
+    p = filename + strlen (filename) - 2;
+    while (p >= filename && *p != '/')
+      p--;
+    strcpy (lastpath, p + 1);  /* relative path */
+  }
+  free (filename);
+  entry = lastpath + strlen (lastpath);
+  if (*(entry-1) != '/')
+    *entry++ = '/';  /* ready to append entries */
+
+  if (strlen (lastpath) > 1) {  /* Current file is not the FTP root */
+    strcpy (entry, "..");
+    child = HTAnchor_findChildAndLink (parent, 0, lastpath, 0);
+    HText_beginAnchor (HT, child);
+    HText_appendText (HT, "Parent Directory");
+    HText_endAnchor (HT);
+    HText_appendCharacter (HT, '\t');
+  }
+  for (;;) {
+    p = entry;
+    while (p - lastpath < LASTPATH_LENGTH) {
+      c = NEXT_DATA_CHAR;
+      if (c == '\r') {
+       c = NEXT_DATA_CHAR;
+       if (c != '\n') {
+         if (TRACE)
+           printf ("Warning: No newline but %d after carriage return.\n", c);
+         break;
+       }
+      }
+      if (c == '\n' || c == (char) EOF)
+       break;
+      *p++ = c;
+    }
+    if (c == (char) EOF && p == entry)
+      break;
+    *p = 0;
+    child = HTAnchor_findChildAndLink (parent, 0, lastpath, 0);
+    HText_beginAnchor (HT, child);
+    HText_appendText (HT, entry);
+    HText_endAnchor (HT);
+    HText_appendCharacter (HT, '\t');
+  }
+
+  HText_appendParagraph (HT);
+  HText_endAppend (HT);
+  return response(NIL) == 2 ? HT_LOADED : -1;
+}
+
+
+#ifndef ERWISE
+/*     Retrieve File from Server
+**     -------------------------
+**
+** On entry,
+**     name            WWW address of a file: document, including hostname
+** On exit,
+**     returns         Socket number for file if good.
+**                     <0 if bad.
+*/
+PUBLIC int HTFTP_open_file_read
+ARGS2 (
+  CONST char *,                        name,
+  HTParentAnchor *,    anchor
+)
+{
+    BOOL isDirectory = NO;
+    int status;
+    int retry;                 /* How many times tried? */
+    
+    for (retry=0; retry<2; retry++) {  /* For timed out/broken connections */
+    
+       status = get_connection(name);
+       if (status<0) return status;
+
+#ifdef LISTEN
+       status = get_listen_socket();
+       if (status<0) return status;
+    
+#ifdef REPEAT_PORT
+/*     Inform the server of the port number we will listen on
+*/
+       {
+           status = response(port_command);
+           if (status !=2) {           /* Could have timed out */
+               if (status<0) continue;         /* try again - net error*/
+               return -status;                 /* bad reply */
+           }
+           if (TRACE) printf("FTP: Port defined.\n");
+       }
+#endif
+#else  /* Use PASV */
+/*     Tell the server to be passive
+*/
+       {
+           char *p;
+           int reply, h0, h1, h2, h3, p0, p1;  /* Parts of reply */
+           status = response("PASV\r\n");
+           if (status !=2) {
+               if (status<0) continue;         /* retry or Bad return */
+               return -status;                 /* bad reply */
+           }
+           for(p=response_text; *p; p++)
+               if ((*p<'0')||(*p>'9')) *p = ' ';       /* Keep only digits */
+           status = sscanf(response_text, "%d%d%d%d%d%d%d",
+                   &reply, &h0, &h1, &h2, &h3, &p0, &p1);
+           if (status<5) {
+               if (TRACE) printf("FTP: PASV reply has no inet address!\n");
+               return -99;
+           }
+           passive_port = (p0<<8) + p1;
+           if (TRACE) printf("FTP: Server is listening on port %d\n",
+                   passive_port);
+       }
+
+/*     Open connection for data:
+*/
+       {
+           struct sockaddr_in soc_address;
+           int status = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+           if (status<0) {
+               (void) HTInetStatus("socket for data socket");
+               return status;
+           }
+           data_soc = status;
+           
+           soc_address.sin_addr.s_addr = control->addr;
+           soc_address.sin_port = htons(passive_port);
+           soc_address.sin_family = AF_INET;       /* Family, host order  */
+           if (TRACE) printf( 
+               "FTP: Data remote address is port %d, inet %d.%d.%d.%d\n",
+                       (unsigned int)ntohs(soc_address.sin_port),
+                       (int)*((unsigned char *)(&soc_address.sin_addr)+0),
+                       (int)*((unsigned char *)(&soc_address.sin_addr)+1),
+                       (int)*((unsigned char *)(&soc_address.sin_addr)+2),
+                       (int)*((unsigned char *)(&soc_address.sin_addr)+3));
+    
+           status = connect(data_soc, (struct sockaddr*)&soc_address,
+                   sizeof(soc_address));
+           if (status<0){
+               (void) HTInetStatus("connect for data");
+               NETCLOSE(data_soc);
+               return status;                  /* Bad return */
+           }
+           
+           if (TRACE) printf("FTP data connected, socket %d\n", data_soc);
+       }
+#endif /* use PASV */
+       status = 0;
+        break; /* No more retries */
+
+    } /* for retries */
+    if (status<0) return status;       /* Failed with this code */
+    
+/*     Ask for the file:
+*/    
+    {
+        char *filename = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
+       char command[LINE_LENGTH+1];
+       if (!*filename) StrAllocCopy(filename, "/");
+       sprintf(command, "RETR %s\r\n", filename);
+       status = response(command);
+       if (status != 1) {  /* Failed : try to CWD to it */
+         sprintf(command, "CWD %s\r\n", filename);
+         status = response(command);
+         if (status == 2) {  /* Successed : let's NAME LIST it */
+           isDirectory = YES;
+           sprintf(command, "NLST\r\n");
+           status = response (command);
+         }
+       }
+       free(filename);
+       if (status != 1) return -status;                /* Action not started */
+    }
+
+#ifdef LISTEN
+/*     Wait for the connection
+*/
+    {
+       struct sockaddr_in soc_address;
+        int    soc_addrlen=sizeof(soc_address);
+       status = accept(master_socket,
+                       (struct sockaddr *)&soc_address,
+                       &soc_addrlen);
+       if (status<0)
+           return HTInetStatus("accept");
+       CTRACE(tfp, "TCP: Accepted new socket %d\n", status);
+       data_soc = status;
+    }
+#else
+/* @@ */
+#endif
+    if (isDirectory)
+      return read_directory (anchor, name);
+      /* returns HT_LOADED or error */
+    else
+      return data_soc;
+       
+} /* open_file_read */
+#endif /* ERWISE */
+
+/*     Close socket opened for reading a file, and get final message
+**     -------------------------------------------------------------
+**
+*/
+PUBLIC int HTFTP_close_file
+ARGS1 (int,soc)
+{
+    int status;
+
+    if (soc!=data_soc) {
+        if (TRACE) printf("HTFTP: close socket %d: (not FTP data socket).\n",
+               soc);
+       return NETCLOSE(soc);
+    }
+    status = NETCLOSE(data_soc);
+    if (TRACE) printf("FTP: Closing data socket %d\n", data_soc);
+    if (status<0) (void) HTInetStatus("close");        /* Comment only */
+    data_soc = -1;     /* invalidate it */
+    
+    status = response(NIL);
+    if (status!=2) return -status;
+    
+    return status;     /* Good */
+}
+
+
+/*___________________________________________________________________________
+*/
+/*     Test program only
+**     -----------------
+**
+**     Compiling this with -DTEST produces a test program.  Unix only
+**
+** Syntax:
+**     test <file or option> ...
+**
+**     options:        -v      Verbose: Turn trace on at this point
+*/
+#ifdef TEST
+
+PUBLIC int WWW_TraceFlag;
+
+#ifdef __STDC__
+int main(int argc, char*argv[])
+#else
+int main(argc, argv)
+       int argc;
+       char *argv[];
+#endif
+{
+    
+    int arg;                   /* Argument number */
+    int status;
+    connection * con;
+    WWW_TraceFlag = (0==strcmp(argv[1], "-v"));        /* diagnostics ? */
+
+#ifdef SUPRESS    
+    status = get_listen_socket();
+    if (TRACE) printf("get_listen_socket returns %d\n\n", status);
+    if (status<0) exit(status);
+
+    status = get_connection(argv[1]);  /* test double use */
+    if (TRACE) printf("get_connection(`%s') returned %d\n\n", argv[1], status);
+    if (status<0) exit(status);
+#endif
+
+    for(arg=1; arg<argc; arg++) {              /* For each argument */
+         if (0==strcmp(argv[arg], "-v")) {
+             WWW_TraceFlag = 1;                        /* -v => Trace on */
+
+        } else {                               /* Filename: */
+       
+           status = HTTP_open_file_read(argv[arg]);
+           if (TRACE) printf("open_file_read returned %d\n\n", status);
+           if (status<0) exit(status);
+               
+       /*      Copy the file to std out:
+       */   
+           {
+               char buffer[INPUT_BUFFER_SIZE];
+               for(;;) {
+                   int status = NETREAD(data_soc, buffer, INPUT_BUFFER_SIZE);
+                   if (status<=0) {
+                       if (status<0) (void) HTInetStatus("read");
+                       break;
+                   }
+                   status = write(1, buffer, status);
+                   if (status<0) {
+                       printf("Write failure!\n");
+                       break;
+                   }
+               }
+           }
+       
+           status = HTTP_close_file(data_soc);
+           if (TRACE) printf("Close_file returned %d\n\n", status);
+
+        } /* if */
+    } /* for */
+    status = response("QUIT\r\n");             /* Be good */
+    if (TRACE) printf("Quit returned %d\n", status);
+
+    while(connections) close_connection(connections);
+    
+    close_master_socket();
+         
+} /* main */
+#endif  /* TEST */
+
diff --git a/Cl/WWWLibrary/HTFTP.h b/Cl/WWWLibrary/HTFTP.h
new file mode 100644 (file)
index 0000000..bd0ffcd
--- /dev/null
@@ -0,0 +1,31 @@
+/*                     FTP access functions            HTFTP.h
+**                     ====================
+*/
+
+/*     Retrieve File from Server
+**     -------------------------
+**
+** On exit,
+**     returns         Socket number for file if good.
+**                     <0 if bad.
+*/
+extern int HTFTP_open_file_read
+PARAMS
+((
+  CONST char * name,
+  HTParentAnchor * anchor
+));
+
+/*     Close socket opened for reading a file, and get final message
+**     -------------------------------------------------------------
+**
+*/
+extern int HTFTP_close_file
+PARAMS ((int soc));
+
+
+/*     Return Host Name
+**     ----------------
+*/
+extern CONST char * HTHostName
+NOPARAMS;
diff --git a/Cl/WWWLibrary/HTFile.c b/Cl/WWWLibrary/HTFile.c
new file mode 100644 (file)
index 0000000..3bef015
--- /dev/null
@@ -0,0 +1,554 @@
+/*                     File Access                             HTFile.c
+**                     ===========
+**
+**     This is unix-specific code in general, with some VMS bits.
+**     These are routines for file access used by WWW browsers.
+**
+** History:
+**        Feb 91       Written Tim Berners-Lee CERN/CN
+**        Apr 91       vms-vms access included using DECnet syntax
+**
+** Bugs:
+**     Cannot access VMS files from a unix machine. How can we know that the
+**     target machine runs VMS?
+*/
+
+#define INFINITY 512           /* file name length @@ FIXME */
+
+
+
+#ifdef unix                    /* if this is to compile on a UNIX machine */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/dir.h>
+#include "HText.h"
+#define GOT_READ_DIR 1    /* if directory reading functions are available */
+#endif
+
+#include "HTUtils.h"
+#include "HTFile.h"
+
+#include "WWW.h"
+#include "HTParse.h"
+#include "tcp.h"
+#include "HTTCP.h"
+#include "HTFTP.h"
+#include "HTAnchor.h"
+
+PRIVATE char *HTMountRoot = "/Net/";           /* Where to find mounts */
+#ifdef vms
+PRIVATE char *HTCacheRoot = "/WWW$SCRATCH/";   /* Where to cache things */
+#else
+PRIVATE char *HTCacheRoot = "/tmp/W3_Cache_";   /* Where to cache things */
+#endif
+
+/* PRIVATE char *HTSaveRoot  = "$(HOME)/WWW/";*/    /* Where to save things */
+
+#ifdef vms
+/*     Convert unix-style name into VMS name
+**     -------------------------------------
+**
+** Bug:        Returns pointer to static -- non-reentrant
+*/
+PRIVATE char * vms_name(CONST char * nn, CONST char * fn)
+{
+
+/*     We try converting the filename into Files-11 syntax. That is, we assume
+**     first that the file is, like us, on a VMS node. We try remote
+**     (or local) DECnet access. Files-11, VMS, VAX and DECnet
+**     are trademarks of Digital Equipment Corporation. 
+**     The node is assumed to be local if the hostname WITHOUT DOMAIN
+**     matches the local one. @@@
+*/
+    static char vmsname[INFINITY];     /* returned */
+    char * filename = (char*)malloc(strlen(fn)+1);
+    char * nodename = (char*)malloc(strlen(nn)+2+1);   /* Copies to hack */
+    char *second;              /* 2nd slash */
+    char *last;                        /* last slash */
+    
+    char * hostname = HTHostName();
+
+    strcpy(filename, fn);
+    strcpy(nodename, "");      /* On same node? Yes if node names match */
+    {
+        char *p, *q;
+        for (p=hostname, q=nn; *p && *p!='.' && *q && *q!='.'; p++, q++){
+           if (TOUPPER(*p)!=TOUPPER(*q)) {
+               strcpy(nodename, nn);
+               q = strchr(nodename, '.');      /* Mismatch */
+               if (q) *q=0;                    /* Chop domain */
+               strcat(nodename, "::");         /* Try decnet anyway */
+               break;
+           }
+       }
+    }
+
+    second = strchr(filename+1, '/');          /* 2nd slash */
+    last = strrchr(filename, '/');     /* last slash */
+        
+    if (!second) {                             /* Only one slash */
+       sprintf(vmsname, "%s%s", nodename, filename);
+    } else if(second==last) {          /* Exactly two slashes */
+       *second = 0;            /* Split filename from disk */
+       sprintf(vmsname, "%s%s:%s", nodename, filename+1, second+1);
+       *second = '/';  /* restore */
+    } else {                           /* More than two slashes */
+       char * p;
+       *second = 0;            /* Split disk from directories */
+       *last = 0;              /* Split dir from filename */
+       sprintf(vmsname, "%s%s:[%s]%s",
+               nodename, filename+1, second+1, last+1);
+       *second = *last = '/';  /* restore filename */
+       for (p=strchr(vmsname, '['); *p!=']'; p++)
+           if (*p=='/') *p='.';        /* Convert dir sep.  to dots */
+    }
+    free(nodename);
+    free(filename);
+    return vmsname;
+}
+
+
+#endif /* vms */
+
+/*     Make the cache file name for a W3 document
+**     ------------------------------------------
+**     Make up a suitable name for saving the node in
+**
+**     E.g.    /tmp/WWW_Cache_news/1234@cernvax.cern.ch
+**             /tmp/WWW_Cache_http/crnvmc/FIND/xx.xxx.xx
+**
+** On exit,
+**     returns a malloc'ed string which must be freed by the caller.
+*/
+PUBLIC char * HTCacheFileName ARGS1(CONST char *,name)
+{
+    char * access = HTParse(name, "", PARSE_ACCESS);
+    char * host = HTParse(name, "", PARSE_HOST);
+    char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
+    
+    char * result;
+    result = (char *)malloc(
+           strlen(HTCacheRoot)+strlen(access)
+           +strlen(host)+strlen(path)+6+1);
+    if (result == NULL) outofmem(__FILE__, "HTCacheFileName");
+    sprintf(result, "%s/WWW/%s/%s%s", HTCacheRoot, access, host, path);
+    free(path);
+    free(access);
+    free(host);
+    return result;
+}
+
+/*     Open a file for write, creating the path
+**     ----------------------------------------
+*/
+#ifdef NOT_IMPLEMENTED
+PRIVATE int HTCreatePath ARGS1(CONST char *,path)
+{
+    return -1;
+}
+#endif
+
+/*     Convert filenames between local and WWW formats
+**     -----------------------------------------------
+**     Make up a suitable name for saving the node in
+**
+**     E.g.    $(HOME)/WWW/news/1234@cernvax.cern.ch
+**             $(HOME)/WWW/http/crnvmc/FIND/xx.xxx.xx
+**
+** On exit,
+**     returns a malloc'ed string which must be freed by the caller.
+*/
+PUBLIC char * HTLocalName ARGS1(CONST char *,name)
+{
+    char * access = HTParse(name, "", PARSE_ACCESS);
+    char * host = HTParse(name, "", PARSE_HOST);
+    char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
+    
+    if (0==strcmp(access, "file")) {
+        free(access);  
+       if ((0==strcmp(host, HTHostName())) || !*host) {
+           free(host);
+           if (TRACE) printf("Node `%s' means path `%s'\n", name, path);
+           return(path);
+       } else {
+           char * result = (char *)malloc(
+                               strlen("/Net/")+strlen(host)+strlen(path)+1);
+              if (result == NULL) outofmem(__FILE__, "HTLocalName");
+           sprintf(result, "%s%s%s", "/Net/", host, path);
+           free(host);
+           free(path);
+           if (TRACE) printf("Node `%s' means file `%s'\n", name, result);
+           return result;
+       }
+    } else {  /* other access */
+       char * result;
+        CONST char * home =  (CONST char*)getenv("HOME");
+       if (!home) home = "/tmp"; 
+       result = (char *)malloc(
+               strlen(home)+strlen(access)+strlen(host)+strlen(path)+6+1);
+      if (result == NULL) outofmem(__FILE__, "HTLocalName");
+       sprintf(result, "%s/WWW/%s/%s%s", home, access, host, path);
+       free(path);
+       free(access);
+       free(host);
+       return result;
+    }
+}
+
+
+/*     Make a WWW name from a full local path name
+**
+** Bugs:
+**     At present, only the names of two network root nodes are hand-coded
+**     in and valid for the NeXT only. This should be configurable in
+**     the general case.
+*/
+
+PUBLIC char * WWW_nameOfFile ARGS1 (CONST char *,name)
+{
+    char * result;
+#ifdef NeXT
+    if (0==strncmp("/private/Net/", name, 13)) {
+       result = (char *)malloc(7+strlen(name+13)+1);
+       if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
+       sprintf(result, "file://%s", name+13);
+    } else
+#endif
+    if (0==strncmp(HTMountRoot, name, 5)) {
+       result = (char *)malloc(7+strlen(name+5)+1);
+       if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
+       sprintf(result, "file://%s", name+5);
+    } else {
+        result = (char *)malloc(7+strlen(HTHostName())+strlen(name)+1);
+       if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
+       sprintf(result, "file://%s%s", HTHostName(), name);
+    }
+    if (TRACE) printf("File `%s'\n\tmeans node `%s'\n", name, result);
+    return result;
+}
+
+
+/*     Determine file format from file name
+**     ------------------------------------
+**
+** Note: This function is also in the server file HTRetrieve.c
+** If it gets complicated, it should be shared.
+**
+*/
+
+PUBLIC HTFormat HTFileFormat ARGS1 (CONST char *,filename)
+{
+    CONST char * extension;
+    for (extension=filename+strlen(filename);
+       (extension>filename) &&
+               (*extension != '.') &&
+               (*extension!='/');
+       extension--) /* search */ ;
+
+    if (*extension == '.') {
+       return    0==strcmp(".html", extension) ? WWW_HTML
+               : 0==strcmp(".rtf",  extension) ? WWW_RICHTEXT
+               : 0==strcmp(".txt",  extension) ? WWW_PLAINTEXT
+               : WWW_PLAINTEXT;        /* Unrecognised, try plain text */
+    } else {
+       return WWW_PLAINTEXT;
+    } 
+}
+
+
+/*     Determine write access to a file
+//     --------------------------------
+//
+// On exit,
+//     return value    YES if file can be accessed and can be written to.
+//
+// Bugs:
+//     1.      No code for non-unix systems.
+//     2.      Isn't there a quicker way?
+*/
+
+#ifdef vms
+#define NO_GROUPS
+#endif
+#ifdef NO_UNIX_IO
+#define NO_GROUPS
+#endif
+#ifdef PCNFS
+#define NO_GROUPS
+#endif
+
+PUBLIC BOOL HTEditable ARGS1 (CONST char *,filename)
+{
+#ifdef NO_GROUPS
+    return NO;         /* Safe answer till we find the correct algorithm */
+#else
+    int        groups[NGROUPS];        
+    uid_t      myUid;
+    int                ngroups;                        /* The number of groups  */
+    struct stat        fileStatus;
+    int                i;
+        
+    if (stat(filename, &fileStatus))           /* Get details of filename */
+       return NO;                              /* Can't even access file! */
+
+    ngroups = getgroups(NGROUPS, groups);      /* Groups to which I belong  */
+    myUid = geteuid();                         /* Get my user identifier */
+
+    if (TRACE) {
+        int i;
+       printf("File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (",
+           fileStatus.st_mode, fileStatus.st_uid, fileStatus.st_gid,
+           myUid, ngroups);
+       for (i=0; i<ngroups; i++) printf(" %d", groups[i]);
+       printf(")\n");
+    }
+    
+    if (fileStatus.st_mode & 0002)             /* I can write anyway? */
+       return YES;
+       
+    if ((fileStatus.st_mode & 0200)            /* I can write my own file? */
+     && (fileStatus.st_uid == myUid))
+       return YES;
+
+    if (fileStatus.st_mode & 0020)             /* Group I am in can write? */
+    {
+       for (i=0; i<ngroups; i++) {
+            if (groups[i] == fileStatus.st_gid)
+               return YES;
+       }
+    }
+    if (TRACE) printf("\tFile is not editable.\n");
+    return NO;                                 /* If no excuse, can't do */
+#endif
+}
+
+
+/*     Open a file descriptor for a document
+**     -------------------------------------
+**
+** On entry,
+**     addr            must point to the fully qualified hypertext reference.
+**
+** On exit,
+**     returns         <0      Error has occured.
+**                     >=0     Value of file descriptor or socket to be used
+**                              to read data.
+**     *pFormat        Set to the format of the file, if known.
+**                     (See WWW.h)
+**
+*/
+int HTOpenFile
+ARGS3
+(
+ CONST char *,addr,
+ HTFormat *,pFormat,
+ HTParentAnchor *,anchor
+)
+{
+    char * filename;
+    int fd = -1;               /* Unix file descriptor number = INVALID */
+    char * nodename = 0;
+    char * newname=0;  /* Simplified name of file */
+
+/*     Reduce the filename to a basic form (hopefully unique!)
+*/
+    StrAllocCopy(newname, addr);
+    filename=HTParse(newname, "", PARSE_PATH|PARSE_PUNCTUATION);
+    nodename=HTParse(newname, "", PARSE_HOST);
+    free(newname);
+    
+    *pFormat = HTFileFormat(filename);
+
+    
+#ifdef vms
+/* Assume that the file is remote vms or ultrix if no domain name @@ */
+    if (!strchr(nodename, '.')) {
+        char * vmsname = vms_name(nodename, filename);
+       fd = open(vmsname, O_RDONLY, 0);
+
+/*     If the file wasn't VMS syntax, then perhaps it is ultrix
+*/
+       if (fd<0) {
+           char ultrixname[INFINITY];
+           if (TRACE) fprintf(stderr, "HTFile: Can't open as %s\n", vmsname);
+           sprintf(ultrixname, "%s::\"%s\"", nodename, filename);
+           fd = open(ultrixname, O_RDONLY, 0);
+           if (fd<0) {
+               if (TRACE) fprintf(stderr, 
+                       "HTFile: Can't open as %s\n", ultrixname);
+           }
+       }
+    }
+#else
+
+/*     For unix, we try to translate the name into the name of a transparently
+**     mounted file.
+*/
+#ifndef NO_UNIX_IO
+    {
+       char * localname = HTLocalName(addr);
+       
+       
+#ifdef GOT_READ_DIR
+/*
+**     Check to see if the 'localname' is in fact a directory.  If it is
+**     create a new hypertext object containing a list of files and 
+**     subdirectories contained in the directory.  All of these are links
+**      to the directories or files listed.
+**      NB This assumes the existance of a type 'struct direct', which will
+**      hold the directory entry, and a type 'DIR' which is used to point to
+**      the current directory being read.
+*/
+       
+       struct stat dir_info;
+       
+       if (stat(localname,&dir_info) == -1) {     /* get file information */
+                                      /* if can't read file information */
+           if (TRACE) fprintf(stderr, "HTFile: can't stat %s\n", localname);
+
+       }  else {               /* Stat was OK */
+               
+
+           if (((dir_info.st_mode) & S_IFMT) == S_IFDIR) {
+               /* if localname is a directory */       
+               HTChildAnchor * child;
+               HText * HT;
+               extern HTStyleSheet * styleSheet;
+               static HTStyle * DirectoryStyle = 0;
+               static HTStyle * H1Style = 0;
+               DIR *dp;
+               struct direct * dirbuf;
+                   
+               char * tmpfilename = NULL;
+               char * shortfilename = NULL;
+               struct stat *file_info = malloc(sizeof(struct stat));
+               
+               if (TRACE)
+                   fprintf(stderr,"%s is a directory\n",localname);
+                       
+               if (!H1Style)
+                   H1Style = HTStyleNamed(styleSheet, "Heading1");
+               
+               if (!DirectoryStyle)
+                   DirectoryStyle = HTStyleNamed(styleSheet, "Dir");
+               
+               HTAnchor_setTitle(anchor, filename);
+                   
+               HT = HText_new(anchor);
+               HText_beginAppend(HT);
+                   
+               HText_setStyle(HT,H1Style);
+               shortfilename=strrchr(localname,'/');
+               /* put the last part of the path in shortfilename */
+               shortfilename++;               /* get rid of leading '/' */
+               if (*shortfilename=='\0')
+                   shortfilename--;
+                   
+               HText_appendText(HT,shortfilename);
+               HText_setStyle(HT,DirectoryStyle);
+               
+               dp = opendir(localname);
+               if (dp) {                   
+                  /* if the directory file is readable */
+                   while (dirbuf = readdir(dp)) {
+                               /* while there are directory entries to be read */
+                       if (dirbuf->d_ino == 0)
+                                       /* if the entry is not being used, skip it */
+                           continue;
+                       
+                       if (!strcmp(dirbuf->d_name,"."))
+                           continue;      /* skip the entry for this directory */
+                           
+                       if (strcmp(dirbuf->d_name,".."))
+                                   /* if the current entry is parent directory */
+                           if ((*(dirbuf->d_name)=='.') ||
+                               (*(dirbuf->d_name)==','))
+                               continue;    /* skip those files whose name begins
+                                               with '.' or ',' */
+    
+                       StrAllocCopy(tmpfilename,localname);
+                       if (strcmp(localname,"/")) 
+                                           /* if filename is not root directory */
+                           StrAllocCat(tmpfilename,"/"); 
+                       else
+                           if (!strcmp(dirbuf->d_name,".."))
+                               continue;
+               /* if root directory and current entry is parent
+                                       directory, skip the current entry */
+                       
+                       StrAllocCat(tmpfilename,dirbuf->d_name);
+                       /* append the current entry's filename to the path */
+                       HTSimplify(tmpfilename);
+               
+                       child = HTAnchor_findChildAndLink(
+                               anchor, 0, tmpfilename, 0);
+                       HText_beginAnchor(HT, child); 
+                       stat(tmpfilename,file_info);
+                       
+                       if (strcmp(dirbuf->d_name,"..")) {
+/* if the current entry is not the parent directory then use the file name */
+                           HText_appendText(HT,dirbuf->d_name);
+                           if (((file_info->st_mode) & S_IFMT) == S_IFDIR) 
+                               HText_appendCharacter(HT, '/'); 
+                       }                       
+                       else {
+                       /* use name of parent directory */
+                           char * endbit = strrchr(tmpfilename, '/');
+                           HText_appendText(HT,"Up to ");
+                           HText_appendText(HT, endbit?endbit+1:tmpfilename);
+                       }       
+                       HText_endAnchor(HT);
+                           
+                       HText_appendCharacter(HT, '\t');
+                   } /* end while directory entries left to read */
+                   
+                   closedir(dp);
+                   free(tmpfilename);
+                   
+               }  else {   /* Directory is not readable */
+                   if (TRACE)
+                       fprintf(stderr,"HTFile.c: directory %s unreadable\n",
+                               localname);
+                   HText_appendText(HT,
+                          "Sorry, can't read the contents of this directory - probably read protected\n");
+                           
+               } /* end if directory not readable */    
+               
+               HText_endAppend(HT);
+                   
+               free(file_info);
+               free(filename);  
+               free(localname);
+               return HT_LOADED;       /* fd not valid, but document loaded anyway */
+               
+           } /* end if localname is directory */
+       
+       } /* end if file stat worked */
+       
+/* End of directory reading section
+*/
+#endif
+
+       fd = open(localname, O_RDONLY, 0);
+       if(TRACE) printf ("HTAccess: Opening `%s' gives %d\n",
+                           localname, fd);
+       free(localname);
+    }
+#endif
+#endif
+
+/*     Now, as transparently mounted access has failed, we try FTP.
+*/
+    if (fd<0)
+       if (strcmp(nodename, HTHostName())!=0) {
+           free(filename);
+           return HTFTP_open_file_read(addr, anchor);
+    }
+
+/*     All attempts have failed if fd<0.
+*/
+    if (fd<0) printf("Can't open `%s', errno=%d\n", filename, errno);
+    free(filename);
+    return fd;
+
+}
diff --git a/Cl/WWWLibrary/HTFile.h b/Cl/WWWLibrary/HTFile.h
new file mode 100644 (file)
index 0000000..473275a
--- /dev/null
@@ -0,0 +1,83 @@
+/*                     File Access                             HTFile.h
+**                     ===========
+**
+**     These are routines for file access used by WWW browsers.
+**
+*/
+
+#include "HTFormat.h"
+
+/*     Convert filenames between local and WWW formats
+**     -----------------------------------------------
+**     Make up a suitable name for saving the node in
+**
+**     E.g.    $(HOME)/WWW/news/1234@cernvax.cern.ch
+**             $(HOME)/WWW/http/crnvmc/FIND/xx.xxx.xx
+*/
+#ifdef __STDC__
+extern char * HTLocalName(const char * name);
+#else
+extern char * HTLocalName();
+#endif
+
+/*     Make a WWW name from a full local path name
+**
+*/
+extern char * WWW_nameOfFile PARAMS((const char * name));
+
+
+/*     Generate the name of a cache file
+*/
+
+extern char * HTCacheFileName PARAMS((CONST char * name));
+
+
+/*     Determine file format from file name
+**     ------------------------------------
+*/
+
+#ifdef __STDC__
+extern int HTFileFormat(const char * filename);
+#else
+extern int HTFileFormat();
+#endif 
+
+
+/*     Determine write access to a file
+//     --------------------------------
+//
+// On exit,
+//     return value    YES if file can be accessed and can be written to.
+//
+//     Isn't there a quicker way?
+*/
+
+#ifdef __STDC__
+extern BOOL HTEditable(const char * filename);
+#else
+extern BOOL HTEditable();
+#endif
+
+
+/*     Open a file descriptor for a document
+**     -------------------------------------
+**
+** On entry,
+**     addr            must point to the fully qualified hypertext reference.
+**
+** On exit,
+**     returns         <0      Error has occured.
+**                     >=0     Value of file descriptor or socket to be used
+**                              to read data.
+**     *pFormat        Set to the format of the file, if known.
+**                     (See WWW.h)
+**
+*/
+extern int HTOpenFile
+PARAMS
+((
+  const char * addr,
+  HTFormat * pFormat,
+  HTParentAnchor * anchor
+));
+
diff --git a/Cl/WWWLibrary/HTFont.h b/Cl/WWWLibrary/HTFont.h
new file mode 100644 (file)
index 0000000..914d33e
--- /dev/null
@@ -0,0 +1,19 @@
+/*             The portable font concept (!?*)
+*/
+
+/*     Line mode browser version:
+*/
+
+typedef int HTFont;    /* For now */
+
+#define HT_NON_BREAK_SPACE ((char)1)   /* For now */
+
+#define HT_FONT                0
+#define HT_CAPITALS    1
+#define HT_BOLD                2
+#define HT_UNDERLINE   4
+#define HT_INVERSE     8
+#define HT_DOUBLE      0x10
+
+#define HT_BLACK       0
+#define HT_WHITE       1
diff --git a/Cl/WWWLibrary/HTFormat.c b/Cl/WWWLibrary/HTFormat.c
new file mode 100644 (file)
index 0000000..ab2a9dd
--- /dev/null
@@ -0,0 +1,133 @@
+/*             Manage different file formats                   HTFormat.c
+**             =============================
+**
+** Bugs:
+**     Not reentrant.
+**
+**     Assumes the incoming stream is ASCII, rather than a local file
+**     format, and so ALWAYS converts from ASCII on non-ASCII machines.
+**     Therefore, non-ASCII machines can't read local files.
+*/
+
+#include "HTUtils.h"
+#include "tcp.h"
+#include "HTFormat.h"
+
+#include "HTML.h"
+#include "HText.h"
+#include "HTStyle.h"
+
+extern HTStyleSheet * styleSheet;
+
+/*     File buffering
+**     --------------
+**
+**     The input file is read using the macro which can read from
+**     a socket or a file.
+**     The input buffer size, if large will give greater efficiency and
+**     release the server faster, and if small will save space on PCs etc.
+*/
+#define INPUT_BUFFER_SIZE 4096         /* Tradeoff */
+PRIVATE char input_buffer[INPUT_BUFFER_SIZE];
+PRIVATE char * input_pointer;
+PRIVATE char * input_limit;
+PRIVATE int input_file_number;
+
+
+/*     Set up the buffering
+**
+**     These routines are public because they are in fact needed by
+**     many parsers, and on PCs and Macs we should not duplicate
+**     the static buffer area.
+*/
+PUBLIC void HTInitInput ARGS1 (int,file_number)
+{
+    input_file_number = file_number;
+    input_pointer = input_limit = input_buffer;
+}
+
+
+PUBLIC char HTGetChararcter NOARGS
+{
+    char ch;
+    do {
+       if (input_pointer >= input_limit) {
+#ifdef ERWISE
+            int status = cl_read_data(
+                   input_file_number, input_buffer, INPUT_BUFFER_SIZE);
+#else
+           int status = NETREAD(
+                   input_file_number, input_buffer, INPUT_BUFFER_SIZE);
+#endif
+           if (status <= 0) {
+               if (status == 0) return (char)EOF;
+               if (TRACE) fprintf(stderr,
+                   "HTFormat: File read error %d\n", status);
+               return (char)EOF; /* -1 is returned by UCX at end of HTTP link */
+           }
+           input_pointer = input_buffer;
+           input_limit = input_buffer + status;
+       }
+       ch = *input_pointer++;
+    } while (ch == (char) 13); /* Ignore ASCII carriage return */
+    
+    return FROMASCII(ch);
+}
+
+
+/*     Parse a file given format and file number
+**     ------------
+*/
+PUBLIC void HTParseFormat ARGS3(
+       HTFormat,format,
+       HTParentAnchor *,anchor,
+       int,file_number)
+{
+/*     Parse the file
+*/
+#ifdef CURSES
+      long    bytecount = 0;
+#endif
+    HTInitInput(file_number);
+
+    switch (format) {
+
+    case WWW_HTML:             /* Parse HTML */
+      {
+        HTML_begin(anchor);
+       SGML_begin(&HTML_dtd);
+       for(;;) {
+           char character;
+           character = HTGetChararcter();
+           if (character == (char)EOF) break;
+#ifdef CURSES
+              if (++bytecount % 1024 == 0) prompt_count(bytecount / 1024);
+#endif
+    
+           SGML_character(&HTML_dtd, character);           
+         }
+       SGML_end(&HTML_dtd);
+      }
+       break;
+
+    default :                  /* unknown format -- Parse plain text */
+      {
+        HText * text = HText_new(anchor);
+       HText_setStyle(text, HTStyleNamed(styleSheet, "Example"));
+       HText_beginAppend(text);
+       for(;;) {
+           char character;
+           character = HTGetChararcter();
+           if (character == (char)EOF) break;
+#ifdef CURSES
+              if (++bytecount % 1024 == 0) prompt_count(bytecount / 1024);
+#endif
+           HText_appendCharacter(text, character);           
+       }
+        HText_endAppend(text);
+      }
+       break;
+       
+    } /* end of switch (format) */
+    
+}
diff --git a/Cl/WWWLibrary/HTFormat.h b/Cl/WWWLibrary/HTFormat.h
new file mode 100644 (file)
index 0000000..70d3fee
--- /dev/null
@@ -0,0 +1,39 @@
+/*             Manage different file formats                   HTFormat.c
+**             =============================
+**
+*/
+#ifndef HTFORMAT_H
+#define HTFORMAT_H
+
+#include "HTUtils.h"
+
+typedef int HTFormat;
+                               /* Can take the following values: */
+#define WWW_INVALID   (-1)
+#define WWW_SOURCE     0       /* Whatever it was                      */
+#define WWW_PLAINTEXT  1       /* plain ISO latin (ASCII)              */
+#define WWW_POSTSCRIPT 2       /* Postscript - encapsulated?           */
+#define        WWW_RICHTEXT    3       /* Microsoft RTF transfer format        */
+#define WWW_HTML       4       /* WWW HyperText Markup Language        */
+#define WWW_BINARY     5       /* Otherwise Unknown binary format */
+
+#include "HTAnchor.h"
+
+
+/*     Clear input buffer and set file number
+*/
+extern void HTInitInput PARAMS((int file_number));
+
+/*     Get next character from buffer
+*/
+extern char HTGetChararcter NOPARAMS;
+
+
+/*     Parse a file given its format
+*/
+extern void HTParseFormat PARAMS((
+       HTFormat        format,
+       HTParentAnchor  *anchor,
+       int             file_number));
+
+#endif
diff --git a/Cl/WWWLibrary/HTGopher.c b/Cl/WWWLibrary/HTGopher.c
new file mode 100644 (file)
index 0000000..5ef66ca
--- /dev/null
@@ -0,0 +1,415 @@
+/*                     GOPHER ACCESS                           HTGopher.c
+**                     =============
+**
+** History:
+**     26 Sep 90       Adapted from other accesses (News, HTTP) TBL
+**     29 Nov 91       Downgraded to C, for portable implementation.
+*/
+
+#define GOPHER_PORT 70         /* See protocol spec */
+#define BIG 1024               /* Bug */
+#define LINE_LENGTH 256                /* Bug */
+
+/*     Gopher entity types:
+*/
+#define GOPHER_TEXT            '0'
+#define GOPHER_MENU            '1'
+#define GOPHER_CSO             '2'
+#define GOPHER_ERROR           '3'
+#define GOPHER_MACBINHEX       '4'
+#define GOPHER_PCBINHEX                '5'
+#define GOPHER_UUENCODED       '6'
+#define GOPHER_INDEX           '7'
+#define GOPHER_TELNET          '8'
+#define GOPHER_HTML            'h'             /* HTML */
+#define GOPHER_DUPLICATE       '+'
+#define GOPHER_WWW             'w'             /* W3 address */
+
+#include <ctype.h>
+#include "HTUtils.h"           /* Coding convention macros */
+#include "tcp.h"
+
+#include "HTGopher.h"
+
+#include "HText.h"
+#include "HTParse.h"
+#include "HTFormat.h"
+#include "HTTCP.h"
+
+#ifdef NeXTStep
+#include <appkit/defaults.h>
+#define GOPHER_PROGRESS(foo)
+#else
+#define GOPHER_PROGRESS(foo) printf("%s\n", (foo))
+#endif
+
+extern HTStyleSheet * styleSheet;
+
+#define NEXT_CHAR HTGetChararcter()
+
+
+
+/*     Module-wide variables
+*/
+PRIVATE int s;                                 /* Socket for GopherHost */
+PRIVATE HText *        HT;                             /* the new hypertext */
+PRIVATE HTParentAnchor *node_anchor;           /* Its anchor */
+PRIVATE int    diagnostic;                     /* level: 0=none 2=source */
+
+PRIVATE HTStyle *addressStyle;                 /* For address etc */
+PRIVATE HTStyle *heading1Style;                        /* For heading level 1 */
+PRIVATE HTStyle *textStyle;                    /* Text style */
+
+
+/*     Matrix of allowed characters in filenames
+**     -----------------------------------------
+*/
+
+PRIVATE BOOL acceptable[256];
+PRIVATE BOOL acceptable_inited = NO;
+
+PRIVATE void init_acceptable NOARGS
+{
+    unsigned int i;
+    char * good = 
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$";
+    for(i=0; i<256; i++) acceptable[i] = NO;
+    for(;*good; good++) acceptable[(unsigned int)*good] = YES;
+    acceptable_inited = YES;
+}
+
+PRIVATE CONST char hex[17] = "0123456789abcdef";
+
+/*     Decdoe one hex character
+*/
+
+PRIVATE char from_hex ARGS1(char, c)
+{
+    return               (c>='0')&&(c<='9') ? c-'0'
+                       : (c>='A')&&(c<='F') ? c-'A'+10
+                       : (c>='a')&&(c<='f') ? c-'a'+10
+                       :                      0;
+}
+
+
+
+/*     Get Styles from stylesheet
+**     --------------------------
+*/
+PRIVATE void get_styles NOARGS
+{
+    if (!heading1Style) heading1Style = HTStyleNamed(styleSheet, "Heading1");
+    if (!addressStyle) addressStyle = HTStyleNamed(styleSheet, "Address");
+    if (!textStyle) textStyle = HTStyleNamed(styleSheet, "Example");
+}
+
+
+/*     Paste in an Anchor
+**     ------------------
+**
+**     The title of the destination is set, as there is no way
+**     of knowing what the title is when we arrive.
+**
+** On entry,
+**     HT      is in append mode.
+**     text    points to the text to be put into the file, 0 terminated.
+**     addr    points to the hypertext refernce address 0 terminated.
+*/
+PRIVATE void write_anchor ARGS2(CONST char *,text, CONST char *,addr)
+{
+    HTChildAnchor      *anchor;
+    HTParentAnchor     *dest;
+    
+    HText_beginAnchor(HT,
+               anchor = HTAnchor_findChildAndLink(node_anchor, "",  addr, 0));
+    dest = HTAnchor_parent(
+           HTAnchor_followMainLink((HTAnchor *)anchor));
+           
+    if (!HTAnchor_title(dest)) HTAnchor_setTitle(dest, text);
+           
+    HText_appendText(HT, text);
+    HText_endAnchor(HT);
+}
+
+
+/*     Parse a Gopher Menu document
+**     ============================
+**
+*/
+
+PRIVATE void parse_menu ARGS2 (
+       CONST char *,   arg,
+       HTParentAnchor *,anAnchor)
+{
+    char gtype;
+    char ch;
+    char line[BIG];
+    char address[BIG];
+    char *name, *selector;             /* Gopher menu fields */
+    char *host;
+    char *port;
+    char *p = line;
+    
+
+#define TAB            '\t'
+#define HEX_ESCAPE     '%'
+
+    if (!HTAnchor_title(anAnchor))
+       HTAnchor_setTitle(anAnchor, arg);/* Tell user something's happening */
+    
+    node_anchor = anAnchor;
+    HT = HText_new(anAnchor);
+    
+    HText_beginAppend(HT);
+    HText_appendText(HT, "Select one of:\n");
+    
+    while ((ch=NEXT_CHAR) != (char)EOF) {
+    
+        if (ch != '\n') {
+           *p = ch;            /* Put character in line */
+           if (p< &line[BIG-1]) p++;
+           
+       } else {
+           *p++ = 0;           /* Terminate line */
+           p = line;           /* Scan it to parse it */
+           port = 0;           /* Flag "not parsed" */
+           if (TRACE) fprintf(stderr, "HTGopher: Menu item: %s\n", line);
+           gtype = *p++;
+           
+           /* Break on line with a dot by itself */
+           if ((gtype=='.') && ((*p=='\r') || (*p==0))) break;
+
+           if (gtype && *p) {
+               name = p;
+               selector = strchr(name, TAB);
+               if (selector) {
+                   *selector++ = 0;    /* Terminate name */
+                   host = strchr(selector, TAB);
+                   if (host) {
+                       *host++ = 0;    /* Terminate selector */
+                       port = strchr(host, TAB);
+                       if (port) {
+                           char *junk;
+                           port[0] = ':';      /* delimit host a la W3 */
+                           junk = strchr(port, TAB);
+                           if (junk) *junk++ = 0;      /* Chop port */
+                           if ((port[1]=='0') && (!port[2]))
+                               port[0] = 0;    /* 0 means none */
+                       } /* no port */
+                   } /* host ok */
+               } /* selector ok */
+           } /* gtype and name ok */
+           
+           if (gtype == GOPHER_WWW) {  /* Gopher pointer to W3 */
+               write_anchor(name, selector);
+               HText_appendParagraph(HT);
+
+           } else if (port) {          /* Other types need port */
+               if (gtype == GOPHER_TELNET) {
+                   if (*selector) sprintf(address, "telnet://%s@%s/",
+                       selector, host);
+                   else sprintf(address, "telnet://%s/", host);
+                   
+               } else {                        /* If parsed ok */
+                   char *q;
+                   char *p;
+                   sprintf(address, "//%s/%c", host, gtype);
+                   q = address+ strlen(address);
+                   for(p=selector; *p; p++) {  /* Encode selector string */
+                       if (acceptable[*p]) *q++ = *p;
+                       else {
+                           *q++ = HEX_ESCAPE;  /* Means hex coming */
+                           *q++ = hex[(*p) >> 4];
+                           *q++ = hex[(*p) & 15];
+                       }
+                   }
+                   *q++ = 0;                   /* terminate address */
+               }
+               write_anchor(name, address);
+               HText_appendParagraph(HT);
+           } else { /* parse error */
+               if (TRACE) fprintf(stderr,
+                       "HTGopher: Bad menu item.\n");
+               HText_appendText(HT, line);
+               HText_appendParagraph(HT);
+           } /* parse error */
+           
+           p = line;   /* Start again at beginning of line */
+           
+       } /* if end of line */
+       
+    } /* Loop over characters */
+       
+    HText_endAppend(HT);
+    return;
+}
+
+/*     Display a Gopher Index document
+**     -------------------------------
+*/
+
+PRIVATE void display_index ARGS2 (
+       CONST char *,   arg,
+       HTParentAnchor *,anAnchor)
+{
+    node_anchor = anAnchor;
+    HT = HText_new(anAnchor);
+    HText_beginAppend(HT);
+    HText_setStyle(HT, heading1Style);
+    HText_appendText(HT, arg);
+    HText_setStyle(HT, textStyle);
+    HText_appendText(HT, "\nThis is a searchable index.\n");
+       
+    if (!HTAnchor_title(anAnchor))
+       HTAnchor_setTitle(anAnchor, arg);/* Tell user something's happening */
+    
+    HText_endAppend(HT);
+    return;
+}
+
+
+/*             Load by name                                    HTLoadGopher
+**             ============
+**
+**      Bug:   No decoding of strange data types as yet.
+**
+*/
+PUBLIC int HTLoadGopher ARGS3(
+       CONST char *,arg,
+       HTParentAnchor *,anAnchor,
+       int,diag)
+{
+    char *command;                     /* The whole command */
+    int status;                                /* tcp return */
+    char gtype;                                /* Gopher Node type */
+    char * selector;                   /* Selector string */
+    struct sockaddr_in soc_address;    /* Binary network address */
+    struct sockaddr_in* sin = &soc_address;
+
+    diagnostic = diag;                 /* set global flag */
+    
+    if (!acceptable_inited) init_acceptable();
+    
+    if (!arg) return -3;               /* Bad if no name sepcified     */
+    if (!*arg) return -2;              /* Bad if name had zero length  */
+    
+    if (TRACE) printf("HTGopher: Looking for %s\n", arg);
+    get_styles();
+    
+    
+/*  Set up defaults:
+*/
+    sin->sin_family = AF_INET;                 /* Family, host order  */
+    sin->sin_port = htons(GOPHER_PORT);                /* Default: new port,  */
+
+    if (TRACE) printf("HTTPAccess: Looking for %s\n", arg);
+
+/* Get node name and optional port number:
+*/
+    {
+       char *p1 = HTParse(arg, "", PARSE_HOST);
+       HTParseInet(sin, p1);
+        free(p1);
+    }
+    
+/* Get entity type, and selector string.
+*/        
+    {
+       char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
+        gtype = '1';           /* Default = menu */
+       selector = p1;
+       if ((*selector++=='/') && (*selector)) {        /* Skip first slash */
+           gtype = *selector++;                        /* Pick up gtype */
+       }
+       if (gtype == GOPHER_INDEX) {
+            HTAnchor_setIndex(anAnchor);       /* Search is allowed */
+           selector = strchr(selector, '?');   /* Look for search string */
+           if (!selector || !*selector) {      /* No search required */
+               display_index(arg, anAnchor);   /* Display "cover page" */
+               return 1;                       /* Local function only */
+           }
+           command = malloc(strlen(selector)+ 2 + 1);
+              if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
+           strcpy(command, selector);
+           
+       } else {                                /* Not index */
+           char * p = selector;
+           char * q = command = malloc(strlen(selector)+2+1);
+              if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
+           while (*p) {                /* Decode hex */
+               if (*p == HEX_ESCAPE) {
+                   char c;
+                   unsigned int b;
+                   p++;
+                   c = *p++;
+                   b =   from_hex(c);
+                   c = *p++;
+                   if (!c) break;      /* Odd number of chars! */
+                   *q++ = (b<<4) + from_hex(c);
+               } else {
+                   *q++ = *p++;        /* Record */
+               }
+           }
+           *q++ = 0;   /* Terminate command */
+       }
+       free(p1);
+    }
+    
+    strcat(command, "\r\n");           /* Include CR for telnet compat. */
+    
+
+/*     Set up a socket to the server for the data:
+*/      
+    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address));
+    if (status<0){
+       if (TRACE) printf("HTTPAccess: Unable to connect to remote host for `%s'.\n",
+           arg);
+       free(command);
+       return HTInetStatus("connect");
+    }
+    
+    HTInitInput(s);            /* Set up input buffereing */
+    
+    if (TRACE) printf("HTGopher: Connected, writing command `%s' to socket %d\n", command, s);
+    
+#ifdef NOT_ASCII
+    {
+       char * p;
+       for(p = command; *p; p++) {
+           *p = TOASCII(*p);
+       }
+    }
+#endif
+
+    status = NETWRITE(s, command, (int)strlen(command));
+    free(command);
+    if (status<0){
+       if (TRACE) printf("HTGopher: Unable to send command.\n");
+           return HTInetStatus("send");
+    }
+
+/*     Now read the data from the socket:
+*/    
+    if (diagnostic==2) gtype = GOPHER_TEXT;    /* Read as plain text anyway */
+    
+    switch (gtype) {
+    
+    case GOPHER_HTML :
+       HTParseFormat(WWW_HTML, anAnchor, s);
+       return 1;
+
+    case GOPHER_MENU :
+    case GOPHER_INDEX :
+        parse_menu(arg, anAnchor);
+       return 1;
+       
+    case GOPHER_TEXT :
+    default:                   /* @@ parse as plain text */
+       HTParseFormat(WWW_PLAINTEXT, anAnchor, s);
+       return 1;
+    } /* switch(gtype) */
+    /*NOTREACHED*/
+}
+
diff --git a/Cl/WWWLibrary/HTGopher.h b/Cl/WWWLibrary/HTGopher.h
new file mode 100644 (file)
index 0000000..83abf2e
--- /dev/null
@@ -0,0 +1,16 @@
+/*                     GOPHER ACCESS                           HTGopher.h
+**                     =============
+**
+** History:
+**      8 Jan 92       Adapted from HTTP TBL
+*/
+
+#ifndef HTGOPHER_H
+#define HTGOPHER_H
+
+#include "HTAnchor.h"
+extern int HTLoadGopher PARAMS((const char *arg,
+       HTParentAnchor * anAnchor,
+       int diag));
+
+#endif /* HTGOPHER_H */
diff --git a/Cl/WWWLibrary/HTHistory.c b/Cl/WWWLibrary/HTHistory.c
new file mode 100644 (file)
index 0000000..ca5bcb0
--- /dev/null
@@ -0,0 +1,154 @@
+#include "HTHistory.h"
+
+#include "tcp.h"               /* for standard io */
+
+static HTList * history;       /* List of visited anchors */
+
+
+/*                             Navigation
+**                             ==========
+*/
+
+/*             Record the jump to an anchor
+**             ----------------------------
+*/
+
+void HTHistory_record
+  ARGS1 (HTAnchor *,destination)
+{
+  if (destination) {
+    if (! history)
+      history = HTList_new();
+    HTList_addObject (history, destination);
+  }
+}
+
+/*             Go back in history (find the last visited node)
+**             ------------------
+*/
+
+HTAnchor * HTHistory_backtrack
+  NOARGS  /* FIXME: Should we add a `sticky' option ? */
+{
+  if (HTHistory_canBacktrack())
+    HTList_removeLastObject (history);
+  return HTList_lastObject (history);  /* is Home if can't backtrack */
+}
+
+BOOL HTHistory_canBacktrack
+  NOARGS
+{
+  return (HTList_objectAt (history, 1) != NULL);
+}
+
+/*             Browse through references in the same parent node
+**             -------------------------------------------------
+**
+**     Take the n-th child's link after or before the one we took to get here.
+**     Positive offset means go towards most recently added children.
+*/
+
+HTAnchor * HTHistory_moveBy
+ ARGS1 (int,offset)
+{
+  HTAnchor * last = HTList_objectAt (history, 1);
+  if (! last)
+    return NULL;  /* No last visited node */
+  if (last != (HTAnchor *) last->parent) {  /* Was a child */
+    HTList * kids = last->parent->children;
+    int i = HTList_indexOf (kids, last); 
+    HTAnchor * nextOne = HTList_objectAt (kids, i - offset);
+    if (nextOne) {
+      HTAnchor * destination = HTAnchor_followMainLink (nextOne);
+      if (destination) {
+       HTList_removeLastObject (history);
+       HTList_removeLastObject (history);
+       HTList_addObject (history, nextOne);
+       HTList_addObject (history, destination);
+      }
+      return destination;
+    } else {
+      if (TRACE) printf(
+               "HTHistory_moveBy: offset by %+d goes out of list %p.\n",
+               offset, kids);
+      return NULL;
+    }
+  } else {  /* Was a parent */
+    return NULL;  /* FIXME we could possibly follow the next link... */
+  }
+}
+
+BOOL HTHistory_canMoveBy
+ ARGS1 (int,offset)
+{
+  HTAnchor * last = HTList_objectAt (history, 1);
+  if (! last)
+    return NO;  /* No last visited node */
+  if (last != (HTAnchor *) last->parent) {  /* Was a child */
+    HTList * kids = last->parent->children;
+    int i = HTList_indexOf (kids, last); 
+    return (HTList_objectAt (kids, i - offset) != NULL);
+  } else {  /* Was a parent */
+    return NO;  /* FIXME we could possibly follow the next link... */
+  }
+}
+
+
+/*                             Retrieval
+**                             =========
+*/
+
+/*             Read numbered visited anchor (1 is the oldest)
+**             ----------------------------
+*/
+
+HTAnchor * HTHistory_read
+  ARGS1 (int,number)
+{
+  return HTList_objectAt (history, HTList_count (history) - number);
+}
+
+
+/*             Recall numbered visited anchor (1 is the oldest)
+**             ------------------------------
+**     This reads the anchor and stores it again in the list, except if last.
+*/
+
+HTAnchor * HTHistory_recall
+  ARGS1 (int,number)
+{
+  HTAnchor * destination =
+    HTList_objectAt (history, HTList_count (history) - number);
+  if (destination && destination != HTList_lastObject (history))
+    HTList_addObject (history, destination);
+  return destination;
+}
+
+/*             Number of Anchors stored
+**             ------------------------
+**
+**     This is needed in order to check the validity of certain commands
+**     for menus, etc.
+(not needed for now. Use canBacktrack, etc.)
+int HTHistory_count
+  NOARGS
+{
+  return HTList_count (history);
+}
+*/
+
+/*             Change last history entry
+**             -------------------------
+**
+**     Sometimes we load a node by one anchor but leave by a different
+**     one, and it is the one we left from which we want to remember.
+*/
+
+void HTHistory_leavingFrom
+  ARGS1 (HTAnchor *,anchor)
+{
+  if (HTList_removeLastObject (history))
+    HTList_addObject (history, anchor);
+  else
+    if (TRACE) fprintf(stderr, "HTHistory_leavingFrom: empty history !\n");
+}
diff --git a/Cl/WWWLibrary/HTHistory.h b/Cl/WWWLibrary/HTHistory.h
new file mode 100644 (file)
index 0000000..bb1bab0
--- /dev/null
@@ -0,0 +1,107 @@
+#ifndef HTHISTORY_H
+#define HTHISTORY_H
+
+#include "HTAnchor.h"
+
+#ifdef SHORT_NAMES
+#define HTHistory_record               HTHiReco
+#define HTHistory_backtrack            HTHiBack
+#define HTHistory_canBacktrack         HTHiCaBa
+#define HTHistory_moveBy               HTHiMoBy
+#define HTHistory_canMoveBy            HTHiCaMo
+#define HTHistory_read                 HTHiRead
+#define HTHistory_recall               HTHiReca
+#define HTHistory_count                        HTHiCoun
+#define HTHistory_leavingFrom          HTHiLeFr
+#endif
+
+/*                             Navigation
+**                             ==========
+*/
+
+/*             Record the jump to an anchor
+**             ----------------------------
+*/
+
+extern void HTHistory_record
+  PARAMS(
+    (HTAnchor * destination)
+  );
+
+/*             Go back in history (find the last visited node)
+**             ------------------
+*/
+
+extern HTAnchor * HTHistory_backtrack
+  NOPARAMS;  /* FIXME: Should we add a `sticky' option ? */
+
+extern BOOL HTHistory_canBacktrack
+  NOPARAMS;
+
+/*             Browse through references in the same parent node
+**             -------------------------------------------------
+**
+**     Take the n-th child's link after or before the one we took to get here.
+**     Positive offset means go towards most recently added children.
+*/
+
+extern HTAnchor * HTHistory_moveBy
+  PARAMS(
+     (int offset)
+     );
+
+extern BOOL HTHistory_canMoveBy
+  PARAMS(
+     (int offset)
+     );
+
+#define HTHistory_next (HTHistory_moveBy (+1))
+#define HTHistory_canNext (HTHistory_canMoveBy (+1))
+#define HTHistory_previous (HTHistory_moveBy (-1))
+#define HTHistory_canPrevious (HTHistory_canMoveBy (-1))
+
+
+/*                             Retrieval
+**                             =========
+*/
+
+/*             Read numbered visited anchor (1 is the oldest)
+**             ----------------------------
+*/
+
+extern HTAnchor * HTHistory_read
+  PARAMS(
+    (int number)
+  );
+
+/*             Recall numbered visited anchor (1 is the oldest)
+**             ------------------------------
+**     This reads the anchor and stores it again in the list, except if last.
+*/
+
+extern HTAnchor * HTHistory_recall
+  PARAMS(
+    (int number)
+  );
+
+/*             Number of Anchors stored
+**             ------------------------
+**
+**     This is needed in order to check the validity of certain commands
+**     for menus, etc.
+(not needed for now. Use canBacktrack, etc.)
+extern int HTHistory_count NOPARAMS;
+*/
+
+/*             Change last history entry
+**             -------------------------
+**
+**     Sometimes we load a node by one anchor but leave by a different
+**     one, and it is the one we left from which we want to remember.
+*/
+extern void HTHistory_leavingFrom
+  PARAMS(
+    (HTAnchor * anchor)
+  );
+
+#endif /* HTHISTORY_H */
diff --git a/Cl/WWWLibrary/HTList.c b/Cl/WWWLibrary/HTList.c
new file mode 100644 (file)
index 0000000..7d40f3e
--- /dev/null
@@ -0,0 +1,127 @@
+/*     A small List class                                            HTList.c
+**     ==================
+**
+**     A list is represented as a sequence of linked nodes of type HTList.
+**     The first node is a header which contains no object.
+**     New nodes are inserted between the header and the rest of the list.
+*/
+
+#include "HTList.h"
+
+#ifdef ERWISE
+#include <stdio.h>
+#endif
+
+HTList * HTList_new NOARGS
+{
+  HTList *newList = (HTList *)malloc (sizeof (HTList));
+  if (newList == NULL) outofmem(__FILE__, "HTList_new");
+  newList->object = NULL;
+  newList->next = NULL;
+  return newList;
+}
+
+void HTList_delete ARGS1(HTList *,this)
+{
+  HTList *current;
+  while (current = this) {
+    this = this->next;
+    free (current);
+  }
+}
+
+void HTList_addObject ARGS2(HTList *,this, void *,newObject)
+{
+  if (this) {
+    HTList *newNode = (HTList *)malloc (sizeof (HTList));
+    if (newNode == NULL) outofmem(__FILE__, "HTList_addObject");
+    newNode->object = newObject;
+    newNode->next = this->next;
+    this->next = newNode;
+  }
+  else
+    if (TRACE) printf ("HTList: Trying to add object %p to a nonexisting list\n",
+                      newObject);
+}
+
+BOOL HTList_removeObject ARGS2(HTList *,this, void *,oldObject)
+{
+  if (this) {
+    HTList *previous;
+    while (this->next) {
+      previous = this;
+      this = this->next;
+      if (this->object == oldObject) {
+       previous->next = this->next;
+       free (this);
+       return YES;  /* Success */
+      }
+    }
+  }
+  return NO;  /* object not found or NULL list */
+}
+
+void * HTList_removeLastObject ARGS1 (HTList *,this)
+{
+  if (this && this->next) {
+    HTList *lastNode = this->next;
+    void * lastObject = lastNode->object;
+    this->next = lastNode->next;
+    free (lastNode);
+    return lastObject;
+  } else  /* Empty list */
+    return NULL;
+}
+
+void * HTList_removeFirstObject ARGS1 (HTList *,this)
+{
+  if (this && this->next) {
+    HTList * prevNode;
+    void *firstObject;
+    while (this->next) {
+      prevNode = this;
+      this = this->next;
+    }
+    firstObject = this->object;
+    prevNode->next = NULL;
+    free (this);
+    return firstObject;
+  } else  /* Empty list */
+    return NULL;
+}
+
+int HTList_count ARGS1 (HTList *,this)
+{
+  int count = 0;
+  if (this)
+    while (this = this->next)
+      count++;
+  return count;
+}
+
+int HTList_indexOf ARGS2(HTList *,this, void *,object)
+{
+  if (this) {
+    int position = 0;
+    while (this = this->next) {
+      if (this->object == object)
+       return position;
+      position++;
+    }
+  }
+  return -1;  /* Object not in the list */
+}
+
+void * HTList_objectAt ARGS2 (HTList *,this, int,position)
+{
+  if (position < 0)
+    return NULL;
+  if (this) {
+    while (this = this->next) {
+      if (position == 0)
+       return this->object;
+      position--;
+    }
+  }
+  return NULL;  /* Reached the end of the list */
+}
diff --git a/Cl/WWWLibrary/HTList.h b/Cl/WWWLibrary/HTList.h
new file mode 100644 (file)
index 0000000..4aed995
--- /dev/null
@@ -0,0 +1,49 @@
+/*             List object
+**
+**     The list object is a generic container for storing collections
+**     of things in order.
+*/
+#ifndef HTLIST_H
+#define HTLIST_H
+
+#include "HTUtils.h"  /* for BOOL type and PARAMS and ARGS*/
+
+typedef struct _HTList HTList;
+
+struct _HTList {
+  void * object;
+  HTList * next;
+};
+
+#ifdef SHORT_NAMES
+#define HTList_new                     HTLiNew
+#define HTList_delete                  HTLiDele
+#define HTList_addObject               HTLiAdOb
+#define HTList_removeObject            HTLiReOb
+#define HTList_removeLastObject                HTLiReLa
+#define HTList_removeFirstObject       HTLiReFi
+#define HTList_count                   HTLiCoun
+#define HTList_indexOf                 HTLiInOf
+#define HTList_objectAt                        HTLiObAt
+#endif
+
+extern HTList *        HTList_new NOPARAMS;
+extern void    HTList_delete PARAMS((HTList *this));
+extern void    HTList_addObject PARAMS((HTList *this, void *newObject));
+extern BOOL    HTList_removeObject PARAMS((HTList *this, void *oldObject));
+extern void *  HTList_removeLastObject PARAMS((HTList *this));
+extern void *  HTList_removeFirstObject PARAMS((HTList *this));
+#define        HTList_isEmpty(this) (this ? this->next == NULL : YES)
+extern int     HTList_count PARAMS((HTList *this));
+extern int     HTList_indexOf PARAMS((HTList *this, void *object));
+#define        HTList_lastObject(this) \
+  (this && this->next ? this->next->object : NULL)
+extern void *  HTList_objectAt PARAMS((HTList *this, int position));
+
+/* Fast macro to traverse the list. Call it first with copy of list header :
+   it returns the first object and increments the passed list pointer.
+   Call it with the same variable until it returns NULL. */
+#define HTList_nextObject(this) \
+  (this && (this = this->next) ? this->object : NULL)
+
+#endif /* HTLIST_H */
diff --git a/Cl/WWWLibrary/HTML.c b/Cl/WWWLibrary/HTML.c
new file mode 100644 (file)
index 0000000..ec1471d
--- /dev/null
@@ -0,0 +1,429 @@
+/*             HTML Parser
+**             ===========
+*/
+#include <ctype.h>
+#include <stdio.h>
+
+#include "HTUtils.h"
+#include "SGML.h"
+#include "HTAtom.h"
+#include "HTChunk.h"
+#include "HText.h"
+#include "HTStyle.h"
+#include "HTML.h"
+
+
+/*                             SPECIAL HTML CODE
+**                             =================
+*/
+
+extern HTStyleSheet * styleSheet;      /* Application-wide */
+
+PRIVATE HTParentAnchor * node_anchor;
+PRIVATE HText * text;
+
+PRIVATE HTStyle * glossary_style;
+PRIVATE HTStyle * list_compact_style;
+PRIVATE HTStyle * glossary_compact_style;
+
+PRIVATE HTChunk title = { 0, 128, 0, 0 };      /* Grow by 128 */
+
+
+/*     Forward declarations of routines for DTD
+*/
+PRIVATE void no_change PARAMS((HTTag * t, HTElement * e));
+PRIVATE void begin_litteral PARAMS((HTTag * t, HTElement * e));
+PRIVATE void begin_element PARAMS((HTTag * t, HTElement * e));
+PRIVATE void end_element PARAMS((HTTag * t, HTElement * e));
+PRIVATE void begin_document PARAMS((HTTag * t, HTElement * e));
+PRIVATE void end_document PARAMS((HTTag * t, HTElement * e));
+PRIVATE void begin_anchor PARAMS((HTTag * t, HTElement * e));
+PRIVATE void end_anchor PARAMS((HTTag * t, HTElement * e));
+PRIVATE void begin_list PARAMS((HTTag * t, HTElement * e));
+PRIVATE void list_element PARAMS((HTTag * t, HTElement * e));
+PRIVATE void end_list PARAMS((HTTag * t, HTElement * e));
+PRIVATE void begin_glossary PARAMS((HTTag * t, HTElement * e));
+PRIVATE void end_glossary PARAMS((HTTag * t, HTElement * e));
+
+PRIVATE int got_styles = 0;
+PRIVATE void get_styles NOPARAMS;
+
+PRIVATE        BOOL            style_change;
+PRIVATE HTStyle *      new_style;
+PRIVATE HTStyle *      old_style;
+PRIVATE BOOL           in_word;  /* Have just had a non-white character */
+
+/*     Style buffering avoids dummy paragraph begin/ends.
+*/
+#define UPDATE_STYLE if (style_change) { \
+       HText_setStyle(text, new_style); \
+       old_style = new_style; \
+       style_change = NO; }
+
+PRIVATE void change_style ARGS1(HTStyle *,style)
+{
+    if (new_style!=style) {
+       style_change = YES /* was old_style == new_style */ ;
+       new_style = style;
+    }
+}
+
+
+/*             TITLE
+*/
+
+/*     Accumulate a character of title
+*/
+#ifdef __STDC__
+static void accumulate_string(char c)
+#else
+static void accumulate_string(c)
+    char c;
+#endif
+{
+    HTChunkPutc(&title, c);
+}
+
+
+/*             Clear the title
+*/
+PRIVATE  void clear_string ARGS2(HTTag *,t, HTElement *,e)
+{
+    HTChunkClear(&title);
+}
+
+PRIVATE void set_title ARGS2(HTTag *,t, HTElement *,e)
+{
+    HTChunkTerminate(&title);
+    HTAnchor_setTitle(node_anchor, title.data);
+}
+
+/*             Character handling
+*/
+PRIVATE void set_index ARGS2(HTTag *,t, HTElement *,e)
+{
+    HTAnchor_setIndex(node_anchor);
+}
+
+PRIVATE void pass_character ARGS1(char, c)
+{
+    if (style_change) {
+        if ((c=='\n') || (c==' ')) return;     /* Ignore it */
+        UPDATE_STYLE;
+    }
+    if (c=='\n') {
+        if (in_word) {
+           HText_appendCharacter(text, ' ');
+           in_word = NO;
+       }
+    } else {
+        HText_appendCharacter(text, c);
+       in_word = YES;
+    }
+}
+
+PRIVATE void litteral_text ARGS1(char, c)
+{
+/*     We guarrantee that the style is up-to-date in begin_litteral
+*/
+    HText_appendCharacter(text, c);            /* @@@@@ */
+}
+
+PRIVATE void ignore_text ARGS1(char, c)
+{
+    /* Do nothing */
+}
+
+PRIVATE void set_next_id  ARGS2(HTTag *,t, HTElement *,e)
+{
+    /* @@@@@  Bad SGML anyway */
+}
+
+PRIVATE void new_paragraph  ARGS2(HTTag *,t, HTElement *,e)
+{
+    UPDATE_STYLE;
+    HText_appendParagraph(text);
+    in_word = NO;
+}
+
+PRIVATE void term ARGS2(HTTag *,t, HTElement *,e)
+{
+    if (!style_change) {
+        HText_appendParagraph(text);
+       in_word = NO;
+    }
+}
+
+PRIVATE void definition ARGS2(HTTag *,t, HTElement *,e)
+{
+    UPDATE_STYLE;
+    pass_character('\t');      /* Just tab out one stop */
+    in_word = NO;
+}
+
+/*             Our Static DTD for HTML
+**             -----------------------
+*/
+
+static entity entities[] = {
+       { "lt", "<" },
+       { "gt", ">" },
+       { "amp", "&" },
+       { "bullet" , "\267" },                  /* @@@ NeXT only */
+       { 0,    0 }  /* Terminate list */
+};
+
+static attr no_attr[] = {{ 0, 0 , 0}};
+
+static attr a_attr[] = {                               /* Anchor attributes */
+#define A_ID 0
+       { "NAME", 0, 0 },                               /* Should be ID */
+#define A_TYPE 1
+       { "TYPE", 0, 0 },
+#define A_HREF 2
+       { "HREF", 0, 0 },
+       { 0, 0 , 0}     /* Terminate list */
+};     
+static attr list_attr[] = {
+#define LIST_COMPACT 0
+       { "COMPACT", 0, 0 },
+       { 0, 0, 0 }     /* Terminate list */
+};
+
+static attr glossary_attr[] = {
+#define GLOSSARY_COMPACT 0
+       { "COMPACT", 0, 0 },
+       { 0, 0, 0 }     /* Terminate list */
+};
+
+static HTTag default_tag =
+    { "DOCUMENT", no_attr , 0, 0, begin_document, pass_character, end_document };
+/*     NAME ATTR  STYLE LITERAL?  ON_BEGIN   ON__CHARACTER     ON_END
+*/
+static HTTag tags[] = {
+#define TITLE_TAG 0
+    { "TITLE", no_attr, 0, 0, clear_string, accumulate_string, set_title },
+#define ISINDEX_TAG 1
+    { "ISINDEX", no_attr, 0, 0, set_index, 0 , 0 },
+#define NEXTID_TAG 2
+    { "NEXTID", no_attr, 0, 0, set_next_id, 0, 0 },
+#define ADDRESS_TAG 3
+    { "ADDRESS"        , no_attr, 0, 0, begin_element, pass_character, end_element },
+#define H1_TAG 4
+    { "H1"     , no_attr, 0, 0, begin_element, pass_character, end_element },
+    { "H2"     , no_attr, 0, 0, begin_element, pass_character, end_element },
+    { "H3"     , no_attr, 0, 0, begin_element, pass_character, end_element },
+    { "H4"     , no_attr, 0, 0, begin_element, pass_character, end_element },
+    { "H5"     , no_attr, 0, 0, begin_element, pass_character, end_element },
+    { "H6"     , no_attr, 0, 0, begin_element, pass_character, end_element },
+    { "H7"     , no_attr, 0, 0, begin_element, pass_character, end_element },
+#define UL_TAG 11
+    { "UL"     , list_attr, 0, 0, begin_list, pass_character, end_list },
+#define OL_TAG 12
+    { "OL"     , list_attr, 0, 0, begin_list, pass_character, end_list },
+#define MENU_TAG 13
+    { "MENU"   , list_attr, 0, 0, begin_list, pass_character, end_list },
+#define DIR_TAG 14
+    { "DIR"    , list_attr, 0, 0, begin_list, pass_character, end_list },
+#define LI_TAG 15
+    { "LI"     , no_attr, 0, 0, list_element, pass_character, 0 },
+#define DL_TAG 16
+    { "DL"     , list_attr, 0, 0, begin_glossary, pass_character, end_glossary },
+    { "DT"     , no_attr, 0, 0, term, pass_character, 0 },
+    { "DD"     , no_attr, 0, 0, definition, pass_character, 0 },
+    { "A"      , a_attr,  0, 0, begin_anchor, pass_character, end_anchor },
+#define P_TAG 20
+    { "P"      , no_attr, 0, 0, new_paragraph, pass_character, 0 },
+#define XMP_TAG 21
+  { "XMP"      , no_attr, 0, YES, begin_litteral, litteral_text, end_element },
+#define LISTING_TAG 22
+  { "LISTING"  , no_attr, 0, YES,begin_litteral, litteral_text, end_element },
+#define PLAINTEXT_TAG 23
+  { "PLAINTEXT", no_attr, 0, YES, begin_litteral, litteral_text, end_element },
+#define COMMENT_TAG 24
+    { "COMMENT", no_attr, 0, YES, no_change, ignore_text, no_change },
+    { 0, 0, 0, 0,  0, 0 , 0}   /* Terminate list */
+};
+
+PUBLIC SGML_dtd HTML_dtd = { tags, &default_tag, entities };
+
+
+/*             Flattening the style structure
+**             ------------------------------
+**
+On the NeXT, and on any read-only browser, it is simpler for the text to have
+a sequence of styles, rather than a nested tree of styles. In this
+case we have to flatten the structure as it arrives from SGML tags into
+a sequence of styles.
+*/
+
+/*     Anchor handling
+**     ---------------
+*/
+PRIVATE void begin_anchor ARGS2(HTTag *,t, HTElement *,e)
+{
+    HTChildAnchor * source = HTAnchor_findChildAndLink(
+       node_anchor,                                            /* parent */
+       a_attr[A_ID].present    ? a_attr[A_ID].value : 0,       /* Tag */
+       a_attr[A_HREF].present  ? a_attr[A_HREF].value : 0,     /* Addresss */
+       a_attr[A_TYPE].present  ? 
+               (HTLinkType*)HTAtom_for(a_attr[A_TYPE].value)
+                : 0);
+    
+    UPDATE_STYLE;
+    HText_beginAnchor(text, source);
+}
+
+PRIVATE void end_anchor ARGS2(HTTag *,  t,
+                       HTElement *,    e)
+{
+    UPDATE_STYLE;
+    HText_endAnchor(text);
+}
+
+
+/*     General SGML Element Handling
+**     -----------------------------
+*/
+PRIVATE void begin_element ARGS2(HTTag *,t, HTElement *,e)
+{
+    change_style((HTStyle*)(t->style));
+}
+PRIVATE void no_change ARGS2(HTTag *,t, HTElement *,e)
+{
+    /* Do nothing */;
+}
+PRIVATE void begin_litteral ARGS2(HTTag *,t, HTElement *,e)
+{
+    change_style(t->style);
+    UPDATE_STYLE;
+}
+PRIVATE void end_element ARGS2(HTTag *,t, HTElement *,e)
+{
+    if (e) change_style(e->tag->style);
+}
+
+/*                     Lists
+*/
+PRIVATE void begin_list ARGS2(HTTag *,t, HTElement *,e)
+{
+    change_style(list_attr[LIST_COMPACT].present
+               ? list_compact_style
+               : (HTStyle*)(t->style));
+    in_word = NO;
+}
+
+PRIVATE void end_list ARGS2(HTTag *,t, HTElement *,e)
+{
+    change_style(e->tag->style);
+    in_word = NO;
+}
+
+PRIVATE void list_element  ARGS2(HTTag *,t, HTElement *,e)
+{
+    if (e->tag != &tags[DIR_TAG])
+       HText_appendParagraph(text);
+    else
+        HText_appendCharacter(text, '\t');     /* Tab @@ nl for UL? */
+    in_word = NO;
+}
+
+
+PRIVATE void begin_glossary ARGS2(HTTag *,t, HTElement *,e)
+{
+    change_style(glossary_attr[GLOSSARY_COMPACT].present
+               ? glossary_compact_style
+               : glossary_style);
+    in_word = NO;
+}
+
+PRIVATE void end_glossary ARGS2(HTTag *,t, HTElement *,e)
+{
+    change_style(e->tag->style);
+    in_word = NO;
+}
+
+
+/*     Begin and End document
+**     ----------------------
+*/
+PUBLIC void HTML_begin ARGS1(HTParentAnchor *,anchor)
+{
+    node_anchor = anchor;
+}
+
+PRIVATE void begin_document ARGS2(HTTag *, t, HTElement *, e)
+{
+    if (!got_styles) get_styles();
+    text = HText_new(node_anchor);
+    HText_beginAppend(text);
+    HText_setStyle(text, default_tag.style);
+    old_style = 0;
+    style_change = NO;
+    in_word = NO;
+}
+
+PRIVATE void end_document ARGS2(HTTag *, t, HTElement *, e)
+{
+    HText_endAppend(text);
+
+}
+
+/*     Get Styles from style sheet
+**     ---------------------------
+*/
+PRIVATE void get_styles NOARGS
+{
+    got_styles = YES;
+    
+    tags[P_TAG].style =
+    default_tag.style =                HTStyleNamed(styleSheet, "Normal");
+    tags[H1_TAG].style =       HTStyleNamed(styleSheet, "Heading1");
+    tags[H1_TAG+1].style =     HTStyleNamed(styleSheet, "Heading2");
+    tags[H1_TAG+2].style =     HTStyleNamed(styleSheet, "Heading3");
+    tags[H1_TAG+3].style =     HTStyleNamed(styleSheet, "Heading4");
+    tags[H1_TAG+4].style =     HTStyleNamed(styleSheet, "Heading5");
+    tags[H1_TAG+5].style =     HTStyleNamed(styleSheet, "Heading6");
+    tags[H1_TAG+6].style =     HTStyleNamed(styleSheet, "Heading7");
+    tags[DL_TAG].style =       HTStyleNamed(styleSheet, "Glossary");
+    tags[UL_TAG].style =       HTStyleNamed(styleSheet, "List");
+    tags[OL_TAG].style =       HTStyleNamed(styleSheet, "List");
+    tags[MENU_TAG].style =     HTStyleNamed(styleSheet, "Menu");
+    list_compact_style =
+    tags[DIR_TAG].style =      HTStyleNamed(styleSheet, "Dir");    
+    glossary_style =           HTStyleNamed(styleSheet, "Glossary");
+    glossary_compact_style =   HTStyleNamed(styleSheet, "GlossaryCompact");
+    tags[ADDRESS_TAG].style=   HTStyleNamed(styleSheet, "Address");
+    tags[PLAINTEXT_TAG].style =
+    tags[XMP_TAG].style =      HTStyleNamed(styleSheet, "Example");
+    tags[LISTING_TAG].style =  HTStyleNamed(styleSheet, "Listing");
+}
+
+
+/*     Parse an HTML file
+**     ------------------
+**
+**     This version takes a pointer to the routine to call
+**     to get each character.
+*/
+BOOL HTML_Parse
+#ifdef __STDC__
+  (HTParentAnchor * anchor, char (*next_char)() )
+#else
+  (anchor, next_char)
+    HTParentAnchor * anchor;
+    char (*next_char)();
+#endif
+{
+        HTML_begin(anchor);
+       SGML_begin(&HTML_dtd);
+       for(;;) {
+           char character;
+           character = (*next_char)();
+           if (character == (char)EOF) break;
+    
+           SGML_character(&HTML_dtd, character);           
+         }
+       SGML_end(&HTML_dtd);
+       return YES;
+}
diff --git a/Cl/WWWLibrary/HTML.h b/Cl/WWWLibrary/HTML.h
new file mode 100644 (file)
index 0000000..acbe240
--- /dev/null
@@ -0,0 +1,17 @@
+/*             The HTML Parser                                 HTML.h
+**             ---------------
+*/
+
+#ifndef HTML_H
+#define HTML_H
+
+#include "HTUtils.h"
+#include "HTAnchor.h"
+#include "SGML.h"
+
+extern SGML_dtd HTML_dtd;      /* The DTD */
+extern void HTML_begin PARAMS((HTParentAnchor * anchor));
+extern BOOL HTML_Parse PARAMS((
+       HTParentAnchor * anchor,
+       char (*next_char)() ));
+#endif
diff --git a/Cl/WWWLibrary/HTNews.c b/Cl/WWWLibrary/HTNews.c
new file mode 100644 (file)
index 0000000..94b827d
--- /dev/null
@@ -0,0 +1,943 @@
+/*                     NEWS ACCESS                             HTNews.c
+**                     ===========
+**
+** History:
+**     26 Sep 90       Written TBL
+**     29 Nov 91       Downgraded to C, for portable implementation.
+*/
+
+#define NEWS_PORT 119          /* See rfc977 */
+#define APPEND                 /* Use append methods */
+#define MAX_CHUNK      40      /* Largest number of articles in one window */
+#define CHUNK_SIZE     20      /* Number of articles for quick display */
+
+#ifndef DEFAULT_NEWS_HOST
+#define DEFAULT_NEWS_HOST "news"
+#endif
+#ifndef SERVER_FILE
+#define SERVER_FILE "/usr/local/lib/rn/server"
+#endif
+
+#include <ctype.h>
+#include "HTUtils.h"           /* Coding convention macros */
+#include "tcp.h"
+
+#include "HTNews.h"
+
+#include "HText.h"
+#include "HTParse.h"
+#include "HTFormat.h"
+
+#ifdef NeXTStep
+#include <appkit/defaults.h>
+#define NEWS_PROGRESS(foo)
+#else
+#define NEWS_PROGRESS(foo) fprintf(stderr, "%s\n", (foo))
+#endif
+
+extern HTStyleSheet * styleSheet;
+
+#define NEXT_CHAR HTGetChararcter()
+#define LINE_LENGTH 512                        /* Maximum length of line of ARTICLE etc */
+#define GROUP_NAME_LENGTH      256     /* Maximum length of group name */
+
+
+/*     Module-wide variables
+*/
+PRIVATE char * NewsHost;
+PRIVATE struct sockaddr_in soc_address;                /* Binary network address */
+PRIVATE int s;                                 /* Socket for NewsHost */
+PRIVATE char response_text[LINE_LENGTH+1];     /* Last response */
+PRIVATE HText *        HT;                             /* the new hypertext */
+PRIVATE HTParentAnchor *node_anchor;           /* Its anchor */
+PRIVATE int    diagnostic;                     /* level: 0=none 2=source */
+
+PRIVATE HTStyle *addressStyle;                 /* For address etc */
+PRIVATE HTStyle *heading1Style;                        /* For heading level 1 */
+PRIVATE HTStyle *textStyle;                    /* Text style */
+
+
+/*     Initialisation for this module
+**     ------------------------------
+**
+**     Except on the NeXT, we pick up the NewsHost name from
+**
+**     1.      Environment variable NNTPSERVER
+**     2.      File SERVER_FILE
+**     3.      Compilation time macro DEFAULT_NEWS_HOST
+**     4.      Default to "news"
+**
+**     On the NeXT, we pick up the NewsHost name from, in order:
+**
+**     1.      WorldWideWeb default "NewsHost"
+**     2.      Global default "NewsHost"
+**     3.      News default "NewsHost"
+**     4.      Compilation time macro DEFAULT_NEWS_HOST
+**     5.      Defualt to "news"
+*/
+PRIVATE BOOL initialized = NO;
+PRIVATE BOOL initialize NOARGS
+{
+    CONST struct hostent  *phost;        /* Pointer to host - See netdb.h */
+    struct sockaddr_in* sin = &soc_address;
+
+        
+/*  Set up defaults:
+*/
+    sin->sin_family = AF_INET;         /* Family = internet, host order  */
+    sin->sin_port = htons(NEWS_PORT);   /* Default: new port,    */
+
+/*   Get name of Host
+*/
+#ifdef NeXTStep
+    if ((NewsHost = NXGetDefaultValue("WorldWideWeb","NewsHost"))==0)
+        if ((NewsHost = NXGetDefaultValue("News","NewsHost")) == 0)
+           NewsHost = "cernvax.cern.ch";
+#else
+    if (getenv("NNTPSERVER")) {
+        StrAllocCopy(NewsHost, (char *)getenv("NNTPSERVER"));
+       if (TRACE) fprintf(stderr, "HTNews: NNTPSERVER defined as `%s'\n",
+               NewsHost);
+    } else {
+        char server_name[256];
+        FILE* fp = fopen(SERVER_FILE, "r");
+        if (fp) {
+           if (fscanf(fp, "%s", server_name)==1) {
+               StrAllocCopy(NewsHost, server_name);
+               if (TRACE) fprintf(stderr,
+               "HTNews: File %s defines news host as `%s'\n",
+                       SERVER_FILE, NewsHost);
+           }
+           fclose(fp);
+       }
+    }
+    if (!NewsHost) NewsHost = DEFAULT_NEWS_HOST;
+#endif
+
+    if (*NewsHost>='0' && *NewsHost<='9') {   /* Numeric node address: */
+       sin->sin_addr.s_addr = inet_addr((char *)NewsHost); /* See arpa/inet.h */
+
+    } else {               /* Alphanumeric node name: */
+       phost=gethostbyname((char*)NewsHost);   /* See netdb.h */
+       if (!phost) {
+#ifdef NeXTStep
+           NXRunAlertPanel(NULL, "Can't find internet node name `%s'.",
+               NULL,NULL,NULL,
+               NewsHost);
+#else
+           fprintf(stderr,
+             "HTNews: Can't find internet node name `%s'.\n",NewsHost);
+#endif
+           CTRACE(tfp,
+             "HTNews: Can't find internet node name `%s'.\n",NewsHost);
+           return NO;  /* Fail */
+       }
+       memcpy(&sin->sin_addr, phost->h_addr, phost->h_length);
+    }
+
+    if (TRACE) printf( 
+       "HTNews: Parsed address as port %4x, inet %d.%d.%d.%d\n",
+               (unsigned int)ntohs(sin->sin_port),
+               (int)*((unsigned char *)(&sin->sin_addr)+0),
+               (int)*((unsigned char *)(&sin->sin_addr)+1),
+               (int)*((unsigned char *)(&sin->sin_addr)+2),
+               (int)*((unsigned char *)(&sin->sin_addr)+3));
+
+    s = -1;            /* Disconnected */
+    
+    return YES;
+}
+
+
+
+/*     Get Styles from stylesheet
+**     --------------------------
+*/
+PRIVATE void get_styles NOARGS
+{
+    if (!heading1Style) heading1Style = HTStyleNamed(styleSheet, "Heading1");
+    if (!addressStyle) addressStyle = HTStyleNamed(styleSheet, "Address");
+    if (!textStyle) textStyle = HTStyleNamed(styleSheet, "Example");
+}
+
+
+/*     Send NNTP Command line to remote host & Check Response
+**     ------------------------------------------------------
+**
+** On entry,
+**     command points to the command to be sent, including CRLF, or is null
+**             pointer if no command to be sent.
+** On exit,
+**     Negative status indicates transmission error, socket closed.
+**     Positive status is an NNTP status.
+*/
+
+
+PRIVATE int response ARGS1(CONST char *,command)
+{
+    int result;    
+    char * p = response_text;
+    if (command) {
+        int status = NETWRITE(s, command, (int)strlen(command));
+       if (status<0){
+           if (TRACE) fprintf(stderr,
+               "HTNews: Unable to send `%s'. Disconnecting.\n");
+           NETCLOSE(s);
+           s = -1;
+           return status;
+       } /* if bad status */
+       if (TRACE) printf("NNTP command sent: %s", command);
+    } /* if command to be sent */
+    
+    for(;;) {  
+       if (((*p++=NEXT_CHAR) == '\n') || (p == &response_text[LINE_LENGTH])) {
+           *p++=0;                             /* Terminate the string */
+           if (TRACE) printf("NNTP Response: %s\n", response_text);
+           sscanf(response_text, "%d", &result);
+           return result;          
+       } /* if end of line */
+       
+       if (*(p-1) < 0) {
+           if (TRACE) fprintf(stderr,
+               "HTNews: EOF on read, closing socket %d\n", s);
+           NETCLOSE(s);        /* End of file, close socket */
+           return s = -1;      /* End of file on response */
+       }
+    } /* Loop over characters */
+}
+
+
+/*     Case insensitive string comparisons
+**     -----------------------------------
+**
+** On entry,
+**     template must be already un upper case.
+**     unknown may be in upper or lower or mixed case to match.
+*/
+PRIVATE BOOL match ARGS2 (CONST char *,unknown, CONST char *,template)
+{
+    CONST char * u = unknown;
+    CONST char * t = template;
+    for (;*u && *t && (TOUPPER(*u)==*t); u++, t++) /* Find mismatch or end */ ;
+    return (BOOL)(*t==0);              /* OK if end of template */
+}
+
+/*     Find Author's name in mail address
+**     ----------------------------------
+**
+** On exit,
+**     THE EMAIL ADDRESS IS CORRUPTED
+**
+** For example, returns "Tim Berners-Lee" if given any of
+**     " Tim Berners-Lee <tim@online.cern.ch> "
+**  or " tim@online.cern.ch ( Tim Berners-Lee ) "
+*/
+PRIVATE char * author_name ARGS1 (char *,email)
+{
+    char *s, *e;
+    
+    if ((s=strchr(email,'(')) && (e=strchr(email, ')')))
+        if (e>s) {
+           *e=0;                       /* Chop off everything after the ')'  */
+           return HTStrip(s+1);        /* Remove leading and trailing spaces */
+       }
+       
+    if ((s=strchr(email,'<')) && (e=strchr(email, '>')))
+        if (e>s) {
+           strcpy(s, e+1);             /* Remove <...> */
+           return HTStrip(email);      /* Remove leading and trailing spaces */
+       }
+       
+    return HTStrip(email);             /* Default to the whole thing */
+
+}
+
+
+/*     Paste in an Anchor
+**     ------------------
+**
+**
+** On entry,
+**     HT      has a selection of zero length at the end.
+**     text    points to the text to be put into the file, 0 terminated.
+**     addr    points to the hypertext refernce address,
+**             terminated by white space, comma, NULL or '>' 
+*/
+PRIVATE void write_anchor ARGS2(CONST char *,text, CONST char *,addr)
+{
+    char href[LINE_LENGTH+1];
+               
+    {
+       CONST char * p;
+       strcpy(href,"news:");
+       for(p=addr; *p && (*p!='>') && !WHITE(*p) && (*p!=','); p++);
+        strncat(href, addr, p-addr);   /* Make complete hypertext reference */
+    }
+    
+    HText_beginAnchor(HT,
+               HTAnchor_findChildAndLink(node_anchor, "",  href, 0));
+    HText_appendText(HT, text);
+    HText_endAnchor(HT);
+}
+
+
+/*     Write list of anchors
+**     ---------------------
+**
+**     We take a pointer to a list of objects, and write out each,
+**     generating an anchor for each.
+**
+** On entry,
+**     HT      has a selection of zero length at the end.
+**     text    points to a comma or space separated list of addresses.
+** On exit,
+**     *text   is NOT any more chopped up into substrings.
+*/
+PRIVATE void write_anchors ARGS1 (char *,text)
+{
+    char * start = text;
+    char * end;
+    char c;
+    for (;;) {
+        for(;*start && (WHITE(*start)); start++);  /* Find start */
+       if (!*start) return;                    /* (Done) */
+        for(end=start; *end && (*end!=' ') && (*end!=','); end++);/* Find end */
+       if (*end) end++;        /* Include comma or space but not NULL */
+       c = *end;
+       *end = 0;
+       write_anchor(start, start);
+       *end = c;
+       start = end;                    /* Point to next one */
+    }
+}
+
+/*     Abort the connection                                    abort_socket
+**     --------------------
+*/
+PRIVATE void abort_socket NOARGS
+{
+    if (TRACE) fprintf(stderr,
+           "HTNews: EOF on read, closing socket %d\n", s);
+    NETCLOSE(s);       /* End of file, close socket */
+    HText_appendText(HT, "Network Error: connection lost");
+    HText_appendParagraph(HT);
+    s = -1;            /* End of file on response */
+    return;
+}
+
+/*     Read in an Article                                      read_article
+**     ------------------
+**
+**
+**     Note the termination condition of a single dot on a line by itself.
+**     RFC 977 specifies that the line "folding" of RFC850 is not used, so we
+**     do not handle it here.
+**
+** On entry,
+**     s       Global socket number is OK
+**     HT      Global hypertext object is ready for appending text
+*/       
+PRIVATE void read_article NOARGS
+{
+
+    char line[LINE_LENGTH+1];
+    char *references=NULL;                     /* Hrefs for other articles */
+    char *newsgroups=NULL;                     /* Newsgroups list */
+    char *p = line;
+    BOOL done = NO;
+    
+/*     Read in the HEADer of the article:
+**
+**     The header fields are either ignored, or formatted and put into the
+**      Text.
+*/
+    if (!diagnostic) {
+        HText_setStyle(HT, addressStyle);
+       while(!done){
+           char ch = *p++ = NEXT_CHAR;
+           if (ch==(char)EOF) {
+               abort_socket(); /* End of file, close socket */
+               return;         /* End of file on response */
+           }
+           if ((ch == '\n') || (p == &line[LINE_LENGTH])) {
+               *--p=0;                         /* Terminate the string */
+               if (TRACE) printf("H %s\n", line);
+
+               if (line[0]=='.') {     
+                   if (line[1]<' ') {          /* End of article? */
+                       done = YES;
+                       break;
+                   }
+               
+               } else if (line[0]<' ') {
+                   break;              /* End of Header? */
+               } else if (match(line, "SUBJECT:")) {
+                   HTAnchor_setTitle(node_anchor, line+8);
+                   HText_setStyle(HT, heading1Style);
+                   HText_appendText(HT, line+8);
+                   HText_setStyle(HT, addressStyle);
+               } else if (match(line, "DATE:")
+                       || match(line, "FROM:")
+                       || match(line, "ORGANIZATION:")) {
+                   strcat(line, "\n");
+                   HText_appendText(HT, strchr(line,':')+1);
+               } else if (match(line, "NEWSGROUPS:")) {
+                   StrAllocCopy(newsgroups, HTStrip(strchr(line,':')+1));
+                   
+               } else if (match(line, "REFERENCES:")) {
+                   StrAllocCopy(references, HTStrip(strchr(line,':')+1));
+                   
+               } /* end if match */
+               p = line;                       /* Restart at beginning */
+           } /* if end of line */
+       } /* Loop over characters */
+    
+       HText_appendCharacter(HT, '\n');
+       HText_setStyle(HT, textStyle);
+       if (newsgroups) {
+           HText_appendText(HT, "\nNewsgroups: ");
+           write_anchors(newsgroups);
+           free(newsgroups);
+       }
+       
+       if (references) {
+           HText_appendText(HT, "\nReferences: ");
+           write_anchors(references);
+           free(references);
+       }
+    
+       HText_appendText(HT, "\n\n\n");
+       
+    } else { /* diagnostic */
+        HText_setStyle(HT, textStyle);
+    }
+    
+/*     Read in the BODY of the Article:
+*/
+    p = line;
+    while(!done){
+       char ch = *p++ = NEXT_CHAR;
+       if (ch==(char)EOF) {
+           abort_socket();     /* End of file, close socket */
+           return;             /* End of file on response */
+       }
+       if ((ch == '\n') || (p == &line[LINE_LENGTH])) {
+           *p++=0;                             /* Terminate the string */
+           if (TRACE) printf("B %s", line);
+           if (line[0]=='.') {
+               if (line[1]<' ') {              /* End of article? */
+                   done = YES;
+                   break;
+               } else {                        /* Line starts with dot */
+                   HText_appendText(HT, &line[1]);     /* Ignore first dot */
+               }
+           } else {
+
+/*     Normal lines are scanned for buried references to other articles.
+**     Unfortunately, it will pick up mail addresses as well!
+*/
+               char *l = line;
+               char * p;
+               while (p=strchr(l, '<')) {
+                   char *q  = strchr(p,'>');
+                   char *at = strchr(p, '@');
+                   if (q && at && at<q) {
+                       char c = q[1];
+                       q[1] = 0;               /* chop up */
+                       *p = 0;
+                       HText_appendText(HT, l);
+                       *p = '<';               /* again */
+                       *q = 0;
+                       HText_beginAnchor(HT,
+                           HTAnchor_findChildAndLink(
+                               node_anchor, "", p+1, 0));
+                       *q = '>';               /* again */
+                       HText_appendText(HT, p);
+                       HText_endAnchor(HT);
+                       q[1] = c;               /* again */
+                       l=q+1;
+                   } else break;               /* line has unmatched <> */
+               } 
+               HText_appendText(HT,  l);       /* Last bit of the line */
+           } /* if not dot */
+           p = line;                           /* Restart at beginning */
+       } /* if end of line */
+    } /* Loop over characters */
+}
+
+
+/*     Read in a List of Newsgroups
+**     ----------------------------
+*/
+/*
+**     Note the termination condition of a single dot on a line by itself.
+**     RFC 977 specifies that the line "folding" of RFC850 is not used, so we
+**     do not handle it here.
+*/        
+PRIVATE void read_list NOARGS
+{
+
+    char line[LINE_LENGTH+1];
+    char *p;
+    BOOL done = NO;
+    
+/*     Read in the HEADer of the article:
+**
+**     The header fields are either ignored, or formatted and put into the
+**     Text.
+*/
+    HText_appendText(HT,  "\nNewsgroups:\n\n");        /* Should be haeding style */
+    p = line;
+    while(!done){
+       char ch = *p++ = NEXT_CHAR;
+       if (ch==(char)EOF) {
+           abort_socket();     /* End of file, close socket */
+           return;             /* End of file on response */
+       }
+       if ((ch == '\n') || (p == &line[LINE_LENGTH])) {
+           *p++=0;                             /* Terminate the string */
+           if (TRACE) printf("B %s", line);
+           if (line[0]=='.') {
+               if (line[1]<' ') {              /* End of article? */
+                   done = YES;
+                   break;
+               } else {                        /* Line starts with dot */
+                   HText_appendText(HT,  &line[1]);
+               }
+           } else {
+
+/*     Normal lines are scanned for references to newsgroups.
+*/
+               char group[LINE_LENGTH];
+               int first, last;
+               char postable;
+               if (sscanf(line, "%s %d %d %c", group, &first, &last, &postable)==4)
+                   write_anchor(line, group);
+               else
+                   HText_appendText(HT, line);
+           } /* if not dot */
+           p = line;                   /* Restart at beginning */
+       } /* if end of line */
+    } /* Loop over characters */
+}
+
+
+/*     Read in a Newsgroup
+**     -------------------
+**     Unfortunately, we have to ask for each article one by one if we
+**     want more than one field.
+**
+*/
+PRIVATE void read_group ARGS3(
+  CONST char *,groupName,
+  int,first_required,
+  int,last_required
+)
+{
+    char line[LINE_LENGTH+1];
+    char author[LINE_LENGTH+1];
+    char subject[LINE_LENGTH+1];
+    char *p;
+    BOOL done;
+
+    char buffer[LINE_LENGTH];
+    char *reference=0;                 /* Href for article */
+    int art;                           /* Article number WITHIN GROUP */
+    int status, count, first, last;    /* Response fields */
+                                       /* count is only an upper limit */
+
+    sscanf(response_text, " %d %d %d %d", &status, &count, &first, &last);
+    if(TRACE) printf("Newsgroup status=%d, count=%d, (%d-%d) required:(%d-%d)\n",
+                       status, count, first, last, first_required, last_required);
+    if (last==0) {
+        HText_appendText(HT,  "\nNo articles in this group.\n");
+       return;
+    }
+    
+#define FAST_THRESHOLD 100     /* Above this, read IDs fast */
+#define CHOP_THRESHOLD 50      /* Above this, chop off the rest */
+
+    if (first_required<first) first_required = first;          /* clip */
+    if ((last_required==0) || (last_required > last)) last_required = last;
+    
+    if (last_required<=first_required) {
+        HText_appendText(HT,  "\nNo articles in this range.\n");
+       return;
+    }
+
+    if (last_required-first_required+1 > MAX_CHUNK) {  /* Trim this block */
+        first_required = last_required-CHUNK_SIZE+1;
+    }
+    if (TRACE) printf (
+    "    Chunk will be (%d-%d)\n", first_required, last_required);
+
+/*     Link to earlier articles
+*/
+    if (first_required>first) {
+       int before;                     /* Start of one before */
+       if (first_required-MAX_CHUNK <= first) before = first;
+       else before = first_required-CHUNK_SIZE;
+       sprintf(buffer, "%s/%d-%d", groupName, before, first_required-1);
+       if (TRACE) printf("    Block before is %s\n", buffer);
+       HText_appendText(HT,  " (");
+       HText_beginAnchor(HT,
+           HTAnchor_findChildAndLink(node_anchor, "", buffer, 0));
+       HText_appendText(HT,  "Earlier articles");
+       HText_endAnchor(HT);
+       HText_appendText(HT,  "...)\n");
+    }
+    
+    done = NO;
+
+/*#define USE_XHDR*/
+#ifdef USE_XHDR
+    if (count>FAST_THRESHOLD)  {
+        sprintf(buffer,
+       "\nThere are about %d articles currently available in %s, IDs as follows:\n\n",
+               count, groupName); 
+        HText_appendText(HT, buffer);
+        sprintf(buffer, "XHDR Message-ID %d-%d\n", first, last);
+       status = response(buffer);
+       if (status==221) {
+
+           p = line;
+           while(!done){
+               char ch = *p++ = NEXT_CHAR;
+               if (ch==(char)EOF) {
+                   abort_socket();     /* End of file, close socket */
+                   return;             /* End of file on response */
+               }
+               if ((ch == '\n') || (p == &line[LINE_LENGTH])) {
+                   *p++=0;                             /* Terminate the string */
+                   if (TRACE) printf("X %s", line);
+                   if (line[0]=='.') {
+                       if (line[1]<' ') {              /* End of article? */
+                           done = YES;
+                           break;
+                       } else {                        /* Line starts with dot */
+                               /* Ignore strange line */
+                       }
+                   } else {
+       
+       /*      Normal lines are scanned for references to articles.
+       */
+                       char * space = strchr(line, ' ');
+                       if (space++)
+                           write_anchor(space, space);
+                   } /* if not dot */
+                   p = line;                   /* Restart at beginning */
+               } /* if end of line */
+           } /* Loop over characters */
+
+           /* leaving loop with "done" set */
+       } /* Good status */
+    };
+#endif
+
+/*     Read newsgroup using individual fields:
+*/
+    if (!done) {
+        if (first==first_required && last==last_required)
+               HText_appendText(HT, "\nAll available articles in ");
+        else HText_appendText(HT,  "\nArticles in ");
+       HText_appendText(HT, groupName);
+       HText_appendText(HT, "\n\n");
+       for(art=first_required; art<=last_required; art++) {
+    
+/*#define OVERLAP*/
+#ifdef OVERLAP
+/* With this code we try to keep the server running flat out by queuing just
+** one extra command ahead of time. We assume (1) that the server won't abort
+** if it get input during output, and (2) that TCP buffering is enough for the
+** two commands. Both these assumptions seem very reasonable. However, we HAVE
+** had a hangup with a loaded server.
+*/
+           if (art==first_required) {
+               if (art==last_required) {
+                       sprintf(buffer, "HEAD %d\n", art);      /* Only one */
+                       status = response(buffer);
+                   } else {                                    /* First of many */
+                       sprintf(buffer, "HEAD %d\nHEAD %d\n", art, art+1);
+                       status = response(buffer);
+                   }
+           } else if (art==last_required) {                    /* Last of many */
+                   status = response(NULL);
+           } else {                                            /* Middle of many */
+                   sprintf(buffer, "HEAD %d\n", art+1);
+                   status = response(buffer);
+           }
+           
+#else  /* NOT OVERLAP */
+           sprintf(buffer, "HEAD %d\n", art);
+           status = response(buffer);
+#endif /* NOT OVERLAP */
+
+           if (status == 221) {        /* Head follows - parse it:*/
+    
+               p = line;                               /* Write pointer */
+               done = NO;
+               while(!done){
+                   char ch = *p++ = NEXT_CHAR;
+                   if (ch==(char)EOF) {
+                       abort_socket(); /* End of file, close socket */
+                       return;         /* End of file on response */
+                   }
+                   if ((ch == '\n')
+                       || (p == &line[LINE_LENGTH]) ) {
+                   
+                       *--p=0;         /* Terminate  & chop LF*/
+                       p = line;               /* Restart at beginning */
+                       if (TRACE) printf("G %s\n", line);
+                       switch(line[0]) {
+    
+                       case '.':
+                           done = (line[1]<' ');       /* End of article? */
+                           break;
+    
+                       case 'S':
+                       case 's':
+                           if (match(line, "SUBJECT:"))
+                               strcpy(subject, line+9);/* Save subject */
+                           break;
+    
+                       case 'M':
+                       case 'm':
+                           if (match(line, "MESSAGE-ID:")) {
+                               char * addr = HTStrip(line+11) +1; /* Chop < */
+                               addr[strlen(addr)-1]=0;         /* Chop > */
+                               StrAllocCopy(reference, addr);
+                           }
+                           break;
+    
+                       case 'f':
+                       case 'F':
+                           if (match(line, "FROM:")) {
+                               char * p;
+                               strcpy(author,
+                                       author_name(strchr(line,':')+1));
+                               p = author + strlen(author) - 1;
+                               if (*p=='\n') *p = 0;   /* Chop off newline */
+                           }
+                           break;
+                                   
+                       } /* end switch on first character */
+                   } /* if end of line */
+               } /* Loop over characters */
+    
+               sprintf(buffer, "\"%s\" - %s", subject, author);
+               if (reference) {
+                   write_anchor(buffer, reference);
+                   free(reference);
+                   reference=0;
+               } else {
+                   HText_appendText(HT, buffer);
+               }
+               HText_appendParagraph(HT);
+               
+    
+/*     Change the title bar to indicate progress!
+*/
+               if (art%10 == 0) {
+                   sprintf(buffer, "Reading newsgroup %s,  Article %d (of %d-%d) ...",
+                           groupName, art, first, last);
+                   HTAnchor_setTitle(node_anchor, buffer);
+               }
+    
+           } /* If good response */
+       } /* Loop over article */           
+    } /* If read headers */
+    
+/*     Link to later articles
+*/
+    if (last_required<last) {
+       int after;                      /* End of article after */
+       after = last_required+CHUNK_SIZE;
+       if (after==last) sprintf(buffer, "news:%s", groupName); /* original group */
+       else sprintf(buffer, "news:%s/%d-%d", groupName, last_required+1, after);
+       if (TRACE) printf("    Block after is %s\n", buffer);
+       HText_appendText(HT,  "(");
+       HText_beginAnchor(HT, HTAnchor_findChildAndLink(
+               node_anchor, "", buffer, 0));
+       HText_appendText(HT,  "Later articles");
+       HText_endAnchor(HT);
+       HText_appendText(HT,  "...)\n");
+    }
+    
+/*     Set window title
+*/
+    sprintf(buffer, "Newsgroup %s,  Articles %d-%d",
+               groupName, first_required, last_required);
+    HTAnchor_setTitle(node_anchor, buffer);
+
+}
+
+
+/*             Load by name                                    HTLoadNews
+**             ============
+*/
+PUBLIC int HTLoadNews ARGS3(
+       CONST char *,arg,
+       HTParentAnchor *,anAnchor,
+       int,diag)
+{
+    char command[257];                 /* The whole command */
+    char groupName[GROUP_NAME_LENGTH]; /* Just the group name */
+    int status;                                /* tcp return */
+    int retries;                       /* A count of how hard we have tried */ 
+    BOOL group_wanted;                 /* Flag: group was asked for, not article */
+    BOOL list_wanted;                  /* Flag: group was asked for, not article */
+    int first, last;                   /* First and last articles asked for */
+
+    diagnostic = diag;                 /* set global flag */
+    
+    if (TRACE) printf("HTNews: Looking for %s\n", arg);
+    get_styles();
+    
+    if (!initialized) initialized = initialize();
+    if (!initialized) return -1;       /* FAIL */
+    
+    {
+        char * p1;
+
+/*     We will ask for the document, omitting the host name & anchor.
+**
+**     Syntax of address is
+**             xxx@yyy                 Article
+**             <xxx@yyy>               Same article
+**             xxxxx                   News group (no "@")
+*/        
+       group_wanted = (strchr(arg, '@')==0) && (strchr(arg, '*')==0);
+       list_wanted  = (strchr(arg, '@')==0) && (strchr(arg, '*')!=0);
+       
+       p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
+       if (list_wanted) {
+           strcpy(command, "LIST ");
+       } else if (group_wanted) {
+           char * slash = strchr(p1, '/');
+           strcpy(command, "GROUP ");
+           first = 0;
+           last = 0;
+           if (slash) {
+               *slash = 0;
+               strcpy(groupName, p1);
+               *slash = '/';
+               (void) sscanf(slash+1, "%d-%d", &first, &last);
+           } else {
+               strcpy(groupName, p1);
+           }
+           strcat(command, groupName);
+       } else {
+           strcpy(command, "ARTICLE ");
+           if (strchr(p1, '<')==0) strcat(command,"<");
+           strcat(command, p1);
+           if (strchr(p1, '>')==0) strcat(command,">");
+       }
+       free(p1);
+
+        strcat(command, "\r\n");               /* CR LF, as in rfc 977 */
+       
+    } /* scope of p1 */
+    
+    if (!*arg) return NO;                      /* Ignore if no name */
+
+    
+/*     Make a hypertext object with an anchor list.
+*/       
+    node_anchor = anAnchor;
+    HT = HText_new(anAnchor);
+    HText_beginAppend(HT);
+       
+/*     Now, let's get a stream setup up from the NewsHost:
+*/       
+    for(retries=0;retries<2; retries++){
+    
+        if (s<0) {
+           HTAnchor_setTitle(node_anchor, "Connecting to NewsHost ...");/* Tell user  */
+            NEWS_PROGRESS("Connecting to NewsHost ...");
+           s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+           status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address));
+           if (status<0){
+               char message[256];
+               NETCLOSE(s);
+               s = -1;
+               if (TRACE) printf("HTNews: Unable to connect to news host.\n");
+/*             if (retries<=1) continue;   WHY TRY AGAIN ?     */
+#ifdef NeXTStep
+               NXRunAlertPanel(NULL,
+                   "Could not access newshost %s.",
+                   NULL,NULL,NULL,
+                   NewsHost);
+#else
+               fprintf(stderr, "Could not access newshost %s\n",
+                   NewsHost);
+#endif
+               sprintf(message,
+"\nCould not access %s.\n\n (Check default WorldWideWeb NewsHost ?)\n",
+                   NewsHost);
+               HText_beginAppend(HT);
+               HText_appendText(HT, message);
+               HText_endAppend(HT);
+               return YES;
+           } else {
+               if (TRACE) printf("HTNews: Connected to news host %s.\n",
+                               NewsHost);
+               HTInitInput(s);         /* set up buffering */
+               if ((response(NULL) / 100) !=2) {
+                       NETCLOSE(s);
+                       s = -1;
+#ifdef NeXTStep
+                       NXRunAlertPanel("News access",
+                           "Could not retrieve information:\n   %s.",
+                           NULL,NULL,NULL,
+                           response_text);
+#endif
+                       HTAnchor_setTitle(node_anchor, "News host response");
+                       HText_beginAppend(HT);
+                       HText_appendText(HT,
+                            "Sorry, could not retrieve information: ");
+                       HText_appendText(HT, response_text);
+                       HText_endAppend(HT);
+                       return YES;
+               }
+           }
+       } /* If needed opening */
+       
+       HTAnchor_setTitle(node_anchor, arg);/* Tell user something's happening */
+       status = response(command);
+       if (status<0) break;
+       if ((status/ 100) !=2) {
+/*         NXRunAlertPanel("News access", response_text,
+               NULL,NULL,NULL);
+*/
+           HText_beginAppend(HT);
+           HText_appendText(HT, response_text);
+           HText_endAppend(HT);
+           NETCLOSE(s);
+           s = -1;
+/* return HT; -- no:the message might be "Timeout-disconnected" left over */
+           continue;   /*      Try again */
+       }
+  
+/*     Load a group, article, etc
+*/
+        HText_beginAppend(HT);
+       
+       if (list_wanted) read_list();
+       else if (group_wanted) read_group(groupName, first, last);
+        else read_article();
+
+       HText_endAppend(HT);
+       return YES;
+       
+    } /* Retry loop */
+    
+    HText_beginAppend(HT);
+    HText_appendText(HT, "Sorry, could not load requested news.\n");
+    HText_endAppend(HT);
+    
+/*    NXRunAlertPanel(NULL, "Sorry, could not load `%s'.",
+           NULL,NULL,NULL, arg);No -- message earlier wil have covered it */
+
+    return YES;
+}
+
diff --git a/Cl/WWWLibrary/HTNews.h b/Cl/WWWLibrary/HTNews.h
new file mode 100644 (file)
index 0000000..2883464
--- /dev/null
@@ -0,0 +1,17 @@
+/*                     NEWS ACCESS                             HTNews.h
+**                     ===========
+**
+** History:
+**     26 Sep 90       Written TBL
+**     29 Nov 91       Downgraded to C, for portable implementation.
+*/
+
+#ifndef HTNEWS_H
+#define HTNEWS_H
+
+#include "HTAnchor.h"
+extern int HTLoadNews PARAMS((const char *arg,
+       HTParentAnchor * anAnchor,
+       int diag));
+
+#endif /* HTNEWS_H */
diff --git a/Cl/WWWLibrary/HTParse.c b/Cl/WWWLibrary/HTParse.c
new file mode 100644 (file)
index 0000000..09b8aa0
--- /dev/null
@@ -0,0 +1,333 @@
+/*             Parse HyperText Document Address                HTParse.c
+**             ================================
+*/
+
+#include "HTUtils.h"
+#include "HTParse.h"
+#include "tcp.h"
+
+struct struct_parts {
+       char * access;
+       char * host;
+       char * absolute;
+       char * relative;
+/*     char * search;          no - treated as part of path */
+       char * anchor;
+};
+
+
+/*     Strip white space off a string
+**     ------------------------------
+**
+** On exit,
+**     Return value points to first non-white character, or to 0 if none.
+**     All trailing white space is OVERWRITTEN with zero.
+*/
+
+#ifdef __STDC__
+char * HTStrip(char * s)
+#else
+char * HTStrip(s)
+       char *s;
+#endif
+{
+#define SPACE(c) ((c==' ')||(c=='\t')||(c=='\n')) 
+    char * p=s;
+    for(p=s;*p;p++);                   /* Find end of string */
+    for(p--;p>=s;p--) {
+       if(SPACE(*p)) *p=0;     /* Zap trailing blanks */
+       else break;
+    }
+    while(SPACE(*s))s++;       /* Strip leading blanks */
+    return s;
+}
+
+
+/*     Scan a filename for its consituents
+**     -----------------------------------
+**
+** On entry,
+**     name    points to a document name which may be incomplete.
+** On exit,
+**      absolute or relative may be nonzero (but not both).
+**     host, anchor and access may be nonzero if they were specified.
+**     Any which are nonzero point to zero terminated strings.
+*/
+#ifdef __STDC__
+PRIVATE void scan(char * name, struct struct_parts *parts)
+#else
+PRIVATE void scan(name, parts)
+    char * name;
+    struct struct_parts *parts;
+#endif
+{
+    char * after_access;
+    char * p;
+    int length = strlen(name);
+    
+    parts->access = 0;
+    parts->host = 0;
+    parts->absolute = 0;
+    parts->relative = 0;
+    parts->anchor = 0;
+    
+    after_access = name;
+    for(p=name; *p; p++) {
+       if (*p==':') {
+               *p = 0;
+               parts->access = name;   /* Access name has been specified */
+               after_access = p+1;
+       }
+       if (*p=='/') break;
+       if (*p=='#') break;
+    }
+    
+    for(p=name+length-1; p>=name; p--) {
+       if (*p =='#') {
+           parts->anchor=p+1;
+           *p=0;                               /* terminate the rest */
+       }
+    }
+    p = after_access;
+    if (*p=='/'){
+       if (p[1]=='/') {
+           parts->host = p+2;          /* host has been specified      */
+           *p=0;                       /* Terminate access             */
+           p=strchr(parts->host,'/');  /* look for end of host name if any */
+           if(p) {
+               *p=0;                   /* Terminate host */
+               parts->absolute = p+1;          /* Root has been found */
+           }
+       } else {
+           parts->absolute = p+1;              /* Root found but no host */
+       }           
+    } else {
+        parts->relative = (*after_access) ? after_access : 0;  /* zero for "" */
+    }
+#ifdef NOT_DEFINED     /* search is just treated as part of path */
+    {
+        char *p = relative ? relative : absolute;
+       if (p) {
+           char * q = strchr(p, '?');  /* Any search string? */
+           if (q) {
+               *q = 0;                 /* If so, chop that off. */
+               parts->search = q+1;
+           }
+       }
+    }
+#endif
+} /*scan */    
+
+
+/*     Parse a Name relative to another name
+**     -------------------------------------
+**
+**     This returns those parts of a name which are given (and requested)
+**     substituting bits from the related name where necessary.
+**
+** On entry,
+**     aName           A filename given
+**      relatedName     A name relative to which aName is to be parsed
+**      wanted          A mask for the bits which are wanted.
+**
+** On exit,
+**     returns         A pointer to a malloc'd string which MUST BE FREED
+*/
+#ifdef __STDC__
+char * HTParse(const char * aName, const char * relatedName, int wanted)
+#else
+char * HTParse(aName, relatedName, wanted)
+    char * aName;
+    char * relatedName;
+    int wanted;
+#endif
+
+{
+    char * result = 0;
+    char * return_value = 0;
+    int len;
+    char * name = 0;
+    char * rel = 0;
+    char * p;
+    struct struct_parts given, related;
+    
+    /* Make working copies of input strings to cut up:
+    */
+    len = strlen(aName)+strlen(relatedName)+10;
+    result=(char *)malloc(len);                /* Lots of space: more than enough */
+    if (result == NULL) outofmem(__FILE__, "HTParse");
+    
+    StrAllocCopy(name, aName);
+    StrAllocCopy(rel, relatedName);
+    
+    scan(name, &given);
+    scan(rel,  &related); 
+    result[0]=0;               /* Clear string  */
+    if (wanted & PARSE_ACCESS)
+        if (given.access|| related.access) {
+           strcat(result, given.access ? given.access : related.access);
+           if(wanted & PARSE_PUNCTUATION) strcat(result, ":");
+       }
+       
+    if (given.access && related.access)        /* If different, inherit nothing. */
+        if (strcmp(given.access, related.access)!=0) {
+           related.host=0;
+           related.absolute=0;
+           related.relative=0;
+           related.anchor=0;
+       }
+       
+    if (wanted & PARSE_HOST)
+        if(given.host || related.host) {
+           if(wanted & PARSE_PUNCTUATION) strcat(result, "//");
+           strcat(result, given.host ? given.host : related.host);
+       }
+       
+    if (given.host && related.host)  /* If different hosts, inherit no path. */
+        if (strcmp(given.host, related.host)!=0) {
+           related.absolute=0;
+           related.relative=0;
+           related.anchor=0;
+       }
+       
+    if (wanted & PARSE_PATH) {
+        if(given.absolute) {                           /* All is given */
+           if(wanted & PARSE_PUNCTUATION) strcat(result, "/");
+           strcat(result, given.absolute);
+       } else if(related.absolute) {   /* Adopt path not name */
+           strcat(result, "/");
+           strcat(result, related.absolute);
+           if (given.relative) {
+               p = strchr(result, '?');        /* Search part? */
+               if (!p) p=result+strlen(result)-1;
+               for (; *p!='/'; p--);   /* last / */
+               p[1]=0;                                 /* Remove filename */
+               strcat(result, given.relative);         /* Add given one */
+               HTSimplify (result);
+           }
+       } else if(given.relative) {
+           strcat(result, given.relative);             /* what we've got */
+       } else if(related.relative) {
+           strcat(result, related.relative);
+       } else {  /* No inheritance */
+           strcat(result, "/");
+       }
+    }
+               
+    if (wanted & PARSE_ANCHOR)
+        if(given.anchor || related.anchor) {
+           if(wanted & PARSE_PUNCTUATION) strcat(result, "#");
+           strcat(result, given.anchor ? given.anchor : related.anchor);
+       }
+    free(rel);
+    free(name);
+    
+    StrAllocCopy(return_value, result);
+    free(result);
+    return return_value;               /* exactly the right length */
+}
+
+/*             Simplify a filename
+//             -------------------
+//
+// A unix-style file is allowed to contain the seqeunce xxx/../ which may be
+// replaced by "" , and the seqeunce "/./" which may be replaced by "/".
+// Simplification helps us recognize duplicate filenames.
+//
+//     Thus,   /etc/junk/../fred       becomes /etc/fred
+//             /etc/junk/./fred        becomes /etc/junk/fred
+*/
+#ifdef __STDC__
+void HTSimplify(char * filename)
+#else
+void HTSimplify(filename)
+    char * filename;
+#endif
+
+{
+    char * p;
+    char * q;
+    for(p=filename+2; *p; p++) {
+        if (*p=='/') {
+           if ((p[1]=='.') && (p[2]=='.') && (p[3]=='/' || !p[3] )) {
+               for (q=p-1; (q>filename) && (*q!='/'); q--); /* prev slash */
+               if (*q=='/') {
+                   strcpy(q, p+3);     /* Remove  /xxx/..      */
+                   if (!*filename) strcpy(filename, "/");
+                   p = q-1;            /* Start again with prev slash  */
+               } else {                        /*   xxx/..     error?  */
+                   strcpy(filename, p[3] ? p+4 : p+3); /* rm  xxx/../  */
+                   p = filename;               /* Start again */
+               }
+           } else if ((p[1]=='.') && (p[2]=='/' || !p[2])) {
+               strcpy(p, p+2);                 /* Remove a slash and a dot */
+           }
+       }
+    }
+}
+
+
+/*             Make Relative Name
+**             ------------------
+**
+** This function creates and returns a string which gives an expression of
+** one address as related to another. Where there is no relation, an absolute
+** address is retured.
+**
+**  On entry,
+**     Both names must be absolute, fully qualified names of nodes
+**     (no anchor bits)
+**
+**  On exit,
+**     The return result points to a newly allocated name which, if
+**     parsed by HTParse relative to relatedName, will yield aName.
+**     The caller is responsible for freeing the resulting name later.
+**
+*/
+#ifdef __STDC__
+char * HTRelative(const char * aName, const char *relatedName)
+#else
+char * HTRelative(aName, relatedName)
+   char * aName;
+   char * relatedName;
+#endif
+{
+    char * result = 0;
+    CONST char *p = aName;
+    CONST char *q = relatedName;
+    CONST char * after_access = 0;
+    CONST char * path = 0;
+    CONST char * last_slash = 0;
+    int slashes = 0;
+    
+    for(;*p; p++, q++) {       /* Find extent of match */
+       if (*p!=*q) break;
+       if (*p==':') after_access = p+1;
+       if (*p=='/') {
+           last_slash = p;
+           slashes++;
+           if (slashes==3) path=p;
+       }
+    }
+    
+    /* q, p point to the first non-matching character or zero */
+    
+    if (!after_access) {                       /* Different access */
+        StrAllocCopy(result, aName);
+    } else if (slashes<3){                     /* Different nodes */
+       StrAllocCopy(result, after_access);
+    } else if (slashes==3){                    /* Same node, different path */
+        StrAllocCopy(result, path);
+    } else {                                   /* Some path in common */
+        int levels= 0;
+        for(; *q && (*q!='#'); q++)  if (*q=='/') levels++;
+       result = (char *)malloc(3*levels + strlen(last_slash) + 1);
+      if (result == NULL) outofmem(__FILE__, "HTRelative");
+       result[0]=0;
+       for(;levels; levels--)strcat(result, "../");
+       strcat(result, last_slash+1);
+    }
+    if (TRACE) printf("HT: `%s' expressed relative to\n    `%s' is\n   `%s'.",
+               aName, relatedName, result);
+    return result;
+}
diff --git a/Cl/WWWLibrary/HTParse.h b/Cl/WWWLibrary/HTParse.h
new file mode 100644 (file)
index 0000000..0bd6d92
--- /dev/null
@@ -0,0 +1,87 @@
+/*             Parse HyperText Document Address                HTParse.h
+**             ================================
+*/
+/*     These are flag bits which may be ORed together to form a number to give
+**     the 'wanted' argument to HTParse.
+*/
+#define PARSE_ACCESS           16
+#define PARSE_HOST              8
+#define PARSE_PATH              4
+#define PARSE_ANCHOR            2
+#define PARSE_PUNCTUATION       1
+#define PARSE_ALL              31
+
+/*     Parse a Name relative to another name
+**     -------------------------------------
+**
+**     This returns those parts of a name which are given (and requested)
+**     substituting bits from the related name where necessary.
+**
+** On entry,
+**     aName           A filename given
+**     relatedName     A name relative to ehich aName is to be parsed
+**      wanted          A mask for the bits which are wanted.
+**
+** On exit,
+**     returns         A pointer to a malloc'd string which MUST BE FREED
+*/
+#ifdef __STDC__
+extern char * HTParse(const char * aName, const char * relatedName, int wanted);
+#else
+extern char * HTParse();
+#endif
+
+/*     Strip white space off a string
+**     ------------------------------
+**
+** On exit,
+**     Return value points to first non-white character, or to 0 if none.
+**     All trailing white space is OVERWRITTEN with zero.
+*/
+#ifdef __STDC__
+extern char * HTStrip(char * s);
+#else
+extern char * HTStrip();
+#endif
+
+/*             Simplify a filename
+**             -------------------
+**
+**     A unix-style file is allowed to contain the seqeunce xxx/../ which
+**     may be replaced by "" , and the seqeunce "/./" which may be replaced
+**     by "/".
+**     Simplification helps us recognize duplicate filenames. It doesn't deal
+**     with soft links, though.
+**     The new (shorter) filename overwrites the old.
+**
+**     Thus,   /etc/junk/../fred       becomes /etc/fred
+**             /etc/junk/./fred        becomes /etc/junk/fred
+*/
+#ifdef __STDC__
+extern void HTSimplify(char * filename);
+#else
+extern void HTSimplify();
+#endif
+
+/*             Make Relative Name
+**             ------------------
+**
+**     This function creates and returns a string which gives an expression of
+**     one address as related to another. Where there is no relation, an
+**     absolute address is retured.
+**
+**  On entry,
+**     Both names must be absolute, fully qualified names of nodes
+**     (no anchor bits)
+**
+**  On exit,
+**     The return result points to a newly allocated name which, if parsed
+**     by HTParse relative to relatedName, will yield aName. The caller is
+**     responsible for freeing the resulting name later.
+**
+*/
+#ifdef __STDC__
+extern char * HTRelative(const char * aName, const char *relatedName);
+#else
+extern char * HTRelative();
+#endif
diff --git a/Cl/WWWLibrary/HTString.c b/Cl/WWWLibrary/HTString.c
new file mode 100644 (file)
index 0000000..e997423
--- /dev/null
@@ -0,0 +1,82 @@
+/*             Case-independent string comparison              HTString.c
+**
+**     Original version came with listserv implementation.
+**     Version TBL Oct 91 replaces one which modified the strings.
+**     02-Dec-91 (JFG) Added stralloccopy and stralloccat
+**     23 Jan 92 (TBL) Changed strallocc* to 8 char HTSAC* for VM and suchlike
+*/
+#include <ctype.h>
+#include "HTUtils.h"
+#include "tcp.h"
+
+#ifndef VM             /* VM has these already it seems */
+       
+/*     Strings of any length
+**     ---------------------
+*/
+PUBLIC int strcasecomp ARGS2 (CONST char*,a, CONST char *,b)
+{
+       CONST char *p =a;
+       CONST char *q =b;
+       for(p=a, q=b; *p && *q; p++, q++) {
+           int diff = TOLOWER(*p) - TOLOWER(*q);
+           if (diff) return diff;
+       }
+       if (*p) return 1;       /* p was longer than q */
+       if (*q) return -1;      /* p was shorter than q */
+       return 0;               /* Exact match */
+}
+
+
+/*     With count limit
+**     ----------------
+*/
+PUBLIC int strncasecomp ARGS3(CONST char*,a, CONST char *,b, int,n)
+{
+       CONST char *p =a;
+       CONST char *q =b;
+       
+       for(p=a, q=b;; p++, q++) {
+           int diff;
+           if (p == a+n) return 0;     /*   Match up to n characters */
+           if (!(*p && *q)) return *p - *q;
+           diff = TOLOWER(*p) - TOLOWER(*q);
+           if (diff) return diff;
+       }
+       /*NOTREACHED*/
+}
+#endif
+
+/*     Allocate a new copy of a string, and returns it
+*/
+PUBLIC char * HTSACopy
+  ARGS2 (char **,dest, CONST char *,src)
+{
+  if (*dest) free(*dest);
+  if (! src)
+    *dest = NULL;
+  else {
+    *dest = (char *) malloc (strlen(src) + 1);
+    if (*dest == NULL) outofmem(__FILE__, "HTSACopy");
+    strcpy (*dest, src);
+  }
+  return *dest;
+}
+
+PUBLIC char * HTSACat
+  ARGS2 (char **,dest, CONST char *,src)
+{
+  if (src && *src) {
+    if (*dest) {
+      int length = strlen (*dest);
+      *dest = (char *) realloc (*dest, length + strlen(src) + 1);
+      if (*dest == NULL) outofmem(__FILE__, "HTSACat");
+      strcpy (*dest + length, src);
+    } else {
+      *dest = (char *) malloc (strlen(src) + 1);
+      if (*dest == NULL) outofmem(__FILE__, "HTSACat");
+      strcpy (*dest, src);
+    }
+  }
+  return *dest;
+}
diff --git a/Cl/WWWLibrary/HTString.h b/Cl/WWWLibrary/HTString.h
new file mode 100644 (file)
index 0000000..1b3c6f2
--- /dev/null
@@ -0,0 +1,19 @@
+/*             Case-independent string comparison              HTString.h
+**             and allocations with copies
+*/
+#ifndef HTSTRING_H
+#define HTSTRING_H
+
+#include "HTUtils.h"
+
+extern int strcasecomp  PARAMS((CONST char *a, CONST char *b));
+extern int strncasecomp PARAMS((CONST char *a, CONST char *b, int n));
+
+extern char * HTSACopy PARAMS ((char **dest, CONST char *src));
+extern char * HTSACat  PARAMS ((char **dest, CONST char *src));
+
+/* Enable the old macro-like calling methods */
+#define StrAllocCopy(dest, src) HTSACopy (&(dest), src)
+#define StrAllocCat(dest, src)  HTSACat  (&(dest), src)
+
+#endif
diff --git a/Cl/WWWLibrary/HTStyle.c b/Cl/WWWLibrary/HTStyle.c
new file mode 100644 (file)
index 0000000..b15801c
--- /dev/null
@@ -0,0 +1,363 @@
+/*     Style Implementation for Hypertext                      HTStyle.c
+**     ==================================
+**
+**     Styles allow the translation between a logical property
+**     of a piece of text and its physical representation.
+**
+**     A StyleSheet is a collection of styles, defining the
+**     translation necessary to
+**     represent a document. It is a linked list of styles.
+*/
+#include "HTStyle.h"
+#include "HTUtils.h"
+
+/*     Create a new style
+*/
+PUBLIC HTStyle* HTStyleNew NOARGS
+{
+    HTStyle * self = (HTStyle *)malloc(sizeof(*self));
+    memset(self, 0, sizeof(*self));
+    self->font = HT_FONT;
+    self->color = HT_BLACK;
+    return self;
+}
+
+/*     Create a new style with a name
+*/
+PUBLIC HTStyle* HTStyleNewNamed ARGS1 (CONST char *,name)
+{
+    HTStyle * self = HTStyleNew();
+    StrAllocCopy(self->name, name);
+    return self;
+}
+
+
+/*     Free a style
+*/
+PUBLIC HTStyle * HTStyleFree ARGS1 (HTStyle *,self)
+{
+    if (self->name) free(self->name);
+    if (self->SGMLTag) free(self->SGMLTag);
+    free(self);
+    return 0;
+}
+
+
+#ifdef SUPPRESS  /* Only on the NeXT */
+/*     Read a style from a stream      (without its name)
+**     --------------------------
+**
+**     Reads a style with paragraph information from a stream.
+**     The style name is not read or written by these routines.
+*/
+#define NONE_STRING "(None)"
+#define HTStream NXStream
+
+HTStyle * HTStyleRead (HTStyle * style, HTStream * stream)
+{
+    char myTag[STYLE_NAME_LENGTH];
+    char fontName[STYLE_NAME_LENGTH];
+    NXTextStyle *p;
+    int        tab;
+    int gotpara;               /* flag: have we got a paragraph definition? */
+       
+    NXScanf(stream, "%s%s%f%d",
+       myTag,
+       fontName,
+       &style->fontSize,
+       &gotpara);
+    if (gotpara) {
+       if (!style->paragraph) {
+           style->paragraph = malloc(sizeof(*(style->paragraph)));
+           style->paragraph->tabs = 0;
+       }
+       p = style->paragraph;
+       NXScanf(stream, "%f%f%f%f%hd%f%f%hd",
+           &p->indent1st,
+           &p->indent2nd,
+           &p->lineHt,
+           &p->descentLine,
+           &p->alignment,
+           &style->spaceBefore,
+           &style->spaceAfter,
+           &p->numTabs);
+       if (p->tabs) free(p->tabs);
+       p->tabs = malloc(p->numTabs * sizeof(p->tabs[0]));
+       for (tab=0; tab < p->numTabs; tab++) {
+           NXScanf(stream, "%hd%f",
+                   &p->tabs[tab].kind,
+                   &p->tabs[tab].x);
+       }
+    } else { /* No paragraph */
+        if (style->paragraph) {
+           free(style->paragraph);
+           style->paragraph = 0;
+       }
+    } /* if no paragraph */
+    StrAllocCopy(style->SGMLTag, myTag);
+    if (strcmp(fontName, NONE_STRING)==0)
+        style->font = 0;
+    else
+        style->font = [Font newFont:fontName size:style->fontSize];
+    return 0;
+
+}
+
+
+/*     Write a style to a stream in a compatible way
+*/
+HTStyle * HTStyleWrite (HTStyle * style, NXStream * stream)
+{
+    int tab;
+    NXTextStyle *p = style->paragraph;
+    NXPrintf(stream, "%s %s %f %d\n",
+       style->SGMLTag,
+       style->font ? [style->font name] : NONE_STRING,
+       style->fontSize,
+       p!=0);
+
+    if (p) {
+       NXPrintf(stream, "\t%f %f %f %f %d %f %f\t%d\n",
+           p->indent1st,
+           p->indent2nd,
+           p->lineHt,
+           p->descentLine,
+           p->alignment,
+           style->spaceBefore,
+           style->spaceAfter,
+           p->numTabs);
+           
+       for (tab=0; tab < p->numTabs; tab++)
+           NXPrintf(stream, "\t%d %f\n",
+                   p->tabs[tab].kind,
+                   p->tabs[tab].x);
+       }
+    return style;
+}
+
+
+/*     Write a style to stdout for diagnostics
+*/
+HTStyle * HTStyleDump (HTStyle * style)
+{
+    int tab;
+    NXTextStyle *p = style->paragraph;
+    printf("Style %d `%s' SGML:%s. Font %s %.1f point.\n",
+       style,
+       style->name,
+       style->SGMLTag,
+       [style->font name],
+       style->fontSize);
+    if (p) {
+        printf(
+       "\tIndents: first=%.0f others=%.0f, Height=%.1f Desc=%.1f\n"
+       "\tAlign=%d, %d tabs. (%.0f before, %.0f after)\n",
+           p->indent1st,
+           p->indent2nd,
+           p->lineHt,
+           p->descentLine,
+           p->alignment,
+           p->numTabs,
+           style->spaceBefore,
+           style->spaceAfter);
+           
+       for (tab=0; tab < p->numTabs; tab++) {
+           printf("\t\tTab kind=%d at %.0f\n",
+                   p->tabs[tab].kind,
+                   p->tabs[tab].x);
+       }
+       printf("\n");
+    } /* if paragraph */
+    return style;
+}
+#endif
+
+
+/*                     StyleSheet Functions
+**                     ====================
+*/
+
+/*     Searching for styles:
+*/
+HTStyle * HTStyleNamed ARGS2 (HTStyleSheet *,self, CONST char *,name)
+{
+    HTStyle * scan;
+    for (scan=self->styles; scan; scan=scan->next)
+        if (0==strcmp(scan->name, name)) return scan;
+    if (TRACE) printf("StyleSheet: No style named `%s'\n", name);
+    return 0;
+}
+
+#ifdef NEXT_SUPRESS            /* Not in general common code */
+
+HTStyle * HTStyleMatching (HTStyleSheet * self, HTStyle *style)
+{
+    HTStyle * scan;
+    for (scan=self->styles; scan; scan=scan->next)
+        if (scan->paragraph == para) return scan;
+    return 0;
+}
+
+/*     Find the style which best fits a given run
+**     ------------------------------------------
+**
+**     This heuristic is used for guessing the style for a run of
+**     text which has been pasted in. In order, we try:
+**
+**     A style whose paragraph structure is actually used by the run.
+**     A style matching in font
+**     A style matching in paragraph style exactly
+**     A style matching in paragraph to a degree
+*/
+
+HTStyle * HTStyleForRun (HTStyleSheet *self, NXRun *run)
+{
+    HTStyle * scan;
+    HTStyle * best = 0;
+    int        bestMatch = 0;
+    NXTextStyle * rp = run->paraStyle;
+    for (scan=self->styles; scan; scan=scan->next)
+        if (scan->paragraph == run->paraStyle) return scan;    /* Exact */
+
+    for (scan=self->styles; scan; scan=scan->next){
+       NXTextStyle * sp = scan->paragraph;
+       if (sp) {
+           int match = 0;
+           if (sp->indent1st ==        rp->indent1st)  match = match+1;
+           if (sp->indent2nd ==        rp->indent2nd)  match = match+2;
+           if (sp->lineHt ==           rp->lineHt)     match = match+1;
+           if (sp->numTabs ==          rp->numTabs)    match = match+1;
+           if (sp->alignment ==        rp->alignment)  match = match+3;
+           if (scan->font ==           run->font)      match = match+10;
+           if (match>bestMatch) {
+                   best=scan;
+                   bestMatch=match;
+           }
+       }
+    }
+    if (TRACE) printf("HTStyleForRun: Best match for style is %d out of 18\n",
+                        bestMatch);
+    return best;
+}
+#endif
+
+
+/*     Add a style to a sheet
+**     ----------------------
+*/
+HTStyleSheet * HTStyleSheetAddStyle ARGS2
+  (HTStyleSheet *,self, HTStyle *,style)
+{
+    style->next = 0;           /* The style will go on the end */
+    if (!self->styles) {
+       self->styles = style;
+    } else {
+       HTStyle * scan;
+        for(scan=self->styles; scan->next; scan=scan->next); /* Find end */
+       scan->next=style;
+    }
+    return self;
+}
+
+
+/*     Remove the given object from a style sheet if it exists
+*/
+HTStyleSheet * HTStyleSheetRemoveStyle ARGS2
+  (HTStyleSheet *,self, HTStyle *,style)
+{
+    if (self->styles = style) {
+       self->styles = style->next;
+       return self;
+    } else {
+       HTStyle * scan;
+       for(scan = self->styles; scan; scan = scan->next) {
+           if (scan->next = style) {
+               scan->next = style->next;
+               return self;
+           }
+       }
+    }
+    return 0;
+}
+
+/*     Create new style sheet
+*/
+
+HTStyleSheet * HTStyleSheetNew NOARGS
+{
+    HTStyleSheet * self = (HTStyleSheet *)malloc(sizeof(*self));
+
+    memset((void*)self, 0, sizeof(*self));     /* ANSI */
+/* Harbison c ref man says (char*)self
+   but k&r ansii and abc books and Think_C say (void*) */
+    
+/*    bzero(self, sizeof(*self)); */           /* BSD */
+    return self;
+}
+
+
+/*     Free off a style sheet pointer
+*/
+HTStyleSheet * HTStyleSheetFree ARGS1 (HTStyleSheet *,self)
+{
+    HTStyle * style;
+    while((style=self->styles)!=0) {
+        self->styles = style->next;
+       HTStyleFree(style);
+    }
+    free(self);
+    return 0;
+}
+
+
+/*     Read a stylesheet from a typed stream
+**     -------------------------------------
+**
+**     Reads a style sheet from a stream.  If new styles have the same names
+**     as existing styles, they replace the old ones without changing the ids.
+*/
+
+#ifdef NEXT_SUPRESS  /* Only on the NeXT */
+HTStyleSheet * HTStyleSheetRead(HTStyleSheet * self, NXStream * stream)
+{
+    int numStyles;
+    int i;
+    HTStyle * style;
+    char styleName[80];
+    NXScanf(stream, " %d ", &numStyles);
+    if (TRACE) printf("Stylesheet: Reading %d styles\n", numStyles);
+    for (i=0; i<numStyles; i++) {
+        NXScanf(stream, "%s", styleName);
+        style = HTStyleNamed(self, styleName);
+       if (!style) {
+           style = HTStyleNewNamed(styleName);
+           (void) HTStyleSheetAddStyle(self, style);
+       }
+       (void) HTStyleRead(style, stream);
+       if (TRACE) HTStyleDump(style);
+    }
+    return self;
+}
+
+/*     Write a stylesheet to a typed stream
+**     ------------------------------------
+**
+**     Writes a style sheet to a stream.
+*/
+
+HTStyleSheet * HTStyleSheetWrite(HTStyleSheet * self, NXStream * stream)
+{
+    int numStyles = 0;
+    HTStyle * style;
+    
+    for(style=self->styles; style; style=style->next) numStyles++;
+    NXPrintf(stream, "%d\n", numStyles);
+    
+    if (TRACE) printf("StyleSheet: Writing %d styles\n", numStyles);
+    for (style=self->styles; style; style=style->next) {
+        NXPrintf(stream, "%s ", style->name);
+       (void) HTStyleWrite(style, stream);
+    }
+    return self;
+}
+#endif
diff --git a/Cl/WWWLibrary/HTStyle.h b/Cl/WWWLibrary/HTStyle.h
new file mode 100644 (file)
index 0000000..cfcc788
--- /dev/null
@@ -0,0 +1,154 @@
+/*     Style Definition for Hypertext                          HTStyle.h
+**     ==============================
+**
+**     Styles allow the translation between a logical property of a piece of
+**     text and its physical representation.
+**
+**     A StyleSheet is a collection of styles, defining the
+**     translation necessary to represent a document.
+**     It is a linked list of styles.
+*/
+#ifndef HTStyle_H
+#define HTStyle_H
+
+#include "HTUtils.h"
+#include "HTAnchor.h"
+#include "HTFont.h"
+
+#ifdef SHORT_NAMES
+#define HTStyleNew                     HTStNew
+#define HTStyleFree                    HTStFree
+#define HTStyleRead                    HTStRead
+#define HTStyleWrite                   HTStWrite
+#define HTStyleSheetNew                        HTStShNe
+#define HTStyleSheetFree               HTStShFr
+#define HTStyleNamed                   HTStName
+#define HTStyleForParagraph            HTStFoPa
+#define HTStyleMatching                        HTStMatc
+#define HTStyleForRun                  HTStFoRu
+#define HTStyleSheetAddStyle           HTStShAd
+#define HTStyleSheetRemoveStyle                HTStShRm
+#define HTStyleSheetRead               HTStShRe
+#define HTStyleSheetWrite              HTStShWr
+#endif
+
+#ifdef NeXT_suppressed
+#include <appkit/appkit.h>
+typedef NXCoord HTCoord;
+#define HTParagraphStyle NXTextStyle
+#define HTCoord NXCoord
+typedef struct _color {
+       float   grey;
+       int     RGBColor;
+} HTColor;
+#else
+
+typedef float HTCoord;
+
+typedef struct _HTParagraphStyle {
+    HTCoord    left_indent;            /* @@@@ junk! etc etc*/
+} HTParagraphStyle;
+
+typedef int HTColor;           /* Sorry about the US spelling! */
+
+#endif
+
+
+
+#define STYLE_NAME_LENGTH      80      /* @@@@@@@@@@@ */
+        
+typedef struct {
+    short              kind;           /* only NX_LEFTTAB implemented*/
+    HTCoord            position;       /* x coordinate for stop */
+} HTTabStop;
+
+
+/*     The Style Structure
+**     -------------------
+*/
+
+typedef struct _HTStyle {
+
+/*     Style management information
+*/
+    struct _HTStyle    *next;          /* Link for putting into stylesheet */
+    char *             name;           /* Style name */
+    char *             SGMLTag;        /* Tag name to start */
+
+
+/*     Character attributes    (a la NXRun)
+*/
+    HTFont             font;           /* Font id */
+    HTCoord            fontSize;       /* The size of font, not independent */
+    HTColor            color;  /* text gray of current run */
+    int                superscript;    /* superscript (-sub) in points */
+
+    HTAnchor           *anchor;        /* Anchor id if any, else zero */
+
+/*     Paragraph Attribtes     (a la NXTextStyle)
+*/
+    HTCoord            indent1st;      /* how far first line in paragraph is
+                                * indented */
+    HTCoord            leftIndent;     /* how far second line is indented */
+    HTCoord            rightIndent;    /* (Missing from NeXT version */
+    short              alignment;      /* quad justification */
+    HTCoord            lineHt;         /* line height */
+    HTCoord            descentLine;    /* descender bottom from baseline */
+    HTTabStop          *tabs;          /* array of tab stops, 0 terminated */
+
+    BOOL               wordWrap;       /* Yes means wrap at space not char */
+    BOOL               freeFormat;     /* Yes means \n is just white space */
+    HTCoord            spaceBefore;    /* Omissions from NXTextStyle */
+    HTCoord            spaceAfter;
+    int                        paraFlags;      /* Paragraph flags, bits as follows: */
+
+#define PARA_KEEP      1       /* Do not break page within this paragraph */
+#define        PARA_WITH_NEXT  2       /* Do not break page after this paragraph */
+
+#define HT_JUSTIFY 0           /* For alignment */
+#define HT_LEFT        1
+#define HT_RIGHT 2
+#define HT_CENTER 3
+  
+} HTStyle;
+
+
+/*     Style functions:
+*/
+extern HTStyle * HTStyleNew NOPARAMS;
+extern HTStyle* HTStyleNewNamed PARAMS ((CONST char * name));
+extern HTStyle * HTStyleFree PARAMS((HTStyle * self));
+#ifdef SUPRESS
+extern HTStyle * HTStyleRead PARAMS((HTStyle * self, HTStream * stream));
+extern HTStyle * HTStyleWrite PARAMS((HTStyle * self, HTStream * stream));
+#endif
+/*             Style Sheet
+**             -----------
+*/
+typedef struct _HTStyleSheet {
+       char *          name;
+       HTStyle *       styles;
+} HTStyleSheet;
+
+
+/*     Stylesheet functions:
+*/
+extern HTStyleSheet * HTStyleSheetNew NOPARAMS;
+extern HTStyleSheet * HTStyleSheetFree PARAMS((HTStyleSheet * self));
+extern HTStyle * HTStyleNamed PARAMS((HTStyleSheet * self, CONST char * name));
+extern HTStyle * HTStyleForParagraph PARAMS((HTStyleSheet * self,
+       HTParagraphStyle * paraStyle));
+extern HTStyle * HTStyleMatching PARAMS((HTStyleSheet *self, HTStyle * style));
+/* extern HTStyle * HTStyleForRun PARAMS((HTStyleSheet *self, NXRun * run)); */
+extern HTStyleSheet * HTStyleSheetAddStyle PARAMS((HTStyleSheet * self,
+       HTStyle * style));
+extern HTStyleSheet * HTStyleSheetRemoveStyle PARAMS((HTStyleSheet * self,
+       HTStyle * style));
+#ifdef SUPPRESS
+extern HTStyleSheet * HTStyleSheetRead PARAMS((HTStyleSheet * self,
+                                               HTStream * stream));
+extern HTStyleSheet * HTStyleSheetWrite PARAMS((HTStyleSheet * self,
+                                               HTStream * stream));
+#endif
+#define CLEAR_POINTER ((void *)-1)     /* Pointer value means "clear me" */
+#endif /* HTStyle_H */
diff --git a/Cl/WWWLibrary/HTTCP.c b/Cl/WWWLibrary/HTTCP.c
new file mode 100644 (file)
index 0000000..afa1727
--- /dev/null
@@ -0,0 +1,297 @@
+/*                     Generic Communication Code              HTTCP.c
+**                     ==========================
+**
+**     This code is in common between client and server sides.
+**
+**     16 Jan 92       Fix strtol() undefined on CMU Mach. - TBL
+*/
+
+
+#include "HTUtils.h"
+#include "tcp.h"               /* Defines SHORT_NAMES if necessary */
+#ifdef SHORT_NAMES
+#define HTInetStatus           HTInStat
+#define HTInetString           HTInStri
+#define HTParseInet            HTPaInet
+#endif
+
+/*     Module-Wide variables
+*/
+
+PRIVATE char *hostname=0;              /* The name of this host */
+
+
+/*     PUBLIC VARIABLES
+*/
+
+/* PUBLIC struct sockaddr_in HTHostAddress; */ /* The internet address of the host */
+                                       /* Valid after call to HTHostName() */
+
+/*     Encode INET status (as in sys/errno.h)                    inet_status()
+**     ------------------
+**
+** On entry,
+**     where           gives a description of what caused the error
+**     global errno    gives the error number in the unix way.
+**
+** On return,
+**     returns         a negative status in the unix way.
+*/
+#ifndef PCNFS
+#ifdef vms
+extern int uerrno;     /* Deposit of error info (as perr errno.h) */
+extern int vmserrno;   /* Deposit of VMS error info */
+extern volatile noshare int errno;  /* noshare to avoid PSECT conflict */
+#else
+#ifndef errno
+extern int errno;
+#endif
+#endif
+
+#ifndef VM
+#ifndef vms
+#ifndef NeXT
+#ifndef THINK_C
+/* Toni */
+/* extern char *sys_errlist[]; */
+extern int sys_nerr;
+#endif  /* think c */
+#endif /* NeXT */
+#endif  /* vms */
+#endif /* VM */
+#endif /* PCNFS */
+
+/*     Report Internet Error
+**     ---------------------
+*/
+#ifdef __STDC__
+PUBLIC int HTInetStatus(char *where)
+#else
+PUBLIC int HTInetStatus(where)
+    char    *where;
+#endif
+{
+    CTRACE(tfp, "TCP: Error %d in `errno' after call to %s() failed.\n\t%s\n",
+           errno,  where,
+#ifdef VM
+           "(Error number not translated)");   /* What Is the VM equiv? */
+#define ER_NO_TRANS_DONE
+#endif
+#ifdef vms
+           "(Error number not translated)");
+#define ER_NO_TRANS_DONE
+#endif
+#ifdef NeXT
+           strerror(errno));
+#define ER_NO_TRANS_DONE
+#endif
+#ifdef THINK_C
+           strerror(errno));
+#define ER_NO_TRANS_DONE
+#endif
+
+#ifndef ER_NO_TRANS_DONE
+           errno < sys_nerr ? sys_errlist[errno] : "Unknown error" );
+#endif
+
+
+#ifdef vms
+    CTRACE(tfp, "         Unix error number (uerrno) = %ld dec\n", uerrno);
+    CTRACE(tfp, "         VMS error (vmserrno)       = %lx hex\n", vmserrno);
+#endif
+    return -errno;
+}
+
+
+/*     Parse a cardinal value                                 parse_cardinal()
+**     ----------------------
+**
+** On entry,
+**     *pp         points to first character to be interpreted, terminated by
+**                 non 0:9 character.
+**     *pstatus    points to status already valid
+**     maxvalue    gives the largest allowable value.
+**
+** On exit,
+**     *pp         points to first unread character
+**     *pstatus    points to status updated iff bad
+*/
+#ifdef __STDC__
+PUBLIC unsigned int HTCardinal(int *pstatus,
+       char            **pp,
+       unsigned int    max_value)
+#else
+PUBLIC unsigned int HTCardinal(pstatus, pp, max_value)
+   int                 *pstatus;
+   char                        **pp;
+   unsigned int                max_value;
+#endif
+{
+    int   n;
+    if ( (**pp<'0') || (**pp>'9')) {       /* Null string is error */
+       *pstatus = -3;  /* No number where one expeceted */
+       return 0;
+    }
+
+    n=0;
+    while ((**pp>='0') && (**pp<='9')) n = n*10 + *((*pp)++) - '0';
+
+    if (n>max_value) {
+       *pstatus = -4;  /* Cardinal outside range */
+       return 0;
+    }
+
+    return n;
+}
+
+
+/*     Produce a string for an inernet address
+**     ---------------------------------------
+**
+** On exit,
+**     returns a pointer to a static string which must be copied if
+**             it is to be kept.
+*/
+#ifdef __STDC__
+PUBLIC const char * HTInetString(struct sockaddr_in* sin)
+#else
+PUBLIC char * HTInetString(sin)
+    struct sockaddr_in *sin;
+#endif
+{
+    static char string[16];
+    sprintf(string, "%d.%d.%d.%d",
+           (int)*((unsigned char *)(&sin->sin_addr)+0),
+           (int)*((unsigned char *)(&sin->sin_addr)+1),
+           (int)*((unsigned char *)(&sin->sin_addr)+2),
+           (int)*((unsigned char *)(&sin->sin_addr)+3));
+    return string;
+}
+
+
+/*     Parse an internet node address and port
+**     ---------------------------------------
+**
+** On entry,
+**     str     points to a string with a node name or number,
+**             with optional trailing colon and port number.
+**     sin     points to the binary internet address field.
+**
+** On exit,
+**     *sin    is filled in. If no port is specified in str, that
+**             field is left unchanged in *sin.
+*/
+#ifdef __STDC__
+PUBLIC int HTParseInet(struct sockaddr_in* sin, const char *str)
+#else
+PUBLIC int HTParseInet(sin, str)
+    struct sockaddr_in *sin;
+    char *str;
+#endif
+{
+    char *port;
+    char host[256];
+    struct hostent  *phost;    /* Pointer to host - See netdb.h */
+    strcpy(host, str);         /* Take a copy we can mutilate */
+
+
+
+/*     Parse port number if present
+*/    
+    if (port=strchr(host, ':')) {
+       *port++ = 0;            /* Chop off port */
+        if (port[0]>='0' && port[0]<='9') {
+#ifdef unix
+           sin->sin_port = htons(atol(port));
+#else
+           sin->sin_port = htons(strtol(port, (char**)0 , 10));
+#endif
+       } else {
+#ifdef SUPPRESS                /* 1. crashes!?!.  2. Not recommended */
+           struct servent * serv = getservbyname(port, (char*)0);
+           if (serv) sin->sin_port = serv->s_port;
+           else if (TRACE) printf("TCP: Unknown service %s\n", port);
+#endif
+       }
+    }
+
+/*     Parse host number if present
+*/  
+    if (*host>='0' && *host<='9') {   /* Numeric node address: */
+       sin->sin_addr.s_addr = inet_addr(host); /* See arpa/inet.h */
+
+    } else {               /* Alphanumeric node name: */
+       printf("HTTCP: Calling gethostbyname2(\"%s\",AF_INET)\n", host);
+       phost=gethostbyname2(host,AF_INET);     /* See netdb.h */
+       if (!phost) {
+           printf(
+                   "HTTPAccess: Can't find internet node name `%s', h_errno: %d.\n",host,h_errno);
+           return -1;  /* Fail? */
+       }
+       memcpy(&sin->sin_addr, phost->h_addr, phost->h_length);
+    }
+
+    if (TRACE) printf( 
+       "TCP: Parsed address as port %d, IP address %d.%d.%d.%d\n",
+               (unsigned int)ntohs(sin->sin_port),
+               (int)*((unsigned char *)(&sin->sin_addr)+0),
+               (int)*((unsigned char *)(&sin->sin_addr)+1),
+               (int)*((unsigned char *)(&sin->sin_addr)+2),
+               (int)*((unsigned char *)(&sin->sin_addr)+3));
+
+    return 0;  /* OK */
+}
+
+
+
+/*     Derive the name of the host on which we are
+**     -------------------------------------------
+**
+*/
+#ifdef __STDC__
+PRIVATE void get_host_details(void)
+#else
+PRIVATE void get_host_details()
+#endif
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64              /* Arbitrary limit */
+#endif
+
+{
+    char name[MAXHOSTNAMELEN+1];       /* The name of this host */
+#ifdef NEED_HOST_ADDRESS               /* no -- needs name server! */
+    struct hostent * phost;            /* Pointer to host -- See netdb.h */
+#endif
+    int namelength = sizeof(name);
+    
+    if (hostname) return;              /* Already done */
+    gethostname(name, namelength);     /* Without domain */
+    CTRACE(tfp, "TCP: Local host name is %s\n", name);
+    StrAllocCopy(hostname, name);
+
+#ifdef NEED_HOST_ADDRESS               /* no -- needs name server! */
+    phost=gethostbyname(name);         /* See netdb.h */
+    if (!phost) {
+       printf(
+               "TCP: Can't find my own internet node address for `%s'!!\n",
+               name);
+       return;  /* Fail! */
+    }
+    StrAllocCopy(hostname, phost->h_name);
+    memcpy(&HTHostAddress, &phost->h_addr, phost->h_length);
+    if (TRACE) printf("     Name server says that is `%s' = %s\n",
+           hostname, HTInetString(&HTHostAddress));
+#endif
+}
+
+#ifdef __STDC__
+PUBLIC const char * HTHostName(void)
+#else
+PUBLIC char * HTHostName()
+#endif
+{
+    get_host_details();
+    return hostname;
+}
+
diff --git a/Cl/WWWLibrary/HTTCP.h b/Cl/WWWLibrary/HTTCP.h
new file mode 100644 (file)
index 0000000..3e9a04a
--- /dev/null
@@ -0,0 +1,98 @@
+/*                     Generic Communication Code              HTTCP.h
+**                     ==========================
+**
+*/
+#include "tcp.h"
+
+#ifdef SHORT_NAMES
+#define HTInetStatus            HTInStat
+#define HTInetString           HTInStri
+#define HTParseInet            HTPaInet
+#endif
+
+
+/*     Produce a string for an internet address
+**     ---------------------------------------
+**
+** On exit,
+**     returns a pointer to a static string which must be copied if
+**             it is to be kept.
+*/
+#ifdef __STDC__
+extern const char * HTInetString(struct sockaddr_in* sin);
+#else
+extern char * HTInetString();
+#endif
+
+
+/*     Encode INET status (as in sys/errno.h)                    inet_status()
+**     ------------------
+**
+** On entry,
+**     where           gives a description of what caused the error
+**     global errno    gives the error number in the unix way.
+**
+** On return,
+**     returns         a negative status in the unix way.
+*/
+#ifdef __STDC__
+extern int HTInetStatus(char *where);
+#else
+extern int HTInetStatus();
+#endif
+
+/*     Publicly accesible variables
+*/
+/* extern struct sockaddr_in HTHostAddress; */
+                       /* The internet address of the host */
+                       /* Valid after call to HTHostName() */
+
+
+/*     Parse a cardinal value                                 parse_cardinal()
+**     ----------------------
+**
+** On entry,
+**     *pp         points to first character to be interpreted, terminated by
+**                 non 0:9 character.
+**     *pstatus    points to status already valid
+**     maxvalue    gives the largest allowable value.
+**
+** On exit,
+**     *pp         points to first unread character
+**     *pstatus    points to status updated iff bad
+*/
+#ifdef __STDC__
+extern unsigned int HTCardinal(int *pstatus,
+       char            **pp,
+       unsigned int    max_value);
+#else
+extern unsigned int HTCardinal();
+#endif
+
+/*     Parse an internet node address and port
+**     ---------------------------------------
+**
+** On entry,
+**     str     points to a string with a node name or number,
+**             with optional trailing colon and port number.
+**     sin     points to the binary internet address field.
+**
+** On exit,
+**     *sin    is filled in. If no port is specified in str, that
+**             field is left unchanged in *sin.
+*/
+#ifdef __STDC__
+extern int HTParseInet(struct sockaddr_in* sin, const char *str);
+#else
+extern int HTParseInet();
+#endif
+
+/*     Get Name of This Machine
+**     ------------------------
+**
+*/
+#ifdef __STDC__
+extern const char * HTHostName(void);
+#else
+extern char * HTHostName();
+#endif
diff --git a/Cl/WWWLibrary/HTTP.c b/Cl/WWWLibrary/HTTP.c
new file mode 100644 (file)
index 0000000..69c6006
--- /dev/null
@@ -0,0 +1,132 @@
+/*     HyperText Tranfer Protocol      - Client implementation         HTTP.c
+**     ==========================
+*/
+
+/*     Module parameters:
+**     -----------------
+**
+**  These may be undefined and redefined by syspec.h
+*/
+#include "HTParse.h"
+#include "HTUtils.h"
+#include "tcp.h"
+#include "HTTCP.h"
+#include "HTFormat.h"
+
+
+#ifndef ERWISE
+/*             Load Dcoument from HTTP Server                  HTLoadHTTP()
+**             ==============================
+**
+**     Given a hypertext address, this routine loads a document.
+**
+**
+** On entry,
+**     arg     is the hypertext reference of the article to be loaded.
+**     gate    is nill if no gateway, else the gateway address.
+**
+** On exit,
+**     returns >=0     If no error, a good socket number
+**             <0      Error.
+**
+**     The socket must be closed by the caller after the document has been
+**     read.
+**
+*/
+PUBLIC int HTLoadHTTP ARGS4 (CONST char *, arg,
+       CONST char *,   gate, 
+       HTAnchor *,     anAnchor,
+       int,            diag)
+{
+    int s;                             /* Socket number for returned data */
+    char *command;                     /* The whole command */
+    int status;                                /* tcp return */
+    struct sockaddr_in soc_address;    /* Binary network address */
+    struct sockaddr_in* sin = &soc_address;
+
+    if (!arg) return -3;                       /* Bad if no name sepcified     */
+    if (!*arg) return -2;                      /* Bad if name had zero length  */
+
+/*  Set up defaults:
+*/
+    sin->sin_family = AF_INET;                 /* Family, host order  */
+    sin->sin_port = htons(TCP_PORT);           /* Default: new port,    */
+
+    if (TRACE) {
+        if (gate) fprintf(stderr,
+               "HTTPAccess: Using gateway %s for %s\n", gate, arg);
+        else fprintf(stderr, "HTTPAccess: Direct access for %s\n", arg);
+    }
+    
+/* Get node name and optional port number:
+*/
+    {
+       char *p1 = HTParse(gate ? gate : arg, "", PARSE_HOST);
+       HTParseInet(sin, p1);
+        free(p1);
+    }
+    
+   
+/*     Now, let's get a socket set up from the server for the sgml data:
+*/      
+    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address));
+    if (status<0){
+       if (TRACE) printf(
+           "HTTP: Unable to connect to remote host for `%s'.\n",
+           arg);
+       free(command);
+       return HTInetStatus("connect");
+    }
+    
+    if (TRACE) printf("HTTP connected, socket %d\n", s);
+
+/*     Ask that node for the document,
+**     omitting the host name & anchor if not gatewayed.
+*/        
+    if (gate) {
+        command = malloc(4 + strlen(arg)+ 2 + 1);
+        if (command == NULL) outofmem(__FILE__, "HTLoadHTTP");
+        strcpy(command, "GET ");
+       strcat(command, arg);
+    } else { /* not gatewayed */
+       char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
+        command = malloc(4 + strlen(p1)+ 2 + 1);
+      if (command == NULL) outofmem(__FILE__, "HTLoadHTTP");
+        strcpy(command, "GET ");
+       strcat(command, p1);
+       free(p1);
+    }
+    strcat(command, "\r\n");           /* Include CR for telnet compat. */
+           
+
+    if (TRACE) printf("HTTP writing command `%s' to socket %d\n", command, s);
+    
+#ifdef NOT_ASCII
+    {
+       char * p;
+       for(p = command; *p; p++) {
+           *p = TOASCII(*p);
+       }
+    }
+#endif
+
+    status = NETWRITE(s, command, (int)strlen(command));
+    free(command);
+    if (status<0){
+       if (TRACE) printf("HTTPAccess: Unable to send command.\n");
+           return HTInetStatus("send");
+    }
+
+/*     Now load the  date
+*/
+    HTParseFormat(diag ? WWW_PLAINTEXT : WWW_HTML,
+                (HTParentAnchor *) anAnchor, s);
+    
+    if (TRACE) printf("HTTP: close socket %d.\n", s);
+    status = NETCLOSE(s);
+
+    return HT_LOADED;                  /* Good return */
+}
+#endif /* ERWISE */
diff --git a/Cl/WWWLibrary/HTTP.h b/Cl/WWWLibrary/HTTP.h
new file mode 100644 (file)
index 0000000..be72957
--- /dev/null
@@ -0,0 +1,39 @@
+/*     HyperText Tranfer Protocol                                      HTTP.h
+**     ==========================
+*/
+
+#ifndef HTTP_H
+#define HTTP_H
+
+#include "HTAnchor.h"
+
+/*             Open Socket for reading from HTTP Server        HTTP_get()
+**             ========================================
+**
+**     Given a hypertext address, this routine opens a socket to the server,
+**     sends the "get" command to ask for the node, and then returns the
+**     socket to the caller. The socket must later be closed by the caller.
+**
+** On entry,
+**     arg     is the hypertext reference of the article to be loaded.
+** On exit,
+**     returns >=0     If no error, a good socket number
+**             <0      Error.
+**
+*/
+#ifdef __STDC__
+extern int HTTP_Get(const char * arg);
+#else
+extern int HTTP_Get();
+#endif
+
+/*     Load Document using HTTP
+**     ------------------------
+*/
+
+extern int HTLoadHTTP PARAMS((CONST char *arg,
+       CONST char * gateway,
+       HTParentAnchor * anAnchor,
+       int diag));
+
+#endif /* HTTP_H */
diff --git a/Cl/WWWLibrary/HTUtils.h b/Cl/WWWLibrary/HTUtils.h
new file mode 100644 (file)
index 0000000..7d92c1d
--- /dev/null
@@ -0,0 +1,192 @@
+/*     Macros for general use                                  HTUtils.h
+**
+**     See also: the system dependent file "tcp.h"
+*/
+
+/* extern void *malloc(size_t size); */
+
+#ifndef HTUTILS_H
+#define HTUTILS_H
+
+#ifdef SHORT_NAMES
+#define WWW_TraceFlag HTTrFlag
+#endif
+
+/*     Debug message control.
+*/
+#ifdef DEBUG
+
+#ifndef STDIO_H
+#include <stdio.h>
+#define STDIO_H
+#endif
+
+#define TRACE (WWW_TraceFlag)
+#define PROGRESS(str) printf(str)
+extern int WWW_TraceFlag;
+#else
+#define TRACE 0
+#define PROGRESS(str) /* nothing for now */
+#endif
+#define CTRACE if(TRACE)fprintf
+#define tfp stdout
+
+/*     Standard C library for malloc() etc
+*/
+#ifdef vax
+#ifdef unix
+#define ultrix /* Assume vax+unix=ultrix */
+#endif
+#endif
+
+#ifndef VMS
+#ifndef ultrix
+#ifdef NeXT
+#include <libc.h>      /* NeXT */
+#endif
+#ifndef MACH           /* Vincent.Cate@furmint.nectar.cs.cmu.edu */
+#include <stdlib.h>    /* ANSI */
+#endif
+#else
+#include <malloc.h>    /* ultrix */
+#include <memory.h>
+#include <stdio.h>
+#endif
+
+#else                          /* VMS */
+#include <stdio.h>
+#include <ctype.h>
+#endif
+
+#define PUBLIC                 /* Accessible outside this module     */
+#define PRIVATE static         /* Accessible only within this module */
+
+#ifdef __STDC__
+#define CONST const            /* "const" only exists in STDC */
+#define NOPARAMS (void)
+#define PARAMS(parameter_list) parameter_list
+#define NOARGS (void)
+#define ARGS1(t,a) \
+  (t a)
+#define ARGS2(t,a,u,b) \
+  (t a, u b)
+#define ARGS3(t,a,u,b,v,c) \
+  (t a, u b, v c)
+#define ARGS4(t,a,u,b,v,c,w,d) \
+  (t a, u b, v c, w d)
+#define ARGS5(t,a,u,b,v,c,w,d,x,e) \
+  (t a, u b, v c, w d, x e)
+#define ARGS6(t,a,u,b,v,c,w,d,x,e,y,f) \
+  (t a, u b, v c, w d, x e, y f)
+#define ARGS7(t,a,u,b,v,c,w,d,x,e,y,f,z,g) \
+  (t a, u b, v c, w d, x e, y f, z g)
+#define ARGS8(t,a,u,b,v,c,w,d,x,e,y,f,z,g,s,h) \
+  (t a, u b, v c, w d, x e, y f, z g, s h)
+#define ARGS9(t,a,u,b,v,c,w,d,x,e,y,f,z,g,s,h,r,i) \
+  (t a, u b, v c, w d, x e, y f, z g, s h, r i)
+
+#else  /* not ANSI */
+
+#define CONST
+#define NOPARAMS ()
+#define PARAMS(parameter_list) ()
+#define NOARGS ()
+#define ARGS1(t,a) (a) \
+  t a;
+#define ARGS2(t,a,u,b) (a,b) \
+  t a; u b;
+#define ARGS3(t,a,u,b,v,c) (a,b,c) \
+  t a; u b; v c;
+#define ARGS4(t,a,u,b,v,c,w,d) (a,b,c,d) \
+  t a; u b; v c; w d;
+#define ARGS5(t,a,u,b,v,c,w,d,x,e) (a,b,c,d,e) \
+  t a; u b; v c; w d; x e;
+#define ARGS6(t,a,u,b,v,c,w,d,x,e,y,f) (a,b,c,d,e,f) \
+  t a; u b; v c; w d; x e; y f;
+#define ARGS7(t,a,u,b,v,c,w,d,x,e,y,f,z,g) (a,b,c,d,e,f,g) \
+  t a; u b; v c; w d; x e; y f; z g;
+#define ARGS8(t,a,u,b,v,c,w,d,x,e,y,f,z,g,s,h) (a,b,c,d,e,f,g,h) \
+  t a; u b; v c; w d; x e; y f; z g; s h;
+#define ARGS9(t,a,u,b,v,c,w,d,x,e,y,f,z,g,s,h,r,i) (a,b,c,d,e,f,g,h,i) \
+  t a; u b; v c; w d; x e; y f; z g; s h; r i;
+
+#endif /* __STDC__ (ANSI) */
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+
+/* Note: GOOD and BAD are already defined (differently) on RS6000 aix */
+/* #define GOOD(status) ((status)&1)    VMS style status: test bit 0         */
+/* #define BAD(status)  (!GOOD(status))         Bit 0 set if OK, otherwise clear   */
+
+#ifndef BOOLEAN_DEFINED
+typedef char   BOOLEAN;                /* Logical value */
+#ifndef CURSES
+#ifndef TRUE
+#define TRUE   (BOOLEAN)1
+#define        FALSE   (BOOLEAN)0
+#endif
+#endif
+#define BOOLEAN_DEFINED
+#endif
+
+#ifndef BOOL
+#define BOOL BOOLEAN
+#endif
+#ifndef YES
+#define YES (BOOLEAN)1
+#define NO (BOOLEAN)0
+#endif
+
+#define TCP_PORT 80            /* 80 Allocated by Jon Postel/ISI 24-Jan-92 */
+
+/*     Is character c white space? */
+
+#ifndef NOT_ASCII
+#define WHITE(c) (((unsigned char)(c))<=' ')   /* Assumes ASCII but faster */
+#else
+#define WHITE(c) ( ((c)==' ') || ((c)=='\t') || ((c)=='\n') || ((c)=='\r') )
+#endif
+
+#define HT_LOADED (29999)              /* Instead of a socket */
+
+#include "HTString.h"  /* String utilities */
+
+#ifndef ERWISE_UI
+/* Toni */
+#include <stdarg.h>
+#endif /* ERWISE */
+</