diff --git a/src/continousPut/passbook.c b/src/continousPut/passbook.c
new file mode 100644
index 0000000000000000000000000000000000000000..a94424939832659fc3fda37e3ee608b67611de14
--- /dev/null
+++ b/src/continousPut/passbook.c
@@ -0,0 +1,682 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <pwd.h>
+#include <unistd.h>
+
+#include "debug.h"
+
+#ifdef PASSBOOK_LIBFUZZER
+#include <stdint.h>
+const char LIBFUZZER_INPUT_FILE[] = "libFuzzerInput.tmp";
+/* turn off tracing to make it run faster */
+#define printf(...)
+#define fprintf(...)
+#endif
+
+const char INSTRUCTION_PUT[] = "put";
+
+const char INSTRUCTION_REM[] = "rem";
+
+const char INSTRUCTION_GET[] = "get";
+
+const char INSTRUCTION_SAVE[] = "save";
+
+const char INSTRUCTION_LIST[] = "list";
+
+const char INSTRUCTION_MASTERPW[] = "masterpw";
+
+/* a credential is a username/password pair */
+typedef struct {
+  char * username;
+  char * password;
+} cred_t;
+
+/* we store a mapping from URLs to credentials using a binary tree
+   to try to ensure log lookup performance */
+typedef struct node {
+  char * url;
+  cred_t cred;
+  // struct node *left;
+  // struct node *right;
+} node_t;
+
+#define MAP_NODE_NUM 1023
+
+static const node_t * lookup(const node_t *map, const char *url){
+  // while (p != NULL){
+  //   int ret = strcmp(url,p->url);
+  //   if (ret == 0){
+  //     return p;
+  //   }else if (ret < 0){
+  //     p = p->left;
+  //   }else{
+  //     p = p->right;
+  //   }
+  // }
+  // return p; // not found
+  if (map == NULL) {
+    map = (node_t *)calloc(MAP_NODE_NUM, sizeof(node_t));
+    assert(map != NULL);
+  }
+  for (int i = 0; i < MAP_NODE_NUM; i++) {
+    if ((map + i)->url == NULL) {
+      continue;
+    }
+    if (strcmp((map + i)->url,url) == 0) {
+      return (map + i);
+    }
+  }
+  return NULL;
+}
+
+
+
+static void node_print(const node_t *p){
+  printf("URL: %s, Username: %s, Password: %s\n",p->url,p->cred.username,p->cred.password);
+}
+
+// /* construct a new node */
+// static node_t *node_new(const char *url, const cred_t cred){
+//   node_t *new = malloc(sizeof(node_t));
+//   assert(new != NULL && "new: malloc failed");
+//   new->url = strdup(url);
+//   assert(new->url != NULL && "new: strdup url failed");
+//   new->cred.username = strdup(cred.username);
+//   assert(new->cred.username != NULL && "new: strdup username failed");  
+//   new->cred.password = strdup(cred.password);
+//   assert(new->cred.password != NULL && "new: strdup password failed");
+//   // new->left = NULL;
+//   // new->right = NULL;
+//   return new;
+// }
+
+// /* updates a node's credential in place: 
+//    replaces p's credential with that from q and frees q */
+// static void node_edit_cred(node_t * p, node_t *q){
+//   free(p->cred.username);
+//   free(p->cred.password);
+
+//   p->cred.username = q->cred.username;
+//   p->cred.password = q->cred.password;
+//   free(q->url);
+//   free(q);
+// }
+
+// static void node_free(node_t *p){
+//   free(p->url);
+//   free(p->cred.username);
+//   free(p->cred.password);
+//   free(p);
+// }
+
+/* insert q into p
+   we assume that if q has children then it cannot already
+   be present in p. Otherwise, if q has no children and we find its url in p,
+   then we edit the found entry in place while preserving its children */
+// static node_t * node_insert(node_t *map, node_t *q){
+  // if (p == NULL){
+  //   return q;
+  // }
+  // if (q == NULL){
+  //   return p;
+  // }
+  // /* we store a pointer to a node pointer that remembers where in the 
+  //    tree the new node needs to be added */
+  // node_t ** new = NULL;
+  // node_t * const start = p;
+  // while (new == NULL) {
+  //   int ret = strcmp(q->url,p->url);
+  //   if (ret == 0){
+  //     assert (q->left == NULL && q->right == NULL && "illegal insertion");
+  //     /* edit the node in place */
+  //     node_edit_cred(p,q);
+  //     /* q is now freed so cannot be used anymore */
+  //     return start;
+  //   }else if (ret < 0){
+  //     if (p->left == NULL){
+  //       new = &(p->left);
+  //     }else{
+  //       p = p->left;
+  //     }
+  //   }else{
+  //     if (p->right == NULL){
+  //       new = &(p->right);
+  //     }else{
+  //       p = p->right;
+  //     }
+  //   }
+  // }
+  // *new = q;
+  // return start;
+// }
+
+/* returns a pointer to the tree with the node added or with the existing
+   node updated if it was  already present */
+static node_t * put(node_t *map, const char *url, const cred_t cred){
+  if (map == NULL) {
+    map = (node_t *)calloc(MAP_NODE_NUM, sizeof(node_t));
+    assert(map != NULL);
+  }
+  for (int i = 0; i < MAP_NODE_NUM; i++) {
+    if ((map + i)->url != NULL && strcmp((map + i)->url, url) == 0) {
+      free((map + i)->cred.password);
+      free((map + i)->cred.username);
+      (map + i)->cred.password = strdup(cred.password);
+      (map + i)->cred.username = strdup(cred.username);
+      return map;
+    }
+  }
+  int i = 0;
+  while (1) {
+    if ((map + i)->url == NULL)
+    {
+      (map + i)->cred.password = strdup(cred.password);
+      (map + i)->cred.username = strdup(cred.username);
+      (map + i)->url = strdup(url);
+      return map;
+    }
+    i++;
+  }
+  return map;
+}
+
+/* destroy tree rooted at p */
+static void destroy(node_t *map){
+  if(map == NULL) {
+    return;
+  }
+  for(int i = 0; i < MAP_NODE_NUM; i++) {
+    if ((map + i)->url != NULL) {
+      free((map + i)->url);
+      free((map + i)->cred.password);
+      free((map + i)->cred.username);
+    } 
+  }
+  free(map);
+  map = NULL;
+}
+
+/* returns a pointer to the tree with the node removed (if it was present) */
+static node_t * rem(node_t *map, const char *url){
+  // node_t * const start = p;
+  // /* remember where the pointer to p was stored */
+  // node_t ** pptr = NULL;
+  // while (p != NULL){
+  //   int ret = strcmp(url,p->url);
+  //   if (ret == 0){
+  //     node_t * left = p->left;
+  //     node_t * const right = p->right;
+  //     left = node_insert(left,right);
+  //     node_free(p);
+  //     if (pptr != NULL){
+  //       *pptr = left;
+  //       return start;
+  //     }else{
+  //       /* p was the only node in the tree */
+  //       assert(p == start);
+  //       return left;
+  //     }
+  //   }else if (ret < 0){
+  //     pptr = &(p->left);
+  //     p = p->left;
+  //   }else{
+  //     pptr = &(p->right);
+  //     p = p->right;
+  //   }
+  // }
+  // return start; // not found
+  if (map == NULL) {
+    map = (node_t *)calloc(MAP_NODE_NUM, sizeof(node_t));
+    assert(map != NULL);
+  }
+  for (int i = 0; i < MAP_NODE_NUM; i++) {
+    if ((map + i)->url == NULL) {
+      continue;
+    }
+    if (strcmp((map + i)->url, url) == 0) {
+      free((map + i)->url);
+      (map + i)->url = NULL;
+      free((map + i)->cred.password);
+      free((map + i)->cred.username);
+      break;
+    }
+  }
+  return map;
+}
+
+const char WHITESPACE[] = " \t\r\n";
+
+
+/* tokenise a string, splitting on characters in WHITESPACE, up to
+ * a maxium of toksLen tokens, each of whose start addresses is put into
+ * toks and each of which is NUL-terminated in str.
+ * returns number of tokens found */
+unsigned int tokenise(char *str, char * toks[], unsigned int toksLen){
+  unsigned numToks = 0;
+  while (numToks < toksLen){
+    /* strip leading whitespace */     
+    size_t start = strspn(str,WHITESPACE);
+    if (str[start] != '\0'){
+      toks[numToks] = &(str[start]);    
+    
+      /* compute the length of the token */
+      const size_t tokLen = strcspn(toks[numToks],WHITESPACE);
+      if (tokLen > 0){
+        toks[numToks][tokLen] = '\0';
+        str = &(toks[numToks][tokLen+1]);
+        numToks++;
+      }else{
+        return numToks;
+      }
+    }else{
+      return numToks;
+    }
+  }
+  return numToks;
+}
+
+#define MAX_LINE_LENGTH  1022
+#define MAX_INSTRUCTIONS 1024
+/* two extra chars in each line: the newline '\n' and NUL '\0' */
+#define INSTRUCTION_LENGTH (MAX_LINE_LENGTH+2)
+
+
+/* a global instruction buffer */
+char inst[INSTRUCTION_LENGTH];
+
+/* password mapping for each url: initially empty */
+node_t * map = NULL;
+
+/* a doubly-linked list of node pointers
+   is used to implement stacks/queues of nodes so we can implement various
+   tree traversal algorithms without using recursion (to avoid stack overflow
+   for very large trees). Stack overflow is a trivial form of memory-safety
+   vulnerability. */
+// typedef struct nodeptr_list_elem {
+//   const node_t *p;
+//   struct nodeptr_list_elem *next;
+//   struct nodeptr_list_elem *prev;
+// } nodeptr_list_elem_t;
+
+// typedef struct nodeptr_list {
+//   nodeptr_list_elem_t *head;
+//   nodeptr_list_elem_t *last;
+// } nodeptr_list_t;
+
+
+// /* push an element p onto the front of a nodeptr list lst */
+// nodeptr_list_t list_push(nodeptr_list_t lst, const node_t *p){
+//   nodeptr_list_elem_t *n = malloc(sizeof(nodeptr_list_elem_t));
+//   assert(n != NULL && "push: malloc failed");
+//   n->p = p;
+//   n->next = lst.head;
+//   n->prev = NULL;  
+//   if (lst.head != NULL){
+//     assert(lst.last != NULL);
+//     lst.head->prev = n;
+//   }else{
+//     assert(lst.last == NULL);
+//     lst.last = n;
+//   }
+//   lst.head = n;
+  
+//   return lst;
+// }
+
+// /* when out is non-NULL we place a pointer to the first node into it.
+//    assumption: lst.head and lst.last are non-NULL */
+// nodeptr_list_t list_pop(nodeptr_list_t lst, const node_t **out){
+//   assert(lst.head != NULL && lst.last != NULL);
+//   if (out != NULL){
+//     *out = lst.head->p;
+//   }
+//   if (lst.last == lst.head){
+//     free(lst.head);
+//     lst.head = NULL;
+//     lst.last = NULL;
+//   }else{
+//     nodeptr_list_elem_t *ret = lst.head->next;
+//     free(lst.head);
+//     lst.head = ret;
+//   }
+//   return lst;
+// }
+
+// /* when out is non-NULL we place a pointer to the last node into it.
+//    assumption: lst.head and lst.last are non-NULL */
+// nodeptr_list_t list_dequeue(nodeptr_list_t lst, const node_t **out){
+//   assert(lst.head != NULL && lst.last != NULL);
+//   if (out != NULL){
+//     *out = lst.last->p;
+//   }
+
+//   if (lst.last == lst.head){
+//     free(lst.head);
+//     lst.head = NULL;
+//     lst.last = NULL;
+//   }else{
+//     nodeptr_list_elem_t *ret = lst.last->prev;
+//     free(lst.last);
+//     lst.last = ret;
+//   }
+//   return lst;
+// }
+
+/* in order traversal to print out nodes in sorted order. Is used to
+   implement listing of all entries in the passbook */
+void print_inorder( ){
+  // nodeptr_list_t lst = {.head = NULL, .last = NULL};
+  // if (p != NULL){
+  //   lst = list_push(lst,p);
+
+  //   while(lst.head != NULL){
+  //     // keep recursing left until we can go no further
+  //     while (p->left != NULL){
+  //       lst = list_push(lst,p->left);
+  //       p = p->left;
+  //     }
+      
+  //     // pop from the stack to simulate the return
+  //     const node_t *q;
+  //     lst = list_pop(lst,&q);
+
+  //     // print the node following the return
+  //     node_print(q);
+
+  //     // simulate right recursive call
+  //     if (q->right != NULL){
+  //       lst = list_push(lst,q->right);
+  //       p = q->right;
+  //     }
+  //   }
+  // }
+  if (map == NULL) {
+    return;
+  }
+  for (int i = 0; i < MAP_NODE_NUM; i++) {
+    if((map + i)->url == NULL) {
+      continue;
+    }
+    node_print((map + i));
+  }
+}
+
+/* save a node to the given file. We save to the file a "put" instruction
+   that will cause the node to be placed back into the passbook when the
+   file is read. */
+void node_save(const node_t *p, FILE *f){
+  fprintf(f,"%s",INSTRUCTION_PUT);
+  fprintf(f," ");
+  fprintf(f,"%s",p->url);
+  fprintf(f," ");  
+  fprintf(f,"%s",p->cred.username);  
+  fprintf(f," ");  
+  fprintf(f,"%s",p->cred.password);
+  fprintf(f,"\n");
+}
+
+/* save the master password to the given file. We save a "masterpw" 
+   instruction that will cause the passbook to prompt the user for the
+   given master password the next time the file is read */
+void masterpw_save(const char *pw, FILE *f){
+  fprintf(f,"%s",INSTRUCTION_MASTERPW);
+  fprintf(f," ");
+  fprintf(f,"%s",pw);
+  fprintf(f,"\n");
+}
+
+/* level order (i.e. breadth-first) traversal to print nodes out in the
+   order that they need to be put back in to an empty tree to ensure
+   that the resulting tree has the same structure as the original one.
+   This is how we save the passbook to a file.
+   Returns 0 on success; nonzero on failure */
+int save_levelorder(const node_t *map, const char *masterpw,
+                    const char * filename){
+#ifdef PASSBOOK_FUZZ
+  // ignore the file name when fuzzing etc. to avoid DoS on the server
+  FILE *f = fopen("/dev/null","w");
+#else
+  FILE *f = fopen(filename,"w");
+#endif
+  if (f == NULL){
+    fprintf(stderr,"Couldn't open file %s for writing.\n",filename);
+    return -1;
+  }
+  masterpw_save(masterpw,f);
+  // nodeptr_list_t lst = {.head = NULL, .last = NULL};
+  // if (p != NULL){
+  //   lst = list_push(lst,p);
+
+  //   while(lst.last != NULL){
+  //     lst = list_dequeue(lst,&p);
+  //     node_save(p,f);
+  //     if (p->left != NULL){
+  //       lst = list_push(lst,p->left);
+  //     }
+  //     if (p->right != NULL){
+  //       lst = list_push(lst,p->right);
+  //     }
+  //   }
+  // }
+  if (map == NULL) {
+    map = (node_t *)calloc(MAP_NODE_NUM, sizeof(node_t));
+    assert(map != NULL);
+  }
+  for(int i = 0; i < MAP_NODE_NUM; i++) {
+    if ((map + i)->url == NULL) {
+      continue;
+    }
+    node_save((map + i), f);
+  }
+  fclose(f);
+  return 0;
+}
+
+/* returns 0 on successful execution of the instruction in inst */
+static int execute(void){
+  char * toks[4]; /* these are pointers to start of different tokens */
+  const unsigned int numToks = tokenise(inst,toks,4);
+    
+  if (numToks == 0){
+    /* blank line */
+    return 0;
+  }
+    
+  if (strcmp(toks[0],INSTRUCTION_GET) == 0){
+    if (numToks != 2){
+      debug_printf("Expected 1 argument to %s instruction but instead found %u\n",INSTRUCTION_GET,numToks-1);
+      return -1;
+    }
+    debug_printf("Looking up: %s\n",toks[1]);
+    const node_t *p = lookup(map,toks[1]);
+    if (p != NULL){
+      node_print(p);
+    }else{
+      printf("Not found.\n");
+    }
+    destroy(map);
+    map = (node_t *)calloc(MAP_NODE_NUM, sizeof(node_t));
+    assert(map != NULL);
+
+  } else if (strcmp(toks[0],INSTRUCTION_REM) == 0){
+    if (numToks != 2){
+      debug_printf("Expected 1 argument to %s instruction but instead found %u\n",INSTRUCTION_REM,numToks-1);
+      return -1;
+    }
+    debug_printf("Removing: %s\n",toks[1]);
+    map = rem(map,toks[1]);
+    destroy(map);
+    map = (node_t *)calloc(MAP_NODE_NUM, sizeof(node_t));
+    assert(map != NULL);
+  } else if (strcmp(toks[0],INSTRUCTION_PUT) == 0){
+    if (numToks != 4){
+      debug_printf("Expected 3 arguments to %s instruction but instead found %u\n",INSTRUCTION_PUT,numToks-1);
+      return -1;
+    }
+    cred_t cred;
+    cred.username = toks[2];
+    cred.password = toks[3];
+    map = put(map,toks[1],cred);
+
+  } else if (strcmp(toks[0],INSTRUCTION_SAVE) == 0){
+    if (numToks != 3){
+      debug_printf("Expected 2 arguments to %s instruction but instead found %u\n",INSTRUCTION_SAVE,numToks-1);
+      return -1;
+    }
+    debug_printf("Saving under master password %s to file: %s\n",toks[1],toks[2]);
+    if (save_levelorder(map,toks[1],toks[2]) != 0){
+      debug_printf("Error saving to file %s\n",toks[2]);
+      return -1;
+    }
+    destroy(map);
+    map = (node_t *)calloc(MAP_NODE_NUM, sizeof(node_t));
+    assert(map != NULL);
+  } else if (strcmp(toks[0],INSTRUCTION_MASTERPW) == 0){
+    if (numToks != 2){
+      debug_printf("Expected 1 argument to %s instruction but instead found %u\n",INSTRUCTION_MASTERPW,numToks-1);      return -1;
+    }
+    // when fuzzing (or gathering coverage stats, etc.) don't check master pw
+#ifndef PASSBOOK_FUZZ
+    const char * pass = getpass("Enter master password: ");
+    if (pass == NULL || strcmp(pass,toks[1]) != 0){
+      fprintf(stderr,"Master password incorrect!\n");
+      exit(1); // exit immediately
+    }
+#else
+    return -1; 
+#endif    
+  } else if (strcmp(toks[0],INSTRUCTION_LIST) == 0){
+    if (numToks != 1){
+      debug_printf("Expected 0 arguments to %s instruction but instead found %u\n",INSTRUCTION_LIST,numToks-1);
+      return -1;
+    }
+    print_inorder();
+    destroy(map);
+    map = (node_t *)calloc(MAP_NODE_NUM, sizeof(node_t));
+    assert(map != NULL);
+  }else{
+    debug_printf("Unrecognised instruction %s\n",toks[0]);
+    return -1;
+  }
+  
+  return 0;
+}
+
+/* returns >=0 on success, in which case the number of instructions executed
+   is returned. Returns < 0 on failure. */
+static int run(FILE *f){
+  assert(f != NULL);
+  
+  int instructionCount = 0;
+  while (instructionCount < MAX_INSTRUCTIONS){
+    memset(inst,0,sizeof(inst));
+    char * res = fgets(inst,sizeof(inst),f);
+    if (res == NULL){
+      if (feof(f)){
+        /* end of file */
+        return instructionCount;
+      }else{
+        debug_printf("Error while reading, having read %d lines\n",instructionCount);
+        return -1;
+      }
+    }
+    if (inst[MAX_LINE_LENGTH] != '\0'){
+      if (!(inst[MAX_LINE_LENGTH] == '\n' && inst[MAX_LINE_LENGTH+1] == '\0')){
+        fprintf(stderr,"Line %d exceeds maximum length (%d)\n",instructionCount+1,MAX_LINE_LENGTH);
+        debug_printf("(Expected at array index %d to find NUL but found '%c' (%d))\n",MAX_LINE_LENGTH,inst[MAX_LINE_LENGTH],inst[MAX_LINE_LENGTH]);
+        return -1;
+      }
+    }else{
+      /* inst[MAX_LINE_LENGTH] == '\0', so
+         strlen is guaranteed to be <= MAX_LINE_LENGTH
+         Check if it has a newline and add it if it needs it */
+      size_t len = strlen(inst);
+      if (len > 0){
+        if (inst[len-1] != '\n'){
+          inst[len] = '\n';
+          inst[len+1] = '\0';
+        }
+      }
+    }
+    instructionCount++;
+    int r = execute();
+    if (r != 0){
+      return -1;
+    }
+  }
+
+  if (feof(f)){
+    /* final line of file didn't have a trailing newline */
+    return instructionCount;
+  }else{
+    /* see if we are at end of file by trying to do one more read.
+       this is necessary if the final line of the file ends in a 
+       newline '\n' character */
+    char c;
+    int res = fread(&c,1,1,f);
+    if (res == 1){
+      fprintf(stderr,"Number of instructions (lines) in file exceeds max (%d)\n",MAX_INSTRUCTIONS);
+      return -1;
+    }else{
+      if (feof(f)){
+        /* final read found the EOF, so all good */
+        return instructionCount;
+      }else{
+        /* probably won't ever get here */
+        debug_printf("Error while trying to test if line %d was empty\n",instructionCount+1);
+        return -1;
+      }
+    }
+  }
+}
+
+#ifdef PASSBOOK_LIBFUZZER
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+  FILE *f = fopen(LIBFUZZER_INPUT_FILE,"w");
+  fwrite(Data,Size,1,f);
+  fclose(f);
+  f = fopen(LIBFUZZER_INPUT_FILE,"r");
+  run(f);
+  fclose(f);
+  destroy(map);
+  map = NULL;
+  return 0; /* libFuzzer wants 0 returned always */
+}
+#else
+int main(const int argc, const char * argv[]){
+  if (argc <= 1){
+    fprintf(stderr,"Usage: %s file1 file2 ...\n",argv[0]);
+    fprintf(stderr,"       use - to read from standard input\n");
+    exit(0);
+  }
+  
+  for (int i = 1; i<argc; i++){
+    printf("Running on input file %s\n",argv[i]);
+    FILE *f;
+    if (strcmp(argv[i],"-") == 0){
+      f = stdin;
+    }else{
+      f = fopen(argv[i],"r");
+      if (f == NULL){
+        fprintf(stderr,"Error opening %s for reading\n",argv[i]);
+        destroy(map);
+        exit(1);
+      }
+    }
+    map = (node_t *)calloc(MAP_NODE_NUM, sizeof(node_t));
+    assert(map != NULL);
+    int ans = run(f);
+    if (ans < 0){
+      fprintf(stderr,"Error\n");
+    }
+    /* do not close stdin */
+    if (f != stdin){
+      fclose(f);
+    }
+  }
+  destroy(map);
+  return 0;
+}
+#endif