summaryrefslogtreecommitdiffstats
path: root/cpukit/httpd
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>1999-10-27 12:50:33 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>1999-10-27 12:50:33 +0000
commitc1cdaa0ce8017b075487e6670f89eb4e715258ea (patch)
treed4571a02595d6cf6a24d40d6968d83ece3b7a574 /cpukit/httpd
parentNew files created by split of old imfs_handlers.c. (diff)
downloadrtems-c1cdaa0ce8017b075487e6670f89eb4e715258ea.tar.bz2
Patch from Emmanuel Raguet <raguet@crf.canon.fr> and Eric Valette
<valette@crf.canon.fr> to add a port of the GoAhead web server (httpd) to the RTEMS build tree. They have successfully used this BSP on i386/pc386 and PowerPC/mcp750. Mark and Joel spoke with Nick Berliner <nickb@goahead.com> on 26 Oct 1999 about this port and got verbal approval to include it in RTEMS distributions.
Diffstat (limited to 'cpukit/httpd')
-rw-r--r--cpukit/httpd/Makefile.in59
-rw-r--r--cpukit/httpd/asp.c313
-rw-r--r--cpukit/httpd/balloc.c836
-rw-r--r--cpukit/httpd/default.c389
-rw-r--r--cpukit/httpd/ej.h226
-rw-r--r--cpukit/httpd/ejlex.c679
-rw-r--r--cpukit/httpd/ejparse.c1665
-rw-r--r--cpukit/httpd/form.c163
-rw-r--r--cpukit/httpd/h.c171
-rw-r--r--cpukit/httpd/handler.c284
-rw-r--r--cpukit/httpd/mime.c112
-rw-r--r--cpukit/httpd/misc.c581
-rw-r--r--cpukit/httpd/ringq.c537
-rw-r--r--cpukit/httpd/rom.c198
-rw-r--r--cpukit/httpd/rtems_webserver.h7
-rw-r--r--cpukit/httpd/security.c109
-rw-r--r--cpukit/httpd/socket.c991
-rw-r--r--cpukit/httpd/sym.c452
-rw-r--r--cpukit/httpd/uemf.c193
-rw-r--r--cpukit/httpd/uemf.h666
-rw-r--r--cpukit/httpd/url.c203
-rw-r--r--cpukit/httpd/value.c74
-rw-r--r--cpukit/httpd/wbase64.c149
-rw-r--r--cpukit/httpd/webcomp.c177
-rw-r--r--cpukit/httpd/webmain.c410
-rw-r--r--cpukit/httpd/webpage.c138
-rw-r--r--cpukit/httpd/webrom.c12
-rw-r--r--cpukit/httpd/webs.c1841
-rw-r--r--cpukit/httpd/webs.h182
-rw-r--r--cpukit/httpd/websuemf.c39
-rw-r--r--cpukit/httpd/wsIntrn.h268
31 files changed, 12124 insertions, 0 deletions
diff --git a/cpukit/httpd/Makefile.in b/cpukit/httpd/Makefile.in
new file mode 100644
index 0000000000..2951acb714
--- /dev/null
+++ b/cpukit/httpd/Makefile.in
@@ -0,0 +1,59 @@
+#
+# $Id$
+#
+
+@SET_MAKE@
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = ..
+subdir = rtems_servers
+
+RTEMS_ROOT = @RTEMS_ROOT@
+PROJECT_ROOT = @PROJECT_ROOT@
+
+VPATH = @srcdir@
+
+LIBNAME = lib.a
+LIB = ${ARCH}/${LIBNAME}
+
+# C and C++ source names, if any, go here -- minus the .c or .cc
+C_PIECES=asp balloc wbase64 default ejlex ejparse form h handler mime \
+ misc webpage ringq rom security socket sym uemf url value webcomp \
+ webrom webs websuemf webmain
+C_FILES = $(C_PIECES:%=%.c)
+C_O_FILES = $(C_PIECES:%=${ARCH}/%.o)
+
+SRCS = $(C_FILES)
+OBJS = $(C_O_FILES)
+
+include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg
+include $(RTEMS_ROOT)/make/lib.cfg
+
+INSTALL_CHANGE = @INSTALL_CHANGE@
+
+#
+# Add local stuff here using +=
+#
+
+DEFINES += -DWEBS -DUEMF -DOS="LINUX" -DLINUX
+CPPFLAGS +=
+CFLAGS +=
+
+#
+# Add your list of files to delete here. The config files
+# already know how to delete some stuff, so you may want
+# to just run 'make clean' first to see what gets missed.
+# 'make clobber' already includes 'make clean'
+#
+
+CLEAN_ADDITIONS += $(LIB)
+CLOBBER_ADDITIONS +=
+
+all: ${ARCH} $(LIB)
+
+$(LIB): $(SRCS) ${OBJS}
+ $(make-library)
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) \
+ && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
diff --git a/cpukit/httpd/asp.c b/cpukit/httpd/asp.c
new file mode 100644
index 0000000000..4611a8f422
--- /dev/null
+++ b/cpukit/httpd/asp.c
@@ -0,0 +1,313 @@
+/*
+ * asp.c -- Active Server Page Support
+ *
+ * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * The ASP module processes ASP pages and executes embedded scripts. It
+ * support an open scripting architecture with in-built support for
+ * Ejscript(TM).
+ */
+
+/********************************* Includes ***********************************/
+
+#include "wsIntrn.h"
+
+/********************************** Locals ************************************/
+
+static sym_fd_t websAspFunctions = -1; /* Symbol table of functions */
+
+/***************************** Forward Declarations ***************************/
+
+static char_t *strtokcmp(char_t* s1, char_t* s2);
+static char_t *skipWhite(char_t *s);
+
+/************************************* Code ***********************************/
+/*
+ * Create script spaces and commands
+ */
+
+int websAspOpen()
+{
+/*
+ * Create the table for ASP functions
+ */
+ websAspFunctions = symOpen(128);
+
+/*
+ * Create standard ASP commands
+ */
+ websAspDefine(T("write"), websAspWrite);
+ return 0;
+}
+
+/************************************* Code ***********************************/
+/*
+ * Close Asp symbol table.
+ */
+
+void websAspClose()
+{
+ if (websAspFunctions != -1) {
+ symClose(websAspFunctions, NULL);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Process ASP requests and expand all scripting commands. We read the
+ * entire ASP page into memory and then process. If you have really big
+ * documents, it is better to make them plain HTML files rather than ASPs.
+ */
+
+int websAspRequest(webs_t wp, char_t* lpath)
+{
+ websStatType sbuf;
+ char *rbuf;
+ char_t *token, *lang, *result, *path, *ep, *cp, *buf, *nextp;
+ char_t *last;
+ int rc, engine, len, ejid;
+
+ a_assert(websValid(wp));
+ a_assert(lpath && *lpath);
+
+ rc = -1;
+ buf = NULL;
+ rbuf = NULL;
+ engine = EMF_SCRIPT_EJSCRIPT;
+ wp->flags |= WEBS_HEADER_DONE;
+ path = websGetRequestPath(wp);
+
+/*
+ * Create Ejscript instance incase it is needed
+ */
+ ejid = ejOpenEngine(wp->cgiVars, websAspFunctions);
+ if (ejid < 0) {
+ websError(wp, 200, T("Can't create Ejscript engine"));
+ goto done;
+ }
+ ejSetUserHandle(ejid, (int) wp);
+
+ if (websPageStat(wp, lpath, path, &sbuf) < 0) {
+ websError(wp, 200, T("Can't stat %s"), lpath);
+ goto done;
+ }
+
+/*
+ * Create a buffer to hold the ASP file in-memory
+ */
+ len = sbuf.size * sizeof(char);
+ if ((rbuf = balloc(B_L, len + 1)) == NULL) {
+ websError(wp, 200, T("Can't get memory"));
+ goto done;
+ }
+ rbuf[len] = '\0';
+
+ if (websPageReadData(wp, rbuf, len) != len) {
+ websError(wp, 200, T("Cant read %s"), lpath);
+ goto done;
+ }
+ websCloseFileHandle(wp);
+
+/*
+ * Convert to UNICODE if necessary.
+ */
+ if ((buf = ballocAscToUni(rbuf)) == NULL) {
+ websError(wp, 200, T("Can't get memory"));
+ goto done;
+ }
+
+/*
+ * Scan for the next "<%"
+ */
+ last = buf;
+ rc = 0;
+ while (rc == 0 && *last && ((nextp = gstrstr(last, T("<%"))) != NULL)) {
+ websWriteBlock(wp, last, (nextp - last));
+ nextp = skipWhite(nextp + 2);
+
+/*
+ * Decode the language
+ */
+ token = T("language");
+
+ if ((lang = strtokcmp(nextp, token)) != NULL) {
+ if ((cp = strtokcmp(lang, T("=javascript"))) != NULL) {
+ engine = EMF_SCRIPT_EJSCRIPT;
+ } else {
+ cp = nextp;
+ }
+ nextp = cp;
+ }
+
+/*
+ * Find tailing bracket and then evaluate the script
+ */
+ if ((ep = gstrstr(nextp, T("%>"))) != NULL) {
+
+ *ep = '\0';
+ last = ep + 2;
+ nextp = skipWhite(nextp);
+/*
+ * Handle backquoted newlines
+ */
+ for (cp = nextp; *cp; ) {
+ if (*cp == '\\' && (cp[1] == '\r' || cp[1] == '\n')) {
+ *cp++ = ' ';
+ while (*cp == '\r' || *cp == '\n') {
+ *cp++ = ' ';
+ }
+ } else {
+ cp++;
+ }
+ }
+
+/*
+ * Now call the relevant script engine. Output is done directly
+ * by the ASP script procedure by calling websWrite()
+ */
+ if (*nextp) {
+ result = NULL;
+ if (engine == EMF_SCRIPT_EJSCRIPT) {
+ rc = scriptEval(engine, nextp, &result, ejid);
+ } else {
+ rc = scriptEval(engine, nextp, &result, (int) wp);
+ }
+ if (rc < 0) {
+/*
+ * On an error, discard all output accumulated so far
+ * and store the error in the result buffer. Be careful if the
+ * user has called websError() already.
+ */
+ if (websValid(wp)) {
+ if (result) {
+ websWrite(wp, T("<h2><b>ASP Error: %s</b></h2>\n"),
+ result);
+ websWrite(wp, T("<pre>%s</pre>"), nextp);
+ bfree(B_L, result);
+ } else {
+ websWrite(wp, T("<h2><b>ASP Error</b></h2>\n%s\n"),
+ nextp);
+ }
+ websWrite(wp, T("</body></html>\n"));
+ rc = 0;
+ }
+ goto done;
+ }
+ }
+
+ } else {
+ websError(wp, 200, T("Unterminated script in %s: \n"), lpath);
+ rc = -1;
+ goto done;
+ }
+ }
+/*
+ * Output any trailing HTML page text
+ */
+ if (last && *last && rc == 0) {
+ websWriteBlock(wp, last, gstrlen(last));
+ }
+ rc = 0;
+
+/*
+ * Common exit and cleanup
+ */
+done:
+ if (websValid(wp)) {
+ websCloseFileHandle(wp);
+ if (ejid >= 0) {
+ ejCloseEngine(ejid);
+ }
+ }
+ bfreeSafe(B_L, buf);
+ bfreeSafe(B_L, rbuf);
+ return rc;
+}
+
+/******************************************************************************/
+/*
+ * Define an ASP Ejscript function. Bind an ASP name to a C procedure.
+ */
+
+int websAspDefine(char_t *name,
+ int (*fn)(int ejid, webs_t wp, int argc, char_t **argv))
+{
+ return ejSetGlobalFunctionDirect(websAspFunctions, name,
+ (int (*)(int, void*, int, char_t**)) fn);
+}
+
+/******************************************************************************/
+/*
+ * Asp write command. This implemements <% write("text"); %> command
+ */
+
+int websAspWrite(int ejid, webs_t wp, int argc, char_t **argv)
+{
+ int i;
+
+ a_assert(websValid(wp));
+ a_assert(argv);
+
+ for (i = 0; i < argc; ) {
+ if (websWriteBlock(wp, argv[i], gstrlen(argv[i])) < 0) {
+ return -1;
+ }
+ if (++i < argc) {
+ if (websWriteBlock(wp, T(" "), 2) < 0) {
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * strtokcmp -- Find s2 in s1. We skip leading white space in s1.
+ * Return a pointer to the location in s1 after s2 ends.
+ */
+
+static char_t* strtokcmp(char_t* s1, char_t* s2)
+{
+ int len;
+
+ s1 = skipWhite(s1);
+ len = gstrlen(s2);
+ for (len = gstrlen(s2); len > 0 && (tolower(*s1) == tolower(*s2)); len--) {
+ if (*s2 == '\0') {
+ return s1;
+ }
+ s1++;
+ s2++;
+ }
+ if (len == 0) {
+ return s1;
+ }
+ return NULL;
+}
+
+/******************************************************************************/
+/*
+ * Skip white space
+ */
+
+static char_t *skipWhite(char_t *s)
+{
+ a_assert(s);
+
+ if (s == NULL) {
+ return s;
+ }
+ while (*s && gisspace(*s)) {
+ s++;
+ }
+ return s;
+}
+
+/******************************************************************************/ \ No newline at end of file
diff --git a/cpukit/httpd/balloc.c b/cpukit/httpd/balloc.c
new file mode 100644
index 0000000000..20612ed148
--- /dev/null
+++ b/cpukit/httpd/balloc.c
@@ -0,0 +1,836 @@
+/*
+ * balloc.c -- Block allocation module
+ *
+ * Copyright (c) GoAhead Software Inc., 1995-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * This module implements a very fast block allocation scheme suitable for
+ * ROMed environments. It maintains block class queues for rapid allocation
+ * and minimal fragmentation. This modules does not coalesce blocks. The
+ * storage space may be populated statically or via the traditional malloc
+ * mechanisms. Large blocks greater than the maximum class size may be
+ * allocated from the O/S or run-time system via malloc. To permit the use
+ * of malloc, call bopen with flags set to B_USE_MALLOC (this is the default).
+ * It is recommended that bopen be called first thing in the application.
+ * If it is not, it will be called with default values on the first call to
+ * balloc(). Note that this code is not designed for multi-threading purposes
+ * and it depends on newly declared variables being initialized to zero.
+ */
+
+/********************************* Includes ***********************************/
+
+#define IN_BALLOC
+
+#if UEMF
+ #include "uemf.h"
+#else
+ #include "basic/basicInternal.h"
+#endif
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+#if !NO_BALLOC
+/********************************* Defines ************************************/
+
+typedef struct {
+ union {
+ void *next; /* Pointer to next in q */
+ int size; /* Actual requested size */
+ } u;
+ int flags; /* Per block allocation flags */
+} bType;
+
+/*
+ * Define B_STATS if you wish to track memory block and stack usage
+ */
+#if B_STATS
+/*
+ * Optional statistics
+ */
+
+typedef struct {
+ long alloc; /* Block allocation calls */
+ long inuse; /* Blocks in use */
+} bStatsType;
+
+typedef struct {
+ char_t file[FNAMESIZE];
+ long allocated; /* Bytes currently allocated */
+ long count; /* Current block count */
+ long allocs; /* Count of alloc attempts */
+} bStatsFileType;
+
+/*
+ * This one is very expensive but great stats
+ */
+typedef struct {
+ void *ptr; /* Pointer to memory */
+ bStatsFileType *who; /* Who allocated the memory */
+} bStatsBlkType;
+
+static bStatsType bStats[B_MAX_CLASS]; /* Per class stats */
+static bStatsFileType bStatsFiles[B_MAX_FILES];/* Per file stats */
+static bStatsBlkType bStatsBlks[B_MAX_BLOCKS];/* Per block stats */
+static int bStatsBlksMax; /* Max block entry */
+static int bStatsFilesMax; /* Max file entry */
+static int bStatsMemInUse; /* Memory currently in use */
+static int bStatsMemMax; /* Max memory ever used */
+static void *bStackMin = (void*) -1;/* Miniumum stack position */
+static void *bStackStart; /* Starting stack position */
+static int bStatsMemMalloc; /* Malloced memory */
+#endif /* B_STATS */
+
+
+/********************************** Locals ************************************/
+/*
+ * bQhead blocks are created as the original memory allocation is freed up.
+ * See bfree.
+ */
+static bType *bQhead[B_MAX_CLASS]; /* Per class block q head */
+static char *bFreeBuf; /* Pointer to free memory */
+static char *bFreeNext; /* Pointer to next free mem */
+static int bFreeSize; /* Size of free memory */
+static int bFreeLeft; /* Size of free left for use */
+static int bFlags = B_USE_MALLOC; /* Default to auto-malloc */
+
+/*************************** Forward Declarations *****************************/
+
+#if B_STATS
+static void bStatsAlloc(B_ARGS_DEC, void *ptr, int q, int size);
+static void bStatsFree(B_ARGS_DEC, void *ptr, int q, int size);
+static void bstatsWrite(int handle, char_t *fmt, ...);
+static int bStatsFileSort(const void *cp1, const void *cp2);
+#endif /* B_STATS */
+
+#if B_FILL || B_VERIFY_CAUSES_SEVERE_OVERHEAD
+static void bFillBlock(void *buf, int bufsize);
+#endif
+
+#if B_VERIFY_CAUSES_SEVERE_OVERHEAD
+static void verifyUsedBlock(bType *bp, int q);
+static void verifyFreeBlock(bType *bp, int q);
+static void verifyBallocSpace();
+#endif
+
+/********************************** Code **************************************/
+/*
+ * Initialize the balloc module. bopen should be called the very first thing
+ * after the application starts and bclose should be called the last thing
+ * before exiting. If bopen is not called, it will be called on the first
+ * allocation with default values. "buf" points to memory to use of size
+ * "bufsize". If buf is NULL, memory is allocated using malloc. flags may
+ * be set to B_USE_MALLOC if using malloc is okay. This routine will allocate
+ * an initial buffer of size bufsize for use by the application.
+ */
+
+int bopen(void *buf, int bufsize, int flags)
+{
+ bFlags = flags;
+
+ if (buf == NULL) {
+ if (bufsize == 0) {
+ bufsize = B_DEFAULT_MEM;
+ }
+ if ((buf = malloc(bufsize)) == NULL) {
+ return -1;
+ }
+#if B_STATS
+ bStatsMemMalloc += bufsize;
+#endif
+ } else {
+ bFlags |= B_USER_BUF;
+ }
+
+ bFreeSize = bFreeLeft = bufsize;
+ bFreeBuf = bFreeNext = buf;
+#if B_FILL || B_VERIFY_CAUSES_SEVERE_OVERHEAD
+ bFillBlock(buf, bufsize);
+#endif
+#if B_STATS
+ bStackStart = &buf;
+#endif
+#if B_VERIFY_CAUSES_SEVERE_OVERHEAD
+ verifyFreeBlock(buf, bufsize);
+#endif
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Close down the balloc module and free all malloced memory.
+ */
+
+void bclose()
+{
+#if B_VERIFY_CAUSES_SEVERE_OVERHEAD
+ verifyBallocSpace();
+#endif
+ if (! (bFlags & B_USER_BUF)) {
+ free(bFreeBuf);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Allocate a block of the requested size. First check the block
+ * queues for a suitable one.
+ */
+
+void *balloc(B_ARGS_DEC, int size)
+{
+ bType *bp;
+ int q, memSize, mask;
+
+/*
+ * Call bopen with default values if the application has not yet done so
+ */
+ if (bFreeBuf == NULL) {
+ if (bopen(NULL, B_DEFAULT_MEM , 0) < 0) {
+ return NULL;
+ }
+ }
+#if B_VERIFY_CAUSES_SEVERE_OVERHEAD
+ verifyBallocSpace();
+#endif
+ if (size < 0) {
+ return NULL;
+ }
+
+/*
+ * Determine the relevant block queue with a block big enough --
+ * include room for the block header.
+ */
+ mask = (size + sizeof(bType)) >> B_SHIFT;
+ for (q = 0; mask; mask >>= 1) {
+ q++;
+ }
+
+ a_assert(0 <= q && q <= B_MAX_CLASS);
+ memSize = (1 << (B_SHIFT + q));
+
+ if (q >= B_MAX_CLASS) {
+/*
+ * Size if bigger than the maximum class. Malloc if use has been okayed
+ */
+ if (bFlags & B_USE_MALLOC) {
+#if B_STATS
+ bstats(0, NULL);
+#endif
+ bp = (bType*) malloc(memSize);
+ if (bp == NULL) {
+ trace(0, T("B: malloc failed for %s:%d, size %d\n"),
+ B_ARGS, memSize);
+ return NULL;
+ }
+#if B_STATS
+ bStatsMemMalloc += memSize;
+#endif
+#if B_FILL || B_VERIFY_CAUSES_SEVERE_OVERHEAD
+ bFillBlock(bp, memSize);
+#endif
+
+ } else {
+ trace(0, T("B: balloc failed for %s:%d, size %d\n"),
+ B_ARGS, memSize);
+ return NULL;
+ }
+ bp->u.size = size;
+ bp->flags = B_MALLOCED;
+
+ } else if ((bp = bQhead[q]) != NULL) {
+/*
+ * Take first block off the relevant q if non-empty
+ */
+ bQhead[q] = bp->u.next;
+#if B_VERIFY_CAUSES_SEVERE_OVERHEAD
+ verifyFreeBlock(bp, q);
+#endif
+#if B_FILL || B_VERIFY_CAUSES_SEVERE_OVERHEAD
+ bFillBlock(bp, memSize);
+#endif
+ bp->u.size = size;
+ bp->flags = 0;
+
+ } else {
+ if (bFreeLeft > memSize) {
+/*
+ * The q was empty, and the free list has spare memory so
+ * create a new block out of the primary free block
+ */
+ bp = (bType*) bFreeNext;
+#if B_VERIFY_CAUSES_SEVERE_OVERHEAD
+ verifyFreeBlock(bp, q);
+#endif
+ bFreeNext += memSize;
+ bFreeLeft -= memSize;
+#if B_FILL || B_VERIFY_CAUSES_SEVERE_OVERHEAD
+ bFillBlock(bp, memSize);
+#endif
+ bp->u.size = size;
+ bp->flags = 0;
+
+ } else if (bFlags & B_USE_MALLOC) {
+ static int once = 0;
+ if (once++ < 20) {
+#if B_STATS
+ bstats(0, NULL);
+#endif
+ }
+/*
+ * Nothing left on the primary free list, so malloc a new block
+ */
+ if ((bp = (bType*) malloc(memSize)) == NULL) {
+ trace(0, T("B: malloc failed for %s:%d size %d\n"),
+ B_ARGS, memSize);
+ return NULL;
+ }
+#if B_STATS
+ bStatsMemMalloc += memSize;
+#endif
+#if B_FILL || B_VERIFY_CAUSES_SEVERE_OVERHEAD
+ bFillBlock(bp, memSize);
+#endif
+ bp->u.size = size;
+ bp->flags = B_MALLOCED;
+
+ } else {
+ trace(0, T("B: alloc failed for %s:%d size %d\n"), B_ARGS, size);
+ return NULL;
+ }
+ }
+
+#if B_STATS
+ bStatsAlloc(B_ARGS, bp, q, size);
+#endif
+ bp->flags |= B_INTEGRITY;
+
+ return (void*) ((char*) bp + sizeof(bType));
+}
+
+/******************************************************************************/
+/*
+ * Free a block back to the relevant free q. We don't free back to the O/S
+ * or run time system unless the block is greater than the maximum class size.
+ * We also do not coalesce blocks.
+ */
+
+void bfree(B_ARGS_DEC, void *mp)
+{
+ bType *bp;
+ int mask, q;
+
+#if B_VERIFY_CAUSES_SEVERE_OVERHEAD
+ verifyBallocSpace();
+#endif
+ a_assert(mp);
+
+ bp = (bType*) ((char*) mp - sizeof(bType));
+ a_assert((bp->flags & B_INTEGRITY_MASK) == B_INTEGRITY);
+ if ((bp->flags & B_INTEGRITY_MASK) != B_INTEGRITY) {
+ return;
+ }
+
+/*
+ * Determine the relevant block queue
+ */
+ mask = (bp->u.size + sizeof(bType)) >> B_SHIFT;
+ for (q = 0; mask; mask >>= 1) {
+ q++;
+ }
+ a_assert(0 <= q && q <= B_MAX_CLASS);
+
+#if B_VERIFY_CAUSES_SEVERE_OVERHEAD
+ verifyUsedBlock(bp,q);
+#endif
+ if (bp->flags & B_MALLOCED) {
+ free(bp);
+ return;
+ }
+
+#if B_STATS
+ bStatsFree(B_ARGS, bp, q, bp->u.size);
+#endif
+#if B_VERIFY_CAUSES_SEVERE_OVERHEAD
+ bFillBlock(bp, 1 << (B_SHIFT + q));
+#endif
+
+/*
+ * Simply link onto the head of the relevant q
+ */
+ bp->u.next = bQhead[q];
+ bQhead[q] = bp;
+}
+
+/******************************************************************************/
+/*
+ * Safe free
+ */
+
+void bfreeSafe(B_ARGS_DEC, void *mp)
+{
+ if (mp) {
+ bfree(B_ARGS, mp);
+ }
+}
+
+/******************************************************************************/
+#if UNICODE
+/*
+ * Duplicate a string, allow NULL pointers and then dup an empty string.
+ */
+
+char *bstrdupA(B_ARGS_DEC, char *s)
+{
+ char *cp;
+ int len;
+
+ if (s == NULL) {
+ s = "";
+ }
+ len = strlen(s) + 1;
+ if (cp = balloc(B_ARGS, len)) {
+ strcpy(cp, s);
+ }
+ return cp;
+}
+
+#endif /* UNICODE */
+/******************************************************************************/
+/*
+ * Duplicate an ascii string, allow NULL pointers and then dup an empty string.
+ * If UNICODE, bstrdup above works with wide chars, so we need this routine
+ * for ascii strings.
+ */
+
+char_t *bstrdup(B_ARGS_DEC, char_t *s)
+{
+ char_t *cp;
+ int len;
+
+ if (s == NULL) {
+ s = T("");
+ }
+ len = gstrlen(s) + 1;
+ if ((cp = balloc(B_ARGS, len * sizeof(char_t))) != NULL) {
+ gstrcpy(cp, s);
+ }
+ return cp;
+}
+
+/******************************************************************************/
+/*
+ * Reallocate a block. Allow NULL pointers and just do a malloc.
+ * Note: if the realloc fails, we return NULL and the previous buffer is
+ * preserved.
+ */
+
+void *brealloc(B_ARGS_DEC, void *mp, int newsize)
+{
+ bType* bp;
+ void *newbuf;
+
+ if (mp == NULL) {
+ return balloc(B_ARGS, newsize);
+ }
+ bp = (bType*) ((char*) mp - sizeof(bType));
+ a_assert((bp->flags & B_INTEGRITY_MASK) == B_INTEGRITY);
+ if ((newbuf = balloc(B_ARGS, newsize)) != NULL) {
+ memcpy(newbuf, mp, bp->u.size);
+ bfree(B_ARGS, mp);
+ }
+ return newbuf;
+}
+
+
+/******************************************************************************/
+#if B_FILL || B_VERIFY_CAUSES_SEVERE_OVERHEAD
+/*
+ * Fill the block (useful during development to catch zero fill assumptions)
+ */
+
+static void bFillBlock(void *buf, int bufsize)
+{
+ memset(buf, B_FILL_CHAR, bufsize);
+}
+#endif
+
+/******************************************************************************/
+#if B_STATS
+/*
+ * Statistics. Do output via calling the writefn callback function with
+ * "handle" as the output file handle.
+ */
+
+void bstats(int handle, void (*writefn)(int handle, char_t *fmt, ...))
+{
+ bStatsFileType *fp;
+ bType *bp;
+ int q, count, mem, total;
+ static int recurseProtect = 0;
+
+ if (recurseProtect++ > 0) {
+ return;
+ }
+
+ if (writefn == NULL) {
+ writefn = bstatsWrite;
+ }
+
+/*
+ * Print stats for each memory block
+ */
+ (*writefn)(handle, T("\nMemory Stats\n"));
+
+/*
+ * The following tabular format is now used for the output.
+ * Q Size Free Bytes Inuse Bytes Allocs
+ * dd ddddd ddd ddddd dddd ddddd dddd
+ */
+ (*writefn)(handle, " Q Size Free Bytes Inuse Bytes Allocs\n");
+
+ total = 0;
+ for (q = 0; q < B_MAX_CLASS; q++) {
+ count = 0;
+ for (bp = bQhead[q]; bp; bp = bp->u.next) {
+ a_assert((bp->flags & B_INTEGRITY_MASK) == B_INTEGRITY);
+ count++;
+ }
+ mem = count * (1 << (q + B_SHIFT));
+ total += mem;
+ (*writefn)(handle,
+ T("%2d %5d %3d %5d %4d %5d %4d\n"),
+ q, 1 << (q + B_SHIFT), count, mem, bStats[q].inuse,
+ bStats[q].inuse * (1 << (q + B_SHIFT)), bStats[q].alloc);
+ }
+
+ (*writefn)(handle, T("\n"));
+
+/*
+ * Print summary stats
+ */
+ (*writefn)(handle, T("Initial free list size %7d\n"), bFreeSize);
+ (*writefn)(handle, T("Max memory malloced %7d\n"), bStatsMemMalloc);
+ (*writefn)(handle, T("Max memory ever used %7d\n"), bStatsMemMax);
+ (*writefn)(handle, T("Memory currently in use %7d\n"), bStatsMemInUse);
+ (*writefn)(handle, T("Max blocks allocated %7d\n"), bStatsBlksMax);
+ (*writefn)(handle, T("Maximum stack used %7d\n"),
+ (int) bStackStart - (int) bStackMin);
+
+ (*writefn)(handle, T("Free memory on all queues %7d\n"), total);
+ (*writefn)(handle, T("Free list buffer left %7d\n"), bFreeLeft);
+ (*writefn)(handle, T("Total free memory %7d\n"), bFreeLeft + total);
+
+/*
+ * Print per file allocation stats
+ */
+ qsort(bStatsFiles, bStatsFilesMax, sizeof(bStatsFileType), bStatsFileSort);
+ (*writefn)(handle, T("\nPer File Memory Stats\n"));
+ total = 0;
+ for (fp = bStatsFiles; fp < &bStatsFiles[bStatsFilesMax]; fp++) {
+ if (fp->file[0]) {
+ (*writefn)(handle,
+ T("%18s, bytes %7d, blocks in use %5d, total allocs %6d\n"),
+ fp->file, fp->allocated, fp->count, fp->allocs);
+ total += fp->allocated;
+ }
+ }
+ (*writefn)(handle, T("\nTotal allocated %7d\n"), total);
+ recurseProtect--;
+}
+
+/******************************************************************************/
+/*
+ * File sort function. Used to sort per file stats
+ */
+
+static int bStatsFileSort(const void *cp1, const void *cp2)
+{
+ bStatsFileType *s1, *s2;
+
+ s1 = (bStatsFileType*) cp1;
+ s2 = (bStatsFileType*) cp2;
+
+ if (s1->allocated < s2->allocated)
+ return -1;
+ else if (s1->allocated == s2->allocated)
+ return 0;
+ return 1;
+}
+
+/******************************************************************************/
+/*
+ * Default output function. Just send to trace channel.
+ */
+
+static void bstatsWrite(int handle, char_t *fmt, ...)
+{
+ va_list args;
+ char_t *buf;
+
+ va_start(args, fmt);
+ buf = NULL;
+ gvsnprintf(&buf, VALUE_MAX_STRING, fmt, args);
+ va_end(args);
+ trace(0, buf);
+ if (buf) {
+ bfree(B_L, buf);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Accumulate allocation statistics
+ */
+
+static void bStatsAlloc(B_ARGS_DEC, void *ptr, int q, int size)
+{
+ bStatsFileType *fp;
+ bStatsBlkType *bp;
+ char_t name[FNAMESIZE + 10];
+
+ a_assert(file && *file);
+ a_assert(0 <= q && q <= B_MAX_CLASS);
+ a_assert(size > 0);
+
+ gsprintf(name, T("%s:%d"), B_ARGS);
+
+ bStats[q].alloc++;
+ bStats[q].inuse++;
+ bStatsMemInUse += size;
+
+ if (bStatsMemInUse > bStatsMemMax) {
+ bStatsMemMax = bStatsMemInUse;
+ }
+
+/*
+ * Track maximum stack usage. Assumes a stack growth down. Approximate as
+ * we only measure this on block allocation.
+ */
+ if ((void*) &file < bStackMin) {
+ bStackMin = (void*) &file;
+ }
+
+/*
+ * Find the file and adjust the stats for this file
+ */
+ for (fp = bStatsFiles; fp < &bStatsFiles[bStatsFilesMax]; fp++) {
+ if (fp->file[0] == file[0] && gstrcmp(fp->file, name) == 0) {
+ fp->allocated += size;
+ fp->count++;
+ fp->allocs++;
+ break;
+ }
+ }
+
+/*
+ * Find the first free slot for this file and add current block size.
+ */
+ if (fp >= &bStatsFiles[bStatsFilesMax]) {
+ for (fp = bStatsFiles; fp < &bStatsFiles[B_MAX_FILES]; fp++) {
+ if (fp->file[0] == '\0') {
+ gstrncpy(fp->file, name, TSZ(fp->file));
+ fp->allocated += size;
+ fp->count++;
+ fp->allocs++;
+ if ((fp - bStatsFiles) >= bStatsFilesMax) {
+ bStatsFilesMax = (fp - bStatsFiles) + 1;
+ }
+ break;
+ }
+ }
+ }
+
+/*
+ * Update the per block stats. Allocate a new slot.
+ */
+ for (bp = bStatsBlks; bp < &bStatsBlks[B_MAX_BLOCKS]; bp++) {
+ if (bp->ptr == NULL) {
+ bp->ptr = ptr;
+ bp->who = fp;
+ if ((bp - bStatsBlks) >= bStatsBlksMax) {
+ bStatsBlksMax = (bp - bStatsBlks) + 1;
+ }
+ break;
+ }
+ }
+}
+
+/******************************************************************************/
+/*
+ * Free statistics
+ */
+
+static void bStatsFree(B_ARGS_DEC, void *ptr, int q, int size)
+{
+ bStatsFileType *fp;
+ bStatsBlkType *bp;
+ char_t name[FNAMESIZE + 10];
+
+ a_assert(file && *file);
+ a_assert(0 <= q && q <= B_MAX_CLASS);
+ a_assert(size > 0);
+
+ bStatsMemInUse -= size;
+ bStats[q].inuse--;
+
+ gsprintf(name, T("%s:%d"), B_ARGS);
+
+/*
+ * Update the per block stats
+ */
+ for (bp = bStatsBlks; bp < &bStatsBlks[bStatsBlksMax]; bp++) {
+ if (bp->ptr == ptr) {
+ bp->ptr = NULL;
+ fp = bp->who;
+ fp->allocated -= size;
+ fp->count--;
+ return;
+ }
+ }
+ a_assert(0);
+
+}
+
+#else /* not B_STATS */
+/******************************************************************************/
+/*
+ * Dummy bstats for external calls that aren't protected by #if B_STATS.
+ */
+
+void bstats(int handle, void (*writefn)(int handle, char_t *fmt, ...))
+{
+}
+#endif /* B_STATS */
+
+/******************************************************************************/
+#if B_VERIFY_CAUSES_SEVERE_OVERHEAD
+/*
+ * The following routines verify the integrity of the balloc memory space.
+ * These functions depend use the B_FILL feature. Corruption is defined
+ * as bad integrity flags in allocated blocks or data other than B_FILL_CHAR
+ * being found anywhere in the space which is unallocated and that is not a
+ * next pointer in the free queues. a_assert is called if any corruption is
+ * found. CAUTION: These functions add severe processing overhead and should
+ * only be used when searching for a tough corruption problem.
+ */
+
+/******************************************************************************/
+/*
+ * verifyUsedBlock verifies that a block which was previously allocated is
+ * still uncorrupted.
+ */
+
+static void verifyUsedBlock(bType *bp, int q)
+{
+ int memSize, size;
+ char *p;
+
+ memSize = (1 << (B_SHIFT + q));
+ a_assert((bp->flags & ~B_MALLOCED) == B_INTEGRITY );
+ size = bp->u.size;
+ for (p = ((char *)bp)+sizeof(bType)+size; p < ((char*)bp)+memSize; p++) {
+ a_assert(*p == B_FILL_CHAR);
+ }
+}
+
+/******************************************************************************/
+/*
+ * verifyFreeBlock verifies that a previously free'd block in one of the queues
+ * is still uncorrupted.
+ */
+
+static void verifyFreeBlock(bType *bp, int q)
+{
+ int memSize;
+ char *p;
+
+ memSize = (1 << (B_SHIFT + q));
+ for (p = ((char *)bp)+sizeof(void*); p < ((char*)bp)+memSize; p++) {
+ a_assert(*p == B_FILL_CHAR);
+ }
+ bp = (bType *)p;
+ a_assert((bp->flags & ~B_MALLOCED) == B_INTEGRITY ||
+ bp->flags == B_FILL_WORD);
+}
+
+/******************************************************************************/
+/*
+ * verifyBallocSpace reads through the entire balloc memory space and
+ * verifies that all allocated blocks are uncorrupted and that with the
+ * exception of free list next pointers all other unallocated space is
+ * filled with B_FILL_CHAR.
+ */
+
+static void verifyBallocSpace()
+{
+ char *p;
+ bType *bp;
+
+ p = bFreeBuf;
+ while (p < (bFreeBuf + bFreeSize)) {
+ bp = (bType *)p;
+ if (bp->u.size > 0xFFFFF) {
+ p += sizeof(bp->u);
+ while (p < (bFreeBuf + bFreeSize) && *p == B_FILL_CHAR) {
+ p++;
+ }
+ } else {
+ a_assert(((bp->flags & ~B_MALLOCED) == B_INTEGRITY) ||
+ bp->flags == B_FILL_WORD);
+ p += (sizeof(bType) + bp->u.size);
+ while (p < (bFreeBuf + bFreeSize) && *p == B_FILL_CHAR) {
+ p++;
+ }
+ }
+ }
+}
+#endif /* B_VERIFY_CAUSES_SEVERE_OVERHEAD */
+
+/******************************************************************************/
+
+#else /* NO_BALLOC */
+int bopen(void *buf, int bufsize, int flags)
+{
+ return 0;
+}
+
+/******************************************************************************/
+
+void bclose()
+{
+}
+
+/******************************************************************************/
+#if UNICODE
+char_t* bstrdupNoBalloc(char_t* s)
+{
+ if (s) {
+ return wcsdup(s);
+ } else {
+ return wcsdup(T(""));
+ }
+}
+#endif /* UNICODE */
+
+/******************************************************************************/
+char* bstrdupANoBalloc(char* s)
+{
+ char* buf;
+
+ if (s == NULL) {
+ s = "";
+ }
+ buf = malloc(strlen(s)+1);
+ strcpy(buf, s);
+ return buf;
+}
+
+#endif /* NO_BALLOC */
+/******************************************************************************/
+
diff --git a/cpukit/httpd/default.c b/cpukit/httpd/default.c
new file mode 100644
index 0000000000..c485c22285
--- /dev/null
+++ b/cpukit/httpd/default.c
@@ -0,0 +1,389 @@
+/*
+ * default.c -- Default URL handler. Includes support for ASP.
+ *
+ * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * This module provides default URL handling and Active Server Page support.
+ *
+ * In many cases we don't check the return code of calls to websWrite as
+ * it is easier, smaller and non-fatal to continue even when the requesting
+ * browser has gone away.
+ */
+
+/********************************* Includes ***********************************/
+
+#include "wsIntrn.h"
+
+/*********************************** Locals ***********************************/
+
+static char_t *websDefaultPage; /* Default page name */
+static char_t *websDefaultDir; /* Default Web page directory */
+
+/**************************** Forward Declarations ****************************/
+
+static void websDefaultWriteEvent(webs_t wp);
+
+/*********************************** Code *************************************/
+/*
+ * Process a default URL request. This will validate the URL and handle "../"
+ * and will provide support for Active Server Pages. As the handler is the
+ * last handler to run, it always indicates that it has handled the URL
+ * by returning 1.
+ */
+
+int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg,
+ char_t *url, char_t *path, char_t* query)
+{
+ websStatType sbuf;
+ char_t *lpath, *tmp;
+ char_t *date;
+ int bytes, flags, nchars;
+
+ a_assert(websValid(wp));
+ a_assert(url && *url);
+ a_assert(path && *path);
+ a_assert(query);
+
+/*
+ * Validate the URL and ensure that ".."s don't give access to unwanted files
+ */
+ flags = websGetRequestFlags(wp);
+
+ if (websValidateUrl(wp, path) < 0) {
+ websError(wp, 500, T("Invalid URL %s"), url);
+ return 1;
+ }
+ lpath = websGetRequestLpath(wp);
+ nchars = gstrlen(lpath) - 1;
+ if (lpath[nchars] == '/' || lpath[nchars] == '\\') {
+ lpath[nchars] = '\0';
+ }
+
+/*
+ * If the file is a directory, redirect using the nominated default page
+ */
+ if (websPageIsDirectory(lpath)) {
+ nchars = gstrlen(path);
+ if (path[nchars-1] == '/' || path[nchars-1] == '\\') {
+ path[--nchars] = '\0';
+ }
+ nchars += gstrlen(websDefaultPage) + 2;
+ tmp = NULL;
+ gsnprintf(&tmp, nchars, T("%s/%s"), path, websDefaultPage);
+ websRedirect(wp, tmp);
+ bfreeSafe(B_L, tmp);
+ return 1;
+ }
+
+/*
+ * Open the document. Stat for later use.
+ */
+ if (websPageOpen(wp, lpath, path, SOCKET_RDONLY | SOCKET_BINARY,
+ 0666) < 0) {
+ websError(wp, 400,
+ T("Can't open document <b>%s</b><br>for URL <b>%s</b>"),
+ lpath, url);
+ return 1;
+ }
+ if (websPageStat(wp, lpath, path, &sbuf) < 0) {
+ websError(wp, 400, T("Can't stat page <b>%s</b><br>for URL <b>%s</b>"),
+ lpath, url);
+ }
+
+/*
+ * If the page has not been modified since the user last received it and it
+ * is not dynamically generated each time (ASP), then optimize request by
+ * sending a 304 Use local copy response
+ */
+ websStats.localHits++;
+#if WEBS_IF_MODIFIED_SUPPORT
+ if (flags & WEBS_IF_MODIFIED && !(flags & WEBS_ASP)) {
+ if (sbuf.mtime <= wp->since) {
+ websWrite(wp, T("HTTP/1.0 304 Use local copy\r\n"));
+
+ /* by license terms the following line of code must
+ * not be modified.
+ */
+ websWrite(wp, T("Server: GoAhead-Webs\r\n"));
+
+ if (flags && WEBS_KEEP_ALIVE) {
+ websWrite(wp, T("Connection: keep-alive\r\n"));
+ }
+ websWrite(wp, T("\r\n"));
+ websSetRequestFlags(wp, flags |= WEBS_HEADER_DONE);
+ websDone(wp, 304);
+ return 1;
+ }
+ }
+#endif
+
+/*
+ * Output the normal HTTP response header
+ */
+ if ((date = websGetDateString(NULL)) != NULL) {
+ websWrite(wp, T("HTTP/1.0 200 OK\r\nDate: %s\r\n"), date);
+
+/*
+ * By license terms the following line of code must not be modified.
+*/
+ websWrite(wp, T("Server: GoAhead-Webs\r\n"));
+ bfree(B_L, date);
+ }
+ flags |= WEBS_HEADER_DONE;
+
+/*
+ * If this is an ASP request, ensure the remote browser doesn't cache it.
+ * Send back both HTTP/1.0 and HTTP/1.1 cache control directives
+ */
+ if (flags & WEBS_ASP) {
+ bytes = 0;
+ websWrite(wp, T("Pragma: no-cache\r\nCache-Control: no-cache\r\n"));
+
+ } else {
+ if ((date = websGetDateString(&sbuf)) != NULL) {
+ websWrite(wp, T("Last-modified: %s\r\n"), date);
+ bfree(B_L, date);
+ }
+ bytes = sbuf.size;
+ }
+
+ if (bytes) {
+ websWrite(wp, T("Content-length: %d\r\n"), bytes);
+ websSetRequestBytes(wp, bytes);
+ }
+ websWrite(wp, T("Content-type: %s\r\n"), websGetRequestType(wp));
+
+ if ((flags & WEBS_KEEP_ALIVE) && !(flags & WEBS_ASP)) {
+ websWrite(wp, T("Connection: keep-alive\r\n"));
+ }
+ websWrite(wp, T("\r\n"));
+
+/*
+ * Evaluate ASP requests
+ */
+ if (flags & WEBS_ASP) {
+ if (websAspRequest(wp, lpath) < 0) {
+ return 1;
+ }
+ websDone(wp, 200);
+ return 1;
+ }
+
+/*
+ * All done if the browser did a HEAD request
+ */
+ if (flags & WEBS_HEAD_REQUEST) {
+ websDone(wp, 200);
+ return 1;
+ }
+/*
+ * For normal web documents, return the data via background write
+ */
+ websSetRequestSocketHandler(wp, SOCKET_WRITABLE, websDefaultWriteEvent);
+ return 1;
+}
+
+/******************************************************************************/
+/*
+ * Validate the URL path and process ".." path segments. Return -1 if the URL
+ * is bad.
+ */
+
+int websValidateUrl(webs_t wp, char_t *path)
+{
+ char_t *parts[64]; /* Array of ptr's to URL parts */
+ char_t *token, *dir, *lpath;
+ int i, len, npart;
+
+ a_assert(websValid(wp));
+ a_assert(path);
+
+ dir = websGetRequestDir(wp);
+ if (dir == NULL || *dir == '\0') {
+ return -1;
+ }
+
+/*
+ * Copy the string so we don't destroy the original
+ */
+ path = bstrdup(B_L, path);
+ websDecodeUrl(path, path, gstrlen(path));
+
+ len = npart = 0;
+ parts[0] = NULL;
+ token = gstrtok(path, T("/"));
+
+/*
+ * Look at each directory segment and process "." and ".." segments
+ * Don't allow the browser to pop outside the root web.
+ */
+ while (token != NULL) {
+ if (gstrcmp(token, T("..")) == 0) {
+ if (npart > 0) {
+ npart--;
+ }
+
+ } else if (gstrcmp(token, T(".")) != 0) {
+ parts[npart] = token;
+ len += gstrlen(token) + 1;
+ npart++;
+ }
+ token = gstrtok(NULL, T("/"));
+ }
+
+/*
+ * Create local path for document. Need extra space all "/" and null.
+ */
+ if (npart) {
+ lpath = balloc(B_L, (gstrlen(dir) + 1 + len + 1) * sizeof(char_t));
+ gstrcpy(lpath, dir);
+
+ for (i = 0; i < npart; i++) {
+ gstrcat(lpath, T("/"));
+ gstrcat(lpath, parts[i]);
+ }
+ websSetRequestLpath(wp, lpath);
+ bfree(B_L, path);
+ bfree(B_L, lpath);
+
+ } else {
+ bfree(B_L, path);
+ return -1;
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Do output back to the browser in the background. This is a socket
+ * write handler.
+ */
+
+static void websDefaultWriteEvent(webs_t wp)
+{
+ int len, wrote, flags, bytes, written;
+ char * buf;
+
+ a_assert(websValid(wp));
+
+ flags = websGetRequestFlags(wp);
+
+ wrote = 0;
+ bytes = 0;
+ written = websGetRequestWritten(wp);
+
+/*
+ * We only do this for non-ASP documents
+ */
+ if ( !(flags & WEBS_ASP)) {
+ bytes = websGetRequestBytes(wp);
+/*
+ * Note: websWriteBlock may return less than we wanted. It will return
+ * -1 on a socket error
+ */
+ if ((buf = balloc(B_L, PAGE_READ_BUFSIZE)) == NULL) {
+ websError(wp, 200, T("Can't get memory"));
+ }
+ else {
+ while ((len = websPageReadData(wp, buf, PAGE_READ_BUFSIZE)) > 0) {
+ if ((wrote = websWriteBlockData(wp, buf, len)) < 0) {
+ break;
+ }
+ written += wrote;
+ if (wrote != len) {
+ websPageSeek(wp, - (wrote - len));
+ break;
+ }
+ }
+/*
+ * Safety. If we are at EOF, we must be done
+ */
+ if (len == 0) {
+ a_assert(written >= bytes);
+ written = bytes;
+ }
+ bfree(B_L, buf);
+ }
+ }
+
+/*
+ * We're done if an error, or all bytes output
+ */
+ websSetRequestWritten(wp, written);
+ if (wrote < 0 || written >= bytes) {
+ websDone(wp, 200);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Closing down. Free resources.
+ */
+
+void websDefaultClose()
+{
+ if (websDefaultPage) {
+ bfree(B_L, websDefaultPage);
+ }
+ if (websDefaultDir) {
+ bfree(B_L, websDefaultDir);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Get the default page for URL requests ending in "/"
+ */
+
+char_t *websGetDefaultPage()
+{
+ return websDefaultPage;
+}
+
+/******************************************************************************/
+/*
+ * Get the default web directory
+ */
+
+char_t *websGetDefaultDir()
+{
+ return websDefaultDir;
+}
+
+/******************************************************************************/
+/*
+ * Set the default page for URL requests ending in "/"
+ */
+
+void websSetDefaultPage(char_t *page)
+{
+ a_assert(page && *page);
+
+ if (websDefaultPage) {
+ bfree(B_L, websDefaultPage);
+ }
+ websDefaultPage = bstrdup(B_L, page);
+}
+
+/******************************************************************************/
+/*
+ * Set the default web directory
+ */
+
+void websSetDefaultDir(char_t *dir)
+{
+ a_assert(dir && *dir);
+ if (websDefaultDir) {
+ bfree(B_L, websDefaultDir);
+ }
+ websDefaultDir = bstrdup(B_L, dir);
+}
+
+/******************************************************************************/
+
diff --git a/cpukit/httpd/ej.h b/cpukit/httpd/ej.h
new file mode 100644
index 0000000000..91eb72a4d2
--- /dev/null
+++ b/cpukit/httpd/ej.h
@@ -0,0 +1,226 @@
+/*
+ * ej.h -- Ejscript(TM) header
+ *
+ * Copyright (c) Go Ahead Software, Inc., 1992-1999
+ *
+ * See the file "license.txt" for information on usage and redistribution
+ */
+
+#ifndef _h_EJ
+#define _h_EJ 1
+
+/******************************** Description *********************************/
+
+/*
+ * Go Ahead Ejscript(TM) header. This defines the Ejscript API and internal
+ * structures.
+ */
+
+/********************************* Includes ***********************************/
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#ifndef CE
+ #include <fcntl.h>
+#endif
+
+#if LYNX
+ #include <unistd.h>
+#endif
+
+#ifdef QNX4
+ #include <dirent.h>
+#endif
+
+#if UEMF
+ #include "uemf.h"
+#else
+ #include <param.h>
+ #include <stat.h>
+ #include "basic/basicInternal.h"
+ #include "emf/emf.h"
+ #include "webs/webs.h"
+#endif
+
+/********************************** Defines ***********************************/
+/*
+ * Constants
+ */
+#define EJ_INC 110 /* Growth for tags/tokens */
+#define EJ_OFFSET 1 /* hAlloc doesn't like 0 entries */
+#define EJ_MAX_RECURSE 100 /* Sanity for maximum recursion */
+
+/*
+ * Ejscript Lexical analyser tokens
+ */
+#define TOK_ERR -1 /* Any error */
+#define TOK_LPAREN 1 /* ( */
+#define TOK_RPAREN 2 /* ) */
+#define TOK_IF 3 /* if */
+#define TOK_ELSE 4 /* else */
+#define TOK_LBRACE 5 /* { */
+#define TOK_RBRACE 6 /* } */
+#define TOK_LOGICAL 7 /* ||, &&, ! */
+#define TOK_EXPR 8 /* +, -, /, % */
+#define TOK_SEMI 9 /* ; */
+#define TOK_LITERAL 10 /* literal string */
+#define TOK_FUNCTION 11 /* function name */
+#define TOK_NEWLINE 12 /* newline white space */
+#define TOK_ID 13 /* function name */
+#define TOK_EOF 14 /* End of script */
+#define TOK_COMMA 15 /* Comma */
+#define TOK_VAR 16 /* var */
+#define TOK_ASSIGNMENT 17 /* = */
+#define TOK_FOR 18 /* for */
+#define TOK_INC_DEC 19 /* ++, -- */
+#define TOK_RETURN 20 /* return */
+
+/*
+ * Expression operators
+ */
+#define EXPR_LESS 1 /* < */
+#define EXPR_LESSEQ 2 /* <= */
+#define EXPR_GREATER 3 /* > */
+#define EXPR_GREATEREQ 4 /* >= */
+#define EXPR_EQ 5 /* == */
+#define EXPR_NOTEQ 6 /* != */
+#define EXPR_PLUS 7 /* + */
+#define EXPR_MINUS 8 /* - */
+#define EXPR_DIV 9 /* / */
+#define EXPR_MOD 10 /* % */
+#define EXPR_LSHIFT 11 /* << */
+#define EXPR_RSHIFT 12 /* >> */
+#define EXPR_MUL 13 /* * */
+#define EXPR_ASSIGNMENT 14 /* = */
+#define EXPR_INC 15 /* ++ */
+#define EXPR_DEC 16 /* -- */
+
+/*
+ * Conditional operators
+ */
+#define COND_AND 1 /* && */
+#define COND_OR 2 /* || */
+#define COND_NOT 3 /* ! */
+
+/*
+ * States
+ */
+#define STATE_ERR -1 /* Error state */
+#define STATE_EOF 1 /* End of file */
+#define STATE_COND 2 /* Parsing a "(conditional)" stmt */
+#define STATE_COND_DONE 3
+#define STATE_RELEXP 4 /* Parsing a relational expr */
+#define STATE_RELEXP_DONE 5
+#define STATE_EXPR 6 /* Parsing an expression */
+#define STATE_EXPR_DONE 7
+#define STATE_STMT 8 /* Parsing General statement */
+#define STATE_STMT_DONE 9
+#define STATE_STMT_BLOCK_DONE 10 /* End of block "}" */
+#define STATE_ARG_LIST 11 /* Function arg list */
+#define STATE_ARG_LIST_DONE 12
+#define STATE_DEC_LIST 16 /* Declaration list */
+#define STATE_DEC_LIST_DONE 17
+#define STATE_DEC 18
+#define STATE_DEC_DONE 19
+#define STATE_BEGIN STATE_STMT
+
+/*
+ * Flags. Used in ej_t and as parameter to parse()
+ */
+#define FLAGS_EXE 0x1 /* Execute statements */
+#define FLAGS_VARIABLES 0x2 /* Allocated variables store */
+#define FLAGS_FUNCTIONS 0x4 /* Allocated function store */
+
+/*
+ * Function call structure
+ */
+typedef struct {
+ char_t *fname; /* Function name */
+ char_t **args; /* Args for function (halloc) */
+ int nArgs; /* Number of args */
+} ejfunc_t;
+
+/*
+ * EJ evaluation block structure
+ */
+typedef struct ejEval {
+ ringq_t tokbuf; /* Current token */
+ ringq_t script; /* Input script for parsing */
+ char_t *putBackToken; /* Putback token string */
+ int putBackTokenId; /* Putback token ID */
+ char_t *line; /* Current line */
+ int lineLength; /* Current line length */
+ int lineNumber; /* Parse line number */
+ int lineColumn; /* Column in line */
+} ejinput_t;
+
+/*
+ * Per Ejscript session structure
+ */
+typedef struct ej {
+ ejinput_t *input; /* Input evaluation block */
+ sym_fd_t functions; /* Symbol table for functions */
+ sym_fd_t *variables; /* hAlloc list of variables */
+ int variableMax; /* Number of entries */
+ ejfunc_t *func; /* Current function */
+ char_t *result; /* Current expression result */
+ char_t *error; /* Error message */
+ char_t *token; /* Pointer to token string */
+ int tid; /* Current token id */
+ int eid; /* Halloc handle */
+ int flags; /* Flags */
+ int userHandle; /* User defined handle */
+} ej_t;
+
+/******************************** Prototypes **********************************/
+
+extern int ejOpenEngine(sym_fd_t variables, sym_fd_t functions);
+extern void ejCloseEngine(int eid);
+extern int ejOpenBlock(int eid);
+extern int ejCloseBlock(int eid, int vid);
+extern char_t *ejEval(int eid, char_t *script, char_t **emsg);
+extern char_t *ejEvalBlock(int eid, char_t *script, char_t **emsg);
+extern char_t *ejEvalFile(int eid, char_t *path, char_t **emsg);
+extern int ejSetGlobalFunction(int eid, char_t *name,
+ int (*fn)(int eid, void *handle, int argc, char_t **argv));
+extern void *ejGetGlobalFunction(int eid, char_t *name);
+extern int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name,
+ int (*fn)(int eid, void *handle, int argc, char_t **argv));
+extern int ejArgs(int argc, char_t **argv, char_t *fmt, ...);
+extern void ejError(ej_t* ep, char_t* fmt, ...);
+extern void ejSetUserHandle(int eid, int handle);
+extern int ejGetUserHandle(int eid);
+extern int ejGetLineNumber(int eid);
+extern void ejSetResult(int eid, char_t *s);
+extern char_t *ejGetResult(int eid);
+extern void ejSetVar(int eid, char_t *var, char_t *value);
+extern void ejSetLocalVar(int eid, char_t *var, char_t *value);
+extern int ejGetVar(int eid, char_t *var, char_t **value);
+extern void ejSetGlobalVar(int eid, char_t *var, char_t *value);
+
+extern int ejLexOpen(ej_t* ep);
+extern void ejLexClose(ej_t* ep);
+extern int ejLexOpenScript(ej_t* ep, char_t *script);
+extern void ejLexCloseScript(ej_t* ep);
+extern void ejLexSaveInputState(ej_t* ep, ejinput_t* state);
+extern void ejLexFreeInputState(ej_t* ep, ejinput_t* state);
+extern void ejLexRestoreInputState(ej_t* ep, ejinput_t* state);
+extern int ejLexGetToken(ej_t* ep, int state);
+extern void ejLexPutbackToken(ej_t* ep, int tid, char_t *string);
+
+extern sym_fd_t ejGetVariableTable(int eid);
+extern sym_fd_t ejGetFunctionTable(int eid);
+
+extern int ejEmfOpen(int eid);
+extern void ejEmfClose(int eid);
+
+extern int ejEmfDbRead(int eid, void *handle, int argc, char_t **argv);
+extern int ejEmfDbReadKeyed(int eid, void *handle, int argc, char_t **argv);
+extern int ejEmfDbTableGetNrow(int eid, void *handle, int argc, char_t **argv);
+extern int ejEmfTrace(int eid, void *handle, int argc, char_t **argv);
+extern int ejEmfDbWrite(int eid, void *handle, int argc, char_t **argv);
+
+#endif /* _h_EJ */
+
+/*****************************************************************************/
diff --git a/cpukit/httpd/ejlex.c b/cpukit/httpd/ejlex.c
new file mode 100644
index 0000000000..67e1504bd7
--- /dev/null
+++ b/cpukit/httpd/ejlex.c
@@ -0,0 +1,679 @@
+/*
+ * ejlex.c -- Ejscript(TM) Lexical Analyser
+ *
+ * Copyright (c) Go Ahead Software, Inc., 1995-1999
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * Ejscript lexical analyser. This implementes a lexical analyser for a
+ * a subset of the JavaScript language.
+ */
+
+/********************************** Includes **********************************/
+
+#include "ej.h"
+
+#if UEMF
+ #include "uemf.h"
+#else
+ #include "basic/basicInternal.h"
+#endif
+
+/****************************** Forward Declarations **************************/
+
+static int getLexicalToken(ej_t* ep, int state);
+static int tokenAddChar(ej_t *ep, int c);
+static int inputGetc(ej_t* ep);
+static void inputPutback(ej_t* ep, int c);
+
+/************************************* Code ***********************************/
+/*
+ * Setup the lexical analyser
+ */
+
+int ejLexOpen(ej_t* ep)
+{
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Close the lexicial analyser
+ */
+
+void ejLexClose(ej_t* ep)
+{
+}
+
+/******************************************************************************/
+/*
+ * Open a new input script
+ */
+
+int ejLexOpenScript(ej_t* ep, char_t *script)
+{
+ ejinput_t *ip;
+
+ a_assert(ep);
+ a_assert(script);
+
+ if ((ep->input = balloc(B_L, sizeof(ejinput_t))) == NULL) {
+ return -1;
+ }
+ ip = ep->input;
+ memset(ip, 0, sizeof(*ip));
+
+ a_assert(ip);
+ a_assert(ip->putBackToken == NULL);
+ a_assert(ip->putBackTokenId == 0);
+
+/*
+ * Create the parse token buffer and script buffer
+ */
+ if (ringqOpen(&ip->tokbuf, EJ_INC, -1) < 0) {
+ return -1;
+ }
+ if (ringqOpen(&ip->script, EJ_INC, -1) < 0) {
+ return -1;
+ }
+/*
+ * Put the Ejscript into a ring queue for easy parsing
+ */
+ ringqPutstr(&ip->script, script);
+
+ ip->lineNumber = 1;
+ ip->lineLength = 0;
+ ip->lineColumn = 0;
+ ip->line = NULL;
+
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Close the input script
+ */
+
+void ejLexCloseScript(ej_t* ep)
+{
+ ejinput_t *ip;
+
+ a_assert(ep);
+
+ ip = ep->input;
+ a_assert(ip);
+
+ if (ip->putBackToken) {
+ bfree(B_L, ip->putBackToken);
+ ip->putBackToken = NULL;
+ }
+ ip->putBackTokenId = 0;
+
+ if (ip->line) {
+ bfree(B_L, ip->line);
+ ip->line = NULL;
+ }
+
+ ringqClose(&ip->tokbuf);
+ ringqClose(&ip->script);
+
+ bfree(B_L, ip);
+}
+
+/******************************************************************************/
+/*
+ * Save the input state
+ */
+
+void ejLexSaveInputState(ej_t* ep, ejinput_t* state)
+{
+ ejinput_t *ip;
+
+ a_assert(ep);
+
+ ip = ep->input;
+ a_assert(ip);
+
+ *state = *ip;
+ if (ip->putBackToken) {
+ state->putBackToken = bstrdup(B_L, ip->putBackToken);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Restore the input state
+ */
+
+void ejLexRestoreInputState(ej_t* ep, ejinput_t* state)
+{
+ ejinput_t *ip;
+
+ a_assert(ep);
+
+ ip = ep->input;
+ a_assert(ip);
+
+ ip->tokbuf = state->tokbuf;
+ ip->script = state->script;
+ ip->putBackTokenId = state->putBackTokenId;
+ if (ip->putBackToken) {
+ bfree(B_L, ip->putBackToken);
+ }
+ if (state->putBackToken) {
+ ip->putBackToken = bstrdup(B_L, state->putBackToken);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Free a saved input state
+ */
+
+void ejLexFreeInputState(ej_t* ep, ejinput_t* state)
+{
+ if (state->putBackToken) {
+ bfree(B_L, state->putBackToken);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Get the next Ejscript token
+ */
+
+int ejLexGetToken(ej_t* ep, int state)
+{
+ ep->tid = getLexicalToken(ep, state);
+ trace(7, T("ejGetToken: %d, \"%s\"\n"), ep->tid, ep->token);
+ return ep->tid;
+}
+
+/******************************************************************************/
+/*
+ * Get the next Ejscript token
+ */
+
+static int getLexicalToken(ej_t* ep, int state)
+{
+ ringq_t *inq, *tokq;
+ ejinput_t* ip;
+ int done, tid, c, quote, style, back_quoted, lval, i;
+
+ a_assert(ep);
+ ip = ep->input;
+ a_assert(ip);
+
+ inq = &ip->script;
+ tokq = &ip->tokbuf;
+
+ ep->tid = -1;
+ tid = -1;
+ ep->token = T("");
+
+ ringqFlush(tokq);
+
+ if (ip->putBackTokenId > 0) {
+ ringqPutstr(tokq, ip->putBackToken);
+ tid = ip->putBackTokenId;
+ ip->putBackTokenId = 0;
+ ep->token = (char_t*) tokq->servp;
+ return tid;
+ }
+
+ if ((c = inputGetc(ep)) < 0) {
+ return TOK_EOF;
+ }
+
+ for (done = 0; !done; ) {
+ switch (c) {
+ case -1:
+ return TOK_EOF;
+
+ case ' ':
+ case '\t':
+ case '\r':
+ do {
+ if ((c = inputGetc(ep)) < 0)
+ break;
+ } while (c == ' ' || c == '\t' || c == '\r');
+ break;
+
+ case '\n':
+ return TOK_NEWLINE;
+
+ case '(':
+ tokenAddChar(ep, c);
+ return TOK_LPAREN;
+
+ case ')':
+ tokenAddChar(ep, c);
+ return TOK_RPAREN;
+
+ case '{':
+ tokenAddChar(ep, c);
+ return TOK_LBRACE;
+
+ case '}':
+ tokenAddChar(ep, c);
+ return TOK_RBRACE;
+
+ case '+':
+ if ((c = inputGetc(ep)) < 0) {
+ ejError(ep, T("Syntax Error"));
+ return TOK_ERR;
+ }
+ if (c != '+' ) {
+ inputPutback(ep, c);
+ tokenAddChar(ep, EXPR_PLUS);
+ return TOK_EXPR;
+ }
+ tokenAddChar(ep, EXPR_INC);
+ return TOK_INC_DEC;
+
+ case '-':
+ if ((c = inputGetc(ep)) < 0) {
+ ejError(ep, T("Syntax Error"));
+ return TOK_ERR;
+ }
+ if (c != '-' ) {
+ inputPutback(ep, c);
+ tokenAddChar(ep, EXPR_MINUS);
+ return TOK_EXPR;
+ }
+ tokenAddChar(ep, EXPR_DEC);
+ return TOK_INC_DEC;
+
+ case '*':
+ tokenAddChar(ep, EXPR_MUL);
+ return TOK_EXPR;
+
+ case '%':
+ tokenAddChar(ep, EXPR_MOD);
+ return TOK_EXPR;
+
+ case '/':
+/*
+ * Handle the division operator and comments
+ */
+ if ((c = inputGetc(ep)) < 0) {
+ ejError(ep, T("Syntax Error"));
+ return TOK_ERR;
+ }
+ if (c != '*' && c != '/') {
+ inputPutback(ep, c);
+ tokenAddChar(ep, EXPR_DIV);
+ return TOK_EXPR;
+ }
+ style = c;
+/*
+ * Eat comments. Both C and C++ comment styles are supported.
+ */
+ while (1) {
+ if ((c = inputGetc(ep)) < 0) {
+ ejError(ep, T("Syntax Error"));
+ return TOK_ERR;
+ }
+ if (c == '\n' && style == '/') {
+ break;
+ } else if (c == '*') {
+ c = inputGetc(ep);
+ if (style == '/') {
+ if (c == '\n') {
+ break;
+ }
+ } else {
+ if (c == '/') {
+ break;
+ }
+ }
+ }
+ }
+/*
+ * Continue looking for a token, so get the next character
+ */
+ if ((c = inputGetc(ep)) < 0) {
+ return TOK_EOF;
+ }
+ break;
+
+ case '<': /* < and <= */
+ if ((c = inputGetc(ep)) < 0) {
+ ejError(ep, T("Syntax Error"));
+ return TOK_ERR;
+ }
+ if (c == '<') {
+ tokenAddChar(ep, EXPR_LSHIFT);
+ return TOK_EXPR;
+ } else if (c == '=') {
+ tokenAddChar(ep, EXPR_LESSEQ);
+ return TOK_EXPR;
+ }
+ tokenAddChar(ep, EXPR_LESS);
+ inputPutback(ep, c);
+ return TOK_EXPR;
+
+ case '>': /* > and >= */
+ if ((c = inputGetc(ep)) < 0) {
+ ejError(ep, T("Syntax Error"));
+ return TOK_ERR;
+ }
+ if (c == '>') {
+ tokenAddChar(ep, EXPR_RSHIFT);
+ return TOK_EXPR;
+ } else if (c == '=') {
+ tokenAddChar(ep, EXPR_GREATEREQ);
+ return TOK_EXPR;
+ }
+ tokenAddChar(ep, EXPR_GREATER);
+ inputPutback(ep, c);
+ return TOK_EXPR;
+
+ case '=': /* "==" */
+ if ((c = inputGetc(ep)) < 0) {
+ ejError(ep, T("Syntax Error"));
+ return TOK_ERR;
+ }
+ if (c == '=') {
+ tokenAddChar(ep, EXPR_EQ);
+ return TOK_EXPR;
+ }
+ inputPutback(ep, c);
+ return TOK_ASSIGNMENT;
+
+ case '!': /* "!=" */
+ if ((c = inputGetc(ep)) < 0) {
+ ejError(ep, T("Syntax Error"));
+ return TOK_ERR;
+ }
+ if (c == '=') {
+ tokenAddChar(ep, EXPR_NOTEQ);
+ return TOK_EXPR;
+ }
+ tokenAddChar(ep, COND_NOT);
+ return TOK_LOGICAL;
+
+ case ';':
+ tokenAddChar(ep, c);
+ return TOK_SEMI;
+
+ case ',':
+ tokenAddChar(ep, c);
+ return TOK_COMMA;
+
+ case '|': /* "||" */
+ if ((c = inputGetc(ep)) < 0 || c != '|') {
+ ejError(ep, T("Syntax Error"));
+ return TOK_ERR;
+ }
+ tokenAddChar(ep, COND_OR);
+ return TOK_LOGICAL;
+
+ case '&': /* "&&" */
+ if ((c = inputGetc(ep)) < 0 || c != '&') {
+ ejError(ep, T("Syntax Error"));
+ return TOK_ERR;
+ }
+ tokenAddChar(ep, COND_AND);
+ return TOK_LOGICAL;
+
+ case '\"': /* String quote */
+ case '\'':
+ quote = c;
+ if ((c = inputGetc(ep)) < 0) {
+ ejError(ep, T("Syntax Error"));
+ return TOK_ERR;
+ }
+ back_quoted = 0;
+ while (c != quote) {
+ if (c == '\\' && !back_quoted) {
+ back_quoted++;
+ } else if (back_quoted) {
+ if (gisdigit((char_t) c)) {
+ lval = 0;
+ for (i = 0; i < 3; i++) {
+ if ('0' <= c && c <= '7') {
+ break;
+ }
+ lval = lval * 8 + c;
+ if ((c = inputGetc(ep)) < 0) {
+ break;
+ }
+ }
+ c = (int) lval;
+
+ } else if (back_quoted) {
+ switch (c) {
+ case 'n':
+ c = '\n'; break;
+ case 'b':
+ c = '\b'; break;
+ case 'f':
+ c = '\f'; break;
+ case 'r':
+ c = '\r'; break;
+ case 't':
+ c = '\t'; break;
+ case 'x':
+ lval = 0;
+ for (i = 0; i < 2; i++) {
+ if (! gisxdigit((char_t) c)) {
+ break;
+ }
+ lval = lval * 16 + c;
+ if ((c = inputGetc(ep)) < 0) {
+ break;
+ }
+ }
+ c = (int) lval;
+ break;
+ case 'u':
+ lval = 0;
+ for (i = 0; i < 4; i++) {
+ if (! gisxdigit((char_t) c)) {
+ break;
+ }
+ lval = lval * 16 + c;
+ if ((c = inputGetc(ep)) < 0) {
+ break;
+ }
+ }
+ c = (int) lval;
+ break;
+ case '\'':
+ case '\"':
+ break;
+ }
+ }
+ back_quoted = 0;
+ if (tokenAddChar(ep, c) < 0) {
+ return TOK_ERR;
+ }
+ } else {
+ if (tokenAddChar(ep, c) < 0) {
+ return TOK_ERR;
+ }
+ }
+ if ((c = inputGetc(ep)) < 0) {
+ ejError(ep, T("Unmatched Quote"));
+ return TOK_ERR;
+ }
+ }
+ return TOK_LITERAL;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ do {
+ if (tokenAddChar(ep, c) < 0) {
+ return TOK_ERR;
+ }
+ if ((c = inputGetc(ep)) < 0)
+ break;
+ } while (gisdigit((char_t) c));
+ inputPutback(ep, c);
+ return TOK_LITERAL;
+
+ default:
+/*
+ * Identifiers or a function names
+ */
+ back_quoted = 0;
+ while (1) {
+ if (c == '\\' && !back_quoted) {
+ back_quoted++;
+ } else {
+ back_quoted = 0;
+ if (tokenAddChar(ep, c) < 0) {
+ break;
+ }
+ }
+ if ((c = inputGetc(ep)) < 0) {
+ break;
+ }
+ if (!back_quoted && (!gisalnum((char_t) c) && c != '$' &&
+ c != '_')) {
+ break;
+ }
+ }
+ if (! gisalpha(*tokq->servp) && *tokq->servp != '$' &&
+ *tokq->servp != '_') {
+ ejError(ep, T("Invalid identifier %s"), tokq->servp);
+ return TOK_ERR;
+ }
+/*
+ * Check for reserved words (only "if", "else", "var", "for"
+ * and "return" at the moment)
+ */
+ if (state == STATE_STMT) {
+ if (gstrcmp(ep->token, T("if")) == 0) {
+ return TOK_IF;
+ } else if (gstrcmp(ep->token, T("else")) == 0) {
+ return TOK_ELSE;
+ } else if (gstrcmp(ep->token, T("var")) == 0) {
+ return TOK_VAR;
+ } else if (gstrcmp(ep->token, T("for")) == 0) {
+ return TOK_FOR;
+ } else if (gstrcmp(ep->token, T("return")) == 0) {
+ return TOK_RETURN;
+ }
+ }
+
+/*
+ * skip white space after token to find out whether this is
+ * a function or not.
+ */
+ while (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
+ if ((c = inputGetc(ep)) < 0)
+ break;
+ }
+
+ tid = (c == '(') ? TOK_FUNCTION : TOK_ID;
+ done++;
+ }
+ }
+
+/*
+ * Putback the last extra character for next time
+ */
+ inputPutback(ep, c);
+ return tid;
+}
+
+/******************************************************************************/
+/*
+ * Putback the last token read
+ */
+
+void ejLexPutbackToken(ej_t* ep, int tid, char_t *string)
+{
+ ejinput_t* ip;
+
+ a_assert(ep);
+ ip = ep->input;
+ a_assert(ip);
+
+ if (ip->putBackToken) {
+ bfree(B_L, ip->putBackToken);
+ }
+ ip->putBackTokenId = tid;
+ ip->putBackToken = bstrdup(B_L, string);
+}
+
+/******************************************************************************/
+/*
+ * Add a character to the token ringq buffer
+ */
+
+static int tokenAddChar(ej_t *ep, int c)
+{
+ ejinput_t* ip;
+
+ a_assert(ep);
+ ip = ep->input;
+ a_assert(ip);
+
+ if (ringqPutc(&ip->tokbuf, (char_t) c) < 0) {
+ ejError(ep, T("Token too big"));
+ return -1;
+ }
+ * ((char_t*) ip->tokbuf.endp) = '\0';
+ ep->token = (char_t*) ip->tokbuf.servp;
+
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Get another input character
+ */
+
+static int inputGetc(ej_t* ep)
+{
+ ejinput_t *ip;
+ int c, len;
+
+ a_assert(ep);
+ ip = ep->input;
+
+ if ((len = ringqLen(&ip->script)) == 0) {
+ return -1;
+ }
+
+ c = ringqGetc(&ip->script);
+
+ if (c == '\n') {
+ ip->lineNumber++;
+ ip->lineColumn = 0;
+ } else {
+ if ((ip->lineColumn + 2) >= ip->lineLength) {
+ ip->lineLength += EJ_INC;
+ ip->line = brealloc(B_L, ip->line, ip->lineLength * sizeof(char_t));
+ }
+ ip->line[ip->lineColumn++] = c;
+ ip->line[ip->lineColumn] = '\0';
+ }
+ return c;
+}
+
+/******************************************************************************/
+/*
+ * Putback a character onto the input queue
+ */
+
+static void inputPutback(ej_t* ep, int c)
+{
+ ejinput_t *ip;
+
+ a_assert(ep);
+
+ ip = ep->input;
+ ringqInsertc(&ip->script, (char_t) c);
+ ip->lineColumn--;
+ ip->line[ip->lineColumn] = '\0';
+}
+
+/******************************************************************************/
diff --git a/cpukit/httpd/ejparse.c b/cpukit/httpd/ejparse.c
new file mode 100644
index 0000000000..1734f6f719
--- /dev/null
+++ b/cpukit/httpd/ejparse.c
@@ -0,0 +1,1665 @@
+/*
+ * ejparse.c -- Ejscript(TM) Parser
+ *
+ * Copyright (c) Go Ahead Software, Inc., 1995-1999
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * Ejscript parser. This implementes a subset of the JavaScript language.
+ * Multiple Ejscript parsers can be opened at a time.
+ */
+
+/********************************** Includes **********************************/
+
+#include "ej.h"
+
+/********************************** Local Data ********************************/
+
+ej_t **ejHandles; /* List of ej handles */
+int ejMax = -1; /* Maximum size of */
+
+/****************************** Forward Declarations **************************/
+
+static ej_t *ejPtr(int eid);
+static void clearString(char_t **ptr);
+static void setString(char_t **ptr, char_t *s);
+static void appendString(char_t **ptr, char_t *s);
+static void freeVar(sym_t* sp);
+static int parse(ej_t *ep, int state, int flags);
+static int parseStmt(ej_t *ep, int state, int flags);
+static int parseDeclaration(ej_t *ep, int state, int flags);
+static int parseArgs(ej_t *ep, int state, int flags);
+static int parseCond(ej_t *ep, int state, int flags);
+static int parseExpr(ej_t *ep, int state, int flags);
+static int evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs);
+static int evalCond(ej_t *ep, char_t *lhs, int rel, char_t *rhs);
+static int evalFunction(ej_t *ep);
+static void freeFunc(ejfunc_t *func);
+
+/************************************* Code ***********************************/
+/*
+ * Initialize a Ejscript engine
+ */
+
+int ejOpenEngine(sym_fd_t variables, sym_fd_t functions)
+{
+ ej_t *ep;
+ int eid, vid;
+
+ if ((eid = hAllocEntry((void***) &ejHandles, &ejMax, sizeof(ej_t))) < 0) {
+ return -1;
+ }
+ ep = ejHandles[eid];
+ ep->eid = eid;
+
+/*
+ * Create a top level symbol table if one is not provided for variables and
+ * functions. Variables may create other symbol tables for block level
+ * declarations so we use hAlloc to manage a list of variable tables.
+ */
+ if ((vid = hAlloc((void***) &ep->variables)) < 0) {
+ ejMax = hFree((void***) &ejHandles, ep->eid);
+ return -1;
+ }
+ if (vid >= ep->variableMax) {
+ ep->variableMax = vid + 1;
+ }
+
+ if (variables == -1) {
+ ep->variables[vid] = symOpen(64) + EJ_OFFSET;
+ ep->flags |= FLAGS_VARIABLES;
+
+ } else {
+ ep->variables[vid] = variables + EJ_OFFSET;
+ }
+
+ if (functions == -1) {
+ ep->functions = symOpen(64);
+ ep->flags |= FLAGS_FUNCTIONS;
+ } else {
+ ep->functions = functions;
+ }
+
+ ejLexOpen(ep);
+
+/*
+ * Define standard constants
+ */
+ ejSetGlobalVar(ep->eid, T("null"), NULL);
+
+#if EMF
+ ejEmfOpen(ep->eid);
+#endif
+ return ep->eid;
+}
+
+/******************************************************************************/
+/*
+ * Close
+ */
+
+void ejCloseEngine(int eid)
+{
+ ej_t *ep;
+ int i;
+
+ if ((ep = ejPtr(eid)) == NULL) {
+ return;
+ }
+
+#if EMF
+ ejEmfClose(eid);
+#endif
+
+ bfreeSafe(B_L, ep->error);
+ ep->error = NULL;
+ bfreeSafe(B_L, ep->result);
+ ep->result = NULL;
+
+ ejLexClose(ep);
+
+ if (ep->flags & FLAGS_VARIABLES) {
+ for (i = ep->variableMax - 1; i >= 0; i--) {
+ symClose(ep->variables[i] - EJ_OFFSET, freeVar);
+ ep->variableMax = hFree((void***) &ep->variables, i);
+ }
+ }
+ if (ep->flags & FLAGS_FUNCTIONS) {
+ symClose(ep->functions, freeVar);
+ }
+
+ ejMax = hFree((void***) &ejHandles, ep->eid);
+ bfree(B_L, ep);
+}
+
+/******************************************************************************/
+/*
+ * Callback from symClose. Free the variable.
+ */
+
+static void freeVar(sym_t* sp)
+{
+ valueFree(&sp->content);
+}
+
+/******************************************************************************/
+/*
+ * Evaluate a Ejscript file
+ */
+
+#if DEV
+char_t *ejEvalFile(int eid, char_t *path, char_t **emsg)
+{
+ gstat_t sbuf;
+ ej_t *ep;
+ char_t *script, *rs;
+ char *fileBuf;
+ int fd;
+
+ a_assert(path && *path);
+
+ if (emsg) {
+ *emsg = NULL;
+ }
+ if ((ep = ejPtr(eid)) == NULL) {
+ return NULL;
+ }
+
+ if ((fd = gopen(path, O_RDONLY | O_BINARY, 0666)) < 0) {
+ ejError(ep, T("Bad handle %d"), eid);
+ return NULL;
+ }
+ if (gstat(path, &sbuf) < 0) {
+ close(fd);
+ ejError(ep, T("Cant stat %s"), path);
+ return NULL;
+ }
+ if ((fileBuf = balloc(B_L, sbuf.st_size + 1)) == NULL) {
+ close(fd);
+ ejError(ep, T("Cant malloc %d"), sbuf.st_size);
+ return NULL;
+ }
+ if (read(fd, fileBuf, sbuf.st_size) != (int)sbuf.st_size) {
+ close(fd);
+ bfree(B_L, fileBuf);
+ ejError(ep, T("Error reading %s"), path);
+ return NULL;
+ }
+ fileBuf[sbuf.st_size] = '\0';
+ close(fd);
+
+ if ((script = ballocAscToUni(fileBuf)) == NULL) {
+ bfree(B_L, fileBuf);
+ ejError(ep, T("Cant malloc %d"), sbuf.st_size + 1);
+ return NULL;
+ }
+ bfree(B_L, fileBuf);
+
+ rs = ejEvalBlock(eid, script, emsg);
+
+ bfree(B_L, script);
+ return rs;
+}
+#endif
+
+/******************************************************************************/
+/*
+ * Create a new variable scope block so that consecutive ejEval calls may
+ * be made with the same varible scope. This space MUST be closed with
+ * ejCloseBlock when the evaluations are complete.
+ */
+
+int ejOpenBlock(int eid)
+{
+ ej_t *ep;
+ int vid;
+
+ if((ep = ejPtr(eid)) == NULL) {
+ return -1;
+ }
+ if ((vid = hAlloc((void***) &ep->variables)) < 0) {
+ return -1;
+ }
+ if (vid >= ep->variableMax) {
+ ep->variableMax = vid + 1;
+ }
+ ep->variables[vid] = symOpen(64) + EJ_OFFSET;
+ return vid;
+
+}
+
+/******************************************************************************/
+/*
+ * Close a variable scope block. The vid parameter is the return value from
+ * the call to ejOpenBlock
+ */
+
+int ejCloseBlock(int eid, int vid)
+{
+ ej_t *ep;
+
+ if((ep = ejPtr(eid)) == NULL) {
+ return -1;
+ }
+ symClose(ep->variables[vid] - EJ_OFFSET, freeVar);
+ ep->variableMax = hFree((void***) &ep->variables, vid);
+ return 0;
+
+}
+/******************************************************************************/
+/*
+ * Create a new variable scope block and evaluate a script. All variables
+ * created during this context will be automatically deleted when complete.
+ */
+
+char_t *ejEvalBlock(int eid, char_t *script, char_t **emsg)
+{
+ char_t* returnVal;
+ int vid;
+
+ a_assert(script);
+
+ vid = ejOpenBlock(eid);
+ returnVal = ejEval(eid, script, emsg);
+ ejCloseBlock(eid, vid);
+
+ return returnVal;
+}
+
+/******************************************************************************/
+/*
+ * Parse and evaluate a Ejscript. The caller may provide a symbol table to
+ * use for variables and function definitions. Return char_t pointer on
+ * success otherwise NULL pointer is returned.
+ */
+
+char_t *ejEval(int eid, char_t *script, char_t **emsg)
+{
+ ej_t *ep;
+ ejinput_t *oldBlock;
+ int state;
+
+ a_assert(script);
+
+ if (emsg) {
+ *emsg = NULL;
+ }
+ if ((ep = ejPtr(eid)) == NULL) {
+ return NULL;
+ }
+
+ setString(&ep->result, T(""));
+
+/*
+ * Allocate a new evaluation block, and save the old one
+ */
+ oldBlock = ep->input;
+ ejLexOpenScript(ep, script);
+
+/*
+ * Do the actual parsing and evaluation
+ */
+ do {
+ state = parse(ep, STATE_BEGIN, FLAGS_EXE);
+ } while (state != STATE_EOF && state != STATE_ERR);
+
+ ejLexCloseScript(ep);
+
+/*
+ * Return any error string to the user
+ */
+ if (state == STATE_ERR && emsg) {
+ *emsg = bstrdup(B_L, ep->error);
+ }
+
+/*
+ * Restore the old evaluation block
+ */
+ ep->input = oldBlock;
+
+ if (state == STATE_EOF) {
+ return ep->result;
+ }
+ if (state == STATE_ERR) {
+ return NULL;
+ }
+ return ep->result;
+}
+
+/******************************************************************************/
+/*
+ * Recursive descent parser for Ejscript
+ */
+
+static int parse(ej_t *ep, int state, int flags)
+{
+ a_assert(ep);
+
+ switch (state) {
+/*
+ * Any statement, function arguments or conditional expressions
+ */
+ case STATE_STMT:
+ case STATE_DEC:
+ state = parseStmt(ep, state, flags);
+ break;
+
+ case STATE_EXPR:
+ state = parseStmt(ep, state, flags);
+ break;
+
+/*
+ * Variable declaration list
+ */
+ case STATE_DEC_LIST:
+ state = parseDeclaration(ep, state, flags);
+ break;
+
+/*
+ * Function argument string
+ */
+ case STATE_ARG_LIST:
+ state = parseArgs(ep, state, flags);
+ break;
+
+/*
+ * Logical condition list (relational operations separated by &&, ||)
+ */
+ case STATE_COND:
+ state = parseCond(ep, state, flags);
+ break;
+
+/*
+ * Expression list
+ */
+ case STATE_RELEXP:
+ state = parseExpr(ep, state, flags);
+ break;
+ }
+
+ if (state == STATE_ERR && ep->error == NULL) {
+ ejError(ep, T("Syntax error"));
+ }
+ return state;
+}
+
+/******************************************************************************/
+/*
+ * Parse any statement including functions and simple relational operations
+ */
+
+static int parseStmt(ej_t *ep, int state, int flags)
+{
+ ejfunc_t func;
+ ejfunc_t *saveFunc;
+ ejinput_t condScript, endScript, bodyScript, incrScript;
+ char_t *value;
+ char_t *identifier;
+ int done, expectSemi, thenFlags, elseFlags, tid, cond, forFlags;
+
+ a_assert(ep);
+
+/*
+ * Set these to NULL, else we try to free them if an error occurs.
+ */
+ endScript.putBackToken = NULL;
+ bodyScript.putBackToken = NULL;
+ incrScript.putBackToken = NULL;
+ condScript.putBackToken = NULL;
+
+ expectSemi = 0;
+ saveFunc = NULL;
+
+ for (done = 0; !done; ) {
+ tid = ejLexGetToken(ep, state);
+
+ switch (tid) {
+ default:
+ ejLexPutbackToken(ep, TOK_EXPR, ep->token);
+ done++;
+ break;
+
+ case TOK_ERR:
+ state = STATE_ERR;
+ done++;
+ break;
+
+ case TOK_EOF:
+ state = STATE_EOF;
+ done++;
+ break;
+
+ case TOK_NEWLINE:
+ break;
+
+ case TOK_SEMI:
+/*
+ * This case is when we discover no statement and just a lone ';'
+ */
+ if (state != STATE_STMT) {
+ ejLexPutbackToken(ep, tid, ep->token);
+ }
+ done++;
+ break;
+
+ case TOK_ID:
+/*
+ * This could either be a reference to a variable or an assignment
+ */
+ identifier = NULL;
+ setString(&identifier, ep->token);
+/*
+ * Peek ahead to see if this is an assignment
+ */
+ tid = ejLexGetToken(ep, state);
+ if (tid == TOK_ASSIGNMENT) {
+ if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) {
+ goto error;
+ }
+ if (flags & FLAGS_EXE) {
+ if ( state == STATE_DEC ) {
+ ejSetLocalVar(ep->eid, identifier, ep->result);
+ }
+ else {
+ if (ejGetVar(ep->eid, identifier, &value) > 0) {
+ ejSetLocalVar(ep->eid, identifier, ep->result);
+ } else {
+ ejSetGlobalVar(ep->eid, identifier, ep->result);
+ }
+ }
+ }
+
+ } else if (tid == TOK_INC_DEC ) {
+ value = NULL;
+ if ( flags & FLAGS_EXE ) {
+ if (ejGetVar(ep->eid, identifier, &value) < 0) {
+ ejError(ep, T("Undefined variable %s\n"), identifier);
+ goto error;
+ }
+ setString(&ep->result, value);
+ if (evalExpr(ep, value, (int) *ep->token, T("1")) < 0) {
+ state = STATE_ERR;
+ break;
+ }
+ ejSetGlobalVar(ep->eid, identifier, ep->result);
+ }
+
+ } else {
+/*
+ * If we are processing a declaration, allow undefined vars
+ */
+ value = NULL;
+ if (state == STATE_DEC) {
+ if (ejGetVar(ep->eid, identifier, &value) > 0) {
+ ejError(ep, T("Variable already declared"),
+ identifier);
+ clearString(&identifier);
+ goto error;
+ }
+ ejSetLocalVar(ep->eid, identifier, NULL);
+ } else {
+ if ( flags & FLAGS_EXE ) {
+ if (ejGetVar(ep->eid, identifier, &value) < 0) {
+ ejError(ep, T("Undefined variable %s\n"),
+ identifier);
+ clearString(&identifier);
+ goto error;
+ }
+ }
+ }
+ setString(&ep->result, value);
+ ejLexPutbackToken(ep, tid, ep->token);
+ }
+ clearString(&identifier);
+
+ if (state == STATE_STMT) {
+ expectSemi++;
+ }
+ done++;
+ break;
+
+ case TOK_LITERAL:
+/*
+ * Set the result to the literal (number or string constant)
+ */
+ setString(&ep->result, ep->token);
+ if (state == STATE_STMT) {
+ expectSemi++;
+ }
+ done++;
+ break;
+
+ case TOK_FUNCTION:
+/*
+ * We must save any current ep->func value for the current stack frame
+ */
+ if (ep->func) {
+ saveFunc = ep->func;
+ }
+ memset(&func, 0, sizeof(ejfunc_t));
+ setString(&func.fname, ep->token);
+ ep->func = &func;
+
+ setString(&ep->result, T(""));
+ if (ejLexGetToken(ep, state) != TOK_LPAREN) {
+ freeFunc(&func);
+ goto error;
+ }
+
+ if (parse(ep, STATE_ARG_LIST, flags) != STATE_ARG_LIST_DONE) {
+ freeFunc(&func);
+ ep->func = saveFunc;
+ goto error;
+ }
+/*
+ * Evaluate the function if required
+ */
+ if (flags & FLAGS_EXE && evalFunction(ep) < 0) {
+ freeFunc(&func);
+ ep->func = saveFunc;
+ goto error;
+ }
+
+ freeFunc(&func);
+ ep->func = saveFunc;
+
+ if (ejLexGetToken(ep, state) != TOK_RPAREN) {
+ goto error;
+ }
+ if (state == STATE_STMT) {
+ expectSemi++;
+ }
+ done++;
+ break;
+
+ case TOK_IF:
+ if (state != STATE_STMT) {
+ goto error;
+ }
+ if (ejLexGetToken(ep, state) != TOK_LPAREN) {
+ goto error;
+ }
+/*
+ * Evaluate the entire condition list "(condition)"
+ */
+ if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) {
+ goto error;
+ }
+ if (ejLexGetToken(ep, state) != TOK_RPAREN) {
+ goto error;
+ }
+/*
+ * This is the "then" case. We need to always parse both cases and
+ * execute only the relevant case.
+ */
+ if (*ep->result == '1') {
+ thenFlags = flags;
+ elseFlags = flags & ~FLAGS_EXE;
+ } else {
+ thenFlags = flags & ~FLAGS_EXE;
+ elseFlags = flags;
+ }
+/*
+ * Process the "then" case
+ */
+ if (parse(ep, STATE_STMT, thenFlags) != STATE_STMT_DONE) {
+ goto error;
+ }
+ tid = ejLexGetToken(ep, state);
+ if (tid != TOK_ELSE) {
+ ejLexPutbackToken(ep, tid, ep->token);
+ done++;
+ break;
+ }
+/*
+ * Process the "else" case
+ */
+ if (parse(ep, STATE_STMT, elseFlags) != STATE_STMT_DONE) {
+ goto error;
+ }
+ done++;
+ break;
+
+ case TOK_FOR:
+/*
+ * Format for the expression is:
+ *
+ * for (initial; condition; incr) {
+ * body;
+ * }
+ */
+ if (state != STATE_STMT) {
+ goto error;
+ }
+ if (ejLexGetToken(ep, state) != TOK_LPAREN) {
+ goto error;
+ }
+
+/*
+ * Evaluate the for loop initialization statement
+ */
+ if (parse(ep, STATE_EXPR, flags) != STATE_EXPR_DONE) {
+ goto error;
+ }
+ if (ejLexGetToken(ep, state) != TOK_SEMI) {
+ goto error;
+ }
+
+/*
+ * The first time through, we save the current input context just
+ * to each step: prior to the conditional, the loop increment and the
+ * loop body.
+ */
+ ejLexSaveInputState(ep, &condScript);
+ if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) {
+ goto error;
+ }
+ cond = (*ep->result != '0');
+
+ if (ejLexGetToken(ep, state) != TOK_SEMI) {
+ goto error;
+ }
+
+/*
+ * Don't execute the loop increment statement or the body first time
+ */
+ forFlags = flags & ~FLAGS_EXE;
+ ejLexSaveInputState(ep, &incrScript);
+ if (parse(ep, STATE_EXPR, forFlags) != STATE_EXPR_DONE) {
+ goto error;
+ }
+ if (ejLexGetToken(ep, state) != TOK_RPAREN) {
+ goto error;
+ }
+
+/*
+ * Parse the body and remember the end of the body script
+ */
+ ejLexSaveInputState(ep, &bodyScript);
+ if (parse(ep, STATE_STMT, forFlags) != STATE_STMT_DONE) {
+ goto error;
+ }
+ ejLexSaveInputState(ep, &endScript);
+
+/*
+ * Now actually do the for loop. Note loop has been rotated
+ */
+ while (cond && (flags & FLAGS_EXE) ) {
+/*
+ * Evaluate the body
+ */
+ ejLexRestoreInputState(ep, &bodyScript);
+ if (parse(ep, STATE_STMT, flags) != STATE_STMT_DONE) {
+ goto error;
+ }
+/*
+ * Evaluate the increment script
+ */
+ ejLexRestoreInputState(ep, &incrScript);
+ if (parse(ep, STATE_EXPR, flags) != STATE_EXPR_DONE) {
+ goto error;
+ }
+/*
+ * Evaluate the condition
+ */
+ ejLexRestoreInputState(ep, &condScript);
+ if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) {
+ goto error;
+ }
+ cond = (*ep->result != '0');
+ }
+ ejLexRestoreInputState(ep, &endScript);
+ done++;
+ break;
+
+ case TOK_VAR:
+ if (parse(ep, STATE_DEC_LIST, flags) != STATE_DEC_LIST_DONE) {
+ goto error;
+ }
+ done++;
+ break;
+
+ case TOK_COMMA:
+ ejLexPutbackToken(ep, TOK_EXPR, ep->token);
+ done++;
+ break;
+
+ case TOK_LPAREN:
+ if (state == STATE_EXPR) {
+ if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) {
+ goto error;
+ }
+ if (ejLexGetToken(ep, state) != TOK_RPAREN) {
+ goto error;
+ }
+ return STATE_EXPR_DONE;
+ }
+ done++;
+ break;
+
+ case TOK_RPAREN:
+ ejLexPutbackToken(ep, tid, ep->token);
+ return STATE_EXPR_DONE;
+
+ case TOK_LBRACE:
+/*
+ * This handles any code in braces except "if () {} else {}"
+ */
+ if (state != STATE_STMT) {
+ goto error;
+ }
+
+/*
+ * Parse will return STATE_STMT_BLOCK_DONE when the RBRACE is seen
+ */
+ do {
+ state = parse(ep, STATE_STMT, flags);
+ } while (state == STATE_STMT_DONE);
+
+ if (ejLexGetToken(ep, state) != TOK_RBRACE) {
+ goto error;
+ }
+ return STATE_STMT_DONE;
+
+ case TOK_RBRACE:
+ if (state == STATE_STMT) {
+ ejLexPutbackToken(ep, tid, ep->token);
+ return STATE_STMT_BLOCK_DONE;
+ }
+ goto error;
+
+ case TOK_RETURN:
+ if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) {
+ goto error;
+ }
+ if (flags & FLAGS_EXE) {
+ while ( ejLexGetToken(ep, state) != TOK_EOF );
+ done++;
+ return STATE_EOF;
+ }
+ break;
+ }
+ }
+
+ if (expectSemi) {
+ tid = ejLexGetToken(ep, state);
+ if (tid != TOK_SEMI && tid != TOK_NEWLINE) {
+ goto error;
+ }
+
+/*
+ * Skip newline after semi-colon
+ */
+ tid = ejLexGetToken(ep, state);
+ if (tid != TOK_NEWLINE) {
+ ejLexPutbackToken(ep, tid, ep->token);
+ }
+ }
+
+/*
+ * Free resources and return the correct status
+ */
+doneParse:
+ if (tid == TOK_FOR) {
+ ejLexFreeInputState(ep, &condScript);
+ ejLexFreeInputState(ep, &incrScript);
+ ejLexFreeInputState(ep, &endScript);
+ ejLexFreeInputState(ep, &bodyScript);
+ }
+ if (state == STATE_STMT) {
+ return STATE_STMT_DONE;
+ } else if (state == STATE_DEC) {
+ return STATE_DEC_DONE;
+ } else if (state == STATE_EXPR) {
+ return STATE_EXPR_DONE;
+ } else if (state == STATE_EOF) {
+ return state;
+ } else {
+ return STATE_ERR;
+ }
+
+/*
+ * Common error exit
+ */
+error:
+ state = STATE_ERR;
+ goto doneParse;
+}
+
+/******************************************************************************/
+/*
+ * Parse variable declaration list
+ */
+
+static int parseDeclaration(ej_t *ep, int state, int flags)
+{
+ int tid;
+
+ a_assert(ep);
+
+/*
+ * Declarations can be of the following forms:
+ * var x;
+ * var x, y, z;
+ * var x = 1 + 2 / 3, y = 2 + 4;
+ *
+ * We set the variable to NULL if there is no associated assignment.
+ */
+
+ do {
+ if ((tid = ejLexGetToken(ep, state)) != TOK_ID) {
+ return STATE_ERR;
+ }
+ ejLexPutbackToken(ep, tid, ep->token);
+
+/*
+ * Parse the entire assignment or simple identifier declaration
+ */
+ if (parse(ep, STATE_DEC, flags) != STATE_DEC_DONE) {
+ return STATE_ERR;
+ }
+
+/*
+ * Peek at the next token, continue if comma seen
+ */
+ tid = ejLexGetToken(ep, state);
+ if (tid == TOK_SEMI) {
+ return STATE_DEC_LIST_DONE;
+ } else if (tid != TOK_COMMA) {
+ return STATE_ERR;
+ }
+ } while (tid == TOK_COMMA);
+
+ if (tid != TOK_SEMI) {
+ return STATE_ERR;
+ }
+ return STATE_DEC_LIST_DONE;
+}
+
+/******************************************************************************/
+/*
+ * Parse function arguments
+ */
+
+static int parseArgs(ej_t *ep, int state, int flags)
+{
+ int tid, aid;
+
+ a_assert(ep);
+
+ do {
+ state = parse(ep, STATE_RELEXP, flags);
+ if (state == STATE_EOF || state == STATE_ERR) {
+ return state;
+ }
+ if (state == STATE_RELEXP_DONE) {
+ aid = hAlloc((void***) &ep->func->args);
+ ep->func->args[aid] = bstrdup(B_L, ep->result);
+ ep->func->nArgs++;
+ }
+/*
+ * Peek at the next token, continue if more args (ie. comma seen)
+ */
+ tid = ejLexGetToken(ep, state);
+ if (tid != TOK_COMMA) {
+ ejLexPutbackToken(ep, tid, ep->token);
+ }
+ } while (tid == TOK_COMMA);
+
+ if (tid != TOK_RPAREN && state != STATE_RELEXP_DONE) {
+ return STATE_ERR;
+ }
+ return STATE_ARG_LIST_DONE;
+}
+
+/******************************************************************************/
+/*
+ * Parse conditional expression (relational ops separated by ||, &&)
+ */
+
+static int parseCond(ej_t *ep, int state, int flags)
+{
+ char_t *lhs, *rhs;
+ int tid, operator;
+
+ a_assert(ep);
+
+ setString(&ep->result, T(""));
+ rhs = lhs = NULL;
+ operator = 0;
+
+ do {
+/*
+ * Recurse to handle one side of a conditional. Accumulate the
+ * left hand side and the final result in ep->result.
+ */
+ state = parse(ep, STATE_RELEXP, flags);
+ if (state != STATE_RELEXP_DONE) {
+ state = STATE_ERR;
+ break;
+ }
+ if (operator > 0) {
+ setString(&rhs, ep->result);
+ if (evalCond(ep, lhs, operator, rhs) < 0) {
+ state = STATE_ERR;
+ break;
+ }
+ }
+ setString(&lhs, ep->result);
+
+ tid = ejLexGetToken(ep, state);
+ if (tid == TOK_LOGICAL) {
+ operator = (int) *ep->token;
+
+ } else if (tid == TOK_RPAREN || tid == TOK_SEMI) {
+ ejLexPutbackToken(ep, tid, ep->token);
+ state = STATE_COND_DONE;
+ break;
+
+ } else {
+ ejLexPutbackToken(ep, tid, ep->token);
+ }
+
+ } while (state == STATE_RELEXP_DONE);
+
+ if (lhs) {
+ bfree(B_L, lhs);
+ }
+ if (rhs) {
+ bfree(B_L, rhs);
+ }
+ return state;
+}
+
+/******************************************************************************/
+/*
+ * Parse expression (leftHandSide operator rightHandSide)
+ */
+
+static int parseExpr(ej_t *ep, int state, int flags)
+{
+ char_t *lhs, *rhs;
+ int rel, tid;
+
+ a_assert(ep);
+
+ setString(&ep->result, T(""));
+ rhs = lhs = NULL;
+ rel = 0;
+
+ do {
+/*
+ * This loop will handle an entire expression list. We call parse
+ * to evalutate each term which returns the result in ep->result.
+ */
+ state = parse(ep, STATE_EXPR, flags);
+ if (state != STATE_EXPR_DONE) {
+ state = STATE_ERR;
+ break;
+ }
+ if (rel > 0) {
+ setString(&rhs, ep->result);
+ if (evalExpr(ep, lhs, rel, rhs) < 0) {
+ state = STATE_ERR;
+ break;
+ }
+ }
+ setString(&lhs, ep->result);
+
+ if ((tid = ejLexGetToken(ep, state)) == TOK_EXPR) {
+ rel = (int) *ep->token;
+
+ } else if (tid == TOK_INC_DEC) {
+ rel = (int) *ep->token;
+
+ } else {
+ ejLexPutbackToken(ep, tid, ep->token);
+ state = STATE_RELEXP_DONE;
+ }
+
+ } while (state == STATE_EXPR_DONE);
+
+ if (rhs)
+ bfree(B_L, rhs);
+ if (lhs)
+ bfree(B_L, lhs);
+ return state;
+}
+
+/******************************************************************************/
+/*
+ * Evaluate a condition. Implements &&, ||, !
+ */
+
+static int evalCond(ej_t *ep, char_t *lhs, int rel, char_t *rhs)
+{
+ char_t buf[16];
+ int l, r, lval;
+
+ a_assert(lhs);
+ a_assert(rhs);
+ a_assert(rel > 0);
+
+ lval = 0;
+ if (gisdigit(*lhs) && gisdigit(*rhs)) {
+ l = gatoi(lhs);
+ r = gatoi(rhs);
+ switch (rel) {
+ case COND_AND:
+ lval = l && r;
+ break;
+ case COND_OR:
+ lval = l || r;
+ break;
+ default:
+ ejError(ep, T("Bad operator %d"), rel);
+ return -1;
+ }
+ } else {
+ if (!gisdigit(*lhs)) {
+ ejError(ep, T("Conditional must be numeric"), lhs);
+ } else {
+ ejError(ep, T("Conditional must be numeric"), rhs);
+ }
+ }
+
+ stritoa(lval, buf, sizeof(buf));
+ setString(&ep->result, buf);
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Evaluate an operation
+ */
+
+static int evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs)
+{
+ char_t *cp, buf[16];
+ int numeric, l, r, lval;
+
+ a_assert(lhs);
+ a_assert(rhs);
+ a_assert(rel > 0);
+
+/*
+ * All of the characters in the lhs and rhs must be numeric
+ */
+ numeric = 1;
+ for (cp = lhs; *cp; cp++) {
+ if (!gisdigit(*cp)) {
+ numeric = 0;
+ break;
+ }
+ }
+ if (numeric) {
+ for (cp = rhs; *cp; cp++) {
+ if (!gisdigit(*cp)) {
+ numeric = 0;
+ break;
+ }
+ }
+ }
+ if (numeric) {
+ l = gatoi(lhs);
+ r = gatoi(rhs);
+ switch (rel) {
+ case EXPR_PLUS:
+ lval = l + r;
+ break;
+ case EXPR_INC:
+ lval = l + 1;
+ break;
+ case EXPR_MINUS:
+ lval = l - r;
+ break;
+ case EXPR_DEC:
+ lval = l - 1;
+ break;
+ case EXPR_MUL:
+ lval = l * r;
+ break;
+ case EXPR_DIV:
+ if (r != 0) {
+ lval = l / r;
+ } else {
+ lval = 0;
+ }
+ break;
+ case EXPR_MOD:
+ if (r != 0) {
+ lval = l % r;
+ } else {
+ lval = 0;
+ }
+ break;
+ case EXPR_LSHIFT:
+ lval = l << r;
+ break;
+ case EXPR_RSHIFT:
+ lval = l >> r;
+ break;
+ case EXPR_EQ:
+ lval = l == r;
+ break;
+ case EXPR_NOTEQ:
+ lval = l != r;
+ break;
+ case EXPR_LESS:
+ lval = (l < r) ? 1 : 0;
+ break;
+ case EXPR_LESSEQ:
+ lval = (l <= r) ? 1 : 0;
+ break;
+ case EXPR_GREATER:
+ lval = (l > r) ? 1 : 0;
+ break;
+ case EXPR_GREATEREQ:
+ lval = (l >= r) ? 1 : 0;
+ break;
+ default:
+ ejError(ep, T("Bad operator %d"), rel);
+ return -1;
+ }
+
+ } else {
+ switch (rel) {
+ case EXPR_PLUS:
+ clearString(&ep->result);
+ appendString(&ep->result, lhs);
+ appendString(&ep->result, rhs);
+ return 0;
+ case EXPR_LESS:
+ lval = gstrcmp(lhs, rhs) < 0;
+ break;
+ case EXPR_LESSEQ:
+ lval = gstrcmp(lhs, rhs) <= 0;
+ break;
+ case EXPR_GREATER:
+ lval = gstrcmp(lhs, rhs) > 0;
+ break;
+ case EXPR_GREATEREQ:
+ lval = gstrcmp(lhs, rhs) >= 0;
+ break;
+ case EXPR_EQ:
+ lval = gstrcmp(lhs, rhs) == 0;
+ break;
+ case EXPR_NOTEQ:
+ lval = gstrcmp(lhs, rhs) != 0;
+ break;
+ case EXPR_INC:
+ case EXPR_DEC:
+ case EXPR_MINUS:
+ case EXPR_DIV:
+ case EXPR_MOD:
+ case EXPR_LSHIFT:
+ case EXPR_RSHIFT:
+ default:
+ ejError(ep, T("Bad operator"));
+ return -1;
+ }
+ }
+
+ stritoa(lval, buf, sizeof(buf));
+ setString(&ep->result, buf);
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Evaluate a function
+ */
+
+static int evalFunction(ej_t *ep)
+{
+ sym_t *sp;
+ int (*fn)(int eid, void *handle, int argc, char_t **argv);
+
+ if ((sp = symLookup(ep->functions, ep->func->fname)) == NULL) {
+ ejError(ep, T("Undefined procedure %s"), ep->func->fname);
+ return -1;
+ }
+ fn = (int (*)(int, void*, int, char_t**)) sp->content.value.integer;
+ if (fn == NULL) {
+ ejError(ep, T("Undefined procedure %s"), ep->func->fname);
+ return -1;
+ }
+
+ return (*fn)(ep->eid, (void*) ep->userHandle, ep->func->nArgs,
+ ep->func->args);
+}
+
+/******************************************************************************/
+/*
+ * Output a parse ej_error message
+ */
+
+void ejError(ej_t* ep, char_t* fmt, ...)
+{
+ va_list args;
+ ejinput_t *ip;
+ char_t *errbuf, *msgbuf;
+
+ a_assert(ep);
+ a_assert(fmt);
+ ip = ep->input;
+
+ va_start(args, fmt);
+ msgbuf = NULL;
+ gvsnprintf(&msgbuf, E_MAX_ERROR, fmt, args);
+ va_end(args);
+
+ if (ep && ip) {
+ errbuf = NULL;
+ gsnprintf(&errbuf, E_MAX_ERROR, T("%s\n At line %d, line => \n\n%s\n"),
+ msgbuf, ip->lineNumber, ip->line);
+ bfreeSafe(B_L, ep->error);
+ ep->error = errbuf;
+ }
+ bfreeSafe(B_L, msgbuf);
+}
+
+/******************************************************************************/
+/*
+ * Clear a string value
+ */
+
+static void clearString(char_t **ptr)
+{
+ a_assert(ptr);
+
+ if (*ptr) {
+ bfree(B_L, *ptr);
+ }
+ *ptr = NULL;
+}
+
+/******************************************************************************/
+/*
+ * Set a string value
+ */
+
+static void setString(char_t **ptr, char_t *s)
+{
+ a_assert(ptr);
+
+ if (*ptr) {
+ bfree(B_L, *ptr);
+ }
+ *ptr = bstrdup(B_L, s);
+}
+
+/******************************************************************************/
+/*
+ * Append to the pointer value
+ */
+
+static void appendString(char_t **ptr, char_t *s)
+{
+ int len, oldlen;
+
+ a_assert(ptr);
+
+ if (*ptr) {
+ len = gstrlen(s);
+ oldlen = gstrlen(*ptr);
+ *ptr = brealloc(B_L, *ptr, (len + oldlen + 1) * sizeof(char_t));
+ gstrcpy(&(*ptr)[oldlen], s);
+ } else {
+ *ptr = bstrdup(B_L, s);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Define a function
+ */
+
+int ejSetGlobalFunction(int eid, char_t *name,
+ int (*fn)(int eid, void *handle, int argc, char_t **argv))
+{
+ ej_t *ep;
+
+ if ((ep = ejPtr(eid)) == NULL) {
+ return -1;
+ }
+ return ejSetGlobalFunctionDirect(ep->functions, name, fn);
+}
+
+/******************************************************************************/
+/*
+ * Define a function directly into the function symbol table.
+ */
+
+int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name,
+ int (*fn)(int eid, void *handle, int argc, char_t **argv))
+{
+ if (symEnter(functions, name, valueInteger((long) fn), 0) == NULL) {
+ return -1;
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Get a function definition
+ */
+
+void *ejGetGlobalFunction(int eid, char_t *name)
+{
+ ej_t *ep;
+ sym_t *sp;
+ int (*fn)(int eid, void *handle, int argc, char_t **argv);
+
+ if ((ep = ejPtr(eid)) == NULL) {
+ return NULL;
+ }
+ if ((sp = symLookup(ep->functions, name)) != NULL) {
+ fn = (int (*)(int, void*, int, char_t**)) sp->content.value.integer;
+ return (void*) fn;
+ }
+ return NULL;
+}
+
+/******************************************************************************/
+/*
+ * Utility routine to crack Ejscript arguments. Return the number of args
+ * seen. This routine only supports %s and %d type args.
+ *
+ * Typical usage:
+ *
+ * if (ejArgs(argc, argv, "%s %d", &name, &age) < 2) {
+ * error("Insufficient args\n");
+ * return -1;
+ * }
+ */
+
+int ejArgs(int argc, char_t **argv, char_t *fmt, ...)
+{
+ va_list vargs;
+ char_t *cp, **sp;
+ int *ip;
+ int argn;
+
+ va_start(vargs, fmt);
+
+ if (argv == NULL) {
+ return 0;
+ }
+
+ for (argn = 0, cp = fmt; cp && *cp && argv[argn]; ) {
+ if (*cp++ != '%') {
+ continue;
+ }
+
+ switch (*cp) {
+ case 'd':
+ ip = va_arg(vargs, int*);
+ *ip = gatoi(argv[argn]);
+ break;
+
+ case 's':
+ sp = va_arg(vargs, char_t**);
+ *sp = argv[argn];
+ break;
+
+ default:
+/*
+ * Unsupported
+ */
+ a_assert(0);
+ }
+ argn++;
+ }
+
+ va_end(vargs);
+ return argn;
+}
+
+/******************************************************************************/
+/*
+ * Define the user handle
+ */
+
+void ejSetUserHandle(int eid, int handle)
+{
+ ej_t *ep;
+
+ if ((ep = ejPtr(eid)) == NULL) {
+ return;
+ }
+ ep->userHandle = handle;
+}
+
+/******************************************************************************/
+/*
+ * Get the user handle
+ */
+
+int ejGetUserHandle(int eid)
+{
+ ej_t *ep;
+
+ if ((ep = ejPtr(eid)) == NULL) {
+ return -1;
+ }
+ return ep->userHandle;
+}
+
+/******************************************************************************/
+/*
+ * Get the current line number
+ */
+
+int ejGetLineNumber(int eid)
+{
+ ej_t *ep;
+
+ if ((ep = ejPtr(eid)) == NULL) {
+ return -1;
+ }
+ return ep->input->lineNumber;
+}
+
+/******************************************************************************/
+/*
+ * Set the result
+ */
+
+void ejSetResult(int eid, char_t *s)
+{
+ ej_t *ep;
+
+ if ((ep = ejPtr(eid)) == NULL) {
+ return;
+ }
+ setString(&ep->result, s);
+}
+
+/******************************************************************************/
+/*
+ * Get the result
+ */
+
+char_t *ejGetResult(int eid)
+{
+ ej_t *ep;
+
+ if ((ep = ejPtr(eid)) == NULL) {
+ return NULL;
+ }
+ return ep->result;
+}
+
+/******************************************************************************/
+/*
+ * Set a variable. Note: a variable with a value of NULL means declared but
+ * undefined. The value is defined in the top-most variable frame.
+ */
+
+void ejSetVar(int eid, char_t *var, char_t *value)
+{
+ ej_t *ep;
+ value_t v;
+
+ a_assert(var && *var);
+
+ if ((ep = ejPtr(eid)) == NULL) {
+ return;
+ }
+ if (value == NULL) {
+ v = valueString(value, 0);
+ } else {
+ v = valueString(value, VALUE_ALLOCATE);
+ }
+ symEnter(ep->variables[ep->variableMax - 1] - EJ_OFFSET, var, v, 0);
+}
+
+/******************************************************************************/
+/*
+ * Set a local variable. Note: a variable with a value of NULL means
+ * declared but undefined. The value is defined in the top-most variable frame.
+ */
+
+void ejSetLocalVar(int eid, char_t *var, char_t *value)
+{
+ ej_t *ep;
+ value_t v;
+
+ a_assert(var && *var);
+
+ if ((ep = ejPtr(eid)) == NULL) {
+ return;
+ }
+ if (value == NULL) {
+ v = valueString(value, 0);
+ } else {
+ v = valueString(value, VALUE_ALLOCATE);
+ }
+ symEnter(ep->variables[ep->variableMax - 1] - EJ_OFFSET, var, v, 0);
+}
+
+/******************************************************************************/
+/*
+ * Set a global variable. Note: a variable with a value of NULL means
+ * declared but undefined. The value is defined in the global variable frame.
+ */
+
+void ejSetGlobalVar(int eid, char_t *var, char_t *value)
+{
+ ej_t *ep;
+ value_t v;
+
+ a_assert(var && *var);
+
+ if ((ep = ejPtr(eid)) == NULL) {
+ return;
+ }
+ if (value == NULL) {
+ v = valueString(value, 0);
+ } else {
+ v = valueString(value, VALUE_ALLOCATE);
+ }
+ symEnter(ep->variables[0] - EJ_OFFSET, var, v, 0);
+}
+
+/******************************************************************************/
+/*
+ * Get a variable
+ */
+
+int ejGetVar(int eid, char_t *var, char_t **value)
+{
+ ej_t *ep;
+ sym_t *sp;
+ int i;
+
+ a_assert(var && *var);
+ a_assert(value);
+
+ if ((ep = ejPtr(eid)) == NULL) {
+ return -1;
+ }
+
+ for (i = ep->variableMax - 1; i >= 0; i--) {
+ if ((sp = symLookup(ep->variables[i] - EJ_OFFSET, var)) == NULL) {
+ continue;
+ }
+ a_assert(sp->content.type == string);
+ *value = sp->content.value.string;
+ return i;
+ }
+ return -1;
+}
+
+/******************************************************************************/
+#if UNUSED
+/*
+ * Get the variable symbol table
+ */
+
+sym_fd_t ejGetVariableTable(int eid)
+{
+ ej_t *ep;
+
+ if ((ep = ejPtr(eid)) == NULL) {
+ return -1;
+ }
+ return ep->variables;
+}
+#endif
+/******************************************************************************/
+/*
+ * Get the functions symbol table
+ */
+
+sym_fd_t ejGetFunctionTable(int eid)
+{
+ ej_t *ep;
+
+ if ((ep = ejPtr(eid)) == NULL) {
+ return -1;
+ }
+ return ep->functions;
+}
+
+/******************************************************************************/
+/*
+ * Free an argument list
+ */
+
+static void freeFunc(ejfunc_t *func)
+{
+ int i;
+
+ for (i = func->nArgs - 1; i >= 0; i--) {
+ bfree(B_L, func->args[i]);
+ func->nArgs = hFree((void***) &func->args, i);
+ }
+ if (func->fname) {
+ bfree(B_L, func->fname);
+ func->fname = NULL;
+ }
+}
+
+/******************************************************************************/
+/*
+ * Get Ejscript pointer
+ */
+
+static ej_t *ejPtr(int eid)
+{
+ a_assert(0 <= eid && eid < ejMax);
+
+ if (eid < 0 || eid >= ejMax || ejHandles[eid] == NULL) {
+ ejError(NULL, T("Bad handle %d"), eid);
+ return NULL;
+ }
+ return ejHandles[eid];
+}
+
+/******************************************************************************/
diff --git a/cpukit/httpd/form.c b/cpukit/httpd/form.c
new file mode 100644
index 0000000000..05d5600ada
--- /dev/null
+++ b/cpukit/httpd/form.c
@@ -0,0 +1,163 @@
+/*
+ * form.c -- Form processing (in-memory CGI) for the GoAhead Web server
+ *
+ * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/********************************** Description *******************************/
+
+/*
+ * This module implements the /goform handler. It emulates CGI processing
+ * but performs this in-process and not as an external process. This enables
+ * a very high performance implementation with easy parsing and decoding
+ * of query strings and posted data.
+ */
+
+/*********************************** Includes *********************************/
+
+#include "wsIntrn.h"
+
+/************************************ Locals **********************************/
+
+static sym_fd_t formSymtab = -1; /* Symbol table for form handlers */
+
+/************************************* Code ***********************************/
+/*
+ * Process a form request. Returns 1 always to indicate it handled the URL
+ */
+
+int websFormHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg,
+ char_t *url, char_t *path, char_t* query)
+{
+ sym_t *sp;
+ char_t formBuf[FNAMESIZE];
+ char_t *cp, *formName;
+ int (*fn)(void *sock, char_t* path, char_t *args);
+
+ a_assert(websValid(wp));
+ a_assert(url && *url);
+ a_assert(path && *path == '/');
+
+ websStats.formHits++;
+
+/*
+ * Extract the form name
+ */
+ gstrncpy(formBuf, path, TSZ(formBuf));
+ if ((formName = gstrchr(&formBuf[1], '/')) == NULL) {
+ websError(wp, 200, T("Missing form name"));
+ return 1;
+ }
+ formName++;
+ if ((cp = gstrchr(formName, '/')) != NULL) {
+ *cp = '\0';
+ }
+
+/*
+ * Lookup the C form function first and then try tcl (no javascript support
+ * yet).
+ */
+ sp = symLookup(formSymtab, formName);
+ if (sp == NULL) {
+ websError(wp, 200, T("Form %s is not defined"), formName);
+ } else {
+ fn = (int (*)(void*, char_t*, char_t*)) sp->content.value.integer;
+ a_assert(fn);
+ if (fn) {
+/*
+ * For good practice, forms must call websDone()
+ */
+ (*fn)((void*) wp, formName, query);
+ if (websValid(wp)) {
+ websError(wp, 200, T("Form didn't call websDone"));
+ }
+ }
+ }
+ return 1;
+}
+
+/******************************************************************************/
+/*
+ * Define a form function in the "form" map space.
+ */
+
+int websFormDefine(char_t *name, void (*fn)(webs_t wp, char_t *path,
+ char_t *query))
+{
+ static int once = 0;
+
+ a_assert(name && *name);
+ a_assert(fn);
+
+ if (fn == NULL) {
+ return -1;
+ }
+
+ if (once++ == 0) {
+ websFormOpen();
+ }
+ symEnter(formSymtab, name, valueInteger((int) fn), (int) NULL);
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Open the symbol table for forms.
+ */
+
+void websFormOpen()
+{
+ formSymtab = symOpen(64);
+}
+
+/******************************************************************************/
+/*
+ * Close the symbol table for forms.
+ */
+
+void websFormClose()
+{
+ if (formSymtab != -1) {
+ symClose(formSymtab, NULL);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Write a webs header. This is a convenience routine to write a common
+ * header for a form back to the browser.
+ */
+
+void websHeader(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ websWrite(wp, T("HTTP/1.0 200 OK\n"));
+
+/*
+ * By license terms the following line of code must not be modified
+ */
+ websWrite(wp, T("Server: GoAhead-Webs\r\n"));
+
+ websWrite(wp, T("Pragma: no-cache\n"));
+ websWrite(wp, T("Cache-control: no-cache\n"));
+ websWrite(wp, T("Content-Type: text/html\n"));
+ websWrite(wp, T("\n"));
+ websWrite(wp, T("<html>\n"));
+}
+
+/******************************************************************************/
+/*
+ * Write a webs footer
+ */
+
+void websFooter(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ websWrite(wp, T("</html>\n"));
+}
+
+/******************************************************************************/
diff --git a/cpukit/httpd/h.c b/cpukit/httpd/h.c
new file mode 100644
index 0000000000..ed7a4553eb
--- /dev/null
+++ b/cpukit/httpd/h.c
@@ -0,0 +1,171 @@
+/*
+ * h.c -- Handle allocation module
+ *
+ * Copyright (c) GoAhead Software Inc., 1995-1999. All Rights Reserved.
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * This module provides a simple API to allocate and free handles
+ * It maintains a dynamic array of pointers. These usually point to
+ * per-handle structures.
+ */
+
+/********************************* Includes ***********************************/
+
+#if UEMF
+ #include "uemf.h"
+#else
+ #include "basic/basicInternal.h"
+#endif
+
+/********************************** Defines ***********************************/
+/*
+ * The handle list stores the length of the list and the number of used
+ * handles in the first two words. These are hidden from the caller by
+ * returning a pointer to the third word to the caller
+ */
+
+#define H_LEN 0 /* First entry holds length of list */
+#define H_USED 1 /* Second entry holds number of used */
+#define H_OFFSET 2 /* Offset to real start of list */
+
+#define H_INCR 16 /* Grow handle list in chunks this size */
+
+/*********************************** Code *************************************/
+/*
+ * Allocate a new file handle. On the first call, the caller must set the
+ * handle map to be a pointer to a null pointer. *map points to the second
+ * element in the handle array.
+ */
+
+int hAlloc(void ***map)
+{
+ int *mp;
+ int handle, len, memsize, incr;
+
+ a_assert(map);
+
+ if (*map == NULL) {
+ incr = H_INCR;
+ memsize = (incr + H_OFFSET) * sizeof(void**);
+ if ((mp = (int*) balloc(B_L, memsize)) == NULL) {
+ return -1;
+ }
+ memset(mp, 0, memsize);
+ mp[H_LEN] = incr;
+ mp[H_USED] = 0;
+ *map = (void**) &mp[H_OFFSET];
+ } else {
+ mp = &((*(int**)map)[-H_OFFSET]);
+ }
+
+ len = mp[H_LEN];
+
+/*
+ * Find the first null handle
+ */
+ if (mp[H_USED] < mp[H_LEN]) {
+ for (handle = 0; handle < len; handle++)
+ if (mp[handle+H_OFFSET] == 0) {
+ mp[H_USED]++;
+ return handle;
+ }
+ } else {
+ handle = len;
+ }
+
+/*
+ * No free handle so grow the handle list. Grow list in chunks of H_INCR.
+ */
+ len += H_INCR;
+ memsize = (len + H_OFFSET) * sizeof(void**);
+ if ((mp = (int*) brealloc(B_L, (void*) mp, memsize)) == NULL) {
+ return -1;
+ }
+ *map = (void**) &mp[H_OFFSET];
+ mp[H_LEN] = len;
+ memset(&mp[H_OFFSET + len - H_INCR], 0, sizeof(int*) * H_INCR);
+ mp[H_USED]++;
+ return handle;
+}
+
+/******************************************************************************/
+/*
+ * Free a handle. This function returns the value of the largest
+ * handle in use plus 1, to be saved as a max value.
+ */
+
+int hFree(void ***map, int handle)
+{
+ int *mp;
+ int len;
+
+ a_assert(map);
+ mp = &((*(int**)map)[-H_OFFSET]);
+ a_assert(mp[H_LEN] >= H_INCR);
+
+ a_assert(mp[handle + H_OFFSET]);
+ a_assert(mp[H_USED]);
+ mp[handle + H_OFFSET] = 0;
+ if (--(mp[H_USED]) == 0) {
+ bfree(B_L, (void*) mp);
+ *map = NULL;
+ }
+
+/*
+ * Find the greatest handle number in use.
+ */
+ if (*map == NULL) {
+ handle = -1;
+ } else {
+ len = mp[H_LEN];
+ if (mp[H_USED] < mp[H_LEN]) {
+ for (handle = len - 1; handle >= 0; handle--) {
+ if (mp[handle + H_OFFSET])
+ break;
+ }
+ } else {
+ handle = len;
+ }
+ }
+ return handle + 1;
+}
+
+/******************************************************************************/
+/*
+ * Allocate an entry in the halloc array.
+ */
+
+int hAllocEntry(void ***list, int *max, int size)
+{
+ char_t *cp;
+ int id;
+
+ a_assert(list);
+ a_assert(max);
+
+ if ((id = hAlloc((void***) list)) < 0) {
+ return -1;
+ }
+
+ if (size > 0) {
+ if ((cp = balloc(B_L, size)) == NULL) {
+ hFree(list, id);
+ return -1;
+ }
+ a_assert(cp);
+ memset(cp, 0, size);
+
+ (*list)[id] = (void*) cp;
+ }
+
+ if (id >= *max) {
+ *max = id + 1;
+ }
+ return id;
+}
+
+/******************************************************************************/
diff --git a/cpukit/httpd/handler.c b/cpukit/httpd/handler.c
new file mode 100644
index 0000000000..d481ec3c0c
--- /dev/null
+++ b/cpukit/httpd/handler.c
@@ -0,0 +1,284 @@
+/*
+ * handler.c -- URL handler support
+ *
+ * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * This module implements a URL handler interface and API to permit
+ * the addition of user definable URL processors.
+ */
+
+/********************************* Includes ***********************************/
+
+#include "wsIntrn.h"
+
+/*********************************** Locals ***********************************/
+
+static websUrlHandlerType* websUrlHandler; /* URL handler list */
+static int websUrlHandlerMax; /* Number of entries */
+
+/**************************** Forward Declarations ****************************/
+
+static int websUrlHandlerSort(const void* p1, const void* p2);
+static int websPublishHandler(webs_t wp, char_t *urlPrefix, char_t *webDir,
+ int sid, char_t *url, char_t *path, char_t *query);
+
+/*********************************** Code *************************************/
+/*
+ * Initialize the URL handler module
+ */
+
+int websUrlHandlerOpen()
+{
+ websAspOpen();
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Close the URL handler module
+ */
+
+void websUrlHandlerClose()
+{
+ websUrlHandlerType* sp;
+
+ websAspClose();
+ for (sp = websUrlHandler; sp < &websUrlHandler[websUrlHandlerMax]; sp++) {
+ bfree(B_L, sp->urlPrefix);
+ if (sp->webDir) {
+ bfree(B_L, sp->webDir);
+ }
+ }
+ bfree(B_L, websUrlHandler);
+ websUrlHandlerMax = 0;
+}
+
+/******************************************************************************/
+/*
+ * Define a new URL handler. urlPrefix is the URL prefix to match. webDir is
+ * an optional root directory path for a web directory. arg is an optional
+ * arg to pass to the URL handler. flags defines the matching order. Valid
+ * flags include WEBS_HANDLER_LAST, WEBS_HANDLER_FIRST. If multiple users
+ * specify last or first, their order is defined alphabetically by the
+ * urlPrefix.
+ */
+
+int websUrlHandlerDefine(char_t *urlPrefix, char_t *webDir, int arg,
+ int (*handler)(webs_t wp, char_t *urlPrefix, char_t *webdir, int arg,
+ char_t *url, char_t *path, char_t *query), int flags)
+{
+ websUrlHandlerType *sp;
+ int len;
+
+ a_assert(urlPrefix);
+ a_assert(handler);
+
+/*
+ * Grow the URL handler array to create a new slot
+ */
+ len = (websUrlHandlerMax + 1) * sizeof(websUrlHandlerType);
+ if ((websUrlHandler = brealloc(B_L, websUrlHandler, len)) == NULL) {
+ return -1;
+ }
+ sp = &websUrlHandler[websUrlHandlerMax++];
+ memset(sp, 0, sizeof(websUrlHandlerType));
+
+ sp->urlPrefix = bstrdup(B_L, urlPrefix);
+ sp->len = gstrlen(sp->urlPrefix);
+ if (webDir) {
+ sp->webDir = bstrdup(B_L, webDir);
+ } else {
+ sp->webDir = bstrdup(B_L, T(""));
+ }
+ sp->handler = handler;
+ sp->arg = arg;
+ sp->flags = flags;
+
+/*
+ * Sort in decreasing URL length order observing the flags for first and last
+ */
+ qsort(websUrlHandler, websUrlHandlerMax, sizeof(websUrlHandlerType),
+ websUrlHandlerSort);
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Delete an existing URL handler. We don't reclaim the space of the old
+ * handler, just NULL the entry. Return -1 if handler is not found.
+ */
+
+int websUrlHandlerDelete(int (*handler)(webs_t wp, char_t *urlPrefix,
+ char_t *webDir, int arg, char_t *url, char_t *path, char_t *query))
+{
+ websUrlHandlerType *sp;
+ int i;
+
+ for (i = 0; i < websUrlHandlerMax; i++) {
+ sp = &websUrlHandler[i];
+ if (sp->handler == handler) {
+ sp->handler = NULL;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/******************************************************************************/
+/*
+ * Sort in decreasing URL length order observing the flags for first and last
+ */
+
+static int websUrlHandlerSort(const void* p1, const void* p2)
+{
+ websUrlHandlerType *s1, *s2;
+ int rc;
+
+ a_assert(p1);
+ a_assert(p2);
+
+ s1 = (websUrlHandlerType*) p1;
+ s2 = (websUrlHandlerType*) p2;
+
+ if ((s1->flags & WEBS_HANDLER_FIRST) || (s2->flags & WEBS_HANDLER_LAST)) {
+ return -1;
+ }
+
+ if ((s2->flags & WEBS_HANDLER_FIRST) || (s1->flags & WEBS_HANDLER_LAST)) {
+ return 1;
+ }
+
+ if ((rc = gstrcmp(s1->urlPrefix, s2->urlPrefix)) == 0) {
+ if (s1->len < s2->len) {
+ return 1;
+ } else if (s1->len > s2->len) {
+ return -1;
+ }
+ }
+ return -rc;
+}
+
+/******************************************************************************/
+/*
+ * Publish a new web directory (Use the default URL handler)
+ */
+
+int websPublish(char_t *urlPrefix, char_t *path)
+{
+ return websUrlHandlerDefine(urlPrefix, path, 0, websPublishHandler, 0);
+}
+
+/******************************************************************************/
+/*
+ * Return the directory for a given prefix. Ignore empty prefixes
+ */
+
+char_t *websGetPublishDir(char_t *path, char_t **urlPrefix)
+{
+ websUrlHandlerType *sp;
+ int i;
+
+ for (i = 0; i < websUrlHandlerMax; i++) {
+ sp = &websUrlHandler[i];
+ if (sp->urlPrefix[0] == '\0') {
+ continue;
+ }
+ if (sp->handler && gstrncmp(sp->urlPrefix, path, sp->len) == 0) {
+ if (urlPrefix) {
+ *urlPrefix = sp->urlPrefix;
+ }
+ return sp->webDir;
+ }
+ }
+ return NULL;
+}
+
+/******************************************************************************/
+/*
+ * Publish URL handler. We just patch the web page Directory and let the
+ * default handler do the rest.
+ */
+
+static int websPublishHandler(webs_t wp, char_t *urlPrefix, char_t *webDir,
+ int sid, char_t *url, char_t *path, char_t *query)
+{
+ int len;
+
+ a_assert(websValid(wp));
+ a_assert(path);
+
+/*
+ * Trim the urlPrefix off the path and set the webdirectory. Add one to step
+ * over the trailing '/'
+ */
+ len = gstrlen(urlPrefix) + 1;
+ websSetRequestPath(wp, webDir, &path[len]);
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * See if any valid handlers are defined for this request. If so, call them
+ * and continue calling valid handlers until one accepts the request.
+ * Return true if a handler was invoked, else return FALSE.
+ */
+
+int websUrlHandlerRequest(webs_t wp)
+{
+ websUrlHandlerType *sp;
+ int i, first;
+
+ a_assert(websValid(wp));
+
+/*
+ * Delete the socket handler as we don't want to start reading any
+ * data on the connection as it may be for the next pipelined HTTP/1.1
+ * request if using Keep Alive
+ */
+ socketDeleteHandler(wp->sid);
+ wp->state = WEBS_PROCESSING;
+ websStats.handlerHits++;
+
+ websSetRequestPath(wp, websGetDefaultDir(), NULL);
+
+/*
+ * We loop over each handler in order till one accepts the request.
+ * The security handler will handle the request if access is NOT allowed.
+ */
+ first = 1;
+ for (i = 0; i < websUrlHandlerMax; i++) {
+ sp = &websUrlHandler[i];
+ if (sp->handler && gstrncmp(sp->urlPrefix, wp->path, sp->len) == 0) {
+ if (first) {
+ websSetEnv(wp);
+ first = 0;
+ }
+ if ((*sp->handler)(wp, sp->urlPrefix, sp->webDir, sp->arg,
+ wp->url, wp->path, wp->query)) {
+ return 1;
+ }
+ if (!websValid(wp)) {
+ trace(0,
+ T("webs: handler %s called websDone, but didn't return 1\n"),
+ sp->urlPrefix);
+ return 1;
+ }
+ }
+ }
+/*
+ * If no handler processed the request, then return an error. Note: It was
+ * the handlers responsibility to call websDone
+ */
+ if (i >= websUrlHandlerMax) {
+ websError(wp, 200, T("No handler for this URL %s"), wp->url);
+ }
+ return 0;
+}
+
+/******************************************************************************/
diff --git a/cpukit/httpd/mime.c b/cpukit/httpd/mime.c
new file mode 100644
index 0000000000..2aa2422ca9
--- /dev/null
+++ b/cpukit/httpd/mime.c
@@ -0,0 +1,112 @@
+/*
+ * mime.c -- Web server mime types
+ *
+ * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * Mime types and file extensions. This module maps URL extensions to
+ * content types.
+ */
+
+/********************************* Includes ***********************************/
+
+#include "wsIntrn.h"
+
+/******************************** Global Data *********************************/
+/*
+ * Addd entries to the MimeList as required for your content
+ */
+
+websMimeType websMimeList[] = {
+ { T("application/java"), T(".class") },
+ { T("application/java"), T(".jar") },
+ { T("text/html"), T(".asp") },
+ { T("text/html"), T(".htm") },
+ { T("text/html"), T(".html") },
+ { T("image/gif"), T(".gif") },
+ { T("image/jpeg"), T(".jpg") },
+ { T("text/css"), T(".css") },
+ { T("text/plain"), T(".txt") },
+
+#if MORE_MIME_TYPES
+ { T("application/binary"), T(".exe") },
+ { T("application/compress"), T(".z") },
+ { T("application/gzip"), T(".gz") },
+ { T("application/octet-stream"), T(".bin") },
+ { T("application/oda"), T(".oda") },
+ { T("application/pdf"), T(".pdf") },
+ { T("application/postscript"), T(".ai") },
+ { T("application/postscript"), T(".eps") },
+ { T("application/postscript"), T(".ps") },
+ { T("application/rtf"), T(".rtf") },
+ { T("application/x-bcpio"), T(".bcpio") },
+ { T("application/x-cpio"), T(".cpio") },
+ { T("application/x-csh"), T(".csh") },
+ { T("application/x-dvi"), T(".dvi") },
+ { T("application/x-gtar"), T(".gtar") },
+ { T("application/x-hdf"), T(".hdf") },
+ { T("application/x-latex"), T(".latex") },
+ { T("application/x-mif"), T(".mif") },
+ { T("application/x-netcdf"), T(".nc") },
+ { T("application/x-netcdf"), T(".cdf") },
+ { T("application/x-ns-proxy-autoconfig"), T(".pac") },
+ { T("application/x-patch"), T(".patch") },
+ { T("application/x-sh"), T(".sh") },
+ { T("application/x-shar"), T(".shar") },
+ { T("application/x-sv4cpio"), T(".sv4cpio") },
+ { T("application/x-sv4crc"), T(".sv4crc") },
+ { T("application/x-tar"), T(".tar") },
+ { T("application/x-tcl"), T(".tcl") },
+ { T("application/x-tex"), T(".tex") },
+ { T("application/x-texinfo"), T(".texinfo") },
+ { T("application/x-texinfo"), T(".texi") },
+ { T("application/x-troff"), T(".t") },
+ { T("application/x-troff"), T(".tr") },
+ { T("application/x-troff"), T(".roff") },
+ { T("application/x-troff-man"), T(".man") },
+ { T("application/x-troff-me"), T(".me") },
+ { T("application/x-troff-ms"), T(".ms") },
+ { T("application/x-ustar"), T(".ustar") },
+ { T("application/x-wais-source"), T(".src") },
+ { T("application/zip"), T(".zip") },
+ { T("audio/basic"), T(".au snd") },
+ { T("audio/x-aiff"), T(".aif") },
+ { T("audio/x-aiff"), T(".aiff") },
+ { T("audio/x-aiff"), T(".aifc") },
+ { T("audio/x-wav"), T(".wav") },
+ { T("audio/x-wav"), T(".ram") },
+ { T("image/ief"), T(".ief") },
+ { T("image/jpeg"), T(".jpeg") },
+ { T("image/jpeg"), T(".jpe") },
+ { T("image/tiff"), T(".tiff") },
+ { T("image/tiff"), T(".tif") },
+ { T("image/x-cmu-raster"), T(".ras") },
+ { T("image/x-portable-anymap"), T(".pnm") },
+ { T("image/x-portable-bitmap"), T(".pbm") },
+ { T("image/x-portable-graymap"), T(".pgm") },
+ { T("image/x-portable-pixmap"), T(".ppm") },
+ { T("image/x-rgb"), T(".rgb") },
+ { T("image/x-xbitmap"), T(".xbm") },
+ { T("image/x-xpixmap"), T(".xpm") },
+ { T("image/x-xwindowdump"), T(".xwd") },
+ { T("text/html"), T(".cfm") },
+ { T("text/html"), T(".shtm") },
+ { T("text/html"), T(".shtml") },
+ { T("text/richtext"), T(".rtx") },
+ { T("text/tab-separated-values"), T(".tsv") },
+ { T("text/x-setext"), T(".etx") },
+ { T("video/mpeg"), T(".mpeg mpg mpe") },
+ { T("video/quicktime"), T(".qt") },
+ { T("video/quicktime"), T(".mov") },
+ { T("video/x-msvideo"), T(".avi") },
+ { T("video/x-sgi-movie"), T(".movie") },
+#endif
+ { NULL, NULL},
+};
+
+/*****************************************************************************/
diff --git a/cpukit/httpd/misc.c b/cpukit/httpd/misc.c
new file mode 100644
index 0000000000..5fbe63329c
--- /dev/null
+++ b/cpukit/httpd/misc.c
@@ -0,0 +1,581 @@
+/*
+ * misc.c -- Miscellaneous routines.
+ *
+ * Copyright (c) GoAhead Software Inc., 1995-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/********************************* Includes ***********************************/
+
+#if UEMF
+ #include "uemf.h"
+#else
+ #include "basic/basicInternal.h"
+#endif
+
+/********************************* Defines ************************************/
+/*
+ * Sprintf buffer structure. Make the increment 8 less than 64 so that
+ * a balloc can use a 64 byte block.
+ */
+
+#define STR_REALLOC 0x1 /* Reallocate the buffer as required */
+#define STR_INC 58 /* Growth increment */
+
+typedef struct {
+ char_t *s; /* Pointer to buffer */
+ int size; /* Current buffer size */
+ int max; /* Maximum buffer size */
+ int count; /* Buffer count */
+ int flags; /* Allocation flags */
+} strbuf_t;
+
+/*
+ * Sprintf formatting flags
+ */
+enum flag {
+ flag_none = 0,
+ flag_minus = 1,
+ flag_plus = 2,
+ flag_space = 4,
+ flag_hash = 8,
+ flag_zero = 16,
+ flag_short = 32,
+ flag_long = 64
+};
+
+/************************** Forward Declarations ******************************/
+
+static int dsnprintf(char_t **s, int size, char_t *fmt, va_list arg,
+ int msize);
+static int strnlen(char_t *s, unsigned int n);
+static void put_char(strbuf_t *buf, char_t c);
+static void put_string(strbuf_t *buf, char_t *s, int len,
+ int width, int prec, enum flag f);
+static void put_ulong(strbuf_t *buf, unsigned long int value, int base,
+ int upper, char_t *prefix, int width, int prec, enum flag f);
+
+/************************************ Code ************************************/
+/*
+ * "basename" returns a pointer to the last component of a pathname
+ * LINUX and LynxOS have their own basename function
+ */
+
+#if ! LINUX & ! LYNX
+char_t *basename(char_t* name)
+{
+ char_t *cp;
+
+#if NW || WIN
+ if (((cp = gstrrchr(name, '\\')) == NULL) &&
+ ((cp = gstrrchr(name, '/')) == NULL)) {
+ return name;
+#else
+ if ((cp = gstrrchr(name, '/')) == NULL) {
+ return name;
+#endif
+ } else if (*(cp + 1) == '\0' && cp == name) {
+ return name;
+ } else if (*(cp + 1) == '\0' && cp != name) {
+ return T("");
+ } else {
+ return ++cp;
+ }
+}
+#endif /* ! LINUX & ! LYNX */
+
+/******************************************************************************/
+/*
+ * Returns a pointer to the directory component of a pathname. bufsize is
+ * the size of the buffer in BYTES!
+ */
+
+char_t *dirname(char_t* buf, char_t* name, int bufsize)
+{
+ char_t* cp;
+ int len;
+
+ a_assert(name);
+ a_assert(buf);
+ a_assert(bufsize > 0);
+
+#if WIN || NW
+ if ((cp = gstrrchr(name, '/')) == NULL &&
+ (cp = gstrrchr(name, '\\')) == NULL)
+#else
+ if ((cp = gstrrchr(name, '/')) == NULL)
+#endif
+ {
+ gstrcpy(buf, T("."));
+ return buf;
+ }
+
+ if ((*(cp + 1) == '\0' && cp == name)) {
+ gstrncpy(buf, T("."), TSZ(bufsize));
+ gstrcpy(buf, T("."));
+ return buf;
+ }
+
+ len = cp - name;
+
+ if (len < bufsize) {
+ gstrncpy(buf, name, len);
+ buf[len] = '\0';
+ } else {
+ gstrncpy(buf, name, TSZ(bufsize));
+ buf[bufsize - 1] = '\0';
+ }
+
+ return buf;
+}
+
+/******************************************************************************/
+/*
+ * sprintf and vsprintf are bad, ok. You can easily clobber memory. Use
+ * gsnprintf and gvsnprintf instead! These functions do _not_ support floating
+ * point, like %e, %f, %g...
+ */
+
+int gsnprintf(char_t **s, int n, char_t *fmt, ...)
+{
+ va_list ap;
+ int result;
+
+ a_assert(s);
+ a_assert(fmt);
+
+ *s = NULL;
+ va_start(ap, fmt);
+ result = gvsnprintf(s, n, fmt, ap);
+ va_end(ap);
+ return result;
+}
+
+/******************************************************************************/
+/*
+ * This function appends the formatted string to the supplied string,
+ * reallocing if required.
+ */
+
+int gsprintfRealloc(char_t **s, int n, int msize, char_t *fmt, ...)
+{
+ va_list ap;
+ int result;
+
+ a_assert(s);
+ a_assert(fmt);
+
+ if (msize == -1) {
+ *s = NULL;
+ }
+ va_start(ap, fmt);
+ result = dsnprintf(s, n, fmt, ap, msize);
+ va_end(ap);
+ return result;
+}
+
+/******************************************************************************/
+/*
+ * A vsprintf replacement.
+ */
+
+int gvsnprintf(char_t **s, int n, char_t *fmt, va_list arg)
+{
+ a_assert(s);
+ a_assert(fmt);
+
+ return dsnprintf(s, n, fmt, arg, 0);
+}
+
+/******************************************************************************/
+/*
+ * Dynamic sprintf implementation. Supports dynamic buffer allocation also.
+ * This function can be called multiple times to grow an existing allocated
+ * buffer. In this case, msize is set to the size of the previously allocated
+ * buffer. The buffer will be realloced, as required. If msize is set, we
+ * return the size of the allocated buffer for use with the next call. For
+ * the first call, msize can be set to -1.
+ */
+
+static int dsnprintf(char_t **s, int size, char_t *fmt, va_list arg, int msize)
+{
+ strbuf_t buf;
+ char_t c;
+
+ a_assert(s);
+ a_assert(fmt);
+
+ memset(&buf, 0, sizeof(buf));
+ buf.s = *s;
+
+ if (*s == NULL || msize != 0) {
+ buf.max = size;
+ buf.flags |= STR_REALLOC;
+ if (msize != 0) {
+ buf.size = max(msize, 0);
+ }
+ if (*s != NULL && msize != 0) {
+ buf.count = gstrlen(*s);
+ }
+ } else {
+ buf.size = size;
+ }
+
+ while ((c = *fmt++) != '\0') {
+ if (c != '%' || (c = *fmt++) == '%') {
+ put_char(&buf, c);
+ } else {
+ enum flag f = flag_none;
+ int width = 0;
+ int prec = -1;
+ for ( ; c != '\0'; c = *fmt++) {
+ if (c == '-') {
+ f |= flag_minus;
+ } else if (c == '+') {
+ f |= flag_plus;
+ } else if (c == ' ') {
+ f |= flag_space;
+ } else if (c == '#') {
+ f |= flag_hash;
+ } else if (c == '0') {
+ f |= flag_zero;
+ } else {
+ break;
+ }
+ }
+ if (c == '*') {
+ width = va_arg(arg, int);
+ if (width < 0) {
+ f |= flag_minus;
+ width = -width;
+ }
+ c = *fmt++;
+ } else {
+ for ( ; gisdigit(c); c = *fmt++) {
+ width = width * 10 + (c - '0');
+ }
+ }
+ if (c == '.') {
+ f &= ~flag_zero;
+ c = *fmt++;
+ if (c == '*') {
+ prec = va_arg(arg, int);
+ c = *fmt++;
+ } else {
+ for (prec = 0; gisdigit(c); c = *fmt++) {
+ prec = prec * 10 + (c - '0');
+ }
+ }
+ }
+ if (c == 'h' || c == 'l') {
+ f |= (c == 'h' ? flag_short : flag_long);
+ c = *fmt++;
+ }
+ if (c == 'd' || c == 'i') {
+ long int value;
+ if (f & flag_short) {
+ value = (short int) va_arg(arg, int);
+ } else if (f & flag_long) {
+ value = va_arg(arg, long int);
+ } else {
+ value = va_arg(arg, int);
+ }
+ if (value >= 0) {
+ if (f & flag_plus) {
+ put_ulong(&buf, value, 10, 0, T("+"), width, prec, f);
+ } else if (f & flag_space) {
+ put_ulong(&buf, value, 10, 0, T(" "), width, prec, f);
+ } else {
+ put_ulong(&buf, value, 10, 0, NULL, width, prec, f);
+ }
+ } else {
+ put_ulong(&buf, -value, 10, 0, T("-"), width, prec, f);
+ }
+ } else if (c == 'o' || c == 'u' || c == 'x' || c == 'X') {
+ unsigned long int value;
+ if (f & flag_short) {
+ value = (unsigned short int) va_arg(arg, unsigned int);
+ } else if (f & flag_long) {
+ value = va_arg(arg, unsigned long int);
+ } else {
+ value = va_arg(arg, unsigned int);
+ }
+ if (c == 'o') {
+ if (f & flag_hash && value != 0) {
+ put_ulong(&buf, value, 8, 0, T("0"), width, prec, f);
+ } else {
+ put_ulong(&buf, value, 8, 0, NULL, width, prec, f);
+ }
+ } else if (c == 'u') {
+ put_ulong(&buf, value, 10, 0, NULL, width, prec, f);
+ } else {
+ if (f & flag_hash && value != 0) {
+ if (c == 'x') {
+ put_ulong(&buf, value, 16, 0, T("0x"), width,
+ prec, f);
+ } else {
+ put_ulong(&buf, value, 16, 1, T("0X"), width,
+ prec, f);
+ }
+ } else {
+ put_ulong(&buf, value, 16, 0, NULL, width, prec, f);
+ }
+ }
+
+ } else if (c == 'c') {
+ char_t value = va_arg(arg, int);
+ put_char(&buf, value);
+
+ } else if (c == 's' || c == 'S') {
+ char_t *value = va_arg(arg, char_t *);
+ if (value == NULL) {
+ put_string(&buf, T("(null)"), -1, width, prec, f);
+ } else if (f & flag_hash) {
+ put_string(&buf,
+ value + 1, (char_t) *value, width, prec, f);
+ } else {
+ put_string(&buf, value, -1, width, prec, f);
+ }
+ } else if (c == 'p') {
+ void *value = va_arg(arg, void *);
+ put_ulong(&buf,
+ (unsigned long int) value, 16, 0, T("0x"), width, prec, f);
+ } else if (c == 'n') {
+ if (f & flag_short) {
+ short int *value = va_arg(arg, short int *);
+ *value = buf.count;
+ } else if (f & flag_long) {
+ long int *value = va_arg(arg, long int *);
+ *value = buf.count;
+ } else {
+ int *value = va_arg(arg, int *);
+ *value = buf.count;
+ }
+ } else {
+ put_char(&buf, c);
+ }
+ }
+ }
+ if (buf.s == NULL) {
+ put_char(&buf, '\0');
+ }
+
+/*
+ * If the user requested a dynamic buffer (*s == NULL), ensure it is returned.
+ */
+ if (*s == NULL || msize != 0) {
+ *s = buf.s;
+ }
+
+ if (*s != NULL && size > 0) {
+ if (buf.count < size) {
+ (*s)[buf.count] = '\0';
+ } else {
+ (*s)[buf.size - 1] = '\0';
+ }
+ }
+
+ if (msize != 0) {
+ return buf.size;
+ }
+ return buf.count;
+}
+
+/******************************************************************************/
+/*
+ * Return the length of a string limited by a given length
+ */
+
+static int strnlen(char_t *s, unsigned int n)
+{
+ unsigned int len;
+
+ len = gstrlen(s);
+ return min(len, n);
+}
+
+/******************************************************************************/
+/*
+ * Add a character to a string buffer
+ */
+
+static void put_char(strbuf_t *buf, char_t c)
+{
+ if (buf->count >= buf->size) {
+ if (! (buf->flags & STR_REALLOC)) {
+ return;
+ }
+ buf->size += STR_INC;
+ if (buf->size > buf->max && buf->size > STR_INC) {
+ a_assert(buf->size <= buf->max);
+ buf->size -= STR_INC;
+ return;
+ }
+ if (buf->s == NULL) {
+ buf->s = balloc(B_L, buf->size * sizeof(char_t*));
+ } else {
+ buf->s = brealloc(B_L, buf->s, buf->size * sizeof(char_t*));
+ }
+ }
+ buf->s[buf->count] = c;
+ ++buf->count;
+}
+
+/******************************************************************************/
+/*
+ * Add a string to a string buffer
+ */
+
+static void put_string(strbuf_t *buf, char_t *s, int len, int width,
+ int prec, enum flag f)
+{
+ int i;
+
+ if (len < 0) {
+ len = strnlen(s, prec >= 0 ? prec : ULONG_MAX);
+ } else if (prec >= 0 && prec < len) {
+ len = prec;
+ }
+ if (width > len && !(f & flag_minus)) {
+ for (i = len; i < width; ++i) {
+ put_char(buf, ' ');
+ }
+ }
+ for (i = 0; i < len; ++i) {
+ put_char(buf, s[i]);
+ }
+ if (width > len && f & flag_minus) {
+ for (i = len; i < width; ++i) {
+ put_char(buf, ' ');
+ }
+ }
+}
+
+/******************************************************************************/
+/*
+ * Add a long to a string buffer
+ */
+
+static void put_ulong(strbuf_t *buf, unsigned long int value, int base,
+ int upper, char_t *prefix, int width, int prec, enum flag f)
+{
+ unsigned long x, x2;
+ int len, zeros, i;
+
+ for (len = 1, x = 1; x < ULONG_MAX / base; ++len, x = x2) {
+ x2 = x * base;
+ if (x2 > value) {
+ break;
+ }
+ }
+ zeros = (prec > len) ? prec - len : 0;
+ width -= zeros + len;
+ if (prefix != NULL) {
+ width -= strnlen(prefix, ULONG_MAX);
+ }
+ if (!(f & flag_minus)) {
+ for (i = 0; i < width; ++i) {
+ put_char(buf, ' ');
+ }
+ }
+ if (prefix != NULL) {
+ put_string(buf, prefix, -1, 0, -1, flag_none);
+ }
+ for (i = 0; i < zeros; ++i) {
+ put_char(buf, '0');
+ }
+ for ( ; x > 0; x /= base) {
+ int digit = (value / x) % base;
+ put_char(buf, (char) ((digit < 10 ? '0' : (upper ? 'A' : 'a') - 10) +
+ digit));
+ }
+ if (f & flag_minus) {
+ for (i = 0; i < width; ++i) {
+ put_char(buf, ' ');
+ }
+ }
+}
+
+/******************************************************************************/
+/*
+ * Convert an ansi string to a unicode string. On an error, we return the
+ * original ansi string which is better than returning NULL. nBytes is the
+ * size of the destination buffer (ubuf) in _bytes_.
+ */
+
+char_t *ascToUni(char_t *ubuf, char *str, int nBytes)
+{
+#if UNICODE
+ if (MultiByteToWideChar(CP_ACP, 0, str, nBytes / sizeof(char_t), ubuf,
+ nBytes / sizeof(char_t)) < 0) {
+ return (char_t*) str;
+ }
+#else
+ memcpy(ubuf, str, nBytes);
+#endif
+ return ubuf;
+}
+
+/******************************************************************************/
+/*
+ * Convert a unicode string to an ansi string. On an error, return the
+ * original unicode string which is better than returning NULL.
+ * N.B. nBytes is the number of _bytes_ in the destination buffer, buf.
+ */
+
+char *uniToAsc(char *buf, char_t* ustr, int nBytes)
+{
+#if UNICODE
+ if (WideCharToMultiByte(CP_ACP, 0, ustr, nBytes, buf, nBytes, NULL,
+ NULL) < 0) {
+ return (char*) ustr;
+ }
+#else
+ memcpy(buf, ustr, nBytes);
+#endif
+ return (char*) buf;
+}
+
+
+/******************************************************************************/
+/*
+ * allocate (balloc) a buffer and do ascii to unicode conversion into it.
+ * cp points to the ascii string which must be NULL terminated.
+ * Return a pointer to the unicode buffer which must be bfree'd later.
+ * Return NULL on failure to get buffer.
+ */
+char_t *ballocAscToUni(char * cp)
+{
+ char_t * unip;
+ int ulen;
+
+ ulen = (strlen(cp) + 1) * sizeof(char_t);
+ if ((unip = balloc(B_L, ulen)) == NULL) {
+ return NULL;
+ }
+ ascToUni(unip, cp, ulen);
+ return unip;
+}
+
+/******************************************************************************/
+/*
+ * allocate (balloc) a buffer and do unicode to ascii conversion into it.
+ * unip points to the unicoded string. ulen is the number of characters
+ * in the unicode string including teminating null, if there is one.
+ * Return a pointer to the ascii buffer which must be bfree'd later.
+ * Return NULL on failure to get buffer.
+ */
+char *ballocUniToAsc(char_t * unip, int ulen)
+{
+ char * cp;
+
+ if ((cp = balloc(B_L, ulen)) == NULL) {
+ return NULL;
+ }
+ uniToAsc(cp, unip, ulen);
+ return cp;
+}
+
+/******************************************************************************/
+
diff --git a/cpukit/httpd/ringq.c b/cpukit/httpd/ringq.c
new file mode 100644
index 0000000000..62c4634eef
--- /dev/null
+++ b/cpukit/httpd/ringq.c
@@ -0,0 +1,537 @@
+/*
+ * ringq.c -- Ring queue buffering module
+ *
+ * Copyright (c) GoAhead Software Inc., 1995-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * A ring queue allows maximum utilization of memory for data storage and is
+ * ideal for input/output buffering. This module provides a highly effecient
+ * implementation and a vehicle for dynamic strings.
+ *
+ * WARNING: This is a public implementation and callers have full access to
+ * the queue structure and pointers. Change this module very carefully.
+ *
+ * This module follows the open/close model.
+ *
+ * Operation of a ringq where rq is a pointer to a ringq :
+ *
+ * rq->buflen contains the size of the buffer.
+ * rq->buf will point to the start of the buffer.
+ * rq->servp will point to the first (un-consumed) data byte.
+ * rq->endp will point to the next free location to which new data is added
+ * rq->endbuf will point to one past the end of the buffer.
+ *
+ * Eg. If the ringq contains the data "abcdef", it might look like :
+ *
+ * +-------------------------------------------------------------------+
+ * | | | | | | | | a | b | c | d | e | f | | | | |
+ * +-------------------------------------------------------------------+
+ * ^ ^ ^ ^
+ * | | | |
+ * rq->buf rq->servp rq->endp rq->enduf
+ *
+ * The queue is empty when servp == endp. This means that the queue will hold
+ * at most rq->buflen -1 bytes. It is the fillers responsibility to ensure
+ * the ringq is never filled such that servp == endp.
+ *
+ * It is the fillers responsibility to "wrap" the endp back to point to
+ * rq->buf when the pointer steps past the end. Correspondingly it is the
+ * consumers responsibility to "wrap" the servp when it steps to rq->endbuf.
+ * The ringqPutc and ringqGetc routines will do this automatically.
+ */
+
+/********************************* Includes ***********************************/
+
+#if UEMF
+ #include "uemf.h"
+#else
+ #include "basic/basicInternal.h"
+#endif
+
+/*********************************** Defines **********************************/
+/*
+ * Faster than a function call
+ */
+
+#define RINGQ_LEN(rq) \
+ ((rq->servp > rq->endp) ? \
+ (rq->buflen + (rq->endp - rq->servp)) : \
+ (rq->endp - rq->servp))
+
+/***************************** Forward Declarations ***************************/
+
+static int ringq_grow(ringq_t *rq);
+
+/*********************************** Code *************************************/
+/*
+ * Create a new ringq. "increment" is the amount to increase the size of the
+ * ringq should it need to grow to accomodate data being added. "maxsize" is
+ * an upper limit (sanity level) beyond which the q must not grow. Set maxsize
+ * to -1 to imply no upper limit. The buffer for the ringq is always
+ * dynamically allocated. Set maxsize
+ */
+
+int ringqOpen(ringq_t *rq, int increment, int maxsize)
+{
+ a_assert(rq);
+ a_assert(increment >= 0);
+
+ if ((rq->buf = balloc(B_L, increment)) == NULL) {
+ return -1;
+ }
+ rq->maxsize = maxsize;
+ rq->buflen = increment;
+ rq->increment = increment;
+ rq->endbuf = &rq->buf[rq->buflen];
+ rq->servp = rq->buf;
+ rq->endp = rq->buf;
+ *rq->servp = '\0';
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Delete a ringq and free the ringq buffer.
+ */
+
+void ringqClose(ringq_t *rq)
+{
+ a_assert(rq);
+ a_assert(rq->buflen == (rq->endbuf - rq->buf));
+
+ if (rq == NULL) {
+ return;
+ }
+
+ ringqFlush(rq);
+ bfree(B_L, (char*) rq->buf);
+ rq->buf = NULL;
+}
+
+/******************************************************************************/
+/*
+ * Return the length of the ringq. Users must fill the queue to a high
+ * water mark of at most one less than the queue size.
+ */
+
+int ringqLen(ringq_t *rq)
+{
+ a_assert(rq);
+ a_assert(rq->buflen == (rq->endbuf - rq->buf));
+
+ if (rq->servp > rq->endp) {
+ return rq->buflen + rq->endp - rq->servp;
+ }
+ else {
+ return rq->endp - rq->servp;
+ }
+}
+
+/******************************************************************************/
+/*
+ * Get a byte from the queue
+ */
+
+int ringqGetc(ringq_t *rq)
+{
+ char_t c;
+ char_t* cp;
+
+ a_assert(rq);
+ a_assert(rq->buflen == (rq->endbuf - rq->buf));
+
+ if (rq->servp == rq->endp) {
+ return -1;
+ }
+
+ cp = (char_t*) rq->servp;
+ c = *cp++;
+ rq->servp = (unsigned char *) cp;
+ if (rq->servp >= rq->endbuf) {
+ rq->servp = rq->buf;
+ }
+ return c;
+}
+
+/******************************************************************************/
+/*
+ * Add a char to the queue. Note if being used to store wide strings
+ * this does not add a trailing '\0'. Grow the q as required.
+ */
+
+int ringqPutc(ringq_t *rq, char_t c)
+{
+ char_t* cp;
+
+ a_assert(rq);
+ a_assert(rq->buflen == (rq->endbuf - rq->buf));
+
+ if (ringqPutBlkMax(rq) < (int) sizeof(char_t) && !ringq_grow(rq)) {
+ return -1;
+ }
+
+ cp = (char_t*) rq->endp;
+ *cp++ = (char_t) c;
+ rq->endp = (unsigned char *) cp;
+ if (rq->endp >= rq->endbuf) {
+ rq->endp = rq->buf;
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Insert a wide character at the front of the queue
+ */
+
+int ringqInsertc(ringq_t *rq, char_t c)
+{
+ char_t* cp;
+
+ a_assert(rq);
+ a_assert(rq->buflen == (rq->endbuf - rq->buf));
+
+ if (ringqPutBlkMax(rq) < (int) sizeof(char_t) && !ringq_grow(rq)) {
+ return -1;
+ }
+ if (rq->servp <= rq->buf) {
+ rq->servp = rq->endbuf;
+ }
+ cp = (char_t*) rq->servp;
+ *--cp = (char_t) c;
+ rq->servp = (unsigned char *) cp;
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Add a string to the queue. Add a trailing wide null (two nulls)
+ */
+
+int ringqPutstr(ringq_t *rq, char_t *str)
+{
+ int rc;
+
+ a_assert(rq);
+ a_assert(str);
+ a_assert(rq->buflen == (rq->endbuf - rq->buf));
+
+ rc = ringqPutBlk(rq, (unsigned char*) str, gstrlen(str) * sizeof(char_t));
+ *((char_t*) rq->endp) = (char_t) '\0';
+ return rc;
+}
+
+/******************************************************************************/
+#if UNICODE
+/*
+ * Get a byte from the queue
+ */
+
+int ringqGetcA(ringq_t *rq)
+{
+ unsigned char c;
+
+ a_assert(rq);
+ a_assert(rq->buflen == (rq->endbuf - rq->buf));
+
+ if (rq->servp == rq->endp) {
+ return -1;
+ }
+
+ c = *rq->servp++;
+ if (rq->servp >= rq->endbuf) {
+ rq->servp = rq->buf;
+ }
+ return c;
+}
+
+/******************************************************************************/
+/*
+ * Add a byte to the queue. Note if being used to store strings this does not
+ * add a trailing '\0'. Grow the q as required.
+ */
+
+int ringqPutcA(ringq_t *rq, char c)
+{
+ a_assert(rq);
+ a_assert(rq->buflen == (rq->endbuf - rq->buf));
+
+ if (ringqPutBlkMax(rq) == 0 && !ringq_grow(rq)) {
+ return -1;
+ }
+
+ *rq->endp++ = (unsigned char) c;
+ if (rq->endp >= rq->endbuf) {
+ rq->endp = rq->buf;
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Insert a byte at the front of the queue
+ */
+
+int ringqInsertcA(ringq_t *rq, char c)
+{
+ a_assert(rq);
+ a_assert(rq->buflen == (rq->endbuf - rq->buf));
+
+ if (ringqPutBlkMax(rq) == 0 && !ringq_grow(rq)) {
+ return -1;
+ }
+ if (rq->servp <= rq->buf) {
+ rq->servp = rq->endbuf;
+ }
+ *--rq->servp = (unsigned char) c;
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Add a string to the queue. Add a trailing null (not really in the q).
+ * ie. beyond the last valid byte.
+ */
+
+int ringqPutstrA(ringq_t *rq, char *str)
+{
+ int rc;
+
+ a_assert(rq);
+ a_assert(str);
+ a_assert(rq->buflen == (rq->endbuf - rq->buf));
+
+ rc = ringqPutBlk(rq, (unsigned char*) str, strlen(str));
+ rq->endp[0] = '\0';
+ return rc;
+}
+
+#endif /* UNICODE */
+/******************************************************************************/
+/*
+ * Add a block of data to the ringq. Return the number of bytes added.
+ * Grow the q as required.
+ */
+
+int ringqPutBlk(ringq_t *rq, unsigned char *buf, int size)
+{
+ int this, bytes_put;
+
+ a_assert(rq);
+ a_assert(rq->buflen == (rq->endbuf - rq->buf));
+ a_assert(buf);
+ a_assert(0 <= size);
+
+/*
+ * Loop adding the maximum bytes we can add in a single straight line copy
+ */
+ bytes_put = 0;
+ while (size > 0) {
+ this = min(ringqPutBlkMax(rq), size);
+ if (this <= 0) {
+ if (! ringq_grow(rq)) {
+ break;
+ }
+ this = min(ringqPutBlkMax(rq), size);
+ }
+
+ memcpy(rq->endp, buf, this);
+ buf += this;
+ rq->endp += this;
+ size -= this;
+ bytes_put += this;
+
+ if (rq->endp >= rq->endbuf) {
+ rq->endp = rq->buf;
+ }
+ }
+ return bytes_put;
+}
+
+/******************************************************************************/
+/*
+ * Get a block of data from the ringq. Return the number of bytes returned.
+ */
+
+int ringqGetBlk(ringq_t *rq, unsigned char *buf, int size)
+{
+ int this, bytes_read;
+
+ a_assert(rq);
+ a_assert(rq->buflen == (rq->endbuf - rq->buf));
+ a_assert(buf);
+ a_assert(0 <= size && size < rq->buflen);
+
+/*
+ * Loop getting the maximum bytes we can get in a single straight line copy
+ */
+ bytes_read = 0;
+ while (size > 0) {
+ this = ringqGetBlkMax(rq);
+ this = min(this, size);
+ if (this <= 0) {
+ break;
+ }
+
+ memcpy(buf, rq->servp, this);
+ buf += this;
+ rq->servp += this;
+ size -= this;
+ bytes_read += this;
+
+ if (rq->servp >= rq->endbuf) {
+ rq->servp = rq->buf;
+ }
+ }
+ return bytes_read;
+}
+
+/******************************************************************************/
+/*
+ * Return the maximum number of bytes the ring q can accept via a single
+ * block copy. Useful if the user is doing their own data insertion.
+ */
+
+int ringqPutBlkMax(ringq_t *rq)
+{
+ int space, in_a_line;
+
+ a_assert(rq);
+ a_assert(rq->buflen == (rq->endbuf - rq->buf));
+
+ space = rq->buflen - RINGQ_LEN(rq) - 1;
+ in_a_line = rq->endbuf - rq->endp;
+
+ return min(in_a_line, space);
+}
+
+/******************************************************************************/
+/*
+ * Return the maximum number of bytes the ring q can provide via a single
+ * block copy. Useful if the user is doing their own data retrieval.
+ */
+
+int ringqGetBlkMax(ringq_t *rq)
+{
+ int len, in_a_line;
+
+ a_assert(rq);
+ a_assert(rq->buflen == (rq->endbuf - rq->buf));
+
+ len = RINGQ_LEN(rq);
+ in_a_line = rq->endbuf - rq->servp;
+
+ return min(in_a_line, len);
+}
+
+/******************************************************************************/
+/*
+ * Adjust the endp pointer after the user has copied data into the queue.
+ */
+
+void ringqPutBlkAdj(ringq_t *rq, int size)
+{
+ a_assert(rq);
+ a_assert(rq->buflen == (rq->endbuf - rq->buf));
+ a_assert(0 <= size && size < rq->buflen);
+
+ rq->endp += size;
+ if (rq->endp >= rq->endbuf) {
+ rq->endp -= rq->buflen;
+ }
+/*
+ * Flush the queue if the endp pointer is corrupted via a bad size
+ */
+ if (rq->endp >= rq->endbuf) {
+ error(E_L, E_LOG, T("Bad end pointer"));
+ ringqFlush(rq);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Adjust the servp pointer after the user has copied data from the queue.
+ */
+
+void ringqGetBlkAdj(ringq_t *rq, int size)
+{
+ a_assert(rq);
+ a_assert(rq->buflen == (rq->endbuf - rq->buf));
+ a_assert(0 < size && size < rq->buflen);
+
+ rq->servp += size;
+ if (rq->servp >= rq->endbuf) {
+ rq->servp -= rq->buflen;
+ }
+/*
+ * Flush the queue if the servp pointer is corrupted via a bad size
+ */
+ if (rq->servp >= rq->endbuf) {
+ error(E_L, E_LOG, T("Bad serv pointer"));
+ ringqFlush(rq);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Flush all data in a ring q. Reset the pointers.
+ */
+
+void ringqFlush(ringq_t *rq)
+{
+ a_assert(rq);
+
+ rq->servp = rq->buf;
+ rq->endp = rq->buf;
+ *rq->servp = '\0';
+}
+
+/******************************************************************************/
+/*
+ * Grow the buffer. Return true if the buffer can be grown. Grow using
+ * the increment size specified when opening the ringq. Don't grow beyond
+ * the maximum possible size.
+ */
+
+static int ringq_grow(ringq_t *rq)
+{
+ unsigned char *newbuf;
+ int len;
+
+ a_assert(rq);
+
+ if (rq->maxsize >= 0 && rq->buflen >= rq->maxsize) {
+ return 0;
+ }
+
+ len = ringqLen(rq);
+
+ if ((newbuf = balloc(B_L, rq->buflen + rq->increment)) == NULL) {
+ return 0;
+ }
+ ringqGetBlk(rq, newbuf, ringqLen(rq));
+ bfree(B_L, (char*) rq->buf);
+
+#if OLD
+ rq->endp = &newbuf[endp];
+ rq->servp = &newbuf[servp];
+ rq->endbuf = &newbuf[rq->buflen];
+ rq->buf = newbuf;
+#endif
+
+ rq->buflen += rq->increment;
+ rq->endp = newbuf;
+ rq->servp = newbuf;
+ rq->buf = newbuf;
+ rq->endbuf = &rq->buf[rq->buflen];
+
+ ringqPutBlk(rq, newbuf, len);
+ return 1;
+}
+
+/******************************************************************************/
diff --git a/cpukit/httpd/rom.c b/cpukit/httpd/rom.c
new file mode 100644
index 0000000000..4e6c8f1e93
--- /dev/null
+++ b/cpukit/httpd/rom.c
@@ -0,0 +1,198 @@
+/*
+ * rom.c -- Support for ROMed page retrieval.
+ *
+ * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * This module provides web page retrieval from compiled web pages. Use the
+ * webcomp program to compile web pages and link into the GoAhead WebServer.
+ * This module uses a hashed symbol table for fast page lookup.
+ *
+ * Usage: webcomp -f webPageFileList -p Prefix >webrom.c
+ */
+
+/********************************* Includes ***********************************/
+
+#include <stdlib.h>
+
+#if CE
+#define EINVAL 22
+#define EBADF 9
+#else
+#include <errno.h>
+#endif
+
+#include "wsIntrn.h"
+
+/******************************** Local Data **********************************/
+
+#if WEBS_PAGE_ROM
+
+sym_fd_t romTab; /* Symbol table for web pages */
+
+/*********************************** Code *************************************/
+/*
+ * Open the ROM module
+ */
+
+int websRomOpen()
+{
+ websRomPageIndexType *wip;
+ int nchars;
+ char_t name[SYM_MAX];
+
+ romTab = symOpen(64);
+
+ for (wip = websRomPageIndex; wip->path; wip++) {
+ gstrncpy(name, wip->path, SYM_MAX);
+ nchars = gstrlen(name) - 1;
+ if (nchars > 0 &&
+ (name[nchars] == '/' || name[nchars] == '\\')) {
+ name[nchars] = '\0';
+ }
+ symEnter(romTab, name, valueInteger((int) wip), 0);
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Close the ROM module
+ */
+
+void websRomClose()
+{
+ symClose(romTab, NULL);
+}
+
+/******************************************************************************/
+/*
+ * Open a web page
+ */
+
+int websRomPageOpen(webs_t wp, char_t *path, int mode, int perm)
+{
+ websRomPageIndexType *wip;
+ sym_t *sp;
+
+ a_assert(websValid(wp));
+ a_assert(path && *path);
+
+ if ((sp = symLookup(romTab, path)) == NULL) {
+ return -1;
+ }
+ wip = (websRomPageIndexType*) sp->content.value.integer;
+ wip->pos = 0;
+ return (wp->docfd = wip - websRomPageIndex);
+}
+
+/******************************************************************************/
+/*
+ * Close a web page
+ */
+
+void websRomPageClose(int fd)
+{
+}
+
+/******************************************************************************/
+/*
+ * Stat a web page
+ */
+
+int websRomPageStat(char_t *path, websStatType* sbuf)
+{
+ websRomPageIndexType *wip;
+ sym_t *sp;
+
+ a_assert(path && *path);
+
+ if ((sp = symLookup(romTab, path)) == NULL) {
+ return -1;
+ }
+ wip = (websRomPageIndexType*) sp->content.value.integer;
+
+ memset(sbuf, 0, sizeof(websStatType));
+ sbuf->size = wip->size;
+ if (wip->page == NULL) {
+ sbuf->isDir = 1;
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Read a web page
+ */
+
+int websRomPageReadData(webs_t wp, char *buf, int nBytes)
+{
+ websRomPageIndexType *wip;
+ int len;
+
+ a_assert(websValid(wp));
+ a_assert(buf);
+ a_assert(wp->docfd >= 0);
+
+ wip = &websRomPageIndex[wp->docfd];
+
+ len = min(wip->size - wip->pos, nBytes);
+ memcpy(buf, &wip->page[wip->pos], len);
+ wip->pos += len;
+ return len;
+}
+
+/******************************************************************************/
+/*
+ * Position a web page
+ */
+
+long websRomPageSeek(webs_t wp, long offset, int origin)
+{
+ websRomPageIndexType *wip;
+ long pos;
+
+ a_assert(websValid(wp));
+ a_assert(origin == SEEK_SET || origin == SEEK_CUR || origin == SEEK_END);
+ a_assert(wp->docfd >= 0);
+
+ wip = &websRomPageIndex[wp->docfd];
+
+ if (origin != SEEK_SET && origin != SEEK_CUR && origin != SEEK_END) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (wp->docfd < 0) {
+ errno = EBADF;
+ return -1;
+ }
+
+ pos = offset;
+ switch (origin) {
+ case SEEK_CUR:
+ pos = wip->pos + offset;
+ break;
+ case SEEK_END:
+ pos = wip->size + offset;
+ break;
+ default:
+ break;
+ }
+
+ if (pos < 0) {
+ errno = EBADF;
+ return -1;
+ }
+
+ return (wip->pos = pos);
+}
+
+#endif
+
+/******************************************************************************/
diff --git a/cpukit/httpd/rtems_webserver.h b/cpukit/httpd/rtems_webserver.h
new file mode 100644
index 0000000000..f1e4046ef5
--- /dev/null
+++ b/cpukit/httpd/rtems_webserver.h
@@ -0,0 +1,7 @@
+/*
+ * rtems_webserver.h --
+ *
+ */
+
+int rtems_initialize_webserver();
+
diff --git a/cpukit/httpd/security.c b/cpukit/httpd/security.c
new file mode 100644
index 0000000000..01d4000f40
--- /dev/null
+++ b/cpukit/httpd/security.c
@@ -0,0 +1,109 @@
+/*
+ * security.c -- Security handler
+ *
+ * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * This module provides a basic security policy. It supports a single global
+ * password and ignores the username. Encoding/decoding of the password is
+ * -not- done.
+ */
+
+/********************************* Includes ***********************************/
+
+#include "wsIntrn.h"
+
+/******************************** Local Data **********************************/
+
+static char_t websPassword[WEBS_MAX_PASS]; /* Access password (decoded) */
+
+/*********************************** Code *************************************/
+/*
+ * Determine if this request should be honored
+ */
+
+int websSecurityHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg,
+ char_t *url, char_t *path, char_t *query)
+{
+ char_t *type, *password;
+ int flags;
+
+ a_assert(websValid(wp));
+ a_assert(url && *url);
+ a_assert(path && *path);
+
+/*
+ * Get the critical request details
+ */
+ type = websGetRequestType(wp);
+ password = websGetRequestPassword(wp);
+ flags = websGetRequestFlags(wp);
+
+/*
+ * Validate the users password if required (local access is always allowed)
+ * We compare the decoded form of the password.
+ */
+ if (*websPassword && !(flags & WEBS_LOCAL_REQUEST)) {
+
+ if (password && *password) {
+ if (gstrcmp(password, websPassword) != 0) {
+ websStats.access++;
+ websError(wp, 200, T("Access Denied\nWrong Password"));
+ websSetPassword(T(""));
+ return 1;
+ }
+ } else {
+/*
+ * This will cause the browser to display a password / username
+ * dialog
+ */
+ websStats.errors++;
+ websError(wp, 401, T("<html><head>Access Denied</head><body>\r\n\
+ Access to this document requires a password.</body>\
+ </html>\r\n"));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Delete the default security handler
+ */
+
+void websSecurityDelete()
+{
+ websUrlHandlerDelete(websSecurityHandler);
+}
+
+/******************************************************************************/
+/*
+ * Store the new password, expect a decoded password. Store in websPassword in
+ * the decoded form.
+ */
+
+void websSetPassword(char_t *password)
+{
+ a_assert(password);
+
+ gstrncpy(websPassword, password, TSZ(websPassword));
+}
+
+/******************************************************************************/
+/*
+ * Get password, return the decoded form
+ */
+
+char_t *websGetPassword()
+{
+ return websPassword;
+}
+
+/******************************************************************************/
+
diff --git a/cpukit/httpd/socket.c b/cpukit/httpd/socket.c
new file mode 100644
index 0000000000..996255081d
--- /dev/null
+++ b/cpukit/httpd/socket.c
@@ -0,0 +1,991 @@
+/*
+ * socket.c -- Socket support module for UNIX
+ *
+ * Copyright (c) Go Ahead, 1995-1999
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * SCO Unix Socket Module. This supports non-blocking buffered socket I/O.
+ */
+
+/********************************** Includes **********************************/
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "uemf.h"
+
+/*********************************** Defines **********************************/
+
+typedef struct {
+ char host[64]; /* Host name */
+ ringq_t inBuf; /* Input ring queue */
+ ringq_t outBuf; /* Output ring queue */
+ ringq_t lineBuf; /* Line ring queue */
+ socketAccept_t accept; /* Accept handler */
+ socketHandler_t handler; /* User I/O handler */
+ int handler_data; /* User handler data */
+ int sid; /* Index into socket[] */
+ int port; /* Port to listen on */
+ int flags; /* Current state flags */
+ int readyMask; /* Events now ready */
+ int interestMask; /* Events interest */
+ int error; /* Last error */
+ int sock; /* Actual socket handle */
+} socket_t;
+
+/************************************ Locals **********************************/
+
+static socket_t** socketList; /* List of open sockets */
+static int socketMax; /* Maximum size of socket */
+static int socketHighestFd = -1; /* Highest socket fd opened */
+
+/***************************** Forward Declarations ***************************/
+
+static int socketAlloc(char* host, int port, socketAccept_t accept, int flags);
+static void socketFree(int sid);
+static void socketAccept(socket_t* sp);
+static int socketGetInput(int sid, char* buf, int toRead, int* errCode);
+static int socketDoOutput(socket_t* sp, char* buf, int toWrite, int* errCode);
+static int socketDoEvent(socket_t *sp);
+static int socketGetError();
+static int socketWaitForEvent(socket_t* sp, int events, int* errCode);
+static int socketNonBlock(socket_t *sp);
+static socket_t* socketPtr(int sid);
+
+/*********************************** Code *************************************/
+/*
+ * Open socket module
+ */
+
+int socketOpen()
+{
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Close the socket module, by closing all open connections
+ */
+
+void socketClose()
+{
+ int i;
+
+ for (i = socketMax; i >= 0; i--) {
+ if (socketList && socketList[i]) {
+ socketCloseConnection(i);
+ }
+ }
+}
+
+/******************************************************************************/
+/*
+ * Open a client or server socket. Host is NULL if we want server capability.
+ */
+
+int socketOpenConnection(char* host, int port, socketAccept_t accept, int flags)
+{
+ socket_t *sp;
+ struct sockaddr_in sockaddr;
+ struct hostent *hostent; /* Host database entry */
+ int sid, rc;
+
+/*
+ * Allocate a socket structure
+ */
+ if ((sid = socketAlloc(host, port, accept, flags)) < 0) {
+ return -1;
+ }
+ sp = socketList[sid];
+ a_assert(sp);
+
+/*
+ * Create the socket address structure
+ */
+ memset((char *) &sockaddr, '\0', sizeof(struct sockaddr_in));
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons((short) (port & 0xFFFF));
+
+ if (host == NULL) {
+ sockaddr.sin_addr.s_addr = INADDR_ANY;
+ } else {
+ sockaddr.sin_addr.s_addr = inet_addr(host);
+ if (sockaddr.sin_addr.s_addr == INADDR_NONE) {
+ hostent = gethostbyname(host);
+ if (hostent != NULL) {
+ memcpy((char *) &sockaddr.sin_addr,
+ (char *) hostent->h_addr_list[0],
+ (size_t) hostent->h_length);
+ } else {
+ errno = ENXIO;
+ socketFree(sid);
+ return -1;
+ }
+ }
+ }
+
+/*
+ * Create the socket. Set the close on exec flag so children don't
+ * inherit the socket.
+ */
+ sp->sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sp->sock < 0) {
+ socketFree(sid);
+ return -1;
+ }
+ fcntl(sp->sock, F_SETFD, FD_CLOEXEC);
+ socketHighestFd = max(socketHighestFd, sp->sock);
+
+/*
+ * Host is set if we are the client
+ */
+ if (host) {
+/*
+ * Connect to the remote server
+ */
+ if (connect(sp->sock, (struct sockaddr *) &sockaddr,
+ sizeof(sockaddr)) < 0) {
+ socketFree(sid);
+ return -1;
+ }
+ socketNonBlock(sp);
+
+ } else {
+/*
+ * Bind to the socket endpoint with resule and the call listen()
+ ** to start listening
+ */
+ rc = 1;
+ setsockopt(sp->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&rc, sizeof(rc));
+ if (bind(sp->sock, (struct sockaddr *) &sockaddr, sizeof(sockaddr))
+ < 0) {
+ socketFree(sid);
+ return -1;
+ }
+ sp->flags |= SOCKET_LISTENING;
+
+ if (listen(sp->sock, SOMAXCONN) < 0) {
+ socketFree(sid);
+ return -1;
+ }
+ sp->interestMask = SOCKET_READABLE;
+ }
+ return sid;
+}
+
+/******************************************************************************/
+/*
+ * Close a socket
+ */
+
+void socketCloseConnection(int sid)
+{
+ socket_t* sp;
+
+ if ((sp = socketPtr(sid)) == NULL) {
+ return;
+ }
+
+/*
+ * We always flush all output before closing. Unlink from the emf event
+ * mechanism and then free (and close) the connection
+ */
+ socketFlush(sid, 1);
+ socketFree(sid);
+}
+
+/******************************************************************************/
+/*
+ * Accept a connection. Called by socketDoEvent
+ */
+
+static void socketAccept(socket_t* sp)
+{
+ struct sockaddr_in addr;
+ socket_t *nsp;
+ size_t len;
+ int newSock, nid;
+
+ a_assert(sp);
+
+/*
+ * Accept the connection and prevent inheriting by children (F_SETFD)
+ */
+ len = sizeof(struct sockaddr_in);
+ if ((newSock = accept(sp->sock, (struct sockaddr *) &addr, &len)) < 0) {
+ return;
+ }
+ fcntl(newSock, F_SETFD, FD_CLOEXEC);
+ socketHighestFd = max(socketHighestFd, newSock);
+
+/*
+ * Create a socket structure and insert into the socket list
+ */
+ nid = socketAlloc(sp->host, sp->port, sp->accept, 0);
+ nsp = socketList[nid];
+ a_assert(nsp);
+ nsp->sock = newSock;
+
+ if (nsp == NULL) {
+ return;
+ }
+/*
+ * Call the user accept callback, the user must call socketCreateHandler
+ * to register for further events of interest.
+ */
+ if (sp->accept != NULL) {
+ if ((sp->accept)(nid, inet_ntoa(addr.sin_addr),
+ ntohs(addr.sin_port)) < 0) {
+ socketFree(nid);
+ return;
+ }
+ }
+ socketNonBlock(nsp);
+}
+
+/******************************************************************************/
+/*
+ * Write to a socket. This may block if the underlying socket cannot
+ * absorb the data. Returns -1 on error, otherwise the number of bytes
+ * written.
+ */
+
+int socketWrite(int sid, char* buf, int bufsize)
+{
+ socket_t* sp;
+ ringq_t* rq;
+ int len, bytesWritten, room;
+
+ a_assert(buf);
+ a_assert(bufsize >= 0);
+
+ if ((sp = socketPtr(sid)) == NULL) {
+ return -1;
+ }
+
+/*
+ * Loop adding as much data to the output ringq as we can absorb
+ * Flush when the ringq is too full and continue.
+ */
+ rq = &sp->outBuf;
+ for (bytesWritten = 0; bufsize > 0; ) {
+ if ((room = ringqPutBlkMax(rq)) == 0) {
+ if (socketFlush(sid, 0) < 0) {
+ return -1;
+ }
+ if ((room = ringqPutBlkMax(rq)) == 0) {
+ break;
+ }
+ continue;
+ }
+ len = min(room, bufsize);
+ ringqPutBlk(rq, (unsigned char*) buf, len);
+ bytesWritten += len;
+ bufsize -= len;
+ buf += len;
+ }
+ return bytesWritten;
+}
+
+/******************************************************************************/
+/*
+ * Read from a socket. Return the number of bytes read if successful. This
+ * may be less than the requested "bufsize" and may be zero. Return -1 for
+ * errors. Return 0 for EOF. Otherwise return the number of bytes read. Since
+ * this may be zero, callers should use socketEof() to distinguish between
+ * this and EOF. Note: this ignores the line buffer, so a previous socketGets
+ * which read a partial line may cause a subsequent socketRead to miss
+ * some data.
+ */
+
+int socketRead(int sid, char* buf, int bufsize)
+{
+ socket_t* sp;
+ ringq_t* rq;
+ int len, room, errCode, bytesRead;
+
+ a_assert(buf);
+ a_assert(bufsize > 0);
+
+ if ((sp = socketPtr(sid)) == NULL) {
+ return -1;
+ }
+
+ if (sp->flags & SOCKET_EOF) {
+ return 0;
+ }
+
+ rq = &sp->inBuf;
+ for (bytesRead = 0; bufsize > 0; ) {
+ len = min(ringqLen(rq), bufsize);
+ if (len <= 0) {
+ room = ringqPutBlkMax(rq);
+ len = socketGetInput(sid, (char*) rq->endp, room, &errCode);
+ if (len < 0) {
+ if (errCode == EWOULDBLOCK) {
+ if (bytesRead >= 0) {
+ return bytesRead;
+ }
+ }
+ return -1;
+
+ } else if (len == 0) {
+/*
+ * This is EOF, but we may have already read some data so pass that
+ * back first before notifying EOF. The next read will return 0
+ * to indicate EOF.
+ */
+ return bytesRead;
+ }
+ ringqPutBlkAdj(rq, len);
+ len = min(len, bufsize);
+ }
+ memcpy(&buf[bytesRead], rq->servp, len);
+ ringqGetBlkAdj(rq, len);
+ bufsize -= len;
+ bytesRead += len;
+ }
+ return bytesRead;
+}
+
+/******************************************************************************/
+/*
+ * Get a string from a socket. This returns data in *buf in a malloced string
+ * after trimming the '\n'. If there is zero bytes returned, *buf will be set
+ * to NULL. It returns -1 for error, EOF or when no complete line yet read.
+ * Otherwise the length of the line is returned. If a partial line is read
+ * socketInputBuffered or socketEof can be used to distinguish between EOF
+ * and partial line still buffered. This routine eats and ignores carriage
+ * returns.
+ */
+
+int socketGets(int sid, char** buf)
+{
+ socket_t* sp;
+ ringq_t* lq;
+ char c;
+ int rc, len;
+
+ a_assert(buf);
+
+ if ((sp = socketPtr(sid)) == NULL) {
+ return -1;
+ }
+ lq = &sp->lineBuf;
+
+ while (1) {
+
+ if ((rc = socketRead(sid, &c, 1)) < 0) {
+ return rc;
+
+ } else if (rc == 0) {
+/*
+ * If there is a partial line and we are at EOF, pretend we saw a '\n'
+ */
+ if (ringqLen(lq) > 0 && (sp->flags & SOCKET_EOF)) {
+ c = '\n';
+ } else {
+ return -1;
+ }
+ }
+/*
+ * If a newline is seen, return the data excluding the new line to the
+ * caller. If carriage return is seen, just eat it.
+ */
+ if (c == '\n') {
+ len = ringqLen(lq);
+ if (len > 0) {
+ if ((*buf = balloc(B_L, len + 1)) == NULL) {
+ return -1;
+ }
+ memset(*buf, 0, len + 1);
+ ringqGetBlk(lq, (unsigned char*) *buf, len);
+ } else {
+ *buf = NULL;
+ }
+ return len;
+
+ } else if (c == '\r') {
+ continue;
+ }
+ ringqPutc(lq, c);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Flush a socket. Do not wait, just initiate output and return.
+ * This will return -1 on errors and 0 if successful.
+ */
+
+int socketFlush(int sid, int block)
+{
+ socket_t* sp;
+ ringq_t* rq;
+ int len, bytesWritten, errCode;
+
+ a_assert(block == 0 || block == 1);
+
+ if ((sp = socketPtr(sid)) == NULL) {
+ return -1;
+ }
+ rq = &sp->outBuf;
+
+/*
+ * Set the background flushing flag which socketDoEvent will check to
+ * continue the flush.
+ */
+ if (!block) {
+ sp->flags |= SOCKET_FLUSHING;
+ }
+
+/*
+ * Break from loop if not blocking after initiating output. If we are blocking
+ * we wait for a write event.
+ */
+ while (ringqLen(rq) > 0) {
+ len = ringqGetBlkMax(&sp->outBuf);
+ bytesWritten = socketDoOutput(sp, (char*) rq->servp, len, &errCode);
+ if (bytesWritten < 0) {
+ if (errCode == EINTR) {
+ continue;
+ } else if (errCode == EWOULDBLOCK || errCode == EAGAIN) {
+ if (! block) {
+ return 0;
+ }
+ if (socketWaitForEvent(sp, SOCKET_WRITABLE | SOCKET_EXCEPTION,
+ &errCode)) {
+ continue;
+ }
+ }
+ return -1;
+ }
+ ringqGetBlkAdj(rq, bytesWritten);
+ if (! block) {
+ break;
+ }
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Return the count of input characters buffered. We look at both the line
+ * buffer and the input (raw) buffer. Return -1 on error or EOF.
+ */
+
+int socketInputBuffered(int sid)
+{
+ socket_t* sp;
+
+ if ((sp = socketPtr(sid)) == NULL) {
+ return -1;
+ }
+ if (socketEof(sid)) {
+ return -1;
+ }
+ return ringqLen(&sp->lineBuf) + ringqLen(&sp->inBuf);
+}
+
+/******************************************************************************/
+/*
+ * Return true if EOF
+ */
+
+int socketEof(int sid)
+{
+ socket_t* sp;
+
+ if ((sp = socketPtr(sid)) == NULL) {
+ return -1;
+ }
+ return sp->flags & SOCKET_EOF;
+}
+
+/******************************************************************************/
+/*
+ * Create a user handler for this socket. The handler called whenever there
+ * is an event of interest as defined by interestMask (SOCKET_READABLE, ...)
+ */
+
+void socketCreateHandler(int sid, int interestMask, socketHandler_t handler,
+ int data)
+{
+ socket_t* sp;
+
+ if ((sp = socketPtr(sid)) == NULL) {
+ return;
+ }
+ sp->handler = handler;
+ sp->handler_data = data;
+ sp->interestMask = interestMask;
+}
+
+/******************************************************************************/
+/*
+ * Delete a handler
+ */
+
+void socketDeleteHandler(int sid)
+{
+ socket_t* sp;
+
+ if ((sp = socketPtr(sid)) == NULL) {
+ return;
+ }
+ sp->handler = NULL;
+ sp->interestMask = 0;
+}
+
+/******************************************************************************/
+/*
+ * Get more input from the socket and return in buf.
+ * Returns 0 for EOF, -1 for errors and otherwise the number of bytes read.
+ */
+
+static int socketGetInput(int sid, char* buf, int toRead, int* errCode)
+{
+ struct sockaddr_in server;
+ socket_t* sp;
+ int len, bytesRead;
+
+ a_assert(buf);
+ a_assert(errCode);
+
+ *errCode = 0;
+
+ if ((sp = socketPtr(sid)) == NULL) {
+ return -1;
+ }
+
+/*
+ * If we have previously seen an EOF condition, then just return
+ */
+ if (sp->flags & SOCKET_EOF) {
+ return 0;
+ }
+
+/*
+ * Read the data
+ */
+ if (sp->flags & SOCKET_BROADCAST) {
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = INADDR_BROADCAST;
+ server.sin_port = htons((short)(sp->port & 0xFFFF));
+ len = sizeof(server);
+ bytesRead = recvfrom(sp->sock, buf, toRead, 0,
+ (struct sockaddr*) &server, &len);
+ } else {
+ bytesRead = recv(sp->sock, buf, toRead, 0);
+ }
+
+ if (bytesRead < 0) {
+ if (errno == ECONNRESET) {
+ return 0;
+ }
+ *errCode = socketGetError();
+ return -1;
+
+ } else if (bytesRead == 0) {
+ sp->flags |= SOCKET_EOF;
+ }
+ return bytesRead;
+}
+
+/******************************************************************************/
+/*
+ * Socket output procedure. Return -1 on errors otherwise return the number
+ * of bytes written.
+ */
+
+static int socketDoOutput(socket_t* sp, char* buf, int toWrite, int* errCode)
+{
+ struct sockaddr_in server;
+ int bytes;
+
+ a_assert(sp);
+ a_assert(buf);
+ a_assert(toWrite > 0);
+ a_assert(errCode);
+
+ *errCode = 0;
+
+/*
+ * Write the data
+ */
+ if (sp->flags & SOCKET_BROADCAST) {
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = INADDR_BROADCAST;
+ server.sin_port = htons((short)(sp->port & 0xFFFF));
+ bytes = sendto(sp->sock, buf, toWrite, 0,
+ (struct sockaddr*) &server, sizeof(server));
+ } else {
+ bytes = send(sp->sock, buf, toWrite, 0);
+ }
+
+ if (bytes == 0 && bytes != toWrite) {
+ *errCode = EWOULDBLOCK;
+ return -1;
+ }
+
+ if (bytes < 0) {
+ *errCode = socketGetError();
+ }
+ return bytes;
+}
+
+/******************************************************************************/
+/*
+ * Return TRUE if there is a socket with an event ready to process,
+ */
+
+int socketReady()
+{
+ socket_t *sp;
+ int i;
+
+ for (i = 0; i < socketMax; i++) {
+ if ((sp = socketList[i]) == NULL) {
+ continue;
+ }
+ if (sp->readyMask & sp->interestMask) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Wait for a handle to become readable or writable and return a number of
+ * noticed events.
+ */
+
+int socketSelect()
+{
+ socket_t *sp;
+ fd_mask *readFds, *writeFds, *exceptFds;
+ int sid, len, nwords, index, bit, nEvents;
+
+/*
+ * Allocate and zero the select masks
+ */
+ nwords = (socketHighestFd + NFDBITS - 1) / NFDBITS;
+ len = nwords * sizeof(int);
+
+ readFds = balloc(B_L, len);
+ memset(readFds, 0, len);
+ writeFds = balloc(B_L, len);
+ memset(writeFds, 0, len);
+ exceptFds = balloc(B_L, len);
+ memset(exceptFds, 0, len);
+
+/*
+ * Set the select event masks for events to watch
+ */
+ for (sid = 0; sid < socketMax; sid++) {
+ if ((sp = socketList[sid]) == NULL) {
+ continue;
+ }
+ a_assert(sp);
+
+/*
+ * Initialize the ready masks and compute the mask offsets.
+ */
+ index = sp->sock / (NBBY * sizeof(fd_mask));
+ bit = 1 << (sp->sock % (NBBY * sizeof(fd_mask)));
+
+/*
+ * Set the appropriate bit in the ready masks for the sp->sock.
+ */
+ if (sp->interestMask & SOCKET_READABLE) {
+ readFds[index] |= bit;
+ }
+ if (sp->interestMask & SOCKET_WRITABLE) {
+ writeFds[index] |= bit;
+ }
+ if (sp->interestMask & SOCKET_EXCEPTION) {
+ exceptFds[index] |= bit;
+ }
+ }
+
+/*
+ * Wait for the event or a timeout.
+ */
+ nEvents = select(socketHighestFd + 1, (fd_set *) readFds,
+ (fd_set *) writeFds, (fd_set *) exceptFds, NULL);
+ if (nEvents > 0) {
+ for (sid = 0; sid < socketMax; sid++) {
+ if ((sp = socketList[sid]) == NULL) {
+ continue;
+ }
+
+ index = sp->sock / (NBBY * sizeof(fd_mask));
+ bit = 1 << (sp->sock % (NBBY * sizeof(fd_mask)));
+
+ if (readFds[index] & bit) {
+ sp->readyMask |= SOCKET_READABLE;
+ }
+ if (writeFds[index] & bit) {
+ sp->readyMask |= SOCKET_WRITABLE;
+ }
+ if (exceptFds[index] & bit) {
+ sp->readyMask |= SOCKET_EXCEPTION;
+ }
+ }
+ }
+
+ bfree(B_L, readFds);
+ bfree(B_L, writeFds);
+ bfree(B_L, exceptFds);
+
+ return nEvents;
+}
+
+/******************************************************************************/
+/*
+ * Process socket events
+ */
+
+void socketProcess()
+{
+ socket_t *sp;
+ int sid;
+
+/*
+ * Process each socket
+ */
+ for (sid = 0; sid < socketMax; sid++) {
+ if ((sp = socketList[sid]) == NULL) {
+ continue;
+ }
+ if ((sp->readyMask & sp->interestMask) ||
+ ((sp->interestMask & SOCKET_READABLE) &&
+ socketInputBuffered(sid))) {
+ socketDoEvent(sp);
+ }
+ }
+}
+
+/******************************************************************************/
+/*
+ * Process and event on the event queue
+ */
+
+static int socketDoEvent(socket_t *sp)
+{
+ ringq_t* rq;
+ int sid;
+
+ a_assert(sp);
+
+ sid = sp->sid;
+ if (sp->readyMask & SOCKET_READABLE) {
+ if (sp->flags & SOCKET_LISTENING) {
+ socketAccept(sp);
+ sp->readyMask = 0;
+ return 1;
+ }
+ } else {
+/*
+ * If there is still read data in the buffers, trigger the read handler
+ * NOTE: this may busy spin if the read handler doesn't read the data
+ */
+ if (sp->interestMask & SOCKET_READABLE && socketInputBuffered(sid)) {
+ sp->readyMask |= SOCKET_READABLE;
+ }
+ }
+
+
+/*
+ * If now writable and flushing in the background, continue flushing
+ */
+ if (sp->readyMask & SOCKET_WRITABLE) {
+ if (sp->flags & SOCKET_FLUSHING) {
+ rq = &sp->outBuf;
+ if (ringqLen(rq) > 0) {
+ socketFlush(sp->sid, 0);
+ } else {
+ sp->flags &= ~SOCKET_FLUSHING;
+ }
+ }
+ }
+
+/*
+ * Now invoke the users socket handler. NOTE: the handler may delete the
+ * socket, so we must be very careful after calling the handler.
+ */
+ if (sp->handler && (sp->interestMask & sp->readyMask)) {
+ (sp->handler)(sid, sp->interestMask & sp->readyMask,
+ sp->handler_data);
+/*
+ * Make sure socket pointer is still valid, then set the readyMask
+ * to 0.
+ */
+ if (socketPtr(sid)) {
+ sp->readyMask = 0;
+ }
+ }
+ return 1;
+}
+
+/******************************************************************************/
+/*
+ * Allocate a new socket structure
+ */
+
+static int socketAlloc(char* host, int port, socketAccept_t accept, int flags)
+{
+ socket_t *sp;
+ int sid;
+
+ if ((sid = hAlloc((void***) &socketList)) < 0) {
+ return -1;
+ }
+ if ((sp = (socket_t*) balloc(B_L, sizeof(socket_t))) == NULL) {
+ hFree((void***) &socket, sid);
+ return -1;
+ }
+ memset(sp, 0, sizeof(socket_t));
+ socketList[sid] = sp;
+ if (sid >= socketMax)
+ socketMax = sid + 1;
+
+ sp->sid = sid;
+ sp->accept = accept;
+ sp->port = port;
+ sp->flags = flags;
+
+ if (host) {
+ strncpy(sp->host, host, sizeof(sp->host));
+ }
+
+ ringqOpen(&sp->inBuf, SOCKET_BUFSIZ, SOCKET_BUFSIZ);
+ ringqOpen(&sp->outBuf, SOCKET_BUFSIZ, SOCKET_BUFSIZ);
+ ringqOpen(&sp->lineBuf, SOCKET_BUFSIZ, -1);
+
+ return sid;
+}
+
+/******************************************************************************/
+/*
+ * Free a socket structure
+ */
+
+static void socketFree(int sid)
+{
+ socket_t* sp;
+ int i;
+
+ if ((sp = socketPtr(sid)) == NULL) {
+ return;
+ }
+ if (sp->sock >= 0) {
+ close(sp->sock);
+ }
+
+ ringqClose(&sp->inBuf);
+ ringqClose(&sp->outBuf);
+ ringqClose(&sp->lineBuf);
+
+ bfree(B_L, sp);
+ socketMax = hFree((void***) &socketList, sid);
+
+/*
+ * Calculate the new highest socket number
+ */
+ socketHighestFd = -1;
+ for (i = 0; i < socketMax; i++) {
+ if ((sp = socketList[i]) == NULL) {
+ continue;
+ }
+ socketHighestFd = max(socketHighestFd, sp->sock);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Validate a socket handle
+ */
+
+static socket_t* socketPtr(int sid)
+{
+ if (sid < 0 || sid >= socketMax || socketList[sid] == NULL) {
+ a_assert(NULL);
+ return NULL;
+ }
+
+ a_assert(socketList[sid]);
+ return socketList[sid];
+}
+
+/******************************************************************************/
+/*
+ * Get the operating system error code
+ */
+
+static int socketGetError()
+{
+ return errno;
+}
+
+/******************************************************************************/
+/*
+ * Wait until an event occurs on a socket. Return 1 on success, 0 on failure.
+ */
+
+static int socketWaitForEvent(socket_t* sp, int interestMask, int* errCode)
+{
+ a_assert(sp);
+
+ while (socketSelect()) {
+ if (sp->readyMask & interestMask) {
+ break;
+ }
+ }
+ if (sp->readyMask & SOCKET_EXCEPTION) {
+ return -1;
+ } else if (sp->readyMask & SOCKET_WRITABLE) {
+ return 0;
+ } else {
+ *errCode = errno = EWOULDBLOCK;
+ return -1;
+ }
+}
+
+/******************************************************************************/
+/*
+ * Put the socket into non-blocking mode
+ */
+
+static int socketNonBlock(socket_t *sp)
+{
+ int flags;
+
+ flags = fcntl(sp->sock, F_GETFL) | O_NONBLOCK;
+ if (fcntl(sp->sock, F_SETFL, flags) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Duplicate stdin and stdout
+ */
+
+int DuplicateStdFile (int sid){
+ int i;
+
+ if (0 != dup2(socketList[sid]->sock, 0) || 1 != dup2(socketList[sid]->sock, 1))
+ return -1;
+
+ return 0;
+
+}
diff --git a/cpukit/httpd/sym.c b/cpukit/httpd/sym.c
new file mode 100644
index 0000000000..c7b47b5e00
--- /dev/null
+++ b/cpukit/httpd/sym.c
@@ -0,0 +1,452 @@
+/*
+ * sym.c -- Symbol Table module
+ *
+ * Copyright (c) GoAhead Software Inc., 1995-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/******************************** Description *********************************/
+/*
+ * This module implements a highly efficient generic symbol table with
+ * update and access routines. Symbols are simple character strings and
+ * the values they take can be flexible types as defined by value_t.
+ * This modules allows multiple symbol tables to be created.
+ */
+
+/********************************* Includes ***********************************/
+
+#if UEMF
+ #include "uemf.h"
+#else
+ #include "basic/basicInternal.h"
+#endif
+
+/********************************* Defines ************************************/
+
+typedef struct { /* Symbol table descriptor */
+ int inuse; /* Is this entry in use */
+ int hash_size; /* Size of the table below */
+ sym_t **hash_table; /* Allocated at run time */
+} sym_tabent_t;
+
+/********************************* Globals ************************************/
+
+static sym_tabent_t **sym; /* List of symbol tables */
+static int sym_max; /* One past the max symbol table */
+
+static int htIndex; /* Current location in table */
+static sym_t* next; /* Next symbol in iteration */
+
+/**************************** Forward Declarations ****************************/
+
+static int hashIndex(sym_tabent_t *tp, char_t *name);
+static sym_t *hash(sym_tabent_t *tp, char_t *name);
+static int calc_prime(int size);
+
+/*********************************** Code *************************************/
+/*
+ * Create a symbol table.
+ */
+
+sym_fd_t symOpen(int hash_size)
+{
+ sym_fd_t sd;
+ sym_tabent_t *tp;
+
+ a_assert(hash_size > 2);
+
+/*
+ * Create a new handle for this symbol table
+ */
+ if ((sd = hAlloc((void***) &sym)) < 0) {
+ return -1;
+ }
+
+/*
+ * Create a new symbol table structure and zero
+ */
+ if ((tp = (sym_tabent_t*) balloc(B_L, sizeof(sym_tabent_t))) == NULL) {
+ sym_max = hFree((void***) &sym, sd);
+ return -1;
+ }
+ memset(tp, 0, sizeof(sym_tabent_t));
+ if (sd >= sym_max) {
+ sym_max = sd + 1;
+ }
+ a_assert(0 <= sd && sd < sym_max);
+ sym[sd] = tp;
+
+/*
+ * Now create the hash table for fast indexing.
+ */
+ tp->hash_size = calc_prime(hash_size);
+ tp->hash_table = (sym_t**) balloc(B_L, tp->hash_size * sizeof(sym_t*));
+ a_assert(tp->hash_table);
+ memset(tp->hash_table, 0, tp->hash_size * sizeof(sym_t*));
+
+ return sd;
+}
+
+/******************************************************************************/
+/*
+ * Close this symbol table. Call a cleanup function to allow the caller
+ * to free resources associated with each symbol table entry.
+ */
+
+void symClose(sym_fd_t sd, void (*cleanup)(sym_t *symp))
+{
+ sym_tabent_t *tp;
+ sym_t *sp, *forw;
+ int i;
+
+ a_assert(0 <= sd && sd < sym_max);
+ tp = sym[sd];
+ a_assert(tp);
+
+/*
+ * Free all symbols in the hash table, then the hash table itself.
+ */
+ for (i = 0; i < tp->hash_size; i++) {
+ for (sp = tp->hash_table[i]; sp; sp = forw) {
+ forw = sp->forw;
+ if (cleanup) {
+ (*cleanup)(sp);
+ }
+ valueFree(&sp->name);
+ bfree(B_L, (void*) sp);
+ sp = forw;
+ }
+ }
+ bfree(B_L, (void*) tp->hash_table);
+
+ sym_max = hFree((void***) &sym, sd);
+ bfree(B_L, (void*) tp);
+}
+
+/******************************************************************************/
+/*
+ * Default callback for freeing the value.
+ */
+
+void symFreeVar(sym_t* sp)
+{
+ valueFree(&sp->content);
+}
+
+/******************************************************************************/
+/*
+ * Return the first symbol in the hashtable if there is one. This call is used
+ * as the first step in traversing the table. A call to symFirst should be
+ * followed by calls to symNext to get all the rest of the entries.
+ */
+
+sym_t* symFirst(sym_fd_t sd)
+{
+ sym_tabent_t *tp;
+ sym_t *sp, *forw;
+ int i;
+
+ a_assert(0 <= sd && sd < sym_max);
+ tp = sym[sd];
+ a_assert(tp);
+
+/*
+ * Find the first symbol in the hashtable and return a pointer to it.
+ */
+ for (i = 0; i < tp->hash_size; i++) {
+ for (sp = tp->hash_table[i]; sp; sp = forw) {
+ forw = sp->forw;
+
+ if (forw == NULL) {
+ htIndex = i + 1;
+ next = tp->hash_table[htIndex];
+ } else {
+ htIndex = i;
+ next = forw;
+ }
+ return sp;
+ }
+ }
+ return NULL;
+}
+
+/******************************************************************************/
+/*
+ * Return the next symbol in the hashtable if there is one. See symFirst.
+ */
+
+sym_t* symNext(sym_fd_t sd)
+{
+ sym_tabent_t *tp;
+ sym_t *sp, *forw;
+ int i;
+
+ a_assert(0 <= sd && sd < sym_max);
+ tp = sym[sd];
+ a_assert(tp);
+
+/*
+ * Find the first symbol in the hashtable and return a pointer to it.
+ */
+ for (i = htIndex; i < tp->hash_size; i++) {
+ for (sp = next; sp; sp = forw) {
+ forw = sp->forw;
+
+ if (forw == NULL) {
+ htIndex = i + 1;
+ next = tp->hash_table[htIndex];
+ } else {
+ htIndex = i;
+ next = forw;
+ }
+ return sp;
+ }
+ next = tp->hash_table[i + 1];
+ }
+ return NULL;
+}
+
+/******************************************************************************/
+/*
+ * Lookup a symbol and return a pointer to the symbol entry. If not present
+ * then return a NULL.
+ */
+
+sym_t *symLookup(sym_fd_t sd, char_t *name)
+{
+ sym_tabent_t *tp;
+ sym_t *sp;
+ char_t *cp;
+
+ a_assert(0 <= sd && sd < sym_max);
+ tp = sym[sd];
+ a_assert(tp);
+
+ if (name == NULL || *name == '\0') {
+ return NULL;
+ }
+
+/*
+ * Do an initial hash and then follow the link chain to find the right entry
+ */
+ for (sp = hash(tp, name); sp; sp = sp->forw) {
+ cp = sp->name.value.string;
+ if (cp[0] == name[0] && gstrcmp(cp, name) == 0) {
+ break;
+ }
+ }
+ return sp;
+}
+
+/******************************************************************************/
+/*
+ * Enter a symbol into the table. If already there, update its value.
+ * Always succeeds if memory available.
+ */
+
+sym_t *symEnter(sym_fd_t sd, char_t *name, value_t v, int arg)
+{
+ sym_tabent_t *tp;
+ sym_t *sp, *last;
+ char_t *cp;
+ int hindex;
+
+ a_assert(name && *name);
+ a_assert(0 <= sd && sd < sym_max);
+ tp = sym[sd];
+ a_assert(tp);
+
+/*
+ * Calculate the first daisy-chain from the hash table. If non-zero, then
+ * we have daisy-chain, so scan it and look for the symbol.
+ */
+ last = NULL;
+ hindex = hashIndex(tp, name);
+ if ((sp = tp->hash_table[hindex]) != NULL) {
+ for (; sp; sp = sp->forw) {
+ cp = sp->name.value.string;
+ if (cp[0] == name[0] && gstrcmp(cp, name) == 0) {
+ break;
+ }
+ last = sp;
+ }
+ if (sp) {
+/*
+ * Found, so update the value
+ * If the caller stores handles which require freeing, they
+ * will be lost here. It is the callers responsibility to free
+ * resources before overwriting existing contents. We will here
+ * free allocated strings which occur due to value_instring().
+ * We should consider providing the cleanup function on the open rather
+ * than the close and then we could call it here and solve the problem.
+ */
+ if (sp->content.valid) {
+ valueFree(&sp->content);
+ }
+ sp->content = v;
+ sp->arg = arg;
+ return sp;
+ }
+/*
+ * Not found so allocate and append to the daisy-chain
+ */
+ sp = (sym_t*) balloc(B_L, sizeof(sym_t));
+ if (sp == NULL) {
+ return NULL;
+ }
+ sp->name = valueString(name, VALUE_ALLOCATE);
+ sp->content = v;
+ sp->forw = (sym_t*) NULL;
+ sp->arg = arg;
+ last->forw = sp;
+
+ } else {
+/*
+ * Daisy chain is empty so we need to start the chain
+ */
+ sp = (sym_t*) balloc(B_L, sizeof(sym_t));
+ if (sp == NULL) {
+ return NULL;
+ }
+ tp->hash_table[hindex] = sp;
+ tp->hash_table[hashIndex(tp, name)] = sp;
+
+ sp->forw = (sym_t*) NULL;
+ sp->content = v;
+ sp->arg = arg;
+ sp->name = valueString(name, VALUE_ALLOCATE);
+ }
+ return sp;
+}
+
+/******************************************************************************/
+/*
+ * Delete a symbol from a table
+ */
+
+int symDelete(sym_fd_t sd, char_t *name)
+{
+ sym_tabent_t *tp;
+ sym_t *sp, *last;
+ char_t *cp;
+ int hindex;
+
+ a_assert(name && *name);
+ a_assert(0 <= sd && sd < sym_max);
+ tp = sym[sd];
+ a_assert(tp);
+
+/*
+ * Calculate the first daisy-chain from the hash table. If non-zero, then
+ * we have daisy-chain, so scan it and look for the symbol.
+ */
+ last = NULL;
+ hindex = hashIndex(tp, name);
+ if ((sp = tp->hash_table[hindex]) != NULL) {
+ for ( ; sp; sp = sp->forw) {
+ cp = sp->name.value.string;
+ if (cp[0] == name[0] && gstrcmp(cp, name) == 0) {
+ break;
+ }
+ last = sp;
+ }
+ }
+ if (sp == (sym_t*) NULL) { /* Not Found */
+ return -1;
+ }
+
+/*
+ * Unlink and free the symbol. Last will be set if the element to be deleted
+ * is not first in the chain.
+ */
+ if (last) {
+ last->forw = sp->forw;
+ } else {
+ tp->hash_table[hindex] = sp->forw;
+ }
+ valueFree(&sp->name);
+ bfree(B_L, (void*) sp);
+
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Hash a symbol and return a pointer to the hash daisy-chain list
+ * All symbols reside on the chain (ie. none stored in the hash table itself)
+ */
+
+static sym_t *hash(sym_tabent_t *tp, char_t *name)
+{
+ a_assert(tp);
+
+ return tp->hash_table[hashIndex(tp, name)];
+}
+
+/******************************************************************************/
+/*
+ * Compute the hash function and return an index into the hash table
+ * We use a basic additive function that is then made modulo the size of the
+ * table.
+ */
+
+static int hashIndex(sym_tabent_t *tp, char_t *name)
+{
+ unsigned int sum;
+ int i;
+
+ a_assert(tp);
+/*
+ * Add in each character shifted up progressively by 7 bits. The shift
+ * amount is rounded so as to not shift too far. It thus cycles with each
+ * new cycle placing character shifted up by one bit.
+ */
+ i = 0;
+ sum = 0;
+ while (*name) {
+ sum += (((int) *name++) << i);
+ i = (i + 7) % (BITS(int) - BITSPERBYTE);
+ }
+ return sum % tp->hash_size;
+}
+
+/******************************************************************************/
+/*
+ * Check if this number is a prime
+ */
+
+static int is_prime(int n)
+{
+ int i;
+
+ a_assert(n > 0);
+
+ for (i = 2; i < n; i++) {
+ if (n % i == 0) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/******************************************************************************/
+/*
+ * Calculate the largest prime smaller than size.
+ */
+
+static int calc_prime(int size)
+{
+ int count;
+
+ a_assert(size > 0);
+
+ for (count = size; count > 0; count--) {
+ if (is_prime(count)) {
+ return count;
+ }
+ }
+ return 1;
+}
+
+/******************************************************************************/
diff --git a/cpukit/httpd/uemf.c b/cpukit/httpd/uemf.c
new file mode 100644
index 0000000000..8a6675f944
--- /dev/null
+++ b/cpukit/httpd/uemf.c
@@ -0,0 +1,193 @@
+/*
+ * uemf.c -- GoAhead Micro Embedded Management Framework
+ *
+ * Copyright (c) Go Ahead Software, Inc., 1995-1999
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/********************************** Description *******************************/
+
+/*
+ * This module provides compatibility with the full GoAhead EMF.
+ * It is a collection of routines which permits the GoAhead WebServer to
+ * run stand-alone and to also load as a solution pack under the GoAhead EMF.
+ */
+
+/*********************************** Includes *********************************/
+
+#include "uemf.h"
+
+/********************************** Local Data ********************************/
+
+int emfInst; /* Application instance handle */
+
+/************************************* Code ***********************************/
+/*
+ * Error message that doesn't need user attention. Customize this code
+ * to direct error messages to whereever the developer wishes
+ */
+
+void error(E_ARGS_DEC, int flags, char_t *fmt, ...)
+{
+ if (flags & E_LOG) {
+ /* Log error message */
+
+ } else if (flags & E_ASSERT) {
+ /* Assert message */
+
+ } else if (flags & E_USER) {
+ /* Display message to the user */
+
+ }
+}
+
+/******************************************************************************/
+/*
+ * Trace log. Customize this function to log trace output
+ */
+
+void trace(int level, char_t *afmt, ...)
+{
+#if DEBUG
+ va_list args;
+ char_t *buf;
+
+ va_start(args, afmt);
+ buf = NULL;
+ gvsnprintf(&buf, VALUE_MAX_STRING, afmt, args);
+ if (buf) {
+ gprintf(buf);
+ bfree(B_L, buf);
+ }
+ va_end(args);
+#endif
+}
+
+/******************************************************************************/
+/*
+ * Save the instance handle
+ */
+
+void emfInstSet(int inst)
+{
+ emfInst = inst;
+}
+
+/******************************************************************************/
+/*
+ * Get the instance handle
+ */
+
+int emfInstGet()
+{
+ return emfInst;
+}
+
+/******************************************************************************/
+/*
+ * Convert a string to lower case
+ */
+
+char_t *strlower(char_t *string)
+{
+ char_t *s;
+
+ a_assert(string);
+
+ if (string == NULL) {
+ return NULL;
+ }
+
+ s = string;
+ while (*s) {
+ if (gisupper(*s)) {
+ *s = (char_t) gtolower(*s);
+ }
+ s++;
+ }
+ *s = '\0';
+ return string;
+}
+
+/******************************************************************************/
+/*
+ * Convert a string to upper case
+ */
+
+char_t *strupper(char_t *string)
+{
+ char_t *s;
+
+ a_assert(string);
+ if (string == NULL) {
+ return NULL;
+ }
+
+ s = string;
+ while (*s) {
+ if (gislower(*s)) {
+ *s = (char_t) gtoupper(*s);
+ }
+ s++;
+ }
+ *s = '\0';
+ return string;
+}
+
+/******************************************************************************/
+/*
+ * Convert integer to ascii string. Allow a NULL string in which case we
+ * allocate a dynamic buffer.
+ */
+
+char_t *stritoa(int n, char_t *string, int width)
+{
+ char_t *cp, *lim, *s;
+ char_t buf[16]; /* Just temp to hold number */
+ int next, minus;
+
+ a_assert(string && width > 0);
+
+ if (string == NULL) {
+ if (width == 0) {
+ width = 10;
+ }
+ if ((string = balloc(B_L, width + 1)) == NULL) {
+ return NULL;
+ }
+ }
+ if (n < 0) {
+ minus = 1;
+ n = -n;
+ width--;
+ } else {
+ minus = 0;
+ }
+
+ cp = buf;
+ lim = &buf[width - 1];
+ while (n > 9 && cp < lim) {
+ next = n;
+ n /= 10;
+ *cp++ = (char_t) (next - n * 10 + '0');
+ }
+ if (cp < lim) {
+ *cp++ = (char_t) (n + '0');
+ }
+
+ s = string;
+ if (minus) {
+ *s++ = '-';
+ }
+
+ while (cp > buf) {
+ *s++ = *--cp;
+ }
+
+ *s++ = '\0';
+ return string;
+}
+
+/******************************************************************************/
+
diff --git a/cpukit/httpd/uemf.h b/cpukit/httpd/uemf.h
new file mode 100644
index 0000000000..1c0d348256
--- /dev/null
+++ b/cpukit/httpd/uemf.h
@@ -0,0 +1,666 @@
+/*
+ * uemf.h -- Go Ahead Micro Embedded Management Framework Header
+ *
+ * Copyright (c) Go Ahead Software, Inc., 1995-1999
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+#ifndef _h_UEMF
+#define _h_UEMF 1
+
+/******************************** Description *********************************/
+
+/*
+ * Go Ahead Web Server header. This defines the Web public APIs
+ */
+
+/******************************* Per O/S Includes *****************************/
+
+#if WIN
+ #include <direct.h>
+ #include <io.h>
+ #include <sys/stat.h>
+ #include <limits.h>
+ #include <tchar.h>
+ #include <windows.h>
+ #include <winnls.h>
+ #include <time.h>
+ #include <sys/types.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+#endif
+
+#if CE
+ #include <limits.h>
+ #include <tchar.h>
+ #include <windows.h>
+ #include <winnls.h>
+ #include "CE/wincompat.h"
+#endif
+
+#if NW
+ #include <stdio.h>
+#endif
+
+#if UNIX
+ #include <stdio.h>
+#endif
+
+#if LINUX
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <sys/param.h>
+ #include <limits.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include <sys/socket.h>
+ #include <sys/select.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+ #include <time.h>
+#endif
+
+#if LYNX
+ #include <limits.h>
+ #include <stdarg.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include <socket.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+ #include <time.h>
+#endif
+
+#if UW
+ #include <stdio.h>
+#endif
+
+#if VXW486
+ #include <vxWorks.h>
+ #include <sockLib.h>
+ #include <selectLib.h>
+ #include <inetLib.h>
+ #include <ioLib.h>
+ #include <stdio.h>
+ #include <stat.h>
+ #include <time.h>
+ #include <usrLib.h>
+#endif
+
+#if QNX4
+ #include <sys/types.h>
+ #include <stdio.h>
+ #include <sys/socket.h>
+ #include <sys/select.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+#endif
+
+/********************************** Includes **********************************/
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <string.h>
+
+/********************************** Unicode ***********************************/
+/*
+ * Constants and limits. Also FNAMESIZE and PATHSIZE are currently defined
+ * in param.h to be 128 and 512
+ */
+#define TRACE_MAX (4096 - 48)
+#define VALUE_MAX_STRING (4096 - 48)
+#define SYM_MAX (512)
+#define XML_MAX 4096 /* Maximum size for tags/tokens */
+#define BUF_MAX 4096 /* General sanity check for bufs */
+
+/*
+ * Type for unicode systems
+ */
+#if UNICODE
+
+/*
+ * To convert strings to UNICODE. We have a level of indirection so things
+ * like T(__FILE__) will expand properly.
+ */
+#define T(x) __TXT(x)
+#define __TXT(s) L ## s
+typedef unsigned short char_t;
+typedef unsigned short uchar_t;
+
+/*
+ * Text size of buffer macro. A buffer bytes will hold (size / char size)
+ * characters.
+ */
+#define TSZ(x) (sizeof(x) / sizeof(char_t))
+
+/*
+ * How many ASCII bytes are required to represent this UNICODE string?
+ */
+#define TASTRL(x) ((wcslen(x) + 1) * sizeof(char_t))
+
+
+#define gmain wmain
+
+#define gsprintf swprintf
+#define gprintf wprintf
+#define gfprintf fwprintf
+#define gsscanf swscanf
+#define gvsprintf vswprintf
+
+#define gstrcpy wcscpy
+#define gstrncpy wcsncpy
+#define gstrncat wcsncat
+#define gstrlen wcslen
+#define gstrcat wcscat
+#define gstrcmp wcscmp
+#define gstrncmp wcsncmp
+#define gstricmp wcsicmp
+#define gstrchr wcschr
+#define gstrrchr wcsrchr
+#define gstrtok wcstok
+#define gstrnset wcsnset
+#define gstrstr wcsstr
+
+#define gfopen _wfopen
+#define gopen _wopen
+#define gcreat _wcreat
+#define gfgets fgetws
+#define gfputs fputws
+#define gunlink _wunlink
+#define grename _wrename
+#define gtmpnam _wtmpnam
+#define gtempnam _wtempnam
+#define gfindfirst _wfindfirst
+#define gfinddata_t _wfinddata_t
+#define gfindnext _wfindnext
+#define gfindclose _findclose
+#define gstat _wstat
+#define gaccess _waccess
+
+typedef struct _stat gstat_t;
+
+#define gmkdir _wmkdir
+#define gchdir _wchdir
+#define grmdir _wrmdir
+#define gremove _wremove
+#define ggetcwd _wgetcwd
+
+#define gtolower towlower
+#define gtoupper towupper
+#define gisspace iswspace
+#define gisdigit iswdigit
+#define gisxdigit iswxdigit
+#define gisalnum iswalnum
+#define gisalpha iswalpha
+#define gisupper iswupper
+#define gislower iswlower
+#define gatoi(s) wcstol(s, NULL, 10)
+
+#define gctime _wctime
+#define ggetenv _wgetenv
+#define gexecvp _wexecvp
+
+#else /* ! UNICODE */
+
+#define T(s) s
+#define TSZ(x) (sizeof(x))
+#define TASTRL(x) (strlen(x) + 1)
+typedef char char_t;
+#if WIN
+typedef unsigned char uchar_t;
+#endif
+
+#define gsprintf sprintf
+#define gprintf printf
+#define gfprintf fprintf
+#define gsscanf sscanf
+#define gvsprintf vsprintf
+
+#define gstrcpy strcpy
+#define gstrncpy strncpy
+#define gstrncat strncat
+#define gstrlen strlen
+#define gstrcat strcat
+#define gstrcmp strcmp
+#define gstrncmp strncmp
+#define gstricmp stricmp
+#define gstrchr strchr
+#define gstrrchr strrchr
+#define gstrtok strtok
+#define gstrnset strnset
+#define gstrstr strstr
+
+#define gfopen fopen
+#define gopen open
+#define gcreat creat
+#define gfgets fgets
+#define gfputs fputs
+#define gunlink unlink
+#define grename rename
+#define gtmpnam tmpnam
+#define gtempnam tempnam
+#define gfindfirst _findfirst
+#define gfinddata_t _finddata_t
+#define gfindnext _findnext
+#define gfindclose _findclose
+#define gstat stat
+#define gaccess access
+
+typedef struct stat gstat_t;
+
+#define gmkdir mkdir
+#define gchdir chdir
+#define grmdir rmdir
+#define gremove remove
+#define ggetcwd getcwd
+
+#define gtolower tolower
+#define gtoupper toupper
+#define gisspace isspace
+#define gisdigit isdigit
+#define gisxdigit isxdigit
+#define gisalnum isalnum
+#define gisalpha isalpha
+#define gisupper isupper
+#define gislower islower
+#define gatoi atoi
+
+#define gctime ctime
+#define ggetenv getenv
+#define gexecvp execvp
+#ifndef VXW486
+#define gmain main
+#endif
+#endif
+
+/********************************** Defines ***********************************/
+
+#define FNAMESIZE 256 /* Max length of file names */
+
+#define E_MAX_ERROR 4096
+/*
+ * Error types
+ */
+#define E_ASSERT 0x1 /* Assertion error */
+#define E_LOG 0x2 /* Log error to log file */
+#define E_USER 0x3 /* Error that must be displayed */
+
+#define E_L T(__FILE__), __LINE__
+#define E_ARGS_DEC char_t *file, int line
+#define E_ARGS file, line
+
+#if ASSERT
+ #define a_assert(C) if (C) ; else error(E_L, E_ASSERT, T("%s"), #C)
+#else
+ #define a_assert(C) if (1) ; else
+#endif
+
+#define VALUE_VALID { {0}, integer, 1 }
+#define VALUE_INVALID { {0}, undefined, 0 }
+
+/*
+ * Allocation flags
+ */
+#define VALUE_ALLOCATE 0x1
+
+#define value_numeric(t) (t == integer)
+#define value_str(t) (t >= string || t <= bytes)
+#define value_ok(t) (t > undefined && t <= symbol)
+
+/*
+ * These values are not prefixed so as to aid code readability
+ */
+#ifndef UW
+#pragma pack(2)
+#endif
+
+typedef enum {
+ undefined = 0,
+ integer = 1,
+ string = 2,
+ bytes = 3,
+ errmsg = 4
+} value_type_t;
+
+/*
+ * In UW, bit fields default to unsigned unless explicitly defined as signed.
+ * Unfortunately, enum become ints, but not explicitly signed. Thus, if using
+ * an enum type in a bit field, it becomes unsigned, but we need signed. So
+ * for UW we declare value_type_t to be a signed int to make this all work.
+ */
+typedef struct {
+
+ union {
+ long integer;
+ char_t *string;
+ char_t *bytes;
+ char_t *errmsg;
+ void *symbol;
+ } value;
+
+#if UW
+ signed int type : 8;
+#else
+ value_type_t type : 8;
+#endif
+
+ unsigned int valid : 1;
+ unsigned int user_def_1 : 1;
+ unsigned int allocated : 1; /* String was balloced */
+} value_t;
+
+/*
+ * Extract a string from the value depending whether inline or via pointer
+ */
+#define value_strget(v) \
+ (((v)->type == bytes) ? (v)->value.bytes : (v)->value.string)
+
+#ifndef UW
+#pragma pack()
+#endif
+
+/******************************************************************************/
+/*
+ * A ring queue allows maximum utilization of memory for data storage and is
+ * ideal for input/output buffering. This module provides a highly effecient
+ * implementation and a vehicle for dynamic strings.
+ *
+ * WARNING: This is a public implementation and callers have full access to
+ * the queue structure and pointers. Change this module very carefully.
+ *
+ * This module follows the open/close model.
+ *
+ * Operation of a ringq where rq is a pointer to a ringq :
+ *
+ * rq->buflen contains the size of the buffer.
+ * rq->buf will point to the start of the buffer.
+ * rq->servp will point to the first (un-consumed) data byte.
+ * rq->endp will point to the next free location to which new data is added
+ * rq->endbuf will point to one past the end of the buffer.
+ *
+ * Eg. If the ringq contains the data "abcdef", it might look like :
+ *
+ * +-------------------------------------------------------------------+
+ * | | | | | | | | a | b | c | d | e | f | | | | |
+ * +-------------------------------------------------------------------+
+ * ^ ^ ^ ^
+ * | | | |
+ * rq->buf rq->servp rq->endp rq->enduf
+ *
+ * The queue is empty when servp == endp. This means that the queue will hold
+ * at most rq->buflen -1 bytes. It is the fillers responsibility to ensure
+ * the ringq is never filled such that servp == endp.
+ *
+ * It is the fillers responsibility to "wrap" the endp back to point to
+ * rq->buf when the pointer steps past the end. Correspondingly it is the
+ * consumers responsibility to "wrap" the servp when it steps to rq->endbuf.
+ * The ringqPutc and ringqGetc routines will do this automatically.
+ */
+
+/*
+ * Ring queue buffer structure
+ */
+typedef struct {
+ unsigned char *buf; /* Holding buffer for data */
+ unsigned char *servp; /* Pointer to start of data */
+ unsigned char *endp; /* Pointer to end of data */
+ unsigned char *endbuf; /* Pointer to end of buffer */
+ int buflen; /* Length of ring queue */
+ int maxsize; /* Maximum size */
+ int increment; /* Growth increment */
+} ringq_t;
+
+/*
+ * Block allocation definitions
+ */
+#define B_L __FILE__, __LINE__
+#define B_ARGS_DEC char *file, int line
+#define B_ARGS file, line
+
+/*
+ * Block classes are: 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192,
+ * 16384, 32768, 65536
+ */
+#define B_SHIFT 4 /* Convert size to class */
+#define B_ROUND ((1 << (B_SHIFT)) - 1)
+#define B_MAX_CLASS 13 /* Maximum class number + 1 */
+#define B_MALLOCED 0x80000000 /* Block was malloced */
+#define B_DEFAULT_MEM (64 * 1024) /* Default memory allocation */
+#define B_MAX_FILES (512) /* Maximum number of files */
+#define B_FILL_CHAR (0x77) /* Fill byte for buffers */
+#define B_FILL_WORD (0x77777777) /* Fill word for buffers */
+#define B_MAX_BLOCKS (64 * 1024) /* Maximum allocated blocks */
+
+/*
+ * Flags. The integer value is used as an arbitrary value to fill the flags.
+ */
+
+#define B_USE_MALLOC 0x1 /* Okay to use malloc if required */
+#define B_USER_BUF 0x2 /* User supplied buffer for mem */
+#define B_INTEGRITY 0x8124000 /* Integrity value */
+#define B_INTEGRITY_MASK 0xFFFF000 /* Integrity mask */
+
+/*
+ * The symbol table record for each symbol entry
+ */
+
+typedef struct sym_t {
+ struct sym_t *forw; /* Pointer to next hash list */
+ value_t name; /* Name of symbol */
+ value_t content; /* Value of symbol */
+ int arg; /* Parameter value */
+} sym_t;
+
+typedef int sym_fd_t; /* Returned by symOpen */
+
+/*
+ * Socket flags
+ */
+#define SOCKET_EOF 0x1 /* Seen end of file */
+#define SOCKET_CONNECTING 0x2 /* Connect in progress */
+#define SOCKET_BROADCAST 0x4 /* Broadcast mode */
+#define SOCKET_PENDING 0x8 /* Message pending on this socket */
+#define SOCKET_FLUSHING 0x10 /* Background flushing */
+#define SOCKET_LISTENING 0x20 /* Server socket listening */
+
+/*
+ * Socket error values
+ */
+#define SOCKET_WOULDBLOCK 1 /* Socket would block on I/O */
+#define SOCKET_RESET 2 /* Socket has been reset */
+#define SOCKET_NETDOWN 3 /* Network is down */
+#define SOCKET_AGAIN 4 /* Issue the request again */
+#define SOCKET_INTR 5 /* Call was interrupted */
+#define SOCKET_INVAL 6 /* Invalid */
+
+/*
+ * Handler event masks
+ */
+#define SOCKET_READABLE 0x2 /* Make socket readable */
+#define SOCKET_WRITABLE 0x4 /* Make socket writable */
+#define SOCKET_EXCEPTION 0x8 /* Interested in exceptions */
+
+#define SOCKET_BUFSIZ 512 /* Underlying buffer size */
+
+typedef void (*socketHandler_t)(int sid, int mask, int data);
+typedef int (*socketAccept_t)(int sid, char *ipaddr, int port);
+
+/*
+ * Script engines
+ */
+#define EMF_SCRIPT_JSCRIPT 0 /* javascript */
+#define EMF_SCRIPT_TCL 1 /* tcl */
+#define EMF_SCRIPT_EJSCRIPT 2 /* Ejscript */
+#define EMF_SCRIPT_MAX 3
+
+#define MAXINT INT_MAX
+#define BITSPERBYTE 8
+#define BITS(type) (BITSPERBYTE * (int) sizeof(type))
+
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/******************************* Per O/S Defines ******************************/
+
+#if VXW486 || LINUX || LYNX
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#define SOCKET_ERROR -1
+#endif
+
+#if WIN
+#undef R_OK
+#define R_OK 4
+#undef W_OK
+#define W_OK 2
+#undef X_OK
+#define X_OK 1
+#undef F_OK
+#define F_OK 0
+#endif
+
+/********************************* Prototypes *********************************/
+
+extern void bclose();
+extern int bopen(void *buf, int bufsize, int flags);
+#if NO_BALLOC
+#undef B_L
+#define B_L x
+#define balloc(x, num) malloc(num)
+#define bfree(x, p) free(p)
+#define bfreeSafe(x, p) \
+ if (p) { bfree(x, p); } else
+#define brealloc(x, p, num) realloc(p, num)
+extern char *bstrdupANoBalloc(char *s);
+#define bstrdupA(x, s) bstrdupANoBalloc(s)
+#if UNICODE
+extern char_t *bstrdupNoBalloc(char_t *s);
+#define bstrdup(x, s) bstrdupNoBalloc(s)
+#define gstrdup(x, s) bstrdupNoBalloc(s)
+#else /* Not UNICODE */
+#define bstrdup(x, s) bstrdupANoBalloc(s)
+#define gstrdup(x, s) bstrdupANoBalloc(s)
+#endif /* UNICODE */
+
+#else /* BALLOC */
+extern void *balloc(B_ARGS_DEC, int size);
+extern void bfree(B_ARGS_DEC, void *mp);
+extern void *brealloc(B_ARGS_DEC, void *buf, int newsize);
+extern void bstats(int handle, void (*writefn)(int fd, char_t *fmt, ...));
+extern char_t *bstrdup(B_ARGS_DEC, char_t *s);
+extern void bfreeSafe(B_ARGS_DEC, void* mp);
+#define gstrdup(B_ARGS, s) bstrdup(B_ARGS, s)
+#if UNICODE
+extern char *bstrdupA(B_ARGS_DEC, char *s);
+#else
+#define bstrdupA bstrdup
+#endif /* UNICODE */
+#endif /* BALLOC */
+
+#if !LINUX
+extern char_t* basename(char_t* name);
+#endif
+
+extern void *emfCreateTimer(int delay, void (*routine)(long arg), long arg);
+extern void emfDeleteTimer(void *id);
+extern int emfInstGet();
+extern void emfInstSet(int inst);
+extern void error(E_ARGS_DEC, int flags, char_t *fmt, ...);
+
+extern int hAlloc(void*** map);
+extern int hFree(void*** map, int handle);
+extern int hAllocEntry(void ***list, int *max, int size);
+
+extern int ringqOpen(ringq_t *rq, int increment, int maxsize);
+extern void ringqClose(ringq_t *rq);
+extern int ringqLen(ringq_t *rq);
+
+extern int ringqPutc(ringq_t *rq, char_t c);
+extern int ringqInsertc(ringq_t *rq, char_t c);
+extern int ringqPutstr(ringq_t *rq, char_t *str);
+extern int ringqGetc(ringq_t *rq);
+
+extern int gvsnprintf(char_t **s, int n, char_t *fmt, va_list arg);
+extern int gsnprintf(char_t **s, int n, char_t *fmt, ...);
+
+#if UNICODE
+extern int ringqPutcA(ringq_t* rq, char c);
+extern int ringqInsertcA(ringq_t* rq, char c);
+extern int ringqPutstrA(ringq_t* rq, char* str);
+extern int ringqGetcA(ringq_t* rq);
+#else
+#define ringqPutcA ringqPutc
+#define ringqInsertcA ringqInsertc
+#define ringqPutstrA ringqPutstr
+#define ringqGetcA ringqGetc
+#endif
+
+extern int ringqPutBlk(ringq_t *rq, unsigned char *buf, int len);
+extern int ringqPutBlkMax(ringq_t *rq);
+extern void ringqPutBlkAdj(ringq_t *rq, int size);
+extern int ringqGetBlk(ringq_t *rq, unsigned char *buf, int len);
+extern int ringqGetBlkMax(ringq_t *rq);
+extern void ringqGetBlkAdj(ringq_t *rq, int size);
+extern void ringqFlush(ringq_t *rq);
+
+extern int scriptSetVar(int engine, char_t *var, char_t *value);
+extern int scriptEval(int engine, char_t *cmd, char_t **rslt, int chan);
+
+extern void socketClose();
+extern void socketCloseConnection(int sid);
+extern void socketCreateHandler(int sid, int mask, socketHandler_t
+ handler, int arg);
+extern void socketDeleteHandler(int sid);
+extern int socketEof(int sid);
+extern int socketFlush(int sid, int block);
+extern int socketGets(int sid, char_t** buf);
+extern int socketInputBuffered(int sid);
+extern int socketOpen();
+extern int socketOpenConnection(char *host, int port,
+ socketAccept_t accept, int flags);
+extern void socketProcess();
+extern int socketRead(int sid, char *buf, int len);
+extern int socketReady();
+extern int socketWrite(int sid, char *buf, int len);
+extern int socketWriteString(int sid, char_t *buf);
+extern int socketSelect();
+
+extern char_t *strlower(char_t *string);
+extern char_t *strupper(char_t *string);
+
+extern char_t *stritoa(int n, char_t *string, int width);
+
+extern sym_fd_t symOpen(int hash_size);
+extern void symClose(sym_fd_t sd, void (*cleanup)(sym_t *sp));
+extern sym_t *symLookup(sym_fd_t sd, char_t *name);
+extern sym_t *symEnter(sym_fd_t sd, char_t *name, value_t v, int arg);
+extern int symDelete(sym_fd_t sd, char_t *name);
+extern void symWalk(sym_fd_t sd, void (*fn)(sym_t *symp));
+
+extern void trace(int lev, char_t *fmt, ...);
+
+extern value_t valueInteger(long value);
+extern value_t valueString(char_t *value, int flags);
+extern value_t valueErrmsg(char_t *value);
+extern void valueFree(value_t *v);
+
+extern char_t *ascToUni(char_t *ubuf, char *str, int nBytes);
+extern char *uniToAsc(char *buf, char_t *ustr, int nBytes);
+extern char_t *ballocAscToUni(char * cp);
+extern char *ballocUniToAsc(char_t * unip, int ulen);
+
+#endif /* _h_UEMF */
+
+/******************************************************************************/
diff --git a/cpukit/httpd/url.c b/cpukit/httpd/url.c
new file mode 100644
index 0000000000..75746fd490
--- /dev/null
+++ b/cpukit/httpd/url.c
@@ -0,0 +1,203 @@
+/*
+ * url.c -- Parse URLs
+ *
+ * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * This module parses URLs into their components.
+ */
+
+/********************************* Includes ***********************************/
+
+#include "wsIntrn.h"
+
+/********************************* Statics ************************************/
+/*
+ * htmExt is declared in this way to avoid a Linux and Solaris segmentation
+ * fault when a constant string is passed to strlower which could change its
+ * argument.
+ */
+
+char_t htmExt[] = T(".htm");
+
+
+/*********************************** Code *************************************/
+/*
+ * Return the mime type for the given URL given a URL.
+ * The caller supplies the buffer to hold the result.
+ * charCnt is the number of characters the buffer will hold, ascii or UNICODE.
+ */
+
+char_t *websUrlType(char_t *url, char_t *buf, int charCnt)
+{
+ sym_t *sp;
+ char_t *ext, *parsebuf;
+
+ a_assert(url && *url);
+ a_assert(buf && charCnt > 0);
+
+ if (url == NULL || *url == '\0') {
+ gstrcpy(buf, T("text/plain"));
+ return buf;
+ }
+ if (websUrlParse(url, &parsebuf, NULL, NULL, NULL, NULL, NULL,
+ NULL, &ext) < 0) {
+ gstrcpy(buf, T("text/plain"));
+ return buf;
+ }
+ strlower(ext);
+
+/*
+ * Lookup the mime type symbol table to find the relevant content type
+ */
+ if ((sp = symLookup(websMime, ext)) != NULL) {
+ gstrncpy(buf, sp->content.value.string, charCnt);
+ } else {
+ gstrcpy(buf, T("text/plain"));
+ }
+ bfree(B_L, parsebuf);
+ return buf;
+}
+
+/******************************************************************************/
+/*
+ * Parse the URL. A buffer is allocated to store the parsed URL in *pbuf.
+ * This must be freed by the caller. NOTE: tag is not yet fully supported.
+ */
+
+int websUrlParse(char_t *url, char_t **pbuf, char_t **phost, char_t **ppath,
+ char_t **pport, char_t **pquery, char_t **pproto, char_t **ptag,
+ char_t **pext)
+{
+ char_t *tok, *cp, *host, *path, *port, *proto, *tag, *query, *ext;
+ char_t *last_delim, *hostbuf, *portbuf, *buf;
+ int c, len, ulen;
+
+ a_assert(url);
+ a_assert(pbuf);
+
+ ulen = gstrlen(url);
+/*
+ * We allocate enough to store a separate hostname and port number fields.
+ * As there are 3 strings in the one buffer, we need room for 3 null chars.
+ * We allocate MAX_PORT_LEN char_t's for the port number.
+ */
+ len = ulen * 2 + MAX_PORT_LEN + 3;
+ if ((buf = balloc(B_L, len * sizeof(char_t))) == NULL) {
+ return -1;
+ }
+ portbuf = &buf[len - MAX_PORT_LEN - 1];
+ hostbuf = &buf[ulen+1];
+ gstrcpy(buf, url);
+ url = buf;
+
+/*
+ * Convert the current listen port to a string. We use this if the URL has
+ * no explicit port setting
+ */
+ stritoa(websGetPort(), portbuf, MAX_PORT_LEN);
+ port = portbuf;
+ path = T("/");
+ proto = T("http");
+ host = T("localhost");
+ query = T("");
+ ext = htmExt;
+ tag = T("");
+
+ if (gstrncmp(url, T("http://"), 7) == 0) {
+ tok = &url[7];
+ tok[-3] = '\0';
+ proto = url;
+ host = tok;
+ for (cp = tok; *cp; cp++) {
+ if (*cp == '/') {
+ break;
+ }
+ if (*cp == ':') {
+ *cp++ = '\0';
+ port = cp;
+ tok = cp;
+ }
+ }
+ if ((cp = gstrchr(tok, '/')) != NULL) {
+/*
+ * If a full URL is supplied, we need to copy the host and port
+ * portions into static buffers.
+ */
+ c = *cp;
+ *cp = '\0';
+ gstrncpy(hostbuf, host, ulen);
+ gstrncpy(portbuf, port, MAX_PORT_LEN);
+ *cp = c;
+ host = hostbuf;
+ port = portbuf;
+ path = cp;
+ tok = cp;
+ }
+
+ } else {
+ path = url;
+ tok = url;
+ }
+
+/*
+ * Parse the tag and the query
+ */
+ if ((cp = gstrchr(tok, '#')) != NULL) {
+ *cp++ = '\0';
+ path = tok;
+ tok = cp;
+ }
+ if ((cp = gstrchr(tok, '?')) != NULL) {
+ *cp++ = '\0';
+ query = cp;
+ }
+
+/*
+ * Only do the following if asked for the extension
+ */
+ if (pext) {
+ if ((cp = gstrrchr(path, '.')) != NULL) {
+ if ((last_delim = gstrrchr(path, '/')) != NULL) {
+ if (last_delim > cp) {
+ ext = htmExt;
+ } else {
+ ext = cp;
+ }
+ } else {
+ ext = cp;
+ }
+ } else {
+ if (path[gstrlen(path) - 1] == '/') {
+ ext = htmExt;
+ }
+ }
+ }
+
+/*
+ * Pass back the fields requested (if not NULL)
+ */
+ if (phost)
+ *phost = host;
+ if (ppath)
+ *ppath = path;
+ if (pport)
+ *pport = port;
+ if (pproto)
+ *pproto = proto;
+ if (pquery)
+ *pquery = query;
+ if (ptag)
+ *ptag = tag;
+ if (pext)
+ *pext = ext;
+ *pbuf = buf;
+ return 0;
+}
+
+/******************************************************************************/
diff --git a/cpukit/httpd/value.c b/cpukit/httpd/value.c
new file mode 100644
index 0000000000..5872382556
--- /dev/null
+++ b/cpukit/httpd/value.c
@@ -0,0 +1,74 @@
+/*
+ * value.c -- Generic type (holds all types)
+ *
+ * Copyright (c) Go Ahead Software, Inc., 1995-1999
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * This module provides a generic type that can hold all possible types.
+ * It is designed to provide maximum effeciency.
+ */
+
+/********************************* Includes ***********************************/
+
+#include "uemf.h"
+
+/*********************************** Locals ***********************************/
+
+
+/*********************************** Code *************************************/
+/*
+ * Initialize a integer value.
+ */
+
+value_t valueInteger(long value)
+{
+ value_t v = VALUE_VALID;
+
+ v.value.integer = value;
+ return v;
+}
+
+/******************************************************************************/
+/*
+ * Initialize a string value. Note: not allocation
+ */
+
+value_t valueString(char_t *value, int flags)
+{
+ value_t v = VALUE_VALID;
+
+ v.type = string;
+ if (flags & VALUE_ALLOCATE) {
+ v.allocated = 1;
+ v.value.string = gstrdup(B_L, value);
+ } else {
+ v.allocated = 0;
+ v.value.string = value;
+ }
+ return v;
+}
+
+/******************************************************************************/
+/*
+ * Free any storage allocated for a value.
+ */
+
+void valueFree(value_t *v)
+{
+ a_assert(v);
+
+ if (v->valid && v->allocated && v->type == string &&
+ v->value.string != NULL) {
+ bfree(B_L, v->value.string);
+ }
+ v->type = undefined;
+ v->valid = 0;
+ v->allocated = 0;
+}
+
+/******************************************************************************/
diff --git a/cpukit/httpd/wbase64.c b/cpukit/httpd/wbase64.c
new file mode 100644
index 0000000000..0898b16728
--- /dev/null
+++ b/cpukit/httpd/wbase64.c
@@ -0,0 +1,149 @@
+/*
+ * base64.c -- Base64 Mime encoding
+ *
+ * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * The base64 command encodes and decodes a string in mime base64 format
+ */
+
+/********************************* Includes ***********************************/
+
+#include "wsIntrn.h"
+
+/******************************** Local Data **********************************/
+/*
+ * Mapping of ANSI chars to base64 Mime encoding alphabet (see below)
+ */
+
+static char_t map64[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+static char_t alphabet64[] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '+', '/',
+};
+
+/*********************************** Code *************************************/
+/*
+ * Decode a buffer from "string" and into "outbuf"
+ */
+
+int websDecode64(char_t* outbuf, char_t* string, int outlen)
+{
+ unsigned long shiftbuf;
+ char_t* cp;
+ char_t* op;
+ int c, i, j, shift;
+
+ op = outbuf;
+ *op = '\0';
+ cp = string;
+ while (*cp && *cp != '=') {
+/*
+ * Map 4 (6bit) input bytes and store in a single long (shiftbuf)
+ */
+ shiftbuf = 0;
+ shift = 18;
+ for (i = 0; i < 4 && *cp && *cp != '='; i++, cp++) {
+ c = map64[*cp & 0xff];
+ if (c == -1) {
+ error(E_L, E_LOG, T("Bad string: %s at %c index %d"), string,
+ c, i);
+ return -1;
+ }
+ shiftbuf = shiftbuf | (c << shift);
+ shift -= 6;
+ }
+/*
+ * Interpret as 3 normal 8 bit bytes (fill in reverse order).
+ * Check for potential buffer overflow before filling.
+ */
+ --i;
+ if ((op + i) >= &outbuf[outlen]) {
+ gstrcpy(outbuf, T("String too big"));
+ return -1;
+ }
+ for (j = 0; j < i; j++) {
+ *op++ = (char_t) ((shiftbuf >> (8 * (2 - j))) & 0xff);
+ }
+ *op = '\0';
+ }
+ return 0;
+}
+
+
+/******************************************************************************/
+/*
+ * Encode a buffer from "string" into "outbuf"
+ */
+
+void websEncode64(char_t* outbuf, char_t* string, int outlen)
+{
+ unsigned long shiftbuf;
+ char_t* cp;
+ char_t* op;
+ int x, i, j, shift;
+
+ op = outbuf;
+ *op = '\0';
+ cp = string;
+ while (*cp) {
+/*
+ * Take three characters and create a 24 bit number in shiftbuf
+ */
+ shiftbuf = 0;
+ for (j = 2; j >= 0 && *cp; j--, cp++) {
+ shiftbuf |= ((*cp & 0xff) << (j * 8));
+ }
+/*
+ * Now convert shiftbuf to 4 base64 letters. The i,j magic calculates
+ * how many letters need to be output.
+ */
+ shift = 18;
+ for (i = ++j; i < 4 && op < &outbuf[outlen] ; i++) {
+ x = (shiftbuf >> shift) & 0x3f;
+ *op++ = alphabet64[(shiftbuf >> shift) & 0x3f];
+ shift -= 6;
+ }
+/*
+ * Pad at the end with '='
+ */
+ while (j-- > 0) {
+ *op++ = '=';
+ }
+ *op = '\0';
+ }
+}
+/******************************************************************************/
diff --git a/cpukit/httpd/webcomp.c b/cpukit/httpd/webcomp.c
new file mode 100644
index 0000000000..90767da01f
--- /dev/null
+++ b/cpukit/httpd/webcomp.c
@@ -0,0 +1,177 @@
+/*
+ * webcomp -- Compile web pages into C source
+ *
+ * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * Usage: webcomp prefix filelist >webrom.c
+ *
+ * filelist is a file containing the pathnames of all web pages
+ * prefix is a path prefix to remove from all the web page pathnames
+ * webrom.c is the resulting C source file to compile and link.
+ */
+
+/********************************* Includes ***********************************/
+
+#include "wsIntrn.h"
+
+/**************************** Forward Declarations ****************************/
+
+static int compile(char_t *fileList, char_t *prefix);
+static void usage();
+
+/*********************************** Code *************************************/
+/*
+ * Main program for webpack test harness
+ */
+
+int gmain(int argc, char_t* argv[])
+{
+ char_t *fileList, *prefix;
+
+ fileList = NULL;
+
+ if (argc != 3) {
+ usage();
+ }
+
+ prefix = argv[1];
+ fileList = argv[2];
+
+ if (compile(fileList, prefix) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Output usage message
+ */
+
+static void usage()
+{
+ fprintf(stderr, "usage: webcomp prefix filelist >output.c\n");
+ exit(2);
+}
+
+/******************************************************************************/
+/*
+ * Compile the web pages
+ */
+
+static int compile(char_t *fileList, char_t *prefix)
+{
+ gstat_t sbuf;
+ FILE *lp;
+ time_t now;
+ char_t file[FNAMESIZE];
+ char_t *cp;
+ char buf[512];
+ unsigned char *p;
+ int j, i, len, fd, nFile;
+
+/*
+ * Open list of files
+ */
+ if ((lp = fopen(fileList, "r")) == NULL) {
+ fprintf(stderr, "Can't open file list %s\n", fileList);
+ return -1;
+ }
+
+ time(&now);
+ fprintf(stdout, "/*\n * webrom.c -- Compiled Web Pages\n *\n");
+ fprintf(stdout, " * Compiled by GoAhead WebCompile: %s */\n\n",
+ ctime(&now));
+ fprintf(stdout, "#include \"wsIntrn.h\"\n\n");
+ fprintf(stdout, "#ifndef WEBS_PAGE_ROM\n");
+ fprintf(stdout, "websRomPageIndexType websRomPageIndex[] = {\n");
+ fprintf(stdout, " { 0, 0, 0 },\n};\n");
+ fprintf(stdout, "#else\n");
+
+/*
+ * Open each input file and compile each web page
+ */
+ nFile = 0;
+ while (fgets(file, sizeof(file), lp) != NULL) {
+ if ((p = strchr(file, '\n')) || (p = strchr(file, '\r'))) {
+ *p = '\0';
+ }
+ if (gstat(file, &sbuf) == 0 && sbuf.st_mode & S_IFDIR) {
+ continue;
+ }
+ if ((fd = gopen(file, O_RDONLY | O_BINARY)) < 0) {
+ fprintf(stderr, "Can't open file %s\n", file);
+ return -1;
+ }
+ fprintf(stdout, "static unsigned char page_%d[] = {\n", nFile);
+
+ while ((len = read(fd, buf, sizeof(buf))) > 0) {
+ p = buf;
+ for (i = 0; i < len; ) {
+ fprintf(stdout, " ");
+ for (j = 0; p < &buf[len] && j < 16; j++, p++) {
+ fprintf(stdout, "%3d,", *p);
+ }
+ i += j;
+ fprintf(stdout, "\n");
+ }
+ }
+ fprintf(stdout, " 0 };\n\n");
+
+ close(fd);
+ nFile++;
+ }
+ fclose(lp);
+
+/*
+ * Now output the page index
+ */
+ fprintf(stdout, "websRomPageIndexType websRomPageIndex[] = {\n");
+
+ if ((lp = fopen(fileList, "r")) == NULL) {
+ fprintf(stderr, "Can't open file list %s\n", fileList);
+ return -1;
+ }
+ nFile = 0;
+ while (fgets(file, sizeof(file), lp) != NULL) {
+ if ((p = strchr(file, '\n')) || (p = strchr(file, '\r'))) {
+ *p = '\0';
+ }
+/*
+ * Remove the prefix and add a leading "/" when we print the path
+ */
+ if (strncmp(file, prefix, gstrlen(prefix)) == 0) {
+ cp = &file[gstrlen(prefix)];
+ } else {
+ cp = file;
+ }
+ if (*cp == '/') {
+ cp++;
+ }
+
+ if (gstat(file, &sbuf) == 0 && sbuf.st_mode & S_IFDIR) {
+ fprintf(stdout, " { T(\"/%s\"), 0, 0 },\n", cp);
+ continue;
+ }
+ fprintf(stdout, " { T(\"/%s\"), page_%d, %d },\n", cp, nFile,
+ sbuf.st_size);
+ nFile++;
+ }
+ fclose(lp);
+
+ fprintf(stdout, " { 0, 0, 0 },\n");
+ fprintf(stdout, "};\n");
+ fprintf(stdout, "#endif /* WEBS_PAGE_ROM */\n");
+
+ fclose(lp);
+ fflush(stdout);
+ return 0;
+}
+
+/******************************************************************************/
diff --git a/cpukit/httpd/webmain.c b/cpukit/httpd/webmain.c
new file mode 100644
index 0000000000..9c12cd394c
--- /dev/null
+++ b/cpukit/httpd/webmain.c
@@ -0,0 +1,410 @@
+/*
+ * main.c -- Main program for the GoAhead WebServer (LINUX version)
+ *
+ * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * Main program for for the GoAhead WebServer. This is a demonstration
+ * main program to initialize and configure the web server.
+ */
+
+/********************************* Includes ***********************************/
+
+#include "uemf.h"
+#include "wsIntrn.h"
+#include <signal.h>
+#include <sys/time.h>
+#include <pthread.h>
+
+/*********************************** Locals ***********************************/
+/*
+ * Change configuration here
+ */
+
+static char_t *rootWeb = T("web"); /* Root web directory */
+static char_t *password = T(""); /* Security password */
+static int port = 80; /* Server port */
+static int retries = 5; /* Server port retries */
+static int finished; /* Finished flag */
+
+/*
+ * Structure to hold timer events
+ */
+typedef struct {
+ void (*routine)(long arg); /* Timer routine */
+ long arg; /* Argument to routine */
+} websTimer_t;
+
+/* The following holds the pointer to an allocated websTimer_t structure .
+ * Using this method only one timer can be active at a time, but
+ * for the WebServer, this should be OK.
+ */
+websTimer_t *tp;
+
+/****************************** Forward Declarations **************************/
+
+static int initWebs();
+static int aspTest(int eid, webs_t wp, int argc, char_t **argv);
+static void formTest(webs_t wp, char_t *path, char_t *query);
+static int websHomePageHandler(webs_t wp, char_t *urlPrefix, char_t *webDir,
+ int arg, char_t* url, char_t* path, char_t* query);
+static void timerProc(int signo);
+#if B_STATS
+static void printMemStats(int handle, char_t *fmt, ...);
+static void memLeaks();
+#endif
+static timer_t timer_id;
+static void rtems_httpd_daemon();
+
+/*********************************** Code *************************************/
+/*
+ * Main -- entry point from LINUX
+ */
+int rtems_initialize_webserver()
+{
+ rtems_status_code sc;
+ rtems_id tid;
+ int priority;
+
+ /***********************************************************************
+ * Default HTTPD priority.
+ **********************************************************************/
+ priority = 40;
+
+ sc = rtems_task_create(rtems_build_name('H', 'T', 'P', 'D'),
+ priority, 8*1024,
+ RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR |
+ RTEMS_INTERRUPT_LEVEL(0),
+ RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
+ &tid);
+ if (sc != RTEMS_SUCCESSFUL)
+ {
+ return(RTEMS_UNSATISFIED);
+ }
+
+ sc = rtems_task_start(tid, rtems_httpd_daemon, 0);
+ if (sc != RTEMS_SUCCESSFUL)
+ {
+ return(RTEMS_UNSATISFIED);
+ }
+
+ return(RTEMS_SUCCESSFUL);
+
+}
+
+static void
+rtems_httpd_daemon()
+{
+/*
+ * Initialize the memory allocator. Allow use of malloc and start with a
+ * 10K heap.
+ */
+ bopen(NULL, (10 * 1024), B_USE_MALLOC);
+
+/*
+ * Initialize the web server
+ */
+ if (initWebs() < 0) {
+ rtems_panic("Unable to initialize Web server !!\n");
+ }
+
+/*
+ * Basic event loop. SocketReady returns true when a socket is ready for
+ * service. SocketSelect will block until an event occurs. SocketProcess
+ * will actually do the servicing.
+ */
+ while (!finished) {
+ if (socketReady() || socketSelect()) {
+ socketProcess();
+ }
+ }
+
+/*
+ * Close the socket module, report memory leaks and close the memory allocator
+ */
+ websCloseServer();
+ socketClose();
+#if B_STATS
+ memLeaks();
+#endif
+ bclose();
+ rtems_task_delete( RTEMS_SELF );
+}
+
+/******************************************************************************/
+/*
+ * Initialize the web server.
+ */
+
+static int initWebs()
+{
+ struct hostent* hp;
+ struct in_addr intaddr;
+ char host[128], dir[128], webdir[128];
+ char *cp;
+ char_t wbuf[128];
+
+/*
+ * Initialize the socket subsystem
+ */
+ socketOpen();
+
+/*
+ * Define the local Ip address, host name, default home page and the
+ * root web directory.
+ */
+ if (gethostname(host, sizeof(host)) < 0) {
+ error(E_L, E_LOG, T("Can't get hostname"));
+ return -1;
+ }
+
+/* intaddr.s_addr = (unsigned long) hostGetByName(host); */
+ if ((hp = gethostbyname(host)) == NULL) {
+ error(E_L, E_LOG, T("Can't get host address"));
+ return -1;
+ }
+ memcpy((char *) &intaddr, (char *) hp->h_addr_list[0],
+ (size_t) hp->h_length);
+
+/*
+ * Set ../web as the root web. Modify this to suit your needs
+ */
+ getcwd(dir, sizeof(dir));
+ if ((cp = strrchr(dir, '/'))) {
+ *cp = '\0';
+ }
+ sprintf(webdir, "%s/%s", dir, rootWeb);
+
+/*
+ * Configure the web server options before opening the web server
+ */
+ websSetDefaultDir(webdir);
+ ascToUni(wbuf, inet_ntoa(intaddr), sizeof(wbuf));
+ websSetIpaddr(wbuf);
+ ascToUni(wbuf, host, sizeof(wbuf));
+ websSetHost(wbuf);
+
+/*
+ * Configure the web server options before opening the web server
+ */
+ websSetDefaultPage(T("default.asp"));
+ websSetPassword(password);
+
+/*
+ * Open the web server on the given port. If that port is taken, try
+ * the next sequential port for up to "retries" attempts.
+ */
+ websOpenServer(port, retries);
+
+/*
+ * First create the URL handlers. Note: handlers are called in sorted order
+ * with the longest path handler examined first. Here we define the security
+ * handler, forms handler and the default web page handler.
+ */
+ websUrlHandlerDefine(T(""), NULL, 0, websSecurityHandler,
+ WEBS_HANDLER_FIRST);
+ websUrlHandlerDefine(T("/goform"), NULL, 0, websFormHandler, 0);
+ websUrlHandlerDefine(T(""), NULL, 0, websDefaultHandler,
+ WEBS_HANDLER_LAST);
+
+/*
+ * Now define two test procedures. Replace these with your application
+ * relevant ASP script procedures and form functions.
+ */
+ websAspDefine(T("aspTest"), aspTest);
+ websFormDefine(T("formTest"), formTest);
+
+/*
+ * Create a handler for the default home page
+ */
+ websUrlHandlerDefine(T("/"), NULL, 0, websHomePageHandler, 0);
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Test Javascript binding for ASP. This will be invoked when "aspTest" is
+ * embedded in an ASP page. See web/asp.asp for usage. Set browser to
+ * "localhost/asp.asp" to test.
+ */
+
+static int aspTest(int eid, webs_t wp, int argc, char_t **argv)
+{
+ char_t *name, *address;
+
+ if (ejArgs(argc, argv, T("%s %s"), &name, &address) < 2) {
+ websError(wp, 400, T("Insufficient args\n"));
+ return -1;
+ }
+ return websWrite(wp, T("Name: %s, Address %s"), name, address);
+}
+/******************************************************************************/
+/*
+ * Test form for posted data (in-memory CGI). This will be called when the
+ * form in web/asp.asp is invoked. Set browser to "localhost/asp.asp" to test.
+ */
+
+static void formTest(webs_t wp, char_t *path, char_t *query)
+{
+ char_t *name, *address;
+
+ name = websGetVar(wp, T("name"), T("Joe Smith"));
+ address = websGetVar(wp, T("address"), T("1212 Milky Way Ave."));
+
+ websHeader(wp);
+ websWrite(wp, T("<body><h2>Name: %s, Address: %s</h2>\n"), name, address);
+ websFooter(wp);
+ websDone(wp, 200);
+}
+
+/******************************************************************************/
+/*
+ * Create a timer to invoke the routine in "delay" milliseconds.
+ */
+
+void *emfCreateTimer(int delay, void (*routine)(long arg), long arg)
+{
+ struct sigaction act;
+ struct itimerspec its = { {0,0}, {0,0} };
+ struct sigevent se;
+ int status;
+
+ if ((tp = balloc(B_L, sizeof(websTimer_t)))) {
+ tp->routine = routine;
+ tp->arg = arg;
+ }
+ else {
+ return NULL;
+ }
+
+ se.sigev_notify = SIGEV_THREAD;
+ se.sigev_value.sival_ptr = tp;
+ se.sigev_notify_function = (void (*)(union sigval)) timerProc;
+
+ /*
+ * NOT POSIX?
+ * se.sigev_notify_attributes = NULL;
+ */
+
+
+ status = timer_create(CLOCK_REALTIME, &se, &timer_id);
+ if (status != 0) {
+ bfree(B_L, tp);
+ return NULL;
+ }
+ /* convert delay millisecs to secs and usecs required by struct */
+ its.it_value.tv_sec = delay / 1000;
+ its.it_value.tv_nsec = (delay % 1000) * 1000000;
+
+ status = timer_settime(timer_id, 0, &its, 0);
+ if (status != 0) {
+ bfree(B_L, tp);
+ return NULL;
+ }
+
+#if 0
+ act.sa_flags = 0;
+ sigemptyset(&act.sa_mask);
+ act.sa_handler = timerProc;
+ sigaction(SIGALRM, &act, NULL);
+
+ /* convert delay millisecs to secs and usecs required by struct */
+ its.it_value.tv_sec = delay / 1000;
+ its.it_value.tv_usec = (delay % 1000) * 1000;
+
+ if (setitimer(ITIMER_REAL, &its, NULL) == -1) {
+ bfree(B_L, tp);
+ return NULL;
+ }
+#endif
+ return tp;
+}
+
+/******************************************************************************/
+/*
+ * Delete a timer
+ */
+
+void emfDeleteTimer(void * id)
+{
+ websTimer_t *wtp;
+ /*struct itimerval its = { {0,0}, {0,0} };*/
+
+ wtp = (websTimer_t *)id;
+ /* setitimer(ITIMER_REAL, &its, NULL);*/
+ timer_delete(timer_id);
+ bfree(B_L, wtp);
+}
+
+/******************************************************************************/
+/*
+ * Timer handler
+ */
+
+static void timerProc(int signo)
+{
+ websTimer_t wtp = *tp;
+
+/* Copy the timer structure to a local first and delete it before calling
+ * the function, since the function could create another timer. In this
+ * implementation, only one timer can be allocated at a time.
+ */
+
+ bfree(B_L, tp);
+ (wtp.routine)(wtp.arg);
+}
+
+/******************************************************************************/
+/*
+ * Home page handler
+ */
+
+static int websHomePageHandler(webs_t wp, char_t *urlPrefix, char_t *webDir,
+ int arg, char_t* url, char_t* path, char_t* query)
+{
+/*
+ * If the empty or "/" URL is invoked, redirect default URLs to the home page
+ */
+ if (*url == '\0' || gstrcmp(url, T("/")) == 0) {
+ websRedirect(wp, T("home.asp"));
+ return 1;
+ }
+ return 0;
+}
+
+/******************************************************************************/
+
+#if B_STATS
+static void memLeaks()
+{
+ int fd;
+
+ if ((fd = gopen(T("leak.txt"), O_CREAT | O_TRUNC | O_WRONLY)) >= 0) {
+ bstats(fd, printMemStats);
+ close(fd);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Print memory usage / leaks
+ */
+
+static void printMemStats(int handle, char_t *fmt, ...)
+{
+ va_list args;
+ char_t buf[256];
+
+ va_start(args, fmt);
+ vsprintf(buf, fmt, args);
+ va_end(args);
+ write(handle, buf, strlen(buf));
+}
+#endif
+
+/******************************************************************************/
diff --git a/cpukit/httpd/webpage.c b/cpukit/httpd/webpage.c
new file mode 100644
index 0000000000..d2b976e38f
--- /dev/null
+++ b/cpukit/httpd/webpage.c
@@ -0,0 +1,138 @@
+/*
+ * Page.c -- Support for page retrieval.
+ *
+ * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * This module provides page retrieval handling. It provides support for
+ * reading web pages from file systems and has expansion for ROMed web
+ * pages.
+ */
+
+/********************************* Includes ***********************************/
+
+#include "wsIntrn.h"
+
+/*********************************** Code *************************************/
+/*
+ * Open a web page. lpath is the local filename. path is the URL path name.
+ */
+
+int websPageOpen(webs_t wp, char_t *lpath, char_t *path, int mode, int perm)
+{
+ a_assert(websValid(wp));
+
+#if WEBS_PAGE_ROM
+ return websRomPageOpen(wp, path, mode, perm);
+#else
+ return (wp->docfd = gopen(lpath, mode, perm));
+#endif /* WEBS_PAGE_ROM */
+}
+
+/******************************************************************************/
+/*
+ * Close a web page
+ */
+
+void websPageClose(webs_t wp)
+{
+#if WEBS_PAGE_ROM
+ websRomPageClose(wp->docfd);
+#else
+ if (wp->docfd >= 0) {
+ close(wp->docfd);
+ wp->docfd = -1;
+ }
+#endif
+}
+
+/******************************************************************************/
+/*
+ * Stat a web page lpath is the local filename. path is the URL path name.
+ */
+
+int websPageStat(webs_t wp, char_t *lpath, char_t *path, websStatType* sbuf)
+{
+#if WEBS_PAGE_ROM
+ return websRomPageStat(path, sbuf);
+#else
+ gstat_t s;
+
+ if (gstat(lpath, &s) < 0) {
+ return -1;
+ }
+ sbuf->size = s.st_size;
+ sbuf->mtime = s.st_mtime;
+ sbuf->isDir = s.st_mode & S_IFDIR;
+ return 0;
+#endif
+}
+
+/******************************************************************************/
+/*
+ * Is this file a directory?
+ */
+
+int websPageIsDirectory(char_t *lpath)
+{
+#if WEBS_PAGE_ROM
+ websStatType sbuf;
+
+ if (websRomPageStat(lpath, &sbuf) >= 0) {
+ return(sbuf.isDir);
+ } else {
+ return 0;
+ }
+#else
+ gstat_t sbuf;
+
+ if (gstat(lpath, &sbuf) >= 0) {
+ return(sbuf.st_mode & S_IFDIR);
+ } else {
+ return 0;
+ }
+#endif
+}
+
+
+/******************************************************************************/
+/*
+ * Read a web page. Returns the number of _bytes_ read.
+ * len is the size of buf, in bytes.
+ */
+
+int websPageReadData(webs_t wp, char *buf, int nBytes)
+{
+
+#if WEBS_PAGE_ROM
+ a_assert(websValid(wp));
+ return websRomPageReadData(wp, buf, nBytes);
+#else
+ a_assert(websValid(wp));
+ return read(wp->docfd, buf, nBytes);
+#endif
+}
+
+/******************************************************************************/
+/*
+ * Move file pointer offset bytes.
+ */
+
+void websPageSeek(webs_t wp, long offset)
+{
+ a_assert(websValid(wp));
+
+#if WEBS_PAGE_ROM
+ websRomPageSeek(wp, offset, SEEK_CUR);
+#else
+ lseek(wp->docfd, offset, SEEK_CUR);
+#endif
+}
+
+/******************************************************************************/
+
diff --git a/cpukit/httpd/webrom.c b/cpukit/httpd/webrom.c
new file mode 100644
index 0000000000..225d8bbf22
--- /dev/null
+++ b/cpukit/httpd/webrom.c
@@ -0,0 +1,12 @@
+/*
+ * webrom.c -- Compiled Web Pages
+ *
+ * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved.
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+#include "wsIntrn.h"
+
+websRomPageIndexType websRomPageIndex[] = {
+ { 0, 0, 0 },
+};
diff --git a/cpukit/httpd/webs.c b/cpukit/httpd/webs.c
new file mode 100644
index 0000000000..e198cc1e9a
--- /dev/null
+++ b/cpukit/httpd/webs.c
@@ -0,0 +1,1841 @@
+/*
+ * webs.c -- GoAhead Embedded HTTP webs server
+ *
+ * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/******************************** Description *********************************/
+
+/*
+ * This module implements an embedded HTTP/1.1 webs server. It supports
+ * loadable URL handlers that define the nature of URL processing performed.
+ */
+
+/********************************* Includes ***********************************/
+
+#include "wsIntrn.h"
+
+/******************************** Global Data *********************************/
+
+websStatsType websStats; /* Web access stats */
+webs_t *webs; /* Open connection list head */
+sym_fd_t websMime; /* Set of mime types */
+int websMax; /* List size */
+int websPort; /* Listen port for server */
+char_t websHost[64]; /* Host name for the server */
+char_t websIpaddr[64]; /* IP address for the server */
+char_t *websHostUrl = NULL; /* URL to access server */
+
+/*********************************** Locals ***********************************/
+/*
+ * Standard HTTP error codes
+ */
+
+websErrorType websErrors[] = {
+ { 200, T("Data follows") },
+ { 204, T("No Content") },
+ { 301, T("Redirect") },
+ { 302, T("Redirect") },
+ { 304, T("User local copy") },
+ { 400, T("Page not found") },
+ { 401, T("Password Required") },
+ { 404, T("Site or Page Not Found") },
+ { 405, T("Access Denied") },
+ { 500, T("Web Error") },
+ { 503, T("Site Temporarily Unavailable. Try again") },
+ { 0, NULL }
+};
+
+#if WEBS_LOG_SUPPORT
+static char_t websLogname[64] = T("log.txt"); /* Log filename */
+static int websLogFd; /* Log file handle */
+#endif
+
+static int websListenSock; /* Listen socket */
+
+/**************************** Forward Declarations ****************************/
+
+static int websAccept(int sid, char *ipaddr, int port);
+static int websAlloc(int sid);
+static char_t *websErrorMsg(int code);
+static void websFree(webs_t wp);
+static void websFreeVar(sym_t* sp);
+static int websGetInput(webs_t wp, char_t **ptext, int *nbytes);
+static int websParseFirst(webs_t wp, char_t *text);
+static void websParseRequest(webs_t wp);
+static void websReadEvent(webs_t wp);
+static void websSocketEvent(int sid, int mask, int data);
+static void websTimeout(long wp);
+static void websTimeoutCancel(webs_t wp);
+static void websMarkTime(webs_t wp);
+static int websGetTimeSinceMark(webs_t wp);
+
+#if WEBS_LOG_SUPPORT
+static void websLog(webs_t wp, int code);
+#endif
+#if WEBS_IF_MODIFIED_SUPPORT
+static time_t dateParse(time_t tip, char_t *cmd);
+#endif
+
+/*********************************** Code *************************************/
+/*
+ * Open the GoAhead WebServer
+ */
+
+int websOpenServer(int port, int retries)
+{
+ websMimeType *mt;
+
+ a_assert(port > 0);
+ a_assert(retries >= 0);
+
+#if WEBS_PAGE_ROM
+ websRomOpen();
+#endif
+
+/*
+ * Create a mime type lookup table for quickly determining the content type
+ */
+ websMime = symOpen(256);
+ a_assert(websMime >= 0);
+ for (mt = websMimeList; mt->type; mt++) {
+ symEnter(websMime, mt->ext, valueString(mt->type, 0), 0);
+ }
+
+/*
+ * Open the URL handler module. The caller should create the required
+ * URL handlers after calling this function.
+ */
+ if (websUrlHandlerOpen() < 0) {
+ return -1;
+ }
+
+#if WEBS_LOG_SUPPORT
+/*
+ * Optional request log support
+ */
+ websLogFd = gopen(websLogname, O_CREAT | O_TRUNC | O_APPEND | O_WRONLY,
+ 0666);
+ a_assert(websLogFd >= 0);
+#endif
+
+ return websOpenListen(port, retries);
+}
+
+/******************************************************************************/
+/*
+ * Close the GoAhead WebServer
+ */
+
+void websCloseServer()
+{
+ webs_t wp;
+ int wid;
+
+/*
+ * Close the listen handle first then all open connections.
+ */
+ websCloseListen();
+
+/*
+ * Close each open browser connection and free all resources
+ */
+ for (wid = websMax; webs && wid >= 0; wid--) {
+ if ((wp = webs[wid]) == NULL) {
+ continue;
+ }
+ socketCloseConnection(wp->sid);
+ websFree(wp);
+ }
+
+#if WEBS_LOG_SUPPORT
+ if (websLogFd >= 0) {
+ close(websLogFd);
+ websLogFd = -1;
+ }
+#endif
+
+#if WEBS_PAGE_ROM
+ websRomClose();
+#endif
+ symClose(websMime, NULL);
+ websFormClose();
+ websUrlHandlerClose();
+}
+
+/******************************************************************************/
+/*
+ * Open the GoAhead WebServer listen port
+ */
+
+int websOpenListen(int port, int retries)
+{
+ int i, orig;
+
+ a_assert(port > 0);
+ a_assert(retries >= 0);
+
+ orig = port;
+/*
+ * Open the webs webs listen port. If we fail, try the next port.
+ */
+ for (i = 0; i <= retries; i++) {
+ websListenSock = socketOpenConnection(NULL, port, websAccept, 0);
+ if (websListenSock >= 0) {
+ break;
+ }
+ port++;
+ }
+ if (i > retries) {
+ error(E_L, E_USER, T("Couldn't open a socket on ports %d - %d"),
+ orig, port - 1);
+ return -1;
+ }
+ trace(0, T("webs: Listening for HTTP requests on port %d\n"), port);
+
+/*
+ * Determine the full URL address to access the home page for this web server
+ */
+ websPort = port;
+ bfreeSafe(B_L, websHostUrl);
+ websHostUrl = NULL;
+ if (port == 80) {
+ websHostUrl = bstrdup(B_L, websHost);
+ } else {
+ gsnprintf(&websHostUrl, WEBS_MAX_URL + 80, T("%s:%d"), websHost, port);
+ }
+ return port;
+}
+
+/******************************************************************************/
+/*
+ * Close webs listen port
+ */
+
+void websCloseListen()
+{
+ if (websListenSock >= 0) {
+ socketCloseConnection(websListenSock);
+ websListenSock = -1;
+ }
+ bfreeSafe(B_L, websHostUrl);
+ websHostUrl = NULL;
+}
+
+/******************************************************************************/
+/*
+ * Accept a connection
+ */
+
+static int websAccept(int sid, char *ipaddr, int port)
+{
+ webs_t wp;
+ int wid;
+
+ a_assert(ipaddr && *ipaddr);
+ a_assert(sid >= 0);
+ a_assert(port >= 0);
+
+/*
+ * Allocate a new handle for this accepted connection. This will allocate
+ * a webs_t structure in the webs[] list
+ */
+ if ((wid = websAlloc(sid)) < 0) {
+ return -1;
+ }
+ wp = webs[wid];
+ a_assert(wp);
+
+ ascToUni(wp->ipaddr, ipaddr, sizeof(wp->ipaddr));
+
+/*
+ * Check if this is a request from a browser on this system. This is useful
+ * to know for permitting administrative operations only for local access
+ */
+ if (gstrcmp(wp->ipaddr, T("127.0.0.1")) == 0 ||
+ gstrcmp(wp->ipaddr, websIpaddr) == 0 ||
+ gstrcmp(wp->ipaddr, websHost) == 0) {
+ wp->flags |= WEBS_LOCAL_REQUEST;
+ }
+
+/*
+ * Arrange for websSocketEvent to be called when read data is available
+ */
+ socketCreateHandler(sid, SOCKET_READABLE , websSocketEvent, (int) wp);
+
+/*
+ * Arrange for a timeout to kill hung requests
+ */
+ wp->timeout = emfCreateTimer(WEBS_TIMEOUT, websTimeout, (long) wp);
+ trace(5, T("webs: accept request\n"));
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * The webs socket handler. Called in response to I/O. We just pass control
+ * to the relevant read or write handler. A pointer to the webs structure
+ * is passed as an (int) in iwp.
+ */
+
+static void websSocketEvent(int sid, int mask, int iwp)
+{
+ webs_t wp;
+
+ wp = (webs_t) iwp;
+ a_assert(wp);
+
+ if (! websValid(wp)) {
+ return;
+ }
+
+ if (mask & SOCKET_READABLE) {
+ websReadEvent(wp);
+ }
+ if (mask & SOCKET_WRITABLE) {
+ if (wp->writeSocket) {
+ (*wp->writeSocket)(wp);
+ }
+ }
+}
+
+/******************************************************************************/
+/*
+ * The webs read handler. This is the primary read event loop. It uses a
+ * state machine to track progress while parsing the HTTP request.
+ * Note: we never block as the socket is always in non-blocking mode.
+ */
+
+static void websReadEvent(webs_t wp)
+{
+ char_t *text;
+ int rc, nbytes, len, done;
+
+ a_assert(wp);
+ a_assert(websValid(wp));
+
+ websMarkTime(wp);
+
+/*
+ * Read as many lines as possible. socketGets is called to read the header
+ * and socketRead is called to read posted data.
+ */
+ text = NULL;
+ for (done = 0; !done; ) {
+ if (text) {
+ bfree(B_L, text);
+ text = NULL;
+ }
+
+/*
+ * Get more input into "text". Returns 0, if more data is needed
+ * to continue, -1 if finished with the request, or 1 if all
+ * required data is available for current state.
+ */
+ while ((rc = websGetInput(wp, &text, &nbytes)) == 0) {
+ ;
+ }
+
+/*
+ * websGetInput returns -1 if it finishes with the request
+ */
+ if (rc < 0) {
+ break;
+ }
+
+/*
+ * This is the state machine for the web server.
+ */
+ switch(wp->state) {
+ case WEBS_BEGIN:
+/*
+ * Parse the first line of the Http header
+ */
+ if (websParseFirst(wp, text) < 0) {
+ done++;
+ break;
+ }
+ wp->state = WEBS_HEADER;
+ break;
+
+ case WEBS_HEADER:
+/*
+ * Store more of the HTTP header. As we are doing line reads, we
+ * need to separate the lines with '\n'
+ */
+ if (ringqLen(&wp->header) > 0) {
+ ringqPutstr(&wp->header, T("\n"));
+ }
+ ringqPutstr(&wp->header, text);
+ break;
+
+ case WEBS_POST_CLEN:
+/*
+ * POST request with content specified by a content length
+ */
+ if (wp->query) {
+ if (wp->query[0] && !(wp->flags & WEBS_POST_DATA)) {
+/*
+ * Special case where the POST request also had query data
+ * specified in the URL, ie. url?query_data. In this case
+ * the URL query data is separated by a '&' from the posted
+ * query data.
+ */
+ len = gstrlen(wp->query);
+ wp->query = brealloc(B_L, wp->query, (len + gstrlen(text) +
+ 2) * sizeof(char_t));
+ wp->query[len++] = '&';
+ gstrcpy(&wp->query[len], text);
+
+ } else {
+/*
+ * The existing query data came from the POST request so just
+ * append it.
+ */
+ len = gstrlen(wp->query);
+ wp->query = brealloc(B_L, wp->query, (len + gstrlen(text) +
+ 1) * sizeof(char_t));
+ if (wp->query) {
+ gstrcpy(&wp->query[len], text);
+ }
+ }
+
+ } else {
+ wp->query = bstrdup(B_L, text);
+ }
+/*
+ * Calculate how much more post data is to be read.
+ */
+ wp->flags |= WEBS_POST_DATA;
+ wp->clen -= nbytes;
+ if (wp->clen > 0) {
+ if (nbytes > 0) {
+ done++;
+ break;
+ }
+ done++;
+ break;
+ }
+/*
+ * No more data so process the request
+ */
+ websUrlHandlerRequest(wp);
+ done++;
+ break;
+
+ case WEBS_POST:
+/*
+ * POST without content-length specification
+ */
+ if (wp->query && *wp->query && !(wp->flags & WEBS_POST_DATA)) {
+ len = gstrlen(wp->query);
+ wp->query = brealloc(B_L, wp->query, (len + gstrlen(text) +
+ 2) * sizeof(char_t));
+ if (wp->query) {
+ wp->query[len++] = '&';
+ gstrcpy(&wp->query[len], text);
+ }
+
+ } else {
+ wp->query = bstrdup(B_L, text);
+ }
+ wp->flags |= WEBS_POST_DATA;
+ done++;
+ break;
+
+ default:
+ websError(wp, 404, T("Bad state"));
+ done++;
+ break;
+ }
+ }
+ if (text) {
+ bfree(B_L, text);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Get input from the browser. Return TRUE (!0) if the request has been
+ * handled. Return -1 on errors, 1 if input read, and 0 to instruct the
+ * caller to call again for more input.
+ *
+ * Note: socketRead will Return the number of bytes read if successful. This
+ * may be less than the requested "bufsize" and may be zero. It returns -1 for
+ * errors. It returns 0 for EOF. Otherwise it returns the number of bytes
+ * read. Since this may be zero, callers should use socketEof() to
+ * distinguish between this and EOF.
+ */
+
+static int websGetInput(webs_t wp, char_t **ptext, int *pnbytes)
+{
+ char_t *text;
+ char buf[WEBS_SOCKET_BUFSIZ+1];
+ int nbytes, len, clen;
+
+ a_assert(websValid(wp));
+ a_assert(ptext);
+ a_assert(pnbytes);
+
+ *ptext = text = NULL;
+ *pnbytes = 0;
+
+/*
+ * If this request is a POST with a content length, we know the number
+ * of bytes to read so we use socketRead().
+ */
+ if (wp->state == WEBS_POST_CLEN) {
+ len = (wp->clen > WEBS_SOCKET_BUFSIZ) ? WEBS_SOCKET_BUFSIZ : wp->clen;
+ } else {
+ len = 0;
+ }
+
+ if (len > 0) {
+ nbytes = socketRead(wp->sid, buf, len);
+
+ if (nbytes < 0) { /* Error */
+ websDone(wp, 0);
+ return -1;
+
+ } else if (nbytes == 0) { /* EOF or No data available */
+ return -1;
+
+ } else { /* Valid data */
+/*
+ * Convert to UNICODE if necessary. First be sure the string
+ * is NULL terminated.
+ */
+ buf[nbytes] = '\0';
+ if ((text = ballocAscToUni(buf)) == NULL) {
+ websError(wp, 503, T("Insufficient memory"));
+ return -1;
+ }
+ }
+
+ } else {
+ nbytes = socketGets(wp->sid, &text);
+
+ if (nbytes < 0) {
+/*
+ * Error, EOF or incomplete
+ */
+ if (socketEof(wp->sid)) {
+/*
+ * If this is a post request without content length, process
+ * the request as we now have all the data. Otherwise just
+ * close the connection.
+ */
+ if (wp->state == WEBS_POST) {
+ websUrlHandlerRequest(wp);
+ } else {
+ websDone(wp, 0);
+ }
+ }
+/*
+ * If state is WEBS_HEADER and the ringq is empty, then this is a
+ * simple request with no additional header fields to process and
+ * no empty line terminator.
+ */
+ if (wp->state == WEBS_HEADER && ringqLen(&wp->header) <= 0) {
+ websParseRequest(wp);
+ websUrlHandlerRequest(wp);
+ }
+ return -1;
+
+ } else if (nbytes == 0) {
+ if (wp->state == WEBS_HEADER) {
+/*
+ * Valid empty line, now finished with header
+ */
+ websParseRequest(wp);
+ if (wp->flags & WEBS_POST_REQUEST) {
+ if (wp->flags & WEBS_CLEN) {
+ wp->state = WEBS_POST_CLEN;
+ clen = wp->clen;
+ } else {
+ wp->state = WEBS_POST;
+ clen = 1;
+ }
+ if (clen > 0) {
+ return 0; /* Get more data */
+ }
+ return 1;
+
+ }
+/*
+ * We've read the header so go and handle the request
+ */
+ websUrlHandlerRequest(wp);
+ }
+ return -1;
+
+ }
+ }
+ a_assert(text);
+ a_assert(nbytes > 0);
+ *ptext = text;
+ *pnbytes = nbytes;
+ return 1;
+}
+
+/******************************************************************************/
+/*
+ * Parse the first line of a HTTP request
+ */
+
+static int websParseFirst(webs_t wp, char_t *text)
+{
+ char_t *op, *proto, *url, *host, *query, *path, *port, *ext, *buf;
+
+ a_assert(websValid(wp));
+ a_assert(text && *text);
+
+/*
+ * Determine the request type: GET, HEAD or POST
+ */
+ op = gstrtok(text, T(" \t"));
+ if (op == NULL || *op == '\0') {
+ websError(wp, 400, T("Bad HTTP request"));
+ return -1;
+ }
+ if (gstrcmp(op, T("GET")) != 0) {
+ if (gstrcmp(op, T("POST")) == 0) {
+ wp->flags |= WEBS_POST_REQUEST;
+ } else if (gstrcmp(op, T("HEAD")) == 0) {
+ wp->flags |= WEBS_HEAD_REQUEST;
+ } else {
+ websError(wp, 400, T("Bad request type"));
+ return -1;
+ }
+ }
+
+/*
+ * Store result in the form (CGI) variable store
+ */
+ websSetVar(wp, T("REQUEST_METHOD"), op);
+
+ url = gstrtok(NULL, T(" \t\n"));
+ if (url == NULL || *url == '\0') {
+ websError(wp, 400, T("Bad HTTP request"));
+ return -1;
+ }
+
+/*
+ * Parse the URL and store all the various URL components. websUrlParse
+ * returns an allocated buffer in buf which we must free. We support both
+ * proxied and non-proxied requests. Proxied requests will have http://host/
+ * at the start of the URL. Non-proxied will just be local path names.
+ */
+ host = path = port = proto = query = ext = NULL;
+ if (websUrlParse(url, &buf, &host, &path, &port, &query, &proto,
+ NULL, &ext) < 0) {
+ websError(wp, 400, T("Bad URL format"));
+ return -1;
+ }
+
+ wp->url = bstrdup(B_L, url);
+ wp->query = bstrdup(B_L, query);
+ wp->host = bstrdup(B_L, host);
+ wp->path = bstrdup(B_L, path);
+ wp->port = gatoi(port);
+ if (gstrcmp(ext, T(".asp")) == 0) {
+ wp->flags |= WEBS_ASP;
+ }
+ bfree(B_L, buf);
+
+ websUrlType(url, wp->type, TSZ(wp->type));
+
+#if WEBS_PROXY_SUPPORT
+/*
+ * Determine if this is a request for local webs data. If it is not a proxied
+ * request from the browser, we won't see the "http://" or the system name, so
+ * we assume it must be talking to us directly for local webs data.
+ * Note: not fully implemented yet.
+ */
+ if (gstrstr(wp->url, T("http://")) == NULL ||
+ ((gstrcmp(wp->host, T("localhost")) == 0 ||
+ gstrcmp(wp->host, websHost) == 0) && (wp->port == websPort))) {
+ wp->flags |= WEBS_LOCAL_PAGE;
+ if (gstrcmp(wp->path, T("/")) == 0) {
+ wp->flags |= WEBS_HOME_PAGE;
+ }
+ }
+#endif
+
+ ringqFlush(&wp->header);
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Parse a full request
+ */
+
+static void websParseRequest(webs_t wp)
+{
+ char_t *upperKey, *cp, *browser, *lp, *key, *value;
+
+ a_assert(websValid(wp));
+
+/*
+ * Define default CGI values
+ */
+ websSetVar(wp, T("HTTP_AUTHORIZATION"), T(""));
+
+/*
+ * Parse the header and create the Http header keyword variables
+ * We rewrite the header as we go for non-local requests. NOTE: this
+ * modifies the header string directly and tokenizes each line with '\0'.
+ */
+ browser = NULL;
+ for (lp = (char_t*) wp->header.servp; lp && *lp; ) {
+ cp = lp;
+ if ((lp = gstrchr(lp, '\n')) != NULL) {
+ lp++;
+ }
+
+ if ((key = gstrtok(cp, T(": \t\n"))) == NULL) {
+ continue;
+ }
+
+ if ((value = gstrtok(NULL, T("\n"))) == NULL) {
+ value = T("");
+ }
+
+ while (gisspace(*value)) {
+ value++;
+ }
+ strlower(key);
+
+/*
+ * Create a variable (CGI) for each line in the header
+ */
+ gsnprintf(&upperKey, (gstrlen(key) + 6), T("HTTP_%s"), key);
+ for (cp = upperKey; *cp; cp++) {
+ if (*cp == '-')
+ *cp = '_';
+ }
+ strupper(upperKey);
+ websSetVar(wp, upperKey, value);
+ bfree(B_L, upperKey);
+
+/*
+ * Track the requesting agent (browser) type
+ */
+ if (gstrcmp(key, T("user-agent")) == 0) {
+ wp->userAgent = bstrdup(B_L, value);
+
+/*
+ * Parse the user authorization. ie. password
+ */
+ } else if (gstrcmp(key, T("authorization")) == 0) {
+ char_t password[FNAMESIZE];
+
+/*
+ * The incoming value is password:username
+ */
+ if ((cp = gstrchr(value, ' ')) != NULL) {
+ websDecode64(password, ++cp, sizeof(password));
+ } else {
+ websDecode64(password, value, sizeof(password));
+ }
+ if ((cp = gstrchr(password, ':')) != NULL) {
+ *cp++ = '\0';
+ }
+ if (cp) {
+ wp->password = bstrdup(B_L, cp);
+ } else {
+ wp->password = bstrdup(B_L, T(""));
+ }
+
+/*
+ * Parse the content length
+ */
+ } else if (gstrcmp(key, T("content-length")) == 0) {
+ wp->flags |= WEBS_CLEN;
+ wp->clen = gatoi(value);
+ websSetVar(wp, T("CONTENT_LENGTH"), value);
+
+#if WEBS_KEEP_ALIVE_SUPPORT
+ } else if (gstrcmp(key, T("connection")) == 0) {
+ strlower(value);
+ if (gstrcmp(value, T("keep-alive")) == 0) {
+ wp->flags |= WEBS_KEEP_ALIVE;
+ }
+#endif
+
+#if WEBS_PROXY_SUPPORT
+/*
+ * This may be useful if you wish to keep a local cache of web pages
+ * for proxied requests.
+ */
+ } else if (gstrcmp(key, T("pragma")) == 0) {
+ char_t tmp[256];
+ gstrncpy(tmp, value, TSZ(tmp));
+ strlower(tmp);
+ if (gstrstr(tmp, T("no-cache"))) {
+ wp->flags |= WEBS_DONT_USE_CACHE;
+ }
+#endif
+
+/*
+ * Store the cookie
+ */
+ } else if (gstrcmp(key, T("cookie")) == 0) {
+ wp->flags |= WEBS_COOKIE;
+ wp->cookie = bstrdup(B_L, value);
+
+#if WEBS_IF_MODIFIED_SUPPORT
+/*
+ * See if the local page has been modified since the browser last
+ * requested this document. If not, just return a 302
+ */
+ } else if (gstrcmp(key, T("if-modified-since")) == 0) {
+ char_t *cmd;
+ time_t tip = 0;
+
+ if (cp = gstrchr(value, ';')) {
+ *cp = '\0';
+ }
+
+ if (cp = gstrstr(value, T(", "))) {
+ cp += 2;
+ }
+
+ if (gstrstr(cp, T("GMT"))) {
+ gsnprintf(&cmd, 64, T("clock scan %s -gmt 1"), cp);
+ } else {
+ gsnprintf(&cmd, 64, T("clock scan %s"), cp);
+ }
+ if (wp->since = dateParse(tip, cmd)) {
+ wp->flags |= WEBS_IF_MODIFIED;
+ }
+ bfreeSafe(B_L, cmd);
+#endif
+ }
+ }
+}
+
+
+#if WEBS_IF_MODIFIED_SUPPORT
+/******************************************************************************/
+/*
+ * Parse the date and time string.
+ */
+
+static time_t dateParse(time_t tip, char_t *cmd)
+{
+ return (time_t)0;
+}
+#endif
+
+
+/******************************************************************************/
+/*
+ * Set the variable (CGI) environment for this request. Create variables
+ * for all standard CGI variables. Also decode the query string and create
+ * a variable for each name=value pair.
+ */
+
+void websSetEnv(webs_t wp)
+{
+ char_t portBuf[8];
+ char_t *keyword, *value;
+
+ a_assert(websValid(wp));
+
+ websSetVar(wp, T("QUERY_STRING"), wp->query);
+ websSetVar(wp, T("GATEWAY_INTERFACE"), T("CGI/1.1"));
+ websSetVar(wp, T("SERVER_HOST"), websHost);
+ websSetVar(wp, T("SERVER_URL"), websHostUrl);
+ websSetVar(wp, T("REMOTE_HOST"), wp->ipaddr);
+ websSetVar(wp, T("REMOTE_ADDR"), wp->ipaddr);
+ websSetVar(wp, T("PATH_INFO"), wp->path);
+ stritoa(websPort, portBuf, sizeof(portBuf));
+ websSetVar(wp, T("SERVER_PORT"), portBuf);
+
+/*
+ * Decode and create an environment query variable for each query keyword.
+ * We split into pairs at each '&', then split pairs at the '='.
+ * Note: we rely on wp->decodedQuery preserving the decoded values in the
+ * symbol table.
+ */
+ wp->decodedQuery = bstrdup(B_L, wp->query);
+ keyword = gstrtok(wp->decodedQuery, T("&"));
+ while (keyword != NULL) {
+ if ((value = gstrchr(keyword, '=')) != NULL) {
+ *value++ = '\0';
+ websDecodeUrl(keyword, keyword, gstrlen(keyword));
+ websDecodeUrl(value, value, gstrlen(value));
+
+ } else {
+ value = T("");
+ }
+
+ if (*keyword) {
+ websSetVar(wp, keyword, value);
+ }
+ keyword = gstrtok(NULL, T("&"));
+ }
+
+#if EMF
+/*
+ * Add GoAhead Embedded Management Framework defines
+ */
+ websSetEmfEnvironment(wp);
+#endif
+}
+
+/******************************************************************************/
+/*
+ * Define a webs (CGI) variable for this connection. Also create in relevant
+ * scripting engines. Note: the incoming value may be volatile.
+ */
+
+void websSetVar(webs_t wp, char_t *var, char_t *value)
+{
+ value_t v;
+
+ a_assert(websValid(wp));
+
+/*
+ * value_instring will allocate the string if required.
+ */
+ if (value) {
+ v = valueString(value, VALUE_ALLOCATE);
+ } else {
+ v = valueString(T(""), VALUE_ALLOCATE);
+ }
+ symEnter(wp->cgiVars, var, v, 0);
+}
+
+/******************************************************************************/
+/*
+ * Return TRUE if a webs variable exists for this connection.
+ */
+
+int websTestVar(webs_t wp, char_t *var)
+{
+ sym_t *sp;
+
+ a_assert(websValid(wp));
+
+ if (var == NULL || *var == '\0') {
+ return 0;
+ }
+
+ if ((sp = symLookup(wp->cgiVars, var)) == NULL) {
+ return 0;
+ }
+ return 1;
+}
+
+/******************************************************************************/
+/*
+ * Get a webs variable but return a default value if string not found.
+ * Note, defaultGetValue can be NULL to permit testing existance.
+ */
+
+char_t *websGetVar(webs_t wp, char_t *var, char_t *defaultGetValue)
+{
+ sym_t *sp;
+
+ a_assert(websValid(wp));
+ a_assert(var && *var);
+
+ if ((sp = symLookup(wp->cgiVars, var)) != NULL) {
+ a_assert(sp->content.type == string);
+ if (sp->content.value.string) {
+ return sp->content.value.string;
+ } else {
+ return T("");
+ }
+ }
+ return defaultGetValue;
+}
+
+/******************************************************************************/
+/*
+ * Cancel the request timeout. Note may be called multiple times.
+ */
+
+static void websTimeoutCancel(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ if (wp->timeout) {
+ emfDeleteTimer(wp->timeout);
+ wp->timeout = NULL;
+ }
+}
+
+/******************************************************************************/
+/*
+ * Output a HTTP response back to the browser. If redirect is set to a
+ * URL, the browser will be sent to this location.
+ */
+
+void websResponse(webs_t wp, int code, char_t *message, char_t *redirect)
+{
+ char_t *date;
+
+ a_assert(websValid(wp));
+
+/*
+ * IE3.0 needs no Keep Alive for some return codes.
+ */
+ wp->flags &= ~WEBS_KEEP_ALIVE;
+
+/*
+ * Only output the header if a header has not already been output.
+ */
+ if ( !(wp->flags & WEBS_HEADER_DONE)) {
+ wp->flags |= WEBS_HEADER_DONE;
+ websWrite(wp, T("HTTP/1.0 %d %s\r\n"), code, websErrorMsg(code));
+
+ /* by license terms the following line of code must
+ * not be modified.
+ */
+ websWrite(wp, T("Server: GoAhead-Webs\r\n"));
+
+ if (wp->flags & WEBS_KEEP_ALIVE) {
+ websWrite(wp, T("Connection: keep-alive\r\n"));
+ }
+ websWrite(wp, T("Pragma: no-cache\r\nCache-Control: no-cache\r\n"));
+
+ if ((date = websGetDateString(NULL)) != NULL) {
+ websWrite(wp, T("Date: %s\r\n"), date);
+ bfree(B_L, date);
+ }
+ websWrite(wp, T("Content-Type: text/html\r\n"));
+
+/*
+ * We don't do a string length here as the message may be multi-line.
+ * Ie. <CR><LF> will count as only one and we will have a content-length
+ * that is too short.
+ *
+ * websWrite(wp, T("Content-Length: %s\r\n"), message);
+ */
+ if (redirect) {
+ websWrite(wp, T("Location: %s\r\n"), redirect);
+ }
+ websWrite(wp, T("\r\n"));
+ }
+
+ if (message && *message) {
+ websWrite(wp, T("%s\r\n"), message);
+ }
+ websDone(wp, code);
+}
+
+/******************************************************************************/
+/*
+ * Redirect the user to another webs page
+ */
+
+void websRedirect(webs_t wp, char_t *url)
+{
+ char_t *msgbuf, *urlbuf;
+
+ a_assert(websValid(wp));
+ a_assert(url);
+
+ websStats.redirects++;
+ msgbuf = urlbuf = NULL;
+
+/*
+ * Some browsers require a http://host qualified URL for redirection
+ */
+ if (gstrstr(url, T("http://")) == NULL) {
+ if (*url == '/') {
+ url++;
+ }
+ gsnprintf(&urlbuf, WEBS_MAX_URL + 80, T("http://%s/%s"),
+ websGetVar(wp, T("HTTP_HOST"), websHostUrl), url);
+ url = urlbuf;
+ }
+
+/*
+ * Add human readable message for completeness. Should not be required.
+ */
+ gsnprintf(&msgbuf, WEBS_MAX_URL + 80,
+ T("<html><head></head><body>\r\n\
+ This document has moved to a new <a href=\"%s\">location</a>.\r\n\
+ Please update your documents to reflect the new location.\r\n\
+ </body></html>\r\n"), url);
+
+ websResponse(wp, 302, msgbuf, url);
+
+ bfreeSafe(B_L, msgbuf);
+ bfreeSafe(B_L, urlbuf);
+}
+
+/******************************************************************************/
+/*
+ * Output an error message and cleanup
+ */
+
+void websError(webs_t wp, int code, char_t *fmt, ...)
+{
+ va_list args;
+ char_t *msg, *userMsg, *buf;
+
+ a_assert(websValid(wp));
+ a_assert(fmt);
+
+ websStats.errors++;
+
+ va_start(args, fmt);
+ userMsg = NULL;
+ gvsnprintf(&userMsg, WEBS_BUFSIZE, fmt, args);
+ va_end(args);
+
+ msg = T("<html><head><title>Document Error: %s</title></head>\r\n\
+ <body><h2>Access Error: %s</h2>\r\n\
+ when trying to obtain <b>%s</b><br><p>%s</p></body></html>\r\n");
+/*
+ * Ensure we have plenty of room
+ */
+ buf = NULL;
+ gsnprintf(&buf, WEBS_BUFSIZE, msg, websErrorMsg(code),
+ websErrorMsg(code), wp->url, userMsg);
+
+ websResponse(wp, code, buf, NULL);
+ bfreeSafe(B_L, buf);
+ bfreeSafe(B_L, userMsg);
+}
+
+/******************************************************************************/
+/*
+ * Return the error message for a given code
+ */
+
+static char_t *websErrorMsg(int code)
+{
+ websErrorType* ep;
+
+ for (ep = websErrors; ep->code; ep++) {
+ if (code == ep->code) {
+ return ep->msg;
+ }
+ }
+ a_assert(0);
+ return T("");
+}
+
+/******************************************************************************/
+/*
+ * Do formatted output to the browser. This is the public ASP and form
+ * write procedure.
+ */
+
+int websWrite(webs_t wp, char_t* fmt, ...)
+{
+ va_list vargs;
+ char_t *buf;
+ int rc;
+
+ a_assert(websValid(wp));
+
+ va_start(vargs, fmt);
+
+ buf = NULL;
+ rc = 0;
+ if (gvsnprintf(&buf, WEBS_BUFSIZE, fmt, vargs) >= WEBS_BUFSIZE) {
+ trace(0, T("webs: websWrite lost data, buffer overflow\n"));
+ }
+ va_end(vargs);
+ a_assert(buf);
+ if (buf) {
+ rc = websWriteBlock(wp, buf, gstrlen(buf));
+ bfree(B_L, buf);
+ }
+ return rc;
+}
+
+/******************************************************************************/
+/*
+ * Write a block of data of length "nChars" to the user's browser. Public
+ * write block procedure. If unicode is turned on this function expects
+ * buf to be a unicode string and it converts it to ASCII before writing.
+ * See websWriteBlockData to always write binary or ASCII data with no
+ * unicode conversion. This returns the number of char_t's processed.
+ */
+
+int websWriteBlock(webs_t wp, char_t *buf, int nChars)
+{
+#if ! UNICODE
+ return websWriteBlockData(wp, buf, nChars);
+#else
+ int r;
+ char *charBuf;
+
+ a_assert(buf);
+ a_assert(nChars >= 0);
+
+ if ((charBuf = ballocUniToAsc(buf, nChars)) == NULL) {
+ return -1;
+ }
+ r = websWriteBlockData(wp, charBuf, nChars);
+ bfree(B_L, charBuf);
+ return r;
+#endif
+}
+
+/******************************************************************************/
+/*
+ * Write a block of data of length "nChars" to the user's browser. Same as
+ * websWriteBlock except that it expects straight ASCII or binary and does no
+ * unicode conversion before writing the data.
+ * This returns the number of chars processed.
+ */
+
+int websWriteBlockData(webs_t wp, char *buf, int nChars)
+{
+ int len, done;
+
+ a_assert(wp);
+ a_assert(websValid(wp));
+ a_assert(buf);
+ a_assert(nChars >= 0);
+
+ done = len = 0;
+ while (nChars > 0) {
+ if ((len = socketWrite(wp->sid, buf, nChars)) < 0) {
+ return -1;
+ }
+/*
+ * Block in flush if the last write could not take any more data
+ */
+ socketFlush(wp->sid, len == 0);
+ nChars -= len;
+ buf += len;
+ done += len;
+ }
+ return done;
+}
+
+/******************************************************************************/
+/*
+ * Decode a URL (or part thereof). Allows insitu decoding.
+ */
+
+void websDecodeUrl(char_t *decoded, char_t *token, int len)
+{
+ char_t *ip, *op;
+ int num, i, c;
+
+ a_assert(decoded);
+ a_assert(token);
+ num = 0;
+
+ op = decoded;
+ for (ip = token; *ip && len > 0; ip++, op++) {
+ if (*ip == '+') {
+ *op = ' ';
+ } else if (*ip == '%' && gisxdigit(ip[1]) && gisxdigit(ip[2])) {
+
+/*
+ * Convert %nn to a single character
+ */
+ ip++;
+ for (i = 0; i < 2; i++, ip++) {
+ c = tolower(*ip);
+ if (c >= 'a' && c <= 'f') {
+ num = (num * 16) + 10 + c - 'a';
+ } else {
+ num = (num * 16) + c - '0';
+ }
+ }
+ *op = (char_t) num;
+ ip--;
+
+ } else {
+ *op = *ip;
+ }
+ len--;
+ }
+ *op = '\0';
+}
+
+/******************************************************************************/
+#if WEBS_LOG_SUPPORT
+/*
+ * Output a log message
+ */
+
+static void websLog(webs_t wp, int code)
+{
+ char_t *buf;
+ char *abuf;
+ int len;
+
+ a_assert(websValid(wp));
+
+ buf = NULL;
+ gsnprintf(&buf, WEBS_MAX_URL + 80, T("%d %s %d %d\n"), time(0),
+ wp->url, code, wp->written);
+ len = gstrlen(buf);
+ abuf = ballocUniToAsc(buf, len+1);
+ write(websLogFd, abuf, len);
+ bfreeSafe(B_L, buf);
+ bfreeSafe(B_L, abuf);
+}
+
+#endif /* WEBS_LOG_SUPPORT */
+
+/******************************************************************************/
+/*
+ * Request timeout. The timeout triggers if we have not read any data from
+ * the users browser in the last WEBS_TIMEOUT period. If we have heard from
+ * the browser, simply re-issue the timeout.
+ */
+
+static void websTimeout(long iwp)
+{
+ webs_t wp;
+ int delay, tm;
+
+ wp = (webs_t) iwp;
+ a_assert(websValid(wp));
+
+ tm = websGetTimeSinceMark(wp) * 1000;
+ if (tm >= WEBS_TIMEOUT) {
+ websStats.timeouts++;
+ wp->timeout = NULL;
+ websDone(wp, 404);
+
+ } else {
+ delay = WEBS_TIMEOUT - tm;
+ a_assert(delay > 0);
+ wp->timeout = emfCreateTimer(delay, websTimeout, (long) wp);
+ }
+}
+
+/******************************************************************************/
+/*
+ * Called when the request is done.
+ */
+
+void websDone(webs_t wp, int code)
+{
+ a_assert(websValid(wp));
+
+/*
+ * Disable socket handler in case keep alive set.
+ */
+ socketDeleteHandler(wp->sid);
+
+ if (code != 200) {
+ wp->flags &= ~WEBS_KEEP_ALIVE;
+ }
+
+#if WEBS_PROXY_SUPPORT
+ if (! (wp->flags & WEBS_LOCAL_PAGE)) {
+ websStats.activeNetRequests--;
+ }
+#endif
+
+#if WEBS_LOG_SUPPORT
+ if (! (wp->flags & WEBS_REQUEST_DONE)) {
+ websLog(wp, code);
+ }
+#endif
+
+/*
+ * Close any opened document by a handler
+ */
+ websPageClose(wp);
+
+/*
+ * If using Keep Alive (HTTP/1.1) we keep the socket open for a period
+ * while waiting for another request on the socket.
+ */
+ if (wp->flags & WEBS_KEEP_ALIVE) {
+ if (socketFlush(wp->sid, 0) == 0) {
+ wp->state = WEBS_BEGIN;
+ wp->flags |= WEBS_REQUEST_DONE;
+ if (wp->header.buf) {
+ ringqFlush(&wp->header);
+ }
+ socketCreateHandler(wp->sid, SOCKET_READABLE, websSocketEvent,
+ (int) wp);
+ websTimeoutCancel(wp);
+ wp->timeout = emfCreateTimer(WEBS_TIMEOUT, websTimeout, (long) wp);
+ return;
+ }
+ } else {
+ websTimeoutCancel(wp);
+ socketCloseConnection(wp->sid);
+ }
+ websFree(wp);
+}
+
+/******************************************************************************/
+/*
+ * Allocate a new webs structure
+ */
+
+static int websAlloc(int sid)
+{
+ webs_t wp;
+ int wid;
+
+/*
+ * Allocate a new handle for this connection
+ */
+ if ((wid = hAllocEntry((void***) &webs, &websMax,
+ sizeof(struct websRec))) < 0) {
+ return -1;
+ }
+ wp = webs[wid];
+
+ wp->wid = wid;
+ wp->sid = sid;
+ wp->state = WEBS_BEGIN;
+ wp->docfd = -1;
+ wp->dir = NULL;
+
+ ringqOpen(&wp->header, WEBS_HEADER_BUFINC, WEBS_MAX_HEADER);
+
+/*
+ * Create storage for the CGI variables. We supply the symbol tables for
+ * both the CGI variables and for the global functions. The function table
+ * is common to all webs instances (ie. all browsers)
+ */
+ wp->cgiVars = symOpen(64);
+
+ return wid;
+}
+
+/******************************************************************************/
+/*
+ * Free a webs structure
+ */
+
+static void websFree(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ if (wp->path)
+ bfree(B_L, wp->path);
+ if (wp->url)
+ bfree(B_L, wp->url);
+ if (wp->host)
+ bfree(B_L, wp->host);
+ if (wp->lpath)
+ bfree(B_L, wp->lpath);
+ if (wp->query)
+ bfree(B_L, wp->query);
+ if (wp->decodedQuery)
+ bfree(B_L, wp->decodedQuery);
+ if (wp->password)
+ bfree(B_L, wp->password);
+ if (wp->userName)
+ bfree(B_L, wp->userName);
+ if (wp->cookie)
+ bfree(B_L, wp->cookie);
+ if (wp->userAgent)
+ bfree(B_L, wp->userAgent);
+ if (wp->dir)
+ bfree(B_L, wp->dir);
+
+ symClose(wp->cgiVars, websFreeVar);
+
+ if (wp->header.buf) {
+ ringqClose(&wp->header);
+ }
+
+ websMax = hFree((void***) &webs, wp->wid);
+ bfree(B_L, wp);
+ a_assert(websMax >= 0);
+}
+
+/******************************************************************************/
+/*
+ * Callback from symClose. Free the variable.
+ */
+
+static void websFreeVar(sym_t* sp)
+{
+ valueFree(&sp->content);
+}
+
+/******************************************************************************/
+/*
+ * Return the server address
+ */
+
+char_t* websGetHost()
+{
+ return websHost;
+}
+
+/******************************************************************************/
+/*
+ * Return the server address
+ */
+
+char_t* websGetHostUrl()
+{
+ return websHostUrl;
+}
+
+/******************************************************************************/
+/*
+ * Return the listen port
+ */
+
+int websGetPort()
+{
+ return websPort;
+}
+
+/******************************************************************************/
+/*
+ * Get the number of bytes to write
+ */
+
+int websGetRequestBytes(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ return wp->numbytes;
+}
+
+/******************************************************************************/
+/*
+ * Get the directory for this request
+ */
+
+char_t *websGetRequestDir(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ if (wp->dir == NULL) {
+ return T("");
+ }
+
+ return wp->dir;
+}
+
+/******************************************************************************/
+/*
+ * Get the flags for this request
+ */
+
+int websGetRequestFlags(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ return wp->flags;
+}
+
+/******************************************************************************/
+/*
+ * Return the IP address
+ */
+
+char_t *websGetRequestIpaddr(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ return wp->ipaddr;
+}
+
+/******************************************************************************/
+/*
+ * Set the local path for the request
+ */
+
+char_t *websGetRequestLpath(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+#if WEBS_PAGE_ROM
+ return wp->path;
+#else
+ return wp->lpath;
+#endif
+}
+
+/******************************************************************************/
+/*
+ * Get the path for this request
+ */
+
+char_t *websGetRequestPath(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ if (wp->path == NULL) {
+ return T("");
+ }
+
+ return wp->path;
+}
+
+/******************************************************************************/
+/*
+ * Return the password
+ */
+
+char_t* websGetRequestPassword(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ return wp->password;
+}
+
+/******************************************************************************/
+/*
+ * Return the request type
+ */
+
+char_t* websGetRequestType(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ return wp->type;
+}
+
+/******************************************************************************/
+/*
+ * Return the username
+ */
+
+char_t* websGetRequestUserName(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ return wp->userName;
+}
+
+/******************************************************************************/
+/*
+ * Get the number of bytes written
+ */
+
+int websGetRequestWritten(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+ return wp->written;
+}
+
+/******************************************************************************/
+/*
+ * Set the hostname
+ */
+
+void websSetHost(char_t *host)
+{
+ gstrncpy(websHost, host, TSZ(websHost));
+}
+
+/******************************************************************************/
+/*
+ * Set the host URL
+ */
+
+void websSetHostUrl(char_t *url)
+{
+ a_assert(url && *url);
+
+ bfreeSafe(B_L, websHostUrl);
+ websHostUrl = gstrdup(B_L, url);
+}
+
+/******************************************************************************/
+/*
+ * Set the IP address
+ */
+
+void websSetIpaddr(char_t *ipaddr)
+{
+ a_assert(ipaddr && *ipaddr);
+
+ gstrncpy(websIpaddr, ipaddr, TSZ(websIpaddr));
+}
+
+/******************************************************************************/
+/*
+ * Set the number of bytes to write
+ */
+
+void websSetRequestBytes(webs_t wp, int bytes)
+{
+ a_assert(websValid(wp));
+ a_assert(bytes >= 0);
+
+ wp->numbytes = bytes;
+}
+
+/******************************************************************************/
+/*
+ * Set the flags for this request
+ */
+
+void websSetRequestFlags(webs_t wp, int flags)
+{
+ a_assert(websValid(wp));
+
+ wp->flags = flags;
+}
+
+/******************************************************************************/
+/*
+ * Set the local path for the request
+ */
+
+void websSetRequestLpath(webs_t wp, char_t *lpath)
+{
+ a_assert(websValid(wp));
+ a_assert(lpath && *lpath);
+
+ if (wp->lpath) {
+ bfree(B_L, wp->lpath);
+ }
+ wp->lpath = bstrdup(B_L, lpath);
+ websSetVar(wp, T("PATH_TRANSLATED"), wp->lpath);
+}
+
+/******************************************************************************/
+/*
+ * Update the URL path and the directory containing the web page
+ */
+
+void websSetRequestPath(webs_t wp, char_t *dir, char_t *path)
+{
+ char_t *tmp;
+
+ a_assert(websValid(wp));
+
+ if (dir) {
+ tmp = wp->dir;
+ wp->dir = bstrdup(B_L, dir);
+ if (tmp) {
+ bfree(B_L, tmp);
+ }
+ }
+ if (path) {
+ tmp = wp->path;
+ wp->path = bstrdup(B_L, path);
+ websSetVar(wp, T("PATH_INFO"), wp->path);
+ if (tmp) {
+ bfree(B_L, tmp);
+ }
+ }
+}
+
+/******************************************************************************/
+/*
+ * Set the Write handler for this socket
+ */
+
+void websSetRequestSocketHandler(webs_t wp, int mask, void (*fn)(webs_t wp))
+{
+ a_assert(websValid(wp));
+
+ wp->writeSocket = fn;
+ socketCreateHandler(wp->sid, SOCKET_WRITABLE, websSocketEvent, (int) wp);
+}
+
+/******************************************************************************/
+/*
+ * Set the number of bytes written
+ */
+
+void websSetRequestWritten(webs_t wp, int written)
+{
+ a_assert(websValid(wp));
+
+ wp->written = written;
+}
+
+/******************************************************************************/
+/*
+ * Reurn true if the webs handle is valid
+ */
+
+int websValid(webs_t wp)
+{
+ int wid;
+
+ for (wid = 0; wid < websMax; wid++) {
+ if (wp == webs[wid]) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Close the document handle.
+ */
+
+int websCloseFileHandle(webs_t wp)
+{
+ a_assert(websValid(wp));
+
+#ifndef WEBS_PAGE_ROM
+ if (wp->docfd >= 0) {
+ close(wp->docfd);
+ wp->docfd = -1;
+ }
+#endif
+
+ return 0;
+}
+
+/******************************************************************************/
+/*
+ * Build an ASCII time string. If sbuf is NULL we use the current time,
+ * else we use the last modified time of sbuf;
+ */
+
+char_t* websGetDateString(websStatType* sbuf)
+{
+ char_t* cp;
+ char_t* r;
+ time_t now;
+
+ if (sbuf == NULL) {
+ time(&now);
+ } else {
+ now = sbuf->mtime;
+ }
+ if ((cp = gctime(&now)) != NULL) {
+ cp[gstrlen(cp) - 1] = '\0';
+ r = bstrdup(B_L, cp);
+ return r;
+ }
+ return NULL;
+}
+
+/******************************************************************************/
+/*
+ * Mark time. Set a timestamp so that, later, we can return the number of
+ * seconds since we made the mark. Note that the mark my not be a
+ * "real" time, but rather a relative marker.
+ */
+
+static void websMarkTime(webs_t wp)
+{
+ wp->timestamp = time(0);
+}
+
+/******************************************************************************/
+/*
+ * Get the number of seconds since the last mark.
+ */
+
+static int websGetTimeSinceMark(webs_t wp)
+{
+ return time(0) - wp->timestamp;
+}
+
+/******************************************************************************/
diff --git a/cpukit/httpd/webs.h b/cpukit/httpd/webs.h
new file mode 100644
index 0000000000..46e11bc2fc
--- /dev/null
+++ b/cpukit/httpd/webs.h
@@ -0,0 +1,182 @@
+/*
+ * webs.h -- Go Ahead Web public header
+ *
+ * Copyright (c) Go Ahead Software Inc., 1992-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for information on usage and redistribution
+ */
+
+#ifndef _h_WEBS
+#define _h_WEBS 1
+
+/******************************** Description *********************************/
+
+/*
+ * Go Ahead Web Server header. This defines the Web public APIs.
+ * Include this header for files that contain ASP or Form procedures.
+ * Include wsIntrn.h when creating URL handlers.
+ */
+
+/********************************* Includes ***********************************/
+
+#include "ej.h"
+
+/********************************** Defines ***********************************/
+
+#define WEBS_HEADER_BUFINC 512 /* Header buffer size */
+#define WEBS_ASP_BUFINC 512 /* Asp expansion increment */
+#define WEBS_MAX_PASS 32 /* Size of password */
+#define WEBS_BUFSIZE 1000 /* websWrite max output string */
+#define WEBS_MAX_HEADER (5 * 1024) /* Sanity check header */
+#define WEBS_MAX_URL 4096 /* Maximum URL size for sanity */
+#define WEBS_SOCKET_BUFSIZ 256 /* Bytes read from socket */
+
+/*
+ * Request flags. Also returned by websGetRequestFlags().
+ */
+#define WEBS_LOCAL_PAGE 0x1 /* Request for local webs page */
+#define WEBS_KEEP_ALIVE 0x2 /* HTTP/1.1 keep alive */
+#define WEBS_DONT_USE_CACHE 0x4 /* Not implemented cache support */
+#define WEBS_COOKIE 0x8 /* Cookie supplied in request */
+#define WEBS_IF_MODIFIED 0x10 /* If-modified-since in request */
+#define WEBS_POST_REQUEST 0x20 /* Post request operation */
+#define WEBS_LOCAL_REQUEST 0x40 /* Request from this system */
+#define WEBS_HOME_PAGE 0x80 /* Request for the home page */
+#define WEBS_ASP 0x100 /* ASP request */
+#define WEBS_HEAD_REQUEST 0x200 /* Head request */
+#define WEBS_CLEN 0x400 /* Request had a content length */
+#define WEBS_FORM 0x800 /* Request is a form */
+#define WEBS_REQUEST_DONE 0x1000 /* Request complete */
+#define WEBS_POST_DATA 0x2000 /* Already appended post data */
+#define WEBS_HEADER_DONE 0x40000 /* Already output the HTTP header */
+
+/*
+ * URL handler flags
+ */
+#define WEBS_HANDLER_FIRST 0x1 /* Process this handler first */
+#define WEBS_HANDLER_LAST 0x2 /* Process this handler last */
+
+/*
+ * Per socket connection webs structure
+ */
+typedef struct websRec {
+ ringq_t header; /* Header dynamic string */
+ time_t since; /* Parsed if-modified-since time */
+ sym_fd_t cgiVars; /* CGI standard variables */
+ sym_fd_t cgiQuery; /* CGI decoded query string */
+ time_t timestamp; /* Last transaction with browser */
+ void* timeout; /* Timeout handle */
+ char_t ipaddr[32]; /* Connecting ipaddress */
+ char_t type[64]; /* Mime type */
+ char_t* dir; /* Directory containing the page */
+ char_t* path; /* Path name without query */
+ char_t* url; /* Full request url */
+ char_t* host; /* Requested host */
+ char_t* lpath; /* Cache local path name */
+ char_t* query; /* Request query */
+ char_t* decodedQuery; /* Decoded request query */
+ char_t* password; /* Authorization password */
+ char_t* userName; /* Authorization username */
+ char_t* cookie; /* Cookie string */
+ char_t* userAgent; /* User agent (browser) */
+ int sid; /* Socket id (handler) */
+ int port; /* Request port number */
+ int state; /* Current state */
+ int flags; /* Current flags -- see above */
+ int code; /* Request result code */
+ int clen; /* Content length */
+ int wid; /* Index into webs */
+ int docfd; /* Document file descriptor */
+ int numbytes; /* Bytes to transfer to browser */
+ int written; /* Bytes actually transferred */
+ void (*writeSocket)(struct websRec *wp);
+} websRec;
+
+typedef websRec *webs_t;
+typedef websRec websType;
+
+/******************************** Prototypes **********************************/
+
+extern int websAspDefine(char_t *name,
+ int (*fn)(int ejid, webs_t wp, int argc, char_t **argv));
+extern int websAspRequest(webs_t wp, char_t *lpath);
+extern void websCloseListen();
+extern int websDecode64(char_t *outbuf, char_t *string, int buflen);
+extern void websDecodeUrl(char_t *token, char_t *decoded, int len);
+extern void websDone(webs_t wp, int code);
+extern void websEncode64(char_t *outbuf, char_t *string, int buflen);
+extern void websError(webs_t wp, int code, char_t *msg, ...);
+extern void websFooter(webs_t wp);
+extern int websFormDefine(char_t *name, void (*fn)(webs_t wp,
+ char_t *path, char_t *query));
+extern char_t *websGetDefaultDir();
+extern char_t *websGetDefaultPage();
+extern char_t *websGetHostUrl();
+extern char_t *websGetPassword();
+extern int websGetPort();
+extern char_t *websGetPublishDir(char_t *path, char_t **urlPrefix);
+extern int websGetRequestBytes(webs_t wp);
+extern char_t *websGetRequestDir(webs_t wp);
+extern int websGetRequestFlags(webs_t wp);
+extern char_t *websGetRequestIpaddr(webs_t wp);
+extern char_t *websGetRequestLpath(webs_t wp);
+extern char_t *websGetRequestPath(webs_t wp);
+extern char_t *websGetRequestPassword(webs_t wp);
+extern char_t *websGetRequestType(webs_t wp);
+extern int websGetRequestWritten(webs_t wp);
+extern char_t *websGetVar(webs_t wp, char_t *var, char_t *def);
+extern void websHeader(webs_t wp);
+extern int websOpenListen(int port, int retries);
+extern int websPageOpen(webs_t wp, char_t *lpath, char_t *path,
+ int mode, int perm);
+extern void websPageClose(webs_t wp);
+extern int websPublish(char_t *urlPrefix, char_t *path);
+extern void websRedirect(webs_t wp, char_t *url);
+extern void websSecurityDelete();
+extern int websSecurityHandler(webs_t wp, char_t *urlPrefix,
+ char_t *webDir, int arg, char_t *url, char_t *path,
+ char_t *query);
+extern void websSetDefaultDir(char_t *dir);
+extern void websSetDefaultPage(char_t *page);
+extern void websSetEnv(webs_t wp);
+extern void websSetHost(char_t *host);
+extern void websSetIpaddr(char_t *ipaddr);
+extern void websSetPassword(char_t *password);
+extern void websSetRequestBytes(webs_t wp, int bytes);
+extern void websSetRequestFlags(webs_t wp, int flags);
+extern void websSetRequestLpath(webs_t wp, char_t *lpath);
+extern void websSetRequestPath(webs_t wp, char_t *dir, char_t *path);
+extern char_t *websGetRequestUserName(webs_t wp);
+extern void websSetRequestWritten(webs_t wp, int written);
+extern void websSetVar(webs_t wp, char_t *var, char_t *value);
+extern int websTestVar(webs_t wp, char_t *var);
+extern int websUrlHandlerDefine(char_t *urlPrefix, char_t *webDir,
+ int arg, int (*fn)(webs_t wp, char_t *urlPrefix,
+ char_t *webDir, int arg, char_t *url, char_t *path,
+ char_t *query), int flags);
+extern int websUrlHandlerDelete(int (*fn)(webs_t wp, char_t *urlPrefix,
+ char_t *webDir, int arg, char_t *url, char_t *path,
+ char_t *query));
+extern int websUrlHandlerRequest(webs_t wp);
+extern int websUrlParse(char_t *url, char_t **buf, char_t **host,
+ char_t **path, char_t **port, char_t **query,
+ char_t **proto, char_t **tag, char_t **ext);
+extern char_t *websUrlType(char_t *webs, char_t *buf, int charCnt);
+extern int websWrite(webs_t wp, char_t* fmt, ...);
+extern int websWriteBlock(webs_t wp, char_t *buf, int nChars);
+extern int websWriteBlockData(webs_t wp, char *buf, int nChars);
+extern int websValid(webs_t wp);
+extern int websValidateUrl(webs_t wp, char_t *path);
+extern int websCloseFileHandle(webs_t wp);
+
+/*
+ * Prototypes for functions available when running as part of the
+ * GoAhead Embedded Management Framework (EMF)
+ */
+#if EMF
+extern void websFormExplain(webs_t wp, char_t *path, char_t *query);
+#endif
+
+#endif /* _h_WEBS */
+
+/******************************************************************************/
diff --git a/cpukit/httpd/websuemf.c b/cpukit/httpd/websuemf.c
new file mode 100644
index 0000000000..48a30f25d0
--- /dev/null
+++ b/cpukit/httpd/websuemf.c
@@ -0,0 +1,39 @@
+/*
+ * websuemf.c -- GoAhead Micro Embedded Management Framework
+ *
+ * Copyright (c) Go Ahead Software Inc., 1995-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for usage and redistribution license requirements
+ */
+
+/********************************** Description *******************************/
+
+/*
+ * This modules provides compatibility with the full GoAhead EMF.
+ */
+
+/*********************************** Includes *********************************/
+
+#include "wsIntrn.h"
+
+/************************************* Code ***********************************/
+/*
+ * Evaluate a script
+ */
+
+int scriptEval(int engine, char_t* cmd, char_t** result, int chan)
+{
+ int ejid;
+
+ if (engine == EMF_SCRIPT_EJSCRIPT) {
+ ejid = (int) chan;
+ if (ejEval(ejid, cmd, NULL) ) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ return -1;
+}
+
+/******************************************************************************/
diff --git a/cpukit/httpd/wsIntrn.h b/cpukit/httpd/wsIntrn.h
new file mode 100644
index 0000000000..bd77ef8862
--- /dev/null
+++ b/cpukit/httpd/wsIntrn.h
@@ -0,0 +1,268 @@
+/*
+ * wsIntrn.h -- Internal Go Ahead Web server header
+ *
+ * Copyright (c) Go Ahead Software Inc., 1992-1999. All Rights Reserved.
+ *
+ * See the file "license.txt" for information on usage and redistribution
+ */
+
+#ifndef _h_WEBS_INTERNAL
+#define _h_WEBS_INTERNAL 1
+
+/******************************** Description *********************************/
+
+/*
+ * Internal Go Ahead Web Server header. This defines the Web private APIs
+ * Include this header when you want to create URL handlers.
+ */
+
+/*********************************** Defines **********************************/
+
+/*
+ * Define this to enable login of web accesses to a file
+ * #define WEBS_LOG_SUPPORT 1
+ *
+ * Define this to enable HTTP/1.1 keep alive support
+ * #define WEBS_KEEP_ALIVE_SUPPORT 1
+ *
+ * Define this to enable if-modified-since support
+ * #define WEBS_IF_MODIFIED_SUPPORT 1
+ *
+ * Define this to support proxy capability and track local vs remote request
+ * Note: this is not yet fully implemented.
+ * #define WEBS_PROXY_SUPPORT 1
+ *
+ * Define this to support reading pages from ROM
+ * Note: this is not yet fully implemented.
+ * #define WEBS_PAGE_ROM 1
+ *
+ * Define this to enable memory allocation and stack usage tracking
+ * #define B_STATS 1
+ */
+
+/********************************** Includes **********************************/
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#if WIN
+ #include <fcntl.h>
+ #include <sys/stat.h>
+ #include <io.h>
+#endif
+
+#if CE
+#if ! UEMF
+ #include <io.h>
+#endif
+#endif
+
+#if NW
+ #include <fcntl.h>
+ #include <sys/stat.h>
+#endif
+
+#if LYNX
+ #include <fcntl.h>
+ #include <sys/stat.h>
+ #include <signal.h>
+ #include <unistd.h>
+#endif
+
+#if UNIX
+ #include <fcntl.h>
+ #include <sys/stat.h>
+ #include <signal.h>
+ #include <unistd.h>
+#endif
+
+#if QNX4
+ #include <fcntl.h>
+ #include <sys/stat.h>
+ #include <signal.h>
+ #include <unistd.h>
+ #include <unix.h>
+#endif
+
+#if UW
+ #include <fcntl.h>
+ #include <sys/stat.h>
+#endif
+
+#if VXW486
+ #include <vxWorks.h>
+ #include <fcntl.h>
+ #include <sys/stat.h>
+#endif
+
+#if UEMF
+ #include "uemf.h"
+ #include "ej.h"
+#else
+ #include "emf/emfInternal.h"
+#endif
+
+#include "webs.h"
+
+/********************************** Defines ***********************************/
+/*
+ * Read handler flags and state
+ */
+#define WEBS_BEGIN 0x1 /* Beginning state */
+#define WEBS_HEADER 0x2 /* Ready to read first line */
+#define WEBS_POST 0x4 /* POST without content */
+#define WEBS_POST_CLEN 0x8 /* Ready to read content for POST */
+#define WEBS_PROCESSING 0x10 /* Processing request */
+#define WEBS_KEEP_TIMEOUT 15000 /* Keep-alive timeout (15 secs) */
+#define WEBS_TIMEOUT 60000 /* General request timeout (60) */
+
+#define PAGE_READ_BUFSIZE 512 /* bytes read from page files */
+#define MAX_PORT_LEN 10 /* max digits in port number */
+
+/*
+ * URL handler structure. Stores the leading URL path and the handler
+ * function to call when the URL path is seen.
+ */
+typedef struct {
+ int (*handler)(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg,
+ char_t *url, char_t *path,
+ char_t *query); /* Callback URL handler function */
+ char_t *webDir; /* Web directory if required */
+ char_t *urlPrefix; /* URL leading prefix */
+ int len; /* Length of urlPrefix for speed */
+ int arg; /* Argument to provide to handler */
+ int flags; /* Flags */
+} websUrlHandlerType;
+
+/*
+ * Webs statistics
+ */
+typedef struct {
+ long errors; /* General errors */
+ long redirects;
+ long net_requests;
+ long activeNetRequests;
+ long activeBrowserRequests;
+ long timeouts;
+ long access; /* Access violations */
+ long localHits;
+ long remoteHits;
+ long formHits;
+ long handlerHits;
+} websStatsType;
+
+extern websStatsType websStats; /* Web access stats */
+
+/*
+ * Error code list
+ */
+typedef struct {
+ int code; /* HTTP error code */
+ char_t *msg; /* HTTP error message */
+} websErrorType;
+
+/*
+ * Mime type list
+ */
+typedef struct {
+ char_t *type; /* Mime type */
+ char_t *ext; /* File extension */
+} websMimeType;
+
+/*
+ * File information structure.
+ */
+typedef struct {
+ unsigned long size; /* File length */
+ int isDir; /* Set if directory */
+ time_t mtime; /* Modified time */
+} websStatType;
+
+/*
+ * Compiled Rom Page Index
+ */
+typedef struct {
+ char_t *path; /* Web page URL path */
+ unsigned char *page; /* Web page data */
+ int size; /* Size of web page in bytes */
+ int pos; /* Current read position */
+} websRomPageIndexType;
+
+/*
+ * Defines for file open.
+ */
+#ifndef CE
+#define SOCKET_RDONLY O_RDONLY
+#define SOCKET_BINARY O_BINARY
+#else /* CE */
+#define SOCKET_RDONLY 0x1
+#define SOCKET_BINARY 0x2
+#endif /* CE */
+
+extern websRomPageIndexType websRomPageIndex[];
+extern websMimeType websMimeList[]; /* List of mime types */
+extern sym_fd_t websMime; /* Set of mime types */
+extern webs_t* webs; /* Session list head */
+extern int websMax; /* List size */
+extern char_t websHost[64]; /* Name of this host */
+extern char_t websIpaddr[64]; /* IP address of this host */
+extern char_t *websHostUrl; /* URL for this host */
+extern int websPort; /* Port number */
+
+/******************************** Prototypes **********************************/
+
+extern int websAspOpen();
+extern void websAspClose();
+extern void websFormOpen();
+extern void websFormClose();
+extern int websAspWrite(int ejid, webs_t wp, int argc, char_t **argv);
+extern void websDefaultClose();
+extern int websDefaultHandler(webs_t wp, char_t *urlPrefix,
+ char_t *webDir, int arg, char_t *url, char_t *path,
+ char_t *query);
+extern int websFormHandler(webs_t wp, char_t *urlPrefix, char_t *webDir,
+ int arg, char_t *url, char_t *path, char_t *query);
+extern int websOpen(int sid);
+extern void websResponse(webs_t wp, int code, char_t *msg,
+ char_t *redirect);
+extern int websJavaScriptEval(webs_t wp, char_t *script);
+extern int websPageReadData(webs_t wp, char *buf, int nBytes);
+extern int websPageOpen(webs_t wp, char_t *lpath, char_t *path, int mode, int perm);
+extern void websPageClose(webs_t wp);
+extern void websPageSeek(webs_t wp, long offset);
+extern int websPageStat(webs_t wp, char_t *lpath, char_t *path,
+ websStatType *sbuf);
+extern int websPageIsDirectory(char_t *lpath);
+extern int websRomOpen();
+extern void websRomClose();
+extern int websRomPageOpen(webs_t wp, char_t *path, int mode, int perm);
+extern void websRomPageClose(int fd);
+extern int websRomPageReadData(webs_t wp, char *buf, int len);
+extern int websRomPageStat(char_t *path, websStatType *sbuf);
+extern long websRomPageSeek(webs_t wp, long offset, int origin);
+extern void websSetRequestSocketHandler(webs_t wp, int mask,
+ void (*fn)(webs_t wp));
+extern int websSolutionHandler(webs_t wp, char_t *urlPrefix,
+ char_t *webDir, int arg, char_t *url, char_t *path,
+ char_t *query);
+extern void websUrlHandlerClose();
+extern int websUrlHandlerOpen();
+extern int websOpenServer(int port, int retries);
+extern void websCloseServer();
+extern char_t* websGetDateString(websStatType* sbuf);
+
+/*
+ * Prototypes for functions available when running as part of the
+ * GoAhead Embedded Management Framework (EMF)
+ */
+#if EMF
+extern int websEmfOpen();
+extern void websEmfClose();
+extern void websSetEmfEnvironment(webs_t wp);
+#endif
+
+#endif /* _h_WEBS_INTERNAL */
+
+/******************************************************************************/