Initial import
diff --git a/contiki/README b/contiki/README
new file mode 100644
index 0000000..1c3ebef
--- /dev/null
+++ b/contiki/README
@@ -0,0 +1,28 @@
+This is the source code for the Contiki desktop environment, which is
+a modern Internet-enabled desktop environment for the Commodore 64 and
+a bunch of other systems written by Adam Dunkels. The Contiki desktop
+environment includes a simple window system and GUI, TCP/IP
+networking, a web server, a telnet client, and a web browser. Contiki
+is written in C and is compiled with Ullrich von Bassewitz' cc65 C
+compiler.
+
+More information about the Contiki desktop environment can be found on
+its web page:
+
+   http://dunkels.com/adam/contiki/
+
+The cc65 C compiler can be found on the cc65 web page:
+
+   http://www.cc65.org/
+
+The Contiki desktop environment source code is divided into the
+following directories:
+
+  apps/ - the Contiki applications
+  ctk/  - the ctk conio windowing toolkit on which Contiki builds
+  ek/   - the ek event kernel and the dispatcher
+  lib/  - various library files
+  uip/  - the uIP TCP/IP stack
+
+To build the Contiki desktop environment from sources, GNU make and
+cc65 must be installed. 
diff --git a/contiki/apps/contiki.c b/contiki/apps/contiki.c
new file mode 100644
index 0000000..0644480
--- /dev/null
+++ b/contiki/apps/contiki.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment
+ *
+ * $Id: contiki.c,v 1.1 2003/03/19 14:13:32 adamdunkels Exp $
+ *
+ */
+
+#include "uip_main.h"
+#include "uip.h"
+#include "uip_arp.h"
+#include "ctk.h"
+#include "dispatcher.h"
+#include "resolv.h"
+#include "email.h"
+
+static struct ctk_menu menu;
+unsigned char menuitem_about, menuitem_processes, menuitem_tcpip;
+
+#define MAX_PROCESSLABELS 10
+static struct ctk_window processwindow;
+static unsigned char ids[MAX_PROCESSLABELS][3];
+static struct ctk_label processidlabels[MAX_PROCESSLABELS];
+static struct ctk_label processnamelabels[MAX_PROCESSLABELS];
+
+static struct ctk_button processupdatebutton =
+  {CTK_BUTTON(0, 9, 6, "Update")};
+static struct ctk_button processclosebutton =
+  {CTK_BUTTON(13, 9, 5, "Close")};
+
+static struct ctk_window aboutdialog;
+static struct ctk_label aboutlabel1 =
+  {CTK_LABEL(5, 0, 23, 1, "The Contiki Desktop OS")};
+static struct ctk_label aboutlabel2 =
+  {CTK_LABEL(3, 3, 28, 1, "A modern, Internet-enabled")};
+static struct ctk_label aboutlabel3 =
+  {CTK_LABEL(2, 4, 28, 1, "operating system and desktop")};
+static struct ctk_label aboutlabel4 =
+  {CTK_LABEL(0, 5, 32, 1, "environment for the Commodore 64")};
+static struct ctk_label aboutlabel5 =
+  {CTK_LABEL(4, 6, 26, 1, "written by Adam Dunkels")};
+
+static char abouturl_petscii[] = "http://dunkels.com/adam/contiki/";
+static char abouturl_ascii[40];
+static struct ctk_hyperlink abouturl = 
+  {CTK_HYPERLINK(0, 8, 32, "http://dunkels.com/adam/contiki/",
+		 abouturl_ascii)};
+static struct ctk_button aboutclose =
+  {CTK_BUTTON(12, 10, 5, "Close")};
+
+/* TCP/IP configuration window. */
+static struct ctk_window tcpipwindow;
+
+#ifdef WITH_ETHERNET
+static struct ctk_label ipaddrlabel =
+  {CTK_LABEL(0, 1, 10, 1, "IP address")};
+static char ipaddr[17];
+static struct ctk_textentry ipaddrtextentry =
+  {CTK_TEXTENTRY(11, 1, 16, 1, ipaddr, 16)};
+static struct ctk_label netmasklabel =
+  {CTK_LABEL(0, 3, 10, 1, "Netmask")};
+static char netmask[17];
+static struct ctk_textentry netmasktextentry =
+  {CTK_TEXTENTRY(11, 3, 16, 1, netmask, 16)};
+static struct ctk_label gatewaylabel =
+  {CTK_LABEL(0, 5, 10, 1, "Gateway")};
+static char gateway[17];
+static struct ctk_textentry gatewaytextentry =
+  {CTK_TEXTENTRY(11, 5, 16, 1, gateway, 16)};
+static struct ctk_label dnsserverlabel =
+  {CTK_LABEL(0, 7, 10, 1, "DNS server")};
+static char dnsserver[17];
+static struct ctk_textentry dnsservertextentry =
+  {CTK_TEXTENTRY(11, 7, 16, 1, dnsserver, 16)};
+#else /* WITH_ETHERNET */
+static struct ctk_label ipaddrlabel =
+  {CTK_LABEL(0, 2, 10, 1, "IP address")};
+static char ipaddr[17];
+static struct ctk_textentry ipaddrtextentry =
+  {CTK_TEXTENTRY(11, 2, 16, 1, ipaddr, 16)};
+static struct ctk_label dnsserverlabel =
+  {CTK_LABEL(0, 4, 10, 1, "DNS server")};
+static char dnsserver[17];
+static struct ctk_textentry dnsservertextentry =
+  {CTK_TEXTENTRY(11, 4, 16, 1, dnsserver, 16)};
+#endif /* WITH_ETHERNET */
+
+static struct ctk_button tcpipclosebutton =
+  {CTK_BUTTON(0, 9, 2, "Ok")};
+
+/* . == 0, + == 1 */
+static unsigned char abouticon_bitmap[3*3*8] = {
+  0x00, 0x7f, 0x43, 0x4c, 0x58, 0x53, 0x60, 0x6f,
+  0x00, 0xff, 0x00, 0x7e, 0x00, 0xff, 0x00, 0xff,
+  0x00, 0xfe, 0xc2, 0x32, 0x1a, 0xca, 0x06, 0xf6,
+
+  0x40, 0x5f, 0x40, 0x5f, 0x40, 0x5f, 0x40, 0x4f,
+  0x00, 0xff, 0x00, 0xff, 0x00, 0xfc, 0x01, 0xf3,
+  0x02, 0xfa, 0x02, 0x82, 0x3e, 0xfe, 0xfe, 0xfe,
+
+  0x60, 0x67, 0x50, 0x59, 0x4c, 0x43, 0x7f, 0x00,
+  0x07, 0xe7, 0x0f, 0xef, 0x0f, 0x0f, 0xff, 0x00,
+  0x8e, 0x06, 0x06, 0x06, 0x8e, 0xfe, 0xfe, 0x00
+};
+
+static char abouticon_textmap[9] = {
+  ' ', ' ', 'c',
+  ' ', '?', ' ',
+  '.', ' ', ' '
+};
+
+static struct ctk_icon abouticon =
+  {CTK_ICON("About Contiki", abouticon_bitmap, abouticon_textmap)};
+
+/* . == 0, + == 1 */
+static unsigned char tcpipconficon_bitmap[3*3*8] = {
+  0x00, 0x79, 0x43, 0x73, 0x47, 0x77, 0x47, 0x6f,
+  0x00, 0xfe, 0xfe, 0xfc, 0xfc, 0xfc, 0xf8, 0xfb,
+  0x00, 0x16, 0x02, 0x00, 0x02, 0x00, 0x00, 0xc2,
+
+  0x48, 0x4c, 0x5f, 0x5f, 0x1f, 0x3f, 0x3f, 0x03,
+  0x79, 0xf0, 0xf0, 0xf0, 0xe0, 0xe0, 0xfe, 0xfc,
+  0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+  0x77, 0x47, 0x70, 0x43, 0x79, 0x41, 0x7c, 0x00,
+  0xfc, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xf7, 0x00,
+  0x00, 0x80, 0x00, 0x00, 0x00, 0x84, 0xf0, 0x00
+};
+
+static char tcpipconficon_textmap[9] = {
+  't', 'c', 'p',
+  '/', 'i', 'p',
+  'c', 'f', 'g'
+};
+
+
+static struct ctk_icon tcpipconficon =
+  {CTK_ICON("TCP/IP conf", tcpipconficon_bitmap, tcpipconficon_textmap)};
+
+static void sighandler(ek_signal_t s, ek_data_t data);
+static struct dispatcher_proc p =
+  {DISPATCHER_PROC("Contiki", NULL, sighandler, NULL)};
+static ek_id_t id;
+
+/*-----------------------------------------------------------------------------------*/
+static void
+update_processwindow(void)
+{
+  unsigned char i, *idsptr;
+  struct dispatcher_proc *p;
+  
+  for(i = 0; i < MAX_PROCESSLABELS; ++i) {
+    p = dispatcher_process(i);
+    if(p != NULL) {
+      idsptr = ids[i];
+      idsptr[0] = '0' + i / 10;
+      idsptr[1] = '0' + i % 10;
+      idsptr[2] = 0;
+      CTK_LABEL_NEW(&processidlabels[i],
+		    0, i + 1, 2, 1, idsptr);
+      CTK_WIDGET_ADD(&processwindow, &processidlabels[i]);
+      
+      CTK_LABEL_NEW(&processnamelabels[i],
+		    3, i + 1, 17, 1, p->name);
+      CTK_WIDGET_ADD(&processwindow, &processnamelabels[i]);
+    }
+  }
+
+  CTK_WIDGET_ADD(&processwindow, &processupdatebutton);
+  CTK_WIDGET_ADD(&processwindow, &processclosebutton);
+  CTK_WIDGET_FOCUS(&processwindow, &processupdatebutton);
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+make_processwindow(void)
+{  
+  ctk_window_new(&processwindow, 20, 10, "Processes");
+  ctk_window_move(&processwindow, 0, 1);
+  update_processwindow();
+}
+/*-----------------------------------------------------------------------------------*/
+void
+contiki_init(void)     
+{
+  id = dispatcher_start(&p);
+  
+  ctk_dialog_new(&aboutdialog, 32, 11);
+  CTK_WIDGET_ADD(&aboutdialog, &aboutlabel1);
+  CTK_WIDGET_ADD(&aboutdialog, &aboutlabel2);
+  CTK_WIDGET_ADD(&aboutdialog, &aboutlabel3);
+  CTK_WIDGET_ADD(&aboutdialog, &aboutlabel4);
+  CTK_WIDGET_ADD(&aboutdialog, &aboutlabel5);
+  CTK_WIDGET_ADD(&aboutdialog, &abouturl);
+  CTK_WIDGET_ADD(&aboutdialog, &aboutclose);
+  CTK_WIDGET_FOCUS(&aboutdialog, &aboutclose);
+  
+  ctk_menu_new(&menu, "Contiki");
+  menuitem_about = ctk_menuitem_add(&menu, "About");
+  menuitem_processes = ctk_menuitem_add(&menu, "Processes");
+
+  menuitem_tcpip = ctk_menuitem_add(&menu, "TCP/IP conf");
+#if 0
+#ifdef WITH_ETHERNET
+  menuitem_ethernet = ctk_menuitem_add(&menu, "Ethernet");
+#endif /* WITH_ETHERNET */
+#ifdef WITH_RS232
+  menuitem_serial = ctk_menuitem_add(&menu, "Serial link");
+#endif /* WITH_RS232 */
+#endif /* 0 */
+
+  strcpy(abouturl_ascii, abouturl_petscii);
+  petsciiconv_toascii(abouturl_ascii, sizeof(abouturl_ascii));
+  
+  /* Create TCP/IP configuration window. */
+  ctk_window_new(&tcpipwindow, 30, 10, "TCP/IP config");
+  ctk_window_move(&tcpipwindow, 4, 6);
+
+
+#ifdef WITH_ETHERNET
+  CTK_WIDGET_ADD(&tcpipwindow, &ipaddrlabel);  
+  CTK_WIDGET_ADD(&tcpipwindow, &ipaddrtextentry);
+  CTK_WIDGET_ADD(&tcpipwindow, &netmasklabel);
+  CTK_WIDGET_ADD(&tcpipwindow, &netmasktextentry);
+  CTK_WIDGET_ADD(&tcpipwindow, &gatewaylabel);
+  CTK_WIDGET_ADD(&tcpipwindow, &gatewaytextentry);
+  CTK_WIDGET_ADD(&tcpipwindow, &dnsserverlabel);
+  CTK_WIDGET_ADD(&tcpipwindow, &dnsservertextentry);
+#else
+  CTK_WIDGET_ADD(&tcpipwindow, &ipaddrlabel);  
+  CTK_WIDGET_ADD(&tcpipwindow, &ipaddrtextentry);
+  CTK_WIDGET_ADD(&tcpipwindow, &dnsserverlabel);
+  CTK_WIDGET_ADD(&tcpipwindow, &dnsservertextentry);  
+#endif /* WITH_ETHERNET */
+
+  CTK_WIDGET_ADD(&tcpipwindow, &tcpipclosebutton);
+
+  CTK_WIDGET_FOCUS(&tcpipwindow, &ipaddrtextentry);  
+
+  ctk_menu_add(&menu);
+
+  CTK_ICON_ADD(&abouticon, id);
+  CTK_ICON_ADD(&tcpipconficon, id);
+
+  dispatcher_listen(ctk_signal_button_activate);
+  dispatcher_listen(ctk_signal_menu_activate);
+  dispatcher_listen(ctk_signal_hyperlink_activate);
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+nullterminate(char *cptr)
+{
+  /* Find the first space character in the ipaddr and put a zero there
+     to end the string. */
+  for(; *cptr != ' ' && *cptr != 0; ++cptr);
+  *cptr = 0;
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+apply_tcpipconfig(void)
+{
+  u16_t addr[2];
+
+#ifdef WITH_UIP
+  nullterminate(ipaddr);
+  if(uip_main_ipaddrconv(ipaddr, (unsigned char *)addr)) {
+    uip_sethostaddr(addr);
+  }
+  
+#ifdef WITH_ETHERNET
+  nullterminate(netmask);
+  if(uip_main_ipaddrconv(netmask, (unsigned char *)addr)) {
+    uip_setnetmask(addr);
+  }
+
+  nullterminate(gateway);
+  if(uip_main_ipaddrconv(gateway, (unsigned char *)addr)) {
+    uip_setdraddr(addr);
+  }
+#endif /* WITH_ETHERNET */
+  
+  nullterminate(dnsserver);
+  if(uip_main_ipaddrconv(dnsserver, (unsigned char *)addr)) {
+    resolv_conf(addr);
+  }
+#endif /* WITH_UIP */
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+sighandler(ek_signal_t s, ek_data_t data)
+{
+  struct ctk_widget *w;
+
+  if(s == ctk_signal_button_activate) {
+    w = (struct ctk_widget *)data;
+    if(w == (struct ctk_widget *)&aboutclose) {
+      ctk_dialog_close();
+      ctk_redraw();
+    } else if(w == (struct ctk_widget *)&abouticon) {
+      ctk_dialog_open(&aboutdialog);
+      ctk_redraw();
+    } else if(w == (struct ctk_widget *)&tcpipclosebutton) {
+      apply_tcpipconfig();
+      ctk_window_close(&tcpipwindow);
+      ctk_redraw();
+    } else if(w == (struct ctk_widget *)&tcpipconficon) {
+      ctk_window_open(&tcpipwindow);
+      ctk_redraw();
+    } else if(w == (struct ctk_widget *)&processupdatebutton) {
+      ctk_window_clear(&processwindow);
+      update_processwindow();
+      ctk_window_redraw(&processwindow);
+    } else if(w == (struct ctk_widget *)&processclosebutton) {
+      ctk_window_close(&processwindow);
+      ctk_redraw();
+    }
+
+  } else if(s == ctk_signal_menu_activate) {
+    if((struct ctk_menu *)data == &menu) {
+      if(menu.active == menuitem_about) {
+	ctk_dialog_open(&aboutdialog);
+      } else if(menu.active == menuitem_processes) {
+	make_processwindow();
+	ctk_window_open(&processwindow);
+      } else if(menu.active == menuitem_tcpip) {
+	ctk_window_open(&tcpipwindow);
+      }
+      ctk_redraw();
+    }
+  } else if(s == ctk_signal_hyperlink_activate) {
+    if((struct ctk_widget *)data == (struct ctk_widget *)&abouturl) {
+      ctk_dialog_close();
+      ctk_redraw();
+    }
+  }
+}
+/*-----------------------------------------------------------------------------------*/
diff --git a/contiki/apps/contiki.h b/contiki/apps/contiki.h
new file mode 100644
index 0000000..e9a50c0
--- /dev/null
+++ b/contiki/apps/contiki.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment for the C64.
+ *
+ * $Id: contiki.h,v 1.1 2003/03/19 14:13:32 adamdunkels Exp $
+ *
+ */
+#ifndef __CONTIKI_H__
+#define __CONTIKI_H__
+
+void contiki_init(void);
+
+#endif /* __CONTIKI_H__ */
diff --git a/contiki/apps/email.c b/contiki/apps/email.c
new file mode 100644
index 0000000..4a1ac38
--- /dev/null
+++ b/contiki/apps/email.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment for the C64.
+ *
+ * $Id: email.c,v 1.1 2003/03/19 14:13:32 adamdunkels Exp $
+ *
+ */
+
+
+#include "ctk.h"
+#include "dispatcher.h"
+#include "smtp.h"
+#include "uip_main.h"
+#include "petsciiconv.h"
+
+#define MAXNUMMSGS 6
+
+static struct ctk_menu menu;
+unsigned char menuitem_open, menuitem_setup;
+
+/* The main window. */
+static struct ctk_window mainwindow;
+
+static struct ctk_button newmailbutton =
+ {CTK_BUTTON(26, 0, 8, "New mail")};
+static struct ctk_button checkbutton =
+ {CTK_BUTTON(0, 0, 10, "Check mail")};
+
+static struct ctk_button msgbuttons[MAXNUMMSGS];
+static struct ctk_label msglabels[MAXNUMMSGS];
+static char msgtitles[20][MAXNUMMSGS];
+static struct ctk_separator sep1 =
+ {CTK_SEPARATOR(0, 7, 36)};
+static struct ctk_separator sep2 =
+ {CTK_SEPARATOR(0, 20, 36)};
+static struct ctk_label statuslabel =
+ {CTK_LABEL(6, 21, 23, 1, "")};
+
+
+static struct ctk_label tolabel =
+  {CTK_LABEL(0, 8, 3, 1, "To:")};
+static char to[40];
+static struct ctk_textentry totextentry =
+  {CTK_TEXTENTRY(8, 8, 26, 1, to, 38)};
+
+static struct ctk_label cclabel =
+  {CTK_LABEL(0, 9, 3, 1, "Cc:")};
+static char cc[40];
+static struct ctk_textentry cctextentry =
+  {CTK_TEXTENTRY(8, 9, 26, 1, cc, 38)};
+
+static struct ctk_label subjectlabel =
+  {CTK_LABEL(0, 10, 8, 1, "Subject:")};
+static char subject[40];
+static struct ctk_textentry subjecttextentry =
+  {CTK_TEXTENTRY(8, 10, 26, 1, subject, 38)};
+
+static char mail[36*9];
+static struct ctk_textentry mailtextentry =
+  {CTK_TEXTENTRY(0, 11, 34, 9, mail, 36)};
+
+static struct ctk_button sendbutton =
+  {CTK_BUTTON(0, 21, 4, "Send")};
+static struct ctk_button erasebutton =
+  {CTK_BUTTON(29, 21, 5, "Erase")};
+
+/* The "Really cancel message?" dialog. */
+static struct ctk_window canceldialog;
+static struct ctk_label canceldialoglabel1 =
+  {CTK_LABEL(2, 1, 22, 1, "Really cancel message?")};
+static struct ctk_label canceldialoglabel2 =
+  {CTK_LABEL(0, 2, 26, 1, "All contents will be lost.")};
+static struct ctk_button cancelyesbutton =
+  {CTK_BUTTON(4, 4, 3, "Yes")};
+static struct ctk_button cancelnobutton =
+  {CTK_BUTTON(18, 8, 2, "No")};
+
+/* The setup window. */
+static struct ctk_window setupwindow;
+static struct ctk_label fromaddresslabel =
+  {CTK_LABEL(0, 0, 25, 1, "Name and e-mail address")};
+static char fromaddress[40];
+static struct ctk_textentry fromaddresstextentry =
+  {CTK_TEXTENTRY(0, 1, 25, 1, fromaddress, 39)};
+
+static struct ctk_label smtpserverlabel =
+  {CTK_LABEL(0, 3, 20, 1, "Outgoing mailserver")};
+static char smtpserver[40];
+static struct ctk_textentry smtpservertextentry =
+  {CTK_TEXTENTRY(0, 4, 25, 1, smtpserver, 39)};
+
+static struct ctk_label pop3serverlabel =
+  {CTK_LABEL(0, 6, 20, 1, "Incoming mailserver")};
+static char pop3server[40];
+static struct ctk_textentry pop3servertextentry =
+  {CTK_TEXTENTRY(0, 7, 25, 1, pop3server, 39)};
+
+static struct ctk_label pop3userlabel =
+  {CTK_LABEL(0, 9, 20, 1, "Mailserver username")};
+static char pop3user[40];
+static struct ctk_textentry pop3usertextentry =
+  {CTK_TEXTENTRY(0, 10, 25, 1, pop3user, 39)};
+  
+static struct ctk_label pop3passwordlabel =
+  {CTK_LABEL(0, 12, 20, 1, "Mailserver password")};
+static char pop3password[40];
+static struct ctk_textentry pop3passwordtextentry =
+  {CTK_TEXTENTRY(0, 13, 25, 1, pop3password, 39)};
+
+
+static struct ctk_button setupokbutton =
+  {CTK_BUTTON(24, 15, 2, "Ok")};
+
+static void sighandler(ek_signal_t s, ek_data_t data);
+static struct dispatcher_proc p =
+  {DISPATCHER_PROC("E-mail client", NULL, sighandler, smtp_appcall)};
+static ek_id_t id;
+
+/*-----------------------------------------------------------------------------------*/
+static void
+make_window(void)
+{
+  unsigned char i;
+  struct ctk_button *button;
+  struct ctk_label *label;
+  
+  /* Create the main window. */
+  ctk_window_new(&mainwindow, 36, 22, "E-mail");
+  ctk_window_move(&mainwindow, 1, 0);
+  
+  CTK_WIDGET_ADD(&mainwindow, &checkbutton);
+  CTK_WIDGET_FOCUS(&mainwindow, &checkbutton);
+  CTK_WIDGET_ADD(&mainwindow, &newmailbutton);
+  CTK_WIDGET_ADD(&mainwindow, &sep1);
+  CTK_WIDGET_ADD(&mainwindow, &sep2);
+  CTK_WIDGET_ADD(&mainwindow, &statuslabel);
+  
+  for(i = 0; i < MAXNUMMSGS; ++i) {
+    button = &msgbuttons[i];
+    CTK_BUTTON_NEW(button, 0, i + 1, 1, " ");
+    CTK_WIDGET_ADD(&mainwindow, button);
+    label = &msglabels[i];
+    CTK_LABEL_NEW(label, 3, i + 1, 33, 1, msgtitles[i]);
+    CTK_WIDGET_ADD(&mainwindow, label);
+  }
+
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+make_composer(void)
+{
+  CTK_WIDGET_ADD(&mainwindow, &tolabel);
+  CTK_WIDGET_ADD(&mainwindow, &cclabel);
+  CTK_WIDGET_ADD(&mainwindow, &subjectlabel);
+  
+  CTK_WIDGET_ADD(&mainwindow, &totextentry);
+  CTK_WIDGET_FOCUS(&mainwindow, &totextentry);  
+  CTK_WIDGET_ADD(&mainwindow, &cctextentry);  
+  CTK_WIDGET_ADD(&mainwindow, &subjecttextentry);
+
+  CTK_WIDGET_ADD(&mainwindow, &mailtextentry);
+  
+  CTK_WIDGET_ADD(&mainwindow, &sendbutton);
+  CTK_WIDGET_ADD(&mainwindow, &erasebutton);
+
+  memset(mail, ' ', sizeof(mail));  
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+make_read(void)
+{
+  CTK_WIDGET_ADD(&mainwindow, &tolabel);
+  CTK_WIDGET_ADD(&mainwindow, &cclabel);
+  CTK_WIDGET_ADD(&mainwindow, &subjectlabel);
+  
+  CTK_WIDGET_ADD(&mainwindow, &totextentry);
+  /*  CTK_WIDGET_FOCUS(&mainwindow, &totextentry);  */
+  CTK_WIDGET_ADD(&mainwindow, &cctextentry);  
+  CTK_WIDGET_ADD(&mainwindow, &subjecttextentry);
+
+  CTK_WIDGET_ADD(&mainwindow, &mailtextentry);
+  
+  CTK_WIDGET_ADD(&mainwindow, &sendbutton);
+  CTK_WIDGET_ADD(&mainwindow, &erasebutton);
+}
+/*-----------------------------------------------------------------------------------*/
+void
+email_init(void)     
+{
+  if(id == EK_ID_NONE) {
+    id = dispatcher_start(&p);
+    
+    /* Create the "Really cancel message?" dialog. */
+    ctk_dialog_new(&canceldialog, 26, 6);
+    CTK_WIDGET_ADD(&canceldialog, &canceldialoglabel1);
+    CTK_WIDGET_ADD(&canceldialog, &canceldialoglabel2);
+    CTK_WIDGET_ADD(&canceldialog, &cancelyesbutton);
+    CTK_WIDGET_ADD(&canceldialog, &cancelnobutton);
+    CTK_WIDGET_FOCUS(&canceldialog, &cancelnobutton);
+    
+    /* Create setup window. */
+    ctk_window_new(&setupwindow, 28, 16, "E-mail setup");
+    ctk_window_move(&setupwindow, 5, 3);
+    
+    CTK_WIDGET_ADD(&setupwindow, &fromaddresslabel);
+    CTK_WIDGET_ADD(&setupwindow, &fromaddresstextentry);
+    CTK_WIDGET_ADD(&setupwindow, &smtpserverlabel);
+    CTK_WIDGET_ADD(&setupwindow, &smtpservertextentry);
+    CTK_WIDGET_ADD(&setupwindow, &pop3serverlabel);
+    CTK_WIDGET_ADD(&setupwindow, &pop3servertextentry);
+    CTK_WIDGET_ADD(&setupwindow, &pop3userlabel);
+    CTK_WIDGET_ADD(&setupwindow, &pop3usertextentry);
+    CTK_WIDGET_ADD(&setupwindow, &pop3passwordlabel);
+    CTK_WIDGET_ADD(&setupwindow, &pop3passwordtextentry);
+    CTK_WIDGET_ADD(&setupwindow, &setupokbutton);
+
+    CTK_WIDGET_FOCUS(&setupwindow, &fromaddresstextentry);
+    
+
+    /* Create main window. */
+    make_window();
+    make_composer();  
+
+    /* Create and add the menu */
+    ctk_menu_new(&menu, "E-mail");
+    menuitem_setup = ctk_menuitem_add(&menu, "Setup");
+    menuitem_open = ctk_menuitem_add(&menu, "Open");
+    ctk_menu_add(&menu);
+
+    /* Attach listeners to signals. */
+    dispatcher_listen(ctk_signal_button_activate);
+    dispatcher_listen(ctk_signal_menu_activate);
+
+    /* Open setup window */
+    ctk_window_open(&setupwindow);
+  } else {
+    ctk_window_open(&mainwindow);
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+applyconfig(void)
+{
+  u16_t addr[2];
+  char *cptr;
+
+  for(cptr = smtpserver; *cptr != ' ' && *cptr != 0; ++cptr);
+  *cptr = 0;
+  
+  if(uip_main_ipaddrconv(smtpserver, (unsigned char *)addr)) {
+    smtp_configure("contiki", addr);
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+prepare_message(void)
+{
+  char *mptr1, *mptr2;
+
+  /* Convert fields to ASCII. */
+  petsciiconv_toascii(to, sizeof(to));
+  petsciiconv_toascii(subject, sizeof(subject));  
+  petsciiconv_toascii(mail, 255);
+  petsciiconv_toascii(mail + 255, sizeof(mail) - 255);
+
+  /* Insert line delimiters. */
+
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+sighandler(ek_signal_t s, ek_data_t data)
+{
+  struct ctk_widget *w;
+  unsigned char i;
+  
+  if(s == ctk_signal_button_activate) {
+    w = (struct ctk_widget *)data;
+    if(w == (struct ctk_widget *)&newmailbutton) {
+      /*      ctk_window_open(&composerwindow);*/
+      ctk_window_close(&mainwindow);
+      make_window();
+      make_composer();
+      ctk_window_open(&mainwindow);
+      ctk_window_redraw(&mainwindow);
+#if 0
+    } else if(w == &replybutton) {
+      /* XXX Fiddle in the from and subject fields into the new
+	 mail. */
+      ctk_window_open(&composerwindow);
+      ctk_redraw();
+#endif 
+    } else if(w == (struct ctk_widget *)&checkbutton) {
+      /* XXX Should actually check email. */
+      ctk_label_set_text(&statuslabel, "Checking mail...");
+      ctk_window_redraw(&mainwindow);
+    } else if(w == (struct ctk_widget *)&sendbutton) {
+      prepare_message();
+      smtp_send(to, fromaddress, subject, mail, sizeof(mail));
+      ctk_label_set_text(&statuslabel, "Sending message...");
+      ctk_redraw();
+    } else if(w == (struct ctk_widget *)&erasebutton) {
+      ctk_dialog_open(&canceldialog);      
+      ctk_window_redraw(&mainwindow);
+    } else if(w == (struct ctk_widget *)&cancelyesbutton) {
+      ctk_dialog_close();
+      ctk_redraw();     
+    } else if(w == (struct ctk_widget *)&cancelnobutton) {
+      ctk_dialog_close();
+      ctk_redraw();
+    } else if(w == (struct ctk_widget *)&setupokbutton) {
+      applyconfig();
+      ctk_window_close(&setupwindow);
+      ctk_window_open(&mainwindow);
+      ctk_redraw();
+    } else {
+      for(i = 0; i < MAXNUMMSGS; ++i) {
+	if(w == (struct ctk_widget *)&msgbuttons[i]) {
+	  ctk_window_close(&mainwindow);
+	  make_window();
+	  /*	  make_read(); download(i); */
+	  ctk_window_open(&mainwindow);
+	  ctk_window_redraw(&mainwindow);
+	  break;
+	}
+      }
+    }
+  } else if(s == ctk_signal_menu_activate) {
+    if((struct ctk_menu *)data == &menu) {
+      if(menu.active == menuitem_open) {
+	ctk_window_open(&mainwindow);
+      } else if(menu.active == menuitem_setup) {
+	ctk_window_open(&setupwindow);
+      }
+      ctk_redraw();
+    }
+
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+void
+smtp_done(unsigned char error)
+{
+  ctk_label_set_text(&statuslabel, "SMTP done");
+  ctk_redraw();  
+}
+/*-----------------------------------------------------------------------------------*/
diff --git a/contiki/apps/email.h b/contiki/apps/email.h
new file mode 100644
index 0000000..09a1ca4
--- /dev/null
+++ b/contiki/apps/email.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment for the C64.
+ *
+ * $Id: email.h,v 1.1 2003/03/19 14:13:33 adamdunkels Exp $
+ *
+ */
+#ifndef __EMAIL_H__
+#define __EMAIL_H__
+
+void email_init(void);
+
+#endif /* __EMAIL_H__ */
diff --git a/contiki/apps/html-strings b/contiki/apps/html-strings
new file mode 100644
index 0000000..e3c43c9
--- /dev/null
+++ b/contiki/apps/html-strings
@@ -0,0 +1,36 @@
+html_slasha "/a\0"
+html_slashcenter "/center\0"
+html_slashform "/form\0"
+html_slashh "/h\0"
+html_slashscript "/script\0"
+html_slashselect "/select\0"
+html_slashstyle "/style\0"
+html_a "a\0"
+html_body "body\0"
+html_br "br\0"
+html_center "center\0"
+html_form "form\0"
+html_frame "frame\0"
+html_h1 "h1\0"
+html_h2 "h2\0"
+html_h3 "h3\0"
+html_h4 "h4\0"
+html_img "img\0"
+html_input "input\0"
+html_li "li\0"
+html_p "p\0"
+html_script "script\0"
+html_select "select\0"
+html_style "style\0"
+html_tr "tr\0"
+html_href "href\0"
+html_alt "alt\0"
+html_src "src\0"
+html_type "type\0"
+html_submit "submit\0"
+html_value "value\0"
+html_action "action\0"
+html_name "name\0"
+html_text "text\0"
+html_size "size\0"
+html_image "image\0"
\ No newline at end of file
diff --git a/contiki/apps/html-strings.c b/contiki/apps/html-strings.c
new file mode 100644
index 0000000..968f857
--- /dev/null
+++ b/contiki/apps/html-strings.c
@@ -0,0 +1,108 @@
+char html_slasha[4] = 
+/* "/a\0" */
+{0x2f, 0x61, 00, };
+char html_slashcenter[9] = 
+/* "/center\0" */
+{0x2f, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 00, };
+char html_slashform[7] = 
+/* "/form\0" */
+{0x2f, 0x66, 0x6f, 0x72, 0x6d, 00, };
+char html_slashh[4] = 
+/* "/h\0" */
+{0x2f, 0x68, 00, };
+char html_slashscript[9] = 
+/* "/script\0" */
+{0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 00, };
+char html_slashselect[9] = 
+/* "/select\0" */
+{0x2f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 00, };
+char html_slashstyle[8] = 
+/* "/style\0" */
+{0x2f, 0x73, 0x74, 0x79, 0x6c, 0x65, 00, };
+char html_a[3] = 
+/* "a\0" */
+{0x61, 00, };
+char html_body[6] = 
+/* "body\0" */
+{0x62, 0x6f, 0x64, 0x79, 00, };
+char html_br[4] = 
+/* "br\0" */
+{0x62, 0x72, 00, };
+char html_center[8] = 
+/* "center\0" */
+{0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 00, };
+char html_form[6] = 
+/* "form\0" */
+{0x66, 0x6f, 0x72, 0x6d, 00, };
+char html_frame[7] = 
+/* "frame\0" */
+{0x66, 0x72, 0x61, 0x6d, 0x65, 00, };
+char html_h1[4] = 
+/* "h1\0" */
+{0x68, 0x31, 00, };
+char html_h2[4] = 
+/* "h2\0" */
+{0x68, 0x32, 00, };
+char html_h3[4] = 
+/* "h3\0" */
+{0x68, 0x33, 00, };
+char html_h4[4] = 
+/* "h4\0" */
+{0x68, 0x34, 00, };
+char html_img[5] = 
+/* "img\0" */
+{0x69, 0x6d, 0x67, 00, };
+char html_input[7] = 
+/* "input\0" */
+{0x69, 0x6e, 0x70, 0x75, 0x74, 00, };
+char html_li[4] = 
+/* "li\0" */
+{0x6c, 0x69, 00, };
+char html_p[3] = 
+/* "p\0" */
+{0x70, 00, };
+char html_script[8] = 
+/* "script\0" */
+{0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 00, };
+char html_select[8] = 
+/* "select\0" */
+{0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 00, };
+char html_style[7] = 
+/* "style\0" */
+{0x73, 0x74, 0x79, 0x6c, 0x65, 00, };
+char html_tr[4] = 
+/* "tr\0" */
+{0x74, 0x72, 00, };
+char html_href[6] = 
+/* "href\0" */
+{0x68, 0x72, 0x65, 0x66, 00, };
+char html_alt[5] = 
+/* "alt\0" */
+{0x61, 0x6c, 0x74, 00, };
+char html_src[5] = 
+/* "src\0" */
+{0x73, 0x72, 0x63, 00, };
+char html_type[6] = 
+/* "type\0" */
+{0x74, 0x79, 0x70, 0x65, 00, };
+char html_submit[8] = 
+/* "submit\0" */
+{0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 00, };
+char html_value[7] = 
+/* "value\0" */
+{0x76, 0x61, 0x6c, 0x75, 0x65, 00, };
+char html_action[8] = 
+/* "action\0" */
+{0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 00, };
+char html_name[6] = 
+/* "name\0" */
+{0x6e, 0x61, 0x6d, 0x65, 00, };
+char html_text[6] = 
+/* "text\0" */
+{0x74, 0x65, 0x78, 0x74, 00, };
+char html_size[6] = 
+/* "size\0" */
+{0x73, 0x69, 0x7a, 0x65, 00, };
+char html_image[7] = 
+/* "image\0" */
+{0x69, 0x6d, 0x61, 0x67, 0x65, 00, };
diff --git a/contiki/apps/html-strings.h b/contiki/apps/html-strings.h
new file mode 100644
index 0000000..7c926f2
--- /dev/null
+++ b/contiki/apps/html-strings.h
@@ -0,0 +1,36 @@
+extern char html_slasha[4];
+extern char html_slashcenter[9];
+extern char html_slashform[7];
+extern char html_slashh[4];
+extern char html_slashscript[9];
+extern char html_slashselect[9];
+extern char html_slashstyle[8];
+extern char html_a[3];
+extern char html_body[6];
+extern char html_br[4];
+extern char html_center[8];
+extern char html_form[6];
+extern char html_frame[7];
+extern char html_h1[4];
+extern char html_h2[4];
+extern char html_h3[4];
+extern char html_h4[4];
+extern char html_img[5];
+extern char html_input[7];
+extern char html_li[4];
+extern char html_p[3];
+extern char html_script[8];
+extern char html_select[8];
+extern char html_style[7];
+extern char html_tr[4];
+extern char html_href[6];
+extern char html_alt[5];
+extern char html_src[5];
+extern char html_type[6];
+extern char html_submit[8];
+extern char html_value[7];
+extern char html_action[8];
+extern char html_name[6];
+extern char html_text[6];
+extern char html_size[6];
+extern char html_image[7];
diff --git a/contiki/apps/htmlparser.c b/contiki/apps/htmlparser.c
new file mode 100644
index 0000000..1ee41d1
--- /dev/null
+++ b/contiki/apps/htmlparser.c
@@ -0,0 +1,780 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment 
+ *
+ * $Id: htmlparser.c,v 1.1 2003/03/19 14:13:33 adamdunkels Exp $
+ *
+ */
+
+/* htmlparser.c:
+ *
+ * Implements a very simplistic HTML parser. It recognizes HTML links
+ * (<a href>-tags), HTML img alt tags, a few text flow break tags
+G * (<br>, <p>, <h>), the <li> tag (but does not even try to
+ * distinguish between <ol> or <ul>) as well as HTML comment tags
+ * (<!-- -->).
+ *
+ * To save memory, the HTML parser is state machine driver, which
+ * means that it will shave off one character from the HTML page,
+ * process that character, and return to the next. Another way of
+ * doing it would be to buffer a number of characters and process them
+ * together.
+ *
+ * The main function in this file is the htmlparser_parse() function
+ * which takes a htmlparser_state structur and a part of an HTML file
+ * as an argument. The htmlparser_parse() function will call the
+ * helper functions parse_char() and parse_tag(). Those functions will
+ * in turn call the two callback functions htmlparser_char() and
+ * htmlparser_tag(). Those functions must be implemented by the using
+ * module (e.g., a web browser program).
+ *
+ * htmlparser_char() will be called for every non-tag character.
+ *
+ * htmlparser_tag() will be called whenever a full tag has been found.
+ *
+ */
+
+
+#include "htmlparser.h"
+#include "html-strings.h"
+#include "www-conf.h"
+
+#if 1
+#define PRINTF(x)
+#else
+#include <stdio.h>
+#define PRINTF(x) printf x
+#endif
+
+#ifdef WITH_CC65
+#define FASTCALL __fastcall__
+#else
+#define FASTCALL
+#endif
+
+/*-----------------------------------------------------------------------------------*/
+#define ISO_A     0x41
+#define ISO_B     0x42
+#define ISO_E     0x45
+#define ISO_F     0x46
+#define ISO_G     0x47
+#define ISO_H     0x48
+#define ISO_I     0x49
+#define ISO_L     0x4c
+#define ISO_M     0x4d
+#define ISO_P     0x50
+#define ISO_R     0x52
+#define ISO_T     0x54
+
+#define ISO_a     (ISO_A | 0x20)
+#define ISO_b     (ISO_B | 0x20)
+#define ISO_e     (ISO_E | 0x20)
+#define ISO_f     (ISO_F | 0x20)
+#define ISO_g     (ISO_G | 0x20)
+#define ISO_h     (ISO_H | 0x20)
+#define ISO_i     (ISO_I | 0x20)
+#define ISO_l     (ISO_L | 0x20)
+#define ISO_m     (ISO_M | 0x20)
+#define ISO_p     (ISO_P | 0x20)
+#define ISO_r     (ISO_R | 0x20)
+#define ISO_t     (ISO_T | 0x20)
+
+#define ISO_ht    0x09
+#define ISO_nl    0x0a
+#define ISO_cr    0x0d
+#define ISO_space 0x20
+#define ISO_bang  0x21
+#define ISO_citation 0x22
+#define ISO_ampersand 0x26
+#define ISO_citation2 0x27
+#define ISO_asterisk 0x2a
+#define ISO_dash  0x2d
+#define ISO_slash 0x2f
+#define ISO_semicolon  0x3b
+#define ISO_lt    0x3c
+#define ISO_eq    0x3d
+#define ISO_gt    0x3e
+
+#define ISO_rbrack 0x5b
+#define ISO_lbrack 0x5d
+
+#define MINORSTATE_NONE           0
+#define MINORSTATE_TEXT           1 /* Parse normal text */
+#define MINORSTATE_EXTCHAR        2 /* Check for semi-colon */
+#define MINORSTATE_TAG            3 /* Check for name of tag. */
+#define MINORSTATE_TAGEND         4 /* Scan for end of tag. */
+#define MINORSTATE_TAGATTR        5 /* Parse tag attr. */
+#define MINORSTATE_TAGATTRSPACE   6 /* Parse optional space after tag
+				       attr. */
+#define MINORSTATE_TAGATTRPARAM   7 /* Parse tag attr parameter. */
+#define MINORSTATE_TAGATTRPARAMNQ 8 /* Parse tag attr parameter without
+				  quotation marks. */
+#define MINORSTATE_HTMLCOMMENT    9 /* Scan for HTML comment end */
+
+#define MAJORSTATE_NONE       0
+#define MAJORSTATE_BODY       1
+#define MAJORSTATE_LINK       2
+#define MAJORSTATE_FORM       3
+#define MAJORSTATE_DISCARD    4
+
+
+struct htmlparser_state {
+  unsigned char minorstate;
+  char tag[20];
+  unsigned char tagptr;
+  char tagattr[20];
+  unsigned char tagattrptr;
+  char tagattrparam[WWW_CONF_MAX_URLLEN];
+  unsigned char tagattrparamptr;
+  unsigned char lastchar, quotechar;
+  unsigned char majorstate, lastmajorstate;
+  char linkurl[WWW_CONF_MAX_URLLEN];
+  char linktext[40];
+  unsigned char linktextptr;
+#if WWW_CONF_FORMS
+  char formaction[WWW_CONF_MAX_FORMACTIONLEN];
+  char formname[WWW_CONF_MAX_FORMNAMELEN];
+  unsigned char inputtype;
+  char inputname[WWW_CONF_MAX_INPUTNAMELEN];
+  char inputvalue[WWW_CONF_MAX_INPUTVALUELEN];
+  unsigned char inputvaluesize;
+#endif /* WWW_CONF_FORMS */
+};
+
+static struct htmlparser_state s;
+
+/*-----------------------------------------------------------------------------------*/
+static char last[1] = {0xff};
+
+static char *tags[] = {
+#define TAG_FIRST       0
+#define TAG_SLASHA      0
+  html_slasha,
+#define TAG_SLASHCENTER 1
+  html_slashcenter,
+#define TAG_SLASHFORM   2
+  html_slashform,
+#define TAG_SLASHH      3
+  html_slashh,
+#define TAG_SLASHSCRIPT 4
+  html_slashscript,
+#define TAG_SLASHSELECT 5
+  html_slashselect,
+#define TAG_SLASHSTYLE  6
+  html_slashstyle,
+#define TAG_A           7
+  html_a,
+#define TAG_BODY        8
+  html_body,
+#define TAG_BR          9
+  html_br,
+#define TAG_CENTER     10 
+  html_center,
+#define TAG_FORM       11
+  html_form,
+#define TAG_FRAME      12    
+  html_frame,
+#define TAG_H1         13  
+  html_h1,
+#define TAG_H2         14
+  html_h2,
+#define TAG_H3         15  
+  html_h3,
+#define TAG_H4         16  
+  html_h4,
+#define TAG_IMG        17  
+  html_img,
+#define TAG_INPUT      18  
+  html_input,
+#define TAG_LI         19
+  html_li,
+#define TAG_P          20
+  html_p,
+#define TAG_SCRIPT     21
+  html_script, 
+#define TAG_SELECT     22
+  html_select,
+#define TAG_STYLE      23
+  html_style,
+#define TAG_TR         24   
+  html_tr,
+#define TAG_LAST       25
+  last,
+};
+
+/*-----------------------------------------------------------------------------------*/
+static unsigned char FASTCALL
+iswhitespace(char c)
+{
+  return (c == ISO_space ||
+	  c == ISO_nl ||
+	  c == ISO_cr ||
+	  c == ISO_ht);
+}
+/*-----------------------------------------------------------------------------------*/
+static unsigned char FASTCALL
+find_tag(char *tag)
+{
+  static unsigned char first, last, i, tabi;
+  static char tagc;
+  
+  tabi = first = TAG_FIRST;
+  last = TAG_LAST;
+  i = 0;
+  
+  do {
+    tagc = tag[i];
+
+    if(tagc == 0 &&
+       tags[first][i] == 0) {
+      return first;
+    }
+    
+    /* First, find first matching tag from table. */
+    while(tagc > (tags[tabi])[i] &&
+	  tabi < last) {
+      ++tabi;
+    }
+    first = tabi;
+    
+    /* Second, find last matching tag from table. */
+    while(tagc == (tags[tabi])[i] &&
+	  tabi < last) {
+      ++tabi;
+    }
+    last = tabi;
+    
+    /* If first and last matching tags are equal, we have a match and
+       return. Else we continue with the next character. */
+    ++i;
+    tabi = first;
+  } while(last != first);
+  return TAG_LAST;
+}
+/*-----------------------------------------------------------------------------------*/
+static void FASTCALL
+parse_char(unsigned char c)
+{
+  if(c < 0x80) {
+    if(s.majorstate == MAJORSTATE_LINK) {
+      if(s.linktextptr < sizeof(s.linktext)) {
+	if(iswhitespace(c)) {
+	  c = ISO_space;
+	}
+	s.linktext[s.linktextptr] = c;
+	++s.linktextptr;
+      }
+    } else if(s.majorstate != MAJORSTATE_DISCARD) {
+      htmlparser_char(c);
+    } 
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+switch_majorstate(unsigned char newstate)
+{
+  if(s.majorstate != newstate) {
+    PRINTF(("Switching state from %d to %d (%d)\n", s.majorstate, newstate, s.lastmajorstate));
+    s.lastmajorstate = s.majorstate;
+    s.majorstate = newstate;
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+parse_tag(void)
+{
+  static char *tagattrparam;
+  static unsigned char size, i;
+  
+  PRINTF(("Parsing tag '%s' '%s' '%s'\n",
+	  s.tag, s.tagattr, s.tagattrparam));
+
+  switch(find_tag(s.tag)) {
+  case TAG_P:
+  case TAG_H1:
+  case TAG_H2:
+  case TAG_H3:
+  case TAG_H4:
+    parse_char(ISO_nl);
+    /* FALLTHROUGH */
+  case TAG_BR:
+  case TAG_TR:
+  case TAG_SLASHH:
+    parse_char(ISO_nl);
+    break;
+  case TAG_LI:
+    parse_char(ISO_nl);
+    parse_char(ISO_asterisk);
+    parse_char(ISO_space);
+    break;
+  case TAG_SCRIPT:
+  case TAG_STYLE:
+  case TAG_SELECT:
+    switch_majorstate(MAJORSTATE_DISCARD);
+    break;
+  case TAG_SLASHSCRIPT:
+  case TAG_SLASHSTYLE:
+  case TAG_SLASHSELECT:
+    switch_majorstate(s.lastmajorstate);
+    break;
+  case TAG_BODY:
+    s.majorstate = s.lastmajorstate = MAJORSTATE_BODY;
+    break;
+  case TAG_FRAME:
+    if(strncmp(s.tagattr, html_src, sizeof(html_src)) == 0 &&
+       s.tagattrparam[0] != 0) {
+      switch_majorstate(MAJORSTATE_BODY);
+      parse_char(ISO_nl);
+      parse_char(ISO_rbrack);
+      parse_char(ISO_space);
+      htmlparser_link(html_frame, s.tagattrparam);
+      PRINTF(("Frame [%s]\n", s.tagattrparam));
+      parse_char(ISO_space);
+      parse_char(ISO_lbrack);
+      parse_char(ISO_nl);
+    }
+    break;
+  case TAG_IMG:
+    if(strncmp(s.tagattr, html_alt, sizeof(html_alt)) == 0 &&
+       s.tagattrparam[0] != 0) {
+      parse_char(ISO_lt);
+      tagattrparam = &s.tagattrparam[0];
+      while(*tagattrparam) {
+	parse_char(*tagattrparam);
+	++tagattrparam;
+      }
+      parse_char(ISO_gt);
+    }
+    break;
+  case TAG_A:
+    PRINTF(("A %s %s\n", s.tagattr, s.tagattrparam));
+    if(strncmp(s.tagattr, html_href, sizeof(html_href)) == 0 &&
+       s.tagattrparam[0] != 0) {
+      strcpy(s.linkurl, s.tagattrparam);
+      switch_majorstate(MAJORSTATE_LINK);
+      s.linktextptr = 0;
+    }
+    break;
+  case TAG_SLASHA:
+    if(s.majorstate == MAJORSTATE_LINK) {
+      switch_majorstate(s.lastmajorstate);
+      s.linktext[s.linktextptr] = 0;
+      htmlparser_link(s.linktext, s.linkurl);
+      PRINTF(("Link '%s' [%s]\n", s.linktext, s.linkurl));
+    }
+    break;
+#if WWW_CONF_FORMS
+  case TAG_FORM:
+    PRINTF(("Form tag\n"));
+    switch_majorstate(MAJORSTATE_FORM);
+    if(strncmp(s.tagattr, html_action, sizeof(html_action)) == 0) {
+      PRINTF(("Form action '%s'\n", s.tagattrparam));
+      strncpy(s.formaction, s.tagattrparam, WWW_CONF_MAX_FORMACTIONLEN - 1);
+    } else if(strncmp(s.tagattr, html_name, sizeof(html_name)) == 0) {
+      PRINTF(("Form name '%s'\n", s.tagattrparam));
+      strncpy(s.formname, s.tagattrparam, WWW_CONF_MAX_FORMNAMELEN - 1);
+    }
+    s.inputname[0] = s.inputvalue[0] = 0;
+    break;
+  case TAG_SLASHFORM:
+    switch_majorstate(MAJORSTATE_BODY);
+    s.formaction[0] = s.formname[0] = 0;
+    break;
+  case TAG_INPUT:
+    if(s.majorstate == MAJORSTATE_FORM) {
+      /* First check if we are called at the end of an input tag. If
+	 so, we should render the input widget. */
+      if(s.tagattr[0] == 0 &&
+	 s.inputname[0] != 0) {
+	PRINTF(("Render input type %d\n", s.inputtype));
+	switch(s.inputtype) {
+	case HTMLPARSER_INPUTTYPE_NONE:
+	case HTMLPARSER_INPUTTYPE_TEXT:
+	  for(i = 0; i < s.inputvaluesize; ++i) {
+	    if(s.inputvalue[i] == 0) {
+	      memset(&s.inputvalue[i], ISO_space, s.inputvaluesize - i);
+	      s.inputvalue[s.inputvaluesize] = 0;
+	      break;
+	    }
+	  }	  
+	  htmlparser_inputfield(s.inputvalue, s.inputname,
+				s.formname, s.formaction);
+	  break;
+	case HTMLPARSER_INPUTTYPE_SUBMIT:
+	case HTMLPARSER_INPUTTYPE_IMAGE:	  
+	  htmlparser_submitbutton(s.inputvalue, s.inputname,
+				  s.formname, s.formaction);
+	  break;
+	}
+	s.inputtype = HTMLPARSER_INPUTTYPE_NONE;
+      } else {
+	PRINTF(("Input '%s' '%s'\n", s.tagattr, s.tagattrparam));
+	if(strncmp(s.tagattr, html_type, sizeof(html_type)) == 0) {
+	  if(strncmp(s.tagattrparam, html_submit,
+		     sizeof(html_submit)) == 0) {
+	    s.inputtype = HTMLPARSER_INPUTTYPE_SUBMIT;
+	  } else if(strncmp(s.tagattrparam, html_image,
+			    sizeof(html_image)) == 0) {
+	    s.inputtype = HTMLPARSER_INPUTTYPE_IMAGE;
+	  } else if(strncmp(s.tagattrparam, html_text,
+			    sizeof(html_text)) == 0) {
+	    s.inputtype = HTMLPARSER_INPUTTYPE_TEXT;
+	  } else {
+	    s.inputtype = HTMLPARSER_INPUTTYPE_OTHER;
+	  }
+	} else if(strncmp(s.tagattr, html_name,
+			  sizeof(html_name)) == 0) {
+	  strncpy(s.inputname, s.tagattrparam,
+		  WWW_CONF_MAX_INPUTNAMELEN);
+	} else if(strncmp(s.tagattr, html_alt,
+			  sizeof(html_alt)) == 0 &&
+		  s.inputtype == HTMLPARSER_INPUTTYPE_IMAGE) {	  
+	  strncpy(s.inputvalue, s.tagattrparam,
+		  WWW_CONF_MAX_INPUTVALUELEN);	  
+	} else if(strncmp(s.tagattr, html_value,
+			  sizeof(html_value)) == 0) {
+	  strncpy(s.inputvalue, s.tagattrparam,
+		  WWW_CONF_MAX_INPUTVALUELEN);
+	} else if(strncmp(s.tagattr, html_size,
+			  sizeof(html_size)) == 0) {
+	  size = 0;
+	  if(s.tagattrparam[0] >= '0' &&
+	     s.tagattrparam[0] <= '9') {
+	    size = s.tagattrparam[0] - '0';
+	    if(s.tagattrparam[1] >= '0' &&
+	       s.tagattrparam[1] <= '9') {
+	      size = size * 10 + (s.tagattrparam[1] - '0');
+	    }
+	  }
+	  if(size >= WWW_CONF_MAX_INPUTVALUELEN) {
+	    size = WWW_CONF_MAX_INPUTVALUELEN - 1;
+	  }
+	  s.inputvaluesize = size;
+	  /*	  strncpy(s.inputvalue, s.tagattrparam,
+		  WWW_CONF_MAX_INPUTVALUELEN);*/
+	}
+      }
+      
+    }
+    break;
+#endif /* WWW_CONF_FORMS */    
+#if WWW_CONF_RENDERSTATE
+  case TAG_CENTER:
+    parse_char(ISO_nl);    
+    htmlparser_renderstate(HTMLPARSER_RENDERSTATE_BEGIN |
+			   HTMLPARSER_RENDERSTATE_CENTER);
+    break;
+  case TAG_SLASHCENTER:
+    parse_char(ISO_nl);
+    htmlparser_renderstate(HTMLPARSER_RENDERSTATE_END |
+			   HTMLPARSER_RENDERSTATE_CENTER);
+    break;
+#endif /* WWW_CONF_RENDERSTATE */
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+void
+htmlparser_init(void)
+{
+  s.majorstate = s.lastmajorstate = MAJORSTATE_DISCARD;
+  s.minorstate = MINORSTATE_TEXT;
+  s.lastchar = 0;
+}
+/*-----------------------------------------------------------------------------------*/
+static char FASTCALL
+lowercase(char c)
+{
+  /* XXX: This is a *brute force* approach to lower-case
+     converting and should *not* be used anywhere else! It
+     works for our purposes, however (i.e., HTML tags). */
+  if(c > 0x40) {
+    return (c & 0x1f) | 0x60;
+  } else {
+    return c;
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+static void 
+endtagfound(void)
+{
+  s.tag[s.tagptr] = 0;
+  s.tagattr[s.tagattrptr] = 0;
+  s.tagattrparam[s.tagattrparamptr] = 0;
+}
+/*-----------------------------------------------------------------------------------*/
+/* htmlparser_parse():
+ *
+ * This is the main function in the HTML parser module and it parses
+ * the HTML data in the input buffer. The htmlparser_state is updated
+ * as the buffer is parsed character by character. The functions
+ * parse_char() and parse_tag() (defined earlier in this file) are
+ * called to process regular characters and HTML tags,
+ * respectively.
+ *
+ * Note that the input buffer does not have to contain full HTML tags;
+ * the parser is state machine driven in order to be able to work with
+ * buffers that have been divided in any way.
+ */
+void
+htmlparser_parse(char *data, u16_t len)
+{
+  static char c;
+  
+  while(len > 0) {
+    c = *data;
+    --len;
+    ++data;
+    
+    switch(s.minorstate) {
+    case MINORSTATE_NONE:
+      break;
+    case MINORSTATE_TEXT:
+      /* We are currently parsing some text, so we look for signs of
+	 an HTML tag starting (i.e., a '<' character). We also
+	 compress any whitespace character to one single space
+	 character (' '). */
+      if(c == ISO_lt) {
+	s.minorstate = MINORSTATE_TAG;
+	s.tagptr = 0;
+	endtagfound();
+      } else if(c == ISO_ampersand) {
+	s.minorstate = MINORSTATE_EXTCHAR;
+      } else {
+	if(iswhitespace(c)) {
+	  if(s.lastchar != ISO_space) {
+	    parse_char(' ');
+	    s.lastchar = ISO_space;
+	    c = ISO_space;
+	  }
+	} else {
+	  parse_char(c);
+	}
+      }
+      break;
+    case MINORSTATE_EXTCHAR:
+      if(c == ISO_semicolon) {	
+	s.minorstate = MINORSTATE_TEXT;
+	parse_char(' ');
+      } else if(iswhitespace(c)) {	
+	s.minorstate = MINORSTATE_TEXT;
+	parse_char('&');
+	parse_char(' ');
+      }
+      break;
+    case MINORSTATE_TAG:
+      /* We are currently parsing within the name of a tag. We check
+	 for the end of a tag (the '>' character) or whitespace (which
+	 indicates that we should parse a tag attr argument
+	 instead). */
+      if(c == ISO_gt) {
+	/* Full tag found. We continue parsing regular text. */
+	s.minorstate = MINORSTATE_TEXT;
+	s.tagattrptr = s.tagattrparamptr = 0;
+	endtagfound();	  
+	parse_tag();
+      } else if(iswhitespace(c)) {
+	/* The name of the tag found. We continue parsing the tag
+	   attr.*/
+	s.minorstate = MINORSTATE_TAGATTR;
+	s.tagattrptr = 0;
+	endtagfound();
+      } else {
+	/* Keep track of the name of the tag, but convert it to
+	   lower case. */
+
+	s.tag[s.tagptr] = lowercase(c);
+	++s.tagptr;
+	/* Check if the ->tag field is full. If so, we just eat up
+	   any data left in the tag. */
+	if(s.tagptr == sizeof(s.tag)) {
+	  s.minorstate = MINORSTATE_TAGEND;
+	}
+      }
+
+      /* Check for HTML comment, indicated by <!-- */
+      if(s.tagptr == 3 &&
+	 s.tag[0] == ISO_bang &&
+	 s.tag[1] == ISO_dash &&
+	 s.tag[2] == ISO_dash) {
+	PRINTF(("Starting comment...\n"));
+	s.minorstate = MINORSTATE_HTMLCOMMENT;
+	s.tagptr = 0;
+	endtagfound();
+      }	         
+      break;
+    case MINORSTATE_TAGATTR:
+      /* We parse the "tag attr", i.e., the "href" in <a
+	 href="...">. */
+      if(c == ISO_gt) {
+	/* Full tag found. */
+	s.minorstate = MINORSTATE_TEXT;
+	s.tagattrparamptr = 0;
+	s.tagattrptr = 0;
+	endtagfound();
+	parse_tag();
+	s.tagptr = 0;
+	endtagfound();
+	
+      } else if(iswhitespace(c)) {
+	if(s.tagattrptr == 0) {
+	  /* Discard leading spaces. */
+	} else {
+	  /* A non-leading space is the end of the attribute. */
+	  s.tagattrparamptr = 0;
+	  endtagfound();
+	  parse_tag();
+	  s.minorstate = MINORSTATE_TAGATTRSPACE;
+	  /*	    s.tagattrptr = 0;
+		    endtagfound();*/
+	}
+      } else if(c == ISO_eq) {	
+	s.minorstate = MINORSTATE_TAGATTRPARAMNQ;
+	s.tagattrparamptr = 0;
+	endtagfound();
+      } else {
+	s.tagattr[s.tagattrptr] = lowercase(c);
+	++s.tagattrptr;
+	/* Check if the "tagattr" field is full. If so, we just eat
+	   up any data left in the tag. */
+	if(s.tagattrptr == sizeof(s.tagattr)) {
+	  s.minorstate = MINORSTATE_TAGEND;
+	}
+      }
+      break;
+    case MINORSTATE_TAGATTRSPACE:
+      if(iswhitespace(c)) {
+	/* Discard spaces. */
+      } else if(c == ISO_eq) {
+	s.minorstate = MINORSTATE_TAGATTRPARAMNQ;
+	s.tagattrparamptr = 0;
+	endtagfound();
+	parse_tag();
+      } else {
+	s.tagattr[0] = lowercase(c);
+	s.tagattrptr = 1;
+	s.minorstate = MINORSTATE_TAGATTR;
+      }
+      break;
+    case MINORSTATE_TAGATTRPARAMNQ:
+      /* We are parsing the "tag attr parameter", i.e., the link part
+	 in <a href="link">. */
+      if(c == ISO_gt) {
+	/* Full tag found. */
+	endtagfound();
+	parse_tag();
+	s.minorstate = MINORSTATE_TEXT;
+	s.tagattrptr = 0;       
+	endtagfound();
+      	parse_tag();
+	s.tagptr = 0;       
+	endtagfound();
+      } else if(iswhitespace(c) &&
+		s.tagattrparamptr == 0) {
+	/* Discard leading spaces. */	  
+      } else if((c == ISO_citation ||
+		 c == ISO_citation2) &&
+		s.tagattrparamptr == 0) {
+	s.minorstate = MINORSTATE_TAGATTRPARAM;
+	s.quotechar = c;
+	PRINTF(("tag attr param q found\n"));
+      } else if(iswhitespace(c)) {
+	PRINTF(("Non-leading space found at %d\n",
+		s.tagattrparamptr));
+	/* Stop parsing if a non-leading space was found */
+	endtagfound();
+	parse_tag();
+	  
+	s.minorstate = MINORSTATE_TAGATTR;
+	s.tagattrptr = 0;
+	endtagfound();
+      } else {
+	s.tagattrparam[s.tagattrparamptr] = c;
+	++s.tagattrparamptr;
+	/* Check if the "tagattr" field is full. If so, we just eat
+	   up any data left in the tag. */
+	if(s.tagattrparamptr >= sizeof(s.tagattrparam) - 1) {
+	  s.minorstate = MINORSTATE_TAGEND;
+	}
+      }
+
+      break;
+    case MINORSTATE_TAGATTRPARAM:
+      /* We are parsing the "tag attr parameter", i.e., the link
+	 part in <a href="link">. */
+      if(c == s.quotechar) {
+	/* Found end of tag attr parameter. */
+	endtagfound();
+	parse_tag();
+	
+	s.minorstate = MINORSTATE_TAGATTR;
+	s.tagattrptr = 0;
+	endtagfound();
+      } else {
+	if(iswhitespace(c)) {
+	  c = ISO_space;
+	}
+	s.tagattrparam[s.tagattrparamptr] = c;
+	++s.tagattrparamptr;
+	/* Check if the "tagattr" field is full. If so, we just eat
+	   up any data left in the tag. */
+	if(s.tagattrparamptr >= sizeof(s.tagattrparam) - 1) {
+	  s.minorstate = MINORSTATE_TAGEND;
+	}
+      }
+
+      break;
+    case MINORSTATE_HTMLCOMMENT:
+      if(c == ISO_dash) {
+	++s.tagptr;
+      } else if(c == ISO_gt && s.tagptr > 0) {
+	PRINTF(("Comment done.\n"));
+	s.minorstate = MINORSTATE_TEXT;
+      } else {
+	s.tagptr = 0;
+      }
+      break;
+    case MINORSTATE_TAGEND:
+      /* Discard characters until a '>' is seen. */
+      if(c == ISO_gt) {
+	s.minorstate = MINORSTATE_TEXT;
+	s.tagattrptr = 0;
+	endtagfound();
+	parse_tag();
+      }
+      break;
+    }
+  
+    s.lastchar = c;
+  }
+}
+/*-----------------------------------------------------------------------------------*/
diff --git a/contiki/apps/htmlparser.h b/contiki/apps/htmlparser.h
new file mode 100644
index 0000000..77ac035
--- /dev/null
+++ b/contiki/apps/htmlparser.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment 
+ *
+ * $Id: htmlparser.h,v 1.1 2003/03/19 14:13:33 adamdunkels Exp $
+ *
+ */
+#ifndef __HTMLPARSER_H__
+#define __HTMLPARSER_H__
+
+#include "uip.h"
+
+/* Callbacks. */
+void htmlparser_char(char c);
+void htmlparser_link(char *text, char *url);
+void htmlparser_submitbutton(char *value,
+			     char *name,
+			     char *formname,
+			     char *formaction);
+void htmlparser_inputfield(char *value,
+			   char *name,
+			   char *formname,
+			   char *formaction);
+
+
+void htmlparser_renderstate(unsigned char state);
+#define HTMLPARSER_RENDERSTATE_STATUSMASK 0x80
+#define HTMLPARSER_RENDERSTATE_BEGIN      0x00
+#define HTMLPARSER_RENDERSTATE_END        0x80
+
+#define HTMLPARSER_RENDERSTATE_NONE       0x00
+#define HTMLPARSER_RENDERSTATE_CENTER     0x01
+#define HTMLPARSER_RENDERSTATE_TABLE      0x02
+#define HTMLPARSER_RENDERSTATE_TR         0x04
+#define HTMLPARSER_RENDERSTATE_TD         0x08
+
+
+#define HTMLPARSER_INPUTTYPE_NONE     0
+#define HTMLPARSER_INPUTTYPE_TEXT     1
+#define HTMLPARSER_INPUTTYPE_PASSWORD 2
+#define HTMLPARSER_INPUTTYPE_SUBMIT   3
+#define HTMLPARSER_INPUTTYPE_IMAGE    4
+#define HTMLPARSER_INPUTTYPE_OTHER    5
+
+
+/* Functions. */
+void htmlparser_init(void);
+void htmlparser_parse(char *data, u16_t len);
+
+
+#endif /* __HTMLPARSER_H__ */
diff --git a/contiki/apps/http-strings b/contiki/apps/http-strings
new file mode 100644
index 0000000..df833e2
--- /dev/null
+++ b/contiki/apps/http-strings
@@ -0,0 +1,15 @@
+http_http "http://"
+http_200 "200 "
+http_301 "301 "
+http_302 "302 "
+http_get "GET "
+http_10 "HTTP/1.0"
+http_11 "HTTP/1.1"
+http_content_type "Content-Type: "
+http_texthtml "text/html"
+http_location "Location: "
+http_host "Host: "
+http_fields "Connection: close\r\nUser-Agent: Contiki/1.0 (Commodore 64; http://dunkels.com/adam/contiki/)\r\n\r\n"
+http_crnl "\r\n"
+http_webserver_d64_headers "HTTP/1.0 200 OK\r\nServer: Contiki/1.0\r\nContent-Type: application/octet-stream\r\nContent-Length: 174848\r\n\r\n"
+
diff --git a/contiki/apps/http-strings.c b/contiki/apps/http-strings.c
new file mode 100644
index 0000000..64c9018
--- /dev/null
+++ b/contiki/apps/http-strings.c
@@ -0,0 +1,42 @@
+char http_http[8] = 
+/* "http://" */
+{0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, };
+char http_200[5] = 
+/* "200 " */
+{0x32, 0x30, 0x30, 0x20, };
+char http_301[5] = 
+/* "301 " */
+{0x33, 0x30, 0x31, 0x20, };
+char http_302[5] = 
+/* "302 " */
+{0x33, 0x30, 0x32, 0x20, };
+char http_get[5] = 
+/* "GET " */
+{0x47, 0x45, 0x54, 0x20, };
+char http_10[9] = 
+/* "HTTP/1.0" */
+{0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30, };
+char http_11[9] = 
+/* "HTTP/1.1" */
+{0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, };
+char http_content_type[15] = 
+/* "Content-Type: " */
+{0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x20, };
+char http_texthtml[10] = 
+/* "text/html" */
+{0x74, 0x65, 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, };
+char http_location[11] = 
+/* "Location: " */
+{0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, };
+char http_host[7] = 
+/* "Host: " */
+{0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, };
+char http_fields[96] = 
+/* "Connection: close\r\nUser-Agent: Contiki/1.0 (Commodore 64; http://dunkels.com/adam/contiki/)\r\n\r\n" */
+{0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0xd, 0xa, 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6b, 0x69, 0x2f, 0x31, 0x2e, 0x30, 0x20, 0x28, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x64, 0x6f, 0x72, 0x65, 0x20, 0x36, 0x34, 0x3b, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x64, 0x75, 0x6e, 0x6b, 0x65, 0x6c, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x64, 0x61, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6b, 0x69, 0x2f, 0x29, 0xd, 0xa, 0xd, 0xa, };
+char http_crnl[3] = 
+/* "\r\n" */
+{0xd, 0xa, };
+char http_webserver_d64_headers[105] = 
+/* "HTTP/1.0 200 OK\r\nServer: Contiki/1.0\r\nContent-Type: application/octet-stream\r\nContent-Length: 174848\r\n\r\n" */
+{0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30, 0x20, 0x32, 0x30, 0x30, 0x20, 0x4f, 0x4b, 0xd, 0xa, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6b, 0x69, 0x2f, 0x31, 0x2e, 0x30, 0xd, 0xa, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6f, 0x63, 0x74, 0x65, 0x74, 0x2d, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0xd, 0xa, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x37, 0x34, 0x38, 0x34, 0x38, 0xd, 0xa, 0xd, 0xa, };
diff --git a/contiki/apps/http-strings.h b/contiki/apps/http-strings.h
new file mode 100644
index 0000000..122ca49
--- /dev/null
+++ b/contiki/apps/http-strings.h
@@ -0,0 +1,14 @@
+extern char http_http[8];
+extern char http_200[5];
+extern char http_301[5];
+extern char http_302[5];
+extern char http_get[5];
+extern char http_10[9];
+extern char http_11[9];
+extern char http_content_type[15];
+extern char http_texthtml[10];
+extern char http_location[11];
+extern char http_host[7];
+extern char http_fields[96];
+extern char http_crnl[3];
+extern char http_webserver_d64_headers[105];
diff --git a/contiki/apps/makestrings b/contiki/apps/makestrings
new file mode 100755
index 0000000..e511bf3
--- /dev/null
+++ b/contiki/apps/makestrings
@@ -0,0 +1,42 @@
+#!/usr/bin/perl
+
+
+sub stringify {
+  my $name = shift(@_);
+  open(OUTPUTC, "> $name.c");
+  open(OUTPUTH, "> $name.h");
+  
+  open(FILE, "$name");
+  
+  while(<FILE>) {
+    if(/(.+) "(.+)"/) {
+      $var = $1;
+      $data = $2;
+      
+      $datan = $data;
+      $datan =~ s/\\r/\r/g;
+      $datan =~ s/\\n/\n/g;
+      $datan =~ s/\\0/\0/g;
+      
+      printf(OUTPUTC "char $var\[%d] = \n", length($datan) + 1);
+      printf(OUTPUTC "/* \"$data\" */\n");
+      printf(OUTPUTC "{");
+      for($j = 0; $j < length($datan); $j++) {
+	printf(OUTPUTC "%#02x, ", unpack("C", substr($datan, $j, 1)));
+      }
+      printf(OUTPUTC "};\n");
+      
+      printf(OUTPUTH "extern char $var\[%d];\n", length($datan) + 1);
+      
+    }
+  }
+  close(OUTPUTC);
+  close(OUTPUTH);
+}
+
+stringify("http-strings");
+stringify("smtp-strings");
+stringify("html-strings");
+
+exit 0;
+
diff --git a/contiki/apps/programs.c b/contiki/apps/programs.c
new file mode 100644
index 0000000..ae581d4
--- /dev/null
+++ b/contiki/apps/programs.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment
+ *
+ * $Id: programs.c,v 1.1 2003/03/19 14:13:33 adamdunkels Exp $
+ *
+ */
+
+#include "ctk.h"
+#include "ek.h"
+#include "dispatcher.h"
+
+#include "webserver.h"
+#include "www.h"
+#include "email.h"
+#include "simpletelnet.h"
+
+
+static struct ctk_menu menu;
+
+static unsigned char menuitem_www,
+  menuitem_email, menuitem_telnet, menuitem_webserver;
+
+#if WITH_WWW
+/* The icon for the WWW browser */
+static unsigned char wwwicon_bitmap[3*3*8] = {
+  0x00, 0x7e, 0x40, 0x73, 0x46, 0x4c, 0x18, 0x13,
+  0x00, 0x00, 0xff, 0x81, 0x34, 0xc9, 0x00, 0xb6,
+  0x00, 0x7e, 0x02, 0xce, 0x72, 0x32, 0x18, 0x48,
+
+  0x30, 0x27, 0x24, 0x20, 0x37, 0x24, 0x20, 0x33,
+  0x00, 0x7b, 0x42, 0x00, 0x7b, 0x42, 0x00, 0x3b,
+  0x0c, 0x24, 0x24, 0x04, 0xa4, 0x24, 0x04, 0x4c,
+
+  0x12, 0x19, 0x4c, 0x46, 0x63, 0x40, 0x7c, 0x00,
+  0x22, 0x91, 0x00, 0xc4, 0x81, 0xff, 0x00, 0x00,
+  0x08, 0x18, 0x32, 0x62, 0xc6, 0x02, 0x3e, 0x00
+};
+
+static char wwwicon_textmap[9] = {
+  'w', 'w', 'w',
+  '(', ')', ' ',
+  ' ', '(', ')'
+};
+
+static struct ctk_icon wwwicon =
+  {CTK_ICON("Web browser", wwwicon_bitmap, wwwicon_textmap)};
+#endif /* WITH_WWW */
+
+#if WITH_WEBSERVER
+/* The icon for the web server */
+static unsigned char webservericon_bitmap[3*3*8] = {
+  0x00, 0x7f, 0x40, 0x41, 0x44, 0x48, 0x40, 0x50,
+  0x00, 0xff, 0x5a, 0x00, 0x00, 0x00, 0x3c, 0x81,
+  0x00, 0xfe, 0x02, 0x82, 0x22, 0x12, 0x02, 0x0a,
+
+  0x41, 0x60, 0x42, 0x62, 0x62, 0x42, 0x60, 0x41,
+  0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18,
+  0x82, 0x06, 0x42, 0x46, 0x46, 0x42, 0x06, 0x82,
+
+  0x50, 0x40, 0x48, 0x44, 0x41, 0x40, 0x7e, 0x00,
+  0xc5, 0x34, 0x3c, 0x52, 0x7a, 0x7e, 0xa1, 0xfd,
+  0x0a, 0x02, 0x12, 0x22, 0x82, 0x02, 0x7e, 0x00
+};
+
+static char webservericon_textmap[9] = {
+  '+', '-', '+',
+  '|', ')', '|',
+  '+', '-', '+'
+};
+
+static struct ctk_icon webservericon =
+  {CTK_ICON("Web server", webservericon_bitmap, webservericon_textmap)};
+#endif /* WITH_WEBSERVER */
+
+
+#if WITH_TELNET
+/* The icon for the telnet client */
+static unsigned char telneticon_bitmap[3*3*8] = {
+  0x00, 0x7f, 0x43, 0x4c, 0x58, 0x53, 0x60, 0x6f,
+  0x00, 0xff, 0x00, 0x7e, 0x00, 0xff, 0x00, 0xff,
+  0x00, 0xfe, 0xc2, 0x32, 0x1a, 0xca, 0x06, 0xf6,
+
+  0x40, 0x5f, 0x40, 0x5f, 0x40, 0x5f, 0x40, 0x4f,
+  0x00, 0xff, 0x00, 0xff, 0x00, 0xfc, 0x01, 0xf3,
+  0x02, 0xfa, 0x02, 0x82, 0x3e, 0xfe, 0xfe, 0xfe,
+
+  0x60, 0x67, 0x50, 0x59, 0x4c, 0x43, 0x7f, 0x00,
+  0x07, 0xe7, 0x0f, 0xef, 0x0f, 0x0f, 0xff, 0x00,
+  0x8e, 0x06, 0x06, 0x06, 0x8e, 0xfe, 0xfe, 0x00
+};
+
+static char telneticon_textmap[9] = {
+  't', 'e', 'l',
+  'n', 'e', 't',
+  '-', '-', '-'
+};
+
+static struct ctk_icon telneticon =
+  {CTK_ICON("Telnet client", telneticon_bitmap, telneticon_textmap)};
+#endif /* WITH_TELNET */
+
+
+static void sighandler(ek_signal_t s, ek_data_t data);
+static struct dispatcher_proc p =
+  {DISPATCHER_PROC("Program handler", NULL, sighandler, NULL)};
+static ek_id_t id;
+
+/*-----------------------------------------------------------------------------------*/
+void
+programs_init(void)     
+{
+  id = dispatcher_start(&p);
+  
+  ctk_menu_new(&menu, "Programs");
+  ctk_menu_add(&menu);
+#ifdef WITH_WWW
+  menuitem_www = ctk_menuitem_add(&menu, "Web browser");
+#endif /* WITH_WWW */
+#ifdef WITH_EMAIL
+  menuitem_email = ctk_menuitem_add(&menu, "E-mail");
+#endif /* WITH_EMAIL */
+#ifdef WITH_WEBSERVER
+  menuitem_webserver = ctk_menuitem_add(&menu, "Web server");
+#endif /* WITH_WEBSERVER */
+#ifdef WITH_TELNET
+  menuitem_telnet = ctk_menuitem_add(&menu, "Telnet client");
+#endif /* WITH_TELNET */
+
+#ifdef WITH_WWW  
+  CTK_ICON_ADD(&wwwicon, id);
+#endif /* WITH_WWW */
+#ifdef WITH_WEBSERVER
+  CTK_ICON_ADD(&webservericon, id);
+#endif /* WITH_WEBSERVER */  
+#ifdef WITH_TELNET
+  CTK_ICON_ADD(&telneticon, id);
+#endif /* WITH_TELNET */  
+  
+  dispatcher_listen(ctk_signal_menu_activate);
+  dispatcher_listen(ctk_signal_button_activate);
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+sighandler(ek_signal_t s, ek_data_t data)
+{
+  if(s == ctk_signal_menu_activate) {
+    if((struct ctk_menu *)data == &menu) {
+      if(menu.active == menuitem_www) {
+#ifdef WITH_WWW
+	www_init();
+	ctk_redraw();
+#endif /* WITH_WWW */
+#ifdef WITH_EMAIL
+      } else if(menu.active == menuitem_email) {
+	email_init();
+	ctk_redraw();
+#endif /* WITH_EMAIL */
+#ifdef WITH_TELNET
+      } else if(menu.active == menuitem_telnet) {
+	simpletelnet_init();
+	ctk_redraw();
+#endif /* WITH_TELNET */
+#ifdef WITH_WEBSERVER
+      } else if(menu.active == menuitem_webserver) {
+	webserver_init();
+	ctk_redraw();
+#endif /* WITH_WEBSERVER */
+      }      
+    }
+  } else if(s == ctk_signal_button_activate) {
+#ifdef WITH_WWW    
+    if((struct ctk_widget *)data == (struct ctk_widget *)&wwwicon) {
+      www_init();
+      ctk_redraw();
+    } 
+#endif /* WITH_WWW */
+#ifdef WITH_WEBSERVER      
+    if((struct ctk_widget *)data ==
+       (struct ctk_widget *)&webservericon) {
+      webserver_init();
+      ctk_redraw();
+    } 
+#endif /* WITH_WEBSERVER */
+#ifdef WITH_TELNET
+    if((struct ctk_widget *)data ==
+       (struct ctk_widget *)&telneticon) {
+      simpletelnet_init();
+      ctk_redraw();
+    } 
+#endif /* WITH_TELNET */
+  }
+  
+}
+/*-----------------------------------------------------------------------------------*/
diff --git a/contiki/apps/programs.h b/contiki/apps/programs.h
new file mode 100644
index 0000000..86f8858
--- /dev/null
+++ b/contiki/apps/programs.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment for the C64.
+ *
+ * $Id: programs.h,v 1.1 2003/03/19 14:13:33 adamdunkels Exp $
+ *
+ */
+#ifndef __PROGRAMS_H__
+#define __PROGRAMS_H__
+
+void programs_init(void);
+
+#endif /* __PROGRAMS_H__ */
diff --git a/contiki/apps/simpletelnet.c b/contiki/apps/simpletelnet.c
new file mode 100644
index 0000000..17e12e9
--- /dev/null
+++ b/contiki/apps/simpletelnet.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment
+ *
+ * $Id: simpletelnet.c,v 1.1 2003/03/19 14:13:33 adamdunkels Exp $
+ *
+ */
+
+#include "petsciiconv.h"
+#include "uip_main.h"
+#include "uip.h"
+#include "ctk.h"
+#include "dispatcher.h"
+#include "resolv.h"
+#include "telnet.h"
+#include "simpletelnet.h"
+
+/* Telnet window */
+static struct ctk_window telnetwindow;
+
+static struct ctk_label telnethostlabel =
+  {CTK_LABEL(1, 0, 4, 1, "Host")};
+static char telnethost[25];
+static struct ctk_textentry telnethosttextentry =
+  {CTK_TEXTENTRY(0, 1, 24, 1, telnethost, 24)};
+
+static struct ctk_label telnetportlabel =
+  {CTK_LABEL(31, 0, 4, 1, "Port")};
+static char telnetport[6];
+static struct ctk_textentry telnetporttextentry =
+  {CTK_TEXTENTRY(30, 1, 5, 1, telnetport, 5)};
+
+static struct ctk_button telnetconnectbutton =
+  {CTK_BUTTON(2, 3, 7, "Connect")};
+static struct ctk_button telnetdisconnectbutton =
+  {CTK_BUTTON(25, 3, 10, "Disconnect")};
+
+static char telnetline[31];
+static struct ctk_textentry telnetlinetextentry =
+  {CTK_TEXTENTRY(0, 5, 30, 1, telnetline, 30)};
+
+
+static struct ctk_button telnetsendbutton =
+  {CTK_BUTTON(32, 5, 4, "Send")};
+
+static struct ctk_label telnetstatus =
+  {CTK_LABEL(0, 19, 38, 1, "")};
+
+static struct ctk_separator telnetsep1 =
+  {CTK_SEPARATOR(0, 7, 38)};
+
+static struct ctk_separator telnetsep2 =
+  {CTK_SEPARATOR(0, 18, 38)};
+
+static char telnettext[38*10];
+static struct ctk_label telnettextarea =
+  {CTK_LABEL(0, 8, 38, 10, telnettext)};
+
+static struct telnet_state ts_appstate;
+
+#define ISO_NL       0x0a
+#define ISO_CR       0x0d
+
+static void sighandler(ek_signal_t s, ek_data_t data);
+static struct dispatcher_proc p =
+  {DISPATCHER_PROC("Simple telnet", NULL, sighandler,
+		   (void (*)(void *))telnet_app)};
+static ek_id_t id;
+
+/*-----------------------------------------------------------------------------------*/
+void
+simpletelnet_init(void)     
+{
+  if(id == EK_ID_NONE) {
+    id = dispatcher_start(&p);
+
+    /* Create Telnet window. */
+    ctk_window_new(&telnetwindow, 38, 20, "Simple telnet");
+    ctk_window_move(&telnetwindow, 0, 1);
+    
+    CTK_WIDGET_ADD(&telnetwindow, &telnethostlabel);
+    CTK_WIDGET_ADD(&telnetwindow, &telnetportlabel);
+    CTK_WIDGET_ADD(&telnetwindow, &telnethosttextentry);
+    CTK_WIDGET_ADD(&telnetwindow, &telnetporttextentry);
+    CTK_WIDGET_ADD(&telnetwindow, &telnetconnectbutton);
+    CTK_WIDGET_ADD(&telnetwindow, &telnetdisconnectbutton);
+    CTK_WIDGET_ADD(&telnetwindow, &telnetlinetextentry);
+    CTK_WIDGET_ADD(&telnetwindow, &telnetsendbutton);
+    
+    CTK_WIDGET_ADD(&telnetwindow, &telnetsep1);
+    CTK_WIDGET_ADD(&telnetwindow, &telnettextarea);
+    
+    CTK_WIDGET_ADD(&telnetwindow, &telnetsep2);
+    CTK_WIDGET_ADD(&telnetwindow, &telnetstatus);
+
+    CTK_WIDGET_FOCUS(&telnetwindow, &telnethosttextentry);
+       
+    /* Attach as a listener to the CTK button press signal. */
+    dispatcher_listen(ctk_signal_button_activate);
+
+    dispatcher_listen(resolv_signal_found);
+  }
+  
+  ctk_window_open(&telnetwindow);
+
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+scrollup(void)
+{
+  unsigned char i;
+  for(i = 1; i < 10; ++i) {
+    memcpy(&telnettext[(i - 1) * 38], &telnettext[i * 38], 38);
+  }
+  memset(&telnettext[9 * 38], 0, 38);
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+add_text(char *text)
+{
+  unsigned char i;
+  unsigned int len;
+  
+  len = strlen(text);
+
+  i = 0;
+  while(len > 0) {
+    if(*text == '\n') {
+      scrollup();
+      i = 0;
+    } else if(*text == '\r') {
+      i = 0;
+    } else {
+      telnettext[9 * 38 + i] = *text;
+      ++i;
+      if(i == 38) {
+	scrollup();
+	i = 0;
+      }
+    }
+    ++text;
+    --len;
+  }
+  
+  /*  if(strlen(text) > 37) {
+      memcpy(&telnettext[9 * 38], text, 37);
+      } else {
+      memcpy(&telnettext[9 * 38], text, strlen(text));
+      }
+  */
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+show(char *text)
+{
+  add_text(text);
+  add_text("\n");
+  ctk_label_set_text(&telnetstatus, text);
+  ctk_window_redraw(&telnetwindow);
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+connect(void)
+{
+  u16_t addr[2], *addrptr;
+  u16_t port;
+  char *cptr;
+  struct uip_conn *conn;
+
+  /* Find the first space character in host and put a zero there
+     to end the string. */
+  for(cptr = telnethost; *cptr != ' ' && *cptr != 0; ++cptr);
+  *cptr = 0;
+
+  addrptr = &addr[0];  
+  if(uip_main_ipaddrconv(telnethost, (unsigned char *)addr) == 0) {
+    addrptr = resolv_lookup(telnethost);
+    if(addrptr == NULL) {
+      resolv_query(telnethost);
+      show("Resolving host...");
+      return;
+    }
+  }
+
+  port = 0;
+  for(cptr = telnetport; *cptr != ' ' && *cptr != 0; ++cptr) {
+    if(*cptr < '0' || *cptr > '9') {
+      show("Port number error");
+      return;
+    }
+    port = 10 * port + *cptr - '0';
+  }
+
+
+  conn = uip_connect(addrptr, port);
+  if(conn == NULL) {
+    show("Out of memory error");
+    return;
+  }
+
+  dispatcher_markconn(conn, &ts_appstate);
+
+  show("Connecting...");
+
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+sighandler(ek_signal_t s, ek_data_t data)
+{
+  struct ctk_widget *w;
+  char *ptr;
+  
+  if(s == ctk_signal_button_activate) {
+    
+    w = (struct ctk_widget *)data;
+    if(w == (struct ctk_widget *)&telnetsendbutton) {
+      petsciiconv_toascii(telnetline, sizeof(telnetline));
+      ptr = telnetline + strlen(telnetline);
+      *ptr++ = ISO_CR;     
+      *ptr++ = ISO_NL;
+      if(telnet_send(&ts_appstate, telnetline, ptr - telnetline)) {
+	/* Could not send. */
+	ctk_label_set_text(&telnetstatus, "Could not send");
+	ctk_window_redraw(&telnetwindow);
+	/*      } else {*/
+	/* Could send */
+      }
+    } else if(w == (struct ctk_widget *)&telnetdisconnectbutton) {
+      telnet_close(&ts_appstate);
+      show("Closing...");
+    } else if(w == (struct ctk_widget *)&telnetconnectbutton) {
+      connect();
+      ctk_window_redraw(&telnetwindow);
+    }
+  } else if(s == resolv_signal_found) {
+    if(strcmp(data, telnethost) == 0) {
+      if(resolv_lookup(telnethost) != NULL) {
+	connect();
+      } else {
+	show("Host not found");
+      }
+    }
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+void
+telnet_connected(struct telnet_state *s)
+{  
+  show("Connected");
+}
+void
+telnet_closed(struct telnet_state *s)
+{
+  show("Connection closed");
+}
+void
+telnet_sent(struct telnet_state *s)
+{
+  petsciiconv_topetscii(telnetline, sizeof(telnetline));
+  add_text(telnetline);
+  memset(telnetline, 0, sizeof(telnetline));
+  ctk_window_redraw(&telnetwindow);
+}
+void
+telnet_aborted(struct telnet_state *s)
+{
+  show("Connection reset by peer");
+}
+void
+telnet_timedout(struct telnet_state *s)
+{
+  show("Connection timed out");
+}
+void
+telnet_newdata(struct telnet_state *s, char *data, u16_t len)
+{
+  petsciiconv_topetscii(data, len);
+  data[len] = 0;
+  add_text(data);
+  ctk_window_redraw(&telnetwindow);
+}
diff --git a/contiki/apps/simpletelnet.h b/contiki/apps/simpletelnet.h
new file mode 100644
index 0000000..e3d5165
--- /dev/null
+++ b/contiki/apps/simpletelnet.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment for the C64.
+ *
+ * $Id: simpletelnet.h,v 1.1 2003/03/19 14:13:33 adamdunkels Exp $
+ *
+ */
+#ifndef __SIMPLETELNET_H__
+#define __SIMPLETELNET_H__
+
+void simpletelnet_init(void);
+void simpletelnet_open(void);
+
+#endif /* __SIMPLETELNET_H__ */
diff --git a/contiki/apps/smtp-strings b/contiki/apps/smtp-strings
new file mode 100644
index 0000000..a9e937c
--- /dev/null
+++ b/contiki/apps/smtp-strings
@@ -0,0 +1,9 @@
+smtp_220 "220"
+smtp_helo "HELO "
+smtp_mail_from "MAIL FROM: "
+smtp_rcpt_to "RCPT TO: "
+smtp_data "DATA"
+smtp_to "To: "
+smtp_from "From: "
+smtp_subject "Subject: "
+smtp_quit "QUIT"
\ No newline at end of file
diff --git a/contiki/apps/smtp-strings.c b/contiki/apps/smtp-strings.c
new file mode 100644
index 0000000..fa50ab6
--- /dev/null
+++ b/contiki/apps/smtp-strings.c
@@ -0,0 +1,27 @@
+char smtp_220[4] = 
+/* "220" */
+{0x32, 0x32, 0x30, };
+char smtp_helo[6] = 
+/* "HELO " */
+{0x48, 0x45, 0x4c, 0x4f, 0x20, };
+char smtp_mail_from[12] = 
+/* "MAIL FROM: " */
+{0x4d, 0x41, 0x49, 0x4c, 0x20, 0x46, 0x52, 0x4f, 0x4d, 0x3a, 0x20, };
+char smtp_rcpt_to[10] = 
+/* "RCPT TO: " */
+{0x52, 0x43, 0x50, 0x54, 0x20, 0x54, 0x4f, 0x3a, 0x20, };
+char smtp_data[5] = 
+/* "DATA" */
+{0x44, 0x41, 0x54, 0x41, };
+char smtp_to[5] = 
+/* "To: " */
+{0x54, 0x6f, 0x3a, 0x20, };
+char smtp_from[7] = 
+/* "From: " */
+{0x46, 0x72, 0x6f, 0x6d, 0x3a, 0x20, };
+char smtp_subject[10] = 
+/* "Subject: " */
+{0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3a, 0x20, };
+char smtp_quit[5] = 
+/* "QUIT" */
+{0x51, 0x55, 0x49, 0x54, };
diff --git a/contiki/apps/smtp-strings.h b/contiki/apps/smtp-strings.h
new file mode 100644
index 0000000..15bed46
--- /dev/null
+++ b/contiki/apps/smtp-strings.h
@@ -0,0 +1,9 @@
+extern char smtp_220[4];
+extern char smtp_helo[6];
+extern char smtp_mail_from[12];
+extern char smtp_rcpt_to[10];
+extern char smtp_data[5];
+extern char smtp_to[5];
+extern char smtp_from[7];
+extern char smtp_subject[10];
+extern char smtp_quit[5];
diff --git a/contiki/apps/smtp.c b/contiki/apps/smtp.c
new file mode 100644
index 0000000..19cd097
--- /dev/null
+++ b/contiki/apps/smtp.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above copyright 
+ *    notice, this list of conditions and the following disclaimer in the 
+ *    documentation and/or other materials provided with the distribution. 
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Adam Dunkels.
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the uIP TCP/IP stack.
+ *
+ * $Id: smtp.c,v 1.1 2003/03/19 14:13:33 adamdunkels Exp $
+ *
+ */
+
+#include "uip.h"
+#include "smtp.h"
+
+#include "smtp-strings.h"
+
+#include <string.h>
+
+#define STATE_SEND_NONE         0
+#define STATE_SEND_HELO         1
+#define STATE_SEND_MAIL_FROM    2
+#define STATE_SEND_RCPT_TO      3
+#define STATE_SEND_DATA         4
+#define STATE_SEND_DATA_HEADERS 5
+#define STATE_SEND_DATA_MESSAGE 6
+#define STATE_SEND_DATA_END     7
+#define STATE_SEND_QUIT         8
+#define STATE_SEND_DONE         9
+
+static char *localhostname;
+static u16_t smtpserver[2];
+
+
+
+#define ISO_nl 0x0a
+#define ISO_cr 0x0d
+
+#define ISO_period 0x2e
+
+#define ISO_2  0x32
+#define ISO_3  0x33
+#define ISO_4  0x34
+#define ISO_5  0x35
+
+
+struct smtp_state {
+  u8_t state;
+  char *to;
+  char *from;
+  char *subject;
+  char *msg;
+  u16_t msglen;
+  
+  u16_t sentlen, textlen;
+  u16_t sendptr;
+
+};
+
+
+static struct smtp_state s;
+
+/*-----------------------------------------------------------------------------------*/
+static void
+senddata(void)    
+{
+  char *textptr;
+
+  if(s.textlen != 0 &&
+     s.textlen == s.sendptr) {
+    return;
+  }
+
+  textptr = (char *)uip_appdata;
+  switch(s.state) {
+  case STATE_SEND_HELO:
+    /* Create HELO message. */
+    strcpy(textptr, smtp_helo);
+    textptr += sizeof(smtp_helo) - 1;
+    strcpy(textptr, localhostname);
+    textptr += strlen(localhostname);
+    *textptr = ISO_cr;
+    ++textptr;
+    *textptr = ISO_nl;
+    ++textptr;
+    /*    printf("s.sendptr %d\n", s.sendptr);*/
+    if(s.sendptr == 0) {
+      s.textlen = textptr - (char *)uip_appdata;
+      /*      printf("s.textlen %d\n", s.textlen);*/
+    }
+    textptr = (char *)uip_appdata;
+    break;
+  case STATE_SEND_MAIL_FROM:
+    /* Create MAIL FROM message. */
+    strcpy(textptr, smtp_mail_from);
+    textptr += sizeof(smtp_mail_from) - 1;
+    strcpy(textptr, s.from);
+    textptr += strlen(s.from);
+    *textptr = ISO_cr;
+    ++textptr;
+    *textptr = ISO_nl;
+    ++textptr;
+    if(s.sendptr == 0) {
+      s.textlen = textptr - (char *)uip_appdata;
+    }
+    textptr = (char *)uip_appdata;
+    break;
+  case STATE_SEND_RCPT_TO:
+    /* Create RCPT_TO message. */
+    strcpy(textptr, smtp_rcpt_to);
+    textptr += sizeof(smtp_rcpt_to) - 1;
+    strcpy(textptr, s.to);
+    textptr += strlen(s.to);
+    *textptr = ISO_cr;
+    ++textptr;
+    *textptr = ISO_nl;
+    ++textptr;
+    if(s.sendptr == 0) {
+      s.textlen = textptr - (char *)uip_appdata;
+    }
+    textptr = (char *)uip_appdata;
+    break;
+  case STATE_SEND_DATA:
+    strcpy(textptr, smtp_data);
+    textptr += sizeof(smtp_data) - 1;
+    *textptr = ISO_cr;
+    ++textptr;
+    *textptr = ISO_nl;
+    ++textptr;
+    if(s.sendptr == 0) {
+      s.textlen = textptr - (char *)uip_appdata;
+    }
+    textptr = (char *)uip_appdata;
+    break;
+  case STATE_SEND_DATA_HEADERS:
+    /* Create mail headers. */
+    strcpy(textptr, smtp_to);
+    textptr += sizeof(smtp_to) - 1;
+    strcpy(textptr, s.to);
+    textptr += strlen(s.to);
+    *textptr = ISO_cr;
+    ++textptr;
+    *textptr = ISO_nl;
+    ++textptr;
+    
+    strcpy(textptr, smtp_from);
+    textptr += sizeof(smtp_from) - 1;
+    strcpy(textptr, s.from);
+    textptr += strlen(s.from);
+    *textptr = ISO_cr;
+    ++textptr;
+    *textptr = ISO_nl;
+    ++textptr;
+    
+    strcpy(textptr, smtp_subject);
+    textptr += sizeof(smtp_subject) - 1;
+    strcpy(textptr, s.subject);
+    textptr += strlen(s.subject);
+    *textptr = ISO_cr;
+    ++textptr;
+    *textptr = ISO_nl;
+    ++textptr;
+    
+    if(s.sendptr == 0) {
+      s.textlen = textptr - (char *)uip_appdata;
+    }
+    textptr = (char *)uip_appdata;
+    break;
+  case STATE_SEND_DATA_MESSAGE:
+    textptr = s.msg;
+    if(s.sendptr == 0) {
+      s.textlen = s.msglen;
+    } 
+    break;
+  case STATE_SEND_DATA_END:
+    *textptr = ISO_cr;
+    ++textptr;
+    *textptr = ISO_nl;
+    ++textptr;
+    *textptr = ISO_period;
+    ++textptr;
+    *textptr = ISO_cr;
+    ++textptr;
+    *textptr = ISO_nl;
+    ++textptr;
+    if(s.sendptr == 0) {
+      s.textlen = 5;
+    }
+    textptr = (char *)uip_appdata;
+    break;
+  case STATE_SEND_QUIT:
+    strcpy(textptr, smtp_quit);
+    textptr += sizeof(smtp_quit) - 1;
+    *textptr = ISO_cr;
+    ++textptr;
+    *textptr = ISO_nl;
+    ++textptr;
+    if(s.sendptr == 0) {
+      s.textlen = textptr - (char *)uip_appdata;
+    }
+    textptr = (char *)uip_appdata;
+    break;
+  default:
+    return;
+  }
+
+  textptr += s.sendptr;
+
+  /*  printf("Senidng '%s'\n", textptr);*/
+  
+  if(s.textlen - s.sendptr > uip_mss()) {
+    s.sentlen = uip_mss();
+  } else {
+    s.sentlen = s.textlen - s.sendptr;
+  }
+  uip_send(textptr, s.sentlen);
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+acked(void)    
+{
+  s.sendptr += s.sentlen;
+  s.sentlen = 0;
+
+  if(s.sendptr == s.textlen) {
+    switch(s.state) {
+    case STATE_SEND_DATA_HEADERS:
+      s.state = STATE_SEND_DATA_MESSAGE;
+      break;
+    case STATE_SEND_DATA_MESSAGE:
+      s.state = STATE_SEND_DATA_END;
+      break;
+    case STATE_SEND_DATA_END:
+      s.state = STATE_SEND_QUIT;
+      break;
+    case STATE_SEND_QUIT:
+      s.state = STATE_SEND_DONE;
+      smtp_done(SMTP_ERR_OK);
+      uip_close();
+      break;
+    }
+    s.sendptr = s.textlen = 0;
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+newdata(void)
+{
+  if(*(char *)uip_appdata == ISO_5) {
+    smtp_done(1);
+    uip_abort();
+    return;
+  }
+  /*  printf("Got %d bytes: '%s'\n", uip_datalen(),
+      uip_appdata);*/
+  switch(s.state) {
+  case STATE_SEND_NONE:       
+    if(strncmp((char *)uip_appdata, smtp_220, 3) == 0) {
+      /*      printf("Got 220\n");*/
+      s.state = STATE_SEND_HELO;
+    }
+    break;
+  case STATE_SEND_HELO:
+    if(*(char *)uip_appdata == ISO_2) {
+      /*      printf("2\n");*/
+      s.state = STATE_SEND_MAIL_FROM;
+      s.sendptr = 0;
+    }    
+    break;
+  case STATE_SEND_MAIL_FROM:
+    if(*(char *)uip_appdata == ISO_2) {
+      /*      printf("2\n");*/
+      s.state = STATE_SEND_RCPT_TO;
+      s.textlen = s.sendptr = 0;
+    }
+    break;
+  case STATE_SEND_RCPT_TO:
+    if(*(char *)uip_appdata == ISO_2) {
+      /*      printf("2\n");*/
+      s.state = STATE_SEND_DATA;
+      s.textlen = s.sendptr = 0;
+    }
+    break;
+  case STATE_SEND_DATA:
+    if(*(char *)uip_appdata == ISO_3) {
+      /*      printf("3\n");*/
+      s.state = STATE_SEND_DATA_HEADERS;
+      s.textlen = s.sendptr = 0;
+    }
+    break;
+  case STATE_SEND_DATA_HEADERS:    
+    if(*(char *)uip_appdata == ISO_3) {
+      /*      printf("3\n");*/
+      s.state = STATE_SEND_DATA_MESSAGE;
+      s.textlen = s.sendptr = 0;
+    }
+    break;
+  }
+    
+}
+/*-----------------------------------------------------------------------------------*/
+void
+smtp_appcall(void *state)
+{
+  if(uip_connected()) {
+    /*    senddata();*/
+    return;
+  }
+  if(uip_acked()) {
+    acked();
+  }
+  if(uip_newdata()) {    
+    newdata();
+  }
+  if(uip_rexmit() ||
+     uip_newdata() ||
+     uip_acked()) {
+    senddata();
+  } else if(uip_poll()) {    
+    senddata();
+  }
+  /*  if(uip_closed()) {
+    printf("Dnoe\n");
+    }*/
+
+
+}
+/*-----------------------------------------------------------------------------------*/
+unsigned char
+smtp_send(char *to, char *from, char *subject,
+	  char *msg, u16_t msglen)
+{
+  struct uip_conn *conn;
+
+  conn = uip_connect(smtpserver, 25);
+  if(conn == NULL) {
+    return 0;
+  }
+  dispatcher_markconn(conn, NULL);
+  
+  s.state = STATE_SEND_NONE;
+  s.sentlen = s.sendptr = s.textlen = 0;
+  s.to = to;
+  s.from = from;
+  s.subject = subject;
+  s.msg = msg;
+  s.msglen = msglen;
+
+  return 1;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+smtp_configure(char *lhostname, u16_t *server)
+{
+  localhostname = lhostname;
+  smtpserver[0] = server[0];
+  smtpserver[1] = server[1];
+}
+/*-----------------------------------------------------------------------------------*/
diff --git a/contiki/apps/smtp.h b/contiki/apps/smtp.h
new file mode 100644
index 0000000..1f9feed
--- /dev/null
+++ b/contiki/apps/smtp.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above copyright 
+ *    notice, this list of conditions and the following disclaimer in the 
+ *    documentation and/or other materials provided with the distribution. 
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Adam Dunkels.
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the uIP TCP/IP stack.
+ *
+ * $Id: smtp.h,v 1.1 2003/03/19 14:13:33 adamdunkels Exp $
+ *
+ */
+#ifndef __SMTP_H__
+#define __SMTP_H__
+
+#include "uipopt.h"
+
+/* Callbacks. */
+#define SMTP_ERR_OK 0
+void smtp_done(unsigned char error);
+
+/* Functions. */
+void smtp_configure(char *localhostname, u16_t *smtpserver);
+unsigned char smtp_send(char *to, char *from,
+			char *subject, char *msg,
+			u16_t msglen);
+
+void smtp_appcall(void *);
+
+
+#ifndef UIP_APPCALL
+#define UIP_APPCALL     smtp_appcall
+#endif
+
+#ifndef UIP_APPSTATE_SIZE
+#define UIP_APPSTATE_SIZE (sizeof(struct smtp_state))
+#endif
+
+
+#endif /* __SMTP_H__ */
diff --git a/contiki/apps/telnet.c b/contiki/apps/telnet.c
new file mode 100644
index 0000000..f28bda9
--- /dev/null
+++ b/contiki/apps/telnet.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above copyright 
+ *    notice, this list of conditions and the following disclaimer in the 
+ *    documentation and/or other materials provided with the distribution. 
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Adam Dunkels.
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the uIP TCP/IP stack.
+ *
+ * $Id: telnet.c,v 1.1 2003/03/19 14:13:33 adamdunkels Exp $
+ *
+ */
+
+#include "uip.h"
+
+#include "telnet.h"
+
+#ifndef NULL
+#define NULL (void *)0
+#endif /* NULL */
+
+#define FLAG_CLOSE 1
+#define FLAG_ABORT 2
+/*-----------------------------------------------------------------------------------*/
+unsigned char
+telnet_send(struct telnet_state *s, char *text, u16_t len)
+{
+  if(s->text != NULL) {
+    return 1;
+  }
+  s->text = text;
+  s->textlen = len;  
+  s->sentlen = 0;
+  return 0;
+}
+/*-----------------------------------------------------------------------------------*/
+unsigned char
+telnet_close(struct telnet_state *s)
+{
+  s->flags = FLAG_CLOSE;
+  if(s->text != NULL) {
+    return 1;
+  }
+  return 0;
+}
+/*-----------------------------------------------------------------------------------*/
+unsigned char
+telnet_abort(struct telnet_state *s)
+{
+  s->flags = FLAG_ABORT;
+  if(s->text != NULL) {
+    return 1;
+  }
+  return 0;
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+acked(struct telnet_state *s)    
+{
+  s->textlen -= s->sentlen;
+  if(s->textlen == 0) {
+    s->text = NULL;
+    telnet_sent(s);
+  } else {
+    s->text += s->sentlen;
+  }
+  s->sentlen = 0;
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+senddata(struct telnet_state *s)    
+{
+  if(s->text == NULL) {
+    uip_send(s->text, 0);
+    return;
+  }
+  if(s->textlen > uip_mss()) {
+    s->sentlen = uip_mss();
+  } else {
+    s->sentlen = s->textlen;
+  }
+  uip_send(s->text, s->sentlen);
+}
+/*-----------------------------------------------------------------------------------*/
+void
+telnet_app(struct telnet_state *s)
+{
+  if(uip_connected()) {
+    s->flags = 0;
+    telnet_connected(s);
+    senddata(s);
+    return;
+  }
+  
+  if(uip_closed()) {
+    telnet_closed(s);
+  }
+  
+  if(uip_aborted()) {
+    telnet_aborted(s);
+  }
+  if(uip_timedout()) {
+    telnet_timedout(s);
+  }
+
+
+  if(s->flags & FLAG_CLOSE) {
+    uip_close();
+    return;
+  }
+  if(s->flags & FLAG_ABORT) {
+    uip_abort();
+    return;
+  }
+  if(uip_acked()) {
+    acked(s);
+  }
+  if(uip_newdata()) {
+    telnet_newdata(s, (char *)uip_appdata, uip_datalen());
+  }
+  if(uip_rexmit() ||
+     uip_newdata() ||
+     uip_acked()) {
+    senddata(s);
+  } else if(uip_poll()) {    
+    senddata(s);
+  }
+}
+/*-----------------------------------------------------------------------------------*/
diff --git a/contiki/apps/telnet.h b/contiki/apps/telnet.h
new file mode 100644
index 0000000..f52ba94
--- /dev/null
+++ b/contiki/apps/telnet.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above copyright 
+ *    notice, this list of conditions and the following disclaimer in the 
+ *    documentation and/or other materials provided with the distribution. 
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Adam Dunkels.
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the uIP TCP/IP stack.
+ *
+ * $Id: telnet.h,v 1.1 2003/03/19 14:13:33 adamdunkels Exp $
+ *
+ */
+#ifndef __TELNET_H__
+#define __TELNET_H__
+
+#include "uipopt.h"
+
+struct telnet_state {
+  unsigned char flags;
+  char *text;
+  u16_t textlen;
+  u16_t sentlen;
+};
+
+void telnet_app(struct telnet_state *s);
+unsigned char telnet_send(struct telnet_state *s, char *text, u16_t len);
+unsigned char telnet_close(struct telnet_state *s);
+unsigned char telnet_abort(struct telnet_state *s);
+
+/* Callbacks, implemented by the caller. */
+void telnet_connected(struct telnet_state *s);
+void telnet_closed(struct telnet_state *s);
+void telnet_sent(struct telnet_state *s);
+void telnet_aborted(struct telnet_state *s);
+void telnet_timedout(struct telnet_state *s);
+void telnet_newdata(struct telnet_state *s, char *data, u16_t len);
+#endif /* __TELNET_H__ */
diff --git a/contiki/apps/webclient.c b/contiki/apps/webclient.c
new file mode 100644
index 0000000..5a72f84
--- /dev/null
+++ b/contiki/apps/webclient.c
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the "contiki" web browser.
+ *
+ * $Id: webclient.c,v 1.1 2003/03/19 14:13:33 adamdunkels Exp $
+ *
+ */
+
+#include "uip.h"
+#include "webclient.h"
+#include "resolv.h"
+#include "uip_main.h"
+
+#include <string.h>
+
+#define WEBCLIENT_TIMEOUT 100
+
+#define WEBCLIENT_STATE_STATUSLINE 0
+#define WEBCLIENT_STATE_HEADERS    1
+#define WEBCLIENT_STATE_DATA       2
+#define WEBCLIENT_STATE_CLOSE      3
+
+#define HTTPFLAG_NONE   0
+#define HTTPFLAG_OK     1
+#define HTTPFLAG_MOVED  2
+#define HTTPFLAG_ERROR  3
+
+
+#define ISO_nl       0x0a
+#define ISO_cr       0x0d
+#define ISO_space    0x20
+
+struct webclient_state {
+  u8_t timer;
+  u8_t state;
+  u8_t httpflag;
+
+  u16_t port;
+  char host[40];
+  char file[100];  
+  u16_t getrequestptr;
+  u16_t getrequestleft;
+  
+  char httpheaderline[200];
+  u16_t httpheaderlineptr;
+
+  char mimetype[32];
+};
+
+static struct webclient_state s;
+
+/*-----------------------------------------------------------------------------------*/
+char *
+webclient_mimetype(void)
+{
+  return &s.mimetype;
+}
+/*-----------------------------------------------------------------------------------*/
+char *
+webclient_filename(void)
+{
+  return &s.file;
+}
+/*-----------------------------------------------------------------------------------*/
+char *
+webclient_hostname(void)
+{
+  return &s.host;
+}
+/*-----------------------------------------------------------------------------------*/
+unsigned short
+webclient_port(void)
+{
+  return s.port;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+webclient_init(void)
+{
+
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+init_connection(void)
+{
+  s.state = WEBCLIENT_STATE_STATUSLINE;
+
+  s.getrequestleft = sizeof(http_get) - 1 + 1 +
+    sizeof(http_10) - 1 +
+    sizeof(http_crnl) - 1 +
+    sizeof(http_host) - 1 +
+    sizeof(http_crnl) - 1 +
+    sizeof(http_fields) - 1 +
+    strlen(s.file) + strlen(s.host);
+  s.getrequestptr = 0;
+
+  s.httpheaderlineptr = 0;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+webclient_close(void)
+{
+  s.state = WEBCLIENT_STATE_CLOSE;
+}
+/*-----------------------------------------------------------------------------------*/
+unsigned char
+webclient_get(char *host, u16_t port, char *file)
+{
+  struct uip_conn *conn;
+  u16_t *ipaddr; 
+
+
+  ipaddr = resolv_lookup(host);
+
+  if(ipaddr == NULL) {
+    return 0;
+  }
+  
+  conn = uip_connect(ipaddr, port);
+
+  if(conn == NULL) {
+    return 0;
+  }
+  
+  dispatcher_markconn(conn, NULL);
+
+  s.port = port;
+  strncpy(s.file, file, sizeof(s.file));
+  strncpy(s.host, host, sizeof(s.host));
+  
+  init_connection();
+  return 1;
+}
+/*-----------------------------------------------------------------------------------*/
+static unsigned char *
+copy_string(unsigned char *dest, unsigned char *src, unsigned char len)
+{
+  return strcpy(dest, src) + len;
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+senddata(void)
+{
+  u16_t len;
+  char *getrequest;
+  char *cptr;
+  
+  if(s.getrequestleft > 0) {
+    cptr = getrequest = (char *)uip_appdata;
+
+    cptr = copy_string(cptr, http_get, sizeof(http_get) - 1);
+    cptr = copy_string(cptr, s.file, strlen(s.file));
+    *cptr++ = ISO_space;
+    cptr = copy_string(cptr, http_10, sizeof(http_10) - 1);
+
+    cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1);
+    
+    cptr = copy_string(cptr, http_host, sizeof(http_host) - 1);
+    cptr = copy_string(cptr, s.host, strlen(s.host));
+    cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1);
+
+    cptr = copy_string(cptr, http_fields, sizeof(http_fields) - 1);
+    
+    len = s.getrequestleft > uip_mss()?
+      uip_mss():
+      s.getrequestleft;
+    uip_send(&(getrequest[s.getrequestptr]), len);
+  }
+}  
+/*-----------------------------------------------------------------------------------*/
+static void
+acked(void)
+{
+  u16_t len;
+  
+  if(s.getrequestleft > 0) {
+    len = s.getrequestleft > uip_mss()?
+      uip_mss():
+      s.getrequestleft;
+    s.getrequestleft -= len;
+    s.getrequestptr += len;
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+static u16_t
+parse_statusline(u16_t len)
+{
+  char *cptr;
+  
+  while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
+    s.httpheaderline[s.httpheaderlineptr] = *uip_appdata;
+    ++uip_appdata;
+    --len;
+    if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
+
+      if((strncmp(s.httpheaderline, http_10,
+		  sizeof(http_10) - 1) == 0) ||
+	 (strncmp(s.httpheaderline, http_11,
+		  sizeof(http_11) - 1) == 0)) {
+	cptr = &(s.httpheaderline[9]);
+	s.httpflag = HTTPFLAG_NONE;
+	if(strncmp(cptr, http_200, sizeof(http_200) - 1) == 0) {
+	  /* 200 OK */
+	  s.httpflag = HTTPFLAG_OK;
+	} else if(strncmp(cptr, http_301, sizeof(http_301) - 1) == 0 ||
+		  strncmp(cptr, http_302, sizeof(http_302) - 1) == 0) {
+	  /* 301 Moved permanently or 302 Found. Location: header line
+	     will contain thw new location. */
+	  s.httpflag = HTTPFLAG_MOVED;
+	} else {
+	  s.httpheaderline[s.httpheaderlineptr - 1] = 0;
+	}
+      } else {
+	uip_abort();
+	webclient_aborted();
+	return 0;
+      }
+      
+      /* We're done parsing the status line, so we reset the pointer
+	 and start parsing the HTTP headers.*/
+      s.httpheaderlineptr = 0;
+      s.state = WEBCLIENT_STATE_HEADERS;
+      break;
+    } else {
+      ++s.httpheaderlineptr;
+    }
+  }
+  return len;
+}
+/*-----------------------------------------------------------------------------------*/
+static u16_t
+parse_headers(u16_t len)
+{
+  char *cptr;
+  
+  while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
+    s.httpheaderline[s.httpheaderlineptr] = *uip_appdata;
+    ++uip_appdata;
+    --len;
+    if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
+      /* We have an entire HTTP header line in s.httpheaderline, so
+	 we parse it. */
+      if(s.httpheaderline[0] == ISO_cr) {
+	/* This was the last header line (i.e., and empty "\r\n"), so
+	   we are done with the headers and proceed with the actual
+	   data. */
+	s.state = WEBCLIENT_STATE_DATA;
+	return len;
+      }
+
+      s.httpheaderline[s.httpheaderlineptr - 1] = 0;
+      /* Check for specific HTTP header fields. */
+      if(strncmp(s.httpheaderline, http_content_type,
+		 sizeof(http_content_type) - 1) == 0) {
+	/* Found Content-type field. */
+	cptr = strchr(s.httpheaderline, ';');
+	if(cptr != NULL) {
+	  *cptr = 0;
+	}
+	strncpy(s.mimetype, s.httpheaderline +
+		sizeof(http_content_type) - 1, sizeof(s.mimetype));
+      } else if(strncmp(s.httpheaderline, http_location,
+			    sizeof(http_location) - 1) == 0) {
+	strncpy(s.file, s.httpheaderline +
+		sizeof(http_location) - 1, sizeof(s.file));
+	s.file[s.httpheaderlineptr - 1] = 0;
+      }
+
+
+      /* We're done parsing, so we reset the pointer and start the
+	 next line. */
+      s.httpheaderlineptr = 0;      
+    } else {
+      ++s.httpheaderlineptr;
+    }
+  }
+  return len;
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+newdata(void)
+{
+  u16_t len;
+
+  len = uip_datalen();
+
+  if(s.state == WEBCLIENT_STATE_STATUSLINE) {
+    len = parse_statusline(len);
+  }
+  
+  if(s.state == WEBCLIENT_STATE_HEADERS && len > 0) {
+    len = parse_headers(len);
+  }
+
+  if(len > 0 && s.state == WEBCLIENT_STATE_DATA &&
+     s.httpflag != HTTPFLAG_MOVED) {
+    webclient_datahandler((char *)uip_appdata, len);
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+void
+webclient_appcall(void *state)
+{
+  struct uip_conn *conn;
+
+  if(s.state == WEBCLIENT_STATE_CLOSE) {
+    webclient_closed();
+    uip_abort();
+    return;
+  }    
+  
+  if(uip_connected()) {
+    s.timer = 0;
+    s.state = WEBCLIENT_STATE_STATUSLINE;
+    senddata();
+    webclient_connected();
+    return;
+  }
+  
+  if(uip_aborted()) {
+    webclient_aborted();
+  }
+  if(uip_timedout()) {
+    webclient_timedout();
+  }
+
+  
+  if(uip_acked()) {
+    s.timer = 0;
+    acked();
+  }
+  if(uip_newdata()) {
+    s.timer = 0;
+    newdata();
+  }
+  if(uip_rexmit() ||
+     uip_newdata() ||
+     uip_acked()) {
+    senddata();
+  } else if(uip_poll()) {
+    ++s.timer;
+    if(s.timer == WEBCLIENT_TIMEOUT) {
+      webclient_timedout();
+      uip_abort();
+      return;
+    }
+        /*    senddata();*/
+  }
+
+  if(uip_closed()) {
+    if(s.httpflag != HTTPFLAG_MOVED) {
+      /* Send NULL data to signal EOF. */
+      webclient_datahandler(NULL, 0);
+    } else {
+      conn = uip_connect(uip_conn->ripaddr, s.port);
+      if(conn != NULL) {
+	dispatcher_markconn(conn, NULL);
+	init_connection();
+      }
+    }
+  }
+}
+/*-----------------------------------------------------------------------------------*/
diff --git a/contiki/apps/webclient.h b/contiki/apps/webclient.h
new file mode 100644
index 0000000..b365428
--- /dev/null
+++ b/contiki/apps/webclient.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the "contiki" web browser.
+ *
+ * $Id: webclient.h,v 1.1 2003/03/19 14:13:33 adamdunkels Exp $
+ *
+ */
+#ifndef __WEBCLIENT_H__
+#define __WEBCLIENT_H__
+
+
+#include "http-strings.h"
+
+/* Callback functions that have to be implemented by the application
+   program. */
+struct webclient_state;
+void webclient_datahandler(char *data, u16_t len);
+void webclient_connected(void);
+void webclient_timedout(void);
+void webclient_aborted(void);
+void webclient_closed(void);
+
+
+/* Functions. */
+void webclient_init(void);
+unsigned char webclient_get(char *host, u16_t port, char *file);
+void webclient_close(void);
+void webclient_appcall(void *);
+
+char *webclient_mimetype(void);
+char *webclient_filename(void);
+char *webclient_hostname(void);
+unsigned short webclient_port(void);
+
+#endif /* __WEBCLIENT_H__ */
diff --git a/contiki/apps/webserver.c b/contiki/apps/webserver.c
new file mode 100644
index 0000000..e317673
--- /dev/null
+++ b/contiki/apps/webserver.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment for the C64.
+ *
+ * $Id: webserver.c,v 1.1 2003/03/19 14:13:33 adamdunkels Exp $
+ *
+ */
+
+
+#include "ctk.h"
+#include "dispatcher.h"
+#include "http-strings.h"
+#include "uip_main.h"
+#include "petsciiconv.h"
+
+#include <c64.h>
+#include <cbm.h>
+
+
+/* The main window. */
+static struct ctk_window mainwindow;
+
+static struct ctk_label message =
+  {CTK_LABEL(0, 0, 14, 1, "Web server is ")};
+static struct ctk_label onoffmessage =
+  {CTK_LABEL(14, 0, 3, 1, "Off")};
+
+
+static struct ctk_label tracklabel =
+  {CTK_LABEL(1, 2, 5, 1, "Track")};
+static struct ctk_label sectorlabel =
+  {CTK_LABEL(1, 3, 6, 1, "Sector")};
+
+static char tracknum[3], sectornum[3];
+
+static struct ctk_label tracknumlabel =
+  {CTK_LABEL(14, 2, 2, 1, tracknum)};
+static struct ctk_label sectornumlabel =
+  {CTK_LABEL(14, 3, 2, 1, sectornum)};
+
+static struct ctk_button onbutton =
+  {CTK_BUTTON(0, 5, 2, "On")};
+static struct ctk_label statuslabel =
+  {CTK_LABEL(4, 5, 8, 1, "")};
+static struct ctk_button offbutton =
+  {CTK_BUTTON(12, 5, 3, "Off")};
+
+static unsigned char onoff;
+#define ON  1
+#define OFF 0
+
+static void sighandler(ek_signal_t s, ek_data_t data);
+static void uipappcall(void *state);
+static struct dispatcher_proc p =
+  {DISPATCHER_PROC("Web server", NULL, sighandler, uipappcall)};
+static ek_id_t id;
+
+
+struct drv_state {
+  u8_t state;
+#define STATE_NONE              0
+#define STATE_RECEIVING_REQUEST 1  
+#define STATE_SENDING_HEADERS   2
+#define STATE_SENDING_DATA      3
+#define STATE_DONE              4
+
+  /*  char filename[40];*/
+  
+  u8_t track;
+  u8_t sect;
+};
+
+#define ISO_slash    0x2f    
+#define ISO_space    0x20
+#define ISO_nl       0x0a
+#define ISO_cr       0x0d
+
+
+static struct drv_state ds;
+
+/*-----------------------------------------------------------------------------------*/
+void
+webserver_init(void)     
+{
+  if(id == EK_ID_NONE) {
+    id = dispatcher_start(&p);
+
+    ctk_window_new(&mainwindow, 17, 6, "Web server");
+    ctk_window_move(&mainwindow, 21, 16);
+  
+    CTK_WIDGET_ADD(&mainwindow, &message);
+    CTK_WIDGET_ADD(&mainwindow, &onoffmessage);
+
+    CTK_WIDGET_ADD(&mainwindow, &tracklabel);
+    CTK_WIDGET_ADD(&mainwindow, &tracknumlabel);
+    CTK_WIDGET_ADD(&mainwindow, &sectorlabel);
+    CTK_WIDGET_ADD(&mainwindow, &sectornumlabel);
+
+    CTK_WIDGET_ADD(&mainwindow, &onbutton);
+    CTK_WIDGET_ADD(&mainwindow, &offbutton);
+    
+    CTK_WIDGET_FOCUS(&mainwindow, &onbutton);
+    
+    /* Attach listeners to signals. */
+    dispatcher_listen(ctk_signal_button_activate);
+
+    dispatcher_uiplisten(80);
+    
+    onoff = OFF;
+  }
+
+  ctk_window_open(&mainwindow);
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+sighandler(ek_signal_t s, ek_data_t data)
+{
+  struct ctk_button *b;
+  unsigned char i;
+  
+  if(s == ctk_signal_button_activate) {
+    b = (struct ctk_button *)data;
+    if(b == &onbutton) {
+      ctk_label_set_text(&onoffmessage, "On ");
+      CTK_WIDGET_REDRAW(&onoffmessage);
+      onoff = ON;
+    } else if(b == &offbutton) {
+      ctk_label_set_text(&onoffmessage, "Off");
+      CTK_WIDGET_REDRAW(&onoffmessage);
+      onoff = OFF;
+    }
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+x_open(u8_t f, u8_t d, u8_t cmd, u8_t *fname)
+{
+  u8_t ret;
+  
+  ret = cbm_open(f, d, cmd, fname);
+  if(ret != 0) {
+    /*    printf("open: error %d\n", ret);*/
+    ctk_label_set_text(&statuslabel, "Open err");
+    CTK_WIDGET_REDRAW(&statuslabel);
+  }
+  
+}
+
+static u8_t cmd[32];
+static void
+read_sector(u8_t device, u8_t track, u8_t sect, void *mem)
+{  
+  int ret;
+  
+  x_open(15, device, 15, NULL);
+  x_open(2, device, 2, "#");
+
+  /*  sprintf(cmd, "u1: 2  0%3d%3d", track, sect);  */
+  strcpy(cmd, "u1: 2  0");
+  cmd[8] = ' ';
+  cmd[9] = '0' + track / 10;
+  cmd[10] = '0' + track % 10;
+  cmd[11] = ' ';
+  cmd[12] = '0' + sect / 10;
+  cmd[13] = '0' + sect % 10;
+  cmd[14] = 0;
+  cbm_write(15, cmd, strlen(cmd));
+  /*  printf("%s\n", cmd);*/
+    
+  ret = cbm_read(2, mem, 256);
+  if(ret == -1) {
+    ctk_label_set_text(&statuslabel, "Read err");
+    CTK_WIDGET_REDRAW(&statuslabel);
+  }
+  /*  printf("read: read %d bytes\n", ret);*/
+
+  cbm_close(2);
+  cbm_close(15);
+
+}
+
+static void
+write_sector(u8_t device, u8_t track, u8_t sect, void *mem)
+{
+/*  u16_t ret;
+  u8_t cmd[32];
+  
+  x_open(15, device, 15, NULL);
+  x_open(2, device, 2, "#");
+
+  sprintf(cmd, "u2: 2  0%3d%3d", track, sect);  
+  cbm_write(15, cmd, strlen(cmd));
+  printf("%s\n", cmd);
+    
+  ret = cbm_write(2, mem, 256);
+  printf("write: wrote %d bytes\n", ret);
+
+  cbm_close(2);
+  cbm_close(15);*/
+}
+
+static u8_t
+next_sector(void)
+{
+  ++ds.sect;
+  if(ds.track < 18) {
+    if(ds.sect == 21) {
+      ++ds.track;
+      ds.sect = 0;
+    }
+  } else if(ds.track < 25) {
+    if(ds.sect == 19) {
+      ++ds.track;
+      ds.sect = 0;
+    }
+  } else if(ds.track < 31) {
+    if(ds.sect == 18) {
+      ++ds.track;
+      ds.sect = 0;
+    }
+  } else if(ds.track < 36) {
+    if(ds.sect == 17) {
+      ++ds.track;
+      ds.sect = 0;
+    }
+  }
+
+  tracknum[0] = '0' + ds.track / 10;
+  tracknum[1] = '0' + ds.track % 10;
+
+  CTK_WIDGET_REDRAW(&tracknumlabel);
+  
+  sectornum[0] = '0' + ds.sect / 10;
+  sectornum[1] = '0' + ds.sect % 10;
+  
+  CTK_WIDGET_REDRAW(&sectornumlabel);
+  
+    
+  if(ds.track == 36) {
+    return 1;
+  }
+  return 0;
+}
+
+
+/*-----------------------------------------------------------------------------------*/
+static void
+uipappcall(void *state)
+{
+  u8_t i;
+  int len;
+  
+  if(onoff == OFF) {
+    cbm_close(4);
+    uip_abort();
+    return;
+  }
+
+  if(uip_connected()) {
+    ds.state = STATE_RECEIVING_REQUEST;
+  } else if(uip_acked()) {
+    switch(ds.state) {
+    case STATE_SENDING_HEADERS:      
+      ds.state = STATE_SENDING_DATA;
+      ds.track = 1;
+      ds.sect = 0;    
+      break;
+    case STATE_SENDING_DATA:
+      if(next_sector() != 0) {
+	cbm_close(4);
+	uip_close();
+	ds.state = STATE_NONE;
+	return;
+      }
+    }
+  } else if(uip_newdata()) {
+    if(ds.state == STATE_RECEIVING_REQUEST) {
+      ds.state = STATE_SENDING_HEADERS;
+#if 0
+      if(strncmp(uip_appdata, http_get, sizeof(http_get)) == 0 &&
+	 uip_appdata[4] == ISO_slash) {
+	/*	if(uip_appdata[5] == ISO_space) {
+	  ds.filename[0] = '$';
+	  ds.filename[1] = 0;
+	} else {
+	  for(i = 0; i < sizeof(ds.filename); ++i) {
+	    ds.filename[i] = uip_appdata[6 + i];
+	    if(ds.filename[i] == ISO_space ||
+	       ds.filename[i] == ISO_nl ||
+	       ds.filename[i] == ISO_cr) {
+	      ds.filename[i] = 0;
+	      break;
+	    }
+	  }
+	}
+	cbm_open(4, 8, 4, ds.filename);*/
+	ds.state = STATE_SENDING_HEADERS;
+      }
+#endif
+    }		      
+  }
+  switch(ds.state) {
+  case STATE_SENDING_HEADERS:
+    uip_send(http_webserver_d64_headers, sizeof(http_webserver_d64_headers) - 1);
+    break;
+  case STATE_SENDING_DATA:
+    read_sector(8, ds.track, ds.sect, (void *)uip_appdata);    
+    /*    len = cbm_read(4, uip_appdata, 256);
+	  if(len > 0) {*/
+      uip_send(uip_appdata, 256);
+      /*    }*/
+    break;
+  }
+  
+}
+/*-----------------------------------------------------------------------------------*/
diff --git a/contiki/apps/webserver.h b/contiki/apps/webserver.h
new file mode 100644
index 0000000..6fb77fd
--- /dev/null
+++ b/contiki/apps/webserver.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment for the C64.
+ *
+ * $Id: webserver.h,v 1.1 2003/03/19 14:13:33 adamdunkels Exp $
+ *
+ */
+#ifndef __WEBSERVER_H__
+#define __WEBSERVER_H__
+
+void webserver_init(void);
+
+#endif /* __WEBSERVER_H__ */
diff --git a/contiki/apps/www-conf.h b/contiki/apps/www-conf.h
new file mode 100644
index 0000000..385c3a7
--- /dev/null
+++ b/contiki/apps/www-conf.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment
+ *
+ * $Id: www-conf.h,v 1.1 2003/03/19 14:13:33 adamdunkels Exp $
+ *
+ */
+#ifndef __WWW_CONF_H__
+#define __WWW_CONF_H__
+
+#define WWW_CONF_MAX_URLLEN 100
+
+#define WWW_CONF_RENDERSTATE 1
+#define WWW_CONF_FORMS       1
+
+
+#define WWW_CONF_MAX_FORMACTIONLEN  40
+#define WWW_CONF_MAX_FORMNAMELEN    20
+#define WWW_CONF_MAX_INPUTNAMELEN   20
+#define WWW_CONF_MAX_INPUTVALUELEN  34
+
+#endif /* __WWW_CONF_H__ */
diff --git a/contiki/apps/www.c b/contiki/apps/www.c
new file mode 100644
index 0000000..f61fda9
--- /dev/null
+++ b/contiki/apps/www.c
@@ -0,0 +1,1007 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment
+ *
+ * $Id: www.c,v 1.1 2003/03/19 14:13:33 adamdunkels Exp $
+ *
+ */
+
+
+#include "ctk.h"
+#include "dispatcher.h"
+#include "webclient.h"
+#include "htmlparser.h"
+#include "http-strings.h"
+#include "resolv.h"
+
+#include "petsciiconv.h"
+
+#include "www-conf.h"
+
+#if 0
+#define PRINTF(x)
+#else
+#include <stdio.h>
+#define PRINTF(x) printf x
+#endif
+
+
+/* The array that holds the current URL. */
+static char url[WWW_CONF_MAX_URLLEN + 1];
+static char tmpurl[WWW_CONF_MAX_URLLEN + 1];
+
+/* The array that holds the web page text. */
+#define WEBPAGE_WIDTH 36
+#define WEBPAGE_HEIGHT 17
+static char webpage[WEBPAGE_WIDTH * WEBPAGE_HEIGHT + 1];
+
+/* The CTK widgets for the main window. */
+static struct ctk_window mainwindow;
+
+static struct ctk_button backbutton =
+  {CTK_BUTTON(0, 0, 4, "Back")};
+static struct ctk_button downbutton =
+  {CTK_BUTTON(10, 0, 4, "Down")};
+static struct ctk_button stopbutton =
+  {CTK_BUTTON(20, 0, 4, "Stop")};
+static struct ctk_button gobutton =
+  {CTK_BUTTON(32, 0, 2, "Go")};
+
+static struct ctk_separator sep1 =
+  {CTK_SEPARATOR(0, 2, 36)};
+
+static char editurl[WWW_CONF_MAX_URLLEN + 1];
+static struct ctk_textentry urlentry =
+  {CTK_TEXTENTRY(0, 1, 34, 1, editurl, WWW_CONF_MAX_URLLEN)};
+static struct ctk_label webpagelabel =
+  {CTK_LABEL(0, 3, WEBPAGE_WIDTH, WEBPAGE_HEIGHT, webpage)};
+
+static char statustexturl[36];
+static struct ctk_label statustext =
+  {CTK_LABEL(0, 21, 36, 1, "")};
+static struct ctk_separator sep2 =
+  {CTK_SEPARATOR(0, 20, 36)};
+
+/* The char arrays that hold the history of visited URLs. */
+#define HISTORY_SIZE 4
+static char history[HISTORY_SIZE][WWW_CONF_MAX_URLLEN];
+static char history_last, history_first;
+
+
+/* The CTK widget definitions for the hyperlinks and the char arrays
+   that hold the link URLs. */
+struct formattribs {
+  char formaction[WWW_CONF_MAX_FORMACTIONLEN];
+  char formname[WWW_CONF_MAX_FORMNAMELEN];
+#define FORMINPUTTYPE_SUBMITBUTTON 1
+#define FORMINPUTTYPE_INPUTFIELD   2
+  unsigned char inputtype;
+  char inputname[WWW_CONF_MAX_INPUTNAMELEN];
+  char *inputvalue;
+};
+
+union pagewidgetattrib {
+  char url[WWW_CONF_MAX_URLLEN];
+  struct formattribs form;
+};
+#define MAX_NUMPAGEWIDGETS 20
+static struct ctk_widget pagewidgets[MAX_NUMPAGEWIDGETS];
+static union pagewidgetattrib pagewidgetattribs[MAX_NUMPAGEWIDGETS];
+static unsigned char pagewidgetptr;
+
+
+/* The "scrolly" variable holds the line number (in the web page) of
+   the first line of text shown on screen. */
+static unsigned short scrolly;
+
+#if WWW_CONF_RENDERSTATE
+static unsigned char renderstate;
+#endif /* WWW_CONF_RENDERSTATE */
+
+/* The "run" flag is used to determine if the web page should be
+   continuosly scrolled upward with new data coming in from below. */
+static unsigned char run;
+
+#define ISO_nl    0x0a
+#define ISO_space 0x20
+#define ISO_ampersand 0x26
+#define ISO_plus 0x2b
+#define ISO_slash 0x2f
+#define ISO_eq    0x3d
+#define ISO_questionmark  0x3f
+
+/* The state of the rendering code. */
+static u8_t x;
+static u16_t starty;
+static char nextword[WEBPAGE_WIDTH + 1];
+static unsigned char nextwordptr;
+
+static unsigned char count;
+static char receivingmsgs[4][23] = {
+  "Receiving web page ...",
+  "Receiving web page. ..",
+  "Receiving web page.. .",
+  "Receiving web page... "
+};
+
+
+static void sighandler(ek_signal_t s, ek_data_t data);
+static struct dispatcher_proc p =
+  {DISPATCHER_PROC("Web browser", NULL, sighandler, webclient_appcall)};
+static ek_id_t id;
+
+
+static void formsubmit(struct formattribs *attribs);
+/*-----------------------------------------------------------------------------------*/
+/* make_window()
+ *
+ * Creates the web browser's window.
+ */
+static void
+make_window(void)
+{ 
+
+  CTK_WIDGET_ADD(&mainwindow, &backbutton);
+  CTK_WIDGET_ADD(&mainwindow, &downbutton);
+  CTK_WIDGET_ADD(&mainwindow, &stopbutton);
+  CTK_WIDGET_ADD(&mainwindow, &gobutton);
+  CTK_WIDGET_ADD(&mainwindow, &urlentry);
+  CTK_WIDGET_ADD(&mainwindow, &sep1);
+  CTK_WIDGET_ADD(&mainwindow, &webpagelabel);
+  CTK_WIDGET_ADD(&mainwindow, &sep2);
+  CTK_WIDGET_ADD(&mainwindow, &statustext);
+
+  CTK_WIDGET_FOCUS(&mainwindow, &stopbutton);
+  
+  pagewidgetptr = 0;
+}
+/*-----------------------------------------------------------------------------------*/
+/* redraw_window():
+ *
+ * Convenience function that calls upon CTK to redraw the browser
+ * window. */
+static void
+redraw_window(void)
+{
+  ctk_window_redraw(&mainwindow);
+}
+/*-----------------------------------------------------------------------------------*/
+/* www_init();
+ *
+ * Initializes and starts the web browser. Called either at startup or
+ * to open the browser window.
+ */
+void
+www_init(void)     
+{
+  if(id == EK_ID_NONE) {
+    id = dispatcher_start(&p);
+    
+    /* Create the main window. */
+    memset(webpage, 0, sizeof(webpage));
+    ctk_window_new(&mainwindow, 36, 22, "WWW");
+    make_window();
+    CTK_WIDGET_FOCUS(&mainwindow, &urlentry);
+    
+    /* Attach as a listener to a number of signals ("Button activate",
+       "Hyperlink activate" and "Hyperlink hover", and the resolver's
+       signal. */
+    dispatcher_listen(ctk_signal_button_activate);
+    dispatcher_listen(ctk_signal_hyperlink_activate);
+    dispatcher_listen(ctk_signal_hyperlink_hover);
+    dispatcher_listen(resolv_signal_found);
+  }
+  ctk_window_open(&mainwindow);
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+clear_page(void)
+{
+  if(ctk_window_isopen(&mainwindow)) {
+    ctk_window_close(&mainwindow);
+  }
+  ctk_window_clear(&mainwindow);
+  make_window();
+  ctk_window_open(&mainwindow);
+  memset(webpage, 0, WEBPAGE_WIDTH * WEBPAGE_HEIGHT);  
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+show_url(void)
+{
+  memcpy(editurl, url, WWW_CONF_MAX_URLLEN);
+  strncpy(editurl, "http://", 7);
+  petsciiconv_topetscii(editurl + 7, WWW_CONF_MAX_URLLEN - 7);
+  CTK_WIDGET_REDRAW(&urlentry);
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+show_statustext(char *text)
+{
+  ctk_label_set_text(&statustext, text);
+  CTK_WIDGET_REDRAW(&statustext);
+}
+/*-----------------------------------------------------------------------------------*/
+/* open_url():
+ *
+ * Called when the URL present in the global "url" variable should be
+ * opened. It will call the hostname resolver as well as the HTTP
+ * client requester.
+ */
+static void
+open_url(void)
+{
+  unsigned char i;
+  static char host[32];
+  char *file;
+  register char *urlptr;
+  unsigned short port;
+
+  /* Trim off any spaces in the end of the url. */
+  urlptr = url + strlen(url) - 1;
+  while(*urlptr == ' ' && urlptr > url) {
+    *urlptr = 0;
+    --urlptr;
+  }
+
+  /* Don't even try to go further if the URL is empty. */
+  if(urlptr == url) {
+    return;
+  }
+
+  /* See if the URL starts with http://, otherwise prepend it. */
+  if(strncmp(url, http_http, 7) != 0) {
+    while(urlptr >= url) {
+      *(urlptr + 7) = *urlptr;
+      --urlptr;
+    }
+    strncpy(url, http_http, 7);
+  } 
+
+  /* Find host part of the URL. */
+  urlptr = &url[7];  
+  for(i = 0; i < sizeof(host); ++i) {
+    if(*urlptr == 0 ||
+       *urlptr == '/' ||
+       *urlptr == ' ' ||
+       *urlptr == ':') {
+      host[i] = 0;
+      break;
+    }
+    host[i] = *urlptr;
+    ++urlptr;
+  }
+
+  /* XXX: Here we should find the port part of the URL, but this isn't
+     currently done because of laziness from the programmer's side
+     :-) */
+  
+  /* Find file part of the URL. */
+  while(*urlptr != '/' && *urlptr != 0) {
+    ++urlptr;
+  }
+  if(*urlptr == '/') {
+    file = urlptr;
+  } else {
+    file = "/";
+  }
+  
+  /* Try to lookup the hostname. If it fails, we initiate a hostname
+     lookup and print out an informative message on the statusbar. */
+  if(resolv_lookup(host) == NULL) {
+    resolv_query(host);
+    show_statustext("Resolving host...");
+    return;
+  }
+
+  /* The hostname we present in the hostname table, so we send out the
+     initial GET request. */
+  if(webclient_get(host, 80, file) == 0) {
+    show_statustext("Out of memory error.");
+  } else {
+    show_statustext("Connecting...");
+  }
+  redraw_window();
+}
+/*-----------------------------------------------------------------------------------*/
+/* open_link(link):
+ *
+ * Will format a link from the current web pages so that it suits the
+ * open_url() function and finally call it to open the requested URL.
+ */
+static void
+open_link(char *link)
+{
+  char *urlptr;
+    
+  if(strncmp(link, http_http, 7) == 0) {
+    /* The link starts with http://. We just copy the contents of the
+       link into the url string and jump away. */
+    strncpy(url, link, WWW_CONF_MAX_URLLEN);
+  } else if(*link == ISO_slash &&
+	    *(link + 1) == ISO_slash) {
+    /* The link starts with //, so we'll copy it into the url
+       variable, starting after the http (which already is present in
+       the url variable since we were able to open the web page on
+       which this link was found in the first place). */
+    strncpy(&url[5], link, WWW_CONF_MAX_URLLEN);   
+  } else if(*link == ISO_slash) {
+    /* The link starts with a slash, so it is a non-relative link
+       within the same web site. We find the start of the filename of
+       the current URL and paste the contents of this link there, and
+       head off to the new URL. */
+    for(urlptr = &url[7];
+	*urlptr != 0 && *urlptr != ISO_slash;
+	++urlptr);    
+    strncpy(urlptr, link, WWW_CONF_MAX_URLLEN - (urlptr - url));    
+  } else {
+    /* A fully relative link is found. We find the last slash in the
+       current URL and paste the link there. */
+    
+    /* XXX: we should really parse any ../ in the link as well. */
+    for(urlptr = url + strlen(url);
+	urlptr != url && *urlptr != ISO_slash;
+	--urlptr);
+    ++urlptr;
+    strncpy(urlptr, link, WWW_CONF_MAX_URLLEN - (urlptr - url));    
+  }
+
+  /* Open the URL. */
+  scrolly = 0;
+  show_url();
+  open_url();
+}
+/*-----------------------------------------------------------------------------------*/
+/* log_back():
+ *
+ * Copies the current URL from the url variable and into the log for
+ * the back button.
+ */
+static void
+log_back(void)
+{
+  memcpy(history[history_last], url, WWW_CONF_MAX_URLLEN);
+  ++history_last;
+  if(history_last > HISTORY_SIZE) {
+    history_last = 0;
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+/* www_dispatcher():
+ *
+ * The program's signal dispatcher function. Is called by the ek
+ * dispatcher whenever a signal arrives.
+ */
+static void
+sighandler(ek_signal_t s, ek_data_t data)
+{
+  static struct ctk_widget *w;
+  static unsigned char i;
+
+  
+  w = (struct ctk_widget *)data;
+  if(s == ctk_signal_button_activate) {
+    if(w == (struct ctk_widget *)&backbutton) {
+      scrolly = 0;
+      run = 1;
+
+      --history_last;
+      if(history_last > HISTORY_SIZE) {
+	history_last = HISTORY_SIZE - 1;
+      }
+      memcpy(url, history[history_last], WWW_CONF_MAX_URLLEN);
+      open_url();
+      CTK_WIDGET_FOCUS(&mainwindow, &backbutton);      
+    } else if(w == (struct ctk_widget *)&downbutton) {
+      run = 1;
+      open_url();
+      CTK_WIDGET_FOCUS(&mainwindow, &downbutton);
+    } else if(w == (struct ctk_widget *)&gobutton) {
+      scrolly = 0;
+      run = 1;
+      log_back();
+      memcpy(url, editurl, WWW_CONF_MAX_URLLEN);
+      petsciiconv_toascii(url, WWW_CONF_MAX_URLLEN);
+      open_url();
+      CTK_WIDGET_FOCUS(&mainwindow, &gobutton);
+    } else if(w == (struct ctk_widget *)&stopbutton) {
+      run = 0;
+      webclient_close();
+#if WWW_CONF_FORMS
+    } else {
+      /* Check form buttons */
+      for(i = 0; i < pagewidgetptr; ++i) {
+	if(&pagewidgets[i] == w) {
+	  formsubmit(&pagewidgetattribs[i].form);
+	  /*	  show_statustext(pagewidgetattribs[i].form.formaction);*/
+	  /*	  PRINTF(("Formaction %s formname %s inputname %s\n",
+		  pagewidgetattribs[i].form.formaction,
+		  pagewidgetattribs[i].form.formname,
+		  pagewidgetattribs[i].form.inputname));*/
+	  break;
+	}
+      }
+#endif /* WWW_CONF_FORMS */
+    }
+  } else if(s == ctk_signal_hyperlink_activate) {
+    log_back();
+    open_link(w->widget.hyperlink.url);
+    CTK_WIDGET_FOCUS(&mainwindow, &stopbutton);
+    run = 1;
+  } else if(s == ctk_signal_hyperlink_hover) {
+    strncpy(statustexturl, w->widget.hyperlink.url,
+	    sizeof(statustexturl));
+    petsciiconv_topetscii(statustexturl, sizeof(statustexturl));
+    show_statustext(statustexturl);
+  } else if(s == resolv_signal_found) {
+    /* Either found a hostname, or not. */
+    if((char *)data != NULL &&
+       resolv_lookup((char *)data) != NULL) {
+      open_url();
+    } else {
+      show_statustext("Host not found.");
+    }
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+/* set_url():
+ *
+ * Constructs an URL from the arguments and puts it into the global
+ * "url" variable and the visible "editurl" (which is shown in the URL
+ * text entry widget in the browser window).
+ */
+static void
+set_url(char *host, u16_t port, char *file)
+{
+  char *urlptr;
+
+  memset(url, 0, WWW_CONF_MAX_URLLEN);
+  
+  if(strncmp(file, http_http, 7) == 0) {
+    strncpy(url, file, sizeof(url));
+  } else {
+    strncpy(url, http_http, 7);
+    urlptr = url + 7;
+    strcpy(urlptr, host);
+    urlptr += strlen(host);
+    strcpy(urlptr, file);
+  }
+
+  show_url();
+}
+/*-----------------------------------------------------------------------------------*/
+/* webclient_aborted():
+ *
+ * Callback function. Called from the webclient when the HTTP
+ * connection was abruptly aborted.
+ */
+void
+webclient_aborted(void)
+{
+  show_statustext("Connection reset by peer");
+}
+/*-----------------------------------------------------------------------------------*/
+/* webclient_timedout():
+ *
+ * Callback function. Called from the webclient when the HTTP
+ * connection timed out.
+ */
+void
+webclient_timedout(void)
+{
+  show_statustext("Connection timed out");
+}
+/*-----------------------------------------------------------------------------------*/
+/* webclient_closed():
+ *
+ * Callback function. Called from the webclient when the HTTP
+ * connection was closed after a request from the "webclient_close()"
+ * function. .
+ */
+void
+webclient_closed(void)
+{  
+  show_statustext("Stopped.");
+  petsciiconv_topetscii(&webpage[(WEBPAGE_HEIGHT - 1) *
+				 WEBPAGE_WIDTH], WEBPAGE_WIDTH);
+  redraw_window();
+}
+/*-----------------------------------------------------------------------------------*/
+/* webclient_closed():
+ *
+ * Callback function. Called from the webclient when the HTTP
+ * connection is connected.
+ */
+void
+webclient_connected(void)
+{
+  x = nextwordptr = 0;
+  starty = scrolly;
+  nextword[0] = 0;
+
+  if(scrolly == 0) {
+    clear_page();
+    redraw_window();
+  }
+    
+  show_statustext("Request sent...");
+  set_url(webclient_hostname(), webclient_port(), webclient_filename());
+
+#if WWW_CONF_RENDERSTATE 
+  renderstate = HTMLPARSER_RENDERSTATE_NONE;
+#endif /* WWW_CONF_RENDERSTATE */
+  htmlparser_init();
+}
+/*-----------------------------------------------------------------------------------*/
+/* scroll():
+ *
+ * Scrolls the entire web page display (text and hyperlinks) one line
+ * upwards.
+ */
+static void
+scroll(void)
+{
+  unsigned char i;
+  unsigned char lptr, linkptrtmp;
+  struct ctk_widget *linksptr;
+  char *statustexttext;
+  struct ctk_widget *focuswidget;
+  
+  /* Scroll text up. */
+  memcpy(webpage, &webpage[WEBPAGE_WIDTH],
+	 (WEBPAGE_HEIGHT - 1) * WEBPAGE_WIDTH);
+  
+  /* Clear last line of text. */
+  memset(&webpage[(WEBPAGE_HEIGHT - 1) * WEBPAGE_WIDTH],
+	 ' ', WEBPAGE_WIDTH);
+  
+  /* Scroll links up. */
+
+  lptr = 0;
+  linksptr = pagewidgets;
+  for(i = 0; i < pagewidgetptr; ++i) {    
+    /* First, check which links that scroll off the top of the page
+       and should be removed. */
+    if(CTK_WIDGET_YPOS(linksptr) == 3) {
+      lptr = i + 1;
+    } else {
+      /* Else, move them upward one notch. */
+      
+      /* XXX: this is really a hack! These values should not be used
+	 like this, but should be obtained and set using some CTK API
+	 function. */
+      linksptr->widget.hyperlink.text -= WEBPAGE_WIDTH;
+
+      --(linksptr->y);
+    }
+    ++linksptr;
+  }
+
+  /* See if there are any links that scroll off the top. */
+  if(lptr != 0) {
+    memcpy(pagewidgets, &pagewidgets[lptr],
+	   sizeof(struct ctk_widget) * (MAX_NUMPAGEWIDGETS - lptr));
+    memcpy(pagewidgetattribs, &pagewidgetattribs[lptr],
+	   sizeof(union pagewidgetattrib) * (MAX_NUMPAGEWIDGETS - lptr));    
+
+    /* Compute new value of linkptr and tuck it away in
+       linkptrtmp. make_window() destroys linkptr, so we need to
+       restore it after the call. */
+    linkptrtmp = pagewidgetptr - lptr;
+
+    /* XXX: hack - these values should *not* be obtained this way, but
+       through some CTK API instead! */
+    statustexttext = statustext.text;
+    focuswidget = mainwindow.focused;
+    ctk_window_clear(&mainwindow);
+    make_window();
+    CTK_WIDGET_FOCUS(&mainwindow, focuswidget);
+    show_statustext(statustexttext);
+    
+    pagewidgetptr = linkptrtmp;
+	
+    linksptr = pagewidgets;
+    for(i = 0; i < pagewidgetptr; ++i) {
+      if(linksptr->type == CTK_WIDGET_HYPERLINK) {
+	linksptr->widget.hyperlink.url -= lptr *
+	  sizeof(union pagewidgetattrib);
+      }
+      CTK_WIDGET_ADD(&mainwindow, linksptr);
+      ++linksptr;
+    }
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+static char tmpcenterline[WEBPAGE_WIDTH];
+/* inc_y():
+ *
+ * Called from the rendering code when it is time to move on line
+ * downwards.
+ */
+static void
+inc_y(void)
+{
+  unsigned char spaces, i;
+  char *cptr;
+  struct ctk_widget *linksptr;
+  
+  if(starty > 0) {
+    --starty;
+  } else {
+#if WWW_CONF_RENDERSTATE
+    /* Check if current line should be centered and if so, center
+       it. */
+    if(renderstate & HTMLPARSER_RENDERSTATE_CENTER) {
+      cptr = &webpage[(WEBPAGE_HEIGHT - 0) * WEBPAGE_WIDTH - 1];
+      for(spaces = 0; spaces < WEBPAGE_WIDTH; ++spaces) {
+	if(*cptr-- != ' ') {
+	  break;
+	}
+      }
+
+      spaces = spaces / 2;
+
+      strncpy(tmpcenterline,
+	      &webpage[(WEBPAGE_HEIGHT - 1) *
+		       WEBPAGE_WIDTH],
+	      WEBPAGE_WIDTH);
+      strncpy(&webpage[(WEBPAGE_HEIGHT - 1) *
+		       WEBPAGE_WIDTH] + spaces,
+	      tmpcenterline,
+	      WEBPAGE_WIDTH - spaces);
+      memset(&webpage[(WEBPAGE_HEIGHT - 1) *
+		      WEBPAGE_WIDTH], ' ', spaces);
+      
+      linksptr = pagewidgets;
+      for(i = 0; i < pagewidgetptr; ++i) {
+	if(CTK_WIDGET_YPOS(linksptr) == 3 + WEBPAGE_HEIGHT - 1) {
+	  linksptr->x += spaces;
+	  linksptr->widget.hyperlink.text += spaces;
+	}
+	++linksptr;
+      }
+    }
+#endif /* WWW_CONF_RENDERSTATE */
+        
+    petsciiconv_topetscii(&webpage[(WEBPAGE_HEIGHT - 1) *
+				   WEBPAGE_WIDTH], WEBPAGE_WIDTH);
+    redraw_window();
+    scroll();
+    ++scrolly;
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+/* webclient_datahandler():   
+ *
+ * Callback function. Called from the webclient module when HTTP data
+ * has arrived.
+ */
+void
+webclient_datahandler(char *data, u16_t len)
+{
+  if(len > 0) {
+    /*    if(strcmp(webclient_mimetype(), http_texthtml) == 0) {*/
+      count = (count + 1) & 3;
+      show_statustext(receivingmsgs[count]);
+      htmlparser_parse(data, len);
+      /*    } else {
+      show_statustext("Receiving non-HTML data...");
+      }*/
+  } else {
+    /* Clear remaining parts of page. */
+    run = 0;
+  }
+  
+  if(data == NULL) {
+    run = 0;
+    show_statustext("Done.");
+    petsciiconv_topetscii(&webpage[(WEBPAGE_HEIGHT - 1) *
+				   WEBPAGE_WIDTH], WEBPAGE_WIDTH);
+    redraw_window();
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+/* output_word():
+ *
+ * Called from the rendering code when a full word has been received
+ * and should be put on screen.
+ */
+static void
+output_word(char c)
+{
+  char *webpageptr;
+
+  if(nextwordptr == 0) {
+    if(c == ISO_nl) {
+      x = 0;
+      inc_y();
+    }
+    return;
+  }
+  
+  if(x + nextwordptr > WEBPAGE_WIDTH) {
+    inc_y();
+    x = 0;
+  }
+
+  nextword[nextwordptr] = 0;
+  if(starty == 0) {
+    webpageptr = &webpage[(WEBPAGE_HEIGHT - 1) * WEBPAGE_WIDTH + x];
+    if(nextwordptr > 0) {
+      strcpy(webpageptr, nextword);
+    }
+    webpageptr[nextwordptr] = ' ';
+  }
+  if(c == ISO_nl) {
+    x = 0;
+    inc_y();
+  } else {
+    x += nextwordptr + 1;
+  }
+  nextwordptr = 0;  
+}
+/*-----------------------------------------------------------------------------------*/
+static void *
+add_pagewidget(char *text, unsigned char type,
+	       unsigned char border)
+{  
+  static unsigned char len, maxwidth;
+  static unsigned char *webpageptr;
+  static void *dataptr;
+  register struct ctk_widget *lptr;
+
+  len = strlen(text);
+
+  if(len + border == 0) {
+    return NULL;
+  }
+  
+  maxwidth = WEBPAGE_WIDTH - (1 + 2 * border);
+  
+  /* If the text of the link is too long so that it does not fit into
+     the width of the current window, counting from the current x
+     coordinate, we first try to jump to the next line. */
+  if(len + x > maxwidth) {
+    output_word(ISO_nl);
+  }
+
+  /* If the text of the link still is too long, we just chop it off!
+     XXX: this is not really the right thing to do, we should probably
+     either make the link a multiline button, or add multiple
+     buttons. But this will do for now. */
+  if(len > maxwidth) {
+    text[maxwidth] = 0;
+    len = maxwidth;
+  }
+
+  dataptr = NULL;
+  
+  if(starty == 0) {
+    webpageptr = &webpage[(WEBPAGE_HEIGHT - 1) * WEBPAGE_WIDTH + x];
+    /* To save memory, we'll copy the widget text to the web page
+       drawing area and reference it from there. */
+    webpageptr[0] = 0;
+    webpageptr += border;
+    strncpy(webpageptr, text, len);
+    webpageptr[len] = 0;
+    webpageptr[len + border] = ' ';
+    if(pagewidgetptr < MAX_NUMPAGEWIDGETS) {
+      dataptr = &pagewidgetattribs[pagewidgetptr];
+      lptr = &pagewidgets[pagewidgetptr];
+      
+      switch(type) {
+      case CTK_WIDGET_HYPERLINK:
+	CTK_HYPERLINK_NEW((struct ctk_hyperlink *)lptr, x,
+			  WEBPAGE_HEIGHT + 2, len,
+			  webpageptr, dataptr);
+	break;
+      case CTK_WIDGET_BUTTON:
+	CTK_BUTTON_NEW((struct ctk_button *)lptr, x,
+		       WEBPAGE_HEIGHT + 2, len,
+		       webpageptr);
+	((struct formattribs *)dataptr)->inputvalue = webpageptr;
+	break;
+      case CTK_WIDGET_TEXTENTRY:
+	CTK_TEXTENTRY_NEW((struct ctk_textentry *)lptr,
+			  x, WEBPAGE_HEIGHT + 2, len, 1,
+			  webpageptr, len);
+	((struct formattribs *)dataptr)->inputvalue = webpageptr;
+	break;	
+      }
+      CTK_WIDGET_ADD(&mainwindow, lptr);
+
+      ++pagewidgetptr;
+    }
+  }
+  /* Increase the x coordinate with the length of the link text plus
+     the extra space behind it and the CTK button markers. */
+  x += len + 1 + 2 * border;
+
+  if(x >= WEBPAGE_WIDTH) {
+    inc_y();
+    x = 0;
+  }
+
+  return dataptr;
+}
+/*-----------------------------------------------------------------------------------*/
+/* htmlparser_link:
+ *
+ * Callback function. Will be called when the HTML parser has parsed a
+ * link. We will put a CTK hyperlink widget at the appropriate position
+ * in the window.
+ */
+void
+htmlparser_link(char *text, char *url)
+{
+  static unsigned char *linkurlptr;
+
+  linkurlptr = add_pagewidget(text, CTK_WIDGET_HYPERLINK, 0);
+  if(linkurlptr != NULL &&
+     strlen(url) < WWW_CONF_MAX_URLLEN) {
+    strcpy(linkurlptr, url);
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+/* htmlparser_char():
+ *
+ * Callback function. Called by the HTML parser module for every
+ * printable character in the HTML file.
+ */
+void
+htmlparser_char(char c)
+{
+  if(c == ' ' ||
+     c == ISO_nl) {
+    output_word(c);
+  } else if(c != 0) {    
+    nextword[nextwordptr] = c;
+    if(nextwordptr < WEBPAGE_WIDTH) {
+      ++nextwordptr;
+    }
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+#if WWW_CONF_RENDERSTATE
+void
+htmlparser_renderstate(unsigned char s)
+{
+  if((s & HTMLPARSER_RENDERSTATE_STATUSMASK) ==
+     HTMLPARSER_RENDERSTATE_BEGIN) {
+    renderstate |= s & ~HTMLPARSER_RENDERSTATE_STATUSMASK;
+  } else {
+    renderstate &= ~(s & ~HTMLPARSER_RENDERSTATE_STATUSMASK);
+  }
+}
+#endif /* WWW_CONF_RENDERSTATE */
+
+#if WWW_CONF_FORMS
+/*-----------------------------------------------------------------------------------*/
+void
+htmlparser_submitbutton(char *text, char *name,
+			char *formname, char *formaction)
+{
+  register struct formattribs *form;
+  form = add_pagewidget(text, CTK_WIDGET_BUTTON, 1);
+  if(form != NULL) {
+    strncpy(form->formaction, formaction, WWW_CONF_MAX_FORMACTIONLEN);
+    strncpy(form->formname, formname, WWW_CONF_MAX_FORMNAMELEN);
+    strncpy(form->inputname, name, WWW_CONF_MAX_INPUTNAMELEN);
+    form->inputtype = FORMINPUTTYPE_SUBMITBUTTON;
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+void
+htmlparser_inputfield(char *text, char *name,
+		      char *formname, char *formaction)
+{
+  register struct formattribs *form;
+  
+  form = add_pagewidget(text, CTK_WIDGET_TEXTENTRY, 1);
+  if(form != NULL) {
+    strncpy(form->formaction, formaction, WWW_CONF_MAX_FORMACTIONLEN);
+    strncpy(form->formname, formname, WWW_CONF_MAX_FORMNAMELEN);
+    strncpy(form->inputname, name, WWW_CONF_MAX_INPUTNAMELEN);
+    form->inputtype = FORMINPUTTYPE_INPUTFIELD;
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+formsubmit(struct formattribs *attribs)
+{
+  unsigned char i, j;
+  char *urlptr, *valueptr;
+  struct formattribs *faptr;
+
+  urlptr = &tmpurl[0];
+
+  strncpy(urlptr, attribs->formaction, WWW_CONF_MAX_URLLEN);
+  tmpurl[WWW_CONF_MAX_URLLEN] = 0;
+  urlptr += strlen(urlptr);
+  *urlptr = ISO_questionmark;
+  ++urlptr;
+  
+  
+  /* Construct an URL by finding all input field forms with the same
+     formname as the current submit button, and add the submit button
+     URL stuff as well. */
+  for(i = 0; i < pagewidgetptr; ++i) {
+    if(urlptr - &tmpurl[0] >= WWW_CONF_MAX_URLLEN) {
+      break;
+    }
+
+    faptr = &pagewidgetattribs[i].form;
+    
+    if(strcmp(attribs->formaction, faptr->formaction) == 0 &&
+       strcmp(attribs->formname, faptr->formname) == 0 &&
+       (faptr->inputtype == FORMINPUTTYPE_INPUTFIELD ||
+	faptr == attribs)) {
+
+      /* Copy the name of the input field into the URL and append a
+	 questionmark. */
+      strncpy(urlptr, faptr->inputname, WWW_CONF_MAX_URLLEN - strlen(tmpurl));
+      tmpurl[WWW_CONF_MAX_URLLEN] = 0;
+      urlptr += strlen(urlptr);
+      *urlptr = ISO_eq;
+      ++urlptr;
+
+      /* Convert and copy the contents of the input field to the URL
+	 and append an ampersand. */
+      valueptr = pagewidgets[i].widget.textentry.text;
+      petsciiconv_toascii(valueptr, WWW_CONF_MAX_INPUTVALUELEN);
+      for(j = 0; j < WWW_CONF_MAX_INPUTVALUELEN; ++j) {
+	if(urlptr - &tmpurl[0] >= WWW_CONF_MAX_URLLEN) {
+	  break;
+	}
+	*urlptr = *valueptr;
+	if(*urlptr == ISO_space) {
+	  *urlptr = ISO_plus;
+	}
+	if(*urlptr == 0) {
+	  break;
+	}
+	++urlptr;
+	++valueptr;
+      }
+      
+      *urlptr = ISO_ampersand;
+      ++urlptr;
+    }
+  }
+  --urlptr;
+  *urlptr = 0;
+  /*  PRINTF(("formsubmit: URL '%s'\n", tmpurl));*/
+  open_link(tmpurl);
+}
+/*-----------------------------------------------------------------------------------*/
+#endif /* WWW_CONF_FORMS */
diff --git a/contiki/apps/www.h b/contiki/apps/www.h
new file mode 100644
index 0000000..87a4a34
--- /dev/null
+++ b/contiki/apps/www.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment for the C64.
+ *
+ * $Id: www.h,v 1.1 2003/03/19 14:13:33 adamdunkels Exp $
+ *
+ */
+#ifndef __WWW_H__
+#define __WWW_H__
+
+void www_init(void);
+
+#endif /* __WWW_H__ */
diff --git a/contiki/ctk/ctk-conf.h b/contiki/ctk/ctk-conf.h
new file mode 100644
index 0000000..5c5c0dc
--- /dev/null
+++ b/contiki/ctk/ctk-conf.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the "ctk" console GUI toolkit for cc65
+ *
+ * $Id: ctk-conf.h,v 1.1 2003/03/19 14:13:33 adamdunkels Exp $
+ *
+ */
+
+#ifndef __CTK_CONF_H__
+#define __CTK_CONF_H__
+
+/*
+ * This file is used for setting various compile time settings for the
+ * CTK GUI toolkit.
+*/
+
+/* Defines which key that is to be used for activating the menus */
+#define CTK_CONF_MENU_KEY             CH_F1
+
+/* Defines which key that is to be used for switching the frontmost
+   window.  */
+#define CTK_CONF_WINDOWSWITCH_KEY     CH_F3
+
+/* Toggles support for desktop icons. */
+#define CTK_CONF_ICONS                0 /* 107 bytes */
+
+/* Toggles support for movable windows. */
+#define CTK_CONF_WINDOWMOVE           0 /* 333 bytes */
+
+/* Toggles support for closable windows. */
+#define CTK_CONF_WINDOWCLOSE          0 /* 14 bytes */
+
+/* Toggles support for multiline text entry editing. */
+#define CTK_CONF_TEXTENTRY_MULTILINE  0 /* 118 bytes */
+
+/* Toggles support for menus. */
+#define CTK_CONF_MENUS                0 /* 1384 bytes */
+
+/* Defines the default width of a menu. */
+#define CTK_CONF_MENUWIDTH            16
+/* The maximum number of menu items in each menu. */
+#define CTK_CONF_MAXMENUITEMS         10
+
+#endif /* __CTK_CONF_H__ */
diff --git a/contiki/ctk/ctk-draw.h b/contiki/ctk/ctk-draw.h
new file mode 100644
index 0000000..8549e23
--- /dev/null
+++ b/contiki/ctk/ctk-draw.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the "ctk" console GUI toolkit for cc65
+ *
+ * $Id: ctk-draw.h,v 1.1 2003/03/19 14:13:34 adamdunkels Exp $
+ *
+ */
+
+#ifndef __CTK_DRAW_H__
+#define __CTK_DRAW_H__
+
+#include "ctk.h"
+
+#include "ctk-arch.h"
+
+
+void ctk_draw_init(void);
+
+void ctk_draw_clear(unsigned char clipy1, unsigned char clipy2);
+void ctk_draw_clear_window(struct ctk_window *window,
+			   unsigned char focus,
+			   unsigned char clipy1, unsigned char clipy2);
+
+void ctk_draw_widget(struct ctk_widget *w,
+		     unsigned char focus,
+		     unsigned char clipy1,
+		     unsigned char clipy2);
+
+void ctk_draw_window(struct ctk_window *window,
+		     unsigned char focus,
+		     unsigned char clipy1,
+		     unsigned char clipy2);
+
+void ctk_draw_dialog(struct ctk_window *dialog);
+
+void ctk_draw_menus(struct ctk_menus *menus);
+
+
+
+/* Returns width and height of screen. */
+unsigned char ctk_draw_width(void);
+unsigned char ctk_draw_height(void);
+
+
+#endif /* __CTK_DRAW_H__ */
diff --git a/contiki/ctk/ctk.c b/contiki/ctk/ctk.c
new file mode 100644
index 0000000..2c70085
--- /dev/null
+++ b/contiki/ctk/ctk.c
@@ -0,0 +1,1120 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the "ctk" console GUI toolkit for cc65
+ *
+ * $Id: ctk.c,v 1.1 2003/03/19 14:13:34 adamdunkels Exp $
+ *
+ */
+
+#include "ek.h"
+#include "dispatcher.h"
+#include "ctk.h"
+#include "ctk-draw.h"
+#include "ctk-conf.h"
+
+static unsigned char height, width;
+
+static unsigned char mode;
+
+static struct ctk_window desktop_window;
+static struct ctk_window *windows;
+static struct ctk_window *dialog;
+
+#if CTK_CONF_MENUS
+static struct ctk_menus menus;
+static struct ctk_menu *lastmenu;
+static struct ctk_menu desktopmenu;
+#endif /* CTK_CONF_MENUS */
+
+#ifndef NULL
+#define NULL (void *)0
+#endif /* NULL */
+
+
+#define REDRAW_NONE         0
+#define REDRAW_ALL          1
+#define REDRAW_FOCUS        2
+#define REDRAW_WIDGETS      4
+#define REDRAW_MENUS        8
+#define REDRAW_MENUPART     16
+
+#define MAX_REDRAWWIDGETS 4
+static unsigned char redraw;
+static struct ctk_widget *redraw_widgets[MAX_REDRAWWIDGETS];
+static unsigned char redraw_widgetptr;
+static unsigned char maxnitems;
+
+static unsigned char iconx, icony;
+#define ICONX_START  (width - 5)
+#define ICONY_START  0
+#define ICONX_DELTA  -8
+#define ICONY_DELTA  5
+#define ICONY_MAX    (height - 4)
+
+static void idle(void);
+static void sighandler(ek_signal_t s, ek_data_t data);
+static struct dispatcher_proc p =
+  {DISPATCHER_PROC("CTK Contiki GUI", idle, sighandler, NULL)};
+static ek_id_t ctkid;
+
+
+ek_signal_t ctk_signal_keypress,
+  ctk_signal_timer,
+  ctk_signal_button_activate,
+  ctk_signal_button_hover,
+  ctk_signal_hyperlink_activate,
+  ctk_signal_hyperlink_hover,
+  ctk_signal_menu_activate,
+  ctk_signal_window_close;
+		       
+
+static unsigned short screensaver_timer;
+#define SCREENSAVER_TIMEOUT (5*60)
+
+#if CTK_CONF_MENUS
+/*-----------------------------------------------------------------------------------*/
+/* make_desktopmenu(void)
+ *
+ * Creates the leftmost menu, "Desktop". Since the desktop menu
+ * contains the list of all open windows, this function will be called
+ * whenever a window is opened or closed.
+ */
+static void
+make_desktopmenu(void)
+{
+  struct ctk_window *w;
+  
+  desktopmenu.nitems = 0;
+  
+  if(windows == NULL) {
+    ctk_menuitem_add(&desktopmenu, "(No windows)");
+  } else {
+    for(w = windows; w != NULL; w = w->next) {
+      ctk_menuitem_add(&desktopmenu, w->title);
+    }
+  }
+}
+#endif /* CTK_CONF_MENUS */
+/*-----------------------------------------------------------------------------------*/
+/* ctk_init(void)
+ *
+ * Initializes CTK. Must be called before any other CTK function.
+ */
+void
+ctk_init(void)
+{
+  ctkid = dispatcher_start(&p);
+  
+  windows = NULL;
+  dialog = NULL;
+
+#if CTK_CONF_MENUS 
+  ctk_menu_new(&desktopmenu, "Desktop");
+  make_desktopmenu();
+  menus.menus = menus.desktopmenu = &desktopmenu;
+#endif /* CTK_CONF_MENUS */
+
+  ctk_draw_init();
+
+  height = ctk_draw_height();
+  width = ctk_draw_width();
+
+  desktop_window.active = NULL;
+
+
+  ctk_signal_keypress = dispatcher_sigalloc();
+  ctk_signal_timer = dispatcher_sigalloc();
+  ctk_signal_button_activate = dispatcher_sigalloc();
+  ctk_signal_button_hover = dispatcher_sigalloc();
+  ctk_signal_hyperlink_activate = dispatcher_sigalloc();
+  ctk_signal_hyperlink_hover = dispatcher_sigalloc();
+  ctk_signal_menu_activate = dispatcher_sigalloc();
+  ctk_signal_window_close = dispatcher_sigalloc();
+  
+  dispatcher_listen(ctk_signal_timer);
+  dispatcher_timer(ctk_signal_timer, NULL, CLK_TCK);
+
+  mode = CTK_MODE_NORMAL;
+
+  iconx = ICONX_START;
+  icony = ICONY_START;
+  
+}
+/*-----------------------------------------------------------------------------------*/
+/* void ctk_mode_set()
+ */
+void
+ctk_mode_set(unsigned char m) {
+  mode = m;
+}
+/*-----------------------------------------------------------------------------------*/
+unsigned char
+ctk_mode_get(void) {
+  return mode;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_icon_add(register struct ctk_widget *icon,
+	     ek_id_t id)
+{
+#if CTK_CONF_ICONS
+  icon->x = iconx;
+  icon->y = icony;
+  icon->widget.icon.owner = id;
+
+  icony += ICONY_DELTA;
+  if(icony >= ICONY_MAX) {
+    icony = ICONY_START;
+    iconx += ICONX_DELTA;
+  }
+  
+  ctk_widget_add(&desktop_window, icon);
+#endif /* CTK_CONF_ICONS */
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_dialog_open(struct ctk_window *d)
+{
+  dialog = d;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_dialog_close(void)
+{
+  dialog = NULL;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_window_open(register struct ctk_window *w)
+{
+  struct ctk_window *w2;
+  
+  /* Check if already open. */
+  for(w2 = windows; w2 != w && w2 != NULL; w2 = w2->next);
+  if(w2 == NULL) {
+   /* Not open, so we add it at the head of the list of open
+       windows. */
+    w->next = windows;
+    if(windows != NULL) {
+      windows->prev = w;
+    }
+    windows = w;
+    w->prev = NULL;
+  } else {
+    /* Window already open, so we move it to the front of the windows
+       list. */
+    if(w != windows) {
+      if(w->next != NULL) {
+	w->next->prev = w->prev;
+      }
+      if(w->prev != NULL) {
+	w->prev->next = w->next;
+      }
+      w->next = windows;
+      windows->prev = w;
+      windows = w;
+      w->prev = NULL;
+    }
+  }
+  
+#if CTK_CONF_MENUS
+  /* Recreate the Desktop menu's window entries.*/
+  make_desktopmenu();
+#endif /* CTK_CONF_MENUS */
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_window_close(struct ctk_window *w)
+{
+  struct ctk_window *w2;
+
+  if(w == NULL) {
+    return;
+  }
+  
+  /* Check if the window to be closed is the first window on the
+     list. */
+  if(w == windows) {
+    windows = w->next;
+    if(windows != NULL) {
+      windows->prev = NULL;
+    }
+    w->next = w->prev = NULL;
+  } else {
+    /* Otherwise we step through the list until we find the window
+       before the one to be closed. We then redirect its ->next
+       pointer and its ->next->prev. */
+    for(w2 = windows; w2->next != w; w2 = w2->next);
+
+    if(w->next != NULL) {
+      w->next->prev = w->prev;
+    }
+    w2->next = w->next;
+    
+    w->next = w->prev = NULL;
+  }
+  
+#if CTK_CONF_MENUS
+  /* Recreate the Desktop menu's window entries.*/
+  make_desktopmenu();
+#endif /* CTK_CONF_MENUS */
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+make_windowbuttons(register struct ctk_window *window)
+{
+#if CTK_CONF_WINDOWMOVE
+  CTK_BUTTON_NEW(&window->titlebutton, 0, -1, window->titlelen, window->title);
+#else
+  CTK_LABEL_NEW(&window->titlebutton, 0, -1, window->titlelen, 1, window->title);
+#endif /* CTK_CONF_WINDOWMOVE */
+  CTK_WIDGET_ADD(window, &window->titlebutton);
+
+
+#if CTK_CONF_WINDOWCLOSE
+  CTK_BUTTON_NEW(&window->closebutton, window->w - 3, -1, 1, "x");
+#else
+  CTK_LABEL_NEW(&window->closebutton, window->w - 4, -1, 3, 1, "   ");
+#endif /* CTK_CONF_WINDOWCLOSE */  
+  CTK_WIDGET_ADD(window, &window->closebutton);
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_window_clear(struct ctk_window *window)
+{
+  window->active = window->inactive = window->focused = NULL;
+  
+  make_windowbuttons(window);
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_menu_add(struct ctk_menu *menu)
+{
+#if CTK_CONF_MENUS
+  struct ctk_menu *m;
+
+  if(lastmenu == NULL) {
+    lastmenu = menu;
+  }
+    
+  for(m = menus.menus; m->next != NULL; m = m->next) {
+    if(m == menu) {
+      return;
+    }
+  }
+  m->next = menu;
+  menu->next = NULL;
+#endif /* CTK_CONF_MENUS */
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_menu_remove(struct ctk_menu *menu)
+{
+#if CTK_CONF_MENUS
+  struct ctk_menu *m;
+
+  for(m = menus.menus; m->next != NULL; m = m->next) {
+    if(m->next == menu) {
+      m->next = menu->next;
+      return;
+    }
+  }
+#endif /* CTK_CONF_MENUS */
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+do_redraw_all(unsigned char clipy1, unsigned char clipy2)
+{
+  struct ctk_window *w;
+  struct ctk_widget *widget;
+
+  if(mode != CTK_MODE_NORMAL &&
+     mode != CTK_MODE_WINDOWMOVE) {
+    return;
+  }
+  
+  ctk_draw_clear(clipy1, clipy2);
+
+  /* Draw widgets in root window */
+  for(widget = desktop_window.active;
+      widget != NULL; widget = widget->next) {
+    ctk_draw_widget(widget, 0, clipy1, clipy2);
+  }
+  
+  /* Draw windows */
+  if(windows != NULL) {
+    /* Find the last window.*/
+    for(w = windows; w->next != NULL; w = w->next);
+
+    /* Draw the windows from back to front. */
+    for(; w != windows; w = w->prev) {
+      ctk_draw_clear_window(w, 0, clipy1, clipy2);
+      ctk_draw_window(w, 0, clipy1, clipy2);
+    }
+    /* Draw focused window */
+    ctk_draw_clear_window(windows, CTK_FOCUS_WINDOW, clipy1, clipy2);
+    ctk_draw_window(windows, CTK_FOCUS_WINDOW, clipy1, clipy2);
+  }
+
+  /* Draw dialog (if any) */
+  if(dialog != NULL) {
+    ctk_draw_dialog(dialog);
+  }
+
+#if CTK_CONF_MENUS
+  ctk_draw_menus(&menus);
+#endif /* CTK_CONF_MENUS */
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_redraw(void)
+{
+  if(DISPATCHER_CURRENT() == ctkid) {
+    if(mode == CTK_MODE_NORMAL ||
+       mode == CTK_MODE_WINDOWMOVE) {
+      do_redraw_all(1, height);
+    }
+  } else {
+    redraw |= REDRAW_ALL;
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_window_redraw(struct ctk_window *w)
+{
+  /* Only redraw the window if it is a dialog or if it is the foremost
+     window. */
+  if(mode != CTK_MODE_NORMAL) {
+    return;
+  }
+  
+  if(w == dialog) {
+    ctk_draw_dialog(w);
+  } else if(dialog == NULL &&
+#if CTK_CONF_MENUS
+	    menus.open == NULL &&
+#endif /* CTK_CONF_MENUS */		    
+	    windows == w) {
+    ctk_draw_window(w, CTK_FOCUS_WINDOW,
+		    0, height);
+  }  
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+window_new(register struct ctk_window *window,
+	   unsigned char w, unsigned char h,
+	   char *title)
+{
+  window->x = window->y = 0;
+  window->w = w;
+  window->h = h;
+  window->title = title;
+  if(title != NULL) {
+    window->titlelen = strlen(title);
+  } else {
+    window->titlelen = 0;
+  }
+  window->next = window->prev = NULL;
+  window->owner = DISPATCHER_CURRENT();
+  window->active = window->inactive = window->focused = NULL;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_window_new(struct ctk_window *window,
+	       unsigned char w, unsigned char h,
+	       char *title)
+{
+  window_new(window, w, h, title);
+
+  make_windowbuttons(window);  
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_dialog_new(register struct ctk_window *window,
+	       unsigned char w, unsigned char h)
+{
+  window_new(window, w, h, NULL);
+
+  window->x = (width - w) / 2;
+  window->y = (height - h - 1) / 2; 
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_menu_new(register struct ctk_menu *menu,
+	     char *title)
+{
+#if CTK_CONF_MENUS
+  menu->next = NULL;
+  menu->title = title;
+  menu->titlelen = strlen(title);
+  menu->active = 0;
+  menu->nitems = 0;
+#endif /* CTK_CONF_MENUS */
+}
+/*-----------------------------------------------------------------------------------*/
+unsigned char
+ctk_menuitem_add(register struct ctk_menu *menu,
+		 char *name)
+{
+#if CTK_CONF_MENUS
+  if(menu->nitems == CTK_CONF_MAXMENUITEMS) {
+    return 0;
+  }
+  menu->items[menu->nitems].title = name;
+  menu->items[menu->nitems].titlelen = strlen(name); 
+  return menu->nitems++;
+#else
+  return 0;
+#endif /* CTK_CONF_MENUS */
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+add_redrawwidget(struct ctk_widget *w)
+{
+  static unsigned char i;
+  
+  if(redraw_widgetptr == MAX_REDRAWWIDGETS) {
+    redraw |= REDRAW_FOCUS;
+  } else {
+    redraw |= REDRAW_WIDGETS;
+    /* Check if it is in the queue already. If so, we don't add it
+       again. */
+    for(i = 0; i < redraw_widgetptr; ++i) {
+      if(redraw_widgets[i] == w) {
+	return;
+      }
+    }
+    redraw_widgets[redraw_widgetptr++] = w;
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_widget_redraw(struct ctk_widget *widget)
+{
+  struct ctk_window *window;
+
+  if(mode != CTK_MODE_NORMAL) {
+    return;
+  }
+
+  /* If this function isn't called by CTK itself, we only queue the
+     redraw request. */
+  if(DISPATCHER_CURRENT() != ctkid) {
+    redraw |= REDRAW_WIDGETS;
+    add_redrawwidget(widget);
+  } else {
+    
+    /* Only redraw widgets that are in the foremost window. If we
+       would allow redrawing widgets in non-focused windows, we would
+       have to redraw all the windows that cover the non-focused
+       window as well, which would lead to flickering.
+
+       Also, we avoid drawing any widgets when the menus are active.
+    */
+    
+#if CTK_CONF_MENUS
+    if(menus.open == NULL)
+#endif /* CTK_CONF_MENUS */
+      {
+	window = widget->window;
+	if(window == dialog) {
+	  ctk_draw_widget(widget, CTK_FOCUS_DIALOG, 0, height);
+	} else if(window == windows) {
+	  ctk_draw_widget(widget, CTK_FOCUS_WINDOW, 0, height);	      
+	} else if(window == &desktop_window) {
+	  ctk_draw_widget(widget, 0, 0, height);
+	}
+      }
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ctk_widget_add(register struct ctk_window *window,
+	       register struct ctk_widget *widget)
+{  
+  if(widget->type == CTK_WIDGET_LABEL ||
+     widget->type == CTK_WIDGET_SEPARATOR) {
+    widget->next = window->inactive;
+    window->inactive = widget;
+    widget->window = window;
+  } else {
+    widget->next = window->active;
+    window->active = widget;
+    widget->window = window;
+    if(window->focused == NULL) {
+      window->focused = widget;
+    }
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+#define UP 0
+#define DOWN 1
+#define LEFT 2
+#define RIGHT 3
+static void
+switch_focus_widget(unsigned char direction)
+{
+  register struct ctk_window *window;
+  register struct ctk_widget *focus;
+  struct ctk_widget *widget;
+  
+  
+  if(dialog != NULL) {
+    window = dialog;
+  } else {
+    window = windows;
+  }
+
+  /* If there are no windows open, we move focus around between the
+     icons on the root window instead. */
+  if(window == NULL) {
+    window = &desktop_window;
+  }
+ 
+  focus = window->focused;
+  add_redrawwidget(focus);
+  
+  if((direction & 1) == 0) {
+    /* Move focus "up" */
+    focus = focus->next;
+  } else {
+    /* Move focus "down" */
+    for(widget = window->active;
+	widget != NULL; widget = widget->next) {
+	if(widget->next == focus) {
+	  break;
+	}      
+    }    
+    focus = widget;
+    if(focus == NULL) {
+      if(window->active != NULL) {	
+	for(focus = window->active;
+	    focus->next != NULL; focus = focus->next);	
+      }
+    }
+  }
+  if(focus == NULL) {
+    focus = window->active;
+  }
+  
+  if(focus != window->focused) {
+    window->focused = focus;
+    /* The operation changed the focus, so we emit a "hover" signal
+       for those widgets that support it. */
+    
+    if(window->focused->type == CTK_WIDGET_HYPERLINK) {    
+      dispatcher_emit(ctk_signal_hyperlink_hover, window->focused,
+		      window->owner);
+    } else if(window->focused->type == CTK_WIDGET_BUTTON) {    
+      dispatcher_emit(ctk_signal_button_hover, window->focused,
+		      window->owner);      
+    } 
+    
+    add_redrawwidget(window->focused);
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+#if CTK_CONF_MENUS
+static void 
+switch_open_menu(unsigned char rightleft)
+{
+  struct ctk_menu *menu;
+  
+  if(rightleft == 0) {
+    /* Move right */
+    for(menu = menus.menus; menu != NULL; menu = menu->next) {
+      if(menu->next == menus.open) {
+	break;
+      }
+    }
+    lastmenu = menus.open;
+    menus.open = menu;
+    if(menus.open == NULL) {
+      for(menu = menus.menus;
+	  menu->next != NULL; menu = menu->next);
+      menus.open = menu;
+    }
+  } else {
+    /* Move to left */
+    lastmenu = menus.open;
+    menus.open = menus.open->next;
+    if(menus.open == NULL) {
+      menus.open = menus.menus;
+    }
+  }
+
+  menus.open->active = 0;  
+  /*  ctk_redraw();*/
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+switch_menu_item(unsigned char updown)
+{
+  register struct ctk_menu *m;
+
+  m = menus.open;
+  
+  if(updown == 0) {
+    /* Move up */
+    if(m->active == 0) {
+      m->active = m->nitems - 1;
+    } else {
+      --m->active;
+      if(m->items[m->active].title[0] == '-') {
+	--m->active;
+      }
+    }
+  } else {
+    /* Move down */
+    if(m->active >= m->nitems - 1) {
+      m->active = 0;
+    } else {      
+      ++m->active;
+      if(m->items[m->active].title[0] == '-') {
+	++m->active;
+      }
+    }
+  }
+  
+}
+#endif /* CTK_CONF_MENUS */
+/*-----------------------------------------------------------------------------------*/
+static unsigned char 
+activate(register struct ctk_widget *w)
+{
+  static unsigned char len;
+  
+  if(w->type == CTK_WIDGET_BUTTON) {
+    if(w == (struct ctk_widget *)&windows->closebutton) {
+#if CTK_CONF_WINDOWCLOSE
+      /*dispatcher_emit(ctk_signal_window_close, windows, w->window->owner);*/
+      ctk_window_close(windows);
+      return REDRAW_ALL;
+#endif /* CTK_CONF_WINDOWCLOSE */
+    } else if(w == (struct ctk_widget *)&windows->titlebutton) {
+#if CTK_CONF_WINDOWCLOSE
+      mode = CTK_MODE_WINDOWMOVE;
+#endif /* CTK_CONF_WINDOWCLOSE */
+    } else {
+      dispatcher_emit(ctk_signal_button_activate, w,
+		      w->window->owner);
+    }
+#if CTK_CONF_ICONS
+  } else if(w->type == CTK_WIDGET_ICON) {
+    dispatcher_emit(ctk_signal_button_activate, w,
+		    w->widget.icon.owner);
+#endif /* CTK_CONF_ICONS */
+  } else if(w->type == CTK_WIDGET_HYPERLINK) {    
+    dispatcher_emit(ctk_signal_hyperlink_activate, w,
+		    DISPATCHER_BROADCAST);
+  } else if(w->type == CTK_WIDGET_TEXTENTRY) {
+    if(w->widget.textentry.state == CTK_TEXTENTRY_NORMAL) {      
+      w->widget.textentry.state = CTK_TEXTENTRY_EDIT;
+      len = strlen(w->widget.textentry.text);
+      if(w->widget.textentry.xpos > len) {
+	w->widget.textentry.xpos = len;
+      }
+    } else if(w->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
+      w->widget.textentry.state = CTK_TEXTENTRY_NORMAL;
+    }
+    add_redrawwidget(w);
+    return REDRAW_WIDGETS;
+  }
+  return REDRAW_NONE;
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+textentry_input(ctk_arch_key_t c,
+		register struct ctk_textentry *t)
+{
+  static char *cptr, *cptr2;
+  static unsigned char len, txpos, typos, tlen;
+
+  txpos = t->xpos;
+  typos = t->ypos;
+  tlen = t->len;
+
+  cptr = &t->text[txpos + typos * tlen];
+      
+  switch(c) {
+  case CH_CURS_LEFT:
+    if(txpos > 0) {
+      --txpos;
+    }
+    break;
+    
+  case CH_CURS_RIGHT:    
+    if(txpos < tlen &&
+       *cptr != 0) {
+      ++txpos;
+    }
+    break;
+
+  case CH_CURS_UP:
+#if CTK_CONF_TEXTENTRY_MULTILINE
+    if(t->h == 1) {
+      txpos = 0;
+    } else {
+      if(typos > 0) {
+	--typos;
+      } else {
+	t->state = CTK_TEXTENTRY_NORMAL;
+      }
+    }
+#else
+    txpos = 0;
+#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
+    break;
+    
+  case CH_CURS_DOWN:
+#if CTK_CONF_TEXTENTRY_MULTILINE
+    if(t->h == 1) {
+      txpos = strlen(t->text);
+    } else {
+      if(typos < t->h - 1) {
+	++typos;
+      } else {
+	t->state = CTK_TEXTENTRY_NORMAL;
+      }
+    }
+#else
+    txpos = strlen(t->text);
+#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
+    break;
+    
+  case CH_ENTER:
+#if CTK_CONF_TEXTENTRY_MULTILINE
+    if(t->h == 1) {
+      t->state = CTK_TEXTENTRY_NORMAL;
+    } else {
+      if(typos < t->h - 1) {
+	++typos;
+	txpos = 0;
+      } else {
+	t->state = CTK_TEXTENTRY_NORMAL;
+      }
+    }
+#else
+    t->state = CTK_TEXTENTRY_NORMAL;
+#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
+    break;
+    
+  default:
+    len = tlen - txpos - 1;
+    if(c == CH_DEL) {
+      if(txpos > 0 && len > 0) {
+	strncpy(cptr - 1, cptr,
+		len);
+	*(cptr + len - 1) = 0;
+	--txpos;
+      }
+    } else {
+      if(len > 0) {
+	cptr2 = cptr + len - 1;
+	while(cptr2 + 1 > cptr) {
+	  *(cptr2 + 1) = *cptr2;
+	  --cptr2;
+	}
+	
+	*cptr = c;
+	++txpos;
+      }
+    }
+    break;
+  }
+
+  t->xpos = txpos;
+  t->ypos = typos;
+}
+/*-----------------------------------------------------------------------------------*/
+#if CTK_CONF_MENUS
+static unsigned char
+menus_input(ctk_arch_key_t c)
+{
+  struct ctk_window *w;
+
+  if(menus.open->nitems > maxnitems) {
+    maxnitems = menus.open->nitems;
+  }
+
+  
+  switch(c) {
+  case CH_CURS_RIGHT:
+    switch_open_menu(1);
+	
+    return REDRAW_MENUPART;
+
+  case CH_CURS_DOWN:
+    switch_menu_item(1);
+    return REDRAW_MENUS;
+
+  case CH_CURS_LEFT:
+    switch_open_menu(0);
+    return REDRAW_MENUPART;
+
+  case CH_CURS_UP:
+    switch_menu_item(0);
+    return REDRAW_MENUS;
+    
+  case CH_ENTER:
+    lastmenu = menus.open;
+    if(menus.open == &desktopmenu) {
+      for(w = windows; w != NULL; w = w->next) {
+	if(w->title == desktopmenu.items[desktopmenu.active].title) {
+	  ctk_window_open(w);
+	  menus.open = NULL;
+	  return REDRAW_ALL;
+	}
+      }
+    } else {
+      dispatcher_emit(ctk_signal_menu_activate, menus.open,
+		      DISPATCHER_BROADCAST);
+    }
+    menus.open = NULL;
+    return REDRAW_MENUPART;
+
+  case CH_F1:
+    lastmenu = menus.open;
+    menus.open = NULL;
+    return REDRAW_MENUPART;
+  }
+}
+#endif /* CTK_CONF_MENUS */
+/*-----------------------------------------------------------------------------------*/
+static void
+idle(void)     
+{
+  static ctk_arch_key_t c;
+  static unsigned char i;  
+  register struct ctk_window *window;
+  register struct ctk_widget *widget;
+
+#if CTK_CONF_MENUS
+  if(menus.open != NULL) {
+    maxnitems = menus.open->nitems;
+  } else {
+    maxnitems = 0;
+  }
+#endif /* CTK_CONF_MENUS */
+  
+  if(mode == CTK_MODE_SCREENSAVER) {
+#ifdef CTK_SCREENSAVER_RUN
+    CTK_SCREENSAVER_RUN();
+#endif /* CTK_SCREENSAVER_RUN */
+    if(ctk_arch_keyavail()) {
+      mode = CTK_MODE_NORMAL;
+      ctk_draw_init();
+      ctk_redraw();
+    }
+  } else if(mode == CTK_MODE_NORMAL) {  
+    while(ctk_arch_keyavail()) {
+      
+      screensaver_timer = 0;
+      
+      c = ctk_arch_getkey();
+      
+      
+      if(dialog != NULL) {
+	window = dialog;
+      } else if(windows != NULL) {
+	window = windows;
+      } else {
+	window = &desktop_window;
+      }      
+      widget = window->focused;
+
+	  
+      if(widget != NULL &&
+	 widget->type == CTK_WIDGET_TEXTENTRY &&
+	 widget->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
+	textentry_input(c, (struct ctk_textentry *)widget);
+	add_redrawwidget(widget);
+#if CTK_CONF_MENUS
+      } else if(menus.open != NULL) {
+	redraw |= menus_input(c);
+#endif /* CTK_CONF_MENUS */
+      } else {      
+	switch(c) {
+	case CH_CURS_RIGHT:
+	  switch_focus_widget(RIGHT);
+	  break;
+	case CH_CURS_DOWN:
+	  switch_focus_widget(DOWN);
+	  break;
+	case CH_CURS_LEFT:
+	  switch_focus_widget(LEFT);
+	  break;
+	case CH_CURS_UP:
+	  switch_focus_widget(UP);
+	  break;
+	case CH_ENTER:
+	  redraw |= activate(widget);
+	  break;
+#if CTK_CONF_MENUS
+	case CTK_CONF_MENU_KEY:
+	  if(dialog == NULL) {
+	    if(lastmenu == NULL) {
+	      menus.open = menus.menus;
+	    } else {
+	      menus.open = lastmenu;
+	    }
+	    menus.open->active = 0;
+	    redraw |= REDRAW_MENUS;
+	  } 
+	  break;
+#endif /* CTK_CONF_MENUS */
+	case CTK_CONF_WINDOWSWITCH_KEY:
+	  if(windows != NULL) {
+	    for(window = windows; window->next != NULL;
+		window = window->next);
+	    ctk_window_open(window);
+	    ctk_redraw();
+	  }
+	  break;
+	default:
+	  if(widget->type == CTK_WIDGET_TEXTENTRY) {
+	    widget->widget.textentry.state = CTK_TEXTENTRY_EDIT;
+	    textentry_input(c, (struct ctk_textentry *)widget);
+	    add_redrawwidget(widget);
+	  } else {
+	    dispatcher_emit(ctk_signal_keypress, (void *)c,
+			    window->owner);
+	  }
+	  break;
+	}
+      }
+
+      if(redraw & REDRAW_WIDGETS) {
+	for(i = 0; i < redraw_widgetptr; ++i) {
+	  ctk_widget_redraw(redraw_widgets[i]);
+	}
+	redraw &= ~REDRAW_WIDGETS;
+	redraw_widgetptr = 0;
+      }
+    }
+    if(redraw & REDRAW_ALL) {
+      do_redraw_all(1, height);
+#if CTK_CONF_MENUS
+    } else if(redraw & REDRAW_MENUPART) {
+      do_redraw_all(1, maxnitems + 1);
+    } else if(redraw & REDRAW_MENUS) {
+      ctk_draw_menus(&menus);
+#endif /* CTK_CONF_MENUS */
+    } else if(redraw & REDRAW_FOCUS) {
+      if(dialog != NULL) {
+	ctk_window_redraw(dialog);
+      } else if(windows != NULL) {
+	ctk_window_redraw(windows);	
+      } else {
+	ctk_window_redraw(&desktop_window);	
+      }
+    } else if(redraw & REDRAW_WIDGETS) {
+      for(i = 0; i < redraw_widgetptr; ++i) {
+	ctk_widget_redraw(redraw_widgets[i]);
+      }
+    }    
+    redraw = 0;
+    redraw_widgetptr = 0;
+#if CTK_CONF_WINDOWMOVE
+  } else if(mode == CTK_MODE_WINDOWMOVE) {
+
+    redraw = 0;
+
+    window = windows;
+    
+    while(mode == CTK_MODE_WINDOWMOVE && ctk_arch_keyavail()) {
+    
+      screensaver_timer = 0;
+      
+      c = ctk_arch_getkey();
+      
+      switch(c) {
+      case CH_CURS_RIGHT:
+	++window->x;
+	if(window->x + window->w + 1 >= width) {
+	  --window->x;
+	}
+	redraw = REDRAW_ALL;
+	break;
+      case CH_CURS_LEFT:
+	if(window->x > 0) {
+	  --window->x;
+	}
+	redraw = REDRAW_ALL;
+	break;
+      case CH_CURS_DOWN:
+	++window->y;
+	if(window->y + window->h + 2 >= height) {
+	  --window->y;
+	}
+	redraw = REDRAW_ALL;
+	break;
+      case CH_CURS_UP:
+	if(window->y > 0) {
+	  --window->y;
+	}
+	redraw = REDRAW_ALL;
+	break;
+      case CH_ENTER:
+      case CH_ESC:
+	mode = CTK_MODE_NORMAL;
+	redraw = REDRAW_ALL;
+	break;
+      }
+    }
+    if(redraw & REDRAW_ALL) {
+      do_redraw_all(1, height);
+    }
+    redraw = 0;
+#endif /* CTK_CONF_WINDOWMOVE */
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+sighandler(ek_signal_t s, ek_data_t data)
+{
+  if(s == ctk_signal_timer) {
+    if(mode == CTK_MODE_NORMAL) {
+      ++screensaver_timer;
+      if(screensaver_timer == SCREENSAVER_TIMEOUT) {
+#ifdef CTK_SCREENSAVER_INIT
+	CTK_SCREENSAVER_INIT();
+#endif /* CTK_SCREENSAVER_INIT */
+	mode = CTK_MODE_SCREENSAVER;
+	screensaver_timer = 0;
+      }
+    }
+    dispatcher_timer(ctk_signal_timer, data, CLK_TCK);
+  }
+}
+/*-----------------------------------------------------------------------------------*/
diff --git a/contiki/ctk/ctk.h b/contiki/ctk/ctk.h
new file mode 100644
index 0000000..57779f6
--- /dev/null
+++ b/contiki/ctk/ctk.h
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the "ctk" console GUI toolkit for cc65
+ *
+ * $Id: ctk.h,v 1.1 2003/03/19 14:13:34 adamdunkels Exp $
+ *
+ */
+
+#ifndef __CTK_H__
+#define __CTK_H__
+
+#include "ctk-conf.h"
+#include "ek.h"
+
+/* Defintions for the CTK widget types. */
+#define CTK_WIDGET_SEPARATOR 1
+#define CTK_WIDGET_LABEL     2
+#define CTK_WIDGET_BUTTON    3
+#define CTK_WIDGET_HYPERLINK 4
+#define CTK_WIDGET_TEXTENTRY 5
+#define CTK_WIDGET_ICON      6
+
+struct ctk_widget;
+
+#define CTK_SEPARATOR(x, y, w) \
+ NULL, NULL, x, y, CTK_WIDGET_SEPARATOR, w
+struct ctk_separator {
+  struct ctk_widget *next;
+  struct ctk_window *window;
+  unsigned char x, y;
+  unsigned char type;
+  unsigned char w;
+};
+
+#define CTK_BUTTON(x, y, w, text) \
+ NULL, NULL, x, y, CTK_WIDGET_BUTTON, w, text
+struct ctk_button {
+  struct ctk_widget *next;
+  struct ctk_window *window;
+  unsigned char x, y;
+  unsigned char type;
+  unsigned char w;
+  char *text;
+};
+
+#define CTK_LABEL(x, y, w, h, text) \
+ NULL, NULL, x, y, CTK_WIDGET_LABEL, w, text, h
+struct ctk_label {
+  struct ctk_widget *next;
+  struct ctk_window *window;
+  unsigned char x, y;
+  unsigned char type;
+  unsigned char w;
+  char *text;
+  unsigned char h;
+};
+
+#define CTK_HYPERLINK(x, y, w, text, url) \
+ NULL, NULL, x, y, CTK_WIDGET_HYPERLINK, w, text, url
+struct ctk_hyperlink {
+  struct ctk_widget *next;
+  struct ctk_window *window;
+  unsigned char x, y;
+  unsigned char type;
+  unsigned char w;
+  char *text;
+  char *url;
+};
+
+/* Editing modes of the CTK textentry widget. */
+#define CTK_TEXTENTRY_NORMAL 0
+#define CTK_TEXTENTRY_EDIT   1
+
+
+#define CTK_TEXTENTRY(x, y, w, h, text, len) \
+  NULL, NULL, x, y, CTK_WIDGET_TEXTENTRY, w, text, h, len, \
+  CTK_TEXTENTRY_NORMAL, 0, 0
+struct ctk_textentry {
+  struct ctk_widget *next;
+  struct ctk_window *window;
+  unsigned char x, y;
+  unsigned char type;
+  unsigned char w;
+  char *text;
+  unsigned char h;
+  unsigned char len;
+  unsigned char state;
+  unsigned char xpos, ypos;
+};
+
+
+#define CTK_ICON(title, bitmap, textmap) \
+ NULL, NULL, 0, 0, CTK_WIDGET_ICON, 0, title, 0, 0, 0, bitmap, textmap
+struct ctk_icon {
+  struct ctk_widget *next;
+  struct ctk_window *window;
+  unsigned char x, y;
+  unsigned char type;
+  unsigned char w;
+  char *title;
+  ek_id_t owner;
+  unsigned char color, fcolor;
+  unsigned char *bitmap;
+  char *textmap;
+};
+
+#define CTK_BITMAP(x, y, w, h, bitmap) \
+  NULL, NULL, x, y, CTK_WIDGET_BITMAP, w, bitmap, h
+struct ctk_bitmap {
+  struct ctk_widget *next;
+  struct ctk_window *window;
+  unsigned char x, y;
+  unsigned char type;
+  unsigned char w;
+  unsigned char *bitmap;
+  unsigned char h;
+};
+
+
+
+/* The following ctk_widget_* structures are internal to CTK. */
+struct ctk_widget_button {
+  char *text;
+};
+
+struct ctk_widget_label {
+  char *text;
+  unsigned char h;
+};
+
+struct ctk_widget_hyperlink {
+  char *text;
+  char *url;
+};
+
+struct ctk_widget_textentry {
+  char *text;
+  unsigned char h;
+  unsigned char len;
+  unsigned char state;
+  unsigned char xpos, ypos;
+};
+
+struct ctk_widget_icon {
+  char *title;
+  ek_id_t owner;
+  unsigned char color, fcolor;
+  unsigned char *bitmap;
+  char *textmap;
+};
+
+struct ctk_widget_bitmap {
+  unsigned char *bitmap;
+  unsigned char h;
+};
+
+struct ctk_widget {
+  struct ctk_widget *next;
+  struct ctk_window *window;
+  unsigned char x, y;
+  unsigned char type;
+  unsigned char w;
+  union {
+    struct ctk_widget_label label;
+    struct ctk_widget_button button;
+    struct ctk_widget_hyperlink hyperlink;
+    struct ctk_widget_textentry textentry;
+    struct ctk_widget_icon icon;
+  } widget;
+};
+
+/* Definition of the CTK window structure. */
+struct ctk_window {
+  struct ctk_window *next, *prev;
+  ek_id_t owner;
+  
+#if CTK_CONF_WINDOWCLOSE
+  struct ctk_button closebutton;
+#else /* CTK_CONF_WINDOWCLOSE */
+  struct ctk_label closebutton;
+#endif /* CTK_CONF_WINDOWCLOSE */
+  
+#if CTK_CONF_WINDOWMOVE
+  struct ctk_button titlebutton;
+#else /* CTK_CONF_WINDOWMOVE */
+  struct ctk_label titlebutton;
+#endif /* CTK_CONF_WINDOWMOVE */
+
+  struct ctk_widget *inactive;
+  struct ctk_widget *active;
+  struct ctk_widget *focused;
+  unsigned char x, y;
+  unsigned char w, h;
+  char *title;
+  unsigned char titlelen;
+};
+
+struct ctk_menuitem {
+  char *title;
+  unsigned char titlelen;
+};
+
+struct ctk_menu {
+  struct ctk_menu *next;
+  char *title;
+  unsigned char titlelen;
+  ek_id_t owner;
+  unsigned char active;
+  unsigned char nitems;
+  struct ctk_menuitem items[CTK_CONF_MAXMENUITEMS];
+};
+
+struct ctk_menus {
+  struct ctk_menu *menus;
+  struct ctk_menu *open;
+  struct ctk_menu *desktopmenu;
+};
+
+/* Global CTK modes. */
+#define CTK_MODE_NORMAL      0
+#define CTK_MODE_WINDOWMOVE  1
+#define CTK_MODE_SCREENSAVER 2
+#define CTK_MODE_EXTERNAL    3
+
+/* General ctk functions. */
+void ctk_init(void);
+void ctk_mode_set(unsigned char mode);
+unsigned char ctk_mode_get(void);
+void ctk_redraw(void);
+
+/* Functions for manipulating windows. */
+void ctk_window_new(struct ctk_window *window,
+		    unsigned char w, unsigned char h,
+		    char *title);
+void ctk_window_clear(struct ctk_window *w);
+void ctk_window_open(struct ctk_window *w);
+#define ctk_window_move(w,xpos,ypos) do {(w)->x=xpos; (w)->y=ypos;}while(0)
+void ctk_window_close(struct ctk_window *w);
+void ctk_window_redraw(struct ctk_window *w);
+#define ctk_window_isopen(w) ((w)->next != NULL)
+
+
+/* Functions for manipulating dialogs. */
+void ctk_dialog_new(struct ctk_window *window,
+		    unsigned char w, unsigned char h);
+void ctk_dialog_open(struct ctk_window *d);
+void ctk_dialog_close(void);
+
+/* Functions for manipulating menus. */
+void ctk_menu_new(struct ctk_menu *menu, char *title);
+void ctk_menu_add(struct ctk_menu *menu);
+void ctk_menu_remove(struct ctk_menu *menu);
+unsigned char ctk_menuitem_add(struct ctk_menu *menu, char *name);
+
+/* Functions for icons. */
+#define CTK_ICON_ADD(icon, id) ctk_icon_add((struct ctk_widget *)icon, id)
+void ctk_icon_add(struct ctk_widget *icon, ek_id_t id);
+
+/* Functions for manipulating widgets. */
+#define CTK_WIDGET_ADD(win, widg) \
+ ctk_widget_add(win, (struct ctk_widget *)widg)
+void ctk_widget_add(struct ctk_window *window,
+		    struct ctk_widget *widget);
+
+#define CTK_WIDGET_FOCUS(win, widg) \
+  (win)->focused = (struct ctk_widget *)(widg)
+#define CTK_WIDGET_REDRAW(widg) \
+ ctk_widget_redraw((struct ctk_widget *)widg)
+void ctk_widget_redraw(struct ctk_widget *w);
+
+#define CTK_WIDGET_YPOS(w) (((struct ctk_widget *)(w))->y)
+
+#define ctk_textentry_set_height(w, height) \
+                           (w)->widget.textentry.h = (height)
+#define ctk_label_set_height(w, height) \
+                           (w)->widget.label.h = (height)
+#define ctk_label_set_text(l, t) (l)->text = (t)
+
+#define CTK_BUTTON_NEW(widg, xpos, ypos, width, buttontext) \
+ (widg)->window = NULL; \
+ (widg)->next = NULL; \
+ (widg)->type = CTK_WIDGET_BUTTON; \
+ (widg)->x = (xpos); \
+ (widg)->y = (ypos); \
+ (widg)->w = (width); \
+ (widg)->text = (buttontext)
+
+#define CTK_LABEL_NEW(widg, xpos, ypos, width, height, labeltext) \
+ (widg)->window = NULL; \
+ (widg)->next = NULL; \
+ (widg)->type = CTK_WIDGET_LABEL; \
+ (widg)->x = (xpos); \
+ (widg)->y = (ypos); \
+ (widg)->w = (width); \
+ (widg)->h = (height); \
+ (widg)->text = (labeltext)
+
+#define CTK_TEXTENTRY_NEW(widg, xxpos, yypos, width, height, textptr, textlen) \
+ (widg)->window = NULL; \
+ (widg)->next = NULL; \
+ (widg)->type = CTK_WIDGET_TEXTENTRY; \
+ (widg)->x = (xxpos); \
+ (widg)->y = (yypos); \
+ (widg)->w = (width); \
+ (widg)->h = (height); \
+ (widg)->text = (textptr); \
+ (widg)->len = (textlen); \
+ (widg)->state = CTK_TEXTENTRY_NORMAL; \
+ (widg)->xpos = 0; \
+ (widg)->ypos = 0
+
+
+
+#define CTK_HYPERLINK_NEW(widg, xpos, ypos, width, linktext, linkurl) \
+ (widg)->window = NULL; \
+ (widg)->next = NULL; \
+ (widg)->type = CTK_WIDGET_HYPERLINK; \
+ (widg)->x = (xpos); \
+ (widg)->y = (ypos); \
+ (widg)->w = (width); \
+ (widg)->text = (linktext); \
+ (widg)->url = (linkurl)
+
+/* Signals. */
+extern ek_signal_t ctk_signal_keypress,
+  ctk_signal_timer,
+  ctk_signal_button_activate,
+  ctk_signal_button_hover,
+  ctk_signal_hyperlink_activate,
+  ctk_signal_hyperlink_hover,
+  ctk_signal_menu_activate,
+  ctk_signal_window_close;
+		       
+/* Focus flags */
+#define CTK_FOCUS_NONE     0
+#define CTK_FOCUS_WIDGET   1
+#define CTK_FOCUS_WINDOW   2
+#define CTK_FOCUS_DIALOG   4
+
+
+
+#endif /* __CTK_H__ */
diff --git a/contiki/ek/dispatcher.c b/contiki/ek/dispatcher.c
new file mode 100644
index 0000000..a2b0a76
--- /dev/null
+++ b/contiki/ek/dispatcher.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the "ek" event kernel.
+ *
+ * $Id: dispatcher.c,v 1.1 2003/03/19 14:16:05 adamdunkels Exp $
+ *
+ */
+
+#include "ek.h"
+#include "dispatcher.h"
+
+#include "uip.h"
+
+#include <time.h>
+/*#include <conio.h>*/
+
+ek_id_t dispatcher_current;
+static struct dispatcher_proc *procs;
+static ek_id_t ids = 0;
+
+
+static ek_signal_t lastsig = 1;
+
+struct listenport {
+  u16_t port;
+  ek_id_t id;
+};
+static struct listenport listenports[UIP_LISTENPORTS];
+static u8_t listenportsptr;
+
+/*-----------------------------------------------------------------------------------*/
+ek_signal_t
+dispatcher_sigalloc(void)
+{
+  return lastsig++;
+}
+/*-----------------------------------------------------------------------------------*/
+ek_id_t
+dispatcher_start(struct dispatcher_proc *p)
+{
+  ek_id_t id;
+
+  id = ids++;
+  
+  if(procs == NULL) {
+    procs = p;
+  } else {
+    p->next = procs;
+    procs = p;
+  }
+  p->id = id;
+
+  dispatcher_current = id;
+  
+  return id;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ek_idle(void)
+{
+  struct dispatcher_proc *p;
+  for(p = procs; p != NULL; p = p->next) {
+    if(p->idle != NULL) {
+      dispatcher_current = p->id;
+      p->idle();
+    }
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+ek_clock_t
+ek_clock(void)
+{
+  return clock();
+}
+/*-----------------------------------------------------------------------------------*/
+ek_err_t
+ek_dispatcher(ek_signal_t s, ek_data_t data, ek_id_t id)
+{
+  struct dispatcher_proc *p;
+  for(p = procs; p != NULL; p = p->next) {
+    if(p->id == id &&
+       p->signalhandler != NULL) {
+      dispatcher_current = id;
+      p->signalhandler(s, data);
+      return EK_ERR_OK;
+    }
+  }
+  return EK_ERR_OK;
+}
+/*-----------------------------------------------------------------------------------*/
+#ifdef WITH_UIP
+void
+dispatcher_uipcall(void)     
+{
+  struct dispatcher_proc *p;
+  struct dispatcher_uipstate *s;
+  u8_t i;
+
+  s = (struct dispatcher_uipstate *)uip_conn->appstate;
+
+  /* If this is a connection request for a listening port, we must
+     mark the connection with the right process ID. */
+  if(uip_connected()) {
+    for(i = 0; i < listenportsptr; ++i) {
+      if(listenports[i].port == uip_conn->lport) {
+	s->id = listenports[i].id;
+	break;
+      }
+    }
+  }
+  
+
+  for(p = procs; p != NULL; p = p->next) {
+    if(p->id == s->id &&
+       p->uiphandler != NULL) {
+      dispatcher_current = p->id;
+      p->uiphandler(s->state);
+    }
+  }
+}
+#endif /* WITH_UIP */
+/*-----------------------------------------------------------------------------------*/
+void
+dispatcher_uiplisten(u16_t port)
+{
+  listenports[listenportsptr].port = htons(port);
+  listenports[listenportsptr].id = dispatcher_current;
+
+  ++listenportsptr;
+
+#ifdef WITH_UIP
+  uip_listen(port);
+#endif /* WITH_UIP */
+}
+/*-----------------------------------------------------------------------------------*/
+void
+dispatcher_markconn(struct uip_conn *conn,
+		    void *appstate)
+{
+  struct dispatcher_uipstate *s;
+
+  s = (struct dispatcher_uipstate *)conn->appstate;
+  s->id = dispatcher_current;
+  s->state = appstate;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+dispatcher_listen(ek_signal_t s)
+{
+  ek_listen(s, dispatcher_current);
+}
+/*-----------------------------------------------------------------------------------*/
+void
+dispatcher_timer(ek_signal_t s, ek_data_t data, ek_ticks_t t)
+{
+  ek_timer(s, data, dispatcher_current, t);
+}
+/*-----------------------------------------------------------------------------------*/
+struct dispatcher_proc *
+dispatcher_process(ek_id_t id)
+{
+  struct dispatcher_proc *p;
+  for(p = procs; p != NULL; p = p->next) {
+    if(p->id == id) {
+      return p;
+    }
+  }
+  return NULL;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+dispatcher_init(void)
+{
+  listenportsptr = 0;
+}
+/*-----------------------------------------------------------------------------------*/
diff --git a/contiki/ek/dispatcher.h b/contiki/ek/dispatcher.h
new file mode 100644
index 0000000..f636e88
--- /dev/null
+++ b/contiki/ek/dispatcher.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the "ek" event kernel.
+ *
+ * $Id: dispatcher.h,v 1.1 2003/03/19 14:16:05 adamdunkels Exp $
+ *
+ */
+#ifndef __DISPATCHER_H__
+#define __DISPATCHER_H__
+
+#include "ek.h"
+
+void dispatcher_init(void);
+
+#define DISPATCHER_BROADCAST EK_ID_ALL
+
+#define DISPATCHER_PROC(name, idle, signal, uip) \
+ NULL, 0, name, idle, signal, uip
+struct dispatcher_proc {
+  struct dispatcher_proc *next;
+  ek_id_t id;
+  char *name;
+  void (* idle)(void);
+  void (* signalhandler)(ek_signal_t s, ek_data_t data);
+  void (* uiphandler)(void *state);
+};
+
+#define DISPATCHER_CURRENT() dispatcher_current
+
+ek_signal_t dispatcher_sigalloc(void);
+
+ek_id_t dispatcher_start(struct dispatcher_proc *p);
+void dispatcher_stop(struct dispatcher_proc *p);
+
+void dispatcher_listen(ek_signal_t s);
+void dispatcher_timer(ek_signal_t s, ek_data_t data, ek_ticks_t t);
+#define dispatcher_emit ek_emit
+
+struct dispatcher_proc *dispatcher_process(ek_id_t id);
+
+struct dispatcher_uipstate {
+  ek_id_t id;  
+  void *state;
+};
+
+
+#define UIP_APPCALL dispatcher_uipcall
+#define UIP_APPSTATE_SIZE sizeof(struct dispatcher_uipstate)
+
+
+#include "uip.h"
+
+struct uip_conn;
+
+void dispatcher_uipcall(void);
+
+void dispatcher_markconn(struct uip_conn *conn,
+			 void *appstate);
+void dispatcher_uiplisten(u16_t port);
+
+extern ek_id_t dispatcher_current;
+
+#endif /* __DISPATCHER_H__ */
diff --git a/contiki/ek/ek-conf.h b/contiki/ek/ek-conf.h
new file mode 100644
index 0000000..64207be
--- /dev/null
+++ b/contiki/ek/ek-conf.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the "ek" event kernel.
+ *
+ * $Id: ek-conf.h,v 1.1 2003/03/19 14:16:05 adamdunkels Exp $
+ *
+ */
+
+
+#ifndef __EK_CONF_H__
+#define __EK_CONF_H__
+
+#include <time.h>
+
+typedef void *ek_data_t;
+
+typedef unsigned char ek_signal_t;
+typedef unsigned char ek_id_t;
+
+/* ek_ticks_t: should be defined to be the largest type that fits the
+   highest timeout value used by the system. For example, if all
+   timeouts are between 1 and 150, the ek_ticks_t can be typedef'd as
+   "unsigned char", but if the maximum timeout is over 256, "unsigned
+   short" is a better choise. */
+typedef unsigned short ek_ticks_t;
+
+/* ek_clock_t: should be defined to be the native clock ticks type
+   used by the underlying system. (Look for time_t or similar.) */
+typedef unsigned long ek_clock_t; 
+
+#define EK_CONF_NUMSIGNALS   32    /* Must be 2^n */
+typedef unsigned char ek_num_signals_t;
+
+#define EK_CONF_NUMTIMERS    4    /* Must be 2^n */
+typedef unsigned char ek_num_timers_t;
+
+#define EK_CONF_NUMLISTENERS  32    /* Must be 2^n */
+typedef unsigned char ek_num_listeners_t;
+
+#define EK_CONF_UNLISTEN 0
+
+#endif /* __EK_CONF_H__ */
diff --git a/contiki/ek/ek.c b/contiki/ek/ek.c
new file mode 100644
index 0000000..3161df9
--- /dev/null
+++ b/contiki/ek/ek.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the "ek" event kernel.
+ *
+ * $Id: ek.c,v 1.1 2003/03/19 14:16:05 adamdunkels Exp $
+ *
+ */
+
+#include "ek.h"
+#include "ek-conf.h"
+
+/* Structure definitions. */
+struct ek_listener {
+  ek_signal_t s;
+  ek_id_t id;
+};
+
+struct ek_timer {
+  struct ek_timer *next;
+  ek_ticks_t t;
+  ek_signal_t s;
+  ek_data_t data;
+  ek_id_t id;
+};
+
+struct ek_signal_data {
+  ek_signal_t s;
+  ek_data_t data;
+  ek_id_t id;
+};
+
+/* Static variables. */
+static ek_num_signals_t nsignals, fsignal;
+static struct ek_signal_data signals[EK_CONF_NUMSIGNALS];
+
+static struct ek_listener listeners[EK_CONF_NUMLISTENERS];
+
+static struct ek_timer timers[EK_CONF_NUMTIMERS];
+static struct ek_timer *timers_free, *timers_used;
+
+#ifndef NULL
+#define NULL (void *)0
+#endif /* NULL */
+
+/*-----------------------------------------------------------------------------------*/
+void
+ek_init(void)
+{
+  ek_num_timers_t i;
+  
+  nsignals = fsignal = 0;
+
+  timers_used = NULL;
+  for(i = 0; i < EK_CONF_NUMTIMERS - 1; ++i) {
+    timers[i].next = &timers[i+1];
+  }
+  timers[i].next = NULL;
+  timers_free = &timers[0];
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ek_signals(void)
+{
+  static ek_signal_t s;
+  static ek_data_t data;
+  static ek_id_t id;
+  static ek_num_listeners_t i;
+
+  if(nsignals > 0) {
+    /* There are signals that we should deliver. */
+    s = signals[fsignal].s;
+    
+    data = signals[fsignal].data;
+    id = signals[fsignal].id;
+    
+    /* Since we have seen the new signal, we move pointer upwards
+       and decrese number. */
+    fsignal = (fsignal + 1) & (EK_CONF_NUMSIGNALS - 1);
+    --nsignals;
+    /* Check for listeners. */
+    for(i = 0; i < EK_CONF_NUMLISTENERS; ++i) {
+      if(listeners[i].s == s &&
+	 (id == EK_ID_NONE ||
+	  id == listeners[i].id)) {
+	ek_dispatcher(s, data, listeners[i].id);
+      }
+    }
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+void
+ek_run(void)
+{
+  static ek_clock_t lasttime, curtime;
+  static ek_signal_t s;
+  static ek_data_t data;
+  static ek_id_t id;
+  static struct ek_timer *tptr;
+  
+  lasttime = ek_clock();
+  
+  while(1) {
+    /* Check the signal list to see if we have signals that should be
+       emitted. */
+    ek_signals();
+    
+    /* If no signals should be emitted, we first check outstanding
+       timers and then call the ek_idle function. */
+    curtime = ek_clock();
+    
+    while(timers_used != NULL &&
+	  (curtime - lasttime) >= (ek_clock_t)timers_used->t) {
+      s = timers_used->s;
+      data = timers_used->data;
+      id = timers_used->id;
+      
+      /* Move this timer slot to the free list. */
+      tptr = timers_used;
+      timers_used = timers_used->next;
+      tptr->next = timers_free;
+      timers_free = tptr;
+      lasttime = curtime;
+      
+      /* Emit the actual signal. Note that this will not trigger
+	 any action until the dispatcher code is executed the next
+	 run through the loop. */
+      ek_emit(s, data, id);
+    }
+    ek_idle();
+    
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+ek_err_t
+ek_emit(ek_signal_t s, ek_data_t data, ek_id_t id)
+{
+  static unsigned char snum;
+  
+  if(nsignals == EK_CONF_NUMSIGNALS) {
+    return EK_ERR_FULL;
+  }
+  
+  if(s != EK_SIGNAL_NONE) {
+    snum = (fsignal + nsignals) & (EK_CONF_NUMSIGNALS - 1);
+    signals[snum].s = s;
+    signals[snum].data = data;
+    signals[snum].id = id;
+    ++nsignals;
+  }
+  return EK_ERR_OK;
+}
+/*-----------------------------------------------------------------------------------*/
+ek_err_t
+ek_listen(ek_signal_t s, ek_id_t id)
+{
+  ek_num_listeners_t i, empty;
+  
+  /* Go through the listeners list to make sure that the registering
+     listener is unique. At the same time, we find the first empty
+     listener in the list. */
+  empty = EK_CONF_NUMLISTENERS;
+  for(i = 0; i < EK_CONF_NUMLISTENERS; ++i) {
+    if(listeners[i].id == id &&
+       listeners[i].s == s) {
+      return EK_ERR_NOTUNIQUE;
+    }
+    if(empty == EK_CONF_NUMLISTENERS &&
+       listeners[i].s == EK_SIGNAL_NONE) {
+      empty = i;
+    }
+  }
+
+  /* We return an error if there were no empty listener slots. */
+  if(empty == EK_CONF_NUMLISTENERS) {
+    return EK_ERR_FULL;
+  }
+
+  /* Else we register the listener and return OK. */
+  listeners[empty].s = s;
+  listeners[empty].id = id;
+
+  return EK_ERR_OK;  
+}
+/*-----------------------------------------------------------------------------------*/
+#if EK_CONF_UNLISTEN
+ek_err_t
+ek_unlisten(ek_signal_t s, ek_id_t id)
+{
+  ek_num_listeners_t i;
+  
+  /* Go through the listeners list to find the listener to
+     unregister. */
+  for(i = 0; i < EK_CONF_NUMLISTENERS; ++i) {
+    if(listeners[i].id == id &&
+       listeners[i].s == s) {
+      listeners[i].s = EK_SIGNAL_NONE;
+      return EK_ERR_OK;
+    }
+  }
+  return EK_ERR_NOTFOUND;
+}
+#endif /* EK_CONF_UNLISTEN */
+/*-----------------------------------------------------------------------------------*/
+ek_err_t
+ek_timer(ek_signal_t s, ek_data_t data, ek_id_t id, ek_ticks_t t)
+{
+  register struct ek_timer *tptr, *tptr2;
+
+  /* First check if all timer slots are used already. If so, we return
+     an error. */
+  if(timers_free == NULL) {
+    return EK_ERR_FULL;
+  }
+
+  /* We take the first unused timer slot. */  
+  tptr = timers_free;
+  timers_free = timers_free->next;
+
+  tptr->next = NULL;
+  tptr->s = s;
+  tptr->data = data;
+  tptr->id = id;
+  
+  /* If there are no running timers, we put this one first on the list
+     and return. */
+  if(timers_used == NULL) {
+
+    tptr->t = t;
+    timers_used = tptr;
+    return EK_ERR_OK;
+  }
+
+  /* Check if this timeout should be placed first in the list. */
+  if(timers_used->t > t) {
+    tptr->t = t;
+    timers_used->t -= t;
+    tptr->next = timers_used;
+    timers_used = tptr;
+
+  } else {
+    /* Calculate where in the list of timers this one should be
+       inserted. This is done by walking through the list while
+       subtracting the cumulative time in ticks from the delay for the
+       current timer. */
+    
+    for(tptr2 = timers_used; tptr2 != NULL; tptr2 = tptr2->next) {
+      t -= tptr2->t;
+      if(tptr2->next == NULL ||
+	 tptr2->next->t > t) {
+	if(tptr2->next != NULL) {
+	  tptr2->next->t -= t;
+	}
+	tptr->t = t;
+	tptr->next = tptr2->next;
+	tptr2->next = tptr;
+	break;
+      }
+    }    
+  }  
+  
+  return EK_ERR_OK;
+}
+/*-----------------------------------------------------------------------------------*/
diff --git a/contiki/ek/ek.h b/contiki/ek/ek.h
new file mode 100644
index 0000000..bd69626
--- /dev/null
+++ b/contiki/ek/ek.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the "ek" event kernel.
+ *
+ * $Id: ek.h,v 1.1 2003/03/19 14:16:05 adamdunkels Exp $
+ *
+ */
+/*-----------------------------------------------------------------------------------*/
+#ifndef __EK_H__
+#define __EK_H__
+
+#include "ek-conf.h"
+
+/* Signals defined by ek: */
+#define EK_SIGNAL_NONE 0
+
+/* Errors: */
+#define EK_ERR_OK        0
+#define EK_ERR_FULL      1
+#define EK_ERR_NOTUNIQUE 2
+#define EK_ERR_NOTFOUND  3
+
+/* Special IDs defined by ek: */
+#define EK_ID_NONE 0
+#define EK_ID_ALL  0
+
+typedef unsigned char ek_err_t;
+
+/* Callback functions (must be implemented by the system/application
+   program using ek): */
+
+/* ek_idle:
+ *
+ * Is called repeatedly by ek when there is nothing else to do. This
+ * function can be used to implement polling operations by the
+ * system/application program using ek.
+ */
+void ek_idle(void);
+
+/* ek_dispatcher:
+ *
+ * Is called by ek when a signal has been omitted. The "id" parameter
+ * can be used to distinguish listeners.
+ *
+ * Return values: TBA
+ */
+ek_err_t ek_dispatcher(ek_signal_t s, ek_data_t data, ek_id_t id);
+
+/* ek_clock:
+ *
+ * Should return the current value of the system clock.
+ */
+ek_clock_t ek_clock(void);
+
+
+/* API functions (are used by programs using ek): */
+
+/* ek_listen:
+ *
+ * Registers the listener identified by "id" with the signal "s". When
+ * the signal "s" is emitted, the ek dispatcher callback will be
+ * invoked for the listener "id".
+ *
+ * The meaning of the identifier "id" is defined by the caller (i.e.,
+ * the application program using the ek kernel).
+ *
+ * Return values: TBA
+ */
+ek_err_t ek_listen(ek_signal_t s, ek_id_t id);
+
+/* ek_unlisten:
+ *
+ * Unregisters a previously registered listener.
+ *
+ * Return values: TBA
+ */
+ek_err_t ek_unlisten(ek_signal_t s, ek_id_t id);
+
+/* ek_emit:
+ *
+ * Emits the signal "s". When control returns to ek, the ek signal
+ * dispatcher will be called for each of the registered listeners for
+ * the signal. If no listeners are registered, the signal is
+ * dropped.
+ *
+ * Return values: TBA
+ */
+ek_err_t ek_emit(ek_signal_t s, ek_data_t data, ek_id_t id);
+
+/* ek_timer:
+ *
+ * Sets a timer that will make the signal "s" to be emitted after "t"
+ * number of clock ticks. The granularity of the clock ticks is
+ * determined by the underlying system on which ek is run.
+ *
+ * Return values: TBA
+ */ 
+ek_err_t ek_timer(ek_signal_t s, ek_data_t data, ek_id_t id,
+		  ek_ticks_t t);
+
+/* ek_init:
+ *
+ * Initializes ek.
+ */
+void ek_init(void);
+
+/* ek_signals:
+ *
+ * Called internally by ek_run(). Processes signals. 
+ */
+void ek_signals(void);
+
+/* ek_run:
+ *
+ * The main function in ek that is called to start ek. This function
+ * never returns.
+ */
+void ek_run(void);
+
+#endif /* __EK_H__ */
+/*-----------------------------------------------------------------------------------*/
diff --git a/contiki/lib/libconio.c b/contiki/lib/libconio.c
new file mode 100644
index 0000000..7c5080d
--- /dev/null
+++ b/contiki/lib/libconio.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment
+ *
+ * $Id: libconio.c,v 1.1 2003/03/19 14:16:05 adamdunkels Exp $
+ *
+ */
+
+#include "libconio.h"
+
+static unsigned char cursx, cursy;
+static unsigned char reversed;
+
+/*-----------------------------------------------------------------------------------*/
+unsigned char
+wherex(void)
+{
+  return cursx;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+clrscr(void)
+{
+  unsigned char x, y;
+
+  for(x = 0; x < LIBCONIO_SCREEN_WIDTH; ++x) {
+    for(y = 0; y < LIBCONIO_SCREEN_HEIGHT; ++y) {
+      gotoxy(x, y);
+      cputc(' ');
+    }
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+void
+revers(unsigned char c)
+{
+  reversed = c;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+cputc(char c)
+{
+  ctk_arch_draw_char(c, cursx, cursy, reversed);
+  ++cursx;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+cputs(char *str)
+{
+  int i;
+  for(i = 0; i < strlen(str); ++i) {
+    cputc(str[i]);
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+void
+cclear(unsigned char length)
+{
+  int i;
+  for(i = 0; i < length; ++i) {
+    cputc(' ');
+  }  
+}
+/*-----------------------------------------------------------------------------------*/
+void
+chline(unsigned char length)
+{
+  int i;
+  for(i = 0; i < length; ++i) {
+    cputc('-');
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+void
+cvline(unsigned char length)
+{
+  int i;
+  for(i = 0; i < length; ++i) {
+    ctk_arch_draw_char('|', cursx, cursy, 0);    
+    ++cursy;
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+void
+gotoxy(unsigned char x, unsigned char y)
+{
+  cursx = x;
+  cursy = y;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+cclearxy(unsigned char x, unsigned char y, unsigned char length)
+{
+  gotoxy(x, y);
+  cclear(length);
+}
+/*-----------------------------------------------------------------------------------*/
+void
+chlinexy(unsigned char x, unsigned char y, unsigned char length)
+{
+  gotoxy(x, y);
+  chline(length);
+}
+/*-----------------------------------------------------------------------------------*/
+void
+cvlinexy(unsigned char x, unsigned char y, unsigned char length)
+{
+  gotoxy(x, y);
+  cvline(length);
+}
+/*-----------------------------------------------------------------------------------*/
+void
+cputsxy(unsigned char x, unsigned char y, char *str)
+{
+  gotoxy(x, y);
+  cputs(str);
+}
+/*-----------------------------------------------------------------------------------*/
+void
+cputcxy(unsigned char x, unsigned char y, char c)
+{
+  gotoxy(x, y);
+  cputc(c);
+}
+/*-----------------------------------------------------------------------------------*/
+void
+textcolor(unsigned char c)
+{
+
+}
+/*-----------------------------------------------------------------------------------*/
diff --git a/contiki/lib/libconio.h b/contiki/lib/libconio.h
new file mode 100644
index 0000000..26c9c03
--- /dev/null
+++ b/contiki/lib/libconio.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment
+ *
+ * $Id: libconio.h,v 1.1 2003/03/19 14:16:05 adamdunkels Exp $
+ *
+ */
+
+#ifndef __LIBCONIO_H__
+#define __LIBCONIO_H__
+
+
+/* This function must be implemented specifically for the architecure: */
+void ctk_arch_draw_char(char c,
+			unsigned char xpos,
+			unsigned char ypos,
+			unsigned char reversedflag);
+
+/* Default definitions that should be overridden by calling module. */
+#ifndef LIBCONIO_SCREEN_WIDTH
+#define LIBCONIO_SCREEN_WIDTH 40
+#endif /* LIBCONIO_SCREEN_WIDTH */
+
+#ifndef LIBCONIO_SCREEN_HEIGHT
+#define LIBCONIO_SCREEN_HEIGHT 25
+#endif /* LIBCONIO_SCREEN_HEIGHT */
+
+
+
+/* These are function declarations for functions implemented in libconio.c */
+unsigned char wherex(void);
+void clrscr(void);
+void revers(unsigned char c);
+void cputc(char c);
+void cputs(char *str);
+void cclear(unsigned char length);
+void chline(unsigned char length);
+void cvline(unsigned char length);
+void gotoxy(unsigned char x, unsigned char y);
+void cclearxy(unsigned char x, unsigned char y, unsigned char length);
+void chlinexy(unsigned char x, unsigned char y, unsigned char length);
+void cvlinexy(unsigned char x, unsigned char y, unsigned char length);
+void cputsxy(unsigned char x, unsigned char y, char *str);
+void cputcxy(unsigned char x, unsigned char y, char c);
+void textcolor(unsigned char c);
+
+
+
+#endif /* __LIBCONIO_H__ */
diff --git a/contiki/lib/petsciiconv.c b/contiki/lib/petsciiconv.c
new file mode 100644
index 0000000..696c8c5
--- /dev/null
+++ b/contiki/lib/petsciiconv.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment for the C64.
+ *
+ * $Id: petsciiconv.c,v 1.1 2003/03/19 14:16:05 adamdunkels Exp $
+ *
+ */
+
+
+static unsigned char petscii2ascii[128] = {
+  0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
+  0x14,0x09,0x0d,0x11,0x93,0x0a,0x0e,0x0f,
+  0x10,0x0b,0x12,0x13,0x08,0x15,0x16,0x17,
+  0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
+  0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
+  0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
+  0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
+  0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
+  0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,
+  0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
+  0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,
+  0x78,0x79,0x7a,0x5b,0x5c,0x5d,0x7e,0x5f,
+  0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,
+  0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
+  0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,
+  0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0x7e,0xdf
+};
+
+static unsigned char ascii2petscii[128] = {
+  0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
+  0x14,0x09,0x0d,0x11,0x93,0x0a,0x0e,0x0f,
+  0x10,0x0b,0x12,0x13,0x08,0x15,0x16,0x17,
+  0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
+  0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
+  0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
+  0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
+  0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
+  0x40,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,
+  0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
+  0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,
+  0xd8,0xd9,0xda,0x5b,0x5c,0x5d,0x5e,0x5f,
+  0xc0,0x41,0x42,0x43,0x44,0x45,0x46,0x47,
+  0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
+  0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,
+  0x58,0x59,0x5a,0xdb,0xdd,0xdd,0x5e,0xdf,
+};
+
+static unsigned int i;
+static unsigned char *ptr;
+
+/*-----------------------------------------------------------------------------------*/
+void
+petsciiconv_toascii(char *buf, unsigned int len)
+{
+  ptr = buf;
+  for(i = len; i > 0; --i) {
+    *ptr = petscii2ascii[*ptr & 0x7f];
+    ++ptr;
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+void
+petsciiconv_topetscii(char *buf, unsigned int len)
+{
+  ptr = buf;
+  for(i = len; i > 0; --i) {
+    *ptr = ascii2petscii[*ptr & 0x7f];
+    ++ptr;
+  }
+}
+/*-----------------------------------------------------------------------------------*/
diff --git a/contiki/lib/petsciiconv.h b/contiki/lib/petsciiconv.h
new file mode 100644
index 0000000..f6b20a6
--- /dev/null
+++ b/contiki/lib/petsciiconv.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment for the C64.
+ *
+ * $Id: petsciiconv.h,v 1.1 2003/03/19 14:16:06 adamdunkels Exp $
+ *
+ */
+#ifndef __PETSCIICONV_H__
+#define __PETSCIICONV_H__
+
+#ifdef WITH_ASCII
+
+#define petsciiconv_toascii(buf, len)
+#define petsciiconv_topetscii(buf, len)
+
+#else /* WITH_ASCII */
+
+void petsciiconv_toascii(char *buf, unsigned int len);
+void petsciiconv_topetscii(char *buf, unsigned int len);
+
+#endif /* WITH_ASCII */
+
+#endif /* __PETSCIICONV_H__ */
diff --git a/contiki/lib/strncasecmp.c b/contiki/lib/strncasecmp.c
new file mode 100644
index 0000000..54e99f6
--- /dev/null
+++ b/contiki/lib/strncasecmp.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment for the C64.
+ *
+ * $Id: strncasecmp.c,v 1.1 2003/03/19 14:16:06 adamdunkels Exp $
+ *
+ */
+
+/* This file contains a naive and non-stanrdards compliant
+   implementation of strncasecmp() for systems that don't implement
+   the function. It works with Contiki, but should most probably not
+   be used anywhere else.
+
+   It copies the n first bytes two strings into two buffers and
+   compares them with strcasecmp.
+*/
+
+#include <string.h>
+
+#define MAX_STRLEN 40
+
+/*static char buf1[MAX_STRLEN],
+  buf2[MAX_STRLEN];*/
+/*-----------------------------------------------------------------------------------*/
+unsigned char
+strncasecmp(const char *s1, const char *s2, unsigned char n)
+{
+  unsigned char len;
+
+  return strncmp(s1, s2, n);
+  
+  /*  len = MAX_STRLEN;
+  if(n < MAX_STRLEN) {
+    len = n;
+  }
+  strncpy(buf1, s1, len);
+  buf1[MAX_STRLEN - 1] = 0;
+  strncpy(buf2, s2, len);
+  buf2[MAX_STRLEN - 1] = 0;
+  return strcasecmp(buf1, buf2);*/
+}
+/*-----------------------------------------------------------------------------------*/
diff --git a/contiki/uip/resolv.c b/contiki/uip/resolv.c
new file mode 100644
index 0000000..919b8c5
--- /dev/null
+++ b/contiki/uip/resolv.c
@@ -0,0 +1,389 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above copyright 
+ *    notice, this list of conditions and the following disclaimer in the 
+ *    documentation and/or other materials provided with the distribution. 
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Adam Dunkels.
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the uIP TCP/IP stack.
+ *
+ * $Id: resolv.c,v 1.1 2003/03/19 14:16:06 adamdunkels Exp $
+ *
+ */
+
+#include "resolv.h"
+#include "dispatcher.h"
+
+#ifndef NULL
+#define NULL (void *)0
+#endif /* NULL */
+
+#define MAX_RETRIES 8
+
+struct dns_hdr {
+  u16_t id;
+  u8_t flags1, flags2;
+#define DNS_FLAG1_RESPONSE        0x80
+#define DNS_FLAG1_OPCODE_STATUS   0x10
+#define DNS_FLAG1_OPCODE_INVERSE  0x08
+#define DNS_FLAG1_OPCODE_STANDARD 0x00
+#define DNS_FLAG1_AUTHORATIVE     0x04
+#define DNS_FLAG1_TRUNC           0x02
+#define DNS_FLAG1_RD              0x01
+#define DNS_FLAG2_RA              0x80
+#define DNS_FLAG2_ERR_MASK        0x0f
+#define DNS_FLAG2_ERR_NONE        0x00
+#define DNS_FLAG2_ERR_NAME        0x03
+  u16_t numquestions;
+  u16_t numanswers;
+  u16_t numauthrr;
+  u16_t numextrarr;
+};
+
+struct dns_answer {
+  /* DNS answer record starts with either a domain name or a pointer
+     to a name already present somewhere in the packet. */
+  u16_t type;
+  u16_t class;
+  u16_t ttl[2];
+  u16_t len;
+  u16_t ipaddr[2];
+};
+
+
+#define STATE_UNUSED 0
+#define STATE_NEW    1
+#define STATE_ASKING 2
+#define STATE_DONE   3
+#define STATE_ERROR  4
+struct namemap {
+  u8_t state;
+  u8_t tmr;
+  u8_t retries;
+  u8_t seqno;
+  u8_t err;
+  char name[64];
+  u16_t ipaddr[2];
+};
+
+#define RESOLV_ENTRIES 8
+
+static struct namemap names[RESOLV_ENTRIES];
+
+static u8_t seqno;
+
+static struct uip_udp_conn *resolv_conn = NULL;
+
+ek_signal_t resolv_signal_found = EK_SIGNAL_NONE;
+
+/*-----------------------------------------------------------------------------------*/
+/* parse_name(name):
+ *
+ * Returns the end of the name.
+ */
+static unsigned char *
+parse_name(unsigned char *query)
+{
+  unsigned char n;
+
+  do {
+    n = *query++;
+    
+    while(n > 0) {
+      /*      printf("%c", *query);*/
+      ++query;
+      --n;
+    };
+    /*    printf(".");*/
+  } while(*query != 0);
+  /*  printf("\n");*/
+  return query + 1;
+}
+/*-----------------------------------------------------------------------------------*/
+/* check_entries(void):
+ *
+ * Runs through the list of names to see if there are any that have
+ * not been queried yet. If so, a query is sent out.
+ */
+static void
+check_entries(void)
+{
+  struct dns_hdr *hdr;
+  char *query, *nptr, *nameptr;
+  u8_t i;
+  u8_t n;
+
+  for(i = 0; i < RESOLV_ENTRIES; ++i) {
+
+    if(names[i].state == STATE_NEW ||
+       names[i].state == STATE_ASKING) {
+      if(names[i].state == STATE_ASKING) {
+	--names[i].tmr;
+	if(names[i].tmr == 0) {
+	  ++names[i].retries;
+	  if(names[i].retries == MAX_RETRIES) {
+	    names[i].state = STATE_ERROR;
+	    resolv_found(names[i].name, NULL);
+	    continue;
+	  }
+	  names[i].tmr = names[i].retries;	  
+	} else {
+	  /*	  printf("Timer %d\n", names[i].tmr);*/
+	  /* Its timer has not run out, so we move on to next
+	     entry. */
+	  continue;
+	}
+      } else {
+	names[i].state = STATE_ASKING;
+	names[i].tmr = 1;
+	names[i].retries = 0;
+      }
+      hdr = (struct dns_hdr *)uip_appdata;
+      hdr->id = htons(i);
+      hdr->flags1 = DNS_FLAG1_RD;
+      hdr->flags2 = 0;
+      hdr->numquestions = htons(1);
+      hdr->numanswers = hdr->numauthrr = hdr->numextrarr = 0;
+      query = (char *)uip_appdata + 12;
+      nameptr = names[i].name;
+      --nameptr;
+      /* Convert hostname into suitable query format. */
+      do {
+	++nameptr;
+	nptr = query;
+	++query;
+	for(n = 0; *nameptr != '.' && *nameptr != 0; ++nameptr) {
+	  *query = *nameptr;
+	  ++query;
+	  ++n;
+	}
+	*nptr = n;
+      } while(*nameptr != 0);
+      nptr = query;
+      *nptr = 0; /* End of query name. */
+      ++nptr;
+      *nptr = 0; /* High byte of query type. */
+      ++nptr;
+      *nptr = 1; /* Low byte of query type. 1 == IP address query. */
+      ++nptr;
+      *nptr = 0; /* High byte of query class. */
+      ++nptr;
+      *nptr = 1; /* Low byte of query class. */
+      ++nptr;
+      uip_udp_send((unsigned char)(nptr - (char *)uip_appdata));
+      break;
+    }
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+newdata(void)
+{
+  char *nameptr;
+  struct dns_answer *ans;
+  struct dns_hdr *hdr;
+  u8_t nquestions, nanswers;
+  u8_t i;
+  
+  hdr = (struct dns_hdr *)uip_appdata;
+  /*  printf("ID %d\n", htons(hdr->id));
+  printf("Query %d\n", hdr->flags1 & DNS_FLAG1_RESPONSE);
+  printf("Error %d\n", hdr->flags2 & DNS_FLAG2_ERR_MASK);
+  printf("Num questions %d, answers %d, authrr %d, extrarr %d\n",
+	 htons(hdr->numquestions),
+	 htons(hdr->numanswers),
+	 htons(hdr->numauthrr),
+	 htons(hdr->numextrarr));
+  */
+
+  /* The ID in the DNS header should be our entry into the name
+     table. */
+  i = htons(hdr->id); 
+  if(i < RESOLV_ENTRIES &&
+     names[i].state == STATE_ASKING) {
+
+    /* This entry is now finished. */
+    names[i].state = STATE_DONE;
+    names[i].err = hdr->flags2 & DNS_FLAG2_ERR_MASK;
+
+    /* Check for error. If so, call callback to inform. */
+    if(names[i].err != 0) {
+      names[i].state = STATE_ERROR;
+      resolv_found(names[i].name, NULL);
+      return;
+    }
+
+    /* We only care about the question(s) and the answers. The authrr
+       and the extrarr are simply discarded. */
+    nquestions = htons(hdr->numquestions);
+    nanswers = htons(hdr->numanswers);
+
+    /* Skip the name in the question. XXX: This should really be
+       checked agains the name in the question, to be sure that they
+       match. */
+    nameptr = parse_name((char *)uip_appdata + 12) + 4;
+
+    while(nanswers > 0) {
+      /* The first byte in the answer resource record determines if it
+	 is a compressed record or a normal one. */
+      if(*nameptr & 0xc0) {       
+	/* Compressed name. */
+	nameptr +=2;
+	/*	printf("Compressed anwser\n");*/
+      } else {
+	/* Not compressed name. */
+	nameptr = parse_name((char *)nameptr);
+      }
+
+      ans = (struct dns_answer *)nameptr;
+      /*      printf("Answer: type %x, class %x, ttl %x, length %x\n",
+	     htons(ans->type), htons(ans->class),
+	     (htons(ans->ttl[0]) << 16) | htons(ans->ttl[1]),
+	     htons(ans->len));*/
+
+      /* Check for IP address type and Internet class. Others are
+	 discarded. */
+      if(ans->type == htons(1) &&
+	 ans->class == htons(1) &&
+	 ans->len == htons(4)) {
+	/*	printf("IP address %d.%d.%d.%d\n",
+	       htons(ans->ipaddr[0]) >> 8,
+	       htons(ans->ipaddr[0]) & 0xff,
+	       htons(ans->ipaddr[1]) >> 8,
+	       htons(ans->ipaddr[1]) & 0xff);*/
+	/* XXX: we should really check that this IP address is the one
+	   we want. */
+	names[i].ipaddr[0] = ans->ipaddr[0];
+	names[i].ipaddr[1] = ans->ipaddr[1];
+	resolv_found(names[i].name, names[i].ipaddr);
+	return;
+      } else {
+	nameptr = nameptr + 10 + htons(ans->len);
+      }
+      --nanswers;
+    }
+  }
+
+}
+/*-----------------------------------------------------------------------------------*/
+/* udp_appcall():
+ *
+ * The main UDP function.
+ */
+void
+udp_appcall(void)
+{
+  if(uip_udp_conn->rport == htons(53)) {
+    if(uip_poll()) {
+      check_entries();
+    }
+    if(uip_newdata()) {
+      newdata();
+    }       
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+/* resolv_query(name):
+ *
+ * Queues a name so that a question for the name will be sent out the
+ * next time the udp_appcall is polled.
+ */
+void
+resolv_query(char *name)
+{
+  u8_t i;
+  u8_t lseq, lseqi;
+
+  lseq = lseqi = 0;
+  
+  for(i = 0; i < RESOLV_ENTRIES; ++i) {
+    if(names[i].state == STATE_UNUSED) {
+      break;
+    }
+    if(seqno - names[i].seqno > lseq) {
+      lseq = seqno - names[i].seqno;
+      lseqi = i;
+    }
+  }
+
+  if(i == RESOLV_ENTRIES) {
+    i = lseqi;
+  }
+
+  /*  printf("Using entry %d\n", i);*/
+
+  strcpy(names[i].name, name);
+  names[i].state = STATE_NEW;
+  names[i].seqno = seqno;
+  ++seqno;
+
+}
+/*-----------------------------------------------------------------------------------*/
+u16_t *
+resolv_lookup(char *name)
+{
+  u8_t i;
+
+  /* Walk through the list to see if the name is in there. If it is
+     not, we return NULL. */
+  for(i = 0; i < RESOLV_ENTRIES; ++i) {
+    if(names[i].state == STATE_DONE &&
+       strcmp(name, names[i].name) == 0) {
+      return names[i].ipaddr;
+    }
+  }
+  return NULL;
+}  
+/*-----------------------------------------------------------------------------------*/
+void
+resolv_conf(u16_t *dnsserver)
+{
+  if(resolv_conn != NULL) {
+    uip_udp_remove(resolv_conn);
+  }
+  
+  resolv_conn = uip_udp_new(dnsserver, 53);
+
+}
+/*-----------------------------------------------------------------------------------*/
+void
+resolv_init(void)
+{
+  u8_t i;
+
+  for(i = 0; i < RESOLV_ENTRIES; ++i) {
+    names[i].state = STATE_DONE;
+  }
+
+  resolv_signal_found = dispatcher_sigalloc();
+}
+/*-----------------------------------------------------------------------------------*/
+void
+resolv_found(char *name, u16_t *ipaddr)
+{
+  dispatcher_emit(resolv_signal_found, name, DISPATCHER_BROADCAST);
+}
diff --git a/contiki/uip/resolv.h b/contiki/uip/resolv.h
new file mode 100644
index 0000000..6360fa1
--- /dev/null
+++ b/contiki/uip/resolv.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above copyright 
+ *    notice, this list of conditions and the following disclaimer in the 
+ *    documentation and/or other materials provided with the distribution. 
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Adam Dunkels.
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the uIP TCP/IP stack.
+ *
+ * $Id: resolv.h,v 1.1 2003/03/19 14:16:06 adamdunkels Exp $
+ *
+ */
+#ifndef __RESOLV_H__
+#define __RESOLV_H__
+
+#include "uip.h"
+#include "ek.h"
+
+/* Signals */
+extern ek_signal_t resolv_signal_found;
+
+/* Callbacks. */
+void resolv_found(char *name, u16_t *ipaddr);
+
+/* Functions. */
+void resolv_conf(u16_t *dnsserver);
+void resolv_init(void);
+u16_t *resolv_lookup(char *name);
+void resolv_query(char *name);
+
+#endif /* __RESOLV_H__ */
diff --git a/contiki/uip/uip.c b/contiki/uip/uip.c
new file mode 100644
index 0000000..ca289c8
--- /dev/null
+++ b/contiki/uip/uip.c
@@ -0,0 +1,1606 @@
+/*
+ * Copyright (c) 2001-2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above copyright 
+ *    notice, this list of conditions and the following disclaimer in the 
+ *    documentation and/or other materials provided with the distribution. 
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Adam Dunkels.
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the uIP TCP/IP stack.
+ *
+ * $Id: uip.c,v 1.1 2003/03/19 14:16:06 adamdunkels Exp $
+ *
+ */
+
+/*
+This is a small implementation of the IP and TCP protocols (as well as
+some basic ICMP stuff). The implementation couples the IP, TCP and the
+application layers very tightly. To keep the size of the compiled code
+down, this code also features heavy usage of the goto statement.
+
+The principle is that we have a small buffer, called the uip_buf, in
+which the device driver puts an incoming packet. The TCP/IP stack
+parses the headers in the packet, and calls upon the application. If
+the remote host has sent data to the application, this data is present
+in the uip_buf and the application read the data from there. It is up
+to the application to put this data into a byte stream if needed. The
+application will not be fed with data that is out of sequence.
+
+If the application whishes to send data to the peer, it should put its
+data into the uip_buf, 40 bytes from the start of the buffer. The
+TCP/IP stack will calculate the checksums, and fill in the necessary
+header fields and finally send the packet back to the peer.
+*/
+
+#include "uip.h"
+#include "uipopt.h"
+#include "uip_arch.h"
+
+/*-----------------------------------------------------------------------------------*/
+/* Variable definitions. */
+
+
+/* The IP address of this host. If it is defined to be fixed (by setting UIP_FIXEDADDR to 1 in uipopt.h), the address is set here. Otherwise, the address */
+#if UIP_FIXEDADDR > 0
+#if UIP_IPV6
+const u16_t uip_hostaddr[8] =
+  {UIP_IP6ADDR0, UIP_IP6ADDR1,
+   UIP_IP6ADDR2, UIP_IP6ADDR3,
+   UIP_IP6ADDR4, UIP_IP6ADDR5,
+   UIP_IP6ADDR6, UIP_IP6ADDR7};
+#else /* UIP_IPV6 */
+const u16_t uip_hostaddr[2] =
+  {htons((UIP_IPADDR0 << 8) | UIP_IPADDR1),
+   htons((UIP_IPADDR2 << 8) | UIP_IPADDR3)};
+#endif /* UIP_IPV6 */
+#else
+#if UIP_IPV6
+u16_t uip_hostaddr[8];       
+#else /* UIP_IPV6 */
+u16_t uip_hostaddr[2];       
+#endif /* UIP_IPV6 */
+#endif /* UIP_FIXEDADDR */
+
+u8_t uip_buf[UIP_BUFSIZE];   /* The packet buffer that contains
+				incoming packets. */
+volatile u8_t *uip_appdata;  /* The uip_appdata pointer points to
+				application data. */
+#if UIP_URGDATA > 0
+volatile u8_t *uip_urgdata;  /* The uip_urgdata pointer points to
+				urgent data (out-of-band data), if
+				present. */
+volatile u8_t uip_urglen, uip_surglen;
+#endif /* UIP_URGDATA > 0 */
+
+#if UIP_BUFSIZE > 255
+volatile u16_t uip_len, uip_slen;
+                             /* The uip_len is either 8 or 16 bits,
+				depending on the maximum packet
+				size. */
+#else
+volatile u8_t uip_len, uip_slen;
+#endif /* UIP_BUFSIZE > 255 */
+
+volatile u8_t uip_flags;     /* The uip_flags variable is used for
+				communication between the TCP/IP stack
+				and the application program. */
+struct uip_conn *uip_conn;   /* uip_conn always points to the current
+				connection. */
+
+struct uip_conn uip_conns[UIP_CONNS];
+                             /* The uip_conns array holds all TCP
+				connections. */
+u16_t uip_listenports[UIP_LISTENPORTS];
+                             /* The uip_listenports list all currently
+				listning ports. */
+#if UIP_UDP
+struct uip_udp_conn *uip_udp_conn;
+struct uip_udp_conn uip_udp_conns[UIP_UDP_CONNS];
+#endif /* UIP_UDP */
+
+
+static u16_t ipid;           /* Ths ipid variable is an increasing
+				number that is used for the IP ID
+				field. */
+
+static u8_t iss[4];          /* The iss variable is used for the TCP
+				initial sequence number. */
+
+#if UIP_ACTIVE_OPEN
+/* XXX static */ u16_t lastport;       /* Keeps track of the last port used for
+				a new connection. */
+#endif /* UIP_ACTIVE_OPEN */
+
+/* Temporary variables. */
+volatile u8_t uip_acc32[4];
+static u8_t c, opt;
+static u16_t tmpport;
+
+/* Structures and definitions. */
+#define TCP_FIN 0x01
+#define TCP_SYN 0x02
+#define TCP_RST 0x04
+#define TCP_PSH 0x08
+#define TCP_ACK 0x10
+#define TCP_URG 0x20
+#define TCP_CTL 0x3f
+
+#define ICMP_ECHO_REPLY 0
+#define ICMP_ECHO       8     
+
+/* Macros. */
+#define BUF ((uip_tcpip_hdr *)&uip_buf[UIP_LLH_LEN])
+#define FBUF ((uip_tcpip_hdr *)&uip_reassbuf[0])
+#define ICMPBUF ((uip_icmpip_hdr *)&uip_buf[UIP_LLH_LEN])
+#define UDPBUF ((uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN])
+
+#if UIP_STATISTICS == 1
+struct uip_stats uip_stat;
+#define UIP_STAT(s) s
+#else
+#define UIP_STAT(s)
+#endif /* UIP_STATISTICS == 1 */
+
+#if UIP_LOGGING == 1
+#include <stdio.h>
+void uip_log(char *msg);
+#define UIP_LOG(m) uip_log(m)
+#else
+#define UIP_LOG(m)
+#endif /* UIP_LOGGING == 1 */
+
+/*-----------------------------------------------------------------------------------*/
+void
+uip_init(void)
+{
+  for(c = 0; c < UIP_LISTENPORTS; ++c) {
+    uip_listenports[c] = 0;
+  }
+  for(c = 0; c < UIP_CONNS; ++c) {
+    uip_conns[c].tcpstateflags = CLOSED;
+  }
+#if UIP_ACTIVE_OPEN
+  lastport = 1024;
+#endif /* UIP_ACTIVE_OPEN */
+
+#if UIP_UDP
+  for(c = 0; c < UIP_UDP_CONNS; ++c) {
+    uip_udp_conns[c].lport = 0;
+  }
+#endif /* UIP_UDP */
+  
+#if UIP_IPV6
+  /* IPv6 initialization. */  
+#if UIP_FIXEDADDR == 0
+  uip_hostaddr[0] = uip_hostaddr[1] =
+    uip_hostaddr[2] = uip_hostaddr[3] =
+    uip_hostaddr[4] = uip_hostaddr[5] =
+    uip_hostaddr[6] = uip_hostaddr[7] = 0;
+#endif /* UIP_FIXEDADDR */
+  
+#else /* UIP_IPV6 */
+
+  /* IPv4 initialization. */
+#if UIP_FIXEDADDR == 0
+  uip_hostaddr[0] = uip_hostaddr[1] = 0;
+#endif /* UIP_FIXEDADDR */
+  
+#endif /* UIP_IPV6 */
+}
+/*-----------------------------------------------------------------------------------*/
+#if UIP_ACTIVE_OPEN
+struct uip_conn *
+uip_connect(u16_t *ripaddr, u16_t rport)
+{
+  struct uip_conn *conn;
+  
+  /* Find an unused local port. */
+ again:
+  ++lastport;
+
+  if(lastport >= 32000) {
+    lastport = 4096;
+  }
+  
+  for(c = 0; c < UIP_CONNS; ++c) {
+    if(uip_conns[c].tcpstateflags != CLOSED &&
+       uip_conns[c].lport == lastport)
+      goto again;
+  }
+
+
+  conn = 0;
+  for(c = 0; c < UIP_CONNS; ++c) {
+    if(uip_conns[c].tcpstateflags == CLOSED) {
+      conn = &uip_conns[c]; 
+      break;
+    }
+    if(uip_conns[c].tcpstateflags == TIME_WAIT) {
+      if(conn == 0 ||
+	 uip_conns[c].timer > uip_conn->timer) {
+	conn = &uip_conns[c];
+      }
+    }
+  }
+
+  if(conn == 0) {
+    return 0;
+  }
+  
+  conn->tcpstateflags = SYN_SENT;
+
+  conn->snd_nxt[0] = iss[0];
+  conn->snd_nxt[1] = iss[1];
+  conn->snd_nxt[2] = iss[2];
+  conn->snd_nxt[3] = iss[3];
+
+  conn->len = 1;   /* TCP length of the SYN is one. */
+  conn->nrtx = 0;
+  conn->timer = 1; /* Send the SYN next time around. */
+  conn->lport = htons(lastport);
+  conn->rport = htons(rport);
+#if UIP_IPV6
+  conn->ripaddr[0] = ripaddr[0];
+  conn->ripaddr[1] = ripaddr[1];
+  conn->ripaddr[2] = ripaddr[2];
+  conn->ripaddr[3] = ripaddr[3];
+  conn->ripaddr[4] = ripaddr[4];
+  conn->ripaddr[5] = ripaddr[5];
+  conn->ripaddr[6] = ripaddr[6];
+  conn->ripaddr[7] = ripaddr[7];
+#else /* UIP_IPV6 */
+  conn->ripaddr[0] = ripaddr[0];
+  conn->ripaddr[1] = ripaddr[1];
+#endif /* UIP_IPV6 */
+  
+  return conn;
+}
+#endif /* UIP_ACTIVE_OPEN */
+/*-----------------------------------------------------------------------------------*/
+#if UIP_UDP
+struct uip_udp_conn *
+uip_udp_new(u16_t *ripaddr, u16_t rport)
+{
+  struct uip_udp_conn *conn;
+  
+  /* Find an unused local port. */
+ again:
+  ++lastport;
+
+  if(lastport >= 32000) {
+    lastport = 4096;
+  }
+  
+  for(c = 0; c < UIP_UDP_CONNS; ++c) {
+    if(uip_udp_conns[c].lport == lastport)
+      goto again;
+  }
+
+
+  conn = 0;
+  for(c = 0; c < UIP_UDP_CONNS; ++c) {
+    if(uip_udp_conns[c].lport == 0) {
+      conn = &uip_udp_conns[c]; 
+      break;
+    }
+  }
+
+  if(conn == 0) {
+    return 0;
+  }
+  
+  conn->lport = htons(lastport);
+  conn->rport = htons(rport);
+#if UIP_IPV6
+  conn->ripaddr[0] = ripaddr[0];
+  conn->ripaddr[1] = ripaddr[1];
+  conn->ripaddr[2] = ripaddr[2];
+  conn->ripaddr[3] = ripaddr[3];
+  conn->ripaddr[4] = ripaddr[4];
+  conn->ripaddr[5] = ripaddr[5];
+  conn->ripaddr[6] = ripaddr[6];
+  conn->ripaddr[7] = ripaddr[7];
+#else /* UIP_IPV6 */
+  conn->ripaddr[0] = ripaddr[0];
+  conn->ripaddr[1] = ripaddr[1];
+#endif /* UIP_IPV6 */
+  
+  return conn;
+}
+#endif /* UIP_UDP */
+/*-----------------------------------------------------------------------------------*/
+void
+uip_listen(u16_t port)
+{
+  for(c = 0; c < UIP_LISTENPORTS; ++c) {
+    if(uip_listenports[c] == 0) {
+      uip_listenports[c] = htons(port);
+      break;
+    }
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+#if UIP_IPV6
+/* IPv4 fragment reassembly is not used in IPv6. */
+#define UIP_REASSEMBLY 0
+#else /* UIP_IPV6 */
+#define UIP_REASSEMBLY 0
+#endif /* UIP_IPV6 */
+
+#define UIP_REASS_MAXAGE 10
+
+#if UIP_REASSEMBLY
+#define UIP_REASS_BUFSIZE (UIP_BUFSIZE - UIP_LLH_LEN)
+static u8_t uip_reassbuf[UIP_REASS_BUFSIZE];
+static u8_t uip_reassbitmap[UIP_REASS_BUFSIZE / (8 * 8)];
+static const u8_t bitmap_bits[8] = {0xff, 0x7f, 0x3f, 0x1f,
+				    0x0f, 0x07, 0x03, 0x01};
+static u16_t uip_reasslen;
+static u8_t uip_reassflags;
+#define UIP_REASS_FLAG_LASTFRAG 0x01
+static u8_t uip_reasstmr;
+
+#define IP_HLEN 20
+#define IP_MF 0x20
+
+static u8_t
+uip_reass(void)
+{
+  u16_t offset, len;
+  u16_t i;
+  
+  /* If ip_reasstmr is zero, no packet is present in the buffer, so we
+     write the IP header of the fragment into the reassembly
+     buffer. The timer is updated with the maximum age. */
+  if(uip_reasstmr == 0) {
+    bcopy(&BUF->vhl, uip_reassbuf, IP_HLEN);
+    uip_reasstmr = UIP_REASS_MAXAGE;
+    uip_reassflags = 0;
+    /* Clear the bitmap. */
+    bzero(uip_reassbitmap, sizeof(uip_reassbitmap));
+  }
+
+  /* Check if the incoming fragment matches the one currently present
+     in the reasembly buffer. If so, we proceed with copying the
+     fragment into the buffer. */
+  if(BUF->srcipaddr[0] == FBUF->srcipaddr[0] &&
+     BUF->destipaddr[1] == FBUF->destipaddr[1] &&
+     BUF->srcipaddr[0] == FBUF->srcipaddr[0] &&
+     BUF->destipaddr[1] == FBUF->destipaddr[1] &&
+     BUF->ipid == FBUF->ipid) {
+    
+    len = (BUF->len[0] << 8) + BUF->len[1] - (BUF->vhl & 0x0f) * 4;
+    offset = (((BUF->ipoffset[0] & 0x3f) << 8) + BUF->ipoffset[1]) * 8;
+
+    /* If the offset or the offset + fragment length overflows the
+       reassembly buffer, we discard the entire packet. */
+    if(offset > UIP_REASS_BUFSIZE ||
+       offset + len > UIP_REASS_BUFSIZE) {
+      uip_reasstmr = 0;
+      goto nullreturn;
+    }
+
+    /* Copy the fragment into the reassembly buffer, at the right
+       offset. */
+    bcopy(BUF + (BUF->vhl & 0x0f) * 4,
+	  &uip_reassbuf[IP_HLEN + offset], len);
+
+    /* Update the bitmap. */
+    if(offset / (8 * 8) == (offset + len) / (8 * 8)) {
+      /* If the two endpoints are in the same byte, we only update
+	 that byte. */
+      uip_reassbitmap[offset / (8 * 8)] |=
+	bitmap_bits[(offset / 8 ) & 7] &
+	~bitmap_bits[((offset + len) / 8 ) & 7];
+    } else {
+      /* If the two endpoints are in different bytes, we update the
+	 bytes in the endpoints and fill the stuff inbetween with
+	 0xff. */
+      uip_reassbitmap[offset / (8 * 8)] |=
+	bitmap_bits[(offset / 8 ) & 7];
+      for(i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i) {
+	uip_reassbitmap[i] = 0xff;
+      }      
+      uip_reassbitmap[(offset + len) / (8 * 8)] |=
+	~bitmap_bits[((offset + len) / 8 ) & 7];
+    }
+    
+    /* If this fragment has the More Fragments flag set to zero, we
+       know that this is the last fragment, so we can calculate the
+       size of the entire packet. We also set the
+       IP_REASS_FLAG_LASTFRAG flag to indicate that we have received
+       the final fragment. */
+
+    if((BUF->ipoffset[0] & IP_MF) == 0) {
+      uip_reassflags |= UIP_REASS_FLAG_LASTFRAG;
+      uip_reasslen = offset + len;
+    }
+    
+    /* Finally, we check if we have a full packet in the buffer. We do
+       this by checking if we have the last fragment and if all bits
+       in the bitmap are set. */
+    if(uip_reassflags & UIP_REASS_FLAG_LASTFRAG) {
+      /* Check all bytes up to and including all but the last byte in
+	 the bitmap. */
+      for(i = 0; i < uip_reasslen / (8 * 8) - 1; ++i) {
+	if(uip_reassbitmap[i] != 0xff) {
+	  goto nullreturn;
+	}
+      }
+      /* Check the last byte in the bitmap. It should contain just the
+	 right amount of bits. */
+      if(uip_reassbitmap[uip_reasslen / (8 * 8)] !=
+	 (u8_t)~bitmap_bits[uip_reasslen / 8 & 7]) {
+	goto nullreturn;
+      }
+
+      /* If we have come this far, we have a full packet in the
+	 buffer, so we allocate a pbuf and copy the packet into it. We
+	 also reset the timer. */
+      uip_reasstmr = 0;
+      bcopy(FBUF, BUF, uip_reasslen);
+
+      /* Pretend to be a "normal" (i.e., not fragmented) IP packet
+	 from now on. */
+      BUF->ipoffset[0] = BUF->ipoffset[1] = 0;
+      BUF->ipchksum = 0;
+      BUF->ipchksum = ~(uip_ipchksum());
+
+      
+
+      return uip_reasslen;
+    }
+  }
+
+ nullreturn:
+  return 0;
+}
+#endif /* IP_REASSEMBLY */
+/*-----------------------------------------------------------------------------------*/
+void
+uip_process(u8_t flag)
+{
+  register struct uip_conn *uip_connr = uip_conn;
+  /*#define uip_connr uip_conn*/
+  
+  uip_appdata = &uip_buf[40 + UIP_LLH_LEN];
+
+  
+  /* Check if we were invoked because of the perodic timer fireing. */
+  if(flag == UIP_TIMER) {
+    /* Increase the initial sequence number. */
+    if(++iss[3] == 0) {
+      if(++iss[2] == 0) {
+	if(++iss[1] == 0) {
+	  ++iss[0];
+	}
+      }
+    }    
+    uip_len = 0;
+    if(uip_connr->tcpstateflags == TIME_WAIT ||
+       uip_connr->tcpstateflags == FIN_WAIT_2) {
+      ++(uip_connr->timer);
+      if(uip_connr->timer == UIP_TIME_WAIT_TIMEOUT) {
+	uip_connr->tcpstateflags = CLOSED;
+      }
+    } else if(uip_connr->tcpstateflags != CLOSED) {
+      /* If the connection has outstanding data, we increase the
+	 connection's timer and see if it has reached the RTO value
+	 in which case we retransmit. */
+      if(uip_outstanding(uip_connr)) {
+	--(uip_connr->timer);
+	if(uip_connr->timer == 0) {
+	  if(uip_connr->nrtx == UIP_MAXRTX ||
+	     ((uip_connr->tcpstateflags == SYN_SENT ||
+	       uip_connr->tcpstateflags == SYN_RCVD) &&
+	      uip_connr->nrtx == UIP_MAXSYNRTX)) {
+	    uip_connr->tcpstateflags = CLOSED;
+
+	    /* We call UIP_APPCALL() with uip_flags set to
+	       UIP_TIMEDOUT to inform the application that the
+	       connection has timed out. */
+	    uip_flags = UIP_TIMEDOUT;
+	    UIP_APPCALL();
+
+	    /* We also send a reset packet to the remote host. */
+	    BUF->flags = TCP_RST | TCP_ACK;
+	    goto tcp_send_nodata;
+	  }
+
+	  /* Exponential backoff. */
+	  uip_connr->timer = UIP_RTO << (uip_connr->nrtx > 4? 4: uip_connr->nrtx);
+
+	  ++(uip_connr->nrtx);
+	  
+	  /* Ok, so we need to retransmit. We do this differently
+	     depending on which state we are in. In ESTABLISHED, we
+	     call upon the application so that it may prepare the
+	     data for the retransmit. In SYN_RCVD, we resend the
+	     SYNACK that we sent earlier and in LAST_ACK we have to
+	     retransmit our FINACK. */
+	  UIP_STAT(++uip_stat.tcp.rexmit);
+	  switch(uip_connr->tcpstateflags & TS_MASK) {
+	  case SYN_RCVD:
+	    /* In the SYN_RCVD state, we should retransmit our
+               SYNACK. */
+	    goto tcp_send_synack;
+	    
+#if UIP_ACTIVE_OPEN
+	  case SYN_SENT:
+	    /* In the SYN_SENT state, we retransmit out SYN. */
+	    BUF->flags = 0;
+	    goto tcp_send_syn;
+#endif /* UIP_ACTIVE_OPEN */
+	    
+	  case ESTABLISHED:
+	    /* In the ESTABLISHED state, we call upon the application
+               to do the actual retransmit after which we jump into
+               the code for sending out the packet (the apprexmit
+               label). */
+	    uip_len = 0;
+	    uip_slen = 0;
+	    uip_flags = UIP_REXMIT;
+	    UIP_APPCALL();
+	    goto apprexmit;
+	    
+	  case FIN_WAIT_1:
+	  case CLOSING:
+	  case LAST_ACK:
+	    /* In all these states we should retransmit a FINACK. */
+	    goto tcp_send_finack;
+	    
+	  }
+	}
+      } else if((uip_connr->tcpstateflags & TS_MASK) == ESTABLISHED) {
+	/* If there was no need for a retransmission, we poll the
+           application for new data. */
+	uip_len = 0;
+	uip_slen = 0;
+	uip_flags = UIP_POLL;
+	UIP_APPCALL();
+	goto appsend;
+      }
+    }
+    goto drop;
+  }
+#if UIP_UDP 
+  if(flag == UIP_UDP_TIMER) {
+    if(uip_udp_conn->lport != 0) {
+      uip_appdata = &uip_buf[UIP_LLH_LEN + 28];
+      uip_len = uip_slen = 0;
+      uip_flags = UIP_POLL;
+      UIP_UDP_APPCALL();
+      goto udp_send;
+    } else {
+      goto drop;
+    }
+  }
+#endif
+
+  /* This is where the input processing starts. */
+  UIP_STAT(++uip_stat.ip.recv);
+
+#if UIP_IPV6
+
+  /* Start of IPv6 input header processing code. */
+
+  /* Check version number in IP header (should be 6) */
+  if((BUF->vtc & 0x60) != 0x60) {
+    UIP_STAT(++uip_stat.ip.drop);
+    UIP_STAT(++uip_stat.ip.vhlerr);
+    UIP_LOG("ip: invalid version.");
+    goto drop;
+  }
+  
+  /* Check packet length. It must be at less than or equal to
+     uip_len. If less, the link layer has added a trailer or padding,
+     and we set uip_len accordingly. */
+  tmpport = (((u16_t)BUF->len[0] << 8) | BUF->len[1]);
+  if(tmpport < uip_len) {
+    uip_len = tmpport;
+  } else if(tmpport > uip_len) {
+    UIP_STAT(++uip_stat.ip.drop);
+    UIP_STAT(++uip_stat.ip.lblenerr);
+    UIP_LOG("ip: packet too short.");                               
+    goto drop;
+  }
+
+  /* Check if the packet is destined for our IP address. */
+  for(c = 0; c < 8; ++c) {
+    if(BUF->destipaddr[c] != uip_hostaddr[c]) {
+      UIP_STAT(++uip_stat.ip.drop);
+      UIP_LOG("ip: packet not for us.");        
+      goto drop;
+    }
+  }
+
+  if(BUF->nxthdr == UIP_PROTO_TCP)  /* Check for TCP packet. If so, jump
+					to the tcp_input label. */
+    goto tcp_input;
+
+  if(BUF->nxthdr != UIP_PROTO_ICMP) { /* We only allow ICMP packets from
+				       here. */
+    UIP_STAT(++uip_stat.ip.drop);
+    UIP_STAT(++uip_stat.ip.protoerr);
+    UIP_LOG("ip: neither tcp nor icmp.");        
+    goto drop;
+  }
+
+  /* Start of (very simplistic!) ICMPv6 input processing code. */
+ icmp_input:  
+  UIP_STAT(++uip_stat.icmp.recv);
+  
+  /* ICMP echo (i.e., ping) processing. This is simple, we only change
+     the ICMP type from ECHO to ECHO_REPLY and adjust the ICMP
+     checksum before we return the packet. */
+  if(ICMPBUF->type != ICMP_ECHO) {
+    UIP_STAT(++uip_stat.icmp.drop);
+    UIP_STAT(++uip_stat.icmp.typeerr);
+    UIP_LOG("icmp: not icmp echo.");
+    goto drop;
+  }
+
+  ICMPBUF->type = ICMP_ECHO_REPLY;
+  
+  if(ICMPBUF->icmpchksum >= htons(0xffff - (ICMP_ECHO << 8))) {
+    ICMPBUF->icmpchksum += htons(ICMP_ECHO << 8) + 1;
+  } else {
+    ICMPBUF->icmpchksum += htons(ICMP_ECHO << 8);
+  }
+  
+  /* Swap IP addresses. */
+  for(c = 0; c < 8; ++c) {
+    tmpport = BUF->destipaddr[c];
+    BUF->destipaddr[c] = BUF->srcipaddr[c];
+    BUF->srcipaddr[c] = tmpport;
+  }
+
+  UIP_STAT(++uip_stat.icmp.sent);
+  goto send;
+
+
+  /* End of IPv6 input header processing code. */
+     
+#else /* UIP_IPV6 */
+
+
+  /* Start of IPv4 input header processing code. */
+  
+  /* Check validity of the IP header. */  
+  if(BUF->vhl != 0x45)  { /* IP version and header length. */
+    UIP_STAT(++uip_stat.ip.drop);
+    UIP_STAT(++uip_stat.ip.vhlerr);
+    UIP_LOG("ip: invalid version or header length.");
+    goto drop;
+  }
+  
+  /* Check the size of the packet. If the size reported to us in
+     uip_len doesn't match the size reported in the IP header, there
+     has been a transmission error and we drop the packet. */
+  
+#if UIP_BUFSIZE > 255
+  if(BUF->len[0] != (uip_len >> 8)) { /* IP length, high byte. */
+    uip_len = (uip_len & 0xff) | (BUF->len[0] << 8);
+  }
+  if(BUF->len[1] != (uip_len & 0xff)) { /* IP length, low byte. */
+    uip_len = (uip_len & 0xff00) | BUF->len[1];
+  }
+#else
+  if(BUF->len[0] != 0) {        /* IP length, high byte. */
+    UIP_STAT(++uip_stat.ip.drop);
+    UIP_STAT(++uip_stat.ip.hblenerr);
+    UIP_LOG("ip: invalid length, high byte.");
+    goto drop;
+  }
+  if(BUF->len[1] != uip_len) {  /* IP length, low byte. */
+    uip_len = BUF->len[1];
+  }
+#endif /* UIP_BUFSIZE > 255 */  
+
+  if(BUF->ipoffset[0] & 0x3f) { /* We don't allow IP fragments. */
+    UIP_STAT(++uip_stat.ip.drop);
+    UIP_STAT(++uip_stat.ip.fragerr);
+    UIP_LOG("ip: fragment dropped.");    
+    goto drop;
+  }
+
+  /* If we are configured to use ping IP address configuration and
+     hasn't been assigned an IP address yet, we accept all ICMP
+     packets. */
+#if UIP_PINGADDRCONF
+  if((uip_hostaddr[0] | uip_hostaddr[1]) == 0) {
+    if(BUF->proto == UIP_PROTO_ICMP) {
+      UIP_LOG("ip: possible ping config packet received.");
+      goto icmp_input;
+    } else {
+      UIP_LOG("ip: packet dropped since no address assigned..");
+      goto drop;
+    }
+  }
+#endif /* UIP_PINGADDRCONF */
+  
+  /* Check if the packet is destined for our IP address. */  
+  if(BUF->destipaddr[0] != uip_hostaddr[0]) {
+    UIP_STAT(++uip_stat.ip.drop);
+    UIP_LOG("ip: packet not for us.");        
+    goto drop;
+  }
+  if(BUF->destipaddr[1] != uip_hostaddr[1]) {
+    UIP_STAT(++uip_stat.ip.drop);
+    UIP_LOG("ip: packet not for us.");        
+    goto drop;
+  }
+
+  if(uip_ipchksum() != 0xffff) { /* Compute and check the IP header
+				    checksum. */
+    UIP_STAT(++uip_stat.ip.drop);
+    UIP_STAT(++uip_stat.ip.chkerr);
+    UIP_LOG("ip: bad checksum.");    
+    goto drop;
+  }
+
+  if(BUF->proto == UIP_PROTO_TCP)  /* Check for TCP packet. If so, jump
+                                     to the tcp_input label. */
+    goto tcp_input;
+
+#if UIP_UDP
+  if(BUF->proto == UIP_PROTO_UDP)
+    goto udp_input;
+#endif /* UIP_UDP */
+
+  if(BUF->proto != UIP_PROTO_ICMP) { /* We only allow ICMP packets from
+					here. */
+    UIP_STAT(++uip_stat.ip.drop);
+    UIP_STAT(++uip_stat.ip.protoerr);
+    UIP_LOG("ip: neither tcp nor icmp.");        
+    goto drop;
+  }
+  
+ icmp_input:
+  UIP_STAT(++uip_stat.icmp.recv);
+  
+  /* ICMP echo (i.e., ping) processing. This is simple, we only change
+     the ICMP type from ECHO to ECHO_REPLY and adjust the ICMP
+     checksum before we return the packet. */
+  if(ICMPBUF->type != ICMP_ECHO) {
+    UIP_STAT(++uip_stat.icmp.drop);
+    UIP_STAT(++uip_stat.icmp.typeerr);
+    UIP_LOG("icmp: not icmp echo.");
+    goto drop;
+  }
+
+  /* If we are configured to use ping IP address assignment, we use
+     the destination IP address of this ping packet and assign it to
+     ourself. */
+#if UIP_PINGADDRCONF
+  if((uip_hostaddr[0] | uip_hostaddr[1]) == 0) {
+    uip_hostaddr[0] = BUF->destipaddr[0];
+    uip_hostaddr[1] = BUF->destipaddr[1];
+  }
+#endif /* UIP_PINGADDRCONF */  
+  
+  ICMPBUF->type = ICMP_ECHO_REPLY;
+  
+  if(ICMPBUF->icmpchksum >= htons(0xffff - (ICMP_ECHO << 8))) {
+    ICMPBUF->icmpchksum += htons(ICMP_ECHO << 8) + 1;
+  } else {
+    ICMPBUF->icmpchksum += htons(ICMP_ECHO << 8);
+  }
+  
+  /* Swap IP addresses. */
+  tmpport = BUF->destipaddr[0];
+  BUF->destipaddr[0] = BUF->srcipaddr[0];
+  BUF->srcipaddr[0] = tmpport;
+  tmpport = BUF->destipaddr[1];
+  BUF->destipaddr[1] = BUF->srcipaddr[1];
+  BUF->srcipaddr[1] = tmpport;
+
+  UIP_STAT(++uip_stat.icmp.sent);
+  goto send;
+
+  /* End of IPv4 input header processing code. */
+  
+#endif /* UIP_IPV6 */
+
+#if UIP_UDP
+  /* UDP input processing. */
+ udp_input:
+  /* UDP processing is really just a hack. We don't do anything to the
+     UDP/IP headers, but let the UDP application do all the hard
+     work. If the application sets uip_slen, it has a packet to
+     send. */
+#if UIP_UDP_CHECKSUMS
+  if(uip_udpchksum() != 0xffff) { 
+    UIP_STAT(++uip_stat.udp.drop);
+    UIP_STAT(++uip_stat.udp.chkerr);
+    UIP_LOG("udp: bad checksum.");    
+    goto drop;
+  }  
+#endif /* UIP_UDP_CHECKSUMS */
+
+  /* Demultiplex this UDP packet between the UDP "connections". */
+  for(uip_udp_conn = &uip_udp_conns[0];
+      uip_udp_conn < &uip_udp_conns[UIP_UDP_CONNS];
+      ++uip_udp_conn) {
+    if(uip_udp_conn->lport != 0 &&
+       UDPBUF->destport == uip_udp_conn->lport &&
+       (uip_udp_conn->rport == 0 ||
+        UDPBUF->srcport == uip_udp_conn->rport) &&
+#if UIP_IPV6
+       BUF->srcipaddr[0] == uip_connr->ripaddr[0] &&
+       BUF->srcipaddr[1] == uip_connr->ripaddr[1] &&
+       BUF->srcipaddr[2] == uip_connr->ripaddr[2] &&
+       BUF->srcipaddr[3] == uip_connr->ripaddr[3] &&
+       BUF->srcipaddr[4] == uip_connr->ripaddr[4] &&
+       BUF->srcipaddr[5] == uip_connr->ripaddr[5] &&
+       BUF->srcipaddr[6] == uip_connr->ripaddr[6] &&
+       BUF->srcipaddr[7] == uip_connr->ripaddr[7]) {
+#else /* UIP_IPV6 */
+       BUF->srcipaddr[0] == uip_udp_conn->ripaddr[0] &&
+       BUF->srcipaddr[1] == uip_udp_conn->ripaddr[1]) {
+#endif /* UIP_IPV6 */       
+      goto udp_found; 
+    }
+  }
+  goto drop;
+  
+ udp_found:
+  uip_len = uip_len - 28;
+  uip_appdata = &uip_buf[UIP_LLH_LEN + 28];
+  uip_flags = UIP_NEWDATA;
+  uip_slen = 0;
+  UIP_UDP_APPCALL();
+ udp_send:
+  if(uip_slen == 0) {
+    goto drop;      
+  }
+  uip_len = uip_slen + 28;
+
+#if UIP_BUFSIZE > 255
+  BUF->len[0] = (uip_len >> 8);
+  BUF->len[1] = (uip_len & 0xff);
+#else
+  BUF->len[0] = 0;
+  BUF->len[1] = uip_len;
+#endif /* UIP_BUFSIZE > 255 */  
+  BUF->proto = UIP_PROTO_UDP;
+
+  UDPBUF->udplen = htons(uip_slen + 8);
+  UDPBUF->udpchksum = 0;
+#if UIP_UDP_CHECKSUMS 
+  /* Calculate UDP checksum. */
+  UDPBUF->udpchksum = ~(uip_udpchksum());
+  if(UDPBUF->udpchksum == 0) {
+    UDPBUF->udpchksum = 0xffff;
+  }
+#endif /* UIP_UDP_CHECKSUMS */
+
+  BUF->srcport  = uip_udp_conn->lport;
+  BUF->destport = uip_udp_conn->rport;
+
+#if UIP_IPV6
+  for(c = 0; c < 8; ++c) {
+    BUF->srcipaddr[c] = uip_hostaddr[c];    
+    BUF->destipaddr[c] = uip_udp_conn->ripaddr[c];
+  }
+#else /* UIP_IPV6 */
+  BUF->srcipaddr[0] = uip_hostaddr[0];
+  BUF->srcipaddr[1] = uip_hostaddr[1];
+  BUF->destipaddr[0] = uip_udp_conn->ripaddr[0];
+  BUF->destipaddr[1] = uip_udp_conn->ripaddr[1];
+ 
+#endif /* UIP_IPV6 */
+
+  uip_appdata = &uip_buf[UIP_LLH_LEN + 40];
+  goto ip_send_nolen;
+#endif /* UIP_UDP */
+  
+  /* TCP input processing. */  
+ tcp_input:
+  UIP_STAT(++uip_stat.tcp.recv);
+
+  /* Start of TCP input header processing code. */
+  
+  if(uip_tcpchksum() != 0xffff) {   /* Compute and check the TCP
+				       checksum. */
+    UIP_STAT(++uip_stat.tcp.drop);
+    UIP_STAT(++uip_stat.tcp.chkerr);
+    UIP_LOG("tcp: bad checksum.");    
+    goto drop;
+  }
+  
+  /* Demultiplex this segment. */
+  /* First check any active connections. */
+  for(uip_connr = &uip_conns[0]; uip_connr < &uip_conns[UIP_CONNS]; ++uip_connr) {
+    if(uip_connr->tcpstateflags != CLOSED &&
+       BUF->destport == uip_connr->lport &&
+       BUF->srcport == uip_connr->rport &&
+#if UIP_IPV6
+       BUF->srcipaddr[0] == uip_connr->ripaddr[0] &&
+       BUF->srcipaddr[1] == uip_connr->ripaddr[1] &&
+       BUF->srcipaddr[2] == uip_connr->ripaddr[2] &&
+       BUF->srcipaddr[3] == uip_connr->ripaddr[3] &&
+       BUF->srcipaddr[4] == uip_connr->ripaddr[4] &&
+       BUF->srcipaddr[5] == uip_connr->ripaddr[5] &&
+       BUF->srcipaddr[6] == uip_connr->ripaddr[6] &&
+       BUF->srcipaddr[7] == uip_connr->ripaddr[7]) {
+#else /* UIP_IPV6 */
+       BUF->srcipaddr[0] == uip_connr->ripaddr[0] &&
+       BUF->srcipaddr[1] == uip_connr->ripaddr[1]) {
+#endif /* UIP_IPV6 */       
+      goto found;    
+    }
+  }
+
+  /* If we didn't find and active connection that expected the packet,
+     either this packet is an old duplicate, or this is a SYN packet
+     destined for a connection in LISTEN. If the SYN flag isn't set,
+     it is an old packet and we send a RST. */
+  if((BUF->flags & TCP_CTL) != TCP_SYN)
+    goto reset;
+  
+  tmpport = BUF->destport;
+  /* Next, check listening connections. */  
+  for(c = 0; c < UIP_LISTENPORTS && uip_listenports[c] != 0; ++c) {
+    if(tmpport == uip_listenports[c])
+      goto found_listen;
+  }
+  
+  /* No matching connection found, so we send a RST packet. */
+  UIP_STAT(++uip_stat.tcp.synrst);
+ reset:
+
+  /* We do not send resets in response to resets. */
+  if(BUF->flags & TCP_RST) 
+    goto drop;
+
+  UIP_STAT(++uip_stat.tcp.rst);
+  
+  BUF->flags = TCP_RST | TCP_ACK;
+  uip_len = 40;
+  BUF->tcpoffset = 5 << 4;
+
+  /* Flip the seqno and ackno fields in the TCP header. */
+  c = BUF->seqno[3];
+  BUF->seqno[3] = BUF->ackno[3];  
+  BUF->ackno[3] = c;
+  
+  c = BUF->seqno[2];
+  BUF->seqno[2] = BUF->ackno[2];  
+  BUF->ackno[2] = c;
+  
+  c = BUF->seqno[1];
+  BUF->seqno[1] = BUF->ackno[1];
+  BUF->ackno[1] = c;
+  
+  c = BUF->seqno[0];
+  BUF->seqno[0] = BUF->ackno[0];  
+  BUF->ackno[0] = c;
+
+  /* We also have to increase the sequence number we are
+     acknowledging. If the least significant byte overflowed, we need
+     to propagate the carry to the other bytes as well. */
+  if(++BUF->ackno[3] == 0) {
+    if(++BUF->ackno[2] == 0) {
+      if(++BUF->ackno[1] == 0) {
+	++BUF->ackno[0];
+      }
+    }
+  }
+ 
+  /* Swap port numbers. */
+  tmpport = BUF->srcport;
+  BUF->srcport = BUF->destport;
+  BUF->destport = tmpport;
+  
+  /* Swap IP addresses. */
+#if UIP_IPV6
+  for(c = 0; c < 8; ++c) {
+    tmpport = BUF->destipaddr[c];
+    BUF->destipaddr[c] = BUF->srcipaddr[c];
+    BUF->srcipaddr[c] = tmpport;
+  }
+#else /* UIP_IPV6 */
+  tmpport = BUF->destipaddr[0];
+  BUF->destipaddr[0] = BUF->srcipaddr[0];
+  BUF->srcipaddr[0] = tmpport;
+  tmpport = BUF->destipaddr[1];
+  BUF->destipaddr[1] = BUF->srcipaddr[1];
+  BUF->srcipaddr[1] = tmpport;
+#endif /* UIP_IPv6 */
+  
+  /* And send out the RST packet! */
+  goto tcp_send_noconn;
+
+  /* This label will be jumped to if we matched the incoming packet
+     with a connection in LISTEN. In that case, we should create a new
+     connection and send a SYNACK in return. */
+ found_listen:
+  /* First we check if there are any connections avaliable. Unused
+     connections are kept in the same table as used connections, but
+     unused ones have the tcpstate set to CLOSED. Also, connections in
+     TIME_WAIT are kept track of and we'll use the oldest one if no
+     CLOSED connections are found. Thanks to Eddie C. Dost for a very
+     nice algorithm for the TIME_WAIT search. */
+  uip_connr = 0;
+  for(c = 0; c < UIP_CONNS; ++c) {
+    if(uip_conns[c].tcpstateflags == CLOSED) {
+      uip_connr = &uip_conns[c];
+      break;
+    }
+    if(uip_conns[c].tcpstateflags == TIME_WAIT) {
+      if(uip_connr == 0 ||
+	 uip_conns[c].timer > uip_connr->timer) {
+	uip_connr = &uip_conns[c];
+      }
+    }
+  }
+
+  if(uip_connr == 0) {
+    /* All connections are used already, we drop packet and hope that
+       the remote end will retransmit the packet at a time when we
+       have more spare connections. */
+    UIP_STAT(++uip_stat.tcp.syndrop);
+    UIP_LOG("tcp: found no unused connections.");
+    goto drop;
+  }
+  uip_conn = uip_connr;
+  
+  /* Fill in the necessary fields for the new connection. */
+  uip_connr->timer = UIP_RTO;
+  uip_connr->nrtx = 0;
+  uip_connr->lport = BUF->destport;
+  uip_connr->rport = BUF->srcport;
+#if UIP_IPV6
+  for(c = 0; c < 8; ++c) {
+    uip_connr->ripaddr[c] = BUF->srcipaddr[c];
+  }
+#else /* UIP_IPV6 */
+  uip_connr->ripaddr[0] = BUF->srcipaddr[0];
+  uip_connr->ripaddr[1] = BUF->srcipaddr[1];
+#endif /* UIP_IPV6 */
+  uip_connr->tcpstateflags = SYN_RCVD;
+
+  uip_connr->snd_nxt[0] = iss[0];
+  uip_connr->snd_nxt[1] = iss[1];
+  uip_connr->snd_nxt[2] = iss[2];
+  uip_connr->snd_nxt[3] = iss[3];
+  uip_connr->len = 1;
+
+  /* rcv_nxt should be the seqno from the incoming packet + 1. */
+  uip_connr->rcv_nxt[3] = BUF->seqno[3];
+  uip_connr->rcv_nxt[2] = BUF->seqno[2];
+  uip_connr->rcv_nxt[1] = BUF->seqno[1];
+  uip_connr->rcv_nxt[0] = BUF->seqno[0];
+  uip_add_rcv_nxt(1);
+
+  /* Parse the TCP MSS option, if present. */
+  if((BUF->tcpoffset & 0xf0) > 0x50) {
+    for(c = 0; c < ((BUF->tcpoffset >> 4) - 5) << 2 ;) {
+      opt = uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + c];
+      if(opt == 0x00) {
+	/* End of options. */	
+	break;
+      } else if(opt == 0x01) {
+	++c;
+	/* NOP option. */
+      } else if(opt == 0x02 &&
+		uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == 0x04) {
+	/* An MSS option with the right option length. */	
+	tmpport = (uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 2 + c] << 8) |
+	  uip_buf[40 + UIP_LLH_LEN + 3 + c];
+	uip_connr->mss = tmpport > UIP_TCP_MSS? UIP_TCP_MSS: tmpport;
+	
+	/* And we are done processing options. */
+	break;
+      } else {
+	/* All other options have a length field, so that we easily
+	   can skip past them. */
+	if(uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == 0) {
+	  /* If the length field is zero, the options are malformed
+	     and we don't process them further. */
+	  break;
+	}
+	c += uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c];
+      }      
+    }
+  }
+  
+  /* Our response will be a SYNACK. */
+#if UIP_ACTIVE_OPEN
+ tcp_send_synack:
+  BUF->flags = TCP_ACK;    
+  
+ tcp_send_syn:
+  BUF->flags |= TCP_SYN;    
+#else /* UIP_ACTIVE_OPEN */
+ tcp_send_synack:
+  BUF->flags = TCP_SYN | TCP_ACK;    
+#endif /* UIP_ACTIVE_OPEN */
+  
+  /* We send out the TCP Maximum Segment Size option with our
+     SYNACK. */
+  BUF->optdata[0] = 2;
+  BUF->optdata[1] = 4;
+  BUF->optdata[2] = (UIP_TCP_MSS) / 256;
+  BUF->optdata[3] = (UIP_TCP_MSS) & 255;
+  uip_len = 44;
+  BUF->tcpoffset = 6 << 4;
+  goto tcp_send;
+
+  /* This label will be jumped to if we found an active connection. */
+ found:
+  uip_conn = uip_connr;
+  uip_flags = 0;
+
+  /* We do a very naive form of TCP reset processing; we just accept
+     any RST and kill our connection. We should in fact check if the
+     sequence number of this reset is wihtin our advertised window
+     before we accept the reset. */
+  if(BUF->flags & TCP_RST) {
+    uip_connr->tcpstateflags = CLOSED;
+    UIP_LOG("tcp: got reset, aborting connection.");
+    uip_flags = UIP_ABORT;
+    UIP_APPCALL();
+    goto drop;
+  }
+  /* All segments that are come thus far should have the ACK flag set,
+     otherwise we drop the packet. */
+  if(!(BUF->flags & TCP_ACK)) {
+    UIP_STAT(++uip_stat.tcp.drop);
+    UIP_STAT(++uip_stat.tcp.ackerr);
+    UIP_LOG("tcp: dropped non-ack segment.");
+    goto drop;
+  }
+      
+  /* Calculated the length of the data, if the application has sent
+     any data to us. */
+  c = (BUF->tcpoffset >> 4) << 2;
+  /* uip_len will contain the length of the actual TCP data. This is
+     calculated by subtracing the length of the TCP header (in
+     c) and the length of the IP header (20 bytes). */
+  uip_len = uip_len - c - 20;
+
+  /* First, check if the sequence number of the incoming packet is
+     what we're expecting next. If not, we send out an ACK with the
+     correct numbers in. */
+  if(uip_len > 0 &&
+     (BUF->seqno[0] != uip_connr->rcv_nxt[0] ||
+      BUF->seqno[1] != uip_connr->rcv_nxt[1] ||
+      BUF->seqno[2] != uip_connr->rcv_nxt[2] ||
+      BUF->seqno[3] != uip_connr->rcv_nxt[3])) {
+    goto tcp_send_ack;
+  }
+
+  /* Next, check if the incoming segment acknowledges any outstanding
+     data. If so, we update the sequence number, reset the length of
+     the outstanding data, calculate RTT estimations, and reset the
+     retransmission timer. */
+  if(uip_outstanding(uip_connr)) {
+    uip_add32(uip_connr->snd_nxt, uip_connr->len);
+    if(BUF->ackno[0] == uip_acc32[0] &&
+       BUF->ackno[1] == uip_acc32[1] &&
+       BUF->ackno[2] == uip_acc32[2] &&
+       BUF->ackno[3] == uip_acc32[3]) {
+      /* Update sequence number. */
+      uip_connr->snd_nxt[0] = uip_acc32[0];
+      uip_connr->snd_nxt[1] = uip_acc32[1];
+      uip_connr->snd_nxt[2] = uip_acc32[2];
+      uip_connr->snd_nxt[3] = uip_acc32[3];
+
+      /* Do RTT estimation, unless we have done retransmissions. */
+      if(uip_connr->nrtx == 0) {
+	signed char m;
+	m = (UIP_RTO << (uip_connr->nrtx > 4? 4: uip_connr->nrtx)) - uip_connr->timer;
+	/* This is taken directly from VJs original code in his paper */
+	m = m - (uip_connr->sa >> 3);
+	uip_connr->sa += m;
+	if(m < 0) {
+	  m = -m;
+	}
+	m = m - (uip_connr->sv >> 2);
+	uip_connr->sv += m;
+	uip_connr->rto = (uip_connr->sa >> 3) + uip_connr->sv;
+
+      }
+
+      /* Set the acknowledged flag. */
+      uip_flags = UIP_ACKDATA;
+      /* Reset the length of the outstanding data. */
+      uip_connr->len = 0;
+      /* Reset the retransmission timer. */
+      uip_connr->timer = UIP_RTO;
+    }
+  }
+
+  /* Do different things depending on in what state the connection is. */
+  switch(uip_connr->tcpstateflags & TS_MASK) {
+    /* CLOSED and LISTEN are not handled here. CLOSE_WAIT is not
+	implemented, since we force the application to close when the
+	peer sends a FIN (hence the application goes directly from
+	ESTABLISHED to LAST_ACK). */
+  case SYN_RCVD:
+    /* In SYN_RCVD we have sent out a SYNACK in response to a SYN, and
+       we are waiting for an ACK that acknowledges the data we sent
+       out the last time. Therefore, we want to have the UIP_ACKDATA
+       flag set. If so, we enter the ESTABLISHED state. */
+    if(uip_flags & UIP_ACKDATA) {
+      uip_connr->tcpstateflags = ESTABLISHED;
+      uip_flags = UIP_CONNECTED;
+      if(uip_len > 0) {
+        uip_flags |= UIP_NEWDATA;
+        uip_add_rcv_nxt(uip_len);
+      }
+      uip_slen = 0;
+      UIP_APPCALL();
+      goto appsend;
+    }
+    goto drop;
+#if UIP_ACTIVE_OPEN
+  case SYN_SENT:
+    /* In SYN_SENT, we wait for a SYNACK that is sent in response to
+       our SYN. The rcv_nxt is set to sequence number in the SYNACK
+       plus one, and we send an ACK. We move into the ESTABLISHED
+       state. */
+    if((uip_flags & UIP_ACKDATA) &&
+       BUF->flags == (TCP_SYN | TCP_ACK)) {
+
+      /* Parse the TCP MSS option, if present. */
+      if((BUF->tcpoffset & 0xf0) > 0x50) {
+	for(c = 0; c < ((BUF->tcpoffset >> 4) - 5) << 2 ;) {
+	  opt = uip_buf[40 + UIP_LLH_LEN + c];
+	  if(opt == 0x00) {
+	    /* End of options. */	
+	    break;
+	  } else if(opt == 0x01) {
+	    ++c;
+	    /* NOP option. */
+	  } else if(opt == 0x02 &&
+		    uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == 0x04) {
+	    /* An MSS option with the right option length. */
+	    tmpport = (uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 2 + c] << 8) |
+	      uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 3 + c];
+	    uip_connr->mss = tmpport > UIP_TCP_MSS? UIP_TCP_MSS: tmpport;
+
+	    /* And we are done processing options. */
+	    break;
+	  } else {
+	    /* All other options have a length field, so that we easily
+	       can skip past them. */
+	    if(uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == 0) {
+	      /* If the length field is zero, the options are malformed
+		 and we don't process them further. */
+	      break;
+	    }
+	    c += uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c];
+	  }      
+	}
+      }
+      uip_connr->tcpstateflags = ESTABLISHED;      
+      uip_connr->rcv_nxt[0] = BUF->seqno[0];
+      uip_connr->rcv_nxt[1] = BUF->seqno[1];
+      uip_connr->rcv_nxt[2] = BUF->seqno[2];
+      uip_connr->rcv_nxt[3] = BUF->seqno[3];
+      uip_add_rcv_nxt(1);
+      uip_flags = UIP_CONNECTED | UIP_NEWDATA;
+      uip_len = 0;
+      uip_slen = 0;
+      UIP_APPCALL();
+      goto appsend;
+    }
+    goto reset;
+#endif /* UIP_ACTIVE_OPEN */
+    
+  case ESTABLISHED:
+    /* In the ESTABLISHED state, we call upon the application to feed
+    data into the uip_buf. If the UIP_ACKDATA flag is set, the
+    application should put new data into the buffer, otherwise we are
+    retransmitting an old segment, and the application should put that
+    data into the buffer.
+
+    If the incoming packet is a FIN, we should close the connection on
+    this side as well, and we send out a FIN and enter the LAST_ACK
+    state. We require that there is no outstanding data; otherwise the
+    sequence numbers will be screwed up. */
+
+    if(BUF->flags & TCP_FIN) {
+      if(uip_outstanding(uip_connr)) {
+	goto drop;
+      }
+      uip_add_rcv_nxt(1 + uip_len);      
+      uip_flags = UIP_CLOSE;
+      if(uip_len > 0) {
+	uip_flags |= UIP_NEWDATA;
+      }
+      UIP_APPCALL();
+      uip_connr->len = 1;
+      uip_connr->tcpstateflags = LAST_ACK;
+      uip_connr->nrtx = 0;
+    tcp_send_finack:
+      BUF->flags = TCP_FIN | TCP_ACK;      
+      goto tcp_send_nodata;
+    }
+
+    /* Check the URG flag. If this is set, the segment carries urgent
+       data that we must pass to the application. */
+    if(BUF->flags & TCP_URG) {
+#if UIP_URGDATA > 0
+      uip_urglen = (BUF->urgp[0] << 8) | BUF->urgp[1];
+      if(uip_urglen > uip_len) {
+	/* There is more urgent data in the next segment to come. */
+	uip_urglen = uip_len;
+      }
+      uip_add_rcv_nxt(uip_urglen);
+      uip_len -= uip_urglen;
+      uip_urgdata = uip_appdata;
+      uip_appdata += uip_urglen;
+    } else {
+      uip_urglen = 0;
+#endif /* UIP_URGDATA > 0 */
+      uip_appdata += (BUF->urgp[0] << 8) | BUF->urgp[1];
+      uip_len -= (BUF->urgp[0] << 8) | BUF->urgp[1];
+    }
+    
+    
+    /* If uip_len > 0 we have TCP data in the packet, and we flag this
+       by setting the UIP_NEWDATA flag and update the sequence number
+       we acknowledge. If the application has stopped the dataflow
+       using uip_stop(), we must not accept any data packets from the
+       remote host. */
+    if(uip_len > 0 && !(uip_connr->tcpstateflags & UIP_STOPPED)) {
+      uip_flags |= UIP_NEWDATA;
+      uip_add_rcv_nxt(uip_len);
+    }
+    
+
+    /* If this packet constitutes an ACK for outstanding data (flagged
+       by the UIP_ACKDATA flag, we should call the application since it
+       might want to send more data. If the incoming packet had data
+       from the peer (as flagged by the UIP_NEWDATA flag), the
+       application must also be notified.
+
+       When the application is called, the global variable uip_len
+       contains the length of the incoming data. The application can
+       access the incoming data through the global pointer
+       uip_appdata, which usually points 40 bytes into the uip_buf
+       array.
+
+       If the application wishes to send any data, this data should be
+       put into the uip_appdata and the length of the data should be
+       put into uip_len. If the application don't have any data to
+       send, uip_len must be set to 0. */
+    if(uip_flags & (UIP_NEWDATA | UIP_ACKDATA)) {
+      uip_slen = 0;
+      UIP_APPCALL();
+
+    appsend:
+      if(uip_flags & UIP_ABORT) {
+	uip_slen = 0;
+	uip_connr->tcpstateflags = CLOSED;
+	BUF->flags = TCP_RST | TCP_ACK;
+	goto tcp_send_nodata;
+      }
+
+      if(uip_flags & UIP_CLOSE) {
+	uip_slen = 0;
+	uip_connr->len = 1;
+	uip_connr->tcpstateflags = FIN_WAIT_1;
+	uip_connr->nrtx = 0;
+	BUF->flags = TCP_FIN | TCP_ACK;
+	goto tcp_send_nodata;	
+      }
+
+      /* If uip_slen > 0, the application has data to be sent. We
+         cannot send data if the application already has outstanding
+         data. */
+      if(uip_slen > 0 &&
+	 !uip_outstanding(uip_connr)) {
+	uip_connr->nrtx = 0;
+	uip_connr->len = uip_slen;
+      } else {	
+	uip_slen = 0;
+      }
+    apprexmit:
+      /* If the application has data to be sent, or if the incoming
+         packet had new data in it, we must send out a packet. */
+      if(uip_slen > 0 || (uip_flags & UIP_NEWDATA)) {
+	/* Add the length of the IP and TCP headers. */
+	uip_len = uip_connr->len + UIP_TCPIP_HLEN;
+	/* We always set the ACK flag in response packets. */
+	BUF->flags = TCP_ACK;
+	/* Send the packet. */
+	goto tcp_send_noopts;
+      }
+    }
+    goto drop;
+  case LAST_ACK:
+    /* We can close this connection if the peer has acknowledged our
+       FIN. This is indicated by the UIP_ACKDATA flag. */     
+    if(uip_flags & UIP_ACKDATA) {
+      uip_connr->tcpstateflags = CLOSED;
+      uip_flags = UIP_CLOSE;
+      UIP_APPCALL();
+    }
+    break;
+    
+  case FIN_WAIT_1:
+    /* The application has closed the connection, but the remote host
+       hasn't closed its end yet. Thus we do nothing but wait for a
+       FIN from the other side. */
+    if(uip_len > 0) {
+      uip_add_rcv_nxt(uip_len);
+    }
+    if(BUF->flags & TCP_FIN) {
+      if(uip_flags & UIP_ACKDATA) {
+	uip_connr->tcpstateflags = TIME_WAIT;
+	uip_connr->timer = 0;
+	uip_connr->len = 0;
+      } else {
+	uip_connr->tcpstateflags = CLOSING;
+      }
+      uip_add_rcv_nxt(1);
+      uip_flags = UIP_CLOSE;
+      UIP_APPCALL();
+      goto tcp_send_ack;
+    } else if(uip_flags & UIP_ACKDATA) {
+      uip_connr->tcpstateflags = FIN_WAIT_2;
+      uip_connr->len = 0;
+      goto drop;
+    }
+    if(uip_len > 0) {
+      goto tcp_send_ack;
+    }
+    goto drop;
+      
+  case FIN_WAIT_2:
+    if(uip_len > 0) {
+      uip_add_rcv_nxt(uip_len);
+    }
+    if(BUF->flags & TCP_FIN) {
+      uip_connr->tcpstateflags = TIME_WAIT;
+      uip_connr->timer = 0;
+      uip_add_rcv_nxt(1);
+      uip_flags = UIP_CLOSE;
+      UIP_APPCALL();
+      goto tcp_send_ack;
+    }
+    if(uip_len > 0) {
+      goto tcp_send_ack;
+    }
+    goto drop;
+
+  case TIME_WAIT:
+    goto tcp_send_ack;
+    
+  case CLOSING:
+    if(uip_flags & UIP_ACKDATA) {
+      uip_connr->tcpstateflags = TIME_WAIT;
+      uip_connr->timer = 0;
+    }
+  }  
+  goto drop;
+  
+
+  /* We jump here when we are ready to send the packet, and just want
+     to set the appropriate TCP sequence numbers in the TCP header. */
+ tcp_send_ack:
+  BUF->flags = TCP_ACK;
+ tcp_send_nodata:
+  uip_len = 40;
+ tcp_send_noopts:
+  BUF->tcpoffset = 5 << 4;
+ tcp_send:
+  /* We're done with the input processing. We are now ready to send a
+     reply. Our job is to fill in all the fields of the TCP and IP
+     headers before calculating the checksum and finally send the
+     packet. */
+  BUF->ackno[0] = uip_connr->rcv_nxt[0];
+  BUF->ackno[1] = uip_connr->rcv_nxt[1];
+  BUF->ackno[2] = uip_connr->rcv_nxt[2];
+  BUF->ackno[3] = uip_connr->rcv_nxt[3];
+  
+  BUF->seqno[0] = uip_connr->snd_nxt[0];
+  BUF->seqno[1] = uip_connr->snd_nxt[1];
+  BUF->seqno[2] = uip_connr->snd_nxt[2];
+  BUF->seqno[3] = uip_connr->snd_nxt[3];
+
+  BUF->proto = UIP_PROTO_TCP;
+  
+  BUF->srcport  = uip_connr->lport;
+  BUF->destport = uip_connr->rport;
+
+#if UIP_IPV6
+  for(c = 0; c < 8; ++c) {
+    BUF->srcipaddr[c] = uip_hostaddr[c];    
+    BUF->destipaddr[c] = uip_connr->ripaddr[c];
+  }
+#else /* UIP_IPV6 */
+  BUF->srcipaddr[0] = uip_hostaddr[0];
+  BUF->srcipaddr[1] = uip_hostaddr[1];
+  BUF->destipaddr[0] = uip_connr->ripaddr[0];
+  BUF->destipaddr[1] = uip_connr->ripaddr[1];
+ 
+#endif /* UIP_IPV6 */
+
+  if(uip_connr->tcpstateflags & UIP_STOPPED) {
+    /* If the connection has issued uip_stop(), we advertise a zero
+       window so that the remote host will stop sending data. */
+    BUF->wnd[0] = BUF->wnd[1] = 0;
+  } else {
+#if (UIP_TCP_MSS) > 255
+    BUF->wnd[0] = (uip_connr->mss >> 8);
+#else
+    BUF->wnd[0] = 0;
+#endif /* UIP_MSS */
+    BUF->wnd[1] = (uip_connr->mss & 0xff); 
+  }
+
+ tcp_send_noconn:
+
+#if UIP_BUFSIZE > 255
+  BUF->len[0] = (uip_len >> 8);
+  BUF->len[1] = (uip_len & 0xff);
+#else
+  BUF->len[0] = 0;
+  BUF->len[1] = uip_len;
+#endif /* UIP_BUFSIZE > 255 */
+
+  /* Calculate TCP checksum. */
+  BUF->tcpchksum = 0;
+  BUF->tcpchksum = ~(uip_tcpchksum());
+  
+ ip_send_nolen:
+
+#if UIP_IPV6
+  BUF->vtc    = 0x60;
+  BUF->tcfl   = 0x00;
+  BUF->fl     = 0x0000;
+  BUF->hoplim = UIP_TTL;
+  BUF->nxthdr = UIP_PROTO_TCP;
+#else /* UIP_IPV6 */
+  BUF->vhl = 0x45;
+  BUF->tos = 0;
+  BUF->ipoffset[0] = BUF->ipoffset[1] = 0;
+  BUF->ttl  = UIP_TTL;
+  ++ipid;
+  BUF->ipid[0] = ipid >> 8;
+  BUF->ipid[1] = ipid & 0xff;
+  
+  /* Calculate IP checksum. */
+  BUF->ipchksum = 0;
+  BUF->ipchksum = ~(uip_ipchksum());
+#endif /* UIP_IPV6 */
+
+  UIP_STAT(++uip_stat.tcp.sent);
+ send:
+  UIP_STAT(++uip_stat.ip.sent);
+  /* Return and let the caller do the actual transmission. */
+  return;
+ drop:
+  uip_len = 0;
+  return;
+}
+/*-----------------------------------------------------------------------------------*/
diff --git a/contiki/uip/uip.h b/contiki/uip/uip.h
new file mode 100644
index 0000000..4ff91f0
--- /dev/null
+++ b/contiki/uip/uip.h
@@ -0,0 +1,671 @@
+/*
+ * Copyright (c) 2001-2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above copyright 
+ *    notice, this list of conditions and the following disclaimer in the 
+ *    documentation and/or other materials provided with the distribution. 
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Adam Dunkels.
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the uIP TCP/IP stack.
+ *
+ * $Id: uip.h,v 1.1 2003/03/19 14:16:07 adamdunkels Exp $
+ *
+ */
+
+#ifndef __UIP_H__
+#define __UIP_H__
+
+#include "uipopt.h"
+
+#ifndef UIP_IPV6
+#define UIP_IPV6 0
+#endif
+
+/*-----------------------------------------------------------------------------------*/
+/* First, the functions that should be called from the
+ * system. Initialization, the periodic timer and incoming packets are
+ * handled by the following three functions.
+ */
+
+/* uip_init(void):
+ *
+ * Must be called at boot up to configure the uIP data structures.
+ */
+void uip_init(void);
+
+/* uip_periodic(conn):
+ *
+ * Should be called when the periodic timer has fired. Should be
+ * called once per connection (0 - UIP_CONNS).
+ */
+#define uip_periodic(conn) do { uip_conn = &uip_conns[conn]; \
+                                uip_process(UIP_TIMER); } while (0)
+
+/* uip_input(void):
+ *
+ * Is called when the network device driver has received new data.
+ */
+#define uip_input()        uip_process(UIP_DATA)
+
+/* uip_sethostaddr(addr):
+ *
+ * Is used to set the IP address.
+ */
+#define uip_sethostaddr(addr) do { uip_hostaddr[0] = addr[0]; \
+                              uip_hostaddr[1] = addr[1]; } while(0)
+
+#if UIP_UDP
+/* uip_udp_periodic(conn):
+ */
+#define uip_udp_periodic(conn) do { uip_udp_conn = &uip_udp_conns[conn]; \
+                                uip_process(UIP_UDP_TIMER); } while (0)
+#endif /* UIP_UDP */
+/*-----------------------------------------------------------------------------------*/
+/* Functions that are used by the uIP application program. Opening and
+ * closing connections, sending and receiving data, etc. is all
+ * handled by the functions below.
+*/
+
+/* uip_listen(port):
+ *
+ * Starts listening to the specified port.
+ */
+void uip_listen(u16_t port);
+
+/* uip_connect(ripaddr, port):
+ *
+ * Returns a connection identifier that connects to a port on the
+ * specified host (given in ripaddr). If no connections are avaliable,
+ * the function returns NULL. This function is avaliable only if
+ * support for active open has been configured (#define
+ * UIP_ACTIVE_OPEN 1 in uipopt.h)
+ */
+struct uip_conn *uip_connect(u16_t *ripaddr, u16_t port);
+
+#if UIP_UDP
+/* uip_udp_new(ripaddr, rport):
+ *
+ * Sets up a new UDP "connection" with the specified parameters.
+ */
+struct uip_udp_conn *uip_udp_new(u16_t *ripaddr, u16_t rport);
+
+/* uip_udp_remove(conn):
+ *
+ * Removes the UDP "connection".
+ */
+#define uip_udp_remove(conn) (conn)->lport = 0
+
+/* uip_udp_send(len):
+ *
+ * Sends a UDP datagram of length len. The data must be present in the
+ * uip_buf buffer (pointed to by uip_appdata).
+ */
+#define uip_udp_send(len) uip_slen = (len)
+#endif /* UIP_UDP */
+
+
+/* uip_outstanding(conn):
+ *
+ * Checks whether a connection has outstanding (i.e., unacknowledged)
+ * data.
+ */
+#define uip_outstanding(conn) ((conn)->len)
+
+/* uip_send(data, len):
+ *
+ * Send data on the current connection. The length of the data must
+ * not exceed the maxium segment size (MSS) for the connection.
+ */
+#define uip_send(data, len) do { uip_appdata = (data); uip_slen = (len);} while(0)   
+
+/* uip_datalen():
+ *
+ * The length of the data that is currently avaliable (if avaliable)
+ * in the uip_appdata buffer. The test function uip_data() is
+ * used to check if data is avaliable.
+ */
+#define uip_datalen()       uip_len
+
+#define uip_urgdatalen()    uip_urglen
+
+/* uip_close():
+ *
+ * Close the current connection.
+ */
+#define uip_close()         (uip_flags = UIP_CLOSE)
+
+/* uip_abort():
+ *
+ * Abort the current connection.
+ */
+#define uip_abort()         (uip_flags = UIP_ABORT)
+
+/* uip_stop():
+ *
+ * Close our receiver's window so that we stop receiving data for the
+ * current connection.
+ */
+#define uip_stop()          (uip_conn->tcpstateflags |= UIP_STOPPED)
+
+/* uip_stopped():
+ *
+ * Find out if the current connection has been previously stopped.
+ */
+#define uip_stopped(conn)   ((conn)->tcpstateflags & UIP_STOPPED)
+
+/* uip_restart():
+ *
+ * Open the window again so that we start receiving data for the
+ * current connection.
+ */
+#define uip_restart()         do { uip_flags |= UIP_NEWDATA; \
+                                   uip_conn->tcpstateflags &= ~UIP_STOPPED; \
+                              } while(0)
+
+
+/* uIP tests that can be made to determine in what state the current
+   connection is, and what the application function should do. */
+
+/* uip_newdata():
+ *
+ * Will reduce to non-zero if there is new data for the application
+ * present at the uip_appdata pointer. The size of the data is
+ * avaliable through the uip_len variable.
+ */
+#define uip_newdata()   (uip_flags & UIP_NEWDATA)
+
+/* uip_acked():
+ *
+ * Will reduce to non-zero if the previously sent data has been
+ * acknowledged by the remote host. This means that the application
+ * can send new data. uip_reset_acked() can be used to reset the acked
+ * flag.
+ */
+#define uip_acked()   (uip_flags & UIP_ACKDATA)
+#define uip_reset_acked() (uip_flags &= ~UIP_ACKDATA)
+
+/* uip_connected():
+ *
+ * Reduces to non-zero if the current connection has been connected to
+ * a remote host. This will happen both if the connection has been
+ * actively opened (with uip_connect()) or passively opened (with
+ * uip_listen()).
+ */
+#define uip_connected() (uip_flags & UIP_CONNECTED)
+
+/* uip_closed():
+ *
+ * Is non-zero if the connection has been closed by the remote
+ * host. The application may do the necessary clean-ups.
+ */
+#define uip_closed()    (uip_flags & UIP_CLOSE)
+
+/* uip_aborted():
+ *
+ * Non-zero if the current connection has been aborted (reset) by the
+ * remote host.
+ */
+#define uip_aborted()    (uip_flags & UIP_ABORT)
+
+/* uip_timedout():
+ *
+ * Non-zero if the current connection has been aborted due to too many
+ * retransmissions.
+ */
+#define uip_timedout()    (uip_flags & UIP_TIMEDOUT)
+
+/* uip_rexmit():
+ *
+ * Reduces to non-zero if the previously sent data has been lost in
+ * the network, and the application should retransmit it. The
+ * application should set the uip_appdata buffer and the uip_len
+ * variable just as it did the last time this data was to be
+ * transmitted.
+ */
+#define uip_rexmit()     (uip_flags & UIP_REXMIT)
+
+/* uip_poll():
+ *
+ * Is non-zero if the reason the application is invoked is that the
+ * current connection has been idle for a while and should be
+ * polled.
+ */ 
+#define uip_poll()       (uip_flags & UIP_POLL)
+
+/* uip_mss():
+ *
+ * Gives the current maxium segment size (MSS) of the current
+ * connection.
+ */
+#define uip_mss()             (uip_conn->mss)
+
+
+/* uIP convenience and converting functions. */
+
+/* uip_ipaddr(&ipaddr, addr0,addr1,addr2,addr3):
+ *
+ * Packs an IP address into a two element 16-bit array. Such arrays
+ * are used to represent IP addresses in uIP.
+ */
+#define uip_ipaddr(addr, addr0,addr1,addr2,addr3) do { \
+                     (addr)[0] = htons(((addr0) << 8) | (addr1)); \
+                     (addr)[1] = htons(((addr2) << 8) | (addr3)); \
+                  } while(0)
+
+/* htons(), ntohs():
+ *
+ * Macros for converting 16-bit quantities between host and network
+ * byte order.
+ */
+#ifndef htons
+#   if BYTE_ORDER == BIG_ENDIAN
+#      define htons(n) (n)
+#   else /* BYTE_ORDER == BIG_ENDIAN */
+#      define htons(n) ((((u16_t)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))
+#   endif /* BYTE_ORDER == BIG_ENDIAN */
+#endif /* htons */
+
+#define ntohs(n) htons(n)
+
+
+/*-----------------------------------------------------------------------------------*/
+/* The following global variables are used for passing parameters
+ * between uIP, the network device driver and the application. */
+/*-----------------------------------------------------------------------------------*/
+
+/* u8_t uip_buf[UIP_BUFSIZE]:
+ *
+ * The uip_buf array is used to hold incoming and outgoing
+ * packets. The device driver fills this with incoming packets.
+ */
+extern u8_t uip_buf[UIP_BUFSIZE];
+
+/* u8_t *uip_appdata:
+ *
+ * This pointer points to the application data when the application is
+ * called. If the application wishes to send data, this is where the
+ * application should write it. The application can also point this to
+ * another location.
+ */
+extern volatile u8_t *uip_appdata; 
+
+#if UIP_URGDATA > 0 
+/* u8_t *uip_urgdata:
+ *
+ * This pointer points to any urgent data that has been received. Only
+ * present if compiled with support for urgent data (UIP_URGDATA).
+ */
+extern volatile u8_t *uip_urgdata; 
+#endif /* UIP_URGDATA > 0 */
+
+
+/* u[8|16]_t uip_len:
+ *
+ * When the application is called, uip_len contains the length of any
+ * new data that has been received from the remote host. The
+ * application should set this variable to the size of any data that
+ * the application wishes to send. When the network device driver
+ * output function is called, uip_len should contain the length of the
+ * outgoing packet.
+ */
+#if UIP_BUFSIZE > 255
+extern volatile u16_t uip_len, uip_slen;
+#else
+extern volatile u8_t uip_len, uip_slen;
+#endif 
+
+#if UIP_URGDATA > 0 
+extern volatile u8_t uip_urglen, uip_surglen;
+#endif /* UIP_URGDATA > 0 */
+
+extern volatile u8_t uip_acc32[4];
+
+/* struct uip_conn:
+ *
+ * The uip_conn structure is used for identifying a connection. All
+ * but one field in the structure are to be considered read-only by an
+ * application. The only exception is the appstate field whos purpose
+ * is to let the application store application-specific state (e.g.,
+ * file pointers) for the connection. The size of this field is
+ * configured in the "uipopt.h" header file.
+ */
+struct uip_conn {
+#if UIP_IPV6
+  u16_t ripaddr[8];   /* The IP address of the remote peer. */
+#else /* UIP_IPV6 */
+  u16_t ripaddr[2];   /* The IP address of the remote peer. */
+#endif /* UIP_IPV6 */
+  
+  u16_t lport, rport; /* The local and the remote port. */
+  
+  u8_t rcv_nxt[4];    /* The sequence number that we expect to receive
+			 next. */
+  u8_t snd_nxt[4];    /* The sequence number that was last sent by
+                         us. */
+#if UIP_TCP_MSS > 255
+  u16_t len;
+  u16_t mss;          /* Maximum segment size for the connection. */
+#else
+  u8_t len;
+  u8_t mss;
+#endif /* UIP_TCP_MSS */
+  u8_t sa, sv, rto;
+  u8_t tcpstateflags; /* TCP state and flags. */
+  u8_t timer;         /* The retransmission timer. */
+  u8_t nrtx;          /* Counts the number of retransmissions for a
+                         particular segment. */
+  
+  u8_t appstate[UIP_APPSTATE_SIZE];
+};
+
+/* struct uip_conn *uip_conn:
+ *
+ * When the application is called, uip_conn will point to the current
+ * conntection, the one that should be processed by the
+ * application. The uip_conns[] array is a list containing all
+ * connections.
+ */
+extern struct uip_conn *uip_conn;  
+extern struct uip_conn uip_conns[UIP_CONNS];
+
+#if UIP_UDP
+/* struct uip_udp_conn:
+ *
+ * The uip_udp_conn structure is used for identifying UDP
+ * "connections".
+ */
+struct uip_udp_conn {
+#if UIP_IPV6
+  u16_t ripaddr[8];   /* The IP address of the remote peer. */
+#else /* UIP_IPV6 */
+  u16_t ripaddr[2];   /* The IP address of the remote peer. */
+#endif /* UIP_IPV6 */
+  u16_t lport, rport;
+};
+
+extern struct uip_udp_conn *uip_udp_conn;
+extern struct uip_udp_conn uip_udp_conns[UIP_UDP_CONNS];
+#endif /* UIP_UDP */
+
+/* struct uip_stats:
+ *
+ * Contains statistics about the TCP/IP stack.
+ */
+struct uip_stats {
+  struct {
+    uip_stats_t drop;
+    uip_stats_t recv;
+    uip_stats_t sent;
+    uip_stats_t vhlerr;   /* Number of packets dropped due to wrong IP version
+			     or header length. */
+    uip_stats_t hblenerr; /* Number of packets dropped due to wrong IP length,
+			     high byte. */
+    uip_stats_t lblenerr; /* Number of packets dropped due to wrong IP length,
+			     low byte. */
+    uip_stats_t fragerr;  /* Number of packets dropped since they were IP
+			     fragments. */
+    uip_stats_t chkerr;   /* Number of packets dropped due to IP checksum errors. */
+    uip_stats_t protoerr; /* Number of packets dropped since they were neither
+			     ICMP nor TCP. */
+  } ip;
+  struct {
+    uip_stats_t drop;
+    uip_stats_t recv;
+    uip_stats_t sent;
+    uip_stats_t typeerr;
+  } icmp;
+  struct {
+    uip_stats_t drop;
+    uip_stats_t recv;
+    uip_stats_t sent;
+    uip_stats_t chkerr;
+    uip_stats_t ackerr;
+    uip_stats_t rst;
+    uip_stats_t rexmit;
+    uip_stats_t syndrop;  /* Number of dropped SYNs due to too few
+			     connections was avaliable. */
+    uip_stats_t synrst;   /* Number of SYNs for closed ports, triggering a
+			     RST. */
+  } tcp;
+};
+
+extern struct uip_stats uip_stat;
+
+
+/*-----------------------------------------------------------------------------------*/
+/* All the stuff below this point is internal to uIP and should not be
+ * used directly by an application or by a device driver.
+ */
+/*-----------------------------------------------------------------------------------*/
+/* u8_t uip_flags:
+ *
+ * When the application is called, uip_flags will contain the flags
+ * that are defined in this file. Please read below for more
+ * infomation.
+ */
+extern volatile u8_t uip_flags;
+
+/* The following flags may be set in the global variable uip_flags
+   before calling the application callback. The UIP_ACKDATA and
+   UIP_NEWDATA flags may both be set at the same time, whereas the
+   others are mutualy exclusive. Note that these flags should *NOT* be
+   accessed directly, but through the uIP functions/macros. */
+
+#define UIP_ACKDATA   1     /* Signifies that the outstanding data was
+			       acked and the application should send
+			       out new data instead of retransmitting
+			       the last data. */
+#define UIP_NEWDATA   2     /* Flags the fact that the peer has sent
+			       us new data. */
+#define UIP_REXMIT    4     /* Tells the application to retransmit the
+			       data that was last sent. */
+#define UIP_POLL      8     /* Used for polling the application, to
+			       check if the application has data that
+			       it wants to send. */
+#define UIP_CLOSE     16    /* The remote host has closed the
+			       connection, thus the connection has
+			       gone away. Or the application signals
+			       that it wants to close the
+			       connection. */
+#define UIP_ABORT     32    /* The remote host has aborted the
+			       connection, thus the connection has
+			       gone away. Or the application signals
+			       that it wants to abort the
+			       connection. */
+#define UIP_CONNECTED 64    /* We have got a connection from a remote
+                               host and have set up a new connection
+                               for it, or an active connection has
+                               been successfully established. */
+
+#define UIP_TIMEDOUT  128   /* The connection has been aborted due to
+			       too many retransmissions. */
+
+
+/* uip_process(flag):
+ *
+ * The actual uIP function which does all the work.
+ */
+void uip_process(u8_t flag);
+
+/* The following flags are passed as an argument to the uip_process()
+   function. They are used to distinguish between the two cases where
+   uip_process() is called. It can be called either because we have
+   incoming data that should be processed, or because the periodic
+   timer has fired. */
+
+#define UIP_DATA    1     /* Tells uIP that there is incoming data in
+                             the uip_buf buffer. The length of the
+                             data is stored in the global variable
+                             uip_len. */
+#define UIP_TIMER   2     /* Tells uIP that the periodic timer has
+                             fired. */
+#if UIP_UDP
+#define UIP_UDP_TIMER 3
+#endif /* UIP_UDP */
+
+/* The TCP states used in the uip_conn->tcpstateflags. */
+#define CLOSED      0
+#define SYN_RCVD    1
+#define SYN_SENT    2
+#define ESTABLISHED 3
+#define FIN_WAIT_1  4
+#define FIN_WAIT_2  5
+#define CLOSING     6
+#define TIME_WAIT   7
+#define LAST_ACK    8
+#define TS_MASK     15
+  
+#define UIP_STOPPED      16
+
+#if UIP_IPV6
+#define UIP_TCPIP_HLEN 60
+#else /* UIP_IPV6 */
+#define UIP_TCPIP_HLEN 40
+#endif /* UIP_IPV6 */
+
+/* The TCP and IP headers. */
+typedef struct {
+  /* IP header. */
+#if UIP_IPV6
+  u8_t vtc,
+    tcfl;
+  u16_t fl;
+  u8_t len[2];
+  u8_t nxthdr, hoplim;
+  u16_t srcipaddr[8],
+    destipaddr[8];
+#else /* UIP_IPV6 */
+  u8_t vhl,
+    tos,          
+    len[2],       
+    ipid[2],        
+    ipoffset[2],  
+    ttl,          
+    proto;     
+  u16_t ipchksum;
+  u16_t srcipaddr[2], 
+    destipaddr[2];
+#endif /* UIP_IPV6 */
+  
+  /* TCP header. */
+  u16_t srcport,
+    destport;
+  u8_t seqno[4],  
+    ackno[4],
+    tcpoffset,
+    flags,
+    wnd[2];     
+  u16_t tcpchksum;
+  u8_t urgp[2];
+  u8_t optdata[4];
+} uip_tcpip_hdr;
+
+/* The ICMP and IP headers. */
+typedef struct {
+  /* IP header. */
+#if UIP_IPV6
+  u16_t vtcfl;
+  u16_t fl;
+  u8_t len[2];
+  u8_t nxthdr, hoplim;
+  u16_t srcipaddr[8],
+    destipaddr[8];
+#else /* UIP_IPV6 */
+  u8_t vhl,
+    tos,          
+    len[2],       
+    ipid[2],        
+    ipoffset[2],  
+    ttl,          
+    proto;     
+  u16_t ipchksum;
+  u16_t srcipaddr[2], 
+    destipaddr[2];
+#endif /* UIP_IPV6 */
+  /* ICMP (echo) header. */
+  u8_t type, icode;
+  u16_t icmpchksum;
+  u16_t id, seqno;  
+} uip_icmpip_hdr;
+
+
+/* The UDP and IP headers. */
+typedef struct {
+  /* IP header. */
+#if UIP_IPV6
+  u8_t vtc,
+    tcfl;
+  u16_t fl;
+  u8_t len[2];
+  u8_t nxthdr, hoplim;
+  u16_t srcipaddr[8],
+    destipaddr[8];
+#else /* UIP_IPV6 */
+  u8_t vhl,
+    tos,          
+    len[2],       
+    ipid[2],        
+    ipoffset[2],  
+    ttl,          
+    proto;     
+  u16_t ipchksum;
+  u16_t srcipaddr[2], 
+    destipaddr[2];
+#endif /* UIP_IPV6 */
+  
+  /* UDP header. */
+  u16_t srcport,
+    destport;
+  u16_t udplen;
+  u16_t udpchksum;
+} uip_udpip_hdr;
+
+#define UIP_PROTO_ICMP  1
+#define UIP_PROTO_TCP   6
+#define UIP_PROTO_UDP   17
+
+#if UIP_FIXEDADDR
+#if UIP_IPV6
+extern const u16_t uip_hostaddr[8];
+#else /* UIP_IPV6 */
+extern const u16_t uip_hostaddr[2];
+#endif /* UIP_IPV6 */
+#else /* UIP_FIXEDADDR */
+#if UIP_IPV6
+extern u16_t uip_hostaddr[8];
+#else /* UIP_IPV6 */
+extern u16_t uip_hostaddr[2];
+#endif /* UIP_IPV6 */
+#endif /* UIP_FIXEDADDR */
+
+#endif /* __UIP_H__ */
+
+
+
+
+
+
+
diff --git a/contiki/uip/uip_arp.c b/contiki/uip/uip_arp.c
new file mode 100644
index 0000000..e758610
--- /dev/null
+++ b/contiki/uip/uip_arp.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2001-2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above copyright 
+ *    notice, this list of conditions and the following disclaimer in the 
+ *    documentation and/or other materials provided with the distribution. 
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Adam Dunkels.
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the uIP TCP/IP stack.
+ *
+ * $Id: uip_arp.c,v 1.1 2003/03/19 14:16:07 adamdunkels Exp $
+ *
+ */
+
+
+#include "uip_arp.h"
+
+struct arp_hdr {
+  struct uip_eth_hdr ethhdr;
+  u16_t hwtype;
+  u16_t protocol;
+  u8_t hwlen;
+  u8_t protolen;
+  u16_t opcode;
+  struct uip_eth_addr shwaddr;
+  u16_t sipaddr[2];
+  struct uip_eth_addr dhwaddr;
+  u16_t dipaddr[2]; 
+};
+
+struct ethip_hdr {
+  struct uip_eth_hdr ethhdr;
+  /* IP header. */
+  u8_t vhl,
+    tos,          
+    len[2],       
+    ipid[2],        
+    ipoffset[2],  
+    ttl,          
+    proto;     
+  u16_t ipchksum;
+  u16_t srcipaddr[2], 
+    destipaddr[2];
+};
+
+#define ARP_REQUEST 1
+#define ARP_REPLY   2
+
+#define ARP_HWTYPE_ETH 1
+
+u16_t uip_arp_draddr[2], uip_arp_netmask[2];
+
+struct arp_entry {
+  u16_t ipaddr[2];
+  struct uip_eth_addr ethaddr;
+  u8_t time;
+};
+
+static const struct uip_eth_addr ethaddr = {{UIP_ETHADDR0,
+					     UIP_ETHADDR1,
+					     UIP_ETHADDR2,
+					     UIP_ETHADDR3,
+					     UIP_ETHADDR4,
+					     UIP_ETHADDR5}};
+
+static struct arp_entry arp_table[UIP_ARPTAB_SIZE];
+static u16_t ipaddr[2];
+static u8_t i, c;
+
+static u8_t arptime;
+static u8_t tmpage;
+
+#define BUF   ((struct arp_hdr *)&uip_buf[0])
+#define IPBUF ((struct ethip_hdr *)&uip_buf[0])
+/*-----------------------------------------------------------------------------------*/
+void
+uip_arp_init(void)
+{
+  for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {
+    arp_table[i].ipaddr[0] =
+      arp_table[i].ipaddr[1] = 0;
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+void
+uip_arp_timer(void)
+{
+  ++arptime;
+  for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {
+    if((arp_table[i].ipaddr[0] | arp_table[i].ipaddr[1]) != 0 &&
+       arptime - arp_table[i].time >= UIP_ARP_MAXAGE) {
+      arp_table[i].ipaddr[0] =
+	arp_table[i].ipaddr[1] = 0;
+    }
+  }
+
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+uip_arp_update(u16_t *ipaddr, struct uip_eth_addr *ethaddr)
+{
+  /* Walk through the ARP mapping table and try to find an entry to
+     update. If none is found, the IP -> MAC address mapping is
+     inserted in the ARP table. */
+  for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {
+    
+    /* Only check those entries that are actually in use. */
+    if(arp_table[i].ipaddr[0] != 0 &&
+       arp_table[i].ipaddr[1] != 0) {
+
+      /* Check if the source IP address of the incoming packet matches
+         the IP address in this ARP table entry. */
+      if(ipaddr[0] == arp_table[i].ipaddr[0] &&
+	 ipaddr[1] == arp_table[i].ipaddr[1]) {
+	 
+	/* An old entry found, update this and return. */
+	for(c = 0; c < 6; ++c) {
+	  arp_table[i].ethaddr.addr[c] = ethaddr->addr[c];
+	}
+	arp_table[i].time = arptime;
+
+	return;
+      }
+    }
+  }
+
+  /* If we get here, no existing ARP table entry was found, so we
+     create one. */
+
+  /* First, we try to find an unused entry in the ARP table. */
+  for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {
+    if(arp_table[i].ipaddr[0] == 0 &&
+       arp_table[i].ipaddr[1] == 0) {
+      break;
+    }
+  }
+
+  /* If no unused entry is found, we try to find the oldest entry and
+     throw it away. */
+  if(i == UIP_ARPTAB_SIZE) {
+    tmpage = 0;
+    c = 0;
+    for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {
+      if(arptime - arp_table[i].time > tmpage) {
+	tmpage = arptime - arp_table[i].time;
+	c = i;
+      }
+    }
+    i = c;
+  }
+
+  /* Now, i is the ARP table entry which we will fill with the new
+     information. */
+  arp_table[i].ipaddr[0] = ipaddr[0];
+  arp_table[i].ipaddr[1] = ipaddr[1];
+  for(c = 0; c < 6; ++c) {
+    arp_table[i].ethaddr.addr[c] = ethaddr->addr[c];
+  }
+  arp_table[i].time = arptime;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+uip_arp_ipin(void)
+{
+
+  /* Only insert/update an entry if the source IP address of the
+     incoming IP packet comes from a host on the local network. */
+  /*  if((IPBUF->srcipaddr[0] & htons((UIP_NETMASK0 << 8) | UIP_NETMASK1)) !=
+     (uip_hostaddr[0]
+      & htons((UIP_NETMASK0 << 8) | UIP_NETMASK1)))
+    return;
+  if((IPBUF->srcipaddr[1] & htons((UIP_NETMASK2 << 8) | UIP_NETMASK3)) !=
+     (uip_hostaddr[1]
+      & htons((UIP_NETMASK2 << 8) | UIP_NETMASK3)))
+    return;
+  */
+  if((IPBUF->srcipaddr[0] & uip_arp_netmask[0]) !=
+     (uip_hostaddr[0] & uip_arp_netmask[0])) {
+    return;
+  }
+  if((IPBUF->srcipaddr[1] & uip_arp_netmask[1]) !=
+     (uip_hostaddr[1] & uip_arp_netmask[1])) {
+    return;
+  }
+  uip_arp_update(IPBUF->srcipaddr, &(IPBUF->ethhdr.src));
+  
+  return;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+uip_arp_arpin(void)
+{
+
+  if(uip_len < sizeof(struct arp_hdr)) {
+    uip_len = 0;
+    return;
+  }
+
+  uip_len = 0;
+  
+  switch(BUF->opcode) {
+  case htons(ARP_REQUEST):
+    /* ARP request. If it asked for our address, we send out a
+       reply. */
+    if(BUF->dipaddr[0] == uip_hostaddr[0] &&
+       BUF->dipaddr[1] == uip_hostaddr[1]) {
+      /* The reply opcode is 2. */
+      BUF->opcode = htons(2);
+
+      for(c = 0; c < 6; ++c) {
+        BUF->dhwaddr.addr[c] = BUF->shwaddr.addr[c];
+	BUF->shwaddr.addr[c] = 
+	  BUF->ethhdr.src.addr[c] = ethaddr.addr[c];
+	BUF->ethhdr.dest.addr[c] = BUF->dhwaddr.addr[c];
+      }
+
+      BUF->dipaddr[0] = BUF->sipaddr[0];
+      BUF->dipaddr[1] = BUF->sipaddr[1];
+      BUF->sipaddr[0] = uip_hostaddr[0];
+      BUF->sipaddr[1] = uip_hostaddr[1];
+
+      BUF->ethhdr.type = htons(UIP_ETHTYPE_ARP);      
+      uip_len = sizeof(struct arp_hdr);
+    }      
+    break;
+  case htons(ARP_REPLY):
+    /* ARP reply. We insert or update the ARP table if it was meant
+       for us. */
+    if(BUF->dipaddr[0] == uip_hostaddr[0] &&
+       BUF->dipaddr[1] == uip_hostaddr[1]) {
+
+      uip_arp_update(BUF->sipaddr, &BUF->shwaddr);
+    }
+    break;
+  }
+
+  return;
+}
+/*-----------------------------------------------------------------------------------*/
+void
+uip_arp_out(void)
+{
+  /* Find the destination IP address in the ARP table and construct
+     the Ethernet header. If the destination IP addres isn't on the
+     local network, we use the default router's IP address instead.
+
+     If not ARP table entry is found, we overwrite the original IP
+     packet with an ARP request for the IP address. */
+
+  /* Check if the destination address is on the local network. */
+  /*  if((IPBUF->destipaddr[0] & htons((UIP_NETMASK0 << 8) | UIP_NETMASK1)) !=
+     (uip_hostaddr[0]
+      & htons((UIP_NETMASK0 << 8) | UIP_NETMASK1)) ||
+     (IPBUF->destipaddr[1] & htons((UIP_NETMASK2 << 8) | UIP_NETMASK3)) !=
+     (uip_hostaddr[1]
+     & htons((UIP_NETMASK2 << 8) | UIP_NETMASK3))) {*/
+  if((IPBUF->destipaddr[0] & uip_arp_netmask[0]) !=
+     (uip_hostaddr[0] & uip_arp_netmask[0]) ||
+     (IPBUF->destipaddr[1] & uip_arp_netmask[1]) !=
+     (uip_hostaddr[1] & uip_arp_netmask[1])) {
+    /* Destination address was not on the local network, so we need to
+       use the default router's IP address instead of the destination
+       address when determining the MAC address. */
+    /*    ipaddr[0] = htons((UIP_DRIPADDR0 << 8) | UIP_DRIPADDR1);
+	  ipaddr[1] = htons((UIP_DRIPADDR2 << 8) | UIP_DRIPADDR3);*/
+    ipaddr[0] = uip_arp_draddr[0];
+    ipaddr[1] = uip_arp_draddr[1];
+  } else {
+    /* Else, we use the destination IP address. */
+    ipaddr[0] = IPBUF->destipaddr[0];
+    ipaddr[1] = IPBUF->destipaddr[1];
+  }
+      
+  for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {
+    if(ipaddr[0] == arp_table[i].ipaddr[0] &&
+       ipaddr[1] == arp_table[i].ipaddr[1])
+      break;
+  }
+
+  if(i == UIP_ARPTAB_SIZE) {
+    /* The destination address was not in our ARP table, so we
+       overwrite the IP packet with an ARP request. */
+
+    for(c = 0; c < 6; ++c) {     
+      BUF->ethhdr.dest.addr[c] = 0xff; /* Broadcast ARP request. */
+      BUF->ethhdr.src.addr[c] = 
+	BUF->shwaddr.addr[c] = ethaddr.addr[c];
+      BUF->dhwaddr.addr[c] = 0;
+    }
+    
+    BUF->dipaddr[0] = ipaddr[0];
+    BUF->dipaddr[1] = ipaddr[1];
+    BUF->sipaddr[0] = uip_hostaddr[0];
+    BUF->sipaddr[1] = uip_hostaddr[1];
+    BUF->opcode = htons(ARP_REQUEST); /* ARP request. */
+    BUF->hwtype = htons(ARP_HWTYPE_ETH);
+    BUF->protocol = htons(UIP_ETHTYPE_IP);
+    BUF->hwlen = 6;
+    BUF->protolen = 4;
+    BUF->ethhdr.type = htons(UIP_ETHTYPE_ARP);
+
+    uip_appdata = &uip_buf[40 + UIP_LLH_LEN];
+    
+    uip_len = sizeof(struct arp_hdr);
+    return;
+  }
+
+  /* Build an ethernet header. */
+  for(c = 0; c < 6; ++c) {
+    IPBUF->ethhdr.dest.addr[c] = arp_table[i].ethaddr.addr[c];
+    IPBUF->ethhdr.src.addr[c] = ethaddr.addr[c];
+  }
+  IPBUF->ethhdr.type = htons(UIP_ETHTYPE_IP);
+
+  uip_len += sizeof(struct uip_eth_hdr);
+}
+/*-----------------------------------------------------------------------------------*/
+
+
+
+
diff --git a/contiki/uip/uip_arp.h b/contiki/uip/uip_arp.h
new file mode 100644
index 0000000..415877a
--- /dev/null
+++ b/contiki/uip/uip_arp.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2001-2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above copyright 
+ *    notice, this list of conditions and the following disclaimer in the 
+ *    documentation and/or other materials provided with the distribution. 
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Adam Dunkels.
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the uIP TCP/IP stack.
+ *
+ * $Id: uip_arp.h,v 1.1 2003/03/19 14:16:07 adamdunkels Exp $
+ *
+ */
+
+#ifndef __UIP_ARP_H__
+#define __UIP_ARP_H__
+
+#include "uip.h"
+
+struct uip_eth_addr {
+  u8_t addr[6];
+};
+  
+struct uip_eth_hdr {
+  struct uip_eth_addr dest;
+  struct uip_eth_addr src;
+  u16_t type;
+};
+
+#define UIP_ETHTYPE_ARP 0x0806
+#define UIP_ETHTYPE_IP  0x0800
+#define UIP_ETHTYPE_IP6 0x86dd 
+
+
+/* The uip_arp_init() function must be called before any of the other
+   ARP functions. */
+void uip_arp_init(void);
+
+/* The uip_arp_ipin() function should be called whenever an IP packet
+   arrives from the Ethernet. This function refreshes the ARP table or
+   inserts a new mapping if none exists. The function assumes that an
+   IP packet with an Ethernet header is present in the uip_buf buffer
+   and that the length of the packet is in the uip_len variable. */
+void uip_arp_ipin(void);
+
+/* The uip_arp_arpin() should be called when an ARP packet is received
+   by the Ethernet driver. This function also assumes that the
+   Ethernet frame is present in the uip_buf buffer. When the
+   uip_arp_arpin() function returns, the contents of the uip_buf
+   buffer should be sent out on the Ethernet if the uip_len variable
+   is > 0. */
+void uip_arp_arpin(void);
+
+/* The uip_arp_out() function should be called when an IP packet
+   should be sent out on the Ethernet. This function creates an
+   Ethernet header before the IP header in the uip_buf buffer. The
+   Ethernet header will have the correct Ethernet MAC destination
+   address filled in if an ARP table entry for the destination IP
+   address (or the IP address of the default router) is present. If no
+   such table entry is found, the IP packet is overwritten with an ARP
+   request and we rely on TCP to retransmit the packet that was
+   overwritten. In any case, the uip_len variable holds the length of
+   the Ethernet frame that should be transmitted. */
+void uip_arp_out(void);
+
+/* The uip_arp_timer() function should be called every ten seconds. It
+   is responsible for flushing old entries in the ARP table. */
+void uip_arp_timer(void);
+
+
+#define uip_setdraddr(addr) do { uip_arp_draddr[0] = addr[0]; \
+                                 uip_arp_draddr[1] = addr[1]; } while(0)
+#define uip_setnetmask(addr) do { uip_arp_netmask[0] = addr[0]; \
+                                  uip_arp_netmask[1] = addr[1]; } while(0)
+
+
+/* Internal variables that are set using the macros uip_setdraddr and
+   uip_setnetmask. */
+extern u16_t uip_arp_draddr[2], uip_arp_netmask[2];
+#endif /* __UIP_ARP_H__ */
diff --git a/contiki/uip/uip_main.c b/contiki/uip/uip_main.c
new file mode 100644
index 0000000..7d8bbc2
--- /dev/null
+++ b/contiki/uip/uip_main.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2001, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above copyright 
+ *    notice, this list of conditions and the following disclaimer in the 
+ *    documentation and/or other materials provided with the distribution. 
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Adam Dunkels.
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the uIP TCP/IP stack.
+ *
+ * $Id: uip_main.c,v 1.1 2003/03/19 14:16:07 adamdunkels Exp $
+ *
+ */
+
+
+/* uip_main.c: initialization code and main event loop. */
+
+#define NULL (void *)0
+
+
+
+#include "uip.h"
+#include "uip_arp.h"
+
+#ifdef WITH_TFE
+#include "cs8900a.h"
+#endif /* WITH_TFE */
+
+#include <time.h>
+
+#define BUF ((struct uip_eth_hdr *)&uip_buf[0])
+
+static u8_t i, arptimer;
+static u16_t start, current;
+
+static void idle(void);
+static struct dispatcher_proc p =
+  {DISPATCHER_PROC("uIP TCP/IP stack", idle, NULL, NULL)};
+static ek_id_t id;
+
+
+/*-----------------------------------------------------------------------------------*/
+static void
+timer(void)
+{
+  for(i = 0; i < UIP_CONNS; ++i) {
+    uip_periodic(i);
+    if(uip_len > 0) {
+#ifdef WITH_TFE
+      uip_arp_out();
+      cs8900a_send();
+#endif /* WITH_TFE */
+#ifdef WITH_RS232
+      rs232dev_send();
+#endif /* WITH_RS232 */
+    }
+  }
+
+  for(i = 0; i < UIP_UDP_CONNS; i++) {
+    uip_udp_periodic(i);
+    /* If the above function invocation resulted in data that
+       should be sent out on the network, the global variable
+       uip_len is set to a value > 0. */
+    if(uip_len > 0) {
+#ifdef WITH_TFE
+      uip_arp_out();
+      cs8900a_send();
+#endif /* WITH_TFE */
+#ifdef WITH_RS232
+      rs232dev_send();
+#endif /* WITH_RS232 */
+    }
+  }   
+}
+/*-----------------------------------------------------------------------------------*/
+void
+uip_main_init(void)
+{
+  u16_t ipaddr[2];
+
+  id = dispatcher_start(&p);
+  
+  arptimer = 0;
+  start = clock();
+  
+}
+/*-----------------------------------------------------------------------------------*/
+static void
+idle(void)
+{
+#ifdef WITH_TFE
+  /* Poll Ethernet device to see if there is a frame avaliable. */
+  uip_len = cs8900a_poll();
+  if(uip_len > 0) {
+    /* A frame was avaliable (and is now read into the uip_buf), so
+       we process it. */ 
+    if(BUF->type == htons(UIP_ETHTYPE_IP)) {
+      uip_arp_ipin();
+      uip_len -= sizeof(struct uip_eth_hdr);
+      uip_input();
+      /* If the above function invocation resulted in data that
+	 should be sent out on the network, the global variable
+	 uip_len is set to a value > 0. */
+      if(uip_len > 0) {
+	uip_arp_out();
+	cs8900a_send();
+      }
+    } else if(BUF->type == htons(UIP_ETHTYPE_ARP)) {
+      uip_arp_arpin();
+      /* If the above function invocation resulted in data that
+	 should be sent out on the network, the global variable
+	 uip_len is set to a value > 0. */	
+      if(uip_len > 0) {
+	cs8900a_send();
+      }
+    }
+  }
+#endif /* WITH_TFE */
+  
+#ifdef WITH_RS232
+  uip_len = rs232dev_poll();
+  if(uip_len > 0) {
+    uip_process(UIP_DATA);
+    if(uip_len > 0) {
+      rs232dev_send();
+    }
+  }
+#endif /* WITH_RS232 */
+
+  /* Check the clock so see if we should call the periodic uIP
+     processing. */
+  current = clock();
+
+  if((current - start) >= CLK_TCK/2) {
+    timer();
+    start = current;
+  }    
+}
+/*-----------------------------------------------------------------------------------*/
+unsigned char
+uip_main_ipaddrconv(char *addrstr, unsigned char *ipaddr)
+{
+  unsigned char tmp;
+  char c;
+  unsigned char i, j;
+
+  tmp = 0;
+  
+  for(i = 0; i < 4; ++i) {
+    j = 0;
+    do {
+      c = *addrstr;
+      ++j;
+      if(j > 4) {
+	return 0;
+      }
+      if(c == '.' || c == 0) {
+	*ipaddr = tmp;
+	++ipaddr;
+	tmp = 0;
+      } else if(c >= '0' || c <= '9') {
+	tmp = (tmp * 10) + (c - '0');
+      } else {
+	return 0;
+      }
+      ++addrstr;
+    } while(c != '.' && c != 0);
+  }
+  return 1;
+}
+
+/*-----------------------------------------------------------------------------------*/
diff --git a/contiki/uip/uip_main.h b/contiki/uip/uip_main.h
new file mode 100644
index 0000000..96fdcba
--- /dev/null
+++ b/contiki/uip/uip_main.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2002, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution. 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *        This product includes software developed by Adam Dunkels. 
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the Contiki desktop environment for the C64.
+ *
+ * $Id: uip_main.h,v 1.1 2003/03/19 14:16:07 adamdunkels Exp $
+ *
+ */
+#ifndef __UIP_MAIN_H__
+#define __UIP_MAIN_H__
+
+void uip_main_init(void);
+unsigned char uip_main_ipaddrconv(char *addrstr, unsigned char *addr);
+
+#endif /* __UIP_MAIN_H__ */
diff --git a/contiki/uip/uipopt.h b/contiki/uip/uipopt.h
new file mode 100644
index 0000000..ef4de36
--- /dev/null
+++ b/contiki/uip/uipopt.h
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2001, Adam Dunkels.
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 2. Redistributions in binary form must reproduce the above copyright 
+ *    notice, this list of conditions and the following disclaimer in the 
+ *    documentation and/or other materials provided with the distribution. 
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Adam Dunkels.
+ * 4. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.  
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
+ *
+ * This file is part of the uIP TCP/IP stack.
+ *
+ * $Id: uipopt.h,v 1.1 2003/03/19 14:16:07 adamdunkels Exp $
+ *
+ */
+
+#ifndef __UIPOPT_H__
+#define __UIPOPT_H__
+
+/* This file is used for tweaking various configuration options for
+   uIP. You should make a copy of this file into one of your project's
+   directories instead of editing this example "uipopt.h" file that
+   comes with the uIP distribution. */
+
+/*-----------------------------------------------------------------------------------*/
+/* First, two typedefs that may have to be tweaked for your particular
+   compiler. The uX_t types are unsigned integer types, where the X is
+   the number of bits in the integer type. Most compilers use
+   "unsigned char" and "unsigned short" for those two,
+   respectively. */
+typedef unsigned char u8_t;
+typedef unsigned short u16_t;
+typedef unsigned long u32_t;
+typedef unsigned long uip_stats_t;
+
+#include <string.h>
+#define bcopy(s,d,l) memcpy(d,s,l)
+
+/*-----------------------------------------------------------------------------------*/
+/* The configuration options for a specific node. This includes IP
+ * address, netmask and default router as well as the Ethernet
+ * address. The netmask, default router and Ethernet address are
+ * appliciable only if uIP should be run over Ethernet.
+ *
+ * All of these should be changed to suit your project.
+*/
+#define UIP_URGDATA 0
+#define UIP_PINGADDRCONF 0
+
+#define UIP_UDP 1
+#define UIP_UDP_CHECKSUMS 0
+#define UIP_UDP_CONNS 1
+#define UIP_UDP_APPCALL udp_appcall
+void udp_appcall(void);
+
+#define UIP_FIXEDADDR 0
+/* UIP_IPADDR: The IP address of this uIP node. */
+/*#define UIP_IPADDR0     192
+#define UIP_IPADDR1     168
+#define UIP_IPADDR2     0
+#define UIP_IPADDR3     2*/
+
+/* UIP_NETMASK: The netmask. */
+#define UIP_NETMASK0    255
+#define UIP_NETMASK1    255
+#define UIP_NETMASK2    255
+#define UIP_NETMASK3    0
+
+/* UIP_DRIPADDR: IP address of the default router. */
+#define UIP_DRIPADDR0   192
+#define UIP_DRIPADDR1   168
+#define UIP_DRIPADDR2   1
+#define UIP_DRIPADDR3   1
+
+/* UIP_ETHADDR: The Ethernet address. */
+#define UIP_ETHADDR0    0x00
+#define UIP_ETHADDR1    0x00
+#define UIP_ETHADDR2    0x00
+#define UIP_ETHADDR3    0x64
+#define UIP_ETHADDR4    0x64
+#define UIP_ETHADDR5    0x64
+
+
+/*-----------------------------------------------------------------------------------*/
+/* The following options are used to configure application specific
+ * setting such as how many TCP ports that should be avaliable and if
+ * the uIP should be configured to support active opens.
+ *
+ * These should probably be tweaked to suite your project.
+ */
+
+/* Include the header file for the application program that should be
+   used. If you don't use the example web server, you should change
+   this. */
+#include "dispatcher.h"
+
+/* UIP_ACTIVE_OPEN: Determines if support for opening connections from
+   uIP should be compiled in. If this isn't needed for your
+   application, don't turn it on. (A web server doesn't need this, for
+   instance.) */
+#define UIP_ACTIVE_OPEN 1
+
+/* UIP_CONNS: The maximum number of simultaneously active
+   connections. */
+#define UIP_CONNS       6
+
+/* UIP_LISTENPORTS: The maximum number of simultaneously listening TCP
+   ports. For a web server, 1 is enough here. */
+#define UIP_LISTENPORTS 6
+
+/* UIP_BUFSIZE: The size of the buffer that holds incoming and
+   outgoing packets. */
+#ifdef WITH_ETHERNET
+#define UIP_BUFSIZE     360
+#else /* WITH_ETHERNET */
+#define UIP_BUFSIZE     360
+#endif /* WITH_ETHERNET */
+
+/* UIP_STATISTICS: Determines if statistics support should be compiled
+   in. The statistics is useful for debugging and to show the user. */
+#define UIP_STATISTICS  0
+
+/* UIP_LOGGING: Determines if logging of certain events should be
+   compiled in. Useful mostly for debugging. The function uip_log(char
+   *msg) must be implemented to suit your architecture if logging is
+   turned on. */
+#define UIP_LOGGING     0
+
+/* UIP_LLH_LEN: The link level header length; this is the offset into
+   the uip_buf where the IP header can be found. For Ethernet, this
+   should be set to 14. For SLIP, this should be set to 0. */
+#ifdef WITH_ETHERNET
+#define UIP_LLH_LEN     14
+#else /* WITH_ETHERNET */
+#define UIP_LLH_LEN     0
+#endif /* WITH_ETHERNET */
+
+/*-----------------------------------------------------------------------------------*/
+/* The following configuration options can be tweaked for your
+ * project, but you are probably safe to use the default values. The
+ * options are listed in order of tweakability.
+ */
+
+/* UIP_ARPTAB_SIZE: The size of the ARP table - use a larger value if
+   this uIP node will have many connections from the local network. */
+#define UIP_ARPTAB_SIZE 8
+
+/* The maxium age of ARP table entries measured in 10ths of
+   seconds. An UIP_ARP_MAXAGE of 120 corresponds to 20 minutes (BSD
+   default). */
+#define UIP_ARP_MAXAGE 120
+
+/* UIP_RTO: The retransmission timeout counted in timer pulses (i.e.,
+   the speed of the periodic timer, usually one second). */
+#define UIP_RTO         3
+
+/* UIP_MAXRTX: The maximum number of times a segment should be
+   retransmitted before the connection should be aborted. */
+#define UIP_MAXSYNRTX   8
+#define UIP_MAXRTX      8
+
+/* UIP_TCP_MSS: The TCP maximum segment size. This should be set to
+   at most UIP_BUFSIZE - UIP_LLH_LEN - 40. */
+#define UIP_TCP_MSS     (UIP_BUFSIZE - UIP_LLH_LEN - 42)
+
+/* UIP_TTL: The IP TTL (time to live) of IP packets sent by uIP. */
+#define UIP_TTL         255
+
+/* UIP_TIME_WAIT_TIMEOUT: How long a connection should stay in the
+   TIME_WAIT state. Has no real implication, so it should be left
+   untouched. */
+#define UIP_TIME_WAIT_TIMEOUT 120
+/*-----------------------------------------------------------------------------------*/
+/* This is where you configure if your CPU architecture is big or
+ * little endian. Most CPUs today are little endian. The most notable
+ * exception are the Motorolas which are big endian. Tweak the
+ * definition of the BYTE_ORDER macro to configure uIP for your
+ * project.
+ */
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN  3412
+#endif /* LITTLE_ENDIAN */
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN     1234
+#endif /* BIGE_ENDIAN */
+
+#ifndef BYTE_ORDER
+#define BYTE_ORDER     LITTLE_ENDIAN
+#endif /* BYTE_ORDER */
+
+#endif /* __UIPOPT_H__ */