--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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++;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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++;
+}
--- /dev/null
+#
+#
+#
+
+#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))
+
+
--- /dev/null
+# 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
+
--- /dev/null
+/* 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 */
+
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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;
+ }
+}
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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;
+}
+
+
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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));
--- /dev/null
+/* 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 */
+
--- /dev/null
+/* 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;
--- /dev/null
+/* 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;
+
+}
--- /dev/null
+/* 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
+));
+
--- /dev/null
+/* 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
--- /dev/null
+/* 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) */
+
+}
--- /dev/null
+/* 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
--- /dev/null
+/* 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*/
+}
+
--- /dev/null
+/* 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 */
--- /dev/null
+#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");
+}
--- /dev/null
+#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 */
--- /dev/null
+/* 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 */
+}
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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
--- /dev/null
+/* 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;
+}
+
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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
+extern char *sys_errlist[]; /* see man perror on cernvax */
+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: */
+#ifdef MVS /* Oustanding problem with crsh in MVS gethostbyname */
+ if(TRACE)printf("HTTCP: Calling gethostbyname(%s)\n", host);
+#endif
+ phost=gethostbyname(host); /* See netdb.h */
+#ifdef MVS
+ if(TRACE)printf("HTTCP: gethostbyname() returned %d\n", phost);
+#endif
+ if (!phost) {
+ if (TRACE) printf(
+ "HTTPAccess: Can't find internet node name `%s'.\n",host);
+ 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) {
+ if (TRACE) 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;
+}
+
--- /dev/null
+/* 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
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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