* released 1.2.4
* merged Alexander Lazic's and Klaus Wagner's work on application
  cookie-based persistence. Since this is the first merge, this version is
  not intended for general use and reports are more than welcome. Some
  documentation is really needed though.
diff --git a/CHANGELOG b/CHANGELOG
index 8057f87..df96dd6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,12 @@
 ChangeLog :
 ===========
 
+2005/01/22 : 1.2.4
+  - merged Alexander Lazic's and Klaus Wagner's work on application
+    cookie-based persistence. Since this is the first merge, this version is
+    not intended for general use and reports are more than welcome. Some
+    documentation is really needed though.
+
 2005/01/22 : 1.2.3 (1.1.30)
   - add an architecture guide to the documentation
   - released without any changes
diff --git a/Makefile b/Makefile
index 7caf492..29a79fc 100644
--- a/Makefile
+++ b/Makefile
@@ -73,7 +73,7 @@
 REGEX_OPTS=$(COPTS.$(REGEX))
 CPU_OPTS=$(COPTS.$(CPU))
 
-COPTS=$(CPU_OPTS) $(TARGET_OPTS) $(REGEX_OPTS) $(SMALL_OPTS)
+COPTS=-I. $(CPU_OPTS) $(TARGET_OPTS) $(REGEX_OPTS) $(SMALL_OPTS)
 LIBS=$(LIBS.$(TARGET)) $(LIBS.$(REGEX))
 
 # - use -DSTATTIME=0 to disable statistics, else specify an interval in
@@ -84,12 +84,12 @@
 
 all: haproxy
 
-haproxy: haproxy.o
+haproxy: src/list.o src/chtbl.o src/hashpjw.o haproxy.o
 	$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
 
 %.o:	%.c
 	$(CC) $(CFLAGS) -c -o $@ $<
 
 clean:
-	rm -f *.[oas] *~ *.rej core haproxy test nohup.out gmon.out
+	rm -f *.[oas] *~ *.rej core haproxy test nohup.out gmon.out src/*.[oas]
 
diff --git a/TODO b/TODO
index 46e2912..047468b 100644
--- a/TODO
+++ b/TODO
@@ -136,7 +136,7 @@
 * listen [ip4.ip4.ip4.ip4]:port[-port]
 * listen [ip6::...ip6]/port[-port]
 - server xxx ipv4 | ipv4: | ipv4:port[-port] | ipv6/ | ipv6/port[-port]
-- appcookie
+* appcookie
 - weighted round robin
 - option to shutdown(listen_sock) when max connections reached
 
diff --git a/haproxy.c b/haproxy.c
index b6682b4..87c4145 100644
--- a/haproxy.c
+++ b/haproxy.c
@@ -57,7 +57,51 @@
 #include <linux/netfilter_ipv4.h>
 #endif
 
-#define HAPROXY_VERSION "1.2.3"
+#if defined(__dietlibc__)
+#include <strings.h>
+#endif
+
+#define TBLSIZ 10
+#define TBLCHKINT 5000 /* The time between two calls of appsession_refresh in ms */
+
+/*
+  These Parts are copied from
+  
+  http://www.oreilly.com/catalog/masteralgoc/index.html
+  Mastering Algorithms with C
+  By Kyle Loudon
+  ISBN: 1-56592-453-3
+  Publishd by O'Reilly
+
+  We have added our own struct to these function.
+ */
+
+#include <include/list.h>
+#include <include/chtbl.h>
+#include <include/hashpjw.h>
+/* end of copied parts */
+
+struct app_pool {
+    void **sessid;
+    void **serverid;
+    int ses_waste, ses_use, ses_msize;
+    int ser_waste, ser_use, ser_msize;
+};
+
+struct app_pool apools;
+int have_appsession;
+
+/* Callback for hash_lookup */
+int match_str(const void *key1, const void *key2);
+
+/* Callback for destroy */
+void destroy(void *data);
+
+#if defined(DEBUG_HASH)
+static void print_table(const CHTbl *htbl);
+#endif
+
+#define HAPROXY_VERSION "1.2.4"
 #define HAPROXY_DATE	"2005/01/22"
 
 /* this is for libc5 for example */
@@ -236,8 +280,10 @@
 #define sizeof_session	sizeof(struct session)
 #define sizeof_buffer	sizeof(struct buffer)
 #define sizeof_fdtab	sizeof(struct fdtab)
+#define sizeof_curappsession	CAPTURE_LEN	/* current_session pool */
 #define sizeof_requri	REQURI_LEN
 #define sizeof_capture	CAPTURE_LEN
+#define sizeof_appsess	sizeof(struct appsessions)
 
 /* different possible states for the sockets */
 #define FD_STCLOSE	0
@@ -501,7 +547,12 @@
     struct server *srv, *cursrv;	/* known servers, current server */
     int nbservers;			/* # of servers */
     char *cookie_name;			/* name of the cookie to look for */
-    int  cookie_len;			/* strlen(cookie_len), computed only once */
+    int  cookie_len;			/* strlen(cookie_name), computed only once */
+    char *appsession_name;		/* name of the cookie to look for */
+    int  appsession_name_len;		/* strlen(appsession_name), computed only once */
+    int  appsession_len;		/* length of the appsession cookie value to be used */
+    int  appsession_timeout;
+    CHTbl htbl_proxy;			/* Per Proxy hashtable */
     char *capture_name;			/* beginning of the name of the cookie to capture */
     int  capture_namelen;		/* length of the cookie name to match */
     int  capture_len;			/* length of the string to be captured */
@@ -598,7 +649,8 @@
     **pool_fdtab    = NULL,
     **pool_requri   = NULL,
     **pool_task	    = NULL,
-    **pool_capture  = NULL;
+    **pool_capture  = NULL,
+    **pool_appsess  = NULL;
 
 struct proxy *proxy  = NULL;	/* list of all existing proxies */
 struct fdtab *fdtab = NULL;	/* array of all the file descriptors */
@@ -745,6 +797,10 @@
 int event_srv_write(int fd);
 int process_session(struct task *t);
 
+static int appsession_task_init(void);
+static int appsession_init(void);
+static int appsession_refresh(struct task *t);
+
 /*********************************************************************/
 /*  general purpose functions  ***************************************/
 /*********************************************************************/
@@ -1605,7 +1661,9 @@
 int connect_server(struct session *s) {
     int fd;
 
-    //    fprintf(stderr,"connect_server : s=%p\n",s);
+#ifdef DEBUG_FULL
+    fprintf(stderr,"connect_server : s=%p\n",s);
+#endif
 
     if (s->flags & SN_DIRECT) { /* srv cannot be null */
 	s->srv_addr = s->srv->addr;
@@ -1729,7 +1787,9 @@
     struct buffer *b = s->req;
     int ret, max;
 
-    //    fprintf(stderr,"event_cli_read : fd=%d, s=%p\n", fd, s);
+#ifdef DEBUG_FULL
+    fprintf(stderr,"event_cli_read : fd=%d, s=%p\n", fd, s);
+#endif
 
     if (fdtab[fd].state != FD_STERROR) {
 	while (1) {
@@ -1824,7 +1884,9 @@
     struct buffer *b = s->rep;
     int ret, max;
 
-    //    fprintf(stderr,"event_srv_read : fd=%d, s=%p\n", fd, s);
+#ifdef DEBUG_FULL
+    fprintf(stderr,"event_srv_read : fd=%d, s=%p\n", fd, s);
+#endif
 
     if (fdtab[fd].state != FD_STERROR) {
 	while (1) {
@@ -1918,7 +1980,9 @@
     struct buffer *b = s->rep;
     int ret, max;
 
-    //    fprintf(stderr,"event_cli_write : fd=%d, s=%p\n", fd, s);
+#ifdef DEBUG_FULL
+    fprintf(stderr,"event_cli_write : fd=%d, s=%p\n", fd, s);
+#endif
 
     if (b->l == 0) { /* let's realign the buffer to optimize I/O */
 	b->r = b->w = b->h = b->lr  = b->data;
@@ -2005,7 +2069,9 @@
     struct buffer *b = s->req;
     int ret, max;
 
-    //fprintf(stderr,"event_srv_write : fd=%d, s=%p\n", fd, s);
+#ifdef DEBUG_FULL
+    fprintf(stderr,"event_srv_write : fd=%d, s=%p\n", fd, s);
+#endif
 
     if (b->l == 0) { /* let's realign the buffer to optimize I/O */
 	b->r = b->w = b->h = b->lr = b->data;
@@ -2694,6 +2760,9 @@
     int c = t->cli_state;
     struct buffer *req = t->req;
     struct buffer *rep = t->rep;
+    int method_checked = 0;
+    appsess *asession_temp = NULL;
+    appsess local_asession;
 
 #ifdef DEBUG_FULL
     fprintf(stderr,"process_cli: c=%s, s=%s\n", cli_stnames[c], srv_stnames[s]);
@@ -2707,6 +2776,7 @@
 	while (req->lr < req->r) { /* this loop only sees one header at each iteration */
 	    char *ptr;
 	    int delete_header;
+	    char *request_line = NULL;
 	
 	    ptr = req->lr;
 
@@ -2815,6 +2885,88 @@
 	     *   req->r  = end of data (not used at this stage)
 	     */
 
+	    if (!method_checked && (t->proxy->appsession_name != NULL) &&
+		((memcmp(req->h, "GET ", 4) == 0) || (memcmp(req->h, "POST ", 4) == 0)) &&
+		((request_line = memchr(req->h, ';', req->lr - req->h)) != NULL)) {
+
+	      /* skip ; */
+	      request_line++;
+
+	      /* look if we have a jsessionid */
+
+	      if (strncasecmp(request_line, t->proxy->appsession_name, t->proxy->appsession_name_len) == 0) {
+
+		/* skip jsessionid= */
+		request_line += t->proxy->appsession_name_len + 1;
+		
+		/* First try if we allready have an appsession */
+		asession_temp = &local_asession;
+		
+		if ((asession_temp->sessid = pool_alloc_from(apools.sessid, apools.ses_msize)) == NULL) {
+		  Alert("Not enough memory process_cli():asession_temp->sessid:calloc().\n");
+		  send_log(t->proxy, LOG_ALERT, "Not enough Memory process_cli():asession_temp->sessid:calloc().\n");
+		  return 0;
+		}
+
+		/* Copy the sessionid */
+		memcpy(asession_temp->sessid, request_line, t->proxy->appsession_len);
+		asession_temp->sessid[t->proxy->appsession_len] = 0;
+		asession_temp->serverid = NULL;
+
+		/* only do insert, if lookup fails */
+		if (chtbl_lookup(&(t->proxy->htbl_proxy), (void *)&asession_temp)) {
+		  if ((asession_temp = pool_alloc(appsess)) == NULL) {
+		    Alert("Not enough memory process_cli():asession:calloc().\n");
+		    send_log(t->proxy, LOG_ALERT, "Not enough memory process_cli():asession:calloc().\n");
+		    return 0;
+		  }
+		  asession_temp->sessid = local_asession.sessid;
+		  asession_temp->serverid = local_asession.serverid;
+		  chtbl_insert(&(t->proxy->htbl_proxy), (void *) asession_temp);
+		} /* end if(chtbl_lookup()) */
+		else{
+		  /*free wasted memory;*/
+		  pool_free_to(apools.sessid, local_asession.sessid);
+		}
+
+		tv_delayfrom(&asession_temp->expire, &now, t->proxy->appsession_timeout);
+		asession_temp->request_count++;
+		
+#if defined(DEBUG_HASH)
+		print_table(&(t->proxy->htbl_proxy));
+#endif
+
+		if (asession_temp->serverid == NULL) {
+		    Alert("Found Application Session without matching server.\n");
+		} else {
+		    struct server *srv = t->proxy->srv;
+		    while (srv) {
+		        if (strcmp(srv->id, asession_temp->serverid) == 0) {
+		            if (srv->state & SRV_RUNNING || t->proxy->options & PR_O_PERSIST) {
+		                /* we found the server and it's usable */
+			        t->flags &= ~SN_CK_MASK;
+			        t->flags |= SN_CK_VALID | SN_DIRECT;
+			        t->srv = srv;
+				break;
+		            }else {
+			        t->flags &= ~SN_CK_MASK;
+			        t->flags |= SN_CK_DOWN;
+			    }
+		        }/* end if(strcmp()) */
+		        srv = srv->next;
+		    }/* end while(srv) */
+		}/* end else of if (asession_temp->serverid == NULL) */
+		
+		method_checked = 1;
+	      }/* end if(strncasecmp(request_line,t->proxy->appsession_name,apssesion_name_len) == 0) */
+	      else {
+		//fprintf(stderr,">>>>>>>>>>>>>>>>>>>>>>NO SESSION\n");
+	      }
+	    }/* end if(!method_checked ...) */
+	    else{
+	      //printf("No Methode-Header with Session-String\n");
+	    }
+	    
 	    if (t->logs.logwait & LW_REQ) {
 		/* we have a complete HTTP request that we must log */
 		int urilen;
@@ -2932,7 +3084,8 @@
 	     * remove it. If no application cookie persists in the header, we
 	     * *MUST* delete it
 	     */
-	    if (!delete_header && (t->proxy->cookie_name != NULL || t->proxy->capture_name != NULL)
+	    if (!delete_header &&
+		(t->proxy->cookie_name != NULL || t->proxy->capture_name != NULL || t->proxy->appsession_name !=NULL)
 		&& !(t->flags & SN_CLDENY) && (ptr >= req->h + 8)
 		&& (strncasecmp(req->h, "Cookie: ", 8) == 0)) {
 		char *p1, *p2, *p3, *p4;
@@ -3001,12 +3154,12 @@
 
 			    if ((t->logs.cli_cookie = pool_alloc(capture)) == NULL) {
 				Alert("HTTP logging : out of memory.\n");
+			    } else {
+				if (log_len > t->proxy->capture_len)
+				    log_len = t->proxy->capture_len;
+				memcpy(t->logs.cli_cookie, p1, log_len);
+				t->logs.cli_cookie[log_len] = 0;
 			    }
-
-			    if (log_len > t->proxy->capture_len)
-				log_len = t->proxy->capture_len;
-			    memcpy(t->logs.cli_cookie, p1, log_len);
-			    t->logs.cli_cookie[log_len] = 0;
 			}
 
 			if ((p2 - p1 == t->proxy->cookie_len) && (t->proxy->cookie_name != NULL) &&
@@ -3051,8 +3204,7 @@
 					t->flags |= SN_CK_VALID | SN_DIRECT;
 					t->srv = srv;
 					break;
-				    }
-				    else {
+				    } else {
 					/* we found a server, but it's down */
 					t->flags &= ~SN_CK_MASK;
 					t->flags |= SN_CK_DOWN;
@@ -3086,8 +3238,7 @@
 				del_cookie = p1;
 				del_colon = colon;
 			    }
-			}
-			else {
+			} else {
 			    /* now we know that we must keep this cookie since it's
 			     * not ours. But if we wanted to delete our cookie
 			     * earlier, we cannot remove the complete header, but we
@@ -3102,6 +3253,66 @@
 				del_cookie = del_colon = NULL;
 			    }
 			}
+			
+			if ((t->proxy->appsession_name != NULL) &&
+				  (memcmp(p1, t->proxy->appsession_name, p2 - p1) == 0)) {
+			    /* first, let's see if the cookie is our appcookie*/
+			    
+			    /* Cool... it's the right one */
+
+			    asession_temp = &local_asession;
+			  
+			    if ((asession_temp->sessid = pool_alloc_from(apools.sessid, apools.ses_msize)) == NULL) {
+				Alert("Not enough memory process_cli():asession->sessid:malloc().\n");
+				send_log(t->proxy, LOG_ALERT, "Not enough memory process_cli():asession->sessid:malloc().\n");
+				return 0;
+			    }
+			  
+			    memcpy(asession_temp->sessid, p3, t->proxy->appsession_len);
+			    asession_temp->sessid[t->proxy->appsession_len] = 0;
+			    asession_temp->serverid = NULL;
+			    
+			    /* only do insert, if lookup fails */
+			    if (chtbl_lookup(&(t->proxy->htbl_proxy), (void *) &asession_temp) != 0) {
+				if ((asession_temp = pool_alloc(appsess)) == NULL) {
+				    Alert("Not enough memory process_cli():asession:calloc().\n");
+				    send_log(t->proxy, LOG_ALERT, "Not enough memory process_cli():asession:calloc().\n");
+				    return 0;
+				}
+				
+				asession_temp->sessid = local_asession.sessid;
+				asession_temp->serverid = local_asession.serverid;
+				chtbl_insert(&(t->proxy->htbl_proxy), (void *) asession_temp);
+			    }
+			    else{
+				/* free wasted memory */
+				pool_free_to(apools.sessid, local_asession.sessid);
+			    }
+			    
+			    if (asession_temp->serverid == NULL) {
+				Alert("Found Application Session without matching server.\n");
+			    } else {
+				struct server *srv = t->proxy->srv;
+				while (srv) {
+				    if(strcmp(srv->id, asession_temp->serverid) == 0) {
+					if (srv->state & SRV_RUNNING || t->proxy->options & PR_O_PERSIST) {
+					    /* we found the server and it's usable */
+					    t->flags &= ~SN_CK_MASK;
+					    t->flags |= SN_CK_VALID | SN_DIRECT;
+					    t->srv = srv;
+					    break;
+					} else {
+					    t->flags &= ~SN_CK_MASK;
+					    t->flags |= SN_CK_DOWN;
+					}
+				    }
+				    srv = srv->next;
+				}/* end while(srv) */
+			    }/* end else if server == NULL */
+			    
+			    tv_delayfrom(&asession_temp->expire, &now, t->proxy->appsession_timeout);
+			    break;
+			}/* end if ((t->proxy->appsession_name != NULL) ... */
 		    }
 
 		    /* we'll have to look for another cookie ... */
@@ -3413,6 +3624,8 @@
     int c = t->cli_state;
     struct buffer *req = t->req;
     struct buffer *rep = t->rep;
+    appsess *asession_temp = NULL;
+    appsess local_asession;
 
 #ifdef DEBUG_FULL
     fprintf(stderr,"process_srv: c=%s, s=%s\n", cli_stnames[c], srv_stnames[s]);
@@ -3829,7 +4042,7 @@
 
 	    /* check for server cookies */
 	    if (!delete_header /*&& (t->proxy->options & PR_O_COOK_ANY)*/
-		&& (t->proxy->cookie_name != NULL || t->proxy->capture_name != NULL)
+		&& (t->proxy->cookie_name != NULL || t->proxy->capture_name != NULL || t->proxy->appsession_name !=NULL)
 		&& (strncasecmp(rep->h, "Set-Cookie: ", 12) == 0)) {
 		char *p1, *p2, *p3, *p4;
 		
@@ -3915,6 +4128,58 @@
 			}
 			break;
 		    }
+
+		    /* first, let's see if the cookie is our appcookie*/
+		    if ((t->proxy->appsession_name != NULL) &&
+			(memcmp(p1, t->proxy->appsession_name, p2 - p1) == 0)) {
+
+		      /* Cool... it's the right one */
+
+		      size_t server_id_len = strlen(t->srv->id)+1;
+		      asession_temp = &local_asession;
+		      
+		      if((asession_temp->sessid = pool_alloc_from(apools.sessid, apools.ses_msize)) == NULL){
+			Alert("Not enought Memory process_srv():asession->sessid:malloc().\n");
+			send_log(t->proxy, LOG_ALERT, "Not enought Memory process_srv():asession->sessid:malloc().\n");
+		      }
+		      memcpy(asession_temp->sessid, p3, t->proxy->appsession_len);
+		      asession_temp->sessid[t->proxy->appsession_len] = 0;
+		      asession_temp->serverid = NULL;
+
+		      /* only do insert, if lookup fails */
+		      if (chtbl_lookup(&(t->proxy->htbl_proxy), (void *) &asession_temp) != 0) {
+	                  if ((asession_temp = pool_alloc(appsess)) == NULL) {
+		              Alert("Not enought Memory process_srv():asession:calloc().\n");
+		              send_log(t->proxy, LOG_ALERT, "Not enought Memory process_srv():asession:calloc().\n");
+            	              return 0;
+                          }
+			  asession_temp->sessid = local_asession.sessid;
+			  asession_temp->serverid = local_asession.serverid;
+			  chtbl_insert(&(t->proxy->htbl_proxy), (void *) asession_temp);
+		      }/* end if(chtbl_lookup()) */
+		      else
+		      {
+		      	/* free wasted memory */
+		      	pool_free_to(apools.sessid, local_asession.sessid);
+		      } /* end else from if(chtbl_lookup()) */
+		      
+		      if(asession_temp->serverid == NULL){
+		        if((asession_temp->serverid = pool_alloc_from(apools.serverid, apools.ser_msize)) == NULL){
+			  Alert("Not enought Memory process_srv():asession->sessid:malloc().\n");
+			  send_log(t->proxy, LOG_ALERT, "Not enought Memory process_srv():asession->sessid:malloc().\n");
+		        }
+			asession_temp->serverid[0] = '\0';
+		      }
+		      
+		      if(asession_temp->serverid[0] == '\0') memcpy(asession_temp->serverid,t->srv->id,server_id_len);
+		      
+		      tv_delayfrom(&asession_temp->expire, &now, t->proxy->appsession_timeout);
+
+#if defined(DEBUG_HASH)
+		      print_table(&(t->proxy->htbl_proxy));
+#endif
+		      break;
+		    }/* end if ((t->proxy->appsession_name != NULL) ... */
 		    else {
 			//	fprintf(stderr,"Ignoring unknown cookie : ");
 			//	write(2, p1, p2-p1);
@@ -4828,8 +5093,41 @@
 		 s->req->l, s->rep?s->rep->l:0, s->cli_fd
 		 );
     }
+}
+
+static void fast_stop(void)
+{
+    struct proxy *p;
+    p = proxy;
+    while (p) {
+        p->grace = 0;
+	p = p->next;
+    }
+    soft_stop();
 }
 
+void sig_int(int sig) {
+    /* This would normally be a hard stop,
+       but we want to be sure about deallocation,
+       and so on, so we do a soft stop with
+       0 GRACE time
+    */
+    fast_stop();
+    /* If we are killed twice, we decide to die*/
+    signal(sig, SIG_DFL);
+}
+
+void sig_term(int sig) {
+    /* This would normally be a hard stop,
+       but we want to be sure about deallocation,
+       and so on, so we do a soft stop with
+       0 GRACE time
+    */
+    fast_stop();
+    /* If we are killed twice, we decide to die*/
+    signal(sig, SIG_DFL);
+}
+
 void chain_regex(struct hdr_exp **head, regex_t *preg, int action, char *replace) {
     struct hdr_exp *exp;
 
@@ -5003,6 +5301,7 @@
 int cfg_parse_listen(char *file, int linenum, char **args) {
     static struct proxy *curproxy = NULL;
     struct server *newsrv = NULL;
+    int rc;
 
     if (!strcmp(args[0], "listen")) {  /* new proxy */
 	if (!*args[1]) {
@@ -5195,7 +5494,36 @@
 		  file, linenum);
 	    return -1;
 	}
-    }
+    }/* end else if (!strcmp(args[0], "cookie"))  */
+    else if (!strcmp(args[0], "appsession")) {  /* cookie name */
+//	  if (curproxy == &defproxy) {
+//	      Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+//	      return -1;
+//	  }
+
+	if (curproxy->appsession_name != NULL) {
+//	      Alert("parsing [%s:%d] : cookie name already specified. Continuing.\n",
+//		    file, linenum);
+//	      return 0;
+	    free(curproxy->appsession_name);
+	}
+	
+	if (*(args[5]) == 0) {
+	  Alert("parsing [%s:%d] : '%s' expects 'appsession' <cookie_name> 'len' <len> 'timeout' <timeout>.\n",
+		file, linenum, args[0]);
+	  return -1;
+	}
+	have_appsession = 1;
+	curproxy->appsession_name = strdup(args[1]);
+	curproxy->appsession_name_len = strlen(curproxy->appsession_name);
+	curproxy->appsession_len = atoi(args[3]);
+	curproxy->appsession_timeout = atoi(args[5]);
+	rc = chtbl_init(&(curproxy->htbl_proxy), TBLSIZ, hashpjw, match_str, destroy);
+	if (rc) {
+	    Alert("Error Init Appsession Hashtable.\n");
+	    return -1;
+	}
+    } /* Url App Session */
     else if (!strcmp(args[0], "capture")) {
 	if (!strcmp(args[1], "cookie")) {  /* name of a cookie to capture */
 	    //	  if (curproxy == &defproxy) {
@@ -6438,10 +6766,13 @@
 
     gethostname(hostname, MAX_HOSTNAME_LEN);
 
+    have_appsession = 0;
     if (readcfgfile(cfg_cfgfile) < 0) {
 	Alert("Error reading configuration file : %s\n", cfg_cfgfile);
 	exit(1);
     }
+    if (have_appsession)
+	appsession_init();
 
     if (global.mode & MODE_CHECK) {
 	qfprintf(stdout, "Configuration file is valid : %s\n", cfg_cfgfile);
@@ -6576,6 +6907,154 @@
     return 0;
 }
 
+int match_str(const void *key1, const void *key2){
+
+    appsess *temp1,*temp2;
+    temp1 = (appsess *)key1;
+    temp2 = (appsess *)key2;
+
+    //fprintf(stdout,">>>>>>>>>>>>>>temp1->sessid :%s:\n",temp1->sessid);
+    //fprintf(stdout,">>>>>>>>>>>>>>temp2->sessid :%s:\n",temp2->sessid);
+  
+    return (strcmp(temp1->sessid,temp2->sessid) == 0);
+}/* end match_str */
+
+void destroy(void *data){
+    appsess *temp1;
+
+    //printf("destroy called\n");
+    temp1 = (appsess *)data;
+
+    if (temp1->sessid)
+	pool_free_to(apools.sessid, temp1->sessid);
+
+    if (temp1->serverid)
+	pool_free_to(apools.serverid, temp1->serverid);
+
+    pool_free(appsess, temp1);
+} /* end destroy */
+
+void appsession_cleanup( void )
+{
+    struct proxy *p = proxy;
+  
+    while(p) {
+	chtbl_destroy(&(p->htbl_proxy));
+	p = p->next;
+    }
+}/* end appsession_cleanup() */
+
+void pool_destroy(void **pool)
+{
+    void *temp, *next;
+    next = pool;
+    while (next) {
+	temp = next;
+	next = *(void **)temp;
+	free(temp);
+    }
+}/* end pool_destroy() */
+
+void deinit(void){
+    struct proxy *p = proxy;
+    struct cap_hdr *h,*h_next;
+    struct server *s,*s_next;
+    struct listener *l,*l_next;
+  
+    while (p) {
+	if (p->id)
+	    free(p->id);
+
+	if (p->check_req)
+	    free(p->check_req);
+
+	if (p->cookie_name)
+	    free(p->cookie_name);
+
+	if (p->capture_name)
+	    free(p->capture_name);
+
+	/* only strup if the user have set in config.
+	   When should we free it?!
+	   if(p->errmsg.msg400) free(p->errmsg.msg400);
+	   if(p->errmsg.msg403) free(p->errmsg.msg403);
+	   if(p->errmsg.msg408) free(p->errmsg.msg408);
+	   if(p->errmsg.msg500) free(p->errmsg.msg500);
+	   if(p->errmsg.msg502) free(p->errmsg.msg502);
+	   if(p->errmsg.msg503) free(p->errmsg.msg503);
+	   if(p->errmsg.msg504) free(p->errmsg.msg504);
+	*/
+	if (p->appsession_name)
+	    free(p->appsession_name);
+
+	h = p->req_cap;
+	while (h) {
+	    h_next = h->next;
+	    if (h->name)
+		free(h->name);
+	    pool_destroy(h->pool);
+	    free(h);
+	    h = h_next;
+	}/* end while(h) */
+
+	h = p->rsp_cap;
+	while (h) {
+	    h_next = h->next;
+	    if (h->name)
+		free(h->name);
+	    
+	    pool_destroy(h->pool);
+	    free(h);
+	    h = h_next;
+	}/* end while(h) */
+	
+	s = p->srv;
+	while (s) {
+	    s_next = s->next;
+	    if(s->id)
+		free(s->id);
+	    
+	    if(s->cookie)
+		free(s->cookie);
+	    
+	    free(s);
+	    s = s_next;
+	}/* end while(s) */
+	
+	l = p->listen;
+	while (l) {
+	    l_next = l->next;
+	    free(l);
+	    l = l_next;
+	}/* end while(l) */
+	
+	pool_destroy((void **) p->req_cap_pool);
+	pool_destroy((void **) p->rsp_cap_pool);
+	p = p->next;
+    }/* end while(p) */
+    
+    if (global.chroot)    free(global.chroot);
+    if (global.pidfile)   free(global.pidfile);
+    
+    if (ReadEvent)        free(ReadEvent);
+    if (WriteEvent)       free(WriteEvent);
+    if (StaticReadEvent)  free(StaticReadEvent);
+    if (StaticWriteEvent) free(StaticWriteEvent);
+    if (fdtab)            free(fdtab);
+    
+    pool_destroy(pool_session);
+    pool_destroy(pool_buffer);
+    pool_destroy(pool_fdtab);
+    pool_destroy(pool_requri);
+    pool_destroy(pool_task);
+    pool_destroy(pool_capture);
+    pool_destroy(pool_appsess);
+    
+    if (have_appsession) {
+        pool_destroy(apools.serverid);
+        pool_destroy(apools.sessid);
+    }
+} /* end deinit() */
 
 int main(int argc, char **argv) {
     FILE *pidfile = NULL;
@@ -6590,6 +7069,8 @@
     signal(SIGQUIT, dump);
     signal(SIGUSR1, sig_soft_stop);
     signal(SIGHUP, sig_dump_state);
+    signal(SIGINT, sig_int);
+    signal(SIGTERM, sig_term);
 
     /* on very high loads, a sigpipe sometimes happen just between the
      * getsockopt() which tells "it's OK to write", and the following write :-(
@@ -6676,5 +7157,152 @@
 
     select_loop();
 
+    /* Free all Hash Keys and all Hash elements */
+    appsession_cleanup();
+    /* Do some cleanup */ 
+    deinit();
+    
     exit(0);
 }
+
+#if defined(DEBUG_HASH)
+static void print_table(const CHTbl *htbl) {
+
+    ListElmt           *element;
+    int                i;
+    appsess *asession;
+
+    /*****************************************************************************
+     *                                                                            *
+     *  Display the chained hash table.                                           *
+     *                                                                            *
+     *****************************************************************************/
+    
+    fprintf(stdout, "Table size is %d\n", chtbl_size(htbl));
+    
+    for (i = 0; i < TBLSIZ; i++) {
+	fprintf(stdout, "Bucket[%03d]\n", i);
+	
+	for (element = list_head(&htbl->table[i]); element != NULL; element = list_next(element)) {
+	    //fprintf(stdout, "%c", *(char *)list_data(element));
+	    asession = (appsess *)list_data(element);
+	    fprintf(stdout, "ELEM :%s:", asession->sessid);
+	    fprintf(stdout, " Server :%s: \n", asession->serverid);
+	    //fprintf(stdout, " Server request_count :%li:\n",asession->request_count);
+	}
+	
+	fprintf(stdout, "\n");
+    }
+    return;
+} /* end print_table */
+#endif
+
+static int appsession_init(void)
+{
+    static int          initialized = 0;
+    int                 idlen;
+    struct server       *s;
+    struct proxy        *p = proxy;
+    
+    if (!initialized) {
+	if (!appsession_task_init()) {
+	    apools.sessid = NULL;
+	    apools.serverid = NULL;
+	    apools.ser_waste = 0;
+	    apools.ser_use = 0;
+	    apools.ser_msize = sizeof(void *);
+	    apools.ses_waste = 0;
+	    apools.ses_use = 0;
+	    apools.ses_msize = sizeof(void *);
+	    while (p) {
+		s = p->srv;
+		if (apools.ses_msize < p->appsession_len)
+		    apools.ses_msize = p->appsession_len;
+		while (s) {
+		    idlen = strlen(s->id);
+		    if (apools.ser_msize < idlen)
+			apools.ser_msize = idlen;
+		    s = s->next;
+		}
+		p = p->next;
+	    }
+	    apools.ser_msize ++; /* we use strings, so reserve space for '\0' */
+	    apools.ses_msize ++;
+	}
+	else {
+	    fprintf(stderr, "appsession_task_init failed\n");
+	    return -1;
+	}
+	initialized ++;
+    }
+    return 0;
+}
+
+static int appsession_task_init(void)
+{
+    static int initialized = 0;
+    struct task *t;
+    if (!initialized) {
+	if ((t = pool_alloc(task)) == NULL)
+	    return -1;
+	t->next = t->prev = t->rqnext = NULL;
+	t->wq = LIST_HEAD(wait_queue);
+	t->state = TASK_IDLE;
+	t->context = NULL;
+	tv_delayfrom(&t->expire, &now, TBLCHKINT);
+	task_queue(t);
+	t->process = appsession_refresh;
+	initialized ++;
+    }
+    return 0;
+}
+
+static int appsession_refresh(struct task *t) {
+    struct proxy       *p = proxy;
+    CHTbl              *htbl;
+    ListElmt           *element, *last;
+    int                i;
+    appsess            *asession;
+    void               *data;
+
+    while (p) {
+        if (p->appsession_name != NULL) {
+            htbl = &p->htbl_proxy;
+            /* if we ever give up the use of TBLSIZ, we need to change this */
+            for (i = 0; i < TBLSIZ; i++) {
+	        last = NULL;
+                for (element = list_head(&htbl->table[i]); element != NULL; element = list_next(element)) {
+                    asession = (appsess *)list_data(element);
+                    if (tv_cmp2_ms(&asession->expire, &now) <= 0) {
+                        if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
+                            int len;
+			    /*
+			      on Linux NULL pointers are catched by sprintf, on solaris -> segfault 
+			    */
+                            len = sprintf(trash, "appsession_refresh: cleaning up expired Session '%s' on Server %s\n", 
+			                  asession->sessid,  asession->serverid?asession->serverid:"(null)");
+                            write(1, trash, len);
+                        }
+			/* delete the expired element from within the hash table */
+                        if ((list_rem_next(&htbl->table[i], last, (void **)&data) == 0) 
+			    && (htbl->table[i].destroy != NULL)) {
+			    htbl->table[i].destroy(data);
+			}
+			if (last == NULL) {/* patient lost his head, get a new one */
+			    element = list_head(&htbl->table[i]);
+			    if (element == NULL) break; /* no heads left, go to next patient */
+			}
+			else
+			    element = last;
+                    }/* end if (tv_cmp2_ms(&asession->expire, &now) <= 0) */
+                    else
+			last = element;
+                }/* end  for (element = list_head(&htbl->table[i]); element != NULL; element = list_next(element)) */
+            }
+	}
+        p = p->next;
+    }
+    tv_delayfrom(&t->expire, &now, TBLCHKINT); /* check expiration every 5 seconds */
+    return TBLCHKINT;
+} /* end appsession_refresh */
+
diff --git a/include/chtbl.h b/include/chtbl.h
new file mode 100644
index 0000000..87b777a
--- /dev/null
+++ b/include/chtbl.h
@@ -0,0 +1,62 @@
+/*
+  This File is copied from
+  
+  http://www.oreilly.com/catalog/masteralgoc/index.html
+  Mastering Algorithms with C
+  By Kyle Loudon
+  ISBN: 1-56592-453-3
+  Publishd by O'Reilly
+  
+ */
+
+/*****************************************************************************
+*                                                                            *
+*  ------------------------------- chtbl.h --------------------------------  *
+*                                                                            *
+*****************************************************************************/
+
+#ifndef CHTBL_H
+#define CHTBL_H
+
+#include <stdlib.h>
+
+#include "list.h"
+
+/*****************************************************************************
+*                                                                            *
+*  Define a structure for chained hash tables.                               *
+*                                                                            *
+*****************************************************************************/
+
+typedef struct CHTbl_ {
+
+  int                buckets;
+
+  int                (*h)(const void *key);
+  int                (*match)(const void *key1, const void *key2);
+  void               (*destroy)(void *data);
+
+  int                size;
+  List               *table;
+} CHTbl;
+
+/*****************************************************************************
+ *                                                                            *
+ *  --------------------------- Public Interface ---------------------------  *
+ *                                                                            *
+ *****************************************************************************/
+
+int chtbl_init(CHTbl *htbl, int buckets, int (*h)(const void *key), int
+	       (*match)(const void *key1, const void *key2), void (*destroy)(void *data));
+
+void chtbl_destroy(CHTbl *htbl);
+
+int chtbl_insert(CHTbl *htbl, const void *data);
+
+int chtbl_remove(CHTbl *htbl, void **data);
+
+int chtbl_lookup(const CHTbl *htbl, void **data);
+
+#define chtbl_size(htbl) ((htbl)->size)
+
+#endif
diff --git a/include/hashpjw.h b/include/hashpjw.h
new file mode 100644
index 0000000..a20b5b3
--- /dev/null
+++ b/include/hashpjw.h
@@ -0,0 +1,47 @@
+/*
+  This File is copied from
+  
+  http://www.oreilly.com/catalog/masteralgoc/index.html
+  Mastering Algorithms with C
+  By Kyle Loudon
+  ISBN: 1-56592-453-3
+  Publishd by O'Reilly
+
+  We have added our own struct to these function.
+ */
+
+/*****************************************************************************
+*                                                                            *
+*  ------------------------------- hashpjw.h ------------------------------  *
+*                                                                            *
+*****************************************************************************/
+
+#ifndef HASHPJW_H
+#define HASHPJW_H
+
+#include <sys/time.h>
+
+typedef struct appsessions {
+    char *sessid;
+    char *serverid;
+    struct timeval expire;		/* next expiration time for this application session */
+    unsigned long int request_count;
+} appsess; /* end struct appsessions */
+
+/*****************************************************************************
+*                                                                            *
+*  Define a table size for demonstration purposes only.                      *
+*                                                                            *
+*****************************************************************************/
+
+#define            PRIME_TBLSIZ       1699
+
+/*****************************************************************************
+*                                                                            *
+*  --------------------------- Public Interface ---------------------------  *
+*                                                                            *
+*****************************************************************************/
+
+int hashpjw(const void *key);
+
+#endif
diff --git a/include/list.h b/include/list.h
new file mode 100644
index 0000000..ae7f086
--- /dev/null
+++ b/include/list.h
@@ -0,0 +1,78 @@
+/*
+  This File is copied from
+  
+  http://www.oreilly.com/catalog/masteralgoc/index.html
+  Mastering Algorithms with C
+  By Kyle Loudon
+  ISBN: 1-56592-453-3
+  Publishd by O'Reilly
+  
+ */
+
+/*****************************************************************************
+*                                                                            *
+*  -------------------------------- list.h --------------------------------  *
+*                                                                            *
+*****************************************************************************/
+
+#ifndef LIST_H
+#define LIST_H
+
+#include <stdlib.h>
+
+/*****************************************************************************
+ *                                                                            *
+ *  Define a structure for linked list elements.                              *
+ *                                                                            *
+ *****************************************************************************/
+
+typedef struct ListElmt_ {
+
+  void               *data;
+  struct ListElmt_   *next;
+} ListElmt;
+
+/*****************************************************************************
+ *                                                                            *
+ *  Define a structure for linked lists.                                      *
+ *                                                                            *
+ *****************************************************************************/
+
+typedef struct List_ {
+  int                size;
+  int                (*match)(const void *key1, const void *key2);
+  void               (*destroy)(void *data);
+  
+  ListElmt           *head;
+  ListElmt           *tail;
+} List;
+
+/*****************************************************************************
+ *                                                                            *
+ *  --------------------------- Public Interface ---------------------------  *
+ *                                                                            *
+ *****************************************************************************/
+
+void list_init(List *list, void (*destroy)(void *data));
+
+void list_destroy(List *list);
+
+int list_ins_next(List *list, ListElmt *element, const void *data);
+
+int list_rem_next(List *list, ListElmt *element, void **data);
+
+#define list_size(list) ((list)->size)
+
+#define list_head(list) ((list)->head)
+
+#define list_tail(list) ((list)->tail)
+
+#define list_is_head(list, element) ((element) == (list)->head ? 1 : 0)
+
+#define list_is_tail(element) ((element)->next == NULL ? 1 : 0)
+
+#define list_data(element) ((element)->data)
+
+#define list_next(element) ((element)->next)
+
+#endif
diff --git a/src/chtbl.c b/src/chtbl.c
new file mode 100644
index 0000000..481b3ac
--- /dev/null
+++ b/src/chtbl.c
@@ -0,0 +1,260 @@
+/*
+  This File is copied from
+  
+  http://www.oreilly.com/catalog/masteralgoc/index.html
+  Mastering Algorithms with C
+  By Kyle Loudon
+  ISBN: 1-56592-453-3
+  Publishd by O'Reilly
+  
+ */
+
+/*****************************************************************************
+ *                                                                            *
+ *  ------------------------------- chtbl.c --------------------------------  *
+ *                                                                            *
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <include/list.h>
+#include <include/chtbl.h>
+
+/*****************************************************************************
+ *                                                                            *
+ *  ------------------------------ chtbl_init ------------------------------  *
+ *                                                                            *
+ *****************************************************************************/
+
+int chtbl_init(CHTbl *htbl, int buckets, int (*h)(const void *key), int
+	       (*match)(const void *key1, const void *key2), void (*destroy)(void*data)) {
+
+  int                i;
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Allocate space for the hash table.                                        *
+   *                                                                            *
+   *****************************************************************************/
+
+  if ((htbl->table = (List *)malloc(buckets * sizeof(List))) == NULL)
+    return -1;
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Initialize the buckets.                                                   *
+   *                                                                            *
+   *****************************************************************************/
+
+  htbl->buckets = buckets;
+
+  for (i = 0; i < htbl->buckets; i++)
+    list_init(&htbl->table[i], destroy);
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Encapsulate the functions.                                                *
+   *                                                                            *
+   *****************************************************************************/
+
+  htbl->h = h;
+  htbl->match = match;
+  htbl->destroy = destroy;
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Initialize the number of elements in the table.                           *
+   *                                                                            *
+   *****************************************************************************/
+
+  htbl->size = 0;
+
+  return 0;
+} /* end chtbl_init () */
+
+/*****************************************************************************
+ *                                                                            *
+ *  ---------------------------- chtbl_destroy -----------------------------  *
+ *                                                                            *
+ *****************************************************************************/
+
+void chtbl_destroy(CHTbl *htbl) {
+
+  int                i;
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Destroy each bucket.                                                      *
+   *                                                                            *
+   *****************************************************************************/
+
+  for (i = 0; i < htbl->buckets; i++) {
+    list_destroy(&htbl->table[i]);
+  } /* end for () */
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Free the storage allocated for the hash table.                            *
+   *                                                                            *
+   *****************************************************************************/
+
+  free(htbl->table);
+
+  /*****************************************************************************
+   *                                                                            *
+   *  No operations are allowed now, but clear the structure as a precaution.   *
+   *                                                                            *
+   *****************************************************************************/
+
+  memset(htbl, 0, sizeof(CHTbl));
+  
+  return;
+} /* end chtbl_destroy() */
+
+/*****************************************************************************
+ *                                                                            *
+ *  ----------------------------- chtbl_insert -----------------------------  *
+ *                                                                            *
+ *****************************************************************************/
+
+int chtbl_insert(CHTbl *htbl, const void *data) {
+
+  void               *temp;
+  int                bucket,retval;
+ 
+  /*****************************************************************************
+   *                                                                            *
+   *  Do nothing if the data is already in the table.                           *
+   *                                                                            *
+   *****************************************************************************/
+
+  temp = (void *)data;
+
+  if (chtbl_lookup(htbl, &temp) == 0)
+    return 1;
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Hash the key.                                                             *
+   *                                                                            *
+   *****************************************************************************/
+
+  bucket = htbl->h(data) % htbl->buckets;
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Insert the data into the bucket.                                          *
+   *                                                                            *
+   *****************************************************************************/
+
+  if ((retval = list_ins_next(&htbl->table[bucket], NULL, data)) == 0)
+    htbl->size++;
+
+  return retval;
+} /* end chtbl_insert() */
+
+/*****************************************************************************
+ *                                                                            *
+ *  ----------------------------- chtbl_remove -----------------------------  *
+ *                                                                            *
+ *****************************************************************************/
+
+int chtbl_remove(CHTbl *htbl, void **data) {
+
+  ListElmt           *element, *prev;
+  int                bucket;
+ 
+  /*****************************************************************************
+   *                                                                            *
+   *  Hash the key.                                                             *
+   *                                                                            *
+   *****************************************************************************/
+
+  bucket = htbl->h(*data) % htbl->buckets;
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Search for the data in the bucket.                                        *
+   *                                                                            *
+   *****************************************************************************/
+
+  prev = NULL;
+
+  for (element = list_head(&htbl->table[bucket]); element != NULL; element = list_next(element)) {
+    if (htbl->match(*data, list_data(element))) {
+
+      /***********************************************************************
+       *                                                                      *
+       *  Remove the data from the bucket.                                    *
+       *                                                                      *
+       ***********************************************************************/
+
+      if (list_rem_next(&htbl->table[bucket], prev, data) == 0) {
+	htbl->size--;
+	return 0;
+      } /* end if() */
+      else {
+	return -1;
+      }/* end else */
+    }/* end if (htbl->match(*data, list_data(element))) */
+    
+    prev = element;
+  }/* end for() */
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Return that the data was not found.                                       *
+   *                                                                            *
+   *****************************************************************************/
+
+  return -1;
+} /* end int chtbl_remove(CHTbl *htbl, void **data) */
+
+/*****************************************************************************
+ *                                                                            *
+ *  ----------------------------- chtbl_lookup -----------------------------  *
+ *                                                                            *
+ *****************************************************************************/
+
+int chtbl_lookup(const CHTbl *htbl, void **data) {
+
+  ListElmt           *element;
+  int                bucket;
+ 
+  /*****************************************************************************
+   *                                                                            *
+   *  Hash the key.                                                             *
+   *                                                                            *
+   *****************************************************************************/
+
+  bucket = htbl->h(*data) % htbl->buckets;
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Search for the data in the bucket.                                        *
+   *                                                                            *
+   *****************************************************************************/
+
+  for (element = list_head(&htbl->table[bucket]); element != NULL; element = list_next(element)) {
+    if (htbl->match(*data, list_data(element))) {
+
+      /***********************************************************************
+       *                                                                      *
+       *  Pass back the data from the table.                                  *
+       *                                                                      *
+       ***********************************************************************/
+
+      *data = list_data(element);
+      return 0;
+    }/* end if() */
+  }/* end for() */
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Return that the data was not found.                                       *
+   *                                                                            *
+   *****************************************************************************/
+
+  return -1;
+} /* end chtbl_lookup() */
diff --git a/src/hashpjw.c b/src/hashpjw.c
new file mode 100644
index 0000000..ef7f209
--- /dev/null
+++ b/src/hashpjw.c
@@ -0,0 +1,62 @@
+/*
+  This File is copied from
+  
+  http://www.oreilly.com/catalog/masteralgoc/index.html
+  Mastering Algorithms with C
+  By Kyle Loudon
+  ISBN: 1-56592-453-3
+  Publishd by O'Reilly
+
+  We have added our own struct to these function.
+ */
+
+/*****************************************************************************
+*                                                                            *
+*  ------------------------------- hashpjw.c ------------------------------  *
+*                                                                            *
+*****************************************************************************/
+
+#include <include/hashpjw.h>
+
+/*****************************************************************************
+*                                                                            *
+*  -------------------------------- hashpjw -------------------------------  *
+*                                                                            *
+*****************************************************************************/
+
+int hashpjw(const void *key) {
+
+  const char         *ptr;
+  unsigned int        val;
+  AppSess *appsession_temp;
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Hash the key by performing a number of bit operations on it.              *
+   *                                                                            *
+   *****************************************************************************/
+
+  val = 0;
+  appsession_temp = (AppSess *)key;
+  ptr = appsession_temp->sessid;
+
+  while (*ptr != '\0') {
+
+    int tmp;
+
+    val = (val << 4) + (*ptr);
+
+    if((tmp = (val & 0xf0000000))) {
+      val = val ^ (tmp >> 24);
+      val = val ^ tmp;
+    }
+    ptr++;
+  }/* end while */
+
+  /*****************************************************************************
+   *                                                                            *
+   *  In practice, replace PRIME_TBLSIZ with the actual table size.             *
+   *                                                                            *
+   *****************************************************************************/
+  return val % PRIME_TBLSIZ;
+}/* end hashpjw */
diff --git a/src/list.c b/src/list.c
new file mode 100644
index 0000000..364ed14
--- /dev/null
+++ b/src/list.c
@@ -0,0 +1,228 @@
+/*
+  This File is copied from
+  
+  http://www.oreilly.com/catalog/masteralgoc/index.html
+  Mastering Algorithms with C
+  By Kyle Loudon
+  ISBN: 1-56592-453-3
+  Publishd by O'Reilly
+  
+ */
+
+/*****************************************************************************
+ *                                                                            *
+ *  -------------------------------- list.c --------------------------------  *
+ *                                                                            *
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <include/list.h>
+
+/*****************************************************************************
+*                                                                            *
+*  ------------------------------- list_init ------------------------------  *
+*                                                                            *
+*****************************************************************************/
+
+void list_init(List *list, void (*destroy)(void *data)) {
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Initialize the list.                                                      *
+   *                                                                            *
+   *****************************************************************************/
+
+  list->size = 0;
+  list->destroy = destroy;
+  list->head = NULL;
+  list->tail = NULL;
+  return;
+} /* end list_init() */
+
+/*****************************************************************************
+ *                                                                            *
+ *  ----------------------------- list_destroy -----------------------------  *
+ *                                                                            *
+ *****************************************************************************/
+
+void list_destroy(List *list) {
+
+  void               *data;
+  int rc; 
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Remove each element.                                                      *
+   *                                                                            *
+   *****************************************************************************/
+
+  while (list_size(list) > 0) {
+    
+    rc = list_rem_next(list, NULL, (void **)&data);
+    
+    if (( rc == 0) && (list->destroy != NULL)) { 
+
+      /***********************************************************************
+       *                                                                      *
+       *  Call a user-defined function to free dynamically allocated data.    *
+       *                                                                      *
+       ***********************************************************************/
+
+      list->destroy(data);
+    }/* end if() */
+  }/* end while() */
+
+  /*****************************************************************************
+   *                                                                            *
+   *  No operations are allowed now, but clear the structure as a precaution.   *
+   *                                                                            *
+   *****************************************************************************/
+
+  memset(list, 0, sizeof(List));
+  return;
+} /* void list_destroy(List *list) */
+
+/*****************************************************************************
+ *                                                                            *
+ *  ----------------------------- list_ins_next ----------------------------  *
+ *                                                                            *
+ *****************************************************************************/
+
+int list_ins_next(List *list, ListElmt *element, const void *data) {
+
+  ListElmt           *new_element;
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Allocate storage for the element.                                         *
+   *                                                                            *
+   *****************************************************************************/
+
+  if ((new_element = (ListElmt *)malloc(sizeof(ListElmt))) == NULL)
+    return -1;
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Insert the element into the list.                                         *
+   *                                                                            *
+   *****************************************************************************/
+
+  new_element->data = (void *)data;
+
+  if (element == NULL) {
+
+    /**************************************************************************
+     *                                                                         *
+     *  Handle insertion at the head of the list.                              *
+     *                                                                         *
+     **************************************************************************/
+
+    if (list_size(list) == 0)
+      list->tail = new_element;
+
+    new_element->next = list->head;
+    list->head = new_element;
+  }/* end if (element == NULL) */
+  else {
+
+    /**************************************************************************
+     *                                                                         *
+     *  Handle insertion somewhere other than at the head.                     *
+     *                                                                         *
+     **************************************************************************/
+
+    if (element->next == NULL)
+      list->tail = new_element;
+
+    new_element->next = element->next;
+    element->next = new_element;
+  }/* end else */
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Adjust the size of the list to account for the inserted element.          *
+   *                                                                            *
+   *****************************************************************************/
+
+  list->size++;
+  return 0;
+} /* end list_ins_next() */
+
+/*****************************************************************************
+ *                                                                            *
+ *  ----------------------------- list_rem_next ----------------------------  *
+ *                                                                            *
+ *****************************************************************************/
+
+int list_rem_next(List *list, ListElmt *element, void **data) {
+
+  ListElmt           *old_element;
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Do not allow removal from an empty list.                                  *
+   *                                                                            *
+   *****************************************************************************/
+
+  if (list_size(list) == 0)
+    return -1;
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Remove the element from the list.                                         *
+   *                                                                            *
+   *****************************************************************************/
+
+  if (element == NULL) {
+
+    /**************************************************************************
+     *                                                                         *
+     *  Handle removal from the head of the list.                              *
+     *                                                                         *
+     **************************************************************************/
+
+    *data = list->head->data;
+    old_element = list->head;
+    list->head = list->head->next;
+
+    if (list_size(list) == 1)
+      list->tail = NULL;
+  }/* end if (element == NULL) */
+  else {
+
+    /**************************************************************************
+     *                                                                         *
+     *  Handle removal from somewhere other than the head.                     *
+     *                                                                         *
+     **************************************************************************/
+
+    if (element->next == NULL)
+      return -1;
+
+    *data = element->next->data;
+    old_element = element->next;
+    element->next = element->next->next;
+
+    if (element->next == NULL)
+      list->tail = element;
+  }/* end else */
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Free the storage allocated by the abstract data type.                     *
+   *                                                                            *
+   *****************************************************************************/
+
+  free(old_element);
+
+  /*****************************************************************************
+   *                                                                            *
+   *  Adjust the size of the list to account for the removed element.           *
+   *                                                                            *
+   *****************************************************************************/
+
+  list->size--;
+  return 0;
+}